#include #include #include #include #include #include #include "ColorText.h" #include "Debug.h" #include "LuaTools.h" #include "PluginManager.h" #include "df/graphic_viewportst.h" #include "df/world.h" #include "modules/Gui.h" #include "modules/Persistence.h" #include "modules/Screen.h" #include "modules/World.h" DFHACK_PLUGIN("design"); using DFHack::color_value; REQUIRE_GLOBAL(window_x); REQUIRE_GLOBAL(window_y); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(plotinfo); using namespace DFHack; using namespace df::enums; namespace DFHack { DBG_DECLARE(design, log, DebugCategory::LINFO); } DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { return CR_OK; } std::map PENS; DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { if (event == DFHack::SC_WORLD_UNLOADED) { DEBUG(log,out).print("clearing PENS\n"); PENS.clear(); } return CR_OK; } struct DrawingPoint { uint32_t penKey = 0; std::pair cursor_coords; DrawingPoint() : penKey(0), cursor_coords({-1, -1}) {} }; typedef std::map> ShapeMap; ShapeMap arr; bool has_point(int x, int y) { return arr.count(x) != 0 && arr.at(x).count(y) != 0; }; // Key tuple is N, W, E, S typedef std::tuple DirectionKey; std::map> CURSORS_MAP = { {{false, false, false, false}, {1, 2}}, // INSIDE {{true, true, false, false}, {0, 1}}, // NW {{true, false, false, false}, {1, 1}}, // NORTH {{true, false, true, false}, {2, 1}}, // NE {{false, true, false, false}, {0, 2}}, // WEST {{false, false, true, false}, {2, 2}}, // EAST {{false, true, false, true}, {0, 3}}, // SW {{false, false, false, true}, {1, 3}}, // SOUTH {{false, false, true, true}, {2, 3}}, // SE {{true, true, true, false}, {3, 2}}, // N_NUB {{true, false, true, true}, {5, 1}}, // E_NUB {{true, true, false, true}, {3, 1}}, // W_NUB {{false, true, true, true}, {4, 2}}, // S_NUB {{false, true, true, false}, {3, 3}}, // VERT_NS {{true, false, false, true}, {4, 1}}, // VERT_EW {{true, true, true, true}, {4, 3}}, // POINT }; enum PenMask { North = 0, South, East, West, DragPoint, MouseOver, InShape, ExtraPoint, NumFlags }; uint32_t gen_pen_key(bool n, bool s, bool e, bool w, bool is_drag_point, bool is_mouse_over, bool inshape, bool extra_point) { std::bitset(PenMask::NumFlags)> ret; ret[PenMask::North] = n; ret[PenMask::South] = s; ret[PenMask::East] = e; ret[PenMask::West] = w; ret[PenMask::DragPoint] = is_drag_point; ret[PenMask::MouseOver] = is_mouse_over; ret[PenMask::InShape] = inshape; ret[PenMask::ExtraPoint] = extra_point; return ret.to_ulong(); } Screen::Pen make_pen(const std::pair &direction, bool is_drag_point, bool is_mouse_over, bool inshape, bool extra_point) { color_value color = COLOR_GREEN; int ycursor_mod = 0; if (!extra_point) { if (is_drag_point) { color = COLOR_CYAN; ycursor_mod += 6; if (is_mouse_over) { color = COLOR_MAGENTA; ycursor_mod += 3; } } } else { ycursor_mod += 15; color = COLOR_LIGHTRED; if (is_mouse_over) { color = COLOR_RED; ycursor_mod += 3; } } Screen::Pen pen; pen.ch = inshape ? 'X' : 'o'; pen.fg = color; int selected_tile_texpos = 0; Screen::findGraphicsTile("CURSORS", direction.first, direction.second + ycursor_mod, &selected_tile_texpos); pen.tile = selected_tile_texpos; return pen; } Screen::Pen get_pen(int x, int y, ShapeMap &arr, const std::string &type = "") { bool n = false, w = false, e = false, s = false; if (has_point(x, y)) { if (y == 0 || !has_point(x, y - 1)) n = true; if (x == 0 || !has_point(x - 1, y)) w = true; if (!has_point(x + 1, y)) e = true; // TODO check map size if (!has_point(x, y + 1)) s = true; // TODO check map size } bool is_drag_point = type == "drag_point"; bool is_extra = type == "extra_point"; bool is_in_shape = has_point(x, y); auto mouse_pos = Gui::getMousePos(); bool mouse_over = mouse_pos.x == x && mouse_pos.y == y; uint32_t pen_key = gen_pen_key(n, s, e, w, is_drag_point, mouse_over, is_in_shape, is_extra); if (CURSORS_MAP.count({n, w, e, s}) > 0 && has_point(x, y)) { arr[x][y].cursor_coords = CURSORS_MAP.at({n, w, e, s}); } if (PENS.find(pen_key) == PENS.end()) { std::pair cursor{-1, -1}; if (type != "") { return make_pen(CURSORS_MAP.at({n, w, e, s}), is_drag_point, mouse_over, is_in_shape, is_extra); } if (CURSORS_MAP.count({n, w, e, s}) > 0) { PENS.emplace(pen_key, make_pen(CURSORS_MAP.at({n, w, e, s}), is_drag_point, mouse_over, is_in_shape, is_extra)); if (type == "" && has_point(x, y)) { arr[x][y].penKey = pen_key; } } } return PENS.at(pen_key); } static int design_load_shape(lua_State *L) { if (lua_istable(L, -1)) { lua_pushnil(L); while (lua_next(L, -2) != 0) { int x = lua_tointeger(L, -2); if (lua_istable(L, -1)) { lua_pushnil(L); while (lua_next(L, -2) != 0) { int y = lua_tointeger(L, -2); bool value = lua_toboolean(L, -1); if (value) { arr[x][y] = DrawingPoint(); } lua_pop(L, 1); } } lua_pop(L, 1); } } return 0; } static int design_clear_shape(lua_State *L) { arr.clear(); return 0; } static int design_draw_shape(lua_State *L) { if (arr.size() == 0) { design_load_shape(L); } for (auto x : arr) { for (auto y : x.second) { Screen::Pen cur_tile = Screen::readTile(x.first, y.first, true); Screen::Pen pen = get_pen(x.first, y.first, arr); cur_tile.tile = pen.tile; Screen::paintTile(cur_tile, x.first - *window_x, y.first - *window_y, true); } } return 0; } static int design_draw_points(lua_State *L) { if (lua_istable(L, -1)) { const char *str; lua_rawgeti(L, -1, 2); str = lua_tostring(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 1); int n = luaL_len(L, -1); for (int i = 1; i <= n; i++) { lua_rawgeti(L, -1, i); int x, y; lua_getfield(L, -1, "y"); y = lua_tointeger(L, -1); lua_getfield(L, -2, "x"); x = lua_tointeger(L, -1); lua_pop(L, 3); Screen::Pen cur_tile = Screen::readTile(x, y, true); Screen::Pen pen = get_pen(x, y, arr, str); cur_tile.tile = pen.tile; Screen::paintTile(cur_tile, x - *window_x, y - *window_y, true); } lua_pop(L, 1); } return 0; } DFHACK_PLUGIN_LUA_COMMANDS{DFHACK_LUA_COMMAND(design_draw_shape), DFHACK_LUA_COMMAND(design_draw_points), DFHACK_LUA_COMMAND(design_clear_shape), DFHACK_LUA_END};