/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "mapgen.h"
#include "voxel.h"
#include "noise.h"
#include "mapblock.h"
#include "map.h"
//#include "serverobject.h"
#include "content_sao.h"
#include "nodedef.h"
#include "content_mapnode.h" // For content_mapnode_get_new_name
#include "voxelalgorithms.h"
#include "profiler.h"
#include "main.h" // For g_profiler
namespace mapgen
{
/*
Some helper functions for the map generator
*/
#if 1
// Returns Y one under area minimum if not found
static s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d,
INodeDefManager *ndef)
{
v3s16 em = vmanip.m_area.getExtent();
s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
s16 y;
for(y=y_nodes_max; y>=y_nodes_min; y--)
{
MapNode &n = vmanip.m_data[i];
if(ndef->get(n).walkable)
break;
vmanip.m_area.add_y(em, i, -1);
}
if(y >= y_nodes_min)
return y;
else
return y_nodes_min - 1;
}
#if 0
// Returns Y one under area minimum if not found
static s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d,
INodeDefManager *ndef)
{
if(!vmanip.m_area.contains(v3s16(p2d.X, vmanip.m_area.MaxEdge.Y, p2d.Y)))
return vmanip.m_area.MinEdge.Y-1;
v3s16 em = vmanip.m_area.getExtent();
s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
s16 y;
content_t c_tree = ndef->getId("mapgen_tree");
content_t c_leaves = ndef->getId("mapgen_leaves");
for(y=y_nodes_max; y>=y_nodes_min; y--)
{
MapNode &n = vmanip.m_data[i];
if(ndef->get(n).walkable
&& n.getContent() != c_tree
&& n.getContent() != c_leaves)
break;
vmanip.m_area.add_y(em, i, -1);
}
if(y >= y_nodes_min)
return y;
else
return y_nodes_min - 1;
}
#endif
// Returns Y one under area minimum if not found
static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
INodeDefManager *ndef)
{
v3s16 em = vmanip.m_area.getExtent();
s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
s16 y;
content_t c_stone = ndef->getId("mapgen_stone");
content_t c_desert_stone = ndef->getId("mapgen_desert_stone");
for(y=y_nodes_max; y>=y_nodes_min; y--)
{
MapNode &n = vmanip.m_data[i];
content_t c = n.getContent();
if(c != CONTENT_IGNORE && (
c == c_stone || c == c_desert_stone))
break;
vmanip.m_area.add_y(em, i, -1);
}
if(y >= y_nodes_min)
return y;
else
return y_nodes_min - 1;
}
#endif
void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
bool is_apple_tree, INodeDefManager *ndef)
{
MapNode treenode(ndef->getId("mapgen_tree"));
MapNode leavesnode(ndef->getId("mapgen_leaves"));
MapNode applenode(ndef->getId("mapgen_apple"));
s16 trunk_h = myrand_range(4, 5);
v3s16 p1 = p0;
for(s16 ii=0; ii<trunk_h; ii++)
{
if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
p1.Y++;
}
// p1 is now the last piece of the trunk
p1.Y -= 1;
VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
Buffer<u8> leaves_d(leaves_a.getVolume());
for(s32 i=0; i<leaves_a.getVolume(); i++)
leaves_d[i] = 0;
// Force leaves at near the end of the trunk
{
s16 d = 1;
for(s16 z=-d; z<=d; z++)
for(s16 y=-d; y<=d; y++)
for(s16 x=-d; x<=d; x++)
{
leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
}
}
// Add leaves randomly
for(u32 iii=0; iii<7; iii++)
{
s16 d = 1;
v3s16 p(
myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
);
for(s16 z=0; z<=d; z++)
for(s16 y=0; y<=d; y++)
for(s16 x=0; x<=d; x++)
{
leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
}
}
// Blit leaves to vmanip
for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
{
v3s16 p(x,y,z);
p += p1;
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
continue;
u32 i = leaves_a.index(x,y,z);
if(leaves_d[i] == 1) {
bool is_apple = myrand_range(0,99) < 10;
if(is_apple_tree && is_apple) {
vmanip.m_data[vi] = applenode;
} else {
vmanip.m_data[vi] = leavesnode;
}
}
}
}
#if 0
static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
INodeDefManager *ndef)
{
MapNode treenode(ndef->getId("mapgen_jungletree"));
MapNode leavesnode(ndef->getId("mapgen_leaves"));
for(s16 x=-1; x<=1; x++)
for(s16 z=-1; z<=1; z++)
{
if(myrand_range(0, 2) == 0)
continue;
v3s16 p1 = p0 + v3s16(x,0,z);
v3s16 p2 = p0 + v3s16(x,-1,z);
if(vmanip.m_area.contains(p2)
&& vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR)
vmanip.m_data[vmanip.m_area.index(p2)] = treenode;
else if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
}
s16 trunk_h = myrand_range(8, 12);
v3s16 p1 = p0;
for(s16 ii=0; ii<trunk_h; ii++)
{
if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
p1.Y++;
}
// p1 is now the last piece of the trunk
p1.Y -= 1;
VoxelArea leaves_a(v3s16(-3,-2,-3), v3s16(3,2,3));
//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
Buffer<u8> leaves_d(leaves_a.getVolume());
for(s32 i=0; i<leaves_a.getVolume(); i++)
leaves_d[i] = 0;
// Force leaves at near the end of the trunk
{
s16 d = 1;
for(s16 z=-d; z<=d; z++)
for(s16 y=-d; y<=d; y++)
for(s16 x=-d; x<=d; x++)
{
leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
}
}
// Add leaves randomly
for(u32 iii=0; iii<30; iii++)
{
s16 d = 1;
v3s16 p(
myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
);
for(s16 z=0; z<=d; z++)
for(s16 y=0; y<=d; y++)
for(s16 x=0; x<=d; x++)
{
leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
}
}
// Blit leaves to vmanip
for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
{
v3s16 p(x,y,z);
p += p1;
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
continue;
u32 i = leaves_a.index(x,y,z);
if(leaves_d[i] == 1)
vmanip.m_data[vi] = leavesnode;
}
}
static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0,
INodeDefManager *ndef)
{
MapNode papyrusnode(ndef->getId("mapgen_papyrus"));
s16 trunk_h = myrand_range(2, 3);
v3s16 p1 = p0;
for(s16 ii=0; ii<trunk_h; ii++)
{
if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = papyrusnode;
p1.Y++;
}
}
static void make_cactus(VoxelManipulator &vmanip, v3s16 p0,
INodeDefManager *ndef)
{
MapNode cactusnode(ndef->getId("mapgen_cactus"));
s16 trunk_h = 3;
v3s16 p1 = p0;
for(s16 ii=0; ii<trunk_h; ii++)
{
if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = cactusnode;
p1.Y++;
}
}
#endif
#if 0
/*
Dungeon making routines
*/
#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
static void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace,
INodeDefManager *ndef)
{
// Make +-X walls
for(s16 z=0; z<roomsize.Z; z++)
for(s16 y=0; y<roomsize.Y; y++)
{
{
v3s16 p = roomplace + v3s16(0,y,z);
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
continue;
vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
}
{
v3s16 p = roomplace + v3s16(roomsize.X-1,y,z);
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
continue;
vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
}
}
// Make +-Z walls
for(s16 x=0; x<roomsize.X; x++)
for(s16 y=0; y<roomsize.Y; y++)
{
{
v3s16 p = roomplace + v3s16(x,y,0);
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
continue;
vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
}
{
v3s16 p = roomplace + v3s16(x,y,roomsize.Z-1);
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
continue;
vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
}
}
// Make +-Y walls (floor and ceiling)
for(s16 z=0; z<roomsize.Z; z++)
for(s16 x=0; x<roomsize.X; x++)
{
{
v3s16 p = roomplace + v3s16(x,0,z);
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
continue;
vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
}
{
v3s16 p = roomplace + v3s16(x,roomsize.Y-1,z);
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
continue;
vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
}
}
// Fill with air
for(s16 z=1; z<roomsize.Z-1; z++)
for(s16 y=1; y<roomsize.Y-1; y++)
for(s16 x=1; x<roomsize.X-1; x++)
{
v3s16 p = roomplace + v3s16(x,y,z);
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
vmanip.m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
vmanip.m_data[vi] = MapNode(CONTENT_AIR);
}
}
static void make_fill(VoxelManipulator &vmanip, v3s16 place, v3s16 size,
u8 avoid_flags, MapNode n, u8 or_flags)
{
for(s16 z=0; z<size.Z; z++)
for(s16 y=0; y<size.Y; y++)
for(s16 x=0; x<size.X; x++)
{
v3s16 p = place + v3s16(x,y,z);
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_flags[vi] & avoid_flags)
continue;
vmanip.m_flags[vi] |= or_flags;
vmanip.m_data[vi] = n;
}
}
static void make_hole1(VoxelManipulator &vmanip, v3s16 place,
INodeDefManager *ndef)
{
make_fill(vmanip, place, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
VMANIP_FLAG_DUNGEON_INSIDE);
}
static void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir,
INodeDefManager *ndef)
{
make_hole1(vmanip, doorplace, ndef);
// Place torch (for testing)
//vmanip.m_data[vmanip.m_area.index(doorplace)] = MapNode(ndef->getId("mapgen_torch"));
}
static v3s16 rand_ortho_dir(PseudoRandom &random)
{
if(random.next()%2==0)
return random.next()%2 ? v3s16(-1,0,0) : v3s16(1,0,0);
else
return random.next()%2 ? v3s16(0,0,-1) : v3s16(0,0,1);
}
static v3s16 turn_xz(v3s16 olddir, int t)
{
v3s16 dir;
if(t == 0)
{
// Turn right
dir.X = olddir.Z;
dir.Z = -olddir.X;
dir.Y = olddir.Y;
}
else
{
// Turn left
dir.X = -olddir.Z;
dir.Z = olddir.X;
dir.Y = olddir.Y;
}
return dir;
}
static v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
{
int turn = random.range(0,2);
v3s16 dir;
if(turn == 0)
{
// Go straight
dir = olddir;
}
else if(turn == 1)
// Turn right
dir = turn_xz(olddir, 0);
else
// Turn left
dir = turn_xz(olddir, 1);
return dir;
}
static void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace,
v3s16 doordir, v3s16 &result_place, v3s16 &result_dir,
PseudoRandom &random, INodeDefManager *ndef)
{
make_hole1(vmanip, doorplace, ndef);
v3s16 p0 = doorplace;
v3s16 dir = doordir;
u32 length;
if(random.next()%2)
length = random.range(1,13);
else
length = random.range(1,6);
length = random.range(1,13);
u32 partlength = random.range(1,13);
u32 partcount = 0;
s16 make_stairs = 0;
if(random.next()%2 == 0 && partlength >= 3)
make_stairs = random.next()%2 ? 1 : -1;
for(u32 i=0; i<length; i++)
{
v3s16 p = p0 + dir;
if(partcount != 0)
p.Y += make_stairs;
/*// If already empty
if(vmanip.getNodeNoExNoEmerge(p).getContent()
== CONTENT_AIR
&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
== CONTENT_AIR)
{
}*/
if(vmanip.m_area.contains(p) == true
&& vmanip.m_area.contains(p+v3s16(0,1,0)) == true)
{
if(make_stairs)
{
make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,5,3),
VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(ndef->getId("mapgen_cobble")), 0);
make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
VMANIP_FLAG_DUNGEON_INSIDE);
make_fill(vmanip, p-dir, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
VMANIP_FLAG_DUNGEON_INSIDE);
}
else
{
make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,4,3),
VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(ndef->getId("mapgen_cobble")), 0);
make_hole1(vmanip, p, ndef);
/*make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
VMANIP_FLAG_DUNGEON_INSIDE);*/
}
p0 = p;
}
else
{
// Can't go here, turn away
dir = turn_xz(dir, random.range(0,1));
make_stairs = -make_stairs;
partcount = 0;
partlength = random.range(1,length);
continue;
}
partcount++;
if(partcount >= partlength)
{
partcount = 0;
dir = random_turn(random, dir);
partlength = random.range(1,length);
make_stairs = 0;
if(random.next()%2 == 0 && partlength >= 3)
make_stairs = random.next()%2 ? 1 : -1;
}
}
result_place = p0;
result_dir = dir;
}
class RoomWalker
{
public:
RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random,
INodeDefManager *ndef):
vmanip(vmanip_),
m_pos(pos),
m_random(random),
m_ndef(ndef)
{
randomizeDir();
}
void randomizeDir()
{
m_dir = rand_ortho_dir(m_random);
}
void setPos(v3s16 pos)
{
m_pos = pos;
}
void setDir(v3s16 dir)
{
m_dir = dir;
}
bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
{
for(u32 i=0; i<100; i++)
{
v3s16 p = m_pos + m_dir;
v3s16 p1 = p + v3s16(0,1,0);
if(vmanip.m_area.contains(p) == false
|| vmanip.m_area.contains(p1) == false
|| i % 4 == 0)
{
randomizeDir();
continue;
}
if(vmanip.getNodeNoExNoEmerge(p).getContent()
== m_ndef->getId("mapgen_cobble")
&& vmanip.getNodeNoExNoEmerge(p1).getContent()
== m_ndef->getId("mapgen_cobble"))
{
// Found wall, this is a good place!
result_place = p;
result_dir = m_dir;
// Randomize next direction
randomizeDir();
return true;
}
/*
Determine where to move next
*/
// Jump one up if the actual space is there
if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
== m_ndef->getId("mapgen_cobble")
&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
== CONTENT_AIR
&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent()
== CONTENT_AIR)
p += v3s16(0,1,0);
// Jump one down if the actual space is there
if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
== m_ndef->getId("mapgen_cobble")
&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
== CONTENT_AIR
&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent()
== CONTENT_AIR)
p += v3s16(0,-1,0);
// Check if walking is now possible
if(vmanip.getNodeNoExNoEmerge(p).getContent()
!= CONTENT_AIR
|| vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
!= CONTENT_AIR)
{
// Cannot continue walking here
randomizeDir();
continue;
}
// Move there
m_pos = p;
}
return false;
}
bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
v3s16 &result_doordir, v3s16 &result_roomplace)
{
for(s16 trycount=0; trycount<30; trycount++)
{
v3s16 doorplace;
v3s16 doordir;
bool r = findPlaceForDoor(doorplace, doordir);
if(r == false)
continue;
v3s16 roomplace;
// X east, Z north, Y up
#if 1
if(doordir == v3s16(1,0,0)) // X+
roomplace = doorplace +
v3s16(0,-1,m_random.range(-roomsize.Z+2,-2));
if(doordir == v3s16(-1,0,0)) // X-
roomplace = doorplace +
v3s16(-roomsize.X+1,-1,m_random.range(-roomsize.Z+2,-2));
if(doordir == v3s16(0,0,1)) // Z+
roomplace = doorplace +
v3s16(m_random.range(-roomsize.X+2,-2),-1,0);
if(doordir == v3s16(0,0,-1)) // Z-
roomplace = doorplace +
v3s16(m_random.range(-roomsize.X+2,-2),-1,-roomsize.Z+1);
#endif
#if 0
if(doordir == v3s16(1,0,0)) // X+
roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2);
if(doordir == v3s16(-1,0,0)) // X-
roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2);
if(doordir == v3s16(0,0,1)) // Z+
roomplace = doorplace + v3s16(-roomsize.X/2,-1,0);
if(doordir == v3s16(0,0,-1)) // Z-
roomplace = doorplace + v3s16(-roomsize.X/2,-1,-roomsize.Z+1);
#endif
|