Code Sketch


yadnesh shahare
By: Mhalsakant School
cleari(); originBottomLeft(); var isGameAudioAllowed = false
try { setNoteInstrument(Instrument.PIANO); isGameAudioAllowed = true; } catch { case _: Exception => println("Audio/MIDI not supported on this device."); }
def playSoundFx(p: Int, d: Int): Unit = { if (isGameAudioAllowed) { try { playNote(p, d) } catch { case _: Exception => } }; }; 

var globalTime = 0.0; var waveAngle = 0.0; var neonGlowPhase = 0.0; var chakraRotation = 0.0; var currentStoryStage = -2; var welcomeTimer = 0.0; var letterTextTyped = 0; var letterTimer = 0.0; var titleCardTimer = 0.0; var selectedRobotType = 1; var isRobotUnlocked = false
var robotChallengeScore = 0; val robotChallengeTarget = 5; var isInsideUnderRocketControl = false; var controlRoomPulse = 0.0; var controlRoomScan = 0.0; var robotGamePlayerX = cwidth / 2.0; var robotGamePlayerY = 80.0; var cyberEnergyX = cwidth / 2.0; var cyberEnergyY = cheight - 150.0; var cyberEnergyVy = -3.5

val fullJanaGanaTune = ArrayBuffer((53,300),(55,300),(57,300),(57,300),(57,300),(57,300),(57,300),(55,300),(57,300),(58,300),(57,300),(55,300),(53,300),(55,300),(53,500),(60,300),(60,300),(60,300),(58,300),(60,300),(58,300),(57,300),(55,300),(57,300),(58,500),(57,300),(55,300),(53,500),(53,300),(55,300),(57,300),(57,300),(57,300),(57,300),(57,300),(55,300),(57,300),(58,300),(57,300),(55,300),(53,300),(55,300),(53,500),(60,300),(60,300),(60,300),(58,300),(60,300),(58,300),(57,300),(55,300),(57,300),(58,500),(57,300),(55,300),(53,500))
val fullVandeMataramTune = Array((60,400),(62,400),(65,500),(62,400),(60,400),(62,500),(60,400),(62,400),(65,400),(67,400),(69,600),(69,400),(70,400),(69,400),(67,400),(65,400),(67,500),(65,400),(62,400),(60,400),(62,400),(60,600)); 

var janaIndex = 0; var vandeIndex = 0; var charIndex = 0; var textDelayTimer = 0.0; var janaGanaFinished = false; var anthemFinished = false; var startAnthemPhase = false
val fullAnthemText = "Jana-gana-mana-adhinayaka jaya he\nBharata-bhagya-vidhata.\nPunjaba-Sindhu-Gujarata-Maratha\nDravida-Utkala-Banga\nVindhya-Himachala-Yamuna-Ganga\nUchchhala-Jaladhi-taranga\nTava subha name jage, Tava subha asisa mage,\nGaye tava jaya gatha.\nJana-gana-mangala-dayaka jaya he\nBharata-bhagya-vidhata.\nJaya he, jaya he, jaya he,\nJaya jaya jaya, jaya he!\n\n--- VANDE MATARAM ---\nVande Mataram! Sujalam Suphalam\nMalayaja Shytamalam Shasya Shyamalam\nMataram! Vande Mataram!"

def playAnthemStep(): Unit = {
  textDelayTimer += 0.04
  if (textDelayTimer >= 0.15) { textDelayTimer = 0.0; if (charIndex < fullAnthemText.length) charIndex += 1 }
  if (!janaGanaFinished) {
    if (janaIndex < fullJanaGanaTune.length) { val (n,d) = fullJanaGanaTune(janaIndex); playSoundFx(n,d); janaIndex += 1; } else { janaGanaFinished = true; };
  } else {
    if (vandeIndex < fullVandeMataramTune.length) { val (n,d) = fullVandeMataramTune(vandeIndex); playSoundFx(n,d); vandeIndex += 1; } else if (charIndex >= fullAnthemText.length) { anthemFinished = true; };
  };
};

val officialLetterText = "CONFIDENTIAL: INDIAN SPACE RESEARCH ORGANISATION (ISRO)\nSUBJECT: SELECTION FOR GAGANYAAN HUMAN SPACEFLIGHT PROGRAM\n\nDear Commander / Candidate,\n\nCongratulations! Your technical excellence, courage and mission skills\nhave earned you a place in India's advanced Gaganyaan simulation.\n\nReport to SDSC SHAR, Sriharikota and prepare for planetary operations.\n\nClick ACCEPT MISSION to start the Gaganyaan mission."

case class PlanetDetail( name: String, realName: String, surfaceCol: Color, darkSurfCol: Color, accent3D: Color, hasRings: Boolean, gameModeType: Int, infoLine1: String, infoLine2: String, infoLine3: String, infoLine4: String, infoLine5: String, infoLine6: String, infoLine7: String, infoLine8: String, atmosphereLine: String, quizQuestion: String, quizOptions: Array[String], correctOpt: Int )

val realSolarSystemMap = Array(
  PlanetDetail( "Mercury","Mercury (Budh)", cm.rgb(120,120,120),cm.rgb(60,60,60),cm.rgb(200,200,200), false,2, "Gravity: 3.7 m/s2 | Closest Planet to Sun", "Temperature: Extreme day/night variation", "Mission: High delta-V and precision navigation", "Distance: 57.9 Million km | Orbit: 88 Days", "Radius: 2,439.7 km", "Mass: 3.301 x 10^23 kg", "Atmosphere: Thin exosphere", "Surface: Heavily cratered", "ATMOSPHERE: Extremely thin exosphere; heat shield critical", "Which planet is closest to the Sun?", Array("Mercury","Venus","Mars","Earth"),0 ),
  PlanetDetail( "Venus","Venus (Shukra)", cm.rgb(210,150,80),cm.rgb(100,70,30),cm.rgb(255,200,130), false,3, "Gravity: 8.87 m/s2", "Dense atmosphere: mostly carbon dioxide", "Mission: radar-assisted surface mapping", "Distance: 108.2 Million km | Orbit: 225 Days", "Radius: 6,051.8 km", "Mass: 4.867 x 10^24 kg", "Pressure: ~92 bar", "Clouds: Sulfuric acid droplets", "ATMOSPHERE: Very dense CO2; EXTREME pressure and temperature", "Which is the hottest planet?", Array("Venus","Mercury","Mars","Jupiter"),0 ),
  PlanetDetail( "Earth","Earth (Prithvi)", cm.rgb(40,130,240),cm.rgb(20,60,120),cm.rgb(100,200,255), false,1, "Gravity: 9.81 m/s2", "Home Planet of the mission", "Launch Centre: SDSC SHAR, Sriharikota", "Distance: 149.6 Million km (1 AU)", "Radius: 6,371 km", "Mass: 5.972 x 10^24 kg", "Atmosphere: 78% nitrogen, 21% oxygen", "Known life-bearing world", "ATMOSPHERE: N2/O2 rich; crew-compatible environment", "How many days does Earth take to orbit the Sun?", Array("365 Days","100 Days","500 Days","30 Days"),0 ),
  PlanetDetail( "Mars","Mars (Mangal)", cm.rgb(200,70,40),cm.rgb(100,30,15),cm.rgb(255,130,100), false,2, "Gravity: 3.72 m/s2", "Mars Orbiter Mission legacy target", "Mission: Precision entry and landing", "Distance: 227.9 Million km | Orbit: 687 Days", "Radius: 3,389.5 km", "Mass: 6.417 x 10^23 kg", "Atmosphere: Mostly CO2", "Topography: Olympus Mons and Valles Marineris", "ATMOSPHERE: Thin CO2 layer; dust and radiation monitoring required", "Which planet is the Red Planet?", Array("Mars","Mercury","Jupiter","Saturn"),0 ),
  PlanetDetail( "Jupiter","Jupiter (Guru)", cm.rgb(210,140,90),cm.rgb(100,60,40),cm.rgb(255,190,140), true,1, "Gravity: 24.79 m/s2", "Largest planet in the Solar System", "Mission: Gas giant observation", "Distance: 778.5 Million km | Orbit: 11.8 Years", "Radius: 69,911 km", "Mass: 1.898 x 10^27 kg", "Composition: Hydrogen and Helium", "Great Red Spot: giant storm", "ATMOSPHERE: Hydrogen/helium gas giant; no solid landing surface", "Which is the largest planet?", Array("Jupiter","Saturn","Neptune","Earth"),0 ),
  PlanetDetail( "Saturn","Saturn (Shani)", cm.rgb(190,170,110),cm.rgb(90,80,50),cm.rgb(240,230,160), true,2, "Gravity: 10.44 m/s2", "Famous bright ring system", "Mission: Ring and moon observation", "Distance: 1.4 Billion km | Orbit: 29.5 Years", "Radius: 58,232 km", "Mass: 5.683 x 10^26 kg", "Rings: Mostly water ice", "Moon highlight: Titan", "ATMOSPHERE: Hydrogen/helium dominated; no conventional surface landing", "Which planet is famous for its rings?", Array("Saturn","Mars","Mercury","Venus"),0 ),
  PlanetDetail( "Uranus","Uranus (Arun)", cm.rgb(100,200,220),cm.rgb(40,90,100),cm.rgb(180,240,255), true,3, "Gravity: 8.69 m/s2", "Extreme axial tilt", "Ice giant with methane", "Distance: 2.87 Billion km | Orbit: 84 Years", "Radius: 25,362 km", "Mass: 8.681 x 10^25 kg", "Atmosphere: Hydrogen, helium and methane", "Extreme seasonal geometry", "ATMOSPHERE: Cold hydrogen/helium/methane envelope; very low temperatures", "Which planet has an extreme axial tilt?", Array("Uranus","Mars","Venus","Mercury"),0 ),
  PlanetDetail( "Neptune","Neptune (Varun)", cm.rgb(50,90,220),cm.rgb(20,40,100),cm.rgb(130,170,255), true,2, "Gravity: 11.15 m/s2", "Outermost major planet", "Supersonic winds", "Distance: 4.5 Billion km | Orbit: 165 Years", "Radius: 24,622 km", "Mass: 1.024 x 10^26 kg", "Dynamic atmosphere", "Moon highlight: Triton", "ATMOSPHERE: Hydrogen/helium/methane; extreme winds detected", "Which is the farthest major planet?", Array("Neptune","Mars","Jupiter","Mercury"),0 )
)

val keplerSolarSystemMap = Array(
  PlanetDetail( "Kepler-22b","Kepler-22b (KOI-087.01)", cm.rgb(70,180,150),cm.rgb(30,90,70),cm.rgb(120,240,200), false,2, "Potential habitable Super-Earth candidate", "Size: larger than Earth", "Orbital distance: ~0.85 AU", "Distance: ~635 Light Years", "Radius: ~15,290 km", "Mass: ~9.1 Earth masses", "Potential ocean world", "Discovered in 2011", "ATMOSPHERE: Potentially interesting; environment requires remote sensing", "What classification is Kepler-22b commonly given?", Array("Super-Earth","Mini-Moon","Small Star","Flat World"),0 ),
  PlanetDetail( "Kepler-16b","Kepler-16b (Kepler-16AB b)", cm.rgb(200,160,220),cm.rgb(90,70,110),cm.rgb(245,210,255), true,3, "Tatooine-like world", "Orbits two stars", "Gas giant composition", "Distance: ~245 Light Years", "Radius: ~53,200 km", "Mass: ~0.33 Jupiter", "K-dwarf + M-dwarf host pair", "Orbit period: 229 Days", "ATMOSPHERE: Gas-giant envelope around a circumbinary planet", "How many host stars does Kepler-16b orbit?", Array("2 Stars (Binary)","1 Star","5 Stars","None"),0 ),
  PlanetDetail( "Kepler-186f","Kepler-186f (KOI-571.05)", cm.rgb(140,210,110),cm.rgb(60,90,45),cm.rgb(200,255,170), false,2, "Earth-sized planet in habitable zone", "Potential candidate for liquid water", "Likely rocky terrain", "Distance: ~580 Light Years", "Radius: ~7,450 km", "Mass: ~1.4 Earth masses", "Host Star: red dwarf", "Orbit: ~130 Days", "ATMOSPHERE: Candidate world; composition remains uncertain", "Is Kepler-186f in the habitable zone?", Array("Yes","No","Unknown","Never"),0 ),
  PlanetDetail( "Kepler-452b","Kepler-452b (KOI-7016.01)", cm.rgb(90,140,200),cm.rgb(40,70,110),cm.rgb(160,210,255), false,1, "Earth cousin candidate", "Orbital year: ~385 days", "Super-Earth class", "Distance: ~1,800 Light Years", "Radius: ~10,380 km", "Mass: ~5 Earth masses", "Host star: Kepler-452", "Older than the Sun", "ATMOSPHERE: Remote sensing candidate with uncertain surface conditions", "What nickname is associated with Kepler-452b?", Array("Earth's Cousin","Moon Twin","Sun Twin","Mars Twin"),0 ),
  PlanetDetail( "Kepler-10b","Kepler-10b (KOI-072.01)", cm.rgb(220,90,90),cm.rgb(110,40,40),cm.rgb(255,160,160), false,3, "Extreme lava world", "Very close to host star", "Surface temperature >1300 C", "Distance: ~560 Light Years", "Radius: ~9,360 km", "Mass: ~3.2 Earth masses", "Very short orbit", "Tidal heating effects", "ATMOSPHERE: Extreme thermal environment; surface may be molten", "What covers the surface of Kepler-10b?", Array("Molten Lava / Rock","Ice Mountains","Cold Water","Green Forests"),0 ),
  PlanetDetail( "Kepler-62f","Kepler-62f (KOI-701.04)", cm.rgb(80,190,210),cm.rgb(35,90,100),cm.rgb(150,245,255), false,1, "Candidate Ocean World", "Approximately 40% larger than Earth", "K-type orange dwarf host", "Distance: ~1,200 Light Years", "Radius: ~8,980 km", "Mass: ~2.8 Earth masses", "Stable global ocean models", "High habitability interest", "ATMOSPHERE: Potential ocean-world climate; composition not confirmed", "What is Kepler-62f most likely modeled as?", Array("A Global Ocean","Only Sand Deserts","Gas Only","Plastic"),0 ),
  PlanetDetail( "Kepler-90i","Kepler-90i (KOI-351 i)", cm.rgb(230,150,60),cm.rgb(110,70,20),cm.rgb(255,210,130), false,2, "Part of an 8-planet system", "Machine-learning assisted discovery", "Sizzling rocky planet", "Distance: ~2,840 Light Years", "Radius: ~8,410 km", "Mass: ~2.5 Earth masses", "Orbit: ~14.4 Days", "Surface temp: ~430 C", "ATMOSPHERE: Hot rocky environment; remote sensing required", "Which technology helped discover Kepler-90i?", Array("Artificial Intelligence","Simple Camera","Microscope","Newspaper"),0 ),
  PlanetDetail( "K2-18b","K2-18b (EPIC 201912552 b)", cm.rgb(60,200,180),cm.rgb(20,90,80),cm.rgb(140,255,235), false,2, "Hycean planet candidate", "Hydrogen-rich atmosphere", "Potential ocean beneath atmosphere", "Distance: ~124 Light Years", "Radius: ~16,620 km", "Mass: ~8.6 Earth masses", "Carbon-bearing molecules studied", "JWST observations", "ATMOSPHERE: Hydrogen-rich; water-vapor and carbon-bearing chemistry studied", "What evidence has been studied in K2-18b's atmosphere?", Array("Water Vapor","Petrol","Diesel","Milk"),0 )
)

var currentSolarSystemId = 1; var selectedPlanet = "Mars"
def getActivePlanetList(): Array[String] = {
  if (currentSolarSystemId == 1) Array("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune")
  else Array("Kepler-22b","Kepler-16b","Kepler-186f","Kepler-452b","Kepler-10b","Kepler-62f","Kepler-90i","K2-18b"); 
}
def getActivePlanetDetailsMap(): Array[PlanetDetail] = {
  if (currentSolarSystemId == 1) realSolarSystemMap else keplerSolarSystemMap
}; 

var rotAngleX = 0.45; var rotAngleY = 0.0; var lastMouseX = 0.0; var lastMouseY = 0.0; var isDraggingSolarView = false; var planetLocked = false; var isTimerRunning = false; var isSpaceTravelling = false; var isLandingOnPlanet = false; var isLaunched = false; var isLanded = false
var customAlertMessage = ""; var quizFeedback = ""; var alertTimer = 0.0; var countdownTimer = 10.0; var spaceTravelProgress = 0.0; var landingProgress = 0.0; var doorOpeningProgress = 0.0; var flagPlantProgress = 0.0; var landingLegProgress = 0.0; var rocketCurrentX = 90.0; var rocketCurrentY = cheight - 170.0; var astronautOut = false; var infoPanelShown = false; var isFlagPlantedOnPlanet = false; var isAstronautReturningIn = false; var quizActive = false; var isMiniGameActive = false; var readyToReturnEarth = false; var infoPageNumber = 1; var activeGameMode = 1
var astroWalkToPlantX = 0.0; val plantedFlagX = cwidth / 2.0 - 80.0; val plantedFlagY = 155.0; var planetMissionPhase = 0; var planetMissionTimer = 0.0; var atmosphereScanProgress = 0.0; var asteroidEventProgress = 0.0; var robotLookAngle = 0.0; var missionReportText = ""; var missionReportVisible = false; var asteroidWarningVisible = false; var asteroidAlertPlayed = false; var cinematicFinished = false; var autoFlagPlanting = false; var autoFlagProgress = 0.0

def resetPlanetCinematic(): Unit = {
  planetMissionPhase = 0; planetMissionTimer = 0.0; atmosphereScanProgress = 0.0; asteroidEventProgress = 0.0; robotLookAngle = 0.0; missionReportText = ""; missionReportVisible = false; asteroidWarningVisible = false; asteroidAlertPlayed = false; cinematicFinished = false; autoFlagPlanting = false; autoFlagProgress = 0.0;
}

case class DynamicParticle( var x: Double, var y: Double, var vx: Double, var vy: Double, var life: Double, col: Color, var size: Double )
val exhaustRibbons = ArrayBuffer.empty[DynamicParticle]; val dustParticles = ArrayBuffer.empty[DynamicParticle]; val explosionParticles = ArrayBuffer.empty[DynamicParticle]; val sparkParticles = ArrayBuffer.empty[DynamicParticle]; val starCount = 850
val starX = Array.fill(starCount)(randomDouble(0,cwidth)); val starY = Array.fill(starCount)(randomDouble(0,cheight)); val starSize = Array.fill(starCount)(randomDouble(1.0,4.2))
val petalShower = ArrayBuffer.fill(90)( DynamicParticle( randomDouble(0,cwidth), randomDouble(cheight,cheight+200), randomDouble(-0.8,0.8), randomDouble(-2.8,-1.2), 1.0, cm.rgb(255,153,51), randomDouble(5,10) ) )

def addTricolorExhaust(x: Double,y: Double): Unit = {
  val t = globalTime * 14.0; 
  exhaustRibbons.append( DynamicParticle( x-8+math.sin(t)*6, y, randomDouble(-1.2,1.2), randomDouble(-4.0,-2.0), 1.0, cm.rgb(255,153,51), randomDouble(8,15) ) )
  exhaustRibbons.append( DynamicParticle( x,y, randomDouble(-0.8,0.8), randomDouble(-5.0,-3.0), 1.0, cm.rgb(255,255,255), randomDouble(10,17) ) )
  exhaustRibbons.append( DynamicParticle( x+8+math.cos(t)*6, y, randomDouble(-1.2,1.2), randomDouble(-4.0,-2.0), 1.0, cm.rgb(18,136,7), randomDouble(8,15) ) )
  for (_ <- 0 until 2) sparkParticles.append( DynamicParticle( x+randomDouble(-6,6), y, randomDouble(-1.4,1.4), randomDouble(-6,-3), 1.0, cm.rgb(255,220,80), randomDouble(2,4) ) ); 
}

def addLandingDust(x: Double,y: Double): Unit = {
  for (_ <- 0 until 5) dustParticles.append( DynamicParticle( x+randomDouble(-35,35), y, randomDouble(-2.8,2.8), randomDouble(0.2,1.8), 1.0, cm.rgb(210,180,140), randomDouble(4,10) ) ); 
}

def triggerExplosion(x: Double,y: Double): Unit = {
  for (_ <- 0 until 35) { val g = randomDouble(50,200).toInt; explosionParticles.append( DynamicParticle( x,y, randomDouble(-7,7), randomDouble(-7,7), 1.0, cm.rgb(255,g,0), randomDouble(5,14) ) ); }; 
}

def updateParticles(): Unit = {
  exhaustRibbons.filterInPlace(_.life > 0); dustParticles.filterInPlace(_.life > 0); explosionParticles.filterInPlace(_.life > 0); sparkParticles.filterInPlace(_.life > 0); 
  exhaustRibbons.foreach { p => p.x += p.vx + math.sin(globalTime*10)*1.5; p.y += p.vy; p.life -= 0.035; p.size += 0.4; }; 
  dustParticles.foreach { p => p.x += p.vx; p.y += p.vy; p.life -= 0.03; p.size += 0.25; }; 
  explosionParticles.foreach { p => p.x += p.vx; p.y += p.vy; p.life -= 0.045; p.size *= 0.94; }; 
  sparkParticles.foreach { p => p.x += p.vx; p.y += p.vy; p.life -= 0.05; }; 
  petalShower.foreach { p => p.y += p.vy; p.x += math.sin(globalTime*3+p.y*0.05)*1.4; if (p.y < 0) { p.y = cheight+20; p.x = randomDouble(0,cwidth); }; }; 
}; 

var miniGameTimer = 20.0; var isMiniGameTimerRunning = false; var devilPlayerX = cwidth/2.0-180.0; var devilPlayerY = 130.0; var devilPlayerVy = 0.0; var devilPlayerVx = 0.0; var isGrounded = false; var canDoubleJump = false; var jumpKeyPressedLastFrame = false; var isDevilKeyCollected = false; var trollKeyX = cwidth/2.0+140.0; var trollKeyY = 140.0; var exitDoorX = cwidth/2.0-180.0; var dodgerShipX = cwidth/2.0; var dodgerShipY = cheight/2.0-50.0; val dodgerCount = 5; var dodgerAstX = Array.fill(dodgerCount)(cwidth/2.0); var dodgerAstY = Array.fill(dodgerCount)(cheight/2.0+150.0); var dodgerAstSpeed = Array.fill(dodgerCount)(4.0); var dodgerScore = 0

def resetDodgerGame(): Unit = {
  val gx = cwidth/2.0-220.0; dodgerShipX = cwidth/2.0; dodgerScore = 0
  for (i <- 0 until dodgerCount) { dodgerAstX(i) = gx+30+randomDouble(0,380); dodgerAstY(i) = cheight/2.0+90+i*40; dodgerAstSpeed(i) = randomDouble(3.2,5.5); }; 
}; 

var landerX = cwidth/2.0; var landerY = cheight/2.0+50.0; var landerVy = 0.0; var landerVx = 0.0; var landerFuel = 100.0
def resetLanderGame(): Unit = { landerX = cwidth/2.0; landerY = cheight/2.0+60; landerVy = 0.2; landerVx = 0; landerFuel = 100; }; 

var isReturnTimerRunning = false; var isReturnLaunched = false; var isLandingOnEarth = false; var isLandedOnEarth = false; var isParadeSceneActive = false; var isParadeInPosition = false
var returnCountdownTimer = 5.0; var earthLandingProgress = 0.0; var earthAstronautWalkRightProgress = 0.0; var astroParadeWalkX = -50.0; var armyParadeWalkX = cwidth+50.0

def dieAndResetDevilLevel(msg: String): Unit = {
  playSoundFx(38,300); triggerExplosion(devilPlayerX,devilPlayerY); customAlertMessage = msg; alertTimer = 1.8; miniGameTimer = 20.0; isMiniGameTimerRunning = true; 
  getActivePlanetDetailsMap().foreach { pd => if (pd.name == selectedPlanet) activeGameMode = pd.gameModeType }
  if (activeGameMode == 1) { resetDodgerGame(); } else if (activeGameMode == 3) { resetLanderGame(); } else {
    val gx = cwidth/2.0-220; val gy = cheight/2.0-100; devilPlayerX = gx+30; devilPlayerY = gy+170; devilPlayerVy = 0; devilPlayerVx = 0; isGrounded = false; canDoubleJump = true; trollKeyX = gx+340; isDevilKeyCollected = false; exitDoorX = gx+40; 
  }; 
}

def draw3DSphere( canvas: CanvasDraw, cx: Double, cy: Double, size: Double, baseColor: Color, lightAccent: Color, rotAngle: Double = 0.0 ): Unit = {
  val layers = 11; val safeSize = math.max(1.0,math.abs(size))
  for (i <- 0 until layers) {
    val factor = math.max(0.01,1.0-i.toDouble/layers); val r = (baseColor.getRed+(lightAccent.getRed-baseColor.getRed)*(1-factor)).toInt; val g = (baseColor.getGreen+(lightAccent.getGreen-baseColor.getGreen)*(1-factor)).toInt; val b = (baseColor.getBlue+(lightAccent.getBlue-baseColor.getBlue)*(1-factor)).toInt; canvas.fill( math.max(0,math.min(255,r)), math.max(0,math.min(255,g)), math.max(0,math.min(255,b)) ); canvas.noStroke(); val off = (1-factor)*safeSize*0.28; val dim = math.max(0.1,safeSize*factor); canvas.ellipse(cx-off,cy+off,dim,dim); 
  }; 
  canvas.fill(255,255,255,45); val sx = cx+math.cos(rotAngle)*safeSize*0.25; val sy = cy+math.sin(rotAngle)*safeSize*0.15; canvas.ellipse( sx,sy, math.max(0.1,safeSize*0.35), math.max(0.1,safeSize*0.20) ); 
}

def project3D( x3d: Double, y3d: Double, z3d: Double, cx: Double, cy: Double ): (Double,Double) = {
  val cosY = math.cos(rotAngleY); val sinY = math.sin(rotAngleY); val cosX = math.cos(rotAngleX); val sinX = math.sin(rotAngleX); val x1 = x3d*cosY+z3d*sinY; val z1 = -x3d*sinY+z3d*cosY; val y2 = y3d*cosX-z1*sinX; (cx+x1,cy+y2); 
}

def drawAstronautPlayer(canvas: CanvasDraw,px: Double,py: Double): Unit = {
  canvas.pushMatrix(); canvas.translate(px,py); canvas.scale(1.0,-1.0); val bob = math.sin(globalTime*8)*4.5; canvas.translate(0,bob)
  if (selectedRobotType == 1) {
    canvas.fill(0,0,0,80); canvas.noStroke(); canvas.ellipse(0,32,28,7); canvas.stroke(0,255,255); canvas.strokeWeight(5.5); canvas.line(-7,0,-9,20); canvas.line(7,0,9,20); canvas.fill(15,25,45); canvas.stroke(0,255,255); canvas.strokeWeight(2.5); canvas.rect(-14,-16,28,28); canvas.fill(255,215,0); canvas.rect(-18,-4,6,12); canvas.rect(12,-4,6,12); val glow = math.max(0,math.min(255,(180+math.sin(globalTime*12)*75).toInt)); canvas.fill(0,glow,255); canvas.ellipse(0,-2,10,10); canvas.fill(20,35,65); canvas.stroke(0,255,255); canvas.strokeWeight(2); canvas.rect(-11,-32,22,14); canvas.fill(0,255,180); canvas.rect(-8,-28,16,5); canvas.fill(255,215,0); canvas.ellipse(0,-35,4,4); 
  } else if (selectedRobotType == 2) {
    canvas.fill(0,0,0,100); canvas.noStroke(); canvas.ellipse(0,34,34,9); canvas.stroke(255,140,0); canvas.strokeWeight(7.5); canvas.line(-9,0,-11,20); canvas.line(9,0,11,20); canvas.fill(45,20,20); canvas.stroke(255,60,0); canvas.strokeWeight(3); canvas.rect(-18,-18,36,30); canvas.fill(255,30,30); canvas.rect(-14,-8,28,5); canvas.fill(80,80,90); canvas.rect(-22,-12,6,16); canvas.rect(16,-12,6,16); canvas.fill(60,30,30); canvas.rect(-13,-34,26,14); canvas.fill(255,220,0); canvas.ellipse(-5,-28,5,5); canvas.ellipse(5,-28,5,5); 
  } else {
    canvas.fill(0,0,0,80); canvas.noStroke(); canvas.ellipse(0,32,26,6); canvas.fill(80,40,120); canvas.stroke(200,100,255); canvas.strokeWeight(2); canvas.ellipse(-10,15,10,10); canvas.ellipse(10,15,10,10); canvas.fill(160,40,220); canvas.stroke(255,150,255); canvas.ellipse(0,-8,28,28); val glow = math.max(0,math.min(255,(120+math.sin(globalTime*10)*125).toInt)); canvas.fill(0,glow,255); canvas.ellipse(0,-8,12,12); canvas.stroke(255,255,255); canvas.strokeWeight(2.5); canvas.line(-8,8,-12,16); canvas.line(8,8,12,16); canvas.stroke(0,255,255); canvas.line(0,-22,0,-34); canvas.fill(0,255,255); canvas.ellipse(0,-34,5,5); 
  }; canvas.popMatrix(); 
}

// GIANT GRAND TRICOLOR FLAG WITH CONTINUOUS ANIMATION AND THICKER POLE (AS REQUESTED)
def drawGiantGrandWavingTricolorFlag( canvas: CanvasDraw, fx: Double, fy: Double ): Unit = {
  val fw = 140.0; val fh = 24.0; 
  canvas.stroke(240,240,240); canvas.strokeWeight(9.0); canvas.line(fx, fy + 140, fx, fy - 35); 
  var x = 0.0
  while (x < fw) {
    val yo = math.sin(globalTime * 8.0 + x * 0.08) * 10.0; 
    canvas.strokeWeight(4.5); 
    canvas.stroke(18, 136, 7); canvas.line(fx + x, fy + yo, fx + x, fy + fh + yo); 
    canvas.stroke(255, 255, 255); canvas.line(fx + x, fy + fh + yo, fx + x, fy + 2 * fh + yo); 
    canvas.stroke(255, 153, 51); canvas.line(fx + x, fy + 2 * fh + yo, fx + x, fy + 3 * fh + yo); 
    x += 3; 
  }
  canvas.fill(0, 0, 200); canvas.noStroke(); 
  canvas.ellipse( fx + 70, fy + 1.5 * fh + math.sin(globalTime * 8.0 + 70 * 0.08) * 10.0, 22.0, 22.0 ); 
}

def drawWavingTricolorFlagWithAshokChakra( canvas: CanvasDraw, fx: Double, fy: Double ): Unit = {
  val fw = 52.0; val fh = 10.0; canvas.stroke(230,230,230); canvas.strokeWeight(6.0); canvas.line(fx,fy+48,fx,fy-15); var x = 0.0
  while (x < fw) {
    val yo = math.sin(waveAngle+x*0.12)*4.5; canvas.strokeWeight(2.8); canvas.stroke(18,136,7); canvas.line(fx+x,fy+yo,fx+x,fy+fh+yo); canvas.stroke(255,255,255); canvas.line(fx+x,fy+fh+yo,fx+x,fy+2*fh+yo); canvas.stroke(255,153,51); canvas.line(fx+x,fy+2*fh+yo,fx+x,fy+3*fh+yo); x += 2; 
  }
  canvas.fill(0,0,190); canvas.noStroke(); canvas.ellipse( fx+26, fy+1.5*fh+math.sin(waveAngle+26*0.12)*4.5, 8.5,8.5 ); 
}

def drawRobotSelectionScreen(canvas: CanvasDraw): Unit = {
  canvas.fill(5,8,22); canvas.noStroke(); canvas.rect(0,0,cwidth,cheight); canvas.stroke(0,255,255,30); canvas.strokeWeight(1)
  for (x <- 0 until cwidth.toInt by 40) canvas.line(x,0,x,cheight)
  for (y <- 0 until cheight.toInt by 40) canvas.line(0,y,cwidth,y)
  for (i <- 0 until 180) { canvas.stroke(255,255,255,170); canvas.strokeWeight(starSize(i)); canvas.point(starX(i),starY(i)); }; 
  val glow = math.max(1.0,4.0+math.sin(globalTime*6)*2.5); canvas.fill(255,215,0); canvas.stroke(255,153,51); canvas.strokeWeight(glow); canvas.text( "CHOOSE & UNLOCK YOUR ISRO ROBOT AVATAR", cwidth/2.0-200, cheight-35 ); canvas.fill(200,230,255); canvas.noStroke(); canvas.text( "Complete the Cyber Orb Challenge to unlock mission access.", cwidth/2.0-215, cheight-65 )
  val names = Array("1. NEON TITAN","2. MECHA WARRIOR","3. ALIEN ROVER"); val descs = Array("Speed & Cyber Core","Heavy Armor Plated","Alien Tech & Wheels"); val startX = cwidth/2.0-240; val boxY = cheight/2.0-85
  for (i <- 0 until 3) {
    val bx = startX+i*170; val selected = selectedRobotType == i+1; canvas.fill(12,22,45,245)
    if (selected) { canvas.stroke(0,255,255); canvas.strokeWeight(4); } else { canvas.stroke(80,90,130); canvas.strokeWeight(1.5); }; 
    canvas.rect(bx,boxY,150,185); val tmp = selectedRobotType; selectedRobotType = i+1; drawAstronautPlayer(canvas,bx+75,boxY+68); selectedRobotType = tmp; canvas.fill(255,255,255); canvas.text(names(i),bx+12,boxY+165); canvas.fill(0,255,200); canvas.text(descs(i),bx+10,boxY+145)
    if ( isMousePressed && mouseX >= bx && mouseX <= bx+150 && mouseY >= boxY && mouseY <= boxY+185 ) { selectedRobotType = i+1; playSoundFx(75,150); }; 
  }; 
  val btnX = cwidth/2.0-130; val btnY = 30.0
  if (!isRobotUnlocked) {
    canvas.fill(255,90,0); canvas.stroke(255,230,0); canvas.strokeWeight(2.5); canvas.rect(btnX,btnY,260,42); canvas.fill(255,255,255); canvas.text("UNLOCK & PLAY CHALLENGE",btnX+18,btnY+26)
    if ( isMousePressed && mouseX >= btnX && mouseX <= btnX+260 && mouseY >= btnY && mouseY <= btnY+42 ) { robotChallengeScore = 0; currentStoryStage = -3; playSoundFx(80,250); }; 
  } else {
    canvas.fill(18,136,7); canvas.stroke(255,255,255); canvas.rect(btnX,btnY,260,42); canvas.fill(255,255,255); canvas.text("CONFIRM ROBOT",btnX+62,btnY+26)
    if ( isMousePressed && mouseX >= btnX && mouseX <= btnX+260 && mouseY >= btnY && mouseY <= btnY+42 ) { currentStoryStage = -1; playSoundFx(80,250); }; 
  }; 
}

def drawRobotChallengeGameScreen(canvas: CanvasDraw): Unit = {
  canvas.fill(5,5,20); canvas.noStroke(); canvas.rect(0,0,cwidth,cheight)
  for (i <- 0 until 180) { canvas.stroke(255,255,255,180); canvas.strokeWeight(starSize(i)); canvas.point(starX(i),starY(i)); }; 
  val gx = cwidth/2.0; val gy = cheight/2.0; canvas.stroke(0,150,255,28); canvas.strokeWeight(1)
  for (x <- 0 until cwidth.toInt by 40) canvas.line(x,0,x,cheight)
  for (y <- 0 until cheight.toInt by 40) canvas.line(0,y,cwidth,y)
  val pulse = 15+math.sin(globalTime*10)*5; draw3DSphere( canvas,cyberEnergyX,cyberEnergyY,pulse, cm.rgb(0,255,255),cm.rgb(255,255,255),globalTime*5 ); drawAstronautPlayer(canvas,robotGamePlayerX,robotGamePlayerY); canvas.fill(255,215,0); canvas.stroke(0,255,255); canvas.strokeWeight(3)
  canvas.text("ROBOT ACCESS CHALLENGE",gx-130,cheight-35); canvas.fill(200,240,255); canvas.noStroke(); canvas.text( s"Catch $robotChallengeTarget Cyber Orbs using LEFT / RIGHT", gx-200,cheight-65 ); canvas.fill(0,255,150); canvas.text( s"ORBS: $robotChallengeScore / $robotChallengeTarget", gx-75,cheight-95 ); 
}

def drawWelcomeSirScreen(canvas: CanvasDraw): Unit = {
  canvas.fill(0,5,20); canvas.noStroke(); canvas.rect(0,0,cwidth,cheight)
  for (i <- 0 until 180) { canvas.stroke(255,255,255,180); canvas.strokeWeight(starSize(i)); canvas.point(starX(i),starY(i)); }; 
  val pg = math.max(1.0,5+math.sin(welcomeTimer*5)*3); 
  canvas.fill(255,140,0); canvas.stroke(255,140,0); canvas.strokeWeight(pg*0.5); canvas.text("WELCOME SIR", cwidth/2.0 - 75, cheight/2.0 + 80)
  canvas.fill(0,160,230); canvas.stroke(0,160,230); canvas.strokeWeight(pg*0.4); canvas.text("TO ISRO GAGANYAAN 3D SPACE MISSION CODE", cwidth/2.0 - 200, cheight/2.0 + 35); 
  canvas.fill(0,160,230); canvas.noStroke(); canvas.text("Features: Real Planetary Data | 3D Controls | Exoplanets System", cwidth/2.0 - 250, cheight/2.0 - 10); 
  canvas.text("Advanced Mini-Games | Dynamic Astronaut Simulations | Patriotic Finale", cwidth/2.0 - 275, cheight/2.0 - 45); 
  val timeLeft = (7.5 - welcomeTimer).max(0.0).toInt + 1
  canvas.text(s"Initializing Mission Call Letter... (${timeLeft}s)", cwidth/2.0 - 145, cheight/2.0 - 100); 
}

def drawISROLetterScreen(canvas: CanvasDraw): Unit = {
  canvas.fill(5,10,25); canvas.noStroke(); canvas.rect(0,0,cwidth,cheight); val lx = cwidth/2.0-260; val ly = cheight/2.0-150; canvas.fill(15,25,50,240); canvas.stroke(255,153,51); canvas.strokeWeight(3); canvas.rect(lx,ly,520,310); canvas.fill(255,215,0); canvas.text("ISRO OFFICIAL INVITATION LETTER",lx+100,ly+25); val txt = officialLetterText.substring(0,math.min(letterTextTyped,officialLetterText.length)); val lines = txt.split("\n"); var y = ly+55
  for (line <- lines) { canvas.fill(220,240,255); canvas.text(line,lx+20,y); y += 18; }
  if (letterTextTyped >= officialLetterText.length) {
    val bx = lx+160; val by = ly-40; canvas.fill(18,136,7); canvas.stroke(255,255,255); canvas.rect(bx,by,200,35); canvas.fill(255,255,255); canvas.text("ACCEPT MISSION",bx+35,by+22)
    if ( isMousePressed && mouseX >= bx && mouseX <= bx+200 && mouseY >= by && mouseY <= by+35 ) { currentStoryStage = 1; playSoundFx(75,250); }; 
  }; 
}

def drawPartOneTitleScreen(canvas: CanvasDraw): Unit = {
  canvas.fill(0,0,0); canvas.noStroke(); canvas.rect(0,0,cwidth,cheight); val glow = math.max(1.0,5+math.sin(titleCardTimer*4)*3); canvas.fill(0,255,255); canvas.stroke(0,180,255); canvas.strokeWeight(glow); canvas.text("CHAPTER 1",cwidth/2.0-45,cheight/2.0+40); canvas.fill(255,215,0); canvas.stroke(255,153,51); canvas.text("PART 1: THE ASTRONAUT",cwidth/2.0-110,cheight/2.0-10); canvas.fill(200,200,200); canvas.noStroke(); canvas.text("Preparing Gaganyaan Space Launch Sequence...",cwidth/2.0-140,cheight/2.0-60); 
}

def drawFullSolarSystem(canvas: CanvasDraw): Unit = {
  val centerX = cwidth/2.0-20; val centerY = cheight/2.0-10; val list = getActivePlanetList(); val speeds = Array(0.70,0.55,0.42,0.35,0.28,0.22,0.17,0.12); canvas.fill(0,255,255,180); canvas.text("Drag mouse to rotate Solar System in 3D!",centerX-130,cheight-25)
  for (i <- list.indices) { val radius = i*28+50; canvas.noFill(); canvas.stroke(0,255,255,45); canvas.strokeWeight(1); canvas.ellipse(centerX,centerY,radius*2,radius*1.2); }; 
  draw3DSphere( canvas,centerX,centerY, math.max(10,72+math.sin(globalTime*6)*10), cm.rgb(255,110,0), cm.rgb(255,255,190), globalTime*2 )
  val map = getActivePlanetDetailsMap()
  for (i <- list.indices) {
    val name = list(i); val radius = i*28+50; val angle = globalTime*speeds(i); val x3d = radius*math.cos(angle); val z3d = radius*math.sin(angle); val (px,py) = project3D(x3d,0,z3d,centerX,centerY); var c1 = cm.rgb(200,200,200); var c2 = cm.rgb(255,255,255); var ring = false; map.foreach { pd => if (pd.name == name) { c1 = pd.surfaceCol; c2 = pd.accent3D; ring = pd.hasRings; }; }; val size = if (name == "Earth" || name == "Kepler-22b" || name == "Kepler-452b") 18.0 else 12.0+i*0.4
    if (ring) { canvas.noFill(); canvas.stroke(245,220,180,210); canvas.strokeWeight(3); canvas.ellipse(px,py,size*2.2,size*0.8); }; 
    draw3DSphere(canvas,px,py,size,c1,c2,globalTime*3+i)
    if ( !planetLocked && isMousePressed && math.abs(mouseX-px)<20 && math.abs(mouseY-py)<20 ) { selectedPlanet = name; }
    if (selectedPlanet == name) { canvas.noFill(); canvas.stroke(0,255,255); canvas.strokeWeight(3); canvas.ellipse(px,py,size+16,size+16); }; 
  }; 
}

def drawPlanetList(canvas: CanvasDraw): Unit = {
  canvas.fill(15,20,45,245); canvas.stroke(0,255,255); canvas.strokeWeight(2.5); canvas.rect(10,10,230,380); canvas.fill(255,255,255); canvas.text("Select Target Planet",15,30); canvas.fill(0,180,220); canvas.rect(15,40,220,28); canvas.fill(255,255,255); canvas.text( if (currentSolarSystemId == 1) "System: Real Solar" else "System: Kepler Exoplanets", 18,59 )
  if ( isMousePressed && mouseX >= 15 && mouseX <= 235 && mouseY >= 40 && mouseY <= 68 ) { currentSolarSystemId = if (currentSolarSystemId == 1) 2 else 1; selectedPlanet = if (currentSolarSystemId == 1) "Mars" else "Kepler-22b"; }; 
  val list = getActivePlanetList(); val map = getActivePlanetDetailsMap()
  for (i <- list.indices) {
    val py = 88+i*32; var display = list(i); map.foreach { pd => if (pd.name == list(i)) display = pd.realName }
    if ( !planetLocked && isMousePressed && mouseX >= 15 && mouseX <= 235 && mouseY >= py-12 && mouseY <= py+12 ) { selectedPlanet = list(i); }
    if (selectedPlanet == list(i)) { canvas.fill(0,180,80); canvas.rect(15,py-12,220,26); canvas.fill(255,255,255); } else { canvas.fill(210,210,210); }; canvas.text(display,18,py); 
  }; 
}

def draw3DPlanetPreview(canvas: CanvasDraw): Unit = {
  val bx = cwidth-250.0; val by = 15.0; canvas.fill(15,20,45,245); canvas.stroke(0,255,255); canvas.strokeWeight(2); canvas.rect(bx,by,235,225); canvas.fill(255,255,255); canvas.text(s"3D Hologram: $selectedPlanet",bx+12,by+22); val cx = bx+117.5; val cy = by+130; var c1 = cm.rgb(200,70,40); var c2 = cm.rgb(255,130,100); var ring = false; getActivePlanetDetailsMap().foreach { pd => if (pd.name == selectedPlanet) { c1 = pd.surfaceCol; c2 = pd.accent3D; ring = pd.hasRings; }; }
  if (ring) { canvas.noFill(); canvas.stroke(220,210,180,230); canvas.strokeWeight(8); canvas.ellipse(cx,cy,185,54); }; 
  draw3DSphere(canvas,cx,cy,115,c1,c2,globalTime*2.5); 
}

def drawPlanetSurfaceBackground(canvas: CanvasDraw): Unit = {
  var surface = cm.rgb(100,30,15); var dark = cm.rgb(50,10,5); getActivePlanetDetailsMap().foreach { pd => if (pd.name == selectedPlanet) { surface = pd.surfaceCol; dark = pd.darkSurfCol; }; }; canvas.fill(surface); canvas.noStroke(); canvas.rect(0,0,cwidth,140); canvas.fill(dark); canvas.ellipse(120,40,60,20); canvas.ellipse(420,60,90,25); canvas.ellipse(650,45,70,22); canvas.rect(0,120,cwidth,25); canvas.fill(40,40,40,130)
  for (i <- 0 until 10) canvas.ellipse(50+i*75,135+math.sin(i*3.2)*5,18+i%4*5,7)
  if (isFlagPlantedOnPlanet) drawWavingTricolorFlagWithAshokChakra(canvas,plantedFlagX,plantedFlagY); 
  drawPlanetMissionAsteroids(canvas); 
}

def drawPlanetMissionAsteroids(canvas: CanvasDraw): Unit = {
  if (planetMissionPhase == 5) {
    val cx = cwidth/2.0; val cy = 220.0; val a1 = asteroidEventProgress*420; val a2 = asteroidEventProgress*500; val a3 = asteroidEventProgress*560; 
    draw3DSphere( canvas,cx-250+a1,cy+60-a1*0.3,28, cm.rgb(120,80,60),cm.rgb(220,150,100),globalTime*4 ); 
    draw3DSphere( canvas,cx+280-a2,cy+30+a2*0.2,21, cm.rgb(100,90,85),cm.rgb(220,170,120),globalTime*5 ); 
    draw3DSphere( canvas,cx-320+a3,cy-20+a3*0.15,17, cm.rgb(150,90,70),cm.rgb(255,180,120),globalTime*3 ); 
    canvas.fill(255,70,50); canvas.stroke(255,150,80); canvas.strokeWeight(2); canvas.text( "WARNING: ASTEROID FIELD DETECTED!", cwidth/2.0-145,180 ); 
  }; 
}

def startPlanetCinematic(): Unit = { resetPlanetCinematic(); doorOpeningProgress = 0; astronautOut = false; }

def updatePlanetCinematic(): Unit = {
  if (!isLanded || isInsideUnderRocketControl || isReturnLaunched || isLandingOnEarth) return
  if (planetMissionPhase == 0 && doorOpeningProgress >= 1.0 && !cinematicFinished) { planetMissionPhase = 1; planetMissionTimer = 0; astronautOut = true; }; 
  planetMissionTimer += 0.04
  if (planetMissionPhase == 1) {
    if (astroWalkToPlantX < 95) { astroWalkToPlantX += 1.8; } else { planetMissionPhase = 2; planetMissionTimer = 0; playSoundFx(72,120); }; 
  } else if (planetMissionPhase == 2) {
    atmosphereScanProgress = math.min(1.0,atmosphereScanProgress+0.012); robotLookAngle = math.sin(planetMissionTimer*2.5)*0.18
    if (planetMissionTimer > 3.4) {
      planetMissionPhase = 3; planetMissionTimer = 0; missionReportVisible = true; var report = "ATMOSPHERIC SCAN COMPLETE"; getActivePlanetDetailsMap().foreach { pd => if (pd.name == selectedPlanet) report = pd.atmosphereLine }; missionReportText = report; playSoundFx(80,200); 
    }; 
  } else if (planetMissionPhase == 3) {
    if (planetMissionTimer > 4.5) { missionReportVisible = false; planetMissionPhase = 4; planetMissionTimer = 0; }; 
  } else if (planetMissionPhase == 4) {
    if (astroWalkToPlantX > 0) { astroWalkToPlantX -= 2.2; } else if (doorOpeningProgress > 0) { doorOpeningProgress -= 0.025; } else { astronautOut = false; planetMissionPhase = 5; planetMissionTimer = 0; asteroidEventProgress = 0; asteroidWarningVisible = true; if (!asteroidAlertPlayed) { playSoundFx(48,350); asteroidAlertPlayed = true; }; }; 
  } else if (planetMissionPhase == 5) {
    asteroidEventProgress = math.min(1.0,asteroidEventProgress+0.018)
    if (planetMissionTimer > 3.5) { asteroidWarningVisible = false; planetMissionPhase = 6; planetMissionTimer = 0; doorOpeningProgress = 0; astronautOut = true; astroWalkToPlantX = 0; playSoundFx(84,160); }; 
  } else if (planetMissionPhase == 6) {
    if (doorOpeningProgress < 1) { doorOpeningProgress += 0.025; } else if (astroWalkToPlantX < 70) { astroWalkToPlantX += 2; } else { planetMissionPhase = 7; planetMissionTimer = 0; }; 
  } else if (planetMissionPhase == 7) {
    if (astroWalkToPlantX < 150) { astroWalkToPlantX += 1.6; } else { planetMissionPhase = 8; planetMissionTimer = 0; autoFlagPlanting = true; autoFlagProgress = 0; }; 
  } else if (planetMissionPhase == 8) {
    if (autoFlagPlanting) {
      autoFlagProgress = math.min(1.0,autoFlagProgress+0.02)
      if (autoFlagProgress >= 1) { autoFlagPlanting = false; isFlagPlantedOnPlanet = true; flagPlantProgress = 1; playSoundFx(85,300); missionReportText = "MISSION MILESTONE: INDIAN TRICOLOR PLANTED ON "+selectedPlanet; missionReportVisible = true; planetMissionPhase = 9; planetMissionTimer = 0; }; 
    }; 
  } else if (planetMissionPhase == 9) {
    if (astroWalkToPlantX > 0) { astroWalkToPlantX -= 2.0; } else if (doorOpeningProgress > 0) { doorOpeningProgress -= 0.025; } else { astronautOut = false; cinematicFinished = true; planetMissionPhase = 10; missionReportVisible = false; }
  }; 
}

def drawAtmosphereScanner(canvas: CanvasDraw): Unit = {
  if (planetMissionPhase == 2) {
    val sx = cwidth/2.0; val sy = 235.0; val beam = 40+math.sin(globalTime*8)*12; canvas.noFill(); canvas.stroke(0,255,255,120); canvas.strokeWeight(2); canvas.ellipse(sx,sy,beam,beam); canvas.ellipse(sx,sy,beam*2,beam*2); canvas.stroke(0,255,255,80); canvas.line( rocketCurrentX-astroWalkToPlantX, rocketCurrentY+10, sx+robotLookAngle*180, sy+90 ); canvas.fill(0,255,255); canvas.noStroke(); canvas.text("ROBOT ATMOSPHERE SCAN",sx-90,sy+65); canvas.text( s"SCAN ${(atmosphereScanProgress*100).toInt}%", sx-30,sy+85 ); 
  }; 
}

def drawMissionReport(canvas: CanvasDraw): Unit = {
  if (missionReportVisible && !isReturnLaunched && !isLandingOnEarth && !isLandedOnEarth) {
    val w = 500.0; val h = 90.0; val x = cwidth/2.0-w/2; val y = cheight-120; canvas.fill(5,25,55,240); canvas.stroke(0,255,255); canvas.strokeWeight(2.5); canvas.rect(x,y,w,h); canvas.fill(255,215,0); canvas.noStroke(); canvas.text( s"ISRO MISSION REPORT ? $selectedPlanet", x+18,y+22 ); canvas.fill(210,245,255); canvas.text(missionReportText,x+18,y+49); canvas.fill(0,255,170); canvas.text("REMOTE SCIENCE LINK ACTIVE", x+18,y+72 ); 
  }; 
}

def drawRobotCinematic(canvas: CanvasDraw): Unit = {
  if (isLanded && astronautOut && !isLandedOnEarth) {
    canvas.pushMatrix(); canvas.translate(rocketCurrentX-astroWalkToPlantX,rocketCurrentY-10)
    if (planetMissionPhase == 2) { canvas.rotate(robotLookAngle); }; 
    drawAstronautPlayer(canvas,0,0); canvas.popMatrix(); 
  }; 
}

def drawISRORocket(canvas: CanvasDraw): Unit = {
  canvas.pushMatrix(); canvas.translate(rocketCurrentX,rocketCurrentY)
  if (isLanded) { canvas.fill(0,0,0,100); canvas.noStroke(); canvas.ellipse(0,-45,50,12); }
  if ( isSpaceTravelling || isLandingOnPlanet || isReturnLaunched || isLandingOnEarth ) {
    val flame = 25+math.sin(globalTime*20)*8+randomDouble(0,12); canvas.fill(255,80,0,200); canvas.ellipse(0,-58,22,math.max(1,flame)); canvas.fill(255,180,0,230); canvas.ellipse(0,-54,14,flame*0.7); canvas.fill(255,255,200,255); canvas.ellipse(0,-50,6,flame*0.4); 
  }; 
  canvas.fill(245,245,255); canvas.stroke(0,255,255); canvas.strokeWeight(2.8); canvas.rect(-12,-45,24,45); canvas.fill(200,200,215); canvas.noStroke(); canvas.rect(0,-45,12,45); canvas.fill(255,255,255,80); canvas.rect(-10,-42,4,40); canvas.fill(225,65,65); canvas.stroke(255,210,210); canvas.strokeWeight(2); canvas.beginShape(); canvas.vertex(0,25); canvas.vertex(-12,0); canvas.vertex(12,0); canvas.endShape(); canvas.fill(225,225,235); canvas.stroke(100,100,130); canvas.rect(-20,-35,7,35); canvas.rect(13,-35,7,35); canvas.stroke(210,210,210); canvas.strokeWeight(3.8); canvas.line(-12,-40,-12-landingLegProgress*15,-45-landingLegProgress*15); canvas.line(-12-landingLegProgress*15,-45-landingLegProgress*15,-12-landingLegProgress*22,-45-landingLegProgress*15); canvas.line(12,-40,12+landingLegProgress*15,-45-landingLegProgress*15); canvas.line(12+landingLegProgress*15,-45-landingLegProgress*15,12+landingLegProgress*22,-45-landingLegProgress*15)
  if (isLanded && !isParadeSceneActive && !isReturnLaunched && !isLandingOnEarth) {
    canvas.fill(40,40,55); canvas.rect(-8,-30,16,math.max(0.1,20*doorOpeningProgress)); canvas.fill(0,255,180,150); canvas.ellipse(0,-12,6+math.sin(globalTime*8)*2,6+math.sin(globalTime*8)*2); 
    drawRobotCinematic(canvas)
    if (cinematicFinished) {
      val bx = -105.0; val by = -78.0; val bw = 210.0; val bh = 38.0; val pulse = math.max(1.0,2+math.sin(globalTime*7)*1.5); canvas.fill(0,170,255,245); canvas.stroke(255,255,255); canvas.strokeWeight(pulse); canvas.rect(bx,by,bw,bh); canvas.fill(255,255,255); canvas.noStroke(); canvas.text("SEE UNDER ROCKET",bx+38,by+25); val localX = mouseX-rocketCurrentX; val localY = mouseY-rocketCurrentY
      if ( isMousePressed && localX >= bx && localX <= bx+bw && localY >= by && localY <= by+bh ) { infoPanelShown = true; playSoundFx(82,220); }; 
    }
  }; canvas.popMatrix(); 
}

def drawQuizPopup(canvas: CanvasDraw): Unit = {
  if (!quizActive) return
  val qx = cwidth/2.0-225; val qy = cheight/2.0-130; canvas.fill(15,20,45,250); canvas.stroke(255,215,0); canvas.strokeWeight(3.5); canvas.rect(qx,qy,450,260); canvas.fill(255,255,255); canvas.text(s"ISRO Quiz: $selectedPlanet",qx+20,qy+25); var question = ""; var opts = Array("A","B","C","D"); var correct = 0; getActivePlanetDetailsMap().foreach { pd => if (pd.name == selectedPlanet) { question = pd.quizQuestion; opts = pd.quizOptions; correct = pd.correctOpt; }; }; canvas.fill(255,225,110); canvas.text(question,qx+15,qy+52)
  for (i <- opts.indices) {
    val oy = qy+75+i*32; canvas.fill(30,45,85); canvas.stroke(0,255,255); canvas.rect(qx+15,oy,420,26); canvas.fill(255,255,255); canvas.text(opts(i),qx+25,oy+18)
    if ( isMousePressed && mouseX >= qx+15 && mouseX <= qx+435 && mouseY >= oy && mouseY <= oy+26 ) {
      if (i == correct) { quizActive = false; isSpaceTravelling = true; spaceTravelProgress = 0; quizFeedback = ""; resetPlanetCinematic(); customAlertMessage = "Correct Answer! Launch sequence initiated."; alertTimer = 3; } else { quizFeedback = "Incorrect Answer! Try Again."; playSoundFx(42,80); }; 
    }; 
  }
  if (quizFeedback != "") { canvas.fill(255,60,60); canvas.text(quizFeedback,qx+20,qy+235); }; 
}

def drawPlanetInfoPopup(canvas: CanvasDraw): Unit = {
  if (!infoPanelShown) return
  val ix = cwidth/2.0-230; val iy = cheight/2.0-130; canvas.fill(15,20,45,250); canvas.stroke(0,255,255); canvas.strokeWeight(3.5); canvas.rect(ix,iy,460,260); var pd = getActivePlanetDetailsMap()(0); getActivePlanetDetailsMap().foreach { p => if (p.name == selectedPlanet) pd = p }; canvas.fill(255,215,0); canvas.text(s"Mission Info: $selectedPlanet ? Page $infoPageNumber/3",ix+20,iy+25); 
  val lines = if (infoPageNumber == 1) Array(pd.infoLine1,pd.infoLine2,pd.infoLine3) else if (infoPageNumber == 2) Array(pd.infoLine4,pd.infoLine5,pd.infoLine6) else Array(pd.infoLine7,pd.infoLine8,pd.atmosphereLine); 
  canvas.fill(220,240,250)
  for (i <- lines.indices) canvas.text(lines(i),ix+20,iy+80+i*30); 
  canvas.fill(45,110,210); canvas.rect(ix+20,iy+195,100,35); canvas.fill(255,255,255); canvas.text("Next Page",ix+30,iy+218); 
  canvas.fill(0,190,90); canvas.rect(ix+140,iy+195,150,35); canvas.fill(255,255,255); canvas.text("Play Game",ix+170,iy+218); 
  canvas.fill(190,45,45); canvas.rect(ix+300,iy+195,130,35); canvas.fill(255,255,255); canvas.text("Close X",ix+325,iy+218); 
  if ( isMousePressed && mouseX >= ix+20 && mouseX <= ix+120 && mouseY >= iy+195 && mouseY <= iy+230 ) { infoPageNumber = if (infoPageNumber == 3) 1 else infoPageNumber+1; }
  if ( isMousePressed && mouseX >= ix+140 && mouseX <= ix+290 && mouseY >= iy+195 && mouseY <= iy+230 ) { infoPanelShown = false; isMiniGameActive = true; miniGameTimer = 20; isMiniGameTimerRunning = true; dieAndResetDevilLevel("Game Started!"); }
  if ( isMousePressed && mouseX >= ix+300 && mouseX <= ix+430 && mouseY >= iy+195 && mouseY <= iy+230 ) { infoPanelShown = false; readyToReturnEarth = true; }; 
}

def drawMiniGames(canvas: CanvasDraw): Unit = {
  if (!isMiniGameActive) return
  val gx = cwidth/2.0-220; val gy = cheight/2.0-100; canvas.fill(10,10,25,250); canvas.stroke(0,255,210); canvas.strokeWeight(3.5); canvas.rect(gx,gy,440,210); canvas.fill(255,65,65); canvas.text(s"Time: ${miniGameTimer.toInt}s",gx+290,gy+190)
  if (activeGameMode == 1) {
    canvas.fill(255,255,255); canvas.text(s"Asteroid Dodger | Score: $dodgerScore/80",gx+15,gy+190); canvas.fill(0,255,210); canvas.stroke(255,255,255); canvas.beginShape(); canvas.vertex(dodgerShipX,dodgerShipY+12); canvas.vertex(dodgerShipX-12,dodgerShipY-12); canvas.vertex(dodgerShipX+12,dodgerShipY-12); canvas.endShape()
    for (i <- 0 until dodgerCount) draw3DSphere( canvas,dodgerAstX(i),dodgerAstY(i),22, cm.rgb(190,110,70),cm.rgb(255,190,110),globalTime*2 ); 
  } else if (activeGameMode == 3) {
    val padX = gx+180; val padY = gy+20; canvas.fill(255,255,255); canvas.text(s"Lander Fuel: ${landerFuel.toInt}%",gx+15,gy+190); canvas.fill(0,255,110); canvas.rect(padX,padY,80,8); canvas.fill(255,255,255); canvas.text("TARGET PAD",padX+5,padY+18); canvas.fill(255,210,0); canvas.rect(landerX-12,landerY-12,24,24)
    if (isKeyPressed(Kc.VK_UP) && landerFuel > 0) { canvas.fill(255,110,0); canvas.ellipse(landerX,landerY-20,10,16); }; 
  } else {
    canvas.fill(255,255,255); canvas.text("Rover Adventure Game",gx+15,gy+190)
    if (!isDevilKeyCollected) { canvas.fill(255,215,0); canvas.ellipse(trollKeyX,trollKeyY,16,16); }; 
    canvas.fill(if (isDevilKeyCollected) cm.rgb(0,255,160) else cm.rgb(110,110,110)); canvas.rect(exitDoorX,gy+135,30,40); drawAstronautPlayer(canvas,devilPlayerX,devilPlayerY); 
  }; 
}

def drawLaunchAndUI(canvas: CanvasDraw): Unit = {
  if (!isSpaceTravelling && !isLaunched) {
    val bx = cwidth-250.0; val by = 250.0
    if (!isTimerRunning && !quizActive) {
      canvas.fill(255,90,0); canvas.stroke(255,230,0); canvas.strokeWeight(4.5); canvas.rect(bx,by,235,45); canvas.fill(255,255,255); canvas.text("Launch ISRO Mission",bx+22,by+28)
      if ( isMousePressed && mouseX >= bx && mouseX <= bx+235 && mouseY >= by && mouseY <= by+45 ) { planetLocked = true; isTimerRunning = true; countdownTimer = 10; playSoundFx(72,120); }; 
    }
    if (isTimerRunning) { canvas.fill(255,215,0); canvas.text(s"Launch Countdown: ${countdownTimer.toInt}s",bx+15,by+28); }; 
  }
  if ( readyToReturnEarth && !isReturnLaunched && !isAstronautReturningIn && !isLandingOnEarth ) {
    canvas.fill(18,136,7); canvas.stroke(0,255,160); canvas.rect(cwidth/2.0-120,25,240,45); canvas.fill(255,255,255); canvas.text("Return to Earth",cwidth/2.0-70,52)
    if ( isMousePressed && mouseX >= cwidth/2.0-120 && mouseX <= cwidth/2.0+120 && mouseY >= 25 && mouseY <= 70 ) { 
      isAstronautReturningIn = true; 
      isFlagPlantedOnPlanet = false; 
    }; 
  }
  if (isReturnTimerRunning) { canvas.fill(255,215,0); canvas.text( s"Return Time: ${returnCountdownTimer.toInt}s", cwidth/2.0-110,80 ); }; 
}

def drawPersonCharacter( canvas: CanvasDraw, px: Double, py: Double, isArmy: Boolean, salute: Boolean ): Unit = {
  canvas.pushMatrix(); canvas.translate(px,py); val swing = if (startAnthemPhase) 0 else math.sin(globalTime*10)*5; canvas.fill(0,0,0,90); canvas.noStroke(); canvas.ellipse(0,-25,24,8); canvas.stroke(if (isArmy) cm.rgb(35,65,22) else cm.rgb(190,190,210)); canvas.strokeWeight(5.5); canvas.line(-5,0,-6+swing,-22); canvas.line(5,0,6-swing,-22); canvas.fill(15,15,15); canvas.noStroke(); canvas.rect(-9,-25,7,4); canvas.rect(3,-25,7,4); canvas.fill(if (isArmy) cm.rgb(80,110,50) else cm.rgb(245,245,255)); canvas.stroke(0,255,255); canvas.rect(-10,0,20,32); canvas.fill(if (isArmy) cm.rgb(215,165,125) else cm.rgb(255,255,255)); canvas.ellipse(0,42,18,18); canvas.stroke(if (isArmy) cm.rgb(80,110,50) else cm.rgb(230,230,240)); canvas.strokeWeight(4.5)
  if (salute) { canvas.line(-8,22,-12,4); canvas.line(8,22,14,34); canvas.line(14,34,8,44); } else { canvas.line(-8,22,-12,4); canvas.line(8,22,12,4); }; canvas.popMatrix(); 
}

def drawEarthFirstLandingScene(canvas: CanvasDraw): Unit = {
  canvas.fill(34,139,34); canvas.noStroke(); canvas.rect(0,0,cwidth,140); canvas.fill(30,110,210); canvas.rect(0,125,cwidth,25); drawISRORocket(canvas)
  if (isLandedOnEarth) drawPersonCharacter( canvas, rocketCurrentX+earthAstronautWalkRightProgress, 165,false,false ); 
}

def drawPatrioticFullText(canvas: CanvasDraw): Unit = {
  if (charIndex > 0) {
    val lines = fullAnthemText.substring(0,math.min(charIndex,fullAnthemText.length)).split("\n"); var y = cheight-40
    for (line <- lines) { canvas.fill(255,215,0,110); canvas.text(line,21,y-1); canvas.fill(255,255,255); canvas.text(line,20,y); y -= 18; }; 
  }; 
}

def drawParadeGroundScene(canvas: CanvasDraw): Unit = {
  canvas.fill(34,139,34); canvas.noStroke(); canvas.rect(0,0,cwidth,140); canvas.fill(25,95,25); canvas.rect(0,125,cwidth,25)
  for (i <- 0 until 3) { drawPersonCharacter(canvas,astroParadeWalkX-i*35,165,false,isParadeInPosition); drawPersonCharacter(canvas,armyParadeWalkX+i*35,165,true,isParadeInPosition); }; 
  petalShower.foreach { p => canvas.fill(255,153,51,180); canvas.ellipse(p.x,p.y,p.size,p.size*0.6); }; 
  
  drawGiantGrandWavingTricolorFlag(canvas, cwidth/2.0 - 70, 150); 
  drawPatrioticFullText(canvas); 
}

def drawCustomAlertPopup(canvas: CanvasDraw): Unit = {
  if (alertTimer > 0 && customAlertMessage != "") { canvas.fill(230,35,35,240); canvas.stroke(255,255,255); canvas.strokeWeight(2.2); canvas.rect(cwidth/2.0-210,80,420,50); canvas.fill(255,255,255); canvas.text(customAlertMessage,cwidth/2.0-195,110); }; 
}

def drawCleanEndScreen(canvas: CanvasDraw): Unit = {
  canvas.fill(0,0,0); canvas.noStroke(); canvas.rect(0,0,cwidth,cheight)
  for (i <- 0 until 120) { canvas.fill(255,255,255,150); canvas.ellipse(starX(i),starY(i),starSize(i),starSize(i)); }; 
  val cx = cwidth/2.0; val cy = cheight/2.0; canvas.fill(255,215,0); canvas.stroke(255,160,0); canvas.strokeWeight(4); canvas.text("Thank You! Mission Complete (THE END)",cx-150,cy+80); canvas.fill(0,255,255); canvas.stroke(0,190,255); canvas.text("Made by Yadnesh Shahare and Bhavesh Shahare",cx-200,cy+30); canvas.fill(255,255,255); canvas.text("Jai Hind! Jai Bharat!",cx-80,cy-20); canvas.fill(180,100,255); canvas.text("Next part in my brother PC",cx-115,cy-80); 
}

def updateState(): Unit = {
  globalTime += 0.022; waveAngle += 0.22; chakraRotation += 0.22; neonGlowPhase += 0.22; updateParticles()
  if (currentStoryStage == -3) {
    if (isKeyPressed(Kc.VK_LEFT) && robotGamePlayerX > 80) robotGamePlayerX -= 7.5
    if (isKeyPressed(Kc.VK_RIGHT) && robotGamePlayerX < cwidth-80) robotGamePlayerX += 7.5; cyberEnergyY += cyberEnergyVy
    if (cyberEnergyY < 50) { cyberEnergyY = cheight-80; cyberEnergyX = randomDouble(100,cwidth-100); }
    if ( math.abs(robotGamePlayerX-cyberEnergyX) < 40 && math.abs(robotGamePlayerY-cyberEnergyY) < 40 ) {
      robotChallengeScore += 1; playSoundFx(78,120); cyberEnergyY = cheight-80; cyberEnergyX = randomDouble(100,cwidth-100)
      if (robotChallengeScore >= robotChallengeTarget) { isRobotUnlocked = true; currentStoryStage = -1; playSoundFx(84,350); }; 
    }; return; 
  }
  if (currentStoryStage == -2) return
  if (currentStoryStage == -1) { welcomeTimer += 0.04; if (welcomeTimer >= 7.5) currentStoryStage = 0; return; }
  if (currentStoryStage == 0) { letterTimer += 0.04; if (letterTimer >= 0.03 && letterTextTyped < officialLetterText.length) { letterTextTyped += 1; letterTimer = 0; }; return; }
  if (currentStoryStage == 1) { titleCardTimer += 0.03; if (titleCardTimer >= 3) currentStoryStage = 2; return; }
  if (startAnthemPhase) { if (!anthemFinished) playAnthemStep(); return; }
  if (alertTimer > 0) { alertTimer -= 0.04; if (alertTimer <= 0) customAlertMessage = ""; }
  if ( !isLaunched && !infoPanelShown && !quizActive && !isMiniGameActive && !isInsideUnderRocketControl ) {
    if (isMousePressed) {
      if ( !isDraggingSolarView && mouseX > 240 && mouseX < cwidth-255 ) { isDraggingSolarView = true; lastMouseX = mouseX; lastMouseY = mouseY; } else if (isDraggingSolarView) { val dx = mouseX-lastMouseX; val dy = mouseY-lastMouseY; rotAngleY += dx*0.012; rotAngleX += dy*0.012; lastMouseX = mouseX; lastMouseY = mouseY; }; 
    } else { isDraggingSolarView = false; }; 
  }
  if (isTimerRunning && countdownTimer > 0) { countdownTimer -= 0.04; if (countdownTimer <= 0) { countdownTimer = 0; isTimerRunning = false; quizActive = true; }; }
  if (isSpaceTravelling && !isLandingOnPlanet) {
    addTricolorExhaust(rocketCurrentX,rocketCurrentY-20); spaceTravelProgress += 0.005; val targetX = cwidth/2.0; val targetY = cheight/2.0; val arc = math.sin(spaceTravelProgress*math.Pi)*90; rocketCurrentX = 90+(targetX-90)*spaceTravelProgress+arc; rocketCurrentY = cheight-170+(targetY-(cheight-170))*spaceTravelProgress-arc
    if (spaceTravelProgress >= 1) { isSpaceTravelling = false; isLandingOnPlanet = true; landingProgress = 0; landingLegProgress = 0; isLaunched = true; rocketCurrentX = cwidth/2.0; rocketCurrentY = cheight+100; }; 
  }
  if (isLandingOnPlanet && !isLanded) {
    addTricolorExhaust(rocketCurrentX,rocketCurrentY-25); landingProgress += 0.004
    if (landingProgress > 0.4 && landingLegProgress < 1) landingLegProgress += 0.02
    if (landingProgress > 0.8) addLandingDust(rocketCurrentX,140); rocketCurrentX = cwidth/2.0+math.sin(landingProgress*math.Pi*5)*18; rocketCurrentY = cheight+100-((cheight+100)-175)*landingProgress
    if (landingProgress >= 1) { isLandingOnPlanet = false; isLanded = true; rocketCurrentX = cwidth/2.0; rocketCurrentY = 175; landingLegProgress = 1; resetPlanetCinematic(); customAlertMessage = s"$selectedPlanet LANDING SUCCESS!"; alertTimer = 3; playSoundFx(45,250); }; 
  }
  if ( isLanded && !infoPanelShown && !isMiniGameActive && !isInsideUnderRocketControl && !isReturnLaunched && !isLandingOnEarth ) {
    if (planetMissionPhase == 0 && doorOpeningProgress < 1) { doorOpeningProgress += 0.01; } else { updatePlanetCinematic(); }; 
  }
  if (isAstronautReturningIn) {
    if (astroWalkToPlantX > 0) { astroWalkToPlantX -= 2; } else if (doorOpeningProgress > 0) { doorOpeningProgress -= 0.02; } else { isAstronautReturningIn = false; astronautOut = false; isReturnTimerRunning = true; returnCountdownTimer = 5; }; 
  }
  if (isReturnTimerRunning && returnCountdownTimer > 0) { returnCountdownTimer -= 0.04; if (returnCountdownTimer <= 0) { returnCountdownTimer = 0; isReturnTimerRunning = false; isReturnLaunched = true; }; }
  if (isReturnLaunched && !isLandingOnEarth && !isLandedOnEarth) {
    addTricolorExhaust(rocketCurrentX,rocketCurrentY-20); rocketCurrentY += 5
    if (rocketCurrentY > cheight+100) { isReturnLaunched = false; isLandingOnEarth = true; earthLandingProgress = 0; landingLegProgress = 0; rocketCurrentX = cwidth/2.0; rocketCurrentY = cheight+100; }; 
  }
  if (isLandingOnEarth && !isLandedOnEarth) {
    addTricolorExhaust(rocketCurrentX,rocketCurrentY-25); earthLandingProgress += 0.004
    if (earthLandingProgress > 0.4 && landingLegProgress < 1) earthLandingProgress += 0.02
    if (earthLandingProgress > 0.8) addLandingDust(rocketCurrentX,140); rocketCurrentX = cwidth/2.0+math.sin(earthLandingProgress*math.Pi*5)*18; rocketCurrentY = cheight+100-((cheight+100)-175)*earthLandingProgress
    if (earthLandingProgress >= 1) { isLandingOnEarth = false; isLandedOnEarth = true; rocketCurrentX = cwidth/2.0; rocketCurrentY = 175; landingLegProgress = 1; doorOpeningProgress = 1; astronautOut = true; playSoundFx(40,250); }; 
  }
  if (isLandedOnEarth && !isParadeSceneActive) { earthAstronautWalkRightProgress += 2.8; if (earthAstronautWalkRightProgress > cwidth/2.0+200) isParadeSceneActive = true; }
  if (isParadeSceneActive && !isParadeInPosition) {
    if (astroParadeWalkX < cwidth/2.0-120) astroParadeWalkX += 1.6
    if (armyParadeWalkX > cwidth/2.0+120) armyParadeWalkX -= 1.6
    if ( astroParadeWalkX >= cwidth/2.0-120 && armyParadeWalkX <= cwidth/2.0+120 ) { isParadeInPosition = true; startAnthemPhase = true; }; 
  }
  if (isMiniGameActive) {
    val gx = cwidth/2.0-220; val gy = cheight/2.0-100
    if (isMiniGameTimerRunning) { miniGameTimer -= 0.04; if (miniGameTimer <= 0) { miniGameTimer = 0; dieAndResetDevilLevel("Time Out! Mission Failed!"); }; }
    if (activeGameMode == 1) {
      if (isKeyPressed(Kc.VK_LEFT) && dodgerShipX > gx+25) dodgerShipX -= 7.5
      if (isKeyPressed(Kc.VK_RIGHT) && dodgerShipX < gx+415) dodgerShipX += 7.5
      for (i <- 0 until dodgerCount) {
        dodgerAstY(i) -= dodgerAstSpeed(i); val hit = math.sqrt( math.pow(dodgerShipX-dodgerAstX(i),2)+ math.pow(dodgerShipY-dodgerAstY(i),2) ) < 22
        if (hit) dieAndResetDevilLevel("Asteroid Impact! Ship Crashed!")
        if (dodgerAstY(i) < gy+10) { dodgerAstY(i) = gy+200; dodgerAstX(i) = gx+30+randomDouble(0,380); dodgerAstSpeed(i) = randomDouble(3,5); dodgerScore += 10; }; 
      }
      if (dodgerScore >= 80) { isMiniGameActive = false; isMiniGameTimerRunning = false; readyToReturnEarth = true; customAlertMessage = "Asteroid Belt Cleared! Return Ready!"; alertTimer = 3.5; }; 
    } else if (activeGameMode == 3) {
      val padX = gx+180; val padY = gy+20; val padW = 80; landerVy -= 0.12; landerY += landerVy; landerX += landerVx
      if (isKeyPressed(Kc.VK_UP) && landerFuel > 0) { landerVy += 0.38; landerFuel -= 0.8; }
      if (isKeyPressed(Kc.VK_LEFT) && landerFuel > 0) { landerVx -= 0.15; landerFuel -= 0.3; }
      if (isKeyPressed(Kc.VK_RIGHT) && landerFuel > 0) { landerVx += 0.15; landerFuel -= 0.3; }; landerX = math.max(gx+20,math.min(gx+420,landerX))
      if (landerY <= gy+30) {
        if ( landerX >= padX && landerX <= padX+padW && math.abs(landerVy) < 1.8 ) { isMiniGameActive = false; isMiniGameTimerRunning = false; readyToReturnEarth = true; customAlertMessage = "Perfect Smooth Landing Achieved!"; alertTimer = 3.5; } else { dieAndResetDevilLevel("Hard Impact! Lander Crashed!"); }; 
      }; 
    } else {
      val floorY = gy+170
      if (isKeyPressed(Kc.VK_RIGHT) && devilPlayerX < gx+410) devilPlayerX += 5.5
      if (isKeyPressed(Kc.VK_LEFT) && devilPlayerX > gx+15) devilPlayerX -= 5.5
      val up = isKeyPressed(Kc.VK_UP)
      if (up && !jumpKeyPressedLastFrame) {
        if (isGrounded) { devilPlayerVy = -10.8; isGrounded = false; canDoubleJump = true; } else if (canDoubleJump) { devilPlayerVy = -9.7; canDoubleJump = false; }; 
      }; jumpKeyPressedLastFrame = up; devilPlayerVy += 0.45; devilPlayerY += devilPlayerVy; devilPlayerVx *= 0.90; devilPlayerX += devilPlayerVx; devilPlayerX = math.max(gx+15,math.min(gx+410,devilPlayerX))
      if (devilPlayerY >= floorY) { devilPlayerY = floorY; devilPlayerVy = 0; isGrounded = true; canDoubleJump = true; } else { isGrounded = false; }
      if ( !isDevilKeyCollected && math.abs(devilPlayerX-trollKeyX) < 25 && math.abs(devilPlayerY-trollKeyY) < 25 ) { isDevilKeyCollected = true; playSoundFx(80,80); }
      if ( isDevilKeyCollected && math.abs(devilPlayerX-exitDoorX) < 30 && math.abs(devilPlayerY-(gy+140)) < 35 ) { isMiniGameActive = false; isMiniGameTimerRunning = false; readyToReturnEarth = true; customAlertMessage = "Rover Adventure Complete!"; alertTimer = 3.5; }; 
    }; 
  }; 
}

def drawParticleEffects(canvas: CanvasDraw): Unit = {
  exhaustRibbons.foreach { p => canvas.fill( p.col.getRed,p.col.getGreen,p.col.getBlue, math.max(0,math.min(255,(p.life*230).toInt)) ); canvas.noStroke(); canvas.ellipse(p.x,p.y,p.size,p.size*0.6); }; 
  dustParticles.foreach { p => canvas.fill( p.col.getRed,p.col.getGreen,p.col.getBlue, math.max(0,math.min(255,(p.life*150).toInt)) ); canvas.noStroke(); canvas.ellipse(p.x,p.y,p.size,p.size*0.8); }; 
  explosionParticles.foreach { p => canvas.fill( p.col.getRed,p.col.getGreen,p.col.getBlue, math.max(0,math.min(255,(p.life*255).toInt)) ); canvas.noStroke(); canvas.ellipse(p.x,p.y,p.size,p.size); }; 
  sparkParticles.foreach { p => canvas.fill(255,240,120,math.max(0,math.min(255,(p.life*255).toInt))); canvas.noStroke(); canvas.ellipse(p.x,p.y,p.size,p.size); }; 
}

def viewState(canvas: CanvasDraw): Unit = {
  if (currentStoryStage == -3) { drawRobotChallengeGameScreen(canvas); return; }
  if (currentStoryStage == -2) { drawRobotSelectionScreen(canvas); return; }
  if (currentStoryStage == -1) { drawWelcomeSirScreen(canvas); return; }
  if (currentStoryStage == 0) { drawISROLetterScreen(canvas); return; }
  if (currentStoryStage == 1) { drawPartOneTitleScreen(canvas); return; }
  if (anthemFinished) { drawCleanEndScreen(canvas); return; }; 
  
  canvas.fill(0,0,0,255); canvas.noStroke(); canvas.rect(0,0,cwidth,cheight)
  for (i <- 0 until starCount) {
    val a = math.max( 50, math.min( 255, (150+math.sin(globalTime*5+i)*105).toInt ) ); canvas.stroke(255,255,255,a); canvas.strokeWeight( math.max( 0.1, starSize(i)+math.sin(globalTime*6+i).toFloat*1.1 ) ); canvas.point(starX(i),starY(i)); 
  }; 
  drawParticleEffects(canvas)
  
  if (isParadeSceneActive) { drawParadeGroundScene(canvas); }
  else if (isLandingOnEarth || isLandedOnEarth) { drawEarthFirstLandingScene(canvas); }
  else {
    if (isLaunched) { drawPlanetSurfaceBackground(canvas); drawAtmosphereScanner(canvas); } else { drawFullSolarSystem(canvas); drawPlanetList(canvas); draw3DPlanetPreview(canvas); }; 
    drawLaunchAndUI(canvas); drawISRORocket(canvas); 
  }; 
  drawQuizPopup(canvas); drawPlanetInfoPopup(canvas); drawMiniGames(canvas); drawCustomAlertPopup(canvas); drawMissionReport(canvas)
  if ( planetMissionPhase == 5 && asteroidWarningVisible ) { canvas.fill(255,70,50); canvas.stroke(255,200,100); canvas.strokeWeight(2); canvas.text( "ASTEROID FIELD ? EVASION PROTOCOL", cwidth/2.0-130,150 ); }; 
}; 

animateWithCanvasDraw { canvas => updateState(); viewState(canvas); }