Code Sketch


yoiiii111
By: Mhalsakant School
Category: Programming
import java.awt.BasicStroke
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Dimension
import java.awt.Font
import java.awt.GradientPaint
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.RenderingHints
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.awt.event.MouseMotionAdapter

import javax.swing.JButton
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.SwingUtilities
import javax.swing.Timer
import javax.swing.WindowConstants

import scala.collection.mutable.ArrayBuffer
import scala.util.Random


// ============================================================
// GOKUL ASHTAMI - ULTRA FESTIVAL ANIMATION
// FULL STANDALONE SCALA / SWING VERSION
// ============================================================


// ============================================================
// CONSTANTS
// ============================================================

val SCREEN_W: Int = 1200
val SCREEN_H: Int = 760

val rnd =
  new Random()

var running: Boolean =
  true

var elapsed: Double =
  0.0

var swingAngle: Double =
  0.0

var swingDirection: Double =
  1.0

var moonPulse: Double =
  0.0

var glowPulse: Double =
  0.0

var flutePulse: Double =
  0.0

var titlePulse: Double =
  0.0

var butterCount: Int =
  0


// ============================================================
// MUTABLE FLOWER
// ============================================================

class Flower(
  var x: Double,
  var y: Double,
  var vx: Double,
  var vy: Double,
  var life: Int,
  var kind: Int
)


// ============================================================
// MUTABLE SPARK
// ============================================================

class Spark(
  var x: Double,
  var y: Double,
  var vx: Double,
  var vy: Double,
  var life: Int,
  var size: Int
)


// ============================================================
// STAR
// ============================================================

class Star(
  var x: Double,
  var y: Double,
  var size: Double,
  var speed: Double,
  var phase: Double
)


// ============================================================
// BUTTER PARTICLE
// ============================================================

class ButterParticle(
  var x: Double,
  var y: Double,
  var vx: Double,
  var vy: Double,
  var life: Int,
  var size: Int
)


// ============================================================
// PARTICLE ARRAYS
// ============================================================

val flowers =
  ArrayBuffer[Flower]()

val sparks =
  ArrayBuffer[Spark]()

val stars =
  ArrayBuffer[Star]()

val butterParticles =
  ArrayBuffer[ButterParticle]()


// ============================================================
// CREATE STARS
// ============================================================

var starCreateIndex: Int =
  0

while (
  starCreateIndex < 95
) {

  stars +=
    new Star(
      rnd.nextDouble() * SCREEN_W,
      60.0 + rnd.nextDouble() * 390.0,
      1.0 + rnd.nextDouble() * 3.0,
      0.2 + rnd.nextDouble() * 0.8,
      rnd.nextDouble() * 6.28
    )

  starCreateIndex += 1
}


// ============================================================
// CREATE FLOWER
// ============================================================

def addFlower(
  px: Double,
  py: Double
): Unit = {

  val k: Int =
    rnd.nextInt(4)

  flowers +=
    new Flower(
      px,
      py,
      -1.5 + rnd.nextDouble() * 3.0,
      -3.0 - rnd.nextDouble() * 2.5,
      90 + rnd.nextInt(90),
      k
    )
}


// ============================================================
// CREATE SPARK
// ============================================================

def addSpark(
  px: Double,
  py: Double
): Unit = {

  val angle: Double =
    rnd.nextDouble() * math.Pi * 2.0

  val speed: Double =
    0.8 + rnd.nextDouble() * 3.5

  sparks +=
    new Spark(
      px,
      py,
      math.cos(angle) * speed,
      math.sin(angle) * speed,
      25 + rnd.nextInt(50),
      2 + rnd.nextInt(5)
    )
}


// ============================================================
// CREATE BUTTER
// ============================================================

def addButterParticle(
  px: Double,
  py: Double
): Unit = {

  butterParticles +=
    new ButterParticle(
      px,
      py,
      -1.0 + rnd.nextDouble() * 2.0,
      -3.0 - rnd.nextDouble() * 2.0,
      60 + rnd.nextInt(45),
      4 + rnd.nextInt(5)
    )
}


// ============================================================
// INITIAL FLOWERS
// ============================================================

var flowerIndex: Int =
  0

while (
  flowerIndex < 18
) {

  addFlower(
    120 + rnd.nextDouble() * 960,
    620 + rnd.nextDouble() * 100
  )

  flowerIndex += 1
}


// ============================================================
// DRAW UTILS
// ============================================================

def drawGlow(
  g: Graphics2D,
  cx: Int,
  cy: Int,
  radius: Int,
  alpha: Int
): Unit = {

  var r: Int =
    radius

  while (
    r > 10
  ) {

    val a: Int =
      math.max(
        5,
        math.min(
          255,
          alpha -
            (radius - r) / 2
        )
      )

    g.setColor(
      new Color(
        255,
        210,
        80,
        a
      )
    )

    g.fillOval(
      cx - r,
      cy - r,
      r * 2,
      r * 2
    )

    r -= 12
  }
}


// ============================================================
// STAR DRAW
// ============================================================

def drawStar(
  g: Graphics2D,
  cx: Int,
  cy: Int,
  outer: Int,
  inner: Int
): Unit = {

  val poly =
    new java.awt.Polygon()

  var i: Int =
    0

  while (
    i < 10
  ) {

    val angle =
      -math.Pi / 2.0 +
      i * math.Pi / 5.0

    val radius =
      if (i % 2 == 0)
        outer
      else
        inner

    poly.addPoint(
      cx +
        (math.cos(angle) * radius).toInt,
      cy +
        (math.sin(angle) * radius).toInt
    )

    i += 1
  }

  g.fillPolygon(
    poly
  )
}


// ============================================================
// PEACOCK FEATHER
// ============================================================

def drawPeacockFeather(
  g: Graphics2D,
  x: Int,
  y: Int,
  scale: Double
): Unit = {

  g.setStroke(
    new BasicStroke(
      math.max(
        2.0f,
        (5.0 * scale).toFloat
      )
    )
  )

  g.setColor(
    new Color(
      30,
      120,
      70
    )
  )

  g.drawLine(
    x,
    y + (85 * scale).toInt,
    x + (18 * scale).toInt,
    y
  )

  g.setColor(
    new Color(
      30,
      150,
      180
    )
  )

  g.fillOval(
    x - (16 * scale).toInt,
    y - (18 * scale).toInt,
    (32 * scale).toInt,
    (45 * scale).toInt
  )

  g.setColor(
    new Color(
      50,
      70,
      190
    )
  )

  g.fillOval(
    x - (10 * scale).toInt,
    y - (10 * scale).toInt,
    (20 * scale).toInt,
    (28 * scale).toInt
  )

  g.setColor(
    new Color(
      30,
      170,
      100
    )
  )

  g.fillOval(
    x - (6 * scale).toInt,
    y - (5 * scale).toInt,
    (12 * scale).toInt,
    (18 * scale).toInt
  )

  g.setColor(
    new Color(
      20,
      40,
      80
    )
  )

  g.fillOval(
    x - (3 * scale).toInt,
    y - (1 * scale).toInt,
    (6 * scale).toInt,
    (10 * scale).toInt
  )
}


// ============================================================
// KRISHNA-LIKE FESTIVE FIGURE
// ============================================================

def drawDivineFigure(
  g: Graphics2D
): Unit = {

  val cx: Int =
    610

  val cy: Int =
    525


  // BODY GLOW

  drawGlow(
    g,
    cx,
    cy - 40,
    120,
    35
  )


  // BODY

  g.setColor(
    new Color(
      65,
      105,
      185
    )
  )

  g.fillRoundRect(
    cx - 52,
    cy - 40,
    104,
    135,
    42,
    42
  )


  // FACE

  g.setColor(
    new Color(
      90,
      125,
      205
    )
  )

  g.fillOval(
    cx - 46,
    cy - 125,
    92,
    102
  )


  // HAIR

  g.setColor(
    new Color(
      25,
      20,
      45
    )
  )

  g.fillOval(
    cx - 50,
    cy - 148,
    100,
    65
  )

  g.fillOval(
    cx - 58,
    cy - 115,
    40,
    60
  )

  g.fillOval(
    cx + 18,
    cy - 118,
    40,
    63
  )


  // EYES

  g.setColor(
    Color.BLACK
  )

  g.fillOval(
    cx - 28,
    cy - 90,
    11,
    8
  )

  g.fillOval(
    cx + 17,
    cy - 90,
    11,
    8
  )


  // TILAK

  g.setColor(
    new Color(
      255,
      245,
      220
    )
  )

  g.fillRoundRect(
    cx - 3,
    cy - 88,
    6,
    26,
    3,
    3
  )


  // CROWN

  g.setColor(
    new Color(
      220,
      160,
      30
    )
  )

  val crown =
    new java.awt.Polygon()

  crown.addPoint(
    cx - 62,
    cy - 135
  )

  crown.addPoint(
    cx - 35,
    cy - 178
  )

  crown.addPoint(
    cx - 12,
    cy - 150
  )

  crown.addPoint(
    cx + 8,
    cy - 184
  )

  crown.addPoint(
    cx + 29,
    cy - 150
  )

  crown.addPoint(
    cx + 55,
    cy - 172
  )

  crown.addPoint(
    cx + 65,
    cy - 132
  )

  crown.addPoint(
    cx - 62,
    cy - 135
  )

  g.fillPolygon(
    crown
  )


  // CROWN JEWELS

  g.setColor(
    new Color(
      80,
      220,
      240
    )
  )

  g.fillOval(
    cx - 8,
    cy - 164,
    16,
    16
  )

  g.setColor(
    new Color(
      255,
      100,
      140
    )
  )

  g.fillOval(
    cx - 42,
    cy - 151,
    11,
    11
  )

  g.fillOval(
    cx + 31,
    cy - 150,
    11,
    11
  )


  // PEACOCK FEATHER

  drawPeacockFeather(
    g,
    cx + 25,
    cy - 185,
    1.15
  )


  // NECKLACE

  g.setColor(
    new Color(
      240,
      190,
      50
    )
  )

  g.setStroke(
    new BasicStroke(
      4
    )
  )

  g.drawArc(
    cx - 42,
    cy - 10,
    84,
    42,
    200,
    140
  )


  // FLUTE

  val fluteY: Int =
    cy + 8 +
      (
        math.sin(flutePulse) *
        4
      ).toInt

  g.setColor(
    new Color(
      215,
      165,
      65
    )
  )

  g.setStroke(
    new BasicStroke(
      8,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
    )
  )

  g.drawLine(
    cx - 80,
    fluteY,
    cx + 80,
    fluteY
  )


  g.setColor(
    new Color(
      60,
      35,
      20
    )
  )

  var holeIndex: Int =
    -2

  while (
    holeIndex <= 2
  ) {

    g.fillOval(
      cx + holeIndex * 25 - 3,
      fluteY - 3,
      6,
      6
    )

    holeIndex += 1
  }


  // ARMS

  g.setColor(
    new Color(
      75,
      115,
      195
    )
  )

  g.setStroke(
    new BasicStroke(
      13,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
    )
  )

  g.drawLine(
    cx - 35,
    cy - 5,
    cx - 88,
    cy + 18
  )

  g.drawLine(
    cx + 35,
    cy - 5,
    cx + 88,
    cy - 4
  )


  // CLOTH

  g.setColor(
    new Color(
      245,
      175,
      35
    )
  )

  g.fillArc(
    cx - 70,
    cy + 65,
    140,
    80,
    180,
    180
  )


  // FEET

  g.setColor(
    new Color(
      75,
      115,
      195
    )
  )

  g.fillOval(
    cx - 52,
    cy + 104,
    35,
    22
  )

  g.fillOval(
    cx + 18,
    cy + 104,
    35,
    22
  )
}


// ============================================================
// BUTTER POT
// ============================================================

def drawButterPot(
  g: Graphics2D
): Unit = {

  val cx: Int =
    610

  val topY: Int =
    155


  g.setColor(
    new Color(
      120,
      75,
      35
    )
  )

  g.fillRoundRect(
    cx - 48,
    topY + 35,
    96,
    100,
    28,
    28
  )


  g.setColor(
    Color.WHITE
  )

  g.fillOval(
    cx - 43,
    topY + 20,
    86,
    40
  )


  g.setColor(
    new Color(
      255,
      235,
      170
    )
  )

  g.fillOval(
    cx - 27,
    topY + 32,
    54,
    27
  )


  g.setColor(
    new Color(
      210,
      150,
      55
    )
  )

  g.setStroke(
    new BasicStroke(
      6
    )
  )

  g.drawArc(
    cx - 60,
    topY + 10,
    120,
    120,
    175,
    185
  )


  // HANGING ROPE

  g.drawLine(
    cx - 42,
    topY - 55,
    cx - 42,
    topY + 20
  )

  g.drawLine(
    cx + 42,
    topY - 55,
    cx + 42,
    topY + 20
  )

  g.drawLine(
    cx - 42,
    topY - 55,
    cx + 42,
    topY - 55
  )
}


// ============================================================
// JANMASHTAMI CRADLE
// ============================================================

def drawCradle(
  g: Graphics2D
): Unit = {

  val cx: Int =
    610

  val y: Int =
    610 +
      (
        math.sin(swingAngle) *
        9
      ).toInt


  // SUPPORT POSTS

  g.setStroke(
    new BasicStroke(
      12,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
    )
  )

  g.setColor(
    new Color(
      115,
      65,
      30
    )
  )

  g.drawLine(
    cx - 105,
    450,
    cx - 72,
    y - 30
  )

  g.drawLine(
    cx + 105,
    450,
    cx + 72,
    y - 30
  )


  // CHAINS

  g.setColor(
    new Color(
      230,
      175,
      60
    )
  )

  g.setStroke(
    new BasicStroke(
      5
    )
  )

  g.drawLine(
    cx - 72,
    y - 30,
    cx - 54,
    y + 8
  )

  g.drawLine(
    cx + 72,
    y - 30,
    cx + 54,
    y + 8
  )


  // CRADLE

  g.setColor(
    new Color(
      165,
      85,
      35
    )
  )

  g.fillRoundRect(
    cx - 92,
    y,
    184,
    48,
    22,
    22
  )

  g.setColor(
    new Color(
      245,
      185,
      50
    )
  )

  g.setStroke(
    new BasicStroke(
      5
    )
  )

  g.drawRoundRect(
    cx - 92,
    y,
    184,
    48,
    22,
    22
  )


  // PILLOW

  g.setColor(
    new Color(
      250,
      245,
      220
    )
  )

  g.fillRoundRect(
    cx - 75,
    y - 17,
    150,
    28,
    20,
    20
  )


  // TINY DIVINE ICON

  g.setColor(
    new Color(
      70,
      110,
      190
    )
  )

  g.fillOval(
    cx - 18,
    y - 10,
    36,
    30
  )


  drawPeacockFeather(
    g,
    cx + 10,
    y - 32,
    0.45
  )
}


// ============================================================
// CLOUD
// ============================================================

def drawCloud(
  g: Graphics2D,
  x: Int,
  y: Int,
  scale: Int
): Unit = {

  g.setColor(
    new Color(
      255,
      255,
      255,
      185
    )
  )

  g.fillOval(
    x,
    y,
    scale * 2,
    scale
  )

  g.fillOval(
    x + scale,
    y - scale / 2,
    scale * 2,
    scale + scale / 2
  )

  g.fillOval(
    x + scale * 2,
    y,
    scale * 2,
    scale
  )
}


// ============================================================
// HILLS
// ============================================================

def drawHills(
  g: Graphics2D
): Unit = {

  g.setColor(
    new Color(
      30,
      95,
      65
    )
  )

  val p =
    new java.awt.Polygon()

  p.addPoint(
    0,
    590
  )

  p.addPoint(
    170,
    500
  )

  p.addPoint(
    310,
    570
  )

  p.addPoint(
    455,
    480
  )

  p.addPoint(
    620,
    565
  )

  p.addPoint(
    790,
    490
  )

  p.addPoint(
    960,
    565
  )

  p.addPoint(
    SCREEN_W,
    510
  )

  p.addPoint(
    SCREEN_W,
    SCREEN_H
  )

  p.addPoint(
    0,
    SCREEN_H
  )

  g.fillPolygon(
    p
  )
}


// ============================================================
// LOTUS
// ============================================================

def drawLotus(
  g: Graphics2D,
  x: Int,
  y: Int
): Unit = {

  g.setColor(
    new Color(
      255,
      135,
      185
    )
  )

  g.fillOval(
    x - 35,
    y - 8,
    40,
    25
  )

  g.fillOval(
    x - 5,
    y - 8,
    40,
    25
  )

  g.fillOval(
    x - 22,
    y - 22,
    44,
    30
  )

  g.setColor(
    new Color(
      245,
      190,
      230
    )
  )

  g.fillOval(
    x - 12,
    y - 18,
    24,
    20
  )
}


// ============================================================
// MAHADWAR / ARCH
// ============================================================

def drawTempleArch(
  g: Graphics2D
): Unit = {

  g.setColor(
    new Color(
      180,
      115,
      45
    )
  )

  g.fillRoundRect(
    40,
    180,
    175,
    350,
    35,
    35
  )

  g.setColor(
    new Color(
      55,
      35,
      30
    )
  )

  g.fillRoundRect(
    70,
    250,
    115,
    280,
    40,
    40
  )

  g.setColor(
    new Color(
      245,
      185,
      55
    )
  )

  g.setStroke(
    new BasicStroke(
      7
    )
  )

  g.drawRoundRect(
    47,
    187,
    161,
    337,
    35,
    35
  )


  g.setColor(
    new Color(
      250,
      220,
      100
    )
  )

  drawStar(
    g,
    128,
    220,
    20,
    9
  )
}


// ============================================================
// WATER
// ============================================================

def drawWater(
  g: Graphics2D
): Unit = {

  g.setColor(
    new Color(
      40,
      125,
      190
    )
  )

  g.fillRect(
    0,
    650,
    SCREEN_W,
    110
  )

  g.setColor(
    new Color(
      255,
      255,
      255,
      110
    )
  )

  g.setStroke(
    new BasicStroke(
      2
    )
  )

  var wx: Int =
    0

  while (
    wx < SCREEN_W
  ) {

    val waveY =
      680 +
        (
          math.sin(
            elapsed * 2.0 +
              wx * 0.035
          ) *
          4
        ).toInt

    g.drawLine(
      wx,
      waveY,
      wx + 55,
      waveY
    )

    wx += 70
  }
}


// ============================================================
// MAIN PANEL
// ============================================================

val festivalPanel =
  new JPanel {

    setPreferredSize(
      new Dimension(
        SCREEN_W,
        SCREEN_H
      )
    )

    override def paintComponent(
      graphics: Graphics
    ): Unit = {

      super.paintComponent(
        graphics
      )

      val g =
        graphics.asInstanceOf[
          Graphics2D
        ]

      g.setRenderingHint(
        RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON
      )

      g.setRenderingHint(
        RenderingHints.KEY_RENDERING,
        RenderingHints.VALUE_RENDER_QUALITY
      )


      // ======================================================
      // SKY
      // ======================================================

      g.setPaint(
        new GradientPaint(
          0,
          0,
          new Color(
            14,
            18,
            70
          ),
          0,
          SCREEN_H,
          new Color(
            255,
            170,
            95
          )
        )
      )

      g.fillRect(
        0,
        0,
        SCREEN_W,
        SCREEN_H
      )


      // ======================================================
      // MOON GLOW
      // ======================================================

      val moonX: Int =
        1000

      val moonY: Int =
        135

      val moonRadius: Int =
        54 +
          (
            math.sin(moonPulse) *
            5
          ).toInt

      drawGlow(
        g,
        moonX,
        moonY,
        120,
        30
      )

      g.setColor(
        new Color(
          255,
          248,
          210
        )
      )

      g.fillOval(
        moonX - moonRadius,
        moonY - moonRadius,
        moonRadius * 2,
        moonRadius * 2
      )


      // ======================================================
      // STARS
      // ======================================================

      var si: Int =
        0

      while (
        si < stars.length
      ) {

        val st =
          stars(si)

        val twinkle =
          (
            math.sin(
              elapsed * 2.0 +
                st.phase
            ) *
            0.5 +
            0.5
          )

        val size =
          math.max(
            1,
            (
              st.size +
                twinkle * 2.0
            ).toInt
          )

        g.setColor(
          new Color(
            255,
            245,
            190,
            160 + (twinkle * 90).toInt
          )
        )

        g.fillOval(
          st.x.toInt,
          st.y.toInt,
          size,
          size
        )

        st.y =
          st.y +
            st.speed * 0.18

        if (
          st.y > 480
        ) {
          st.y =
            50
        }

        si += 1
      }


      // ======================================================
      // CLOUDS
      // ======================================================

      drawCloud(
        g,
        250,
        100,
        45
      )

      drawCloud(
        g,
        760,
        190,
        35
      )

      drawCloud(
        g,
        380,
        230,
        28
      )


      // ======================================================
      // TITLE GLOW
      // ======================================================

      val titleAlpha =
        150 +
          (
            (
              math.sin(titlePulse) *
              0.5 +
              0.5
            ) * 90
          ).toInt

      g.setColor(
        new Color(
          255,
          215,
          90,
          titleAlpha
        )
      )

      g.setFont(
        new Font(
          "Serif",
          Font.BOLD,
          43
        )
      )

      val title =
        "? ??? ???? ????? ?????? ?"

      val titleWidth =
        g.getFontMetrics.stringWidth(
          title
        )

      g.drawString(
        title,
        (SCREEN_W - titleWidth) / 2,
        62
      )


      g.setFont(
        new Font(
          "Arial",
          Font.BOLD,
          17
        )
      )

      g.setColor(
        new Color(
          255,
          250,
          230,
          220
        )
      )

      val subtitle =
        "A luminous festival night in Gokul"

      val subtitleWidth =
        g.getFontMetrics.stringWidth(
          subtitle
        )

      g.drawString(
        subtitle,
        (SCREEN_W - subtitleWidth) / 2,
        90
      )


      // ======================================================
      // TEMPLE
      // ======================================================

      drawTempleArch(
        g
      )


      // ======================================================
      // HILLS
      // ======================================================

      drawHills(
        g
      )


      // ======================================================
      // TREES
      // ======================================================

      var treeX: Int =
        250

      while (
        treeX < 1120
      ) {

        g.setColor(
          new Color(
            55,
            105,
            45
          )
        )

        g.fillRect(
          treeX,
          455,
          18,
          130
        )

        g.setColor(
          new Color(
            30,
            125,
            55
          )
        )

        g.fillOval(
          treeX - 45,
          405,
          105,
          100
        )

        g.fillOval(
          treeX + 5,
          385,
          100,
          105
        )

        treeX += 190
      }


      // ======================================================
      // LOTUS
      // ======================================================

      drawLotus(
        g,
        305,
        650
      )

      drawLotus(
        g,
        930,
        650
      )


      // ======================================================
      // WATER
      // ======================================================

      drawWater(
        g
      )


      // ======================================================
      // BUTTER POT
      // ======================================================

      drawButterPot(
        g
      )


      // ======================================================
      // DIVINE FIGURE
      // ======================================================

      drawDivineFigure(
        g
      )


      // ======================================================
      // CRADLE
      // ======================================================

      drawCradle(
        g
      )


      // ======================================================
      // FLOWER PARTICLES
      // ======================================================

      var fi: Int =
        flowers.length - 1

      while (
        fi >= 0
      ) {

        val f =
          flowers(fi)

        val flowerSize =
          7 + f.kind * 2

        val petalColor =
          if (f.kind == 0)
            new Color(
              255,
              110,
              165
            )
          else if (f.kind == 1)
            new Color(
              255,
              215,
              80
            )
          else if (f.kind == 2)
            new Color(
              175,
              115,
              255
            )
          else
            new Color(
              255,
              150,
              90
            )

        g.setColor(
          petalColor
        )

        g.fillOval(
          f.x.toInt - flowerSize,
          f.y.toInt - flowerSize / 2,
          flowerSize,
          flowerSize
        )

        g.fillOval(
          f.x.toInt,
          f.y.toInt - flowerSize,
          flowerSize,
          flowerSize
        )

        g.fillOval(
          f.x.toInt,
          f.y.toInt,
          flowerSize,
          flowerSize
        )

        g.fillOval(
          f.x.toInt - flowerSize,
          f.y.toInt,
          flowerSize,
          flowerSize
        )

        g.setColor(
          Color.YELLOW
        )

        g.fillOval(
          f.x.toInt,
          f.y.toInt,
          4,
          4
        )


        f.x =
          f.x + f.vx

        f.y =
          f.y + f.vy

        f.vy =
          f.vy + 0.045

        f.life =
          f.life - 1

        if (
          f.life <= 0 ||
          f.y > SCREEN_H
        ) {

          flowers.remove(
            fi
          )
        }

        fi -= 1
      }


      // ======================================================
      // SPARKS
      // ======================================================

      var spi: Int =
        sparks.length - 1

      while (
        spi >= 0
      ) {

        val s =
          sparks(spi)

        val a =
          math.max(
            0,
            math.min(
              255,
              s.life * 6
            )
          )

        g.setColor(
          new Color(
            255,
            225,
            100,
            a
          )
        )

        g.fillOval(
          s.x.toInt - s.size / 2,
          s.y.toInt - s.size / 2,
          s.size,
          s.size
        )


        s.x =
          s.x + s.vx

        s.y =
          s.y + s.vy

        s.vy =
          s.vy + 0.035

        s.life =
          s.life - 1

        if (
          s.life <= 0
        ) {

          sparks.remove(
            spi
          )
        }

        spi -= 1
      }


      // ======================================================
      // BUTTER PARTICLES
      // ======================================================

      var bi: Int =
        butterParticles.length - 1

      while (
        bi >= 0
      ) {

        val b =
          butterParticles(bi)

        g.setColor(
          new Color(
            255,
            238,
            155,
            math.max(
              0,
              math.min(
                255,
                b.life * 4
              )
            )
          )
        )

        g.fillOval(
          b.x.toInt,
          b.y.toInt,
          b.size,
          b.size
        )


        b.x =
          b.x + b.vx

        b.y =
          b.y + b.vy

        b.vy =
          b.vy + 0.05

        b.life =
          b.life - 1

        if (
          b.life <= 0
        ) {

          butterParticles.remove(
            bi
          )
        }

        bi -= 1
      }


      // ======================================================
      // FESTIVAL SIDE GLOW
      // ======================================================

      drawGlow(
        g,
        115,
        600,
        70,
        20
      )

      drawGlow(
        g,
        1085,
        600,
        70,
        20
      )


      // ======================================================
      // BOTTOM INFO
      // ======================================================

      g.setColor(
        new Color(
          20,
          15,
          40,
          175
        )
      )

      g.fillRoundRect(
        300,
        700,
        600,
        42,
        20,
        20
      )

      g.setColor(
        new Color(
          255,
          240,
          180
        )
      )

      g.setFont(
        new Font(
          "Arial",
          Font.BOLD,
          18
        )
      )

      val info =
        "? ????? ?????? ? ???? ? ???? ? ????? ? ???? ? ???? ?"

      val infoWidth =
        g.getFontMetrics.stringWidth(
          info
        )

      g.drawString(
        info,
        (SCREEN_W - infoWidth) / 2,
        728
      )
    }
  }


// ============================================================
// MOUSE INTERACTION
// ============================================================

festivalPanel.addMouseListener(
  new MouseAdapter {

    override def mousePressed(
      e: MouseEvent
    ): Unit = {

      var i: Int =
        0

      while (
        i < 7
      ) {

        addSpark(
          e.getX,
          e.getY
        )

        i += 1
      }


      addButterParticle(
        e.getX,
        e.getY
      )

      butterCount =
        butterCount + 1

      festivalPanel.repaint()
    }
  }
)


// ============================================================
// MOUSE MOTION
// ============================================================

festivalPanel.addMouseMotionListener(
  new MouseMotionAdapter {

    override def mouseMoved(
      e: MouseEvent
    ): Unit = {

      if (
        rnd.nextInt(8) == 0
      ) {

        addSpark(
          e.getX,
          e.getY
        )
      }

      festivalPanel.repaint()
    }
  }
)


// ============================================================
// START BUTTON
// ============================================================

val startButton =
  new JButton(
    "FESTIVAL"
  )

startButton.setFocusable(
  false
)


// ============================================================
// TOGGLE BUTTON
// ============================================================

startButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      running =
        !running

      if (running) {

        startButton.setText(
          "PAUSE"
        )

      } else {

        startButton.setText(
          "PLAY"
        )
      }
    }
  }
)


// ============================================================
// FRAME
// ============================================================

val frame =
  new JFrame(
    "ULTRA GOKUL ASHTAMI FESTIVAL"
  )

frame.setDefaultCloseOperation(
  WindowConstants.EXIT_ON_CLOSE
)

frame.setLayout(
  new BorderLayout()
)

frame.add(
  festivalPanel,
  BorderLayout.CENTER
)

frame.add(
  startButton,
  BorderLayout.SOUTH
)

frame.setResizable(
  false
)

frame.pack()

frame.setLocationRelativeTo(
  null
)


// ============================================================
// ANIMATION TIMER
// ============================================================

val animationTimer: Timer =
  new Timer(
    30,
    new ActionListener {

      override def actionPerformed(
        e: ActionEvent
      ): Unit = {

        if (running) {

          elapsed =
            elapsed + 0.03

          moonPulse =
            moonPulse + 0.025

          glowPulse =
            glowPulse + 0.04

          flutePulse =
            flutePulse + 0.06

          titlePulse =
            titlePulse + 0.055


          // SWING MOTION

          swingAngle =
            swingAngle +
              0.035 *
              swingDirection

          if (
            math.abs(swingAngle) >
            0.36
          ) {

            swingDirection =
              -swingDirection
          }


          // RANDOM FLOWERS

          if (
            flowers.length < 22 &&
            rnd.nextInt(4) == 0
          ) {

            addFlower(
              420 +
                rnd.nextDouble() * 400,
              610
            )
          }


          // RANDOM SPARKS

          if (
            rnd.nextInt(5) == 0
          ) {

            addSpark(
              600 +
                math.sin(elapsed) * 70,
              500 +
                math.cos(elapsed * 1.3) * 30
            )
          }


          // RANDOM BUTTER

          if (
            rnd.nextInt(18) == 0
          ) {

            addButterParticle(
              610 +
                rnd.nextDouble() * 55 -
                27,
              180
            )
          }


          festivalPanel.repaint()
        }
      }
    }
  )


// ============================================================
// WINDOW CLOSE -> STOP TIMER
// ============================================================

frame.addWindowListener(
  new java.awt.event.WindowAdapter {

    override def windowClosed(
      e: java.awt.event.WindowEvent
    ): Unit = {

      animationTimer.stop()
      running = false
    }
  }
)


// ============================================================
// SHOW
// ============================================================

SwingUtilities.invokeLater(
  new Runnable {

    override def run(): Unit = {

      startButton.setText(
        "PAUSE"
      )

      frame.setVisible(
        true
      )

      festivalPanel.requestFocusInWindow()
    }
  }
)


// ============================================================
// START ANIMATION
// ============================================================

animationTimer.start()