|
@@ -0,0 +1,382 @@
|
|
|
+/**
|
|
|
+ * 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 <http://www.gnu.org/licenses/>.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <sstream>
|
|
|
+#include <thread>
|
|
|
+#include <fstream>
|
|
|
+#include <iomanip>
|
|
|
+#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<void()> 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<void(std::string param)> 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);
|
|
|
+
|
|
|
+ _window.setActive(false); // Weird behaviour without this.
|
|
|
+ _window.lockFramerate(true);
|
|
|
+
|
|
|
+ _loadingHint.setString(_loadingHints[rand() % _loadingHints.size()]);
|
|
|
+ _loadingHint.setPosition(_window.getSize().x / 2.f - _loadingHint.getLocalBounds().width / 2.f, _window.getSize().y - 60.f);
|
|
|
+
|
|
|
+ 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();
|
|
|
+ coreFPScalc(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::coreFPScalc(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();
|
|
|
+}
|