Skip to content

Instantly share code, notes, and snippets.

@Pilcrow182
Last active March 9, 2022 00:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pilcrow182/6566668e96e74de6d6dbbcf318ddabc1 to your computer and use it in GitHub Desktop.
Save Pilcrow182/6566668e96e74de6d6dbbcf318ddabc1 to your computer and use it in GitHub Desktop.
math.randomseed(os.time())
roguelike = {
init_map = function(width, height, wall_probability)
local map, x, y = {}, 0, 0
-- Fill map with random splatter. It will be smoothed by roguelike.next_gen()
for row = 1, height do
map[row] = {}
for column = 1, width do
map[row][column] = math.floor(math.random() + wall_probability / 100)
end
end
-- Paint a big X after random splatter to ensure the map is mostly connected.
-- this is faster than any flood-fill function I could come up with, but we
-- *may* generate small isolated sections since we're not checking connectivity.
local max_offset = math.max(width, height)
for offset = 1, max_offset do
x = math.floor((height * offset / max_offset) + 0.5)
y = math.floor((width * offset / max_offset) + 0.5)
for xc = math.max(1, x-1), math.min(height, x+1) do
for yc = math.max(1, y-1), math.min(width, y+1) do
map[xc][yc] = 0
map[xc][width+1-yc] = 0
end
end
end
return map
end,
get_neighbors = function(map, x, y, radius)
local neighbor_count, add_num = 0, 0
for xn = x - radius, x + radius do
for yn = y - radius, y + radius do
if map[xn] and map[xn][yn] then
add_num = map[xn][yn]
else
add_num = 0
end
neighbor_count = neighbor_count + add_num
end
end
return neighbor_count
end,
next_gen = function(map, near_threshold, far_threshold)
local new = {}
for row = 1, #map do
new[row] = {}
for column = 1, #map[1] do
local neighbors_near = roguelike.get_neighbors(map, row, column, 1)
local neighbors_far = roguelike.get_neighbors(map, row, column, 2)
if neighbors_near >= near_threshold or neighbors_far <= far_threshold then
new[row][column] = 1
else
new[row][column] = 0
end
end
end
return new
end,
make_map = function(width, height, wall_probability)
local map = roguelike.init_map(width, height, wall_probability)
for g = 1, 4 do map = roguelike.next_gen(map, 5, 2) end
for g = 1, 3 do map = roguelike.next_gen(map, 5, 0) end
for g = 1, 2 do map = roguelike.next_gen(map, 7, 0) end
return map
end,
print_map = function(data)
local line, char = "", ""
for row = 1, #data do
line = ""
for column = 1, #data[1] do
if data[row][column] == 1 then
char = "#"
else
char = "."
end
line = line .. char
end
print(line)
end
end,
}
local test_map = roguelike.make_map(70, 50, 20)
if minetest then -- this check is here so we can still debug using the cli lua interpreter
minetest.register_node("roguelike:stone", {
description = "Roguelike Stone",
tiles = {"default_stone.png"},
is_ground_content = false,
groups = {cracky = 3, stone = 1},
})
minetest.register_node("roguelike:air", {
description = "Glowing Air (roguelike)",
drawtype = "airlike",
inventory_image = "air.png",
wield_image = "air.png",
paramtype = "light",
light_source = 14,
is_ground_content = false,
air_equivalent = true,
floodable = true,
pointable = false,
sunlight_propagates = true,
walkable = false,
diggable = false,
buildable_to = true,
groups = {not_in_creative_inventory = 1},
})
roguelike.place_map = function(data, pos)
minetest.chat_send_all("Making map at pos " .. minetest.pos_to_string(pos))
local line, char, node_name = "", "", ""
for row = 0, #data+1 do
for column = 0, #data[1]+1 do
if data[row] and data[row][column] then
if data[row][column] == 1 then
node_name = "roguelike:stone"
else
node_name = "roguelike:air"
end
minetest.set_node({x = pos.x + row, y = pos.y - 1, z = pos.z + column}, {name="roguelike:stone"}) -- floor
for height = 0, 2 do
minetest.set_node({x = pos.x + row, y = pos.y + height, z = pos.z + column}, {name=node_name}) -- walls
end
minetest.set_node({x = pos.x + row, y = pos.y + 3, z = pos.z + column}, {name="roguelike:stone"}) -- ceiling
else
for height = -1, 3 do
minetest.set_node({x = pos.x + row, y = pos.y + height, z = pos.z + column}, {name="roguelike:stone"}) -- outer walls
end
end
end
end
end
minetest.register_chatcommand("test", {
params = "",
description = "create a procedually generated roguelike map (using Cellular Automata)",
func = function(name, param)
local player = minetest.get_player_by_name(name)
local pos = player:getpos()
pos = {x = math.floor(pos.x - 2.5), y = math.floor(pos.y - 4.5), z = math.floor(pos.z - 2.5)}
roguelike.place_map(test_map, pos)
pos = player:getpos()
pos = {x = math.floor(pos.x + 0.5), y = math.floor(pos.y - 4.5), z = math.floor(pos.z + 0.5)}
for xoff = -1, 1 do
for zoff = -1, 1 do
for yoff = 0, 2 do
minetest.set_node({x = pos.x + xoff, y = pos.y + yoff, z = pos.z + zoff}, {name="roguelike:air"})
end
for yoff = 3, 4 do
minetest.set_node({x = pos.x + xoff, y = pos.y + yoff, z = pos.z + zoff}, {name="roguelike:stone"})
end
end
end
for yoff = 0, 4 do
minetest.set_node({x = pos.x, y = pos.y + yoff, z = pos.z}, {name="default:ladder", param2 = 5})
minetest.set_node({x = pos.x, y = pos.y + yoff, z = pos.z - 1}, {name="roguelike:stone"})
end
end,
})
end
roguelike.print_map(test_map)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment