diff --git a/data/dfhack-config/init/dfhack.control-panel-system.init b/data/dfhack-config/init/dfhack.control-panel-system.init new file mode 100644 index 000000000..8b1431373 --- /dev/null +++ b/data/dfhack-config/init/dfhack.control-panel-system.init @@ -0,0 +1,4 @@ +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +enable faststart diff --git a/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init new file mode 100644 index 000000000..66a07d14f --- /dev/null +++ b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init @@ -0,0 +1,4 @@ +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +on-new-fortress enable fix/protect-nicks diff --git a/data/dfhack-config/init/onMapLoad.control-panel-repeats.init b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init new file mode 100644 index 000000000..cbed00b67 --- /dev/null +++ b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init @@ -0,0 +1,5 @@ +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +repeat --name general-strike --time 1 --timeUnits days --command [ fix/general-strike -q ] +repeat --name warn-starving --time 10 --timeUnits days --command [ warn-starving ] diff --git a/docs/changelog.txt b/docs/changelog.txt index f96f29dbd..259a7ce7f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `faststart`: speeds up the "Loading..." screen so the Main Menu appears sooner ## Fixes - `hotkeys`: hotkey hints on menu popup will no longer get their last character cut off by the scrollbar @@ -44,8 +45,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game -@ `buildingplan`: rearranged elements of ``planneroverlay`` interface +-@ `buildingplan`: rearranged elements of ``itemselection`` interface - Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game - Mods: scripts from only the most recent version of an installed mod are added to the script path +- Mods: give active mods a chance to reattach their load hooks when a world is reloaded +- `gui/control-panel`: bugfix services are now enabled by default ## Documentation diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index 0c9513fef..38117503c 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -461,6 +461,11 @@ Ok, you're all set up! Now, let's take a look at an example dfhack.onStateChange[GLOBAL_KEY] = function(sc) if sc == SC_MAP_UNLOADED then dfhack.run_command('disable', 'example-mod') + + -- ensure our mod doesn't try to enable itself when a different + -- world is loaded where we are *not* active + dfhack.onStateChange[GLOBAL_KEY] = nil + return end diff --git a/docs/plugins/faststart.rst b/docs/plugins/faststart.rst new file mode 100644 index 000000000..b39269e01 --- /dev/null +++ b/docs/plugins/faststart.rst @@ -0,0 +1,18 @@ +faststart +========= + +.. dfhack-tool:: + :summary: Makes the main menu appear sooner. + :tags: dfhack interface + :no-command: + +This plugin accelerates the initial "Loading..." screen that appears when the +game first starts, so you don't have to wait as long before the Main Menu +appears and you can start playing. + +Usage +----- + +:: + + enable faststart diff --git a/library/Core.cpp b/library/Core.cpp index d13c82fa5..97c69946e 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2148,7 +2148,10 @@ void Core::onStateChange(color_ostream &out, state_change_event event) loadModScriptPaths(out); auto L = Lua::Core::State; Lua::StackUnwinder top(L); - Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); + Lua::CallLuaModuleFunction(con, L, "script-manager", "reload", 1, 0, + [](lua_State* L) { + Lua::Push(L, true); + }); // fallthrough } case SC_WORLD_UNLOADED: diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index d30dc7f77..08903c7bd 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2729,7 +2729,7 @@ static int filesystem_listdir_recursive(lua_State *L) include_prefix = lua_toboolean(L, 3); std::map files; int err = DFHack::Filesystem::listdir_recursive(dir, files, depth, include_prefix); - if (err != -1) { + if (err != 0 && err != -1) { lua_pushnil(L); lua_pushstring(L, strerror(err)); lua_pushinteger(L, err); diff --git a/library/lua/script-manager.lua b/library/lua/script-manager.lua index 1a7161a10..450012357 100644 --- a/library/lua/script-manager.lua +++ b/library/lua/script-manager.lua @@ -6,22 +6,27 @@ local utils = require('utils') -- enabled API -- for each script that can be loaded as a module, calls cb(script_name, env) -function foreach_module_script(cb) +function foreach_module_script(cb, preprocess_script_file_fn) for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do local files = dfhack.filesystem.listdir_recursive( script_path, nil, false) if not files then goto skip_path end for _,f in ipairs(files) do - if not f.isdir and - f.path:endswith('.lua') and - not f.path:startswith('test/') and - not f.path:startswith('internal/') then - local script_name = f.path:sub(1, #f.path - 4) -- remove '.lua' - local ok, script_env = pcall(reqscript, script_name) - if ok then - cb(script_name, script_env) - end + if f.isdir or not f.path:endswith('.lua') or + f.path:startswith('.git') or + f.path:startswith('test/') or + f.path:startswith('internal/') then + goto continue end + if preprocess_script_file_fn then + preprocess_script_file_fn(script_path, f.path) + end + local script_name = f.path:sub(1, #f.path - 4) -- remove '.lua' + local ok, script_env = pcall(reqscript, script_name) + if ok then + cb(script_name, script_env) + end + ::continue:: end ::skip_path:: end @@ -42,9 +47,17 @@ local function process_script(env_name, env) enabled_map[env_name] = fn end -function reload() +function reload(refresh_active_mod_scripts) enabled_map = utils.OrderedTable() - foreach_module_script(process_script) + local force_refresh_fn = refresh_active_mod_scripts and function(script_path, script_name) + if script_path:find('scripts_modactive') then + internal_script = dfhack.internal.scripts[script_path..'/'..script_name] + if internal_script then + internal_script.env = nil + end + end + end or nil + foreach_module_script(process_script, force_refresh_fn) end local function ensure_loaded() @@ -97,7 +110,7 @@ end local function add_script_path(mod_script_paths, path) if dfhack.filesystem.isdir(path) then - print('indexing scripts from mod script path: ' .. path) + print('indexing mod scripts: ' .. path) table.insert(mod_script_paths, path) end end @@ -120,7 +133,7 @@ function get_mod_script_paths() -- if a world is loaded, process active mods first, and lock to active version if dfhack.isWorldLoaded() then for _,path in ipairs(df.global.world.object_loader.object_load_order_src_dir) do - path = tostring(path) + path = tostring(path.value) if not path:startswith(INSTALLED_MODS_PATH) then goto continue end local id = get_mod_id_and_version(path) if not id then goto continue end diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index dc7fa9276..53d0296c7 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -112,6 +112,7 @@ dfhack_plugin(dig-now dig-now.cpp LINK_LIBRARIES lua) #dfhack_plugin(embark-tools embark-tools.cpp) dfhack_plugin(eventful eventful.cpp LINK_LIBRARIES lua) dfhack_plugin(fastdwarf fastdwarf.cpp) +dfhack_plugin(faststart faststart.cpp) dfhack_plugin(filltraffic filltraffic.cpp) #dfhack_plugin(fix-unit-occupancy fix-unit-occupancy.cpp) #dfhack_plugin(fixveins fixveins.cpp) diff --git a/plugins/faststart.cpp b/plugins/faststart.cpp new file mode 100644 index 000000000..de014801c --- /dev/null +++ b/plugins/faststart.cpp @@ -0,0 +1,69 @@ +// Fast Startup tweak + +#include "Core.h" +#include +#include +#include +#include +#include + +#include "df/viewscreen_initial_prepst.h" +#include + +using namespace DFHack; +using namespace df::enums; +using std::vector; + +// Uncomment this to make the Loading screen as fast as possible +// This has the side effect of removing the dwarf face animation +// (and briefly making the game become unresponsive) + +//#define REALLY_FAST + +DFHACK_PLUGIN("faststart"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +struct prep_hook : df::viewscreen_initial_prepst +{ + typedef df::viewscreen_initial_prepst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, logic, ()) + { +#ifdef REALLY_FAST + while (breakdown_level != interface_breakdown_types::STOPSCREEN) + { + render_count++; + INTERPOSE_NEXT(logic)(); + } +#else + render_count = 4; + INTERPOSE_NEXT(logic)(); +#endif + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(prep_hook, logic); + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (enable != is_enabled) + { + if (!INTERPOSE_HOOK(prep_hook, logic).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) +{ + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + INTERPOSE_HOOK(prep_hook, logic).remove(); + return CR_OK; +} diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua index 9cfe0f843..84e866502 100644 --- a/plugins/lua/buildingplan/itemselection.lua +++ b/plugins/lua/buildingplan/itemselection.lua @@ -55,7 +55,7 @@ end ItemSelection = defclass(ItemSelection, widgets.Window) ItemSelection.ATTRS{ frame_title='Choose items', - frame={w=56, h=20, l=4, t=8}, + frame={w=56, h=24, l=4, t=7}, resizable=true, index=DEFAULT_NIL, desc=DEFAULT_NIL, @@ -81,107 +81,144 @@ function ItemSelection:init() end self:addviews{ - widgets.Label{ - frame={t=0, l=0, r=16}, - text={ - self.desc, plural, NEWLINE, - ('Select up to %d item%s ('):format(self.quantity, plural), - {text=function() return self.num_selected end}, - ' selected)', - }, - }, - widgets.Label{ - frame={r=0, w=15, t=0, h=3}, - text_pen=BUILD_TEXT_PEN, - text_hpen=BUILD_TEXT_HPEN, - text={ - ' Use filter ', NEWLINE, - ' for remaining ', NEWLINE, - ' items ', + widgets.Panel{ + view_id='header', + frame={t=0, h=3}, + subviews={ + widgets.Label{ + frame={t=0, l=0, r=16}, + text={ + self.desc, plural, NEWLINE, + ('Select up to %d item%s ('):format(self.quantity, plural), + {text=function() return self.num_selected end}, + ' selected)', + }, + }, + widgets.Label{ + frame={r=0, w=15, t=0, h=3}, + text_pen=BUILD_TEXT_PEN, + text_hpen=BUILD_TEXT_HPEN, + text={ + ' Use filter ', NEWLINE, + ' for remaining ', NEWLINE, + ' items ', + }, + on_click=self:callback('submit'), + visible=function() return self.num_selected < self.quantity end, + }, + widgets.Label{ + frame={r=0, w=15, t=0, h=3}, + text_pen=BUILD_TEXT_PEN, + text_hpen=BUILD_TEXT_HPEN, + text={ + ' ', NEWLINE, + ' Continue ', NEWLINE, + ' ', + }, + on_click=self:callback('submit'), + visible=function() return self.num_selected >= self.quantity end, + }, }, - on_click=self:callback('submit'), - visible=function() return self.num_selected < self.quantity end, }, - widgets.Label{ - frame={r=0, w=15, t=0, h=3}, - text_pen=BUILD_TEXT_PEN, - text_hpen=BUILD_TEXT_HPEN, - text={ - ' ', NEWLINE, - ' Continue ', NEWLINE, - ' ', + } + + self:addviews{ + widgets.Panel{ + view_id='body', + frame={t=self.subviews.header.frame.h, b=4}, + subviews={ + widgets.EditField{ + view_id='search', + frame={l=1, t=0}, + label_text='Search: ', + on_char=function(ch) return ch:match('[%l -]') end, + }, + widgets.CycleHotkeyLabel{ + frame={l=1, t=2}, + key='CUSTOM_SHIFT_R', + label='Sort by:', + options={ + {label='Recently used', value=sort_by_recency}, + {label='Name', value=sort_by_name}, + {label='Amount', value=sort_by_quantity}, + }, + on_change=self:callback('on_sort'), + }, + widgets.Panel{ + frame={l=0, t=3, r=0, b=0}, + frame_style=gui.INTERIOR_FRAME, + subviews={ + widgets.FilteredList{ + view_id='flist', + frame={t=0, b=0}, + case_sensitive=false, + choices=choices, + icon_width=2, + on_submit=self:callback('toggle_group'), + }, + }, + }, }, - on_click=self:callback('submit'), - visible=function() return self.num_selected >= self.quantity end, }, - widgets.FilteredList{ - view_id='flist', - frame={t=3, l=0, r=0, b=4}, - case_sensitive=false, - choices=choices, - icon_width=2, - on_submit=self:callback('toggle_group'), - edit_on_char=function(ch) return ch:match('[%l -]') end, - }, - widgets.CycleHotkeyLabel{ - frame={l=0, b=2}, - key='CUSTOM_SHIFT_R', - label='Sort by:', - options={ - {label='Recently used', value=sort_by_recency}, - {label='Name', value=sort_by_name}, - {label='Amount', value=sort_by_quantity}, + widgets.Panel{ + view_id='footer', + frame={l=1, r=1, b=0, h=3}, + subviews={ + widgets.HotkeyLabel{ + frame={l=0, h=1, t=0}, + key='KEYBOARD_CURSOR_RIGHT_FAST', + key_sep='----: ', -- these hypens function as "padding" to be overwritten by the next Label + label='Use one', + auto_width=true, + on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=6, w=5, t=0}, + text_pen=COLOR_LIGHTGREEN, + text='Right', -- this overrides the "6----" characters from the previous HotkeyLabel + }, + widgets.HotkeyLabel{ + frame={l=1, h=1, t=1}, + key='KEYBOARD_CURSOR_LEFT_FAST', + key_sep='---: ', -- these hypens function as "padding" to be overwritten by the next Label + label='Use one fewer', + auto_width=true, + on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=7, w=4, t=1}, + text_pen=COLOR_LIGHTGREEN, + text='Left', -- this overrides the "4---" characters from the previous HotkeyLabel + }, + widgets.HotkeyLabel{ + frame={l=6, t=2, h=2}, + key='SELECT', + label='Use all/none', + auto_width=true, + on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.HotkeyLabel{ + frame={r=5, t=0}, + key='LEAVESCREEN', + label='Go back', + auto_width=true, + on_activate=self:callback('on_cancel'), + }, + widgets.HotkeyLabel{ + frame={r=4, t=2}, + key='CUSTOM_SHIFT_C', + label='Continue', + auto_width=true, + on_activate=self:callback('submit'), + }, }, - on_change=self:callback('on_sort'), - }, - widgets.HotkeyLabel{ - frame={l=0, b=1}, - key='SELECT', - label='Use all/none', - auto_width=true, - on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.HotkeyLabel{ - frame={l=22, b=1}, - key='CUSTOM_SHIFT_C', - label='Continue', - auto_width=true, - on_activate=self:callback('submit'), - }, - widgets.HotkeyLabel{ - frame={l=38, b=1}, - key='LEAVESCREEN', - label='Go back', - auto_width=true, - on_activate=self:callback('on_cancel'), - }, - widgets.HotkeyLabel{ - frame={l=0, b=0}, - key='KEYBOARD_CURSOR_RIGHT_FAST', - key_sep=' : ', - label='Use one', - auto_width=true, - on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.Label{ - frame={l=6, b=0, w=5}, - text_pen=COLOR_LIGHTGREEN, - text='Right', - }, - widgets.HotkeyLabel{ - frame={l=23, b=0}, - key='KEYBOARD_CURSOR_LEFT_FAST', - key_sep=' : ', - label='Use one fewer', - auto_width=true, - on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.Label{ - frame={l=29, b=0, w=4}, - text_pen=COLOR_LIGHTGREEN, - text='Left', }, } + + self.subviews.flist.list.frame.t = 0 + self.subviews.flist.edit.visible = false + self.subviews.flist.edit = self.subviews.search + self.subviews.search.on_change = self.subviews.flist:callback('onFilterChange') end -- resort and restore selection @@ -234,7 +271,7 @@ function ItemSelection:get_choices(sort_fn) for desc,choice in pairs(buckets) do local data = choice.data choice.text = { - {width=10, text=function() return ('[%d/%d]'):format(data.selected, data.quantity) end}, + {width=10, text=function() return ('%d/%d'):format(data.selected, data.quantity) end}, {gap=2, text=desc}, } table.insert(choices, choice) diff --git a/scripts b/scripts index e6216cc28..a4e5d4514 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e6216cc28e4315df5fb128411d0ca57fe78ccb2b +Subproject commit a4e5d4514ec33462ee0c0bd25e02d4a9c3e2ce01