mirror of
https://github.com/minetest-mods/nether.git
synced 2025-07-02 00:10:32 +02:00
Respect protected areas when spawning remote portals (#27)
* respect protected areas when spawning remote portals * Use portal_shape to determine area to check for natural/protected * Log warning on deprecated function call
This commit is contained in:
113
portal_api.lua
113
portal_api.lua
@ -1303,8 +1303,9 @@ end
|
||||
|
||||
|
||||
-- invoked when a player attempts to turn obsidian nodes into an open portal
|
||||
-- player_name is optional, allowing a player to spawn a remote portal in their own protected area
|
||||
-- ignition_node_name is optional
|
||||
local function ignite_portal(ignition_pos, ignition_node_name)
|
||||
local function ignite_portal(ignition_pos, player_name, ignition_node_name)
|
||||
|
||||
if ignition_node_name == nil then ignition_node_name = minetest.get_node(ignition_pos).name end
|
||||
if DEBUG then minetest.chat_send_all("IGNITE the " .. ignition_node_name .. " at " .. minetest.pos_to_string(ignition_pos)) end
|
||||
@ -1354,9 +1355,9 @@ local function ignite_portal(ignition_pos, ignition_node_name)
|
||||
|
||||
local destination_anchorPos, destination_orientation
|
||||
if portal_definition.is_within_realm(ignition_pos) then
|
||||
destination_anchorPos, destination_orientation = portal_definition.find_surface_anchorPos(anchorPos)
|
||||
destination_anchorPos, destination_orientation = portal_definition.find_surface_anchorPos(anchorPos, player_name or "")
|
||||
else
|
||||
destination_anchorPos, destination_orientation = portal_definition.find_realm_anchorPos(anchorPos)
|
||||
destination_anchorPos, destination_orientation = portal_definition.find_realm_anchorPos(anchorPos, player_name or "")
|
||||
end
|
||||
if DEBUG and destination_orientation == nil then minetest.chat_send_all("No destination_orientation given") end
|
||||
if destination_orientation == nil then destination_orientation = orientation end
|
||||
@ -1825,7 +1826,7 @@ function register_frame_node(frame_node_name)
|
||||
extended_node_def.mesecons = {effector = {
|
||||
action_on = function (pos, node)
|
||||
if DEBUG then minetest.chat_send_all("portal frame material: mesecons action ON") end
|
||||
ignite_portal(pos, node.name)
|
||||
ignite_portal(pos, nil, node.name)
|
||||
end,
|
||||
action_off = function (pos, node)
|
||||
if DEBUG then minetest.chat_send_all("portal frame material: mesecons action OFF") end
|
||||
@ -2081,7 +2082,7 @@ function nether.register_portal(name, portaldef)
|
||||
end
|
||||
|
||||
if portaldef.find_surface_anchorPos == nil then -- default to using find_surface_target_y()
|
||||
portaldef.find_surface_anchorPos = function(pos)
|
||||
portaldef.find_surface_anchorPos = function(pos, player_name)
|
||||
|
||||
local destination_pos = {x = pos.x, y = 0, z = pos.z}
|
||||
local existing_portal_location, existing_portal_orientation =
|
||||
@ -2089,7 +2090,7 @@ function nether.register_portal(name, portaldef)
|
||||
if existing_portal_location ~= nil then
|
||||
return existing_portal_location, existing_portal_orientation
|
||||
else
|
||||
destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, name)
|
||||
destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, name, player_name)
|
||||
return destination_pos
|
||||
end
|
||||
end
|
||||
@ -2153,10 +2154,10 @@ end
|
||||
function nether.register_portal_ignition_item(item_name, ignition_failure_sound)
|
||||
|
||||
minetest.override_item(item_name, {
|
||||
on_place = function(stack, _, pt)
|
||||
on_place = function(stack, placer, pt)
|
||||
local done = false
|
||||
if pt.under and nether.is_frame_node[minetest.get_node(pt.under).name] then
|
||||
done = ignite_portal(pt.under)
|
||||
done = ignite_portal(pt.under, placer:get_player_name())
|
||||
if done and not minetest.settings:get_bool("creative_mode") then
|
||||
stack:take_item()
|
||||
end
|
||||
@ -2175,22 +2176,22 @@ end
|
||||
|
||||
-- use this when determining where to spawn a portal, to avoid overwriting player builds
|
||||
-- It checks the area for any nodes that aren't ground or trees.
|
||||
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
|
||||
-- (Water also fails this test, unless it is unemerged)
|
||||
function nether.volume_is_natural(minp, maxp)
|
||||
function nether.volume_is_natural_and_unprotected(minp, maxp, player_name)
|
||||
|
||||
local c_air = minetest.get_content_id("air")
|
||||
local c_ignore = minetest.get_content_id("ignore")
|
||||
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local pos1 = {x = minp.x, y = minp.y, z = minp.z}
|
||||
local pos2 = {x = maxp.x, y = maxp.y, z = maxp.z}
|
||||
local emin, emax = vm:read_from_map(pos1, pos2)
|
||||
local emin, emax = vm:read_from_map(minp, maxp)
|
||||
local area = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
|
||||
local data = vm:get_data()
|
||||
|
||||
for z = pos1.z, pos2.z do
|
||||
for y = pos1.y, pos2.y do
|
||||
local vi = area:index(pos1.x, y, z)
|
||||
for x = pos1.x, pos2.x do
|
||||
for z = minp.z, maxp.z do
|
||||
for y = minp.y, maxp.y do
|
||||
local vi = area:index(minp.x, y, z)
|
||||
for x = minp.x, maxp.x do
|
||||
local id = data[vi] -- Existing node
|
||||
if DEBUG and id == nil then minetest.chat_send_all("nil block at index " .. vi) end
|
||||
if id ~= c_air and id ~= c_ignore and id ~= nil then -- checked for common natural or not emerged
|
||||
@ -2200,7 +2201,7 @@ function nether.volume_is_natural(minp, maxp)
|
||||
-- trees are natural but not "ground content"
|
||||
local node_groups = nodedef.groups
|
||||
if node_groups == nil or (node_groups.tree == nil and node_groups.leaves == nil and node_groups.leafdecay == nil) then
|
||||
if DEBUG then minetest.chat_send_all("volume_is_natural() found unnatural node " .. name) end
|
||||
if DEBUG then minetest.chat_send_all("volume_is_natural_and_unprotected() found unnatural node " .. name) end
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -2210,13 +2211,73 @@ function nether.volume_is_natural(minp, maxp)
|
||||
end
|
||||
end
|
||||
|
||||
if DEBUG then minetest.chat_send_all("Volume is natural") end
|
||||
if minetest.is_area_protected(minp, maxp, player_name or "") then
|
||||
if DEBUG then minetest.chat_send_all("Volume is protected " .. minetest.pos_to_string(minp) .. "-" .. minetest.pos_to_string(maxp)) end
|
||||
return false;
|
||||
end
|
||||
|
||||
if DEBUG then minetest.chat_send_all("Volume is natural and unprotected for player '" .. (player_name or "") .. "', " .. minetest.pos_to_string(minp) .. "-" .. minetest.pos_to_string(maxp)) end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Deprecated, use nether.volume_is_natural_and_unprotected() instead.
|
||||
function nether.volume_is_natural(minp, maxp)
|
||||
|
||||
if nether.deprecation_warning_volume_is_natural == nil then
|
||||
local stack = debug.traceback("", 2);
|
||||
local calling_func = (string.split(stack, "\n", false, 2, false)[2] or ""):trim()
|
||||
minetest.log("warning",
|
||||
"Deprecated function \"nether.volume_is_natural()\" invoked. Use \"nether.volume_is_natural_and_unprotected()\" instead. " ..
|
||||
calling_func)
|
||||
nether.deprecation_warning_volume_is_natural = true;
|
||||
end
|
||||
|
||||
return nether.volume_is_natural_and_unprotected(minp, maxp)
|
||||
end
|
||||
|
||||
-- Gets the volume that may be altered if a portal is placed at the anchor_pos
|
||||
-- orientation is optional, but specifying it will reduce the volume returned
|
||||
-- portal_name is optional, but specifying it will reduce the volume returned
|
||||
-- returns minp, maxp
|
||||
function nether.get_schematic_volume(anchor_pos, orientation, portal_name)
|
||||
|
||||
if orientation == nil then
|
||||
-- Return a volume large enough for any orientation
|
||||
local minp0, maxp0 = nether.get_schematic_volume(anchor_pos, 0, portal_name)
|
||||
local minp1, maxp1 = nether.get_schematic_volume(anchor_pos, 1, portal_name)
|
||||
|
||||
-- ToDo: If an asymmetric portal is used with an anchor not at the center of the
|
||||
-- schematic then we will also need to check orientations 3 and 4.
|
||||
-- (The currently existing portal-shapes are not affected)
|
||||
return
|
||||
{x = math.min(minp0.x, minp1.x), y = math.min(minp0.y, minp1.y), z = math.min(minp0.z, minp1.z)},
|
||||
{x = math.max(maxp0.x, maxp1.x), y = math.max(maxp0.y, maxp1.y), z = math.max(maxp0.z, maxp1.z)}
|
||||
end
|
||||
|
||||
-- Assume the largest possible portal shape unless we know it's a smaller one.
|
||||
local shape_defintion = nether.PortalShape_Circular
|
||||
if portal_name ~= nil and nether.registered_portals[portal_name] ~= nil then
|
||||
shape_defintion = nether.registered_portals[portal_name].shape
|
||||
end
|
||||
|
||||
local size = shape_defintion.schematic.size
|
||||
local minp = shape_defintion.get_schematicPos_from_anchorPos(anchor_pos, orientation);
|
||||
local maxp
|
||||
|
||||
if (orientation % 2) == 0 then
|
||||
maxp = {x = minp.x + size.x - 1, y = minp.y + size.y - 1, z = minp.z + size.z - 1}
|
||||
else
|
||||
maxp = {x = minp.x + size.z - 1, y = minp.y + size.y - 1, z = minp.z + size.x - 1}
|
||||
end
|
||||
return minp, maxp
|
||||
end
|
||||
|
||||
|
||||
-- Can be used when implementing custom find_surface_anchorPos() functions
|
||||
-- portal_name is optional, providing it allows existing portals on the surface to be reused.
|
||||
function nether.find_surface_target_y(target_x, target_z, portal_name)
|
||||
-- portal_name is optional, providing it allows existing portals on the surface to be reused, and
|
||||
-- a potentially smaller volume to be checked by volume_is_natural_and_unprotected().
|
||||
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
|
||||
function nether.find_surface_target_y(target_x, target_z, portal_name, player_name)
|
||||
|
||||
assert(target_x ~= nil and target_z ~= nil, "Arguments `target_x` and `target_z` cannot be nil when calling find_surface_target_y()")
|
||||
|
||||
@ -2246,18 +2307,22 @@ function nether.find_surface_target_y(target_x, target_z, portal_name)
|
||||
end
|
||||
end
|
||||
|
||||
local minp_schem, maxp_schem = nether.get_schematic_volume({x = target_x, y = 0, z = target_z}, nil, portal_name)
|
||||
local minp = {x = minp_schem.x, y = 0, z = minp_schem.z}
|
||||
local maxp = {x = maxp_schem.x, y = 0, z = maxp_schem.z}
|
||||
|
||||
for y = start_y, start_y - 256, -16 do
|
||||
-- Check volume for non-natural nodes
|
||||
local minp = {x = target_x - 1, y = y - 1, z = target_z - 2}
|
||||
local maxp = {x = target_x + 2, y = y + 3, z = target_z + 2}
|
||||
if nether.volume_is_natural(minp, maxp) then
|
||||
minp.y = minp_schem.y + y
|
||||
maxp.y = maxp_schem.y + y
|
||||
if nether.volume_is_natural_and_unprotected(minp, maxp, player_name) then
|
||||
return y
|
||||
elseif portal_name ~= nil and nether.registered_portals[portal_name] ~= nil then
|
||||
-- players have built here - don't grief.
|
||||
-- but reigniting existing portals in portal rooms is fine - desirable even.
|
||||
local anchorPos, orientation, is_ignited = is_within_portal_frame(nether.registered_portals[portal_name], {x = target_x, y = y, z = target_z})
|
||||
if anchorPos ~= nil then
|
||||
if DEBUG then minetest.chat_send_all("Volume_is_natural check failed, but a portal frame is here " .. minetest.pos_to_string(anchorPos) .. ", so this is still a good target y level") end
|
||||
if DEBUG then minetest.chat_send_all("volume_is_natural_and_unprotected check failed, but a portal frame is here " .. minetest.pos_to_string(anchorPos) .. ", so this is still a good target y level") end
|
||||
return y
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user