(7ee8dbc11) v0.9.8.0
This commit is contained in:
@@ -32,6 +32,18 @@ namespace Barotrauma
|
||||
public virtual bool UnequipItems => false;
|
||||
|
||||
protected readonly List<AIObjective> subObjectives = new List<AIObjective>();
|
||||
private float _cumulatedDevotion;
|
||||
protected float CumulatedDevotion
|
||||
{
|
||||
get { return _cumulatedDevotion; }
|
||||
set { _cumulatedDevotion = MathHelper.Clamp(value, 0, MaxDevotion); }
|
||||
}
|
||||
|
||||
protected virtual float MaxDevotion => 10;
|
||||
|
||||
/// <summary>
|
||||
/// Final priority value after all calculations.
|
||||
/// </summary>
|
||||
public float Priority { get; set; }
|
||||
public float PriorityModifier { get; private set; } = 1;
|
||||
public readonly Character character;
|
||||
@@ -59,6 +71,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public virtual bool IsLoop { get; set; }
|
||||
public IEnumerable<AIObjective> SubObjectives => subObjectives;
|
||||
public AIObjective CurrentSubObjective => subObjectives.FirstOrDefault();
|
||||
|
||||
private readonly List<AIObjective> all = new List<AIObjective>();
|
||||
public IEnumerable<AIObjective> GetSubObjectivesRecursive(bool includingSelf = false)
|
||||
@@ -86,7 +99,7 @@ namespace Barotrauma
|
||||
|
||||
public AIObjective GetActiveObjective()
|
||||
{
|
||||
var subObjective = SubObjectives.FirstOrDefault();
|
||||
var subObjective = CurrentSubObjective;
|
||||
return subObjective == null ? this : subObjective.GetActiveObjective();
|
||||
}
|
||||
|
||||
@@ -157,7 +170,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (!AllowSubObjectiveSorting) { return; }
|
||||
if (subObjectives.None()) { return; }
|
||||
subObjectives.Sort((x, y) => y.GetPriority().CompareTo(x.GetPriority()));
|
||||
subObjectives.ForEach(so => so.GetPriority());
|
||||
subObjectives.Sort((x, y) => y.Priority.CompareTo(x.Priority));
|
||||
if (ConcurrentObjectives)
|
||||
{
|
||||
subObjectives.ForEach(so => so.SortSubObjectives());
|
||||
@@ -168,26 +182,38 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public virtual float GetPriority() => Priority * PriorityModifier;
|
||||
|
||||
public virtual bool IsDuplicate<T>(T otherObjective) where T : AIObjective => otherObjective.Option == Option;
|
||||
|
||||
public virtual void Update(float deltaTime)
|
||||
/// <summary>
|
||||
/// Call this only when the priority needs to be recalculated. Use the cached Priority property when you don't need to recalculate.
|
||||
/// </summary>
|
||||
public virtual float GetPriority()
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
Priority = AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
else if (objectiveManager.WaitTimer <= 0)
|
||||
else
|
||||
{
|
||||
if (objectiveManager.CurrentObjective != null)
|
||||
{
|
||||
if (objectiveManager.CurrentObjective == this || objectiveManager.CurrentObjective.subObjectives.Any(so => so == this))
|
||||
{
|
||||
Priority += Devotion * PriorityModifier * deltaTime;
|
||||
}
|
||||
}
|
||||
Priority = MathHelper.Clamp(Priority, 0, 100);
|
||||
Priority = CumulatedDevotion * PriorityModifier;
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
private void UpdateDevotion(float deltaTime)
|
||||
{
|
||||
var currentObjective = objectiveManager.CurrentObjective;
|
||||
if (currentObjective != null && (currentObjective == this || currentObjective.subObjectives.Any(so => so == this)))
|
||||
{
|
||||
CumulatedDevotion += Devotion * PriorityModifier * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsDuplicate<T>(T otherObjective) where T : AIObjective => otherObjective.Option == Option;
|
||||
|
||||
public virtual void Update(float deltaTime)
|
||||
{
|
||||
if (objectiveManager.CurrentOrder != this && objectiveManager.WaitTimer <= 0)
|
||||
{
|
||||
UpdateDevotion(deltaTime);
|
||||
}
|
||||
subObjectives.ForEach(so => so.Update(deltaTime));
|
||||
}
|
||||
@@ -264,6 +290,7 @@ namespace Barotrauma
|
||||
|
||||
public virtual void OnDeselected()
|
||||
{
|
||||
CumulatedDevotion = 0;
|
||||
Deselected?.Invoke();
|
||||
}
|
||||
|
||||
@@ -282,6 +309,7 @@ namespace Barotrauma
|
||||
isCompleted = false;
|
||||
hasBeenChecked = false;
|
||||
_abandon = false;
|
||||
CumulatedDevotion = 0;
|
||||
}
|
||||
|
||||
protected abstract void Act(float deltaTime);
|
||||
|
||||
+6
-2
@@ -100,7 +100,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPriority() => (Enemy != null && (Enemy.Removed || Enemy.IsDead)) ? 0 : Math.Min(100 * PriorityModifier, 100);
|
||||
public override float GetPriority()
|
||||
{
|
||||
Priority = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) ? 0 : Math.Min(100 * PriorityModifier, 100);
|
||||
return Priority;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
@@ -139,7 +143,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (seekAmmunition == null)
|
||||
{
|
||||
if (TryArm() && Enemy != null && !Enemy.Removed)
|
||||
if (Mode != CombatMode.Retreat && TryArm() && Enemy != null && !Enemy.Removed)
|
||||
{
|
||||
OperateWeapon(deltaTime);
|
||||
}
|
||||
|
||||
-9
@@ -74,15 +74,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
private bool CheckItem(Item i) => itemIdentifiers.Any(id => i.Prefab.Identifier == id || i.HasTag(id)) && i.ConditionPercentage > ConditionLevel;
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
|
||||
-9
@@ -54,15 +54,6 @@ namespace Barotrauma
|
||||
|
||||
protected override bool Check() => IsCompleted;
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
Item itemToDecontain = targetItem ?? sourceContainer.Inventory.FindItem(i => itemIdentifiers.Any(id => i.Prefab.Identifier == id || i.HasTag(id)), recursive: false);
|
||||
|
||||
+33
-17
@@ -1,5 +1,4 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -29,32 +28,44 @@ namespace Barotrauma
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (!objectiveManager.IsCurrentOrder<AIObjectiveExtinguishFires>()
|
||||
&& Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return 0; }
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 3 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (targetHull == character.CurrentHull)
|
||||
&& Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
|
||||
{
|
||||
distanceFactor = 1;
|
||||
Priority = 0;
|
||||
}
|
||||
float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull);
|
||||
float severityFactor = MathHelper.Lerp(0, 1, severity / 100);
|
||||
float devotion = Math.Min(Priority, 10) / 100;
|
||||
return MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + severityFactor * distanceFactor, 0, 1));
|
||||
else
|
||||
{
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 3 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (targetHull == character.CurrentHull)
|
||||
{
|
||||
distanceFactor = 1;
|
||||
}
|
||||
float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull);
|
||||
float severityFactor = MathHelper.Lerp(0, 1, severity / 100);
|
||||
float devotion = CumulatedDevotion / 100;
|
||||
Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severityFactor * distanceFactor * PriorityModifier), 0, 1));
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
protected override bool Check() => targetHull.FireSources.None();
|
||||
|
||||
private float sinTime;
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
var extinguisherItem = character.Inventory.FindItemByIdentifier("extinguisher") ?? character.Inventory.FindItemByTag("extinguisher");
|
||||
var extinguisherItem = character.Inventory.FindItemByIdentifier("fireextinguisher") ?? character.Inventory.FindItemByTag("fireextinguisher");
|
||||
if (extinguisherItem == null || extinguisherItem.Condition <= 0.0f || !character.HasEquippedItem(extinguisherItem))
|
||||
{
|
||||
TryAddSubObjective(ref getExtinguisherObjective, () =>
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogFindExtinguisher"), null, 2.0f, "findextinguisher", 30.0f);
|
||||
return new AIObjectiveGetItem(character, "extinguisher", objectiveManager, equip: true);
|
||||
return new AIObjectiveGetItem(character, "fireextinguisher", objectiveManager, equip: true)
|
||||
{
|
||||
// If the item is inside an unsafe hull, decrease the priority
|
||||
GetItemPriority = i => HumanAIController.UnsafeHulls.Contains(i.CurrentHull) ? 0.1f : 1
|
||||
};
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -79,8 +90,12 @@ namespace Barotrauma
|
||||
{
|
||||
useExtinquisherTimer = 0.0f;
|
||||
}
|
||||
// Aim
|
||||
character.CursorPosition = fs.Position;
|
||||
if (extinguisher.Item.RequireAimToUse)
|
||||
Vector2 fromCharacterToFireSource = fs.WorldPosition - character.WorldPosition;
|
||||
float dist = fromCharacterToFireSource.Length();
|
||||
character.CursorPosition += VectorExtensions.Forward(extinguisherItem.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2);
|
||||
if (extinguisherItem.RequireAimToUse)
|
||||
{
|
||||
bool isOperatingButtons = false;
|
||||
if (SteeringManager == PathSteering)
|
||||
@@ -95,8 +110,9 @@ namespace Barotrauma
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
sinTime += deltaTime * 10;
|
||||
}
|
||||
character.SetInput(extinguisher.Item.IsShootable ? InputType.Shoot : InputType.Use, false, true);
|
||||
character.SetInput(extinguisherItem.IsShootable ? InputType.Shoot : InputType.Use, false, true);
|
||||
extinguisher.Use(deltaTime, character);
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
{
|
||||
@@ -110,7 +126,7 @@ namespace Barotrauma
|
||||
if (move)
|
||||
{
|
||||
//go to the first firesource
|
||||
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager)
|
||||
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager, closeEnough: extinguisher.Range / 2)
|
||||
{
|
||||
DialogueIdentifier = "dialogcannotreachfire",
|
||||
TargetName = fs.Hull.DisplayName
|
||||
|
||||
-1
@@ -9,7 +9,6 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "extinguish fires";
|
||||
public override bool ForceRun => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
public AIObjectiveExtinguishFires(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { }
|
||||
|
||||
|
||||
-1
@@ -9,7 +9,6 @@ namespace Barotrauma
|
||||
public override string DebugTag => $"find diving gear ({gearTag})";
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
private readonly string gearTag;
|
||||
private readonly string fallbackTag;
|
||||
|
||||
+33
-20
@@ -26,13 +26,35 @@ namespace Barotrauma
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private AIObjectiveFindDivingGear divingGearObjective;
|
||||
|
||||
public AIObjectiveFindSafety(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { }
|
||||
public AIObjectiveFindSafety(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { }
|
||||
|
||||
protected override bool Check() => false;
|
||||
public override bool CanBeCompleted => true;
|
||||
|
||||
private bool resetPriority;
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (character.CurrentHull == null)
|
||||
{
|
||||
Priority = objectiveManager.CurrentOrder is AIObjectiveGoTo ? 0 : 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HumanAIController.NeedsDivingGear(character, character.CurrentHull, out _) && !HumanAIController.HasDivingGear(character))
|
||||
{
|
||||
Priority = 100;
|
||||
}
|
||||
Priority = MathHelper.Clamp(Priority, 0, 100);
|
||||
if (divingGearObjective != null && !divingGearObjective.IsCompleted && divingGearObjective.CanBeCompleted)
|
||||
{
|
||||
// Boost the priority while seeking the diving gear
|
||||
Priority = Math.Max(Priority, Math.Min(AIObjectiveManager.OrderPriority + 20, 100));
|
||||
}
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (resetPriority)
|
||||
@@ -44,28 +66,19 @@ namespace Barotrauma
|
||||
if (character.CurrentHull == null)
|
||||
{
|
||||
currenthullSafety = 0;
|
||||
Priority = objectiveManager.CurrentOrder is AIObjectiveGoTo ? 0 : 100;
|
||||
return;
|
||||
}
|
||||
if (HumanAIController.NeedsDivingGear(character, character.CurrentHull, out _) && !HumanAIController.HasDivingGear(character))
|
||||
{
|
||||
Priority = 100;
|
||||
}
|
||||
currenthullSafety = HumanAIController.CurrentHullSafety;
|
||||
if (currenthullSafety > HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
{
|
||||
Priority -= priorityDecrease * deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
float dangerFactor = (100 - currenthullSafety) / 100;
|
||||
Priority += dangerFactor * priorityIncrease * deltaTime;
|
||||
}
|
||||
Priority = MathHelper.Clamp(Priority, 0, 100);
|
||||
if (divingGearObjective != null && !divingGearObjective.IsCompleted && divingGearObjective.CanBeCompleted)
|
||||
{
|
||||
// Boost the priority while seeking the diving gear
|
||||
Priority = Math.Max(Priority, Math.Min(AIObjectiveManager.OrderPriority + 20, 100));
|
||||
currenthullSafety = HumanAIController.CurrentHullSafety;
|
||||
if (currenthullSafety > HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
{
|
||||
Priority -= priorityDecrease * deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
float dangerFactor = (100 - currenthullSafety) / 100;
|
||||
Priority += dangerFactor * priorityIncrease * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-10
@@ -29,16 +29,23 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (Leak.Removed || Leak.Open <= 0) { return 0; }
|
||||
float xDist = Math.Abs(character.WorldPosition.X - Leak.WorldPosition.X);
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - Leak.WorldPosition.Y);
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally).
|
||||
// If the target is close, ignore the distance factor alltogether so that we keep fixing the leaks that are nearby.
|
||||
float distanceFactor = xDist < 200 && yDist < 100 ? 1 : MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, xDist + yDist * 3.0f));
|
||||
float severity = AIObjectiveFixLeaks.GetLeakSeverity(Leak) / 100;
|
||||
float max = Math.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
||||
float devotion = Math.Min(Priority, 10) / 100;
|
||||
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + severity * distanceFactor * PriorityModifier, 0, 1));
|
||||
if (Leak.Removed || Leak.Open <= 0)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float xDist = Math.Abs(character.WorldPosition.X - Leak.WorldPosition.X);
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - Leak.WorldPosition.Y);
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally).
|
||||
// If the target is close, ignore the distance factor alltogether so that we keep fixing the leaks that are nearby.
|
||||
float distanceFactor = xDist < 200 && yDist < 100 ? 1 : MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, xDist + yDist * 3.0f));
|
||||
float severity = AIObjectiveFixLeaks.GetLeakSeverity(Leak) / 100;
|
||||
float max = Math.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
||||
float devotion = CumulatedDevotion / 100;
|
||||
Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1));
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
|
||||
+3
-4
@@ -11,7 +11,6 @@ namespace Barotrauma
|
||||
public override string DebugTag => "fix leaks";
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
public AIObjectiveFixLeaks(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { }
|
||||
|
||||
@@ -36,7 +35,7 @@ namespace Barotrauma
|
||||
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveFixLeaks>());
|
||||
int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveFixLeaks>(), onlyBots: true);
|
||||
int totalLeaks = Targets.Count();
|
||||
if (totalLeaks == 0) { return 0; }
|
||||
int secondaryLeaks = Targets.Count(l => l.IsRoomToRoom);
|
||||
@@ -44,13 +43,13 @@ namespace Barotrauma
|
||||
bool anyFixers = otherFixers > 0;
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
float ratio = anyFixers ? totalLeaks / otherFixers : 1;
|
||||
float ratio = anyFixers ? totalLeaks / (float)otherFixers : 1;
|
||||
return Targets.Sum(t => GetLeakSeverity(t)) * ratio;
|
||||
}
|
||||
else
|
||||
{
|
||||
float ratio = leaks == 0 ? 1 : anyFixers ? leaks / otherFixers : 1;
|
||||
if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / HumanAIController.CountCrew() > 0.75f))
|
||||
if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f))
|
||||
{
|
||||
// Enough fixers
|
||||
return 0;
|
||||
|
||||
-9
@@ -31,15 +31,6 @@ namespace Barotrauma
|
||||
|
||||
public bool AllowToFindDivingGear { get; set; } = true;
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
public AIObjectiveGetItem(Character character, Item targetItem, AIObjectiveManager objectiveManager, bool equip = true, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
|
||||
+15
-6
@@ -51,14 +51,23 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (followControlledCharacter && Character.Controlled == null) { return 0.0f; }
|
||||
if (Target is Entity e && e.Removed) { return 0.0f; }
|
||||
if (IgnoreIfTargetDead && Target is Character character && character.IsDead) { return 0.0f; }
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
if (followControlledCharacter && Character.Controlled == null)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
Priority = 0;
|
||||
}
|
||||
return 1.0f;
|
||||
if (Target is Entity e && e.Removed)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
if (IgnoreIfTargetDead && Target is Character character && character.IsDead)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.GetPriority();
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
public AIObjectiveGoTo(ISpatialEntity target, Character character, AIObjectiveManager objectiveManager, bool repeat = false, bool getDivingGearIfNeeded = true, float priorityModifier = 1, float closeEnough = 0)
|
||||
|
||||
+5
-8
@@ -45,20 +45,17 @@ namespace Barotrauma
|
||||
private float randomUpdateInterval = 5;
|
||||
public float Random { get; private set; }
|
||||
|
||||
public void SetRandom()
|
||||
public void CalculatePriority()
|
||||
{
|
||||
Random = Rand.Range(0.5f, 1.5f);
|
||||
randomTimer = randomUpdateInterval;
|
||||
}
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
float max = Math.Min(Math.Min(AIObjectiveManager.RunPriority, AIObjectiveManager.OrderPriority) - 1, 100);
|
||||
float initiative = character.GetSkillLevel("initiative");
|
||||
Priority = MathHelper.Lerp(1, max, MathUtils.InverseLerp(100, 0, initiative * Random));
|
||||
return Priority;
|
||||
}
|
||||
|
||||
public override float GetPriority() => Priority;
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (objectiveManager.CurrentObjective == this)
|
||||
@@ -69,7 +66,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRandom();
|
||||
CalculatePriority();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,7 +179,7 @@ namespace Barotrauma
|
||||
if (!character.IsClimbing)
|
||||
{
|
||||
if (SteeringManager != PathSteering || (PathSteering.CurrentPath != null &&
|
||||
(PathSteering.CurrentPath.NextNode == null || PathSteering.CurrentPath.Unreachable || PathSteering.CurrentPath.HasOutdoorsNodes)))
|
||||
(PathSteering.CurrentPath.Finished || PathSteering.CurrentPath.Unreachable || PathSteering.CurrentPath.HasOutdoorsNodes)))
|
||||
{
|
||||
Wander(deltaTime);
|
||||
return;
|
||||
|
||||
+39
-13
@@ -45,6 +45,7 @@ namespace Barotrauma
|
||||
public override bool CanBeCompleted => true;
|
||||
public override bool AbandonWhenCannotCompleteSubjectives => false;
|
||||
public override bool AllowSubObjectiveSorting => true;
|
||||
public virtual bool InverseTargetEvaluation => false;
|
||||
|
||||
public override bool IsLoop { get => true; set => throw new System.Exception("Trying to set the value for IsLoop from: " + System.Environment.StackTrace); }
|
||||
|
||||
@@ -107,21 +108,46 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (character.LockHands) { return 0; }
|
||||
if (character.Submarine == null) { return 0; }
|
||||
if (Targets.None()) { return 0; }
|
||||
// Allow the target value to be more than 100.
|
||||
float targetValue = TargetEvaluation();
|
||||
// If the target value is less than 1% of the max value, let's just treat it as zero.
|
||||
if (targetValue < 1) { return 0; }
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
if (character.LockHands || character.Submarine == null || Targets.None())
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
Priority = 0;
|
||||
}
|
||||
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90);
|
||||
float devotion = MathHelper.Min(10, Priority);
|
||||
float value = MathHelper.Clamp((devotion + targetValue * PriorityModifier) / 100, 0, 1);
|
||||
return MathHelper.Lerp(0, max, value);
|
||||
else
|
||||
{
|
||||
// Allow the target value to be more than 100.
|
||||
float targetValue = TargetEvaluation();
|
||||
if (InverseTargetEvaluation)
|
||||
{
|
||||
targetValue = 100 - targetValue;
|
||||
}
|
||||
var currentSubObjective = CurrentSubObjective;
|
||||
if (currentSubObjective != null && currentSubObjective.Priority > targetValue)
|
||||
{
|
||||
// If the priority is higher than the target value, let's just use it.
|
||||
// The priority calculation is more precise, but it takes into account things like distances,
|
||||
// so it's better not to use it if it's lower than the rougher targetValue.
|
||||
targetValue = Priority;
|
||||
}
|
||||
// If the target value is less than 1% of the max value, let's just treat it as zero.
|
||||
if (targetValue < 1)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
Priority = AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
else
|
||||
{
|
||||
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90);
|
||||
float value = MathHelper.Clamp((CumulatedDevotion + (targetValue * PriorityModifier)) / 100, 0, 1);
|
||||
Priority = MathHelper.Lerp(0, max, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
protected void UpdateTargets()
|
||||
|
||||
+17
-6
@@ -41,6 +41,15 @@ namespace Barotrauma
|
||||
public bool IsActiveObjective<T>() where T : AIObjective => GetActiveObjective() is T;
|
||||
|
||||
public AIObjective GetActiveObjective() => CurrentObjective?.GetActiveObjective();
|
||||
/// <summary>
|
||||
/// Returns the last active objective of the specific type.
|
||||
/// </summary>
|
||||
public T GetActiveObjective<T>() where T : AIObjective => CurrentObjective?.GetSubObjectivesRecursive(includingSelf: true).LastOrDefault(so => so is T) as T;
|
||||
|
||||
/// <summary>
|
||||
/// Returns all active objectives of the specific type. Creates a new collection -> don't use too frequently.
|
||||
/// </summary>
|
||||
public IEnumerable<T> GetActiveObjectives<T>() where T : AIObjective => CurrentObjective?.GetSubObjectivesRecursive(includingSelf: true).Where(so => so is T).Select(so => so as T);
|
||||
|
||||
public bool HasActiveObjective<T>() where T : AIObjective => CurrentObjective is T || CurrentObjective != null && CurrentObjective.GetSubObjectivesRecursive().Any(so => so is T);
|
||||
|
||||
@@ -146,7 +155,7 @@ namespace Barotrauma
|
||||
{
|
||||
var previousObjective = CurrentObjective;
|
||||
var firstObjective = Objectives.FirstOrDefault();
|
||||
if (CurrentOrder != null && firstObjective != null && CurrentOrder.GetPriority() > firstObjective.GetPriority())
|
||||
if (CurrentOrder != null && firstObjective != null && CurrentOrder.Priority > firstObjective.Priority)
|
||||
{
|
||||
CurrentObjective = CurrentOrder;
|
||||
}
|
||||
@@ -158,14 +167,14 @@ namespace Barotrauma
|
||||
{
|
||||
previousObjective?.OnDeselected();
|
||||
CurrentObjective?.OnSelected();
|
||||
GetObjective<AIObjectiveIdle>().SetRandom();
|
||||
GetObjective<AIObjectiveIdle>().CalculatePriority();
|
||||
}
|
||||
return CurrentObjective;
|
||||
}
|
||||
|
||||
public float GetCurrentPriority()
|
||||
{
|
||||
return CurrentObjective == null ? 0.0f : CurrentObjective.GetPriority();
|
||||
return CurrentObjective == null ? 0.0f : CurrentObjective.Priority;
|
||||
}
|
||||
|
||||
public void UpdateObjectives(float deltaTime)
|
||||
@@ -203,9 +212,11 @@ namespace Barotrauma
|
||||
|
||||
public void SortObjectives()
|
||||
{
|
||||
CurrentOrder?.GetPriority();
|
||||
Objectives.ForEach(o => o.GetPriority());
|
||||
if (Objectives.Any())
|
||||
{
|
||||
Objectives.Sort((x, y) => y.GetPriority().CompareTo(x.GetPriority()));
|
||||
Objectives.Sort((x, y) => y.Priority.CompareTo(x.Priority));
|
||||
}
|
||||
GetCurrentObjective()?.SortSubObjectives();
|
||||
}
|
||||
@@ -297,7 +308,7 @@ namespace Barotrauma
|
||||
{
|
||||
IsLoop = true,
|
||||
// Don't override unless it's an order by a player
|
||||
Override = orderGiver != null && (orderGiver == Character.Controlled || orderGiver.IsRemotePlayer)
|
||||
Override = orderGiver != null && orderGiver.IsPlayer
|
||||
};
|
||||
break;
|
||||
default:
|
||||
@@ -306,7 +317,7 @@ namespace Barotrauma
|
||||
{
|
||||
IsLoop = true,
|
||||
// Don't override unless it's an order by a player
|
||||
Override = orderGiver != null && (orderGiver == Character.Controlled || orderGiver.IsRemotePlayer)
|
||||
Override = orderGiver != null && orderGiver.IsPlayer
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
+38
-18
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -31,19 +32,32 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (component.Item.ConditionPercentage <= 0) { return 0; }
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
if (component.Item.ConditionPercentage <= 0)
|
||||
{
|
||||
return AIObjectiveManager.OrderPriority;
|
||||
Priority = 0;
|
||||
}
|
||||
if (component.Item.CurrentHull == null) { return 0; }
|
||||
if (component.Item.CurrentHull.FireSources.Count > 0) { return 0; }
|
||||
if (IsOperatedByAnother(GetTarget())) { return 0; }
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == component.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return 0; }
|
||||
float devotion = MathHelper.Min(10, Priority);
|
||||
float value = devotion + AIObjectiveManager.OrderPriority * PriorityModifier;
|
||||
float max = MathHelper.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
||||
return MathHelper.Clamp(value, 0, max);
|
||||
else
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
Priority = AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
if (component.Item.CurrentHull == null || component.Item.CurrentHull.FireSources.Any() || IsOperatedByAnother(character, GetTarget(), out _))
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else if (Character.CharacterList.Any(c => c.CurrentHull == component.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float value = CumulatedDevotion + (AIObjectiveManager.OrderPriority * PriorityModifier);
|
||||
float max = MathHelper.Min((AIObjectiveManager.OrderPriority - 1), 90);
|
||||
Priority = MathHelper.Clamp(value, 0, max);
|
||||
}
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
public AIObjectiveOperateItem(ItemComponent item, Character character, AIObjectiveManager objectiveManager, string option, bool requireEquip, Entity operateTarget = null, bool useController = false, float priorityModifier = 1)
|
||||
@@ -62,25 +76,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsOperatedByAnother(ItemComponent target)
|
||||
public static bool IsOperatedByAnother(Character character, ItemComponent target, out Character operatingCharacter)
|
||||
{
|
||||
operatingCharacter = null;
|
||||
foreach (var c in Character.CharacterList)
|
||||
{
|
||||
if (c == character) { continue; }
|
||||
if (!HumanAIController.IsFriendly(c)) { continue; }
|
||||
if (character != null && c == character) { continue; }
|
||||
if (character?.AIController is HumanAIController humanAi && !humanAi.IsFriendly(c)) { continue; }
|
||||
if (c.SelectedConstruction != target.Item) { continue; }
|
||||
operatingCharacter = c;
|
||||
// If the other character is player, don't try to operate
|
||||
if (c.IsRemotePlayer || Character.Controlled == c) { return true; }
|
||||
if (c.AIController is HumanAIController humanAi)
|
||||
if (c.AIController is HumanAIController controllingHumanAi)
|
||||
{
|
||||
// If the other character is ordered to operate the item, let him do it
|
||||
if (humanAi.ObjectiveManager.IsCurrentOrder<AIObjectiveOperateItem>())
|
||||
if (controllingHumanAi.ObjectiveManager.IsCurrentOrder<AIObjectiveOperateItem>())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target is Steering)
|
||||
if (character == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (target is Steering)
|
||||
{
|
||||
// Steering is hard-coded -> cannot use the required skills collection defined in the xml
|
||||
return character.GetSkillLevel("helm") <= c.GetSkillLevel("helm");
|
||||
@@ -116,7 +136,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
// Don't allow to operate an item that someone with a better skills already operates, unless this is an order
|
||||
if (objectiveManager.CurrentOrder != this && IsOperatedByAnother(target))
|
||||
if (objectiveManager.CurrentOrder != this && IsOperatedByAnother(character, target, out _))
|
||||
{
|
||||
// Don't abandon
|
||||
return;
|
||||
|
||||
-1
@@ -12,7 +12,6 @@ namespace Barotrauma
|
||||
public override string DebugTag => "pump water";
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool UnequipItems => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
private IEnumerable<Pump> pumpList;
|
||||
|
||||
|
||||
+20
-13
@@ -30,21 +30,28 @@ namespace Barotrauma
|
||||
{
|
||||
// TODO: priority list?
|
||||
// Ignore items that are being repaired by someone else.
|
||||
if (Item.Repairables.Any(r => r.CurrentFixer != null && r.CurrentFixer != character)) { return 0; }
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - Item.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 5 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - Item.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.25f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (Item.CurrentHull == character.CurrentHull)
|
||||
if (Item.Repairables.Any(r => r.CurrentFixer != null && r.CurrentFixer != character))
|
||||
{
|
||||
distanceFactor = 1;
|
||||
Priority = 0;
|
||||
}
|
||||
float damagePriority = MathHelper.Lerp(1, 0, Item.Condition / Item.MaxCondition);
|
||||
float successFactor = MathHelper.Lerp(0, 1, Item.Repairables.Average(r => r.DegreeOfSuccess(character)));
|
||||
float isSelected = IsRepairing ? 50 : 0;
|
||||
float devotion = (Math.Min(Priority, 10) + isSelected) / 100;
|
||||
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90);
|
||||
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + damagePriority * distanceFactor * successFactor * PriorityModifier, 0, 1));
|
||||
else
|
||||
{
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - Item.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 5 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - Item.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.25f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (Item.CurrentHull == character.CurrentHull)
|
||||
{
|
||||
distanceFactor = 1;
|
||||
}
|
||||
float damagePriority = MathHelper.Lerp(1, 0, Item.Condition / Item.MaxCondition);
|
||||
float successFactor = MathHelper.Lerp(0, 1, Item.Repairables.Average(r => r.DegreeOfSuccess(character)));
|
||||
float isSelected = IsRepairing ? 50 : 0;
|
||||
float devotion = (CumulatedDevotion + isSelected) / 100;
|
||||
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90);
|
||||
Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (damagePriority * distanceFactor * successFactor * PriorityModifier), 0, 1));
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
protected override bool Check()
|
||||
|
||||
+3
-4
@@ -81,18 +81,17 @@ namespace Barotrauma
|
||||
// Don't stop fixing until done
|
||||
return 100;
|
||||
}
|
||||
int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveRepairItems>());
|
||||
int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveRepairItems>(), onlyBots: true);
|
||||
int items = Targets.Count;
|
||||
bool anyFixers = otherFixers > 0;
|
||||
float ratio = anyFixers ? items / otherFixers : 1;
|
||||
var result = ratio;
|
||||
float ratio = anyFixers ? items / (float)otherFixers : 1;
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return Targets.Sum(t => 100 - t.ConditionPercentage) * ratio;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / HumanAIController.CountCrew() > 0.75f))
|
||||
if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f))
|
||||
{
|
||||
// Enough fixers
|
||||
return 0;
|
||||
|
||||
+50
-19
@@ -14,7 +14,7 @@ namespace Barotrauma
|
||||
|
||||
const float TreatmentDelay = 0.5f;
|
||||
|
||||
const float CloseEnoughToTreat = 150.0f;
|
||||
const float CloseEnoughToTreat = 100.0f;
|
||||
|
||||
private readonly Character targetCharacter;
|
||||
|
||||
@@ -44,6 +44,15 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (targetCharacter.SelectedBy != null && targetCharacter.SelectedBy != character)
|
||||
{
|
||||
var otherCharacter = character.SelectedBy;
|
||||
if (otherCharacter != null)
|
||||
{
|
||||
// Someone else is rescuing/holding the target.
|
||||
Abandon = otherCharacter.IsPlayer || character.GetSkillLevel("medical") < otherCharacter.GetSkillLevel("medical");
|
||||
}
|
||||
}
|
||||
|
||||
if (targetCharacter != character)
|
||||
{
|
||||
@@ -67,7 +76,11 @@ namespace Barotrauma
|
||||
TargetName = targetCharacter.DisplayName
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () => RemoveSubObjective(ref goToObjective));
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
Abandon = true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -86,7 +99,11 @@ namespace Barotrauma
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(safeHull, character, objectiveManager),
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () => RemoveSubObjective(ref goToObjective));
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
safeHull = character.CurrentHull;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,7 +122,11 @@ namespace Barotrauma
|
||||
TargetName = targetCharacter.DisplayName
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () => RemoveSubObjective(ref goToObjective));
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
Abandon = true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -127,6 +148,11 @@ namespace Barotrauma
|
||||
private Dictionary<string, float> currentTreatmentSuitabilities = new Dictionary<string, float>();
|
||||
private void GiveTreatment(float deltaTime)
|
||||
{
|
||||
if (!targetCharacter.IsPlayer)
|
||||
{
|
||||
// If the target is a bot, don't let it move
|
||||
targetCharacter.AIController.SteeringManager.Reset();
|
||||
}
|
||||
if (treatmentTimer > 0.0f)
|
||||
{
|
||||
treatmentTimer -= deltaTime;
|
||||
@@ -137,9 +163,8 @@ namespace Barotrauma
|
||||
//find which treatments are the most suitable to treat the character's current condition
|
||||
targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, normalize: false);
|
||||
|
||||
var allAfflictions = GetVitalityReducingAfflictions(targetCharacter).OrderByDescending(a => a.GetVitalityDecrease(targetCharacter.CharacterHealth));
|
||||
//check if we already have a suitable treatment for any of the afflictions
|
||||
foreach (Affliction affliction in allAfflictions)
|
||||
foreach (Affliction affliction in GetSortedAfflictions(targetCharacter))
|
||||
{
|
||||
foreach (KeyValuePair<string, float> treatmentSuitability in affliction.Prefab.TreatmentSuitability)
|
||||
{
|
||||
@@ -200,10 +225,12 @@ namespace Barotrauma
|
||||
onAbandon: () => RemoveSubObjective(ref getItemObjective));
|
||||
}
|
||||
}
|
||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||
if (character != targetCharacter)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ApplyTreatment(Affliction affliction, Item item)
|
||||
{
|
||||
var targetLimb = targetCharacter.CharacterHealth.GetAfflictionLimb(affliction);
|
||||
@@ -240,7 +267,7 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return false;
|
||||
}
|
||||
bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) > AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager);
|
||||
bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter);
|
||||
if (isCompleted && targetCharacter != character)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name),
|
||||
@@ -253,20 +280,24 @@ namespace Barotrauma
|
||||
{
|
||||
if (targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead)
|
||||
{
|
||||
return 0;
|
||||
Priority = 0;
|
||||
}
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (targetCharacter.CurrentHull == character.CurrentHull)
|
||||
else
|
||||
{
|
||||
distanceFactor = 1;
|
||||
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (targetCharacter.CurrentHull == character.CurrentHull)
|
||||
{
|
||||
distanceFactor = 1;
|
||||
}
|
||||
float vitalityFactor = 1 - AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) / 100;
|
||||
float devotion = CumulatedDevotion / 100;
|
||||
Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (vitalityFactor * distanceFactor * PriorityModifier), 0, 1));
|
||||
}
|
||||
float vitalityFactor = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter);
|
||||
float devotion = Math.Min(Priority, 10) / 100;
|
||||
return MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + vitalityFactor * distanceFactor, 0, 1));
|
||||
return Priority;
|
||||
}
|
||||
|
||||
public static IEnumerable<Affliction> GetVitalityReducingAfflictions(Character character) => character.CharacterHealth.GetAllAfflictions(a => a.GetVitalityDecrease(character.CharacterHealth) > 0);
|
||||
public static IEnumerable<Affliction> GetSortedAfflictions(Character character) => CharacterHealth.SortAfflictionsBySeverity(character.CharacterHealth.GetAllAfflictions());
|
||||
}
|
||||
}
|
||||
|
||||
+67
-8
@@ -8,11 +8,11 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "rescue all";
|
||||
public override bool ForceRun => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
public override bool InverseTargetEvaluation => true;
|
||||
|
||||
private const float vitalityThreshold = 80;
|
||||
private const float vitalityThresholdForOrders = 95;
|
||||
public static float GetVitalityThreshold(AIObjectiveManager manager)
|
||||
private const float vitalityThresholdForOrders = 100;
|
||||
public static float GetVitalityThreshold(AIObjectiveManager manager, Character character, Character target)
|
||||
{
|
||||
if (manager == null)
|
||||
{
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
return manager.CurrentOrder is AIObjectiveRescueAll ? vitalityThresholdForOrders : vitalityThreshold;
|
||||
return character == target || manager.CurrentOrder is AIObjectiveRescueAll ? vitalityThresholdForOrders : vitalityThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,50 @@ namespace Barotrauma
|
||||
|
||||
protected override IEnumerable<Character> GetList() => Character.CharacterList;
|
||||
|
||||
protected override float TargetEvaluation() => Targets.Max(t => GetVitalityFactor(t));
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
int otherRescuers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveRescueAll>(), onlyBots: true);
|
||||
int targetCount = Targets.Count;
|
||||
bool anyRescuers = otherRescuers > 0;
|
||||
float ratio = anyRescuers ? targetCount / (float)otherRescuers : 1;
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
return Targets.Min(t => GetVitalityFactor(t)) / ratio;
|
||||
}
|
||||
else
|
||||
{
|
||||
float multiplier = 1;
|
||||
if (anyRescuers)
|
||||
{
|
||||
float mySkill = character.GetSkillLevel("medical");
|
||||
int betterRescuers = HumanAIController.CountCrew(c => c != HumanAIController && c.Character.Info.Job.GetSkillLevel("medical") >= mySkill, onlyBots: true);
|
||||
if (targetCount / (float)betterRescuers <= 1)
|
||||
{
|
||||
// Enough rescuers
|
||||
return 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool foundOtherMedics = HumanAIController.IsTrueForAnyCrewMember(c => c != HumanAIController && c.Character.Info.Job.Prefab.Identifier == "medicaldoctor");
|
||||
if (foundOtherMedics)
|
||||
{
|
||||
if (character.Info.Job.Prefab.Identifier != "medicaldoctor")
|
||||
{
|
||||
// Double the vitality factor -> less likely to take action
|
||||
multiplier = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Targets.Min(t => GetVitalityFactor(t)) / ratio * multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
public static float GetVitalityFactor(Character character) => Math.Min(character.HealthPercentage - character.Bleeding - character.Bloodloss - Math.Min(character.Oxygen, 0), 100);
|
||||
public static float GetVitalityFactor(Character character)
|
||||
{
|
||||
float vitality = character.HealthPercentage - character.Bleeding - character.Bloodloss + Math.Min(character.Oxygen, 0);
|
||||
return Math.Clamp(vitality, 0, 100);
|
||||
}
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Character target)
|
||||
=> new AIObjectiveRescue(character, target, objectiveManager, PriorityModifier);
|
||||
@@ -47,16 +88,34 @@ namespace Barotrauma
|
||||
if (!HumanAIController.IsFriendly(character, target)) { return false; }
|
||||
if (character.AIController is HumanAIController humanAI)
|
||||
{
|
||||
if (GetVitalityFactor(target) > GetVitalityThreshold(humanAI.ObjectiveManager)) { return false; }
|
||||
if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target)) { return false; }
|
||||
if (!humanAI.ObjectiveManager.IsCurrentOrder<AIObjectiveRescueAll>())
|
||||
{
|
||||
// Ignore unsafe hulls, unless ordered
|
||||
if (humanAI.UnsafeHulls.Contains(target.CurrentHull))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetVitalityFactor(target) > vitalityThreshold) { return false; }
|
||||
if (GetVitalityFactor(target) >= vitalityThreshold) { return false; }
|
||||
}
|
||||
if (target.Submarine == null || character.Submarine == null) { return false; }
|
||||
if (target.Submarine.TeamID != character.Submarine.TeamID) { return false; }
|
||||
if (target.CurrentHull == null) { return false; }
|
||||
if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, true)) { return false; }
|
||||
if (!target.IsPlayer && HumanAIController.IsActive(target) && target.AIController is HumanAIController targetAI)
|
||||
{
|
||||
// Ignore all concious targets that are currently fighting, fleeing or treating characters
|
||||
if (targetAI.ObjectiveManager.HasActiveObjective<AIObjectiveCombat>() ||
|
||||
targetAI.ObjectiveManager.HasActiveObjective<AIObjectiveFindSafety>() ||
|
||||
targetAI.ObjectiveManager.HasActiveObjective<AIObjectiveRescue>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Don't go into rooms that have enemies
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { return false; }
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user