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