#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) { // 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); // 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); // Initialize the times and quadrants data structure timesAndQuadrants.push_back(std::make_pair(1, "00:00:000")); } void Game::run() { sf::Clock clock; // 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); while (window.isOpen()) { float dt = clock.restart().asSeconds(); if(lapPoints.size() < 3) { // Update game time sf::Time gameTime = gameTimeClock.getElapsedTime(); int milis = static_cast(gameTime.asMilliseconds()); int seconds = static_cast(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(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::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); speedText.setString(std::to_string(static_cast(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(speedText); if (lapPoints.size() == 3) { displayEndGame(); } else { window.draw(timerText); } displaySavedTimes(); window.display(); 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 = asciiMap[currY * 194 + currX]; //std::cout << x << ", " << y << 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(); } }