2013-08-17 15:46:48 +03:00
/*
* CRmgTemplateZone . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
# include "StdInc.h"
# include "CRmgTemplateZone.h"
2014-05-22 20:25:17 +03:00
# include "../mapping/CMapEditManager.h"
# include "../mapping/CMap.h"
2013-08-17 15:46:48 +03:00
# include "../VCMI_Lib.h"
# include "../CTownHandler.h"
2014-06-01 15:10:44 +03:00
# include "../CCreatureHandler.h"
2014-06-05 21:19:42 +03:00
# include "../CSpellHandler.h" //for choosing random spells
2013-08-17 15:46:48 +03:00
2014-07-06 23:14:37 +03:00
# include "../mapObjects/CommonConstructors.h"
# include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h
2014-06-22 17:26:08 +03:00
# include "../mapObjects/CGPandoraBox.h"
# include "../mapObjects/CRewardableObject.h"
2014-06-05 19:52:14 +03:00
2014-05-22 20:25:17 +03:00
class CMap ;
class CMapEditManager ;
2014-07-06 23:14:37 +03:00
//class CGObjectInstance;
2014-05-22 20:25:17 +03:00
2013-08-17 15:46:48 +03:00
CRmgTemplateZone : : CTownInfo : : CTownInfo ( ) : townCount ( 0 ) , castleCount ( 0 ) , townDensity ( 0 ) , castleDensity ( 0 )
{
}
int CRmgTemplateZone : : CTownInfo : : getTownCount ( ) const
{
return townCount ;
}
void CRmgTemplateZone : : CTownInfo : : setTownCount ( int value )
{
2014-05-23 18:12:31 +03:00
if ( value < 0 )
throw rmgException ( " Negative value for town count not allowed. " ) ;
2013-08-17 15:46:48 +03:00
townCount = value ;
}
int CRmgTemplateZone : : CTownInfo : : getCastleCount ( ) const
{
return castleCount ;
}
void CRmgTemplateZone : : CTownInfo : : setCastleCount ( int value )
{
2014-05-23 18:12:31 +03:00
if ( value < 0 )
throw rmgException ( " Negative value for castle count not allowed. " ) ;
2013-08-17 15:46:48 +03:00
castleCount = value ;
}
int CRmgTemplateZone : : CTownInfo : : getTownDensity ( ) const
{
return townDensity ;
}
void CRmgTemplateZone : : CTownInfo : : setTownDensity ( int value )
{
2014-05-23 18:12:31 +03:00
if ( value < 0 )
throw rmgException ( " Negative value for town density not allowed. " ) ;
2013-08-17 15:46:48 +03:00
townDensity = value ;
}
int CRmgTemplateZone : : CTownInfo : : getCastleDensity ( ) const
{
return castleDensity ;
}
void CRmgTemplateZone : : CTownInfo : : setCastleDensity ( int value )
{
2014-05-23 18:12:31 +03:00
if ( value < 0 )
throw rmgException ( " Negative value for castle density not allowed. " ) ;
2013-08-17 15:46:48 +03:00
castleDensity = value ;
}
2014-05-30 17:50:06 +03:00
CTileInfo : : CTileInfo ( ) : nearestObjectDistance ( INT_MAX ) , terrain ( ETerrainType : : WRONG )
2014-05-22 20:25:17 +03:00
{
2014-05-30 17:50:06 +03:00
occupied = ETileType : : POSSIBLE ; //all tiles are initially possible to place objects or passages
2014-05-22 20:25:17 +03:00
}
2014-07-25 18:10:16 +03:00
float CTileInfo : : getNearestObjectDistance ( ) const
2014-05-22 20:25:17 +03:00
{
return nearestObjectDistance ;
}
2014-07-25 18:10:16 +03:00
void CTileInfo : : setNearestObjectDistance ( float value )
2014-05-22 20:25:17 +03:00
{
2014-07-25 18:10:16 +03:00
nearestObjectDistance = std : : max < float > ( 0 , value ) ; //never negative (or unitialized)
2014-05-22 20:25:17 +03:00
}
2014-05-30 17:50:06 +03:00
bool CTileInfo : : shouldBeBlocked ( ) const
2014-05-22 20:25:17 +03:00
{
2014-05-30 17:50:06 +03:00
return occupied = = ETileType : : BLOCKED ;
2014-05-22 20:25:17 +03:00
}
2014-05-30 17:50:06 +03:00
bool CTileInfo : : isBlocked ( ) const
2014-05-22 20:25:17 +03:00
{
2014-05-30 17:50:06 +03:00
return occupied = = ETileType : : BLOCKED | | occupied = = ETileType : : USED ;
2014-05-22 20:25:17 +03:00
}
2014-05-30 17:50:06 +03:00
bool CTileInfo : : isPossible ( ) const
2014-05-22 20:25:17 +03:00
{
2014-05-30 17:50:06 +03:00
return occupied = = ETileType : : POSSIBLE ;
2014-05-22 20:25:17 +03:00
}
2014-05-30 17:50:06 +03:00
bool CTileInfo : : isFree ( ) const
{
return occupied = = ETileType : : FREE ;
}
2014-07-04 00:11:24 +03:00
bool CTileInfo : : isUsed ( ) const
{
return occupied = = ETileType : : USED ;
}
2014-05-30 17:50:06 +03:00
void CTileInfo : : setOccupied ( ETileType : : ETileType value )
2014-05-22 20:25:17 +03:00
{
occupied = value ;
}
2015-01-16 10:40:11 +02:00
ETileType : : ETileType CTileInfo : : getTileType ( ) const
{
return occupied ;
}
2014-05-30 17:50:06 +03:00
ETerrainType CTileInfo : : getTerrainType ( ) const
2014-05-22 20:25:17 +03:00
{
return terrain ;
}
2014-05-30 17:50:06 +03:00
void CTileInfo : : setTerrainType ( ETerrainType value )
2014-05-22 20:25:17 +03:00
{
terrain = value ;
}
2014-06-22 17:26:08 +03:00
CRmgTemplateZone : : CRmgTemplateZone ( ) :
id ( 0 ) ,
type ( ETemplateZoneType : : PLAYER_START ) ,
size ( 1 ) ,
townsAreSameType ( false ) ,
matchTerrainToTown ( true ) ,
2014-10-30 14:03:53 +02:00
townType ( ETownType : : NEUTRAL ) ,
2014-06-22 17:26:08 +03:00
terrainType ( ETerrainType : : GRASS ) ,
2014-12-26 17:17:39 +02:00
zoneMonsterStrength ( EMonsterStrength : : ZONE_NORMAL ) ,
2015-03-01 11:20:49 +02:00
minGuardedValue ( 0 ) ,
questArtZone ( nullptr )
2013-08-17 15:46:48 +03:00
{
terrainTypes = getDefaultTerrainTypes ( ) ;
}
TRmgTemplateZoneId CRmgTemplateZone : : getId ( ) const
{
return id ;
}
void CRmgTemplateZone : : setId ( TRmgTemplateZoneId value )
{
2014-05-23 18:12:31 +03:00
if ( value < = 0 )
throw rmgException ( boost : : to_string ( boost : : format ( " Zone %d id should be greater than 0. " ) % id ) ) ;
2013-08-17 15:46:48 +03:00
id = value ;
}
ETemplateZoneType : : ETemplateZoneType CRmgTemplateZone : : getType ( ) const
{
return type ;
}
void CRmgTemplateZone : : setType ( ETemplateZoneType : : ETemplateZoneType value )
{
type = value ;
}
int CRmgTemplateZone : : getSize ( ) const
{
return size ;
}
void CRmgTemplateZone : : setSize ( int value )
{
2014-05-23 18:12:31 +03:00
if ( value < = 0 )
throw rmgException ( boost : : to_string ( boost : : format ( " Zone %d size needs to be greater than 0. " ) % id ) ) ;
2013-08-17 15:46:48 +03:00
size = value ;
}
boost : : optional < int > CRmgTemplateZone : : getOwner ( ) const
{
return owner ;
}
void CRmgTemplateZone : : setOwner ( boost : : optional < int > value )
{
2014-05-23 18:12:31 +03:00
if ( ! ( * value > = 0 & & * value < = PlayerColor : : PLAYER_LIMIT_I ) )
throw rmgException ( boost : : to_string ( boost : : format ( " Owner of zone %d has to be in range 0 to max player count. " ) % id ) ) ;
2013-08-17 15:46:48 +03:00
owner = value ;
}
const CRmgTemplateZone : : CTownInfo & CRmgTemplateZone : : getPlayerTowns ( ) const
{
return playerTowns ;
}
void CRmgTemplateZone : : setPlayerTowns ( const CTownInfo & value )
{
playerTowns = value ;
}
const CRmgTemplateZone : : CTownInfo & CRmgTemplateZone : : getNeutralTowns ( ) const
{
return neutralTowns ;
}
void CRmgTemplateZone : : setNeutralTowns ( const CTownInfo & value )
{
neutralTowns = value ;
}
bool CRmgTemplateZone : : getTownsAreSameType ( ) const
{
return townsAreSameType ;
}
void CRmgTemplateZone : : setTownsAreSameType ( bool value )
{
townsAreSameType = value ;
}
const std : : set < TFaction > & CRmgTemplateZone : : getTownTypes ( ) const
{
return townTypes ;
}
void CRmgTemplateZone : : setTownTypes ( const std : : set < TFaction > & value )
{
townTypes = value ;
}
2014-10-30 14:03:53 +02:00
void CRmgTemplateZone : : setMonsterTypes ( const std : : set < TFaction > & value )
{
monsterTypes = value ;
}
2013-08-17 15:46:48 +03:00
std : : set < TFaction > CRmgTemplateZone : : getDefaultTownTypes ( ) const
{
std : : set < TFaction > defaultTowns ;
auto towns = VLC - > townh - > getDefaultAllowed ( ) ;
for ( int i = 0 ; i < towns . size ( ) ; + + i )
{
if ( towns [ i ] ) defaultTowns . insert ( i ) ;
}
return defaultTowns ;
}
bool CRmgTemplateZone : : getMatchTerrainToTown ( ) const
{
return matchTerrainToTown ;
}
void CRmgTemplateZone : : setMatchTerrainToTown ( bool value )
{
matchTerrainToTown = value ;
}
const std : : set < ETerrainType > & CRmgTemplateZone : : getTerrainTypes ( ) const
{
return terrainTypes ;
}
void CRmgTemplateZone : : setTerrainTypes ( const std : : set < ETerrainType > & value )
{
assert ( value . find ( ETerrainType : : WRONG ) = = value . end ( ) & & value . find ( ETerrainType : : BORDER ) = = value . end ( ) & &
value . find ( ETerrainType : : WATER ) = = value . end ( ) & & value . find ( ETerrainType : : ROCK ) = = value . end ( ) ) ;
terrainTypes = value ;
}
std : : set < ETerrainType > CRmgTemplateZone : : getDefaultTerrainTypes ( ) const
{
std : : set < ETerrainType > terTypes ;
2014-06-14 18:14:59 +03:00
static const ETerrainType : : EETerrainType allowedTerTypes [ ] = { ETerrainType : : DIRT , ETerrainType : : SAND , ETerrainType : : GRASS , ETerrainType : : SNOW ,
ETerrainType : : SWAMP , ETerrainType : : ROUGH , ETerrainType : : SUBTERRANEAN , ETerrainType : : LAVA } ;
for ( auto & allowedTerType : allowedTerTypes )
terTypes . insert ( allowedTerType ) ;
2013-08-17 15:46:48 +03:00
2014-06-14 18:14:59 +03:00
return terTypes ;
2013-08-17 15:46:48 +03:00
}
2014-05-22 20:25:17 +03:00
2014-06-14 21:05:19 +03:00
void CRmgTemplateZone : : setMinesAmount ( TResource res , ui16 amount )
{
mines [ res ] = amount ;
}
std : : map < TResource , ui16 > CRmgTemplateZone : : getMinesInfo ( ) const
{
return mines ;
}
2014-05-24 13:42:06 +03:00
void CRmgTemplateZone : : addConnection ( TRmgTemplateZoneId otherZone )
{
connections . push_back ( otherZone ) ;
}
2015-03-01 11:20:49 +02:00
void CRmgTemplateZone : : setQuestArtZone ( CRmgTemplateZone * otherZone )
{
questArtZone = otherZone ;
}
2014-05-24 13:42:06 +03:00
std : : vector < TRmgTemplateZoneId > CRmgTemplateZone : : getConnections ( ) const
{
return connections ;
}
2014-06-04 16:16:23 +03:00
2014-06-15 12:08:06 +03:00
void CRmgTemplateZone : : setMonsterStrength ( EMonsterStrength : : EMonsterStrength val )
{
assert ( vstd : : iswithin ( val , EMonsterStrength : : ZONE_WEAK , EMonsterStrength : : ZONE_STRONG ) ) ;
zoneMonsterStrength = val ;
}
2014-06-04 16:16:23 +03:00
void CRmgTemplateZone : : addTreasureInfo ( CTreasureInfo & info )
{
treasureInfo . push_back ( info ) ;
}
std : : vector < CTreasureInfo > CRmgTemplateZone : : getTreasureInfo ( )
{
return treasureInfo ;
}
2014-06-26 21:12:37 +03:00
std : : set < int3 > * CRmgTemplateZone : : getFreePaths ( )
{
return & freePaths ;
}
2014-05-24 13:42:06 +03:00
float3 CRmgTemplateZone : : getCenter ( ) const
{
return center ;
}
2014-05-30 17:50:06 +03:00
void CRmgTemplateZone : : setCenter ( const float3 & f )
2014-05-24 13:42:06 +03:00
{
//limit boundaries to (0,1) square
center = float3 ( std : : min ( std : : max ( f . x , 0.f ) , 1.f ) , std : : min ( std : : max ( f . y , 0.f ) , 1.f ) , f . z ) ;
}
2014-05-22 20:25:17 +03:00
bool CRmgTemplateZone : : pointIsIn ( int x , int y )
{
2014-05-24 15:06:08 +03:00
return true ;
2014-05-22 20:25:17 +03:00
}
2014-05-30 17:50:06 +03:00
int3 CRmgTemplateZone : : getPos ( ) const
2014-05-22 20:25:17 +03:00
{
2014-05-24 13:42:06 +03:00
return pos ;
}
2014-05-25 12:02:15 +03:00
void CRmgTemplateZone : : setPos ( const int3 & Pos )
2014-05-24 13:42:06 +03:00
{
pos = Pos ;
2014-05-22 20:25:17 +03:00
}
2014-05-25 12:02:15 +03:00
void CRmgTemplateZone : : addTile ( const int3 & pos )
2014-05-24 15:06:08 +03:00
{
2014-05-30 22:23:41 +03:00
tileinfo . insert ( pos ) ;
}
2014-05-31 11:56:14 +03:00
std : : set < int3 > CRmgTemplateZone : : getTileInfo ( ) const
{
return tileinfo ;
}
2014-07-07 19:01:15 +03:00
void CRmgTemplateZone : : discardDistantTiles ( CMapGenerator * gen , float distance )
{
//TODO: mark tiles beyond zone as unavailable, but allow to connect with adjacent zones
//for (auto tile : tileinfo)
//{
// if (tile.dist2d(this->pos) > distance)
// {
// gen->setOccupied(tile, ETileType::USED);
// //gen->setOccupied(tile, ETileType::BLOCKED); //fixme: crash at rendering?
// }
//}
2014-07-03 13:28:51 +03:00
vstd : : erase_if ( tileinfo , [ distance , this ] ( const int3 & tile ) - > bool
{
return tile . dist2d ( this - > pos ) > distance ;
} ) ;
}
2015-01-16 20:28:27 +02:00
void CRmgTemplateZone : : clearTiles ( )
{
tileinfo . clear ( ) ;
}
2014-07-24 20:16:49 +03:00
void CRmgTemplateZone : : initFreeTiles ( CMapGenerator * gen )
{
vstd : : copy_if ( tileinfo , vstd : : set_inserter ( possibleTiles ) , [ gen ] ( const int3 & tile ) - > bool
{
return gen - > isPossible ( tile ) ;
} ) ;
2015-02-25 20:34:02 +02:00
if ( freePaths . empty ( ) )
freePaths . insert ( pos ) ; //zone must have at least one free tile where other paths go - for instance in the center
2014-07-24 20:16:49 +03:00
}
2014-05-30 22:23:41 +03:00
void CRmgTemplateZone : : createBorder ( CMapGenerator * gen )
{
for ( auto tile : tileinfo )
{
gen - > foreach_neighbour ( tile , [ this , gen ] ( int3 & pos )
{
if ( ! vstd : : contains ( this - > tileinfo , pos ) )
2014-05-31 11:56:14 +03:00
{
gen - > foreach_neighbour ( pos , [ this , gen ] ( int3 & pos )
{
if ( gen - > isPossible ( pos ) )
gen - > setOccupied ( pos , ETileType : : BLOCKED ) ;
} ) ;
}
} ) ;
}
}
2014-06-12 22:10:43 +03:00
void CRmgTemplateZone : : fractalize ( CMapGenerator * gen )
{
2014-09-23 22:12:10 +03:00
for ( auto tile : tileinfo )
{
if ( gen - > isFree ( tile ) )
freePaths . insert ( tile ) ;
}
2014-07-08 12:28:55 +03:00
std : : vector < int3 > clearedTiles ( freePaths . begin ( ) , freePaths . end ( ) ) ;
2014-06-12 22:10:43 +03:00
std : : set < int3 > possibleTiles ;
std : : set < int3 > tilesToClear ; //will be set clear
std : : set < int3 > tilesToIgnore ; //will be erased in this iteration
2014-07-08 12:28:55 +03:00
//the more treasure density, the greater distance between paths. Scaling is experimental.
2014-12-20 15:01:48 +02:00
int totalDensity = 0 ;
for ( auto ti : treasureInfo )
totalDensity = + ti . density ;
2015-01-16 10:40:11 +02:00
const float minDistance = 10 * 10 ; //squared
2014-12-20 15:01:48 +02:00
2014-06-12 22:10:43 +03:00
for ( auto tile : tileinfo )
{
if ( gen - > isFree ( tile ) )
clearedTiles . push_back ( tile ) ;
2014-06-22 17:26:08 +03:00
else if ( gen - > isPossible ( tile ) )
2014-06-12 22:10:43 +03:00
possibleTiles . insert ( tile ) ;
}
2014-07-15 20:52:58 +03:00
assert ( clearedTiles . size ( ) ) ; //this should come from zone connections
2014-06-12 22:10:43 +03:00
2015-01-16 18:39:16 +02:00
if ( type ! = ETemplateZoneType : : JUNCTION )
2014-06-12 22:10:43 +03:00
{
2015-01-16 18:39:16 +02:00
//junction is not fractalized, has only one straight path
//everything else remains blocked
while ( possibleTiles . size ( ) )
2014-06-12 22:10:43 +03:00
{
2015-01-16 18:39:16 +02:00
//link tiles in random order
std : : vector < int3 > tilesToMakePath ( possibleTiles . begin ( ) , possibleTiles . end ( ) ) ;
RandomGeneratorUtil : : randomShuffle ( tilesToMakePath , gen - > rand ) ;
2014-06-12 22:10:43 +03:00
2015-01-16 18:39:16 +02:00
for ( auto tileToMakePath : tilesToMakePath )
2014-06-12 22:10:43 +03:00
{
2015-01-16 18:39:16 +02:00
//find closest free tile
float currentDistance = 1e10 ;
int3 closestTile ( - 1 , - 1 , - 1 ) ;
for ( auto clearTile : clearedTiles )
2014-06-12 22:10:43 +03:00
{
2015-01-16 18:39:16 +02:00
float distance = tileToMakePath . dist2dSQ ( clearTile ) ;
if ( distance < currentDistance )
{
currentDistance = distance ;
closestTile = clearTile ;
}
if ( currentDistance < = minDistance )
{
//this tile is close enough. Forget about it and check next one
tilesToIgnore . insert ( tileToMakePath ) ;
break ;
}
2014-06-12 22:10:43 +03:00
}
2015-01-16 18:39:16 +02:00
//if tiles is not close enough, make path to it
if ( currentDistance > minDistance )
2014-06-12 22:10:43 +03:00
{
2015-01-16 18:39:16 +02:00
crunchPath ( gen , tileToMakePath , closestTile , id , & tilesToClear ) ;
break ; //next iteration - use already cleared tiles
2014-06-12 22:10:43 +03:00
}
}
2015-01-16 18:39:16 +02:00
for ( auto tileToClear : tilesToClear )
2014-06-12 22:10:43 +03:00
{
2015-01-16 18:39:16 +02:00
//move cleared tiles from one set to another
clearedTiles . push_back ( tileToClear ) ;
vstd : : erase_if_present ( possibleTiles , tileToClear ) ;
2014-06-12 22:10:43 +03:00
}
2015-01-16 18:39:16 +02:00
for ( auto tileToClear : tilesToIgnore )
{
//these tiles are already connected, ignore them
vstd : : erase_if_present ( possibleTiles , tileToClear ) ;
}
if ( tilesToClear . empty ( ) ) //nothing else can be done (?)
break ;
tilesToClear . clear ( ) ; //empty this container
tilesToIgnore . clear ( ) ;
2014-06-12 22:10:43 +03:00
}
}
2014-07-08 12:28:55 +03:00
for ( auto tile : clearedTiles )
{
freePaths . insert ( tile ) ;
}
2014-12-24 16:07:20 +02:00
//now block most distant tiles away from passages
2015-01-16 10:40:11 +02:00
float blockDistance = minDistance * 0.25f ;
2014-12-24 16:07:20 +02:00
2015-01-16 10:40:11 +02:00
for ( auto tile : tileinfo )
2014-12-24 16:07:20 +02:00
{
2015-01-16 10:40:11 +02:00
if ( ! gen - > isPossible ( tile ) )
continue ;
2014-12-24 16:07:20 +02:00
bool closeTileFound = false ;
for ( auto clearTile : freePaths )
{
float distance = tile . dist2dSQ ( clearTile ) ;
if ( distance < blockDistance )
{
closeTileFound = true ;
break ;
}
}
if ( ! closeTileFound ) //this tile is far enough from passages
gen - > setOccupied ( tile , ETileType : : BLOCKED ) ;
}
2014-07-08 12:28:55 +03:00
2015-01-16 10:40:11 +02:00
# define PRINT_FRACTALIZED_MAP false
if ( PRINT_FRACTALIZED_MAP ) //enable to debug
2014-07-08 12:28:55 +03:00
{
std : : ofstream out ( boost : : to_string ( boost : : format ( " zone %d " ) % id ) ) ;
int levels = gen - > map - > twoLevel ? 2 : 1 ;
int width = gen - > map - > width ;
int height = gen - > map - > height ;
for ( int k = 0 ; k < levels ; k + + )
{
for ( int j = 0 ; j < height ; j + + )
{
for ( int i = 0 ; i < width ; i + + )
{
2015-01-16 10:40:11 +02:00
char t = ' ? ' ;
switch ( gen - > 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 ;
2014-07-08 12:28:55 +03:00
}
out < < std : : endl ;
}
out < < std : : endl ;
}
2015-01-16 10:40:11 +02:00
out < < std : : endl ;
2014-07-08 12:28:55 +03:00
}
2014-11-01 10:52:56 +02:00
//logGlobal->infoStream() << boost::format ("Zone %d subdivided fractally") %id;
2014-06-12 22:10:43 +03:00
}
bool CRmgTemplateZone : : crunchPath ( CMapGenerator * gen , const int3 & src , const int3 & dst , TRmgTemplateZoneId zone , std : : set < int3 > * clearedTiles )
2014-05-31 11:56:14 +03:00
{
/*
make shortest path with free tiles , reachning dst or closest already free tile . Avoid blocks .
do not leave zone border
*/
bool result = false ;
bool end = false ;
int3 currentPos = src ;
float distance = currentPos . dist2dSQ ( dst ) ;
while ( ! end )
{
if ( currentPos = = dst )
2014-06-13 13:04:17 +03:00
{
result = true ;
2014-05-31 11:56:14 +03:00
break ;
2014-06-13 13:04:17 +03:00
}
2014-05-31 11:56:14 +03:00
auto lastDistance = distance ;
2014-06-12 22:10:43 +03:00
gen - > foreach_neighbour ( currentPos , [ this , gen , & currentPos , dst , & distance , & result , & end , clearedTiles ] ( int3 & pos )
2014-05-31 11:56:14 +03:00
{
if ( ! result ) //not sure if lambda is worth it...
{
if ( pos = = dst )
{
result = true ;
end = true ;
}
if ( pos . dist2dSQ ( dst ) < distance )
{
if ( ! gen - > isBlocked ( pos ) )
{
if ( vstd : : contains ( tileinfo , pos ) )
{
if ( gen - > isPossible ( pos ) )
{
gen - > setOccupied ( pos , ETileType : : FREE ) ;
2014-06-12 22:10:43 +03:00
if ( clearedTiles )
clearedTiles - > insert ( pos ) ;
2014-05-31 11:56:14 +03:00
currentPos = pos ;
distance = currentPos . dist2dSQ ( dst ) ;
}
else if ( gen - > isFree ( pos ) )
{
end = true ;
result = true ;
}
}
}
}
}
2014-05-30 22:23:41 +03:00
} ) ;
2014-09-23 19:32:32 +03:00
int3 anotherPos ( - 1 , - 1 , - 1 ) ;
if ( ! ( result | | distance < lastDistance ) ) //we do not advance, use more advaced pathfinding algorithm?
{
//try any nearby tiles, even if its not closer than current
float lastDistance = 2 * distance ; //start with significantly larger value
gen - > foreach_neighbour ( currentPos , [ this , gen , & currentPos , dst , & lastDistance , & anotherPos , & end , clearedTiles ] ( int3 & pos )
{
if ( currentPos . dist2dSQ ( dst ) < lastDistance ) //try closest tiles from all surrounding unused tiles
{
if ( vstd : : contains ( tileinfo , pos ) )
{
if ( gen - > isPossible ( pos ) )
{
if ( clearedTiles )
clearedTiles - > insert ( pos ) ;
anotherPos = pos ;
lastDistance = currentPos . dist2dSQ ( dst ) ;
}
}
}
} ) ;
if ( anotherPos . valid ( ) )
{
if ( clearedTiles )
clearedTiles - > insert ( anotherPos ) ;
gen - > setOccupied ( anotherPos , ETileType : : FREE ) ;
currentPos = anotherPos ;
}
}
if ( ! ( result | | distance < lastDistance | | anotherPos . valid ( ) ) )
2014-05-31 11:56:14 +03:00
{
2014-09-23 19:32:32 +03:00
logGlobal - > warnStream ( ) < < boost : : format ( " No tile closer than %s found on path from %s to %s " ) % currentPos % src % dst ;
2014-05-31 11:56:14 +03:00
break ;
}
2014-05-30 22:23:41 +03:00
}
2014-05-31 11:56:14 +03:00
return result ;
2014-05-24 15:06:08 +03:00
}
2014-06-01 17:31:15 +03:00
void CRmgTemplateZone : : addRequiredObject ( CGObjectInstance * obj , si32 strength )
2014-05-22 20:25:17 +03:00
{
2014-06-01 17:31:15 +03:00
requiredObjects . push_back ( std : : make_pair ( obj , strength ) ) ;
2014-06-01 13:02:43 +03:00
}
2014-12-20 23:13:10 +02:00
void CRmgTemplateZone : : addCloseObject ( CGObjectInstance * obj , si32 strength )
{
closeObjects . push_back ( std : : make_pair ( obj , strength ) ) ;
}
2014-05-24 15:33:22 +03:00
2014-07-15 15:38:05 +03:00
bool CRmgTemplateZone : : addMonster ( CMapGenerator * gen , int3 & pos , si32 strength , bool clearSurroundingTiles , bool zoneGuard )
2014-06-01 15:10:44 +03:00
{
2014-06-04 11:16:08 +03:00
//precalculate actual (randomized) monster strength based on this post
//http://forum.vcmi.eu/viewtopic.php?p=12426#12426
int mapMonsterStrength = gen - > mapGenOptions - > getMonsterStrength ( ) ;
2014-07-15 15:38:05 +03:00
int monsterStrength = ( zoneGuard ? 0 : zoneMonsterStrength ) + mapMonsterStrength - 1 ; //array index from 0 to 4
2014-06-04 11:16:08 +03:00
static const int value1 [ ] = { 2500 , 1500 , 1000 , 500 , 0 } ;
static const int value2 [ ] = { 7500 , 7500 , 7500 , 5000 , 5000 } ;
static const float multiplier1 [ ] = { 0.5 , 0.75 , 1.0 , 1.5 , 1.5 } ;
static const float multiplier2 [ ] = { 0.5 , 0.75 , 1.0 , 1.0 , 1.5 } ;
int strength1 = std : : max ( 0.f , ( strength - value1 [ monsterStrength ] ) * multiplier1 [ monsterStrength ] ) ;
int strength2 = std : : max ( 0.f , ( strength - value2 [ monsterStrength ] ) * multiplier2 [ monsterStrength ] ) ;
strength = strength1 + strength2 ;
if ( strength < 2000 )
2014-06-04 21:59:01 +03:00
return false ; //no guard at all
2014-06-04 11:16:08 +03:00
2014-06-01 15:10:44 +03:00
CreatureID creId = CreatureID : : NONE ;
int amount = 0 ;
2014-06-07 23:27:36 +03:00
std : : vector < CreatureID > possibleCreatures ;
for ( auto cre : VLC - > creh - > creatures )
2014-06-01 15:10:44 +03:00
{
2014-06-07 23:27:36 +03:00
if ( cre - > special )
continue ;
2014-10-30 14:03:53 +02:00
if ( ! vstd : : contains ( monsterTypes , cre - > faction ) )
continue ;
2015-02-28 22:14:45 +02:00
if ( ( cre - > AIValue * ( cre - > ammMin + cre - > ammMax ) / 2 < strength ) & & ( strength < cre - > AIValue * 100 ) ) //at least one full monster. size between average size of given stack and 100
2014-06-04 11:16:08 +03:00
{
2014-06-07 23:27:36 +03:00
possibleCreatures . push_back ( cre - > idNumber ) ;
2014-06-04 11:16:08 +03:00
}
2014-06-01 15:10:44 +03:00
}
2014-06-07 23:27:36 +03:00
if ( possibleCreatures . size ( ) )
2014-06-08 09:42:29 +03:00
{
2014-06-07 23:27:36 +03:00
creId = * RandomGeneratorUtil : : nextItem ( possibleCreatures , gen - > rand ) ;
2014-06-08 09:42:29 +03:00
amount = strength / VLC - > creh - > creatures [ creId ] - > AIValue ;
if ( amount > = 4 )
amount * = gen - > rand . nextDouble ( 0.75 , 1.25 ) ;
}
2014-06-07 23:27:36 +03:00
else //just pick any available creature
{
creId = CreatureID ( 132 ) ; //Azure Dragon
amount = strength / VLC - > creh - > creatures [ creId ] - > AIValue ;
}
2014-06-01 15:10:44 +03:00
auto guard = new CGCreature ( ) ;
guard - > ID = Obj : : MONSTER ;
guard - > subID = creId ;
2015-01-10 11:23:58 +02:00
guard - > character = CGCreature : : HOSTILE ;
2014-06-01 15:10:44 +03:00
auto hlp = new CStackInstance ( creId , amount ) ;
//will be set during initialization
guard - > putStack ( SlotID ( 0 ) , hlp ) ;
2014-07-15 20:10:03 +03:00
//logGlobal->traceStream() << boost::format ("Adding stack of %d %s. Map monster strenght %d, zone monster strength %d, base monster value %d")
// % amount % VLC->creh->creatures[creId]->namePl % mapMonsterStrength % zoneMonsterStrength % strength;
2014-07-15 15:38:05 +03:00
2014-06-01 15:10:44 +03:00
placeObject ( gen , guard , pos ) ;
2014-07-09 23:09:06 +03:00
if ( clearSurroundingTiles )
{
//do not spawn anything near monster
gen - > foreach_neighbour ( pos , [ gen ] ( int3 pos )
{
if ( gen - > isPossible ( pos ) )
gen - > setOccupied ( pos , ETileType : : FREE ) ;
} ) ;
}
2014-06-04 21:59:01 +03:00
return true ;
2014-06-01 15:10:44 +03:00
}
2014-12-20 15:01:48 +02:00
bool CRmgTemplateZone : : createTreasurePile ( CMapGenerator * gen , int3 & pos , float minDistance , const CTreasureInfo & treasureInfo )
2014-06-04 11:16:08 +03:00
{
2014-07-08 21:13:51 +03:00
CTreasurePileInfo info ;
2014-06-05 18:19:11 +03:00
std : : map < int3 , CGObjectInstance * > treasures ;
std : : set < int3 > boundary ;
2014-06-13 13:04:17 +03:00
int3 guardPos ( - 1 , - 1 , - 1 ) ;
2014-07-08 21:13:51 +03:00
info . nextTreasurePos = pos ;
2014-06-04 16:16:23 +03:00
2014-12-20 15:01:48 +02:00
int maxValue = treasureInfo . max ;
int minValue = treasureInfo . min ;
2014-06-07 16:51:03 +03:00
2014-12-26 20:35:45 +02:00
ui32 desiredValue = ( gen - > rand . nextInt ( minValue , maxValue ) ) ;
2014-06-04 11:16:08 +03:00
int currentValue = 0 ;
CGObjectInstance * object = nullptr ;
2014-12-26 20:35:45 +02:00
while ( currentValue < = desiredValue - 100 ) //no objects with value below 100 are avaiable
2014-06-04 11:16:08 +03:00
{
2014-07-08 21:13:51 +03:00
treasures [ info . nextTreasurePos ] = nullptr ;
2014-07-09 10:22:50 +03:00
2014-12-26 20:35:45 +02:00
for ( auto treasurePos : treasures )
2014-06-05 18:19:11 +03:00
{
2014-12-26 20:35:45 +02:00
gen - > foreach_neighbour ( treasurePos . first , [ gen , & boundary ] ( int3 pos )
2014-06-05 18:19:11 +03:00
{
2014-12-26 20:35:45 +02:00
boundary . insert ( pos ) ;
} ) ;
}
for ( auto treasurePos : treasures )
{
//leaving only boundary around objects
vstd : : erase_if_present ( boundary , treasurePos . first ) ;
}
2014-07-09 10:22:50 +03:00
2014-12-26 20:35:45 +02:00
for ( auto tile : boundary )
{
//we can't extend boundary anymore
if ( ! ( gen - > isBlocked ( tile ) | | gen - > isPossible ( tile ) ) )
break ;
2014-06-05 18:19:11 +03:00
}
2014-09-22 14:36:55 +03:00
ObjectInfo oi = getRandomObject ( gen , info , desiredValue , maxValue , currentValue ) ;
2014-07-29 16:58:54 +03:00
if ( ! oi . value ) //0 value indicates no object
2014-07-08 21:13:51 +03:00
{
vstd : : erase_if_present ( treasures , info . nextTreasurePos ) ;
2014-06-04 20:08:04 +03:00
break ;
2014-07-08 21:13:51 +03:00
}
else
{
2014-07-29 16:58:54 +03:00
object = oi . generateObject ( ) ;
2014-07-15 23:33:51 +03:00
//remove from possible objects
auto oiptr = std : : find ( possibleObjects . begin ( ) , possibleObjects . end ( ) , oi ) ;
assert ( oiptr ! = possibleObjects . end ( ) ) ;
oiptr - > maxPerZone - - ;
2014-07-27 08:37:56 +03:00
if ( ! oiptr - > maxPerZone )
possibleObjects . erase ( oiptr ) ;
2014-07-15 23:33:51 +03:00
//TODO
2014-07-08 21:13:51 +03:00
//update treasure pile area
2014-07-09 13:05:14 +03:00
int3 visitablePos = info . nextTreasurePos ;
2014-07-08 21:13:51 +03:00
2014-07-09 10:22:50 +03:00
//TODO: actually we need to check is object is either !blockVisit or removable after visit - this means object below can be accessed
2014-07-08 21:13:51 +03:00
if ( oi . templ . isVisitableFromTop ( ) )
info . visitableFromTopPositions . insert ( visitablePos ) ; //can be accessed from any direction
2014-07-09 10:22:50 +03:00
else
info . visitableFromBottomPositions . insert ( visitablePos ) ; //can be accessed only from bottom or side
2014-07-08 21:13:51 +03:00
for ( auto blockedOffset : oi . templ . getBlockedOffsets ( ) )
2014-07-09 10:22:50 +03:00
{
2014-07-09 13:05:14 +03:00
int3 blockPos = info . nextTreasurePos + blockedOffset + oi . templ . getVisitableOffset ( ) ; //object will be moved to align vistable pos to treasure pos
info . occupiedPositions . insert ( blockPos ) ;
info . blockedPositions . insert ( blockPos ) ;
2014-07-09 10:22:50 +03:00
}
2014-07-08 21:13:51 +03:00
info . occupiedPositions . insert ( visitablePos ) ;
2014-06-04 20:08:04 +03:00
2014-07-29 16:58:54 +03:00
currentValue + = oi . value ;
2014-06-05 18:19:11 +03:00
2014-07-29 16:58:54 +03:00
treasures [ info . nextTreasurePos ] = object ;
2014-06-05 18:19:11 +03:00
2014-07-29 16:58:54 +03:00
//now find place for next object
int3 placeFound ( - 1 , - 1 , - 1 ) ;
2014-06-07 15:02:57 +03:00
2014-07-29 20:19:15 +03:00
//randomize next position from among possible ones
std : : vector < int3 > boundaryCopy ( boundary . begin ( ) , boundary . end ( ) ) ;
2014-11-22 15:17:53 +02:00
//RandomGeneratorUtil::randomShuffle(boundaryCopy, gen->rand);
auto chooseTopTile = [ ] ( const int3 & lhs , const int3 & rhs ) - > bool
{
return lhs . y < rhs . y ;
} ;
boost : : sort ( boundaryCopy , chooseTopTile ) ; //start from top tiles to allow objects accessible from bottom
2014-07-29 20:19:15 +03:00
for ( auto tile : boundaryCopy )
2014-06-05 18:19:11 +03:00
{
2014-07-29 16:58:54 +03:00
if ( gen - > isPossible ( tile ) ) //we can place new treasure only on possible tile
2014-06-05 18:19:11 +03:00
{
2014-07-29 16:58:54 +03:00
bool here = true ;
2014-09-22 15:18:01 +03:00
gen - > foreach_neighbour ( tile , [ gen , & here , minDistance ] ( int3 pos )
2014-07-29 16:58:54 +03:00
{
2014-09-22 15:18:01 +03:00
if ( ! ( gen - > isBlocked ( pos ) | | gen - > isPossible ( pos ) ) | | gen - > getNearestObjectDistance ( pos ) < minDistance )
2014-07-29 16:58:54 +03:00
here = false ;
} ) ;
if ( here )
{
placeFound = tile ;
break ;
}
2014-06-05 18:19:11 +03:00
}
}
2014-07-29 16:58:54 +03:00
if ( placeFound . valid ( ) )
info . nextTreasurePos = placeFound ;
2015-01-15 12:21:29 +02:00
else
break ; //no more place to add any objects
}
2014-06-04 20:08:04 +03:00
}
2014-07-08 21:13:51 +03:00
2014-11-22 14:01:28 +02:00
if ( treasures . size ( ) )
2014-06-04 20:08:04 +03:00
{
2014-07-09 10:22:50 +03:00
//find object closest to zone center, then connect it to the middle of the zone
2014-07-09 12:38:16 +03:00
int3 closestFreeTile ( - 1 , - 1 , - 1 ) ;
if ( info . visitableFromBottomPositions . size ( ) ) //get random treasure tile, starting from objects accessible only from bottom
closestFreeTile = findClosestTile ( freePaths , * RandomGeneratorUtil : : nextItem ( info . visitableFromBottomPositions , gen - > rand ) ) ;
else
closestFreeTile = findClosestTile ( freePaths , * RandomGeneratorUtil : : nextItem ( info . visitableFromTopPositions , gen - > rand ) ) ;
2014-06-13 09:00:26 +03:00
int3 closestTile = int3 ( - 1 , - 1 , - 1 ) ;
float minDistance = 1e10 ;
2014-07-09 10:22:50 +03:00
for ( auto visitablePos : info . visitableFromBottomPositions ) //objects that are not visitable from top must be accessible from bottom or side
2014-06-13 09:00:26 +03:00
{
2014-07-09 12:38:16 +03:00
if ( closestFreeTile . dist2d ( visitablePos ) < minDistance )
2014-06-13 09:00:26 +03:00
{
2014-07-09 17:39:17 +03:00
closestTile = visitablePos + int3 ( 0 , 1 , 0 ) ; //start below object (y+1), possibly even outside the map (?)
2014-07-09 12:38:16 +03:00
minDistance = closestFreeTile . dist2d ( visitablePos ) ;
2014-07-09 10:22:50 +03:00
}
}
if ( ! closestTile . valid ( ) )
{
for ( auto visitablePos : info . visitableFromTopPositions ) //all objects are accessible from any direction
{
2014-07-09 12:38:16 +03:00
if ( closestFreeTile . dist2d ( visitablePos ) < minDistance )
2014-07-09 10:22:50 +03:00
{
closestTile = visitablePos ;
2014-07-09 12:38:16 +03:00
minDistance = closestFreeTile . dist2d ( visitablePos ) ;
2014-07-09 10:22:50 +03:00
}
2014-06-13 09:00:26 +03:00
}
}
assert ( closestTile . valid ( ) ) ;
2014-07-09 10:22:50 +03:00
2014-07-09 12:38:16 +03:00
for ( auto tile : info . occupiedPositions )
2014-07-09 10:22:50 +03:00
{
if ( gen - > map - > isInTheMap ( tile ) ) //pile boundary may reach map border
2014-07-09 12:38:16 +03:00
gen - > setOccupied ( tile , ETileType : : BLOCKED ) ; //so that crunch path doesn't cut through objects
2014-07-09 10:22:50 +03:00
}
2014-07-09 12:38:16 +03:00
if ( ! crunchPath ( gen , closestTile , closestFreeTile , id ) )
2014-06-13 13:04:17 +03:00
{
2014-07-09 12:38:16 +03:00
//we can't connect this pile, just block it off and start over
2014-06-13 13:04:17 +03:00
for ( auto treasure : treasures )
{
if ( gen - > isPossible ( treasure . first ) )
gen - > setOccupied ( treasure . first , ETileType : : BLOCKED ) ;
}
return true ;
}
2014-06-13 09:00:26 +03:00
2014-07-09 15:27:12 +03:00
//update boundary around our objects, including knowledge about objects visitable from bottom
boundary . clear ( ) ;
2014-12-26 17:17:39 +02:00
2014-12-26 20:35:45 +02:00
for ( auto tile : info . visitableFromBottomPositions )
2014-07-09 15:27:12 +03:00
{
2014-12-26 20:35:45 +02:00
gen - > foreach_neighbour ( tile , [ tile , & boundary ] ( int3 pos )
2014-07-09 15:27:12 +03:00
{
2014-12-26 20:35:45 +02:00
if ( pos . y > = tile . y ) //don't block these objects from above
2014-12-26 17:17:39 +02:00
boundary . insert ( pos ) ;
2014-12-26 20:35:45 +02:00
} ) ;
}
for ( auto tile : info . visitableFromTopPositions )
{
gen - > foreach_neighbour ( tile , [ & boundary ] ( int3 pos )
{
boundary . insert ( pos ) ;
} ) ;
}
2014-07-09 15:27:12 +03:00
2014-12-26 22:13:13 +02:00
bool isPileGuarded = currentValue > = minGuardedValue ;
2014-12-26 20:35:45 +02:00
for ( auto tile : boundary ) //guard must be standing there
{
if ( gen - > isFree ( tile ) ) //this tile could be already blocked, don't place a monster here
2014-06-05 22:37:39 +03:00
{
2014-12-26 20:35:45 +02:00
guardPos = tile ;
break ;
2014-06-12 22:51:16 +03:00
}
2014-12-26 20:35:45 +02:00
}
2014-06-05 22:37:39 +03:00
2014-12-26 20:35:45 +02:00
if ( guardPos . valid ( ) )
{
for ( auto treasure : treasures )
2014-06-13 13:04:17 +03:00
{
2014-12-26 20:35:45 +02:00
int3 visitableOffset = treasure . second - > getVisitableOffset ( ) ;
placeObject ( gen , treasure . second , treasure . first + visitableOffset ) ;
}
if ( addMonster ( gen , guardPos , currentValue , false ) )
{ //block only if the object is guarded
for ( auto tile : boundary )
2014-06-13 13:04:17 +03:00
{
2014-12-26 20:35:45 +02:00
if ( gen - > isPossible ( tile ) )
gen - > setOccupied ( tile , ETileType : : BLOCKED ) ;
2014-06-13 13:04:17 +03:00
}
2014-12-26 20:35:45 +02:00
//do not spawn anything near monster
gen - > foreach_neighbour ( guardPos , [ gen ] ( int3 pos )
{
if ( gen - > isPossible ( pos ) )
gen - > setOccupied ( pos , ETileType : : FREE ) ;
} ) ;
2014-06-13 13:04:17 +03:00
}
2014-12-26 22:13:13 +02:00
else //mo monster in this pile, make some free space (needed?)
{
for ( auto tile : boundary )
if ( gen - > isPossible ( tile ) )
gen - > setOccupied ( tile , ETileType : : FREE ) ;
}
2014-12-26 20:35:45 +02:00
}
2014-12-26 22:13:13 +02:00
else if ( isPileGuarded ) //we couldn't make a connection to this location, block it
2014-12-26 20:35:45 +02:00
{
for ( auto treasure : treasures )
2014-06-05 18:19:11 +03:00
{
2014-12-26 20:35:45 +02:00
if ( gen - > isPossible ( treasure . first ) )
gen - > setOccupied ( treasure . first , ETileType : : BLOCKED ) ;
2014-07-29 16:58:54 +03:00
2014-12-26 20:35:45 +02:00
delete treasure . second ;
2014-06-05 18:19:11 +03:00
}
}
2014-06-13 13:04:17 +03:00
2014-06-04 20:08:04 +03:00
return true ;
}
else //we did not place eveyrthing successfully
return false ;
}
2014-06-14 18:14:59 +03:00
void CRmgTemplateZone : : initTownType ( CMapGenerator * gen )
2014-06-01 13:02:43 +03:00
{
2014-06-15 22:23:32 +03:00
//FIXME: handle case that this player is not present -> towns should be set to neutral
int totalTowns = 0 ;
2014-09-23 22:12:10 +03:00
auto cutPathAroundTown = [ gen , this ] ( const CGTownInstance * town )
{
//cut contour around town in case it was placed in a middle of path. TODO: find better solution
for ( auto tile : town - > getBlockedPos ( ) )
{
gen - > foreach_neighbour ( tile , [ gen , & tile ] ( int3 & pos )
{
if ( gen - > isPossible ( pos ) )
{
gen - > setOccupied ( pos , ETileType : : FREE ) ;
}
} ) ;
}
} ;
auto addNewTowns = [ & totalTowns , gen , this , & cutPathAroundTown ] ( int count , bool hasFort , PlayerColor player )
2014-07-03 10:39:26 +03:00
{
for ( int i = 0 ; i < count ; i + + )
{
2014-09-23 22:12:10 +03:00
auto town = new CGTownInstance ( ) ;
2014-07-03 10:39:26 +03:00
town - > ID = Obj : : TOWN ;
if ( this - > townsAreSameType )
town - > subID = townType ;
else
2014-10-30 14:03:53 +02:00
{
if ( townTypes . size ( ) )
town - > subID = * RandomGeneratorUtil : : nextItem ( townTypes , gen - > rand ) ;
else
town - > subID = * RandomGeneratorUtil : : nextItem ( getDefaultTownTypes ( ) , gen - > rand ) ; //it is possible to have zone with no towns allowed
}
2014-07-03 10:39:26 +03:00
town - > tempOwner = player ;
if ( hasFort )
town - > builtBuildings . insert ( BuildingID : : FORT ) ;
town - > builtBuildings . insert ( BuildingID : : DEFAULT ) ;
2014-10-26 14:09:59 +02:00
for ( auto spell : VLC - > spellh - > objects ) //add all regular spells to town
{
if ( ! spell - > isSpecialSpell ( ) & & ! spell - > isCreatureAbility ( ) )
town - > possibleSpells . push_back ( spell - > id ) ;
}
2014-07-07 12:29:16 +03:00
if ( ! totalTowns )
{
//first town in zone sets the facton of entire zone
town - > subID = townType ;
2014-07-07 17:20:48 +03:00
//register MAIN town of zone
gen - > registerZone ( town - > subID ) ;
2014-07-07 12:29:16 +03:00
//first town in zone goes in the middle
2014-07-08 14:17:47 +03:00
placeAndGuardObject ( gen , town , getPos ( ) + town - > getVisitableOffset ( ) , 0 ) ;
2014-09-23 22:12:10 +03:00
cutPathAroundTown ( town ) ;
2014-07-07 12:29:16 +03:00
}
2014-07-03 10:39:26 +03:00
else
addRequiredObject ( town ) ;
totalTowns + + ;
}
} ;
2014-05-22 20:25:17 +03:00
if ( ( type = = ETemplateZoneType : : CPU_START ) | | ( type = = ETemplateZoneType : : PLAYER_START ) )
{
2014-06-15 22:23:32 +03:00
//set zone types to player faction, generate main town
2014-05-22 20:25:17 +03:00
logGlobal - > infoStream ( ) < < " Preparing playing zone " ;
int player_id = * owner - 1 ;
auto & playerInfo = gen - > map - > players [ player_id ] ;
if ( playerInfo . canAnyonePlay ( ) )
{
PlayerColor player ( player_id ) ;
2014-06-14 18:14:59 +03:00
townType = gen - > mapGenOptions - > getPlayersSettings ( ) . find ( player ) - > second . getStartingTown ( ) ;
2014-05-22 20:25:17 +03:00
2014-10-30 11:00:29 +02:00
if ( townType = = CMapGenOptions : : CPlayerSettings : : RANDOM_TOWN )
2014-10-30 14:03:53 +02:00
{
if ( townTypes . size ( ) )
townType = * RandomGeneratorUtil : : nextItem ( townTypes , gen - > rand ) ;
else
townType = * RandomGeneratorUtil : : nextItem ( getDefaultTownTypes ( ) , gen - > rand ) ; //it is possible to have zone with no towns allowed
}
2014-06-15 22:23:32 +03:00
auto town = new CGTownInstance ( ) ;
town - > ID = Obj : : TOWN ;
2014-05-22 20:25:17 +03:00
2014-06-14 18:14:59 +03:00
town - > subID = townType ;
2014-05-22 20:25:17 +03:00
town - > tempOwner = player ;
town - > builtBuildings . insert ( BuildingID : : FORT ) ;
town - > builtBuildings . insert ( BuildingID : : DEFAULT ) ;
2014-10-26 14:09:59 +02:00
for ( auto spell : VLC - > spellh - > objects ) //add all regular spells to town
{
if ( ! spell - > isSpecialSpell ( ) & & ! spell - > isCreatureAbility ( ) )
town - > possibleSpells . push_back ( spell - > id ) ;
}
2014-07-08 14:17:47 +03:00
//towns are big objects and should be centered around visitable position
placeAndGuardObject ( gen , town , getPos ( ) + town - > getVisitableOffset ( ) , 0 ) ; //generate no guards, but free path to entrance
2014-09-23 22:12:10 +03:00
cutPathAroundTown ( town ) ;
2014-05-22 20:25:17 +03:00
2014-06-15 22:23:32 +03:00
totalTowns + + ;
2014-07-07 17:20:48 +03:00
//register MAIN town of zone only
gen - > registerZone ( town - > subID ) ;
2014-06-15 22:23:32 +03:00
2014-05-24 19:39:58 +03:00
logGlobal - > traceStream ( ) < < " Fill player info " < < player_id ;
2014-06-05 18:19:11 +03:00
2014-05-22 20:25:17 +03:00
// Update player info
playerInfo . allowedFactions . clear ( ) ;
2014-06-14 18:14:59 +03:00
playerInfo . allowedFactions . insert ( townType ) ;
2014-05-22 20:25:17 +03:00
playerInfo . hasMainTown = true ;
2014-07-01 08:07:40 +03:00
playerInfo . posOfMainTown = town - > pos - town - > getVisitableOffset ( ) ;
2014-05-22 20:25:17 +03:00
playerInfo . generateHeroAtMainTown = true ;
2014-06-15 22:23:32 +03:00
//now create actual towns
2014-07-03 10:39:26 +03:00
addNewTowns ( playerTowns . getCastleCount ( ) - 1 , true , player ) ;
addNewTowns ( playerTowns . getTownCount ( ) , false , player ) ;
2014-06-15 22:23:32 +03:00
2014-06-01 13:02:43 +03:00
//requiredObjects.push_back(town);
2014-05-22 20:25:17 +03:00
}
else
{
type = ETemplateZoneType : : TREASURE ;
2014-10-31 18:09:34 +02:00
if ( townTypes . size ( ) )
townType = * RandomGeneratorUtil : : nextItem ( townTypes , gen - > rand ) ;
else
townType = * RandomGeneratorUtil : : nextItem ( getDefaultTownTypes ( ) , gen - > rand ) ; //it is possible to have zone with no towns allowed
2014-05-22 20:25:17 +03:00
}
}
2014-05-24 15:33:22 +03:00
else //no player
{
2014-10-31 18:09:34 +02:00
if ( townTypes . size ( ) )
townType = * RandomGeneratorUtil : : nextItem ( townTypes , gen - > rand ) ;
else
townType = * RandomGeneratorUtil : : nextItem ( getDefaultTownTypes ( ) , gen - > rand ) ; //it is possible to have zone with no towns allowed
2014-05-24 15:33:22 +03:00
}
2014-06-15 22:23:32 +03:00
2014-07-03 10:39:26 +03:00
addNewTowns ( neutralTowns . getCastleCount ( ) , true , PlayerColor : : NEUTRAL ) ;
addNewTowns ( neutralTowns . getTownCount ( ) , false , PlayerColor : : NEUTRAL ) ;
2014-10-30 14:03:53 +02:00
if ( ! totalTowns ) //if there's no town present, get random faction for dwellings and pandoras
{
//25% chance for neutral
if ( gen - > rand . nextInt ( 1 , 100 ) < = 25 )
{
townType = ETownType : : NEUTRAL ;
}
else
{
if ( townTypes . size ( ) )
townType = * RandomGeneratorUtil : : nextItem ( townTypes , gen - > rand ) ;
else if ( monsterTypes . size ( ) )
townType = * RandomGeneratorUtil : : nextItem ( monsterTypes , gen - > rand ) ; //this happens in Clash of Dragons in treasure zones, where all towns are banned
}
}
2014-06-14 18:14:59 +03:00
}
void CRmgTemplateZone : : initTerrainType ( CMapGenerator * gen )
{
2014-10-30 14:03:53 +02:00
if ( matchTerrainToTown & & townType ! = ETownType : : NEUTRAL )
2014-06-14 18:14:59 +03:00
terrainType = VLC - > townh - > factions [ townType ] - > nativeTerrain ;
else
terrainType = * RandomGeneratorUtil : : nextItem ( terrainTypes , gen - > rand ) ;
2014-07-04 11:45:00 +03:00
//TODO: allow new types of terrain?
if ( pos . z )
{
if ( terrainType ! = ETerrainType : : LAVA )
terrainType = ETerrainType : : SUBTERRANEAN ;
}
else
{
if ( terrainType = = ETerrainType : : SUBTERRANEAN )
terrainType = ETerrainType : : DIRT ;
}
2014-07-03 18:24:28 +03:00
paintZoneTerrain ( gen , terrainType ) ;
}
void CRmgTemplateZone : : paintZoneTerrain ( CMapGenerator * gen , ETerrainType terrainType )
{
2014-05-24 15:33:22 +03:00
std : : vector < int3 > tiles ;
for ( auto tile : tileinfo )
{
2014-05-30 22:23:41 +03:00
tiles . push_back ( tile ) ;
2014-05-24 15:33:22 +03:00
}
gen - > editManager - > getTerrainSelection ( ) . setSelection ( tiles ) ;
2014-06-14 18:14:59 +03:00
gen - > editManager - > drawTerrain ( terrainType , & gen - > rand ) ;
}
2014-06-14 21:05:19 +03:00
bool CRmgTemplateZone : : placeMines ( CMapGenerator * gen )
{
std : : vector < Res : : ERes > required_mines ;
required_mines . push_back ( Res : : ERes : : WOOD ) ;
required_mines . push_back ( Res : : ERes : : ORE ) ;
static const Res : : ERes woodOre [ ] = { Res : : ERes : : WOOD , Res : : ERes : : ORE } ;
static const Res : : ERes preciousResources [ ] = { Res : : ERes : : GEMS , Res : : ERes : : CRYSTAL , Res : : ERes : : MERCURY , Res : : ERes : : SULFUR } ;
for ( const auto & res : woodOre )
{
for ( int i = 0 ; i < mines [ res ] ; i + + )
{
auto mine = new CGMine ( ) ;
mine - > ID = Obj : : MINE ;
mine - > subID = static_cast < si32 > ( res ) ;
mine - > producedResource = res ;
mine - > producedQuantity = mine - > defaultResProduction ( ) ;
2014-12-20 23:13:10 +02:00
addCloseObject ( mine , 1500 ) ;
2014-06-14 21:05:19 +03:00
}
}
for ( const auto & res : preciousResources )
{
for ( int i = 0 ; i < mines [ res ] ; i + + )
{
auto mine = new CGMine ( ) ;
mine - > ID = Obj : : MINE ;
mine - > subID = static_cast < si32 > ( res ) ;
mine - > producedResource = res ;
mine - > producedQuantity = mine - > defaultResProduction ( ) ;
addRequiredObject ( mine , 3500 ) ;
}
}
for ( int i = 0 ; i < mines [ Res : : GOLD ] ; i + + )
{
auto mine = new CGMine ( ) ;
mine - > ID = Obj : : MINE ;
mine - > subID = static_cast < si32 > ( Res : : GOLD ) ;
mine - > producedResource = Res : : GOLD ;
mine - > producedQuantity = mine - > defaultResProduction ( ) ;
addRequiredObject ( mine , 7000 ) ;
}
return true ;
}
2014-06-15 09:48:46 +03:00
bool CRmgTemplateZone : : createRequiredObjects ( CMapGenerator * gen )
2014-06-14 18:14:59 +03:00
{
2014-11-01 10:52:56 +02:00
logGlobal - > traceStream ( ) < < " Creating required objects " ;
2014-06-01 13:02:43 +03:00
for ( const auto & obj : requiredObjects )
2014-05-22 20:25:17 +03:00
{
int3 pos ;
2014-06-01 17:31:15 +03:00
if ( ! findPlaceForObject ( gen , obj . first , 3 , pos ) )
2014-05-22 20:25:17 +03:00
{
2014-05-23 18:12:31 +03:00
logGlobal - > errorStream ( ) < < boost : : format ( " Failed to fill zone %d due to lack of space " ) % id ;
2014-05-22 20:25:17 +03:00
//TODO CLEANUP!
return false ;
}
2014-07-15 15:38:05 +03:00
placeObject ( gen , obj . first , pos ) ;
2014-07-25 17:26:50 +03:00
guardObject ( gen , obj . first , obj . second , ( obj . first - > ID = = Obj : : MONOLITH_TWO_WAY ) , true ) ;
//paths to required objects constitute main paths of zone. otherwise they just may lead to middle and create dead zones
2014-05-22 20:25:17 +03:00
}
2014-12-20 23:13:10 +02:00
for ( const auto & obj : closeObjects )
{
std : : vector < int3 > tiles ( possibleTiles . begin ( ) , possibleTiles . end ( ) ) ; //new tiles vector after each object has been placed
// smallest distance to zone center, greatest distance to nearest object
auto isCloser = [ this , gen ] ( const int3 & lhs , const int3 & rhs ) - > bool
{
return ( this - > pos . dist2dSQ ( lhs ) * 0.5f - gen - > getNearestObjectDistance ( lhs ) ) < ( this - > pos . dist2dSQ ( rhs ) * 0.5f - gen - > getNearestObjectDistance ( rhs ) ) ;
} ;
boost : : sort ( tiles , isCloser ) ;
setTemplateForObject ( gen , obj . first ) ;
auto tilesBlockedByObject = obj . first - > getBlockedOffsets ( ) ;
bool result = false ;
for ( auto tile : tiles )
{
//object must be accessible from at least one surounding tile
if ( ! isAccessibleFromAnywhere ( gen , obj . first - > appearance , tile , tilesBlockedByObject ) )
continue ;
//avoid borders
if ( gen - > isPossible ( tile ) )
{
if ( areAllTilesAvailable ( gen , obj . first , tile , tilesBlockedByObject ) )
{
placeObject ( gen , obj . first , tile ) ;
guardObject ( gen , obj . first , obj . second , ( obj . first - > ID = = Obj : : MONOLITH_TWO_WAY ) , true ) ;
result = true ;
break ;
}
}
}
if ( ! result )
{
logGlobal - > errorStream ( ) < < boost : : format ( " Failed to fill zone %d due to lack of space " ) % id ;
//TODO CLEANUP!
return false ;
}
}
2014-06-15 09:48:46 +03:00
return true ;
}
void CRmgTemplateZone : : createTreasures ( CMapGenerator * gen )
{
2014-12-26 17:17:39 +02:00
int mapMonsterStrength = gen - > mapGenOptions - > getMonsterStrength ( ) ;
int monsterStrength = zoneMonsterStrength + mapMonsterStrength - 1 ; //array index from 0 to 4
static int minGuardedValues [ ] = { 6500 , 4167 , 3000 , 1833 , 1333 } ;
minGuardedValue = minGuardedValues [ monsterStrength ] ;
2014-12-20 15:01:48 +02:00
auto valueComparator = [ ] ( const CTreasureInfo & lhs , const CTreasureInfo & rhs ) - > bool
{
return lhs . max > rhs . max ;
} ;
2014-07-30 14:52:21 +03:00
2014-12-20 15:01:48 +02:00
//place biggest treasures first at large distance, place smaller ones inbetween
boost : : sort ( treasureInfo , valueComparator ) ;
2014-06-04 11:16:08 +03:00
2014-12-20 15:01:48 +02:00
int totalDensity = 0 ;
for ( auto t : treasureInfo )
{
totalDensity + = t . density ;
2014-07-24 20:16:49 +03:00
2014-12-20 15:01:48 +02:00
//treasure density is inversely proportional to zone size but must be scaled back to map size
//also, normalize it to zone count - higher count means relatively smaller zones
//this is squared distance for optimization purposes
2014-12-26 20:35:45 +02:00
const double minDistance = std : : max < float > ( ( 125.f / totalDensity ) , 2 ) ;
2014-12-20 15:01:48 +02:00
//distance lower than 2 causes objects to overlap and crash
2014-05-23 20:14:20 +03:00
2014-12-20 15:01:48 +02:00
do {
//optimization - don't check tiles which are not allowed
vstd : : erase_if ( possibleTiles , [ gen ] ( const int3 & tile ) - > bool
{
return ! gen - > isPossible ( tile ) ;
} ) ;
int3 pos ;
2014-12-26 20:35:45 +02:00
//If we are able to place at least one object with value lower than minGuardedValue, it's ok
if ( ! findPlaceForTreasurePile ( gen , minDistance , pos , t . min ) )
2014-12-20 15:01:48 +02:00
{
break ;
}
createTreasurePile ( gen , pos , minDistance , t ) ;
} while ( true ) ;
}
2014-06-15 09:48:46 +03:00
}
2014-05-22 20:25:17 +03:00
2014-06-15 09:48:46 +03:00
void CRmgTemplateZone : : createObstacles ( CMapGenerator * gen )
2014-09-23 17:53:26 +03:00
{
2014-09-22 16:27:42 +03:00
//tighten obstacles to improve visuals
2014-09-23 17:53:26 +03:00
for ( int i = 0 ; i < 3 ; + + i )
2014-09-22 16:27:42 +03:00
{
2014-09-23 17:53:26 +03:00
int blockedTiles = 0 ;
int freeTiles = 0 ;
2014-09-22 16:27:42 +03:00
2014-09-23 17:53:26 +03:00
for ( auto tile : tileinfo )
2014-09-22 16:27:42 +03:00
{
2014-09-23 17:53:26 +03:00
if ( ! gen - > isPossible ( tile ) ) //only possible tiles can change
continue ;
int blockedNeighbours = 0 ;
int freeNeighbours = 0 ;
gen - > foreach_neighbour ( tile , [ gen , & blockedNeighbours , & freeNeighbours ] ( int3 & pos )
{
if ( gen - > isBlocked ( pos ) )
blockedNeighbours + + ;
if ( gen - > isFree ( pos ) )
freeNeighbours + + ;
} ) ;
if ( blockedNeighbours > 4 )
{
gen - > setOccupied ( tile , ETileType : : BLOCKED ) ;
blockedTiles + + ;
}
else if ( freeNeighbours > 4 )
{
gen - > setOccupied ( tile , ETileType : : FREE ) ;
freeTiles + + ;
}
}
2014-11-01 10:52:56 +02:00
logGlobal - > traceStream ( ) < < boost : : format ( " Set %d tiles to BLOCKED and %d tiles to FREE " ) % blockedTiles % freeTiles ;
2014-09-22 16:27:42 +03:00
}
2014-10-31 19:47:10 +02:00
# define MAKE_COOL_UNDERGROUND_TUNNELS false
if ( pos . z & & MAKE_COOL_UNDERGROUND_TUNNELS ) //underground
2014-07-04 00:11:24 +03:00
{
std : : vector < int3 > rockTiles ;
for ( auto tile : tileinfo )
2014-07-04 10:54:55 +03:00
{
2014-07-04 00:11:24 +03:00
if ( gen - > shouldBeBlocked ( tile ) )
{
2014-07-04 10:54:55 +03:00
bool placeRock = true ;
2014-07-04 00:11:24 +03:00
gen - > foreach_neighbour ( tile , [ gen , & placeRock ] ( int3 & pos )
{
if ( ! ( gen - > shouldBeBlocked ( pos ) | | gen - > isPossible ( pos ) ) )
placeRock = false ;
} ) ;
if ( placeRock )
{
rockTiles . push_back ( tile ) ;
}
}
}
gen - > editManager - > getTerrainSelection ( ) . setSelection ( rockTiles ) ;
gen - > editManager - > drawTerrain ( ETerrainType : : ROCK , & gen - > rand ) ;
2014-09-22 16:27:42 +03:00
for ( auto tile : rockTiles )
{
gen - > setOccupied ( tile , ETileType : : USED ) ; //don't place obstacles in a rock
//gen->foreach_neighbour (tile, [gen](int3 &pos)
//{
// if (!gen->isUsed(pos))
// gen->setOccupied (pos, ETileType::BLOCKED);
//});
}
2014-07-04 00:11:24 +03:00
}
2014-07-25 18:55:48 +03:00
typedef std : : vector < ObjectTemplate > obstacleVector ;
//obstacleVector possibleObstacles;
std : : map < ui8 , obstacleVector > obstaclesBySize ;
typedef std : : pair < ui8 , obstacleVector > obstaclePair ;
std : : vector < obstaclePair > possibleObstacles ;
2014-07-04 00:11:24 +03:00
2014-06-28 10:46:32 +03:00
//get all possible obstacles for this terrain
for ( auto primaryID : VLC - > objtypeh - > knownObjects ( ) )
{
for ( auto secondaryID : VLC - > objtypeh - > knownSubObjects ( primaryID ) )
{
auto handler = VLC - > objtypeh - > getHandlerFor ( primaryID , secondaryID ) ;
if ( handler - > isStaticObject ( ) )
{
for ( auto temp : handler - > getTemplates ( ) )
{
if ( temp . canBePlacedAt ( terrainType ) & & temp . getBlockMapOffset ( ) . valid ( ) )
2014-07-25 18:55:48 +03:00
obstaclesBySize [ temp . getBlockedOffsets ( ) . size ( ) ] . push_back ( temp ) ;
2014-06-28 10:46:32 +03:00
}
}
}
}
2014-07-25 18:55:48 +03:00
for ( auto o : obstaclesBySize )
{
possibleObstacles . push_back ( std : : make_pair ( o . first , o . second ) ) ;
}
2014-07-26 09:12:45 +03:00
boost : : sort ( possibleObstacles , [ ] ( const obstaclePair & p1 , const obstaclePair & p2 ) - > bool
2014-07-25 18:55:48 +03:00
{
return p1 . first > p2 . first ; //bigger obstacles first
} ) ;
2014-06-28 10:46:32 +03:00
2014-05-22 20:25:17 +03:00
auto sel = gen - > editManager - > getTerrainSelection ( ) ;
sel . clearSelection ( ) ;
2014-06-28 10:46:32 +03:00
2014-07-25 18:55:48 +03:00
auto tryToPlaceObstacleHere = [ this , gen , & possibleObstacles ] ( int3 & tile , int index ) - > bool
2014-05-22 20:25:17 +03:00
{
2014-07-25 18:55:48 +03:00
auto temp = * RandomGeneratorUtil : : nextItem ( possibleObstacles [ index ] . second , gen - > rand ) ;
2014-07-08 09:59:13 +03:00
int3 obstaclePos = tile - temp . getBlockMapOffset ( ) ;
2014-06-28 10:46:32 +03:00
if ( canObstacleBePlacedHere ( gen , temp , obstaclePos ) ) //can be placed here
{
auto obj = VLC - > objtypeh - > getHandlerFor ( temp . id , temp . subid ) - > create ( temp ) ;
2014-07-24 20:16:49 +03:00
placeObject ( gen , obj , obstaclePos , false ) ;
2014-06-28 10:46:32 +03:00
return true ;
}
return false ;
} ;
2014-05-31 13:26:37 +03:00
2014-07-25 19:52:24 +03:00
//reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left
for ( auto tile : boost : : adaptors : : reverse ( tileinfo ) )
2014-06-28 10:46:32 +03:00
{
2014-07-25 19:52:24 +03:00
//fill tiles that should be blocked with obstacles or are just possible (with some probability)
2014-07-26 09:12:45 +03:00
if ( gen - > shouldBeBlocked ( tile ) | | ( gen - > isPossible ( tile ) & & gen - > rand . nextInt ( 1 , 100 ) < 60 ) )
2014-05-22 20:25:17 +03:00
{
2014-07-25 18:55:48 +03:00
//start from biggets obstacles
for ( int i = 0 ; i < possibleObstacles . size ( ) ; i + + )
{
if ( tryToPlaceObstacleHere ( tile , i ) )
break ;
}
2014-06-28 10:46:32 +03:00
}
2014-05-22 20:25:17 +03:00
}
2014-06-15 09:48:46 +03:00
}
bool CRmgTemplateZone : : fill ( CMapGenerator * gen )
{
initTerrainType ( gen ) ;
2014-07-15 20:52:58 +03:00
freePaths . insert ( pos ) ; //zone center should be always clear to allow other tiles to connect
2014-07-06 23:14:37 +03:00
addAllPossibleObjects ( gen ) ;
2014-07-24 20:16:49 +03:00
2014-06-15 09:48:46 +03:00
placeMines ( gen ) ;
createRequiredObjects ( gen ) ;
fractalize ( gen ) ; //after required objects are created and linked with their own paths
createTreasures ( gen ) ;
createObstacles ( gen ) ;
2014-05-23 18:12:31 +03:00
logGlobal - > infoStream ( ) < < boost : : format ( " Zone %d filled successfully " ) % id ;
2014-05-22 20:25:17 +03:00
return true ;
}
2014-12-26 17:17:39 +02:00
bool CRmgTemplateZone : : findPlaceForTreasurePile ( CMapGenerator * gen , float min_dist , int3 & pos , int value )
2014-06-07 10:09:50 +03:00
{
2014-07-25 18:10:16 +03:00
float best_distance = 0 ;
2014-06-07 10:09:50 +03:00
bool result = false ;
2014-12-26 17:17:39 +02:00
bool needsGuard = value > minGuardedValue ;
2014-06-07 10:09:50 +03:00
//logGlobal->infoStream() << boost::format("Min dist for density %f is %d") % density % min_dist;
2014-07-24 20:16:49 +03:00
for ( auto tile : possibleTiles )
2014-06-07 10:09:50 +03:00
{
2014-09-22 15:18:01 +03:00
auto dist = gen - > getNearestObjectDistance ( tile ) ;
2014-06-07 10:09:50 +03:00
2014-07-24 20:16:49 +03:00
if ( ( dist > = min_dist ) & & ( dist > best_distance ) )
2014-06-07 10:09:50 +03:00
{
bool allTilesAvailable = true ;
2014-12-26 17:17:39 +02:00
gen - > foreach_neighbour ( tile , [ & gen , & allTilesAvailable , needsGuard ] ( int3 neighbour )
2014-06-07 10:09:50 +03:00
{
2014-12-26 17:17:39 +02:00
if ( ! ( gen - > isPossible ( neighbour ) | | gen - > shouldBeBlocked ( neighbour ) | | ( ! needsGuard & & gen - > isFree ( neighbour ) ) ) )
2014-06-07 10:09:50 +03:00
{
allTilesAvailable = false ; //all present tiles must be already blocked or ready for new objects
}
} ) ;
if ( allTilesAvailable )
{
best_distance = dist ;
pos = tile ;
result = true ;
}
}
}
if ( result )
{
2014-12-26 17:17:39 +02:00
gen - > setOccupied ( pos , ETileType : : BLOCKED ) ; //block that tile //FIXME: why?
2014-06-07 10:09:50 +03:00
}
return result ;
}
2014-06-28 10:46:32 +03:00
bool CRmgTemplateZone : : canObstacleBePlacedHere ( CMapGenerator * gen , ObjectTemplate & temp , int3 & pos )
{
2014-07-08 09:59:13 +03:00
if ( ! gen - > map - > isInTheMap ( pos ) ) //blockmap may fit in the map, but botom-right corner does not
return false ;
2014-06-28 10:46:32 +03:00
auto tilesBlockedByObject = temp . getBlockedOffsets ( ) ;
for ( auto blockingTile : tilesBlockedByObject )
{
int3 t = pos + blockingTile ;
if ( ! gen - > map - > isInTheMap ( t ) | | ! ( gen - > isPossible ( t ) | | gen - > shouldBeBlocked ( t ) ) )
{
2014-07-08 09:59:13 +03:00
return false ; //if at least one tile is not possible, object can't be placed here
2014-06-28 10:46:32 +03:00
}
}
2014-07-08 09:59:13 +03:00
return true ;
2014-06-28 10:46:32 +03:00
}
2014-07-15 23:33:51 +03:00
bool CRmgTemplateZone : : isAccessibleFromAnywhere ( CMapGenerator * gen , ObjectTemplate & appearance , int3 & tile , const std : : set < int3 > & tilesBlockedByObject ) const
2014-07-09 12:38:16 +03:00
{
bool accessible = false ;
for ( int x = - 1 ; x < 2 ; x + + )
{
for ( int y = - 1 ; y < 2 ; y + + )
{
if ( x & & y ) //check only if object is visitable from another tile
{
int3 offset = appearance . getVisitableOffset ( ) + int3 ( x , y , 0 ) ;
if ( ! vstd : : contains ( tilesBlockedByObject , offset ) )
{
int3 nearbyPos = tile + offset ;
if ( gen - > map - > isInTheMap ( nearbyPos ) )
{
if ( appearance . isVisitableFrom ( x , y ) & & ! gen - > isBlocked ( nearbyPos ) )
accessible = true ;
}
}
}
} ;
}
return accessible ;
}
2014-12-20 23:13:10 +02:00
void CRmgTemplateZone : : setTemplateForObject ( CMapGenerator * gen , CGObjectInstance * obj )
2014-05-22 20:25:17 +03:00
{
2014-05-31 13:26:37 +03:00
if ( obj - > appearance . id = = Obj : : NO_OBJ )
{
2014-06-03 22:45:18 +03:00
auto templates = VLC - > objtypeh - > getHandlerFor ( obj - > ID , obj - > subID ) - > getTemplates ( gen - > map - > getTile ( getPos ( ) ) . terType ) ;
2014-05-31 13:26:37 +03:00
if ( templates . empty ( ) )
2014-12-20 23:13:10 +02:00
throw rmgException ( boost : : to_string ( boost : : format ( " Did not find graphics for object (%d,%d) at % s " ) % obj->ID %obj->subID %pos)) ;
2014-05-31 13:26:37 +03:00
obj - > appearance = templates . front ( ) ;
}
2014-12-20 23:13:10 +02:00
}
bool CRmgTemplateZone : : areAllTilesAvailable ( CMapGenerator * gen , CGObjectInstance * obj , int3 & tile , std : : set < int3 > & tilesBlockedByObject ) const
{
for ( auto blockingTile : tilesBlockedByObject )
{
int3 t = tile + blockingTile ;
if ( ! gen - > map - > isInTheMap ( t ) | | ! gen - > isPossible ( t ) )
{
//if at least one tile is not possible, object can't be placed here
return false ;
}
}
return true ;
}
bool CRmgTemplateZone : : findPlaceForObject ( CMapGenerator * gen , CGObjectInstance * obj , si32 min_dist , int3 & pos )
{
//we need object apperance to deduce free tile
setTemplateForObject ( gen , obj ) ;
2014-05-31 13:26:37 +03:00
2014-05-22 20:25:17 +03:00
//si32 min_dist = sqrt(tileinfo.size()/density);
int best_distance = 0 ;
bool result = false ;
2014-06-22 17:26:08 +03:00
//si32 w = gen->map->width;
//si32 h = gen->map->height;
2014-05-31 15:11:20 +03:00
2014-05-22 20:25:17 +03:00
//logGlobal->infoStream() << boost::format("Min dist for density %f is %d") % density % min_dist;
2014-06-07 11:13:59 +03:00
auto tilesBlockedByObject = obj - > getBlockedOffsets ( ) ;
for ( auto tile : tileinfo )
2014-05-22 20:25:17 +03:00
{
2014-06-07 11:13:59 +03:00
//object must be accessible from at least one surounding tile
2014-07-09 12:38:16 +03:00
if ( ! isAccessibleFromAnywhere ( gen , obj - > appearance , tile , tilesBlockedByObject ) )
2014-06-07 11:13:59 +03:00
continue ;
2014-05-31 15:11:20 +03:00
auto ti = gen - > getTile ( tile ) ;
2014-05-22 20:25:17 +03:00
auto dist = ti . getNearestObjectDistance ( ) ;
//avoid borders
2014-05-30 22:23:41 +03:00
if ( gen - > isPossible ( tile ) & & ( dist > = min_dist ) & & ( dist > best_distance ) )
2014-05-22 20:25:17 +03:00
{
2014-12-20 23:13:10 +02:00
if ( areAllTilesAvailable ( gen , obj , tile , tilesBlockedByObject ) )
2014-05-31 11:56:14 +03:00
{
best_distance = dist ;
pos = tile ;
result = true ;
}
2014-05-22 20:25:17 +03:00
}
}
2014-05-31 11:56:14 +03:00
if ( result )
{
gen - > setOccupied ( pos , ETileType : : BLOCKED ) ; //block that tile
}
2014-05-22 20:25:17 +03:00
return result ;
}
2014-05-23 18:12:31 +03:00
void CRmgTemplateZone : : checkAndPlaceObject ( CMapGenerator * gen , CGObjectInstance * object , const int3 & pos )
2014-05-22 20:25:17 +03:00
{
2014-05-23 20:14:20 +03:00
if ( ! gen - > map - > isInTheMap ( pos ) )
2014-07-08 09:59:13 +03:00
throw rmgException ( boost : : to_string ( boost : : format ( " Position of object %d at %s is outside the map " ) % object - > id % pos ) ) ;
2014-05-22 20:25:17 +03:00
object - > pos = pos ;
2014-05-23 18:12:31 +03:00
2014-05-23 20:14:20 +03:00
if ( object - > isVisitable ( ) & & ! gen - > map - > isInTheMap ( object - > visitablePos ( ) ) )
2014-05-23 18:12:31 +03:00
throw rmgException ( boost : : to_string ( boost : : format ( " Visitable tile %s of object %d at %s is outside the map " ) % object - > visitablePos ( ) % object - > id % object - > pos ( ) ) ) ;
for ( auto tile : object - > getBlockedPos ( ) )
{
if ( ! gen - > map - > isInTheMap ( tile ) )
throw rmgException ( boost : : to_string ( boost : : format ( " Tile %s of object %d at %s is outside the map " ) % tile ( ) % object - > id % object - > pos ( ) ) ) ;
}
2014-05-31 13:26:37 +03:00
if ( object - > appearance . id = = Obj : : NO_OBJ )
{
2014-07-03 18:24:28 +03:00
auto terrainType = gen - > map - > getTile ( pos ) . terType ;
auto templates = VLC - > objtypeh - > getHandlerFor ( object - > ID , object - > subID ) - > getTemplates ( terrainType ) ;
2014-05-31 13:26:37 +03:00
if ( templates . empty ( ) )
2014-07-03 18:24:28 +03:00
throw rmgException ( boost : : to_string ( boost : : format ( " Did not find graphics for object (%d,%d) at % s ( terrain % d ) " ) %object->ID %object->subID %pos %terrainType)) ;
2014-05-23 20:14:20 +03:00
2014-05-31 13:26:37 +03:00
object - > appearance = templates . front ( ) ;
}
2014-05-22 20:25:17 +03:00
gen - > editManager - > insertObject ( object , pos ) ;
2014-06-15 09:48:46 +03:00
//logGlobal->traceStream() << boost::format ("Successfully inserted object (%d,%d) at pos %s") %object->ID %object->subID %pos();
2014-05-23 18:12:31 +03:00
}
2014-07-24 20:16:49 +03:00
void CRmgTemplateZone : : placeObject ( CMapGenerator * gen , CGObjectInstance * object , const int3 & pos , bool updateDistance )
2014-05-23 18:12:31 +03:00
{
2014-06-15 09:48:46 +03:00
//logGlobal->traceStream() << boost::format("Inserting object at %d %d") % pos.x % pos.y;
2014-05-23 18:12:31 +03:00
checkAndPlaceObject ( gen , object , pos ) ;
2014-05-22 20:25:17 +03:00
auto points = object - > getBlockedPos ( ) ;
if ( object - > isVisitable ( ) )
2014-05-29 13:42:05 +03:00
points . insert ( pos + object - > getVisitableOffset ( ) ) ;
points . insert ( pos ) ;
2014-06-01 15:10:44 +03:00
for ( auto p : points )
2014-05-22 20:25:17 +03:00
{
2014-06-01 15:10:44 +03:00
if ( gen - > map - > isInTheMap ( p ) )
2014-05-22 20:25:17 +03:00
{
2014-06-01 15:10:44 +03:00
gen - > setOccupied ( p , ETileType : : USED ) ;
2014-05-22 20:25:17 +03:00
}
}
2014-07-24 20:16:49 +03:00
if ( updateDistance )
{
for ( auto tile : possibleTiles ) //don't need to mark distance for not possible tiles
{
si32 d = pos . dist2dSQ ( tile ) ; //optimization, only relative distance is interesting
2014-07-25 18:10:16 +03:00
gen - > setNearestObjectDistance ( tile , std : : min < float > ( d , gen - > getNearestObjectDistance ( tile ) ) ) ;
2014-07-24 20:16:49 +03:00
}
2014-05-22 20:25:17 +03:00
}
}
2014-07-15 15:38:05 +03:00
void CRmgTemplateZone : : placeAndGuardObject ( CMapGenerator * gen , CGObjectInstance * object , const int3 & pos , si32 str , bool zoneGuard )
2014-07-03 18:24:28 +03:00
{
placeObject ( gen , object , pos ) ;
2014-07-15 15:38:05 +03:00
guardObject ( gen , object , str , zoneGuard ) ;
2014-07-03 18:24:28 +03:00
}
2014-06-15 20:56:58 +03:00
std : : vector < int3 > CRmgTemplateZone : : getAccessibleOffsets ( CMapGenerator * gen , CGObjectInstance * object )
2014-05-22 20:25:17 +03:00
{
2014-06-15 20:56:58 +03:00
//get all tiles from which this object can be accessed
2014-05-23 20:14:20 +03:00
int3 visitable = object - > visitablePos ( ) ;
2014-05-22 20:25:17 +03:00
std : : vector < int3 > tiles ;
2014-06-15 10:39:30 +03:00
auto tilesBlockedByObject = object - > getBlockedPos ( ) ; //absolue value, as object is already placed
2014-05-30 22:23:41 +03:00
gen - > foreach_neighbour ( visitable , [ & ] ( int3 & pos )
2014-05-22 20:25:17 +03:00
{
2014-05-30 22:23:41 +03:00
if ( gen - > isPossible ( pos ) )
2014-05-22 20:25:17 +03:00
{
2014-06-15 10:39:30 +03:00
if ( ! vstd : : contains ( tilesBlockedByObject , pos ) )
{
if ( object - > appearance . isVisitableFrom ( pos . x - visitable . x , pos . y - visitable . y ) & & ! gen - > isBlocked ( pos ) ) //TODO: refactor - info about visitability from absolute coordinates
{
tiles . push_back ( pos ) ;
}
}
2014-05-30 22:23:41 +03:00
} ;
} ) ;
2014-06-15 20:56:58 +03:00
return tiles ;
}
2014-07-25 17:26:50 +03:00
bool CRmgTemplateZone : : guardObject ( CMapGenerator * gen , CGObjectInstance * object , si32 str , bool zoneGuard , bool addToFreePaths )
2014-06-15 20:56:58 +03:00
{
logGlobal - > traceStream ( ) < < boost : : format ( " Guard object at %s " ) % object - > pos ( ) ;
std : : vector < int3 > tiles = getAccessibleOffsets ( gen , object ) ;
int3 guardTile ( - 1 , - 1 , - 1 ) ;
for ( auto tile : tiles )
{
2014-11-06 14:06:16 +02:00
//crunching path may fail if center of the zone is directly over wide object
2014-07-25 17:26:50 +03:00
//make sure object is accessible before surrounding it with blocked tiles
if ( crunchPath ( gen , tile , findClosestTile ( freePaths , tile ) , id , addToFreePaths ? & freePaths : nullptr ) )
2014-06-15 20:56:58 +03:00
{
guardTile = tile ;
break ;
}
}
if ( ! guardTile . valid ( ) )
{
logGlobal - > errorStream ( ) < < boost : : format ( " Failed to crunch path to object at %s " ) % object - > pos ( ) ;
2014-05-22 20:25:17 +03:00
return false ;
}
2014-07-15 15:38:05 +03:00
if ( addMonster ( gen , guardTile , str , false , zoneGuard ) ) //do not place obstacles around unguarded object
2014-06-04 21:59:01 +03:00
{
for ( auto pos : tiles )
2014-06-15 20:56:58 +03:00
{
if ( ! gen - > isFree ( pos ) )
gen - > setOccupied ( pos , ETileType : : BLOCKED ) ;
}
2014-07-23 12:42:05 +03:00
gen - > foreach_neighbour ( guardTile , [ & ] ( int3 & pos )
{
if ( gen - > isPossible ( pos ) )
gen - > setOccupied ( pos , ETileType : : FREE ) ;
} ) ;
2014-06-04 21:59:01 +03:00
2014-06-15 20:56:58 +03:00
gen - > setOccupied ( guardTile , ETileType : : USED ) ;
2014-06-04 21:59:01 +03:00
}
2014-07-01 08:07:40 +03:00
else //allow no guard or other object in front of this object
{
for ( auto tile : tiles )
2014-07-23 12:42:05 +03:00
if ( gen - > isPossible ( tile ) )
2014-07-15 20:10:03 +03:00
gen - > setOccupied ( tile , ETileType : : FREE ) ;
2014-07-01 08:07:40 +03:00
}
2014-06-01 15:10:44 +03:00
2014-05-22 20:25:17 +03:00
return true ;
}
2014-06-05 21:19:42 +03:00
2014-09-22 14:36:55 +03:00
ObjectInfo CRmgTemplateZone : : getRandomObject ( CMapGenerator * gen , CTreasurePileInfo & info , ui32 desiredValue , ui32 maxValue , ui32 currentValue )
2014-06-05 21:19:42 +03:00
{
2014-07-09 15:27:12 +03:00
//int objectsVisitableFromBottom = 0; //for debug
2014-06-05 21:19:42 +03:00
std : : vector < std : : pair < ui32 , ObjectInfo > > tresholds ;
ui32 total = 0 ;
2014-07-29 16:58:54 +03:00
//calculate actual treasure value range based on remaining value
2014-12-26 20:35:45 +02:00
ui32 maxVal = desiredValue - currentValue ;
2014-09-22 14:36:55 +03:00
ui32 minValue = 0.25f * ( desiredValue - currentValue ) ;
2014-06-05 21:19:42 +03:00
//roulette wheel
2014-07-24 20:16:49 +03:00
for ( ObjectInfo & oi : possibleObjects ) //copy constructor turned out to be costly
2014-06-05 21:19:42 +03:00
{
2014-09-22 14:36:55 +03:00
if ( oi . value > = minValue & & oi . value < = maxVal & & oi . maxPerZone > 0 )
2014-06-05 21:19:42 +03:00
{
2014-07-09 12:38:16 +03:00
int3 newVisitableOffset = oi . templ . getVisitableOffset ( ) ; //visitablePos assumes object will be shifter by visitableOffset
int3 newVisitablePos = info . nextTreasurePos ;
if ( ! oi . templ . isVisitableFromTop ( ) )
{
2014-07-09 15:27:12 +03:00
//objectsVisitableFromBottom++;
2014-07-09 12:38:16 +03:00
//there must be free tiles under object
2014-07-18 08:40:27 +03:00
auto blockedOffsets = oi . templ . getBlockedOffsets ( ) ;
if ( ! isAccessibleFromAnywhere ( gen , oi . templ , newVisitablePos , blockedOffsets ) )
2014-07-09 12:38:16 +03:00
continue ;
}
2014-07-09 10:22:50 +03:00
2014-07-09 17:39:17 +03:00
//NOTE: y coordinate grows downwards
2014-07-09 10:22:50 +03:00
if ( info . visitableFromBottomPositions . size ( ) + info . visitableFromTopPositions . size ( ) ) //do not try to match first object in zone
2014-07-08 21:13:51 +03:00
{
bool fitsHere = false ;
2014-07-09 10:22:50 +03:00
2014-07-09 12:38:16 +03:00
if ( oi . templ . isVisitableFromTop ( ) ) //new can be accessed from any direction
2014-07-08 21:13:51 +03:00
{
2014-07-09 10:22:50 +03:00
for ( auto tile : info . visitableFromTopPositions )
{
2014-07-09 12:38:16 +03:00
int3 actualTile = tile + newVisitableOffset ;
if ( newVisitablePos . areNeighbours ( actualTile ) ) //we access other removable object from any position
2014-07-09 10:22:50 +03:00
{
fitsHere = true ;
break ;
}
}
for ( auto tile : info . visitableFromBottomPositions )
2014-07-08 21:13:51 +03:00
{
2014-07-09 12:38:16 +03:00
int3 actualTile = tile + newVisitableOffset ;
2014-07-09 17:39:17 +03:00
if ( newVisitablePos . areNeighbours ( actualTile ) & & newVisitablePos . y > = actualTile . y ) //we access existing static object from side or bottom only
2014-07-08 21:13:51 +03:00
{
fitsHere = true ;
break ;
}
}
}
2014-07-09 12:38:16 +03:00
else //if new object is not visitable from top, it must be accessible from below or side
2014-07-08 21:13:51 +03:00
{
for ( auto tile : info . visitableFromTopPositions )
{
2014-07-09 12:38:16 +03:00
int3 actualTile = tile + newVisitableOffset ;
2014-07-09 17:39:17 +03:00
if ( newVisitablePos . areNeighbours ( actualTile ) & & newVisitablePos . y < = actualTile . y ) //we access existing removable object from top or side only
2014-07-09 10:22:50 +03:00
{
fitsHere = true ;
break ;
}
}
for ( auto tile : info . visitableFromBottomPositions )
{
2014-07-09 12:38:16 +03:00
int3 actualTile = tile + newVisitableOffset ;
if ( newVisitablePos . areNeighbours ( actualTile ) & & newVisitablePos . y = = actualTile . y ) //we access other static object from side only
2014-07-08 21:13:51 +03:00
{
fitsHere = true ;
break ;
}
}
}
2014-07-09 12:38:16 +03:00
if ( ! fitsHere )
continue ;
2014-07-08 21:13:51 +03:00
}
2014-07-09 10:22:50 +03:00
//now check blockmap, including our already reserved pile area
2014-07-08 21:13:51 +03:00
bool fitsBlockmap = true ;
2014-07-09 10:22:50 +03:00
2014-07-08 21:13:51 +03:00
std : : set < int3 > blockedOffsets = oi . templ . getBlockedOffsets ( ) ;
2014-07-09 12:38:16 +03:00
blockedOffsets . insert ( newVisitableOffset ) ;
2014-07-08 21:13:51 +03:00
for ( auto blockingTile : blockedOffsets )
{
2014-07-09 12:38:16 +03:00
int3 t = info . nextTreasurePos + newVisitableOffset + blockingTile ;
2014-07-08 21:13:51 +03:00
if ( ! gen - > map - > isInTheMap ( t ) | | vstd : : contains ( info . occupiedPositions , t ) )
{
fitsBlockmap = false ; //if at least one tile is not possible, object can't be placed here
break ;
}
if ( ! ( gen - > isPossible ( t ) | | gen - > isBlocked ( t ) ) ) //blocked tiles of object may cover blocked tiles, but not used or free tiles
{
fitsBlockmap = false ;
break ;
}
}
if ( ! fitsBlockmap )
continue ;
2014-06-05 21:19:42 +03:00
total + = oi . probability ;
2014-07-29 16:58:54 +03:00
//assert (oi.value > 0);
2014-06-05 21:19:42 +03:00
tresholds . push_back ( std : : make_pair ( total , oi ) ) ;
}
}
2014-07-09 15:27:12 +03:00
//logGlobal->infoStream() << boost::format ("Number of objects visitable from bottom: %d") % objectsVisitableFromBottom;
2014-06-05 21:19:42 +03:00
if ( tresholds . empty ( ) )
{
2014-07-29 16:58:54 +03:00
ObjectInfo oi ;
//Generate pandora Box with gold if the value is extremely high
2014-06-07 23:27:36 +03:00
if ( minValue > 20000 ) //we don't have object valuable enough
2014-06-05 21:19:42 +03:00
{
2014-06-07 23:27:36 +03:00
oi . generateObject = [ minValue ] ( ) - > CGObjectInstance *
{
auto obj = new CGPandoraBox ( ) ;
obj - > ID = Obj : : PANDORAS_BOX ;
obj - > subID = 0 ;
obj - > resources [ Res : : GOLD ] = minValue ;
return obj ;
} ;
2014-07-08 21:13:51 +03:00
oi . setTemplate ( Obj : : PANDORAS_BOX , 0 , terrainType ) ;
2014-06-07 23:27:36 +03:00
oi . value = minValue ;
oi . probability = 0 ;
}
2014-07-29 16:58:54 +03:00
else //generate empty object with 0 value if the value if we can't spawn anything
2014-06-07 23:27:36 +03:00
{
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
return nullptr ;
} ;
2014-07-08 21:13:51 +03:00
oi . setTemplate ( Obj : : PANDORAS_BOX , 0 , terrainType ) ; //TODO: null template or something? should be never used, but hell knows
2014-07-29 16:58:54 +03:00
oi . value = 0 ; // this field is checked to determine no object
2014-06-07 23:27:36 +03:00
oi . probability = 0 ;
}
return oi ;
2014-06-05 21:19:42 +03:00
}
2014-07-29 16:58:54 +03:00
else
2014-06-05 21:19:42 +03:00
{
2014-07-29 16:58:54 +03:00
int r = gen - > rand . nextInt ( 1 , total ) ;
for ( auto t : tresholds )
{
if ( r < = t . first )
return t . second ;
}
assert ( 0 ) ; //we should never be here
2014-06-05 21:19:42 +03:00
}
2014-06-22 17:26:08 +03:00
//FIXME: control reaches end of non-void function. Missing return?
2014-06-05 21:19:42 +03:00
}
2015-02-28 23:37:04 +02:00
void CRmgTemplateZone : : addAllPossibleObjects ( CMapGenerator * gen )
2014-06-05 21:19:42 +03:00
{
ObjectInfo oi ;
2014-07-15 23:33:51 +03:00
oi . maxPerMap = std : : numeric_limits < ui32 > ( ) . max ( ) ;
2014-06-05 21:19:42 +03:00
2014-07-23 19:02:17 +03:00
int numZones = gen - > getZones ( ) . size ( ) ;
2015-02-28 23:37:04 +02:00
for ( auto primaryID : VLC - > objtypeh - > knownObjects ( ) )
{
for ( auto secondaryID : VLC - > objtypeh - > knownSubObjects ( primaryID ) )
{
auto handler = VLC - > objtypeh - > getHandlerFor ( primaryID , secondaryID ) ;
2014-07-05 20:35:46 +03:00
if ( ! handler - > isStaticObject ( ) & & handler - > getRMGInfo ( ) . value )
{
for ( auto temp : handler - > getTemplates ( ) )
{
if ( temp . canBePlacedAt ( terrainType ) )
{
oi . generateObject = [ gen , temp ] ( ) - > CGObjectInstance *
{
return VLC - > objtypeh - > getHandlerFor ( temp . id , temp . subid ) - > create ( temp ) ;
} ;
2014-07-23 19:02:17 +03:00
auto rmgInfo = handler - > getRMGInfo ( ) ;
oi . value = rmgInfo . value ;
oi . probability = rmgInfo . rarity ;
2014-07-08 21:13:51 +03:00
oi . templ = temp ;
2014-07-23 19:02:17 +03:00
oi . maxPerZone = rmgInfo . zoneLimit ;
2015-02-28 23:37:04 +02:00
vstd : : amin ( oi . maxPerZone , rmgInfo . mapLimit / numZones ) ; //simple, but should distribute objects evenly on large maps
possibleObjects . push_back ( oi ) ;
2014-07-05 20:35:46 +03:00
}
}
}
2015-02-28 23:37:04 +02:00
}
2014-07-06 23:14:37 +03:00
}
2014-07-25 11:44:17 +03:00
//prisons
//levels 1, 5, 10, 20, 30
2015-02-28 23:37:04 +02:00
static int prisonExp [ ] = { 0 , 5000 , 15000 , 90000 , 500000 } ;
static int prisonValues [ ] = { 2500 , 5000 , 10000 , 20000 , 30000 } ;
2014-07-25 11:44:17 +03:00
for ( int i = 0 ; i < 5 ; i + + )
{
2014-09-21 16:59:35 +03:00
oi . generateObject = [ i , gen , this ] ( ) - > CGObjectInstance *
2014-07-25 11:44:17 +03:00
{
auto obj = new CGHeroInstance ;
obj - > ID = Obj : : PRISON ;
std : : vector < ui32 > possibleHeroes ;
for ( int j = 0 ; j < gen - > map - > allowedHeroes . size ( ) ; j + + )
{
if ( gen - > map - > allowedHeroes [ j ] )
possibleHeroes . push_back ( j ) ;
}
auto hid = * RandomGeneratorUtil : : nextItem ( possibleHeroes , gen - > rand ) ;
2014-09-21 16:59:35 +03:00
obj - > subID = hid ; //will be initialized later
2015-01-03 00:01:14 +02:00
obj - > exp = prisonExp [ i ] ;
2014-07-25 11:44:17 +03:00
obj - > setOwner ( PlayerColor : : NEUTRAL ) ;
gen - > map - > allowedHeroes [ hid ] = false ; //ban this hero
gen - > decreasePrisonsRemaining ( ) ;
2014-09-21 16:59:35 +03:00
obj - > appearance = VLC - > objtypeh - > getHandlerFor ( Obj : : PRISON , 0 ) - > getTemplates ( terrainType ) . front ( ) ; //can't init template with hero subID
2014-07-25 11:44:17 +03:00
return obj ;
} ;
2015-02-28 23:37:04 +02:00
oi . setTemplate ( Obj : : PRISON , 0 , terrainType ) ;
2014-07-25 11:44:17 +03:00
oi . value = prisonValues [ i ] ;
oi . probability = 30 ;
oi . maxPerZone = gen - > getPrisonsRemaning ( ) / 5 ; //probably not perfect, but we can't generate more prisons than hereos.
2015-02-28 23:37:04 +02:00
possibleObjects . push_back ( oi ) ;
2014-07-25 11:44:17 +03:00
}
2014-07-15 23:33:51 +03:00
//all following objects are unlimited
oi . maxPerZone = std : : numeric_limits < ui32 > ( ) . max ( ) ;
2014-07-06 23:14:37 +03:00
//dwellings
2014-07-08 12:57:02 +03:00
auto subObjects = VLC - > objtypeh - > knownSubObjects ( Obj : : CREATURE_GENERATOR1 ) ;
//don't spawn original "neutral" dwellings that got replaced by Conflux dwellings in AB
2015-02-28 23:37:04 +02:00
static int elementalConfluxROE [ ] = { 7 , 13 , 16 , 47 } ;
2014-07-08 12:57:02 +03:00
for ( int i = 0 ; i < 4 ; i + + )
vstd : : erase_if_present ( subObjects , elementalConfluxROE [ i ] ) ;
for ( auto secondaryID : subObjects )
2014-07-06 23:14:37 +03:00
{
auto dwellingHandler = dynamic_cast < const CDwellingInstanceConstructor * > ( VLC - > objtypeh - > getHandlerFor ( Obj : : CREATURE_GENERATOR1 , secondaryID ) . get ( ) ) ;
auto creatures = dwellingHandler - > getProducedCreatures ( ) ;
if ( creatures . empty ( ) )
continue ;
auto cre = creatures . front ( ) ;
if ( cre - > faction = = townType )
{
2014-07-24 13:26:10 +03:00
float nativeZonesCount = gen - > getZoneCount ( cre - > faction ) ;
oi . value = cre - > AIValue * cre - > growth * ( 1 + ( nativeZonesCount / gen - > getTotalZoneCount ( ) ) + ( nativeZonesCount / 2 ) ) ;
2014-07-06 23:14:37 +03:00
oi . probability = 40 ;
for ( auto temp : dwellingHandler - > getTemplates ( ) )
{
if ( temp . canBePlacedAt ( terrainType ) )
{
oi . generateObject = [ gen , temp , secondaryID , dwellingHandler ] ( ) - > CGObjectInstance *
{
auto obj = VLC - > objtypeh - > getHandlerFor ( Obj : : CREATURE_GENERATOR1 , secondaryID ) - > create ( temp ) ;
//dwellingHandler->configureObject(obj, gen->rand);
obj - > tempOwner = PlayerColor : : NEUTRAL ;
return obj ;
} ;
2014-07-08 21:13:51 +03:00
oi . templ = temp ;
2015-02-28 23:37:04 +02:00
possibleObjects . push_back ( oi ) ;
2014-07-06 23:14:37 +03:00
}
}
}
}
2014-06-05 21:19:42 +03:00
2015-02-28 23:37:04 +02:00
static const int scrollValues [ ] = { 500 , 2000 , 3000 , 4000 , 5000 } ;
2014-06-05 21:19:42 +03:00
for ( int i = 0 ; i < 5 ; i + + )
{
oi . generateObject = [ i , gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGArtifact ( ) ;
obj - > ID = Obj : : SPELL_SCROLL ;
obj - > subID = 0 ;
std : : vector < SpellID > out ;
//TODO: unify with cb->getAllowedSpells?
2015-01-03 00:01:14 +02:00
for ( ui32 spellid = 0 ; spellid < gen - > map - > allowedSpell . size ( ) ; spellid + + ) //spellh size appears to be greater (?)
2014-06-05 21:19:42 +03:00
{
2015-01-03 00:01:14 +02:00
const CSpell * spell = SpellID ( spellid ) . toSpell ( ) ;
2015-02-28 23:37:04 +02:00
if ( gen - > map - > allowedSpell [ spell - > id ] & & spell - > level = = i + 1 )
2014-06-05 21:19:42 +03:00
{
out . push_back ( spell - > id ) ;
}
}
auto a = CArtifactInstance : : createScroll ( RandomGeneratorUtil : : nextItem ( out , gen - > rand ) - > toSpell ( ) ) ;
gen - > map - > addNewArtifactInstance ( a ) ;
obj - > storedArtifact = a ;
return obj ;
} ;
2015-02-28 23:37:04 +02:00
oi . setTemplate ( Obj : : SPELL_SCROLL , 0 , terrainType ) ;
2014-06-05 21:19:42 +03:00
oi . value = scrollValues [ i ] ;
oi . probability = 30 ;
2015-02-28 23:37:04 +02:00
possibleObjects . push_back ( oi ) ;
2014-06-05 21:19:42 +03:00
}
2014-07-06 08:15:52 +03:00
//pandora box with gold
for ( int i = 1 ; i < 5 ; i + + )
{
oi . generateObject = [ i ] ( ) - > CGObjectInstance *
{
auto obj = new CGPandoraBox ( ) ;
obj - > ID = Obj : : PANDORAS_BOX ;
obj - > subID = 0 ;
obj - > resources [ Res : : GOLD ] = i * 5000 ;
return obj ;
} ;
2015-02-28 23:37:04 +02:00
oi . setTemplate ( Obj : : PANDORAS_BOX , 0 , terrainType ) ;
2014-07-06 08:15:52 +03:00
oi . value = i * 5000 ; ;
oi . probability = 5 ;
2015-02-28 23:37:04 +02:00
possibleObjects . push_back ( oi ) ;
2014-07-06 08:15:52 +03:00
}
//pandora box with experience
for ( int i = 1 ; i < 5 ; i + + )
{
oi . generateObject = [ i ] ( ) - > CGObjectInstance *
{
auto obj = new CGPandoraBox ( ) ;
obj - > ID = Obj : : PANDORAS_BOX ;
obj - > subID = 0 ;
obj - > gainedExp = i * 5000 ;
return obj ;
} ;
2015-02-28 23:37:04 +02:00
oi . setTemplate ( Obj : : PANDORAS_BOX , 0 , terrainType ) ;
2014-07-06 08:15:52 +03:00
oi . value = i * 6000 ; ;
oi . probability = 20 ;
2015-02-28 23:37:04 +02:00
possibleObjects . push_back ( oi ) ;
2014-07-06 08:15:52 +03:00
}
//pandora box with creatures
2015-02-28 23:37:04 +02:00
static const int tierValues [ ] = { 5000 , 7000 , 9000 , 12000 , 16000 , 21000 , 27000 } ;
2014-07-06 08:15:52 +03:00
2015-02-28 22:14:45 +02:00
auto creatureToCount = [ ] ( CCreature * creature ) - > int
{
int actualTier = creature - > level > 7 ? 6 : creature - > level - 1 ;
float creaturesAmount = tierValues [ actualTier ] / creature - > AIValue ;
if ( creaturesAmount < = 5 )
{
creaturesAmount = boost : : math : : round ( creaturesAmount ) ; //allow single monsters
if ( creaturesAmount < 1 )
return 0 ;
}
else if ( creaturesAmount < = 12 )
{
( creaturesAmount / = 2 ) * = 2 ;
}
else if ( creaturesAmount < = 50 )
{
creaturesAmount = boost : : math : : round ( creaturesAmount / 5 ) * 5 ;
}
else
{
creaturesAmount = boost : : math : : round ( creaturesAmount / 10 ) * 10 ;
}
return creaturesAmount ;
} ;
2014-07-06 08:15:52 +03:00
for ( auto creature : VLC - > creh - > creatures )
{
2014-10-30 14:03:53 +02:00
if ( ! creature - > special & & creature - > faction = = townType )
2014-07-06 08:15:52 +03:00
{
2015-02-28 22:14:45 +02:00
int creaturesAmount = creatureToCount ( creature ) ;
if ( ! creaturesAmount )
continue ;
2014-07-06 08:15:52 +03:00
oi . generateObject = [ creature , creaturesAmount ] ( ) - > CGObjectInstance *
{
auto obj = new CGPandoraBox ( ) ;
obj - > ID = Obj : : PANDORAS_BOX ;
obj - > subID = 0 ;
auto stack = new CStackInstance ( creature , creaturesAmount ) ;
obj - > creatures . putStack ( SlotID ( 0 ) , stack ) ;
return obj ;
} ;
2015-02-28 23:37:04 +02:00
oi . setTemplate ( Obj : : PANDORAS_BOX , 0 , terrainType ) ;
oi . value = ( 2 * ( creature - > AIValue ) * creaturesAmount * ( 1 + ( float ) ( gen - > getZoneCount ( creature - > faction ) ) / gen - > getTotalZoneCount ( ) ) ) / 3 ;
2014-07-06 08:15:52 +03:00
oi . probability = 3 ;
2015-02-28 23:37:04 +02:00
possibleObjects . push_back ( oi ) ;
2014-07-06 08:15:52 +03:00
}
}
//Pandora with 12 spells of certain level
for ( int i = 1 ; i < = GameConstants : : SPELL_LEVELS ; i + + )
{
oi . generateObject = [ i , gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGPandoraBox ( ) ;
obj - > ID = Obj : : PANDORAS_BOX ;
obj - > subID = 0 ;
std : : vector < CSpell * > spells ;
for ( auto spell : VLC - > spellh - > objects )
{
if ( ! spell - > isSpecialSpell ( ) & & spell - > level = = i )
spells . push_back ( spell ) ;
}
RandomGeneratorUtil : : randomShuffle ( spells , gen - > rand ) ;
for ( int j = 0 ; j < std : : min < int > ( 12 , spells . size ( ) ) ; j + + )
{
2015-02-28 23:37:04 +02:00
obj - > spells . push_back ( spells [ j ] - > id ) ;
2014-07-06 08:15:52 +03:00
}
return obj ;
} ;
2015-02-28 23:37:04 +02:00
oi . setTemplate ( Obj : : PANDORAS_BOX , 0 , terrainType ) ;
2014-07-06 08:15:52 +03:00
oi . value = ( i + 1 ) * 2500 ; //5000 - 15000
oi . probability = 2 ;
2015-02-28 23:37:04 +02:00
possibleObjects . push_back ( oi ) ;
2014-07-06 08:15:52 +03:00
}
//Pandora with 15 spells of certain school
for ( int i = 1 ; i < = 4 ; i + + )
{
oi . generateObject = [ i , gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGPandoraBox ( ) ;
obj - > ID = Obj : : PANDORAS_BOX ;
obj - > subID = 0 ;
std : : vector < CSpell * > spells ;
for ( auto spell : VLC - > spellh - > objects )
{
if ( ! spell - > isSpecialSpell ( ) )
{
bool school = false ; //TODO: we could have better interface for iterating schools
switch ( i )
{
2015-02-28 23:37:04 +02:00
case 1 :
school = spell - > air ;
break ;
case 2 :
school = spell - > earth ;
break ;
case 3 :
school = spell - > fire ;
break ;
case 4 :
school = spell - > water ;
break ;
2014-07-06 08:15:52 +03:00
}
if ( school )
spells . push_back ( spell ) ;
}
}
RandomGeneratorUtil : : randomShuffle ( spells , gen - > rand ) ;
for ( int j = 0 ; j < std : : min < int > ( 15 , spells . size ( ) ) ; j + + )
{
2015-02-28 23:37:04 +02:00
obj - > spells . push_back ( spells [ j ] - > id ) ;
2014-07-06 08:15:52 +03:00
}
return obj ;
} ;
2015-02-28 23:37:04 +02:00
oi . setTemplate ( Obj : : PANDORAS_BOX , 0 , terrainType ) ;
2014-07-06 08:15:52 +03:00
oi . value = 15000 ;
oi . probability = 2 ;
2015-02-28 23:37:04 +02:00
possibleObjects . push_back ( oi ) ;
2014-07-06 08:15:52 +03:00
}
// Pandora box with 60 random spells
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGPandoraBox ( ) ;
obj - > ID = Obj : : PANDORAS_BOX ;
obj - > subID = 0 ;
std : : vector < CSpell * > spells ;
for ( auto spell : VLC - > spellh - > objects )
{
if ( ! spell - > isSpecialSpell ( ) )
spells . push_back ( spell ) ;
}
RandomGeneratorUtil : : randomShuffle ( spells , gen - > rand ) ;
for ( int j = 0 ; j < std : : min < int > ( 60 , spells . size ( ) ) ; j + + )
{
2015-02-28 23:37:04 +02:00
obj - > spells . push_back ( spells [ j ] - > id ) ;
2014-07-06 08:15:52 +03:00
}
return obj ;
} ;
2015-02-28 23:37:04 +02:00
oi . setTemplate ( Obj : : PANDORAS_BOX , 0 , terrainType ) ;
2014-07-06 08:15:52 +03:00
oi . value = 3000 ;
oi . probability = 2 ;
2015-02-28 23:37:04 +02:00
possibleObjects . push_back ( oi ) ;
2015-02-28 22:14:45 +02:00
//seer huts with creatures or generic rewards
2015-03-01 11:20:49 +02:00
if ( questArtZone ) //we won't be placing seer huts if there is no zone left to place arties
{
static const int genericSeerHuts = 8 ;
int seerHutsPerType = 0 ;
const int questArtsRemaining = gen - > getQuestArtsRemaning ( ) . size ( ) ;
2015-02-28 22:14:45 +02:00
2015-03-01 11:20:49 +02:00
std : : vector < CCreature * > creatures ;
2015-02-28 22:14:45 +02:00
2015-03-01 11:20:49 +02:00
for ( auto cre : VLC - > creh - > creatures )
2015-02-28 22:14:45 +02:00
{
2015-03-01 11:20:49 +02:00
if ( cre - > faction = = townType )
{
creatures . push_back ( cre ) ;
}
2015-02-28 22:14:45 +02:00
}
2015-03-01 11:20:49 +02:00
//general issue is that not many artifact types are available for quests
2015-02-28 22:14:45 +02:00
2015-03-01 11:20:49 +02:00
if ( questArtsRemaining > = genericSeerHuts + creatures . size ( ) )
{
seerHutsPerType = questArtsRemaining / ( genericSeerHuts + creatures . size ( ) ) ;
}
else if ( questArtsRemaining > = genericSeerHuts )
{
seerHutsPerType = 1 ;
}
oi . maxPerZone = seerHutsPerType ;
2015-02-28 22:14:45 +02:00
2015-03-01 11:20:49 +02:00
RandomGeneratorUtil : : randomShuffle ( creatures , gen - > rand ) ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
auto generateArtInfo = [ ] ( ArtifactID id ) - > ObjectInfo
{
ObjectInfo artInfo ;
artInfo . probability = 1e6 ; //99,9% to spawn that art in first treasure pile
artInfo . maxPerZone = 1 ;
artInfo . value = 2000 ; //treasure art
artInfo . generateObject = [ id ] ( ) - > CGObjectInstance *
{
auto obj = new CGArtifact ;
obj - > ID = Obj : : ARTIFACT ;
obj - > subID = id ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
return obj ;
//TODO: place required artifact in next zone
} ;
return artInfo ;
} ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
for ( int i = 0 ; i < std : : min < int > ( creatures . size ( ) , questArtsRemaining - genericSeerHuts ) ; i + + )
2015-02-28 23:37:04 +02:00
{
2015-03-01 11:20:49 +02:00
auto creature = creatures [ i ] ;
int creaturesAmount = creatureToCount ( creature ) ;
2015-02-28 22:14:45 +02:00
2015-03-01 11:20:49 +02:00
if ( ! creaturesAmount )
continue ;
2015-02-28 23:37:04 +02:00
2015-03-01 11:20:49 +02:00
int randomAppearance = * RandomGeneratorUtil : : nextItem ( VLC - > objtypeh - > knownSubObjects ( Obj : : SEER_HUT ) , gen - > rand ) ;
2015-02-28 23:37:04 +02:00
2015-03-01 11:20:49 +02:00
oi . generateObject = [ creature , creaturesAmount , randomAppearance , gen , this , generateArtInfo ] ( ) - > CGObjectInstance *
{
auto obj = new CGSeerHut ( ) ;
obj - > ID = Obj : : SEER_HUT ;
obj - > subID = randomAppearance ;
obj - > rewardType = CGSeerHut : : CREATURE ;
obj - > rID = creature - > idNumber ;
obj - > rVal = creaturesAmount ;
2015-02-28 23:37:04 +02:00
2015-03-01 11:20:49 +02:00
obj - > quest - > missionType = CQuest : : MISSION_ART ;
ArtifactID artid = * RandomGeneratorUtil : : nextItem ( gen - > getQuestArtsRemaning ( ) , gen - > rand ) ;
obj - > quest - > m5arts . push_back ( artid ) ;
gen - > banQuestArt ( artid ) ;
gen - > map - > addQuest ( obj ) ;
this - > questArtZone - > possibleObjects . push_back ( generateArtInfo ( artid ) ) ;
return obj ;
//TODO: place required artifact in next zone
} ;
oi . setTemplate ( Obj : : SEER_HUT , randomAppearance , terrainType ) ;
oi . value = ( ( 2 * ( creature - > AIValue ) * creaturesAmount * ( 1 + ( float ) ( gen - > getZoneCount ( creature - > faction ) ) / gen - > getTotalZoneCount ( ) ) ) - 4000 ) / 3 ;
oi . probability = 3 ;
possibleObjects . push_back ( oi ) ;
}
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
static int seerExpGold [ ] = { 5000 , 10000 , 15000 , 20000 } ;
static int seerValues [ ] = { 2000 , 5333 , 8666 , 12000 } ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
for ( int i = 0 ; i < 4 ; i + + ) //seems that code for exp and gold reward is similiar
2015-03-01 10:46:09 +02:00
{
2015-03-01 11:20:49 +02:00
int randomAppearance = * RandomGeneratorUtil : : nextItem ( VLC - > objtypeh - > knownSubObjects ( Obj : : SEER_HUT ) , gen - > rand ) ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
oi . setTemplate ( Obj : : SEER_HUT , randomAppearance , terrainType ) ;
oi . value = seerValues [ i ] ;
oi . probability = 10 ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
oi . generateObject = [ i , randomAppearance , gen , this , generateArtInfo ] ( ) - > CGObjectInstance *
{
auto obj = new CGSeerHut ( ) ;
obj - > ID = Obj : : SEER_HUT ;
obj - > subID = randomAppearance ;
obj - > rewardType = CGSeerHut : : EXPERIENCE ;
obj - > rID = 0 ; //unitialized?
obj - > rVal = seerExpGold [ i ] ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
obj - > quest - > missionType = CQuest : : MISSION_ART ;
ArtifactID artid = * RandomGeneratorUtil : : nextItem ( gen - > getQuestArtsRemaning ( ) , gen - > rand ) ;
obj - > quest - > m5arts . push_back ( artid ) ;
gen - > banQuestArt ( artid ) ;
gen - > map - > addQuest ( obj ) ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
this - > questArtZone - > possibleObjects . push_back ( generateArtInfo ( artid ) ) ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
return obj ;
//TODO: place required artifact in next zone
} ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
possibleObjects . push_back ( oi ) ;
2015-03-01 10:46:09 +02:00
2015-03-01 11:20:49 +02:00
oi . generateObject = [ i , randomAppearance , gen , this , generateArtInfo ] ( ) - > CGObjectInstance *
{
auto obj = new CGSeerHut ( ) ;
obj - > ID = Obj : : SEER_HUT ;
obj - > subID = randomAppearance ;
obj - > rewardType = CGSeerHut : : RESOURCES ;
obj - > rID = Res : : GOLD ;
obj - > rVal = seerExpGold [ i ] ;
obj - > quest - > missionType = CQuest : : MISSION_ART ;
ArtifactID artid = * RandomGeneratorUtil : : nextItem ( gen - > getQuestArtsRemaning ( ) , gen - > rand ) ;
obj - > quest - > m5arts . push_back ( artid ) ;
gen - > banQuestArt ( artid ) ;
gen - > map - > addQuest ( obj ) ;
this - > questArtZone - > possibleObjects . push_back ( generateArtInfo ( artid ) ) ;
return obj ;
//TODO: place required artifact in next zone
} ;
possibleObjects . push_back ( oi ) ;
}
2015-02-28 22:14:45 +02:00
}
2014-06-05 21:19:42 +03:00
}
2014-07-08 21:13:51 +03:00
void ObjectInfo : : setTemplate ( si32 type , si32 subtype , ETerrainType terrainType )
{
templ = VLC - > objtypeh - > getHandlerFor ( type , subtype ) - > getTemplates ( terrainType ) . front ( ) ;
2014-07-26 09:12:45 +03:00
}