1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-21 21:17:49 +02:00

Merge pull request #2297 from vcmi/rmg_roads

Rmg roads
This commit is contained in:
DjWarmonger 2023-07-11 09:25:24 +02:00 committed by GitHub
commit f6cc61e0be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 244 additions and 134 deletions

View File

@ -119,12 +119,11 @@ RandomMapTab::RandomMapTab():
std::string cbRoadType = "selectRoad_" + road->getJsonKey(); std::string cbRoadType = "selectRoad_" + road->getJsonKey();
addCallback(cbRoadType, [&, road](bool on) addCallback(cbRoadType, [&, road](bool on)
{ {
mapGenOptions->setRoadEnabled(road->getJsonKey(), on); mapGenOptions->setRoadEnabled(road->getId(), on);
updateMapInfoByHost(); updateMapInfoByHost();
}); });
} }
build(config); build(config);
updateMapInfoByHost(); updateMapInfoByHost();
@ -313,7 +312,7 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts)
{ {
if(auto w = widget<CToggleButton>(r->getJsonKey())) if(auto w = widget<CToggleButton>(r->getJsonKey()))
{ {
w->setSelected(opts->isRoadEnabled(r->getJsonKey())); w->setSelected(opts->isRoadEnabled(r->getId()));
} }
} }
} }

View File

@ -26,6 +26,9 @@ CMapGenOptions::CMapGenOptions()
waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr) waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr)
{ {
resetPlayersMap(); resetPlayersMap();
setRoadEnabled(RoadId(Road::DIRT_ROAD), true);
setRoadEnabled(RoadId(Road::GRAVEL_ROAD), true);
setRoadEnabled(RoadId(Road::COBBLESTONE_ROAD), true);
} }
si32 CMapGenOptions::getWidth() const si32 CMapGenOptions::getWidth() const
@ -233,17 +236,26 @@ void CMapGenOptions::setMapTemplate(const std::string & name)
setMapTemplate(VLC->tplh->getTemplate(name)); setMapTemplate(VLC->tplh->getTemplate(name));
} }
void CMapGenOptions::setRoadEnabled(const std::string & roadName, bool enable) void CMapGenOptions::setRoadEnabled(const RoadId & roadType, bool enable)
{ {
if(enable) if (enable)
disabledRoads.erase(roadName); {
enabledRoads.insert(roadType);
}
else else
disabledRoads.insert(roadName); {
enabledRoads.erase(roadType);
}
} }
bool CMapGenOptions::isRoadEnabled(const std::string & roadName) const bool CMapGenOptions::isRoadEnabled(const RoadId & roadType) const
{ {
return !disabledRoads.count(roadName); return enabledRoads.count(roadType);
}
bool CMapGenOptions::isRoadEnabled() const
{
return !enabledRoads.empty();
} }
void CMapGenOptions::setPlayerTeam(const PlayerColor & color, const TeamID & team) void CMapGenOptions::setPlayerTeam(const PlayerColor & color, const TeamID & team)

View File

@ -110,8 +110,9 @@ public:
EMonsterStrength::EMonsterStrength getMonsterStrength() const; EMonsterStrength::EMonsterStrength getMonsterStrength() const;
void setMonsterStrength(EMonsterStrength::EMonsterStrength value); void setMonsterStrength(EMonsterStrength::EMonsterStrength value);
bool isRoadEnabled(const std::string & roadName) const; bool isRoadEnabled(const RoadId & roadType) const;
void setRoadEnabled(const std::string & roadName, bool enable); bool isRoadEnabled() const;
void setRoadEnabled(const RoadId & roadType, bool enable);
/// The first player colors belong to standard players and the last player colors belong to comp only players. /// The first player colors belong to standard players and the last player colors belong to comp only players.
/// All standard players are by default of type EPlayerType::AI. /// All standard players are by default of type EPlayerType::AI.
@ -156,7 +157,7 @@ private:
EWaterContent::EWaterContent waterContent; EWaterContent::EWaterContent waterContent;
EMonsterStrength::EMonsterStrength monsterStrength; EMonsterStrength::EMonsterStrength monsterStrength;
std::map<PlayerColor, CPlayerSettings> players; std::map<PlayerColor, CPlayerSettings> players;
std::set<std::string> disabledRoads; std::set<RoadId> enabledRoads;
const CRmgTemplate * mapTemplate; const CRmgTemplate * mapTemplate;
@ -187,7 +188,7 @@ public:
setMapTemplate(templateName); setMapTemplate(templateName);
} }
h & disabledRoads; h & enabledRoads;
} }
} }
}; };

View File

@ -80,11 +80,6 @@ void CMapGenerator::loadConfig()
config.pandoraMultiplierSpells = randomMapJson["pandoras"]["valueMultiplierSpells"].Integer(); config.pandoraMultiplierSpells = randomMapJson["pandoras"]["valueMultiplierSpells"].Integer();
config.pandoraSpellSchool = randomMapJson["pandoras"]["valueSpellSchool"].Integer(); config.pandoraSpellSchool = randomMapJson["pandoras"]["valueSpellSchool"].Integer();
config.pandoraSpell60 = randomMapJson["pandoras"]["valueSpell60"].Integer(); config.pandoraSpell60 = randomMapJson["pandoras"]["valueSpell60"].Integer();
//override config with game options
if(!mapGenOptions.isRoadEnabled(config.secondaryRoadType))
config.secondaryRoadType = "";
if(!mapGenOptions.isRoadEnabled(config.defaultRoadType))
config.defaultRoadType = config.secondaryRoadType;
config.singleThread = randomMapJson["singleThread"].Bool(); config.singleThread = randomMapJson["singleThread"].Bool();
} }

View File

@ -435,11 +435,12 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
} }
} }
ZoneConnection::ZoneConnection() ZoneConnection::ZoneConnection():
: zoneA(-1), zoneA(-1),
zoneB(-1), zoneB(-1),
guardStrength(0), guardStrength(0),
connectionType(EConnectionType::EConnectionType::GUARDED) connectionType(rmg::EConnectionType::GUARDED),
hasRoad(rmg::ERoadOption::ROAD_TRUE)
{ {
} }
@ -475,11 +476,16 @@ int ZoneConnection::getGuardStrength() const
return guardStrength; return guardStrength;
} }
EConnectionType::EConnectionType ZoneConnection::getConnectionType() const rmg::EConnectionType ZoneConnection::getConnectionType() const
{ {
return connectionType; return connectionType;
} }
rmg::ERoadOption ZoneConnection::getRoadOption() const
{
return hasRoad;
}
bool operator==(const ZoneConnection & l, const ZoneConnection & r) bool operator==(const ZoneConnection & l, const ZoneConnection & r)
{ {
return l.zoneA == r.zoneA && l.zoneB == r.zoneB && l.guardStrength == r.guardStrength; return l.zoneA == r.zoneA && l.zoneB == r.zoneB && l.guardStrength == r.guardStrength;
@ -495,10 +501,18 @@ void ZoneConnection::serializeJson(JsonSerializeFormat & handler)
"wide" "wide"
}; };
static const std::vector<std::string> roadOptions =
{
"true",
"false",
"random"
};
handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("a", zoneA, -1); handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("a", zoneA, -1);
handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("b", zoneB, -1); handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("b", zoneB, -1);
handler.serializeInt("guard", guardStrength, 0); handler.serializeInt("guard", guardStrength, 0);
handler.serializeEnum("type", connectionType, connectionTypes); handler.serializeEnum("type", connectionType, connectionTypes);
handler.serializeEnum("road", hasRoad, roadOptions);
} }
} }

View File

@ -67,20 +67,24 @@ public:
void serializeJson(JsonSerializeFormat & handler); void serializeJson(JsonSerializeFormat & handler);
}; };
namespace EConnectionType
{
enum class EConnectionType
{
GUARDED = 0, //default
FICTIVE,
REPULSIVE,
WIDE
};
}
namespace rmg namespace rmg
{ {
enum class EConnectionType
{
GUARDED = 0, //default
FICTIVE,
REPULSIVE,
WIDE
};
enum class ERoadOption
{
ROAD_TRUE,
ROAD_FALSE,
ROAD_RANDOM
};
class DLL_LINKAGE ZoneConnection class DLL_LINKAGE ZoneConnection
{ {
public: public:
@ -91,7 +95,8 @@ public:
TRmgTemplateZoneId getZoneB() const; TRmgTemplateZoneId getZoneB() const;
TRmgTemplateZoneId getOtherZoneId(TRmgTemplateZoneId id) const; TRmgTemplateZoneId getOtherZoneId(TRmgTemplateZoneId id) const;
int getGuardStrength() const; int getGuardStrength() const;
EConnectionType::EConnectionType getConnectionType() const; rmg::EConnectionType getConnectionType() const;
rmg::ERoadOption getRoadOption() const;
void serializeJson(JsonSerializeFormat & handler); void serializeJson(JsonSerializeFormat & handler);
@ -100,7 +105,8 @@ private:
TRmgTemplateZoneId zoneA; TRmgTemplateZoneId zoneA;
TRmgTemplateZoneId zoneB; TRmgTemplateZoneId zoneB;
int guardStrength; int guardStrength;
EConnectionType::EConnectionType connectionType; rmg::EConnectionType connectionType;
rmg::ERoadOption hasRoad;
}; };
class DLL_LINKAGE ZoneOptions class DLL_LINKAGE ZoneOptions

View File

@ -75,7 +75,7 @@ void CZonePlacer::findPathsBetweenZones()
for (auto & connection : connectedZoneIds) for (auto & connection : connectedZoneIds)
{ {
if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) if (connection.getConnectionType() == rmg::EConnectionType::REPULSIVE)
{ {
//Do not consider virtual connections for graph distance //Do not consider virtual connections for graph distance
continue; continue;
@ -536,7 +536,7 @@ void CZonePlacer::attractConnectedZones(TZoneMap & zones, TForceVector & forces,
for (const auto & connection : zone.second->getConnections()) for (const auto & connection : zone.second->getConnections())
{ {
if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) if (connection.getConnectionType() == rmg::EConnectionType::REPULSIVE)
{ {
continue; continue;
} }
@ -625,7 +625,7 @@ void CZonePlacer::separateOverlappingZones(TZoneMap &zones, TForceVector &forces
//TODO: Consider z plane? //TODO: Consider z plane?
for (auto& connection : zone.second->getConnections()) for (auto& connection : zone.second->getConnections())
{ {
if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) if (connection.getConnectionType() == rmg::EConnectionType::REPULSIVE)
{ {
auto & otherZone = zones[connection.getOtherZoneId(zone.second->getId())]; auto & otherZone = zones[connection.getOtherZoneId(zone.second->getId())];
float3 otherZoneCenter = otherZone->getCenter(); float3 otherZoneCenter = otherZone->getCenter();
@ -693,7 +693,7 @@ void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDista
for (const auto& connection : firstZone->getConnections()) for (const auto& connection : firstZone->getConnections())
{ {
//FIXME: Should we also exclude fictive connections? //FIXME: Should we also exclude fictive connections?
if (connection.getConnectionType() != EConnectionType::EConnectionType::REPULSIVE) if (connection.getConnectionType() != rmg::EConnectionType::REPULSIVE)
{ {
connectedZones.insert(connection.getOtherZoneId(firstZone->getId())); connectedZones.insert(connection.getOtherZoneId(firstZone->getId()));
} }
@ -740,7 +740,7 @@ void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDista
float maxDistance = 0; float maxDistance = 0;
for (auto con : misplacedZone->getConnections()) for (auto con : misplacedZone->getConnections())
{ {
if (con.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) if (con.getConnectionType() == rmg::EConnectionType::REPULSIVE)
{ {
continue; continue;
} }

View File

@ -98,6 +98,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
bool success = false; bool success = false;
auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA()); auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA());
auto & otherZone = map.getZones().at(otherZoneId); auto & otherZone = map.getZones().at(otherZoneId);
bool createRoad = shouldGenerateRoad(connection);
//1. Try to make direct connection //1. Try to make direct connection
//Do if it's not prohibited by terrain settings //Do if it's not prohibited by terrain settings
@ -110,7 +111,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
if (directConnectionIterator != dNeighbourZones.end()) if (directConnectionIterator != dNeighbourZones.end())
{ {
if (connection.getConnectionType() == EConnectionType::EConnectionType::WIDE) if (connection.getConnectionType() == rmg::EConnectionType::WIDE)
{ {
for (auto borderPos : directConnectionIterator->second) for (auto borderPos : directConnectionIterator->second)
{ {
@ -159,8 +160,8 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
} }
} }
if (connection.getConnectionType() == EConnectionType::EConnectionType::FICTIVE || if (connection.getConnectionType() == rmg::EConnectionType::FICTIVE ||
connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) connection.getConnectionType() == rmg::EConnectionType::REPULSIVE)
{ {
//Fictive or repulsive connections are not real, take no action //Fictive or repulsive connections are not real, take no action
dCompleted.push_back(connection); dCompleted.push_back(connection);
@ -254,14 +255,17 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
otherZone->getModificator<ObjectManager>()->updateDistances(guardPos); otherZone->getModificator<ObjectManager>()->updateDistances(guardPos);
} }
assert(zone.getModificator<RoadPlacer>()); if (createRoad)
zone.getModificator<RoadPlacer>()->addRoadNode(guardPos); {
assert(zone.getModificator<RoadPlacer>());
zone.getModificator<RoadPlacer>()->addRoadNode(guardPos);
assert(otherZone->getModificator<RoadPlacer>()); assert(otherZone->getModificator<RoadPlacer>());
otherZone->getModificator<RoadPlacer>()->addRoadNode(roadNode); otherZone->getModificator<RoadPlacer>()->addRoadNode(roadNode);
assert(otherZone->getModificator<ConnectionsPlacer>()); assert(otherZone->getModificator<ConnectionsPlacer>());
otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection); otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);
}
success = true; success = true;
} }
@ -274,7 +278,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
{ {
if(generator.getZoneWater() && generator.getZoneWater()->getModificator<WaterProxy>()) if(generator.getZoneWater() && generator.getZoneWater()->getModificator<WaterProxy>())
{ {
if(generator.getZoneWater()->getModificator<WaterProxy>()->waterKeepConnection(connection.getZoneA(), connection.getZoneB())) if(generator.getZoneWater()->getModificator<WaterProxy>()->waterKeepConnection(connection, createRoad))
{ {
assert(otherZone->getModificator<ConnectionsPlacer>()); assert(otherZone->getModificator<ConnectionsPlacer>());
otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection); otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);
@ -293,6 +297,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA()); auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA());
auto & otherZone = map.getZones().at(otherZoneId); auto & otherZone = map.getZones().at(otherZoneId);
bool allowRoad = shouldGenerateRoad(connection);
//3. place subterrain gates //3. place subterrain gates
if(zone.isUnderground() != otherZone->isUnderground()) if(zone.isUnderground() != otherZone->isUnderground())
{ {
@ -341,8 +347,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
zone.connectPath(path1); zone.connectPath(path1);
otherZone->connectPath(path2); otherZone->connectPath(path2);
manager.placeObject(rmgGate1, guarded1, true); manager.placeObject(rmgGate1, guarded1, true, allowRoad);
managerOther.placeObject(rmgGate2, guarded2, true); managerOther.placeObject(rmgGate2, guarded2, true, allowRoad);
assert(otherZone->getModificator<ConnectionsPlacer>()); assert(otherZone->getModificator<ConnectionsPlacer>());
otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection); otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);
@ -359,8 +365,10 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
auto * teleport1 = factory->create(); auto * teleport1 = factory->create();
auto * teleport2 = factory->create(); auto * teleport2 = factory->create();
zone.getModificator<ObjectManager>()->addRequiredObject(teleport1, connection.getGuardStrength()); RequiredObjectInfo obj1(teleport1, connection.getGuardStrength(), allowRoad);
otherZone->getModificator<ObjectManager>()->addRequiredObject(teleport2, connection.getGuardStrength()); RequiredObjectInfo obj2(teleport2, connection.getGuardStrength(), allowRoad);
zone.getModificator<ObjectManager>()->addRequiredObject(obj1);
otherZone->getModificator<ObjectManager>()->addRequiredObject(obj2);
assert(otherZone->getModificator<ConnectionsPlacer>()); assert(otherZone->getModificator<ConnectionsPlacer>());
otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection); otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);
@ -386,6 +394,12 @@ void ConnectionsPlacer::collectNeighbourZones()
} }
} }
bool ConnectionsPlacer::shouldGenerateRoad(const rmg::ZoneConnection& connection) const
{
return connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE ||
(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM && zone.getRand().nextDouble() >= 0.5f);
}
void ConnectionsPlacer::createBorder() void ConnectionsPlacer::createBorder()
{ {
rmg::Area borderArea(zone.getArea().getBorder()); rmg::Area borderArea(zone.getArea().getBorder());
@ -401,7 +415,7 @@ void ConnectionsPlacer::createBorder()
{ {
auto otherZone = connection.getOtherZoneId(zone.getId()); auto otherZone = connection.getOtherZoneId(zone.getId());
if (connection.getConnectionType() == EConnectionType::EConnectionType::WIDE) if (connection.getConnectionType() == rmg::EConnectionType::WIDE)
{ {
auto sharedBorder = borderArea.getSubarea([this, otherZone, &borderOutsideArea](const int3 & t) auto sharedBorder = borderArea.getSubarea([this, otherZone, &borderOutsideArea](const int3 & t)
{ {

View File

@ -29,6 +29,8 @@ public:
void otherSideConnection(const rmg::ZoneConnection & connection); void otherSideConnection(const rmg::ZoneConnection & connection);
void createBorder(); void createBorder();
bool shouldGenerateRoad(const rmg::ZoneConnection& connection) const;
protected: protected:
void collectNeighbourZones(); void collectNeighbourZones();

View File

@ -63,8 +63,11 @@ bool MinePlacer::placeMines(ObjectManager & manager)
createdMines.push_back(mine); createdMines.push_back(mine);
if(!i && (res == EGameResID::WOOD || res == EGameResID::ORE)) if (!i && (res == EGameResID::WOOD || res == EGameResID::ORE))
manager.addCloseObject(mine, rmginfo.value); //only first wood&ore mines are close {
//only first wood & ore mines are close
manager.addCloseObject(RequiredObjectInfo(mine, rmginfo.value));
}
else else
requiredObjects.push_back(std::pair<CGObjectInstance*, ui32>(mine, rmginfo.value)); requiredObjects.push_back(std::pair<CGObjectInstance*, ui32>(mine, rmginfo.value));
} }
@ -74,7 +77,7 @@ bool MinePlacer::placeMines(ObjectManager & manager)
RandomGeneratorUtil::randomShuffle(requiredObjects, zone.getRand()); RandomGeneratorUtil::randomShuffle(requiredObjects, zone.getRand());
for (const auto& obj : requiredObjects) for (const auto& obj : requiredObjects)
{ {
manager.addRequiredObject(obj.first, obj.second); manager.addRequiredObject(RequiredObjectInfo(obj.first, obj.second));
} }
//create extra resources //create extra resources
@ -84,9 +87,13 @@ bool MinePlacer::placeMines(ObjectManager & manager)
{ {
for(int rc = zone.getRand().nextInt(1, extraRes); rc > 0; --rc) for(int rc = zone.getRand().nextInt(1, extraRes); rc > 0; --rc)
{ {
auto * resourse = dynamic_cast<CGResource *>(VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create()); auto * resource = dynamic_cast<CGResource *>(VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create());
resourse->amount = CGResource::RANDOM_AMOUNT; resource->amount = CGResource::RANDOM_AMOUNT;
manager.addNearbyObject(resourse, mine);
RequiredObjectInfo roi;
roi.obj = resource;
roi.nearbyTarget = mine;
manager.addNearbyObject(roi);
} }
} }
} }

View File

@ -57,22 +57,22 @@ void ObjectManager::createDistancesPriorityQueue()
} }
} }
void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength) void ObjectManager::addRequiredObject(const RequiredObjectInfo & info)
{ {
RecursiveLock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
requiredObjects.emplace_back(obj, strength); requiredObjects.emplace_back(info);
} }
void ObjectManager::addCloseObject(CGObjectInstance * obj, si32 strength) void ObjectManager::addCloseObject(const RequiredObjectInfo & info)
{ {
RecursiveLock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
closeObjects.emplace_back(obj, strength); closeObjects.emplace_back(info);
} }
void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget) void ObjectManager::addNearbyObject(const RequiredObjectInfo & info)
{ {
RecursiveLock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
nearbyObjects.emplace_back(obj, nearbyTarget); nearbyObjects.emplace_back(info);
} }
void ObjectManager::updateDistances(const rmg::Object & obj) void ObjectManager::updateDistances(const rmg::Object & obj)
@ -331,13 +331,11 @@ bool ObjectManager::createRequiredObjects()
logGlobal->trace("Creating required objects"); logGlobal->trace("Creating required objects");
//RecursiveLock lock(externalAccessMutex); //Why could requiredObjects be modified during the loop? //RecursiveLock lock(externalAccessMutex); //Why could requiredObjects be modified during the loop?
for(const auto & object : requiredObjects) for(const auto & objInfo : requiredObjects)
{ {
auto * obj = object.first; rmg::Object rmgObject(*objInfo.obj);
//FIXME: Invalid dObject inside object?
rmg::Object rmgObject(*obj);
rmgObject.setTemplate(zone.getTerrainType()); rmgObject.setTemplate(zone.getTerrainType());
bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY)); bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE); auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
@ -349,14 +347,14 @@ bool ObjectManager::createRequiredObjects()
} }
zone.connectPath(path); zone.connectPath(path);
placeObject(rmgObject, guarded, true); placeObject(rmgObject, guarded, true, objInfo.createRoad);
for(const auto & nearby : nearbyObjects) for(const auto & nearby : nearbyObjects)
{ {
if(nearby.second != obj) if(nearby.nearbyTarget != nearby.obj)
continue; continue;
rmg::Object rmgNearObject(*nearby.first); rmg::Object rmgNearObject(*nearby.obj);
rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside()); rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside());
possibleArea.intersect(zone.areaPossible()); possibleArea.intersect(zone.areaPossible());
if(possibleArea.empty()) if(possibleArea.empty())
@ -366,21 +364,18 @@ bool ObjectManager::createRequiredObjects()
} }
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), zone.getRand())); rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), zone.getRand()));
placeObject(rmgNearObject, false, false); placeObject(rmgNearObject, false, false, nearby.createRoad);
} }
} }
for(const auto & object : closeObjects) for(const auto & objInfo : closeObjects)
{ {
auto * obj = object.first;
//TODO: Wrap into same area proxy?
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
auto possibleArea = zone.areaPossible(); auto possibleArea = zone.areaPossible();
rmg::Object rmgObject(*obj); rmg::Object rmgObject(*objInfo.obj);
rmgObject.setTemplate(zone.getTerrainType()); rmgObject.setTemplate(zone.getTerrainType());
bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY)); bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, auto path = placeAndConnectObject(zone.areaPossible(), rmgObject,
[this, &rmgObject](const int3 & tile) [this, &rmgObject](const int3 & tile)
{ {
@ -401,10 +396,10 @@ bool ObjectManager::createRequiredObjects()
for(const auto & nearby : nearbyObjects) for(const auto & nearby : nearbyObjects)
{ {
if(nearby.second != obj) if(nearby.nearbyTarget != objInfo.obj)
continue; continue;
rmg::Object rmgNearObject(*nearby.first); rmg::Object rmgNearObject(*nearby.obj);
rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside()); rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside());
possibleArea.intersect(zone.areaPossible()); possibleArea.intersect(zone.areaPossible());
if(possibleArea.empty()) if(possibleArea.empty())
@ -420,10 +415,10 @@ bool ObjectManager::createRequiredObjects()
//create object on specific positions //create object on specific positions
//TODO: implement guards //TODO: implement guards
for (const auto &obj : instantObjects) for (const auto &objInfo : instantObjects)
{ {
rmg::Object rmgObject(*obj.first); rmg::Object rmgObject(*objInfo.obj);
rmgObject.setPosition(obj.second); rmgObject.setPosition(objInfo.pos);
placeObject(rmgObject, false, false); placeObject(rmgObject, false, false);
} }
@ -435,7 +430,7 @@ bool ObjectManager::createRequiredObjects()
return true; return true;
} }
void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance) void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/)
{ {
object.finalize(map); object.finalize(map);
@ -493,21 +488,23 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
} }
} }
switch(object.instances().front()->object().ID) if (createRoad)
{ {
case Obj::TOWN: if (auto* m = zone.getModificator<RoadPlacer>())
case Obj::RANDOM_TOWN: m->addRoadNode(object.instances().front()->getVisitablePosition());
case Obj::MONOLITH_TWO_WAY: }
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
case Obj::MONOLITH_ONE_WAY_EXIT:
case Obj::SUBTERRANEAN_GATE:
case Obj::SHIPYARD:
if(auto * m = zone.getModificator<RoadPlacer>())
m->addRoadNode(object.instances().front()->getVisitablePosition());
break;
//TODO: Add road node to these objects:
/*
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
case Obj::RANDOM_TOWN:
case Obj::MONOLITH_ONE_WAY_EXIT:
*/
switch (object.instances().front()->object().ID)
{
case Obj::WATER_WHEEL: case Obj::WATER_WHEEL:
if(auto * m = zone.getModificator<RiverPlacer>()) if (auto* m = zone.getModificator<RiverPlacer>())
m->addRiverNode(object.instances().front()->getVisitablePosition()); m->addRiverNode(object.instances().front()->getVisitablePosition());
break; break;
@ -613,4 +610,20 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard
return true; return true;
} }
RequiredObjectInfo::RequiredObjectInfo():
obj(nullptr),
nearbyTarget(nullptr),
guardStrength(0),
createRoad(true)
{}
RequiredObjectInfo::RequiredObjectInfo(CGObjectInstance* obj, ui32 guardStrength, bool createRoad, CGObjectInstance* nearbyTarget):
obj(obj),
nearbyTarget(nearbyTarget),
guardStrength(guardStrength),
createRoad(createRoad)
{}
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -29,6 +29,18 @@ struct DistanceMaximizeFunctor
} }
}; };
struct RequiredObjectInfo
{
RequiredObjectInfo();
RequiredObjectInfo(CGObjectInstance* obj, ui32 guardStrength = 0, bool createRoad = false, CGObjectInstance* nearbyTarget = nullptr);
CGObjectInstance* obj;
CGObjectInstance* nearbyTarget;
int3 pos;
ui32 guardStrength;
bool createRoad;
};
class ObjectManager: public Modificator class ObjectManager: public Modificator
{ {
public: public:
@ -45,9 +57,9 @@ public:
void process() override; void process() override;
void init() override; void init() override;
void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0); void addRequiredObject(const RequiredObjectInfo & info);
void addCloseObject(CGObjectInstance * obj, si32 guardStrength = 0); void addCloseObject(const RequiredObjectInfo & info);
void addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget); void addNearbyObject(const RequiredObjectInfo & info);
bool createRequiredObjects(); bool createRequiredObjects();
@ -59,7 +71,7 @@ public:
CGCreature * chooseGuard(si32 strength, bool zoneGuard = false); CGCreature * chooseGuard(si32 strength, bool zoneGuard = false);
bool addGuard(rmg::Object & object, si32 strength, bool zoneGuard = false); bool addGuard(rmg::Object & object, si32 strength, bool zoneGuard = false);
void placeObject(rmg::Object & object, bool guarded, bool updateDistance); void placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad = false);
void updateDistances(const rmg::Object & obj); void updateDistances(const rmg::Object & obj);
void updateDistances(const int3& pos); void updateDistances(const int3& pos);
@ -72,10 +84,10 @@ public:
protected: protected:
//content info //content info
std::vector<std::pair<CGObjectInstance*, ui32>> requiredObjects; std::vector<RequiredObjectInfo> requiredObjects;
std::vector<std::pair<CGObjectInstance*, ui32>> closeObjects; std::vector<RequiredObjectInfo> closeObjects;
std::vector<std::pair<CGObjectInstance*, int3>> instantObjects; std::vector<RequiredObjectInfo> instantObjects;
std::vector<std::pair<CGObjectInstance*, CGObjectInstance*>> nearbyObjects; std::vector<RequiredObjectInfo> nearbyObjects;
std::vector<CGObjectInstance*> objects; std::vector<CGObjectInstance*> objects;
rmg::Area objectsVisitableArea; rmg::Area objectsVisitableArea;

View File

@ -80,24 +80,40 @@ bool RoadPlacer::createRoad(const int3 & dst)
void RoadPlacer::drawRoads(bool secondary) void RoadPlacer::drawRoads(bool secondary)
{ {
if((secondary && generator.getConfig().secondaryRoadType.empty())
|| (!secondary && generator.getConfig().defaultRoadType.empty()))
return;
//RecursiveLock lock(externalAccessMutex);
{ {
//FIXME: double lock - unsafe //Clean space under roads even if they won't be eventually generated
Zone::Lock lock(zone.areaMutex); Zone::Lock lock(zone.areaMutex);
zone.areaPossible().subtract(roads); zone.areaPossible().subtract(roads);
zone.freePaths().unite(roads); zone.freePaths().unite(roads);
} }
if (!generator.getMapGenOptions().isRoadEnabled())
{
return;
}
if((secondary && generator.getConfig().secondaryRoadType.empty())
|| (!secondary && generator.getConfig().defaultRoadType.empty()))
return;
//TODO: Allow custom road type for object
//TODO: Remove these default types
auto tiles = roads.getTilesVector(); auto tiles = roads.getTilesVector();
std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType); std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType);
RoadId roadType(*VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "road", roadName)); RoadId roadType(*VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "road", roadName));
mapProxy->drawRoads(zone.getRand(), tiles, roadType);
//If our road type is not enabled, choose highest below it
for (int8_t bestRoad = roadType.getNum(); bestRoad > RoadId(Road::NO_ROAD).getNum(); bestRoad--)
{
if (generator.getMapGenOptions().isRoadEnabled(RoadId(bestRoad)))
{
mapProxy->drawRoads(zone.getRand(), tiles, RoadId(bestRoad));
return;
}
}
} }
void RoadPlacer::addRoadNode(const int3& node) void RoadPlacer::addRoadNode(const int3& node)

View File

@ -152,7 +152,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
}, ObjectManager::OptimizeType::WEIGHT); }, ObjectManager::OptimizeType::WEIGHT);
} }
rmgObject.setPosition(position + int3(2, 2, 0)); //place visitable tile in the exact center of a zone rmgObject.setPosition(position + int3(2, 2, 0)); //place visitable tile in the exact center of a zone
manager.placeObject(rmgObject, false, true); manager.placeObject(rmgObject, false, true, true);
cleanupBoundaries(rmgObject); cleanupBoundaries(rmgObject);
zone.setPos(rmgObject.getVisitablePosition()); //roads lead to main town zone.setPos(rmgObject.getVisitablePosition()); //roads lead to main town
return position; return position;
@ -216,7 +216,9 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player
placeMainTown(manager, *town); placeMainTown(manager, *town);
} }
else else
manager.addRequiredObject(town); {
manager.addRequiredObject(RequiredObjectInfo(town, 0, true));
}
totalTowns++; totalTowns++;
} }
} }

View File

@ -134,6 +134,8 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
if(adopter->getCoastTiles().empty()) if(adopter->getCoastTiles().empty())
return result; return result;
bool createRoad = false;
//block zones are not connected by template //block zones are not connected by template
for(auto& lake : lakes) for(auto& lake : lakes)
{ {
@ -163,16 +165,22 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
if(auto * m = dst.getModificator<TownPlacer>()) if(auto * m = dst.getModificator<TownPlacer>())
zoneTowns = m->getTotalTowns(); zoneTowns = m->getTotalTowns();
if (vstd::contains(lake.keepRoads, dst.getId()))
{
createRoad = true;
}
//FIXME: Why are Shipyards not allowed in zones with no towns?
if(dst.getType() == ETemplateZoneType::PLAYER_START || dst.getType() == ETemplateZoneType::CPU_START || zoneTowns) if(dst.getType() == ETemplateZoneType::PLAYER_START || dst.getType() == ETemplateZoneType::CPU_START || zoneTowns)
{ {
if(placeShipyard(dst, lake, generator.getConfig().shipyardGuard, result)) if(placeShipyard(dst, lake, generator.getConfig().shipyardGuard, createRoad, result))
{ {
logGlobal->info("Shipyard successfully placed at zone %d", dst.getId()); logGlobal->info("Shipyard successfully placed at zone %d", dst.getId());
} }
else else
{ {
logGlobal->warn("Shipyard placement failed, trying boat at zone %d", dst.getId()); logGlobal->warn("Shipyard placement failed, trying boat at zone %d", dst.getId());
if(placeBoat(dst, lake, result)) if(placeBoat(dst, lake, createRoad, result))
{ {
logGlobal->warn("Boat successfully placed at zone %d", dst.getId()); logGlobal->warn("Boat successfully placed at zone %d", dst.getId());
} }
@ -184,7 +192,7 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
} }
else else
{ {
if(placeBoat(dst, lake, result)) if(placeBoat(dst, lake, createRoad, result))
{ {
logGlobal->info("Boat successfully placed at zone %d", dst.getId()); logGlobal->info("Boat successfully placed at zone %d", dst.getId());
} }
@ -199,21 +207,29 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
return result; return result;
} }
bool WaterProxy::waterKeepConnection(TRmgTemplateZoneId zoneA, TRmgTemplateZoneId zoneB) bool WaterProxy::waterKeepConnection(const rmg::ZoneConnection & connection, bool createRoad)
{ {
const auto & zoneA = connection.getZoneA();
const auto & zoneB = connection.getZoneB();
for(auto & lake : lakes) for(auto & lake : lakes)
{ {
if(lake.neighbourZones.count(zoneA) && lake.neighbourZones.count(zoneB)) if(lake.neighbourZones.count(zoneA) && lake.neighbourZones.count(zoneB))
{ {
lake.keepConnections.insert(zoneA); lake.keepConnections.insert(zoneA);
lake.keepConnections.insert(zoneB); lake.keepConnections.insert(zoneB);
if (createRoad)
{
lake.keepRoads.insert(zoneA);
lake.keepRoads.insert(zoneB);
}
return true; return true;
} }
} }
return false; return false;
} }
bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info) bool WaterProxy::placeBoat(Zone & land, const Lake & lake, bool createRoad, RouteInfo & info)
{ {
auto * manager = zone.getModificator<ObjectManager>(); auto * manager = zone.getModificator<ObjectManager>();
if(!manager) if(!manager)
@ -284,7 +300,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info)
zone.connectPath(path); zone.connectPath(path);
land.connectPath(landPath); land.connectPath(landPath);
manager->placeObject(rmgObject, false, true); manager->placeObject(rmgObject, false, true, createRoad);
land.getModificator<ObjectManager>()->updateDistances(rmgObject); //Keep land objects away from the boat land.getModificator<ObjectManager>()->updateDistances(rmgObject); //Keep land objects away from the boat
break; break;
} }
@ -292,7 +308,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info)
return !boardingPositions.empty(); return !boardingPositions.empty();
} }
bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, RouteInfo & info) bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool createRoad, RouteInfo & info)
{ {
auto * manager = land.getModificator<ObjectManager>(); auto * manager = land.getModificator<ObjectManager>();
if(!manager) if(!manager)
@ -372,7 +388,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, Route
info.boarding = boardingPosition; info.boarding = boardingPosition;
info.water = shipPositions; info.water = shipPositions;
manager->placeObject(rmgObject, guarded, true); manager->placeObject(rmgObject, guarded, true, createRoad);
zone.areaPossible().subtract(shipyardOutToBlock); zone.areaPossible().subtract(shipyardOutToBlock);
for(const auto & i : shipyardOutToBlock.getTilesVector()) for(const auto & i : shipyardOutToBlock.getTilesVector())

View File

@ -34,9 +34,10 @@ public:
std::map<int, rmg::Tileset> reverseDistanceMap; std::map<int, rmg::Tileset> reverseDistanceMap;
std::map<TRmgTemplateZoneId, rmg::Area> neighbourZones; //zones boardered. Area - part of land std::map<TRmgTemplateZoneId, rmg::Area> neighbourZones; //zones boardered. Area - part of land
std::set<TRmgTemplateZoneId> keepConnections; std::set<TRmgTemplateZoneId> keepConnections;
std::set<TRmgTemplateZoneId> keepRoads;
}; };
bool waterKeepConnection(TRmgTemplateZoneId zoneA, TRmgTemplateZoneId zoneB); bool waterKeepConnection(const rmg::ZoneConnection & connection, bool createRoad);
RouteInfo waterRoute(Zone & dst); RouteInfo waterRoute(Zone & dst);
void process() override; void process() override;
@ -47,8 +48,8 @@ public:
protected: protected:
void collectLakes(); void collectLakes();
bool placeShipyard(Zone & land, const Lake & lake, si32 guard, RouteInfo & info); bool placeShipyard(Zone & land, const Lake & lake, si32 guard, bool createRoad, RouteInfo & info);
bool placeBoat(Zone & land, const Lake & lake, RouteInfo & info); bool placeBoat(Zone & land, const Lake & lake, bool createRoad, RouteInfo & info);
protected: protected:
std::vector<Lake> lakes; //disconnected parts of zone. Used to work with water zones std::vector<Lake> lakes; //disconnected parts of zone. Used to work with water zones