(6401d5d4b) The medic should now drag only unconcious targets into safety before applying treatment. Increase the VitalityThreshold and reduce the distance factor. Add a couple new dialogs.

This commit is contained in:
Joonas Rikkonen
2019-05-16 05:48:59 +03:00
parent 44d78a1466
commit 351e7bbd71
13 changed files with 375 additions and 76 deletions
@@ -200,6 +200,31 @@ namespace Barotrauma
{
#if DEBUG
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
#endif
abandon = true;
return;
}
Vector2 gapDiff = Leak.WorldPosition - character.WorldPosition;
// TODO: use the collider size/reach?
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
{
HumanAIController.AnimController.Crouching = true;
}
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
bool canOperate = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach;
if (canOperate)
{
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak));
}
else
{
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach * 0.75f });
}
var repairTool = weldingTool.GetComponent<RepairTool>();
if (repairTool == null)
{
#if DEBUG
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
#endif
abandon = true;
return;
@@ -74,6 +74,21 @@ namespace Barotrauma
}
}
public override void Update(float deltaTime)
{
if (objectiveManager.CurrentObjective == this)
{
if (randomTimer > 0)
{
randomTimer -= deltaTime;
}
else
{
SetRandom();
}
}
}
public override bool IsCompleted() => false;
public override bool CanBeCompleted => true;
@@ -102,6 +102,10 @@ namespace Barotrauma
{
isCompleted = true;
}
if (component.AIOperate(deltaTime, character, this))
{
isCompleted = true;
}
}
else
{
@@ -20,8 +20,6 @@ namespace Barotrauma
private float treatmentTimer;
public override bool CanBeCompleted => base.CanBeCompleted && AIObjectiveRescueAll.IsValidTarget(targetCharacter, character);
public AIObjectiveRescue(Character character, Character targetCharacter, AIObjectiveManager objectiveManager, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier)
{
@@ -33,14 +31,19 @@ namespace Barotrauma
AIObjectiveRescue rescueObjective = otherObjective as AIObjectiveRescue;
return rescueObjective != null && rescueObjective.targetCharacter == targetCharacter;
}
protected override void Act(float deltaTime)
{
// Target is not in a safe place -> Move to a safe place first
if (HumanAIController.GetHullSafety(targetCharacter.CurrentHull, targetCharacter) < HumanAIController.HULL_SAFETY_THRESHOLD)
// Unconcious target is not in a safe place -> Move to a safe place first
if (targetCharacter.IsUnconscious && HumanAIController.GetHullSafety(targetCharacter.CurrentHull, targetCharacter) < HumanAIController.HULL_SAFETY_THRESHOLD)
{
if (character.SelectedCharacter != targetCharacter)
{
character.Speak(TextManager.Get("DialogFoundUnconsciousTarget")
.Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.DisplayName),
null, 1.0f,
"foundunconscioustarget" + targetCharacter.Name, 60.0f);
// Go to the target and select it
if (!character.CanInteractWith(targetCharacter))
{
@@ -68,28 +71,30 @@ namespace Barotrauma
// Ensure that we have the find safety objective (should always be the case)
objectiveManager.AddObjective(new AIObjectiveFindSafety(character, objectiveManager));
}
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(findSafety.FindBestHull(), character, objectiveManager));
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(findSafety.FindBestHull(new Hull[] { character.CurrentHull }), character, objectiveManager));
}
return;
}
if (subObjectives.Any()) { return; }
// We can start applying treatment
if (!character.CanInteractWith(targetCharacter))
{
// Go to the target and select it
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager));
}
else
{
if (character.SelectedCharacter == null)
// We can start applying treatment
if (character.SelectedCharacter != targetCharacter)
{
character?.Speak(TextManager.Get("DialogFoundUnconsciousTarget")
character.Speak(TextManager.Get("DialogFoundWoundedTarget")
.Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.DisplayName),
null, 1.0f,
"foundunconscioustarget" + targetCharacter.Name, 60.0f);
"foundwoundedtarget" + targetCharacter.Name, 60.0f);
character.SelectCharacter(targetCharacter);
}
character.SelectCharacter(targetCharacter);
GiveTreatment(deltaTime);
}
}
@@ -160,7 +165,7 @@ namespace Barotrauma
{
itemListStr = string.Join(" or ", string.Join(", ", itemNameList.Take(itemNameList.Count - 1)), itemNameList.Last());
}
character?.Speak(TextManager.Get("DialogListRequiredTreatments")
character.Speak(TextManager.Get("DialogListRequiredTreatments")
.Replace("[targetname]", targetCharacter.Name)
.Replace("[treatmentlist]", itemListStr),
null, 2.0f, "listrequiredtreatments" + targetCharacter.Name, 60.0f);
@@ -199,7 +204,7 @@ namespace Barotrauma
bool isCompleted = targetCharacter.Vitality / targetCharacter.MaxVitality > AIObjectiveRescueAll.VitalityThreshold;
if (isCompleted)
{
character?.Speak(TextManager.Get("DialogTargetHealed").Replace("[targetname]", targetCharacter.Name),
character.Speak(TextManager.Get("DialogTargetHealed").Replace("[targetname]", targetCharacter.Name),
null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f);
}
return isCompleted || targetCharacter.Removed || targetCharacter.IsDead;
@@ -207,12 +212,21 @@ namespace Barotrauma
public override float GetPriority()
{
if (targetCharacter.CurrentHull == null || targetCharacter.IsDead) { return 0; ; }
if (targetCharacter == null) { return 0; }
if (targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead)
{
abandon = true;
return 0;
}
// Don't go into rooms that have enemies
if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(c))) { return 0; }
if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(c)))
{
abandon = true;
return 0;
}
// Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f;
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 10000, dist));
float distanceFactor = MathHelper.Lerp(1, 0.5f, MathUtils.InverseLerp(0, 10000, dist));
float vitalityFactor = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter);
float devotion = Math.Max(Priority, 10) / 100;
return MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + vitalityFactor * distanceFactor, 0, 1));
@@ -7,9 +7,10 @@ namespace Barotrauma
class AIObjectiveRescueAll : AIObjectiveLoop<Character>
{
public override string DebugTag => "rescue all";
public override bool ForceRun => true;
//only treat characters whose vitality is below this (0.8 = 80% of max vitality)
public const float VitalityThreshold = 0.8f;
//only treat characters whose vitality is below this (0.9 = 90% of max vitality)
public const float VitalityThreshold = 0.9f;
public AIObjectiveRescueAll(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier) { }
@@ -829,6 +829,25 @@ namespace Barotrauma.Items.Components
}
}
if (targetItem.Prefab.DeconstructItems.Any())
{
inputContainer.Inventory.RemoveItem(targetItem);
Entity.Spawner.AddToRemoveQueue(targetItem);
MoveInputQueue();
PutItemsToLinkedContainer();
}
else
{
if (outputContainer.Inventory.Items.All(i => i != null))
{
targetItem.Drop(dropper: null);
}
else
{
outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true);
}
}
if (targetItem.Prefab.DeconstructItems.Any())
{
inputContainer.Inventory.RemoveItem(targetItem);
@@ -212,33 +212,6 @@ namespace Barotrauma.Items.Components
}
}
public Vector2? PosToMaintain
{
get { return posToMaintain; }
set { posToMaintain = value; }
}
struct ObstacleDebugInfo
{
public Vector2 Point1;
public Vector2 Point2;
public Vector2? Intersection;
public float Dot;
public Vector2 AvoidStrength;
public ObstacleDebugInfo(GraphEdge edge, Vector2? intersection, float dot, Vector2 avoidStrength)
{
Point1 = edge.Point1;
Point2 = edge.Point2;
Intersection = intersection;
Dot = dot;
AvoidStrength = avoidStrength;
}
}
//edge point 1, edge point 2, avoid strength
private List<ObstacleDebugInfo> debugDrawObstacles = new List<ObstacleDebugInfo>();
@@ -1688,6 +1688,10 @@ namespace Barotrauma
{
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
}
if (!broken)
{
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
}
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; }
@@ -455,6 +455,25 @@ namespace Barotrauma
}
}
public string DisplayName
{
get;
private set;
}
private string roomName;
[Editable, Serialize("", true, translationTextTag: "RoomName.")]
public string RoomName
{
get { return roomName; }
set
{
if (roomName == value) { return; }
roomName = value;
DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName;
}
}
public override Rectangle Rect
{
get