455 lines
16 KiB
C++
455 lines
16 KiB
C++
#include "game.h"
|
|
|
|
#define DELAY 30000
|
|
|
|
Game::Game()
|
|
:max_x(1000), max_y(800), direction(0), window(sf::VideoMode(max_x, max_y), "ASCII RACER"),
|
|
racecar(400.0f, 30.0f, 1.7f, sf::Color::Red), bot(400.0f, 30.0f, 1.7f, sf::Color::Green) { // maxSpeed, acceleration, and steerSpeed values
|
|
if (!font.loadFromFile("cascaydia.otf")) {
|
|
std::cerr << "Failed to load font" << std::endl;
|
|
exit(1);
|
|
}
|
|
|
|
if (!backgroundMusic.openFromFile("background_music.wav")) {
|
|
std::cerr << "Failed to load background music" << std::endl;
|
|
exit(1);
|
|
}
|
|
|
|
// Set the volume (adjust as needed)
|
|
backgroundMusic.setVolume(50);
|
|
|
|
// Play the background music
|
|
backgroundMusic.play();
|
|
|
|
// Create a text object for displaying speed
|
|
speedText.setFont(font);
|
|
nitroText.setFont(font);
|
|
|
|
// Create a map text object and set its properties
|
|
mapText.setFont(font);
|
|
mapText.setCharacterSize(24);
|
|
mapText.setFillColor(sf::Color::White);
|
|
mapText.setPosition(0, 0);
|
|
mapText.setOutlineColor(sf::Color::Red);
|
|
|
|
timerText.setFont(font);
|
|
timerText.setCharacterSize(24);
|
|
timerText.setFillColor(sf::Color::White);
|
|
|
|
racecar.setPosition(1000, 80); // Set initial position for the racecar
|
|
racecar.setRotation(180);
|
|
|
|
bot.setPosition(1000, 80); // Set initial position for the bot
|
|
bot.setRotation(180);
|
|
|
|
// Initialize the times and quadrants data structure
|
|
timesAndQuadrants.push_back(std::make_pair(1, "00:00:000"));
|
|
}
|
|
|
|
void Game::run() {
|
|
sf::Clock clock;
|
|
|
|
loadPlayerMovesFromFile("player_moves.txt");
|
|
|
|
// Create a view that will follow the racecar
|
|
view = sf::View(sf::FloatRect(0, 0, max_x, max_y));
|
|
view.setCenter(racecar.getX(), racecar.getY()); // Center the view on the racecar
|
|
window.setView(view);
|
|
|
|
backgroundMusic.setLoop(true);
|
|
|
|
int currentMove = 0;
|
|
|
|
while (window.isOpen()) {
|
|
float dt = clock.restart().asSeconds();
|
|
|
|
if(lapPoints.size() < 3) {
|
|
|
|
botMove(currentMove, dt);
|
|
currentMove++;
|
|
|
|
// Update game time
|
|
sf::Time gameTime = gameTimeClock.getElapsedTime();
|
|
int milis = static_cast<int>(gameTime.asMilliseconds());
|
|
int seconds = static_cast<int>(gameTime.asSeconds());
|
|
int minutes = seconds / 60;
|
|
seconds %= 60;
|
|
milis %= 1000;
|
|
|
|
// Use std::setw and std::setfill to format numbers as two digits
|
|
std::ostringstream timerStream;
|
|
timerStream << std::setw(2) << std::setfill('0') << minutes << ":"
|
|
<< std::setw(2) << std::setfill('0') << seconds << ":"
|
|
<< std::setw(2) << std::setfill('0') << milis;
|
|
|
|
std::string timerString = timerStream.str();
|
|
timerText.setString(timerString);
|
|
|
|
// Detect when the racecar changes quadrants and save the time
|
|
int currentQuadrant = determineQuadrant(racecar.getX(), racecar.getY());
|
|
if (currentQuadrant != timesAndQuadrants.back().first) {
|
|
timesAndQuadrants.push_back(std::make_pair(currentQuadrant, timerString));
|
|
}
|
|
|
|
determineLap(static_cast<int>(gameTime.asMilliseconds()), timerString);
|
|
}
|
|
|
|
sf::Event event;
|
|
while (window.pollEvent(event)) {
|
|
if (event.type == sf::Event::Closed) {
|
|
window.close();
|
|
backgroundMusic.stop();
|
|
}
|
|
}
|
|
|
|
if(lapPoints.size() < 3) {
|
|
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift)) {
|
|
if(nitro > 0) {
|
|
nitro--;
|
|
float newX = racecar.getX() + ((racecar.getSpeed() + 1.1) * dt * cos(racecar.getRotation()));
|
|
float newY = racecar.getY() + ((racecar.getSpeed() + 1.1) * dt * sin(racecar.getRotation()));
|
|
//std::cout << "New: " << newX << "," << newY << " Old: " << racecar.getX() << "," << racecar.getY() << std::endl;
|
|
if (!isTrackCollision(newX, newY)) {
|
|
racecar.boost(dt);
|
|
//std::cout << nitro << std::endl;
|
|
mapText.setOutlineThickness(0);
|
|
} else {
|
|
//racecar.stop();
|
|
racecar.boost(dt);
|
|
modifier += 0.01;
|
|
mapText.setOutlineThickness(6);
|
|
}
|
|
}
|
|
else {
|
|
if (racecar.getSpeed() < 0) {
|
|
racecar.brake(dt);
|
|
} else {
|
|
float newX = racecar.getX() + ((racecar.getSpeed() + 1.1) * dt * cos(racecar.getRotation()));
|
|
float newY = racecar.getY() + ((racecar.getSpeed() + 1.1) * dt * sin(racecar.getRotation()));
|
|
//std::cout << "New: " << newX << "," << newY << " Old: " << racecar.getX() << "," << racecar.getY() << std::endl;
|
|
|
|
if (!isTrackCollision(newX, newY)) {
|
|
racecar.accelerate(dt);
|
|
mapText.setOutlineThickness(0);
|
|
} else {
|
|
//racecar.stop();
|
|
modifier += 0.01;
|
|
mapText.setOutlineThickness(6);
|
|
}
|
|
}
|
|
}
|
|
} else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
|
|
if (racecar.getSpeed() < 0) {
|
|
racecar.brake(dt);
|
|
} else {
|
|
float newX = racecar.getX() + ((racecar.getSpeed() + 1.1) * dt * cos(racecar.getRotation()));
|
|
float newY = racecar.getY() + ((racecar.getSpeed() + 1.1) * dt * sin(racecar.getRotation()));
|
|
//std::cout << "New: " << newX << "," << newY << " Old: " << racecar.getX() << "," << racecar.getY() << std::endl;
|
|
|
|
if (!isTrackCollision(newX, newY)) {
|
|
racecar.accelerate(dt);
|
|
mapText.setOutlineThickness(0);
|
|
} else {
|
|
//racecar.stop();
|
|
modifier += 0.01;
|
|
mapText.setOutlineThickness(6);
|
|
}
|
|
}
|
|
} else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
|
|
if (racecar.getSpeed() > 0) {
|
|
racecar.brake(dt);
|
|
} else {
|
|
float newX = racecar.getX() + ((racecar.getSpeed() + 1.1) * dt * cos(racecar.getRotation()));
|
|
float newY = racecar.getY() + ((racecar.getSpeed() + 1.1) * dt * sin(racecar.getRotation()));
|
|
//std::cout << "New: " << newX << "," << newY << " Old: " << racecar.getX() << "," << racecar.getY() << std::endl;
|
|
|
|
if (!isTrackCollision(newX, newY)) {
|
|
racecar.reverse(dt);
|
|
mapText.setOutlineThickness(0);
|
|
} else {
|
|
//racecar.stop();
|
|
modifier += 0.01;
|
|
mapText.setOutlineThickness(6);
|
|
}
|
|
}
|
|
} else {
|
|
racecar.decelerate(dt);
|
|
}
|
|
|
|
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
|
|
racecar.steer(dt, -1.0f); // Steer left
|
|
} else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
|
|
racecar.steer(dt, 1.0f); // Steer right
|
|
}
|
|
|
|
racecar.update(dt);
|
|
bot.update(dt);
|
|
|
|
nitroText.setString("Nitro: " + std::to_string(nitro));
|
|
nitroText.setCharacterSize(24);
|
|
nitroText.setFillColor(sf::Color::White);
|
|
nitroText.setPosition(racecar.getX() - 475, racecar.getY() + 300);
|
|
|
|
speedText.setString(std::to_string(static_cast<int>(racecar.getSpeed())) + " km/h");
|
|
speedText.setCharacterSize(24);
|
|
speedText.setFillColor(sf::Color::Green);
|
|
speedText.setPosition(racecar.getX() - 475, racecar.getY() + 350);
|
|
if(racecar.getSpeed() < 0){
|
|
speedText.setFillColor(sf::Color::Blue);
|
|
}
|
|
if(racecar.getSpeed() >= 250){
|
|
speedText.setCharacterSize(27);
|
|
speedText.setFillColor(sf::Color::Yellow);
|
|
}
|
|
if(racecar.getSpeed() >= 360){
|
|
speedText.setCharacterSize(30);
|
|
speedText.setFillColor(sf::Color::Red);
|
|
}
|
|
timerText.setPosition(racecar.getX() - 80, racecar.getY() - 350);
|
|
|
|
// Update the view to follow the racecar
|
|
view.setCenter(racecar.getX(), racecar.getY());
|
|
window.setView(view);
|
|
}
|
|
|
|
window.clear();
|
|
mapText.setString(asciiMap);
|
|
window.draw(mapText);
|
|
window.draw(racecar.getDrawable());
|
|
window.draw(bot.getDrawable());
|
|
window.draw(speedText);
|
|
window.draw(nitroText);
|
|
|
|
displaySavedTimes();
|
|
|
|
if (lapPoints.size() == 3) {
|
|
displayEndGame();
|
|
}
|
|
else {
|
|
window.draw(timerText);
|
|
}
|
|
|
|
window.display();
|
|
|
|
if (lapPoints.size() == 3) {
|
|
while (window.isOpen()) {
|
|
sf::Event event;
|
|
while (window.pollEvent(event)) {
|
|
if (event.type == sf::Event::Closed) {
|
|
window.close();
|
|
backgroundMusic.stop();
|
|
}
|
|
}
|
|
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Enter))
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
sf::sleep(sf::microseconds(DELAY));
|
|
}
|
|
}
|
|
|
|
bool Game::isTrackCollision(float x, float y) {
|
|
if (x <= 0) {
|
|
return true;
|
|
} else if (y <= 0) {
|
|
return true;
|
|
} else {
|
|
int currX = (x / 13.85) - 1;
|
|
int currY = (y / 27.88) - 1;
|
|
char currentChar;
|
|
if (currY * 194 + currX < 6378)
|
|
currentChar = asciiMap[currY * 194 + currX];
|
|
else
|
|
return true;
|
|
//td::cout << currY * 194 + currX << std::endl;
|
|
if (currentChar == '\\' || currentChar == '|' || currentChar == '/' || currentChar == '-' || currentChar == '_') {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false; // No collision with track or out of bounds
|
|
}
|
|
|
|
void Game::determineLap(int millis, std::string timerString) {
|
|
lapFinished = true;
|
|
int requiredOrder[] = {1,2,1,2,3,4,1};
|
|
if(timesAndQuadrants.size() >= 7) {
|
|
for (int i = 0; i < 7; i++) {
|
|
if (std::get<0>(timesAndQuadrants[i]) != requiredOrder[i])
|
|
lapFinished = false;
|
|
}
|
|
}
|
|
else {
|
|
lapFinished = false;
|
|
}
|
|
|
|
if (lapFinished) {
|
|
int penality = (millis - lastRound) * modifier;
|
|
if (penality > 100000 ) penality = 100000;
|
|
unsigned int points = (100000 - penality);
|
|
lapPoints.push_back(points);
|
|
timesAndQuadrants.clear();
|
|
timesAndQuadrants.push_back(std::make_pair(1, timerString));
|
|
lastRound = millis;
|
|
modifier = 1.0f;
|
|
}
|
|
}
|
|
|
|
int Game::determineQuadrant(float x, float y) {
|
|
if (y <= 460 && x <= 1150)
|
|
return 1;
|
|
if (y > 460 && x <= 1150)
|
|
return 2;
|
|
if (y > 460 && x > 1150)
|
|
return 3;
|
|
if (y <= 460 && x > 1150)
|
|
return 4;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Game::displaySavedTimes() {
|
|
// Display the saved times on the left side of the screen
|
|
int textY = 10; // Adjust the vertical position as needed
|
|
for (const auto& lapTime : lapPoints) {
|
|
std::string timeString = "Lap points: " + std::to_string(lapTime);
|
|
sf::Text text;
|
|
text.setFont(font);
|
|
text.setCharacterSize(18);
|
|
text.setFillColor(sf::Color::White);
|
|
text.setPosition(racecar.getX() - 475, (racecar.getY() - 300) + textY);
|
|
text.setString(timeString);
|
|
window.draw(text);
|
|
textY += 30; // Adjust the spacing as needed
|
|
}
|
|
for (const auto& timeAndQuadrant : timesAndQuadrants) {
|
|
std::string timeString = "Quadrant " + std::to_string(timeAndQuadrant.first) + ": " + timeAndQuadrant.second;
|
|
sf::Text text;
|
|
text.setFont(font);
|
|
text.setCharacterSize(18);
|
|
text.setFillColor(sf::Color::White);
|
|
text.setPosition(racecar.getX() - 475, (racecar.getY() - 300) + textY);
|
|
text.setString(timeString);
|
|
window.draw(text);
|
|
textY += 30; // Adjust the spacing as needed
|
|
}
|
|
}
|
|
|
|
void Game::displayEndGame() {
|
|
// Load high scores from a file
|
|
loadHighScores();
|
|
|
|
// Get the best score from the current run
|
|
std::sort(lapPoints.begin(), lapPoints.end());
|
|
int bestScore = lapPoints.back();
|
|
|
|
// If the new score is in the top ten, add it
|
|
if (highScores.size() < 10 || bestScore > highScores.back()) {
|
|
highScores.push_back(bestScore);
|
|
}
|
|
|
|
// Sort the high scores in descending order
|
|
std::sort(highScores.rbegin(), highScores.rend());
|
|
|
|
// Keep only the top ten scores
|
|
if (highScores.size() > 10) {
|
|
highScores.pop_back();
|
|
}
|
|
|
|
// Write the updated high scores back to the file
|
|
writeHighScores();
|
|
|
|
// Display the best score from the current run and the top ten high scores
|
|
int textY = 10; // Adjust the vertical position as needed
|
|
|
|
// Display the best score from the current run
|
|
std::string bestScoreString = "Best Lap: " + std::to_string(bestScore);
|
|
sf::Text bestScoreText;
|
|
bestScoreText.setFont(font);
|
|
bestScoreText.setCharacterSize(20);
|
|
bestScoreText.setFillColor(sf::Color::White);
|
|
bestScoreText.setPosition(racecar.getX(), (racecar.getY() - 300) + textY);
|
|
bestScoreText.setString(bestScoreString);
|
|
window.draw(bestScoreText);
|
|
textY += 30; // Adjust the spacing as needed
|
|
|
|
// Display the top ten high scores
|
|
for (size_t i = 0; i < highScores.size(); ++i) {
|
|
std::string scoreString = "High Score #" + std::to_string(i + 1) + ": " + std::to_string(highScores[i]);
|
|
sf::Text scoreText;
|
|
scoreText.setFont(font);
|
|
scoreText.setCharacterSize(20);
|
|
scoreText.setFillColor(sf::Color::White);
|
|
scoreText.setPosition(racecar.getX(), (racecar.getY() - 300) + textY);
|
|
scoreText.setString(scoreString);
|
|
window.draw(scoreText);
|
|
textY += 30; // Adjust the spacing as needed
|
|
}
|
|
}
|
|
|
|
void Game::loadHighScores() {
|
|
std::ifstream file(highScoresFile);
|
|
if (file.is_open()) {
|
|
int score;
|
|
while (file >> score) {
|
|
highScores.push_back(score);
|
|
}
|
|
file.close();
|
|
}
|
|
}
|
|
|
|
// Function to write high scores to a file
|
|
void Game::writeHighScores() {
|
|
std::ofstream file(highScoresFile);
|
|
if (file.is_open()) {
|
|
for (const int& score : highScores) {
|
|
file << score << std::endl;
|
|
}
|
|
file.close();
|
|
}
|
|
}
|
|
|
|
void Game::loadPlayerMovesFromFile(const std::string& filename) {
|
|
std::ifstream file(filename);
|
|
if (file.is_open()) {
|
|
int move;
|
|
while (file >> move) {
|
|
lastPlayerMoves.push_back(move);
|
|
}
|
|
file.close();
|
|
}
|
|
}
|
|
|
|
void Game::botMove(int i, float dt) {
|
|
int move = lastPlayerMoves[i];
|
|
switch (move) {
|
|
case 0:
|
|
bot.brake(dt);
|
|
break;
|
|
case 1:
|
|
bot.accelerate(dt);
|
|
break;
|
|
case 2:
|
|
bot.decelerate(dt);
|
|
break;
|
|
case 3:
|
|
bot.decelerate(dt);
|
|
bot.steer(dt, -1.0f);
|
|
break;
|
|
case 4:
|
|
bot.decelerate(dt);
|
|
bot.steer(dt, 1.0f);
|
|
break;
|
|
case 5:
|
|
bot.accelerate(dt);
|
|
bot.steer(dt, -1.0f);
|
|
break;
|
|
case 6:
|
|
bot.accelerate(dt);
|
|
bot.steer(dt, 1.0f);
|
|
break;
|
|
}
|
|
}
|