Client LuaCsForBarotrauma
ParticleManager.cs
1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 
7 namespace Barotrauma.Particles
8 {
10  {
11  AlphaBlend, Additive//, Distortion
12  }
13 
15  {
16  Default,
17  Foreground,
19  }
20 
22  {
23  private const int MaxOutOfViewDist = 500;
24 
25  private int particleCount;
26  public int ParticleCount
27  {
28  get { return particleCount; }
29  }
30 
31  private int maxParticles;
32  public int MaxParticles
33  {
34  get { return maxParticles; }
35  set
36  {
37  if (maxParticles == value || value < 4) { return; }
38 
39  Particle[] newParticles = new Particle[value];
40  for (int i = 0; i < Math.Min(maxParticles, value); i++)
41  {
42  newParticles[i] = particles[i];
43  }
44 
45  particleCount = Math.Min(particleCount, value);
46  particles = newParticles;
47  maxParticles = value;
48 
49  var oldParticlesInCreationOrder = particlesInCreationOrder.ToList();
50  particlesInCreationOrder.Clear();
51  foreach (var particle in oldParticlesInCreationOrder)
52  {
53  if (particles.Contains(particle))
54  {
55  particlesInCreationOrder.AddLast(particle);
56  }
57  }
58  }
59  }
60  private Particle[] particles;
61 
67  private readonly LinkedList<Particle> particlesInCreationOrder = new LinkedList<Particle>();
68 
69  private Camera cam;
70 
71  public Camera Camera
72  {
73  get { return cam; }
74  set { cam = value; }
75  }
76 
77  public ParticleManager(Camera cam)
78  {
79  this.cam = cam;
80 
81  MaxParticles = GameSettings.CurrentConfig.Graphics.ParticleLimit;
82  }
83 
84  public Particle CreateParticle(string prefabName, Vector2 position, float angle, float speed, Hull hullGuess = null, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
85  {
86  return CreateParticle(prefabName, position, new Vector2((float)Math.Cos(angle), (float)-Math.Sin(angle)) * speed, angle, hullGuess, collisionIgnoreTimer, tracerPoints: tracerPoints);
87  }
88 
89  public Particle CreateParticle(string prefabName, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
90  {
91  ParticlePrefab prefab = FindPrefab(prefabName);
92 
93  if (prefab == null)
94  {
95  DebugConsole.ThrowError("Particle prefab \"" + prefabName + "\" not found!");
96  return null;
97  }
98  return CreateParticle(prefab, position, velocity, rotation, hullGuess, collisionIgnoreTimer: collisionIgnoreTimer, tracerPoints:tracerPoints);
99  }
100 
101  public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, ParticleDrawOrder drawOrder = ParticleDrawOrder.Default, float collisionIgnoreTimer = 0f, float lifeTimeMultiplier = 1f, Tuple<Vector2, Vector2> tracerPoints = null)
102  {
103  if (prefab == null || prefab.Sprites.Count == 0) { return null; }
104  if (particleCount >= MaxParticles)
105  {
106  for (int i = 0; i < particleCount; i++)
107  {
108  if (particles[i].Prefab.Priority < prefab.Priority ||
109  (!particles[i].Prefab.DrawAlways && prefab.DrawAlways))
110  {
111  RemoveParticle(i);
112  break;
113  }
114  }
115  if (particleCount >= MaxParticles) { return null; }
116  }
117 
118  Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
119 
120  Vector2 minPos = new Vector2(Math.Min(position.X, particleEndPos.X), Math.Min(position.Y, particleEndPos.Y));
121  Vector2 maxPos = new Vector2(Math.Max(position.X, particleEndPos.X), Math.Max(position.Y, particleEndPos.Y));
122 
123  if (tracerPoints != null)
124  {
125  minPos = new Vector2(
126  Math.Min(Math.Min(minPos.X, tracerPoints.Item1.X), tracerPoints.Item2.X),
127  Math.Min(Math.Min(minPos.Y, tracerPoints.Item1.Y), tracerPoints.Item2.Y));
128  maxPos = new Vector2(
129  Math.Max(Math.Max(maxPos.X, tracerPoints.Item1.X), tracerPoints.Item2.X),
130  Math.Max(Math.Max(maxPos.Y, tracerPoints.Item1.Y), tracerPoints.Item2.Y));
131  }
132 
133  Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
134 
135  if (!prefab.DrawAlways)
136  {
137  if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
138  if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
139  }
140 
141  if (particles[particleCount] == null) { particles[particleCount] = new Particle(); }
142  Particle particle = particles[particleCount];
143 
144  particle.Init(prefab, position, velocity, rotation, hullGuess, drawOrder, collisionIgnoreTimer, lifeTimeMultiplier, tracerPoints: tracerPoints);
145  particleCount++;
146  particlesInCreationOrder.AddFirst(particle);
147 
148  return particle;
149  }
150 
151  public static List<ParticlePrefab> GetPrefabList()
152  {
153  return ParticlePrefab.Prefabs.ToList();
154  }
155 
156  public static ParticlePrefab FindPrefab(string prefabName)
157  {
158  ParticlePrefab.Prefabs.TryGet(prefabName, out ParticlePrefab prefab);
159  return prefab;
160  }
161 
162  private void RemoveParticle(int index)
163  {
164  particlesInCreationOrder.Remove(particles[index]);
165  particleCount--;
166 
167  (particles[particleCount], particles[index]) = (particles[index], particles[particleCount]);
168  }
169 
170 
171  public void RemoveParticle(Particle particle)
172  {
173  for (int i = 0; i < particleCount; i++)
174  {
175  if (particles[i] == particle)
176  {
177  RemoveParticle(i);
178  return;
179  }
180  }
181  }
182 
183  public void Update(float deltaTime)
184  {
185  MaxParticles = GameSettings.CurrentConfig.Graphics.ParticleLimit;
186 
187  for (int i = 0; i < particleCount; i++)
188  {
189  bool remove;
190  try
191  {
192  remove = particles[i].Update(deltaTime) == Particle.UpdateResult.Delete;
193  }
194  catch (Exception e)
195  {
196  DebugConsole.ThrowError("Particle update failed", e);
197  remove = true;
198  }
199 
200  if (remove) { RemoveParticle(i); }
201  }
202  }
203 
204  public void UpdateTransforms()
205  {
206  for (int i = 0; i < particleCount; i++)
207  {
208  particles[i].UpdateDrawPos();
209  }
210  }
211 
212  public Dictionary<ParticlePrefab, int> CountActiveParticles()
213  {
214  Dictionary<ParticlePrefab, int> activeParticles = new Dictionary<ParticlePrefab, int>();
215  for (int i = 0; i < particleCount; i++)
216  {
217  if (!activeParticles.ContainsKey(particles[i].Prefab)) activeParticles[particles[i].Prefab] = 0;
218  activeParticles[particles[i].Prefab]++;
219  }
220  return activeParticles;
221  }
222 
223  public void Draw(SpriteBatch spriteBatch, bool inWater, bool? inSub, ParticleBlendState blendState, bool? background = false)
224  {
226 
227  foreach (var particle in particlesInCreationOrder)
228  {
229  if (particle.BlendState != blendState) { continue; }
230  //equivalent to !particles[i].DrawTarget.HasFlag(drawTarget) but garbage free and faster
231  if ((particle.DrawTarget & drawTarget) == 0) { continue; }
232  if (inSub.HasValue)
233  {
234  bool isOutside = particle.CurrentHull == null;
235  if (particle.DrawOrder != ParticleDrawOrder.Foreground && isOutside == inSub.Value)
236  {
237  continue;
238  }
239  }
240  if (background.HasValue)
241  {
242  bool isBackgroundParticle = particle.DrawOrder == ParticleDrawOrder.Background;
243  if (background.Value != isBackgroundParticle) { continue; }
244  }
245  particle.Draw(spriteBatch);
246  }
247  }
248 
249  public void ClearParticles()
250  {
251  particleCount = 0;
252  particlesInCreationOrder.Clear();
253  }
254 
255  public void RemoveByPrefab(ParticlePrefab prefab)
256  {
257  if (particles == null) { return; }
258  for (int i = particles.Length - 1; i >= 0; i--)
259  {
260  if (particles[i]?.Prefab == prefab)
261  {
262  if (i < particleCount) { particleCount--; }
263 
264  particlesInCreationOrder.Remove(particles[particleCount]);
265  Particle swap = particles[particleCount];
266  particles[particleCount] = null;
267  particles[i] = swap;
268  }
269  }
270  }
271 
272  }
273 }
Rectangle WorldView
Definition: Camera.cs:123
UpdateResult Update(float deltaTime)
Definition: Particle.cs:219
void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess=null, ParticleDrawOrder drawOrder=ParticleDrawOrder.Default, float collisionIgnoreTimer=0f, float lifeTimeMultiplier=1f, Tuple< Vector2, Vector2 > tracerPoints=null)
Definition: Particle.cs:113
Particle CreateParticle(string prefabName, Vector2 position, float angle, float speed, Hull hullGuess=null, float collisionIgnoreTimer=0f, Tuple< Vector2, Vector2 > tracerPoints=null)
void RemoveByPrefab(ParticlePrefab prefab)
Dictionary< ParticlePrefab, int > CountActiveParticles()
void RemoveParticle(Particle particle)
void Draw(SpriteBatch spriteBatch, bool inWater, bool? inSub, ParticleBlendState blendState, bool? background=false)
static List< ParticlePrefab > GetPrefabList()
Particle CreateParticle(string prefabName, Vector2 position, Vector2 velocity, float rotation=0.0f, Hull hullGuess=null, float collisionIgnoreTimer=0f, Tuple< Vector2, Vector2 > tracerPoints=null)
static ParticlePrefab FindPrefab(string prefabName)
Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation=0.0f, Hull hullGuess=null, ParticleDrawOrder drawOrder=ParticleDrawOrder.Default, float collisionIgnoreTimer=0f, float lifeTimeMultiplier=1f, Tuple< Vector2, Vector2 > tracerPoints=null)
static readonly PrefabCollection< ParticlePrefab > Prefabs
readonly List< Sprite > Sprites
Vector2 CalculateEndPosition(Vector2 startPosition, Vector2 velocity)