Triangles.cpp 12 KB

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