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;
166 CleanupItems(deltaTime);
174 bool currentTargetIsInvalid =
175 currentTarget ==
null ||
187 else if (currentTarget !=
null)
199 if (currentTarget !=
null && !currentTargetIsInvalid)
205 currentTargetIsInvalid =
true;
212 currentTargetIsInvalid =
true;
219 if (newTargetTimer > timerMargin)
228 if (currentTarget ==
null)
235 SetTargetTimerHigh();
240 if (currentTarget ==
null)
245 if (newTargetTimer <= 0.0f)
247 if (!searchingNewHull)
250 searchingNewHull =
true;
253 else if (targetHulls.Any())
256 currentTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
262 if (node.Waypoint.CurrentHull ==
null) {
return false; }
267 }, endNodeFilter: node => node.Waypoint.Stairs ==
null && node.Waypoint.Ladders ==
null && (!isCurrentHullAllowed || !
IsForbidden(node.Waypoint.CurrentHull)));
268 if (path.Unreachable)
271 int index = targetHulls.IndexOf(currentTarget);
272 targetHulls.RemoveAt(index);
273 hullWeights.RemoveAt(index);
275 currentTarget =
null;
281 SetTargetTimerNormal();
282 searchingNewHull =
false;
287 SetTargetTimerHigh();
288 searchingNewHull =
false;
291 newTargetTimer -= deltaTime;
296 else if (currentTarget !=
null)
299 nodeFilter: node => node.Waypoint.CurrentHull !=
null,
300 endNodeFilter: node => node.Waypoint.Ladders ==
null && node.Waypoint.Stairs ==
null);
312 if (character.IsClimbing)
314 PathSteering.Reset();
315 character.StopClimbing();
317 var currentHull = character.CurrentHull;
318 if (!character.AnimController.InWater && currentHull !=
null)
320 standStillTimer -= deltaTime;
321 if (standStillTimer > 0.0f)
323 walkDuration = Rand.Range(walkDurationMin, walkDurationMax);
329 if (Vector2.DistanceSquared(c.
WorldPosition, character.WorldPosition) > 60.0f * 60.0f) {
continue; }
330 if ((humanAI.ObjectiveManager.CurrentObjective is
AIObjectiveIdle idleObjective && idleObjective.standStillTimer > 0.0f) ||
335 if (tooCloseCharacter !=
null &&
336 Math.Sign(tooCloseCharacter.
WorldPosition.X - character.WorldPosition.X) != Math.Sign(c.
WorldPosition.X - character.WorldPosition.X))
338 tooCloseCharacter =
null;
341 tooCloseCharacter = c;
347 if (tooCloseCharacter !=
null && !tooCloseCharacter.
Removed && Vector2.DistanceSquared(tooCloseCharacter.
WorldPosition, character.WorldPosition) < 50.0f * 50.0f)
349 Vector2 diff = character.WorldPosition - tooCloseCharacter.
WorldPosition;
350 if (diff.LengthSquared() < 0.0001f) { diff = Rand.Vector(1.0f); }
351 if (Math.Abs(diff.X) > 0 &&
352 (character.WorldPosition.X > currentHull.WorldRect.Right - 50 || character.WorldPosition.X < currentHull.WorldRect.Left + 50))
355 tooCloseCharacter =
null;
356 PathSteering.Reset();
358 walkDuration = Math.Min(walkDuration, walkDurationMin);
362 newTargetTimer = Math.Min(newTargetTimer, 1);
368 PathSteering.SteeringManual(deltaTime, Vector2.Normalize(diff));
374 PathSteering.Reset();
375 tooCloseCharacter =
null;
378 chairCheckTimer -= deltaTime;
379 if (chairCheckTimer <= 0.0f && character.SelectedSecondaryItem ==
null)
383 if (item.
CurrentHull != currentHull || !item.
HasTag(Tags.ChairItem)) {
continue; }
386 var controller = item.GetComponent<
Controller>();
387 if (controller ==
null || controller.User !=
null) {
continue; }
390 chairCheckTimer = chairCheckInterval;
394 if (standStillTimer < -walkDuration)
396 standStillTimer = Rand.Range(standStillMin, standStillMax);
400 PathSteering.Wander(deltaTime);
405 standStillTimer = waitTime;
407 currentTarget =
null;
408 SetTargetTimerHigh();
411 private void FindTargetHulls()
417 if (character.Submarine ==
null) {
break; }
418 if (HumanAIController.UnsafeHulls.Contains(hull)) {
continue; }
419 if (hull.Submarine ==
null) {
continue; }
420 if (hull.Submarine.Info.IsRuin || hull.Submarine.Info.IsWreck) {
continue; }
423 if (hull.Submarine.TeamID != character.TeamID)
431 if (hull.Submarine.TeamID != character.Submarine.TeamID)
438 if (IsForbidden(hull)) {
continue; }
440 if (!character.Submarine.IsConnectedTo(hull.Submarine)) {
continue; }
442 if (hull.RectWidth < 200) {
continue; }
444 if (character.AnimController is HumanoidAnimController animController)
446 if (hull.CeilingHeight < ConvertUnits.ToDisplayUnits(animController.HeadPosition.Value))
451 if (!targetHulls.Contains(hull))
453 targetHulls.Add(hull);
454 float weight = hull.RectWidth;
455 float distanceFactor = GetDistanceFactor(hull.WorldPosition, verticalDistanceMultiplier: 5, maxDistance: 2500,
456 factorAtMinDistance: 1, factorAtMaxDistance: 0);
457 if (behavior == BehaviorType.Patrol)
460 distanceFactor = 1.0f - distanceFactor;
462 float waterFactor = MathHelper.Lerp(1, 0, MathUtils.InverseLerp(0, 100, hull.WaterPercentage * 2));
463 weight *= distanceFactor * waterFactor;
464 System.Diagnostics.Debug.Assert(weight >= 0);
465 hullWeights.Add(weight);
469 if (PreferredOutpostModuleTypes.Any() && character.CurrentHull !=
null)
471 for (
int i = 0; i < targetHulls.Count; i++)
473 if (targetHulls[i].OutpostModuleTags.Any(t => PreferredOutpostModuleTypes.Contains(t)))
475 hullWeights[i] *= Rand.Range(10.0f, 100.0f);
482 private readonly
float checkItemsInterval = 1;
483 private float checkItemsTimer;
484 private readonly List<Item> itemsToClean =
new List<Item>();
485 private readonly List<Item> ignoredItems =
new List<Item>();
487 private void CleanupItems(
float deltaTime)
489 if (checkItemsTimer <= 0)
491 checkItemsTimer = checkItemsInterval * Rand.Range(0.9f, 1.1f);
492 var hull = character.CurrentHull;
495 itemsToClean.Clear();
496 foreach (Item item
in Item.CleanableItems)
498 if (item.CurrentHull != hull) {
continue; }
499 if (AIObjectiveCleanupItems.IsValidTarget(item, character, checkInventory:
true, allowUnloading:
false) && !ignoredItems.Contains(item))
501 itemsToClean.Add(item);
504 if (itemsToClean.Any())
506 var targetItem = itemsToClean.OrderBy(i => Math.Abs(character.WorldPosition.X - i.WorldPosition.X)).FirstOrDefault();
507 if (targetItem !=
null)
509 var cleanupObjective =
new AIObjectiveCleanupItem(targetItem, character, objectiveManager, PriorityModifier);
510 cleanupObjective.Abandoned += () => ignoredItems.Add(targetItem);
511 subObjectives.Add(cleanupObjective);
518 checkItemsTimer -= deltaTime;
528 currentTarget =
null;
529 searchingNewHull =
false;
530 tooCloseCharacter =
null;
533 checkItemsTimer = 0;;
534 itemsToClean.Clear();
535 ignoredItems.Clear();
536 autonomousObjectiveRetryTimer = 10;
542 foreach (var subObjective
in SubObjectives)
546 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 CheckObjectiveSpecific()
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
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)