diff --git a/gui.cpp b/gui.cpp new file mode 100644 index 0000000..e96e2fa --- /dev/null +++ b/gui.cpp @@ -0,0 +1,171 @@ +#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" + +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(Value val) { + return VALUES[size_t(val)]; +} + +GUI::GUI() : game_map_(50, 20), + canvas_(60 * 2, 20 * 4), + window_(sf::VideoMode(1600,900), "Roguish"), + screen_(0,0) +{ + 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); + screen_ = Screen::Create(Dimension::Full()); +} + +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 { + 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); + } else if(goal_.x == player_.location.x && goal_.y == player_.location.y) { + status_text_ = "YOU WIN!"; + } + } + } +} + +void GUI::render_scene() { + Render(screen_, document_->Render()); + std::string screen_out = screen_.ToString(); + std::wstring utf8 = converter_.from_bytes(screen_out); + text_.setString(utf8); + text_.setPosition({0,0}); + + window_.clear(); + window_.draw(text_); + window_.display(); +} + +int GUI::main() { + create_renderer(); + + while(window_.isOpen()) { + render_scene(); + handle_events(); + } + + return 0; +} diff --git a/gui.hpp b/gui.hpp new file mode 100644 index 0000000..5bce3ae --- /dev/null +++ b/gui.hpp @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "entity.hpp" +#include "map.hpp" + +using std::string; +using ftxui::Canvas, ftxui::Component, ftxui::Screen; + +enum class Value { + BLACK=0, DARK_DARK, DARK_MID, + DARK_LIGHT, MID, LIGHT_DARK, LIGHT_MID, + LIGHT_LIGHT, WHITE, TRANSPARENT +}; + +class GUI { + sf::Color color(Value val); + Map game_map_; + sf::SoundBuffer hit_buf_; + sf::Sound hit_sound_; + bool show_paths_ = false; + string status_text_ = "NOT DEAD"; + Entity player_; + Entity enemy_; + Point goal_; + Component document_; + Component map_view_; + Canvas canvas_; + sf::Font font_; + sf::Text text_; + std::wstring_convert> converter_; + sf::RenderWindow window_; + Screen screen_; + +public: + GUI(); + // disable copying + GUI(GUI &gui) = delete; + + void create_renderer(); + void render_scene(); + void handle_events(); + + int main(); +};