aboutsummaryrefslogtreecommitdiff
path: root/users/snowe/ocean_dream.c
diff options
context:
space:
mode:
Diffstat (limited to 'users/snowe/ocean_dream.c')
-rw-r--r--users/snowe/ocean_dream.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/users/snowe/ocean_dream.c b/users/snowe/ocean_dream.c
new file mode 100644
index 000000000..2f372628d
--- /dev/null
+++ b/users/snowe/ocean_dream.c
@@ -0,0 +1,555 @@
1/*
2 * Copyright 2021 Tyler Thrailkill (@snowe/@snowe2010) <tyler.b.thrailkill@gmail.com>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "ocean_dream.h"
19#include "quantum.h"
20#include "print.h"
21
22// Calculated Parameters
23#define TWINKLE_PROBABILITY_MODULATOR 100 / TWINKLE_PROBABILITY // CALCULATED: Don't Touch
24#define TOTAL_STARS STARS_PER_LINE *NUMBER_OF_STAR_LINES // CALCULATED: Don't Touch
25#define OCEAN_ANIMATION_MODULATOR NUMBER_OF_FRAMES / OCEAN_ANIMATION_SPEED // CALCULATED: Don't Touch
26#define SHOOTING_STAR_ANIMATION_MODULATOR NUMBER_OF_FRAMES / SHOOTING_STAR_ANIMATION_SPEED // CALCULATED: Don't Touch
27#define STAR_ANIMATION_MODULATOR NUMBER_OF_FRAMES / STAR_ANIMATION_SPEED // CALCULATED: Don't Touch
28
29uint8_t animation_counter = 0; // global animation counter.
30bool is_calm = false;
31uint32_t starry_night_anim_timer = 0;
32uint32_t starry_night_anim_sleep = 0;
33static int current_wpm = 0;
34
35static uint8_t increment_counter(uint8_t counter, uint8_t max) {
36 counter++;
37 if (counter >= max) {
38 return 0;
39 } else {
40 return counter;
41 }
42}
43
44#ifdef ENABLE_WAVE
45static uint8_t decrement_counter(uint8_t counter, uint8_t max) {
46 counter--;
47 if (counter < 0 || counter > max) {
48 return max;
49 } else {
50 return counter;
51 }
52}
53#endif
54
55#ifdef ENABLE_MOON // region
56# ifndef STATIC_MOON
57uint8_t moon_animation_frame = 0; // keeps track of current moon frame
58uint16_t moon_animation_counter = 0; // counts how many frames to wait before animating moon to next frame
59# endif
60
61# ifdef STATIC_MOON
62static const char PROGMEM moon[6] = {
63 0x18, 0x7E, 0xFF, 0xC3, 0x81, 0x81,
64};
65# endif
66
67# ifndef STATIC_MOON
68static const char PROGMEM moon_animation[14][8] = {
69 // clang-format off
70 { 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, },
71 { 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x42, 0x00, },
72 { 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xC3, 0x00, 0x00, },
73 { 0x3C, 0x7E, 0xFF, 0xFF, 0xC3, 0x81, 0x00, 0x00, },
74 { 0x3C, 0x7E, 0xFF, 0xC3, 0x81, 0x00, 0x00, 0x00, },
75 { 0x3C, 0x7E, 0xC3, 0x81, 0x81, 0x00, 0x00, 0x00, },
76 { 0x3C, 0x42, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00, },
77 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
78 { 0x00, 0x00, 0x00, 0x00, 0x81, 0x81, 0x42, 0x3C, },
79 { 0x00, 0x00, 0x00, 0x81, 0x81, 0xC3, 0x7E, 0x3C, },
80 { 0x00, 0x00, 0x00, 0x81, 0xC3, 0xFF, 0x7E, 0x3C, },
81 { 0x00, 0x00, 0x81, 0xC3, 0xFF, 0xFF, 0x7E, 0x3C, },
82 { 0x00, 0x00, 0xC3, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, },
83 { 0x00, 0x42, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, },
84 // clang-format on
85};
86# endif
87
88static void draw_moon(void) {
89# ifdef STATIC_MOON
90 oled_set_cursor(MOON_COLUMN, MOON_LINE);
91 oled_write_raw_P(moon, 6);
92# endif
93# ifndef STATIC_MOON
94 moon_animation_counter = increment_counter(moon_animation_counter, ANIMATE_MOON_EVERY_N_FRAMES);
95 if (moon_animation_counter == 0) {
96 moon_animation_frame = increment_counter(moon_animation_frame, 14);
97 oled_set_cursor(MOON_COLUMN, MOON_LINE);
98 oled_write_raw_P(moon_animation[moon_animation_frame], 8);
99 }
100# endif
101}
102#endif // endregion
103
104#ifdef ENABLE_WAVE // region
105uint8_t starry_night_wave_frame_width_counter = 31;
106uint8_t rough_waves_frame_counter = 0;
107
108// clang-format off
109static const char PROGMEM ocean_top[8][32] = {
110 // still ocean
111 {
112 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
113 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
114 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
115 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
116 },
117 // small ripples
118 {
119 0x20, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
120 0x20, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
121 0x20, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
122 0x20, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
123 },
124 // level 2 ripples
125 {
126 0x20, 0x60, 0x40, 0x40, 0x20, 0x60, 0x40, 0x40,
127 0x20, 0x60, 0x40, 0x40, 0x20, 0x60, 0x40, 0x40,
128 0x20, 0x60, 0x40, 0x40, 0x20, 0x60, 0x40, 0x40,
129 0x20, 0x60, 0x40, 0x40, 0x20, 0x60, 0x40, 0x40,
130 },
131 // level 3 waves
132 {
133 0x40, 0x20, 0x10, 0x20, 0x40, 0x40, 0x40, 0x40,
134 0x40, 0x20, 0x10, 0x20, 0x40, 0x40, 0x40, 0x40,
135 0x40, 0x20, 0x10, 0x20, 0x40, 0x40, 0x40, 0x40,
136 0x40, 0x20, 0x10, 0x20, 0x40, 0x40, 0x40, 0x40,
137 },
138 {
139 0x40, 0x40, 0x20, 0x10, 0x28, 0x50, 0x40, 0x40,
140 0x40, 0x40, 0x20, 0x10, 0x28, 0x50, 0x40, 0x40,
141 0x40, 0x40, 0x20, 0x10, 0x28, 0x50, 0x40, 0x40,
142 0x40, 0x40, 0x20, 0x10, 0x28, 0x50, 0x40, 0x40,
143 },
144 {
145 0x40, 0x40, 0x40, 0x20, 0x10, 0x30, 0x70, 0x60,
146 0x40, 0x40, 0x40, 0x20, 0x10, 0x30, 0x70, 0x60,
147 0x40, 0x40, 0x40, 0x20, 0x10, 0x30, 0x70, 0x60,
148 0x40, 0x40, 0x40, 0x20, 0x10, 0x30, 0x70, 0x60,
149 },
150};
151static const char PROGMEM ocean_bottom[8][32] = {
152 // still ocean
153 {
154 0x00, 0x40, 0x40, 0x41, 0x01, 0x01, 0x01, 0x21,
155 0x20, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x44,
156 0x44, 0x40, 0x40, 0x00, 0x00, 0x08, 0x08, 0x00,
157 0x01, 0x01, 0x01, 0x00, 0x40, 0x40, 0x00, 0x00,
158 },
159 // small ripples
160 {
161 0x00, 0x00, 0x40, 0x40, 0x01, 0x01, 0x01, 0x20,
162 0x20, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04,
163 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
164 0x00, 0x01, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00,
165 },
166 // level 2 ripples
167 {
168 0x00, 0x00, 0x40, 0x40, 0x01, 0x01, 0x01, 0x20,
169 0x20, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04,
170 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
171 0x00, 0x01, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00,
172 },
173 // level 3 waves
174 {
175 0x00, 0x40, 0x40, 0x42, 0x42, 0x03, 0x11, 0x11,
176 0x20, 0x20, 0x00, 0x00, 0x08, 0x0C, 0x0C, 0x04,
177 0x05, 0x41, 0x41, 0x21, 0x20, 0x00, 0x00, 0x08,
178 0x0A, 0x0A, 0x0B, 0x41, 0x41, 0x41, 0x41, 0x00,
179 },
180 {
181 0x10, 0x10, 0x00, 0x80, 0x84, 0xC4, 0x02, 0x06,
182 0x84, 0x44, 0xC0, 0x80, 0x80, 0x20, 0x20, 0x10,
183 0x08, 0x12, 0x91, 0x81, 0x42, 0x40, 0x00, 0x00,
184 0x10, 0x12, 0x22, 0x22, 0x24, 0x04, 0x84, 0x80,
185 },
186 {
187 0x08, 0x80, 0x80, 0x82, 0x82, 0x03, 0x21, 0x21,
188 0x10, 0x10, 0x00, 0x00, 0x04, 0x04, 0x0C, 0x08,
189 0x09, 0x41, 0x42, 0x22, 0x20, 0x00, 0x00, 0x08,
190 0x0A, 0x0A, 0x0B, 0x41, 0x43, 0x42, 0x42, 0x00,
191 },
192};
193// clang-format on
194
195static void animate_waves(void) {
196 starry_night_wave_frame_width_counter = decrement_counter(starry_night_wave_frame_width_counter, WIDTH - 1); // only 3 frames for last wave type
197 rough_waves_frame_counter = increment_counter(rough_waves_frame_counter, 3); // only 3 frames for last wave type
198
199 void draw_ocean(uint8_t frame, uint16_t offset, uint8_t byte_index) {
200 oled_write_raw_byte(pgm_read_byte(ocean_top[frame] + byte_index), offset);
201 oled_write_raw_byte(pgm_read_byte(ocean_bottom[frame] + byte_index), offset + WIDTH);
202 }
203
204 for (int i = 0; i < WIDTH; ++i) {
205 uint16_t offset = OCEAN_LINE * WIDTH + i;
206 uint8_t byte_index = starry_night_wave_frame_width_counter + i;
207 if (byte_index >= WIDTH) {
208 byte_index = byte_index - WIDTH;
209 }
210 if (is_calm || current_wpm <= WAVE_CALM) {
211 draw_ocean(0, offset, byte_index);
212 } else if (current_wpm <= WAVE_HEAVY_STORM) {
213 draw_ocean(1, offset, byte_index);
214 } else if (current_wpm <= WAVE_HURRICANE) {
215 draw_ocean(2, offset, byte_index);
216 } else {
217 draw_ocean(3 + rough_waves_frame_counter, offset, byte_index);
218 }
219 }
220}
221#endif // endregion
222
223#ifdef ENABLE_ISLAND // region
224uint8_t island_frame_1 = 0;
225
226// clang-format off
227// only use 46 bytes (first 18 are blank, so we don't write them, makes it smaller and we can see the shooting stars properly!)
228
229// To save space and allow the shooting stars to be seen, only draw the tree on every frame.
230// Tree is only 14bytes wide so we save 108 bytes on just the first row. Second row, the
231// first 18 bytes is always the same piece of land, so only store that once, which saves 90 bytes
232static const char PROGMEM islandRightTop[6][14] = {
233 {0x84, 0xEC, 0x6C, 0x3C, 0xF8, 0xFE, 0x3F, 0x6B, 0xDB, 0xB9, 0x30, 0x40, 0x00, 0x00,},
234 {0x80, 0xC3, 0xEE, 0x7C, 0xB8, 0xFC, 0xFE, 0x6F, 0xDB, 0x9B, 0xB2, 0x30, 0x00, 0x00,},
235 {0x00, 0xC0, 0xEE, 0x7F, 0x3D, 0xF8, 0xFC, 0x7E, 0x57, 0xDB, 0xDB, 0x8A, 0x00, 0x00,},
236 {0x00, 0xC0, 0xE6, 0x7F, 0x3B, 0xF9, 0xFC, 0xFC, 0xB6, 0xB3, 0x33, 0x61, 0x00, 0x00,},
237 {0x00, 0x00, 0x00, 0x00, 0x80, 0xEE, 0xFF, 0xFB, 0xF9, 0xFC, 0xDE, 0xB6, 0xB6, 0x24,},
238 {0x00, 0x00, 0x00, 0x00, 0xC0, 0xEE, 0xFE, 0xFF, 0xFB, 0xFD, 0xEE, 0xB6, 0xB6, 0x92,},
239};
240static const char PROGMEM islandRightBottom[6][14] = {
241 {0x41, 0x40, 0x60, 0x3E, 0x3F, 0x23, 0x20, 0x60, 0x41, 0x43, 0x40, 0x40, 0x40, 0x80,},
242 {0x40, 0x41, 0x60, 0x3E, 0x3F, 0x23, 0x20, 0x60, 0x40, 0x40, 0x41, 0x41, 0x40, 0x80,},
243 {0x40, 0x40, 0x61, 0x3D, 0x3F, 0x27, 0x21, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80,},
244 {0x40, 0x43, 0x61, 0x3C, 0x3F, 0x27, 0x21, 0x60, 0x41, 0x43, 0x43, 0x42, 0x40, 0x80,},
245 {0x40, 0x40, 0x60, 0x3C, 0x3F, 0x27, 0x23, 0x63, 0x44, 0x40, 0x41, 0x41, 0x41, 0x81,},
246 {0x40, 0x40, 0x60, 0x3C, 0x3F, 0x27, 0x23, 0x63, 0x42, 0x42, 0x41, 0x41, 0x41, 0x80,},
247};
248static const char PROGMEM islandLeft[18] = {
249 0x80, 0x40, 0x40, 0x40, 0x40, 0x60,
250 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
251 0x20, 0x20, 0x20, 0x60, 0x40, 0x40,
252};
253// clang-format on
254
255static void animate_island(void) {
256 if (animation_counter == 0) {
257 island_frame_1 = increment_counter(island_frame_1, 2);
258 }
259
260 void draw_island_parts(uint8_t frame) {
261 oled_set_cursor(ISLAND_COLUMN + 3, ISLAND_LINE);
262 oled_write_raw_P(islandRightTop[frame], 14);
263 oled_set_cursor(ISLAND_COLUMN + 0, ISLAND_LINE + 1);
264 oled_write_raw_P(islandLeft, 18);
265 oled_set_cursor(ISLAND_COLUMN + 3, ISLAND_LINE + 1);
266 oled_write_raw_P(islandRightBottom[frame], 14);
267 }
268
269 if (is_calm || current_wpm < ISLAND_CALM) {
270 draw_island_parts(0);
271 } else if (current_wpm >= ISLAND_CALM && current_wpm < ISLAND_HEAVY_STORM) {
272 draw_island_parts(island_frame_1 + 1);
273 } else if (current_wpm >= ISLAND_HEAVY_STORM && current_wpm < ISLAND_HURRICANE) {
274 draw_island_parts(island_frame_1 + 2);
275 } else {
276 draw_island_parts(island_frame_1 + 4);
277 }
278}
279#endif // endregion
280
281#ifdef ENABLE_STARS // region
282bool stars_setup = false; // only setup stars once, then we just twinkle them
283struct Coordinate {
284 int x;
285 int y;
286 bool exists;
287};
288
289struct Coordinate stars[TOTAL_STARS]; // tracks all stars/coordinates
290
291/**
292 * Setup all the initial stars on the screen
293 * This function divides the screen into regions based on STARS_PER_LINE and NUMBER_OF_STAR_LINES
294 * where each line is made up of 8x8 pixel groups, that are populated by a single star.
295 *
296 * Not sure how this function will work with larger or smaller screens.
297 * It should be fine, as long as the screen width is a multiple of 8
298 */
299static void setup_stars(void) {
300 // For every line, split the line into STARS_PER_LINE, find a random point in that region, and turn the pixel on
301 // 36% probability it will not be added
302 // (said another way, 80% chance it will start out lit in the x direction, then 80% chance it will start out lit in the y direction = 64% probability it will start out lit at all)
303 for (int line = 0; line < NUMBER_OF_STAR_LINES; ++line) {
304 for (int column_group = 0; column_group < STARS_PER_LINE; ++column_group) {
305 uint8_t rand_column = rand() % 10;
306 uint8_t rand_row = rand() % 10;
307 if (rand_column < 8 && rand_row < 8) {
308 int column_adder = column_group * 8;
309 int line_adder = line * 8;
310 int x = rand_column + column_adder;
311 int y = rand_row + line_adder;
312 oled_write_pixel(x, y, true);
313 stars[column_group + (line * STARS_PER_LINE)].x = x;
314 stars[column_group + (line * STARS_PER_LINE)].y = y;
315 stars[column_group + (line * STARS_PER_LINE)].exists = true;
316 } else {
317 stars[column_group + (line * STARS_PER_LINE)].exists = false;
318 }
319 }
320 }
321 stars_setup = true;
322}
323
324/**
325 * Twinkle the stars (move them one pixel in any direction) with a probability of 50% to twinkle any given star
326 */
327static void twinkle_stars(void) {
328 for (int line = 0; line < NUMBER_OF_STAR_LINES; ++line) {
329 for (int column_group = 0; column_group < STARS_PER_LINE; ++column_group) {
330 struct Coordinate star = stars[column_group + (line * STARS_PER_LINE)];
331
332 // skip stars that were never added
333 if (!star.exists) {
334 continue;
335 }
336 if (rand() % TWINKLE_PROBABILITY_MODULATOR == 0) {
337 oled_write_pixel(star.x, star.y, false); // black out pixel
338
339 // don't allow stars to leave their own region
340 if (star.x == (column_group * 8)) { // star is the farthest left it can go in its region
341 star.x++; // move it right immediately
342 } else if (star.x == (((column_group + 1) * 8) - 1)) { // star is farthest right it can go in its region
343 star.x--; // move it left immediately
344 }
345 if (star.y == (line * 8)) { // star is the farthest up it can go in its region
346 star.y++; // move it down immediately
347 } else if (star.y == (((line + 1) * 8) - 1)) { // star is farthest down it can go in its region
348 star.y--; // move it up immediately
349 }
350
351 // now decide direction
352 int new_x;
353 int x_choice = rand() % 3;
354 if (x_choice == 0) {
355 new_x = star.x - 1;
356 } else if (x_choice == 1) {
357 new_x = star.x + 1;
358 } else {
359 new_x = star.x;
360 }
361
362 int new_y;
363 int y_choice = rand() % 3;
364 if (y_choice == 0) {
365 new_y = star.y - 1;
366 } else if (y_choice == 1) {
367 new_y = star.y + 1;
368 } else {
369 new_y = star.y;
370 }
371
372 star.x = new_x;
373 star.y = new_y;
374 oled_write_pixel(new_x, new_y, true);
375 }
376
377 stars[column_group + (line * STARS_PER_LINE)] = star;
378 }
379 }
380}
381
382/**
383 * Setup the stars and then animate them on subsequent frames
384 */
385static void animate_stars(void) {
386 if (!stars_setup) {
387 setup_stars();
388 } else {
389 twinkle_stars();
390 }
391}
392#endif // endregion
393
394#ifdef ENABLE_SHOOTING_STARS // region
395bool shooting_stars_setup = false; // only setup shooting stars array once with defaults
396
397struct ShootingStar {
398 int x_1;
399 int y_1;
400 int x_2;
401 int y_2;
402 bool running;
403 int frame;
404 int delay;
405};
406
407struct ShootingStar shooting_stars[MAX_NUMBER_OF_SHOOTING_STARS]; // tracks all the shooting stars
408
409static void setup_shooting_star(struct ShootingStar *shooting_star) {
410 int column_to_start = rand() % (WIDTH / 2);
411 int row_to_start = rand() % (HEIGHT - 48); // shooting_stars travel diagonally 1 down, 1 across. So the lowest a shooting_star can start and not 'hit' the ocean is 32 above the ocean.
412
413 shooting_star->x_1 = column_to_start;
414 shooting_star->y_1 = row_to_start;
415 shooting_star->x_2 = column_to_start + 1;
416 shooting_star->y_2 = row_to_start + 1;
417 shooting_star->running = true;
418 shooting_star->frame++;
419 shooting_star->delay = rand() % SHOOTING_STAR_DELAY;
420}
421
422static void move_shooting_star(struct ShootingStar *shooting_star) {
423 oled_write_pixel(shooting_star->x_1, shooting_star->y_1, false);
424 oled_write_pixel(shooting_star->x_2, shooting_star->y_2, false);
425
426 shooting_star->x_1++;
427 shooting_star->y_1++;
428 shooting_star->x_2++;
429 shooting_star->y_2++;
430 shooting_star->frame++;
431
432 oled_write_pixel(shooting_star->x_1, shooting_star->y_1, true);
433 oled_write_pixel(shooting_star->x_2, shooting_star->y_2, true);
434}
435
436static void finish_shooting_star(struct ShootingStar *shooting_star) {
437 oled_write_pixel(shooting_star->x_1, shooting_star->y_1, false);
438 oled_write_pixel(shooting_star->x_2, shooting_star->y_2, false);
439 shooting_star->running = false;
440 shooting_star->frame = 0;
441}
442
443static void animate_shooting_star(struct ShootingStar *shooting_star) {
444 if (shooting_star->frame > SHOOTING_STAR_FRAMES) {
445 finish_shooting_star(shooting_star);
446 return;
447 } else if (!shooting_star->running) {
448 setup_shooting_star(shooting_star);
449 } else {
450 if (shooting_star->delay == 0) {
451 move_shooting_star(shooting_star);
452 } else {
453 shooting_star->delay--;
454 }
455 }
456}
457
458static void animate_shooting_stars(void) {
459 if (is_calm) {
460 return;
461 }
462 if (!shooting_stars_setup) {
463 for (int i = 0; i < MAX_NUMBER_OF_SHOOTING_STARS; ++i) {
464 shooting_stars[i].running = false;
465 }
466 shooting_stars_setup = true;
467 }
468 /**
469 * Fixes issue with stars that were falling _while_ the
470 * wpm dropped below the condition for them to keep falling
471 */
472 void end_extra_stars(uint8_t starting_index) {
473 for (int shooting_star_index = starting_index; shooting_star_index < MAX_NUMBER_OF_SHOOTING_STARS; ++shooting_star_index) {
474 struct ShootingStar shooting_star = shooting_stars[shooting_star_index];
475 if (shooting_star.running) {
476 finish_shooting_star(&shooting_star);
477 shooting_stars[shooting_star_index] = shooting_star;
478 }
479 }
480 }
481
482 int number_of_shooting_stars = current_wpm / SHOOTING_STAR_WPM_INCREMENT;
483 number_of_shooting_stars = (number_of_shooting_stars > MAX_NUMBER_OF_SHOOTING_STARS) ? MAX_NUMBER_OF_SHOOTING_STARS : number_of_shooting_stars;
484
485 if (number_of_shooting_stars == 0) {
486 // make sure all shooting_stars are ended
487 end_extra_stars(0);
488 } else {
489 for (int shooting_star_index = 0; shooting_star_index < number_of_shooting_stars; ++shooting_star_index) {
490 struct ShootingStar shooting_star = shooting_stars[shooting_star_index];
491 animate_shooting_star(&shooting_star);
492 shooting_stars[shooting_star_index] = shooting_star;
493 }
494 end_extra_stars(number_of_shooting_stars);
495 }
496}
497#endif // endregion
498
499/**
500 * Main rendering function
501 *
502 * Calls all different animations at different rates
503 */
504void render_stars(void) {
505 // // animation timer
506 if (timer_elapsed32(starry_night_anim_timer) > STARRY_NIGHT_ANIM_FRAME_DURATION) {
507 starry_night_anim_timer = timer_read32();
508 current_wpm = get_current_wpm();
509
510#ifdef ENABLE_ISLAND
511 animate_island();
512#endif
513
514#ifdef ENABLE_SHOOTING_STARS
515 if (animation_counter % SHOOTING_STAR_ANIMATION_MODULATOR == 0) {
516 animate_shooting_stars();
517 }
518#endif
519
520#ifdef ENABLE_STARS
521 // TODO offsetting the star animation from the wave animation would look better,
522 // but if I do that, then the stars appear in the water because
523 // the ocean animation has to wait a bunch of frames to overwrite it.
524 // Possible solutions:
525 // 1. Only draw stars to the top of the island/ocean.
526 // 2. Draw ocean every frame, only move ocean on frames matching modulus
527 // Problems:
528 // 1. What if someone wants to move the island up a bit, or they want to have the stars reflect in the water?
529 // 2. More cpu intensive. And I'm already running out of cpu as it is...
530 if (animation_counter % STAR_ANIMATION_MODULATOR == 0) {
531 animate_stars();
532 }
533#endif
534
535#ifdef ENABLE_WAVE
536 if (animation_counter % OCEAN_ANIMATION_MODULATOR == 0) {
537 animate_waves();
538 }
539#endif
540
541#ifdef ENABLE_MOON
542 draw_moon();
543#endif
544
545 animation_counter = increment_counter(animation_counter, NUMBER_OF_FRAMES);
546 }
547
548 // this fixes the screen on and off bug
549 if (current_wpm > 0) {
550 oled_on();
551 starry_night_anim_sleep = timer_read32();
552 } else if (timer_elapsed32(starry_night_anim_sleep) > OLED_TIMEOUT) {
553 oled_off();
554 }
555}