|
|
|
@ -1,243 +1,158 @@
|
|
|
|
|
#include <iostream> |
|
|
|
|
#include <functional> |
|
|
|
|
#include <fstream> |
|
|
|
|
#include <vector> |
|
|
|
|
#include <memory> |
|
|
|
|
#include <string> |
|
|
|
|
#include <cmath> |
|
|
|
|
|
|
|
|
|
#include <GL/gl.h> |
|
|
|
|
#include <GL/glu.h> |
|
|
|
|
#include <GL/glut.h> |
|
|
|
|
|
|
|
|
|
#include "pixel_wise.h" |
|
|
|
|
#include "drawable.h" |
|
|
|
|
#include "glut_events.h" |
|
|
|
|
#include "utils.hpp" |
|
|
|
|
#include "no_maze.h" |
|
|
|
|
#include "maze.h" |
|
|
|
|
|
|
|
|
|
GLfloat deltaTime; |
|
|
|
|
|
|
|
|
|
bool gameRunning = false; |
|
|
|
|
|
|
|
|
|
bool mouseDown = false; |
|
|
|
|
int leftScore = 0; |
|
|
|
|
int rightScore = 0; |
|
|
|
|
|
|
|
|
|
int leftMove = 0; |
|
|
|
|
int rightMove = 0; |
|
|
|
|
std::vector<std::vector<int>> maze; |
|
|
|
|
std::unique_ptr<GlutEvents> events; |
|
|
|
|
|
|
|
|
|
void display(void) { |
|
|
|
|
if (events) { |
|
|
|
|
events->display(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
void overlayDisplay(void) { |
|
|
|
|
if (events) { |
|
|
|
|
events->overlayDisplay(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
void reshape(int width, int height) { |
|
|
|
|
if (events) { |
|
|
|
|
events->reshape(width, height); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
void keyDown(unsigned char key, int x, int y) { |
|
|
|
|
if (events) { |
|
|
|
|
events->keyboard(key, x, y); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
void keyUp(unsigned char key, int x, int y) { |
|
|
|
|
if (events) { |
|
|
|
|
events->keyboardUp(key, x, y); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
void mouse(int button, int state, int x, int y) { |
|
|
|
|
if (state == GLUT_DOWN) { |
|
|
|
|
gameRunning = true; |
|
|
|
|
if (events) { |
|
|
|
|
events->mouse(button, state, x, y); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class BoardMiddle : public Drawable { |
|
|
|
|
public: |
|
|
|
|
virtual void draw() { |
|
|
|
|
glBegin(GL_POLYGON); |
|
|
|
|
glColor3ub(0xBD, 0xBD, 0xBD); |
|
|
|
|
glVertex2fv(PW::nm(-2, -1).data()); |
|
|
|
|
glVertex2fv(PW::nm(2, -1).data()); |
|
|
|
|
glVertex2fv(PW::nm(2, 1).data()); |
|
|
|
|
glVertex2fv(PW::nm(-2, 1).data()); |
|
|
|
|
glEnd(); |
|
|
|
|
} |
|
|
|
|
} bm; |
|
|
|
|
|
|
|
|
|
class Ball : public Drawable { |
|
|
|
|
public: |
|
|
|
|
static constexpr GLfloat defaultVeloX = 0.5; |
|
|
|
|
|
|
|
|
|
GLfloat radius; |
|
|
|
|
GLfloat x, y; |
|
|
|
|
GLfloat veloX, veloY; |
|
|
|
|
|
|
|
|
|
Ball(GLfloat x, GLfloat y) : x(x), y(y), radius(0.025), veloX(defaultVeloX) { |
|
|
|
|
|
|
|
|
|
void motion(int x, int y) { |
|
|
|
|
if (events) { |
|
|
|
|
events->motion(x, y); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
virtual void draw() { |
|
|
|
|
glBegin(GL_POLYGON); |
|
|
|
|
glColor3ub(0xFF, 0xEE, 0x58); |
|
|
|
|
for (float i = 0; i < 360; i += 0.5) { |
|
|
|
|
const auto rad = Utils::toRad(i); |
|
|
|
|
glVertex2f(x + radius * cos(rad), y + radius * sin(rad)); |
|
|
|
|
} |
|
|
|
|
glEnd(); |
|
|
|
|
void passiveMotion(int x, int y) { |
|
|
|
|
if (events) { |
|
|
|
|
events->passiveMotion(x, y); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void update() { |
|
|
|
|
x += veloX * deltaTime; |
|
|
|
|
y += veloY * deltaTime; |
|
|
|
|
} |
|
|
|
|
} ball {0, 0}; |
|
|
|
|
|
|
|
|
|
class Text : public Drawable { |
|
|
|
|
std::function<std::string()> strObtainer; |
|
|
|
|
public: |
|
|
|
|
std::function<GLfloat()> x, y; |
|
|
|
|
void *font; |
|
|
|
|
Text(std::function<std::string()> strObtainer, GLfloat x, GLfloat y) : strObtainer(strObtainer), x([=](){return x;}), y([=](){return y;}), font(GLUT_BITMAP_HELVETICA_18) {} |
|
|
|
|
Text(std::function<std::string()> strObtainer, std::function<GLfloat()> x, std::function<GLfloat()> y) : strObtainer(strObtainer), x(x), y(y), font(GLUT_BITMAP_HELVETICA_18) {} |
|
|
|
|
Text(std::function<std::string()> strObtainer, std::array<GLfloat, 2> pos) : strObtainer(strObtainer), x([=](){return pos[0];}), y([=](){return pos[1];}), font(GLUT_BITMAP_HELVETICA_18) {} |
|
|
|
|
Text(std::function<std::string()> strObtainer, std::function<std::array<GLfloat, 2>()> pos) : strObtainer(strObtainer), x([=](){return pos()[0];}), y([=](){return pos()[1];}), font(GLUT_BITMAP_HELVETICA_18) {} |
|
|
|
|
|
|
|
|
|
virtual void draw() { |
|
|
|
|
glRasterPos2f(x(), y()); |
|
|
|
|
for (const auto& c: strObtainer()) { |
|
|
|
|
glutBitmapCharacter(font, c); |
|
|
|
|
void visibility(int state) { |
|
|
|
|
if (events) { |
|
|
|
|
events->visibility(state); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} leftScoreText{[](){ return std::to_string(leftScore); }, [](){return PW::tm(-100, 20);}}, |
|
|
|
|
rightScoreText{[](){ return std::to_string(rightScore); }, [](){return PW::tm(100, 20);}}; |
|
|
|
|
|
|
|
|
|
class Paddle : public Drawable { |
|
|
|
|
public: |
|
|
|
|
GLfloat height, width; |
|
|
|
|
GLfloat x, y; |
|
|
|
|
|
|
|
|
|
Paddle(GLfloat x, GLfloat y) : x(x), y(y), height(0.4), width(0.02) {} |
|
|
|
|
|
|
|
|
|
virtual void draw() { |
|
|
|
|
glBegin(GL_POLYGON); |
|
|
|
|
glVertex2f(x, y); |
|
|
|
|
glVertex2f(x + width, y); |
|
|
|
|
glVertex2f(x + width, y + height); |
|
|
|
|
glVertex2f(x, y + height); |
|
|
|
|
glEnd(); |
|
|
|
|
} |
|
|
|
|
} leftPaddle{-0.9, -0.2}, rightPaddle{0.9 - 0.02, -0.2}; |
|
|
|
|
|
|
|
|
|
void display(void) { |
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT); |
|
|
|
|
|
|
|
|
|
bm.draw(); |
|
|
|
|
ball.draw(); |
|
|
|
|
|
|
|
|
|
glColor3ub(0x66, 0xBB, 0x6A); |
|
|
|
|
leftScoreText.draw(); |
|
|
|
|
leftPaddle.draw(); |
|
|
|
|
|
|
|
|
|
glColor3ub(0x42, 0xA5, 0xF5); |
|
|
|
|
rightScoreText.draw(); |
|
|
|
|
rightPaddle.draw(); |
|
|
|
|
|
|
|
|
|
glFlush(); |
|
|
|
|
void entry(int state) { |
|
|
|
|
if (events) { |
|
|
|
|
events->entry(state); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
void special(int key, int x, int y) { |
|
|
|
|
if (events) { |
|
|
|
|
events->special(key, x, y); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int gameTime; |
|
|
|
|
|
|
|
|
|
const GLfloat paddleMoveSpeed = 0.8; |
|
|
|
|
|
|
|
|
|
void idle() { |
|
|
|
|
int newTime = glutGet(GLUT_ELAPSED_TIME); |
|
|
|
|
deltaTime = (newTime - gameTime) / 1000.0; |
|
|
|
|
gameTime = newTime; |
|
|
|
|
|
|
|
|
|
if (gameRunning) { |
|
|
|
|
ball.update(); |
|
|
|
|
|
|
|
|
|
// Detect ball collission
|
|
|
|
|
if (leftPaddle.x <= (ball.x + ball.radius) && (ball.x - ball.radius) <= (leftPaddle.x + leftPaddle.width)) { |
|
|
|
|
// Ball inside left-right area of left paddle
|
|
|
|
|
if (leftPaddle.y <= ball.y && ball.y <= leftPaddle.y + leftPaddle.height) { |
|
|
|
|
// Ball inside up-down area of left paddle
|
|
|
|
|
ball.veloX = ball.defaultVeloX; |
|
|
|
|
ball.veloY = Utils::nummap(ball.y, leftPaddle.y, leftPaddle.y + leftPaddle.height, (GLfloat)-1, (GLfloat)1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (rightPaddle.x <= (ball.x + ball.radius) && (ball.x - ball.radius) <= (rightPaddle.x + rightPaddle.width)) { |
|
|
|
|
// Ball inside left-right area of right paddle
|
|
|
|
|
if (rightPaddle.y <= ball.y && ball.y <= rightPaddle.y + rightPaddle.height) { |
|
|
|
|
// Ball inside up-down area of right paddle
|
|
|
|
|
ball.veloX = -ball.defaultVeloX; |
|
|
|
|
ball.veloY = Utils::nummap(ball.y, rightPaddle.y, rightPaddle.y + rightPaddle.height, (GLfloat)-1, (GLfloat)1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (ball.x + ball.radius < -1) { |
|
|
|
|
// Ball exited board on the left side
|
|
|
|
|
rightScore += 1; |
|
|
|
|
ball.x = ball.y = 0; |
|
|
|
|
ball.veloY = 0; |
|
|
|
|
gameRunning = false; |
|
|
|
|
} |
|
|
|
|
else if (ball.x - ball.radius > 1) { |
|
|
|
|
// Ball exited board on the right side
|
|
|
|
|
leftScore += 1; |
|
|
|
|
ball.x = ball.y = 0; |
|
|
|
|
ball.veloY = 0; |
|
|
|
|
gameRunning = false; |
|
|
|
|
} |
|
|
|
|
else if (ball.y - ball.radius < -1) { |
|
|
|
|
// Ball touched bottom side
|
|
|
|
|
ball.veloY *= -1; |
|
|
|
|
} |
|
|
|
|
else if (ball.y + ball.radius > 1) { |
|
|
|
|
// Ball touched top side
|
|
|
|
|
ball.veloY *= -1; |
|
|
|
|
if (events) { |
|
|
|
|
events->idle(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Move paddle
|
|
|
|
|
leftPaddle.y += leftMove * paddleMoveSpeed * deltaTime; |
|
|
|
|
rightPaddle.y += rightMove * paddleMoveSpeed * deltaTime; |
|
|
|
|
|
|
|
|
|
// Clamp paddle to stay on screen
|
|
|
|
|
if (leftPaddle.y < -1) leftPaddle.y = -1; |
|
|
|
|
if (rightPaddle.y < -1) rightPaddle.y = -1; |
|
|
|
|
if (leftPaddle.y > 1 - leftPaddle.height) leftPaddle.y = 1 - leftPaddle.height; |
|
|
|
|
if (rightPaddle.y > 1 - rightPaddle.height) rightPaddle.y = 1 - rightPaddle.height; |
|
|
|
|
void load_maze(const std::string& path) { |
|
|
|
|
std::ifstream f{path}; |
|
|
|
|
std::string line; |
|
|
|
|
|
|
|
|
|
glutPostRedisplay(); |
|
|
|
|
while(true) { |
|
|
|
|
std::getline(f, line); |
|
|
|
|
if (!f) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void keyDown(unsigned char key, int x, int y) { |
|
|
|
|
switch(key) { |
|
|
|
|
case 'w': |
|
|
|
|
case 'W': |
|
|
|
|
leftMove = 1; |
|
|
|
|
std::vector<int> row; |
|
|
|
|
for (const auto& c: line) { |
|
|
|
|
switch(c) { |
|
|
|
|
case ' ': |
|
|
|
|
row.push_back(0); |
|
|
|
|
break; |
|
|
|
|
case 's': |
|
|
|
|
case 'S': |
|
|
|
|
leftMove = -1; |
|
|
|
|
break; |
|
|
|
|
case 'p': |
|
|
|
|
case 'P': |
|
|
|
|
rightMove = 1; |
|
|
|
|
break; |
|
|
|
|
case ';': |
|
|
|
|
case ':': |
|
|
|
|
rightMove = -1; |
|
|
|
|
case 'X': |
|
|
|
|
row.push_back(1); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
row.push_back(-1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void keyUp(unsigned char key, int x, int y) { |
|
|
|
|
switch(key) { |
|
|
|
|
case 'w': |
|
|
|
|
case 'W': |
|
|
|
|
case 's': |
|
|
|
|
case 'S': |
|
|
|
|
leftMove = 0; |
|
|
|
|
break; |
|
|
|
|
case 'p': |
|
|
|
|
case 'P': |
|
|
|
|
case ';': |
|
|
|
|
case ':': |
|
|
|
|
rightMove = 0; |
|
|
|
|
break; |
|
|
|
|
if (row.size() > 0) { |
|
|
|
|
maze.push_back(row); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) |
|
|
|
|
{ |
|
|
|
|
std::string maze_file; |
|
|
|
|
if (argc < 2) { |
|
|
|
|
events = std::make_unique<NoMazeScreen>(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
maze_file = std::string{argv[1]}; |
|
|
|
|
load_maze(maze_file); |
|
|
|
|
events = std::make_unique<MazeScreen>(maze); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
glutInit(&argc, argv); |
|
|
|
|
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); |
|
|
|
|
glutInitWindowSize(600, 600); |
|
|
|
|
glutInitWindowPosition(glutGet(GLUT_SCREEN_WIDTH) * 0.6, (glutGet(GLUT_SCREEN_HEIGHT) - 600) * 0.5); |
|
|
|
|
glutCreateWindow("Pong"); |
|
|
|
|
|
|
|
|
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); |
|
|
|
|
glClearDepth(1.0f); |
|
|
|
|
glDepthFunc(GL_LEQUAL); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glutInitWindowSize(640, 480); |
|
|
|
|
glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH) - 640) * 0.5, (glutGet(GLUT_SCREEN_HEIGHT) - 480) * 0.5); |
|
|
|
|
if (maze_file.size() > 0) { |
|
|
|
|
glutCreateWindow((std::string{"Maze - "} + maze_file).c_str()); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
glutCreateWindow("Maze"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (maze_file.size() > 0) { |
|
|
|
|
glEnable(GL_DEPTH_TEST); |
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
|
|
|
|
glMatrixMode(GL_PROJECTION); |
|
|
|
|
glLoadIdentity(); |
|
|
|
|
gluPerspective(60, 1, 0.1, 100); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
glutDisplayFunc(display); |
|
|
|
|
glutOverlayDisplayFunc(overlayDisplay); |
|
|
|
|
glutReshapeFunc(reshape); |
|
|
|
|
glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF); |
|
|
|
|
glutKeyboardFunc(keyDown); |
|
|
|
|
glutKeyboardUpFunc(keyUp); |
|
|
|
|
glutDisplayFunc(display); |
|
|
|
|
glutMouseFunc(mouse); |
|
|
|
|
glutMotionFunc(motion); |
|
|
|
|
glutPassiveMotionFunc(passiveMotion); |
|
|
|
|
glutVisibilityFunc(visibility); |
|
|
|
|
glutEntryFunc(entry); |
|
|
|
|
glutSpecialFunc(special); |
|
|
|
|
glutIdleFunc(idle); |
|
|
|
|
glutMainLoop(); |
|
|
|
|
return 0; |
|
|
|
|