2022-08-09 09:54:32 +04:00
/*
* ConnectionsPlacer . 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 "ConnectionsPlacer.h"
2023-05-20 10:17:37 +02:00
# include "../CMapGenerator.h"
# include "../RmgMap.h"
# include "../../TerrainHandler.h"
2023-06-02 21:47:37 +03:00
# include "../../mapObjectConstructors/AObjectTypeHandler.h"
# include "../../mapObjectConstructors/CObjectClassesHandler.h"
2023-06-17 15:21:42 +03:00
# include "../../mapObjects/CGCreature.h"
2023-05-20 10:17:37 +02:00
# include "../../mapping/CMapEditManager.h"
# include "../RmgObject.h"
2022-08-09 09:54:32 +04:00
# include "ObjectManager.h"
2023-05-20 10:17:37 +02:00
# include "../Functions.h"
2022-08-09 09:54:32 +04:00
# include "RoadPlacer.h"
2023-05-20 10:17:37 +02:00
# include "../TileInfo.h"
2022-08-09 09:54:32 +04:00
# include "WaterAdopter.h"
# include "WaterProxy.h"
# include "TownPlacer.h"
2024-06-01 15:28:17 +00:00
# include <vstd/RNG.h>
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_BEGIN
2024-03-27 06:16:48 +01:00
std : : pair < Zone : : Lock , Zone : : Lock > ConnectionsPlacer : : lockZones ( std : : shared_ptr < Zone > otherZone )
{
if ( zone . getId ( ) = = otherZone - > getId ( ) )
return { } ;
while ( true )
{
2025-02-28 15:25:58 +01:00
auto lock1 = Zone : : Lock ( zone . areaMutex , std : : try_to_lock ) ;
auto lock2 = Zone : : Lock ( otherZone - > areaMutex , std : : try_to_lock ) ;
2024-03-27 06:16:48 +01:00
if ( lock1 . owns_lock ( ) & & lock2 . owns_lock ( ) )
{
return { std : : move ( lock1 ) , std : : move ( lock2 ) } ;
}
}
}
2022-08-09 09:54:32 +04:00
void ConnectionsPlacer : : process ( )
{
collectNeighbourZones ( ) ;
2023-05-20 07:06:27 +02:00
auto diningPhilosophers = [ this ] ( std : : function < void ( const rmg : : ZoneConnection & ) > f )
2022-08-09 09:54:32 +04:00
{
2023-05-20 07:06:27 +02:00
for ( auto & c : dConnections )
{
2024-07-29 21:56:07 +02:00
if ( c . getZoneA ( ) = = c . getZoneB ( ) )
{
// Zone can always be connected to itself, but only by monolith pair
RecursiveLock lock ( externalAccessMutex ) ;
if ( ! vstd : : contains ( dCompleted , c ) )
{
placeMonolithConnection ( c ) ;
2024-07-30 08:50:33 +02:00
continue ;
2024-07-29 21:56:07 +02:00
}
}
2023-05-20 07:06:27 +02:00
auto otherZone = map . getZones ( ) . at ( c . getZoneB ( ) ) ;
auto * cp = otherZone - > getModificator < ConnectionsPlacer > ( ) ;
while ( cp )
{
2025-02-28 15:25:58 +01:00
RecursiveLock lock1 ( externalAccessMutex , std : : try_to_lock ) ;
RecursiveLock lock2 ( cp - > externalAccessMutex , std : : try_to_lock ) ;
2023-05-20 07:06:27 +02:00
if ( lock1 . owns_lock ( ) & & lock2 . owns_lock ( ) )
{
if ( ! vstd : : contains ( dCompleted , c ) )
{
f ( c ) ;
}
break ;
}
}
}
} ;
2024-07-29 20:36:23 +02:00
diningPhilosophers ( [ this ] ( const rmg : : ZoneConnection & c )
{
forcePortalConnection ( c ) ;
} ) ;
2023-05-20 07:06:27 +02:00
diningPhilosophers ( [ this ] ( const rmg : : ZoneConnection & c )
{
2023-05-20 07:55:28 +02:00
selfSideDirectConnection ( c ) ;
2023-05-20 07:06:27 +02:00
} ) ;
2023-05-20 07:55:28 +02:00
createBorder ( ) ;
2023-05-20 07:06:27 +02:00
diningPhilosophers ( [ this ] ( const rmg : : ZoneConnection & c )
2022-08-09 09:54:32 +04:00
{
2023-05-20 07:55:28 +02:00
selfSideIndirectConnection ( c ) ;
2023-05-20 07:06:27 +02:00
} ) ;
2022-08-09 09:54:32 +04:00
}
void ConnectionsPlacer : : init ( )
{
DEPENDENCY ( WaterAdopter ) ;
DEPENDENCY ( TownPlacer ) ;
POSTFUNCTION ( RoadPlacer ) ;
POSTFUNCTION ( ObjectManager ) ;
2025-03-14 14:37:33 +01:00
for ( auto c : zone . getConnections ( ) )
2024-03-27 06:16:48 +01:00
{
2025-03-14 14:37:33 +01:00
addConnection ( c ) ;
2024-03-27 06:16:48 +01:00
}
2022-08-09 09:54:32 +04:00
}
void ConnectionsPlacer : : addConnection ( const rmg : : ZoneConnection & connection )
{
dConnections . push_back ( connection ) ;
}
void ConnectionsPlacer : : otherSideConnection ( const rmg : : ZoneConnection & connection )
{
dCompleted . push_back ( connection ) ;
}
2024-07-29 20:36:23 +02:00
void ConnectionsPlacer : : forcePortalConnection ( const rmg : : ZoneConnection & connection )
{
// This should always succeed
if ( connection . getConnectionType ( ) = = rmg : : EConnectionType : : FORCE_PORTAL )
{
placeMonolithConnection ( connection ) ;
}
}
2022-08-09 09:54:32 +04:00
void ConnectionsPlacer : : selfSideDirectConnection ( const rmg : : ZoneConnection & connection )
{
bool success = false ;
2024-11-23 10:43:14 +01:00
auto otherZoneId = connection . getOtherZoneId ( zone . getId ( ) ) ;
2022-08-09 09:54:32 +04:00
auto & otherZone = map . getZones ( ) . at ( otherZoneId ) ;
2023-07-07 21:27:24 +02:00
bool createRoad = shouldGenerateRoad ( connection ) ;
2022-08-09 09:54:32 +04:00
//1. Try to make direct connection
//Do if it's not prohibited by terrain settings
2025-02-14 16:23:37 +00:00
const auto * ourTerrain = LIBRARY - > terrainTypeHandler - > getById ( zone . getTerrainType ( ) ) ;
const auto * otherTerrain = LIBRARY - > terrainTypeHandler - > getById ( otherZone - > getTerrainType ( ) ) ;
2023-01-17 22:58:22 +02:00
bool directProhibited = vstd : : contains ( ourTerrain - > prohibitTransitions , otherZone - > getTerrainType ( ) )
| | vstd : : contains ( otherTerrain - > prohibitTransitions , zone . getTerrainType ( ) ) ;
2024-03-27 06:16:48 +01:00
auto lock = lockZones ( otherZone ) ;
2022-08-09 09:54:32 +04:00
auto directConnectionIterator = dNeighbourZones . find ( otherZoneId ) ;
2023-06-14 14:12:42 +02:00
2023-06-17 19:09:38 +02:00
if ( directConnectionIterator ! = dNeighbourZones . end ( ) )
{
2023-07-07 16:32:17 +02:00
if ( connection . getConnectionType ( ) = = rmg : : EConnectionType : : WIDE )
2023-06-17 19:09:38 +02:00
{
for ( auto borderPos : directConnectionIterator - > second )
{
//TODO: Refactor common code with direct connection
2024-03-27 06:16:48 +01:00
int3 potentialPos = zone . areaPossible ( ) - > nearest ( borderPos ) ;
2023-06-17 19:09:38 +02:00
assert ( borderPos ! = potentialPos ) ;
auto safetyGap = rmg : : Area ( { potentialPos } ) ;
safetyGap . unite ( safetyGap . getBorderOutside ( ) ) ;
2024-03-27 06:16:48 +01:00
safetyGap . intersect ( zone . areaPossible ( ) . get ( ) ) ;
2023-06-17 19:09:38 +02:00
if ( ! safetyGap . empty ( ) )
{
2024-03-27 06:16:48 +01:00
safetyGap . intersect ( otherZone - > areaPossible ( ) . get ( ) ) ;
2023-06-17 19:09:38 +02:00
if ( safetyGap . empty ( ) )
{
2024-03-27 06:16:48 +01:00
rmg : : Area border ( zone . area ( ) - > getBorder ( ) ) ;
border . unite ( otherZone - > area ( ) - > getBorder ( ) ) ;
2023-06-17 19:09:38 +02:00
auto costFunction = [ & border ] ( const int3 & s , const int3 & d )
{
return 1.f / ( 1.f + border . distanceSqr ( d ) ) ;
} ;
2024-11-22 09:28:37 +01:00
auto ourArea = zone . areaForRoads ( ) ;
auto theirArea = otherZone - > areaForRoads ( ) ;
2023-06-17 19:09:38 +02:00
theirArea . add ( potentialPos ) ;
rmg : : Path ourPath ( ourArea ) ;
rmg : : Path theirPath ( theirArea ) ;
2024-03-27 06:16:48 +01:00
ourPath . connect ( zone . freePaths ( ) . get ( ) ) ;
2023-06-17 19:09:38 +02:00
ourPath = ourPath . search ( potentialPos , true , costFunction ) ;
2024-03-27 06:16:48 +01:00
theirPath . connect ( otherZone - > freePaths ( ) . get ( ) ) ;
2023-06-17 19:09:38 +02:00
theirPath = theirPath . search ( potentialPos , true , costFunction ) ;
if ( ourPath . valid ( ) & & theirPath . valid ( ) )
{
zone . connectPath ( ourPath ) ;
otherZone - > connectPath ( theirPath ) ;
2023-06-18 19:20:12 +02:00
otherZone - > getModificator < ObjectManager > ( ) - > updateDistances ( potentialPos ) ;
2023-06-17 19:09:38 +02:00
success = true ;
break ;
}
}
}
}
}
}
2023-07-07 16:32:17 +02:00
if ( connection . getConnectionType ( ) = = rmg : : EConnectionType : : FICTIVE | |
connection . getConnectionType ( ) = = rmg : : EConnectionType : : REPULSIVE )
2023-06-18 12:03:41 +02:00
{
//Fictive or repulsive connections are not real, take no action
dCompleted . push_back ( connection ) ;
return ;
}
2023-06-14 14:12:42 +02:00
float maxDist = - 10e6 ;
2023-06-17 19:09:38 +02:00
if ( ! success & & ! directProhibited & & directConnectionIterator ! = dNeighbourZones . end ( ) )
2022-08-09 09:54:32 +04:00
{
int3 guardPos ( - 1 , - 1 , - 1 ) ;
2023-06-14 14:12:42 +02:00
int3 roadNode ;
for ( auto borderPos : directConnectionIterator - > second )
2022-08-09 09:54:32 +04:00
{
2024-03-27 06:16:48 +01:00
int3 potentialPos = zone . areaPossible ( ) - > nearest ( borderPos ) ;
2023-06-14 14:12:42 +02:00
assert ( borderPos ! = potentialPos ) ;
2023-03-16 18:42:28 +01:00
2024-01-22 09:27:30 +01:00
//Check if guard pos doesn't touch any 3rd zone. This would create unwanted passage to 3rd zone
bool adjacentZone = false ;
map . foreach_neighbour ( potentialPos , [ this , & adjacentZone , otherZoneId ] ( int3 & pos )
{
auto zoneId = map . getZoneID ( pos ) ;
if ( zoneId ! = zone . getId ( ) & & zoneId ! = otherZoneId )
{
adjacentZone = true ;
}
} ) ;
if ( adjacentZone )
{
continue ;
}
2023-06-14 14:12:42 +02:00
//Take into account distance to objects from both sides
float dist = std : : min ( map . getTileInfo ( potentialPos ) . getNearestObjectDistance ( ) ,
map . getTileInfo ( borderPos ) . getNearestObjectDistance ( ) ) ;
if ( dist > 3 ) //Don't place guards at adjacent tiles
2022-08-09 09:54:32 +04:00
{
2023-03-16 18:42:28 +01:00
2023-06-14 14:12:42 +02:00
auto safetyGap = rmg : : Area ( { potentialPos } ) ;
2023-03-16 18:42:28 +01:00
safetyGap . unite ( safetyGap . getBorderOutside ( ) ) ;
2024-03-27 06:16:48 +01:00
safetyGap . intersect ( zone . areaPossible ( ) . get ( ) ) ;
2023-03-16 18:42:28 +01:00
if ( ! safetyGap . empty ( ) )
{
2024-03-27 06:16:48 +01:00
safetyGap . intersect ( otherZone - > areaPossible ( ) . get ( ) ) ;
2023-03-16 18:42:28 +01:00
if ( safetyGap . empty ( ) )
2023-06-14 14:12:42 +02:00
{
float distanceToCenter = zone . getPos ( ) . dist2d ( potentialPos ) * otherZone - > getPos ( ) . dist2d ( potentialPos ) ;
auto localDist = ( dist - distanceToCenter ) * //Prefer close to zone center
( std : : max ( distanceToCenter , dist ) / std : : min ( distanceToCenter , dist ) ) ;
//Distance to center dominates and is negative, so imbalanced proportions will result in huge penalty
if ( localDist > maxDist )
{
maxDist = localDist ;
guardPos = potentialPos ;
roadNode = borderPos ;
}
}
2023-03-16 18:42:28 +01:00
}
2022-08-09 09:54:32 +04:00
}
}
2025-02-28 19:56:43 +00:00
if ( guardPos . isValid ( ) )
2022-08-09 09:54:32 +04:00
{
assert ( zone . getModificator < ObjectManager > ( ) ) ;
auto & manager = * zone . getModificator < ObjectManager > ( ) ;
auto * monsterType = manager . chooseGuard ( connection . getGuardStrength ( ) , true ) ;
2024-11-21 22:00:24 +01:00
2024-03-27 06:16:48 +01:00
rmg : : Area border ( zone . area ( ) - > getBorder ( ) ) ;
border . unite ( otherZone - > area ( ) - > getBorder ( ) ) ;
2024-11-21 22:00:24 +01:00
auto localCostFunction = rmg : : Path : : createCurvedCostFunction ( zone . area ( ) - > getBorder ( ) ) ;
auto otherCostFunction = rmg : : Path : : createCurvedCostFunction ( otherZone - > area ( ) - > getBorder ( ) ) ;
2024-11-22 09:28:37 +01:00
auto ourArea = zone . areaForRoads ( ) ;
auto theirArea = otherZone - > areaForRoads ( ) ;
2022-08-09 09:54:32 +04:00
theirArea . add ( guardPos ) ;
2023-02-11 19:05:02 +03:00
rmg : : Path ourPath ( ourArea ) ;
rmg : : Path theirPath ( theirArea ) ;
2024-03-27 06:16:48 +01:00
ourPath . connect ( zone . freePaths ( ) . get ( ) ) ;
2024-11-21 22:00:24 +01:00
ourPath = ourPath . search ( guardPos , true , localCostFunction ) ;
2024-03-27 06:16:48 +01:00
theirPath . connect ( otherZone - > freePaths ( ) . get ( ) ) ;
2024-11-21 22:00:24 +01:00
theirPath = theirPath . search ( guardPos , true , otherCostFunction ) ;
2022-08-09 09:54:32 +04:00
if ( ourPath . valid ( ) & & theirPath . valid ( ) )
{
zone . connectPath ( ourPath ) ;
otherZone - > connectPath ( theirPath ) ;
if ( monsterType )
{
rmg : : Object monster ( * monsterType ) ;
monster . setPosition ( guardPos ) ;
2023-07-08 09:11:20 +02:00
manager . placeObject ( monster , false , true ) ;
2023-03-16 16:20:11 +01:00
//Place objects away from the monster in the other zone, too
otherZone - > getModificator < ObjectManager > ( ) - > updateDistances ( monster ) ;
2022-08-09 09:54:32 +04:00
}
else
{
2023-06-23 21:43:49 +02:00
//Update distances from empty passage, too
2024-03-27 06:16:48 +01:00
zone . areaPossible ( ) - > erase ( guardPos ) ;
zone . freePaths ( ) - > add ( guardPos ) ;
2022-08-09 09:54:32 +04:00
map . setOccupied ( guardPos , ETileType : : FREE ) ;
2023-06-23 21:43:49 +02:00
manager . updateDistances ( guardPos ) ;
otherZone - > getModificator < ObjectManager > ( ) - > updateDistances ( guardPos ) ;
2022-08-09 09:54:32 +04:00
}
2023-07-07 21:27:24 +02:00
if ( createRoad )
2023-07-06 22:15:00 +02:00
{
assert ( zone . getModificator < RoadPlacer > ( ) ) ;
zone . getModificator < RoadPlacer > ( ) - > addRoadNode ( guardPos ) ;
assert ( otherZone - > getModificator < RoadPlacer > ( ) ) ;
otherZone - > getModificator < RoadPlacer > ( ) - > addRoadNode ( roadNode ) ;
}
2024-11-23 10:43:14 +01:00
assert ( otherZone - > getModificator < ConnectionsPlacer > ( ) ) ;
otherZone - > getModificator < ConnectionsPlacer > ( ) - > otherSideConnection ( connection ) ;
2023-07-06 22:15:00 +02:00
2022-08-09 09:54:32 +04:00
success = true ;
}
}
}
//2. connect via water
bool waterMode = map . getMapGenOptions ( ) . getWaterContent ( ) ! = EWaterContent : : NONE ;
if ( waterMode & & zone . isUnderground ( ) = = otherZone - > isUnderground ( ) )
{
if ( generator . getZoneWater ( ) & & generator . getZoneWater ( ) - > getModificator < WaterProxy > ( ) )
{
2023-07-07 21:27:24 +02:00
if ( generator . getZoneWater ( ) - > getModificator < WaterProxy > ( ) - > waterKeepConnection ( connection , createRoad ) )
2022-08-09 09:54:32 +04:00
{
assert ( otherZone - > getModificator < ConnectionsPlacer > ( ) ) ;
otherZone - > getModificator < ConnectionsPlacer > ( ) - > otherSideConnection ( connection ) ;
success = true ;
}
}
}
if ( success )
dCompleted . push_back ( connection ) ;
}
void ConnectionsPlacer : : selfSideIndirectConnection ( const rmg : : ZoneConnection & connection )
{
bool success = false ;
auto otherZoneId = ( connection . getZoneA ( ) = = zone . getId ( ) ? connection . getZoneB ( ) : connection . getZoneA ( ) ) ;
auto & otherZone = map . getZones ( ) . at ( otherZoneId ) ;
2023-07-06 22:15:00 +02:00
bool allowRoad = shouldGenerateRoad ( connection ) ;
2022-08-09 09:54:32 +04:00
//3. place subterrain gates
if ( zone . isUnderground ( ) ! = otherZone - > isUnderground ( ) )
{
int3 zShift ( 0 , 0 , zone . getPos ( ) . z - otherZone - > getPos ( ) . z ) ;
2023-12-18 10:55:40 +01:00
2024-03-27 06:16:48 +01:00
auto lock = lockZones ( otherZone ) ;
2023-12-18 10:55:40 +01:00
std : : scoped_lock doubleLock ( zone . areaMutex , otherZone - > areaMutex ) ;
2024-03-27 06:16:48 +01:00
auto commonArea = zone . areaPossible ( ) . get ( ) * ( otherZone - > areaPossible ( ) . get ( ) + zShift ) ;
2022-08-09 09:54:32 +04:00
if ( ! commonArea . empty ( ) )
{
assert ( zone . getModificator < ObjectManager > ( ) ) ;
auto & manager = * zone . getModificator < ObjectManager > ( ) ;
assert ( otherZone - > getModificator < ObjectManager > ( ) ) ;
auto & managerOther = * otherZone - > getModificator < ObjectManager > ( ) ;
2025-02-14 16:23:37 +00:00
auto factory = LIBRARY - > objtypeh - > getHandlerFor ( Obj : : SUBTERRANEAN_GATE , 0 ) ;
2024-01-01 16:37:48 +02:00
auto * gate1 = factory - > create ( map . mapInstance - > cb , nullptr ) ;
auto * gate2 = factory - > create ( map . mapInstance - > cb , nullptr ) ;
2023-02-11 19:05:02 +03:00
rmg : : Object rmgGate1 ( * gate1 ) ;
rmg : : Object rmgGate2 ( * gate2 ) ;
2023-09-30 01:19:18 +02:00
rmgGate1 . setTemplate ( zone . getTerrainType ( ) , zone . getRand ( ) ) ;
rmgGate2 . setTemplate ( otherZone - > getTerrainType ( ) , zone . getRand ( ) ) ;
2022-08-09 09:54:32 +04:00
bool guarded1 = manager . addGuard ( rmgGate1 , connection . getGuardStrength ( ) , true ) ;
bool guarded2 = managerOther . addGuard ( rmgGate2 , connection . getGuardStrength ( ) , true ) ;
int minDist = 3 ;
2024-03-27 06:16:48 +01:00
rmg : : Path path2 ( otherZone - > area ( ) . get ( ) ) ;
2022-08-09 09:54:32 +04:00
rmg : : Path path1 = manager . placeAndConnectObject ( commonArea , rmgGate1 , [ this , minDist , & path2 , & rmgGate1 , & zShift , guarded2 , & managerOther , & rmgGate2 ] ( const int3 & tile )
{
2023-05-19 20:30:15 +02:00
auto ti = map . getTileInfo ( tile ) ;
auto otherTi = map . getTileInfo ( tile - zShift ) ;
2022-08-09 09:54:32 +04:00
float dist = ti . getNearestObjectDistance ( ) ;
2023-03-16 18:42:28 +01:00
float otherDist = otherTi . getNearestObjectDistance ( ) ;
if ( dist < minDist | | otherDist < minDist )
2022-08-09 09:54:32 +04:00
return - 1.f ;
2023-07-18 09:53:42 +02:00
//This could fail is accessibleArea is below the map
2023-10-31 16:04:21 +01:00
rmg : : Area toPlace ( rmgGate1 . getArea ( ) ) ;
toPlace . unite ( toPlace . getBorderOutside ( ) ) ; // Add a bit of extra space around
toPlace . erase_if ( [ this ] ( const int3 & tile )
2023-07-18 09:53:42 +02:00
{
2023-10-31 16:04:21 +01:00
return ! map . isOnMap ( tile ) ;
} ) ;
2022-08-09 09:54:32 +04:00
toPlace . translate ( - zShift ) ;
2022-08-20 14:17:27 +04:00
path2 = managerOther . placeAndConnectObject ( toPlace , rmgGate2 , minDist , guarded2 , true , ObjectManager : : OptimizeType : : NONE ) ;
2022-08-09 09:54:32 +04:00
2023-04-12 09:31:09 +02:00
return path2 . valid ( ) ? ( dist * otherDist ) : - 1.f ;
2023-03-16 18:42:28 +01:00
} , guarded1 , true , ObjectManager : : OptimizeType : : DISTANCE ) ;
2022-08-09 09:54:32 +04:00
if ( path1 . valid ( ) & & path2 . valid ( ) )
{
2023-07-07 20:17:20 +02:00
manager . placeObject ( rmgGate1 , guarded1 , true , allowRoad ) ;
managerOther . placeObject ( rmgGate2 , guarded2 , true , allowRoad ) ;
2024-11-22 09:28:37 +01:00
replaceWithCurvedPath ( path1 , zone , rmgGate1 . getVisitablePosition ( ) ) ;
replaceWithCurvedPath ( path2 , * otherZone , rmgGate2 . getVisitablePosition ( ) ) ;
zone . connectPath ( path1 ) ;
otherZone - > connectPath ( path2 ) ;
2022-08-09 09:54:32 +04:00
assert ( otherZone - > getModificator < ConnectionsPlacer > ( ) ) ;
otherZone - > getModificator < ConnectionsPlacer > ( ) - > otherSideConnection ( connection ) ;
success = true ;
}
}
}
//4. place monoliths/portals
if ( ! success )
{
2024-07-29 20:36:23 +02:00
placeMonolithConnection ( connection ) ;
2022-08-09 09:54:32 +04:00
}
2024-07-29 20:36:23 +02:00
}
void ConnectionsPlacer : : placeMonolithConnection ( const rmg : : ZoneConnection & connection )
{
auto otherZoneId = ( connection . getZoneA ( ) = = zone . getId ( ) ? connection . getZoneB ( ) : connection . getZoneA ( ) ) ;
auto & otherZone = map . getZones ( ) . at ( otherZoneId ) ;
bool allowRoad = shouldGenerateRoad ( connection ) ;
2025-02-14 16:23:37 +00:00
auto factory = LIBRARY - > objtypeh - > getHandlerFor ( Obj : : MONOLITH_TWO_WAY , generator . getNextMonlithIndex ( ) ) ;
2024-07-29 20:36:23 +02:00
auto * teleport1 = factory - > create ( map . mapInstance - > cb , nullptr ) ;
auto * teleport2 = factory - > create ( map . mapInstance - > cb , nullptr ) ;
RequiredObjectInfo obj1 ( teleport1 , connection . getGuardStrength ( ) , allowRoad ) ;
RequiredObjectInfo obj2 ( teleport2 , connection . getGuardStrength ( ) , allowRoad ) ;
zone . getModificator < ObjectManager > ( ) - > addRequiredObject ( obj1 ) ;
otherZone - > getModificator < ObjectManager > ( ) - > addRequiredObject ( obj2 ) ;
2024-07-29 21:56:07 +02:00
dCompleted . push_back ( connection ) ;
2024-07-29 20:36:23 +02:00
assert ( otherZone - > getModificator < ConnectionsPlacer > ( ) ) ;
otherZone - > getModificator < ConnectionsPlacer > ( ) - > otherSideConnection ( connection ) ;
2022-08-09 09:54:32 +04:00
}
void ConnectionsPlacer : : collectNeighbourZones ( )
{
2024-03-27 06:16:48 +01:00
auto border = zone . area ( ) - > getBorderOutside ( ) ;
2023-02-11 19:05:02 +03:00
for ( const auto & i : border )
2022-08-09 09:54:32 +04:00
{
if ( ! map . isOnMap ( i ) )
continue ;
auto zid = map . getZoneID ( i ) ;
assert ( zid ! = zone . getId ( ) ) ;
dNeighbourZones [ zid ] . insert ( i ) ;
}
}
2022-07-26 16:07:42 +03:00
2023-07-06 22:15:00 +02:00
bool ConnectionsPlacer : : shouldGenerateRoad ( const rmg : : ZoneConnection & connection ) const
{
2025-03-14 14:37:33 +01:00
if ( connection . getRoadOption ( ) = = rmg : : ERoadOption : : ROAD_RANDOM )
logGlobal - > error ( " Random road between zones %d and %d " , connection . getZoneA ( ) , connection . getZoneB ( ) ) ;
else
logGlobal - > info ( " Should generate road between zones %d and %d: %d " , connection . getZoneA ( ) , connection . getZoneB ( ) , connection . getRoadOption ( ) = = rmg : : ERoadOption : : ROAD_TRUE ) ;
2025-03-14 11:03:28 +01:00
return connection . getRoadOption ( ) = = rmg : : ERoadOption : : ROAD_TRUE ;
2023-07-06 22:15:00 +02:00
}
2023-05-20 07:55:28 +02:00
void ConnectionsPlacer : : createBorder ( )
{
2024-03-27 06:16:48 +01:00
rmg : : Area borderArea ( zone . area ( ) - > getBorder ( ) ) ;
rmg : : Area borderOutsideArea ( zone . area ( ) - > getBorderOutside ( ) ) ;
2023-05-20 07:55:28 +02:00
auto blockBorder = borderArea . getSubarea ( [ this , & borderOutsideArea ] ( const int3 & t )
{
auto tile = borderOutsideArea . nearest ( t ) ;
return map . isOnMap ( tile ) & & map . getZones ( ) [ map . getZoneID ( tile ) ] - > getType ( ) ! = ETemplateZoneType : : WATER ;
} ) ;
2023-06-17 19:09:38 +02:00
//No border for wide connections
for ( auto & connection : zone . getConnections ( ) ) // We actually placed that connection already
{
auto otherZone = connection . getOtherZoneId ( zone . getId ( ) ) ;
2023-07-07 16:32:17 +02:00
if ( connection . getConnectionType ( ) = = rmg : : EConnectionType : : WIDE )
2023-06-17 19:09:38 +02:00
{
2023-06-17 19:31:16 +02:00
auto sharedBorder = borderArea . getSubarea ( [ this , otherZone , & borderOutsideArea ] ( const int3 & t )
2023-06-17 19:09:38 +02:00
{
auto tile = borderOutsideArea . nearest ( t ) ;
return map . isOnMap ( tile ) & & map . getZones ( ) [ map . getZoneID ( tile ) ] - > getId ( ) = = otherZone ;
} ) ;
blockBorder . subtract ( sharedBorder ) ;
}
} ;
2024-03-27 06:16:48 +01:00
auto areaPossible = zone . areaPossible ( ) ;
2023-05-20 07:55:28 +02:00
for ( const auto & tile : blockBorder . getTilesVector ( ) )
{
if ( map . isPossible ( tile ) )
{
map . setOccupied ( tile , ETileType : : BLOCKED ) ;
2024-03-27 06:16:48 +01:00
areaPossible - > erase ( tile ) ;
2023-05-20 07:55:28 +02:00
}
2024-03-27 06:16:48 +01:00
map . foreachDirectNeighbour ( tile , [ this , & areaPossible ] ( int3 & nearbyPos )
2023-05-20 07:55:28 +02:00
{
if ( map . isPossible ( nearbyPos ) & & map . getZoneID ( nearbyPos ) = = zone . getId ( ) )
{
map . setOccupied ( nearbyPos , ETileType : : BLOCKED ) ;
2024-03-27 06:16:48 +01:00
areaPossible - > erase ( nearbyPos ) ;
2023-05-20 07:55:28 +02:00
}
} ) ;
}
}
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_END