From 9e1e34f69b63bf74fbe73b73e6f0837b9dd14940 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Wed, 18 Apr 2012 00:16:44 -0500 Subject: [PATCH 01/18] Created new stockcheck plugin --- plugins/devel/CMakeLists.txt | 1 + plugins/devel/stockcheck.cpp | 121 +++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 plugins/devel/stockcheck.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 9e7e7af6f..736a5194f 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -15,4 +15,5 @@ DFHACK_PLUGIN(dumpmats dumpmats.cpp) #DFHACK_PLUGIN(tiles tiles.cpp) DFHACK_PLUGIN(regrass regrass.cpp) DFHACK_PLUGIN(counters counters.cpp) +DFHACK_PLUGIN(stockcheck stockcheck.cpp) diff --git a/plugins/devel/stockcheck.cpp b/plugins/devel/stockcheck.cpp new file mode 100644 index 000000000..ab301a9dc --- /dev/null +++ b/plugins/devel/stockcheck.cpp @@ -0,0 +1,121 @@ +#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/viewscreen_dwarfmodest.h" +#include "df/item.h" +#include "df/items_other_id.h" +#include "modules/MapCache.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 & parameters); +static command_result stockcheck(color_ostream &out, vector & parameters); +static bool copystock_guard(df::viewscreen *top); + +DFHACK_PLUGIN("stockcheck"); + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) +{ + if (world && ui) { + commands.push_back( + PluginCommand("stockcheck", "Check for stockpile adequacy.", + stockcheck, false, + "Scan world for unstockpiled items and verify stockpiles exist for them.\n" + ) + ); + } + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + +static command_result stockcheck(color_ostream &out, vector & parameters) +{ + CoreSuspender suspend; + MapExtras::MapCache mc; + + 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(build); + df::stockpile_settings st = sp->settings; + out << "Stockpile " << sp->stockpile_number << ":\n"; + out << " width=" << build->room.width << " height= " << build->room.height << endl; + + int x1 = build->room.x; + int x2 = build->room.x + build->room.width; + int y1 = build->room.y; + int y2 = build->room.y + build->room.height; + int e = 0; + int size = 0, free = 0; + for (int x = x1; x < x2; x++) + for (int y = y1; y < y2; y++) + if (build->room.extents[e++] == 1) + { + size++; + DFCoord cursor (x,y,build->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++; + } + } + + out << " size=" << size << " free= " << free << endl; + } + } + + std::vector &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); +#undef F + + for (size_t i = 0; i < items.size(); i++) + { + df::item *item = items[i]; + if (item->flags.whole & bad_flags.whole) + continue; + + + } + out << "Stockcheck done" << endl; + + return CR_OK; +} From 2dc3aa1b892ade4caf19eb264b6599b1deed09ff Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Wed, 18 Apr 2012 11:33:17 -0500 Subject: [PATCH 02/18] danger of rotting) --- plugins/devel/stockcheck.cpp | 229 +++++++++++++++++++++++++++++------ 1 file changed, 193 insertions(+), 36 deletions(-) diff --git a/plugins/devel/stockcheck.cpp b/plugins/devel/stockcheck.cpp index ab301a9dc..8dc44e98a 100644 --- a/plugins/devel/stockcheck.cpp +++ b/plugins/devel/stockcheck.cpp @@ -8,10 +8,14 @@ #include "df/ui.h" #include "df/building_stockpilest.h" #include "df/global_objects.h" -#include "df/viewscreen_dwarfmodest.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; @@ -35,9 +39,9 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector 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 & parameters) { CoreSuspender suspend; - MapExtras::MapCache mc; + + std::vector stockpiles; for (int i = 0; i < world->buildings.all.size(); ++i) { @@ -61,38 +129,10 @@ static command_result stockcheck(color_ostream &out, vector & parameter if (df::enums::building_type::Stockpile == type) { building_stockpilest *sp = virtual_cast(build); - df::stockpile_settings st = sp->settings; - out << "Stockpile " << sp->stockpile_number << ":\n"; - out << " width=" << build->room.width << " height= " << build->room.height << endl; - - int x1 = build->room.x; - int x2 = build->room.x + build->room.width; - int y1 = build->room.y; - int y2 = build->room.y + build->room.height; - int e = 0; - int size = 0, free = 0; - for (int x = x1; x < x2; x++) - for (int y = y1; y < y2; y++) - if (build->room.extents[e++] == 1) - { - size++; - DFCoord cursor (x,y,build->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++; - } - } - - out << " size=" << size << " free= " << free << endl; + StockpileInfo *spi = new StockpileInfo(sp); + stockpiles.push_back(spi); } + } std::vector &items = world->items.other[items_other_id::ANY_FREE]; @@ -105,6 +145,7 @@ static command_result stockcheck(color_ostream &out, vector & parameter 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++) @@ -113,9 +154,125 @@ static command_result stockcheck(color_ostream &out, vector & parameter 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; } - out << "Stockcheck done" << endl; return CR_OK; } + From e0bb5bf53b7466542413328c36f82cfef709d0fe Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Fri, 20 Apr 2012 14:22:20 -0500 Subject: [PATCH 03/18] Add "naked" (has no inventory) predicate to zone (useful for mass pitting) --- plugins/zone.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/plugins/zone.cpp b/plugins/zone.cpp index ee6abf327..fe8a4f759 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -366,6 +366,7 @@ 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 *); int32_t getUnitAge(df::unit* unit) { @@ -644,6 +645,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 +1755,8 @@ command_result df_zone (color_ostream &out, vector & 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_agemin = false; bool find_agemax = false; @@ -2126,6 +2135,15 @@ command_result df_zone (color_ostream &out, vector & 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 == "grazer" && !invert_filter) { find_grazer = true; @@ -2393,6 +2411,8 @@ command_result df_zone (color_ostream &out, vector & 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_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))) From ad73b8c737190d98797e88be4a121deb3bae3a5f Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Fri, 20 Apr 2012 14:38:50 -0500 Subject: [PATCH 04/18] Add "tamable" for animals that can be tamed (PET or PET_EXOTIC in raws) --- plugins/zone.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/plugins/zone.cpp b/plugins/zone.cpp index fe8a4f759..4fbf9b69b 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -367,6 +367,7 @@ 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) { @@ -621,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; @@ -1757,6 +1772,8 @@ command_result df_zone (color_ostream &out, vector & parameters) 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; @@ -2144,6 +2161,15 @@ command_result df_zone (color_ostream &out, vector & parameters) 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; @@ -2413,6 +2439,8 @@ command_result df_zone (color_ostream &out, vector & parameters) || (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))) From c104f822a4ed01324ce98bfbeffbbf026dbc2770 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sat, 21 Apr 2012 12:53:46 -0500 Subject: [PATCH 05/18] Move stripcaged to master branch, works fine. --- plugins/devel/CMakeLists.txt | 1 + plugins/devel/stripcaged.cpp | 103 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 plugins/devel/stripcaged.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index fd25962b2..ef2816608 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -14,4 +14,5 @@ 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) diff --git a/plugins/devel/stripcaged.cpp b/plugins/devel/stripcaged.cpp new file mode 100644 index 000000000..7e492cb01 --- /dev/null +++ b/plugins/devel/stripcaged.cpp @@ -0,0 +1,103 @@ + +#include +#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/Units.h" +#include "modules/Maps.h" +#include "modules/Gui.h" +#include "modules/World.h" +#include "MiscUtils.h" + +#include +#include "df/world.h" +#include "df/world_raws.h" +#include "df/building_def.h" +#include "df/unit_inventory_item.h" +#include +#include + +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 & 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 &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 & 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; +} From f5644f385bb5bf101444729cbaa6cd64ae343007 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sat, 21 Apr 2012 23:22:21 -0500 Subject: [PATCH 06/18] Probe now displays biome savagery and evilness. Devel plugin bprobe (also in this commit) goes digging around in region data at embark screen, not really useful for public consumption. --- plugins/devel/CMakeLists.txt | 1 + plugins/devel/rprobe.cpp | 107 +++++++++++++++++++++++++++++++++++ plugins/probe.cpp | 11 +++- 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 plugins/devel/rprobe.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index ef2816608..70daa7b2c 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -15,4 +15,5 @@ DFHACK_PLUGIN(dumpmats dumpmats.cpp) DFHACK_PLUGIN(counters counters.cpp) DFHACK_PLUGIN(stockcheck stockcheck.cpp) DFHACK_PLUGIN(stripcaged stripcaged.cpp) +DFHACK_PLUGIN(rprobe rprobe.cpp) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp new file mode 100644 index 000000000..4378c547c --- /dev/null +++ b/plugins/devel/rprobe.cpp @@ -0,0 +1,107 @@ +// 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 +#include +#include +#include +#include +#include + +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 & parameters); + +DFHACK_PLUGIN("rprobe"); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &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 & parameters) +{ + CoreSuspender suspend; + + // 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; + } + + 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]; + out << i << ": x = " << rg.x << ", y = " << rg.y; + + df::world_data::T_region_map* rd = &data->region_map[rg.x][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 << + " sav: " << rd->savagery << + " evil: " << rd->evilness; + + 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 << dec << endl; + + } + + return CR_OK; +} diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 4e041f180..058a9e5ca 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -210,8 +210,17 @@ command_result df_probe (color_ostream &out, vector & 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]; + // biome, geolayer - out << "biome: " << des.bits.biome << std::endl; + out << "biome: " << des.bits.biome << " (" << + "region id=" << biome->region_id << ", " << + "savagery " << biome->savagery << ", " << + "evilness " << biome->evilness << ")" << std::endl; out << "geolayer: " << des.bits.geolayer_index << std::endl; int16_t base_rock = mc.baseMaterialAt(cursor); From 26cec5f60c120b7ea4ce4592ef926bd11f2c2cc4 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sun, 22 Apr 2012 10:17:14 -0500 Subject: [PATCH 07/18] Probe now displays the "surroundings" name for the biome (e.g. Joyous Wilds, or Terrifying). Rprobe can be used from the embark screen to set the evilness of the selected biome; use with due care. --- plugins/devel/rprobe.cpp | 22 +++++++++++++++++++++- plugins/probe.cpp | 9 +++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp index 4378c547c..8829b9a4f 100644 --- a/plugins/devel/rprobe.cpp +++ b/plugins/devel/rprobe.cpp @@ -61,6 +61,9 @@ command_result rprobe (color_ostream &out, vector & parameters) { CoreSuspender suspend; + bool set = false; + int to_set, set_val; + // Embark screen active: estimate using world geology data VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, Core::getTopViewscreen()); @@ -73,6 +76,17 @@ command_result rprobe (color_ostream &out, vector & parameters) return CR_FAILURE; } + if (parameters.size() == 1) + { + if (!screen->biome_highlighted) + { + return CR_WRONG_USAGE; + } + set = true; + to_set = screen->biome_idx; + set_val = atoi(parameters[0].c_str()); + } + df::world_data *data = world->world_data; coord2d cur_region = screen->region_pos; @@ -80,10 +94,16 @@ command_result rprobe (color_ostream &out, vector & parameters) for (int i = 0; i < screen->biome_rgn.size(); i++) { coord2d rg = screen->biome_rgn[i]; - out << i << ": x = " << rg.x << ", y = " << rg.y; df::world_data::T_region_map* rd = &data->region_map[rg.x][rg.y]; + if (set && i == to_set) { + rd->evilness = set_val; + out << "* Set evilness to " << set_val << endl; + } + + out << i << ": x = " << rg.x << ", y = " << rg.y; + out << " region_id: " << rd->region_id << " geo_index: " << rd->geo_index << diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 058a9e5ca..06b1f968e 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -216,9 +216,18 @@ command_result df_probe (color_ostream &out, vector & parameters) 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 << " (" << "region id=" << biome->region_id << ", " << + surroundings[surr] << ", " << "savagery " << biome->savagery << ", " << "evilness " << biome->evilness << ")" << std::endl; out << "geolayer: " << des.bits.geolayer_index From e06c80f69025850b0c6e6443759be2dc32db9d04 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sun, 22 Apr 2012 23:36:13 -0500 Subject: [PATCH 08/18] Fix minor ostream munging glitch in rprobe. --- plugins/devel/rprobe.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp index 8829b9a4f..c8d7e1b1a 100644 --- a/plugins/devel/rprobe.cpp +++ b/plugins/devel/rprobe.cpp @@ -119,7 +119,7 @@ command_result rprobe (color_ostream &out, vector & parameters) out << endl << setfill('0') << setw(8) << hex << (int)(rd+j) << ": "; out << " " << setfill('0') << setw(8) << hex << p[j]; } - out << dec << endl; + out << setfill(' ') << setw(0) << dec << endl; } From 8dcf2db316da4a64d6bb5207fdfd2546071fad41 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 23 Apr 2012 09:45:00 -0500 Subject: [PATCH 09/18] rs. Use at your own peril, as strangeness may ensue. --- plugins/devel/rprobe.cpp | 62 +++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp index c8d7e1b1a..7a091a962 100644 --- a/plugins/devel/rprobe.cpp +++ b/plugins/devel/rprobe.cpp @@ -62,7 +62,7 @@ command_result rprobe (color_ostream &out, vector & parameters) CoreSuspender suspend; bool set = false; - int to_set, set_val; + 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()); @@ -76,15 +76,33 @@ command_result rprobe (color_ostream &out, vector & parameters) return CR_FAILURE; } - if (parameters.size() == 1) + + if (parameters.size() == 2) { - if (!screen->biome_highlighted) - { + 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; - to_set = screen->biome_idx; - set_val = atoi(parameters[0].c_str()); + set_val = atoi(parameters[1].c_str()); } df::world_data *data = world->world_data; @@ -98,8 +116,20 @@ command_result rprobe (color_ostream &out, vector & parameters) df::world_data::T_region_map* rd = &data->region_map[rg.x][rg.y]; if (set && i == to_set) { - rd->evilness = set_val; - out << "* Set evilness to " << set_val << endl; + 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; @@ -108,10 +138,16 @@ command_result rprobe (color_ostream &out, vector & parameters) " region_id: " << rd->region_id << " geo_index: " << rd->geo_index << " landmass_id: " << rd->landmass_id << - " flags: " << hex << rd->flags.as_int() << dec << - " sav: " << rd->savagery << - " evil: " << rd->evilness; - + " 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++) { From ac0e4a15e8ed403c8f41dba695df196960a26820 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 23 Apr 2012 09:46:57 -0500 Subject: [PATCH 10/18] Rprobe can now be used to change any of the region_map paremeters. Use at your own peril, as strangeness may ensue. --- plugins/devel/rprobe.cpp | 62 +++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp index c8d7e1b1a..7a091a962 100644 --- a/plugins/devel/rprobe.cpp +++ b/plugins/devel/rprobe.cpp @@ -62,7 +62,7 @@ command_result rprobe (color_ostream &out, vector & parameters) CoreSuspender suspend; bool set = false; - int to_set, set_val; + 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()); @@ -76,15 +76,33 @@ command_result rprobe (color_ostream &out, vector & parameters) return CR_FAILURE; } - if (parameters.size() == 1) + + if (parameters.size() == 2) { - if (!screen->biome_highlighted) - { + 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; - to_set = screen->biome_idx; - set_val = atoi(parameters[0].c_str()); + set_val = atoi(parameters[1].c_str()); } df::world_data *data = world->world_data; @@ -98,8 +116,20 @@ command_result rprobe (color_ostream &out, vector & parameters) df::world_data::T_region_map* rd = &data->region_map[rg.x][rg.y]; if (set && i == to_set) { - rd->evilness = set_val; - out << "* Set evilness to " << set_val << endl; + 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; @@ -108,10 +138,16 @@ command_result rprobe (color_ostream &out, vector & parameters) " region_id: " << rd->region_id << " geo_index: " << rd->geo_index << " landmass_id: " << rd->landmass_id << - " flags: " << hex << rd->flags.as_int() << dec << - " sav: " << rd->savagery << - " evil: " << rd->evilness; - + " 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++) { From 5f5152f4498722d61e7c9c7f207ef3b5dfbda84c Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Thu, 26 Apr 2012 09:38:25 -0500 Subject: [PATCH 11/18] Autolabor: Instead of guessing who the nobles are based on noble skills, actually find out who they are and behave appropriately. Certain responsibilities are assigned various penalties to increase their holders' availability to perform noble functions. Also, holders of the "manage health" responsibility are always assigned the diagnose labor. The responsibility penalties are first-approximation and will likely need tuning. --- plugins/autolabor.cpp | 124 ++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 46 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index cfd9419db..73f8e7c27 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -23,6 +23,15 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include using std::string; using std::endl; @@ -446,21 +455,44 @@ 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 }; static bool isOptionEnabled(unsigned flag) @@ -714,15 +746,45 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) std::vector dwarf_info(n_dwarfs); - std::vector best_noble(ARRAY_COUNT(noble_skills)); - std::vector highest_noble_skill(ARRAY_COUNT(noble_skills)); - std::vector 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; + + } + } for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s != dwarfs[dwarf]->status.souls[0]->skills.end(); s++) { @@ -733,30 +795,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 +806,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 +1059,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++) { @@ -1040,6 +1068,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) assert(dwarf >= 0); assert(dwarf < n_dwarfs); + + bool preferred_dwarf = false; if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE) preferred_dwarf = true; @@ -1047,6 +1077,8 @@ 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 (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf) continue; From b4a8bcd8d02f0277d21e1da2c26201a48ccd559b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 19 Apr 2012 19:17:07 +0400 Subject: [PATCH 12/18] Refactor MapCache: make it parse everything that is known re tiles & mats. Conflicts: library/xml --- library/include/modules/MapCache.h | 249 +++++++++++++---- library/include/modules/Materials.h | 10 + library/modules/Maps.cpp | 410 ++++++++++++++++++++++++---- plugins/dig.cpp | 8 +- plugins/mapexport/mapexport.cpp | 4 +- plugins/probe.cpp | 58 ++-- plugins/prospector.cpp | 4 +- 7 files changed, 600 insertions(+), 143 deletions(-) diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index 8eac514be..4ddf0c984 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -31,12 +31,17 @@ distribution. #include #include #include "df/map_block.h" +#include "df/tile_bitmask.h" #include "df/block_square_event_mineralst.h" #include "df/construction.h" #include "df/item.h" using namespace DFHack; +namespace df { + struct world_region_details; +} + namespace MapExtras { @@ -50,6 +55,38 @@ inline bool is_valid_tile_coord(df::coord2d p) { return (p.x & ~15) == 0 && (p.y & ~15) == 0; } +class Block; + +class BlockInfo +{ + Block *mblock; + MapCache *parent; + df::map_block *block; + +public: + t_blockmaterials veinmats; + t_blockmaterials basemats; + t_blockmaterials grass; + std::map plants; + + df::feature_init *global_feature; + df::feature_init *local_feature; + + BlockInfo() + : mblock(NULL), parent(NULL), block(NULL), + global_feature(NULL), local_feature(NULL) {} + + void prepare(Block *mblock); + + t_matpair getBaseMaterial(df::tiletype tt, df::coord2d pos); + + static void SquashVeins(df::map_block *mb, t_blockmaterials & materials); + static void SquashFrozenLiquids (df::map_block *mb, tiletypes40d & frozen); + static void SquashRocks (df::map_block *mb, t_blockmaterials & materials, + std::vector< std::vector > * layerassign); + static void SquashGrass(df::map_block *mb, t_blockmaterials &materials); +}; + class DFHACK_EXPORT Block { public: @@ -62,39 +99,81 @@ public: //Arbitrary tag field for flood fills etc. int16_t &tag(df::coord2d p) { + if (!tags) init_tags(); return index_tile(tags, p); } + // Base layer + df::tiletype baseTiletypeAt(df::coord2d p) + { + if (!tiles) init_tiles(); + return index_tile(tiles->base_tiles,p); + } + t_matpair baseMaterialAt(df::coord2d p) + { + if (!basemats) init_tiles(true); + return t_matpair( + index_tile(basemats->mattype,p), + index_tile(basemats->matindex,p) + ); + } + bool isVeinAt(df::coord2d p) + { + using namespace df::enums::tiletype_material; + auto tm = tileMaterial(baseTiletypeAt(p)); + return tm == MINERAL; + } + bool isLayerAt(df::coord2d p) + { + using namespace df::enums::tiletype_material; + auto tm = tileMaterial(baseTiletypeAt(p)); + return tm == STONE || tm == SOIL; + } + int16_t veinMaterialAt(df::coord2d p) { - return index_tile(veinmats,p); + return isVeinAt(p) ? baseMaterialAt(p).mat_index : -1; } - int16_t baseMaterialAt(df::coord2d p) + int16_t layerMaterialAt(df::coord2d p) { - return index_tile(basemats,p); + if (!basemats) init_tiles(true); + return index_tile(basemats->layermat,p); } - df::tiletype BaseTileTypeAt(df::coord2d p) + // Static layer (base + constructions) + df::tiletype staticTiletypeAt(df::coord2d p) { - auto tt = index_tile(contiles,p); - if (tt != tiletype::Void) return tt; - tt = index_tile(icetiles,p); - if (tt != tiletype::Void) return tt; - return index_tile(rawtiles,p); + if (!tiles) init_tiles(); + if (tiles->con_info) + return index_tile(tiles->con_info->tiles,p); + return baseTiletypeAt(p); } - df::tiletype TileTypeAt(df::coord2d p) + t_matpair staticMaterialAt(df::coord2d p) { - return index_tile(rawtiles,p); + if (!basemats) init_tiles(true); + if (tiles->con_info) + return t_matpair( + index_tile(tiles->con_info->mattype,p), + index_tile(tiles->con_info->matindex,p) + ); + return baseMaterialAt(p); } - bool setTiletypeAt(df::coord2d p, df::tiletype tiletype) + bool hasConstructionAt(df::coord2d p) { - if(!valid) return false; - dirty_tiletypes = true; - //printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y); - index_tile(rawtiles,p) = tiletype; - return true; + if (!tiles) init_tiles(); + return tiles->con_info && + tiles->con_info->constructed.getassignment(p); } + df::tiletype tiletypeAt(df::coord2d p) + { + if (!block) return tiletype::Void; + if (tiles) + return index_tile(tiles->raw_tiles,p); + return index_tile(block->tiletype,p); + } + bool setTiletypeAt(df::coord2d, df::tiletype tt, bool force = false); + uint16_t temperature1At(df::coord2d p) { return index_tile(temp1,p); @@ -179,29 +258,29 @@ public: bool is_valid() { return valid; } df::map_block *getRaw() { return block; } + MapCache *getParent() { return parent; } + private: friend class MapCache; MapCache *parent; df::map_block *block; - static void SquashVeins(df::map_block *mb, t_blockmaterials & materials); - static void SquashFrozenLiquids (df::map_block *mb, tiletypes40d & frozen); - static void SquashConstructions (df::map_block *mb, tiletypes40d & constructions); - static void SquashRocks (df::map_block *mb, t_blockmaterials & materials, - std::vector< std::vector > * layerassign); - bool valid; bool dirty_designations:1; - bool dirty_tiletypes:1; + bool dirty_tiles:1; bool dirty_temperatures:1; bool dirty_blockflags:1; bool dirty_occupancies:1; DFCoord bcoord; - int16_t tags[16][16]; + // Custom tags for floodfill + typedef int16_t T_tags[16]; + T_tags *tags; + void init_tags(); + // On-ground item count info typedef int T_item_counts[16]; T_item_counts *item_counts; void init_item_counts(); @@ -209,30 +288,53 @@ private: bool addItemOnGround(df::item *item); bool removeItemOnGround(df::item *item); - tiletypes40d rawtiles; + struct ConInfo { + df::tile_bitmask constructed; + df::tiletype tiles[16][16]; + t_blockmaterials mattype; + t_blockmaterials matindex; + }; + struct TileInfo { + df::tile_bitmask frozen; + df::tile_bitmask dirty_raw; + df::tiletype raw_tiles[16][16]; + + ConInfo *con_info; + + df::tile_bitmask dirty_base; + df::tiletype base_tiles[16][16]; + + TileInfo(); + ~TileInfo(); + + void init_coninfo(); + }; + struct BasematInfo { + df::tile_bitmask dirty; + t_blockmaterials mattype; + t_blockmaterials matindex; + t_blockmaterials layermat; + + BasematInfo(); + }; + TileInfo *tiles; + BasematInfo *basemats; + void init_tiles(bool basemat = false); + void ParseTiles(TileInfo *tiles); + void ParseBasemats(TileInfo *tiles, BasematInfo *bmats); + designations40d designation; occupancies40d occupancy; t_blockflags blockflags; - t_blockmaterials veinmats; - t_blockmaterials basemats; t_temperatures temp1; t_temperatures temp2; - tiletypes40d contiles; // what's underneath constructions - tiletypes40d icetiles; // what's underneath ice }; class DFHACK_EXPORT MapCache { public: - MapCache() - { - valid = 0; - Maps::getSize(x_bmax, y_bmax, z_max); - x_tmax = x_bmax*16; y_tmax = y_bmax*16; - validgeo = Maps::ReadGeology(&layer_mats, &geoidx); - valid = true; - }; + MapCache(); ~MapCache() { trash(); @@ -251,19 +353,60 @@ class DFHACK_EXPORT MapCache df::tiletype baseTiletypeAt (DFCoord tilecoord) { - Block * b= BlockAtTile(tilecoord); - return b ? b->BaseTileTypeAt(tilecoord) : tiletype::Void; + Block *b = BlockAtTile(tilecoord); + return b ? b->baseTiletypeAt(tilecoord) : tiletype::Void; + } + t_matpair baseMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->baseMaterialAt(tilecoord) : t_matpair(); + } + int16_t veinMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->veinMaterialAt(tilecoord) : -1; + } + int16_t layerMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->layerMaterialAt(tilecoord) : -1; + } + bool isVeinAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b && b->isVeinAt(tilecoord); } + bool isLayerAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b && b->isLayerAt(tilecoord); + } + + df::tiletype staticTiletypeAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->staticTiletypeAt(tilecoord) : tiletype::Void; + } + t_matpair staticMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->staticMaterialAt(tilecoord) : t_matpair(); + } + bool hasConstructionAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b && b->hasConstructionAt(tilecoord); + } + df::tiletype tiletypeAt (DFCoord tilecoord) { - Block * b= BlockAtTile(tilecoord); - return b ? b->TileTypeAt(tilecoord) : tiletype::Void; + Block *b = BlockAtTile(tilecoord); + return b ? b->tiletypeAt(tilecoord) : tiletype::Void; } - bool setTiletypeAt(DFCoord tilecoord, df::tiletype tiletype) + bool setTiletypeAt (DFCoord tilecoord, df::tiletype tt, bool force = false) { - if (Block * b= BlockAtTile(tilecoord)) - return b->setTiletypeAt(tilecoord, tiletype); - return false; + Block *b = BlockAtTile(tilecoord); + return b && b->setTiletypeAt(tilecoord, tt, force); } uint16_t temperature1At (DFCoord tilecoord) @@ -290,17 +433,6 @@ class DFHACK_EXPORT MapCache return false; } - int16_t veinMaterialAt (DFCoord tilecoord) - { - Block * b= BlockAtTile(tilecoord); - return b ? b->veinMaterialAt(tilecoord) : -1; - } - int16_t baseMaterialAt (DFCoord tilecoord) - { - Block * b= BlockAtTile(tilecoord); - return b ? b->baseMaterialAt(tilecoord) : -1; - } - int16_t tagAt(DFCoord tilecoord) { Block * b= BlockAtTile(tilecoord); @@ -311,6 +443,7 @@ class DFHACK_EXPORT MapCache Block * b= BlockAtTile(tilecoord); if (b) b->tag(tilecoord) = val; } + void resetTags(); df::tile_designation designationAt (DFCoord tilecoord) { @@ -378,6 +511,7 @@ class DFHACK_EXPORT MapCache private: friend class Block; + friend class BlockInfo; bool valid; bool validgeo; @@ -388,6 +522,7 @@ private: uint32_t z_max; std::vector geoidx; std::vector< std::vector > layer_mats; + std::map region_details; std::map blocks; }; } diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index 3d70dc9fa..76c89de30 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -60,6 +60,14 @@ namespace df namespace DFHack { + struct t_matpair { + int16_t mat_type; + int32_t mat_index; + + t_matpair(int16_t type = -1, int32_t index = -1) + : mat_type(type), mat_index(index) {} + }; + struct DFHACK_EXPORT MaterialInfo { static const int NUM_BUILTIN = 19; static const int GROUP_SIZE = 200; @@ -91,6 +99,7 @@ namespace DFHack public: MaterialInfo(int16_t type = -1, int32_t index = -1) { decode(type, index); } + MaterialInfo(const t_matpair &mp) { decode(mp.mat_type, mp.mat_index); } template MaterialInfo(T *ptr) { decode(ptr); } bool isValid() const { return material != NULL; } @@ -107,6 +116,7 @@ namespace DFHack bool decode(int16_t type, int32_t index = -1); bool decode(df::item *item); bool decode(const df::material_vec_ref &vr, int idx); + bool decode(const t_matpair &mp) { return decode(mp.mat_type, mp.mat_index); } template bool decode(T *ptr) { // Assume and exploit a certain naming convention diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 327e26986..b45ae7191 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -51,6 +51,9 @@ using namespace std; #include "df/burrow.h" #include "df/block_burrow.h" #include "df/block_burrow_link.h" +#include "df/world_region_details.h" +#include "df/builtin_mats.h" +#include "df/block_square_event_grassst.h" using namespace DFHack; using namespace df::enums; @@ -158,7 +161,7 @@ df::world_data::T_region_map *Maps::getRegionBiome(df::coord2d rgn_pos) df::feature_init *Maps::getGlobalInitFeature(int32_t index) { auto data = world->world_data; - if (!data) + if (!data || index < 0) return NULL; auto rgn = vector_get(data->underground_regions, index); @@ -186,7 +189,7 @@ bool Maps::GetGlobalFeature(t_feature &feature, int32_t index) df::feature_init *Maps::getLocalInitFeature(df::coord2d rgn_pos, int32_t index) { auto data = world->world_data; - if (!data) + if (!data || index < 0) return NULL; if (rgn_pos.x < 0 || rgn_pos.x >= data->world_width || @@ -353,7 +356,7 @@ bool Maps::ReadGeology(vector > *layer_mats, vector int bioRX = world->map.region_x / 16 + ((i % 3) - 1); int bioRY = world->map.region_y / 16 + ((i / 3) - 1); - df::coord2d rgn_pos(clip_range(bioRX,0,world_width-1),clip_range(bioRX,0,world_height-1)); + df::coord2d rgn_pos(clip_range(bioRX,0,world_width-1),clip_range(bioRY,0,world_height-1)); (*geoidx)[i] = rgn_pos; @@ -389,7 +392,7 @@ bool Maps::ReadGeology(vector > *layer_mats, vector MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) { dirty_designations = false; - dirty_tiletypes = false; + dirty_tiles = false; dirty_temperatures = false; dirty_blockflags = false; dirty_occupancies = false; @@ -397,12 +400,12 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) bcoord = _bcoord; block = Maps::getBlock(bcoord); item_counts = NULL; - - memset(tags,0,sizeof(tags)); + tags = NULL; + tiles = NULL; + basemats = NULL; if(block) { - COPY(rawtiles, block->tiletype); COPY(designation, block->designation); COPY(occupancy, block->occupancy); blockflags = block->flags; @@ -410,33 +413,189 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) COPY(temp1, block->temperature_1); COPY(temp2, block->temperature_2); - SquashVeins(block,veinmats); - SquashConstructions(block, contiles); - SquashFrozenLiquids(block, icetiles); - if(parent->validgeo) - SquashRocks(block,basemats,&parent->layer_mats); - else - memset(basemats,-1,sizeof(basemats)); valid = true; } else { blockflags.whole = 0; - memset(rawtiles,0,sizeof(rawtiles)); memset(designation,0,sizeof(designation)); memset(occupancy,0,sizeof(occupancy)); memset(temp1,0,sizeof(temp1)); memset(temp2,0,sizeof(temp2)); - memset(veinmats,-1,sizeof(veinmats)); - memset(contiles,0,sizeof(contiles)); - memset(icetiles,0,sizeof(icetiles)); - memset(basemats,-1,sizeof(basemats)); } } MapExtras::Block::~Block() { delete[] item_counts; + delete[] tags; + delete tiles; + delete basemats; +} + +void MapExtras::Block::init_tags() +{ + if (!tags) + tags = new T_tags[16]; + memset(tags,0,sizeof(T_tags)*16); +} + +void MapExtras::Block::init_tiles(bool basemat) +{ + if (!tiles) + { + tiles = new TileInfo(); + + if (block) + ParseTiles(tiles); + } + + if (basemat && !basemats) + { + basemats = new BasematInfo(); + + if (block) + ParseBasemats(tiles, basemats); + } +} + +MapExtras::Block::TileInfo::TileInfo() +{ + frozen.clear(); + dirty_raw.clear(); + memset(raw_tiles,0,sizeof(raw_tiles)); + con_info = NULL; + dirty_base.clear(); + memset(base_tiles,0,sizeof(base_tiles)); +} + +MapExtras::Block::TileInfo::~TileInfo() +{ + delete con_info; +} + +void MapExtras::Block::TileInfo::init_coninfo() +{ + if (con_info) + return; + + con_info = new ConInfo(); + con_info->constructed.clear(); + COPY(con_info->tiles, base_tiles); + memset(con_info->mattype, -1, sizeof(con_info->mattype)); + memset(con_info->matindex, -1, sizeof(con_info->matindex)); +} + +MapExtras::Block::BasematInfo::BasematInfo() +{ + dirty.clear(); + memset(mattype,0,sizeof(mattype)); + memset(matindex,-1,sizeof(matindex)); + memset(layermat,-1,sizeof(layermat)); +} + +bool MapExtras::Block::setTiletypeAt(df::coord2d pos, df::tiletype tt, bool force) +{ + if (!block) + return false; + + if (!basemats) + init_tiles(true); + + pos = pos & 15; + + dirty_tiles = true; + tiles->raw_tiles[pos.x][pos.y] = tt; + tiles->dirty_raw.setassignment(pos, true); + + return true; +} + +void MapExtras::Block::ParseTiles(TileInfo *tiles) +{ + tiletypes40d icetiles; + BlockInfo::SquashFrozenLiquids(block, icetiles); + + COPY(tiles->raw_tiles, block->tiletype); + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + using namespace df::enums::tiletype_material; + + df::tiletype tt = tiles->raw_tiles[x][y]; + df::coord coord = block->map_pos + df::coord(x,y,0); + + // Frozen liquid comes topmost + if (tileMaterial(tt) == FROZEN_LIQUID) + { + tiles->frozen.setassignment(x,y,true); + if (icetiles[x][y] != tiletype::Void) + { + tt = icetiles[x][y]; + } + } + + // The next layer may be construction + bool is_con = false; + + if (tileMaterial(tt) == CONSTRUCTION) + { + df::construction *con = df::construction::find(coord); + if (con) + { + if (!tiles->con_info) + tiles->init_coninfo(); + + is_con = true; + tiles->con_info->constructed.setassignment(x,y,true); + tiles->con_info->tiles[x][y] = tt; + tiles->con_info->mattype[x][y] = con->mat_type; + tiles->con_info->matindex[x][y] = con->mat_index; + + tt = con->original_tile; + } + } + + // Finally, base material + tiles->base_tiles[x][y] = tt; + + // Copy base info back to construction layer + if (tiles->con_info && !is_con) + tiles->con_info->tiles[x][y] = tt; + } + } +} + +void MapExtras::Block::ParseBasemats(TileInfo *tiles, BasematInfo *bmats) +{ + BlockInfo info; + + info.prepare(this); + + COPY(bmats->layermat, info.basemats); + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + using namespace df::enums::tiletype_material; + + auto tt = tiles->base_tiles[x][y]; + auto mat = info.getBaseMaterial(tt, df::coord2d(x,y)); + + bmats->mattype[x][y] = mat.mat_type; + bmats->matindex[x][y] = mat.mat_index; + + // Copy base info back to construction layer + if (tiles->con_info && !tiles->con_info->constructed.getassignment(x,y)) + { + tiles->con_info->mattype[x][y] = mat.mat_type; + tiles->con_info->matindex[x][y] = mat.mat_index; + } + } + } } bool MapExtras::Block::Write () @@ -454,10 +613,21 @@ bool MapExtras::Block::Write () block->flags.bits.designated = true; dirty_designations = false; } - if(dirty_tiletypes) + if(dirty_tiles && tiles) { - COPY(block->tiletype, rawtiles); - dirty_tiletypes = false; + dirty_tiles = false; + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + if (tiles->dirty_raw.getassignment(x,y)) + block->tiletype[x][y] = tiles->raw_tiles[x][y]; + } + } + + delete tiles; tiles = NULL; + delete basemats; basemats = NULL; } if(dirty_temperatures) { @@ -473,65 +643,145 @@ bool MapExtras::Block::Write () return true; } -void MapExtras::Block::SquashVeins(df::map_block *mb, t_blockmaterials & materials) +void MapExtras::BlockInfo::prepare(Block *mblock) { - memset(materials,-1,sizeof(materials)); - std::vector veins; - Maps::SortBlockEvents(mb,&veins); - for (uint32_t x = 0;x<16;x++) for (uint32_t y = 0; y< 16;y++) + this->mblock = mblock; + + block = mblock->getRaw(); + parent = mblock->getParent(); + + SquashVeins(block,veinmats); + SquashGrass(block, grass); + + if (parent->validgeo) + SquashRocks(block,basemats,&parent->layer_mats); + else + memset(basemats,-1,sizeof(basemats)); + + for (size_t i = 0; i < block->plants.size(); i++) { - df::tiletype tt = mb->tiletype[x][y]; - if (tileMaterial(tt) == tiletype_material::MINERAL) + auto pp = block->plants[i]; + plants[pp->pos] = pp; + } + + global_feature = Maps::getGlobalInitFeature(block->global_feature); + local_feature = Maps::getLocalInitFeature(block->region_pos, block->local_feature); +} + +t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos) +{ + using namespace df::enums::tiletype_material; + + t_matpair rv(0,-1); + int x = pos.x, y = pos.y; + + switch (tileMaterial(tt)) { + case CONSTRUCTION: // just a fallback + case SOIL: + case STONE: + rv.mat_index = basemats[x][y]; + break; + + case MINERAL: + rv.mat_index = veinmats[x][y]; + break; + + case LAVA_STONE: + if (auto details = parent->region_details[mblock->biomeRegionAt(pos)]) + rv.mat_index = details->lava_stone; + break; + + case PLANT: + rv.mat_type = MaterialInfo::PLANT_BASE; + if (auto plant = plants[block->map_pos + df::coord(x,y,0)]) { - for (size_t i = 0; i < veins.size(); i++) + if (auto raw = df::plant_raw::find(plant->material)) { - if (veins[i]->getassignment(x,y)) - materials[x][y] = veins[i]->inorganic_mat; + rv.mat_type = raw->material_defs.type_basic_mat; + rv.mat_index = raw->material_defs.idx_basic_mat; } } + break; + + case GRASS_LIGHT: + case GRASS_DARK: + case GRASS_DRY: + case GRASS_DEAD: + rv.mat_type = MaterialInfo::PLANT_BASE; + if (auto raw = df::plant_raw::find(grass[x][y])) + { + rv.mat_type = raw->material_defs.type_basic_mat; + rv.mat_index = raw->material_defs.idx_basic_mat; + } + break; + + case FEATURE: + { + auto dsgn = block->designation[x][y]; + + if (dsgn.bits.feature_local && local_feature) + local_feature->getMaterial(&rv.mat_type, &rv.mat_index); + else if (dsgn.bits.feature_global && global_feature) + global_feature->getMaterial(&rv.mat_type, &rv.mat_index); + + break; } + + case FROZEN_LIQUID: + case POOL: + case BROOK: + case RIVER: + rv.mat_type = builtin_mats::WATER; + break; + + case ASHES: + case FIRE: + case CAMPFIRE: + rv.mat_type = builtin_mats::ASH; + break; + + default: + rv.mat_type = -1; + } + + return rv; } -void MapExtras::Block::SquashFrozenLiquids(df::map_block *mb, tiletypes40d & frozen) +void MapExtras::BlockInfo::SquashVeins(df::map_block *mb, t_blockmaterials & materials) { - std::vector ices; - Maps::SortBlockEvents(mb,NULL,&ices); - for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++) + std::vector veins; + Maps::SortBlockEvents(mb,&veins); + memset(materials,-1,sizeof(materials)); + for (uint32_t x = 0;x<16;x++) for (uint32_t y = 0; y< 16;y++) { - df::tiletype tt = mb->tiletype[x][y]; - frozen[x][y] = tiletype::Void; - if (tileMaterial(tt) == tiletype_material::FROZEN_LIQUID) + for (size_t i = 0; i < veins.size(); i++) { - for (size_t i = 0; i < ices.size(); i++) - { - df::tiletype tt2 = ices[i]->tiles[x][y]; - if (tt2 != tiletype::Void) - { - frozen[x][y] = tt2; - break; - } - } + if (veins[i]->getassignment(x,y)) + materials[x][y] = veins[i]->inorganic_mat; } } } -void MapExtras::Block::SquashConstructions (df::map_block *mb, tiletypes40d & constructions) +void MapExtras::BlockInfo::SquashFrozenLiquids(df::map_block *mb, tiletypes40d & frozen) { + std::vector ices; + Maps::SortBlockEvents(mb,NULL,&ices); + memset(frozen,0,sizeof(frozen)); for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++) { - df::tiletype tt = mb->tiletype[x][y]; - constructions[x][y] = tiletype::Void; - if (tileMaterial(tt) == tiletype_material::CONSTRUCTION) + for (size_t i = 0; i < ices.size(); i++) { - DFCoord coord = mb->map_pos + df::coord(x,y,0); - df::construction *con = df::construction::find(coord); - if (con) - constructions[x][y] = con->original_tile; + df::tiletype tt2 = ices[i]->tiles[x][y]; + if (tt2 != tiletype::Void) + { + frozen[x][y] = tt2; + break; + } } } } -void MapExtras::Block::SquashRocks (df::map_block *mb, t_blockmaterials & materials, +void MapExtras::BlockInfo::SquashRocks (df::map_block *mb, t_blockmaterials & materials, std::vector< std::vector > * layerassign) { // get the layer materials @@ -547,6 +797,25 @@ void MapExtras::Block::SquashRocks (df::map_block *mb, t_blockmaterials & materi } } +void MapExtras::BlockInfo::SquashGrass(df::map_block *mb, t_blockmaterials &materials) +{ + std::vector grasses; + Maps::SortBlockEvents(mb, NULL, NULL, NULL, &grasses); + memset(materials,-1,sizeof(materials)); + for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++) + { + int amount = 0; + for (size_t i = 0; i < grasses.size(); i++) + { + if (grasses[i]->amount[x][y] >= amount) + { + amount = grasses[i]->amount[x][y]; + materials[x][y] = grasses[i]->plant_index; + } + } + } +} + df::coord2d MapExtras::Block::biomeRegionAt(df::coord2d p) { if (!block) @@ -662,6 +931,24 @@ bool MapExtras::Block::removeItemOnGround(df::item *item) return true; } +MapExtras::MapCache::MapCache() +{ + valid = 0; + Maps::getSize(x_bmax, y_bmax, z_max); + x_tmax = x_bmax*16; y_tmax = y_bmax*16; + validgeo = Maps::ReadGeology(&layer_mats, &geoidx); + valid = true; + + if (auto data = df::global::world->world_data) + { + for (size_t i = 0; i < data->region_details.size(); i++) + { + auto info = data->region_details[i]; + region_details[info->pos] = info; + } + } +} + MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) { if(!valid) @@ -685,6 +972,15 @@ MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) } } +void MapExtras::MapCache::resetTags() +{ + for (auto it = blocks.begin(); it != blocks.end(); ++it) + { + delete[] it->second->tags; + it->second->tags = NULL; + } +} + df::burrow *Maps::findBurrowByName(std::string name) { auto &vec = df::burrow::get_vector(); diff --git a/plugins/dig.cpp b/plugins/dig.cpp index 64161a3aa..21c199102 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -1196,7 +1196,7 @@ command_result digl (color_ostream &out, vector & parameters) df::tile_designation des = MCache->designationAt(xy); df::tiletype tt = MCache->tiletypeAt(xy); int16_t veinmat = MCache->veinMaterialAt(xy); - int16_t basemat = MCache->baseMaterialAt(xy); + int16_t basemat = MCache->layerMaterialAt(xy); if( veinmat != -1 ) { con.printerr("This is a vein. Use vdig instead!\n"); @@ -1215,7 +1215,7 @@ command_result digl (color_ostream &out, vector & parameters) if (MCache->tagAt(current)) continue; int16_t vmat2 = MCache->veinMaterialAt(current); - int16_t bmat2 = MCache->baseMaterialAt(current); + int16_t bmat2 = MCache->layerMaterialAt(current); tt = MCache->tiletypeAt(current); if(!DFHack::isWallTerrain(tt)) @@ -1282,7 +1282,7 @@ command_result digl (color_ostream &out, vector & parameters) //below = 1; des_minus = MCache->designationAt(current-1); vmat_minus = MCache->veinMaterialAt(current-1); - bmat_minus = MCache->baseMaterialAt(current-1); + bmat_minus = MCache->layerMaterialAt(current-1); tt_minus = MCache->tiletypeAt(current-1); if ( tileMaterial(tt_minus)==tiletype_material::STONE || tileMaterial(tt_minus)==tiletype_material::SOIL) @@ -1293,7 +1293,7 @@ command_result digl (color_ostream &out, vector & parameters) //above = 1; des_plus = MCache->designationAt(current+1); vmat_plus = MCache->veinMaterialAt(current+1); - bmat_plus = MCache->baseMaterialAt(current+1); + bmat_plus = MCache->layerMaterialAt(current+1); tt_plus = MCache->tiletypeAt(current+1); if ( tileMaterial(tt_plus)==tiletype_material::STONE || tileMaterial(tt_plus)==tiletype_material::SOIL) diff --git a/plugins/mapexport/mapexport.cpp b/plugins/mapexport/mapexport.cpp index e0a7e5e69..592c526db 100644 --- a/plugins/mapexport/mapexport.cpp +++ b/plugins/mapexport/mapexport.cpp @@ -193,7 +193,7 @@ command_result mapexport (color_ostream &out, std::vector & parame prototile->set_flow_size(des.bits.flow_size); } - df::tiletype type = b->TileTypeAt(coord); + df::tiletype type = b->tiletypeAt(coord); prototile->set_type((dfproto::Tile::TileType)tileShape(type)); prototile->set_tile_material((dfproto::Tile::TileMaterialType)tileMaterial(type)); @@ -204,7 +204,7 @@ command_result mapexport (color_ostream &out, std::vector & parame case tiletype_material::SOIL: case tiletype_material::STONE: prototile->set_material_type(0); - prototile->set_material_index(b->baseMaterialAt(coord)); + prototile->set_material_index(b->layerMaterialAt(coord)); break; case tiletype_material::MINERAL: prototile->set_material_type(0); diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 06b1f968e..f7430af26 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -103,6 +103,30 @@ command_result df_cprobe (color_ostream &out, vector & parameters) return CR_OK; } +void describeTile(color_ostream &out, df::tiletype tiletype) +{ + out.print("%d", tiletype); + if(tileName(tiletype)) + out.print(" = %s",tileName(tiletype)); + out.print("\n"); + + df::tiletype_shape shape = tileShape(tiletype); + df::tiletype_material material = tileMaterial(tiletype); + df::tiletype_special special = tileSpecial(tiletype); + df::tiletype_variant variant = tileVariant(tiletype); + out.print("%-10s: %4d %s\n","Class" ,shape, + ENUM_KEY_STR(tiletype_shape, shape).c_str()); + out.print("%-10s: %4d %s\n","Material" , + material, ENUM_KEY_STR(tiletype_material, material).c_str()); + out.print("%-10s: %4d %s\n","Special" , + special, ENUM_KEY_STR(tiletype_special, special).c_str()); + out.print("%-10s: %4d %s\n" ,"Variant" , + variant, ENUM_KEY_STR(tiletype_variant, variant).c_str()); + out.print("%-10s: %s\n" ,"Direction", + tileDirection(tiletype).getStr()); + out.print("\n"); +} + command_result df_probe (color_ostream &out, vector & parameters) { //bool showBlock, showDesig, showOccup, showTile, showMisc; @@ -186,26 +210,12 @@ command_result df_probe (color_ostream &out, vector & parameters) */ // tiletype - out.print("tiletype: %d", tiletype); - if(tileName(tiletype)) - out.print(" = %s",tileName(tiletype)); - out.print("\n"); - - df::tiletype_shape shape = tileShape(tiletype); - df::tiletype_material material = tileMaterial(tiletype); - df::tiletype_special special = tileSpecial(tiletype); - df::tiletype_variant variant = tileVariant(tiletype); - out.print("%-10s: %4d %s\n","Class" ,shape, - ENUM_KEY_STR(tiletype_shape, shape).c_str()); - out.print("%-10s: %4d %s\n","Material" , - material, ENUM_KEY_STR(tiletype_material, material).c_str()); - out.print("%-10s: %4d %s\n","Special" , - special, ENUM_KEY_STR(tiletype_special, special).c_str()); - out.print("%-10s: %4d %s\n" ,"Variant" , - variant, ENUM_KEY_STR(tiletype_variant, variant).c_str()); - out.print("%-10s: %s\n" ,"Direction", - tileDirection(tiletype).getStr()); - out.print("\n"); + out.print("tiletype: "); + describeTile(out, tiletype); + out.print("static: "); + describeTile(out, mc.staticTiletypeAt(cursor)); + out.print("base: "); + describeTile(out, mc.baseTiletypeAt(cursor)); out.print("temperature1: %d U\n",mc.temperature1At(cursor)); out.print("temperature2: %d U\n",mc.temperature2At(cursor)); @@ -232,7 +242,7 @@ command_result df_probe (color_ostream &out, vector & parameters) "evilness " << biome->evilness << ")" << std::endl; out << "geolayer: " << des.bits.geolayer_index << std::endl; - int16_t base_rock = mc.baseMaterialAt(cursor); + int16_t base_rock = mc.layerMaterialAt(cursor); if(base_rock != -1) { out << "Layer material: " << dec << base_rock; @@ -256,6 +266,12 @@ command_result df_probe (color_ostream &out, vector & parameters) else out << endl; } + MaterialInfo minfo(mc.baseMaterialAt(cursor)); + if (minfo.isValid()) + out << "Base material: " << minfo.getToken() << " / " << minfo.toString() << endl; + minfo.decode(mc.staticMaterialAt(cursor)); + if (minfo.isValid()) + out << "Static material: " << minfo.getToken() << " / " << minfo.toString() << endl; // liquids if(des.bits.flow_size) { diff --git a/plugins/prospector.cpp b/plugins/prospector.cpp index c90a66c5a..e2f1e9534 100644 --- a/plugins/prospector.cpp +++ b/plugins/prospector.cpp @@ -474,7 +474,7 @@ command_result prospector (color_ostream &con, vector & parameters) liquidWater.add(global_z); } - df::tiletype type = b->TileTypeAt(coord); + df::tiletype type = b->tiletypeAt(coord); df::tiletype_shape tileshape = tileShape(type); df::tiletype_material tilemat = tileMaterial(type); @@ -506,7 +506,7 @@ command_result prospector (color_ostream &con, vector & parameters) { case tiletype_material::SOIL: case tiletype_material::STONE: - layerMats[b->baseMaterialAt(coord)].add(global_z); + layerMats[b->layerMaterialAt(coord)].add(global_z); break; case tiletype_material::MINERAL: veinMats[b->veinMaterialAt(coord)].add(global_z); From 02e43428ee70874851b9a9775b8cfb579f859f80 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 19 Apr 2012 11:03:29 +0400 Subject: [PATCH 13/18] Sync to changes in the data structure definitions. --- .../df/custom/block_burrow.methods.inc | 17 +++------ .../block_square_event_mineralst.methods.inc | 16 +++----- .../df/custom/tile_bitmask.methods.inc | 38 +++++++++++++++++++ library/modules/Maps.cpp | 2 +- library/xml | 2 +- plugins/devel/buildprobe.cpp | 2 +- 6 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 library/include/df/custom/tile_bitmask.methods.inc diff --git a/library/include/df/custom/block_burrow.methods.inc b/library/include/df/custom/block_burrow.methods.inc index 216a58662..3959fcf35 100644 --- a/library/include/df/custom/block_burrow.methods.inc +++ b/library/include/df/custom/block_burrow.methods.inc @@ -1,26 +1,21 @@ inline bool getassignment( const df::coord2d &xy ) { - return getassignment(xy.x,xy.y); + return tile_bitmask.getassignment(xy); } inline bool getassignment( int x, int y ) { - return (tile_bitmask[y] & (1 << x)); + return tile_bitmask.getassignment(x,y); } inline void setassignment( const df::coord2d &xy, bool bit ) { - return setassignment(xy.x,xy.y, bit); + return tile_bitmask.setassignment(xy, bit); } inline void setassignment( int x, int y, bool bit ) { - if(bit) - tile_bitmask[y] |= (1 << x); - else - tile_bitmask[y] &= ~(1 << x); + return tile_bitmask.setassignment(x, y, bit); } bool has_assignments() { - for (int i = 0; i < 16; i++) - if (tile_bitmask[i]) - return true; - return false; + return tile_bitmask.has_assignments(); } + diff --git a/library/include/df/custom/block_square_event_mineralst.methods.inc b/library/include/df/custom/block_square_event_mineralst.methods.inc index 216a58662..4da4c65be 100644 --- a/library/include/df/custom/block_square_event_mineralst.methods.inc +++ b/library/include/df/custom/block_square_event_mineralst.methods.inc @@ -1,26 +1,20 @@ inline bool getassignment( const df::coord2d &xy ) { - return getassignment(xy.x,xy.y); + return tile_bitmask.getassignment(xy); } inline bool getassignment( int x, int y ) { - return (tile_bitmask[y] & (1 << x)); + return tile_bitmask.getassignment(x,y); } inline void setassignment( const df::coord2d &xy, bool bit ) { - return setassignment(xy.x,xy.y, bit); + return tile_bitmask.setassignment(xy, bit); } inline void setassignment( int x, int y, bool bit ) { - if(bit) - tile_bitmask[y] |= (1 << x); - else - tile_bitmask[y] &= ~(1 << x); + return tile_bitmask.setassignment(x, y, bit); } bool has_assignments() { - for (int i = 0; i < 16; i++) - if (tile_bitmask[i]) - return true; - return false; + return tile_bitmask.has_assignments(); } diff --git a/library/include/df/custom/tile_bitmask.methods.inc b/library/include/df/custom/tile_bitmask.methods.inc new file mode 100644 index 000000000..18b312a08 --- /dev/null +++ b/library/include/df/custom/tile_bitmask.methods.inc @@ -0,0 +1,38 @@ +inline uint16_t &operator[] (int y) +{ + return bits[y]; +} +void clear() +{ + memset(bits,0,sizeof(bits)); +} +void set_all() +{ + memset(bits,0xFF,sizeof(bits)); +} +inline bool getassignment( const df::coord2d &xy ) +{ + return getassignment(xy.x,xy.y); +} +inline bool getassignment( int x, int y ) +{ + return (bits[y] & (1 << x)); +} +inline void setassignment( const df::coord2d &xy, bool bit ) +{ + return setassignment(xy.x,xy.y, bit); +} +inline void setassignment( int x, int y, bool bit ) +{ + if(bit) + bits[y] |= (1 << x); + else + bits[y] &= ~(1 << x); +} +bool has_assignments() +{ + for (int i = 0; i < 16; i++) + if (bits[i]) + return true; + return false; +} diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index b45ae7191..06e525fea 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -1066,7 +1066,7 @@ df::block_burrow *Maps::getBlockBurrowMask(df::burrow *burrow, df::map_block *bl link->item = new df::block_burrow; link->item->id = burrow->id; - memset(link->item->tile_bitmask,0,sizeof(link->item->tile_bitmask)); + link->item->tile_bitmask.clear(); link->item->link = link; link->next = NULL; diff --git a/library/xml b/library/xml index e8036d3f1..a1e342afe 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e8036d3f13c6be0141899baae90f605ad11d5385 +Subproject commit a1e342afe5a5e1e07672cd8b6553953bc251a05d diff --git a/plugins/devel/buildprobe.cpp b/plugins/devel/buildprobe.cpp index 6c360455a..17e95237f 100644 --- a/plugins/devel/buildprobe.cpp +++ b/plugins/devel/buildprobe.cpp @@ -119,7 +119,7 @@ command_result writeFlag (color_ostream &out, vector & parameters) MapExtras::MapCache * MCache = new MapExtras::MapCache(); t_occupancy oc = MCache->occupancyAt(cursor); - oc.bits.building = value; + oc.bits.building = df::tile_building_occ(value); MCache->setOccupancyAt(cursor, oc); MCache->WriteAll(); From c0c790f58c9cf409492d615ffb5ef6d4ba26fbd0 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 19 Apr 2012 19:17:07 +0400 Subject: [PATCH 14/18] Refactor MapCache: make it parse everything that is known re tiles & mats. --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index a1e342afe..25c2a3dad 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit a1e342afe5a5e1e07672cd8b6553953bc251a05d +Subproject commit 25c2a3dad964abbcceb5abd41558b71fb113e83b From 53e0ba8b8d047a8cd4edd1a8067c218080e6e889 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Thu, 26 Apr 2012 12:54:10 -0500 Subject: [PATCH 15/18] Actually write the noble penalty to the dwarf_info structure.... --- plugins/autolabor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 73f8e7c27..f489c229e 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -784,6 +784,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) dwarf_info[dwarf].medical = 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++) From 7c606da636e77280b6a7281464d7e8cd38f7530a Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Thu, 26 Apr 2012 13:46:38 -0500 Subject: [PATCH 16/18] Autolabor will exclude the trade broker from all labors if the "trader requested" flag is on at the trade depot. --- plugins/autolabor.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index f489c229e..49bd2afc1 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -493,6 +494,7 @@ struct dwarf_info_t 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) @@ -715,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) { @@ -727,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) @@ -783,6 +793,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) 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; } @@ -1069,8 +1082,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) assert(dwarf >= 0); assert(dwarf < n_dwarfs); - - bool preferred_dwarf = false; if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE) preferred_dwarf = true; @@ -1080,6 +1091,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) 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; From e7c55ab6e162c1fbc32741d6283780805dea96e7 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 30 Apr 2012 09:01:48 -0500 Subject: [PATCH 17/18] Also exclude trader from hauling when requested at depot. --- plugins/autolabor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 49bd2afc1..2eaa27b2b 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -1130,6 +1130,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) std::vector 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); } From 6903f3877fb7f3b8aa1f1062e342f0bc2b6001ae Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Tue, 1 May 2012 10:58:12 -0500 Subject: [PATCH 18/18] Devel plugin nestboxes scans all nestboxes for unhatchable eggs and optionally dumps them. --- plugins/devel/CMakeLists.txt | 2 +- plugins/devel/nestboxes.cpp | 116 +++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 plugins/devel/nestboxes.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 70daa7b2c..5d1d585ab 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -16,4 +16,4 @@ 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) diff --git a/plugins/devel/nestboxes.cpp b/plugins/devel/nestboxes.cpp new file mode 100644 index 000000000..b3d24cd92 --- /dev/null +++ b/plugins/devel/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 & parameters); + +DFHACK_PLUGIN("nestboxes"); + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &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 & 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(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; +} +