protected override float GetPriority() { if (!IsAllowed) { Priority = 0; Abandon = true; return(Priority); } if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead) { Priority = 0; Abandon = true; } else { // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally) float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist)); if (targetCharacter.CurrentHull == character.CurrentHull) { distanceFactor = 1; } float vitalityFactor = 1 - AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) / 100; float devotion = CumulatedDevotion / 100; Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (vitalityFactor * distanceFactor * PriorityModifier), 0, 1)); } return(Priority); }
public override float GetPriority() { if (targetCharacter == null) { return(0); } if (targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead) { abandon = true; return(0); } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(c))) { abandon = true; return(0); } // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally) float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f; float distanceFactor = MathHelper.Lerp(1, 0.5f, MathUtils.InverseLerp(0, 10000, dist)); float vitalityFactor = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter); float devotion = Math.Min(Priority, 10) / 100; return(MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + vitalityFactor * distanceFactor, 0, 1))); }
public override float GetPriority() { if (targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead) { return(0); } // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally) float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist)); if (targetCharacter.CurrentHull == character.CurrentHull) { distanceFactor = 1; } float vitalityFactor = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter); float devotion = Math.Min(Priority, 10) / 100; return(MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + vitalityFactor * distanceFactor, 0, 1))); }
protected override bool Check() { if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead) { Abandon = true; return(false); } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { Abandon = true; return(false); } bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter); if (isCompleted && targetCharacter != character) { character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name), null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f); } return(isCompleted); }
protected override bool CheckObjectiveSpecific() { if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead) { Abandon = true; return(false); } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { Abandon = true; return(false); } bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) || targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold); if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam) { character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name), null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f); } return(isCompleted); }
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); }
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 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 void ReportProblems() { Order newOrder = null; if (Character.CurrentHull != null) { foreach (var hull in VisibleHulls) { foreach (Character c in Character.CharacterList) { if (c.CurrentHull != hull) { continue; } if (AIObjectiveFightIntruders.IsValidTarget(c, Character)) { AddTargets <AIObjectiveFightIntruders, Character>(Character, c); if (newOrder == null) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportintruders"); newOrder = new Order(orderPrefab, c.CurrentHull, null, orderGiver: Character); } } } if (AIObjectiveExtinguishFires.IsValidTarget(hull, Character)) { AddTargets <AIObjectiveExtinguishFires, Hull>(Character, hull); if (newOrder == null) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportfire"); newOrder = new Order(orderPrefab, hull, null, orderGiver: Character); } } foreach (Character c in Character.CharacterList) { if (c.CurrentHull != hull) { continue; } if (AIObjectiveRescueAll.IsValidTarget(c, Character)) { if (AddTargets <AIObjectiveRescueAll, Character>(c, Character)) { if (newOrder == null) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "requestfirstaid"); newOrder = new Order(orderPrefab, c.CurrentHull, null, orderGiver: Character); } } } } foreach (var gap in hull.ConnectedGaps) { if (AIObjectiveFixLeaks.IsValidTarget(gap, Character)) { AddTargets <AIObjectiveFixLeaks, Gap>(Character, gap); if (newOrder == null && !gap.IsRoomToRoom) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportbreach"); newOrder = new Order(orderPrefab, hull, null, orderGiver: Character); } } } foreach (Item item in Item.ItemList) { if (item.CurrentHull != hull) { continue; } if (AIObjectiveRepairItems.IsValidTarget(item, Character)) { if (item.Repairables.All(r => item.Condition > r.ShowRepairUIThreshold)) { continue; } AddTargets <AIObjectiveRepairItems, Item>(Character, item); if (newOrder == null) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportbrokendevices"); newOrder = new Order(orderPrefab, item.CurrentHull, item.Repairables?.FirstOrDefault(), orderGiver: Character); } } } } } if (newOrder != null) { if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime)) { Character.Speak(newOrder.GetChatMessage("", Character.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order); #if SERVER GameMain.Server.SendOrderChatMessage(new OrderChatMessage(newOrder, "", Character.CurrentHull, null, Character)); #endif } } }
public override bool IsCompleted() { if (targetCharacter == null || targetCharacter.Removed) { abandon = true; return(true); } bool isCompleted = targetCharacter.Bleeding <= 0 && targetCharacter.Vitality / targetCharacter.MaxVitality > AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager); if (isCompleted) { character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name), null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f); } return(isCompleted || targetCharacter.IsDead); }
public void SetOrder(Order order, string option, Character orderGiver) { CurrentOrder = null; if (order == null) { return; } switch (order.AITag.ToLowerInvariant()) { case "follow": CurrentOrder = new AIObjectiveGoTo(orderGiver, character, true) { CloseEnough = 1.5f, AllowGoingOutside = true, IgnoreIfTargetDead = true, FollowControlledCharacter = orderGiver == character }; break; case "wait": CurrentOrder = new AIObjectiveGoTo(character, character, true) { AllowGoingOutside = true }; break; case "fixleaks": CurrentOrder = new AIObjectiveFixLeaks(character); break; case "chargebatteries": CurrentOrder = new AIObjectiveChargeBatteries(character, option); break; case "rescue": CurrentOrder = new AIObjectiveRescueAll(character); break; case "repairsystems": CurrentOrder = new AIObjectiveRepairItems(character) { RequireAdequateSkills = option != "all" }; break; case "pumpwater": CurrentOrder = new AIObjectivePumpWater(character, option); break; case "extinguishfires": CurrentOrder = new AIObjectiveExtinguishFires(character); 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; } CurrentOrder = new AIObjectiveOperateItem(order.TargetItemComponent, character, option, false, null, order.UseController); break; default: if (order.TargetItemComponent == null) { return; } CurrentOrder = new AIObjectiveOperateItem(order.TargetItemComponent, character, option, false, null, order.UseController); break; } }
public AIObjective CreateObjective(Order order, string option, Character orderGiver, 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 = option == "jobspecific" }; break; case "pumpwater": if (order.TargetItemComponent is Pump targetPump) { newObjective = new AIObjectiveOperateItem(targetPump, character, this, option, false, priorityModifier: priorityModifier); // newObjective.Completed += DismissSelf; } 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); } newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, 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); } newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, priorityModifier: priorityModifier) { IsLoop = true, // Don't override unless it's an order by a player Override = orderGiver != null && orderGiver.IsPlayer }; break; } return(newObjective); }