protected override float TargetEvaluation() { if (character.SelectedConstruction != null && Targets.Any(t => character.SelectedConstruction == t && t.ConditionPercentage < 100)) { // Don't stop fixing until done return(100); } int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective <AIObjectiveRepairItems>() && !c.Character.IsIncapacitated, onlyBots: true); int items = Targets.Count; bool anyFixers = otherFixers > 0; float ratio = anyFixers ? items / (float)otherFixers : 1; if (objectiveManager.CurrentOrder == this) { return(Targets.Sum(t => 100 - t.ConditionPercentage) * ratio); } else { if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f)) { // Enough fixers return(0); } if (RequireAdequateSkills) { return(Targets.Sum(t => GetTargetPriority(t, character)) * ratio); } else { return(Targets.Sum(t => 100 - t.ConditionPercentage) * ratio); } } }
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 void Update(AIObjectiveManager objectiveManager, float deltaTime) { if (character.CurrentHull == null) { currenthullSafety = 0; Priority = 5; return; } if (character.OxygenAvailable < CharacterHealth.LowOxygenThreshold) { Priority = 100; } currenthullSafety = HumanAIController.GetHullSafety(character.CurrentHull); if (currenthullSafety > HumanAIController.HULL_SAFETY_THRESHOLD) { Priority -= priorityDecrease * deltaTime; } else { float dangerFactor = (100 - currenthullSafety) / 100; Priority += dangerFactor * priorityIncrease * deltaTime; } Priority = MathHelper.Clamp(Priority, 0, 100); if (divingGearObjective != null && !divingGearObjective.IsCompleted() && divingGearObjective.CanBeCompleted) { Priority = Math.Max(Priority, AIObjectiveManager.OrderPriority + 10); } }
protected override bool ShouldInterruptSubObjective(AIObjective subObjective) { if (subObjective is AIObjectiveFindSafety) { if (character.SelectedCharacter != targetCharacter) { return(true); } if (character.AnimController.InWater || targetCharacter.AnimController.InWater) { return(false); } foreach (Limb limb in targetCharacter.AnimController.Limbs) { if (!Submarine.RectContains(targetCharacter.CurrentHull.WorldRect, limb.WorldPosition)) { return(false); } } return(!character.AnimController.InWater && !targetCharacter.AnimController.InWater && HumanAIController.GetHullSafety(character.CurrentHull, character) > HumanAIController.HULL_SAFETY_THRESHOLD); } return(false); }
protected override float TargetEvaluation() { int totalLeaks = Targets.Count(); if (totalLeaks == 0) { return(0); } int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective <AIObjectiveFixLeaks>() && !c.Character.IsIncapacitated, onlyBots: true); bool anyFixers = otherFixers > 0; if (objectiveManager.IsOrder(this)) { float ratio = anyFixers ? totalLeaks / (float)otherFixers : 1; return(Targets.Sum(t => GetLeakSeverity(t)) * ratio); } else { int secondaryLeaks = Targets.Count(l => l.IsRoomToRoom); int leaks = totalLeaks - secondaryLeaks; float ratio = leaks == 0 ? 1 : anyFixers ? leaks / otherFixers : 1; if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f)) { // Enough fixers return(0); } return(Targets.Sum(t => GetLeakSeverity(t)) * ratio); } }
public static bool IsValidTarget(Character target, Character character) { if (target == null || target.IsDead || target.Removed) { return(false); } if (target == character) { return(false); } if (HumanAIController.IsFriendly(character, target)) { return(false); } if (target.Submarine == null) { return(false); } if (target.Submarine.TeamID != character.TeamID) { return(false); } if (target.CurrentHull == null) { return(false); } if (character.Submarine != null) { if (!character.Submarine.IsConnectedTo(target.Submarine)) { return(false); } } return(true); }
public bool OrderAttendedTo(float timeSinceLastCheck = 0f) { if (!HumanAIController.IsActive(OrderedCharacter)) { return(false); } // accept only the highest priority order if (CurrentOrder != null && OrderedCharacter.GetCurrentOrderWithTopPriority()?.Order != CurrentOrder) { #if DEBUG ShipCommandManager.ShipCommandLog($"Order {CurrentOrder.Name} did not match current order for character {OrderedCharacter} in {this}"); #endif return(false); } if (!shipCommandManager.AbleToTakeOrder(OrderedCharacter)) { #if DEBUG ShipCommandManager.ShipCommandLog(OrderedCharacter + " was unable to perform assigned order in " + this); #endif return(false); } return(true); }
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 (!IsAllowed) { Priority = 0; Abandon = true; } else if (HumanAIController.IsTrueForAnyCrewMember(other => other != HumanAIController && 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 = MathHelper.Min(AIObjectiveManager.OrderPriority - reduction, 90); float devotion = CumulatedDevotion / 100; Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1)); } return(Priority); }
private void OnArrestTargetReached() { if (HumanAIController.HasItem(character, "handlocker", out Item handCuffs) && Enemy.Stun > 0 && character.CanInteractWith(Enemy)) { if (HumanAIController.TryToMoveItem(handCuffs, Enemy.Inventory)) { handCuffs.Equip(Enemy); } else { #if DEBUG DebugConsole.NewMessage($"{character.Name}: Failed to handcuff the target.", Color.Red); #endif } // Confiscate stolen goods. foreach (var item in Enemy.Inventory.Items) { if (item == null || item == handCuffs) { continue; } if (item.StolenDuringRound) { item.Drop(character); character.Inventory.TryPutItem(item, character, new List <InvSlotType>() { InvSlotType.Any }); } } // TODO: enable //character.Speak(TextManager.Get("DialogTargetArrested"), null, 3.0f, "targetarrested", 30.0f); IsCompleted = true; } }
protected override void Act(float deltaTime) { // Only continue when the get item sub objectives have been completed. if (subObjectives.Any()) { return; } if (HumanAIController.FindSuitableContainer(character, item, ignoredContainers, ref itemIndex, out Item suitableContainer)) { itemIndex = 0; if (suitableContainer != null) { bool equip = item.HasTag(AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR) || ( item.GetComponent <Wearable>() == null && item.AllowedSlots.None(s => s == InvSlotType.Card || s == InvSlotType.Head || s == InvSlotType.Headset || s == InvSlotType.InnerClothes || s == InvSlotType.OuterClothes)); TryAddSubObjective(ref decontainObjective, () => new AIObjectiveDecontainItem(character, item, objectiveManager, targetContainer: suitableContainer.GetComponent <ItemContainer>()) { Equip = equip, DropIfFails = true }, onCompleted: () => { if (equip) { HumanAIController.ReequipUnequipped(); } IsCompleted = true; }, onAbandon: () => { if (equip) { HumanAIController.ReequipUnequipped(); } if (decontainObjective != null && decontainObjective.ContainObjective != null && decontainObjective.ContainObjective.CanBeCompleted) { ignoredContainers.Add(suitableContainer); } else { Abandon = true; } }); } else { Abandon = true; } } else { objectiveManager.GetObjective <AIObjectiveIdle>().Wander(deltaTime); } }
public override void Reset() { base.Reset(); moveInsideObjective = null; moveInCaveObjective = null; moveOutsideObjective = null; usingEscapeBehavior = false; isSteeringThroughGap = false; HumanAIController.ResetEscape(); }
protected override float GetPriority() { if (!IsAllowed || Item.IgnoreByAI(character)) { Priority = 0; Abandon = true; if (IsRepairing()) { Item.Repairables.ForEach(r => r.StopRepairing(character)); } return(Priority); } if (HumanAIController.IsItemRepairedByAnother(Item, out _)) { Priority = 0; IsCompleted = true; } else { float distanceFactor = 1; if (!isPriority && Item.CurrentHull != character.CurrentHull) { float yDist = Math.Abs(character.WorldPosition.Y - Item.WorldPosition.Y); yDist = yDist > 100 ? yDist * 5 : 0; float dist = Math.Abs(character.WorldPosition.X - Item.WorldPosition.X) + yDist; distanceFactor = MathHelper.Lerp(1, 0.25f, MathUtils.InverseLerp(0, 4000, dist)); } float requiredSuccessFactor = objectiveManager.HasOrder <AIObjectiveRepairItems>() ? 0 : AIObjectiveRepairItems.RequiredSuccessFactor; float severity = isPriority ? 1 : AIObjectiveRepairItems.GetTargetPriority(Item, character, requiredSuccessFactor) / 100; bool isSelected = IsRepairing(); float selectedBonus = isSelected ? 100 - MaxDevotion : 0; float devotion = (CumulatedDevotion + selectedBonus) / 100; float reduction = isPriority ? 1 : isSelected ? 2 : 3; float max = AIObjectiveManager.LowestOrderPriority - reduction; float highestWeight = -1; foreach (string tag in Item.Prefab.Tags) { if (JobPrefab.ItemRepairPriorities.TryGetValue(tag, out float weight) && weight > highestWeight) { highestWeight = weight; } } if (highestWeight == -1) { // Predefined weight not found. highestWeight = 1; } Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (severity * distanceFactor * highestWeight * PriorityModifier), 0, 1)); } return(Priority); }
private void Move(float deltaTime) { switch (Mode) { case CombatMode.Offensive: case CombatMode.Arrest: Engage(deltaTime); break; case CombatMode.Defensive: if (character.IsOnPlayerTeam && !Enemy.IsPlayer && objectiveManager.IsCurrentOrder <AIObjectiveGoTo>()) { if ((character.CurrentHull == null || character.CurrentHull == Enemy.CurrentHull) && sqrDistance < 200 * 200) { Engage(deltaTime); } else { // Keep following the goto target var gotoObjective = objectiveManager.GetOrder <AIObjectiveGoTo>(); if (gotoObjective != null) { gotoObjective.ForceAct(deltaTime); if (!character.AnimController.InWater) { HumanAIController.FaceTarget(Enemy); ForceWalk = true; HumanAIController.AutoFaceMovement = false; } } else { SteeringManager.Reset(); } } } else { Retreat(deltaTime); } break; case CombatMode.Retreat: Retreat(deltaTime); break; default: throw new NotImplementedException(); } }
private bool IsOperatedByAnother(ItemComponent target) { foreach (var c in Character.CharacterList) { if (c == character) { continue; } if (!HumanAIController.IsFriendly(c)) { continue; } if (c.SelectedConstruction != target.Item) { continue; } // If the other character is player, don't try to operate if (c.IsRemotePlayer || Character.Controlled == c) { return(true); } if (c.AIController is HumanAIController humanAi) { // If the other character is ordered to operate the item, let him do it if (humanAi.ObjectiveManager.IsCurrentOrder <AIObjectiveOperateItem>()) { return(true); } else { if (target is Steering) { // Steering is hard-coded -> cannot use the required skills collection defined in the xml return(character.GetSkillLevel("helm") <= c.GetSkillLevel("helm")); } else { return(target.DegreeOfSuccess(character) <= target.DegreeOfSuccess(c)); } } } else { // Shouldn't go here, unless we allow non-humans to operate items return(false); } } return(false); }
protected override void Act(float deltaTime) { if (abortCondition != null && abortCondition()) { Abandon = true; return; } if (AllowCoolDown) { coolDownTimer -= deltaTime; } if (seekAmmunitionObjective == null && seekWeaponObjective == null) { if (Mode != CombatMode.Retreat && TryArm() && !IsEnemyDisabled) { OperateWeapon(deltaTime); } if (HoldPosition) { SteeringManager.Reset(); } else if (seekAmmunitionObjective == null && seekWeaponObjective == null) { Move(deltaTime); } switch (Mode) { case CombatMode.Offensive: if (TargetEliminated && objectiveManager.IsCurrentOrder <AIObjectiveFightIntruders>()) { character.Speak(TextManager.Get("DialogTargetDown"), null, 3.0f, "targetdown", 30.0f); } break; case CombatMode.Arrest: if (HumanAIController.HasItem(Enemy, "handlocker", out _, requireEquipped: true)) { IsCompleted = true; } else if (Enemy.IsKnockedDown && !objectiveManager.IsCurrentObjective <AIObjectiveFightIntruders>() && !HumanAIController.HasItem(character, "handlocker", out _, requireEquipped: false)) { IsCompleted = true; } break; } } }
public 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))); }
public override float GetPriority() { if (character.TeamID == CharacterTeamType.FriendlyNPC && Enemy != null) { if (Enemy.Submarine == null || (Enemy.Submarine.TeamID != character.TeamID && Enemy.Submarine != character.Submarine)) { Priority = 0; Abandon = true; return Priority; } } float damageFactor = MathUtils.InverseLerp(0.0f, 5.0f, HumanAIController.GetDamageDoneByAttacker(Enemy) / 100.0f); Priority = TargetEliminated ? 0 : Math.Min((95 + damageFactor) * PriorityModifier, 100); return Priority; }
public static bool IsValidTarget(Character target, Character character) { if (target == null || target.IsDead || target.Removed) { return(false); } if (!HumanAIController.IsFriendly(character, target)) { return(false); } if (character.AIController is HumanAIController humanAI) { if (GetVitalityFactor(target) > GetVitalityThreshold(humanAI.ObjectiveManager)) { return(false); } } else { if (GetVitalityFactor(target) > vitalityThreshold) { return(false); } } if (target.Submarine == null || character.Submarine == null) { return(false); } if (target.Submarine.TeamID != character.Submarine.TeamID) { return(false); } if (target.CurrentHull == null) { return(false); } if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, true)) { return(false); } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { return(false); } return(true); }
public bool CreateCombatBehavior(MentalType mentalType) { Character mentalAttackTarget = Character.CharacterList.Where( possibleTarget => HumanAIController.IsActive(possibleTarget) && (possibleTarget.TeamID != character.TeamID || mentalType == MentalType.Berserk) && humanAIController.VisibleHulls.Contains(possibleTarget.CurrentHull) && possibleTarget != character).GetRandom(); if (mentalAttackTarget == null) { return(false); } var combatMode = AIObjectiveCombat.CombatMode.None; bool holdFire = mentalType == MentalType.Afraid && character.IsSecurity; switch (mentalType) { case MentalType.Afraid: combatMode = character.IsSecurity ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Retreat; break; case MentalType.Desperate: // might be unnecessary to explicitly declare as arrest against non-humans combatMode = character.IsSecurity && mentalAttackTarget.IsHuman ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Defensive; break; case MentalType.Berserk: combatMode = AIObjectiveCombat.CombatMode.Offensive; break; } // using this as an explicit time-out for the behavior. it's possible it will never run out because of the manager being disabled, but combat objective has failsafes for that mentalBehaviorTimer = MentalBehaviorInterval; humanAIController.AddCombatObjective(combatMode, mentalAttackTarget, allowHoldFire: holdFire, abortCondition: obj => mentalBehaviorTimer <= 0f); string textIdentifier = $"dialogmentalstatereaction{combatMode.ToString().ToLowerInvariant()}"; character.Speak(TextManager.Get(textIdentifier), delay: Rand.Range(0.5f, 1.0f), identifier: textIdentifier, minDurationBetweenSimilar: 25f); if (mentalType == MentalType.Berserk && !character.HasTeamChange(MentalTeamChange)) { // TODO: could this be handled in the switch block above? character.TryAddNewTeamChange(MentalTeamChange, new ActiveTeamChange(CharacterTeamType.None, ActiveTeamChange.TeamChangePriorities.Absolute, aggressiveBehavior: true)); } return(true); }
public static bool IsValidTarget(Character target, Character character) { if (target == null || target.Removed) { return(false); } if (target.IsDead) { return(false); } if (target.IsUnconscious && target.Params.Health.ConstantHealthRegeneration <= 0.0f) { return(false); } if (target == character) { return(false); } if (target.Submarine == null) { return(false); } if (character.Submarine == null) { return(false); } if (target.CurrentHull == null) { return(false); } if (HumanAIController.IsFriendly(character, target)) { return(false); } if (!character.Submarine.IsConnectedTo(target.Submarine)) { return(false); } if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { return(false); } return(true); }
public static bool IsValidTarget(Character target, Character character) { if (target == null || target.IsDead || target.Removed) { return(false); } if (!HumanAIController.IsFriendly(character, target)) { return(false); } if (character.AIController is HumanAIController humanAI) { if (target.Bleeding < 1 && target.Vitality / target.MaxVitality > GetVitalityThreshold(humanAI.ObjectiveManager)) { return(false); } } else { if (target.Bleeding < 1 && target.Vitality / target.MaxVitality > vitalityThreshold) { return(false); } } if (target.Submarine == null) { return(false); } if (target.Submarine.TeamID != character.Submarine.TeamID) { return(false); } if (target.CurrentHull == null) { return(false); } if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, true)) { return(false); } return(true); }
protected override float TargetEvaluation() { var selectedItem = character.SelectedConstruction; if (selectedItem != null && AIObjectiveRepairItem.IsRepairing(character, selectedItem) && selectedItem.ConditionPercentage < 100) { // Don't stop fixing until completely done return(100); } int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective <AIObjectiveRepairItems>() && !c.Character.IsIncapacitated, onlyBots: true); int items = Targets.Count; if (items == 0) { return(0); } bool anyFixers = otherFixers > 0; float ratio = anyFixers ? items / (float)otherFixers : 1; if (objectiveManager.IsOrder(this)) { return(Targets.Sum(t => 100 - t.ConditionPercentage)); } else { if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f)) { // Enough fixers return(0); } if (RequireAdequateSkills) { return(Targets.Sum(t => GetTargetPriority(t, character, RequiredSuccessFactor)) * ratio); } else { return(Targets.Sum(t => 100 - t.ConditionPercentage) * ratio); } } }
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 AIObjective ObjectiveConstructor(Character target) { var combatObjective = new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier); if (character.TeamID == Character.TeamType.FriendlyNPC && target.TeamID == Character.TeamType.Team1 && GameMain.GameSession?.GameMode is CampaignMode campaign) { var reputation = campaign.Map?.CurrentLocation?.Reputation; if (reputation != null && reputation.NormalizedValue < Reputation.HostileThreshold) { combatObjective.holdFireCondition = () => { //hold fire while the enemy is in the airlock (except if they've attacked us) if (HumanAIController.GetDamageDoneByAttacker(target) > 0.0f) { return(false); } return(target.CurrentHull == null || target.CurrentHull.OutpostModuleTags.Any(t => t.Equals("airlock", System.StringComparison.OrdinalIgnoreCase))); }; character.Speak(TextManager.Get("dialogenteroutpostwarning"), null, Rand.Range(0.5f, 1.0f), "leaveoutpostwarning", 30.0f); } } return(combatObjective); }
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); } }); } }
protected virtual void PutItem(Item item, int i, Character user, bool removeItem = true, bool createNetworkEvent = true) { if (i < 0 || i >= slots.Length) { string errorMsg = "Inventory.PutItem failed: index was out of range(" + i + ").\n" + Environment.StackTrace.CleanupStackTrace(); GameAnalyticsManager.AddErrorEventOnce("Inventory.PutItem:IndexOutOfRange", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } if (Owner == null) { return; } Inventory prevInventory = item.ParentInventory; Inventory prevOwnerInventory = item.FindParentInventory(inv => inv is CharacterInventory); if (createNetworkEvent) { CreateNetworkEvent(); //also delay syncing the inventory the item was inside if (prevInventory != null && prevInventory != this) { prevInventory.syncItemsDelay = 1.0f; } } if (removeItem) { item.Drop(user); if (item.ParentInventory != null) { item.ParentInventory.RemoveItem(item); } } slots[i].Add(item); item.ParentInventory = this; #if CLIENT if (visualSlots != null) { visualSlots[i]?.ShowBorderHighlight(Color.White, 0.1f, 0.4f); } #endif if (item.body != null) { item.body.Enabled = false; item.body.BodyType = FarseerPhysics.BodyType.Dynamic; } #if SERVER if (prevOwnerInventory is CharacterInventory characterInventory && characterInventory != this && Owner == user) { var client = GameMain.Server?.ConnectedClients?.Find(cl => cl.Character == user); GameMain.Server?.KarmaManager.OnItemTakenFromPlayer(characterInventory, client, item); } #endif if (this is CharacterInventory) { if (prevInventory != this && prevOwnerInventory != this) { HumanAIController.ItemTaken(item, user); } } else { if (item.FindParentInventory(inv => inv is CharacterInventory) is CharacterInventory currentInventory) { if (currentInventory != prevInventory) { HumanAIController.ItemTaken(item, user); } } } }
protected override void Act(float deltaTime) { if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead) { Abandon = true; return; } var otherRescuer = targetCharacter.SelectedBy; if (otherRescuer != null && otherRescuer != character) { // Someone else is rescuing/holding the target. Abandon = otherRescuer.IsPlayer || character.GetSkillLevel("medical") < otherRescuer.GetSkillLevel("medical"); return; } if (targetCharacter != character) { if (targetCharacter.IsIncapacitated) { // Check if the character needs more oxygen if (!ignoreOxygen && character.SelectedCharacter == targetCharacter || character.CanInteractWith(targetCharacter)) { // Replace empty oxygen and welding fuel. if (HumanAIController.HasItem(targetCharacter, AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR, out IEnumerable <Item> suits, requireEquipped: true)) { Item suit = suits.FirstOrDefault(); if (suit != null) { AIController.UnequipEmptyItems(character, suit); AIController.UnequipContainedItems(character, suit, it => it.HasTag("weldingfuel")); } } else if (HumanAIController.HasItem(targetCharacter, AIObjectiveFindDivingGear.LIGHT_DIVING_GEAR, out IEnumerable <Item> masks, requireEquipped: true)) { Item mask = masks.FirstOrDefault(); if (mask != null) { AIController.UnequipEmptyItems(character, mask); AIController.UnequipContainedItems(character, mask, it => it.HasTag("weldingfuel")); } } bool ShouldRemoveDivingSuit() => targetCharacter.OxygenAvailable < CharacterHealth.InsufficientOxygenThreshold && targetCharacter.CurrentHull?.LethalPressure <= 0; if (ShouldRemoveDivingSuit()) { suits.ForEach(suit => suit.Drop(character)); } else if (suits.Any() && suits.None(s => s.OwnInventory?.AllItems != null && s.OwnInventory.AllItems.Any(it => it.HasTag(AIObjectiveFindDivingGear.OXYGEN_SOURCE) && it.ConditionPercentage > 0))) { // The target has a suit equipped with an empty oxygen tank. // Can't remove the suit, because the target needs it. // If we happen to have an extra oxygen tank in the inventory, let's swap it. Item spareOxygenTank = FindOxygenTank(targetCharacter) ?? FindOxygenTank(character); if (spareOxygenTank != null) { Item suit = suits.FirstOrDefault(); if (suit != null) { // Insert the new oxygen tank TryAddSubObjective(ref replaceOxygenObjective, () => new AIObjectiveContainItem(character, spareOxygenTank, suit.GetComponent <ItemContainer>(), objectiveManager), onCompleted: () => RemoveSubObjective(ref replaceOxygenObjective), onAbandon: () => { RemoveSubObjective(ref replaceOxygenObjective); ignoreOxygen = true; if (ShouldRemoveDivingSuit()) { suits.ForEach(suit => suit.Drop(character)); } }); return; } } Item FindOxygenTank(Character c) => c.Inventory.FindItem(i => i.HasTag(AIObjectiveFindDivingGear.OXYGEN_SOURCE) && i.ConditionPercentage > 1 && i.FindParentInventory(inv => inv.Owner is Item otherItem && otherItem.HasTag("diving")) == null, recursive: true); } } if (HumanAIController.GetHullSafety(targetCharacter.CurrentHull, targetCharacter) < HumanAIController.HULL_SAFETY_THRESHOLD) { // Incapacitated target is not in a safe place -> Move to a safe place first if (character.SelectedCharacter != targetCharacter) { if (targetCharacter.CurrentHull != null && HumanAIController.VisibleHulls.Contains(targetCharacter.CurrentHull) && targetCharacter.CurrentHull.DisplayName != null) { character.Speak(TextManager.GetWithVariables("DialogFoundUnconsciousTarget", new string[2] { "[targetname]", "[roomname]" }, new string[2] { targetCharacter.Name, targetCharacter.CurrentHull.DisplayName }, new bool[2] { false, true }), null, 1.0f, "foundunconscioustarget" + targetCharacter.Name, 60.0f); } // Go to the target and select it if (!character.CanInteractWith(targetCharacter)) { RemoveSubObjective(ref replaceOxygenObjective); RemoveSubObjective(ref goToObjective); TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager) { CloseEnough = CloseEnoughToTreat, DialogueIdentifier = "dialogcannotreachpatient", TargetName = targetCharacter.DisplayName }, onCompleted: () => RemoveSubObjective(ref goToObjective), onAbandon: () => { RemoveSubObjective(ref goToObjective); Abandon = true; }); } else { character.SelectCharacter(targetCharacter); } } else { // Drag the character into safety if (safeHull == null) { if (findHullTimer > 0) { findHullTimer -= deltaTime; } else { safeHull = objectiveManager.GetObjective <AIObjectiveFindSafety>().FindBestHull(HumanAIController.VisibleHulls); findHullTimer = findHullInterval * Rand.Range(0.9f, 1.1f); } } if (safeHull != null && character.CurrentHull != safeHull) { RemoveSubObjective(ref replaceOxygenObjective); RemoveSubObjective(ref goToObjective); TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(safeHull, character, objectiveManager), onCompleted: () => RemoveSubObjective(ref goToObjective), onAbandon: () => { RemoveSubObjective(ref goToObjective); safeHull = character.CurrentHull; }); } } } } } if (subObjectives.Any()) { return; } if (targetCharacter != character && !character.CanInteractWith(targetCharacter)) { RemoveSubObjective(ref replaceOxygenObjective); RemoveSubObjective(ref goToObjective); // Go to the target and select it TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager) { CloseEnough = CloseEnoughToTreat, DialogueIdentifier = "dialogcannotreachpatient", TargetName = targetCharacter.DisplayName }, onCompleted: () => RemoveSubObjective(ref goToObjective), onAbandon: () => { RemoveSubObjective(ref goToObjective); Abandon = true; }); } else { // We can start applying treatment if (character != targetCharacter && character.SelectedCharacter != targetCharacter) { if (targetCharacter.CurrentHull.DisplayName != null) { character.Speak(TextManager.GetWithVariables("DialogFoundWoundedTarget", new string[2] { "[targetname]", "[roomname]" }, new string[2] { targetCharacter.Name, targetCharacter.CurrentHull.DisplayName }, new bool[2] { false, true }), null, 1.0f, "foundwoundedtarget" + targetCharacter.Name, 60.0f); } } GiveTreatment(deltaTime); } }
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); }