diff --git a/.travis.yml b/.travis.yml index f8bcfd9..c9a79d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,11 @@ matrix: - CONAN_SETTINGS_FLAG="-g cmake -s compiler=\"Visual Studio\" -s compiler.version=15 -s arch=x86_64 -s build_type=Release -b missing" - CMAKE_GENERATOR_FLAG="-G \"Visual Studio 15 2017\" -DCMAKE_GENERATOR_PLATFORM=x64" - BUILD_CONFIG=Release + - DEPLOY_EXE=winBin.zip + cache: + directories: + - $HOME/.conan/data + # Windows msvc Debug - name: "Windows Debug" os: windows @@ -18,6 +23,11 @@ matrix: - CONAN_SETTINGS_FLAG="-g cmake -s compiler=\"Visual Studio\" -s compiler.version=15 -s arch=x86_64 -s build_type=Debug -b missing" - CMAKE_GENERATOR_FLAG="-G \"Visual Studio 15 2017\" -DCMAKE_GENERATOR_PLATFORM=x64" - BUILD_CONFIG=Debug + - DEPLOY_EXE=winBin.zip + cache: + directories: + - $HOME/.conan/data + # MacOS clang Release - name: "MacOS Release" os: osx @@ -26,9 +36,14 @@ matrix: - compiler=clang - COMPILER_CC=clang - COMPILER_CXX=clang++ - - CONAN_SETTINGS_FLAG="-g cmake -s build_type=Release" + - CONAN_SETTINGS_FLAG="-g cmake -s build_type=Release -b missing" - CMAKE_GENERATOR_FLAG="" - BUILD_CONFIG=Release + - DEPLOY_EXE=macBin.zip + cache: + directories: + - $HOME/.conan/data + # MacOS clang Debug - name: "MacOS Debug" os: osx @@ -37,9 +52,14 @@ matrix: - compiler=clang - COMPILER_CC=clang - COMPILER_CXX=clang++ - - CONAN_SETTINGS_FLAG="-g cmake -s build_type=Debug" + - CONAN_SETTINGS_FLAG="-g cmake -s build_type=Debug -b missing" - CMAKE_GENERATOR_FLAG="" - BUILD_CONFIG=Debug + - DEPLOY_EXE=macBin.zip + cache: + directories: + - $HOME/.conan/data + # Linux gcc Release - name: "Linux Release" os: linux @@ -48,13 +68,18 @@ matrix: - COMPILER=g++-8 - COMPILER_CC=gcc-8 - COMPILER_CXX=g++-8 - - CONAN_SETTINGS_FLAG="-g cmake -s build_type=Release -s compiler.libcxx=libstdc++11" + - CONAN_SETTINGS_FLAG="-g cmake -s build_type=Release -s compiler.libcxx=libstdc++11 -b missing" - CMAKE_GENERATOR_FLAG="-G \"Unix Makefiles\"" - BUILD_CONFIG=Release + - DEPLOY_EXE=linuxBin.zip + cache: + directories: + - $HOME/.conan/data addons: apt: sources: ['ubuntu-toolchain-r-test'] packages: ['gcc-8', 'g++-8' ] + # Linux gcc Debug - name: "Linux Debug" os: linux @@ -63,26 +88,38 @@ matrix: - COMPILER=g++-8 - COMPILER_CC=gcc-8 - COMPILER_CXX=g++-8 - - CONAN_SETTINGS_FLAG="-g cmake -s build_type=Debug -s compiler.libcxx=libstdc++11" + - CONAN_SETTINGS_FLAG="-g cmake -s build_type=Debug -s compiler.libcxx=libstdc++11 -b missing" - CMAKE_GENERATOR_FLAG="-G \"Unix Makefiles\"" - BUILD_CONFIG=Debug + - DEPLOY_EXE=linuxBin.zip + cache: + directories: + - $HOME/.conan/data addons: apt: sources: ['ubuntu-toolchain-r-test'] packages: ['gcc-8', 'g++-8' ] allow_failures: - os: osx + - os: linux fast_finish: true # The pre-install script. On windows it will install conan via chocolatey -install: +before_install: - | if [[ "${TRAVIS_OS_NAME}" == "windows" ]]; then - choco install conan - else - pip install --user --upgrade pip - pip install --user conan --upgrade + choco install python3 + export PATH="/c/Python37:/c/Python37/Scripts:$PATH" + python -m pip install --upgrade pip fi +install: + - | + if [[ "${TRAVIS_OS_NAME}" == "windows" ]]; then + pip install conan + else + pip install --user --upgrade pip + pip install --user conan --upgrade + fi # Setup Build before_script: # The path to conan does not get set properly because travis does not restart the shell @@ -109,4 +146,26 @@ script: # Cmake Build - mkdir build && cd build - eval cmake .. ${CMAKE_GENERATOR_FLAG} - - eval cmake --build . --config "${BUILD_CONFIG}" \ No newline at end of file + - eval cmake --build . --config ${BUILD_CONFIG} + +# Check dir +before_deploy: + - | + if [[ "${TRAVIS_OS_NAME}" == "windows" ]]; then + echo %cd% + ls + powershell.exe Compress-Archive -Path .\bin\ -DestinationPath ${DEPLOY_EXE} + else + pwd + ls + zip -r ${DEPLOY_EXE} bin/ + fi +# Deploy to github +deploy: + provider: releases + api_key: ${GITHUB_TOKEN} + file: ${DEPLOY_EXE} + skip_cleanup: true + on: + branch: master + condition: "$BUILD_CONFIG = Release" \ No newline at end of file diff --git a/.vs/tasks.vs.json b/.vs/tasks.vs.json index f73da46..33eed2d 100644 --- a/.vs/tasks.vs.json +++ b/.vs/tasks.vs.json @@ -8,7 +8,7 @@ "workingDirectory": "conan/", "command": "${env.COMSPEC}", "args": [ - "conan install .. -g cmake_multi -s build_type=Release" + "conan install .. -g cmake_multi -s compiler=\"Visual Studio\" -s compiler.version=15 -s arch=x86_64 -s build_type=Release -b missing" ] }, { @@ -18,7 +18,7 @@ "workingDirectory": "conan/", "command": "${env.COMSPEC}", "args": [ - "conan install .. -g cmake_multi -s build_type=Debug" + "conan install .. -g cmake_multi -s compiler=\"Visual Studio\" -s compiler.version=15 -s arch=x86_64 -s build_type=Debug -b missing" ] }, { diff --git a/CMakeLists.txt b/CMakeLists.txt index 10444be..663a40a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,11 @@ else() cmake_policy(VERSION 3.14) endif() +# OS +if(UNIX AND NOT APPLE) + set(LINUX TRUE) +endif() + # Project Creation project (Cnake) set (CMAKE_CXX_STANDARD 17) @@ -36,6 +41,7 @@ else() endif() endif() +# Find some packaages find_package(nlohmann_json 3.2.0 REQUIRED) # Program Asset Management diff --git a/CMakeSettings.json b/CMakeSettings.json index e74fc0a..2ad7bc7 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -10,31 +10,19 @@ "buildCommandArgs": "-v", "ctestCommandArgs": "", "inheritEnvironments": [ "msvc_x64_x64" ], - "variables": [ - { - "name": "CMAKE_MAKE_PROGRAM", - "value": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe", - "type": "FILEPATH" - } - ] + "variables": [] }, { "name": "x64-Release", "generator": "Ninja", - "configurationType": "Release", + "configurationType": "RelWithDebInfo", "buildRoot": "${projectDir}\\build\\${name}", "installRoot": "${projectDir}\\install\\${name}", "cmakeCommandArgs": "", "buildCommandArgs": "-v", "ctestCommandArgs": "", "inheritEnvironments": [ "msvc_x64_x64" ], - "variables": [ - { - "name": "CMAKE_MAKE_PROGRAM", - "value": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe", - "type": "FILEPATH" - } - ] + "variables": [] }, { "name": "WSL-Debug", @@ -48,7 +36,6 @@ "ctestCommandArgs": "", "inheritEnvironments": [ "linux_x64" ], "wslPath": "${defaultWSLPath}", - "addressSanitizerEnabled": true, "addressSanitizerRuntimeFlags": "detect_leaks=0", "variables": [] } diff --git a/README.md b/README.md index e0a05b5..0d4c320 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,10 @@ This is a clone of the famous "Snake" game coded in [C++](http://cplusplus.com) ## Dependencies && Resource SFML v2.5.1 - All the static libraries -- All includes +- All includes + +Conan v1.11.1 +Cmake v3.1+ ## Contributions If you wish to contribute to my little project here for some odd reason, @@ -70,3 +73,7 @@ you are welcome to do so but just follow a few things. ## License This project is under the [MIT License](https://choosealicense.com/licenses/mit/) + +## Credits +[Pickup_03](https://freesound.org/people/LittleRobotSoundFactory/sounds/270342/)"eat.wav" by [Little Robot Sound Factory](https://freesound.org/people/LittleRobotSoundFactory/) under the [Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/legalcode) +[Hit_03](https://freesound.org/people/LittleRobotSoundFactory/sounds/270332/)"die.wav" by [Little Robot Sound Factory](https://freesound.org/people/LittleRobotSoundFactory/) under the [Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/legalcode) diff --git a/assets/audio/bgm.wav b/assets/audio/bgm.wav new file mode 100644 index 0000000..fe3dfc9 Binary files /dev/null and b/assets/audio/bgm.wav differ diff --git a/assets/audio/die.wav b/assets/audio/die.wav new file mode 100644 index 0000000..230a6eb Binary files /dev/null and b/assets/audio/die.wav differ diff --git a/assets/audio/eat.wav b/assets/audio/eat.wav new file mode 100644 index 0000000..75764f1 Binary files /dev/null and b/assets/audio/eat.wav differ diff --git a/assets/modes/GameMode.json b/assets/modes/GameMode.json index fdba642..50fc8f5 100644 --- a/assets/modes/GameMode.json +++ b/assets/modes/GameMode.json @@ -12,6 +12,10 @@ { "textureName": "snakebody", "textureLoc": "SnakeBody.png" + }, + { + "textureName": "fruit", + "textureLoc": "Fruit.png" } ], "objectCount": 1, diff --git a/assets/modes/SettingsMode.json b/assets/modes/SettingsMode.json index 0bef2a8..350ed41 100644 --- a/assets/modes/SettingsMode.json +++ b/assets/modes/SettingsMode.json @@ -3,10 +3,10 @@ "textures": [ { "textureName": "background", - "textureLoc": "MenuBg.png" + "textureLoc": "SettingsSplash.png" } ], - "objectCount": 2, + "objectCount": 8, "objects": [ { "name": "background", @@ -14,10 +14,46 @@ "textureName": "background" }, { - "name": "placeHolder", + "name": "fps60", + "size": [ 230, 130 ], + "position": [ 1010, 180 ], + "textureName": "empty" + }, + { + "name": "fpsUnlimited", + "size": [ 400, 130 ], + "position": [ 1250, 180 ], + "textureName": "empty" + }, + { + "name": "speedSlow", + "size": [ 230, 130 ], + "position": [ 870, 475 ], + "textureName": "empty" + }, + { + "name": "speedMed", + "size": [ 330, 130 ], + "position": [ 1105, 475 ], + "textureName": "empty" + }, + { + "name": "speedFast", + "size": [ 200, 130 ], + "position": [ 1440, 475 ], + "textureName": "empty" + }, + { + "name": "SaveButton", + "size": [ 400, 200 ], + "position": [ 400, 770 ], + "textureName": "empty" + }, + { + "name": "ExitButton", "size": [ 400, 200 ], - "position": [ 760, 430 ], - "textureName": "green" + "position": [ 1110, 770 ], + "textureName": "empty" } ] } \ No newline at end of file diff --git a/assets/textures/Fruit.png b/assets/textures/Fruit.png new file mode 100644 index 0000000..6e46f6f Binary files /dev/null and b/assets/textures/Fruit.png differ diff --git a/assets/textures/SettingsSplash.png b/assets/textures/SettingsSplash.png new file mode 100644 index 0000000..153aba4 Binary files /dev/null and b/assets/textures/SettingsSplash.png differ diff --git a/assets/textures/SnakeHead.png b/assets/textures/SnakeHead.png index d45dafb..938908c 100644 Binary files a/assets/textures/SnakeHead.png and b/assets/textures/SnakeHead.png differ diff --git a/assets/textures/playfield.png b/assets/textures/playfield.png index 990a046..568b684 100644 Binary files a/assets/textures/playfield.png and b/assets/textures/playfield.png differ diff --git a/assets/textures/snakebody.png b/assets/textures/snakebody.png index 261264b..557b503 100644 Binary files a/assets/textures/snakebody.png and b/assets/textures/snakebody.png differ diff --git a/conanfile.txt b/conanfile.txt index 0cb2a67..487c435 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -4,4 +4,5 @@ jsonformoderncpp/3.7.0@vthiery/stable [options] sfml:window=True -sfml:graphics=True \ No newline at end of file +sfml:graphics=True +sfml:audio=True \ No newline at end of file diff --git a/include/GameMode.hpp b/include/GameMode.hpp index dc09fd8..ea7e675 100644 --- a/include/GameMode.hpp +++ b/include/GameMode.hpp @@ -2,6 +2,7 @@ #include "Mode.hpp" #include "Player.hpp" #include +#include class GameMode : public Mode { @@ -12,5 +13,11 @@ class GameMode : public Mode void processKeys(sf::Keyboard::Key, bool); private: Player mPlayer; + sf::Text score; + sf::Font font; + sf::Sound eat; + sf::SoundBuffer eatSound; + sf::Sound die; + sf::SoundBuffer dieSound; float gameSpeed; }; \ No newline at end of file diff --git a/include/Player.hpp b/include/Player.hpp index 9953e86..2322c13 100644 --- a/include/Player.hpp +++ b/include/Player.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include @@ -20,10 +21,11 @@ class Player : public sf::Drawable // Setters void processKeys(sf::Keyboard::Key); // Getters + sf::Vector2f getHeadPos(); // Processors void movePlayer(); void addPart(); - bool safeCheck(sf::RectangleShape&); + bool safeCheck(sf::RectangleShape&, sf::Text&, sf::Sound&, sf::Sound&); private: virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const; private: @@ -31,4 +33,5 @@ class Player : public sf::Drawable Direction m_dir; Direction m_lastDir; std::mutex* mu; + int playerScore; }; diff --git a/include/Settings.hpp b/include/Settings.hpp index 71ce14b..44dbcbc 100644 --- a/include/Settings.hpp +++ b/include/Settings.hpp @@ -1,16 +1,18 @@ #pragma once +#include class Settings { public: - Settings(); + Settings(sf::RenderWindow*); ~Settings() = default; bool getFpsLock() { return fpsLock; }; float getGameSpeed() { return gameSpeed; }; - void setFpsLock(bool temp) { fpsLock = temp; }; + void setFpsLock(bool); void setGameSpeed(float temp) { gameSpeed = temp; }; void saveToFile(); private: bool fpsLock; float gameSpeed; + sf::RenderWindow* window; }; \ No newline at end of file diff --git a/include/SettingsMode.hpp b/include/SettingsMode.hpp index 6364660..42e16a8 100644 --- a/include/SettingsMode.hpp +++ b/include/SettingsMode.hpp @@ -1,14 +1,17 @@ #pragma once #include "Mode.hpp" +#include "Settings.hpp" #include class SettingsMode : public Mode { public: - SettingsMode(std::mutex* mutex); + SettingsMode(std::mutex*, Settings*); virtual std::pair Run(sf::Time, sf::RenderWindow&) override; private: private: - + Settings* settings; + bool fps; + float speed; }; \ No newline at end of file diff --git a/src/GameMode.cpp b/src/GameMode.cpp index 6a9deea..f691e4a 100644 --- a/src/GameMode.cpp +++ b/src/GameMode.cpp @@ -8,10 +8,22 @@ GameMode::GameMode(std::mutex* mutex, float speed) : Mode("GameMode.json", mutex, ModeOption::Game) , mPlayer(objectTextures, mutex) , gameSpeed{ speed } { + eatSound.loadFromFile("assets/audio/eat.wav"); + eat.setBuffer(eatSound); + eat.setVolume(40); + dieSound.loadFromFile("assets/audio/die.wav"); + die.setBuffer(dieSound); + eat.setVolume(60); + font.loadFromFile("assets/fonts/bauh.ttf"); + score.setFont(font); + score.setCharacterSize(30); + score.setString("Score : 0"); + score.setPosition(1920 - (score.getLocalBounds().width + (score.getCharacterSize() * 2)), 0); + screenObjects.emplace_back(&score); screenObjects.emplace_back(&mPlayer); sf::RectangleShape food(sf::Vector2f(50, 50)); - food.setPosition(500, 500); - Mode::pushObject("food", food, "Blue"); + food.setPosition(510, 490); + Mode::pushObject("food", food, "fruit"); } std::pair GameMode::Run(sf::Time time, sf::RenderWindow& window) { @@ -39,13 +51,15 @@ std::pair GameMode::Run(sf::Time time, sf::RenderWindow& // Update Game Logic if (timeBank.asSeconds() > gameSpeed) { mPlayer.movePlayer(); - if (mPlayer.safeCheck(screenObjectsMap["food"])) { + if (mPlayer.safeCheck(screenObjectsMap["food"], score, eat, die)) { return std::make_pair(ModeAction::Add, ModeOption::Lose); } + auto [x, y] = mPlayer.getHeadPos(); + std::cout << "[" << x << "," << y << "]" << std::endl; timeBank -= (sf::seconds)(gameSpeed); } else { timeBank += time; } - // Im case of no state changes + // In case of no state changes return std::make_pair(ModeAction::None, ModeOption::None); } diff --git a/src/Player.cpp b/src/Player.cpp index 9db11d0..7eadc53 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -1,16 +1,19 @@ #include "Player.hpp" #include #include +#include Player::Player(const std::map& textures, std::mutex* mut) - : snakeBody(5, sf::RectangleShape(sf::Vector2f(50.0f, 50.0f))) + : snakeBody(4, sf::RectangleShape(sf::Vector2f(50.0f, 50.0f))) , m_dir{ Direction::Left } , m_lastDir{ Direction::Left } , mu{ mut } + , playerScore{ 0 } { for (int i = 0; i < snakeBody.size(); i++) { - snakeBody[i].setPosition((float)(800 + (i * 100)), 500.0f); + snakeBody[i].setPosition((float)(985 + (i * 50)), 565.0f); snakeBody[i].setTexture(&(textures.at("snakebody"))); + snakeBody[i].setOrigin(snakeBody[i].getSize().x / 2, snakeBody[i].getSize().y / 2); } snakeBody[0].setTexture(&(textures.at("snakehead"))); } @@ -52,20 +55,25 @@ void Player::processKeys(sf::Keyboard::Key key) { void Player::movePlayer() { sf::Vector2f vel; auto scale = snakeBody[0].getSize().x; + auto* head = &snakeBody[0]; std::cout << scale; switch (m_dir) { case Up: vel = sf::Vector2f(0, -scale); + head->setRotation(90); break; case Right: vel = sf::Vector2f(scale, 0); + head->setRotation(180); break; case Down: vel = sf::Vector2f(0, scale); + head->setRotation(270); break; case Left: vel = sf::Vector2f(-scale, 0); + head->setRotation(0); break; default: break; @@ -87,28 +95,38 @@ void Player::addPart() { mu->unlock(); } -bool Player::safeCheck(sf::RectangleShape& fruit) { +bool Player::safeCheck(sf::RectangleShape& fruit, sf::Text& text, sf::Sound& eat, sf::Sound& die) { auto head = snakeBody[0]; - sf::Vector2f headPos(head.getPosition().x + (head.getSize().x / 2), head.getPosition().y + (head.getSize().y / 2)); + sf::Vector2f headPos(head.getPosition().x, head.getPosition().y); sf::Vector2f fruitPos(fruit.getPosition().x + (fruit.getSize().x / 2), fruit.getPosition().y + (fruit.getSize().y / 2)); - for (int i = 1; i < snakeBody.size(); i++) { - if (snakeBody[i].getGlobalBounds().contains(headPos)) { + for (int i = 1; i < snakeBody.size(); i++) { // Check if snake has eaten itself + if (snakeBody[i].getGlobalBounds().intersects(snakeBody[0].getGlobalBounds())) { + die.play(); return true; } } - if (head.getGlobalBounds().contains(fruitPos)) { - std::srand((unsigned int)std::time(NULL)); + if (head.getGlobalBounds().contains(fruitPos)) { // Check if snake ate fruit and is so replace it Player::addPart(); - float x = (float)((rand() % (1920 / 50)) * 50); - float y = (float)((rand() % (1080 / 50)) * 50); + eat.play(); + text.setString("Score : " + std::to_string(++playerScore)); + float x = (float)((rand() % (1920 / 50)) * 50) + 10.0f; + float y = (float)((rand() % (1080 / 50)) * 50) - 10.0f; + while (x < 310 || x >= 1610 || y < 40 || y >= 1040) { + x = (float)((rand() % (1920 / 50)) * 50) + 10.0f; + y = (float)((rand() % (1080 / 50)) * 50) - 10.0f; + } bool done = true; fruit.setPosition(x, y); fruitPos = sf::Vector2f(fruit.getPosition().x + (fruit.getSize().x / 2), fruit.getPosition().y + (fruit.getSize().y / 2)); do { for (int i = 1; i < snakeBody.size(); i++) { if (snakeBody[i].getGlobalBounds().contains(fruitPos)) { - x = (float)((rand() % (1920 / 50)) * 50); - y = (float)((rand() % (1080 / 50)) * 50); + x = (float)((rand() % (1920 / 50)) * 50) + 10.0f; + y = (float)((rand() % (1080 / 50)) * 50) - 10.0f; + while (x < 310 || x >= 1610 || y < 40 || y >= 1040) { + x = (float)((rand() % (1920 / 50)) * 50) + 10.0f; + y = (float)((rand() % (1080 / 50)) * 50) - 10.0f; + } fruit.setPosition(x, y); fruitPos = sf::Vector2f(fruit.getPosition().x + (fruit.getSize().x / 2), fruit.getPosition().y + (fruit.getSize().y / 2)); done = false; @@ -117,12 +135,18 @@ bool Player::safeCheck(sf::RectangleShape& fruit) { done = true; } } while (!done); - } else if (headPos.x < 0 || headPos.x > 1920 || headPos.y < 0 || headPos.y > 1080) { + } else if (headPos.x < 310 || headPos.x > 1610 || headPos.y < 40 || headPos.y > 1040) { + die.play(); return true; } return false; } + +sf::Vector2f Player::getHeadPos() { + return snakeBody[0].getPosition(); +} + void Player::draw(sf::RenderTarget& target, sf::RenderStates states) const { for (auto& snakePart : snakeBody) { target.draw(snakePart, states); diff --git a/src/Settings.cpp b/src/Settings.cpp index 7c8ef4f..b06fff9 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -3,26 +3,28 @@ #include #include -Settings::Settings() { +Settings::Settings(sf::RenderWindow* win) : window{ win } { std::fstream file; file.open("assets/settings.txt", std::ios::in); std::string line; - if (!file.fail()) { + if (!file.fail()) { // Load from existing file std::getline(file, line, '\n'); std::cout << line << std::endl; std::getline(file, line, '\n'); std::cout << line << std::endl; if (line == "true") { fpsLock = true; + win->setFramerateLimit(60); } else { fpsLock = false; + win->setFramerateLimit(0); } std::getline(file, line, '\n'); std::cout << line << std::endl; std::getline(file, line, '\n'); std::cout << line << std::endl; gameSpeed = std::stof(line); - } else { + } else { // Create file if one does not exist file.open("assets/settings.txt", std::ios::out); file << "# fpslock\n" << "false\n" << "# gamespeed\n" << ".10f\n"; fpsLock = false; @@ -31,6 +33,14 @@ Settings::Settings() { file.close(); } +void Settings::setFpsLock(bool temp) { + fpsLock = temp; + if (fpsLock) + window->setFramerateLimit(60); + else + window->setFramerateLimit(0); +} + void Settings::saveToFile() { std::fstream file; file.open("assets/settings.txt", std::ios::out); diff --git a/src/SettingsMode.cpp b/src/SettingsMode.cpp index ba1f2ab..56bd8ad 100644 --- a/src/SettingsMode.cpp +++ b/src/SettingsMode.cpp @@ -1,6 +1,10 @@ #include "SettingsMode.hpp" +#include "Settings.hpp" +#include -SettingsMode::SettingsMode(std::mutex* mutex) : Mode("SettingsMode.json", mutex, ModeOption::Intro) { +SettingsMode::SettingsMode(std::mutex* mutex, Settings* settings) : settings{ settings }, Mode("SettingsMode.json", mutex, ModeOption::Intro) { + fps = settings->getFpsLock(); + speed = settings->getGameSpeed(); auto mode = sf::VideoMode::getDesktopMode(); } @@ -16,11 +20,34 @@ std::pair SettingsMode::Run(sf::Time time, sf::RenderWin case sf::Event::KeyPressed: switch (evnt.key.code) { + case sf::Keyboard::Escape: case sf::Keyboard::BackSpace: return std::make_pair(ModeAction::DropTo, ModeOption::Menu); break; } break; + case sf::Event::MouseButtonPressed: { + sf::Vector2i mousePos = sf::Mouse::getPosition(window); + sf::Vector2f mousePosF(static_cast(mousePos.x), static_cast(mousePos.y)); + if (screenObjectsMap["fps60"].getGlobalBounds().contains(mousePosF)) + fps = true; + else if (screenObjectsMap["fpsUnlimited"].getGlobalBounds().contains(mousePosF)) + fps = false; + else if (screenObjectsMap["speedSlow"].getGlobalBounds().contains(mousePosF)) + speed = .20f; + else if (screenObjectsMap["speedMed"].getGlobalBounds().contains(mousePosF)) + speed = .10f; + else if (screenObjectsMap["speedFast"].getGlobalBounds().contains(mousePosF)) + speed = .05f; + else if (screenObjectsMap["SaveButton"].getGlobalBounds().contains(mousePosF)) { + settings->setFpsLock(fps); + settings->setGameSpeed(speed); + return std::make_pair(ModeAction::DropTo, ModeOption::One); + } + else if (screenObjectsMap["ExitButton"].getGlobalBounds().contains(mousePosF)) + return std::make_pair(ModeAction::DropTo, ModeOption::One); + } + break; default: break; } @@ -31,6 +58,6 @@ std::pair SettingsMode::Run(sf::Time time, sf::RenderWin } else { timeBank += time; } - // Im case of no state changes + // In case of no state changes return std::make_pair(ModeAction::None, ModeOption::None); } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 1065920..69359cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ // Preprocessor #include +#include #include #include #include @@ -7,12 +8,16 @@ #include #include #include +#include #include #include #include #include // Entry Point int main() { + // Seed rand + std::srand((unsigned int)std::time(NULL)); + // Create mutex for synccing threads static std::mutex mu; // Setup Window auto desktop = sf::VideoMode::getDesktopMode(); @@ -20,10 +25,7 @@ int main() { sf::RenderWindow window(desktop, "Cnake", sf::Style::None); window.setActive(false); // Load Settings - Settings gameSettings; - if (gameSettings.getFpsLock()) { - window.setFramerateLimit(60); - } + Settings gameSettings(&window); // Prepare Stack std::stack> ModeStack; ModeStack.push(std::make_unique(&mu)); @@ -68,6 +70,26 @@ int main() { } std::cout << "Rendering thread closed!" << std::endl; }); + // Variables for manipulation music thread + std::atomic killSong = false; + std::atomic playSong = false; + // Create Music Thread + std::thread MusicThread([&isRunning, &killSong, &playSong] { + sf::Music music; + music.openFromFile("assets/audio/bgm.wav"); + music.setVolume(70); + music.setLoop(true); + while (isRunning) { + if (killSong) { + music.pause(); + killSong = false; + } else if (playSong) { + music.play(); + playSong = false; + } + } + music.stop(); + }); // Begin Game sf::Clock GameClock; while (!ModeStack.empty()) { @@ -88,13 +110,15 @@ int main() { ModeStack.push(std::make_unique(&mu)); break; case ModeOption::Settings: - ModeStack.push(std::make_unique(&mu)); + ModeStack.push(std::make_unique(&mu, &gameSettings)); break; case ModeOption::Game: + playSong = true; ModeStack.push(std::make_unique(&mu, gameSettings.getGameSpeed())); break; case ModeOption::Paused: case ModeOption::Lose: + killSong = true; isPaused = true; while (!isWaiting) { } @@ -114,16 +138,26 @@ int main() { // Safely Kill Render Thread if ((result.second == ModeOption::None) || (result.second == ModeOption::One && ModeStack.size() == 1)) { isRunning = false; + MusicThread.join(); RenderThread.join(); - } + } else if (ModeStack.top()->type() == ModeOption::Game) { + killSong = true; + } mu.lock(); if (ModeStack.top()->type() == ModeOption::Paused) { + playSong = true; showStats = true; } if (result.second == ModeOption::One) { + if (ModeStack.top()->type() == ModeOption::Game) { + killSong = true; + } ModeStack.pop(); } else { while (!(ModeStack.empty()) && (ModeStack.top()->type() != result.second)) { + if (ModeStack.top()->type() == ModeOption::Game) { + killSong = true; + } ModeStack.pop(); } }