/* * Seven Kingdoms: Ancient Adversaries * * Copyright 1997,1998 Enlight Software Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // Filename : OPLASMA.CPP // Description: plasma map generator // Ownership : Gilbert #include #include #include #include // ---------- define constant ----------// enum { SHIFT_VALUE=18, // shift based on no. of colors MAX_COLOR=256 // max. no. of colors }; // --------- Begin of function Plasma::Plasma -----------// Plasma::Plasma() { max_x = max_y = 0; matrix = NULL; } // --------- End of function Plasma::Plasma -----------// // --------- Begin of function Plasma::~Plasma -----------// Plasma::~Plasma() { deinit(); } // --------- End of function Plasma::~Plasma -----------// // --------- Begin of function Plasma::init -----------// void Plasma::init(short x, short y) { deinit(); max_x = x; max_y = y; matrix = (short *) mem_add( (x+1)*(y+1)*sizeof(short) ); memset( matrix, 0, (x+1)*(y+1)*sizeof(short) ); } // --------- End of function Plasma::init -----------// // --------- Begin of function Plasma::deinit -----------// void Plasma::deinit() { if( matrix) mem_del(matrix); matrix = NULL; max_x = max_y = 0; } // --------- End of function Plasma::deinit -----------// // --------- Begin of function Plasma::get_pix -----------// short Plasma::get_pix(short x, short y) { return matrix[y * (max_x+1) + x]; } // --------- End of function Plasma::get_pix -----------// // --------- Begin of function Plasma::plot -----------// void Plasma::plot(short x, short y, short value) { matrix[y * (max_x+1) + x] = value; } // --------- End of function Plasma::plot -----------// // --------- Begin of function Plasma::generate -----------// void Plasma::generate(int genMethod, int grainFactor, int randomSeed) { int i,k, n; U16 rnd[4]; iparmx = grainFactor * 8; srand(randomSeed); for(n = 0; n < 4; n++) rnd[n] = 1+(((m.rand()/MAX_COLOR)*(MAX_COLOR-1))>>(SHIFT_VALUE-11)); plot( 0, 0, rnd[0]); plot(max_x, 0, rnd[1]); plot(max_x, max_y, rnd[2]); plot( 0, max_y, rnd[3]); recur_level = 0; if ( genMethod == 0) // use original method { sub_divide(0,0,max_x,max_y); } else // use new method { recur1 = i = k = 1; while(new_sub_divide(0,0,max_x,max_y,i)==0) { k = k * 2; if (k > max_x && k > max_y) break; i++; } } } // --------- End of function Plasma::generate -----------// // --------- Begin of function Plasma::sub_divide -----------// void Plasma::sub_divide(int x1,int y1,int x2,int y2) { int x,y; S32 v,i; if(x2-x1<2 && y2-y1<2) return; recur_level++; recur1 = 320L >> recur_level; x = (x1+x2)>>1; y = (y1+y2)>>1; if((v=get_pix(x,y1)) == 0) v=adjust(x1,y1,x ,y1,x2,y1); i=v; if((v=get_pix(x2,y)) == 0) v=adjust(x2,y1,x2,y ,x2,y2); i+=v; if((v=get_pix(x,y2)) == 0) v=adjust(x1,y2,x ,y2,x2,y2); i+=v; if((v=get_pix(x1,y)) == 0) v=adjust(x1,y1,x1,y ,x1,y2); i+=v; if(get_pix(x,y) == 0) plot(x,y,(U16)((i+2)>>2)); sub_divide(x1,y1,x ,y); sub_divide(x ,y1,x2,y); sub_divide(x ,y ,x2,y2); sub_divide(x1,y ,x ,y2); recur_level--; } // --------- End of function Plasma::sub_divide -----------// // --------- Begin of function Plasma::new_sub_divide -----------// int Plasma::new_sub_divide(int x1,int y1,int x2,int y2, int recur) { int x,y; int nx1; int nx; int ny1, ny; S32 i, v; struct sub { BYTE t; // top of stack int v[16]; // subdivided value BYTE r[16]; // recursion level }; static struct sub subx, suby; /* recur1=1; for (i=1;i<=recur;i++) recur1 = recur1 * 2; recur1=320/recur1; */ recur1 = 320L >> recur; suby.t = 2; ny = suby.v[0] = y2; ny1 = suby.v[2] = y1; suby.r[0] = suby.r[2] = 0; suby.r[1] = 1; y = suby.v[1] = (ny1 + ny) >> 1; while (suby.t >= 1) { while (suby.r[suby.t-1] < recur) { /* 1. Create new entry at top of the stack */ /* 2. Copy old top value to new top value. */ /* This is largest y value. */ /* 3. Smallest y is now old mid point */ /* 4. Set new mid point recursion level */ /* 5. New mid point value is average */ /* of largest and smallest */ suby.t++; ny1 = suby.v[suby.t] = suby.v[suby.t-1]; ny = suby.v[suby.t-2]; suby.r[suby.t] = suby.r[suby.t-1]; y = suby.v[suby.t-1] = (ny1 + ny) >> 1; suby.r[suby.t-1] = (int)max(suby.r[suby.t], suby.r[suby.t-2])+1; } subx.t = 2; nx = subx.v[0] = x2; nx1 = subx.v[2] = x1; subx.r[0] = subx.r[2] = 0; subx.r[1] = 1; x = subx.v[1] = (nx1 + nx) >> 1; while (subx.t >= 1) { while (subx.r[subx.t-1] < recur) { subx.t++; /* move the top ofthe stack up 1 */ nx1 = subx.v[subx.t] = subx.v[subx.t-1]; nx = subx.v[subx.t-2]; subx.r[subx.t] = subx.r[subx.t-1]; x = subx.v[subx.t-1] = (nx1 + nx) >> 1; subx.r[subx.t-1] = (int)max(subx.r[subx.t], subx.r[subx.t-2])+1; } if ((i = get_pix(nx, y)) == 0) i = adjust(nx,ny1,nx,y ,nx,ny); v = i; if ((i = get_pix(x, ny)) == 0) i = adjust(nx1,ny,x ,ny,nx,ny); v += i; if(get_pix(x,y) == 0) { if ((i = get_pix(x, ny1)) == 0) i = adjust(nx1,ny1,x ,ny1,nx,ny1); v += i; if ((i = get_pix(nx1, y)) == 0) i = adjust(nx1,ny1,nx1,y ,nx1,ny); v += i; plot(x,y,(U16)((v + 2) >> 2)); } if (subx.r[subx.t-1] == recur) subx.t = subx.t - 2; } if (suby.r[suby.t-1] == recur) suby.t = suby.t - 2; } return(0); } // --------- End of function Plasma::new_sub_divide -----------// // --------- Begin of function Plasma::adjust -----------// U16 Plasma::adjust(int xa,int ya,int x,int y,int xb,int yb) { S32 pseudoRandom; pseudoRandom = ((S32)iparmx)*((m.rand()-16383)); // pseudoRandom = pseudoRandom*(abs(xa-xb)+abs(ya-yb)); pseudoRandom = pseudoRandom * recur1; pseudoRandom = pseudoRandom >> SHIFT_VALUE; pseudoRandom = (((S32)get_pix(xa,ya)+(S32)get_pix(xb,yb)+1)>>1)+pseudoRandom; if (pseudoRandom >= MAX_COLOR) pseudoRandom = MAX_COLOR-1; if(pseudoRandom < 1) pseudoRandom = 1; plot(x,y,(U16)pseudoRandom); return((U16)pseudoRandom); } // --------- End of function Plasma::adjust -----------// // --------- Begin of function Plasma::add_base_level -----------// void Plasma::add_base_level(short baseLevel) { //---------- adjust map baseLevel -----------// int i; int totalLoc=(max_x +1) * (max_y +1); short *locPtr; err_when( max_x == 0 || max_y == 0 || matrix == NULL); for( i=0, locPtr=matrix ; i= MAX_COLOR) *locPtr = MAX_COLOR-1; } } // --------- End of function Plasma::add_base_level -----------// // --------- Begin of function Plasma::calc_tera_base_level -----------// int Plasma::calc_tera_base_level(short minHeight) { //------- count the percentage of sea and land -------// int totalLoc=(max_x+1) * (max_y+1); int i, locHeight, landCount=0, baseLevel=0; for( i=0 ; i= minHeight ) landCount++; } // ensure that percentage of land won't be less than 1/3 if( landCount < 2*totalLoc/3 ) // if land is less than 2/3 of the map baseLevel = 50L * (totalLoc-landCount) / totalLoc; //-------- ensure availability of sea in the map -----------// int xLoc, yLoc, seaCount=0, lowestGrassHeight=0xFFFF; err_when(max_x == 0 || max_y == 0); //---------- scan top & bottom side -----------// for( xLoc=0 ; xLoc<=max_x ; xLoc++ ) { //----------- top side ------------// locHeight = get_pix(xLoc,0) + baseLevel; if( locHeight < minHeight ) // it's a sea seaCount++; else { if( locHeight < lowestGrassHeight ) lowestGrassHeight = locHeight; } //--------- bottom side -----------// locHeight = get_pix(xLoc,max_y) + baseLevel; if( locHeight < minHeight ) // it's a sea seaCount++; else { if( locHeight < lowestGrassHeight ) lowestGrassHeight = locHeight; } } //---------- scan left & right side -----------// for( yLoc=0 ; yLoc<=max_y ; yLoc++ ) { //----------- top side ------------// locHeight = get_pix(0,yLoc) + baseLevel; if( locHeight < minHeight) // it's a sea seaCount++; else { if( locHeight < lowestGrassHeight ) lowestGrassHeight = locHeight; } //--------- bottom side -----------// locHeight = get_pix(max_x,yLoc) + baseLevel; if( locHeight < minHeight ) // it's a sea seaCount++; else { if( locHeight < lowestGrassHeight ) lowestGrassHeight = locHeight; } } //------- If there is not enough sea --------// static short min_sea_count_array[] = { 1600, 700, 200 }; int minSeaCount = min_sea_count_array[config.land_mass-OPTION_LOW]; if( seaCount < minSeaCount ) baseLevel -= lowestGrassHeight - minHeight + (minSeaCount-seaCount) / 15; return baseLevel; } // --------- End of function Plasma::calc_tera_base_level -----------// // --------- Begin of function Plasma::stat -----------// // // classify heights into groups and count the frequency of // each group // // groups (input) no. of groups // minHeights (input) lower limit of each group, must be strictly increasing // freq (output) frequency of each group // return value total frequency // int Plasma::stat(int groups, short *minHeights, int *freq) { int total = (max_x+1)*(max_y+1); int g; // -------- initialize freq count to zero ------// for( g = groups-1; g >= 0; --g) freq[g] = 0; // -------- classify and increase counter -------// for( int i = 0; i < total; ++i) { short v = matrix[i]; for( g = groups-1; g >= 0; --g) { if( v >= minHeights[g]) { freq[g]++; break; } } } return total; } // --------- End of function Plasma::stat -----------// // --------- Begin of function Plasma::generate2 -----------// // // not initialize the corners, so some points should be // initialized before calling this function // void Plasma::generate2(int genMethod, int grainFactor, int randomSeed) { int i,k; iparmx = grainFactor * 8; srand(randomSeed); recur_level = 0; if ( genMethod == 0) // use original method { sub_divide(0,0,max_x,max_y); } else // use new method { recur1 = i = k = 1; while(new_sub_divide(0,0,max_x,max_y,i)==0) { k = k * 2; if (k > max_x && k > max_y) break; i++; } } } // --------- End of function Plasma::generate2 -----------// // --------- begin of function Plasma::shuffle_level --------// // change height of a point, if this point and 8 point around it is within // minHeight and maxHeight void Plasma::shuffle_level(short minHeight, short maxHeight, short amplitude) { // don't change boundary points int x,y; err_when( maxHeight - minHeight < 5 ); for( y = 1; y < max_y; ++y) { for( x = 1; x < max_x; ++x) { short h; if( (h = get_pix(x,y)) >= minHeight && h <= maxHeight && (h = get_pix(x-1,y-1)) >= minHeight && h <= maxHeight && (h = get_pix(x,y-1)) >= minHeight && h <= maxHeight && (h = get_pix(x+1,y-1)) >= minHeight && h <= maxHeight && (h = get_pix(x-1,y)) >= minHeight && h <= maxHeight && (h = get_pix(x+1,y)) >= minHeight && h <= maxHeight && (h = get_pix(x-1,y+1)) >= minHeight && h <= maxHeight && (h = get_pix(x,y+1)) >= minHeight && h <= maxHeight && (h = get_pix(x+1,y+1)) >= minHeight && h <= maxHeight ) { h = get_pix(x,y); // method 1 - random amplitude //h += m.random(amplitude*2+1) - amplitude; //if( h > maxHeight ) // h = maxHeight; //if( h < minHeight ) // h = minHeight; // method 2 - folding (amplitude : +1, +3, +5... or -1, -3, -5...) if( amplitude >= 0) h = minHeight + (h - minHeight) * amplitude; else h = maxHeight + (maxHeight - h) * amplitude; int loopCount = 20; while( h < minHeight || h > maxHeight ) { err_when( --loopCount <= 0 ); if( h > maxHeight ) { h = maxHeight - (h - maxHeight); } else if( h < minHeight ) { h = minHeight + (minHeight - h); } } plot(x, y, h); } } } } // --------- end of function Plasma::shuffle_level --------//