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:
parent
ae63e0c30e
commit
f3f1fb8033
@ -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;
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user