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

Randomized center positions of zones.

This commit is contained in:
DjWarmonger 2014-05-24 12:42:06 +02:00
parent 3a6f748fb5
commit 27dcf70b1a
10 changed files with 324 additions and 37 deletions

View File

@ -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" />

View File

@ -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>

View File

@ -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;
}

View File

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

View File

@ -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;
}

View File

@ -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)

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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
lib/rmg/float3.h Normal file
View File

@ -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;
}
};