settings->setDefault("default_privs", "interact, shout");
settings->setDefault("player_transfer_distance", "0");
settings->setDefault("enable_pvp", "true");
- settings->setDefault("vertical_spawn_range", "16");
settings->setDefault("disallow_empty_password", "false");
settings->setDefault("disable_anticheat", "false");
settings->setDefault("enable_rollback_recording", "false");
}
+int EmergeManager::getSpawnLevelAtPoint(v2s16 p)
+{
+ if (m_mapgens.size() == 0 || !m_mapgens[0]) {
+ errorstream << "EmergeManager: getSpawnLevelAtPoint() called"
+ " before mapgen init" << std::endl;
+ return 0;
+ }
+
+ return m_mapgens[0]->getSpawnLevelAtPoint(p);
+}
+
+
int EmergeManager::getGroundLevelAtPoint(v2s16 p)
{
if (m_mapgens.size() == 0 || !m_mapgens[0]) {
// Mapgen helpers methods
Biome *getBiomeAtPoint(v3s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
int getGroundLevelAtPoint(v2s16 p);
bool isBlockUnderground(v3s16 blockpos);
virtual void makeChunk(BlockMakeData *data) {}
virtual int getGroundLevelAtPoint(v2s16 p) { return 0; }
+ // getSpawnLevelAtPoint() is a function within each mapgen that returns a
+ // suitable y co-ordinate for player spawn ('suitable' usually meaning
+ // within 16 nodes of water_level). If a suitable spawn level cannot be
+ // found at the specified (X, Z) 'MAX_MAP_GENERATION_LIMIT' is returned to
+ // signify this and to cause Server::findSpawnPos() to try another (X, Z).
+ virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; }
+
private:
DISABLE_CLASS_COPY(Mapgen);
};
/////////////////////////////////////////////////////////////////
-int MapgenFlat::getGroundLevelAtPoint(v2s16 p)
+int MapgenFlat::getSpawnLevelAtPoint(v2s16 p)
{
+ s16 level_at_point = ground_level;
float n_terrain = NoisePerlin2D(&noise_terrain->np, p.X, p.Y, seed);
+
if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
- s16 depress = (lake_threshold - n_terrain) * lake_steepness;
- return ground_level - depress;
+ level_at_point = ground_level -
+ (lake_threshold - n_terrain) * lake_steepness;
} else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) {
- s16 rise = (n_terrain - hill_threshold) * hill_steepness;
- return ground_level + rise;
- } else {
- return ground_level;
+ level_at_point = ground_level +
+ (n_terrain - hill_threshold) * hill_steepness;
}
+
+ if (ground_level < water_level) // Ocean world, allow spawn in water
+ return MYMAX(level_at_point, water_level);
+ else if (level_at_point > water_level)
+ return level_at_point; // Spawn on land
+ else
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
}
~MapgenFlat();
virtual void makeChunk(BlockMakeData *data);
- int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
void calculateNoise();
s16 generateTerrain();
MgStoneType generateBiomes(float *heat_map, float *humidity_map);
/////////////////////////////////////////////////////////////////
-int MapgenFractal::getGroundLevelAtPoint(v2s16 p)
+int MapgenFractal::getSpawnLevelAtPoint(v2s16 p)
{
- s16 search_start = 128;
- s16 search_end = -128;
-
- for (s16 y = search_start; y >= search_end; y--) {
- if (getFractalAtPoint(p.X, y, p.Y))
- return y;
+ bool solid_below = false; // Dry solid node is present below to spawn on
+ u8 air_count = 0; // Consecutive air nodes above the dry solid node
+ s16 seabed_level = NoisePerlin2D(&noise_seabed->np, p.X, p.Y, seed);
+ // Seabed can rise above water_level or might be raised to create dry land
+ s16 search_start = MYMAX(seabed_level, water_level + 1);
+ if (seabed_level > water_level)
+ solid_below = true;
+
+ for (s16 y = search_start; y <= search_start + 128; y++) {
+ if (getFractalAtPoint(p.X, y, p.Y)) { // Fractal node
+ solid_below = true;
+ air_count = 0;
+ } else if (solid_below) { // Air above solid node
+ air_count++;
+ if (air_count == 2)
+ return y - 2;
+ }
}
- return -MAX_MAP_GENERATION_LIMIT;
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
}
~MapgenFractal();
virtual void makeChunk(BlockMakeData *data);
- int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
void calculateNoise();
bool getFractalAtPoint(s16 x, s16 y, s16 z);
s16 generateTerrain();
}
-int MapgenSinglenode::getGroundLevelAtPoint(v2s16 p)
+int MapgenSinglenode::getSpawnLevelAtPoint(v2s16 p)
{
return 0;
}
~MapgenSinglenode();
void makeChunk(BlockMakeData *data);
- int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
};
struct MapgenFactorySinglenode : public MapgenFactory {
}
-int MapgenV5::getGroundLevelAtPoint(v2s16 p)
+int MapgenV5::getSpawnLevelAtPoint(v2s16 p)
{
//TimeTaker t("getGroundLevelAtPoint", NULL, PRECISION_MICRO);
f *= 1.6;
float h = NoisePerlin2D(&noise_height->np, p.X, p.Y, seed);
- s16 search_start = 128; // Only bother searching this range, actual
- s16 search_end = -128; // ground level is rarely higher or lower.
-
- for (s16 y = search_start; y >= search_end; y--) {
+ for (s16 y = 128; y >= -128; y--) {
float n_ground = NoisePerlin3D(&noise_ground->np, p.X, y, p.Y, seed);
- // If solid
- if (n_ground * f > y - h) {
+
+ if (n_ground * f > y - h) { // If solid
// If either top 2 nodes of search are solid this is inside a
- // mountain or floatland with no space for the player to spawn.
- if (y >= search_start - 1)
- return MAX_MAP_GENERATION_LIMIT;
- else
- return y; // Ground below at least 2 nodes of space
+ // mountain or floatland with possibly no space for the player to spawn.
+ if (y >= 127) {
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ } else { // Ground below at least 2 nodes of empty space
+ if (y <= water_level || y > water_level + 16)
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ else
+ return y;
+ }
}
}
//printf("getGroundLevelAtPoint: %dus\n", t.stop());
- return -MAX_MAP_GENERATION_LIMIT;
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn position, no ground found
}
~MapgenV5();
virtual void makeChunk(BlockMakeData *data);
- int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
void calculateNoise();
int generateBaseTerrain();
MgStoneType generateBiomes(float *heat_map, float *humidity_map);
}
+int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
+{
+ s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
+ if (level_at_point <= water_level ||
+ level_at_point > water_level + 16)
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ else
+ return level_at_point;
+}
+
+
//////////////////////// Noise functions
float MapgenV6::getMudAmount(v2s16 p)
void makeChunk(BlockMakeData *data);
int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
float baseTerrainLevel(float terrain_base, float terrain_higher,
float steepness, float height_select);
///////////////////////////////////////
-int MapgenV7::getGroundLevelAtPoint(v2s16 p)
+int MapgenV7::getSpawnLevelAtPoint(v2s16 p)
{
// Base terrain calculation
s16 y = baseTerrainLevelAtPoint(p.X, p.Y);
// Ridge/river terrain calculation
float width = 0.2;
float uwatern = NoisePerlin2D(&noise_ridge_uwater->np, p.X, p.Y, seed) * 2;
- // actually computing the depth of the ridge is much more expensive;
- // if inside a river, simply guess
+ // if inside a river this is an unsuitable spawn point
if (fabs(uwatern) <= width)
- return water_level - 10;
+ return MAX_MAP_GENERATION_LIMIT;
// Mountain terrain calculation
- int iters = 128; // don't even bother iterating more than 128 times..
+ int iters = 128;
while (iters--) {
- //current point would have been air
- if (!getMountainTerrainAtPoint(p.X, y, p.Y))
- return y;
-
+ if (!getMountainTerrainAtPoint(p.X, y + 1, p.Y)) { // Air, y is ground level
+ if (y <= water_level || y > water_level + 16)
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ else
+ return y;
+ }
y++;
}
- return y;
+ // Unsuitable spawn point, no ground surface found
+ return MAX_MAP_GENERATION_LIMIT;
}
~MapgenV7();
virtual void makeChunk(BlockMakeData *data);
- int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
Biome *getBiomeAtPoint(v3s16 p);
float baseTerrainLevelAtPoint(s16 x, s16 z);
//Profiler *mapgen_profiler = &mapgen_prof;
static FlagDesc flagdesc_mapgen_valleys[] = {
- {"altitude_chill", MG_VALLEYS_ALT_CHILL},
- {"humid_rivers", MG_VALLEYS_HUMID_RIVERS},
+ {"altitude_chill", MGVALLEYS_ALT_CHILL},
+ {"humid_rivers", MGVALLEYS_HUMID_RIVERS},
{NULL, 0}
};
MapgenValleysParams *sp = (MapgenValleysParams *)params->sparams;
this->spflags = sp->spflags;
- this->humid_rivers = (spflags & MG_VALLEYS_HUMID_RIVERS);
- this->use_altitude_chill = (spflags & MG_VALLEYS_ALT_CHILL);
+ this->humid_rivers = (spflags & MGVALLEYS_HUMID_RIVERS);
+ this->use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL);
this->altitude_chill = sp->altitude_chill;
this->humidity_adjust = params->np_biome_humidity.offset - 50.f;
MapgenValleysParams::MapgenValleysParams()
{
- spflags = MG_VALLEYS_HUMID_RIVERS | MG_VALLEYS_ALT_CHILL;
+ spflags = MGVALLEYS_HUMID_RIVERS | MGVALLEYS_ALT_CHILL;
altitude_chill = 90; // The altitude at which temperature drops by 20C.
large_cave_depth = -33;
}
-int MapgenValleys::getGroundLevelAtPoint(v2s16 p)
+int MapgenValleys::getSpawnLevelAtPoint(v2s16 p)
{
- // ***********************************
- // This method (deliberately) does not
- // return correct terrain values.
- // ***********************************
-
- // Since MT doesn't normally deal with rivers, check
- // to make sure this isn't a request for a location
- // in a river.
+ // Check to make sure this isn't a request for a location in a river.
float rivers = NoisePerlin2D(&noise_rivers->np, p.X, p.Y, seed);
-
- // If it's wet, return an unusable number.
if (fabs(rivers) < river_size_factor)
- return MAX_MAP_GENERATION_LIMIT;
-
- // Otherwise, return the real result.
- return terrainLevelAtPoint(p.X, p.Y);
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+
+ s16 level_at_point = terrainLevelAtPoint(p.X, p.Y);
+ if (level_at_point <= water_level ||
+ level_at_point > water_level + 16)
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ else
+ return level_at_point;
}
#include "mapgen.h"
-/////////////////// Mapgen Valleys flags
-#define MG_VALLEYS_ALT_CHILL 0x01
-#define MG_VALLEYS_HUMID_RIVERS 0x02
+////////////// Mapgen Valleys flags
+#define MGVALLEYS_ALT_CHILL 0x01
+#define MGVALLEYS_HUMID_RIVERS 0x02
// Feed only one variable into these.
#define MYSQUARE(x) (x) * (x)
~MapgenValleys();
virtual void makeChunk(BlockMakeData *data);
- int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
s16 large_cave_depth;
return nodeposf * BS;
}
- s16 water_level = map.getWaterLevel();
- s16 vertical_spawn_range = g_settings->getS16("vertical_spawn_range");
bool is_good = false;
// Try to find a good place a few times
- for(s32 i = 0; i < 1000 && !is_good; i++) {
+ for(s32 i = 0; i < 4000 && !is_good; i++) {
s32 range = 1 + i;
// We're going to try to throw the player to this position
v2s16 nodepos2d = v2s16(
-range + (myrand() % (range * 2)),
-range + (myrand() % (range * 2)));
- // Get ground height at point
- s16 groundheight = map.findGroundLevel(nodepos2d);
- // Don't go underwater or to high places
- if (groundheight <= water_level ||
- groundheight > water_level + vertical_spawn_range)
+ // Get spawn level at point
+ s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
+ // Continue if MAX_MAP_GENERATION_LIMIT was returned by
+ // the mapgen to signify an unsuitable spawn position
+ if (spawn_level == MAX_MAP_GENERATION_LIMIT)
continue;
- v3s16 nodepos(nodepos2d.X, groundheight, nodepos2d.Y);
+ v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
s32 air_count = 0;
for (s32 i = 0; i < 10; i++) {