diff --git a/LUA_API.rst b/LUA_API.rst index fc86070c3..df997008c 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -699,6 +699,10 @@ Units module Items module ------------ +* ``dfhack.items.getPosition(item)`` + + Returns true *x,y,z* of the item. + * ``dfhack.items.getOwner(item)`` Returns the owner unit or *nil*. @@ -708,6 +712,22 @@ Items module Replaces the owner of the item. If unit is *nil*, removes ownership. Returns *false* in case of error. +* ``dfhack.items.getContainer(item)`` + + Returns the container item or *nil*. + +* ``dfhack.items.getContainedItems(item)`` + + Returns a list of items contained in this one. + +* ``dfhack.items.moveToGround(item,pos)`` + + Move the item to the ground at position. Returns *false* if impossible. + +* ``dfhack.items.moveToContainer(item,container)`` + + Move the item to the container. Returns *false* if impossible. + Maps module ----------- diff --git a/Lua API.html b/Lua API.html index 7c3eeddb9..4be1050fb 100644 --- a/Lua API.html +++ b/Lua API.html @@ -929,6 +929,9 @@ a lua list containing them.

Items module

diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index febc59026..25f654b8c 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -45,6 +45,7 @@ distribution. #include "modules/Items.h" #include "modules/Materials.h" #include "modules/Maps.h" +#include "modules/MapCache.h" #include "LuaWrapper.h" #include "LuaTools.h" @@ -85,6 +86,33 @@ void push_pointer_vector(lua_State *state, const std::vector &pvec) } } +template +T *get_checked_arg(lua_State *state, int arg) +{ + luaL_checkany(state, arg); + + auto ptr = Lua::GetDFObject(state, arg); + if (!ptr) + luaL_argerror(state, arg, "invalid type"); + return ptr; +} + +int push_pos(lua_State *state, df::coord pos) +{ + if (!pos.isValid()) + { + lua_pushnil(state); + return 1; + } + else + { + lua_pushinteger(state, pos.x); + lua_pushinteger(state, pos.y); + lua_pushinteger(state, pos.z); + return 3; + } +} + /************************************************** * Per-world persistent configuration storage API * **************************************************/ @@ -610,12 +638,47 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { { NULL, NULL } }; +static bool items_moveToGround(df::item *item, df::coord pos) +{ + MapExtras::MapCache mc; + return Items::moveToGround(mc, item, pos); +} + +static bool items_moveToContainer(df::item *item, df::item *container) +{ + MapExtras::MapCache mc; + return Items::moveToContainer(mc, item, container); +} + static const LuaWrapper::FunctionReg dfhack_items_module[] = { WRAPM(Items, getOwner), WRAPM(Items, setOwner), + WRAPM(Items, getContainer), + WRAPN(moveToGround, items_moveToGround), + WRAPN(moveToContainer, items_moveToContainer), { NULL, NULL } }; +static int items_getPosition(lua_State *state) +{ + return push_pos(state, Items::getPosition(get_checked_arg(state,1))); +} + +static int items_getContainedItems(lua_State *state) +{ + std::vector pvec; + Items::getContainedItems(get_checked_arg(state,1),&pvec); + push_pointer_vector(state, pvec); + return 1; +} + +static const luaL_Reg dfhack_items_funcs[] = { + { "getPosition", items_getPosition }, + { "getContainedItems", items_getContainedItems }, + { NULL, NULL } +}; + + static bool maps_isBlockBurrowTile(df::burrow *burrow, df::map_block *block, int x, int y) { return Maps::isBlockBurrowTile(burrow, block, df::coord2d(x,y)); @@ -642,14 +705,8 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = { static int maps_listBurrowBlocks(lua_State *state) { - luaL_checkany(state, 1); - - auto ptr = Lua::GetDFObject(state, 1); - if (!ptr) - luaL_argerror(state, 1, "invalid burrow type"); - std::vector pvec; - Maps::listBurrowBlocks(&pvec, ptr); + Maps::listBurrowBlocks(&pvec, get_checked_arg(state,1)); push_pointer_vector(state, pvec); return 1; } @@ -673,6 +730,6 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "gui", dfhack_gui_module); OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs); OpenModule(state, "units", dfhack_units_module); - OpenModule(state, "items", dfhack_items_module); + OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs); OpenModule(state, "maps", dfhack_maps_module, dfhack_maps_funcs); } diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 9bab02cc8..e7720fa91 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -42,6 +42,10 @@ namespace df struct itemdef; } +namespace MapExtras { + class MapCache; +} + /** * \defgroup grp_items Items module and its types * @ingroup grp_modules @@ -128,13 +132,15 @@ DFHACK_EXPORT df::unit *getOwner(df::item *item); DFHACK_EXPORT bool setOwner(df::item *item, df::unit *unit); /// which item is it contained in? -DFHACK_EXPORT int32_t getItemContainerID(const df::item * item); -DFHACK_EXPORT df::item *getItemContainer(const df::item * item); +DFHACK_EXPORT df::item *getContainer(df::item *item); /// which items does it contain? -DFHACK_EXPORT bool getContainedItems(const df::item * item, /*output*/ std::vector &items); +DFHACK_EXPORT void getContainedItems(df::item *item, /*output*/ std::vector *items); + +/// Returns the true position of the item. +DFHACK_EXPORT df::coord getPosition(df::item *item); + +DFHACK_EXPORT bool moveToGround(MapExtras::MapCache &mc, df::item *item, df::coord pos); +DFHACK_EXPORT bool moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container); -/// read item references, filtered by class -DFHACK_EXPORT bool readItemRefs(const df::item * item, const df::general_ref_type type, - /*output*/ std::vector &values); } } \ No newline at end of file diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index a6606d152..626f60bec 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -86,6 +86,21 @@ function copyall(table) return rv end +function pos2xyz(pos) + local x = pos.x + if x and x ~= -30000 then + return x, pos.y, pos.z + end +end + +function xyz2pos(x,y,z) + if x then + return {x=x,y=y,z=z} + else + return {x=-30000,y=-30000,z=-30000} + end +end + function dfhack.persistent:__tostring() return "" diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 20b95bc23..ca15ed278 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -39,6 +39,7 @@ using namespace std; #include "modules/Materials.h" #include "modules/Items.h" #include "modules/Units.h" +#include "modules/MapCache.h" #include "ModuleFactory.h" #include "Core.h" #include "Error.h" @@ -46,6 +47,7 @@ using namespace std; #include "df/world.h" #include "df/item.h" +#include "df/building.h" #include "df/tool_uses.h" #include "df/itemdef_weaponst.h" #include "df/itemdef_trapcompst.h" @@ -65,6 +67,8 @@ using namespace std; #include "df/job_item.h" #include "df/general_ref.h" #include "df/general_ref_unit_itemownerst.h" +#include "df/general_ref_contains_itemst.h" +#include "df/general_ref_contained_in_itemst.h" using namespace DFHack; using namespace df::enums; @@ -472,44 +476,183 @@ bool Items::setOwner(df::item *item, df::unit *unit) return true; } - -int32_t Items::getItemContainerID(const df::item * item) +df::item *Items::getContainer(df::item * item) { + CHECK_NULL_POINTER(item); + for (size_t i = 0; i < item->itemrefs.size(); i++) { df::general_ref *ref = item->itemrefs[i]; if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM) - return ref->getID(); + return ref->getItem(); } - return -1; + + return NULL; } -df::item *Items::getItemContainer(const df::item * item) +void Items::getContainedItems(df::item *item, std::vector *items) { + CHECK_NULL_POINTER(item); + + items->clear(); + for (size_t i = 0; i < item->itemrefs.size(); i++) { df::general_ref *ref = item->itemrefs[i]; - if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM) - return ref->getItem(); + if (ref->getType() != general_ref_type::CONTAINS_ITEM) + continue; + + auto child = ref->getItem(); + if (child) + items->push_back(child); } - return NULL; } -bool Items::getContainedItems(const df::item * item, std::vector &items) +df::coord Items::getPosition(df::item *item) { - return readItemRefs(item, general_ref_type::CONTAINS_ITEM, items); + CHECK_NULL_POINTER(item); + + if (item->flags.bits.in_inventory || + item->flags.bits.in_chest || + item->flags.bits.in_building) + { + for (size_t i = 0; i < item->itemrefs.size(); i++) + { + df::general_ref *ref = item->itemrefs[i]; + + switch (ref->getType()) + { + case general_ref_type::CONTAINED_IN_ITEM: + if (auto item2 = ref->getItem()) + return getPosition(item2); + break; + + case general_ref_type::UNIT_HOLDER: + if (auto unit = ref->getUnit()) + return unit->pos; + break; + + case general_ref_type::BUILDING_HOLDER: + if (auto bld = ref->getBuilding()) + return df::coord(bld->centerx, bld->centery, bld->z); + break; + } + } + } + + return item->pos; } -bool Items::readItemRefs(const df::item * item, df::general_ref_type type, std::vector &values) +static void removeRef(std::vector &vec, df::general_ref_type type, int id) { - values.clear(); + for (int i = vec.size()-1; i >= 0; i--) + { + df::general_ref *ref = vec[i]; + if (ref->getType() != type || ref->getID() != id) + continue; - for (size_t i = 0; i < item->itemrefs.size(); i++) + vector_erase_at(vec, i); + delete ref; + } +} + +static bool detachItem(MapExtras::MapCache &mc, df::item *item) +{ + if (item->flags.bits.on_ground) { - df::general_ref *ref = item->itemrefs[i]; - if (ref->getType() == type) - values.push_back(ref->getID()); + if (!mc.removeItemOnGround(item)) + Core::printerr("Item was marked on_ground, but not in block: %d (%d,%d,%d)\n", + item->id, item->pos.x, item->pos.y, item->pos.z); + + item->flags.bits.on_ground = false; + return true; } + else if (item->flags.bits.in_inventory) + { + bool found = false; - return !values.empty(); + for (int i = item->itemrefs.size()-1; i >= 0; i--) + { + df::general_ref *ref = item->itemrefs[i]; + + switch (ref->getType()) + { + case general_ref_type::CONTAINED_IN_ITEM: + if (auto item2 = ref->getItem()) + removeRef(item2->itemrefs, general_ref_type::CONTAINS_ITEM, item->id); + break; + + case general_ref_type::UNIT_HOLDER: + case general_ref_type::BUILDING_HOLDER: + return false; + + default: + continue; + } + + found = true; + vector_erase_at(item->itemrefs, i); + delete ref; + } + + if (!found) + return false; + + item->flags.bits.in_inventory = false; + return true; + } + else + return false; +} + +static void putOnGround(MapExtras::MapCache &mc, df::item *item, df::coord pos) +{ + item->pos = pos; + item->flags.bits.on_ground = true; + + if (!mc.addItemOnGround(item)) + Core::printerr("Could not add item %d to ground at (%d,%d,%d)\n", + item->id, pos.x, pos.y, pos.z); +} + +bool DFHack::Items::moveToGround(MapExtras::MapCache &mc, df::item *item, df::coord pos) +{ + CHECK_NULL_POINTER(item); + + if (!detachItem(mc, item)) + return false; + + putOnGround(mc, item, pos); + return true; +} + +bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container) +{ + CHECK_NULL_POINTER(item); + CHECK_NULL_POINTER(container); + + if (!detachItem(mc, item)) + return false; + + auto ref1 = df::allocate(); + auto ref2 = df::allocate(); + + if (!ref1 || !ref2) + { + delete ref1; delete ref2; + Core::printerr("Could not allocate container refs.\n"); + putOnGround(mc, item, getPosition(container)); + return false; + } + + item->pos = container->pos; + item->flags.bits.in_inventory = true; + container->flags.bits.container = true; + + ref1->item_id = item->id; + container->itemrefs.push_back(ref1); + ref2->item_id = container->id; + item->itemrefs.push_back(ref2); + + return true; } diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index c0aa9b634..fce80d775 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -596,7 +596,7 @@ void MapExtras::Block::init_item_counts() if (item_counts) return; item_counts = new T_item_counts[16]; - memset(item_counts, 0, sizeof(T_item_counts)); + memset(item_counts, 0, sizeof(T_item_counts)*16); if (!block) return; diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index a71555b7a..2620d9dea 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -182,15 +182,7 @@ static command_result autodump_main(color_ostream &out, vector & parame // Don't move items if they're already at the cursor if (pos_cursor != pos_item) - { - if (!MC.removeItemOnGround(itm)) - out.printerr("Item %d wasn't in the source block.\n", itm->id); - - itm->pos = pos_cursor; - - if (!MC.addItemOnGround(itm)) - out.printerr("Could not add item %d to destination block.\n", itm->id); - } + Items::moveToGround(MC, itm, pos_cursor); } else // destroy {