From 88c914142bcbd1a24bac09f5057b69472d7021d9 Mon Sep 17 00:00:00 2001 From: Robert Heinrich Date: Thu, 29 Mar 2012 15:33:54 +0200 Subject: [PATCH] added plugin changeitem (change material type and base quality) --- README.rst | 19 +++ library/xml | 2 +- plugins/CMakeLists.txt | 1 + plugins/changeitem.cpp | 322 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 plugins/changeitem.cpp diff --git a/README.rst b/README.rst index 1e5f84ca7..7f41f82f9 100644 --- a/README.rst +++ b/README.rst @@ -144,6 +144,25 @@ Example: ``changevein NATIVE_PLATINUM`` Convert vein at cursor position into platinum ore. +changeitem +========== +Allows changing item material and base quality. By default the item currently selected in the UI will be changed (you can select items in the 'k' list or inside containers/inventory). By default change is only allowed if materials is of the same subtype (for example wood<->wood, stone<->stone etc). But since some transformations work pretty well and may be desired you can override this with 'force'. Note that some attributes will not be touched, possibly resulting in weirdness. To get an idea how the RAW id should look like, check some items with 'info'. Using 'force' might create items which are not touched by crafters/haulers. + +Options +------- +:info: Don't change anything, print some info instead. +:here: Change all items at the cursor position. Requires in-game curser. +:material, m: Change material. Must be followed by valid material RAW id. +:quality, q: Change base quality. Must be followed by number (0-5). +:force: Ignore subtypes, force change to new material. + +Examples: +--------- +``changeitem m INORGANIC:GRANITE here`` + Change material of all items under the cursor to granite. +``changeitem q 5`` + Change currently selected item to masterpiece quality. + cursecheck ========== Checks a single map tile or the whole map/world for cursed creatures (ghosts, vampires, necromancers, werebeasts, zombies). diff --git a/library/xml b/library/xml index 1b1fe798e..8bb7f923b 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 1b1fe798e553cf0ed309606f32b8448bb96b30c8 +Subproject commit 8bb7f923b1d124610db7e30aeb3be8f4cb9bd021 diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 84bdf8239..229c73e60 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -91,6 +91,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(follow follow.cpp) DFHACK_PLUGIN(changevein changevein.cpp) DFHACK_PLUGIN(changelayer changelayer.cpp) + DFHACK_PLUGIN(changeitem changeitem.cpp) DFHACK_PLUGIN(advtools advtools.cpp) DFHACK_PLUGIN(tweak tweak.cpp) DFHACK_PLUGIN(feature feature.cpp) diff --git a/plugins/changeitem.cpp b/plugins/changeitem.cpp new file mode 100644 index 000000000..b3fb2debc --- /dev/null +++ b/plugins/changeitem.cpp @@ -0,0 +1,322 @@ +// changeitem plugin +// allows to change the material type and quality of selected items +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" +#include "modules/Maps.h" +#include "modules/Gui.h" +#include "modules/Items.h" +#include "modules/Materials.h" +#include "modules/MapCache.h" + +#include "DataDefs.h" +#include "df/item.h" +#include "df/world.h" +#include "df/general_ref.h" + +using namespace DFHack; +using namespace df::enums; + +using MapExtras::Block; +using MapExtras::MapCache; +using df::global::world; + +DFHACK_PLUGIN("changeitem"); + +command_result df_changeitem(color_ostream &out, vector & parameters); + +const string changeitem_help = + "Changeitem allows to change some item attributes.\n" + "By default the item currently selected in the UI will be changed\n" + "(you can select items in the 'k' list or inside containers/inventory).\n" + "By default change is only allowed if materials is of the same subtype\n" + "(for example wood<->wood, stone<->stone etc). But since some transformations\n" + "work pretty well and may be desired you can override this with 'force'.\n" + "Note that some attributes will not be touched, possibly resulting in weirdness.\n" + "To get an idea how the RAW id should look like, check some items with 'info'.\n" + "Using 'force' might create items which are not touched by crafters/haulers.\n" + "Options:\n" + " info - don't change anything, print some item info instead\n" + " here - change all items at cursor position\n" + " material, m - change material. must be followed by material RAW id\n" + " quality, q - change base quality. must be followed by number (0-5)\n" + " force - ignore subtypes, force change to new material.\n" + "Example:\n" + " changeitem m INORGANIC:GRANITE here" + " change material of all items under the cursor to granite\n" + " changeitem q 5\n" + " change currently selected item to masterpiece quality\n"; + + +DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) +{ + commands.clear(); + commands.push_back(PluginCommand( + "changeitem", "Change item attributes (material, quality).", + df_changeitem, false, + changeitem_help.c_str() + )); + + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + +// probably there is some method in the library which does the same +// todo: look for it :) +string describeQuality(int q) +{ + switch(q) + { + case 0: + return "Basic"; + case 1: + return "-Well-crafted-"; + case 2: + return "+Finely-crafted+"; + case 3: + return "*Superior quality*"; + case 4: + return "#Exceptional#"; + case 5: + return "$Masterful$"; + default: + return "!INVALID!"; + } +} + +command_result changeitem_execute( + color_ostream &out, df::item * item, + bool info, bool force, + bool change_material, string new_material, + bool change_quality, int new_quality); + +command_result df_changeitem(color_ostream &out, vector & parameters) +{ + CoreSuspender suspend; + + bool here = false; + bool info = false; + bool force = false; + + bool change_material = false; + string new_material = "none"; + + bool change_quality = false; + int new_quality = 0; + + for (size_t i = 0; i < parameters.size(); i++) + { + string & p = parameters[i]; + + if (p == "help" || p == "?") + { + out << changeitem_help << endl; + return CR_OK; + } + else if (p == "here") + { + here = true; + } + else if (p == "info") + { + info = true; + } + else if (p == "force") + { + force = true; + } + else if (p == "material" || p == "m" ) + { + // must be followed by material RAW id + // (string like 'INORGANIC:GRANITE', 'PLANT:MAPLE:WOOD', ...) + if(i == parameters.size()-1) + { + out.printerr("no material specified!\n"); + } + change_material = true; + new_material = parameters[i+1]; + i++; + } + else if (p == "quality" || p == "q") + { + // must be followed by numeric quality (allowed: 0-5) + if(i == parameters.size()-1) + { + out.printerr("no quality specified!\n"); + } + string & q = parameters[i+1]; + // meh. should use a stringstream instead. but it's only 6 numbers + if(q == "0") + new_quality = 0; + else if(q == "1") + new_quality = 1; + else if(q == "2") + new_quality = 2; + else if(q == "3") + new_quality = 3; + else if(q == "4") + new_quality = 4; + else if(q == "5") + new_quality = 5; + else + { + out << "Invalid quality specified!" << endl; + return CR_WRONG_USAGE; + } + out << "change to quality: " << describeQuality(new_quality) << endl; + change_quality = true; + i++; + } + else + { + out << p << ": Unknown command!" << endl; + return CR_WRONG_USAGE; + } + } + + if (!Maps::IsValid()) + { + out.printerr("Map is not available!\n"); + return CR_FAILURE; + } + + MaterialInfo mat_new; + if (change_material && !mat_new.find(new_material)) + { + out.printerr("No such material!\n"); + return CR_FAILURE; + } + + if (here) + { + int processed_total = 0; + int cx, cy, cz; + DFCoord pos_cursor; + + // needs a cursor + if (!Gui::getCursorCoords(cx,cy,cz)) + { + out.printerr("Cursor position not found. Please enable the cursor.\n"); + return CR_FAILURE; + } + pos_cursor = DFCoord(cx,cy,cz); + + // uh. is this check necessary? + // changeitem doesn't do stuff with map blocks... + { + MapCache MC; + Block * b = MC.BlockAt(pos_cursor / 16); + if(!b) + { + out.printerr("Cursor is in an invalid/uninitialized area. Place it over a floor.\n"); + return CR_FAILURE; + } + // when only changing material it doesn't matter if cursor is over a tile + //df::tiletype ttype = MC.tiletypeAt(pos_cursor); + //if(!DFHack::isFloorTerrain(ttype)) + //{ + // out.printerr("Cursor should be placed over a floor.\n"); + // return CR_FAILURE; + //} + } + + // iterate over all items, process those where pos = pos_cursor + size_t numItems = world->items.all.size(); + for(size_t i=0; i< numItems; i++) + { + df::item * item = world->items.all[i]; + DFCoord pos_item(item->pos.x, item->pos.y, item->pos.z); + + if (pos_item != pos_cursor) + continue; + + changeitem_execute(out, item, info, force, change_material, new_material, change_quality, new_quality); + processed_total++; + } + out.print("Done. %d items processed.\n", processed_total); + } + else + { + // needs a selected item + df::item *item = Gui::getSelectedItem(out); + if (!item) + { + out.printerr("No item selected.\n"); + return CR_FAILURE; + } + changeitem_execute(out, item, info, force, change_material, new_material, change_quality, new_quality); + } + return CR_OK; +} + +command_result changeitem_execute( + color_ostream &out, df::item * item, + bool info, bool force, + bool change_material, string new_material, + bool change_quality, int new_quality ) +{ + MaterialInfo mat_new; + MaterialInfo mat_old; + + if(change_material) + mat_new.find(new_material); + if(change_material || info) + mat_old.decode(item); + + // print some info, don't change stuff + if(info) + { + out << "Item info: " << endl; + out << " quality: " << describeQuality(item->getQuality()) << endl; + //if(item->isImproved()) + // out << " imp.quality: " << describeQuality(item->getImprovementQuality()) << endl; + out << " material: " << mat_old.getToken() << endl; + return CR_OK; + } + + if(change_quality) + { + item->setQuality(new_quality); + // it would be nice to be able to change the improved quality, too + // (only allowed if the item is already improved) + // but there is no method in item.h which supports that + // ok: hints from _Q/angavrilov: improvent is a vector, an item can have more than one improvement + // -> virtual_cast to item_constructedst + } + + if(change_material) + { + // subtype and mode should match to avoid doing dumb stuff like changing boulders into meat whatever + // changing a stone cabinet to wood is fine, though. as well as lots of other combinations. + // still, it's better to make the user activate 'force' if he really wants to. + + if(force||(mat_old.subtype == mat_new.subtype && mat_old.mode==mat_new.mode)) + { + item->setMaterial(mat_new.type); + item->setMaterialIndex(mat_new.index); + } + else + { + out.printerr("change denied: subtype doesn't match. use 'force' to override.\n"); + } + + item->flags.bits.unk8 = 0; // recalc temperatures next time touched + item->flags.bits.weight_computed = 0; // recalc weight next time touched + } + return CR_OK; +}