public override float GetPriority() { if (!IsAllowed) { Priority = 0; return(Priority); } if (!objectiveManager.IsCurrentOrder <AIObjectiveExtinguishFires>() && Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { Priority = 0; } else { float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y); yDist = yDist > 100 ? yDist * 3 : 0; float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist)); if (targetHull == character.CurrentHull) { distanceFactor = 1; } float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull); float severityFactor = MathHelper.Lerp(0, 1, severity / 100); float devotion = CumulatedDevotion / 100; Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severityFactor * distanceFactor * PriorityModifier), 0, 1)); } return(Priority); }
public override float GetPriority() { if (Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c))) { return(0); } // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally) float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y) * 2.0f; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 10000, dist)); float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull); float severityFactor = MathHelper.Lerp(0, 1, severity / 100); float devotion = Math.Min(Priority, 10) / 100; return(MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + severityFactor * distanceFactor, 0, 1))); }
protected override float GetPriority() { if (!IsAllowed) { Priority = 0; Abandon = true; return(Priority); } bool isOrder = objectiveManager.HasOrder <AIObjectiveExtinguishFires>(); if (!isOrder && Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { // Don't go into rooms with any enemies, unless it's an order Priority = 0; Abandon = true; } else { float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y); yDist = yDist > 100 ? yDist * 3 : 0; float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist)); if (targetHull == character.CurrentHull || HumanAIController.VisibleHulls.Contains(targetHull)) { distanceFactor = 1; } float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull); if (severity > 0.75f && !isOrder && targetHull.RoomName != null && !targetHull.RoomName.Contains("reactor", StringComparison.OrdinalIgnoreCase) && !targetHull.RoomName.Contains("engine", StringComparison.OrdinalIgnoreCase) && !targetHull.RoomName.Contains("command", StringComparison.OrdinalIgnoreCase)) { // Ignore severe fires to prevent casualities unless ordered to extinguish. Priority = 0; Abandon = true; } else { float devotion = CumulatedDevotion / 100; Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1)); } } return(Priority); }
public override float GetPriority() { if (!IsAllowed) { Priority = 0; Abandon = true; return(Priority); } bool isOrder = objectiveManager.HasOrder <AIObjectiveExtinguishFires>(); if (!isOrder && Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { // Don't go into rooms with any enemies, unless it's an order Priority = 0; Abandon = true; } else { float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y); yDist = yDist > 100 ? yDist * 3 : 0; float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist)); if (targetHull == character.CurrentHull || HumanAIController.VisibleHulls.Contains(targetHull)) { distanceFactor = 1; } float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull); if (severity > 0.5f && !isOrder) { // Ignore severe fires unless ordered. (Let the fire drain all the oxygen instead). Priority = 0; Abandon = true; } else { float devotion = CumulatedDevotion / 100; Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1)); } } return(Priority); }
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 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); }