Faction Test v1.0.1.0
This commit is contained in:
+23
-14
@@ -352,7 +352,7 @@ namespace Barotrauma
|
||||
Weapon = null;
|
||||
continue;
|
||||
}
|
||||
if (WeaponComponent.IsLoaded(character))
|
||||
if (WeaponComponent.IsNotEmpty(character))
|
||||
{
|
||||
// All good, the weapon is loaded
|
||||
break;
|
||||
@@ -470,7 +470,7 @@ namespace Barotrauma
|
||||
// Not in the inventory anymore or cannot find the weapon component
|
||||
return false;
|
||||
}
|
||||
if (!WeaponComponent.IsLoaded(character))
|
||||
if (!WeaponComponent.IsNotEmpty(character))
|
||||
{
|
||||
// Try reloading (and seek ammo)
|
||||
if (!Reload(seekAmmo))
|
||||
@@ -541,7 +541,7 @@ namespace Barotrauma
|
||||
priority /= 2;
|
||||
}
|
||||
}
|
||||
if (!weapon.IsLoaded(character))
|
||||
if (!weapon.IsNotEmpty(character))
|
||||
{
|
||||
if (weapon is RangedWeapon && !isAllowedToSeekWeapons)
|
||||
{
|
||||
@@ -554,7 +554,15 @@ namespace Barotrauma
|
||||
priority /= 2;
|
||||
}
|
||||
}
|
||||
if (Enemy.IsKnockedDown)
|
||||
|
||||
if (Enemy.Params.Health.StunImmunity)
|
||||
{
|
||||
if (weapon.Item.HasTag("stunner"))
|
||||
{
|
||||
priority /= 2;
|
||||
}
|
||||
}
|
||||
else if (Enemy.IsKnockedDown)
|
||||
{
|
||||
// Enemy is stunned, reduce the priority of stunner weapons.
|
||||
Attack attack = GetAttackDefinition(weapon);
|
||||
@@ -640,11 +648,11 @@ namespace Barotrauma
|
||||
{
|
||||
statusEffects = statusEffects.Concat(hitEffects);
|
||||
}
|
||||
float afflictionsStun = attack.Afflictions.Keys.Sum(a => a.Identifier == "stun" ? a.Strength : 0);
|
||||
float afflictionsStun = attack.Afflictions.Keys.Sum(a => a.Identifier == AfflictionPrefab.StunType ? a.Strength : 0);
|
||||
float effectsStun = statusEffects.None() ? 0 : statusEffects.Max(se =>
|
||||
{
|
||||
float stunAmount = 0;
|
||||
var stunAffliction = se.Afflictions.Find(a => a.Identifier == "stun");
|
||||
var stunAffliction = se.Afflictions.Find(a => a.Identifier == AfflictionPrefab.StunType);
|
||||
if (stunAffliction != null)
|
||||
{
|
||||
stunAmount = stunAffliction.Strength;
|
||||
@@ -1168,30 +1176,31 @@ namespace Barotrauma
|
||||
if (sqrDistance > repairTool.Range * repairTool.Range) { return; }
|
||||
}
|
||||
float aimFactor = MathHelper.PiOver2 * (1 - AimAccuracy);
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4 + aimFactor)
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.WorldPosition - Weapon.WorldPosition) < MathHelper.PiOver4 + aimFactor)
|
||||
{
|
||||
if (myBodies == null)
|
||||
{
|
||||
myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
|
||||
}
|
||||
var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel;
|
||||
var pickedBody = Submarine.PickBody(Weapon.SimPosition, Enemy.SimPosition, myBodies, collisionCategories, allowInsideFixture: true);
|
||||
if (pickedBody != null)
|
||||
// Check that we don't hit friendlies. No need to check the walls, because there's a separate check for that at 1096 (which intentionally has a small delay)
|
||||
var pickedBodies = Submarine.PickBodies(Weapon.SimPosition, Character.GetRelativeSimPosition(from: Weapon, to: Enemy), myBodies, Physics.CollisionCharacter);
|
||||
foreach (var body in pickedBodies)
|
||||
{
|
||||
Character target = null;
|
||||
if (pickedBody.UserData is Character c)
|
||||
if (body.UserData is Character c)
|
||||
{
|
||||
target = c;
|
||||
}
|
||||
else if (pickedBody.UserData is Limb limb)
|
||||
else if (body.UserData is Limb limb)
|
||||
{
|
||||
target = limb.character;
|
||||
}
|
||||
if (target != null && (target == Enemy || !HumanAIController.IsFriendly(target)))
|
||||
if (target != null && (target != Enemy || HumanAIController.IsFriendly(target)))
|
||||
{
|
||||
UseWeapon(deltaTime);
|
||||
return;
|
||||
}
|
||||
}
|
||||
UseWeapon(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -200,7 +201,8 @@ namespace Barotrauma
|
||||
(container.Item.GetRootContainer()?.OwnInventory?.Locked ?? false) ||
|
||||
ItemToContain == null || ItemToContain.Removed ||
|
||||
!ItemToContain.IsOwnedBy(character) || container.Item.GetRootInventoryOwner() is Character c && c != character,
|
||||
SpeakIfFails = !objectiveManager.IsCurrentOrder<AIObjectiveCleanupItems>()
|
||||
SpeakIfFails = !objectiveManager.IsCurrentOrder<AIObjectiveCleanupItems>(),
|
||||
endNodeFilter = n => Vector2.DistanceSquared(n.Waypoint.WorldPosition, container.Item.WorldPosition) <= MathUtils.Pow2(AIObjectiveGetItem.DefaultReach)
|
||||
},
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective));
|
||||
@@ -244,7 +246,8 @@ namespace Barotrauma
|
||||
|
||||
public bool IsInTargetSlot(Item item)
|
||||
{
|
||||
if (container?.Inventory is ItemInventory inventory && TargetSlot is not null)
|
||||
if (TargetSlot == null) { return true; }
|
||||
if (container?.Inventory is ItemInventory inventory)
|
||||
{
|
||||
return inventory.IsInSlot(item, (int)TargetSlot);
|
||||
}
|
||||
|
||||
+39
-29
@@ -19,7 +19,6 @@ namespace Barotrauma
|
||||
|
||||
private AIObjectiveGetItem getExtinguisherObjective;
|
||||
private AIObjectiveGoTo gotoObjective;
|
||||
private float useExtinquisherTimer;
|
||||
|
||||
public AIObjectiveExtinguishFire(Character character, Hull targetHull, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
@@ -44,7 +43,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y);
|
||||
float characterY = character.CurrentHull?.WorldPosition.Y ?? character.WorldPosition.Y;
|
||||
float yDist = Math.Abs(characterY - 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));
|
||||
@@ -119,24 +119,18 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
break;
|
||||
}
|
||||
float xDist = Math.Abs(character.WorldPosition.X - fs.WorldPosition.X) - fs.DamageRange;
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - fs.WorldPosition.Y);
|
||||
bool inRange = xDist + yDist < extinguisher.Range;
|
||||
// Use the hull position, because the fire x pos is sometimes inside a wall -> the bot can't ever see it and continues running towards the wall.
|
||||
ISpatialEntity lookTarget = character.CurrentHull == targetHull || character.CurrentHull.linkedTo.Contains(targetHull) ? targetHull : fs as ISpatialEntity;
|
||||
bool move = !inRange || !character.CanSeeTarget(lookTarget);
|
||||
if ((inRange && character.CanSeeTarget(lookTarget)) || useExtinquisherTimer > 0)
|
||||
float xDist = Math.Abs(character.WorldPosition.X - fs.WorldPosition.X);
|
||||
float yDist = Math.Abs(character.CurrentHull.WorldPosition.Y - targetHull.WorldPosition.Y);
|
||||
float dist = xDist + yDist;
|
||||
bool inRange = dist < extinguisher.Range;
|
||||
bool isInDamageRange = fs.IsInDamageRange(character, fs.DamageRange) && character.CanSeeTarget(targetHull);
|
||||
bool moveCloser = !isInDamageRange && (!inRange || !character.CanSeeTarget(targetHull));
|
||||
bool operateExtinguisher = !moveCloser || (dist < extinguisher.Range * 1.2f && character.CanSeeTarget(targetHull));
|
||||
if (operateExtinguisher)
|
||||
{
|
||||
useExtinquisherTimer += deltaTime;
|
||||
if (useExtinquisherTimer > 2.0f)
|
||||
{
|
||||
useExtinquisherTimer = 0.0f;
|
||||
}
|
||||
// Aim
|
||||
character.CursorPosition = fs.Position;
|
||||
Vector2 fromCharacterToFireSource = fs.WorldPosition - character.WorldPosition;
|
||||
float dist = fromCharacterToFireSource.Length();
|
||||
character.CursorPosition += VectorExtensions.Forward(extinguisherItem.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2);
|
||||
character.CursorPosition += VectorExtensions.Forward(extinguisherItem.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, fromCharacterToFireSource.Length() / 2);
|
||||
if (extinguisherItem.RequireAimToUse)
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
@@ -148,25 +142,29 @@ namespace Barotrauma
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogPutOutFire", "[roomname]", targetHull.DisplayName, FormatCapitals.Yes).Value, null, 0, "putoutfire".ToIdentifier(), 10.0f);
|
||||
}
|
||||
// Prevents running into the flames.
|
||||
objectiveManager.CurrentObjective.ForceWalk = true;
|
||||
}
|
||||
if (move)
|
||||
if (moveCloser)
|
||||
{
|
||||
//go to the first firesource
|
||||
if (TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager, closeEnough: Math.Max(fs.DamageRange, extinguisher.Range * 0.7f))
|
||||
{
|
||||
DialogueIdentifier = "dialogcannotreachfire".ToIdentifier(),
|
||||
TargetName = fs.Hull.DisplayName
|
||||
},
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref gotoObjective)))
|
||||
if (TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager, closeEnough: extinguisher.Range * 0.8f)
|
||||
{
|
||||
DialogueIdentifier = "dialogcannotreachfire".ToIdentifier(),
|
||||
TargetName = fs.Hull.DisplayName,
|
||||
},
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref gotoObjective)))
|
||||
{
|
||||
gotoObjective.requiredCondition = () => character.CanSeeTarget(targetHull);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!operateExtinguisher || isInDamageRange)
|
||||
{
|
||||
character.AIController.SteeringManager.Reset();
|
||||
// Don't walk into the flames.
|
||||
RemoveSubObjective(ref gotoObjective);
|
||||
SteeringManager.Reset();
|
||||
}
|
||||
// Only target one fire source at the time.
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -177,8 +175,20 @@ namespace Barotrauma
|
||||
base.Reset();
|
||||
getExtinguisherObjective = null;
|
||||
gotoObjective = null;
|
||||
useExtinquisherTimer = 0;
|
||||
sinTime = 0;
|
||||
SteeringManager.Reset();
|
||||
}
|
||||
|
||||
protected override void OnCompleted()
|
||||
{
|
||||
base.OnCompleted();
|
||||
SteeringManager.Reset();
|
||||
}
|
||||
|
||||
protected override void OnAbandon()
|
||||
{
|
||||
base.OnAbandon();
|
||||
SteeringManager.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-24
@@ -47,31 +47,25 @@ namespace Barotrauma
|
||||
}
|
||||
if (character.CurrentHull == null)
|
||||
{
|
||||
if (!character.NeedsAir)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Priority = (
|
||||
objectiveManager.HasOrder<AIObjectiveGoTo>(o => o.Priority > 0) ||
|
||||
objectiveManager.HasOrder<AIObjectiveReturn>(o => o.Priority > 0) ||
|
||||
objectiveManager.HasActiveObjective<AIObjectiveRescue>() ||
|
||||
objectiveManager.Objectives.Any(o => o is AIObjectiveCombat && o.Priority > 0))
|
||||
&& HumanAIController.HasDivingSuit(character) ? 0 : 100;
|
||||
}
|
||||
Priority = (
|
||||
objectiveManager.HasOrder<AIObjectiveGoTo>(o => o.Priority > 0) ||
|
||||
objectiveManager.HasOrder<AIObjectiveReturn>(o => o.Priority > 0) ||
|
||||
objectiveManager.HasActiveObjective<AIObjectiveRescue>() ||
|
||||
objectiveManager.Objectives.Any(o => o is AIObjectiveCombat && o.Priority > 0))
|
||||
&& character.IsProtectedFromPressure ? 0 : 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HumanAIController.NeedsDivingGear(character.CurrentHull, out bool needsSuit) &&
|
||||
if ((character.IsLowInOxygen && !character.AnimController.HeadInWater && HumanAIController.HasDivingSuit(character, requireOxygenTank: false)) ||
|
||||
(HumanAIController.NeedsDivingGear(character.CurrentHull, out bool needsSuit) &&
|
||||
(needsSuit ?
|
||||
!HumanAIController.HasDivingSuit(character, conditionPercentage: AIObjectiveFindDivingGear.GetMinOxygen(character)) :
|
||||
!HumanAIController.HasDivingGear(character, conditionPercentage: AIObjectiveFindDivingGear.GetMinOxygen(character))))
|
||||
!HumanAIController.HasDivingGear(character, conditionPercentage: AIObjectiveFindDivingGear.GetMinOxygen(character)))))
|
||||
{
|
||||
Priority = 100;
|
||||
}
|
||||
else if ((objectiveManager.IsCurrentOrder<AIObjectiveGoTo>() || objectiveManager.IsCurrentOrder<AIObjectiveReturn>()) &&
|
||||
character.Submarine != null && !AIController.IsOnFriendlyTeam(character.TeamID, character.Submarine.TeamID))
|
||||
character.Submarine != null && !character.IsOnFriendlyTeam(character.Submarine.TeamID))
|
||||
{
|
||||
// Ordered to follow, hold position, or return back to main sub inside a hostile sub
|
||||
// -> ignore find safety unless we need to find a diving gear
|
||||
@@ -117,6 +111,11 @@ namespace Barotrauma
|
||||
if (currenthullSafety > HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
{
|
||||
Priority -= priorityDecrease * deltaTime;
|
||||
if (currenthullSafety >= 100)
|
||||
{
|
||||
// Reduce the priority to zero so that the bot can get switch to other objectives immediately, e.g. when entering the airlock.
|
||||
Priority = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -137,12 +136,14 @@ namespace Barotrauma
|
||||
private float retryTimer;
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (resetPriority) { return; }
|
||||
var currentHull = character.CurrentHull;
|
||||
bool dangerousPressure = currentHull == null || currentHull.LethalPressure > 0 && character.PressureProtection <= 0;
|
||||
if (!character.LockHands && (!dangerousPressure || cannotFindSafeHull))
|
||||
bool dangerousPressure = !character.IsProtectedFromPressure && (currentHull == null || currentHull.LethalPressure > 0);
|
||||
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 needsEquipment = false;
|
||||
bool needsEquipment = shouldActOnSuffocation;
|
||||
if (needsDivingSuit)
|
||||
{
|
||||
needsEquipment = !HumanAIController.HasDivingSuit(character, AIObjectiveFindDivingGear.GetMinOxygen(character));
|
||||
@@ -218,7 +219,11 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref goToObjective,
|
||||
constructor: () => new AIObjectiveGoTo(currentSafeHull, character, objectiveManager, getDivingGearIfNeeded: true)
|
||||
{
|
||||
AllowGoingOutside = HumanAIController.HasDivingSuit(character, conditionPercentage: 50)
|
||||
AllowGoingOutside =
|
||||
character.IsProtectedFromPressure ||
|
||||
character.CurrentHull == null ||
|
||||
character.CurrentHull.IsTaggedAirlock() ||
|
||||
character.CurrentHull.LeadsOutside(character)
|
||||
},
|
||||
onCompleted: () =>
|
||||
{
|
||||
@@ -349,8 +354,8 @@ namespace Barotrauma
|
||||
//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.Any())
|
||||
float hullSuitability = EstimateHullSuitability(character, hull);
|
||||
if (hulls.None())
|
||||
{
|
||||
hulls.Add(hull);
|
||||
}
|
||||
@@ -445,9 +450,12 @@ namespace Barotrauma
|
||||
{
|
||||
hullSafety = 100;
|
||||
}
|
||||
float characterY = character.CurrentHull?.WorldPosition.Y ?? character.WorldPosition.Y;
|
||||
float yDist = Math.Abs(characterY - potentialHull.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 3 : 0;
|
||||
float distance = Math.Abs(character.WorldPosition.X - potentialHull.WorldPosition.X) + yDist;
|
||||
// Huge preference for closer targets
|
||||
float distance = Vector2.DistanceSquared(character.WorldPosition, potentialHull.WorldPosition);
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, MathUtils.Pow(100000, 2), distance));
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, 10000, distance));
|
||||
hullSafety *= distanceFactor;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
// Intentionally exclude wrecks from this check
|
||||
|
||||
+23
-11
@@ -155,17 +155,21 @@ namespace Barotrauma
|
||||
bool canOperate = toLeak.LengthSquared() < reach * reach;
|
||||
if (canOperate)
|
||||
{
|
||||
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: Identifier.Empty, requireEquip: true, operateTarget: Leak),
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () =>
|
||||
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: Identifier.Empty, requireEquip: true, operateTarget: Leak)
|
||||
{
|
||||
// Use an empty filter to override the default
|
||||
EndNodeFilter = n => true
|
||||
},
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () =>
|
||||
{
|
||||
if (CheckObjectiveSpecific()) { IsCompleted = true; }
|
||||
else
|
||||
{
|
||||
if (CheckObjectiveSpecific()) { IsCompleted = true; }
|
||||
else
|
||||
{
|
||||
// Failed to operate. Probably too far.
|
||||
Abandon = true;
|
||||
}
|
||||
});
|
||||
// Failed to operate. Probably too far.
|
||||
Abandon = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -178,7 +182,7 @@ namespace Barotrauma
|
||||
requiredCondition = () =>
|
||||
Leak.Submarine == character.Submarine &&
|
||||
Leak.linkedTo.Any(e => e is Hull h && (character.CurrentHull == h || h.linkedTo.Contains(character.CurrentHull))),
|
||||
endNodeFilter = n => n.Waypoint.CurrentHull != null && Leak.linkedTo.Any(e => e is Hull h && h == n.Waypoint.CurrentHull),
|
||||
endNodeFilter = IsSuitableEndNode,
|
||||
// The Go To objective can be abandoned if the leak is fixed (in which case we don't want to use the dialogue)
|
||||
SpeakCannotReachCondition = () => !CheckObjectiveSpecific()
|
||||
},
|
||||
@@ -197,6 +201,14 @@ namespace Barotrauma
|
||||
}
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref gotoObjective));
|
||||
|
||||
bool IsSuitableEndNode(PathNode n)
|
||||
{
|
||||
if (n.Waypoint.CurrentHull is null) { return false; }
|
||||
if (n.Waypoint.CurrentHull.ConnectedGaps.Contains(Leak)) { return true; }
|
||||
// Accept also nodes located in the linked hulls (multi-hull rooms)
|
||||
return Leak.linkedTo.Any(e => e is Hull h && h.linkedTo.Contains(n.Waypoint.CurrentHull));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-12
@@ -54,7 +54,7 @@ namespace Barotrauma
|
||||
public bool AllowVariants { get; set; }
|
||||
public bool Equip { get; set; }
|
||||
public bool Wear { get; set; }
|
||||
public bool RequireLoaded { get; set; }
|
||||
public bool RequireNonEmpty { get; set; }
|
||||
public bool EvaluateCombatPriority { get; set; }
|
||||
public bool CheckPathForEachItem { get; set; }
|
||||
public bool SpeakIfFails { get; set; }
|
||||
@@ -123,6 +123,11 @@ namespace Barotrauma
|
||||
return ignoredTags;
|
||||
}
|
||||
|
||||
public static Func<PathNode, bool> CreateEndNodeFilter(ISpatialEntity targetEntity)
|
||||
{
|
||||
return n => (n.Waypoint.Ladders == null || n.Waypoint.IsInWater) && Vector2.DistanceSquared(n.Waypoint.WorldPosition, targetEntity.WorldPosition) <= MathUtils.Pow2(DefaultReach);
|
||||
}
|
||||
|
||||
private bool CheckInventory()
|
||||
{
|
||||
if (IdentifiersOrTags == null) { return false; }
|
||||
@@ -155,11 +160,6 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (character.Submarine == null)
|
||||
{
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (IdentifiersOrTags != null && !isDoneSeeking)
|
||||
{
|
||||
if (checkInventory)
|
||||
@@ -171,9 +171,14 @@ namespace Barotrauma
|
||||
}
|
||||
if (!isDoneSeeking)
|
||||
{
|
||||
if (character.Submarine == null)
|
||||
{
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (!AllowDangerousPressure)
|
||||
{
|
||||
bool dangerousPressure = character.CurrentHull == null || character.CurrentHull.LethalPressure > 0 && character.PressureProtection <= 0;
|
||||
bool dangerousPressure = !character.IsProtectedFromPressure && (character.CurrentHull == null || character.CurrentHull.LethalPressure > 0);
|
||||
if (dangerousPressure)
|
||||
{
|
||||
#if DEBUG
|
||||
@@ -192,6 +197,11 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (character.Submarine == null)
|
||||
{
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (targetItem == null || targetItem.Removed)
|
||||
{
|
||||
#if DEBUG
|
||||
@@ -307,7 +317,8 @@ namespace Barotrauma
|
||||
{
|
||||
// If the root container changes, the item is no longer where it was (taken by someone -> need to find another item)
|
||||
AbortCondition = obj => targetItem == null || targetItem.GetRootInventoryOwner() != moveToTarget,
|
||||
SpeakIfFails = false
|
||||
SpeakIfFails = false,
|
||||
endNodeFilter = CreateEndNodeFilter(moveToTarget)
|
||||
};
|
||||
},
|
||||
onAbandon: () =>
|
||||
@@ -391,10 +402,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (!itemInventory.Container.HasRequiredItems(character, addMessage: false)) { continue; }
|
||||
}
|
||||
float itemPriority = 1;
|
||||
float itemPriority = item.Prefab.BotPriority;
|
||||
if (GetItemPriority != null)
|
||||
{
|
||||
itemPriority = GetItemPriority(item);
|
||||
itemPriority *= GetItemPriority(item);
|
||||
}
|
||||
Entity rootInventoryOwner = item.GetRootInventoryOwner();
|
||||
if (rootInventoryOwner is Item ownerItem)
|
||||
@@ -513,7 +524,7 @@ namespace Barotrauma
|
||||
float lowestCost = float.MaxValue;
|
||||
foreach (MapEntityPrefab prefab in MapEntityPrefab.List)
|
||||
{
|
||||
if (!(prefab is ItemPrefab itemPrefab)) { continue; }
|
||||
if (prefab is not ItemPrefab itemPrefab) { continue; }
|
||||
if (IdentifiersOrTags.Any(id => id == prefab.Identifier || prefab.Tags.Contains(id)))
|
||||
{
|
||||
float cost = itemPrefab.DefaultPrice != null && itemPrefab.CanBeBought ?
|
||||
@@ -561,7 +572,7 @@ namespace Barotrauma
|
||||
if (ignoredIdentifiersOrTags != null && CheckItemIdentifiersOrTags(item, ignoredIdentifiersOrTags)) { return false; }
|
||||
if (item.Condition < TargetCondition) { return false; }
|
||||
if (ItemFilter != null && !ItemFilter(item)) { return false; }
|
||||
if (RequireLoaded && item.Components.Any(i => !i.IsLoaded(character))) { return false; }
|
||||
if (RequireNonEmpty && item.Components.Any(i => !i.IsNotEmpty(character))) { return false; }
|
||||
return CheckItemIdentifiersOrTags(item, IdentifiersOrTags) || (AllowVariants && !item.Prefab.VariantOf.IsEmpty && IdentifiersOrTags.Contains(item.Prefab.VariantOf));
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ namespace Barotrauma
|
||||
public bool CheckInventory { get; set; }
|
||||
public bool EvaluateCombatPriority { get; set; }
|
||||
public bool CheckPathForEachItem { get; set; }
|
||||
public bool RequireLoaded { get; set; }
|
||||
public bool RequireNonEmpty { get; set; }
|
||||
public bool RequireAllItems { get; set; }
|
||||
|
||||
private readonly ImmutableArray<Identifier> gearTags;
|
||||
@@ -61,7 +61,7 @@ namespace Barotrauma
|
||||
AllowStealing = AllowStealing,
|
||||
ignoredIdentifiersOrTags = ignoredTags,
|
||||
CheckPathForEachItem = CheckPathForEachItem,
|
||||
RequireLoaded = RequireLoaded,
|
||||
RequireNonEmpty = RequireNonEmpty,
|
||||
ItemCount = count,
|
||||
SpeakIfFails = RequireAllItems
|
||||
},
|
||||
|
||||
+50
-24
@@ -50,6 +50,7 @@ namespace Barotrauma
|
||||
private readonly float minDistance = 50;
|
||||
private readonly float seekGapsInterval = 1;
|
||||
private float seekGapsTimer;
|
||||
private bool cantFindDivingGear;
|
||||
|
||||
/// <summary>
|
||||
/// Display units
|
||||
@@ -90,7 +91,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public bool UseDistanceRelativeToAimSourcePos { get; set; } = false;
|
||||
|
||||
public override bool AbandonWhenCannotCompleteSubjectives => !repeat;
|
||||
public override bool AbandonWhenCannotCompleteSubjectives => false;
|
||||
|
||||
public override bool AllowOutsideSubmarine => AllowGoingOutside;
|
||||
public override bool AllowInAnySub => true;
|
||||
@@ -263,48 +264,73 @@ namespace Barotrauma
|
||||
}
|
||||
if (!Abandon)
|
||||
{
|
||||
if (getDivingGearIfNeeded && !character.LockHands)
|
||||
if (getDivingGearIfNeeded)
|
||||
{
|
||||
Character followTarget = Target as Character;
|
||||
bool needsDivingSuit = (!isInside || hasOutdoorNodes) && character.NeedsAir && !character.HasAbilityFlag(AbilityFlags.ImmuneToPressure);
|
||||
bool needsDivingGear = needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit);
|
||||
if (Mimic)
|
||||
bool needsDivingSuit = (!isInside || hasOutdoorNodes) && !character.IsImmuneToPressure;
|
||||
bool tryToGetDivingGear = needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit);
|
||||
bool tryToGetDivingSuit = needsDivingSuit;
|
||||
if (Mimic && !character.IsImmuneToPressure)
|
||||
{
|
||||
if (HumanAIController.HasDivingSuit(followTarget))
|
||||
{
|
||||
needsDivingGear = true;
|
||||
needsDivingSuit = true;
|
||||
tryToGetDivingGear = true;
|
||||
tryToGetDivingSuit = true;
|
||||
}
|
||||
else if (HumanAIController.HasDivingMask(followTarget))
|
||||
else if (HumanAIController.HasDivingMask(followTarget) && character.CharacterHealth.OxygenLowResistance < 1)
|
||||
{
|
||||
needsDivingGear = true;
|
||||
tryToGetDivingGear = true;
|
||||
}
|
||||
}
|
||||
bool needsEquipment = false;
|
||||
float minOxygen = AIObjectiveFindDivingGear.GetMinOxygen(character);
|
||||
if (needsDivingSuit)
|
||||
if (tryToGetDivingSuit)
|
||||
{
|
||||
needsEquipment = !HumanAIController.HasDivingSuit(character, minOxygen);
|
||||
}
|
||||
else if (needsDivingGear)
|
||||
else if (tryToGetDivingGear)
|
||||
{
|
||||
needsEquipment = !HumanAIController.HasDivingGear(character, minOxygen);
|
||||
}
|
||||
if (needsEquipment)
|
||||
if (character.LockHands)
|
||||
{
|
||||
cantFindDivingGear = true;
|
||||
}
|
||||
if (cantFindDivingGear && needsDivingSuit)
|
||||
{
|
||||
// Don't try to reach the target without a suit because it's lethal.
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (needsEquipment && !cantFindDivingGear)
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
if (findDivingGear != null && !findDivingGear.CanBeCompleted)
|
||||
{
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: false, objectiveManager),
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref findDivingGear));
|
||||
}
|
||||
else
|
||||
{
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager),
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref findDivingGear));
|
||||
}
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: tryToGetDivingSuit, objectiveManager),
|
||||
onAbandon: () =>
|
||||
{
|
||||
cantFindDivingGear = true;
|
||||
if (needsDivingSuit)
|
||||
{
|
||||
// Shouldn't try to reach the target without a suit, because it's lethal.
|
||||
Abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try again without requiring the diving suit
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: false, objectiveManager),
|
||||
onAbandon: () =>
|
||||
{
|
||||
Abandon = character.CurrentHull != null && (objectiveManager.CurrentOrder != this || Target.Submarine == null);
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
},
|
||||
onCompleted: () =>
|
||||
{
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
});
|
||||
}
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref findDivingGear));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -170,7 +170,8 @@ namespace Barotrauma
|
||||
TargetHull = character.CurrentHull;
|
||||
}
|
||||
|
||||
if (behavior == BehaviorType.StayInHull)
|
||||
bool currentTargetIsInvalid = currentTarget == null || IsForbidden(currentTarget) || (PathSteering.CurrentPath != null && PathSteering.CurrentPath.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull)));
|
||||
if (behavior == BehaviorType.StayInHull && !currentTargetIsInvalid)
|
||||
{
|
||||
currentTarget = TargetHull;
|
||||
bool stayInHull = character.CurrentHull == currentTarget && IsSteeringFinished() && !character.IsClimbing;
|
||||
@@ -190,9 +191,6 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
bool currentTargetIsInvalid = currentTarget == null || IsForbidden(currentTarget) ||
|
||||
(PathSteering.CurrentPath != null && PathSteering.CurrentPath.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull)));
|
||||
|
||||
if (currentTarget != null && !currentTargetIsInvalid)
|
||||
{
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC && !character.IsEscorted)
|
||||
|
||||
+7
-6
@@ -364,8 +364,7 @@ namespace Barotrauma
|
||||
CurrentOrders.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
var currentOrderInfo = character.GetCurrentOrder(currentOrder);
|
||||
if (currentOrderInfo is Order)
|
||||
if (character.GetCurrentOrder(currentOrder) is Order currentOrderInfo)
|
||||
{
|
||||
int currentPriority = currentOrderInfo.ManualPriority;
|
||||
if (currentOrder.ManualPriority != currentPriority)
|
||||
@@ -539,7 +538,8 @@ namespace Barotrauma
|
||||
KeepActiveWhenReady = true,
|
||||
CheckInventory = true,
|
||||
Equip = false,
|
||||
FindAllItems = true
|
||||
FindAllItems = true,
|
||||
RequireNonEmpty = false
|
||||
};
|
||||
break;
|
||||
case "findweapon":
|
||||
@@ -555,7 +555,8 @@ namespace Barotrauma
|
||||
KeepActiveWhenReady = false,
|
||||
CheckInventory = false,
|
||||
EvaluateCombatPriority = true,
|
||||
FindAllItems = false
|
||||
FindAllItems = false,
|
||||
RequireNonEmpty = true
|
||||
};
|
||||
}
|
||||
prepareObjective.KeepActiveWhenReady = false;
|
||||
@@ -600,9 +601,9 @@ namespace Barotrauma
|
||||
|
||||
Order dismissOrder = currentOrder.GetDismissal();
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.IsSinglePlayer)
|
||||
if (GameMain.GameSession?.CrewManager is CrewManager cm && cm.IsSinglePlayer)
|
||||
{
|
||||
GameMain.GameSession.CrewManager.SetCharacterOrder(character, dismissOrder);
|
||||
character.SetOrder(dismissOrder, isNewOrder: true, speak: false);
|
||||
}
|
||||
#else
|
||||
GameMain.Server?.SendOrderChatMessage(new OrderChatMessage(dismissOrder, character, character));
|
||||
|
||||
+6
-1
@@ -23,6 +23,11 @@ namespace Barotrauma
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private AIObjectiveGetItem getItemObjective;
|
||||
|
||||
/// <summary>
|
||||
/// If undefined, a default filter will be used.
|
||||
/// </summary>
|
||||
public Func<PathNode, bool> EndNodeFilter;
|
||||
|
||||
public bool Override { get; set; } = true;
|
||||
|
||||
public override bool CanBeCompleted => base.CanBeCompleted && (!useController || controller != null);
|
||||
@@ -232,7 +237,7 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(target.Item, character, objectiveManager, closeEnough: 50)
|
||||
{
|
||||
TargetName = target.Item.Name,
|
||||
endNodeFilter = node => node.Waypoint.Ladders == null
|
||||
endNodeFilter = EndNodeFilter ?? AIObjectiveGetItem.CreateEndNodeFilter(target.Item)
|
||||
},
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective));
|
||||
|
||||
+4
-3
@@ -27,6 +27,7 @@ namespace Barotrauma
|
||||
public bool FindAllItems { get; set; }
|
||||
public bool Equip { get; set; }
|
||||
public bool EvaluateCombatPriority { get; set; }
|
||||
public bool RequireNonEmpty { get; set; }
|
||||
|
||||
private AIObjective GetSubObjective()
|
||||
{
|
||||
@@ -74,7 +75,7 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
|
||||
}
|
||||
else if (items.Any(i => i.Components.Any(i => !i.IsLoaded(character))))
|
||||
else if (items.Any(i => i.Components.Any(i => !i.IsNotEmpty(character))))
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
@@ -106,7 +107,7 @@ namespace Barotrauma
|
||||
CheckInventory = CheckInventory,
|
||||
Equip = Equip,
|
||||
EvaluateCombatPriority = EvaluateCombatPriority,
|
||||
RequireLoaded = true,
|
||||
RequireNonEmpty = RequireNonEmpty,
|
||||
RequireAllItems = requireAll
|
||||
},
|
||||
onCompleted: () =>
|
||||
@@ -157,7 +158,7 @@ namespace Barotrauma
|
||||
{
|
||||
EvaluateCombatPriority = EvaluateCombatPriority,
|
||||
SpeakIfFails = true,
|
||||
RequireLoaded = true
|
||||
RequireNonEmpty = RequireNonEmpty
|
||||
};
|
||||
}
|
||||
if (!TryAddSubObjective(ref getSingleItemObjective, getItemConstructor,
|
||||
|
||||
+22
-12
@@ -17,7 +17,6 @@ namespace Barotrauma
|
||||
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private AIObjectiveContainItem refuelObjective;
|
||||
private float previousCondition = -1;
|
||||
private RepairTool repairTool;
|
||||
|
||||
private const float WaitTimeBeforeRepair = 0.5f;
|
||||
@@ -196,15 +195,7 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
}
|
||||
}
|
||||
if (previousCondition == -1)
|
||||
{
|
||||
previousCondition = Item.Condition;
|
||||
}
|
||||
else if (Item.Condition < previousCondition)
|
||||
{
|
||||
// If the current condition is less than the previous condition, we can't complete the task, so let's abandon it. The item is probably deteriorating at a greater speed than we can repair it.
|
||||
Abandon = true;
|
||||
}
|
||||
CheckPreviousCondition(deltaTime);
|
||||
}
|
||||
if (Abandon)
|
||||
{
|
||||
@@ -229,7 +220,6 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref goToObjective,
|
||||
constructor: () =>
|
||||
{
|
||||
previousCondition = -1;
|
||||
var objective = new AIObjectiveGoTo(Item, character, objectiveManager)
|
||||
{
|
||||
TargetName = Item.Name
|
||||
@@ -251,6 +241,27 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private const float conditionCheckDelay = 1;
|
||||
private float conditionCheckTimer;
|
||||
private float previousCondition;
|
||||
private void CheckPreviousCondition(float deltaTime)
|
||||
{
|
||||
if (Item == null || Item.Removed) { return; }
|
||||
conditionCheckTimer -= deltaTime;
|
||||
if (conditionCheckTimer > 0) { return; }
|
||||
conditionCheckTimer = conditionCheckDelay;
|
||||
if (previousCondition > -1 && Item.Condition < previousCondition)
|
||||
{
|
||||
// If the current condition is less than the previous condition, we can't complete the task, so let's abandon it. The item is probably deteriorating at a greater speed than we can repair it.
|
||||
Abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the previous condition is not yet stored or if it's valid (greater or equal to current condition), save the condition for the next check here.
|
||||
previousCondition = Item.Condition;
|
||||
}
|
||||
}
|
||||
|
||||
private void FindRepairTool()
|
||||
{
|
||||
foreach (Repairable repairable in Item.Repairables)
|
||||
@@ -303,7 +314,6 @@ namespace Barotrauma
|
||||
base.Reset();
|
||||
goToObjective = null;
|
||||
refuelObjective = null;
|
||||
previousCondition = -1;
|
||||
repairTool = null;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-15
@@ -320,10 +320,10 @@ namespace Barotrauma
|
||||
foreach (KeyValuePair<Identifier, float> treatmentSuitability in currentTreatmentSuitabilities)
|
||||
{
|
||||
if (treatmentSuitability.Value <= cprSuitability) { continue; }
|
||||
if (MapEntityPrefab.Find(null, treatmentSuitability.Key, showErrorMessages: false) is ItemPrefab itemPrefab)
|
||||
if (ItemPrefab.Prefabs.TryGet(treatmentSuitability.Key, out ItemPrefab itemPrefab))
|
||||
{
|
||||
if (!Item.ItemList.Any(it => ((MapEntity)it).Prefab.Identifier == treatmentSuitability.Key)) { continue; }
|
||||
suitableItemIdentifiers.Add(treatmentSuitability.Key);
|
||||
if (Item.ItemList.None(it => it.Prefab.Identifier == treatmentSuitability.Key)) { continue; }
|
||||
suitableItemIdentifiers.Add(itemPrefab.Identifier);
|
||||
//only list the first 4 items
|
||||
if (itemNameList.Count < 4)
|
||||
{
|
||||
@@ -482,18 +482,6 @@ namespace Barotrauma
|
||||
|
||||
public static IEnumerable<Affliction> GetSortedAfflictions(Character character, bool excludeBuffs = true) => CharacterHealth.SortAfflictionsBySeverity(character.CharacterHealth.GetAllAfflictions(), excludeBuffs);
|
||||
|
||||
public static IEnumerable<Affliction> GetTreatableAfflictions(Character character)
|
||||
{
|
||||
var allAfflictions = character.CharacterHealth.GetAllAfflictions();
|
||||
foreach (Affliction affliction in allAfflictions)
|
||||
{
|
||||
if (affliction.Prefab.IsBuff || affliction.Strength < affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
if (!affliction.Prefab.TreatmentSuitability.Any(kvp => kvp.Value > 0)) { continue; }
|
||||
if (allAfflictions.Any(otherAffliction => affliction.Prefab.IgnoreTreatmentIfAfflictedBy.Contains(otherAffliction.Identifier))) { continue; }
|
||||
yield return affliction;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
+22
-3
@@ -26,7 +26,7 @@ namespace Barotrauma
|
||||
// When targeting player characters, always treat them when ordered, else use the threshold so that minor/non-severe damage is ignored.
|
||||
// If we ignore any damage when the player orders a bot to do healings, it's observed to cause confusion among the players.
|
||||
// On the other hand, if the bots too eagerly heal characters when it's not necessary, it's inefficient and can feel frustrating, because it can't be controlled.
|
||||
return character == target || manager.HasOrder<AIObjectiveRescueAll>() ? (target.IsPlayer ? 100 : vitalityThresholdForOrders) : vitalityThreshold;
|
||||
return character == target || manager.HasOrder<AIObjectiveRescueAll>() ? (target.IsPlayer && target.HealthPercentage < 100 ? 100 : vitalityThresholdForOrders) : vitalityThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,15 +67,34 @@ namespace Barotrauma
|
||||
float vitality = 100;
|
||||
vitality -= character.Bleeding * 2;
|
||||
vitality += Math.Min(character.Oxygen, 0);
|
||||
vitality -= character.CharacterHealth.GetAfflictionStrength("paralysis");
|
||||
foreach (Affliction affliction in AIObjectiveRescue.GetTreatableAfflictions(character))
|
||||
foreach (Affliction affliction in GetTreatableAfflictions(character))
|
||||
{
|
||||
float strength = character.CharacterHealth.GetPredictedStrength(affliction, predictFutureDuration: 10.0f);
|
||||
vitality -= affliction.GetVitalityDecrease(character.CharacterHealth, strength) / character.MaxVitality * 100;
|
||||
if (affliction.Prefab.AfflictionType == AfflictionPrefab.ParalysisType)
|
||||
{
|
||||
vitality -= affliction.Strength;
|
||||
}
|
||||
else if (affliction.Prefab.AfflictionType == AfflictionPrefab.PoisonType)
|
||||
{
|
||||
vitality -= affliction.Strength;
|
||||
}
|
||||
}
|
||||
return Math.Clamp(vitality, 0, 100);
|
||||
}
|
||||
|
||||
public static IEnumerable<Affliction> GetTreatableAfflictions(Character character)
|
||||
{
|
||||
var allAfflictions = character.CharacterHealth.GetAllAfflictions();
|
||||
foreach (Affliction affliction in allAfflictions)
|
||||
{
|
||||
if (affliction.Prefab.IsBuff || affliction.Strength < affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
if (affliction.Prefab.TreatmentSuitability.None(kvp => kvp.Value > 0)) { continue; }
|
||||
if (allAfflictions.Any(otherAffliction => affliction.Prefab.IgnoreTreatmentIfAfflictedBy.Contains(otherAffliction.Identifier))) { continue; }
|
||||
yield return affliction;
|
||||
}
|
||||
}
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Character target)
|
||||
=> new AIObjectiveRescue(character, target, objectiveManager, PriorityModifier);
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace Barotrauma
|
||||
private AIObjectiveGoTo moveInsideObjective, moveOutsideObjective;
|
||||
private bool usingEscapeBehavior, isSteeringThroughGap;
|
||||
|
||||
public override bool AllowOutsideSubmarine => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
public AIObjectiveReturn(Character character, Character orderGiver, AIObjectiveManager objectiveManager, float priorityModifier = 1.0f) : base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
ReturnTarget = GetReturnTarget(Submarine.MainSubs) ?? GetReturnTarget(Submarine.Loaded);
|
||||
|
||||
Reference in New Issue
Block a user