Code Sketch


rider 2311 by shlok thakur
By: Mhalsakant School
Category: Programming
// ============================================================
// RIDER 2311
// Beginner Bike Game for Learning Scala in Kojo
// AI GUIDE: ALEX
// ============================================================

cleari()
originBottomLeft()

// ------------------------------------------------------------
// GAME SCREENS
// ------------------------------------------------------------
// 0 = Start
// 1 = Bike selection
// 2 = Playing
// 3 = Game over
// 4 = Level complete
// 5 = Final screen

var screen = 0

// ------------------------------------------------------------
// PLAYER
// ------------------------------------------------------------

var riderName = "RIDER 2311"
var selectedBike = 1

// Bike positions
var bikeX = 300.0
var bikeY = 90.0

val bikeSpeed = 6.0

// Keyboard states
var moveLeft = false
var moveRight = false
var moveUp = false
var moveDown = false

// ------------------------------------------------------------
// GAME VARIABLES
// ------------------------------------------------------------

var level = 1
var score = 0
var lives = 3

var roadSpeed = 4.0

var distance = 0.0
val targetDistance = 1000.0

var fuel = 100.0

var gameOverReason = ""

// ------------------------------------------------------------
// OBSTACLES
// ------------------------------------------------------------

var obstacle1X = 150.0
var obstacle1Y = 650.0

var obstacle2X = 350.0
var obstacle2Y = 900.0

var obstacle3X = 520.0
var obstacle3Y = 1200.0

// ------------------------------------------------------------
// COINS
// ------------------------------------------------------------

var coin1X = 250.0
var coin1Y = 800.0

var coin2X = 450.0
var coin2Y = 1100.0

var coin3X = 330.0
var coin3Y = 1450.0

var coin1Taken = false
var coin2Taken = false
var coin3Taken = false

// ------------------------------------------------------------
// TIMER
// ------------------------------------------------------------

var frameCounter = 0

// ------------------------------------------------------------
// ALEX AI MESSAGE
// ------------------------------------------------------------

var alexMessage = "Hello! I am Alex."

// This function displays Alex's message.
// It is defined here, so Kojo will know what alex() means.
def alex(message: String) {
    alexMessage = message
}

// ------------------------------------------------------------
// RESET GAME
// ------------------------------------------------------------

def resetGame() {

    screen = 1

    level = 1
    score = 0
    lives = 3

    bikeX = cwidth / 2.0
    bikeY = 90.0

    distance = 0.0
    fuel = 100.0

    obstacle1Y = 650.0
    obstacle2Y = 900.0
    obstacle3Y = 1200.0

    coin1Y = 800.0
    coin2Y = 1100.0
    coin3Y = 1450.0

    coin1Taken = false
    coin2Taken = false
    coin3Taken = false

    gameOverReason = ""

    alex("Welcome to RIDER 2311! I am Alex. Let's ride!")
}

// ------------------------------------------------------------
// START LEVEL
// ------------------------------------------------------------

def startLevel(newLevel: Int) {

    level = newLevel

    bikeX = cwidth / 2.0
    bikeY = 90.0

    distance = 0.0
    fuel = 100.0

    frameCounter = 0

    obstacle1Y = cheight + 150.0
    obstacle2Y = cheight + 450.0
    obstacle3Y = cheight + 750.0

    coin1Y = cheight + 300.0
    coin2Y = cheight + 600.0
    coin3Y = cheight + 900.0

    coin1Taken = false
    coin2Taken = false
    coin3Taken = false

    if (level == 1) {
        roadSpeed = 4.0
        alex("Level 1! Use LEFT and RIGHT to ride.")
    }
    else if (level == 2) {
        roadSpeed = 5.0
        alex("Level 2! The road is faster. Stay focused!")
    }
    else {
        roadSpeed = 6.0
        alex("Level 3! Final race! You can do it!")
    }

    screen = 2
}

// ------------------------------------------------------------
// COLLISION FUNCTION
// ------------------------------------------------------------

def collision(
    x1: Double,
    y1: Double,
    r1: Double,
    x2: Double,
    y2: Double,
    r2: Double
): Boolean = {

    val dx = x1 - x2
    val dy = y1 - y2

    val distanceBetween =
        math.sqrt(dx * dx + dy * dy)

    distanceBetween < r1 + r2
}

// ------------------------------------------------------------
// DRAW BIKE
// ------------------------------------------------------------

def drawBike(
    canvas: CanvasDraw,
    x: Double,
    y: Double,
    bikeType: Int
) {

    canvas.noStroke()

    // Wheels
    canvas.fill(20, 20, 20)

    canvas.ellipse(
        x - 18,
        y - 20,
        25,
        25
    )

    canvas.ellipse(
        x + 18,
        y - 20,
        25,
        25
    )

    // Bike body
    if (bikeType == 1) {
        canvas.fill(255, 50, 30)
    }
    else if (bikeType == 2) {
        canvas.fill(30, 150, 255)
    }
    else {
        canvas.fill(80, 255, 100)
    }

    canvas.rect(
        x - 20,
        y - 8,
        40,
        15
    )

    // Seat
    canvas.fill(30, 30, 30)

    canvas.rect(
        x - 8,
        y + 10,
        20,
        5
    )

    // Rider
    canvas.fill(255, 200, 150)

    canvas.ellipse(
        x,
        y + 25,
        18,
        18
    )

    canvas.fill(40, 40, 40)

    canvas.rect(
        x - 10,
        y + 8,
        20,
        18
    )
}

// ------------------------------------------------------------
// DRAW OBSTACLE
// ------------------------------------------------------------

def drawObstacle(
    canvas: CanvasDraw,
    x: Double,
    y: Double
) {

    canvas.fill(220, 40, 40)
    canvas.noStroke()

    canvas.rect(
        x - 22,
        y - 18,
        44,
        36
    )

    canvas.fill(255, 230, 0)

    canvas.rect(
        x - 15,
        y - 5,
        30,
        5
    )
}

// ------------------------------------------------------------
// DRAW COIN
// ------------------------------------------------------------

def drawCoin(
    canvas: CanvasDraw,
    x: Double,
    y: Double,
    taken: Boolean
) {

    if (!taken) {

        canvas.fill(255, 215, 0)
        canvas.noStroke()

        canvas.ellipse(
            x,
            y,
            20,
            20
        )

        canvas.fill(100, 70, 0)

        canvas.text(
            "$",
            x - 4,
            y - 5
        )
    }
}

// ------------------------------------------------------------
// START SCREEN
// ------------------------------------------------------------

def drawStartScreen(canvas: CanvasDraw) {

    canvas.background(5, 10, 25)

    canvas.fill(0, 180, 255)

    canvas.text(
        "RIDER 2311",
        cwidth / 2.0 - 70,
        cheight - 100
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        "A BEGINNER SCALA BIKE GAME",
        cwidth / 2.0 - 120,
        cheight - 140
    )

    canvas.fill(0, 200, 100)

    canvas.rect(
        cwidth / 2.0 - 100,
        cheight / 2.0 - 25,
        200,
        55
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        "START GAME",
        cwidth / 2.0 - 55,
        cheight / 2.0 + 8
    )

    canvas.fill(255, 220, 0)

    canvas.text(
        "AI GUIDE: ALEX",
        cwidth / 2.0 - 65,
        80
    )

    canvas.fill(220, 220, 220)

    canvas.text(
        "Learn variables, if statements, functions and movement!",
        70,
        45
    )
}

// ------------------------------------------------------------
// BIKE SELECTION
// ------------------------------------------------------------

def drawBikeSelection(canvas: CanvasDraw) {

    canvas.background(10, 10, 25)

    canvas.fill(255, 255, 255)

    canvas.text(
        "CHOOSE YOUR BIKE",
        cwidth / 2.0 - 75,
        cheight - 70
    )

    // Bike 1
    canvas.fill(50, 30, 30)
    canvas.rect(70, 220, 150, 190)

    drawBike(
        canvas,
        145,
        300,
        1
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        "RED BIKE",
        115,
        245
    )

    // Bike 2
    canvas.fill(20, 40, 70)
    canvas.rect(270, 220, 150, 190)

    drawBike(
        canvas,
        345,
        300,
        2
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        "BLUE BIKE",
        310,
        245
    )

    // Bike 3
    canvas.fill(20, 60, 30)
    canvas.rect(470, 220, 150, 190)

    drawBike(
        canvas,
        545,
        300,
        3
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        "GREEN BIKE",
        500,
        245
    )

    canvas.fill(255, 215, 0)

    canvas.text(
        "CLICK A BIKE",
        cwidth / 2.0 - 50,
        100
    )

    canvas.fill(180, 220, 255)

    canvas.text(
        alexMessage,
        80,
        55
    )
}

// ------------------------------------------------------------
// ROAD
// ------------------------------------------------------------

def drawRoad(canvas: CanvasDraw) {

    canvas.background(30, 120, 50)

    // Road
    canvas.fill(60, 60, 60)

    canvas.rect(
        70,
        0,
        cwidth - 140,
        cheight
    )

    // Road edges
    canvas.fill(255, 255, 255)

    canvas.rect(
        65,
        0,
        5,
        cheight
    )

    canvas.rect(
        cwidth - 70,
        0,
        5,
        cheight
    )

    // Road center lines
    canvas.fill(255, 230, 0)

    var lineY = 0.0

    while (lineY < cheight) {

        canvas.rect(
            cwidth / 2.0 - 4,
            lineY,
            8,
            60
        )

        lineY += 120
    }
}

// ------------------------------------------------------------
// GAMEPLAY SCREEN
// ------------------------------------------------------------

def drawGameScreen(canvas: CanvasDraw) {

    drawRoad(canvas)

    // Move obstacles downward
    obstacle1Y -= roadSpeed
    obstacle2Y -= roadSpeed
    obstacle3Y -= roadSpeed

    coin1Y -= roadSpeed
    coin2Y -= roadSpeed
    coin3Y -= roadSpeed

    // Reset obstacles
    if (obstacle1Y < -50) {
        obstacle1Y = cheight + 150
        obstacle1X =
            100 + randomDouble(cwidth - 200)
    }

    if (obstacle2Y < -50) {
        obstacle2Y = cheight + 400
        obstacle2X =
            100 + randomDouble(cwidth - 200)
    }

    if (obstacle3Y < -50) {
        obstacle3Y = cheight + 700
        obstacle3X =
            100 + randomDouble(cwidth - 200)
    }

    // Reset coins
    if (coin1Y < -30) {
        coin1Y = cheight + 250
        coin1Taken = false
    }

    if (coin2Y < -30) {
        coin2Y = cheight + 500
        coin2Taken = false
    }

    if (coin3Y < -30) {
        coin3Y = cheight + 750
        coin3Taken = false
    }

    // Draw obstacles
    drawObstacle(
        canvas,
        obstacle1X,
        obstacle1Y
    )

    drawObstacle(
        canvas,
        obstacle2X,
        obstacle2Y
    )

    drawObstacle(
        canvas,
        obstacle3X,
        obstacle3Y
    )

    // Draw coins
    drawCoin(
        canvas,
        coin1X,
        coin1Y,
        coin1Taken
    )

    drawCoin(
        canvas,
        coin2X,
        coin2Y,
        coin2Taken
    )

    drawCoin(
        canvas,
        coin3X,
        coin3Y,
        coin3Taken
    )

    // Draw bike
    drawBike(
        canvas,
        bikeX,
        bikeY,
        selectedBike
    )

    // --------------------------------------------------------
    // COLLISION WITH OBSTACLES
    // --------------------------------------------------------

    if (
        collision(
            bikeX,
            bikeY,
            18,
            obstacle1X,
            obstacle1Y,
            22
        )
    ) {

        lives -= 1

        obstacle1Y = cheight + 200

        alex("Watch out! You hit an obstacle.")

        if (lives <= 0) {

            gameOverReason =
                "You ran out of lives."

            screen = 3
        }
    }

    if (
        collision(
            bikeX,
            bikeY,
            18,
            obstacle2X,
            obstacle2Y,
            22
        )
    ) {

        lives -= 1

        obstacle2Y = cheight + 350

        alex("Be careful, Rider 2311!")

        if (lives <= 0) {

            gameOverReason =
                "You ran out of lives."

            screen = 3
        }
    }

    if (
        collision(
            bikeX,
            bikeY,
            18,
            obstacle3X,
            obstacle3Y,
            22
        )
    ) {

        lives -= 1

        obstacle3Y = cheight + 500

        alex("Keep your eyes on the road!")

        if (lives <= 0) {

            gameOverReason =
                "You ran out of lives."

            screen = 3
        }
    }

    // --------------------------------------------------------
    // COIN COLLISION
    // --------------------------------------------------------

    if (
        !coin1Taken &&
        collision(
            bikeX,
            bikeY,
            18,
            coin1X,
            coin1Y,
            10
        )
    ) {

        coin1Taken = true
        score += 10

        alex("Great! You collected a coin.")
    }

    if (
        !coin2Taken &&
        collision(
            bikeX,
            bikeY,
            18,
            coin2X,
            coin2Y,
            10
        )
    ) {

        coin2Taken = true
        score += 10

        alex("Excellent! Ten more points.")
    }

    if (
        !coin3Taken &&
        collision(
            bikeX,
            bikeY,
            18,
            coin3X,
            coin3Y,
            10
        )
    ) {

        coin3Taken = true
        score += 10

        alex("Amazing riding!")
    }

    // --------------------------------------------------------
    // DISTANCE
    // --------------------------------------------------------

    distance += roadSpeed

    // Fuel slowly decreases
    fuel -= 0.025

    if (fuel <= 0) {

        fuel = 0

        gameOverReason =
            "Your bike ran out of fuel."

        screen = 3
    }

    // Level completed
    if (distance >= targetDistance) {

        score += 50

        if (level < 3) {

            alex("Level complete! Excellent riding.")

            screen = 4
        }
        else {

            alex("Fantastic! You completed RIDER 2311!")

            screen = 5
        }
    }

    // --------------------------------------------------------
    // UI
    // --------------------------------------------------------

    canvas.fill(0, 0, 0, 190)

    canvas.rect(
        0,
        cheight - 80,
        cwidth,
        80
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        s"RIDER 2311   LEVEL: $level",
        20,
        cheight - 25
    )

    canvas.text(
        s"SCORE: $score",
        230,
        cheight - 25
    )

    canvas.text(
        s"LIVES: $lives",
        360,
        cheight - 25
    )

    canvas.text(
        s"FUEL: ${fuel.toInt}%",
        470,
        cheight - 25
    )

    // Alex message
    canvas.fill(180, 220, 255)

    canvas.text(
        s"Alex: $alexMessage",
        20,
        25
    )
}

// ------------------------------------------------------------
// GAME OVER
// ------------------------------------------------------------

def drawGameOver(canvas: CanvasDraw) {

    canvas.background(40, 5, 5)

    canvas.fill(255, 60, 60)

    canvas.text(
        "GAME OVER",
        cwidth / 2.0 - 60,
        cheight / 2.0 + 80
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        gameOverReason,
        cwidth / 2.0 - 100,
        cheight / 2.0 + 40
    )

    canvas.text(
        s"Final Score: $score",
        cwidth / 2.0 - 65,
        cheight / 2.0
    )

    canvas.fill(200, 50, 50)

    canvas.rect(
        cwidth / 2.0 - 100,
        cheight / 2.0 - 80,
        200,
        50
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        "TRY AGAIN",
        cwidth / 2.0 - 50,
        cheight / 2.0 - 50
    )

    canvas.fill(180, 220, 255)

    canvas.text(
        s"Alex: $alexMessage",
        30,
        50
    )
}

// ------------------------------------------------------------
// LEVEL COMPLETE
// ------------------------------------------------------------

def drawLevelComplete(canvas: CanvasDraw) {

    canvas.background(10, 60, 30)

    canvas.fill(0, 255, 150)

    canvas.text(
        s"LEVEL $level COMPLETE!",
        cwidth / 2.0 - 100,
        cheight / 2.0 + 80
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        s"Score: $score",
        cwidth / 2.0 - 50,
        cheight / 2.0 + 35
    )

    canvas.fill(0, 180, 100)

    canvas.rect(
        cwidth / 2.0 - 120,
        cheight / 2.0 - 70,
        240,
        55
    )

    canvas.fill(255, 255, 255)

    if (level == 1) {

        canvas.text(
            "PLAY LEVEL 2",
            cwidth / 2.0 - 65,
            cheight / 2.0 - 35
        )
    }
    else {

        canvas.text(
            "PLAY LEVEL 3",
            cwidth / 2.0 - 65,
            cheight / 2.0 - 35
        )
    }

    canvas.fill(180, 220, 255)

    canvas.text(
        s"Alex: $alexMessage",
        30,
        50
    )
}

// ------------------------------------------------------------
// FINAL SCREEN
// ------------------------------------------------------------

def drawFinalScreen(canvas: CanvasDraw) {

    canvas.background(5, 20, 40)

    canvas.fill(255, 215, 0)

    canvas.text(
        "MISSION COMPLETE!",
        cwidth / 2.0 - 100,
        cheight / 2.0 + 100
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        "RIDER 2311",
        cwidth / 2.0 - 60,
        cheight / 2.0 + 55
    )

    canvas.text(
        s"FINAL SCORE: $score",
        cwidth / 2.0 - 70,
        cheight / 2.0 + 15
    )

    canvas.fill(0, 180, 120)

    canvas.rect(
        cwidth / 2.0 - 120,
        cheight / 2.0 - 80,
        240,
        55
    )

    canvas.fill(255, 255, 255)

    canvas.text(
        "PLAY AGAIN",
        cwidth / 2.0 - 55,
        cheight / 2.0 - 45
    )

    canvas.fill(180, 220, 255)

    canvas.text(
        "Alex: You are becoming a Scala programmer!",
        30,
        50
    )
}

// ------------------------------------------------------------
// KEYBOARD
// ------------------------------------------------------------

onKeyPress { key =>

    if (
        key == Kc.VK_LEFT ||
        key == Kc.VK_A
    ) {

        moveLeft = true
    }

    if (
        key == Kc.VK_RIGHT ||
        key == Kc.VK_D
    ) {

        moveRight = true
    }

    if (
        key == Kc.VK_UP ||
        key == Kc.VK_W
    ) {

        moveUp = true
    }

    if (
        key == Kc.VK_DOWN ||
        key == Kc.VK_S
    ) {

        moveDown = true
    }
}

onKeyRelease { key =>

    if (
        key == Kc.VK_LEFT ||
        key == Kc.VK_A
    ) {

        moveLeft = false
    }

    if (
        key == Kc.VK_RIGHT ||
        key == Kc.VK_D
    ) {

        moveRight = false
    }

    if (
        key == Kc.VK_UP ||
        key == Kc.VK_W
    ) {

        moveUp = false
    }

    if (
        key == Kc.VK_DOWN ||
        key == Kc.VK_S
    ) {

        moveDown = false
    }
}

// ------------------------------------------------------------
// MOUSE
// ------------------------------------------------------------

onMouseClick { (x, y) =>

    // START SCREEN
    if (screen == 0) {

        if (
            x >= cwidth / 2.0 - 100 &&
            x <= cwidth / 2.0 + 100 &&
            y >= cheight / 2.0 - 25 &&
            y <= cheight / 2.0 + 30
        ) {

            screen = 1

            alex(
                "Choose your bike! I will help you."
            )
        }
    }

    // BIKE SELECTION
    else if (screen == 1) {

        if (
            x >= 70 &&
            x <= 220 &&
            y >= 220 &&
            y <= 410
        ) {

            selectedBike = 1

            startLevel(1)
        }

        else if (
            x >= 270 &&
            x <= 420 &&
            y >= 220 &&
            y <= 410
        ) {

            selectedBike = 2

            startLevel(1)
        }

        else if (
            x >= 470 &&
            x <= 620 &&
            y >= 220 &&
            y <= 410
        ) {

            selectedBike = 3

            startLevel(1)
        }
    }

    // GAME OVER
    else if (screen == 3) {

        if (
            x >= cwidth / 2.0 - 100 &&
            x <= cwidth / 2.0 + 100 &&
            y >= cheight / 2.0 - 80 &&
            y <= cheight / 2.0 - 30
        ) {

            lives = 3
            score = 0

            startLevel(1)
        }
    }

    // LEVEL COMPLETE
    else if (screen == 4) {

        if (
            x >= cwidth / 2.0 - 120 &&
            x <= cwidth / 2.0 + 120 &&
            y >= cheight / 2.0 - 70 &&
            y <= cheight / 2.0 - 15
        ) {

            if (level == 1) {

                startLevel(2)
            }
            else {

                startLevel(3)
            }
        }
    }

    // FINAL SCREEN
    else if (screen == 5) {

        if (
            x >= cwidth / 2.0 - 120 &&
            x <= cwidth / 2.0 + 120 &&
            y >= cheight / 2.0 - 80 &&
            y <= cheight / 2.0 - 25
        ) {

            lives = 3
            score = 0

            screen = 1

            alex(
                "Welcome back! Choose your bike."
            )
        }
    }
}

// ------------------------------------------------------------
// UPDATE PLAYER
// ------------------------------------------------------------

def updatePlayer() {

    if (screen == 2) {

        if (moveLeft) {

            bikeX -= bikeSpeed
        }

        if (moveRight) {

            bikeX += bikeSpeed
        }

        if (moveUp) {

            bikeY += bikeSpeed
        }

        if (moveDown) {

            bikeY -= bikeSpeed
        }

        // Keep bike inside road
        if (bikeX < 90) {

            bikeX = 90
        }

        if (bikeX > cwidth - 90) {

            bikeX = cwidth - 90
        }

        if (bikeY < 60) {

            bikeY = 60
        }

        if (bikeY > cheight - 100) {

            bikeY = cheight - 100
        }
    }
}

// ------------------------------------------------------------
// MAIN GAME LOOP
// ------------------------------------------------------------

animateWithCanvasDraw { canvas =>

    if (screen == 0) {

        drawStartScreen(canvas)
    }

    else if (screen == 1) {

        drawBikeSelection(canvas)
    }

    else if (screen == 2) {

        updatePlayer()

        drawGameScreen(canvas)
    }

    else if (screen == 3) {

        drawGameOver(canvas)
    }

    else if (screen == 4) {

        drawLevelComplete(canvas)
    }

    else if (screen == 5) {

        drawFinalScreen(canvas)
    }
}