Mode refactor
This commit is contained in:
parent
eaf3dcea91
commit
c562c7d932
16
TODO.txt
Normal file
16
TODO.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Modes:
|
||||||
|
Tetris
|
||||||
|
Matrix
|
||||||
|
Ludo
|
||||||
|
Pacman
|
||||||
|
Snake
|
||||||
|
Strobe
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
NewYear => change due date (birthday etc)
|
||||||
|
|
||||||
|
Hardware:
|
||||||
|
Gamepads
|
||||||
|
Heatsink
|
||||||
|
Extend Matrix (double?)
|
||||||
|
Microphone
|
||||||
@ -20,3 +20,4 @@ upload_protocol = espota
|
|||||||
;upload_speed = 921600
|
;upload_speed = 921600
|
||||||
monitor_port = /dev/ttyUSB0
|
monitor_port = /dev/ttyUSB0
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
|
monitor_filters = esp32_exception_decoder
|
||||||
|
|||||||
@ -9,8 +9,12 @@
|
|||||||
#define ____ 0
|
#define ____ 0
|
||||||
#define FULL 255
|
#define FULL 255
|
||||||
|
|
||||||
|
#define countof(x) (sizeof(x) / sizeof(x[0]))
|
||||||
|
|
||||||
typedef int64_t microseconds_t;
|
typedef int64_t microseconds_t;
|
||||||
|
|
||||||
|
typedef unsigned long millis_t;
|
||||||
|
|
||||||
double doStep(double valueCurrent, double valueMin, double valueMax, microseconds_t millisecondsTotal, microseconds_t microsecondsDelta);
|
double doStep(double valueCurrent, double valueMin, double valueMax, microseconds_t millisecondsTotal, microseconds_t microsecondsDelta);
|
||||||
|
|
||||||
bool randomBool(int uncertainty);
|
bool randomBool(int uncertainty);
|
||||||
|
|||||||
@ -16,7 +16,7 @@ bool dirty = false;
|
|||||||
|
|
||||||
bool notify = false;
|
bool notify = false;
|
||||||
|
|
||||||
unsigned long lastDirtyMillis = 0;
|
millis_t lastDirtyMillis = 0;
|
||||||
|
|
||||||
void config_setup() {
|
void config_setup() {
|
||||||
if (!config_load()) {
|
if (!config_load()) {
|
||||||
|
|||||||
@ -29,7 +29,7 @@ private:
|
|||||||
|
|
||||||
Adafruit_NeoPixel leds;
|
Adafruit_NeoPixel leds;
|
||||||
|
|
||||||
unsigned long fpsLastMillis = 0;
|
millis_t fpsLastMillis = 0;
|
||||||
|
|
||||||
int fps = 0;
|
int fps = 0;
|
||||||
|
|
||||||
|
|||||||
32
src/mode.cpp
32
src/mode.cpp
@ -8,12 +8,13 @@
|
|||||||
#include "mode/NewYear/NewYear.h"
|
#include "mode/NewYear/NewYear.h"
|
||||||
#include "mode/Starfield/Starfield.h"
|
#include "mode/Starfield/Starfield.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
#include "mode/Matrix/Matrix.h"
|
||||||
|
|
||||||
ModeId currentModeId = NONE;
|
ModeId currentModeId = NONE;
|
||||||
|
|
||||||
microseconds_t lastMicros = 0;
|
microseconds_t lastMicros = 0;
|
||||||
|
|
||||||
ModeBase *mode = nullptr;
|
Mode *mode = nullptr;
|
||||||
|
|
||||||
void unloadOldMode();
|
void unloadOldMode();
|
||||||
|
|
||||||
@ -22,6 +23,8 @@ void loadNewMode();
|
|||||||
void mode_step();
|
void mode_step();
|
||||||
|
|
||||||
void mode_loop() {
|
void mode_loop() {
|
||||||
|
Serial.print("\n");
|
||||||
|
|
||||||
if (currentModeId != config.mode) {
|
if (currentModeId != config.mode) {
|
||||||
unloadOldMode();
|
unloadOldMode();
|
||||||
loadNewMode();
|
loadNewMode();
|
||||||
@ -49,6 +52,7 @@ void setSpeed(double speed) {
|
|||||||
|
|
||||||
void unloadOldMode() {
|
void unloadOldMode() {
|
||||||
if (mode != nullptr) {
|
if (mode != nullptr) {
|
||||||
|
Serial.print("[MODE] unload\n");
|
||||||
delete mode;
|
delete mode;
|
||||||
mode = nullptr;
|
mode = nullptr;
|
||||||
}
|
}
|
||||||
@ -58,36 +62,40 @@ void unloadOldMode() {
|
|||||||
void loadNewMode() {
|
void loadNewMode() {
|
||||||
currentModeId = config.mode;
|
currentModeId = config.mode;
|
||||||
lastMicros = 0;
|
lastMicros = 0;
|
||||||
|
Serial.printf("[MODE] loading %d\n", currentModeId);
|
||||||
switch (currentModeId) {
|
switch (currentModeId) {
|
||||||
case BORDER:
|
case BORDER:
|
||||||
mode = new Border(&display);
|
mode = new Border(display);
|
||||||
break;
|
break;
|
||||||
case CLOCK:
|
case CLOCK:
|
||||||
mode = new Clock(&display);
|
mode = new Clock(display);
|
||||||
break;
|
break;
|
||||||
case GAME_OF_LIFE_BLACK_WHITE:
|
case GAME_OF_LIFE_BLACK_WHITE:
|
||||||
mode = new GameOfLife(&display, BLACK_WHITE);
|
mode = new GameOfLife(display, BLACK_WHITE);
|
||||||
break;
|
break;
|
||||||
case GAME_OF_LIFE_GRAYSCALE:
|
case GAME_OF_LIFE_GRAYSCALE:
|
||||||
mode = new GameOfLife(&display, GRAYSCALE);
|
mode = new GameOfLife(display, GRAYSCALE);
|
||||||
break;
|
break;
|
||||||
case GAME_OF_LIFE_COLOR_FADE:
|
case GAME_OF_LIFE_COLOR_FADE:
|
||||||
mode = new GameOfLife(&display, COLOR_FADE);
|
mode = new GameOfLife(display, COLOR_FADE);
|
||||||
break;
|
break;
|
||||||
case GAME_OF_LIFE_RANDOM_COLOR:
|
case GAME_OF_LIFE_RANDOM_COLOR:
|
||||||
mode = new GameOfLife(&display, RANDOM_COLOR);
|
mode = new GameOfLife(display, RANDOM_COLOR);
|
||||||
break;
|
break;
|
||||||
case PONG:
|
case PONG:
|
||||||
mode = new Pong(&display);
|
mode = new Pong(display);
|
||||||
break;
|
break;
|
||||||
case SPACE_INVADERS:
|
case SPACE_INVADERS:
|
||||||
mode = new SpaceInvaders(&display);
|
mode = new SpaceInvaders(display);
|
||||||
break;
|
break;
|
||||||
case NEW_YEAR:
|
case NEW_YEAR:
|
||||||
mode = new NewYear(&display);
|
mode = new NewYear(display);
|
||||||
break;
|
break;
|
||||||
case STARFIELD:
|
case STARFIELD:
|
||||||
mode = new Starfield(&display);
|
mode = new Starfield(display);
|
||||||
|
break;
|
||||||
|
case MATRIX:
|
||||||
|
mode = new Matrix(display);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Serial.print("No mode loaded.\n");
|
Serial.print("No mode loaded.\n");
|
||||||
@ -104,5 +112,5 @@ void mode_step() {
|
|||||||
auto currentMicros = (int64_t) micros();
|
auto currentMicros = (int64_t) micros();
|
||||||
microseconds_t dt = (microseconds_t) min(1000000.0, max(1.0, (double) (currentMicros - lastMicros) * config.speed));
|
microseconds_t dt = (microseconds_t) min(1000000.0, max(1.0, (double) (currentMicros - lastMicros) * config.speed));
|
||||||
lastMicros = currentMicros;
|
lastMicros = currentMicros;
|
||||||
mode->step(dt);
|
mode->loop(dt);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,30 +1,32 @@
|
|||||||
#ifndef TEST_H
|
#ifndef MODE_BORDER_H
|
||||||
#define TEST_H
|
#define MODE_BORDER_H
|
||||||
|
|
||||||
#include "mode/Mode.h"
|
#include "mode/Mode.h"
|
||||||
|
|
||||||
class Border : public Mode<Border> {
|
class Border : public Mode {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit Border(Display *display) :
|
explicit Border(Display &display) :
|
||||||
Mode(display) {
|
Mode(display) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
~Border() override = default;
|
|
||||||
|
|
||||||
const char *getName() override {
|
const char *getName() override {
|
||||||
return "Border";
|
return "Border";
|
||||||
}
|
}
|
||||||
|
|
||||||
void doStep(microseconds_t dt) override {
|
protected:
|
||||||
for (int y = 0; y < display->height; y++) {
|
|
||||||
for (int x = 0; x < display->width; x++) {
|
void draw(Display &display) override {
|
||||||
if (x == 0 || x == display->width - 1 || y == 0 || y == display->height - 1) {
|
Serial.print("[BORDER] draw\n");
|
||||||
display->set(x, y, WHITE);
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
Serial.printf("[BORDER] draw %d / %d\n", x, y);
|
||||||
|
if (x == 0 || x == width - 1 || y == 0 || y == height - 1) {
|
||||||
|
display.set(x, y, WHITE);
|
||||||
} else {
|
} else {
|
||||||
display->set(x, y, BLACK);
|
display.set(x, y, BLACK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,43 +1,44 @@
|
|||||||
#ifndef CLOCK_H
|
#ifndef MODE_CLOCK_H
|
||||||
#define CLOCK_H
|
#define MODE_CLOCK_H
|
||||||
|
|
||||||
#include "mode/Mode.h"
|
#include "mode/Mode.h"
|
||||||
|
|
||||||
class Clock : public Mode<Clock> {
|
class Clock : public Mode {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit Clock(Display *display) :
|
explicit Clock(Display &display) :
|
||||||
Mode(display) {
|
Mode(display) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
~Clock() override = default;
|
|
||||||
|
|
||||||
const char *getName() override {
|
const char *getName() override {
|
||||||
return "Clock";
|
return "Clock";
|
||||||
}
|
}
|
||||||
|
|
||||||
void doStep(microseconds_t dt) override {
|
protected:
|
||||||
tm info{};
|
|
||||||
time_t now;
|
void step(microseconds_t dt) override {
|
||||||
time(&now);
|
if (realtimeChanged) {
|
||||||
localtime_r(&now, &info);
|
markDirty();
|
||||||
bool ok = info.tm_year >= (2023 - 1900);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Display &display) override {
|
||||||
|
display.clear();
|
||||||
|
|
||||||
display->clear();
|
|
||||||
uint8_t x = 2;
|
uint8_t x = 2;
|
||||||
x += display->print(x, 1, ok ? info.tm_hour / 10 : 13, WHITE);
|
x += display.print(x, 1, realtimeOK ? now.tm_hour / 10 : 13, WHITE);
|
||||||
x++;
|
x++;
|
||||||
x += display->print(x, 1, ok ? info.tm_hour % 10 : 13, WHITE);
|
x += display.print(x, 1, realtimeOK ? now.tm_hour % 10 : 13, WHITE);
|
||||||
x += display->print(x, 1, 10, WHITE);
|
x += display.print(x, 1, 10, WHITE);
|
||||||
x += display->print(x, 1, ok ? info.tm_min / 10 : 13, WHITE);
|
x += display.print(x, 1, realtimeOK ? now.tm_min / 10 : 13, WHITE);
|
||||||
x++;
|
x++;
|
||||||
x += display->print(x, 1, ok ? info.tm_min % 10 : 13, WHITE);
|
x += display.print(x, 1, realtimeOK ? now.tm_min % 10 : 13, WHITE);
|
||||||
x += display->print(x, 1, 10, WHITE);
|
x += display.print(x, 1, 10, WHITE);
|
||||||
x += display->print(x, 1, ok ? info.tm_sec / 10 : 13, WHITE);
|
x += display.print(x, 1, realtimeOK ? now.tm_sec / 10 : 13, WHITE);
|
||||||
x++;
|
x++;
|
||||||
x += display->print(x, 1, ok ? info.tm_sec % 10 : 13, WHITE);
|
x += display.print(x, 1, realtimeOK ? now.tm_sec % 10 : 13, WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,7 +14,7 @@ public:
|
|||||||
Color color = BLACK;
|
Color color = BLACK;
|
||||||
|
|
||||||
void animate(microseconds_t dt) {
|
void animate(microseconds_t dt) {
|
||||||
// TODO "doStep" does not work as expected
|
// TODO fading does not work as expected
|
||||||
if (alive) {
|
if (alive) {
|
||||||
fade = doStep(fade, 0.0, 255.0, 200, +dt);
|
fade = doStep(fade, 0.0, 255.0, 200, +dt);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,20 +1,27 @@
|
|||||||
#ifndef GAMEOFLIFE_H
|
#ifndef MODE_GAME_OF_LIFE_H
|
||||||
#define GAMEOFLIFE_H
|
#define MODE_GAME_OF_LIFE_H
|
||||||
|
|
||||||
#include "mode/Mode.h"
|
#include "mode/Mode.h"
|
||||||
#include "display/Display.h"
|
|
||||||
#include "Cell.h"
|
#include "Cell.h"
|
||||||
|
|
||||||
enum ColorMode {
|
enum ColorMode {
|
||||||
BLACK_WHITE, GRAYSCALE, COLOR_FADE, RANDOM_COLOR
|
BLACK_WHITE, GRAYSCALE, COLOR_FADE, RANDOM_COLOR
|
||||||
};
|
};
|
||||||
|
|
||||||
class GameOfLife : public Mode<GameOfLife> {
|
class GameOfLife : public Mode {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ColorMode colorMode;
|
ColorMode colorMode;
|
||||||
|
|
||||||
|
size_t cellsSize;
|
||||||
|
|
||||||
|
Cell *cells;
|
||||||
|
|
||||||
|
Cell *cellsEnd;
|
||||||
|
|
||||||
|
Cell *next;
|
||||||
|
|
||||||
microseconds_t runtime = 0;
|
microseconds_t runtime = 0;
|
||||||
|
|
||||||
uint8_t steps = 0;
|
uint8_t steps = 0;
|
||||||
@ -25,21 +32,19 @@ private:
|
|||||||
|
|
||||||
uint8_t isSteadyCount = 0;
|
uint8_t isSteadyCount = 0;
|
||||||
|
|
||||||
Cell *cells;
|
|
||||||
|
|
||||||
Cell *next;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit GameOfLife(Display *display, ColorMode colorMode) :
|
explicit GameOfLife(Display &display, ColorMode colorMode) :
|
||||||
Mode(display),
|
Mode(display),
|
||||||
colorMode(colorMode) {
|
colorMode(colorMode),
|
||||||
cells = (Cell *) malloc(display->pixelCount * sizeof(Cell));
|
cellsSize(display.pixelCount * sizeof(Cell)) {
|
||||||
for (Cell *cell = cells; cell < cells + display->pixelCount; cell++) {
|
cells = (Cell *) malloc(cellsSize);
|
||||||
|
cellsEnd = cells + cellsSize;
|
||||||
|
for (Cell *cell = cells; cell < cells + display.pixelCount; cell++) {
|
||||||
kill(cell);
|
kill(cell);
|
||||||
}
|
}
|
||||||
next = (Cell *) malloc(display->pixelCount * sizeof(Cell));
|
next = (Cell *) malloc(cellsSize);
|
||||||
for (Cell *cell = next; cell < next + display->pixelCount; cell++) {
|
for (Cell *cell = next; cell < next + display.pixelCount; cell++) {
|
||||||
kill(cell);
|
kill(cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,7 +74,9 @@ public:
|
|||||||
return "???";
|
return "???";
|
||||||
}
|
}
|
||||||
|
|
||||||
void doStep(microseconds_t dt) override {
|
protected:
|
||||||
|
|
||||||
|
void step(microseconds_t dt) override {
|
||||||
runtime += dt;
|
runtime += dt;
|
||||||
if (runtime >= 500000) {
|
if (runtime >= 500000) {
|
||||||
runtime = 0;
|
runtime = 0;
|
||||||
@ -85,14 +92,45 @@ public:
|
|||||||
nextGeneration();
|
nextGeneration();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
animate(dt);
|
for (Cell *cell = cells; cell < cellsEnd; cell++) {
|
||||||
|
cell->animate(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO don't always markDirty. Be more efficient
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Display &display) override {
|
||||||
|
Cell *cell = cells;
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
uint8_t brightness;
|
||||||
|
switch (colorMode) {
|
||||||
|
case BLACK_WHITE:
|
||||||
|
brightness = cell->alive ? 255 : 0;
|
||||||
|
display.set(x, y, gray(brightness));
|
||||||
|
break;
|
||||||
|
case GRAYSCALE:
|
||||||
|
brightness = (uint8_t) cell->fade;
|
||||||
|
display.set(x, y, gray(brightness));
|
||||||
|
break;
|
||||||
|
case COLOR_FADE:
|
||||||
|
display.set(x, y, {cell->getR(), cell->getG(), cell->getB()});
|
||||||
|
break;
|
||||||
|
case RANDOM_COLOR:
|
||||||
|
display.set(x, y, cell->alive ? cell->color : BLACK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cell++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void randomFill() {
|
void randomFill() {
|
||||||
isSteadyCount = 0;
|
isSteadyCount = 0;
|
||||||
for (Cell *cell = cells; cell < cells + display->pixelCount; cell++) {
|
for (Cell *cell = cells; cell < cellsEnd; cell++) {
|
||||||
if (random(4) == 0) {
|
if (random(4) == 0) {
|
||||||
if (!cell->alive) {
|
if (!cell->alive) {
|
||||||
spawn(cell);
|
spawn(cell);
|
||||||
@ -117,11 +155,11 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void nextGeneration() {
|
void nextGeneration() {
|
||||||
memcpy(next, cells, display->pixelCount * sizeof(Cell));
|
memcpy(next, cells, cellsSize);
|
||||||
Cell *src = cells;
|
Cell *src = cells;
|
||||||
Cell *dst = next;
|
Cell *dst = next;
|
||||||
for (int y = 0; y < display->height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < display->width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
uint8_t around = countAround(x, y);
|
uint8_t around = countAround(x, y);
|
||||||
if (src->alive) {
|
if (src->alive) {
|
||||||
if (around <= 2 || 6 <= around) {
|
if (around <= 2 || 6 <= around) {
|
||||||
@ -134,14 +172,14 @@ private:
|
|||||||
dst++;
|
dst++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memcpy(cells, next, display->pixelCount * sizeof(Cell));
|
memcpy(cells, next, cellsSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print() {
|
void print() {
|
||||||
Cell *cell = cells;
|
Cell *cell = cells;
|
||||||
for (int y = 0; y < display->height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
Serial.print("|");
|
Serial.print("|");
|
||||||
for (int x = 0; x < display->width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
Serial.print(cell->alive ? "x|" : " |");
|
Serial.print(cell->alive ? "x|" : " |");
|
||||||
cell++;
|
cell++;
|
||||||
}
|
}
|
||||||
@ -150,33 +188,6 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void animate(microseconds_t dt) {
|
|
||||||
Cell *cell = cells;
|
|
||||||
for (int y = 0; y < display->height; y++) {
|
|
||||||
for (int x = 0; x < display->width; x++) {
|
|
||||||
cell->animate(dt);
|
|
||||||
uint8_t brightness;
|
|
||||||
switch (colorMode) {
|
|
||||||
case BLACK_WHITE:
|
|
||||||
brightness = cell->alive ? 255 : 0;
|
|
||||||
display->set(x, y, gray(brightness));
|
|
||||||
break;
|
|
||||||
case GRAYSCALE:
|
|
||||||
brightness = (uint8_t) cell->fade;
|
|
||||||
display->set(x, y, gray(brightness));
|
|
||||||
break;
|
|
||||||
case COLOR_FADE:
|
|
||||||
display->set(x, y, {cell->getR(), cell->getG(), cell->getB()});
|
|
||||||
break;
|
|
||||||
case RANDOM_COLOR:
|
|
||||||
display->set(x, y, cell->alive ? cell->color : BLACK);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cell++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t countAround(int x, int y) {
|
uint8_t countAround(int x, int y) {
|
||||||
return countIfAlive(x - 1, y - 1) + countIfAlive(x + 0, y - 1) + countIfAlive(x + 1, y - 1) +
|
return countIfAlive(x - 1, y - 1) + countIfAlive(x + 0, y - 1) + countIfAlive(x + 1, y - 1) +
|
||||||
countIfAlive(x - 1, y + 0) + /* */ countIfAlive(x + 1, y + 0) +
|
countIfAlive(x - 1, y + 0) + /* */ countIfAlive(x + 1, y + 0) +
|
||||||
@ -184,15 +195,16 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t countIfAlive(int x, int y) {
|
uint8_t countIfAlive(int x, int y) {
|
||||||
if (x < 0 || y < 0 || x >= display->width || y >= display->height) {
|
if (x < 0 || y < 0 || x >= width || y >= height) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return get(x, y)->alive ? 1 : 0;
|
return get(x, y)->alive ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cell *get(int x, int y) {
|
Cell *get(int x, int y) {
|
||||||
return cells + display->width * y + x;
|
return cells + width * y + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
118
src/mode/Mode.h
118
src/mode/Mode.h
@ -1,12 +1,8 @@
|
|||||||
#ifndef MODE_H
|
#ifndef MODE_H
|
||||||
#define MODE_H
|
#define MODE_H
|
||||||
|
|
||||||
#define TIMER_COUNT 10
|
|
||||||
|
|
||||||
#include "BASICS.h"
|
#include "BASICS.h"
|
||||||
#include "display/Display.h"
|
#include "display/Display.h"
|
||||||
#include "Timer.h"
|
|
||||||
#include "ModeBase.h"
|
|
||||||
|
|
||||||
enum ModeId {
|
enum ModeId {
|
||||||
NONE,
|
NONE,
|
||||||
@ -20,25 +16,121 @@ enum ModeId {
|
|||||||
SPACE_INVADERS,
|
SPACE_INVADERS,
|
||||||
NEW_YEAR,
|
NEW_YEAR,
|
||||||
STARFIELD,
|
STARFIELD,
|
||||||
|
MATRIX,
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
class Mode {
|
||||||
class Mode : public ModeBase {
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct Timer {
|
||||||
|
millis_t interval;
|
||||||
|
millis_t last;
|
||||||
|
};
|
||||||
|
|
||||||
|
Display &_display;
|
||||||
|
|
||||||
|
bool dirty = true;
|
||||||
|
|
||||||
|
Timer timers[2] = {
|
||||||
|
{0, 0},
|
||||||
|
{0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
static T *instance;
|
const uint8_t width;
|
||||||
|
|
||||||
|
const uint8_t height;
|
||||||
|
|
||||||
|
bool realtimeOK = false;
|
||||||
|
|
||||||
|
bool realtimeChanged = false;
|
||||||
|
|
||||||
|
tm now = {0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
time_t epoch = 0;
|
||||||
|
|
||||||
|
virtual void tick(uint8_t index, millis_t dt) {};
|
||||||
|
|
||||||
|
virtual void step(microseconds_t dt) {};
|
||||||
|
|
||||||
|
virtual void draw(Display &display) {};
|
||||||
|
|
||||||
|
void timer(uint8_t index, millis_t interval) {
|
||||||
|
if (index >= countof(timers)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timers[index].interval = interval;
|
||||||
|
timers[index].last = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void markDirty() {
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit Mode(Display *display) :
|
explicit Mode(Display &display) :
|
||||||
ModeBase(display) {
|
_display(display),
|
||||||
instance = (T *) this;
|
width(display.width),
|
||||||
|
height(display.height) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Mode() = default;
|
||||||
|
|
||||||
|
virtual const char *getName() = 0;
|
||||||
|
|
||||||
|
void loop(microseconds_t dt) {
|
||||||
|
Serial.print("[MODE] realtime\n");
|
||||||
|
realtime();
|
||||||
|
|
||||||
|
Serial.print("[MODE] handleTimers\n");
|
||||||
|
handleTimers();
|
||||||
|
|
||||||
|
Serial.print("[MODE] step\n");
|
||||||
|
step(dt);
|
||||||
|
|
||||||
|
if (dirty) {
|
||||||
|
dirty = false;
|
||||||
|
Serial.print("[MODE] draw\n");
|
||||||
|
draw(_display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void realtime() {
|
||||||
|
time_t tmp;
|
||||||
|
time(&tmp);
|
||||||
|
realtimeOK = tmp > 1600000000;
|
||||||
|
if (realtimeOK) {
|
||||||
|
realtimeChanged = epoch != tmp;
|
||||||
|
if (realtimeChanged) {
|
||||||
|
epoch = tmp;
|
||||||
|
localtime_r(&tmp, &now);
|
||||||
|
now.tm_year += 1900;
|
||||||
|
now.tm_mon += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
realtimeChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleTimers() {
|
||||||
|
millis_t ms = millis();
|
||||||
|
for (Timer *timer = timers; timer < timers + sizeof(timers); timer++) {
|
||||||
|
if (timer->interval > 0) {
|
||||||
|
millis_t dt = ms - timer->last;
|
||||||
|
if (dt >= timer->interval) {
|
||||||
|
timer->last = ms;
|
||||||
|
tick(timer - timers, dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T *Mode<T>::instance = nullptr;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,91 +0,0 @@
|
|||||||
#ifndef MODE_BASE_H
|
|
||||||
#define MODE_BASE_H
|
|
||||||
|
|
||||||
#define TIMER_COUNT 10
|
|
||||||
|
|
||||||
#include "BASICS.h"
|
|
||||||
#include "display/Display.h"
|
|
||||||
#include "Timer.h"
|
|
||||||
|
|
||||||
class ModeBase {
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Timer *timers[TIMER_COUNT]{};
|
|
||||||
|
|
||||||
uint8_t timerCount = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
Display *display;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit ModeBase(Display *display) :
|
|
||||||
display(display) {
|
|
||||||
for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
|
|
||||||
*timer = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ModeBase() {
|
|
||||||
destroyAllTimers();
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual const char *getName() = 0;
|
|
||||||
|
|
||||||
void step(microseconds_t dt) {
|
|
||||||
for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
|
|
||||||
if (*timer != nullptr) {
|
|
||||||
(*timer)->step(dt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doStep(dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
virtual void doStep(microseconds_t dt) {};
|
|
||||||
|
|
||||||
Timer *createTimer(long long millisecondsInterval, Timer::Callback callback) {
|
|
||||||
for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
|
|
||||||
if (*timer == nullptr) {
|
|
||||||
*timer = new Timer(millisecondsInterval * 1000, callback);
|
|
||||||
timerCount++;
|
|
||||||
return *timer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.printf("ERROR: Cannot create more than %d timers!\n", TIMER_COUNT);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool destroyTimer(Timer *t) {
|
|
||||||
for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
|
|
||||||
if (*timer == t) {
|
|
||||||
_destroyTimer(timer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.printf("ERROR: No such timer.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroyAllTimers() {
|
|
||||||
for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
|
|
||||||
if (*timer != nullptr) {
|
|
||||||
_destroyTimer(timer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void _destroyTimer(Timer **timer) {
|
|
||||||
timerCount--;
|
|
||||||
free(*timer);
|
|
||||||
*timer = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -11,7 +11,9 @@ class Firework {
|
|||||||
INITIAL, RISE, EXPLODE, SPARKLE
|
INITIAL, RISE, EXPLODE, SPARKLE
|
||||||
};
|
};
|
||||||
|
|
||||||
Display *display{};
|
uint8_t width = 0;
|
||||||
|
|
||||||
|
uint8_t height = 0;
|
||||||
|
|
||||||
Color color = MAGENTA;
|
Color color = MAGENTA;
|
||||||
|
|
||||||
@ -31,17 +33,18 @@ class Firework {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void init(Display *_display) {
|
void init(Display &display) {
|
||||||
this->display = _display;
|
width = display.width;
|
||||||
|
height = display.height;
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
position = Vector((double) random(display->width), display->height);
|
position = Vector((double) random(width), height);
|
||||||
color = randomColor();
|
color = randomColor();
|
||||||
state = INITIAL;
|
state = INITIAL;
|
||||||
|
|
||||||
destinationHeight = display->height / 2.0 + (double) random(5) - 2;
|
destinationHeight = height / 2.0 + (double) random(5) - 2;
|
||||||
explosionRadius = (double) random(3) + 1;
|
explosionRadius = (double) random(3) + 1;
|
||||||
sparkleMax = 100;
|
sparkleMax = 100;
|
||||||
|
|
||||||
@ -49,12 +52,12 @@ public:
|
|||||||
sparkle = 0.0;
|
sparkle = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void launch() {
|
bool launch() {
|
||||||
if (state != INITIAL) {
|
if (state != INITIAL) {
|
||||||
Serial.println("ERROR: Cannot start Firework. Already started.");
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
state = RISE;
|
state = RISE;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void step(microseconds_t dt) {
|
void step(microseconds_t dt) {
|
||||||
@ -81,7 +84,7 @@ public:
|
|||||||
case INITIAL:
|
case INITIAL:
|
||||||
break;
|
break;
|
||||||
case RISE:
|
case RISE:
|
||||||
position.y = doStep(position.y, 0.0, display->height, 1000, -dt);
|
position.y = doStep(position.y, 0.0, height, 1000, -dt);
|
||||||
break;
|
break;
|
||||||
case EXPLODE:
|
case EXPLODE:
|
||||||
explosion = doStep(explosion, 0.0, explosionRadius, 500, +dt);
|
explosion = doStep(explosion, 0.0, explosionRadius, 500, +dt);
|
||||||
@ -92,42 +95,40 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw() {
|
void draw(Display display) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case INITIAL:
|
case INITIAL:
|
||||||
break;
|
break;
|
||||||
case RISE:
|
case RISE:
|
||||||
display->set(position, YELLOW);
|
display.set(position, YELLOW);
|
||||||
break;
|
break;
|
||||||
case EXPLODE:
|
case EXPLODE:
|
||||||
drawParticle(+0.0, +1.0);
|
drawParticle(display, +0.0, +1.0);
|
||||||
drawParticle(+0.7, +0.7);
|
drawParticle(display, +0.7, +0.7);
|
||||||
drawParticle(+1.0, +0.0);
|
drawParticle(display, +1.0, +0.0);
|
||||||
drawParticle(+0.7, -0.7);
|
drawParticle(display, +0.7, -0.7);
|
||||||
drawParticle(+0.0, -1.0);
|
drawParticle(display, +0.0, -1.0);
|
||||||
drawParticle(-0.7, -0.7);
|
drawParticle(display, -0.7, -0.7);
|
||||||
drawParticle(-1.0, +0.0);
|
drawParticle(display, -1.0, +0.0);
|
||||||
drawParticle(-0.7, +0.7);
|
drawParticle(display, -0.7, +0.7);
|
||||||
break;
|
break;
|
||||||
case SPARKLE:
|
case SPARKLE:
|
||||||
if (randomBool(2)) drawParticle(+0.0, +1.0);
|
if (randomBool(2)) drawParticle(display, +0.0, +1.0);
|
||||||
if (randomBool(2)) drawParticle(+0.7, +0.7);
|
if (randomBool(2)) drawParticle(display, +0.7, +0.7);
|
||||||
if (randomBool(2)) drawParticle(+1.0, +0.0);
|
if (randomBool(2)) drawParticle(display, +1.0, +0.0);
|
||||||
if (randomBool(2)) drawParticle(+0.7, -0.7);
|
if (randomBool(2)) drawParticle(display, +0.7, -0.7);
|
||||||
if (randomBool(2)) drawParticle(+0.0, -1.0);
|
if (randomBool(2)) drawParticle(display, +0.0, -1.0);
|
||||||
if (randomBool(2)) drawParticle(-0.7, -0.7);
|
if (randomBool(2)) drawParticle(display, -0.7, -0.7);
|
||||||
if (randomBool(2)) drawParticle(-1.0, +0.0);
|
if (randomBool(2)) drawParticle(display, -1.0, +0.0);
|
||||||
if (randomBool(2)) drawParticle(-0.7, +0.7);
|
if (randomBool(2)) drawParticle(display, -0.7, +0.7);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawParticle(double x, double y) {
|
private:
|
||||||
display->set(position.plus(x * explosion, y * explosion), color);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isAlive() {
|
void drawParticle(Display display, double x, double y) {
|
||||||
return state != INITIAL;
|
display.set(position.plus(x * explosion, y * explosion), color);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,145 +1,225 @@
|
|||||||
#ifndef NEWYEAR_H
|
#ifndef MODE_NEW_YEAR_H
|
||||||
#define NEWYEAR_H
|
#define MODE_NEW_YEAR_H
|
||||||
|
|
||||||
#define MAX_FIREWORKS 10
|
#define MAX_FIREWORKS 10
|
||||||
|
|
||||||
#include "mode/Mode.h"
|
#include "mode/Mode.h"
|
||||||
#include "Firework.h"
|
#include "Firework.h"
|
||||||
|
|
||||||
class NewYear : public Mode<NewYear> {
|
class NewYear : public Mode {
|
||||||
|
|
||||||
Firework fireworksBegin[MAX_FIREWORKS];
|
private:
|
||||||
Firework *fireworksEnd = fireworksBegin + MAX_FIREWORKS;
|
|
||||||
|
|
||||||
void launch(Timer *timer, uint32_t counter, uint32_t currentCount) {
|
Firework fireworks[MAX_FIREWORKS];
|
||||||
for (Firework *firework = fireworksBegin; firework < fireworksEnd; firework++) {
|
|
||||||
if (!firework->isAlive()) {
|
int8_t lastSecond = -1;
|
||||||
firework->launch();
|
|
||||||
|
millis_t lastSecondMillis = 0;
|
||||||
|
|
||||||
|
uint8_t days = 0;
|
||||||
|
|
||||||
|
uint8_t level = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit NewYear(Display &display) :
|
||||||
|
Mode(display) {
|
||||||
|
timer(0, 500);
|
||||||
|
for (auto &firework: fireworks) {
|
||||||
|
firework.init(display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getName() override {
|
||||||
|
return "NewYear";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void step(microseconds_t dt) override {
|
||||||
|
if (!realtimeOK) {
|
||||||
|
setMode(NO_TIME);
|
||||||
|
} else if (now.tm_mon != 1 || now.tm_mday != 1 || now.tm_hour != 0) {
|
||||||
|
days = getDayCountForYear(now.tm_year) - now.tm_yday - 1;
|
||||||
|
if (days == 0) {
|
||||||
|
loopLastDay();
|
||||||
|
} else {
|
||||||
|
loopMultipleDays();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setMode(FIREWORK);
|
||||||
|
for (auto &firework: fireworks) {
|
||||||
|
firework.step(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopLastDay() {
|
||||||
|
setMode(LAST_DAY);
|
||||||
|
int levelTmp = getLevel();
|
||||||
|
if (level != levelTmp) {
|
||||||
|
level = levelTmp;
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopMultipleDays() {
|
||||||
|
setMode(MULTIPLE_DAYS);
|
||||||
|
if (realtimeChanged) {
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick(uint8_t index, millis_t dt) override {
|
||||||
|
launch();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Display &display) override {
|
||||||
|
display.clear();
|
||||||
|
if (realtimeOK) {
|
||||||
|
drawNoTime(display);
|
||||||
|
} else if (now.tm_mon == 1 && now.tm_mday == 1 && now.tm_hour == 0) {
|
||||||
|
drawYear(display, now.tm_year + 1900);
|
||||||
|
drawFirework(display);
|
||||||
|
} else {
|
||||||
|
drawCountdown(display, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
NO_TIME,
|
||||||
|
MULTIPLE_DAYS,
|
||||||
|
LAST_DAY,
|
||||||
|
FIREWORK,
|
||||||
|
};
|
||||||
|
|
||||||
|
State _state = NO_TIME;
|
||||||
|
|
||||||
|
void setMode(State state) {
|
||||||
|
if (_state != state) {
|
||||||
|
_state = state;
|
||||||
|
markDirty();
|
||||||
|
timer(0, 0);
|
||||||
|
switch (state) {
|
||||||
|
case LAST_DAY:
|
||||||
|
lastSecond = -1;
|
||||||
|
break;
|
||||||
|
case FIREWORK:
|
||||||
|
timer(0, 500);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLevel() {
|
||||||
|
if (lastSecond < 0 || lastSecond != now.tm_sec) {
|
||||||
|
lastSecond = (int8_t) now.tm_sec;
|
||||||
|
lastSecondMillis = millis();
|
||||||
|
}
|
||||||
|
millis_t mils = millis() - lastSecondMillis;
|
||||||
|
int levelTmp = (int) round(32 * mils / 1000.0);
|
||||||
|
return levelTmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void launch() {
|
||||||
|
for (auto &firework: fireworks) {
|
||||||
|
if (firework.launch()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int lastSecond = -1;
|
void drawCountdown(Display &display, const tm &now) {
|
||||||
|
uint8_t hours = (24 - now.tm_hour - (now.tm_min > 0 || now.tm_sec > 0 ? 1 : 0));
|
||||||
unsigned long lastSecondMillis = 0;
|
uint8_t minutes = (60 - now.tm_min - (now.tm_sec > 0 ? 1 : 0)) % 60;
|
||||||
|
uint8_t seconds = (60 - now.tm_sec) % 60;
|
||||||
void step(Timer *timer, uint32_t counter, uint32_t currentCount) {
|
|
||||||
tm info{};
|
|
||||||
time_t now;
|
|
||||||
time(&now);
|
|
||||||
localtime_r(&now, &info);
|
|
||||||
info.tm_year += 1900;
|
|
||||||
info.tm_mon += 1;
|
|
||||||
bool ok = info.tm_year >= 2023;
|
|
||||||
|
|
||||||
display->clear();
|
|
||||||
if (!ok) {
|
|
||||||
drawNoTime();
|
|
||||||
} else if (info.tm_mon == 1 && info.tm_mday == 1 && info.tm_hour == 0) {
|
|
||||||
drawYear(counter, info.tm_year + 1900);
|
|
||||||
drawFirework(timer->interval);
|
|
||||||
} else {
|
|
||||||
drawCountdown(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawNoTime() {
|
|
||||||
uint8_t x = 2;
|
|
||||||
x += display->print(x, 1, 13, WHITE);
|
|
||||||
x++;
|
|
||||||
x += display->print(x, 1, 13, WHITE);
|
|
||||||
x += display->print(x, 1, 10, WHITE);
|
|
||||||
x += display->print(x, 1, 13, WHITE);
|
|
||||||
x++;
|
|
||||||
x += display->print(x, 1, 13, WHITE);
|
|
||||||
x += display->print(x, 1, 10, WHITE);
|
|
||||||
x += display->print(x, 1, 13, WHITE);
|
|
||||||
x++;
|
|
||||||
x += display->print(x, 1, 13, WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawCountdown(const tm &time) {
|
|
||||||
int days = getDayCountForYear(time.tm_year) - time.tm_yday - 1;
|
|
||||||
|
|
||||||
int hours = (24 - time.tm_hour - (time.tm_min > 0 || time.tm_sec > 0 ? 1 : 0));
|
|
||||||
int minutes = (60 - time.tm_min - (time.tm_sec > 0 ? 1 : 0)) % 60;
|
|
||||||
int seconds = (60 - time.tm_sec) % 60;
|
|
||||||
|
|
||||||
uint8_t x = 0;
|
uint8_t x = 0;
|
||||||
|
|
||||||
if (days > 0) {
|
if (days > 0) {
|
||||||
drawDay(days, &x);
|
drawDay(display, days, &x);
|
||||||
x += display->print(x, 1, 10, WHITE);
|
x += display.print(x, 1, 10, WHITE);
|
||||||
} else {
|
} else {
|
||||||
x += 2;
|
x += 2;
|
||||||
}
|
}
|
||||||
drawHour(days, hours, &x);
|
drawHour(display, days, hours, &x);
|
||||||
x += display->print(x, 1, 10, WHITE);
|
x += display.print(x, 1, 10, WHITE);
|
||||||
draw2Digit(minutes, &x);
|
draw2Digit(display, minutes, &x);
|
||||||
if (days <= 0) {
|
if (days <= 0) {
|
||||||
x += display->print(x, 1, 10, WHITE);
|
x += display.print(x, 1, 10, WHITE);
|
||||||
draw2Digit(seconds, &x);
|
draw2Digit(display, seconds, &x);
|
||||||
drawSubSecondsBar(time.tm_sec);
|
drawSubSecondsBar(display);
|
||||||
} else {
|
} else {
|
||||||
drawSecondsBar(seconds);
|
drawSecondsBar(display, seconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawDay(int days, uint8_t *x) {
|
static void drawNoTime(Display &display) {
|
||||||
|
uint8_t x = 2;
|
||||||
|
x += display.print(x, 1, 13, WHITE);
|
||||||
|
x++;
|
||||||
|
x += display.print(x, 1, 13, WHITE);
|
||||||
|
x += display.print(x, 1, 10, WHITE);
|
||||||
|
x += display.print(x, 1, 13, WHITE);
|
||||||
|
x++;
|
||||||
|
x += display.print(x, 1, 13, WHITE);
|
||||||
|
x += display.print(x, 1, 10, WHITE);
|
||||||
|
x += display.print(x, 1, 13, WHITE);
|
||||||
|
x++;
|
||||||
|
x += display.print(x, 1, 13, WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawDay(Display &display, int days, uint8_t *x) {
|
||||||
if (days > 100) {
|
if (days > 100) {
|
||||||
*x += display->print(*x, 1, days / 100, WHITE);
|
*x += display.print(*x, 1, days / 100, WHITE);
|
||||||
} else {
|
} else {
|
||||||
*x += 3;
|
*x += 3;
|
||||||
}
|
}
|
||||||
(*x)++;
|
(*x)++;
|
||||||
|
|
||||||
if (days > 10) {
|
if (days > 10) {
|
||||||
*x += display->print(*x, 1, days / 10 % 10, WHITE);
|
*x += display.print(*x, 1, days / 10 % 10, WHITE);
|
||||||
} else {
|
} else {
|
||||||
*x += 3;
|
*x += 3;
|
||||||
}
|
}
|
||||||
(*x)++;
|
(*x)++;
|
||||||
|
|
||||||
*x += display->print(*x, 1, days % 10, WHITE);
|
*x += display.print(*x, 1, days % 10, WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawHour(int days, int hours, uint8_t *x) {
|
static void drawHour(Display &display, int days, int hours, uint8_t *x) {
|
||||||
if (days > 0 || hours >= 10) {
|
if (days > 0 || hours >= 10) {
|
||||||
*x += display->print(*x, 1, hours / 10, WHITE);
|
*x += display.print(*x, 1, hours / 10, WHITE);
|
||||||
} else {
|
} else {
|
||||||
*x += 3;
|
*x += 3;
|
||||||
}
|
}
|
||||||
(*x)++;
|
(*x)++;
|
||||||
*x += display->print(*x, 1, hours % 10, WHITE);
|
*x += display.print(*x, 1, hours % 10, WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw2Digit(int value, uint8_t *x) {
|
static void draw2Digit(Display &display, int value, uint8_t *x) {
|
||||||
*x += display->print(*x, 1, value / 10, WHITE);
|
*x += display.print(*x, 1, value / 10, WHITE);
|
||||||
(*x)++;
|
(*x)++;
|
||||||
*x += display->print(*x, 1, value % 10, WHITE);
|
*x += display.print(*x, 1, value % 10, WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawSecondsBar(int seconds) {
|
static void drawSecondsBar(Display &display, int seconds) {
|
||||||
for (int pos = 0; pos < 30; pos++) {
|
for (int pos = 0; pos < 30; pos++) {
|
||||||
if (pos <= seconds - 30) {
|
if (pos <= seconds - 30) {
|
||||||
display->set(pos + 1, 7, GREEN);
|
display.set(pos + 1, 7, GREEN);
|
||||||
} else if (pos <= seconds) {
|
} else if (pos <= seconds) {
|
||||||
display->set(pos + 1, 7, RED);
|
display.set(pos + 1, 7, RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawSubSecondsBar(const int second) {
|
void drawSubSecondsBar(Display &display) const {
|
||||||
if (lastSecond < 0 || lastSecond != second) {
|
|
||||||
lastSecond = second;
|
|
||||||
lastSecondMillis = millis();
|
|
||||||
}
|
|
||||||
unsigned long mils = millis() - lastSecondMillis;
|
|
||||||
int level = (int) round(32 * mils / 1000.0);
|
|
||||||
for (int pos = 0; pos < 32; pos++) {
|
for (int pos = 0; pos < 32; pos++) {
|
||||||
if (pos < 32 - level) {
|
if (pos < 32 - level) {
|
||||||
display->set(pos, 7, GREEN);
|
display.set(pos, 7, GREEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,42 +232,22 @@ class NewYear : public Mode<NewYear> {
|
|||||||
return 365;
|
return 365;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawYear(uint32_t counter, int year) {
|
static void drawYear(Display &display, int year) {
|
||||||
uint8_t x = 8;
|
uint8_t x = 8;
|
||||||
x += display->print(x, 1, year / 1000 % 10, counter % 64 != 0 ? WHITE : BLACK);
|
x += display.print(x, 1, year / 1000 % 10, WHITE);
|
||||||
x++;
|
x++;
|
||||||
x += display->print(x, 1, year / 100 % 10, counter % 64 != 1 ? WHITE : BLACK);
|
x += display.print(x, 1, year / 100 % 10, WHITE);
|
||||||
x++;
|
x++;
|
||||||
x += display->print(x, 1, year / 10 % 10, counter % 64 != 2 ? WHITE : BLACK);
|
x += display.print(x, 1, year / 10 % 10, WHITE);
|
||||||
x++;
|
x++;
|
||||||
x += display->print(x, 1, year / 1 % 10, counter % 64 != 3 ? WHITE : BLACK);
|
x += display.print(x, 1, year / 1 % 10, WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawFirework(microseconds_t dt) const {
|
void drawFirework(Display &display) {
|
||||||
for (auto *firework = (Firework *) fireworksBegin; firework < fireworksEnd; firework++) {
|
for (auto &firework: fireworks) {
|
||||||
if (firework->isAlive()) {
|
firework.draw(display);
|
||||||
firework->step(dt);
|
|
||||||
firework->draw();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit NewYear(Display *display) :
|
|
||||||
Mode(display) {
|
|
||||||
createTimer(500, [](Timer *timer, uint32_t counter, uint32_t currentCount) { instance->launch(timer, counter, currentCount); });
|
|
||||||
createTimer(50, [](Timer *timer, uint32_t counter, uint32_t currentCount) { instance->step(timer, counter, currentCount); });
|
|
||||||
for (Firework *firework = fireworksBegin; firework < fireworksEnd; firework++) {
|
|
||||||
firework->init(display);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~NewYear() override = default;
|
|
||||||
|
|
||||||
const char *getName() override {
|
|
||||||
return "NewYear";
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
#ifndef PONG_H
|
#ifndef MODE_PONG_H
|
||||||
#define PONG_H
|
#define MODE_PONG_H
|
||||||
|
|
||||||
#include "mode/Mode.h"
|
#include "mode/Mode.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "display/Vector.h"
|
#include "display/Vector.h"
|
||||||
|
|
||||||
class Pong : public Mode<Pong> {
|
class Pong : public Mode {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -25,34 +25,45 @@ private:
|
|||||||
|
|
||||||
microseconds_t timeout = 0;
|
microseconds_t timeout = 0;
|
||||||
|
|
||||||
void resetPlayer() {
|
public:
|
||||||
player0.size = 3;
|
|
||||||
player0.score = 0;
|
explicit Pong(Display &display) :
|
||||||
player0.position = (display->height - player0.size) / 2;
|
Mode(display),
|
||||||
player1.size = 3;
|
position(width / 2.0, height / 2.0),
|
||||||
player1.score = 0;
|
velocity(Vector::polar(random(360), exp10(1))) {
|
||||||
player1.position = (display->height - player1.size) / 2;
|
timer(0, 100);
|
||||||
|
spawnBall(random(2) == 0 ? -1 : +1);
|
||||||
|
resetPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void tick(Timer *timer, uint32_t totalCount, uint32_t currentCount) {
|
const char *getName() override {
|
||||||
|
return "Pong";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void tick(uint8_t index, millis_t dt) override {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case SCORE:
|
case SCORE:
|
||||||
timeout -= currentCount * timer->interval;
|
timeout -= dt;
|
||||||
if (timeout <= 0) {
|
if (timeout <= 0) {
|
||||||
status = PLAY;
|
status = PLAY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PLAY:
|
case PLAY:
|
||||||
position = position.plus(velocity);
|
position = position.plus(velocity);
|
||||||
player0.randomMove(display->height);
|
player0.randomMove(height);
|
||||||
player1.randomMove(display->height);
|
player1.randomMove(height);
|
||||||
paddleBounce();
|
paddleBounce();
|
||||||
checkScoring();
|
checkScoring();
|
||||||
topBottomBounce();
|
topBottomBounce();
|
||||||
draw();
|
|
||||||
|
// TODO don't always markDirty. Be more efficient
|
||||||
|
markDirty();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case OVER:
|
case OVER:
|
||||||
timeout -= currentCount * timer->interval;
|
timeout -= dt;
|
||||||
if (timeout <= 0) {
|
if (timeout <= 0) {
|
||||||
resetPlayer();
|
resetPlayer();
|
||||||
status = SCORE;
|
status = SCORE;
|
||||||
@ -62,15 +73,54 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void draw(Display &display) override {
|
||||||
|
display.clear();
|
||||||
|
switch (status) {
|
||||||
|
case SCORE:
|
||||||
|
display.print(1, 1, player0.score, GREEN);
|
||||||
|
display.print(width - 1 - DISPLAY_CHAR_WIDTH, 1, player1.score, RED);
|
||||||
|
break;
|
||||||
|
case PLAY:
|
||||||
|
for (int i = 0; i < player0.size; ++i) {
|
||||||
|
display.set(1, (uint8_t) round(player0.position) + i, GREEN);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < player1.size; ++i) {
|
||||||
|
display.set(width - 2, (uint8_t) round(player1.position) + i, RED);
|
||||||
|
}
|
||||||
|
display.set((uint8_t) round(position.x), (uint8_t) round(position.y), WHITE);
|
||||||
|
break;
|
||||||
|
case OVER:
|
||||||
|
if (player0.score > player1.score) {
|
||||||
|
display.print(1, 1, 11, GREEN);
|
||||||
|
display.print(width - 1 - DISPLAY_CHAR_WIDTH, 1, 12, RED);
|
||||||
|
} else if (player0.score < player1.score) {
|
||||||
|
display.print(1, 1, 12, RED);
|
||||||
|
display.print(width - 1 - DISPLAY_CHAR_WIDTH, 1, 11, GREEN);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void resetPlayer() {
|
||||||
|
player0.size = 3;
|
||||||
|
player0.score = 0;
|
||||||
|
player0.position = (height - player0.size) / 2;
|
||||||
|
player1.size = 3;
|
||||||
|
player1.score = 0;
|
||||||
|
player1.position = (height - player1.size) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
void topBottomBounce() {
|
void topBottomBounce() {
|
||||||
position = position.bounceInBox(display->width, display->height);
|
position = position.bounceInBox(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkScoring() {
|
void checkScoring() {
|
||||||
if (position.x < 0) {
|
if (position.x < 0) {
|
||||||
player1.score++;
|
player1.score++;
|
||||||
spawnBall(+1);
|
spawnBall(+1);
|
||||||
} else if (position.x >= display->width) {
|
} else if (position.x >= width) {
|
||||||
player0.score++;
|
player0.score++;
|
||||||
spawnBall(-1);
|
spawnBall(-1);
|
||||||
}
|
}
|
||||||
@ -84,67 +134,21 @@ private:
|
|||||||
if (position.x == 1 && player0.position <= position.y && position.y < player0.position + player0.size) {
|
if (position.x == 1 && player0.position <= position.y && position.y < player0.position + player0.size) {
|
||||||
velocity.x = -velocity.x;
|
velocity.x = -velocity.x;
|
||||||
position.x = 3;
|
position.x = 3;
|
||||||
} else if (position.x == display->width - 2 && player1.position <= position.y && position.y < player1.position + player1.size) {
|
} else if (position.x == width - 2 && player1.position <= position.y && position.y < player1.position + player1.size) {
|
||||||
velocity.x = -velocity.x;
|
velocity.x = -velocity.x;
|
||||||
position.x = display->width - 4;
|
position.x = width - 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void spawnBall(int direction) {
|
void spawnBall(int direction) {
|
||||||
position.x = (double) display->width / 2.0;
|
position.x = (double) width / 2.0;
|
||||||
position.y = (double) display->height / 2.0;
|
position.y = (double) height / 2.0;
|
||||||
velocity.x = direction;
|
velocity.x = direction;
|
||||||
velocity.y = 0;
|
velocity.y = 0;
|
||||||
status = SCORE;
|
status = SCORE;
|
||||||
timeout = 2000000;
|
timeout = 2000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw() {
|
|
||||||
display->clear();
|
|
||||||
switch (status) {
|
|
||||||
case SCORE:
|
|
||||||
display->print(1, 1, player0.score, GREEN);
|
|
||||||
display->print(display->width - 1 - DISPLAY_CHAR_WIDTH, 1, player1.score, RED);
|
|
||||||
break;
|
|
||||||
case PLAY:
|
|
||||||
for (int i = 0; i < player0.size; ++i) {
|
|
||||||
display->set(1, (uint8_t) round(player0.position) + i, GREEN);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < player1.size; ++i) {
|
|
||||||
display->set(display->width - 2, (uint8_t) round(player1.position) + i, RED);
|
|
||||||
}
|
|
||||||
display->set((uint8_t) round(position.x), (uint8_t) round(position.y), WHITE);
|
|
||||||
break;
|
|
||||||
case OVER:
|
|
||||||
if (player0.score > player1.score) {
|
|
||||||
display->print(1, 1, 11, GREEN);
|
|
||||||
display->print(display->width - 1 - DISPLAY_CHAR_WIDTH, 1, 12, RED);
|
|
||||||
} else if (player0.score < player1.score) {
|
|
||||||
display->print(1, 1, 12, RED);
|
|
||||||
display->print(display->width - 1 - DISPLAY_CHAR_WIDTH, 1, 11, GREEN);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit Pong(Display *display) :
|
|
||||||
Mode(display),
|
|
||||||
position(display->width / 2.0, display->height / 2.0),
|
|
||||||
velocity(Vector::polar(random(360), exp10(1))) {
|
|
||||||
instance = this;
|
|
||||||
createTimer(100, [](Timer *timer, uint32_t counter, uint32_t currentCount) { instance->tick(timer, counter, currentCount); });
|
|
||||||
spawnBall(random(2) == 0 ? -1 : +1);
|
|
||||||
resetPlayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
~Pong() override = default;
|
|
||||||
|
|
||||||
const char *getName() override {
|
|
||||||
return "Pong";
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#ifndef SPACEINVADERS_H
|
#ifndef MODE_SPACE_INVADERS_H
|
||||||
#define SPACEINVADERS_H
|
#define MODE_SPACE_INVADERS_H
|
||||||
|
|
||||||
#define ROCKET_MAX 20
|
#define ROCKET_MAX 20
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ struct Invader {
|
|||||||
uint8_t y;
|
uint8_t y;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SpaceInvaders : public Mode<SpaceInvaders> {
|
class SpaceInvaders : public Mode {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -45,10 +45,10 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit SpaceInvaders(Display *display) :
|
explicit SpaceInvaders(Display &display) :
|
||||||
Mode(display),
|
Mode(display),
|
||||||
invadersCountX(display->width / 3),
|
invadersCountX(width / 3),
|
||||||
invadersCountY(display->height / 4) {
|
invadersCountY(height / 4) {
|
||||||
|
|
||||||
swarmBegin = (Invader *) malloc(sizeof(Invader) * invadersCountX * invadersCountY);
|
swarmBegin = (Invader *) malloc(sizeof(Invader) * invadersCountX * invadersCountY);
|
||||||
swarmEnd = swarmBegin + invadersCountX * invadersCountY;
|
swarmEnd = swarmBegin + invadersCountX * invadersCountY;
|
||||||
@ -68,24 +68,37 @@ public:
|
|||||||
return "Space Invaders";
|
return "Space Invaders";
|
||||||
}
|
}
|
||||||
|
|
||||||
void doStep(microseconds_t dt) override {
|
protected:
|
||||||
|
|
||||||
|
void step(microseconds_t dt) override {
|
||||||
stepRockets(dt);
|
stepRockets(dt);
|
||||||
stepInvaders(dt);
|
stepInvaders(dt);
|
||||||
stepHero(dt);
|
stepHero(dt);
|
||||||
|
|
||||||
draw();
|
|
||||||
|
|
||||||
collide();
|
collide();
|
||||||
if (invadersAlive == 0) {
|
if (invadersAlive == 0) {
|
||||||
Serial.println("WINNER!");
|
Serial.println("WINNER!");
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
if (swarmY + (invadersCountY - 1) * 2 >= display->height - 1) { // TODO this is only correct if there still are any invaders in the last row (otherwise we "Game Over" too early)
|
// TODO this is only correct if there still are any invaders in the last row (otherwise we "Game Over" too early)
|
||||||
|
if (swarmY + (invadersCountY - 1) * 2 >= height - 1) {
|
||||||
Serial.println("GAME OVER");
|
Serial.println("GAME OVER");
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO don't always markDirty. Be more efficient
|
||||||
|
markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void draw(Display &display) override {
|
||||||
|
display.clear();
|
||||||
|
drawInvaders(display);
|
||||||
|
drawRockets(display);
|
||||||
|
drawHero(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
void stepRockets(microseconds_t dt) {
|
void stepRockets(microseconds_t dt) {
|
||||||
rocketRuntime += dt;
|
rocketRuntime += dt;
|
||||||
if (rocketRuntime > 200000) {
|
if (rocketRuntime > 200000) {
|
||||||
@ -146,7 +159,7 @@ public:
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
heroX++;
|
heroX++;
|
||||||
if (heroX >= display->width - 2 || randomBool(20)) {
|
if (heroX >= width - 2 || randomBool(20)) {
|
||||||
heroLeft = true;
|
heroLeft = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,7 +177,7 @@ public:
|
|||||||
if (!rocket->alive && rocket->flash == 0) {
|
if (!rocket->alive && rocket->flash == 0) {
|
||||||
rocket->alive = true;
|
rocket->alive = true;
|
||||||
rocket->x = heroX;
|
rocket->x = heroX;
|
||||||
rocket->y = display->height - 2;
|
rocket->y = height - 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,47 +208,40 @@ public:
|
|||||||
&& swarmX + invader->x * 3 + 1 >= rocket->x;
|
&& swarmX + invader->x * 3 + 1 >= rocket->x;
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw() {
|
void drawInvaders(Display &display) {
|
||||||
display->clear();
|
|
||||||
drawInvaders();
|
|
||||||
drawRockets();
|
|
||||||
drawHero();
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawInvaders() {
|
|
||||||
for (Invader *invader = swarmBegin; invader < swarmEnd; invader++) {
|
for (Invader *invader = swarmBegin; invader < swarmEnd; invader++) {
|
||||||
if (invader->alive) {
|
if (invader->alive) {
|
||||||
display->set(swarmX + invader->x * 3 + 0, swarmY + invader->y * 2, RED);
|
display.set(swarmX + invader->x * 3 + 0, swarmY + invader->y * 2, RED);
|
||||||
display->set(swarmX + invader->x * 3 + 1, swarmY + invader->y * 2, RED);
|
display.set(swarmX + invader->x * 3 + 1, swarmY + invader->y * 2, RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawRockets() {
|
void drawRockets(Display &display) {
|
||||||
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
|
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
|
||||||
if (rocket->alive) {
|
if (rocket->alive) {
|
||||||
display->set(rocket->x, rocket->y, YELLOW);
|
display.set(rocket->x, rocket->y, YELLOW);
|
||||||
} else if (rocket->flash > 0) {
|
} else if (rocket->flash > 0) {
|
||||||
display->set(rocket->x - 1, rocket->y - 1, gray(rocket->flash));
|
display.set(rocket->x - 1, rocket->y - 1, gray(rocket->flash));
|
||||||
display->set(rocket->x - 1, rocket->y + 1, gray(rocket->flash));
|
display.set(rocket->x - 1, rocket->y + 1, gray(rocket->flash));
|
||||||
display->set(rocket->x + 0, rocket->y + 0, gray(rocket->flash));
|
display.set(rocket->x + 0, rocket->y + 0, gray(rocket->flash));
|
||||||
display->set(rocket->x + 1, rocket->y + 1, gray(rocket->flash));
|
display.set(rocket->x + 1, rocket->y + 1, gray(rocket->flash));
|
||||||
display->set(rocket->x + 1, rocket->y - 1, gray(rocket->flash));
|
display.set(rocket->x + 1, rocket->y - 1, gray(rocket->flash));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawHero() {
|
void drawHero(Display &display) {
|
||||||
display->set(heroX - 1, display->height - 1, BLUE);
|
display.set(heroX - 1, height - 1, BLUE);
|
||||||
display->set(heroX + 0, display->height - 1, BLUE);
|
display.set(heroX + 0, height - 1, BLUE);
|
||||||
display->set(heroX + 1, display->height - 1, BLUE);
|
display.set(heroX + 1, height - 1, BLUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
heroRuntime = 0;
|
heroRuntime = 0;
|
||||||
heroLeft = false;
|
heroLeft = false;
|
||||||
heroShoot = 0;
|
heroShoot = 0;
|
||||||
heroX = display->width / 2;
|
heroX = width / 2;
|
||||||
|
|
||||||
rocketRuntime = 0;
|
rocketRuntime = 0;
|
||||||
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
|
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
#ifndef MEDIATABLE_STARFIELD_H
|
#ifndef MODE_STARFIELD_H
|
||||||
#define MEDIATABLE_STARFIELD_H
|
#define MODE_STARFIELD_H
|
||||||
|
|
||||||
#include "mode/Mode.h"
|
#include "mode/Mode.h"
|
||||||
|
|
||||||
#define STAR_COUNT 20
|
#define STAR_COUNT 20
|
||||||
|
|
||||||
class Starfield : public Mode<Starfield> {
|
class Starfield : public Mode {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -15,12 +15,12 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit Starfield(Display *display) :
|
explicit Starfield(Display &display) :
|
||||||
Mode(display),
|
Mode(display),
|
||||||
center(display->width / 2.0, display->height / 2.0) {
|
center(width / 2.0, height / 2.0) {
|
||||||
for (auto &star: stars) {
|
for (auto &star: stars) {
|
||||||
star.x = random(display->width);
|
star.x = random(width);
|
||||||
star.y = random(display->height);
|
star.y = random(height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,16 +28,26 @@ public:
|
|||||||
return "Starfield";
|
return "Starfield";
|
||||||
}
|
}
|
||||||
|
|
||||||
void doStep(microseconds_t dt) override {
|
protected:
|
||||||
display->clear();
|
|
||||||
|
void step(microseconds_t dt) override {
|
||||||
for (auto &star: stars) {
|
for (auto &star: stars) {
|
||||||
const Vector velocity = star.minus(center).multiply((double) dt / 200000.0);
|
const Vector velocity = star.minus(center).multiply((double) dt / 200000.0);
|
||||||
star = star.plus(velocity);
|
star = star.plus(velocity);
|
||||||
if (star.x < 0 || star.x >= display->width || star.y < 0 || star.y >= display->height) {
|
if (star.x < 0 || star.x >= width || star.y < 0 || star.y >= height) {
|
||||||
star = center.plus(Vector::polar(random(360), 1));
|
star = center.plus(Vector::polar(random(360), 1));
|
||||||
}
|
}
|
||||||
uint8_t brightness = (uint8_t) round(255.0 * star.minus(center).length / (display->width / 2.0));
|
}
|
||||||
display->set(star, gray(brightness));
|
|
||||||
|
// TODO don't always markDirty. Be more efficient
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Display &display) override {
|
||||||
|
display.clear();
|
||||||
|
for (auto &star: stars) {
|
||||||
|
uint8_t brightness = (uint8_t) round(255.0 * star.minus(center).length / (width / 2.0));
|
||||||
|
display.set(star, gray(brightness));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
#ifndef TIMER_H
|
|
||||||
#define TIMER_H
|
|
||||||
|
|
||||||
#include "BASICS.h"
|
|
||||||
|
|
||||||
class Timer {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef void (*Callback)(Timer *timer, uint32_t counter, uint32_t currentCount);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Callback callback;
|
|
||||||
|
|
||||||
microseconds_t accu = 0;
|
|
||||||
|
|
||||||
uint32_t totalCount = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
microseconds_t interval;
|
|
||||||
|
|
||||||
Timer(microseconds_t interval, Callback callback) :
|
|
||||||
callback(callback),
|
|
||||||
interval(interval) {
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
void step(microseconds_t dt) {
|
|
||||||
accu += dt;
|
|
||||||
if (accu >= interval) {
|
|
||||||
uint32_t currentCount = accu / interval;
|
|
||||||
totalCount += currentCount;
|
|
||||||
accu = accu % interval;
|
|
||||||
callback(this, totalCount, currentCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -113,5 +113,5 @@ void web_fps_off() {
|
|||||||
|
|
||||||
void redirect() {
|
void redirect() {
|
||||||
server.sendHeader("location", "/");
|
server.sendHeader("location", "/");
|
||||||
server.send(302, "text/plain", "ok");
|
server.send(302, "text/plain", "realtimeOK");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user