Transposer/Entities/BloodParticle.cs
namespace Sandbox.Transposer;
/// <summary>
/// Physics-based single red pixel that falls with gravity, bounces off X bounds,
/// and converts into a <see cref="Trail"/> when it lands.
/// Spawned constantly by the player as blood drips.
/// </summary>
public class BloodParticle : Entity
{
private Vector2 _velocity;
// Tracks net downward displacement remaining before landing. Starts positive
// (pixels to fall) and is decremented by downward movement each frame.
// Upward movement (for spurts) temporarily increases it, so the particle
// lands that many pixels below its spawn point regardless of arc height.
private float _distanceToFall;
private float _gravity;
private float _opacity;
private bool _playNoise;
public BloodParticle( int x, int y, Vector2 velocity, float distanceToFall, float gravity,
TransposerScene scene, bool playNoise = false )
{
PixelPosition = new PixelPoint( x, y );
_scene = scene;
_type = "bloodparticle";
_layer = Globals.DEPTH_BLOOD_PARTICLE;
_velocity = velocity;
_distanceToFall = distanceToFall;
_gravity = gravity;
_opacity = Game.Random.Float( 0.65f, 0.9f );
_playNoise = playNoise;
}
public override void UpdateEntity( float deltaTime )
{
base.UpdateEntity( deltaTime );
float yMove = _velocity.y * deltaTime;
float newY = ExactY + yMove;
if ( IsInBounds( PixelY, (int)MathF.Round( newY ), 3 ) )
{
ExactY = newY;
}
else
{
Land();
return;
}
float xMove = _velocity.x * deltaTime;
float newX = ExactX + xMove;
if ( IsInBounds( (int)MathF.Round( newX ), PixelY, 3 ) )
{
ExactX = newX;
}
else
{
if ( _velocity.x > 0 ) PixelX--;
_velocity = new Vector2( 0, _velocity.y );
}
_distanceToFall += yMove;
if ( _distanceToFall <= 0f )
{
if ( _playNoise )
AudioManager.PlaySfx( Sfx.BloodSplat );
Land();
return;
}
// Light air resistance — only applied while moving in the "positive" direction
// (upward for Y, rightward for X). Downward/leftward motion is not damped.
if ( _velocity.y > 0f )
_velocity = new Vector2( _velocity.x, _velocity.y * 0.99f );
if ( _velocity.x > 0f )
_velocity = new Vector2( _velocity.x * 0.96f, _velocity.y );
_velocity = new Vector2( _velocity.x, _velocity.y + _gravity );
}
public override void Draw()
{
Color32 color = new( 255, 0, 0, (byte)(_opacity * 255f) );
Screen.AddPixel( PixelX, PixelY, color );
}
private void Land()
{
_scene.AddEntity( new Trail( PixelX, PixelY, _opacity, _scene ) );
_scene.RemoveEntity( this );
((GameScene)_scene).RemoveBloodParticle( this );
}
}