Code Sketch


sniper
By: Mhalsakant School
Category: Art
cleari()

// --- Game Dimensions ---
val canvasWidth = 800
val canvasHeight = 600
val roadWidth = 360

// --- Background Terrain ---
val bg = Picture.rectangle(canvasWidth, canvasHeight)
bg.setFillColor(Color(10, 25, 10, 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()

// --- Road Boundary Lines ---
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()

// --- 2-Way Road Central Double Yellow Line ---
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()

// --- Animated Lane Markers ---
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)
  }
}

// --- Side Destination: Right Side Hotel & Parking Spot ---
var destinationY = 700.0
val hotelWidth = 110
val hotelHeight = 90

val hotelBuilding = Picture.rectangle(hotelWidth, hotelHeight)
hotelBuilding.setFillColor(Color(70, 40, 90, 255))
hotelBuilding.setPenColor(white)

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

val hotelSign = Picture.text("HOTEL DELUXE", 11)
hotelSign.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 updateDestination(): Unit = {
  hotelBuilding.setPosition(parkingSpotX, destinationY + 110)
  hotelRoof.setPosition(parkingSpotX - 5, destinationY + 110 + hotelHeight)
  hotelSign.setPosition(parkingSpotX + 5, destinationY + 110 + hotelHeight - 20)
  
  parkingSpot.setPosition(parkingSpotX, destinationY)
  parkingSign.setPosition(parkingSpotX + 22, destinationY + 32)
}

hotelBuilding.draw()
hotelRoof.draw()
hotelSign.draw()
parkingSpot.draw()
parkingSign.draw()
updateDestination()

// --- Night Overlay ---
val nightOverlay = Picture.rectangle(canvasWidth, canvasHeight)
nightOverlay.setFillColor(Color(0, 0, 0, 140))
nightOverlay.setPenColor(noColor)
nightOverlay.setPosition(-canvasWidth / 2, -canvasHeight / 2)
nightOverlay.draw()

// --- Player Car ---
val playerBody = Picture.rectangle(52, 95)
playerBody.setFillColor(Color(245, 245, 250, 255))
playerBody.setPenColor(black)

val playerRoof = Picture.rectangle(46, 34)
playerRoof.setFillColor(Color(230, 230, 235, 255))
playerRoof.setPenColor(gray)

val playerGlass = Picture.rectangle(44, 20)
playerGlass.setFillColor(Color(20, 40, 60, 255))
playerGlass.setPenColor(noColor)

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

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

val playerHeadL = Picture.rectangle(9, 6)
playerHeadL.setFillColor(gray)
playerHeadL.setPenColor(noColor)

val playerHeadR = Picture.rectangle(9, 6)
playerHeadR.setFillColor(gray)
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()
playerGlass.draw()
playerTailL.draw()
playerTailR.draw()
playerHeadL.draw()
playerHeadR.draw()

// --- Enemy Car (Opposing Right Lane) ---
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()

// --- Same-Lane Traffic Car (Left Lane) ---
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()

// --- Game Control Variables ---
var carX = -100.0
var carY = -220.0
val carSteerSpeed = 6.0
var currentPlayerSpeed = 0.0

var enemyX = 40.0
var enemyY = 380.0
var enemySpeed = 6.0

var leftTrafficX = -100.0
var leftTrafficY = 500.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

def updatePlayerPos(): Unit = {
  playerBody.setPosition(carX, carY)
  playerRoof.setPosition(carX + 3, carY + 28)
  playerGlass.setPosition(carX + 4, carY + 58)

  playerTailL.setPosition(carX + 2, carY + 2)
  playerTailR.setPosition(carX + 42, carY + 2)

  playerHeadL.setPosition(carX + 2, carY + 88)
  playerHeadR.setPosition(carX + 41, carY + 88)

  if (isLightActive) {
    beamL.setPosition(carX + 2, carY + 94)
    beamR.setPosition(carX + 41, carY + 94)
  }
}

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 + 120
  enemySpeed = currentLevel match {
    case 1 => 6.0
    case 2 => 9.0
    case 3 => 13.0
  }
  updateEnemyPos()
}

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

resetEnemy()
resetLeftTraffic()
updatePlayerPos()

// --- Top-Right Mini-Map ---
val mapWidth = 110
val mapHeight = 110
val mapX = canvasWidth / 2 - mapWidth - 15
val mapY = canvasHeight / 2 - mapHeight - 15

val miniMapBg = Picture.rectangle(mapWidth, mapHeight)
miniMapBg.setFillColor(Color(15, 20, 25, 240))
miniMapBg.setPenColor(white)
miniMapBg.setPosition(mapX, mapY)
miniMapBg.draw()

val mapRoad = Picture.rectangle(20, mapHeight - 16)
mapRoad.setFillColor(Color(50, 50, 55, 255))
mapRoad.setPenColor(noColor)
mapRoad.setPosition(mapX + mapWidth / 2 - 10, mapY + 8)
mapRoad.draw()

val mapPlayerIcon = Picture.rectangle(6, 8)
mapPlayerIcon.setFillColor(Color(0, 230, 255, 255))
mapPlayerIcon.setPenColor(noColor)
mapPlayerIcon.draw()

val mapHotelIcon = Picture.rectangle(8, 8)
mapHotelIcon.setFillColor(Color(255, 50, 50, 255))
mapHotelIcon.setPenColor(noColor)
mapHotelIcon.setPosition(mapX + mapWidth / 2 + 15, mapY + mapHeight - 20)
mapHotelIcon.draw()

val mapTitleText = Picture.text("MINI MAP", 10)
mapTitleText.setFillColor(white)
mapTitleText.setPosition(mapX + 28, mapY + mapHeight - 14)
mapTitleText.draw()

// --- HUD Displays ---
val hudText = Picture.text("", 15)
hudText.setFillColor(white)
hudText.setPosition(-canvasWidth / 2 + 15, canvasHeight / 2 - 35)
hudText.draw()

val helpControlsText = Picture.text("Drive: SPACE | Reverse: DOWN | Brake: B | Steer: LEFT/RIGHT", 13)
helpControlsText.setFillColor(Color(0, 230, 255, 255))
helpControlsText.setPosition(-canvasWidth / 2 + 15, -canvasHeight / 2 + 20)
helpControlsText.draw()

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

def updateUI(): Unit = {
  val speedKmh = (currentPlayerSpeed * 12).toInt
  val levelLabel = currentLevel match {
    case 1 => "1 (EASY)"
    case 2 => "2 (MEDIUM)"
    case 3 => "3 (HARD)"
  }
  hudText.update(s"LEVEL: $levelLabel | SCORE: $score | SPEED: $speedKmh KM/H")
}

def updateMapProgress(): Unit = {
  val totalProgress = ((currentLevel - 1) * targetDistancePerLevel) + levelDistance
  val maxProgress = 3 * targetDistancePerLevel
  val ratio = Math.min(1.0, Math.max(0.0, totalProgress / maxProgress))

  val iconY = mapY + 12 + (ratio * (mapHeight - 35))
  mapPlayerIcon.setPosition(mapX + mapWidth / 2 - 7, iconY)

  if (currentLevel == 3 && levelDistance > targetDistancePerLevel * 0.7) {
    if (destinationY > -100) {
      destinationY -= currentPlayerSpeed * 0.4
      updateDestination()
    }
  }
}

// --- Game Loop ---
animate {
  if (!gameOver && !gameWon) {

    // --- Controls ---
    if (isKeyPressed(Kc.VK_SPACE)) { // Accelerate
      currentPlayerSpeed = 7.0 + (currentLevel * 2.0)
    } else if (isKeyPressed(Kc.VK_DOWN)) { // Reverse
      currentPlayerSpeed = -4.0
    } else if (isKeyPressed(Kc.VK_B)) { // Brake
      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)
    }

    // Steering
    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 += currentPlayerSpeed * 0.2
      if (levelDistance < 0) levelDistance = 0.0
    }
    
    updateMapProgress()
    updateUI()

    // Level Check
    if (levelDistance >= targetDistancePerLevel) {
      if (currentLevel < 3) {
        currentLevel += 1
        levelDistance = 0.0
      }
    }

    // Exact Parking Spot Verification (Level 3 Required)
    if (currentLevel == 3 && destinationY <= -50) {
      val isInsideSpotX = carX >= (parkingSpotX - 10) && (carX + 52) <= (parkingSpotX + parkingSpotWidth + 10)
      val isInsideSpotY = carY >= (destinationY - 20) && (carY + 95) <= (destinationY + parkingSpotHeight + 20)

      if (isInsideSpotX && isInsideSpotY && currentPlayerSpeed == 0.0) {
        gameWon = true
        statusText.setFillColor(Color(50, 255, 50, 255))
        statusText.update("PERFECTLY PARKED! GAME WON! Press 'R'")
        statusText.draw()
      }
    }

    // Light Dipper Control
    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(gray)
        playerHeadR.setFillColor(gray)
        beamL.erase()
        beamR.erase()
      }
    }

    updatePlayerPos()

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

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

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

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