diff --git a/lib/VCMI_lib.vcxproj b/lib/VCMI_lib.vcxproj
index 6fcb2f534..18e150ee1 100644
--- a/lib/VCMI_lib.vcxproj
+++ b/lib/VCMI_lib.vcxproj
@@ -321,6 +321,7 @@
+
diff --git a/lib/VCMI_lib.vcxproj.filters b/lib/VCMI_lib.vcxproj.filters
index f16a9ec91..4650865c0 100644
--- a/lib/VCMI_lib.vcxproj.filters
+++ b/lib/VCMI_lib.vcxproj.filters
@@ -396,5 +396,8 @@
Header Files
+
+ rmg
+
-
+
\ No newline at end of file
diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp
index 7a3330c7b..afb7c8040 100644
--- a/lib/rmg/CMapGenerator.cpp
+++ b/lib/rmg/CMapGenerator.cpp
@@ -12,6 +12,7 @@
#include "../filesystem/Filesystem.h"
#include "CRmgTemplate.h"
#include "CRmgTemplateZone.h"
+#include "CZonePlacer.h"
CMapGenerator::CMapGenerator(shared_ptr mapGenOptions, int randomSeed /*= std::time(nullptr)*/) :
mapGenOptions(mapGenOptions), randomSeed(randomSeed)
@@ -26,10 +27,10 @@ CMapGenerator::~CMapGenerator()
std::unique_ptr CMapGenerator::generate()
{
- mapGenOptions->finalize(rand);
+ mapGenOptions->finalize(rand);
- map = make_unique();
- editManager = map->getEditManager();
+ map = make_unique();
+ editManager = map->getEditManager();
try
{
editManager->getUndoManager().setUndoRedoLimit(0);
@@ -144,13 +145,16 @@ void CMapGenerator::genZones()
auto tmpl = mapGenOptions->getMapTemplate();
- auto zones = tmpl->getZones();
+ zones = tmpl->getZones(); //copy from template (refactor?)
int player_per_side = zones.size() > 4 ? 3 : 2;
int zones_cnt = zones.size() > 4 ? 9 : 4;
logGlobal->infoStream() << boost::format("Map size %d %d, players per side %d") % w % h % player_per_side;
+ CZonePlacer placer(this);
+ placer.placeZones(mapGenOptions, &rand);
+
int i = 0;
int part_w = w/player_per_side;
int part_h = h/player_per_side;
@@ -192,4 +196,9 @@ void CMapGenerator::addHeaderInfo()
map->description = getMapDescription();
map->difficulty = 1;
addPlayerInfo();
+}
+
+std::map CMapGenerator::getZones() const
+{
+ return zones;
}
\ No newline at end of file
diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h
index c91d9e95c..badbf267d 100644
--- a/lib/rmg/CMapGenerator.h
+++ b/lib/rmg/CMapGenerator.h
@@ -62,6 +62,8 @@ public:
int randomSeed;
CMapEditManager * editManager;
+ std::map getZones() const;
+
private:
std::map zones;
diff --git a/lib/rmg/CRmgTemplateStorage.cpp b/lib/rmg/CRmgTemplateStorage.cpp
index 38af91529..ec91e6304 100644
--- a/lib/rmg/CRmgTemplateStorage.cpp
+++ b/lib/rmg/CRmgTemplateStorage.cpp
@@ -81,6 +81,16 @@ void CJsonRmgTemplateLoader::loadTemplates()
connections.push_back(conn);
}
tpl->setConnections(connections);
+ {
+ auto zones = tpl->getZones();
+ for (auto con : tpl->getConnections())
+ {
+ auto idA = con.getZoneA()->getId();
+ auto idB = con.getZoneB()->getId();
+ zones[idA]->addConnection(idB);
+ zones[idB]->addConnection(idA);
+ }
+ }
tpl->validate();
templates[tpl->getName()] = tpl;
}
diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp
index bcebe8ea9..26e76f12e 100644
--- a/lib/rmg/CRmgTemplateZone.cpp
+++ b/lib/rmg/CRmgTemplateZone.cpp
@@ -272,6 +272,26 @@ void CRmgTemplateZone::setTownTypeLikeZone(boost::optional v
townTypeLikeZone = value;
}
+void CRmgTemplateZone::addConnection(TRmgTemplateZoneId otherZone)
+{
+ connections.push_back (otherZone);
+}
+
+std::vector CRmgTemplateZone::getConnections() const
+{
+ return connections;
+}
+float3 CRmgTemplateZone::getCenter() const
+{
+ return center;
+}
+void CRmgTemplateZone::setCenter(float3 f)
+{
+ //limit boundaries to (0,1) square
+ center = float3 (std::min(std::max(f.x, 0.f), 1.f), std::min(std::max(f.y, 0.f), 1.f), f.z);
+}
+
+
bool CRmgTemplateZone::pointIsIn(int x, int y)
{
int i, j;
@@ -316,21 +336,26 @@ void CRmgTemplateZone::setShape(std::vector shape)
}
}
-int3 CRmgTemplateZone::getCenter()
+int3 CRmgTemplateZone::getPos()
{
- si32 cx = 0;
- si32 cy = 0;
- si32 area = 0;
- si32 sz = shape.size();
- //include last->first too
- for(si32 i = 0, j = sz-1; i < sz; j = i++) {
- si32 sf = (shape[i].x * shape[j].y - shape[j].x * shape[i].y);
- cx += (shape[i].x + shape[j].x) * sf;
- cy += (shape[i].y + shape[j].y) * sf;
- area += sf;
- }
- area /= 2;
- return int3(std::abs(cx/area/6), std::abs(cy/area/6), shape[0].z);
+ //si32 cx = 0;
+ //si32 cy = 0;
+ //si32 area = 0;
+ //si32 sz = shape.size();
+ ////include last->first too
+ //for(si32 i = 0, j = sz-1; i < sz; j = i++) {
+ // si32 sf = (shape[i].x * shape[j].y - shape[j].x * shape[i].y);
+ // cx += (shape[i].x + shape[j].x) * sf;
+ // cy += (shape[i].y + shape[j].y) * sf;
+ // area += sf;
+ //}
+ //area /= 2;
+ //return int3(std::abs(cx/area/6), std::abs(cy/area/6), shape[0].z);
+ return pos;
+}
+void CRmgTemplateZone::setPos(int3 Pos)
+{
+ pos = Pos;
}
bool CRmgTemplateZone::fill(CMapGenerator* gen)
@@ -356,7 +381,7 @@ bool CRmgTemplateZone::fill(CMapGenerator* gen)
town->builtBuildings.insert(BuildingID::FORT);
town->builtBuildings.insert(BuildingID::DEFAULT);
- placeObject(gen, town, getCenter());
+ placeObject(gen, town, getPos());
logGlobal->infoStream() << "Placed object";
logGlobal->infoStream() << "Fill player info " << player_id;
@@ -512,7 +537,7 @@ void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance*
void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos)
{
- logGlobal->infoStream() << boost::format("Inserting object at %d %d") % pos.x % pos.y;
+ logGlobal->traceStream() << boost::format("Inserting object at %d %d") % pos.x % pos.y;
checkAndPlaceObject (gen, object, pos);
@@ -537,7 +562,7 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str)
{
- logGlobal->infoStream() << boost::format("Guard object at %d %d") % object->pos.x % object->pos.y;
+ logGlobal->traceStream() << boost::format("Guard object at %d %d") % object->pos.x % object->pos.y;
int3 visitable = object->visitablePos();
std::vector tiles;
for(int i = -1; i < 2; ++i)
diff --git a/lib/rmg/CRmgTemplateZone.h b/lib/rmg/CRmgTemplateZone.h
index 1f94f8ac9..0272c122f 100644
--- a/lib/rmg/CRmgTemplateZone.h
+++ b/lib/rmg/CRmgTemplateZone.h
@@ -13,6 +13,7 @@
#include "../GameConstants.h"
#include "CMapGenerator.h"
+#include "float3.h"
class CMapgenerator;
@@ -98,10 +99,20 @@ public:
void setTerrainTypeLikeZone(boost::optional value);
boost::optional getTownTypeLikeZone() const;
void setTownTypeLikeZone(boost::optional value);
+
+ float3 getCenter() const;
+ void setCenter(float3 f);
+ int3 getPos();
+ void setPos(int3 pos);
+
void setShape(std::vector shape);
bool fill(CMapGenerator* gen);
+ void addConnection(TRmgTemplateZoneId otherZone);
+ std::vector getConnections() const;
+
private:
+ //template info
TRmgTemplateZoneId id;
ETemplateZoneType::ETemplateZoneType type;
int size;
@@ -113,11 +124,17 @@ private:
std::set terrainTypes;
boost::optional terrainTypeLikeZone, townTypeLikeZone;
- std::vector shape;
- std::map tileinfo;
+ //content info
+ std::vector shape; //TODO: remove
std::vector objects;
- int3 getCenter();
+ //placement info
+ int3 pos;
+ float3 center;
+ std::map tileinfo; //irregular area assined to zone
+ std::vector connections; //list of adjacent zones
+ std::map alreadyConnected; //TODO: allow multiple connections between two zones?
+
bool pointIsIn(int x, int y);
bool findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos);
void checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos);
diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp
index 43c4d85e5..52e58f2ad 100644
--- a/lib/rmg/CZonePlacer.cpp
+++ b/lib/rmg/CZonePlacer.cpp
@@ -10,16 +10,20 @@
*/
#include "StdInc.h"
+#include "../CRandomGenerator.h"
#include "CZonePlacer.h"
+#include "CRmgTemplateZone.h"
#include "CZoneGraphGenerator.h"
-CPlacedZone::CPlacedZone(const CRmgTemplateZone * zone)// : zone(zone)
+class CRandomGenerator;
+
+CPlacedZone::CPlacedZone(const CRmgTemplateZone * zone) : zone(zone)
{
}
-CZonePlacer::CZonePlacer()// : map(nullptr), gen(nullptr)
+CZonePlacer::CZonePlacer(CMapGenerator * Gen) : gen(Gen)
{
}
@@ -29,7 +33,85 @@ CZonePlacer::~CZonePlacer()
}
-void CZonePlacer::placeZones(CMap * map, unique_ptr graph, CRandomGenerator * gen)
+int3 CZonePlacer::cords (float3 f) const
{
-
+ return int3(f.x * gen->map->width, f.y * gen->map->height, f.z);
+}
+
+void CZonePlacer::placeZones(shared_ptr mapGenOptions, CRandomGenerator * rand)
+{
+ //some relaxation-simmulated annealing algorithm
+
+ const int iterations = 5;
+ float temperature = 1;
+ const float temperatureModifier = 0.9;
+
+ logGlobal->infoStream() << "Starting zone placement";
+
+ int width = mapGenOptions->getWidth();
+ int height = mapGenOptions->getHeight();
+
+ auto zones = gen->getZones();
+
+ //TODO: consider underground zones
+
+ float totalSize = 0;
+ for (auto zone : zones)
+ {
+ totalSize += zone.second->getSize();
+ zone.second->setCenter (float3(rand->nextDouble(0,1), rand->nextDouble(0,1), 0));
+ }
+ //prescale zones
+ float prescaler = sqrt (width * height / totalSize) / 3.14f; //let's assume we try to fit N circular zones with radius = size on a map
+ float mapSize = sqrt (width * height);
+ for (auto zone : zones)
+ {
+ zone.second->setSize (zone.second->getSize() * prescaler);
+ }
+
+ for (int i = 0; i < iterations; ++i)
+ {
+ for (auto zone : zones)
+ {
+ //attract connected zones
+ for (auto con : zone.second->getConnections())
+ {
+ auto otherZone = zones[con];
+ float distance = zone.second->getCenter().dist2d (otherZone->getCenter());
+ float minDistance = (zone.second->getSize() + otherZone->getSize())/mapSize; //scale down to (0,1) coordinates
+ if (distance > minDistance)
+ {
+ //attract our zone
+ float scaler = (distance - minDistance)/distance * temperature; //positive
+ auto positionVector = (otherZone->getCenter() - zone.second->getCenter()); //positive value
+ zone.second->setCenter (zone.second->getCenter() + positionVector * scaler); //positive movement
+ }
+ }
+ }
+ for (auto zone : zones)
+ {
+ //separate overlaping zones
+ for (auto otherZone : zones)
+ {
+ if (zone == otherZone)
+ continue;
+
+ float distance = zone.second->getCenter().dist2d (otherZone.second->getCenter());
+ float minDistance = (zone.second->getSize() + otherZone.second->getSize())/mapSize;
+ if (distance < minDistance)
+ {
+ //move our zone away
+ float scaler = (distance ? (distance - minDistance)/distance : 1) * temperature; //negative
+ auto positionVector = (otherZone.second->getCenter() - zone.second->getCenter()); //positive value
+ zone.second->setCenter (zone.second->getCenter() + positionVector * scaler); //negative movement
+ }
+ }
+ }
+ temperature *= temperatureModifier;
+ }
+ for (auto zone : zones) //finalize zone positions
+ {
+ zone.second->setPos(cords(zone.second->getCenter()));
+ logGlobal->infoStream() << boost::format ("Placed zone %d at relative position %s and coordinates %s") % zone.first % zone.second->getCenter() % zone.second->getPos();
+ }
}
diff --git a/lib/rmg/CZonePlacer.h b/lib/rmg/CZonePlacer.h
index 7ba721f26..648adb428 100644
--- a/lib/rmg/CZonePlacer.h
+++ b/lib/rmg/CZonePlacer.h
@@ -11,35 +11,41 @@
#pragma once
+#include "CMapGenerator.h"
+#include "../mapping/CMap.h"
+
+#include "float3.h"
+#include "../int3.h"
+
class CZoneGraph;
class CMap;
class CRandomGenerator;
class CRmgTemplateZone;
+class CMapGenerator;
class CPlacedZone
{
public:
- explicit CPlacedZone(const CRmgTemplateZone * zone);
+ explicit CPlacedZone(const CRmgTemplateZone * Zone);
private:
- //const CRmgTemplateZone * zone;
+ const CRmgTemplateZone * zone;
//TODO exact outline data of zone
//TODO perhaps further zone data, guards, obstacles, etc...
};
-//TODO add voronoi helper classes(?), etc...
-
class CZonePlacer
{
public:
- CZonePlacer();
+ explicit CZonePlacer(CMapGenerator * gen);
+ int3 cords (float3 f) const;
~CZonePlacer();
- void placeZones(CMap * map, unique_ptr graph, CRandomGenerator * gen);
+ void placeZones(shared_ptr mapGenOptions, CRandomGenerator * rand);
private:
//CMap * map;
- unique_ptr graph;
- //CRandomGenerator * gen;
+ //unique_ptr graph;
+ CMapGenerator * gen;
};
diff --git a/lib/rmg/float3.h b/lib/rmg/float3.h
new file mode 100644
index 000000000..36f6feba0
--- /dev/null
+++ b/lib/rmg/float3.h
@@ -0,0 +1,132 @@
+#pragma once
+
+/*
+ * float3.h, 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
+ *
+ */
+
+/// Class which consists of three float values. Represents position virtual RMG (0;1) area.
+class float3
+{
+public:
+ float x, y;
+ si32 z;
+ inline float3():x(0),y(0),z(0){}; //c-tor, x/y/z initialized to 0
+ inline float3(const float X, const float Y, const si32 Z):x(X),y(Y),z(Z){}; //c-tor
+ inline float3(const float3 & val) : x(val.x), y(val.y), z(val.z){} //copy c-tor
+ inline float3 & operator=(const float3 & val) {x = val.x; y = val.y; z = val.z; return *this;} //assignemt operator
+ ~float3() {} // d-tor - does nothing
+ inline float3 operator+(const float3 & i) const //returns float3 with coordinates increased by corresponding coordinate of given float3
+ {return float3(x+i.x,y+i.y,z+i.z);}
+ inline float3 operator+(const float i) const //returns float3 with coordinates increased by given numer
+ {return float3(x+i,y+i,z+i);}
+ inline float3 operator-(const float3 & i) const //returns float3 with coordinates decreased by corresponding coordinate of given float3
+ {return float3(x-i.x,y-i.y,z-i.z);}
+ inline float3 operator-(const float i) const //returns float3 with coordinates decreased by given numer
+ {return float3(x-i,y-i,z-i);}
+ inline float3 operator*(const float i) const //returns float3 with plane coordinates decreased by given numer
+ {return float3(x*i, y*i, z);}
+ inline float3 operator/(const float i) const //returns float3 with plane coordinates decreased by given numer
+ {return float3(x/i, y/i, z);}
+ inline float3 operator-() const //returns opposite position
+ {return float3(-x,-y,-z);}
+ inline double dist2d(const float3 &other) const //distance (z coord is not used)
+ {return std::sqrt((double)(x-other.x)*(x-other.x) + (y-other.y)*(y-other.y));}
+ inline bool areNeighbours(const float3 &other) const
+ {return dist2d(other) < 2. && z == other.z;}
+ inline void operator+=(const float3 & i)
+ {
+ x+=i.x;
+ y+=i.y;
+ z+=i.z;
+ }
+ inline void operator+=(const float & i)
+ {
+ x+=i;
+ y+=i;
+ z+=i;
+ }
+ inline void operator-=(const float3 & i)
+ {
+ x-=i.x;
+ y-=i.y;
+ z-=i.z;
+ }
+ inline void operator-=(const float & i)
+ {
+ x+=i;
+ y+=i;
+ z+=i;
+ }
+ inline void operator*=(const float & i) //scale on plane
+ {
+ x*=i;
+ y*=i;
+ }
+ inline void operator/=(const float & i) //scale on plane
+ {
+ x/=i;
+ y/=i;
+ }
+
+ inline bool operator==(const float3 & i) const
+ {return (x==i.x) && (y==i.y) && (z==i.z);}
+ inline bool operator!=(const float3 & i) const
+ {return !(*this==i);}
+ inline bool operator<(const float3 & i) const
+ {
+ if (zi.z)
+ return false;
+ if (yi.y)
+ return false;
+ if (xi.x)
+ return false;
+ return false;
+ }
+ inline std::string operator ()() const
+ {
+ return "(" + boost::lexical_cast(x) +
+ " " + boost::lexical_cast(y) +
+ " " + boost::lexical_cast(z) + ")";
+ }
+ inline bool valid() const
+ {
+ return z >= 0; //minimal condition that needs to be fulfilled for tiles in the map
+ }
+ template void serialize(Handler &h, const float version)
+ {
+ h & x & y & z;
+ }
+
+};
+inline std::istream & operator>>(std::istream & str, float3 & dest)
+{
+ str>>dest.x>>dest.y>>dest.z;
+ return str;
+}
+inline std::ostream & operator<<(std::ostream & str, const float3 & sth)
+{
+ return str<()(pos.x);
+ vstd::hash_combine(ret, pos.y);
+ vstd::hash_combine(ret, pos.z);
+ return ret;
+ }
+};