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

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -20,6 +20,11 @@ namespace Barotrauma
{
get;
}
Vector2 WorldPosition
{
get;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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()

View File

@@ -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)

View File

@@ -814,7 +814,7 @@ namespace Barotrauma
}
else
{
notClimbing = targetMovement.X != 0.0f;
notClimbing = Math.Abs(targetMovement.X) > 0.05f;
}
//stop climbing if:

View File

@@ -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()

View File

@@ -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); }

View File

@@ -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);

View File

@@ -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)

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>();
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++;

View File

@@ -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()

View File

@@ -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);
}