2019-03-08 01:22:28 +02:00
local S = minetest.get_translator ( " mcl_portals " )
2017-08-17 01:16:29 +03:00
-- Parameters
2017-08-18 03:42:26 +03:00
local SPAWN_MIN = mcl_vars.mg_end_min + 70
local SPAWN_MAX = mcl_vars.mg_end_min + 98
2017-08-21 19:30:37 +03:00
local mg_name = minetest.get_mapgen_setting ( " mg_name " )
2017-11-21 06:39:27 +02:00
-- End portal
2017-08-17 01:16:29 +03:00
minetest.register_node ( " mcl_portals:portal_end " , {
2019-03-08 01:22:28 +02:00
description = S ( " End Portal " ) ,
_doc_items_longdesc = S ( " An End portal teleports creatures and objects to the mysterious End dimension (and back!). " ) ,
_doc_items_usagehelp = S ( " 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. " ) ,
2017-08-17 01:16:29 +03:00
tiles = {
{
name = " mcl_portals_end_portal.png " ,
animation = {
type = " vertical_frames " ,
aspect_w = 16 ,
aspect_h = 16 ,
2017-09-02 15:45:05 +03:00
length = 1.0 ,
2017-08-17 01:16:29 +03:00
} ,
} ,
2018-05-16 20:22:55 +03:00
{
name = " mcl_portals_end_portal.png " ,
animation = {
type = " vertical_frames " ,
aspect_w = 16 ,
aspect_h = 16 ,
length = 6.0 ,
} ,
} ,
2017-11-21 06:39:27 +02:00
" blank.png " ,
2017-08-17 01:16:29 +03:00
} ,
drawtype = " nodebox " ,
paramtype = " light " ,
sunlight_propagates = true ,
use_texture_alpha = true ,
2018-05-16 20:22:55 +03:00
walkable = true ,
2017-08-17 01:16:29 +03:00
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 ,
2018-05-16 20:22:55 +03:00
-- This prevents “falling through”
collision_box = {
type = " fixed " ,
fixed = {
{ - 0.5 , - 0.5 , - 0.5 , 0.5 , - 7 / 16 , 0.5 } ,
} ,
} ,
2017-08-17 01:16:29 +03:00
node_box = {
type = " fixed " ,
fixed = {
2017-11-21 06:39:27 +02:00
{ - 0.5 , - 0.5 , - 0.5 , 0.5 , 4 / 16 , 0.5 } ,
2017-08-17 01:16:29 +03:00
} ,
} ,
2018-05-16 20:22:55 +03:00
groups = { not_in_creative_inventory = 1 , disable_jump = 1 } ,
2017-08-17 04:27:31 +03:00
_mcl_hardness = - 1 ,
_mcl_blast_resistance = 18000000 ,
2017-08-17 01:16:29 +03:00
} )
2017-11-21 10:54:45 +02:00
-- Obsidian platform at the End portal destination in the End
2017-11-21 06:39:27 +02:00
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 }
2017-08-17 01:16:29 +03:00
for x = p1.x , p2.x do
for y = p1.y , p2.y do
2017-11-21 06:39:27 +02:00
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 )
2017-08-17 01:16:29 +03:00
end
end
end
end
2017-08-17 02:09:32 +03:00
end
2017-08-17 01:16:29 +03:00
end
2017-11-21 10:54:45 +02:00
-- 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
2017-08-17 05:36:08 +03:00
end
2017-11-21 10:54:45 +02:00
return false
2017-08-17 01:16:29 +03:00
end
2017-11-21 10:54:45 +02:00
-- 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
2017-08-17 01:16:29 +03:00
end
2017-11-21 10:54:45 +02:00
if streak_end then
break
2017-08-17 01:16:29 +03:00
end
2017-11-21 10:54:45 +02:00
streak = 0
last_param2 = nil
2017-08-17 01:16:29 +03:00
end
2017-11-21 10:54:45 +02:00
-- 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
2017-08-17 01:16:29 +03:00
end
2017-11-21 10:54:45 +02:00
-- All checks survived! We have a valid portal!
2018-01-26 23:20:15 +02:00
local k
2017-11-21 10:54:45 +02:00
if dir.x > 0 then
k = 1
else
k = - 3
2017-08-17 01:16:29 +03:00
end
2017-11-21 10:54:45 +02:00
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
2018-01-26 23:20:15 +02:00
local k
2017-11-21 10:54:45 +02:00
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 }
2017-08-17 01:16:29 +03:00
end
end
2017-11-21 10:54:45 +02:00
return false
2017-08-17 01:16:29 +03:00
end
2017-12-09 15:58:06 +02:00
-- Generate or destroy a 3× 3 end portal beginning at pos. To be used to fill an end portal framea.
-- If destroy == true, the 3× 3 area is removed instead.
local function end_portal_area ( pos , destroy )
2017-11-21 10:54:45 +02:00
local SIZE = 3
2017-12-09 15:58:06 +02:00
local name
if destroy then
name = " air "
else
name = " mcl_portals:portal_end "
end
2017-11-21 10:54:45 +02:00
for x = pos.x , pos.x + SIZE - 1 do
for z = pos.z , pos.z + SIZE - 1 do
2017-12-09 15:58:06 +02:00
minetest.set_node ( { x = x , y = pos.y , z = z } , { name = name } )
2017-08-17 01:16:29 +03:00
end
end
end
minetest.register_abm ( {
label = " End portal teleportation " ,
nodenames = { " mcl_portals:portal_end " } ,
interval = 1 ,
2017-11-21 06:39:27 +02:00
chance = 1 ,
2017-08-17 01:16:29 +03:00
action = function ( pos , node )
2017-11-21 06:39:27 +02:00
-- 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 )
2017-11-21 08:26:45 +02:00
local legacy_portal_target = meta : get_string ( " portal_frame1 " )
2017-11-21 06:39:27 +02:00
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
2017-08-17 01:16:29 +03:00
if obj : is_player ( ) or lua_entity then
2017-11-24 04:10:02 +02:00
local dim = mcl_worlds.pos_to_dimension ( pos )
2017-11-21 06:39:27 +02:00
2019-02-01 07:33:07 +02:00
local objpos = obj : get_pos ( )
2017-11-21 23:58:11 +02:00
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
2017-11-21 06:39:27 +02:00
local target
if dim == " end " then
-- End portal in the End:
2019-03-09 11:04:54 +02:00
-- Teleport back to the player's spawn or world spawn in the Overworld.
2017-11-24 08:04:03 +02:00
2019-03-09 11:04:54 +02:00
if obj : is_player ( ) then
target = mcl_spawn.get_spawn_pos ( obj )
else
target = mcl_spawn.get_world_spawn_pos ( obj )
end
2017-11-21 06:39:27 +02:00
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
2017-11-22 07:16:33 +02:00
minetest.after ( 1 , check_and_build_end_portal_destination , pos )
end
2017-11-21 06:39:27 +02:00
end
local platform
2017-11-22 07:16:33 +02:00
build_end_portal_destination ( platform_pos )
2017-11-21 06:39:27 +02:00
check_and_build_end_portal_destination ( platform_pos )
2017-08-17 02:33:36 +03:00
2017-11-21 06:39:27 +02:00
target = table.copy ( platform_pos )
target.y = target.y + 1
2017-08-17 01:16:29 +03:00
end
2017-11-21 06:39:27 +02:00
-- Teleport
obj : set_pos ( target )
2017-11-21 08:24:56 +02:00
if obj : is_player ( ) then
2017-11-24 04:10:02 +02:00
-- Look towards the main End island
2017-11-21 08:24:56 +02:00
if dim ~= " end " then
obj : set_look_horizontal ( math.pi / 2 )
end
2017-11-24 04:48:32 +02:00
mcl_worlds.dimension_change ( obj , mcl_worlds.pos_to_dimension ( target ) )
2017-11-21 07:03:07 +02:00
minetest.sound_play ( " mcl_portals_teleport " , { pos = target , gain = 0.5 , max_hear_distance = 16 } )
2017-11-21 06:39:27 +02:00
end
2017-08-17 01:16:29 +03:00
end
end
end ,
} )
2017-12-09 15:58:06 +02:00
local rotate_frame , rotate_frame_eye
2017-08-17 01:16:29 +03:00
2017-12-09 15:58:06 +02:00
if minetest.get_modpath ( " screwdriver " ) then
2017-12-09 16:01:41 +02:00
-- Intentionally not rotatable
rotate_frame = false
2017-12-09 15:58:06 +02:00
rotate_frame_eye = false
end
2017-08-17 01:16:29 +03:00
2017-09-02 16:49:41 +03:00
minetest.register_node ( " mcl_portals:end_portal_frame " , {
2019-03-08 01:22:28 +02:00
description = S ( " End Portal Frame " ) ,
2019-03-25 13:05:57 +02:00
_doc_items_longdesc = S ( " End portal frames are used in the construction of End portals. Each block has a socket for an eye of ender. " ) .. " \n " .. S ( " NOTE: The End dimension is currently incomplete and might change in future versions. " ) ,
_doc_items_usagehelp = S ( " 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 " .. S ( " Place an eye of ender into each block. The end portal appears in the middle after placing the final eye. " ) .. " \n " .. S ( " Once placed, an eye of ender can not be taken back. " ) ,
2017-09-02 16:49:41 +03:00
groups = { creative_breakable = 1 , deco_block = 1 } ,
2017-09-02 17:16:04 +03:00
tiles = { " mcl_portals_endframe_top.png " , " mcl_portals_endframe_bottom.png " , " mcl_portals_endframe_side.png " } ,
2017-09-02 16:49:41 +03:00
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 ,
2017-12-09 15:58:06 +02:00
on_rotate = rotate_frame ,
2017-09-02 16:49:41 +03:00
_mcl_blast_resistance = 18000000 ,
_mcl_hardness = - 1 ,
} )
minetest.register_node ( " mcl_portals:end_portal_frame_eye " , {
2019-03-08 01:22:28 +02:00
description = S ( " End Portal Frame with Eye of Ender " ) ,
2017-09-02 16:49:41 +03:00
_doc_items_create_entry = false ,
2018-05-13 06:20:41 +03:00
groups = { creative_breakable = 1 , deco_block = 1 , comparator_signal = 15 } ,
2017-09-02 17:16:04 +03:00
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 " } ,
2017-09-02 16:49:41 +03:00
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 ,
2017-12-09 15:58:06 +02:00
on_destruct = function ( pos )
local ok , ppos = check_end_portal_frame ( pos )
if ok then
end_portal_area ( ppos , true )
end
end ,
2018-05-13 06:20:41 +03:00
on_construct = function ( pos )
local ok , ppos = check_end_portal_frame ( pos )
if ok then
end_portal_area ( ppos )
end
end ,
2017-12-09 15:58:06 +02:00
on_rotate = rotate_frame_eye ,
2017-09-02 16:49:41 +03:00
_mcl_blast_resistance = 18000000 ,
_mcl_hardness = - 1 ,
} )
if minetest.get_modpath ( " doc " ) then
doc.add_entry_alias ( " nodes " , " mcl_portals:end_portal_frame " , " nodes " , " mcl_portals:end_portal_frame_eye " )
end
2017-12-09 15:58:06 +02:00
--[[ ITEM OVERRIDES ]]
2017-08-17 01:16:29 +03:00
-- Portal opener
minetest.override_item ( " mcl_end:ender_eye " , {
on_place = function ( itemstack , user , pointed_thing )
2017-08-17 05:12:34 +03: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 04:27:31 +03:00
end
2017-08-17 05:12:34 +03:00
end
2017-09-02 20:15:15 +03:00
-- Place eye of ender into end portal frame
2017-11-21 10:54:45 +02:00
if pointed_thing.under and node.name == " mcl_portals:end_portal_frame " then
2019-02-08 22:59:01 +02:00
local protname = user : get_player_name ( )
if minetest.is_protected ( pointed_thing.under , protname ) then
minetest.record_protection_violation ( pointed_thing.under , protname )
return itemstack
end
2018-05-13 06:20:41 +03:00
minetest.set_node ( pointed_thing.under , { name = " mcl_portals:end_portal_frame_eye " , param2 = node.param2 } )
2017-11-21 10:54:45 +02:00
2017-09-02 20:15:15 +03:00
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
2017-11-21 10:54:45 +02:00
2018-05-13 06:20:41 +03:00
local ok = check_end_portal_frame ( pointed_thing.under )
2017-11-21 10:54:45 +02:00
if ok then
if minetest.get_modpath ( " doc " ) then
doc.mark_entry_as_revealed ( user : get_player_name ( ) , " nodes " , " mcl_portals:portal_end " )
end
end
2017-09-02 20:15:15 +03:00
end
2017-08-17 01:16:29 +03:00
return itemstack
end ,
} )