diff --git a/plugins/createitem.cpp b/plugins/createitem.cpp index e1bb50b82..6e9cd6ea1 100644 --- a/plugins/createitem.cpp +++ b/plugins/createitem.cpp @@ -7,6 +7,7 @@ #include "MiscUtils.h" #include "modules/Maps.h" +#include "modules/MapCache.h" #include "modules/Gui.h" #include "modules/Items.h" #include "modules/Materials.h" @@ -22,6 +23,7 @@ #include "df/creature_raw.h" #include "df/caste_raw.h" #include "df/reaction_reagent.h" +#include "df/reaction_reagent_itemst.h" #include "df/reaction_product_itemst.h" using namespace std; @@ -33,18 +35,27 @@ using df::global::gametype; DFHACK_PLUGIN("createitem"); +int dest_container = -1, dest_building = -1; + command_result df_createitem (color_ostream &out, vector & parameters); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand("createitem", "Create arbitrary item at the selected unit's feet.", df_createitem, false, + commands.push_back(PluginCommand("createitem", "Create arbitrary items.", df_createitem, false, "Syntax: createitem [count]\n" " - Item token for what you wish to create, as specified in custom\n" " reactions. If the item has no subtype, omit the :NONE.\n" " - The material you want the item to be made of, as specified\n" " in custom reactions. For REMAINS, FISH, FISH_RAW, VERMIN,\n" " PET, and EGG, replace this with a creature ID and caste.\n" - " [count] - How many of the item you wish to create.\n")); + " [count] - How many of the item you wish to create.\n" + "\n" + "By default, items are created at the feet of the selected unit.\n" + "\n" + "Syntax: createitem \n" + " - Where to put subsequently created items.\n" + " Valid values are 'floor', 'item', and 'building'.\n" + )); return CR_OK; } @@ -61,6 +72,14 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it bool is_gloves = (prod->item_type == df::item_type::GLOVES); bool is_shoes = (prod->item_type == df::item_type::SHOES); + df::item *container = NULL; + df::building *building = NULL; + df::reaction_reagent_itemst *reagent = NULL; + if (dest_container != -1) + container = df::item::find(dest_container); + if (dest_building != -1) + building = df::building::find(dest_building); + prod->produce(unit, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE, df::historical_entity::find(unit->civ_id), ((*gametype == df::game_type::DWARF_MAIN) || (*gametype == df::game_type::DWARF_RECLAIM)) ? df::world_site::find(ui->site_id) : NULL); @@ -70,9 +89,28 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it // otherwise, make a second set because shoes are normally made in pairs if (is_shoes && out_items.size() == prod->count * 2) is_shoes = false; + + MapExtras::MapCache mc; + for (size_t i = 0; i < out_items.size(); i++) { - out_items[i]->moveToGround(unit->pos.x, unit->pos.y, unit->pos.z); + bool on_ground = true; + if (container) + { + on_ground = false; + out_items[i]->flags.bits.removed = 1; + if (!Items::moveToContainer(mc, out_items[i], container)) + out_items[i]->moveToGround(container->pos.x, container->pos.y, container->pos.z); + } + if (building) + { + on_ground = false; + out_items[i]->flags.bits.removed = 1; + if (!Items::moveToBuilding(mc, out_items[i], (df::building_actual *)building, 0)) + out_items[i]->moveToGround(building->centerx, building->centery, building->z); + } + if (on_ground) + out_items[i]->moveToGround(unit->pos.x, unit->pos.y, unit->pos.z); if (is_gloves) { // if the reaction creates gloves without handedness, then create 2 sets (left and right) @@ -97,8 +135,104 @@ command_result df_createitem (color_ostream &out, vector & parameters) int32_t mat_index = -1; int count = 1; + if (parameters.size() == 1) + { + if (parameters[0] == "floor") + { + dest_container = -1; + dest_building = -1; + out.print("Items created will be placed on the floor.\n"); + return CR_OK; + } + else if (parameters[0] == "item") + { + dest_building = -1; + df::item *item = Gui::getSelectedItem(out); + if (!item) + { + out.printerr("You must select a container!\n"); + return CR_FAILURE; + } + switch (item->getType()) + { + case df::item_type::FLASK: + case df::item_type::BARREL: + case df::item_type::BUCKET: + case df::item_type::ANIMALTRAP: + case df::item_type::BOX: + case df::item_type::BIN: + case df::item_type::BACKPACK: + case df::item_type::QUIVER: + break; + case df::item_type::TOOL: + if (item->hasToolUse(df::tool_uses::LIQUID_CONTAINER)) + break; + if (item->hasToolUse(df::tool_uses::FOOD_STORAGE)) + break; + if (item->hasToolUse(df::tool_uses::SMALL_OBJECT_STORAGE)) + break; + if (item->hasToolUse(df::tool_uses::TRACK_CART)) + break; + default: + out.printerr("The selected item cannot be used for item storage!\n"); + return CR_FAILURE; + } + dest_container = item->id; + string name; + item->getItemDescription(&name, 0); + out.print("Items created will be placed inside %s.\n", name.c_str()); + return CR_OK; + } + else if (parameters[0] == "building") + { + dest_container = -1; + df::building *building = Gui::getSelectedBuilding(out); + if (!building) + { + out.printerr("You must select a building!\n"); + return CR_FAILURE; + } + switch (building->getType()) + { + case df::building_type::Coffin: + case df::building_type::Furnace: + case df::building_type::TradeDepot: + case df::building_type::Shop: + case df::building_type::Box: + case df::building_type::Weaponrack: + case df::building_type::Armorstand: + case df::building_type::Workshop: + case df::building_type::Cabinet: + case df::building_type::SiegeEngine: + case df::building_type::Trap: + case df::building_type::AnimalTrap: + case df::building_type::Cage: + case df::building_type::Wagon: + case df::building_type::NestBox: + case df::building_type::Hive: + break; + default: + out.printerr("The selected building cannot be used for item storage!\n"); + return CR_FAILURE; + } + if (building->getBuildStage() != building->getMaxBuildStage()) + { + out.printerr("The selected building has not yet been fully constructed!\n"); + return CR_FAILURE; + } + dest_building = building->id; + string name; + building->getName(&name); + out.print("Items created will be placed inside %s.\n", name.c_str()); + return CR_OK; + } + else + return CR_WRONG_USAGE; + } + if ((parameters.size() < 2) || (parameters.size() > 3)) return CR_WRONG_USAGE; + item_str = parameters[0]; material_str = parameters[1]; @@ -250,6 +384,17 @@ command_result df_createitem (color_ostream &out, vector & parameters) break; } + if ((dest_container != -1) && !df::item::find(dest_container)) + { + dest_container = -1; + out.printerr("Previously selected container no longer exists - item will be placed on the floor.\n"); + } + if ((dest_building != -1) && !df::building::find(dest_building)) + { + dest_building = -1; + out.printerr("Previously selected building no longer exists - item will be placed on the floor.\n"); + } + bool result = makeItem(prod, unit); delete prod; if (!result)