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-05-22 20:25:17 +03:00
class CMap ;
class CMapEditManager ;
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-05-30 17:50:06 +03:00
int CTileInfo : : getNearestObjectDistance ( ) const
2014-05-22 20:25:17 +03:00
{
return nearestObjectDistance ;
}
2014-05-30 17:50:06 +03:00
void CTileInfo : : setNearestObjectDistance ( int value )
2014-05-22 20:25:17 +03:00
{
2014-05-23 20:14:20 +03:00
nearestObjectDistance = std : : max ( 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 ;
}
void CTileInfo : : setOccupied ( ETileType : : ETileType value )
2014-05-22 20:25:17 +03:00
{
occupied = value ;
}
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 ;
}
2013-08-17 15:46:48 +03:00
CRmgTemplateZone : : CRmgTemplateZone ( ) : id ( 0 ) , type ( ETemplateZoneType : : PLAYER_START ) , size ( 1 ) ,
townsAreSameType ( false ) , matchTerrainToTown ( true )
{
townTypes = getDefaultTownTypes ( ) ;
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 ;
}
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 ;
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 ) ;
return terTypes ;
}
boost : : optional < TRmgTemplateZoneId > CRmgTemplateZone : : getTerrainTypeLikeZone ( ) const
{
return terrainTypeLikeZone ;
}
void CRmgTemplateZone : : setTerrainTypeLikeZone ( boost : : optional < TRmgTemplateZoneId > value )
{
terrainTypeLikeZone = value ;
}
boost : : optional < TRmgTemplateZoneId > CRmgTemplateZone : : getTownTypeLikeZone ( ) const
{
return townTypeLikeZone ;
}
void CRmgTemplateZone : : setTownTypeLikeZone ( boost : : optional < TRmgTemplateZoneId > value )
{
townTypeLikeZone = value ;
}
2014-05-22 20:25:17 +03:00
2014-05-24 13:42:06 +03:00
void CRmgTemplateZone : : addConnection ( TRmgTemplateZoneId otherZone )
{
connections . push_back ( otherZone ) ;
}
std : : vector < TRmgTemplateZoneId > CRmgTemplateZone : : getConnections ( ) const
{
return connections ;
}
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-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-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-05-31 15:11:20 +03:00
bool CRmgTemplateZone : : crunchPath ( CMapGenerator * gen , const int3 & src , const int3 & dst , TRmgTemplateZoneId zone )
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 )
break ;
auto lastDistance = distance ;
gen - > foreach_neighbour ( currentPos , [ this , gen , & currentPos , dst , & distance , & result , & end ] ( int3 & pos )
{
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 ) ;
currentPos = pos ;
distance = currentPos . dist2dSQ ( dst ) ;
}
else if ( gen - > isFree ( pos ) )
{
end = true ;
result = true ;
}
else
throw rmgException ( boost : : to_string ( boost : : format ( " Tile %s of uknown type found on path " ) % pos ( ) ) ) ;
}
}
}
}
2014-05-30 22:23:41 +03:00
} ) ;
2014-05-31 11:56:14 +03:00
if ( ! ( result | | distance < lastDistance ) ) //we do not advance, use more avdnaced pathfinding algorithm?
{
logGlobal - > warnStream ( ) < < boost : : format ( " No tile closer than %s found on path from %s to %s " ) % currentPos % src % dst ;
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-05-24 15:33:22 +03:00
2014-06-04 21:59:01 +03:00
bool CRmgTemplateZone : : addMonster ( CMapGenerator * gen , int3 & pos , si32 strength )
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 zoneMonsterStrength = 0 ; //TODO: range -1..1 based on template settings
int mapMonsterStrength = gen - > mapGenOptions - > getMonsterStrength ( ) ;
int monsterStrength = zoneMonsterStrength + mapMonsterStrength - 1 ; //array index from 0 to 4
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 ;
while ( true )
{
creId = VLC - > creh - > pickRandomMonster ( gen - > rand ) ;
auto cre = VLC - > creh - > creatures [ creId ] ;
2014-06-04 11:16:08 +03:00
if ( ( cre - > AIValue * ( cre - > ammMin + cre - > ammMax ) / 2 < strength ) & & ( strength < cre - > AIValue * 100 ) ) //at leats one full monster. size between minimum size of given stack and 100
{
amount = strength / cre - > AIValue ;
if ( amount > = 4 )
amount * = gen - > rand . nextDouble ( 0.75 , 1.25 ) ;
2014-06-01 15:10:44 +03:00
break ;
2014-06-04 11:16:08 +03:00
}
2014-06-01 15:10:44 +03:00
}
auto guard = new CGCreature ( ) ;
guard - > ID = Obj : : MONSTER ;
guard - > subID = creId ;
auto hlp = new CStackInstance ( creId , amount ) ;
//will be set during initialization
guard - > putStack ( SlotID ( 0 ) , hlp ) ;
placeObject ( gen , guard , pos ) ;
2014-06-04 21:59:01 +03:00
return true ;
2014-06-01 15:10:44 +03:00
}
2014-06-04 11:16:08 +03:00
bool CRmgTemplateZone : : createTreasurePile ( CMapGenerator * gen , int3 & pos )
{
2014-06-05 18:19:11 +03:00
std : : map < int3 , CGObjectInstance * > treasures ;
std : : set < int3 > boundary ;
int3 guardPos ;
int3 nextTreasurePos = pos ;
2014-06-04 16:16:23 +03:00
//default values
int maxValue = 5000 ;
int minValue = 1500 ;
//TODO: choose random treasure info based on density
if ( treasureInfo . size ( ) )
{
maxValue = treasureInfo . front ( ) . max ;
minValue = treasureInfo . front ( ) . min ;
}
2014-06-04 11:16:08 +03:00
int currentValue = 0 ;
CGObjectInstance * object = nullptr ;
while ( currentValue < minValue )
{
2014-06-05 18:19:11 +03:00
//TODO: this works only for 1-tile objects
//make sure our shape is consistent
treasures [ nextTreasurePos ] = nullptr ;
for ( auto treasurePos : treasures )
{
gen - > foreach_neighbour ( treasurePos . first , [ gen , & boundary ] ( int3 pos )
{
boundary . insert ( pos ) ;
} ) ;
}
for ( auto treasurePos : treasures )
{
//leaving only boundary around objects
vstd : : erase_if_present ( boundary , treasurePos . first ) ;
}
for ( auto tile : boundary )
{
//we can't extend boundary anymore
if ( ! ( gen - > isBlocked ( tile ) | | gen - > isPossible ( tile ) ) )
break ;
}
2014-06-04 11:16:08 +03:00
int remaining = maxValue - currentValue ;
2014-06-04 20:08:04 +03:00
auto oi = getRandomObject ( gen , remaining ) ;
object = oi . generateObject ( ) ;
if ( ! object )
break ;
currentValue + = oi . value ;
2014-06-05 18:19:11 +03:00
treasures [ nextTreasurePos ] = object ;
//now find place for next object
int3 placeFound ( - 1 , - 1 , - 1 ) ;
2014-06-07 15:02:57 +03:00
//FIXME: find out why teh following code causes crashes
//std::vector<int3> boundaryVec(boundary.begin(), boundary.end());
//RandomGeneratorUtil::randomShuffle(boundaryVec, gen->rand);
//for (auto tile : boundaryVec)
2014-06-05 18:19:11 +03:00
for ( auto tile : boundary )
{
if ( gen - > isPossible ( tile ) ) //we can place new treasure only on possible tile
{
bool here = true ;
gen - > foreach_neighbour ( tile , [ gen , & here ] ( int3 pos )
{
if ( ! ( gen - > isBlocked ( pos ) | | gen - > isPossible ( pos ) ) )
here = false ;
} ) ;
if ( here )
{
placeFound = tile ;
break ;
}
}
}
if ( placeFound . valid ( ) )
nextTreasurePos = placeFound ;
2014-06-04 20:08:04 +03:00
}
2014-06-05 18:19:11 +03:00
if ( treasures . size ( ) )
2014-06-04 20:08:04 +03:00
{
2014-06-05 18:19:11 +03:00
for ( auto treasure : treasures )
{
2014-06-07 15:02:57 +03:00
placeObject ( gen , treasure . second , treasure . first - treasure . second - > getVisitableOffset ( ) ) ;
2014-06-05 18:19:11 +03:00
}
2014-06-05 22:37:39 +03:00
std : : vector < int3 > accessibleTiles ; //we can't place guard in dead-end of zone, make sure that at least one neightbouring tile is possible and not blocked
for ( auto tile : boundary )
{
2014-06-07 11:47:38 +03:00
if ( gen - > shouldBeBlocked ( tile ) ) //this tile could be already blocked, don't place a monster here
continue ;
2014-06-05 22:37:39 +03:00
bool possible = false ;
gen - > foreach_neighbour ( tile , [ gen , & accessibleTiles , & possible , boundary ] ( int3 pos )
{
if ( gen - > isPossible ( pos ) & & ! vstd : : contains ( boundary , pos ) ) //do not check tiles that are going to be blocked
possible = true ;
} ) ;
if ( possible )
accessibleTiles . push_back ( tile ) ;
}
guardPos = * RandomGeneratorUtil : : nextItem ( accessibleTiles , gen - > rand ) ;
2014-06-05 18:19:11 +03:00
if ( addMonster ( gen , guardPos , currentValue ) )
{ //block only if object is guarded
2014-06-05 22:37:39 +03:00
2014-06-05 18:19:11 +03:00
for ( auto tile : boundary )
{
if ( gen - > isPossible ( tile ) )
gen - > setOccupied ( tile , ETileType : : BLOCKED ) ;
}
}
2014-06-04 20:08:04 +03:00
return true ;
}
else //we did not place eveyrthing successfully
return false ;
}
2014-06-01 13:02:43 +03:00
bool CRmgTemplateZone : : fill ( CMapGenerator * gen )
{
2014-06-04 20:08:04 +03:00
addAllPossibleObjects ( gen ) ;
2014-05-24 15:33:22 +03:00
int townId = 0 ;
2014-05-22 20:25:17 +03:00
if ( ( type = = ETemplateZoneType : : CPU_START ) | | ( type = = ETemplateZoneType : : PLAYER_START ) )
{
logGlobal - > infoStream ( ) < < " Preparing playing zone " ;
int player_id = * owner - 1 ;
auto & playerInfo = gen - > map - > players [ player_id ] ;
if ( playerInfo . canAnyonePlay ( ) )
{
PlayerColor player ( player_id ) ;
auto town = new CGTownInstance ( ) ;
town - > ID = Obj : : TOWN ;
2014-05-24 15:33:22 +03:00
townId = gen - > mapGenOptions - > getPlayersSettings ( ) . find ( player ) - > second . getStartingTown ( ) ;
2014-05-22 20:25:17 +03:00
if ( townId = = CMapGenOptions : : CPlayerSettings : : RANDOM_TOWN )
2014-05-23 18:12:31 +03:00
townId = * RandomGeneratorUtil : : nextItem ( VLC - > townh - > getAllowedFactions ( ) , gen - > rand ) ; // all possible towns, skip neutral
2014-05-22 20:25:17 +03:00
town - > subID = townId ;
town - > tempOwner = player ;
town - > builtBuildings . insert ( BuildingID : : FORT ) ;
town - > builtBuildings . insert ( BuildingID : : DEFAULT ) ;
2014-06-03 09:57:20 +03:00
placeObject ( gen , town , getPos ( ) + town - > getVisitableOffset ( ) ) ; //towns are big objects and should be centered around visitable position
2014-05-22 20:25:17 +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-05 18:19:11 +03:00
playerInfo . allowedFactions . insert ( townId ) ;
2014-05-22 20:25:17 +03:00
playerInfo . hasMainTown = true ;
playerInfo . posOfMainTown = town - > pos - int3 ( 2 , 0 , 0 ) ;
playerInfo . generateHeroAtMainTown = true ;
2014-06-01 13:02:43 +03:00
//requiredObjects.push_back(town);
2014-05-22 20:25:17 +03:00
std : : vector < Res : : ERes > required_mines ;
required_mines . push_back ( Res : : ERes : : WOOD ) ;
required_mines . push_back ( Res : : ERes : : ORE ) ;
for ( const auto res : required_mines )
{
auto mine = new CGMine ( ) ;
mine - > ID = Obj : : MINE ;
mine - > subID = static_cast < si32 > ( res ) ;
mine - > producedResource = res ;
mine - > producedQuantity = mine - > defaultResProduction ( ) ;
2014-06-01 17:31:15 +03:00
addRequiredObject ( mine ) ;
2014-05-22 20:25:17 +03:00
}
}
else
{
type = ETemplateZoneType : : TREASURE ;
2014-05-24 15:33:22 +03:00
townId = * RandomGeneratorUtil : : nextItem ( VLC - > townh - > getAllowedFactions ( ) , gen - > rand ) ;
2014-05-22 20:25:17 +03:00
logGlobal - > infoStream ( ) < < " Skipping this zone cause no player " ;
}
}
2014-05-24 15:33:22 +03:00
else //no player
{
townId = * RandomGeneratorUtil : : nextItem ( VLC - > townh - > getAllowedFactions ( ) , gen - > rand ) ;
}
//paint zone with matching terrain
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 ) ;
gen - > editManager - > drawTerrain ( VLC - > townh - > factions [ townId ] - > nativeTerrain , & gen - > rand ) ;
2014-05-22 20:25:17 +03:00
logGlobal - > infoStream ( ) < < " 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-05-24 19:39:58 +03:00
logGlobal - > traceStream ( ) < < " Looking for place " ;
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-05-24 19:39:58 +03:00
logGlobal - > traceStream ( ) < < " Place found " ;
2014-05-22 20:25:17 +03:00
2014-06-01 17:31:15 +03:00
placeObject ( gen , obj . first , pos ) ;
if ( obj . second )
{
2014-06-01 22:01:18 +03:00
guardObject ( gen , obj . first , obj . second ) ;
2014-06-01 17:31:15 +03:00
}
2014-05-22 20:25:17 +03:00
}
const double res_mindist = 5 ;
2014-06-04 11:16:08 +03:00
//TODO: just placeholder to chekc for possible locations
2014-05-22 20:25:17 +03:00
do {
int3 pos ;
2014-06-07 10:09:50 +03:00
if ( ! findPlaceForTreasurePile ( gen , 5 , pos ) )
2014-05-22 20:25:17 +03:00
{
break ;
}
2014-06-04 11:16:08 +03:00
createTreasurePile ( gen , pos ) ;
2014-05-23 20:14:20 +03:00
2014-05-22 20:25:17 +03:00
} while ( true ) ;
auto sel = gen - > editManager - > getTerrainSelection ( ) ;
sel . clearSelection ( ) ;
2014-05-30 22:23:41 +03:00
for ( auto tile : tileinfo )
2014-05-22 20:25:17 +03:00
{
2014-05-31 13:26:37 +03:00
//test code - block all the map to show paths clearly
//if (gen->isPossible(tile))
// gen->setOccupied(tile, ETileType::BLOCKED);
2014-05-30 22:23:41 +03:00
if ( gen - > shouldBeBlocked ( tile ) ) //fill tiles that should be blocked with obstacles
2014-05-22 20:25:17 +03:00
{
auto obj = new CGObjectInstance ( ) ;
obj - > ID = static_cast < Obj > ( 130 ) ;
obj - > subID = 0 ;
2014-05-30 22:23:41 +03:00
placeObject ( gen , obj , tile ) ;
2014-05-22 20:25:17 +03:00
}
}
2014-05-23 20:14:20 +03:00
//logGlobal->infoStream() << boost::format("Filling %d with ROCK") % sel.getSelectedItems().size();
2014-05-22 20:25:17 +03:00
//gen->editManager->drawTerrain(ETerrainType::ROCK, &gen->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-06-07 10:09:50 +03:00
bool CRmgTemplateZone : : findPlaceForTreasurePile ( CMapGenerator * gen , si32 min_dist , int3 & pos )
{
//si32 min_dist = sqrt(tileinfo.size()/density);
int best_distance = 0 ;
bool result = false ;
//logGlobal->infoStream() << boost::format("Min dist for density %f is %d") % density % min_dist;
for ( auto tile : tileinfo )
{
auto dist = gen - > getTile ( tile ) . getNearestObjectDistance ( ) ;
if ( gen - > isPossible ( tile ) & & ( dist > = min_dist ) & & ( dist > best_distance ) )
{
bool allTilesAvailable = true ;
gen - > foreach_neighbour ( tile , [ & gen , & allTilesAvailable ] ( int3 neighbour )
{
if ( ! ( gen - > isPossible ( neighbour ) | | gen - > isBlocked ( neighbour ) ) )
{
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 )
{
gen - > setOccupied ( pos , ETileType : : BLOCKED ) ; //block that tile
}
return result ;
}
2014-05-22 20:25:17 +03:00
bool CRmgTemplateZone : : findPlaceForObject ( CMapGenerator * gen , CGObjectInstance * obj , si32 min_dist , int3 & pos )
{
2014-05-31 13:26:37 +03:00
//we need object apperance to deduce free tiles
if ( obj - > appearance . id = = Obj : : NO_OBJ )
{
auto templates = VLC - > dobjinfo - > pickCandidates ( obj - > ID , obj - > subID , gen - > map - > getTile ( getPos ( ) ) . terType ) ;
if ( templates . empty ( ) )
throw rmgException ( boost : : to_string ( boost : : format ( " Did not find graphics for object (%d,%d) at % s " ) %obj->ID %obj->subID %pos)) ;
obj - > appearance = templates . front ( ) ;
}
2014-05-22 20:25:17 +03:00
//si32 min_dist = sqrt(tileinfo.size()/density);
int best_distance = 0 ;
bool result = false ;
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
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 = obj - > getVisitableOffset ( ) + int3 ( x , y , 0 ) ;
if ( ! vstd : : contains ( tilesBlockedByObject , offset ) )
{
int3 nearbyPos = tile + offset ;
if ( gen - > map - > isInTheMap ( nearbyPos ) )
{
if ( obj - > appearance . isVisitableFrom ( x , y ) & & ! gen - > isBlocked ( nearbyPos ) )
accessible = true ;
}
}
}
} ;
if ( ! accessible )
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-05-31 11:56:14 +03:00
bool allTilesAvailable = true ;
2014-06-07 11:13:59 +03:00
for ( auto blockingTile : tilesBlockedByObject )
2014-05-31 11:56:14 +03:00
{
2014-05-31 13:26:37 +03:00
int3 t = tile + blockingTile ;
if ( ! gen - > map - > isInTheMap ( t ) | | ! gen - > isPossible ( t ) )
2014-05-31 11:56:14 +03:00
{
allTilesAvailable = false ; //if at least one tile is not possible, object can't be placed here
break ;
}
}
if ( allTilesAvailable )
{
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 ) )
throw rmgException ( boost : : to_string ( boost : : format ( " Position of object %d at %s is outside the map " ) % object - > id % object - > 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 )
{
auto templates = VLC - > dobjinfo - > pickCandidates ( object - > ID , object - > subID , gen - > map - > getTile ( pos ) . terType ) ;
if ( templates . empty ( ) )
throw rmgException ( boost : : to_string ( boost : : format ( " Did not find graphics for object (%d,%d) at % s " ) %object->ID %object->subID %pos)) ;
2014-05-23 20:14:20 +03:00
2014-05-31 13:26:37 +03:00
object - > appearance = templates . front ( ) ;
}
2014-05-23 20:14:20 +03:00
gen - > map - > addBlockVisTiles ( object ) ;
2014-05-22 20:25:17 +03:00
gen - > editManager - > insertObject ( object , pos ) ;
2014-05-23 20:14:20 +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
}
void CRmgTemplateZone : : placeObject ( CMapGenerator * gen , CGObjectInstance * object , const int3 & pos )
{
2014-05-24 13:42:06 +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-05-30 22:23:41 +03:00
for ( auto tile : tileinfo )
2014-05-22 20:25:17 +03:00
{
2014-05-30 22:23:41 +03:00
si32 d = pos . dist2d ( tile ) ;
gen - > setNearestObjectDistance ( tile , std : : min ( d , gen - > getNearestObjectDistance ( tile ) ) ) ;
2014-05-22 20:25:17 +03:00
}
}
bool CRmgTemplateZone : : guardObject ( CMapGenerator * gen , CGObjectInstance * object , si32 str )
{
2014-05-24 13:42:06 +03:00
logGlobal - > traceStream ( ) < < boost : : format ( " Guard object at %d %d " ) % object - > pos . x % object - > pos . y ;
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-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
logGlobal - > traceStream ( ) < < boost : : format ( " Block at %d %d " ) % pos . x % pos . y ;
if ( gen - > isPossible ( pos ) )
2014-05-22 20:25:17 +03:00
{
2014-05-30 22:23:41 +03:00
tiles . push_back ( pos ) ;
} ;
} ) ;
2014-05-22 20:25:17 +03:00
if ( ! tiles . size ( ) )
{
logGlobal - > infoStream ( ) < < " Failed " ;
return false ;
}
2014-05-23 20:14:20 +03:00
auto guard_tile = * RandomGeneratorUtil : : nextItem ( tiles , gen - > rand ) ;
2014-05-22 20:25:17 +03:00
2014-06-05 18:19:11 +03:00
if ( addMonster ( gen , guard_tile , str ) ) //do not place obstacles around unguarded object
2014-06-04 21:59:01 +03:00
{
for ( auto pos : tiles )
gen - > setOccupied ( pos , ETileType : : BLOCKED ) ;
gen - > setOccupied ( guard_tile , ETileType : : USED ) ;
}
else
gen - > setOccupied ( guard_tile , ETileType : : FREE ) ;
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
ObjectInfo CRmgTemplateZone : : getRandomObject ( CMapGenerator * gen , ui32 value )
{
std : : vector < std : : pair < ui32 , ObjectInfo > > tresholds ;
ui32 total = 0 ;
ui32 minValue = 0.25f * value ;
//roulette wheel
for ( auto oi : possibleObjects )
{
if ( oi . value > = minValue & & oi . value < = value )
{
2014-06-07 15:02:57 +03:00
//TODO: check place for non-removable object
//problem: we need at least template for the object that does not yet exist
2014-06-05 21:19:42 +03:00
total + = oi . probability ;
tresholds . push_back ( std : : make_pair ( total , oi ) ) ;
}
}
//TODO: generate pandora box with gold if the value is very high
if ( tresholds . empty ( ) )
{
ObjectInfo oi ;
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
return nullptr ;
} ;
oi . value = 0 ;
oi . probability = 0 ;
}
int r = gen - > rand . nextInt ( 1 , total ) ;
for ( auto t : tresholds )
{
if ( r < = t . first )
return t . second ;
}
}
void CRmgTemplateZone : : addAllPossibleObjects ( CMapGenerator * gen )
{
//TODO: move typical objects to config
ObjectInfo oi ;
static const Res : : ERes preciousRes [ ] = { Res : : ERes : : CRYSTAL , Res : : ERes : : GEMS , Res : : ERes : : MERCURY , Res : : ERes : : SULFUR } ;
for ( int i = 0 ; i < 4 ; i + + )
{
oi . generateObject = [ i , gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGResource ( ) ;
obj - > ID = Obj : : RESOURCE ;
obj - > subID = static_cast < si32 > ( preciousRes [ i ] ) ;
obj - > amount = 0 ;
return obj ;
} ;
oi . value = 1400 ;
oi . probability = 300 ;
possibleObjects . push_back ( oi ) ;
}
static const Res : : ERes woodOre [ ] = { Res : : ERes : : WOOD , Res : : ERes : : ORE } ;
for ( int i = 0 ; i < 2 ; i + + )
{
oi . generateObject = [ i , gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGResource ( ) ;
obj - > ID = Obj : : RESOURCE ;
obj - > subID = static_cast < si32 > ( woodOre [ i ] ) ;
obj - > amount = 0 ;
return obj ;
} ;
oi . value = 1400 ;
oi . probability = 300 ;
possibleObjects . push_back ( oi ) ;
}
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGResource ( ) ;
obj - > ID = Obj : : RESOURCE ;
obj - > subID = static_cast < si32 > ( Res : : ERes : : GOLD ) ;
obj - > amount = 0 ;
return obj ;
} ;
oi . value = 750 ;
oi . probability = 300 ;
possibleObjects . push_back ( oi ) ;
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGPickable ( ) ;
obj - > ID = Obj : : TREASURE_CHEST ;
obj - > subID = 0 ;
return obj ;
} ;
oi . value = 1500 ;
oi . probability = 1000 ;
possibleObjects . push_back ( oi ) ;
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGArtifact ( ) ;
obj - > ID = Obj : : RANDOM_TREASURE_ART ;
obj - > subID = 0 ;
auto a = new CArtifactInstance ( ) ;
gen - > map - > addNewArtifactInstance ( a ) ;
obj - > storedArtifact = a ;
return obj ;
} ;
oi . value = 2000 ;
oi . probability = 150 ;
possibleObjects . push_back ( oi ) ;
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGArtifact ( ) ;
obj - > ID = Obj : : RANDOM_MINOR_ART ;
obj - > subID = 0 ;
auto a = new CArtifactInstance ( ) ;
gen - > map - > addNewArtifactInstance ( a ) ;
obj - > storedArtifact = a ;
return obj ;
} ;
oi . value = 5000 ;
oi . probability = 150 ;
possibleObjects . push_back ( oi ) ;
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGArtifact ( ) ;
obj - > ID = Obj : : RANDOM_MAJOR_ART ;
obj - > subID = 0 ;
auto a = new CArtifactInstance ( ) ;
gen - > map - > addNewArtifactInstance ( a ) ;
obj - > storedArtifact = a ;
return obj ;
} ;
oi . value = 10000 ;
oi . probability = 150 ;
possibleObjects . push_back ( oi ) ;
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGArtifact ( ) ;
obj - > ID = Obj : : RANDOM_RELIC_ART ;
obj - > subID = 0 ;
auto a = new CArtifactInstance ( ) ;
gen - > map - > addNewArtifactInstance ( a ) ;
obj - > storedArtifact = a ;
return obj ;
} ;
oi . value = 20000 ;
oi . probability = 150 ;
possibleObjects . push_back ( oi ) ;
static const int scrollValues [ ] = { 500 , 2000 , 3000 , 4000 , 5000 } ;
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?
for ( ui32 i = 0 ; i < gen - > map - > allowedSpell . size ( ) ; i + + ) //spellh size appears to be greater (?)
{
const CSpell * spell = SpellID ( i ) . toSpell ( ) ;
if ( gen - > map - > allowedSpell [ spell - > id ] & & spell - > level = = i + 1 )
{
out . push_back ( spell - > id ) ;
}
}
auto a = CArtifactInstance : : createScroll ( RandomGeneratorUtil : : nextItem ( out , gen - > rand ) - > toSpell ( ) ) ;
gen - > map - > addNewArtifactInstance ( a ) ;
obj - > storedArtifact = a ;
return obj ;
} ;
oi . value = scrollValues [ i ] ;
oi . probability = 30 ;
possibleObjects . push_back ( oi ) ;
}
2014-06-07 15:02:57 +03:00
//non-removable object for test
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGMagicWell ( ) ;
obj - > ID = Obj : : MAGIC_WELL ;
obj - > subID = 0 ;
return obj ;
} ;
oi . value = 250 ;
oi . probability = 100 ;
possibleObjects . push_back ( oi ) ;
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CGObelisk ( ) ;
obj - > ID = Obj : : OBELISK ;
obj - > subID = 0 ;
return obj ;
} ;
oi . value = 3500 ;
oi . probability = 200 ;
possibleObjects . push_back ( oi ) ;
oi . generateObject = [ gen ] ( ) - > CGObjectInstance *
{
auto obj = new CBank ( ) ;
obj - > ID = Obj : : CREATURE_BANK ;
obj - > subID = 5 ; //naga bank
return obj ;
} ;
oi . value = 3000 ;
oi . probability = 100 ;
possibleObjects . push_back ( oi ) ;
2014-06-05 21:19:42 +03:00
}