summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <me.theuser@yahoo.com>2018-10-17 18:35:02 -0400
committerWilliam Wilgus <me.theuser@yahoo.com>2018-11-05 00:30:46 -0500
commit85079483c5595c2a04774ae9fbf529719eaec9fa (patch)
treebc57bf5d59bff3c03afa4b06949b39ed48f90e34
parentd9f9def505b367b9361ba96d33e6edf1687716fb (diff)
downloadrockbox-85079483c5595c2a04774ae9fbf529719eaec9fa.tar.gz
rockbox-85079483c5595c2a04774ae9fbf529719eaec9fa.zip
Lua Boomshine Update with circles + levels
added a few more levels and increased difficulty based on level Rliimage allows us to use circles in boomshine Optimized some of the code to exceed the speed of the original Change-Id: I68eb74f8b68fcdc8a9d022cd172374744425f15d
-rw-r--r--apps/plugins/boomshine.lua278
1 files changed, 193 insertions, 85 deletions
diff --git a/apps/plugins/boomshine.lua b/apps/plugins/boomshine.lua
index 1729cbbf8c..63ac6b11fa 100644
--- a/apps/plugins/boomshine.lua
+++ b/apps/plugins/boomshine.lua
@@ -11,6 +11,7 @@
11 See http://www.yvoschaap.com/chainrxn/ and http://www.k2xl.com/games/boomshine/ 11 See http://www.yvoschaap.com/chainrxn/ and http://www.k2xl.com/games/boomshine/
12 12
13 Copyright (C) 2009 by Maurus Cuelenaere 13 Copyright (C) 2009 by Maurus Cuelenaere
14 Copyright (C) 2018 William Wilgus -- Added circles, blit cursor, hard levels
14 15
15 This program is free software; you can redistribute it and/or 16 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License 17 modify it under the terms of the GNU General Public License
@@ -21,14 +22,32 @@
21 KIND, either express or implied. 22 KIND, either express or implied.
22 23
23]]-- 24]]--
24
25require "actions" 25require "actions"
26-- [[only save the actions we are using]]
27actions_pla = {}
28for key, val in pairs(rb.actions) do
29 for _, v in ipairs({"PLA_", "TOUCHSCREEN"}) do
30 if string.find (key, v) then
31 actions_pla[key] = val
32 break
33 end
34 end
35end
36rb.actions = nil
37rb.contexts = nil
38-------------------------------------
39
40local _LCD = rb.lcd_framebuffer()
41local BSAND = 0x8
42local rocklib_image = getmetatable(rb.lcd_framebuffer())
43local _ellipse = rocklib_image.ellipse
26 44
27local CYCLETIME = rb.HZ / 50 45local CYCLETIME = rb.HZ / 50
28local HAS_TOUCHSCREEN = rb.action_get_touchscreen_press ~= nil 46local HAS_TOUCHSCREEN = rb.action_get_touchscreen_press ~= nil
29local DEFAULT_BALL_SIZE = rb.LCD_HEIGHT > rb.LCD_WIDTH and rb.LCD_WIDTH / 30 47local DEFAULT_BALL_SIZE = rb.LCD_HEIGHT > rb.LCD_WIDTH and rb.LCD_WIDTH / 30
30 or rb.LCD_HEIGHT / 30 48 or rb.LCD_HEIGHT / 30
31local MAX_BALL_SPEED = DEFAULT_BALL_SIZE / 2 49local MAX_BALL_SPEED = DEFAULT_BALL_SIZE / 2
50local DEC_BALL_SPEED = DEFAULT_BALL_SIZE / 8
32local DEFAULT_FOREGROUND_COLOR = rb.lcd_get_foreground ~= nil 51local DEFAULT_FOREGROUND_COLOR = rb.lcd_get_foreground ~= nil
33 and rb.lcd_get_foreground() 52 and rb.lcd_get_foreground()
34 or 0 53 or 0
@@ -46,7 +65,15 @@ local levels = {
46 {30, 45}, 65 {30, 45},
47 {37, 50}, 66 {37, 50},
48 {48, 55}, 67 {48, 55},
49 {55, 60} 68 {59, 60},
69 {29, 30},
70 {24, 25},
71 {19, 20},
72 {14, 15},
73 {9, 10},
74 {10, 10},
75 {4, 5},
76 {5, 5}
50 } 77 }
51 78
52local Ball = { 79local Ball = {
@@ -55,16 +82,44 @@ local Ball = {
55 implosion = false 82 implosion = false
56 } 83 }
57 84
58function Ball:new(o) 85local function create_cursor(size)
86 local cursor
87 if not HAS_TOUCHSCREEN then
88 cursor = rb.new_image(size, size)
89 cursor:clear(0)
90 local sz2 = size / 2
91 local sz4 = size / 4
92
93 cursor:line(1, 1, sz4, 1, 1)
94 cursor:line(1, 1, 1, sz4, 1)
95
96 cursor:line(1, size, sz4, size, 1)
97 cursor:line(1, size, 1, size - sz4, 1)
98
99 cursor:line(size, size, size - sz4, size, 1)
100 cursor:line(size, size, size, size - sz4, 1)
101
102 cursor:line(size, 1, size - sz4, 1, 1)
103 cursor:line(size, 1, size, sz4, 1)
104
105 --crosshairs
106 cursor:line(sz2 - sz4, sz2, sz2 + sz4, sz2, 1)
107 cursor:line(sz2, sz2 - sz4, sz2, sz2 + sz4, 1)
108 end
109 return cursor
110end
111
112function Ball:new(o, level)
113 level = level or 1
59 if o == nil then 114 if o == nil then
60 o = { 115 o = {
61 x = math.random(0, rb.LCD_WIDTH - self.size), 116 x = math.random(0, rb.LCD_WIDTH - self.size),
62 y = math.random(0, rb.LCD_HEIGHT - self.size), 117 y = math.random(0, rb.LCD_HEIGHT - self.size),
63 color = random_color(), 118 color = random_color(),
64 up_speed = Ball:generateSpeed(), 119 up_speed = Ball:generateSpeed(level),
65 right_speed = Ball:generateSpeed(), 120 right_speed = Ball:generateSpeed(level),
66 explosion_size = math.random(2*self.size, 4*self.size), 121 explosion_size = math.random(2*self.size, 4*self.size),
67 life_duration = math.random(rb.HZ, rb.HZ*5) 122 life_duration = math.random(rb.HZ / level, rb.HZ*5),
68 } 123 }
69 end 124 end
70 125
@@ -73,8 +128,10 @@ function Ball:new(o)
73 return o 128 return o
74end 129end
75 130
76function Ball:generateSpeed() 131function Ball:generateSpeed(level)
77 local speed = math.random(-MAX_BALL_SPEED, MAX_BALL_SPEED) 132 local ballspeed = MAX_BALL_SPEED - (DEC_BALL_SPEED * (level - 1))
133 if ballspeed < 2 then ballspeed = 2 end
134 local speed = math.random(-ballspeed, ballspeed)
78 if speed == 0 then 135 if speed == 0 then
79 speed = 1 -- Make sure all balls move 136 speed = 1 -- Make sure all balls move
80 end 137 end
@@ -82,39 +139,48 @@ function Ball:generateSpeed()
82 return speed 139 return speed
83end 140end
84 141
142function Ball:draw_exploded()
143 --[[
144 --set_foreground(self.color)
145 --rb.lcd_fillrect(self.x, self.y, self.size, self.size)
146 ]]
147
148 _ellipse(_LCD, self.x, self.y,
149 self.x + self.size, self.y + self.size , self.color, nil, true)
150end
151
85function Ball:draw() 152function Ball:draw()
86 --[[ 153 --[[
87 I know these aren't circles, but as there's no current circle 154 --set_foreground(self.color)
88 implementation in Rockbox, rectangles will just do fine (drawing 155 --rb.lcd_fillrect(self.x, self.y, self.size, self.size)
89 circles from within Lua is far too slow). 156 ]]
90 ]]-- 157
91 set_foreground(self.color) 158 _ellipse(_LCD, self.x, self.y,
92 rb.lcd_fillrect(self.x, self.y, self.size, self.size) 159 self.x + self.size, self.y + self.size , self.color, self.color, true)
93end 160end
94 161
95function Ball:step() 162function Ball:step_exploded()
96 if self.exploded then 163 if self.implosion and self.size > 0 then
97 if self.implosion and self.size > 0 then 164 self.size = self.size - 2
98 self.size = self.size - 2 165 self.x = self.x + 1 -- We do this because we want to stay centered
99 self.x = self.x + 1 -- We do this because we want to stay centered 166 self.y = self.y + 1
100 self.y = self.y + 1 167 elseif self.size < self.explosion_size then
101 elseif self.size < self.explosion_size then 168 self.size = self.size + 2
102 self.size = self.size + 2 169 self.x = self.x - 1 -- We do this for the same reasons as above
103 self.x = self.x - 1 -- We do this for the same reasons as above 170 self.y = self.y - 1
104 self.y = self.y - 1
105 end
106 return
107 end 171 end
172end
108 173
174function Ball:step()
109 self.x = self.x + self.right_speed 175 self.x = self.x + self.right_speed
110 self.y = self.y + self.up_speed 176 self.y = self.y + self.up_speed
111 if (self.right_speed > 0 and self.x + self.size >= rb.LCD_WIDTH) or 177 if (self.x <= 0 or self.x >= rb.LCD_WIDTH - self.size) then
112 (self.right_speed < 0 and self.x <= 0) then
113 self.right_speed = -self.right_speed 178 self.right_speed = -self.right_speed
179 self.x = self.x + self.right_speed
114 end 180 end
115 if (self.up_speed > 0 and self.y + self.size >= rb.LCD_HEIGHT) or 181 if (self.y <= 0 or self.y >= rb.LCD_HEIGHT - self.size ) then
116 (self.up_speed < 0 and self.y <= 0) then
117 self.up_speed = -self.up_speed 182 self.up_speed = -self.up_speed
183 self.y = self.y + self.up_speed
118 end 184 end
119end 185end
120 186
@@ -137,7 +203,8 @@ end
137local Cursor = { 203local Cursor = {
138 size = DEFAULT_BALL_SIZE*2, 204 size = DEFAULT_BALL_SIZE*2,
139 x = rb.LCD_WIDTH/2, 205 x = rb.LCD_WIDTH/2,
140 y = rb.LCD_HEIGHT/2 206 y = rb.LCD_HEIGHT/2,
207 image = create_cursor(DEFAULT_BALL_SIZE*2)
141 } 208 }
142 209
143function Cursor:new() 210function Cursor:new()
@@ -145,18 +212,18 @@ function Cursor:new()
145end 212end
146 213
147function Cursor:do_action(action) 214function Cursor:do_action(action)
148 if action == rb.actions.ACTION_TOUCHSCREEN and HAS_TOUCHSCREEN then 215 if action == actions_pla.ACTION_TOUCHSCREEN and HAS_TOUCHSCREEN then
149 _, self.x, self.y = rb.action_get_touchscreen_press() 216 _, self.x, self.y = rb.action_get_touchscreen_press()
150 return true 217 return true
151 elseif action == rb.actions.PLA_SELECT then 218 elseif action == actions_pla.PLA_SELECT then
152 return true 219 return true
153 elseif (action == rb.actions.PLA_RIGHT) then 220 elseif (action == actions_pla.PLA_RIGHT or action == actions_pla.PLA_RIGHT_REPEAT) then
154 self.x = self.x + self.size 221 self.x = self.x + self.size
155 elseif (action == rb.actions.PLA_LEFT) then 222 elseif (action == actions_pla.PLA_LEFT or action == actions_pla.PLA_LEFT_REPEAT) then
156 self.x = self.x - self.size 223 self.x = self.x - self.size
157 elseif (action == rb.actions.PLA_UP) then 224 elseif (action == actions_pla.PLA_UP or action == actions_pla.PLA_UP_REPEAT) then
158 self.y = self.y - self.size 225 self.y = self.y - self.size
159 elseif (action == rb.actions.PLA_DOWN) then 226 elseif (action == actions_pla.PLA_DOWN or action == actions_pla.PLA_DOWN_REPEAT) then
160 self.y = self.y + self.size 227 self.y = self.y + self.size
161 end 228 end
162 229
@@ -176,25 +243,16 @@ function Cursor:do_action(action)
176end 243end
177 244
178function Cursor:draw() 245function Cursor:draw()
179 set_foreground(DEFAULT_FOREGROUND_COLOR)
180 246
181 rb.lcd_hline(self.x - self.size/2, self.x - self.size/4, self.y - self.size/2) 247 rocklib_image.copy(_LCD, self.image, self.x - self.size/2, self.y - self.size/2,
182 rb.lcd_hline(self.x + self.size/4, self.x + self.size/2, self.y - self.size/2) 248 _NIL, _NIL, _NIL, _NIL, true, BSAND, DEFAULT_FOREGROUND_COLOR)
183 rb.lcd_hline(self.x - self.size/2, self.x - self.size/4, self.y + self.size/2)
184 rb.lcd_hline(self.x + self.size/4, self.x + self.size/2, self.y + self.size/2)
185 rb.lcd_vline(self.x - self.size/2, self.y - self.size/2, self.y - self.size/4)
186 rb.lcd_vline(self.x - self.size/2, self.y + self.size/4, self.y + self.size/2)
187 rb.lcd_vline(self.x + self.size/2, self.y - self.size/2, self.y - self.size/4)
188 rb.lcd_vline(self.x + self.size/2, self.y + self.size/4, self.y + self.size/2)
189
190 rb.lcd_hline(self.x - self.size/4, self.x + self.size/4, self.y)
191 rb.lcd_vline(self.x, self.y - self.size/4, self.y + self.size/4)
192end 249end
193 250
194function draw_positioned_string(bottom, right, str) 251function draw_positioned_string(bottom, right, str)
195 local _, w, h = rb.font_getstringsize(str, rb.FONT_UI) 252 local _, w, h = rb.font_getstringsize(str, rb.FONT_UI)
196 253 local x = not right or (rb.LCD_WIDTH-w)*right - 1
197 rb.lcd_putsxy((rb.LCD_WIDTH-w)*right, (rb.LCD_HEIGHT-h)*bottom, str) 254 local y = not bottom or (rb.LCD_HEIGHT-h)*bottom - 1
255 rb.lcd_putsxy(x, y, str)
198end 256end
199 257
200function set_foreground(color) 258function set_foreground(color)
@@ -213,31 +271,43 @@ end
213 271
214function start_round(level, goal, nrBalls, total) 272function start_round(level, goal, nrBalls, total)
215 local player_added, score, exit, nrExpendedBalls = false, 0, false, 0 273 local player_added, score, exit, nrExpendedBalls = false, 0, false, 0
216 local balls, explodedBalls = {}, {} 274 local Balls, explodedBalls = {}, {}
275 local ball_ct, ball_el
276 local tick, endtick
277 local action = 0
278 local hit_detected = false
217 local cursor = Cursor:new() 279 local cursor = Cursor:new()
218 280
219 -- Initialize the balls 281 -- Initialize the balls
220 for _=1,nrBalls do 282 for _=1,nrBalls do
221 table.insert(balls, Ball:new()) 283 table.insert(Balls, Ball:new(nil, level))
284 end
285
286 local function draw_stats()
287 draw_positioned_string(0, 0, string.format("%d balls expended", nrExpendedBalls))
288 draw_positioned_string(0, 1, string.format("Level %d", level))
289 draw_positioned_string(1, 1, string.format("%d level points", score))
290 draw_positioned_string(1, 0, string.format("%d total points", total + score))
222 end 291 end
223 292
224 -- Make sure there are no unwanted touchscreen presses 293 -- Make sure there are no unwanted touchscreen presses
225 rb.button_clear_queue() 294 rb.button_clear_queue()
226 295
296 set_foreground(DEFAULT_FOREGROUND_COLOR) -- color for text
297
227 while true do 298 while true do
228 local endtick = rb.current_tick() + CYCLETIME 299 endtick = rb.current_tick() + CYCLETIME
229 300
230 -- Check if the round is over 301 -- Check if the round is over
231 if #explodedBalls == 0 and player_added then 302 if player_added and #explodedBalls == 0 then
232 break 303 break
233 end 304 end
234 305
235 -- Check for actions 306 if action == actions_pla.PLA_EXIT or action == actions_pla.PLA_CANCEL then
236 local action = rb.get_plugin_action(0)
237 if action == rb.actions.PLA_EXIT or action == rb.actions.PLA_CANCEL then
238 exit = true 307 exit = true
239 break 308 break
240 end 309 end
310
241 if not player_added and cursor:do_action(action) then 311 if not player_added and cursor:do_action(action) then
242 local player = Ball:new({ 312 local player = Ball:new({
243 x = cursor.x, 313 x = cursor.x,
@@ -248,26 +318,28 @@ function start_round(level, goal, nrBalls, total)
248 exploded = true, 318 exploded = true,
249 death_time = rb.current_tick() + rb.HZ * 3 319 death_time = rb.current_tick() + rb.HZ * 3
250 }) 320 })
251 table.insert(explodedBalls, player) 321 explodedBalls[1] = player
252 player_added = true 322 player_added = true
323 cursor = nil
253 end 324 end
254 325
255 -- Check for hits 326 -- Check for hits
256 for i, ball in ipairs(balls) do 327 for i, Ball in ipairs(Balls) do
257 for _, explodedBall in ipairs(explodedBalls) do 328 for _, explodedBall in ipairs(explodedBalls) do
258 if ball:checkHit(explodedBall) then 329 if Ball:checkHit(explodedBall) then
259 score = score + 100*level 330 explodedBalls[#explodedBalls + 1] = Ball
260 nrExpendedBalls = nrExpendedBalls + 1 331 --table.remove(Balls, i)
261 table.insert(explodedBalls, ball) 332 Balls[i] = false
262 table.remove(balls, i) 333 hit_detected = true
263 break 334 break
264 end 335 end
265 end 336 end
266 end 337 end
267 338
268 -- Check if we're dead yet 339 -- Check if we're dead yet
340 tick = rb.current_tick()
269 for i, explodedBall in ipairs(explodedBalls) do 341 for i, explodedBall in ipairs(explodedBalls) do
270 if rb.current_tick() >= explodedBall.death_time then 342 if explodedBall.death_time < tick then
271 if explodedBall.size > 0 then 343 if explodedBall.size > 0 then
272 explodedBall.implosion = true -- We should be dying 344 explodedBall.implosion = true -- We should be dying
273 else 345 else
@@ -277,38 +349,64 @@ function start_round(level, goal, nrBalls, total)
277 end 349 end
278 350
279 -- Drawing phase 351 -- Drawing phase
280 rb.lcd_clear_display() 352 if hit_detected then
353 hit_detected = false
354 -- same as table.remove(Balls, i) but more efficient
355 ball_el = 1
356 ball_ct = #Balls
357 for i = 1, ball_ct do
358 if Balls[i] then
359 Balls[ball_el] = Balls[i]
360 ball_el = ball_el + 1
361 end
362 end
363 -- remove any remaining exploded balls
364 for i = ball_el, ball_ct do
365 Balls[i] = nil
366 end
367 -- Calculate score
368 nrExpendedBalls = nrBalls - ball_el + 1
369 score = nrExpendedBalls * level * 100
370 end
281 371
282 set_foreground(DEFAULT_FOREGROUND_COLOR) 372 rb.lcd_clear_display()
283 draw_positioned_string(0, 0, string.format("%d balls expended", nrExpendedBalls)) 373 draw_stats()
284 draw_positioned_string(0, 1, string.format("Level %d", level))
285 draw_positioned_string(1, 1, string.format("%d level points", score))
286 draw_positioned_string(1, 0, string.format("%d total points", total+score))
287 374
288 for _, ball in ipairs(balls) do 375 if not (player_added or HAS_TOUCHSCREEN) then
289 ball:step() 376 cursor:draw()
290 ball:draw()
291 end 377 end
292 378
293 for _, explodedBall in ipairs(explodedBalls) do 379 for _, Ball in ipairs(Balls) do
294 explodedBall:step() 380 Ball:step()
295 explodedBall:draw() 381 Ball:draw()
296 end 382 end
297 383
298 if not HAS_TOUCHSCREEN and not player_added then 384 for _, explodedBall in ipairs(explodedBalls) do
299 cursor:draw() 385 explodedBall:step_exploded()
386 explodedBall:draw_exploded()
300 end 387 end
301 388
302 -- Push framebuffer to the LCD 389 -- Push framebuffer to the LCD
303 rb.lcd_update() 390 rb.lcd_update()
304 391
392 -- Check for actions
305 if rb.current_tick() < endtick then 393 if rb.current_tick() < endtick then
306 rb.sleep(endtick - rb.current_tick()) 394 action = rb.get_plugin_action(endtick - rb.current_tick())
307 else 395 else
308 rb.yield() 396 rb.yield()
397 action = rb.get_plugin_action(0)
309 end 398 end
399
310 end 400 end
311 401
402 --splash the final stats for a moment at end
403 rb.lcd_clear_display()
404 for _, Ball in ipairs(Balls) do Ball:draw() end
405 _LCD:clear(nil, nil, nil, nil, nil, nil, 2, 2)
406 draw_stats()
407 rb.lcd_update()
408 rb.sleep(rb.HZ * 2)
409
312 return exit, score, nrExpendedBalls 410 return exit, score, nrExpendedBalls
313end 411end
314 412
@@ -320,6 +418,7 @@ function display_message(to, ...)
320 418
321 rb.lcd_clear_display() 419 rb.lcd_clear_display()
322 set_foreground(DEFAULT_FOREGROUND_COLOR) 420 set_foreground(DEFAULT_FOREGROUND_COLOR)
421
323 if w > rb.LCD_WIDTH then 422 if w > rb.LCD_WIDTH then
324 rb.lcd_puts_scroll(0, y/h, message) 423 rb.lcd_puts_scroll(0, y/h, message)
325 else 424 else
@@ -340,7 +439,7 @@ function display_message(to, ...)
340 if to == -1 then 439 if to == -1 then
341 rb.sleep(rb.HZ/2) 440 rb.sleep(rb.HZ/2)
342 rb.button_clear_queue() 441 rb.button_clear_queue()
343 rb.button_get(1) 442 rb.button_get(rb.HZ * 60)
344 else 443 else
345 rb.sleep(to) 444 rb.sleep(to)
346 end 445 end
@@ -351,6 +450,8 @@ end
351if HAS_TOUCHSCREEN then 450if HAS_TOUCHSCREEN then
352 rb.touchscreen_set_mode(rb.TOUCHSCREEN_POINT) 451 rb.touchscreen_set_mode(rb.TOUCHSCREEN_POINT)
353end 452end
453
454--[[MAIN PROGRAM]]
354rb.backlight_force_on() 455rb.backlight_force_on()
355 456
356math.randomseed(os.time()) 457math.randomseed(os.time())
@@ -359,9 +460,12 @@ local idx, highscore = 1, 0
359while levels[idx] ~= nil do 460while levels[idx] ~= nil do
360 local goal, nrBalls = levels[idx][1], levels[idx][2] 461 local goal, nrBalls = levels[idx][1], levels[idx][2]
361 462
463 collectgarbage("collect") --run gc now to hopefully prevent interruption later
464
362 display_message(rb.HZ*2, "Level %d: get %d out of %d balls", idx, goal, nrBalls) 465 display_message(rb.HZ*2, "Level %d: get %d out of %d balls", idx, goal, nrBalls)
363 466
364 local exit, score, nrExpendedBalls = start_round(idx, goal, nrBalls, highscore) 467 local exit, score, nrExpendedBalls = start_round(idx, goal, nrBalls, highscore)
468
365 if exit then 469 if exit then
366 break -- Exiting.. 470 break -- Exiting..
367 else 471 else
@@ -371,11 +475,14 @@ while levels[idx] ~= nil do
371 highscore = highscore + score 475 highscore = highscore + score
372 else 476 else
373 display_message(rb.HZ*2, "You lost!") 477 display_message(rb.HZ*2, "You lost!")
478 highscore = highscore - score - idx * 100
479 if highscore < 0 then break end
374 end 480 end
375 end 481 end
376end 482end
377 483if highscore <= 0 then
378if idx > #levels then 484 display_message(-1, "You lost at level %d", idx)
485elseif idx > #levels then
379 display_message(-1, "You finished the game with %d points!", highscore) 486 display_message(-1, "You finished the game with %d points!", highscore)
380else 487else
381 display_message(-1, "You made it till level %d with %d points!", idx, highscore) 488 display_message(-1, "You made it till level %d with %d points!", idx, highscore)
@@ -383,3 +490,4 @@ end
383 490
384-- Restore user backlight settings 491-- Restore user backlight settings
385rb.backlight_use_settings() 492rb.backlight_use_settings()
493