225 lines
8.9 KiB
C#
225 lines
8.9 KiB
C#
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
|
|
namespace Barotrauma.Particles
|
|
{
|
|
class ParticleEmitter
|
|
{
|
|
private float emitTimer;
|
|
private float burstEmitTimer;
|
|
|
|
public readonly ParticleEmitterPrefab Prefab;
|
|
|
|
public ParticleEmitter(XElement element)
|
|
{
|
|
Prefab = new ParticleEmitterPrefab(element);
|
|
}
|
|
|
|
public ParticleEmitter(ParticleEmitterPrefab prefab)
|
|
{
|
|
System.Diagnostics.Debug.Assert(prefab != null, "The prefab of a particle emitter cannot be null");
|
|
Prefab = prefab;
|
|
}
|
|
|
|
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)
|
|
{
|
|
emitTimer += deltaTime * amountMultiplier;
|
|
burstEmitTimer -= deltaTime;
|
|
|
|
if (Prefab.ParticlesPerSecond > 0)
|
|
{
|
|
float emitInterval = 1.0f / Prefab.ParticlesPerSecond;
|
|
while (emitTimer > emitInterval)
|
|
{
|
|
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle);
|
|
emitTimer -= emitInterval;
|
|
}
|
|
}
|
|
|
|
if (burstEmitTimer > 0.0f) { return; }
|
|
|
|
burstEmitTimer = Prefab.EmitInterval;
|
|
for (int i = 0; i < Prefab.ParticleAmount * amountMultiplier; i++)
|
|
{
|
|
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle);
|
|
}
|
|
}
|
|
|
|
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null)
|
|
{
|
|
angle += Rand.Range(Prefab.AngleMin, Prefab.AngleMax);
|
|
|
|
Vector2 dir = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
|
|
Vector2 velocity = dir * Rand.Range(Prefab.VelocityMin, Prefab.VelocityMax) * velocityMultiplier;
|
|
position += dir * Rand.Range(Prefab.DistanceMin, Prefab.DistanceMax);
|
|
|
|
var particle = GameMain.ParticleManager.CreateParticle(overrideParticle ?? Prefab.ParticlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop);
|
|
|
|
if (particle != null)
|
|
{
|
|
particle.Size *= Rand.Range(Prefab.ScaleMin, Prefab.ScaleMax) * sizeMultiplier;
|
|
particle.HighQualityCollisionDetection = Prefab.HighQualityCollisionDetection;
|
|
if (colorMultiplier.HasValue)
|
|
{
|
|
particle.ColorMultiplier = colorMultiplier.Value.ToVector4();
|
|
}
|
|
else if (Prefab.ColorMultiplier != Color.White)
|
|
{
|
|
particle.ColorMultiplier = Prefab.ColorMultiplier.ToVector4();
|
|
}
|
|
}
|
|
}
|
|
|
|
public Rectangle CalculateParticleBounds(Vector2 startPosition)
|
|
{
|
|
Rectangle bounds = new Rectangle((int)startPosition.X, (int)startPosition.Y, (int)startPosition.X, (int)startPosition.Y);
|
|
|
|
for (float angle = Prefab.AngleMin; angle <= Prefab.AngleMax; angle += 0.1f)
|
|
{
|
|
Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Prefab.VelocityMax;
|
|
Vector2 endPosition = Prefab.ParticlePrefab.CalculateEndPosition(startPosition, velocity);
|
|
|
|
Vector2 endSize = Prefab.ParticlePrefab.CalculateEndSize();
|
|
float spriteExtent = 0.0f;
|
|
foreach (Sprite sprite in Prefab.ParticlePrefab.Sprites)
|
|
{
|
|
if (sprite is SpriteSheet spriteSheet)
|
|
{
|
|
spriteExtent = Math.Max(spriteExtent, Math.Max(spriteSheet.FrameSize.X * endSize.X, spriteSheet.FrameSize.Y * endSize.Y));
|
|
}
|
|
else
|
|
{
|
|
spriteExtent = Math.Max(spriteExtent, Math.Max(sprite.size.X * endSize.X, sprite.size.Y * endSize.Y));
|
|
}
|
|
}
|
|
|
|
bounds = new Rectangle(
|
|
(int)Math.Min(bounds.X, endPosition.X - Prefab.DistanceMax - spriteExtent / 2),
|
|
(int)Math.Min(bounds.Y, endPosition.Y - Prefab.DistanceMax - spriteExtent / 2),
|
|
(int)Math.Max(bounds.X, endPosition.X + Prefab.DistanceMax + spriteExtent / 2),
|
|
(int)Math.Max(bounds.Y, endPosition.Y + Prefab.DistanceMax + spriteExtent / 2));
|
|
}
|
|
|
|
bounds = new Rectangle(bounds.X, bounds.Y, bounds.Width - bounds.X, bounds.Height - bounds.Y);
|
|
|
|
return bounds;
|
|
}
|
|
}
|
|
|
|
class ParticleEmitterPrefab
|
|
{
|
|
public readonly string Name;
|
|
|
|
private string particlePrefabName;
|
|
|
|
private ParticlePrefab particlePrefab;
|
|
public ParticlePrefab ParticlePrefab
|
|
{
|
|
get
|
|
{
|
|
if (particlePrefab == null && particlePrefabName != null)
|
|
{
|
|
particlePrefab = GameMain.ParticleManager?.FindPrefab(particlePrefabName);
|
|
if (particlePrefab == null) { particlePrefabName = null; }
|
|
}
|
|
return particlePrefab;
|
|
}
|
|
}
|
|
|
|
public readonly float AngleMin, AngleMax;
|
|
|
|
public readonly float DistanceMin, DistanceMax;
|
|
|
|
public readonly float VelocityMin, VelocityMax;
|
|
|
|
public readonly float ScaleMin, ScaleMax;
|
|
|
|
public readonly float EmitInterval;
|
|
public readonly int ParticleAmount;
|
|
|
|
public readonly float ParticlesPerSecond;
|
|
|
|
public readonly bool HighQualityCollisionDetection;
|
|
|
|
public readonly bool CopyEntityAngle;
|
|
|
|
public readonly Color ColorMultiplier;
|
|
|
|
public bool DrawOnTop => forceDrawOnTop || ParticlePrefab.DrawOnTop;
|
|
private readonly bool forceDrawOnTop;
|
|
|
|
public ParticleEmitterPrefab(XElement element)
|
|
{
|
|
Name = element.Name.ToString();
|
|
particlePrefabName = element.GetAttributeString("particle", "");
|
|
|
|
if (element.Attribute("startrotation") == null)
|
|
{
|
|
AngleMin = element.GetAttributeFloat("anglemin", 0.0f);
|
|
AngleMax = element.GetAttributeFloat("anglemax", 0.0f);
|
|
}
|
|
else
|
|
{
|
|
AngleMin = element.GetAttributeFloat("angle", 0.0f);
|
|
AngleMax = AngleMin;
|
|
}
|
|
|
|
AngleMin = MathHelper.ToRadians(MathHelper.Clamp(AngleMin, -360.0f, 360.0f));
|
|
AngleMax = MathHelper.ToRadians(MathHelper.Clamp(AngleMax, -360.0f, 360.0f));
|
|
|
|
if (element.Attribute("scalemin") == null)
|
|
{
|
|
ScaleMin = 1.0f;
|
|
ScaleMax = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
ScaleMin = element.GetAttributeFloat("scalemin", 1.0f);
|
|
ScaleMax = Math.Max(ScaleMin, element.GetAttributeFloat("scalemax", 1.0f));
|
|
}
|
|
|
|
if (element.Attribute("distance") == null)
|
|
{
|
|
DistanceMin = element.GetAttributeFloat("distancemin", 0.0f);
|
|
DistanceMax = element.GetAttributeFloat("distancemax", 0.0f);
|
|
}
|
|
else
|
|
{
|
|
DistanceMin = DistanceMax = element.GetAttributeFloat("distance", 0.0f);
|
|
}
|
|
if (DistanceMax < DistanceMin)
|
|
{
|
|
var temp = DistanceMin;
|
|
DistanceMin = DistanceMax;
|
|
DistanceMax = temp;
|
|
}
|
|
|
|
if (element.Attribute("velocity") == null)
|
|
{
|
|
VelocityMin = element.GetAttributeFloat("velocitymin", 0.0f);
|
|
VelocityMax = element.GetAttributeFloat("velocitymax", 0.0f);
|
|
}
|
|
else
|
|
{
|
|
VelocityMin = VelocityMax = element.GetAttributeFloat("velocity", 0.0f);
|
|
}
|
|
if (VelocityMax < VelocityMin)
|
|
{
|
|
var temp = VelocityMin;
|
|
VelocityMin = VelocityMax;
|
|
VelocityMax = temp;
|
|
}
|
|
|
|
EmitInterval = element.GetAttributeFloat("emitinterval", 0.0f);
|
|
ParticlesPerSecond = element.GetAttributeFloat("particlespersecond", 0);
|
|
ParticleAmount = element.GetAttributeInt("particleamount", 0);
|
|
HighQualityCollisionDetection = element.GetAttributeBool("highqualitycollisiondetection", false);
|
|
CopyEntityAngle = element.GetAttributeBool("copyentityangle", false);
|
|
forceDrawOnTop = element.GetAttributeBool("drawontop", false);
|
|
ColorMultiplier = element.GetAttributeColor("colormultiplier", Color.White);
|
|
}
|
|
}
|
|
}
|