Fix crash when creating Nether portal
[MineClone/MineClone2.git] / mods / ITEMS / mcl_portals / portal_end.lua
blob8461e446c6f241ed5fbfdd1396e7dcf2cd941ef4
1 -- Parameters
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")
7 -- End portal
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.",
12 tiles = {
14 name = "mcl_portals_end_portal.png",
15 animation = {
16 type = "vertical_frames",
17 aspect_w = 16,
18 aspect_h = 16,
19 length = 1.0,
23 name = "mcl_portals_end_portal.png",
24 animation = {
25 type = "vertical_frames",
26 aspect_w = 16,
27 aspect_h = 16,
28 length = 6.0,
31 "blank.png",
33 drawtype = "nodebox",
34 paramtype = "light",
35 sunlight_propagates = true,
36 use_texture_alpha = true,
37 walkable = true,
38 diggable = false,
39 pointable = false,
40 buildable_to = false,
41 is_ground_content = false,
42 drop = "",
43 -- This is 15 in MC.
44 light_source = 14,
45 post_effect_color = {a = 192, r = 0, g = 0, b = 0},
46 alpha = 192,
47 -- This prevents “falling through”
48 collision_box = {
49 type = "fixed",
50 fixed = {
51 {-0.5, -0.5, -0.5, 0.5, -7/16, 0.5},
54 node_box = {
55 type = "fixed",
56 fixed = {
57 {-0.5, -0.5, -0.5, 0.5, 4/16, 0.5},
60 groups = {not_in_creative_inventory = 1, disable_jump = 1 },
62 _mcl_hardness = -1,
63 _mcl_blast_resistance = 18000000,
66 -- Obsidian platform at the End portal destination in the End
67 local function build_end_portal_destination(pos)
68 local p1 = {x = pos.x - 2, y = pos.y, z = pos.z-2}
69 local p2 = {x = pos.x + 2, y = pos.y+2, z = pos.z+2}
71 for x = p1.x, p2.x do
72 for y = p1.y, p2.y do
73 for z = p1.z, p2.z do
74 local newp = {x=x,y=y,z=z}
75 -- Build obsidian platform
76 if minetest.registered_nodes[minetest.get_node(newp).name].is_ground_content then
77 if y == p1.y then
78 minetest.set_node(newp, {name="mcl_core:obsidian"})
79 else
80 minetest.remove_node(newp)
81 end
82 end
83 end
84 end
85 end
86 end
89 -- Check if pos is part of a valid end portal frame, filled with eyes of ender.
90 local function check_end_portal_frame(pos)
91 -- Check if pos has an end portal frame with eye of ender
92 local eframe = function(pos, param2)
93 local node = minetest.get_node(pos)
94 if node.name == "mcl_portals:end_portal_frame_eye" then
95 if param2 == nil or node.param2 == param2 then
96 return true, node
97 end
98 end
99 return false
102 -- Step 1: Find a row of 3 end portal frames with eyes, all facing the same direction
103 local streak = 0
104 local streak_start, streak_end, streak_start_node, streak_end_node
105 local last_param2
106 local axes = { "x", "z" }
107 for a=1, #axes do
108 local axis = axes[a]
109 for b=pos[axis]-2, pos[axis]+2 do
110 local cpos = table.copy(pos)
111 cpos[axis] = b
112 local e, node = eframe(cpos, last_param2)
113 if e then
114 last_param2 = node.param2
115 streak = streak + 1
116 if streak == 1 then
117 streak_start = table.copy(pos)
118 streak_start[axis] = b
119 streak_start_node = node
120 elseif streak == 3 then
121 streak_end = table.copy(pos)
122 streak_end[axis] = b
123 streak_end_node = node
124 break
126 else
127 streak = 0
128 last_param2 = nil
131 if streak_end then
132 break
134 streak = 0
135 last_param2 = nil
137 -- Has a row been found?
138 if streak_end then
139 -- Step 2: Using the known facedir, check the remaining spots in which we expect
140 -- “eyed” end portal frames.
141 local dir = minetest.facedir_to_dir(streak_start_node.param2)
142 if dir.x ~= 0 then
143 for i=1, 3 do
144 if not eframe({x=streak_start.x + i*dir.x, y=streak_start.y, z=streak_start.z - 1}) then
145 return false
147 if not eframe({x=streak_start.x + i*dir.x, y=streak_start.y, z=streak_end.z + 1}) then
148 return false
150 if not eframe({x=streak_start.x + 4*dir.x, y=streak_start.y, z=streak_start.z + i-1}) then
151 return false
154 -- All checks survived! We have a valid portal!
155 local k
156 if dir.x > 0 then
157 k = 1
158 else
159 k = -3
161 return true, { x = streak_start.x + k, y = streak_start.y, z = streak_start.z }
162 elseif dir.z ~= 0 then
163 for i=1, 3 do
164 if not eframe({x=streak_start.x - 1, y=streak_start.y, z=streak_start.z + i*dir.z}) then
165 return false
167 if not eframe({x=streak_end.x + 1, y=streak_start.y, z=streak_start.z + i*dir.z}) then
168 return false
170 if not eframe({x=streak_start.x + i-1, y=streak_start.y, z=streak_start.z + 4*dir.z}) then
171 return false
174 local k
175 if dir.z > 0 then
176 k = 1
177 else
178 k = -3
180 -- All checks survived! We have a valid portal!
181 return true, { x = streak_start.x, y = streak_start.y, z = streak_start.z + k }
184 return false
187 -- Generate or destroy a 3×3 end portal beginning at pos. To be used to fill an end portal framea.
188 -- If destroy == true, the 3×3 area is removed instead.
189 local function end_portal_area(pos, destroy)
190 local SIZE = 3
191 local name
192 if destroy then
193 name = "air"
194 else
195 name = "mcl_portals:portal_end"
197 for x=pos.x, pos.x+SIZE-1 do
198 for z=pos.z, pos.z+SIZE-1 do
199 minetest.set_node({x=x,y=pos.y,z=z}, {name=name})
204 minetest.register_abm({
205 label = "End portal teleportation",
206 nodenames = {"mcl_portals:portal_end"},
207 interval = 1,
208 chance = 1,
209 action = function(pos, node)
210 -- Destroy legacy end portals created with quartz block frame
211 -- by turning them into cobwebs.
212 -- We can tell if a end portal is legacy if it has portal_target as metadata.
213 -- FIXME: Remove this after some time.
214 local meta = minetest.get_meta(pos)
215 local legacy_portal_target = meta:get_string("portal_frame1")
216 if legacy_portal_target and legacy_portal_target ~= "" then
217 minetest.set_node(pos, {name="mcl_core:cobweb"})
218 return
221 for _,obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
222 local lua_entity = obj:get_luaentity() --maikerumine added for objects to travel
223 if obj:is_player() or lua_entity then
224 local dim = mcl_worlds.pos_to_dimension(pos)
226 local objpos = obj:getpos()
227 if objpos == nil then
228 return
231 -- Check if object is actually in portal.
232 objpos.y = math.ceil(objpos.y)
233 if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then
234 return
237 local target
238 if dim == "end" then
239 -- End portal in the End:
240 -- Teleport back to the player's spawn in the Overworld.
242 target = mcl_spawn.get_spawn_pos(obj)
243 else
244 -- End portal in any other dimension:
245 -- Teleport to the End at a fixed position and generate a
246 -- 5×5 obsidian platform below.
248 local platform_pos = mcl_vars.mg_end_platform_pos
249 -- force emerge of target1 area
250 minetest.get_voxel_manip():read_from_map(platform_pos, platform_pos)
251 if not minetest.get_node_or_nil(platform_pos) then
252 minetest.emerge_area(vector.subtract(platform_pos, 3), vector.add(platform_pos, 3))
255 -- Build destination
256 local function check_and_build_end_portal_destination(pos)
257 local n = minetest.get_node_or_nil(pos)
258 if n and n.name ~= "mcl_core:obsidian" then
259 build_end_portal_destination(pos)
260 minetest.after(2, check_and_build_end_portal_destination, pos)
261 elseif not n then
262 minetest.after(1, check_and_build_end_portal_destination, pos)
266 local platform
267 build_end_portal_destination(platform_pos)
268 check_and_build_end_portal_destination(platform_pos)
270 target = table.copy(platform_pos)
271 target.y = target.y + 1
274 -- Teleport
275 obj:set_pos(target)
276 if obj:is_player() then
277 -- Look towards the main End island
278 if dim ~= "end" then
279 obj:set_look_horizontal(math.pi/2)
281 mcl_worlds.dimension_change(obj, mcl_worlds.pos_to_dimension(target))
282 minetest.sound_play("mcl_portals_teleport", {pos=target, gain=0.5, max_hear_distance = 16})
286 end,
289 local rotate_frame, rotate_frame_eye
291 if minetest.get_modpath("screwdriver") then
292 -- Intentionally not rotatable
293 rotate_frame = false
294 rotate_frame_eye = false
297 minetest.register_node("mcl_portals:end_portal_frame", {
298 description = "End Portal Frame",
299 _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.",
300 _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.",
301 groups = { creative_breakable = 1, deco_block = 1 },
302 tiles = { "mcl_portals_endframe_top.png", "mcl_portals_endframe_bottom.png", "mcl_portals_endframe_side.png" },
303 paramtype2 = "facedir",
304 drawtype = "nodebox",
305 node_box = {
306 type = "fixed",
307 fixed = { -0.5, -0.5, -0.5, 0.5, 5/16, 0.5 },
309 is_ground_content = false,
310 sounds = mcl_sounds.node_sound_stone_defaults(),
311 paramtype = "light",
312 sunlight_propagates = false,
313 light_source = 1,
315 on_rotate = rotate_frame,
317 _mcl_blast_resistance = 18000000,
318 _mcl_hardness = -1,
321 minetest.register_node("mcl_portals:end_portal_frame_eye", {
322 description = "End Portal Frame with Eye of Ender",
323 _doc_items_create_entry = false,
324 groups = { creative_breakable = 1, deco_block = 1, comparator_signal = 15 },
325 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" },
326 paramtype2 = "facedir",
327 drawtype = "nodebox",
328 node_box = {
329 type = "fixed",
330 fixed = {
331 { -0.5, -0.5, -0.5, 0.5, 5/16, 0.5 }, -- Frame
332 { -4/16, 5/16, -4/16, 4/16, 0.5, 4/16 }, -- Eye
335 is_ground_content = false,
336 sounds = mcl_sounds.node_sound_stone_defaults(),
337 paramtype = "light",
338 sunlight_propagates = false,
339 light_source = 1,
340 on_destruct = function(pos)
341 local ok, ppos = check_end_portal_frame(pos)
342 if ok then
343 end_portal_area(ppos, true)
345 end,
346 on_construct = function(pos)
347 local ok, ppos = check_end_portal_frame(pos)
348 if ok then
349 end_portal_area(ppos)
351 end,
353 on_rotate = rotate_frame_eye,
355 _mcl_blast_resistance = 18000000,
356 _mcl_hardness = -1,
359 if minetest.get_modpath("doc") then
360 doc.add_entry_alias("nodes", "mcl_portals:end_portal_frame", "nodes", "mcl_portals:end_portal_frame_eye")
364 --[[ ITEM OVERRIDES ]]
366 -- Portal opener
367 minetest.override_item("mcl_end:ender_eye", {
368 on_place = function(itemstack, user, pointed_thing)
369 -- Use pointed node's on_rightclick function first, if present
370 local node = minetest.get_node(pointed_thing.under)
371 if user and not user:get_player_control().sneak then
372 if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
373 return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
377 -- Place eye of ender into end portal frame
378 if pointed_thing.under and node.name == "mcl_portals:end_portal_frame" then
379 minetest.set_node(pointed_thing.under, { name = "mcl_portals:end_portal_frame_eye", param2 = node.param2 })
381 if minetest.get_modpath("doc") then
382 doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:end_portal_frame")
384 minetest.sound_play(
385 "default_place_node_hard",
386 {pos = pointed_thing.under, gain = 0.5, max_hear_distance = 16})
387 if not minetest.settings:get_bool("creative_mode") then
388 itemstack:take_item() -- 1 use
391 local ok = check_end_portal_frame(pointed_thing.under)
392 if ok then
393 if minetest.get_modpath("doc") then
394 doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:portal_end")
398 return itemstack
399 end,