/** * Triangles * Copyright (C) 2016 POSITIVE MENTAL ATTITUDE * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "Triangles.hpp" #include "IngameState.hpp" #include "MenuState.hpp" #include "Utility.hpp" Triangles::Triangles(int argc, char** argv): _returnCode(0), _window("Triangles", sf::Vector2u(640u, 480u)), _current(nullptr), _initialState(State::Menu) { _variables.fullscreen = false; _variables.vsync = false; _variables.running = true; _variables.needsUpdate = false; _variables.shaders = true; _variables.window = &_window; _variables.assets = &_assets; _variables.core = this; _variables.foreground = nullptr; passArguments(argc, argv); if(_variables.running) { init(); refresh(); load(_initialState); } } void Triangles::init() { _context = _variables; _window.setContext(&_context); _window.recreate(); _fps.setFont(_assets.loadFont("data/ttf/canonical/Ubuntu-L.ttf")); _fps.setCharacterSize(12); _fps.setColor(sf::Color::Black); _fps.setPosition(10, 10); _fps.setString("TRIANGLES"); _fpsRect.setPosition(0, 0); _fpsRect.setSize(sf::Vector2f(375.f, 60.f)); _fpsRect.setFillColor(sf::Color(255, 255, 255, 120)); _loadingText.setString("Loading"); _loadingHint.setFont(_assets.loadFont("data/ttf/canonical/Ubuntu-L.ttf")); _loadingText.setFont(_assets.loadFont("data/ttf/canonical/Ubuntu-L.ttf")); _loadingHint.setColor(sf::Color::White); _loadingText.setColor(sf::Color::White); _loadingTriangle.setColor(sf::Color::White); std::ifstream ifs; ifs.open("data/hints.txt"); while(ifs.good()) { std::string s; std::getline(ifs, s); if(s.size() > 1) _loadingHints.push_back(sf::String(s)); } ifs.close(); if(_loadingHints.empty()) _loadingHints.push_back(sf::String("")); } void Triangles::passArguments(int argc, char** argv) { std::string argvStr[argc]; for(int i = 0; i < argc; ++i) { argvStr[i] = argv[i]; if(argvStr[i].size() < 2) argvStr[i] = "--kaczka"; } searchArgument(argc, argvStr, 'f', "fullscreen", [this]() { _variables.fullscreen = true; }); searchArgument(argc, argvStr, 'w', "window", [this]() { _variables.fullscreen = false; }); searchArgument(argc, argvStr, 'v', "vsync", [this]() { _variables.vsync = true; }); searchArgument(argc, argvStr, 's', "skip", [this]() { _initialState = State::Ingame; }); searchArgument(argc, argvStr, 'r', "resolution", [this](std::string param) { if(param == "640x480") { std::cerr << "What year is it?\n"; _returnCode = 1; _variables.running = false; } std::size_t found = param.rfind('x'); if(found == std::string::npos) return; std::string sub = param; std::string sup = param; sub.erase(sub.begin() + found, sub.end()); sup.erase(sup.begin(), sup.begin() + found + 1); unsigned w, h; std::stringstream ss; ss << sub; ss >> w; ss.str(std::string()); ss.clear(); ss << sup; ss >> h; _window.setSize(w, h); }); searchArgument(argc, argvStr, 'h', "help", [this]() { std::cout << "triangles: triangles [OPTION]..." << std::endl; std::cout << "A pseudogame not meant to be run by sane people." << std::endl << std::endl; std::cout << "Copyright (C) 2016 POSITIVE MENTAL ATTITUDE" << std::endl; std::cout << "This program comes with ABSOLUTELY NO WARRANTY;" << std::endl; std::cout << "This is free software, and you are welcome to redistribute it" << std::endl; std::cout << "under certain conditions; see LICENSE.md" << std::endl << std::endl; std::cout << std::setw(8) << "-f" << " or " << std::setw(16) << "--fullscreen" << " prevents from playing in windowed mode; try F11" << std::endl; std::cout << std::setw(8) << "-w" << " or " << std::setw(16) << "--window" << " prevents from playing in fullscreen mode; try F11" << std::endl; std::cout << std::setw(8) << "-v" << " or " << std::setw(16) << "--vsync" << " prevents from playing without vertical synchronization; try F10" << std::endl; std::cout << std::setw(8) << "-s" << " or " << std::setw(16) << "--skip" << " prevents from going to the main menu; try Escape" << std::endl; std::cout << std::setw(8) << "-h" << " or " << std::setw(16) << "--help" << " prevents from running the actual game" << std::endl; std::cout << std::setw(8) << "-r WxH" << " or " << std::setw(16) << "--resolution=WxH" << " prevents from running at 640x480" << std::endl; _variables.running = false; }); } void Triangles::searchArgument(int argc, std::string argvStr[], char callShort, std::string callLong, std::function lambda) { for(int i = 1; i < argc; ++i) { if((argvStr[i][0] == '-' and argvStr[i][1] == callShort) or argvStr[i] == "--" + callLong) { lambda(); break; } } } void Triangles::searchArgument(int argc, std::string argvStr[], char callShort, std::string callLong, std::function lambda) { for(int i = 1; i < argc; ++i) { if(argvStr[i][0] == '-' and argvStr[i][1] == callShort and i < argc - 1) { lambda(argvStr[i + 1]); break; } std::size_t found = argvStr[i].rfind('='); if(found == std::string::npos) continue; std::string sub = argvStr[i]; std::string sup = argvStr[i]; sub.erase(sub.begin() + found, sub.end()); sup.erase(sup.begin(), sup.begin() + found + 1); if(sub == "--" + callLong) { lambda(sup); break; } } } void Triangles::refresh() { float u = _window.getSize().x >= 1024 ? 1.f : 0.75f; _loadingHint.setCharacterSize(20 * u); _loadingText.setCharacterSize(20 * u); _loadingTriangle.setSize(12.f * u); _loadingTriangle.setPosition(_window.getSize().x - 140.f, _window.getSize().y - (u == 1.f ? 45.f : 50.f)); _loadingText.setPosition(_window.getSize().x - 120.f, _window.getSize().y - 60.f); } void Triangles::load(int stateType) { if(_current) delete _current; switch(stateType) { case State::Menu: _current = new MenuState; break; case State::Ingame: _current = new IngameState; break; default: return; } _current->setContext(&_context); _loadingHint.setString(_loadingHints[rand() % _loadingHints.size()]); _loadingHint.setPosition(_window.getSize().x / 2.f - _loadingHint.getLocalBounds().width / 2.f, _window.getSize().y - 60.f); _window.lockFramerate(true); _window.setActive(false); std::thread t1(&Triangles::loadingRender, this); std::thread t2(&State::init, _current); t1.join(); t2.join(); _window.lockFramerate(false); _current->refresh(); } int Triangles::run(unsigned count) { sf::Clock clock; sf::Time delta, epsilon = sf::seconds(1.f / 60.f); if(_context.running) { Echo::out(Echo::Empty, "Triangles version -1.0.0.0"); Echo::out(Echo::Empty, "Copyright (C) 2016 POSITIVE MENTAL ATTITUDE"); Echo::out(Echo::Empty, "This program comes with ABSOLUTELY NO WARRANTY;"); Echo::out(Echo::Empty, "This is free software, and you are welcome to redistribute it"); Echo::out(Echo::Empty, "under certain conditions; see LICENSE.md"); Echo::out(Echo::Info, "This is #", count, " Triangles run on this install.", count >= 666 ? " Thanks for being addicted!" : " I like cookies."); } /** * Main loop */ while(_context.running) { if(_context.needsUpdate) { refresh(); _current->refresh(); _context.needsUpdate = false; _variables = _context; } if(_current->status() != State::Ongoing) { load(_current->status()); } coreThink(); coreInput(); delta = clock.restart(); fpsCalc(delta); while(delta > epsilon) { delta -= epsilon; coreUpdate(epsilon); } coreUpdate(delta); coreRender(); } return _returnCode; } void Triangles::coreThink() { sf::Event event; while(_window.pollEvent(event)) { if(_current->status()) _current->coreThink(event); _window.think(event); if(event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::F11) _context.fullscreen = !_context.fullscreen, _variables.fullscreen = _context.fullscreen, _window.recreate(); if(event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::F10) _context.vsync = !_context.vsync, _variables.vsync = _context.vsync, _window.recreate(); } } void Triangles::coreInput() { _current->coreInput(); _window.think(); } void Triangles::fpsCalc(sf::Time delta) { static sf::Clock clock; static float avg = 0.f, avgL = 0.f; static float min = 10000.f, max = 0.f; static float minL = 0.f, maxL = 0.f; static unsigned avgC = 0; float curr = 1.f / delta.asSeconds(); if(avgC == 0) { avg = curr, avgC = 1; min = curr; max = curr; } else { avg = (avg * avgC + curr) / (avgC + 1); ++avgC; // Called explicitly in order to avoid undefined behaviour. min = std::min(min, curr); max = std::max(max, curr); } if(clock.getElapsedTime() >= sf::seconds(1.f)) { minL = min; maxL = max; avgL = avg; avgC = 0; clock.restart(); } std::ostringstream oss; oss << "Build time: " << __DATE__ << ' ' << __TIME__ << '\n'; oss << "Framerate: (min/max/avg/curr): " << minL << '/' << maxL << '/' << avgL << '/' << curr << '\n'; oss << "Settings: " << (_variables.vsync ? "vsync " : "") << (_variables.fullscreen ? "fullscreen " : "") << (_variables.shaders ? "shaders " : "") << '\n'; _fps.setString(oss.str()); } void Triangles::coreUpdate(sf::Time delta) { _current->coreUpdate(delta); } void Triangles::loadingRender() { _window.setActive(true); while(!_current->status() and _context.running) { _loadingTriangle.rotate(8.f); coreThink(); _window.clear(); _window.draw(_loadingHint); _window.draw(_loadingText); _window.draw(_loadingTriangle); _window.render(); } } void Triangles::coreRender() { _window.clear(); _current->coreRender(_variables.shaders); _window.draw(_fpsRect); _window.draw(_fps); _window.render(); }