Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Characters/AI/AITarget.cs
1 using Microsoft.Xna.Framework;
2 using System;
3 using System.Collections.Generic;
4 using System.Xml.Linq;
5 
6 namespace Barotrauma
7 {
8  partial class AITarget
9  {
10  public static List<AITarget> List = new List<AITarget>();
11 
12  private Entity entity;
13  public Entity Entity
14  {
15  get
16  {
17  if (entity != null && entity.Removed) { return null; }
18  return entity;
19  }
20  }
21 
22  private float soundRange;
23  private float sightRange;
24 
28  public float FadeOutTime { get; private set; } = 2;
29 
30  public bool Static { get; private set; }
31  public bool StaticSound { get; private set; }
32  public bool StaticSight { get; private set; }
33 
34  public float SoundRange
35  {
36  get { return soundRange; }
37  set
38  {
39  if (float.IsNaN(value))
40  {
41  DebugConsole.ThrowError("Attempted to set the SoundRange of an AITarget to NaN.\n" + Environment.StackTrace.CleanupStackTrace());
42  return;
43  }
44  soundRange = MathHelper.Clamp(value, MinSoundRange, MaxSoundRange);
45  if (soundRange > 0.0f && !Static && FadeOutTime > 0.0f)
46  {
47  NeedsUpdate = true;
48  }
49  }
50  }
51 
52  public float SightRange
53  {
54  get { return sightRange; }
55  set
56  {
57  if (float.IsNaN(value))
58  {
59  DebugConsole.ThrowError("Attempted to set the SightRange of an AITarget to NaN.\n" + Environment.StackTrace.CleanupStackTrace());
60  return;
61  }
62  sightRange = MathHelper.Clamp(value, MinSightRange, MaxSightRange);
63  if (sightRange > 0 && !Static && FadeOutTime > 0.0f)
64  {
65  NeedsUpdate = true;
66  }
67  }
68  }
69 
70  private float sectorRad = MathHelper.TwoPi;
71  public float SectorDegrees
72  {
73  get { return MathHelper.ToDegrees(sectorRad); }
74  set { sectorRad = MathHelper.ToRadians(value); }
75  }
76 
77  private Vector2 sectorDir;
78  public Vector2 SectorDir
79  {
80  get { return sectorDir; }
81  set
82  {
83  if (!MathUtils.IsValid(value))
84  {
85  string errorMsg = "Invalid AITarget sector direction (" + value + ")\n" + Environment.StackTrace.CleanupStackTrace();
86  DebugConsole.ThrowError(errorMsg);
87  GameAnalyticsManager.AddErrorEventOnce("AITarget.SectorDir:" + entity?.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
88  return;
89  }
90  sectorDir = value;
91  }
92  }
93 
94  public float SonarDisruption
95  {
96  get;
97  set;
98  }
99 
101  public Identifier SonarIconIdentifier;
102 
103  private bool inDetectable;
104 
105  public double InDetectableSetTime;
106 
110  public bool InDetectable
111  {
112  get
113  {
114  return inDetectable || (SoundRange <= 0 && SightRange <= 0);
115  }
116  set
117  {
118  inDetectable = value;
119  if (inDetectable)
120  {
121  InDetectableSetTime = Timing.TotalTime;
122  NeedsUpdate = true;
123  }
124  }
125  }
126 
127 
128  public float MinSoundRange, MinSightRange;
129  public float MaxSoundRange = 100000, MaxSightRange = 100000;
130 
134  public bool NeedsUpdate
135  {
136  get;
137  private set;
138  } = true;
139 
140  public TargetType Type { get; private set; }
141 
142  public enum TargetType
143  {
144  Any,
145  HumanOnly,
146  EnemyOnly
147  }
148 
149  public Vector2 WorldPosition
150  {
151  get
152  {
153  if (entity == null || entity.Removed)
154  {
155 #if DEBUG
156  DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
157 #endif
158  GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
159  GameAnalyticsManager.ErrorSeverity.Error,
160  "Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
161  return Vector2.Zero;
162  }
163 
164  return entity.WorldPosition;
165  }
166  }
167 
168  public Vector2 SimPosition
169  {
170  get
171  {
172  if (entity == null || entity.Removed)
173  {
174 #if DEBUG
175  DebugConsole.ThrowError("Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
176 #endif
177  GameAnalyticsManager.AddErrorEventOnce("AITarget.WorldPosition:EntityRemoved",
178  GameAnalyticsManager.ErrorSeverity.Error,
179  "Attempted to access a removed AITarget\n" + Environment.StackTrace.CleanupStackTrace());
180  return Vector2.Zero;
181  }
182 
183  return entity.SimPosition;
184  }
185  }
186 
190  public bool ShouldBeIgnored()
191  {
192  if (InDetectable) { return true; }
193  if (Entity == null) { return true; }
194  if (Level.Loaded != null && WorldPosition.Y > Level.Loaded.Size.Y)
195  {
196  return true;
197  }
198  return false;
199  }
200 
201  public AITarget(Entity e, XElement element) : this(e)
202  {
203  SightRange = element.GetAttributeFloat("sightrange", 0.0f);
204  SoundRange = element.GetAttributeFloat("soundrange", 0.0f);
205  MinSightRange = element.GetAttributeFloat("minsightrange", 0f);
206  MinSoundRange = element.GetAttributeFloat("minsoundrange", 0f);
207  MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
208  MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
209  FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
210  Static = element.GetAttributeBool("static", Static);
211  StaticSight = element.GetAttributeBool("staticsight", StaticSight);
212  StaticSound = element.GetAttributeBool("staticsound", StaticSound);
213  if (Static)
214  {
215  StaticSound = true;
216  StaticSight = true;
217  }
218  SonarDisruption = element.GetAttributeFloat("sonardisruption", 0.0f);
219  string label = element.GetAttributeString("sonarlabel", "");
220  SonarLabel = TextManager.Get(label).Fallback(label);
221  SonarIconIdentifier = element.GetAttributeIdentifier("sonaricon", Identifier.Empty);
222  Type = element.GetAttributeEnum("type", TargetType.Any);
223  Reset();
224  }
225 
226  public AITarget(Entity e)
227  {
228  entity = e;
229  List.Add(this);
230  }
231 
232  public void Update(float deltaTime)
233  {
234  InDetectable = false;
235  if (!Static && FadeOutTime > 0)
236  {
237  // The aitarget goes silent/invisible if the components don't keep it active
238  if (!StaticSight && sightRange > 0)
239  {
240  DecreaseSightRange(deltaTime);
241  }
242  if (!StaticSound && soundRange > 0)
243  {
244  DecreaseSoundRange(deltaTime);
245  }
246  if (sightRange <= 0 && soundRange <= 0)
247  {
248  NeedsUpdate = false;
249  }
250  }
251  else
252  {
253  NeedsUpdate = false;
254  }
255  }
256 
257  public void IncreaseSoundRange(float deltaTime, float speed = 1)
258  {
259  SoundRange += speed * deltaTime * (MaxSoundRange / FadeOutTime);
260  }
261 
262  public void IncreaseSightRange(float deltaTime, float speed = 1)
263  {
264  SightRange += speed * deltaTime * (MaxSightRange / FadeOutTime);
265  }
266 
267  public void DecreaseSoundRange(float deltaTime, float speed = 1)
268  {
269  SoundRange -= speed * deltaTime * (MaxSoundRange / FadeOutTime);
270  }
271 
272  public void DecreaseSightRange(float deltaTime, float speed = 1)
273  {
274  SightRange -= speed * deltaTime * (MaxSightRange / FadeOutTime);
275  }
276 
277  public bool HasSector()
278  {
279  return sectorRad < MathHelper.TwoPi;
280  }
281 
282  public bool IsWithinSector(Vector2 worldPosition)
283  {
284  if (!HasSector()) { return true; }
285  Vector2 diff = worldPosition - WorldPosition;
286  return Math.Abs(MathUtils.GetShortestAngle(MathUtils.VectorToAngle(diff), MathUtils.VectorToAngle(sectorDir))) <= sectorRad * 0.5f;
287  }
288 
289  public void Remove()
290  {
291  List.Remove(this);
292  entity = null;
293  }
294 
295  public void Reset()
296  {
297  if (Static)
298  {
299  SightRange = MaxSightRange;
301  }
302  else
303  {
304  // Non-static ai targets must be kept alive by a custom logic (e.g. item components)
305  SightRange = StaticSight ? MaxSightRange : MinSightRange;
307  }
308  }
309  }
310 }
void DecreaseSoundRange(float deltaTime, float speed=1)
bool NeedsUpdate
Does the AI target do something that requires Update() to be called (e.g. static targets don't need t...
float FadeOutTime
How long does it take for the ai target to fade out if not kept alive.
void IncreaseSoundRange(float deltaTime, float speed=1)
void IncreaseSightRange(float deltaTime, float speed=1)
bool ShouldBeIgnored()
Is some condition met (e.g. entity null, indetectable, outside level) that prevents anyone from detec...
void DecreaseSightRange(float deltaTime, float speed=1)
bool InDetectable
Should be reset to false each frame and kept indetectable by e.g. a status effect.
virtual Vector2 WorldPosition
Definition: Entity.cs:49
virtual Vector2 SimPosition
Definition: Entity.cs:45
LocalizedString Fallback(LocalizedString fallback, bool useDefaultLanguageIfFound=true)
Use this text instead if the original text cannot be found.