2 using Microsoft.Xna.Framework;
4 using System.Collections.Generic;
11 private const float MinValue =
int.MinValue,
12 MaxValue =
int.MaxValue;
16 private float angleMin, angleMax;
28 AngleMinRad = MathHelper.ToRadians(MathHelper.Clamp(value, -360.0f, 360.0f));
39 AngleMaxRad = MathHelper.ToRadians(MathHelper.Clamp(value, -360.0f, 360.0f));
68 [
Editable(ValueStep = 1, MinValueInt = 0, MaxValueInt = 1000),
Serialize(0,
IsPropertySaveable.Yes, description:
"The number of particles to spawn per frame, or every x seconds if EmitInterval is set.")]
74 [
Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 10.0f, MinValueFloat = 0.0f),
Serialize(0f,
IsPropertySaveable.Yes, description:
"If larger than 0, a particle is spawned every x pixels across the ray cast by a hitscan weapon.")]
77 [
Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 100.0f, MinValueFloat = 0.0f),
Serialize(0f,
IsPropertySaveable.Yes, description:
"Delay before the emitter becomes active after being created.")]
86 [
Editable,
Serialize(
true,
IsPropertySaveable.Yes, description:
"Should the entity heading direction be applied to the particle rotation? Only affects after flipping the texture and when CopyEntityAngle is true.")]
89 [
Editable,
Serialize(
false,
IsPropertySaveable.Yes, description:
"Only relevant for status effects. Makes the emitter copy the angle from the target of the effect instead of the entity applying the effect.")]
92 [
Editable,
Serialize(
false,
IsPropertySaveable.Yes, description:
"Only relevant for particles spawned by another particle. Makes the emitter copy the scale of the parent particle.")]
135 private float emitTimer;
136 private float burstEmitTimer;
137 private float initialDelay;
148 System.Diagnostics.Debug.Assert(prefab !=
null,
"The prefab of a particle emitter cannot be null");
152 public void Emit(
float deltaTime, Vector2 position,
Hull? hullGuess =
null,
float angle = 0.0f,
float particleRotation = 0.0f,
float velocityMultiplier = 1.0f,
float sizeMultiplier = 1.0f,
float amountMultiplier = 1.0f, Color? colorMultiplier =
null,
ParticlePrefab? overrideParticle =
null,
bool mirrorAngle =
false, Tuple<Vector2, Vector2>? tracerPoints =
null)
156 if (initialDelay <
Prefab.Properties.InitialDelay)
158 initialDelay += deltaTime;
162 emitTimer += deltaTime * amountMultiplier;
163 burstEmitTimer -= deltaTime;
165 if (
Prefab.Properties.EmitAcrossRayInterval > 0.0f && tracerPoints !=
null)
167 Vector2 dir = tracerPoints.Item2 - tracerPoints.Item1;
168 if (dir.LengthSquared() > 0.001f)
170 float dist = dir.Length();
172 for (
float z = 0.0f; z < dist; z +=
Prefab.Properties.EmitAcrossRayInterval)
174 Vector2 pos = tracerPoints.Item1 + dir * z;
175 Emit(pos, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, mirrorAngle, tracerPoints:
null);
180 if (
Prefab.Properties.ParticlesPerSecond > 0)
182 float emitInterval = 1.0f /
Prefab.Properties.ParticlesPerSecond;
183 while (emitTimer > emitInterval)
185 Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, mirrorAngle, tracerPoints: tracerPoints);
186 emitTimer -= emitInterval;
190 if (burstEmitTimer > 0.0f) {
return; }
192 burstEmitTimer =
Prefab.Properties.EmitInterval;
193 for (
int i = 0; i <
Prefab.Properties.ParticleAmount * amountMultiplier; i++)
195 Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, mirrorAngle, tracerPoints: tracerPoints);
199 private void Emit(Vector2 position,
Hull? hullGuess,
float angle,
float particleRotation,
float velocityMultiplier,
float sizeMultiplier, Color? colorMultiplier =
null,
ParticlePrefab? overrideParticle =
null,
bool mirrorAngle =
false, Tuple<Vector2, Vector2>? tracerPoints =
null)
201 var particlePrefab = overrideParticle ??
Prefab.ParticlePrefab;
202 if (particlePrefab ==
null)
204 DebugConsole.AddWarning($
"Could not find the particle prefab \"{Prefab.ParticlePrefabName}\".",
209 Vector2 velocity = Vector2.Zero;
210 if (!MathUtils.NearlyEqual(
Prefab.Properties.VelocityMax * velocityMultiplier, 0.0f) || !MathUtils.NearlyEqual(
Prefab.Properties.DistanceMax, 0.0f))
212 angle += Rand.Range(
Prefab.Properties.AngleMinRad,
Prefab.Properties.AngleMaxRad) * (mirrorAngle ? -1 : 1);
213 Vector2 dir =
new Vector2((
float)Math.Cos(angle), (
float)Math.Sin(angle));
214 velocity = dir * Rand.Range(
Prefab.Properties.VelocityMin,
Prefab.Properties.VelocityMax) * velocityMultiplier;
215 position += dir * Rand.Range(
Prefab.Properties.DistanceMin,
Prefab.Properties.DistanceMax);
218 var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, particlePrefab.DrawOnTop ||
Prefab.
DrawOnTop, lifeTimeMultiplier:
Prefab.
Properties.
LifeTimeMultiplier, tracerPoints: tracerPoints);
220 if (particle !=
null)
225 if (colorMultiplier.HasValue)
227 particle.ColorMultiplier = colorMultiplier.Value.ToVector4();
238 Rectangle bounds =
new Rectangle((
int)startPosition.X, (
int)startPosition.Y, (
int)startPosition.X, (
int)startPosition.Y);
239 if (
Prefab.ParticlePrefab ==
null) {
return bounds; }
241 for (
float angle =
Prefab.Properties.AngleMinRad; angle <=
Prefab.Properties.AngleMaxRad; angle += 0.1f)
243 Vector2 velocity =
new Vector2((
float)Math.Cos(angle), (
float)Math.Sin(angle)) *
Prefab.Properties.VelocityMax;
244 Vector2 endPosition =
Prefab.ParticlePrefab.CalculateEndPosition(startPosition, velocity);
246 Vector2 endSize =
Prefab.ParticlePrefab.CalculateEndSize();
247 float spriteExtent = 0.0f;
248 foreach (
Sprite sprite
in Prefab.ParticlePrefab.Sprites)
252 spriteExtent = Math.Max(spriteExtent, Math.Max(spriteSheet.FrameSize.X * endSize.X, spriteSheet.FrameSize.Y * endSize.Y));
256 spriteExtent = Math.Max(spriteExtent, Math.Max(sprite.
size.X * endSize.X, sprite.
size.Y * endSize.Y));
261 (
int)Math.Min(bounds.X, endPosition.X -
Prefab.Properties.DistanceMax - spriteExtent / 2),
262 (
int)Math.Min(bounds.Y, endPosition.Y -
Prefab.Properties.DistanceMax - spriteExtent / 2),
263 (
int)Math.Max(bounds.X, endPosition.X +
Prefab.Properties.DistanceMax + spriteExtent / 2),
264 (
int)Math.Max(bounds.Y, endPosition.Y +
Prefab.Properties.DistanceMax + spriteExtent / 2));
267 bounds =
new Rectangle(bounds.X, bounds.Y, bounds.Width - bounds.X, bounds.Height - bounds.Y);
293 if (element ==
null) {
throw new ArgumentNullException(nameof(element)); }
ContentPackage? ContentPackage
Identifier GetAttributeIdentifier(string key, string def)
readonly ParticleEmitterPrefab Prefab
void Emit(float deltaTime, Vector2 position, Hull? hullGuess=null, float angle=0.0f, float particleRotation=0.0f, float velocityMultiplier=1.0f, float sizeMultiplier=1.0f, float amountMultiplier=1.0f, Color? colorMultiplier=null, ParticlePrefab? overrideParticle=null, bool mirrorAngle=false, Tuple< Vector2, Vector2 >? tracerPoints=null)
ParticleEmitter(ContentXElement element)
Rectangle CalculateParticleBounds(Vector2 startPosition)
ParticleEmitter(ParticleEmitterPrefab prefab)
readonly? ContentPackage ContentPackage
ParticleEmitterPrefab(ParticlePrefab prefab, ParticleEmitterProperties properties)
ParticleEmitterPrefab(ContentXElement element)
readonly ParticleEmitterProperties Properties
readonly Identifier ParticlePrefabName
Dictionary< Identifier, SerializableProperty > SerializableProperties
bool HighQualityCollisionDetection
float EmitAcrossRayInterval
bool CopyParentParticleScale
ParticleEmitterProperties(XElement element)
static readonly PrefabCollection< ParticlePrefab > Prefabs
ContentPackage? ContentPackage
readonly Identifier Identifier
static Dictionary< Identifier, SerializableProperty > DeserializeProperties(object obj, XElement element=null)