- 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).
161 lines
5.1 KiB
C#
161 lines
5.1 KiB
C#
using Barotrauma.Items.Components;
|
|
using FarseerPhysics;
|
|
using Microsoft.Xna.Framework;
|
|
using System;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
class AIObjectiveGoTo : AIObjective
|
|
{
|
|
private Entity target;
|
|
|
|
private Vector2 targetPos;
|
|
|
|
private bool repeat;
|
|
|
|
//how long until the path to the target is declared unreachable
|
|
private float waitUntilPathUnreachable;
|
|
|
|
private bool getDivingGearIfNeeded;
|
|
|
|
public override float GetPriority(AIObjectiveManager objectiveManager)
|
|
{
|
|
if (objectiveManager.CurrentOrder == this)
|
|
{
|
|
return AIObjectiveManager.OrderPriority;
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
public override bool CanBeCompleted
|
|
{
|
|
get
|
|
{
|
|
if (repeat || waitUntilPathUnreachable > 0.0f) return true;
|
|
var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
|
|
|
|
//path doesn't exist (= hasn't been searched for yet), assume for now that the target is reachable
|
|
if (pathSteering.CurrentPath == null) return true;
|
|
|
|
return (!pathSteering.CurrentPath.Unreachable);
|
|
}
|
|
}
|
|
|
|
public Entity Target
|
|
{
|
|
get { return target; }
|
|
}
|
|
|
|
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, bool getDivingGearIfNeeded = true)
|
|
: base(character, "")
|
|
{
|
|
this.targetPos = simPos;
|
|
this.repeat = repeat;
|
|
|
|
waitUntilPathUnreachable = 5.0f;
|
|
this.getDivingGearIfNeeded = getDivingGearIfNeeded;
|
|
}
|
|
|
|
protected override void Act(float deltaTime)
|
|
{
|
|
if (target == character)
|
|
{
|
|
character.AIController.SteeringManager.Reset();
|
|
|
|
return;
|
|
}
|
|
|
|
waitUntilPathUnreachable -= deltaTime;
|
|
|
|
if (character.SelectedConstruction!=null && character.SelectedConstruction.GetComponent<Ladder>()==null)
|
|
{
|
|
character.SelectedConstruction = null;
|
|
}
|
|
|
|
if (target != null) character.AIController.SelectTarget(target.AiTarget);
|
|
|
|
Vector2 currTargetPos = Vector2.Zero;
|
|
|
|
if (target == null)
|
|
{
|
|
currTargetPos = targetPos;
|
|
}
|
|
else
|
|
{
|
|
currTargetPos = target.SimPosition;
|
|
|
|
//if character is outside the sub and target isn't, transform the position
|
|
if (character.Submarine != null && target.Submarine == null)
|
|
{
|
|
currTargetPos -= character.Submarine.SimPosition;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
character.AIController.SteeringManager.SteeringSeek(currTargetPos);
|
|
|
|
var indoorsSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
|
|
|
|
if (indoorsSteering.CurrentPath == null || indoorsSteering.CurrentPath.Unreachable)
|
|
{
|
|
indoorsSteering.SteeringWander();
|
|
}
|
|
else if (getDivingGearIfNeeded && indoorsSteering.CurrentPath != null && indoorsSteering.CurrentPath.HasOutdoorsNodes)
|
|
{
|
|
AddSubObjective(new AIObjectiveFindDivingGear(character, true));
|
|
}
|
|
}
|
|
}
|
|
|
|
public override bool IsCompleted()
|
|
{
|
|
if (repeat) return false;
|
|
|
|
bool completed = false;
|
|
|
|
float allowedDistance = 0.5f;
|
|
var item = target as Item;
|
|
|
|
if (item != null)
|
|
{
|
|
allowedDistance = Math.Max(ConvertUnits.ToSimUnits(item.InteractDistance), allowedDistance);
|
|
if (item.IsInsideTrigger(character.WorldPosition)) completed = true;
|
|
}
|
|
|
|
completed = completed || Vector2.DistanceSquared(target != null ? target.SimPosition : targetPos, character.SimPosition) < allowedDistance * allowedDistance;
|
|
|
|
if (completed) character.AIController.SteeringManager.Reset();
|
|
|
|
return completed;
|
|
}
|
|
|
|
public override bool IsDuplicate(AIObjective otherObjective)
|
|
{
|
|
AIObjectiveGoTo objective = otherObjective as AIObjectiveGoTo;
|
|
if (objective == null) return false;
|
|
|
|
if (objective.target == target) return true;
|
|
|
|
return (objective.targetPos == targetPos);
|
|
}
|
|
}
|
|
}
|