Triangles.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. /**
  2. * Triangles
  3. * Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, version 3 of the License.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <sstream>
  18. #include <thread>
  19. #include <fstream>
  20. #include <iomanip>
  21. #include "Triangles.hpp"
  22. #include "IngameState.hpp"
  23. #include "MenuState.hpp"
  24. #include "Utility.hpp"
  25. #include <GL/gl.h>
  26. Triangles::Triangles(int argc, char** argv):
  27. _returnCode(0),
  28. _window("Triangles", sf::Vector2u(640u, 480u)),
  29. _current(nullptr),
  30. _initialState(State::Menu)
  31. {
  32. _variables.fullscreen = false;
  33. _variables.borderless = false;
  34. _variables.vsync = true;
  35. _variables.running = true;
  36. _variables.needsUpdate = false;
  37. _variables.shaders = sf::Shader::isAvailable();
  38. _variables.nevermore = false;
  39. _variables.window = &_window;
  40. _variables.assets = &_assets;
  41. _variables.core = this;
  42. _variables.foreground = nullptr;
  43. /**
  44. * Shintel has bugs, so we detect if the graphics card is shintel iGPU.
  45. */
  46. if(glGetString(GL_VENDOR)[0] == (unsigned char)'I')
  47. _variables.mustRecreate = true;
  48. else
  49. _variables.mustRecreate = false;
  50. passArguments(argc, argv);
  51. if(_variables.running)
  52. {
  53. init();
  54. refresh();
  55. load(_initialState);
  56. }
  57. }
  58. void Triangles::init()
  59. {
  60. _context = _variables;
  61. _window.setContext(&_context);
  62. _window.recreate();
  63. _fps.setFont(_assets.loadFont("data/ttf/canonical/Ubuntu-L.ttf"));
  64. _fps.setCharacterSize(12);
  65. _fps.setColor(sf::Color::Black);
  66. _fps.setPosition(10, 10);
  67. _fps.setString("TRIANGLES");
  68. _fpsRect.setPosition(0, 0);
  69. _fpsRect.setSize(sf::Vector2f(375.f, 60.f));
  70. _fpsRect.setFillColor(sf::Color(255, 255, 255, 120));
  71. _loadingText.setString("Loading");
  72. _loadingHint.setFont(_assets.loadFont("data/ttf/canonical/Ubuntu-L.ttf"));
  73. _loadingText.setFont(_assets.loadFont("data/ttf/canonical/Ubuntu-L.ttf"));
  74. _loadingHint.setColor(sf::Color::White);
  75. _loadingText.setColor(sf::Color::White);
  76. _loadingTriangle.setColor(sf::Color::White);
  77. _loadingHints.push_back(L"This is a hint. A placeholder.");
  78. _loadingHints.push_back(L"There are two perfect things: chocolate and 45° isosceles triangle.");
  79. _loadingHints.push_back(L"Si les trangles faisaient un dieu, ils lui donneraient trois cótés. — Montesquieu");
  80. _loadingHints.push_back(L"Triangles do not have speed caps, but you will hit the wall eventually.");
  81. _loadingHints.push_back(L"when patafour pls owocek you lazy piece of shit");
  82. _loadingHints.push_back(L"Hit left control to switch light types.");
  83. _loadingHints.push_back(L"S=√(p(p-a)(p-b)(p-c))");
  84. _loadingHints.push_back(L"S=12141px²");
  85. _loadingHints.push_back(L"Positive vibes. PMA. Positive mental attitude.");
  86. _loadingHints.push_back(L"You can lose up to 4856.4px.");
  87. _loadingHints.push_back(L"Wasted.");
  88. _loadingHints.push_back(L"Ah, the Scalene Triangle.");
  89. _loadingHints.push_back(L"Δ");
  90. _loadingHints.push_back(L"Δ\nΔ Δ");
  91. _loadingHints.push_back(L"Segmentation fault (core dumped)");
  92. _loadingHints.push_back(L"The stars act as consolation prizes when you go in the wrong direction.");
  93. _loadingHints.push_back(L"[](){}(); [](){}(); [](){}();");
  94. }
  95. void Triangles::passArguments(int argc, char** argv)
  96. {
  97. std::string argvStr[argc];
  98. for(int i = 0; i < argc; ++i)
  99. {
  100. argvStr[i] = argv[i];
  101. if(argvStr[i].size() < 2)
  102. argvStr[i] = "--kaczka";
  103. }
  104. searchArgument(argc, argvStr, "f", "fullscreen", [this]()
  105. {
  106. _variables.fullscreen = true;
  107. });
  108. searchArgument(argc, argvStr, "w", "window", [this]()
  109. {
  110. _variables.fullscreen = false;
  111. });
  112. searchArgument(argc, argvStr, "b", "borderlesswindow", [this]()
  113. {
  114. _variables.borderless = true;
  115. });
  116. searchArgument(argc, argvStr, "v", "vsync", [this]()
  117. {
  118. _variables.vsync = true;
  119. });
  120. searchArgument(argc, argvStr, "s", "skip", [this]()
  121. {
  122. _initialState = State::Ingame;
  123. });
  124. searchArgument(argc, argvStr, "sf", "skipfullscreen", [this]()
  125. {
  126. _initialState = State::Ingame;
  127. _variables.fullscreen = true;
  128. _variables.nevermore = true;
  129. });
  130. searchArgument(argc, argvStr, "r", "resolution", [this](std::string param)
  131. {
  132. if(param == "640x480")
  133. {
  134. std::cerr << "What year is it?\n";
  135. _returnCode = 1;
  136. _variables.running = false;
  137. }
  138. std::size_t found = param.rfind('x');
  139. if(found == std::string::npos)
  140. return;
  141. std::string sub = param;
  142. std::string sup = param;
  143. sub.erase(sub.begin() + found, sub.end());
  144. sup.erase(sup.begin(), sup.begin() + found + 1);
  145. unsigned w, h;
  146. std::stringstream ss;
  147. ss << sub;
  148. ss >> w;
  149. ss.str(std::string());
  150. ss.clear();
  151. ss << sup;
  152. ss >> h;
  153. _window.setSize(w, h);
  154. });
  155. searchArgument(argc, argvStr, "h", "help", [this]()
  156. {
  157. std::cout << "triangles: triangles [OPTION]..." << std::endl;
  158. std::cout << "A pseudogame not meant to be run by sane people." << std::endl << std::endl;
  159. std::cout << "Copyright (C) 2016 POSITIVE MENTAL ATTITUDE" << std::endl;
  160. std::cout << "This program comes with ABSOLUTELY NO WARRANTY;" << std::endl;
  161. std::cout << "This is free software, and you are welcome to redistribute it" << std::endl;
  162. std::cout << "under certain conditions; see LICENSE.md" << std::endl << std::endl;
  163. std::cout << std::setw(8) << "-v" << " or " << std::setw(16) << "--vsync" << " prevents from playing without vertical synchronization; try F10" << std::endl;
  164. std::cout << std::setw(8) << "-s" << " or " << std::setw(16) << "--skip" << " prevents from going to the main menu; try Escape" << std::endl;
  165. std::cout << std::setw(8) << "-f" << " or " << std::setw(16) << "--fullscreen" << " prevents from playing in windowed mode; try F11" << std::endl;
  166. std::cout << std::setw(8) << "-sf" << " or " << std::setw(16) << "--skipfullscreen" << " combines --fullscreen and --skip" << std::endl;
  167. std::cout << std::setw(8) << "-w" << " or " << std::setw(16) << "--window" << " prevents from playing in fullscreen mode; try F11" << std::endl;
  168. std::cout << std::setw(8) << "-h" << " or " << std::setw(16) << "--help" << " prevents from running the actual game" << std::endl;
  169. std::cout << std::setw(8) << "-r WxH" << " or " << std::setw(16) << "--resolution=WxH" << " prevents from running at 640x480" << std::endl;
  170. _variables.running = false;
  171. });
  172. }
  173. void Triangles::searchArgument(int argc, std::string argvStr[], std::string callShort, std::string callLong, std::function<void()> lambda)
  174. {
  175. for(int i = 1; i < argc; ++i)
  176. {
  177. if((argvStr[i][0] == '-' && argvStr[i][1] == callShort[0]) || argvStr[i] == "--" + callLong)
  178. {
  179. if((callShort.size() == 1 && argvStr[i].size() == 2) || argvStr[i][2] == callShort[1])
  180. {
  181. lambda();
  182. break;
  183. }
  184. }
  185. }
  186. }
  187. void Triangles::searchArgument(int argc, std::string argvStr[], std::string callShort, std::string callLong, std::function<void(std::string param)> lambda)
  188. {
  189. for(int i = 1; i < argc; ++i)
  190. {
  191. if(argvStr[i][0] == '-' && argvStr[i][1] == callShort[0] && i < argc - 1)
  192. {
  193. lambda(argvStr[i + 1]);
  194. break;
  195. }
  196. std::size_t found = argvStr[i].rfind('=');
  197. if(found == std::string::npos)
  198. continue;
  199. std::string sub = argvStr[i];
  200. std::string sup = argvStr[i];
  201. sub.erase(sub.begin() + found, sub.end());
  202. sup.erase(sup.begin(), sup.begin() + found + 1);
  203. if(sub == "--" + callLong)
  204. {
  205. lambda(sup);
  206. break;
  207. }
  208. }
  209. }
  210. void Triangles::refresh()
  211. {
  212. float u = _window.getSize().x >= 1024 ? 1.f : 0.75f;
  213. _loadingHint.setCharacterSize(20 * u);
  214. _loadingText.setCharacterSize(20 * u);
  215. _loadingTriangle.setSize(12.f * u);
  216. _loadingTriangle.setPosition(_window.getSize().x - 140.f, _window.getSize().y - (u == 1.f ? 45.f : 50.f));
  217. _loadingText.setPosition(_window.getSize().x - 120.f, _window.getSize().y - 60.f);
  218. }
  219. void Triangles::load(int stateType)
  220. {
  221. Echo::debug("Deleting current state.");
  222. if(_current)
  223. delete _current;
  224. Echo::debug("Loading state ID: ", stateType);
  225. switch(stateType)
  226. {
  227. case State::Menu:
  228. _current = new MenuState;
  229. break;
  230. case State::Ingame:
  231. _current = new IngameState;
  232. break;
  233. default:
  234. return;
  235. }
  236. _current->setContext(&_context);
  237. _loadingHint.setString(_loadingHints[rand() % _loadingHints.size()]);
  238. _loadingHint.setPosition(_window.getSize().x / 2.f - _loadingHint.getLocalBounds().width / 2.f, _window.getSize().y - 60.f);
  239. _window.lockFramerate(true);
  240. _window.setActive(false);
  241. std::thread t1(&Triangles::loadingRender, this);
  242. std::thread t2(&State::init, _current);
  243. t1.join();
  244. t2.join();
  245. _window.lockFramerate(false);
  246. _current->refresh();
  247. if(_variables.mustRecreate)
  248. _window.recreate();
  249. Echo::debug("Done.");
  250. }
  251. int Triangles::run()
  252. {
  253. sf::Clock clock;
  254. sf::Time delta, epsilon = sf::seconds(1.f / 30.f);
  255. if(_context.running)
  256. {
  257. Echo::out(Echo::Empty, "Triangles version 0.0.6");
  258. Echo::out(Echo::Empty, "Copyright (C) 2016 POSITIVE MENTAL ATTITUDE");
  259. Echo::out(Echo::Empty, "This program comes with ABSOLUTELY NO WARRANTY;");
  260. Echo::out(Echo::Empty, "This is free software, and you are welcome to redistribute it");
  261. Echo::out(Echo::Empty, "under certain conditions; see LICENSE file");
  262. }
  263. /**
  264. * Main loop
  265. */
  266. while(_context.running)
  267. {
  268. if(_context.needsUpdate)
  269. {
  270. refresh();
  271. _current->refresh();
  272. _context.needsUpdate = false;
  273. _variables = _context;
  274. }
  275. if(_current->status() != State::Ongoing)
  276. {
  277. load(_current->status());
  278. }
  279. coreThink();
  280. coreInput();
  281. delta = clock.restart();
  282. fpsCalc(delta);
  283. while(delta > epsilon)
  284. {
  285. delta -= epsilon;
  286. coreUpdate(epsilon);
  287. }
  288. coreUpdate(delta);
  289. coreRender();
  290. }
  291. return _returnCode;
  292. }
  293. void Triangles::coreThink()
  294. {
  295. sf::Event event;
  296. while(_window.pollEvent(event))
  297. {
  298. if(_current->status())
  299. _current->coreThink(event);
  300. _window.think(event);
  301. }
  302. }
  303. void Triangles::coreInput()
  304. {
  305. _current->coreInput();
  306. _window.think();
  307. }
  308. void Triangles::fpsCalc(sf::Time delta)
  309. {
  310. static sf::Clock clock;
  311. static float avg = 0.f, avgL = 0.f;
  312. static float min = 10000.f, max = 0.f;
  313. static float minL = 0.f, maxL = 0.f;
  314. static unsigned avgC = 0;
  315. float curr = 1.f / delta.asSeconds();
  316. if(avgC == 0)
  317. {
  318. avg = curr, avgC = 1;
  319. min = curr;
  320. max = curr;
  321. }
  322. else
  323. {
  324. avg = (avg * avgC + curr) / (avgC + 1);
  325. ++avgC; // Called explicitly in order to avoid undefined behaviour.
  326. min = std::min(min, curr);
  327. max = std::max(max, curr);
  328. }
  329. if(clock.getElapsedTime() >= sf::seconds(1.f))
  330. {
  331. minL = min;
  332. maxL = max;
  333. avgL = avg;
  334. avgC = 0;
  335. clock.restart();
  336. }
  337. std::ostringstream oss;
  338. oss << "Build time: " << __DATE__ << ' ' << __TIME__ << '\n';
  339. oss << "Framerate: (min/max/avg/curr): " << minL << '/' << maxL << '/' << avgL << '/' << curr << '\n';
  340. oss << "Settings: " << (_variables.vsync ? "vsync " : "") << (_variables.fullscreen ? "fullscreen " : "") << (_variables.shaders ? "shaders " : "") << '\n';
  341. _fps.setString(oss.str());
  342. }
  343. void Triangles::coreUpdate(sf::Time delta)
  344. {
  345. _current->coreUpdate(delta);
  346. }
  347. void Triangles::loadingRender()
  348. {
  349. _window.setActive(true);
  350. while(!_current->status() && _context.running)
  351. {
  352. _loadingTriangle.rotate(8.f);
  353. coreThink();
  354. _window.clear();
  355. _window.draw(_loadingHint);
  356. _window.draw(_loadingText);
  357. _window.draw(_loadingTriangle);
  358. _window.render();
  359. }
  360. }
  361. void Triangles::coreRender()
  362. {
  363. _window.clear();
  364. _current->coreRender();
  365. #ifdef TRIANGLES_DEBUG
  366. _window.draw(_fpsRect);
  367. _window.draw(_fps);
  368. #endif
  369. _window.render();
  370. }