2013-01-06 22:30:12 +03:00
# include "StdInc.h"
# include "CMapGenerator.h"
2013-04-07 13:48:07 +03:00
# include "../mapping/CMap.h"
2013-01-06 22:30:12 +03:00
# include "../VCMI_Lib.h"
# include "../CGeneralTextHandler.h"
2013-04-07 13:48:07 +03:00
# include "../mapping/CMapEditManager.h"
2013-01-06 22:30:12 +03:00
# include "../CTownHandler.h"
# include "../StringConstants.h"
2014-05-22 17:27:13 +03:00
# include "../filesystem/Filesystem.h"
2014-05-24 13:42:06 +03:00
# include "CZonePlacer.h"
2014-07-04 15:46:02 +03:00
# include "../mapObjects/CObjectClassesHandler.h"
2013-08-19 21:20:11 +03:00
2015-01-03 08:20:52 +02:00
static const int3 dirs4 [ ] = { int3 ( 0 , 1 , 0 ) , int3 ( 0 , - 1 , 0 ) , int3 ( - 1 , 0 , 0 ) , int3 ( + 1 , 0 , 0 ) } ;
2014-05-30 22:23:41 +03:00
void CMapGenerator : : foreach_neighbour ( const int3 & pos , std : : function < void ( int3 & pos ) > foo )
2014-05-24 19:39:58 +03:00
{
2015-12-04 01:06:02 +02:00
for ( const int3 & dir : int3 : : getDirs ( ) )
2014-05-30 17:50:06 +03:00
{
2014-05-30 22:23:41 +03:00
int3 n = pos + dir ;
2016-01-13 20:17:12 +02:00
/*important notice: perform any translation before this function is called,
so the actual map position is checked */
2014-05-30 17:50:06 +03:00
if ( map - > isInTheMap ( n ) )
2014-05-31 15:11:20 +03:00
foo ( n ) ;
2014-05-30 17:50:06 +03:00
}
2014-05-24 19:39:58 +03:00
}
2015-01-03 08:20:52 +02:00
void CMapGenerator : : foreachDirectNeighbour ( const int3 & pos , std : : function < void ( int3 & pos ) > foo )
{
for ( const int3 & dir : dirs4 )
{
int3 n = pos + dir ;
if ( map - > isInTheMap ( n ) )
foo ( n ) ;
2015-11-14 15:50:29 +02:00
}
2015-01-03 08:20:52 +02:00
}
2014-05-24 19:39:58 +03:00
2014-07-27 14:59:53 +03:00
CMapGenerator : : CMapGenerator ( ) :
2016-11-27 21:37:41 +02:00
mapGenOptions ( nullptr ) , randomSeed ( 0 ) , editManager ( nullptr ) ,
zonesTotal ( 0 ) , tiles ( nullptr ) , prisonsRemaining ( 0 ) ,
monolithIndex ( 0 )
2014-05-22 17:27:13 +03:00
{
2013-01-06 22:30:12 +03:00
}
2014-05-30 17:50:06 +03:00
void CMapGenerator : : initTiles ( )
2013-01-06 22:30:12 +03:00
{
2014-05-30 17:50:06 +03:00
map - > initTerrain ( ) ;
int width = map - > width ;
int height = map - > height ;
int level = map - > twoLevel ? 2 : 1 ;
tiles = new CTileInfo * * [ width ] ;
for ( int i = 0 ; i < width ; + + i )
{
tiles [ i ] = new CTileInfo * [ height ] ;
for ( int j = 0 ; j < height ; + + j )
{
tiles [ i ] [ j ] = new CTileInfo [ level ] ;
}
}
2016-08-09 10:12:13 +02:00
zoneColouring . resize ( boost : : extents [ map - > twoLevel ? 2 : 1 ] [ map - > width ] [ map - > height ] ) ;
2014-05-30 17:50:06 +03:00
}
2013-01-06 22:30:12 +03:00
2014-05-30 17:50:06 +03:00
CMapGenerator : : ~ CMapGenerator ( )
{
2014-07-01 13:07:53 +03:00
if ( tiles )
2014-05-30 17:50:06 +03:00
{
2014-07-01 13:07:53 +03:00
int width = mapGenOptions - > getWidth ( ) ;
int height = mapGenOptions - > getHeight ( ) ;
for ( int i = 0 ; i < width ; i + + )
2014-05-30 17:50:06 +03:00
{
2014-07-01 13:07:53 +03:00
for ( int j = 0 ; j < height ; j + + )
2014-05-30 17:50:06 +03:00
{
delete [ ] tiles [ i ] [ j ] ;
}
delete [ ] tiles [ i ] ;
}
delete [ ] tiles ;
}
2013-01-06 22:30:12 +03:00
}
2014-07-25 11:44:17 +03:00
void CMapGenerator : : initPrisonsRemaining ( )
{
prisonsRemaining = 0 ;
for ( auto isAllowed : map - > allowedHeroes )
{
if ( isAllowed )
prisonsRemaining + + ;
}
2014-12-19 15:04:59 +02:00
prisonsRemaining = std : : max < int > ( 0 , prisonsRemaining - 16 * mapGenOptions - > getPlayerCount ( ) ) ; //so at least 16 heroes will be available for every player
2014-07-25 11:44:17 +03:00
}
2015-02-28 22:14:45 +02:00
void CMapGenerator : : initQuestArtsRemaining ( )
{
for ( auto art : VLC - > arth - > artifacts )
{
2015-09-23 21:38:16 +02:00
if ( art - > aClass = = CArtifact : : ART_TREASURE & & VLC - > arth - > legalArtifact ( art - > id ) & & art - > constituentOf . empty ( ) ) //don't use parts of combined artifacts
questArtifacts . push_back ( art - > id ) ;
2015-02-28 22:14:45 +02:00
}
}
2014-07-27 14:59:53 +03:00
std : : unique_ptr < CMap > CMapGenerator : : generate ( CMapGenOptions * mapGenOptions , int randomSeed /*= std::time(nullptr)*/ )
2013-01-06 22:30:12 +03:00
{
2014-07-27 14:59:53 +03:00
this - > mapGenOptions = mapGenOptions ;
this - > randomSeed = randomSeed ;
2014-08-31 20:08:39 +03:00
assert ( mapGenOptions ) ;
2014-07-27 14:59:53 +03:00
rand . setSeed ( this - > randomSeed ) ;
2014-05-24 13:42:06 +03:00
mapGenOptions - > finalize ( rand ) ;
2013-01-06 22:30:12 +03:00
2014-05-24 13:42:06 +03:00
map = make_unique < CMap > ( ) ;
editManager = map - > getEditManager ( ) ;
2014-07-25 11:44:17 +03:00
2014-05-23 18:12:31 +03:00
try
{
editManager - > getUndoManager ( ) . setUndoRedoLimit ( 0 ) ;
2014-08-31 20:08:39 +03:00
//FIXME: somehow mapGenOption is nullptr at this point :?
2014-05-23 18:12:31 +03:00
addHeaderInfo ( ) ;
2014-05-30 17:50:06 +03:00
initTiles ( ) ;
2013-01-06 22:30:12 +03:00
2014-07-25 11:44:17 +03:00
initPrisonsRemaining ( ) ;
2015-02-28 22:14:45 +02:00
initQuestArtsRemaining ( ) ;
2014-05-23 18:12:31 +03:00
genZones ( ) ;
2014-05-23 20:14:20 +03:00
map - > calculateGuardingGreaturePositions ( ) ; //clear map so that all tiles are unguarded
2014-05-23 18:12:31 +03:00
fillZones ( ) ;
2014-07-27 14:59:53 +03:00
//updated guarded tiles will be calculated in CGameState::initMapObjects()
2014-05-23 18:12:31 +03:00
}
catch ( rmgException & e )
{
2014-05-23 20:14:20 +03:00
logGlobal - > errorStream ( ) < < " Random map generation received exception: " < < e . what ( ) ;
2014-05-23 18:12:31 +03:00
}
2013-01-06 22:30:12 +03:00
return std : : move ( map ) ;
}
std : : string CMapGenerator : : getMapDescription ( ) const
{
2014-09-18 15:29:57 +03:00
assert ( mapGenOptions ) ;
assert ( map ) ;
2013-01-06 22:30:12 +03:00
const std : : string waterContentStr [ 3 ] = { " none " , " normal " , " islands " } ;
const std : : string monsterStrengthStr [ 3 ] = { " weak " , " normal " , " strong " } ;
2014-09-18 15:29:57 +03:00
int monsterStrengthIndex = mapGenOptions - > getMonsterStrength ( ) - EMonsterStrength : : GLOBAL_WEAK ; //does not start from 0
2013-05-21 22:08:06 +03:00
std : : stringstream ss ;
ss < < boost : : str ( boost : : format ( std : : string ( " Map created by the Random Map Generator. \n Template was %s, Random seed was %d, size %dx%d " ) +
2015-05-27 11:30:46 +02:00
" , levels %s, players %d, computers %d, water %s, monster %s, VCMI map " ) % mapGenOptions - > getMapTemplate ( ) - > getName ( ) %
2014-05-23 12:56:51 +03:00
randomSeed % map - > width % map - > height % ( map - > twoLevel ? " 2 " : " 1 " ) % static_cast < int > ( mapGenOptions - > getPlayerCount ( ) ) %
static_cast < int > ( mapGenOptions - > getCompOnlyPlayerCount ( ) ) % waterContentStr [ mapGenOptions - > getWaterContent ( ) ] %
2014-09-18 15:29:57 +03:00
monsterStrengthStr [ monsterStrengthIndex ] ) ;
2013-01-06 22:30:12 +03:00
2014-05-23 12:56:51 +03:00
for ( const auto & pair : mapGenOptions - > getPlayersSettings ( ) )
2013-01-06 22:30:12 +03:00
{
2013-04-14 22:24:31 +03:00
const auto & pSettings = pair . second ;
if ( pSettings . getPlayerType ( ) = = EPlayerType : : HUMAN )
2013-01-06 22:30:12 +03:00
{
2013-03-03 20:06:03 +03:00
ss < < " , " < < GameConstants : : PLAYER_COLOR_NAMES [ pSettings . getColor ( ) . getNum ( ) ] < < " is human " ;
2013-01-06 22:30:12 +03:00
}
2013-04-14 22:24:31 +03:00
if ( pSettings . getStartingTown ( ) ! = CMapGenOptions : : CPlayerSettings : : RANDOM_TOWN )
2013-01-06 22:30:12 +03:00
{
2013-03-03 20:06:03 +03:00
ss < < " , " < < GameConstants : : PLAYER_COLOR_NAMES [ pSettings . getColor ( ) . getNum ( ) ]
2014-05-22 22:40:34 +03:00
< < " town choice is " < < VLC - > townh - > factions [ pSettings . getStartingTown ( ) ] - > name ;
2013-01-06 22:30:12 +03:00
}
}
return ss . str ( ) ;
}
void CMapGenerator : : addPlayerInfo ( )
{
// Calculate which team numbers exist
2015-06-01 21:57:43 +02:00
enum ETeams { CPHUMAN = 0 , CPUONLY = 1 , AFTER_LAST = 2 } ;
std : : array < std : : list < int > , 2 > teamNumbers ;
2013-01-06 22:30:12 +03:00
int teamOffset = 0 ;
2015-06-01 21:57:43 +02:00
int playerCount = 0 ;
int teamCount = 0 ;
2013-01-06 22:30:12 +03:00
2015-06-01 21:57:43 +02:00
for ( int i = CPHUMAN ; i < AFTER_LAST ; + + i )
{
if ( i = = CPHUMAN )
{
playerCount = mapGenOptions - > getPlayerCount ( ) ;
teamCount = mapGenOptions - > getTeamCount ( ) ;
}
else
{
playerCount = mapGenOptions - > getCompOnlyPlayerCount ( ) ;
teamCount = mapGenOptions - > getCompOnlyTeamCount ( ) ;
}
2015-11-14 15:50:29 +02:00
2013-05-21 22:08:06 +03:00
if ( playerCount = = 0 )
2013-01-06 22:30:12 +03:00
{
continue ;
}
2015-06-01 21:57:43 +02:00
int playersPerTeam = playerCount / ( teamCount = = 0 ? playerCount : teamCount ) ;
2013-05-21 22:08:06 +03:00
int teamCountNorm = teamCount ;
if ( teamCountNorm = = 0 )
2013-01-06 22:30:12 +03:00
{
2013-05-21 22:08:06 +03:00
teamCountNorm = playerCount ;
2013-01-06 22:30:12 +03:00
}
2013-05-21 22:08:06 +03:00
for ( int j = 0 ; j < teamCountNorm ; + + j )
2013-01-06 22:30:12 +03:00
{
for ( int k = 0 ; k < playersPerTeam ; + + k )
{
teamNumbers [ i ] . push_back ( j + teamOffset ) ;
}
}
2013-05-21 22:08:06 +03:00
for ( int j = 0 ; j < playerCount - teamCountNorm * playersPerTeam ; + + j )
2013-01-06 22:30:12 +03:00
{
teamNumbers [ i ] . push_back ( j + teamOffset ) ;
}
2013-05-21 22:08:06 +03:00
teamOffset + = teamCountNorm ;
2013-01-06 22:30:12 +03:00
}
// Team numbers are assigned randomly to every player
2015-06-01 21:57:43 +02:00
//TODO: allow customize teams in rmg template
2014-05-23 12:56:51 +03:00
for ( const auto & pair : mapGenOptions - > getPlayersSettings ( ) )
2013-01-06 22:30:12 +03:00
{
2013-04-14 22:24:31 +03:00
const auto & pSettings = pair . second ;
2013-01-06 22:30:12 +03:00
PlayerInfo player ;
player . canComputerPlay = true ;
2015-06-01 21:57:43 +02:00
int j = ( pSettings . getPlayerType ( ) = = EPlayerType : : COMP_ONLY ) ? CPUONLY : CPHUMAN ;
if ( j = = CPHUMAN )
2013-01-06 22:30:12 +03:00
{
player . canHumanPlay = true ;
}
2014-05-29 18:34:46 +03:00
2015-06-01 21:57:43 +02:00
if ( teamNumbers [ j ] . empty ( ) )
{
logGlobal - > errorStream ( ) < < boost : : format ( " Not enough places in team for %s player " ) % ( ( j = = CPUONLY ) ? " CPU " : " CPU or human " ) ;
assert ( teamNumbers [ j ] . size ( ) ) ;
}
2014-05-29 18:34:46 +03:00
auto itTeam = RandomGeneratorUtil : : nextItem ( teamNumbers [ j ] , rand ) ;
2013-03-03 20:06:03 +03:00
player . team = TeamID ( * itTeam ) ;
2013-01-06 22:30:12 +03:00
teamNumbers [ j ] . erase ( itTeam ) ;
2013-03-03 20:06:03 +03:00
map - > players [ pSettings . getColor ( ) . getNum ( ) ] = player ;
2013-01-06 22:30:12 +03:00
}
2014-05-23 12:56:51 +03:00
map - > howManyTeams = ( mapGenOptions - > getTeamCount ( ) = = 0 ? mapGenOptions - > getPlayerCount ( ) : mapGenOptions - > getTeamCount ( ) )
+ ( mapGenOptions - > getCompOnlyTeamCount ( ) = = 0 ? mapGenOptions - > getCompOnlyPlayerCount ( ) : mapGenOptions - > getCompOnlyTeamCount ( ) ) ;
2013-01-06 22:30:12 +03:00
}
2014-05-22 17:27:13 +03:00
void CMapGenerator : : genZones ( )
2013-01-06 22:30:12 +03:00
{
2014-05-22 20:25:17 +03:00
editManager - > clearTerrain ( & rand ) ;
2014-05-23 12:56:51 +03:00
editManager - > getTerrainSelection ( ) . selectRange ( MapRect ( int3 ( 0 , 0 , 0 ) , mapGenOptions - > getWidth ( ) , mapGenOptions - > getHeight ( ) ) ) ;
2014-05-22 20:25:17 +03:00
editManager - > drawTerrain ( ETerrainType : : GRASS , & rand ) ;
2013-01-06 22:30:12 +03:00
2014-05-23 12:56:51 +03:00
auto tmpl = mapGenOptions - > getMapTemplate ( ) ;
2014-05-24 13:42:06 +03:00
zones = tmpl - > getZones ( ) ; //copy from template (refactor?)
2013-01-20 17:43:58 +03:00
2014-05-24 13:42:06 +03:00
CZonePlacer placer ( this ) ;
placer . placeZones ( mapGenOptions , & rand ) ;
2014-05-24 15:06:08 +03:00
placer . assignZones ( mapGenOptions ) ;
2014-05-24 13:42:06 +03:00
2014-05-22 17:27:13 +03:00
logGlobal - > infoStream ( ) < < " Zones generated successfully " ;
}
void CMapGenerator : : fillZones ( )
2015-11-14 15:50:29 +02:00
{
2014-07-23 20:17:07 +03:00
//init native town count with 0
for ( auto faction : VLC - > townh - > getAllowedFactions ( ) )
zonesPerFaction [ faction ] = 0 ;
2016-08-15 21:37:38 +02:00
findZonesForQuestArts ( ) ;
2014-05-22 17:27:13 +03:00
logGlobal - > infoStream ( ) < < " Started filling zones " ;
2014-05-31 11:56:14 +03:00
2014-07-24 20:16:49 +03:00
//initialize possible tiles before any object is actually placed
for ( auto it : zones )
it . second - > initFreeTiles ( this ) ;
2016-08-13 19:48:44 +02:00
createDirectConnections ( ) ; //direct
2014-07-08 21:13:51 +03:00
//make sure all connections are passable before creating borders
2014-05-31 11:56:14 +03:00
for ( auto it : zones )
2016-08-13 19:48:44 +02:00
it . second - > createBorder ( this ) ; //once direct connections are done
createConnections2 ( ) ; //subterranean gates and monoliths
//we need info about all town types to evaluate dwellings and pandoras with creatures properly
for ( auto it : zones )
2014-07-23 20:17:07 +03:00
it . second - > initTownType ( this ) ;
2016-08-13 19:48:44 +02:00
2014-09-21 22:51:51 +03:00
std : : vector < CRmgTemplateZone * > treasureZones ;
2014-07-23 20:17:07 +03:00
for ( auto it : zones )
{
2014-05-22 20:25:17 +03:00
it . second - > fill ( this ) ;
2014-09-21 22:51:51 +03:00
if ( it . second - > getType ( ) = = ETemplateZoneType : : TREASURE )
treasureZones . push_back ( it . second ) ;
}
2015-03-28 19:56:28 +02:00
//set apriopriate free/occupied tiles, including blocked underground rock
2015-03-28 23:03:38 +02:00
createObstaclesCommon1 ( ) ;
//set back original terrain for underground zones
for ( auto it : zones )
it . second - > createObstacles1 ( this ) ;
createObstaclesCommon2 ( ) ;
2015-03-28 19:56:28 +02:00
//place actual obstacles matching zone terrain
for ( auto it : zones )
2015-05-25 16:37:57 +02:00
{
2015-03-28 23:03:38 +02:00
it . second - > createObstacles2 ( this ) ;
2015-05-25 16:37:57 +02:00
}
# define PRINT_MAP_BEFORE_ROADS true
if ( PRINT_MAP_BEFORE_ROADS ) //enable to debug
{
std : : ofstream out ( " road debug " ) ;
int levels = map - > twoLevel ? 2 : 1 ;
int width = map - > width ;
int height = map - > height ;
for ( int k = 0 ; k < levels ; k + + )
{
for ( int j = 0 ; j < height ; j + + )
{
for ( int i = 0 ; i < width ; i + + )
{
char t = ' ? ' ;
switch ( getTile ( int3 ( i , j , k ) ) . getTileType ( ) )
{
case ETileType : : FREE :
t = ' ' ; break ;
case ETileType : : BLOCKED :
t = ' # ' ; break ;
case ETileType : : POSSIBLE :
t = ' - ' ; break ;
case ETileType : : USED :
t = ' O ' ; break ;
}
out < < t ;
}
out < < std : : endl ;
}
out < < std : : endl ;
}
out < < std : : endl ;
}
for ( auto it : zones )
{
it . second - > connectRoads ( this ) ; //draw roads after everything else has been placed
}
2015-03-28 19:56:28 +02:00
2014-09-21 22:51:51 +03:00
//find place for Grail
if ( treasureZones . empty ( ) )
{
for ( auto it : zones )
treasureZones . push_back ( it . second ) ;
}
auto grailZone = * RandomGeneratorUtil : : nextItem ( treasureZones , rand ) ;
map - > grailPos = * RandomGeneratorUtil : : nextItem ( * grailZone - > getFreePaths ( ) , rand ) ;
2014-05-22 17:27:13 +03:00
logGlobal - > infoStream ( ) < < " Zones filled successfully " ;
2013-01-06 22:30:12 +03:00
}
2015-03-28 23:03:38 +02:00
void CMapGenerator : : createObstaclesCommon1 ( )
2015-03-28 19:56:28 +02:00
{
2015-03-28 23:03:38 +02:00
if ( map - > twoLevel ) //underground
2015-03-28 21:09:54 +02:00
{
2015-03-28 22:13:22 +02:00
//negative approach - create rock tiles first, then make sure all accessible tiles have no rock
2015-03-28 21:09:54 +02:00
std : : vector < int3 > rockTiles ;
for ( int x = 0 ; x < map - > width ; x + + )
{
for ( int y = 0 ; y < map - > height ; y + + )
{
int3 tile ( x , y , 1 ) ;
if ( shouldBeBlocked ( tile ) )
{
2015-03-28 22:13:22 +02:00
rockTiles . push_back ( tile ) ;
2015-03-28 21:09:54 +02:00
}
}
}
editManager - > getTerrainSelection ( ) . setSelection ( rockTiles ) ;
editManager - > drawTerrain ( ETerrainType : : ROCK , & rand ) ;
2015-03-28 23:03:38 +02:00
}
}
2015-03-28 22:13:22 +02:00
2015-03-28 23:03:38 +02:00
void CMapGenerator : : createObstaclesCommon2 ( )
{
if ( map - > twoLevel )
{
2015-03-28 22:13:22 +02:00
//finally mark rock tiles as occupied, spawn no obstacles there
for ( int x = 0 ; x < map - > width ; x + + )
{
for ( int y = 0 ; y < map - > height ; y + + )
{
int3 tile ( x , y , 1 ) ;
if ( map - > getTile ( tile ) . terType = = ETerrainType : : ROCK )
{
setOccupied ( tile , ETileType : : USED ) ;
}
}
2015-03-28 21:09:54 +02:00
}
}
2015-03-28 19:56:28 +02:00
//tighten obstacles to improve visuals
for ( int i = 0 ; i < 3 ; + + i )
{
int blockedTiles = 0 ;
int freeTiles = 0 ;
for ( int z = 0 ; z < ( map - > twoLevel ? 2 : 1 ) ; z + + )
{
for ( int x = 0 ; x < map - > width ; x + + )
{
for ( int y = 0 ; y < map - > height ; y + + )
{
int3 tile ( x , y , z ) ;
if ( ! isPossible ( tile ) ) //only possible tiles can change
continue ;
int blockedNeighbours = 0 ;
int freeNeighbours = 0 ;
foreach_neighbour ( tile , [ this , & blockedNeighbours , & freeNeighbours ] ( int3 & pos )
{
if ( this - > isBlocked ( pos ) )
blockedNeighbours + + ;
if ( this - > isFree ( pos ) )
freeNeighbours + + ;
} ) ;
if ( blockedNeighbours > 4 )
{
setOccupied ( tile , ETileType : : BLOCKED ) ;
blockedTiles + + ;
}
else if ( freeNeighbours > 4 )
{
setOccupied ( tile , ETileType : : FREE ) ;
freeTiles + + ;
}
}
}
}
logGlobal - > traceStream ( ) < < boost : : format ( " Set %d tiles to BLOCKED and %d tiles to FREE " ) % blockedTiles % freeTiles ;
}
}
2015-03-01 11:20:49 +02:00
void CMapGenerator : : findZonesForQuestArts ( )
{
//we want to place arties in zones that were not yet filled (higher index)
for ( auto connection : mapGenOptions - > getMapTemplate ( ) - > getConnections ( ) )
{
auto zoneA = connection . getZoneA ( ) ;
auto zoneB = connection . getZoneB ( ) ;
if ( zoneA - > getId ( ) > zoneB - > getId ( ) )
{
zoneB - > setQuestArtZone ( zoneA ) ;
}
else if ( zoneA - > getId ( ) < zoneB - > getId ( ) )
{
zoneA - > setQuestArtZone ( zoneB ) ;
}
}
}
2016-08-13 19:48:44 +02:00
void CMapGenerator : : createDirectConnections ( )
2014-06-01 17:31:15 +03:00
{
for ( auto connection : mapGenOptions - > getMapTemplate ( ) - > getConnections ( ) )
{
auto zoneA = connection . getZoneA ( ) ;
auto zoneB = connection . getZoneB ( ) ;
//rearrange tiles in random order
auto tilesCopy = zoneA - > getTileInfo ( ) ;
std : : vector < int3 > tiles ( tilesCopy . begin ( ) , tilesCopy . end ( ) ) ;
int3 guardPos ( - 1 , - 1 , - 1 ) ;
auto otherZoneTiles = zoneB - > getTileInfo ( ) ;
2014-07-04 10:54:55 +03:00
int3 posA = zoneA - > getPos ( ) ;
int3 posB = zoneB - > getPos ( ) ;
2016-10-27 19:22:29 +02:00
// auto zoneAid = zoneA->getId();
2016-08-09 13:40:46 +02:00
auto zoneBid = zoneB - > getId ( ) ;
2014-07-04 10:54:55 +03:00
if ( posA . z = = posB . z )
2014-06-01 17:31:15 +03:00
{
2016-07-24 18:15:23 +02:00
std : : vector < int3 > middleTiles ;
for ( auto tile : tilesCopy )
2014-06-01 17:31:15 +03:00
{
2014-07-03 18:24:28 +03:00
if ( isBlocked ( tile ) ) //tiles may be occupied by subterranean gates already placed
continue ;
2016-08-09 13:40:46 +02:00
foreachDirectNeighbour ( tile , [ & guardPos , tile , & otherZoneTiles , & middleTiles , this , zoneBid ] ( int3 & pos ) //must be direct since paths also also generated between direct neighbours
2014-07-03 18:24:28 +03:00
{
2016-08-09 13:40:46 +02:00
if ( getZoneID ( pos ) = = zoneBid )
2016-07-24 18:15:23 +02:00
middleTiles . push_back ( tile ) ;
2014-07-03 18:24:28 +03:00
} ) ;
2016-07-24 18:15:23 +02:00
}
2016-07-25 18:37:42 +02:00
//find tiles with minimum manhattan distance from center of the mass of zone border
size_t tilesCount = middleTiles . size ( ) ? middleTiles . size ( ) : 1 ;
int3 middleTile = std : : accumulate ( middleTiles . begin ( ) , middleTiles . end ( ) , int3 ( 0 , 0 , 0 ) ) ;
middleTile . x / = tilesCount ;
middleTile . y / = tilesCount ;
middleTile . z / = tilesCount ; //TODO: implement division operator for int3?
2016-07-28 12:15:19 +02:00
boost : : sort ( middleTiles , [ middleTile ] ( const int3 & lhs , const int3 & rhs ) - > bool
2016-07-24 18:15:23 +02:00
{
//choose tiles with both corrdinates in the middle
2016-07-25 18:37:42 +02:00
return lhs . mandist2d ( middleTile ) < rhs . mandist2d ( middleTile ) ;
2016-07-24 18:15:23 +02:00
} ) ;
2016-07-25 18:37:42 +02:00
//remove 1/4 tiles from each side - path should cross zone borders at smooth angle
size_t removedCount = tilesCount / 4 ; //rounded down
2016-07-24 18:15:23 +02:00
middleTiles . erase ( middleTiles . end ( ) - removedCount , middleTiles . end ( ) ) ;
middleTiles . erase ( middleTiles . begin ( ) , middleTiles . begin ( ) + removedCount ) ;
RandomGeneratorUtil : : randomShuffle ( middleTiles , rand ) ;
for ( auto tile : middleTiles )
{
guardPos = tile ;
2014-07-03 18:24:28 +03:00
if ( guardPos . valid ( ) )
{
2016-07-24 18:15:23 +02:00
setOccupied ( guardPos , ETileType : : FREE ) ; //just in case monster is too weak to spawn
zoneA - > addMonster ( this , guardPos , connection . getGuardStrength ( ) , false , true ) ;
2014-07-03 18:24:28 +03:00
//zones can make paths only in their own area
2015-06-02 16:40:32 +02:00
zoneA - > crunchPath ( this , guardPos , posA , true , zoneA - > getFreePaths ( ) ) ; //make connection towards our zone center
2015-11-14 15:50:29 +02:00
zoneB - > crunchPath ( this , guardPos , posB , true , zoneB - > getFreePaths ( ) ) ; //make connection towards other zone center
2015-01-18 15:19:00 +02:00
zoneA - > addRoadNode ( guardPos ) ;
zoneB - > addRoadNode ( guardPos ) ;
2014-07-03 18:24:28 +03:00
break ; //we're done with this connection
}
}
}
2016-08-13 19:48:44 +02:00
if ( ! guardPos . valid ( ) )
connectionsLeft . push_back ( connection ) ;
}
}
void CMapGenerator : : createConnections2 ( )
{
for ( auto & connection : connectionsLeft )
{
auto zoneA = connection . getZoneA ( ) ;
auto zoneB = connection . getZoneB ( ) ;
int3 guardPos ( - 1 , - 1 , - 1 ) ;
int3 posA = zoneA - > getPos ( ) ;
int3 posB = zoneB - > getPos ( ) ;
2016-08-15 21:37:38 +02:00
auto strength = connection . getGuardStrength ( ) ;
2016-08-13 19:48:44 +02:00
if ( posA . z ! = posB . z ) //try to place subterranean gates
2015-11-14 15:50:29 +02:00
{
2016-08-15 21:37:38 +02:00
auto sgt = VLC - > objtypeh - > getHandlerFor ( Obj : : SUBTERRANEAN_GATE , 0 ) - > getTemplates ( ) . front ( ) ;
auto tilesBlockedByObject = sgt . getBlockedOffsets ( ) ;
2016-08-13 19:48:44 +02:00
2016-08-15 21:37:38 +02:00
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : SUBTERRANEAN_GATE , 0 ) ;
auto gate1 = factory - > create ( ObjectTemplate ( ) ) ;
auto gate2 = factory - > create ( ObjectTemplate ( ) ) ;
2016-07-10 12:29:48 +02:00
2016-08-15 21:37:38 +02:00
while ( ! guardPos . valid ( ) )
2016-07-10 12:29:48 +02:00
{
2016-08-15 21:37:38 +02:00
bool continueOuterLoop = false ;
//find common tiles for both zones
auto tileSetA = zoneA - > getPossibleTiles ( ) ,
tileSetB = zoneB - > getPossibleTiles ( ) ;
2016-07-10 12:29:48 +02:00
2016-08-15 21:37:38 +02:00
std : : vector < int3 > tilesA ( tileSetA . begin ( ) , tileSetA . end ( ) ) ,
tilesB ( tileSetB . begin ( ) , tileSetB . end ( ) ) ;
2016-08-14 08:36:15 +02:00
2016-08-15 21:37:38 +02:00
std : : vector < int3 > commonTiles ;
2014-07-03 18:24:28 +03:00
2016-08-15 21:37:38 +02:00
//required for set_intersection
boost : : sort ( tilesA ) ;
boost : : sort ( tilesB ) ;
2016-07-10 10:11:29 +02:00
2016-08-15 21:37:38 +02:00
boost : : set_intersection ( tilesA , tilesB , std : : back_inserter ( commonTiles ) , [ ] ( const int3 & lhs , const int3 & rhs ) - > bool
{
//ignore z coordinate
if ( lhs . x < rhs . x )
return true ;
else
return lhs . y < rhs . y ;
} ) ;
2014-07-04 10:54:55 +03:00
2016-08-15 21:37:38 +02:00
vstd : : erase_if ( commonTiles , [ ] ( const int3 & tile ) - > bool
{
return ( ! tile . x ) | | ( ! tile . y ) ; //gates shouldn't go outside map (x = 0) and look bad at the very top (y = 0)
} ) ;
2014-07-04 00:11:24 +03:00
2016-08-15 21:37:38 +02:00
if ( commonTiles . empty ( ) )
break ; //nothing more to do
boost : : sort ( commonTiles , [ posA , posB ] ( const int3 & lhs , const int3 & rhs ) - > bool
2014-07-04 00:11:24 +03:00
{
2016-08-15 21:37:38 +02:00
//choose tiles which are equidistant to zone centers
return ( std : : abs < double > ( posA . dist2dSQ ( lhs ) - posB . dist2dSQ ( lhs ) ) < std : : abs < double > ( ( posA . dist2dSQ ( rhs ) - posB . dist2dSQ ( rhs ) ) ) ) ;
} ) ;
2014-07-04 11:45:00 +03:00
2016-08-15 21:37:38 +02:00
for ( auto tile : commonTiles )
{
tile . z = posA . z ;
int3 otherTile = tile ;
otherTile . z = posB . z ;
float distanceFromA = posA . dist2d ( tile ) ;
float distanceFromB = posB . dist2d ( otherTile ) ;
if ( distanceFromA > 5 & & distanceFromB > 5 )
{
if ( zoneA - > areAllTilesAvailable ( this , gate1 , tile , tilesBlockedByObject ) & &
zoneB - > areAllTilesAvailable ( this , gate2 , otherTile , tilesBlockedByObject ) )
2014-07-04 11:45:00 +03:00
{
2016-08-13 19:48:44 +02:00
if ( zoneA - > getAccessibleOffset ( this , sgt , tile ) . valid ( ) & & zoneB - > getAccessibleOffset ( this , sgt , otherTile ) . valid ( ) )
2016-07-10 10:11:29 +02:00
{
2016-08-15 21:37:38 +02:00
EObjectPlacingResult : : EObjectPlacingResult result1 = zoneA - > tryToPlaceObjectAndConnectToPath ( this , gate1 , tile ) ;
EObjectPlacingResult : : EObjectPlacingResult result2 = zoneB - > tryToPlaceObjectAndConnectToPath ( this , gate2 , otherTile ) ;
if ( ( result1 = = EObjectPlacingResult : : SUCCESS ) & & ( result2 = = EObjectPlacingResult : : SUCCESS ) )
{
zoneA - > placeObject ( this , gate1 , tile ) ;
zoneA - > guardObject ( this , gate1 , strength , true , true ) ;
zoneB - > placeObject ( this , gate2 , otherTile ) ;
zoneB - > guardObject ( this , gate2 , strength , true , true ) ;
guardPos = tile ; //set to break the loop
break ;
}
else if ( ( result1 = = EObjectPlacingResult : : SEALED_OFF ) | | ( result2 = = EObjectPlacingResult : : SEALED_OFF ) )
{
//sealed-off tiles were blocked, exit inner loop and get another tile set
2016-10-27 19:22:29 +02:00
continueOuterLoop = true ;
2016-08-15 21:37:38 +02:00
break ;
}
else
continue ; //try with another position
2016-07-10 10:11:29 +02:00
}
2014-07-04 11:45:00 +03:00
}
2014-07-04 00:11:24 +03:00
}
}
2016-11-27 21:37:41 +02:00
if ( ! continueOuterLoop ) //we didn't find ANY tile - break outer loop
2016-08-15 21:37:38 +02:00
break ;
}
if ( ! guardPos . valid ( ) ) //cleanup? is this safe / enough?
{
delete gate1 ;
delete gate2 ;
2014-06-01 17:31:15 +03:00
}
}
if ( ! guardPos . valid ( ) )
{
2015-11-14 15:50:29 +02:00
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : MONOLITH_TWO_WAY , getNextMonlithIndex ( ) ) ;
auto teleport1 = factory - > create ( ObjectTemplate ( ) ) ;
auto teleport2 = factory - > create ( ObjectTemplate ( ) ) ;
2014-06-01 17:31:15 +03:00
2016-08-15 21:37:38 +02:00
zoneA - > addRequiredObject ( teleport1 , strength ) ;
zoneB - > addRequiredObject ( teleport2 , strength ) ;
2015-11-14 15:50:29 +02:00
}
2014-06-01 17:31:15 +03:00
}
}
2013-01-06 22:30:12 +03:00
void CMapGenerator : : addHeaderInfo ( )
{
2016-02-09 19:20:03 +02:00
map - > version = EMapFormat : : VCMI ;
2014-05-23 12:56:51 +03:00
map - > width = mapGenOptions - > getWidth ( ) ;
map - > height = mapGenOptions - > getHeight ( ) ;
map - > twoLevel = mapGenOptions - > getHasTwoLevels ( ) ;
2013-01-06 22:30:12 +03:00
map - > name = VLC - > generaltexth - > allTexts [ 740 ] ;
map - > description = getMapDescription ( ) ;
map - > difficulty = 1 ;
addPlayerInfo ( ) ;
2014-05-24 13:42:06 +03:00
}
2015-01-18 15:19:00 +02:00
void CMapGenerator : : checkIsOnMap ( const int3 & tile ) const
{
if ( ! map - > isInTheMap ( tile ) )
2015-11-14 15:50:29 +02:00
throw rmgException ( boost : : to_string ( boost : : format ( " Tile %s is outside the map " ) % tile ) ) ;
2015-01-18 15:19:00 +02:00
}
2014-05-24 13:42:06 +03:00
std : : map < TRmgTemplateZoneId , CRmgTemplateZone * > CMapGenerator : : getZones ( ) const
{
return zones ;
2014-05-29 18:34:46 +03:00
}
2014-05-30 17:50:06 +03:00
2014-05-30 22:23:41 +03:00
bool CMapGenerator : : isBlocked ( const int3 & tile ) const
2014-05-30 17:50:06 +03:00
{
2015-01-18 15:19:00 +02:00
checkIsOnMap ( tile ) ;
2014-05-30 17:50:06 +03:00
return tiles [ tile . x ] [ tile . y ] [ tile . z ] . isBlocked ( ) ;
}
2014-05-30 22:23:41 +03:00
bool CMapGenerator : : shouldBeBlocked ( const int3 & tile ) const
2014-05-30 17:50:06 +03:00
{
2015-01-18 15:19:00 +02:00
checkIsOnMap ( tile ) ;
2014-05-30 17:50:06 +03:00
return tiles [ tile . x ] [ tile . y ] [ tile . z ] . shouldBeBlocked ( ) ;
}
2014-05-30 22:23:41 +03:00
bool CMapGenerator : : isPossible ( const int3 & tile ) const
2014-05-30 17:50:06 +03:00
{
2015-01-18 15:19:00 +02:00
checkIsOnMap ( tile ) ;
2014-05-30 17:50:06 +03:00
return tiles [ tile . x ] [ tile . y ] [ tile . z ] . isPossible ( ) ;
}
2014-05-30 22:23:41 +03:00
bool CMapGenerator : : isFree ( const int3 & tile ) const
2014-05-30 17:50:06 +03:00
{
2015-01-18 15:19:00 +02:00
checkIsOnMap ( tile ) ;
2014-05-30 17:50:06 +03:00
return tiles [ tile . x ] [ tile . y ] [ tile . z ] . isFree ( ) ;
}
2014-07-04 00:11:24 +03:00
bool CMapGenerator : : isUsed ( const int3 & tile ) const
{
2015-01-18 15:19:00 +02:00
checkIsOnMap ( tile ) ;
2014-07-04 00:11:24 +03:00
return tiles [ tile . x ] [ tile . y ] [ tile . z ] . isUsed ( ) ;
}
2015-01-18 15:19:00 +02:00
bool CMapGenerator : : isRoad ( const int3 & tile ) const
{
checkIsOnMap ( tile ) ;
2015-11-14 15:50:29 +02:00
return tiles [ tile . x ] [ tile . y ] [ tile . z ] . isRoad ( ) ;
2015-01-18 15:19:00 +02:00
}
2014-05-31 15:11:20 +03:00
void CMapGenerator : : setOccupied ( const int3 & tile , ETileType : : ETileType state )
2014-05-30 17:50:06 +03:00
{
2015-01-18 15:19:00 +02:00
checkIsOnMap ( tile ) ;
2014-05-30 17:50:06 +03:00
tiles [ tile . x ] [ tile . y ] [ tile . z ] . setOccupied ( state ) ;
}
2014-05-30 22:23:41 +03:00
2015-01-18 15:19:00 +02:00
void CMapGenerator : : setRoad ( const int3 & tile , ERoadType : : ERoadType roadType )
2014-05-30 22:23:41 +03:00
{
2015-01-18 15:19:00 +02:00
checkIsOnMap ( tile ) ;
2015-11-14 15:50:29 +02:00
tiles [ tile . x ] [ tile . y ] [ tile . z ] . setRoadType ( roadType ) ;
2015-01-18 15:19:00 +02:00
}
CTileInfo CMapGenerator : : getTile ( const int3 & tile ) const
{
checkIsOnMap ( tile ) ;
2014-05-30 22:23:41 +03:00
return tiles [ tile . x ] [ tile . y ] [ tile . z ] ;
}
2016-08-09 10:12:13 +02:00
TRmgTemplateZoneId CMapGenerator : : getZoneID ( const int3 & tile ) const
{
checkIsOnMap ( tile ) ;
return zoneColouring [ tile . z ] [ tile . x ] [ tile . y ] ;
}
2016-11-27 21:37:41 +02:00
void CMapGenerator : : setZoneID ( const int3 & tile , TRmgTemplateZoneId zid )
2016-08-09 10:12:13 +02:00
{
checkIsOnMap ( tile ) ;
zoneColouring [ tile . z ] [ tile . x ] [ tile . y ] = zid ;
}
2015-08-12 16:40:08 +02:00
bool CMapGenerator : : isAllowedSpell ( SpellID sid ) const
{
assert ( sid > = 0 ) ;
if ( sid < map - > allowedSpell . size ( ) )
{
return map - > allowedSpell [ sid ] ;
}
else
return false ;
}
2014-07-25 18:10:16 +03:00
void CMapGenerator : : setNearestObjectDistance ( int3 & tile , float value )
2014-05-30 22:23:41 +03:00
{
2015-01-18 15:19:00 +02:00
checkIsOnMap ( tile ) ;
2014-05-30 22:23:41 +03:00
tiles [ tile . x ] [ tile . y ] [ tile . z ] . setNearestObjectDistance ( value ) ;
}
2014-07-25 18:10:16 +03:00
float CMapGenerator : : getNearestObjectDistance ( const int3 & tile ) const
2014-05-30 22:23:41 +03:00
{
2015-01-18 15:19:00 +02:00
checkIsOnMap ( tile ) ;
2014-05-30 22:23:41 +03:00
return tiles [ tile . x ] [ tile . y ] [ tile . z ] . getNearestObjectDistance ( ) ;
2014-06-01 13:02:43 +03:00
}
int CMapGenerator : : getNextMonlithIndex ( )
{
2014-07-04 15:46:02 +03:00
if ( monolithIndex > = VLC - > objtypeh - > knownSubObjects ( Obj : : MONOLITH_TWO_WAY ) . size ( ) )
2014-10-30 22:23:25 +02:00
{
2014-10-31 13:58:55 +02:00
//logGlobal->errorStream() << boost::to_string(boost::format("RMG Error! There is no Monolith Two Way with index %d available!") % monolithIndex);
//monolithIndex++;
//return VLC->objtypeh->knownSubObjects(Obj::MONOLITH_TWO_WAY).size() - 1;
2014-10-30 22:23:25 +02:00
//TODO: interrupt map generation and report error
2014-10-31 13:58:55 +02:00
throw rmgException ( boost : : to_string ( boost : : format ( " There is no Monolith Two Way with index %d available! " ) % monolithIndex ) ) ;
2014-10-30 22:23:25 +02:00
}
2014-07-04 15:46:02 +03:00
else
return monolithIndex + + ;
2014-06-01 22:53:00 +03:00
}
2014-07-07 17:20:48 +03:00
2014-07-25 11:44:17 +03:00
int CMapGenerator : : getPrisonsRemaning ( ) const
{
return prisonsRemaining ;
}
void CMapGenerator : : decreasePrisonsRemaining ( )
{
2014-07-26 09:12:45 +03:00
prisonsRemaining = std : : max ( 0 , prisonsRemaining - 1 ) ;
2014-07-25 11:44:17 +03:00
}
2015-02-28 23:37:04 +02:00
std : : vector < ArtifactID > CMapGenerator : : getQuestArtsRemaning ( ) const
2015-02-28 22:14:45 +02:00
{
2015-02-28 23:37:04 +02:00
return questArtifacts ;
2015-02-28 22:14:45 +02:00
}
2015-02-28 23:37:04 +02:00
void CMapGenerator : : banQuestArt ( ArtifactID id )
2015-02-28 22:14:45 +02:00
{
2015-02-28 23:37:04 +02:00
map - > allowedArtifact [ id ] = false ;
vstd : : erase_if_present ( questArtifacts , id ) ;
2015-02-28 22:14:45 +02:00
}
2014-07-07 17:20:48 +03:00
void CMapGenerator : : registerZone ( TFaction faction )
{
zonesPerFaction [ faction ] + + ;
zonesTotal + + ;
}
ui32 CMapGenerator : : getZoneCount ( TFaction faction )
{
return zonesPerFaction [ faction ] ;
}
ui32 CMapGenerator : : getTotalZoneCount ( ) const
{
return zonesTotal ;
}