4 using Microsoft.Xna.Framework;
6 using System.Collections.Generic;
20 get {
return behavior; }
28 newTargetIntervalMin = 60;
29 newTargetIntervalMax = 120;
34 newTargetIntervalMin = 40;
35 newTargetIntervalMax = 60;
40 newTargetIntervalMin = 15;
41 newTargetIntervalMax = 30;
49 private float newTargetIntervalMin;
50 private float newTargetIntervalMax;
51 private float standStillMin;
52 private float standStillMax;
53 private readonly
float walkDurationMin = 5;
54 private readonly
float walkDurationMax = 10;
64 private Hull currentTarget;
65 private float newTargetTimer;
67 private bool searchingNewHull;
69 private float standStillTimer;
70 private float walkDuration;
74 const float chairCheckInterval = 5.0f;
75 private float chairCheckTimer;
77 private float autonomousObjectiveRetryTimer = 10;
79 private readonly List<Hull> targetHulls =
new List<Hull>(20);
80 private readonly List<float> hullWeights =
new List<float>(20);
85 standStillTimer = Rand.Range(-10.0f, 10.0f);
86 walkDuration = Rand.Range(0.0f, 10.0f);
87 chairCheckTimer = Rand.Range(0.0f, chairCheckInterval);
108 public override void Update(
float deltaTime)
113 private float timerMargin;
115 private void SetTargetTimerLow()
120 timerMargin = Math.Min(timerMargin, newTargetIntervalMin);
121 newTargetTimer = Math.Max(newTargetTimer, timerMargin);
124 private void SetTargetTimerHigh()
128 newTargetTimer = Math.Max(newTargetTimer, newTargetIntervalMin);
132 private void SetTargetTimerNormal()
134 newTargetTimer = currentTarget !=
null &&
character.
AnimController.
InWater ? newTargetIntervalMin : Rand.Range(newTargetIntervalMin, newTargetIntervalMax);
140 protected override void Act(
float deltaTime)
146 if (autonomousObjectiveRetryTimer > 0)
148 autonomousObjectiveRetryTimer -= deltaTime;
175 CleanupItems(deltaTime);
183 bool currentTargetIsInvalid =
184 currentTarget ==
null ||
196 else if (currentTarget !=
null)
208 if (currentTarget !=
null && !currentTargetIsInvalid)
214 currentTargetIsInvalid =
true;
221 currentTargetIsInvalid =
true;
228 if (newTargetTimer > timerMargin)
237 if (currentTarget ==
null)
244 SetTargetTimerHigh();
249 if (currentTarget ==
null)
254 if (newTargetTimer <= 0.0f)
256 if (!searchingNewHull)
259 searchingNewHull =
true;
262 else if (targetHulls.Any())
265 currentTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
271 if (node.Waypoint.CurrentHull ==
null) {
return false; }
276 }, endNodeFilter: node => node.Waypoint.Stairs ==
null && node.Waypoint.Ladders ==
null && (!isCurrentHullAllowed || !
IsForbidden(node.Waypoint.CurrentHull)));
277 if (path.Unreachable)
280 int index = targetHulls.IndexOf(currentTarget);
281 targetHulls.RemoveAt(index);
282 hullWeights.RemoveAt(index);
284 currentTarget =
null;
290 SetTargetTimerNormal();
291 searchingNewHull =
false;
296 SetTargetTimerHigh();
297 searchingNewHull =
false;
300 newTargetTimer -= deltaTime;
305 else if (currentTarget !=
null)
308 nodeFilter: node => node.Waypoint.CurrentHull !=
null,
309 endNodeFilter: node => node.Waypoint.Ladders ==
null && node.Waypoint.Stairs ==
null);
321 if (character.IsClimbing)
323 PathSteering.Reset();
324 character.StopClimbing();
326 var currentHull = character.CurrentHull;
327 if (!character.AnimController.InWater && currentHull !=
null)
329 standStillTimer -= deltaTime;
330 if (standStillTimer > 0.0f)
332 walkDuration = Rand.Range(walkDurationMin, walkDurationMax);
338 if (Vector2.DistanceSquared(c.
WorldPosition, character.WorldPosition) > 60.0f * 60.0f) {
continue; }
339 if ((humanAI.ObjectiveManager.CurrentObjective is
AIObjectiveIdle idleObjective && idleObjective.standStillTimer > 0.0f) ||
344 if (tooCloseCharacter !=
null &&
345 Math.Sign(tooCloseCharacter.
WorldPosition.X - character.WorldPosition.X) != Math.Sign(c.
WorldPosition.X - character.WorldPosition.X))
347 tooCloseCharacter =
null;
350 tooCloseCharacter = c;
356 if (tooCloseCharacter !=
null && !tooCloseCharacter.
Removed && Vector2.DistanceSquared(tooCloseCharacter.
WorldPosition, character.WorldPosition) < 50.0f * 50.0f)
358 Vector2 diff = character.WorldPosition - tooCloseCharacter.
WorldPosition;
359 if (diff.LengthSquared() < 0.0001f) { diff = Rand.Vector(1.0f); }
360 if (Math.Abs(diff.X) > 0 &&
361 (character.WorldPosition.X > currentHull.WorldRect.Right - 50 || character.WorldPosition.X < currentHull.WorldRect.Left + 50))
364 tooCloseCharacter =
null;
365 PathSteering.Reset();
367 walkDuration = Math.Min(walkDuration, walkDurationMin);
371 newTargetTimer = Math.Min(newTargetTimer, 1);
377 PathSteering.SteeringManual(deltaTime, Vector2.Normalize(diff));
383 PathSteering.Reset();
384 tooCloseCharacter =
null;
387 chairCheckTimer -= deltaTime;
388 if (chairCheckTimer <= 0.0f && character.SelectedSecondaryItem ==
null)
392 if (item.
CurrentHull != currentHull || !item.
HasTag(Tags.ChairItem)) {
continue; }
395 var controller = item.GetComponent<
Controller>();
396 if (controller ==
null || controller.User !=
null) {
continue; }
399 chairCheckTimer = chairCheckInterval;
403 if (standStillTimer < -walkDuration)
405 standStillTimer = Rand.Range(standStillMin, standStillMax);
409 PathSteering.Wander(deltaTime);
414 standStillTimer = waitTime;
416 currentTarget =
null;
417 SetTargetTimerHigh();
420 private void FindTargetHulls()
426 if (character.Submarine ==
null) {
break; }
427 if (HumanAIController.UnsafeHulls.Contains(hull)) {
continue; }
428 if (hull.Submarine ==
null) {
continue; }
429 if (hull.Submarine.Info.IsRuin || hull.Submarine.Info.IsWreck) {
continue; }
432 if (hull.Submarine.TeamID != character.TeamID)
440 if (hull.Submarine.TeamID != character.Submarine.TeamID)
447 if (IsForbidden(hull)) {
continue; }
449 if (!character.Submarine.IsConnectedTo(hull.Submarine)) {
continue; }
451 if (hull.RectWidth < 200) {
continue; }
453 if (character.AnimController is HumanoidAnimController animController)
455 if (hull.CeilingHeight < ConvertUnits.ToDisplayUnits(animController.HeadPosition.Value))
460 if (!targetHulls.Contains(hull))
462 targetHulls.Add(hull);
463 float weight = hull.RectWidth;
464 float distanceFactor = GetDistanceFactor(hull.WorldPosition, verticalDistanceMultiplier: 5, maxDistance: 2500,
465 factorAtMinDistance: 1, factorAtMaxDistance: 0);
466 if (behavior == BehaviorType.Patrol)
469 distanceFactor = 1.0f - distanceFactor;
471 float waterFactor = MathHelper.Lerp(1, 0, MathUtils.InverseLerp(0, 100, hull.WaterPercentage * 2));
472 weight *= distanceFactor * waterFactor;
473 System.Diagnostics.Debug.Assert(weight >= 0);
474 hullWeights.Add(weight);
478 if (PreferredOutpostModuleTypes.Any() && character.CurrentHull !=
null)
480 for (
int i = 0; i < targetHulls.Count; i++)
482 if (targetHulls[i].OutpostModuleTags.Any(t => PreferredOutpostModuleTypes.Contains(t)))
484 hullWeights[i] *= Rand.Range(10.0f, 100.0f);
491 private readonly
float checkItemsInterval = 1;
492 private float checkItemsTimer;
493 private readonly List<Item> itemsToClean =
new List<Item>();
494 private readonly List<Item> ignoredItems =
new List<Item>();
496 private void CleanupItems(
float deltaTime)
498 if (checkItemsTimer <= 0)
500 checkItemsTimer = checkItemsInterval * Rand.Range(0.9f, 1.1f);
501 if (character.Submarine is not Submarine sub) {
return; }
502 if (sub.TeamID != character.TeamID) {
return; }
503 if (character.CurrentHull is not Hull currentHull) {
return; }
504 itemsToClean.Clear();
505 foreach (Item item
in Item.CleanableItems)
507 if (item.CurrentHull != currentHull) {
continue; }
508 if (AIObjectiveCleanupItems.IsValidTarget(item, character, checkInventory:
true, allowUnloading:
false) && !ignoredItems.Contains(item))
510 itemsToClean.Add(item);
513 if (itemsToClean.Any())
515 var targetItem = itemsToClean.MinBy(i => Math.Abs(character.WorldPosition.X - i.WorldPosition.X));
516 if (targetItem !=
null)
518 var cleanupObjective =
new AIObjectiveCleanupItem(targetItem, character, objectiveManager, PriorityModifier);
519 cleanupObjective.Abandoned += () => ignoredItems.Add(targetItem);
520 subObjectives.Add(cleanupObjective);
526 checkItemsTimer -= deltaTime;
536 currentTarget =
null;
537 searchingNewHull =
false;
538 tooCloseCharacter =
null;
541 checkItemsTimer = 0;;
542 itemsToClean.Clear();
543 ignoredItems.Clear();
544 autonomousObjectiveRetryTimer = 10;
552 foreach (var subObjective
in SubObjectives)
556 cleanUpObjective.DropTarget();
virtual void SelectTarget(AITarget target)
void FaceTarget(ISpatialEntity target)
float CalculatePriority()
Call this only when the priority needs to be recalculated. Use the cached Priority property when you ...
float Priority
Final priority value after all calculations.
readonly Character character
IndoorsSteeringManager PathSteering
readonly AIObjectiveManager objectiveManager
override Identifier Identifier
override bool CheckObjectiveState()
Should return whether the objective is completed or not.
override bool AllowAutomaticItemUnequipping
void FaceTargetAndWait(ISpatialEntity target, float waitTime)
override float GetPriority()
readonly HashSet< Identifier > PreferredOutpostModuleTypes
AIObjectiveIdle(Character character, AIObjectiveManager objectiveManager, float priorityModifier=1)
override bool CanBeCompleted
override bool AllowInAnySub
override void Act(float deltaTime)
override void Update(float deltaTime)
static bool IsForbidden(Hull hull)
void Wander(float deltaTime)
override void OnDeselected()
void CalculatePriority(float max=0)
bool FailedAutonomousObjectives
void CreateAutonomousObjectives()
virtual AIController AIController
override Vector2? SimPosition
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
static readonly List< Character > CharacterList
Character SelectedCharacter
Vector2 GetRelativeSimPosition(ISpatialEntity target, Vector2? worldPos=null)
void ReleaseSecondaryItem()
readonly AnimController AnimController
virtual Vector2 WorldPosition
static readonly List< Hull > HullList
readonly HashSet< Hull > UnsafeHulls
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 SetPath(Vector2 targetPos, SteeringPath path)
static float smallRoomSize
Inventory ParentInventory
bool TryInteract(Character user, bool ignoreRequiredItems=false, bool forceSelectKey=false, bool forceUseKey=false)
bool HasTag(Identifier tag)
static readonly List< Item > ItemList
bool AllowDeselectWhenIdling
SteeringPath FindPath(Vector2 start, Vector2 end, Submarine hostSub=null, string errorMsgStr=null, float minGapSize=0, Func< PathNode, bool > startNodeFilter=null, Func< PathNode, bool > endNodeFilter=null, Func< PathNode, bool > nodeFilter=null, bool checkVisibility=true)