diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 44a79f2be..13884dc2d 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -3,6 +3,7 @@ #include "DataDefs.h" #include "Export.h" #include "PluginManager.h" +#include "MiscUtils.h" #include "modules/Screen.h" #include "modules/Gui.h" @@ -16,6 +17,8 @@ #include "df/interface_key.h" using namespace DFHack; +using df::global::enabler; +using df::global::gps; #define FOR_ITER_TOOLS(iter) for(auto iter = tools.begin(); iter != tools.end(); iter++) @@ -34,17 +37,36 @@ void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen) } } +void get_embark_pos (df::viewscreen_choose_start_sitest * screen, + int& x1, int& x2, int& y1, int& y2, int& w, int& h) +{ + x1 = screen->location.embark_pos_min.x, + x2 = screen->location.embark_pos_max.x, + y1 = screen->location.embark_pos_min.y, + y2 = screen->location.embark_pos_max.y, + w = x2 - x1 + 1, + h = y2 - y1 + 1; +} + +void set_embark_pos (df::viewscreen_choose_start_sitest * screen, + int x1, int x2, int y1, int y2) +{ + screen->location.embark_pos_min.x = x1; + screen->location.embark_pos_max.x = x2; + screen->location.embark_pos_min.y = y1; + screen->location.embark_pos_max.y = y2; +} + +#define GET_EMBARK_POS(screen, a, b, c, d, e, f) \ + int a, b, c, d, e, f; \ + get_embark_pos(screen, a, b, c, d, e, f); + void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy) { /* Reproduces DF's embark resizing functionality * Local area resizes up and to the right, unless it's already touching the edge */ - int x1 = screen->location.embark_pos_min.x, - x2 = screen->location.embark_pos_max.x, - y1 = screen->location.embark_pos_min.y, - y2 = screen->location.embark_pos_max.y, - width = x2 - x1 + dx, - height = y2 - y1 + dy; + GET_EMBARK_POS(screen, x1, x2, y1, y2, width, height); if (x1 == x2 && dx == -1) dx = 0; if (y1 == y2 && dy == -1) @@ -66,11 +88,7 @@ void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy) } y2 = std::min(15, y2); - screen->location.embark_pos_min.x = x1; - screen->location.embark_pos_max.x = x2; - screen->location.embark_pos_min.y = y1; - screen->location.embark_pos_max.y = y2; - + set_embark_pos(screen, x1, x2, y1, y2); update_embark_sidebar(screen); } @@ -96,6 +114,7 @@ public: virtual void after_render(start_sitest* screen) { }; virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) { }; virtual void after_feed(start_sitest* screen, ikey_set* input) { }; + virtual void after_mouse_event(start_sitest* screen) { }; }; std::vector tools; @@ -320,6 +339,294 @@ public: }; }; +class MouseControl : public EmbarkTool +{ +protected: + // Used for event handling + int prev_x; + int prev_y; + bool prev_lbut; + // Used for controls + bool base_max_x; + bool base_max_y; + bool in_local_move; + bool in_local_edge_resize_x; + bool in_local_edge_resize_y; + bool in_local_corner_resize; + // These keep track of where the embark location would be (i.e. if + // the mouse is dragged out of the local embark area), to prevent + // the mouse from moving the actual area when it's 20+ tiles away + int local_overshoot_x1; + int local_overshoot_x2; + int local_overshoot_y1; + int local_overshoot_y2; + inline bool in_local_adjust() + { + return in_local_move || in_local_edge_resize_x || in_local_edge_resize_y || + in_local_corner_resize; + } + void lbut_press(start_sitest* screen, bool pressed, int x, int y) + { + GET_EMBARK_POS(screen, x1, x2, y1, y2, width, height); + in_local_move = in_local_edge_resize_x = in_local_edge_resize_y = + in_local_corner_resize = false; + if (pressed) + { + if (x >= 1 && x <= 16 && y >= 2 && y <= 17) + { + // Local embark - translate to local map coordinates + x -= 1; + y -= 2; + if ((x == x1 || x == x2) && (y == y1 || y == y2)) + { + in_local_corner_resize = true; + base_max_x = (x == x2); + base_max_y = (y == y2); + } + else if (x == x1 || x == x2) + { + in_local_edge_resize_x = true; + base_max_x = (x == x2); + base_max_y = false; + } + else if (y == y1 || y == y2) + { + in_local_edge_resize_y = true; + base_max_x = false; + base_max_y = (y == y2); + } + else if (x > x1 && x < x2 && y > y1 && y < y2) + { + in_local_move = true; + base_max_x = base_max_y = false; + local_overshoot_x1 = x1; + local_overshoot_x2 = x2; + local_overshoot_y1 = y1; + local_overshoot_y2 = y2; + } + } + } + update_embark_sidebar(screen); + } + void mouse_move(start_sitest* screen, int x, int y) + { + GET_EMBARK_POS(screen, x1, x2, y1, y2, width, height); + if (x == -1 && prev_x > (2 + 16)) + { + x = gps->dimx; + gps->mouse_x = x - 1; + } + if (y == -1 && prev_y > (1 + 16)) + { + y = gps->dimy; + gps->mouse_y = y - 1; + } + if (in_local_corner_resize || in_local_edge_resize_x || in_local_edge_resize_y) + { + x -= 1; + y -= 2; + } + if (in_local_corner_resize) + { + x = std::max(0, std::min(15, x)); + y = std::max(0, std::min(15, y)); + if (base_max_x) + x2 = x; + else + x1 = x; + if (base_max_y) + y2 = y; + else + y1 = y; + if (x1 > x2) + { + std::swap(x1, x2); + base_max_x = !base_max_x; + } + if (y1 > y2) + { + std::swap(y1, y2); + base_max_y = !base_max_y; + } + } + else if (in_local_edge_resize_x) + { + x = std::max(0, std::min(15, x)); + if (base_max_x) + x2 = x; + else + x1 = x; + if (x1 > x2) + { + std::swap(x1, x2); + base_max_x = !base_max_x; + } + } + else if (in_local_edge_resize_y) + { + y = std::max(0, std::min(15, y)); + if (base_max_y) + y2 = y; + else + y1 = y; + if (y1 > y2) + { + std::swap(y1, y2); + base_max_y = !base_max_y; + } + } + else if (in_local_move) + { + int dx = x - prev_x; + int dy = y - prev_y; + local_overshoot_x1 += dx; + local_overshoot_x2 += dx; + local_overshoot_y1 += dy; + local_overshoot_y2 += dy; + if (local_overshoot_x1 < 0) + { + x1 = 0; + x2 = width - 1; + } + else if (local_overshoot_x2 > 15) + { + x1 = 15 - (width - 1); + x2 = 15; + } + else + { + x1 = local_overshoot_x1; + x2 = local_overshoot_x2; + } + if (local_overshoot_y1 < 0) + { + y1 = 0; + y2 = height - 1; + } + else if (local_overshoot_y2 > 15) + { + y1 = 15 - (height - 1); + y2 = 15; + } + else + { + y1 = local_overshoot_y1; + y2 = local_overshoot_y2; + } + } + set_embark_pos(screen, x1, x2, y1, y2); + } +public: + MouseControl() + :EmbarkTool(), + prev_x(0), + prev_y(0), + prev_lbut(false), + base_max_x(false), + base_max_y(false), + in_local_move(false), + in_local_edge_resize_x(false), + in_local_edge_resize_y(false), + in_local_corner_resize(false), + local_overshoot_x1(0), + local_overshoot_x2(0), + local_overshoot_y1(0), + local_overshoot_y2(0) + { } + virtual std::string getId() { return "mouse"; } + virtual std::string getName() { return "Mouse control"; } + virtual std::string getDesc() { return "Implements mouse controls on the embark screen"; } + virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_M; } + virtual void after_render(start_sitest* screen) + { + GET_EMBARK_POS(screen, x1, x2, y1, y2, width, height); + int mouse_x = gps->mouse_x, mouse_y = gps->mouse_y; + int local_x = prev_x - 1; + int local_y = prev_y - 2; + if (local_x >= x1 && local_x <= x2 && local_y >= y1 && local_y <= y2) + { + int screen_x1 = x1 + 1; + int screen_x2 = x2 + 1; + int screen_y1 = y1 + 2; + int screen_y2 = y2 + 2; + UIColor fg = in_local_adjust() ? COLOR_GREY : COLOR_DARKGREY; + Screen::Pen corner_ul = Screen::Pen((char)201, fg, COLOR_BLACK); + Screen::Pen corner_ur = Screen::Pen((char)187, fg, COLOR_BLACK); + Screen::Pen corner_dl = Screen::Pen((char)200, fg, COLOR_BLACK); + Screen::Pen corner_dr = Screen::Pen((char)188, fg, COLOR_BLACK); + Screen::Pen border_ud = Screen::Pen((char)205, fg, COLOR_BLACK); + Screen::Pen border_lr = Screen::Pen((char)186, fg, COLOR_BLACK); + if (in_local_corner_resize || + ((local_x == x1 || local_x == x2) && (local_y == y1 || local_y == y2))) + { + if (local_x == x1 && local_y == y1) + Screen::paintTile(corner_ul, screen_x1, screen_y1); + else if (local_x == x2 && local_y == y1) + Screen::paintTile(corner_ur, screen_x2, screen_y1); + else if (local_x == x1 && local_y == y2) + Screen::paintTile(corner_dl, screen_x1, screen_y2); + else if (local_x == x2 && local_y == y2) + Screen::paintTile(corner_dr, screen_x2, screen_y2); + } + else if (in_local_edge_resize_x || local_x == x1 || local_x == x2) + { + if ((in_local_edge_resize_x && !base_max_x) || local_x == x1) + { + Screen::paintTile(corner_ul, screen_x1, screen_y1); + for (int i = screen_y1 + 1; i <= screen_y2 - 1; ++i) + Screen::paintTile(border_lr, screen_x1, i); + Screen::paintTile(corner_dl, screen_x1, screen_y2); + } + else + { + Screen::paintTile(corner_ur, screen_x2, screen_y1); + for (int i = screen_y1 + 1; i <= screen_y2 - 1; ++i) + Screen::paintTile(border_lr, screen_x2, i); + Screen::paintTile(corner_dr, screen_x2, screen_y2); + } + } + else if (in_local_edge_resize_y || local_y == y1 || local_y == y2) + { + if ((in_local_edge_resize_y && !base_max_y) || local_y == y1) + { + Screen::paintTile(corner_ul, screen_x1, screen_y1); + for (int i = screen_x1 + 1; i <= screen_x2 - 1; ++i) + Screen::paintTile(border_ud, i, screen_y1); + Screen::paintTile(corner_ur, screen_x2, screen_y1); + } + else + { + Screen::paintTile(corner_dl, screen_x1, screen_y2); + for (int i = screen_x1 + 1; i <= screen_x2 - 1; ++i) + Screen::paintTile(border_ud, i, screen_y2); + Screen::paintTile(corner_dr, screen_x2, screen_y2); + } + } + else + { + Screen::paintTile(corner_ul, screen_x1, screen_y1); + Screen::paintTile(corner_ur, screen_x2, screen_y1); + Screen::paintTile(corner_dl, screen_x1, screen_y2); + Screen::paintTile(corner_dr, screen_x2, screen_y2); + } + } + } + virtual void after_mouse_event(start_sitest* screen) + { + if (enabler->mouse_lbut != prev_lbut) + { + lbut_press(screen, enabler->mouse_lbut, gps->mouse_x, gps->mouse_y); + } + if (gps->mouse_x != prev_x || gps->mouse_y != prev_y) + { + mouse_move(screen, gps->mouse_x, gps->mouse_y); + } + prev_lbut = enabler->mouse_lbut; + prev_x = gps->mouse_x; + prev_y = gps->mouse_y; + }; +}; + class embark_tools_settings : public dfhack_viewscreen { public: @@ -352,8 +659,9 @@ public: EmbarkTool* t = *iter; x = min_x + 2; OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(t->getToggleKey())); - OutputString(COLOR_WHITE, x, y, ": " + t->getName() + - (t->getEnabled() ? ": Enabled" : ": Disabled")); + OutputString(COLOR_WHITE, x, y, ": " + t->getName() + ": "); + OutputString(t->getEnabled() ? COLOR_GREEN : COLOR_RED, x, y, + t->getEnabled() ? "Enabled" : "Disabled"); y++; } }; @@ -427,28 +735,24 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest y = dim.y - 5; OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S)); OutputString(COLOR_WHITE, x, y, ": Enabled: "); - std::list parts; - FOR_ITER_TOOLS(iter) + std::vector parts; + FOR_ITER_TOOLS(it) { - EmbarkTool* tool = *iter; - if (tool->getEnabled()) - { - parts.push_back(tool->getName()); - parts.push_back(", "); - } + if ((*it)->getEnabled()) + parts.push_back((*it)->getName()); } if (parts.size()) { - parts.pop_back(); // Remove trailing comma - for (auto iter = parts.begin(); iter != parts.end(); iter++) + std::string label = join_strings(", ", parts); + if (label.size() > dim.x - x - 1) { - OutputString(COLOR_LIGHTMAGENTA, x, y, *iter); + label.resize(dim.x - x - 1 - 3); + label.append("..."); } + OutputString(COLOR_LIGHTMAGENTA, x, y, label); } else - { OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)"); - } } void display_settings() @@ -511,6 +815,7 @@ command_result embark_tools_cmd (color_ostream &out, std::vector & DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { tools.push_back(new EmbarkAnywhere); + tools.push_back(new MouseControl); tools.push_back(new NanoEmbark); tools.push_back(new SandIndicator); tools.push_back(new StablePosition); @@ -550,6 +855,35 @@ DFhackCExport command_result plugin_enable (color_ostream &out, bool enable) return CR_OK; } +DFhackCExport command_result plugin_onupdate (color_ostream &out) +{ + static int8_t mask = 0; + static decltype(gps->mouse_x) prev_x = -1; + static decltype(gps->mouse_y) prev_y = -1; + df::viewscreen* parent = DFHack::Gui::getCurViewscreen(); + VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, parent); + if (!screen) + return CR_OK; + int8_t new_mask = (enabler->mouse_lbut << 1) | + (enabler->mouse_rbut << 2) | + (enabler->mouse_lbut_down << 3) | + (enabler->mouse_rbut_down << 4) | + (enabler->mouse_lbut_lift << 5) | + (enabler->mouse_rbut_lift << 6); + if (mask != new_mask || prev_x != gps->mouse_x || prev_y != gps->mouse_y) + { + FOR_ITER_TOOLS(iter) + { + if ((*iter)->getEnabled()) + (*iter)->after_mouse_event(screen); + } + } + mask = new_mask; + prev_x = gps->mouse_x; + prev_y = gps->mouse_y; + return CR_OK; +} + command_result embark_tools_cmd (color_ostream &out, std::vector & parameters) { CoreSuspender suspend;