The next little game in the series where I make a fancy rogue game.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
roguish/tools/designer.cpp

273 lines
7.1 KiB

#include <chrono> // for operator""s, chrono_literals
#include <thread> // for sleep_for
#include "dinkyecs.hpp"
#include "events.hpp"
#include "dbc.hpp"
#include "render.hpp"
#include "lights.hpp"
#include <filesystem>
#include <fcntl.h>
#include "constants.hpp"
#include "point.hpp"
#include <fmt/core.h>
#include <locale>
#include <codecvt>
#include <vector>
using namespace fmt;
using namespace ftxui;
using namespace std::chrono_literals;
using lighting::LightSource, lighting::LightRender;
namespace fs = std::filesystem;
using std::string, std::wstring, std::vector;
const Point GRID_SIZE={15,8};
const int DEFAULT_FONT_SIZE=200;
struct FontGrid {
size_t width;
size_t height;
vector<vector<string>> $chars;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
FontGrid(size_t width, size_t height) :
width(width), height(height),
$chars(height, vector<string>(width, ""))
{
}
void render(wchar_t start_char, bool fill) {
wchar_t cur_char = start_char;
for(size_t y = 0; y < height; ++y) {
for(size_t x = 0; x < width; ++x) {
if(!fill) {
cur_char += (x+1) * (y+1);
}
wstring out_w{cur_char};
$chars[y][x] = from_unicode(out_w);
}
}
}
string from_unicode(wstring input) {
try {
return $converter.to_bytes(input);
} catch(...) {
return "?";
}
}
wchar_t to_unicode_char(size_t x, size_t y) {
try {
string input = $chars[y][x]; // BUG: bounds check this instead
return $converter.from_bytes(input)[0];
} catch(...) {
return L'?';
}
}
string at(size_t x, size_t y) {
return $chars[y][x];
}
unsigned int page_size() {
return width * height;
}
};
struct WhatTheColor {
int h;
int s;
int v;
Component h_slider;
Component s_slider;
Component v_slider;
};
class GUI {
public:
DinkyECS::World& $world;
Panel $font_view;
Panel $status_ui;
Canvas $canvas;
SFMLRender $renderer;
FontGrid $font_grid;
wchar_t $start_char = L'\u28cc';
wchar_t $fill_char = WCHAR_MIN;
WhatTheColor $fg_color;
WhatTheColor $bg_color;
Component $fg_settings;
Component $bg_settings;
GUI(DinkyECS::World &world) :
$world(world),
$font_view(GAME_MAP_POS, 0, GRID_SIZE.x, GRID_SIZE.y, true),
$status_ui(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
$font_grid(GRID_SIZE.x, GRID_SIZE.y),
$fg_color{.h=20, .s=50, .v=20},
$bg_color{.h=100, .s=100, .v=100}
{
resize_fonts(DEFAULT_FONT_SIZE);
$font_grid.render($start_char, false);
}
void resize_fonts(int new_size) {
println("RESIZE MAP TO {}", new_size);
$renderer.resize_grid(new_size, $font_view);
// set canvas to best size
$canvas = Canvas($font_view.width * 2, $font_view.height * 4);
}
void draw_font_grid() {
for(size_t y = 0; y < $font_grid.height; y++) {
for(size_t x = 0; x < $font_grid.width; x++) {
$canvas.DrawText(x * 2, y * 4, $font_grid.at(x, y), [&](auto &pixel) {
pixel.foreground_color = Color::HSV($fg_color.h, $fg_color.s, $fg_color.v);
pixel.background_color = Color::HSV($bg_color.h, $bg_color.s, $bg_color.v);
});
}
}
}
void create_renderer() {
$renderer.init_terminal();
$font_view.set_renderer(Renderer([&]{
draw_font_grid();
return canvas($canvas);
}));
$fg_color.h_slider = Slider("FG H:", &$fg_color.h, 0, 255, 1);
$fg_color.s_slider = Slider("FG S:", &$fg_color.s, 0, 255, 1);
$fg_color.v_slider = Slider("FG V:", &$fg_color.v, 0, 255, 1);
$bg_color.h_slider = Slider("BG H:", &$bg_color.h, 0, 255, 1);
$bg_color.s_slider = Slider("BG S:", &$bg_color.s, 0, 255, 1);
$bg_color.v_slider = Slider("BG V:", &$bg_color.v, 0, 255, 1);
$status_ui.set_renderer(Renderer([&]{
return hbox({
hflow(
vbox(
text(format("\\u{:x} {} MIN: {}, MAX: {}", int($fill_char), int($fill_char), WCHAR_MIN, WCHAR_MAX)) | border,
separator(),
text(format("FG H: {}, S: {}, V: {}",
$fg_color.h, $fg_color.s, $fg_color.v)) | border,
$fg_color.h_slider->Render(),
separator(),
$fg_color.s_slider->Render(),
separator(),
$fg_color.v_slider->Render(),
separator(),
text(format("BG H: {}, S: {}, V: {}",
$bg_color.h, $bg_color.s, $bg_color.v)) | border,
$bg_color.h_slider->Render(),
separator(),
$bg_color.s_slider->Render(),
separator(),
$bg_color.v_slider->Render()
) | flex_grow
),
separator(),
hbox(),
});
}));
$status_ui.add($fg_color.h_slider);
$status_ui.add($fg_color.s_slider);
$status_ui.add($fg_color.v_slider);
$status_ui.add($bg_color.h_slider);
$status_ui.add($bg_color.s_slider);
$status_ui.add($bg_color.v_slider);
}
void shutdown() {
$renderer.close();
}
void select_cell(Point pos) {
$fill_char = $font_grid.to_unicode_char(pos.x, pos.y);
$font_grid.render($fill_char, true);
}
void deselect_cell() {
$font_grid.render($start_char, false);
}
bool handle_ui_events() {
bool event_happened;
using KB = sf::Keyboard;
sf::Event event;
int font_size = $renderer.font_size();
while($renderer.poll_event(event)) {
if(event.type == sf::Event::Closed) {
shutdown();
return true;
} else if(event.type == sf::Event::KeyPressed) {
if(KB::isKeyPressed(KB::Up)) {
$start_char = std::max(WCHAR_MIN+1, $start_char - $font_grid.page_size());
$font_grid.render($start_char, false);
event_happened = true;
$renderer.clear_cache();
} else if(KB::isKeyPressed(KB::Down)) {
$start_char = std::min(WCHAR_MAX, $start_char + $font_grid.page_size());
$font_grid.render($start_char, false);
$renderer.clear_cache();
} else if(KB::isKeyPressed(KB::Equal)) {
resize_fonts(font_size + 10);
} else if(KB::isKeyPressed(KB::Hyphen)) {
resize_fonts(font_size - 10);
event_happened = true;
}
} else if(sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
Point pos;
if($renderer.mouse_position($font_view, pos)) {
select_cell(pos);
event_happened = true;
} else if($renderer.mouse_position($status_ui, pos)) {
$status_ui.mouse_click(Mouse::Button::Left, pos);
}
} else if(sf::Mouse::isButtonPressed(sf::Mouse::Right)) {
deselect_cell();
}
}
return event_happened;
}
void render_scene() {
$renderer.clear();
$status_ui.render();
$renderer.draw($status_ui);
$font_view.render();
$renderer.draw($font_view);
$renderer.display();
}
};
int main(int argc, char *argv[]) {
DinkyECS::World world;
GUI gui(world);
gui.create_renderer();
do {
gui.render_scene();
if(gui.handle_ui_events()) {
println("THERE WERE EVENTS");
}
std::this_thread::sleep_for(10ms);
} while(gui.$renderer.is_open());
return 0;
}