private void Engage() { if (character.LockHands || Enemy == null) { Mode = CombatMode.Retreat; SteeringManager.Reset(); return; } retreatTarget = null; RemoveSubObjective(ref retreatObjective); RemoveSubObjective(ref seekAmmunition); if (followTargetObjective != null && followTargetObjective.Target != Enemy) { followTargetObjective = null; } TryAddSubObjective(ref followTargetObjective, constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true) { IgnoreIfTargetDead = true, DialogueIdentifier = "dialogcannotreachtarget", TargetName = Enemy.DisplayName }, onAbandon: () => { Abandon = true; SteeringManager.Reset(); }); if (followTargetObjective != null) { followTargetObjective.CloseEnough = WeaponComponent is RangedWeapon ? 1000 : WeaponComponent is MeleeWeapon mw ? mw.Range : WeaponComponent is RepairTool rt ? rt.Range : 50; } }
private void Engage() { retreatTarget = null; RemoveSubObjective(ref retreatObjective); RemoveSubObjective(ref seekAmmunition); if (followTargetObjective != null && followTargetObjective.Target != Enemy) { followTargetObjective = null; } TryAddSubObjective(ref followTargetObjective, constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true) { AllowGoingOutside = true, IgnoreIfTargetDead = true }, onAbandon: () => { Mode = CombatMode.Retreat; SteeringManager.Reset(); }); if (followTargetObjective != null && subObjectives.Contains(followTargetObjective)) { followTargetObjective.CloseEnough = WeaponComponent is RangedWeapon ? 300 : WeaponComponent is MeleeWeapon mw ? mw.Range : WeaponComponent is RepairTool rt ? rt.Range : 50; } }
public void Wander(float deltaTime) { if (character.IsClimbing) { return; } //steer away from edges of the hull var currentHull = character.CurrentHull; if (currentHull != null) { float roomWidth = currentHull.Rect.Width; if (roomWidth < WallAvoidDistance * 4) { PathSteering.Reset(); } else { float leftDist = character.Position.X - currentHull.Rect.X; float rightDist = currentHull.Rect.Right - character.Position.X; if (leftDist < WallAvoidDistance && rightDist < WallAvoidDistance) { if (Math.Abs(rightDist - leftDist) > WallAvoidDistance / 2) { PathSteering.SteeringManual(deltaTime, Vector2.UnitX * Math.Sign(rightDist - leftDist)); } else { PathSteering.Reset(); } } else if (leftDist < WallAvoidDistance) { float speed = (WallAvoidDistance - leftDist) / WallAvoidDistance; PathSteering.SteeringManual(deltaTime, Vector2.UnitX * MathHelper.Clamp(speed, 0.25f, 1)); PathSteering.WanderAngle = 0.0f; } else if (rightDist < WallAvoidDistance) { float speed = (WallAvoidDistance - rightDist) / WallAvoidDistance; PathSteering.SteeringManual(deltaTime, -Vector2.UnitX * MathHelper.Clamp(speed, 0.25f, 1)); PathSteering.WanderAngle = MathHelper.Pi; } else { SteeringManager.SteeringWander(); } } } else { SteeringManager.SteeringWander(); } if (!character.AnimController.InWater) { //reset vertical steering to prevent dropping down from platforms etc character.AIController.SteeringManager.ResetY(); } }
public BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position) { this.Prefab = prefab; this.position = position; drawPosition = position; steeringManager = new SteeringManager(this); velocity = new Vector3( Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly), Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly), Rand.Range(0.0f, prefab.WanderZAmount, Rand.RandSync.ClientOnly)); checkWallsTimer = Rand.Range(0.0f, CheckWallsInterval, Rand.RandSync.ClientOnly); foreach (XElement subElement in prefab.Config.Elements()) { List <SpriteDeformation> deformationList = null; switch (subElement.Name.ToString().ToLowerInvariant()) { case "deformablesprite": deformationList = spriteDeformations; break; case "deformablelightsprite": deformationList = lightSpriteDeformations; break; default: continue; } foreach (XElement animationElement in subElement.Elements()) { SpriteDeformation deformation = null; int sync = animationElement.GetAttributeInt("sync", -1); if (sync > -1) { string typeName = animationElement.GetAttributeString("type", "").ToLowerInvariant(); deformation = uniqueSpriteDeformations.Find(d => d.TypeName == typeName && d.Sync == sync); } if (deformation == null) { deformation = SpriteDeformation.Load(animationElement, prefab.Name); if (deformation != null) { uniqueSpriteDeformations.Add(deformation); } } if (deformation != null) { deformationList.Add(deformation); } } } }
public HumanAIController(Character c) : base(c) { insideSteering = new IndoorsSteeringManager(this, true, false); outsideSteering = new SteeringManager(this); objectiveManager = new AIObjectiveManager(c); reactTimer = Rand.Range(0f, reactionTime); sortTimer = Rand.Range(0f, sortObjectiveInterval); hullVisibilityTimer = Rand.Range(0f, hullVisibilityTimer); InitProjSpecific(); }
protected override bool Check() { if (IsOffensiveOrArrest && Mode != initialMode) { Abandon = true; SteeringManager.Reset(); return false; } return IsEnemyDisabled || (!IsOffensiveOrArrest && coolDownTimer <= 0); }
private void UpdateEscape(float deltaTime) { if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed) { state = AIState.None; return; } SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(SimPosition - selectedAiTarget.SimPosition) * 5); SteeringManager.SteeringWander(1.0f); SteeringManager.SteeringAvoid(deltaTime, 2f); }
public HumanAIController(Character c) : base(c) { if (!c.IsHuman) { throw new System.Exception($"Tried to create a human ai controller for a non-human: {c.SpeciesName}!"); } insideSteering = new IndoorsSteeringManager(this, true, false); outsideSteering = new SteeringManager(this); objectiveManager = new AIObjectiveManager(c); reactTimer = Rand.Range(0f, reactionTime); sortTimer = Rand.Range(0f, sortObjectiveInterval); InitProjSpecific(); }
public HumanAIController(Character c) : base(c) { insideSteering = new IndoorsSteeringManager(this, true, false); outsideSteering = new SteeringManager(this); objectiveManager = new AIObjectiveManager(c); objectiveManager.AddObjective(new AIObjectiveFindSafety(c)); objectiveManager.AddObjective(new AIObjectiveIdle(c)); updateObjectiveTimer = Rand.Range(0.0f, UpdateObjectiveInterval); InitProjSpecific(); }
protected override void OnAbandon() { base.OnAbandon(); SteeringManager?.Reset(); if (character.IsOnPlayerTeam && objectiveManager.CurrentOrder == objectiveManager.CurrentObjective) { string msg = TextManager.Get("dialogcannotreturn", returnNull: true); if (msg != null) { character.Speak(msg, identifier: "dialogcannotreturn", minDurationBetweenSimilar: 5.0f); } } }
private void Move(float deltaTime) { switch (Mode) { case CombatMode.Offensive: case CombatMode.Arrest: Engage(deltaTime); break; case CombatMode.Defensive: if (character.IsOnPlayerTeam && !Enemy.IsPlayer && objectiveManager.IsCurrentOrder <AIObjectiveGoTo>()) { if ((character.CurrentHull == null || character.CurrentHull == Enemy.CurrentHull) && sqrDistance < 200 * 200) { Engage(deltaTime); } else { // Keep following the goto target var gotoObjective = objectiveManager.GetOrder <AIObjectiveGoTo>(); if (gotoObjective != null) { gotoObjective.ForceAct(deltaTime); if (!character.AnimController.InWater) { HumanAIController.FaceTarget(Enemy); ForceWalk = true; HumanAIController.AutoFaceMovement = false; } } else { SteeringManager.Reset(); } } } else { Retreat(deltaTime); } break; case CombatMode.Retreat: Retreat(deltaTime); break; default: throw new NotImplementedException(); } }
protected override void Act(float deltaTime) { if (abortCondition != null && abortCondition()) { Abandon = true; return; } if (AllowCoolDown) { coolDownTimer -= deltaTime; } if (seekAmmunitionObjective == null && seekWeaponObjective == null) { if (Mode != CombatMode.Retreat && TryArm() && !IsEnemyDisabled) { OperateWeapon(deltaTime); } if (HoldPosition) { SteeringManager.Reset(); } else if (seekAmmunitionObjective == null && seekWeaponObjective == null) { Move(deltaTime); } switch (Mode) { case CombatMode.Offensive: if (TargetEliminated && objectiveManager.IsCurrentOrder <AIObjectiveFightIntruders>()) { character.Speak(TextManager.Get("DialogTargetDown"), null, 3.0f, "targetdown", 30.0f); } break; case CombatMode.Arrest: if (HumanAIController.HasItem(Enemy, "handlocker", out _, requireEquipped: true)) { IsCompleted = true; } else if (Enemy.IsKnockedDown && !objectiveManager.IsCurrentObjective <AIObjectiveFightIntruders>() && !HumanAIController.HasItem(character, "handlocker", out _, requireEquipped: false)) { IsCompleted = true; } break; } } }
public BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position) { this.prefab = prefab; this.position = position; drawPosition = position; steeringManager = new SteeringManager(this); velocity = new Vector3( Rand.Range(-prefab.Speed, prefab.Speed), Rand.Range(-prefab.Speed, prefab.Speed), Rand.Range(0.0f, prefab.WanderZAmount)); checkWallsTimer = Rand.Range(0.0f, CheckWallsInterval); }
/// <summary> /// Seeks for more ammunition. Creates a new subobjective. /// </summary> private void SeekAmmunition(string[] ammunitionIdentifiers) { retreatTarget = null; RemoveSubObjective(ref retreatObjective); RemoveSubObjective(ref followTargetObjective); TryAddSubObjective(ref seekAmmunition, constructor: () => new AIObjectiveContainItem(character, ammunitionIdentifiers, Weapon.GetComponent <ItemContainer>(), objectiveManager) { targetItemCount = Weapon.GetComponent <ItemContainer>().Capacity, checkInventory = false }, onAbandon: () => { Weapon = null; Mode = CombatMode.Retreat; SteeringManager.Reset(); }); }
private void UpdateEscape(float deltaTime) { if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed) { state = AIState.None; return; } Vector2 escapeDir = Vector2.Normalize(SimPosition - selectedAiTarget.SimPosition); if (!MathUtils.IsValid(escapeDir)) { escapeDir = Vector2.UnitY; } SteeringManager.SteeringManual(deltaTime, escapeDir * 5); SteeringManager.SteeringWander(1.0f); SteeringManager.SteeringAvoid(deltaTime, 2f); }
public EnemyAIController(Character c, string file) : base(c) { targetMemories = new Dictionary <AITarget, AITargetMemory>(); XDocument doc = ToolBox.TryLoadXml(file); if (doc == null || doc.Root == null) { return; } XElement aiElement = doc.Root.Element("ai"); if (aiElement == null) { return; } attackRooms = ToolBox.GetAttributeFloat(aiElement, 0.0f, "attackrooms", "attackpriorityrooms") / 100.0f; attackHumans = ToolBox.GetAttributeFloat(aiElement, 0.0f, "attackhumans", "attackpriorityhumans") / 100.0f; attackWeaker = ToolBox.GetAttributeFloat(aiElement, 0.0f, "attackweaker", "attackpriorityweaker") / 100.0f; attackStronger = ToolBox.GetAttributeFloat(aiElement, 0.0f, "attackstronger", "attackprioritystronger") / 100.0f; eatDeadPriority = ToolBox.GetAttributeFloat(aiElement, "eatpriority", 0.0f) / 100.0f; combatStrength = ToolBox.GetAttributeFloat(aiElement, "combatstrength", 1.0f); attackCoolDown = ToolBox.GetAttributeFloat(aiElement, "attackcooldown", 5.0f); sight = ToolBox.GetAttributeFloat(aiElement, "sight", 0.0f); hearing = ToolBox.GetAttributeFloat(aiElement, "hearing", 0.0f); attackWhenProvoked = ToolBox.GetAttributeBool(aiElement, "attackwhenprovoked", false); fleeHealthThreshold = ToolBox.GetAttributeFloat(aiElement, "fleehealththreshold", 0.0f); attachToWalls = ToolBox.GetAttributeBool(aiElement, "attachtowalls", false); outsideSteering = new SteeringManager(this); insideSteering = new IndoorsSteeringManager(this, false); steeringManager = outsideSteering; state = AIState.None; }
private void Retreat(float deltaTime) { RemoveSubObjective(ref followTargetObjective); RemoveSubObjective(ref seekAmmunition); if (retreatObjective != null && retreatObjective.Target != retreatTarget) { retreatObjective = null; } if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted)) { if (findHullTimer > 0) { findHullTimer -= deltaTime; } else { retreatTarget = findSafety.FindBestHull(HumanAIController.VisibleHulls); findHullTimer = findHullInterval * Rand.Range(0.9f, 1.1f); } } if (retreatTarget != null && character.CurrentHull != retreatTarget) { TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true), onAbandon: () => { if (Enemy != null && HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull)) { // If in the same room with an enemy -> don't try to escape because we'd want to fight it SteeringManager.Reset(); RemoveSubObjective(ref retreatObjective); } else { // else abandon and fall back to find safety mode Abandon = true; } }, onCompleted: () => RemoveSubObjective(ref retreatObjective)); } }
protected override bool Check() { if (initialMode == CombatMode.Offensive && Mode != CombatMode.Offensive) { Abandon = true; SteeringManager.Reset(); return(false); } bool completed = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) || (initialMode != CombatMode.Offensive && coolDownTimer <= 0); if (completed) { if (objectiveManager.CurrentOrder == this && Enemy != null && Enemy.IsDead) { character.Speak(TextManager.Get("DialogTargetDown"), null, 3.0f, "targetdown", 30.0f); } if (Weapon != null) { Unequip(); } } return(completed); }
public EnemyAIController(Character c, string file) : base(c) { targetMemories = new Dictionary <AITarget, AITargetMemory>(); XDocument doc = ToolBox.TryLoadXml(file); if (doc == null || doc.Root == null) { return; } XElement aiElement = doc.Root.Element("ai"); if (aiElement == null) { return; } attackRooms = ToolBox.GetAttributeFloat(aiElement, "attackrooms", 0.0f) / 100.0f; attackHumans = ToolBox.GetAttributeFloat(aiElement, "attackhumans", 0.0f) / 100.0f; attackWeaker = ToolBox.GetAttributeFloat(aiElement, "attackweaker", 0.0f) / 100.0f; attackStronger = ToolBox.GetAttributeFloat(aiElement, "attackstronger", 0.0f) / 100.0f; attackCoolDown = ToolBox.GetAttributeFloat(aiElement, "attackcooldown", 5.0f); sight = ToolBox.GetAttributeFloat(aiElement, "sight", 0.0f); hearing = ToolBox.GetAttributeFloat(aiElement, "hearing", 0.0f); attackWhenProvoked = ToolBox.GetAttributeBool(aiElement, "attackwhenprovoked", false); outsideSteering = new SteeringManager(this); insideSteering = new IndoorsSteeringManager(this, false); steeringManager = outsideSteering; state = AiState.None; }
protected override void Act(float deltaTime) { if (followControlledCharacter) { if (Character.Controlled == null) { abandon = true; return; } Target = Character.Controlled; } if (Target == character) { character.AIController.SteeringManager.Reset(); abandon = true; return; } waitUntilPathUnreachable -= deltaTime; if (!character.IsClimbing) { character.SelectedConstruction = null; } if (Target is Entity e) { if (e.Removed) { abandon = true; } else { character.AIController.SelectTarget(e.AiTarget); } } bool isInside = character.CurrentHull != null; bool insideSteering = SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.IsPathDirty; var targetHull = Target is Hull h ? h : Target is Item i ? i.CurrentHull : Target is Character c ? c.CurrentHull : character.CurrentHull; bool targetIsOutside = (Target != null && targetHull == null) || (insideSteering && PathSteering.CurrentPath.HasOutdoorsNodes); if (isInside && targetIsOutside && !AllowGoingOutside) { abandon = true; } else if (waitUntilPathUnreachable < 0) { if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && PathSteering.CurrentPath.Unreachable) { if (repeat) { SteeringManager.Reset(); } else { abandon = true; } } } if (abandon) { #if DEBUG DebugConsole.NewMessage($"{character.Name}: Cannot reach the target: {Target.ToString()}", Color.Yellow); #endif if (objectiveManager.CurrentOrder != null) { character.Speak(TextManager.Get("DialogCannotReach"), identifier: "cannotreach", minDurationBetweenSimilar: 10.0f); } character.AIController.SteeringManager.Reset(); } else { Vector2 currTargetSimPos = Vector2.Zero; currTargetSimPos = Target.SimPosition; // Take the sub position into account in the sim pos if (SteeringManager != PathSteering && character.Submarine == null && Target.Submarine != null) { currTargetSimPos += Target.Submarine.SimPosition; } else if (character.Submarine != null && Target.Submarine == null) { currTargetSimPos -= character.Submarine.SimPosition; } else if (character.Submarine != Target.Submarine) { if (character.Submarine != null && Target.Submarine != null) { Vector2 diff = character.Submarine.SimPosition - Target.Submarine.SimPosition; currTargetSimPos -= diff; } } character.AIController.SteeringManager.SteeringSeek(currTargetSimPos); if (SteeringManager != PathSteering) { SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 1, heading: VectorExtensions.Forward(character.AnimController.Collider.Rotation)); } if (getDivingGearIfNeeded) { Character followTarget = Target as Character; bool needsDivingGear = HumanAIController.NeedsDivingGear(targetHull) || mimic && HumanAIController.HasDivingMask(followTarget); bool needsDivingSuit = needsDivingGear && (targetHull == null || targetIsOutside || targetHull.WaterPercentage > 90) || mimic && HumanAIController.HasDivingSuit(followTarget); bool needsEquipment = false; if (needsDivingSuit) { needsEquipment = !HumanAIController.HasDivingSuit(character); } else if (needsDivingGear) { needsEquipment = !HumanAIController.HasDivingMask(character); } if (needsEquipment) { TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager)); } } } }
private void GiveTreatment(float deltaTime) { if (targetCharacter == null) { string errorMsg = $"{character.Name}: Attempted to update a Rescue objective with no target!"; DebugConsole.ThrowError(errorMsg); Abandon = true; return; } SteeringManager?.Reset(); if (!targetCharacter.IsPlayer) { // If the target is a bot, don't let it move targetCharacter.AIController?.SteeringManager?.Reset(); } if (treatmentTimer > 0.0f) { treatmentTimer -= deltaTime; return; } treatmentTimer = TreatmentDelay; float cprSuitability = targetCharacter.Oxygen < 0.0f ? -targetCharacter.Oxygen * 100.0f : 0.0f; //find which treatments are the most suitable to treat the character's current condition targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, normalize: false); //check if we already have a suitable treatment for any of the afflictions foreach (Affliction affliction in GetSortedAfflictions(targetCharacter)) { if (affliction == null) { throw new Exception("Affliction was null"); } if (affliction.Prefab == null) { throw new Exception("Affliction prefab was null"); } foreach (KeyValuePair <string, float> treatmentSuitability in affliction.Prefab.TreatmentSuitability) { if (currentTreatmentSuitabilities.ContainsKey(treatmentSuitability.Key) && currentTreatmentSuitabilities[treatmentSuitability.Key] > 0.0f) { Item matchingItem = character.Inventory.FindItemByIdentifier(treatmentSuitability.Key, true); if (matchingItem == null) { continue; } if (targetCharacter != character) { character.SelectCharacter(targetCharacter); } ApplyTreatment(affliction, matchingItem); //wait a bit longer after applying a treatment to wait for potential side-effects to manifest treatmentTimer = TreatmentDelay * 4; return; } } } // Find treatments outside of own inventory only if inside the own sub. if (character.Submarine != null && character.Submarine.TeamID == character.TeamID) { //didn't have any suitable treatments available, try to find some medical items if (currentTreatmentSuitabilities.Any(s => s.Value > cprSuitability)) { itemNameList.Clear(); suitableItemIdentifiers.Clear(); foreach (KeyValuePair <string, float> treatmentSuitability in currentTreatmentSuitabilities) { if (treatmentSuitability.Value <= cprSuitability) { continue; } if (MapEntityPrefab.Find(null, treatmentSuitability.Key, showErrorMessages: false) is ItemPrefab itemPrefab) { if (!Item.ItemList.Any(it => it.prefab.Identifier == treatmentSuitability.Key)) { continue; } suitableItemIdentifiers.Add(treatmentSuitability.Key); //only list the first 4 items if (itemNameList.Count < 4) { itemNameList.Add(itemPrefab.Name); } } } if (itemNameList.Any()) { string itemListStr = ""; if (itemNameList.Count == 1) { itemListStr = itemNameList[0]; } else { itemListStr = string.Join(" or ", string.Join(", ", itemNameList.Take(itemNameList.Count - 1)), itemNameList.Last()); } if (targetCharacter != character && character.IsOnPlayerTeam) { character.Speak(TextManager.GetWithVariables("DialogListRequiredTreatments", new string[2] { "[targetname]", "[treatmentlist]" }, new string[2] { targetCharacter.Name, itemListStr }, new bool[2] { false, true }), null, 2.0f, "listrequiredtreatments" + targetCharacter.Name, 60.0f); } RemoveSubObjective(ref getItemObjective); TryAddSubObjective(ref getItemObjective, constructor: () => new AIObjectiveGetItem(character, suitableItemIdentifiers.ToArray(), objectiveManager, equip: true, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC), onCompleted: () => RemoveSubObjective(ref getItemObjective), onAbandon: () => { Abandon = true; if (character != targetCharacter && character.IsOnPlayerTeam) { character.Speak(TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, formatCapitals: false), identifier: "cannottreatpatient", minDurationBetweenSimilar: 20.0f); } }); } else if (cprSuitability <= 0) { character.Speak(TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, formatCapitals: false), identifier: "cannottreatpatient", minDurationBetweenSimilar: 20.0f); Abandon = true; } } } else if (!targetCharacter.IsUnconscious) { //no suitable treatments found, not inside our own sub (= can't search for more treatments), the target isn't unconscious (= can't give CPR) character.Speak(TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, formatCapitals: false), identifier: "cannottreatpatient", minDurationBetweenSimilar: 20.0f); Abandon = true; return; } if (character != targetCharacter) { if (cprSuitability > 0.0f) { character.SelectCharacter(targetCharacter); character.AnimController.Anim = AnimController.Animation.CPR; } else { character.DeselectCharacter(); } } }
protected override void Act(float deltaTime) { if (PathSteering == null) { return; } //don't keep dragging others when idling if (character.SelectedCharacter != null) { character.DeselectCharacter(); } if (!character.IsClimbing) { character.SelectedConstruction = null; } bool currentHullForbidden = IsForbidden(character.CurrentHull); if (!currentHullForbidden && !character.AnimController.InWater && !character.IsClimbing && HumanAIController.ObjectiveManager.WaitTimer > 0) { SteeringManager.Reset(); return; } bool currentTargetIsInvalid = currentTarget == null || IsForbidden(currentTarget) || (PathSteering.CurrentPath != null && PathSteering.CurrentPath.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull))); if (currentTargetIsInvalid || (currentTarget == null && currentHullForbidden)) { newTargetTimer = 0; standStillTimer = 0; } else if (character.IsClimbing) { if (currentTarget == null) { newTargetTimer = 0; } else { // Don't allow new targets when climbing. newTargetTimer = Math.Max(newTargetIntervalMin, newTargetTimer); } } else if (character.AnimController.InWater) { if (currentTarget == null) { newTargetTimer = 0; } } if (newTargetTimer <= 0.0f) { if (!searchingNewHull) { //find all available hulls first FindTargetHulls(); searchingNewHull = true; return; } else if (targetHulls.Count > 0) { //choose a random available hull var randomHull = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced); bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull); if (isCurrentHullOK) { // Check that there is no unsafe or forbidden hulls on the way to the target // Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull. var path = PathSteering.PathFinder.FindPath(character.SimPosition, randomHull.SimPosition); if (path.Unreachable || path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull))) { //can't go to this room, remove it from the list and try another room next frame int index = targetHulls.IndexOf(randomHull); targetHulls.RemoveAt(index); hullWeights.RemoveAt(index); PathSteering.Reset(); return; } } currentTarget = randomHull; searchingNewHull = false; } if (currentTarget != null) { character.AIController.SelectTarget(currentTarget.AiTarget); string errorMsg = null; #if DEBUG bool isRoomNameFound = currentTarget.RoomName != null; errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.RoomName : currentTarget.ToString()) + ")"; #endif var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsg); PathSteering.SetPath(path); } newTargetTimer = currentTarget != null && character.AnimController.InWater ? newTargetIntervalMin : Rand.Range(newTargetIntervalMin, newTargetIntervalMax); } newTargetTimer -= deltaTime; //wander randomly // - if reached the end of the path // - if the target is unreachable // - if the path requires going outside if (!character.IsClimbing) { if (SteeringManager != PathSteering || (PathSteering.CurrentPath != null && (PathSteering.CurrentPath.NextNode == null || PathSteering.CurrentPath.Unreachable || PathSteering.CurrentPath.HasOutdoorsNodes))) { if (!character.AnimController.InWater) { standStillTimer -= deltaTime; if (standStillTimer > 0.0f) { walkDuration = Rand.Range(walkDurationMin, walkDurationMax); PathSteering.Reset(); return; } if (standStillTimer < -walkDuration) { standStillTimer = Rand.Range(standStillMin, standStillMax); } } Wander(deltaTime); return; } } if (currentTarget != null) { character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition); } }
private void Escape(float deltaTime) { abandon = true; SteeringManager.Reset(); HumanAIController.ObjectiveManager.GetObjective <AIObjectiveFindSafety>().Priority = 100; }
private void Abandon(float deltaTime) { abandon = true; SteeringManager.Reset(); }
private void UpdateEscape(float deltaTime) { SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(SimPosition - selectedAiTarget.SimPosition) * 5); SteeringManager.SteeringWander(1.0f); SteeringManager.SteeringAvoid(deltaTime, 2f); }
protected override void Act(float deltaTime) { if (followControlledCharacter) { if (Character.Controlled == null) { Abandon = true; return; } Target = Character.Controlled; } if (Target == character) { // Wait character.AIController.SteeringManager.Reset(); return; } waitUntilPathUnreachable -= deltaTime; if (!character.IsClimbing) { character.SelectedConstruction = null; } if (Target is Entity e) { if (e.Removed) { Abandon = true; } else { character.AIController.SelectTarget(e.AiTarget); } } var targetHull = Target is Hull h ? h : Target is Item i ? i.CurrentHull : Target is Character c ? c.CurrentHull : character.CurrentHull; if (!followControlledCharacter) { // Abandon if going through unsafe paths. Note ignores unsafe nodes when following an order or when the objective is set to ignore unsafe hulls. bool containsUnsafeNodes = HumanAIController.CurrentOrder == null && !HumanAIController.ObjectiveManager.CurrentObjective.IgnoreUnsafeHulls && PathSteering != null && PathSteering.CurrentPath != null && PathSteering.CurrentPath.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull)); if (containsUnsafeNodes || HumanAIController.UnreachableHulls.Contains(targetHull)) { Abandon = true; SteeringManager.Reset(); return; } } bool insideSteering = SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.IsPathDirty; bool isInside = character.CurrentHull != null; bool targetIsOutside = (Target != null && targetHull == null) || (insideSteering && PathSteering.CurrentPath.HasOutdoorsNodes); if (isInside && targetIsOutside && !AllowGoingOutside) { Abandon = true; } else if (waitUntilPathUnreachable < 0) { if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && PathSteering.CurrentPath.Unreachable && !PathSteering.IsPathDirty) { if (repeat) { SteeringManager.Reset(); } else { Abandon = true; } } } if (Abandon) { #if DEBUG DebugConsole.NewMessage($"{character.Name}: Cannot reach the target: {Target.ToString()}", Color.Yellow); #endif if (objectiveManager.CurrentOrder != null && objectiveManager.CurrentOrder.ReportFailures) { character.Speak(TextManager.Get("DialogCannotReach"), identifier: "cannotreach", minDurationBetweenSimilar: 10.0f); } SteeringManager.Reset(); } else { if (getDivingGearIfNeeded && !character.LockHands) { Character followTarget = Target as Character; bool needsDivingSuit = targetIsOutside; bool needsDivingGear = needsDivingSuit || HumanAIController.NeedsDivingGear(character, targetHull, out needsDivingSuit); if (!needsDivingGear && mimic) { if (HumanAIController.HasDivingSuit(followTarget)) { needsDivingGear = true; needsDivingSuit = true; } else if (HumanAIController.HasDivingMask(followTarget)) { needsDivingGear = true; } } bool needsEquipment = false; if (needsDivingSuit) { needsEquipment = !HumanAIController.HasDivingSuit(character, AIObjectiveFindDivingGear.lowOxygenThreshold); } else if (needsDivingGear) { needsEquipment = !HumanAIController.HasDivingGear(character, AIObjectiveFindDivingGear.lowOxygenThreshold); } if (needsEquipment) { TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager), onAbandon: () => Abandon = true, onCompleted: () => RemoveSubObjective(ref findDivingGear)); return; } } if (repeat && IsCloseEnough) { OnCompleted(); return; } if (SteeringManager == PathSteering) { Func <PathNode, bool> nodeFilter = null; if (isInside && !AllowGoingOutside) { nodeFilter = node => node.Waypoint.CurrentHull != null; } PathSteering.SteeringSeek(character.GetRelativeSimPosition(Target), 1, startNodeFilter, endNodeFilter, nodeFilter); } else { SteeringManager.SteeringSeek(character.GetRelativeSimPosition(Target), 10); } if (!insideSteering) { SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 1); } } }
private void Engage() { if (character.LockHands || Enemy == null) { Mode = CombatMode.Retreat; SteeringManager.Reset(); return; } retreatTarget = null; RemoveSubObjective(ref retreatObjective); RemoveSubObjective(ref seekAmmunition); if (followTargetObjective != null && followTargetObjective.Target != Enemy) { RemoveFollowTarget(); } TryAddSubObjective(ref followTargetObjective, constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true, closeEnough: 50) { IgnoreIfTargetDead = true, DialogueIdentifier = "dialogcannotreachtarget", TargetName = Enemy.DisplayName }, onAbandon: () => { Abandon = true; SteeringManager.Reset(); }); if (followTargetObjective == null) { return; } if (Mode == CombatMode.Arrest && Enemy.Stun > 2) { if (HumanAIController.HasItem(character, "handlocker", out Item handCuffs)) { if (!arrestingRegistered) { arrestingRegistered = true; followTargetObjective.Completed += OnArrestTargetReached; } followTargetObjective.CloseEnough = 100; } else { RemoveFollowTarget(); SteeringManager.Reset(); } } else if (WeaponComponent == null) { RemoveFollowTarget(); SteeringManager.Reset(); } else { followTargetObjective.CloseEnough = WeaponComponent is RangedWeapon ? 1000 : WeaponComponent is MeleeWeapon mw ? mw.Range : WeaponComponent is RepairTool rt ? rt.Range : 50; } }