From 657851125f57467dee1d8f86af0db8d748691d9b Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 17 Aug 2017 00:16:29 +0200 Subject: [PATCH] Add nether and end portal (WIP) New mod: mcl_portals, based on maikerumine's work --- mods/MAPGEN/mcl_portals/LICENSE | 7 + mods/MAPGEN/mcl_portals/README.md | 9 + mods/MAPGEN/mcl_portals/depends.txt | 7 + mods/MAPGEN/mcl_portals/description.txt | 1 + mods/MAPGEN/mcl_portals/init.lua | 10 + mods/MAPGEN/mcl_portals/mod.conf | 1 + mods/MAPGEN/mcl_portals/portal_end.lua | 385 ++++++++++++++++ mods/MAPGEN/mcl_portals/portal_nether.lua | 416 ++++++++++++++++++ .../textures/mcl_portals_end_portal.png | Bin 0 -> 8925 bytes .../textures/mcl_portals_particle.png | Bin 0 -> 226 bytes .../textures/mcl_portals_portal.png | Bin 0 -> 8960 bytes 11 files changed, 836 insertions(+) create mode 100644 mods/MAPGEN/mcl_portals/LICENSE create mode 100644 mods/MAPGEN/mcl_portals/README.md create mode 100644 mods/MAPGEN/mcl_portals/depends.txt create mode 100644 mods/MAPGEN/mcl_portals/description.txt create mode 100644 mods/MAPGEN/mcl_portals/init.lua create mode 100644 mods/MAPGEN/mcl_portals/mod.conf create mode 100644 mods/MAPGEN/mcl_portals/portal_end.lua create mode 100644 mods/MAPGEN/mcl_portals/portal_nether.lua create mode 100644 mods/MAPGEN/mcl_portals/textures/mcl_portals_end_portal.png create mode 100644 mods/MAPGEN/mcl_portals/textures/mcl_portals_particle.png create mode 100644 mods/MAPGEN/mcl_portals/textures/mcl_portals_portal.png diff --git a/mods/MAPGEN/mcl_portals/LICENSE b/mods/MAPGEN/mcl_portals/LICENSE new file mode 100644 index 000000000..ece42d0aa --- /dev/null +++ b/mods/MAPGEN/mcl_portals/LICENSE @@ -0,0 +1,7 @@ +The MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/mods/MAPGEN/mcl_portals/README.md b/mods/MAPGEN/mcl_portals/README.md new file mode 100644 index 000000000..09b4f3110 --- /dev/null +++ b/mods/MAPGEN/mcl_portals/README.md @@ -0,0 +1,9 @@ +# Portals mod for MineClone 2 +## How to create portals + +Nether portal: Build an upright frame of obsidian, 4 blocks wide and 5 blocks high, and use a flint and steel inside it. +End portal: Build an upright frame of red nether brick blocks, 4 blocks wide and 5 blocks high, and use an eye of ender inside it. + +## Credits +Created by maikerumine and Wuzzy. +License: MIT License (see `LICENSE`). diff --git a/mods/MAPGEN/mcl_portals/depends.txt b/mods/MAPGEN/mcl_portals/depends.txt new file mode 100644 index 000000000..fbb8a6e2d --- /dev/null +++ b/mods/MAPGEN/mcl_portals/depends.txt @@ -0,0 +1,7 @@ +mcl_init +mcl_util +mcl_core +mcl_fire +mcl_nether +mcl_end +doc? diff --git a/mods/MAPGEN/mcl_portals/description.txt b/mods/MAPGEN/mcl_portals/description.txt new file mode 100644 index 000000000..fe84531f3 --- /dev/null +++ b/mods/MAPGEN/mcl_portals/description.txt @@ -0,0 +1 @@ +Adds buildable portals to the Nether and End dimensions. diff --git a/mods/MAPGEN/mcl_portals/init.lua b/mods/MAPGEN/mcl_portals/init.lua new file mode 100644 index 000000000..cf4716c40 --- /dev/null +++ b/mods/MAPGEN/mcl_portals/init.lua @@ -0,0 +1,10 @@ +-- Load files + +-- Nether portal: +-- Obsidian frame, activated by flint and steel +dofile(minetest.get_modpath("mcl_portals").."/portal_nether.lua") + +-- End portal (W.I.P): +-- Red nether brick block frame, activated by an eye of ender +dofile(minetest.get_modpath("mcl_portals").."/portal_end.lua") + diff --git a/mods/MAPGEN/mcl_portals/mod.conf b/mods/MAPGEN/mcl_portals/mod.conf new file mode 100644 index 000000000..e82fbe6c1 --- /dev/null +++ b/mods/MAPGEN/mcl_portals/mod.conf @@ -0,0 +1 @@ +name = mcl_portals diff --git a/mods/MAPGEN/mcl_portals/portal_end.lua b/mods/MAPGEN/mcl_portals/portal_end.lua new file mode 100644 index 000000000..2de6174f3 --- /dev/null +++ b/mods/MAPGEN/mcl_portals/portal_end.lua @@ -0,0 +1,385 @@ +-- Parameters + +local END_DEPTH = mcl_vars.mg_end_min +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 +} + +-- 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} +}) + +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 = "mcl_nether:red_nether_brick"}) + p.y = p.y + 1 + end + for i = 1, 3 do + minetest.set_node(p, {name = "mcl_nether:red_nether_brick"}) + p.x = p.x + 1 + end + for i = 1, 4 do + minetest.set_node(p, {name = "mcl_nether:red_nether_brick"}) + p.y = p.y - 1 + end + for i = 1, 3 do + minetest.set_node(p, {name = "mcl_nether:red_nether_brick"}) + 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("p1", minetest.pos_to_string(p1)) + meta:set_string("p2", minetest.pos_to_string(p2)) + meta:set_string("target3", 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 = END_DEPTH + math.random(20, 120) -- Search start + local nobj_cave_point = minetest.get_perlin(np_cave) + local air = 0 -- Consecutive air nodes found + + for y = start_y, start_y - 120, -1 do + local nval_cave = nobj_cave_point: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 ~= "mcl_nether:red_nether_brick" 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 < END_DEPTH then + target3.y = math.random(-52, 100) + 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) + meta:set_string("p1", minetest.pos_to_string(p1)) + meta:set_string("p2", minetest.pos_to_string(p2)) + meta:set_string("target3", 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("target3")) + 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 + objpos.y = objpos.y + 0.1 -- Fix some glitches at -8000. FIXME: WTF? + if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then + return + end + + obj:setpos(target3) + minetest.sound_play("tng_transporter1", {pos=target3,gain=0.5,max_hear_distance = 8,}) -- maikerumine added sound when travel + + 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) + minetest.after(4, check_and_build_end_portal, pos, target3) + elseif not n then + minetest.after(1, check_and_build_end_portal, pos, target3) + end + end + + minetest.after(1, check_and_build_end_portal, pos, target3) + + end, obj, pos, target3) + end + end + end + end, +}) + + +--[[ ITEM OVERRIDES ]] + +-- Frame material +minetest.override_item("mcl_nether:red_nether_brick", { + on_destruct = function(pos) + local meta = minetest.get_meta(pos) + local p1 = minetest.string_to_pos(meta:get_string("p1")) + local p2 = minetest.string_to_pos(meta:get_string("p2")) + local target3 = minetest.string_to_pos(meta:get_string("target3")) + if not p1 or not p2 then + return + end + + for x = p1.x, p2.x do + for y = p1.y, p2.y do + for z = p1.z, p2.z do + local nn = minetest.get_node({x = x, y = y, z = z}).name + if nn == "mcl_nether:red_nether_brick" or nn == "mcl_portals:portal_end" then + if nn == "mcl_portals:portal_end" then + minetest.remove_node({x = x, y = y, z = z}) + end + local m = minetest.get_meta({x = x, y = y, z = z}) + m:set_string("p1", "") + m:set_string("p2", "") + m:set_string("target3", "") + end + end + end + end + + meta = minetest.get_meta(target3) + if not meta then + return + end + p1 = minetest.string_to_pos(meta:get_string("p1")) + p2 = minetest.string_to_pos(meta:get_string("p2")) + if not p1 or not p2 then + return + end + + for x = p1.x, p2.x do + for y = p1.y, p2.y do + for z = p1.z, p2.z do + local nn = minetest.get_node({x = x, y = y, z = z}).name + if nn == "mcl_nether:red_nether_brick" or nn == "mcl_portals:portal_end" then + if nn == "mcl_portals:portal_end" then + minetest.remove_node({x = x, y = y, z = z}) + end + local m = minetest.get_meta({x = x, y = y, z = z}) + m:set_string("p1", "") + m:set_string("p2", "") + m:set_string("target3", "") + end + end + end + end + end, +}) + +-- 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 red nether brick blocks with a length of 4 and a height of 5 blocks, leaving only air in the center. After placing this frame, use the nether quartz on the frame.", + on_place = function(itemstack, user, pointed_thing) + local nodedef = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name] --new + + minetest.sound_play( + "fire_flint_and_steel", + {pos = pointed_thing.above, gain = 0.5, max_hear_distance = 8} + ) + if pointed_thing.under and minetest.get_node(pointed_thing.under).name == "mcl_nether:red_nether_brick" then + make_end_portal(pointed_thing.under) + end + + if not minetest.setting_getbool("creative_mode") and used == true then + itemstack:take_item() -- 1 use + end + return itemstack + end, +}) + diff --git a/mods/MAPGEN/mcl_portals/portal_nether.lua b/mods/MAPGEN/mcl_portals/portal_nether.lua new file mode 100644 index 000000000..94ed0986c --- /dev/null +++ b/mods/MAPGEN/mcl_portals/portal_nether.lua @@ -0,0 +1,416 @@ +-- Parameters + +local NETHER_DEPTH = mcl_vars.mg_nether_min +local TCAVE = 0.6 +local nobj_cave = nil + +-- 3D noise +local np_cave = { + offset = 0, + scale = 1, + spread = {x = 384, y = 128, z = 384}, + seed = 59033, + octaves = 5, + persist = 0.7 +} + +minetest.register_node("mcl_portals:portal", { + description = "Nether Portal", + tiles = { + "blank.png", + "blank.png", + "blank.png", + "blank.png", + { + name = "mcl_portals_portal.png", + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 0.5, + }, + }, + { + name = "mcl_portals_portal.png", + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 0.5, + }, + }, + }, + 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 = "", + light_source = 11, + post_effect_color = {a = 180, r = 128, g = 23, b = 23}, + 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} +}) + + + +-- Functions +--Build arrival portal +local function build_portal(pos, target) + 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 = "mcl_core:obsidian"}) + p.y = p.y + 1 + end + for i = 1, 3 do + minetest.set_node(p, {name = "mcl_core:obsidian"}) + p.x = p.x + 1 + end + for i = 1, 4 do + minetest.set_node(p, {name = "mcl_core:obsidian"}) + p.y = p.y - 1 + end + for i = 1, 3 do + minetest.set_node(p, {name = "mcl_core:obsidian"}) + 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", param2 = 0}) + end + local meta = minetest.get_meta(p) + meta:set_string("p1", minetest.pos_to_string(p1)) + meta:set_string("p2", minetest.pos_to_string(p2)) + meta:set_string("target", minetest.pos_to_string(target)) + + 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_nether_target_y(target_x, target_z) + local start_y = NETHER_DEPTH + math.random(38, 117) -- Search start + local nobj_cave_point = minetest.get_perlin(np_cave) + local air = 4 + + for y = start_y, start_y -117, -1 do + local nval_cave = nobj_cave_point:get3d({x = target_x, y = y, z = target_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_check(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 ~= "mcl_core:obsidian" then + return false + end + end + + return true +end + +local function check_portal(p1, p2) + if p1.x ~= p2.x then + if not move_check(p1, p2.x, "x") then + return false + end + if not move_check(p2, p1.x, "x") then + return false + end + elseif p1.z ~= p2.z then + if not move_check(p1, p2.z, "z") then + return false + end + if not move_check(p2, p1.z, "z") then + return false + end + else + return false + end + + if not move_check(p1, p2.y, "y") then + return false + end + if not move_check(p2, p1.y, "y") then + return false + end + + return true +end + +local function is_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_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_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_portal(pos) + local p1, p2 = is_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 target = {x = p1.x, y = p1.y, z = p1.z} + target.x = target.x + 1 + if target.y < NETHER_DEPTH then + target.y = math.random(-52, 100) + else + target.y = find_nether_target_y(target.x, target.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", param2 = param2}) + end + local meta = minetest.get_meta(p) + meta:set_string("p1", minetest.pos_to_string(p1)) + meta:set_string("p2", minetest.pos_to_string(p2)) + meta:set_string("target", minetest.pos_to_string(target)) + end + end + + return true +end + + +minetest.register_abm({ + label = "Nether portal teleportation and particles", + nodenames = {"mcl_portals:portal"}, + interval = 1, + chance = 2, + action = function(pos, node) + minetest.add_particlespawner( + 32, --amount + 4, --time + {x = pos.x - 0.25, y = pos.y - 0.25, z = pos.z - 0.25}, --minpos + {x = pos.x + 0.25, y = pos.y + 0.25, z = pos.z + 0.25}, --maxpos + {x = -0.8, y = -0.8, z = -0.8}, --minvel + {x = 0.8, y = 0.8, z = 0.8}, --maxvel + {x = 0, y = 0, z = 0}, --minacc + {x = 0, y = 0, z = 0}, --maxacc + 0.5, --minexptime + 1, --maxexptime + 1, --minsize + 2, --maxsize + false, --collisiondetection + "mcl_portals_particle.png" --texture + ) + 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 target = minetest.string_to_pos(meta:get_string("target")) + if target then + -- force emerge of target area + minetest.get_voxel_manip():read_from_map(target, target) + if not minetest.get_node_or_nil(target) then + minetest.emerge_area( + vector.subtract(target, 4), vector.add(target, 4)) + end + -- teleport the player + minetest.after(3, function(obj, pos, target) + local objpos = obj:getpos() if objpos == nil then return end --maikerumine added for objects to travel + objpos.y = objpos.y + 0.1 -- Fix some glitches at -8000 + if minetest.get_node(objpos).name ~= "mcl_portals:portal" then + return + end + + obj:setpos(target) + minetest.sound_play("tng_transporter1", {pos=target,gain=0.5,max_hear_distance = 8,}) --maikerumine added sound when travel + local function check_and_build_portal(pos, target) + local n = minetest.get_node_or_nil(target) + if n and n.name ~= "mcl_portals:portal" then + build_portal(target, pos) + minetest.after(2, check_and_build_portal, pos, target) + minetest.after(4, check_and_build_portal, pos, target) + elseif not n then + minetest.after(1, check_and_build_portal, pos, target) + end + end + + minetest.after(1, check_and_build_portal, pos, target) + + end, obj, pos, target) + end + end + end + end, +}) + + +--[[ ITEM OVERRIDES ]] + +-- Frame material +minetest.override_item("mcl_core:obsidian", { + on_destruct = function(pos) + local meta = minetest.get_meta(pos) + local p1 = minetest.string_to_pos(meta:get_string("p1")) + local p2 = minetest.string_to_pos(meta:get_string("p2")) + local target = minetest.string_to_pos(meta:get_string("target")) + if not p1 or not p2 then + return + end + + for x = p1.x, p2.x do + for y = p1.y, p2.y do + for z = p1.z, p2.z do + local nn = minetest.get_node({x = x, y = y, z = z}).name + if nn == "mcl_core:obsidian" or nn == "mcl_portals:portal" then + if nn == "mcl_portals:portal" then + minetest.remove_node({x = x, y = y, z = z}) + end + local m = minetest.get_meta({x = x, y = y, z = z}) + m:set_string("p1", "") + m:set_string("p2", "") + m:set_string("target", "") + end + end + end + end + + meta = minetest.get_meta(target) + if not meta then + return + end + p1 = minetest.string_to_pos(meta:get_string("p1")) + p2 = minetest.string_to_pos(meta:get_string("p2")) + if not p1 or not p2 then + return + end + + for x = p1.x, p2.x do + for y = p1.y, p2.y do + for z = p1.z, p2.z do + local nn = minetest.get_node({x = x, y = y, z = z}).name + if nn == "mcl_core:obsidian" or nn == "mcl_portals:portal" then + if nn == "mcl_portals:portal" then + minetest.remove_node({x = x, y = y, z = z}) + end + local m = minetest.get_meta({x = x, y = y, z = z}) + m:set_string("p1", "") + m:set_string("p2", "") + m:set_string("target", "") + end + end + end + end + end, +}) + +-- Portal opener +minetest.override_item("mcl_fire:flint_and_steel", { + _doc_items_longdesc = "Flint and steel is a tool to start fires, ignite blocks and open portals.", + _doc_items_usagehelp = "Rightclick the surface of a block to attempt to light a fire in front of it. On netherrack it will start an eternal fire. Using it on TNT will ignite it. To open a Nether portal, place an upright frame of obsidian with a length of 4 and a height of 5 blocks, leaving only air in the center. After placing this frame, use the flint and steel on inside of the frame.", + on_place = function(itemstack, user, pointed_thing) + local idef = itemstack:get_definition() + minetest.sound_play( + "fire_flint_and_steel", + {pos = pointed_thing.above, gain = 0.5, max_hear_distance = 8} + ) + local used = false + + if pointed_thing.under and minetest.get_node(pointed_thing.under).name == "mcl_core:obsidian" then + done = make_portal(pointed_thing.under) + else + if pointed_thing.type == "node" then + local nodedef = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name] + if nodedef._on_ignite then + nodedef._on_ignite(pointed_thing.under, user) + else + mcl_fire.set_fire(pointed_thing) + end + used = true + end + end + if itemstack:get_count() == 0 and idef.sound and idef.sound.breaks then + minetest.sound_play(idef.sound.breaks, {pos=user:getpos(), gain=0.5}) + end + if not minetest.setting_getbool("creative_mode") and used == true then + itemstack:add_wear(65535/65) -- 65 uses + end + return itemstack + end, +}) + diff --git a/mods/MAPGEN/mcl_portals/textures/mcl_portals_end_portal.png b/mods/MAPGEN/mcl_portals/textures/mcl_portals_end_portal.png new file mode 100644 index 0000000000000000000000000000000000000000..975be7f7de9b688a3c2701e0f41c36883568a00a GIT binary patch literal 8925 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 literal 0 HcmV?d00001 diff --git a/mods/MAPGEN/mcl_portals/textures/mcl_portals_particle.png b/mods/MAPGEN/mcl_portals/textures/mcl_portals_particle.png new file mode 100644 index 0000000000000000000000000000000000000000..56a5b78c42392a3cac53daffe6ace26f490de837 GIT binary patch literal 226 zcmeAS@N?(olHy`uVBq!ia0y~yVBi2@4mJh`h9ms@x)~T4*pj^6T^Rm@;DWu&Co?cG za29w(7Bet#3xhBt!>l~~ps`Gk#*Sv(5?8B^jKQR1ARo12Y;fRVd0xXQcgyYH^;xVr>|xLT-?FecxO}|_i{qW}Lpf#4 z@?M34w+_eTH8=ZgJ{QEuyedG_so`+C)1EeyvS-38^14KyDO}D;on28K@Zv$g-bqcF W|8>ijBr-5CFnGH9xvXt(|8-y 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 a^Zg$;^U2=YvD>-;0000