- 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).
215 lines
7.1 KiB
C#
215 lines
7.1 KiB
C#
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
class AIObjectiveFindSafety : AIObjective
|
|
{
|
|
const float SearchHullInterval = 3.0f;
|
|
const float MinSafety = 50.0f;
|
|
|
|
private AIObjectiveGoTo goToObjective;
|
|
|
|
private List<Hull> unreachable;
|
|
|
|
private float currenthullSafety;
|
|
|
|
private float searchHullTimer;
|
|
|
|
private AIObjective divingGearObjective;
|
|
|
|
public float? OverrideCurrentHullSafety;
|
|
|
|
public AIObjectiveFindSafety(Character character)
|
|
: base(character, "")
|
|
{
|
|
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 (NeedsDivingGear())
|
|
{
|
|
if (!FindDivingGear(deltaTime)) return;
|
|
}
|
|
|
|
|
|
if (searchHullTimer > 0.0f)
|
|
{
|
|
searchHullTimer -= deltaTime;
|
|
}
|
|
else
|
|
{
|
|
var bestHull = FindBestHull();
|
|
if (bestHull != null)
|
|
{
|
|
goToObjective = new AIObjectiveGoTo(bestHull, character);
|
|
}
|
|
|
|
searchHullTimer = SearchHullInterval;
|
|
}
|
|
|
|
if (goToObjective != null)
|
|
{
|
|
var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
|
|
if (pathSteering != null && pathSteering.CurrentPath != null &&
|
|
pathSteering.CurrentPath.Unreachable && !unreachable.Contains(goToObjective.Target))
|
|
{
|
|
unreachable.Add(goToObjective.Target as Hull);
|
|
}
|
|
|
|
|
|
goToObjective.TryComplete(deltaTime);
|
|
}
|
|
}
|
|
|
|
private bool FindDivingGear(float deltaTime)
|
|
{
|
|
if (divingGearObjective == null)
|
|
{
|
|
divingGearObjective = new AIObjectiveFindDivingGear(character, false);
|
|
}
|
|
|
|
if (divingGearObjective.IsCompleted()) return true;
|
|
|
|
divingGearObjective.TryComplete(deltaTime);
|
|
return divingGearObjective.IsCompleted();
|
|
}
|
|
|
|
private Hull FindBestHull()
|
|
{
|
|
Hull bestHull = null;
|
|
float bestValue = currenthullSafety;
|
|
|
|
foreach (Hull hull in Hull.hullList)
|
|
{
|
|
if (hull == character.AnimController.CurrentHull || unreachable.Contains(hull)) continue;
|
|
|
|
float hullValue = GetHullSafety(hull, character);
|
|
//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)
|
|
{
|
|
bestHull = hull;
|
|
bestValue = hullValue;
|
|
}
|
|
}
|
|
|
|
return bestHull;
|
|
}
|
|
|
|
public override bool IsDuplicate(AIObjective otherObjective)
|
|
{
|
|
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.WaterVolume / currentHull.Volume > 0.5f) return true;
|
|
|
|
if (currentHull.OxygenPercentage < 30.0f) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
|
|
var nearbyHulls = character.AnimController.CurrentHull.GetConnectedHulls(3);
|
|
|
|
foreach (Hull hull in nearbyHulls)
|
|
{
|
|
foreach (FireSource fireSource in hull.FireSources)
|
|
{
|
|
//increase priority if almost within damage range of a fire
|
|
if (character.Position.X > fireSource.Position.X - fireSource.DamageRange * 2 &&
|
|
character.Position.X < fireSource.Position.X + fireSource.Size.X + fireSource.DamageRange * 2 &&
|
|
character.Position.Y > hull.Rect.Y - hull.Rect.Height &&
|
|
character.Position.Y < hull.Rect.Y)
|
|
{
|
|
priority += Math.Max(fireSource.Size.X, 50.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NeedsDivingGear())
|
|
{
|
|
if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f;
|
|
}
|
|
|
|
return priority;
|
|
}
|
|
|
|
public static float GetHullSafety(Hull hull, Character character)
|
|
{
|
|
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)
|
|
{
|
|
//increase priority if almost within damage range of a fire
|
|
if (character.Position.X > fireSource.Position.X - fireSource.DamageRange * 2 &&
|
|
character.Position.X < fireSource.Position.X + fireSource.Size.X + fireSource.DamageRange * 2 &&
|
|
character.Position.Y > hull2.Rect.Y - hull2.Rect.Height &&
|
|
character.Position.Y < hull2.Rect.Y)
|
|
{
|
|
fireAmount += Math.Max(fireSource.Size.X, 50.0f);
|
|
}
|
|
}
|
|
}
|
|
safety -= fireAmount;
|
|
|
|
return MathHelper.Clamp(safety, 0.0f, 100.0f);
|
|
}
|
|
}
|
|
}
|