ocelot-desktop/src/main/scala/ocelot/desktop/node/SmokeParticleNode.scala
2025-08-17 23:22:49 +03:00

80 lines
2.6 KiB
Scala

package ocelot.desktop.node
import ocelot.desktop.color.RGBAColorNorm
import ocelot.desktop.geometry.{Size2D, Vector2D}
import ocelot.desktop.graphics.{Graphics, IconSource}
import ocelot.desktop.node.SmokeParticleNode._
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.particle.Particle
import ocelot.desktop.util.Spritesheet
import scala.util.Random
trait SmokeParticleNode extends Node {
protected def emitSmoke(): Unit = synchronized {
for (_ <- 1 to randomCount) {
UiHandler.root.workspaceView.particleSystem.add(new SmokeParticle)
}
}
private class SmokeParticle extends Particle(ttl = randomDuration) {
private val color: RGBAColorNorm = randomColor
private var velocity: Vector2D = Vector2D(randomVelocityComponent, randomVelocityComponent)
private var offset: Vector2D = Vector2D(0, 0)
override def update(dt: Float): Unit = {
time += dt
offset += (velocity + SmokeParticleVolatilizationSpeed) * dt * speed
velocity *= math.pow(SmokeParticleVelocityDamping, dt).toFloat
}
override def draw(g: Graphics): Unit = {
val spriteRect = Spritesheet.sprites(IconSource.Particles.Smoke.path)
val animationFrameCount = spriteRect.h / spriteRect.w
val animationFrame = (time / ttl * animationFrameCount).toInt
val particlePosition = bounds.center + offset - SmokeParticleSize.toVector * .5f
g.sprite(
IconSource.Particles.Smoke.path,
particlePosition.x,
particlePosition.y,
SmokeParticleSize.width,
SmokeParticleSize.height,
color,
Some(spriteRect.copy(
y = spriteRect.y + animationFrame * spriteRect.w,
h = spriteRect.w,
)),
)
}
}
}
private object SmokeParticleNode {
private final val SmokeParticleSize: Size2D = Size2D(32, 32)
private final val SmokeParticleVelocityRange: Float = 300
private final val SmokeParticleVolatilizationSpeed: Vector2D = Vector2D(0, -50)
private final val SmokeParticleVelocityDamping: Float = .1f
private final val SmokeParticleCount: (Int, Int) = (10, 20)
private final val SmokeParticleAnimationDuration: (Float, Float) = (1f, 4f)
private def randomVelocityComponent: Float = Random.between(
-SmokeParticleVelocityRange,
SmokeParticleVelocityRange,
)
private def randomColor: RGBAColorNorm = {
val channel = Random.between(.5f, .9f)
RGBAColorNorm(channel, channel, channel)
}
private def randomDuration: Float = Random.between(
SmokeParticleAnimationDuration._1,
SmokeParticleAnimationDuration._2,
)
private def randomCount: Int = Random.between(
SmokeParticleCount._1,
SmokeParticleCount._2,
)
}