Unstable 1.8.4.0
This commit is contained in:
+119
-33
@@ -3,6 +3,7 @@ using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -31,7 +32,7 @@ namespace Barotrauma
|
||||
|
||||
public AIObjectiveFindSafety(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { }
|
||||
|
||||
protected override bool CheckObjectiveSpecific() => false;
|
||||
protected override bool CheckObjectiveState() => false;
|
||||
public override bool CanBeCompleted => true;
|
||||
|
||||
private bool resetPriority;
|
||||
@@ -41,9 +42,9 @@ namespace Barotrauma
|
||||
if (character.CurrentHull == null)
|
||||
{
|
||||
Priority = (
|
||||
objectiveManager.HasOrder<AIObjectiveGoTo>(o => o.Priority > 0) ||
|
||||
objectiveManager.CurrentOrder is AIObjectiveGoTo ||
|
||||
objectiveManager.HasActiveObjective<AIObjectiveRescue>() ||
|
||||
objectiveManager.Objectives.Any(o => (o is AIObjectiveCombat || o is AIObjectiveReturn) && o.Priority > 0))
|
||||
objectiveManager.Objectives.Any(o => o is AIObjectiveCombat or AIObjectiveReturn && o.Priority > 0))
|
||||
&& ((!character.IsLowInOxygen && character.IsImmuneToPressure)|| HumanAIController.HasDivingSuit(character)) ? 0 : AIObjectiveManager.EmergencyObjectivePriority - 10;
|
||||
}
|
||||
else
|
||||
@@ -70,6 +71,11 @@ namespace Barotrauma
|
||||
Priority = AIObjectiveManager.MaxObjectivePriority;
|
||||
}
|
||||
}
|
||||
else if (objectiveManager.CurrentOrder is AIObjectiveGoTo { IsFollowOrder: true })
|
||||
{
|
||||
// Ordered to follow -> Don't flee from the enemies/fires (doesn't get here if we need more oxygen).
|
||||
Priority = 0;
|
||||
}
|
||||
else if ((objectiveManager.IsCurrentOrder<AIObjectiveGoTo>() || objectiveManager.IsCurrentOrder<AIObjectiveReturn>()) &&
|
||||
character.Submarine != null && !character.IsOnFriendlyTeam(character.Submarine.TeamID))
|
||||
{
|
||||
@@ -82,7 +88,7 @@ namespace Barotrauma
|
||||
Priority = 0;
|
||||
}
|
||||
Priority = MathHelper.Clamp(Priority, 0, AIObjectiveManager.MaxObjectivePriority);
|
||||
if (divingGearObjective != null && !divingGearObjective.IsCompleted && divingGearObjective.CanBeCompleted)
|
||||
if (divingGearObjective is { IsCompleted: false, CanBeCompleted: true, Priority: > 0f })
|
||||
{
|
||||
// Boost the priority while seeking the diving gear
|
||||
Priority = Math.Max(Priority, Math.Min(AIObjectiveManager.EmergencyObjectivePriority - 1, AIObjectiveManager.MaxObjectivePriority));
|
||||
@@ -148,7 +154,13 @@ namespace Barotrauma
|
||||
bool shouldActOnSuffocation = character.IsLowInOxygen && !character.AnimController.HeadInWater && HumanAIController.HasDivingSuit(character, requireOxygenTank: false);
|
||||
if (!character.LockHands && (!dangerousPressure || shouldActOnSuffocation || cannotFindSafeHull))
|
||||
{
|
||||
bool needsDivingGear = HumanAIController.NeedsDivingGear(currentHull, out bool needsDivingSuit);
|
||||
bool needsDivingGear = HumanAIController.NeedsDivingGear(currentHull, out bool needsDivingSuit, objectiveManager);
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC && character.Submarine?.Info is { IsOutpost: true })
|
||||
{
|
||||
// In outposts, the NPCs don't try to use diving suits, because otherwise there's probably not enough for those trying to fix the leaks.
|
||||
// This is not a hard rule: the bots may still grab a suit, unless they find a diving mask.
|
||||
needsDivingSuit = false;
|
||||
}
|
||||
bool needsEquipment = shouldActOnSuffocation;
|
||||
if (needsDivingSuit)
|
||||
{
|
||||
@@ -306,10 +318,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (escapeVel != Vector2.Zero)
|
||||
if (escapeVel != Vector2.Zero && character.CurrentHull is Hull currentHull)
|
||||
{
|
||||
float left = character.CurrentHull.Rect.X + 50;
|
||||
float right = character.CurrentHull.Rect.Right - 50;
|
||||
float left = currentHull.Rect.X + 50;
|
||||
float right = currentHull.Rect.Right - 50;
|
||||
//only move if we haven't reached the edge of the room
|
||||
if (escapeVel.X < 0 && character.Position.X > left || escapeVel.X > 0 && character.Position.X < right)
|
||||
{
|
||||
@@ -339,6 +351,10 @@ namespace Barotrauma
|
||||
float bestHullValue = 0;
|
||||
bool bestHullIsAirlock = false;
|
||||
Hull potentialBestHull;
|
||||
|
||||
#if DEBUG
|
||||
private readonly Stopwatch stopWatch = new Stopwatch();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the best (safe, nearby) hull the character can find a path to.
|
||||
@@ -353,6 +369,9 @@ namespace Barotrauma
|
||||
bestHullIsAirlock = false;
|
||||
hulls.Clear();
|
||||
var connectedSubs = character.Submarine?.GetConnectedSubs();
|
||||
#if DEBUG
|
||||
stopWatch.Restart();
|
||||
#endif
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.Submarine == null) { continue; }
|
||||
@@ -363,25 +382,66 @@ namespace Barotrauma
|
||||
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
|
||||
if (HumanAIController.UnreachableHulls.Contains(hull)) { continue; }
|
||||
if (connectedSubs != null && !connectedSubs.Contains(hull.Submarine)) { continue; }
|
||||
//sort the hulls based on distance and which sub they're in
|
||||
//tends to make the method much faster, because we find a potential hull earlier and can discard further-away hulls more easily
|
||||
//(for instance, an NPC in an outpost might otherwise go through all the hulls in the main sub first and do tons of expensive
|
||||
//path calculations, only to discard all of them when going through the hulls in the outpost)
|
||||
float hullSuitability = EstimateHullSuitability(character, hull);
|
||||
if (hulls.None())
|
||||
{
|
||||
hulls.Add(hull);
|
||||
}
|
||||
else
|
||||
{
|
||||
//sort the hulls first based on distance and a rough suitability estimation
|
||||
//tends to make the method much faster, because we find a potential hull earlier and can discard further-away hulls more easily
|
||||
//(for instance, an NPC in an outpost might otherwise go through all the hulls in the main sub first and do tons of expensive
|
||||
//path calculations, only to discard all of them when going through the hulls in the outpost)
|
||||
bool addLast = true;
|
||||
float hullSuitability = EstimateHullSuitability(hull);
|
||||
for (int i = 0; i < hulls.Count; i++)
|
||||
{
|
||||
if (hullSuitability > EstimateHullSuitability(character, hulls[i]))
|
||||
Hull otherHull = hulls[i];
|
||||
float otherHullSuitability = EstimateHullSuitability(otherHull);
|
||||
if (hullSuitability > otherHullSuitability)
|
||||
{
|
||||
hulls.Insert(i, hull);
|
||||
addLast = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (addLast)
|
||||
{
|
||||
hulls.Add(hull);
|
||||
}
|
||||
}
|
||||
|
||||
float EstimateHullSuitability(Hull h)
|
||||
{
|
||||
float distX = Math.Abs(h.WorldPosition.X - character.WorldPosition.X);
|
||||
float distY = Math.Abs(h.WorldPosition.Y - character.WorldPosition.Y);
|
||||
if (character.CurrentHull != null)
|
||||
{
|
||||
distY *= 3;
|
||||
}
|
||||
float dist = distX + distY;
|
||||
float suitability = -dist;
|
||||
const float suitabilityReduction = 10000.0f;
|
||||
if (h.Submarine != character.Submarine)
|
||||
{
|
||||
suitability -= suitabilityReduction;
|
||||
}
|
||||
if (character.CurrentHull != null)
|
||||
{
|
||||
if (h.AvoidStaying)
|
||||
{
|
||||
suitability -= suitabilityReduction;
|
||||
}
|
||||
if (HumanAIController.UnsafeHulls.Contains(h))
|
||||
{
|
||||
suitability -= suitabilityReduction;
|
||||
}
|
||||
if (HumanAIController.NeedsDivingGear(h, out _))
|
||||
{
|
||||
suitability -= suitabilityReduction;
|
||||
}
|
||||
}
|
||||
return suitability;
|
||||
}
|
||||
}
|
||||
if (hulls.None())
|
||||
@@ -390,19 +450,10 @@ namespace Barotrauma
|
||||
return HullSearchStatus.Finished;
|
||||
}
|
||||
hullSearchIndex = 0;
|
||||
}
|
||||
|
||||
static float EstimateHullSuitability(Character character, Hull hull)
|
||||
{
|
||||
float dist =
|
||||
Math.Abs(hull.WorldPosition.X - character.WorldPosition.X) +
|
||||
Math.Abs(hull.WorldPosition.Y - character.WorldPosition.Y) * 3;
|
||||
float suitability = -dist;
|
||||
if (hull.Submarine != character.Submarine)
|
||||
{
|
||||
suitability -= 10000.0f;
|
||||
}
|
||||
return suitability;
|
||||
#if DEBUG
|
||||
stopWatch.Stop();
|
||||
DebugConsole.Log($"({character.DisplayName}) Sorted hulls by suitability in {stopWatch.ElapsedMilliseconds} ms");
|
||||
#endif
|
||||
}
|
||||
|
||||
Hull potentialHull = hulls[hullSearchIndex];
|
||||
@@ -420,7 +471,7 @@ namespace Barotrauma
|
||||
if (hullSafety > bestHullValue)
|
||||
{
|
||||
//avoid airlock modules if not allowed to change the sub
|
||||
if (allowChangingSubmarine || !potentialHull.OutpostModuleTags.Any(t => t == "airlock"))
|
||||
if (allowChangingSubmarine || potentialHull.OutpostModuleTags.All(t => t != "airlock"))
|
||||
{
|
||||
// Don't allow to go outside if not already outside.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, character.GetRelativeSimPosition(potentialHull), character.Submarine, nodeFilter: node => node.Waypoint.CurrentHull != null);
|
||||
@@ -431,12 +482,47 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
// Each unsafe node reduces the hull safety value.
|
||||
// Ignore the current hull, because otherwise we couldn't find a path out.
|
||||
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
|
||||
hullSafety /= 1 + unsafeNodes;
|
||||
// Check the path safety. Each unsafe node reduces the hull safety value.
|
||||
Hull previousHull = null;
|
||||
foreach (WayPoint node in path.Nodes)
|
||||
{
|
||||
Hull hull = node.CurrentHull;
|
||||
if (hull == previousHull)
|
||||
{
|
||||
// Let's evaluate each hull only once. If we'd want to make this foolproof, we'd have to add the checked hulls to a list,
|
||||
// yet in practice there shouldn't be a case where the path would get back to a hull once it has exited it.
|
||||
continue;
|
||||
}
|
||||
previousHull = hull;
|
||||
if (hull == character.CurrentHull)
|
||||
{
|
||||
// Ignore the current hull, because otherwise we couldn't find a path out.
|
||||
continue;
|
||||
}
|
||||
if (HumanAIController.UnsafeHulls.Contains(hull))
|
||||
{
|
||||
// Compare safety of the node hull to the current hull safety.
|
||||
float nodeHullSafety = HumanAIController.GetHullSafety(hull, hull.GetConnectedHulls(true, 1), character);
|
||||
if (nodeHullSafety < HumanAIController.HULL_SAFETY_THRESHOLD && nodeHullSafety < HumanAIController.CurrentHullSafety)
|
||||
{
|
||||
// If the node hull is considered unsafe and less safe than the current hull, let's ignore the target.
|
||||
hullSafety = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, each unsafe hull on the path reduces the safety of the target hull by 50% of their threat value.
|
||||
float hullThreat = 100 - nodeHullSafety;
|
||||
hullSafety -= hullThreat / 2;
|
||||
if (hullSafety <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(potentialHull, true))
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(potentialHull, includingConnectedSubs: true))
|
||||
{
|
||||
hullSafety /= 10;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user