diff options
Diffstat (limited to 'apps/plugins/lua_scripts/stars.lua')
-rw-r--r-- | apps/plugins/lua_scripts/stars.lua | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/apps/plugins/lua_scripts/stars.lua b/apps/plugins/lua_scripts/stars.lua new file mode 100644 index 0000000000..14fae0c4b7 --- /dev/null +++ b/apps/plugins/lua_scripts/stars.lua | |||
@@ -0,0 +1,256 @@ | |||
1 | --[[ | ||
2 | __________ __ ___. | ||
3 | Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | \/ \/ \/ \/ \/ | ||
8 | $Id$ | ||
9 | |||
10 | Copyright (C) 2024 William Wilgus | ||
11 | This program is free software; you can redistribute it and/or | ||
12 | modify it under the terms of the GNU General Public License | ||
13 | as published by the Free Software Foundation; either version 2 | ||
14 | of the License, or (at your option) any later version. | ||
15 | This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | KIND, either express or implied. | ||
17 | ]]-- | ||
18 | --https://nullprogram.com/blog/2011/06/13/ [Infinite Parallax Starfield] | ||
19 | |||
20 | -- Imports | ||
21 | local _clr = require("color") -- clrset, clrinc provides device independent colors | ||
22 | local _lcd = require("lcd") -- lcd helper functions | ||
23 | local _draw = require("draw") -- draw all the things (primitives) | ||
24 | local _poly = require("draw_poly") -- vector drawing with tables of coords | ||
25 | require("actions") | ||
26 | --'CONSTANTS' (in lua there really is no such thing as all vars are mutable) | ||
27 | -------------------------------------------------------- | ||
28 | --colors for fg/bg ------------------------ | ||
29 | --first number of each quad is fallback for monochrome devices, excluded columns default to 0 | ||
30 | local WHITE = _clr.set(-1, 255, 255, 255) | ||
31 | local BLACK = _clr.set(0, 0, 0, 0) | ||
32 | local RED = _clr.set(WHITE, 100) | ||
33 | local GREEN = _clr.set(WHITE, 0, 100) | ||
34 | local BGREEN = _clr.set(WHITE, 0, 255) | ||
35 | local BLUE = _clr.set(WHITE, 0, 0, 100) | ||
36 | |||
37 | local STAR_SEED = 0x9d2c5680; | ||
38 | local STAR_TILE_SIZE = math.max(rb.LCD_WIDTH, rb.LCD_HEIGHT) * 4; | ||
39 | local bxor, band, rshift, lshift, arshift = bit.bxor, bit.band, bit.rshift, bit.lshift, bit.arshift | ||
40 | local random, randomseed = math.random, math.randomseed | ||
41 | local start_x, start_y, start_z | ||
42 | |||
43 | -- load users coords from file if it exists | ||
44 | local fname = rb.PLUGIN_DATA_DIR .. "/stars.pos" | ||
45 | file = io.open(fname, "r") | ||
46 | if file then | ||
47 | local v = 0 | ||
48 | for line in file:lines() do | ||
49 | v = v + 1 | ||
50 | if v == 1 then | ||
51 | start_x = tonumber(line) or 0 | ||
52 | elseif v == 2 then | ||
53 | start_y = tonumber(line) or 0 | ||
54 | elseif v == 3 then | ||
55 | start_z = tonumber(line) or 0 | ||
56 | else | ||
57 | break; | ||
58 | end | ||
59 | end | ||
60 | io.close( file ) | ||
61 | end | ||
62 | |||
63 | -- Robert Jenkins' 96 bit Mix Function. | ||
64 | local function mix (a, b, c) | ||
65 | a=a-b; a=a-c; a=bxor(a, (rshift(c, 13))); | ||
66 | b=b-c; b=b-a; b=bxor(b, (lshift(a, 8))); | ||
67 | c=c-a; c=c-b; c=bxor(c, (rshift(b, 13))); | ||
68 | a=a-b; a=a-c; a=bxor(a, (rshift(c, 12))); | ||
69 | b=b-c; b=b-a; b=bxor(b, (lshift(a, 16))); | ||
70 | c=c-a; c=c-b; c=bxor(c, (rshift(b, 5))); | ||
71 | a=a-b; a=a-c; a=bxor(a, (rshift(c, 3))); | ||
72 | b=b-c; b=b-a; b=bxor(b, (lshift(a, 10))); | ||
73 | c=c-a; c=c-b; c=bxor(c, (rshift(b, 15))); | ||
74 | |||
75 | return c | ||
76 | end | ||
77 | |||
78 | -- given 32 bit number returns a table of 8 nibbles (4 bits) | ||
79 | local function s_bytes_nib(bits, value) | ||
80 | -- bits must be multiples of 8 (sizeof byte) | ||
81 | local bbuffer = {} | ||
82 | local byte | ||
83 | local nbytes = bit.rshift(bits, 3) | ||
84 | for b = 1, nbytes do | ||
85 | if value > 0 then | ||
86 | byte = value % 256 | ||
87 | value = (value - byte) / 256 | ||
88 | else | ||
89 | byte = 0 | ||
90 | end | ||
91 | bbuffer[#bbuffer + 1] = band(byte,0xF) | ||
92 | bbuffer[#bbuffer + 1] = band(rshift(byte, 2), 0xF) | ||
93 | end | ||
94 | return bbuffer | ||
95 | end | ||
96 | |||
97 | --[[ given table t and total elems desired uses random elems of t | ||
98 | and random numbers between 1 and max_v if #t < total_elems]] | ||
99 | function randomize_table(t, total_elems, max_v) | ||
100 | local rand_t = {} | ||
101 | local i = 1 | ||
102 | repeat | ||
103 | local v = t[random(1, total_elems)] | ||
104 | if v then | ||
105 | rand_t[i] = v | ||
106 | else | ||
107 | rand_t[i] = random(1, max_v) | ||
108 | end | ||
109 | i = i + 1 | ||
110 | until i > total_elems | ||
111 | return rand_t | ||
112 | end | ||
113 | |||
114 | local function drawship(img, x, y, color, ship_t) | ||
115 | --_poly.polyline(img, x, y, ship_t, color, true, true) | ||
116 | _poly.polygon(img, x, y, ship_t, color, BLACK, true) | ||
117 | end | ||
118 | |||
119 | local function draw_astroid(img, x, y, size, shape, color, scale_x, scale_y) | ||
120 | --the random number generator gets seeded with the hash so we get the same figure each time | ||
121 | randomseed(shape) | ||
122 | -- we also use the 4 bytes of the hash as 4 coord pairs and randomly generate 8 more (16) half the size (8) | ||
123 | local uniq_t = randomize_table(s_bytes_nib(32, shape), 16, 8) | ||
124 | _poly.polyline(img, x, y, uniq_t, color, true, true, scale_x or 1, scale_y or 1) | ||
125 | --_poly.polygon(img, x, y, uniq_t, color, color, true, scale_x or 1, scale_y or 1) --filled figures | ||
126 | end | ||
127 | |||
128 | local function drawStars (img, xoff, yoff, starscale, color, scale_x, scale_y) | ||
129 | local size = STAR_TILE_SIZE / starscale | ||
130 | local s_x, s_y = scale_x, scale_y | ||
131 | local w, h = rb.LCD_WIDTH, rb.LCD_HEIGHT | ||
132 | |||
133 | -- Top-left tile's top-left position | ||
134 | local sx = ((xoff - w/2) / size) * size - size; | ||
135 | local sy = ((yoff - h/2) / size) * size - size; | ||
136 | |||
137 | --Draw each tile currently in view. | ||
138 | for i = sx, w + sx + size*3, size do | ||
139 | for j = sy, h + sy + size*3, size do | ||
140 | local hash = mix(STAR_SEED, (i / size), (j / size)) | ||
141 | for n = 0, 2 do | ||
142 | local px = (hash % size) + (i - xoff); | ||
143 | hash = arshift(hash, 3) | ||
144 | |||
145 | local py = (hash % size) + (j - yoff); | ||
146 | hash = arshift(hash, 3) | ||
147 | |||
148 | if px > 0 and px < w and py > 0 and py < h then | ||
149 | if n > 0 and starscale < 5 then | ||
150 | draw_astroid(img, px, py, n, hash, color, s_x, s_y) | ||
151 | else | ||
152 | if scale_x > 1 then | ||
153 | img:set(px, py, color) | ||
154 | img:set(px + 1, py, color) | ||
155 | elseif scale_y > 1 then | ||
156 | img:set(px, py, color) | ||
157 | img:set(px, py + 1, color) | ||
158 | else | ||
159 | img:set(px, py, color) | ||
160 | end | ||
161 | end | ||
162 | end | ||
163 | end | ||
164 | end | ||
165 | end | ||
166 | end | ||
167 | |||
168 | do | ||
169 | local act = rb.actions | ||
170 | local quit = false | ||
171 | local last_action = 0 | ||
172 | local x,y,z = start_x or 0, start_y or 0, start_z or 8 | ||
173 | local x_fast = rb.LCD_WIDTH / 4 | ||
174 | local y_fast = rb.LCD_HEIGHT / 4 | ||
175 | local ship_x = (rb.LCD_WIDTH - 0xF) / 2 | ||
176 | local ship_y = rb.LCD_HEIGHT - (rb.LCD_HEIGHT / 3) | ||
177 | local scale_x, scale_y = 1, 1 | ||
178 | -- vector draw the ship points for each direction (<>^v) | ||
179 | local ship_lt_t = {0,7, 15,0, 9,7, 15,15, 0,7} | ||
180 | local ship_rt_t = {0,0, 5,7, 0,15, 15,7, 0,0} | ||
181 | local ship_up_t = {0,15, 7,0, 15,15, 7,9, 0,15} | ||
182 | local ship_dn_t = {0,0, 7,15, 15,0, 7,5, 0,0} | ||
183 | local ship_t = ship_up_t | ||
184 | |||
185 | function action_event(action) | ||
186 | if action == act.PLA_EXIT or action == act.PLA_CANCEL then | ||
187 | quit = true | ||
188 | start_x, start_y, start_z = x, y, z | ||
189 | elseif action == act.PLA_RIGHT_REPEAT then | ||
190 | x = x + x_fast | ||
191 | scale_x = scale_x + 1 | ||
192 | scale_y = 1 | ||
193 | elseif action == act.PLA_LEFT_REPEAT then | ||
194 | x = x - x_fast | ||
195 | scale_x = scale_x + 1 | ||
196 | scale_y = 1 | ||
197 | elseif action == act.PLA_UP_REPEAT then | ||
198 | y = y - y_fast | ||
199 | scale_y = scale_y + 1 | ||
200 | scale_x = 1 | ||
201 | elseif action == act.PLA_DOWN_REPEAT then | ||
202 | y = y + y_fast | ||
203 | scale_y = scale_y + 1 | ||
204 | scale_x = 1 | ||
205 | elseif action == act.PLA_RIGHT then | ||
206 | x = x + 1 | ||
207 | ship_t = ship_rt_t | ||
208 | elseif action == act.PLA_LEFT then | ||
209 | x = x - 1 | ||
210 | ship_t = ship_lt_t | ||
211 | elseif action == act.PLA_UP then | ||
212 | y = y - 1 | ||
213 | ship_t = ship_up_t | ||
214 | elseif action == act.PLA_DOWN then | ||
215 | y = y + 1 | ||
216 | ship_t = ship_dn_t | ||
217 | elseif action == act.PLA_SELECT then | ||
218 | z = z + 4 | ||
219 | if z > 16 then z = 0 end | ||
220 | elseif action == act.ACTION_NONE then | ||
221 | scale_x = 1 | ||
222 | scale_y = 1 | ||
223 | end | ||
224 | |||
225 | _lcd:clear(BLACK) | ||
226 | for i = 0, z, 4 do | ||
227 | drawStars(_LCD, x, y, i+1, RED, scale_x, scale_y) | ||
228 | drawStars(_LCD, x, y, i+2, GREEN, scale_x, scale_y) | ||
229 | drawStars(_LCD, x, y, i+3, BLUE, scale_x, scale_y) | ||
230 | drawStars(_LCD, x, y, i+4, WHITE, scale_x, scale_y) | ||
231 | end | ||
232 | drawship(_LCD, ship_x, ship_y, BGREEN, ship_t) | ||
233 | _lcd:update() | ||
234 | |||
235 | last_action = action | ||
236 | end | ||
237 | |||
238 | function action_set_quit(bQuit) | ||
239 | quit = bQuit | ||
240 | end | ||
241 | |||
242 | function action_quit() | ||
243 | return quit | ||
244 | end | ||
245 | end | ||
246 | |||
247 | action_event(rb.actions.ACTION_NONE) -- we can call this now but not after registering.. | ||
248 | local eva = rockev.register("action", action_event) | ||
249 | |||
250 | while not action_quit() do rb.sleep(rb.HZ) end | ||
251 | |||
252 | if start_x and start_y then | ||
253 | file = io.open(fname, "w") | ||
254 | file:write(start_x, "\n", start_y, "\n", start_z, "\n") | ||
255 | io.close( file ) | ||
256 | end | ||