Code Sketch


dolly 24
By: Mhalsakant School
Category: Art

cleari()
originBottomLeft()

var shipX = 400.0
var shipY = 90.0
var score = 0
var lives = 3
var level = 1
var tick = 0
var gameOver = false
var bossActive = false
var bossX = 400.0
var bossHP = 10
var bossMaxHP = 10
var bossDir = 1.0
var nextBossScore = 50

val meteorX = Array(100.0, 250.0, 400.0, 550.0, 700.0)
val meteorY = Array(500.0, 650.0, 800.0, 950.0, 1100.0)

val bulletX = Array.fill(20)(0.0)
val bulletY = Array.fill(20)(0.0)
val bulletOn = Array.fill(20)(false)

val starX = Array(180.0, 350.0, 520.0, 680.0)
val starY = Array(400.0, 600.0, 800.0, 1000.0)
val starTaken = Array.fill(4)(false)

def resetGame() {
    shipX = 400.0
    score = 0
    lives = 3
    level = 1
    tick = 0
    gameOver = false
    bossActive = false
    bossX = 400.0
    bossHP = 10
    bossMaxHP = 10
    bossDir = 1.0
    nextBossScore = 50

    for (i <- meteorY.indices) {
        meteorY(i) = 500.0 + i * 150.0
        meteorX(i) = 100.0 + i * 140.0
    }

    for (i <- bulletOn.indices) {
        bulletOn(i) = false
    }

    for (i <- starTaken.indices) {
        starTaken(i) = false
        starY(i) = 400.0 + i * 200.0
    }
}

def fireBullet() {
    for (i <- bulletOn.indices) {
        if (!bulletOn(i)) {
            bulletOn(i) = true
            bulletX(i) = shipX
            bulletY(i) = shipY + 20
            return
        }
    }
}

onKeyPress { key =>
    if (key == Kc.VK_R) {
        resetGame()
    }

    if (!gameOver) {
        if (key == Kc.VK_LEFT) {
            shipX = math.max(25.0, shipX - 25.0)
        }

        if (key == Kc.VK_RIGHT) {
            shipX = math.min(cwidth - 25.0, shipX + 25.0)
        }

        if (key == Kc.VK_SPACE) {
            fireBullet()
        }
    }
}

def drawBackground(c: CanvasDraw) {
    c.fill(3, 4, 25)
    c.noStroke()
    c.rect(0, 0, cwidth, cheight)

    for (i <- 0 until 70) {
        val x = (i * 137 + tick * (i % 3 + 1)) % cwidth.toInt
        val y = (i * 197) % cheight.toInt
        c.fill(120, 180, 255)
        c.ellipse(x.toDouble, y.toDouble, 2, 2)
    }

    c.fill(20, 25, 65)
    c.rect(0, 0, cwidth, 5)
}

def drawShip(c: CanvasDraw) {
    c.fill(0, 210, 255)
    c.stroke(255, 255, 255)
    c.strokeWeight(2)
    c.rect(shipX - 18, shipY - 12, 36, 28)

    c.fill(30, 90, 255)
    c.noStroke()
    c.ellipse(shipX, shipY + 17, 22, 24)

    c.fill(255, 100, 10)
    c.ellipse(shipX, shipY - 20, 12, 18)

    c.fill(255, 255, 100)
    c.ellipse(shipX, shipY + 5, 10, 10)
}

def drawMeteors(c: CanvasDraw) {
    for (i <- meteorX.indices) {
        c.fill(190, 65, 45)
        c.stroke(255, 150, 60)
        c.strokeWeight(2)
        c.ellipse(meteorX(i), meteorY(i), 30, 30)
    }
}

def drawBullets(c: CanvasDraw) {
    for (i <- bulletOn.indices) {
        if (bulletOn(i)) {
            c.fill(50, 255, 100)
            c.noStroke()
            c.rect(bulletX(i) - 3, bulletY(i), 6, 18)
        }
    }
}

def drawStars(c: CanvasDraw) {
    for (i <- starX.indices) {
        if (!starTaken(i)) {
            c.fill(255, 235, 0)
            c.noStroke()
            c.ellipse(starX(i), starY(i), 18, 18)
        }
    }
}

def drawBoss(c: CanvasDraw) {
    if (bossActive) {
        c.fill(180, 20, 90)
        c.stroke(255, 80, 150)
        c.strokeWeight(3)
        c.rect(bossX - 55, cheight - 150, 110, 55)

        c.fill(255, 30, 50)
        c.noStroke()
        c.ellipse(bossX - 28, cheight - 122, 15, 15)
        c.ellipse(bossX + 28, cheight - 122, 15, 15)

        c.fill(255, 255, 255)
        c.text(
            s"BOSS HP: $bossHP",
            bossX - 40,
            cheight - 80
        )
    }
}

def updateGame() {
    if (!gameOver) {
        tick += 1

        val meteorSpeed = 4.0 + level * 0.7
        val starSpeed = 3.0 + level * 0.2

        for (i <- meteorY.indices) {
            meteorY(i) -= meteorSpeed

            if (meteorY(i) < 0) {
                meteorY(i) = cheight + 30
                meteorX(i) = randomDouble(30, cwidth - 30)
            }

            if (
                math.abs(meteorX(i) - shipX) < 30 &&
                math.abs(meteorY(i) - shipY) < 28
            ) {
                lives -= 1
                meteorY(i) = cheight + 100
                meteorX(i) = randomDouble(30, cwidth - 30)

                if (lives <= 0) {
                    gameOver = true
                }
            }
        }

        for (i <- bulletOn.indices) {
            if (bulletOn(i)) {
                bulletY(i) += 12

                if (bulletY(i) > cheight) {
                    bulletOn(i) = false
                }

                for (j <- meteorX.indices) {
                    if (
                        bulletOn(i) &&
                        math.abs(bulletX(i) - meteorX(j)) < 25 &&
                        math.abs(bulletY(i) - meteorY(j)) < 25
                    ) {
                        bulletOn(i) = false
                        meteorY(j) = cheight + 50
                        meteorX(j) = randomDouble(30, cwidth - 30)
                        score += 5
                    }
                }

                if (
                    bulletOn(i) &&
                    bossActive &&
                    math.abs(bulletX(i) - bossX) < 60 &&
                    math.abs(bulletY(i) - (cheight - 120)) < 40
                ) {
                    bulletOn(i) = false
                    bossHP -= 1

                    if (bossHP <= 0) {
                        bossActive = false
                        score += 50
                        level += 1
                        nextBossScore += 50
                        bossHP = 10 + level * 3
                        bossMaxHP = bossHP
                    }
                }
            }
        }

        for (i <- starY.indices) {
            starY(i) -= starSpeed

            if (starY(i) < 0) {
                starY(i) = cheight + 80
                starX(i) = randomDouble(30, cwidth - 30)
                starTaken(i) = false
            }

            if (
                !starTaken(i) &&
                math.abs(starX(i) - shipX) < 25 &&
                math.abs(starY(i) - shipY) < 25
            ) {
                starTaken(i) = true
                score += 10
            }
        }

        if (score >= nextBossScore && !bossActive) {
            bossActive = true
        }

        if (bossActive) {
            bossX += bossDir * (3.0 + level * 0.3)

            if (bossX > cwidth - 65) {
                bossDir = -1.0
            }

            if (bossX < 65) {
                bossDir = 1.0
            }
        }

        if (tick % 100 == 0) {
            score += 5
        }
    }
}

def drawScore(c: CanvasDraw) {
    c.fill(255, 255, 255)
    c.noStroke()
    c.text(s"SCORE: $score", 20, cheight - 30)
    c.text(s"LIVES: $lives", 20, cheight - 55)
    c.text(s"LEVEL: $level", 20, cheight - 80)
    c.text("SPACE SHOOTER", cwidth / 2 - 60, cheight - 30)
}

def drawGameOver(c: CanvasDraw) {
    c.fill(5, 5, 30)
    c.noStroke()
    c.rect(0, 0, cwidth, cheight)

    c.fill(255, 50, 70)
    c.text("GAME OVER!", cwidth / 2 - 45, cheight / 2 + 35)

    c.fill(255, 255, 255)
    c.text(s"FINAL SCORE: $score", cwidth / 2 - 55, cheight / 2)
    c.text(s"LEVEL REACHED: $level", cwidth / 2 - 60, cheight / 2 - 30)
    c.text("Press R to Restart", cwidth / 2 - 60, cheight / 2 - 65)
}

animateWithCanvasDraw { c =>
    updateGame()

    if (!gameOver) {
        drawBackground(c)
        drawStars(c)
        drawMeteors(c)
        drawBullets(c)
        drawShip(c)
        drawBoss(c)
        drawScore(c)
    } else {
        drawGameOver(c)
    }
}