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:
@@ -29,12 +29,17 @@ namespace Barotrauma
|
||||
|
||||
public Vector2 SimPosition
|
||||
{
|
||||
get { return Character.AnimController.Limbs[0].SimPosition; }
|
||||
get { return Character.SimPosition; }
|
||||
}
|
||||
|
||||
public Vector2 WorldPosition
|
||||
{
|
||||
get { return Character.WorldPosition; }
|
||||
}
|
||||
|
||||
public Vector2 Velocity
|
||||
{
|
||||
get { return Character.AnimController.Limbs[0].LinearVelocity; }
|
||||
get { return Character.AnimController.RefLimb.LinearVelocity; }
|
||||
}
|
||||
|
||||
public AiState State
|
||||
|
||||
@@ -71,7 +71,33 @@ namespace Barotrauma
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -86,17 +112,20 @@ namespace Barotrauma
|
||||
|
||||
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)
|
||||
{
|
||||
Character.AnimController.TargetMovement = new Vector2( 0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
|
||||
}
|
||||
}
|
||||
|
||||
//unequip diving suit if running out of oxygen
|
||||
if (Character.Oxygen < 50.0f && Character.AnimController.CurrentHull!=null &&
|
||||
Character.AnimController.CurrentHull.OxygenPercentage > 20.0f &&
|
||||
Character.AnimController.CurrentHull.Volume < Character.AnimController.CurrentHull.FullVolume*0.3f)
|
||||
//suit can be taken off if there character is inside a hull and there's air in the room
|
||||
bool canTakeOffSuit = Character.AnimController.CurrentHull != null &&
|
||||
Character.AnimController.CurrentHull.OxygenPercentage > 30.0f &&
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override void OnAttacked(IDamageable attacker, float amount)
|
||||
@@ -172,7 +198,7 @@ namespace Barotrauma
|
||||
{
|
||||
GUI.DrawLine(spriteBatch,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ namespace Barotrauma
|
||||
{
|
||||
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;
|
||||
|
||||
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
|
||||
pos -= CurrentPath.CurrentNode.Submarine.SimPosition;
|
||||
}
|
||||
if (CurrentPath.CurrentNode.Submarine != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -135,17 +145,32 @@ namespace Barotrauma
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -153,41 +178,67 @@ namespace Barotrauma
|
||||
{
|
||||
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;
|
||||
|
||||
bool open = currentPath.CurrentNode != null &&
|
||||
Math.Sign(door.Item.SimPosition.X - host.SimPosition.X) == Math.Sign(currentPath.CurrentNode.SimPosition.X - host.SimPosition.X);
|
||||
|
||||
if (currentPath.CurrentNode==null)
|
||||
WayPoint node = null;
|
||||
WayPoint nextNode = null;
|
||||
|
||||
if (i==0)
|
||||
{
|
||||
open = false;
|
||||
node = currentPath.CurrentNode;
|
||||
nextNode = currentPath.NextNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (door.LinkedGap.isHorizontal)
|
||||
{
|
||||
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);
|
||||
}
|
||||
node = currentPath.PrevNode;
|
||||
nextNode = currentPath.CurrentNode;
|
||||
}
|
||||
|
||||
//toggle the door if it's the previous node and open, or if it's current node and closed
|
||||
if (door.IsOpen != open)
|
||||
if (node == null || node.ConnectedGap == null || node.ConnectedGap.ConnectedDoor == null) continue;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Barotrauma
|
||||
|
||||
if (currentHull != null)
|
||||
{
|
||||
if (currentHull.Volume / currentHull.FullVolume > 0.5f || character.Oxygen < 80.0f)
|
||||
if (NeedsDivingGear())
|
||||
{
|
||||
if (!FindDivingGear(deltaTime)) return;
|
||||
}
|
||||
@@ -131,6 +131,19 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
if (character.Oxygen < 80.0f)
|
||||
@@ -142,8 +155,12 @@ namespace Barotrauma
|
||||
currenthullSafety = GetHullSafety(character.AnimController.CurrentHull, character);
|
||||
priority = 100.0f - currenthullSafety;
|
||||
|
||||
if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f;
|
||||
|
||||
if (NeedsDivingGear())
|
||||
{
|
||||
if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f;
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace Barotrauma
|
||||
|
||||
public bool IgnoreContainedItems;
|
||||
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
|
||||
private bool equip;
|
||||
|
||||
public override bool CanBeCompleted
|
||||
@@ -52,13 +54,8 @@ namespace Barotrauma
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (targetItem == null)
|
||||
{
|
||||
FindTargetItem();
|
||||
if (targetItem == null) return;
|
||||
}
|
||||
|
||||
if (moveToTarget == null) return;
|
||||
FindTargetItem();
|
||||
if (targetItem == null || moveToTarget == null) return;
|
||||
|
||||
if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.PickDistance*2.0f)
|
||||
{
|
||||
@@ -97,9 +94,15 @@ namespace Barotrauma
|
||||
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);
|
||||
|
||||
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++;
|
||||
|
||||
@@ -136,22 +139,12 @@ namespace Barotrauma
|
||||
|
||||
targetItem = item;
|
||||
moveToTarget = rootContainer ?? item;
|
||||
|
||||
AddGoToObjective(moveToTarget);
|
||||
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
private void AddGoToObjective(Item gotoToTarget)
|
||||
{
|
||||
subObjectives.Clear();
|
||||
AddSubObjective(new AIObjectiveGoTo(gotoToTarget, character));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override bool IsDuplicate(AIObjective otherObjective)
|
||||
{
|
||||
AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem;
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace Barotrauma
|
||||
//how long until the path to the target is declared unreachable
|
||||
private float waitUntilPathUnreachable;
|
||||
|
||||
private bool getDivingGearIfNeeded;
|
||||
|
||||
public override bool CanBeCompleted
|
||||
{
|
||||
get
|
||||
@@ -35,23 +37,25 @@ namespace Barotrauma
|
||||
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, "")
|
||||
{
|
||||
this.target = target;
|
||||
this.repeat = repeat;
|
||||
|
||||
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, "")
|
||||
{
|
||||
this.targetPos = simPos;
|
||||
this.repeat = repeat;
|
||||
|
||||
waitUntilPathUnreachable = 5.0f;
|
||||
this.getDivingGearIfNeeded = getDivingGearIfNeeded;
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
@@ -108,7 +112,7 @@ namespace Barotrauma
|
||||
{
|
||||
indoorsSteering.SteeringWander();
|
||||
}
|
||||
else if (indoorsSteering.CurrentPath != null && indoorsSteering.HasOutdoorsNodes)
|
||||
else if (getDivingGearIfNeeded && indoorsSteering.CurrentPath != null && indoorsSteering.HasOutdoorsNodes)
|
||||
{
|
||||
AddSubObjective(new AIObjectiveFindDivingGear(character, true));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -11,8 +12,6 @@ namespace Barotrauma
|
||||
AITarget currentTarget;
|
||||
private float newTargetTimer;
|
||||
|
||||
|
||||
|
||||
public AIObjectiveIdle(Character character) : base(character, "")
|
||||
{
|
||||
|
||||
@@ -23,7 +22,6 @@ namespace Barotrauma
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
|
||||
@@ -53,7 +51,8 @@ namespace Barotrauma
|
||||
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
|
||||
if (pathSteering==null || (pathSteering.CurrentPath != null &&
|
||||
@@ -62,13 +61,28 @@ namespace Barotrauma
|
||||
//steer away from edges of the hull
|
||||
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;
|
||||
character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition);
|
||||
character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition, 2.0f);
|
||||
}
|
||||
|
||||
private AITarget FindRandomTarget()
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma
|
||||
public float F,G,H;
|
||||
|
||||
public List<PathNode> connections;
|
||||
public float[] distances;
|
||||
public List<float> distances;
|
||||
|
||||
public WayPoint Waypoint
|
||||
{
|
||||
@@ -63,15 +63,14 @@ namespace Barotrauma
|
||||
var nodeList = nodes.Values.ToList();
|
||||
foreach (PathNode node in nodeList)
|
||||
{
|
||||
node.distances = new float[node.connections.Count];
|
||||
for (int i = 0; i< node.distances.Length; i++)
|
||||
node.distances = new List<float>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PathFinder
|
||||
@@ -87,9 +86,66 @@ namespace Barotrauma
|
||||
{
|
||||
nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => w.MoveWithLevel != insideSubmarine));
|
||||
|
||||
foreach (WayPoint wp in wayPoints)
|
||||
{
|
||||
wp.linkedTo.CollectionChanged += WaypointLinksChanged;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
|
||||
@@ -100,21 +156,21 @@ namespace Barotrauma
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
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
|
||||
//if (node.Waypoint != null && node.Waypoint.Submarine != null && node.Waypoint.CurrentHull == null)
|
||||
//{
|
||||
// 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;
|
||||
//prefer nodes that are closer to the end position
|
||||
dist += Vector2.Distance(end, nodePos) / 10.0f;
|
||||
|
||||
if (dist<closestDist || startNode==null)
|
||||
{
|
||||
//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;
|
||||
startNode = node;
|
||||
@@ -144,7 +200,11 @@ namespace Barotrauma
|
||||
if (dist < closestDist || endNode == null)
|
||||
{
|
||||
//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;
|
||||
endNode = node;
|
||||
@@ -234,7 +294,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < currNode.connections.Count; i++)
|
||||
{
|
||||
PathNode nextNode = currNode.connections[i];
|
||||
|
||||
|
||||
//a node that hasn't been searched yet
|
||||
if (nextNode.state==0)
|
||||
{
|
||||
@@ -272,7 +332,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (end.state==0)
|
||||
if (end.state == 0 || end.Parent == null)
|
||||
{
|
||||
//path not found
|
||||
return new SteeringPath(true);
|
||||
@@ -290,6 +350,8 @@ namespace Barotrauma
|
||||
pathNode = pathNode.Parent;
|
||||
}
|
||||
|
||||
finalPath.Add(start.Waypoint);
|
||||
|
||||
finalPath.Reverse();
|
||||
|
||||
foreach (WayPoint wayPoint in finalPath)
|
||||
|
||||
@@ -814,7 +814,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
notClimbing = targetMovement.X != 0.0f;
|
||||
notClimbing = Math.Abs(targetMovement.X) > 0.05f;
|
||||
}
|
||||
|
||||
//stop climbing if:
|
||||
|
||||
@@ -74,6 +74,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float FloorY
|
||||
{
|
||||
get { return floorY; }
|
||||
}
|
||||
|
||||
public float Mass
|
||||
{
|
||||
get;
|
||||
@@ -330,14 +335,8 @@ namespace Barotrauma
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lowestLimb.Position.Y < structure.Rect.Y - structure.Rect.Height + stairPosY) return false;
|
||||
}
|
||||
if (lowestLimb.Position.Y < structure.Rect.Y - structure.Rect.Height + stairPosY - 10.0f) return false;
|
||||
|
||||
|
||||
|
||||
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()
|
||||
|
||||
@@ -42,6 +42,11 @@ namespace Barotrauma
|
||||
get { return FarseerPhysics.ConvertUnits.ToSimUnits(position); }
|
||||
}
|
||||
|
||||
public Vector2 WorldPosition
|
||||
{
|
||||
get { return position; }
|
||||
}
|
||||
|
||||
public Vector2 Velocity
|
||||
{
|
||||
get { return new Vector2(velocity.X, velocity.Y); }
|
||||
|
||||
@@ -1143,7 +1143,14 @@ namespace Barotrauma
|
||||
|
||||
Vector2 pos = DrawPosition;
|
||||
pos.Y = -pos.Y;
|
||||
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
AnimController.DebugDraw(spriteBatch);
|
||||
|
||||
if (aiTarget != null) aiTarget.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
if (this == controlled) return;
|
||||
|
||||
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;
|
||||
|
||||
Vector2 healthBarPos = new Vector2(pos.X - 50, DrawPosition.Y + 100.0f);
|
||||
|
||||
@@ -126,6 +126,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void Dock(DockingPort target)
|
||||
{
|
||||
if (item.Submarine.DockedTo.Contains(target.item.Submarine)) return;
|
||||
|
||||
if (dockingTarget != null)
|
||||
{
|
||||
Undock();
|
||||
@@ -154,6 +156,23 @@ namespace Barotrauma.Items.Components
|
||||
Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X) :
|
||||
Math.Sign(item.WorldPosition.Y - dockingTarget.item.WorldPosition.Y);
|
||||
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);
|
||||
}
|
||||
@@ -304,6 +323,24 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
dockingTarget.item.Submarine.DockedTo.Remove(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();
|
||||
|
||||
@@ -330,7 +367,7 @@ namespace Barotrauma.Items.Components
|
||||
gap.Remove();
|
||||
gap = null;
|
||||
}
|
||||
|
||||
|
||||
if (bodies!=null)
|
||||
{
|
||||
foreach (Body body in bodies)
|
||||
|
||||
@@ -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>();
|
||||
|
||||
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)
|
||||
{
|
||||
var recipients = c.Recipients;
|
||||
@@ -1080,6 +1089,34 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
stepsTaken++;
|
||||
|
||||
@@ -895,14 +895,20 @@ namespace Barotrauma
|
||||
return sub;
|
||||
}
|
||||
|
||||
public static bool Unloading
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public static void Unload()
|
||||
{
|
||||
Unloading = true;
|
||||
|
||||
Sound.OnGameEnd();
|
||||
|
||||
if (GameMain.LightManager != null) GameMain.LightManager.ClearLights();
|
||||
|
||||
|
||||
foreach (Submarine sub in loaded)
|
||||
{
|
||||
sub.Remove();
|
||||
@@ -919,6 +925,8 @@ namespace Barotrauma
|
||||
Ragdoll.list.Clear();
|
||||
|
||||
GameMain.World.Clear();
|
||||
|
||||
Unloading = false;
|
||||
}
|
||||
|
||||
public override void Remove()
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace Barotrauma
|
||||
int iconY = (int)(Math.Floor(iconIndices[(int)spawnType]*IconSize / (float)iconTexture.Width))*IconSize;
|
||||
|
||||
int iconSize = ConnectedGap == null && Ladders == null ? IconSize : (int)(IconSize * 1.5f);
|
||||
|
||||
|
||||
spriteBatch.Draw(iconTexture,
|
||||
new Rectangle((int)(drawPos.X - iconSize/2), (int)(drawPos.Y - iconSize/2), iconSize, iconSize),
|
||||
new Rectangle(iconX, iconY, IconSize,IconSize), clr);
|
||||
@@ -176,7 +176,7 @@ namespace Barotrauma
|
||||
{
|
||||
editingHUD = CreateEditingHUD();
|
||||
}
|
||||
|
||||
|
||||
editingHUD.Update((float)Physics.step);
|
||||
editingHUD.Draw(spriteBatch);
|
||||
|
||||
@@ -321,7 +321,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float minDist = 150.0f;
|
||||
float heightFromFloor = 100.0f;
|
||||
float heightFromFloor = 110.0f;
|
||||
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
@@ -607,7 +607,11 @@ namespace Barotrauma
|
||||
float dist = Vector2.Distance(wp.Position, Position);
|
||||
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;
|
||||
closest = wp;
|
||||
@@ -620,6 +624,8 @@ namespace Barotrauma
|
||||
|
||||
private void ConnectTo(WayPoint wayPoint2)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(this != wayPoint2);
|
||||
|
||||
if (!linkedTo.Contains(wayPoint2)) linkedTo.Add(wayPoint2);
|
||||
if (!wayPoint2.linkedTo.Contains(this)) wayPoint2.linkedTo.Add(this);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user