368 lines
12 KiB
Lua
368 lines
12 KiB
Lua
-- Parameters
|
||
local SPAWN_MIN = mcl_vars.mg_end_min+70
|
||
local SPAWN_MAX = mcl_vars.mg_end_min+98
|
||
|
||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||
|
||
-- 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 = "Hop into the portal to teleport. Entering an End portal in the Overworld teleports you to a fixed position in the End dimension and creates a 5×5 obsidian platform at your destination. End portals in the End will lead back to your spawn point in the Overworld.",
|
||
tiles = {
|
||
{
|
||
name = "mcl_portals_end_portal.png",
|
||
animation = {
|
||
type = "vertical_frames",
|
||
aspect_w = 16,
|
||
aspect_h = 16,
|
||
length = 1.0,
|
||
},
|
||
},
|
||
"blank.png",
|
||
"blank.png",
|
||
"blank.png",
|
||
"blank.png",
|
||
"blank.png",
|
||
},
|
||
drawtype = "nodebox",
|
||
paramtype = "light",
|
||
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.5, 0.5, 4/16, 0.5},
|
||
},
|
||
},
|
||
groups = {not_in_creative_inventory = 1},
|
||
|
||
_mcl_hardness = -1,
|
||
_mcl_blast_resistance = 18000000,
|
||
})
|
||
|
||
-- Obsidian platform at the End portal destination in the 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
|
||
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
|
||
end
|
||
|
||
|
||
-- Check if pos is part of a valid end portal frame, filled with eyes of ender.
|
||
local function check_end_portal_frame(pos)
|
||
-- Check if pos has an end portal frame with eye of ender
|
||
local eframe = function(pos, param2)
|
||
local node = minetest.get_node(pos)
|
||
if node.name == "mcl_portals:end_portal_frame_eye" then
|
||
if param2 == nil or node.param2 == param2 then
|
||
return true, node
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
-- Step 1: Find a row of 3 end portal frames with eyes, all facing the same direction
|
||
local streak = 0
|
||
local streak_start, streak_end, streak_start_node, streak_end_node
|
||
local last_param2
|
||
local axes = { "x", "z" }
|
||
for a=1, #axes do
|
||
local axis = axes[a]
|
||
for b=pos[axis]-2, pos[axis]+2 do
|
||
local cpos = table.copy(pos)
|
||
cpos[axis] = b
|
||
local e, node = eframe(cpos, last_param2)
|
||
if e then
|
||
last_param2 = node.param2
|
||
streak = streak + 1
|
||
if streak == 1 then
|
||
streak_start = table.copy(pos)
|
||
streak_start[axis] = b
|
||
streak_start_node = node
|
||
elseif streak == 3 then
|
||
streak_end = table.copy(pos)
|
||
streak_end[axis] = b
|
||
streak_end_node = node
|
||
break
|
||
end
|
||
else
|
||
streak = 0
|
||
last_param2 = nil
|
||
end
|
||
end
|
||
if streak_end then
|
||
break
|
||
end
|
||
streak = 0
|
||
last_param2 = nil
|
||
end
|
||
-- Has a row been found?
|
||
if streak_end then
|
||
-- Step 2: Using the known facedir, check the remaining spots in which we expect
|
||
-- “eyed” end portal frames.
|
||
local dir = minetest.facedir_to_dir(streak_start_node.param2)
|
||
if dir.x ~= 0 then
|
||
for i=1, 3 do
|
||
if not eframe({x=streak_start.x + i*dir.x, y=streak_start.y, z=streak_start.z - 1}) then
|
||
return false
|
||
end
|
||
if not eframe({x=streak_start.x + i*dir.x, y=streak_start.y, z=streak_end.z + 1}) then
|
||
return false
|
||
end
|
||
if not eframe({x=streak_start.x + 4*dir.x, y=streak_start.y, z=streak_start.z + i-1}) then
|
||
return false
|
||
end
|
||
end
|
||
-- All checks survived! We have a valid portal!
|
||
if dir.x > 0 then
|
||
k = 1
|
||
else
|
||
k = -3
|
||
end
|
||
return true, { x = streak_start.x + k, y = streak_start.y, z = streak_start.z }
|
||
elseif dir.z ~= 0 then
|
||
for i=1, 3 do
|
||
if not eframe({x=streak_start.x - 1, y=streak_start.y, z=streak_start.z + i*dir.z}) then
|
||
return false
|
||
end
|
||
if not eframe({x=streak_end.x + 1, y=streak_start.y, z=streak_start.z + i*dir.z}) then
|
||
return false
|
||
end
|
||
if not eframe({x=streak_start.x + i-1, y=streak_start.y, z=streak_start.z + 4*dir.z}) then
|
||
return false
|
||
end
|
||
end
|
||
if dir.z > 0 then
|
||
k = 1
|
||
else
|
||
k = -3
|
||
end
|
||
-- All checks survived! We have a valid portal!
|
||
return true, { x = streak_start.x, y = streak_start.y, z = streak_start.z + k }
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
-- Generate a 3×3 end portal beginning at pos. To be used to fill an end portal frame
|
||
local function spawn_end_portal(pos)
|
||
local SIZE = 3
|
||
for x=pos.x, pos.x+SIZE-1 do
|
||
for z=pos.z, pos.z+SIZE-1 do
|
||
minetest.set_node({x=x,y=pos.y,z=z}, {name="mcl_portals:portal_end"})
|
||
end
|
||
end
|
||
end
|
||
|
||
minetest.register_abm({
|
||
label = "End portal teleportation",
|
||
nodenames = {"mcl_portals:portal_end"},
|
||
interval = 1,
|
||
chance = 1,
|
||
action = function(pos, node)
|
||
-- 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 = meta:get_string("portal_frame1")
|
||
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
|
||
local _, dim = mcl_util.y_to_layer(pos.y)
|
||
|
||
local objpos = obj:getpos()
|
||
if objpos == nil then
|
||
return
|
||
end
|
||
|
||
-- Check if object is actually in portal.
|
||
objpos.y = math.ceil(objpos.y)
|
||
if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then
|
||
return
|
||
end
|
||
|
||
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 }
|
||
if mg_name == "flat" then
|
||
target.y = mcl_vars.mg_bedrock_overworld_max + 5
|
||
end
|
||
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
|
||
|
||
-- 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
|
||
|
||
local platform
|
||
check_and_build_end_portal_destination(platform_pos)
|
||
|
||
target = table.copy(platform_pos)
|
||
target.y = target.y + 1
|
||
end
|
||
|
||
-- Teleport
|
||
obj:set_pos(target)
|
||
if obj:is_player() then
|
||
-- Look towards the End island
|
||
if 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
|
||
end,
|
||
})
|
||
|
||
|
||
--[[ ITEM OVERRIDES ]]
|
||
|
||
minetest.register_node("mcl_portals:end_portal_frame", {
|
||
description = "End Portal Frame",
|
||
_doc_items_longdesc = "End portal frames are used in the construction of End portals. Each block has a socket for an eye of ender.",
|
||
_doc_items_usagehelp = "To create an End portal, you need 12 end portal frames and 12 eyes of ender. The end portal frames have to be arranged around a horizontal 3×3 area with each block facing inward. Any other arrangement will fail." .. "\n" .. "Place an eye of ender into each block. The end portal appears in the middle after placing the final eye." .. "\n" .. "Once placed, an eye of ender can not be taken back.",
|
||
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",
|
||
drawtype = "nodebox",
|
||
node_box = {
|
||
type = "fixed",
|
||
fixed = { -0.5, -0.5, -0.5, 0.5, 5/16, 0.5 },
|
||
},
|
||
is_ground_content = false,
|
||
sounds = mcl_sounds.node_sound_stone_defaults(),
|
||
paramtype = "light",
|
||
sunlight_propagates = false,
|
||
light_source = 1,
|
||
_mcl_blast_resistance = 18000000,
|
||
_mcl_hardness = -1,
|
||
})
|
||
|
||
minetest.register_node("mcl_portals:end_portal_frame_eye", {
|
||
description = "End Portal Frame with Eye of Ender",
|
||
_doc_items_create_entry = false,
|
||
groups = { creative_breakable = 1, not_in_creative_inventory = 1 },
|
||
tiles = { "mcl_portals_endframe_top.png^[lowpart:75:mcl_portals_endframe_eye.png", "mcl_portals_endframe_bottom.png", "mcl_portals_endframe_eye.png^mcl_portals_endframe_side.png" },
|
||
paramtype2 = "facedir",
|
||
drawtype = "nodebox",
|
||
node_box = {
|
||
type = "fixed",
|
||
fixed = {
|
||
{ -0.5, -0.5, -0.5, 0.5, 5/16, 0.5 }, -- Frame
|
||
{ -4/16, 5/16, -4/16, 4/16, 0.5, 4/16 }, -- Eye
|
||
},
|
||
},
|
||
is_ground_content = false,
|
||
sounds = mcl_sounds.node_sound_stone_defaults(),
|
||
paramtype = "light",
|
||
sunlight_propagates = false,
|
||
light_source = 1,
|
||
_mcl_blast_resistance = 18000000,
|
||
_mcl_hardness = -1,
|
||
-- TODO: Destroy end portal if this block got destroyed
|
||
})
|
||
|
||
if minetest.get_modpath("doc") then
|
||
doc.add_entry_alias("nodes", "mcl_portals:end_portal_frame", "nodes", "mcl_portals:end_portal_frame_eye")
|
||
end
|
||
|
||
|
||
-- Portal opener
|
||
minetest.override_item("mcl_end:ender_eye", {
|
||
_doc_items_longdesc = "Eye of ender can be used in the construction of End portal frames.",
|
||
_doc_items_usagehelp = "Find a structure with 12 end portal frames surrounding a horizontal aread of 3×3 blocks, with each block facing inward. Place an eye of ender into each end portal frame to create the portal.",
|
||
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)
|
||
if user and not user:get_player_control().sneak then
|
||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
|
||
end
|
||
end
|
||
|
||
-- Place eye of ender into end portal frame
|
||
if pointed_thing.under and node.name == "mcl_portals:end_portal_frame" then
|
||
minetest.swap_node(pointed_thing.under, { name = "mcl_portals:end_portal_frame_eye", param2 = node.param2 })
|
||
|
||
if minetest.get_modpath("doc") then
|
||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:end_portal_frame")
|
||
end
|
||
minetest.sound_play(
|
||
"default_place_node_hard",
|
||
{pos = pointed_thing.under, gain = 0.5, max_hear_distance = 16})
|
||
if not minetest.settings:get_bool("creative_mode") then
|
||
itemstack:take_item() -- 1 use
|
||
end
|
||
|
||
local ok, ppos = check_end_portal_frame(pointed_thing.under)
|
||
if ok then
|
||
spawn_end_portal(ppos)
|
||
if minetest.get_modpath("doc") then
|
||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:portal_end")
|
||
end
|
||
end
|
||
end
|
||
return itemstack
|
||
end,
|
||
})
|
||
|