From 7c15d5f9ac8e5b7edf7e435bf7925de2742eb3ac Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 21 Nov 2017 05:39:27 +0100 Subject: [PATCH] More MC-like end portal block (WIP) --- mods/CORE/mcl_init/init.lua | 2 + mods/ITEMS/mcl_portals/portal_end.lua | 263 +++++++++----------------- mods/MAPGEN/mcl_mapgen_core/init.lua | 21 ++ 3 files changed, 115 insertions(+), 171 deletions(-) diff --git a/mods/CORE/mcl_init/init.lua b/mods/CORE/mcl_init/init.lua index c68df274f0..d239c8d9ea 100644 --- a/mods/CORE/mcl_init/init.lua +++ b/mods/CORE/mcl_init/init.lua @@ -72,6 +72,8 @@ end mcl_vars.mg_end_min = -27073 -- Carefully chosen to be at a mapchunk border mcl_vars.mg_end_max_official = mcl_vars.mg_end_min + minecraft_height_limit mcl_vars.mg_end_max = mcl_vars.mg_overworld_min - 2000 +mcl_vars.mg_end_platform_pos = { x = 100, y = mcl_vars.mg_end_min + 80, z = 0 } + -- Realm barrier used to safely separate the End from the void below the Overworld mcl_vars.mg_realm_barrier_overworld_end_max = mcl_vars.mg_end_max mcl_vars.mg_realm_barrier_overworld_end_min = mcl_vars.mg_end_max - 11 diff --git a/mods/ITEMS/mcl_portals/portal_end.lua b/mods/ITEMS/mcl_portals/portal_end.lua index 3f649431c9..55f934a8f4 100644 --- a/mods/ITEMS/mcl_portals/portal_end.lua +++ b/mods/ITEMS/mcl_portals/portal_end.lua @@ -18,13 +18,6 @@ local np_cave = { octaves = 5, persist = 0.7 } --- Portal frame material -local fake_portal_frame = "mcl_nether:quartz_block" - --- Table of objects (including players) which recently teleported by a --- End portal. Those objects have a brief cooloff period before they --- can teleport again. This prevents annoying back-and-forth teleportation. -local portal_cooloff = {} -- Destroy portal if pos (portal frame or portal node) got destroyed local destroy_portal = function(pos) @@ -56,7 +49,7 @@ local destroy_portal = function(pos) end end local nn = minetest.get_node(p).name - if nn == fake_portal_frame or nn == "mcl_portals:portal_end" then + if 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) @@ -72,26 +65,13 @@ local destroy_portal = function(pos) end end --- Fake end portal +-- End portal -- TODO: Create real end portal minetest.register_node("mcl_portals:portal_end", { description = "End Portal", _doc_items_longdesc = "An End portal teleports creatures and objects to the mysterious End dimension (and back!).", _doc_items_usagehelp = "Stand in the portal for a moment to activate the teleportation. Entering such a portal for the first time will create a new portal in your destination. End portal which were built in the End will lead back to the Overworld. An End portal is destroyed if any of its surrounding frame blocks is destroyed.", 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 = 1.0, - }, - }, { name = "mcl_portals_end_portal.png", animation = { @@ -101,10 +81,14 @@ minetest.register_node("mcl_portals:portal_end", { length = 1.0, }, }, + "blank.png", + "blank.png", + "blank.png", + "blank.png", + "blank.png", }, drawtype = "nodebox", paramtype = "light", - paramtype2 = "facedir", sunlight_propagates = true, use_texture_alpha = true, walkable = false, @@ -120,7 +104,7 @@ minetest.register_node("mcl_portals:portal_end", { node_box = { type = "fixed", fixed = { - {-0.5, -0.5, -0.1, 0.5, 0.5, 0.1}, + {-0.5, -0.5, -0.5, 0.5, 4/16, 0.5}, }, }, groups = {not_in_creative_inventory = 1}, @@ -130,84 +114,25 @@ minetest.register_node("mcl_portals:portal_end", { _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 = fake_portal_frame}) - p.y = p.y + 1 - end - for i = 1, 3 do - minetest.set_node(p, {name = fake_portal_frame}) - p.x = p.x + 1 - end - for i = 1, 4 do - minetest.set_node(p, {name = fake_portal_frame}) - p.y = p.y - 1 - end - for i = 1, 3 do - minetest.set_node(p, {name = fake_portal_frame}) - p.x = p.x - 1 - end +local function build_end_portal_destination(pos) + local p1 = {x = pos.x - 2, y = pos.y, z = pos.z-2} + local p2 = {x = pos.x + 2, y = pos.y+2, z = pos.z+2} 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)) - - for z = -2, 2 do - if z ~= 0 then - local newp = {x=p.x, y=p.y, z=p.z+z} - if y ~= p1.y then - if minetest.registered_nodes[ - minetest.get_node(newp).name].is_ground_content then - minetest.remove_node(newp) - end - else - -- Build obsidian platform if floating - local newp_below = table.copy(newp) - newp_below.y = newp.y - 1 - if minetest.get_node(newp).name == "air" and minetest.get_node(newp_below).name == "air" then - minetest.set_node(newp, {name="mcl_core:obsidian"}) - end - - end + for z = p1.z, p2.z do + local newp = {x=x,y=y,z=z} + -- Build obsidian platform + if minetest.registered_nodes[minetest.get_node(newp).name].is_ground_content then + if y == p1.y then + minetest.set_node(newp, {name="mcl_core:obsidian"}) + else + minetest.remove_node(newp) end end end end -end - -local function find_end_target3_y2(target3_x, target3_z) - local start_y = math.random(SPAWN_MIN, SPAWN_MAX) -- 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, SPAWN_MIN, -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) @@ -304,18 +229,6 @@ local function make_end_portal(pos) 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 - if mg_name == "flat" then - target3.y = mcl_vars.mg_bedrock_overworld_max + 5 - else - target3.y = math.random(mcl_vars.mg_overworld_min + 40, mcl_vars.mg_overworld_min + 96) - end - 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 = {} @@ -345,67 +258,86 @@ minetest.register_abm({ label = "End portal teleportation", nodenames = {"mcl_portals:portal_end"}, interval = 1, - chance = 2, + chance = 1, 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 + -- Destroy legacy end portals created with quartz block frame + -- by turning them into cobwebs. + -- We can tell if a end portal is legacy if it has portal_target as metadata. + -- FIXME: Remove this after some time. + local meta = minetest.get_meta(pos) + local legacy_portal_target = minetest.string_to_pos(meta:get_string("portal_target")) + if legacy_portal_target and legacy_portal_target ~= "" then + minetest.set_node(pos, {name="mcl_core:cobweb"}) + return + end + + for _,obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do + local lua_entity = obj:get_luaentity() --maikerumine added for objects to travel if obj:is_player() or lua_entity then - -- No rapid back-and-forth teleportatio - if portal_cooloff[obj] then - return - end - 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)) + local _, dim = mcl_util.y_to_layer(pos.y) + + local target + if dim == "end" then + -- End portal in the End: + -- Teleport back to the player's spawn in the Overworld. + -- TODO: Implement better spawn point detection + + target = minetest.string_to_pos(obj:get_attribute("mcl_beds:spawn")) + if not target then + target = minetest.setting_get_pos("static_spawnpoint") + end + if not target then + target = { x=0, y=0, z=0 } + end + else + -- End portal in any other dimension: + -- Teleport to the End at a fixed position and generate a + -- 5×5 obsidian platform below. + + local platform_pos = mcl_vars.mg_end_platform_pos + + -- force emerge of target1 area + minetest.get_voxel_manip():read_from_map(platform_pos, platform_pos) + if not minetest.get_node_or_nil(platform_pos) then + minetest.emerge_area(vector.subtract(platform_pos, 3), vector.add(platform_pos, 3)) end - -- teleport the object - minetest.after(3, function(obj, pos, target3) - -- No rapid back-and-forth teleportatio - if portal_cooloff[obj] then - return - end - local objpos = obj:getpos() - if objpos == nil then - return - end - -- If player stands, player is at ca. something+0.5 - -- which might cause precision problems, so we used ceil. - objpos.y = math.ceil(objpos.y) - if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then - return - end + local objpos = obj:getpos() + if objpos == nil then + return + end + -- If player stands, player is at ca. something+0.5 + -- which might cause precision problems, so we used ceil. + objpos.y = math.ceil(objpos.y) + 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 + -- Build destination + local function check_and_build_end_portal_destination(pos) + local n = minetest.get_node_or_nil(pos) + if n and n.name ~= "mcl_core:obsidian" then + build_end_portal_destination(pos) + minetest.after(2, check_and_build_end_portal_destination, pos) + elseif not n then + minetest.after(1, check_and_build_end_portal_destination, pos) + end + end - check_and_build_end_portal(pos, target3) + local platform + check_and_build_end_portal_destination(platform_pos) - -- Teleport - obj:setpos(target3) - minetest.sound_play("mcl_portals_teleport", {pos=target3, gain=0.5, max_hear_distance = 16}) - - -- Enable teleportation cooloff to prevent frequent back-and-forth teleportation - portal_cooloff[obj] = true - minetest.after(3, function(o) - portal_cooloff[o] = false - end, obj) - - end, obj, pos, target3) + target = table.copy(platform_pos) + target.y = target.y + 1 end + + -- Teleport + obj:set_pos(target) + -- Look towards the End island + if obj:is_player() and dim ~= "end" then + obj:set_look_horizontal(math.pi/2) + end + minetest.sound_play("mcl_portals_teleport", {pos=target, gain=0.5, max_hear_distance = 16}) end end end, @@ -414,20 +346,9 @@ minetest.register_abm({ --[[ ITEM OVERRIDES ]] -local portal_open_help = "To open an End portal, place an upright frame of quartz blocks with a length of 4 blocks and a height of 5 blocks, leaving only air in the center. After placing this frame, use an eye of ender on the frame. The eye of ender is destroyed in the process." - --- Fake frame material -minetest.override_item(fake_portal_frame, { - _doc_items_longdesc = "A block of quartz can be used to create End portals.", - _doc_items_usagehelp = portal_open_help, - on_destruct = destroy_portal, -}) - - -- End Portal Frame (TODO) minetest.register_node("mcl_portals:end_portal_frame", { description = "End Portal Frame", - _doc_items_longdesc = "This block is currently only used for decoration. You can place an eye of ender into it for fun, but nothing will happen.", groups = { creative_breakable = 1, deco_block = 1 }, tiles = { "mcl_portals_endframe_top.png", "mcl_portals_endframe_bottom.png", "mcl_portals_endframe_side.png" }, paramtype2 = "facedir", @@ -477,7 +398,7 @@ end -- Portal opener minetest.override_item("mcl_end:ender_eye", { _doc_items_longdesc = "An eye of ender can be used to open End portals.", - _doc_items_usagehelp = portal_open_help, + -- TODO: _doc_items_usagehelp = , on_place = function(itemstack, user, pointed_thing) -- Use pointed node's on_rightclick function first, if present local node = minetest.get_node(pointed_thing.under) diff --git a/mods/MAPGEN/mcl_mapgen_core/init.lua b/mods/MAPGEN/mcl_mapgen_core/init.lua index 0cea055858..48b53b1c88 100644 --- a/mods/MAPGEN/mcl_mapgen_core/init.lua +++ b/mods/MAPGEN/mcl_mapgen_core/init.lua @@ -45,6 +45,7 @@ local WITCH_HUT_HEIGHT = 3 -- Exact Y level to spawn witch huts at. This height -- Content IDs local c_bedrock = minetest.get_content_id("mcl_core:bedrock") +local c_obsidian = minetest.get_content_id("mcl_core:obsidian") local c_stone = minetest.get_content_id("mcl_core:stone") local c_dirt = minetest.get_content_id("mcl_core:dirt") local c_dirt_with_grass = minetest.get_content_id("mcl_core:dirt_with_grass") @@ -1829,6 +1830,7 @@ minetest.register_on_generated(function(minp, maxp, seed) -- End block fixes: -- * Replace water with end stone or air (depending on height). -- * Remove stone, sand, dirt in v6 so our End map generator works in v6. + -- * Generate spawn platform (End portal destination) elseif minp.y <= mcl_vars.mg_end_max and maxp.y >= mcl_vars.mg_end_min then local nodes if mg_name == "v6" then @@ -1854,6 +1856,25 @@ minetest.register_on_generated(function(minp, maxp, seed) end end + + -- Obsidian spawn platform + if minp.y <= mcl_vars.mg_end_platform_pos.y and maxp.y >= mcl_vars.mg_end_platform_pos.y and + minp.x <= mcl_vars.mg_end_platform_pos.x and maxp.x >= mcl_vars.mg_end_platform_pos.z and + minp.z <= mcl_vars.mg_end_platform_pos.z and maxp.z >= mcl_vars.mg_end_platform_pos.z then + for x=math.max(minp.x, mcl_vars.mg_end_platform_pos.x-2), math.min(maxp.x, mcl_vars.mg_end_platform_pos.x+2) do + for z=math.max(minp.z, mcl_vars.mg_end_platform_pos.z-2), math.min(maxp.z, mcl_vars.mg_end_platform_pos.z+2) do + for y=math.max(minp.y, mcl_vars.mg_end_platform_pos.y), math.min(maxp.y, mcl_vars.mg_end_platform_pos.y+2) do + local p_pos = area:index(x, y, z) + if y == mcl_vars.mg_end_platform_pos.y then + data[p_pos] = c_obsidian + else + data[p_pos] = c_air + end + end + end + end + lvm_used = true + end end end