int main(int argc, char *argv[]) { /* initialize your non-curses data structures here */ signal(SIGINT, finish); /* arrange interrupts to terminate */ setlocale(LC_ALL,""); initscr(); /* initialize the curses library */ keypad(stdscr, TRUE); /* enable keyboard mapping */ nonl(); /* tell curses not to do NL->CR/NL on output */ cbreak(); /* take input chars one at a time, no wait for \n */ noecho(); /* don't echo input */ //nodelay(stdscr, true); keypad(stdscr, TRUE); scrollok(stdscr, TRUE); if (has_colors()) { start_color(); /* * Simple color assignment, often all we need. */ init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK); init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK); init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK); init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK); init_color(COLOR_CYAN, 700, 700, 700); // lt grey init_color(COLOR_MAGENTA, 500, 500, 500); // dk grey init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK); init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK); init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK); } int x_max,y_max,z_max; uint32_t x_max_a,y_max_a,z_max_a; /* uint16_t tiletypes[16][16]; DFHack::t_designation designations[16][16]; uint8_t regionoffsets[16]; */ map <int16_t, uint32_t> materials; materials.clear(); mapblock40d blocks[3][3]; vector<DFHack::t_effect_df40d> effects; vector< vector <uint16_t> > layerassign; vector<t_vein> veinVector; vector<t_frozenliquidvein> IceVeinVector; vector<t_spattervein> splatter; vector<t_grassvein> grass; vector<t_worldconstruction> wconstructs; t_temperatures b_temp1; t_temperatures b_temp2; DFHack::Materials * Mats = 0; DFHack::Maps * Maps = 0; DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context* DF; try { pDF = DF = DFMgr.getSingleContext(); DF->Attach(); Maps = DF->getMaps(); } catch (exception& e) { cerr << e.what() << endl; #ifndef LINUX_BUILD cin.ignore(); #endif finish(0); } bool hasmats = true; try { Mats = DF->getMaterials(); } catch (exception& e) { hasmats = false; } // init the map if(!Maps->Start()) { error = "Can't find a map to look at."; finish(0); } Maps->getSize(x_max_a,y_max_a,z_max_a); x_max = x_max_a; y_max = y_max_a; z_max = z_max_a; bool hasInorgMats = false; bool hasPlantMats = false; bool hasCreatureMats = false; if(hasmats) { // get stone matgloss mapping if(Mats->ReadInorganicMaterials()) { hasInorgMats = true; } if(Mats->ReadCreatureTypes()) { hasCreatureMats = true; } if(Mats->ReadOrganicMaterials()) { hasPlantMats = true; } } /* // get region geology if(!DF.ReadGeology( layerassign )) { error = "Can't read local geology."; pDF = 0; finish(0); } */ // FIXME: could fail on small forts int cursorX = x_max/2 - 1; int cursorY = y_max/2 - 1; int cursorZ = z_max/2 - 1; bool dig = false; bool dump = false; bool digbit = false; bool dotwiddle; unsigned char twiddle = 0; int vein = 0; int filenum = 0; bool dirtybit = false; uint32_t blockaddr = 0; uint32_t blockaddr2 = 0; t_blockflags bflags; bflags.whole = 0; enum e_tempmode { TEMP_NO, TEMP_1, TEMP_2, WATER_SALT, WATER_STAGNANT }; e_tempmode temperature = TEMP_NO; // resume so we don't block DF while we wait for input DF->Resume(); for (;;) { dig = false; dump = false; dotwiddle = false; digbit = false; int c = getch(); /* refresh, accept single keystroke of input */ flushinp(); clrscr(); /* process the command keystroke */ switch(c) { case KEY_DOWN: cursorY ++; break; case KEY_UP: cursorY --; break; case KEY_LEFT: cursorX --; break; case KEY_RIGHT: cursorX ++; break; case KEY_NPAGE: cursorZ --; break; case KEY_PPAGE: cursorZ ++; break; case '+': vein ++; break; case 'd': dig = true; break; case 'o': dump = true; break; case '-': vein --; break; case 'z': digbit = true; break; case '/': if(twiddle != 0) twiddle--; break; case '*': twiddle++; break; case 't': dotwiddle = true; break; case 'b': temperature = TEMP_NO; break; case 'n': temperature = TEMP_1; break; case 'm': temperature = TEMP_2; break; case 'c': temperature = WATER_SALT; break; case 'v': temperature = WATER_STAGNANT; break; case 27: // escape key DF->Detach(); return 0; break; default: break; } cursorX = max(cursorX, 0); cursorY = max(cursorY, 0); cursorZ = max(cursorZ, 0); cursorX = min(cursorX, x_max - 1); cursorY = min(cursorY, y_max - 1); cursorZ = min(cursorZ, z_max - 1); if(twiddle > 31) twiddle = 31; // clear data before we suspend memset(blocks,0,sizeof(blocks)); veinVector.clear(); IceVeinVector.clear(); effects.clear(); splatter.clear(); grass.clear(); dirtybit = 0; // Supend, read/write data DF->Suspend(); // restart cleared modules Maps->Start(); if(hasmats) { Mats->Start(); if(hasInorgMats) { Mats->ReadInorganicMaterials(); } if(hasPlantMats) { Mats->ReadOrganicMaterials(); } if(hasCreatureMats) { Mats->ReadCreatureTypes(); } } /* if(DF.InitReadEffects(effectnum)) { for(uint32_t i = 0; i < effectnum;i++) { t_effect_df40d effect; DF.ReadEffect(i,effect); effects.push_back(effect); } } */ for(int i = -1; i <= 1; i++) for(int j = -1; j <= 1; j++) { mapblock40d * Block = &blocks[i+1][j+1]; if(Maps->isValidBlock(cursorX+i,cursorY+j,cursorZ)) { Maps->ReadBlock40d(cursorX+i,cursorY+j,cursorZ, Block); // extra processing of the block in the middle if(i == 0 && j == 0) { if(hasInorgMats) do_features(DF, Block, cursorX, cursorY, 50,10, Mats->inorganic); // read veins Maps->ReadVeins(cursorX+i,cursorY+j,cursorZ,&veinVector,&IceVeinVector,&splatter,&grass, &wconstructs); // get pointer to block blockaddr = Maps->getBlockPtr(cursorX+i,cursorY+j,cursorZ); blockaddr2 = Block->origin; // dig all veins and trees if(dig) { for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) { int16_t tiletype = Block->tiletypes[x][y]; TileShape tc = tileShape(tiletype); TileMaterial tm = tileMaterial(tiletype); if( tc == WALL && tm == VEIN || tc == TREE_OK || tc == TREE_DEAD) { Block->designation[x][y].bits.dig = designation_default; } } Maps->WriteDesignations(cursorX+i,cursorY+j,cursorZ, &(Block->designation)); } // read temperature data Maps->ReadTemperatures(cursorX+i,cursorY+j,cursorZ,&b_temp1, &b_temp2 ); if(dotwiddle) { bitset<32> bs = Block->designation[0][0].whole; bs.flip(twiddle); Block->designation[0][0].whole = bs.to_ulong(); Maps->WriteDesignations(cursorX+i,cursorY+j,cursorZ, &(Block->designation)); dotwiddle = false; } // do a dump of the block data if(dump) { hexdump(DF,blockaddr,0x1E00,filenum); filenum++; } // read/write dirty bit of the block Maps->ReadDirtyBit(cursorX+i,cursorY+j,cursorZ,dirtybit); Maps->ReadBlockFlags(cursorX+i,cursorY+j,cursorZ,bflags); if(digbit) { dirtybit = !dirtybit; Maps->WriteDirtyBit(cursorX+i,cursorY+j,cursorZ,dirtybit); } } } } // Resume, print stuff to the terminal DF->Resume(); for(int i = -1; i <= 1; i++) for(int j = -1; j <= 1; j++) { mapblock40d * Block = &blocks[i+1][j+1]; for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) { int color = COLOR_BLACK; color = pickColor(Block->tiletypes[x][y]); /* if(!Block->designation[x][y].bits.hidden) { puttile(x+(i+1)*16,y+(j+1)*16,Block->tiletypes[x][y], color); } else*/ { attron(A_STANDOUT); puttile(x+(i+1)*16,y+(j+1)*16,Block->tiletypes[x][y], color); attroff(A_STANDOUT); } } // print effects for the center tile /* if(i == 0 && j == 0) { for(uint zz = 0; zz < effects.size();zz++) { if(effects[zz].z == cursorZ && !effects[zz].isHidden) { // block coords to tile coords uint16_t x = effects[zz].x - (cursorX * 16); uint16_t y = effects[zz].y - (cursorY * 16); if(x < 16 && y < 16) { putch(x + 16,y + 16,'@',COLOR_WHITE); } } } } */ } gotoxy(50,0); cprintf("arrow keys, PGUP, PGDN = navigate"); gotoxy(50,1); cprintf("+,- = switch vein"); gotoxy(50,2); uint32_t mineralsize = veinVector.size(); uint32_t icesize = IceVeinVector.size(); uint32_t splattersize = splatter.size(); uint32_t grasssize = grass.size(); uint32_t wconstrsize = wconstructs.size(); uint32_t totalVeinSize = mineralsize+ icesize + splattersize + grasssize + wconstrsize; if(vein == totalVeinSize) vein = totalVeinSize - 1; if(vein < -1) vein = -1; cprintf("X %d/%d, Y %d/%d, Z %d/%d. Vein %d of %d",cursorX+1,x_max,cursorY+1,y_max,cursorZ,z_max,vein+1,totalVeinSize); if(!veinVector.empty() || !IceVeinVector.empty() || !splatter.empty() || !grass.empty() || !wconstructs.empty()) { if(vein != -1 && vein < totalVeinSize) { uint32_t realvein = 0; if(vein < mineralsize) { realvein = vein; //iterate through vein rows for(uint32_t j = 0;j<16;j++) { //iterate through the bits for (uint32_t k = 0; k< 16;k++) { // and the bit array with a one-bit mask, check if the bit is set bool set = !!(((1 << k) & veinVector[realvein].assignment[j]) >> k); if(set) { putch(k+16,j+16,'$',COLOR_RED); } } } if(hasInorgMats) { gotoxy(50,3); cprintf("Mineral: %s",Mats->inorganic[veinVector[vein].type].id); } } else if (vein < mineralsize + icesize) { realvein = vein - mineralsize; t_frozenliquidvein &frozen = IceVeinVector[realvein]; for(uint32_t i = 0;i<16;i++) { for (uint32_t j = 0; j< 16;j++) { int color = COLOR_BLACK; int tile = frozen.tiles[i][j]; color = pickColor(tile); attron(A_STANDOUT); puttile(i+16,j+16,tile, color); attroff(A_STANDOUT); } } gotoxy(50,3); cprintf("ICE"); } else if(vein < mineralsize + icesize + splattersize) { realvein = vein - mineralsize - icesize; for(uint32_t yyy = 0; yyy < 16; yyy++) { for(uint32_t xxx = 0; xxx < 16; xxx++) { uint8_t intensity = splatter[realvein].intensity[xxx][yyy]; if(intensity) { attron(A_STANDOUT); putch(xxx+16,yyy+16,'*', COLOR_RED); attroff(A_STANDOUT); } } } if(hasCreatureMats) { gotoxy(50,3); cprintf("Spatter: %s",PrintSplatterType(splatter[realvein].mat1,splatter[realvein].mat2,Mats->race).c_str()); } } else if(vein < mineralsize + icesize + splattersize + grasssize) { realvein = vein - mineralsize - icesize - splattersize; t_grassvein & grassy =grass[realvein]; for(uint32_t yyy = 0; yyy < 16; yyy++) { for(uint32_t xxx = 0; xxx < 16; xxx++) { uint8_t intensity = grassy.intensity[xxx][yyy]; if(intensity) { attron(A_STANDOUT); putch(xxx+16,yyy+16,'X', COLOR_RED); attroff(A_STANDOUT); } } } if(hasPlantMats) { gotoxy(50,3); cprintf("Grass: 0x%x, %s",grassy.address_of, Mats->organic[grassy.material].id); } } else { realvein = vein - mineralsize - icesize - splattersize - grasssize; t_worldconstruction & wconstr=wconstructs[realvein]; for(uint32_t j = 0; j < 16; j++) { for(uint32_t k = 0; k < 16; k++) { bool set = !!(((1 << k) & wconstr.assignment[j]) >> k); if(set) { putch(k+16,j+16,'$',COLOR_RED); } } } if(hasInorgMats) { gotoxy(50,3); cprintf("Road: 0x%x, %d - %s", wconstr.address_of, wconstr.material,Mats->inorganic[wconstr.material].id); } } }
// FIXME: use block cache, break into manageable bits int main (void) { srand ( (unsigned int)time(NULL) ); //Message of intent cout << "DF Hole" << endl << "This tool will instantly dig a chasm, pit, pipe, etc through hell, wherever your cursor is." << endl << "This can not be undone! End program now if you don't want hellish fun." << endl ; //User selection of settings should have it own routine, a structure for settings, I know //sloppy mess, but this is just a demo utility. //Pit Types. e_pitType pittype = selectPitType(); //Here are all the settings. //Default values are set here. int pitdepth=0; int roof=-1; int holeradius=6; int wallthickness=1; int wallpillar=1; int holepillar=1; int exposehell = 0; int fillmagma=0; int fillwater=0; int stopatmagma=0; int exposemagma=0; int aquify=0; //The Tile Type to use for the walls lining the hole //263 is semi-molten rock, 331 is obsidian uint32_t whell=263, wmolten=263, wmagma=331, wcave=331; //The Tile Type to use for the hole's floor at bottom of the map //35 is chasm, 42 is eerie pit , 340 is obsidian floor, 344 is featstone floor, 264 is 'magma flow' floor uint32_t floor=35, cap=340; int floorvar=0; //Modify default settings based on pit type. switch ( pittype ) { case pitTypeChasm: floor=35; break; case pitTypeEerie: floor=42; break; case pitTypeFloor: floor=344; floorvar=3; break; case pitTypeSolid: holeradius=0; wallthickness=7; wallpillar=4; break; case pitTypeOasis: stopatmagma=-1; fillwater=-1; holeradius=5; wallthickness=2; //aquify=-1; floor=340; floorvar=3; break; case pitTypeOPool: pitdepth=5; fillwater=-1; holeradius=5; wallthickness=2; //aquify=-1; floor=340; floorvar=3; break; case pitTypeMagma: stopatmagma=-1; exposemagma=-1; wallthickness=2; fillmagma=-1; floor=264; break; case pitTypeMPool: pitdepth=5; wallthickness=2; fillmagma=-1; floor=340; floorvar=3; break; } //Should tiles be revealed? int reveal=0; int accept = getyesno("Use default settings?",1); while ( !accept ) { //Pit Depth pitdepth = getint( "Enter max depth (0 for bottom of map)", 0, INT_MAX, pitdepth ); //Hole Size holeradius = getint( "Enter hole radius, 0 to 16", 0, 16, holeradius ); //Wall thickness wallthickness = getint( "Enter wall thickness, 0 to 16", 0, 16, wallthickness ); //Obsidian Pillars holepillar = getint( "Number of Obsidian Pillars in hole, 0 to 255", 0, 255, holepillar ); wallpillar = getint( "Number of Obsidian Pillars in wall, 0 to 255", 0, 255, wallpillar ); //Open Hell? exposehell=getyesno("Expose the pit to hell (no walls in hell)?",exposehell); //Stop when magma sea is hit? stopatmagma=getyesno("Stop at magma sea?",stopatmagma); exposemagma=getyesno("Expose magma sea (no walls in magma)?",exposemagma); //Fill? fillmagma=getyesno("Fill with magma?",fillmagma); if (fillmagma) aquify=fillwater=0; fillwater=getyesno("Fill with water?",fillwater); //aquify=getyesno("Aquifer?",aquify); /////////////////////////////////////////////////////////////////////////////////////////////// //Print settings. //If a settings struct existed, this could be in a routine printf("Using Settings:\n"); printf("Pit Type......: %d = %s\n", pittype, pitTypeDesc[pittype]); printf("Depth.........: %d\n", pitdepth); printf("Hole Radius...: %d\n", holeradius); printf("Wall Thickness: %d\n", wallthickness); printf("Pillars, Hole.: %d\n", holepillar); printf("Pillars, Wall.: %d\n", wallpillar); printf("Expose Hell...: %c\n", (exposehell?'Y':'N') ); printf("Stop at Magma.: %c\n", (stopatmagma?'Y':'N') ); printf("Expose Magma..: %c\n", (exposemagma?'Y':'N') ); printf("Magma Fill....: %c\n", (fillmagma?'Y':'N') ); printf("Water Fill....: %c\n", (fillwater?'Y':'N') ); printf("Aquifer.......: %c\n", (aquify?'Y':'N') ); accept = getyesno("Accept these settings?",1); } int64_t n; uint32_t x_max,y_max,z_max; //Pattern to dig unsigned char pattern[16][16]; for (int regen=1;regen; ) { regen=0; memset(pattern,0,sizeof(pattern)); //Calculate a randomized circle. //These values found through experimentation. int x=0, y=0, n=0; //Two concentric irregular circles //Outer circle, solid. if ( wallthickness ) { drawcircle(holeradius+wallthickness, pattern, 2); } //Inner circle, hole. if ( holeradius ) { drawcircle(holeradius, pattern, 1); } //Post-process to be certain the wall totally encloses hole. if (wallthickness) { for (y=0;y<16;++y) { for (x=0;x<16;++x) { if ( 1==pattern[x][y] ) { //No hole at edges. if ( x<1 || x>14 || y<1 || y>14 ) { pattern[x][y]=2; } } else if ( 0==pattern[x][y] ) { //check neighbors checkneighbors( pattern , x,y, 1, 2); } } } } //Makes sure that somewhere random gets a vertical pillar of rock which is safe //to dig stairs down, to permit access to anywhere within the pit from the top. for (n=holepillar; n ; --n) { settileat( pattern , 1 , 3 , rand()&255 ); } for (n=wallpillar; n ; --n) { settileat( pattern , 2 , 3 , rand()&255 ); } //Note: //At this point, the pattern holds: //0 for all tiles which will be ignored. //1 for all tiles set to empty pit space. //2 for all normal walls. //3 for the straight obsidian top-to-bottom wall. //4 is randomized between wall or floor (!not implemented!) printf("\nPattern:\n"); const char patternkey[] = ".cW!?567890123"; //Print the pattern for (y=0;y<16;++y) { for (x=0;x<16;++x) { cout << patternkey[ pattern[x][y] ]; } cout << endl; } cout << endl; regen = !getyesno("Acceptable Pattern?",1); } //Post-process settings to fix problems here if (pitdepth<1) { pitdepth=INT_MAX; } /////////////////////////////////////////////////////////////////////////////////////////////// cerr << "Loading memory map..." << endl; //Connect to DF! DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context *DF = DFMgr.getSingleContext(); //Init cerr << "Attaching to DF..." << endl; try { DF->Attach(); } catch (exception& e) { cerr << e.what() << endl; #ifndef LINUX_BUILD cin.ignore(); #endif return 1; } // init the map DFHack::Maps *Mapz = DF->getMaps(); if (!Mapz->Start()) { cerr << "Can't init map. Exiting." << endl; #ifndef LINUX_BUILD cin.ignore(); #endif return 1; } Mapz->getSize(x_max,y_max,z_max); //Get cursor int32_t cursorX, cursorY, cursorZ; DFHack::Gui *Gui = DF->getGui(); Gui->getCursorCoords(cursorX,cursorY,cursorZ); if (-30000==cursorX) { cout << "No cursor position found. Exiting." << endl; #ifndef LINUX_BUILD cin.ignore(); #endif return 1; } //Block coordinates int32_t bx=cursorX/16, by=cursorY/16, bz=cursorZ; //Tile coordinates within block int32_t tx=cursorX%16, ty=cursorY%16, tz=cursorZ; /* //Access the DF interface to pause the game. //Copied from the reveal tool. DFHack::Gui *Gui =DF->getGui(); cout << "Pausing..." << endl; Gui->SetPauseState(true); DF->Resume(); waitmsec(1000); DF->Suspend(); */ //Verify that every z-level at this location exists. for (int32_t Z = 0; Z<= bz ;Z++) { if ( ! Mapz->isValidBlock(bx,by,Z) ) { cout << "This block does't exist! Exiting." << endl; #ifndef LINUX_BUILD cin.ignore(); #endif return 1; } } //Get all the map features. vector<DFHack::t_feature> global_features; if (!Mapz->ReadGlobalFeatures(global_features)) { cout << "Couldn't load global features! Probably a version problem." << endl; #ifndef LINUX_BUILD cin.ignore(); #endif return 1; } std::map <DFHack::DFCoord, std::vector<DFHack::t_feature *> > local_features; if (!Mapz->ReadLocalFeatures(local_features)) { cout << "Couldn't load local features! Probably a version problem." << endl; #ifndef LINUX_BUILD cin.ignore(); #endif return 1; } //Get info on current tile, to determine how to generate the pit mapblock40d topblock; Mapz->ReadBlock40d( bx, by, bz , &topblock ); //Related block info DFCoord pc(bx,by); mapblock40d block; const TileRow * tp; t_designation * d; ////////////////////////////////////// //From top to bottom, dig this thing. ////////////////////////////////////// //Top level, cap. //Might make this an option in the future //For now, no wall means no cap. if (wallthickness) { Mapz->ReadBlock40d( bx, by, bz , &block ); for (uint32_t x=0;x<16;++x) { for (uint32_t y=0;y<16;++y) { if ( (pattern[x][y]>1) || (roof && pattern[x][y]) ) { tp = getTileRow(block.tiletypes[x][y]); d = &block.designation[x][y]; //Only modify this level if it's 'empty' if ( EMPTY != tp->shape && RAMP_TOP != tp->shape && STAIR_DOWN != tp->shape && DFHack::TILE_STREAM_TOP != tp->special) { continue; } //Need a floor for empty space. if (reveal) { d->bits.hidden = 0; //topblock.designation[x][y].bits.hidden; } //Always clear the dig designation. d->bits.dig = designation_no; //unlock fluids, so they fall down the pit. d->bits.flow_forbid = d->bits.liquid_static=0; block.blockflags.bits.liquid_1 = block.blockflags.bits.liquid_2 = 1; //Remove aquifer, to prevent bugginess d->bits.water_table=0; //Set the tile. block.tiletypes[x][y] = cap + rand()%4; } } } //Write the block. Mapz->WriteBlockFlags(bx,by,bz, block.blockflags ); Mapz->WriteDesignations(bx,by,bz, &block.designation ); Mapz->WriteTileTypes(bx,by,bz, &block.tiletypes ); Mapz->WriteDirtyBit(bx,by,bz,1); } /////////////////////////////////////////////////////////////////////////////////////////////// //All levels in between. int done=0; uint32_t t,v; int32_t z = bz-1; int32_t bottom = max(0,bz-pitdepth-1); assert( bottom>=0 && bottom<=bz ); for ( ; !done && z>=bottom ; --z) { int watercount=0; int magmacount=0; int moltencount=0; int rockcount=0; int veincount=0; int emptycount=0; int hellcount=0; int templecount=0; int adamcount=0; int featcount=0; int tpat; cout << z << endl; assert( Mapz->isValidBlock(bx,by,z) ); if (!Mapz->ReadBlock40d( bx, by, z , &block )) { cout << "Bad block! " << bx << "," << by << "," << z << endl; } //Pre-process this z-level, to get some tile statistics. for (int32_t x=0;x<16;++x) { for (int32_t y=0;y<16;++y) { t=0; tp = getTileRow(block.tiletypes[x][y]); d = &block.designation[x][y]; tpat=pattern[x][y]; //Tile type material categories switch ( tp->material ) { case AIR: ++emptycount; break; case MAGMA: ++moltencount; break; case VEIN: ++veincount; break; case FEATSTONE: case HFS: case OBSIDIAN: //basicly, ignored. break; default: if ( EMPTY == tp->shape || RAMP_TOP == tp->shape || STAIR_DOWN == tp->shape ) { ++emptycount; } else { ++rockcount; } break; } //Magma and water if ( d->bits.flow_size ) { if (d->bits.liquid_type) { ++magmacount; } else { ++watercount; } } //Check for Features if ( block.local_feature > -1 || block.global_feature > -1 ) { //Count tiles which actually are in the feature. //It is possible for a block to have a feature, but no tiles to be feature. if ( d->bits.feature_global || d->bits.feature_local ) { //All features ++featcount; if ( d->bits.feature_global && d->bits.feature_local ) { cout << "warn:tile is global and local at same time!" << endl; } n=0; if ( block.global_feature > -1 && d->bits.feature_global ) { n=global_features[block.global_feature].type; switch ( n ) { case feature_Other: //no count break; case feature_Adamantine_Tube: ++adamcount; break; case feature_Underworld: ++hellcount; break; case feature_Hell_Temple: ++templecount; break; default: //something here. for debugging, it may be interesting to know. if (n) cout << '(' << n << ')'; } } n=0; if ( block.local_feature > -1 && d->bits.feature_local ) { n=local_features[pc][block.local_feature]->type; switch ( n ) { case feature_Other: //no count break; case feature_Adamantine_Tube: ++adamcount; break; case feature_Underworld: ++hellcount; break; case feature_Hell_Temple: ++templecount; break; default: //something here. for debugging, it may be interesting to know. if (n) cout << '[' << n << ']'; } } } } } } //If stopping at magma, and no no non-feature stone in this layer, and magma found, then we're either at //or below the magma sea / molten rock. if ( stopatmagma && (moltencount || magmacount) && (!exposemagma || !rockcount) ) { //If not exposing magma, quit at the first sign of magma. //If exposing magma, quite once magma is exposed. done=-1; } ///////////////////////////////////////////////////////////////////////////////////////////////// //Some checks, based on settings and stats collected //First check, are we at illegal depth? if ( !done && hellcount && stopatmagma ) { //Panic! done=-1; tpat=0; cout << "error: illegal breach of hell!" << endl; } ///////////////////////////////////////////////////////////////////////////////////////////////// //Actually process the current z-level. //These loops do the work. for (int32_t x=0;!done && x<16;++x) { for (int32_t y=0;!done && y<16;++y) { t=0; tp = getTileRow(block.tiletypes[x][y]); d = &block.designation[x][y]; tpat=pattern[x][y]; //Up front, remove aquifer, to prevent bugginess //It may be added back if aquify is set. d->bits.water_table=0; //Change behaviour based on settings and stats from this z-level //In hell? if ( tpat && tpat!=3 && isfeature(global_features, local_features,block,pc,x,y,feature_Underworld ) ) { if ( exposehell ) { tpat=0; } } //Expose magma? if ( tpat && tpat!=3 && exposemagma ) { //Leave certain tiles unchanged. switch ( tp->material ) { case HFS: case FEATSTONE: case MAGMA: tpat=0; default: break; } //Adamantine may be left unchanged... if ( isfeature(global_features, local_features,block,pc,x,y,feature_Adamantine_Tube ) ) { tpat=0; } //Leave magma sea unchanged. if ( d->bits.flow_size && d->bits.liquid_type) { tpat=0; } } //For all situations... //Special modification for walls, always for adamantine. if ( isfeature(global_features, local_features,block,pc,x,y,feature_Adamantine_Tube ) ) { if ( 2==pattern[x][y] || 3==pattern[x][y] ) { tpat=2; } } //Border or space? switch (tpat) { case 0: continue; break; case 1: //Empty Space t=32; //d->bits.light = topblock.designation[x][y].bits.light; //d->bits.skyview = topblock.designation[x][y].bits.skyview; //d->bits.subterranean = topblock.designation[x][y].bits.subterranean; //Erase special markers? //d->bits.feature_global = d->bits.feature_local = 0; //Water? Magma? if (fillmagma || fillwater) { d->bits.flow_size=7; d->bits.water_stagnant = false; d->bits.water_salt = false; if (fillmagma) { d->bits.liquid_type=liquid_magma; } else { d->bits.liquid_type=liquid_water; } } else { //Otherwise, remove all liquids. d->bits.flow_size=0; d->bits.water_stagnant = false; d->bits.water_salt = false; d->bits.liquid_type = liquid_water; } break; case 2: //Wall. //First guess based on current material switch ( tp->material ) { case OBSIDIAN: t=wmagma; break; case MAGMA: t=wmolten; break; case HFS: //t=whell; break; case VEIN: t=440; //Solid vein block break; case FEATSTONE: t=335; //Solid feature stone block break; default: t=wcave; } //Adamantine (a local feature) trumps veins. { //Local Feature? if ( block.local_feature > -1 ) { switch ( n=local_features[pc][block.local_feature]->type ) { case feature_Underworld: case feature_Hell_Temple: //Only adopt these if there is no global feature present if ( block.global_feature >-1 ) { break; } case feature_Adamantine_Tube: //Always for adamantine, sometimes for others //Whatever the feature is made of. "featstone wall" d->bits.feature_global = 0; d->bits.feature_local = 1; t=335; break; } } //Global Feature? else if (block.global_feature > -1 && !d->bits.feature_local ) { switch ( n=global_features[block.global_feature].type ) { case feature_Adamantine_Tube: case feature_Underworld: case feature_Hell_Temple: //Whatever the feature is made of. "featstone wall" d->bits.feature_global = 1; t=335; break; } } } //Erase any liquids, as they cause problems. d->bits.flow_size=0; d->bits.water_stagnant = false; d->bits.water_salt = false; d->bits.liquid_type=liquid_water; //Placing an aquifer? //(bugged, these aquifers don't generate water!) if ( aquify ) { //Only normal stone types can be aquified if ( tp->material!=MAGMA && tp->material!=FEATSTONE && tp->material!=HFS ) { //Only place next to the hole. //If no hole, place in middle. if ( checkneighbors(pattern,x,y,1) || (7==x && 7==y) ) { d->bits.water_table = 1; //t=265; //soil wall } } } break; case 3: ////No obsidian walls on bottom of map! //if(z<1 && (d->bits.feature_global || d->bits.feature_local) ) { // t=335; //} //Special wall, always sets to obsidian, to give a stairway t=331; //Erase special markers d->bits.feature_global = d->bits.feature_local = 0; //Erase any liquids, as they cause problems. d->bits.flow_size=0; d->bits.water_stagnant = false; d->bits.water_salt = false; d->bits.liquid_type=liquid_water; break; default: cout << ".error,bad pattern."; } //For all tiles. if (reveal) { d->bits.hidden = 0; //topblock.designation[x][y].bits.hidden; } //Always clear the dig designation. d->bits.dig=designation_no; //Make it underground, because it is capped d->bits.subterranean=1; d->bits.light=0; d->bits.skyview=0; //unlock fluids, so they fall down the pit. d->bits.flow_forbid = d->bits.liquid_static=0; block.blockflags.bits.liquid_1 = block.blockflags.bits.liquid_2 = 1; //Set the tile. block.tiletypes[x][y] = t; } } //Write the block. Mapz->WriteBlockFlags(bx,by,z, block.blockflags ); Mapz->WriteDesignations(bx,by,z, &block.designation ); Mapz->WriteTileTypes(bx,by,z, &block.tiletypes ); Mapz->WriteDirtyBit(bx,by,z,1); } //Re-process the last z-level handled above. z++; assert( z>=0 ); /////////////////////////////////////////////////////////////////////////////////////////////// //The bottom level is special. if (-1) { if (!Mapz->ReadBlock40d( bx, by, z , &block )) { cout << "Bad block! " << bx << "," << by << "," << z << endl; } for (uint32_t x=0;x<16;++x) { for (uint32_t y=0;y<16;++y) { t=floor; v=floorvar; tp = getTileRow(block.tiletypes[x][y]); d = &block.designation[x][y]; if ( exposehell ) { //Leave hell tiles unchanged when exposing hell. if ( isfeature(global_features,local_features,block,pc,x,y,feature_Underworld) ) { continue; } } //Does expose magma need anything at this level? if ( exposemagma && stopatmagma ) { continue; } switch (pattern[x][y]) { case 0: continue; break; case 1: //Empty becomes floor. //Base floor type on the z-level first, features, then tile type. if (!z) { //Bottom of map, use the floor specified, always. break; } ////Only place floor where ground is already solid when exposing //if( EMPTY == tp->c || RAMP_TOP == tp->c || STAIR_DOWN == tp->c ){ // continue; //} if ( d->bits.feature_global || d->bits.feature_global ) { //Feature Floor! t=344; break; } //Tile material check. switch ( tp->material ) { case OBSIDIAN: t=340; v=3; break; case MAGMA: v=0; t=264; //magma flow break; case HFS: //should only happen at bottom of map break; case VEIN: t=441; //vein floor v=3; break; case FEATSTONE: t=344; v=3; break; } break; case 2: case 3: //Walls already drawn. //Ignore. continue; break; } //For all tiles. if (reveal) d->bits.hidden = 0; //topblock.designation[x][y].bits.hidden; //Always clear the dig designation. d->bits.dig=designation_no; //unlock fluids d->bits.flow_forbid = d->bits.liquid_static=0; block.blockflags.bits.liquid_1 = block.blockflags.bits.liquid_2 = 1; //Set the tile. block.tiletypes[x][y] = t + ( v ? rand()&v : 0 ); } } //Write the block. Mapz->WriteBlockFlags(bx,by,z, block.blockflags ); Mapz->WriteDesignations(bx,by,z, &block.designation ); Mapz->WriteTileTypes(bx,by,z, &block.tiletypes ); Mapz->WriteDirtyBit(bx,by,z,1); } DF->Detach(); #ifndef LINUX_BUILD cout << "Done. Press any key to continue" << endl; cin.ignore(); #endif return 0; }
static command_result autodump_main(Core * c, vector <string> & parameters) { // Command line options bool destroy = false; bool here = false; if(parameters.size() > 0) { string & p = parameters[0]; if(p == "destroy") destroy = true; else if (p == "destroy-here") destroy = here = true; else if(p == "?" || p == "help") { c->con.print( "This utility lets you quickly move all items designated to be dumped.\n" "Items are instantly moved to the cursor position, the dump flag is unset,\n" "and the forbid flag is set, as if it had been dumped normally.\n" "Be aware that any active dump item tasks still point at the item.\n\n" "Options:\n" "destroy - instead of dumping, destroy the items instantly.\n" "destroy-here - only affect the tile under cursor.\n" ); return CR_OK; } } c->Suspend(); DFHack::VersionInfo *mem = c->vinfo; DFHack::Gui * Gui = c->getGui(); DFHack::Items * Items = c->getItems(); DFHack::Maps *Maps = c->getMaps(); vector <df_item*> p_items; if(!Items->readItemVector(p_items)) { c->con.printerr("Can't access the item vector.\n"); return CR_FAILURE; } std::size_t numItems = p_items.size(); // init the map if(!Maps->Start()) { c->con.printerr("Can't initialize map.\n"); return CR_FAILURE; } MapCache MC (Maps); int i = 0; int dumped_total = 0; int cx, cy, cz; DFCoord pos_cursor; if(!destroy || here) { if (!Gui->getCursorCoords(cx,cy,cz)) { c->con.printerr("Cursor position not found. Please enabled the cursor.\n"); return CR_FAILURE; } pos_cursor = DFCoord(cx,cy,cz); } if (!destroy) { { Block * b = MC.BlockAt(pos_cursor / 16); if(!b) { c->con.printerr("Cursor is in an invalid/uninitialized area. Place it over a floor.\n"); return CR_FAILURE; } uint16_t ttype = MC.tiletypeAt(pos_cursor); if(!DFHack::isFloorTerrain(ttype)) { c->con.printerr("Cursor should be placed over a floor.\n"); return CR_FAILURE; } } } coordmap counts; // proceed with the dumpification operation for(std::size_t i=0; i< numItems; i++) { df_item * itm = p_items[i]; DFCoord pos_item(itm->x, itm->y, itm->z); // keep track how many items are at places. all items. coordmap::iterator it = counts.find(pos_item); if(it == counts.end()) { std::pair< coordmap::iterator, bool > inserted = counts.insert(std::make_pair(pos_item,1)); it = inserted.first; } else { it->second ++; } // iterator is valid here, we use it later to decrement the pile counter if the item is moved // only dump the stuff marked for dumping and laying on the ground if ( !itm->flags.dump || !itm->flags.on_ground || itm->flags.construction || itm->flags.hidden || itm->flags.in_building || itm->flags.in_chest || itm->flags.in_inventory || itm->flags.artifact1 ) continue; if(!destroy) // move to cursor { // Change flags to indicate the dump was completed, as if by super-dwarfs itm->flags.dump = false; itm->flags.forbid = true; // Don't move items if they're already at the cursor if (pos_cursor == pos_item) continue; // Do we need to fix block-local item ID vector? if(pos_item/16 != pos_cursor/16) { // yes... cerr << "Moving from block to block!" << endl; df_block * bl_src = Maps->getBlock(itm->x /16, itm->y/16, itm->z); df_block * bl_tgt = Maps->getBlock(cx /16, cy/16, cz); if(bl_src) { std::remove(bl_src->items.begin(), bl_src->items.end(),itm->id); } else { cerr << "No source block" << endl; } if(bl_tgt) { bl_tgt->items.push_back(itm->id); } else { cerr << "No target block" << endl; } } // Move the item itm->x = pos_cursor.x; itm->y = pos_cursor.y; itm->z = pos_cursor.z; } else // destroy { if (here && pos_item != pos_cursor) continue; itm->flags.garbage_colect = true; // Cosmetic changes: make them disappear from view instantly itm->flags.forbid = true; itm->flags.hidden = true; } // keeping track of item pile sizes ;) it->second --; dumped_total++; } if(!destroy) // TODO: do we have to do any of this when destroying items? { // for each item pile, see if it reached zero. if so, unset item flag on the tile it's on coordmap::iterator it = counts.begin(); coordmap::iterator end = counts.end(); while(it != end) { if(it->second == 0) { t_occupancy occ = MC.occupancyAt(it->first); occ.bits.item = false; MC.setOccupancyAt(it->first, occ); } it++; } // Set "item here" flag on target tile, if we moved any items to the target tile. if (dumped_total > 0) { // assume there is a possibility the cursor points at some weird location with missing block data Block * b = MC.BlockAt(pos_cursor / 16); if(b) { t_occupancy occ = MC.occupancyAt(pos_cursor); occ.bits.item = 1; MC.setOccupancyAt(pos_cursor,occ); } } // write map changes back to DF. MC.WriteAll(); // Is this necessary? Is "forbid" a dirtyable attribute like "dig" is? Maps->WriteDirtyBit(cx/16, cy/16, cz, true); } c->con.print("Done. %d items %s.\n", dumped_total, destroy ? "marked for desctruction" : "quickdumped"); return CR_OK; }