Human AI improvements & fixes:

- Replaced item name comparisons with Prefab.NameMatches (-> item names can be changed without breaking the AIs).
- Having an AIObjective set as the current order of the character doesn't automatically cause it to have a high priority. For example, the order to fix leaks has a low priority if there are no leaks to fix.
- AIObjectiveFixLeaks makes sure the character is wearing a diving suit before going to fix a leak. The characters used to run in and out of flooded rooms because the AIObjectiveFindSafety objective would become active as soon as the character entered the room, causing the character to run out, and then immediately run back because they are no longer in immediate danger of drowning, making the FixLeaks objective the most high-priority one.
- Characters attempt to find a room with no water in AIObjectiveIdle even if the character is wearing a diving suit.
- AIObjectiveFindSafety considers flooded rooms dangerous even if the character is wearing a diving suit (-> the character attempts to go into a more dry room instead of happily idling in the flooded one).
- Distance to a hull doesn't decrease its desirability nearly as much in AIObjectiveFindSafety (-> fixes characters not bothering to move into a non-flooded room if it's far away).
- AIObjectiveOperateItem makes sure the item is equipped before using it (-> characters can't weld leaks with the welder in their inventory).
This commit is contained in:
Joonas Rikkonen
2017-12-21 19:49:26 +02:00
parent 9ed2871ede
commit 604fc65154
17 changed files with 310 additions and 169 deletions

View File

@@ -64,7 +64,7 @@ namespace Barotrauma
protected override Vector2 DoSteeringSeek(Vector2 target, float speed = 1)
{
//find a new path if one hasn't been found yet or the target is different from the current target
if (currentPath == null || Vector2.Distance(target, currentTarget)>1.0f || findPathTimer < -5.0f)
if (currentPath == null || Vector2.Distance(target, currentTarget) > 1.0f || findPathTimer < -1.0f)
{
if (findPathTimer > 0.0f) return Vector2.Zero;
@@ -73,21 +73,21 @@ namespace Barotrauma
if (character != null && character.Submarine == null)
{
var targetHull = Hull.FindHull(FarseerPhysics.ConvertUnits.ToDisplayUnits(target), null, false);
if (targetHull!=null && targetHull.Submarine != null)
if (targetHull != null && targetHull.Submarine != null)
{
pos -= targetHull.SimPosition;
}
}
}
currentPath = pathFinder.FindPath(pos, target);
findPathTimer = Rand.Range(1.0f,1.2f);
findPathTimer = Rand.Range(1.0f, 1.2f);
return DiffToCurrentNode();
}
Vector2 diff = DiffToCurrentNode();
var collider = character.AnimController.Collider;
//if not in water and the waypoint is between the top and bottom of the collider, no need to move vertically
if (!character.AnimController.InWater &&
@@ -104,7 +104,7 @@ namespace Barotrauma
private Vector2 DiffToCurrentNode()
{
if (currentPath == null || currentPath.Finished || currentPath.Unreachable) return Vector2.Zero;
if (currentPath == null || currentPath.Unreachable) return Vector2.Zero;
if (currentPath.Finished)
{
@@ -113,8 +113,8 @@ namespace Barotrauma
{
//todo: take multiple subs into account
pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition;
}
return currentTarget-pos2;
}
return currentTarget - pos2;
}
if (canOpenDoors && !character.LockHands) CheckDoorsInPath();

View File

@@ -4,21 +4,13 @@ using System.Linq;
namespace Barotrauma
{
class AIObjective
abstract class AIObjective
{
protected List<AIObjective> subObjectives;
protected readonly List<AIObjective> subObjectives;
protected float priority;
protected Character character;
protected readonly Character character;
protected string option;
public virtual bool IsCompleted()
{
return false;
}
public virtual bool CanBeCompleted
{
get { return true; }
@@ -27,15 +19,12 @@ namespace Barotrauma
public string Option
{
get { return option; }
}
}
public AIObjective(Character character, string option)
{
subObjectives = new List<AIObjective>();
this.character = character;
this.option = option;
#if DEBUG
@@ -60,8 +49,6 @@ namespace Barotrauma
Act(deltaTime);
}
protected virtual void Act(float deltaTime) { }
public void AddSubObjective(AIObjective objective)
{
if (subObjectives.Any(o => o.IsDuplicate(objective))) return;
@@ -69,19 +56,20 @@ namespace Barotrauma
subObjectives.Add(objective);
}
public virtual float GetPriority(Character character)
public AIObjective GetCurrentSubObjective()
{
return 0.0f;
AIObjective currentSubObjective = this;
while (currentSubObjective.subObjectives.Count > 0)
{
currentSubObjective = subObjectives[0];
}
return currentSubObjective;
}
public virtual bool IsDuplicate(AIObjective otherObjective)
{
#if DEBUG
throw new NotImplementedException();
#else
return (this.GetType() == otherObjective.GetType());
#endif
}
protected abstract void Act(float deltaTime);
public abstract bool IsCompleted();
public abstract float GetPriority(AIObjectiveManager objectiveManager);
public abstract bool IsDuplicate(AIObjective otherObjective);
}
}

View File

@@ -33,7 +33,6 @@ namespace Barotrauma
}
coolDownTimer = CoolDown;
}
protected override void Act(float deltaTime)
@@ -41,17 +40,24 @@ namespace Barotrauma
coolDownTimer -= deltaTime;
var weapon = character.Inventory.FindItem("weapon");
if (weapon==null)
if (weapon == null)
{
Escape(deltaTime);
}
else
{
//TODO: make sure the weapon is ready to use (projectiles/batteries loaded)
if (!character.SelectedItems.Contains(weapon))
{
character.Inventory.TryPutItem(weapon, 3, false, character);
weapon.Equip(character);
if (character.Inventory.TryPutItem(weapon, 3, false, character))
{
weapon.Equip(character);
}
else
{
return;
}
}
character.CursorPosition = enemy.Position;
character.SetInput(InputType.Aim, false, true);
@@ -99,8 +105,13 @@ namespace Barotrauma
return enemy.IsDead || coolDownTimer <= 0.0f;
}
public override float GetPriority(Character character)
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
//clamp the strength to the health of this character
//(it doesn't make a difference whether the enemy does 200 or 600 damage, it's one hit kill anyway)

View File

@@ -19,15 +19,6 @@ namespace Barotrauma
{
this.itemName = itemName;
this.container = container;
//check if the container has room for more items
//canBeCompleted = false;
//foreach (Item contained in container.inventory.Items)
//{
// if (contained != null) continue;
// canBeCompleted = true;
// break;
//}
}
public override bool IsCompleted()
@@ -35,6 +26,16 @@ namespace Barotrauma
return isCompleted || container.Inventory.FindItem(itemName)!=null;
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 1.0f;
}
protected override void Act(float deltaTime)
{
if (isCompleted) return;

View File

@@ -15,7 +15,7 @@ namespace Barotrauma
if (item == null) return false;
var containedItems = item.ContainedItems;
var oxygenTank = Array.Find(containedItems, i => i.Name == "Oxygen Tank" && i.Condition > 0.0f);
var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank") && i.Condition > 0.0f);
return oxygenTank != null;
}
@@ -42,7 +42,7 @@ 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");
var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank"));
if (oxygenTank != null)
{
@@ -66,15 +66,18 @@ namespace Barotrauma
if (subObjective != null)
{
subObjective.TryComplete(deltaTime);
//isCompleted = subObjective.IsCompleted();
}
}
public override float GetPriority(Character character)
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (character.AnimController.CurrentHull == null) return 100.0f;
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 100.0f - character.Oxygen;
}

View File

@@ -1,4 +1,5 @@
using System;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -27,39 +28,23 @@ namespace Barotrauma
unreachable = new List<Hull>();
}
public override bool IsCompleted()
{
return false;
}
protected override void Act(float deltaTime)
{
var currentHull = character.AnimController.CurrentHull;
currenthullSafety = OverrideCurrentHullSafety == null ?
GetHullSafety(currentHull, character) : (float)OverrideCurrentHullSafety;
if (currentHull != null)
if (NeedsDivingGear())
{
if (NeedsDivingGear())
{
if (!FindDivingGear(deltaTime)) return;
}
if (currenthullSafety > MinSafety)
{
if (Math.Abs(currentHull.WorldPosition.X - character.WorldPosition.X) > 100.0f)
{
character.AIController.SteeringManager.SteeringSeek(currentHull.SimPosition, 0.5f);
}
else
{
character.AIController.SteeringManager.Reset();
}
character.AIController.SelectTarget(null);
goToObjective = null;
return;
}
if (!FindDivingGear(deltaTime)) return;
}
if (searchHullTimer > 0.0f)
{
@@ -79,7 +64,7 @@ namespace Barotrauma
if (goToObjective != null)
{
var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
if (pathSteering!=null && pathSteering.CurrentPath!= null &&
if (pathSteering != null && pathSteering.CurrentPath != null &&
pathSteering.CurrentPath.Unreachable && !unreachable.Contains(goToObjective.Target))
{
unreachable.Add(goToObjective.Target as Hull);
@@ -92,7 +77,7 @@ namespace Barotrauma
private bool FindDivingGear(float deltaTime)
{
if (divingGearObjective==null)
if (divingGearObjective == null)
{
divingGearObjective = new AIObjectiveFindDivingGear(character, false);
}
@@ -113,8 +98,9 @@ namespace Barotrauma
if (hull == character.AnimController.CurrentHull || unreachable.Contains(hull)) continue;
float hullValue = GetHullSafety(hull, character);
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);
//slight preference over hulls that are closer
hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.X - hull.Position.X)) * 0.1f;
hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.Y - hull.Position.Y)) * 0.2f;
if (bestHull == null || hullValue > bestValue)
{
@@ -144,13 +130,13 @@ namespace Barotrauma
return false;
}
public override float GetPriority(Character character)
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (character.Oxygen < 80.0f)
{
return 150.0f - character.Oxygen;
}
if (character.AnimController.CurrentHull == null) return 5.0f;
currenthullSafety = GetHullSafety(character.AnimController.CurrentHull, character);
priority = 100.0f - currenthullSafety;
@@ -171,13 +157,12 @@ namespace Barotrauma
}
}
}
if (NeedsDivingGear())
{
if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f;
}
return priority;
}
@@ -185,11 +170,28 @@ namespace Barotrauma
{
if (hull == null) return 0.0f;
float safety = 100.0f;
float waterPercentage = (hull.WaterVolume / hull.Volume) * 100.0f;
if (hull.LethalPressure > 0.0f && character.PressureProtection <= 0.0f)
{
safety -= 100.0f;
}
else if (character.OxygenAvailable <= 0.0f)
{
safety -= waterPercentage;
}
else
{
safety -= waterPercentage * 0.1f;
}
if (hull.OxygenPercentage < 30.0f) safety -= (30.0f - hull.OxygenPercentage) * 5.0f;
if (safety <= 0.0f) return 0.0f;
float fireAmount = 0.0f;
var nearbyHulls = hull.GetConnectedHulls(3);
foreach (Hull hull2 in nearbyHulls)
{
foreach (FireSource fireSource in hull2.FireSources)
@@ -204,13 +206,9 @@ namespace Barotrauma
}
}
}
safety -= fireAmount;
float safety = 100.0f - fireAmount;
if (waterPercentage > 30.0f && character.OxygenAvailable <= 0.0f) safety -= waterPercentage;
if (hull.OxygenPercentage < 30.0f) safety -= (30.0f - hull.OxygenPercentage) * 5.0f;
return safety;
return MathHelper.Clamp(safety, 0.0f, 100.0f);
}
}
}

View File

@@ -7,7 +7,9 @@ namespace Barotrauma
{
class AIObjectiveFixLeak : AIObjective
{
private Gap leak;
private readonly Gap leak;
private AIObjectiveGoTo gotoObjective;
public Gap Leak
{
@@ -20,15 +22,20 @@ namespace Barotrauma
this.leak = leak;
}
public override float GetPriority(Character character)
public override bool IsCompleted()
{
return leak.Open <= 0.0f || leak.Removed;
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (leak.Open == 0.0f) return 0.0f;
float leakSize = (leak.IsHorizontal ? leak.Rect.Height : leak.Rect.Width) * Math.Max(leak.Open, 0.1f);
float dist = Vector2.DistanceSquared(character.SimPosition, leak.SimPosition);
dist = Math.Max(dist/100.0f, 1.0f);
return Math.Min(leakSize/dist, 40.0f);
dist = Math.Max(dist / 100.0f, 1.0f);
return Math.Min(leakSize / dist, 40.0f);
}
public override bool IsDuplicate(AIObjective otherObjective)
@@ -44,7 +51,7 @@ namespace Barotrauma
if (weldingTool == null)
{
subObjectives.Add(new AIObjectiveGetItem(character, "Welding Tool", true));
AddSubObjective(new AIObjectiveGetItem(character, "Welding Tool", true));
return;
}
else
@@ -52,25 +59,31 @@ namespace Barotrauma
var containedItems = weldingTool.ContainedItems;
if (containedItems == null) return;
var fuelTank = Array.Find(containedItems, i => i.Name == "Welding Fuel Tank" && i.Condition > 0.0f);
var fuelTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Welding Fuel Tank") && i.Condition > 0.0f);
if (fuelTank == null)
{
AddSubObjective(new AIObjectiveContainItem(character, "Welding Fuel Tank", weldingTool.GetComponent<ItemContainer>()));
return;
}
}
var repairTool = weldingTool.GetComponent<RepairTool>();
if (repairTool == null) return;
if (Vector2.Distance(character.WorldPosition, leak.WorldPosition) > 300.0f)
Vector2 standPosition = GetStandPosition();
if (Vector2.DistanceSquared(character.WorldPosition, leak.WorldPosition) > 100.0f * 100.0f)
{
AddSubObjective(new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character));
var gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(standPosition), character);
if (!gotoObjective.IsCompleted())
{
AddSubObjective(gotoObjective);
return;
}
}
else
{
AddSubObjective(new AIObjectiveOperateItem(repairTool, character, "", leak));
}
AddSubObjective(new AIObjectiveOperateItem(repairTool, character, "", true, leak));
}
private Vector2 GetStandPosition()

View File

@@ -7,38 +7,80 @@ namespace Barotrauma
{
class AIObjectiveFixLeaks : AIObjective
{
const float UpdateGapListInterval = 10.0f;
const float UpdateGapListInterval = 5.0f;
private float updateGapListTimer;
private double lastGapUpdate;
private AIObjectiveIdle idleObjective;
private AIObjectiveFindDivingGear findDivingGear;
private List<AIObjectiveFixLeak> objectiveList;
public AIObjectiveFixLeaks(Character character)
: base (character, "")
{
objectiveList = new List<AIObjectiveFixLeak>();
}
public override bool IsCompleted()
{
return false;
if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null)
{
UpdateGapList();
lastGapUpdate = Timing.TotalTime;
}
return objectiveList.Count == 0;
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null)
{
UpdateGapList();
lastGapUpdate = Timing.TotalTime;
}
float priority = 0.0f;
foreach (AIObjectiveFixLeak fixObjective in objectiveList)
{
//gaps from outside to inside significantly increase the priority
if (!fixObjective.Leak.IsRoomToRoom)
{
priority = Math.Max(priority + fixObjective.Leak.Open * 100.0f, 50.0f);
}
else
{
priority += fixObjective.Leak.Open * 10.0f;
}
if (priority >= 100.0f) break;
}
return Math.Min(priority, 100.0f);
}
protected override void Act(float deltaTime)
{
updateGapListTimer -= deltaTime;
if (updateGapListTimer<=0.0f)
if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null)
{
UpdateGapList();
updateGapListTimer = UpdateGapListInterval;
lastGapUpdate = Timing.TotalTime;
}
if (objectiveList.Any())
{
if (!objectiveList[objectiveList.Count - 1].Leak.IsRoomToRoom)
{
if (findDivingGear == null) findDivingGear = new AIObjectiveFindDivingGear(character, true);
if (!findDivingGear.IsCompleted() && findDivingGear.CanBeCompleted)
{
findDivingGear.TryComplete(deltaTime);
return;
}
}
objectiveList[objectiveList.Count - 1].TryComplete(deltaTime);
if (!objectiveList[objectiveList.Count - 1].CanBeCompleted ||
@@ -56,6 +98,7 @@ namespace Barotrauma
private void UpdateGapList()
{
if (objectiveList == null) objectiveList = new List<AIObjectiveFixLeak>();
objectiveList.Clear();
foreach (Gap gap in Gap.GapList)

View File

@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System;
namespace Barotrauma
{
@@ -26,6 +27,16 @@ namespace Barotrauma
get { return canBeCompleted; }
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 1.0f;
}
public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false)
: base(character, "")
{

View File

@@ -18,6 +18,16 @@ namespace Barotrauma
private bool getDivingGearIfNeeded;
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 1.0f;
}
public override bool CanBeCompleted
{
get
@@ -93,7 +103,7 @@ namespace Barotrauma
}
}
if (Vector2.Distance(currTargetPos, character.SimPosition) < 1.0f)
if (Vector2.DistanceSquared(currTargetPos, character.SimPosition) < 0.5f * 0.5f)
{
character.AIController.SteeringManager.Reset();
character.AnimController.TargetDir = currTargetPos.X > character.SimPosition.X ? Direction.Right : Direction.Left;
@@ -104,7 +114,7 @@ namespace Barotrauma
var indoorsSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
if (indoorsSteering.CurrentPath==null || indoorsSteering.CurrentPath.Unreachable)
if (indoorsSteering.CurrentPath == null || indoorsSteering.CurrentPath.Unreachable)
{
indoorsSteering.SteeringWander();
}
@@ -130,7 +140,7 @@ namespace Barotrauma
if (item.IsInsideTrigger(character.WorldPosition)) completed = true;
}
completed = completed || Vector2.Distance(target != null ? target.SimPosition : targetPos, character.SimPosition) < allowedDistance;
completed = completed || Vector2.DistanceSquared(target != null ? target.SimPosition : targetPos, character.SimPosition) < allowedDistance * allowedDistance;
if (completed) character.AIController.SteeringManager.Reset();

View File

@@ -9,15 +9,21 @@ namespace Barotrauma
{
const float WallAvoidDistance = 150.0f;
AITarget currentTarget;
private AITarget currentTarget;
private float newTargetTimer;
private AIObjectiveFindSafety findSafety;
public AIObjectiveIdle(Character character) : base(character, "")
{
}
public override float GetPriority(Character character)
public override bool IsCompleted()
{
return false;
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{
return 1.0f;
}
@@ -31,6 +37,14 @@ namespace Barotrauma
{
return;
}
if (character.AnimController.InWater)
{
//attempt to find a safer place if in water
if (findSafety == null) findSafety = new AIObjectiveFindSafety(character);
findSafety.TryComplete(deltaTime);
return;
}
if (newTargetTimer <= 0.0f)
{
@@ -92,17 +106,10 @@ namespace Barotrauma
return;
}
}
if (character.AnimController.InWater)
{
character.AIController.SteeringManager.SteeringManual(deltaTime, new Vector2(0.0f, 0.5f));
}
else
{
character.AIController.SteeringManager.SteeringWander();
//reset vertical steering to prevent dropping down from platforms etc
character.AIController.SteeringManager.ResetY();
}
character.AIController.SteeringManager.SteeringWander();
//reset vertical steering to prevent dropping down from platforms etc
character.AIController.SteeringManager.ResetY();
return;
}

View File

@@ -11,15 +11,17 @@ namespace Barotrauma
private Character character;
private AIObjective currentObjective;
private AIObjective currentOrder;
public AIObjective CurrentOrder
{
get { return currentOrder; }
}
public AIObjective CurrentObjective
{
get
{
if (currentObjective != null) return currentObjective;
return objectives.Any() ? objectives[0] : null;
}
get;
private set;
}
public AIObjectiveManager(Character character)
@@ -47,8 +49,13 @@ namespace Barotrauma
public float GetCurrentPriority(Character character)
{
if (currentObjective != null) return OrderPriority;
return (CurrentObjective == null) ? 0.0f : CurrentObjective.GetPriority(character);
if (CurrentOrder != null &&
(objectives.Count == 0 || currentOrder.GetPriority(this) > objectives[0].GetPriority(this)))
{
return CurrentOrder.GetPriority(this);
}
return objectives.Count == 0 ? 0.0f : objectives[0].GetPriority(this);
}
public void UpdateObjectives()
@@ -59,43 +66,46 @@ namespace Barotrauma
objectives = objectives.FindAll(o => !o.IsCompleted() && o.CanBeCompleted);
//sort objectives according to priority
objectives.Sort((x, y) => y.GetPriority(character).CompareTo(x.GetPriority(character)));
objectives.Sort((x, y) => y.GetPriority(this).CompareTo(x.GetPriority(this)));
}
public void DoCurrentObjective(float deltaTime)
{
if (currentObjective != null && (!objectives.Any() || objectives[0].GetPriority(character) < OrderPriority))
if (currentOrder != null && (!objectives.Any() || objectives[0].GetPriority(this) < currentOrder.GetPriority(this)))
{
currentObjective.TryComplete(deltaTime);
CurrentObjective = currentOrder;
currentOrder.TryComplete(deltaTime);
return;
}
if (!objectives.Any()) return;
objectives[0].TryComplete(deltaTime);
CurrentObjective = objectives[0];
}
public void SetOrder(Order order, string option)
{
if (order == null) return;
currentObjective = null;
currentOrder = null;
switch (order.Name.ToLowerInvariant())
{
case "follow":
currentObjective = new AIObjectiveGoTo(Character.Controlled, character, true);
currentOrder = new AIObjectiveGoTo(Character.Controlled, character, true);
break;
case "wait":
currentObjective = new AIObjectiveGoTo(character, character, true);
currentOrder = new AIObjectiveGoTo(character, character, true);
break;
case "fixleaks":
case "fix leaks":
currentObjective = new AIObjectiveFixLeaks(character);
currentOrder = new AIObjectiveFixLeaks(character);
break;
default:
if (order.TargetItem == null) return;
currentObjective = new AIObjectiveOperateItem(order.TargetItem, character, option, null, order.UseController);
currentOrder = new AIObjectiveOperateItem(order.TargetItem, character, option, false, null, order.UseController);
break;
}

View File

@@ -1,5 +1,6 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
@@ -14,6 +15,8 @@ namespace Barotrauma
private bool canBeCompleted;
private bool requireEquip;
public override bool CanBeCompleted
{
get
@@ -27,11 +30,21 @@ namespace Barotrauma
get { return operateTarget; }
}
public AIObjectiveOperateItem(ItemComponent item, Character character, string option, Entity operateTarget = null, bool useController = false)
:base (character, option)
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 1.0f;
}
public AIObjectiveOperateItem(ItemComponent item, Character character, string option, bool requireEquip, Entity operateTarget = null, bool useController = false)
: base (character, option)
{
this.component = item;
this.requireEquip = requireEquip;
this.operateTarget = operateTarget;
if (useController)
@@ -72,6 +85,43 @@ namespace Barotrauma
}
else
{
if (requireEquip && !character.HasEquippedItem(component.Item))
{
//the item has to be equipped before using it if it's holdable
var holdable = component.Item.GetComponent<Holdable>();
if (holdable == null)
{
DebugConsole.ThrowError("AIObjectiveOperateItem failed - equipping item " + component.Item + " is required but the item has no Holdable component");
return;
}
for (int i = 0; i < CharacterInventory.limbSlots.Length; i++)
{
if (CharacterInventory.limbSlots[i] == InvSlotType.Any ||
!holdable.AllowedSlots.Any(s => s.HasFlag(CharacterInventory.limbSlots[i])))
{
continue;
}
//equip slot already taken
if (character.Inventory.Items[i] != null)
{
//try to put the item in an Any slot, and drop it if that fails
if (!character.Inventory.Items[i].AllowedSlots.Contains(InvSlotType.Any) ||
!character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List<InvSlotType>() { InvSlotType.Any }))
{
character.Inventory.Items[i].Drop();
}
}
if (character.Inventory.TryPutItem(component.Item, i, true, character))
{
component.Item.Equip(character);
break;
}
}
return;
}
if (component.AIOperate(deltaTime, character, this)) isCompleted = true;
}
}

View File

@@ -3,7 +3,7 @@ using System.Diagnostics;
namespace Barotrauma
{
class AIObjectiveRescue : AIObjective
/*class AIObjectiveRescue : AIObjective
{
private readonly Character targetCharacter;
@@ -30,5 +30,5 @@ namespace Barotrauma
return targetCharacter.IsDead ? 1000.0f / distance : 10000.0f / distance;
}
}
}*/
}

View File

@@ -3,7 +3,7 @@ using System.Linq;
namespace Barotrauma
{
class AIObjectiveRescueAll : AIObjective
/*class AIObjectiveRescueAll : AIObjective
{
private List<Character> rescueTargets;
@@ -44,5 +44,5 @@ namespace Barotrauma
AddSubObjective(new AIObjectiveRescue(character, target));
}
}
}
}*/
}

View File

@@ -103,11 +103,7 @@ namespace Barotrauma.Items.Components
{
if (character == null) return false;
if (!character.IsKeyDown(InputType.Aim)) return false;
//if (DoesUseFail(Character)) return false;
//targetPosition = targetPosition.X, -targetPosition.Y);
float degreeOfSuccess = DegreeOfSuccess(character)/100.0f;
if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess)
@@ -241,7 +237,7 @@ namespace Barotrauma.Items.Components
{
Gap leak = objective.OperateTarget as Gap;
if (leak == null) 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

View File

@@ -258,9 +258,9 @@ namespace Barotrauma.Items.Components
if (batteryToLoad == null) return true;
if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed*0.4f)
if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f)
{
objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, ""));
objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, "", false));
return false;
}
}