190 lines
4.8 KiB
C++
190 lines
4.8 KiB
C++
#ifndef MODE_PONG_H
|
|
#define MODE_PONG_H
|
|
|
|
#include "Player.h"
|
|
#include "Vector.h"
|
|
#include "mode/Mode.h"
|
|
|
|
class Pong final : public Mode {
|
|
|
|
enum Status {
|
|
SCORE, PLAY, OVER
|
|
};
|
|
|
|
Player player0;
|
|
|
|
Player player1;
|
|
|
|
Vector ball;
|
|
|
|
Vector velocity;
|
|
|
|
Status status = PLAY;
|
|
|
|
microseconds_t timeoutMicroseconds = 0;
|
|
|
|
public:
|
|
|
|
explicit Pong(Display& display) : Mode(display),
|
|
ball(width / 2.0, height / 2.0),
|
|
velocity(Vector::polar(random(360), exp10(1))) {
|
|
timer(0, 100);
|
|
spawnBall(random(2) == 0 ? -1 : +1);
|
|
resetPlayer();
|
|
}
|
|
|
|
const char *getName() override {
|
|
return "Pong";
|
|
}
|
|
|
|
void move(const int index, int x, const int y) override {
|
|
if (index == 0) {
|
|
player0.random = false;
|
|
player0.y = min(height - player0.size, max(0, player0.y + y));
|
|
} else if (index == 1) {
|
|
player1.random = false;
|
|
player1.y = min(height - player1.size, max(0, player1.y + y));
|
|
}
|
|
}
|
|
|
|
void fire(const int index) override {
|
|
if (index == 0) {
|
|
player0.random = false;
|
|
} else if (index == 1) {
|
|
player1.random = false;
|
|
}
|
|
}
|
|
|
|
protected:
|
|
|
|
void tick(uint8_t index, const microseconds_t microseconds) override {
|
|
switch (status) {
|
|
case SCORE:
|
|
timeoutMicroseconds -= microseconds;
|
|
if (timeoutMicroseconds <= 0) {
|
|
status = PLAY;
|
|
}
|
|
break;
|
|
case PLAY:
|
|
ball = ball.plus(velocity);
|
|
if (player0.random) {
|
|
player0.randomMove(height);
|
|
}
|
|
if (player1.random) {
|
|
player1.randomMove(height);
|
|
}
|
|
topBottomBounce();
|
|
paddleBounce();
|
|
checkScoring();
|
|
markDirty();
|
|
|
|
break;
|
|
case OVER:
|
|
timeoutMicroseconds -= microseconds;
|
|
if (timeoutMicroseconds <= 0) {
|
|
resetPlayer();
|
|
status = PLAY;
|
|
timeoutMicroseconds = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void draw(Display& display) override {
|
|
display.clear();
|
|
switch (status) {
|
|
case SCORE:
|
|
display.printf(1, 1, Green, "%d", player0.score);
|
|
display.printf(28, 1, Red, "%d", player1.score);
|
|
break;
|
|
case PLAY:
|
|
for (auto i = 0; i < player0.size; ++i) {
|
|
display.setPixel(1, static_cast<uint8_t>(round(player0.y)) + i, Green);
|
|
}
|
|
for (auto i = 0; i < player1.size; ++i) {
|
|
display.setPixel(width - 2, static_cast<uint8_t>(round(player1.y)) + i, Red);
|
|
}
|
|
display.setPixel(static_cast<uint8_t>(round(ball.x)), static_cast<uint8_t>(round(ball.y)), White);
|
|
break;
|
|
case OVER:
|
|
if (player0.score > player1.score) {
|
|
display.printf(1, 1, Green, "W", player0.score);
|
|
display.printf(28, 1, Red, "L", player1.score);
|
|
} else if (player0.score < player1.score) {
|
|
display.printf(1, 1, Red, "L", player0.score);
|
|
display.printf(26, 1, Green, "W", player1.score);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
void resetPlayer() {
|
|
player0.size = 3;
|
|
player0.score = 0;
|
|
player0.y = (height - player0.size) / 2;
|
|
player0.random = true;
|
|
player1.size = 3;
|
|
player1.score = 0;
|
|
player1.y = (height - player1.size) / 2;
|
|
player1.random = true;
|
|
}
|
|
|
|
void topBottomBounce() {
|
|
while (ball.y < 0 || ball.y >= height) {
|
|
if (ball.y < 0) {
|
|
ball.y = -ball.y;
|
|
velocity.y = -velocity.y;
|
|
} else if (ball.y >= height) {
|
|
ball.y = 2 * height - ball.y - 1;
|
|
velocity.y = -velocity.y;
|
|
}
|
|
}
|
|
}
|
|
|
|
void checkScoring() {
|
|
if (ball.x < 0) {
|
|
player1.score++;
|
|
Serial.printf("Player 1 scored: %d\n", player1.score);
|
|
spawnBall(+1);
|
|
} else if (ball.x >= width) {
|
|
player0.score++;
|
|
Serial.printf("Player 0 scored: %d\n", player0.score);
|
|
spawnBall(-1);
|
|
}
|
|
if (player0.score >= 10 || player1.score >= 10) {
|
|
status = OVER;
|
|
timeoutMicroseconds = 2000 * 1000;
|
|
}
|
|
}
|
|
|
|
void paddleBounce() {
|
|
const auto paddleHitPosition0 = ball.y - player0.y;
|
|
if (ball.x >= 1 && ball.x < 2 && paddleHitPosition0 >= 0 && paddleHitPosition0 < player0.size) {
|
|
velocity.x = -velocity.x;
|
|
velocity.y = max(-2.0, min(+2.0, velocity.y + paddleHitPosition0 - 1));
|
|
ball.x = 3;
|
|
return;
|
|
}
|
|
const auto paddleHitPosition1 = ball.y - player1.y;
|
|
if (ball.x >= width - 2 && ball.x < width - 1 && paddleHitPosition1 >= 0 && paddleHitPosition1 < player1.size) {
|
|
velocity.x = -velocity.x;
|
|
velocity.y = max(-2.0, min(+2.0, velocity.y + paddleHitPosition1 - 1));
|
|
ball.x = width - 4;
|
|
}
|
|
}
|
|
|
|
void spawnBall(const int direction) {
|
|
ball.x = static_cast<double>(width) / 2.0;
|
|
ball.y = static_cast<double>(height) / 2.0;
|
|
velocity.x = direction;
|
|
velocity.y = 0;
|
|
status = SCORE;
|
|
timeoutMicroseconds = 2000 * 1000;
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|