Code Sketch


Bixuu02 challenge of you
By: Mhalsakant School
Category: Programming
// ============================================================
// CRIME KILLER - BIXUU'S HARD EDITION (KOJO)
// ============================================================

cleari()
originBottomLeft()

// ============================================================
// GAME STATES & VARIABLES
// ============================================================
var screen = -1 
var selectedAvatar = 0 
var selectedGun = 0
var currentLevel = 1
var totalCoins = 100
var earnedCoinsThisLevel = 0

// Player Stats
var playerX = cwidth / 2.0
var playerY = 80.0
val playerRadius = 18.0
var playerHealth = 100.0
val maxPlayerHealth = 100.0

// Gun Stats (Hard Mode Balance)
var gunDamage = 15.0 
var gunFireRate = 0.08
var lastShotTime = 0.0

// Defense & VFX
var glooWallActive = false
var glooWallTimer = 0.0
var glooWallX = 0.0
var glooWallY = 0.0
var muzzleFlashTimer = 0.0

// Controls
var isW = false; var isS = false; var isA = false; var isD = false
var isFiring = false

// Avatar Unlocks
var isHarshalUnlocked = true
var isFireUnlocked = false
var isWaterUnlocked = false
var isBixuuUnlocked = false

val FIRE_PRICE = 200
val WATER_PRICE = 200
val BIXUU_PRICE = 500

var isFrozen = false
var freezeTimer = 0.0

// ============================================================
// GAME CLASSES: HARD BOSSES & BULLETS
// ============================================================
class HardBoss(var x: Double, var y: Double, var speed: Double) {
  var isAlive = true
  var health = 1500.0 
  val maxHealth = 1500.0
  var shootTimer = 0.0

  def update(pX: Double, pY: Double) {
    if (isAlive) {
      if (!isFrozen) {
        val angle = math.atan2(pY - y, pX - x)
        x += math.cos(angle) * speed
        y += math.sin(angle) * speed
      }
      shootTimer += 0.016
    }
  }

  def draw(canvas: CanvasDraw) {
    if (isAlive) {
      canvas.noStroke()
      canvas.fill(0, 0, 0, 90)
      canvas.ellipse(x, y - 22, 60, 14)

      canvas.fill(130, 0, 180); canvas.rect(x - 22, y - 25, 44, 50)
      canvas.fill(255, 215, 0); canvas.ellipse(x, y + 25, 32, 32)
      canvas.fill(255, 0, 0); canvas.ellipse(x - 7, y + 27, 8, 8); canvas.ellipse(x + 7, y + 27, 8, 8)

      canvas.fill(40, 40, 40); canvas.rect(x - 35, y + 48, 70, 8)
      canvas.fill(255, 30, 30); canvas.rect(x - 35, y + 48, 70 * (math.max(0.0, health) / maxHealth), 8)
    }
  }
}

class Bullet(var x: Double, var y: Double, var vx: Double, var vy: Double, var damage: Double, var isBossBullet: Boolean) {
  var active = true
  def update() {
    x += vx
    y += vy
    if (x < 0 || x > cwidth || y < 0 || y > cheight) active = false
  }
  def draw(canvas: CanvasDraw) {
    if (active) {
      canvas.noStroke()
      if (isBossBullet) {
        canvas.fill(255, 30, 30); canvas.ellipse(x, y, 10, 10)
      } else {
        canvas.fill(255, 255, 100); canvas.ellipse(x, y, 10, 10)
      }
    }
  }
}

val bosses = ArrayBuffer.empty[HardBoss]
val bullets = ArrayBuffer.empty[Bullet]

// ============================================================
// SETUP & LOGIC
// ============================================================
def startLevel(lvl: Int) {
  currentLevel = lvl
  playerX = cwidth / 2.0
  playerY = 80.0
  playerHealth = 100.0
  glooWallActive = false
  earnedCoinsThisLevel = 0

  bosses.clear()
  bullets.clear()

  val bossCount = lvl
  for (i <- 1 to bossCount) {
    val spawnX = 100.0 + (i * (cwidth - 200.0) / (bossCount + 1))
    val spawnY = cheight - 120.0
    val bossSpeed = if (selectedAvatar == 0) 1.8 else 1.2
    bosses.append(new HardBoss(spawnX, spawnY, bossSpeed))
  }
  screen = 2
}

def isColliding(x1: Double, y1: Double, r1: Double, x2: Double, y2: Double, r2: Double): Boolean = {
  val dx = x1 - x2; val dy = y1 - y2
  math.sqrt(dx * dx + dy * dy) < (r1 + r2)
}

def fireGun() {
  val currentTime = System.currentTimeMillis() / 1000.0
  if (currentTime - lastShotTime >= gunFireRate) {
    lastShotTime = currentTime
    muzzleFlashTimer = 0.05

    var targetX = playerX
    var targetY = playerY + 300.0

    val activeBosses = bosses.filter(_.isAlive)
    if (activeBosses.nonEmpty) {
      val nearest = activeBosses.minBy(b => math.hypot(b.x - playerX, b.y - playerY))
      targetX = nearest.x
      targetY = nearest.y
    }

    val angle = math.atan2(targetY - playerY, targetX - playerX)
    val speed = 20.0
    bullets.append(new Bullet(playerX, playerY, math.cos(angle) * speed, math.sin(angle) * speed, gunDamage, false))
  }
}

def useMedkit() {
  playerHealth = (playerHealth + 50.0).min(maxPlayerHealth)
}

def useSpecialPower() {
  if (selectedAvatar == 1) {
    bosses.foreach { b => if (b.isAlive) b.health -= 300.0 }
  } else if (selectedAvatar == 2) {
    isFrozen = true; freezeTimer = 4.0
  } else if (selectedAvatar == 3) {
    isFrozen = true; freezeTimer = 5.0
    bosses.foreach { b => if (b.isAlive) b.health -= 500.0 }
  }
}

def drawPlayerAvatar(canvas: CanvasDraw, x: Double, y: Double, avatarType: Int) {
  canvas.noStroke()
  canvas.fill(0, 0, 0, 90); canvas.ellipse(x, y - 20, 32, 10)

  avatarType match {
    case 0 => canvas.fill(40, 180, 70); canvas.rect(x - 12, y - 20, 24, 30); canvas.fill(255, 220, 180); canvas.ellipse(x, y + 15, 18, 18)
    case 1 => canvas.fill(230, 60, 10); canvas.rect(x - 12, y - 20, 24, 30); canvas.fill(255, 200, 100); canvas.ellipse(x, y + 15, 18, 18)
    case 2 => canvas.fill(0, 130, 240); canvas.rect(x - 12, y - 20, 24, 30); canvas.fill(200, 240, 255); canvas.ellipse(x, y + 15, 18, 18)
    case 3 => canvas.fill(140, 0, 210); canvas.rect(x - 15, y - 22, 30, 34); canvas.fill(255, 215, 0); canvas.ellipse(x, y + 16, 22, 22)
  }

  canvas.fill(30, 30, 30); canvas.rect(x + 8, y - 2, 18, 6)
}

// ============================================================
// GAME SCREENS
// ============================================================

def drawIntroLetterScreen(canvas: CanvasDraw) {
  canvas.background(12, 14, 20)
  canvas.fill(25, 28, 40); canvas.stroke(255, 50, 50); canvas.strokeWeight(3)
  canvas.rect(cwidth/2 - 270, cheight/2 - 180, 540, 360)

  canvas.fill(255, 50, 50); canvas.text("--- ULTRA HARD MODE: BOSS FIGHT ---", cwidth/2 - 180, cheight/2 + 140)
  canvas.fill(230, 230, 230)
  canvas.text("???????? ???????? ??? ?????? (Level 1 = 1 Boss, Level 5 = 5 Bosses)!", cwidth/2 - 240, cheight/2 + 80)
  canvas.text("??? ????? ??????????? ???? ??? ?? ????? ??? ??????? ????? ?????!", cwidth/2 - 240, cheight/2 + 40)
  canvas.text("?????????: W,A,S,D = ????? | F = ???? | M = ?????? | G = ???? ???", cwidth/2 - 230, cheight/2 + 10)

  canvas.fill(200, 30, 30); canvas.noStroke(); canvas.rect(cwidth/2 - 110, cheight/2 - 120, 220, 50)
  canvas.fill(255, 255, 255); canvas.text("ENTER HARD MODE", cwidth/2 - 80, cheight/2 - 90)
}

def drawAvatarSelectionScreen(canvas: CanvasDraw) {
  canvas.background(15, 18, 30)
  canvas.fill(255, 215, 0); canvas.text("SELECT AVATAR (AVOID HARD BOSS PENALTY)", cwidth/2 - 190, cheight - 50)
  canvas.fill(255, 255, 255); canvas.text(s"TOTAL COINS: $totalCoins", cwidth/2 - 65, cheight - 80)

  val boxW = 120.0; val boxH = 180.0; val startY = cheight/2 - 50.0

  canvas.fill(30, 50, 30); canvas.stroke(0, 255, 0); canvas.rect(cwidth/2 - 270, startY, boxW, boxH)
  drawPlayerAvatar(canvas, cwidth/2 - 210, startY + 110, 0)
  canvas.fill(255, 255, 255); canvas.text("HARSHAL", cwidth/2 - 245, startY + 40)

  canvas.fill(50, 20, 10); canvas.stroke(255, 100, 0); canvas.rect(cwidth/2 - 130, startY, boxW, boxH)
  drawPlayerAvatar(canvas, cwidth/2 - 70, startY + 110, 1)
  canvas.fill(255, 255, 255); canvas.text("FIRE", cwidth/2 - 90, startY + 40)
  canvas.text(if(isFireUnlocked) "SELECT" else "200 COINS", cwidth/2 - 110, startY + 15)

  canvas.fill(10, 30, 60); canvas.stroke(0, 150, 255); canvas.rect(cwidth/2 + 10, startY, boxW, boxH)
  drawPlayerAvatar(canvas, cwidth/2 + 70, startY + 110, 2)
  canvas.fill(255, 255, 255); canvas.text("WATER", cwidth/2 + 45, startY + 40)
  canvas.text(if(isWaterUnlocked) "SELECT" else "200 COINS", cwidth/2 + 25, startY + 15)

  canvas.fill(40, 10, 50); canvas.stroke(200, 0, 255); canvas.rect(cwidth/2 + 150, startY, boxW, boxH)
  drawPlayerAvatar(canvas, cwidth/2 + 210, startY + 110, 3)
  canvas.fill(255, 215, 0); canvas.text("BIXUU", cwidth/2 + 185, startY + 40)
  canvas.text(if(isBixuuUnlocked) "SELECT" else "500 COINS", cwidth/2 + 165, startY + 15)
}

def drawLandingScreen(canvas: CanvasDraw) {
  canvas.background(40, 20, 20)
  canvas.fill(255, 50, 50); canvas.text(s"WARNING: $currentLevel HARD BOSS(ES) AHEAD!", cwidth/2 - 140, cheight - 80)
  canvas.fill(220, 50, 0); canvas.rect(cwidth/2 - 150, cheight/2 - 40, 300, 60)
  canvas.fill(255, 255, 255); canvas.text("START BATTLE", cwidth/2 - 60, cheight/2 - 5)
}

def drawGameplayScreen(canvas: CanvasDraw) {
  canvas.background(20, 15, 25)

  if (glooWallActive) {
    canvas.fill(0, 200, 255, 180); canvas.rect(glooWallX - 10, glooWallY - 35, 20, 70)
    glooWallTimer -= 0.016
    if (glooWallTimer <= 0) glooWallActive = false
  }

  if (muzzleFlashTimer > 0) {
    canvas.fill(255, 240, 100); canvas.ellipse(playerX + 24, playerY - 2, 20, 20)
    muzzleFlashTimer -= 0.016
  }

  if (isFiring) fireGun()

  // Bullet Collision Logic
  bullets.foreach { b =>
    b.update(); b.draw(canvas)
    if (!b.isBossBullet && b.active) {
      bosses.foreach { boss =>
        if (boss.isAlive && isColliding(b.x, b.y, 8, boss.x, boss.y, 30)) {
          
          if (currentLevel == 5) {
            // Level 5 Super Power: One shot kills ALL bosses
            bosses.foreach { allB =>
              if (allB.isAlive) {
                allB.health = 0
                allB.isAlive = false
                totalCoins += 100
                earnedCoinsThisLevel += 100
              }
            }
            b.active = false
          } else {
            // Normal Damage for Levels 1 to 4
            boss.health -= b.damage
            b.active = false
            if (boss.health <= 0) {
              boss.isAlive = false
              totalCoins += 100
              earnedCoinsThisLevel += 100
            }
          }
        }
      }
    }

    if (b.isBossBullet && b.active) {
      if (glooWallActive && isColliding(b.x, b.y, 6, glooWallX, glooWallY, 35)) {
        b.active = false
      } else if (isColliding(b.x, b.y, 6, playerX, playerY, playerRadius)) {
        playerHealth -= 12.0
        b.active = false
      }
    }
  }

  // Boss Movement & Attack
  bosses.foreach { boss =>
    boss.update(playerX, playerY)
    boss.draw(canvas)

    if (boss.isAlive) {
      if (isColliding(boss.x, boss.y, 30, playerX, playerY, playerRadius)) {
        playerHealth -= 1.5
      }
      if (boss.shootTimer > 0.4) {
        boss.shootTimer = 0.0
        val angle = math.atan2(playerY - boss.y, playerX - boss.x)
        bullets.append(new Bullet(boss.x, boss.y, math.cos(angle) * 9, math.sin(angle) * 9, 12, true))
      }
    }
  }

  drawPlayerAvatar(canvas, playerX, playerY, selectedAvatar)

  // HUD
  canvas.fill(15, 15, 20); canvas.rect(0, cheight - 50, cwidth, 50)
  canvas.fill(255, 255, 255); canvas.text(s"LEVEL: $currentLevel/5", 15, cheight - 20)
  canvas.fill(255, 215, 0); canvas.text(s"COINS: $totalCoins", 130, cheight - 20)
  canvas.fill(255, 50, 50); canvas.text(s"BOSSES ALIVE: ${bosses.count(_.isAlive)}", 250, cheight - 20)

  canvas.fill(80, 0, 0); canvas.rect(420, cheight - 35, 120, 18)
  canvas.fill(0, 255, 0); canvas.rect(420, cheight - 35, 120 * (math.max(0.0, playerHealth) / maxPlayerHealth), 18)

  if (playerHealth <= 50.0 && playerHealth > 0) {
    canvas.fill(220, 30, 30); canvas.rect(560, cheight - 42, 160, 32)
    canvas.fill(255, 255, 255); canvas.text("? MEDKIT [M]", 590, cheight - 20)
  }

  if (playerHealth <= 0) {
    screen = 4
  } else if (bosses.forall(!_.isAlive)) {
    if (currentLevel == 5) screen = 6 else screen = 5
  }
}

def drawGameOverScreen(canvas: CanvasDraw) {
  canvas.background(40, 5, 5)
  canvas.fill(255, 50, 50); canvas.text("HARD BOSS DESTROYED YOU!", cwidth/2 - 110, cheight/2 + 40)
  canvas.fill(200, 30, 30); canvas.rect(cwidth/2 - 80, cheight/2 - 30, 160, 45)
  canvas.fill(255, 255, 255); canvas.text("RETRY LEVEL", cwidth/2 - 45, cheight/2 - 2)
}

def drawLevelClearScreen(canvas: CanvasDraw) {
  canvas.background(15, 25, 20)
  canvas.fill(25, 45, 35); canvas.stroke(255, 215, 0); canvas.strokeWeight(3)
  canvas.rect(cwidth/2 - 200, cheight/2 - 120, 400, 240)

  canvas.fill(255, 215, 0); canvas.text(s"LEVEL $currentLevel CLEAR!", cwidth/2 - 70, cheight/2 + 80)
  canvas.fill(255, 255, 255); canvas.text(s"EARNED $earnedCoinsThisLevel COINS FROM BOSSES!", cwidth/2 - 130, cheight/2 + 30)

  canvas.fill(0, 180, 80); canvas.noStroke()
  canvas.rect(cwidth/2 - 100, cheight/2 - 80, 200, 45)
  canvas.fill(255, 255, 255); canvas.text("NEXT HARD LEVEL", cwidth/2 - 75, cheight/2 - 52)
}

def drawFinalThankYouLetter(canvas: CanvasDraw) {
  canvas.background(20, 10, 35)
  canvas.fill(35, 20, 55); canvas.stroke(255, 215, 0); canvas.strokeWeight(4)
  canvas.rect(cwidth/2 - 280, cheight/2 - 160, 560, 320)

  canvas.fill(255, 215, 0); canvas.text("? BIXUU'S HARD GAME COMPLETED! ?", cwidth/2 - 165, cheight/2 + 110)
  
  canvas.fill(255, 255, 255)
  canvas.text("?????? ???????? ?? ????? ???? ??? ?? ???????? ????!", cwidth/2 - 170, cheight/2 + 60)
  canvas.text("?????? ??? ???? ??????????? ??? ?????? ??? ?????? ?????? ??? ???.", cwidth/2 - 245, cheight/2 + 25)

  canvas.fill(0, 255, 150)
  canvas.text("BIXUU GAME IS COMING SOON...", cwidth/2 - 120, cheight/2 - 25)

  canvas.fill(220, 50, 50); canvas.noStroke(); canvas.rect(cwidth/2 - 90, cheight/2 - 130, 180, 45)
  canvas.fill(255, 255, 255); canvas.text("RESTART GAME", cwidth/2 - 55, cheight/2 - 102)
}

// ============================================================
// LISTENERS & MAIN LOOP
// ============================================================

onKeyPress { key =>
  if (key == Kc.VK_W || key == 87) isW = true
  if (key == Kc.VK_S || key == 83) isS = true
  if (key == Kc.VK_A || key == 65) isA = true
  if (key == Kc.VK_D || key == 68) isD = true
  if (key == Kc.VK_F || key == 70) isFiring = true
  if (key == Kc.VK_M || key == 77) useMedkit()
  if (key == Kc.VK_G || key == 71) { glooWallActive = true; glooWallTimer = 3.5; glooWallX = playerX + 35; glooWallY = playerY }
  if (key == Kc.VK_E || key == 69) useSpecialPower()
}

onKeyRelease { key =>
  if (key == Kc.VK_W || key == 87) isW = false
  if (key == Kc.VK_S || key == 83) isS = false
  if (key == Kc.VK_A || key == 65) isA = false
  if (key == Kc.VK_D || key == 68) isD = false
  if (key == Kc.VK_F || key == 70) isFiring = false
}

onMouseClick { (x, y) =>
  if (screen == -1) {
    if (x >= cwidth/2 - 110 && x <= cwidth/2 + 110 && y >= cheight/2 - 120 && y <= cheight/2 - 70) screen = 0
  }
  else if (screen == 0) {
    val startY = cheight/2 - 50.0
    if (y >= startY && y <= startY + 180) {
      if (x >= cwidth/2 - 270 && x <= cwidth/2 - 150) { selectedAvatar = 0; startLevel(currentLevel) }
      else if (x >= cwidth/2 - 130 && x <= cwidth/2 - 10) {
        if (isFireUnlocked) { selectedAvatar = 1; startLevel(currentLevel) }
        else if (totalCoins >= FIRE_PRICE) { totalCoins -= FIRE_PRICE; isFireUnlocked = true; selectedAvatar = 1; startLevel(currentLevel) }
      }
      else if (x >= cwidth/2 + 10 && x <= cwidth/2 + 130) {
        if (isWaterUnlocked) { selectedAvatar = 2; startLevel(currentLevel) }
        else if (totalCoins >= WATER_PRICE) { totalCoins -= WATER_PRICE; isWaterUnlocked = true; selectedAvatar = 2; startLevel(currentLevel) }
      }
      else if (x >= cwidth/2 + 150 && x <= cwidth/2 + 270) {
        if (isBixuuUnlocked) { selectedAvatar = 3; startLevel(currentLevel) }
        else if (totalCoins >= BIXUU_PRICE) { totalCoins -= BIXUU_PRICE; isBixuuUnlocked = true; selectedAvatar = 3; startLevel(currentLevel) }
      }
    }
  }
  else if (screen == 2) {
    if (x >= cwidth/2 - 150 && x <= cwidth/2 + 150 && y >= cheight/2 - 40 && y <= cheight/2 + 20) screen = 3
  }
  else if (screen == 3) {
    if (playerHealth <= 50.0 && x >= 560 && x <= 720 && y >= cheight - 42) useMedkit()
  }
  else if (screen == 4) {
    if (x >= cwidth/2 - 80 && x <= cwidth/2 + 80 && y >= cheight/2 - 30 && y <= cheight/2 + 15) startLevel(currentLevel)
  }
  else if (screen == 5) {
    if (x >= cwidth/2 - 100 && x <= cwidth/2 + 100 && y >= cheight/2 - 80 && y <= cheight/2 - 35) {
      currentLevel += 1
      screen = 0
    }
  }
  else if (screen == 6) {
    if (x >= cwidth/2 - 90 && x <= cwidth/2 + 90 && y >= cheight/2 - 130 && y <= cheight/2 - 85) {
      currentLevel = 1
      screen = 0
    }
  }
}

animateWithCanvasDraw { canvas =>
  if (screen == -1) drawIntroLetterScreen(canvas)
  else if (screen == 0) drawAvatarSelectionScreen(canvas)
  else if (screen == 2) drawLandingScreen(canvas)
  else if (screen == 3) {
    val moveSpeed = 7.0
    if (isW && playerY < cheight - 70) playerY += moveSpeed
    if (isS && playerY > 40) playerY -= moveSpeed
    if (isA && playerX > 40) playerX -= moveSpeed
    if (isD && playerX < cwidth - 40) playerX += moveSpeed

    if (freezeTimer > 0) {
      freezeTimer -= 0.016
      if (freezeTimer <= 0) isFrozen = false
    }

    drawGameplayScreen(canvas)
  }
  else if (screen == 4) drawGameOverScreen(canvas)
  else if (screen == 5) drawLevelClearScreen(canvas)
  else if (screen == 6) drawFinalThankYouLetter(canvas)
}