Code Sketch


Bixuu02
By: Mhalsakant School
Category: Programming
// ============================================================
// KEYBOARD CONTROLLED CAR DRIVING MISSION GAME (FIXED)
// ============================================================

cleari()
originBottomLeft()

// ============================================================
// GAME VARIABLES & STATES
// ============================================================
var screen = 0 // 0: Letter, 1: Playing, 2: Game Over, 3: Victory
var playerX = 80.0
var playerY = 180.0
var playerSpeed = 6.0

// KEYBOARD STATE FLAGS
var isW = false
var isS = false
var isA = false
var isD = false

var gameTimer = 20
var frameCounter = 0

var finishX = 1200.0
var showFinishLine = false
var roadOffset = 0.0

// Obstacle Cars Class
class ObstacleCar(var x: Double, var y: Double, var speed: Double, val r: Int, val g: Int, val b: Int) {
    def update() {
        x -= speed
        if (x < -100) {
            x = cwidth + randomDouble(200)
            y = 90 + randomDouble(170)
            speed = 4.0 + randomDouble(4.0)
        }
    }

    def draw(canvas: CanvasDraw) {
        canvas.noStroke()
        canvas.fill(40, 40, 40, 100)
        canvas.ellipse(x + 35, y - 2, 80, 12)
        canvas.fill(r, g, b)
        canvas.rect(x, y + 5, 70, 25)
        canvas.rect(x + 15, y + 25, 40, 18)
        canvas.fill(200, 230, 255)
        canvas.rect(x + 20, y + 27, 30, 14)
        canvas.fill(20, 20, 20)
        canvas.ellipse(x + 15, y + 4, 16, 16)
        canvas.ellipse(x + 55, y + 4, 16, 16)
    }
}

val obstacles = ArrayBuffer.empty[ObstacleCar]

def resetGame() {
    playerX = 80.0
    playerY = 180.0
    gameTimer = 20
    frameCounter = 0
    showFinishLine = false
    finishX = cwidth + 300.0

    isW = false
    isS = false
    isA = false
    isD = false

    obstacles.clear()
    val colors = Array((220, 30, 30), (255, 140, 0), (156, 39, 176), (0, 150, 136))
    for (i <- 0 until 4) {
        val (r, g, b) = colors(i % 4)
        obstacles.append(new ObstacleCar(cwidth + i * 220 + randomDouble(100), 90 + (i * 45), 4.0 + randomDouble(3.0), r, g, b))
    }
}

// ============================================================
// DRAW PLAYER CAR
// ============================================================
def drawPlayerCar(canvas: CanvasDraw, x: Double, y: Double) {
    canvas.noStroke()
    canvas.fill(30, 30, 30, 120)
    canvas.ellipse(x + 40, y - 2, 90, 14)

    // Blue Sports Car
    canvas.fill(30, 144, 255)
    canvas.rect(x, y + 5, 80, 28)
    canvas.fill(0, 102, 204)
    canvas.rect(x + 18, y + 28, 44, 20)

    canvas.fill(220, 245, 255)
    canvas.rect(x + 22, y + 30, 36, 15)

    canvas.fill(255, 255, 150)
    canvas.ellipse(x + 78, y + 12, 8, 10)

    canvas.fill(20, 20, 20)
    canvas.ellipse(x + 18, y + 4, 18, 18)
    canvas.ellipse(x + 62, y + 4, 18, 18)
    canvas.fill(220, 220, 220)
    canvas.ellipse(x + 18, y + 4, 8, 8)
    canvas.ellipse(x + 62, y + 4, 8, 8)
}

def drawCheckeredFinishLine(canvas: CanvasDraw, x: Double) {
    val tileSize = 15
    val rows = 14
    val cols = 3

    for (r <- 0 until rows) {
        for (c <- 0 until cols) {
            if ((r + c) % 2 == 0) canvas.fill(0, 0, 0)
            else canvas.fill(255, 255, 255)
            canvas.noStroke()
            canvas.rect(x + (c * tileSize), 75 + (r * tileSize), tileSize, tileSize)
        }
    }
}

// ============================================================
// UPDATE MOVEMENT
// ============================================================
def updatePlayerMovement() {
    if (screen == 1) {
        if (isW && playerY < 240) playerY += playerSpeed
        if (isS && playerY > 85) playerY -= playerSpeed
        if (isA && playerX > 20) playerX -= playerSpeed
        if (isD && playerX < cwidth - 100) playerX += playerSpeed
    }
}

// ============================================================
// SCREENS
// ============================================================
def drawLetterScreen(canvas: CanvasDraw) {
    canvas.background(20, 30, 50)

    canvas.fill(255, 248, 220)
    canvas.stroke(212, 175, 55)
    canvas.strokeWeight(4)
    canvas.rect(cwidth/2 - 240, cheight/2 - 170, 480, 350)

    canvas.fill(200, 30, 30)
    canvas.text("CAR DRIVING MISSION!", cwidth/2 - 120, cheight/2 + 130)

    canvas.fill(40, 40, 40)
    canvas.text("Dear Driver,", cwidth/2 - 210, cheight/2 + 90)
    canvas.text("Control your car using Keyboard WASD or Arrow Keys:", cwidth/2 - 210, cheight/2 + 60)
    canvas.text("  [W / UP]    : Move Up", cwidth/2 - 190, cheight/2 + 35)
    canvas.text("  [S / DOWN]  : Move Down", cwidth/2 - 190, cheight/2 + 15)
    canvas.text("  [A / LEFT]  : Move Left", cwidth/2 - 190, cheight/2 - 5)
    canvas.text("  [D / RIGHT] : Move Right", cwidth/2 - 190, cheight/2 - 25)

    // Start Button
    canvas.fill(220, 50, 50)
    canvas.noStroke()
    canvas.rect(cwidth/2 - 120, cheight/2 - 120, 240, 50)
    canvas.fill(255, 255, 255)
    canvas.text("START CAR MISSION", cwidth/2 - 92, cheight/2 - 90)
}

def drawGameplayScreen(canvas: CanvasDraw) {
    canvas.background(135, 206, 235)

    canvas.noStroke()
    canvas.fill(34, 139, 34)
    canvas.rect(0, 0, cwidth, cheight)

    canvas.fill(60, 60, 65)
    canvas.rect(0, 75, cwidth, 210)

    canvas.fill(255, 255, 255)
    canvas.rect(0, 75, cwidth, 6)
    canvas.rect(0, 279, cwidth, 6)

    roadOffset = (roadOffset - 6.0) % 40.0
    canvas.fill(255, 215, 0)
    var lineX = roadOffset
    while (lineX < cwidth + 40) {
        canvas.rect(lineX, 177, 25, 6)
        lineX += 45
    }

    if (showFinishLine) {
        drawCheckeredFinishLine(canvas, finishX)
        finishX -= 4.0
    }

    drawPlayerCar(canvas, playerX, playerY)

    repeatFor(obstacles) { obs =>
        obs.update()
        obs.draw(canvas)

        if (math.abs(playerX - obs.x) < 60 && math.abs(playerY - obs.y) < 25) {
            screen = 2
        }
    }

    // Dashboard Timer
    canvas.fill(0, 0, 0, 180)
    canvas.rect(20, cheight - 60, 220, 45)
    canvas.fill(255, 215, 0)
    canvas.text(s"TIME LEFT: $gameTimer SECONDS", 35, cheight - 32)

    if (showFinishLine && playerX >= finishX) {
        screen = 3
    }
}

def drawGameOverScreen(canvas: CanvasDraw) {
    canvas.background(40, 10, 10)
    canvas.fill(255, 50, 50)
    canvas.text("MISSION FAILED - CAR CRASHED!", cwidth/2 - 160, cheight/2 + 40)
    canvas.fill(255, 255, 255)
    canvas.text("Be careful! Avoid oncoming traffic.", cwidth/2 - 130, cheight/2)

    canvas.fill(0, 150, 200)
    canvas.noStroke()
    canvas.rect(cwidth/2 - 90, cheight/2 - 70, 180, 45)
    canvas.fill(255, 255, 255)
    canvas.text("RETRY MISSION", cwidth/2 - 62, cheight/2 - 42)
}

def drawVictoryScreen(canvas: CanvasDraw) {
    canvas.background(20, 60, 30)
    drawCheckeredFinishLine(canvas, cwidth/2 - 25)

    canvas.fill(255, 215, 0)
    canvas.text("YOU WIN! MISSION COMPLETED!", cwidth/2 - 150, cheight/2 + 60)

    canvas.fill(255, 255, 255)
    canvas.text("Congratulations! You safely navigated through traffic", cwidth/2 - 180, cheight/2 + 15)
    canvas.text("and crossed the Checkered Finish Line in 20 seconds!", cwidth/2 - 185, cheight/2 - 15)

    canvas.fill(0, 180, 80)
    canvas.noStroke()
    canvas.rect(cwidth/2 - 100, cheight/2 - 90, 200, 45)
    canvas.fill(255, 255, 255)
    canvas.text("PLAY AGAIN", cwidth/2 - 50, cheight/2 - 62)
}

// ============================================================
// CORRECTED KEY LISTENERS USING INT KEY CODES
// ============================================================
onKeyPress { key =>
    if (key == Kc.VK_W || key == Kc.VK_UP || key == 87 || key == 38) isW = true
    if (key == Kc.VK_S || key == Kc.VK_DOWN || key == 83 || key == 40) isS = true
    if (key == Kc.VK_A || key == Kc.VK_LEFT || key == 65 || key == 37) isA = true
    if (key == Kc.VK_D || key == Kc.VK_RIGHT || key == 68 || key == 39) isD = true
}

onKeyRelease { key =>
    if (key == Kc.VK_W || key == Kc.VK_UP || key == 87 || key == 38) isW = false
    if (key == Kc.VK_S || key == Kc.VK_DOWN || key == 83 || key == 40) isS = false
    if (key == Kc.VK_A || key == Kc.VK_LEFT || key == 65 || key == 37) isA = false
    if (key == Kc.VK_D || key == Kc.VK_RIGHT || key == 68 || key == 39) isD = false
}

// ============================================================
// MOUSE CLICK HANDLER FOR BUTTONS
// ============================================================
onMouseClick { (x, y) =>
    if (screen == 0) {
        if (x >= cwidth/2 - 120 && x <= cwidth/2 + 120 && y >= cheight/2 - 120 && y <= cheight/2 - 70) {
            resetGame()
            screen = 1
        }
    } else if (screen == 2) {
        if (x >= cwidth/2 - 90 && x <= cwidth/2 + 90 && y >= cheight/2 - 70 && y <= cheight/2 - 25) {
            resetGame()
            screen = 1
        }
    } else if (screen == 3) {
        if (x >= cwidth/2 - 100 && x <= cwidth/2 + 100 && y >= cheight/2 - 90 && y <= cheight/2 - 45) {
            resetGame()
            screen = 1
        }
    }
}

// ============================================================
// MAIN GAME LOOP
// ============================================================
animateWithCanvasDraw { canvas =>
    if (screen == 0) {
        drawLetterScreen(canvas)
    } else if (screen == 1) {
        updatePlayerMovement()

        frameCounter += 1
        if (frameCounter >= 60) {
            frameCounter = 0
            if (gameTimer > 0) {
                gameTimer -= 1
            } else {
                showFinishLine = true
            }
        }
        drawGameplayScreen(canvas)
    } else if (screen == 2) {
        drawGameOverScreen(canvas)
    } else if (screen == 3) {
        drawVictoryScreen(canvas)
    }
}