diff options
| author | Alexander Gavrilov | 2012-09-05 21:27:42 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-09-05 21:27:42 +0400 |
| commit | 8d876cc7d92faf1616d914e03c890772256ebb83 (patch) | |
| tree | 86a87e2dc8a586a0d12f51966d065b9f5ae92de1 | |
| parent | 57086ac56eb489abd0c7759aed084020edc71148 (diff) | |
| download | dfhack-8d876cc7d92faf1616d914e03c890772256ebb83.tar.gz dfhack-8d876cc7d92faf1616d914e03c890772256ebb83.tar.bz2 dfhack-8d876cc7d92faf1616d914e03c890772256ebb83.tar.xz | |
Support renaming some buildings, and arbitrary units, via gui script.
| -rw-r--r-- | dfhack.init-example | 4 | ||||
| -rw-r--r-- | library/lua/gui/dialogs.lua | 9 | ||||
| -rw-r--r-- | library/modules/World.cpp | 4 | ||||
| -rw-r--r-- | plugins/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | plugins/lua/rename.lua | 13 | ||||
| -rw-r--r-- | plugins/proto/rename.proto | 7 | ||||
| -rw-r--r-- | plugins/rename.cpp | 201 | ||||
| -rw-r--r-- | scripts/gui/rename.lua | 63 |
8 files changed, 296 insertions, 7 deletions
diff --git a/dfhack.init-example b/dfhack.init-example index 39c0e61d..af8b17f0 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -16,6 +16,10 @@ keybinding add Ctrl-K autodump-destroy-item # quicksave, only in main dwarfmode screen and menu page keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave +# gui/rename script +keybinding add Ctrl-Shift-N gui/rename +keybinding add Ctrl-Shift-P "gui/rename unit-profession" + ############################## # Generic adv mode bindings # ############################## diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index e6d30c97..35720f87 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -9,6 +9,7 @@ local dscreen = dfhack.screen MessageBox = defclass(MessageBox, gui.FramedScreen) +MessageBox.focus_path = 'MessageBox' MessageBox.frame_style = gui.GREY_LINE_FRAME function MessageBox:init(info) @@ -31,7 +32,7 @@ end function MessageBox:getWantedFrameSize() local text = self.text - local w = #(self.frame_title or '') + 2 + local w = #(self.frame_title or '') + 4 w = math.max(w, 20) w = math.max(self.frame_width or w, w) for _, l in ipairs(text) do @@ -41,7 +42,7 @@ function MessageBox:getWantedFrameSize() if h > 1 then h = h+1 end - return w, #text+2 + return w+2, #text+2 end function MessageBox:onRenderBody(dc) @@ -102,6 +103,8 @@ end InputBox = defclass(InputBox, MessageBox) +InputBox.focus_path = 'InputBox' + function InputBox:init(info) info = info or {} self:init_fields{ @@ -127,7 +130,7 @@ function InputBox:onRenderBody(dc) dc:fill(dc.x1+1,dc.y,dc.x2-1,dc.y) local cursor = '_' - if math.floor(dfhack.getTickCount()/500) % 2 == 0 then + if math.floor(dfhack.getTickCount()/300) % 2 == 0 then cursor = ' ' end local txt = self.input .. cursor diff --git a/library/modules/World.cpp b/library/modules/World.cpp index 393e7cbf..e14aa02a 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -285,13 +285,13 @@ PersistentDataItem World::GetPersistentData(int entry_id) PersistentDataItem World::GetPersistentData(const std::string &key, bool *added) { - *added = false; + if (added) *added = false; PersistentDataItem rv = GetPersistentData(key); if (!rv.isValid()) { - *added = true; + if (added) *added = true; rv = AddPersistentData(key); } diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index a2e52017..04da3e6c 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -92,7 +92,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(seedwatch seedwatch.cpp) DFHACK_PLUGIN(initflags initflags.cpp) DFHACK_PLUGIN(stockpiles stockpiles.cpp) - DFHACK_PLUGIN(rename rename.cpp PROTOBUFS rename) + DFHACK_PLUGIN(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename) DFHACK_PLUGIN(jobutils jobutils.cpp) DFHACK_PLUGIN(workflow workflow.cpp) DFHACK_PLUGIN(showmood showmood.cpp) diff --git a/plugins/lua/rename.lua b/plugins/lua/rename.lua new file mode 100644 index 00000000..0e7128f5 --- /dev/null +++ b/plugins/lua/rename.lua @@ -0,0 +1,13 @@ +local _ENV = mkmodule('plugins.rename') + +--[[ + + Native functions: + + * canRenameBuilding(building) + * isRenamingBuilding(building) + * renameBuilding(building, name) + +--]] + +return _ENV
\ No newline at end of file diff --git a/plugins/proto/rename.proto b/plugins/proto/rename.proto index aa1e95f4..810091fc 100644 --- a/plugins/proto/rename.proto +++ b/plugins/proto/rename.proto @@ -17,3 +17,10 @@ message RenameUnitIn { optional string nickname = 2; optional string profession = 3; } + +// RPC RenameBuilding : RenameBuildingIn -> EmptyMessage +message RenameBuildingIn { + required int32 building_id = 1; + + optional string name = 2; +} diff --git a/plugins/rename.cpp b/plugins/rename.cpp index 1871d0f7..99dc6848 100644 --- a/plugins/rename.cpp +++ b/plugins/rename.cpp @@ -3,11 +3,15 @@ #include "Export.h" #include "PluginManager.h" +#include <Error.h> +#include <LuaTools.h> + #include "modules/Gui.h" #include "modules/Translation.h" #include "modules/Units.h" +#include "modules/World.h" -#include "DataDefs.h" +#include <VTableInterpose.h> #include "df/ui.h" #include "df/world.h" #include "df/squad.h" @@ -18,6 +22,11 @@ #include "df/historical_figure_info.h" #include "df/assumed_identity.h" #include "df/language_name.h" +#include "df/building_stockpilest.h" +#include "df/building_workshopst.h" +#include "df/building_furnacest.h" +#include "df/building_trapst.h" +#include "df/building_siegeenginest.h" #include "RemoteServer.h" #include "rename.pb.h" @@ -36,6 +45,8 @@ using namespace dfproto; using df::global::ui; using df::global::world; +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event); + static command_result rename(color_ostream &out, vector <string> & parameters); DFHACK_PLUGIN("rename"); @@ -51,8 +62,32 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi " rename unit \"nickname\"\n" " rename unit-profession \"custom profession\"\n" " (a unit must be highlighted in the ui)\n" + " rename building \"nickname\"\n" + " (a building must be highlighted via 'q')\n" )); + + if (Core::getInstance().isMapLoaded()) + plugin_onstatechange(out, SC_MAP_LOADED); } + + return CR_OK; +} + +static void init_buildings(bool enable); + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_MAP_LOADED: + init_buildings(true); + break; + case SC_MAP_UNLOADED: + init_buildings(false); + break; + default: + break; + } + return CR_OK; } @@ -61,6 +96,133 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) return CR_OK; } +/* + * Building renaming - it needs some per-type hacking. + */ + +#define KNOWN_BUILDINGS \ + BUILDING('p', building_stockpilest, "Stockpile") \ + BUILDING('w', building_workshopst, NULL) \ + BUILDING('e', building_furnacest, NULL) \ + BUILDING('T', building_trapst, NULL) \ + BUILDING('i', building_siegeenginest, NULL) + +#define BUILDING(code, cname, tag) \ + struct cname##_hook : df::cname { \ + typedef df::cname interpose_base; \ + DEFINE_VMETHOD_INTERPOSE(void, getName, (std::string *buf)) { \ + if (!name.empty()) {\ + buf->clear(); \ + *buf += name; \ + *buf += " ("; \ + if (tag) *buf += (const char*)tag; \ + else { std::string tmp; INTERPOSE_NEXT(getName)(&tmp); *buf += tmp; } \ + *buf += ")"; \ + return; \ + } \ + else \ + INTERPOSE_NEXT(getName)(buf); \ + } \ + }; \ + IMPLEMENT_VMETHOD_INTERPOSE(cname##_hook, getName); +KNOWN_BUILDINGS +#undef BUILDING + +static char getBuildingCode(df::building *bld) +{ + CHECK_NULL_POINTER(bld); + +#define BUILDING(code, cname, tag) \ + if (strict_virtual_cast<df::cname>(bld)) return code; +KNOWN_BUILDINGS +#undef BUILDING + + return 0; +} + +static bool enable_building_rename(char code, bool enable) +{ + switch (code) { +#define BUILDING(code, cname, tag) \ + case code: return INTERPOSE_HOOK(cname##_hook, getName).apply(enable); +KNOWN_BUILDINGS +#undef BUILDING + default: + return false; + } +} + +static void disable_building_rename() +{ +#define BUILDING(code, cname, tag) \ + INTERPOSE_HOOK(cname##_hook, getName).remove(); +KNOWN_BUILDINGS +#undef BUILDING +} + +static bool is_enabled_building(char code) +{ + switch (code) { +#define BUILDING(code, cname, tag) \ + case code: return INTERPOSE_HOOK(cname##_hook, getName).is_applied(); +KNOWN_BUILDINGS +#undef BUILDING + default: + return false; + } +} + +static void init_buildings(bool enable) +{ + disable_building_rename(); + + if (enable) + { + auto pworld = Core::getInstance().getWorld(); + auto entry = pworld->GetPersistentData("rename/building_types"); + + if (entry.isValid()) + { + std::string val = entry.val(); + for (size_t i = 0; i < val.size(); i++) + enable_building_rename(val[i], true); + } + } +} + +static bool canRenameBuilding(df::building *bld) +{ + return getBuildingCode(bld) != 0; +} + +static bool isRenamingBuilding(df::building *bld) +{ + return is_enabled_building(getBuildingCode(bld)); +} + +static bool renameBuilding(df::building *bld, std::string name) +{ + char code = getBuildingCode(bld); + if (code == 0 && !name.empty()) + return false; + + if (!name.empty() && !is_enabled_building(code)) + { + auto pworld = Core::getInstance().getWorld(); + auto entry = pworld->GetPersistentData("rename/building_types", NULL); + if (!entry.isValid()) + return false; + + if (!enable_building_rename(code, true)) + return false; + + entry.val().push_back(code); + } + + bld->name = name; + return true; +} + static df::squad *getSquadByIndex(unsigned idx) { auto entity = df::historical_entity::find(ui->group_id); @@ -101,14 +263,37 @@ static command_result RenameUnit(color_ostream &stream, const RenameUnitIn *in) return CR_OK; } +static command_result RenameBuilding(color_ostream &stream, const RenameBuildingIn *in) +{ + auto building = df::building::find(in->building_id()); + if (!building) + return CR_NOT_FOUND; + + if (in->has_name()) + { + if (!renameBuilding(building, in->name())) + return CR_FAILURE; + } + + return CR_OK; +} + DFhackCExport RPCService *plugin_rpcconnect(color_ostream &) { RPCService *svc = new RPCService(); svc->addFunction("RenameSquad", RenameSquad); svc->addFunction("RenameUnit", RenameUnit); + svc->addFunction("RenameBuilding", RenameBuilding); return svc; } +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(canRenameBuilding), + DFHACK_LUA_FUNCTION(isRenamingBuilding), + DFHACK_LUA_FUNCTION(renameBuilding), + DFHACK_LUA_END +}; + static command_result rename(color_ostream &out, vector <string> ¶meters) { CoreSuspender suspend; @@ -167,6 +352,20 @@ static command_result rename(color_ostream &out, vector <string> ¶meters) unit->custom_profession = parameters[1]; } + else if (cmd == "building") + { + if (parameters.size() != 2) + return CR_WRONG_USAGE; + + if (ui->main.mode != ui_sidebar_mode::QueryBuilding) + return CR_WRONG_USAGE; + + if (!renameBuilding(world->selected_building, parameters[1])) + { + out.printerr("This type of building is not supported.\n"); + return CR_FAILURE; + } + } else { if (!parameters.empty() && cmd != "?") diff --git a/scripts/gui/rename.lua b/scripts/gui/rename.lua new file mode 100644 index 00000000..a457a0bf --- /dev/null +++ b/scripts/gui/rename.lua @@ -0,0 +1,63 @@ +-- Rename various objects via gui. + +local gui = require 'gui' +local dlg = require 'gui.dialogs' +local plugin = require 'plugins.rename' + +local mode = ... +local focus = dfhack.gui.getCurFocus() + +local function verify_mode(expected) + if mode ~= nil and mode ~= expected then + qerror('Invalid UI state for mode '..mode) + end +end + +if string.match(focus, '^dwarfmode/QueryBuilding/Some') then + verify_mode('building') + + local building = df.global.world.selected_building + if plugin.canRenameBuilding(building) then + dlg.showInputPrompt( + 'Rename Building', + 'Enter a new name for the building:', COLOR_GREEN, + building.name, + curry(plugin.renameBuilding, building) + ) + else + dlg.showMessage( + 'Rename Building', + 'Cannot rename this type of building.', COLOR_LIGHTRED + ) + end +elseif dfhack.gui.getSelectedUnit(true) then + local unit = dfhack.gui.getSelectedUnit(true) + + if mode == 'unit-profession' then + dlg.showInputPrompt( + 'Rename Unit', + 'Enter a new profession for the unit:', COLOR_GREEN, + unit.custom_profession, + function(newval) + unit.custom_profession = newval + end + ) + else + verify_mode('unit') + + local vname = dfhack.units.getVisibleName(unit) + local vnick = '' + if vname and vname.has_name then + vnick = vname.nickname + end + + dlg.showInputPrompt( + 'Rename Unit', + 'Enter a new nickname for the unit:', COLOR_GREEN, + vnick, + curry(dfhack.units.setNickname, unit) + ) + end +elseif mode then + verify_mode(nil) +end |
