Merge branch 'DFHack:develop' into develop

develop
silverflyone 2023-02-19 13:39:41 +11:00 committed by GitHub
commit 5deac65be1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 262 additions and 46 deletions

@ -42,17 +42,19 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped.
-@ `autochop`: generate default names for burrows with no assigned names
- ``Buildings::StockpileIterator``: check for stockpile items on block boundary.
- `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment; add support for adamantine cloth (off by default); improve logging
-@ `confirm`: fix fps drop when enabled
## Misc Improvements
- DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids`
- `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth)
-@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window
- `confirm`: configuration data is now persisted globally.
## Documentation
## API
- ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system
- Units module: added new predicates for:
- ``isGeldable()``
- ``isMarkedForGelding()``
@ -60,8 +62,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## Lua
- ``dfhack.gui.getSelectedCivZone``: returns the Zone that the user has selected currently
- ``widgets.FilteredList``: Added ``edit_on_change`` optional parameter to allow a custom callback on filter edit change.
- Added ``widgets.TabBar`` and ``widgets.Tab`` (migrated from control-panel.lua)
## Removed

@ -5028,6 +5028,41 @@ The widget implements:
Same as with an ordinary list.
TabBar class
------------
This widget implements a set of one or more tabs to allow navigation between groups of content. Tabs automatically wrap on
the width of the window and will continue rendering on the next line(s) if all tabs cannot fit on a single line.
:key: Specifies a keybinding that can be used to switch to the next tab.
:key_back: Specifies a keybinding that can be used to switch to the previous tab.
:labels: A table of strings; entry representing the label text for a single tab. The order of the entries
determines the order the tabs will appear in.
:on_select: Callback executed when a tab is selected. It receives the selected tab index as an argument. The provided function
should update the value of whichever variable your script uses to keep track of the currently selected tab.
:get_cur_page: The function used by the TabBar to determine which Tab is currently selected. The function you provide should
return an integer that corresponds to the non-zero index of the currently selected Tab (i.e. whatever variable
you update in your ``on_select`` callback)
:active_tab_pens: A table of pens used to render active tabs. See the default implementation in widgets.lua for an example
of how to construct the table. Leave unspecified to use the default pens.
:inactive_tab_pens: A table of pens used to render inactive tabs. See the default implementation in widgets.lua for an example
of how to construct the table. Leave unspecified to use the default pens.
:get_pens: A function used to determine which pens should be used to render a tab. Receives the index of the tab as the first
argument and the TabBar widget itself as the second. The default implementation, which will handle most situations,
returns ``self.active_tab_pens``, if ``self.get_cur_page() == idx``, otherwise returns ``self.inactive_tab_pens``.
Tab class
---------
This widget implements a single clickable tab and is the main component of the TabBar widget. Usage of the ``TabBar``
widget does not require direct usage of ``Tab``.
:id: The id of the tab.
:label: The text displayed on the tab.
:on_select: Callback executed when the tab is selected.
:get_pens: A function that is used during ``Tab:onRenderBody`` to determine the pens that should be used for drawing. See the
usage of ``Tab`` in ``TabBar:init()`` for an example. See the default value of ``active_tab_pens`` or ``inactive_tab_pens``
in ``TabBar`` for an example of how to construct pens.
.. _lua-plugins:

@ -30,7 +30,11 @@ By default, ``tailor`` will prefer using materials in this order::
silk cloth yarn leather
but you can use the ``tailor materials`` command to restrict which materials
are used, and in what order.
are used, and in what order. ``tailor`` supports adamantine cloth (using the
keyword ``adamantine``) but does not use it by default, as most players find
adamantine too precious to routinely make into cloth. ``tailor`` does not
support modded "cloth" types which utilize custom reactions to making clothing
out of those cloth types.
Examples
--------
@ -46,3 +50,12 @@ Examples
Restrict the materials used for automatically manufacturing clothing to
silk, cloth, and yarn, preferred in that order. This saves leather for
other uses, like making armor.
Note
----
The reason for the limitation on modded cloth-like materials is
because custom reactions do not support the in-game mechanic
which allows a manager order to specify a different size for clothing items.
This mechanic only works for reactions that use the default make-clothing or
make-armor reactions, and is a limitation of the game itself.

@ -2091,4 +2091,156 @@ function FilteredList:onFilterChar(char, text)
return true
end
local DEFAULT_ACTIVE_TAB_PENS = {
text_mode_tab_pen=to_pen{fg=COLOR_YELLOW},
text_mode_label_pen=to_pen{fg=COLOR_WHITE},
lt=to_pen{tile=1005, write_to_lower=true},
lt2=to_pen{tile=1006, write_to_lower=true},
t=to_pen{tile=1007, fg=COLOR_BLACK, write_to_lower=true, top_of_text=true},
rt2=to_pen{tile=1008, write_to_lower=true},
rt=to_pen{tile=1009, write_to_lower=true},
lb=to_pen{tile=1015, write_to_lower=true},
lb2=to_pen{tile=1016, write_to_lower=true},
b=to_pen{tile=1017, fg=COLOR_BLACK, write_to_lower=true, bottom_of_text=true},
rb2=to_pen{tile=1018, write_to_lower=true},
rb=to_pen{tile=1019, write_to_lower=true},
}
local DEFAULT_INACTIVE_TAB_PENS = {
text_mode_tab_pen=to_pen{fg=COLOR_BROWN},
text_mode_label_pen=to_pen{fg=COLOR_DARKGREY},
lt=to_pen{tile=1000, write_to_lower=true},
lt2=to_pen{tile=1001, write_to_lower=true},
t=to_pen{tile=1002, fg=COLOR_WHITE, write_to_lower=true, top_of_text=true},
rt2=to_pen{tile=1003, write_to_lower=true},
rt=to_pen{tile=1004, write_to_lower=true},
lb=to_pen{tile=1010, write_to_lower=true},
lb2=to_pen{tile=1011, write_to_lower=true},
b=to_pen{tile=1012, fg=COLOR_WHITE, write_to_lower=true, bottom_of_text=true},
rb2=to_pen{tile=1013, write_to_lower=true},
rb=to_pen{tile=1014, write_to_lower=true},
}
---------
-- Tab --
---------
Tab = defclass(Tabs, Widget)
Tab.ATTRS{
id=DEFAULT_NIL,
label=DEFAULT_NIL,
on_select=DEFAULT_NIL,
get_pens=DEFAULT_NIL,
}
function Tab:preinit(init_table)
init_table.frame = init_table.frame or {}
init_table.frame.w = #init_table.label + 4
init_table.frame.h = 2
end
function Tab:onRenderBody(dc)
local pens = self.get_pens()
dc:seek(0, 0)
if dfhack.screen.inGraphicsMode() then
dc:char(nil, pens.lt):char(nil, pens.lt2)
for i=1,#self.label do
dc:char(self.label:sub(i,i), pens.t)
end
dc:char(nil, pens.rt2):char(nil, pens.rt)
dc:seek(0, 1)
dc:char(nil, pens.lb):char(nil, pens.lb2)
for i=1,#self.label do
dc:char(self.label:sub(i,i), pens.b)
end
dc:char(nil, pens.rb2):char(nil, pens.rb)
else
local tp = pens.text_mode_tab_pen
dc:char(' ', tp):char('/', tp)
for i=1,#self.label do
dc:char('-', tp)
end
dc:char('\\', tp):char(' ', tp)
dc:seek(0, 1)
dc:char('/', tp):char('-', tp)
dc:string(self.label, pens.text_mode_label_pen)
dc:char('-', tp):char('\\', tp)
end
end
function Tab:onInput(keys)
if Tab.super.onInput(self, keys) then return true end
if keys._MOUSE_L_DOWN and self:getMousePos() then
self.on_select(self.id)
return true
end
end
-------------
-- Tab Bar --
-------------
TabBar = defclass(TabBar, ResizingPanel)
TabBar.ATTRS{
labels=DEFAULT_NIL,
on_select=DEFAULT_NIL,
get_cur_page=DEFAULT_NIL,
active_tab_pens=DEFAULT_ACTIVE_TAB_PENS,
inactive_tab_pens=DEFAULT_INACTIVE_TAB_PENS,
get_pens=DEFAULT_NIL,
key=DEFAULT_NIL,
key_back=DEFAULT_NIL,
}
function TabBar:init()
for idx,label in ipairs(self.labels) do
self:addviews{
Tab{
frame={t=0, l=0},
id=idx,
label=label,
on_select=self.on_select,
get_pens=self.get_pens and function()
return self.get_pens(idx, self)
end or function()
if self.get_cur_page() == idx then
return self.active_tab_pens
end
return self.inactive_tab_pens
end,
}
}
end
end
function TabBar:postComputeFrame(body)
local t, l, width = 0, 0, body.width
for _,tab in ipairs(self.subviews) do
if l > 0 and l + tab.frame.w > width then
t = t + 2
l = 0
end
tab.frame.t = t
tab.frame.l = l
l = l + tab.frame.w
end
end
function TabBar:onInput(keys)
if TabBar.super.onInput(self, keys) then return true end
if self.key and keys[self.key] then
local zero_idx = self.get_cur_page() - 1
local next_zero_idx = (zero_idx + 1) % #self.labels
self.on_select(next_zero_idx + 1)
return true
end
if self.key_back and keys[self.key_back] then
local zero_idx = self.get_cur_page() - 1
local prev_zero_idx = (zero_idx - 1) % #self.labels
self.on_select(prev_zero_idx + 1)
return true
end
end
return _ENV

@ -1 +1 @@
Subproject commit 7639896c68508d968c36d00bd165bbd513171048
Subproject commit 54d3f896c9938f79758fed07c20da78c78094723

@ -761,7 +761,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
df::building_tradedepotst* depot = (df::building_tradedepotst*) build;
trader_requested = trader_requested || depot->trade_flags.bits.trader_requested;
INFO(cycle,out).print(trader_requested
TRACE(cycle,out).print(trader_requested
? "Trade depot found and trader requested, trader will be excluded from all labors.\n"
: "Trade depot found but trader is not requested.\n"
);

@ -66,6 +66,7 @@ bool set_conf_paused (string name, bool pause);
class confirmation_base {
public:
bool dirty = false;
enum cstate { INACTIVE, ACTIVE, SELECTED };
virtual string get_id() = 0;
virtual string get_focus_string() = 0;
@ -281,6 +282,7 @@ public:
}
state = s;
dirty = true;
if (s == INACTIVE) {
active_id = "";
confirmation_base::active = nullptr;
@ -371,17 +373,18 @@ public:
return state == ACTIVE;
}
void render() {
static vector<string> lines;
static const std::string pause_message =
"Pause confirmations until you exit this screen";
Screen::Pen corner_ul = Screen::Pen((char)201, COLOR_GREY, COLOR_BLACK);
Screen::Pen corner_ur = Screen::Pen((char)187, COLOR_GREY, COLOR_BLACK);
Screen::Pen corner_dl = Screen::Pen((char)200, COLOR_GREY, COLOR_BLACK);
Screen::Pen corner_dr = Screen::Pen((char)188, COLOR_GREY, COLOR_BLACK);
Screen::Pen border_ud = Screen::Pen((char)205, COLOR_GREY, COLOR_BLACK);
Screen::Pen border_lr = Screen::Pen((char)186, COLOR_GREY, COLOR_BLACK);
if (state == ACTIVE)
{
static vector<string> lines;
static const std::string pause_message =
"Pause confirmations until you exit this screen";
Screen::Pen corner_ul = Screen::Pen((char)201, COLOR_GREY, COLOR_BLACK);
Screen::Pen corner_ur = Screen::Pen((char)187, COLOR_GREY, COLOR_BLACK);
Screen::Pen corner_dl = Screen::Pen((char)200, COLOR_GREY, COLOR_BLACK);
Screen::Pen corner_dr = Screen::Pen((char)188, COLOR_GREY, COLOR_BLACK);
Screen::Pen border_ud = Screen::Pen((char)205, COLOR_GREY, COLOR_BLACK);
Screen::Pen border_lr = Screen::Pen((char)186, COLOR_GREY, COLOR_BLACK);
split_string(&lines, get_message(), "\n");
size_t max_length = 40;
for (string line : lines)
@ -463,8 +466,10 @@ public:
}
set_state(INACTIVE);
}
// clean up any artifacts
df::global::gps->force_full_display_count = 1;
if(dirty) {
dirty = false;
df::global::gps->force_full_display_count = 1;
}
}
string get_id() override = 0;
string get_focus_string() override = 0;
@ -612,15 +617,7 @@ DFhackCExport command_result plugin_init (color_ostream &out, vector <PluginComm
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
{
if (is_enabled != enable)
{
for (auto c : confirmations)
{
if (!c.second->apply(enable))
return CR_FAILURE;
}
is_enabled = enable;
}
is_enabled = enable;
if (is_enabled)
{
conf_lua::simple_call("check");

@ -28,7 +28,8 @@ function setMaterials(names)
idxs.silk or -1,
idxs.cloth or -1,
idxs.yarn or -1,
idxs.leather or -1)
idxs.leather or -1,
idxs.adamantine or -1)
end
function parse_commandline(...)

@ -56,6 +56,7 @@ enum ConfigValues {
CONFIG_CLOTH_IDX = 2,
CONFIG_YARN_IDX = 3,
CONFIG_LEATHER_IDX = 4,
CONFIG_ADAMANTINE_IDX = 5,
};
static int get_config_val(PersistentDataItem &c, int index) {
@ -119,10 +120,11 @@ static const MatType
M_SILK = MatType("silk", df::job_material_category::mask_silk, df::armor_general_flags::SOFT),
M_CLOTH = MatType("cloth", df::job_material_category::mask_cloth, df::armor_general_flags::SOFT),
M_YARN = MatType("yarn", df::job_material_category::mask_yarn, df::armor_general_flags::SOFT),
M_LEATHER = MatType("leather", df::job_material_category::mask_leather, df::armor_general_flags::LEATHER);
M_LEATHER = MatType("leather", df::job_material_category::mask_leather, df::armor_general_flags::LEATHER),
M_ADAMANTINE = MatType("adamantine", df::job_material_category::mask_strand, df::armor_general_flags::SOFT);
static const std::list<MatType> all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER };
static std::list<MatType> material_order = all_materials;
static const std::list<MatType> all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER, M_ADAMANTINE };
static std::list<MatType> material_order = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; // M_ADAMANTINE is not included by default
static struct BadFlags {
uint32_t whole;
@ -208,11 +210,13 @@ public:
supply[M_CLOTH] += ss;
else if (mat.material->flags.is_set(df::material_flags::YARN))
supply[M_YARN] += ss;
else if (mat.material->flags.is_set(df::material_flags::STOCKPILE_THREAD_METAL))
supply[M_ADAMANTINE] += ss;
else
{
std::string d;
i->getItemDescription(&d, 0);
WARN(cycle).print("tailor: weird cloth item found: %s (%d)\n", d.c_str(), i->id);
DEBUG(cycle).print("tailor: weird cloth item found: %s (%d)\n", d.c_str(), i->id);
}
}
}
@ -224,7 +228,8 @@ public:
supply[M_LEATHER] += i->getStackSize();
}
DEBUG(cycle).print("tailor: available silk %d yarn %d cloth %d leather %d\n", supply[M_SILK], supply[M_YARN], supply[M_CLOTH], supply[M_LEATHER]);
DEBUG(cycle).print("tailor: available silk %d yarn %d cloth %d leather %d adamantine %d\n",
supply[M_SILK], supply[M_YARN], supply[M_CLOTH], supply[M_LEATHER], supply[M_ADAMANTINE]);
}
void scan_replacements()
@ -253,8 +258,8 @@ public:
wearing.insert(inv->item->getType());
}
int size = world->raws.creatures.all[u->race]->adultsize;
sizes[size] = u->race;
int usize = world->raws.creatures.all[u->race]->adultsize;
sizes[usize] = u->race;
for (auto ty : std::set<df::item_type>{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES })
{
@ -262,9 +267,9 @@ public:
{
TRACE(cycle).print("tailor: one %s of size %d needed to cover %s\n",
ENUM_KEY_STR(item_type, ty).c_str(),
size,
usize,
Translation::TranslateName(&u->name, false).c_str());
needed[std::make_pair(ty, size)] += 1;
needed[std::make_pair(ty, usize)] += 1;
}
}
@ -278,11 +283,11 @@ public:
}
const df::job_type o = oo->second;
int size = world->raws.creatures.all[w->getMakerRace()]->adultsize;
int isize = world->raws.creatures.all[w->getMakerRace()]->adultsize;
std::string description;
w->getItemDescription(&description, 0);
if (available[std::make_pair(ty, size)] > 0)
if (available[std::make_pair(ty, usize)] > 0)
{
if (w->flags.bits.owned)
{
@ -298,10 +303,10 @@ public:
if (wearing.count(ty) == 0)
{
DEBUG(cycle).print("tailor: allocating a %s to %s\n",
ENUM_KEY_STR(item_type, ty).c_str(),
DEBUG(cycle).print("tailor: allocating a %s (size %d) to %s\n",
ENUM_KEY_STR(item_type, ty).c_str(), usize,
Translation::TranslateName(&u->name, false).c_str());
available[std::make_pair(ty, size)] -= 1;
available[std::make_pair(ty, usize)] -= 1;
}
if (w->getWear() > 1)
@ -309,10 +314,10 @@ public:
}
else
{
DEBUG(cycle).print ("tailor: %s worn by %s needs replacement, but none available\n",
description.c_str(),
Translation::TranslateName(&u->name, false).c_str());
orders[std::make_tuple(o, w->getSubtype(), size)] += 1;
DEBUG(cycle).print ("tailor: %s (size %d) worn by %s (size %d) needs replacement, but none available\n",
description.c_str(), isize,
Translation::TranslateName(&u->name, false).c_str(), usize);
orders[std::make_tuple(o, w->getSubtype(), usize)] += 1;
}
}
}
@ -405,6 +410,13 @@ public:
std::tie(ty, sub, size) = o.first;
int count = o.second;
if (sizes.count(size) == 0)
{
WARN(cycle).print("tailor: cannot determine race for clothing of size %d, skipped\n",
size);
continue;
}
if (count > 0)
{
std::vector<int16_t> v;
@ -573,6 +585,8 @@ static void set_material_order() {
material_order.push_back(M_YARN);
else if (i == (size_t)get_config_val(config, CONFIG_LEATHER_IDX))
material_order.push_back(M_LEATHER);
else if (i == (size_t)get_config_val(config, CONFIG_ADAMANTINE_IDX))
material_order.push_back(M_ADAMANTINE);
}
if (!material_order.size())
std::copy(all_materials.begin(), all_materials.end(), std::back_inserter(material_order));
@ -689,7 +703,8 @@ static void tailor_doCycle(color_ostream &out) {
// remember, these are ONE-based indices from Lua
static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx,
int32_t clothIdx, int32_t yarnIdx, int32_t leatherIdx) {
int32_t clothIdx, int32_t yarnIdx, int32_t leatherIdx,
int32_t adamantineIdx) {
DEBUG(config,out).print("entering tailor_setMaterialPreferences\n");
// it doesn't really matter if these are invalid. set_material_order will do
@ -698,6 +713,7 @@ static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx,
set_config_val(config, CONFIG_CLOTH_IDX, clothIdx - 1);
set_config_val(config, CONFIG_YARN_IDX, yarnIdx - 1);
set_config_val(config, CONFIG_LEATHER_IDX, leatherIdx - 1);
set_config_val(config, CONFIG_ADAMANTINE_IDX, adamantineIdx - 1);
set_material_order();
}

@ -1 +1 @@
Subproject commit 9b6340be8044989d562c19d7b8bd64dbf4af5e88
Subproject commit 7e9613e21df4ec75c4f392f1e461693b52a9228a