Code Sketch
yadnesh shahare
Category: Programming
// --- ISRO ULTIMATE MISSION: MASTERPIECE EDITION (3D ROTATABLE & ZOOMABLE) ---
cleari(); originBottomLeft();
// MIDI Safe Initialization
var isGameAudioAllowed = false
try {
setNoteInstrument(Instrument.PIANO)
isGameAudioAllowed = true
} catch {
case e: 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 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, vandeIndex, charIndex, textDelayTimer) = (0, 0, 0, 0.0)
var (janaGanaFinished, anthemFinished, startAnthemPhase) = (false, false, 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 { fullJanaGanaTune.clear(); janaGanaFinished = true }
} else {
if (vandeIndex < fullVandeMataramTune.length) { val (n, d) = fullVandeMataramTune(vandeIndex); playSoundFx(n, d); vandeIndex += 1 }
else if (charIndex >= fullAnthemText.length) anthemFinished = true
}
}
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, 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/s� | Closest Planet to Sun", " Extreme Temp: Very hot days, freezing nights", " High Orbital Inclination & Delta-V Criteria", " Distance: 57.9 Million km | Orbit: 88 Days", " Radius: 2,439.7 km (~2.44 Million m)", " Mass: 3.301 � 10�� kg | Temp: -180�C to 430�C", " Exosphere: Thin helium & sodium trace", " Surface: Heavily cratered silicate crust", "Which planet is closest to the Sun in our Solar System?", 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/s� | Shukrayaan-1 Target", " Dense Atmosphere: 96.5% Carbon Dioxide", " High-Resolution Synthetic Aperture Radar", " Distance: 108.2 Million km | Orbit: 225 Days", " Radius: 6,051.8 km (~6.05 Million m)", " Mass: 4.867 � 10�? kg | Temp: ~464�C", " Pressure: 92 bar (Supercritical CO2)", " Clouds: Concentrated sulfuric acid droplets", "Which is the hottest planet in the Solar System?", 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/s� | Our Home Planet", " Satellite: Moon (Chandrayaan-3 South Pole)", " Launch Centre: SDSC SHAR, Sriharikota", " Distance: 149.6 Million km (1 AU)", " Radius: 6,371 km (~6.37 Million m)", " Mass: 5.972 � 10�? kg | Temp: -89�C to 58�C", " Atmosphere: 78% Nitrogen, 21% Oxygen", " Biosphere: Only known life-bearing world", "How many days does Earth take to complete one orbit around 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/s� | Mars Orbiter Mission (MOM)", " Launch Vehicle: PSLV-XL C25 Config", " Interplanetary Hohmann Transfer Orbit", " Distance: 227.9 Million km | Orbit: 687 Days", " Radius: 3,389.5 km (~3.39 Million m)", " Mass: 6.417 � 10�� kg | Temp: -125�C to 20�C", " Thin Atmosphere: 95% CO2, low pressure", " Topography: Olympus Mons & Valles Marineris", "Which planet is famously known as 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/s� | Largest Planet", " Powerful Magnetic Field & Radiation", " Subsurface Water Ocean on Europa", " Distance: 778.5 Million km | Orbit: 11.8 Years", " Radius: 69,911 km (~69.9 Million m)", " Mass: 1.898 � 10�? kg | Temp: ~ -110�C", " Composition: 90% Hydrogen, 10% Helium", " Great Red Spot: Giant anticyclonic storm", "Which is the largest planet in our Solar System?", 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/s� | Ringed Planet Jewel", " Titan's Dense Atmosphere & Hydrocarbons", " Active Cryovolcanism on Enceladus", " Distance: 1.4 Billion km | Orbit: 29.5 Years", " Radius: 58,232 km (~58.2 Million m)", " Mass: 5.683 � 10�? kg | Temp: ~ -140�C", " Rings: 99% water ice with silicate dust", " Density: Less than water (0.687 g/cm�)", "Which planet is famous for its bright prominent ring system?", 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/s� | Extreme 97.77� Tilt", " Ice Giant with Methane Mantle Overlay", " Off-center Offset Magnetic Axis", " Distance: 2.87 Billion km | Orbit: 84 Years", " Radius: 25,362 km (~25.3 Million m)", " Mass: 8.681 � 10�? kg | Temp: ~ -195�C", " Atmosphere: Hydrogen, helium, methane ice", " Coldest Planetary Atmosphere in Solar System", "Which blue-green ice giant 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/s� | Outermost Major Planet", " Supersonic Winds: Up to 2,100 km/h", " Retrograde Orbit of Triton Moon", " Distance: 4.5 Billion km | Orbit: 165 Years", " Radius: 24,622 km (~24.6 Million m)", " Mass: 1.024 � 10�? kg | Temp: ~ -200�C", " Dynamic Atmosphere: Great Dark Spot storms", " Triton: Active nitrogen geysers observed", "Which is the farthest major planet from the Sun in our Solar System?", 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, " First habitable 'Super-Earth' planet candidate.", " Size: 2.4 times larger than Earth.", " Orbital Distance: 0.85 AU (127 Million km)", " Distance from Earth: ~635 Light Years", " Radius: ~15,290 km (~15.29 Million m)", " Mass: ~9.1 Earth Masses | Temp: -11�C to 22�C", " Planet Type: Potential Ocean World or Mini-Neptune", " Discovery Year: 2011 (Kepler Telescope)", "Because Kepler-22b is larger than Earth, what classification is it 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, " Famous 'Tatooine' planet with dual suns.", " Orbits around two stars simultaneously (Circumbinary).", " Gas Giant composition (Saturn-sized).", " Distance from Earth: ~245 Light Years", " Radius: ~53,200 km (~53.2 Million m)", " Mass: ~0.33 Jupiter Mass | Temp: -70�C", " Primary Stars: K-dwarf + M-dwarf pair", " Orbit Period: 229 Days around binary center", "How many host stars does planet 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, " First Earth-sized planet in Habitable Zone.", " Potential candidate for liquid surface water.", " High probability of rocky surface terrain.", " Distance from Earth: ~580 Light Years", " Radius: ~7,450 km (~7.45 Million m)", " Mass: ~1.4 Earth Mass | Orbit: 130 Days", " Host Star: Red Dwarf (M1V class)", " Insolation: Receives 32% of Earth's sunlight", "Is planet Kepler-186f located in its star's 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, " Famously known as 'Earth's Cousin' (Earth 2.0).", " Orbital year length: 385 Days (Near Earth twin).", " Super-Earth Class rocky/thick atmosphere planet.", " Distance from Earth: ~1,800 Light Years", " Radius: ~10,380 km (~10.38 Million m)", " Mass: ~5 Earth Masses | Temp: ~ -8�C", " Host Star: Kepler-452 (G2V Solar Twin)", " Age: ~6 Billion Years (1.5B years older than Sun)", "What nickname was given to Kepler-452b due to its Earth-like traits?", 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' with molten surface.", " Very close proximity to host star.", " Surface temp churns rock/lava (>1,300�C).", " Distance from Earth: ~560 Light Years", " Radius: ~9,360 km (~9.36 Million m)", " Mass: ~3.2 Earth Masses | Orbit: 20 Hours", " Tidal Locking: One side permanently molten", " Density: High silicate and iron core", "What covers the surface of Kepler-10b due to extreme heat?", 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' covered by global sea.", " Approximately 40% larger than Earth.", " Host Star: K-type Orange Dwarf.", " Distance from Earth: ~1,200 Light Years", " Radius: ~8,980 km (~8.98 Million m)", " Mass: ~2.8 Earth Masses | Orbit: 267 Days", " Climate Model: Stable global ocean with greenhouse gas", " Habitable Index: Very High", "What is Kepler-62f most likely covered by?", 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 8-planet system found by Google AI.", " Discovered via Neural Network Machine Learning.", " Sizzling rocky terrestrial surface.", " Distance from Earth: ~2,840 Light Years", " Radius: ~8,410 km (~8.41 Million m)", " Mass: ~2.5 Earth Masses | Orbit: 14.4 Days", " System Structure: Matches Solar System planet count", " Surface Temp: ~430�C", "Which technology helped discover planet Kepler-90i?", Array("Artificial Intelligence (Google AI)", "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 with hydrogen atmosphere.", " JWST detected water vapor & methane traces.", " Potential liquid ocean beneath atmosphere.", " Distance from Earth: ~124 Light Years", " Radius: ~16,620 km (~16.62 Million m)", " Mass: ~8.6 Earth Masses | Temp: ~ -8�C", " JWST Detection: Carbon-bearing molecules (CO2, CH4)", " Potential Biosignature: DMS trace research", "What trace evidence did the Space Telescope find in K2-18b's atmosphere?", Array("Water Vapor", "Petrol", "Diesel", "Milk"), 0)
)
val starCount = 350
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.0))
var (waveAngle, chakraRotation, neonGlowPhase, globalTime) = (0.0, 0.0, 0.0, 0.0)
var currentSolarSystemId = 1
// --- 3D ROTATION & ZOOM CONTROLS ---
var cameraRotationX = 0.5 // Tilt Angle
var cameraRotationY = 0.0 // Rotation Angle
var zoomScale = 1.0 // Zoom Level
var lastMouseX = 0.0
var lastMouseY = 0.0
var isDraggingSolarSystem = false
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 selectedPlanet = "Mars"
var (planetLocked, isTimerRunning, isSpaceTravelling, isLandingOnPlanet, isLaunched, isLanded) = (false, false, false, false, false, false)
var (customAlertMessage, quizFeedback) = ("", "")
var (alertTimer, countdownTimer, spaceTravelProgress, landingProgress, doorOpeningProgress, astroWaitTimer, flagPlantProgress, landingLegProgress) = (0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
var (rocketCurrentX, rocketCurrentY) = (90.0, cheight - 170.0)
var (astronautOut, infoPanelShown, isFlagPlantedOnPlanet, isAstronautReturningIn, quizActive, isMiniGameActive, readyToReturnEarth) = (false, false, false, false, false, false, false)
var (infoPageNumber, activeGameMode) = (1, 1)
var (astroWalkToPlantX, miniGameTimer) = (0.0, 20.0)
val (plantedFlagX, plantedFlagY) = ((cwidth / 2.0) - 80.0, 155.0)
// --- PARTICLES DATA ---
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 petalShower = ArrayBuffer.fill(40)(DynamicParticle(randomDouble(0, cwidth), randomDouble(cheight, cheight + 200), randomDouble(-0.5, 0.5), randomDouble(-2.0, -1.0), 1.0, cm.rgb(255, 153, 51), randomDouble(4, 7)))
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.0, 14.0)))
exhaustRibbons.append(DynamicParticle(x, y, randomDouble(-0.8, 0.8), randomDouble(-5.0, -3.0), 1.0, cm.rgb(255, 255, 255), randomDouble(10.0, 16.0)))
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.0, 14.0)))
}
def addLandingDust(x: Double, y: Double): Unit = {
for (_ <- 0 until 4) {
dustParticles.append(DynamicParticle(x + randomDouble(-30, 30), y, randomDouble(-2.5, 2.5), randomDouble(0.2, 1.5), 1.0, cm.rgb(210, 180, 140), randomDouble(4.0, 9.0)))
}
}
def triggerExplosion(x: Double, y: Double): Unit = {
for (_ <- 0 until 25) {
val gVal = randomDouble(50, 200).toInt
explosionParticles.append(DynamicParticle(x, y, randomDouble(-6.0, 6.0), randomDouble(-6.0, 6.0), 1.0, cm.rgb(255, gVal, 0), randomDouble(5.0, 12.0)))
}
}
def updateExhaust(): Unit = {
exhaustRibbons.filterInPlace(_.life > 0)
exhaustRibbons.foreach { p =>
p.x += p.vx + math.sin(globalTime * 10.0) * 1.5
p.y += p.vy
p.life -= 0.035
p.size += 0.4
}
dustParticles.filterInPlace(_.life > 0)
dustParticles.foreach { p =>
p.x += p.vx
p.y += p.vy
p.life -= 0.03
p.size += 0.2
}
explosionParticles.filterInPlace(_.life > 0)
explosionParticles.foreach { p =>
p.x += p.vx
p.y += p.vy
p.life -= 0.05
p.size *= 0.95
}
petalShower.foreach { p =>
p.y += p.vy; p.x += math.sin(globalTime * 3 + p.y * 0.05) * 1.2
if (p.y < 0) { p.y = cheight + 20; p.x = randomDouble(0, cwidth) }
}
}
var isMiniGameTimerRunning = false
var (devilPlayerX, devilPlayerY, devilPlayerVy, devilPlayerVx) = ((cwidth / 2.0) - 180.0, 130.0, 0.0, 0.0)
var (isGrounded, canDoubleJump, jumpKeyPressedLastFrame, isDevilKeyCollected) = (false, false, false, false)
var (trollKeyX, trollKeyY, exitDoorX) = ((cwidth / 2.0) + 140.0, 140.0, (cwidth / 2.0) - 180.0)
var (dodgerShipX, dodgerShipY) = (cwidth / 2.0, cheight / 2.0 - 50.0)
val dodgerCount = 4
var dodgerAstX = Array.fill(dodgerCount)(cwidth / 2.0)
var dodgerAstY = Array.fill(dodgerCount)(cheight / 2.0 + 150.0)
var dodgerAstSpeed = Array.fill(dodgerCount)(3.8)
var dodgerScore = 0
var (landerX, landerY, landerVy, landerVx, landerFuel) = (cwidth / 2.0, cheight / 2.0 + 50.0, 0.0, 0.0, 100.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.0 + randomDouble(0, 380); dodgerAstY(i) = (cheight / 2.0 + 90.0) + (i * 45.0); dodgerAstSpeed(i) = randomDouble(3.0, 5.0) }
}
def resetLanderGame(): Unit = { landerX = cwidth / 2.0; landerY = cheight / 2.0 + 60.0; landerVy = 0.2; landerVx = 0.0; landerFuel = 100.0 }
var (isReturnTimerRunning, isReturnLaunched, isLandingOnEarth, isLandedOnEarth, isParadeSceneActive, isParadeInPosition) = (false, false, false, false, false, false)
var (returnCountdownTimer, earthLandingProgress, earthAstronautWalkRightProgress) = (5.0, 0.0, 0.0)
var (astroParadeWalkX, armyParadeWalkX) = (-50.0, cwidth + 50.0)
def dieAndResetDevilLevel(msg: String): Unit = {
playSoundFx(38, 300); triggerExplosion(devilPlayerX, devilPlayerY); customAlertMessage = s" $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.0; val gy = cheight / 2.0 - 100.0
devilPlayerX = gx + 30.0; devilPlayerY = gy + 170.0; devilPlayerVy = 0.0; devilPlayerVx = 0.0; isGrounded = false; canDoubleJump = true
trollKeyX = gx + 340.0; isDevilKeyCollected = false; exitDoorX = gx + 40.0
}
}
def handleSolar3DControls(): Unit = {
if (!isLaunched && !infoPanelShown && !quizActive && !isMiniGameActive) {
// Mouse Drag to Rotate 3D Solar System
if (isMousePressed) {
if (mouseX > 250 && mouseX < cwidth - 260) {
if (isDraggingSolarSystem) {
val dx = mouseX - lastMouseX
val dy = mouseY - lastMouseY
cameraRotationY += dx * 0.01
cameraRotationX += dy * 0.01
cameraRotationX = math.max(0.1, math.min(1.4, cameraRotationX)) // Clamp tilt angle
}
isDraggingSolarSystem = true
lastMouseX = mouseX
lastMouseY = mouseY
}
} else {
isDraggingSolarSystem = false
}
// Keyboard Zoom Controls
if (isKeyPressed(Kc.VK_UP)) zoomScale = math.min(2.5, zoomScale + 0.02)
if (isKeyPressed(Kc.VK_DOWN)) zoomScale = math.max(0.4, zoomScale - 0.02)
}
}
def updateState(): Unit = {
if (startAnthemPhase) { waveAngle += 0.22; if (!anthemFinished) playAnthemStep(); return }
waveAngle += 0.22; chakraRotation += 0.22; neonGlowPhase += 0.22; globalTime += 0.025
updateExhaust()
handleSolar3DControls()
if (alertTimer > 0) { alertTimer -= 0.04; if (alertTimer <= 0) customAlertMessage = "" }
if (isTimerRunning && countdownTimer > 0) { countdownTimer -= 0.04; if (countdownTimer <= 0) { countdownTimer = 0.0; isTimerRunning = false; quizActive = true } }
if (isSpaceTravelling && !isLandingOnPlanet) {
addTricolorExhaust(rocketCurrentX, rocketCurrentY - 20); spaceTravelProgress += 0.005
var (targetX, targetY) = (cwidth / 2.0, cheight / 2.0)
val pList = getActivePlanetList(); val planetSpeeds = Array(0.8, 0.6, 0.45, 0.4, 0.35, 0.28, 0.22, 0.16)
for (i <- pList.indices if pList(i) == selectedPlanet) {
val a = globalTime * (if(i < planetSpeeds.length) planetSpeeds(i) else 0.2)
targetX = (cwidth / 2.0 - 30.0) + math.cos(a) * ((i + 1) * 20.0); targetY = (cheight / 2.0) + math.sin(a) * ((i + 1) * 12.0)
}
val arcOffset = math.sin(spaceTravelProgress * math.Pi) * 90.0
rocketCurrentX = (90.0 + (targetX - 90.0) * spaceTravelProgress) + arcOffset
rocketCurrentY = ((cheight - 170.0) + (targetY - (cheight - 170.0)) * spaceTravelProgress) - arcOffset
if (spaceTravelProgress >= 1.0) { isSpaceTravelling = false; isLandingOnPlanet = true; landingProgress = 0.0; landingLegProgress = 0.0; isLaunched = true; rocketCurrentX = cwidth / 2.0; rocketCurrentY = cheight + 100.0 }
}
if (isLandingOnPlanet && !isLanded) {
addTricolorExhaust(rocketCurrentX, rocketCurrentY - 25); landingProgress += 0.004
if (landingProgress > 0.4 && landingLegProgress < 1.0) landingLegProgress += 0.02
if (landingProgress > 0.8) addLandingDust(rocketCurrentX, 140.0)
rocketCurrentX = (cwidth / 2.0) + math.sin(landingProgress * math.Pi * 5) * 18.0
rocketCurrentY = (cheight + 100.0) - ((cheight + 100.0) - 175.0) * landingProgress
if (landingProgress >= 1.0) { isLandingOnPlanet = false; isLanded = true; rocketCurrentX = cwidth / 2.0; rocketCurrentY = 175.0; landingLegProgress = 1.0; playSoundFx(45, 250) }
}
if (isLanded && !isReturnLaunched && !isAstronautReturningIn && !isLandingOnEarth && !readyToReturnEarth && !infoPanelShown && !isMiniGameActive) {
if (doorOpeningProgress < 1.0) doorOpeningProgress += 0.01
else {
astronautOut = true
if (astroWalkToPlantX < 80.0) astroWalkToPlantX += 1.2
else if (flagPlantProgress < 1.0) { flagPlantProgress += 0.02; if (flagPlantProgress >= 1.0) isFlagPlantedOnPlanet = true }
else { astroWaitTimer += 0.04; if (astroWaitTimer >= 1.2) { infoPanelShown = true; readyToReturnEarth = true } }
}
}
if (isAstronautReturningIn) {
if (astroWalkToPlantX > 0.0) astroWalkToPlantX -= 1.5
else if (doorOpeningProgress > 0.0) doorOpeningProgress -= 0.02
else { isAstronautReturningIn = false; astronautOut = false; isReturnTimerRunning = true; returnCountdownTimer = 5.0 }
}
if (isReturnTimerRunning && returnCountdownTimer > 0) { returnCountdownTimer -= 0.04; if (returnCountdownTimer <= 0) { returnCountdownTimer = 0.0; isReturnTimerRunning = false; isReturnLaunched = true } }
if (isReturnLaunched && !isLandingOnEarth && !isLandedOnEarth) {
addTricolorExhaust(rocketCurrentX, rocketCurrentY - 20); rocketCurrentY += 5.0
if (rocketCurrentY > cheight + 100.0) { isReturnLaunched = false; isLandingOnEarth = true; earthLandingProgress = 0.0; landingLegProgress = 0.0; rocketCurrentX = cwidth / 2.0; rocketCurrentY = cheight + 100.0 }
}
if (isLandingOnEarth && !isLandedOnEarth) {
addTricolorExhaust(rocketCurrentX, rocketCurrentY - 25); earthLandingProgress += 0.004
if (earthLandingProgress > 0.4 && landingLegProgress < 1.0) landingLegProgress += 0.02
if (earthLandingProgress > 0.8) addLandingDust(rocketCurrentX, 140.0)
rocketCurrentX = (cwidth / 2.0) + math.sin(earthLandingProgress * math.Pi * 5) * 18.0
rocketCurrentY = (cheight + 100.0) - ((cheight + 100.0) - 175.0) * earthLandingProgress
if (earthLandingProgress >= 1.0) { isLandingOnEarth = false; isLandedOnEarth = true; rocketCurrentX = cwidth / 2.0; rocketCurrentY = 175.0; landingLegProgress = 1.0; doorOpeningProgress = 1.0; astronautOut = true; playSoundFx(40, 250) }
}
if (isLandedOnEarth && !isParadeSceneActive) { earthAstronautWalkRightProgress += 2.8; if (earthAstronautWalkRightProgress > cwidth / 2.0 + 200.0) isParadeSceneActive = true }
if (isParadeSceneActive && !isParadeInPosition) {
if (astroParadeWalkX < (cwidth / 2.0) - 120.0) astroParadeWalkX += 1.6
if (armyParadeWalkX > (cwidth / 2.0) + 120.0) armyParadeWalkX -= 1.6
if (astroParadeWalkX >= (cwidth / 2.0) - 120.0 && armyParadeWalkX <= (cwidth / 2.0) + 120.0) { isParadeInPosition = true; startAnthemPhase = true }
}
if (isMiniGameActive) {
val gx = cwidth / 2.0 - 220.0; val gy = cheight / 2.0 - 100.0
if (isMiniGameTimerRunning) { miniGameTimer -= 0.04; if (miniGameTimer <= 0.0) { miniGameTimer = 0.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)
if (math.sqrt(math.pow(dodgerShipX - dodgerAstX(i), 2) + math.pow(dodgerShipY - dodgerAstY(i), 2)) < 22.0) dieAndResetDevilLevel("Asteroid Impact! Ship Crashed!")
if (dodgerAstY(i) < gy + 10) { dodgerAstY(i) = gy + 200.0; dodgerAstX(i) = gx + 30.0 + randomDouble(0, 380); dodgerAstSpeed(i) = randomDouble(3.0, 5.0); dodgerScore += 10; playSoundFx(72, 30) }
}
if (dodgerScore >= 80) { playSoundFx(75, 200); isMiniGameActive = false; isMiniGameTimerRunning = false; readyToReturnEarth = true; customAlertMessage = " Asteroid Belt Cleared! Return Ready!"; alertTimer = 3.5 }
} else if (activeGameMode == 3) {
val padX = gx + 180.0; val padY = gy + 20.0; val padW = 80.0
landerVy -= 0.12; landerY += landerVy; landerX += landerVx
if (isKeyPressed(Kc.VK_UP) && landerFuel > 0) { landerVy += 0.38; landerFuel -= 0.8; playSoundFx(50, 30) }
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 }
if (landerX < gx + 20) landerX = gx + 20; if (landerX > gx + 420) landerX = gx + 420
if (landerY <= gy + 30) {
if (landerX >= padX && landerX <= padX + padW && math.abs(landerVy) < 1.8) { playSoundFx(80, 250); 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.0
if (isKeyPressed(Kc.VK_RIGHT) && devilPlayerX < gx + 410) devilPlayerX += 5.5
if (isKeyPressed(Kc.VK_LEFT) && devilPlayerX > gx + 15) devilPlayerX -= 5.5
val upPressedNow = isKeyPressed(Kc.VK_UP)
if (upPressedNow && !jumpKeyPressedLastFrame) {
if (isGrounded) { devilPlayerVy = -10.8; isGrounded = false; canDoubleJump = true; playSoundFx(60, 50) }
else if (canDoubleJump) { devilPlayerVy = -10.8 * 0.9; canDoubleJump = false; playSoundFx(67, 50) }
}
jumpKeyPressedLastFrame = upPressedNow
devilPlayerVy += 0.45; devilPlayerY += devilPlayerVy; devilPlayerX += devilPlayerVx; devilPlayerVx *= 0.90
if (devilPlayerX < gx + 15) devilPlayerX = gx + 15; if (devilPlayerX > gx + 410) devilPlayerX = gx + 410
if (devilPlayerY >= floorY) { devilPlayerY = floorY; devilPlayerVy = 0.0; devilPlayerVx = 0.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) { playSoundFx(78, 200); isMiniGameActive = false; isMiniGameTimerRunning = false; readyToReturnEarth = true; customAlertMessage = " Rover Adventure Complete!"; alertTimer = 3.5 }
}
}
}
def drawParticleEffects(canvas: CanvasDraw): Unit = {
exhaustRibbons.foreach { p =>
val safeSize = math.max(0.1, p.size)
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, safeSize, math.max(0.1, safeSize * 0.6))
}
dustParticles.foreach { p =>
val safeSize = math.max(0.1, p.size)
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, safeSize, safeSize * 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)
}
}
def draw3DSphere(canvas: CanvasDraw, cx: Double, cy: Double, size: Double, baseColor: Color, lightAccent: Color, rotAngle: Double = 0.0): Unit = {
val layers = 9
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 offset = (1 - factor) * (safeSize * 0.28)
val curDim = math.max(0.1, safeSize * factor)
canvas.ellipse(cx - offset, cy + offset, curDim, curDim)
}
canvas.fill(255, 255, 255, 35)
val spotX = cx + math.cos(rotAngle) * (safeSize * 0.25)
val spotY = cy + math.sin(rotAngle) * (safeSize * 0.15)
canvas.ellipse(spotX, spotY, math.max(0.1, safeSize * 0.35), math.max(0.1, safeSize * 0.2))
}
def drawOrbitingSatellite(canvas: CanvasDraw, cx: Double, cy: Double, dist: Double): Unit = {
val satAngle = globalTime * 1.5
val sx = cx + math.cos(satAngle) * dist
val sy = cy + math.sin(satAngle) * (dist * 0.4)
canvas.fill(200, 200, 220); canvas.noStroke(); canvas.ellipse(sx, sy, 6, 6)
canvas.stroke(0, 200, 255); canvas.strokeWeight(1.2)
canvas.line(sx - 8, sy, sx + 8, sy)
}
// 3D ROTATABLE AND ZOOMABLE SOLAR SYSTEM
def drawFullSolarSystem(canvas: CanvasDraw): Unit = {
val (centerX, centerY) = (cwidth / 2.0 - 10.0, cheight / 2.0 - 10.0)
canvas.noFill(); val pList = getActivePlanetList()
// Instruction Overlay
canvas.fill(255, 255, 255, 180)
canvas.text("?? Drag Mouse to Rotate 3D Solar System | ???? Arrow Keys to Zoom", centerX - 220, cheight - 25)
// Render Orbits with 3D Angles
for (i <- pList.indices) {
val alphaVal = math.max(0, math.min(255, (80 + math.sin(globalTime * 3 + i) * 45).toInt))
canvas.stroke(0, 220, 255, alphaVal)
canvas.strokeWeight(1.2)
val baseDist = (i * 26.0 + 45.0) * zoomScale
val ew = math.max(0.1, baseDist * 2)
val eh = math.max(0.1, baseDist * 2 * cameraRotationX)
canvas.ellipse(centerX, centerY, ew, eh)
}
val sunGlow = math.max(10.0, ((math.sin(globalTime * 6) * 10 + 72) * zoomScale).toDouble)
draw3DSphere(canvas, centerX, centerY, sunGlow, cm.rgb(255, 110, 0), cm.rgb(255, 255, 190), globalTime * 2)
drawOrbitingSatellite(canvas, centerX, centerY, 100.0 * zoomScale)
val planetSpeeds = Array(0.8, 0.6, 0.45, 0.4, 0.35, 0.28, 0.22, 0.16); val activeMap = getActivePlanetDetailsMap()
for (i <- pList.indices) {
val planetName = pList(i);
val angle = (globalTime * (if(i < planetSpeeds.length) planetSpeeds(i) else 0.2)) + cameraRotationY
val dist = (i * 26.0 + 45.0) * zoomScale
val curX = centerX + math.cos(angle) * dist
val curY = centerY + math.sin(angle) * (dist * cameraRotationX)
var (pCol, pAccent, pRing) = (cm.rgb(200, 200, 200), cm.rgb(255, 255, 255), false)
activeMap.foreach { pd => if (pd.name == planetName) { pCol = pd.surfaceCol; pAccent = pd.accent3D; pRing = pd.hasRings } }
val baseSize = if (Array("Earth", "Kepler-22b", "Kepler-452b").contains(planetName)) 18.0 else (12.0 + (i * 0.4))
val pSize = math.max(1.0, baseSize * zoomScale)
// --- ???????? ??????????? ???? ?????? ???? (Thin White Ring for All Planets) ---
canvas.noFill()
canvas.stroke(255, 255, 255, 180) // ?????? ???
canvas.strokeWeight((1.0 * zoomScale).toFloat) // ???? ???? ??????? ????
val thinRingW = pSize * 2.2
val thinRingH = math.max(0.1, pSize * 0.6 * cameraRotationX)
canvas.ellipse(curX, curY, thinRingW, thinRingH)
// ??? Saturn ?? ????? ???? ?????????
if (pRing) {
canvas.noFill(); canvas.stroke(245, 220, 180, 210); canvas.strokeWeight((2.5 * zoomScale).toFloat);
val ringW = pSize * (2.4 + math.sin(globalTime * 2) * 0.1)
canvas.ellipse(curX, curY, ringW, math.max(0.1, pSize * 0.8 * cameraRotationX))
}
draw3DSphere(canvas, curX, curY, pSize, pCol, pAccent, globalTime * 3 + i)
if (!planetLocked && isMousePressed && !isDraggingSolarSystem && math.abs(mouseX - curX) < 20 && math.abs(mouseY - curY) < 20) {
if (planetName == "Earth") { selectedPlanet = "Earth"; customAlertMessage = "You are already on Earth!"; alertTimer = 3.0 } else selectedPlanet = planetName
}
// ?????????? ???????????? ???? ???? (Cyan) ????, ?????? ??????? ????? ???.
if (planetName == selectedPlanet) {
canvas.noFill(); canvas.stroke(0, 255, 255, 220);
val glowW = math.max(1.0, 1.5 + math.sin(neonGlowPhase * 6).toFloat * 1.0)
canvas.strokeWeight(glowW)
canvas.ellipse(curX, curY, pSize + 12, math.max(0.1, (pSize + 12) * cameraRotationX))
}
}
}
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)
val switchPulse = math.max(0, math.min(255, (math.sin(globalTime * 4) * 30 + 180).toInt))
canvas.fill(0, switchPulse, 220); canvas.stroke(255, 255, 255); canvas.strokeWeight(1.5); canvas.rect(15, 40, 220, 28)
canvas.fill(255, 255, 255); canvas.text(if (currentSolarSystemId == 1) " System: Real Solar" else " System: Kepler Planet", 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"
customAlertMessage = if (currentSolarSystemId == 1) "Real Solar System Selected!" else "Kepler Exoplanet System Selected!"
alertTimer = 2.5
}
val activeMap = getActivePlanetDetailsMap(); val pList = getActivePlanetList()
for (i <- pList.indices) {
val py = 88.0 + (i * 32.0); var rName = pList(i)
activeMap.foreach { pd => if (pd.name == pList(i)) rName = pd.realName }
if (!planetLocked && isMousePressed && mouseX >= 15 && mouseX <= 235 && mouseY >= py - 12 && mouseY <= py + 12) {
if (pList(i) == "Earth") { selectedPlanet = "Earth"; customAlertMessage = "You are already on Earth!"; alertTimer = 2.5 } else selectedPlanet = pList(i)
}
if (pList(i) == selectedPlanet) { 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(s" $rName", 18, py)
}
}
def draw3DPlanetPreview(canvas: CanvasDraw): Unit = {
val (boxX, boxY, boxW, boxH) = (cwidth - 250.0, 15.0, 235.0, 225.0)
canvas.fill(15, 20, 45, 245); canvas.stroke(0, 255, 255); canvas.strokeWeight(2.0); canvas.rect(boxX, boxY, boxW, boxH)
canvas.fill(255, 255, 255); canvas.text(s"3D Hologram: $selectedPlanet", boxX + 12, boxY + 22)
val (cx, cy) = (boxX + boxW / 2.0, boxY + boxH / 2.0 + 15.0)
var (pCol, pAccent, pRings) = (cm.rgb(200, 70, 40), cm.rgb(255, 130, 100), false)
getActivePlanetDetailsMap().foreach { pd => if (pd.name == selectedPlanet) { pCol = pd.surfaceCol; pAccent = pd.accent3D; pRings = pd.hasRings } }
// Hologram ????? ?????? ???? ?????? ????
canvas.noFill(); canvas.stroke(255, 255, 255, 180); canvas.strokeWeight(1.5); canvas.ellipse(cx, cy, 175, 45)
if (pRings) { canvas.noFill(); canvas.stroke(220, 210, 180, 230); canvas.strokeWeight(6.5); canvas.ellipse(cx, cy, 185, 54) }
draw3DSphere(canvas, cx, cy, 115, pCol, pAccent, globalTime * 2.5)
}
def drawPlanetSurfaceBackground(canvas: CanvasDraw): Unit = {
var (surfCol, darkSurfCol) = (cm.rgb(100, 30, 15), cm.rgb(50, 10, 5))
getActivePlanetDetailsMap().foreach { pd => if (pd.name == selectedPlanet) { surfCol = pd.surfaceCol; darkSurfCol = pd.darkSurfCol } }
canvas.fill(surfCol); canvas.noStroke(); canvas.rect(0, 0, cwidth, 140)
canvas.fill(darkSurfCol);
canvas.ellipse(120, 40, 60, 20); canvas.ellipse(420, 60, 90, 25); canvas.ellipse(650, 45, 70, 22);
canvas.rect(0, 120, cwidth, 25)
if (isFlagPlantedOnPlanet) drawWavingTricolorFlagWithAshokChakra(canvas, plantedFlagX, plantedFlagY)
}
def drawWavingTricolorFlagWithAshokChakra(canvas: CanvasDraw, fx: Double, fy: Double): Unit = {
val (fw, fh) = (52.0, 10.0); canvas.stroke(230, 230, 230); canvas.strokeWeight(3.5); canvas.line(fx, fy + 45, fx, fy - 12)
var x = 0.0; while (x < fw) {
val yOffset = math.sin(waveAngle + (x * 0.12)) * 4.5; canvas.strokeWeight(2.8)
canvas.stroke(255, 153, 51); canvas.line(fx + x, fy + fh * 2 + yOffset, fx + x, fy + fh * 3 + yOffset)
canvas.stroke(255, 255, 255); canvas.line(fx + x, fy + fh + yOffset, fx + x, fy + fh * 2 + yOffset)
canvas.stroke(18, 136, 7); canvas.line(fx + x, fy + yOffset, fx + x, fy + fh + yOffset); x += 2.0
}
val (chakraX, chakraY) = (fx + 26.0, fy + (fh * 1.5) + math.sin(waveAngle + (26.0 * 0.12)) * 4.5)
canvas.fill(0, 0, 190); canvas.noStroke(); canvas.ellipse(chakraX, chakraY, 8.5, 8.5); canvas.stroke(255, 255, 255); canvas.strokeWeight(0.6); canvas.point(chakraX, chakraY)
}
def drawPersonCharacter(canvas: CanvasDraw, px: Double, py: Double, isArmy: Boolean, isSaluting: Boolean): Unit = {
canvas.pushMatrix(); canvas.translate(px, py)
canvas.fill(0, 0, 0, 90); canvas.noStroke(); canvas.ellipse(0, -25, 24, 8)
val stepSwing = if (startAnthemPhase) 0.0 else math.sin(globalTime * 10) * 5.5
canvas.stroke(if (isArmy) cm.rgb(35, 65, 22) else cm.rgb(190, 190, 210)); canvas.strokeWeight(5.5)
canvas.line(-5, 0, -6 + stepSwing, -22); canvas.line(5, 0, 6 - stepSwing, -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.strokeWeight(1.5); 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)
if (isArmy) { canvas.fill(50, 80, 30); canvas.rect(-10, 48, 20, 6) } else { canvas.fill(0, 190, 250); canvas.ellipse(0, 42, 10, 8) }
canvas.stroke(if (isArmy) cm.rgb(80, 110, 50) else cm.rgb(230, 230, 240)); canvas.strokeWeight(4.5)
if (isSaluting) { 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 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)
val (fx, fy, fw, fh) = (cwidth / 2.0 - 70.0, cheight / 2.0 + 10.0, 140.0, 26.0); canvas.stroke(230, 230, 230); canvas.strokeWeight(6.5); canvas.line(fx, fy + fh * 3 + 20, fx, fy - 140)
var x = 0.0; while (x < fw) {
val yOffset = math.sin(waveAngle + (x * 0.05)) * 8.5; canvas.strokeWeight(5.2)
canvas.stroke(255, 153, 51); canvas.line(fx + x, fy + fh * 2 + yOffset, fx + x, fy + fh * 3 + yOffset)
canvas.stroke(255, 255, 255); canvas.line(fx + x, fy + fh + yOffset, fx + x, fy + fh * 2 + yOffset)
canvas.stroke(18, 136, 7); canvas.line(fx + x, fy + yOffset, fx + x, fy + fh + yOffset); x += 4.0
}
canvas.fill(0, 0, 190); canvas.noStroke(); canvas.ellipse(fx + 70.0, fy + (fh * 1.5) + math.sin(waveAngle + 3.5) * 8.5, 19, 19)
petalShower.foreach { p =>
canvas.fill(p.col.getRed, p.col.getGreen, p.col.getBlue, 200)
canvas.ellipse(p.x, p.y, p.size, p.size * 0.6)
}
for (i <- 0 until 3) { drawPersonCharacter(canvas, astroParadeWalkX - (i * 35.0), 165.0, false, isParadeInPosition); drawPersonCharacter(canvas, armyParadeWalkX + (i * 35.0), 165.0, true, isParadeInPosition) }
drawPatrioticFullText(canvas)
}
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.0, false, false)
}
def drawLaunchAndUI(canvas: CanvasDraw): Unit = {
if (!isSpaceTravelling && !isLaunched) {
val (btnX, btnY) = (cwidth - 250.0, 250.0)
if (!isTimerRunning && !quizActive) {
canvas.fill(255, 90, 0); canvas.stroke(255, 230, 0); canvas.strokeWeight(math.max(1.0, math.sin(neonGlowPhase * 5) * 2.5 + 4.5).toFloat); canvas.rect(btnX, btnY, 235, 45)
canvas.fill(255, 255, 255); canvas.text("? Launch ISRO Mission", btnX + 22, btnY + 28)
if (isMousePressed && mouseX >= btnX && mouseX <= btnX + 235 && mouseY >= btnY && mouseY <= btnY + 45) { planetLocked = true; isTimerRunning = true; countdownTimer = 10.0 }
}
if (isTimerRunning) { canvas.fill(255, 215, 0); canvas.text(s"Launch Countdown: ${countdownTimer.toInt}s", btnX + 15, btnY + 28) }
}
if (readyToReturnEarth && !isReturnLaunched && !isAstronautReturningIn && !isLandingOnEarth) {
canvas.fill(18, 136, 7); canvas.stroke(0, 255, 160); canvas.strokeWeight(math.max(1.0, math.sin(neonGlowPhase * 5) * 2.5 + 5.0).toFloat); canvas.rect(cwidth / 2.0 - 120, 25, 240, 45)
canvas.fill(255, 255, 255); canvas.text("? Return to Earth", cwidth / 2.0 - 85, 52)
if (isMousePressed && mouseX >= cwidth / 2.0 - 120 && mouseX <= cwidth / 2.0 + 120 && mouseY >= 25 && mouseY <= 70) isAstronautReturningIn = true
}
if (isReturnTimerRunning) { canvas.fill(255, 215, 0); canvas.text(s"Return Time: ${returnCountdownTimer.toInt}s", cwidth / 2.0 - 110, 80) }
}
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 flameSize = math.max(1.0, randomDouble(25.0, 42.0))
canvas.fill(255, 80, 0, 200); canvas.noStroke(); canvas.ellipse(0, -58, 22, flameSize)
canvas.fill(255, 180, 0, 230); canvas.ellipse(0, -54, 14, flameSize * 0.7)
canvas.fill(255, 255, 200, 255); canvas.ellipse(0, -50, 6, flameSize * 0.4)
}
if (!isSpaceTravelling && !isLaunched) { canvas.fill(255, 255, 255); canvas.text("ISRO", -18, 48) }
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)
val reflectAlpha = math.max(0, math.min(255, (math.sin(globalTime * 8) * 40 + 60).toInt))
canvas.fill(255, 255, 255, reflectAlpha); canvas.rect(-10, -42, 4, 40)
canvas.fill(255, 65, 65); canvas.stroke(255, 210, 210); canvas.strokeWeight(2.0); 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) {
canvas.fill(40, 40, 55); canvas.rect(-8, -30, 16, math.max(0.1, 20.0 * doorOpeningProgress))
if (flagPlantProgress > 0.0 && !isFlagPlantedOnPlanet) drawWavingTricolorFlagWithAshokChakra(canvas, -80.0, -20.0)
if (astronautOut && !isLandedOnEarth) {
canvas.pushMatrix(); canvas.translate(-astroWalkToPlantX, -30.0)
drawAstronautPlayer(canvas, 0, 0)
if (astroWalkToPlantX >= 80.0 && flagPlantProgress < 1.0) { canvas.stroke(255, 255, 255); canvas.strokeWeight(3.0); canvas.line(-5, 0, -15, 10) }
canvas.popMatrix()
}
}
canvas.popMatrix()
}
def drawAstronautPlayer(canvas: CanvasDraw, px: Double, py: Double): Unit = {
val legAnim = math.sin(globalTime * 12) * 3.0
canvas.fill(245, 245, 255); canvas.stroke(0, 255, 255); canvas.strokeWeight(1.5); canvas.rect(px - 8, py - 12, 16, 24)
canvas.stroke(200, 200, 200); canvas.strokeWeight(3)
canvas.line(px - 4, py - 12, px - 4 + legAnim, py - 20)
canvas.line(px + 4, py - 12, px + 4 - legAnim, py - 20)
canvas.fill(255, 255, 255); canvas.ellipse(px, py - 18, 14, 14)
val visorBlink = if (math.sin(globalTime * 8) > 0) cm.rgb(0, 190, 250) else cm.rgb(0, 240, 255)
canvas.fill(visorBlink); canvas.noStroke(); canvas.ellipse(px, py - 18, 9, 7)
}
def drawQuizPopup(canvas: CanvasDraw): Unit = {
if (quizActive) {
val (qx, qy) = (cwidth / 2.0 - 225.0, cheight / 2.0 - 130.0)
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 qText = ""; var opts = Array("A", "B", "C", "D"); var corr = 0
getActivePlanetDetailsMap().foreach { pd => if (pd.name == selectedPlanet) { qText = pd.quizQuestion; opts = pd.quizOptions; corr = pd.correctOpt } }
canvas.fill(255, 225, 110); canvas.text(qText, qx + 15, qy + 52)
for (i <- opts.indices) {
val optY = qy + 75.0 + (i * 32.0); canvas.fill(30, 45, 85); canvas.stroke(0, 255, 255); canvas.strokeWeight(1.5); canvas.rect(qx + 15, optY, 420, 26)
canvas.fill(255, 255, 255); canvas.text(opts(i), qx + 25, optY + 18)
if (isMousePressed && mouseX >= qx + 15 && mouseX <= qx + 435 && mouseY >= optY && mouseY <= optY + 26) {
if (i == corr) { quizActive = false; isSpaceTravelling = true; spaceTravelProgress = 0.0; customAlertMessage = "Correct Answer! Mission Continues!"; alertTimer = 3.0 }
else quizFeedback = "Incorrect Answer! Try Again."
}
}
if (quizFeedback != "") { canvas.fill(255, 60, 60); canvas.text(quizFeedback, qx + 20, qy + 235) }
}
}
def drawPlanetInfoPopup(canvas: CanvasDraw): Unit = {
if (infoPanelShown) {
val (ix, iy) = (cwidth / 2.0 - 230.0, cheight / 2.0 - 130.0)
canvas.fill(15, 20, 45, 250); canvas.stroke(0, 255, 255); canvas.strokeWeight(3.5); canvas.rect(ix, iy, 460, 260)
val signalGlow = math.max(0, math.min(255, (math.sin(globalTime * 10) * 100 + 155).toInt))
canvas.fill(0, signalGlow, 255); canvas.noStroke(); canvas.ellipse(ix + 430, iy + 25, 10, 10)
var rName = selectedPlanet; val activeMap = getActivePlanetDetailsMap()
activeMap.foreach { pd => if (pd.name == selectedPlanet) rName = pd.realName }
canvas.fill(255, 215, 0); canvas.text(s" Mission Info: $rName (Page $infoPageNumber/3)", ix + 20, iy + 25)
var (l1, l2, l3, l4, l5, l6, l7, l8) = ("", "", "", "", "", "", "", "")
activeMap.foreach { pd => if (pd.name == selectedPlanet) { l1 = pd.infoLine1; l2 = pd.infoLine2; l3 = pd.infoLine3; l4 = pd.infoLine4; l5 = pd.infoLine5; l6 = pd.infoLine6; l7 = pd.infoLine7; l8 = pd.infoLine8 } }
if (infoPageNumber == 1) { canvas.fill(100, 225, 255); canvas.text(" Primary Planetary Characteristics:", ix + 20, iy + 52); canvas.fill(225, 225, 245); canvas.text(l1, ix + 20, iy + 80); canvas.text(l2, ix + 20, iy + 110); canvas.text(l3, ix + 20, iy + 140) }
else if (infoPageNumber == 2) { canvas.fill(100, 255, 210); canvas.text(" Real Scientific & Distance Data:", ix + 20, iy + 52); canvas.fill(225, 225, 245); canvas.text(l4, ix + 20, iy + 80); canvas.text(l5, ix + 20, iy + 110); canvas.text(l6, ix + 20, iy + 140) }
else { canvas.fill(255, 190, 110); canvas.text(" Atmospheric Spectrum & ISRO Notes:", ix + 20, iy + 52); canvas.fill(225, 225, 245); canvas.text(l7, ix + 20, iy + 80); canvas.text(l8, ix + 20, iy + 110) }
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)
if (isMousePressed && mouseX >= ix + 20 && mouseX <= ix + 120 && mouseY >= iy + 195 && mouseY <= iy + 230) infoPageNumber = if (infoPageNumber == 3) 1 else infoPageNumber + 1
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)
if (isMousePressed && mouseX >= ix + 140 && mouseX <= ix + 290 && mouseY >= iy + 195 && mouseY <= iy + 230) { infoPanelShown = false; isMiniGameActive = true; dieAndResetDevilLevel("Game Started!") }
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 + 300 && mouseX <= ix + 430 && mouseY >= iy + 195 && mouseY <= iy + 230) { infoPanelShown = false; readyToReturnEarth = true }
}
}
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.0, 80.0, 420, 50)
canvas.fill(255, 255, 255); canvas.text(customAlertMessage, cwidth / 2.0 - 195.0, 110.0)
}
}
def drawMiniGames(canvas: CanvasDraw): Unit = {
if (isMiniGameActive) {
val (gx, gy) = (cwidth / 2.0 - 220.0, cheight / 2.0 - 100.0)
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.strokeWeight(1.5)
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, padY, padW) = (gx + 180.0, gy + 20.0, 80.0)
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, padW, 8); canvas.fill(255, 255, 255); canvas.text("TARGET PAD", padX + 5, padY + 18)
canvas.fill(255, 210, 0); canvas.stroke(255, 255, 255); canvas.strokeWeight(1.5); canvas.rect(landerX - 12, landerY - 12, 24, 24)
if (isKeyPressed(Kc.VK_UP) && landerFuel > 0) { canvas.fill(255, 110, 0); canvas.noStroke(); canvas.ellipse(landerX, landerY - 20, 10, 16) }
} else {
canvas.fill(255, 255, 255); canvas.text(s" Rover Adventure Game", gx + 15, gy + 190)
canvas.fill(255, 153, 51); canvas.rect(gx + 15, gy + 170, 410, 12)
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 drawPatrioticFullText(canvas: CanvasDraw): Unit = {
if (charIndex > 0) {
val lines = fullAnthemText.substring(0, charIndex).split("\n"); var startY = cheight - 40.0
for (i <- lines.indices) {
canvas.fill(255, 215, 0, 110); canvas.text(lines(i), 21.0, startY - 1)
canvas.fill(255, 255, 255); canvas.text(lines(i), 20.0, startY); startY -= 18.0
}
}
}
def drawCleanEndScreen(canvas: CanvasDraw): Unit = {
canvas.fill(0, 0, 0); canvas.noStroke(); canvas.rect(0, 0, cwidth, cheight)
val (centerX, centerY) = (cwidth / 2.0, cheight / 2.0); val glowFactor = math.max(1.0, (math.sin(globalTime * 5) * 2 + 4.0).toFloat)
canvas.fill(255, 215, 0); canvas.stroke(255, 160, 0); canvas.strokeWeight(glowFactor); canvas.text("Thank You! Mission Complete", centerX - 110.0, centerY + 60.0)
canvas.fill(0, 255, 255); canvas.stroke(0, 190, 255); canvas.strokeWeight(glowFactor * 0.8); canvas.text("Made by Yadnesh Shahare and Bhavesh Shahare", centerX - 200.0, centerY)
canvas.fill(255, 255, 255); canvas.stroke(255, 255, 255); canvas.strokeWeight(glowFactor * 0.5); canvas.text("Jai Hind! Jai Bharat!", centerX - 80.0, centerY - 60.0)
}
def viewState(canvas: CanvasDraw): Unit = {
if (anthemFinished) drawCleanEndScreen(canvas)
else {
canvas.fill(0, 0, 0, 255); canvas.noStroke(); canvas.rect(0, 0, cwidth, cheight)
for (i <- 0 until starCount) {
val starAlpha = math.max(50, math.min(255, (150 + math.sin(globalTime * 5 + i) * 105).toInt))
canvas.stroke(255, 255, 255, starAlpha)
val starW = math.max(0.1, starSize(i) + (math.sin(globalTime * 6 + i).toFloat * 1.1))
canvas.strokeWeight(starW)
canvas.point(starX(i), starY(i))
}
drawParticleEffects(canvas)
if (isParadeSceneActive) drawParadeGroundScene(canvas)
else if (isLandingOnEarth || isLandedOnEarth) drawEarthFirstLandingScene(canvas)
else {
if (isLaunched) drawPlanetSurfaceBackground(canvas)
else { drawFullSolarSystem(canvas); drawPlanetList(canvas); draw3DPlanetPreview(canvas) }
drawLaunchAndUI(canvas); drawISRORocket(canvas)
}
drawQuizPopup(canvas); drawPlanetInfoPopup(canvas); drawMiniGames(canvas); drawCustomAlertPopup(canvas)
}
}
animateWithCanvasDraw { canvas => updateState(); viewState(canvas) }