Code Sketch


Bixuu02
By: Mhalsakant School
Category: Programming
// ============================================================
// BIXUU HORROR - CREEPY ENTITY ATTACK GAME
// ============================================================

cleari()
originBottomLeft()

// ============================================================
// GAME STATES & VARIABLES
// ============================================================
var screen = 0 
// 0: Avatar Selection
// 1: Gameplay Screen
// 2: Game Over Screen
// 3: Mandir Prayer Screen
// 4: Final Letter Screen

var selectedAvatar = 0 
// 1: Fire Master
// 2: Water Master
// 3: Air Master

var currentLevel = 1
var playerX = cwidth / 2.0
var playerY = 80.0
var playerSpeed = 6.0
val playerRadius = 15.0

// Player Health Mechanics
var playerHealth = 100.0
val maxPlayerHealth = 100.0

// KEYBOARD STATES
var isW = false
var isS = false
var isA = false
var isD = false

var gameTimer = 20
var frameCounter = 0

// Boss Mechanics
var bossHealth = 100.0 // Evil Power Starts at 100%
val maxBossHealth = 100.0
var bossAttackTimer = 0.0
var isBossAttacking = false

// Black & White Strip
var stripY = cheight + 100.0
var stripSpeed = 3.0

// Power Mechanics
var isGhostFrozen = false
var freezeTimer = 0.0 
var isBoundaryCrossed = false

var fireEffectTimer = 0.0
var airEffectTimer = 0.0

var powerLevel = 100.0
val maxPower = 100.0

// Class for Creepy Boss Entity
class Ghost(var x: Double, var y: Double, var speed: Double, var defaultX: Double, var defaultY: Double) {
    var isRespawning = false
    var respawnTimer = 0.0

    def update(targetX: Double, targetY: Double) {
        if (isRespawning) {
            respawnTimer -= 0.016
            if (respawnTimer <= 0) {
                isRespawning = false
                x = defaultX
                y = defaultY
            }
        } else if (!isGhostFrozen && !isBoundaryCrossed && bossHealth > 0) {
            // Entity Moves Directly Towards Player
            if (x < targetX) x += speed
            if (x > targetX) x -= speed
            if (y < targetY) y += speed
            if (y > targetY) y -= speed
        }
    }

    // DRAW CREEPY ENTITY FACE
    def draw(canvas: CanvasDraw) {
        if (!isRespawning && bossHealth > 0) {
            canvas.noStroke()
            
            // Creepy Dark Aura/Body
            if (isGhostFrozen) canvas.fill(50, 150, 200, 240)
            else canvas.fill(120, 0, 10, 240)
            canvas.ellipse(x, y, 95, 110)

            // Outer Glowing Creepy Eyes (Glowing Yellow/Red)
            canvas.fill(255, 200, 0)
            canvas.ellipse(x - 20, y + 15, 22, 26)
            canvas.ellipse(x + 20, y + 15, 22, 26)

            // Inner Evil Pupils (Red/Black Slit)
            canvas.fill(150, 0, 0)
            canvas.ellipse(x - 20, y + 15, 8, 16)
            canvas.ellipse(x + 20, y + 15, 8, 16)

            // Creepy Bloody Mouth
            canvas.fill(20, 0, 0)
            canvas.ellipse(x, y - 18, 45, 25)

            // Sharp Fangs (Teeth)
            canvas.fill(255, 255, 255)
            // Top Fangs
            canvas.beginShape()
            canvas.vertex(x - 15, y - 8)
            canvas.vertex(x - 10, y - 8)
            canvas.vertex(x - 12.5, y - 20)
            canvas.endShape()

            canvas.beginShape()
            canvas.vertex(x + 10, y - 8)
            canvas.vertex(x + 15, y - 8)
            canvas.vertex(x + 12.5, y - 20)
            canvas.endShape()
        }
    }
}

val ghosts = ArrayBuffer.empty[Ghost]

// Small Soldiers Class
class Soldier(var x: Double, var y: Double, var speed: Double) {
    var isRespawning = false
    var respawnTimer = 0.0

    def update() {
        if (isRespawning) {
            respawnTimer -= 0.016
            if (respawnTimer <= 0) {
                isRespawning = false
                y = cheight + randomDouble(100.0)
                x = 80 + randomDouble(cwidth - 160)
            }
        } else if (!isGhostFrozen && !isBoundaryCrossed) {
            y -= speed
            if (y < -50) {
                y = cheight + randomDouble(100.0)
                x = 80 + randomDouble(cwidth - 160)
                speed = 4.0 + randomDouble(3.0)
            }
        }
    }
    
    def draw(canvas: CanvasDraw) {
        if (!isRespawning) {
            canvas.noStroke()
            if (isGhostFrozen) canvas.fill(0, 200, 255)
            else canvas.fill(255, 60, 0)
            
            canvas.ellipse(x, y, 35, 35)
            canvas.fill(255, 255, 0)
            canvas.ellipse(x - 7, y + 4, 6, 6)
            canvas.ellipse(x + 7, y + 4, 6, 6)
        }
    }
}

val soldiers = ArrayBuffer.empty[Soldier]
for (i <- 0 until 5) {
    soldiers.append(new Soldier(80 + randomDouble(cwidth - 160), cheight + i * 140, 4.0 + randomDouble(3.0)))
}

// Reset Level Parameters
def startLevel(lvl: Int) {
    currentLevel = lvl
    playerX = cwidth / 2.0
    playerY = 80.0
    playerHealth = 100.0
    gameTimer = 20
    frameCounter = 0
    bossHealth = 100.0
    bossAttackTimer = 0.0
    isBossAttacking = false
    stripY = cheight + 100.0

    isGhostFrozen = false
    freezeTimer = 0.0
    fireEffectTimer = 0.0
    airEffectTimer = 0.0
    isBoundaryCrossed = false
    powerLevel = maxPower

    isW = false
    isS = false
    isA = false
    isD = false

    var i = 0
    repeatFor(soldiers) { s =>
        s.x = 80 + randomDouble(cwidth - 160)
        s.y = cheight + i * 150
        s.speed = 4.0 + randomDouble(3.0)
        s.isRespawning = false
        i += 1
    }

    ghosts.clear()
    if (currentLevel >= 1) ghosts.append(new Ghost(cwidth / 2.0, cheight + 150.0, 1.8, cwidth / 2.0, cheight + 150.0))
    if (currentLevel >= 2) ghosts.append(new Ghost(100.0, cheight + 200.0, 1.6, 100.0, cheight + 200.0))
    if (currentLevel >= 3) ghosts.append(new Ghost(cwidth - 100.0, cheight + 250.0, 2.0, cwidth - 100.0, cheight + 250.0))

    screen = 1
}

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

// ============================================================
// PLAYER POWER ACTION
// ============================================================
def usePower() {
    if (powerLevel >= 25.0) {
        powerLevel -= 25.0

        if (gameTimer <= 10 && bossHealth > 0) {
            bossHealth = math.max(0.0, bossHealth - 20.0)
        }

        if (selectedAvatar == 1) {
            fireEffectTimer = 1.0
            repeatFor(soldiers) { s => s.isRespawning = true; s.respawnTimer = 2.0 }
            repeatFor(ghosts) { g => g.isRespawning = true; g.respawnTimer = 2.0 }
        } 
        else if (selectedAvatar == 2) {
            isGhostFrozen = true
            freezeTimer = 5.0
        } 
        else if (selectedAvatar == 3) {
            airEffectTimer = 1.0
            repeatFor(soldiers) { s => s.x = if (s.x < cwidth/2) 20.0 else cwidth-20.0; s.isRespawning = true; s.respawnTimer = 2.0 }
            repeatFor(ghosts) { g => g.x = if (g.x < cwidth/2) 20.0 else cwidth-20.0; g.isRespawning = true; g.respawnTimer = 2.0 }
        }
    }
}

// ============================================================
// DRAWING HELPERS
// ============================================================

def drawPlayerAvatar(canvas: CanvasDraw, x: Double, y: Double, avatarType: Int) {
    canvas.noStroke()
    if (avatarType == 1) {
        canvas.fill(255, 50, 0)
        canvas.rect(x - 12, y - 35, 24, 35)
        canvas.fill(255, 200, 100)
        canvas.ellipse(x, y + 10, 20, 20)
    } else if (avatarType == 2) {
        canvas.fill(0, 100, 255)
        canvas.rect(x - 12, y - 35, 24, 35)
        canvas.fill(200, 230, 255)
        canvas.ellipse(x, y + 10, 20, 20)
    } else {
        canvas.fill(200, 220, 255)
        canvas.rect(x - 12, y - 35, 24, 35)
        canvas.fill(255, 255, 255)
        canvas.ellipse(x, y + 10, 20, 20)
    }
}

def drawPowerEffects(canvas: CanvasDraw) {
    if (fireEffectTimer > 0) {
        canvas.stroke(255, 100, 0)
        canvas.strokeWeight(5)
        for (angle <- 0 until 360 by 30) {
            val rad = math.toRadians(angle.toDouble)
            val endX = playerX + math.cos(rad) * 400
            val endY = playerY + math.sin(rad) * 400
            canvas.line(playerX, playerY, endX, endY)
        }
    }

    if (airEffectTimer > 0) {
        canvas.stroke(200, 240, 255)
        canvas.strokeWeight(8)
        canvas.line(playerX, playerY, 0, playerY)
        canvas.line(playerX, playerY, cwidth, playerY)
    }
}

def drawBlackAndWhiteBar(canvas: CanvasDraw, x: Double, y: Double, w: Double, h: Double) {
    val cols = 20
    val boxWidth = w / cols
    for (i <- 0 until cols) {
        if (i % 2 == 0) canvas.fill(255, 255, 255)
        else canvas.fill(0, 0, 0)
        canvas.rect(x + (i * boxWidth), y, boxWidth, h)
    }
}

def updatePlayerMovement() {
    if (screen == 1) {
        if (isW && playerY < cheight - 120) playerY += playerSpeed
        if (isS && playerY > 60) playerY -= playerSpeed
        if (isA && playerX > 80) playerX -= playerSpeed
        if (isD && playerX < cwidth - 80) playerX += playerSpeed
    }
}

// ============================================================
// SCREENS
// ============================================================

// SCREEN 0: AVATAR SELECTION
def drawAvatarSelectionScreen(canvas: CanvasDraw) {
    canvas.background(10, 10, 20)

    canvas.fill(255, 255, 255)
    canvas.text("CHOOSE YOUR HERO AVATAR", cwidth/2 - 130, cheight - 60)

    val boxW = 150.0
    val boxH = 200.0
    val startX = cwidth/2 - 240.0
    val startY = cheight/2 - 60.0

    // Fire Master
    canvas.fill(50, 20, 10)
    canvas.stroke(255, 80, 0)
    canvas.strokeWeight(3)
    canvas.rect(startX, startY, boxW, boxH)
    drawPlayerAvatar(canvas, startX + boxW/2, startY + 120, 1)
    canvas.fill(255, 255, 255)
    canvas.text("FIRE MAN", startX + 35, startY + 55)
    canvas.text("(-20% Boss Power)", startX + 10, startY + 30)

    // Water Master
    canvas.fill(10, 30, 60)
    canvas.stroke(0, 120, 255)
    canvas.rect(startX + 180, startY, boxW, boxH)
    drawPlayerAvatar(canvas, startX + 180 + boxW/2, startY + 120, 2)
    canvas.fill(255, 255, 255)
    canvas.text("WATER MAN", startX + 180 + 30, startY + 55)
    canvas.text("(Freeze 5 Seconds)", startX + 180 + 10, startY + 30)

    // Air Master
    canvas.fill(30, 40, 50)
    canvas.stroke(200, 220, 255)
    canvas.rect(startX + 360, startY, boxW, boxH)
    drawPlayerAvatar(canvas, startX + 360 + boxW/2, startY + 120, 3)
    canvas.fill(255, 255, 255)
    canvas.text("AIR MAN", startX + 360 + 40, startY + 55)
    canvas.text("(-20% Boss Power)", startX + 360 + 10, startY + 30)

    canvas.text("CLICK ON ANY AVATAR TO START GAME", cwidth/2 - 160, 50)
}

// SCREEN 1: GAMEPLAY SCREEN
def drawGameplayScreen(canvas: CanvasDraw) {
    canvas.background(15, 5, 5)

    // Side Boundaries
    canvas.fill(40, 20, 20)
    canvas.noStroke()
    canvas.rect(0, 0, 60, cheight)
    canvas.rect(cwidth - 60, 0, 60, cheight)

    // BOSS EVIL POWER HEALTH BAR (AT TOP)
    if (gameTimer <= 10) {
        canvas.fill(40, 10, 10)
        canvas.rect(cwidth/2 - 150, cheight - 50, 300, 22)
        canvas.fill(255, 0, 0)
        canvas.rect(cwidth/2 - 150, cheight - 50, 300 * (bossHealth / maxBossHealth), 22)
        canvas.fill(255, 255, 255)
        canvas.text(s"CREEPY ENTITY POWER: ${bossHealth.toInt}%", cwidth/2 - 100, cheight - 33)
    }

    // FIRST 10 SECONDS: SOLDIERS ONLY
    if (gameTimer > 10) {
        repeatFor(soldiers) { s =>
            s.update()
            s.draw(canvas)

            if (!s.isRespawning && isColliding(playerX, playerY, playerRadius, s.x, s.y, 17.5)) {
                screen = 2 // Game Over
            }
        }
    } 
    // LAST 10 SECONDS: CREEPY ENTITY ENTERS & CHARGES AT PLAYER
    else if (gameTimer <= 10 && bossHealth > 0) {
        repeatFor(ghosts) { g =>
            g.update(playerX, playerY)
            g.draw(canvas)

            // Boss Attacks Player
            if (!isGhostFrozen) {
                bossAttackTimer += 0.016
                if (bossAttackTimer >= 2.5) {
                    bossAttackTimer = 0.0
                    isBossAttacking = true
                    playerHealth = math.max(0.0, playerHealth - 40.0)
                    if (playerHealth <= 0) screen = 2 // Game Over
                }
            }

            if (isBossAttacking) {
                canvas.stroke(255, 0, 0)
                canvas.strokeWeight(6)
                canvas.line(g.x, g.y, playerX, playerY)
            }

            if (!g.isRespawning && isColliding(playerX, playerY, playerRadius, g.x, g.y, 40.0)) {
                screen = 2 
            }
        }
    }

    // Draw Player
    drawPlayerAvatar(canvas, playerX, playerY, selectedAvatar)

    // Draw Effects
    drawPowerEffects(canvas)

    // STRIP & GREEN MANDIR BUTTON
    if ((gameTimer <= 10 && bossHealth <= 0) || gameTimer <= 0) {
        if (stripY > cheight - 160) {
            stripY -= stripSpeed
        }
        drawBlackAndWhiteBar(canvas, 60, stripY, cwidth - 120, 40)

        if (playerY >= stripY - 10) {
            isBoundaryCrossed = true
        }

        canvas.fill(0, 180, 80)
        canvas.rect(cwidth/2 - 120, stripY - 55, 240, 45)
        canvas.fill(255, 255, 255)
        canvas.text("ENTER MANDIR TO PRAY", cwidth/2 - 95, stripY - 27)
    }

    // PLAYER HEALTH BAR
    canvas.fill(50, 50, 50)
    canvas.rect(80, 20, 140, 18)
    canvas.fill(0, 255, 100)
    canvas.rect(80, 20, 140 * (playerHealth / maxPlayerHealth), 18)
    canvas.fill(255, 255, 255)
    canvas.text(s"YOUR HP: ${playerHealth.toInt}%", 80, 52)

    // PLAYER POWER BUTTON
    canvas.fill(200, 50, 0)
    canvas.rect(240, 15, 160, 32)
    canvas.fill(255, 255, 255)
    if (selectedAvatar == 1) canvas.text("FIRE BLAST (E)", 250, 36)
    else if (selectedAvatar == 2) canvas.text("ICE FREEZE (E)", 250, 36)
    else canvas.text("AIR RAYS (E)", 255, 36)

    // UI INFO
    canvas.fill(255, 255, 255)
    canvas.text(s"LEVEL: $currentLevel", 80, cheight - 30)
    canvas.text(s"TIME: $gameTimer S", cwidth - 180, cheight - 30)
}

// SCREEN 2: GAME OVER SCREEN
def drawGameOverScreen(canvas: CanvasDraw) {
    canvas.background(30, 0, 0)
    canvas.fill(255, 255, 255)
    canvas.text("THE END - GAME OVER!", cwidth/2 - 110, cheight/2 + 50)
    canvas.text("YOU WERE DESTROYED BY THE CREEPY ENTITY!", cwidth/2 - 170, cheight/2 + 10)

    canvas.fill(200, 30, 30)
    canvas.rect(cwidth/2 - 100, cheight/2 - 60, 200, 50)
    canvas.fill(255, 255, 255)
    canvas.text("RETRY LEVEL", cwidth/2 - 50, cheight/2 - 30)
}

// SCREEN 3: MANDIR PRAYER SCENE
def drawMandirScreen(canvas: CanvasDraw) {
    canvas.background(10, 10, 25)

    canvas.fill(180, 140, 40)
    canvas.rect(cwidth/2 - 110, cheight/2 - 10, 220, 160)
    canvas.fill(255, 215, 0)
    canvas.beginShape()
    canvas.vertex(cwidth/2 - 130, cheight/2 + 150)
    canvas.vertex(cwidth/2 + 130, cheight/2 + 150)
    canvas.vertex(cwidth/2, cheight/2 + 230)
    canvas.endShape()

    canvas.fill(255, 100, 0)
    canvas.ellipse(cwidth/2, cheight/2 + 50, 30, 30)

    drawPlayerAvatar(canvas, cwidth/2, cheight/2 - 20, selectedAvatar)

    canvas.fill(255, 255, 255)
    canvas.text("Thank you God, tune mala wachawla.", cwidth/2 - 150, cheight - 40)
    canvas.text("Ata mi ekdam powerful ahe. Thank you.", cwidth/2 - 160, cheight - 70)

    canvas.fill(0, 180, 80)
    canvas.rect(cwidth/2 - 160, cheight/2 - 150, 320, 55)
    canvas.fill(255, 255, 255)

    if (currentLevel == 1) canvas.text("PLAY LEVEL 2 GAME", cwidth/2 - 80, cheight/2 - 118)
    else if (currentLevel == 2) canvas.text("PLAY LEVEL 3 GAME", cwidth/2 - 80, cheight/2 - 118)
    else canvas.text("COMPLETE THIS MISSION", cwidth/2 - 110, cheight/2 - 118)
}

// SCREEN 4: FINAL THANK YOU LETTER
def drawFinalLetterScreen(canvas: CanvasDraw) {
    canvas.background(5, 5, 10)

    canvas.fill(25, 25, 35)
    canvas.stroke(0, 255, 150)
    canvas.strokeWeight(4)
    canvas.rect(cwidth/2 - 270, cheight/2 - 160, 540, 320)

    canvas.fill(255, 255, 255)
    canvas.text("ALL 3 LEVELS COMPLETED SUCCESSFULLY!", cwidth/2 - 180, cheight/2 + 110)
    canvas.text("THANK YOU FOR COMPLETE THIS MISSION.", cwidth/2 - 220, cheight/2 + 50)
    canvas.text("BIXUU IS NEXT GAME READY COMING SOON", cwidth/2 - 220, cheight/2 + 10)
    canvas.text("PLEASE WAIT...", cwidth/2 - 220, cheight/2 - 30)

    canvas.fill(0, 150, 200)
    canvas.noStroke()
    canvas.rect(cwidth/2 - 90, cheight/2 - 120, 180, 45)
    canvas.fill(255, 255, 255)
    canvas.text("PLAY FROM START", cwidth/2 - 75, cheight/2 - 92)
}

// ============================================================
// LISTENERS & MAIN LOOP
// ============================================================
onKeyPress { key =>
    if (key == Kc.VK_W || key == Kc.VK_UP || key == 87 || key == 38) isW = true
    if (key == Kc.VK_S || key == Kc.VK_DOWN || key == 83 || key == 40) isS = true
    if (key == Kc.VK_A || key == Kc.VK_LEFT || key == 65 || key == 37) isA = true
    if (key == Kc.VK_D || key == Kc.VK_RIGHT || key == 68 || key == 39) isD = true
    if (key == Kc.VK_E || key == 69) usePower()
}

onKeyRelease { key =>
    if (key == Kc.VK_W || key == Kc.VK_UP || key == 87 || key == 38) isW = false
    if (key == Kc.VK_S || key == Kc.VK_DOWN || key == 83 || key == 40) isS = false
    if (key == Kc.VK_A || key == Kc.VK_LEFT || key == 65 || key == 37) isA = false
    if (key == Kc.VK_D || key == Kc.VK_RIGHT || key == 68 || key == 39) isD = false
}

onMouseClick { (x, y) =>
    if (screen == 0) {
        val startX = cwidth/2 - 240.0
        val startY = cheight/2 - 60.0
        if (y >= startY && y <= startY + 200) {
            if (x >= startX && x <= startX + 150) { selectedAvatar = 1; startLevel(1) }
            else if (x >= startX + 180 && x <= startX + 330) { selectedAvatar = 2; startLevel(1) }
            else if (x >= startX + 360 && x <= startX + 510) { selectedAvatar = 3; startLevel(1) }
        }
    }
    else if (screen == 1) {
        if (x >= 240 && x <= 400 && y >= 15 && y <= 47) {
            usePower()
        }
        if (((gameTimer <= 10 && bossHealth <= 0) || gameTimer <= 0) && x >= cwidth/2 - 120 && x <= cwidth/2 + 120 && y >= stripY - 55 && y <= stripY - 10) {
            screen = 3
        }
    }
    else if (screen == 2) {
        if (x >= cwidth/2 - 100 && x <= cwidth/2 + 100 && y >= cheight/2 - 60 && y <= cheight/2 - 10) {
            startLevel(currentLevel)
        }
    }
    else if (screen == 3) {
        if (x >= cwidth/2 - 160 && x <= cwidth/2 + 160 && y >= cheight/2 - 150 && y <= cheight/2 - 95) {
            if (currentLevel == 1) startLevel(2)
            else if (currentLevel == 2) startLevel(3)
            else screen = 4
        }
    }
    else if (screen == 4) {
        if (x >= cwidth/2 - 90 && x <= cwidth/2 + 90 && y >= cheight/2 - 120 && y <= cheight/2 - 75) {
            screen = 0
        }
    }
}

animateWithCanvasDraw { canvas =>
    if (screen == 0) drawAvatarSelectionScreen(canvas)
    else if (screen == 1) {
        updatePlayerMovement()

        if (powerLevel < maxPower) powerLevel = math.min(maxPower, powerLevel + 0.4)

        if (freezeTimer > 0) {
            freezeTimer -= 0.016
            if (freezeTimer <= 0) isGhostFrozen = false
        }
        if (fireEffectTimer > 0) fireEffectTimer -= 0.05
        if (airEffectTimer > 0) airEffectTimer -= 0.05

        frameCounter += 1
        if (frameCounter >= 60) {
            frameCounter = 0
            if (gameTimer > 0) gameTimer -= 1
        }
        drawGameplayScreen(canvas)
    } 
    else if (screen == 2) drawGameOverScreen(canvas)
    else if (screen == 3) drawMandirScreen(canvas)
    else if (screen == 4) drawFinalLetterScreen(canvas)
}