// Produces a list of materials available on the map. // Options: // -a : show unrevealed tiles // -p : don't show plants // -s : don't show slade // -t : don't show demon temple //#include <cstdlib> #include <iostream> #include <iomanip> #include <map> #include <algorithm> #include <vector> using namespace std; #include <DFHack.h> #include <modules/MapCache.h> #include "Core.h" #include <Console.h> #include <PluginManager.h> using namespace DFHack; struct matdata { const static int invalid_z = -30000; matdata() { count = 0; lower_z = invalid_z; upper_z = invalid_z; } matdata (const matdata & copyme) { count = copyme.count; lower_z = copyme.lower_z; upper_z = copyme.upper_z; } unsigned int add( int z_level = invalid_z ) { count ++; if(z_level != invalid_z) { if(lower_z == invalid_z || z_level < lower_z) { lower_z = z_level; } if(upper_z == invalid_z || z_level > upper_z) { upper_z = z_level; } } return count; } unsigned int count; int lower_z; int upper_z; }; bool operator>(const matdata & q1, const matdata & q2) { return q1.count > q2.count; } typedef std::map<int16_t, matdata> MatMap; typedef std::vector< pair<int16_t, matdata> > MatSorter; typedef std::vector<DFHack::t_feature> FeatureList; typedef std::vector<DFHack::t_feature*> FeatureListPointer; typedef std::map<DFHack::DFCoord, FeatureListPointer> FeatureMap; typedef std::vector<DFHack::df_plant *> PlantList; #define TO_PTR_VEC(obj_vec, ptr_vec) \ ptr_vec.clear(); \ for (size_t i = 0; i < obj_vec.size(); i++) \ ptr_vec.push_back(&obj_vec[i]) template<template <typename> class P = std::greater > struct compare_pair_second { template<class T1, class T2> bool operator()(const std::pair<T1, T2>& left, const std::pair<T1, T2>& right) { return P<T2>()(left.second, right.second); } }; // printMats() accepts a vector of pointers to t_matgloss so that it can // deal t_matgloss and all subclasses. template <typename T> void printMats(DFHack::Console & con, MatMap &mat, std::vector<T*> &materials) { unsigned int total = 0; MatSorter sorting_vector; for (MatMap::const_iterator it = mat.begin(); it != mat.end(); ++it) { sorting_vector.push_back(*it); } std::sort(sorting_vector.begin(), sorting_vector.end(), compare_pair_second<>()); for (MatSorter::const_iterator it = sorting_vector.begin(); it != sorting_vector.end(); ++it) { if(it->first >= materials.size()) { con << "Bad index: " << it->first << " out of " << materials.size() << endl; continue; } T* mat = materials[it->first]; con << std::setw(25) << mat->ID << " : " << std::setw(9) << it->second.count; if(it->second.lower_z != it->second.upper_z) con <<" Z:" << std::setw(4) << it->second.lower_z << ".." << it->second.upper_z << std::endl; else con <<" Z:" << std::setw(4) << it->second.lower_z << std::endl; total += it->second.count; } con << ">>> TOTAL = " << total << std::endl << std::endl; } void printVeins(DFHack::Console & con, MatMap &mat_map, DFHack::Materials* mats) { MatMap ores; MatMap gems; MatMap rest; for (MatMap::const_iterator it = mat_map.begin(); it != mat_map.end(); ++it) { DFHack::df_inorganic_type *gloss = mats->df_inorganic->at(it->first); if (gloss->mat.isGem()) gems[it->first] = it->second; else if (gloss->isOre()) ores[it->first] = it->second; else rest[it->first] = it->second; } con << "Ores:" << std::endl; printMats(con, ores, *mats->df_inorganic); con << "Gems:" << std::endl; printMats(con, gems, *mats->df_inorganic); con << "Other vein stone:" << std::endl; printMats(con, rest, *mats->df_inorganic); } DFhackCExport command_result prospector (Core * c, vector <string> & parameters); DFhackCExport const char * plugin_name ( void ) { return "prospector"; } DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &commands) { commands.clear(); commands.push_back(PluginCommand("prospect","Show stats of available raw resources. Use option 'all' to show hidden resources.",prospector)); return CR_OK; } DFhackCExport command_result plugin_shutdown ( Core * c ) { return CR_OK; } DFhackCExport command_result prospector (DFHack::Core * c, vector <string> & parameters) { bool showHidden = false; bool showPlants = true; bool showSlade = true; bool showTemple = true; Console & con = c->con; for(int i = 0; i < parameters.size();i++) { if (parameters[0] == "all") { showHidden = true; } else if(parameters[i] == "help" || parameters[i] == "?") { c->con.print("Prints a big list of all the present minerals.\n" "By default, only the visible part of the map is scanned.\n" "\n" "Options:\n" "all - Scan the whole map, as if it was revealed.\n" ); return CR_OK; } } uint32_t x_max = 0, y_max = 0, z_max = 0; c->Suspend(); DFHack::Maps *maps = c->getMaps(); if (!maps->Start()) { con.printerr("Cannot get map info!\n"); c->Resume(); return CR_FAILURE; } maps->getSize(x_max, y_max, z_max); MapExtras::MapCache map(maps); DFHack::Materials *mats = c->getMaterials(); if (!mats->df_inorganic) { con.printerr("Unable to read inorganic material definitons!\n"); c->Resume(); return CR_FAILURE; } if (showPlants && !mats->df_organic) { con.printerr("Unable to read organic material definitons; plants won't be listed!\n"); showPlants = false; } FeatureList globalFeatures; FeatureMap localFeatures; DFHack::t_feature *blockFeatureGlobal = 0; DFHack::t_feature *blockFeatureLocal = 0; bool hasAquifer = false; bool hasDemonTemple = false; bool hasLair = false; MatMap baseMats; MatMap layerMats; MatMap veinMats; MatMap plantMats; MatMap treeMats; if (!(showSlade && maps->ReadGlobalFeatures(globalFeatures))) { con.printerr("Unable to read global features; slade won't be listed!\n"); } if (!maps->ReadLocalFeatures(localFeatures)) { con.printerr("Unable to read local features; adamantine and demon temples won't be listed.\n" ); } uint32_t vegCount = 0; DFHack::Vegetation *veg = c->getVegetation(); if (showPlants && !veg->Start()) { con.printerr("Unable to read vegetation; plants won't be listed!\n" ); } 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 DFHack::DFCoord blockCoord(b_x, b_y); MapExtras::Block *b = map.BlockAt(DFHack::DFCoord(b_x, b_y, z)); if (!b || !b->valid) { continue; } { // Find features uint16_t index = b->raw.global_feature; if (index != -1 && index < globalFeatures.size()) { blockFeatureGlobal = &globalFeatures[index]; } index = b->raw.local_feature; FeatureMap::const_iterator it = localFeatures.find(blockCoord); if (it != localFeatures.end()) { FeatureListPointer features = it->second; if (index != -1 && index < features.size()) { blockFeatureLocal = features[index]; } } } // Iterate over all the tiles in the block for(uint32_t y = 0; y < 16; y++) { for(uint32_t x = 0; x < 16; x++) { DFHack::DFCoord coord(x, y); DFHack::t_designation des = b->DesignationAt(coord); DFHack::t_occupancy occ = b->OccupancyAt(coord); // Skip hidden tiles if (!showHidden && des.bits.hidden) { continue; } // Check for aquifer if (des.bits.water_table) { hasAquifer = true; } // Check for lairs if (occ.bits.monster_lair) { hasLair = true; } uint16_t type = b->TileTypeAt(coord); const DFHack::TileRow *info = DFHack::getTileRow(type); if (!info) { con << "Bad type: " << type << std::endl; continue; } // We only care about these types switch (info->shape) { case DFHack::WALL: case DFHack::PILLAR: case DFHack::FORTIFICATION: break; default: continue; } // Count the material type baseMats[info->material].add(z); // Find the type of the tile switch (info->material) { case DFHack::SOIL: case DFHack::STONE: layerMats[b->baseMaterialAt(coord)].add(z); break; case DFHack::VEIN: veinMats[b->veinMaterialAt(coord)].add(z); break; case DFHack::FEATSTONE: if (blockFeatureLocal && des.bits.feature_local) { if (blockFeatureLocal->type == DFHack::feature_Adamantine_Tube && blockFeatureLocal->main_material == 0) // stone { veinMats[blockFeatureLocal->sub_material].add(z); } else if (showTemple && blockFeatureLocal->type == DFHack::feature_Hell_Temple) { hasDemonTemple = true; } } if (showSlade && blockFeatureGlobal && des.bits.feature_global && blockFeatureGlobal->type == DFHack::feature_Underworld && blockFeatureGlobal->main_material == 0) // stone { layerMats[blockFeatureGlobal->sub_material].add(z); } break; case DFHack::OBSIDIAN: // TODO ? break; } } } // Check plants this way, as the other way wasn't getting them all // and we can check visibility more easily here if (showPlants) { PlantList * plants; if (maps->ReadVegetation(b_x, b_y, z, plants)) { for (PlantList::const_iterator it = plants->begin(); it != plants->end(); it++) { const DFHack::df_plant & plant = *(*it); DFHack::DFCoord loc(plant.x, plant.y); loc = loc % 16; if (showHidden || !b->DesignationAt(loc).bits.hidden) { if(plant.is_shrub) plantMats[plant.material].add(z); else treeMats[plant.material].add(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) << DFHack::TileMaterialString[it->first] << " : " << it->second.count << std::endl; } con << std::endl << "Layer materials:" << std::endl; printMats(con, layerMats, *mats->df_inorganic); printVeins(con, veinMats, mats); if (showPlants) { con << "Shrubs:" << std::endl; printMats(con, plantMats, *mats->df_organic); con << "Wood in trees:" << std::endl; printMats(con, treeMats, *mats->df_organic); } if (hasAquifer) { con << "Has aquifer" << std::endl; } if (hasDemonTemple) { con << "Has demon temple" << std::endl; } if (hasLair) { con << "Has lair" << std::endl; } // Cleanup if (showPlants) { veg->Finish(); } mats->Finish(); maps->Finish(); c->Resume(); con << std::endl; return CR_OK; }