Code Sketch
bhavesh shahare
// --- OPERATION SINDOOR: PART 2 - SMOOTH CONTROLS & SLOW ENEMIES ---
cleari()
originBottomLeft()
// --- GAME STATES ---
var gameState = 0
var letterTextTyped = 0
var letterTimer = 0.0
// Game Timer & Target
var gameTimeRemaining = 50.0
var missilesDestroyed = 0
val targetMissiles = 20
// Hero Tank Entry variables
var heroX = cwidth / 2.0 - 150.0
var heroY = 30.0
var heroState = "WALKING"
// Finale Attack Variables
var finaleMissileX = cwidth / 2.0 + 200.0
var finaleMissileY = cheight + 100.0
val officialLetterText =
"TOP SECRET DIRECTIVE: PART 2 - THE RETURN\n" +
"SUBJECT: ASTRONAUT SUMMONED FOR BORDER TACTICAL COMBAT\n\n" +
"Welcome back, Commander.\n" +
"Hostile air strikes & ballistic threats have breached the border.\n" +
"Your Mission: Hold the defense line for 50 SECONDS and\n" +
"neutralize at least 20 ENEMY MISSILES.\n\n" +
"Click 'CONFIRM COMMAND' below to board your Heavy Battle Tank!"
val gravity = Vector2D(0, -0.06)
var waveAngle = 0.0
var chakraRotation = 0.0
var missileX = cwidth / 2.0 - 220.0
var missileY = 140.0
var missileAngle = math.Pi / 2.0
var selectedMissileType = 1
var shootCooldown = 0
var launchCooldown = 0
// --- PARTICLES & EXPLOSIONS SYSTEM ---
class ExplosionSpark(x0: Double, y0: Double, r: Int, g: Int, b: Int) {
var loc = Vector2D(x0, y0)
var vel = Vector2D(randomDouble(-8, 8), randomDouble(-8, 8))
var life = 255.0
def step() {
loc = loc + vel
vel = vel * 0.90
life -= 15.0
}
def view(canvas: CanvasDraw) {
if (life > 0) {
canvas.noStroke()
canvas.fill(r, g, b, life.toInt)
canvas.ellipse(loc.x, loc.y, random(4, 12), random(4, 12))
}
}
}
val sparks = ArrayBuffer.empty[ExplosionSpark]
class Shockwave(val x: Double, val y: Double) {
var radius = 5.0
var alpha = 255.0
var active = true
def step() {
radius += 6.0
alpha -= 18.0
if (alpha <= 0) active = false
}
def view(canvas: CanvasDraw) {
if (active) {
canvas.noFill()
canvas.stroke(255, 100, 0, alpha.toInt)
canvas.strokeWeight(4)
canvas.ellipse(x, y, radius * 2, radius * 2)
}
}
}
val shockwaves = ArrayBuffer.empty[Shockwave]
// --- MISSILE & TANK SYSTEM ---
class StrategicMissile(var x: Double, var y: Double, val vx: Double, val vy: Double, var angle: Double, val mType: Int) {
var active = true
var trail = ArrayBuffer.empty[Vector2D]
def step() {
if (mType == 2) angle += 0.006
else if (mType == 3) angle += math.sin(x * 0.05) * 0.03
val speed = math.sqrt(vx*vx + vy*vy)
x += math.cos(angle) * speed
y += math.sin(angle) * speed
trail.append(Vector2D(x, y))
if (trail.length > 18) trail.remove(0)
if (x < -100 || x > cwidth + 100 || y < -100 || y > cheight + 100) active = false
}
def view(canvas: CanvasDraw) {
canvas.noStroke()
for (i <- 0 until trail.length) {
val alpha = ((i.toDouble / trail.length) * 160).toInt
canvas.fill(255, 140, 0, alpha)
canvas.ellipse(trail(i).x, trail(i).y, 6, 6)
}
canvas.pushMatrix()
canvas.translate(x, y); canvas.rotate(angle)
canvas.fill(220, 220, 230); canvas.stroke(40, 40, 50); canvas.strokeWeight(1)
canvas.rect(-20, -7, 40, 14)
canvas.fill(200, 20, 20); canvas.noStroke(); canvas.ellipse(20, 0, 14, 14)
canvas.popMatrix()
}
}
val strategicMissiles = ArrayBuffer.empty[StrategicMissile]
var tankX = cwidth / 2.0 + 120.0
val tankY = 30.0
var cannonAngle = math.Pi / 2.0
var muzzleFlashTimer = 0
class TankShell(var x: Double, var y: Double, val vx: Double, val vy: Double) {
var active = true
def step() {
x += vx; y += vy
if (x < -50 || x > cwidth + 50 || y < -50 || y > cheight + 50) active = false
}
def view(canvas: CanvasDraw) {
canvas.fill(255, 160, 0); canvas.noStroke()
canvas.ellipse(x, y, 10, 10)
}
}
val tankShells = ArrayBuffer.empty[TankShell]
// ENEMY MISSILE (SLOWER SPEED APPLIED HERE)
class EnemyMissile(var x: Double, var y: Double, var vy: Double) {
var active = true
var exploded = false
def step() {
if (!exploded) {
y += vy // Slower downward movement
if (y < 0) active = false
}
}
def view(canvas: CanvasDraw) {
if (!exploded) {
canvas.pushMatrix()
canvas.translate(x, y); canvas.rotate(-math.Pi / 2)
canvas.fill(180, 30, 30); canvas.stroke(70, 10, 10); canvas.strokeWeight(1)
canvas.rect(-16, -6, 32, 12)
canvas.fill(50, 50, 50); canvas.noStroke(); canvas.ellipse(16, 0, 10, 10)
canvas.popMatrix()
}
}
}
val enemyMissiles = ArrayBuffer.empty[EnemyMissile]
var enemySpawnTimer = 0
// --- TRICOLOR FIREWORKS ---
val tricolorPalette = Array(cm.rgb(255, 153, 51), cm.rgb(255, 255, 255), cm.rgb(18, 136, 7))
class Particle(x0: Double, y0: Double, col: Color, seed: Boolean) {
var location = Vector2D(x0, y0)
private var velocity = if (seed) Vector2D(0, random(7, 12)) else Vector2D(randomNormalDouble, randomNormalDouble) * randomDouble(2, 5)
private var acceleration = Vector2D(0, 0)
var lifespan = 255.0
private var exploded = false
def applyForce(force: Vector2D) { acceleration = acceleration + force }
def shouldExplode: Boolean = !exploded && lifespan <= 0
def checkExplode() { if (seed && velocity.y < 0) lifespan = 0 }
def isDead: Boolean = { if (seed) exploded else lifespan <= 0 }
def explode() { exploded = true }
def step() {
velocity = velocity + acceleration
location = location + velocity
if (!seed) { lifespan -= 5.0; velocity = velocity * 0.94 }
acceleration = Vector2D(0, 0)
checkExplode()
}
def view(canvas: CanvasDraw) {
canvas.stroke(col)
canvas.strokeWeight(if (seed) 4 else 2)
canvas.point(location.x, location.y)
}
}
class Firework() {
private val fireworkColor = tricolorPalette(random(tricolorPalette.length))
private val firework = new Particle(random(cwidth), 0, fireworkColor, true)
private val particles = ArrayBuffer.empty[Particle]
def done: Boolean = firework.isDead && particles.isEmpty
def step() {
particles.filterInPlace(p => !p.isDead)
if (!firework.isDead) {
firework.applyForce(gravity); firework.step()
if (firework.shouldExplode) {
repeat(60) { particles.append(new Particle(firework.location.x, firework.location.y, fireworkColor, false)) }
firework.explode()
}
}
repeatFor(particles) { p => p.applyForce(gravity); p.step() }
}
def view(canvas: CanvasDraw) {
firework.view(canvas)
repeatFor(particles) { p => p.view(canvas) }
}
}
val fireworks = ArrayBuffer.empty[Firework]
def createExplosionEffects(x: Double, y: Double) {
shockwaves.append(new Shockwave(x, y))
for (i <- 0 until 15) sparks.append(new ExplosionSpark(x, y, 255, random(100, 220), 0))
}
// CONTINUOUS HOLD & CONTROL PROCESSOR
def handleContinuousControls() {
if (gameState == 2 && isMousePressed) {
val mx = mouseX; val my = mouseY
// Tank Controls (Hold to move/rotate)
if (mx >= 20 && mx <= 70 && my >= 90 && my <= 125) tankX -= 4.0
else if (mx >= 75 && mx <= 125 && my >= 90 && my <= 125) tankX += 4.0
else if (mx >= 130 && mx <= 180 && my >= 90 && my <= 125) cannonAngle -= 0.04
else if (mx >= 185 && mx <= 235 && my >= 90 && my <= 125) cannonAngle += 0.04
else if (mx >= 240 && mx <= 295 && my >= 90 && my <= 125) {
if (shootCooldown <= 0) {
val tbx = tankX; val tby = tankY + 36.0; val bl = 65.0
val tx = tbx + math.cos(cannonAngle) * bl
val ty = tby + math.sin(cannonAngle) * bl
tankShells.append(new TankShell(tx, ty, math.cos(cannonAngle) * 10.0, math.sin(cannonAngle) * 10.0))
muzzleFlashTimer = 5
shootCooldown = 12 // Rate of fire control
}
}
// Missile Launcher Controls (Hold to rotate)
else if (mx >= 300 && mx <= 345 && my >= 90 && my <= 125) missileAngle -= 0.04
else if (mx >= 350 && mx <= 395 && my >= 90 && my <= 125) missileAngle += 0.04
else if (mx >= 400 && mx <= 445 && my >= 90 && my <= 125) selectedMissileType = 1
else if (mx >= 450 && mx <= 495 && my >= 90 && my <= 125) selectedMissileType = 2
else if (mx >= 500 && mx <= 545 && my >= 90 && my <= 125) selectedMissileType = 3
else if (mx >= 555 && mx <= 630 && my >= 90 && my <= 125) {
if (launchCooldown <= 0) {
val mTipX = missileX + math.cos(missileAngle) * 55.0
val mTipY = missileY + math.sin(missileAngle) * 55.0
strategicMissiles.append(new StrategicMissile(mTipX, mTipY, math.cos(missileAngle) * 7.5, math.sin(missileAngle) * 7.5, missileAngle, selectedMissileType))
launchCooldown = 18
}
}
}
}
def updateState() {
if (shootCooldown > 0) shootCooldown -= 1
if (launchCooldown > 0) launchCooldown -= 1
handleContinuousControls()
if (gameState == 0) {
letterTimer += 0.04
if (letterTimer >= 0.03 && letterTextTyped < officialLetterText.length) {
letterTextTyped += 1; letterTimer = 0.0
}
return
}
if (gameState == 1) {
if (heroX < tankX - 15) {
heroX += 2.5
} else {
heroState = "BOARDED"
gameState = 2
}
return
}
if (gameState == 2) {
gameTimeRemaining -= 0.033
if (gameTimeRemaining <= 0.0) {
gameTimeRemaining = 0.0
gameState = 3 // Trigger High-Speed Finale Attack
}
fireworks.filterInPlace(f => !f.done)
if (randomDouble(1) < 0.06) fireworks.append(new Firework())
repeatFor(fireworks)(_.step())
sparks.filterInPlace(_.life > 0); repeatFor(sparks)(_.step())
shockwaves.filterInPlace(_.active); repeatFor(shockwaves)(_.step())
waveAngle += 0.08; chakraRotation += 0.05
if (tankX < 60.0) tankX = 60.0
if (tankX > cwidth - 60.0) tankX = cwidth - 60.0
if (muzzleFlashTimer > 0) muzzleFlashTimer -= 1
enemySpawnTimer += 1
if (enemySpawnTimer >= 80) {
enemySpawnTimer = 0
val ex = random(100, cwidth - 100)
enemyMissiles.append(new EnemyMissile(ex, cheight + 20.0, -0.45)) // VERY SLOW MISSILE SPEED (-0.45)
}
tankShells.filterInPlace(_.active); repeatFor(tankShells)(_.step())
strategicMissiles.filterInPlace(_.active); repeatFor(strategicMissiles)(_.step())
enemyMissiles.filterInPlace(_.active); repeatFor(enemyMissiles)(_.step())
repeatFor(enemyMissiles) { em =>
if (!em.exploded) {
repeatFor(tankShells) { ts =>
if (ts.active && math.abs(ts.x - em.x) < 30 && math.abs(ts.y - em.y) < 30) {
em.exploded = true; ts.active = false
missilesDestroyed += 1
createExplosionEffects(em.x, em.y)
}
}
repeatFor(strategicMissiles) { sm =>
if (sm.active && math.abs(sm.x - em.x) < 40 && math.abs(sm.y - em.y) < 40) {
em.exploded = true; sm.active = false
missilesDestroyed += 1
createExplosionEffects(em.x, em.y)
}
}
}
}
}
// STATE 3: FINALE MISSILE STRIKE & GAME END GUARANTEE
if (gameState == 3) {
val targetX = tankX
val targetY = tankY + 20.0
finaleMissileX += (targetX - finaleMissileX) * 0.2
finaleMissileY -= 15.0
if (finaleMissileY <= targetY + 15.0) {
createExplosionEffects(tankX, tankY + 30.0)
for (i <- 0 until 60) sparks.append(new ExplosionSpark(tankX, tankY + 30.0, 255, random(50, 255), 0))
gameState = 4 // GUARANTEED END TO STATE 4
}
}
if (gameState == 4) {
sparks.filterInPlace(_.life > 0); repeatFor(sparks)(_.step())
shockwaves.filterInPlace(_.active); repeatFor(shockwaves)(_.step())
}
}
// --- DRAWING FUNCTIONS ---
def drawHeroAstronaut(canvas: CanvasDraw, x: Double, y: Double) {
if (heroState == "BOARDED") return
canvas.fill(240, 240, 255); canvas.ellipse(x, y + 22, 14, 14)
canvas.fill(0, 150, 255); canvas.ellipse(x, y + 23, 8, 6)
canvas.fill(40, 70, 40); canvas.rect(x - 5, y, 10, 16)
canvas.stroke(20, 20, 20); canvas.strokeWeight(2)
canvas.line(x - 3, y, x - 5, y - 8); canvas.line(x + 3, y, x + 5, y - 8)
}
def drawLauncherPad(canvas: CanvasDraw, mx: Double, my: Double) {
canvas.fill(40, 45, 55); canvas.stroke(20, 25, 30); canvas.strokeWeight(2)
canvas.ellipse(mx, my, 58, 58)
canvas.pushMatrix()
canvas.translate(mx, my); canvas.rotate(missileAngle)
canvas.fill(60, 75, 70); canvas.stroke(30, 40, 35); canvas.strokeWeight(2)
canvas.rect(-14, -16, 62, 32)
canvas.fill(220, 30, 30); canvas.rect(16, -16, 8, 32)
canvas.popMatrix()
}
def drawRealisticArmyTank(canvas: CanvasDraw, tx: Double, ty: Double) {
if (gameState == 4) return // Hide tank after explosion
canvas.fill(30, 33, 38); canvas.stroke(10, 10, 12); canvas.strokeWeight(1)
canvas.rect(tx - 50, ty, 100, 22)
canvas.fill(70, 75, 70)
for (i <- -4 to 4) canvas.ellipse(tx + (i * 11), ty + 11, 9, 9)
canvas.fill(50, 80, 45); canvas.rect(tx - 40, ty + 22, 80, 20)
canvas.fill(40, 70, 35); canvas.ellipse(tx, ty + 44, 44, 24)
val turretBaseX = tx; val turretBaseY = ty + 44.0; val barrelLength = 65.0
canvas.stroke(25, 50, 20); canvas.strokeWeight(8)
val tipX = turretBaseX + math.cos(cannonAngle) * barrelLength
val tipY = turretBaseY + math.sin(cannonAngle) * barrelLength
canvas.line(turretBaseX, turretBaseY, tipX, tipY)
if (muzzleFlashTimer > 0) {
canvas.noStroke(); canvas.fill(255, 160, 0, 230); canvas.ellipse(tipX, tipY, 40, 40)
}
}
def drawControlButtons(canvas: CanvasDraw) {
canvas.fill(15, 20, 35, 220); canvas.stroke(0, 255, 200); canvas.strokeWeight(1)
canvas.rect(15, 85, 665, 50)
canvas.fill(50, 50, 80); canvas.rect(20, 95, 50, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("<- T", 30, 105)
canvas.fill(50, 50, 80); canvas.rect(75, 95, 50, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("T ->", 85, 105)
canvas.fill(70, 40, 40); canvas.rect(130, 95, 50, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("<- C", 140, 105)
canvas.fill(70, 40, 40); canvas.rect(185, 95, 50, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("C ->", 195, 105)
canvas.fill(200, 70, 0); canvas.rect(240, 95, 55, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("FIRE", 252, 105)
canvas.fill(40, 90, 90); canvas.rect(300, 95, 45, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("<- M", 308, 105)
canvas.fill(40, 90, 90); canvas.rect(350, 95, 45, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("M ->", 358, 105)
canvas.fill(if (selectedMissileType == 1) 180 else 80, 30, 120); canvas.rect(400, 95, 45, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("BRM", 408, 105)
canvas.fill(if (selectedMissileType == 2) 180 else 80, 30, 120); canvas.rect(450, 95, 45, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("AGN", 458, 105)
canvas.fill(if (selectedMissileType == 3) 180 else 80, 30, 120); canvas.rect(500, 95, 45, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("SUD", 508, 105)
canvas.fill(140, 0, 180); canvas.rect(555, 95, 75, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("LAUNCH", 568, 105)
}
def drawHUD(canvas: CanvasDraw) {
canvas.fill(10, 15, 30, 230); canvas.stroke(255, 215, 0); canvas.strokeWeight(1.5)
canvas.rect(20, cheight - 50, 300, 38)
canvas.fill(255, 215, 0)
canvas.text(s"? TIME: ${gameTimeRemaining.toInt}s | ? KILLS: $missilesDestroyed / $targetMissiles", 32, cheight - 38)
}
def drawWavingTricolorFlag(canvas: CanvasDraw) {
val fw = 160.0; val fh = 28.0
val fx = (cwidth - fw) / 2.0 + 130.0
val fy = cheight / 2.0 - 20.0
canvas.stroke(210, 210, 210); canvas.strokeWeight(6)
canvas.line(fx, fy + fh * 3 + 15, fx, fy - 170)
val stepSize = 4.0
var x = 0.0
while (x < fw) {
val yOffset = math.sin(waveAngle + (x * 0.03)) * 8.0
canvas.stroke(255, 153, 51); canvas.strokeWeight(stepSize + 1)
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 += stepSize
}
val chakraX = fx + fw / 2.0
val chakraY = fy + fh + (fh / 2.0) + (math.sin(waveAngle + ((fw / 2.0) * 0.03)) * 8.0)
val radius = 11.0
canvas.stroke(0, 0, 130); canvas.strokeWeight(2); canvas.noFill()
canvas.ellipse(chakraX, chakraY, radius * 2, radius * 2)
}
def drawIntroLetter(canvas: CanvasDraw) {
canvas.fill(5, 8, 20); canvas.noStroke(); canvas.rect(0, 0, cwidth, cheight)
val (lx, ly, lw, lh) = (cwidth / 2.0 - 270.0, cheight / 2.0 - 150.0, 540.0, 320.0)
canvas.fill(15, 25, 45, 245); canvas.stroke(0, 255, 200); canvas.strokeWeight(2.5); canvas.rect(lx, ly, lw, lh)
canvas.fill(255, 215, 0); canvas.text("? PART 2: THE ASTRONAUT RETURNS AS ARMY HERO ??", lx + 60, ly + 25)
if (letterTextTyped > 0) {
val printedText = officialLetterText.substring(0, letterTextTyped)
val lines = printedText.split("\n")
var currY = ly + 55.0
for (line <- lines) {
canvas.fill(220, 240, 255)
canvas.text(line, lx + 20, currY)
currY += 17.0
}
}
if (letterTextTyped >= officialLetterText.length) {
val btnX = lx + 170.0; val btnY = ly + 20.0
canvas.fill(0, 180, 120); canvas.stroke(255, 255, 255); canvas.strokeWeight(2.0)
canvas.rect(btnX, btnY, 200, 36)
canvas.fill(255, 255, 255); canvas.text("CONFIRM COMMAND ?", btnX + 22, btnY + 23)
if (isMousePressed && mouseX >= btnX && mouseX <= btnX + 200 && mouseY >= btnY && mouseY <= btnY + 36) {
gameState = 1
}
}
}
// MARTYRDOM SCREEN
def drawMartyrTributeScreen(canvas: CanvasDraw) {
canvas.fill(10, 5, 5, 240); canvas.noStroke(); canvas.rect(0, 0, cwidth, cheight)
val (cx, cy) = (cwidth / 2.0, cheight / 2.0)
canvas.fill(255, 50, 50); canvas.text("? ENEMY SUPER-MISSILE IMPACT DETECTED ?", cx - 180, cy + 90)
canvas.fill(255, 215, 0)
canvas.text("?? A BRAVE SOLDIER HAS BEEN MARTYRED ??", cx - 170, cy + 40)
canvas.fill(240, 240, 240)
canvas.text(s"FINAL COMBAT STATS: Neutralized $missilesDestroyed Enemy Missiles", cx - 160, cy)
canvas.text("His sacrifice protected the nation till the final breath.", cx - 165, cy - 30)
canvas.fill(0, 255, 200); canvas.text("SALUTE TO OUR BRAVE HERO! AMAR RAHE! ?", cx - 150, cy - 80)
}
def viewState(canvas: CanvasDraw) {
if (gameState == 0) {
drawIntroLetter(canvas); return
}
canvas.fill(8, 10, 22, 50); canvas.noStroke(); canvas.rect(0, 0, cwidth, cheight)
repeatFor(fireworks)(_.view(canvas))
drawLauncherPad(canvas, missileX, missileY)
drawRealisticArmyTank(canvas, tankX, tankY)
drawHeroAstronaut(canvas, heroX, heroY)
if (gameState == 2) {
drawControlButtons(canvas)
drawHUD(canvas)
}
repeatFor(sparks)(_.view(canvas))
repeatFor(shockwaves)(_.view(canvas))
repeatFor(tankShells)(_.view(canvas))
repeatFor(strategicMissiles)(_.view(canvas))
repeatFor(enemyMissiles)(_.view(canvas))
if (gameState == 3) {
canvas.pushMatrix()
canvas.translate(finaleMissileX, finaleMissileY); canvas.rotate(-math.Pi / 2)
canvas.fill(255, 0, 0); canvas.rect(-30, -12, 60, 24)
canvas.fill(255, 255, 0); canvas.ellipse(30, 0, 24, 24)
canvas.popMatrix()
}
if (gameState == 4) {
drawMartyrTributeScreen(canvas)
} else {
drawWavingTricolorFlag(canvas)
}
}
animateWithCanvasDraw { canvas =>
updateState()
viewState(canvas)
}