A little LUA console script for dfhack (versions 0.43.x-0.44.12). Used to list, help locate and optionally log most valuable items on map. Works with every mode (i.e. arena, fort and adventurer), but some functionality may be missing depending on mode. Should be placed in the scripts directory, e.g.
\DwarfFortress\hack\scripts\list-valuables.lua--lists and optionally logs most valuable items
---------------CONFIGURATION---------------
MULTICOLOR = true -- change to false to prevent use of different colours
SHOW_OWNERSHIP = true -- checks ownership (apart from trader's, which is always checked), items not owned by fortress are red
SHOW_STATE = true --shows another line with more info, if item is in building, or inventory, or owned by someone etc., implies enabled SHOW_OWNERSHIP
SHOW_FORBID = true --shows curly brackets around forbidden item
SHOW_TOTAL = false --change to true to show number of items shown and their total value
DEEP_CHECK = true --checks item presence on map in non-dwarf modes, slow if there are many items
------------END-OF-CONFIGURATION-----------
local help = [====[
list-valuables
========
Shows a list of most valuable items on a map.
``list-valuables -show N`` shows N most valuable items instead of 10
``list-valuables -state`` shows state if not shown, or hides if shown
``list-valuables -hide N`` hides/unhides Nth item to help localize it
``list-valuables -filter NAME`` shows items containing NAME only
``list-valuables -total`` shows number and total value of items
``list-valuables -log`` logs the list to a file in main DF directory
]====]
local fval_tmp,fval_item,fval_posx,fval_posy,fval_posz,my_file=nil,nil,nil,nil,nil,nil
local tmp_item_list,tmp_str,form_str,fnum_str,state_t,TOP_N,hide_N,h_str,restricted,offmap,inv,nofilter,items_n,total_v={},"",nil,nil,{},10,nil,"",false,false,false,true,0,0
local mapx,mapy,mapz=dfhack.maps.getTileSize()
local utils = require 'utils'
kon = dfhack.df2console
kol = dfhack.color
validArgs = validArgs or utils.invert({
'help',
'show',
'state',
'log',
'hide',
'filter',
'total'
})
function list_table (input_table)
local my_str=table.concat(input_table,", ")
local my_tmp=nil
if my_str then
my_tmp=string.find(my_str,",[^,]*$")
if my_tmp then
my_str=table.concat{my_str:sub(1,my_tmp-1), " and", my_str:sub(my_tmp+1)}
end
my_str=my_str.."."
return my_str
end
return nil
end
function on_map(my_x,my_y,my_z,my_id,my_g)
if my_x<0 or my_y<0 then return nil end
if my_x>mapx-1 or my_y>mapy-1 then return nil end
if df.global.gamemode~=0 and DEEP_CHECK and my_g then
local my_block=dfhack.maps.getTileBlock(my_x,my_y,my_z)
if my_block then for k,v in ipairs(my_block.items) do if v==my_id then return true end end end
return nil
end
return true
end
function do_nothing (col)
end
-- Main --
local args = utils.processArgs({...}, validArgs)
if args.help then
print(help)
return
end
if not MULTICOLOR then kol = do_nothing end
if args.show then
if #args.show==0 then TOP_N=10
else
TOP_N=tonumber(args.show)
if not TOP_N then TOP_N=10 end
if TOP_N<1 then TOP_N=1 end
end
else
TOP_N=10
end
TOP_N=math.floor(TOP_N)
if args.hide then
if #args.hide>0 then
hide_N=tonumber(args.hide)
if hide_N and hide_N>=1 then hide_N=math.floor(hide_N) else hide_N=nil end
else
hide_N=nil
end
end
if args.filter then
if #args.filter>0 then nofilter=false end
end
if args.state then
if SHOW_STATE then SHOW_STATE=false
else SHOW_STATE=true end
end
for k,v in ipairs(df.global.world.items.all) do
fval_posx,fval_posy,fval_posz=dfhack.items.getPosition(v)
if fval_posx and on_map(fval_posx,fval_posy,fval_posz,v.id,v.flags.on_ground) then
fval_tmp=dfhack.items.getValue(v)
tmp_item_list[k]=fval_tmp
end
end
function getKeysSortedByValue(tbl, sortFunction)
local keys = {}
for key in pairs(tbl) do
table.insert(keys, key)
end
table.sort(keys, function(a, b) return sortFunction(tbl[a], tbl[b]) end)
return keys
end
local sortedKeys = getKeysSortedByValue(tmp_item_list, function(a, b) return a > b end)
if sortedKeys and TOP_N>#sortedKeys then TOP_N=#sortedKeys end
local tpn=tostring(TOP_N) tpn=#tpn tpn=tostring(tpn) tpn="%"..tpn.."d"
form_str=tpn.." %s (%s%s) at x=%s y=%s z=%s%s"
if args.log then
if df.global.world.world_data then
if #df.global.world.world_data.active_site>0 then
my_fortA=dfhack.TranslateName(df.global.world.world_data.active_site[0].name,true)
my_fortB=dfhack.TranslateName(df.global.world.world_data.active_site[0].name,false)
else
my_fortA=df.game_mode[df.global.gamemode] my_fortB=df.game_type[df.global.gametype]
end
my_filename=my_fortA.."_valuables_"..df.global.cur_year.."_"..df.global.cur_year_tick..".txt"
my_file = assert(io.open(my_filename, "w"))
if my_file then print(string.format("Logging to %s",my_filename))
my_file:write("Valuables of "..my_fortA.."/"..my_fortB)
if not nofilter then my_file:write(" (FILTER: "..args.filter..")") end
end
end
end
for i, key in ipairs(sortedKeys) do
if i<=TOP_N then
tmp_str="" restricted=false state_t={}
fval_item=df.global.world.items.all[key]
tmp_str=dfhack.items.getDescription(fval_item,0,true)
if SHOW_FORBID and tmp_str and fval_item.flags.forbid then tmp_str="{"..tmp_str.."}" end
if nofilter or string.match(kon(tmp_str),args.filter) then
fval_tmp=dfhack.items.getValue(fval_item)
items_n=items_n+1 total_v=total_v+fval_tmp
fval_posx,fval_posy,fval_posz=dfhack.items.getPosition(fval_item)
if fval_item.flags.trader then restricted=true table.insert(state_t,"owned by trader")end
if fval_item.flags.in_inventory then inv=true else inv=false end
if SHOW_OWNERSHIP or SHOW_STATE then offmap=false
for ii,vv in ipairs(fval_item.general_refs) do
if df.general_ref_unit_holderst:is_instance(vv) then offmap=true if inv then table.insert(state_t,"in inventory (unit)") inv=false end
for iii,vvv in ipairs(df.global.world.units.active) do
if vvv.id==vv.unit_id then offmap=false
if vvv.flags2.visitor or vvv.flags2.visitor_uninvited or vvv.flags1.forest or vvv.flags1.merchant or vvv.flags1.diplomat then restricted=true table.insert(state_t,"held by visitor") end
end
end
end
end
if inv then table.insert(state_t,"in inventory (container)") end
end
if hide_N and hide_N==i then if fval_item.flags.hidden then fval_item.flags.hidden=false else fval_item.flags.hidden=true end end
if fval_item.flags.hidden then h_str=" H" else h_str="" end
if restricted then kol(COLOR_RED) elseif offmap then kol(COLOR_YELLOW) else kol(nil) end
print(string.format(form_str,i,kon(tmp_str),fval_tmp,kon(string.char(15)),fval_posx,fval_posy,fval_posz,h_str))
if my_file then my_file:write(string.format("\n"..form_str,i,kon(tmp_str),fval_tmp,kon(string.char(164)),fval_posx,fval_posy,fval_posz,h_str)) end
if SHOW_STATE then
if fval_item.flags.in_building then table.insert(state_t,"in a building") end
if fval_item.flags.owned and not fval_item.flags.trader then table.insert(state_t,"owned") end
if fval_item.flags.owned and fval_item.flags.trader then table.insert(state_t,"not for sale") end
if offmap then table.insert(state_t,"off-map") end
if fval_item.flags2[3] then table.insert(state_t,"in location") end
if fval_item.flags.in_chest then table.insert(state_t,"stored") end
if fval_item.flags.in_job then table.insert(state_t,"in JOB") end
kol(COLOR_DARKGREY)
if #state_t>0 then
dfhack.print(string.format(tpn,i)) print(string.format("->Item %s",list_table(state_t)))
if my_file then
my_file:write(string.format("\n"..tpn,i))
my_file:write(string.format("->Item %s",list_table(state_t)))
end
end
end
end
end
end
if SHOW_TOTAL or args.total then kol(nil)
print(string.format("Items shown: %s (total value: %s%s)",items_n,total_v,kon(string.char(15))))
if my_file then my_file:write(string.format("\nItems shown: %s (total value: %s%s)",items_n,total_v,kon(string.char(164)))) end
end
if my_file then my_file:close() end
kol(nil)
It was made specifically to help with selecting bait items for kobold traps, but it can be used for some other tasks, like saving a list of all items to a file (sorted according to value), or total value of some specific items. I use it regularly to check value of roasts stored in trading depot for example.
It can be configured both with line options and with in-script options (part of script near beginning which is named "CONFIGURATION"). Several line options can be used at once.
Without options, it simply lists 10 most valuable items. Example:
1 @<@adamantine chain leggings@>@ (1234800¤) at x=116 y=8 z=170
1->Item in a building.
2 {@<@segmented adamantine breastplate@>@} (944400¤) at x=133 y=56 z=159
3 @<@iron nadak@>@ (822000¤) at x=139 y=89 z=166
3->Item in a building and in location.
4 {@<@giant cave spider silk coat@>@} (760760¤) at x=132 y=59 z=159
5 @<@iron mini-forge@>@ (379200¤) at x=136 y=62 z=159
5->Item in a building.
6 =vodka roast [149]= (312304¤) at x=99 y=83 z=117
7 @<@cinnamon grossular gudas@>@ (163200¤) at x=147 y=109 z=166
7->Item in inventory (unit) and owned.
8 @<@native gold door@>@ (140400¤) at x=133 y=97 z=168 H
9 @<@steel weapon rack@>@ (91200¤) at x=129 y=67 z=168
9->Item in a building.
10 @<@iron statue of Cilob Urnwrung@>@ (88800¤) at x=136 y=96 z=29
10->Item in a building.First line for each entry gives chart position in the ranking of most valuable items, description or name of the item, value and position on map. Additionally a letter H at the end denotes "hidden" flag (which can be set/cleared by the script), like in position 8 of the list above.
Second line (optional) gives some additional info about state of the item, if it is held in inventory or built into building for example. No all items have this info.
If the script is configured to show total, also number and total value of items shown is presented, e.g.:
Items shown: 10 (total value: 4937064¤) Line options:
list-valuables -help - display line options
list-valuables -show N - shows N most valuable items.
For example
list-valuables -show 100 will show up to one hundred most valuable items. If you want to see all items (or rather all which fits into console buffer) then use a very high number, like 99999 or 1000000.
list-valuables -state - shows state if not shown, or hides if shown (default state is in CONFIGURATION, see below)
State is additional line of information for some items, like whether it is in inventory, if it is owned, held by visitor or a trader. This is important for baiting, because kobolds don't steal items in inventory of live persons, or built into buildings.
list-valuables -hide N - hides/unhides Nth item to help localize it
Switches the hidden flag of an item, which is particularly helpful in quantum stockpiles or other heaps. N is the number on the most valuable items list (printed before name of the item, a chart position). To get item you must run the script once, to set the flag you need another run. Since this number is temporary, changes for less valuable items often, it's best to pause the game before trying to us this option. NOTE: in adventurer mode the hidden items do not disappear, but they are listed at the end of a particular list, so this option still can be useful to a degree.
list-valuables -filter NAME - shows items containing NAME only
Show only items containing a NAME in their description. NAME can be a standard Lua pattern. Additionally the curly brackets can be used to show forbidden items. Use a dot instead of space (or %s pattern, as it's a standard in Lua). Examples of filters:
-filter large.*leather --match large items made of leather
-filter giant.*leather --match items made of leather of giant animals
-filter giant.cave.spider.silk --match GCS silk or item made from it
-filter giant%scave%sspider%ssilk --match GCS silk or item made from it
-filter {.*roast --match forbidden roasts
-filter [^{].*roast --match unforbidden roasts
Masterwork quality uses @ sign, exceptional items use =.
Note that the items shown still must belong to the top N valuable on map, like in -show option (defaults to ten). If you suspect that not all interesting items are shown, then increase N value in -show.
list-valuables -total - shows number and total value of items
Display number and sums up the value of items shown. Useful for counting particular type of items and their value, when paired with -filter option.
list-valuables -log - logs the list to a file in main DF directory
The file allows to see more items than the console buffer holds, including all items, by saving the output to a file in main DF directory. If huge number of items is shown, this may take a couple of megabytes. Name of this file depends on mode and name of the fortress (if available). Use a text editor for looking through the log file.
CONFIGURATION options are switches which can set some default behaviour of the script. They are located near beginning of the script. They have format
OPTION = true or
OPTION = false. If "true" the OPTION is enabled, if "false" the OPTION is disabled. CONFIGURATION options and their default state:
MULTICOLOR = true -- change to false to prevent use of different colours. By default uses grey to show the state (second) line, red to show items barred from fortress (usually trader or visitor owned), and yellow to show items off-map (just stolen or carried by missionairies, though stolen trader items are still red). Disable to use only the default console colour.
SHOW_OWNERSHIP = true -- checks ownership (apart from trader's, which is always checked), items not owned by fortress are red. Disabling marginally speds up the script. If SHOW_STATE is enabled, then ownership is checked regardless.
SHOW_STATE = true --shows second line with more info, if item is in building, or in inventory, or owned by someone etc. Implies enabled SHOW_OWNERSHIP. Line option -state temporarily switches this setting to opposite.
NOTE: "trader" means the item has "trader" flag, which rarely has something to do with actual trader. Sometimes one of your visitors or residents have such item. It is barred from being used by the rest of the fortress, though. If it is owned (as opposed to only held) then it is described as "not for sale".
SHOW_FORBID = true --shows curly brackets around forbidden item. The brackets are outside description, which slightly differs from the DF behaviour. The bracket can be used in filter. Disable if you don't care if the items are forbidden or not.
SHOW_TOTAL = false --change to true to always show number of items shown and their total value, it then works as if the -total option was always enabled. If set to false you can still use -total option.
DEEP_CHECK = true --more thoroughly checks item presence on map in non-dwarf mode. It's always disabled in dwarf fortress mode, because it is not necessary (standard checks suffice) and very CPU demanding. You may disable it if the script works slowly in adventurer mode. This will result in showing items which are not really present on current map, like demonic slabs or corpses, body parts and equipment of creatures slain by your adventurer in the past (not all of such items will be shown, only those which by coincidence have x,y coordinates inside current map). However, I recommend to have it enabled, unless the script works with too noticeable delay in adventurer.
Issues:
One issue with this script is that it will show items not yet discovered by you or your people. Fortunately they are rarely very spoilerific, usually it's bodies, remains and spider webs, and as such they are not in the top 10 of most valuable items. If you have DEEP_CHECK disabled in adventurer and are away from the vault, the demonic slabs can be still shown in top ten (they cost 1200), though their location doesn't give much in most cases. Similarly mummy treasue can be seen before you enter the pyramid.
All items must have unique rank (chart position), even if they have the same value, to use with -hide option. The items are sorted in this order:
-value
--age (older items first if the same value)
---ID number (lower numbers first if the same age)
The second and third order rules (age and ID) are not really guaranteed, but they are usually observed.