Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs
T
2019-03-18 20:39:27 +02:00

275 lines
9.8 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)
{
AllowGoingOutside = true
};
}
searchHullTimer = SearchHullInterval;
}
if (goToObjective != null)
{
goToObjective.TryComplete(deltaTime);
if (character.AIController.SteeringManager is IndoorsSteeringManager pathSteering &&
pathSteering.CurrentPath != null &&
pathSteering.CurrentPath.Unreachable && !unreachable.Contains(goToObjective.Target))
{
unreachable.Add(goToObjective.Target as Hull);
goToObjective = null;
}
}
//goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found)
// -> attempt to manually steer away from hazards
else if (currentHull != null)
{
Vector2 escapeVel = Vector2.Zero;
foreach (FireSource fireSource in currentHull.FireSources)
{
int dir = Math.Sign(character.Position.X - fireSource.Position.X);
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f);
escapeVel += new Vector2(dir * distMultiplier, 0.0f);
}
foreach (Character enemy in Character.CharacterList)
{
if (enemy.CurrentHull == currentHull && !enemy.IsDead && !enemy.IsUnconscious &&
(enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID))
{
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f);
escapeVel += new Vector2(Math.Sign(character.Position.X - enemy.Position.X) * distMultiplier, 0.0f);
}
}
if (escapeVel != Vector2.Zero)
{
//only move if we haven't reached the edge of the room
if ((escapeVel.X < 0 && character.Position.X > currentHull.Rect.X + 50) ||
(escapeVel.X > 0 && character.Position.X < currentHull.Rect.Right - 50))
{
character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel);
}
else
{
character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left;
character.AIController.SteeringManager.Reset();
}
}
else
{
character.AIController.SteeringManager.Reset();
}
}
}
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.CurrentHull == null) return 5.0f;
currenthullSafety = GetHullSafety(character.CurrentHull, character);
priority = 100.0f - currenthullSafety;
//var nearbyHulls = character.CurrentHull.GetConnectedHulls(3);
//increase priority slightly if there's a fire in the room
//(will increase more heavily if near the damage range of the fire)
if (character.CurrentHull.FireSources.Count > 0)
{
priority += 5.0f;
}
/*foreach (Hull hull in nearbyHulls)
{
foreach (FireSource fireSource in hull.FireSources)
{
//heavily increase priority if almost within damage range of a fire
if (fireSource.IsInDamageRange(character, fireSource.DamageRange * 1.25f))
{
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;
bool extinguishFires =
character.AIController.ObjectiveManager?.CurrentOrder is AIObjectiveExtinguishFires ||
character.AIController.ObjectiveManager?.CurrentOrder is AIObjectiveExtinguishFire;
float fireAmount = 0.0f;
var nearbyHulls = hull.GetConnectedHulls(3);
foreach (Hull hull2 in nearbyHulls)
{
foreach (FireSource fireSource in hull2.FireSources)
{
//increase priority if near the damage range of a fire
//if extinguishing fires, the character can go closer the damage range
if (fireSource.IsInDamageRange(character, fireSource.DamageRange * (extinguishFires ? 1.25f : 5.0f)))
{
fireAmount += Math.Max(fireSource.Size.X, AIObjectiveManager.OrderPriority + 1.0f);
}
}
}
safety -= fireAmount;
foreach (Character enemy in Character.CharacterList)
{
if (enemy.CurrentHull == hull && !enemy.IsDead && !enemy.IsUnconscious &&
(enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID))
{
safety -= 10.0f;
}
}
return MathHelper.Clamp(safety, 0.0f, 100.0f);
}
}
}