#include // for operator""s, chrono_literals #include // for cout, ostream #include #include // for allocator, shared_ptr #include // for string, operator<< #include // for sleep_for #include #include // for hflow, paragraph, separator, hbox, vbox, filler, operator|, border, Element #include // for Render #include // for ftxui #include #include #include #include #include #include "dbc.hpp" #include "gui.hpp" #include "rand.hpp" #include "components.hpp" #include "systems.hpp" using std::string; using namespace fmt; using namespace std::chrono_literals; using namespace ftxui; std::array VALUES{ sf::Color{1, 4, 2}, // black sf::Color{9, 29, 16}, // dark dark sf::Color{14, 50, 26}, // dark mid sf::Color{0, 109, 44}, // dark light sf::Color{63, 171, 92}, // mid sf::Color{161, 217, 155}, // light dark sf::Color{199, 233, 192}, // light mid sf::Color{229, 245, 224}, // light light sf::Color{255, 255, 255}, // white sf::Color::Transparent, // white }; sf::Color GUI::color(int val) { return VALUES[size_t(val)]; } sf::Color GUI::color(Value val) { return VALUES[size_t(val)]; } GUI::GUI() : $game_map(GAME_MAP_X, GAME_MAP_Y), $canvas(VIEW_PORT_X * 2, VIEW_PORT_Y * 4), $window(sf::VideoMode(VIDEO_X,VIDEO_Y), "Roguish"), $screen(SCREEN_X, SCREEN_Y), $map_screen(VIEW_PORT_X, VIEW_PORT_Y) { int res = $hit_buf.loadFromFile("./assets/hit.wav"); dbc::check(res, "failed to load hit.wav"); $hit_sound.setBuffer($hit_buf); $font.loadFromFile("./assets/text.otf"); $ui_text.setFont($font); $ui_text.setPosition(0,0); $ui_text.setCharacterSize(UI_FONT_SIZE); $ui_text.setFillColor(color(Value::LIGHT_LIGHT)); $map_text.setFont($font); $map_text.setPosition(GAME_MAP_POS,0); $map_text.setCharacterSize(MAP_FONT_SIZE); $map_text.setFillColor(color(Value::MID)); $game_map.generate(); } void GUI::create_renderer() { auto player = $world.get(); $map_view = Renderer([&, player] { System::draw_map($world, $game_map, $canvas, VIEW_PORT_X, VIEW_PORT_Y); return canvas($canvas); }); $document = Renderer([&, player]{ const auto& player_combat = $world.component(player.entity); return hbox({ hflow( vbox( text(format("HP: {}", player_combat.hp)) | border, text($status_text) | border ) | xflex_grow ), separator(), hbox(), }); }); } void GUI::handle_events() { sf::Event event; while($window.pollEvent(event)) { if(event.type == sf::Event::Closed) { $window.close(); } else if(event.type == sf::Event::KeyPressed) { auto player = $world.get(); auto& player_motion = $world.component(player.entity); auto& player_position = $world.component(player.entity); if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { player_motion.dx = -1; } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { player_motion.dx = 1; } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { player_motion.dy = -1; } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { player_motion.dy = 1; } $game_map.set_target(player_position.location); $game_map.make_paths(); // COMPOSE system? You create a bunch of callbacks and then combine them into // a single run over the data? System::enemy_pathing($world, $game_map, player); System::motion($world, $game_map); System::combat($world, player); } } } void GUI::burn() { for(int i = 0; i < 20; ++i) { $map_text.setFillColor(color(i % VALUES.size())); int x = Random::uniform(-10,10); int y = Random::uniform(-10,10); draw_screen(false, x, y); std::this_thread::sleep_for(2ms); } $map_text.setFillColor(color(Value::MID)); } void GUI::draw_screen(bool clear, float map_off_x, float map_off_y) { if(clear) $window.clear(); $window.draw($ui_text); $map_text.setPosition(GAME_MAP_POS+map_off_x, map_off_y); $window.draw($map_text); $window.display(); } void GUI::shake() { $hit_sound.play(); for(int i = 0; i < 10; ++i) { int x = Random::uniform(-10,10); int y = Random::uniform(-10,10); // add x/y back to draw screen draw_screen(true, x, y); std::this_thread::sleep_for(1ms); } } void GUI::configure_world() { dbc::check($game_map.room_count() > 1, "not enough rooms in map."); // configure a player as a fact of the world Player player{$world.entity()}; $world.set(player); $world.assign(player.entity, {$game_map.place_entity(0)}); $world.assign(player.entity, {0, 0}); $world.assign(player.entity, {100, 10}); $world.assign(player.entity, {PLAYER_TILE}); auto enemy = $world.entity(); $world.assign(enemy, {$game_map.place_entity(1)}); $world.assign(enemy, {0,0}); $world.assign(enemy, {20, 10}); $world.assign(enemy, {ENEMY_TILE}); auto enemy2 = $world.entity(); $world.assign(enemy2, {$game_map.place_entity(2)}); $world.assign(enemy2, {0,0}); $world.assign(enemy2, {20, 10}); $world.assign(enemy2, {"*"}); auto gold = $world.entity(); $world.assign(gold, {$game_map.place_entity($game_map.room_count() - 1)}); $world.assign(gold, {100}); $world.assign(gold, {"$"}); } void GUI::render_scene() { Render($map_screen, $map_view->Render()); Render($screen, $document->Render()); std::string $screenout = $screen.ToString(); std::wstring main_screen_utf8 = $converter.from_bytes($screenout); $ui_text.setString(main_screen_utf8); std::string $map_screenout = $map_screen.ToString(); std::wstring map_screen_utf8 = $converter.from_bytes($map_screenout); $map_text.setString(map_screen_utf8); draw_screen(); } int GUI::main() { configure_world(); create_renderer(); while($window.isOpen()) { render_scene(); handle_events(); std::this_thread::sleep_for(10ms); } return 0; }