/* * 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 : OGENMAP.CPP //Description : World Map generation function part 1 //Ownership : Gilbert #include #include #include #include #include #include #include #include #include #include //-------- Begin of function World::generate_map ----------// // void World::generate_map() { const int dispProgress = 1; const int maxGenMapSteps = 14; vga_front.unlock_buf(); int curGenMapSteps = 0; if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } //--- loc_matrix, first store terrain height, then world map icon id ---// loc_matrix = (Location*) mem_resize( loc_matrix, MAX_WORLD_X_LOC * MAX_WORLD_Y_LOC * sizeof(Location) ); max_x_loc = MAX_WORLD_X_LOC; max_y_loc = MAX_WORLD_Y_LOC; /* #ifdef DEBUG // testing the completeness of Ocean-DarkGrass joint // static TerrainTypeCode ta[6] = { TERRAIN_OCEAN,TERRAIN_OCEAN,TERRAIN_OCEAN, TERRAIN_DARK_GRASS,TERRAIN_DARK_GRASS,TERRAIN_DARK_GRASS }; static SubTerrainMask sta[6] = { BOTTOM_MASK, MIDDLE_MASK, TOP_MASK, BOTTOM_MASK, MIDDLE_MASK, TOP_MASK }; int failure = 0; int nw, ne, sw, se; for( nw = 0; nw < 6; ++nw) for( ne = 0; ne < 6; ++ne) for( sw = 0; sw < 6; ++sw) for( se = 0; se < 6; ++se) { if(!terrain_res.scan( ta[nw], sta[nw], ta[ne], sta[ne], ta[sw], sta[sw], ta[se], sta[se], 1,0,0)) { TerrainTypeCode nwType=ta[nw], neType=ta[ne], swType=ta[sw], seType=ta[se]; SubTerrainMask nwMask=sta[nw], neMask=sta[ne], swMask=sta[sw], seMask=sta[se]; failure++; }; } #endif */ //----------- start generating -----------// // ---------- generate plasma map ----------// Plasma heightMap; memset( loc_matrix , 0, sizeof(Location) * MAX_WORLD_X_LOC * MAX_WORLD_Y_LOC ); heightMap.init(max_x_loc, max_y_loc); heightMap.generate( m.random(2), 5, m.rand() ); curGenMapSteps++; // 1 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } // ###### begin Gilbert 27/8 ########// // ---------- add base level --------// // heightMap.add_base_level(heightMap.calc_tera_base_level(TerrainRes::min_height(TERRAIN_DARK_GRASS))); // grouping plasma sample data, find sea or land first int totalLoc = (max_x_loc+1) * (max_y_loc+1); short heightLimit[2]; int heightFreq[2]; int minLandCount, maxLandCount; int initHeightLimit = TerrainRes::min_height(TERRAIN_DARK_GRASS); switch(config.land_mass) { case OPTION_LOW: minLandCount = totalLoc *4/10; maxLandCount = totalLoc *6/10; break; case OPTION_MODERATE: minLandCount = totalLoc *6/10; maxLandCount = totalLoc *8/10; break; case OPTION_HIGH: minLandCount = totalLoc *8 /10; maxLandCount = totalLoc; break; default: err_here(); } int avgLandCount = (minLandCount + maxLandCount) /2; heightLimit[0] = 0; heightLimit[1] = initHeightLimit; heightMap.stat(2, heightLimit, heightFreq); int& landCount = heightFreq[1]; int& seaCount = heightFreq[0]; int loopCount = 0; while( ++loopCount <= 4 && (landCountmaxLandCount) ) { if( landCount < minLandCount ) { // positive add_base_level to gain more land // find a level between 0 to TerrainRes::min_height(TERRAIN_DARK_GRASS) // assume heightlevel below heightLimit[1] is evenly distributed, // approximate a new heightLimit[1] such that landCount is avgLandCount // (heightLimit[1] - newheightLimit[1]) * seaCount / (heightLimit[1] - heightLimit[0]) + landCount = avgLandCount heightLimit[1] = heightLimit[1] - (avgLandCount - landCount) * (heightLimit[1] - heightLimit[0]) / seaCount; } else if( landCount > maxLandCount ) { // negative add_base_level to reduce land // find a level above TerrainRes::min_height(TERRAIN_DARK_GRASS) // assume heightlevel above heightLimit[1] is evenly distributed, // approximate a new heightLimit[1] such that landCount is avgLandCount const int maxHeightLimit = 255; // landCount * (maxHeightLimit - newheightLimit[1])/ (maxHeightLimit - heightLimit[1]) = avgLandCount heightLimit[1] = maxHeightLimit - avgLandCount * (maxHeightLimit - heightLimit[1]) / landCount; } heightMap.stat(2, heightLimit, heightFreq); } if( abs( heightLimit[1] - initHeightLimit ) > 2 ) { heightMap.add_base_level(initHeightLimit - heightLimit[1]); } curGenMapSteps++; // 2 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } // ###### end Gilbert 27/8 ########// // --------- remove odd terrain --------// for(short y = 0; y <= heightMap.max_y; ++y) for(short x = 0; x <= heightMap.max_x; ++x) { remove_odd(heightMap, x, y, 5); } curGenMapSteps++; // 3 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } // ------------ shuffle sub-terrain level ---------// heightMap.shuffle_level(TerrainRes::min_height(TERRAIN_OCEAN), TerrainRes::max_height(TERRAIN_OCEAN), -3 ); heightMap.shuffle_level(TerrainRes::min_height(TERRAIN_DARK_GRASS), TerrainRes::max_height(TERRAIN_DARK_GRASS), 3 ); heightMap.shuffle_level(TerrainRes::min_height(TERRAIN_LIGHT_GRASS), TerrainRes::max_height(TERRAIN_LIGHT_GRASS), 3 ); heightMap.shuffle_level(TerrainRes::min_height(TERRAIN_DARK_DIRT), TerrainRes::max_height(TERRAIN_DARK_DIRT), 3 ); curGenMapSteps++; // 4 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } set_tera_id(heightMap); curGenMapSteps++; // 5 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } substitute_pattern(); curGenMapSteps++; // 6 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } set_loc_flags(); //--------- assign the map --------// assign_map(); curGenMapSteps++; // 7 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } gen_hills(TERRAIN_DARK_DIRT); curGenMapSteps++; // 8 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } set_region_id(); curGenMapSteps++; // 9 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } gen_dirt(40,30,60); curGenMapSteps++; // 10 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } gen_rocks(5,10,30); curGenMapSteps++; // 11 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } set_harbor_bit(); curGenMapSteps++; // 12 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } plant_init(); curGenMapSteps++; // 13 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } init_fire(); curGenMapSteps++; // 14 if( dispProgress ) { vga_front.lock_buf(); game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, 0 ); vga_front.unlock_buf(); } /* // randomly put a fire Location *locPtr; do { locPtr = zoom_matrix->get_loc(m.random(MAX_MAP_WIDTH), m.random(MAX_MAP_HEIGHT)); if( locPtr->flammability > 0) { locPtr->fire_level = 80; break; } } while(1); */ vga_front.lock_buf(); //----- debug code: validate terrain_id -----// #ifdef DEBUG Location* locPtr = loc_matrix; for( int i=0 ; iterrain_id < 1 || locPtr->terrain_id > terrain_res.terrain_count ); } #endif } //---------- End of function World::generate_map ------------// //---------- Begin of function World::set_tera_id -----------// // // Set terrain icon id // void World::set_tera_id(Plasma &plasma) { //------- create a world map based on the terrain map ------// memset(loc_matrix, 0, sizeof(Location)*max_x_loc*max_y_loc); for( int y = 0; y < max_y_loc; ++y) { for( int x = 0; x < max_x_loc; ++x) { int nwType, neType, swType, seType; int nwSubType, neSubType, swSubType, seSubType; nwType = TerrainRes::terrain_height(plasma.get_pix(x,y), &nwSubType); neType = TerrainRes::terrain_height(plasma.get_pix(x+1,y), &neSubType); swType = TerrainRes::terrain_height(plasma.get_pix(x,y+1), &swSubType); seType = TerrainRes::terrain_height(plasma.get_pix(x+1,y+1), &seSubType); if((get_loc(x,y)->terrain_id = terrain_res.scan( nwType, nwSubType, neType, neSubType, swType, swSubType, seType, seSubType ,0,1,0)) == 0) { err.run("Error World::set_tera_id, Cannot find terrain type %d:%d, %d:%d, %d:%d, %d:%d", nwType, nwSubType, neType, neSubType, swType, swSubType, seType, seSubType); } } } } //---------- End of function World::set_tera_id -----------// //---------- Begin of function World::set_loc_flags -----------// // void World::set_loc_flags() { int i; int totalLoc=MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC; Location* locPtr=loc_matrix; //----- set power_off of the map edges -----// for( int xLoc=0 ; xLocset_power_off(); get_loc(xLoc, MAX_WORLD_Y_LOC-1)->set_power_off(); } for( int yLoc=0 ; yLocset_power_off(); get_loc(MAX_WORLD_X_LOC-1, yLoc)->set_power_off(); } //-----------------------------------------// if( config.explore_whole_map ) { for( i=0 ; iexplored_on(); if( terrain_res[locPtr->terrain_id]->is_coast() ) { locPtr->loc_flag |= LOCATE_COAST; if(terrain_res[locPtr->terrain_id]->average_type!=TERRAIN_OCEAN) locPtr->set_power_off(); else set_surr_power_off(i%MAX_WORLD_X_LOC, i/MAX_WORLD_X_LOC); } locPtr->walkable_reset(); } } else { for( i=0 ; iexplored_off(); if( terrain_res[locPtr->terrain_id]->is_coast() ) { locPtr->loc_flag |= LOCATE_COAST; if(terrain_res[locPtr->terrain_id]->average_type!=TERRAIN_OCEAN) locPtr->set_power_off(); else set_surr_power_off(i%MAX_WORLD_X_LOC, i/MAX_WORLD_X_LOC); } locPtr->walkable_reset(); } } } //---------- End of function World::set_loc_flags -----------// //---------- Begin of function World::remove_odd --------// void World::remove_odd(Plasma &plasma, short x, short y, short recur) { if( recur < 0) return; // -------- compare the TerrainTypeCode of four adjacent square ------// int center = TerrainRes::terrain_height(plasma.get_pix(x,y)); int same = 0; int diff = 0; short diffTerrain = -1; short diffHeight; short sameX, sameY; // ------- compare north square -------// if( y > 0) { if( center == TerrainRes::terrain_height(plasma.get_pix(x,y-1)) ) { same++; sameX = x; sameY = y-1; } else { diff++; if( diffTerrain < 0) { // new diffHeight diffHeight = plasma.get_pix(x,y-1); diffTerrain = TerrainRes::terrain_height(diffHeight); } else { // three terrain types are close, don't change anything if( diffTerrain != TerrainRes::terrain_height(plasma.get_pix(x,y-1))) return; } } } // ------- compare south square -------// if( y < plasma.max_y) { if( center == TerrainRes::terrain_height(plasma.get_pix(x,y+1)) ) { same++; sameX = x; sameY = y+1; } else { diff++; if( diffTerrain < 0) { // new diffHeight diffHeight = plasma.get_pix(x,y+1); diffTerrain = TerrainRes::terrain_height(diffHeight); } else { // three terrain types are close, don't change anything if( diffTerrain != TerrainRes::terrain_height(plasma.get_pix(x,y+1))) return; } } } // ------- compare west square -------// if( x > 0) { if( center == TerrainRes::terrain_height(plasma.get_pix(x-1,y)) ) { same++; sameX = x-1; sameY = y; } else { diff++; if( diffTerrain < 0) { // new diffHeight diffHeight = plasma.get_pix(x-1,y); diffTerrain = TerrainRes::terrain_height(diffHeight); } else { // three terrain types are close, don't change anything if( diffTerrain != TerrainRes::terrain_height(plasma.get_pix(x-1,y))) return; } } } // ------- compare east square -------// if( x < plasma.max_x) { if( center == TerrainRes::terrain_height(plasma.get_pix(x+1,y)) ) { same++; sameX = x+1; sameY = y; } else { diff++; if( diffTerrain < 0) { // new diffHeight diffHeight = plasma.get_pix(x+1,y); diffTerrain = TerrainRes::terrain_height(diffHeight); } else { // three terrain types are close, don't change anything if( diffTerrain != TerrainRes::terrain_height(plasma.get_pix(x+1,y))) return; } } } if( same <= 1 && diff >= 2) { // flatten plasma.plot(x,y, diffHeight); // propagate to next square if( same == 1) { remove_odd(plasma, sameX, sameY, recur-1); } } } //---------- End of function World::remove_odd --------// //---------- Begin of function World::substitute_pattern -----// void World::substitute_pattern() { short terrainId; int SubFound; const unsigned int resultArraySize = 20; TerrainSubInfo *candSub[resultArraySize]; for( short y = 0; y < max_y_loc; ++y) { for( short x = 0; x < max_x_loc; ++x) { terrainId = get_loc(x,y)->terrain_id; SubFound = terrain_res.search_pattern( terrain_res[terrainId]->pattern_id, candSub, resultArraySize); for( int i = 0; i < SubFound; ++i) { short tx = x, ty = y; char flag = 1; TerrainSubInfo *terrainSubInfo = candSub[i]; // ----- test if a substitution matches for(terrainSubInfo = candSub[i]; terrainSubInfo != NULL; terrainSubInfo = terrainSubInfo->next_step) { if( tx < 0 || tx >= max_x_loc || ty < 0 || ty >= max_y_loc || terrain_res[get_loc(tx,ty)->terrain_id]->pattern_id != terrainSubInfo->old_pattern_id) { flag = 0; break; } // ----- update tx, ty according to post_move -----// switch(terrainSubInfo->post_move) { case 1: ty--; break; // North case 2: ty--; tx++; break; // NE case 3: tx++; break; // East case 4: tx++; ty++; break; // SE case 5: ty++; break; // South case 6: ty++; tx--; break; // SW case 7: tx--; break; // West case 8: tx--; ty--; break; // NW } } // ------ replace pattern -------// if(flag) { tx = x; ty = y; for(terrainSubInfo = candSub[i]; terrainSubInfo != NULL; terrainSubInfo = terrainSubInfo->next_step) { TerrainInfo *oldTerrain = terrain_res[get_loc(tx,ty)->terrain_id]; if( !(get_loc(tx,ty)->terrain_id = terrain_res.scan(oldTerrain->average_type, oldTerrain->secondary_type + terrainSubInfo->sec_adj, terrainSubInfo->new_pattern_id, 0,1,0) )) { err_here(); // cannot find terrain_id } // ----- update tx, ty according to post_move -----// switch(terrainSubInfo->post_move) { case 1: ty--; break; // North case 2: ty--; tx++; break; // NE case 3: tx++; break; // East case 4: tx++; ty++; break; // SE case 5: ty++; break; // South case 6: ty++; tx--; break; // SW case 7: tx--; break; // West case 8: tx--; ty--; break; // NW } } break; } } } } } //---------- End of function World::substitute_pattern -----// //---------- Begin of function World::set_region_id -----// // must be called before any mountain or buildings on the map static RegionType walkable; // to save stack space static unsigned char regionId; void World::set_region_id() { int i,x,y; int totalLoc=max_x_loc * max_y_loc; Location* locPtr=loc_matrix; // -------- reset region_id to zero for( i=0 ; iregion_id = 0; } regionId = 0; for( y = 0; y < max_y_loc; ++y) { locPtr = get_loc(0,y); for( x = 0; x < max_x_loc; ++x, ++locPtr) { if( !locPtr->region_id && locPtr->region_type() != REGION_INPASSABLE) { walkable = locPtr->region_type(); ++regionId; fill_region(x,y); err_when( regionId == 255); } } } region_array.init(regionId); // ------ update adjacency information and region area ------// regionId = 0; for( y = 0; y < max_y_loc; ++y) { locPtr = get_loc(0,y); for( x = 0; x < max_x_loc; ++x, ++locPtr) { int thisRegionId = locPtr->region_id; // #### begin Gilbert 19/2 ######// if( thisRegionId > 0) { region_array.inc_size( thisRegionId ); } // #### end Gilbert 19/2 ######// if( thisRegionId > regionId) { if(thisRegionId == regionId+1) regionId++; region_array.set_region( thisRegionId, locPtr->region_type()); } int adjRegionId; if( y > 0) { if( x > 0 && (adjRegionId = get_loc(x-1,y-1)->region_id) < thisRegionId ) region_array.set_adjacent( thisRegionId, adjRegionId); if( (adjRegionId = get_loc(x,y-1)->region_id) < thisRegionId ) region_array.set_adjacent( thisRegionId, adjRegionId); if( x < max_x_loc-1 && (adjRegionId = get_loc(x+1,y-1)->region_id) < thisRegionId ) region_array.set_adjacent( thisRegionId, adjRegionId); } if( x > 0 && (adjRegionId = get_loc(x-1,y)->region_id) < thisRegionId ) region_array.set_adjacent( thisRegionId, adjRegionId); if( x < max_x_loc-1 && (adjRegionId = get_loc(x+1,y)->region_id) < thisRegionId ) region_array.set_adjacent( thisRegionId, adjRegionId); if( y < max_y_loc-1) { if( x > 0 && (adjRegionId = get_loc(x-1,y+1)->region_id) < thisRegionId ) region_array.set_adjacent( thisRegionId, adjRegionId); if( (adjRegionId = get_loc(x,y+1)->region_id) < thisRegionId ) region_array.set_adjacent( thisRegionId, adjRegionId); if( x < max_x_loc-1 && (adjRegionId = get_loc(x+1,y+1)->region_id) < thisRegionId ) region_array.set_adjacent( thisRegionId, adjRegionId); } } } //---- sort the region after setting its size ----// region_array.sort_region(); //-------- initialize region_stat_array ----------// region_array.init_region_stat(); } //---------- End of function World::set_region_id -----// //---------- Begin of function World::fill_region -----// void World::fill_region(short x, short y) { err_when( x < 0 || x >= max_x_loc || y < 0 || y >= max_y_loc); short left, right; // Location *locPtr; // extent x to left and right for( left = x; left >= 0 && !get_loc(left,y)->region_id && get_loc(left,y)->region_type() == walkable; --left) { get_loc(left,y)->region_id = regionId; } ++left; for( right=x+1; right < max_x_loc && !get_loc(right,y)->region_id && get_loc(right,y)->region_type() == walkable; ++right) { get_loc(right,y)->region_id = regionId; } --right; // ------- scan line below ---------// y++; if( y < max_y_loc ) { for( x = left>0?left-1:0 ; x <= right+1 && x < max_x_loc; ++x ) { if( !get_loc(x,y)->region_id && get_loc(x,y)->region_type() == walkable) { fill_region(x,y); } } } // ------- scan line above -------- // y -= 2; if( y >= 0) { for( x = left>0?left-1:0 ; x <= right+1 && x < max_x_loc; ++x ) { if( !get_loc(x,y)->region_id && get_loc(x,y)->region_type() == walkable) { fill_region(x,y); } } } } //---------- End of function World::fill_region -----// //---------- Begin of function World::set_harbor_bit -----// void World::set_harbor_bit() { // a pass during genmap to set LOCATE_HARBOR_BIT // notice this bit is only a necessary condition to build harbor int x,y; Location *locPtr; for( y = 0; y < max_y_loc-2; ++y) { locPtr = get_loc(0,y); for( x = 0; x < max_x_loc-2; ++x, ++locPtr) { if( can_build_firm(x, y, FIRM_HARBOR)) { locPtr->set_harbor_bit(); } } } } //---------- End of function World::set_harbor_bit -----//