Bunch of crew AI bugfixes/improvements:

- replacing empty oxygen tanks
- fixed characters only fixing leaks in the outer hull
- prioritizing large leaks over small ones
- fixed characters doing nothing after fixing all the leaks (and sinking to the bottom if they happen to be outside)
- more accurate welder aiming
- AIObjectiveGetItem makes the characters go to the cabinet/container an item is inside, not to the actual item (which may not be actually positioned at the container)
This commit is contained in:
Regalis
2016-03-16 17:41:59 +02:00
parent 804510b144
commit d6a57f9533
15 changed files with 228 additions and 163 deletions
@@ -45,16 +45,22 @@ namespace Barotrauma
if (containedItems == null) return;
//check if there's an oxygen tank in the mask
var oxygenTank = Array.Find(containedItems, i => i.Name == "Oxygen Tank" && i.Condition > 0.0f);
var oxygenTank = Array.Find(containedItems, i => i.Name == "Oxygen Tank");
if (oxygenTank != null)
{
//isCompleted = true;
return;
if (oxygenTank.Condition > 0.0f)
{
return;
}
else
{
oxygenTank.Drop();
}
}
if (!(subObjective is AIObjectiveContainItem))
if (!(subObjective is AIObjectiveContainItem) || subObjective.IsCompleted())
{
subObjective = new AIObjectiveContainItem(character, "Oxygen Tank", item.GetComponent<ItemContainer>());
}
@@ -68,6 +74,13 @@ namespace Barotrauma
}
}
public override float GetPriority(Character character)
{
if (character.AnimController.CurrentHull == null) return 100.0f;
return 100.0f - character.Oxygen;
}
public override bool IsDuplicate(AIObjective otherObjective)
{
return otherObjective is AIObjectiveFindDivingGear;
@@ -29,68 +29,41 @@ namespace Barotrauma
protected override void Act(float deltaTime)
{
if (character.AnimController.CurrentHull == null) return;
currenthullSafety = OverrideCurrentHullSafety == null ?
GetHullSafety(character.AnimController.CurrentHull) : (float)OverrideCurrentHullSafety;
if (character.AnimController.CurrentHull == null || currenthullSafety > MinSafety)
{
character.AIController.SteeringManager.SteeringSeek(character.AnimController.CurrentHull.SimPosition);
character.AIController.SelectTarget(null);
goToObjective = null;
return;
}
var currentHull = character.AnimController.CurrentHull;
if (currentHull.Volume / currentHull.FullVolume > 0.5f)
currenthullSafety = OverrideCurrentHullSafety == null ?
GetHullSafety(currentHull) : (float)OverrideCurrentHullSafety;
if (currentHull != null)
{
if (!FindDivingGear(deltaTime)) return;
if (currentHull.Volume / currentHull.FullVolume > 0.5f || character.Oxygen < 80.0f)
{
if (!FindDivingGear(deltaTime)) return;
}
if (currenthullSafety > MinSafety)
{
character.AIController.SteeringManager.SteeringSeek(currentHull.SimPosition, 0.5f);
character.AIController.SelectTarget(null);
goToObjective = null;
return;
}
}
if (searchHullTimer>0.0f)
if (searchHullTimer > 0.0f)
{
searchHullTimer -= deltaTime;
//return;
}
else
{
Hull bestHull = null;
float bestValue = currenthullSafety;
foreach (Hull hull in Hull.hullList)
{
if (hull == character.AnimController.CurrentHull) continue;
if (unreachable.Contains(hull)) continue;
float hullValue = GetHullSafety(hull);
hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.X- hull.Position.X));
hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.Y - hull.Position.Y)*2.0f);
if (bestHull == null || hullValue > bestValue)
{
bestHull = hull;
bestValue = hullValue;
}
}
var bestHull = FindBestHull();
if (bestHull != null)
{
//var path = pathSteering.PathFinder.FindPath(character.SimPosition, bestHull.SimPosition);
//if (pathSteering.CurrentPath == null || (pathSteering.CurrentPath.NextNode==null && pathSteering.CurrentPath.Cost > path.Cost) ||
// pathSteering.CurrentPath.Unreachable || goToObjective==null)
//{
//pathSteering.SetPath(path);
goToObjective = new AIObjectiveGoTo(bestHull, character);
//}
//haracter.AIController.SelectTarget(bestHull.AiTarget);
goToObjective = new AIObjectiveGoTo(bestHull, character);
}
searchHullTimer = SearchHullInterval;
}
@@ -117,43 +90,29 @@ namespace Barotrauma
divingGearObjective.TryComplete(deltaTime);
return divingGearObjective.IsCompleted();
}
//var item = character.Inventory.FindItem("diving");
//if (item == null)
//{
// //get a diving mask/suit first
// if (!(divingGearObjective is AIObjectiveGetItem))
// {
// divingGearObjective = new AIObjectiveGetItem(character, "diving", true);
// }
//}
//else
//{
// var containedItems = item.ContainedItems;
// if (containedItems == null) return true;
private Hull FindBestHull()
{
Hull bestHull = null;
float bestValue = currenthullSafety;
// //check if there's an oxygen tank in the mask
// var oxygenTank = Array.Find(containedItems, i => i.Name == "Oxygen Tank" && i.Condition > 0.0f);
foreach (Hull hull in Hull.hullList)
{
if (hull == character.AnimController.CurrentHull || unreachable.Contains(hull)) continue;
// if (oxygenTank != null) return true;
float hullValue = GetHullSafety(hull);
hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.X - hull.Position.X));
hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.Y - hull.Position.Y) * 2.0f);
if (bestHull == null || hullValue > bestValue)
{
bestHull = hull;
bestValue = hullValue;
}
}
// if (!(divingGearObjective is AIObjectiveContainItem))
// {
// divingGearObjective = new AIObjectiveContainItem(character, "Oxygen Tank", item.GetComponent<ItemContainer>());
// }
//}
//if (divingGearObjective != null)
//{
// divingGearObjective.TryComplete(deltaTime);
// bool isCompleted = divingGearObjective.IsCompleted();
// if (isCompleted) divingGearObjective = null;
// return isCompleted;
//}
//return false;
return bestHull;
}
public override bool IsDuplicate(AIObjective otherObjective)
@@ -163,11 +122,15 @@ namespace Barotrauma
public override float GetPriority(Character character)
{
if (character.AnimController.CurrentHull == null) return 0.0f;
if (character.Oxygen < 80.0f)
{
return 150.0f - character.Oxygen;
}
if (character.AnimController.CurrentHull == null) return 5.0f;
currenthullSafety = GetHullSafety(character.AnimController.CurrentHull);
priority = 100.0f - currenthullSafety;
if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f;
return priority;
@@ -175,6 +138,8 @@ namespace Barotrauma
private float GetHullSafety(Hull hull)
{
if (hull == null) return 0.0f;
float waterPercentage = (hull.Volume / hull.FullVolume)*100.0f;
float fireAmount = 0.0f;
@@ -185,7 +150,7 @@ namespace Barotrauma
float safety = 100.0f - fireAmount;
if (waterPercentage > 30.0f && character.Oxygen<80.0f) safety -= waterPercentage;
if (waterPercentage > 30.0f && character.OxygenAvailable<=0.0f) safety -= waterPercentage;
if (hull.OxygenPercentage < 30.0f) safety -= (30.0f-hull.OxygenPercentage)*5.0f;
return safety;
@@ -63,14 +63,15 @@ namespace Barotrauma
}
}
if (Vector2.Distance(character.Position, leak.Position) > 300.0f)
var repairTool = weldingTool.GetComponent<RepairTool>();
if (repairTool == null) return;
if (Vector2.Distance(character.WorldPosition, leak.WorldPosition) > 300.0f)
{
AddSubObjective(new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character));
}
else
{
var repairTool = weldingTool.GetComponent<RepairTool>();
if (repairTool == null) return;
AddSubObjective(new AIObjectiveOperateItem(repairTool, character, "", leak));
}
}
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using System;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
@@ -10,6 +11,8 @@ namespace Barotrauma
private float updateGapListTimer;
private AIObjectiveIdle idleObjective;
private List<AIObjectiveFixLeak> objectiveList;
public AIObjectiveFixLeaks(Character character)
@@ -36,13 +39,19 @@ namespace Barotrauma
if (objectiveList.Any())
{
objectiveList[objectiveList.Count-1].TryComplete(deltaTime);
objectiveList[objectiveList.Count - 1].TryComplete(deltaTime);
if (!objectiveList[objectiveList.Count-1].CanBeCompleted || objectiveList[objectiveList.Count-1].IsCompleted())
if (!objectiveList[objectiveList.Count - 1].CanBeCompleted ||
objectiveList[objectiveList.Count - 1].IsCompleted())
{
objectiveList.RemoveAt(objectiveList.Count - 1);
}
}
else
{
if (idleObjective == null) idleObjective = new AIObjectiveIdle(character);
idleObjective.TryComplete(deltaTime);
}
}
private void UpdateGapList()
@@ -51,13 +60,14 @@ namespace Barotrauma
foreach (Gap gap in Gap.GapList)
{
if (gap.IsRoomToRoom || gap.ConnectedDoor != null || gap.Open < 0.1f) continue;
if (gap.ConnectedWall == null) continue;
if (gap.ConnectedDoor != null || gap.Open <= 0.0f) continue;
float dist = Vector2.DistanceSquared(character.WorldPosition, gap.WorldPosition);
float gapPriority = GetGapFixPriority(gap);
int index = 0;
while (index<objectiveList.Count &&
Vector2.DistanceSquared(objectiveList[index].Leak.WorldPosition, character.WorldPosition)>dist)
while (index < objectiveList.Count &&
GetGapFixPriority(objectiveList[index].Leak) < gapPriority)
{
index++;
}
@@ -66,6 +76,23 @@ namespace Barotrauma
}
}
private float GetGapFixPriority(Gap gap)
{
if (gap == null) return 0.0f;
//larger gap -> higher priority
float gapPriority = (gap.isHorizontal ? gap.Rect.Width : gap.Rect.Height) * gap.Open;
//prioritize gaps that are close
gapPriority /= Math.Max(Vector2.Distance(character.WorldPosition, gap.WorldPosition), 1.0f);
//gaps to outside are much higher priority
if (!gap.IsRoomToRoom) gapPriority *= 10.0f;
return gapPriority;
}
public override bool IsDuplicate(AIObjective otherObjective)
{
return otherObjective is AIObjectiveFixLeaks;
@@ -11,7 +11,7 @@ namespace Barotrauma
{
private string itemName;
private Item targetItem;
private Item targetItem, moveToTarget;
private int currSearchIndex;
@@ -52,83 +52,96 @@ namespace Barotrauma
protected override void Act(float deltaTime)
{
if (targetItem != null)
if (targetItem == null)
{
if (Vector2.Distance(character.Position, targetItem.Position) < targetItem.PickDistance)
FindTargetItem();
if (targetItem == null) return;
}
if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.PickDistance*2.0f)
{
int targetSlot = -1;
if (equip)
{
int targetSlot = -1;
if (equip)
var pickable = targetItem.GetComponent<Pickable>();
//check if all the slots required by the item are free
foreach (LimbSlot slots in pickable.AllowedSlots)
{
var pickable = targetItem.GetComponent<Pickable>();
//check if all the slots required by the item are free
foreach (LimbSlot slots in pickable.AllowedSlots)
{
if (slots.HasFlag(LimbSlot.Any)) continue;
if (slots.HasFlag(LimbSlot.Any)) continue;
for (int i = 0; i<character.Inventory.Items.Length; i++)
{
//slot not needed by the item, continue
if (!slots.HasFlag(CharacterInventory.limbSlots[i])) continue;
for (int i = 0; i<character.Inventory.Items.Length; i++)
{
//slot not needed by the item, continue
if (!slots.HasFlag(CharacterInventory.limbSlots[i])) continue;
targetSlot = i;
targetSlot = i;
//slot free, continue
if (character.Inventory.Items[i] == null) continue;
//slot free, continue
if (character.Inventory.Items[i] == null) continue;
//try to move the existing item to LimbSlot.Any and continue if successful
if (character.Inventory.TryPutItem(character.Inventory.Items[i], new List<LimbSlot>() { LimbSlot.Any }, false)) continue;
//try to move the existing item to LimbSlot.Any and continue if successful
if (character.Inventory.TryPutItem(character.Inventory.Items[i], new List<LimbSlot>() { LimbSlot.Any }, false)) continue;
//if everything else fails, simply drop the existing item
character.Inventory.Items[i].Drop();
}
//if everything else fails, simply drop the existing item
character.Inventory.Items[i].Drop();
}
}
targetItem.Pick(character, false, true);
if (targetSlot>-1 && character.Inventory.IsInLimbSlot(targetItem, LimbSlot.Any))
{
character.Inventory.TryPutItem(targetItem, targetSlot, true, false);
}
}
return;
targetItem.Pick(character, false, true);
if (targetSlot > -1 && character.Inventory.IsInLimbSlot(targetItem, LimbSlot.Any))
{
character.Inventory.TryPutItem(targetItem, targetSlot, true, false);
}
}
else if (!subObjectives.Any())
{
AddGoToObjective(targetItem);
}
}
for (int i = 0; i<10 && currSearchIndex<Item.ItemList.Count-2; i++)
/// <summary>
/// searches for an item that matches the desired item and adds a goto subobjective if one is found
/// </summary>
private void FindTargetItem()
{
float currDist = moveToTarget == null ? 0.0f : Vector2.DistanceSquared(moveToTarget.Position, character.Position);
for (int i = 0; i < 5 && currSearchIndex < Item.ItemList.Count - 2; i++)
{
currSearchIndex++;
//don't try to get items from outside the sub
if (Item.ItemList[currSearchIndex].CurrentHull == null) continue;
var item = Item.ItemList[currSearchIndex];
if (!Item.ItemList[currSearchIndex].HasTag(itemName) && Item.ItemList[currSearchIndex].Name != itemName) continue;
if (IgnoreContainedItems && Item.ItemList[currSearchIndex].Container != null) continue;
if (Item.ItemList[currSearchIndex].ParentInventory is CharacterInventory) continue;
if (item.CurrentHull == null || item.Condition <= 0.0f) continue;
if (IgnoreContainedItems && item.Container != null) continue;
if (item.Name != itemName && !item.HasTag(itemName)) continue;
if (item.ParentInventory is CharacterInventory) continue;
//ignore if item is further away than the currently targeted item
Item rootContainer = item.GetRootContainer();
if (moveToTarget != null && Vector2.DistanceSquared((rootContainer ?? item).Position, character.Position) > currDist) continue;
targetItem = Item.ItemList[currSearchIndex];
targetItem = item;
moveToTarget = rootContainer ?? item;
AddGoToObjective(targetItem);
AddGoToObjective(moveToTarget);
return;
}
if (currSearchIndex >= Item.ItemList.Count) canBeCompleted = false;
//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 item)
private void AddGoToObjective(Item gotoToTarget)
{
Item moveToTarget = item;
while (moveToTarget.Container != null)
{
moveToTarget = moveToTarget.Container;
}
AddSubObjective(new AIObjectiveGoTo(moveToTarget, character));
subObjectives.Clear();
AddSubObjective(new AIObjectiveGoTo(gotoToTarget, character));
}
public override bool IsDuplicate(AIObjective otherObjective)
{
@@ -104,10 +104,13 @@ namespace Barotrauma
var indoorsSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
if (indoorsSteering.CurrentPath != null && indoorsSteering.HasOutdoorsNodes)
if (indoorsSteering.CurrentPath.Unreachable)
{
AddSubObjective(new AIObjectiveGetItem(character, "Diving Suit", true));
indoorsSteering.SteeringWander();
}
else if (indoorsSteering.CurrentPath != null && indoorsSteering.HasOutdoorsNodes)
{
AddSubObjective(new AIObjectiveFindDivingGear(character, true));
}
}
}
@@ -40,8 +40,11 @@ namespace Barotrauma
if (currentTarget != null)
{
var path = pathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition);
if (path.Cost > 200.0f) return;
Vector2 pos = character.SimPosition;
if (character != null && character.Submarine == null) pos -= Submarine.Loaded.SimPosition;
var path = pathSteering.PathFinder.FindPath(pos, currentTarget.SimPosition);
if (path.Cost > 200.0f && character.AnimController.CurrentHull!=null) return;
pathSteering.SetPath(path);
}
@@ -114,7 +114,7 @@ namespace Barotrauma
if (dist<closestDist || startNode==null)
{
//if searching for a path inside the sub, make sure the waypoint is visible
if (insideSubmarine && node.Waypoint.CurrentHull != null && Submarine.CheckVisibility(start, node.Waypoint.SimPosition) != null) continue;
if (insideSubmarine && Submarine.CheckVisibility(start, node.Waypoint.SimPosition) != null) continue;
closestDist = dist;
startNode = node;
@@ -160,7 +160,7 @@ namespace Barotrauma
var path = FindPath(startNode,endNode);
sw.Stop();
System.Diagnostics.Debug.WriteLine("findpath: " + sw.ElapsedTicks);
System.Diagnostics.Debug.WriteLine("findpath: " + sw.ElapsedMilliseconds+" ms");
return path;
}
+2 -2
View File
@@ -173,7 +173,7 @@ namespace Barotrauma
keyboardDispatcher = new KeyboardDispatcher(window);
}
public T GetChild<T>()
public T GetChild<T>() where T : GUIComponent
{
foreach (GUIComponent child in children)
{
@@ -182,7 +182,7 @@ namespace Barotrauma
return default(T);
}
public GUIComponent GetChild(object obj)
{
foreach (GUIComponent child in children)
@@ -220,7 +220,29 @@ namespace Barotrauma.Items.Components
Gap leak = objective.OperateTarget as Gap;
if (leak == null) return true;
if (Vector2.Distance(leak.WorldPosition, item.WorldPosition) > range) return true;
float dist = Vector2.Distance(leak.WorldPosition, item.WorldPosition);
//too far away -> consider this done and hope the AI is smart enough to move closer
if (dist > range*5.0f) return true;
//steer closer if almost in range
if (dist > range)
{
Vector2 standPos = leak.isHorizontal ?
new Vector2(Math.Sign(item.WorldPosition.X - leak.WorldPosition.X), 0.0f)
: new Vector2(0.0f, Math.Sign(item.WorldPosition.Y - leak.WorldPosition.Y));
standPos = leak.WorldPosition + standPos * range;
character.AIController.SteeringManager.SteeringManual(deltaTime, (standPos - character.WorldPosition) / 1000.0f);
}
else
{
//close enough -> stop moving
character.AIController.SteeringManager.Reset();
}
character.CursorPosition = leak.Position;
character.SetInput(InputType.Aim, false, true);
@@ -171,7 +171,7 @@ namespace Barotrauma.Items.Components
}
}
ApplyStatusEffects(ActionType.OnUse, 1.0f, null);
ApplyStatusEffects(ActionType.OnUse, 1.0f);
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
+14
View File
@@ -466,6 +466,20 @@ namespace Barotrauma
}
return CurrentHull;
}
public Item GetRootContainer()
{
if (Container == null) return null;
Item rootContainer = Container;
while (rootContainer.Container != null)
{
rootContainer = rootContainer.Container;
}
return rootContainer;
}
public void AddTag(string tag)
{
+2
View File
@@ -38,6 +38,8 @@ namespace Barotrauma
public Door ConnectedDoor;
public Structure ConnectedWall;
public Vector2 LerpedFlowForce
{
get { return lerpedFlowForce; }
+4 -2
View File
@@ -577,8 +577,9 @@ namespace Barotrauma
gapRect.Width += 20;
gapRect.Height += 20;
sections[sectionIndex].gap = new Gap(gapRect, !isHorizontal, Submarine);
if(CastShadow)
GenerateConvexHull();
sections[sectionIndex].gap.ConnectedWall = this;
if(CastShadow) GenerateConvexHull();
}
}
@@ -758,6 +759,7 @@ namespace Barotrauma
if (s.GapIndex == -1) continue;
s.gap = FindEntityByID((ushort)s.GapIndex) as Gap;
if (s.gap != null) s.gap.ConnectedWall = this;
}
}
+3 -3
View File
@@ -299,7 +299,7 @@ namespace Barotrauma
wayPoint.Remove();
}
float minDist = 200.0f;
float minDist = 150.0f;
float heightFromFloor = 100.0f;
foreach (Hull hull in Hull.hullList)
@@ -324,7 +324,7 @@ namespace Barotrauma
prevWaypoint = wayPoint;
}
}
float outSideWaypointInterval = 200.0f;
int outsideWaypointDist = 100;
@@ -521,7 +521,7 @@ namespace Barotrauma
for (int dir = -1; dir <= 1; dir += 2)
{
float tolerance = gap.IsRoomToRoom ? 30.0f : outSideWaypointInterval / 2.0f;
float tolerance = gap.IsRoomToRoom ? 50.0f : outSideWaypointInterval / 2.0f;
WayPoint closest = wayPoint.FindClosest(dir, true, new Vector2(-tolerance, tolerance));
if (closest == null) continue;
wayPoint.ConnectTo(closest);