2021-05-29 16:12:33 +02:00
local S = minetest.get_translator ( minetest.get_current_modname ( ) )
2019-03-08 00:22:28 +01:00
2021-07-22 00:46:43 +02:00
local has_doc = minetest.get_modpath ( " doc " )
2024-02-21 01:37:59 +01:00
mcl_portals.registered_on_beat_game = { }
function mcl_portals . register_on_beat_game ( func )
table.insert ( mcl_portals.registered_on_beat_game , func )
end
2017-08-17 00:16:29 +02:00
-- Parameters
2021-05-22 23:07:56 +02:00
--local SPAWN_MIN = mcl_vars.mg_end_min+70
--local SPAWN_MAX = mcl_vars.mg_end_min+98
2017-08-18 02:42:26 +02:00
2021-05-22 23:07:56 +02:00
--local mg_name = minetest.get_mapgen_setting("mg_name")
2017-08-21 18:30:37 +02:00
2021-05-29 16:12:33 +02:00
local function destroy_portal ( pos )
2020-01-06 16:49:22 +01:00
local neighbors = {
{ x = 1 , y = 0 , z = 0 } ,
{ x =- 1 , y = 0 , z = 0 } ,
{ x = 0 , y = 0 , z = 1 } ,
{ x = 0 , y = 0 , z =- 1 } ,
}
for n = 1 , # neighbors do
local npos = vector.add ( pos , neighbors [ n ] )
if minetest.get_node ( npos ) . name == " mcl_portals:portal_end " then
minetest.remove_node ( npos )
end
end
end
2021-01-16 18:51:30 +04:00
local ep_scheme = {
2021-01-21 18:35:04 +04:00
{ o = { x = 0 , y = 0 , z = 1 } , p = 1 } ,
{ o = { x = 0 , y = 0 , z = 2 } , p = 1 } ,
{ o = { x = 0 , y = 0 , z = 3 } , p = 1 } ,
{ o = { x = 1 , y = 0 , z = 4 } , p = 2 } ,
{ o = { x = 2 , y = 0 , z = 4 } , p = 2 } ,
{ o = { x = 3 , y = 0 , z = 4 } , p = 2 } ,
{ o = { x = 4 , y = 0 , z = 3 } , p = 3 } ,
{ o = { x = 4 , y = 0 , z = 2 } , p = 3 } ,
{ o = { x = 4 , y = 0 , z = 1 } , p = 3 } ,
{ o = { x = 3 , y = 0 , z = 0 } , p = 0 } ,
{ o = { x = 2 , y = 0 , z = 0 } , p = 0 } ,
{ o = { x = 1 , y = 0 , z = 0 } , p = 0 } ,
2021-01-16 18:51:30 +04:00
}
2017-11-21 05:39:27 +01:00
-- End portal
2017-08-17 00:16:29 +02:00
minetest.register_node ( " mcl_portals:portal_end " , {
2019-03-08 00:22:28 +01:00
description = S ( " End Portal " ) ,
2020-02-19 04:54:17 +01:00
_tt_help = S ( " Used to construct end portals " ) ,
2019-03-08 00:22:28 +01:00
_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 00:16:29 +02:00
tiles = {
{
name = " mcl_portals_end_portal.png " ,
animation = {
type = " vertical_frames " ,
aspect_w = 16 ,
aspect_h = 16 ,
2017-09-02 14:45:05 +02:00
length = 1.0 ,
2017-08-17 00:16:29 +02:00
} ,
} ,
2018-05-16 19:22:55 +02:00
{
name = " mcl_portals_end_portal.png " ,
animation = {
type = " vertical_frames " ,
aspect_w = 16 ,
aspect_h = 16 ,
length = 6.0 ,
} ,
} ,
2017-11-21 05:39:27 +01:00
" blank.png " ,
2017-08-17 00:16:29 +02:00
} ,
drawtype = " nodebox " ,
paramtype = " light " ,
sunlight_propagates = true ,
2021-02-18 10:39:19 +01:00
use_texture_alpha = minetest.features . use_texture_alpha_string_modes and " blend " or true ,
2021-01-16 18:51:30 +04:00
walkable = false ,
2017-08-17 00:16:29 +02:00
diggable = false ,
buildable_to = false ,
is_ground_content = false ,
drop = " " ,
2024-11-03 19:16:21 +02:00
light_source = minetest.LIGHT_MAX ,
2017-08-17 00:16:29 +02:00
post_effect_color = { a = 192 , r = 0 , g = 0 , b = 0 } ,
2020-01-06 16:49:22 +01:00
after_destruct = destroy_portal ,
2018-05-16 19:22:55 +02: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 00:16:29 +02:00
node_box = {
type = " fixed " ,
fixed = {
2017-11-21 05:39:27 +01:00
{ - 0.5 , - 0.5 , - 0.5 , 0.5 , 4 / 16 , 0.5 } ,
2017-08-17 00:16:29 +02:00
} ,
} ,
2024-10-26 16:19:10 +02:00
groups = { portal = 1 , not_in_creative_inventory = 1 , disable_jump = 1 , unmovable_by_piston = 1 } ,
2017-08-17 03:27:31 +02:00
_mcl_hardness = - 1 ,
2022-06-12 11:30:39 +02:00
_mcl_blast_resistance = 3600000 ,
2017-08-17 00:16:29 +02:00
} )
2017-11-21 09:54:45 +01:00
-- Check if pos is part of a valid end portal frame, filled with eyes of ender.
local function check_end_portal_frame ( pos )
2021-01-16 18:51:30 +04:00
for i = 1 , 12 do
local pos0 = vector.subtract ( pos , ep_scheme [ i ] . o )
local portal = true
for j = 1 , 12 do
local p = vector.add ( pos0 , ep_scheme [ j ] . o )
local node = minetest.get_node ( p )
if not node or node.name ~= " mcl_portals:end_portal_frame_eye " or node.param2 ~= ep_scheme [ j ] . p then
portal = false
break
2017-11-21 09:54:45 +01:00
end
2017-08-17 00:16:29 +02:00
end
2021-01-16 18:51:30 +04:00
if portal then
return true , { x = pos0.x + 1 , y = pos0.y , z = pos0.z + 1 }
2017-08-17 00:16:29 +02:00
end
end
2017-11-21 09:54:45 +01:00
return false
2017-08-17 00:16:29 +02:00
end
2017-12-09 14:58:06 +01: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 09:54:45 +01:00
local SIZE = 3
2017-12-09 14:58:06 +01:00
local name
if destroy then
name = " air "
else
name = " mcl_portals:portal_end "
end
2020-01-06 16:51:40 +01:00
local posses = { }
2017-11-21 09:54:45 +01:00
for x = pos.x , pos.x + SIZE - 1 do
for z = pos.z , pos.z + SIZE - 1 do
2020-01-06 16:51:40 +01:00
table.insert ( posses , { x = x , y = pos.y , z = z } )
2017-08-17 00:16:29 +02:00
end
end
2020-01-06 16:51:40 +01:00
minetest.bulk_set_node ( posses , { name = name } )
2017-08-17 00:16:29 +02:00
end
2024-02-06 21:53:46 +01:00
local function show_credits ( player )
local meta = player : get_meta ( )
local completed_end = meta : get_int ( " completed_end " )
if completed_end == 0 then
meta : set_int ( " completed_end " , 1 )
2024-02-21 01:37:59 +01:00
for _ , func in ipairs ( mcl_portals.registered_on_beat_game ) do
func ( player )
end
mcl_credits.show ( player )
2024-02-06 21:53:46 +01:00
end
end
2021-01-16 18:51:30 +04:00
function mcl_portals . end_teleport ( obj , pos )
if not obj then return end
local pos = pos or obj : get_pos ( )
if not pos then return end
local dim = mcl_worlds.pos_to_dimension ( pos )
2017-11-21 05:39:27 +01:00
2021-01-16 18:51:30 +04:00
local target
if dim == " end " then
-- End portal in the End:
-- Teleport back to the player's spawn or world spawn in the Overworld.
if obj : is_player ( ) then
target = mcl_spawn.get_player_spawn_pos ( obj )
end
2017-11-21 22:58:11 +01:00
2024-07-26 03:41:44 +02:00
target = target or mcl_spawn.get_world_spawn_pos ( )
2021-01-16 18:51:30 +04:00
else
-- End portal in any other dimension:
2024-03-21 04:51:39 +01:00
-- Teleport to the End at a fixed position.
-- The destination is built by mcl_structures.
2017-11-21 22:58:11 +01:00
2021-01-16 18:51:30 +04:00
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
2021-01-06 13:01:27 +01:00
2021-01-16 18:51:30 +04:00
target = table.copy ( platform_pos )
target.y = target.y + 1
end
2017-11-21 05:39:27 +01:00
2021-01-16 18:51:30 +04:00
-- Teleport
obj : set_pos ( target )
2017-08-17 01:33:36 +02:00
2021-01-16 18:51:30 +04:00
if obj : is_player ( ) then
-- Look towards the main End island
if dim ~= " end " then
obj : set_look_horizontal ( math.pi / 2 )
2021-04-04 19:13:46 +02:00
-- Show credits
else
2024-02-06 21:53:46 +01:00
show_credits ( obj )
2021-01-16 18:51:30 +04:00
end
mcl_worlds.dimension_change ( obj , mcl_worlds.pos_to_dimension ( target ) )
minetest.sound_play ( " mcl_portals_teleport " , { pos = target , gain = 0.5 , max_hear_distance = 16 } , true )
2024-03-21 04:42:35 +01:00
else
local l = obj : get_luaentity ( )
if l and l.is_mob then
l._just_portaled = 5
end
2021-01-16 18:51:30 +04:00
end
end
2017-11-21 05:39:27 +01:00
2024-09-03 04:33:04 +02:00
function mcl_portals . end_portal_teleport ( pos )
for obj in minetest.objects_inside_radius ( pos , 1 ) do
local lua_entity = obj : get_luaentity ( )
2021-01-16 18:51:30 +04:00
if obj : is_player ( ) or lua_entity then
local objpos = obj : get_pos ( )
if objpos == nil then
return
end
2021-01-06 13:01:27 +01:00
2021-01-16 18:51:30 +04:00
-- 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
2017-08-17 00:16:29 +02:00
end
2021-01-16 18:51:30 +04:00
mcl_portals.end_teleport ( obj , objpos )
2022-06-11 12:29:11 -06:00
awards.unlock ( obj : get_player_name ( ) , " mcl:enterEndPortal " )
2017-08-17 00:16:29 +02:00
end
2021-01-16 18:51:30 +04:00
end
end
minetest.register_abm ( {
label = " End portal teleportation " ,
nodenames = { " mcl_portals:portal_end " } ,
interval = 0.1 ,
chance = 1 ,
action = mcl_portals.end_portal_teleport ,
2017-08-17 00:16:29 +02:00
} )
2017-12-09 14:58:06 +01:00
local rotate_frame , rotate_frame_eye
2017-08-17 00:16:29 +02:00
2017-12-09 14:58:06 +01:00
if minetest.get_modpath ( " screwdriver " ) then
2017-12-09 15:01:41 +01:00
-- Intentionally not rotatable
rotate_frame = false
2017-12-09 14:58:06 +01:00
rotate_frame_eye = false
end
2017-08-17 00:16:29 +02:00
2024-07-26 03:41:44 +02:00
local function after_place_node ( pos , placer , itemstack , pointed_thing ) ---@diagnostic disable-line: unused-local
2021-01-21 18:35:04 +04:00
local node = minetest.get_node ( pos )
if node then
node.param2 = ( node.param2 + 2 ) % 4
minetest.swap_node ( pos , node )
local ok , ppos = check_end_portal_frame ( pos )
if ok then
-- Epic 'portal open' sound effect that can be heard everywhere
minetest.sound_play ( " mcl_portals_open_end_portal " , { gain = 0.8 } , true )
end_portal_area ( ppos )
end
end
end
2017-09-02 15:49:41 +02:00
minetest.register_node ( " mcl_portals:end_portal_frame " , {
2019-03-08 00:22:28 +01:00
description = S ( " End Portal Frame " ) ,
2020-02-19 04:54:17 +01:00
_tt_help = S ( " Used to construct end portals " ) ,
2019-03-25 12:05:57 +01: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. " ) ,
2024-10-26 16:19:10 +02:00
groups = { creative_breakable = 1 , deco_block = 1 , end_portal_frame = 1 , unmovable_by_piston = 1 } ,
2017-09-02 16:16:04 +02:00
tiles = { " mcl_portals_endframe_top.png " , " mcl_portals_endframe_bottom.png " , " mcl_portals_endframe_side.png " } ,
2021-02-18 14:00:17 +01:00
use_texture_alpha = minetest.features . use_texture_alpha_string_modes and " opaque " or false ,
2017-09-02 15:49:41 +02: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 " ,
light_source = 1 ,
2017-12-09 14:58:06 +01:00
on_rotate = rotate_frame ,
2021-01-21 18:35:04 +04:00
after_place_node = after_place_node ,
2020-04-17 21:40:13 +02:00
_mcl_blast_resistance = 36000000 ,
2017-09-02 15:49:41 +02:00
_mcl_hardness = - 1 ,
} )
minetest.register_node ( " mcl_portals:end_portal_frame_eye " , {
2019-03-08 00:22:28 +01:00
description = S ( " End Portal Frame with Eye of Ender " ) ,
2020-03-08 03:18:47 +01:00
_tt_help = S ( " Used to construct end portals " ) ,
2017-09-02 15:49:41 +02:00
_doc_items_create_entry = false ,
2024-10-26 16:19:10 +02:00
groups = { creative_breakable = 1 , deco_block = 1 , comparator_signal = 15 , end_portal_frame = 2 , not_in_creative_inventory = 1 , unmovable_by_piston = 1 } ,
2017-09-02 16:16:04 +02: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 " } ,
2021-02-18 14:00:17 +01:00
use_texture_alpha = minetest.features . use_texture_alpha_string_modes and " opaque " or false ,
2017-09-02 15:49:41 +02:00
paramtype2 = " facedir " ,
drawtype = " nodebox " ,
2024-08-22 22:14:41 +02:00
_mcl_baseitem = " mcl_portals:end_portal_frame " ,
2017-09-02 15:49:41 +02:00
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 " ,
light_source = 1 ,
2017-12-09 14:58:06 +01:00
on_destruct = function ( pos )
local ok , ppos = check_end_portal_frame ( pos )
if ok then
end_portal_area ( ppos , true )
end
end ,
on_rotate = rotate_frame_eye ,
2021-01-21 18:35:04 +04:00
after_place_node = after_place_node ,
2020-04-17 21:40:13 +02:00
_mcl_blast_resistance = 36000000 ,
2017-09-02 15:49:41 +02:00
_mcl_hardness = - 1 ,
} )
2021-07-22 00:46:43 +02:00
if has_doc then
2017-09-02 15:49:41 +02:00
doc.add_entry_alias ( " nodes " , " mcl_portals:end_portal_frame " , " nodes " , " mcl_portals:end_portal_frame_eye " )
end
2017-12-09 14:58:06 +01:00
--[[ ITEM OVERRIDES ]]
2017-08-17 00:16:29 +02:00
-- Portal opener
2024-02-14 01:15:47 +01:00
local old_on_place = minetest.registered_items [ " mcl_end:ender_eye " ] . on_place
2017-08-17 00:16:29 +02:00
minetest.override_item ( " mcl_end:ender_eye " , {
on_place = function ( itemstack , user , pointed_thing )
2017-08-17 04:12:34 +02:00
2024-03-03 20:38:18 +01:00
local rc = mcl_util.call_on_rightclick ( itemstack , user , pointed_thing )
if rc then return rc end
local node = minetest.get_node ( pointed_thing.under )
2017-09-02 19:15:15 +02:00
-- Place eye of ender into end portal frame
2017-11-21 09:54:45 +01:00
if pointed_thing.under and node.name == " mcl_portals:end_portal_frame " then
2019-02-08 21:59:01 +01: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 05:20:41 +02:00
minetest.set_node ( pointed_thing.under , { name = " mcl_portals:end_portal_frame_eye " , param2 = node.param2 } )
2017-11-21 09:54:45 +01:00
2021-07-22 00:46:43 +02:00
if has_doc then
2017-09-02 19:15:15 +02:00
doc.mark_entry_as_revealed ( user : get_player_name ( ) , " nodes " , " mcl_portals:end_portal_frame " )
end
minetest.sound_play (
" default_place_node_hard " ,
2020-04-07 00:55:45 +02:00
{ pos = pointed_thing.under , gain = 0.5 , max_hear_distance = 16 } , true )
2020-07-10 16:08:40 +02:00
if not minetest.is_creative_enabled ( user : get_player_name ( ) ) then
2017-09-02 19:15:15 +02:00
itemstack : take_item ( ) -- 1 use
end
2017-11-21 09:54:45 +01:00
2021-01-21 18:35:04 +04:00
local ok , ppos = check_end_portal_frame ( pointed_thing.under )
2017-11-21 09:54:45 +01:00
if ok then
2021-01-21 18:35:04 +04:00
-- Epic 'portal open' sound effect that can be heard everywhere
minetest.sound_play ( " mcl_portals_open_end_portal " , { gain = 0.8 } , true )
end_portal_area ( ppos )
2021-07-22 00:46:43 +02:00
if has_doc then
2017-11-21 09:54:45 +01:00
doc.mark_entry_as_revealed ( user : get_player_name ( ) , " nodes " , " mcl_portals:portal_end " )
end
end
2024-02-14 01:15:47 +01:00
elseif old_on_place then
return old_on_place ( itemstack , user , pointed_thing )
2017-09-02 19:15:15 +02:00
end
2017-08-17 00:16:29 +02:00
return itemstack
end ,
} )