Unstable 1.1.14.0
This commit is contained in:
+67
-66
@@ -16,12 +16,13 @@ namespace Barotrauma
|
||||
|
||||
public override bool AllowOutsideSubmarine => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
public override bool AllowWhileHandcuffed => false;
|
||||
|
||||
const float TreatmentDelay = 0.5f;
|
||||
|
||||
const float CloseEnoughToTreat = 100.0f;
|
||||
|
||||
private readonly Character targetCharacter;
|
||||
public readonly Character Target;
|
||||
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private AIObjectiveContainItem replaceOxygenObjective;
|
||||
@@ -44,7 +45,7 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
this.targetCharacter = targetCharacter;
|
||||
Target = targetCharacter;
|
||||
}
|
||||
|
||||
protected override void OnAbandon()
|
||||
@@ -61,55 +62,55 @@ namespace Barotrauma
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (character.LockHands || targetCharacter == null || targetCharacter.Removed || targetCharacter.IsDead)
|
||||
if (Target == null || Target.Removed || Target.IsDead)
|
||||
{
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
var otherRescuer = targetCharacter.SelectedBy;
|
||||
var otherRescuer = Target.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 (Target != character)
|
||||
{
|
||||
if (targetCharacter.IsIncapacitated)
|
||||
if (Target.IsIncapacitated)
|
||||
{
|
||||
// Check if the character needs more oxygen
|
||||
if (!ignoreOxygen && character.SelectedCharacter == targetCharacter || character.CanInteractWith(targetCharacter))
|
||||
if (!ignoreOxygen && character.SelectedCharacter == Target || character.CanInteractWith(Target))
|
||||
{
|
||||
// Replace empty oxygen and welding fuel.
|
||||
if (HumanAIController.HasItem(targetCharacter, AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR, out IEnumerable<Item> suits, requireEquipped: true))
|
||||
if (HumanAIController.HasItem(Target, Tags.HeavyDivingGear, 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"));
|
||||
AIController.UnequipContainedItems(character, suit, it => it.HasTag(Tags.WeldingFuel));
|
||||
}
|
||||
}
|
||||
else if (HumanAIController.HasItem(targetCharacter, AIObjectiveFindDivingGear.LIGHT_DIVING_GEAR, out IEnumerable<Item> masks, requireEquipped: true))
|
||||
else if (HumanAIController.HasItem(Target, Tags.LightDivingGear, 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"));
|
||||
AIController.UnequipContainedItems(character, mask, it => it.HasTag(Tags.WeldingFuel));
|
||||
}
|
||||
}
|
||||
bool ShouldRemoveDivingSuit() => targetCharacter.OxygenAvailable < CharacterHealth.InsufficientOxygenThreshold && targetCharacter.CurrentHull?.LethalPressure <= 0;
|
||||
bool ShouldRemoveDivingSuit() => Target.OxygenAvailable < CharacterHealth.InsufficientOxygenThreshold && Target.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)))
|
||||
else if (suits.Any() && suits.None(s => s.OwnInventory?.AllItems != null && s.OwnInventory.AllItems.Any(it => it.HasTag(Tags.OxygenSource) && 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);
|
||||
Item spareOxygenTank = FindOxygenTank(Target) ?? FindOxygenTank(character);
|
||||
if (spareOxygenTank != null)
|
||||
{
|
||||
Item suit = suits.FirstOrDefault();
|
||||
@@ -133,36 +134,36 @@ namespace Barotrauma
|
||||
|
||||
Item FindOxygenTank(Character c) =>
|
||||
c.Inventory.FindItem(i =>
|
||||
i.HasTag(AIObjectiveFindDivingGear.OXYGEN_SOURCE) &&
|
||||
i.HasTag(Tags.OxygenSource) &&
|
||||
i.ConditionPercentage > 1 &&
|
||||
i.FindParentInventory(inv => inv.Owner is Item otherItem && otherItem.HasTag("diving")) == null,
|
||||
i.FindParentInventory(inv => inv.Owner is Item otherItem && otherItem.HasTag(Tags.DivingGear)) == null,
|
||||
recursive: true);
|
||||
}
|
||||
}
|
||||
if (character.Submarine != null && targetCharacter.CurrentHull != null)
|
||||
if (character.Submarine != null && Target.CurrentHull != null)
|
||||
{
|
||||
if (HumanAIController.GetHullSafety(targetCharacter.CurrentHull, targetCharacter) < HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
if (HumanAIController.GetHullSafety(Target.CurrentHull, Target) < HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
{
|
||||
// Incapacitated target is not in a safe place -> Move to a safe place first
|
||||
if (character.SelectedCharacter != targetCharacter)
|
||||
if (character.SelectedCharacter != Target)
|
||||
{
|
||||
if (HumanAIController.VisibleHulls.Contains(targetCharacter.CurrentHull) && targetCharacter.CurrentHull.DisplayName != null)
|
||||
if (HumanAIController.VisibleHulls.Contains(Target.CurrentHull) && Target.CurrentHull.DisplayName != null)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariables("DialogFoundUnconsciousTarget",
|
||||
("[targetname]", targetCharacter.Name, FormatCapitals.No),
|
||||
("[roomname]", targetCharacter.CurrentHull.DisplayName, FormatCapitals.Yes)).Value,
|
||||
null, 1.0f, $"foundunconscioustarget{targetCharacter.Name}".ToIdentifier(), 60.0f);
|
||||
("[targetname]", Target.Name, FormatCapitals.No),
|
||||
("[roomname]", Target.CurrentHull.DisplayName, FormatCapitals.Yes)).Value,
|
||||
null, 1.0f, $"foundunconscioustarget{Target.Name}".ToIdentifier(), 60.0f);
|
||||
}
|
||||
// Go to the target and select it
|
||||
if (!character.CanInteractWith(targetCharacter))
|
||||
if (!character.CanInteractWith(Target))
|
||||
{
|
||||
RemoveSubObjective(ref replaceOxygenObjective);
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager)
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(Target, character, objectiveManager)
|
||||
{
|
||||
CloseEnough = CloseEnoughToTreat,
|
||||
DialogueIdentifier = "dialogcannotreachpatient".ToIdentifier(),
|
||||
TargetName = targetCharacter.DisplayName
|
||||
TargetName = Target.DisplayName
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () =>
|
||||
@@ -173,7 +174,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
character.SelectCharacter(targetCharacter);
|
||||
character.SelectCharacter(Target);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -213,16 +214,16 @@ namespace Barotrauma
|
||||
|
||||
if (subObjectives.Any()) { return; }
|
||||
|
||||
if (targetCharacter != character && !character.CanInteractWith(targetCharacter))
|
||||
if (Target != character && !character.CanInteractWith(Target))
|
||||
{
|
||||
RemoveSubObjective(ref replaceOxygenObjective);
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
// Go to the target and select it
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager)
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(Target, character, objectiveManager)
|
||||
{
|
||||
CloseEnough = CloseEnoughToTreat,
|
||||
DialogueIdentifier = "dialogcannotreachpatient".ToIdentifier(),
|
||||
TargetName = targetCharacter.DisplayName
|
||||
TargetName = Target.DisplayName
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () =>
|
||||
@@ -234,14 +235,14 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
// We can start applying treatment
|
||||
if (character != targetCharacter && character.SelectedCharacter != targetCharacter)
|
||||
if (character != Target && character.SelectedCharacter != Target)
|
||||
{
|
||||
if (targetCharacter.CurrentHull?.DisplayName != null)
|
||||
if (Target.CurrentHull?.DisplayName != null)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariables("DialogFoundWoundedTarget",
|
||||
("[targetname]", targetCharacter.Name, FormatCapitals.No),
|
||||
("[roomname]", targetCharacter.CurrentHull.DisplayName, FormatCapitals.Yes)).Value,
|
||||
null, 1.0f, $"foundwoundedtarget{targetCharacter.Name}".ToIdentifier(), 60.0f);
|
||||
("[targetname]", Target.Name, FormatCapitals.No),
|
||||
("[roomname]", Target.CurrentHull.DisplayName, FormatCapitals.Yes)).Value,
|
||||
null, 1.0f, $"foundwoundedtarget{Target.Name}".ToIdentifier(), 60.0f);
|
||||
}
|
||||
}
|
||||
GiveTreatment(deltaTime);
|
||||
@@ -253,7 +254,7 @@ namespace Barotrauma
|
||||
private readonly Dictionary<Identifier, float> currentTreatmentSuitabilities = new Dictionary<Identifier, float>();
|
||||
private void GiveTreatment(float deltaTime)
|
||||
{
|
||||
if (targetCharacter == null)
|
||||
if (Target == null)
|
||||
{
|
||||
string errorMsg = $"{character.Name}: Attempted to update a Rescue objective with no target!";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
@@ -263,10 +264,10 @@ namespace Barotrauma
|
||||
|
||||
SteeringManager.Reset();
|
||||
|
||||
if (!targetCharacter.IsPlayer)
|
||||
if (!Target.IsPlayer)
|
||||
{
|
||||
// If the target is a bot, don't let it move
|
||||
targetCharacter.AIController?.SteeringManager?.Reset();
|
||||
Target.AIController?.SteeringManager?.Reset();
|
||||
}
|
||||
if (treatmentTimer > 0.0f)
|
||||
{
|
||||
@@ -275,28 +276,28 @@ namespace Barotrauma
|
||||
}
|
||||
treatmentTimer = TreatmentDelay;
|
||||
|
||||
float cprSuitability = targetCharacter.Oxygen < 0.0f ? -targetCharacter.Oxygen * 100.0f : 0.0f;
|
||||
float cprSuitability = Target.Oxygen < 0.0f ? -Target.Oxygen * 100.0f : 0.0f;
|
||||
|
||||
//find which treatments are the most suitable to treat the character's current condition
|
||||
targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, user: character, normalize: false, predictFutureDuration: 10.0f);
|
||||
Target.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, user: character, normalize: false, predictFutureDuration: 10.0f);
|
||||
|
||||
//check if we already have a suitable treatment for any of the afflictions
|
||||
foreach (Affliction affliction in GetSortedAfflictions(targetCharacter))
|
||||
foreach (Affliction affliction in GetSortedAfflictions(Target))
|
||||
{
|
||||
if (affliction == null) { throw new Exception("Affliction was null"); }
|
||||
if (affliction.Prefab == null) { throw new Exception("Affliction prefab was null"); }
|
||||
float bestSuitability = 0.0f;
|
||||
Item bestItem = null;
|
||||
foreach (KeyValuePair<Identifier, float> treatmentSuitability in affliction.Prefab.TreatmentSuitability)
|
||||
foreach (KeyValuePair<Identifier, float> treatmentSuitability in affliction.Prefab.TreatmentSuitabilities)
|
||||
{
|
||||
if (currentTreatmentSuitabilities.ContainsKey(treatmentSuitability.Key) &&
|
||||
currentTreatmentSuitabilities[treatmentSuitability.Key] > bestSuitability)
|
||||
{
|
||||
Item matchingItem = character.Inventory.FindItemByIdentifier(treatmentSuitability.Key, true);
|
||||
//allow taking items from the target's inventory too if the target is unconscious
|
||||
if (matchingItem == null && targetCharacter.IsIncapacitated)
|
||||
if (matchingItem == null && Target.IsIncapacitated)
|
||||
{
|
||||
matchingItem ??= targetCharacter.Inventory?.FindItemByIdentifier(treatmentSuitability.Key, true);
|
||||
matchingItem ??= Target.Inventory?.FindItemByIdentifier(treatmentSuitability.Key, true);
|
||||
}
|
||||
if (matchingItem != null)
|
||||
{
|
||||
@@ -307,7 +308,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (bestItem != null)
|
||||
{
|
||||
if (targetCharacter != character) { character.SelectCharacter(targetCharacter); }
|
||||
if (Target != character) { character.SelectCharacter(Target); }
|
||||
ApplyTreatment(affliction, bestItem);
|
||||
//wait a bit longer after applying a treatment to wait for potential side-effects to manifest
|
||||
treatmentTimer = TreatmentDelay * 4;
|
||||
@@ -370,12 +371,12 @@ namespace Barotrauma
|
||||
("[treatment1]", itemListStr),
|
||||
("[treatment2]", itemNameList.Last()));
|
||||
}
|
||||
if (targetCharacter != character && character.IsOnPlayerTeam)
|
||||
if (Target != character && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariables("DialogListRequiredTreatments",
|
||||
("[targetname]", targetCharacter.Name, FormatCapitals.No),
|
||||
("[targetname]", Target.Name, FormatCapitals.No),
|
||||
("[treatmentlist]", itemListStr, FormatCapitals.Yes)).Value,
|
||||
null, 2.0f, $"listrequiredtreatments{targetCharacter.Name}".ToIdentifier(), 60.0f);
|
||||
null, 2.0f, $"listrequiredtreatments{Target.Name}".ToIdentifier(), 60.0f);
|
||||
}
|
||||
RemoveSubObjective(ref getItemObjective);
|
||||
TryAddSubObjective(ref getItemObjective,
|
||||
@@ -397,18 +398,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!targetCharacter.IsUnconscious)
|
||||
else if (!Target.IsUnconscious)
|
||||
{
|
||||
Abandon = true;
|
||||
//no suitable treatments found, not inside our own sub (= can't search for more treatments), the target isn't unconscious (= can't give CPR)
|
||||
SpeakCannotTreat();
|
||||
return;
|
||||
}
|
||||
if (character != targetCharacter)
|
||||
if (character != Target)
|
||||
{
|
||||
if (cprSuitability > 0.0f)
|
||||
{
|
||||
character.SelectCharacter(targetCharacter);
|
||||
character.SelectCharacter(Target);
|
||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||
performedCpr = true;
|
||||
}
|
||||
@@ -421,40 +422,40 @@ namespace Barotrauma
|
||||
|
||||
private void SpeakCannotTreat()
|
||||
{
|
||||
LocalizedString msg = character == targetCharacter ?
|
||||
LocalizedString msg = character == Target ?
|
||||
TextManager.Get("dialogcannottreatself") :
|
||||
TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, FormatCapitals.No);
|
||||
TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", Target.DisplayName, FormatCapitals.No);
|
||||
character.Speak(msg.Value, identifier: "cannottreatpatient".ToIdentifier(), minDurationBetweenSimilar: 20.0f);
|
||||
}
|
||||
|
||||
private void ApplyTreatment(Affliction affliction, Item item)
|
||||
{
|
||||
item.ApplyTreatment(character, targetCharacter, targetCharacter.CharacterHealth.GetAfflictionLimb(affliction));
|
||||
item.ApplyTreatment(character, Target, Target.CharacterHealth.GetAfflictionLimb(affliction));
|
||||
}
|
||||
|
||||
protected override bool CheckObjectiveSpecific()
|
||||
{
|
||||
bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter);
|
||||
if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam)
|
||||
bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(Target) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, Target);
|
||||
if (isCompleted && Target != character && character.IsOnPlayerTeam)
|
||||
{
|
||||
string textTag = performedCpr ? "DialogTargetResuscitated" : "DialogTargetHealed";
|
||||
string message = TextManager.GetWithVariable(textTag, "[targetname]", targetCharacter.Name)?.Value;
|
||||
character.Speak(message, delay: 1.0f, identifier: $"targethealed{targetCharacter.Name}".ToIdentifier(), minDurationBetweenSimilar: 60.0f);
|
||||
string message = TextManager.GetWithVariable(textTag, "[targetname]", Target.Name)?.Value;
|
||||
character.Speak(message, delay: 1.0f, identifier: $"targethealed{Target.Name}".ToIdentifier(), minDurationBetweenSimilar: 60.0f);
|
||||
}
|
||||
return isCompleted;
|
||||
}
|
||||
|
||||
protected override float GetPriority()
|
||||
{
|
||||
if (!IsAllowed || targetCharacter == null)
|
||||
if (Target == null) { Abandon = true; }
|
||||
if (!IsAllowed) { HandleNonAllowed(); }
|
||||
if (Abandon)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
return Priority;
|
||||
}
|
||||
if (character.CurrentHull != null)
|
||||
{
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c)))
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == Target.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c)))
|
||||
{
|
||||
// Don't go into rooms that have enemies
|
||||
Priority = 0;
|
||||
@@ -462,18 +463,18 @@ namespace Barotrauma
|
||||
return Priority;
|
||||
}
|
||||
}
|
||||
float horizontalDistance = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X);
|
||||
float verticalDistance = Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y);
|
||||
float horizontalDistance = Math.Abs(character.WorldPosition.X - Target.WorldPosition.X);
|
||||
float verticalDistance = Math.Abs(character.WorldPosition.Y - Target.WorldPosition.Y);
|
||||
if (character.Submarine?.Info is { IsRuin: false })
|
||||
{
|
||||
verticalDistance *= 2;
|
||||
}
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, horizontalDistance + verticalDistance));
|
||||
if (character.CurrentHull != null && targetCharacter.CurrentHull == character.CurrentHull)
|
||||
if (character.CurrentHull != null && Target.CurrentHull == character.CurrentHull)
|
||||
{
|
||||
distanceFactor = 1;
|
||||
}
|
||||
float vitalityFactor = 1 - AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) / 100;
|
||||
float vitalityFactor = 1 - AIObjectiveRescueAll.GetVitalityFactor(Target) / 100;
|
||||
float devotion = CumulatedDevotion / 100;
|
||||
Priority = MathHelper.Lerp(0, AIObjectiveManager.EmergencyObjectivePriority, MathHelper.Clamp(devotion + (vitalityFactor * distanceFactor * PriorityModifier), 0, 1));
|
||||
return Priority;
|
||||
|
||||
Reference in New Issue
Block a user