Code Sketch


sniper_0718
By: Mhalsakant School
Category: Art
cleari()
// --- Game Dimensions ---
val canvasWidth = 800 
val canvasHeight = 600 
val roadWidth = 340 

// --- Game States ---
var gameState = "MENU" // "MENU", "PLAYING", "VICTORY"
var selectedLevel = 1
var enemiesRemaining = 0
var totalEnemiesInLevel = 0

// --- Background & Road (Static) ---
val bg = Picture.rectangle(canvasWidth, canvasHeight) 
bg.setFillColor(Color(10, 25, 10, 255))
bg.setPenColor(noColor)
bg.setPosition(-canvasWidth / 2, -canvasHeight / 2)

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

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

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

val dashCount = 8 
val dashHeight = 40 
val dashGap = 50 
val yellowDashesL = Array.fill(dashCount) { Picture.rectangle(4, dashHeight).withFillColor(Color(255, 204, 0, 255)).withPenColor(noColor) } 
val yellowDashesR = Array.fill(dashCount) { Picture.rectangle(4, dashHeight).withFillColor(Color(255, 204, 0, 255)).withPenColor(noColor) } 

for (i <- 0 until dashCount) {
  val yPos = (canvasHeight / 2) - (i * (dashHeight + dashGap))
  yellowDashesL(i).setPosition(-5, yPos)
  yellowDashesR(i).setPosition(1, yPos)
}

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

// --- Menu UI Boxes (Levels: 20, 40, 50, 100) ---
val menuBg = Picture.rectangle(canvasWidth, canvasHeight).withFillColor(Color(15, 20, 30, 255)).withPenColor(noColor)
menuBg.setPosition(-canvasWidth / 2, -canvasHeight / 2)

val titleText = Picture.text("SELECT LEVEL - AK-47 SHOOTER", 24).withFillColor(white)
titleText.setPosition(-180, 180)

val box1 = Picture.rectangle(160, 80).withFillColor(Color(40, 100, 40, 255)).withPenColor(white)
val text1 = Picture.text("1. 20", 18).withFillColor(white)
box1.setPosition(-220, 50); text1.setPosition(-190, 85)

val box2 = Picture.rectangle(160, 80).withFillColor(Color(40, 60, 120, 255)).withPenColor(white)
val text2 = Picture.text("2. 40", 18).withFillColor(white)
box2.setPosition(-30, 50); text2.setPosition(0, 85)

val box3 = Picture.rectangle(160, 80).withFillColor(Color(120, 100, 40, 255)).withPenColor(white)
val text3 = Picture.text("3. 50", 18).withFillColor(white)
box3.setPosition(-220, -60); text3.setPosition(-190, -25)

val box4 = Picture.rectangle(160, 80).withFillColor(Color(120, 40, 40, 255)).withPenColor(white)
val text4 = Picture.text("4. 100", 18).withFillColor(white)
box4.setPosition(-30, -60); text4.setPosition(0, -25)

// --- Player with AK-47 ---
val charHead = Picture.circle(8).withFillColor(Color(255, 220, 177, 255)).withPenColor(noColor)
val charBody = Picture.rectangle(16, 24).withFillColor(Color(0, 122, 255, 255)).withPenColor(noColor)
val charArmL = Picture.rectangle(5, 18).withFillColor(Color(40, 40, 40, 255)).withPenColor(noColor)
val akGun = Picture.rectangle(26, 6).withFillColor(Color(60, 40, 20, 255)).withPenColor(black)
val charLegL = Picture.rectangle(6, 20).withFillColor(Color(20, 20, 20, 255)).withPenColor(noColor)
val charLegR = Picture.rectangle(6, 20).withFillColor(Color(20, 20, 20, 255)).withPenColor(noColor)

var charX = 0.0
var charY = -200.0
val moveSpeed = 5.0

def updateCharPos(): Unit = {
  charHead.setPosition(charX, charY + 26)
  charBody.setPosition(charX - 8, charY + 2)
  charArmL.setPosition(charX - 15, charY + 2)
  akGun.setPosition(charX + 3, charY + 8) 
  charLegL.setPosition(charX - 6, charY - 20)
  charLegR.setPosition(charX + 2, charY - 20)
}

// --- Enemies Group ---
val enemyCount = 4 
val enemyHeads = Array.fill(enemyCount)(Picture.circle(8).withFillColor(Color(255, 80, 80, 255)).withPenColor(noColor))
val enemyBodies = Array.fill(enemyCount)(Picture.rectangle(16, 24).withFillColor(Color(180, 20, 20, 255)).withPenColor(noColor))

var enemyX = Array.fill(enemyCount)(0.0)
var enemyY = Array.fill(enemyCount)(0.0)
var enemyActive = Array.fill(enemyCount)(false)
val enemySpeed = 2.2

def resetSingleEnemy(i: Int): Unit = {
  enemyX(i) = random(-80, 80) 
  enemyY(i) = random(300, 450)
  enemyActive(i) = true
}

def initAllEnemies(): Unit = {
  for (i <- 0 until enemyCount) {
    resetSingleEnemy(i)
    enemyHeads(i).draw()
    enemyBodies(i).draw()
  }
}

// --- 5 Bullets Setup ---
val maxBullets = 5
val bulletPics = Array.fill(maxBullets)(Picture.rectangle(4, 12).withFillColor(yellow).withPenColor(noColor))
val bulletX = Array.fill(maxBullets)(0.0)
val bulletY = Array.fill(maxBullets)(-1000.0)
val bulletActive = Array.fill(maxBullets)(false)
val bulletSpeed = 22.0

for (bp <- bulletPics) {
  bp.setPosition(-1000, -1000)
  bp.draw()
}

// HUD Texts
val hudText = Picture.text("", 15).withFillColor(white)
hudText.setPosition(-canvasWidth / 2 + 20, canvasHeight / 2 - 40)

val victoryText = Picture.text("VICTORY! ALL LEVELS COMPLETED!", 24).withFillColor(Color(0, 255, 100, 255))
victoryText.setPosition(-220, 0)

def drawMenu(): Unit = {
  menuBg.draw()
  titleText.draw()
  box1.draw(); text1.draw()
  box2.draw(); text2.draw()
  box3.draw(); text3.draw()
  box4.draw(); text4.draw()
}

def eraseMenu(): Unit = {
  menuBg.erase()
  titleText.erase()
  box1.erase(); text1.erase()
  box2.erase(); text2.erase()
  box3.erase(); text3.erase()
  box4.erase(); text4.erase()
}

def startLevel(levelNum: Int): Unit = {
  selectedLevel = levelNum
  totalEnemiesInLevel = levelNum match {
    case 1 => 20
    case 2 => 40
    case 3 => 50
    case 4 => 100
    case _ => 20
  }
  enemiesRemaining = totalEnemiesInLevel
  
  eraseMenu()
  
  bg.draw()
  road.draw()
  lineLeft.draw()
  lineRight.draw()
  for (d <- yellowDashesL) d.draw()
  for (d <- yellowDashesR) d.draw()
  nightOverlay.draw()
  
  charHead.draw(); charBody.draw(); charArmL.draw(); akGun.draw(); charLegL.draw(); charLegR.draw()
  
  initAllEnemies()
  
  hudText.draw()
  hudText.update(s"Level $selectedLevel | Enemies Left: $enemiesRemaining | Arrows/WASD: Move | Z: Shoot")
  
  updateCharPos()
  gameState = "PLAYING"
}

// Mouse Click for Level Selection
onMouseClick { (x, y) =>
  if (gameState == "MENU") {
    if (x >= -220 && x <= -60 && y >= 50 && y <= 130) startLevel(1)
    else if (x >= -30 && x <= 130 && y >= 50 && y <= 130) startLevel(2)
    else if (x >= -220 && x <= -60 && y >= -60 && y <= 20) startLevel(3)
    else if (x >= -30 && x <= 130 && y >= -60 && y <= 20) startLevel(4)
  }
}

var keyZReleased = true
drawMenu()

// --- Game Loop ---
animate {
  if (gameState == "PLAYING") {
    // Movement Controls (Both Arrow Keys and WASD supported)
    if (isKeyPressed(Kc.VK_A) || isKeyPressed(Kc.VK_LEFT)) charX -= moveSpeed
    if (isKeyPressed(Kc.VK_D) || isKeyPressed(Kc.VK_RIGHT)) charX += moveSpeed
    if (isKeyPressed(Kc.VK_W) || isKeyPressed(Kc.VK_UP)) charY += moveSpeed
    if (isKeyPressed(Kc.VK_S) || isKeyPressed(Kc.VK_DOWN)) charY -= moveSpeed

    // Road Boundaries
    if (charX < -150) charX = -150
    if (charX > 150) charX = 150
    if (charY < -250) charY = -250
    if (charY > 100) charY = 100

    updateCharPos()

    // Move Enemies Downwards
    for (i <- 0 until enemyCount) {
      if (enemyActive(i)) {
        enemyY(i) -= enemySpeed
        
        if (enemyY(i) < charY - 50) {
          enemyY(i) = random(300, 450)
          enemyX(i) = random(-80, 80)
        }
        
        enemyHeads(i).setPosition(enemyX(i), enemyY(i) + 26)
        enemyBodies(i).setPosition(enemyX(i) - 8, enemyY(i) + 2)
      }
    }

    // Shoot with Z Key (5 Bullets burst)
    val zCurrentlyPressed = isKeyPressed(Kc.VK_Z)
    if (zCurrentlyPressed && keyZReleased) {
      val offsets = Array(-30.0, -15.0, 0.0, 15.0, 30.0)
      for (j <- 0 until maxBullets) {
        if (!bulletActive(j)) {
          bulletX(j) = charX + offsets(j)
          bulletY(j) = charY + 30
          bulletActive(j) = true
        }
      }
      keyZReleased = false
    }
    if (!zCurrentlyPressed) {
      keyZReleased = true
    }

    // Bullets Movement & Collision Logic
    for (j <- 0 until maxBullets) {
      if (bulletActive(j)) {
        bulletY(j) += bulletSpeed
        bulletPics(j).setPosition(bulletX(j), bulletY(j))

        // Check Collision with Enemies
        for (i <- 0 until enemyCount) {
          if (enemyActive(i) && Math.abs(bulletX(j) - enemyX(i)) < 25 && Math.abs(bulletY(j) - enemyY(i)) < 25) {
            enemiesRemaining -= 1
            hudText.update(s"Level $selectedLevel | Enemies Left: $enemiesRemaining | Arrows/WASD: Move | Z: Shoot")
            
            bulletActive(j) = false
            bulletY(j) = -1000.0
            bulletPics(j).setPosition(bulletX(j), bulletY(j))
            
            if (enemiesRemaining > 0) {
              resetSingleEnemy(i)
            } else {
              if (selectedLevel < 4) {
                startLevel(selectedLevel + 1)
              } else {
                gameState = "VICTORY"
                hudText.erase()
                for (bp <- bulletPics) bp.setPosition(-1000, -1000)
                for (e <- enemyHeads) e.erase()
                for (e <- enemyBodies) e.erase()
                victoryText.draw()
              }
            }
          }
        }

        // Hide bullet if it goes off screen
        if (bulletY(j) > canvasHeight / 2) {
          bulletActive(j) = false
          bulletY(j) = -1000.0
          bulletPics(j).setPosition(bulletX(j), bulletY(j))
        }
      } else {
        bulletPics(j).setPosition(-1000, -1000)
      }
    }
  }
}