mirror of
https://github.com/minetest-mods/nether.git
synced 2024-12-27 19:30:28 +01:00
rearrange code
eliminate need to assign extinguish_portal() to a local var, and group the helper functions together in the API section
This commit is contained in:
parent
e2666146ca
commit
835ad9686d
425
portal_api.lua
425
portal_api.lua
@ -218,10 +218,6 @@ local ignition_item_name
|
||||
local S = nether.get_translator
|
||||
local mod_storage = minetest.get_mod_storage()
|
||||
|
||||
-- this is a function that will be assigned to further down, allowing functions to use it
|
||||
-- that are defined before it is.
|
||||
local extinguish_portal
|
||||
|
||||
|
||||
local function get_timerPos_from_p1_and_p2(p1, p2)
|
||||
-- Pick a frame node for the portal's timer.
|
||||
@ -361,6 +357,108 @@ local function list_closest_portals(portal_definition, anchorPos, distance_limit
|
||||
end
|
||||
|
||||
|
||||
-- the timerNode is used to keep the metadata as that node already needs to be known any time a portal is stopped or run
|
||||
-- see also ambient_sound_stop()
|
||||
function ambient_sound_play(portal_definition, soundPos, timerNodeMeta)
|
||||
if portal_definition.sounds.ambient ~= nil then
|
||||
local soundLength = portal_definition.sounds.ambient.length
|
||||
if soundLength == nil then soundLength = 3 end
|
||||
local lastPlayed = timerNodeMeta:get_int("ambient_sound_last_played")
|
||||
|
||||
-- Using "os.time() % soundLength == 0" is lightweight but means delayed starts, so trying a stored lastPlayed
|
||||
if os.time() >= lastPlayed + soundLength then
|
||||
local soundHandle = minetest.sound_play(portal_definition.sounds.ambient, {pos = soundPos, max_hear_distance = 6})
|
||||
if timerNodeMeta ~= nil then
|
||||
timerNodeMeta:set_int("ambient_sound_handle", soundHandle)
|
||||
timerNodeMeta:set_int("ambient_sound_last_played", os.time())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- the timerNode is used to keep the metadata as that node already needs to be known any time a portal is stopped or run
|
||||
-- see also ambient_sound_play()
|
||||
function ambient_sound_stop(timerNodeMeta)
|
||||
if timerNodeMeta ~= nil then
|
||||
local soundHandle = timerNodeMeta:get_int("ambient_sound_handle")
|
||||
minetest.sound_stop(soundHandle)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- WARNING - this is invoked by on_destruct, so you can't assume there's an accesible node at pos
|
||||
function extinguish_portal(pos, node_name, frame_was_destroyed)
|
||||
|
||||
-- mesecons seems to invoke action_off() 6 times every time you place a block?
|
||||
if DEBUG then minetest.chat_send_all("extinguish_portal" .. minetest.pos_to_string(pos) .. " " .. node_name) end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local p1 = minetest.string_to_pos(meta:get_string("p1"))
|
||||
local p2 = minetest.string_to_pos(meta:get_string("p2"))
|
||||
local target = minetest.string_to_pos(meta:get_string("target"))
|
||||
if p1 == nil or p2 == nil then
|
||||
if DEBUG then minetest.chat_send_all(" no active portal found to extinguish") end
|
||||
return
|
||||
end
|
||||
|
||||
local portal_definition = get_portal_definition(node_name, p1, p2)
|
||||
if portal_definition == nil then
|
||||
minetest.log("error", "extinguish_portal() invoked on " .. node_name .. " but no registered portal is constructed from " .. node_name)
|
||||
return -- no portal frames are made from this type of node
|
||||
end
|
||||
|
||||
if portal_definition.sounds.extinguish ~= nil then
|
||||
minetest.sound_play(portal_definition.sounds.extinguish, {pos = p1})
|
||||
end
|
||||
|
||||
-- stop timer and ambient sound
|
||||
local timerPos = get_timerPos_from_p1_and_p2(p1, p2)
|
||||
minetest.get_node_timer(timerPos):stop()
|
||||
ambient_sound_stop(minetest.get_meta(timerPos))
|
||||
|
||||
-- update the ignition state in the portal location info
|
||||
local anchorPos, orientation = portal_definition.shape.get_anchorPos_and_orientation_from_p1_and_p2(p1, p2)
|
||||
if frame_was_destroyed then
|
||||
remove_portal_location_info(portal_definition.name, anchorPos)
|
||||
else
|
||||
store_portal_location_info(portal_definition.name, anchorPos, orientation, false)
|
||||
end
|
||||
|
||||
local frame_node_name = portal_definition.frame_node_name
|
||||
local wormhole_node_name = portal_definition.wormhole_node_name
|
||||
|
||||
|
||||
for x = p1.x, p2.x do
|
||||
for y = p1.y, p2.y do
|
||||
for z = p1.z, p2.z do
|
||||
local nn = minetest.get_node({x = x, y = y, z = z}).name
|
||||
if nn == frame_node_name or nn == wormhole_node_name then
|
||||
if nn == wormhole_node_name then
|
||||
minetest.remove_node({x = x, y = y, z = z})
|
||||
end
|
||||
local m = minetest.get_meta({x = x, y = y, z = z})
|
||||
m:set_string("p1", "")
|
||||
m:set_string("p2", "")
|
||||
m:set_string("target", "")
|
||||
m:set_string("frame_node_name", "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if target ~= nil then
|
||||
if DEBUG then minetest.chat_send_all(" attempting to also extinguish target with wormholePos " .. minetest.pos_to_string(target)) end
|
||||
extinguish_portal(target, node_name)
|
||||
end
|
||||
|
||||
if portal_definition.on_extinguish ~= nil then
|
||||
portal_definition.on_extinguish(portal_definition, anchorPos, orientation)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
-- Note: will extinguish any portal using the same nodes that are being set
|
||||
local function set_portal_metadata(portal_definition, anchorPos, orientation, destination_wormholePos, ignite)
|
||||
|
||||
@ -608,128 +706,6 @@ local function locate_or_build_portal(portal_definition, suggested_wormholePos,
|
||||
end
|
||||
|
||||
|
||||
-- use this when determining where to spawn a portal, to avoid overwriting player builds
|
||||
function nether.volume_is_natural(minp, maxp)
|
||||
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 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
|
||||
local id = data[vi] -- Existing node
|
||||
if id ~= c_air and id ~= c_ignore then -- These are natural
|
||||
local name = minetest.get_name_from_content_id(id)
|
||||
local nodedef = minetest.registered_nodes[name]
|
||||
if not nodedef.is_ground_content then
|
||||
-- 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) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
vi = vi + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
"The normal realm portal has a particular X, Z, it searches downwards for a suitable Y.
|
||||
It can't be placed in mid-air, and for performance the test for a suitable placement position cannot move downwards in
|
||||
1 node steps, instead it moves downwards in 16 node steps, so it will almost always be placed buried in solid nodes.
|
||||
|
||||
The portal cannot be placed in any volume that contains non-natural nodes (is_ground_content = false) to not grief
|
||||
player builds. This makes it even more likely the portal will be a little way underground.
|
||||
|
||||
The portal is placed with air nodes around it to create a space so it isn't embedded in stone.
|
||||
It is expected that the player has a pickaxe to dig their way out, this is highly likely if they have built a portal
|
||||
and are exploring the nether. The player will not be trapped.
|
||||
|
||||
Note that MC also often places portals embedded in stone.
|
||||
|
||||
The code could be [edit: has been] altered to first try to find a surface position, but if this surface position is
|
||||
unsuitable due to being near player builds, the portal will still move downwards into the ground, so this is
|
||||
unavoidable.
|
||||
|
||||
Any search for a suitable resting-on-surface or resting-on-cave-surface position will be somewhat complex, to avoid
|
||||
placement on a tiny floating island or narrow spike etc. which would be impractical or deadly to the player.
|
||||
|
||||
A portal room embedded underground is the safest and the most accessible for the player.
|
||||
|
||||
So i decided to start the placement position search at y = -16 as that, or a little below, is the most likely suitable
|
||||
position: Ground is almost always present there, it's below any lakes or seas, below most player builds.
|
||||
|
||||
Also, the search for non-natural nodes doesn't actually guarantee avoiding player builds, as a player build can be
|
||||
composed of only natural nodes (is_ground_content = true). So even more good reason to start the search a little way
|
||||
underground where player builds are more unlikely. Y = -16 seemed a reasonable compromise between safety and distance
|
||||
from surface.
|
||||
|
||||
Each placement position search has to search a volume of nodes for non-natural nodes, this is not lightweight, and many
|
||||
searches may happen if there a lot of underground player builds present. So the code has been written to avoid
|
||||
intensive procedures."
|
||||
-- https://github.com/minetest-mods/nether/issues/5#issuecomment-506983676
|
||||
]]
|
||||
function nether.find_surface_target_y(target_x, target_z, portal_name)
|
||||
|
||||
-- try to spawn on surface first
|
||||
if minetest.get_spawn_level ~= nil then -- older versions of Minetest don't have this
|
||||
local surface_level = minetest.get_spawn_level(target_x, target_z)
|
||||
if surface_level ~= nil then
|
||||
-- get_spawn_level() seems to err on the side of caution and sometimes spawn the player a
|
||||
-- block higher than the ground level.
|
||||
local shouldBeGroundPos = {x = target_x, y = surface_level - 1, z = target_z}
|
||||
local groundNode = minetest.get_node_or_nil(shouldBeGroundPos)
|
||||
if groundNode == nil then
|
||||
-- force the area to be loaded - it's going to be loaded anyway by volume_is_natural()
|
||||
minetest.get_voxel_manip():read_from_map(shouldBeGroundPos, shouldBeGroundPos)
|
||||
groundNode = minetest.get_node(shouldBeGroundPos)
|
||||
end
|
||||
if not groundNode.is_ground_content then
|
||||
surface_level = surface_level - 1
|
||||
end
|
||||
-- Check volume for non-natural nodes
|
||||
local minp = {x = target_x - 1, y = surface_level - 1, z = target_z - 2}
|
||||
local maxp = {x = target_x + 2, y = surface_level + 3, z = target_z + 2}
|
||||
if nether.volume_is_natural(minp, maxp) then
|
||||
return surface_level
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- fallback to underground search
|
||||
local start_y = -16
|
||||
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
|
||||
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
|
||||
return y
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return start_y - 256 -- Fallback
|
||||
end
|
||||
|
||||
|
||||
-- invoked when a player attempts to turn obsidian nodes into an open portal
|
||||
-- ignition_node_name is optional
|
||||
local function ignite_portal(ignition_pos, ignition_node_name)
|
||||
@ -788,106 +764,6 @@ local function ignite_portal(ignition_pos, ignition_node_name)
|
||||
end
|
||||
end
|
||||
|
||||
-- the timerNode is used to keep the metadata as that node already needs to be known any time a portal is stopped or run
|
||||
-- see also ambient_sound_stop()
|
||||
function ambient_sound_play(portal_definition, soundPos, timerNodeMeta)
|
||||
if portal_definition.sounds.ambient ~= nil then
|
||||
local soundLength = portal_definition.sounds.ambient.length
|
||||
if soundLength == nil then soundLength = 3 end
|
||||
local lastPlayed = timerNodeMeta:get_int("ambient_sound_last_played")
|
||||
|
||||
-- Using "os.time() % soundLength == 0" is lightweight but means delayed starts, so trying a stored lastPlayed
|
||||
if os.time() >= lastPlayed + soundLength then
|
||||
local soundHandle = minetest.sound_play(portal_definition.sounds.ambient, {pos = soundPos, max_hear_distance = 6})
|
||||
if timerNodeMeta ~= nil then
|
||||
timerNodeMeta:set_int("ambient_sound_handle", soundHandle)
|
||||
timerNodeMeta:set_int("ambient_sound_last_played", os.time())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- the timerNode is used to keep the metadata as that node already needs to be known any time a portal is stopped or run
|
||||
-- see also ambient_sound_play()
|
||||
function ambient_sound_stop(timerNodeMeta)
|
||||
if timerNodeMeta ~= nil then
|
||||
local soundHandle = timerNodeMeta:get_int("ambient_sound_handle")
|
||||
minetest.sound_stop(soundHandle)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- WARNING - this is invoked by on_destruct, so you can't assume there's an accesible node at pos
|
||||
extinguish_portal = function(pos, node_name, frame_was_destroyed) -- assigned rather than declared because extinguish_portal is already declared, for use by earlier functions in the file.
|
||||
|
||||
-- mesecons seems to invoke action_off() 6 times every time you place a block?
|
||||
if DEBUG then minetest.chat_send_all("extinguish_portal" .. minetest.pos_to_string(pos) .. " " .. node_name) end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local p1 = minetest.string_to_pos(meta:get_string("p1"))
|
||||
local p2 = minetest.string_to_pos(meta:get_string("p2"))
|
||||
local target = minetest.string_to_pos(meta:get_string("target"))
|
||||
if p1 == nil or p2 == nil then
|
||||
if DEBUG then minetest.chat_send_all(" no active portal found to extinguish") end
|
||||
return
|
||||
end
|
||||
|
||||
local portal_definition = get_portal_definition(node_name, p1, p2)
|
||||
if portal_definition == nil then
|
||||
minetest.log("error", "extinguish_portal() invoked on " .. node_name .. " but no registered portal is constructed from " .. node_name)
|
||||
return -- no portal frames are made from this type of node
|
||||
end
|
||||
|
||||
if portal_definition.sounds.extinguish ~= nil then
|
||||
minetest.sound_play(portal_definition.sounds.extinguish, {pos = p1})
|
||||
end
|
||||
|
||||
-- stop timer and ambient sound
|
||||
local timerPos = get_timerPos_from_p1_and_p2(p1, p2)
|
||||
minetest.get_node_timer(timerPos):stop()
|
||||
ambient_sound_stop(minetest.get_meta(timerPos))
|
||||
|
||||
-- update the ignition state in the portal location info
|
||||
local anchorPos, orientation = portal_definition.shape.get_anchorPos_and_orientation_from_p1_and_p2(p1, p2)
|
||||
if frame_was_destroyed then
|
||||
remove_portal_location_info(portal_definition.name, anchorPos)
|
||||
else
|
||||
store_portal_location_info(portal_definition.name, anchorPos, orientation, false)
|
||||
end
|
||||
|
||||
local frame_node_name = portal_definition.frame_node_name
|
||||
local wormhole_node_name = portal_definition.wormhole_node_name
|
||||
|
||||
|
||||
for x = p1.x, p2.x do
|
||||
for y = p1.y, p2.y do
|
||||
for z = p1.z, p2.z do
|
||||
local nn = minetest.get_node({x = x, y = y, z = z}).name
|
||||
if nn == frame_node_name or nn == wormhole_node_name then
|
||||
if nn == wormhole_node_name then
|
||||
minetest.remove_node({x = x, y = y, z = z})
|
||||
end
|
||||
local m = minetest.get_meta({x = x, y = y, z = z})
|
||||
m:set_string("p1", "")
|
||||
m:set_string("p2", "")
|
||||
m:set_string("target", "")
|
||||
m:set_string("frame_node_name", "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if target ~= nil then
|
||||
if DEBUG then minetest.chat_send_all(" attempting to also extinguish target with wormholePos " .. minetest.pos_to_string(target)) end
|
||||
extinguish_portal(target, node_name)
|
||||
end
|
||||
|
||||
if portal_definition.on_extinguish ~= nil then
|
||||
portal_definition.on_extinguish(portal_definition, anchorPos, orientation)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- invoked when a player is standing in a portal
|
||||
local function ensure_remote_portal_then_teleport(player, portal_definition, local_anchorPos, local_orientation, destination_wormholePos)
|
||||
|
||||
@ -917,6 +793,7 @@ local function ensure_remote_portal_then_teleport(player, portal_definition, loc
|
||||
|
||||
if dest_wormhole_node.name == portal_definition.wormhole_node_name then
|
||||
-- portal exists
|
||||
|
||||
local destination_orientation = get_orientation_from_param2(dest_wormhole_node.param2)
|
||||
local destination_anchorPos = portal_definition.shape.get_anchorPos_from_wormholePos(destination_wormholePos, destination_orientation)
|
||||
portal_definition.shape.disable_portal_trap(destination_anchorPos, destination_orientation)
|
||||
@ -957,9 +834,9 @@ local function ensure_remote_portal_then_teleport(player, portal_definition, loc
|
||||
portal_definition.on_player_teleported(portal_definition, player, playerPos, new_playerPos)
|
||||
end
|
||||
else
|
||||
-- no wormhole node at destination - destination portal either needs to be built or ignited
|
||||
-- A very rare edge-case that takes time to set up:
|
||||
-- If the destination portal is unlit and shares a node with a lit portal that is linked to this
|
||||
-- no wormhole node at destination - destination portal either needs to be built or ignited.
|
||||
-- Note: A very rare edge-case that is difficult to set up:
|
||||
-- If the destination portal is unlit and its frame shares a node with a lit portal that is linked to this
|
||||
-- portal (but has not been travelled through, thus not linking this portal back to it), then igniting
|
||||
-- the destination portal will extinguish the portal it's touching, which will extinguish this portal
|
||||
-- which will leave a confused player.
|
||||
@ -1230,7 +1107,6 @@ function test_shapedef_is_valid(shape_defintion)
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- check for mistakes people might make in portal definitions
|
||||
function test_portaldef_is_valid(portal_definition)
|
||||
|
||||
@ -1245,6 +1121,7 @@ function test_portaldef_is_valid(portal_definition)
|
||||
end
|
||||
|
||||
|
||||
-- convert portals made with old ABM version of nether mod to use the timer instead
|
||||
minetest.register_lbm({
|
||||
label = "Start portal timer",
|
||||
name = "nether:start_portal_timer",
|
||||
@ -1406,6 +1283,94 @@ function nether.register_portal_ignition_item(item_name)
|
||||
ignition_item_name = item_name
|
||||
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.
|
||||
function nether.volume_is_natural(minp, maxp)
|
||||
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 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
|
||||
local id = data[vi] -- Existing node
|
||||
if id ~= c_air and id ~= c_ignore then -- These are natural
|
||||
local name = minetest.get_name_from_content_id(id)
|
||||
local nodedef = minetest.registered_nodes[name]
|
||||
if not nodedef.is_ground_content then
|
||||
-- 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) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
vi = vi + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
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)
|
||||
|
||||
-- try to spawn on surface first
|
||||
if minetest.get_spawn_level ~= nil then -- older versions of Minetest don't have this
|
||||
local surface_level = minetest.get_spawn_level(target_x, target_z)
|
||||
if surface_level ~= nil then
|
||||
-- get_spawn_level() seems to err on the side of caution and sometimes spawn the player a
|
||||
-- block higher than the ground level.
|
||||
local shouldBeGroundPos = {x = target_x, y = surface_level - 1, z = target_z}
|
||||
local groundNode = minetest.get_node_or_nil(shouldBeGroundPos)
|
||||
if groundNode == nil then
|
||||
-- force the area to be loaded - it's going to be loaded anyway by volume_is_natural()
|
||||
minetest.get_voxel_manip():read_from_map(shouldBeGroundPos, shouldBeGroundPos)
|
||||
groundNode = minetest.get_node(shouldBeGroundPos)
|
||||
end
|
||||
if not groundNode.is_ground_content then
|
||||
surface_level = surface_level - 1
|
||||
end
|
||||
-- Check volume for non-natural nodes
|
||||
local minp = {x = target_x - 1, y = surface_level - 1, z = target_z - 2}
|
||||
local maxp = {x = target_x + 2, y = surface_level + 3, z = target_z + 2}
|
||||
if nether.volume_is_natural(minp, maxp) then
|
||||
return surface_level
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- fallback to underground search
|
||||
local start_y = -16
|
||||
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
|
||||
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
|
||||
return y
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return start_y - 256 -- Fallback
|
||||
end
|
||||
|
||||
|
||||
-- Returns the anchorPos, orientation of the nearest portal, or nil.
|
||||
-- A y_factor of 0 means y does not affect the distance_limit, a y_factor of 1 means y is included,
|
||||
-- and a y_factor of 2 would squash the search-sphere by a factor of 2 on the y-axis, etc.
|
||||
|
Loading…
Reference in New Issue
Block a user