// 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 <dfhack/extra/MapExtras.h> #include <xgetopt.h> #include <dfhack/extra/termutil.h> typedef std::map<int16_t, unsigned int> MatMap; typedef std::map<int16_t, int> LevelMap; typedef std::vector< pair<int16_t, unsigned int> > 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::dfh_plant> PlantList; bool parseOptions(int argc, char **argv, bool &showHidden, bool &showPlants, bool &showSlade, bool &showTemple, bool &showLevels) { char c; xgetopt opt(argc, argv, "apstz"); opt.opterr = 0; while ((c = opt()) != -1) { switch (c) { case 'a': showHidden = true; break; case 'p': showPlants = false; break; case 's': showSlade = false; break; case 't': showTemple = false; break; case 'z': showLevels = true; break; case '?': switch (opt.optopt) { // For when we take arguments default: if (isprint(opt.optopt)) std::cerr << "Unknown option -" << opt.optopt << "!" << std::endl; else std::cerr << "Unknown option character " << (int) opt.optopt << "!" << std::endl; } default: // Um..... return false; } } return true; } 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); } }; inline void countMat(MatMap &mat, LevelMap &low, LevelMap &high, int16_t id, int z) { mat[id]++; if (!low.count(id)) low[id] = z; high[id] = z; } void printLevels(LevelMap &low, LevelMap &high, int16_t id, int z_base) { int lowv = low[id]; int highv = high[id]; std::cout << " (" << (z_base+low[id]) << ".." << (z_base+high[id]) << ")"; } void printMats(MatMap &mat, std::vector<DFHack::t_matgloss> &materials, bool showLevels, LevelMap &low, LevelMap &high, int z_base) { 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()) { cerr << "Bad index: " << it->first << " out of " << materials.size() << endl; continue; } DFHack::t_matgloss mat = materials[it->first]; std::cout << std::setw(25) << mat.id << " : " << it->second; if (showLevels) printLevels(low, high, it->first, z_base); std::cout << std::endl; total += it->second; } std::cout << ">>> TOTAL = " << total << std::endl << std::endl; } int main(int argc, char *argv[]) { bool temporary_terminal = TemporaryTerminal(); bool showHidden = false; bool showPlants = true; bool showSlade = true; bool showTemple = true; bool showLevels = false; if (!parseOptions(argc, argv, showHidden, showPlants, showSlade, showTemple, showLevels)) { return -1; } uint32_t x_max = 0, y_max = 0, z_max = 0; int32_t x_base = 0, y_base = 0, z_base = 0; DFHack::ContextManager manager("Memory.xml"); DFHack::Context *context = manager.getSingleContext(); if (!context->Attach()) { std::cerr << "Unable to attach to DF!" << std::endl; if(temporary_terminal) std::cin.ignore(); return 1; } DFHack::Maps *maps = context->getMaps(); if (!maps->Start()) { std::cerr << "Cannot get map info!" << std::endl; context->Detach(); if(temporary_terminal) std::cin.ignore(); return 1; } maps->getSize(x_max, y_max, z_max); maps->getPosition(x_base, y_base, z_base); MapExtras::MapCache map(maps); DFHack::Materials *mats = context->getMaterials(); if (!mats->ReadInorganicMaterials()) { std::cerr << "Unable to read inorganic material definitons!" << std::endl; context->Detach(); if(temporary_terminal) std::cin.ignore(); return 1; } if (showPlants && !mats->ReadOrganicMaterials()) { std::cerr << "Unable to read organic material definitons; plants won't be listed!" << std::endl; 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; int topMagmaLevel = -1; MatMap baseMats; LevelMap baseMatLow, baseMatHigh; MatMap layerMats; LevelMap layerMatLow, layerMatHigh; MatMap veinMats; LevelMap veinMatLow, veinMatHigh; MatMap plantMats; MatMap treeMats; LevelMap plantMatLow, plantMatHigh; if (!(showSlade && maps->ReadGlobalFeatures(globalFeatures))) { std::cerr << "Unable to read global features; slade won't be listed!" << std::endl; } if (!maps->ReadLocalFeatures(localFeatures)) { std::cerr << "Unable to read local features; adamantine " << (showTemple ? "and demon temples " : "") << "won't be listed!" << std::endl; } uint32_t vegCount = 0; DFHack::Vegetation *veg = context->getVegetation(); if (showPlants && !veg->Start(vegCount)) { std::cerr << "Unable to read vegetation; plants won't be listed!" << std::endl; } 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; } // Remember magma if (des.bits.flow_size && des.bits.liquid_type == 1) { topMagmaLevel = z; } uint16_t type = b->TileTypeAt(coord); const DFHack::TileRow *info = DFHack::getTileRow(type); if (!info) { std::cerr << "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 countMat(baseMats, baseMatLow, baseMatHigh, info->material, z); // Find the type of the tile switch (info->material) { case DFHack::SOIL: case DFHack::STONE: countMat(layerMats, layerMatLow, layerMatHigh, b->baseMaterialAt(coord), z); break; case DFHack::VEIN: countMat(veinMats, veinMatLow, veinMatHigh, b->veinMaterialAt(coord), z); break; case DFHack::FEATSTONE: if (blockFeatureLocal && des.bits.feature_local) { if (blockFeatureLocal->type == DFHack::feature_Adamantine_Tube && blockFeatureLocal->main_material == 0) // stone { countMat(veinMats, veinMatLow, veinMatHigh, blockFeatureLocal->sub_material, 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 { countMat(layerMats, layerMatLow, layerMatHigh, blockFeatureGlobal->sub_material, 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::t_plant & plant = (*it).sdata; DFHack::DFCoord loc(plant.x, plant.y); loc = loc % 16; if (showHidden || !b->DesignationAt(loc).bits.hidden) { MatMap &mmap = (plant.is_shrub ? plantMats : treeMats); countMat(mmap, plantMatLow, plantMatHigh, plant.material, z); } } } } // Block end } // block x // Clean uneeded memory map.trash(); } // block y } // z MatMap::const_iterator it; std::cout << "Base materials:" << std::endl; for (it = baseMats.begin(); it != baseMats.end(); ++it) { std::cout << std::setw(25) << DFHack::TileMaterialString[it->first] << " : " << it->second; if (showLevels) printLevels(baseMatLow, baseMatHigh, it->first, z_base); std::cout << std::endl; } std::cout << std::endl << "Layer materials:" << std::endl; printMats(layerMats, mats->inorganic, showLevels, layerMatLow, layerMatHigh, z_base); std::cout << "Vein materials:" << std::endl; printMats(veinMats, mats->inorganic, showLevels, veinMatLow, veinMatHigh, z_base); if (showPlants) { std::cout << "Shrubs:" << std::endl; printMats(plantMats, mats->organic, showLevels, plantMatLow, plantMatHigh, z_base); std::cout << "Wood in trees:" << std::endl; printMats(treeMats, mats->organic, showLevels, plantMatLow, plantMatHigh, z_base); } if (showLevels && topMagmaLevel >= 0) { std::cout << "Topmost magma z-level: " << (z_base + topMagmaLevel) << std::endl; } if (hasAquifer) { std::cout << "Has aquifer" << std::endl; } if (hasDemonTemple) { std::cout << "Has demon temple" << std::endl; } if (hasLair) { std::cout << "Has lair" << std::endl; } // Cleanup if (showPlants) { veg->Finish(); } mats->Finish(); maps->Finish(); context->Detach(); if(temporary_terminal) { std::cout << " Press any key to finish."; std::cin.ignore(); } std::cout << std::endl; return 0; }