#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