#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 // 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" 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(GAME_MAP_X * 2, GAME_MAP_Y * 4), $window(sf::VideoMode(VIDEO_X,VIDEO_Y), "Roguish"), $screen(SCREEN_X, SCREEN_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"); $text.setFont($font); $text.setCharacterSize(30); $text.setFillColor(color(Value::LIGHT_DARK)); $game_map.generate(); $player.location = $game_map.place_entity(0); $enemy.location = $game_map.place_entity(1); $goal = $game_map.place_entity($game_map.room_count() - 1); } void GUI::create_renderer() { $map_view = Renderer([&] { Matrix &walls = $game_map.walls(); $game_map.set_target($player.location); $game_map.make_paths(); Matrix &paths = $game_map.paths(); if($player.in_state(EntityState::DEAD)) { $status_text = "DEAD!"; } for(size_t x = 0; x < walls[0].size(); ++x) { for(size_t y = 0; y < walls.size(); ++y) { string tile = walls[y][x] == 1 ? "#" : format("{}", paths[y][x]); if(tile == "#") { $canvas.DrawText(x*2, y*4, tile); } else if($show_paths) { //int pnum = paths[y][x]; $canvas.DrawText(x*2, y*4, tile); } else { $canvas.DrawText(x*2, y*4, "."); } } } $canvas.DrawText($enemy.location.x*2, $enemy.location.y*4, "!"); $canvas.DrawText($player.location.x*2, $player.location.y*4, "@"); $canvas.DrawText($goal.x*2, $goal.y*4, "$"); return canvas($canvas); }); $document = Renderer([&]{ return hbox({ hflow( vbox( text(format("HP: {}", $player.hp)) | border, text($status_text) | border ) | xflex_grow ), separator(), hbox($map_view->Render()), }); }); } 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) { size_t x = $player.location.x; size_t y = $player.location.y; if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { x -= 1; } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { x += 1; } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { y -= 1; } else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { y += 1; } if($game_map.inmap(x,y) && !$game_map.iswall(x,y)) { $game_map.clear_target($player.location); $player.move({x, y}); } else { $shake_it = true; $hit_sound.play(); } // move $enemy here bool found = $game_map.neighbors($enemy.location, true); if(!found) { $status_text = "ENEMY STUCK!"; } if($enemy.location.x == $player.location.x && $enemy.location.y == $player.location.y) { $player.event(EntityEvent::HIT); $burn_baby_burn = true; } else if($goal.x == $player.location.x && $goal.y == $player.location.y) { $status_text = "YOU WIN!"; } } } } void GUI::burn() { for(int i = 0; i < 20; ++i) { $text.setFillColor(color(i % VALUES.size())); int x = Random::rand_int(-10,10); int y = Random::rand_int(-10,10); $text.setPosition({(float)x,(float)y}); $window.draw($text); $window.display(); std::this_thread::sleep_for(2ms); } $text.setFillColor(color(Value::LIGHT_DARK)); } inline void draw_screen(sf::RenderWindow &window, sf::Text &text, float x=0, float y=0) { text.setPosition({x,y}); window.clear(); window.draw(text); window.display(); } void GUI::shake() { for(int i = 0; i < 10; ++i) { int x = Random::rand_int(-10,10); int y = Random::rand_int(-10,10); draw_screen($window, $text, (float)x, (float)y); std::this_thread::sleep_for(1ms); } } void GUI::render_scene() { Render($screen, $document->Render()); std::string $screenout = $screen.ToString(); std::wstring utf8 = $converter.from_bytes($screenout); $text.setString(utf8); if($shake_it) { shake(); $shake_it = false; } if($burn_baby_burn) { burn(); $burn_baby_burn = false; } draw_screen($window, $text, 0, 0); } int GUI::main() { create_renderer(); while($window.isOpen()) { render_scene(); handle_events(); std::this_thread::sleep_for(10ms); } return 0; }