Improve Nether Portals (#1315) (as a squash)

Remove Nether portal caches, https://git.minetest.land/MineClone2/MineClone2/issues/1210

Store all exits from Nether portals in quick-access table

Implement proper Nether portal search, using the table, https://git.minetest.land/MineClone2/MineClone2/issues/1055

Store Nether portal exits table in mod storage

Remove exits from table on Nether portal destruction

Align destination area to [map chunks 5x5x5](https://git.minetest.land/MineClone2/MineClone2/wiki/World-structure%3A-positions%2C-boundaries%2C-blocks%2C-chunks%2C-dimensions%2C-barriers-and-the-void) to avoid lots of ```emerge_area()``` calls

Support Nether roof, https://git.minetest.land/MineClone2/MineClone2/issues/1267

Implement better suitable place search, https://git.minetest.land/MineClone2/MineClone2/issues/1126

Implement object queue not to trigger the same search again

Avoid lava lakes, https://git.minetest.land/MineClone2/MineClone2/issues/1126

Add ```/spawnstruct nether_portal``` chat command
Co-Authored-By: kay27 <kay27@noreply.git.minetest.land>
Co-Committed-By: kay27 <kay27@noreply.git.minetest.land>
This commit is contained in:
kay27 2021-03-21 23:14:33 +00:00
parent 1f925b6c84
commit 03feb36558
7 changed files with 598 additions and 569 deletions

View file

@ -33,25 +33,26 @@ mcl_vars.MAP_BLOCKSIZE = math.max(1, core.MAP_BLOCKSIZE or 16)
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000) mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, core.MAX_MAP_GENERATION_LIMIT or 31000) mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, core.MAX_MAP_GENERATION_LIMIT or 31000)
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2) local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
local chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
local central_chunk_max_pos = central_chunk_min_pos + chunk_size_in_nodes - 1 local central_chunk_max_pos = central_chunk_min_pos + mcl_vars.chunk_size_in_nodes - 1
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) / mcl_vars.MAP_BLOCKSIZE) local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) / mcl_vars.MAP_BLOCKSIZE)
local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1 local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / mcl_vars.chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits. local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / mcl_vars.chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * mcl_vars.chunk_size_in_nodes
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * chunk_size_in_nodes mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
local function coordinate_to_block(x) local function coordinate_to_block(x)
return math.floor(x / mcl_vars.MAP_BLOCKSIZE) return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
end end
local function coordinate_to_chunk(x) local function coordinate_to_chunk(x)
return math.floor((coordinate_to_block(x) + central_chunk_offset) / mcl_vars.chunksize) return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
end end
function mcl_vars.pos_to_block(pos) function mcl_vars.pos_to_block(pos)
@ -70,7 +71,7 @@ function mcl_vars.pos_to_chunk(pos)
} }
end end
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / chunk_size_in_nodes) local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
local k_positive_z = k_positive * 2 local k_positive_z = k_positive * 2
local k_positive_y = k_positive_z * k_positive_z local k_positive_y = k_positive_z * k_positive_z

View file

@ -6,7 +6,7 @@ mcl_worlds = {}
function mcl_worlds.is_in_void(pos) function mcl_worlds.is_in_void(pos)
local void = local void =
not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or
(pos.y < mcl_vars.mg_nether_max and pos.y > mcl_vars.mg_nether_min) or (pos.y < mcl_vars.mg_nether_max+128 and pos.y > mcl_vars.mg_nether_min) or
(pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min)) (pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
local void_deadly = false local void_deadly = false
@ -15,11 +15,11 @@ function mcl_worlds.is_in_void(pos)
-- Overworld → Void → End → Void → Nether → Void -- Overworld → Void → End → Void → Nether → Void
if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then
void_deadly = pos.y < mcl_vars.mg_overworld_min - deadly_tolerance void_deadly = pos.y < mcl_vars.mg_overworld_min - deadly_tolerance
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max then elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max+128 then
-- The void between End and Nether. Like usual, but here, the void -- The void between End and Nether. Like usual, but here, the void
-- *above* the Nether also has a small tolerance area, so player -- *above* the Nether also has a small tolerance area, so player
-- can fly above the Nether without getting hurt instantly. -- can fly above the Nether without getting hurt instantly.
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max + deadly_tolerance) void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max+128 + deadly_tolerance)
elseif pos.y < mcl_vars.mg_nether_min then elseif pos.y < mcl_vars.mg_nether_min then
void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
end end
@ -35,7 +35,7 @@ end
function mcl_worlds.y_to_layer(y) function mcl_worlds.y_to_layer(y)
if y >= mcl_vars.mg_overworld_min then if y >= mcl_vars.mg_overworld_min then
return y - mcl_vars.mg_overworld_min, "overworld" return y - mcl_vars.mg_overworld_min, "overworld"
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max then elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
return y - mcl_vars.mg_nether_min, "nether" return y - mcl_vars.mg_nether_min, "nether"
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
return y - mcl_vars.mg_end_min, "end" return y - mcl_vars.mg_end_min, "end"
@ -73,7 +73,7 @@ end
-- Takes a position and returns true if this position can have Nether dust -- Takes a position and returns true if this position can have Nether dust
function mcl_worlds.has_dust(pos) function mcl_worlds.has_dust(pos)
-- Weather in the Overworld and the high part of the void below -- Weather in the Overworld and the high part of the void below
return pos.y <= mcl_vars.mg_nether_max + 64 and pos.y >= mcl_vars.mg_nether_min - 64 return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
end end
-- Takes a position (pos) and returns true if compasses are working here -- Takes a position (pos) and returns true if compasses are working here

View file

@ -2826,7 +2826,7 @@ local falling = function(self, pos)
end end
if mcl_portals ~= nil then if mcl_portals ~= nil then
if mcl_portals.nether_portal_cooloff[self.object] then if mcl_portals.nether_portal_cooloff(self.object) then
return false -- mob has teleported through Nether portal - it's 99% not falling return false -- mob has teleported through Nether portal - it's 99% not falling
end end
end end

View file

@ -1,4 +1,4 @@
name = mcl_portals name = mcl_portals
description = Adds buildable portals to the Nether and End dimensions. description = Adds buildable portals to the Nether and End dimensions.
depends = mcl_init, mcl_worlds, mcl_core, mcl_nether, mcl_end, mcl_particles, mcl_spawn depends = mcl_nether, mcl_end, mcl_particles, mcl_spawn
optional_depends = awards, doc optional_depends = awards, doc

File diff suppressed because it is too large Load diff

View file

@ -29,7 +29,7 @@ local function add_chunk(pos)
end end
prev = d prev = d
end end
chunks[#chunks] = {n, n} chunks[#chunks+1] = {n, n}
end end
function mcl_mapgen_core.is_generated(pos) function mcl_mapgen_core.is_generated(pos)
local n = mcl_vars.get_chunk_number(pos) -- unsigned int local n = mcl_vars.get_chunk_number(pos) -- unsigned int
@ -1790,6 +1790,8 @@ local generate_nether_decorations = function(minp, maxp, seed)
return return
end end
minetest.log("action", "[mcl_mapgen_core] Nether decorations " .. minetest.pos_to_string(minp) .. " ... " .. minetest.pos_to_string(maxp))
-- TODO: Generate everything based on Perlin noise instead of PseudoRandom -- TODO: Generate everything based on Perlin noise instead of PseudoRandom
local bpos local bpos
@ -1847,6 +1849,7 @@ local generate_nether_decorations = function(minp, maxp, seed)
end end
minetest.register_on_generated(function(minp, maxp, blockseed) minetest.register_on_generated(function(minp, maxp, blockseed)
minetest.log("action", "[mcl_mapgen_core] Generating chunk " .. minetest.pos_to_string(minp) .. " ... " .. minetest.pos_to_string(maxp))
add_chunk(minp) add_chunk(minp)
local p1, p2 = {x=minp.x, y=minp.y, z=minp.z}, {x=maxp.x, y=maxp.y, z=maxp.z} local p1, p2 = {x=minp.x, y=minp.y, z=minp.z}, {x=maxp.x, y=maxp.y, z=maxp.z}
if lvm > 0 then if lvm > 0 then
@ -2132,12 +2135,8 @@ local function basic(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
-- * Replace water with Nether lava. -- * Replace water with Nether lava.
-- * Replace stone, sand dirt in v6 so the Nether works in v6. -- * Replace stone, sand dirt in v6 so the Nether works in v6.
elseif minp.y <= mcl_vars.mg_nether_max and maxp.y >= mcl_vars.mg_nether_min then elseif minp.y <= mcl_vars.mg_nether_max and maxp.y >= mcl_vars.mg_nether_min then
local nodes
if mg_name == "v6" then if mg_name == "v6" then
nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:water_source", "mcl_core:stone", "mcl_core:sand", "mcl_core:dirt"}) local nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:water_source", "mcl_core:stone", "mcl_core:sand", "mcl_core:dirt"})
else
nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:water_source"})
end
for n=1, #nodes do for n=1, #nodes do
local p_pos = area:index(nodes[n].x, nodes[n].y, nodes[n].z) local p_pos = area:index(nodes[n].x, nodes[n].y, nodes[n].z)
if data[p_pos] == c_water then if data[p_pos] == c_water then
@ -2151,6 +2150,18 @@ local function basic(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
lvm_used = true lvm_used = true
end end
end end
else
minetest.emerge_area(minp, maxp, function(blockpos, action, calls_remaining, param)
if calls_remaining > 0 then return end
-- local nodes = minetest.find_nodes_in_area(param.minp, param.maxp, {"mcl_core:water_source"})
local nodes = minetest.find_nodes_in_area(param.minp, param.maxp, {"group:water"})
local sn=(mcl_observers and mcl_observers.swap_node) or minetest.swap_node
local l = {name="mcl_nether:nether_lava_source"}
for _, n in pairs(nodes) do
sn(n, l)
end
end, {minp=vector.new(minp), maxp=vector.new(maxp)})
end
-- End block fixes: -- End block fixes:
-- * Replace water with end stone or air (depending on height). -- * Replace water with end stone or air (depending on height).

View file

@ -534,7 +534,7 @@ end
-- Debug command -- Debug command
minetest.register_chatcommand("spawnstruct", { minetest.register_chatcommand("spawnstruct", {
params = "desert_temple | desert_well | igloo | witch_hut | boulder | ice_spike_small | ice_spike_large | fossil | end_exit_portal | end_portal_shrine | dungeon", params = "desert_temple | desert_well | igloo | witch_hut | boulder | ice_spike_small | ice_spike_large | fossil | end_exit_portal | end_portal_shrine | nether_portal | dungeon",
description = S("Generate a pre-defined structure near your position."), description = S("Generate a pre-defined structure near your position."),
privs = {debug = true}, privs = {debug = true},
func = function(name, param) func = function(name, param)
@ -570,6 +570,8 @@ minetest.register_chatcommand("spawnstruct", {
mcl_structures.generate_end_portal_shrine(pos, rot, pr) mcl_structures.generate_end_portal_shrine(pos, rot, pr)
elseif param == "dungeon" and mcl_dungeons and mcl_dungeons.spawn_dungeon then elseif param == "dungeon" and mcl_dungeons and mcl_dungeons.spawn_dungeon then
mcl_dungeons.spawn_dungeon(pos, rot, pr) mcl_dungeons.spawn_dungeon(pos, rot, pr)
elseif param == "nether_portal" and mcl_portals and mcl_portals.spawn_nether_portal then
mcl_portals.spawn_nether_portal(pos, rot, pr, name)
elseif param == "" then elseif param == "" then
message = S("Error: No structure type given. Please use “/spawnstruct <type>”.") message = S("Error: No structure type given. Please use “/spawnstruct <type>”.")
errord = true errord = true