diff --git a/README.rst b/README.rst index e8e444bb7..a563f7bb8 100644 --- a/README.rst +++ b/README.rst @@ -615,8 +615,7 @@ Options ------- :tweak clear-missing: Remove the missing status from the selected unit. This allows engraving slabs for ghostly, but not yet found, creatures. :tweak clear-ghostly: Remove the ghostly status from the selected unit and mark it as dead. This allows getting rid of bugged ghosts which do not show up in the engraving slab menu at all, even after using clear-missing. It works, but is potentially very dangerous - so use with care. Probably (almost certainly) it does not have the same effects like a proper burial. You've been warned. -:tweak clear-resident: Remove the resident flag from the selected unit. Intended to fix bugged migrants who stay at the map edge and don't enter your fort. Only works for dwarves of the own civilization. -:tweak clear-merchant: Remove the merchant flag from the selected unit. Assimilates bugged merchants who don't leave the map into your fort. Only works for dwarves of the own civilization. +:tweak fixmigrant: Remove the resident/merchant flag from the selected unit. Intended to fix bugged migrants/traders who stay at the map edge and don't enter your fort. Only works for dwarves (or generally the player's race in modded games). Can be abused to grab caravan merchants, but that might result into weirdness during trading. tubefill ======== diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 3ac9b625b..31969bde7 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -10,6 +10,8 @@ #include "modules/Units.h" #include "modules/Items.h" +#include "MiscUtils.h" + #include "DataDefs.h" #include "df/ui.h" #include "df/world.h" @@ -53,14 +55,11 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector & params); -// to be called by tweak-merchant and tweak-resident +// to be called by tweak-fixmigrant // units forced into the fort by removing the flags do not own their clothes // which has the result that they drop all their clothes and become unhappy because they are naked +// so we need to make them own their clothes and add them to their uniform command_result fix_clothing_ownership(color_ostream &out, df::unit* unit) { // first, find one owned item to initialize the vtable @@ -101,6 +101,8 @@ command_result fix_clothing_ownership(color_ostream &out, df::unit* unit) { df::unit_inventory_item* inv_item = unit->inventory[j]; df::item* item = inv_item->item; + // unforbid items (for the case of kidnapping caravan escorts who have their stuff forbidden by default) + inv_item->item->flags.bits.forbid = 0; if(inv_item->mode == df::unit_inventory_item::T_mode::Worn) { // ignore armor? @@ -111,12 +113,18 @@ command_result fix_clothing_ownership(color_ostream &out, df::unit* unit) if(!Items::getOwner(item)) { if(Items::setOwner(item, unit)) + { + // add to uniform, so they know they should wear their clothes + insert_into_vector(unit->military.uniforms[0], item->id); fixcount++; + } else out << "could not change ownership for item!" << endl; } } } + // clear uniform_drop (without this they would drop their clothes and pick them up some time later) + unit->military.uniform_drop.clear(); out << "ownership for " << fixcount << " clothes fixed" << endl; return CR_OK; } @@ -166,49 +174,67 @@ static command_result tweak(color_ostream &out, vector ¶meters) return CR_FAILURE; } } - else if (cmd == "clear-resident") + else if (cmd == "fixmigrant") { df::unit *unit = getSelectedUnit(out); - if (!unit) - return CR_FAILURE; - // must be own race and civ and a merchant - if ( unit->flags2.bits.resident - && unit->race == df::global::ui->race_id - && unit->civ_id == df::global::ui->civ_id) + if (!unit) { - // remove resident flag - unit->flags2.bits.resident = 0; - return fix_clothing_ownership(out, unit); + out << "No unit selected!" << endl; + return CR_FAILURE; } - else + + if(unit->race != df::global::ui->race_id) { - out.print("That's not a resident dwarf of your civilization!\n"); + out << "Selected unit does not belong to your race!" << endl; return CR_FAILURE; } + + // case #1: migrants who have the resident flag set + // see http://dffd.wimbli.com/file.php?id=6139 for a save + if (unit->flags2.bits.resident) + unit->flags2.bits.resident = 0; + + // case #2: migrants who have the merchant flag + // happens on almost all maps after a few migrant waves + if(unit->flags1.bits.merchant) + unit->flags1.bits.merchant = 0; + + // this one is a cheat, but bugged migrants usually have the same civ_id + // so it should not be triggered in most cases + // if it happens that the player has 'foreign' units of the same race + // (vanilla df: dwarves not from mountainhome) on his map, just grab them + if(unit->civ_id != df::global::ui->civ_id) + unit->civ_id = df::global::ui->civ_id; + + return fix_clothing_ownership(out, unit); } - else if (cmd == "clear-merchant") + else if (cmd == "makeown") { + // force a unit into your fort, regardless of civ or race + // allows to "steal" caravan guards etc df::unit *unit = getSelectedUnit(out); if (!unit) - return CR_FAILURE; - - // must be own race and civ and a merchant - if ( unit->flags1.bits.merchant - && unit->race == df::global::ui->race_id - && unit->civ_id == df::global::ui->civ_id) - { - // remove merchant flag - unit->flags1.bits.merchant = 0; - return fix_clothing_ownership(out, unit); - } - else { - out.print("That's not a dwarf merchant of your civilization!\n"); + out << "No unit selected!" << endl; return CR_FAILURE; } + if (unit->flags2.bits.resident) + unit->flags2.bits.resident = 0; + if(unit->flags1.bits.merchant) + unit->flags1.bits.merchant = 0; + if(unit->flags1.bits.forest) + unit->flags1.bits.forest = 0; + if(unit->civ_id != df::global::ui->civ_id) + unit->civ_id = df::global::ui->civ_id; + if(unit->profession == df::profession::MERCHANT) + unit->profession = df::profession::TRADER; + if(unit->profession2 == df::profession::MERCHANT) + unit->profession2 = df::profession::TRADER; + return fix_clothing_ownership(out, unit); } - else return CR_WRONG_USAGE; + else + return CR_WRONG_USAGE; return CR_OK; } diff --git a/plugins/zone.cpp b/plugins/zone.cpp index c668abe99..2f2b64588 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -905,7 +905,7 @@ bool isAssigned(df::unit* unit) if( rtype == df::general_ref_type::BUILDING_CIVZONE_ASSIGNED || rtype == df::general_ref_type::BUILDING_CAGED || rtype == df::general_ref_type::BUILDING_CHAIN - || (rtype == df::general_ref_type::CONTAINED_IN_ITEM && isBuiltCageAtPos(unit->pos)) + || (rtype == df::general_ref_type::CONTAINED_IN_ITEM && isInBuiltCage(unit)) ) { assigned = true; @@ -958,12 +958,11 @@ bool isInBuiltCage(df::unit* unit) df::building* building = world->buildings.all[b]; if( building->getType() == building_type::Cage) { - df::building_cagest* oldcage = (df::building_cagest*) building; - for(size_t oc=0; ocassigned_creature.size(); oc++) + df::building_cagest* cage = (df::building_cagest*) building; + for(size_t c=0; cassigned_creature.size(); c++) { - if(oldcage->assigned_creature[oc] == unit->id) + if(cage->assigned_creature[c] == unit->id) { - oldcage->assigned_creature.erase(oldcage->assigned_creature.begin() + oc); caged = true; break; } @@ -2770,7 +2769,9 @@ command_result autoButcher( color_ostream &out, bool verbose = false ) || !isTame(unit) || isWar(unit) // ignore war dogs etc || isHunter(unit) // ignore hunting dogs etc - || (isContainedInItem(unit) && hasValidMapPos(unit) && isBuiltCageAtPos(unit->pos)) + // ignore creatures in built cages to leave zoos alone + // (TODO: allow some kind of slaughter cages which you can place near the butcher) + || (isContainedInItem(unit) && isInBuiltCage(unit)) || unit->name.has_name ) continue;