Client LuaCsForBarotrauma
BackgroundCreature.cs
2 using Microsoft.Xna.Framework;
3 using Microsoft.Xna.Framework.Graphics;
4 using System;
5 using System.Collections.Generic;
6 using System.Xml.Linq;
7 
8 namespace Barotrauma
9 {
10 
12  {
13  const float MaxDepth = 10000.0f;
14 
15  const float CheckWallsInterval = 5.0f;
16 
17  public bool Visible;
18 
20 
21  private readonly List<SpriteDeformation> uniqueSpriteDeformations = new List<SpriteDeformation>();
22  private readonly List<SpriteDeformation> spriteDeformations = new List<SpriteDeformation>();
23  private readonly List<SpriteDeformation> lightSpriteDeformations = new List<SpriteDeformation>();
24 
25  private Vector2 position;
26 
27  private Vector3 velocity;
28 
29  private float depth;
30 
31  private float alpha = 1.0f;
32 
33  private readonly SteeringManager steeringManager;
34 
35  private float checkWallsTimer, flashTimer;
36 
37  private float wanderZPhase;
38  private Vector2 obstacleDiff;
39  private float obstacleDist;
40 
41  public Swarm Swarm;
42 
43  Vector2 drawPosition;
44 
45  public Vector2[,] CurrentSpriteDeformation
46  {
47  get;
48  private set;
49  }
51  {
52  get;
53  private set;
54  }
55 
56  public Vector2 SimPosition
57  {
58  get { return FarseerPhysics.ConvertUnits.ToSimUnits(position); }
59  }
60 
61  public Vector2 WorldPosition
62  {
63  get { return position; }
64  }
65 
66  public Vector2 Velocity
67  {
68  get { return new Vector2(velocity.X, velocity.Y); }
69  }
70 
71  public Vector2 Steering
72  {
73  get;
74  set;
75  }
76 
77  public BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position)
78  {
79  this.Prefab = prefab;
80  this.position = position;
81 
82  drawPosition = position;
83 
84  steeringManager = new SteeringManager(this);
85 
86  velocity = new Vector3(
87  Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly),
88  Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly),
89  Rand.Range(0.0f, prefab.WanderZAmount, Rand.RandSync.ClientOnly));
90 
91  checkWallsTimer = Rand.Range(0.0f, CheckWallsInterval, Rand.RandSync.ClientOnly);
92 
93  foreach (var subElement in prefab.Config.Elements())
94  {
95  List<SpriteDeformation> deformationList = null;
96  switch (subElement.Name.ToString().ToLowerInvariant())
97  {
98  case "deformablesprite":
99  deformationList = spriteDeformations;
100  break;
101  case "deformablelightsprite":
102  deformationList = lightSpriteDeformations;
103  break;
104  default:
105  continue;
106  }
107  foreach (XElement animationElement in subElement.Elements())
108  {
109  SpriteDeformation deformation = null;
110  int sync = animationElement.GetAttributeInt("sync", -1);
111  if (sync > -1)
112  {
113  string typeName = animationElement.GetAttributeString("type", "").ToLowerInvariant();
114  deformation = uniqueSpriteDeformations.Find(d => d.TypeName == typeName && d.Sync == sync);
115  }
116  if (deformation == null)
117  {
118  deformation = SpriteDeformation.Load(animationElement, prefab.Name);
119  if (deformation != null)
120  {
121  uniqueSpriteDeformations.Add(deformation);
122  }
123  }
124  if (deformation != null)
125  {
126  deformationList.Add(deformation);
127  }
128  }
129  }
130  }
131 
132  public void Update(float deltaTime)
133  {
134  position += new Vector2(velocity.X, velocity.Y) * deltaTime;
135  depth = MathHelper.Clamp(depth + velocity.Z * deltaTime, Prefab.MinDepth, Prefab.MaxDepth * 10);
136 
137  if (Prefab.FlashInterval > 0.0f)
138  {
139  flashTimer -= deltaTime;
140  if (flashTimer > 0.0f)
141  {
142  alpha = 0.0f;
143  }
144  else
145  {
146  //value goes from 0 to 1 and back to 0 during the flash
147  alpha = (float)Math.Sin(-flashTimer / Prefab.FlashDuration * MathHelper.Pi) * PerlinNoise.GetPerlin((float)Timing.TotalTime * 0.1f, (float)Timing.TotalTime * 0.2f);
148  if (flashTimer < -Prefab.FlashDuration)
149  {
150  flashTimer = Prefab.FlashInterval;
151  }
152  }
153  }
154 
155  checkWallsTimer -= deltaTime;
156  if (checkWallsTimer <= 0.0f && Level.Loaded != null)
157  {
158  checkWallsTimer = CheckWallsInterval;
159 
160  obstacleDiff = Vector2.Zero;
161  if (position.Y > Level.Loaded.Size.Y)
162  {
163  obstacleDiff = Vector2.UnitY;
164  }
165  else if (position.Y < 0.0f)
166  {
167  obstacleDiff = -Vector2.UnitY;
168  }
169  else if (position.X < 0.0f)
170  {
171  obstacleDiff = -Vector2.UnitX;
172  }
173  else if (position.X > Level.Loaded.Size.X)
174  {
175  obstacleDiff = Vector2.UnitX;
176  }
177  else
178  {
179  var cells = Level.Loaded.GetCells(position, 1);
180  if (cells.Count > 0)
181  {
182  int cellCount = 0;
183  foreach (Voronoi2.VoronoiCell cell in cells)
184  {
185  Vector2 diff = cell.Center - position;
186  if (diff.LengthSquared() > 5000.0f * 5000.0f) { continue; }
187  obstacleDiff += diff;
188  cellCount++;
189  }
190  if (cellCount > 0)
191  {
192  obstacleDiff /= cellCount;
193  obstacleDist = obstacleDiff.Length();
194  obstacleDiff = Vector2.Normalize(obstacleDiff);
195  }
196  }
197  }
198  }
199 
200  if (Swarm != null)
201  {
202  Vector2 midPoint = Swarm.MidPoint();
203  float midPointDist = Vector2.Distance(SimPosition, midPoint) * 100.0f;
204  if (midPointDist > Swarm.MaxDistance)
205  {
206  steeringManager.SteeringSeek(midPoint, ((midPointDist / Swarm.MaxDistance) - 1.0f) * Prefab.Speed);
207  }
208  steeringManager.SteeringManual(deltaTime, Swarm.AvgVelocity() * Swarm.Cohesion);
209  }
210 
211  if (Prefab.WanderAmount > 0.0f)
212  {
213  steeringManager.SteeringWander(Prefab.Speed);
214  }
215 
216  if (obstacleDiff != Vector2.Zero)
217  {
218  steeringManager.SteeringManual(deltaTime, -obstacleDiff * (1.0f - obstacleDist / 5000.0f) * Prefab.Speed);
219  }
220 
221  steeringManager.Update(Prefab.Speed);
222 
223  if (Prefab.WanderZAmount > 0.0f)
224  {
225  wanderZPhase += Rand.Range(-Prefab.WanderZAmount, Prefab.WanderZAmount);
226  velocity.Z = (float)Math.Sin(wanderZPhase) * Prefab.Speed;
227  }
228 
229  velocity = Vector3.Lerp(velocity, new Vector3(Steering.X, Steering.Y, velocity.Z), deltaTime);
230 
231  UpdateDeformations(deltaTime);
232  }
233 
234  public void DrawLightSprite(SpriteBatch spriteBatch, Camera cam)
235  {
236  Draw(spriteBatch, cam, Prefab.LightSprite, Prefab.DeformableLightSprite, CurrentLightSpriteDeformation, Color.White * alpha);
237  }
238 
239  public void Draw(SpriteBatch spriteBatch, Camera cam)
240  {
241  Draw(spriteBatch,
242  cam,
243  Prefab.Sprite,
244  Prefab.DeformableSprite,
246  Color.Lerp(Color.White, Level.Loaded.BackgroundColor, depth / Math.Max(MaxDepth, Prefab.MaxDepth)) * alpha);
247  }
248 
249  private void Draw(SpriteBatch spriteBatch, Camera cam, Sprite sprite, DeformableSprite deformableSprite, Vector2[,] currentSpriteDeformation, Color color)
250  {
251  if (sprite == null && deformableSprite == null) { return; }
252  if (color.A == 0) { return; }
253 
254  float rotation = 0.0f;
255  if (!Prefab.DisableRotation)
256  {
257  rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y));
258  if (velocity.X < 0.0f) { rotation -= MathHelper.Pi; }
259  }
260 
261  drawPosition = GetDrawPosition(cam);
262 
263  float scale = GetScale();
264  sprite?.Draw(spriteBatch,
265  new Vector2(drawPosition.X, -drawPosition.Y),
266  color,
267  rotation,
268  scale,
269  Prefab.DisableFlipping || velocity.X > 0.0f ? SpriteEffects.None : SpriteEffects.FlipHorizontally,
270  Math.Min(depth / MaxDepth, 1.0f));
271 
272  if (deformableSprite != null)
273  {
274  if (currentSpriteDeformation != null)
275  {
276  deformableSprite.Deform(currentSpriteDeformation);
277  }
278  else
279  {
280  deformableSprite.Reset();
281  }
282  deformableSprite?.Draw(cam,
283  new Vector3(drawPosition.X, drawPosition.Y, Math.Min(depth / 10000.0f, 1.0f)),
284  deformableSprite.Origin,
285  rotation,
286  Vector2.One * scale,
287  color,
288  mirror: Prefab.DisableFlipping || velocity.X <= 0.0f);
289  }
290  }
291 
292  public Vector2 GetDrawPosition(Camera cam)
293  {
294  Vector2 drawPosition = WorldPosition;
295  if (depth >= 0)
296  {
297  Vector2 camOffset = drawPosition - cam.WorldViewCenter;
298  drawPosition -= camOffset * depth / MaxDepth;
299  }
300  return drawPosition;
301  }
302 
303  public float GetScale()
304  {
305  return Math.Max(1.0f - depth / MaxDepth, 0.05f) * Prefab.Scale;
306  }
307 
309  {
310  Vector2 min = GetDrawPosition(cam);
311  Vector2 max = min;
312 
313  float scale = GetScale();
314  GetSpriteExtents(Prefab.Sprite, ref min, ref max);
315  GetSpriteExtents(Prefab.LightSprite, ref min, ref max);
316  GetSpriteExtents(Prefab.DeformableSprite?.Sprite, ref min, ref max);
317  GetSpriteExtents(Prefab.DeformableLightSprite?.Sprite, ref min, ref max);
318 
319  return new Rectangle(min.ToPoint(), (max - min).ToPoint());
320 
321  void GetSpriteExtents(Sprite sprite, ref Vector2 min, ref Vector2 max)
322  {
323  if (sprite == null) { return; }
324  min.X = Math.Min(min.X, min.X - sprite.size.X * sprite.RelativeOrigin.X * scale);
325  min.Y = Math.Min(min.Y, min.Y - sprite.size.Y * sprite.RelativeOrigin.Y * scale);
326  max.X = Math.Max(max.X, max.X + sprite.size.X * (1.0f - sprite.RelativeOrigin.X) * scale);
327  max.Y = Math.Max(max.Y, max.Y + sprite.size.Y * (1.0f - sprite.RelativeOrigin.Y) * scale);
328  }
329  }
330 
331  private void UpdateDeformations(float deltaTime)
332  {
333  foreach (SpriteDeformation deformation in uniqueSpriteDeformations)
334  {
335  deformation.Update(deltaTime);
336  }
337  if (spriteDeformations.Count > 0)
338  {
340  }
341  if (lightSpriteDeformations.Count > 0)
342  {
343  CurrentLightSpriteDeformation = SpriteDeformation.GetDeformation(lightSpriteDeformations, Prefab.DeformableLightSprite.Size);
344  }
345  }
346  }
347 
348  class Swarm
349  {
350  public List<BackgroundCreature> Members;
351 
352  public readonly float MaxDistance;
353  public readonly float Cohesion;
354 
355  public Vector2 MidPoint()
356  {
357  if (Members.Count == 0) return Vector2.Zero;
358 
359  Vector2 midPoint = Vector2.Zero;
360 
361  foreach (BackgroundCreature member in Members)
362  {
363  midPoint += member.SimPosition;
364  }
365 
366  midPoint /= Members.Count;
367 
368  return midPoint;
369  }
370 
371  public Vector2 AvgVelocity()
372  {
373  if (Members.Count == 0) return Vector2.Zero;
374 
375  Vector2 avgVel = Vector2.Zero;
376  foreach (BackgroundCreature member in Members)
377  {
378  avgVel += member.Velocity;
379  }
380  avgVel /= Members.Count;
381  return avgVel;
382  }
383 
384  public Swarm(List<BackgroundCreature> members, float maxDistance, float cohesion)
385  {
386  Members = members;
387  MaxDistance = maxDistance;
388  Cohesion = cohesion;
389  foreach (BackgroundCreature bgSprite in members)
390  {
391  bgSprite.Swarm = this;
392  }
393  }
394  }
395 }
Vector2 GetDrawPosition(Camera cam)
void Draw(SpriteBatch spriteBatch, Camera cam)
void DrawLightSprite(SpriteBatch spriteBatch, Camera cam)
Rectangle GetExtents(Camera cam)
BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position)
readonly BackgroundCreaturePrefab Prefab
readonly DeformableSprite DeformableSprite
Vector2 WorldViewCenter
Definition: Camera.cs:126
void Deform(Func< Vector2, Vector2 > deformFunction)
Deform the vertices of the sprite using an arbitrary function. The in-parameter of the function is th...
void Draw(Camera cam, Vector3 pos, Vector2 origin, float rotate, Vector2 scale, Color color, bool mirror=false, bool invert=false)
List< VoronoiCell > GetCells(Vector2 worldPos, int searchDepth=2)
static SpriteDeformation Load(string deformationType, string parentDebugName)
abstract void Update(float deltaTime)
abstract void GetDeformation(out Vector2[,] deformation, out float multiplier, bool inverse)
void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect=SpriteEffects.None)
readonly float Cohesion
List< BackgroundCreature > Members
readonly float MaxDistance
Swarm(List< BackgroundCreature > members, float maxDistance, float cohesion)