3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
20 const float TreatmentDelay = 0.5f;
22 const float CloseEnoughToTreat = 100.0f;
29 private float treatmentTimer;
30 private Hull safeHull;
31 private float findHullTimer;
32 private bool ignoreOxygen;
33 private readonly
float findHullInterval = 1.0f;
34 private bool performedCpr;
39 if (targetCharacter ==
null)
41 string errorMsg = $
"Attempted to create a Rescue objective with no target!\n" + Environment.StackTrace.CleanupStackTrace();
43 GameAnalyticsManager.AddErrorEventOnce(
"AIObjectiveRescue:ctor:targetnull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
62 protected override void Act(
float deltaTime)
70 if (otherRescuer !=
null && otherRescuer !=
character)
86 Item suit = suits.FirstOrDefault();
95 Item mask = masks.FirstOrDefault();
103 if (ShouldRemoveDivingSuit())
105 suits.ForEach(suit => suit.Drop(
character));
107 else if (suits.Any() && suits.None(s => s.OwnInventory?.AllItems !=
null && s.OwnInventory.AllItems.Any(it => it.HasTag(Tags.OxygenSource) && it.ConditionPercentage > 0)))
113 if (spareOxygenTank !=
null)
115 Item suit = suits.FirstOrDefault();
120 onCompleted: () => RemoveSubObjective(ref replaceOxygenObjective),
123 RemoveSubObjective(ref replaceOxygenObjective);
125 if (ShouldRemoveDivingSuit())
136 i.HasTag(Tags.OxygenSource) &&
137 i.ConditionPercentage > 1 &&
138 i.FindParentInventory(inv => inv.Owner is
Item otherItem && otherItem.
HasTag(Tags.DivingGear)) ==
null,
151 character.
Speak(TextManager.GetWithVariables(
"DialogFoundUnconsciousTarget",
154 null, 1.0f, $
"foundunconscioustarget{Target.Name}".ToIdentifier(), 60.0f);
159 RemoveSubObjective(ref replaceOxygenObjective);
160 RemoveSubObjective(ref goToObjective);
163 CloseEnough = CloseEnoughToTreat,
164 DialogueIdentifier =
"dialogcannotreachpatient".ToIdentifier(),
167 onCompleted: () => RemoveSubObjective(ref goToObjective),
170 RemoveSubObjective(ref goToObjective);
182 if (safeHull ==
null)
184 if (findHullTimer > 0)
186 findHullTimer -= deltaTime;
191 if (hullSearchStatus != HullSearchStatus.Finished) {
return; }
192 safeHull = potentialSafeHull;
193 findHullTimer = findHullInterval * Rand.Range(0.9f, 1.1f);
198 RemoveSubObjective(ref replaceOxygenObjective);
199 RemoveSubObjective(ref goToObjective);
201 onCompleted: () => RemoveSubObjective(ref goToObjective),
204 RemoveSubObjective(ref goToObjective);
218 RemoveSubObjective(ref replaceOxygenObjective);
219 RemoveSubObjective(ref goToObjective);
223 CloseEnough = CloseEnoughToTreat,
224 DialogueIdentifier =
"dialogcannotreachpatient".ToIdentifier(),
227 onCompleted: () => RemoveSubObjective(ref goToObjective),
230 RemoveSubObjective(ref goToObjective);
241 character.
Speak(TextManager.GetWithVariables(
"DialogFoundWoundedTarget",
244 null, 1.0f, $
"foundwoundedtarget{Target.Name}".ToIdentifier(), 60.0f);
247 GiveTreatment(deltaTime);
251 private readonly List<Identifier> suitableItemIdentifiers =
new List<Identifier>();
252 private readonly List<LocalizedString> itemNameList =
new List<LocalizedString>();
253 private readonly Dictionary<Identifier, float> currentTreatmentSuitabilities =
new Dictionary<Identifier, float>();
254 private void GiveTreatment(
float deltaTime)
258 string errorMsg = $
"{character.Name}: Attempted to update a Rescue objective with no target!";
259 DebugConsole.ThrowError(errorMsg);
271 if (treatmentTimer > 0.0f)
273 treatmentTimer -= deltaTime;
276 treatmentTimer = TreatmentDelay;
280 float bestSuitability = 0.0f;
281 Item bestItem =
null;
287 currentTreatmentSuitabilities,
290 predictFutureDuration: 10.0f);
292 foreach (KeyValuePair<Identifier, float> treatmentSuitability
in currentTreatmentSuitabilities)
294 float thisSuitability = currentTreatmentSuitabilities[treatmentSuitability.Key];
295 if (thisSuitability <= 0) {
continue; }
303 if (matchingItem ==
null) {
continue; }
308 float suitabilityForThisAffliction = affliction.Prefab.GetTreatmentSuitability(matchingItem);
309 float totalSuitability = thisSuitability * suitabilityForThisAffliction;
310 if (matchingItem !=
null && totalSuitability > bestSuitability)
312 bestItem = matchingItem;
313 afflictionToTreat = affliction;
314 bestSuitability = totalSuitability;
319 if (bestItem !=
null && bestSuitability > cprSuitability)
322 ApplyTreatment(afflictionToTreat, bestItem);
324 treatmentTimer = TreatmentDelay * 4;
333 currentTreatmentSuitabilities, user:
character, predictFutureDuration: 10.0f);
335 if (currentTreatmentSuitabilities.Any(s => s.Value > cprSuitability))
337 itemNameList.Clear();
338 suitableItemIdentifiers.Clear();
339 foreach (KeyValuePair<Identifier, float> treatmentSuitability
in currentTreatmentSuitabilities.OrderByDescending(s => s.Value))
341 if (treatmentSuitability.Value <= cprSuitability) {
continue; }
342 if (ItemPrefab.Prefabs.TryGet(treatmentSuitability.Key, out ItemPrefab itemPrefab))
344 if (
Item.ItemList.None(it => it.Prefab.Identifier == treatmentSuitability.Key)) {
continue; }
345 suitableItemIdentifiers.Add(itemPrefab.Identifier);
347 if (itemNameList.Count < 4)
349 itemNameList.Add(itemPrefab.Name);
353 if (itemNameList.Any())
355 LocalizedString itemListStr =
"";
356 if (itemNameList.Count == 1)
358 itemListStr = itemNameList[0];
360 else if (itemNameList.Count == 2)
363 itemListStr = TextManager.GetWithVariables(
364 "DialogRequiredTreatmentOptionsLast",
365 (
"[treatment1]", itemNameList[0]),
366 (
"[treatment2]", itemNameList[1]));
371 itemListStr = TextManager.GetWithVariables(
372 "DialogRequiredTreatmentOptionsFirst",
373 (
"[treatment1]", itemNameList[0]),
374 (
"[treatment2]", itemNameList[1]));
375 for (
int i = 2; i < itemNameList.Count - 1; i++)
377 itemListStr = TextManager.GetWithVariables(
378 "DialogRequiredTreatmentOptionsFirst",
379 (
"[treatment1]", itemListStr),
380 (
"[treatment2]", itemNameList[i]));
382 itemListStr = TextManager.GetWithVariables(
383 "DialogRequiredTreatmentOptionsLast",
384 (
"[treatment1]", itemListStr),
385 (
"[treatment2]", itemNameList.Last()));
389 character.
Speak(TextManager.GetWithVariables(
"DialogListRequiredTreatments",
392 null, 2.0f, $
"listrequiredtreatments{Target.Name}".ToIdentifier(), 60.0f);
395 var itemsToFind = currentTreatmentSuitabilities
398 .Select(kvp => kvp.Key);
400 RemoveSubObjective(ref getItemObjective);
401 TryAddSubObjective(ref getItemObjective,
404 GetItemPriority = it => currentTreatmentSuitabilities.GetValueOrDefault(it.Prefab.Identifier)
406 onCompleted: () => RemoveSubObjective(ref getItemObjective),
416 else if (cprSuitability <= 0)
432 if (cprSuitability > 0.0f)
447 return FindMedicalItem(inventory, it => it.Prefab.Identifier == itemIdentifier);
452 if (inventory ==
null) {
return null; }
454 Item match = inventory.
FindItem(predicate, recursive:
false);
455 if (match !=
null) {
return match; }
459 foreach (var potentialContainer
in inventory.AllItems.OrderByDescending(it => it.OwnInventory?.Capacity ?? -1))
462 if (match !=
null) {
return match; }
467 private void SpeakCannotTreat()
470 TextManager.Get(
"dialogcannottreatself") :
472 character.
Speak(msg.
Value, identifier:
"cannottreatpatient".ToIdentifier(), minDurationBetweenSimilar: 20.0f);
475 private void ApplyTreatment(Affliction affliction, Item item)
485 string textTag = performedCpr ?
"DialogTargetResuscitated" :
"DialogTargetHealed";
486 string message = TextManager.GetWithVariable(textTag,
"[targetname]",
Target.
Name)?.Value;
487 character.
Speak(message, delay: 1.0f, identifier: $
"targethealed{Target.Name}".ToIdentifier(), minDurationBetweenSimilar: 60.0f);
514 verticalDistance *= 2;
516 float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, horizontalDistance + verticalDistance));
532 goToObjective =
null;
533 getItemObjective =
null;
534 replaceOxygenObjective =
null;
536 ignoreOxygen =
false;
void UnequipContainedItems(Item parentItem, Func< Item, bool > predicate=null, bool avoidDroppingInSea=true, int? unequipMax=null)
void UnequipEmptyItems(Item parentItem, bool avoidDroppingInSea=true)
IEnumerable< Hull > VisibleHulls
Returns hulls that are visible to the character, including the current hull. Note that this is not an...
SteeringManager SteeringManager
float Priority
Final priority value after all calculations.
readonly Character character
SteeringManager SteeringManager
readonly List< AIObjective > subObjectives
readonly AIObjectiveManager objectiveManager
const float EmergencyObjectivePriority
Priority of objectives such as finding safety, rescuing someone in a critical state or defending agai...
static float GetVitalityThreshold(AIObjectiveManager manager, Character character, Character target)
static float GetVitalityFactor(Character character)
override bool KeepDivingGearOn
override void OnDeselected()
override void OnCompleted()
override bool AllowOutsideSubmarine
override void Act(float deltaTime)
override void OnAbandon()
override Identifier Identifier
override bool CheckObjectiveState()
Should return whether the objective is completed or not.
static IEnumerable< Affliction > GetSortedAfflictions(Character character, bool excludeBuffs=true)
override float GetPriority()
static Item FindMedicalItem(Inventory inventory, Func< Item, bool > predicate)
static Item FindMedicalItem(Inventory inventory, Identifier itemIdentifier)
override bool AllowWhileHandcuffed
AIObjectiveRescue(Character character, Character targetCharacter, AIObjectiveManager objectiveManager, float priorityModifier=1)
override bool AllowInAnySub
readonly Character Target
static IEnumerable< Affliction > SortAfflictionsBySeverity(IEnumerable< Affliction > afflictions, bool excludeBuffs=true)
Automatically filters out buffs.
Limb GetAfflictionLimb(Affliction affliction)
const float InsufficientOxygenThreshold
IReadOnlyCollection< Affliction > GetAllAfflictions()
void GetSuitableTreatments(Dictionary< Identifier, float > treatmentSuitability, Character user, Limb limb=null, bool ignoreHiddenAfflictions=false, float predictFutureDuration=0.0f)
Get the identifiers of the items that can be used to treat the character. Takes into account all the ...
void SelectCharacter(Character character)
void Speak(string message, ChatMessageType? messageType=null, float delay=0.0f, Identifier identifier=default, float minDurationBetweenSimilar=0.0f)
CharacterHealth CharacterHealth
bool CanInteractWith(Character c, float maxDist=200.0f, bool checkVisibility=true, bool skipDistanceCheck=false)
virtual AIController AIController
float GetSkillLevel(Identifier skillIdentifier)
Get the character's current skill level, taking into account any temporary boosts from wearables and ...
static readonly List< Character > CharacterList
Character SelectedCharacter
CharacterInventory Inventory
readonly AnimController AnimController
virtual Vector2 WorldPosition
LocalizedString DisplayName
const float HULL_SAFETY_THRESHOLD
static bool HasItem(Character character, Identifier tagOrIdentifier, out IEnumerable< Item > items, Identifier containedTag=default, float conditionPercentage=0, bool requireEquipped=false, bool recursive=true, Func< Item, bool > predicate=null)
Note: uses a single list for matching items. The item is reused each time when the method is called....
static bool IsActive(Character c)
float GetHullSafety(Hull hull, Character character, IEnumerable< Hull > visibleHulls=null)
static bool IsFriendly(Character me, Character other, bool onlySameTeam=false)
Item FindItem(Func< Item, bool > predicate, bool recursive)
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
ItemInventory OwnInventory
bool HasTag(Identifier tag)