summaryrefslogtreecommitdiff
path: root/apps/bitmaps/native (follow)
Commit message (Expand)AuthorAge
* woops, fix the colour on the icons which are still blackJonathan Gordon2007-07-21
* Fix FS#7044 - if the color RGB(0,255,255) is in a bmp it will be replaced wit...Jonathan Gordon2007-07-14
* Since the greyscale remote deserved an own iconset the greyscale main screens...Marianne Arnold2007-04-17
* Customizable icons for all bitmap targets. (FS#7013)Jonathan Gordon2007-04-16
* Start of work on a port to the Archos 'AV300' (AV320/340/380) - a working UI ...Dave Chapman2007-01-14
* Restored broken bitmapLinus Nielsen Feltzing2007-01-10
* Removed executable flagLinus Nielsen Feltzing2007-01-09
* new usb logos for all screens, utilising lcd_bitmap_transparent() on colour d...Marianne Arnold2006-11-20
* Next step of Makefile tuning: * Use 'make' internal commands for printing mes...Jens Arnold2006-10-27
* Archos recorders, Ondios: aspect corrected rockbox logo, by Marianne Arnold.Jens Arnold2006-10-18
* Move USB logo to an external (native-depth) bitmap.Dave Chapman2006-10-06
* Add new build target for iriver H10 5/6Gb.Barry Wardell2006-08-19
* 'Repaired' Rockbox logo for archos, by Marianne Arnold. Spot the difference...Jens Arnold2006-07-27
* Properly sized rockbox logo for iPod mini, in greyscale.Jens Arnold2006-04-02
* first gigabeat commitMarcoen Hirschberg2006-02-24
* Patch #1435744 from Andrew Scott - initial iPod Mini supportDave Chapman2006-02-21
* First attempt at generating and using .h files from bitmaps using bmp2rb. Th...Dave Chapman2006-02-21
* 320x98 Rockbox logo for larger LCDs (currently only iPod 5G).Zakk Roberts2006-02-18
* Fix 4-grey rockbox logo.Jens Arnold2006-02-17
* Corrected version of 176x54 (Nano) colour logo (it wasn't being used until lo...Dave Chapman2006-02-02
* Added (somewhat blurry) boot logo for X5 and made sure it builds logos forDaniel Stenberg2006-01-22
* Initial attempt at using bmp2rb in the build system. Don't forget to re-run ...Dave Chapman2006-01-22
href='#n220'>220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
--[[
             __________               __   ___.
   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
                     \/            \/     \/    \/            \/
 $Id$

 Port of Chain Reaction (which is based on Boomshine) to Rockbox in Lua.
 See http://www.yvoschaap.com/chainrxn/ and http://www.k2xl.com/games/boomshine/

 Copyright (C) 2009 by Maurus Cuelenaere

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 KIND, either express or implied.

]]--

require "actions"

local CYCLETIME = rb.HZ / 50
local HAS_TOUCHSCREEN = rb.action_get_touchscreen_press ~= nil
local DEFAULT_BALL_SIZE = rb.LCD_HEIGHT > rb.LCD_WIDTH and rb.LCD_WIDTH  / 30
                                                       or  rb.LCD_HEIGHT / 30
local MAX_BALL_SPEED = DEFAULT_BALL_SIZE / 2
local DEFAULT_FOREGROUND_COLOR = rb.lcd_get_foreground ~= nil
                                                     and rb.lcd_get_foreground()
                                                     or  0

local levels = {
            --  {GOAL, TOTAL_BALLS},
                {1,   5},
                {2,  10},
                {4,  15},
                {6,  20},
                {10, 25},
                {15, 30},
                {18, 35},
                {22, 40},
                {30, 45},
                {37, 50},
                {48, 55},
                {55, 60}
           }

local Ball = {
                size = DEFAULT_BALL_SIZE,
                exploded = false,
                implosion = false
             }

function Ball:new(o)
    if o == nil then
        o = {
                x = math.random(self.size, rb.LCD_WIDTH - self.size),
                y = math.random(self.size, rb.LCD_HEIGHT - self.size),
                color = random_color(),
                up_speed = Ball:generateSpeed(),
                right_speed = Ball:generateSpeed(),
                explosion_size = math.random(2*self.size, 4*self.size),
                life_duration = math.random(rb.HZ, rb.HZ*5)
            }
    end

    setmetatable(o, self)
    self.__index = self
    return o
end

function Ball:generateSpeed()
    local speed = math.random(-MAX_BALL_SPEED, MAX_BALL_SPEED)
    if speed == 0 then
        speed = 1       -- Make sure all balls move
    end

    return speed
end

function Ball:draw()
    --[[
         I know these aren't circles, but as there's no current circle
         implementation in Rockbox, rectangles will just do fine (drawing
         circles from within Lua is far too slow).
    ]]--
    set_foreground(self.color)
    rb.lcd_fillrect(self.x, self.y, self.size, self.size)
end

function Ball:step()
    if self.exploded then
        if self.implosion and self.size > 0 then
            self.size = self.size - 2
            self.x = self.x + 1 -- We do this because we want to stay centered
            self.y = self.y + 1
        elseif self.size < self.explosion_size then
            self.size = self.size + 2
            self.x = self.x - 1 -- We do this for the same reasons as above
            self.y = self.y - 1
        end
        return
    end

    self.x = self.x + self.right_speed
    self.y = self.y + self.up_speed
    if (self.x + self.size) >= rb.LCD_WIDTH or self.x <= self.size then
        self.right_speed = self.right_speed * (-1)
    elseif (self.y + self.size) >= rb.LCD_HEIGHT or self.y <= self.size then
        self.up_speed = self.up_speed * (-1)
    end
end

function Ball:checkHit(other)
    local x_dist = math.abs(other.x - self.x)
    local y_dist = math.abs(other.y - self.y)
    local x_size = self.x > other.x and other.size or self.size
    local y_size = self.y > other.y and other.size or self.size

    if (x_dist <= x_size) and (y_dist <= y_size) then
        assert(not self.exploded)
        self.exploded = true
        self.death_time = rb.current_tick() + self.life_duration
        if not other.exploded then
            other.exploded = true
            other.death_time = rb.current_tick() + other.life_duration
        end
        return true
    end

    return false
end

local Cursor = {
                size = DEFAULT_BALL_SIZE*2,
                x = rb.LCD_WIDTH/2,
                y = rb.LCD_HEIGHT/2
             }

function Cursor:new()
    return self
end

function Cursor:do_action(action)
    if action == rb.actions.ACTION_TOUCHSCREEN and HAS_TOUCHSCREEN then
        _, self.x, self.y = rb.action_get_touchscreen_press()
        return true
    elseif action == rb.actions.ACTION_KBD_SELECT then
        return true
    elseif (action == rb.actions.ACTION_KBD_RIGHT) then
        self.x = self.x + self.size
    elseif (action == rb.actions.ACTION_KBD_LEFT) then
        self.x = self.x - self.size
    elseif (action == rb.actions.ACTION_KBD_UP) then
        self.y = self.y - self.size
    elseif (action == rb.actions.ACTION_KBD_DOWN) then
        self.y = self.y + self.size
    end

    if self.x > rb.LCD_WIDTH then
        self.x = 0
    elseif self.x < 0 then
        self.x = rb.LCD_WIDTH
    end

    if self.y > rb.LCD_HEIGHT then
        self.y = 0
    elseif self.y < 0 then
        self.y = rb.LCD_HEIGHT
    end

    return false
end

function Cursor:draw()
    set_foreground(DEFAULT_FOREGROUND_COLOR)

    rb.lcd_hline(self.x - self.size/2, self.x - self.size/4, self.y - self.size/2)
    rb.lcd_hline(self.x + self.size/4, self.x + self.size/2, self.y - self.size/2)
    rb.lcd_hline(self.x - self.size/2, self.x - self.size/4, self.y + self.size/2)
    rb.lcd_hline(self.x + self.size/4, self.x + self.size/2, self.y + self.size/2)
    rb.lcd_vline(self.x - self.size/2, self.y - self.size/2, self.y - self.size/4)
    rb.lcd_vline(self.x - self.size/2, self.y + self.size/4, self.y + self.size/2)
    rb.lcd_vline(self.x + self.size/2, self.y - self.size/2, self.y - self.size/4)
    rb.lcd_vline(self.x + self.size/2, self.y + self.size/4, self.y + self.size/2)

    rb.lcd_hline(self.x - self.size/4, self.x + self.size/4, self.y)
    rb.lcd_vline(self.x, self.y - self.size/4, self.y + self.size/4)
end

function draw_positioned_string(bottom, right, str)
    local _, w, h = rb.font_getstringsize(str, rb.FONT_UI)

    rb.lcd_putsxy((rb.LCD_WIDTH-w)*right, (rb.LCD_HEIGHT-h)*bottom, str)
end

function set_foreground(color)
    if rb.lcd_set_foreground ~= nil then
        rb.lcd_set_foreground(color)
    end
end

function random_color()
    if rb.lcd_rgbpack ~= nil then --color target
        return rb.lcd_rgbpack(math.random(1,255), math.random(1,255), math.random(1,255))
    end

    return math.random(1, rb.LCD_DEPTH)
end

function start_round(level, goal, nrBalls, total)
    local player_added, score, exit, nrExpandedBalls = false, 0, false, 0
    local balls, explodedBalls = {}, {}
    local cursor = Cursor:new()

    -- Initialize the balls
    for _=1,nrBalls do
        table.insert(balls, Ball:new())
    end

    -- Make sure there are no unwanted touchscreen presses
    rb.button_clear_queue()

    while true do
        local endtick = rb.current_tick() + CYCLETIME

        -- Check if the round is over
        if #explodedBalls == 0 and player_added then
            break
        end

        -- Check for actions
        local action = rb.get_action(rb.contexts.CONTEXT_KEYBOARD, 0)
        if(action == rb.actions.ACTION_KBD_ABORT) then
            exit = true
            break
        end
        if not player_added and cursor:do_action(action) then
            local player = Ball:new({
                                x = cursor.x,
                                y = cursor.y,
                                color = DEFAULT_FOREGROUND_COLOR,
                                size = 10,
                                explosion_size = 3*DEFAULT_BALL_SIZE,
                                exploded = true,
                                death_time = rb.current_tick() + rb.HZ * 3
                            })
            table.insert(explodedBalls, player)
            player_added = true
        end

        -- Check for hits
        for i, ball in ipairs(balls) do
            for _, explodedBall in ipairs(explodedBalls) do
                if ball:checkHit(explodedBall) then
                    score = score + 100*level
                    nrExpandedBalls = nrExpandedBalls + 1
                    table.insert(explodedBalls, ball)
                    table.remove(balls, i)
                    break
                end
            end
        end

        -- Check if we're dead yet
        for i, explodedBall in ipairs(explodedBalls) do
            if rb.current_tick() >= explodedBall.death_time then
                if explodedBall.size > 0 then
                    explodedBall.implosion = true -- We should be dying
                else
                    table.remove(explodedBalls, i) -- We're imploded!
                end
            end
        end

        -- Drawing phase
        rb.lcd_clear_display()

        set_foreground(DEFAULT_FOREGROUND_COLOR)
        draw_positioned_string(0, 0, string.format("%d balls expanded", nrExpandedBalls))
        draw_positioned_string(0, 1, string.format("Level %d", level))
        draw_positioned_string(1, 1, string.format("%d level points", score))
        draw_positioned_string(1, 0, string.format("%d total points", total+score))

        for _, ball in ipairs(balls) do
            ball:step()
            ball:draw()
        end

        for _, explodedBall in ipairs(explodedBalls) do
            explodedBall:step()
            explodedBall:draw()
        end

        if not HAS_TOUCHSCREEN and not player_added then
            cursor:draw()
        end

        -- Push framebuffer to the LCD
        rb.lcd_update()

        if rb.current_tick() < endtick then
            rb.sleep(endtick - rb.current_tick())
        else
            rb.yield()
        end
    end

    return exit, score, nrExpandedBalls
end

-- Helper function to display a message
function display_message(...)
    local message = string.format(...)
    local _, w, h = rb.font_getstringsize(message, rb.FONT_UI)
    local x, y = (rb.LCD_WIDTH - w) / 2, (rb.LCD_HEIGHT - h) / 2

    rb.lcd_clear_display()
    set_foreground(DEFAULT_FOREGROUND_COLOR)
    if w > rb.LCD_WIDTH then
        rb.lcd_puts_scroll(x/w, y/h, message)
    else
        rb.lcd_putsxy(x, y, message)
    end
    rb.lcd_update()

    rb.sleep(rb.HZ * 2)

    rb.lcd_stop_scroll() -- Stop our scrolling message
end

if HAS_TOUCHSCREEN then
    rb.touchscreen_set_mode(rb.TOUCHSCREEN_POINT)
end
rb.backlight_force_on()

local idx, highscore = 1, 0
while levels[idx] ~= nil do
    local goal, nrBalls = levels[idx][1], levels[idx][2]

    display_message("Level %d: get %d out of %d balls", idx, goal, nrBalls)

    local exit, score, nrExpandedBalls = start_round(idx, goal, nrBalls, highscore)
    if exit then
        break -- Exiting..
    else
        if nrExpandedBalls >= goal then
            display_message("You won!")
            idx = idx + 1
            highscore = highscore + score
        else
            display_message("You lost!")
        end
    end
end

if idx > #levels then
    display_message("You finished the game with %d points!", highscore)
else
    display_message("You made it till level %d with %d points!", idx, highscore)
end

-- Restore user backlight settings
rb.backlight_use_settings()