local max_depth = 31000 local cells = 10 local cell_size = 60 / cells dofile(fun_caves.path .. "/trophies.lua") dofile(fun_caves.path .. "/tesseract.lua") -- dungeon floor, basic newnode = fun_caves.clone_node("default:stone") newnode.description = "Dungeon Stone" newnode.legacy_mineral = false newnode.groups = {fortress = 1} minetest.register_node("fun_caves:dungeon_floor_1", newnode) -- dungeon walls, basic newnode = fun_caves.clone_node("default:sandstone") newnode.description = "Dungeon Stone" newnode.groups = {fortress = 1} minetest.register_node("fun_caves:dungeon_wall_1", newnode) -- dungeon walls, type 2 newnode = fun_caves.clone_node("default:desert_stone") newnode.description = "Dungeon Stone" newnode.groups = {fortress = 1} minetest.register_node("fun_caves:dungeon_wall_2", newnode) newnode = fun_caves.clone_node("default:glass") newnode.description = "Dungeon Stone" newnode.groups = {fortress = 1} minetest.register_node("fun_caves:dungeon_wall_transparent", newnode) newnode = fun_caves.clone_node("default:glass") newnode.description = "Dungeon Stone" newnode.groups = {fortress = 1} newnode.tiles = {'fun_caves_blank.png'} newnode.pointable = false minetest.register_node("fun_caves:dungeon_wall_invisible", newnode) newnode = fun_caves.clone_node("default:meselamp") newnode.description = "Dungeon Light" newnode.light_source = 10 newnode.groups = {fortress = 1} minetest.register_node("fun_caves:dungeon_light", newnode) local treasures = { {'fun_caves:aquamarine', 'fun_caves:garnet', 'fun_caves:zoisite', 'fun_caves:coral_gem', 'fun_caves:moonstone', 'fun_caves:pure_copper', 'fun_caves:sky_iron', 'fun_caves:sky_iron', 'fun_caves:sky_iron', 'fun_caves:sky_iron', 'default:obsidian'}, {'fun_caves:aquamarine', 'fun_caves:garnet', 'fun_caves:zoisite', 'fun_caves:coral_gem', 'fun_caves:moonstone', 'fun_caves:pure_copper', 'fun_caves:metallic_ice', 'fun_caves:metallic_ice', 'fun_caves:metallic_ice', 'fun_caves:metallic_ice', 'default:obsidian'}, {'fun_caves:aquamarine', 'fun_caves:garnet', 'fun_caves:zoisite', 'fun_caves:coral_gem', 'fun_caves:moonstone', 'default:obsidian'}, {'fun_caves:aquamarine', 'fun_caves:garnet', 'fun_caves:zoisite', 'fun_caves:coral_gem', 'fun_caves:moonstone', 'default:obsidian'}, {'fun_caves:aquamarine', 'fun_caves:garnet', 'fun_caves:zoisite', 'fun_caves:coral_gem', 'fun_caves:moonstone', 'default:obsidian'}, {'fun_caves:aquamarine', 'fun_caves:garnet', 'fun_caves:zoisite', 'fun_caves:coral_gem', 'fun_caves:moonstone', 'default:obsidian'}, } local filler = {'default:apple 50', 'default:coal_lump 99', 'default:wood 99', 'default:obsidian_shard', 'default:steel_ingot 10', 'default:mese_crystal', 'default:copper_ingot 10', 'default:bronze_ingot 10', 'default:diamond 10', 'fun_caves:silver_ingot 10', 'default:gold_ingot 10', 'fun_caves:moon_glass 25', 'fun_caves:moon_juice 50', 'default:sword_mese', 'default:pick_mese', 'default:sword_diamond', 'default:pick_diamond', 'fun_caves:constant_flame'} if minetest.registered_entities['mobs_monster:stone_monster'] then filler[#filler+1] = 'mobs_monster:stone_monster' end if minetest.registered_entities['mobs_monster:dungeon_master'] then filler[#filler+1] = 'mobs_monster:dungeon_master' end local trophies = { {'fun_caves:unobtainium', 'fun_caves:philosophers_stone'}, {'fun_caves:unobtainium', 'fun_caves:philosophers_stone'}, {'fun_caves:unobtainium', 'fun_caves:philosophers_stone'}, {'fun_caves:unobtainium', 'fun_caves:philosophers_stone'}, {'fun_caves:unobtainium', 'fun_caves:philosophers_stone'}, {'fun_caves:unobtainium', 'fun_caves:philosophers_stone'}, } local chest_formspec = "size[8,9]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots .. "list[current_name;main;0,0.3;8,4;]" .. "list[current_player;main;0,4.85;8,1;]" .. "list[current_player;main;0,6.08;8,3;8]" .. "listring[current_name;main]" .. "listring[current_player;main]" .. default.get_hotbar_bg(0,4.85) local newnode = fun_caves.clone_node("default:chest") newnode.description = "Treasure Chest" newnode.on_construct = nil newnode.drop = 'default:chest' newnode.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) if not pos then return end local meta = minetest.get_meta(pos) if not meta then return end local ready = meta:get_string('formspec') if ready == '' then local level = math.max(6, math.ceil(pos.y / math.floor(max_depth / 6))) local big_item = treasures[level][math.random(#treasures[level])] meta:set_string("formspec", chest_formspec) local inv = meta:get_inventory() inv:set_size("main", 8*4) inv:add_item('main', big_item) for i = 1, math.random(12) do inv:add_item('main', filler[math.random(#filler)]) end if math.random(20) == 1 then inv:add_item('main', trophies[level][math.random(#trophies[level])]) end end end minetest.register_node("fun_caves:coffer", newnode) -- All arrays in the maze functions are zero-based. local floor_noise_1 = {offset = 0, scale = 1, seed = 1587, spread = {x = 30, y = 30, z = 30}, octaves = 3, persist = 1, lacunarity = 2} function table.shuffle(self) local j local t = self for i = #t, 1, -1 do j = math.random(0, i) t[i], t[j] = t[j], t[i] end end function unionfind(max) if not (max and type(max) == 'number') then return end local u = { _parent = {}, _rank = {} } for i = 0, max-1 do u._parent[i] = i u._rank[i] = 0 end u.find = function(self, i) local p = self._parent[i] if i == p then return i end self._parent[i] = self:find(p) return self._parent[i] end u.union = function(self, i, j) i = i or 0 j = j or 0 local root1 = self:find(i) local root2 = self:find(j) if root1 == root2 then return end if self._rank[root1] > self._rank[root2] then self._parent[root2] = root1 elseif self._rank[root2] > self._rank[root1] then self._parent[root1] = root2 else self._parent[root2] = root1 self._rank[root1] = self._rank[root1] + 1 end end return u end local function maze_floor(y2, minp, maxp, data, area, node) if not (y2 and minp and maxp and data and area and node and type(y2) == 'number' and type(data) == 'table') then return end -- walls is zero-based. local walls = {} for i = 0, 2 * cells * cells - 1 do walls[i] = i end table.shuffle(walls) local set = unionfind(cells * cells) -- Remove walls in a continuous path. for m = 0, #walls-1 do local c = walls[m] local a = math.floor(c / 2) local i = a % cells local j = math.floor(a / cells) local u = c % 2 == 0 and 1 or 0 local v = c % 2 == 1 and 1 or 0 local b = a + u + cells * v if i < cells - u and j < cells - v and set:find(a) ~= set:find(b) then set:union(a, b) local x = (i + u) * cell_size + minp.x local y = minp.y + y2 * cell_size local z = (j + v) * cell_size + minp.z for z1 = z + (1-v), z + (1-v) * (cell_size - 1) do for y1 = y + 1, y + (cell_size - 1) do local ivm = area:index(x + (1-u), y1, z1) for x1 = x + (1-u), x + (1-u) * (cell_size - 1) do if x1 < maxp.x and z1 < maxp.z and x1 > minp.x and z1 > minp.z then data[ivm] = node["air"] end ivm = ivm + 1 end end end end end end local designs = { { {0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0}, {0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0}, {0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0}, {1,1,1,1,0,1,1,1,1,1,0,1,1,1,0,1,0,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, {1,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0}, {0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0}, {0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0}, {0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0}, {1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, {1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0}, {0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0}, {0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0}, {0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0}, {0,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, }, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, }, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0}, {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, {0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0}, {0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0}, {0,1,0,1,0,1,1,1,1,1,0,0,0,1,0,1,0,1,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0}, {0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0}, {0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,0}, {0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0}, {0,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,1,0}, {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, }, { {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0}, }, } local function design_floor(y2, minp, maxp, data, area, node) if not (y2 and minp and maxp and data and area and node and type(y2) == 'number' and type(data) == 'table') then return end local design = designs[math.random(#designs)] local py = minp.y + y2 * cell_size for z = 1, 10 do for x = 1, 9 do if design[z * 2 - 1][x * 2] == 0 then local px = minp.x + x * cell_size local pz = minp.z + (z - 1) * cell_size for z1 = pz + 1, pz + cell_size - 1 do local ivm = area:index(px, py + 1, z1) for y1 = py + 1, py + cell_size - 1 do data[ivm] = node['air'] ivm = ivm + area.ystride end end end end end for z = 1, 9 do for x = 1, 10 do if design[z * 2][x * 2 - 1] == 0 then local px = minp.x + (x - 1) * cell_size local pz = minp.z + z * cell_size for x1 = px + 1, px + cell_size - 1 do local ivm = area:index(x1, py + 1, pz) for y1 = py + 1, py + cell_size - 1 do data[ivm] = node['air'] ivm = ivm + area.ystride end end end end end for z = 1, 9 do for x = 1, 9 do if design[z * 2][x * 2] == 0 then local px = minp.x + x * cell_size local pz = minp.z + z * cell_size local ivm = area:index(px, py + 1, pz) for y1 = py + 1, py + cell_size - 1 do data[ivm] = node['air'] ivm = ivm + area.ystride end end end end end fun_caves.fortress = function(minp_in, maxp_in, data, area, node) if not (minp_in and maxp_in and data and area and node and type(data) == 'table') then return end -- hungry maze -- chests (w traps) -- step traps (math based) -- hidden doors/downs -- hot/ice floors -- torches -- local minp = vector.add(minp_in, 10) local maxp = vector.subtract(maxp_in, 9) local level = math.min(6, math.ceil(maxp.y / math.floor(max_depth / -6))) local inner_floor = node['fun_caves:dungeon_floor_1'] local outer_wall = node['fun_caves:dungeon_wall_2'] local inner_wall = node['fun_caves:dungeon_wall_1'] local treasure_count = 0 local csize = vector.add(vector.subtract(maxp_in, minp_in), 1) local floor_1 = minetest.get_perlin_map(floor_noise_1, {x=csize.x, y=csize.z}):get2dMap_flat({x=minp_in.x, y=minp_in.z}) if not floor_1 then return end for y2 = 0, cells-1 do local floor_type = math.random(20) local dox, doz = math.random(0, cells-1), math.random(0, cells-1) for z = minp.z, maxp.z do for y = minp.y + y2 * cell_size, minp.y + y2 * cell_size + (cell_size - 1) do local ivm = area:index(minp.x, y, z) for x = minp.x, maxp.x do if x == minp.x or z == minp.z or x == maxp.x or z == maxp.z then data[ivm] = outer_wall elseif (y - minp.y) % cell_size == 0 then if math.floor((z - minp.z) / cell_size) == doz and math.floor((x - minp.x) / cell_size) == dox and (z - minp.z) % cell_size ~= 0 and (x - minp.x) % cell_size ~= 0 and y ~= minp.y then data[ivm] = node["air"] else data[ivm] = inner_floor end elseif floor_type ~= 3 and ((z - minp.z) % cell_size == 0 or (x - minp.x) % cell_size == 0) then if y == minp.y + y2 * cell_size + 3 and ((z - minp.z) % cell_size == 3 or (x - minp.x) % cell_size == 3) and math.random(5) == 1 then data[ivm] = node['fun_caves:dungeon_light'] elseif floor_type == 1 and level > 3 then data[ivm] = node['fun_caves:dungeon_wall_invisible'] elseif floor_type == 1 then data[ivm] = node['fun_caves:dungeon_wall_transparent'] elseif floor_type ~= 3 then data[ivm] = inner_wall end else if y2 == 0 and y == minp.y + y2 * cell_size + 1 and data[ivm - area.ystride] ~= node['air'] and math.random(700) == 1 then treasure_count = treasure_count + 1 data[ivm] = node['fun_caves:coffer'] else data[ivm] = node["air"] end end ivm = ivm + 1 end end end if floor_type ~= 3 then local wall_type = math.random(#designs + 1) if wall_type == 1 then maze_floor(y2, minp, maxp, data, area, node) else design_floor(y2, minp, maxp, data, area, node) end end local index = 0 for z = minp.z + 1, maxp.z - 1 do for x = minp.x + 1, maxp.x - 1 do local ivm = area:index(x, minp.y + y2 * cell_size, z) if floor_type == 2 then if data[ivm] ~= node['air'] and data[ivm + area.ystride] == node['air'] then if level > 3 then data[ivm + area.ystride] = node['default:lava_source'] else data[ivm + area.ystride] = node['fun_caves:hot_cobble'] end end elseif floor_type == 3 then if (y2 < 9 or (z - minp_in.z) > 16 or (x - minp_in.x) > 16) and data[ivm] ~= node['air'] then local index = (z - minp_in.z) * csize.x + x - minp_in.x for i = 1, math.min((cell_size - 1), math.floor((cell_size - 1) * math.abs(floor_1[index]))) do if data[ivm + area.ystride * i] == node['air'] then data[ivm + area.ystride * i] = node['default:dirt'] end end end elseif floor_type == 4 then for i = 1, (cell_size - 1) do if data[ivm + area.ystride * i] == node['air'] then data[ivm + area.ystride * i] = node['default:water_source'] end end end end end end end