/**
* 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();
}