Merge branch 'develop' into bplan_planneroverlay

develop
Myk 2023-04-07 01:14:06 -07:00 committed by GitHub
commit ec114bea7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 276 additions and 113 deletions

@ -0,0 +1,4 @@
# DO NOT EDIT THIS FILE
# Please use gui/control-panel to edit this file
enable faststart

@ -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

@ -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 ]

@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
# Future # Future
## New Plugins ## New Plugins
- `faststart`: speeds up the "Loading..." screen so the Main Menu appears sooner
## Fixes ## Fixes
- `hotkeys`: hotkey hints on menu popup will no longer get their last character cut off by the scrollbar - `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 ## Misc Improvements
- `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game - `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 ``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 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: 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 ## Documentation

@ -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) dfhack.onStateChange[GLOBAL_KEY] = function(sc)
if sc == SC_MAP_UNLOADED then if sc == SC_MAP_UNLOADED then
dfhack.run_command('disable', 'example-mod') 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 return
end end

@ -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

@ -2148,7 +2148,10 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
loadModScriptPaths(out); loadModScriptPaths(out);
auto L = Lua::Core::State; auto L = Lua::Core::State;
Lua::StackUnwinder top(L); 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 // fallthrough
} }
case SC_WORLD_UNLOADED: case SC_WORLD_UNLOADED:

@ -2729,7 +2729,7 @@ static int filesystem_listdir_recursive(lua_State *L)
include_prefix = lua_toboolean(L, 3); include_prefix = lua_toboolean(L, 3);
std::map<std::string, bool> files; std::map<std::string, bool> files;
int err = DFHack::Filesystem::listdir_recursive(dir, files, depth, include_prefix); int err = DFHack::Filesystem::listdir_recursive(dir, files, depth, include_prefix);
if (err != -1) { if (err != 0 && err != -1) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, strerror(err)); lua_pushstring(L, strerror(err));
lua_pushinteger(L, err); lua_pushinteger(L, err);

@ -6,22 +6,27 @@ local utils = require('utils')
-- enabled API -- enabled API
-- for each script that can be loaded as a module, calls cb(script_name, env) -- 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 for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do
local files = dfhack.filesystem.listdir_recursive( local files = dfhack.filesystem.listdir_recursive(
script_path, nil, false) script_path, nil, false)
if not files then goto skip_path end if not files then goto skip_path end
for _,f in ipairs(files) do for _,f in ipairs(files) do
if not f.isdir and if f.isdir or not f.path:endswith('.lua') or
f.path:endswith('.lua') and f.path:startswith('.git') or
not f.path:startswith('test/') and f.path:startswith('test/') or
not f.path:startswith('internal/') then f.path:startswith('internal/') then
local script_name = f.path:sub(1, #f.path - 4) -- remove '.lua' goto continue
local ok, script_env = pcall(reqscript, script_name)
if ok then
cb(script_name, script_env)
end
end 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 end
::skip_path:: ::skip_path::
end end
@ -42,9 +47,17 @@ local function process_script(env_name, env)
enabled_map[env_name] = fn enabled_map[env_name] = fn
end end
function reload() function reload(refresh_active_mod_scripts)
enabled_map = utils.OrderedTable() 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 end
local function ensure_loaded() local function ensure_loaded()
@ -97,7 +110,7 @@ end
local function add_script_path(mod_script_paths, path) local function add_script_path(mod_script_paths, path)
if dfhack.filesystem.isdir(path) then 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) table.insert(mod_script_paths, path)
end end
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 a world is loaded, process active mods first, and lock to active version
if dfhack.isWorldLoaded() then if dfhack.isWorldLoaded() then
for _,path in ipairs(df.global.world.object_loader.object_load_order_src_dir) do 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 if not path:startswith(INSTALLED_MODS_PATH) then goto continue end
local id = get_mod_id_and_version(path) local id = get_mod_id_and_version(path)
if not id then goto continue end if not id then goto continue end

@ -112,6 +112,7 @@ dfhack_plugin(dig-now dig-now.cpp LINK_LIBRARIES lua)
#dfhack_plugin(embark-tools embark-tools.cpp) #dfhack_plugin(embark-tools embark-tools.cpp)
dfhack_plugin(eventful eventful.cpp LINK_LIBRARIES lua) dfhack_plugin(eventful eventful.cpp LINK_LIBRARIES lua)
dfhack_plugin(fastdwarf fastdwarf.cpp) dfhack_plugin(fastdwarf fastdwarf.cpp)
dfhack_plugin(faststart faststart.cpp)
dfhack_plugin(filltraffic filltraffic.cpp) dfhack_plugin(filltraffic filltraffic.cpp)
#dfhack_plugin(fix-unit-occupancy fix-unit-occupancy.cpp) #dfhack_plugin(fix-unit-occupancy fix-unit-occupancy.cpp)
#dfhack_plugin(fixveins fixveins.cpp) #dfhack_plugin(fixveins fixveins.cpp)

@ -0,0 +1,69 @@
// Fast Startup tweak
#include "Core.h"
#include <Console.h>
#include <Export.h>
#include <PluginManager.h>
#include <MiscUtils.h>
#include <VTableInterpose.h>
#include "df/viewscreen_initial_prepst.h"
#include <vector>
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 <PluginCommand> &commands)
{
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
INTERPOSE_HOOK(prep_hook, logic).remove();
return CR_OK;
}

@ -55,7 +55,7 @@ end
ItemSelection = defclass(ItemSelection, widgets.Window) ItemSelection = defclass(ItemSelection, widgets.Window)
ItemSelection.ATTRS{ ItemSelection.ATTRS{
frame_title='Choose items', frame_title='Choose items',
frame={w=56, h=20, l=4, t=8}, frame={w=56, h=24, l=4, t=7},
resizable=true, resizable=true,
index=DEFAULT_NIL, index=DEFAULT_NIL,
desc=DEFAULT_NIL, desc=DEFAULT_NIL,
@ -81,107 +81,144 @@ function ItemSelection:init()
end end
self:addviews{ self:addviews{
widgets.Label{ widgets.Panel{
frame={t=0, l=0, r=16}, view_id='header',
text={ frame={t=0, h=3},
self.desc, plural, NEWLINE, subviews={
('Select up to %d item%s ('):format(self.quantity, plural), widgets.Label{
{text=function() return self.num_selected end}, frame={t=0, l=0, r=16},
' selected)', text={
}, self.desc, plural, NEWLINE,
}, ('Select up to %d item%s ('):format(self.quantity, plural),
widgets.Label{ {text=function() return self.num_selected end},
frame={r=0, w=15, t=0, h=3}, ' selected)',
text_pen=BUILD_TEXT_PEN, },
text_hpen=BUILD_TEXT_HPEN, },
text={ widgets.Label{
' Use filter ', NEWLINE, frame={r=0, w=15, t=0, h=3},
' for remaining ', NEWLINE, text_pen=BUILD_TEXT_PEN,
' items ', 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, self:addviews{
text_hpen=BUILD_TEXT_HPEN, widgets.Panel{
text={ view_id='body',
' ', NEWLINE, frame={t=self.subviews.header.frame.h, b=4},
' Continue ', NEWLINE, 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{ widgets.Panel{
view_id='flist', view_id='footer',
frame={t=3, l=0, r=0, b=4}, frame={l=1, r=1, b=0, h=3},
case_sensitive=false, subviews={
choices=choices, widgets.HotkeyLabel{
icon_width=2, frame={l=0, h=1, t=0},
on_submit=self:callback('toggle_group'), key='KEYBOARD_CURSOR_RIGHT_FAST',
edit_on_char=function(ch) return ch:match('[%l -]') end, key_sep='----: ', -- these hypens function as "padding" to be overwritten by the next Label
}, label='Use one',
widgets.CycleHotkeyLabel{ auto_width=true,
frame={l=0, b=2}, on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end,
key='CUSTOM_SHIFT_R', },
label='Sort by:', widgets.Label{
options={ frame={l=6, w=5, t=0},
{label='Recently used', value=sort_by_recency}, text_pen=COLOR_LIGHTGREEN,
{label='Name', value=sort_by_name}, text='Right', -- this overrides the "6----" characters from the previous HotkeyLabel
{label='Amount', value=sort_by_quantity}, },
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 end
-- resort and restore selection -- resort and restore selection
@ -234,7 +271,7 @@ function ItemSelection:get_choices(sort_fn)
for desc,choice in pairs(buckets) do for desc,choice in pairs(buckets) do
local data = choice.data local data = choice.data
choice.text = { 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}, {gap=2, text=desc},
} }
table.insert(choices, choice) table.insert(choices, choice)

@ -1 +1 @@
Subproject commit e6216cc28e4315df5fb128411d0ca57fe78ccb2b Subproject commit a4e5d4514ec33462ee0c0bd25e02d4a9c3e2ce01