Code Sketch


sniper_0718
By: Mhalsakant School
// Clear screen and setup canvas
cleari()
setBackground(black)

// --- Helper: Draw Curved Square App Icon ---
def drawCurvedSquare(x: Double, y: Double, size: Double, fillColor: Color, label: String): Unit = {
  val box = Picture.rectangle(size, size)
    .withFillColor(fillColor)
    .withPenColor(fillColor)
  
  val txt = Picture.text(label)
    .withPenColor(black)
  
  val btn = picStackCentered(box, txt)
  btn.setPosition(x + size/2, y + size/2)
  btn.draw()
}

// ==========================================
// GAME 1: LAND ROVER DEFENDER CAR GAME
// ==========================================
def runCarGame(): Unit = {
  cleari()
  val canvasWidth = 800
  val canvasHeight = 600
  val roadWidth = 360

  val bg = Picture.rectangle(canvasWidth, canvasHeight)
  bg.setFillColor(Color(70, 160, 70, 255))
  bg.setPenColor(noColor)
  bg.setPosition(-canvasWidth / 2, -canvasHeight / 2)
  bg.draw()

  val road = Picture.rectangle(roadWidth, canvasHeight)
  road.setFillColor(Color(35, 35, 38, 255))
  road.setPenColor(noColor)
  road.setPosition(-roadWidth / 2, -canvasHeight / 2)
  road.draw()

  val lineLeft = Picture.rectangle(6, canvasHeight)
  lineLeft.setFillColor(white)
  lineLeft.setPenColor(noColor)
  lineLeft.setPosition(-roadWidth / 2 + 5, -canvasHeight / 2)
  lineLeft.draw()

  val lineRight = Picture.rectangle(6, canvasHeight)
  lineRight.setFillColor(white)
  lineRight.setPenColor(noColor)
  lineRight.setPosition(roadWidth / 2 - 11, -canvasHeight / 2)
  lineRight.draw()

  val centerDivider1 = Picture.rectangle(3, canvasHeight)
  centerDivider1.setFillColor(Color(255, 204, 0, 255))
  centerDivider1.setPenColor(noColor)
  centerDivider1.setPosition(-4, -canvasHeight / 2)
  centerDivider1.draw()

  val centerDivider2 = Picture.rectangle(3, canvasHeight)
  centerDivider2.setFillColor(Color(255, 204, 0, 255))
  centerDivider2.setPenColor(noColor)
  centerDivider2.setPosition(1, -canvasHeight / 2)
  centerDivider2.draw()

  val dashCount = 8
  val dashHeight = 40
  val dashGap = 50
  var dashOffset = 0.0

  val whiteDashesL = Array.fill(dashCount) {
    val d = Picture.rectangle(3, dashHeight)
    d.setFillColor(Color(200, 200, 200, 150))
    d.setPenColor(noColor)
    d.draw()
    d
  }

  val whiteDashesR = Array.fill(dashCount) {
    val d = Picture.rectangle(3, dashHeight)
    d.setFillColor(Color(200, 200, 200, 150))
    d.setPenColor(noColor)
    d.draw()
    d
  }

  def updateDashes(moveSpeed: Double): Unit = {
    dashOffset = (dashOffset - moveSpeed) % (dashHeight + dashGap)
    for (i <- 0 until dashCount) {
      val yPos = (canvasHeight / 2) - (i * (dashHeight + dashGap)) + dashOffset
      whiteDashesL(i).setPosition(-roadWidth / 4, yPos)
      whiteDashesR(i).setPosition(roadWidth / 4, yPos)
    }
  }

  val destFixedY = 120.0
  val destWidth = 110
  val destHeight = 90

  val building = Picture.rectangle(destWidth, destHeight)
  building.setFillColor(Color(70, 40, 90, 255))
  building.setPenColor(white)

  val roof = Picture.rectangle(destWidth + 10, 15)
  roof.setFillColor(Color(200, 50, 50, 255))
  roof.setPenColor(noColor)

  val destSign = Picture.text("HOTEL DELUXE", 11)
  destSign.setFillColor(yellow)

  val parkingSpotX = (roadWidth / 2 + 15).toDouble
  val parkingSpotWidth = 70
  val parkingSpotHeight = 100

  val parkingSpot = Picture.rectangle(parkingSpotWidth, parkingSpotHeight)
  parkingSpot.setFillColor(Color(50, 50, 55, 255))
  parkingSpot.setPenColor(white)

  val parkingSign = Picture.text("P", 30)
  parkingSign.setFillColor(Color(0, 230, 255, 255))

  def updateDestinationPosition(): Unit = {
    building.setPosition(parkingSpotX, destFixedY + 110)
    roof.setPosition(parkingSpotX - 5, destFixedY + 110 + destHeight)
    destSign.setPosition(parkingSpotX + 5, destFixedY + 110 + destHeight - 20)
    parkingSpot.setPosition(parkingSpotX, destFixedY)
    parkingSign.setPosition(parkingSpotX + 22, destFixedY + 32)
  }

  def hideDestination(): Unit = {
    building.setPosition(-1000, -1000)
    roof.setPosition(-1000, -1000)
    destSign.setPosition(-1000, -1000)
    parkingSpot.setPosition(-1000, -1000)
    parkingSign.setPosition(-1000, -1000)
  }

  def showDestination(): Unit = {
    updateDestinationPosition()
  }

  hideDestination()

  val playerBody = Picture.rectangle(54, 100)
  playerBody.setFillColor(Color(245, 245, 250, 255))
  playerBody.setPenColor(gray)

  val playerRoof = Picture.rectangle(48, 42)
  playerRoof.setFillColor(Color(215, 215, 220, 255))
  playerRoof.setPenColor(gray)

  val playerRack = Picture.rectangle(44, 30)
  playerRack.setFillColor(Color(30, 30, 30, 255))
  playerRack.setPenColor(noColor)

  val playerGlassF = Picture.rectangle(42, 16)
  playerGlassF.setFillColor(Color(20, 30, 45, 255))
  playerGlassF.setPenColor(noColor)

  val playerGlassR = Picture.rectangle(42, 18)
  playerGlassR.setFillColor(Color(20, 30, 45, 255))
  playerGlassR.setPenColor(noColor)

  val playerSpareWheel = Picture.rectangle(16, 20)
  playerSpareWheel.setFillColor(Color(40, 40, 40, 255))
  playerSpareWheel.setPenColor(black)

  val playerTailL = Picture.rectangle(6, 6)
  playerTailL.setFillColor(Color(220, 20, 20, 255))
  playerTailL.setPenColor(noColor)

  val playerTailR = Picture.rectangle(6, 6)
  playerTailR.setFillColor(Color(220, 20, 20, 255))
  playerTailR.setPenColor(noColor)

  val playerHeadL = Picture.rectangle(10, 8)
  playerHeadL.setFillColor(yellow)
  playerHeadL.setPenColor(noColor)

  val playerHeadR = Picture.rectangle(10, 8)
  playerHeadR.setFillColor(yellow)
  playerHeadR.setPenColor(noColor)

  val beamL = Picture.fromPath { p =>
    p.moveTo(0, 0)
    p.lineTo(-45, 170)
    p.lineTo(20, 170)
    p.lineTo(9, 0)
  }
  beamL.setFillColor(Color(245, 245, 255, 140))
  beamL.setPenColor(noColor)

  val beamR = Picture.fromPath { p =>
    p.moveTo(0, 0)
    p.lineTo(-20, 170)
    p.lineTo(45, 170)
    p.lineTo(9, 0)
  }
  beamR.setFillColor(Color(245, 245, 255, 140))
  beamR.setPenColor(noColor)

  playerBody.draw()
  playerRoof.draw()
  playerRack.draw()
  playerGlassF.draw()
  playerGlassR.draw()
  playerSpareWheel.draw()
  playerTailL.draw()
  playerTailR.draw()
  playerHeadL.draw()
  playerHeadR.draw()

  val enemyBody = Picture.rectangle(52, 95)
  enemyBody.setFillColor(Color(200, 30, 30, 255))
  enemyBody.setPenColor(gray)

  val enemyRoof = Picture.rectangle(44, 34)
  enemyRoof.setFillColor(Color(120, 20, 20, 255))
  enemyRoof.setPenColor(noColor)

  val enemyGlass = Picture.rectangle(44, 18)
  enemyGlass.setFillColor(Color(50, 50, 70, 255))
  enemyGlass.setPenColor(noColor)

  val enemyHeadL = Picture.rectangle(8, 6)
  enemyHeadL.setFillColor(yellow)
  enemyHeadL.setPenColor(noColor)

  val enemyHeadR = Picture.rectangle(8, 6)
  enemyHeadR.setFillColor(yellow)
  enemyHeadR.setPenColor(noColor)

  enemyBody.draw()
  enemyRoof.draw()
  enemyGlass.draw()
  enemyHeadL.draw()
  enemyHeadR.draw()

  val leftTrafficBody = Picture.rectangle(52, 95)
  leftTrafficBody.setFillColor(Color(30, 140, 200, 255))
  leftTrafficBody.setPenColor(gray)

  val leftTrafficRoof = Picture.rectangle(44, 34)
  leftTrafficRoof.setFillColor(Color(15, 80, 130, 255))
  leftTrafficRoof.setPenColor(noColor)

  val leftTrafficGlass = Picture.rectangle(44, 18)
  leftTrafficGlass.setFillColor(Color(40, 60, 80, 255))
  leftTrafficGlass.setPenColor(noColor)

  val leftTrafficTailL = Picture.rectangle(8, 6)
  leftTrafficTailL.setFillColor(Color(220, 20, 20, 255))
  leftTrafficTailL.setPenColor(noColor)

  val leftTrafficTailR = Picture.rectangle(8, 6)
  leftTrafficTailR.setFillColor(Color(220, 20, 20, 255))
  leftTrafficTailR.setPenColor(noColor)

  leftTrafficBody.draw()
  leftTrafficRoof.draw()
  leftTrafficGlass.draw()
  leftTrafficTailL.draw()
  leftTrafficTailR.draw()

  var carX = -100.0
  var carY = -220.0
  val carSteerSpeed = 6.0
  var currentPlayerSpeed = 0.0
  var enemyX = 40.0
  var enemyY = 420.0
  var enemySpeed = 6.0
  var leftTrafficX = -100.0
  var leftTrafficY = 650.0
  var leftTrafficSpeed = 3.0
  var isLightActive = false
  var currentLevel = 1
  var levelDistance = 0.0
  val targetDistancePerLevel = 600.0
  var gameOver = false
  var gameWon = false
  var score = 0
  var levelTransitionTimer = 0.0
  var destinationShown = false

  def updatePlayerPos(): Unit = {
    playerBody.setPosition(carX, carY)
    playerRoof.setPosition(carX + 3, carY + 29)
    playerRack.setPosition(carX + 5, carY + 35)
    playerGlassF.setPosition(carX + 6, carY + 68)
    playerGlassR.setPosition(carX + 6, carY + 44)
    playerSpareWheel.setPosition(carX + 19, carY + 4)
    playerTailL.setPosition(carX + 4, carY + 2)
    playerTailR.setPosition(carX + 44, carY + 2)
    playerHeadL.setPosition(carX + 4, carY + 92)
    playerHeadR.setPosition(carX + 40, carY + 92)
    if (isLightActive) {
      beamL.setPosition(carX + 4, carY + 98)
      beamR.setPosition(carX + 40, carY + 98)
    }
  }

  def updateEnemyPos(): Unit = {
    enemyBody.setPosition(enemyX, enemyY)
    enemyRoof.setPosition(enemyX + 4, enemyY + 28)
    enemyGlass.setPosition(enemyX + 4, enemyY + 20)
    enemyHeadL.setPosition(enemyX + 2, enemyY + 2)
    enemyHeadR.setPosition(enemyX + 42, enemyY + 2)
  }

  def updateLeftTrafficPos(): Unit = {
    leftTrafficBody.setPosition(leftTrafficX, leftTrafficY)
    leftTrafficRoof.setPosition(leftTrafficX + 4, leftTrafficY + 28)
    leftTrafficGlass.setPosition(leftTrafficX + 4, leftTrafficY + 58)
    leftTrafficTailL.setPosition(leftTrafficX + 2, leftTrafficY + 2)
    leftTrafficTailR.setPosition(leftTrafficX + 42, leftTrafficY + 2)
  }

  def resetEnemy(): Unit = {
    enemyX = random(20, (roadWidth / 2 - 65).toInt)
    enemyY = canvasHeight / 2 + 180
    enemySpeed = currentLevel match {
      case 1 => 6.0
      case 2 => 10.0
      case 3 => 15.0
    }
    updateEnemyPos()
  }

  def resetLeftTraffic(): Unit = {
    leftTrafficX = random((-roadWidth / 2 + 15).toInt, -65)
    leftTrafficY = canvasHeight / 2 + 380
    leftTrafficSpeed = currentLevel match {
      case 1 => 2.0
      case 2 => 5.0
      case 3 => 8.0
    }
    updateLeftTrafficPos()
  }

  def setupLevelTheme(): Unit = {
    currentLevel match {
      case 1 =>
        bg.setFillColor(Color(70, 160, 70, 255))
        building.setFillColor(Color(70, 40, 90, 255))
        roof.setFillColor(Color(200, 50, 50, 255))
        destSign.update("HOTEL DELUXE")
      case 2 =>
        bg.setFillColor(Color(190, 140, 70, 255))
        building.setFillColor(Color(130, 70, 40, 255))
        roof.setFillColor(Color(240, 180, 40, 255))
        destSign.update("SUNSET VILLA")
      case 3 =>
        bg.setFillColor(Color(20, 25, 45, 255))
        building.setFillColor(Color(30, 90, 110, 255))
        roof.setFillColor(Color(220, 40, 120, 255))
        destSign.update("BEACH CLUB")
    }
    hideDestination()
    destinationShown = false
  }

  resetEnemy()
  resetLeftTraffic()
  updatePlayerPos()
  setupLevelTheme()

  val hudText = Picture.text("", 15)
  hudText.setFillColor(Color(20, 20, 20, 255))
  hudText.setPosition(-canvasWidth / 2 + 15, canvasHeight / 2 - 35)
  hudText.draw()

  val statusText = Picture.text("", 24)
  statusText.setPosition(-240, 0)

  animate {
    if (!gameOver && !gameWon) {
      if (levelDistance < targetDistancePerLevel) {
        if (isKeyPressed(Kc.VK_SPACE)) { 
          currentPlayerSpeed = 7.0 + (currentLevel * 2.0)
        } else if (isKeyPressed(Kc.VK_DOWN)) { 
          currentPlayerSpeed = -4.0
        } else if (isKeyPressed(Kc.VK_B)) { 
          currentPlayerSpeed = 0.0
        } else {
          if (currentPlayerSpeed > 0) currentPlayerSpeed = Math.max(0.0, currentPlayerSpeed - 0.5)
          if (currentPlayerSpeed < 0) currentPlayerSpeed = Math.min(0.0, currentPlayerSpeed + 0.5)
        }
      } else {
        currentPlayerSpeed = 0.0
      }

      if (isKeyPressed(Kc.VK_LEFT)) carX -= carSteerSpeed
      if (isKeyPressed(Kc.VK_RIGHT)) carX += carSteerSpeed

      val minX = -canvasWidth / 2 + 20
      val maxX = canvasWidth / 2 - 70
      if (carX < minX) carX = minX
      if (carX > maxX) carX = maxX

      updateDashes(currentPlayerSpeed)
      
      if (currentPlayerSpeed != 0.0 && levelDistance < targetDistancePerLevel) {
        levelDistance += currentPlayerSpeed * 0.2
        if (levelDistance < 0) levelDistance = 0.0
      }

      val speedKmh = (currentPlayerSpeed * 12).toInt
      val levelLabel = currentLevel match {
        case 1 => "1 (HOTEL)"
        case 2 => "2 (VILLA)"
        case 3 => "3 (CLUB)"
      }
      
      hudText.update("LEVEL: " + levelLabel + " | SCORE: " + score + " | SPEED: " + speedKmh + " KM/H")

      if (levelDistance >= targetDistancePerLevel) {
        if (!destinationShown) {
          showDestination()
          destinationShown = true
        }
        if (carY < destFixedY) carY += 4.0
        if (carX < parkingSpotX - 10) carX += 1.0
        else if (carX > parkingSpotX + 10) carX -= 1.0

        val isInsideSpotX = carX >= (parkingSpotX - 35) && (carX + 54) <= (parkingSpotX + parkingSpotWidth + 35)
        val isInsideSpotY = carY >= (destFixedY - 40) && (carY + 100) <= (destFixedY + parkingSpotHeight + 40)

        if (isInsideSpotX && isInsideSpotY) {
          levelTransitionTimer += 1.0
          if (levelTransitionTimer > 180.0) {
            levelTransitionTimer = 0.0
            if (currentLevel < 3) {
              currentLevel += 1
              levelDistance = 0.0
              carY = -220.0
              setupLevelTheme()
              resetEnemy()
              resetLeftTraffic()
            } else {
              gameWon = true
              statusText.setFillColor(Color(50, 255, 50, 255))
              statusText.update("WON! Press 'R'")
              statusText.draw()
            }
          } else {
            statusText.setFillColor(Color(0, 230, 255, 255))
            statusText.update("PARKED! Next level in...")
            statusText.draw()
          }
        }
      }

      if (isKeyPressed(Kc.VK_A)) {
        if (!isLightActive) {
          isLightActive = true
          playerHeadL.setFillColor(Color(245, 245, 255, 255))
          playerHeadR.setFillColor(Color(245, 245, 255, 255))
          beamL.draw()
          beamR.draw()
        }
      } else {
        if (isLightActive) {
          isLightActive = false
          playerHeadL.setFillColor(yellow)
          playerHeadR.setFillColor(yellow)
          beamL.erase()
          beamR.erase()
        }
      }

      updatePlayerPos()

      if (levelDistance < targetDistancePerLevel) {
        enemyY -= (enemySpeed + currentPlayerSpeed * 0.5)
        if (enemyY < -canvasHeight / 2 - 120) {
          if (currentPlayerSpeed > 0) score += 10
          resetEnemy()
        }
      }
      updateEnemyPos()

      if (levelDistance < targetDistancePerLevel) {
        leftTrafficY -= (currentPlayerSpeed - leftTrafficSpeed)
        if (leftTrafficY < -canvasHeight / 2 - 120) {
          if (currentPlayerSpeed > 0) score += 15
          resetLeftTraffic()
        }
      }
      updateLeftTrafficPos()

      val crashEnemy = Math.abs((carX + 27) - (enemyX + 26)) < 44 && Math.abs((carY + 50) - (enemyY + 47)) < 82
      val crashLeftTraffic = Math.abs((carX + 27) - (leftTrafficX + 26)) < 44 && Math.abs((carY + 50) - (leftTrafficY + 47)) < 82

      if ((crashEnemy || crashLeftTraffic) && levelDistance < targetDistancePerLevel) {
        gameOver = true
        statusText.setFillColor(Color(255, 50, 50, 255))
        statusText.update("CRASHED! Press 'R'")
        statusText.draw()
      }
    } else {
      if (isKeyPressed(Kc.VK_R)) {
        gameOver = false
        gameWon = false
        currentLevel = 1
        levelDistance = 0.0
        score = 0
        currentPlayerSpeed = 0.0
        setupLevelTheme()
        statusText.erase()
        carX = -100.0
        carY = -220.0
        resetEnemy()
        resetLeftTraffic()
        updatePlayerPos()
      }
    }
  }
}

// ==========================================
// MAIN MENU / LAUNCHER ("BABU APP")
// ==========================================
def runBabuApp(): Unit = {
  cleari()
  setBackground(darkGray)

  val phoneFrame = Picture.rectangle(340, 540)
    .withPenColor(white)
    .withFillColor(black)
  phoneFrame.setPosition(-170, -270)
  phoneFrame.draw()

  val title = Picture.text("Babu App Menu")
    .withPenColor(cyan)
  title.setPosition(-60, 200)
  title.draw()

  val subTitle = Picture.text("Click an App Icon below", 12)
    .withPenColor(white)
  subTitle.setPosition(-65, 170)
  subTitle.draw()

  drawCurvedSquare(-80, 50, 90, pink, "Car Game")
  drawCurvedSquare(20, 50, 90, green, "Truck Game")

  onMouseClick { (x, y) =>
    if (x >= -80 && x <= 10 && y >= 50 && y <= 140) {
      runCarGame()
    }
  }
}

// ==========================================
// LOCK SCREEN LOGIN (USING readln POPUP)
// ==========================================
def runLockScreen(): Unit = {
  val lockFrame = Picture.rectangle(340, 540)
    .withPenColor(white)
    .withFillColor(darkGray)
  lockFrame.setPosition(-170, -270)
  lockFrame.draw()

  val lockTitle = Picture.text("Babu App - Locked")
    .withPenColor(yellow)
  lockTitle.setPosition(-70, 120)
  lockTitle.draw()

  val boxPrompt = Picture.text("Click the box below to login:")
    .withPenColor(white)
  boxPrompt.setPosition(-110, 90)
  boxPrompt.draw()

  val inputBoxBg = Picture.rectangle(240, 50)
    .withPenColor(cyan)
    .withFillColor(black)
  inputBoxBg.setPosition(-120, 20)
  inputBoxBg.draw()

  val boxText = Picture.text("Enter Password")
    .withPenColor(cyan)
  boxText.setPosition(-65, 35)
  boxText.draw()

  val errDisplay = Picture.text("")
    .withPenColor(red)
  errDisplay.setPosition(-100, -50)
  errDisplay.draw()

  onMouseClick { (x, y) =>
    if (x >= -120 && x <= 120 && y >= 20 && y <= 70) {
      // Using readln for Kojo popup dialog input
      val enteredPassword = readln("Enter Password to open Babu App:").trim
      
      if (enteredPassword == "aishu") {
        println("Access Granted! Opening Babu App...")
        runBabuApp()
      } else {
        println("Wrong Password! Access Denied.")
        errDisplay.update("Wrong Password! Try Again.")
      }
    }
  }
}

// Start the lock screen when the program runs
runLockScreen()