Client LuaCsForBarotrauma
AIObjectiveExtinguishFire.cs
2 using Microsoft.Xna.Framework;
3 using System;
4 using System.Linq;
6 
7 namespace Barotrauma
8 {
10  {
11  public override Identifier Identifier { get; set; } = "extinguish fire".ToIdentifier();
12  public override bool ForceRun => true;
13  protected override bool ConcurrentObjectives => true;
14  public override bool KeepDivingGearOn => true;
15  protected override bool AllowInAnySub => true;
16  protected override bool AllowWhileHandcuffed => false;
17 
18  private readonly Hull targetHull;
19 
20  private AIObjectiveGetItem getExtinguisherObjective;
21  private AIObjectiveGoTo gotoObjective;
22 
23  public AIObjectiveExtinguishFire(Character character, Hull targetHull, AIObjectiveManager objectiveManager, float priorityModifier = 1)
24  : base(character, objectiveManager, priorityModifier)
25  {
26  this.targetHull = targetHull;
27  }
28 
29  protected override float GetPriority()
30  {
31  if (!IsAllowed)
32  {
34  return Priority;
35  }
36  bool isOrder = objectiveManager.HasOrder<AIObjectiveExtinguishFires>();
37  if (!isOrder && Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
38  {
39  // Don't go into rooms with any enemies, unless it's an order
40  Priority = 0;
41  Abandon = true;
42  }
43  else
44  {
45  float characterY = character.CurrentHull?.WorldPosition.Y ?? character.WorldPosition.Y;
46 
47  float distanceFactor = 1.0f;
48  if (targetHull != character.CurrentHull &&
49  !HumanAIController.VisibleHulls.Contains(targetHull))
50  {
51  distanceFactor =
53  new Vector2(character.WorldPosition.Y, characterY),
54  targetHull.WorldPosition,
55  verticalDistanceMultiplier: 3,
56  maxDistance: 5000,
57  factorAtMaxDistance: 0.1f);
58  }
59  float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull);
60  if (severity > 0.75f && !isOrder &&
61  targetHull.RoomName != null &&
62  !targetHull.RoomName.Contains("reactor", StringComparison.OrdinalIgnoreCase) &&
63  !targetHull.RoomName.Contains("engine", StringComparison.OrdinalIgnoreCase) &&
64  !targetHull.RoomName.Contains("command", StringComparison.OrdinalIgnoreCase))
65  {
66  // Ignore severe fires to prevent casualities unless ordered to extinguish.
67  Priority = 0;
68  Abandon = true;
69  }
70  else
71  {
72  float devotion = CumulatedDevotion / 100;
73  Priority = MathHelper.Lerp(0, AIObjectiveManager.MaxObjectivePriority, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1));
74  }
75  }
76  return Priority;
77  }
78 
79  protected override bool CheckObjectiveSpecific() => targetHull.FireSources.None();
80 
81  private float sinTime;
82  protected override void Act(float deltaTime)
83  {
84  var extinguisherItem = character.Inventory.FindItemByTag("fireextinguisher".ToIdentifier());
85  if (extinguisherItem == null || extinguisherItem.Condition <= 0.0f || !character.HasEquippedItem(extinguisherItem))
86  {
87  TryAddSubObjective(ref getExtinguisherObjective, () =>
88  {
89  if (character.IsOnPlayerTeam && !character.HasEquippedItem("fireextinguisher".ToIdentifier(), allowBroken: false))
90  {
91  character.Speak(TextManager.Get("DialogFindExtinguisher").Value, null, 2.0f, "findextinguisher".ToIdentifier(), 30.0f);
92  }
93  var getItemObjective = new AIObjectiveGetItem(character, "fireextinguisher".ToIdentifier(), objectiveManager, equip: true)
94  {
95  AllowStealing = true,
96  // If the item is inside an unsafe hull, decrease the priority
97  GetItemPriority = i => HumanAIController.UnsafeHulls.Contains(i.CurrentHull) ? 0.1f : 1
98  };
100  {
101  getItemObjective.Abandoned += () => character.Speak(TextManager.Get("dialogcannotfindfireextinguisher").Value, null, 0.0f, "dialogcannotfindfireextinguisher".ToIdentifier(), 10.0f);
102  };
103  return getItemObjective;
104  });
105  }
106  else
107  {
108  var extinguisher = extinguisherItem.GetComponent<RepairTool>();
109  if (extinguisher == null)
110  {
111 #if DEBUG
112  DebugConsole.ThrowError($"{character.Name}: AIObjectiveExtinguishFire failed - the item \"" + extinguisherItem + "\" has no RepairTool component but is tagged as an extinguisher");
113 #endif
114  Abandon = true;
115  return;
116  }
117  foreach (FireSource fs in targetHull.FireSources)
118  {
119  if (fs == null) { continue; }
120  if (fs.Removed) { continue; }
121  if (character.CurrentHull == null)
122  {
123  Abandon = true;
124  break;
125  }
126  float xDist = Math.Abs(character.WorldPosition.X - fs.WorldPosition.X);
127  float yDist = Math.Abs(character.CurrentHull.WorldPosition.Y - targetHull.WorldPosition.Y);
128  float dist = xDist + yDist;
129  bool inRange = dist < extinguisher.Range;
130  bool isInDamageRange = fs.IsInDamageRange(character, fs.DamageRange) && character.CanSeeTarget(targetHull);
131  bool moveCloser = !isInDamageRange && (!inRange || !character.CanSeeTarget(targetHull));
132  bool operateExtinguisher = !moveCloser || (dist < extinguisher.Range * 1.2f && character.CanSeeTarget(targetHull));
133  if (operateExtinguisher)
134  {
136  Vector2 fromCharacterToFireSource = fs.WorldPosition - character.WorldPosition;
137  character.CursorPosition += VectorExtensions.Forward(extinguisherItem.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, fromCharacterToFireSource.Length() / 2);
138  if (extinguisherItem.RequireAimToUse)
139  {
140  character.SetInput(InputType.Aim, false, true);
141  sinTime += deltaTime * 10;
142  }
143  character.SetInput(extinguisherItem.IsShootable ? InputType.Shoot : InputType.Use, false, true);
144  extinguisher.Use(deltaTime, character);
145  if (!targetHull.FireSources.Contains(fs))
146  {
147  character.Speak(TextManager.GetWithVariable("DialogPutOutFire", "[roomname]", targetHull.DisplayName, FormatCapitals.Yes).Value, null, 0, "putoutfire".ToIdentifier(), 10.0f);
148  }
149  // Prevents running into the flames.
151  }
152  if (moveCloser)
153  {
154  if (TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager, closeEnough: extinguisher.Range * 0.8f)
155  {
156  DialogueIdentifier = "dialogcannotreachfire".ToIdentifier(),
157  TargetName = fs.Hull.DisplayName,
158  },
159  onAbandon: () => Abandon = true,
160  onCompleted: () => RemoveSubObjective(ref gotoObjective)))
161  {
162  gotoObjective.requiredCondition = () => character.CanSeeTarget(targetHull);
163  }
164  }
165  else if (!operateExtinguisher || isInDamageRange)
166  {
167  // Don't walk into the flames.
168  RemoveSubObjective(ref gotoObjective);
170  }
171  // Only target one fire source at the time.
172  break;
173  }
174  }
175  }
176 
177  public override void Reset()
178  {
179  base.Reset();
180  getExtinguisherObjective = null;
181  gotoObjective = null;
182  sinTime = 0;
184  }
185 
186  protected override void OnCompleted()
187  {
188  base.OnCompleted();
190  }
191 
192  protected override void OnAbandon()
193  {
194  base.OnAbandon();
196  }
197  }
198 }
IEnumerable< Hull > VisibleHulls
Returns hulls that are visible to the character, including the current hull. Note that this is not an...
override bool CheckObjectiveSpecific()
Should return whether the objective is completed or not.
AIObjectiveExtinguishFire(Character character, Hull targetHull, AIObjectiveManager objectiveManager, float priorityModifier=1)
static float GetFireSeverity(Hull hull)
0-1 based on the horizontal size of all of the fires in the hull.
float Priority
Final priority value after all calculations.
static float GetDistanceFactor(Vector2 selfPos, Vector2 targetWorldPos, float factorAtMaxDistance, float verticalDistanceMultiplier=3, float maxDistance=10000.0f, float factorAtMinDistance=1.0f)
Get a normalized value representing how close the target position is. The value is a rough estimation...
AIObjective CurrentObjective
Includes orders.
const float MaxObjectivePriority
Highest possible priority for any objective. Used in certain cases where the character needs to react...
void SetInput(InputType inputType, bool hit, bool held)
void Speak(string message, ChatMessageType? messageType=null, float delay=0.0f, Identifier identifier=default, float minDurationBetweenSimilar=0.0f)
bool HasEquippedItem(Item item, InvSlotType? slotType=null, Func< InvSlotType, bool > predicate=null)
bool CanSeeTarget(ISpatialEntity target, ISpatialEntity seeingEntity=null, bool seeThroughWindows=false, bool checkFacing=false)
virtual Vector2 WorldPosition
Definition: Entity.cs:49
bool IsInDamageRange(Character c, float damageRange)
static bool IsActive(Character c)
static bool IsFriendly(Character me, Character other, bool onlySameTeam=false)
Item FindItemByTag(Identifier tag, bool recursive=false)