/** * 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 #ifdef __linux__ #include #endif #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.borderless = false; _variables.vsync = true; _variables.running = true; _variables.needsUpdate = false; _variables.shaders = sf::Shader::isAvailable(); _variables.nevermore = false; _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); _loadingHints.push_back(L"This is a hint. A placeholder."); _loadingHints.push_back(L"There are two perfect things: chocolate and 45° isosceles triangle."); _loadingHints.push_back(L"Si les trangles faisaient un dieu, ils lui donneraient trois cótés. — Montesquieu"); _loadingHints.push_back(L"Triangles do not have speed caps, but you will hit the wall eventually."); _loadingHints.push_back(L"when patafour pls owocek you lazy piece of shit"); _loadingHints.push_back(L"Hit left control to switch light types."); _loadingHints.push_back(L"S=√(p(p-a)(p-b)(p-c))"); _loadingHints.push_back(L"S=12141px²"); _loadingHints.push_back(L"Positive vibes. PMA. Positive mental attitude."); _loadingHints.push_back(L"You can lose up to 4856.4px."); _loadingHints.push_back(L"Wasted."); _loadingHints.push_back(L"Ah, the Scalene Triangle."); _loadingHints.push_back(L"Δ"); _loadingHints.push_back(L"Δ\nΔ Δ"); _loadingHints.push_back(L"Segmentation fault (core dumped)"); _loadingHints.push_back(L"The stars act as consolation prizes when you go in the wrong direction."); _loadingHints.push_back(L"[](){}(); [](){}(); [](){}();"); } 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, "b", "borderlesswindow", [this]() { _variables.borderless = true; }); searchArgument(argc, argvStr, "v", "vsync", [this]() { _variables.vsync = true; }); searchArgument(argc, argvStr, "s", "skip", [this]() { _initialState = State::Ingame; }); searchArgument(argc, argvStr, "sf", "skipfullscreen", [this]() { _initialState = State::Ingame; _variables.fullscreen = true; _variables.nevermore = true; }); 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) << "-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) << "-f" << " or " << std::setw(16) << "--fullscreen" << " prevents from playing in windowed mode; try F11" << std::endl; std::cout << std::setw(8) << "-sf" << " or " << std::setw(16) << "--skipfullscreen" << " combines --fullscreen and --skip" << 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) << "-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[], std::string callShort, std::string callLong, std::function lambda) { for(int i = 1; i < argc; ++i) { if((argvStr[i][0] == '-' && argvStr[i][1] == callShort[0]) || argvStr[i] == "--" + callLong) { if((callShort.size() == 1 && argvStr[i].size() == 2) || argvStr[i][2] == callShort[1]) { lambda(); break; } } } } void Triangles::searchArgument(int argc, std::string argvStr[], std::string callShort, std::string callLong, std::function lambda) { for(int i = 1; i < argc; ++i) { if(argvStr[i][0] == '-' && argvStr[i][1] == callShort[0] && 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) { Echo::debug("Deleting current state."); if(_current) delete _current; Echo::debug("Loading state ID: ", stateType); 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); #ifdef __linux__ std::thread t1(&Triangles::loadingRender, this); std::thread t2(&State::init, _current); t1.join(); t2.join(); #else sf::Thread t1(&Triangles::loadingRender, this); sf::Thread t2(&State::init, _current); t1.launch(); t2.launch(); t1.wait(); t2.wait(); #endif _window.lockFramerate(false); _current->refresh(); Echo::debug("Done."); } int Triangles::run() { sf::Clock clock; sf::Time delta, epsilon = sf::seconds(1.f / 30.f); if(_context.running) { Echo::out(Echo::Empty, "Triangles version 0.0.5"); 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 file"); } /** * 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); } } 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() && _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(); #ifdef TRIANGLES_DEBUG _window.draw(_fpsRect); _window.draw(_fps); #endif _window.render(); }