1 local nether_channel = minetest.mod_channel_join("nether_teleporters")
3 local path = minetest.get_modpath("nether")
4 dofile(path.."/schem.lua")
6 minetest.register_biome({
12 node_riverbed = "air",
17 node_dungeon_alt = "air",
18 node_dungeon_stair = "air",
23 humidity_point = -100,
26 minetest.register_node("nether:bedrock", {
27 description = "Bedrock",
28 tiles = {"bedrock.png"},
29 groups = {unbreakable = 1, pathable = 1},
30 sounds = main.stoneSound(),
31 is_ground_content = false,
32 --light_source = 14, --debugging
36 minetest.register_node("nether:netherrack", {
37 description = "Netherrack",
38 tiles = {"netherrack.png"},
39 groups = {netherrack = 1, pathable = 1},
40 sounds = main.stoneSound(),
41 is_ground_content = false,
48 tools = {"main:woodpick","main:stonepick","main:ironpick","main:goldpick","main:diamondpick"},
49 items = {"nether:netherrack"},
56 minetest.register_node("nether:obsidian", {
57 description = "Obsidian",
58 tiles = {"obsidian.png"},
59 groups = {stone = 5, pathable = 1},
60 --groups = {stone = 1, pathable = 1}, --leave this here for debug
61 sounds = main.stoneSound(),
62 is_ground_content = false,
63 after_destruct = function(pos, oldnode)
64 destroy_nether_portal(pos)
69 --this is from https://github.com/paramat/lvm_example/blob/master/init.lua
72 -- Set the 3D noise parameters for the terrain.
73 local perlin= minetest.get_mapgen_params()
77 spread = {x = 384, y = 192, z = 384},
78 seed = 5900033, --perlin.seed
86 -- Set singlenode mapgen (air nodes only).
87 -- Disable the engine lighting calculation since that will be done for a
88 -- mapchunk of air nodes and will be incorrect after we place nodes.
90 --minetest.set_mapgen_params({mgname = "singlenode", flags = "nolight"})
93 -- Get the content IDs for the nodes used.
95 local c_sandstone = minetest.get_content_id("nether:netherrack")
96 local c_bedrock = minetest.get_content_id("nether:bedrock")
97 local c_air = minetest.get_content_id("air")
98 local c_lava = minetest.get_content_id("main:lava")
101 -- Initialize noise object to nil. It will be created once only during the
102 -- generation of the first mapchunk, to minimise memory use.
104 local nobj_terrain = nil
107 -- Localise noise buffer table outside the loop, to be re-used for all
108 -- mapchunks, therefore minimising memory use.
110 local nvals_terrain = {}
113 -- Localise data buffer table outside the loop, to be re-used for all
114 -- mapchunks, therefore minimising memory use.
119 -- On generated function.
121 -- 'minp' and 'maxp' are the minimum and maximum positions of the mapchunk that
122 -- define the 3D volume.
123 minetest.register_on_generated(function(minp, maxp, seed)
124 --nether starts at -10033 y
125 if maxp.y > -10033 then
128 -- Start time of mapchunk generation.
129 --local t0 = os.clock()
133 -- Side length of mapchunk.
134 local sidelen = maxp.x - minp.x + 1
135 -- Required dimensions of the 3D noise perlin map.
136 local permapdims3d = {x = sidelen, y = sidelen, z = sidelen}
137 -- Create the perlin map noise object once only, during the generation of
138 -- the first mapchunk when 'nobj_terrain' is 'nil'.
139 nobj_terrain = minetest.get_perlin_map(np_terrain, permapdims3d) --nobj_terrain or
140 -- Create a flat array of noise values from the perlin map, with the
141 -- minimum point being 'minp'.
142 -- Set the buffer parameter to use and reuse 'nvals_terrain' for this.
143 nobj_terrain:get3dMap_flat(minp, nvals_terrain)
147 -- Load the voxelmanip with the result of engine mapgen. Since 'singlenode'
148 -- mapgen is used this will be a mapchunk of air nodes.
149 local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
150 -- 'area' is used later to get the voxelmanip indexes for positions.
151 local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}
152 -- Get the content ID data from the voxelmanip in the form of a flat array.
153 -- Set the buffer parameter to use and reuse 'data' for this.
158 -- Noise index for the flat array of noise values.
160 -- Process the content IDs in 'data'.
161 -- The most useful order is a ZYX loop because:
162 -- 1. This matches the order of the 3D noise flat array.
163 -- 2. This allows a simple +1 incrementing of the voxelmanip index along x
166 for z = minp.z, maxp.z do
167 for y = minp.y, maxp.y do
168 -- Voxelmanip index for the flat array of content IDs.
169 -- Initialise to first node in this x row.
170 local vi = area:index(minp.x, y, z)
171 for x = minp.x, maxp.x do
172 -- Consider a 'solidness' value for each node,
173 -- let's call it 'density', where
174 -- density = density noise + density gradient.
175 local density_noise = nvals_terrain[ni]
176 -- Density gradient is a value that is 0 at water level (y = 1)
177 -- and falls in value with increasing y. This is necessary to
178 -- create a 'world surface' with only solid nodes deep underground
179 -- and only air high above water level.
180 -- Here '128' determines the typical maximum height of the terrain.
181 local density_gradient = (1 - y) / 128
183 --print(density_noise, density_gradient)
184 -- Place solid nodes when 'density' > 0.
185 --if density_noise + density_gradient > 0 then
186 if density_noise > 0 and y ~= -10033 then
187 data[vi] = c_sandstone
188 -- Otherwise if at or below water level place water.
189 elseif y == -10033 then
192 -- data[vi] = c_water
193 elseif y > -15000 then
199 -- Increment noise index.
201 -- Increment voxelmanip index along x row.
202 -- The voxelmanip index increases by 1 when
203 -- moving by 1 node in the +x direction.
209 -- After processing, write content ID data back to the voxelmanip.
211 -- Calculate lighting for what has been created.
214 vm:set_lighting({day=7,night=7}, minp, maxp)
216 -- Write what has been created to the world.
218 -- Liquid nodes were placed so set them flowing.
219 --vm:update_liquids()
221 -- Print generation time of this mapchunk.
222 --local chugent = math.ceil((os.clock() - t0) * 1000)
223 --print ("[lvm_example] Mapchunk generation time " .. chugent .. " ms")
227 minetest.register_node("nether:portal", {
228 description = "Nether Portal",
232 name = "nether_portal.png",
233 backface_culling = true,
235 type = "vertical_frames",
242 name = "nether_portal.png",
243 backface_culling = true,
245 type = "vertical_frames",
252 drawtype = "nodebox",
254 --paramtype2 = "facedir",
255 sunlight_propagates = true,
256 use_texture_alpha = false,
260 buildable_to = false,
261 is_ground_content = false,
264 --post_effect_color = {a = 180, r = 51, g = 7, b = 89},
270 connect_front = {0, -1/2, -1/2, 0, 1/2, 0 },
271 connect_left = {-1/2, -1/2, 0, 0, 1/2, 0},
272 connect_back = {0, -1/2, 0, 0, 1/2, 1/2 },
273 connect_right = { 0, -1/2, 0, 1/2, 1/2, 0},
275 connects_to = {"nether:portal","nether:obsidian"},
276 groups = {unbreakable=1},
277 --on_destruct = destroy_portal,
280 --branch out from center
282 local portal_failure = false
283 local x_failed = false
285 --this can be used globally to create nether portals from obsidian
286 function create_nether_portal(pos,origin,axis)
287 --create the origin node for stored memory
290 portal_failure = false
296 --2d virtual memory map creation (x axis)
300 --index only direct neighbors
301 if x_failed == false and (math.abs(x)+math.abs(y) == 1) then
302 local i = vector.add(pos,vector.new(x,y,0))
304 local execute_collection = true
306 if n_index[i.x] and n_index[i.x][i.y] then
307 if n_index[i.x][i.y][i.z] then
308 execute_collection = false
312 if execute_collection == true then
313 --print(minetest.get_node(i).name)
315 if minetest.get_node(i).name == "air" then
317 if vector.distance(i,origin) < 50 then
318 --add data to both maps
319 if not n_index[i.x] then n_index[i.x] = {} end
320 if not n_index[i.x][i.y] then n_index[i.x][i.y] = {} end
321 n_index[i.x][i.y][i.z] = {nether_portal=1} --get_group(i,"redstone_power")}
322 --the data to the 3d array must be written to memory before this is executed
323 --or a stack overflow occurs!!!
324 --pass down info for activators
325 create_nether_portal(i,origin,"x")
330 create_nether_portal(origin,origin,"z")
332 elseif minetest.get_node(i).name ~= "nether:obsidian" then
335 create_nether_portal(origin,origin,"z")
341 --2d virtual memory map creation (z axis)
342 elseif axis == "z" then
345 --index only direct neighbors
346 if x_failed == true and portal_failure == false and (math.abs(z)+math.abs(y) == 1) then
347 local i = vector.add(pos,vector.new(0,y,z))
349 local execute_collection = true
351 if n_index[i.x] and n_index[i.x][i.y] then
352 if n_index[i.x][i.y][i.z] then
353 execute_collection = false
357 if execute_collection == true then
358 --print(minetest.get_node(i).name)
360 if minetest.get_node(i).name == "air" then
361 if vector.distance(i,origin) < 50 then
362 --add data to both maps
363 if not n_index[i.x] then n_index[i.x] = {} end
364 if not n_index[i.x][i.y] then n_index[i.x][i.y] = {} end
365 n_index[i.x][i.y][i.z] = {nether_portal=1} --get_group(i,"redstone_power")}
366 --the data to the 3d array must be written to memory before this is executed
367 --or a stack overflow occurs!!!
368 --pass down info for activators
369 create_nether_portal(i,origin,"z")
371 --print("portal failed")
372 portal_failure = true
376 elseif minetest.get_node(i).name ~= "nether:obsidian" then
377 --print("portal failed")
378 portal_failure = true
388 --creates a nether portal in the nether
389 --this essentially makes it so you have to move 30 away from one portal to another otherwise it will travel to an existing portal
390 local nether_origin_pos = nil
391 local function spawn_portal_into_nether_callback(blockpos, action, calls_remaining, param)
392 if calls_remaining == 0 then
393 local portal_exists = minetest.find_node_near(nether_origin_pos, 30, {"nether:portal"})
395 if not portal_exists then
396 local min = vector.subtract(nether_origin_pos,30)
397 local max = vector.add(nether_origin_pos,30)
398 local platform = minetest.find_nodes_in_area_under_air(min, max, {"nether:netherrack","main:lava"})
400 if platform and next(platform) then
401 --print("setting the platform")
402 local platform_location = platform[math.random(1,table.getn(platform))]
404 minetest.place_schematic(platform_location, portalSchematic,"0",nil,true,"place_center_x, place_center_z")
406 --print("generate a portal within netherrack")
407 minetest.place_schematic(nether_origin_pos, portalSchematic,"0",nil,true,"place_center_x, place_center_z")
410 --print("portal exists, utilizing")
412 nether_origin_pos = nil
415 --creates nether portals in the overworld
416 local function spawn_portal_into_overworld_callback(blockpos, action, calls_remaining, param)
417 if calls_remaining == 0 then
418 local portal_exists = minetest.find_node_near(nether_origin_pos, 30, {"nether:portal"})
420 if not portal_exists then
421 local min = vector.subtract(nether_origin_pos,30)
422 local max = vector.add(nether_origin_pos,30)
423 local platform = minetest.find_nodes_in_area_under_air(min, max, {"main:stone","main:water","main:grass","main:sand","main:dirt"})
425 if platform and next(platform) then
426 --print("setting the platform")
427 local platform_location = platform[math.random(1,table.getn(platform))]
429 minetest.place_schematic(platform_location, portalSchematic,"0",nil,true,"place_center_x, place_center_z")
431 --print("generate a portal within overworld stone")
432 minetest.place_schematic(nether_origin_pos, portalSchematic,"0",nil,true,"place_center_x, place_center_z")
435 --print("portal exists, utilizing")
437 nether_origin_pos = nil
442 local function generate_nether_portal_in_nether(pos)
443 if pos.y > -10033 then
444 --center the location to the lava height
445 pos.y = -15000--+math.random(-30,30)
446 nether_origin_pos = pos
448 local min = vector.subtract(nether_origin_pos,30)
449 local max = vector.add(nether_origin_pos,30)
451 --force load the area
452 minetest.emerge_area(min, max, spawn_portal_into_nether_callback)
454 --center the location to the water height
455 pos.y = 0--+math.random(-30,30)
456 nether_origin_pos = pos
457 --prefer height for mountains
458 local min = vector.subtract(nether_origin_pos,vector.new(30,30,30))
459 local max = vector.add(nether_origin_pos,vector.new(30,120,30))
461 --force load the area
462 minetest.emerge_area(min, max, spawn_portal_into_overworld_callback)
467 --modify the map with the collected data
468 local function portal_modify_map(n_copy)
469 local sorted_table = {}
470 local created_portal = false
471 for x,datax in pairs(n_copy) do
472 for y,datay in pairs(datax) do
473 for z,index in pairs(datay) do
474 --try to create a return side nether portal
475 if created_portal == false then
476 created_portal = true
477 generate_nether_portal_in_nether(vector.new(x,y,z))
479 table.insert(sorted_table, vector.new(x,y,z))
483 minetest.bulk_set_node(sorted_table, {name="nether:portal"})
486 -------------------------------------------------------------------------------------------
487 --the teleporter parts - stored here for now so I can read from other functions
488 local teleporting_player = nil
489 local function teleport_to_overworld(blockpos, action, calls_remaining, param)
490 if calls_remaining == 0 then
491 local portal_exists = minetest.find_node_near(nether_origin_pos, 30, {"nether:portal"})
492 if portal_exists then
493 print(teleporting_player)
494 if teleporting_player then
495 teleporting_player:set_pos(portal_exists)
498 teleporting_player = nil
501 local function teleport_to_nether(blockpos, action, calls_remaining, param)
502 if calls_remaining == 0 then
503 local portal_exists = minetest.find_node_near(nether_origin_pos, 30, {"nether:portal"})
504 if portal_exists then
505 print(teleporting_player)
506 if teleporting_player then
507 teleporting_player:set_pos(portal_exists)
510 teleporting_player = nil
514 --this initializes all teleporter commands from the client
515 minetest.register_on_modchannel_message(function(channel_name, sender, message)
516 if channel_name == "nether_teleporters" then
517 local player = minetest.get_player_by_name(sender)
518 local pos = player:get_pos()
520 if pos.y > -10033 then
521 --center the location to the lava height
522 pos.y = -15000--+math.random(-30,30)
523 nether_origin_pos = pos
525 local min = vector.subtract(nether_origin_pos,30)
526 local max = vector.add(nether_origin_pos,30)
528 --force load the area
529 teleporting_player = player
530 minetest.emerge_area(min, max, teleport_to_nether)
532 --center the location to the water height
533 pos.y = 0--+math.random(-30,30)
534 nether_origin_pos = pos
535 --prefer height for mountains
536 local min = vector.subtract(nether_origin_pos,vector.new(30,30,30))
537 local max = vector.add(nether_origin_pos,vector.new(30,120,30))
539 --force load the area
540 teleporting_player = player
541 minetest.emerge_area(min, max, teleport_to_overworld)
545 -------------------------------------------------------------------------------------------
547 -------------------------------------------------------------------------------
549 local destroy_n_index = {}
550 local destroy_portal_failure = false
551 local destroy_x_failed = false
553 --this can be used globally to create nether portals from obsidian
554 function destroy_nether_portal(pos,origin,axis)
555 --create the origin node for stored memory
559 --3d virtual memory map creation (x axis)
563 --index only direct neighbors
564 if (math.abs(x)+math.abs(z)+math.abs(y) == 1) then
565 local i = vector.add(pos,vector.new(x,y,z))
567 local execute_collection = true
569 if destroy_n_index[i.x] and destroy_n_index[i.x][i.y] then
570 if destroy_n_index[i.x][i.y][i.z] then
571 execute_collection = false
575 if execute_collection == true then
576 --print(minetest.get_node(i).name)
578 if minetest.get_node(i).name == "nether:portal" then
579 if vector.distance(i,origin) < 50 then
580 --add data to both maps
581 if not destroy_n_index[i.x] then destroy_n_index[i.x] = {} end
582 if not destroy_n_index[i.x][i.y] then destroy_n_index[i.x][i.y] = {} end
583 destroy_n_index[i.x][i.y][i.z] = {nether_portal=1} --get_group(i,"redstone_power")}
584 --the data to the 3d array must be written to memory before this is executed
585 --or a stack overflow occurs!!!
586 --pass down info for activators
587 destroy_nether_portal(i,origin,"z")
597 --modify the map with the collected data
598 local function destroy_portal_modify_map(destroy_n_copy)
599 local destroy_sorted_table = {}
600 for x,datax in pairs(destroy_n_copy) do
601 for y,datay in pairs(datax) do
602 for z,index in pairs(datay) do
603 table.insert(destroy_sorted_table, vector.new(x,y,z))
607 minetest.bulk_set_node(destroy_sorted_table, {name="air"})
610 minetest.register_globalstep(function(dtime)
611 --if indexes exist then calculate redstone
612 if n_index and next(n_index) and portal_failure == false then
613 --create the old version to help with deactivation calculation
614 local n_copy = table.copy(n_index)
615 portal_modify_map(n_copy)
616 portal_failure = false
618 if x_failed == true then
621 if portal_failure == true then
622 portal_failure = false
624 --clear the index to avoid cpu looping wasting processing power
628 --if indexes exist then calculate redstone
629 if destroy_n_index and next(destroy_n_index) and destroy_portal_failure == false then
630 --create the old version to help with deactivation calculation
631 local destroy_n_copy = table.copy(destroy_n_index)
632 destroy_portal_modify_map(destroy_n_copy)
634 --clear the index to avoid cpu looping wasting processing power