Tons of AI + pathfinding bugfixes:

- waypoints are created between docked subs and removed when they undock
- fixed start waypoint being left out of steering paths
- NPCs won't close doors/hatches on themselves
- NPCs won't let go of ladders until their feet are above the lower edge of the hull
- fixed FindDivingGear "loops": need to find a suit -> need to get to a suit -> need a suit to get to the suit -> need to find a suit...
- fixed characters constantly turning from side to side in small rooms
- recursive function for finding the button which opens a door (so a button doesn't have to be connected straight to a door for an NPC to be able to open it)
- AIObjectiveGetItem keeps searching for more suitable items even if a path to a matching item has been found
This commit is contained in:
Regalis
2016-07-08 20:53:11 +03:00
parent c8dae18135
commit a5111d33df
17 changed files with 397 additions and 125 deletions
@@ -29,12 +29,17 @@ namespace Barotrauma
public Vector2 SimPosition public Vector2 SimPosition
{ {
get { return Character.AnimController.Limbs[0].SimPosition; } get { return Character.SimPosition; }
}
public Vector2 WorldPosition
{
get { return Character.WorldPosition; }
} }
public Vector2 Velocity public Vector2 Velocity
{ {
get { return Character.AnimController.Limbs[0].LinearVelocity; } get { return Character.AnimController.RefLimb.LinearVelocity; }
} }
public AiState State public AiState State
@@ -71,7 +71,33 @@ namespace Barotrauma
steeringManager.Update(moveSpeed); steeringManager.Update(moveSpeed);
Character.AnimController.IgnorePlatforms = (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X*0.5f)); Character.AnimController.IgnorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f &&
(-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X));
var currPath = (steeringManager as IndoorsSteeringManager).CurrentPath;
if (currPath != null && currPath.CurrentNode != null)
{
if (currPath.CurrentNode.WorldPosition.Y < Character.WorldPosition.Y - 200)
{
Character.AnimController.IgnorePlatforms = true;
}
if (Character.AnimController.Stairs != null)
{
float yDiff = currPath.CurrentNode.WorldPosition.Y - Character.WorldPosition.Y;
if (Math.Abs(yDiff)>10.0f)
{
int dir = Math.Sign(yDiff);
float movement = Character.AnimController.Stairs.StairDirection == Direction.Right ?
dir * Character.AnimController.TargetMovement.Length() : -dir * Character.AnimController.TargetMovement.Length();
Character.AnimController.TargetMovement = new Vector2(movement, 0.0f);
}
}
}
(Character.AnimController as HumanoidAnimController).Crouching = false; (Character.AnimController as HumanoidAnimController).Crouching = false;
@@ -86,17 +112,20 @@ namespace Barotrauma
if (Character.SelectedConstruction != null && Character.SelectedConstruction.GetComponent<Items.Components.Ladder>()!=null) if (Character.SelectedConstruction != null && Character.SelectedConstruction.GetComponent<Items.Components.Ladder>()!=null)
{ {
var currPath = (steeringManager as IndoorsSteeringManager).CurrentPath;
if (currPath != null && currPath.CurrentNode != null && currPath.CurrentNode.Ladders != null) if (currPath != null && currPath.CurrentNode != null && currPath.CurrentNode.Ladders != null)
{ {
Character.AnimController.TargetMovement = new Vector2( 0.0f, Math.Sign(Character.AnimController.TargetMovement.Y)); Character.AnimController.TargetMovement = new Vector2( 0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
} }
} }
//unequip diving suit if running out of oxygen //suit can be taken off if there character is inside a hull and there's air in the room
if (Character.Oxygen < 50.0f && Character.AnimController.CurrentHull!=null && bool canTakeOffSuit = Character.AnimController.CurrentHull != null &&
Character.AnimController.CurrentHull.OxygenPercentage > 20.0f && Character.AnimController.CurrentHull.OxygenPercentage > 30.0f &&
Character.AnimController.CurrentHull.Volume < Character.AnimController.CurrentHull.FullVolume*0.3f) Character.AnimController.CurrentHull.Volume < Character.AnimController.CurrentHull.FullVolume * 0.3f;
//the suit can be taken off and the character is running out of oxygen (couldn't find a tank for the suit?) or idling
//-> take the suit off
if (canTakeOffSuit && (Character.Oxygen < 50.0f || objectiveManager.CurrentObjective is AIObjectiveIdle))
{ {
var divingSuit = Character.Inventory.FindItem("Diving Suit"); var divingSuit = Character.Inventory.FindItem("Diving Suit");
if (divingSuit != null) divingSuit.Drop(Character); if (divingSuit != null) divingSuit.Drop(Character);
@@ -121,9 +150,6 @@ namespace Barotrauma
{ {
Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left; Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left;
} }
} }
public override void OnAttacked(IDamageable attacker, float amount) public override void OnAttacked(IDamageable attacker, float amount)
@@ -172,7 +198,7 @@ namespace Barotrauma
{ {
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
new Vector2(pathSteering.CurrentPath.Nodes[i].WorldPosition.X, -pathSteering.CurrentPath.Nodes[i].WorldPosition.Y), new Vector2(pathSteering.CurrentPath.Nodes[i].WorldPosition.X, -pathSteering.CurrentPath.Nodes[i].WorldPosition.Y),
new Vector2(pathSteering.CurrentPath.Nodes[i - 1].WorldPosition.Y, -pathSteering.CurrentPath.Nodes[i-1].WorldPosition.Y), new Vector2(pathSteering.CurrentPath.Nodes[i - 1].WorldPosition.X, -pathSteering.CurrentPath.Nodes[i-1].WorldPosition.Y),
Color.LightGreen); Color.LightGreen);
} }
} }
@@ -20,6 +20,11 @@ namespace Barotrauma
{ {
get; get;
} }
Vector2 WorldPosition
{
get;
}
@@ -122,12 +122,22 @@ namespace Barotrauma
if (currentPath.CurrentNode!=null && currentPath.CurrentNode.SimPosition.Y > character.SimPosition.Y+1.0f) allowedDistance*=0.5f; if (currentPath.CurrentNode!=null && currentPath.CurrentNode.SimPosition.Y > character.SimPosition.Y+1.0f) allowedDistance*=0.5f;
Vector2 pos = host.SimPosition; Vector2 pos = host.SimPosition;
if (character != null && character.Submarine == null &&
CurrentPath.CurrentNode != null && CurrentPath.CurrentNode.Submarine != null) if (character != null && currentPath.CurrentNode != null)
{ {
//todo: take multiple subs into account if (CurrentPath.CurrentNode.Submarine != null)
pos -= CurrentPath.CurrentNode.Submarine.SimPosition; {
} if (character.Submarine == null)
{
pos -= CurrentPath.CurrentNode.Submarine.SimPosition;
}
else if (character.Submarine != currentPath.CurrentNode.Submarine)
{
pos -= FarseerPhysics.ConvertUnits.ToSimUnits(currentPath.CurrentNode.Submarine.Position-character.Submarine.Position);
}
}
}
if (currentPath.CurrentNode!= null && currentPath.CurrentNode.Ladders!=null) if (currentPath.CurrentNode!= null && currentPath.CurrentNode.Ladders!=null)
{ {
@@ -135,17 +145,32 @@ namespace Barotrauma
{ {
currentPath.CurrentNode.Ladders.Item.Pick(character, false, true); currentPath.CurrentNode.Ladders.Item.Pick(character, false, true);
} }
if (Math.Sign(host.Steering.Y) == currentPath.CurrentNode.SimPosition.Y - pos.Y)
{
allowedDistance = 0.0f;
}
} }
currentPath.CheckProgress(pos, allowedDistance); currentPath.CheckProgress(pos, allowedDistance);
if (currentPath.CurrentNode == null) return Vector2.Zero; if (currentPath.CurrentNode == null) return Vector2.Zero;
var hull = character.AnimController.CurrentHull;
if (character.AnimController.Anim == AnimController.Animation.Climbing)
{
float x = currentPath.CurrentNode.SimPosition.X - pos.X;
float y = (currentPath.CurrentNode.SimPosition.Y) - pos.Y;
if (Math.Abs(x) < Math.Abs(y) * 10.0f)
{
x = 0.0f;
}
else if (character.AnimController.LowestLimb != null && hull != null)
{
if (character.AnimController.LowestLimb.Position.Y < hull.Rect.Y - hull.Rect.Height + 10.0f) x = 0.0f;
}
character.AnimController.IgnorePlatforms = false;
return new Vector2(x,y);
}
return currentPath.CurrentNode.SimPosition - pos; return currentPath.CurrentNode.SimPosition - pos;
} }
@@ -153,41 +178,67 @@ namespace Barotrauma
{ {
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
WayPoint node = i == 0 ? currentPath.CurrentNode : currentPath.PrevNode;
if (node == null || node.ConnectedGap == null || node.ConnectedGap.ConnectedDoor == null) continue;
var door = node.ConnectedGap.ConnectedDoor; WayPoint node = null;
WayPoint nextNode = null;
bool open = currentPath.CurrentNode != null &&
Math.Sign(door.Item.SimPosition.X - host.SimPosition.X) == Math.Sign(currentPath.CurrentNode.SimPosition.X - host.SimPosition.X); if (i==0)
if (currentPath.CurrentNode==null)
{ {
open = false; node = currentPath.CurrentNode;
nextNode = currentPath.NextNode;
} }
else else
{ {
if (door.LinkedGap.isHorizontal) node = currentPath.PrevNode;
{ nextNode = currentPath.CurrentNode;
open = Math.Sign(door.Item.SimPosition.X - character.SimPosition.X) == Math.Sign(currentPath.CurrentNode.SimPosition.X - character.SimPosition.X);
}
else
{
open = Math.Sign(door.Item.SimPosition.Y - character.SimPosition.Y) == Math.Sign(currentPath.CurrentNode.SimPosition.Y - character.SimPosition.Y);
}
} }
//toggle the door if it's the previous node and open, or if it's current node and closed if (node == null || node.ConnectedGap == null || node.ConnectedGap.ConnectedDoor == null) continue;
if (door.IsOpen != open)
if (nextNode == null) continue;
var door = node.ConnectedGap.ConnectedDoor;
bool shouldBeOpen = false;
if (door.LinkedGap.isHorizontal)
{ {
var buttons = door.Item.GetConnectedComponents<Controller>(); int currentDir = Math.Sign(nextNode.WorldPosition.X - door.Item.WorldPosition.X);
shouldBeOpen = (door.Item.WorldPosition.X - character.WorldPosition.X) * currentDir > -50.0f;
}
else
{
int currentDir = Math.Sign(nextNode.WorldPosition.Y - door.Item.WorldPosition.Y);
shouldBeOpen = (door.Item.WorldPosition.Y - character.WorldPosition.Y) * currentDir > -80.0f;
}
//toggle the door if it's the previous node and open, or if it's current node and closed
if (door.IsOpen != shouldBeOpen)
{
var buttons = door.Item.GetConnectedComponents<Controller>(true);
Controller closestButton = null;
float closestDist = 0.0f;
foreach (Controller controller in buttons) foreach (Controller controller in buttons)
{ {
if (Vector2.Distance(controller.Item.Position, character.Position) > controller.Item.PickDistance * 2.0f) continue; float dist = Vector2.Distance(controller.Item.WorldPosition, character.WorldPosition);
if (dist > controller.Item.PickDistance * 2.0f) continue;
controller.Item.Pick(character, false, true); if (dist < closestDist || closestButton == null)
{
closestButton = controller;
closestDist = dist;
}
}
if (closestButton != null)
{
closestButton.Item.Pick(character, false, true);
break; break;
} }
} }
@@ -37,7 +37,7 @@ namespace Barotrauma
if (currentHull != null) if (currentHull != null)
{ {
if (currentHull.Volume / currentHull.FullVolume > 0.5f || character.Oxygen < 80.0f) if (NeedsDivingGear())
{ {
if (!FindDivingGear(deltaTime)) return; if (!FindDivingGear(deltaTime)) return;
} }
@@ -131,6 +131,19 @@ namespace Barotrauma
return (otherObjective is AIObjectiveFindSafety); return (otherObjective is AIObjectiveFindSafety);
} }
private bool NeedsDivingGear()
{
var currentHull = character.AnimController.CurrentHull;
if (currentHull == null) return true;
//there's lots of water in the room -> get a suit
if (currentHull.Volume / currentHull.FullVolume > 0.5f) return true;
if (currentHull.OxygenPercentage < 30.0f) return true;
return false;
}
public override float GetPriority(Character character) public override float GetPriority(Character character)
{ {
if (character.Oxygen < 80.0f) if (character.Oxygen < 80.0f)
@@ -142,8 +155,12 @@ namespace Barotrauma
currenthullSafety = GetHullSafety(character.AnimController.CurrentHull, character); currenthullSafety = GetHullSafety(character.AnimController.CurrentHull, character);
priority = 100.0f - currenthullSafety; priority = 100.0f - currenthullSafety;
if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f;
if (NeedsDivingGear())
{
if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f;
}
return priority; return priority;
} }
@@ -19,6 +19,8 @@ namespace Barotrauma
public bool IgnoreContainedItems; public bool IgnoreContainedItems;
private AIObjectiveGoTo goToObjective;
private bool equip; private bool equip;
public override bool CanBeCompleted public override bool CanBeCompleted
@@ -52,13 +54,8 @@ namespace Barotrauma
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
if (targetItem == null) FindTargetItem();
{ if (targetItem == null || moveToTarget == null) return;
FindTargetItem();
if (targetItem == null) return;
}
if (moveToTarget == null) return;
if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.PickDistance*2.0f) if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.PickDistance*2.0f)
{ {
@@ -97,9 +94,15 @@ namespace Barotrauma
character.Inventory.TryPutItem(targetItem, targetSlot, true, false); character.Inventory.TryPutItem(targetItem, targetSlot, true, false);
} }
} }
else if (!subObjectives.Any()) else
{ {
AddGoToObjective(targetItem); if (goToObjective == null)
{
bool gettingDivingGear = itemName == "diving" || itemName == "Diving Gear";
goToObjective = new AIObjectiveGoTo(moveToTarget, character, false, !gettingDivingGear);
}
goToObjective.TryComplete(deltaTime);
} }
} }
@@ -111,7 +114,7 @@ namespace Barotrauma
{ {
float currDist = moveToTarget == null ? 0.0f : Vector2.DistanceSquared(moveToTarget.Position, character.Position); float currDist = moveToTarget == null ? 0.0f : Vector2.DistanceSquared(moveToTarget.Position, character.Position);
for (int i = 0; i < 5 && currSearchIndex < Item.ItemList.Count - 2; i++) for (int i = 0; i < 10 && currSearchIndex < Item.ItemList.Count - 2; i++)
{ {
currSearchIndex++; currSearchIndex++;
@@ -136,22 +139,12 @@ namespace Barotrauma
targetItem = item; targetItem = item;
moveToTarget = rootContainer ?? item; moveToTarget = rootContainer ?? item;
AddGoToObjective(moveToTarget);
} }
//if searched through all the items and a target wasn't found, can't be completed //if searched through all the items and a target wasn't found, can't be completed
if (currSearchIndex >= Item.ItemList.Count && targetItem == null) canBeCompleted = false; if (currSearchIndex >= Item.ItemList.Count && targetItem == null) canBeCompleted = false;
} }
private void AddGoToObjective(Item gotoToTarget)
{
subObjectives.Clear();
AddSubObjective(new AIObjectiveGoTo(gotoToTarget, character));
}
public override bool IsDuplicate(AIObjective otherObjective) public override bool IsDuplicate(AIObjective otherObjective)
{ {
AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem; AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem;
@@ -16,6 +16,8 @@ namespace Barotrauma
//how long until the path to the target is declared unreachable //how long until the path to the target is declared unreachable
private float waitUntilPathUnreachable; private float waitUntilPathUnreachable;
private bool getDivingGearIfNeeded;
public override bool CanBeCompleted public override bool CanBeCompleted
{ {
get get
@@ -35,23 +37,25 @@ namespace Barotrauma
get { return target; } get { return target; }
} }
public AIObjectiveGoTo(Entity target, Character character, bool repeat = false) public AIObjectiveGoTo(Entity target, Character character, bool repeat = false, bool getDivingGearIfNeeded = true)
: base (character, "") : base (character, "")
{ {
this.target = target; this.target = target;
this.repeat = repeat; this.repeat = repeat;
waitUntilPathUnreachable = 5.0f; waitUntilPathUnreachable = 5.0f;
this.getDivingGearIfNeeded = getDivingGearIfNeeded;
} }
public AIObjectiveGoTo(Vector2 simPos, Character character, bool repeat = false) public AIObjectiveGoTo(Vector2 simPos, Character character, bool repeat = false, bool getDivingGearIfNeeded = true)
: base(character, "") : base(character, "")
{ {
this.targetPos = simPos; this.targetPos = simPos;
this.repeat = repeat; this.repeat = repeat;
waitUntilPathUnreachable = 5.0f; waitUntilPathUnreachable = 5.0f;
this.getDivingGearIfNeeded = getDivingGearIfNeeded;
} }
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
@@ -108,7 +112,7 @@ namespace Barotrauma
{ {
indoorsSteering.SteeringWander(); indoorsSteering.SteeringWander();
} }
else if (indoorsSteering.CurrentPath != null && indoorsSteering.HasOutdoorsNodes) else if (getDivingGearIfNeeded && indoorsSteering.CurrentPath != null && indoorsSteering.HasOutdoorsNodes)
{ {
AddSubObjective(new AIObjectiveFindDivingGear(character, true)); AddSubObjective(new AIObjectiveFindDivingGear(character, true));
} }
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -11,8 +12,6 @@ namespace Barotrauma
AITarget currentTarget; AITarget currentTarget;
private float newTargetTimer; private float newTargetTimer;
public AIObjectiveIdle(Character character) : base(character, "") public AIObjectiveIdle(Character character) : base(character, "")
{ {
@@ -23,7 +22,6 @@ namespace Barotrauma
return 1.0f; return 1.0f;
} }
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
@@ -53,7 +51,8 @@ namespace Barotrauma
newTargetTimer = currentTarget == null ? 5.0f : 15.0f; newTargetTimer = currentTarget == null ? 5.0f : 15.0f;
} }
newTargetTimer -= deltaTime; newTargetTimer -= deltaTime;
//wander randomly if reached the end of the path or the target is unreachable //wander randomly if reached the end of the path or the target is unreachable
if (pathSteering==null || (pathSteering.CurrentPath != null && if (pathSteering==null || (pathSteering.CurrentPath != null &&
@@ -62,13 +61,28 @@ namespace Barotrauma
//steer away from edges of the hull //steer away from edges of the hull
if (character.AnimController.CurrentHull!=null) if (character.AnimController.CurrentHull!=null)
{ {
if (character.Position.X < character.AnimController.CurrentHull.Rect.X + WallAvoidDistance) float leftDist = character.Position.X - character.AnimController.CurrentHull.Rect.X;
float rightDist = character.AnimController.CurrentHull.Rect.Right - character.Position.X;
if (leftDist < WallAvoidDistance && rightDist < WallAvoidDistance)
{ {
pathSteering.SteeringManual(deltaTime, Vector2.UnitX*5.0f); if (Math.Abs(rightDist - leftDist) > WallAvoidDistance / 2)
{
pathSteering.SteeringManual(deltaTime, Vector2.UnitX * Math.Sign(rightDist - leftDist));
}
else
{
pathSteering.Reset();
return;
}
} }
else if (character.Position.X > character.AnimController.CurrentHull.Rect.Right - WallAvoidDistance) else if (leftDist < WallAvoidDistance)
{ {
pathSteering.SteeringManual(deltaTime, -Vector2.UnitX); pathSteering.SteeringManual(deltaTime, Vector2.UnitX * (WallAvoidDistance-leftDist)/WallAvoidDistance);
}
else if (rightDist < WallAvoidDistance)
{
pathSteering.SteeringManual(deltaTime, -Vector2.UnitX * (WallAvoidDistance-rightDist)/WallAvoidDistance);
} }
} }
@@ -85,7 +99,7 @@ namespace Barotrauma
} }
if (currentTarget == null) return; if (currentTarget == null) return;
character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition); character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition, 2.0f);
} }
private AITarget FindRandomTarget() private AITarget FindRandomTarget()
+80 -18
View File
@@ -19,7 +19,7 @@ namespace Barotrauma
public float F,G,H; public float F,G,H;
public List<PathNode> connections; public List<PathNode> connections;
public float[] distances; public List<float> distances;
public WayPoint Waypoint public WayPoint Waypoint
{ {
@@ -63,15 +63,14 @@ namespace Barotrauma
var nodeList = nodes.Values.ToList(); var nodeList = nodes.Values.ToList();
foreach (PathNode node in nodeList) foreach (PathNode node in nodeList)
{ {
node.distances = new float[node.connections.Count]; node.distances = new List<float>();
for (int i = 0; i< node.distances.Length; i++) for (int i = 0; i< node.connections.Count; i++)
{ {
node.distances[i] = Vector2.Distance(node.position, node.connections[i].position); node.distances.Add(Vector2.Distance(node.position, node.connections[i].position));
} }
} }
return nodeList; return nodeList;
} }
} }
class PathFinder class PathFinder
@@ -87,9 +86,66 @@ namespace Barotrauma
{ {
nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => w.MoveWithLevel != insideSubmarine)); nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => w.MoveWithLevel != insideSubmarine));
foreach (WayPoint wp in wayPoints)
{
wp.linkedTo.CollectionChanged += WaypointLinksChanged;
}
this.insideSubmarine = insideSubmarine; this.insideSubmarine = insideSubmarine;
} }
void WaypointLinksChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (Submarine.Unloading) return;
var waypoints = sender as IEnumerable<MapEntity>;
foreach (MapEntity me in waypoints)
{
WayPoint wp = me as WayPoint;
if (me == null) continue;
var node = nodes.Find(n => n.Waypoint == wp);
if (node == null) return;
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
{
for (int i = node.connections.Count - 1; i >= 0; i--)
{
//remove connection if the waypoint isn't connected anymore
if (wp.linkedTo.FirstOrDefault(l => l == node.connections[i].Waypoint) == null)
{
node.connections.RemoveAt(i);
node.distances.RemoveAt(i);
}
}
}
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
for (int i = 0; i < wp.linkedTo.Count; i++)
{
WayPoint connected = wp.linkedTo[i] as WayPoint;
if (connected == null) continue;
//already connected, continue
if (node.connections.Any(n => n.Waypoint == connected)) continue;
var matchingNode = nodes.Find(n => n.Waypoint == connected);
if (matchingNode == null)
{
#if DEBUG
DebugConsole.ThrowError("Waypoint connections were changed, no matching path node found in PathFinder");
#endif
return;
}
node.connections.Add(matchingNode);
node.distances.Add(Vector2.Distance(node.Position, matchingNode.Position));
}
}
}
}
public SteeringPath FindPath(Vector2 start, Vector2 end) public SteeringPath FindPath(Vector2 start, Vector2 end)
{ {
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
@@ -100,21 +156,21 @@ namespace Barotrauma
foreach (PathNode node in nodes) foreach (PathNode node in nodes)
{ {
Vector2 nodePos = node.Position; Vector2 nodePos = node.Position;
float dist = System.Math.Abs(start.X - nodePos.X) +
System.Math.Abs(start.Y - nodePos.Y) * 10.0f; //higher cost for vertical movement
//if node waypoint is one of submarine waypoints outside the sub, transform position //prefer nodes that are closer to the end position
//if (node.Waypoint != null && node.Waypoint.Submarine != null && node.Waypoint.CurrentHull == null) dist += Vector2.Distance(end, nodePos) / 10.0f;
//{
// nodePos -= node.Waypoint.Submarine.Position;
//}
float dist = System.Math.Abs(start.X-nodePos.X)+
System.Math.Abs(start.Y - nodePos.Y)*10.0f +
Vector2.Distance(end,nodePos)/2.0f;
if (dist<closestDist || startNode==null) if (dist<closestDist || startNode==null)
{ {
//if searching for a path inside the sub, make sure the waypoint is visible //if searching for a path inside the sub, make sure the waypoint is visible
if (insideSubmarine && Submarine.CheckVisibility(start, node.Waypoint.SimPosition) != null) continue; if (insideSubmarine)
{
var body = Submarine.CheckVisibility(start, node.Waypoint.SimPosition);
if (body != null && body.UserData is Structure) continue;
}
closestDist = dist; closestDist = dist;
startNode = node; startNode = node;
@@ -144,7 +200,11 @@ namespace Barotrauma
if (dist < closestDist || endNode == null) if (dist < closestDist || endNode == null)
{ {
//if searching for a path inside the sub, make sure the waypoint is visible //if searching for a path inside the sub, make sure the waypoint is visible
if (insideSubmarine && node.Waypoint.CurrentHull!=null && Submarine.CheckVisibility(end, node.Waypoint.SimPosition) != null) continue; if (insideSubmarine)
{
var body = Submarine.CheckVisibility(end, node.Waypoint.SimPosition);
if (body != null && body.UserData is Structure) continue;
}
closestDist = dist; closestDist = dist;
endNode = node; endNode = node;
@@ -234,7 +294,7 @@ namespace Barotrauma
for (int i = 0; i < currNode.connections.Count; i++) for (int i = 0; i < currNode.connections.Count; i++)
{ {
PathNode nextNode = currNode.connections[i]; PathNode nextNode = currNode.connections[i];
//a node that hasn't been searched yet //a node that hasn't been searched yet
if (nextNode.state==0) if (nextNode.state==0)
{ {
@@ -272,7 +332,7 @@ namespace Barotrauma
} }
} }
if (end.state==0) if (end.state == 0 || end.Parent == null)
{ {
//path not found //path not found
return new SteeringPath(true); return new SteeringPath(true);
@@ -290,6 +350,8 @@ namespace Barotrauma
pathNode = pathNode.Parent; pathNode = pathNode.Parent;
} }
finalPath.Add(start.Waypoint);
finalPath.Reverse(); finalPath.Reverse();
foreach (WayPoint wayPoint in finalPath) foreach (WayPoint wayPoint in finalPath)
@@ -814,7 +814,7 @@ namespace Barotrauma
} }
else else
{ {
notClimbing = targetMovement.X != 0.0f; notClimbing = Math.Abs(targetMovement.X) > 0.05f;
} }
//stop climbing if: //stop climbing if:
@@ -74,6 +74,11 @@ namespace Barotrauma
} }
} }
public float FloorY
{
get { return floorY; }
}
public float Mass public float Mass
{ {
get; get;
@@ -330,14 +335,8 @@ namespace Barotrauma
lowestLimb.Position.X - structure.Rect.X : structure.Rect.Width - (lowestLimb.Position.X - structure.Rect.X); lowestLimb.Position.X - structure.Rect.X : structure.Rect.Width - (lowestLimb.Position.X - structure.Rect.X);
if (character.IsDead) if (lowestLimb.Position.Y < structure.Rect.Y - structure.Rect.Height + stairPosY - 10.0f) return false;
{
if (lowestLimb.Position.Y < structure.Rect.Y - structure.Rect.Height + stairPosY - 10.0f) return false;
}
else
{
if (lowestLimb.Position.Y < structure.Rect.Y - structure.Rect.Height + stairPosY) return false;
}
if (targetMovement.Y < 0.5f) if (targetMovement.Y < 0.5f)
@@ -477,7 +476,10 @@ namespace Barotrauma
} }
} }
if (ignorePlatforms)
{
GUI.DrawLine(spriteBatch, new Vector2(refLimb.WorldPosition.X, -refLimb.WorldPosition.Y), new Vector2(refLimb.WorldPosition.X, -refLimb.WorldPosition.Y+50), Color.Orange, 0, 5);
}
} }
public virtual void Flip() public virtual void Flip()
@@ -42,6 +42,11 @@ namespace Barotrauma
get { return FarseerPhysics.ConvertUnits.ToSimUnits(position); } get { return FarseerPhysics.ConvertUnits.ToSimUnits(position); }
} }
public Vector2 WorldPosition
{
get { return position; }
}
public Vector2 Velocity public Vector2 Velocity
{ {
get { return new Vector2(velocity.X, velocity.Y); } get { return new Vector2(velocity.X, velocity.Y); }
+8 -8
View File
@@ -1143,7 +1143,14 @@ namespace Barotrauma
Vector2 pos = DrawPosition; Vector2 pos = DrawPosition;
pos.Y = -pos.Y; pos.Y = -pos.Y;
if (GameMain.DebugDraw)
{
AnimController.DebugDraw(spriteBatch);
if (aiTarget != null) aiTarget.Draw(spriteBatch);
}
if (this == controlled) return; if (this == controlled) return;
if (IsNetworkPlayer && info!=null) if (IsNetworkPlayer && info!=null)
@@ -1158,13 +1165,6 @@ namespace Barotrauma
} }
} }
if (GameMain.DebugDraw)
{
AnimController.DebugDraw(spriteBatch);
if (aiTarget != null) aiTarget.Draw(spriteBatch);
}
if (isDead) return; if (isDead) return;
Vector2 healthBarPos = new Vector2(pos.X - 50, DrawPosition.Y + 100.0f); Vector2 healthBarPos = new Vector2(pos.X - 50, DrawPosition.Y + 100.0f);
@@ -126,6 +126,8 @@ namespace Barotrauma.Items.Components
public void Dock(DockingPort target) public void Dock(DockingPort target)
{ {
if (item.Submarine.DockedTo.Contains(target.item.Submarine)) return;
if (dockingTarget != null) if (dockingTarget != null)
{ {
Undock(); Undock();
@@ -154,6 +156,23 @@ namespace Barotrauma.Items.Components
Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X) : Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X) :
Math.Sign(item.WorldPosition.Y - dockingTarget.item.WorldPosition.Y); Math.Sign(item.WorldPosition.Y - dockingTarget.item.WorldPosition.Y);
dockingTarget.dockingDir = -dockingDir; dockingTarget.dockingDir = -dockingDir;
foreach (WayPoint wp in WayPoint.WayPointList)
{
if (wp.Submarine != item.Submarine || wp.SpawnType != SpawnType.Path) continue;
if (!Submarine.RectContains(item.Rect, wp.Position)) continue;
foreach (WayPoint wp2 in WayPoint.WayPointList)
{
if (wp2.Submarine != dockingTarget.item.Submarine || wp2.SpawnType != SpawnType.Path) continue;
if (!Submarine.RectContains(dockingTarget.item.Rect, wp2.Position)) continue;
wp.linkedTo.Add(wp2);
wp2.linkedTo.Add(wp);
}
}
CreateJoint(false); CreateJoint(false);
} }
@@ -304,6 +323,24 @@ namespace Barotrauma.Items.Components
dockingTarget.item.Submarine.DockedTo.Remove(item.Submarine); dockingTarget.item.Submarine.DockedTo.Remove(item.Submarine);
item.Submarine.DockedTo.Remove(dockingTarget.item.Submarine); item.Submarine.DockedTo.Remove(dockingTarget.item.Submarine);
//remove all waypoint links between this sub and the dockingtarget
foreach (WayPoint wp in WayPoint.WayPointList)
{
if (wp.Submarine != item.Submarine || wp.SpawnType != SpawnType.Path) continue;
for (int i = wp.linkedTo.Count - 1; i >= 0; i--)
{
var wp2 = wp.linkedTo[i] as WayPoint;
if (wp2 == null) continue;
if (wp.Submarine == dockingTarget.item.Submarine)
{
wp.linkedTo.RemoveAt(i);
wp2.linkedTo.Remove(wp);
}
}
}
item.linkedTo.Clear(); item.linkedTo.Clear();
@@ -330,7 +367,7 @@ namespace Barotrauma.Items.Components
gap.Remove(); gap.Remove();
gap = null; gap = null;
} }
if (bodies!=null) if (bodies!=null)
{ {
foreach (Body body in bodies) foreach (Body body in bodies)
+41 -4
View File
@@ -1060,13 +1060,22 @@ namespace Barotrauma
} }
} }
public List<T> GetConnectedComponents<T>() public List<T> GetConnectedComponents<T>(bool recursive = false)
{ {
ConnectionPanel connectionPanel = GetComponent<ConnectionPanel>();
if (connectionPanel == null) return new List<T>();
List<T> connectedComponents = new List<T>(); List<T> connectedComponents = new List<T>();
if (recursive)
{
List<Item> alreadySearched = new List<Item>() {this};
GetConnectedComponentsRecursive<T>(alreadySearched, connectedComponents);
return connectedComponents;
}
ConnectionPanel connectionPanel = GetComponent<ConnectionPanel>();
if (connectionPanel == null) return connectedComponents;
foreach (Connection c in connectionPanel.Connections) foreach (Connection c in connectionPanel.Connections)
{ {
var recipients = c.Recipients; var recipients = c.Recipients;
@@ -1080,6 +1089,34 @@ namespace Barotrauma
return connectedComponents; return connectedComponents;
} }
private void GetConnectedComponentsRecursive<T>(List<Item> alreadySearched, List<T> connectedComponents)
{
alreadySearched.Add(this);
ConnectionPanel connectionPanel = GetComponent<ConnectionPanel>();
if (connectionPanel == null) return;
foreach (Connection c in connectionPanel.Connections)
{
var recipients = c.Recipients;
foreach (Connection recipient in recipients)
{
if (alreadySearched.Contains(recipient.Item)) continue;
var component = recipient.Item.GetComponent<T>();
if (component != null)
{
connectedComponents.Add(component);
}
recipient.Item.GetConnectedComponentsRecursive<T>(alreadySearched, connectedComponents);
}
}
}
public void SendSignal(int stepsTaken, string signal, string connectionName, float power = 0.0f) public void SendSignal(int stepsTaken, string signal, string connectionName, float power = 0.0f)
{ {
stepsTaken++; stepsTaken++;
+9 -1
View File
@@ -895,14 +895,20 @@ namespace Barotrauma
return sub; return sub;
} }
public static bool Unloading
{
get;
private set;
}
public static void Unload() public static void Unload()
{ {
Unloading = true;
Sound.OnGameEnd(); Sound.OnGameEnd();
if (GameMain.LightManager != null) GameMain.LightManager.ClearLights(); if (GameMain.LightManager != null) GameMain.LightManager.ClearLights();
foreach (Submarine sub in loaded) foreach (Submarine sub in loaded)
{ {
sub.Remove(); sub.Remove();
@@ -919,6 +925,8 @@ namespace Barotrauma
Ragdoll.list.Clear(); Ragdoll.list.Clear();
GameMain.World.Clear(); GameMain.World.Clear();
Unloading = false;
} }
public override void Remove() public override void Remove()
+10 -4
View File
@@ -140,7 +140,7 @@ namespace Barotrauma
int iconY = (int)(Math.Floor(iconIndices[(int)spawnType]*IconSize / (float)iconTexture.Width))*IconSize; int iconY = (int)(Math.Floor(iconIndices[(int)spawnType]*IconSize / (float)iconTexture.Width))*IconSize;
int iconSize = ConnectedGap == null && Ladders == null ? IconSize : (int)(IconSize * 1.5f); int iconSize = ConnectedGap == null && Ladders == null ? IconSize : (int)(IconSize * 1.5f);
spriteBatch.Draw(iconTexture, spriteBatch.Draw(iconTexture,
new Rectangle((int)(drawPos.X - iconSize/2), (int)(drawPos.Y - iconSize/2), iconSize, iconSize), new Rectangle((int)(drawPos.X - iconSize/2), (int)(drawPos.Y - iconSize/2), iconSize, iconSize),
new Rectangle(iconX, iconY, IconSize,IconSize), clr); new Rectangle(iconX, iconY, IconSize,IconSize), clr);
@@ -176,7 +176,7 @@ namespace Barotrauma
{ {
editingHUD = CreateEditingHUD(); editingHUD = CreateEditingHUD();
} }
editingHUD.Update((float)Physics.step); editingHUD.Update((float)Physics.step);
editingHUD.Draw(spriteBatch); editingHUD.Draw(spriteBatch);
@@ -321,7 +321,7 @@ namespace Barotrauma
} }
float minDist = 150.0f; float minDist = 150.0f;
float heightFromFloor = 100.0f; float heightFromFloor = 110.0f;
foreach (Hull hull in Hull.hullList) foreach (Hull hull in Hull.hullList)
{ {
@@ -607,7 +607,11 @@ namespace Barotrauma
float dist = Vector2.Distance(wp.Position, Position); float dist = Vector2.Distance(wp.Position, Position);
if (closest == null || dist < closestDist) if (closest == null || dist < closestDist)
{ {
if (Submarine.CheckVisibility(SimPosition, wp.SimPosition) != null) continue; var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, true);
if (body != null)
{
if (body.UserData is Structure) continue;
}
closestDist = dist; closestDist = dist;
closest = wp; closest = wp;
@@ -620,6 +624,8 @@ namespace Barotrauma
private void ConnectTo(WayPoint wayPoint2) private void ConnectTo(WayPoint wayPoint2)
{ {
System.Diagnostics.Debug.Assert(this != wayPoint2);
if (!linkedTo.Contains(wayPoint2)) linkedTo.Add(wayPoint2); if (!linkedTo.Contains(wayPoint2)) linkedTo.Add(wayPoint2);
if (!wayPoint2.linkedTo.Contains(this)) wayPoint2.linkedTo.Add(this); if (!wayPoint2.linkedTo.Contains(this)) wayPoint2.linkedTo.Add(this);
} }