summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
authorMaurus Cuelenaere <mcuelenaere@gmail.com>2009-10-28 22:48:25 +0000
committerMaurus Cuelenaere <mcuelenaere@gmail.com>2009-10-28 22:48:25 +0000
commit4d668c7ae116935056a85ed31bff121d93db9319 (patch)
treec9142245ace61724e8205e683feb104f6750a274 /apps/plugins
parent21bca65d0498de4f6c3f45f768e564d733313124 (diff)
downloadrockbox-4d668c7ae116935056a85ed31bff121d93db9319.zip
rockbox-4d668c7ae116935056a85ed31bff121d93db9319.tar.gz
rockbox-4d668c7ae116935056a85ed31bff121d93db9319.tar.bz2
rockbox-4d668c7ae116935056a85ed31bff121d93db9319.tar.xz
Add new Lua touchscreen-only game: Boomshine
Flyspray: FS#10727 Author: myself git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23387 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/SOURCES3
-rw-r--r--apps/plugins/boomshine.lua278
2 files changed, 281 insertions, 0 deletions
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 82dd4b7..5dbdffd 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -173,6 +173,9 @@ md5sum.c
#if (PLUGIN_BUFFER_SIZE >= 0x80000)
lua.c
+#ifdef HAVE_TOUCHSCREEN
+boomshine.lua
+#endif
#endif
#ifdef USB_ENABLE_HID
diff --git a/apps/plugins/boomshine.lua b/apps/plugins/boomshine.lua
new file mode 100644
index 0000000..0067d28
--- /dev/null
+++ b/apps/plugins/boomshine.lua
@@ -0,0 +1,278 @@
+--[[
+ __________ __ ___.
+ 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 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 = 10,
+ 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 = rb.lcd_rgbpack(math.random(0,255), math.random(0,255), math.random(0,255)),
+ up_speed = math.random(-3, 3),
+ right_speed = math.random(-3, 3),
+ explosion_size = math.random((3*self.size)/2, (5*self.size)/2),
+ life_duration = math.random(rb.HZ, rb.HZ*5)
+ }
+
+ -- Make sure all balls move
+ if o.up_speed == 0 then o.up_speed = 1 end
+ if o.right_speed == 0 then o.right_speed = 1 end
+ end
+
+ setmetatable(o, self)
+ self.__index = self
+ return o
+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).
+ ]]--
+ rb.lcd_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
+
+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 start_round(level, goal, nrBalls, total)
+ local player_added, score, exit, nrExpandedBalls = false, 0, false, 0
+ local balls, explodedBalls = {}, {}
+
+ -- 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_STD, 0)
+ if (action == rb.actions.ACTION_TOUCHSCREEN) then
+ local _, x, y = rb.action_get_touchscreen_press()
+ if not player_added then
+ local player = Ball:new({
+ x = x,
+ y = y,
+ color = rb.lcd_rgbpack(255, 255, 255),
+ size = 10,
+ explosion_size = 30,
+ exploded = true,
+ death_time = rb.current_tick() + rb.HZ * 3
+ })
+ table.insert(explodedBalls, player)
+ player_added = true
+ end
+ elseif(action == rb.actions.ACTION_STD_CANCEL) then
+ exit = true
+ break
+ 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()
+ rb.lcd_set_foreground(rb.lcd_rgbpack(255, 255, 255))
+ 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
+
+ rb.lcd_update()
+
+ if rb.current_tick() < endtick then
+ rb.sleep(endtick - rb.current_tick())
+ else
+ rb.yield()
+ end
+ end
+
+ return exit, score
+end
+
+-- Helper function to display a message
+function display_message(message)
+ 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()
+ rb.lcd_set_foreground(rb.lcd_rgbpack(255, 255, 255))
+ 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
+
+rb.touchscreen_set_mode(rb.TOUCHSCREEN_POINT)
+rb.backlight_force_on()
+
+local idx, highscore = 1, 0
+while true do
+ local level = levels[idx]
+ local goal, nrBalls = level[1], level[2]
+
+ if level == nil then
+ break -- No more levels to play
+ end
+
+ display_message(string.format("Level %d: get %d out of %d balls", idx, goal, nrBalls))
+
+ local exit, score = start_round(idx, goal, nrBalls, highscore)
+ if exit then
+ break -- Exiting..
+ else
+ if score >= goal then
+ display_message("You won!")
+ idx = idx + 1
+ highscore = highscore + score
+ else
+ display_message("You lost!")
+ end
+ end
+end
+
+display_message(string.format("You made it till level %d with %d points!", idx, highscore))
+
+-- Restore user backlight settings
+rb.backlight_use_settings()
+