command_result prospector (color_ostream &con, vector <string> & parameters) { bool showHidden = false; bool showPlants = true; bool showValue = false; bool showHFS = false; for(size_t i = 0; i < parameters.size();i++) { if (parameters[i] == "all") { showHidden = true; } else if (parameters[i] == "value") { showValue = true; } else if (parameters[i] == "hell") { showHidden = showHFS = true; } else return CR_WRONG_USAGE; } CoreSuspender suspend; // Embark screen active: estimate using world geology data if (VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, Core::getTopViewscreen())) return embark_prospector(con, screen, showHidden, showValue); if (!Maps::IsValid()) { con.printerr("Map is not available!\n"); return CR_FAILURE; } uint32_t x_max = 0, y_max = 0, z_max = 0; Maps::getSize(x_max, y_max, z_max); MapExtras::MapCache map; DFHack::Materials *mats = Core::getInstance().getMaterials(); DFHack::t_feature blockFeature; bool hasAquifer = false; MatMap baseMats; MatMap layerMats; MatMap veinMats; MatMap plantMats; MatMap treeMats; matdata liquidWater; matdata liquidMagma; matdata aquiferTiles; matdata hfsTiles; uint32_t vegCount = 0; for(uint32_t z = 0; z < z_max; z++) { for(uint32_t b_y = 0; b_y < y_max; b_y++) { for(uint32_t b_x = 0; b_x < x_max; b_x++) { // Get the map block df::coord2d blockCoord(b_x, b_y); MapExtras::Block *b = map.BlockAt(DFHack::DFCoord(b_x, b_y, z)); if (!b || !b->is_valid()) { continue; } // Find features b->GetFeature(&blockFeature); int global_z = world->map.region_z + z; // Iterate over all the tiles in the block for(uint32_t y = 0; y < 16; y++) { for(uint32_t x = 0; x < 16; x++) { df::coord2d coord(x, y); df::tile_designation des = b->DesignationAt(coord); df::tile_occupancy occ = b->OccupancyAt(coord); // Skip hidden tiles if (!showHidden && des.bits.hidden) { continue; } // Check for aquifer if (des.bits.water_table) { hasAquifer = true; aquiferTiles.add(global_z); } // Check for liquid if (des.bits.flow_size) { if (des.bits.liquid_type == tile_liquid::Magma) liquidMagma.add(global_z); else liquidWater.add(global_z); } df::tiletype type = b->tiletypeAt(coord); df::tiletype_shape tileshape = tileShape(type); df::tiletype_material tilemat = tileMaterial(type); // We only care about these types switch (tileshape) { case tiletype_shape::WALL: case tiletype_shape::FORTIFICATION: break; case tiletype_shape::EMPTY: /* find the top of the HFS chamber */ if (tilemat == tiletype_material::AIR && des.bits.feature && des.bits.hidden && blockFeature.type == feature_type::glowing_pit) { hfsTiles.add(global_z); } default: continue; } // Count the material type baseMats[tilemat].add(global_z); // Find the type of the tile switch (tilemat) { case tiletype_material::SOIL: case tiletype_material::STONE: layerMats[b->layerMaterialAt(coord)].add(global_z); break; case tiletype_material::MINERAL: veinMats[b->veinMaterialAt(coord)].add(global_z); break; case tiletype_material::LAVA_STONE: // TODO ? break; default: break; } } } // Check plants this way, as the other way wasn't getting them all // and we can check visibility more easily here if (showPlants) { auto block = Maps::getBlock(b_x,b_y,z); stl::vector<df::plant *> *plants = block ? &block->plants : NULL; if(plants) { for (auto it = plants->begin(); it != plants->end(); it++) { const df::plant & plant = *(*it); df::coord2d loc(plant.pos.x, plant.pos.y); loc = loc % 16; if (showHidden || !b->DesignationAt(loc).bits.hidden) { if(plant.flags.bits.is_shrub) plantMats[plant.plant_id].add(global_z); else treeMats[plant.wood_id].add(global_z); } } } } // Block end } // block x // Clean uneeded memory map.trash(); } // block y } // z MatMap::const_iterator it; con << "Base materials:" << std::endl; for (it = baseMats.begin(); it != baseMats.end(); ++it) { con << std::setw(25) << ENUM_KEY_STR(tiletype_material,(df::tiletype_material)it->first) << " : " << it->second.count << std::endl; } if (liquidWater.count || liquidMagma.count) { con << std::endl << "Liquids:" << std::endl; if (liquidWater.count) { con << std::setw(25) << "WATER" << " : "; printMatdata(con, liquidWater); } if (liquidWater.count) { con << std::setw(25) << "MAGMA" << " : "; printMatdata(con, liquidMagma); } } con << std::endl << "Layer materials:" << std::endl; printMats<df::matgloss_stone, shallower>(con, layerMats, world->raws.matgloss.stone, showValue); printVeins(con, veinMats, mats, showValue); if (showPlants) { con << "Shrubs:" << std::endl; printMats<df::matgloss_plant, std::greater>(con, plantMats, world->raws.matgloss.plant, showValue); con << "Wood in trees:" << std::endl; printMats<df::matgloss_wood, std::greater>(con, treeMats, world->raws.matgloss.wood, showValue); } if (hasAquifer) { con << "Has aquifer"; if (aquiferTiles.count) { con << " : "; printMatdata(con, aquiferTiles); } else con << std::endl; } if (showHFS && hfsTiles.count) { con << "Has HFS : "; printMatdata(con, hfsTiles); } // Cleanup mats->Finish(); con << std::endl; return CR_OK; }
static command_result embark_prospector(color_ostream &out, df::viewscreen_choose_start_sitest *screen, bool showHidden, bool showValue) { if (!world) { out.printerr("World data is not available.\n"); return CR_FAILURE; } df::world_data *data = &world->world_data; coord2d cur_region = screen->region_pos; auto cur_details = get_details(data, cur_region); if (!cur_details) { out.printerr("Current region details are not available.\n"); return CR_FAILURE; } // Compute material maps MatMap layerMats; MatMap veinMats; matdata world_bottom; // Compute biomes std::map<coord2d, int> biomes; /*if (screen->biome_highlighted) { out.print("Processing one embark tile of biome F%d.\n\n", screen->biome_idx+1); biomes[screen->biome_rgn[screen->biome_idx]]++; }*/ for (int x = screen->embark_pos_min.x; x <= screen->embark_pos_max.x; x++) { for (int y = screen->embark_pos_min.y; y <= screen->embark_pos_max.y; y++) { EmbarkTileLayout tile; if (!estimate_underground(out, tile, cur_details, x, y) || !estimate_materials(out, tile, layerMats, veinMats)) return CR_FAILURE; world_bottom.add(tile.base_z, 0); world_bottom.add(tile.elevation-1, 0); } } // Print the report out << "Layer materials:" << std::endl; printMats<df::matgloss_stone, shallower>(out, layerMats, world->raws.matgloss.stone, showValue); if (showHidden) { DFHack::Materials *mats = Core::getInstance().getMaterials(); printVeins(out, veinMats, mats, showValue); mats->Finish(); } out << "Embark depth: " << (world_bottom.upper_z-world_bottom.lower_z+1) << " "; printMatdata(out, world_bottom, true); out << std::endl << "Warning: the above data is only a very rough estimate." << std::endl; return CR_OK; }
command_result mapexport (color_ostream &out, std::vector <std::string> & parameters) { bool showHidden = false; int filenameParameter = 1; for(size_t i = 0; i < parameters.size();i++) { if(parameters[i] == "help" || parameters[i] == "?") { out.print("Exports the currently visible map to a file.\n" "Usage: mapexport [options] <filename>\n" "Example: mapexport all embark.dfmap\n" "Options:\n" " all - Export the entire map, not just what's revealed.\n" ); return CR_OK; } if (parameters[i] == "all") { showHidden = true; filenameParameter++; } } CoreSuspender suspend; uint32_t x_max=0, y_max=0, z_max=0; if (!Maps::IsValid()) { out.printerr("Map is not available!\n"); return CR_FAILURE; } if (parameters.size() < filenameParameter) { out.printerr("Please supply a filename.\n"); return CR_FAILURE; } std::string filename = parameters[filenameParameter-1]; if (filename.rfind(".dfmap") == std::string::npos) filename += ".dfmap"; out << "Writing to " << filename << "..." << std::endl; std::ofstream output_file(filename, std::ios::out | std::ios::trunc | std::ios::binary); if (!output_file.is_open()) { out.printerr("Couldn't open the output file.\n"); return CR_FAILURE; } ZeroCopyOutputStream *raw_output = new OstreamOutputStream(&output_file); GzipOutputStream *zip_output = new GzipOutputStream(raw_output); CodedOutputStream *coded_output = new CodedOutputStream(zip_output); coded_output->WriteLittleEndian32(0x50414DDF); //Write our file header Maps::getSize(x_max, y_max, z_max); MapExtras::MapCache map; DFHack::Materials *mats = Core::getInstance().getMaterials(); out << "Writing map info..." << std::endl; dfproto::Map protomap; protomap.set_x_size(x_max); protomap.set_y_size(y_max); protomap.set_z_size(z_max); out << "Writing material dictionary..." << std::endl; for (size_t i = 0; i < world->raws.inorganics.size(); i++) { dfproto::Material *protomaterial = protomap.add_inorganic_material(); protomaterial->set_index(i); protomaterial->set_name(world->raws.inorganics[i]->id); } for (size_t i = 0; i < world->raws.plants.all.size(); i++) { dfproto::Material *protomaterial = protomap.add_organic_material(); protomaterial->set_index(i); protomaterial->set_name(world->raws.plants.all[i]->id); } std::map<df::coord,std::pair<uint32_t,uint16_t> > constructionMaterials; if (Constructions::isValid()) { for (uint32_t i = 0; i < Constructions::getCount(); i++) { df::construction *construction = Constructions::getConstruction(i); constructionMaterials[construction->pos] = std::make_pair(construction->mat_index, construction->mat_type); } } coded_output->WriteVarint32(protomap.ByteSize()); protomap.SerializeToCodedStream(coded_output); DFHack::t_feature blockFeatureGlobal; DFHack::t_feature blockFeatureLocal; out.print("Writing map block information"); for(uint32_t z = 0; z < z_max; z++) { for(uint32_t b_y = 0; b_y < y_max; b_y++) { for(uint32_t b_x = 0; b_x < x_max; b_x++) { if (b_x == 0 && b_y == 0 && z % 10 == 0) out.print("."); // Get the map block df::coord2d blockCoord(b_x, b_y); MapExtras::Block *b = map.BlockAt(DFHack::DFCoord(b_x, b_y, z)); if (!b || !b->valid) { continue; } dfproto::Block protoblock; protoblock.set_x(b_x); protoblock.set_y(b_y); protoblock.set_z(z); { // Find features uint32_t index = b->raw.global_feature; if (index != -1) Maps::GetGlobalFeature(blockFeatureGlobal, index); index = b->raw.local_feature; if (index != -1) Maps::GetLocalFeature(blockFeatureLocal, blockCoord, index); } int global_z = df::global::world->map.region_z + z; // Iterate over all the tiles in the block for(uint32_t y = 0; y < 16; y++) { for(uint32_t x = 0; x < 16; x++) { df::coord2d coord(x, y); df::tile_designation des = b->DesignationAt(coord); df::tile_occupancy occ = b->OccupancyAt(coord); // Skip hidden tiles if (!showHidden && des.bits.hidden) { continue; } dfproto::Tile *prototile = protoblock.add_tile(); prototile->set_x(x); prototile->set_y(y); // Check for liquid if (des.bits.flow_size) { prototile->set_liquid_type((dfproto::Tile::LiquidType)des.bits.liquid_type); prototile->set_flow_size(des.bits.flow_size); } df::tiletype type = b->TileTypeAt(coord); prototile->set_type((dfproto::Tile::TileType)tileShape(type)); prototile->set_tile_material((dfproto::Tile::TileMaterialType)tileMaterial(type)); df::coord map_pos = df::coord(b_x*16+x,b_y*16+y,z); switch (tileMaterial(type)) { case tiletype_material::SOIL: case tiletype_material::STONE: prototile->set_material_type(0); prototile->set_material_index(b->baseMaterialAt(coord)); break; case tiletype_material::MINERAL: prototile->set_material_type(0); prototile->set_material_index(b->veinMaterialAt(coord)); break; case tiletype_material::FEATURE: if (blockFeatureLocal.type != -1 && des.bits.feature_local) { if (blockFeatureLocal.type == feature_type::deep_special_tube && blockFeatureLocal.main_material == 0) // stone { prototile->set_material_type(0); prototile->set_material_index(blockFeatureLocal.sub_material); } if (blockFeatureGlobal.type != -1 && des.bits.feature_global && blockFeatureGlobal.type == feature_type::feature_underworld_from_layer && blockFeatureGlobal.main_material == 0) // stone { prototile->set_material_type(0); prototile->set_material_index(blockFeatureGlobal.sub_material); } } break; case tiletype_material::CONSTRUCTION: if (constructionMaterials.find(map_pos) != constructionMaterials.end()) { prototile->set_material_index(constructionMaterials[map_pos].first); prototile->set_material_type(constructionMaterials[map_pos].second); } break; default: break; } } } PlantList *plants; if (Maps::ReadVegetation(b_x, b_y, z, plants)) { for (PlantList::const_iterator it = plants->begin(); it != plants->end(); it++) { const df::plant & plant = *(*it); df::coord2d loc(plant.pos.x, plant.pos.y); loc = loc % 16; if (showHidden || !b->DesignationAt(loc).bits.hidden) { dfproto::Plant *protoplant = protoblock.add_plant(); protoplant->set_x(loc.x); protoplant->set_y(loc.y); protoplant->set_is_shrub(plant.flags.bits.is_shrub); protoplant->set_material(plant.material); } } } coded_output->WriteVarint32(protoblock.ByteSize()); protoblock.SerializeToCodedStream(coded_output); } // block x // Clean uneeded memory map.trash(); } // block y } // z delete coded_output; delete zip_output; delete raw_output; mats->Finish(); out.print("\nMap succesfully exported!\n"); return CR_OK; }
command_result prospector (color_ostream &con, vector <string> & parameters) { bool showHidden = false; bool showPlants = true; bool showSlade = true; bool showTemple = true; bool showValue = false; bool showTube = false; for(size_t i = 0; i < parameters.size();i++) { if (parameters[i] == "all") { showHidden = true; } else if (parameters[i] == "value") { showValue = true; } else if (parameters[i] == "hell") { showHidden = showTube = true; } else return CR_WRONG_USAGE; } CoreSuspender suspend; // Embark screen active: estimate using world geology data if (VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, Core::getTopViewscreen())) return embark_prospector(con, screen, showHidden, showValue); if (!Maps::IsValid()) { con.printerr("Map is not available!\n"); return CR_FAILURE; } uint32_t x_max = 0, y_max = 0, z_max = 0; Maps::getSize(x_max, y_max, z_max); MapExtras::MapCache map; DFHack::Materials *mats = Core::getInstance().getMaterials(); DFHack::t_feature blockFeatureGlobal; DFHack::t_feature blockFeatureLocal; bool hasAquifer = false; bool hasDemonTemple = false; bool hasLair = false; MatMap baseMats; MatMap layerMats; MatMap veinMats; MatMap plantMats; MatMap treeMats; matdata liquidWater; matdata liquidMagma; matdata aquiferTiles; matdata tubeTiles; uint32_t vegCount = 0; for(uint32_t z = 0; z < z_max; z++) { for(uint32_t b_y = 0; b_y < y_max; b_y++) { for(uint32_t b_x = 0; b_x < x_max; b_x++) { // Get the map block df::coord2d blockCoord(b_x, b_y); MapExtras::Block *b = map.BlockAt(DFHack::DFCoord(b_x, b_y, z)); if (!b || !b->is_valid()) { continue; } // Find features b->GetGlobalFeature(&blockFeatureGlobal); b->GetLocalFeature(&blockFeatureLocal); int global_z = world->map.region_z + z; // Iterate over all the tiles in the block for(uint32_t y = 0; y < 16; y++) { for(uint32_t x = 0; x < 16; x++) { df::coord2d coord(x, y); df::tile_designation des = b->DesignationAt(coord); df::tile_occupancy occ = b->OccupancyAt(coord); // Skip hidden tiles if (!showHidden && des.bits.hidden) { continue; } // Check for aquifer if (des.bits.water_table) { hasAquifer = true; aquiferTiles.add(global_z); } // Check for lairs if (occ.bits.monster_lair) { hasLair = true; } // Check for liquid if (des.bits.flow_size) { if (des.bits.liquid_type == tile_liquid::Magma) liquidMagma.add(global_z); else liquidWater.add(global_z); } df::tiletype type = b->tiletypeAt(coord); df::tiletype_shape tileshape = tileShape(type); df::tiletype_material tilemat = tileMaterial(type); // We only care about these types switch (tileshape) { case tiletype_shape::WALL: case tiletype_shape::FORTIFICATION: break; case tiletype_shape::EMPTY: /* A heuristic: tubes inside adamantine have EMPTY:AIR tiles which still have feature_local set. Also check the unrevealed status, so as to exclude any holes mined by the player. */ if (tilemat == tiletype_material::AIR && des.bits.feature_local && des.bits.hidden && blockFeatureLocal.type == feature_type::deep_special_tube) { tubeTiles.add(global_z); } default: continue; } // Count the material type baseMats[tilemat].add(global_z); // Find the type of the tile switch (tilemat) { case tiletype_material::SOIL: case tiletype_material::STONE: layerMats[b->layerMaterialAt(coord)].add(global_z); break; case tiletype_material::MINERAL: veinMats[b->veinMaterialAt(coord)].add(global_z); break; case tiletype_material::FEATURE: if (blockFeatureLocal.type != -1 && des.bits.feature_local) { if (blockFeatureLocal.type == feature_type::deep_special_tube && blockFeatureLocal.main_material == 0) // stone { veinMats[blockFeatureLocal.sub_material].add(global_z); } else if (showTemple && blockFeatureLocal.type == feature_type::deep_surface_portal) { hasDemonTemple = true; } } if (showSlade && blockFeatureGlobal.type != -1 && des.bits.feature_global && blockFeatureGlobal.type == feature_type::feature_underworld_from_layer && blockFeatureGlobal.main_material == 0) // stone { layerMats[blockFeatureGlobal.sub_material].add(global_z); } break; case tiletype_material::LAVA_STONE: // TODO ? break; default: break; } } } // Check plants this way, as the other way wasn't getting them all // and we can check visibility more easily here if (showPlants) { auto block = Maps::getBlockColumn(b_x,b_y); vector<df::plant *> *plants = block ? &block->plants : NULL; if(plants) { for (PlantList::const_iterator it = plants->begin(); it != plants->end(); it++) { const df::plant & plant = *(*it); if (plant.pos.z != z) continue; df::coord2d loc(plant.pos.x, plant.pos.y); loc = loc % 16; if (showHidden || !b->DesignationAt(loc).bits.hidden) { if(plant.flags.bits.is_shrub) plantMats[plant.material].add(global_z); else treeMats[plant.material].add(global_z); } } } } // Block end } // block x // Clean uneeded memory map.trash(); } // block y } // z MatMap::const_iterator it; con << "Base materials:" << std::endl; for (it = baseMats.begin(); it != baseMats.end(); ++it) { con << std::setw(25) << ENUM_KEY_STR(tiletype_material,(df::tiletype_material)it->first) << " : " << it->second.count << std::endl; } if (liquidWater.count || liquidMagma.count) { con << std::endl << "Liquids:" << std::endl; if (liquidWater.count) { con << std::setw(25) << "WATER" << " : "; printMatdata(con, liquidWater); } if (liquidWater.count) { con << std::setw(25) << "MAGMA" << " : "; printMatdata(con, liquidMagma); } } con << std::endl << "Layer materials:" << std::endl; printMats<df::inorganic_raw, shallower>(con, layerMats, world->raws.inorganics, showValue); printVeins(con, veinMats, mats, showValue); if (showPlants) { con << "Shrubs:" << std::endl; printMats<df::plant_raw, std::greater>(con, plantMats, world->raws.plants.all, showValue); con << "Wood in trees:" << std::endl; printMats<df::plant_raw, std::greater>(con, treeMats, world->raws.plants.all, showValue); } if (hasAquifer) { con << "Has aquifer"; if (aquiferTiles.count) { con << " : "; printMatdata(con, aquiferTiles); } else con << std::endl; } if (showTube && tubeTiles.count) { con << "Has HFS tubes : "; printMatdata(con, tubeTiles); } if (hasDemonTemple) { con << "Has demon temple" << std::endl; } if (hasLair) { con << "Has lair" << std::endl; } // Cleanup mats->Finish(); con << std::endl; return CR_OK; }
static command_result embark_prospector(DFHack::Core *c, df::viewscreen_choose_start_sitest *screen, bool showHidden, bool showValue) { if (!world || !world->world_data) { c->con.printerr("World data is not available.\n"); return CR_FAILURE; } df::world_data *data = world->world_data; coord2d cur_region = screen->region_pos; int d_idx = linear_index(data->region_details, &df::world_region_details::pos, cur_region); auto cur_details = vector_get(data->region_details, d_idx); if (!cur_details) { c->con.printerr("Current region details are not available.\n"); return CR_FAILURE; } // Compute biomes std::map<coord2d, int> biomes; if (screen->biome_highlighted) { c->con.print("Processing one embark tile of biome F%d.\n\n", screen->biome_idx+1); biomes[screen->biome_rgn[screen->biome_idx]]++; } else { for (int x = screen->embark_pos_min.x; x <= screen->embark_pos_max.x; x++) { for (int y = screen->embark_pos_min.y; y <= screen->embark_pos_max.y; y++) { int bv = clip_range(cur_details->biome[x][y], 1, 9); biomes[cur_region + biome_delta[bv-1]]++; } } } // Compute material maps MatMap layerMats; MatMap veinMats; for (auto biome_it = biomes.begin(); biome_it != biomes.end(); ++biome_it) { int bx = clip_range(biome_it->first.x, 0, data->world_width-1); int by = clip_range(biome_it->first.y, 0, data->world_height-1); auto ®ion = data->region_map[bx][by]; df::world_geo_biome *geo_biome = df::world_geo_biome::find(region.geo_index); if (!geo_biome) { c->con.printerr("Region geo-biome not found: (%d,%d)\n", bx, by); return CR_FAILURE; } int cnt = biome_it->second; for (unsigned i = 0; i < geo_biome->layers.size(); i++) { auto layer = geo_biome->layers[i]; layerMats[layer->mat_index].add(layer->bottom_height, 0); int level_cnt = layer->top_height - layer->bottom_height + 1; int layer_size = 48*48*cnt*level_cnt; int sums[ENUM_LAST_ITEM(inclusion_type)+1] = { 0 }; for (unsigned j = 0; j < layer->vein_mat.size(); j++) if (inclusion_type::is_valid(layer->vein_type[j])) sums[layer->vein_type[j]] += layer->vein_unk_38[j]; for (unsigned j = 0; j < layer->vein_mat.size(); j++) { // TODO: find out how to estimate the real density // this code assumes that vein_unk_38 is the weight // used when choosing the vein material int size = layer->vein_unk_38[j]*cnt*level_cnt; df::inclusion_type type = layer->vein_type[j]; switch (type) { case inclusion_type::VEIN: // 3 veins of 80 tiles avg size = size * 80 * 3 / sums[type]; break; case inclusion_type::CLUSTER: // 1 cluster of 700 tiles avg size = size * 700 * 1 / sums[type]; break; case inclusion_type::CLUSTER_SMALL: size = size * 6 * 7 / sums[type]; break; case inclusion_type::CLUSTER_ONE: size = size * 1 * 5 / sums[type]; break; default: // shouldn't actually happen size = cnt*level_cnt; } veinMats[layer->vein_mat[j]].add(layer->bottom_height, 0); veinMats[layer->vein_mat[j]].add(layer->top_height, size); layer_size -= size; } layerMats[layer->mat_index].add(layer->top_height, std::max(0,layer_size)); } } // Print the report c->con << "Layer materials:" << std::endl; printMats<df::inorganic_raw, shallower>(c->con, layerMats, world->raws.inorganics, showValue); if (showHidden) { DFHack::Materials *mats = c->getMaterials(); printVeins(c->con, veinMats, mats, showValue); mats->Finish(); } c->con << "Warning: the above data is only a very rough estimate." << std::endl; return CR_OK; }