123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- -- Maze map generator mod for Minetest
- -- Copyright © 2017 Pedro Gimeno Fortea
- -- Many thanks to Paramat for the numerous tips with the flat world generator
- -- and understanding the VoxelManip, and the people at IRC for their support.
- -- Which height to use for the terrain
- local terrain_height = 0
- -- Flat world generator from here:
- -- https://gist.github.com/pgimeno/1e046dd6bacb1624a70b37af5ea1d805
- minetest.set_mapgen_setting('mg_name', 'flat', true)
- minetest.set_mapgen_setting('mgflat_ground_level', terrain_height, true)
- -- water has to be below ground level to not inundate everything;
- -- it can't be equal lest you spawn at (0,0,0).
- minetest.set_mapgen_setting('water_level', -31000, true)
- minetest.set_mapgen_setting('mg_flags', 'light, nocaves, nodungeons, nodecorations', true);
- minetest.clear_registered_biomes()
- minetest.clear_registered_ores()
- minetest.clear_registered_decorations()
- minetest.register_biome {
- name = "mazeworld",
- --node_dust = "",
- node_top = "default:dirt_with_dry_grass", -- layer 1
- depth_top = 1,
- -- node_filler = "default:dirt", -- layer 2 (not uniform)
- -- depth_filler = 0,
- node_stone = "default:desert_stone", -- layer 3 (all the way to the bottom)
- --node_water_top = "",
- --depth_water_top = ,
- --node_water = "",
- --node_river_water = "",
- y_min = -31000,
- y_max = 31000,
- heat_point = 50,
- humidity_point = 50,
- }
- -- <paramat> you can stack biomes on top of each other if you need changes of
- -- materials with depth
- -- Maze generator - a variant of the recursive nested algorithm
- -- with base size 2 and a method to make long walls less prominent (TODO)
- local wall_nodeid = minetest.get_content_id('default:sandstonebrick')
- local empty_nodeid = minetest.get_content_id('air')
- local maze_height = terrain_height + 1
- local worldseed = tonumber(minetest.get_mapgen_setting("seed"))
- -- These variables are used to pass values to the functions
- -- (a bit ugly, but more comfortable)
- local xmin, ymin, zmin, xmax, ymax, zmax
- local sx, sy, sxy, base, xbase, zbase
- -- This is the VoxelManip data. Make it a top-level local as recommended.
- local data = {}
- local function intersects(px, pz, size)
- return not (px + size - 1 < xmin or px > xmax
- or pz + size - 1 < zmin or pz > zmax)
- end
- -- Return a pseudorandom generator that generates a sequence
- -- which is always the same for a given size, 2D position,
- -- and world seed, but different for each.
- local function get_rng(px, pz, size)
- return PcgRandom(PcgRandom(worldseed, size):next() + px, pz)
- end
- local function setmaze(x, z, id)
- for y = 0, sx, sx do
- data[base + (z - zbase) * sxy + (x - xbase) + y] = id
- end
- end
- local function setwall(x, z)
- setmaze(x, z, wall_nodeid)
- end
- local function clearwall(x, z)
- setmaze(x, z, empty_nodeid)
- end
- local function mazegen_recursive(xpos, zpos, size)
- -- A size-2 maze is essentially a U-shape.
- local rng = get_rng(xpos, zpos, size)
- -- Pick up the direction where the wall is.
- local dir = rng:next(0, 3)
- if size == 2 then
- -- reached the end of the recursion - fill this 4x4 block
- -- first the fixed walls
- setwall(xpos, zpos)
- setwall(xpos + 1, zpos)
- setwall(xpos + 2, zpos)
- setwall(xpos + 3, zpos)
- setwall(xpos, zpos + 1)
- setwall(xpos, zpos + 2)
- setwall(xpos, zpos + 3)
- setwall(xpos + 2, zpos + 2)
- -- now the extra wall
- local ofsx = dir == 0 and 1 or dir == 2 and -1 or 0
- local ofsz = dir == 1 and 1 or dir == 3 and -1 or 0
- setwall(xpos + 2 + ofsx, zpos + 2 + ofsz)
- return
- end
- -- Test all 4 positions at this nesting level, recursing if the
- -- current position touches the block.
- for z = zpos, zpos + size, size do
- for x = xpos, xpos + size, size do
- if intersects(x, z, size) then
- mazegen_recursive(x, z, size / 2)
- end
- end
- end
- if dir ~= 0 then
- -- remove one random block from the wall that extends to the east
- local rnd = rng:next(0, size/2 - 1) * 2 + 1
- if intersects(xpos + size + rnd, zpos + size, 1) then
- clearwall(xpos + size + rnd, zpos + size)
- end
- end
- if dir ~= 1 then
- -- remove one random block from the wall that extends to the north
- local rnd = rng:next(0, size/2 - 1) * 2 + 1
- if intersects(xpos + size, zpos + size + rnd, 1) then
- clearwall(xpos + size, zpos + size + rnd)
- end
- end
- if dir ~= 2 then
- -- remove one random block from the wall that extends to the west
- local rnd = rng:next(0, size/2 - 1) * 2 + 1
- if intersects(xpos + size - rnd, zpos + size, 1) then
- clearwall(xpos + size - rnd, zpos + size)
- end
- end
- if dir ~= 3 then
- -- remove one random block from the wall that extends to the south
- local rnd = rng:next(0, size/2 - 1) * 2 + 1
- if intersects(xpos + size, zpos + size - rnd, 1) then
- clearwall(xpos + size, zpos + size - rnd)
- end
- end
- end
- minetest.register_on_generated(function (minp, maxp, blockseed)
- local vm, min, max = minetest.get_mapgen_object("voxelmanip")
- -- does block include maze_height or maze_height + 1?
- if minp.y <= maze_height and maxp.y >= maze_height
- or minp.y <= maze_height + 1 and maxp.y >= maze_height + 1
- then
- sx, sy = max.x - min.x + 1, max.y - min.y + 1
- sxy = sx * sy
- base = (maze_height - min.y) * sx + 1
- xbase, zbase = min.x, min.z
- xmin, ymin, zmin = minp.x, minp.y, minp.z
- xmax, ymax, zmax = maxp.x, maxp.y, maxp.z
- -- Prepare to modify the voxelmanip
- vm:get_data(data)
- mazegen_recursive(-32768, -32768, 32768)
- vm:set_data(data)
- vm:write_to_map()
- end
- end)
|