Code Sketch


sniper_0718
By: Mhalsakant School
Category: Art
cleari()

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

// --- Background Terrain (Dynamic per level) ---
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()

// --- 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 (Hidden Initially) ---
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()

// --- Realistic White Land Rover Defender (Player Car) ---
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()

// --- 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 = 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 =>
      // Level 1: Countryside / City Day (Green Theme)
      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 =>
      // Level 2: Desert Highway (Sandy Orange Theme)
      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 =>
      // Level 3: Night Drive / Cyber City (Dark Navy Theme)
      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()

// --- 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(Color(20, 20, 20, 255))
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(10, 40, 80, 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 (HOTEL)"
    case 2 => "2 (VILLA)"
    case 3 => "3 (CLUB)"
  }
  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)
}

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

    // --- Controls ---
    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
    }
    
    updateMapProgress()
    updateUI()

    // Level Distance Complete Check -> Reveal Destination & Smoothly auto-steer towards parking
    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) { // ~3 seconds delay
          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("ALL DESTINATIONS REACHED! WON! Press 'R'")
            statusText.draw()
          }
        } else {
          statusText.setFillColor(Color(0, 230, 255, 255))
          statusText.update(s"PARKED! Next level in ${((180.0 - levelTransitionTimer)/60.0).toInt + 1}s...")
          statusText.draw()
        }
      } else {
        statusText.setFillColor(Color(255, 204, 0, 255))
        statusText.update("PARKING IN PROGRESS...")
        statusText.draw()
      }
    } else {
      statusText.erase()
    }

    // 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(yellow)
        playerHeadR.setFillColor(yellow)
        beamL.erase()
        beamR.erase()
      }
    }

    updatePlayerPos()

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

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

    // Collision Check
    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' to Restart")
      statusText.draw()
    }
  } else {
    if (isKeyPressed(Kc.VK_R)) {
      gameOver = false
      gameWon = false
      currentLevel = 1
      levelDistance = 0.0
      score = 0
      currentPlayerSpeed = 0.0
      levelTransitionTimer = 0.0
      setupLevelTheme()
      statusText.erase()
      carX = -100.0
      carY = -220.0
      resetEnemy()
      resetLeftTraffic()
      updatePlayerPos()
    }
  }
}