1 using Microsoft.Xna.Framework;
3 using System.Collections.Generic;
16 private readonly
bool repeat;
18 private float waitUntilPathUnreachable;
19 private readonly
bool getDivingGearIfNeeded;
39 private float _closeEnoughMultiplier = 1;
42 get {
return _closeEnoughMultiplier; }
43 set { _closeEnoughMultiplier = Math.Max(value, 1); }
45 private float _closeEnough = 50;
46 private readonly
float minDistance = 50;
47 private readonly
float seekGapsInterval = 1;
48 private float seekGapsTimer;
49 private bool cantFindDivingGear;
77 _closeEnough = Math.Max(minDistance, value);
143 private readonly
float avoidLookAheadDistance = 5;
144 private readonly
float pathWaitingTime = 3;
150 this.repeat = repeat;
151 waitUntilPathUnreachable = pathWaitingTime;
152 this.getDivingGearIfNeeded = getDivingGearIfNeeded;
168 private void SpeakCannotReach()
173 DebugConsole.NewMessage($
"{character.Name}: Cannot reach the target: {Target}", Color.Yellow);
184 if (msg.IsNullOrEmpty() || !msg.Loaded) {
return; }
190 protected override void Act(
float deltaTime)
234 isUnreachable =
true;
244 isUnreachable =
true;
278 waitUntilPathUnreachable -= deltaTime;
282 if (waitUntilPathUnreachable < 0)
284 waitUntilPathUnreachable = pathWaitingTime;
287 if (!IsDoneFollowing())
300 waitUntilPathUnreachable = pathWaitingTime;
304 if (getDivingGearIfNeeded)
309 bool tryToGetDivingSuit = needsDivingSuit;
314 tryToGetDivingGear =
true;
315 tryToGetDivingSuit =
true;
319 tryToGetDivingGear =
true;
322 bool needsEquipment =
false;
324 if (tryToGetDivingSuit)
328 else if (tryToGetDivingGear)
334 cantFindDivingGear =
true;
336 if (cantFindDivingGear && needsDivingSuit)
342 if (needsEquipment && !cantFindDivingGear)
348 cantFindDivingGear =
true;
357 RemoveSubObjective(ref findDivingGear);
362 RemoveSubObjective(ref findDivingGear);
366 RemoveSubObjective(ref findDivingGear);
370 onCompleted: () => RemoveSubObjective(ref findDivingGear));
374 if (IsDoneFollowing())
379 float maxGapDistance = 500;
388 if (seekGapsTimer > 0)
390 seekGapsTimer -= deltaTime;
398 SeekGaps(maxGapDistance);
399 seekGapsTimer = seekGapsInterval * Rand.Range(0.1f, 1.1f);
414 if (closestBody !=
null)
442 if (checkScooterTimer <= 0)
445 checkScooterTimer = checkScooterTime * Rand.Range(0.75f, 1.25f);
447 bool shouldUseScooter =
Mimic && targetCharacter !=
null && targetCharacter.
HasEquippedItem(Tags.Scooter, allowBroken:
false);
448 if (!shouldUseScooter)
450 float threshold = 500;
454 shouldUseScooter = Math.Abs(diff.X) > threshold || Math.Abs(diff.Y) > 150;
464 scooter = equippedScooters.FirstOrDefault();
466 else if (shouldUseScooter)
475 bool hasBattery =
false;
476 if (
HumanAIController.
HasItem(
character, Tags.Scooter, out IEnumerable<Item> nonEquippedScooters, containedTag: Tags.MobileBattery, conditionPercentage: 1, requireEquipped:
false))
479 scooter = nonEquippedScooters.FirstOrDefault();
485 scooter = _nonEquippedScooters.FirstOrDefault();
489 if (scooter !=
null && hasBattery)
498 if (shouldUseScooter)
508 if (!scooter.
Combine(batteries.OrderByDescending(b => b.Condition).First(),
character))
528 checkScooterTimer -= deltaTime;
535 checkScooterTimer = 0;
540 Func<PathNode, bool> nodeFilter =
null;
543 nodeFilter = n => n.Waypoint.CurrentHull !=
null;
549 nodeFilter = n => n.Waypoint.Submarine ==
null;
553 nodeFilter = n => n.Waypoint.Submarine !=
null || n.Waypoint.Ruin !=
null;
570 nodeFilter: nodeFilter,
611 void UseScooter(Vector2 targetWorldPos)
622 Vector2 dir = Vector2.Normalize(diff);
625 float sqrDist = diff.LengthSquared();
633 bool isFacing = dot > 0.9f;
634 if (!isFacing && sqrDist > MathUtils.Pow2(
CloseEnough))
648 bool IsDoneFollowing()
664 private bool useScooter;
665 private float checkScooterTimer;
666 private readonly
float checkScooterTime = 0.5f;
672 if (target is
Hull h)
676 else if (target is
Item i)
678 return i.CurrentHull;
682 return c.CurrentHull ?? c.AnimController.CurrentHull;
686 return Hull.
FindHull(structure.Position, useWorldCoordinates:
false);
688 else if (target is
Gap g)
690 return g.FlowTargetHull;
694 return wp.CurrentHull;
708 private void SeekGaps(
float maxDistance)
710 Gap selectedGap =
null;
711 float selectedDistance = -1;
715 if (gap.
Open < 1) {
continue; }
723 if (Vector2.Dot(Vector2.Normalize(toGap), toTargetNormalized) < 0) {
continue; }
724 float squaredDistance = toGap.LengthSquared();
725 if (squaredDistance > maxDistance * maxDistance) {
continue; }
726 if (selectedGap ==
null || squaredDistance < selectedDistance)
729 selectedDistance = squaredDistance;
817 private void StopMovement()
845 findDivingGear =
null;
850 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
override bool CheckObjectiveSpecific()
Should return whether the objective is completed or not.
float ExtraDistanceWhileSwimming
bool UseDistanceRelativeToAimSourcePos
If true, the distance to the destination is calculated from the character's AimSourcePos (= shoulder)...
bool FaceTargetOnCompleted
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.
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
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....
override Vector2 Position
void ReleaseSecondaryItem()
readonly AnimController AnimController
bool CanSeeTarget(ISpatialEntity target, ISpatialEntity seeingEntity=null, bool seeThroughWindows=false, bool checkFacing=false)
bool IsAnySlotAvailable(Item item)
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)
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