Code Sketch


bhavesh shailesh shahare
By: Mhalsakant School
Category: Programming
// --- OPERATION SINDOOR: ULTIMATE ATTACK & DEFENCE EDITION ---
cleari()
originBottomLeft()

// --- GAME STATES ---
// 0: Intro, 1: Letter, 2: Mode Selection, 3: Cutscene, 4: Gameplay, 5: Blast Climax, 6: Final End Screen
var gameState = 0
var letterTimer = 0
var gameMode = "DEFENCE" // "DEFENCE" or "ATTACK"

// --- ATTACK MODE ENEMY BASE HEALTH ---
var enemyBaseHealth = 100

// --- CUTSCENE VARIABLES ---
var cutsceneTankX = cwidth + 120.0
var heroX = -50.0
var cutscenePhase = 1 
var hatchOpen = false
var heroInside = false

// NUKE CLIMAX BLAST VARIABLES
var nukeX = cwidth / 2.0
var nukeY = cheight + 200.0
var nukeSpeed = 35.0
var blastRadius = 0.0

// --- GAMEPLAY VARIABLES ---
var timer = 40.0
val targetKills = 25
var interceptedCount = 0
var baseHealth = 100

// Player Tank Position
var playerTankX = cwidth / 2.0
val playerTankY = 35.0
var playerCannonAngle = math.Pi / 2.0
var muzzleFlashTimer = 0

// Rapid Fire Cooldown
var fireCooldown = 0

// --- ALLY TANKS (DEFENCE MODE ONLY) ---
class AllyTank(val x: Double, val y: Double) {
    var cannonAngle = math.Pi / 2.0
    var fireTimer = random(20, 50)
    var lockedTarget: EnemyTarget = null

    def step(targets: ArrayBuffer[EnemyTarget], shells: ArrayBuffer[TankShell]) {
        fireTimer -= 1
        var minDist = 9999.0
        lockedTarget = null
        repeatFor(targets) { em =>
            val d = math.hypot(em.x - x, em.y - y)
            if (d < minDist && em.y > y && !em.exploded) {
                minDist = d
                lockedTarget = em
            }
        }

        if (lockedTarget != null) {
            val dx = lockedTarget.x - x
            val dy = lockedTarget.y - y
            cannonAngle = math.atan2(dy, dx)
        }

        if (fireTimer <= 0 && lockedTarget != null) {
            val tx = x + math.cos(cannonAngle) * 55.0
            val ty = y + math.sin(cannonAngle) * 55.0
            shells.append(new TankShell(tx, ty, math.cos(cannonAngle) * 18.0, math.sin(cannonAngle) * 18.0, true))
            fireTimer = random(20, 45)
        }
    }

    def view(canvas: CanvasDraw) {
        canvas.fill(20, 30, 25); canvas.stroke(0, 255, 200); canvas.strokeWeight(1)
        canvas.rect(x - 38, y, 76, 18)
        canvas.fill(35, 50, 40); canvas.rect(x - 30, y + 18, 60, 16)
        canvas.fill(0, 200, 150, 200); canvas.ellipse(x, y + 34, 30, 20)

        canvas.stroke(0, 255, 255); canvas.strokeWeight(5)
        canvas.line(x, y + 34, x + math.cos(cannonAngle) * 50.0, y + math.sin(cannonAngle) * 50.0)
    }
}
val allyTanks = ArrayBuffer(new AllyTank(140, 35), new AllyTank(cwidth - 140, 35))

// --- PARTICLES ---
class BlastParticle(x0: Double, y0: Double, r: Int, g: Int, b: Int) {
    var loc = Vector2D(x0, y0)
    var vel = Vector2D(randomDouble(-10, 10), randomDouble(-10, 10))
    var life = 255.0

    def step() { loc = loc + vel; life -= 10.0 }

    def view(canvas: CanvasDraw) {
        if (life > 0) {
            canvas.noStroke()
            canvas.fill(r, g, b, life.toInt)
            canvas.ellipse(loc.x, loc.y, random(8, 16), random(8, 16))
        }
    }
}
val blastParticles = ArrayBuffer.empty[BlastParticle]

// --- PLAYER DRAG MISSILES ---
class StrategicMissile(var x: Double, var y: Double, var angle: Double) {
    var active = true
    def step() {
        x += math.cos(angle) * 25.0
        y += math.sin(angle) * 25.0
        if (x < -100 || x > cwidth + 100 || y < -100 || y > cheight + 100) active = false
    }

    def view(canvas: CanvasDraw) {
        canvas.pushMatrix()
        canvas.translate(x, y); canvas.rotate(angle)
        canvas.fill(255, 50, 0); canvas.stroke(255, 255, 0); canvas.strokeWeight(2)
        canvas.rect(-22, -8, 44, 16)
        canvas.fill(0, 255, 255); canvas.ellipse(22, 0, 14, 14)
        canvas.popMatrix()
    }
}
val strategicMissiles = ArrayBuffer.empty[StrategicMissile]

class TankShell(var x: Double, var y: Double, val vx: Double, val vy: Double, val isAlly: Boolean) {
    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.noStroke()
        if (isAlly) {
            canvas.fill(0, 255, 255, 220); canvas.ellipse(x, y, 10, 10)
        } else {
            canvas.fill(255, 220, 0); canvas.ellipse(x, y, 16, 16)
        }
    }
}
val tankShells = ArrayBuffer.empty[TankShell]

// --- ENEMY TARGETS ---
class EnemyTarget(var x: Double, var y: Double, var vy: Double, val mName: String, val isBase: Boolean = false) {
    var active = true
    var exploded = false
    var explosionTimer = 0

    def step() {
        if (!exploded) {
            if (!isBase) {
                y += vy
                if (y < 15) { 
                    active = false
                    // ATTACK MODE ????? ??????? ?????? ????? ????! (No Damage in Attack Mode)
                    if (gameMode == "DEFENCE") {
                        baseHealth -= 10
                    }
                }
            }
        } else {
            explosionTimer += 1
            if (explosionTimer > 15) active = false
        }
    }

    def view(canvas: CanvasDraw) {
        if (!exploded) {
            if (isBase) {
                canvas.fill(120, 30, 30); canvas.stroke(255, 0, 0); canvas.strokeWeight(2)
                canvas.rect(x - 45, y, 90, 30)
                canvas.fill(60, 10, 10); canvas.rect(x - 30, y + 30, 60, 20)
                canvas.fill(255, 255, 255); canvas.text(mName, x - 35, y + 10)
            } else {
                canvas.pushMatrix()
                canvas.translate(x, y); canvas.rotate(-math.Pi / 2)
                canvas.fill(180, 20, 20); canvas.stroke(255, 255, 0); canvas.strokeWeight(2)
                canvas.rect(-20, -8, 40, 16)
                canvas.fill(255, 255, 255); canvas.ellipse(20, 0, 14, 14)
                canvas.popMatrix()
                
                canvas.fill(255, 100, 100); canvas.text(mName, x - 20, y + 18)
            }
        } else {
            canvas.noStroke(); canvas.fill(255, 100, 0, 220)
            canvas.ellipse(x, y, 60 + explosionTimer * 3, 60 + explosionTimer * 3)
        }
    }
}
val enemyTargets = ArrayBuffer.empty[EnemyTarget]
var enemySpawnTimer = 0

// --- CUTSCENE HELPERS ---
def drawArmyMan(canvas: CanvasDraw, x: Double, y: Double) {
    canvas.pushMatrix(); canvas.translate(x, y)
    canvas.fill(20, 20, 20); canvas.noStroke(); canvas.rect(-7, 0, 5, 8); canvas.rect(2, 0, 5, 8)
    canvas.fill(40, 60, 30); canvas.rect(-7, 8, 6, 20); canvas.rect(1, 8, 6, 20)
    canvas.fill(55, 80, 40); canvas.rect(-9, 28, 18, 22)
    canvas.fill(220, 170, 130); canvas.ellipse(0, 56, 12, 14)
    canvas.fill(30, 50, 20); canvas.ellipse(0, 60, 16, 10)
    canvas.popMatrix()
}

def drawCutsceneTank(canvas: CanvasDraw, tx: Double, ty: Double) {
    canvas.fill(30, 33, 38); canvas.stroke(10, 10, 12); canvas.strokeWeight(1)
    canvas.rect(tx - 50, ty, 100, 22)
    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)

    if (hatchOpen) {
        canvas.fill(15, 15, 15); canvas.ellipse(tx, ty + 53, 20, 8)
    } else {
        canvas.fill(25, 30, 25); canvas.ellipse(tx, ty + 53, 22, 8)
    }
    canvas.stroke(25, 50, 20); canvas.strokeWeight(8)
    canvas.line(tx, ty + 44, tx - 60, ty + 44)
}

def updateCutscene() {
    val targetTankX = cwidth / 2.0 + 80.0
    val targetHeroX = targetTankX - 5.0

    if (cutscenePhase == 1) {
        if (cutsceneTankX > targetTankX) cutsceneTankX -= 4.0
        if (heroX < targetHeroX) heroX += 3.0
        if (cutsceneTankX <= targetTankX && heroX >= targetHeroX) cutscenePhase = 2
    } else if (cutscenePhase == 2) {
        hatchOpen = true; cutscenePhase = 3
    } else if (cutscenePhase == 3) {
        heroInside = true; cutscenePhase = 4
    } else if (cutscenePhase == 4) {
        hatchOpen = false; cutscenePhase = 5
    } else if (cutscenePhase == 5) {
        cutsceneTankX -= 5.0
        if (cutsceneTankX < -150) {
            gameState = 4 // START GAMEPLAY
            if (gameMode == "ATTACK") {
                enemyTargets.append(new EnemyTarget(cwidth / 2.0 - 150, cheight - 80, 0, "PAK-BASE 1", true))
                enemyTargets.append(new EnemyTarget(cwidth / 2.0 + 150, cheight - 80, 0, "PAK-BASE 2", true))
            }
        }
    }
}

// --- UPDATE GAMEPLAY ---
def updateState() {
    if (gameState == 0) {
        if (isMousePressed) { gameState = 1; letterTimer = 40 }
        return
    }
    if (gameState == 1) {
        if (letterTimer > 0) letterTimer -= 1
        else if (isMousePressed) gameState = 2
        return
    }
    if (gameState == 2) {
        if (isMousePressed) {
            val defBtnX = cwidth / 2.0 - 220.0
            val attBtnX = cwidth / 2.0 + 40.0
            val btnY = cheight / 2.0 - 30.0

            if (mouseX >= defBtnX && mouseX <= defBtnX + 180 && mouseY >= btnY && mouseY <= btnY + 70) {
                gameMode = "DEFENCE"
                gameState = 3
            } else if (mouseX >= attBtnX && mouseX <= attBtnX + 180 && mouseY >= btnY && mouseY <= btnY + 70) {
                gameMode = "ATTACK"
                gameState = 3
            }
        }
        return
    }

    if (gameState == 3) { updateCutscene(); return }

    if (gameState == 5) {
        if (nukeY > cheight / 2.0) {
            nukeY -= nukeSpeed
        } else {
            blastRadius += 40.0
            if (blastRadius > cwidth * 1.6) {
                gameState = 6
            }
        }
        return
    }

    if (gameState == 6) return

    timer -= 0.03

    if (gameMode == "DEFENCE") {
        if (timer <= 0 || baseHealth <= 0 || interceptedCount >= targetKills) {
            gameState = 5
        }
    } else {
        // ATTACK MODE WIN CONDITION (?????? ????? ????!)
        if (timer <= 0 || enemyBaseHealth <= 0) {
            gameState = 5
        }
    }

    blastParticles.filterInPlace(_.life > 0); repeatFor(blastParticles)(_.step())

    val dx = mouseX - playerTankX
    val dy = mouseY - (playerTankY + 44.0)
    playerCannonAngle = math.atan2(dy, dx)

    if (fireCooldown > 0) fireCooldown -= 1
    if (isMousePressed && fireCooldown <= 0) {
        val tx = playerTankX + math.cos(playerCannonAngle) * 65.0
        val ty = (playerTankY + 44.0) + math.sin(playerCannonAngle) * 65.0
        
        strategicMissiles.append(new StrategicMissile(tx, ty, playerCannonAngle))
        tankShells.append(new TankShell(tx, ty, math.cos(playerCannonAngle) * 25.0, math.sin(playerCannonAngle) * 25.0, false))
        
        muzzleFlashTimer = 3
        fireCooldown = 1
    }
    if (muzzleFlashTimer > 0) muzzleFlashTimer -= 1

    if (gameMode == "DEFENCE") {
        repeatFor(allyTanks) { at => at.step(enemyTargets, tankShells) }
    }

    // ENEMY MISSILE SPAWN (ATTACK MODE ????? ?????? ?????)
    enemySpawnTimer += 1
    val spawnRate = if (gameMode == "ATTACK") 45 else 25 // Attack Mode ????? ?????? ?????
    val missileSpeed = if (gameMode == "ATTACK") -2.5 else -4.5 // ??? ??? ?????

    if (enemySpawnTimer >= spawnRate) {
        enemySpawnTimer = 0
        val names = Array("PAKF-16", "BABUR-2", "SHAHEEN-3")
        enemyTargets.append(new EnemyTarget(random(60, cwidth - 60), cheight + 20.0, missileSpeed, names(random(names.length))))
    }

    tankShells.filterInPlace(_.active); repeatFor(tankShells)(_.step())
    strategicMissiles.filterInPlace(_.active); repeatFor(strategicMissiles)(_.step())
    enemyTargets.filterInPlace(_.active); repeatFor(enemyTargets)(_.step())

    repeatFor(enemyTargets) { em =>
        if (!em.exploded) {
            repeatFor(tankShells) { ts =>
                if (ts.active && math.abs(ts.x - em.x) < 45 && math.abs(ts.y - em.y) < 45) {
                    if (em.isBase) {
                        enemyBaseHealth -= 3
                    } else {
                        em.exploded = true
                        interceptedCount += 1
                    }
                    ts.active = false
                    for (i <- 0 until 10) blastParticles.append(new BlastParticle(em.x, em.y, 255, 120, 0))
                }
            }
            repeatFor(strategicMissiles) { sm =>
                if (sm.active && math.abs(sm.x - em.x) < 50 && math.abs(sm.y - em.y) < 50) {
                    if (em.isBase) {
                        enemyBaseHealth -= 6
                    } else {
                        em.exploded = true
                        interceptedCount += 1
                    }
                    sm.active = false
                    for (i <- 0 until 15) blastParticles.append(new BlastParticle(em.x, em.y, 0, 255, 255))
                }
            }
        }
    }
}

// --- DRAW TANK ---
def drawPlayerTank(canvas: CanvasDraw, tx: Double, ty: Double) {
    canvas.stroke(255, 0, 0, 180); canvas.strokeWeight(1)
    canvas.line(tx, ty + 40, mouseX, mouseY)

    canvas.fill(20, 25, 30); canvas.stroke(0, 255, 150); canvas.strokeWeight(1)
    canvas.rect(tx - 50, ty, 100, 22)
    canvas.fill(40, 70, 50); canvas.rect(tx - 40, ty + 22, 80, 20)
    canvas.fill(20, 90, 60); canvas.ellipse(tx, ty + 42, 46, 24)

    val turretBaseX = tx; val turretBaseY = ty + 42.0
    canvas.stroke(255, 200, 0); canvas.strokeWeight(10)
    val tipX = turretBaseX + math.cos(playerCannonAngle) * 65.0
    val tipY = turretBaseY + math.sin(playerCannonAngle) * 65.0
    canvas.line(turretBaseX, turretBaseY, tipX, tipY)

    if (muzzleFlashTimer > 0) {
        canvas.noStroke(); canvas.fill(255, 255, 0, 250); canvas.ellipse(tipX, tipY, 45, 45)
    }
}

def drawHUD(canvas: CanvasDraw) {
    canvas.fill(255, 215, 0)
    canvas.text(s"TIME: ${math.max(0, timer.toInt)}s", 20, cheight - 20)
    
    if (gameMode == "DEFENCE") {
        canvas.text(s"DESTROYED: $interceptedCount / $targetKills", 140, cheight - 20)
        canvas.text(s"SHIELD: $baseHealth%", 380, cheight - 20)
        canvas.fill(0, 255, 200); canvas.text("MODE: DEFENCE (PROTECT BORDER FROM MISSILES)", 120, cheight - 45)
    } else {
        canvas.fill(255, 50, 50); canvas.text(s"PAK BASE HEALTH: ${math.max(0, enemyBaseHealth)}%", 140, cheight - 20)
        canvas.fill(255, 215, 0); canvas.text(s"DESTROYED TARGETS: $interceptedCount", 380, cheight - 20)
        canvas.fill(0, 255, 150); canvas.text("MODE: ATTACK (GOD MODE: YOU ARE INVINCIBLE! DESTROY ENEMY!)", 120, cheight - 45)
    }
}

// --- SCENE RENDERING ---
def drawIntroPart2(canvas: CanvasDraw) {
    canvas.fill(5, 5, 10); canvas.rect(0, 0, cwidth, cheight)
    canvas.fill(255, 50, 50); canvas.text("THIS IS A SECOND PART", cwidth / 2.0 - 130, cheight / 2.0 + 40)
    canvas.fill(255, 215, 0); canvas.text("OPERATION SINDOOR: BORDER WARRIORS", cwidth / 2.0 - 210, cheight / 2.0)
    canvas.fill(200, 200, 200); canvas.text("CLICK ANYWHERE TO START BRIEFING", cwidth / 2.0 - 150, cheight / 2.0 - 50)
}

def drawLetterScreen(canvas: CanvasDraw) {
    canvas.fill(245, 240, 220); canvas.stroke(100, 80, 50); canvas.strokeWeight(3)
    val lx = cwidth / 2.0 - 320.0; val ly = cheight / 2.0 - 220.0
    canvas.rect(lx, ly, 640, 440)

    canvas.fill(180, 30, 30); canvas.noStroke(); canvas.rect(lx, ly + 380, 640, 60)
    canvas.fill(255, 255, 255); canvas.text("TOP SECRET // COMMAND DIRECTIVE", lx + 20, ly + 400)

    canvas.fill(20, 20, 30)
    canvas.text("A. COMMANDER BRIEFING: Border area is under alert.", lx + 20, ly + 340)
    canvas.text("B. CHOOSE TACTICS: Take DEFENCE to protect or ATTACK to destroy!", lx + 20, ly + 300)
    canvas.text("C. HIGH POWER WEAPONS: Rapid Missiles & Invincible Armor in Attack Mode.", lx + 20, ly + 260)

    if (letterTimer > 0) {
        canvas.fill(150, 150, 150); canvas.rect(lx + 170, ly + 20, 300, 40)
        canvas.fill(255, 255, 255); canvas.text("LOADING OPTIONS...", lx + 230, ly + 38)
    } else {
        canvas.fill(20, 140, 40); canvas.rect(lx + 170, ly + 20, 300, 40)
        canvas.fill(255, 255, 255); canvas.text("CLICK TO CHOOSE MODE", lx + 210, ly + 38)
    }
}

def drawModeSelectionScreen(canvas: CanvasDraw) {
    canvas.fill(10, 15, 25); canvas.rect(0, 0, cwidth, cheight)

    canvas.fill(255, 215, 0)
    canvas.text("CHOOSE YOUR TACTICAL STRATEGY", cwidth / 2.0 - 210, cheight / 2.0 + 130)

    val defBtnX = cwidth / 2.0 - 220.0
    val attBtnX = cwidth / 2.0 + 40.0
    val btnY = cheight / 2.0 - 30.0

    // TAKE DEFENCE BUTTON
    canvas.fill(20, 100, 200); canvas.stroke(0, 255, 255); canvas.strokeWeight(2)
    canvas.rect(defBtnX, btnY, 180, 70)
    canvas.fill(255, 255, 255)
    canvas.text("TAKE DEFENCE", defBtnX + 22, btnY + 40)

    // TAKE ATTACK BUTTON
    canvas.fill(200, 40, 40); canvas.stroke(255, 255, 0); canvas.strokeWeight(2)
    canvas.rect(attBtnX, btnY, 180, 70)
    canvas.fill(255, 255, 255)
    canvas.text("TAKE ATTACK", attBtnX + 30, btnY + 40)

    canvas.fill(200, 200, 200)
    canvas.text("[DEFENCE]: Defend Indian Border as Army Tank Hero!", cwidth / 2.0 - 220, btnY - 30)
    canvas.text("[ATTACK]: Unlimited Missiles, Invincible Armor & Destroy Pak Base!", cwidth / 2.0 + 10, btnY - 30)
}

def drawClimaxBlast(canvas: CanvasDraw) {
    canvas.fill(10, 10, 15); canvas.rect(0, 0, cwidth, cheight)

    if (nukeY > cheight / 2.0) {
        canvas.fill(255, 30, 0); canvas.stroke(255, 255, 0); canvas.strokeWeight(3)
        canvas.rect(nukeX - 15, nukeY, 30, 90)
        canvas.fill(255, 255, 255); canvas.ellipse(nukeX, nukeY, 30, 30)
    } else {
        canvas.noStroke(); canvas.fill(255, 100, 0, 240)
        canvas.ellipse(cwidth / 2.0, cheight / 2.0, blastRadius, blastRadius)
        canvas.fill(255, 255, 200, 250)
        canvas.ellipse(cwidth / 2.0, cheight / 2.0, blastRadius * 0.6, blastRadius * 0.6)
    }
}

// FINAL BLACK END SCREEN
def drawFinalBlackScreen(canvas: CanvasDraw) {
    canvas.fill(0, 0, 0); canvas.rect(0, 0, cwidth, cheight)

    if (gameMode == "ATTACK" && enemyBaseHealth <= 0) {
        canvas.fill(0, 255, 100)
        canvas.text("VICTORY! PAKISTAN BASE DESTROYED!", cwidth / 2.0 - 280, cheight / 2.0 + 110)
    } else {
        canvas.fill(255, 50, 50)
        canvas.text("PRAY FOR THE ARMY", cwidth / 2.0 - 160, cheight / 2.0 + 110)
    }

    canvas.fill(255, 215, 0)
    canvas.text("Support our brave heroes & stand strong for the nation!", cwidth / 2.0 - 240, cheight / 2.0 + 50)

    canvas.fill(255, 153, 51)
    canvas.text("JAI HIND  |  JAI BHARAT", cwidth / 2.0 - 150, cheight / 2.0 - 5)

    canvas.fill(255, 255, 255)
    canvas.text("--- THE END ---", cwidth / 2.0 - 120, cheight / 2.0 - 65)

    canvas.fill(0, 220, 100)
    canvas.text("THE END OF THE ASTRONAUT & ARMY'S LIFE", cwidth / 2.0 - 180, cheight / 2.0 - 115)
}

def viewState(canvas: CanvasDraw) {
    if (gameState == 0) { drawIntroPart2(canvas); return }
    if (gameState == 1) { drawLetterScreen(canvas); return }
    if (gameState == 2) { drawModeSelectionScreen(canvas); return }
    if (gameState == 3) {
        canvas.fill(20, 20, 30); canvas.rect(0, 0, cwidth, cheight)
        canvas.fill(40, 35, 25); canvas.rect(0, 0, cwidth, 35)
        drawCutsceneTank(canvas, cutsceneTankX, 35)
        if (!heroInside) drawArmyMan(canvas, heroX, 35)
        return
    }
    if (gameState == 5) { drawClimaxBlast(canvas); return }
    if (gameState == 6) { drawFinalBlackScreen(canvas); return }

    // GAMEPLAY SCREEN
    canvas.fill(5, 10, 20, 60); canvas.noStroke(); canvas.rect(0, 0, cwidth, cheight)

    repeatFor(blastParticles) { bp => bp.view(canvas) }
    if (gameMode == "DEFENCE") repeatFor(allyTanks) { at => at.view(canvas) }
    drawPlayerTank(canvas, playerTankX, playerTankY)
    
    repeatFor(tankShells) { s => s.view(canvas) }
    repeatFor(strategicMissiles) { sm => sm.view(canvas) }
    repeatFor(enemyTargets) { et => et.view(canvas) }
    drawHUD(canvas)
}

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