Code Sketch


BH,vesh 02
By: Mhalsakant School
Category: Art
// --- OPERATION SINDOOR: LAUNCHER ROTATION EDITION ---
cleari()
originBottomLeft()

val gravity = Vector2D(0, -0.08)
var waveAngle = 0.0
var chakraRotation = 0.0

var missileX = cwidth / 2.0 - 180.0
var missileY = 150.0
var missileAngle = math.Pi / 2.0
var selectedMissileType = 1

// --- 1. ADVANCED MISSILE 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.005
        } else if (mType == 3) {
            angle += math.sin(x * 0.05) * 0.02
        }
        
        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 > 15) 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) * 120).toInt
            canvas.fill(255, 120, 0, alpha)
            canvas.ellipse(trail(i).x, trail(i).y, 6, 6)
        }

        canvas.pushMatrix()
        canvas.translate(x, y)
        canvas.rotate(angle)

        if (mType == 1) {
            canvas.fill(210, 210, 220)
            canvas.stroke(50, 50, 60)
            canvas.strokeWeight(1)
            canvas.rect(-20, -7, 40, 14)
            canvas.fill(180, 20, 20)
            canvas.noStroke()
            canvas.ellipse(20, 0, 14, 14)
            canvas.fill(40, 40, 40)
            canvas.rect(-25, -11, 8, 22)
        } else if (mType == 2) {
            canvas.fill(40, 45, 55)
            canvas.stroke(20, 20, 20)
            canvas.rect(-25, -10, 50, 20)
            canvas.fill(220, 150, 20)
            canvas.ellipse(25, 0, 16, 16)
            canvas.fill(100, 100, 100)
            canvas.rect(-30, -14, 8, 28)
        } else {
            canvas.fill(30, 120, 180)
            canvas.stroke(10, 50, 90)
            canvas.rect(-18, -6, 36, 12)
            canvas.fill(0, 255, 255)
            canvas.noStroke()
            canvas.ellipse(18, 0, 12, 12)
            canvas.fill(255, 255, 0)
            canvas.rect(-12, -3, 24, 6)
        }

        canvas.fill(255, 200, 0, 230)
        canvas.noStroke()
        canvas.ellipse(-26, 0, 14, 10)
        canvas.fill(255, 69, 0, 180)
        canvas.ellipse(-32, 0, 18, 6)

        canvas.popMatrix()
    }
}
val strategicMissiles = ArrayBuffer.empty[StrategicMissile]

// --- 2. HEAVY REALISTIC TANK & SHELLS ---
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, 140, 0)
        canvas.noStroke()
        canvas.ellipse(x, y, 10, 10)
        canvas.fill(255, 69, 0, 180)
        canvas.ellipse(x - vx * 2, y - vy * 2, 7, 7)
    }
}
val tankShells = ArrayBuffer.empty[TankShell]

// --- 3. ENEMY THREAT MISSILES ---
class EnemyMissile(var x: Double, var y: Double, var vy: Double) {
    var active = true
    var exploded = false
    var explosionTimer = 0

    def step() {
        if (!exploded) {
            y += vy
            if (y < 0) active = false
        } else {
            explosionTimer += 1
            if (explosionTimer > 30) active = false
        }
    }

    def view(canvas: CanvasDraw) {
        if (!exploded) {
            canvas.pushMatrix()
            canvas.translate(x, y)
            canvas.rotate(-math.Pi / 2)
            
            canvas.fill(160, 30, 30)
            canvas.stroke(60, 10, 10)
            canvas.strokeWeight(1)
            canvas.rect(-16, -6, 32, 12)
            canvas.fill(40, 40, 40)
            canvas.noStroke()
            canvas.ellipse(16, 0, 10, 10)
            canvas.popMatrix()
        } else {
            canvas.noStroke()
            canvas.fill(255, 80, 0, 180)
            canvas.ellipse(x, y, 60 + explosionTimer * 2, 60 + explosionTimer * 2)
            canvas.fill(255, 255, 0, 220)
            canvas.ellipse(x, y, 30, 30)
        }
    }
}
val enemyMissiles = ArrayBuffer.empty[EnemyMissile]
var enemySpawnTimer = 0

// --- 4. TRICOLOR CELEBRATORY 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(6, 12)) else randomExplosionVector
    private var acceleration = Vector2D(0, 0)
    var lifespan = 255.0
    private var exploded = false

    def randomExplosionVector: Vector2D = {
        val rv1 = Vector2D(randomNormalDouble, randomNormalDouble)
        rv1 * randomDouble(2, 5)
    }

    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 -= 4
            velocity = velocity * 0.94
        }
        acceleration = Vector2D(0, 0)
        checkExplode()
    }

    def view(canvas: CanvasDraw) {
        canvas.stroke(col)
        if (seed) canvas.strokeWeight(5) else canvas.strokeWeight(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(90) {
                    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 updateState() {
    fireworks.filterInPlace(f => !f.done)
    if (randomDouble(1) < 0.08) fireworks.append(new Firework())
    repeatFor(fireworks) { f => f.step() }

    waveAngle += 0.08
    chakraRotation += 0.05

    // Tank Controls
    if (mouseX >= 20 && mouseX <= 75 && mouseY >= 90 && mouseY <= 130) tankX -= 3.0
    else if (mouseX >= 80 && mouseX <= 135 && mouseY >= 90 && mouseY <= 130) tankX += 3.0
    else if (mouseX >= 140 && mouseX <= 195 && mouseY >= 90 && mouseY <= 130) cannonAngle -= 0.05
    else if (mouseX >= 200 && mouseX <= 255 && mouseY >= 90 && mouseY <= 130) cannonAngle += 0.05
    else if (mouseX >= 260 && mouseX <= 315 && mouseY >= 90 && mouseY <= 130) {
        val tbx = tankX; val tby = tankY + 36.0; val bl = 60.0
        val tx = tbx + math.cos(cannonAngle) * bl
        val ty = tby + math.sin(cannonAngle) * bl
        tankShells.append(new TankShell(tx, ty, math.cos(cannonAngle) * 8.0, math.sin(cannonAngle) * 8.0))
        muzzleFlashTimer = 8
    }
    // Missile Launcher Rotation Controls (<-- M and M -->)
    else if (mouseX >= 325 && mouseX <= 375 && mouseY >= 90 && mouseY <= 130) missileAngle -= 0.05
    else if (mouseX >= 380 && mouseX <= 430 && mouseY >= 90 && mouseY <= 130) missileAngle += 0.05
    
    // Missile Type Selection (BRM, AGN, SUD)
    else if (mouseX >= 435 && mouseX <= 485 && mouseY >= 90 && mouseY <= 130) selectedMissileType = 1
    else if (mouseX >= 490 && mouseX <= 540 && mouseY >= 90 && mouseY <= 130) selectedMissileType = 2
    else if (mouseX >= 545 && mouseX <= 595 && mouseY >= 90 && mouseY <= 130) selectedMissileType = 3
    
    // Launch Strategic Missile
    else if (mouseX >= 600 && mouseX <= 675 && mouseY >= 90 && mouseY <= 130) {
        val mTipX = missileX + math.cos(missileAngle) * 50.0
        val mTipY = missileY + math.sin(missileAngle) * 50.0
        strategicMissiles.append(new StrategicMissile(mTipX, mTipY, math.cos(missileAngle) * 6.0, math.sin(missileAngle) * 6.0, missileAngle, selectedMissileType))
    }

    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 >= 100) {
        enemySpawnTimer = 0
        val ex = random(120, cwidth - 120)
        enemyMissiles.append(new EnemyMissile(ex, cheight + 20.0, -0.7))
    }

    tankShells.filterInPlace(_.active)
    repeatFor(tankShells) { s => s.step() }

    strategicMissiles.filterInPlace(_.active)
    repeatFor(strategicMissiles) { sm => sm.step() }

    enemyMissiles.filterInPlace(_.active)
    repeatFor(enemyMissiles) { em => em.step() }

    repeatFor(enemyMissiles) { em =>
        if (!em.exploded) {
            repeatFor(tankShells) { ts =>
                if (ts.active && math.abs(ts.x - em.x) < 25 && math.abs(ts.y - em.y) < 25) {
                    em.exploded = true; ts.active = false
                }
            }
            repeatFor(strategicMissiles) { sm =>
                if (sm.active && math.abs(sm.x - em.x) < 35 && math.abs(sm.y - em.y) < 35) {
                    em.exploded = true; sm.active = false
                }
            }
        }
    }
}

def drawLauncherPad(canvas: CanvasDraw, mx: Double, my: Double) {
    canvas.fill(50, 55, 65)
    canvas.stroke(20, 25, 30)
    canvas.strokeWeight(2)
    canvas.ellipse(mx, my, 55, 55)
    
    canvas.pushMatrix()
    canvas.translate(mx, my)
    canvas.rotate(missileAngle)
    
    canvas.fill(70, 80, 75)
    canvas.stroke(30, 40, 35)
    canvas.strokeWeight(2)
    canvas.rect(-14, -16, 60, 32)
    
    if (selectedMissileType == 1) canvas.fill(200, 30, 30)
    else if (selectedMissileType == 2) canvas.fill(220, 140, 0)
    else canvas.fill(0, 200, 255)
    
    canvas.rect(15, -16, 8, 32)
    canvas.popMatrix()
}

def drawRealisticArmyTank(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(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, 140, 0, 230)
        canvas.ellipse(tipX, tipY, 35, 35)
        canvas.fill(255, 255, 0, 250)
        canvas.ellipse(tipX, tipY, 20, 20)
        canvas.fill(255, 255, 255)
        canvas.ellipse(tipX, tipY, 10, 10)
    }
}

def drawControlButtons(canvas: CanvasDraw) {
    canvas.fill(20, 20, 35, 210)
    canvas.stroke(180, 180, 200)
    canvas.strokeWeight(1)
    canvas.rect(15, 85, 665, 50)

    // Tank Controls
    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)
    
    // Cannon Angle Controls
    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)
    
    // Tank Fire
    canvas.fill(200, 70, 0); canvas.rect(240, 95, 55, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("FIRE", 252, 105)

    // Launcher Rotate Controls (<-- M and M -->)
    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)

    // Missile Type Selection (BRM, AGN, SUD)
    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)

    // Launch Strategic Missile
    canvas.fill(140, 0, 180); canvas.rect(555, 95, 75, 30); canvas.fill(cm.rgb(255, 255, 255)); canvas.text("LAUNCH", 568, 105)
}

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)
    canvas.strokeWeight(1)
    for (i <- 0 until 24) {
        val angle = chakraRotation + (i * (2 * math.Pi / 24))
        canvas.line(chakraX, chakraY, chakraX + math.cos(angle) * radius, chakraY + math.sin(angle) * radius)
    }
}

def viewState(canvas: CanvasDraw) {
    canvas.fill(10, 10, 25, 45)
    canvas.noStroke()
    canvas.rect(0, 0, cwidth, cheight)

    repeatFor(fireworks) { f => f.view(canvas) }

    drawLauncherPad(canvas, missileX, missileY)
    drawRealisticArmyTank(canvas, tankX, tankY)
    drawControlButtons(canvas)

    repeatFor(tankShells) { s => s.view(canvas) }
    repeatFor(strategicMissiles) { sm => sm.view(canvas) }
    repeatFor(enemyMissiles) { em => em.view(canvas) }

    drawWavingTricolorFlag(canvas)
}

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