From 468e9875e5880c726e5734c9914fdb640c77f189 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 8 Feb 2023 00:00:31 -0500 Subject: [PATCH 01/29] Update DFHack version to 50.07-alpha0, update xml --- CMakeLists.txt | 4 ++-- library/xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b2cfe724e..6854b3300 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,8 +189,8 @@ if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl endif() # set up versioning. -set(DF_VERSION "50.05") -set(DFHACK_RELEASE "alpha3.1") +set(DF_VERSION "50.07") +set(DFHACK_RELEASE "alpha0") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/library/xml b/library/xml index 68f8bfa92..e3c3d6a75 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 68f8bfa92b68ef9bc30da7f5a8ea5cf91f69ea16 +Subproject commit e3c3d6a755de05c36bb7f2ac873d6ccb70410589 From 057d2d8c5d3a3fd5d748c85e36bf195e9b9cce16 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 8 Feb 2023 00:00:52 -0500 Subject: [PATCH 02/29] Update scripts (hermit) --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 112d69ed3..f969699a4 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 112d69ed3e0efb22e3ec6a6c8e58ad3135b80351 +Subproject commit f969699a447fde05b0bd3397a8b1e2ce668bc54d From 8ee05af6efa0260457a87bff05333a9a858180ac Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Feb 2023 15:11:17 -0800 Subject: [PATCH 03/29] update logo to crisper version provided by TaxiService --- data/art/dfhack.png | Bin 1465 -> 372 bytes docs/changelog.txt | 1 + 2 files changed, 1 insertion(+) diff --git a/data/art/dfhack.png b/data/art/dfhack.png index 17719f084a45a3b037bab5fd4355d5f76f674fbd..7cbb5dbf6201a921aae295aa71fbfa45297636fb 100644 GIT binary patch delta 356 zcmV-q0h|803-khz8Gix*003u+ueAUG02y>eSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM008_+L_t(|+Qe5u4#Xe`ti-?WrO)Z5uXUSkVhDpMR=Olj3_+j+jG9U* zTcqUcJB~BR`@Vm2T`l*1=S^F%ga^oVl~S@zYddr;xiO)tC4bd#`H66cN(CZyn;OGT zobOs+1CbMXH_R7#P%e}(q=3#1QTHc!wcHR*qaRXaj6s9Azeg3`rlQdFJ8g=tCuYJ; zcBo+t``Cb@`2ZgQVyu z)K`E305ylW-7c57cK`qY4rN$LW=%~1DgXcg2mk;800000(o>TF0000R{AK$GU=4uJ=cBK$ zkEy9C#>dA=CX?9hb`@l`TG8wOi`8T@aq!?lnwpwevt|td@87@Y?c2A6!(ry;=7>Zh z#N%=Net+JHo12?Cefl)W|LoPPR|06Ry1F_!ckZ0#z0GEmg@uL8aQX4$ht$>8<;>C9 z*(r^Ujal!UPJgF}h$sLeB5iGLn!z5AM-~?s<>t+s8ed;u-}2$|`0-;+%!?NpWn}>D+qVzD-%n|2X?`VKIaw|eO-@c~Vlq`(w{Bf7 zj;Z^Vlm8KbWHPDl)9EyWgM(RqlgUI)O-)_^@p$~tdqFT5T(tr&my6!s-Yoz1>(|x9 zxt1pui~Tu(PoF+%0=!-?0Dixp_V#v-zrMacXMeRaR!~sz`(^~NxVXr`zC+%x7%fD zX-Pyxd_JG__xH>0-Ma;l!-o$`b#=8G*VEG@A3uJ~5}=IfL`3H2=U0`?!oorf1_MP! zMSnS2p3#AVf&xlPO3>@|h=^Je5m7l-n(qH`CQK%i7z_q=UdUJ^64C6GtcXx3#Kgpe zN|H*YlyB$Gos^fC<8rx(#bU}=RaM1~9XoKjTzvoj9k18R-Me?WeEBkEWo3B1UYeSk za5|lIcX#vr`SbkED4kAAZ*Q*va_7z+)qh@FTU#ZSN{NU_XJ=;?FMK|qYCY&I*2jz**0xN#$^syPp(nVuex#}&EK=`;W%BO`b`9)D~$ z8&y?RSq*3V_U%+wRuYTFsIRYQ{rdGvdXFDJPFGhKwY9aBl$7M>Kn6KLTU#63wrvAY zs2@Ij2*BC1XPKOw#N+W0jYb(B9_GM-1L*a7YHDh**=!US7pq)n&YWRpW(KR(N+1wW z%}c|9j3xlm(a|Aq-n(64=dNVr>92%`TY5_ z3=ItlAQKZ4`8lw>B_BI>jOyxYrIMjgNb$t&b~7|Iq~aSI8kAfwxAO9GH6~N~mCC3A zkU3N&5(xz+9*?U|`SRsU#>U2|sHjlewXv~L{U78yPbQN|)u^J;=;{D6#DDwx^{XOZ zC=^mEXt7vmZf@q(sZ#*_`t=L5*^JR> Date: Tue, 7 Feb 2023 15:40:23 -0800 Subject: [PATCH 04/29] add light AA --- data/art/dfhack.png | Bin 372 -> 997 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/art/dfhack.png b/data/art/dfhack.png index 7cbb5dbf6201a921aae295aa71fbfa45297636fb..133dc5aaf8c98921bcc0203ebd986d4915c726cd 100644 GIT binary patch delta 906 zcmV;519klL0_6vgNPh!uNklIMuJ-o!v_=t}TK@FQbar-Dencvj z!dNbsrOxQ+Xp5lg4mvwKVuhlhuM ztZb{*sz`>1hX)4--EMa%6heXNG+KS4a4AVf6_Mj;G=KW;93UX8tE)T>1Ogb_+S;U! zGJT%|e6^fTPfu$TMsT9zKvAOU*4CChJ`zX?pqMb3OeTG>J3T#>3&4rp-Cb!N(Vv-_ zA^!02P%0}$RR!!6Y>@_JMUaX`pHBK%RG$b-lF1}7OzZ3GRYJ76 zxruKC9Dgw~gGXerDgy!`kQuQeO&Xm}C#^!xSX~pYVH74L2uoy!GrkM}-6T9XIN)1B z>p%@rM@I);ji?!7VzC&nK0Q6{@9(3uu!=8cROf!4n3w?iqBmC0_YV-5+Hk*xF8o+wK0M5_PF^-RqQ!8CxUo$|#0a*t_`a?rQ0|Ns(&lW-g9G?o2 z;)68a-QAI0ATt=qv8p^|AVCb3>qyOk#bV(TF%^qN+6e;g)5Wr}vB3(2-U>je4({*o zF@HWjK5Eh5<#HiPtTQz=Mb(wd<>X#=Aff3`h){fhHo6fxM2m}yNH|V+c6Meqn@NsF zMS&h29c^!Kqim4>_V#wj=_oxtJu3En1dHTzhzQM0ZHTAQN3lq@7!#g3D6QAmSAH6O g)$6)(|3R7?{TJ$|00RKEP1L&_iGFGT0FzJwBvOyD5&!@I delta 275 zcmV+u0qp+e2lN7vNPhtQNklpPA!$osy3a$PO=e&u5fhrM(!ILoC~>82Y#lgA?%Yd@eB5k0J_jn%(!&A9 zBTpnmPf|wohPsH`856YfLo+t``Cb@`2Z ZgQVyu)K`E300=dQxZN(7xObDk119bWfv^Ao From be0cec95206eb19dca075cbec611a81a9d848bcf Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Wed, 8 Feb 2023 01:59:57 -0500 Subject: [PATCH 05/29] Current attempt to fix lua stack smashing --- plugins/automelt.cpp | 37 ++++++++++++++++++++++++++++++++----- plugins/lua/automelt.lua | 11 ++++++++--- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index e8001c48b..1df7f9a68 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -567,6 +567,25 @@ static void push_stockpile_config(lua_State *L, PersistentDataItem &c) { get_config_bool(c, STOCKPILE_CONFIG_MONITORED)); } +static void emplace_bulk_stockpile_configs(lua_State *L, PersistentDataItem &c, map> &stockpiles) { + int32_t id = get_config_val(c, STOCKPILE_CONFIG_ID); + bool monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); + map stockpile_config; + stockpile_config.emplace("id", id); + stockpile_config.emplace("monitored", monitored); + + stockpiles.emplace(id, stockpile_config); +} + +static void emplace_bulk_stockpile_configs(lua_State *L, int id, bool monitored, map> &stockpiles) { + + map stockpile_config; + stockpile_config.emplace("id", id); + stockpile_config.emplace("monitored", monitored); + + stockpiles.emplace(id, stockpile_config); +} + static void automelt_designate(color_ostream &out) { DEBUG(status, out).print("entering automelt designate\n"); out.print("designated %d item(s) for melting\n", do_cycle(out)); @@ -736,6 +755,8 @@ static int automelt_getSelectedStockpileConfig(lua_State *L){ return 1; } + + static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { color_ostream *out = Lua::GetOutput(L); if (!out) @@ -762,24 +783,30 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { Lua::Push(L, item_count_piles); Lua::Push(L, marked_item_count_piles); Lua::Push(L, premarked_item_count_piles); - int32_t bldg_count = 0; + + map> stockpile_config_map; for (auto pile : world->buildings.other.STOCKPILE) { if (!isStockpile(pile)) continue; - bldg_count++; int id = pile->id; if (watched_stockpiles.count(id)) { DEBUG(cycle,*out).print("indexed_id=%d\n", get_config_val(watched_stockpiles[id], STOCKPILE_CONFIG_ID)); - push_stockpile_config(L, watched_stockpiles[id]); + emplace_bulk_stockpile_configs(L, watched_stockpiles[id], stockpile_config_map); + } else { - push_stockpile_config(L, id, false); + emplace_bulk_stockpile_configs(L, id, false, stockpile_config_map); } } + + Lua::Push(L, stockpile_config_map); + + DEBUG(cycle, *out).print("confmap_length: %d\n", stockpile_config_map.size()); + DEBUG(perf, *out).print("exit automelt_getItemCountsAndStockpileConfigs\n"); - return 4+bldg_count; + return 5; } DFHACK_PLUGIN_LUA_FUNCTIONS{ diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index b49a434a1..535c056e6 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -64,8 +64,12 @@ function getItemCountsAndStockpileConfigs() ret.item_counts = table.remove(data, 1) ret.marked_item_counts = table.remove(data, 1) ret.premarked_item_counts = table.remove(data, 1) - ret.stockpile_configs = data - for _,c in ipairs(ret.stockpile_configs) do + local unparsed_stockpile_configs = table.remove(data, 1) + ret.stockpile_configs = {} + + for idx,c in pairs(unparsed_stockpile_configs) do + print(c.id) + print(c.monitored) if not c.id or c.id == -1 then c.name = "ERROR" c.monitored = false @@ -76,7 +80,8 @@ function getItemCountsAndStockpileConfigs() end c.monitored = c.monitored ~= 0 end - + table.insert(ret.stockpile_configs, c) + end return ret end From 585b8da03a932dcf784218338b5dc2d42165c1a7 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 8 Feb 2023 07:15:14 +0000 Subject: [PATCH 06/29] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index f969699a4..54f5d5f3d 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f969699a447fde05b0bd3397a8b1e2ce668bc54d +Subproject commit 54f5d5f3d3b68b07323e88109b9a44d98abe52b1 From d0b4b1a4a5b3aa0fe55f2e954a26ffb072a9092f Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 8 Feb 2023 07:36:55 +0000 Subject: [PATCH 07/29] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index e3c3d6a75..cd5baf4ea 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e3c3d6a755de05c36bb7f2ac873d6ccb70410589 +Subproject commit cd5baf4ea82fead3883368da526ac2b606a6209b From c1b9de87d2303acecd763d71c020d70046049dc8 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Mon, 6 Feb 2023 22:40:59 -0500 Subject: [PATCH 08/29] Add case_sensitive attr to FilteredList --- library/lua/gui/widgets.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d8c605b93..61cd92cfa 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1866,6 +1866,7 @@ end FilteredList = defclass(FilteredList, Widget) FilteredList.ATTRS { + case_sensitive = true, edit_below = false, edit_key = DEFAULT_NIL, edit_ignore_keys = DEFAULT_NIL, @@ -2026,11 +2027,17 @@ function FilteredList:setFilter(filter, pos) -- start matches at non-space or non-punctuation. this allows -- punctuation itself to be matched if that is useful (e.g. -- filenames or parameter names) - if key ~= '' and + if key ~= '' then + if self.case_sensitive and not search_key:match('%f[^%p\x00]'..key) and not search_key:match('%f[^%s\x00]'..key) then - ok = false - break + ok = false + break + elseif not string.lower(search_key):match('%f[^%p\x00]'..string.lower(key)) and + not string.lower(search_key):match('%f[^%s\x00]'..string.lower(key)) then + ok = false + break + end end end if ok then From 13440d18a85f4fb2de4f0d4f71137ff84f76dd9c Mon Sep 17 00:00:00 2001 From: Robob27 Date: Mon, 6 Feb 2023 22:48:18 -0500 Subject: [PATCH 09/29] Add case_sensitive to FilteredList docs --- docs/dev/Lua API.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 503f4bece..3580b5244 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4958,6 +4958,7 @@ construction that allows filtering the list by subwords of its items. In addition to passing through all attributes supported by List, it supports: +:case_sensitive: If true, matching is case sensitive. Defaults to true. :edit_pen: If specified, used instead of ``cursor_pen`` for the edit field. :edit_below: If true, the edit field is placed below the list instead of above. :edit_key: If specified, the edit field is disabled until this key is pressed. From 54560bc5dbab1b0b1ddaf26d5e73487aaf8f15fa Mon Sep 17 00:00:00 2001 From: Robob27 Date: Wed, 8 Feb 2023 02:02:50 -0500 Subject: [PATCH 10/29] Don't duplicate regex --- library/lua/gui/widgets.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 61cd92cfa..c249b579f 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -2028,15 +2028,15 @@ function FilteredList:setFilter(filter, pos) -- punctuation itself to be matched if that is useful (e.g. -- filenames or parameter names) if key ~= '' then - if self.case_sensitive and - not search_key:match('%f[^%p\x00]'..key) and + if not self.case_sensitive then + search_key = string.lower(search_key) + key = string.lower(key) + end + + if not search_key:match('%f[^%p\x00]'..key) and not search_key:match('%f[^%s\x00]'..key) then ok = false break - elseif not string.lower(search_key):match('%f[^%p\x00]'..string.lower(key)) and - not string.lower(search_key):match('%f[^%s\x00]'..string.lower(key)) then - ok = false - break end end end From dd10451c2f66b16f0097ef622290803151dd0919 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Wed, 8 Feb 2023 17:42:42 +1100 Subject: [PATCH 11/29] Remove library/military silver crossbow work-order It is not possible to create a work-order for making silver crossbows, so the silver crossbow recipe belongs in the alternative military library with other artifact/non-craftable weapon recipes. It's already present in the library/military_include_artifact_materials work-orders, so simply needed removal from the vanilla library/military orders version. --- data/orders/military.json | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/data/orders/military.json b/data/orders/military.json index 5b7604dac..0d06dd124 100644 --- a/data/orders/military.json +++ b/data/orders/military.json @@ -1138,42 +1138,6 @@ "job" : "MakeWeapon", "material" : "INORGANIC:SILVER" }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 64, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:SILVER" - }, { "amount_left" : 1, "amount_total" : 1, From 24827eabe268df2d762e4403f0261e2cf376b1c5 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Wed, 8 Feb 2023 20:25:52 +1100 Subject: [PATCH 12/29] Remove checks for silver from library/military for crossbows. --- data/orders/military.json | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/data/orders/military.json b/data/orders/military.json index 0d06dd124..0e53747b6 100644 --- a/data/orders/military.json +++ b/data/orders/military.json @@ -1620,12 +1620,6 @@ "item_type" : "WEAPON", "material" : "INORGANIC:STEEL", "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 } ], "item_subtype" : "ITEM_WEAPON_CROSSBOW", @@ -2321,12 +2315,6 @@ "material" : "INORGANIC:STEEL", "value" : 30 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -3032,12 +3020,6 @@ "material" : "INORGANIC:STEEL", "value" : 30 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -3820,12 +3802,6 @@ "material" : "INORGANIC:STEEL", "value" : 30 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -4698,12 +4674,6 @@ "material" : "INORGANIC:STEEL", "value" : 30 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "flags" : From 1f38b294cbdb34b0eedd6be503d0b034bbe559f3 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Wed, 8 Feb 2023 20:32:32 +1100 Subject: [PATCH 13/29] Add note in changelog about silver crossbows --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ece6f04e1..9faa0ab4f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,6 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `clean`: new hotkey for `spotclean`: Ctrl-C - `autobutcher`: changed defaults from 5 females / 1 male to 4 females / 2 males so a single unfortunate accident doesn't leave players without a mating pair - `autobutcher`: now immediately loads races available at game start into the watchlist +- `orders`: recipe for silver crossbows removed from ``library/military`` as it is not a vanilla recipe, but is available in ``library/military_include_artifact_materials`` ## Documentation From 22b31bd7f1d970e65397edb5cde72dc3c4b82a24 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Wed, 8 Feb 2023 12:37:35 -0500 Subject: [PATCH 14/29] Update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ece6f04e1..fbc26f062 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -57,6 +57,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - `overlay`: overlay widgets can now specify focus paths for the viewscreens they attach to so they only appear in specific contexts. see `overlay-dev-guide` for details. - ``widgets.CycleHotkeyLabel``: Added ``key_back`` optional parameter to cycle backwards. +- ``widgets.FilteredList``: Added ``case_sensitive`` optional paramter to determine if filtering is case sensitive. ## Removed From b950b569267888a6f0a61c81adc08406c805d9a5 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Wed, 8 Feb 2023 13:00:13 -0500 Subject: [PATCH 15/29] Add methods to HotkeyLabel --- docs/changelog.txt | 2 ++ docs/dev/Lua API.rst | 10 ++++++++++ library/lua/gui/widgets.lua | 14 ++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ece6f04e1..7ba027f76 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -57,6 +57,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - `overlay`: overlay widgets can now specify focus paths for the viewscreens they attach to so they only appear in specific contexts. see `overlay-dev-guide` for details. - ``widgets.CycleHotkeyLabel``: Added ``key_back`` optional parameter to cycle backwards. +- ``widgets.HotkeyLabel``: Added ``setLabel`` method to allow easily updating the label text without mangling the keyboard shortcut. +- ``widgets.HotkeyLabel``: Added ``setOnActivate`` method to allow easily updating the ``on_activate`` callback. ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 503f4bece..43ed9c935 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4808,6 +4808,16 @@ It has the following attributes: :on_activate: If specified, it is the callback that will be called whenever the hotkey is pressed or the label is clicked. +The HotkeyLabel widget implements the following methods: + +* ``hotkeylabel:setLabel(label)`` + + Updates the label without altering the hotkey text. + +* ``hotkeylabel:setOnActivate(on_activate)`` + + Updates the on_activate callback. + CycleHotkeyLabel class ---------------------- diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d8c605b93..580273dc8 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1419,6 +1419,20 @@ HotkeyLabel.ATTRS{ } function HotkeyLabel:init() + self:initializeLabel() +end + +function HotkeyLabel:setOnActivate(on_activate) + self.on_activate = on_activate + self:initializeLabel() +end + +function HotkeyLabel:setLabel(label) + self.label = label + self:initializeLabel() +end + +function HotkeyLabel:initializeLabel() self:setText{{key=self.key, key_sep=self.key_sep, text=self.label, on_activate=self.on_activate}} end From 02a249fdee4181318a24672c14b4213ccaec5824 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Wed, 8 Feb 2023 14:01:32 -0500 Subject: [PATCH 16/29] Fixes the lua stack smashing issue --- plugins/autochop.cpp | 40 +++++++++++++++++++++++++++++++++++++--- plugins/automelt.cpp | 22 ++++++++-------------- plugins/lua/autochop.lua | 7 +++++-- plugins/lua/automelt.lua | 2 -- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index bf7540e29..7a16a79b4 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -803,6 +803,33 @@ static void push_burrow_config(lua_State *L, PersistentDataItem &c) { get_config_bool(c, BURROW_CONFIG_PROTECT_COOKABLE)); } +static void emplace_bulk_burrow_config(lua_State *L, map> &burrows, int id, bool chop = false, + bool clearcut = false, bool protect_brewable = false, + bool protect_edible = false, bool protect_cookable = false) { + + map burrow_config; + burrow_config.emplace("id", id); + burrow_config.emplace("chop", chop); + burrow_config.emplace("clearcut", clearcut); + burrow_config.emplace("protect_brewable", protect_brewable); + burrow_config.emplace("protect_edible", protect_edible); + burrow_config.emplace("protect_cookable", protect_cookable); + + burrows.emplace(id, burrow_config); + +} + +static void emplace_bulk_burrow_config(lua_State *L, map> &burrows, PersistentDataItem &c) { + emplace_bulk_burrow_config(L, burrows, get_config_val(c, BURROW_CONFIG_ID), + get_config_bool(c, BURROW_CONFIG_CHOP), + get_config_bool(c, BURROW_CONFIG_CLEARCUT), + get_config_bool(c, BURROW_CONFIG_PROTECT_BREWABLE), + get_config_bool(c, BURROW_CONFIG_PROTECT_EDIBLE), + get_config_bool(c, BURROW_CONFIG_PROTECT_COOKABLE)); + + +} + static int autochop_getTreeCountsAndBurrowConfigs(lua_State *L) { color_ostream *out = Lua::GetOutput(L); if (!out) @@ -818,6 +845,9 @@ static int autochop_getTreeCountsAndBurrowConfigs(lua_State *L) { &designated_trees, &accessible_yield, &tree_counts, &designated_tree_counts); map summary; + + map> burrow_config_map; + summary.emplace("accessible_trees", accessible_trees); summary.emplace("inaccessible_trees", inaccessible_trees); summary.emplace("designated_trees", designated_trees); @@ -831,13 +861,17 @@ static int autochop_getTreeCountsAndBurrowConfigs(lua_State *L) { for (auto &burrow : plotinfo->burrows.list) { int id = burrow->id; if (watched_burrows_indices.count(id)) { - push_burrow_config(L, watched_burrows[watched_burrows_indices[id]]); + // push_burrow_config(L, watched_burrows[watched_burrows_indices[id]]); + emplace_bulk_burrow_config(L, burrow_config_map, watched_burrows[watched_burrows_indices[id]]); } else { - push_burrow_config(L, id); + // push_burrow_config(L, id); + emplace_bulk_burrow_config(L, burrow_config_map, id); } } - return 3 + plotinfo->burrows.list.size(); + Lua::Push(L, burrow_config_map); + + return 4; } static int autochop_getBurrowConfig(lua_State *L) { diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 1df7f9a68..ca0cf1626 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -567,9 +567,8 @@ static void push_stockpile_config(lua_State *L, PersistentDataItem &c) { get_config_bool(c, STOCKPILE_CONFIG_MONITORED)); } -static void emplace_bulk_stockpile_configs(lua_State *L, PersistentDataItem &c, map> &stockpiles) { - int32_t id = get_config_val(c, STOCKPILE_CONFIG_ID); - bool monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); +static void emplace_bulk_stockpile_config(lua_State *L, int id, bool monitored, map> &stockpiles) { + map stockpile_config; stockpile_config.emplace("id", id); stockpile_config.emplace("monitored", monitored); @@ -577,13 +576,10 @@ static void emplace_bulk_stockpile_configs(lua_State *L, PersistentDataItem &c, stockpiles.emplace(id, stockpile_config); } -static void emplace_bulk_stockpile_configs(lua_State *L, int id, bool monitored, map> &stockpiles) { - - map stockpile_config; - stockpile_config.emplace("id", id); - stockpile_config.emplace("monitored", monitored); - - stockpiles.emplace(id, stockpile_config); +static void emplace_bulk_stockpile_config(lua_State *L, PersistentDataItem &c, map> &stockpiles) { + int32_t id = get_config_val(c, STOCKPILE_CONFIG_ID); + bool monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); + emplace_bulk_stockpile_config(L, id, monitored, stockpiles); } static void automelt_designate(color_ostream &out) { @@ -792,17 +788,15 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { int id = pile->id; if (watched_stockpiles.count(id)) { - DEBUG(cycle,*out).print("indexed_id=%d\n", get_config_val(watched_stockpiles[id], STOCKPILE_CONFIG_ID)); - emplace_bulk_stockpile_configs(L, watched_stockpiles[id], stockpile_config_map); + emplace_bulk_stockpile_config(L, watched_stockpiles[id], stockpile_config_map); } else { - emplace_bulk_stockpile_configs(L, id, false, stockpile_config_map); + emplace_bulk_stockpile_config(L, id, false, stockpile_config_map); } } Lua::Push(L, stockpile_config_map); - DEBUG(cycle, *out).print("confmap_length: %d\n", stockpile_config_map.size()); DEBUG(perf, *out).print("exit automelt_getItemCountsAndStockpileConfigs\n"); diff --git a/plugins/lua/autochop.lua b/plugins/lua/autochop.lua index 6b8c9f943..e9bad2eef 100644 --- a/plugins/lua/autochop.lua +++ b/plugins/lua/autochop.lua @@ -96,14 +96,17 @@ function getTreeCountsAndBurrowConfigs() ret.summary = table.remove(data, 1) ret.tree_counts = table.remove(data, 1) ret.designated_tree_counts = table.remove(data, 1) - ret.burrow_configs = data - for _,c in ipairs(ret.burrow_configs) do + local unparsed_burrow_configs = table.remove(data, 1) + + ret.burrow_configs = {} + for idx,c in pairs(unparsed_burrow_configs) do c.name = df.burrow.find(c.id).name c.chop = c.chop ~= 0 c.clearcut = c.clearcut ~= 0 c.protect_brewable = c.protect_brewable ~= 0 c.protect_edible = c.protect_edible ~= 0 c.protect_cookable = c.protect_cookable ~= 0 + table.insert(ret.burrow_configs, c) end return ret end diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index 535c056e6..395934867 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -68,8 +68,6 @@ function getItemCountsAndStockpileConfigs() ret.stockpile_configs = {} for idx,c in pairs(unparsed_stockpile_configs) do - print(c.id) - print(c.monitored) if not c.id or c.id == -1 then c.name = "ERROR" c.monitored = false From bf0b3f8845d44f85aa899083d5feb335ac507f7d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 19:06:33 +0000 Subject: [PATCH 17/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- plugins/automelt.cpp | 2 +- plugins/lua/automelt.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index ca0cf1626..862507413 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -568,7 +568,7 @@ static void push_stockpile_config(lua_State *L, PersistentDataItem &c) { } static void emplace_bulk_stockpile_config(lua_State *L, int id, bool monitored, map> &stockpiles) { - + map stockpile_config; stockpile_config.emplace("id", id); stockpile_config.emplace("monitored", monitored); diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index 395934867..cbd1bd538 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -66,7 +66,7 @@ function getItemCountsAndStockpileConfigs() ret.premarked_item_counts = table.remove(data, 1) local unparsed_stockpile_configs = table.remove(data, 1) ret.stockpile_configs = {} - + for idx,c in pairs(unparsed_stockpile_configs) do if not c.id or c.id == -1 then c.name = "ERROR" @@ -79,7 +79,7 @@ function getItemCountsAndStockpileConfigs() c.monitored = c.monitored ~= 0 end table.insert(ret.stockpile_configs, c) - + end return ret end From 5d8d378917c75c61fed72a6e37009838edee4ec5 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 8 Feb 2023 12:03:40 -0800 Subject: [PATCH 18/29] Implements plugin: channel-safely v1.2.3 - Revises a few log lines - Adds d_assert macro to replace assert usage - prints an error to indicate d_assert failed in Release builds as well as Debug builds - could be added to the Debug utilities to allow use of assertions on necessary code without needing to buffer the results for use in the assert statement - Fixes bug wherein designations are never put into marker mode (related to the assert statements) --- plugins/channel-safely/channel-manager.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/plugins/channel-safely/channel-manager.cpp b/plugins/channel-safely/channel-manager.cpp index c22bd8a8e..aa7a24461 100644 --- a/plugins/channel-safely/channel-manager.cpp +++ b/plugins/channel-safely/channel-manager.cpp @@ -5,7 +5,17 @@ #include //hash function for df::coord #include +#define NUMARGS(...) std::tuple_size::value +#define d_assert(condition, ...) \ + static_assert(NUMARGS(__VA_ARGS__) >= 1, "d_assert(condition, format, ...) requires at least up to format as arguments"); \ + if (!condition) { \ + DFHack::Core::getInstance().getConsole().printerr(__VA_ARGS__); \ + assert(0); \ + } + + df::unit* find_dwarf(const df::coord &map_pos) { + df::unit* nearest = nullptr; uint32_t distance; for (auto unit : df::global::world->units.active) { @@ -57,6 +67,7 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool // cavein prevention bool cavein_possible = false; uint8_t least_access = 100; + std::unordered_map cavein_candidates; if (!marker_mode) { /* To prevent cave-ins we're looking at accessibility of tiles with open space below them @@ -111,7 +122,7 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool // if no cave-in is possible [or we don't check for], we'll just execute normally and move on if (!cavein_possible) { TRACE(manager).print("cave-in evaluated false\n"); - assert(manage_one(pos, true, marker_mode)); + d_assert(manage_one(pos, true, marker_mode), "manage_one() is failing under !cavein"); continue; } // cavein is only possible if marker_mode is false @@ -136,16 +147,16 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool evT->priority[Coord(local)] = v; } } - assert(manage_one(pos, true, false)); + d_assert(manage_one(pos, true, false), "manage_one() is failing for cavein "); continue; } // cavein possible, but we failed to meet the criteria for activation if (cavein_candidates.count(pos)) { - DEBUG(manager).print("cave-in evaluated true and no dignow and (%d > %d)\n", cavein_candidates[pos], least_access+OFFSET); + DEBUG(manager).print("cave-in evaluated true and the cavein candidate's accessibility check was made as (%d <= %d)\n", cavein_candidates[pos], least_access+OFFSET); } else { - DEBUG(manager).print("cave-in evaluated true and no dignow and pos is not a candidate\n"); + DEBUG(manager).print("cave-in evaluated true and the position was not a candidate, nor was it set for dignow\n"); } - assert(manage_one(pos, true, true)); + d_assert(manage_one(pos, true, true), "manage_one() is failing to set a cave-in causing designation to marker mode"); } INFO(manager).print("manage_group() is done\n"); } From caeb6d2f84786b953b1d7400669cc9306b5879af Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 8 Feb 2023 12:09:32 -0800 Subject: [PATCH 19/29] Updates changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ece6f04e1..f8d8e041b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,6 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Units::isFortControlled``: Account for agitated wildlife - Fix right click sometimes closing both a DFHack window and a vanilla panel - Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible +- `channel-safely`: fixed bug resulting in marker mode never being set for any designation ## Misc Improvements - `automelt`: is now more resistent to savegame corruption From cd12f95849be5c519b83a15cc8bf1ceb6a64cea0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 12:15:19 -0800 Subject: [PATCH 20/29] add const & to Push overloads; remove circular def --- library/LuaApi.cpp | 2 +- library/LuaTools.cpp | 8 ++++---- library/include/LuaTools.h | 39 +++++++++++++++++++++----------------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 45ea7d84d..69d43821e 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -478,7 +478,7 @@ static void OpenPersistent(lua_State *state) static int DFHACK_MATINFO_TOKEN = 0; -void Lua::Push(lua_State *state, MaterialInfo &info) +void Lua::Push(lua_State *state, const MaterialInfo &info) { if (!info.isValid()) { diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index b4d009c7b..ed0b0699d 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -101,7 +101,7 @@ void DFHack::Lua::Push(lua_State *state, const Units::NoblePosition &pos) lua_setfield(state, -2, "position"); } -void DFHack::Lua::Push(lua_State *state, df::coord pos) +void DFHack::Lua::Push(lua_State *state, const df::coord &pos) { lua_createtable(state, 0, 3); lua_pushinteger(state, pos.x); @@ -112,7 +112,7 @@ void DFHack::Lua::Push(lua_State *state, df::coord pos) lua_setfield(state, -2, "z"); } -void DFHack::Lua::Push(lua_State *state, df::coord2d pos) +void DFHack::Lua::Push(lua_State *state, const df::coord2d &pos) { lua_createtable(state, 0, 2); lua_pushinteger(state, pos.x); @@ -191,7 +191,7 @@ void DFHack::Lua::PushInterfaceKeys(lua_State *L, } } -int DFHack::Lua::PushPosXYZ(lua_State *state, df::coord pos) +int DFHack::Lua::PushPosXYZ(lua_State *state, const df::coord &pos) { if (!pos.isValid()) { @@ -207,7 +207,7 @@ int DFHack::Lua::PushPosXYZ(lua_State *state, df::coord pos) } } -int DFHack::Lua::PushPosXY(lua_State *state, df::coord2d pos) +int DFHack::Lua::PushPosXY(lua_State *state, const df::coord2d &pos) { if (!pos.isValid()) { diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index ca9aac788..86d3d0c7e 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -325,10 +325,10 @@ namespace DFHack {namespace Lua { inline void Push(lua_State *state, const std::string &str) { lua_pushlstring(state, str.data(), str.size()); } - DFHACK_EXPORT void Push(lua_State *state, df::coord obj); - DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj); + DFHACK_EXPORT void Push(lua_State *state, const df::coord &obj); + DFHACK_EXPORT void Push(lua_State *state, const df::coord2d &obj); void Push(lua_State *state, const Units::NoblePosition &pos); - DFHACK_EXPORT void Push(lua_State *state, MaterialInfo &info); + DFHACK_EXPORT void Push(lua_State *state, const MaterialInfo &info); DFHACK_EXPORT void Push(lua_State *state, const Screen::Pen &info); template inline void Push(lua_State *state, T *ptr) { PushDFObject(state, ptr); @@ -361,29 +361,34 @@ namespace DFHack {namespace Lua { DFHACK_EXPORT void GetVector(lua_State *state, std::vector &pvec); - DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos); - DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos); - - template - inline void TableInsert(lua_State *state, T_Key key, T_Value value) - { - Lua::Push(state, key); - Lua::Push(state, value); - lua_settable(state, -3); - } + DFHACK_EXPORT int PushPosXYZ(lua_State *state, const df::coord &pos); + DFHACK_EXPORT int PushPosXY(lua_State *state, const df::coord2d &pos); template void Push(lua_State *L, const std::map &pmap) { lua_createtable(L, 0, pmap.size()); - for (auto &entry : pmap) - TableInsert(L, entry.first, entry.second); + for (auto &entry : pmap) { + Lua::Push(L, entry.first); + Lua::Push(L, entry.second); + lua_settable(L, -3); + } } template void Push(lua_State *L, const std::unordered_map &pmap) { lua_createtable(L, 0, pmap.size()); - for (auto &entry : pmap) - TableInsert(L, entry.first, entry.second); + for (auto &entry : pmap) { + Lua::Push(L, entry.first); + Lua::Push(L, entry.second); + lua_settable(L, -3); + } + } + + template + inline void TableInsert(lua_State *state, const T_Key &key, const T_Value &value) { + Lua::Push(state, key); + Lua::Push(state, value); + lua_settable(state, -3); } DFHACK_EXPORT void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true); From 1e4a73007ebd68c1fea81b01d90fe2752378c9fe Mon Sep 17 00:00:00 2001 From: Eamon Bode Date: Wed, 8 Feb 2023 15:44:55 -0500 Subject: [PATCH 21/29] Apply suggestions from code review Whitespace fixes Co-authored-by: Myk --- plugins/autochop.cpp | 4 ---- plugins/automelt.cpp | 3 --- 2 files changed, 7 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 7a16a79b4..3a433f7a9 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -816,7 +816,6 @@ static void emplace_bulk_burrow_config(lua_State *L, map> &burrows, PersistentDataItem &c) { @@ -826,8 +825,6 @@ static void emplace_bulk_burrow_config(lua_State *L, map> &stockpiles) { - map stockpile_config; stockpile_config.emplace("id", id); stockpile_config.emplace("monitored", monitored); @@ -751,8 +750,6 @@ static int automelt_getSelectedStockpileConfig(lua_State *L){ return 1; } - - static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { color_ostream *out = Lua::GetOutput(L); if (!out) From 5a5fcbd1346b49734deecc895b4115d27c23b343 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Wed, 8 Feb 2023 15:52:58 -0500 Subject: [PATCH 22/29] Add changes to changelog.txt --- docs/changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index daf0e8e1d..18635ca2b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Fix right click sometimes closing both a DFHack window and a vanilla panel - Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible - `channel-safely`: fixed bug resulting in marker mode never being set for any designation +- `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs +- `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs ## Misc Improvements - `automelt`: is now more resistent to savegame corruption From 9f76d64e420a1e78557bfadb4760f369afc8dbec Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 14:02:44 -0800 Subject: [PATCH 23/29] update misery; persist state --- docs/plugins/misery.rst | 33 +++- plugins/CMakeLists.txt | 2 +- plugins/lua/misery.lua | 43 +++++ plugins/misery.cpp | 352 ++++++++++++++++++++++++---------------- 4 files changed, 285 insertions(+), 145 deletions(-) create mode 100644 plugins/lua/misery.lua diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst index f2c4d1952..8a65d8419 100644 --- a/docs/plugins/misery.rst +++ b/docs/plugins/misery.rst @@ -2,18 +2,35 @@ misery ====== .. dfhack-tool:: - :summary: Increase the intensity of negative dwarven thoughts. - :tags: fort armok auto units + :summary: Increase the intensity of your citizens' negative thoughts. + :tags: fort gameplay units -When enabled, negative thoughts that your dwarves have will multiply by the -specified factor. +When enabled, negative thoughts that your citizens have will multiply by the +specified factor. This makes it more challenging to keep them happy. Usage ----- +:: + + enable misery + misery [status] + misery + misery clear + +The default misery factor is ``2``, meaning that your dwarves will become +miserable twice as fast. + +Examples +-------- + ``enable misery`` - Start multiplying negative thoughts. -``misery `` - Change the multiplicative factor of bad thoughts. The default is ``2``. + Start multiplying bad thoughts for your citizens! + +``misery 5`` + Make dwarves become unhappy 5 times faster than normal -- this is quite + challenging to handle! + ``misery clear`` - Clear away negative thoughts added by ``misery``. + Clear away negative thoughts added by ``misery``. Note that this will not + clear negative thoughts that your dwarves accumulated "naturally". diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index df49c3d59..89733bf20 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -130,7 +130,7 @@ dfhack_plugin(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(luasocket luasocket.cpp LINK_LIBRARIES clsocket lua dfhack-tinythread) #dfhack_plugin(manipulator manipulator.cpp) #dfhack_plugin(map-render map-render.cpp LINK_LIBRARIES lua) -dfhack_plugin(misery misery.cpp) +dfhack_plugin(misery misery.cpp LINK_LIBRARIES lua) #dfhack_plugin(mode mode.cpp) #dfhack_plugin(mousequery mousequery.cpp) dfhack_plugin(nestboxes nestboxes.cpp) diff --git a/plugins/lua/misery.lua b/plugins/lua/misery.lua new file mode 100644 index 000000000..cd507c9b3 --- /dev/null +++ b/plugins/lua/misery.lua @@ -0,0 +1,43 @@ +local _ENV = mkmodule('plugins.misery') + +local argparse = require('argparse') + +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true + return + end + + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + }) +end + +function status() + print(('misery is %s'):format(isEnabled() and "enabled" or "disabled")) + print(('misery factor is: %d'):format(misery_getFactor())) +end + +function parse_commandline(...) + local args, opts = {...}, {} + local positionals = process_args(opts, args) + + if opts.help then + return false + end + + local command = table.remove(positionals, 1) + if not command or command == 'status' then + status() + elseif command == 'factor' then + misery_setFactor(positionals[1]) + elseif command == 'clear' then + misery_clear() + else + return false + end + + return true +end + +return _ENV diff --git a/plugins/misery.cpp b/plugins/misery.cpp index 870c4480c..f241394ae 100644 --- a/plugins/misery.cpp +++ b/plugins/misery.cpp @@ -1,14 +1,7 @@ #include -#include #include #include -#include "DataDefs.h" -#include "Export.h" -#include "PluginManager.h" - -#include "modules/Units.h" - #include "df/emotion_type.h" #include "df/plotinfost.h" #include "df/unit.h" @@ -17,179 +10,266 @@ #include "df/unit_thought_type.h" #include "df/world.h" -using namespace std; +#include "modules/Persistence.h" +#include "modules/Units.h" +#include "modules/World.h" + +#include "Core.h" +#include "Debug.h" +#include "LuaTools.h" +#include "PluginManager.h" + +using std::string; +using std::vector; + using namespace DFHack; DFHACK_PLUGIN("misery"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); -REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(cur_year); REQUIRE_GLOBAL(cur_year_tick); +REQUIRE_GLOBAL(world); -typedef df::unit_personality::T_emotions Emotion; - -static int factor = 1; -static int tick = 0; -const int INTERVAL = 1000; +namespace DFHack { + DBG_DECLARE(misery, cycle, DebugCategory::LINFO); + DBG_DECLARE(misery, config, DebugCategory::LINFO); +} -command_result misery(color_ostream& out, vector& parameters); -void add_misery(df::unit *unit); -void clear_misery(df::unit *unit); +static const string CONFIG_KEY = string(plugin_name) + "/config"; +static PersistentDataItem config; -const int FAKE_EMOTION_FLAG = (1 << 30); -const int STRENGTH_MULTIPLIER = 100; +enum ConfigValues { + CONFIG_IS_ENABLED = 0, + CONFIG_FACTOR = 1, +}; -bool is_valid_unit (df::unit *unit) { - if (!Units::isOwnRace(unit) || !Units::isOwnCiv(unit)) - return false; - if (!Units::isActive(unit)) - return false; - return true; +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) + return -1; + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; +} +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); } -inline bool is_fake_emotion (Emotion *e) { - return e->flags.whole & FAKE_EMOTION_FLAG; +static const int32_t CYCLE_TICKS = 1200; // one day +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle + +static command_result do_command(color_ostream &out, vector ¶meters); +static void do_cycle(color_ostream &out); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + DEBUG(config,out).print("initializing %s\n", plugin_name); + + // provide a configuration interface for the plugin + commands.push_back(PluginCommand( + plugin_name, + "Increase the intensity of negative dwarven thoughts.", + do_command)); + + return CR_OK; } -void add_misery (df::unit *unit) { - // Add a fake miserable thought - // Remove any fake ones that already exist - if (!unit || !unit->status.current_soul) - return; - clear_misery(unit); - auto &emotions = unit->status.current_soul->personality.emotions; - Emotion *e = new Emotion; - e->type = df::emotion_type::MISERY; - e->thought = df::unit_thought_type::SoapyBath; - e->flags.whole |= FAKE_EMOTION_FLAG; - emotions.push_back(e); +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } - for (Emotion *e : emotions) { - if (is_fake_emotion(e)) { - e->year = *cur_year; - e->year_tick = *cur_year_tick; - e->strength = STRENGTH_MULTIPLIER * factor; - e->severity = STRENGTH_MULTIPLIER * factor; - } + if (enable != is_enabled) { + is_enabled = enable; + DEBUG(config,out).print("%s from the API; persisting\n", + is_enabled ? "enabled" : "disabled"); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + if (enable) + do_cycle(out); + } else { + DEBUG(config,out).print("%s from the API, but already %s; no action\n", + is_enabled ? "enabled" : "disabled", + is_enabled ? "enabled" : "disabled"); } + return CR_OK; } -void clear_misery (df::unit *unit) { - if (!unit || !unit->status.current_soul) - return; - auto &emotions = unit->status.current_soul->personality.emotions; - auto it = remove_if(emotions.begin(), emotions.end(), [](Emotion *e) { - if (is_fake_emotion(e)) { - delete e; - return true; - } - return false; - }); - emotions.erase(it, emotions.end()); -} +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + DEBUG(config,out).print("shutting down %s\n", plugin_name); -DFhackCExport command_result plugin_shutdown(color_ostream& out) { - factor = 0; return CR_OK; } -DFhackCExport command_result plugin_onupdate(color_ostream& out) { - static bool wasLoaded = false; - if ( factor == 0 || !world || !world->map.block_index ) { - if ( wasLoaded ) { - //we just unloaded the game: clear all data - factor = 0; - is_enabled = false; - wasLoaded = false; - } - return CR_OK; - } +DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; + config = World::GetPersistentData(CONFIG_KEY); - if ( !wasLoaded ) { - wasLoaded = true; + if (!config.isValid()) { + DEBUG(config,out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + set_config_val(config, CONFIG_FACTOR, 2); } - if ( tick < INTERVAL ) { - tick++; - return CR_OK; - } - tick = 0; + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + DEBUG(config,out).print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); - //TODO: consider units.active - for (df::unit *unit : world->units.all) { - if (is_valid_unit(unit)) { - add_misery(unit); + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(config,out).print("world unloaded; disabling %s\n", + plugin_name); + is_enabled = false; } } - return CR_OK; } -DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { - commands.push_back(PluginCommand( - "misery", - "Increase the intensity of negative dwarven thoughts.", - misery)); +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) + do_cycle(out); return CR_OK; } -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if (enable != is_enabled) - { - is_enabled = enable; - factor = enable ? 1 : 0; - tick = INTERVAL; - } +static bool call_misery_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(config).print("calling misery lua function: '%s'\n", fn_name); - return CR_OK; + CoreSuspender guard; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!out) + out = &Core::getInstance().getConsole(); + + return Lua::CallLuaModuleFunction(*out, L, "plugins.misery", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); } -command_result misery(color_ostream &out, vector& parameters) { - if ( !world || !world->map.block_index ) { - out.printerr("misery can only be enabled in fortress mode with a fully-loaded game.\n"); +static command_result do_command(color_ostream &out, vector ¶meters) { + CoreSuspender suspend; + + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); return CR_FAILURE; } - if ( parameters.size() < 1 || parameters.size() > 2 ) { - return CR_WRONG_USAGE; + bool show_help = false; + if (!call_misery_lua(&out, "parse_commandline", parameters.size(), 1, + [&](lua_State *L) { + for (const string ¶m : parameters) + Lua::Push(L, param); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; } - if ( parameters[0] == "disable" ) { - if ( parameters.size() > 1 ) { - return CR_WRONG_USAGE; - } - factor = 0; - is_enabled = false; - return CR_OK; - } else if ( parameters[0] == "enable" ) { - is_enabled = true; - factor = 1; - if ( parameters.size() == 2 ) { - int a = atoi(parameters[1].c_str()); - if ( a < 1 ) { - out.printerr("Second argument must be a positive integer.\n"); - return CR_WRONG_USAGE; - } - factor = a; - } - tick = INTERVAL; - } else if ( parameters[0] == "clear" ) { - for (df::unit *unit : world->units.all) { - if (is_valid_unit(unit)) { - clear_misery(unit); - } - } - } else { - int a = atoi(parameters[0].c_str()); - if ( a < 0 ) { - return CR_WRONG_USAGE; + return show_help ? CR_WRONG_USAGE : CR_OK; +} + +///////////////////////////////////////////////////// +// cycle logic +// + +const int FAKE_EMOTION_FLAG = (1 << 30); +const int STRENGTH_MULTIPLIER = 100; + +typedef df::unit_personality::T_emotions Emotion; + +static bool is_fake_emotion(Emotion *e) { + return e->flags.whole & FAKE_EMOTION_FLAG; +} + +static void clear_misery(df::unit *unit) { + if (!unit || !unit->status.current_soul) + return; + auto &emotions = unit->status.current_soul->personality.emotions; + auto it = std::remove_if(emotions.begin(), emotions.end(), [](Emotion *e) { + if (is_fake_emotion(e)) { + delete e; + return true; } - factor = a; - is_enabled = factor > 0; + return false; + }); + emotions.erase(it, emotions.end()); +} +// clears fake negative thoughts then runs the given lambda +static void affect_units( + std::function &&process_unit = [](df::unit *){}) { + for (auto unit : world->units.active) { + if (!Units::isCitizen(unit) || !unit->status.current_soul) + continue; + + clear_misery(unit); + std::forward &&>(process_unit)(unit); } +} - return CR_OK; +static void do_cycle(color_ostream &out) { + // mark that we have recently run + cycle_timestamp = world->frame_counter; + + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); + + int strength = STRENGTH_MULTIPLIER * get_config_val(config, CONFIG_FACTOR); + + affect_units([&](df::unit *unit) { + Emotion *e = new Emotion; + e->type = df::emotion_type::MISERY; + e->thought = df::unit_thought_type::SoapyBath; + e->flags.whole |= FAKE_EMOTION_FLAG; + e->year = *cur_year; + e->year_tick = *cur_year_tick; + e->strength = strength; + e->severity = strength; + unit->status.current_soul->personality.emotions.push_back(e); + }); } + +///////////////////////////////////////////////////// +// Lua API +// + +static void misery_clear(color_ostream &out) { + DEBUG(config,out).print("entering misery_clear\n"); + affect_units(); +} + +static void misery_setFactor(color_ostream &out, int32_t factor) { + DEBUG(config,out).print("entering misery_setFactor\n"); + if (1 >= factor) { + out.printerr("factor must be at least 2\n"); + return; + } + set_config_val(config, CONFIG_FACTOR, factor); + if (is_enabled) + do_cycle(out); +} + +static int misery_getFactor(color_ostream &out) { + DEBUG(config,out).print("entering tailor_getFactor\n"); + return get_config_val(config, CONFIG_FACTOR); +} + +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(misery_clear), + DFHACK_LUA_FUNCTION(misery_setFactor), + DFHACK_LUA_FUNCTION(misery_getFactor), + DFHACK_LUA_END +}; From 6a04b577b0140eec1d514a3c3d6a0f94c6a7338b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 14:03:22 -0800 Subject: [PATCH 24/29] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 18635ca2b..ad8763899 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `automelt`: is now more resistent to savegame corruption - `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) +- `misery`: now persists state with the fort - `autodump`: reinstate ``autodump-destroy-item``, hotkey: Ctrl-K - `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H - `dig`: new hotkeys for vein designation on z-level (Ctrl-V) and vein designation across z-levels (Ctrl-Shift-V) From 5c84d180011c06f4f4f36032f023984953aa0044 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 18:38:16 -0800 Subject: [PATCH 25/29] update tailor, persist state, use best practices --- docs/plugins/tailor.rst | 28 ++- plugins/CMakeLists.txt | 2 +- plugins/lua/tailor.lua | 56 +++++ plugins/tailor.cpp | 527 +++++++++++++++++++++------------------- 4 files changed, 350 insertions(+), 263 deletions(-) create mode 100644 plugins/lua/tailor.lua diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index 4dc4f53a4..0e8980948 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -5,16 +5,15 @@ tailor :summary: Automatically keep your dwarves in fresh clothing. :tags: fort auto workorders -Whenever the bookkeeper updates stockpile records, this plugin will scan the -fort. If there are fresh cloths available, dwarves who are wearing tattered -clothing will have their rags confiscated (in the same manner as the -`cleanowned` tool) so that they'll reequip with replacement clothes. +Once a day, this plugin will scan the clothing situation in the fort. If there +are fresh cloths available, dwarves who are wearing tattered clothing will have +their rags confiscated (in the same manner as the `cleanowned` tool) so that +they'll reequip with replacement clothes. -If there are not enough clothes available, manager orders will be generated -to manufacture some more. ``tailor`` will intelligently create orders using -raw materials that you have on hand in the fort. For example, if you have -lots of silk, but no cloth, then ``tailor`` will order only silk clothing to -be made. +If there are not enough clothes available, manager orders will be generated to +manufacture some more. ``tailor`` will intelligently create orders using raw +materials that you have on hand in the fort. For example, if you have lots of +silk, but no cloth, then ``tailor`` will order only silk clothing to be made. Usage ----- @@ -22,7 +21,8 @@ Usage :: enable tailor - tailor status + tailor [status] + tailor now tailor materials [ ...] By default, ``tailor`` will prefer using materials in this order:: @@ -32,12 +32,16 @@ By default, ``tailor`` will prefer using materials in this order:: but you can use the ``tailor materials`` command to restrict which materials are used, and in what order. -Example -------- +Examples +-------- ``enable tailor`` Start replacing tattered clothes with default settings. +``tailor now`` + Run a scan and order cycle right now, regardless of whether the plugin is + enabled. + ``tailor materials silk cloth yarn`` Restrict the materials used for automatically manufacturing clothing to silk, cloth, and yarn, preferred in that order. This saves leather for diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index df49c3d59..7e8258aeb 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -159,7 +159,7 @@ dfhack_plugin(showmood showmood.cpp) #add_subdirectory(stockpiles) #dfhack_plugin(stocks stocks.cpp) #dfhack_plugin(strangemood strangemood.cpp) -dfhack_plugin(tailor tailor.cpp) +dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(title-folder title-folder.cpp) #dfhack_plugin(title-version title-version.cpp) diff --git a/plugins/lua/tailor.lua b/plugins/lua/tailor.lua new file mode 100644 index 000000000..5c748fbfa --- /dev/null +++ b/plugins/lua/tailor.lua @@ -0,0 +1,56 @@ +local _ENV = mkmodule('plugins.tailor') + +local argparse = require('argparse') +local utils = require('utils') + +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true + return + end + + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + }) +end + +function status() + print(('tailor is %s'):format(enabled and "enabled" or "disabled")) + print('materials preference order:') + for _,name in ipairs(tailor_getMaterialPreferences()) do + print((' %s'):format(name)) + end +end + +function setMaterials(names) + local idxs = utils.invert(names) + tailor_setMaterialPreferences( + idxs.silk or -1, + idxs.cloth or -1, + idxs.yarn or -1, + idxs.leather or -1) +end + +function parse_commandline(...) + local args, opts = {...}, {} + local positionals = process_args(opts, args) + + if opts.help then + return false + end + + local command = table.remove(positionals, 1) + if not command or command == 'status' then + status() + elseif command == 'now' then + tailor_doCycle() + elseif command == 'materials' then + setMaterials(positionals) + else + return false + end + + return true +end + +return _ENV diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 2cca8a2c8..b35f434b8 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -1,136 +1,161 @@ /* * Tailor plugin. Automatically manages keeping your dorfs clothed. - * For best effect, place "tailor enable" in your dfhack.init configuration, - * or set AUTOENABLE to true. */ -#include "Core.h" -#include "DataDefs.h" -#include "Debug.h" -#include "PluginManager.h" +#include +#include +#include #include "df/creature_raw.h" -#include "df/global_objects.h" #include "df/historical_entity.h" +#include "df/item.h" +#include "df/item_flags.h" #include "df/itemdef_armorst.h" #include "df/itemdef_glovesst.h" #include "df/itemdef_helmst.h" #include "df/itemdef_pantsst.h" #include "df/itemdef_shoesst.h" #include "df/items_other_id.h" -#include "df/job.h" -#include "df/job_type.h" #include "df/manager_order.h" #include "df/plotinfost.h" #include "df/world.h" -#include "modules/Maps.h" -#include "modules/Units.h" +#include "Core.h" +#include "Debug.h" +#include "LuaTools.h" +#include "PluginManager.h" + +#include "modules/Materials.h" +#include "modules/Persistence.h" #include "modules/Translation.h" +#include "modules/Units.h" #include "modules/World.h" -using namespace DFHack; +using std::string; +using std::vector; -using df::global::world; -using df::global::plotinfo; +using namespace DFHack; DFHACK_PLUGIN("tailor"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); -#define AUTOENABLE false -DFHACK_PLUGIN_IS_ENABLED(enabled); - -REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(standing_orders_use_dyed_cloth); +REQUIRE_GLOBAL(world); namespace DFHack { DBG_DECLARE(tailor, cycle, DebugCategory::LINFO); DBG_DECLARE(tailor, config, DebugCategory::LINFO); } -class Tailor { - // ARMOR, SHOES, HELM, GLOVES, PANTS +static const string CONFIG_KEY = string(plugin_name) + "/config"; +static PersistentDataItem config; - // ah, if only STL had a bimap +enum ConfigValues { + CONFIG_IS_ENABLED = 0, + CONFIG_SILK_IDX = 1, + CONFIG_CLOTH_IDX = 2, + CONFIG_YARN_IDX = 3, + CONFIG_LEATHER_IDX = 4, +}; -private: +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) + return -1; + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; +} +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); +} - const std::map jobTypeMap = { - { df::job_type::MakeArmor, df::item_type::ARMOR }, - { df::job_type::MakePants, df::item_type::PANTS }, - { df::job_type::MakeHelm, df::item_type::HELM }, - { df::job_type::MakeGloves, df::item_type::GLOVES }, - { df::job_type::MakeShoes, df::item_type::SHOES } - }; - - const std::map itemTypeMap = { - { df::item_type::ARMOR, df::job_type::MakeArmor }, - { df::item_type::PANTS, df::job_type::MakePants }, - { df::item_type::HELM, df::job_type::MakeHelm }, - { df::item_type::GLOVES, df::job_type::MakeGloves }, - { df::item_type::SHOES, df::job_type::MakeShoes } - }; - -#define F(x) df::item_flags::mask_##x - const df::item_flags bad_flags = { - ( - F(dump) | F(forbid) | F(garbage_collect) | - F(hostile) | F(on_fire) | F(rotten) | F(trader) | - F(in_building) | F(construction) | F(owned) - ) - #undef F - }; - - class MatType { - - public: - std::string name; - df::job_material_category job_material; - df::armor_general_flags armor_flag; - - bool operator==(const MatType& m) const - { - return name == m.name; - } +static const int32_t CYCLE_TICKS = 1200; // one day +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle - // operator< is required to use this as a std::map key - bool operator<(const MatType& m) const - { - return name < m.name; - } +// ah, if only STL had a bimap +static const std::map jobTypeMap = { + { df::job_type::MakeArmor, df::item_type::ARMOR }, + { df::job_type::MakePants, df::item_type::PANTS }, + { df::job_type::MakeHelm, df::item_type::HELM }, + { df::job_type::MakeGloves, df::item_type::GLOVES }, + { df::job_type::MakeShoes, df::item_type::SHOES } +}; - MatType(std::string& n, df::job_material_category jm, df::armor_general_flags af) - : name(n), job_material(jm), armor_flag(af) {}; - MatType(const char* n, df::job_material_category jm, df::armor_general_flags af) - : name(std::string(n)), job_material(jm), armor_flag(af) {}; +static const std::map itemTypeMap = { + { df::item_type::ARMOR, df::job_type::MakeArmor }, + { df::item_type::PANTS, df::job_type::MakePants }, + { df::item_type::HELM, df::job_type::MakeHelm }, + { df::item_type::GLOVES, df::job_type::MakeGloves }, + { df::item_type::SHOES, df::job_type::MakeShoes } +}; + +class MatType { +public: + const std::string name; + const df::job_material_category job_material; + const df::armor_general_flags armor_flag; + + bool operator==(const MatType& m) const { + return name == m.name; + } - }; + // operator< is required to use this as a std::map key + bool operator<(const MatType& m) const { + return name < m.name; + } - 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); + MatType(std::string& n, df::job_material_category jm, df::armor_general_flags af) + : name(n), job_material(jm), armor_flag(af) {}; + MatType(const char* n, df::job_material_category jm, df::armor_general_flags af) + : name(std::string(n)), job_material(jm), armor_flag(af) {}; +}; - std::list all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; +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); + +static const std::list all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; +static std::list material_order = all_materials; + +static struct BadFlags { + uint32_t whole; + + BadFlags() { + df::item_flags flags; + #define F(x) flags.bits.x = true; + F(dump); F(forbid); F(garbage_collect); + F(hostile); F(on_fire); F(rotten); F(trader); + F(in_building); F(construction); F(owned); + F(in_chest); F(removed); F(encased); + F(spider_web); + #undef F + whole = flags.whole; + } +} badFlags; +class Tailor { +private: std::map, int> available; // key is item type & size std::map, int> needed; // same std::map, int> queued; // same std::map sizes; // this maps body size to races - std::map, int> orders; // key is item type, item subtype, size std::map supply; - - color_ostream* out; - - std::list material_order = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; std::map reserves; int default_reserve = 10; +public: void reset() { available.clear(); @@ -145,9 +170,7 @@ private: { for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing" { - if (i->flags.whole & bad_flags.whole) - continue; - if (i->flags.bits.owned) + if (i->flags.whole & badFlags.whole) continue; if (i->getWear() >= 1) continue; @@ -164,7 +187,7 @@ private: for (auto i : world->items.other[df::items_other_id::CLOTH]) { - if (i->flags.whole & bad_flags.whole) + if (i->flags.whole & badFlags.whole) continue; if (require_dyed && !i->hasImprovements()) @@ -197,7 +220,7 @@ private: for (auto i : world->items.other[df::items_other_id::SKIN_TANNED]) { - if (i->flags.whole & bad_flags.whole) + if (i->flags.whole & badFlags.whole) continue; supply[M_LEATHER] += i->getStackSize(); } @@ -369,8 +392,9 @@ private: } - void place_orders() + int place_orders() { + int ordered = 0; auto entity = world->entities.all[plotinfo->civ_id]; for (auto& o : orders) @@ -477,6 +501,7 @@ private: ); count -= c; + ordered += c; } else { @@ -486,215 +511,217 @@ private: } } } + return ordered; } +}; -public: - void do_scan(color_ostream& o) - { - out = &o; - - reset(); - - // scan for useable clothing +static std::unique_ptr tailor_instance; - scan_clothing(); +static command_result do_command(color_ostream &out, vector ¶meters); +static int do_cycle(color_ostream &out); - // scan for clothing raw materials +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + DEBUG(config,out).print("initializing %s\n", plugin_name); - scan_materials(); + tailor_instance = dts::make_unique(); - // scan for units who need replacement clothing + // provide a configuration interface for the plugin + commands.push_back(PluginCommand( + plugin_name, + "Automatically keep your dwarves in fresh clothing.", + do_command)); - scan_replacements(); + return CR_OK; +} - // create new orders +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } - create_orders(); + if (enable != is_enabled) { + is_enabled = enable; + DEBUG(config,out).print("%s from the API; persisting\n", + is_enabled ? "enabled" : "disabled"); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + if (enable) + do_cycle(out); + } else { + DEBUG(config,out).print("%s from the API, but already %s; no action\n", + is_enabled ? "enabled" : "disabled", + is_enabled ? "enabled" : "disabled"); + } + return CR_OK; +} - // scan existing orders and subtract +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + DEBUG(config,out).print("shutting down %s\n", plugin_name); - scan_existing_orders(); + tailor_instance.release(); - // place orders + return CR_OK; +} - place_orders(); +static void set_material_order() { + material_order.clear(); + for (int i = 0; i < all_materials.size(); ++i) { + if (i == get_config_val(config, CONFIG_SILK_IDX)) + material_order.push_back(M_SILK); + else if (i == get_config_val(config, CONFIG_CLOTH_IDX)) + material_order.push_back(M_CLOTH); + else if (i == get_config_val(config, CONFIG_YARN_IDX)) + material_order.push_back(M_YARN); + else if (i == get_config_val(config, CONFIG_LEATHER_IDX)) + material_order.push_back(M_LEATHER); } + if (!material_order.size()) + std::copy(all_materials.begin(), all_materials.end(), std::back_inserter(material_order)); +} -public: - command_result set_materials(color_ostream& out, std::vector& parameters) - { - std::list newmat; - newmat.clear(); - - for (auto m = parameters.begin() + 1; m != parameters.end(); m++) - { - auto nameMatch = [m](MatType& m1) { return *m == m1.name; }; - auto mm = std::find_if(all_materials.begin(), all_materials.end(), nameMatch); - if (mm == all_materials.end()) - { - WARN(config,out).print("tailor: material %s not recognized\n", m->c_str()); - return CR_WRONG_USAGE; - } - else { - newmat.push_back(*mm); - } - } - - material_order = newmat; - INFO(config,out).print("tailor: material list set to %s\n", get_material_list().c_str()); +DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; + config = World::GetPersistentData(CONFIG_KEY); - return CR_OK; + if (!config.isValid()) { + DEBUG(config,out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); } -public: - std::string get_material_list() - { - std::string s; - for (const auto& m : material_order) - { - if (!s.empty()) s += ", "; - s += m.name; - } - return s; - } + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + DEBUG(config,out).print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); + set_material_order(); -public: - void process(color_ostream& out) - { - bool found = false; + return CR_OK; +} - for (df::job_list_link* link = &world->jobs.list; link != NULL; link = link->next) - { - if (link->item == NULL) continue; - if (link->item->job_type == df::enums::job_type::UpdateStockpileRecords) - { - found = true; - break; - } +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(config,out).print("world unloaded; disabling %s\n", + plugin_name); + is_enabled = false; } + } + return CR_OK; +} - if (found) - { - do_scan(out); - } +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { + int ordered = do_cycle(out); + if (0 < ordered) + out.print("tailor: ordered %d items of clothing\n", ordered); } -}; + return CR_OK; +} -static std::unique_ptr tailor_instance; +static bool call_tailor_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(config).print("calling tailor lua function: '%s'\n", fn_name); -#define DELTA_TICKS 50 + CoreSuspender guard; -DFhackCExport command_result plugin_onupdate(color_ostream& out) -{ - if (!enabled || !tailor_instance) - return CR_OK; + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); - if (!Maps::IsValid()) - return CR_OK; + if (!out) + out = &Core::getInstance().getConsole(); - if (DFHack::World::ReadPauseState()) - return CR_OK; + return Lua::CallLuaModuleFunction(*out, L, "plugins.tailor", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); +} - if (world->frame_counter % DELTA_TICKS != 0) - return CR_OK; +static command_result do_command(color_ostream &out, vector ¶meters) { + CoreSuspender suspend; - { - CoreSuspender suspend; - tailor_instance->process(out); + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); + return CR_FAILURE; } - return CR_OK; + bool show_help = false; + if (!call_tailor_lua(&out, "parse_commandline", parameters.size(), 1, + [&](lua_State *L) { + for (const string ¶m : parameters) + Lua::Push(L, param); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; + } + + return show_help ? CR_WRONG_USAGE : CR_OK; } -static command_result tailor_cmd(color_ostream& out, std::vector & parameters) { - bool desired = enabled; - if (parameters.size() == 1 && (parameters[0] == "enable" || parameters[0] == "on" || parameters[0] == "1")) - { - desired = true; - } - else if (parameters.size() == 1 && (parameters[0] == "disable" || parameters[0] == "off" || parameters[0] == "0")) - { - desired = false; - } - else if (parameters.size() == 1 && (parameters[0] == "usage" || parameters[0] == "help" || parameters[0] == "?")) - { - return CR_WRONG_USAGE; - } - else if (parameters.size() == 1 && parameters[0] == "test") - { - if (tailor_instance) - { - tailor_instance->do_scan(out); - return CR_OK; - } - else - { - out.print("%s: not instantiated\n", plugin_name); - return CR_FAILURE; - } - } - else if (parameters.size() > 1 && parameters[0] == "materials") - { - if (tailor_instance) - { - return tailor_instance->set_materials(out, parameters); - } - else - { - out.print("%s: not instantiated\n", plugin_name); - return CR_FAILURE; - } - } - else if (parameters.size() == 1 && parameters[0] != "status") - { - return CR_WRONG_USAGE; - } +///////////////////////////////////////////////////// +// cycle logic +// - out.print("Tailor is %s %s.\n", (desired == enabled) ? "currently" : "now", desired ? "enabled" : "disabled"); - if (tailor_instance) - { - out.print("Material list is: %s\n", tailor_instance->get_material_list().c_str()); - } - else - { - out.print("%s: not instantiated\n", plugin_name); - } +static int do_cycle(color_ostream &out) { + // mark that we have recently run + cycle_timestamp = world->frame_counter; - enabled = desired; + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); - return CR_OK; + tailor_instance->reset(); + tailor_instance->scan_clothing(); + tailor_instance->scan_materials(); + tailor_instance->scan_replacements(); + tailor_instance->create_orders(); + tailor_instance->scan_existing_orders(); + return tailor_instance->place_orders(); } +///////////////////////////////////////////////////// +// Lua API +// -DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event event) -{ - return CR_OK; +static void tailor_doCycle(color_ostream &out) { + DEBUG(config,out).print("entering tailor_doCycle\n"); + out.print("ordered %d items of clothing\n", do_cycle(out)); } -DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) -{ - enabled = enable; - return CR_OK; -} +// 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) { + DEBUG(config,out).print("entering tailor_setMaterialPreferences\n"); -DFhackCExport command_result plugin_init(color_ostream& out, std::vector & commands) -{ - tailor_instance = std::move(dts::make_unique()); + // it doesn't really matter if these are invalid. set_material_order will do + // the right thing. + set_config_val(config, CONFIG_SILK_IDX, silkIdx); + set_config_val(config, CONFIG_CLOTH_IDX, clothIdx); + set_config_val(config, CONFIG_YARN_IDX, yarnIdx); + set_config_val(config, CONFIG_LEATHER_IDX, leatherIdx); - if (AUTOENABLE) { - enabled = true; - } + set_material_order(); +} - commands.push_back(PluginCommand( - plugin_name, - "Automatically keep your dwarves in fresh clothing.", - tailor_cmd)); - return CR_OK; +static int tailor_getMaterialPreferences(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(config,*out).print("entering tailor_getMaterialPreferences\n"); + vector names; + for (const auto& m : material_order) + names.emplace_back(m.name); + Lua::PushVector(L, names); + return 1; } -DFhackCExport command_result plugin_shutdown(color_ostream& out) -{ - tailor_instance.release(); +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(tailor_doCycle), + DFHACK_LUA_FUNCTION(tailor_setMaterialPreferences), + DFHACK_LUA_END +}; - return plugin_enable(out, false); -} +DFHACK_PLUGIN_LUA_COMMANDS { + DFHACK_LUA_COMMAND(tailor_getMaterialPreferences), + DFHACK_LUA_END +}; From 4d4e94c44993b7ee3609a008267d0f6a6a1012eb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 14:01:38 -0800 Subject: [PATCH 26/29] convert index --- plugins/lua/tailor.lua | 2 +- plugins/tailor.cpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/lua/tailor.lua b/plugins/lua/tailor.lua index 5c748fbfa..bcfd8bc10 100644 --- a/plugins/lua/tailor.lua +++ b/plugins/lua/tailor.lua @@ -15,7 +15,7 @@ local function process_args(opts, args) end function status() - print(('tailor is %s'):format(enabled and "enabled" or "disabled")) + print(('tailor is %s'):format(isEnabled() and "enabled" or "disabled")) print('materials preference order:') for _,name in ipairs(tailor_getMaterialPreferences()) do print((' %s'):format(name)) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index b35f434b8..8b132397a 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -3,7 +3,6 @@ */ #include -#include #include #include "df/creature_raw.h" @@ -695,10 +694,10 @@ static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx, // it doesn't really matter if these are invalid. set_material_order will do // the right thing. - set_config_val(config, CONFIG_SILK_IDX, silkIdx); - set_config_val(config, CONFIG_CLOTH_IDX, clothIdx); - set_config_val(config, CONFIG_YARN_IDX, yarnIdx); - set_config_val(config, CONFIG_LEATHER_IDX, leatherIdx); + set_config_val(config, CONFIG_SILK_IDX, silkIdx - 1); + 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_material_order(); } From 8bd985359702e8cb388d09841b4b5b11eefbcdab Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 14:16:11 -0800 Subject: [PATCH 27/29] make gcc happy --- library/xml | 2 +- plugins/tailor.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/xml b/library/xml index cd5baf4ea..eb58cda0f 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit cd5baf4ea82fead3883368da526ac2b606a6209b +Subproject commit eb58cda0f106c20776c273516f010ce21c45f89d diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 8b132397a..5511e2b5d 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -564,14 +564,14 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { static void set_material_order() { material_order.clear(); - for (int i = 0; i < all_materials.size(); ++i) { - if (i == get_config_val(config, CONFIG_SILK_IDX)) + for (size_t i = 0; i < all_materials.size(); ++i) { + if (i == (size_t)get_config_val(config, CONFIG_SILK_IDX)) material_order.push_back(M_SILK); - else if (i == get_config_val(config, CONFIG_CLOTH_IDX)) + else if (i == (size_t)get_config_val(config, CONFIG_CLOTH_IDX)) material_order.push_back(M_CLOTH); - else if (i == get_config_val(config, CONFIG_YARN_IDX)) + else if (i == (size_t)get_config_val(config, CONFIG_YARN_IDX)) material_order.push_back(M_YARN); - else if (i == get_config_val(config, CONFIG_LEATHER_IDX)) + else if (i == (size_t)get_config_val(config, CONFIG_LEATHER_IDX)) material_order.push_back(M_LEATHER); } if (!material_order.size()) From 252a05ba11c51636c27d14b45f35146b0e910c73 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 9 Feb 2023 00:02:46 +0000 Subject: [PATCH 28/29] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index eb58cda0f..eff493010 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit eb58cda0f106c20776c273516f010ce21c45f89d +Subproject commit eff493010d11358fc8243239dbf8d07024eedb0c diff --git a/scripts b/scripts index 54f5d5f3d..8807e7a78 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 54f5d5f3d3b68b07323e88109b9a44d98abe52b1 +Subproject commit 8807e7a7845611b2cc613fb3903f6f8763e91300 From 1fd807fc4d657ebfff30c658260014ed99937a4d Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 8 Feb 2023 22:16:16 -0500 Subject: [PATCH 29/29] Mark as dev-only changes: #2848, #2842, #2816, #2792 --- docs/changelog.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ad8763899..c151e2ebc 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,12 +40,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Fix right click sometimes closing both a DFHack window and a vanilla panel - Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible - `channel-safely`: fixed bug resulting in marker mode never being set for any designation -- `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs -- `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs +-@ `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs +-@ `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs ## Misc Improvements - `automelt`: is now more resistent to savegame corruption -- `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) +-@ `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) - `misery`: now persists state with the fort - `autodump`: reinstate ``autodump-destroy-item``, hotkey: Ctrl-K - `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H @@ -53,7 +53,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `clean`: new hotkey for `spotclean`: Ctrl-C - `autobutcher`: changed defaults from 5 females / 1 male to 4 females / 2 males so a single unfortunate accident doesn't leave players without a mating pair - `autobutcher`: now immediately loads races available at game start into the watchlist -- replaced DFHack logo used for the hover hotspot with a crisper image +-@ replaced DFHack logo used for the hover hotspot with a crisper image - `orders`: recipe for silver crossbows removed from ``library/military`` as it is not a vanilla recipe, but is available in ``library/military_include_artifact_materials`` ## Documentation @@ -72,7 +72,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # 50.05-alpha3.1 ## Fixes -- `seedwatch`: fix parameter parsing when setting targets +-@ `seedwatch`: fix parameter parsing when setting targets # 50.05-alpha3