Code Sketch


navya temburne
By: Mhalsakant School
// ============================================================================
// ULTRA-ADVANCED LUXURY RAKSHA BANDHAN ANIMATION & SOUND ENGINE (100% FIXED)
// Scala Graphics Library & Canvas Audio Synthesis Engine
// ============================================================================

clear()
setBackground(Color(10, 6, 24))

// ----------------------------------------------------------------------------
// 1. GLOBAL CONSTANTS & MATHEMATICAL UTILITIES
// ----------------------------------------------------------------------------
val MATH_PI = 3.141592653589793
val MAX_PARTICLES_LAYER_1 = 36
val MAX_PARTICLES_LAYER_2 = 24
val MAX_PARTICLES_LAYER_3 = 12
val TOTAL_FIREWORK_STAGES = 3

def degreesToRadians(degrees: Double): Double = {
  return degrees * MATH_PI / 180.0
}

def calculateSineWave(time: Double, frequency: Double, amplitude: Double): Double = {
  return Math.sin(degreesToRadians(time * frequency)) * amplitude
}

def calculateCosineWave(time: Double, frequency: Double, amplitude: Double): Double = {
  return Math.cos(degreesToRadians(time * frequency)) * amplitude
}

// ----------------------------------------------------------------------------
// 2. CONFIGURATION & STATE MANAGEMENT
// ----------------------------------------------------------------------------
val sisterName = "fill name in line 33"

// Animation Control Variables
var isOpened = false
var lidY = 60.0          
var lidAngle = 0.0       
var fireworkRadiusPrimary = 0.0
var fireworkRadiusSecondary = 0.0
var fireworkRadiusTertiary = 0.0
val fireworkMaxRadius = 240.0

// Rakhi Rotational Physics Engine State
var rakhiPrimaryAngle = 0.0
var rakhiSecondaryAngle = 0.0
var rakhiGlowPulse = 0.0

// Typewriter Dynamic Engine Parameters
val fullGreetingTitle = "HAPPY RAKSHABANDHAN"
val subTextLine1 = "BEST SISTER EVER"
val subTextLine2 = "May your life be filled with happiness, peace, and eternal joy!"

var currentTitleIndex = 0
var currentSub1Index = 0
var currentSub2Index = 0

var masterAnimationTimer = 0
var typewriterStepCounter = 0

// Audio Melodic Synthesizer Engine Configuration
val backgroundMelodyNotes = Array(
  60, 64, 67, 72, 71, 67, 69, 72, 76, 74, 72, 67,
  64, 67, 72, 76, 77, 76, 72, 69, 72, 67, 64, 60
)
var melodyNotePointer = 0
var audioTimerCounter = 0

// ----------------------------------------------------------------------------
// 3. BACKGROUND MUSIC SYNTHESIS ENGINE
// ----------------------------------------------------------------------------
def executeBackgroundMusicStep(): Unit = {
  audioTimerCounter += 1
  if (audioTimerCounter % 6 == 0) {
    val currentNote = backgroundMelodyNotes(melodyNotePointer)
    playNote(currentNote, 320)
    melodyNotePointer = (melodyNotePointer + 1) % backgroundMelodyNotes.length
  }
}

// ----------------------------------------------------------------------------
// 4. ADVANCED GRAPHICAL DRAWING SUBSYSTEMS
// ----------------------------------------------------------------------------

// FIXED: Used getRed, getGreen, getBlue to fetch pure Int values safely
def renderGlowRectangle(width: Double, height: Double, posX: Double, posY: Double, color: Color, layers: Int): Unit = {
  val r: Int = color.getRed
  val g: Int = color.getGreen
  val b: Int = color.getBlue
  
  for (i <- 1 to layers) {
    val expansion = i * 4
    val alpha = Math.max(10, 80 - (i * 15))
    val glowPass = Picture.rectangle(width + expansion, height + expansion)
    glowPass.setFillColor(Color(r, g, b, alpha))
    glowPass.setPosition(posX - (expansion / 2.0), posY - (expansion / 2.0))
    draw(glowPass)
  }
}

// Layered Luxury Ribbon Rendering Engine
def renderLuxuryRibbonSystem(boxWidth: Double, boxHeight: Double, posX: Double, posY: Double): Unit = {
  val ribbonVertical = Picture.rectangle(36, boxHeight)
  ribbonVertical.setFillColor(Color(255, 215, 0))
  ribbonVertical.setPosition(posX + (boxWidth / 2.0) - 18, posY)
  draw(ribbonVertical)

  val ribbonVHighlight = Picture.rectangle(6, boxHeight)
  ribbonVHighlight.setFillColor(Color(255, 255, 200))
  ribbonVHighlight.setPosition(posX + (boxWidth / 2.0) - 14, posY)
  draw(ribbonVHighlight)

  val ribbonHorizontal = Picture.rectangle(boxWidth, 36)
  ribbonHorizontal.setFillColor(Color(255, 215, 0))
  ribbonHorizontal.setPosition(posX, posY + (boxHeight / 2.0) - 18)
  draw(ribbonHorizontal)

  val ribbonHHighlight = Picture.rectangle(boxWidth, 6)
  ribbonHHighlight.setFillColor(Color(255, 255, 200))
  ribbonHHighlight.setPosition(posX, posY + (boxHeight / 2.0) - 14)
  draw(ribbonHHighlight)
}

// 3D Gift Box Chassis & Lid Renderer
def renderGiftBoxStructure(): Unit = {
  if (!isOpened || lidY < 240) {
    renderGlowRectangle(210, 150, -105, -85, Color(255, 0, 100), 4)

    val primaryContainer = Picture.rectangle(200, 140)
    primaryContainer.setFillColor(Color(180, 12, 45))
    primaryContainer.setPenColor(Color(255, 215, 0))
    primaryContainer.setPenThickness(3)
    primaryContainer.setPosition(-100, -80)
    draw(primaryContainer)

    renderLuxuryRibbonSystem(200, 140, -100, -80)

    val lidChassis = Picture.rectangle(220, 48)
    lidChassis.setFillColor(Color(220, 20, 60))
    lidChassis.setPenColor(Color(255, 235, 120))
    lidChassis.setPenThickness(3)
    lidChassis.setPosition(-110, lidY)
    lidChassis.rotate(lidAngle)
    draw(lidChassis)

    val lidRibbon = Picture.rectangle(40, 48)
    lidRibbon.setFillColor(Color(255, 215, 0))
    lidRibbon.setPosition(-20, lidY)
    lidRibbon.rotate(lidAngle)
    draw(lidRibbon)

    if (!isOpened) {
      val callToActionShadow = Picture.text("? Move Cursor / Click Box to Open Magical Surprise ?", 15)
      callToActionShadow.setPenColor(Color(0, 0, 0))
      callToActionShadow.setPosition(-214, -146)
      draw(callToActionShadow)

      val callToActionText = Picture.text("? Move Cursor / Click Box to Open Magical Surprise ?", 15)
      callToActionText.setPenColor(Color(255, 215, 0))
      callToActionText.setPosition(-215, -145)
      draw(callToActionText)
    }
  }
}

// Particle Fireworks Blast Subsystem
def renderTripleLayerFireworks(): Unit = {
  if (isOpened) {
    for (i <- 0 until MAX_PARTICLES_LAYER_1) {
      val angle = (360.0 / MAX_PARTICLES_LAYER_1) * i
      val rad = degreesToRadians(angle)
      val px = fireworkRadiusPrimary * Math.cos(rad)
      val py = fireworkRadiusPrimary * Math.sin(rad)

      val sparkParticle = Picture.circle(7)
      if (i % 4 == 0) sparkParticle.setFillColor(Color(255, 215, 0))
      else if (i % 4 == 1) sparkParticle.setFillColor(Color(255, 50, 150))
      else if (i % 4 == 2) sparkParticle.setFillColor(Color(0, 240, 255))
      else sparkParticle.setFillColor(Color(255, 255, 255))

      sparkParticle.setPosition(px, py + 40)
      draw(sparkParticle)
    }

    for (j <- 0 until MAX_PARTICLES_LAYER_2) {
      val angle = (360.0 / MAX_PARTICLES_LAYER_2) * j + 15.0
      val rad = degreesToRadians(angle)
      val px = fireworkRadiusSecondary * Math.cos(rad)
      val py = fireworkRadiusSecondary * Math.sin(rad)

      val sparkParticle = Picture.circle(5)
      if (j % 3 == 0) sparkParticle.setFillColor(Color(255, 105, 180))
      else if (j % 3 == 1) sparkParticle.setFillColor(Color(50, 255, 200))
      else sparkParticle.setFillColor(Color(255, 235, 120))

      sparkParticle.setPosition(px, py + 40)
      draw(sparkParticle)
    }

    for (k <- 0 until MAX_PARTICLES_LAYER_3) {
      val angle = (360.0 / MAX_PARTICLES_LAYER_3) * k + 30.0
      val rad = degreesToRadians(angle)
      val px = fireworkRadiusTertiary * Math.cos(rad)
      val py = fireworkRadiusTertiary * Math.sin(rad)

      val sparkParticle = Picture.circle(4)
      sparkParticle.setFillColor(Color(255, 255, 255))
      sparkParticle.setPosition(px, py + 40)
      draw(sparkParticle)
    }
  }
}

// Animated Luxury Rakhi Renderer
def renderAnimatedLuxuryRakhi(centerX: Double, centerY: Double): Unit = {
  val threadLeftShadow = Picture.rectangle(170, 8)
  threadLeftShadow.setFillColor(Color(100, 10, 30))
  threadLeftShadow.setPosition(centerX - 210, centerY - 4)
  draw(threadLeftShadow)

  val threadLeftCore = Picture.rectangle(170, 6)
  threadLeftCore.setFillColor(Color(255, 40, 90))
  threadLeftCore.setPosition(centerX - 210, centerY - 3)
  draw(threadLeftCore)

  val threadRightShadow = Picture.rectangle(170, 8)
  threadRightShadow.setFillColor(Color(100, 10, 30))
  threadRightShadow.setPosition(centerX + 40, centerY - 4)
  draw(threadRightShadow)

  val threadRightCore = Picture.rectangle(170, 6)
  threadRightCore.setFillColor(Color(255, 40, 90))
  threadRightCore.setPosition(centerX + 40, centerY - 3)
  draw(threadRightCore)

  for (b <- 0 until 12) {
    val angle = (360.0 / 12) * b + rakhiSecondaryAngle
    val rad = degreesToRadians(angle)
    val bx = centerX + (58 + rakhiGlowPulse) * Math.cos(rad)
    val by = centerY + (58 + rakhiGlowPulse) * Math.sin(rad)

    val starBead = Picture.circle(5)
    starBead.setFillColor(Color(255, 215, 0))
    starBead.setPosition(bx, by)
    draw(starBead)
  }

  val dialOuter = Picture.circle(44)
  dialOuter.setFillColor(Color(220, 20, 60))
  dialOuter.setPenColor(Color(255, 215, 0))
  dialOuter.setPenThickness(3)
  dialOuter.setPosition(centerX, centerY)
  draw(dialOuter)

  for (p <- 0 until 8) {
    val angle = (360.0 / 8) * p + rakhiPrimaryAngle
    val rad = degreesToRadians(angle)
    val px = centerX + 34 * Math.cos(rad)
    val py = centerY + 34 * Math.sin(rad)

    val pearlNode = Picture.circle(4)
    pearlNode.setFillColor(Color(255, 255, 230))
    pearlNode.setPosition(px, py)
    draw(pearlNode)
  }

  val dialMiddle = Picture.circle(28)
  dialMiddle.setFillColor(Color(255, 215, 0))
  dialMiddle.setPenColor(Color(255, 255, 255))
  dialMiddle.setPenThickness(2)
  dialMiddle.setPosition(centerX, centerY)
  draw(dialMiddle)

  val dialInner = Picture.circle(14)
  dialInner.setFillColor(Color(0, 180, 216))
  dialInner.setPosition(centerX, centerY)
  draw(dialInner)

  val gemHighlight = Picture.circle(4)
  gemHighlight.setFillColor(Color(255, 255, 255))
  gemHighlight.setPosition(centerX - 3, centerY + 3)
  draw(gemHighlight)
}

// Card & Text Engine
def renderCardContentAndText(): Unit = {
  if (lidY >= 200) {
    renderGlowRectangle(540, 360, -270, -180, Color(255, 0, 127), 5)

    val mainCardFrame = Picture.rectangle(530, 350)
    mainCardFrame.setPenColor(Color(255, 215, 0))
    mainCardFrame.setPenThickness(4)
    mainCardFrame.setFillColor(Color(20, 10, 38))
    mainCardFrame.setPosition(-265, -175)
    draw(mainCardFrame)

    val innerFiligree = Picture.rectangle(510, 330)
    innerFiligree.setPenColor(Color(255, 215, 0, 100))
    innerFiligree.setPenThickness(1)
    innerFiligree.setPosition(-255, -165)
    draw(innerFiligree)

    renderAnimatedLuxuryRakhi(0, 40)

    val currentTitleSubstring = fullGreetingTitle.substring(0, currentTitleIndex)
    
    val titleShadow = Picture.text(s"? $currentTitleSubstring ?", 24)
    titleShadow.setPenColor(Color(0, 0, 0))
    titleShadow.setPosition(-198, 118)
    draw(titleShadow)

    val titleMain = Picture.text(s"? $currentTitleSubstring ?", 24)
    titleMain.setPenColor(Color(255, 215, 0))
    titleMain.setPosition(-200, 120)
    draw(titleMain)

    if (currentTitleIndex >= fullGreetingTitle.length) {
      val currentSub1Substring = subTextLine1.substring(0, currentSub1Index)
      
      val nameShadow = Picture.text(s"? $currentSub1Substring: $sisterName ?", 21)
      nameShadow.setPenColor(Color(0, 0, 0))
      nameShadow.setPosition(-168, -48)
      draw(nameShadow)

      val nameMain = Picture.text(s"? $currentSub1Substring: $sisterName ?", 21)
      nameMain.setPenColor(Color(255, 105, 180))
      nameMain.setPosition(-170, -46)
      draw(nameMain)
    }

    if (currentSub1Index >= subTextLine1.length) {
      val currentSub2Substring = subTextLine2.substring(0, currentSub2Index)
      
      val blessingShadow = Picture.text(s"? $currentSub2Substring", 14)
      blessingShadow.setPenColor(Color(0, 0, 0))
      blessingShadow.setPosition(-218, -98)
      draw(blessingShadow)

      val blessingMain = Picture.text(s"? $currentSub2Substring", 14)
      blessingMain.setPenColor(Color(240, 240, 250))
      blessingMain.setPosition(-220, -96)
      draw(blessingMain)
    }
  }
}

// Master Scene Pipeline
def renderMasterScenePipeline(): Unit = {
  erasePictures()
  renderGiftBoxStructure()
  renderTripleLayerFireworks()
  renderCardContentAndText()
}

renderMasterScenePipeline()

// ----------------------------------------------------------------------------
// 5. MASTER ANIMATION LOOP
// ----------------------------------------------------------------------------
animateWithState(0.0) { frame =>
  if (isOpened) {
    executeBackgroundMusicStep()

    if (lidY < 230.0) {
      lidY += 8.0
      lidAngle += 5.0
    }

    if (fireworkRadiusPrimary < fireworkMaxRadius) {
      fireworkRadiusPrimary += 7.0
    }
    if (fireworkRadiusPrimary > 30.0 && fireworkRadiusSecondary < (fireworkMaxRadius - 30.0)) {
      fireworkRadiusSecondary += 6.0
    }
    if (fireworkRadiusSecondary > 30.0 && fireworkRadiusTertiary < (fireworkMaxRadius - 60.0)) {
      fireworkRadiusTertiary += 5.0
    }

    rakhiPrimaryAngle = (rakhiPrimaryAngle + 3.5) % 360.0
    rakhiSecondaryAngle = (rakhiSecondaryAngle - 2.0) % 360.0
    rakhiGlowPulse = calculateSineWave(frame, 6.0, 4.0)

    typewriterStepCounter += 1
    if (typewriterStepCounter % 3 == 0) {
      if (currentTitleIndex < fullGreetingTitle.length) {
        currentTitleIndex += 1
      } else if (currentSub1Index < subTextLine1.length) {
        currentSub1Index += 1
      } else if (currentSub2Index < subTextLine2.length) {
        currentSub2Index += 1
      }
    }

    renderMasterScenePipeline()
  }
  frame + 1.0
}

// ----------------------------------------------------------------------------
// 6. EVENT HANDLERS
// ----------------------------------------------------------------------------
def triggerBoxOpening(): Unit = {
  if (!isOpened) {
    isOpened = true
    fireworkRadiusPrimary = 10.0
    fireworkRadiusSecondary = 5.0
    fireworkRadiusTertiary = 0.0
  }
}

onMouseMove { (x, y) =>
  if (!isOpened) {
    if (x >= -110 && x <= 110 && y >= -50 && y <= 120) {
      triggerBoxOpening()
    }
  }
}

onMouseClick { (x, y) =>
  if (!isOpened) {
    if (x >= -110 && x <= 110 && y >= -50 && y <= 120) {
      triggerBoxOpening()
    }
  }
}