Cross-platform compatibility crap should go in porting.h.
*/
+#define HAXMODE 0
+
#define APPNAME "minetest"
#define DEBUGFILE "debug.txt"
g_settings.setDefault("ravines_amount", "1.0");
g_settings.setDefault("coal_amount", "1.0");*/
g_settings.setDefault("heightmap_blocksize", "16");
- g_settings.setDefault("height_randmax", "linear 0 0 30");
- g_settings.setDefault("height_randfactor", "linear 0.50 -0.10 0");
+ //g_settings.setDefault("height_randmax", "linear 0 0 30");
+ g_settings.setDefault("height_randmax", "linear 0.5 0 0");
+ //g_settings.setDefault("height_randfactor", "linear 0.50 -0.10 0");
+ g_settings.setDefault("height_randfactor", "linear 0.60 0 0");
g_settings.setDefault("height_base", "linear 5 0 0");
- g_settings.setDefault("plants_amount", "0.2");
+ g_settings.setDefault("plants_amount", "1.0");
g_settings.setDefault("ravines_amount", "0");
g_settings.setDefault("coal_amount", "1.0");
v3f playerpos = player->getPosition();
// Apply physics to local player
- if(player->isLocal())
+ if(player->isLocal() && HAXMODE == false)
{
// Apply gravity to local player
v3f speed = player->getSpeed();
#include "heightmap.h"
+// For MAP_BLOCKSIZE
+#include "mapblock.h"
+
/*
ValueGenerator
*/
}
}
- /*
- Seed borders from master heightmap
- NOTE: Does this actually have any effect on the output?
- */
- /*struct SeedSpec
- {
- v2s16 neighbour_start;
- v2s16 heightmap_start;
- v2s16 dir;
- };
-
- SeedSpec seeds[4] =
- {
- { // Z- edge on X-axis
- v2s16(0, -1), // neighbour_start
- v2s16(0, 0), // heightmap_start
- v2s16(1, 0) // dir
- },
- { // Z+ edge on X-axis
- v2s16(0, m_blocksize),
- v2s16(0, m_blocksize),
- v2s16(1, 0)
- },
- { // X- edge on Z-axis
- v2s16(-1, 0),
- v2s16(0, 0),
- v2s16(0, 1)
- },
- { // X+ edge on Z-axis
- v2s16(m_blocksize, 0),
- v2s16(m_blocksize, 0),
- v2s16(0, 1)
- },
- };
-
- for(s16 i=0; i<4; i++){
- v2s16 npos = seeds[i].neighbour_start + m_pos_on_master * m_blocksize;
- v2s16 hpos = seeds[i].heightmap_start;
- for(s16 s=0; s<m_blocksize+1; s++){
- f32 h = m_master->getGroundHeight(npos, false);
- //dstream<<"h="<<h<<std::endl;
- if(h < GROUNDHEIGHT_VALID_MINVALUE)
- continue;
- //break;
- setGroundHeight(hpos, h);
- hpos += seeds[i].dir;
- npos += seeds[i].dir;
- }
- }*/
-
- /*dstream<<"borders seeded:"<<std::endl;
- print();*/
-
/*
Fill with corners[] (if not already set)
*/
m_heightmaps.insert(p, heightmap);
f32 corners[4];
-
- if(m_palist)
+
+ s32 div = SECTOR_HEIGHTMAP_SPLIT * MAP_BLOCKSIZE;
+
{
- //TODO: palist must be taken into account in generateContinued.
- // It is almost useless in here.
- s32 div = 2 * 16;
- Settings *attr = m_palist->getNearAttr(p / div);
- assert(attr);
- corners[0] = attr->getFloat("baseheight");
- corners[1] = attr->getFloat("baseheight");
- corners[2] = attr->getFloat("baseheight");
- corners[3] = attr->getFloat("baseheight");
+ PointAttributeList *palist = m_padb->getList("hm_baseheight");
+
+ if(palist->empty())
+ {
+ corners[0] = 0;
+ corners[1] = 0;
+ corners[2] = 0;
+ corners[3] = 0;
+ }
+ else
+ {
+#if 0
+ corners[0] = palist->getNearAttr((p+v2s16(0,0)) * div).getFloat();
+ corners[1] = palist->getNearAttr((p+v2s16(1,0)) * div).getFloat();
+ corners[2] = palist->getNearAttr((p+v2s16(1,1)) * div).getFloat();
+ corners[3] = palist->getNearAttr((p+v2s16(0,1)) * div).getFloat();
+#endif
+#if 1
+ corners[0] = palist->getInterpolatedFloat((p+v2s16(0,0))*div);
+ corners[1] = palist->getInterpolatedFloat((p+v2s16(1,0))*div);
+ corners[2] = palist->getInterpolatedFloat((p+v2s16(1,1))*div);
+ corners[3] = palist->getInterpolatedFloat((p+v2s16(0,1))*div);
+#endif
+ }
}
- else
+ /*else
{
corners[0] = m_base_generator->getValue(p+v2s16(0,0));
corners[1] = m_base_generator->getValue(p+v2s16(1,0));
corners[2] = m_base_generator->getValue(p+v2s16(1,1));
corners[3] = m_base_generator->getValue(p+v2s16(0,1));
- }
+ }*/
+
+ /*f32 randmax = m_randmax_generator->getValue(p);
+ f32 randfactor = m_randfactor_generator->getValue(p);*/
- f32 randmax = m_randmax_generator->getValue(p);
- f32 randfactor = m_randfactor_generator->getValue(p);
+ f32 randmax = m_padb->getList("hm_randmax")
+ ->getInterpolatedFloat(p*div);
+ f32 randfactor = m_padb->getList("hm_randfactor")
+ ->getInterpolatedFloat(p*div);
+ //dstream<<"randmax="<<randmax<<" randfactor="<<randfactor<<std::endl;
heightmap->generateContinued(randmax, randfactor, corners);
}
throw VersionMismatchException
("ERROR: UnlimitedHeightmap format not supported");
+ if(version <= 7)
+ {
+ /*if(m_base_generator->getId() != VALUE_GENERATOR_ID_CONSTANT
+ || m_randmax_generator->getId() != VALUE_GENERATOR_ID_CONSTANT
+ || m_randfactor_generator->getId() != VALUE_GENERATOR_ID_CONSTANT)*/
+ /*if(std::string(m_base_generator->getName()) != "constant"
+ || std::string(m_randmax_generator->getName()) != "constant"
+ || std::string(m_randfactor_generator->getName()) != "constant")
+ {
+ throw SerializationError
+ ("Cannot write UnlimitedHeightmap in old version: "
+ "Generators are not ConstantGenerators.");
+ }*/
+
+ // Dummy values
+ f32 basevalue = 0.0;
+ f32 randmax = 0.0;
+ f32 randfactor = 0.0;
+
+ // Write version
+ os.write((char*)&version, 1);
+
+ /*
+ [0] u16 blocksize
+ [2] s32 randmax*1000
+ [6] s32 randfactor*1000
+ [10] s32 basevalue*1000
+ [14] u32 heightmap_count
+ [18] X * (v2s16 pos + heightmap)
+ */
+ u32 heightmap_size =
+ FixedHeightmap::serializedLength(version, m_blocksize);
+ u32 heightmap_count = m_heightmaps.size();
+
+ //dstream<<"heightmap_size="<<heightmap_size<<std::endl;
+
+ u32 datasize = 2+4+4+4+4+heightmap_count*(4+heightmap_size);
+ SharedBuffer<u8> data(datasize);
+
+ writeU16(&data[0], m_blocksize);
+ writeU32(&data[2], (s32)(randmax*1000.0));
+ writeU32(&data[6], (s32)(randfactor*1000.0));
+ writeU32(&data[10], (s32)(basevalue*1000.0));
+ writeU32(&data[14], heightmap_count);
+
+ core::map<v2s16, FixedHeightmap*>::Iterator j;
+ j = m_heightmaps.getIterator();
+ u32 i=0;
+ for(; j.atEnd() == false; j++)
+ {
+ FixedHeightmap *hm = j.getNode()->getValue();
+ v2s16 pos = j.getNode()->getKey();
+ writeV2S16(&data[18+i*(4+heightmap_size)], pos);
+ hm->serialize(&data[18+i*(4+heightmap_size)+4], version);
+ i++;
+ }
+
+ os.write((char*)*data, data.getSize());
+ }
+ else if(version <= 11)
+ {
+ // Write version
+ os.write((char*)&version, 1);
+
+ u8 buf[4];
+
+ writeU16(buf, m_blocksize);
+ os.write((char*)buf, 2);
+
+ /*m_randmax_generator->serialize(os);
+ m_randfactor_generator->serialize(os);
+ m_base_generator->serialize(os);*/
+ os<<"constant 0.0\n";
+ os<<"constant 0.0\n";
+ os<<"constant 0.0\n";
+
+ u32 heightmap_count = m_heightmaps.size();
+ writeU32(buf, heightmap_count);
+ os.write((char*)buf, 4);
+
+ u32 heightmap_size =
+ FixedHeightmap::serializedLength(version, m_blocksize);
+
+ SharedBuffer<u8> hmdata(heightmap_size);
+
+ core::map<v2s16, FixedHeightmap*>::Iterator j;
+ j = m_heightmaps.getIterator();
+ u32 i=0;
+ for(; j.atEnd() == false; j++)
+ {
+ v2s16 pos = j.getNode()->getKey();
+ writeV2S16(buf, pos);
+ os.write((char*)buf, 4);
+
+ FixedHeightmap *hm = j.getNode()->getValue();
+ hm->serialize(*hmdata, version);
+ os.write((char*)*hmdata, hmdata.getSize());
+
+ i++;
+ }
+ }
+ else
+ {
+ // Write version
+ os.write((char*)&version, 1);
+
+ u8 buf[4];
+
+ writeU16(buf, m_blocksize);
+ os.write((char*)buf, 2);
+
+ /*m_randmax_generator->serialize(os);
+ m_randfactor_generator->serialize(os);
+ m_base_generator->serialize(os);*/
+
+ u32 heightmap_count = m_heightmaps.size();
+ writeU32(buf, heightmap_count);
+ os.write((char*)buf, 4);
+
+ u32 heightmap_size =
+ FixedHeightmap::serializedLength(version, m_blocksize);
+
+ SharedBuffer<u8> hmdata(heightmap_size);
+
+ core::map<v2s16, FixedHeightmap*>::Iterator j;
+ j = m_heightmaps.getIterator();
+ u32 i=0;
+ for(; j.atEnd() == false; j++)
+ {
+ v2s16 pos = j.getNode()->getKey();
+ writeV2S16(buf, pos);
+ os.write((char*)buf, 4);
+
+ FixedHeightmap *hm = j.getNode()->getValue();
+ hm->serialize(*hmdata, version);
+ os.write((char*)*hmdata, hmdata.getSize());
+
+ i++;
+ }
+ }
+
+#if 0
if(version <= 7)
{
/*if(m_base_generator->getId() != VALUE_GENERATOR_ID_CONSTANT
i++;
}
}
+#endif
}
-UnlimitedHeightmap * UnlimitedHeightmap::deSerialize(std::istream &is)
+UnlimitedHeightmap * UnlimitedHeightmap::deSerialize(std::istream &is,
+ PointAttributeDatabase *padb)
{
u8 version;
is.read((char*)&version, 1);
throw SerializationError
("UnlimitedHeightmap::deSerialize: no enough input data");
s16 blocksize = readU16(&data[0]);
- f32 randmax = (f32)readU32(&data[2]) / 1000.0;
- f32 randfactor = (f32)readU32(&data[6]) / 1000.0;
- f32 basevalue = (f32)readU32(&data[10]) / 1000.0;
+ // Dummy read randmax, randfactor, basevalue
+ /*f32 randmax = (f32)*/readU32(&data[2]) /*/ 1000.0*/;
+ /*f32 randfactor = (f32)*/readU32(&data[6]) /*/ 1000.0*/;
+ /*f32 basevalue = (f32)*/readU32(&data[10]) /*/ 1000.0*/;
u32 heightmap_count = readU32(&data[14]);
/*dstream<<"UnlimitedHeightmap::deSerialize():"
//dstream<<"heightmap_size="<<heightmap_size<<std::endl;
- ValueGenerator *maxgen = new ConstantGenerator(randmax);
+ /*ValueGenerator *maxgen = new ConstantGenerator(randmax);
ValueGenerator *factorgen = new ConstantGenerator(randfactor);
- ValueGenerator *basegen = new ConstantGenerator(basevalue);
-
+ ValueGenerator *basegen = new ConstantGenerator(basevalue);*/
+
UnlimitedHeightmap *hm = new UnlimitedHeightmap
- (blocksize, maxgen, factorgen, basegen, NULL);
+ (blocksize, padb);
for(u32 i=0; i<heightmap_count; i++)
{
}
return hm;
}
+ else if(version <= 11)
+ {
+ u8 buf[4];
+
+ is.read((char*)buf, 2);
+ s16 blocksize = readU16(buf);
+
+ // Dummy-read three lines (generators)
+ std::string templine;
+ std::getline(is, templine, '\n');
+
+ is.read((char*)buf, 4);
+ u32 heightmap_count = readU32(buf);
+
+ u32 heightmap_size =
+ FixedHeightmap::serializedLength(version, blocksize);
+
+ UnlimitedHeightmap *hm = new UnlimitedHeightmap
+ (blocksize, padb);
+
+ for(u32 i=0; i<heightmap_count; i++)
+ {
+ is.read((char*)buf, 4);
+ v2s16 pos = readV2S16(buf);
+
+ SharedBuffer<u8> data(heightmap_size);
+ is.read((char*)*data, heightmap_size);
+ if(is.gcount() != (s32)(heightmap_size)){
+ delete hm;
+ throw SerializationError
+ ("UnlimitedHeightmap::deSerialize: no enough input data");
+ }
+ FixedHeightmap *f = new FixedHeightmap(hm, pos, blocksize);
+ f->deSerialize(*data, version);
+ hm->m_heightmaps.insert(pos, f);
+ }
+ return hm;
+ }
else
{
u8 buf[4];
is.read((char*)buf, 2);
s16 blocksize = readU16(buf);
- ValueGenerator *maxgen = ValueGenerator::deSerialize(is);
+ /*ValueGenerator *maxgen = ValueGenerator::deSerialize(is);
ValueGenerator *factorgen = ValueGenerator::deSerialize(is);
- ValueGenerator *basegen = ValueGenerator::deSerialize(is);
+ ValueGenerator *basegen = ValueGenerator::deSerialize(is);*/
is.read((char*)buf, 4);
u32 heightmap_count = readU32(buf);
FixedHeightmap::serializedLength(version, blocksize);
UnlimitedHeightmap *hm = new UnlimitedHeightmap
- (blocksize, maxgen, factorgen, basegen, NULL);
+ (blocksize, padb);
for(u32 i=0; i<heightmap_count; i++)
{
s16 m_blocksize;
// TODO: Remove ValueGenerators
- ValueGenerator *m_randmax_generator;
+ /*ValueGenerator *m_randmax_generator;
ValueGenerator *m_randfactor_generator;
- ValueGenerator *m_base_generator;
+ ValueGenerator *m_base_generator;*/
- PointAttributeList *m_palist;
+ PointAttributeDatabase *m_padb;
public:
UnlimitedHeightmap(
s16 blocksize,
- ValueGenerator *randmax_generator,
+ /*ValueGenerator *randmax_generator,
ValueGenerator *randfactor_generator,
- ValueGenerator *base_generator,
- PointAttributeList *palist=NULL
+ ValueGenerator *base_generator,*/
+ PointAttributeDatabase *padb
):
m_blocksize(blocksize),
- m_randmax_generator(randmax_generator),
+ /*m_randmax_generator(randmax_generator),
m_randfactor_generator(randfactor_generator),
- m_base_generator(base_generator),
- m_palist(palist)
+ m_base_generator(base_generator),*/
+ m_padb(padb)
{
- assert(m_randmax_generator != NULL);
+ /*assert(m_randmax_generator != NULL);
assert(m_randfactor_generator != NULL);
- assert(m_base_generator != NULL);
+ assert(m_base_generator != NULL);*/
+ assert(m_padb);
}
~UnlimitedHeightmap()
delete i.getNode()->getValue();
}
- delete m_randmax_generator;
+ /*delete m_randmax_generator;
delete m_randfactor_generator;
- delete m_base_generator;
+ delete m_base_generator;*/
}
void print();
//SharedBuffer<u8> serialize(u8 version);
void serialize(std::ostream &os, u8 version);
- static UnlimitedHeightmap * deSerialize(std::istream &istr);
+ static UnlimitedHeightmap * deSerialize(std::istream &istr,
+ PointAttributeDatabase *padb);
};
#endif
Map(dout_server),
m_heightmap(NULL)
{
+ /*
+ Experimental and debug stuff
+ */
+
+ {
+ PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
+ PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
+ PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
+ PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
+ PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
+
+ for(u32 i=0; i<3000; i++)
+ {
+ u32 lim = MAP_GENERATION_LIMIT;
+ if(i < 200)
+ lim = 1000;
+
+ v3s16 p(
+ -lim + myrand()%(lim*2),
+ 0,
+ -lim + myrand()%(lim*2)
+ );
+ /*float plants_amount = (float)(myrand()%1050) / 1000.0;
+ plants_amount = pow(plants_amount, 5);
+ list_plants_amount->addPoint(p, Attribute(plants_amount));*/
+
+ float plants_amount = 0;
+ if(myrand()%5 == 0)
+ {
+ plants_amount = 1.5;
+ }
+ else if(myrand()%4 == 0)
+ {
+ plants_amount = 0.5;
+ }
+ else if(myrand()%2 == 0)
+ {
+ plants_amount = 0.03;
+ }
+ else
+ {
+ plants_amount = 0.0;
+ }
+
+ float caves_amount = 0;
+ if(myrand()%5 == 0)
+ {
+ caves_amount = 1.0;
+ }
+ else if(myrand()%3 == 0)
+ {
+ caves_amount = 0.3;
+ }
+ else
+ {
+ caves_amount = 0.05;
+ }
+
+ list_plants_amount->addPoint(p, Attribute(plants_amount));
+ list_caves_amount->addPoint(p, Attribute(caves_amount));
+ }
+#if 1
+ for(u32 i=0; i<3000; i++)
+ {
+ u32 lim = MAP_GENERATION_LIMIT;
+ if(i < 100)
+ lim = 1000;
+
+ v3s16 p(
+ -lim + myrand()%(lim*2),
+ 0,
+ -lim + myrand()%(lim*2)
+ );
+
+ /*s32 bh_i = (myrand()%200) - 50;
+ float baseheight = (float)bh_i;
+
+ float m = 100.;
+ float e = 3.;
+ float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
+ randmax = pow(randmax, e);
+
+ //float randmax = (float)(myrand()%60);
+ float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
+
+ float baseheight = 0;
+ float randmax = 0;
+ float randfactor = 0;
+
+ if(myrand()%4 == 0)
+ {
+ baseheight = 100;
+ randmax = 100;
+ randfactor = 0.63;
+ }
+ else if(myrand()%5 == 0)
+ {
+ baseheight = 200;
+ randmax = 200;
+ randfactor = 0.66;
+ }
+ else if(myrand()%4 == 0)
+ {
+ baseheight = -3;
+ randmax = 30;
+ randfactor = 0.7;
+ }
+ else if(myrand()%3 == 0)
+ {
+ baseheight = 0;
+ randmax = 30;
+ randfactor = 0.60;
+ }
+ else
+ {
+ baseheight = -3;
+ randmax = 20;
+ randfactor = 0.5;
+ }
+
+ list_baseheight->addPoint(p, Attribute(baseheight));
+ list_randmax->addPoint(p, Attribute(randmax));
+ list_randfactor->addPoint(p, Attribute(randfactor));
+ }
+#endif
+
+ /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
+ list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
+ list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
+ }
+
+#if 0
+ {
+ PointAttributeList *palist = m_padb.getList("hm_baseheight");
+
+ {
+ v3s16 p(0,0,0);
+ Attribute attr;
+ attr.set("5");
+ palist->addPoint(p, attr);
+ }
+
+ /*{
+ v3s16 p(-50,-50,0);
+ Attribute attr;
+ attr.set("-10");
+ palist->addPoint(p, attr);
+ }
+
+ {
+ v3s16 p(50,0,50);
+ Attribute attr;
+ attr.set("200");
+ palist->addPoint(p, attr);
+ }*/
+ }
+#endif
+#if 0
+ {
+ PointAttributeList *palist = m_padb.getList("plants_amount");
+
+ // Back
+ {
+ v3s16 p(0,0,-100);
+ Attribute attr;
+ attr.set("0");
+ palist->addPoint(p, attr);
+ }
+
+ // Front right
+ {
+ v3s16 p(100,0,100);
+ Attribute attr;
+ attr.set("2.0");
+ palist->addPoint(p, attr);
+ }
+
+ // Front left
+ {
+ v3s16 p(-100,0,100);
+ Attribute attr;
+ attr.set("0.2");
+ palist->addPoint(p, attr);
+ }
+ }
+#endif
+
+ /*
+ Try to load map; if not found, create a new one.
+ */
+
m_savedir = savedir;
m_map_saving_enabled = false;
dstream<<DTIME<<"Initializing new map."<<std::endl;
// Create master heightmap
- ValueGenerator *maxgen =
+ /*ValueGenerator *maxgen =
ValueGenerator::deSerialize(hmp.randmax);
ValueGenerator *factorgen =
ValueGenerator::deSerialize(hmp.randfactor);
ValueGenerator *basegen =
ValueGenerator::deSerialize(hmp.base);
m_heightmap = new UnlimitedHeightmap
- (hmp.blocksize, maxgen, factorgen, basegen);
+ (hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
+
+ /*m_heightmap = new UnlimitedHeightmap
+ (hmp.blocksize, &m_padb);*/
+
+ m_heightmap = new UnlimitedHeightmap
+ (32, &m_padb);
// Set map parameters
m_params = mp;
s16 hm_d = MAP_BLOCKSIZE / hm_split;
ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
+
+ // Sector position on map in nodes
+ v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
/*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
" heightmaps and objects"<<std::endl;*/
- // Loop through sub-heightmaps
- for(s16 y=0; y<hm_split; y++)
- for(s16 x=0; x<hm_split; x++)
- {
- v2s16 p_in_sector = v2s16(x,y);
- v2s16 mhm_p = p2d * hm_split + p_in_sector;
- f32 corners[4] = {
- m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
- m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
- m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
- m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
- };
-
- /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
- <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
- <<std::endl;*/
-
- FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
- mhm_p, hm_d);
- sector->setHeightmap(p_in_sector, hm);
-
- //TODO: Make these values configurable
-
- //hm->generateContinued(0.0, 0.0, corners);
- hm->generateContinued(0.25, 0.2, corners);
- //hm->generateContinued(0.5, 0.2, corners);
- //hm->generateContinued(1.0, 0.2, corners);
- //hm->generateContinued(2.0, 0.2, corners);
-
- //hm->print();
-
- }
-
/*
- Generate objects
+ Calculate some information about local properties
*/
- core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
- sector->setObjects(objects);
-
v2s16 mhm_p = p2d * hm_split;
f32 corners[4] = {
m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
pitness /= 4.0;
pitness /= MAP_BLOCKSIZE;
//dstream<<"pitness="<<pitness<<std::endl;
+
+ /*
+ Get local attributes
+ */
+ // Get plant amount from attributes
+ PointAttributeList *palist = m_padb.getList("plants_amount");
+ assert(palist);
+ /*float local_plants_amount =
+ palist->getNearAttr(nodepos2d).getFloat();*/
+ float local_plants_amount =
+ palist->getInterpolatedFloat(nodepos2d);
+
+ /*
+ Generate sector heightmap
+ */
+
+ // Loop through sub-heightmaps
+ for(s16 y=0; y<hm_split; y++)
+ for(s16 x=0; x<hm_split; x++)
+ {
+ v2s16 p_in_sector = v2s16(x,y);
+ v2s16 mhm_p = p2d * hm_split + p_in_sector;
+ f32 corners[4] = {
+ m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
+ m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
+ m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
+ m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
+ };
+
+ /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
+ <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
+ <<std::endl;*/
+
+ FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
+ mhm_p, hm_d);
+ sector->setHeightmap(p_in_sector, hm);
+
+ //TODO: Make these values configurable
+ //hm->generateContinued(0.0, 0.0, corners);
+ //hm->generateContinued(0.25, 0.2, corners);
+ //hm->generateContinued(0.5, 0.2, corners);
+ //hm->generateContinued(1.0, 0.2, corners);
+ //hm->generateContinued(2.0, 0.2, corners);
+ hm->generateContinued(2.0 * avgslope, 0.5, corners);
+
+ //hm->print();
+ }
+
+ /*
+ Generate objects
+ */
+
+ core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
+ sector->setObjects(objects);
+
/*
Plant some trees if there is not much slope
*/
{
// Avgslope is the derivative of a hill
- float t = avgslope * avgslope;
- float a = MAP_BLOCKSIZE * m_params.plants_amount;
+ //float t = avgslope * avgslope;
+ float t = avgslope;
+ float a = MAP_BLOCKSIZE * m_params.plants_amount * local_plants_amount;
u32 tree_max;
- if(t > 0.03)
- tree_max = a / (t/0.03);
+ //float something = 0.17*0.17;
+ float something = 0.3;
+ if(t > something)
+ tree_max = a / (t/something);
else
tree_max = a;
+
u32 count = (myrand()%(tree_max+1));
//u32 count = tree_max;
for(u32 i=0; i<count; i++)
{
// Pitness usually goes at around -0.5...0.5
u32 bush_max = 0;
- u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
+ u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount * local_plants_amount;
if(pitness > 0)
bush_max = (pitness*a*4);
if(bush_max > a)
*/
if(m_params.ravines_amount != 0)
{
- if(myrand()%(s32)(20.0 / m_params.ravines_amount) == 0)
+ if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
{
s16 s = 6;
s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
+ /*
+ Get local attributes
+ */
+
+ v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
+ PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
+ float caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
+
/*
Generate dungeons
*/
}
catch(InvalidPositionException &e){}
+ // Check y-
+ try
+ {
+ s16 y = -1;
+ for(s16 x=0; x<ued; x++)
+ for(s16 z=0; z<ued; z++)
+ {
+ v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
+ if(getNode(ap).d == CONTENT_AIR)
+ {
+ orp = v3f(x+1,0,z+1);
+ found_existing = true;
+ goto continue_generating;
+ }
+ }
+ }
+ catch(InvalidPositionException &e){}
+
+ // Check y+
+ try
+ {
+ s16 y = ued;
+ for(s16 x=0; x<ued; x++)
+ for(s16 z=0; z<ued; z++)
+ {
+ v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
+ if(getNode(ap).d == CONTENT_AIR)
+ {
+ orp = v3f(x+1,ued-1,z+1);
+ found_existing = true;
+ goto continue_generating;
+ }
+ }
+ }
+ catch(InvalidPositionException &e){}
+
continue_generating:
/*
Don't always generate dungeon
*/
bool do_generate_dungeons = true;
+ // Don't generate if no part is underground
if(!some_part_underground)
do_generate_dungeons = false;
+ // If block is partly underground, caves are generated.
else if(!completely_underground)
- do_generate_dungeons = rand() % 5;
- else if(found_existing)
+ do_generate_dungeons = (rand() % 100 <= (u32)(caves_amount*100));
+ // Always continue if found existing dungeons underground
+ else if(found_existing && completely_underground)
do_generate_dungeons = true;
+ // If underground and no dungeons found
else
- do_generate_dungeons = rand() % 2;
+ do_generate_dungeons = (rand() % 2 == 0);
if(do_generate_dungeons)
{
changed_blocks.insert(block->getPos(), block);
}
+
+ if(HAXMODE)
+ {
+ // Don't calculate lighting at all
+ lighting_invalidated_blocks.clear();
+ }
return block;
}
if(m_heightmap != NULL)
delete m_heightmap;
- m_heightmap = UnlimitedHeightmap::deSerialize(is);
+ m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
}
void ServerMap::saveSectorMeta(ServerMapSector *sector)
//u16 max_objects_in_block;
};
+/*
+ ServerMap
+
+ This is the only map class that is able to generate map.
+*/
+
class ServerMap : public Map
{
public:
virtual void PrintInfo(std::ostream &out);
private:
+ // Generator parameters
UnlimitedHeightmap *m_heightmap;
MapParams m_params;
+ PointAttributeDatabase m_padb;
std::string m_savedir;
bool m_map_saving_enabled;
class Client;
+/*
+ ClientMap
+
+ This is the only map class that is able to render itself on screen.
+*/
+
class ClientMap : public Map, public scene::ISceneNode
{
public:
{
// Doing nothing here will block the player from
// walking over map borders
+
+ // Go over borders in debug mode
+ if(HAXMODE)
+ continue;
}
core::aabbox3d<f32> nodebox = Map::getNodeBox(
v3f speed = v3f(0,0,0);
+ if(HAXMODE)
+ {
+ v3f speed = getSpeed();
+ speed.Y = 0;
+ setSpeed(speed);
+ }
+
// Superspeed mode
bool superspeed = false;
if(control.superspeed)
{
- speed += move_direction;
- superspeed = true;
+ if(HAXMODE)
+ {
+ v3f speed = getSpeed();
+ speed.Y = -20*BS;
+ setSpeed(speed);
+ }
+ else
+ {
+ speed += move_direction;
+ superspeed = true;
+ }
}
+ if(HAXMODE)
+ superspeed = true;
+
if(control.up)
{
speed += move_direction;
}
if(control.jump)
{
- if(touching_ground)
+ if(HAXMODE)
+ {
+ v3f speed = getSpeed();
+ /*speed.Y += 20.*BS * dtime * 2;
+ if(speed.Y < 0)
+ speed.Y = 0;*/
+ speed.Y = 20*BS;
+ setSpeed(speed);
+ }
+ else if(touching_ground)
{
v3f speed = getSpeed();
speed.Y = 6.5*BS;
speed = speed.normalize() * walkspeed_max;
f32 inc = walk_acceleration * BS * dtime;
-
+
+ if(HAXMODE)
+ inc = walk_acceleration * BS * dtime * 10;
+
// Accelerate to target speed with maximum increment
accelerate(speed, inc);
}
9: (dev) block objects
10: (dev) water pressure
11: (dev) zlib'd blocks, block flags
+ 12: (dev) UnlimitedHeightmap now uses interpolated areas
*/
// This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255
// Highest supported serialization version
-#define SER_FMT_VER_HIGHEST 11
+#define SER_FMT_VER_HIGHEST 12
// Lowest supported serialization version
#define SER_FMT_VER_LOWEST 2
if(optional)
only_from_disk = true;
+
+ // First check if the block already exists
+ if(only_from_disk)
+ {
+ block = map.getBlockNoCreate(p);
+ }
- block = map.emergeBlock(
- p,
- only_from_disk,
- changed_blocks,
- lighting_invalidated_blocks);
+ if(block == NULL)
+ {
+ block = map.emergeBlock(
+ p,
+ only_from_disk,
+ changed_blocks,
+ lighting_invalidated_blocks);
+ /*
+ EXPERIMENTAL: Create a few other blocks too
+ */
+
+ map.emergeBlock(
+ p + v3s16(0,1,0),
+ only_from_disk,
+ changed_blocks,
+ lighting_invalidated_blocks);
+
+ map.emergeBlock(
+ p + v3s16(0,-1,0),
+ only_from_disk,
+ changed_blocks,
+ lighting_invalidated_blocks);
+
+ }
+
// If it is a dummy, block was not found on disk
if(block->isDummy())
{
continue;
bool generate = d <= d_max_gen;
-
- // Limit the generating area vertically to 2/3
- if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
- generate = false;
+
+ if(HAXMODE)
+ {
+ // Don't generate above player
+ if(p.Y > center.Y)
+ generate = false;
+ }
+ else
+ {
+ // Limit the generating area vertically to 2/3
+ if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
+ generate = false;
+ }
/*
Don't send already sent blocks
continue;
}
+ if(HAXMODE)
+ {
+ /*
+ Ignore block if it is not at ground surface
+ */
+ v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
+ p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
+ f32 y = server->m_env.getMap().getGroundHeight(p2d);
+ // The sector might not exist yet, thus no heightmap
+ if(y > GROUNDHEIGHT_VALID_MINVALUE)
+ {
+ f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
+ if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3)
+ continue;
+ }
+ }
+
/*
Check if map has this block
*/
/*
Set player position
*/
-#if 0
- // We're going to throw the player to this position
- //v2s16 nodepos(29990,29990);
- //v2s16 nodepos(9990,9990);
- v2s16 nodepos(0,0);
- v2s16 sectorpos = getNodeSectorPos(nodepos);
- // Get sector
- m_env.getMap().emergeSector(sectorpos);
- // Get ground height at point
- f32 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
- // The sector should have been generated -> groundheight exists
- assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
- // Don't go underwater
- if(groundheight < WATER_LEVEL)
- groundheight = WATER_LEVEL;
-#endif
+
+ dstream<<"Server: Finding spawn place for player \""
+ <<player->getName()<<"\""<<std::endl;
#if 1
v2s16 nodepos;
// Try to find a good place a few times
for(s32 i=0; i<100; i++)
{
- s32 range = 1 + i*2;
+ s32 range = 1 + i*4;
// We're going to try to throw the player to this position
nodepos = v2s16(-range/2 + (myrand()%range),
-range/2 + (myrand()%range));
{
//g_heightmap_debugprint = true;
const s16 BS1 = 4;
- UnlimitedHeightmap hm1(BS1,
+ /*UnlimitedHeightmap hm1(BS1,
new ConstantGenerator(0.0),
new ConstantGenerator(0.0),
- new ConstantGenerator(5.0));
+ new ConstantGenerator(5.0));*/
+ PointAttributeDatabase padb;
+ UnlimitedHeightmap hm1(BS1, &padb);
// Go through it so it generates itself
for(s16 y=0; y<=BS1; y++){
for(s16 x=0; x<=BS1; x++){
dstream<<std::endl;
const s16 BS1 = 8;
- UnlimitedHeightmap hm1(BS1,
+ /*UnlimitedHeightmap hm1(BS1,
new ConstantGenerator(10.0),
new ConstantGenerator(0.3),
- new ConstantGenerator(0.0));
+ new ConstantGenerator(0.0));*/
+
+ PointAttributeDatabase padb;
+
+ padb.getList("hm_baseheight")->addPoint(v2s16(-BS1,0), Attribute(0));
+ padb.getList("hm_randmax")->addPoint(v2s16(-BS1,0), Attribute(0));
+ padb.getList("hm_randfactor")->addPoint(v2s16(-BS1,0), Attribute(0.0));
+
+ padb.getList("hm_baseheight")->addPoint(v2s16(0,0), Attribute(-20));
+ padb.getList("hm_randmax")->addPoint(v2s16(0,0), Attribute(0));
+ padb.getList("hm_randfactor")->addPoint(v2s16(0,0), Attribute(0.5));
+
+ padb.getList("hm_baseheight")->addPoint(v2s16(BS1*2,BS1), Attribute(0));
+ padb.getList("hm_randmax")->addPoint(v2s16(BS1*2,BS1), Attribute(30));
+ padb.getList("hm_randfactor")->addPoint(v2s16(BS1*2,BS1), Attribute(0.9));
+
+ UnlimitedHeightmap hm1(BS1, &padb);
// Force hm1 to generate a some heightmap
hm1.getGroundHeight(v2s16(0,0));
next = seed;
}
+// Float with distance
+struct DFloat
+{
+ float v;
+ u32 d;
+};
+
+float PointAttributeList::getInterpolatedFloat(v3s16 p)
+{
+ const u32 near_wanted_count = 5;
+ // Last is nearest, first is farthest
+ core::list<DFloat> near;
+
+ for(core::list<PointWithAttr>::Iterator
+ i = m_points.begin();
+ i != m_points.end(); i++)
+ {
+ PointWithAttr &pwa = *i;
+ u32 d = pwa.p.getDistanceFrom(p);
+
+ DFloat df;
+ df.v = pwa.attr.getFloat();
+ df.d = d;
+
+ // If near list is empty, add directly and continue
+ if(near.size() == 0)
+ {
+ near.push_back(df);
+ continue;
+ }
+
+ // Get distance of farthest in near list
+ u32 near_d = 100000;
+ if(near.size() > 0)
+ {
+ core::list<DFloat>::Iterator i = near.begin();
+ near_d = i->d;
+ }
+
+ /*
+ If point is closer than the farthest in the near list or
+ there are not yet enough points on the list
+ */
+ if(d < near_d || near.size() < near_wanted_count)
+ {
+ // Find the right place in the near list and put it there
+
+ // Go from farthest to near in the near list
+ core::list<DFloat>::Iterator i = near.begin();
+ for(; i != near.end(); i++)
+ {
+ // Stop when i is at the first nearer node
+ if(i->d < d)
+ break;
+ }
+ // Add df to before i
+ if(i == near.end())
+ near.push_back(df);
+ else
+ near.insert_before(i, df);
+
+ // Keep near list at right size
+ if(near.size() > near_wanted_count)
+ {
+ core::list<DFloat>::Iterator j = near.begin();
+ near.erase(j);
+ }
+ }
+ }
+
+ // Return if no values found
+ if(near.size() == 0)
+ return 0.0;
+
+ /*
+20:58:29 < tejeez> joka pisteelle a += arvo / etäisyys^6; b += 1 / etäisyys^6; ja
+lopuks sit otetaan a/b
+ */
+
+ float a = 0;
+ float b = 0;
+ for(core::list<DFloat>::Iterator i = near.begin();
+ i != near.end(); i++)
+ {
+ if(i->d == 0)
+ return i->v;
+
+ //float dd = pow((float)i->d, 6);
+ float dd = pow((float)i->d, 5);
+ float v = i->v;
+ //dstream<<"dd="<<dd<<", v="<<v<<std::endl;
+ a += v / dd;
+ b += 1 / dd;
+ }
+
+ return a / b;
+}
+
+#if 0
+float PointAttributeList::getInterpolatedFloat(v3s16 p)
+{
+ const u32 near_wanted_count = 2;
+ const u32 nearest_wanted_count = 2;
+ // Last is near
+ core::list<DFloat> near;
+
+ for(core::list<PointWithAttr>::Iterator
+ i = m_points.begin();
+ i != m_points.end(); i++)
+ {
+ PointWithAttr &pwa = *i;
+ u32 d = pwa.p.getDistanceFrom(p);
+
+ DFloat df;
+ df.v = pwa.attr.getFloat();
+ df.d = d;
+
+ // If near list is empty, add directly and continue
+ if(near.size() == 0)
+ {
+ near.push_back(df);
+ continue;
+ }
+
+ // Get distance of farthest in near list
+ u32 near_d = 100000;
+ if(near.size() > 0)
+ {
+ core::list<DFloat>::Iterator i = near.begin();
+ near_d = i->d;
+ }
+
+ /*
+ If point is closer than the farthest in the near list or
+ there are not yet enough points on the list
+ */
+ if(d < near_d || near.size() < near_wanted_count)
+ {
+ // Find the right place in the near list and put it there
+
+ // Go from farthest to near in the near list
+ core::list<DFloat>::Iterator i = near.begin();
+ for(; i != near.end(); i++)
+ {
+ // Stop when i is at the first nearer node
+ if(i->d < d)
+ break;
+ }
+ // Add df to before i
+ if(i == near.end())
+ near.push_back(df);
+ else
+ near.insert_before(i, df);
+
+ // Keep near list at right size
+ if(near.size() > near_wanted_count)
+ {
+ core::list<DFloat>::Iterator j = near.begin();
+ near.erase(j);
+ }
+ }
+ }
+
+ // Return if no values found
+ if(near.size() == 0)
+ return 0.0;
+
+ /*
+ Get nearest ones
+ */
+
+ u32 nearest_count = nearest_wanted_count;
+ if(nearest_count > near.size())
+ nearest_count = near.size();
+ core::list<DFloat> nearest;
+ {
+ core::list<DFloat>::Iterator i = near.getLast();
+ for(u32 j=0; j<nearest_count; j++)
+ {
+ nearest.push_front(*i);
+ i--;
+ }
+ }
+
+ /*
+ TODO: Try this:
+20:58:29 < tejeez> joka pisteelle a += arvo / etäisyys^6; b += 1 / etäisyys^6; ja
+lopuks sit otetaan a/b
+ */
+
+ /*
+ Get total distance to nearest points
+ */
+
+ float nearest_d_sum = 0;
+ for(core::list<DFloat>::Iterator i = nearest.begin();
+ i != nearest.end(); i++)
+ {
+ nearest_d_sum += (float)i->d;
+ }
+
+ /*
+ Interpolate a value between the first ones
+ */
+
+ dstream<<"nearest.size()="<<nearest.size()<<std::endl;
+
+ float interpolated = 0;
+
+ for(core::list<DFloat>::Iterator i = nearest.begin();
+ i != nearest.end(); i++)
+ {
+ float weight;
+ if(nearest_d_sum > 0.001)
+ weight = (float)i->d / nearest_d_sum;
+ else
+ weight = 1. / nearest.size();
+ /*dstream<<"i->d="<<i->d<<" nearest_d_sum="<<nearest_d_sum
+ <<" weight="<<weight<<std::endl;*/
+ interpolated += weight * i->v;
+ }
+
+ return interpolated;
+}
+#endif
+
return atoi(s.c_str());
}
+inline std::string itos(s32 i)
+{
+ std::ostringstream o;
+ o<<i;
+ return o.str();
+}
+
+inline std::string ftos(float f)
+{
+ std::ostringstream o;
+ o<<f;
+ return o.str();
+}
+
/*
A base class for simple background thread implementation
*/
return stoi(get(name));
}
+ void clear()
+ {
+ m_settings.clear();
+ m_defaults.clear();
+ }
+
+ Settings & operator+=(Settings &other)
+ {
+ if(&other == this)
+ return *this;
+
+ for(core::map<std::string, std::string>::Iterator
+ i = other.m_settings.getIterator();
+ i.atEnd() == false; i++)
+ {
+ m_settings.insert(i.getNode()->getKey(),
+ i.getNode()->getValue());
+ }
+
+ for(core::map<std::string, std::string>::Iterator
+ i = other.m_defaults.getIterator();
+ i.atEnd() == false; i++)
+ {
+ m_defaults.insert(i.getNode()->getKey(),
+ i.getNode()->getValue());
+ }
+
+ }
+
+ Settings & operator=(Settings &other)
+ {
+ if(&other == this)
+ return *this;
+
+ clear();
+ (*this) += other;
+
+ return *this;
+ }
+
private:
core::map<std::string, std::string> m_settings;
core::map<std::string, std::string> m_defaults;
#define MYRAND_MAX 32767
/*
- TODO: Some kind of a thing that stores arbitary data related to
- 2D coordinate points
+ Some kind of a thing that stores attributes related to
+ coordinate points
*/
+struct Attribute
+{
+ Attribute()
+ {
+ }
+
+ Attribute(const std::string &value):
+ m_value(value)
+ {
+ }
+
+ Attribute(float value)
+ {
+ m_value = ftos(value);
+ }
+
+ void set(const std::string &value)
+ {
+ m_value = value;
+ }
+
+ std::string get()
+ {
+ return m_value;
+ }
+
+ bool getBool()
+ {
+ return is_yes(get());
+ }
+
+ float getFloat()
+ {
+ float f;
+ std::istringstream vis(get());
+ vis>>f;
+ return f;
+ }
+
+ u16 getU16()
+ {
+ return stoi(get(), 0, 65535);
+ }
+
+ s16 getS16()
+ {
+ return stoi(get(), -32768, 32767);
+ }
+
+ s32 getS32()
+ {
+ return stoi(get());
+ }
+
+ std::string m_value;
+};
+
+class PointAttributeList
+{
+ struct PointWithAttr
+ {
+ v3s16 p;
+ Attribute attr;
+ };
+
+public:
+ ~PointAttributeList()
+ {
+ /*for(core::list<PointWithAttr>::Iterator
+ i = m_points.begin();
+ i != m_points.end(); i++)
+ {
+ PointWithAttr &pwa = *i;
+ //delete pwa.attr;
+ }*/
+ }
+
+ Attribute getNearAttr(v3s16 p)
+ {
+ core::list<PointWithAttr>::Iterator
+ nearest_i = m_points.end();
+ s16 nearest_d = 32767;
+ for(core::list<PointWithAttr>::Iterator
+ i = m_points.begin();
+ i != m_points.end(); i++)
+ {
+ PointWithAttr &pwa = *i;
+ s16 d = pwa.p.getDistanceFrom(p);
+ if(d < nearest_d)
+ {
+ nearest_i = i;
+ nearest_d = d;
+ }
+ }
+
+ if(nearest_i == m_points.end())
+ Attribute();
+
+ return nearest_i->attr;
+ }
+
+ Attribute getNearAttr(v2s16 p)
+ {
+ return getNearAttr(v3s16(p.X, 0, p.Y));
+ }
+
+ bool empty()
+ {
+ return (m_points.size() == 0);
+ }
+
+ /*
+ Take all points in range, or at least the nearest point,
+ and interpolate the values as floats
+ */
+ float getInterpolatedFloat(v3s16 p);
+
+ float getInterpolatedFloat(v2s16 p)
+ {
+ return getInterpolatedFloat(v3s16(p.X, 0, p.Y));
+ }
+
+ //float getInterpolatedFloat(v3s16 p, s32 range);
+ /*float getInterpolatedFloat(v2s16 p, s32 range)
+ {
+ return getInterpolatedFloat(v3s16(p.X, 0, p.Y), range);
+ }*/
+
+ void addPoint(v3s16 p, const Attribute &attr)
+ {
+ PointWithAttr pattr;
+ pattr.p = p;
+ pattr.attr = attr;
+ m_points.push_back(pattr);
+ }
+
+ void addPoint(v2s16 p, const Attribute &attr)
+ {
+ addPoint(v3s16(p.X, 0, p.Y), attr);
+ }
+
+private:
+ core::list<PointWithAttr> m_points;
+};
+
+/*
+ Basically just a wrapper to core::map<PointAttributeList*>
+*/
+
+class PointAttributeDatabase
+{
+public:
+ ~PointAttributeDatabase()
+ {
+ for(core::map<std::string, PointAttributeList*>::Iterator
+ i = m_lists.getIterator();
+ i.atEnd() == false; i++)
+ {
+ delete i.getNode()->getValue();
+ }
+ }
+
+ PointAttributeList *getList(const std::string &name)
+ {
+ PointAttributeList *list = NULL;
+
+ core::map<std::string, PointAttributeList*>::Node *n;
+ n = m_lists.find(name);
+
+ if(n == NULL)
+ {
+ list = new PointAttributeList();
+ m_lists.insert(name, list);
+ }
+ else
+ {
+ list = n->getValue();
+ }
+
+ return list;
+ }
+private:
+ core::map<std::string, PointAttributeList*> m_lists;
+};
+
#endif