#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <math.h>
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <filesystem>
#include "networking.h"
#include "renderer.h"
#include "raycaster.h"
#include "UI.h"
#include "constants.h"
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::thread;
// NETWORK
Network network;
char inBuff[IN_OUT_BUFF_SIZE]; // data to receive
char outBuff[IN_OUT_BUFF_SIZE]; // data to send
size_t bytesReceived;
// PLAYERS
GLfloat positions[STARTING_PARAMETER_COUNT] = {}; // our x,y
GLfloat otherPlayerPositions[STARTING_PARAMETER_COUNT] = {}; // other player x, y
GLfloat vertices1[VERTICES_SIZE]; // our controlled character matrix
GLfloat vertices2[VERTICES_SIZE]; // other player matrix
// RENDERING
GLFWwindow* window;
// GAMESTATE
int GAMESTATE = START;
bool connected = false;
double xpos = 0, ypos = 0; // mouse
bool firstMouse = true;
double lastX = 0, lastY = 0;
bool shooting = false;
int hitCount = 0;
float deltaTime = 1 / 60; // stores actual speed of the game, 1/60 is placeholder
double lastShotTime = 0.0;
bool newImageAvaliable = false;
string msg = "";
static void updatePlayer(GLfloat* newPositions, GLfloat* playerVerticies)
{
// newPositions comes in as [x, y], vertices is [x, y, z, r, g, b, ... ... ]
playerVerticies[0] = newPositions[0] + PLAYER_SIZE; // Top right x
playerVerticies[1] = newPositions[1] + PLAYER_SIZE; // Top right y
playerVerticies[6] = newPositions[0] - PLAYER_SIZE; // Top left x
playerVerticies[7] = newPositions[1] + PLAYER_SIZE; // Top left y
playerVerticies[12] = newPositions[0] - PLAYER_SIZE; // Bottom left x
playerVerticies[13] = newPositions[1] - PLAYER_SIZE; // Bottom left y
playerVerticies[18] = newPositions[0] + PLAYER_SIZE; // Bottom right x
playerVerticies[19] = newPositions[1] - PLAYER_SIZE; // Bottom right y
}
static bool isCollision(GLfloat xPos, GLfloat yPos) {
int mapXpos = static_cast<int>((xPos + 1.0f) / tileSize);
int mapYpos = static_cast<int>((1.0f - yPos) / tileSize);
if (mapXpos < 0) mapXpos = 0;
if (mapYpos < 0) mapYpos = 0;
if (mapXpos >= mapX) mapXpos = mapX - 1;
if (mapYpos >= mapY) mapYpos = mapY - 1;
return map[mapYpos * mapX + mapXpos] == 1;
}
bool isCrosshairOverPlayer(float xpos, float ypos, const GLfloat* myPos, const GLfloat* otherPos) {
float dx = otherPos[0] - myPos[0];
float dy = otherPos[1] - myPos[1];
float distance = sqrt(dx * dx + dy * dy);
float angleToOther = atan2f(dx, dy) * (180.0f / 3.14159f);
float relativeAngle = angleToOther - xpos;
if (relativeAngle < -180.0f) relativeAngle += 360.0f;
if (relativeAngle > 180.0f) relativeAngle -= 360.0f;
float screenX = (relativeAngle / (RAY_NUM * 0.5f));
float verticalAngleToOther = 0.0f;
float relativePitch = verticalAngleToOther - ypos;
float screenY = (relativePitch / 100.0f);
return fabs(screenX) < (0.005 / distance) && fabs(screenY) < (0.01 / distance); // adjust numbers for hitbox
}
static void processInput(SOCKET sock)
{
float moveAmount = MOVE_SPEED * deltaTime; // scales move speed by framerate
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
double x, y;
glfwGetCursorPos(window, &x, &y);
if (firstMouse) {
lastX = x;
lastY = y;
firstMouse = false;
}
double xoffset = x - lastX;
double yoffset = y - lastY;
lastX = x;
lastY = y;
xoffset /= MOUSE_SENS;
yoffset /= MOUSE_SENS;
xpos += xoffset;
ypos += yoffset;
if (ypos > 50) ypos = 50;
if (ypos < -50) ypos = -50;
if (xpos < 0) xpos += 360;
if (xpos >= 360) xpos -= 360;
static double shotCooldown = 1.0 / 60.0;
double currentTime = glfwGetTime();
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) {
if (currentTime - lastShotTime >= shotCooldown) {
shooting = true;
lastShotTime = currentTime;
}
else {
shooting = false;
}
}
else {
shooting = false;
GLfloat newX = positions[0];
GLfloat newY = positions[1];
if ((glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)) {
newX -= moveAmount * -sin(degToRad(xpos));
newY += moveAmount * cos(degToRad(xpos));
}
if ((glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)) {
newX += moveAmount * -sin(degToRad(xpos));
newY -= moveAmount * cos(degToRad(xpos));
}
if ((glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)) {
newX -= moveAmount * cos(degToRad(xpos));
newY += moveAmount * sin(degToRad(xpos));
}
if ((glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)) {
newX += moveAmount * cos(degToRad(xpos));
newY -= moveAmount * sin(degToRad(xpos));
}
if (!isCollision(newX, newY)) {
positions[0] = newX;
positions[1] = newY;
}
else {
if (!isCollision(newX, positions[1])) {
positions[0] = newX;
}
if (!isCollision(positions[0], newY)) {
positions[1] = newY;
}
}
updatePlayer(positions, vertices1);
}
if (GAMESTATE == DEBUG) {
GLfloat newX = otherPlayerPositions[0];
GLfloat newY = otherPlayerPositions[1];
if ((glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS)) {
newY += moveAmount;
}
if ((glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS)) {
newY -= moveAmount;
}
if ((glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS)) {
newX -= moveAmount;
}
if ((glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS)) {
newX += moveAmount;
}
if (!isCollision(newX, newY)) {
otherPlayerPositions[0] = newX;
otherPlayerPositions[1] = newY;
}
else {
if (!isCollision(newX, otherPlayerPositions[1])) {
otherPlayerPositions[0] = newX;
}
if (!isCollision(otherPlayerPositions[0], newY)) {
otherPlayerPositions[1] = newY;
}
}
updatePlayer(otherPlayerPositions, vertices2);
}
}
static bool sendStatePacket(SOCKET sock) {
char outBuff[IN_OUT_BUFF_SIZE];
int offset = 0;
// Use header 1 for the full state packet
outBuff[offset++] = 1;
// Serialize positions[0] as a network order uint32_t
uint32_t netPosX;
memcpy(&netPosX, &positions[0], sizeof(float));
netPosX = htonl(netPosX);
memcpy(outBuff + offset, &netPosX, sizeof(uint32_t));
offset += sizeof(uint32_t);
// Serialize positions[1] as a network order uint32_t
uint32_t netPosY;
memcpy(&netPosY, &positions[1], sizeof(float));
netPosY = htonl(netPosY);
memcpy(outBuff + offset, &netPosY, sizeof(uint32_t));
offset += sizeof(uint32_t);
// Serialize shooting flag as uint8_t 1 for true 0 for false
outBuff[offset++] = shooting ? 1 : 0;
// Serialize hitCount as int32_t in network order
int32_t netHitCount = htonl(hitCount);
memcpy(outBuff + offset, &netHitCount, sizeof(int32_t));
offset += sizeof(int32_t);
// Serialize GAMESTATE as int32_t in network order
int32_t gameState = htonl(GAMESTATE);
//cout << "sending " << gameState << endl;
memcpy(outBuff + offset, &gameState, sizeof(int32_t));
offset += sizeof(int32_t);
return network.sendData(sock, outBuff, offset);
}
static bool sendPlayerTexture(SOCKET sock) {
std::ifstream textureFile("textures/playerTexture.png", std::ios::binary | std::ios::ate);
if (!textureFile.is_open()) {
std::cerr << "Failed to open texture file for reading." << endl;
return false;
}
std::streamsize fileSize = textureFile.tellg();
textureFile.seekg(0, std::ios::beg);
if (fileSize > IN_OUT_BUFF_SIZE - sizeof(uint8_t) - sizeof(uint32_t)) {
std::cerr << "Texture file is too large to send." << endl;
textureFile.close();
return false;
}
std::vector<char> buffer(static_cast<size_t>(fileSize));
if (!textureFile.read(buffer.data(), fileSize)) {
std::cerr << "Failed to read texture file." << endl;
textureFile.close();
return false;
}
textureFile.close();
char outBuff[IN_OUT_BUFF_SIZE];
int offset = 0;
outBuff[offset++] = 2;
uint32_t netFileSize = htonl(static_cast<uint32_t>(fileSize));
memcpy(outBuff + offset, &netFileSize, sizeof(uint32_t));
offset += sizeof(uint32_t);
memcpy(outBuff + offset, buffer.data(), static_cast<size_t>(fileSize));
offset += static_cast<int>(fileSize);
newImageAvaliable = true;
return network.sendData(sock, outBuff, offset);
}
static void handlePacket(const char* packet, size_t packetSize) {
int offset = 0;
uint8_t header = packet[offset++];
if (header == 1) {
// Full state packet expects 2 uint32_t positions, 1 byte for shooting, 1 int32_t hitCount
if (packetSize >= offset + (2 * sizeof(uint32_t) + sizeof(uint8_t) + sizeof(int32_t))) {
// Deserialize positions[0]
uint32_t netPosX;
memcpy(&netPosX, packet + offset, sizeof(uint32_t));
netPosX = ntohl(netPosX);
float posX;
memcpy(&posX, &netPosX, sizeof(float));
offset += sizeof(uint32_t);
// Deserialize positions[1]
uint32_t netPosY;
memcpy(&netPosY, packet + offset, sizeof(uint32_t));
netPosY = ntohl(netPosY);
float posY;
memcpy(&posY, &netPosY, sizeof(float));
offset += sizeof(uint32_t);
// Deserialize shooting flag
uint8_t shootFlag = packet[offset++];
bool remoteShooting = (shootFlag != 0);
// Deserialize hitCount
int32_t netHitCount;
memcpy(&netHitCount, packet + offset, sizeof(int32_t));
int receivedHitCount = ntohl(netHitCount);
offset += sizeof(int32_t);
// Deserialize GAMESTATE
int32_t state;
memcpy(&state, packet + offset, sizeof(int32_t));
int receivedstate = ntohl(state);
offset += sizeof(int32_t);
//cout << "receiving " << receivedstate << endl;
// Update
if (receivedstate == WIN) {
GAMESTATE = LOSS; // we really only detect when we lose and send that but checking anyway
}
else if (receivedstate == LOSS) {
GAMESTATE = WIN;
msg = "YOU ARE A WINNER";
}
otherPlayerPositions[0] = posX;
otherPlayerPositions[1] = posY;
updatePlayer(otherPlayerPositions, vertices2);
if (receivedHitCount >= maxHealth) {
cout << "You died." << endl;
msg = "YOU ARE A LOSER";
GAMESTATE = LOSS;
}
}
}
else if (header == 2) {
// Texture packet
cout << "recieved texture packet" << endl;
int offset = 1;
if (packetSize >= offset + sizeof(uint32_t)) {
// Deserialize texture data
uint32_t netFileSize;
memcpy(&netFileSize, packet + offset, sizeof(uint32_t));
uint32_t fileSize = ntohl(netFileSize);
offset += sizeof(uint32_t);
// Check if the packet contains the full texture data
if (packetSize >= offset + fileSize) {
// Extract the texture data
const char* textureData = packet + offset;
// Save the texture data
std::ofstream outFile("textures/receivedPlayerTexture.png", std::ios::binary);
if (outFile.is_open()) {
outFile.write(textureData, fileSize);
outFile.close();
// Reload texture
//textureIDs[2] = loadTexture("textures/receivedPlayerTexture.png");
}
else {
std::cerr << "Failed to open file for writing texture." << endl;
}
}
else {
std::cerr << "Incomplete texture data received." << endl;
}
}
else {
std::cerr << "Texture packet does not contain size information." << endl;
}
}
else {
// Unknown
}
}
/* TODO:
x wall collisions
x other player rendering
x textures
x texture coordinates
x fix hit detection
? texture mapped walls
x separate rendering functions
x separate raycasting functions
x framerate based shooting
x UI
x start menu
x end menu
x character editor
x character texture sending
x text and typing
*/
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(SCREEN_X, SCREEN_Y, "AWESOME GAME", nullptr, nullptr);
//window = glfwCreateWindow(SCREEN_X, SCREEN_Y, "AWESOME GAME", glfwGetPrimaryMonitor(), nullptr);
if (window == nullptr) {
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
WSACleanup();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
cout << "Failed to initialize GLAD" << endl;
return -1;
}
glViewport(0, 0, SCREEN_X, SCREEN_Y);
GLuint dummyTexture = loadTexture("textures/dummyTexture.png"); // 0
GLuint wallTexture = loadTexture("textures/dummyTexture.png"); // wall texture is broken for now...
GLuint playerTexture = loadTexture("textures/playerTexture.png"); // previously saved texture but will be updated after main menu
GLuint buttonTextures = loadTexture("textures/buttons.png");
GLuint textTextures = loadTexture("textures/boldText.png");
// ensure to update frag shader when adding more textures
GLuint textureIDs[] = { dummyTexture, wallTexture, playerTexture, buttonTextures, textTextures };
int textureCount = sizeof(textureIDs) / sizeof(textureIDs[0]);
Shader shaderProgram("shaders/default.vert", "shaders/default.frag");
Renderer renderer(shaderProgram, textureIDs, textureCount);
shaderProgram.Activate();
glUniform1f(glGetUniformLocation(shaderProgram.ID, "scale"), 1.0f);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
SOCKET sock = INVALID_SOCKET;
while (!glfwWindowShouldClose(window)) {
bool textBoxEditMode = false;
sock = INVALID_SOCKET;
SOCKET listeningSocket = INVALID_SOCKET;
SOCKET client_socket = INVALID_SOCKET;
sockaddr_in client;
int clientSize = sizeof(client);
bool waitingForClient = false;
string placeholderText = "WELCOME, SELECT A MODE TO BEGIN.";
const float btnY = 0.15f;
const float hostBtnX = -0.05f;
const float joinBtnX = 0.35f;
const float soloBtnX = 0.75f;
const float btnW = 300.0f;
const float btnH = 400.0f;
const float btnWPressed = 290.0f;
const float btnHPressed = 390.0f;
Button hostButton = Button(btnW, btnH, hostBtnX, btnY, 200, 50, 50, 4, "HOST");
Button joinButton = Button(btnW, btnH, joinBtnX, btnY, 50, 200, 50, 4, "JOIN");
Button soloButton = Button(btnW, btnH, soloBtnX, btnY, 50, 50, 200, 4, "SOLO");
Text message = Text(1000, 0.35, 0, 4, "ENTER HOST IP ADRESS:");
TextBox IPTextBox = TextBox(600, 50, 0.60, -.250, 4, "__.__.__...");
DrawingBoard drawingBoard(900.0f, 300.0f, -0.65f, 0.1f, "textures/playerTexture.png");
Text drawDesc = Text(800, -0.65, 0.8, 4, "DRAW YOUR PLAYER!");
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
while (!glfwWindowShouldClose(window)) { // main menu
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// networking
if (waitingForClient && listeningSocket != INVALID_SOCKET) {
client_socket = accept(listeningSocket, (sockaddr*)&client, &clientSize);
if (client_socket == INVALID_SOCKET) {
int err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) {
placeholderText = "Client connection failed!";
closesocket(listeningSocket);
listeningSocket = INVALID_SOCKET;
WSACleanup();
waitingForClient = false;
}
}
else {
closesocket(listeningSocket);
listeningSocket = INVALID_SOCKET;
sock = client_socket;
waitingForClient = false;
GAMESTATE = SERVER;
placeholderText = "Client connected!";
for (int i = 0; i < STARTING_PARAMETER_COUNT; i++) positions[i] = defaultServerPos[i];
for (int i = 0; i < STARTING_PARAMETER_COUNT; i++) otherPlayerPositions[i] = defaultClientPos[i];
updatePlayer(positions, vertices1);
updatePlayer(otherPlayerPositions, vertices2);
break;
}
}
Text titleText(SCREEN_X, .35, 0.7f, 4, "AWESOME GAME");
titleText.pushToRenderer(&renderer);
Text statusText(SCREEN_X, 0.35, 0.5f, 4, placeholderText);
statusText.pushToRenderer(&renderer);
Text ipLabel(600, 0.1f, -.250, 4, "IP ADDRESS:");
ipLabel.pushToRenderer(&renderer);
IPTextBox.pushToRenderer(&renderer);
// drawing board logic
drawingBoard.isClicked(window);
drawingBoard.pushToRenderer(&renderer);
// button visual feedback
Button hostButton(btnW, btnH, hostBtnX, btnY, 200, 50, 50, 4, "HOST");
Button joinButton(btnW, btnH, joinBtnX, btnY, 50, 200, 50, 4, "JOIN");
Button soloButton(btnW, btnH, soloBtnX, btnY, 50, 50, 200, 4, "SOLO");
bool hostPressed = hostButton.isClicked(window);
bool joinPressed = joinButton.isClicked(window);
bool soloPressed = soloButton.isClicked(window);
bool hostHovered = hostButton.isHovered(window);
bool joinHovered = joinButton.isHovered(window);
bool soloHovered = soloButton.isHovered(window);
Button hostBtnDraw = hostButton;
Button joinBtnDraw = joinButton;
Button soloBtnDraw = soloButton;
if (hostPressed) {
hostBtnDraw = Button(btnWPressed, btnHPressed, hostBtnX, btnY, 255, 50, 50, 4, "HOST");
}
else if (hostHovered) {
hostBtnDraw = Button(btnW, btnH, hostBtnX, btnY, 230, 50, 50, 4, "HOST");
}
if (joinPressed) {
joinBtnDraw = Button(btnWPressed, btnHPressed, joinBtnX, btnY, 50, 255, 50, 4, "JOIN");
}
else if (joinHovered) {
joinBtnDraw = Button(btnW, btnH, joinBtnX, btnY, 50, 230, 50, 4, "JOIN");
}
if (soloPressed) {
soloBtnDraw = Button(btnWPressed, btnHPressed, soloBtnX, btnY, 50, 50, 255, 4, "SOLO");
}
else if (soloHovered) {
soloBtnDraw = Button(btnW, btnH, soloBtnX, btnY, 50, 50, 230, 4, "SOLO");
}
hostBtnDraw.pushToRenderer(&renderer);
joinBtnDraw.pushToRenderer(&renderer);
soloBtnDraw.pushToRenderer(&renderer);
// instructions
Text hostDesc(1600, 0.35, -0.5f, 4, "* HOST: STARTS A SERVER FOR OTHERS TO JOIN.");
Text joinDesc(1600, 0.35, -0.6f, 4, "* JOIN: CONNECTS TO EXISTING SERVER BY IP.");
Text soloDesc(1600, 0.35, -0.7f, 4, "* SOLO: STARTS A SINGLE PLAYER SESSION.");
hostDesc.pushToRenderer(&renderer);
joinDesc.pushToRenderer(&renderer);
soloDesc.pushToRenderer(&renderer);
drawDesc.pushToRenderer(&renderer);
// button logic
if (hostPressed && !waitingForClient) {
listeningSocket = network.serverSetup();
if (listeningSocket == INVALID_SOCKET) {
placeholderText = "SERVER SETUP ERROR!";
}
else {
waitingForClient = true;
placeholderText = "WAITING FOR CLIENT ON: " + network.printLocalIPAddress();
}
}
if (joinPressed) {
if (listeningSocket != INVALID_SOCKET) {
closesocket(listeningSocket);
listeningSocket = INVALID_SOCKET;
waitingForClient = false;
}
if (IPTextBox.message.empty() || IPTextBox.message == "PLEASE ENTER IP HERE.") {
IPTextBox.message = "PLEASE ENTER IP HERE.";
placeholderText = "PLEASE ENTER A VALID IP ADDRESS.";
}
else {
placeholderText = "IP ADDRESS ERROR TRY AGAIN!";
IPTextBox.pushToRenderer(&renderer);
sock = network.clientSetup(IPTextBox.message);
if (sock == INVALID_SOCKET) {
placeholderText = "IP ADDRESS ERROR TRY AGAIN!";
}
else {
GAMESTATE = CLIENT;
for (int i = 0; i < STARTING_PARAMETER_COUNT; i++) positions[i] = defaultClientPos[i];
for (int i = 0; i < STARTING_PARAMETER_COUNT; i++) otherPlayerPositions[i] = defaultServerPos[i];
updatePlayer(positions, vertices1);
updatePlayer(otherPlayerPositions, vertices2);
break;
}
}
}
if (soloPressed) {
if (listeningSocket != INVALID_SOCKET) {
closesocket(listeningSocket);
listeningSocket = INVALID_SOCKET;
WSACleanup();
waitingForClient = false;
}
GAMESTATE = DEBUG;
cout << "debug/singleplayer mode >>" << endl;
for (int i = 0; i < STARTING_PARAMETER_COUNT; i++) {
positions[i] = defaultServerPos[i];
}
for (int i = 0; i < STARTING_PARAMETER_COUNT; i++) {
otherPlayerPositions[i] = defaultClientPos[i];
}
updatePlayer(positions, vertices1);
updatePlayer(otherPlayerPositions, vertices2);
break;
}
// text box input
if (IPTextBox.isClicked(window)) {
IPTextBox.message = "";
textBoxEditMode = true;
}
else if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) {
textBoxEditMode = false;
}
if (textBoxEditMode) IPTextBox.collectInput(window);
renderer.renderFrame();
if (drawingBoard.saveRequested) {
glFinish();
glReadBuffer(GL_BACK);
float leftNDC = drawingBoard.xpos - 0.5f * (drawingBoard.width / SCREEN_X);
float rightNDC = drawingBoard.xpos + 0.5f * (drawingBoard.width / SCREEN_X);
float bottomNDC = drawingBoard.ypos - 0.5f * (drawingBoard.height / SCREEN_Y);
float topNDC = drawingBoard.ypos + 0.5f * (drawingBoard.height / SCREEN_Y);
int leftPx = static_cast<int>((leftNDC + 1.0f) * 0.5f * SCREEN_X);
int rightPx = static_cast<int>((rightNDC + 1.0f) * 0.5f * SCREEN_X);
int bottomPx = static_cast<int>((bottomNDC + 1.0f) * 0.5f * SCREEN_Y);
int topPx = static_cast<int>((topNDC + 1.0f) * 0.5f * SCREEN_Y);
captureRegion(leftPx, bottomPx, rightPx - leftPx, topPx - bottomPx, drawingBoard.saveAs);
drawingBoard.saved = true;
drawingBoard.saveRequested = false;
}
glfwSwapBuffers(window);
glfwPollEvents();
}
if (GAMESTATE == SERVER || GAMESTATE == CLIENT) {
sendPlayerTexture(sock);
newImageAvaliable = true;
connected = true;
//textureIDs[2] = loadTexture("textures/receivedPlayerTexture.png");
//cout << "set received texture" << endl;
}
else {
//cout << "loaded own texture" << endl;
//textureIDs[2] = loadTexture("textures/playerTexture.png");
}
auto lastTime = std::chrono::high_resolution_clock::now();
Text FPSText = Text(400, 0.8, 0.8, 4, "FPS: 0");
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// multiplayer loop
while (!glfwWindowShouldClose(window) && (GAMESTATE == SERVER || GAMESTATE == CLIENT)) {
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
processInput(sock);
if (!sendStatePacket(sock)) {
cout << "connection lost..." << endl;
break;
}
if (!network.recvData(sock, inBuff, IN_OUT_BUFF_SIZE, bytesReceived)) {
cout << "Connection lost..." << endl;
break;
}
handlePacket(inBuff, bytesReceived);
for (int i = -0.5 * RAY_NUM; i < 0.5 * RAY_NUM; i++) { // cast rays
drawRaycastLine(renderer, xpos + i, xpos, ypos, positions);
drawMap(renderer);
}
// get angle to other player
float angleToOther = atan2f((otherPlayerPositions[0] - positions[0]), (otherPlayerPositions[1] - positions[1]));
float angleToOtherDegrees = angleToOther * (180.0f / 3.14159f);
float relativeAngle = angleToOtherDegrees - xpos;
if (relativeAngle < -180.0f) relativeAngle += 360.0f;
if (relativeAngle > 180.0f) relativeAngle -= 360.0f;
bool onScreen = drawPlayer(renderer, relativeAngle, angleToOther, ypos, otherPlayerPositions, positions, hitCount); // check if we can and draw other player
if (shooting && isCrosshairOverPlayer(xpos, ypos, positions, otherPlayerPositions) && onScreen) {
shooting = false;
hitCount++;
if (hitCount > maxHealth) {
hitCount = 0;
}
}
drawCrosshair(renderer, Renderer::ColorRGB());
drawPlayerQuad(renderer, positions, Renderer::ColorRGB());
drawPlayerQuad(renderer, otherPlayerPositions, Renderer::ColorRGB());
FPSText.message = "FPS: " + std::to_string(int(1.0f / deltaTime));
FPSText.pushToRenderer(&renderer);
renderer.renderFrame();
glfwSwapBuffers(window);
glfwPollEvents();
shooting = false;
ZeroMemory(inBuff, IN_OUT_BUFF_SIZE);
auto currentTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> elapsed = currentTime - lastTime;
deltaTime = elapsed.count();
lastTime = currentTime;
if (newImageAvaliable) {
textureIDs[2] = loadTexture("textures/receivedPlayerTexture.png");
newImageAvaliable = false;
}
}
// debug loop
while (!glfwWindowShouldClose(window) && (GAMESTATE == DEBUG)) {
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
for (int i = -0.5 * RAY_NUM; i < 0.5 * RAY_NUM; i++) { // cast rays
drawRaycastLine(renderer, xpos + i, xpos, ypos, positions);
drawMap(renderer);
}
// angle to other player
float angleToOther = atan2f((otherPlayerPositions[0] - positions[0]), (otherPlayerPositions[1] - positions[1]));
float angleToOtherDegrees = angleToOther * (180.0f / 3.14159f);
float relativeAngle = angleToOtherDegrees - xpos;
if (relativeAngle < -180.0f) relativeAngle += 360.0f;
if (relativeAngle > 180.0f) relativeAngle -= 360.0f;
bool onScreen = drawPlayer(renderer, relativeAngle, angleToOther, ypos, otherPlayerPositions, positions, hitCount);
if (shooting && isCrosshairOverPlayer(xpos, ypos, positions, otherPlayerPositions) && onScreen) {
shooting = false;
hitCount++;
if (hitCount > maxHealth) {
GAMESTATE = WIN;
msg = "GOOD JOB!";
}
}
drawCrosshair(renderer, Renderer::ColorRGB());
drawPlayerQuad(renderer, positions, Renderer::ColorRGB());
drawPlayerQuad(renderer, otherPlayerPositions, Renderer::ColorRGB());
FPSText.message = "FPS: " + std::to_string(int(1.0f / deltaTime));
FPSText.pushToRenderer(&renderer);
renderer.renderFrame();
glfwSwapBuffers(window);
glfwPollEvents();
processInput(sock);
auto currentTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> elapsed = currentTime - lastTime;
deltaTime = elapsed.count();
lastTime = currentTime;
//cout << "fps: " << 1.0f / deltaTime << endl;
}
// end menu
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
while (!glfwWindowShouldClose(window) && (GAMESTATE == WIN || GAMESTATE == LOSS)) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (connected) { // we dont really enforce
sendStatePacket(sock);
network.recvData(sock, inBuff, IN_OUT_BUFF_SIZE, bytesReceived);
handlePacket(inBuff, bytesReceived);
}
Text instrText(SCREEN_X, 0, 0.7f, 4, "PRESS 'C' TO GO BACK TO MAIN MENU");
Text statusText(SCREEN_X, 0, 0.5f, 4, msg);
instrText.pushToRenderer(&renderer);
statusText.pushToRenderer(&renderer);
if (glfwGetKey(window, GLFW_KEY_C) == GLFW_PRESS) {
break;
closesocket(sock);
WSACleanup();
}
renderer.renderFrame();
glfwSwapBuffers(window);
glfwPollEvents();
}
GAMESTATE = START;
msg = "";
hitCount = 0;
}
shaderProgram.Delete();
glfwDestroyWindow(window);
glfwTerminate();
closesocket(sock);
WSACleanup();
return 0;
}