No worries, gotcha covered fam.
It just so happens that I've written a DFHack script that teleports items to a pedestal. It's not quite finished, but it's functional.
subdirectory.
--Teleports and displays an item in a display furniture.
--[====[
put-item-on-display
===================
Teleports and displays an item in a display furniture.
Usage:
First, set the target display furniture by running the script
while viewing the display furniture building.
After doing this, you can teleport items to the display furniture
by running the script while viewing an item's description page.
Suggestion: assign keybindings to do this.
e.g. add these lines to dfhack-config/init/dfhack.init :
keybinding add Ctrl-Shift-Z@dwarfmode/ViewSheets/ITEM "put-item-on-display"
keybinding add Ctrl-Shift-Z@dwarfmode/ViewSheets/BUILDING "put-item-on-display"
]====]
local utils=require('utils')
local verbose = true
function printf(...)
print(dfhack.df2console(string.format(...)))
end
function vprintf(...)
if verbose then
printf(...)
end
end
function put_item_on_display_teleport(item, building)
local desc = dfhack.items.getDescription(item, 0)
local title = dfhack.items.getBookTitle(item)
if title ~= '' then desc = title; end
desc = string.format("%s (%d)", desc, item.id)
if not item.flags.on_ground then
vprintf("Warning: skipping %s: not .on_ground", desc)
elseif item.flags.in_job then
vprintf("Warning: skipping %s: .in_job", desc)
elseif item.flags.hostile then
vprintf("Warning: skipping %s: .hostile", desc)
elseif item.flags.in_inventory then
vprintf("Warning: skipping %s: .in_inventory", desc)
elseif item.flags.removed then
vprintf("Warning: skipping %s: .removed", desc)
elseif item.flags.in_building then -- redundant to .on_ground
vprintf("Warning: skipping %s: .in_building", desc)
--elseif item.flags.container then -- redundant to .on_ground
----NO IT ISNT; it means THIS ITEM is a container.
-- vprintf("Warning: skipping %s: .container", desc)
-- allow .dead_dwarf
-- allow .rotten
-- allow .spider_web
elseif item.flags.construction then
vprintf("Warning: skipping %s: .construction", desc)
-- allow .encased
-- allow .unk12
-- allow .murder
-- allow .foreign
-- allow .trader; see below.
-- allow .owned
elseif item.flags.garbage_collect then
vprintf("Warning: skipping %s: .garbage_collect", desc)
-- allow .artifact
-- allow .forbid
elseif item.flags.already_uncategorized then
vprintf("Warning: skipping %s: .already_uncategorized", desc)
-- allow .dump
elseif item.flags.on_fire then
vprintf("Warning: skipping %s: .on_fire", desc)
elseif item.flags.melt then
vprintf("Warning: skipping %s: .melt", desc)
-- allow .hidden
elseif item.flags.in_chest then
vprintf("Warning: skipping %s: .in_chest", desc)
elseif item.flags.use_recorded then
vprintf("Warning: skipping %s: .use_recorded", desc)
-- allow .artifact_mood
-- allow .temps_computed
-- allow .weight_computed
elseif item.flags.unk30 then
vprintf("Warning: skipping %s: .unk30", desc)
-- allow .from_worldgen
elseif item.flags2.has_rider then
vprintf("Warning: skipping %s: .has_rider", desc)
elseif item.flags2.unk1 then
vprintf("Warning: skipping %s: .unk1", desc)
-- allow .grown
-- allow .unk_book
-- TODO: test flags2 .4 through .31 ?? They do get set sometimes.
-- TODO are there any relevant GeneralRef's ?
--elseif nil ~= dfhack.items.getGeneralRef(item, xxx) then
-- vprintf("Warning: skipping %s: General Ref xxx", desc)
-- TODO maybe delete the GeneralRef df.general_ref_type.UNIT_ITEMOWNER
-- and clear flags.owner ?? hmm, also delete the item.id from unit.owned_items[] .
else -- item is okay to deal with. verify the building and do the job.
-- local pos = xyz2pos(building.centerx, building.centery, building.z)
local pos = utils.getBuildingCenter(building)
-- TODO is this step necessary? Probably not.
if not dfhack.items.moveToGround(item, pos) then
vprintf("Warning: skipping %s: moveToGround() failed.", desc)
return
end
if not dfhack.items.moveToBuilding(item, building) then
vprintf("Warning: skipping %s: moveToBuilding() failed.", desc)
return
end
-- strangely, dfhack.items.moveToBuilding() doesn't set .in_building.
item.flags.in_building = true
item.flags.on_ground = false
item.flags.in_inventory = false -- redundant; we don't allow items in an inventory.
item.flags.dump = false
item.flags.forbid = true -- I think I want this.
item.flags.trader = false -- it can happen that .trader items end up on the ground,
-- sometimes visitors bring them and drop them. Claim them.
-- (Do they drop them when they are grabbed for interrogation?)
-- from here on, I just want to abort the script on failure.
-- failure means the game is in an inconsistent state.
-- Okay. It's in the building. Now we need to display it.
local igr = df.general_ref_building_display_furniturest:new()
igr.building_id = building.id
item.general_refs:insert('#', igr)
-- on brief inspection (one example, 3 .displayed_items[]) this is probably sorted.
-- DONE check that, because if it's sorted, it gets out-of-sync with the building contents.
-- Answer: checked with an 80-item pedestal.
-- building.contained_items[] also gets sorted by item/id. But just the .mode 0 items,
-- the .mode 2 item still comes first.
-- DONE2: Actually, I have a pedestal with NONSORTED items in lockstep.
-- I now suspect that they get incidentally sorted during the fortress map load.
-- Accordingly, I am going to NOT keep .displayed_items sorted; I will insert in lockstep
-- with .contained_items.
--utils.insert_sorted(building.displayed_items, item.id)
building.displayed_items:insert('#', item.id)
-- ... and we're done.
end -- big if
end
-- GLOBAL for persistence. Uses the .id instead of the actual building object to
-- prevent problems from objects being moved around, deallocated, etc.
buildingid = buildingid or -1
-- if Viewing a building and that building's type is display furniture, cache that building's id.
if dfhack.gui.getSelectedBuilding(true) then
local building = dfhack.gui.getSelectedBuilding(true)
if building ~= nil and building._type == df.building_display_furniturest then
buildingid = building.id -- GLOBAL for persistence.
vprintf("targetting %s (%d)", utils.getBuildingName(building), buildingid)
else
-- TODO complain.
end
return
end
if buildingid == -1 then
printf("First, you must specify a display furniture building, by running this script while Viewing the building.")
return
end
local building = df.building.find(buildingid)
if building == nil then -- this could happen e.g. if the building is deconstructed.
printf("Error: could not find building %d", buildingid)
return
end
if building._type ~= df.building_display_furniturest then -- can't happen.
printf("Error: building %d is not of type df.building_display_furniturest", buildingid);
return
end
-- TODO perform job on all in select rectangle, with options for forbidden/unforbidden.
local items = { dfhack.gui.getSelectedItem() }
for _, item in ipairs(items) do
put_item_on_display_teleport(item, building)
end
.