1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

Improve treasure placement logic (#775)

Improve treasure placement logic (#775)
This commit is contained in:
Nordsoft91 2022-08-20 14:17:27 +04:00 committed by Andrii Danylchenko
parent ae63e0c30e
commit f3f1fb8033
8 changed files with 149 additions and 38 deletions

View File

@ -286,8 +286,8 @@ void CMapGenerator::fillZones()
for(auto it : map->getZones())
{
it.second->initModificators();
it.second->initFreeTiles();
it.second->initModificators();
}
std::vector<std::shared_ptr<Zone>> treasureZones;

View File

@ -224,10 +224,10 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
rmg::Area toPlace(rmgGate1.getArea() + rmgGate1.getAccessibleArea());
toPlace.translate(-zShift);
path2 = managerOther.placeAndConnectObject(toPlace, rmgGate2, minDist, guarded2, true, false);
path2 = managerOther.placeAndConnectObject(toPlace, rmgGate2, minDist, guarded2, true, ObjectManager::OptimizeType::NONE);
return path2.valid() ? 1.f : -1.f;
}, guarded1, true, false);
}, guarded1, true, ObjectManager::OptimizeType::NONE);
if(path1.valid() && path2.valid())
{

View File

@ -34,6 +34,16 @@ void ObjectManager::init()
{
DEPENDENCY(WaterAdopter);
POSTFUNCTION(RoadPlacer);
createDistancesPriorityQueue();
}
void ObjectManager::createDistancesPriorityQueue()
{
tilesByDistance.clear();
for (auto & tile : zone.areaPossible().getTilesVector())
{
tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile)));
}
}
void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength)
@ -53,10 +63,12 @@ void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * n
void ObjectManager::updateDistances(const rmg::Object & obj)
{
tilesByDistance.clear();
for (auto tile : zone.areaPossible().getTiles()) //don't need to mark distance for not possible tiles
{
ui32 d = obj.getArea().distanceSqr(tile); //optimization, only relative distance is interesting
map.setNearestObjectDistance(tile, std::min((float)d, map.getNearestObjectDistance(tile)));
tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile)));
}
}
@ -65,59 +77,102 @@ const rmg::Area & ObjectManager::getVisitableArea() const
return objectsVisitableArea;
}
int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool optimizer) const
int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, OptimizeType optimizer) const
{
float bestWeight = 0.f;
int3 result(-1, -1, -1);
for(const auto & tile : searchArea.getTiles())
if(optimizer & OptimizeType::DISTANCE)
{
obj.setPosition(tile);
if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea()))
continue;
float weight = weightFunction(tile);
if(weight > bestWeight)
auto open = tilesByDistance;
while(!open.empty())
{
bestWeight = weight;
result = tile;
if(!optimizer)
break;
auto node = open.top();
open.pop();
int3 tile = node.first;
if(!searchArea.contains(tile))
continue;
obj.setPosition(tile);
if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea()))
continue;
float weight = weightFunction(tile);
if(weight > bestWeight)
{
bestWeight = weight;
result = tile;
if(!(optimizer & OptimizeType::WEIGHT))
break;
}
}
}
else
{
for(const auto & tile : searchArea.getTiles())
{
obj.setPosition(tile);
if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea()))
continue;
float weight = weightFunction(tile);
if(weight > bestWeight)
{
bestWeight = weight;
result = tile;
if(!(optimizer & OptimizeType::WEIGHT))
break;
}
}
}
if(result.valid())
obj.setPosition(result);
return result;
}
int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool optimizer) const
int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, OptimizeType optimizer) const
{
return findPlaceForObject(searchArea, obj, [this, min_dist](const int3 & tile)
return findPlaceForObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
{
auto ti = map.getTile(tile);
float dist = ti.getNearestObjectDistance();
if(dist < min_dist)
return -1.f;
for(auto & t : obj.getArea().getTilesVector())
{
if(map.getTile(t).getNearestObjectDistance() < min_dist)
return -1.f;
}
return dist;
}, optimizer);
}
rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, bool optimizer) const
rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const
{
return placeAndConnectObject(searchArea, obj, [this, min_dist](const int3 & tile)
return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
{
auto ti = map.getTile(tile);
float dist = ti.getNearestObjectDistance();
if(dist < min_dist)
return -1.f;
for(auto & t : obj.getArea().getTilesVector())
{
if(map.getTile(t).getNearestObjectDistance() < min_dist)
return -1.f;
}
return dist;
}, isGuarded, onlyStraight, optimizer);
}
rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, bool optimizer) const
rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const
{
int3 pos;
auto possibleArea = searchArea;
@ -172,7 +227,7 @@ bool ObjectManager::createRequiredObjects()
rmg::Object rmgObject(*obj);
rmgObject.setTemplate(zone.getTerrainType());
bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY));
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, true);
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
if(!path.valid())
{
@ -217,7 +272,7 @@ bool ObjectManager::createRequiredObjects()
dist *= (dist > 12.f * 12.f) ? 10.f : 1.f; //tiles closer 12 are preferrable
dist = 1000000.f - dist; //some big number
return dist + map.getNearestObjectDistance(tile);
}, guarded, false, true);
}, guarded, false, OptimizeType::WEIGHT);
if(!path.valid())
{

View File

@ -12,13 +12,31 @@
#include "Zone.h"
#include "RmgObject.h"
#include <boost/heap/priority_queue.hpp> //A*
class CGObjectInstance;
class ObjectTemplate;
class CGCreature;
typedef std::pair<int3, float> TDistance;
struct DistanceMaximizeFunctor
{
bool operator()(const TDistance & lhs, const TDistance & rhs) const
{
return (rhs.second > lhs.second);
}
};
class ObjectManager: public Modificator
{
public:
enum OptimizeType
{
NONE = 0x00000000,
WEIGHT = 0x00000001,
DISTANCE = 0x00000010
};
public:
MODIFICATOR(ObjectManager);
@ -31,17 +49,18 @@ public:
bool createRequiredObjects();
int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool optimizer) const;
int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool optimizer) const;
int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, OptimizeType optimizer) const;
int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, OptimizeType optimizer) const;
rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, bool optimizer) const;
rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, bool optimizer) const;
rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const;
rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const;
CGCreature * chooseGuard(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 updateDistances(const rmg::Object & obj);
void createDistancesPriorityQueue();
const rmg::Area & getVisitableArea() const;
@ -53,4 +72,7 @@ protected:
std::vector<std::pair<CGObjectInstance*, CGObjectInstance*>> nearbyObjects;
std::vector<CGObjectInstance*> objects;
rmg::Area objectsVisitableArea;
boost::heap::priority_queue<TDistance, boost::heap::compare<DistanceMaximizeFunctor>> tilesByDistance;
};

View File

@ -142,7 +142,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
{
float distance = zone.getPos().dist2dSQ(t);
return 100000.f - distance; //some big number
}, true);
}, ObjectManager::OptimizeType::WEIGHT);
rmgObject.setPosition(position);
manager.placeObject(rmgObject, false, true);
cleanupBoundaries(rmgObject);

View File

@ -545,7 +545,7 @@ std::vector<ObjectInfo*> TreasurePlacer::prepareTreasurePile(const CTreasureInfo
return objectInfos;
}
rmg::Object TreasurePlacer::constuctTreasurePile(const std::vector<ObjectInfo*> & treasureInfos)
rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*> & treasureInfos, bool densePlacement)
{
rmg::Object rmgObject;
for(auto & oi : treasureInfos)
@ -568,7 +568,32 @@ rmg::Object TreasurePlacer::constuctTreasurePile(const std::vector<ObjectInfo*>
return rmgObject;
}
int3 nextPos = *RandomGeneratorUtil::nextItem(accessibleArea.getTiles(), generator.rand);
std::vector<int3> bestPositions;
if(densePlacement)
{
int bestPositionsWeight = std::numeric_limits<int>::max();
for(auto & t : accessibleArea.getTilesVector())
{
instance.setPosition(t);
int w = rmgObject.getAccessibleArea().getTilesVector().size();
if(w < bestPositionsWeight)
{
bestPositions.clear();
bestPositions.push_back(t);
bestPositionsWeight = w;
}
else if(w == bestPositionsWeight)
{
bestPositions.push_back(t);
}
}
}
else
{
bestPositions = accessibleArea.getTilesVector();
}
int3 nextPos = *RandomGeneratorUtil::nextItem(bestPositions, generator.rand);
instance.setPosition(nextPos - rmgObject.getPosition());
auto instanceAccessibleArea = instance.getAccessibleArea();
@ -637,6 +662,8 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu
void TreasurePlacer::createTreasures(ObjectManager & manager)
{
const int maxAttempts = 2;
int mapMonsterStrength = map.getMapGenOptions().getMonsterStrength();
int monsterStrength = zone.zoneMonsterStrength + mapMonsterStrength - 1; //array index from 0 to 4
@ -684,7 +711,7 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
const float minDistance = std::max<float>((125.f / totalDensity), 2.0f);
//distance lower than 2 causes objects to overlap and crash
while(true)
for(int attempt = 0; attempt <= maxAttempts;)
{
auto treasurePileInfos = prepareTreasurePile(t);
if(treasurePileInfos.empty())
@ -692,7 +719,7 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
int value = std::accumulate(treasurePileInfos.begin(), treasurePileInfos.end(), 0, [](int v, const ObjectInfo * oi){return v + oi->value;});
auto rmgObject = constuctTreasurePile(treasurePileInfos);
auto rmgObject = constructTreasurePile(treasurePileInfos, attempt == maxAttempts);
if(rmgObject.instances().empty()) //handle incorrect placement
{
restoreZoneLimits(treasurePileInfos);
@ -716,6 +743,12 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
if(ti.getNearestObjectDistance() < minDistance)
return -1.f;
for(auto & t : rmgObject.getArea().getTilesVector())
{
if(map.getTile(t).getNearestObjectDistance() < minDistance)
return -1.f;
}
auto guardedArea = rmgObject.instances().back()->getAccessibleArea();
auto areaToBlock = rmgObject.getAccessibleArea(true);
areaToBlock.subtract(guardedArea);
@ -723,11 +756,11 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
return -1.f;
return ti.getNearestObjectDistance();
}, guarded, false, false);
}, guarded, false, ObjectManager::OptimizeType::DISTANCE);
}
else
{
path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, false);
path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE);
}
if(path.valid())
@ -744,12 +777,13 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
}
zone.connectPath(path);
manager.placeObject(rmgObject, guarded, true);
attempt = 0;
}
else
{
restoreZoneLimits(treasurePileInfos);
rmgObject.clear();
break;
++attempt;
}
}
}

View File

@ -52,7 +52,7 @@ protected:
ObjectInfo * getRandomObject(ui32 desiredValue, ui32 currentValue, ui32 maxValue, bool allowLargeObjects);
std::vector<ObjectInfo*> prepareTreasurePile(const CTreasureInfo & treasureInfo);
rmg::Object constuctTreasurePile(const std::vector<ObjectInfo*> & treasureInfos);
rmg::Object constructTreasurePile(const std::vector<ObjectInfo*> & treasureInfos, bool densePlacement = false);
protected:

View File

@ -230,7 +230,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info)
}
//try to place boat at water, create paths on water and land
auto path = manager->placeAndConnectObject(shipPositions, rmgObject, 2, false, true, false);
auto path = manager->placeAndConnectObject(shipPositions, rmgObject, 2, false, true, ObjectManager::OptimizeType::NONE);
auto landPath = land.searchPath(boardingPosition, false);
if(!path.valid() || !landPath.valid())
{
@ -298,7 +298,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, Route
return -1.f;
return 1.0f;
}, guarded, true, false);
}, guarded, true, ObjectManager::OptimizeType::NONE);
//search path to boarding position
auto searchArea = land.areaPossible() - rmgObject.getArea();