Просмотр исходного кода

Randomized center positions of zones.

DjWarmonger 11 лет назад
Родитель
Сommit
27dcf70b1a

+ 1 - 0
lib/VCMI_lib.vcxproj

@@ -321,6 +321,7 @@
     <ClInclude Include="rmg\CRmgTemplateZone.h" />
     <ClInclude Include="rmg\CZoneGraphGenerator.h" />
     <ClInclude Include="rmg\CZonePlacer.h" />
+    <ClInclude Include="rmg\float3.h" />
     <ClInclude Include="StartInfo.h" />
     <ClInclude Include="StdInc.h" />
     <ClInclude Include="UnlockGuard.h" />

+ 4 - 1
lib/VCMI_lib.vcxproj.filters

@@ -396,5 +396,8 @@
     <ClInclude Include="registerTypes\RegisterTypes.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="rmg\float3.h">
+      <Filter>rmg</Filter>
+    </ClInclude>
   </ItemGroup>
-</Project>
+</Project>

+ 13 - 4
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<CMapGenOptions> mapGenOptions, int randomSeed /*= std::time(nullptr)*/) :
 	mapGenOptions(mapGenOptions), randomSeed(randomSeed)
@@ -26,10 +27,10 @@ CMapGenerator::~CMapGenerator()
 
 std::unique_ptr<CMap> CMapGenerator::generate()
 {
-		mapGenOptions->finalize(rand);
+	mapGenOptions->finalize(rand);
 
-		map = make_unique<CMap>();
-		editManager = map->getEditManager();
+	map = make_unique<CMap>();
+	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<TRmgTemplateZoneId, CRmgTemplateZone*> CMapGenerator::getZones() const
+{
+	return zones;
 }

+ 2 - 0
lib/rmg/CMapGenerator.h

@@ -62,6 +62,8 @@ public:
 	int randomSeed;
 	CMapEditManager * editManager;
 
+	std::map<TRmgTemplateZoneId, CRmgTemplateZone*> getZones() const;
+
 private:
 	std::map<TRmgTemplateZoneId, CRmgTemplateZone*> zones;
 

+ 10 - 0
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;
 		}

+ 42 - 17
lib/rmg/CRmgTemplateZone.cpp

@@ -272,6 +272,26 @@ void CRmgTemplateZone::setTownTypeLikeZone(boost::optional<TRmgTemplateZoneId> v
 	townTypeLikeZone = value;
 }
 
+void CRmgTemplateZone::addConnection(TRmgTemplateZoneId otherZone)
+{
+	connections.push_back (otherZone);
+}
+
+std::vector<TRmgTemplateZoneId> 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<int3> 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<int3> tiles;
 	for(int i = -1; i < 2; ++i)

+ 20 - 3
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<TRmgTemplateZoneId> value);
 	boost::optional<TRmgTemplateZoneId> getTownTypeLikeZone() const;
 	void setTownTypeLikeZone(boost::optional<TRmgTemplateZoneId> value);
+
+	float3 getCenter() const;
+	void setCenter(float3 f);
+	int3 getPos();
+	void setPos(int3 pos);
+
 	void setShape(std::vector<int3> shape);
 	bool fill(CMapGenerator* gen);
 
+	void addConnection(TRmgTemplateZoneId otherZone);
+	std::vector<TRmgTemplateZoneId> getConnections() const;
+
 private:
+	//template info
 	TRmgTemplateZoneId id;
 	ETemplateZoneType::ETemplateZoneType type;
 	int size;
@@ -113,11 +124,17 @@ private:
 	std::set<ETerrainType> terrainTypes;
 	boost::optional<TRmgTemplateZoneId> terrainTypeLikeZone, townTypeLikeZone;
 
-	std::vector<int3> shape;
-	std::map<int3, CTileInfo> tileinfo;
+	//content info
+	std::vector<int3> shape; //TODO: remove
 	std::vector<CGObjectInstance*> objects;
 
-	int3 getCenter();
+	//placement info
+	int3 pos;
+	float3 center;
+	std::map<int3, CTileInfo> tileinfo; //irregular area assined to zone
+	std::vector<TRmgTemplateZoneId> connections; //list of adjacent zones
+	std::map<TRmgTemplateZoneId, bool> 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);

+ 85 - 3
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<CZoneGraph> 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<CMapGenOptions> 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();
+	}
 }

+ 14 - 8
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<CZoneGraph> graph, CRandomGenerator * gen);
+	void placeZones(shared_ptr<CMapGenOptions> mapGenOptions, CRandomGenerator * rand);
 
 private:
 	//CMap * map;
-	unique_ptr<CZoneGraph> graph;
-	//CRandomGenerator * gen;
+	//unique_ptr<CZoneGraph> graph;
+	CMapGenerator * gen;
 };

+ 132 - 0
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 (z<i.z)
+			return true;
+		if (z>i.z)
+			return false;
+		if (y<i.y)
+			return true;
+		if (y>i.y)
+			return false;
+		if (x<i.x)
+			return true;
+		if (x>i.x)
+			return false;
+		return false;
+	}
+	inline std::string operator ()() const
+	{
+		return	"(" + boost::lexical_cast<std::string>(x) +
+				" " + boost::lexical_cast<std::string>(y) +
+				" " + boost::lexical_cast<std::string>(z) + ")";
+	}
+	inline bool valid() const
+	{
+		return z >= 0; //minimal condition that needs to be fulfilled for tiles in the map
+	}
+	template <typename Handler> 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<<sth.x<<' '<<sth.y<<' '<<sth.z;
+}
+
+struct Shashfloat3
+{
+	size_t operator()(float3 const& pos) const
+	{
+		size_t ret = std::hash<float>()(pos.x);
+		vstd::hash_combine(ret, pos.y);
+		vstd::hash_combine(ret, pos.z);
+		return ret;
+	}
+};