public void SetOrder(Order order, string option, Character orderGiver, bool speak) { if (character.IsDead) { #if DEBUG DebugConsole.ThrowError("Attempted to set an order for a dead character"); #else return; #endif } ClearIgnored(); CurrentOrder = CreateObjective(order, option, orderGiver, isAutonomous: false); if (CurrentOrder == null) { // Recreate objectives, because some of them may be removed, if impossible to complete (e.g. due to path finding) CreateAutonomousObjectives(); } else { // This should be redundant, because all the objectives are reset when they are selected as active. CurrentOrder.Reset(); if (speak) { character.Speak(TextManager.Get("DialogAffirmative"), null, 1.0f); if (speakRoutine != null) { CoroutineManager.StopCoroutines(speakRoutine); } speakRoutine = CoroutineManager.InvokeAfter(() => { if (GameMain.GameSession == null || Level.Loaded == null) { return; } if (CurrentOrder != null && character.SpeechImpediment < 100.0f) { if (CurrentOrder is AIObjectiveRepairItems repairItems && repairItems.Targets.None()) { character.Speak(TextManager.Get("DialogNoRepairTargets"), null, 3.0f, "norepairtargets"); } else if (CurrentOrder is AIObjectiveChargeBatteries chargeBatteries && chargeBatteries.Targets.None()) { character.Speak(TextManager.Get("DialogNoBatteries"), null, 3.0f, "nobatteries"); } else if (CurrentOrder is AIObjectiveExtinguishFires extinguishFires && extinguishFires.Targets.None()) { character.Speak(TextManager.Get("DialogNoFire"), null, 3.0f, "nofire"); }
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 void SetOrder(Order order, string option, Character orderGiver, bool speak = true) { CurrentOrderOption = option; CurrentOrder = order; objectiveManager.SetOrder(order, option, orderGiver); if (speak && Character.SpeechImpediment < 100.0f) { Character.Speak(TextManager.Get("DialogAffirmative"), null, 1.0f); } SetOrderProjSpecific(order); }
public void SetOrder(Order order, string option, Character orderGiver, bool speak = true) { CurrentOrderOption = option; CurrentOrder = order; objectiveManager.SetOrder(order, option, orderGiver); if (ObjectiveManager.CurrentOrder != null && speak && Character.SpeechImpediment < 100.0f) { if (ObjectiveManager.CurrentOrder is AIObjectiveRepairItems repairItems && repairItems.Targets.None()) { Character.Speak(TextManager.Get("DialogNoRepairTargets"), null, 3.0f, "norepairtargets"); } else if (ObjectiveManager.CurrentOrder is AIObjectiveChargeBatteries chargeBatteries && chargeBatteries.Targets.None()) { Character.Speak(TextManager.Get("DialogNoBatteries"), null, 3.0f, "nobatteries"); }
private void UpdateSpeaking() { if (Character.Oxygen < 20.0f) { Character.Speak(TextManager.Get("DialogLowOxygen"), null, 0, "lowoxygen", 30.0f); } if (Character.Bleeding > 2.0f) { Character.Speak(TextManager.Get("DialogBleeding"), null, 0, "bleeding", 30.0f); } if (Character.PressureTimer > 50.0f && Character.CurrentHull != null) { Character.Speak(TextManager.GetWithVariable("DialogPressure", "[roomname]", Character.CurrentHull.DisplayName, true), null, 0, "pressure", 30.0f); } }
protected void ReportProblems() { Order newOrder = null; if (Character.CurrentHull != null) { if (Character.CurrentHull.FireSources.Count > 0) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportfire"); newOrder = new Order(orderPrefab, Character.CurrentHull, null); } if (Character.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.ConnectedDoor == null && g.Open > 0.0f)) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportbreach"); newOrder = new Order(orderPrefab, Character.CurrentHull, null); } foreach (Character c in Character.CharacterList) { if (c.CurrentHull == Character.CurrentHull && !c.IsDead && (c.AIController is EnemyAIController || (c.TeamID != Character.TeamID && Character.TeamID != Character.TeamType.FriendlyNPC && c.TeamID != Character.TeamType.FriendlyNPC))) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportintruders"); newOrder = new Order(orderPrefab, Character.CurrentHull, null); } } } if (Character.CurrentHull != null && (Character.Bleeding > 1.0f || Character.Vitality < Character.MaxVitality * 0.1f)) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "requestfirstaid"); newOrder = new Order(orderPrefab, Character.CurrentHull, null); } 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); } } }
public void SetOrder(Order order, string option, int priority, Character orderGiver, bool speak) { if (character.IsDead) { #if DEBUG DebugConsole.ThrowError("Attempted to set an order for a dead character"); #else return; #endif } ClearIgnored(); if (order == null || order.Identifier == "dismissed") { if (!string.IsNullOrEmpty(option)) { if (CurrentOrders.Any(o => o.MatchesDismissedOrder(option))) { var dismissedOrderInfo = CurrentOrders.First(o => o.MatchesDismissedOrder(option)); CurrentOrders.Remove(dismissedOrderInfo); } } else { CurrentOrders.Clear(); } } // Make sure the order priorities reflect those set by the player for (int i = CurrentOrders.Count - 1; i >= 0; i--) { var currentOrder = CurrentOrders[i]; if (currentOrder.Objective == null || currentOrder.MatchesOrder(order, option)) { CurrentOrders.RemoveAt(i); continue; } var currentOrderInfo = character.GetCurrentOrder(currentOrder.Order, currentOrder.OrderOption); if (currentOrderInfo.HasValue) { int currentPriority = currentOrderInfo.Value.ManualPriority; if (currentOrder.ManualPriority != currentPriority) { CurrentOrders[i] = new OrderInfo(currentOrder, currentPriority); } } else { CurrentOrders.RemoveAt(i); } } var newCurrentOrder = CreateObjective(order, option, orderGiver, isAutonomous: false); if (newCurrentOrder != null) { CurrentOrders.Add(new OrderInfo(order, option, priority, newCurrentOrder)); } if (!HasOrders()) { // Recreate objectives, because some of them may be removed, if impossible to complete (e.g. due to path finding) CreateAutonomousObjectives(); } else { // This should be redundant, because all the objectives are reset when they are selected as active. newCurrentOrder?.Reset(); if (speak && character.IsOnPlayerTeam) { character.Speak(TextManager.Get("DialogAffirmative"), null, 1.0f); //if (speakRoutine != null) //{ // CoroutineManager.StopCoroutines(speakRoutine); //} //speakRoutine = CoroutineManager.InvokeAfter(() => //{ // if (GameMain.GameSession == null || Level.Loaded == null) { return; } // if (newCurrentOrder != null && character.SpeechImpediment < 100.0f) // { // if (newCurrentOrder is AIObjectiveRepairItems repairItems && repairItems.Targets.None()) // { // character.Speak(TextManager.Get("DialogNoRepairTargets"), null, 3.0f, "norepairtargets"); // } // else if (newCurrentOrder is AIObjectiveChargeBatteries chargeBatteries && chargeBatteries.Targets.None()) // { // character.Speak(TextManager.Get("DialogNoBatteries"), null, 3.0f, "nobatteries"); // } // else if (newCurrentOrder is AIObjectiveExtinguishFires extinguishFires && extinguishFires.Targets.None()) // { // character.Speak(TextManager.Get("DialogNoFire"), null, 3.0f, "nofire"); // } // else if (newCurrentOrder is AIObjectiveFixLeaks fixLeaks && fixLeaks.Targets.None()) // { // character.Speak(TextManager.Get("DialogNoLeaks"), null, 3.0f, "noleaks"); // } // else if (newCurrentOrder is AIObjectiveFightIntruders fightIntruders && fightIntruders.Targets.None()) // { // character.Speak(TextManager.Get("DialogNoEnemies"), null, 3.0f, "noenemies"); // } // else if (newCurrentOrder is AIObjectiveRescueAll rescueAll && rescueAll.Targets.None()) // { // character.Speak(TextManager.Get("DialogNoRescueTargets"), null, 3.0f, "norescuetargets"); // } // else if (newCurrentOrder is AIObjectivePumpWater pumpWater && pumpWater.Targets.None()) // { // character.Speak(TextManager.Get("DialogNoPumps"), null, 3.0f, "nopumps"); // } // } //}, 3); } } }
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 } } }
void UpdateCommandDecision(float timeSinceLastUpdate) { #if DEBUG ShipCommandLog("Updating command for character " + character); #endif shipGlobalIssues.ForEach(c => c.CalculateGlobalIssue()); AlliedCharacters.Clear(); EnemyCharacters.Clear(); bool isEmergency = false; foreach (Character potentialCharacter in Character.CharacterList) { if (!HumanAIController.IsActive(character)) { continue; } if (HumanAIController.IsFriendly(character, potentialCharacter, true) && potentialCharacter.AIController is HumanAIController) { if (AbleToTakeOrder(potentialCharacter)) { AlliedCharacters.Add(potentialCharacter); } } else { EnemyCharacters.Add(potentialCharacter); if (potentialCharacter.Submarine == CommandedSubmarine) // if enemies are on board, don't issue normal orders anymore { isEmergency = true; } } } attendedIssues.Clear(); availableIssues.Clear(); foreach (ShipIssueWorker shipIssueWorker in ShipIssueWorkers) { float importance = shipIssueWorker.CalculateImportance(isEmergency); if (shipIssueWorker.OrderAttendedTo(timeSinceLastUpdate)) { #if DEBUG ShipCommandLog("Current importance for " + shipIssueWorker + " was " + importance + " and it was already being attended by " + shipIssueWorker.OrderedCharacter); #endif attendedIssues.Add(shipIssueWorker); } else { #if DEBUG ShipCommandLog("Current importance for " + shipIssueWorker + " was " + importance + " and it is not attended to"); #endif shipIssueWorker.RemoveOrder(); availableIssues.Add(shipIssueWorker); } } availableIssues.Sort((x, y) => y.Importance.CompareTo(x.Importance)); attendedIssues.Sort((x, y) => x.Importance.CompareTo(y.Importance)); ShipIssueWorker mostImportantIssue = availableIssues.FirstOrDefault(); float bestValue = 0f; Character bestCharacter = null; if (mostImportantIssue != null && mostImportantIssue.Importance > MinimumIssueThreshold) { IEnumerable <Character> bestCharacters = CrewManager.GetCharactersSortedForOrder(mostImportantIssue.SuggestedOrderPrefab, AlliedCharacters, character, true); foreach (Character orderedCharacter in bestCharacters) { float issueApplicability = mostImportantIssue.Importance; // prefer not to switch if not qualified issueApplicability *= mostImportantIssue.SuggestedOrderPrefab.AppropriateJobs.Contains(orderedCharacter.Info.Job.Prefab.Identifier) ? 1f : 0.75f; ShipIssueWorker occupiedIssue = attendedIssues.FirstOrDefault(i => i.OrderedCharacter == orderedCharacter); if (occupiedIssue != null) { if (occupiedIssue.GetType() == mostImportantIssue.GetType() && mostImportantIssue is ShipIssueWorkerGlobal && occupiedIssue is ShipIssueWorkerGlobal) { continue; } // reverse redundancy to ensure certain issues can be switched over easily (operating weapons) if (mostImportantIssue.AllowEasySwitching && occupiedIssue.AllowEasySwitching) { issueApplicability /= mostImportantIssue.CurrentRedundancy; } // give slight preference if not qualified for current job issueApplicability += occupiedIssue.SuggestedOrderPrefab.AppropriateJobs.Contains(orderedCharacter.Info.Job.Prefab.Identifier) ? 0 : 7.5f; // prefer not to switch orders unless considerably more important issueApplicability -= IssueDevotionBuffer; if (issueApplicability + IssueDevotionBuffer < occupiedIssue.Importance) { continue; } } // prefer first one in bestCharacters in tiebreakers if (issueApplicability > bestValue) { bestValue = issueApplicability; bestCharacter = orderedCharacter; } } } if (bestCharacter != null && mostImportantIssue != null) { #if DEBUG ShipCommandLog("Setting " + mostImportantIssue + " for character " + bestCharacter); #endif mostImportantIssue.SetOrder(bestCharacter); } else // if we didn't give an order, let's try to dismiss someone instead { foreach (ShipIssueWorker shipIssueWorker in ShipIssueWorkers) { if (shipIssueWorker.Importance <= 0f && shipIssueWorker.OrderAttendedTo()) { #if DEBUG ShipCommandLog("Dismissing " + shipIssueWorker + " for character " + shipIssueWorker.OrderedCharacter); #endif Order orderPrefab = Order.GetPrefab("dismissed"); character.Speak(orderPrefab.GetChatMessage(shipIssueWorker.OrderedCharacter.Name, "", givingOrderToSelf: false)); shipIssueWorker.OrderedCharacter.SetOrder(Order.GetPrefab("dismissed"), orderOption: null, priority: 3, character); shipIssueWorker.RemoveOrder(); break; } } } }