RGBMatrixDisplay/src/mode/SpaceInvaders/SpaceInvaders.h

244 lines
5.7 KiB
C++

#ifndef SPACEINVADERS_H
#define SPACEINVADERS_H
#define ROCKET_MAX 20
#include "mode/Mode.h"
struct Rocket {
volatile bool alive;
uint8_t x;
uint8_t y;
};
struct Invader {
bool alive;
uint8_t x;
uint8_t y;
};
class SpaceInvaders : public Mode {
private:
millis_t heroRuntime = 0;
uint8_t heroX = 0;
bool heroLeft = false;
uint8_t heroShoot = 0;
uint8_t invadersCountX;
uint8_t invadersCountY;
uint8_t invadersAlive = 0;
millis_t swarmRuntime = 0;
uint8_t swarmY = 0;
bool swarmLeft = false;
bool swarmDown = false;
uint8_t swarmX = 0;
Invader *swarmBegin;
Invader *swarmEnd;
millis_t rocketRuntime = 0;
Rocket *rocketsBegin;
Rocket *rocketsEnd;
public:
explicit SpaceInvaders(Display *display) :
Mode(display),
invadersCountX(display->width / 3),
invadersCountY(display->height / 4) {
swarmBegin = (Invader *) malloc(sizeof(Invader) * invadersCountX * invadersCountY);
swarmEnd = swarmBegin + invadersCountX * invadersCountY;
rocketsBegin = (Rocket *) malloc(sizeof(Rocket) * ROCKET_MAX);
rocketsEnd = rocketsBegin + ROCKET_MAX;
reset();
}
~SpaceInvaders() override {
free(swarmBegin);
free(rocketsBegin);
};
const char *getName() override {
return "Space Invaders";
}
void step(millis_t dt) override {
stepRockets(dt);
stepInvaders(dt);
stepHero(dt);
draw();
collide();
if (invadersAlive == 0) {
Serial.println("WINNER!");
reset();
}
if (swarmY + (invadersCountY - 1) * 2 >= display->height - 1) {
Serial.println("GAME OVER");
reset();
}
}
void stepRockets(millis_t dt) {
rocketRuntime += dt;
if (rocketRuntime > 200) {
rocketRuntime -= 200;
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
if (rocket->alive) {
if (rocket->y == 0) {
rocket->alive = false;
} else {
rocket->y -= 1;
}
}
}
}
}
void stepInvaders(millis_t dt) {
swarmRuntime += dt;
if (swarmDown && swarmRuntime > 500) {
swarmDown = false;
swarmY++;
}
if (swarmRuntime >= 1000) {
swarmRuntime -= 1000;
if (swarmLeft) {
swarmX--;
if (swarmX == 0) {
swarmLeft = false;
}
} else {
swarmX++;
if (swarmX == 3) {
swarmLeft = true;
}
}
if (swarmX == 0) {
swarmDown = true;
}
}
}
void stepHero(millis_t dt) {
heroRuntime += dt;
if (heroRuntime >= 50) {
heroRuntime -= 50;
if (heroLeft) {
heroX--;
if (heroX <= 1) {
heroLeft = false;
}
} else {
heroX++;
if (heroX >= display->width - 2) {
heroLeft = true;
}
}
heroShoot++;
if (heroShoot >= 10) {
heroShoot = 0;
shoot();
}
}
}
void shoot() {
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
if (!rocket->alive) {
rocket->alive = true;
rocket->x = heroX;
rocket->y = display->height - 2;
}
}
}
void collide() {
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
for (Invader *invader = swarmBegin; invader < swarmEnd; invader++) {
if (invader->alive && rocket->alive && collide(rocket, invader)) {
rocket->alive = false;
invader->alive = false;
invadersAlive--;
// Serial.printf("Killed invader at (%2u/%2u): %2u/%2u alive.\n", invader->x, invader->y, invadersAlive, invadersCountX * invadersCountY);
}
}
}
}
bool collide(const Rocket *rocket, const Invader *invader) const {
return swarmY + invader->y * 2 == rocket->y && swarmX + invader->x * 3 <= rocket->x && rocket->x <= swarmX + invader->x * 3 + 1;
}
void draw() {
display->clear();
drawInvaders();
drawRockets();
drawHero();
}
void drawInvaders() {
for (Invader *invader = swarmBegin; invader < swarmEnd; invader++) {
if (invader->alive) {
display->set(swarmX + invader->x * 3 + 0, swarmY + invader->y * 2, 255, 0, 0);
display->set(swarmX + invader->x * 3 + 1, swarmY + invader->y * 2, 255, 0, 0);
}
}
}
void drawRockets() {
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
if (!rocket->alive) {
continue;
}
display->set(rocket->x, rocket->y, 255, 255, 0);
}
}
void drawHero() {
display->set(heroX - 1, display->height - 1, 0, 0, 255);
display->set(heroX + 0, display->height - 1, 0, 0, 255);
display->set(heroX + 1, display->height - 1, 0, 0, 255);
}
void reset() {
heroRuntime = 0;
heroLeft = false;
heroShoot = 0;
heroX = display->width / 2;
rocketRuntime = 0;
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
rocket->alive = false;
rocket->x = 0;
rocket->y = 0;
}
swarmRuntime = 0;
invadersAlive = invadersCountX * invadersCountY;
swarmX = 0;
swarmY = 0;
swarmLeft = false;
swarmDown = false;
uint8_t n = 0;
for (Invader *invader = swarmBegin; invader < swarmEnd; invader++) {
invader->alive = true;
invader->x = n % invadersCountX;
invader->y = n / invadersCountX;
n++;
}
}
};
#endif