2 local SPAWN_MIN
= mcl_vars
.mg_end_min
+70
3 local SPAWN_MAX
= mcl_vars
.mg_end_min
+98
5 local mg_name
= minetest
.get_mapgen_setting("mg_name")
8 minetest
.register_node("mcl_portals:portal_end", {
9 description
= "End Portal",
10 _doc_items_longdesc
= "An End portal teleports creatures and objects to the mysterious End dimension (and back!).",
11 _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.",
14 name
= "mcl_portals_end_portal.png",
16 type = "vertical_frames",
30 sunlight_propagates
= true,
31 use_texture_alpha
= true,
36 is_ground_content
= false,
40 post_effect_color
= {a
= 192, r
= 0, g
= 0, b
= 0},
45 {-0.5, -0.5, -0.5, 0.5, 4/16, 0.5},
48 groups
= {not_in_creative_inventory
= 1},
51 _mcl_blast_resistance
= 18000000,
54 -- Obsidian platform at the End portal destination in the End
55 local function build_end_portal_destination(pos
)
56 local p1
= {x
= pos
.x
- 2, y
= pos
.y
, z
= pos
.z
-2}
57 local p2
= {x
= pos
.x
+ 2, y
= pos
.y
+2, z
= pos
.z
+2}
62 local newp
= {x
=x
,y
=y
,z
=z
}
63 -- Build obsidian platform
64 if minetest
.registered_nodes
[minetest
.get_node(newp
).name
].is_ground_content
then
66 minetest
.set_node(newp
, {name
="mcl_core:obsidian"})
68 minetest
.remove_node(newp
)
77 -- Check if pos is part of a valid end portal frame, filled with eyes of ender.
78 local function check_end_portal_frame(pos
)
79 -- Check if pos has an end portal frame with eye of ender
80 local eframe
= function(pos
, param2
)
81 local node
= minetest
.get_node(pos
)
82 if node
.name
== "mcl_portals:end_portal_frame_eye" then
83 if param2
== nil or node
.param2
== param2
then
90 -- Step 1: Find a row of 3 end portal frames with eyes, all facing the same direction
92 local streak_start
, streak_end
, streak_start_node
, streak_end_node
94 local axes
= { "x", "z" }
97 for b
=pos
[axis
]-2, pos
[axis
]+2 do
98 local cpos
= table.copy(pos
)
100 local e
, node
= eframe(cpos
, last_param2
)
102 last_param2
= node
.param2
105 streak_start
= table.copy(pos
)
106 streak_start
[axis
] = b
107 streak_start_node
= node
108 elseif streak
== 3 then
109 streak_end
= table.copy(pos
)
111 streak_end_node
= node
125 -- Has a row been found?
127 -- Step 2: Using the known facedir, check the remaining spots in which we expect
128 -- “eyed” end portal frames.
129 local dir
= minetest
.facedir_to_dir(streak_start_node
.param2
)
132 if not eframe({x
=streak_start
.x
+ i
*dir
.x
, y
=streak_start
.y
, z
=streak_start
.z
- 1}) then
135 if not eframe({x
=streak_start
.x
+ i
*dir
.x
, y
=streak_start
.y
, z
=streak_end
.z
+ 1}) then
138 if not eframe({x
=streak_start
.x
+ 4*dir
.x
, y
=streak_start
.y
, z
=streak_start
.z
+ i
-1}) then
142 -- All checks survived! We have a valid portal!
149 return true, { x
= streak_start
.x
+ k
, y
= streak_start
.y
, z
= streak_start
.z
}
150 elseif dir
.z
~= 0 then
152 if not eframe({x
=streak_start
.x
- 1, y
=streak_start
.y
, z
=streak_start
.z
+ i
*dir
.z
}) then
155 if not eframe({x
=streak_end
.x
+ 1, y
=streak_start
.y
, z
=streak_start
.z
+ i
*dir
.z
}) then
158 if not eframe({x
=streak_start
.x
+ i
-1, y
=streak_start
.y
, z
=streak_start
.z
+ 4*dir
.z
}) then
168 -- All checks survived! We have a valid portal!
169 return true, { x
= streak_start
.x
, y
= streak_start
.y
, z
= streak_start
.z
+ k
}
175 -- Generate or destroy a 3×3 end portal beginning at pos. To be used to fill an end portal framea.
176 -- If destroy == true, the 3×3 area is removed instead.
177 local function end_portal_area(pos
, destroy
)
183 name
= "mcl_portals:portal_end"
185 for x
=pos
.x
, pos
.x
+SIZE
-1 do
186 for z
=pos
.z
, pos
.z
+SIZE
-1 do
187 minetest
.set_node({x
=x
,y
=pos
.y
,z
=z
}, {name
=name
})
192 minetest
.register_abm({
193 label
= "End portal teleportation",
194 nodenames
= {"mcl_portals:portal_end"},
197 action
= function(pos
, node
)
198 -- Destroy legacy end portals created with quartz block frame
199 -- by turning them into cobwebs.
200 -- We can tell if a end portal is legacy if it has portal_target as metadata.
201 -- FIXME: Remove this after some time.
202 local meta
= minetest
.get_meta(pos
)
203 local legacy_portal_target
= meta
:get_string("portal_frame1")
204 if legacy_portal_target
and legacy_portal_target
~= "" then
205 minetest
.set_node(pos
, {name
="mcl_core:cobweb"})
209 for _
,obj
in ipairs(minetest
.get_objects_inside_radius(pos
, 1)) do
210 local lua_entity
= obj
:get_luaentity() --maikerumine added for objects to travel
211 if obj
:is_player() or lua_entity
then
212 local dim
= mcl_worlds
.pos_to_dimension(pos
)
214 local objpos
= obj
:getpos()
215 if objpos
== nil then
219 -- Check if object is actually in portal.
220 objpos
.y
= math
.ceil(objpos
.y
)
221 if minetest
.get_node(objpos
).name
~= "mcl_portals:portal_end" then
227 -- End portal in the End:
228 -- Teleport back to the player's spawn in the Overworld.
230 target
= mcl_spawn
.get_spawn_pos(obj
)
232 -- End portal in any other dimension:
233 -- Teleport to the End at a fixed position and generate a
234 -- 5×5 obsidian platform below.
236 local platform_pos
= mcl_vars
.mg_end_platform_pos
237 -- force emerge of target1 area
238 minetest
.get_voxel_manip():read_from_map(platform_pos
, platform_pos
)
239 if not minetest
.get_node_or_nil(platform_pos
) then
240 minetest
.emerge_area(vector
.subtract(platform_pos
, 3), vector
.add(platform_pos
, 3))
244 local function check_and_build_end_portal_destination(pos
)
245 local n
= minetest
.get_node_or_nil(pos
)
246 if n
and n
.name
~= "mcl_core:obsidian" then
247 build_end_portal_destination(pos
)
248 minetest
.after(2, check_and_build_end_portal_destination
, pos
)
250 minetest
.after(1, check_and_build_end_portal_destination
, pos
)
255 build_end_portal_destination(platform_pos
)
256 check_and_build_end_portal_destination(platform_pos
)
258 target
= table.copy(platform_pos
)
259 target
.y
= target
.y
+ 1
264 if obj
:is_player() then
265 -- Look towards the main End island
267 obj
:set_look_horizontal(math
.pi
/2)
269 mcl_worlds
.dimension_change(obj
, mcl_worlds
.pos_to_dimension(target
))
270 minetest
.sound_play("mcl_portals_teleport", {pos
=target
, gain
=0.5, max_hear_distance
= 16})
277 local rotate_frame
, rotate_frame_eye
279 if minetest
.get_modpath("screwdriver") then
280 -- Intentionally not rotatable
282 rotate_frame_eye
= false
285 minetest
.register_node("mcl_portals:end_portal_frame", {
286 description
= "End Portal Frame",
287 _doc_items_longdesc
= "End portal frames are used in the construction of End portals. Each block has a socket for an eye of ender." .. "\n" .. "NOTE: The End dimension is currently incomplete and boring.",
288 _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.",
289 groups
= { creative_breakable
= 1, deco_block
= 1 },
290 tiles
= { "mcl_portals_endframe_top.png", "mcl_portals_endframe_bottom.png", "mcl_portals_endframe_side.png" },
291 paramtype2
= "facedir",
292 drawtype
= "nodebox",
295 fixed
= { -0.5, -0.5, -0.5, 0.5, 5/16, 0.5 },
297 is_ground_content
= false,
298 sounds
= mcl_sounds
.node_sound_stone_defaults(),
300 sunlight_propagates
= false,
303 on_rotate
= rotate_frame
,
305 _mcl_blast_resistance
= 18000000,
309 minetest
.register_node("mcl_portals:end_portal_frame_eye", {
310 description
= "End Portal Frame with Eye of Ender",
311 _doc_items_create_entry
= false,
312 groups
= { creative_breakable
= 1, not_in_creative_inventory
= 1, comparator_signal
= 15 },
313 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" },
314 paramtype2
= "facedir",
315 drawtype
= "nodebox",
319 { -0.5, -0.5, -0.5, 0.5, 5/16, 0.5 }, -- Frame
320 { -4/16, 5/16, -4/16, 4/16, 0.5, 4/16 }, -- Eye
323 is_ground_content
= false,
324 sounds
= mcl_sounds
.node_sound_stone_defaults(),
326 sunlight_propagates
= false,
328 on_destruct
= function(pos
)
329 local ok
, ppos
= check_end_portal_frame(pos
)
331 end_portal_area(ppos
, true)
335 on_rotate
= rotate_frame_eye
,
337 _mcl_blast_resistance
= 18000000,
341 if minetest
.get_modpath("doc") then
342 doc
.add_entry_alias("nodes", "mcl_portals:end_portal_frame", "nodes", "mcl_portals:end_portal_frame_eye")
346 --[[ ITEM OVERRIDES ]]
349 minetest
.override_item("mcl_end:ender_eye", {
350 on_place
= function(itemstack
, user
, pointed_thing
)
351 -- Use pointed node's on_rightclick function first, if present
352 local node
= minetest
.get_node(pointed_thing
.under
)
353 if user
and not user
:get_player_control().sneak
then
354 if minetest
.registered_nodes
[node
.name
] and minetest
.registered_nodes
[node
.name
].on_rightclick
then
355 return minetest
.registered_nodes
[node
.name
].on_rightclick(pointed_thing
.under
, node
, user
, itemstack
) or itemstack
359 -- Place eye of ender into end portal frame
360 if pointed_thing
.under
and node
.name
== "mcl_portals:end_portal_frame" then
361 minetest
.swap_node(pointed_thing
.under
, { name
= "mcl_portals:end_portal_frame_eye", param2
= node
.param2
})
363 if minetest
.get_modpath("doc") then
364 doc
.mark_entry_as_revealed(user
:get_player_name(), "nodes", "mcl_portals:end_portal_frame")
367 "default_place_node_hard",
368 {pos
= pointed_thing
.under
, gain
= 0.5, max_hear_distance
= 16})
369 if not minetest
.settings
:get_bool("creative_mode") then
370 itemstack
:take_item() -- 1 use
373 local ok
, ppos
= check_end_portal_frame(pointed_thing
.under
)
375 end_portal_area(ppos
)
376 if minetest
.get_modpath("doc") then
377 doc
.mark_entry_as_revealed(user
:get_player_name(), "nodes", "mcl_portals:portal_end")