1 using Microsoft.Xna.Framework;
3 using System.Collections.Generic;
17 private readonly
bool repeat;
19 private float waitUntilPathUnreachable;
20 private readonly
bool getDivingGearIfNeeded;
40 private float _closeEnoughMultiplier = 1;
43 get {
return _closeEnoughMultiplier; }
44 set { _closeEnoughMultiplier = Math.Max(value, 1); }
46 private float _closeEnough = 50;
47 private readonly
float minDistance = 50;
48 private readonly
float seekGapsInterval = 1;
49 private float seekGapsTimer;
50 private bool cantFindDivingGear;
78 _closeEnough = Math.Max(minDistance, value);
99 private readonly
Identifier ExoSuitRefuel =
"dialog.exosuit.refuel".ToIdentifier();
100 private readonly
Identifier ExoSuitOutOfFuel =
"dialog.exosuit.outoffuel".ToIdentifier();
147 private readonly
float avoidLookAheadDistance = 5;
148 private readonly
float pathWaitingTime = 3;
154 this.repeat = repeat;
155 waitUntilPathUnreachable = pathWaitingTime;
156 this.getDivingGearIfNeeded = getDivingGearIfNeeded;
172 private void SpeakCannotReach()
177 DebugConsole.NewMessage($
"{character.Name}: Cannot reach the target: {Target}", Color.Yellow);
188 if (msg.IsNullOrEmpty() || !msg.Loaded) {
return; }
194 protected override void Act(
float deltaTime)
201 if (checkExoSuitTimer <= 0)
203 checkExoSuitTimer = CheckExoSuitTime * Rand.Range(0.9f, 1.1f);
205 exoSuit.GetComponent<
Powered>() is not { HasPower: true })
212 character.
Speak(TextManager.Get(ExoSuitRefuel).Value, minDurationBetweenSimilar: 10f, identifier: ExoSuitRefuel);
217 if (containedItem.
HasTag(Tags.DivingSuitFuel) && containedItem.
Condition <= 0)
224 const int targetSlot = 1;
225 Item fuelRod = fuelRods.MaxBy(b => b.Condition);
226 exoSuitInventory.TryPutItem(fuelRod, targetSlot, allowSwapping:
true, allowCombine:
true, user:
character);
230 character.
Speak(TextManager.Get(ExoSuitOutOfFuel).Value, minDurationBetweenSimilar: 30.0f, identifier: ExoSuitOutOfFuel);
236 checkExoSuitTimer -= deltaTime;
275 isUnreachable =
true;
285 isUnreachable =
true;
319 waitUntilPathUnreachable -= deltaTime;
323 if (waitUntilPathUnreachable < 0)
325 waitUntilPathUnreachable = pathWaitingTime;
328 if (!IsDoneFollowing())
341 waitUntilPathUnreachable = pathWaitingTime;
345 if (getDivingGearIfNeeded)
350 bool tryToGetDivingSuit = needsDivingSuit;
355 tryToGetDivingGear =
true;
356 tryToGetDivingSuit =
true;
360 tryToGetDivingGear =
true;
363 bool needsEquipment =
false;
365 if (tryToGetDivingSuit)
369 else if (tryToGetDivingGear)
375 cantFindDivingGear =
true;
377 if (cantFindDivingGear && needsDivingSuit)
383 if (needsEquipment && !cantFindDivingGear)
389 cantFindDivingGear =
true;
398 RemoveSubObjective(ref findDivingGear);
403 RemoveSubObjective(ref findDivingGear);
407 RemoveSubObjective(ref findDivingGear);
411 onCompleted: () => RemoveSubObjective(ref findDivingGear));
415 if (IsDoneFollowing())
420 float maxGapDistance = 500;
429 if (seekGapsTimer > 0)
431 seekGapsTimer -= deltaTime;
439 SeekGaps(maxGapDistance);
440 seekGapsTimer = seekGapsInterval * Rand.Range(0.1f, 1.1f);
455 if (closestBody !=
null)
483 if (checkScooterTimer <= 0)
486 checkScooterTimer = CheckScooterTime * Rand.Range(0.9f, 1.1f);
488 bool shouldUseScooter =
Mimic && targetCharacter !=
null && targetCharacter.
HasEquippedItem(Tags.Scooter, allowBroken:
false);
489 if (!shouldUseScooter)
491 float threshold = 500;
495 shouldUseScooter = Math.Abs(diff.X) > threshold || Math.Abs(diff.Y) > 150;
505 scooter = equippedScooters.FirstOrDefault();
507 else if (shouldUseScooter)
519 bool hasBattery =
false;
520 if (
HumanAIController.
HasItem(
character, Tags.Scooter, out IEnumerable<Item> nonEquippedScootersWithBattery, containedTag: Tags.MobileBattery, conditionPercentage: 1, requireEquipped:
false))
522 scooter = nonEquippedScootersWithBattery.FirstOrDefault();
527 scooter = nonEquippedScootersWithoutBattery.FirstOrDefault();
531 if (scooter !=
null && hasBattery)
540 if (shouldUseScooter)
550 if (!scooter.
Combine(batteries.OrderByDescending(b => b.Condition).First(),
character))
569 checkScooterTimer -= deltaTime;
576 checkScooterTimer = 0;
581 Func<PathNode, bool> nodeFilter =
null;
584 nodeFilter = n => n.Waypoint.CurrentHull !=
null;
590 nodeFilter = n => n.Waypoint.Submarine ==
null;
594 nodeFilter = n => n.Waypoint.Submarine !=
null || n.Waypoint.Ruin !=
null;
611 nodeFilter: nodeFilter,
652 void UseScooter(Vector2 targetWorldPos)
663 Vector2 dir = Vector2.Normalize(diff);
666 float sqrDist = diff.LengthSquared();
674 bool isFacing = dot > 0.9f;
675 if (!isFacing && sqrDist > MathUtils.Pow2(
CloseEnough))
689 bool IsDoneFollowing()
705 private bool useScooter;
706 private float checkScooterTimer;
707 private const float CheckScooterTime = 0.5f;
709 private float checkExoSuitTimer;
710 private const float CheckExoSuitTime = 2.0f;
716 if (target is
Hull h)
720 else if (target is
Item i)
722 return i.CurrentHull;
726 return c.CurrentHull ?? c.AnimController.CurrentHull;
730 return Hull.
FindHull(structure.Position, useWorldCoordinates:
false);
732 else if (target is
Gap g)
734 return g.FlowTargetHull;
738 return wp.CurrentHull;
752 private void SeekGaps(
float maxDistance)
754 Gap selectedGap =
null;
755 float selectedDistance = -1;
759 if (gap.
Open < 1) {
continue; }
767 if (Vector2.Dot(Vector2.Normalize(toGap), toTargetNormalized) < 0) {
continue; }
768 float squaredDistance = toGap.LengthSquared();
769 if (squaredDistance > maxDistance * maxDistance) {
continue; }
770 if (selectedGap ==
null || squaredDistance < selectedDistance)
773 selectedDistance = squaredDistance;
860 private void StopMovement()
888 findDivingGear =
null;
893 pathSteering.ResetPath();
virtual bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)
virtual void SelectTarget(AITarget target)
bool TakeItem(Item item, CharacterInventory targetInventory, bool equip, bool wear=false, bool dropOtherIfCannotMove=true, bool allowSwapping=false, bool storeUnequipped=false, IEnumerable< Identifier > targetTags=null)
SteeringManager SteeringManager
bool HasValidPath(bool requireNonDirty=true, bool requireUnfinished=true, Func< WayPoint, bool > nodePredicate=null)
Is the current path valid, using the provided parameters.
bool IsCurrentPathNullOrUnreachable
void FaceTarget(ISpatialEntity target)
static float GetMinOxygen(Character character)
Func< PathNode, bool > endNodeFilter
Func< float > PriorityGetter
override float GetPriority()
override bool KeepDivingGearOn
Func< bool > requiredCondition
Doesn't allow the objective to complete if this condition is false
float CloseEnough
Display units
Identifier DialogueIdentifier
override Identifier Identifier
float ExtraDistanceOutsideSub
AIObjectiveGoTo(ISpatialEntity target, Character character, AIObjectiveManager objectiveManager, bool repeat=false, bool getDivingGearIfNeeded=true, float priorityModifier=1, float closeEnough=0)
void ForceAct(float deltaTime)
LocalizedString TargetName
static Hull GetTargetHull(ISpatialEntity target)
override void OnAbandon()
override void OnCompleted()
Func< bool > SpeakCannotReachCondition
override bool AllowInAnySub
float CloseEnoughMultiplier
float ExtraDistanceWhileSwimming
bool UseDistanceRelativeToAimSourcePos
If true, the distance to the destination is calculated from the character's AimSourcePos (= shoulder)...
bool FaceTargetOnCompleted
override bool CheckObjectiveState()
Should return whether the objective is completed or not.
override void Act(float deltaTime)
override bool AbandonWhenCannotCompleteSubObjectives
bool AlwaysUseEuclideanDistance
override bool AllowOutsideSubmarine
virtual bool IgnoreUnsafeHulls
float Priority
Final priority value after all calculations.
readonly Character character
IndoorsSteeringManager PathSteering
HumanAIController HumanAIController
readonly AIObjectiveManager objectiveManager
bool FailedToFindDivingGearForDepth
AIObjective CurrentObjective
Includes orders.
AIObjective?? CurrentOrder
The AIObjective in CurrentOrders with the highest AIObjective.Priority
float GetOrderPriority(AIObjective objective)
bool IsOrder(AIObjective objective)
Vector2 AimSourceWorldPos
float OxygenLowResistance
0-1.
bool TryPutItemInAnySlot(Item item)
void SelectCharacter(Character character)
void SetInput(InputType inputType, bool hit, bool held)
void Speak(string message, ChatMessageType? messageType=null, float delay=0.0f, Identifier identifier=default, float minDurationBetweenSimilar=0.0f)
CharacterHealth CharacterHealth
bool CanInteractWith(Character c, float maxDist=200.0f, bool checkVisibility=true, bool skipDistanceCheck=false)
virtual AIController AIController
bool HasEquippedItem(Item item, InvSlotType? slotType=null, Func< InvSlotType, bool > predicate=null)
override Vector2? SimPosition
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
CharacterInventory Inventory
bool Unequip(Item item)
Attempts to unequip an item. First tries to put the item in any slot. If that fails,...
Item GetEquippedItem(Identifier tagOrIdentifier=default, InvSlotType? slotType=null)
Vector2 GetRelativeSimPosition(ISpatialEntity target, Vector2? worldPos=null)
Item SelectedSecondaryItem
The secondary selected item. It's an item other than a device (see SelectedItem), e....
bool TryPutItemInBag(Item item)
override Vector2 Position
void ReleaseSecondaryItem()
readonly AnimController AnimController
bool HasHandsFull(out(Item leftHandItem, Item rightHandItem) items)
bool CanSeeTarget(ISpatialEntity target, ISpatialEntity seeingEntity=null, bool seeThroughWindows=false, bool checkFacing=false)
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
static readonly List< InvSlotType > AnySlot
virtual Vector2 WorldPosition
static List< Gap > GapList
static Hull FindHull(Vector2 position, Hull guess=null, bool useWorldCoordinates=true, bool inclusive=true)
Returns the hull which contains the point (or null if it isn't inside any)
Hull(Rectangle rectangle)
static bool HasDivingSuit(Character character, float conditionPercentage=0, bool requireOxygenTank=true, bool requireSuitablePressureProtection=true)
Check whether the character has a diving suit in usable condition, suitable pressure protection for t...
static bool HasItem(Character character, Identifier tagOrIdentifier, out IEnumerable< Item > items, Identifier containedTag=default, float conditionPercentage=0, bool requireEquipped=false, bool recursive=true, Func< Item, bool > predicate=null)
Note: uses a single list for matching items. The item is reused each time when the method is called....
static bool HasDivingGear(Character character, float conditionPercentage=0, bool requireOxygenTank=true)
readonly HashSet< Hull > UnreachableHulls
void AskToRecalculateHullSafety(Hull hull)
bool NeedsDivingGear(Hull hull, out bool needsSuit)
bool UseOutsideWaypoints
Waypoints that are not linked to a sub (e.g. main path).
static bool IsFriendly(Character me, Character other, bool onlySameTeam=false)
readonly HashSet< Hull > UnsafeHulls
static bool HasDivingMask(Character character, float conditionPercentage=0, bool requireOxygenTank=true)
Check whether the character has a diving mask in usable condition plus some oxygen.
void SteeringSeek(Vector2 target, float weight, float minGapWidth=0, Func< PathNode, bool > startNodeFilter=null, Func< PathNode, bool > endNodeFilter=null, Func< PathNode, bool > nodeFilter=null, bool checkVisiblity=true)
void SteeringSeekSimple(Vector2 targetSimPos, float weight=1)
bool HasTag(Identifier tag)
IEnumerable< Item > ContainedItems
bool Combine(Item item, Character user)
override Vector2 SimPosition
void SteeringManual(float deltaTime, Vector2 velocity)
void SteeringSeek(Vector2 targetSimPos, float weight=1)
void SteeringAvoid(float deltaTime, float lookAheadDistance, float weight=1)
static Body CheckVisibility(Vector2 rayStart, Vector2 rayEnd, bool ignoreLevel=false, bool ignoreSubs=false, bool ignoreSensors=true, bool ignoreDisabledWalls=true, bool ignoreBranches=true, Predicate< Fixture > blocksVisibilityPredicate=null)
Check visibility between two points (in sim units).
override Vector2 SimPosition
override Vector2? Position