151 lines
4.0 KiB
C++
151 lines
4.0 KiB
C++
#ifndef PONG_H
|
|
#define PONG_H
|
|
|
|
#include "mode/Mode.h"
|
|
#include "Player.h"
|
|
#include "display/Vector.h"
|
|
|
|
class Pong : public Mode<Pong> {
|
|
|
|
private:
|
|
|
|
enum Status {
|
|
SCORE, PLAY, OVER
|
|
};
|
|
|
|
Player player0;
|
|
|
|
Player player1;
|
|
|
|
Vector position;
|
|
|
|
Vector velocity;
|
|
|
|
Status status = PLAY;
|
|
|
|
microseconds_t timeout = 0;
|
|
|
|
void resetPlayer() {
|
|
player0.size = 3;
|
|
player0.score = 0;
|
|
player0.position = (display->height - player0.size) / 2;
|
|
player1.size = 3;
|
|
player1.score = 0;
|
|
player1.position = (display->height - player1.size) / 2;
|
|
}
|
|
|
|
void tick(Timer *timer, uint32_t totalCount, uint32_t currentCount) {
|
|
switch (status) {
|
|
case SCORE:
|
|
timeout -= currentCount * timer->interval;
|
|
if (timeout <= 0) {
|
|
status = PLAY;
|
|
}
|
|
break;
|
|
case PLAY:
|
|
position.add(velocity);
|
|
player0.randomMove(display->height);
|
|
player1.randomMove(display->height);
|
|
paddleBounce();
|
|
checkScoring();
|
|
topBottomBounce();
|
|
draw();
|
|
break;
|
|
case OVER:
|
|
timeout -= currentCount * timer->interval;
|
|
if (timeout <= 0) {
|
|
resetPlayer();
|
|
status = SCORE;
|
|
timeout = 2000000;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void topBottomBounce() {
|
|
position.bounceInBox(display->width, display->height);
|
|
}
|
|
|
|
void checkScoring() {
|
|
if (position.x < 0) {
|
|
player1.score++;
|
|
spawnBall(+1);
|
|
} else if (position.x >= display->width) {
|
|
player0.score++;
|
|
spawnBall(-1);
|
|
}
|
|
if (player0.score >= 10 || player1.score >= 10) {
|
|
status = OVER;
|
|
timeout = 2000000;
|
|
}
|
|
}
|
|
|
|
void paddleBounce() {
|
|
if (position.x == 1 && player0.position <= position.y && position.y < player0.position + player0.size) {
|
|
velocity.x = -velocity.x;
|
|
position.x = 3;
|
|
} else if (position.x == display->width - 2 && player1.position <= position.y && position.y < player1.position + player1.size) {
|
|
velocity.x = -velocity.x;
|
|
position.x = display->width - 4;
|
|
}
|
|
}
|
|
|
|
void spawnBall(int direction) {
|
|
position.x = (double) display->width / 2.0;
|
|
position.y = (double) display->height / 2.0;
|
|
velocity.x = direction;
|
|
velocity.y = 0;
|
|
status = SCORE;
|
|
timeout = 2000000;
|
|
}
|
|
|
|
void draw() {
|
|
display->clear();
|
|
switch (status) {
|
|
case SCORE:
|
|
display->print(1, 1, player0.score, 0, 255, 0);
|
|
display->print(display->width - 1 - DISPLAY_CHAR_WIDTH, 1, player1.score, 255, 0, 0);
|
|
break;
|
|
case PLAY:
|
|
for (int i = 0; i < player0.size; ++i) {
|
|
display->set(1, (uint8_t) round(player0.position) + i, 0, 255, 0);
|
|
}
|
|
for (int i = 0; i < player1.size; ++i) {
|
|
display->set(display->width - 2, (uint8_t) round(player1.position) + i, 255, 0, 0);
|
|
}
|
|
display->set((uint8_t) round(position.x), (uint8_t) round(position.y), 255, 255, 255);
|
|
break;
|
|
case OVER:
|
|
if (player0.score > player1.score) {
|
|
display->print(1, 1, 11, 0, 255, 0);
|
|
display->print(display->width - 1 - DISPLAY_CHAR_WIDTH, 1, 12, 255, 0, 0);
|
|
} else if (player0.score < player1.score) {
|
|
display->print(1, 1, 12, 255, 0, 0);
|
|
display->print(display->width - 1 - DISPLAY_CHAR_WIDTH, 1, 11, 0, 255, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
explicit Pong(Display *display) :
|
|
Mode(display),
|
|
position(display->width / 2.0, display->height / 2.0),
|
|
velocity(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
|