Code Sketch


Bixuu02
By: Mhalsakant School
Category: Programming
import scala.collection.mutable.ArrayBuffer

cleari()
originBottomLeft()

// ============================================================
// OPERATION SINDOOR - ADVANCED SAINIK COMMAND EDITION
// KOJO / CANVASDRAW
// Password: yadnesh
// ============================================================

var currentGameState = -1
val ENGINE_VERSION = "V14.0 - ADVANCED COMMAND"

// ============================================================
// PASSWORD SECURITY
// ============================================================

val missionPassword = "yadnesh"
var passwordUnlocked = false
var passwordInput = ""
var passwordErrorTimer = 0.0
var passwordSuccessPulse = 0.0

var keyYLast = false
var keyDLast = false
var keyNLast = false
var keyEPassLast = false
var keySLast = false
var keyHLast = false
var keyAAllLast = false
var keyRLast = false
var keySpaceLast = false

// ============================================================
// AVATAR SYSTEM
// ============================================================

var selectedAvatar = 1
var avatarHover = 0
var avatarLocked = false
var avatarTransition = 0.0
var avatarSelectFlash = 0.0
var avatarFloat = 0.0

val avatarNames = Array("NEON COMMANDER","IRON DEFENDER","SHADOW SCOUT","QUANTUM SPECIALIST")
val avatarRoles = Array("TACTICAL COMMAND","HEAVY DEFENSE","RAPID RESPONSE","ENERGY SYSTEMS")

// ============================================================
// CLOCKS / MISSION
// ============================================================

var universalClock = 0.0
var briefingIndex = 0
var briefingTimer = 0.0
var combatTimer = 100.0
var scoreDestroyedCount = 0
val quotaRequirement = 35

// ============================================================
// COMMANDER
// ============================================================

var commanderHP = 100.0
var commanderX = cwidth / 2.0
var commanderY = 145.0
var commanderPulse = 0.0

// ============================================================
// TANK
// ============================================================

var tankX = cwidth / 2.0 + 90.0
val tankY = 32.0
var turretAngle = math.Pi / 2.0
var tankRecoil = 0.0
var tankHeat = 0.0
var tankEngine = 0.0
var cannonFlash = 0
var cannonCooldown = 0
var missileLaunchPulse = 0.0
var missileLaunchSoundCooldown = 0

// ============================================================
// SILO / AIR DEFENSE
// ============================================================

var siloX = cwidth / 2.0 - 260.0
var siloY = 145.0
var siloAngle = math.Pi / 2.0
var missileType = 1
var missileCooldown = 0
var akashX = cwidth / 2.0 - 90.0
var akashY = 110.0
var pinakaX = cwidth / 2.0 + 200.0
var pinakaY = 120.0
var akashAngle = 0.0
var pinakaPulse = 0.0
var pinakaCooldown = 0

// ============================================================
// BOSS / FINAL STRIKE
// ============================================================

var bossActive = false
var bossX = cwidth / 2.0
var bossY = cheight - 145.0
var bossHP = 800.0
val bossMaxHP = 800.0
var bossShield = 100.0
var bossPhase = 1
var bossMovement = 0.0
var bossCore = 0.0
var strikeX = 0.0
var strikeY = 0.0
var strikeAngle = math.Pi / 2.0
var strikeSpeed = 8.5

// ============================================================
// FX
// ============================================================

var screenShake = 0.0
var colorFlash = 0.0
var redAlert = 0.0
var cinematicBars = 0.0
var scanPulse = 0.0
var gridPulse = 0.0

// ============================================================
// SQUAD
// ============================================================

val squadCount = 4
var squadDeploy = 0.0
var squadMarch = 0.0
var squadSalute = 0.0
var squadScan = 0.0
var squadDrone = 0.0

// ============================================================
// BRIEFING
// ============================================================

val briefingText =
  "TOP SECRET DIRECTIVE: OPERATION SINDOOR\n" +
  "DEFENSE STRATEGIC COMMAND NETWORK\n\n" +
  "MULTIPLE HOSTILE SIGNALS DETECTED.\n" +
  "PERIMETER STATUS: CRITICAL.\n\n" +
  "SAINIK COMMAND SQUAD: READY\n" +
  "MAIN BATTLE TANK: READY\n" +
  "AKASH AIR DEFENSE: ONLINE\n" +
  "PINAKA ROCKET GRID: ONLINE\n" +
  "COMMAND DRONE: ONLINE\n" +
  "QUANTUM LINK: STABLE\n\n" +
  "OBJECTIVES:\n" +
  "DEFEND THE SECTOR.\n" +
  "NEUTRALIZE HOSTILE WAVES.\n" +
  "DEFEAT THE FLAGSHIP.\n\n" +
  "INITIALIZE HYPER DEFENSE."

// ============================================================
// AUDIO
// ============================================================

var audioEnabled = false
try {
  setNoteInstrument(Instrument.SYNTH_STRINGS)
  audioEnabled = true
} catch {
  case _: Exception => audioEnabled = false
}

def playFX(note: Int, duration: Int): Unit = {
  if (audioEnabled) {
    try { playNote(note, duration) } catch { case _: Exception => }
  }
}

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

val MAX_STARS = 520
val MAX_PARTICLES = 1300
val MAX_SHOCKWAVES = 70
val MAX_ENEMIES = 75

val starX = Array.fill(MAX_STARS)(randomDouble(0.0, cwidth))
val starY = Array.fill(MAX_STARS)(randomDouble(50.0, cheight))
val starSize = Array.fill(MAX_STARS)(randomDouble(0.5, 3.6))
val starSpeed = Array.fill(MAX_STARS)(randomDouble(0.5, 4.0))

// ============================================================
// PARTICLES
// ============================================================

case class Particle(var x: Double,var y: Double,var vx: Double,var vy: Double,var life: Double,val r: Int,val g: Int,val b: Int,var size: Double)
val particles = ArrayBuffer.empty[Particle]

def spawnParticles(x: Double,y: Double,r: Int,g: Int,b: Int,count: Int): Unit = {
  var i = 0
  while (i < count && particles.length < MAX_PARTICLES) {
    particles.append(Particle(x,y,randomDouble(-7.5,7.5),randomDouble(-7.5,7.5),1.0,r,g,b,randomDouble(2.0,9.0)))
    i += 1
  }
}

def updateParticles(): Unit = {
  particles.filterInPlace(_.life > 0.0)
  particles.foreach { p =>
    p.x += p.vx
    p.y += p.vy
    p.vx *= 0.965
    p.vy *= 0.965
    p.life -= 0.024
    p.size *= 0.986
  }
}

def drawParticles(c: CanvasDraw): Unit = {
  particles.foreach { p =>
    val alpha = math.max(0, math.min(255, (p.life * 255.0).toInt))
    c.fill(p.r,p.g,p.b,alpha)
    c.noStroke()
    c.ellipse(p.x,p.y,p.size,p.size)
  }
}

// ============================================================
// SHOCKWAVES
// ============================================================

class Shockwave(val x: Double,val y: Double,val kind: Int) {
  var radius = 5.0
  var life = 255.0
  def update(): Unit = { radius += 10.0; life -= 13.0 }
  def draw(c: CanvasDraw): Unit = {
    if (life > 0.0) {
      c.noFill()
      if (kind == 0) c.stroke(0,255,255,life.toInt)
      else if (kind == 1) c.stroke(255,180,50,life.toInt)
      else c.stroke(255,60,60,life.toInt)
      c.strokeWeight(3.0)
      c.ellipse(x,y,radius*2.0,radius*2.0)
    }
  }
}
val shockwaves = ArrayBuffer.empty[Shockwave]

// ============================================================
// SHELLS
// ============================================================

class Shell(var x: Double,var y: Double,val vx: Double,val vy: Double) {
  var active = true
  val trail = ArrayBuffer.empty[(Double,Double)]
  def update(): Unit = {
    x += vx
    y += vy
    trail.append((x,y))
    if (trail.length > 14) trail.remove(0)
    if (x < -100 || x > cwidth + 100 || y < -100 || y > cheight + 100) active = false
  }
  def draw(c: CanvasDraw): Unit = {
    var i = 0
    while (i < trail.length) {
      val p = trail(i)
      c.fill(255,150,30,(i.toDouble / trail.length * 180.0).toInt)
      c.noStroke()
      c.ellipse(p._1,p._2,4.0,4.0)
      i += 1
    }
    c.fill(255,245,175)
    c.noStroke()
    c.ellipse(x,y,12.0,12.0)
  }
}
val shells = ArrayBuffer.empty[Shell]

// ============================================================
// MISSILES
// ============================================================

class Missile(var x: Double,var y: Double,val speed: Double,var angle: Double,val kind: Int) {
  var active = true
  val trail = ArrayBuffer.empty[(Double,Double)]
  def update(): Unit = {
    if (kind == 2) angle += 0.025
    if (kind == 3) angle += math.sin(universalClock*8.0)*0.05
    x += math.cos(angle) * speed
    y += math.sin(angle) * speed
    trail.append((x,y))
    if (trail.length > 25) trail.remove(0)
    if (x < -150 || x > cwidth+150 || y < -150 || y > cheight+150) active = false
    spawnParticles(x - math.cos(angle)*18.0,y - math.sin(angle)*18.0,40,210,255,1)
  }
  def draw(c: CanvasDraw): Unit = {
    var i = 0
    while (i < trail.length) {
      val p = trail(i)
      val aa = (i.toDouble/trail.length*210.0).toInt
      if (kind == 1) c.fill(0,255,230,aa)
      else if (kind == 2) c.fill(255,170,40,aa)
      else c.fill(255,70,40,aa)
      c.noStroke()
      c.ellipse(p._1,p._2,5.0,5.0)
      i += 1
    }
    c.pushMatrix()
    c.translate(x,y)
    c.rotate(angle)
    c.fill(235,250,255)
    c.stroke(0,110,150)
    c.strokeWeight(1.2)
    c.rect(-25,-7,50,14)
    c.fill(0,245,255)
    c.noStroke()
    c.ellipse(25,0,15,15)
    c.popMatrix()
  }
}
val missiles = ArrayBuffer.empty[Missile]

// ============================================================
// ENEMIES
// ============================================================

class Enemy(var x: Double,var y: Double,val speed: Double,val pattern: Int) {
  var active = true
  var destroyed = false
  var angle = 0.0
  val trail = ArrayBuffer.empty[(Double,Double)]
  def update(): Unit = {
    if (!destroyed) {
      y += speed
      angle += 0.07
      if (pattern == 1) x += math.sin(universalClock*4.0+y*0.02)*3.0
      if (pattern == 2) x += math.sin(universalClock*6.0+y*0.02)*5.0
      if (pattern == 3) x += math.cos(universalClock*7.0+y*0.025)*7.0
      if (pattern == 4) x += math.sin(universalClock*10.0+y*0.04)*9.0
      trail.append((x,y))
      if (trail.length > 20) trail.remove(0)
      if (y < -100) active = false
    }
  }
  def draw(c: CanvasDraw): Unit = {
    if (!destroyed) {
      var i = 0
      while (i < trail.length) {
        val p = trail(i)
        c.fill(255,40,60,(i.toDouble/trail.length*165.0).toInt)
        c.noStroke()
        c.ellipse(p._1,p._2,4,4)
        i += 1
      }
      c.pushMatrix()
      c.translate(x,y)
      c.rotate(math.sin(angle)*0.18)
      c.fill(70,15,20)
      c.stroke(170,25,35)
      c.strokeWeight(2)
      c.rect(-28,-8,56,16)
      c.fill(210,40,45)
      c.rect(-17,-14,34,28)
      c.fill(255,170,40)
      c.noStroke()
      c.ellipse(22,0,14,14)
      c.popMatrix()
    }
  }
}
val enemies = ArrayBuffer.empty[Enemy]

// ============================================================
// EXPLOSIONS
// ============================================================

def explosion(x: Double,y: Double,power: Int): Unit = {
  shockwaves.append(new Shockwave(x,y,1))
  if (shockwaves.length > MAX_SHOCKWAVES) shockwaves.remove(0)
  spawnParticles(x,y,255,190,50,power)
  spawnParticles(x,y,255,60,30,power/2)
  screenShake = math.max(screenShake,7.0 + power*0.15)
  colorFlash = math.max(colorFlash,0.25)
}

def megaExplosion(x: Double,y: Double): Unit = {
  explosion(x,y,45)
  shockwaves.append(new Shockwave(x,y,0))
  shockwaves.append(new Shockwave(x,y,2))
  spawnParticles(x,y,255,255,220,35)
  screenShake = 18.0
  colorFlash = 0.65
  playFX(42,100)
}

// ============================================================
// AVATAR DRAW
// ============================================================

def drawAvatar(c: CanvasDraw,x: Double,y: Double,avatarType: Int,scale: Double,selected: Boolean,salute: Boolean): Unit = {
  c.pushMatrix()
  c.translate(x,y)
  c.scale(scale,scale)
  val breath = math.sin(avatarFloat*2.0)*1.5
  val walk = math.sin(squadMarch)*4.0

  if (selected) {
    c.noFill()
    c.stroke(0,255,255,150)
    c.strokeWeight(2.5)
    c.ellipse(0,30,105+math.sin(avatarFloat*3.0)*8.0,145)
    c.stroke(255,215,60,100)
    c.ellipse(0,30,125+math.sin(avatarFloat*2.0)*8.0,165)
  }

  c.fill(0,0,0,110); c.noStroke(); c.ellipse(0,-35,60,12)
  c.stroke(35,45,52); c.strokeWeight(7)
  c.line(-9,0,-14+walk,-31); c.line(9,0,14-walk,-31)
  c.stroke(15,20,25); c.strokeWeight(8)
  c.line(-14+walk,-31,-22+walk,-36); c.line(14-walk,-31,22-walk,-36)

  if (avatarType == 1) { c.fill(45,75,85); c.stroke(0,220,255) }
  else if (avatarType == 2) { c.fill(72,67,58); c.stroke(255,165,50) }
  else if (avatarType == 3) { c.fill(35,40,60); c.stroke(175,80,255) }
  else { c.fill(35,68,58); c.stroke(0,255,180) }
  c.strokeWeight(2); c.rect(-20,breath,40,43)

  if (avatarType == 1) c.fill(0,220,255)
  else if (avatarType == 2) c.fill(255,100,30)
  else if (avatarType == 3) c.fill(185,75,255)
  else c.fill(0,255,180)
  c.noStroke(); c.ellipse(0,21+breath,10,10)

  c.stroke(55,75,82); c.strokeWeight(6)
  if (salute) {
    c.line(-15,28,-22,46); c.line(15,28,21,46)
  } else {
    c.line(-15,28,-26,7); c.line(15,28,26,7)
  }

  c.fill(170,125,85); c.noStroke(); c.rect(-7,38,14,10)
  c.fill(52,70,78); c.stroke(10,22,28); c.strokeWeight(2); c.ellipse(0,68+breath,50,45)

  if (avatarType == 1) { c.fill(5,35,48); c.stroke(0,235,255) }
  else if (avatarType == 2) { c.fill(75,35,25); c.stroke(255,170,50) }
  else if (avatarType == 3) { c.fill(30,15,60); c.stroke(190,90,255) }
  else { c.fill(5,45,35); c.stroke(0,255,180) }
  c.strokeWeight(2.5); c.ellipse(0,66+breath,34,20)

  val lightPulse = 120 + (math.abs(math.sin(avatarFloat*3.0))*120.0).toInt
  if (avatarType == 3) c.fill(190,80,lightPulse)
  else if (avatarType == 4) c.fill(0,lightPulse,180)
  else c.fill(0,lightPulse,255)
  c.noStroke(); c.ellipse(0,91+breath,6,6)

  c.fill(255,153,51); c.rect(-27,12,6,5)
  c.fill(255,255,255); c.rect(-27,17,6,5)
  c.fill(18,136,7); c.rect(-27,22,6,5)
  c.popMatrix()
}

// ============================================================
// STARFIELD
// ============================================================

def drawStarfield(c: CanvasDraw): Unit = {
  var i = 0
  while (i < MAX_STARS) {
    val alpha = math.max(55,math.min(255,(75+math.abs(math.sin(universalClock*starSpeed(i)+i))*180).toInt))
    var yy = starY(i) - universalClock*starSpeed(i)*5.0
    while (yy < 45) yy += cheight-45
    c.fill(225,240,255,alpha); c.noStroke(); c.ellipse(starX(i),yy,starSize(i),starSize(i))
    i += 1
  }
}

// ============================================================
// PASSWORD INPUT
// ============================================================

def passwordChar(ch: Char): Unit = {
  if (passwordUnlocked) return
  if (passwordInput.length < missionPassword.length) passwordInput += ch
  if (passwordInput == missionPassword) {
    passwordUnlocked = true
    passwordInput = ""
    passwordSuccessPulse = 1.0
    currentGameState = -2
    avatarLocked = false
    playFX(84,180)
  } else if (passwordInput.length >= missionPassword.length) {
    passwordInput = ""
    passwordErrorTimer = 1.8
    playFX(40,120)
  }
}

def updatePassword(): Unit = {
  if (passwordUnlocked) return
  if (passwordErrorTimer > 0) passwordErrorTimer -= 0.04
  val y = isKeyPressed(Kc.VK_Y)
  val d = isKeyPressed(Kc.VK_D)
  val n = isKeyPressed(Kc.VK_N)
  val e = isKeyPressed(Kc.VK_E)
  val s = isKeyPressed(Kc.VK_S)
  val h = isKeyPressed(Kc.VK_H)
  val a = isKeyPressed(Kc.VK_A)
  val r = isKeyPressed(Kc.VK_R)
  val sp = isKeyPressed(Kc.VK_SPACE)
  if (y && !keyYLast) passwordChar('y')
  if (d && !keyDLast) passwordChar('d')
  if (n && !keyNLast) passwordChar('n')
  if (e && !keyEPassLast) passwordChar('e')
  if (s && !keySLast) passwordChar('s')
  if (h && !keyHLast) passwordChar('h')
  if (a && !keyAAllLast) passwordChar('a')
  if (r && !keyRLast) passwordChar('r')
  if (sp && !keySpaceLast) passwordChar(' ')
  keyYLast = y; keyDLast = d; keyNLast = n; keyEPassLast = e; keySLast = s; keyHLast = h; keyAAllLast = a; keyRLast = r; keySpaceLast = sp
}

// ============================================================
// PASSWORD SCREEN
// ============================================================

def drawPasswordScreen(c: CanvasDraw): Unit = {
  c.background(1,3,14)
  drawStarfield(c)
  val cx = cwidth/2.0
  val cy = cheight/2.0
  val glow = 3.0 + math.abs(math.sin(avatarFloat*2.0))*2.0

  c.fill(6,18,42,250); c.stroke(0,255,255); c.strokeWeight(glow); c.rect(cx-275,cy-165,550,330)
  c.fill(0,255,230); c.text("DEFENSE COMMAND SECURITY",cx-132,cy+120)
  c.fill(255,215,60); c.text("OPERATION SINDOOR",cx-82,cy+88)

  c.fill(25,35,60); c.stroke(0,220,255); c.strokeWeight(3); c.rect(cx-42,cy-28,84,65)
  c.noFill(); c.stroke(0,220,255); c.strokeWeight(5)
  c.line(cx-28,cy-28,cx-28,cy-52); c.line(cx-28,cy-52,cx,cy-68); c.line(cx,cy-68,cx+28,cy-52); c.line(cx+28,cy-52,cx+28,cy-28)
  c.fill(255,215,60); c.noStroke(); c.ellipse(cx,cy,10,10)

  c.fill(170,210,230); c.text("ENTER ACCESS PASSWORD",cx-105,cy-88)
  c.fill(4,22,40); c.stroke(0,210,255); c.strokeWeight(2); c.rect(cx-135,cy-130,270,38)
  val masked = passwordInput.map(_ => "*").mkString
  c.fill(0,255,180); c.noStroke(); c.text(masked,cx-110,cy-105)

  if (passwordErrorTimer > 0) {
    c.fill(255,70,70); c.text("ACCESS DENIED - RETRY",cx-88,cy-145)
  } else {
    c.fill(0,220,180); c.text("WAITING FOR AUTHORIZATION",cx-115,cy-145)
  }

  c.fill(100,150,190); c.text("TYPE: YADNESH SHA...",cx-92,cy+145)
  c.fill(80,120,150); c.text("SPACE IS REQUIRED BETWEEN WORDS",cx-125,cy+120)

  if (passwordSuccessPulse > 0) {
    c.noFill(); c.stroke(0,255,180,(passwordSuccessPulse*230).toInt); c.strokeWeight(5)
    c.rect(cx-290,cy-180,580,360); passwordSuccessPulse *= 0.90
  }
}

// ============================================================
// AVATAR SELECTION - 4 CARDS -> ONLY SELECTED CARD
// ============================================================

def drawAvatarSelectionScreen(c: CanvasDraw): Unit = {
  avatarFloat += 0.045
  avatarButtonClockSafe()
  c.background(2,6,20)
  drawStarfield(c)

  c.stroke(0,170,220,28); c.strokeWeight(1)
  var gx = 0.0
  while (gx < cwidth) { c.line(gx,0,gx,cheight); gx += 45 }
  var gy = 0.0
  while (gy < cheight) { c.line(0,gy,cwidth,gy); gy += 45 }

  c.fill(0,255,230); c.text("DEFENSE COMMAND // AVATAR SELECTION",cwidth/2.0-175,cheight-30)
  c.fill(255,215,60); c.text("SELECT YOUR SAINIK AVATAR",cwidth/2.0-125,cheight-58)

  val cardW = 170.0
  val cardH = 275.0
  val firstX = cwidth/2.0-365.0
  val cardY = cheight/2.0-145.0

  if (!avatarLocked) {
    c.fill(140,190,210); c.text("Choose one specialist - the other avatars will hide after selection.",cwidth/2.0-200,cheight-84)
    var i = 0
    while (i < 4) {
      val x = firstX + i*185.0
      val hovered = mouseX >= x && mouseX <= x+cardW && mouseY >= cardY && mouseY <= cardY+cardH
      val selected = selectedAvatar == i+1
      if (selected) { c.fill(0,65,85,245); c.stroke(0,255,255); c.strokeWeight(3.5) }
      else if (hovered) { c.fill(20,45,70,245); c.stroke(255,215,70); c.strokeWeight(2.5) }
      else { c.fill(7,20,40,235); c.stroke(55,90,120); c.strokeWeight(1.5) }
      c.rect(x,cardY,cardW,cardH)
      drawAvatar(c,x+cardW/2.0,cardY+105,i+1,0.9,selected,false)
      c.fill(cm.rgb(255,255,255)); c.text(avatarNames(i),x+19,cardY+178)
      c.fill(0,245,220); c.text(avatarRoles(i),x+26,cardY+200)
      c.fill(160,190,210)
      if (i == 0) { c.text("Balanced Combat",x+24,cardY+228); c.text("Tactical AI",x+43,cardY+248) }
      else if (i == 1) { c.text("Heavy Armour",x+31,cardY+228); c.text("High Defense",x+42,cardY+248) }
      else if (i == 2) { c.text("Rapid Movement",x+28,cardY+228); c.text("Recon Expert",x+38,cardY+248) }
      else { c.text("Energy Control",x+28,cardY+228); c.text("Quantum Systems",x+20,cardY+248) }
      if (isMousePressed && hovered) {
        selectedAvatar = i+1
        avatarLocked = true
        avatarTransition = 1.0
        avatarSelectFlash = 1.0
        playFX(72+i*4,90)
      }
      i += 1
    }
  } else {
    val centerX = cwidth/2.0
    val centerY = cheight/2.0-10.0
    avatarTransition *= 0.93
    avatarSelectFlash *= 0.90
    c.fill(0,55,80,245); c.stroke(0,255,255); c.strokeWeight(4); c.rect(centerX-135,centerY-155,270,315)
    c.noFill(); c.stroke(0,255,255,130); c.strokeWeight(2.5)
    c.ellipse(centerX,centerY-15,235+math.sin(avatarFloat*3.0)*10,280)
    c.stroke(255,215,60,90); c.ellipse(centerX,centerY-15,255+math.sin(avatarFloat*2.0)*12,300)
    drawAvatar(c,centerX,centerY-30,selectedAvatar,1.28,true,false)
    c.fill(cm.rgb(255,255,255)); c.text(avatarNames(selectedAvatar-1),centerX-65,centerY+105)
    c.fill(0,255,220); c.text(avatarRoles(selectedAvatar-1),centerX-58,centerY+130)
    c.fill(255,215,60); c.text("AVATAR SELECTED",centerX-62,centerY+155)
    c.fill(130,210,230); c.text("3 OTHER AVATARS HIDDEN",centerX-95,centerY-190)
    if (avatarSelectFlash > 0.01) { c.noFill(); c.stroke(255,255,255,(avatarSelectFlash*230).toInt); c.strokeWeight(8); c.ellipse(centerX,centerY-15,270+avatarSelectFlash*90,320+avatarSelectFlash*100) }
  }

  c.fill(100,255,225); c.text("SELECTED: "+avatarNames(selectedAvatar-1),cwidth/2.0-112,104)

  val bx = cwidth/2.0-140
  val by = 42.0
  c.fill(if (avatarLocked) 0 else 55,if (avatarLocked) 150 else 70,if (avatarLocked) 95 else 85)
  c.stroke(0,255,180); c.strokeWeight(2.5); c.rect(bx,by,280,48)
  c.fill(cm.rgb(255,255,255)); c.text(if (avatarLocked) "START MISSION" else "SELECT AVATAR FIRST",bx+62,by+30)

  if (isMousePressed && avatarLocked && mouseX >= bx && mouseX <= bx+280 && mouseY >= by && mouseY <= by+48) {
    currentGameState = 0
    briefingIndex = 0
    briefingTimer = 0.0
    squadDeploy = 0.0
    playFX(84,180)
  }
}

def avatarButtonClockSafe(): Unit = { }

// ============================================================
// BRIEFING
// ============================================================

def drawBriefing(c: CanvasDraw): Unit = {
  c.background(1,4,16); drawStarfield(c)
  c.fill(8,16,40,250); c.stroke(0,255,255); c.strokeWeight(3)
  val bx = cwidth/2.0-315; val by = cheight/2.0-190
  c.rect(bx,by,630,400)
  c.fill(255,215,60); c.text("OPERATION SINDOOR // COMMAND BRIEFING",bx+30,by+32)
  c.fill(80,255,225); c.text(ENGINE_VERSION,bx+440,by+32)
  val shown = briefingText.substring(0,briefingIndex)
  val lines = shown.split("\n")
  var ty = by+68
  for (line <- lines) { c.fill(230,240,255); c.text(line,bx+25,ty); ty += 19 }
  if (briefingIndex >= briefingText.length) {
    val sx = bx+170; val sy = by-58
    c.fill(0,220,150); c.stroke(cm.rgb(255,255,255)); c.strokeWeight(2); c.rect(sx,sy,290,44)
    c.fill(cm.rgb(255,255,255)); c.text("INITIALIZE MISSION",sx+63,sy+28)
    if (isMousePressed && mouseX>=sx && mouseX<=sx+290 && mouseY>=sy && mouseY<=sy+44) {
      currentGameState = 1; squadDeploy = 0.0; playFX(80,150)
    }
  }
}

// ============================================================
// COMBAT BACKGROUND
// ============================================================

def drawCombatBackground(c: CanvasDraw): Unit = {
  c.background(2,5,20); drawStarfield(c)
  c.fill(15,50,150,25); c.noStroke(); c.ellipse(cwidth/2,100,cwidth+100,160)
  c.stroke(0,120,160,35); c.strokeWeight(1)
  var y = 80.0
  while (y < 260) { c.line(0,y,cwidth,y); y += 25 }
  var x = 0.0
  while (x < cwidth) { c.line(x,120,cwidth/2,80); x += 50 }
  c.noFill(); c.stroke(0,220,255,35); c.strokeWeight(2)
  c.ellipse(cwidth/2,180,480+math.sin(gridPulse)*25,260)
}

// ============================================================
// TANK
// ============================================================

def drawTank(c: CanvasDraw): Unit = {
  c.fill(0,0,0,120); c.noStroke(); c.ellipse(tankX,tankY-4,175,24)
  c.fill(34,39,44); c.stroke(12,17,22); c.strokeWeight(1.5); c.rect(tankX-70,tankY,140,32)
  c.fill(100,105,101)
  for (i <- -5 to 5) c.ellipse(tankX+i*13.5,tankY+16,11,11)
  c.fill(68,110,60); c.rect(tankX-55,tankY+30,110,30)
  c.fill(58,96,52); c.ellipse(tankX,tankY+59,60,34)
  val barrel = 90+tankRecoil*4
  val tx = tankX+math.cos(turretAngle)*barrel
  val ty = tankY+59+math.sin(turretAngle)*barrel
  c.stroke(42,72,37); c.strokeWeight(10+tankRecoil*2); c.line(tankX,tankY+59,tx,ty)
  val enginePulse = 70+(math.abs(math.sin(tankEngine))*150).toInt
  c.fill(255,80,20,enginePulse); c.noStroke(); c.ellipse(tankX-50,tankY+4,8,5); c.ellipse(tankX+50,tankY+4,8,5)
  if (cannonFlash > 0) { c.fill(255,150,40,220); c.ellipse(tx,ty,50,50); c.fill(255,250,180,240); c.ellipse(tx,ty,22,22) }
}

// ============================================================
// SILO
// ============================================================

def drawSilo(c: CanvasDraw): Unit = {
  c.fill(58,63,73); c.stroke(25,30,38); c.strokeWeight(2); c.ellipse(siloX,siloY,76,76)
  c.pushMatrix(); c.translate(siloX,siloY); c.rotate(siloAngle); c.fill(82,108,94); c.rect(-18,-21,80,42); c.popMatrix()
}

// ============================================================
// AIR DEFENSE
// ============================================================

def drawAirDefense(c: CanvasDraw): Unit = {
  c.fill(45,85,60); c.stroke(20,45,30); c.strokeWeight(2); c.rect(akashX-36,akashY-16,72,32)
  c.pushMatrix(); c.translate(akashX,akashY); c.rotate(akashAngle); c.stroke(0,255,180,170); c.strokeWeight(2); c.line(0,0,25,0); c.popMatrix()
  c.fill(0,255,180); c.noStroke(); c.ellipse(akashX,akashY,15,15); c.fill(cm.rgb(255,255,255)); c.text("AKASH",akashX-22,akashY-24)
  c.fill(85,65,43); c.stroke(45,30,18); c.strokeWeight(2); c.rect(pinakaX-42,pinakaY-20,84,40)
  c.fill(255,153,51,(70+(math.abs(math.sin(pinakaPulse))*160)).toInt); c.noStroke(); c.ellipse(pinakaX-17,pinakaY,9,9); c.ellipse(pinakaX,pinakaY,9,9); c.ellipse(pinakaX+17,pinakaY,9,9)
  c.fill(cm.rgb(255,255,255)); c.text("PINAKA",pinakaX-26,pinakaY-28)
}

// ============================================================
// SQUAD
// ============================================================

def drawSquad(c: CanvasDraw): Unit = {
  var i = 0
  while (i < squadCount) {
    val offset = (i-1.5)*75
    val x = cwidth/2+offset
    val y = 140+math.sin(squadMarch+i.toDouble)*2.5
    drawAvatar(c,x,y,selectedAvatar,0.9*math.max(0.2,squadDeploy),false,squadSalute>0.5)
    i += 1
  }
}

// ============================================================
// COMMAND DRONE
// ============================================================

def drawCommandDrone(c: CanvasDraw): Unit = {
  val x = cwidth/2+math.cos(squadDrone)*220
  val y = 250+math.sin(squadDrone*1.7)*30
  c.fill(40,50,60); c.stroke(0,220,255); c.strokeWeight(2); c.ellipse(x,y,38,18)
  c.fill(0,230,255); c.noStroke(); c.ellipse(x,y,10,10)
  c.stroke(180,240,255,150); c.strokeWeight(2); c.line(x-20,y,x-42,y); c.line(x+20,y,x+42,y)
}

// ============================================================
// RADAR
// ============================================================

def drawRadar(c: CanvasDraw): Unit = {
  val x = 80.0; val y = cheight-120.0
  c.fill(5,20,30,230); c.stroke(0,255,160,200); c.strokeWeight(2)
  c.ellipse(x,y,120,120); c.ellipse(x,y,82,82); c.ellipse(x,y,42,42)
  c.line(x-60,y,x+60,y); c.line(x,y-60,x,y+60)
  c.stroke(0,255,180); c.strokeWeight(2.5); c.line(x,y,x+math.cos(universalClock)*56,y+math.sin(universalClock)*56)
  repeatFor(enemies) { e =>
    if (!e.destroyed) {
      val dx = math.max(-50,math.min(50,(e.x-cwidth/2)/5))
      val dy = math.max(-50,math.min(50,(e.y-cheight/2)/5))
      c.fill(255,50,60); c.noStroke(); c.ellipse(x+dx,y+dy,5,5)
    }
  }
}

// ============================================================
// HUD
// ============================================================

def drawHUD(c: CanvasDraw): Unit = {
  c.fill(5,10,30,245); c.stroke(0,255,255,190); c.strokeWeight(2); c.rect(15,cheight-66,cwidth-30,50)
  c.fill(255,220,70); c.text("OPERATION SINDOOR",28,cheight-38)
  c.fill(100,255,225); c.text(s"TARGETS: $scoreDestroyedCount / $quotaRequirement",210,cheight-38)
  c.fill(255,120,110); c.text(s"TIME: ${combatTimer.toInt}s",390,cheight-38)
  c.fill(30,35,48); c.rect(500,cheight-53,170,10)
  c.fill(40,220,110); c.rect(500,cheight-53,170*math.max(0,math.min(1,commanderHP/100)),10)
  c.fill(cm.rgb(255,255,255)); c.text("COMMANDER HP",545,cheight-25)
  drawRadar(c)
}

// ============================================================
// CONTROLS
// ============================================================

def drawControls(c: CanvasDraw): Unit = {
  c.fill(14,20,45,255); c.stroke(0,255,255); c.strokeWeight(1.8); c.rect(15,88,710,52)
  val labs = Array("T-","T+","C-","C+","FIRE","M-","M+","A","B","C","LAUNCH")
  val xs = Array(20.0,78.0,136.0,194.0,252.0,320.0,374.0,426.0,480.0,534.0,592.0)
  val ws = Array(52.0,52.0,52.0,52.0,62.0,48.0,48.0,48.0,48.0,48.0,120.0)
  var i = 0
  while (i < labs.length) {
    if (i == 4) c.fill(240,100,0)
    else if (i >= 7 && i <= 9) c.fill(if (missileType == i-6) 240 else 80,35,150)
    else if (i == 10) c.fill(200,0,240)
    else c.fill(70,75,110)
    c.rect(xs(i),96,ws(i),32); c.fill(cm.rgb(255,255,255)); c.text(labs(i),xs(i)+18,116); i += 1
  }
}

// ============================================================
// PRIMARY COMMAND PANEL - 4 CLEAR COMMANDS
// ============================================================

def drawPrimaryCommands(c: CanvasDraw): Unit = {
  val y = cheight - 142.0
  val h = 40.0
  val gap = 10.0
  val start = 168.0
  val w = 124.0
  val labels = Array("MOVE", "AIM", "FIRE", "MISSILE")
  val keys = Array("LEFT / RIGHT", "UP / DOWN", "SPACE", "ENTER")

  c.fill(5, 14, 34, 235)
  c.stroke(0, 220, 255, 150)
  c.strokeWeight(1.5)
  c.rect(start - 10, y - 8, 4 * w + 3 * gap + 20, h + 18)

  var i = 0
  while (i < 4) {
    val x = start + i * (w + gap)

    if (i == 3 && missileLaunchPulse > 0.05) {
      c.fill(245, 90, 20)
      c.stroke(255, 220, 90)
      c.strokeWeight(2.5)
    } else if (i == 2) {
      c.fill(170, 72, 20)
      c.stroke(255, 170, 80)
      c.strokeWeight(1.5)
    } else {
      c.fill(24, 46, 78)
      c.stroke(0, 210, 255, 160)
      c.strokeWeight(1.5)
    }

    c.rect(x, y, w, h)
    c.fill(cm.rgb(255,255,255))
    c.text(labels(i), x + 40, y + 18)
    c.fill(160, 205, 225)
    c.text(keys(i), x + 20, y + 33)
    i += 1
  }
}

def handlePrimaryCommands(): Unit = {
  if (currentGameState != 2 && currentGameState != 3) return
  if (!isMousePressed) return

  val y = cheight - 142.0
  val h = 40.0
  val gap = 10.0
  val start = 168.0
  val w = 124.0

  if (mouseY >= y && mouseY <= y + h) {
    if (mouseX >= start && mouseX <= start + w) {
      tankX -= 10.0
    } else if (mouseX >= start + (w + gap) && mouseX <= start + (w + gap) + w) {
      turretAngle += 0.10
    } else if (mouseX >= start + 2*(w + gap) && mouseX <= start + 2*(w + gap) + w) {
      fireGun()
    } else if (mouseX >= start + 3*(w + gap) && mouseX <= start + 3*(w + gap) + w) {
      fireMissile()
    }
  }
}

// ============================================================
// BOSS
// ============================================================

def drawBoss(c: CanvasDraw): Unit = {
  if (!bossActive) return
  if (bossShield > 0) { c.noFill(); c.stroke(0,220,255,140); c.strokeWeight(5); c.ellipse(bossX,bossY,165+math.sin(bossCore)*10,100+math.sin(bossCore)*8) }
  c.fill(125,20,30); c.stroke(255,190,50); c.strokeWeight(3); c.ellipse(bossX,bossY,125,64)
  c.fill(60,65,80); c.rect(bossX-42,bossY-40,84,24)
  val pulse = 20+math.abs(math.sin(bossCore))*7
  c.fill(255,50,40,150); c.noStroke(); c.ellipse(bossX,bossY,pulse+12,pulse+12)
  c.fill(255,50,40); c.ellipse(bossX,bossY,pulse,pulse)
  c.fill(20,20,28); c.rect(bossX-85,bossY+58,170,10)
  val hpRatio = math.max(0,math.min(1,bossHP/bossMaxHP))
  c.fill(255,45,45); c.rect(bossX-85,bossY+58,170*hpRatio,10)
  if (bossShield > 0) { c.fill(0,210,255); c.rect(bossX-85,bossY+72,170*math.max(0,math.min(1,bossShield/100)),5) }
  c.fill(255,220,80); c.text(s"PHASE $bossPhase",bossX-32,bossY+95)
}

// ============================================================
// TARGET LOCK / SCAN
// ============================================================

def drawTargetLock(c: CanvasDraw): Unit = {
  if (enemies.nonEmpty) {
    val t = enemies.head
    if (!t.destroyed) {
      val px = t.x; val py = t.y
      c.noFill(); c.stroke(0,255,220,200); c.strokeWeight(2)
      c.line(px-32,py-24,px-10,py-24); c.line(px+10,py-24,px+32,py-24)
      c.line(px-32,py+24,px-10,py+24); c.line(px+10,py+24,px+32,py+24)
      c.fill(0,255,220); c.noStroke(); c.text("TARGET",px-22,py+42)
    }
  }
}

def drawSainikScan(c: CanvasDraw): Unit = {
  val cx = cwidth/2.0; val cy = 185.0
  c.noFill(); c.stroke(0,255,220,70); c.strokeWeight(2)
  c.ellipse(cx,cy,350+math.sin(squadScan)*20,170)
  c.stroke(0,230,255,100)
  c.line(cx,cy,cx+math.cos(squadScan)*290,cy+math.sin(squadScan)*110)
}

// ============================================================
// COMBAT
// ============================================================

def drawCombat(c: CanvasDraw): Unit = {
  drawCombatBackground(c)
  drawSainikScan(c)
  drawSquad(c)
  drawCommandDrone(c)
  drawTank(c)
  drawSilo(c)
  drawAirDefense(c)
  drawParticles(c)
  repeatFor(shells)(_.draw(c))
  repeatFor(missiles)(_.draw(c))
  repeatFor(enemies)(_.draw(c))
  repeatFor(shockwaves)(_.draw(c))
  drawTargetLock(c)
  drawBoss(c)
  drawControls(c)
  drawPrimaryCommands(c)
  drawHUD(c)
  c.fill(150,195,220)
  c.text("KEYS: LEFT / RIGHT MOVE  |  UP / DOWN AIM  |  SPACE FIRE  |  ENTER MISSILE",168,cheight-90)
  if (bossActive) { c.fill(255,60,60); c.text("FLAGSHIP BATTLE ACTIVE",cwidth/2-95,cheight-35) }
}

// ============================================================
// FIRE
// ============================================================

def fireGun(): Unit = {
  if (cannonCooldown <= 0) {
    val mx = tankX+math.cos(turretAngle)*94
    val my = tankY+59+math.sin(turretAngle)*94
    shells.append(new Shell(mx,my,math.cos(turretAngle)*14,math.sin(turretAngle)*14))
    cannonCooldown = 8; cannonFlash = 6; tankRecoil = 3.5; tankHeat += 10
    spawnParticles(mx,my,255,180,60,8); screenShake = 4; playFX(72,40)
  }
}

def playMissileLaunchFX(): Unit = {
  // Four-stage launch/boost sound: ignition -> whoosh -> boost -> lock.
  playFX(48, 30)
  playFX(60, 30)
  playFX(72, 40)
  playFX(84, 55)
}

def fireMissile(): Unit = {
  if (missileCooldown <= 0) {
    val speed = if (missileType == 2) 8.0 else if (missileType == 3) 11.0 else 9.0
    val mx = siloX+math.cos(siloAngle)*65
    val my = siloY+math.sin(siloAngle)*65
    missiles.append(new Missile(mx,my,speed,siloAngle,missileType))
    missileCooldown = 12
    missileLaunchPulse = 1.0
    missileLaunchSoundCooldown = 10
    spawnParticles(mx,my,255,140,40,18)
    spawnParticles(mx,my,255,235,120,6)
    screenShake = 3
    playMissileLaunchFX()
  }
}

// ============================================================
// CONTROLS
// ============================================================

def handleMouseControls(): Unit = {
  if (currentGameState != 2 && currentGameState != 3) return
  if (!isMousePressed) return
  val mx = mouseX; val my = mouseY
  if (mx >= 20 && mx <= 70 && my >= 90 && my <= 125) tankX -= 6
  else if (mx >= 75 && mx <= 125 && my >= 90 && my <= 125) tankX += 6
  else if (mx >= 130 && mx <= 180 && my >= 90 && my <= 125) turretAngle -= 0.06
  else if (mx >= 185 && mx <= 235 && my >= 90 && my <= 125) turretAngle += 0.06
  else if (mx >= 240 && mx <= 315 && my >= 90 && my <= 125) fireGun()
  else if (mx >= 320 && mx <= 365 && my >= 90 && my <= 125) siloAngle -= 0.06
  else if (mx >= 370 && mx <= 415 && my >= 90 && my <= 125) siloAngle += 0.06
  else if (mx >= 420 && mx <= 465 && my >= 90 && my <= 125) missileType = 1
  else if (mx >= 470 && mx <= 515 && my >= 90 && my <= 125) missileType = 2
  else if (mx >= 520 && mx <= 565 && my >= 90 && my <= 125) missileType = 3
  else if (mx >= 570 && mx <= 700 && my >= 90 && my <= 125) fireMissile()
}

def handleKeyboard(): Unit = {
  if (currentGameState != 2 && currentGameState != 3) return
  if (isKeyPressed(Kc.VK_LEFT)) tankX -= 3.5
  if (isKeyPressed(Kc.VK_RIGHT)) tankX += 3.5
  if (isKeyPressed(Kc.VK_UP)) turretAngle += 0.045
  if (isKeyPressed(Kc.VK_DOWN)) turretAngle -= 0.045
  if (isKeyPressed(Kc.VK_SPACE)) fireGun()
  if (isKeyPressed(Kc.VK_1)) missileType = 1
  if (isKeyPressed(Kc.VK_2)) missileType = 2
  if (isKeyPressed(Kc.VK_3)) missileType = 3
  if (isKeyPressed(Kc.VK_ENTER)) fireMissile()
}

// ============================================================
// COLLISIONS / WORLD
// ============================================================

def checkCollisions(): Unit = {
  repeatFor(enemies) { enemy =>
    if (!enemy.destroyed) {
      repeatFor(shells) { shell =>
        if (shell.active && math.abs(shell.x-enemy.x)<40 && math.abs(shell.y-enemy.y)<40) {
          shell.active=false; enemy.destroyed=true; scoreDestroyedCount+=1; explosion(enemy.x,enemy.y,25)
        }
      }
      repeatFor(missiles) { missile =>
        if (missile.active && math.abs(missile.x-enemy.x)<46 && math.abs(missile.y-enemy.y)<46) {
          missile.active=false; enemy.destroyed=true; scoreDestroyedCount+=1; explosion(enemy.x,enemy.y,30)
        }
      }
    }
  }
  if (bossActive) {
    repeatFor(shells) { shell =>
      if (shell.active && math.abs(shell.x-bossX)<75 && math.abs(shell.y-bossY)<65) {
        shell.active=false
        if (bossShield > 0) bossShield -= 8 else bossHP -= 18
        explosion(shell.x,shell.y,28)
      }
    }
    repeatFor(missiles) { missile =>
      if (missile.active && math.abs(missile.x-bossX)<78 && math.abs(missile.y-bossY)<68) {
        missile.active=false
        if (bossShield > 0) bossShield -= 18 else bossHP -= 38
        explosion(missile.x,missile.y,32)
      }
    }
    if (bossShield < 0) bossShield = 0
  }
}

def updateObjects(): Unit = {
  shells.filterInPlace(_.active); repeatFor(shells)(_.update())
  missiles.filterInPlace(_.active); repeatFor(missiles)(_.update())
  enemies.filterInPlace(_.active); repeatFor(enemies)(_.update())
  shockwaves.filterInPlace(_.life > 0); repeatFor(shockwaves)(_.update())
}

def spawnEnemy(): Unit = {
  if (enemies.length >= MAX_ENEMIES) return
  val x = randomDouble(80,cwidth-80)
  val pattern = random(1,5)
  val speed = -(0.7+randomDouble(0,0.65))
  enemies.append(new Enemy(x,cheight+40,speed,pattern))
  spawnParticles(x,cheight-10,255,50,50,7)
}

def startBoss(): Unit = {
  bossActive=true; bossHP=bossMaxHP; bossShield=100; bossPhase=1; bossMovement=0; currentGameState=3
  redAlert=1; screenShake=10; cinematicBars=8; playFX(42,220)
}

// ============================================================
// FINAL STRIKE / VICTORY
// ============================================================

def drawFinalStrike(c: CanvasDraw): Unit = {
  if (currentGameState != 4) return
  c.pushMatrix(); c.translate(strikeX,strikeY); c.rotate(strikeAngle)
  c.fill(255,80,20,60); c.noStroke(); c.ellipse(-25,0,80,40)
  c.fill(245,245,245); c.stroke(150,30,30); c.strokeWeight(2); c.rect(-38,-12,76,24)
  c.fill(255,235,100); c.noStroke(); c.ellipse(40,0,28,28)
  c.popMatrix()
}

def drawVictory(c: CanvasDraw): Unit = {
  c.background(0,0,0); drawStarfield(c)
  c.fill(255,153,51); c.rect(cwidth/2-220,70,440,5)
  c.fill(cm.rgb(255,255,255)); c.rect(cwidth/2-220,61,440,5)
  c.fill(18,136,7); c.rect(cwidth/2-220,52,440,5)
  c.fill(255,220,100); c.text("JAI HIND",cwidth/2-40,cheight/2+110)
  c.fill(cm.rgb(255,255,255)); c.text("MISSION COMPLISHED",cwidth/2-135,cheight/2+55)
  c.fill(255,235,190); c.text("SAINIK COMMAND SUCCEEDED",cwidth/2-125,cheight/2+20)
  c.fill(100,240,255); c.text("QUANTUM DEFENSE STATUS: STABLE",cwidth/2-135,cheight/2-15)
  c.fill(cm.rgb(255,255,255)); c.text("SELECTED AVATAR:",cwidth/2-110,cheight/2-55)
  c.fill(0,255,190); c.text(avatarNames(selectedAvatar-1),cwidth/2-95,cheight/2-78)
  c.fill(255,220,80); c.text(s"DEFENSE SCORE: $scoreDestroyedCount",cwidth/2-85,cheight/2-110)
  drawAvatar(c,cwidth/2,cheight/2-180,selectedAvatar,1.35,false,true)
}

// ============================================================
// DEPLOYMENT
// ============================================================

def drawDeployment(c: CanvasDraw): Unit = {
  drawCombatBackground(c)
  val p = math.max(0.2,math.min(1,squadDeploy))
  var i=0
  while(i<squadCount){
    val offset=(i-1.5)*75; val sx=cwidth/2+offset; val sy=140+math.sin(squadMarch+i*0.8)*3
    drawAvatar(c,sx,sy,selectedAvatar,p,false,true); i+=1
  }
  drawTank(c); drawSilo(c); drawAirDefense(c)
  c.fill(255,215,60); c.text("SAINIK SQUAD DEPLOYMENT",cwidth/2-115,cheight-35)
  c.fill(100,255,225); c.text("FORMATION ALPHA // ALL UNITS READY",cwidth/2-135,cheight-60)
}

// ============================================================
// UPDATE WORLD
// ============================================================

def updateWorld(): Unit = {
  universalClock += 0.025
  avatarFloat += 0.035
  commanderPulse += 0.06
  tankEngine += 0.08
  squadMarch += 0.14
  squadScan += 0.04
  squadDrone += 0.025
  akashAngle += 0.035
  pinakaPulse += 0.07
  bossCore += 0.09
  scanPulse += 0.05
  gridPulse += 0.08
  screenShake *= 0.86
  colorFlash *= 0.84
  redAlert *= 0.95
  cinematicBars *= 0.97
  tankRecoil *= 0.85
  tankHeat *= 0.99
  missileLaunchPulse *= 0.88
  if (missileLaunchSoundCooldown > 0) missileLaunchSoundCooldown -= 1
  if (cannonFlash>0) cannonFlash-=1
  if (cannonCooldown>0) cannonCooldown-=1
  if (missileCooldown>0) missileCooldown-=1
  updateParticles()

  if (!passwordUnlocked) { updatePassword(); return }

  if (currentGameState == 0) {
    briefingTimer += 0.04
    if (briefingTimer >= 0.025 && briefingIndex < briefingText.length) { briefingIndex+=1; briefingTimer=0 }
    return
  }

  if (currentGameState == 1) {
    squadDeploy=math.min(1,squadDeploy+0.018)
    squadSalute=0.5+math.sin(universalClock)*0.5
    if (squadDeploy>=1) { currentGameState=2; playFX(82,140) }
    return
  }

  if (currentGameState == 2) {
    handlePrimaryCommands(); handleMouseControls(); handleKeyboard(); combatTimer -= 0.033; updateObjects(); checkCollisions()
    tankX=math.max(60,math.min(cwidth-60,tankX))
    if (randomDouble(0,1)<0.055) spawnEnemy()
    if (pinakaCooldown<=0 && enemies.nonEmpty) {
      val target=enemies.head; val angle=math.atan2(target.y-pinakaY,target.x-pinakaX)
      missiles.append(new Missile(pinakaX,pinakaY,10.5,angle,3)); pinakaCooldown=38; playFX(68,30)
    } else pinakaCooldown-=1
    if (combatTimer<=45 && !bossActive) { startBoss(); return }
    return
  }

  if (currentGameState == 3) {
    handlePrimaryCommands(); handleMouseControls(); handleKeyboard(); updateObjects(); checkCollisions(); bossMovement+=0.035
    bossX=cwidth/2+math.sin(bossMovement*1.8)*225
    if (bossPhase==1 && bossHP<500) { bossPhase=2; redAlert=1; screenShake=8; playFX(48,140) }
    if (bossPhase==2 && bossHP<230) { bossPhase=3; redAlert=1; screenShake=11; playFX(38,160) }
    if (randomDouble(0,1)<0.20) spawnParticles(bossX+randomDouble(-50,50),bossY+randomDouble(-30,30),255,50,40,2)
    if (bossHP<=0) {
      bossActive=false; currentGameState=4; strikeX=tankX+260; strikeY=cheight+160; strikeSpeed=8.5; cinematicBars=12
      megaExplosion(bossX,bossY); playFX(36,250)
    }
    return
  }

  if (currentGameState == 4) {
    val dx=tankX-strikeX; val dy=(tankY+50)-strikeY
    val targetAngle=math.atan2(dy,dx)
    strikeAngle += (targetAngle-strikeAngle)*0.08
    strikeX += math.cos(strikeAngle)*strikeSpeed
    strikeY += math.sin(strikeAngle)*strikeSpeed
    strikeSpeed += 0.06
    if (math.abs(strikeX-tankX)<30 && math.abs(strikeY-(tankY+50))<30) {
      megaExplosion(tankX,tankY+50); currentGameState=5; playFX(36,300)
    }
    updateObjects(); return
  }

  if (currentGameState == 5) { updateObjects(); return }
}

// ============================================================
// RENDER
// ============================================================

def renderGame(c: CanvasDraw): Unit = {
  if (currentGameState == -1) { drawPasswordScreen(c); return }
  if (currentGameState == -2) { drawAvatarSelectionScreen(c); return }
  if (currentGameState == 0) { drawBriefing(c); return }
  if (currentGameState == 1) { drawDeployment(c); return }
  if (currentGameState == 5) { drawVictory(c); return }

  if (screenShake > 0.5) {
    c.pushMatrix(); c.translate(randomDouble(-screenShake,screenShake),randomDouble(-screenShake,screenShake))
  }

  drawCombat(c)
  if (currentGameState == 4) drawFinalStrike(c)

  if (screenShake > 0.5) c.popMatrix()

  if (colorFlash > 0.01) {
    c.fill(255,245,210,math.max(0,math.min(255,(colorFlash*255).toInt))); c.noStroke(); c.rect(0,0,cwidth,cheight)
  }

  if (redAlert > 0.02) {
    c.noFill(); c.stroke(255,20,20,math.min(190,(redAlert*190).toInt)); c.strokeWeight(6); c.rect(4,4,cwidth-8,cheight-8)
  }

  val bars=math.max(0,math.min(55,cinematicBars.toInt))
  if (bars>0) { c.fill(cm.rgb(0,0,0)); c.noStroke(); c.rect(0,0,cwidth,bars); c.rect(0,cheight-bars,cwidth,bars) }
}

// ============================================================
// MAIN LOOP
// ============================================================

animateWithCanvasDraw { c =>
  updateWorld()
  renderGame(c)
}