258 lines
6.6 KiB
C++
258 lines
6.6 KiB
C++
#ifndef SPACEINVADERS_H
|
|
#define SPACEINVADERS_H
|
|
|
|
#define ROCKET_MAX 20
|
|
|
|
#include "mode/Mode.h"
|
|
|
|
struct Rocket {
|
|
bool alive;
|
|
uint16_t flash;
|
|
uint8_t x;
|
|
uint8_t y;
|
|
};
|
|
|
|
struct Invader {
|
|
bool alive;
|
|
uint8_t x;
|
|
uint8_t y;
|
|
};
|
|
|
|
class SpaceInvaders : public Mode {
|
|
|
|
private:
|
|
|
|
microseconds_t heroRuntime = 0;
|
|
uint8_t heroX = 0;
|
|
bool heroLeft = false;
|
|
uint8_t heroShoot = 0;
|
|
|
|
uint8_t invadersCountX;
|
|
uint8_t invadersCountY;
|
|
uint8_t invadersAlive = 0;
|
|
|
|
microseconds_t swarmRuntime = 0;
|
|
uint8_t swarmY = 0;
|
|
bool swarmLeft = false;
|
|
bool swarmDown = false;
|
|
uint8_t swarmX = 0;
|
|
Invader *swarmBegin;
|
|
Invader *swarmEnd;
|
|
|
|
microseconds_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(microseconds_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) { // TODO this is only correct if there still are any invaders in the last row (otherwise we "Game Over" too early)
|
|
Serial.println("GAME OVER");
|
|
reset();
|
|
}
|
|
}
|
|
|
|
void stepRockets(microseconds_t dt) {
|
|
rocketRuntime += dt;
|
|
if (rocketRuntime > 200000) {
|
|
rocketRuntime -= 200000;
|
|
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {
|
|
if (rocket->alive) {
|
|
if (rocket->y == 0) {
|
|
rocket->alive = false;
|
|
} else {
|
|
rocket->y -= 1;
|
|
}
|
|
} else if (rocket->flash > 0) {
|
|
if (rocket->flash < dt) {
|
|
rocket->flash = 0;
|
|
} else {
|
|
rocket->flash -= dt;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void stepInvaders(microseconds_t dt) {
|
|
swarmRuntime += dt;
|
|
|
|
if (swarmDown && swarmRuntime > 500000) {
|
|
swarmDown = false;
|
|
swarmY++;
|
|
}
|
|
|
|
if (swarmRuntime >= 1000000) {
|
|
swarmRuntime -= 1000000;
|
|
if (swarmLeft) {
|
|
swarmX--;
|
|
if (swarmX == 0) {
|
|
swarmLeft = false;
|
|
}
|
|
} else {
|
|
swarmX++;
|
|
if (swarmX == 3) {
|
|
swarmLeft = true;
|
|
}
|
|
}
|
|
if (swarmX == 0) {
|
|
swarmDown = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void stepHero(microseconds_t dt) {
|
|
heroRuntime += dt;
|
|
if (heroRuntime >= 50000) {
|
|
heroRuntime -= 50000;
|
|
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;
|
|
rocket->flash = 1000;
|
|
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) {
|
|
display->set(rocket->x, rocket->y, 255, 255, 0);
|
|
} else if (rocket->flash > 0) {
|
|
display->set(rocket->x - 1, rocket->y - 1, rocket->flash, rocket->flash, rocket->flash);
|
|
display->set(rocket->x - 1, rocket->y + 1, rocket->flash, rocket->flash, rocket->flash);
|
|
display->set(rocket->x + 0, rocket->y + 0, rocket->flash, rocket->flash, rocket->flash);
|
|
display->set(rocket->x + 1, rocket->y + 1, rocket->flash, rocket->flash, rocket->flash);
|
|
display->set(rocket->x + 1, rocket->y - 1, rocket->flash, rocket->flash, rocket->flash);
|
|
}
|
|
}
|
|
}
|
|
|
|
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->flash = 0;
|
|
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
|