-- Parameters local TCAVE = 0.6 local nobj_cave = nil -- 3D noise local np_cave = { offset = 0, scale = 1, spread = {x = 384, y = 128, z = 384}, -- squashed 3:1 seed = 59033, octaves = 5, persist = 0.7 } -- Portal frame material local portal_frame = "mcl_nether:quartz_block" -- Destroy portal if pos (portal frame or portal node) got destroyed local destroy_portal = function(pos) -- Deactivate Nether portal local meta = minetest.get_meta(pos) local p1 = minetest.string_to_pos(meta:get_string("portal_frame1")) local p2 = minetest.string_to_pos(meta:get_string("portal_frame2")) if not p1 or not p2 then return end local first = true -- p1 metadata of first node local mp1 for x = p1.x, p2.x do for y = p1.y, p2.y do for z = p1.z, p2.z do local p = vector.new(x, y, z) local m = minetest.get_meta(p) if first then --[[ Only proceed if the first node still has metadata. If it doesn't have metadata, another node propably triggred the delection routine earlier, so we bail out earlier to avoid an infinite cascade of on_destroy events. ]] mp1 = minetest.string_to_pos(m:get_string("portal_frame1")) if not mp1 then return end end local nn = minetest.get_node(p).name if nn == portal_frame or nn == "mcl_portals:portal_end" then -- Remove portal nodes, but not myself if nn == "mcl_portals:portal_end" and not vector.equals(p, pos) then minetest.remove_node(p) end -- Clear metadata of portal nodes and the frame m:set_string("portal_frame1", "") m:set_string("portal_frame2", "") m:set_string("portal_target", "") end first = false end end end end -- Nodes minetest.register_node("mcl_portals:portal_end", { description = "End Portal", tiles = { "blank.png", "blank.png", "blank.png", "blank.png", { name = "mcl_portals_end_portal.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 2.0, }, }, { name = "mcl_portals_end_portal.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 2.0, }, }, }, drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", sunlight_propagates = true, use_texture_alpha = true, walkable = false, diggable = false, pointable = false, buildable_to = false, is_ground_content = false, drop = "", -- This is 15 in MC. light_source = 14, post_effect_color = {a = 192, r = 0, g = 0, b = 0}, alpha = 192, node_box = { type = "fixed", fixed = { {-0.5, -0.5, -0.1, 0.5, 0.5, 0.1}, }, }, groups = {not_in_creative_inventory = 1}, on_destruct = destroy_portal, _mcl_hardness = -1, _mcl_blast_resistance = 18000000, }) local function build_end_portal(pos, target3) local p = {x = pos.x - 1, y = pos.y - 1, z = pos.z} local p1 = {x = pos.x - 1, y = pos.y - 1, z = pos.z} local p2 = {x = p1.x + 3, y = p1.y + 4, z = p1.z} for i = 1, 4 do minetest.set_node(p, {name = portal_frame}) p.y = p.y + 1 end for i = 1, 3 do minetest.set_node(p, {name = portal_frame}) p.x = p.x + 1 end for i = 1, 4 do minetest.set_node(p, {name = portal_frame}) p.y = p.y - 1 end for i = 1, 3 do minetest.set_node(p, {name = portal_frame}) p.x = p.x - 1 end for x = p1.x, p2.x do for y = p1.y, p2.y do p = {x = x, y = y, z = p1.z} if not (x == p1.x or x == p2.x or y == p1.y or y == p2.y) then minetest.set_node(p, {name = "mcl_portals:portal_end", param2 = 0}) end local meta = minetest.get_meta(p) meta:set_string("portal_frame1", minetest.pos_to_string(p1)) meta:set_string("portal_frame2", minetest.pos_to_string(p2)) meta:set_string("portal_target", minetest.pos_to_string(target3)) if y ~= p1.y then for z = -2, 2 do if z ~= 0 then p.z = p.z + z if minetest.registered_nodes[ minetest.get_node(p).name].is_ground_content then minetest.remove_node(p) end p.z = p.z - z end end end end end end local function find_end_target3_y2(target3_x, target3_z) local start_y = mcl_vars.mg_end_min + math.random(20, 120) -- Search start if not nobj_cave then nobj_cave = minetest.get_perlin(np_cave) end local air = 0 -- Consecutive air nodes found for y = start_y, start_y - 120, -1 do local nval_cave = nobj_cave:get3d({x = target3_x, y = y, z = target3_z}) if nval_cave > TCAVE then -- Cavern air = air + 1 else -- Not cavern, check if 4 nodes of space above if air >= 4 then return y + 2 else -- Not enough space, reset air to zero air = 0 end end end return start_y -- Fallback end local function move_check2(p1, max, dir) local p = {x = p1.x, y = p1.y, z = p1.z} local d = math.abs(max - p1[dir]) / (max - p1[dir]) while p[dir] ~= max do p[dir] = p[dir] + d if minetest.get_node(p).name ~= portal_frame then return false end end return true end local function check_end_portal(p1, p2) if p1.x ~= p2.x then if not move_check2(p1, p2.x, "x") then return false end if not move_check2(p2, p1.x, "x") then return false end elseif p1.z ~= p2.z then if not move_check2(p1, p2.z, "z") then return false end if not move_check2(p2, p1.z, "z") then return false end else return false end if not move_check2(p1, p2.y, "y") then return false end if not move_check2(p2, p1.y, "y") then return false end return true end local function is_end_portal(pos) for d = -3, 3 do for y = -4, 4 do local px = {x = pos.x + d, y = pos.y + y, z = pos.z} local pz = {x = pos.x, y = pos.y + y, z = pos.z + d} if check_end_portal(px, {x = px.x + 3, y = px.y + 4, z = px.z}) then return px, {x = px.x + 3, y = px.y + 4, z = px.z} end if check_end_portal(pz, {x = pz.x, y = pz.y + 4, z = pz.z + 3}) then return pz, {x = pz.x, y = pz.y + 4, z = pz.z + 3} end end end end local function make_end_portal(pos) local p1, p2 = is_end_portal(pos) if not p1 or not p2 then return false end for d = 1, 2 do for y = p1.y + 1, p2.y - 1 do local p if p1.z == p2.z then p = {x = p1.x + d, y = y, z = p1.z} else p = {x = p1.x, y = y, z = p1.z + d} end if minetest.get_node(p).name ~= "air" then return false end end end local param2 if p1.z == p2.z then param2 = 0 else param2 = 1 end local target3 = {x = p1.x, y = p1.y, z = p1.z} target3.x = target3.x + 1 if target3.y < mcl_vars.mg_end_max and target3.y > mcl_vars.mg_end_min then target3.y = math.random(mcl_vars.mg_overworld_min + 40, mcl_vars.mg_overworld_min + 96) else target3.y = find_end_target3_y2(target3.x, target3.z) end for d = 0, 3 do for y = p1.y, p2.y do local p = {} if param2 == 0 then p = {x = p1.x + d, y = y, z = p1.z} else p = {x = p1.x, y = y, z = p1.z + d} end if minetest.get_node(p).name == "air" then minetest.set_node(p, {name = "mcl_portals:portal_end", param2 = param2}) end local meta = minetest.get_meta(p) -- Portal frame corners meta:set_string("portal_frame1", minetest.pos_to_string(p1)) meta:set_string("portal_frame2", minetest.pos_to_string(p2)) -- Portal target coordinates meta:set_string("portal_target", minetest.pos_to_string(target3)) end end return true end minetest.register_abm({ label = "End portal teleportation", nodenames = {"mcl_portals:portal_end"}, interval = 1, chance = 2, action = function(pos, node) for _,obj in ipairs(minetest.get_objects_inside_radius(pos,1)) do --maikerumine added for objects to travel local lua_entity = obj:get_luaentity() --maikerumine added for objects to travel if obj:is_player() or lua_entity then local meta = minetest.get_meta(pos) local target3 = minetest.string_to_pos(meta:get_string("portal_target")) if target3 then -- force emerge of target3 area minetest.get_voxel_manip():read_from_map(target3, target3) if not minetest.get_node_or_nil(target3) then minetest.emerge_area( vector.subtract(target3, 4), vector.add(target3, 4)) end -- teleport the player minetest.after(3, function(obj, pos, target3) local objpos = obj:getpos() if objpos == nil then return end --maikerumine added for objects to travel if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then return end -- Build destination local function check_and_build_end_portal(pos, target3) local n = minetest.get_node_or_nil(target3) if n and n.name ~= "mcl_portals:portal_end" then build_end_portal(target3, pos) minetest.after(2, check_and_build_end_portal, pos, target3) elseif not n then minetest.after(1, check_and_build_end_portal, pos, target3) end end check_and_build_end_portal(pos, target3) -- Teleport obj:setpos(target3) minetest.sound_play("mcl_portals_teleport", {pos=target3, gain=0.5, max_hear_distance = 16}) end, obj, pos, target3) end end end end, }) --[[ ITEM OVERRIDES ]] -- Frame material minetest.override_item(portal_frame, { on_destruct = destroy_portal, }) -- Portal opener minetest.override_item("mcl_end:ender_eye", { _doc_items_longdesc = "An eye of ander can be used to open a portal to the End.", _doc_items_usagehelp = "To open an End portal, place an upright frame of quartz blocks with a length of 4 and a height of 5 blocks, leaving only air in the center. After placing this frame, use the eye of ender on the frame.", on_place = function(itemstack, user, pointed_thing) local nodedef = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name] --new -- If used on frame, open portal if pointed_thing.under and minetest.get_node(pointed_thing.under).name == portal_frame then make_end_portal(pointed_thing.under) if minetest.get_modpath("doc") then doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:portal_end") end minetest.sound_play( "fire_flint_and_steel", {pos = pointed_thing.above, gain = 0.5, max_hear_distance = 8} ) if not minetest.setting_getbool("creative_mode") and used == true then itemstack:take_item() -- 1 use end end return itemstack end, })