Unstable 1.8.4.0

This commit is contained in:
Markus Isberg
2025-03-12 12:56:27 +00:00
parent a4c3e868e4
commit a4a3427e4e
627 changed files with 29860 additions and 10018 deletions
@@ -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;
}