forked from minetest-mods/nether
Add PortalShape_Platform
and other work on the portal examples also documentation and fixing issue where apples prevented volume_is_natural() from returning true
This commit is contained in:
parent
b9e85582f9
commit
6752964c96
5
init.lua
5
init.lua
@ -31,13 +31,16 @@ nether.get_translator = S
|
|||||||
nether.DEPTH = -5000
|
nether.DEPTH = -5000
|
||||||
nether.FASTTRAVEL_FACTOR = 8 -- 10 could be better value for Minetest, since there's no sprint, but ex-Minecraft players will be mathing for 8
|
nether.FASTTRAVEL_FACTOR = 8 -- 10 could be better value for Minetest, since there's no sprint, but ex-Minecraft players will be mathing for 8
|
||||||
nether.PORTAL_BOOK_LOOT_WEIGHTING = 0.9 -- Likelyhood of finding the Book of Portals (guide) in dungeon chests. Set to 0 to disable.
|
nether.PORTAL_BOOK_LOOT_WEIGHTING = 0.9 -- Likelyhood of finding the Book of Portals (guide) in dungeon chests. Set to 0 to disable.
|
||||||
|
nether.ENABLE_EXAMPLE_PORTALS = false -- Enables extra portal types - examples of how to create your own portal types using the Nether's portal API. Once enabled, their shapes will be shown in the book of portals.
|
||||||
|
|
||||||
-- Load files
|
-- Load files
|
||||||
dofile(nether.path .. "/portal_api.lua")
|
dofile(nether.path .. "/portal_api.lua")
|
||||||
dofile(nether.path .. "/nodes.lua")
|
dofile(nether.path .. "/nodes.lua")
|
||||||
dofile(nether.path .. "/mapgen.lua")
|
dofile(nether.path .. "/mapgen.lua")
|
||||||
|
|
||||||
|
if nether.ENABLE_EXAMPLE_PORTALS then
|
||||||
|
dofile(nether.path .. "/portal_examples.lua")
|
||||||
|
end
|
||||||
|
|
||||||
-- Portals are ignited by right-clicking with a mese crystal fragment
|
-- Portals are ignited by right-clicking with a mese crystal fragment
|
||||||
nether.register_portal_ignition_item(
|
nether.register_portal_ignition_item(
|
||||||
|
182
portal_api.lua
182
portal_api.lua
@ -29,21 +29,25 @@ p1 & p2 p1 and p2 is the system used by earlier versions of the nether mod
|
|||||||
they define the bounding volume for the portal.
|
they define the bounding volume for the portal.
|
||||||
The value of p1 and p2 is kept in the metadata of every node in the portal
|
The value of p1 and p2 is kept in the metadata of every node in the portal
|
||||||
|
|
||||||
WormholePos The location of the node a portal's target is set to, and a player is teleported
|
WormholePos The location of the node that a portal's target is set to, and a player is teleported
|
||||||
to. It can also be used to test whether a portal is active.
|
to. It can also be used to test whether a portal is active.
|
||||||
|
|
||||||
AnchorPos Introduced by the portal_api, this should
|
AnchorPos Introduced by the portal_api. Coordinates for portals are normally given in terms of
|
||||||
Usually an orientation is required with an AnchorPos
|
the AnchorPos. The AnchorPos does not change with portal orientation - portals rotate
|
||||||
|
around the AnchorPos. Ideally an AnchorPos would be near the bottom center of a portal
|
||||||
|
shape, but this is not the case with PortalShape_Traditional to keep comptaibility with
|
||||||
|
earlier versions of the nether mod.
|
||||||
|
Usually an orientation is required with an AnchorPos.
|
||||||
|
|
||||||
TimerPos The portal_api replaces ABMs with a single node timer per portal, and the TimerPos is the
|
TimerPos The portal_api replaces ABMs with a single node timer per portal, and the TimerPos is the
|
||||||
node in which that timer is located. Extra metadata is also kept in the TimerNode.
|
node in which that timer is located. Extra metadata is also kept in the TimerPos node.
|
||||||
|
|
||||||
|
|
||||||
Portal shapes
|
Portal shapes
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
||||||
For the PortalShape_Traditional implementation, anchorPos, wormholdPos and TimerPos are defined
|
For the PortalShape_Traditional implementation, p1, p2, anchorPos, wormholdPos and TimerPos are defined
|
||||||
as follows:
|
as follows:
|
||||||
.
|
.
|
||||||
+--------+--------+--------+--------+
|
+--------+--------+--------+--------+
|
||||||
@ -59,8 +63,8 @@ Portal shapes
|
|||||||
| |Wormhole | |
|
| |Wormhole | |
|
||||||
| | Pos | |
|
| | Pos | |
|
||||||
+--------+--------+--------+--------+
|
+--------+--------+--------+--------+
|
||||||
AnchorPos| Node | | |
|
AnchorPos|TimerPos| | |
|
||||||
| p1 | Timer | | |
|
| p1 | | | |
|
||||||
+--------+--------+--------+--------+
|
+--------+--------+--------+--------+
|
||||||
|
|
||||||
+X/East or +Z/North ----->
|
+X/East or +Z/North ----->
|
||||||
@ -71,15 +75,15 @@ or vice-versa, however AnchorPos is in the bottom/south/west-corner to keep comp
|
|||||||
with earlier versions of nether mod (which only records portal corners p1 & p2 in the node
|
with earlier versions of nether mod (which only records portal corners p1 & p2 in the node
|
||||||
metadata).
|
metadata).
|
||||||
|
|
||||||
Orientation is 0 or 90, 0 meaning a portal that faces north/south - i.e. obsidian running
|
Orientation is yaw, either 0 or 90, 0 meaning a portal that faces north/south - i.e. obsidian
|
||||||
east/west.
|
running east/west.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
|
||||||
-- This object defines a portal's shape, segregating the shape logic code from portal behaviour code.
|
-- This object defines a portal's shape, segregating the shape logic code from portal behaviour code.
|
||||||
-- You can create a new "PortalShape" definition object which implements the same
|
-- You can create a new "PortalShape" definition object which implements the same
|
||||||
-- functions if you wish to register a custom shaped portal in register_portal(). Examples follow
|
-- functions if you wish to register a custom shaped portal in register_portal(). Examples of other
|
||||||
-- after PortalShape_Traditional.
|
-- shapes follow after PortalShape_Traditional.
|
||||||
-- Since it's symmetric, this PortalShape definition has only implemented orientations of 0 and 90
|
-- Since it's symmetric, this PortalShape definition has only implemented orientations of 0 and 90
|
||||||
nether.PortalShape_Traditional = {
|
nether.PortalShape_Traditional = {
|
||||||
name = "Traditional",
|
name = "Traditional",
|
||||||
@ -355,6 +359,92 @@ nether.PortalShape_Circular = {
|
|||||||
} -- End of PortalShape_Circular class
|
} -- End of PortalShape_Circular class
|
||||||
|
|
||||||
|
|
||||||
|
-- Example alternative PortalShape
|
||||||
|
-- This platform shape is symmetrical around the y-axis, so the orientation value never matters.
|
||||||
|
nether.PortalShape_Platform = {
|
||||||
|
name = "Platform",
|
||||||
|
size = vector.new(5, 2, 5), -- size of the portal, and not necessarily the size of the schematic,
|
||||||
|
-- which may clear area around the portal.
|
||||||
|
schematic_filename = nether.path .. "/schematics/nether_portal_platform.mts",
|
||||||
|
|
||||||
|
-- returns the coords for minetest.place_schematic() that will place the schematic on the anchorPos
|
||||||
|
get_schematicPos_from_anchorPos = function(anchorPos, orientation)
|
||||||
|
return {x = anchorPos.x - 2, y = anchorPos.y, z = anchorPos.z - 2}
|
||||||
|
end,
|
||||||
|
|
||||||
|
get_wormholePos_from_anchorPos = function(anchorPos, orientation)
|
||||||
|
-- wormholePos is the node above anchorPos
|
||||||
|
return {x = anchorPos.x, y = anchorPos.y + 1, z = anchorPos.z}
|
||||||
|
end,
|
||||||
|
|
||||||
|
get_anchorPos_from_wormholePos = function(wormholePos, orientation)
|
||||||
|
-- wormholePos is the node above anchorPos
|
||||||
|
return {x = wormholePos.x, y = wormholePos.y - 1, z = wormholePos.z}
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- p1 and p2 are used to keep maps compatible with earlier versions of this mod.
|
||||||
|
-- p1 is the bottom/west/south corner of the portal, and p2 is the opposite corner, together
|
||||||
|
-- they define the bounding volume for the portal.
|
||||||
|
get_p1_and_p2_from_anchorPos = function(self, anchorPos, orientation)
|
||||||
|
assert(self ~= nil and self.name == nether.PortalShape_Platform.name, "Must pass self as first argument, or use shape:func() instead of shape.func()")
|
||||||
|
local p1 = {x = anchorPos.x - 2, y = anchorPos.y, z = anchorPos.z - 2}
|
||||||
|
local p2 = {x = anchorPos.x + 2, y = anchorPos.y + 1, z = anchorPos.z + 2}
|
||||||
|
return p1, p2
|
||||||
|
end,
|
||||||
|
|
||||||
|
get_anchorPos_and_orientation_from_p1_and_p2 = function(p1, p2)
|
||||||
|
return {x= p1.x + 2, y = p1.y, z = p1.z + 2}, 0
|
||||||
|
end,
|
||||||
|
|
||||||
|
apply_func_to_frame_nodes = function(anchorPos, orientation, func)
|
||||||
|
local shortCircuited
|
||||||
|
local yPlus1 = anchorPos.y + 1
|
||||||
|
-- use short-circuiting of boolean evaluation to allow func() to cause an abort by returning true
|
||||||
|
shortCircuited =
|
||||||
|
func({x = anchorPos.x - 2, y = yPlus1, z = anchorPos.z - 1}) or func({x = anchorPos.x + 2, y = yPlus1, z = anchorPos.z - 1}) or
|
||||||
|
func({x = anchorPos.x - 2, y = yPlus1, z = anchorPos.z }) or func({x = anchorPos.x + 2, y = yPlus1, z = anchorPos.z }) or
|
||||||
|
func({x = anchorPos.x - 2, y = yPlus1, z = anchorPos.z + 1}) or func({x = anchorPos.x + 2, y = yPlus1, z = anchorPos.z + 1}) or
|
||||||
|
|
||||||
|
func({x = anchorPos.x - 1, y = yPlus1, z = anchorPos.z - 2}) or func({x = anchorPos.x - 1, y = yPlus1, z = anchorPos.z + 2}) or
|
||||||
|
func({x = anchorPos.x , y = yPlus1, z = anchorPos.z - 2}) or func({x = anchorPos.x , y = yPlus1, z = anchorPos.z + 2}) or
|
||||||
|
func({x = anchorPos.x + 1, y = yPlus1, z = anchorPos.z - 2}) or func({x = anchorPos.x + 1, y = yPlus1, z = anchorPos.z + 2}) or
|
||||||
|
|
||||||
|
func({x = anchorPos.x - 1, y = anchorPos.y, z = anchorPos.z - 1}) or
|
||||||
|
func({x = anchorPos.x - 1, y = anchorPos.y, z = anchorPos.z }) or
|
||||||
|
func({x = anchorPos.x - 1, y = anchorPos.y, z = anchorPos.z + 1}) or
|
||||||
|
func({x = anchorPos.x , y = anchorPos.y, z = anchorPos.z - 1}) or
|
||||||
|
func({x = anchorPos.x , y = anchorPos.y, z = anchorPos.z }) or
|
||||||
|
func({x = anchorPos.x , y = anchorPos.y, z = anchorPos.z + 1}) or
|
||||||
|
func({x = anchorPos.x + 1, y = anchorPos.y, z = anchorPos.z - 1}) or
|
||||||
|
func({x = anchorPos.x + 1, y = anchorPos.y, z = anchorPos.z }) or
|
||||||
|
func({x = anchorPos.x + 1, y = anchorPos.y, z = anchorPos.z + 1})
|
||||||
|
return not shortCircuited
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- returns true if function was applied to all wormhole nodes
|
||||||
|
apply_func_to_wormhole_nodes = function(anchorPos, orientation, func)
|
||||||
|
local shortCircuited
|
||||||
|
local yPlus1 = anchorPos.y + 1
|
||||||
|
-- use short-circuiting of boolean evaluation to allow func() to cause an abort by returning true
|
||||||
|
shortCircuited =
|
||||||
|
func({x = anchorPos.x - 1, y = yPlus1, z = anchorPos.z - 1}) or
|
||||||
|
func({x = anchorPos.x - 1, y = yPlus1, z = anchorPos.z }) or
|
||||||
|
func({x = anchorPos.x - 1, y = yPlus1, z = anchorPos.z + 1}) or
|
||||||
|
func({x = anchorPos.x , y = yPlus1, z = anchorPos.z - 1}) or
|
||||||
|
func({x = anchorPos.x , y = yPlus1, z = anchorPos.z }) or
|
||||||
|
func({x = anchorPos.x , y = yPlus1, z = anchorPos.z + 1}) or
|
||||||
|
func({x = anchorPos.x + 1, y = yPlus1, z = anchorPos.z - 1}) or
|
||||||
|
func({x = anchorPos.x + 1, y = yPlus1, z = anchorPos.z }) or
|
||||||
|
func({x = anchorPos.x + 1, y = yPlus1, z = anchorPos.z + 1})
|
||||||
|
return not shortCircuited
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Check for suffocation
|
||||||
|
disable_portal_trap = function(anchorPos, orientation)
|
||||||
|
|
||||||
|
-- Not implemented.
|
||||||
|
end
|
||||||
|
} -- End of PortalShape_Platform class
|
||||||
|
|
||||||
|
|
||||||
--====================================================--
|
--====================================================--
|
||||||
@ -397,27 +487,61 @@ local function get_timerPos_from_p1_and_p2(p1, p2)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- orientation is the rotation degrees passed to place_schematic: 0, 90, 180, or 270
|
-- orientation is the yaw rotation degrees passed to place_schematic: 0, 90, 180, or 270
|
||||||
-- color is a value from 0 to 7 corresponding to the color of pixels in nether_portals_palette.png
|
-- color is a value from 0 to 7 corresponding to the color of pixels in nether_portals_palette.png
|
||||||
local function get_param2_from_color_and_orientation(color, orientation)
|
-- portal_is_horizontal is a bool indicating whether the portal lies flat or stands vertically
|
||||||
|
local function get_colorfacedir_from_color_and_orientation(color, orientation, portal_is_horizontal)
|
||||||
assert(orientation, "no orientation passed")
|
assert(orientation, "no orientation passed")
|
||||||
|
|
||||||
|
local axis_direction, rotation
|
||||||
|
local dir = math.floor((orientation % 360) / 90 + 0.5)
|
||||||
|
|
||||||
|
-- if the portal is vertical then node axis direction will be +Y (up) and portal orientation
|
||||||
|
-- will set the node's rotation.
|
||||||
|
-- if the portal is horizontal then the node axis direction reflects the yaw orientation and
|
||||||
|
-- the node's rotation will be whatever's needed to keep the texture horizontal (either 0 or 1)
|
||||||
|
if portal_is_horizontal then
|
||||||
|
if dir == 0 then axis_direction = 1 end -- North
|
||||||
|
if dir == 1 then axis_direction = 3 end -- East
|
||||||
|
if dir == 2 then axis_direction = 2 end -- South
|
||||||
|
if dir == 3 then axis_direction = 4 end -- West
|
||||||
|
rotation = math.floor(axis_direction / 2); -- a rotation is only needed if axis_direction is east or west
|
||||||
|
else
|
||||||
|
axis_direction = 0 -- 0 is up, or +Y
|
||||||
|
rotation = dir
|
||||||
|
end
|
||||||
|
|
||||||
-- wormhole nodes have a paramtype2 of colorfacedir, which means the
|
-- wormhole nodes have a paramtype2 of colorfacedir, which means the
|
||||||
-- high 3 bits are palette, followed by 3 direction bits and 2 rotation bits.
|
-- high 3 bits are palette, followed by 3 direction bits and 2 rotation bits.
|
||||||
-- We set the palette bits and rotation
|
-- We set the palette bits and rotation
|
||||||
return (orientation / 90) + color * 32
|
return rotation + axis_direction * 4 + color * 32
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_orientation_from_param2(param2)
|
local function get_orientation_from_colorfacedir(param2)
|
||||||
-- Strip off the top 6 bits, unfortunately MT lua has no bitwise '&'
|
|
||||||
|
local axis_direction = 0
|
||||||
|
-- Strip off the top 6 bits to leave the 2 rotation bits, unfortunately MT lua has no bitwise '&'
|
||||||
-- (high 3 bits are palette, followed by 3 direction bits then 2 rotation bits)
|
-- (high 3 bits are palette, followed by 3 direction bits then 2 rotation bits)
|
||||||
if param2 >= 128 then param2 = param2 - 128 end
|
if param2 >= 128 then param2 = param2 - 128 end
|
||||||
if param2 >= 64 then param2 = param2 - 64 end
|
if param2 >= 64 then param2 = param2 - 64 end
|
||||||
if param2 >= 32 then param2 = param2 - 32 end
|
if param2 >= 32 then param2 = param2 - 32 end
|
||||||
if param2 >= 16 then param2 = param2 - 16 end
|
if param2 >= 16 then param2 = param2 - 16; axis_direction = axis_direction + 4 end
|
||||||
if param2 >= 8 then param2 = param2 - 8 end
|
if param2 >= 8 then param2 = param2 - 8; axis_direction = axis_direction + 2 end
|
||||||
|
if param2 >= 4 then param2 = param2 - 4; axis_direction = axis_direction + 1 end
|
||||||
|
|
||||||
|
-- if the portal is vertical then node axis direction will be +Y (up) and portal orientation
|
||||||
|
-- will set the node's rotation.
|
||||||
|
-- if the portal is horizontal then the node axis direction reflects the yaw orientation and
|
||||||
|
-- the node's rotation will be whatever's needed to keep the texture horizontal (either 0 or 1)
|
||||||
|
if axis_direction == 0 or axis_direction == 5 then
|
||||||
|
-- portal is vertical
|
||||||
return param2 * 90
|
return param2 * 90
|
||||||
|
else
|
||||||
|
if axis_direction == 1 then return 0 end
|
||||||
|
if axis_direction == 3 then return 90 end
|
||||||
|
if axis_direction == 2 then return 180 end
|
||||||
|
if axis_direction == 4 then return 270 end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Combining frame_node_name, p1, and p2 will always be enough to uniquely identify a portal_definition
|
-- Combining frame_node_name, p1, and p2 will always be enough to uniquely identify a portal_definition
|
||||||
@ -629,7 +753,7 @@ local function set_portal_metadata(portal_definition, anchorPos, orientation, de
|
|||||||
-- they define the bounding volume for the portal.
|
-- they define the bounding volume for the portal.
|
||||||
local p1, p2 = portal_definition.shape:get_p1_and_p2_from_anchorPos(anchorPos, orientation)
|
local p1, p2 = portal_definition.shape:get_p1_and_p2_from_anchorPos(anchorPos, orientation)
|
||||||
local p1_string, p2_string = minetest.pos_to_string(p1), minetest.pos_to_string(p2)
|
local p1_string, p2_string = minetest.pos_to_string(p1), minetest.pos_to_string(p2)
|
||||||
local param2 = get_param2_from_color_and_orientation(portal_definition.wormhole_node_color, orientation)
|
local param2 = get_colorfacedir_from_color_and_orientation(portal_definition.wormhole_node_color, orientation, portal_definition.wormhole_node_is_horizontal)
|
||||||
|
|
||||||
local update_aborted-- using closures to allow the updateFunc to return extra information - by setting this variable
|
local update_aborted-- using closures to allow the updateFunc to return extra information - by setting this variable
|
||||||
|
|
||||||
@ -793,7 +917,7 @@ local function build_portal(portal_definition, anchorPos, orientation, destinati
|
|||||||
-- set the param2 on wormhole nodes to ensure they are the right color
|
-- set the param2 on wormhole nodes to ensure they are the right color
|
||||||
local wormholeNode = {
|
local wormholeNode = {
|
||||||
name = portal_definition.wormhole_node_name,
|
name = portal_definition.wormhole_node_name,
|
||||||
param2 = get_param2_from_color_and_orientation(portal_definition.wormhole_node_color, orientation)
|
param2 = get_colorfacedir_from_color_and_orientation(portal_definition.wormhole_node_color, orientation, portal_definition.wormhole_node_is_horizontal)
|
||||||
}
|
}
|
||||||
portal_definition.shape.apply_func_to_wormhole_nodes(
|
portal_definition.shape.apply_func_to_wormhole_nodes(
|
||||||
anchorPos,
|
anchorPos,
|
||||||
@ -974,7 +1098,7 @@ local function ensure_remote_portal_then_teleport(player, portal_definition, loc
|
|||||||
if dest_wormhole_node.name == portal_definition.wormhole_node_name then
|
if dest_wormhole_node.name == portal_definition.wormhole_node_name then
|
||||||
-- portal exists
|
-- portal exists
|
||||||
|
|
||||||
local destination_orientation = get_orientation_from_param2(dest_wormhole_node.param2)
|
local destination_orientation = get_orientation_from_colorfacedir(dest_wormhole_node.param2)
|
||||||
local destination_anchorPos = portal_definition.shape.get_anchorPos_from_wormholePos(destination_wormholePos, destination_orientation)
|
local destination_anchorPos = portal_definition.shape.get_anchorPos_from_wormholePos(destination_wormholePos, destination_orientation)
|
||||||
portal_definition.shape.disable_portal_trap(destination_anchorPos, destination_orientation)
|
portal_definition.shape.disable_portal_trap(destination_anchorPos, destination_orientation)
|
||||||
|
|
||||||
@ -1420,6 +1544,7 @@ local portaldef_default = {
|
|||||||
shape = PortalShape_Traditional,
|
shape = PortalShape_Traditional,
|
||||||
wormhole_node_name = "nether:portal",
|
wormhole_node_name = "nether:portal",
|
||||||
wormhole_node_color = 0,
|
wormhole_node_color = 0,
|
||||||
|
wormhole_node_is_horizontal = false,
|
||||||
frame_node_name = "default:obsidian",
|
frame_node_name = "default:obsidian",
|
||||||
particle_texture = "nether_particle.png",
|
particle_texture = "nether_particle.png",
|
||||||
particle_texture_animation = nil,
|
particle_texture_animation = nil,
|
||||||
@ -1565,13 +1690,14 @@ function nether.volume_is_natural(minp, maxp)
|
|||||||
local vi = area:index(pos1.x, y, z)
|
local vi = area:index(pos1.x, y, z)
|
||||||
for x = pos1.x, pos2.x do
|
for x = pos1.x, pos2.x do
|
||||||
local id = data[vi] -- Existing node
|
local id = data[vi] -- Existing node
|
||||||
if id ~= c_air and id ~= c_ignore then -- These are natural
|
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 -- These are natural or not emerged
|
||||||
local name = minetest.get_name_from_content_id(id)
|
local name = minetest.get_name_from_content_id(id)
|
||||||
local nodedef = minetest.registered_nodes[name]
|
local nodedef = minetest.registered_nodes[name]
|
||||||
if not nodedef.is_ground_content then
|
if not nodedef.is_ground_content then
|
||||||
-- trees are natural but not "ground content"
|
-- trees are natural but not "ground content"
|
||||||
local node_groups = nodedef.groups
|
local node_groups = nodedef.groups
|
||||||
if node_groups == nil or (node_groups.tree == nil and node_groups.leaves == nil) then
|
if node_groups == nil or (node_groups.tree == nil and node_groups.leaves == nil and node_groups.leafdecay == nil) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1602,7 +1728,15 @@ function nether.find_surface_target_y(target_x, target_z, portal_name)
|
|||||||
groundNode = minetest.get_node(shouldBeGroundPos)
|
groundNode = minetest.get_node(shouldBeGroundPos)
|
||||||
end
|
end
|
||||||
if not groundNode.is_ground_content then
|
if not groundNode.is_ground_content then
|
||||||
|
if DEBUG then minetest.chat_send_all("find_surface_target_y dropped spawn_level by 1") end
|
||||||
surface_level = surface_level - 1
|
surface_level = surface_level - 1
|
||||||
|
|
||||||
|
shouldBeGroundPos.y = shouldBeGroundPos.y - 1
|
||||||
|
groundNode = minetest.get_node_or_nil(shouldBeGroundPos)
|
||||||
|
if groundNode ~= nil and not groundNode.is_ground_content then
|
||||||
|
if DEBUG then minetest.chat_send_all("find_surface_target_y dropped spawn_level by 2") end
|
||||||
|
surface_level = surface_level - 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
-- Check volume for non-natural nodes
|
-- Check volume for non-natural nodes
|
||||||
local minp = {x = target_x - 1, y = surface_level - 1, z = target_z - 2}
|
local minp = {x = target_x - 1, y = surface_level - 1, z = target_z - 2}
|
||||||
@ -1662,7 +1796,7 @@ function nether.find_nearest_working_portal(portal_name, anchorPos, distance_lim
|
|||||||
if portalFound then
|
if portalFound then
|
||||||
return portal_info.anchorPos, portal_info.orientation
|
return portal_info.anchorPos, portal_info.orientation
|
||||||
else
|
else
|
||||||
if DEBUG then minetest.chat_send_all("removing portal from mod_storage at " .. minetest.pos_to_string(portal_info.anchorPos) .. " orientation " .. portal_info.orientation) end
|
if DEBUG then minetest.chat_send_all("Portal wasn't found, removing portal from mod_storage at " .. minetest.pos_to_string(portal_info.anchorPos) .. " orientation " .. portal_info.orientation) end
|
||||||
-- The portal at that location must have been destroyed
|
-- The portal at that location must have been destroyed
|
||||||
remove_portal_location_info(portal_name, portal_info.anchorPos)
|
remove_portal_location_info(portal_name, portal_info.anchorPos)
|
||||||
end
|
end
|
||||||
|
@ -21,6 +21,12 @@ one kind of portal with the same frame material — such as obsidian — provide
|
|||||||
the size of the PortalShape is distinct from any other type of portal that is
|
the size of the PortalShape is distinct from any other type of portal that is
|
||||||
using the same node as its frame, and portal sizes remain small.
|
using the same node as its frame, and portal sizes remain small.
|
||||||
|
|
||||||
|
Stone is not a good choice for portal frame nodes as the Minetest engine may
|
||||||
|
convert it into terrain nodes if the biome-pass happens after the portal is
|
||||||
|
created. Similarly, avoid using nodes which may be replaced by ABMs etc. in the
|
||||||
|
game without the node's on_destruct being triggered.
|
||||||
|
|
||||||
|
|
||||||
Realms
|
Realms
|
||||||
------
|
------
|
||||||
|
|
||||||
@ -152,7 +158,7 @@ Used by `nether.register_portal`.
|
|||||||
find_realm_anchorPos = function(surface_anchorPos),
|
find_realm_anchorPos = function(surface_anchorPos),
|
||||||
-- Required. Return a position in the realm that a portal created at
|
-- Required. Return a position in the realm that a portal created at
|
||||||
-- surface_anchorPos will link to.
|
-- surface_anchorPos will link to.
|
||||||
-- Return an anchorPos or anchorPos, orientation
|
-- Return an anchorPos or (anchorPos, orientation)
|
||||||
-- If orientation is not specified then the orientation of the surface
|
-- If orientation is not specified then the orientation of the surface
|
||||||
-- portal will be used.
|
-- portal will be used.
|
||||||
-- If the location of an existing portal is returned then include the
|
-- If the location of an existing portal is returned then include the
|
||||||
@ -179,6 +185,6 @@ Used by `nether.register_portal`.
|
|||||||
on_ignite = function(portalDef, anochorPos, orientation)
|
on_ignite = function(portalDef, anochorPos, orientation)
|
||||||
-- invoked when a player or mesecon ignites a portal
|
-- invoked when a player or mesecon ignites a portal
|
||||||
on_created = function(portalDef, anochorPos, orientation)
|
on_created = function(portalDef, anochorPos, orientation)
|
||||||
-- invoked when a portal creates a remote twin, usually when a
|
-- invoked when a portal creates a remote twin, this is usually when
|
||||||
-- player travels through a portal for the first time.
|
-- a player travels through a portal for the first time.
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
Nether mod portal examples for Minetest
|
Nether mod portal examples for Minetest
|
||||||
|
|
||||||
To use this file, add the following line to init.lua:
|
These portal API examples are independent of the Nether.
|
||||||
dofile(nether.path .. "/portal_examples.lua")
|
To use this file, set nether.ENABLE_EXAMPLE_PORTALS to true in init.lua
|
||||||
|
|
||||||
|
|
||||||
Copyright (C) 2019 Treer
|
Copyright (C) 2019 Treer
|
||||||
@ -23,12 +23,20 @@
|
|||||||
|
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
|
-- Sets how far a Surface Portal will travel, measured in cells along the Moore curve,
|
||||||
|
-- which are about 117 nodes square each. Larger numbers will generally mean further distance
|
||||||
|
-- as-the-crow-flies, but for small adjustments this will not always be true due to the how
|
||||||
|
-- the Moore curve frequently doubles back upon itself.
|
||||||
|
local SURFACE_TRAVEL_DISTANCE = 15
|
||||||
|
|
||||||
|
|
||||||
local S = nether.get_translator
|
local S = nether.get_translator
|
||||||
|
|
||||||
nether.register_portal("floatlands_portal", {
|
nether.register_portal("floatlands_portal", {
|
||||||
shape = nether.PortalShape_Traditional,
|
shape = nether.PortalShape_Platform,
|
||||||
frame_node_name = "default:ice",
|
frame_node_name = "default:ice",
|
||||||
wormhole_node_color = 7, -- 2 is blue
|
wormhole_node_color = 7, -- 2 is blue
|
||||||
|
wormhole_node_is_horizontal = true, -- indicate the wormhole surface is horizontal
|
||||||
particle_texture = {
|
particle_texture = {
|
||||||
name = "nether_particle_anim1.png",
|
name = "nether_particle_anim1.png",
|
||||||
animation = {
|
animation = {
|
||||||
@ -97,12 +105,12 @@ Requiring 14 blocks of ice, but otherwise constructed the same as the portal to
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
-- These Moore Curve functions requred by circular_portal's find_surface_anchorPos() will
|
-- These Moore Curve functions requred by surface_portal's find_surface_anchorPos() will
|
||||||
-- be assigned later in this file.
|
-- be assigned later in this file.
|
||||||
local get_moore_distance -- will be function get_moore_distance(cell_count, x, y): integer
|
local get_moore_distance -- will be function get_moore_distance(cell_count, x, y): integer
|
||||||
local get_moore_coords -- will be function get_moore_coords(cell_count, distance): pos2d
|
local get_moore_coords -- will be function get_moore_coords(cell_count, distance): pos2d
|
||||||
|
|
||||||
nether.register_portal("circular_portal", {
|
nether.register_portal("surface_portal", {
|
||||||
shape = nether.PortalShape_Circular,
|
shape = nether.PortalShape_Circular,
|
||||||
frame_node_name = "default:cobble",
|
frame_node_name = "default:cobble",
|
||||||
wormhole_node_color = 4, -- 4 is cyan
|
wormhole_node_color = 4, -- 4 is cyan
|
||||||
@ -117,7 +125,7 @@ nether.register_portal("circular_portal", {
|
|||||||
└─╚═╤═╤═┼─┘
|
└─╚═╤═╤═┼─┘
|
||||||
└─┴─┴─┘
|
└─┴─┴─┘
|
||||||
|
|
||||||
|
These
|
||||||
]] .. "\u{25A9}"),
|
]] .. "\u{25A9}"),
|
||||||
|
|
||||||
is_within_realm = function(pos)
|
is_within_realm = function(pos)
|
||||||
@ -138,7 +146,6 @@ nether.register_portal("circular_portal", {
|
|||||||
-- surface (following a Moore curve) so will be using a different x and z to realm_anchorPos.
|
-- surface (following a Moore curve) so will be using a different x and z to realm_anchorPos.
|
||||||
|
|
||||||
local cellCount = 512
|
local cellCount = 512
|
||||||
local travelDistanceInCells = 10
|
|
||||||
local maxDistFromOrigin = 30000 -- the world edges are at X=30927, X=−30912, Z=30927 and Z=−30912
|
local maxDistFromOrigin = 30000 -- the world edges are at X=30927, X=−30912, Z=30927 and Z=−30912
|
||||||
|
|
||||||
-- clip realm_anchorPos to maxDistFromOrigin, and move the origin so that all values are positive
|
-- clip realm_anchorPos to maxDistFromOrigin, and move the origin so that all values are positive
|
||||||
@ -147,22 +154,25 @@ nether.register_portal("circular_portal", {
|
|||||||
|
|
||||||
local divisor = math.ceil(maxDistFromOrigin * 2 / cellCount)
|
local divisor = math.ceil(maxDistFromOrigin * 2 / cellCount)
|
||||||
local distance = get_moore_distance(cellCount, math.floor(x / divisor + 0.5), math.floor(z / divisor + 0.5))
|
local distance = get_moore_distance(cellCount, math.floor(x / divisor + 0.5), math.floor(z / divisor + 0.5))
|
||||||
local destination_distance = (distance + travelDistanceInCells) % (cellCount * cellCount)
|
local destination_distance = (distance + SURFACE_TRAVEL_DISTANCE) % (cellCount * cellCount)
|
||||||
local moore_pos = get_moore_coords(cellCount, destination_distance)
|
local moore_pos = get_moore_coords(cellCount, destination_distance)
|
||||||
|
|
||||||
-- deterministically look for a location where get_spawn_level() gives us a height
|
|
||||||
local target_x = moore_pos.x * divisor - maxDistFromOrigin
|
local target_x = moore_pos.x * divisor - maxDistFromOrigin
|
||||||
local target_z = moore_pos.y * divisor - maxDistFromOrigin
|
local target_z = moore_pos.y * divisor - maxDistFromOrigin
|
||||||
|
local adj_x, adj_z = 0, 0
|
||||||
|
|
||||||
|
if minetest.get_spawn_level ~= nil then -- older versions of Minetest don't have this
|
||||||
|
-- Deterministically look for a location in the cell where get_spawn_level() can give
|
||||||
|
-- us a surface height, since nether.find_surface_target_y() works much better when
|
||||||
|
-- it can use get_spawn_level()
|
||||||
local prng = PcgRandom( -- seed the prng so that all portals for these Moore Curve coords will use the same random location
|
local prng = PcgRandom( -- seed the prng so that all portals for these Moore Curve coords will use the same random location
|
||||||
moore_pos.x * 65732 +
|
moore_pos.x * 65732 +
|
||||||
moore_pos.y * 729 +
|
moore_pos.y * 729 +
|
||||||
minetest.get_mapgen_setting("seed") * 3
|
minetest.get_mapgen_setting("seed") * 3
|
||||||
)
|
)
|
||||||
|
|
||||||
local radius = divisor / 2 - 2
|
local radius = divisor / 2 - 5
|
||||||
local attemptLimit = 10
|
local attemptLimit = 10 -- how many attempts we'll make to find a good location
|
||||||
local adj_x, adj_z
|
|
||||||
for attempt = 1, attemptLimit do
|
for attempt = 1, attemptLimit do
|
||||||
adj_x = math.floor(prng:rand_normal_dist(-radius, radius, 2) + 0.5)
|
adj_x = math.floor(prng:rand_normal_dist(-radius, radius, 2) + 0.5)
|
||||||
adj_z = math.floor(prng:rand_normal_dist(-radius, radius, 2) + 0.5)
|
adj_z = math.floor(prng:rand_normal_dist(-radius, radius, 2) + 0.5)
|
||||||
@ -173,14 +183,15 @@ nether.register_portal("circular_portal", {
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local destination_pos = {x = target_x + adj_x, y = 0, z = target_z + adj_z}
|
local destination_pos = {x = target_x + adj_x, y = 0, z = target_z + adj_z}
|
||||||
-- a y_factor of 0 makes the search ignore the altitude of the portals
|
-- a y_factor of 0 makes the search ignore the altitude of the portals
|
||||||
local existing_portal_location, existing_portal_orientation = nether.find_nearest_working_portal("circular_portal", destination_pos, radius, 0)
|
local existing_portal_location, existing_portal_orientation = nether.find_nearest_working_portal("surface_portal", destination_pos, radius, 0)
|
||||||
if existing_portal_location ~= nil then
|
if existing_portal_location ~= nil then
|
||||||
return existing_portal_location, existing_portal_orientation
|
return existing_portal_location, existing_portal_orientation
|
||||||
else
|
else
|
||||||
destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, "circular_portal")
|
destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, "surface_portal")
|
||||||
return destination_pos
|
return destination_pos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -192,7 +203,7 @@ nether.register_portal("circular_portal", {
|
|||||||
-- Hilbert curve and Moore curve functions --
|
-- Hilbert curve and Moore curve functions --
|
||||||
--=========================================--
|
--=========================================--
|
||||||
|
|
||||||
-- These are space-filling curves, used by the circular_portal example as a way to determine where
|
-- These are space-filling curves, used by the surface_portal example as a way to determine where
|
||||||
-- to place portals. https://en.wikipedia.org/wiki/Moore_curve
|
-- to place portals. https://en.wikipedia.org/wiki/Moore_curve
|
||||||
|
|
||||||
|
|
||||||
|
BIN
schematics/nether_portal_platform.mts
Normal file
BIN
schematics/nether_portal_platform.mts
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user