CCCPaste

Breakout

-- Breakout Game for SimpleOS
-- CC: Tweaked compatible

local w, h = term.getSize()
h = h - 1

-- Game state
local paddleX = math.floor(w/2) - 3
local paddleWidth = 7
local ballX, ballY = math.floor(w/2), h - 3
local ballDX, ballDY = 1, -1
local bricks = {}
local score = 0
local lives = 3
local gameOver = false
local gameWon = false
local speed = 0.05
local ballActive = false

-- Brick settings
local brickRows = 5
local brickCols = math.floor((w - 4) / 4)
local brickWidth = 3
local brickStartY = 3

-- Brick colors
local brickColors = {
    colors.red,
    colors.orange,
    colors.yellow,
    colors.green,
    colors.cyan
}

-- Initialize bricks
local function initBricks()
    bricks = {}
    for row = 1, brickRows do
        bricks[row] = {}
        for col = 1, brickCols do
            bricks[row][col] = true
        end
    end
end

-- Count remaining bricks
local function countBricks()
    local count = 0
    for row = 1, brickRows do
        for col = 1, brickCols do
            if bricks[row][col] then count = count + 1 end
        end
    end
    return count
end

-- Reset ball
local function resetBall()
    ballX = paddleX + paddleWidth / 2
    ballY = h - 3
    ballDX = (math.random(2) == 1) and 1 or -1
    ballDY = -1
    ballActive = false
end

-- Draw game
local function draw()
    term.setBackgroundColor(colors.black)
    term.clear()
    
    -- Draw bricks
    for row = 1, brickRows do
        for col = 1, brickCols do
            if bricks[row][col] then
                local x = 2 + (col - 1) * (brickWidth + 1)
                local y = brickStartY + row - 1
                term.setBackgroundColor(brickColors[row] or colors.white)
                term.setCursorPos(x, y)
                term.write(string.rep(" ", brickWidth))
            end
        end
    end
    
    -- Draw paddle
    term.setBackgroundColor(colors.white)
    term.setCursorPos(paddleX, h - 1)
    term.write(string.rep(" ", paddleWidth))
    
    -- Draw ball
    term.setBackgroundColor(colors.black)
    term.setTextColor(colors.yellow)
    term.setCursorPos(math.floor(ballX), math.floor(ballY))
    term.write("O")
    
    -- Draw info bar
    term.setBackgroundColor(colors.gray)
    for x = 1, w do
        term.setCursorPos(x, h + 1)
        term.write(" ")
    end
    term.setTextColor(colors.white)
    term.setCursorPos(2, h + 1)
    term.write("Score: " .. score .. "  Lives: " .. lives)
    term.setCursorPos(w - 7, h + 1)
    term.write("[Q]uit")
    
    -- Instructions
    if not ballActive and not gameOver and not gameWon then
        term.setBackgroundColor(colors.black)
        term.setTextColor(colors.gray)
        term.setCursorPos(math.floor(w/2) - 8, math.floor(h/2))
        term.write("Press SPACE to launch")
    end
end

-- Update ball
local function update()
    if not ballActive then
        ballX = paddleX + paddleWidth / 2
        return
    end
    
    ballX = ballX + ballDX
    ballY = ballY + ballDY
    
    -- Wall collisions
    if ballX <= 1 then
        ballX = 1
        ballDX = math.abs(ballDX)
    elseif ballX >= w then
        ballX = w
        ballDX = -math.abs(ballDX)
    end
    
    if ballY <= 1 then
        ballY = 1
        ballDY = math.abs(ballDY)
    end
    
    -- Paddle collision
    if ballY >= h - 2 and ballY <= h - 1 then
        if ballX >= paddleX and ballX <= paddleX + paddleWidth then
            ballDY = -math.abs(ballDY)
            -- Adjust angle based on hit position
            local hitPos = (ballX - paddleX) / paddleWidth
            ballDX = (hitPos - 0.5) * 3
            if math.abs(ballDX) < 0.5 then
                ballDX = (ballDX >= 0) and 0.5 or -0.5
            end
        end
    end
    
    -- Brick collisions
    for row = 1, brickRows do
        for col = 1, brickCols do
            if bricks[row][col] then
                local bx = 2 + (col - 1) * (brickWidth + 1)
                local by = brickStartY + row - 1
                
                if ballX >= bx and ballX < bx + brickWidth and
                   math.floor(ballY) == by then
                    bricks[row][col] = false
                    score = score + (brickRows - row + 1) * 10
                    ballDY = -ballDY
                    
                    if countBricks() == 0 then
                        gameWon = true
                    end
                end
            end
        end
    end
    
    -- Ball lost
    if ballY > h then
        lives = lives - 1
        if lives <= 0 then
            gameOver = true
        else
            resetBall()
        end
    end
end

-- Game over screen
local function showGameOver()
    term.setBackgroundColor(colors.black)
    term.setTextColor(colors.red)
    term.setCursorPos(math.floor(w/2) - 4, math.floor(h/2))
    term.write("GAME OVER")
    term.setTextColor(colors.white)
    term.setCursorPos(math.floor(w/2) - 6, math.floor(h/2) + 1)
    term.write("Score: " .. score)
    term.setCursorPos(math.floor(w/2) - 8, math.floor(h/2) + 3)
    term.write("ENTER=Restart Q=Quit")
end

-- Win screen
local function showWin()
    term.setBackgroundColor(colors.black)
    term.setTextColor(colors.lime)
    term.setCursorPos(math.floor(w/2) - 4, math.floor(h/2))
    term.write("YOU WIN!")
    term.setTextColor(colors.white)
    term.setCursorPos(math.floor(w/2) - 6, math.floor(h/2) + 1)
    term.write("Score: " .. score)
    term.setCursorPos(math.floor(w/2) - 8, math.floor(h/2) + 3)
    term.write("ENTER=Restart Q=Quit")
end

-- Main game loop
local function run()
    initBricks()
    resetBall()
    score = 0
    lives = 3
    gameOver = false
    gameWon = false
    paddleX = math.floor(w/2) - 3
    
    while true do
        draw()
        
        if gameOver then
            showGameOver()
        elseif gameWon then
            showWin()
        end
        
        local timer = os.startTimer(speed)
        
        while true do
            local event, p1 = os.pullEvent()
            
            if event == "key" then
                if gameOver or gameWon then
                    if p1 == keys.enter then
                        initBricks()
                        resetBall()
                        score = 0
                        lives = 3
                        gameOver = false
                        gameWon = false
                        paddleX = math.floor(w/2) - 3
                        break
                    elseif p1 == keys.q then
                        return
                    end
                else
                    if p1 == keys.left then
                        paddleX = math.max(1, paddleX - 2)
                    elseif p1 == keys.right then
                        paddleX = math.min(w - paddleWidth, paddleX + 2)
                    elseif p1 == keys.space then
                        ballActive = true
                    elseif p1 == keys.q then
                        return
                    end
                end
                if not (gameOver or gameWon) then
                    draw()
                end
            elseif event == "timer" and p1 == timer then
                break
            end
        end
        
        if not (gameOver or gameWon) then
            update()
        end
    end
end

run()