diff --git a/tools/playground/hellhole.cpp b/tools/playground/hellhole.cpp index 06d1847d3..16905a83a 100644 --- a/tools/playground/hellhole.cpp +++ b/tools/playground/hellhole.cpp @@ -32,11 +32,237 @@ void waitmsec (int delay) } #endif +#define minmax(MinV,V,MaxV) (max((MinV),min((MaxV),(V)))) + +//User interaction enums. +//Pit Type (these only have meaning within hellhole, btw) +#define PITTYPEMACRO \ + X(pitTypeChasm,"Bottomless Chasm" ) \ + X(pitTypeEerie,"Bottomless Eerie Pit" ) \ + X(pitTypeMagma,"Magma Pit (similar to volcanoe, no hell access)" ) +//end PITTYPEMACRO + +#define X(name,desc) name, +enum e_pitType { + pitTypeInvalid=-1, + PITTYPEMACRO + pitTypeCount, +}; +#undef X + + +#define X(name,desc) desc, +const char * pitTypeDesc[pitTypeCount+1] = +{ + PITTYPEMACRO + "" +}; +#undef X + + + + +int getyesno( const char * msg , int default_value ){ + const int bufferlen=4; + static char buf[bufferlen]; + memset(buf,0,bufferlen); + while(-1){ + if(msg) printf("\n%s (default=%s)\n:" , msg , (default_value?"yes":"no") ); + fflush(stdin); + fgets(buf,bufferlen,stdin); + switch(buf[0]){ + case 0: case 0x0d: case 0x0a: + return default_value; + case 'y': case 'Y': case 'T': case 't': case '1': + return -1; + case 'n': case 'N': case 'F': case 'f': case '0': + return 0; + } + } + return 0; +} + +int getint( const char * msg , int min, int max, int default_value ){ + const int bufferlen=16; + static char buf[bufferlen]; + int n=0; + memset(buf,0,bufferlen); + while(-1){ + if(msg) printf("\n%s (default=%d)\n:" , msg , default_value); + fflush(stdin); + fgets(buf,bufferlen,stdin); + if( !buf[0] || 0x0a==buf[0] || 0x0d==buf[0] ) return default_value; + if( sscanf(buf,"%d", &n) ){ + if(n>=min && n<=max ) return n; + } + } +} + + + +//Interactive, get pit type from user +#define X( e , desc ) printf("%2d) %s\n", (e), (desc) ); +e_pitType selectPitType(e_pitType default_value ){ + e_pitType r=pitTypeInvalid; + int c=-1; + while( -1 ){ + printf("Enter the type of hole to dig (if no entry, default everything):\n" ); + PITTYPEMACRO + printf(":"); + return (e_pitType)getint(NULL, 0, pitTypeCount-1, default_value ); + } +} +#undef X + + +void drawcircle(const int radius, unsigned char pattern[16][16], unsigned char v ){ + //Small circles get better randomness if handled manually + if( 1==radius ){ + pattern[7][7]=v; + if( (rand()&1) ) pattern[6][7]=v; + if( (rand()&1) ) pattern[8][7]=v; + if( (rand()&1) ) pattern[7][6]=v; + if( (rand()&1) ) pattern[7][8]=v; + + }else if( 2==radius ){ + pattern[7][7]=v; + pattern[7][5]=v; + pattern[7][6]=v; + pattern[7][8]=v; + pattern[7][9]=v; + pattern[6][7]=v; + pattern[8][7]=v; + pattern[9][7]=v; + pattern[6][6]=v; + pattern[6][8]=v; + pattern[8][6]=v; + pattern[8][8]=v; + pattern[5][7]=v; + + if( (rand()&1) ) pattern[6][5]=v; + if( (rand()&1) ) pattern[5][6]=v; + if( (rand()&1) ) pattern[8][5]=v; + if( (rand()&1) ) pattern[9][6]=v; + if( (rand()&1) ) pattern[6][9]=v; + if( (rand()&1) ) pattern[5][8]=v; + if( (rand()&1) ) pattern[8][9]=v; + if( (rand()&1) ) pattern[9][8]=v; + }else{ + //radius 3 or larger, simple circle calculation. + int x,y; + for(y=0-radius; y<=radius; ++y){ + for(x=0-radius; x<=radius; ++x){ + if(x*x+y*y <= radius*radius + (rand()&31-8) ){ + pattern[ minmax(0,7+x,15) ][ minmax(0,7+y,15) ]=v; + } + } + } + //Prevent boxy patterns with a quick modification on edges + if(rand()&1) pattern[ 7 ][ minmax(0,7+radius+1,15) ] = v; + if(rand()&1) pattern[ 7 ][ minmax(0,7-radius-1,15) ] = v; + if(rand()&1) pattern[ minmax(0,7+radius+1,15) ][ 7 ] = v; + if(rand()&1) pattern[ minmax(0,7-radius-1,15) ][ 7 ] = v; + } +} + +void settileat(unsigned char pattern[16][16], const unsigned char needle, const unsigned char v, const int index ) +{ + int ok=0; + int safety=256*256; + int y,x,i=0; + //Scan for sequential index + while( !ok && --safety ){ + for(y=0 ; !ok && y<16 ; ++y ){ + for(x=0 ; !ok && x<16 ; ++x ){ + if( needle==pattern[x][y] ){ + ++i; + if( index==i ){ + //Got it! + pattern[x][y]=v; + ok=-1; + } + } + } + } + } +} int main (void) { - srand ( time(NULL) ); + srand ( (unsigned int)time(NULL) ); + + //Message of intent + cout << + "DF Hell Hole" << endl << + "This program will instantly dig a bottomless hole 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(pitTypeInvalid); + + + //Hole Diameter + int holeradius=6; + if( pitTypeInvalid != pittype && pitTypeMagma != pittype ){ + holeradius = getint( "Enter hole radius, 0 to 8", 0, 8, holeradius ); + } + + //Wall thickness + int wallthickness=1; + if( pitTypeInvalid != pittype && pitTypeMagma != pittype ){ + wallthickness = getint( "Enter wall thickness, 0 to 8", 0, 8, wallthickness ); + } + + //Obsidian Pillars + int pillarwall=1; + if( pitTypeInvalid != pittype ){ + pillarwall = getint( "Number of Obsidian Pillars in wall, 0 to 255", 0, 255, pillarwall ); + } + int pillarchasm=1; + if( pitTypeInvalid != pittype ){ + pillarchasm = getint( "Number of Obsidian Pillars in hole, 0 to 255", 0, 255, pillarchasm ); + } + + //Open Hell? + int exposehell = 0; + if( pitTypeInvalid != pittype && pitTypeMagma != pittype && wallthickness ){ + exposehell=getyesno("Expose the pit to hell (no walls in hell)?",0); + } + + //Fill? + int fillmagma=0; + if( pitTypeInvalid != pittype ){ + fillmagma=getyesno("Fill with magma?",0); + } + int fillwater=0; + if( pitTypeInvalid != pittype && !fillmagma){ + fillwater=getyesno("Fill with water?",0); + } + + + //If skipped all settings, fix the pit type + if( pitTypeInvalid == pittype ) pittype = pitTypeChasm; + + + /////////////////////////////////////////////////////////////////////////////////////////////// + //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("Hole Radius...: %d\n", holeradius); + printf("Wall Thickness: %d\n", wallthickness); + printf("Pillars, Wall.: %d\n", pillarwall); + printf("Pillars, Hole.: %d\n", pillarchasm); + printf("Expose Hell...: %c\n", (exposehell?'Y':'N') ); + printf("Magma Fill....: %c\n", (fillmagma?'Y':'N') ); + printf("Water Fill....: %c\n", (fillwater?'Y':'N') ); + + int64_t n; uint32_t x_max,y_max,z_max; @@ -47,81 +273,100 @@ int main (void) //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 uint32_t floor=35, cap=340; + if( pitTypeEerie == pittype ) floor=42; + //Should tiles be revealed? - int reveal=0; + int reveal=1; //Pattern to dig - unsigned char pattern[16][16] = {0,}; + 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 radius=6; - int x=0, y=0; + int x=0, y=0, n=0; - for(y=-radius; y<=radius; ++y){ - for(x=-radius; x<=radius; ++x){ - if(x*x+y*y <= radius*radius + (rand()&31) ){ - pattern[7+x][7+y]=1; - } - } + //Two concentric irregular circles + //Outer circle, solid. + if( wallthickness ){ + drawcircle(holeradius+wallthickness, pattern, 2); } - //Post-process to figure out where to put walls. - 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; + //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 + if( x>0 && y>0 && 1==pattern[x-1][y-1] ){ pattern[x][y]=2; continue; } + if( x>0 && 1==pattern[x-1][y ] ){ pattern[x][y]=2; continue; } + if( y>0 && 1==pattern[x ][y-1] ){ pattern[x][y]=2; continue; } + if( x<15 && 1==pattern[x+1][y ] ){ pattern[x][y]=2; continue; } + if( x<15&& y>0 && 1==pattern[x+1][y-1] ){ pattern[x][y]=2; continue; } + if( x<15&& y<15&& 1==pattern[x+1][y+1] ){ pattern[x][y]=2; continue; } + if( y<15&& 1==pattern[x ][y+1] ){ pattern[x][y]=2; continue; } + if( x>0 && y<15&& 1==pattern[x-1][y+1] ){ pattern[x][y]=2; continue; } } - }else if( 0==pattern[x][y] ){ - //check neighbors - if( x>0 && y>0 && 1==pattern[x-1][y-1] ){ pattern[x][y]=2; continue; } - if( x>0 && 1==pattern[x-1][y ] ){ pattern[x][y]=2; continue; } - if( y>0 && 1==pattern[x ][y-1] ){ pattern[x][y]=2; continue; } - if( x<15 && 1==pattern[x+1][y ] ){ pattern[x][y]=2; continue; } - if( x<15&& y>0 && 1==pattern[x+1][y-1] ){ pattern[x][y]=2; continue; } - if( x<15&& y<15&& 1==pattern[x+1][y+1] ){ pattern[x][y]=2; continue; } - if( y<15&& 1==pattern[x ][y+1] ){ pattern[x][y]=2; continue; } - if( x>0 && y<15&& 1==pattern[x-1][y+1] ){ pattern[x][y]=2; continue; } } } } + //Final pass, 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(x=0, y=0; 1!=pattern[x][y]; x=rand()&15, y=rand()&15 ){} - pattern[x][y]=3; + for(n=pillarchasm; n ; --n){ + settileat( pattern , 1 , 3 , rand()&255 ); + } + for(n=pillarwall; n ; --n){ + settileat( pattern , 2 , 3 , rand()&255 ); + } -#if 0 - cout << endl; - //Print the pattern (debugging) + + //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. + + printf("\nPattern:\n"); + const char patternkey[] = ".cW!4567890123"; + + //Print the pattern for(y=0;y<16;++y){ for(x=0;x<16;++x){ - printf("%d",pattern[x][y]); + cout << patternkey[ pattern[x][y] ]; } cout << endl; } cout << endl; - cin.ignore(); - return 0; -#endif + regen = !getyesno("Acceptable Pattern?",1); } - + /////////////////////////////////////////////////////////////////////////////////////////////// + + //Connect to DF! DFHack::ContextManager DFMgr("Memory.xml"); DFHack::Context *DF = DFMgr.getSingleContext(); - //Message of intent - cout << - "DF Hell Hole" << endl << - "This program will instantly dig a hole through hell, wherever your cursor is." << endl << - "This can not be undone! End program now if you don't want hellish fun." << endl - ; - cin.ignore(); //Init try @@ -136,7 +381,7 @@ int main (void) #endif return 1; } - + // init the map DFHack::Maps *Mapz = DF->getMaps(); if(!Mapz->Start()) @@ -182,7 +427,7 @@ int main (void) //Verify that every z-level at this location exists. - for(uint32_t z = 0; z<= bz ;z++){ + for(int32_t z = 0; z<= bz ;z++){ if( ! Mapz->isValidBlock(bx,by,z) ){ cout << "This block does't exist yet!" << endl << "Designate the lowest level for digging to make DF allocate the block, then try again." << endl; #ifndef LINUX_BUILD @@ -252,21 +497,74 @@ int main (void) Mapz->WriteTileTypes(bx,by,bz, &block.tiletypes ); Mapz->WriteDirtyBit(bx,by,bz,1); + //Various behaviour flags. + int moltencount=0; + int solidcount=0; + int emptycount=0; + int hellcount=0; + int tpat; + //All levels in between. uint32_t t; - for(uint32_t z = bz-1; z>0 ; --z){ + for(int32_t z = bz-1; z>0 ; --z){ + moltencount=0; + solidcount=0; + emptycount=0; + hellcount=0; cout << z << endl; assert( Mapz->isValidBlock(bx,by,z) ); if(!Mapz->ReadBlock40d( bx, by, z , &block )){ cout << "Bad block! " << bx << "," << by << "," << z; } - for(uint32_t x=0;x<16;++x){ - for(uint32_t y=0;y<16;++y){ + for(int32_t x=0;x<16;++x){ + for(int32_t y=0;y<16;++y){ t=0; tp = getTileTypeP(block.tiletypes[x][y]); d = &block.designation[x][y]; + tpat=pattern[x][y]; + + //Manipulate behaviour based on settings and location + switch( tp->m ){ + case MAGMA: + ++moltencount; + //Making a fake volcanoe/magma pipe? + if( pitTypeMagma == pittype ){ + //Leave tile unchanged. + tpat=0; + } + break; + case OBSIDIAN: + case FEATSTONE: + case HFS: + //ignore, even though it is technically solid + break; + case VEIN: + default: + if( EMPTY != tp->c ){ + ++emptycount; + }else{ + ++solidcount; + } + } + + //Check hell status + if( + (block.local_feature > -1 && feature_Underworld==local_features[pc][block.local_feature]->type ) + || + (block.global_feature > -1 && feature_Underworld==global_features[block.global_feature].type ) + ){ + ++hellcount; + + //Are we leaving hell open? + if( exposehell ){ + //Leave tile unchanged. + tpat=0; + } + } + + //Border or space? - switch(pattern[x][y]){ + switch(tpat){ case 0: continue; break; @@ -280,6 +578,17 @@ int main (void) //Erase special markers d->bits.feature_global = d->bits.feature_local = 0; + //Water? Magma? + if(fillmagma || fillwater){ + d->bits.flow_size=7; + d->bits.liquid_character = liquid_fresh; + if(fillmagma){ + d->bits.liquid_type=liquid_magma; + }else{ + d->bits.liquid_type=liquid_water; + } + } + break; case 2: //Border. @@ -373,6 +682,7 @@ int main (void) d->bits.water_table=0; //Set the tile. block.tiletypes[x][y] = t; + } } @@ -381,40 +691,48 @@ int main (void) Mapz->WriteDesignations(bx,by,z, &block.designation ); Mapz->WriteTileTypes(bx,by,z, &block.tiletypes ); Mapz->WriteDirtyBit(bx,by,z,1); - } + //Making a fake volcanoe/magma pipe? + if( pitTypeMagma == pittype && !solidcount){ + //Nothing «solid», we're making a magma pipe, we're done. + z=0; + } - //The bottom level is special. - Mapz->ReadBlock40d( bx, by, 0 , &block ); - for(uint32_t x=0;x<16;++x){ - for(uint32_t y=0;y<16;++y){ - //Only the portion below the empty space is handled. - if(pattern[x][y]){ - if( 1==pattern[x][y] ) t=floor; - else if( 3==pattern[x][y] ) t=331; - else continue; + } - tp = getTileTypeP(block.tiletypes[x][y]); - d = &block.designation[x][y]; - //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, 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] = floor; + //The bottom level is special. + if( pitTypeMagma != pittype ) { + Mapz->ReadBlock40d( bx, by, 0 , &block ); + for(uint32_t x=0;x<16;++x){ + for(uint32_t y=0;y<16;++y){ + //Only the portion below the empty space is handled. + if(pattern[x][y]){ + if( 1==pattern[x][y] ) t=floor; + else if( 3==pattern[x][y] ) t=331; + else continue; + + tp = getTileTypeP(block.tiletypes[x][y]); + d = &block.designation[x][y]; + + //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, 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] = floor; + } } } + //Write the block. + Mapz->WriteBlockFlags(bx,by,0, block.blockflags ); + Mapz->WriteDesignations(bx,by,0, &block.designation ); + Mapz->WriteTileTypes(bx,by,0, &block.tiletypes ); + Mapz->WriteDirtyBit(bx,by,0,1); } - //Write the block. - Mapz->WriteBlockFlags(bx,by,0, block.blockflags ); - Mapz->WriteDesignations(bx,by,0, &block.designation ); - Mapz->WriteTileTypes(bx,by,0, &block.tiletypes ); - Mapz->WriteDirtyBit(bx,by,0,1); - DF->Detach();