Code Sketch


sniper_0718
By: Mhalsakant School
Category: Art
cleari()

// --- Canvas & Road Setup ---
val canvasWidth = 800
val canvasHeight = 600
val roadWidth = 340

val bg = Picture.rectangle(canvasWidth, canvasHeight)
bg.setFillColor(color(10, 25, 10))
bg.setPenColor(noColor)
bg.setPosition(-canvasWidth / 2, -canvasHeight / 2)
bg.draw()

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

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

// --- Dashes ---
val dashCount = 8
val dashHeight = 40
val dashGap = 50
var dashOffset = 0.0

val yellowDashesL = Array.fill(dashCount) {
  val d = Picture.rectangle(4, dashHeight)
  d.setFillColor(color(255, 204, 0))
  d.setPenColor(noColor)
  d.draw()
  d
}

def updateDashes(speed: Double, roadCurve: Double): Unit = {
  dashOffset = (dashOffset - speed) % (dashHeight + dashGap)
  for (i <- 0 until dashCount) {
    val yPos = (canvasHeight / 2) - (i * (dashHeight + dashGap)) + dashOffset
    yellowDashesL(i).setPosition(-2 + roadCurve, yPos)
  }
}

// --- ROUTE & WALLET DATA ---
case class RouteInfo(name: String, distance: Double, reward: Int)

val routesList = Array(
  RouteInfo("Pune -> Satara", 1500.0, 1500),
  RouteInfo("Solapur -> Kolhapur", 1500.0, 1500),
  RouteInfo("Mumbai -> Karnataka", 3009.0, 3009)
)

var currentRouteIdx = 0
var playerWallet = 1000
var distanceMeters = routesList(currentRouteIdx).distance

// --- SPEED & MOVEMENT VARIABLES ---
var currentSpeed = 0.0
val maxSpeed = 12.0
val acceleration = 0.4
val friction = 0.2

// --- OPEN BALANCE DISPLAY ---
val walletText = Picture.text(s"BALANCE: ?$playerWallet", 18)
walletText.setFillColor(color(0, 255, 120))
walletText.setPosition(-canvasWidth / 2 + 20, canvasHeight / 2 - 35)
walletText.draw()

// --- SPEEDOMETER DISPLAY ---
val speedText = Picture.text("SPEED: 0 KM/H", 16)
speedText.setFillColor(color(0, 230, 255))
speedText.setPosition(-canvasWidth / 2 + 20, canvasHeight / 2 - 65)
speedText.draw()

// --- RIGHT CORNER 'S' SONG SYMBOL BUTTON ---
val songButtonBg = Picture.rectangle(40, 40)
songButtonBg.setFillColor(color(200, 30, 60))
songButtonBg.setPenColor(white)
songButtonBg.setPosition(canvasWidth / 2 - 50, canvasHeight / 2 - 50)
songButtonBg.draw()

val songButtonText = Picture.text("S", 18)
songButtonText.setFillColor(white)
songButtonText.setPosition(canvasWidth / 2 - 38, canvasHeight / 2 - 42)
songButtonText.draw()

var songPlaying = false
var sKeyWasPressed = false
val songStatusText = Picture.text("", 12)
songStatusText.setFillColor(color(255, 215, 0))
songStatusText.setPosition(canvasWidth / 2 - 180, canvasHeight / 2 - 110)

// --- SMALLER GPS TRACKER (Top Right) ---
val mapBg = Picture.rectangle(120, 50)
mapBg.setFillColor(color(15, 20, 35))
mapBg.setPenColor(cyan)
mapBg.setPosition(canvasWidth / 2 - 140, canvasHeight / 2 - 55)
mapBg.draw()

val mapRoad = Picture.rectangle(100, 6)
mapRoad.setFillColor(color(60, 60, 70))
mapRoad.setPosition(canvasWidth / 2 - 130, canvasHeight / 2 - 33)
mapRoad.draw()

val mapTitle = Picture.text("GPS", 9)
mapTitle.setFillColor(color(0, 230, 255))
mapTitle.setPosition(canvasWidth / 2 - 130, canvasHeight / 2 - 18)
mapTitle.draw()

val mapDestPin = Picture.text("D", 10)
mapDestPin.setFillColor(red)
mapDestPin.setPosition(canvasWidth / 2 - 42, canvasHeight / 2 - 38)
mapDestPin.draw()

val mapPlayerPin = Picture.circle(4)
mapPlayerPin.setFillColor(yellow)
mapPlayerPin.setPosition(canvasWidth / 2 - 130, canvasHeight / 2 - 33)
mapPlayerPin.draw()

// --- ROUTE SELECTOR BUTTON (Placed on Left Side of Map) ---
val routeBtnBg = Picture.rectangle(95, 30)
routeBtnBg.setFillColor(color(40, 90, 180))
routeBtnBg.setPenColor(cyan)
routeBtnBg.setPosition(canvasWidth / 2 - 245, canvasHeight / 2 - 45)
routeBtnBg.draw()

val routeBtnText = Picture.text("ROUTES ('M')", 9)
routeBtnText.setFillColor(white)
routeBtnText.setPosition(canvasWidth / 2 - 240, canvasHeight / 2 - 30)
routeBtnText.draw()

// --- DESTINATION LIST PANEL ---
val listPanelBg = Picture.rectangle(350, 180)
listPanelBg.setFillColor(color(15, 15, 25))
listPanelBg.setPenColor(cyan)
listPanelBg.setPosition(-175, -90)

val listHeader = Picture.text("--- SELECT A ROUTE ---", 13)
listHeader.setFillColor(color(255, 200, 0))
listHeader.setPosition(-150, 60)

val routeItem1 = Picture.text("1. Pune to Satara - 1500", 11)
routeItem1.setFillColor(white); routeItem1.setPosition(-160, 30)

val routeItem2 = Picture.text("2. Solapur to Kolhapur - 1500", 11)
routeItem2.setFillColor(white); routeItem2.setPosition(-160, 5)

val routeItem3 = Picture.text("3. Mumbai to Karnataka - 3009", 11)
routeItem3.setFillColor(white); routeItem3.setPosition(-160, -20)

val listCloseHint = Picture.text("Press '1', '2', '3' to select or 'M' to close", 10)
listCloseHint.setFillColor(gray); listCloseHint.setPosition(-165, -55)

var showDestinationList = false
var mKeyWasPressed = false

// --- HOTEL & GREEN PARKING BOX GRAPHICS ---
var hotelY = canvasHeight / 2.0 + 400.0

val hotelBuilding = Picture.rectangle(95, 150)
hotelBuilding.setFillColor(color(100, 70, 50))
hotelBuilding.setPenColor(yellow)

val hotelSign = Picture.text("HIGHWAY HOTEL", 11)
hotelSign.setFillColor(yellow)

val hotelDoor = Picture.rectangle(30, 45)
hotelDoor.setFillColor(color(50, 30, 20))

val greenParkingBox = Picture.rectangle(80, 110)
greenParkingBox.setFillColor(color(30, 160, 60))
greenParkingBox.setPenColor(color(0, 255, 100))

val parkHereText = Picture.text("PARK HERE & PRESS 'P'", 10)
parkHereText.setFillColor(color(255, 255, 255))

// --- PLAYER TRUCK ---
val playerCab = Picture.rectangle(52, 40)
playerCab.setFillColor(color(245, 245, 250))
playerCab.setPenColor(black)

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

val playerBed = Picture.rectangle(50, 65)
playerBed.setFillColor(color(180, 40, 40))
playerBed.setPenColor(black)

playerBed.draw(); playerCab.draw(); playerGlass.draw()

var carX = -26.0
var carY = -220.0
val carSteerSpeed = 6.0

def updatePlayerPos(): Unit = {
  playerBed.setPosition(carX + 1, carY)
  playerCab.setPosition(carX + 1, carY + 63)
  playerGlass.setPosition(carX + 5, carY + 80)
}

updatePlayerPos()

// --- HUD Displays ---
var gameOver = false
var routeCompleted = false

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

val helpText = Picture.text("Drive: SPACE | Steer: LEFT/RIGHT | Menu: 'M' | Park: 'P' | Song: 'S'", 11)
helpText.setFillColor(color(0, 230, 255))
helpText.setPosition(-canvasWidth / 2 + 20, -canvasHeight / 2 + 20)
helpText.draw()

val victoryText = Picture.text("", 16)
victoryText.setFillColor(color(0, 255, 100))
victoryText.setPosition(-270, 0)

// Helper to draw Hotel and Green Parking Box with Road Curves
def drawHotel(roadCurve: Double): Unit = {
  hotelBuilding.setPosition(roadWidth / 2 + 5 + roadCurve, hotelY)
  hotelBuilding.draw()
  hotelSign.setPosition(roadWidth / 2 + 10 + roadCurve, hotelY + 115)
  hotelSign.draw()
  hotelDoor.setPosition(roadWidth / 2 + 38 + roadCurve, hotelY + 10)
  hotelDoor.draw()
  
  greenParkingBox.setPosition(roadWidth / 2 - 85 + roadCurve, hotelY - 20)
  greenParkingBox.draw()
  parkHereText.setPosition(roadWidth / 2 - 83 + roadCurve, hotelY + 35)
  parkHereText.draw()
}

def eraseHotel(): Unit = {
  hotelBuilding.erase()
  hotelSign.erase()
  hotelDoor.erase()
  greenParkingBox.erase()
  parkHereText.erase()
}

// --- Game Loop ---
animate {
  if (!gameOver && !routeCompleted) {
    val currentRoute = routesList(currentRouteIdx)
    hudText.update(s"ROUTE: ${currentRoute.name} | DIST: ${distanceMeters.toInt} M")
    walletText.update(s"BALANCE: ?$playerWallet")

    val displaySpeed = (currentSpeed * 10).toInt
    speedText.update(s"SPEED: $displaySpeed KM/H")

    // Calculate winding road curve offset based on distance traveled
    val roadCurveX = Math.sin((currentRoute.distance - distanceMeters) * 0.004) * 90.0

    // Update Road and Lane positions with turns
    road.setPosition(-roadWidth / 2 + roadCurveX, -canvasHeight / 2)
    lineLeft.setPosition(-roadWidth / 2 + 5 + roadCurveX, -canvasHeight / 2)
    lineRight.setPosition(roadWidth / 2 - 11 + roadCurveX, -canvasHeight / 2)

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

    // Dynamic road boundaries considering turns
    val minX = roadCurveX - roadWidth / 2 + 12
    val maxX = roadCurveX + roadWidth / 2 - 64
    if (carX < minX) carX = minX
    if (carX > maxX) carX = maxX

    // Menu Toggle via Keyboard 'M'
    val mPressedNow = isKeyPressed(Kc.VK_M)
    if (mPressedNow && !mKeyWasPressed) {
      showDestinationList = !showDestinationList
      if (showDestinationList) {
        listPanelBg.draw(); listHeader.draw(); routeItem1.draw(); routeItem2.draw(); routeItem3.draw(); listCloseHint.draw()
      } else {
        listPanelBg.erase(); listHeader.erase(); routeItem1.erase(); routeItem2.erase(); routeItem3.erase(); listCloseHint.erase()
      }
    }
    mKeyWasPressed = mPressedNow

    // Route selection via keys 1, 2, 3 when menu is open
    if (showDestinationList) {
      var selectedRoute = -1
      if (isKeyPressed(Kc.VK_1)) selectedRoute = 0
      if (isKeyPressed(Kc.VK_2)) selectedRoute = 1
      if (isKeyPressed(Kc.VK_3)) selectedRoute = 2

      if (selectedRoute != -1) {
        currentRouteIdx = selectedRoute
        distanceMeters = routesList(currentRouteIdx).distance
        currentSpeed = 0.0
        hotelY = canvasHeight / 2.0 + 400.0
        eraseHotel()
        victoryText.erase()
        carX = roadCurveX - 26.0
        showDestinationList = false
        listPanelBg.erase(); listHeader.erase(); routeItem1.erase(); routeItem2.erase(); routeItem3.erase(); listCloseHint.erase()
      }
    }

    // Song Toggle Control ('S')
    val sPressedNow = isKeyPressed(Kc.VK_S)
    if (sPressedNow && !sKeyWasPressed) {
      songPlaying = !songPlaying
      if (songPlaying) {
        songStatusText.update("? Playing: Deewaana Deewaana")
        songStatusText.draw()
      } else {
        songStatusText.erase()
      }
    }
    sKeyWasPressed = sPressedNow

    // ACCELERATION & SPEED MECHANIC
    if (isKeyPressed(Kc.VK_SPACE)) {
      if (currentSpeed < maxSpeed) {
        currentSpeed += acceleration
      }
    } else {
      if (currentSpeed > 0) {
        currentSpeed -= friction
        if (currentSpeed < 0) currentSpeed = 0.0
      }
    }

    if (currentSpeed > 0) {
      updateDashes(currentSpeed * 2, roadCurveX)

      if (distanceMeters > 0) {
        distanceMeters -= currentSpeed * 0.3
        if (distanceMeters < 0) distanceMeters = 0.0
      }

      val progressRatio = (currentRoute.distance - distanceMeters) / currentRoute.distance
      val mapPinX = (canvasWidth / 2 - 130) + (progressRatio * 90.0)
      mapPlayerPin.setPosition(mapPinX, canvasHeight / 2 - 33)
    }

    // Hotel & Parking Box approach when distance finishes
    if (distanceMeters <= 0) {
      if (hotelY > -50) hotelY -= (currentSpeed + 2.0)
      drawHotel(roadCurveX)
    }

    // DESTINATION GREEN BOX PARKING CLAIM & REWARD ADDITION TO BALANCE
    val inGreenBox = carX >= roadCurveX + roadWidth / 2 - 90 && carX <= roadCurveX + roadWidth / 2 - 10 && carY >= hotelY - 40 && carY <= hotelY + 60
    if (distanceMeters <= 0 && inGreenBox && isKeyPressed(Kc.VK_P)) {
      routeCompleted = true
      playerWallet += currentRoute.reward
      walletText.update(s"BALANCE: ?$playerWallet")
      victoryText.update(s"PARKED! EARNED ?${currentRoute.reward}\nPress 'R' for Next Level")
      victoryText.draw()
    }

    updatePlayerPos()
  } else if (routeCompleted) {
    if (isKeyPressed(Kc.VK_R)) {
      currentRouteIdx = (currentRouteIdx + 1) % routesList.length
      routeCompleted = false
      distanceMeters = routesList(currentRouteIdx).distance
      currentSpeed = 0.0
      hotelY = canvasHeight / 2.0 + 400.0
      eraseHotel()
      victoryText.erase()
      carX = -26.0
      updatePlayerPos()
    }
  }
}