Merge remote-tracking branch 'ab9rf/master'

develop
Alexander Gavrilov 2012-05-05 19:26:56 +04:00
commit 1b5ce7b717
8 changed files with 826 additions and 49 deletions

@ -23,6 +23,16 @@
#include <df/building.h>
#include <df/workshop_type.h>
#include <df/unit_misc_trait.h>
#include <df/entity_position_responsibility.h>
#include <df/historical_figure.h>
#include <df/historical_entity.h>
#include <df/histfig_entity_link.h>
#include <df/histfig_entity_link_positionst.h>
#include <df/entity_position_assignment.h>
#include <df/entity_position.h>
#include <df/building_tradedepotst.h>
#include <MiscUtils.h>
using std::string;
using std::endl;
@ -446,21 +456,45 @@ static const struct labor_default default_labor_infos[] = {
/* WAX_WORKING */ {AUTOMATIC, false, 1, 200, 0},
};
static const df::job_skill noble_skills[] = {
df::enums::job_skill::APPRAISAL,
df::enums::job_skill::ORGANIZATION,
df::enums::job_skill::RECORD_KEEPING,
static const int responsibility_penalties[] = {
0, /* LAW_MAKING */
0, /* LAW_ENFORCEMENT */
3000, /* RECEIVE_DIPLOMATS */
0, /* MEET_WORKERS */
1000, /* MANAGE_PRODUCTION */
3000, /* TRADE */
1000, /* ACCOUNTING */
0, /* ESTABLISH_COLONY_TRADE_AGREEMENTS */
0, /* MAKE_INTRODUCTIONS */
0, /* MAKE_PEACE_AGREEMENTS */
0, /* MAKE_TOPIC_AGREEMENTS */
0, /* COLLECT_TAXES */
0, /* ESCORT_TAX_COLLECTOR */
0, /* EXECUTIONS */
0, /* TAME_EXOTICS */
0, /* RELIGION */
0, /* ATTACK_ENEMIES */
0, /* PATROL_TERRITORY */
0, /* MILITARY_GOALS */
0, /* MILITARY_STRATEGY */
0, /* UPGRADE_SQUAD_EQUIPMENT */
0, /* EQUIPMENT_MANIFESTS */
0, /* SORT_AMMUNITION */
0, /* BUILD_MORALE */
5000 /* HEALTH_MANAGEMENT */
};
struct dwarf_info_t
{
int highest_skill;
int total_skill;
bool is_best_noble;
int mastery_penalty;
int assigned_jobs;
dwarf_state state;
bool has_exclusive_labor;
int noble_penalty; // penalty for assignment due to noble status
bool medical; // this dwarf has medical responsibility
bool trader; // this dwarf has trade responsibility
};
static bool isOptionEnabled(unsigned flag)
@ -683,6 +717,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
bool has_butchers = false;
bool has_fishery = false;
bool trader_requested = false;
for (int i = 0; i < world->buildings.all.size(); ++i)
{
@ -695,7 +730,14 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
has_butchers = true;
if (df::enums::workshop_type::Fishery == subType)
has_fishery = true;
}
}
else if (df::enums::building_type::TradeDepot == type)
{
df::building_tradedepotst* depot = (df::building_tradedepotst*) build;
trader_requested = depot->flags.bits.trader_requested;
if (print_debug)
out.print("Trade depot found and trader requested, trader will be excluded from all labors.\n");
}
}
for (int i = 0; i < world->units.all.size(); ++i)
@ -714,15 +756,49 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
std::vector<dwarf_info_t> dwarf_info(n_dwarfs);
std::vector<int> best_noble(ARRAY_COUNT(noble_skills));
std::vector<int> highest_noble_skill(ARRAY_COUNT(noble_skills));
std::vector<int> highest_noble_experience(ARRAY_COUNT(noble_skills));
// Find total skill and highest skill for each dwarf. More skilled dwarves shouldn't be used for minor tasks.
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
assert(dwarfs[dwarf]->status.souls.size() > 0);
// assert(dwarfs[dwarf]->status.souls.size() > 0);
// assert fails can cause DF to crash, so don't do that
if (dwarfs[dwarf]->status.souls.size() <= 0)
continue;
// compute noble penalty
int noble_penalty = 0;
df::historical_figure* hf = df::historical_figure::find(dwarfs[dwarf]->hist_figure_id);
for (int i = 0; i < hf->entity_links.size(); i++) {
df::histfig_entity_link* hfelink = hf->entity_links.at(i);
if (hfelink->getType() == df::histfig_entity_link_type::POSITION) {
df::histfig_entity_link_positionst *epos =
(df::histfig_entity_link_positionst*) hfelink;
df::historical_entity* entity = df::historical_entity::find(epos->entity_id);
if (!entity)
continue;
df::entity_position_assignment* assignment = binsearch_in_vector(entity->positions.assignments, epos->assignment_id);
if (!assignment)
continue;
df::entity_position* position = binsearch_in_vector(entity->positions.own, assignment->position_id);
if (!position)
continue;
for (int n = 0; n < 25; n++)
if (position->responsibilities[n])
noble_penalty += responsibility_penalties[n];
if (position->responsibilities[df::entity_position_responsibility::HEALTH_MANAGEMENT])
dwarf_info[dwarf].medical = true;
if (position->responsibilities[df::entity_position_responsibility::TRADE])
dwarf_info[dwarf].trader = true;
}
dwarf_info[dwarf].noble_penalty = noble_penalty;
}
for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s != dwarfs[dwarf]->status.souls[0]->skills.end(); s++)
{
@ -733,30 +809,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
int skill_level = (*s)->rating;
int skill_experience = (*s)->experience;
// Track the dwarfs with the best Appraisal, Organization, and Record Keeping skills.
// They are likely to have appointed noble positions, so should be kept free where possible.
int noble_skill_id = -1;
for (int i = 0; i < ARRAY_COUNT(noble_skills); i++)
{
if (skill == noble_skills[i])
noble_skill_id = i;
}
if (noble_skill_id >= 0)
{
assert(noble_skill_id < ARRAY_COUNT(noble_skills));
if (highest_noble_skill[noble_skill_id] < skill_level ||
(highest_noble_skill[noble_skill_id] == skill_level &&
highest_noble_experience[noble_skill_id] < skill_experience))
{
highest_noble_skill[noble_skill_id] = skill_level;
highest_noble_experience[noble_skill_id] = skill_experience;
best_noble[noble_skill_id] = dwarf;
}
}
// Track total & highest skill among normal/medical skills. (We don't care about personal or social skills.)
if (skill_class != df::enums::job_skill_class::Normal && skill_class != df::enums::job_skill_class::Medical)
@ -768,24 +820,13 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
}
}
// Mark the best nobles, so we try to keep them non-busy. (It would be better to find the actual assigned nobles.)
for (int i = 0; i < ARRAY_COUNT(noble_skills); i++)
{
assert(best_noble[i] >= 0);
assert(best_noble[i] < n_dwarfs);
dwarf_info[best_noble[i]].is_best_noble = true;
}
// Calculate a base penalty for using each dwarf for a task he isn't good at.
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
dwarf_info[dwarf].mastery_penalty -= 40 * dwarf_info[dwarf].highest_skill;
dwarf_info[dwarf].mastery_penalty -= 10 * dwarf_info[dwarf].total_skill;
if (dwarf_info[dwarf].is_best_noble)
dwarf_info[dwarf].mastery_penalty -= 250;
dwarf_info[dwarf].mastery_penalty -= dwarf_info[dwarf].noble_penalty;
for (int labor = ENUM_FIRST_ITEM(unit_labor); labor <= ENUM_LAST_ITEM(unit_labor); labor++)
{
@ -1032,6 +1073,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
* Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs
* (sleeping, eating, on break, etc.) will have labors assigned, but will not be counted.
* Military and children/nobles will not have labors assigned.
* Dwarfs with the "health management" responsibility are always assigned DIAGNOSIS.
*/
for (int i = 0; i < candidates.size() && labor_infos[labor].active_dwarfs < max_dwarfs; i++)
{
@ -1047,6 +1089,10 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
preferred_dwarf = true;
if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive)
preferred_dwarf = true;
if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE)
preferred_dwarf = true;
if (dwarf_info[dwarf].trader && trader_requested)
continue;
if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf)
continue;
@ -1084,6 +1130,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
std::vector<int> hauler_ids;
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
if (dwarf_info[dwarf].trader && trader_requested)
continue;
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY)
hauler_ids.push_back(dwarf);
}

@ -13,4 +13,7 @@ DFHACK_PLUGIN(frozen frozen.cpp)
DFHACK_PLUGIN(dumpmats dumpmats.cpp)
#DFHACK_PLUGIN(tiles tiles.cpp)
DFHACK_PLUGIN(counters counters.cpp)
DFHACK_PLUGIN(stockcheck stockcheck.cpp)
DFHACK_PLUGIN(stripcaged stripcaged.cpp)
DFHACK_PLUGIN(rprobe rprobe.cpp)
DFHACK_PLUGIN(nestboxes nestboxes.cpp)

@ -0,0 +1,116 @@
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "DataDefs.h"
#include "df/world.h"
#include "df/ui.h"
#include "df/building_nest_boxst.h"
#include "df/building_type.h"
#include "df/global_objects.h"
#include "df/item.h"
#include "df/unit.h"
#include "df/building.h"
#include "df/items_other_id.h"
#include "df/creature_raw.h"
#include "modules/MapCache.h"
#include "modules/Items.h"
using std::vector;
using std::string;
using std::endl;
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::global::ui;
static command_result nestboxes(color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("nestboxes");
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
if (world && ui) {
commands.push_back(
PluginCommand("nestboxes", "Derp.",
nestboxes, false,
"Derp.\n"
)
);
}
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
static command_result nestboxes(color_ostream &out, vector <string> & parameters)
{
CoreSuspender suspend;
bool clean = false;
int dump_count = 0;
int good_egg = 0;
if (parameters.size() == 1 && parameters[0] == "clean")
{
clean = true;
}
for (int i = 0; i < world->buildings.all.size(); ++i)
{
df::building *build = world->buildings.all[i];
auto type = build->getType();
if (df::enums::building_type::NestBox == type)
{
bool needs_clean = false;
df::building_nest_boxst *nb = virtual_cast<df::building_nest_boxst>(build);
out << "Nestbox at (" << nb->x1 << "," << nb->y1 << ","<< nb->z << "): claimed-by " << nb->claimed_by << ", contained item count " << nb->contained_items.size() << " (" << nb->anon_1 << ")" << endl;
if (nb->contained_items.size() > 1)
needs_clean = true;
if (nb->claimed_by != -1)
{
df::unit* u = df::unit::find(nb->claimed_by);
if (u)
{
out << " Claimed by ";
if (u->name.has_name)
out << u->name.first_name << ", ";
df::creature_raw *raw = df::global::world->raws.creatures.all[u->race];
out << raw->creature_id
<< ", pregnancy timer " << u->relations.pregnancy_timer << endl;
if (u->relations.pregnancy_timer > 0)
needs_clean = false;
}
}
for (int j = 1; j < nb->contained_items.size(); j++)
{
df::item* item = nb->contained_items[j]->item;
if (needs_clean) {
if (clean && !item->flags.bits.dump)
{
item->flags.bits.dump = 1;
dump_count += item->getStackSize();
}
} else {
good_egg += item->getStackSize();
}
}
}
}
if (clean)
{
out << dump_count << " eggs dumped." << endl;
}
out << good_egg << " fertile eggs found." << endl;
return CR_OK;
}

@ -0,0 +1,163 @@
// Produces a list of materials available on the map.
// Options:
// -a : show unrevealed tiles
// -p : don't show plants
// -s : don't show slade
// -t : don't show demon temple
//#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <map>
#include <algorithm>
#include <vector>
using namespace std;
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/MapCache.h"
#include "MiscUtils.h"
#include "DataDefs.h"
#include "df/world.h"
#include "df/world_data.h"
#include "df/world_region_details.h"
#include "df/world_geo_biome.h"
#include "df/world_geo_layer.h"
#include "df/inclusion_type.h"
#include "df/viewscreen_choose_start_sitest.h"
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::coord2d;
command_result rprobe (color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("rprobe");
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"rprobe", "Display assorted region information from embark screen",
rprobe, false,
"Display assorted region information from embark screen\n"
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
command_result rprobe (color_ostream &out, vector <string> & parameters)
{
CoreSuspender suspend;
bool set = false;
int to_set, set_field, set_val;
// Embark screen active: estimate using world geology data
VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, Core::getTopViewscreen());
if (!screen)
return CR_WRONG_USAGE;
if (!world || !world->world_data)
{
out.printerr("World data is not available.\n");
return CR_FAILURE;
}
if (parameters.size() == 2)
{
if (parameters[0] == "wet")
set_field = 0;
else if (parameters[0] == "veg")
set_field = 1;
else if (parameters[0] == "tem")
set_field = 2;
else if (parameters[0] == "evi")
set_field = 3;
else if (parameters[0] == "hil")
set_field = 4;
else if (parameters[0] == "sav")
set_field = 5;
else if (parameters[0] == "sal")
set_field = 6;
else
return CR_WRONG_USAGE;
if (screen->biome_highlighted)
to_set = screen->biome_idx;
else
to_set = 0;
set = true;
set_val = atoi(parameters[1].c_str());
}
df::world_data *data = world->world_data;
coord2d cur_region = screen->region_pos;
// Compute biomes
for (int i = 0; i < screen->biome_rgn.size(); i++)
{
coord2d rg = screen->biome_rgn[i];
df::world_data::T_region_map* rd = &data->region_map[rg.x][rg.y];
if (set && i == to_set) {
if (set_field == 0)
rd->wetness = set_val;
else if (set_field == 1)
rd->vegetation = set_val;
else if (set_field == 2)
rd->temperature = set_val;
else if (set_field == 3)
rd->evilness = set_val;
else if (set_field == 4)
rd->hilliness = set_val;
else if (set_field == 5)
rd->savagery = set_val;
else if (set_field == 6)
rd->saltiness = set_val;
}
out << i << ": x = " << rg.x << ", y = " << rg.y;
out <<
" region_id: " << rd->region_id <<
" geo_index: " << rd->geo_index <<
" landmass_id: " << rd->landmass_id <<
" flags: " << hex << rd->flags.as_int() << dec << endl;
out <<
"wet: " << rd->wetness << " " <<
"veg: " << rd->vegetation << " " <<
"tem: " << rd->temperature << " " <<
"evi: " << rd->evilness << " " <<
"hil: " << rd->hilliness << " " <<
"sav: " << rd->savagery << " " <<
"sal: " << rd->saltiness;
int32_t *p = (int32_t *)rd;
int c = sizeof(*rd) / sizeof(int32_t);
for (int j = 0; j < c; j++) {
if (j % 8 == 0)
out << endl << setfill('0') << setw(8) << hex << (int)(rd+j) << ": ";
out << " " << setfill('0') << setw(8) << hex << p[j];
}
out << setfill(' ') << setw(0) << dec << endl;
}
return CR_OK;
}

@ -0,0 +1,278 @@
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "DataDefs.h"
#include "df/world.h"
#include "df/ui.h"
#include "df/building_stockpilest.h"
#include "df/global_objects.h"
#include "df/item.h"
#include "df/unit.h"
#include "df/building.h"
#include "df/items_other_id.h"
#include "df/item_stockpile_ref.h"
#include "modules/MapCache.h"
#include "modules/Items.h"
using std::vector;
using std::string;
using std::endl;
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::global::ui;
using df::global::selection_rect;
using df::building_stockpilest;
static command_result copystock(color_ostream &out, vector <string> & parameters);
static command_result stockcheck(color_ostream &out, vector <string> & parameters);
static bool copystock_guard(df::viewscreen *top);
DFHACK_PLUGIN("stockcheck");
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
if (world && ui) {
commands.push_back(
PluginCommand("stockcheck", "Check for unprotected rottable items.",
stockcheck, false,
"Scan world for items that are susceptible to rot. Currently just lists the items.\n"
)
);
}
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
struct StockpileInfo {
building_stockpilest* sp;
int size;
int free;
int x1, x2, y1, y2, z;
public:
StockpileInfo(building_stockpilest *sp_) : sp(sp_)
{
MapExtras::MapCache mc;
z = sp_->z;
x1 = sp_->room.x;
x2 = sp_->room.x + sp_->room.width;
y1 = sp_->room.y;
y2 = sp_->room.y + sp_->room.height;
int e = 0;
size = 0;
free = 0;
for (int y = y1; y < y2; y++)
for (int x = x1; x < x2; x++)
if (sp_->room.extents[e++] == 1)
{
size++;
DFCoord cursor (x,y,z);
uint32_t blockX = x / 16;
uint32_t tileX = x % 16;
uint32_t blockY = y / 16;
uint32_t tileY = y % 16;
MapExtras::Block * b = mc.BlockAt(cursor/16);
if(b && b->is_valid())
{
auto &block = *b->getRaw();
df::tile_occupancy &occ = block.occupancy[tileX][tileY];
if (!occ.bits.item)
free++;
}
}
}
bool isFull() { return free == 0; }
bool canHold(df::item *i)
{
return false;
}
bool inStockpile(df::item *i)
{
df::item *container = Items::getContainer(i);
if (container)
return inStockpile(container);
if (i->pos.z != z) return false;
if (i->pos.x < x1 || i->pos.x >= x2 ||
i->pos.y < y1 || i->pos.y >= y2) return false;
int e = (i->pos.x - x1) + (i->pos.y - y1) * sp->room.width;
return sp->room.extents[e] == 1;
}
int getId() { return sp->id; }
};
static command_result stockcheck(color_ostream &out, vector <string> & parameters)
{
CoreSuspender suspend;
std::vector<StockpileInfo*> stockpiles;
for (int i = 0; i < world->buildings.all.size(); ++i)
{
df::building *build = world->buildings.all[i];
auto type = build->getType();
if (df::enums::building_type::Stockpile == type)
{
building_stockpilest *sp = virtual_cast<building_stockpilest>(build);
StockpileInfo *spi = new StockpileInfo(sp);
stockpiles.push_back(spi);
}
}
std::vector<df::item*> &items = world->items.other[items_other_id::ANY_FREE];
// Precompute a bitmask with the bad flags
df::item_flags bad_flags;
bad_flags.whole = 0;
#define F(x) bad_flags.bits.x = true;
F(dump); F(forbid); F(garbage_collect);
F(hostile); F(on_fire); F(rotten); F(trader);
F(in_building); F(construction); F(artifact1);
F(spider_web); F(owned); F(in_job);
#undef F
for (size_t i = 0; i < items.size(); i++)
{
df::item *item = items[i];
if (item->flags.whole & bad_flags.whole)
continue;
// we really only care about MEAT, FISH, FISH_RAW, PLANT, CHEESE, FOOD, and EGG
df::item_type typ = item->getType();
if (typ != df::enums::item_type::MEAT &&
typ != df::enums::item_type::FISH &&
typ != df::enums::item_type::FISH_RAW &&
typ != df::enums::item_type::PLANT &&
typ != df::enums::item_type::CHEESE &&
typ != df::enums::item_type::FOOD &&
typ != df::enums::item_type::EGG)
continue;
df::item *container = 0;
df::unit *holder = 0;
df::building *building = 0;
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:
container = ref->getItem();
break;
case general_ref_type::UNIT_HOLDER:
holder = ref->getUnit();
break;
case general_ref_type::BUILDING_HOLDER:
building = ref->getBuilding();
break;
}
}
df::item *nextcontainer = container;
df::item *lastcontainer = 0;
while(nextcontainer) {
df::item *thiscontainer = nextcontainer;
nextcontainer = 0;
for (size_t i = 0; i < thiscontainer->itemrefs.size(); i++)
{
df::general_ref *ref = thiscontainer->itemrefs[i];
switch (ref->getType())
{
case general_ref_type::CONTAINED_IN_ITEM:
lastcontainer = nextcontainer = ref->getItem();
break;
case general_ref_type::UNIT_HOLDER:
holder = ref->getUnit();
break;
case general_ref_type::BUILDING_HOLDER:
building = ref->getBuilding();
break;
}
}
}
if (holder)
continue; // carried items do not rot as far as i know
if (building) {
df::building_type btype = building->getType();
if (btype == df::enums::building_type::TradeDepot ||
btype == df::enums::building_type::Wagon)
continue; // items in trade depot or the embark wagon do not rot
if (typ == df::enums::item_type::EGG && btype ==df::enums::building_type::NestBox)
continue; // eggs in nest box do not rot
}
int canHoldCount = 0;
StockpileInfo *current = 0;
for (int idx = 0; idx < stockpiles.size(); idx++)
{
StockpileInfo *spi = stockpiles[idx];
if (spi->canHold(item)) canHoldCount++;
if (spi->inStockpile(item)) current=spi;
}
if (current)
continue;
std::string description;
item->getItemDescription(&description, 0);
out << " * " << description;
if (container) {
std::string containerDescription;
container->getItemDescription(&containerDescription, 0);
out << ", in container " << containerDescription;
if (lastcontainer) {
std::string lastcontainerDescription;
lastcontainer->getItemDescription(&lastcontainerDescription, 0);
out << ", in container " << lastcontainerDescription;
}
}
if (holder) {
out << ", carried";
}
if (building) {
out << ", in building " << building->id << " (type=" << building->getType() << ")";
}
out << ", flags=" << std::hex << item->flags.whole << std::dec;
out << endl;
}
return CR_OK;
}

@ -0,0 +1,103 @@
#include <iostream>
#include <iomanip>
#include <climits>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <ctime>
#include <cstdio>
using namespace std;
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Units.h"
#include "modules/Maps.h"
#include "modules/Gui.h"
#include "modules/World.h"
#include "MiscUtils.h"
#include <df/ui.h>
#include "df/world.h"
#include "df/world_raws.h"
#include "df/building_def.h"
#include "df/unit_inventory_item.h"
#include <df/creature_raw.h>
#include <df/caste_raw.h>
using std::vector;
using std::string;
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::global::cursor;
using df::global::ui;
using namespace DFHack::Gui;
command_result df_stripcaged(color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("stripcaged");
// check if contained in item (e.g. animals in cages)
bool isContainedInItem(df::unit* unit)
{
bool contained = false;
for (size_t r=0; r < unit->refs.size(); r++)
{
df::general_ref * ref = unit->refs[r];
auto rtype = ref->getType();
if(rtype == df::general_ref_type::CONTAINED_IN_ITEM)
{
contained = true;
break;
}
}
return contained;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"stripcaged", "strip caged units of all items",
df_stripcaged, false,
"Clears forbid and sets dump for the inventories of all caged units."
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
command_result df_stripcaged(color_ostream &out, vector <string> & parameters)
{
CoreSuspender suspend;
size_t count = 0;
for (size_t i=0; i < world->units.all.size(); i++)
{
df::unit* unit = world->units.all[i];
if (isContainedInItem(unit))
{
for (size_t j=0; j < unit->inventory.size(); j++)
{
df::unit_inventory_item* uii = unit->inventory[j];
if (uii->item)
{
uii->item->flags.bits.forbid = 0;
uii->item->flags.bits.dump = 1;
count++;
}
}
}
}
out << count << " items marked for dumping" << endl;
return CR_OK;
}

@ -220,8 +220,26 @@ command_result df_probe (color_ostream &out, vector <string> & parameters)
out.print("temperature1: %d U\n",mc.temperature1At(cursor));
out.print("temperature2: %d U\n",mc.temperature2At(cursor));
int offset = block.region_offset[des.bits.biome];
df::coord2d region_pos = block.region_pos + df::coord2d ((offset % 3) - 1, (offset / 3) -1);
df::world_data::T_region_map* biome =
&world->world_data->region_map[region_pos.x][region_pos.y];
int sav = biome->savagery;
int evi = biome->evilness;
int sindex = sav > 65 ? 2 : sav < 33 ? 0 : 1;
int eindex = evi > 65 ? 2 : evi < 33 ? 0 : 1;
int surr = sindex + eindex * 3;
char* surroundings[] = { "Serene", "Mirthful", "Joyous Wilds", "Calm", "Wilderness", "Untamed Wilds", "Sinister", "Haunted", "Terrifying" };
// biome, geolayer
out << "biome: " << des.bits.biome << std::endl;
out << "biome: " << des.bits.biome << " (" <<
"region id=" << biome->region_id << ", " <<
surroundings[surr] << ", " <<
"savagery " << biome->savagery << ", " <<
"evilness " << biome->evilness << ")" << std::endl;
out << "geolayer: " << des.bits.geolayer_index
<< std::endl;
int16_t base_rock = mc.layerMaterialAt(cursor);

@ -366,6 +366,8 @@ void cageInfo(color_ostream & out, df::building* building, bool verbose);
void chainInfo(color_ostream & out, df::building* building, bool verbose);
bool isBuiltCageAtPos(df::coord pos);
bool isInBuiltCageRoom(df::unit*);
bool isNaked(df::unit *);
bool isTamable(df::unit *);
int32_t getUnitAge(df::unit* unit)
{
@ -620,6 +622,20 @@ bool isTrainableHunting(df::unit* unit)
return false;
}
bool isTamable(df::unit* unit)
{
df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
size_t sizecas = raw->caste.size();
for (size_t j = 0; j < sizecas;j++)
{
df::caste_raw *caste = raw->caste[j];
if(caste->flags.is_set(caste_raw_flags::PET) ||
caste->flags.is_set(caste_raw_flags::PET_EXOTIC))
return true;
}
return false;
}
bool isMale(df::unit* unit)
{
return unit->sex == 1;
@ -644,6 +660,12 @@ bool hasValidMapPos(df::unit* unit)
return false;
}
bool isNaked(df::unit* unit)
{
return (unit->inventory.empty());
}
int getUnitIndexFromId(df::unit* unit_)
{
for (size_t i=0; i < world->units.all.size(); i++)
@ -1748,6 +1770,10 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
bool find_not_milkable = false;
bool find_named = false;
bool find_not_named = false;
bool find_naked = false;
bool find_not_naked = false;
bool find_tamable = false;
bool find_not_tamable = false;
bool find_agemin = false;
bool find_agemax = false;
@ -2126,6 +2152,24 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
find_not_egglayer = true;
invert_filter=false;
}
else if(p == "naked" && !invert_filter)
{
find_naked = true;
}
else if(p == "naked" && invert_filter)
{
find_not_naked = true;
invert_filter=false;
}
else if(p == "tamable" && !invert_filter)
{
find_tamable = true;
}
else if(p == "tamable" && invert_filter)
{
find_not_tamable = true;
invert_filter=false;
}
else if(p == "grazer" && !invert_filter)
{
find_grazer = true;
@ -2393,6 +2437,10 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
|| (find_not_female && isFemale(unit))
|| (find_named && !unit->name.has_name)
|| (find_not_named && unit->name.has_name)
|| (find_naked && !isNaked(unit))
|| (find_not_naked && isNaked(unit))
|| (find_tamable && !isTamable(unit))
|| (find_not_tamable && isTamable(unit))
|| (find_trainable_war && (isWar(unit) || isHunter(unit) || !isTrainableWar(unit)))
|| (find_not_trainable_war && isTrainableWar(unit)) // hm, is this check enough?
|| (find_trainable_hunting && (isWar(unit) || isHunter(unit) || !isTrainableHunting(unit)))