protected override float TargetEvaluation() { if (Targets.None()) { return(100); } if (!objectiveManager.IsOrder(this)) { if (!character.IsMedic && HumanAIController.IsTrueForAnyCrewMember(c => c != HumanAIController && c.Character.IsMedic && !c.Character.IsUnconscious)) { // Don't do anything if there's a medic on board and we are not a medic return(100); } } float worstCondition = Targets.Min(t => GetVitalityFactor(t)); if (Targets.Contains(character)) { if (character.Bleeding > 10) { // Enforce the highest priority when bleeding out. worstCondition = 0; } // Boost the priority when wounded. worstCondition /= 2; } return(worstCondition); }
public override float GetPriority() { if (!IsAllowed) { Priority = 0; Abandon = true; } else if (HumanAIController.IsTrueForAnyCrewMember(other => other != HumanAIController && other.Character.IsBot && other.ObjectiveManager.GetActiveObjective <AIObjectiveFixLeak>()?.Leak == Leak)) { Priority = 0; Abandon = true; } else { float xDist = Math.Abs(character.WorldPosition.X - Leak.WorldPosition.X); float yDist = Math.Abs(character.WorldPosition.Y - Leak.WorldPosition.Y); // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally). // If the target is close, ignore the distance factor alltogether so that we keep fixing the leaks that are nearby. float distanceFactor = isPriority || xDist < 200 && yDist < 100 ? 1 : MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 3000, xDist + yDist * 3.0f)); float severity = isPriority ? 1 : AIObjectiveFixLeaks.GetLeakSeverity(Leak) / 100; float reduction = isPriority ? 1 : 2; float max = AIObjectiveManager.LowestOrderPriority - reduction; float devotion = CumulatedDevotion / 100; Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1)); } return(Priority); }
protected override float TargetEvaluation() { if (!character.IsOnPlayerTeam) { return(Targets.None() ? 0 : 100); } int totalEnemies = Targets.Count(); if (totalEnemies == 0) { return(0); } if (character.IsSecurity) { return(100); } if (objectiveManager.IsOrder(this)) { return(100); } return(HumanAIController.IsTrueForAnyCrewMember(c => c.Character.IsSecurity && !c.Character.IsIncapacitated && c.Character.Submarine == character.Submarine) ? 0 : 100); }
protected override float TargetEvaluation() { int otherRescuers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective <AIObjectiveRescueAll>(), onlyBots: true); int targetCount = Targets.Count; bool anyRescuers = otherRescuers > 0; float ratio = anyRescuers ? targetCount / (float)otherRescuers : 1; if (objectiveManager.CurrentOrder == this) { return(Targets.Min(t => GetVitalityFactor(t)) / ratio); } else { float multiplier = 1; if (anyRescuers) { float mySkill = character.GetSkillLevel("medical"); int betterRescuers = HumanAIController.CountCrew(c => c != HumanAIController && c.Character.Info.Job.GetSkillLevel("medical") >= mySkill, onlyBots: true); if (targetCount / (float)betterRescuers <= 1) { // Enough rescuers return(100); } else { bool foundOtherMedics = HumanAIController.IsTrueForAnyCrewMember(c => c != HumanAIController && c.Character.Info.Job.Prefab.Identifier == "medicaldoctor"); if (foundOtherMedics) { if (character.Info.Job.Prefab.Identifier != "medicaldoctor") { // Double the vitality factor -> less likely to take action multiplier = 2; } } } } return(Targets.Min(t => GetVitalityFactor(t)) / ratio * multiplier); } }
protected override float GetPriority() { bool isOrder = objectiveManager.IsOrder(this); if (!IsAllowed || character.LockHands) { Priority = 0; Abandon = !isOrder; return(Priority); } if (!isOrder && component.Item.ConditionPercentage <= 0) { Priority = 0; } else { if (isOrder) { Priority = objectiveManager.GetOrderPriority(this); } ItemComponent target = GetTarget(); Item targetItem = target?.Item; if (targetItem == null) { #if DEBUG DebugConsole.ThrowError("Item or component of AI Objective Operate item was null. This shouldn't happen."); #endif Abandon = true; Priority = 0; return(Priority); } var reactor = component?.Item.GetComponent <Reactor>(); if (reactor != null) { if (!isOrder) { if (reactor.LastUserWasPlayer && character.TeamID != CharacterTeamType.FriendlyNPC) { // The reactor was previously operated by a player -> ignore. Priority = 0; return(Priority); } } switch (Option) { case "shutdown": if (!reactor.PowerOn) { Priority = 0; return(Priority); } break; case "powerup": // Check that we don't already have another order that is targeting the same item. // Without this the autonomous objective will tell the bot to turn the reactor on again. if (IsAnotherOrderTargetingSameItem(objectiveManager.ForcedOrder) || objectiveManager.CurrentOrders.Any(o => IsAnotherOrderTargetingSameItem(o.Objective))) { Priority = 0; return(Priority); } bool IsAnotherOrderTargetingSameItem(AIObjective objective) { return(objective is AIObjectiveOperateItem operateObjective && operateObjective != this && operateObjective.GetTarget() == target && operateObjective.Option != Option); } break; } } else if (!isOrder) { var steering = component?.Item.GetComponent <Steering>(); if (steering != null && (steering.AutoPilot || HumanAIController.IsTrueForAnyCrewMember(c => c != HumanAIController && c.Character.IsCaptain))) { // Ignore if already set to autopilot or if there's a captain onboard Priority = 0; return(Priority); } } if (targetItem.CurrentHull == null || targetItem.Submarine != character.Submarine && !isOrder || targetItem.CurrentHull.FireSources.Any() || HumanAIController.IsItemOperatedByAnother(target, out _) || Character.CharacterList.Any(c => c.CurrentHull == targetItem.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)) || component.Item.IgnoreByAI(character) || useController && controller.Item.IgnoreByAI(character)) { Priority = 0; } else { if (isOrder) { float max = objectiveManager.GetOrderPriority(this); float value = CumulatedDevotion + (max * PriorityModifier); Priority = MathHelper.Clamp(value, 0, max); } else { float value = CumulatedDevotion + (AIObjectiveManager.LowestOrderPriority * PriorityModifier); float max = AIObjectiveManager.LowestOrderPriority - 1; if (reactor != null && reactor.PowerOn && reactor.FissionRate > 1 && reactor.AutoTemp && Option == "powerup") { // Already on, no need to operate. value = 0; } Priority = MathHelper.Clamp(value, 0, max); } } } return(Priority); }