summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/plugins/boomshine.lua698
1 files changed, 435 insertions, 263 deletions
diff --git a/apps/plugins/boomshine.lua b/apps/plugins/boomshine.lua
index c7ed6dfdf1..fcdf7c9a1c 100644
--- a/apps/plugins/boomshine.lua
+++ b/apps/plugins/boomshine.lua
@@ -11,7 +11,7 @@
See http://www.yvoschaap.com/chainrxn/ and http://www.k2xl.com/games/boomshine/
Copyright (C) 2009 by Maurus Cuelenaere
- Copyright (C) 2018 William Wilgus -- Added circles, logic rewrite, hard levels
+ Copyright (C) 2020 William Wilgus -- Added circles, logic rewrite, hard levels
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -23,7 +23,9 @@
]]--
require "actions"
---[[only save the actions we are using]]
+local DEBUG = false
+
+--[[only save the actions we are using]]----------------------------------------
pla = {}
for key, val in pairs(rb.actions) do
for _, v in ipairs({"PLA_", "TOUCHSCREEN", "_NONE"}) do
@@ -33,15 +35,32 @@ for key, val in pairs(rb.actions) do
end
end
rb.actions, rb.contexts = nil, nil
---------------------------------------
+--------------------------------------------------------------------------------
+
+
+--[[ Profiling ]]---------------------------------------------------------------
+local screen_redraw_count = 0
+local screen_redraw_duration = 0
+--------------------------------------------------------------------------------
+
+--[[ References ]]--------------------------------------------------------------
local _LCD = rb.lcd_framebuffer()
local rocklib_image = getmetatable(_LCD)
-local _ellipse = rocklib_image.ellipse
-local BSAND = 0x8
-local irand = math.random
-
+local _ellipse = rocklib_image.ellipse--
+local irand = math.random--
+local lcd_putsxy = rb.lcd_putsxy--
+local strfmt = string.format--
+local Cursor_Ref = nil
+local Ball_Ref = {}
+--------------------------------------------------------------------------------
+
+
+--[[ 'Constants' ]]-------------------------------------------------------------
+local SCORE_MULTIPLY = 10
+local BSAND = 0x8--
local HAS_TOUCHSCREEN = rb.action_get_touchscreen_press ~= nil
+local Empty_fn = function() end
local LCD_H, LCD_W = rb.LCD_HEIGHT, rb.LCD_WIDTH
local DEFAULT_BALL_SZ = LCD_H > LCD_W and LCD_W / 30 or LCD_H / 30
DEFAULT_BALL_SZ = DEFAULT_BALL_SZ - bit.band(DEFAULT_BALL_SZ, 1)
@@ -61,10 +80,16 @@ end
local FMT_EXPEND, FMT_LEVEL = "%d balls expended", "Level %d"
local FMT_TOTPTS, FMT_LVPTS = "%d total points", "%d level points"
+--------------------------------------------------------------------------------
+
+
+--[[ Other ]]-------------------------------------------------------------------
+local Player_Added
+local Action_Evt = pla.ACTION_NONE
local levels = {
-- {GOAL, TOTAL_BALLS},
- {1, 5},
+ {1, 5},
{2, 10},
{4, 15},
{6, 20},
@@ -85,7 +110,21 @@ local levels = {
{4, 5},
{5, 5}
}
+--------------------------------------------------------------------------------
+
+--[[ Event Callback ]]----------------------------------------------------------
+function action_event(action)
+ if action == pla.PLA_CANCEL then
+ Action_Evt = pla.PLA_EXIT
+ else
+ Action_Evt = action
+ end
+end
+--------------------------------------------------------------------------------
+
+
+--[[ Helper functions ]]--------------------------------------------------------
local function getstringsize(str)
local _, w, h = rb.font_getstringsize(str, rb.FONT_UI)
return w, h
@@ -97,90 +136,180 @@ local function set_foreground(color)
end
end
-local function random_color()
- if rb.lcd_rgbpack ~= nil then -- color target
- return rb.lcd_rgbpack(irand(1,255), irand(1,255), irand(1,255))
+local function calc_score(total, level, goal, expended)
+ local bonus = 0
+ local score = (expended * level) * SCORE_MULTIPLY
+ if expended < goal then
+ score = -(score + (level * SCORE_MULTIPLY))
+ elseif expended > goal then
+ bonus = (expended - goal) * (level * SCORE_MULTIPLY)
+ end
+ total = total + score + bonus
+ return total, score, bonus
+end
+
+local function wait_anykey(to_secs)
+ rb.sleep(rb.HZ / 2)
+ rb.button_clear_queue()
+ rb.button_get_w_tmo(rb.HZ * to_secs)
+end
+
+local function disp_msg(to, ...)
+ local message = strfmt(...)
+ local w, h = getstringsize(message)
+ local x, y = (LCD_W - w) / 2, (LCD_H - h) / 2
+
+ rb.lcd_clear_display()
+ set_foreground(DEFAULT_FG_CLR)
+
+ if w > LCD_W then
+ rb.lcd_puts_scroll(0, (y / h), message)
+ else
+ lcd_putsxy(x, y, message)
+ end
+
+ if to == -1 then
+ local msg = "Press button to exit"
+ w, h = getstringsize(msg)
+ x = (LCD_W - w) / 2
+ if x < 0 then
+ rb.lcd_puts_scroll(0, (y / h) + 1, msg)
+ else
+ lcd_putsxy(x, y + h, msg)
+ end
+ end
+ rb.lcd_update()
+
+ if to == -1 then
+ wait_anykey(60)
+ else
+ rb.sleep(to)
end
- local color = irand(1, rb.LCD_DEPTH)
- color = (rb.LCD_DEPTH == 2) and (3 - color) or color -- invert for 2-bit screens
+ rb.lcd_scroll_stop() -- Stop our scrolling message
+end
- return color
+local function test_speed()
+ local test_spd, redraw, dur = start_round(0, 0, 0, 0) -- test speed of target
+ --Logic speed, screen redraw, duration
+ if DEBUG == true then
+ disp_msg(rb.HZ * 5, "Spd: %d, Redraw: %d Dur: %d Ms", test_spd, redraw, dur)
+ end
+ if test_spd < 25 or redraw < 10 then
+ rb.splash(rb.HZ, "Slow Target..")
+
+ if test_spd < 10 then
+ MAX_BALL_SPEED = MAX_BALL_SPEED + MAX_BALL_SPEED
+ elseif test_spd < 15 then
+ MAX_BALL_SPEED = MAX_BALL_SPEED + MAX_BALL_SPEED / 2
+ elseif test_spd < 25 then
+ MAX_BALL_SPEED = MAX_BALL_SPEED + MAX_BALL_SPEED / 4
+ end
+ end
end
+--------------------------------------------------------------------------------
+
+--[[ Ball Functions ]]----------------------------------------------------------
local Ball = {
-- Ball defaults
sz = DEFAULT_BALL_SZ,
next_tick = 0,
- state = B_MOVE
+ state = B_MOVE,
}
-function Ball:new(o, level)
+function Ball:new(o, level, color)
+ local function random_color()
+ if color == nil then
+ if rb.lcd_rgbpack ~= nil then -- color target
+ return rb.lcd_rgbpack(irand(1,255), irand(1,255), irand(1,255))
+ end
+ color = irand(1, rb.LCD_DEPTH)
+ color = (rb.LCD_DEPTH == 2) and (3 - color) or color -- invert for 2-bit screens
+ end
+ return color
+ end
+
if o == nil then
level = level or 1
- local maxdelay = (level <= 5) and 10 or level
+ local maxdelay = (level <= 5) and 15 or level * 2
o = {
x = irand(1, LCD_W - self.sz),
y = irand(1, LCD_H - self.sz),
color = random_color(),
- xi = Ball:genSpeed(),
- yi = Ball:genSpeed(),
+ xi = self.genSpeed(), -- x increment; x + sz after explode
+ yi = self.genSpeed(), -- y increment; y + sz after explode
step_delay = irand(3, maxdelay / 2),
explosion_sz = irand(2 * self.sz, 4 * self.sz),
- life_ticks = irand(rb.HZ / level, rb.HZ * (maxdelay / 5))
+ life_ticks = irand(rb.HZ / level, rb.HZ * (maxdelay / 5)),
+ draw_fn = nil,
+ step_fn = self.step -- reference to current stepping function
}
end
- o.life_ticks = o.life_ticks + DEFAULT_BALL_SZ -- extra time for larger screens
+ local Ball = o or {} -- so we can use Ball. instead of self
+
+ -- these functions end up in a closure; faster to call, use more RAM
+ function Ball.draw()
+ _ellipse(_LCD, o.x, o.y, o.x + o.sz, o.y + o.sz, o.color, o.color, true)
+ end
+
+ function Ball.draw_exploded()
+ _ellipse(_LCD, o.x, o.y, o.xi, o.yi, o.color, nil, true)
+ end
+
+ if Ball.draw_fn == nil then
+ Ball.draw_fn = Ball.draw -- reference to current drawing function
+ end
+
setmetatable(o, self)
self.__index = self
return o
end
+-- these functions are shared by reference, slower to call, use less RAM
function Ball:genSpeed()
local speed = irand(-MAX_BALL_SPEED, MAX_BALL_SPEED)
return speed ~= 0 and speed or MAX_BALL_SPEED -- Make sure all balls move
end
-function Ball:draw()
- _ellipse(_LCD, self.x, self.y,
- self.x + self.sz, self.y + self.sz , self.color, self.color, true)
-end
-
function Ball:step(tick)
+ local function rndspeed(cur)
+ local speed = cur + irand(-2, 2)
+ if speed < -MAX_BALL_SPEED then
+ speed = -MAX_BALL_SPEED
+ elseif speed > MAX_BALL_SPEED then
+ speed = MAX_BALL_SPEED
+ elseif speed == 0 then
+ speed = cur
+ end
+ return speed
+ end
+
self.next_tick = tick + self.step_delay
self.x = self.x + self.xi
self.y = self.y + self.yi
+ local max_x = LCD_W - self.sz
+ local max_y = LCD_H - self.sz
- if (self.x <= 0 or self.x >= (LCD_W - self.sz)) then
+ if self.x <= 0 then
self.xi = -self.xi
- self.x = self.x + self.xi
+ self.x = 1
+ self.yi = rndspeed(self.yi)
+ elseif self.x >= max_x then
+ self.xi = -self.xi
+ self.x = max_x
+ self.yi = rndspeed(self.yi)
end
- if (self.y <= 0 or self.y >= (LCD_H - self.sz)) then
+ if self.y <= 0 then
self.yi = -self.yi
- self.y = self.y + self.yi
- end
-end
-
-function Ball:checkHit(other)
- if (other.xi >= self.x) and (other.yi >= self.y) and
- (self.x + self.sz >= other.x) and (self.y + self.sz >= other.y) then
- self.state = B_EXPLODE
- -- x/y increment no longer needed it is now impact region
- self.xi = self.x + self.sz
- self.yi = self.y + self.sz
-
- if other.state < B_EXPLODE then -- add duration to the ball that got hit
- other.next_tick = other.next_tick + self.life_ticks
- end
- return true
+ self.y = 1
+ self.xi = rndspeed(self.xi)
+ elseif self.y >= max_y then
+ self.yi = -self.yi
+ self.y = max_y
+ self.xi = rndspeed(self.xi)
end
-
- return false
-end
-
-function Ball:draw_exploded()
- _ellipse(_LCD, self.x, self.y, self.xi, self.yi, self.color, nil, true)
end
function Ball:step_exploded(tick)
@@ -188,20 +317,24 @@ function Ball:step_exploded(tick)
-- B_EXPLODE >> B_DIE >> BWAIT >> B_IMPLODE >> B_DEAD
if self.state == B_EXPLODE and self.sz < self.explosion_sz then
self.sz = self.sz + 2
- self.x = self.x - 1 -- We do this because we want to stay centered
+ self.x = self.x - 1 -- stay centered
self.y = self.y - 1
elseif self.state == B_DIE then
self.state = B_WAIT
self.next_tick = tick + self.life_ticks
return
- elseif self.state == B_IMPLODE and self.sz > 0 then
- self.sz = self.sz - 2
- self.x = self.x + 1 -- We do this because we want to stay centered
- self.y = self.y + 1
- elseif self.state <= B_IMPLODE then
- self.state = B_DEAD
- return
- elseif self.next_tick < tick then
+ elseif self.state == B_IMPLODE then
+ if self.sz > 0 then
+ self.sz = self.sz - 2
+ self.x = self.x + 1 -- stay centered
+ self.y = self.y + 1
+ else
+ self.state = B_DEAD
+ self.draw_fn = Empty_fn
+ self.step_fn = Empty_fn
+ return B_DEAD
+ end
+ elseif self.next_tick < tick then -- decay to next lower state
self.state = self.state - 1
return
end
@@ -211,59 +344,90 @@ function Ball:step_exploded(tick)
self.yi = self.y + self.sz
end
+function Ball:checkHit(other)
+ local x, y = self.x, self.y
+ if (other.xi >= x) and (other.yi >= y) then
+ local sz = self.sz
+ local xi, yi = x + sz, y + sz
+ if (xi >= other.x) and (yi >= other.y) then
+ -- update impact region
+ self.xi = xi
+ self.yi = yi
+ -- change to exploded state
+ self.state = B_EXPLODE
+ self.draw_fn = self.draw_exploded
+ self.step_fn = self.step_exploded
+
+ if other.state < B_EXPLODE then -- add duration to the ball that got hit
+ other.next_tick = other.next_tick + self.life_ticks
+ end
+ return true
+ end
+ end
+
+ return false
+end
+--------------------------------------------------------------------------------
+
+
+--[[ Cursor Functions ]]--------------------------------------------------------
local Cursor = {
sz = (DEFAULT_BALL_SZ * 2),
x = (LCD_W / 2),
y = (LCD_H / 2),
color = DEFAULT_FG_CLR
+ --image = nil
}
function Cursor:new()
if rb.LCD_DEPTH == 2 then -- invert for 2 - bit screens
self.color = 3 - DEFAULT_FG_CLR
end
- self:create_image(DEFAULT_BALL_SZ * 2)
- return self
-end
-
-function Cursor:create_image(sz)
- if not HAS_TOUCHSCREEN then
- sz = sz + 1
- local img = rb.new_image(sz, sz)
- local sz2 = (sz / 2) + 1
- local sz4 = (sz / 4)
-
- img:clear(0)
- img:line(1, 1, sz4 + 1, 1, 1)
- img:line(1, 1, 1, sz4 + 1, 1)
-
- img:line(1, sz, sz4 + 1, sz, 1)
- img:line(1, sz, 1, sz - sz4, 1)
-
- img:line(sz, sz, sz - sz4, sz, 1)
- img:line(sz, sz, sz, sz - sz4, 1)
-
- img:line(sz, 1, sz - sz4, 1, 1)
- img:line(sz, 1, sz, sz4 + 1, 1)
-
- -- crosshairs
- img:line(sz2 - sz4, sz2, sz2 + sz4, sz2, 1)
- img:line(sz2, sz2 - sz4, sz2, sz2 + sz4, 1)
- self.image = img
+ if not HAS_TOUCHSCREEN and not self.image then
+ local function create_image(sz)
+ sz = sz + 1
+ local img = rb.new_image(sz, sz)
+ local sz2 = (sz / 2) + 1
+ local sz4 = (sz / 4)
+
+ img:clear(0)
+ img:line(1, 1, sz4 + 1, 1, 1)
+ img:line(1, 1, 1, sz4 + 1, 1)
+
+ img:line(1, sz, sz4 + 1, sz, 1)
+ img:line(1, sz, 1, sz - sz4, 1)
+
+ img:line(sz, sz, sz - sz4, sz, 1)
+ img:line(sz, sz, sz, sz - sz4, 1)
+
+ img:line(sz, 1, sz - sz4, 1, 1)
+ img:line(sz, 1, sz, sz4 + 1, 1)
+
+ -- crosshairs
+ img:line(sz2 - sz4, sz2, sz2 + sz4, sz2, 1)
+ img:line(sz2, sz2 - sz4, sz2, sz2 + sz4, 1)
+ return img
+ end
+ self.image = create_image(DEFAULT_BALL_SZ * 2)
end
-end
-local function clamp_roll(iVal, iMin, iMax)
- if iVal < iMin then
- iVal = iMax
- elseif iVal > iMax then
- iVal = iMin
+ function Cursor.draw()
+ rocklib_image.copy(_LCD, self.image, self.x, self.y, _NIL, _NIL,
+ _NIL, _NIL, true, BSAND, self.color)
end
- return iVal
+ return self
end
function Cursor:do_action(action)
+ local function clamp_roll(iVal, iMin, iMax)
+ if iVal < iMin then
+ iVal = iMax
+ elseif iVal > iMax then
+ iVal = iMin
+ end
+ return iVal
+ end
local xi, yi = 0, 0
if HAS_TOUCHSCREEN and action == pla.ACTION_TOUCHSCREEN then
@@ -279,188 +443,225 @@ function Cursor:do_action(action)
yi = -self.sz
elseif (action == pla.PLA_DOWN or action == pla.PLA_DOWN_REPEAT) then
yi = self.sz
+ else
+ Action_Evt = pla.ACTION_NONE
+ return false
end
self.x = clamp_roll(self.x + xi, 1, LCD_W - self.sz)
self.y = clamp_roll(self.y + yi, 1, LCD_H - self.sz)
-
+ Action_Evt = pla.ACTION_NONE
return false
end
+--------------------------------------------------------------------------------
-function Cursor:draw()
- rocklib_image.copy(_LCD, self.image, self.x, self.y, _NIL, _NIL,
- _NIL, _NIL, true, BSAND, self.color)
-end
-local function calc_score(total, level, goal, expended)
- local score = (expended * level) * 100
- if expended < goal then
- score = -(score + (level * 100))
- end
- total = total + score
- return total
-end
-
-local function draw_pos_str(bottom, right, str)
- local w, h = getstringsize(str)
- local x = (right > 0) and ((LCD_W - w) * right - 1) or 1
- local y = (bottom > 0) and ((LCD_H - h) * bottom - 1) or 1
- rb.lcd_putsxy(x, y, str)
-end
-
-local function wait_anykey(to_secs)
- rb.sleep(rb.HZ / 2)
- rb.button_clear_queue()
- rb.button_get_w_tmo(rb.HZ * to_secs)
-end
+--[[ Game function ]]-----------------------------------------------------------
+function start_round(level, goal, nrBalls, total)
+ Player_Added = false
+ --[[ References ]]--
+ local current_tick = rb.current_tick
+ local lcd_update = rb.lcd_update
+ local lcd_clear_display = rb.lcd_clear_display
-local function start_round(level, goal, nrBalls, total)
- local player_added, score = false, 0
+ local test_spd = false
+ local is_exit = false;
+ local score = 0
local last_expend, nrBalls_expend = 0, 0
- local balls_exploded = 1 -- keep looping when player_added == false
- local action = 0
local Balls = {}
- local str_level = string.format(FMT_LEVEL, level) -- static
- local str_totpts = string.format(FMT_TOTPTS, total) -- static
+ local balls_exploded = 1 -- to keep looping when player_added == false
+
+ local tick_next = 0
+ local cursor = nil
+ local draw_cursor = Empty_fn
+ local refresh = rb.HZ/20
+
+ local function level_stats(level, total)
+ return strfmt(FMT_LEVEL, level), strfmt(FMT_TOTPTS, total)
+ end
+
+ local str_level, str_totpts = level_stats(level, total) -- static for lvl
local str_expend, str_lvlpts
- local tick, cursor
- local test_spd = false
local function update_stats()
-- we only create a new string when a hit is detected
- str_expend = string.format(FMT_EXPEND, nrBalls_expend)
- str_lvlpts = string.format(FMT_LVPTS, score)
+ str_expend = strfmt(FMT_EXPEND, nrBalls_expend)
+ str_lvlpts = strfmt(FMT_LVPTS, score)
end
- local function draw_stats()
+ function draw_stats()
+ local function draw_pos_str(bottom, right, str)
+ local w, h = getstringsize(str)
+ local x = (right > 0) and ((LCD_W - w) * right - 1) or 1
+ local y = (bottom > 0) and ((LCD_H - h) * bottom - 1) or 1
+ lcd_putsxy(x, y, str)
+ end
+ -- pos(B, R) [B/R]=0 => [y/x]=1, [B/R]=1 => [y/x]=lcd[H/W]-string[H/W]
draw_pos_str(0, 0, str_expend)
draw_pos_str(0, 1, str_level)
draw_pos_str(1, 1, str_lvlpts)
draw_pos_str(1, 0, str_totpts)
end
+ -- all Balls share same function, will by changed by player_add()
+ local checkhit_fn = Empty_fn
+
+ local function checkhit(Ball)
+ if Ball.state == B_MOVE then
+ for i = #Balls, 1, -1 do
+ if Balls[i].state < B_MOVE and
+ Ball:checkHit(Balls[i]) then -- exploded?
+ balls_exploded = balls_exploded + 1
+ nrBalls_expend = nrBalls_expend + 1
+ break
+ end
+ end
+ end
+ end
+
local function add_player()
-- cursor becomes exploded ball
local player = Ball:new({
- x = cursor.x,
- y = cursor.y,
+ x = cursor.x - cursor.sz,
+ y = cursor.y - cursor.sz,
color = cursor.color,
step_delay = 3,
explosion_sz = (3 * DEFAULT_BALL_SZ),
- life_ticks = (test_time == true) and (100) or
+ life_ticks = (test_spd) and (rb.HZ) or
irand(rb.HZ * 2, rb.HZ * DEFAULT_BALL_SZ),
sz = 10,
state = B_EXPLODE
})
- -- set x/y impact region
+ -- set x/y impact region -->[]
player.xi = player.x + player.sz
player.yi = player.y + player.sz
+ player.draw_fn = player.draw_exploded
+ player.step_fn = player.step_exploded
table.insert(Balls, player)
balls_exploded = 1
- player_added = true
+ Player_Added = true
cursor = nil
+ draw_cursor = Empty_fn
+ -- only need collision detection after player add
+ checkhit_fn = checkhit
end
- if level < 1 then
+ local function speedtest()
-- check speed of target
set_foreground(DEFAULT_BG_CLR) --hide text during test
- local bkcolor = (rb.LCD_DEPTH == 2) and (3) or 0
level = 1
- nrBalls = 20
- cursor = { x = LCD_W * 2, y = LCD_H * 2, color = bkcolor}
+ nrBalls = 100
+ cursor = {x = LCD_W * 2, y = LCD_H * 2, color = 0, sz = 1}
table.insert(Balls, Ball:new({
x = 1, y = 1, xi = 1, yi = 1,
- color = bkcolor, step_delay = 1,
+ color = 0, step_delay = 0,
explosion_sz = 0, life_ticks = 0,
- step = function() test_spd = test_spd + 1 end
+ draw_fn = Empty_fn,
+ step_fn = function() test_spd = test_spd + 1 end
})
)
- add_player()
test_spd = 0
+ add_player()
+ end
+
+ local function screen_redraw()
+ -- (draw_fn changes dynamically at runtime)
+ for i = 1, #Balls do
+ Balls[i].draw_fn()
+ end
+
+ draw_stats()
+ draw_cursor()
+
+ lcd_update()
+ lcd_clear_display()
+ end
+
+ local function game_loop()
+ local tick = current_tick()
+ for _, Ball in ipairs(Balls) do
+ if tick > Ball.next_tick then
+ -- (step_fn changes dynamically at runtime)
+ if Ball:step_fn(tick) == B_DEAD then
+ balls_exploded = balls_exploded - 1
+ else
+ checkhit_fn(Ball)
+ end
+ end
+ end
+ return tick
+ end
+
+ if level < 1 then
+ speedtest()
+ local bkcolor = (rb.LCD_DEPTH == 2) and (3) or 0
+ -- Initialize the balls
+ if DEBUG then bkcolor = nil end
+ for i=1, nrBalls do
+ table.insert(Balls, Ball:new(nil, level, bkcolor))
+ end
else
+ speedtest = nil
set_foreground(DEFAULT_FG_CLR) -- color for text
cursor = Cursor:new()
- end
-
- -- Initialize the balls
- for i=1, nrBalls do
- table.insert(Balls, Ball:new(nil, level))
+ if not HAS_TOUCHSCREEN then
+ draw_cursor = cursor.draw
+ end
+ -- Initialize the balls
+ for i=1, nrBalls do
+ table.insert(Balls, Ball:new(nil, level))
+ end
end
-- Make sure there are no unwanted touchscreen presses
rb.button_clear_queue()
+ Action_Evt = pla.ACTION_NONE
+
update_stats() -- load status strings
- -- Check if the round is over
- while balls_exploded > 0 do
- tick = rb.current_tick()
+ if rb.cpu_boost then rb.cpu_boost(true) end
+ collectgarbage("collect") -- run gc now to hopefully prevent interruption later
- if action ~= pla.ACTION_NONE and (action == pla.PLA_EXIT or
- action == pla.PLA_CANCEL) then
- action = pla.PLA_EXIT
+ local duration = current_tick()
+ -- Game loop >> Check if the round is over
+ while balls_exploded > 0 do
+ if Action_Evt == pla.PLA_EXIT then
+ is_exit = true
break
end
- rb.lcd_clear_display()
-
- if not player_added then
- if action ~= pla.ACTION_NONE and cursor:do_action(action) then
- add_player()
- elseif not HAS_TOUCHSCREEN then
- cursor:draw()
- end
- end
-
- for _, Ball in ipairs(Balls) do
- if Ball.state == B_MOVE then
- if tick > Ball.next_tick then
- Ball:step(tick)
- for i = #Balls, 1, -1 do
- if Balls[i].state < B_MOVE and
- Ball:checkHit(Balls[i]) then -- exploded?
- balls_exploded = balls_exploded + 1
- nrBalls_expend = nrBalls_expend + 1
- break
- end
- end
- end
- -- check if state changed draw ball if still moving
- if Ball.state == B_MOVE then
- Ball:draw()
- end
- elseif Ball.state ~= B_DEAD then
- if tick > Ball.next_tick then
- Ball:step_exploded(tick)
- end
- if Ball.state ~= B_DEAD then
- Ball:draw_exploded()
- else
- balls_exploded = balls_exploded - 1
+ if game_loop() > tick_next then
+ tick_next = current_tick() + refresh
+ if not Player_Added then
+ if Action_Evt ~= pla.ACTION_NONE and cursor:do_action(Action_Evt) then
+ add_player()
end
end
- end
- draw_stats() -- stats redrawn every frame
- rb.lcd_update() -- Push framebuffer to the LCD
+ if nrBalls_expend ~= last_expend then -- hit detected?
+ last_expend = nrBalls_expend
+ score = (nrBalls_expend * level) * SCORE_MULTIPLY
+ update_stats() -- only update stats when not current
+ if nrBalls_expend == nrBalls then break end -- round is over?
+ end
- if nrBalls_expend ~= last_expend then -- hit detected?
- last_expend = nrBalls_expend
- score = (nrBalls_expend * level) * 100
- update_stats() -- only update stats when not current
- if nrBalls_expend == nrBalls then break end -- round is over?
+ screen_redraw_count = screen_redraw_count + 1
+ screen_redraw()
end
-
rb.yield() -- yield to other tasks
-
- action = rb.get_plugin_action(0) -- Check for actions
end
- if test_spd and test_spd > 0 then return test_spd end
+ screen_redraw_duration = screen_redraw_duration + (rb.current_tick() - duration)
+ if rb.cpu_boost then rb.cpu_boost(false) end
+
+ if test_spd and test_spd > 0 then
+ return test_spd, screen_redraw_count, screen_redraw_duration *10 --ms
+ end
-- splash the final stats for a moment at end
- rb.lcd_clear_display()
+ lcd_clear_display()
for _, Ball in ipairs(Balls) do
-- move balls back to their initial exploded positions
if Ball.state == B_DEAD then
@@ -471,56 +672,22 @@ local function start_round(level, goal, nrBalls, total)
Ball:draw()
end
- if DEFAULT_BALL_SZ > 3 then
+ if DEFAULT_BALL_SZ > 3 then -- dither
_LCD:clear(nil, nil, nil, nil, nil, nil, 2, 2)
end
total = calc_score(total, level, goal, nrBalls_expend)
- str_totpts = string.format(FMT_TOTPTS, total)
+ str_level, str_totpts = level_stats(level, total)
draw_stats()
- rb.lcd_update()
+ lcd_update()
wait_anykey(2)
- return action == pla.PLA_EXIT, score, nrBalls_expend
+ return is_exit, score, nrBalls_expend
end
+--------------------------------------------------------------------------------
--- Helper function to display a message
-local function disp_msg(to, ...)
- local message = string.format(...)
- local w, h = getstringsize(message)
- local x, y = (LCD_W - w) / 2, (LCD_H - h) / 2
-
- rb.lcd_clear_display()
- set_foreground(DEFAULT_FG_CLR)
-
- if w > LCD_W then
- rb.lcd_puts_scroll(0, (y / h), message)
- else
- rb.lcd_putsxy(x, y, message)
- end
- if to == -1 then
- local msg = "Press button to exit"
- w, h = getstringsize(msg)
- x = (LCD_W - w) / 2
- if x < 0 then
- rb.lcd_puts_scroll(0, (y / h) + 1, msg)
- else
- rb.lcd_putsxy(x, y + h, msg)
- end
- end
- rb.lcd_update()
-
- if to == -1 then
- wait_anykey(60)
- else
- rb.sleep(to)
- end
-
- rb.lcd_scroll_stop() -- Stop our scrolling message
-end
-
---[[MAIN PROGRAM]]
+--[[MAIN PROGRAM]]--------------------------------------------------------------
do -- attempt to get stats to fit on screen
local function getwidth(str)
local w, _ = getstringsize(str)
@@ -530,7 +697,7 @@ do -- attempt to get stats to fit on screen
if (w0 + w1) > LCD_W then
FMT_EXPEND, FMT_LEVEL = "%d balls", "Lv %d"
end
- w0, w1 = getwidth(FMT_TOTPTS), getwidth(FMT_LVPTS)
+ w0, w1 = getwidth(FMT_TOTPTS), getwidth(FMT_LVPTS)
if (w0 + w1 + getwidth("0000000")) > LCD_W then
FMT_TOTPTS, FMT_LVPTS = "%d total", "%d lv"
end
@@ -542,23 +709,16 @@ end
rb.backlight_force_on()
-math.randomseed(os.time())
-
-local idx, highscore = 1, 0
+local eva = rockev.register("action", action_event, rb.HZ / 10)
-local test_spd = start_round(0, 0, 0, 0) -- test speed of target
+math.randomseed(os.time() or 1)
-if test_spd < 100 then
- rb.splash(100, "Slow Target..")
+local idx, highscore = 1, 0
- if test_spd < 25 then
- MAX_BALL_SPEED = MAX_BALL_SPEED + MAX_BALL_SPEED
- elseif test_spd < 50 then
- MAX_BALL_SPEED = MAX_BALL_SPEED + MAX_BALL_SPEED / 2
- elseif test_spd < 100 then
- MAX_BALL_SPEED = MAX_BALL_SPEED + MAX_BALL_SPEED / 4
- end
-end
+disp_msg(rb.HZ, "BoomShine")
+test_speed()
+rb.sleep(rb.HZ * 2)
+test_speed = nil
while levels[idx] ~= nil do
local goal, nrBalls = levels[idx][1], levels[idx][2]
@@ -567,18 +727,26 @@ while levels[idx] ~= nil do
disp_msg(rb.HZ * 2, "Level %d: get %d out of %d balls", idx, goal, nrBalls)
- local exit, score, nrBalls_expend = start_round(idx, goal, nrBalls, highscore)
+ local is_exit, score, nrBalls_expend = start_round(idx, goal, nrBalls, highscore)
+ if DEBUG == true then
+ local fps = screen_redraw_count * 100 / screen_redraw_duration
+ disp_msg(rb.HZ * 5, "Redraw: %d fps", fps)
+ end
- if exit then
+ if is_exit then
break -- Exiting..
else
- highscore = calc_score(highscore, idx, goal, nrBalls_expend)
+ highscore, score, bonus = calc_score(highscore, idx, goal, nrBalls_expend)
if nrBalls_expend >= goal then
- disp_msg(rb.HZ * 2, "You won!")
+ if bonus == 0 then
+ disp_msg(rb.HZ * 2, "You won!")
+ else
+ disp_msg(rb.HZ * 2, "You won BONUS!")
+ end
levels[idx] = nil
idx = idx + 1
else
- disp_msg(rb.HZ * 2, "You lost %d points!", score + (idx * 100))
+ disp_msg(rb.HZ * 2, "You lost %d points!", -score)
if highscore < 0 then break end
end
end
@@ -594,3 +762,7 @@ end
-- Restore user backlight settings
rb.backlight_use_settings()
+if rb.cpu_boost then rb.cpu_boost(false) end
+
+os.exit()
+--------------------------------------------------------------------------------