public override void Reset() { base.Reset(); getItemObjective = null; goToObjective = null; containedItems.Clear(); }
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 override void Update(float deltaTime) { if (isFinished) { return; } affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList(); foreach (var npc in affectedNpcs) { if (!(npc.AIController is HumanAIController humanAiController)) { continue; } if (Wait) { gotoObjective = new AIObjectiveGoTo(npc, npc, humanAiController.ObjectiveManager, repeat: true) { OverridePriority = 100.0f }; humanAiController.ObjectiveManager.AddObjective(gotoObjective); humanAiController.ObjectiveManager.WaitTimer = 0.0f; } else { if (gotoObjective != null) { gotoObjective.Abandon = true; } } } isFinished = true; }
public override void Reset() { base.Reset(); getExtinguisherObjective = null; gotoObjective = null; useExtinquisherTimer = 0; sinTime = 0; }
public override void Reset() { base.Reset(); goToObjective = null; refuelObjective = null; previousCondition = -1; repairTool = null; }
public override void Reset() { base.Reset(); getWeldingTool = null; refuelObjective = null; gotoObjective = null; operateObjective = null; }
public override void Reset() { base.Reset(); goToObjective = null; getItemObjective = null; replaceOxygenObjective = null; safeHull = null; ignoreOxygen = false; }
protected override void Act(float deltaTime) { if (isCompleted) { return; } //get the item that should be contained Item itemToContain = null; foreach (string identifier in itemIdentifiers) { itemToContain = character.Inventory.FindItemByIdentifier(identifier) ?? character.Inventory.FindItemByTag(identifier); if (itemToContain != null && itemToContain.Condition > 0.0f) { break; } } if (itemToContain == null) { getItemObjective = new AIObjectiveGetItem(character, itemIdentifiers) { GetItemPriority = GetItemPriority, ignoredContainerIdentifiers = ignoredContainerIdentifiers }; AddSubObjective(getItemObjective); return; } if (container.Item.ParentInventory == character.Inventory) { var containedItems = container.Inventory.Items; //if there's already something in the mask (empty oxygen tank?), drop it var existingItem = containedItems.FirstOrDefault(i => i != null); if (existingItem != null) { existingItem.Drop(character); } character.Inventory.RemoveItem(itemToContain); container.Inventory.TryPutItem(itemToContain, null); } else { if (container.Item.CurrentHull != character.CurrentHull || (Vector2.Distance(character.Position, container.Item.Position) > container.Item.InteractDistance && !container.Item.IsInsideTrigger(character.WorldPosition))) { goToObjective = new AIObjectiveGoTo(container.Item, character); AddSubObjective(goToObjective); return; } container.Combine(itemToContain); } isCompleted = true; }
public override void Reset() { base.Reset(); moveInsideObjective = null; moveInCaveObjective = null; moveOutsideObjective = null; usingEscapeBehavior = false; isSteeringThroughGap = false; HumanAIController.ResetEscape(); }
protected override void Act(float deltaTime) { if (isCompleted) { return; } Item itemToDecontain = null; //get the item that should be de-contained if (targetItem == null) { if (itemIdentifiers != null) { foreach (string identifier in itemIdentifiers) { itemToDecontain = container.Inventory.FindItemByIdentifier(identifier) ?? container.Inventory.FindItemByTag(identifier); if (itemToDecontain != null) { break; } } } } else { itemToDecontain = targetItem; } if (itemToDecontain == null || itemToDecontain.Container != container.Item) // Item not found or already de-contained, consider complete { isCompleted = true; return; } if (itemToDecontain.OwnInventory != character.Inventory && itemToDecontain.ParentInventory != character.Inventory) { if (Vector2.Distance(character.Position, container.Item.Position) > container.Item.InteractDistance && !container.Item.IsInsideTrigger(character.WorldPosition)) { goToObjective = new AIObjectiveGoTo(container.Item, character); AddSubObjective(goToObjective); return; } } itemToDecontain.Drop(character); isCompleted = true; }
public override bool IsDuplicate(AIObjective otherObjective) { AIObjectiveGoTo objective = otherObjective as AIObjectiveGoTo; if (objective == null) { return(false); } if (objective.target == target) { return(true); } return(objective.targetPos == targetPos); }
protected override void Act(float deltaTime) { if (isCompleted) { return; } //get the item that should be contained var itemToContain = character.Inventory.FindItem(itemNames); if (itemToContain == null) { getItemObjective = new AIObjectiveGetItem(character, itemNames); getItemObjective.GetItemPriority = GetItemPriority; getItemObjective.IgnoreContainedItems = IgnoreAlreadyContainedItems; AddSubObjective(getItemObjective); return; } if (container.Item.ParentInventory == character.Inventory) { var containedItems = container.Inventory.Items; //if there's already something in the mask (empty oxygen tank?), drop it var existingItem = containedItems.FirstOrDefault(i => i != null); if (existingItem != null) { existingItem.Drop(character); } character.Inventory.RemoveItem(itemToContain); container.Inventory.TryPutItem(itemToContain, null); } else { if (Vector2.Distance(character.Position, container.Item.Position) > container.Item.InteractDistance && !container.Item.IsInsideTrigger(character.WorldPosition)) { goToObjective = new AIObjectiveGoTo(container.Item, character); AddSubObjective(goToObjective); return; } container.Combine(itemToContain); } isCompleted = true; }
protected override void Act(float deltaTime) { var weldingTool = character.Inventory.FindItem("Welding Tool"); if (weldingTool == null) { AddSubObjective(new AIObjectiveGetItem(character, "Welding Tool", true)); return; } else { var containedItems = weldingTool.ContainedItems; if (containedItems == null) { return; } var fuelTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Welding Fuel Tank") && i.Condition > 0.0f); if (fuelTank == null) { AddSubObjective(new AIObjectiveContainItem(character, "Welding Fuel Tank", weldingTool.GetComponent <ItemContainer>())); return; } } var repairTool = weldingTool.GetComponent <RepairTool>(); if (repairTool == null) { return; } Vector2 standPosition = GetStandPosition(); if (Vector2.DistanceSquared(character.WorldPosition, leak.WorldPosition) > 100.0f * 100.0f) { var gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(standPosition), character); if (!gotoObjective.IsCompleted()) { AddSubObjective(gotoObjective); return; } } AddSubObjective(new AIObjectiveOperateItem(repairTool, character, "", true, leak)); }
private void Retreat() { RemoveSubObjective(ref followTargetObjective); RemoveSubObjective(ref seekAmmunition); if (retreatObjective != null && retreatObjective.Target != retreatTarget) { retreatObjective = null; } if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted)) { retreatTarget = findSafety.FindBestHull(HumanAIController.VisibleHulls); } if (character.CurrentHull != retreatTarget) { TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true)); } }
private void Move(float deltaTime) { // Retreat to safety // TODO: aggressive behaviour, chasing? if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted)) { retreatTarget = HumanAIController.ObjectiveManager.GetObjective <AIObjectiveFindSafety>().FindBestHull(); } if (retreatTarget != null) { if (retreatObjective == null || retreatObjective.Target != retreatTarget) { retreatObjective = new AIObjectiveGoTo(retreatTarget, character, false, true); } retreatObjective.TryComplete(deltaTime); } }
private void Retreat(float deltaTime) { if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted)) { retreatTarget = HumanAIController.ObjectiveManager.GetObjective <AIObjectiveFindSafety>().FindBestHull(new List <Hull>() { character.CurrentHull }); } if (retreatTarget != null) { if (retreatObjective == null || retreatObjective.Target != retreatTarget) { retreatObjective = new AIObjectiveGoTo(retreatTarget, character, false, true); } retreatObjective.TryComplete(deltaTime); } }
protected override void Act(float deltaTime) { var currentHull = character.AnimController.CurrentHull; currenthullSafety = OverrideCurrentHullSafety == null? GetHullSafety(currentHull, character) : (float)OverrideCurrentHullSafety; if (NeedsDivingGear()) { if (!FindDivingGear(deltaTime)) { return; } } if (searchHullTimer > 0.0f) { searchHullTimer -= deltaTime; } else { var bestHull = FindBestHull(); if (bestHull != null) { goToObjective = new AIObjectiveGoTo(bestHull, character); } searchHullTimer = SearchHullInterval; } if (goToObjective != null) { var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager; if (pathSteering != null && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Unreachable && !unreachable.Contains(goToObjective.Target)) { unreachable.Add(goToObjective.Target as Hull); } goToObjective.TryComplete(deltaTime); } }
public override void Update(float deltaTime) { if (isFinished) { return; } target = ParentEvent.GetTargets(TargetTag).FirstOrDefault(); if (target == null) { return; } affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList(); foreach (var npc in affectedNpcs) { if (!(npc.AIController is HumanAIController humanAiController)) { continue; } if (Follow) { var newObjective = new AIObjectiveGoTo(target, npc, humanAiController.ObjectiveManager, repeat: true) { OverridePriority = 100.0f }; humanAiController.ObjectiveManager.AddObjective(newObjective); humanAiController.ObjectiveManager.WaitTimer = 0.0f; } else { foreach (var goToObjective in humanAiController.ObjectiveManager.GetActiveObjectives <AIObjectiveGoTo>()) { if (goToObjective.Target == target) { goToObjective.Abandon = true; } } } } isFinished = true; }
protected override void Act(float deltaTime) { //target in water -> move to a dry place first if (targetCharacter.AnimController.InWater) { if (character.SelectedCharacter != targetCharacter) { if (!character.CanInteractWith(targetCharacter)) { AddSubObjective(goToObjective = new AIObjectiveGoTo(targetCharacter, character)); } else { character.SelectCharacter(targetCharacter); } } else { AddSubObjective(new AIObjectiveFindSafety(character)); } return; } //target not in water -> we can start applying treatment if (!character.CanInteractWith(targetCharacter)) { AddSubObjective(goToObjective = new AIObjectiveGoTo(targetCharacter, character)); } else { if (character.SelectedCharacter == null) { character?.Speak(TextManager.Get("DialogFoundUnconsciousTarget") .Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.DisplayName), null, 1.0f, "foundunconscioustarget" + targetCharacter.Name, 60.0f); } character.SelectCharacter(targetCharacter); GiveTreatment(deltaTime); } }
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)); } }
private Character SpawnWatchman(Submarine outpost) { WayPoint watchmanSpawnpoint = WayPoint.WayPointList.Find(wp => wp.Submarine == outpost); if (watchmanSpawnpoint == null) { DebugConsole.ThrowError("Failed to spawn a watchman at the outpost. No spawnpoints found inside the outpost."); return(null); } string seed = outpost == Level.Loaded.StartOutpost ? map.SelectedLocation.Name : map.CurrentLocation.Name; Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); JobPrefab watchmanJob = JobPrefab.Get("watchman"); var variant = Rand.Range(0, watchmanJob.Variants, Rand.RandSync.Server); CharacterInfo characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: watchmanJob, variant: variant); var spawnedCharacter = Character.Create(characterInfo, watchmanSpawnpoint.WorldPosition, Level.Loaded.Seed + (outpost == Level.Loaded.StartOutpost ? "start" : "end")); InitializeWatchman(spawnedCharacter); var objectiveManager = (spawnedCharacter.AIController as HumanAIController)?.ObjectiveManager; if (objectiveManager != null) { var moveOrder = new AIObjectiveGoTo(watchmanSpawnpoint, spawnedCharacter, objectiveManager, repeat: true, getDivingGearIfNeeded: false); moveOrder.Completed += () => { // Turn towards the center of the sub. Doesn't work in all possible cases, but this is the simplest solution for now. spawnedCharacter.AnimController.TargetDir = spawnedCharacter.Submarine.WorldPosition.X > spawnedCharacter.WorldPosition.X ? Direction.Right : Direction.Left; }; objectiveManager.SetOrder(moveOrder); } if (watchmanJob != null) { spawnedCharacter.GiveJobItems(); } return(spawnedCharacter); }
protected override void Act(float deltaTime) { // Only continue when the get item sub objectives have been completed. if (subObjectives.Any()) { return; } foreach (Repairable repairable in Item.Repairables) { if (!repairable.HasRequiredItems(character, false)) { //make sure we have all the items required to fix the target item foreach (var kvp in repairable.requiredItems) { foreach (RelatedItem requiredItem in kvp.Value) { subObjectives.Add(new AIObjectiveGetItem(character, requiredItem.Identifiers, objectiveManager, true)); } } return; } } if (repairTool == null) { FindRepairTool(); } if (repairTool != null) { var containedItems = repairTool.Item.ContainedItems; if (containedItems == null) { #if DEBUG DebugConsole.ThrowError($"{character.Name}: AIObjectiveRepairItem failed - the item \"" + repairTool + "\" has no proper inventory"); #endif Abandon = true; return; } // Drop empty tanks foreach (Item containedItem in containedItems) { if (containedItem == null) { continue; } if (containedItem.Condition <= 0.0f) { containedItem.Drop(character); } } RelatedItem item = null; Item fuel = null; foreach (RelatedItem requiredItem in repairTool.requiredItems[RelatedItem.RelationType.Contained]) { item = requiredItem; fuel = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it)); if (fuel != null) { break; } } if (fuel == null) { RemoveSubObjective(ref goToObjective); TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, item.Identifiers, repairTool.Item.GetComponent <ItemContainer>(), objectiveManager), onCompleted: () => RemoveSubObjective(ref refuelObjective), onAbandon: () => Abandon = true); return; } } if (character.CanInteractWith(Item, out _, checkLinked: false)) { HumanAIController.FaceTarget(Item); if (repairTool != null) { OperateRepairTool(deltaTime); } foreach (Repairable repairable in Item.Repairables) { if (repairable.CurrentFixer != null && repairable.CurrentFixer != character) { // Someone else is repairing the target. Abandon the objective if the other is better at this than us. Abandon = repairable.DegreeOfSuccess(character) < repairable.DegreeOfSuccess(repairable.CurrentFixer); } if (!Abandon) { if (character.SelectedConstruction != Item) { if (!Item.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true) && !Item.TryInteract(character, ignoreRequiredItems: true, forceActionKey: true)) { Abandon = true; } } if (previousCondition == -1) { previousCondition = Item.Condition; } else if (Item.Condition < previousCondition) { // If the current condition is less than the previous condition, we can't complete the task, so let's abandon it. The item is probably deteriorating at a greater speed than we can repair it. Abandon = true; } } if (Abandon) { if (IsRepairing) { character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f); } repairable.StopRepairing(character); } else if (repairable.CurrentFixer != character) { repairable.StartRepairing(character, Repairable.FixActions.Repair); } break; } } else { RemoveSubObjective(ref refuelObjective); // If cannot reach the item, approach it. TryAddSubObjective(ref goToObjective, constructor: () => { previousCondition = -1; var objective = new AIObjectiveGoTo(Item, character, objectiveManager) { // Don't stop in ladders, because we can't interact with other items while holding the ladders. endNodeFilter = node => node.Waypoint.Ladders == null }; if (repairTool != null) { objective.CloseEnough = repairTool.Range * 0.75f; } return(objective); }, onAbandon: () => { Abandon = true; if (IsRepairing) { character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f); } }); } }
public AIObjective CreateObjective(Order order, string option, Character orderGiver, float priorityModifier = 1) { if (order == null) { return(null); } AIObjective newObjective; switch (order.AITag.ToLowerInvariant()) { case "follow": if (orderGiver == null) { return(null); } newObjective = new AIObjectiveGoTo(orderGiver, character, this, repeat: true, priorityModifier: priorityModifier) { CloseEnough = 100, AllowGoingOutside = true, IgnoreIfTargetDead = true, followControlledCharacter = orderGiver == character, mimic = true }; break; case "wait": newObjective = new AIObjectiveGoTo(character, character, this, repeat: true, priorityModifier: priorityModifier) { AllowGoingOutside = true }; break; case "fixleaks": newObjective = new AIObjectiveFixLeaks(character, this, priorityModifier); break; case "chargebatteries": newObjective = new AIObjectiveChargeBatteries(character, this, option, priorityModifier); break; case "rescue": newObjective = new AIObjectiveRescueAll(character, this, priorityModifier); break; case "repairsystems": newObjective = new AIObjectiveRepairItems(character, this, priorityModifier) { RequireAdequateSkills = option == "jobspecific" }; break; case "pumpwater": newObjective = new AIObjectivePumpWater(character, this, option, priorityModifier: priorityModifier); break; case "extinguishfires": newObjective = new AIObjectiveExtinguishFires(character, this, priorityModifier); break; case "fightintruders": newObjective = new AIObjectiveFightIntruders(character, this, priorityModifier); break; case "steer": var steering = (order?.TargetEntity as Item)?.GetComponent <Steering>(); if (steering != null) { steering.PosToMaintain = steering.Item.Submarine?.WorldPosition; } if (order.TargetItemComponent == null) { return(null); } newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, priorityModifier: priorityModifier) { IsLoop = true }; break; default: if (order.TargetItemComponent == null) { return(null); } newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, priorityModifier: priorityModifier) { IsLoop = true }; break; } return(newObjective); }
protected override void Act(float deltaTime) { FindTargetItem(); if (targetItem == null || moveToTarget == null) { HumanAIController.ObjectiveManager.GetObjective <AIObjectiveIdle>().Wander(deltaTime); //SteeringManager.SteeringWander(); return; } if (moveToTarget.CurrentHull == character.CurrentHull && Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance * 2, 2)) { int targetSlot = -1; if (equip) { var pickable = targetItem.GetComponent <Pickable>(); if (pickable == null) { canBeCompleted = false; return; } //check if all the slots required by the item are free foreach (InvSlotType slots in pickable.AllowedSlots) { if (slots.HasFlag(InvSlotType.Any)) { continue; } for (int i = 0; i < character.Inventory.Items.Length; i++) { //slot not needed by the item, continue if (!slots.HasFlag(character.Inventory.SlotTypes[i])) { continue; } targetSlot = i; //slot free, continue if (character.Inventory.Items[i] == null) { continue; } //try to move the existing item to LimbSlot.Any and continue if successful if (character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List <InvSlotType>() { InvSlotType.Any })) { continue; } //if everything else fails, simply drop the existing item character.Inventory.Items[i].Drop(character); } } } targetItem.TryInteract(character, false, true); if (targetSlot > -1 && !character.HasEquippedItem(targetItem)) { character.Inventory.TryPutItem(targetItem, targetSlot, false, false, character); } } else { if (goToObjective == null || moveToTarget != goToObjective.Target) { //check if we're already looking for a diving gear bool gettingDivingGear = (targetItem != null && targetItem.Prefab.Identifier == "divingsuit" || targetItem.HasTag("diving")) || (itemIdentifiers != null && (itemIdentifiers.Contains("diving") || itemIdentifiers.Contains("divingsuit"))); //don't attempt to get diving gear to reach the destination if the item we're trying to get is diving gear goToObjective = new AIObjectiveGoTo(moveToTarget, character, false, !gettingDivingGear); } goToObjective.TryComplete(deltaTime); if (!goToObjective.CanBeCompleted) { targetItem = null; moveToTarget = null; ignoredItems.Add(targetItem); } } }
public AIObjective CreateObjective(Order order, string option, Character orderGiver, bool isAutonomous, float priorityModifier = 1) { if (order == null || order.Identifier == "dismissed") { return(null); } AIObjective newObjective; switch (order.Identifier.ToLowerInvariant()) { case "follow": if (orderGiver == null) { return(null); } newObjective = new AIObjectiveGoTo(orderGiver, character, this, repeat: true, priorityModifier: priorityModifier) { CloseEnough = Rand.Range(90, 100) + Rand.Range(50, 70) * Math.Min(HumanAIController.CountCrew(c => c.ObjectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.Target == orderGiver, onlyBots: true), 4), extraDistanceOutsideSub = 100, extraDistanceWhileSwimming = 100, AllowGoingOutside = true, IgnoreIfTargetDead = true, followControlledCharacter = true, mimic = true, DialogueIdentifier = "dialogcannotreachplace" }; break; case "wait": newObjective = new AIObjectiveGoTo(order.TargetSpatialEntity ?? character, character, this, repeat: true, priorityModifier: priorityModifier) { AllowGoingOutside = character.Submarine == null || (order.TargetSpatialEntity != null && character.Submarine != order.TargetSpatialEntity.Submarine) }; break; case "fixleaks": newObjective = new AIObjectiveFixLeaks(character, this, priorityModifier: priorityModifier, prioritizedHull: order.TargetEntity as Hull); break; case "chargebatteries": newObjective = new AIObjectiveChargeBatteries(character, this, option, priorityModifier); break; case "rescue": newObjective = new AIObjectiveRescueAll(character, this, priorityModifier); break; case "repairsystems": case "repairmechanical": case "repairelectrical": newObjective = new AIObjectiveRepairItems(character, this, priorityModifier: priorityModifier, prioritizedItem: order.TargetEntity as Item) { RelevantSkill = order.AppropriateSkill, RequireAdequateSkills = isAutonomous }; break; case "pumpwater": if (order.TargetItemComponent is Pump targetPump) { if (!order.TargetItemComponent.Item.IsInteractable(character)) { return(null); } newObjective = new AIObjectiveOperateItem(targetPump, character, this, option, false, priorityModifier: priorityModifier) { IsLoop = true, Override = orderGiver != null && orderGiver.IsPlayer }; // ItemComponent.AIOperate() returns false by default -> We'd have to set IsLoop = false and implement a custom override of AIOperate for the Pump.cs, // if we want that the bot just switches the pump on/off and continues doing something else. // If we want that the bot does the objective and then forgets about it, I think we could do the same plus dismiss when the bot is done. } else { newObjective = new AIObjectivePumpWater(character, this, option, priorityModifier: priorityModifier); } break; case "extinguishfires": newObjective = new AIObjectiveExtinguishFires(character, this, priorityModifier); break; case "fightintruders": newObjective = new AIObjectiveFightIntruders(character, this, priorityModifier); break; case "steer": var steering = (order?.TargetEntity as Item)?.GetComponent <Steering>(); if (steering != null) { steering.PosToMaintain = steering.Item.Submarine?.WorldPosition; } if (order.TargetItemComponent == null) { return(null); } if (!order.TargetItemComponent.Item.IsInteractable(character)) { return(null); } newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, controller: order.ConnectedController, priorityModifier: priorityModifier) { IsLoop = true, // Don't override unless it's an order by a player Override = orderGiver != null && orderGiver.IsPlayer }; break; case "setchargepct": newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, false, priorityModifier: priorityModifier) { IsLoop = false, Override = !character.IsDismissed, completionCondition = () => { if (float.TryParse(option, out float pct)) { var targetRatio = Math.Clamp(pct, 0f, 1f); var currentRatio = (order.TargetItemComponent as PowerContainer).RechargeRatio; return(Math.Abs(targetRatio - currentRatio) < 0.05f); } return(true); } }; break; case "getitem": newObjective = new AIObjectiveGetItem(character, order.TargetEntity as Item ?? order.TargetItemComponent?.Item, this, false, priorityModifier: priorityModifier) { MustBeSpecificItem = true }; break; case "cleanupitems": if (order.TargetEntity is Item targetItem) { if (targetItem.HasTag("allowcleanup") && targetItem.ParentInventory == null && targetItem.OwnInventory != null) { // Target all items inside the container newObjective = new AIObjectiveCleanupItems(character, this, targetItem.OwnInventory.AllItems, priorityModifier); } else { newObjective = new AIObjectiveCleanupItems(character, this, targetItem, priorityModifier); } } else { newObjective = new AIObjectiveCleanupItems(character, this, priorityModifier: priorityModifier); } break; default: if (order.TargetItemComponent == null) { return(null); } if (!order.TargetItemComponent.Item.IsInteractable(character)) { return(null); } newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, controller: order.ConnectedController, priorityModifier: priorityModifier) { IsLoop = true, // Don't override unless it's an order by a player Override = orderGiver != null && orderGiver.IsPlayer }; if (newObjective.Abandon) { return(null); } break; } return(newObjective); }
public override void Reset() { base.Reset(); goToObjective = null; getItemObjective = null; }
public AIObjective CreateObjective(Order order, string option, Character orderGiver, bool isAutonomous, float priorityModifier = 1) { if (order == null) { return(null); } AIObjective newObjective; switch (order.Identifier.ToLowerInvariant()) { case "follow": if (orderGiver == null) { return(null); } newObjective = new AIObjectiveGoTo(orderGiver, character, this, repeat: true, priorityModifier: priorityModifier) { CloseEnough = 100, AllowGoingOutside = true, IgnoreIfTargetDead = true, followControlledCharacter = orderGiver == character, mimic = true, DialogueIdentifier = "dialogcannotreachplace" }; break; case "wait": newObjective = new AIObjectiveGoTo(order.TargetEntity ?? character, character, this, repeat: true, priorityModifier: priorityModifier) { AllowGoingOutside = character.CurrentHull == null }; break; case "fixleaks": newObjective = new AIObjectiveFixLeaks(character, this, priorityModifier: priorityModifier, prioritizedHull: order.TargetEntity as Hull); break; case "chargebatteries": newObjective = new AIObjectiveChargeBatteries(character, this, option, priorityModifier); break; case "rescue": newObjective = new AIObjectiveRescueAll(character, this, priorityModifier); break; case "repairsystems": case "repairmechanical": case "repairelectrical": newObjective = new AIObjectiveRepairItems(character, this, priorityModifier: priorityModifier, prioritizedItem: order.TargetEntity as Item) { RelevantSkill = order.AppropriateSkill, RequireAdequateSkills = isAutonomous }; break; case "pumpwater": if (order.TargetItemComponent is Pump targetPump) { if (order.TargetItemComponent.Item.NonInteractable) { return(null); } newObjective = new AIObjectiveOperateItem(targetPump, character, this, option, false, priorityModifier: priorityModifier) { IsLoop = true, Override = orderGiver != null && orderGiver.IsPlayer }; // ItemComponent.AIOperate() returns false by default -> We'd have to set IsLoop = false and implement a custom override of AIOperate for the Pump.cs, // if we want that the bot just switches the pump on/off and continues doing something else. // If we want that the bot does the objective and then forgets about it, I think we could do the same plus dismiss when the bot is done. } else { newObjective = new AIObjectivePumpWater(character, this, option, priorityModifier: priorityModifier); } break; case "extinguishfires": newObjective = new AIObjectiveExtinguishFires(character, this, priorityModifier); break; case "fightintruders": newObjective = new AIObjectiveFightIntruders(character, this, priorityModifier); break; case "steer": var steering = (order?.TargetEntity as Item)?.GetComponent <Steering>(); if (steering != null) { steering.PosToMaintain = steering.Item.Submarine?.WorldPosition; } if (order.TargetItemComponent == null) { return(null); } if (order.TargetItemComponent.Item.NonInteractable) { return(null); } newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, controller: order.ConnectedController, priorityModifier: priorityModifier) { IsLoop = true, // Don't override unless it's an order by a player Override = orderGiver != null && orderGiver.IsPlayer }; break; default: if (order.TargetItemComponent == null) { return(null); } if (order.TargetItemComponent.Item.NonInteractable) { return(null); } newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, controller: order.ConnectedController, priorityModifier: priorityModifier) { IsLoop = true, // Don't override unless it's an order by a player Override = orderGiver != null && orderGiver.IsPlayer }; if (newObjective.Abandon) { return(null); } break; } return(newObjective); }
protected override void Act(float deltaTime) { var currentHull = character.AnimController.CurrentHull; if (HumanAIController.NeedsDivingGear(currentHull) && divingGearObjective == null) { bool needsDivingSuit = currentHull == null || currentHull.WaterPercentage > 90; bool hasEquipment = needsDivingSuit ? HumanAIController.HasDivingSuit(character) : HumanAIController.HasDivingGear(character); if (!hasEquipment) { divingGearObjective = new AIObjectiveFindDivingGear(character, needsDivingSuit); } } if (divingGearObjective != null) { divingGearObjective.TryComplete(deltaTime); if (divingGearObjective.IsCompleted()) { divingGearObjective = null; Priority = 0; } else if (divingGearObjective.CanBeCompleted) { // If diving gear objective is active and can be completed, wait for it to complete. return; } else { divingGearObjective = null; // Reset the timer so that we get a safe hull target. searchHullTimer = 0; } } if (unreachableClearTimer > 0) { unreachableClearTimer -= deltaTime; } else { unreachableClearTimer = clearUnreachableInterval; unreachable.Clear(); } if (searchHullTimer > 0.0f) { searchHullTimer -= deltaTime; } else if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD) { var bestHull = FindBestHull(); if (bestHull != null && bestHull != currentHull) { if (goToObjective != null) { if (goToObjective.Target != bestHull) { // If we need diving gear, we should already have it, if possible. goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false) { AllowGoingOutside = HumanAIController.HasDivingSuit(character) }; } } else { goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false) { AllowGoingOutside = HumanAIController.HasDivingSuit(character) }; } } searchHullTimer = SearchHullInterval; } if (goToObjective != null) { goToObjective.TryComplete(deltaTime); if (!goToObjective.CanBeCompleted) { if (!unreachable.Contains(goToObjective.Target)) { unreachable.Add(goToObjective.Target as Hull); } goToObjective = null; HumanAIController.ObjectiveManager.GetObjective <AIObjectiveIdle>().Wander(deltaTime); //SteeringManager.SteeringWander(); } } else if (currentHull != null) { //goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found) // -> attempt to manually steer away from hazards Vector2 escapeVel = Vector2.Zero; foreach (FireSource fireSource in currentHull.FireSources) { Vector2 dir = character.Position - fireSource.Position; float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f); escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier); } foreach (Character enemy in Character.CharacterList) { //don't run from friendly NPCs if (enemy.TeamID == Character.TeamType.FriendlyNPC) { continue; } //friendly NPCs don't run away from anything but characters controlled by EnemyAIController (= monsters) if (character.TeamID == Character.TeamType.FriendlyNPC && !(enemy.AIController is EnemyAIController)) { continue; } if (enemy.CurrentHull == currentHull && !enemy.IsDead && !enemy.IsUnconscious && (enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID)) { Vector2 dir = character.Position - enemy.Position; float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f); escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier); } } if (escapeVel != Vector2.Zero) { //only move if we haven't reached the edge of the room if ((escapeVel.X < 0 && character.Position.X > currentHull.Rect.X + 50) || (escapeVel.X > 0 && character.Position.X < currentHull.Rect.Right - 50)) { character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel); } else { character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left; character.AIController.SteeringManager.Reset(); } } else { character.AIController.SteeringManager.Reset(); } } }
protected override void Act(float deltaTime) { var currentHull = character.AnimController.CurrentHull; bool needsDivingGear = HumanAIController.NeedsDivingGear(currentHull); bool needsDivingSuit = needsDivingGear && (currentHull == null || currentHull.WaterPercentage > 90); bool needsEquipment = false; if (needsDivingSuit) { needsEquipment = !HumanAIController.HasDivingSuit(character); } else if (needsDivingGear) { needsEquipment = !HumanAIController.HasDivingMask(character); } if (needsEquipment) { TryAddSubObjective(ref divingGearObjective, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager), onAbandon: () => searchHullTimer = Math.Min(1, searchHullTimer)); } else { if (divingGearObjective != null && divingGearObjective.IsCompleted()) { // Reset the devotion. Priority = 0; divingGearObjective = null; } if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD) { searchHullTimer = Math.Min(1, searchHullTimer); } if (searchHullTimer > 0.0f) { searchHullTimer -= deltaTime; } else { searchHullTimer = SearchHullInterval; previousSafeHull = currentSafeHull; currentSafeHull = FindBestHull(); if (currentSafeHull == null) { currentSafeHull = previousSafeHull; } if (currentSafeHull != null && currentSafeHull != currentHull) { if (goToObjective?.Target != currentSafeHull) { goToObjective = null; } TryAddSubObjective(ref goToObjective, constructor: () => new AIObjectiveGoTo(currentSafeHull, character, objectiveManager, getDivingGearIfNeeded: true) { AllowGoingOutside = HumanAIController.HasDivingSuit(character) }, onAbandon: () => unreachable.Add(goToObjective.Target as Hull)); } else { goToObjective = null; } } if (goToObjective != null) { if (goToObjective.IsCompleted()) { objectiveManager.GetObjective <AIObjectiveIdle>()?.Wander(deltaTime); } Priority = 0; return; } if (currentHull == null) { return; } //goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found) // -> attempt to manually steer away from hazards Vector2 escapeVel = Vector2.Zero; // TODO: optimize foreach (FireSource fireSource in HumanAIController.VisibleHulls.SelectMany(h => h.FireSources)) { Vector2 dir = character.Position - fireSource.Position; float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f); escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier); } foreach (Character enemy in Character.CharacterList) { if (enemy.IsDead || enemy.IsUnconscious || enemy.Removed || HumanAIController.IsFriendly(enemy)) { continue; } if (HumanAIController.VisibleHulls.Contains(enemy.CurrentHull)) { Vector2 dir = character.Position - enemy.Position; float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f); escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier); } } if (escapeVel != Vector2.Zero) { float left = currentHull.Rect.X + 50; float right = currentHull.Rect.Right - 50; //only move if we haven't reached the edge of the room if (escapeVel.X < 0 && character.Position.X > left || escapeVel.X > 0 && character.Position.X < right) { character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel); } else { character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left; character.AIController.SteeringManager.Reset(); } } else { Priority = 0; objectiveManager.GetObjective <AIObjectiveIdle>()?.Wander(deltaTime); } } }