2017-08-17 00:16:29 +02:00
-- 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
}
2017-08-17 01:19:47 +02:00
-- Portal frame material
local portal_frame = " mcl_nether:quartz_block "
2017-08-17 03:27:31 +02:00
-- 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 )
2017-08-17 03:43:26 +02:00
local p1 = minetest.string_to_pos ( meta : get_string ( " portal_frame1 " ) )
local p2 = minetest.string_to_pos ( meta : get_string ( " portal_frame2 " ) )
2017-08-17 03:27:31 +02:00
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 . ] ]
2017-08-17 03:43:26 +02:00
mp1 = minetest.string_to_pos ( m : get_string ( " portal_frame1 " ) )
2017-08-17 03:27:31 +02:00
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
2017-08-17 03:43:26 +02:00
m : set_string ( " portal_frame1 " , " " )
m : set_string ( " portal_frame2 " , " " )
m : set_string ( " portal_target " , " " )
2017-08-17 03:27:31 +02:00
end
first = false
end
end
end
end
2017-08-17 00:16:29 +02:00
-- 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 } ,
} ,
} ,
2017-08-17 03:27:31 +02:00
groups = { not_in_creative_inventory = 1 } ,
on_destruct = destroy_portal ,
_mcl_hardness = - 1 ,
_mcl_blast_resistance = 18000000 ,
2017-08-17 00:16:29 +02:00
} )
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
2017-08-17 01:19:47 +02:00
minetest.set_node ( p , { name = portal_frame } )
2017-08-17 00:16:29 +02:00
p.y = p.y + 1
end
for i = 1 , 3 do
2017-08-17 01:19:47 +02:00
minetest.set_node ( p , { name = portal_frame } )
2017-08-17 00:16:29 +02:00
p.x = p.x + 1
end
for i = 1 , 4 do
2017-08-17 01:19:47 +02:00
minetest.set_node ( p , { name = portal_frame } )
2017-08-17 00:16:29 +02:00
p.y = p.y - 1
end
for i = 1 , 3 do
2017-08-17 01:19:47 +02:00
minetest.set_node ( p , { name = portal_frame } )
2017-08-17 00:16:29 +02:00
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 )
2017-08-17 03:43:26 +02:00
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 ) )
2017-08-17 00:16:29 +02:00
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 )
2017-08-17 01:58:17 +02:00
local start_y = mcl_vars.mg_end_min + math.random ( 20 , 120 ) -- Search start
2017-08-17 01:09:32 +02:00
if not nobj_cave then
nobj_cave = minetest.get_perlin ( np_cave )
end
2017-08-17 00:16:29 +02:00
local air = 0 -- Consecutive air nodes found
for y = start_y , start_y - 120 , - 1 do
2017-08-17 01:09:32 +02:00
local nval_cave = nobj_cave : get3d ( { x = target3_x , y = y , z = target3_z } )
2017-08-17 00:16:29 +02:00
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
2017-08-17 01:19:47 +02:00
if minetest.get_node ( p ) . name ~= portal_frame then
2017-08-17 00:16:29 +02:00
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
2017-08-17 01:58:17 +02:00
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 )
2017-08-17 00:16:29 +02:00
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 )
2017-08-17 03:43:26 +02:00
-- 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 ) )
2017-08-17 00:16:29 +02:00
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 )
2017-08-17 03:43:26 +02:00
local target3 = minetest.string_to_pos ( meta : get_string ( " portal_target " ) )
2017-08-17 00:16:29 +02:00
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
2017-08-17 01:33:36 +02:00
2017-08-17 00:16:29 +02:00
-- teleport the player
minetest.after ( 3 , function ( obj , pos , target3 )
local objpos = obj : getpos ( )
2017-08-17 04:21:59 +02:00
-- 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 )
2017-08-17 00:16:29 +02:00
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
2017-08-17 01:01:09 +02:00
-- Build destination
2017-08-17 00:16:29 +02:00
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
2017-08-17 01:33:36 +02:00
check_and_build_end_portal ( pos , target3 )
2017-08-17 00:16:29 +02:00
2017-08-17 01:01:09 +02:00
-- Teleport
obj : setpos ( target3 )
minetest.sound_play ( " mcl_portals_teleport " , { pos = target3 , gain = 0.5 , max_hear_distance = 16 } )
2017-08-17 00:16:29 +02:00
end , obj , pos , target3 )
end
end
end
end ,
} )
--[[ ITEM OVERRIDES ]]
-- Frame material
2017-08-17 01:19:47 +02:00
minetest.override_item ( portal_frame , {
2017-08-17 03:27:31 +02:00
on_destruct = destroy_portal ,
2017-08-17 00:16:29 +02:00
} )
-- 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. " ,
2017-08-17 01:19:47 +02:00
_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. " ,
2017-08-17 00:16:29 +02:00
on_place = function ( itemstack , user , pointed_thing )
2017-08-17 04:12:34 +02:00
-- 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
2017-08-17 03:27:31 +02:00
end
2017-08-17 04:12:34 +02:00
end
-- If used on portal frame, open a portal
if pointed_thing.under and node.name == portal_frame then
local opened = make_end_portal ( pointed_thing.under )
if opened then
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 = 16 } )
if not minetest.settings : get_bool ( " creative_mode " ) then
itemstack : take_item ( ) -- 1 use
end
2017-08-17 01:11:06 +02:00
end
2017-08-17 00:16:29 +02:00
end
return itemstack
end ,
} )