mirror of
https://github.com/minetest-mods/nether.git
synced 2024-12-28 11:50:26 +01:00
Add example alternative shape PortalShape_Circular
Also fixes up a few bugs that only became apparent when not using the traditional portal shape
This commit is contained in:
parent
31cf6a9bc3
commit
d5a551b4cf
182
portal_api.lua
182
portal_api.lua
@ -20,11 +20,31 @@ nether.portals_palette = {
|
|||||||
|
|
||||||
--[[
|
--[[
|
||||||
|
|
||||||
|
Positions
|
||||||
|
=========
|
||||||
|
|
||||||
|
p1 & p2 p1 and p2 is the system used by earlier versions of the nether mod, which the portal_api
|
||||||
|
is forwards and backwards compatible with.
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
to. It can also be used to test whether a portal is active.
|
||||||
|
|
||||||
|
AnchorPos Introduced by the portal_api, this should
|
||||||
|
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
|
||||||
|
node in which that timer is located. Extra metadata is also kept in the TimerNode.
|
||||||
|
|
||||||
|
|
||||||
Portal shapes
|
Portal shapes
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
||||||
For this PortalShape_Traditional implementation, anchorPos and wormholdPos are defined as follows:
|
For the PortalShape_Traditional implementation, anchorPos, wormholdPos and TimerPos are defined
|
||||||
|
as follows:
|
||||||
.
|
.
|
||||||
+--------+--------+--------+--------+
|
+--------+--------+--------+--------+
|
||||||
| | Frame | |
|
| | Frame | |
|
||||||
@ -58,9 +78,11 @@ 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().
|
-- functions if you wish to register a custom shaped portal in register_portal(). Examples 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",
|
||||||
size = vector.new(4, 5, 1), -- size of the portal, and not necessarily the size of the schematic,
|
size = vector.new(4, 5, 1), -- size of the portal, and not necessarily the size of the schematic,
|
||||||
-- which may clear area around the portal.
|
-- which may clear area around the portal.
|
||||||
schematic_filename = nether.path .. "/schematics/nether_portal.mts",
|
schematic_filename = nether.path .. "/schematics/nether_portal.mts",
|
||||||
@ -98,6 +120,7 @@ nether.PortalShape_Traditional = {
|
|||||||
-- they define the bounding volume for the portal.
|
-- they define the bounding volume for the portal.
|
||||||
get_p1_and_p2_from_anchorPos = function(self, anchorPos, orientation)
|
get_p1_and_p2_from_anchorPos = function(self, anchorPos, orientation)
|
||||||
assert(orientation, "no orientation passed")
|
assert(orientation, "no orientation passed")
|
||||||
|
assert(self ~= nil and self.name == nether.PortalShape_Traditional.name, "Must pass self as first argument, or use shape:func() instead of shape.func()")
|
||||||
local p1 = anchorPos -- PortalShape_Traditional puts the anchorPos at p1 for backwards&forwards compatibility
|
local p1 = anchorPos -- PortalShape_Traditional puts the anchorPos at p1 for backwards&forwards compatibility
|
||||||
local p2
|
local p2
|
||||||
|
|
||||||
@ -120,6 +143,7 @@ nether.PortalShape_Traditional = {
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
-- returns true if function was applied to all frame nodes
|
||||||
apply_func_to_frame_nodes = function(anchorPos, orientation, func)
|
apply_func_to_frame_nodes = function(anchorPos, orientation, func)
|
||||||
-- a 4x5 portal is small enough that hardcoded positions is simpler that procedural code
|
-- a 4x5 portal is small enough that hardcoded positions is simpler that procedural code
|
||||||
local shortCircuited
|
local shortCircuited
|
||||||
@ -162,6 +186,7 @@ nether.PortalShape_Traditional = {
|
|||||||
return not shortCircuited
|
return not shortCircuited
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
-- returns true if function was applied to all wormhole nodes
|
||||||
apply_func_to_wormhole_nodes = function(anchorPos, orientation, func)
|
apply_func_to_wormhole_nodes = function(anchorPos, orientation, func)
|
||||||
local shortCircuited
|
local shortCircuited
|
||||||
if orientation == 0 then
|
if orientation == 0 then
|
||||||
@ -202,6 +227,136 @@ nether.PortalShape_Traditional = {
|
|||||||
} -- End of PortalShape_Traditional class
|
} -- End of PortalShape_Traditional class
|
||||||
|
|
||||||
|
|
||||||
|
-- Example alternative PortalShape
|
||||||
|
nether.PortalShape_Circular = {
|
||||||
|
name = "Circular",
|
||||||
|
size = vector.new(7, 7, 1), -- 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_circular.mts",
|
||||||
|
|
||||||
|
-- returns the coords for minetest.place_schematic() that will place the schematic on the anchorPos
|
||||||
|
get_schematicPos_from_anchorPos = function(anchorPos, orientation)
|
||||||
|
assert(orientation, "no orientation passed")
|
||||||
|
if orientation == 0 then
|
||||||
|
return {x = anchorPos.x - 3, y = anchorPos.y, z = anchorPos.z - 3}
|
||||||
|
else
|
||||||
|
return {x = anchorPos.x - 3, y = anchorPos.y, z = anchorPos.z - 3 }
|
||||||
|
end
|
||||||
|
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(orientation, "no orientation passed")
|
||||||
|
assert(self ~= nil and self.name == nether.PortalShape_Circular.name, "Must pass self as first argument, or use shape:func() instead of shape.func()")
|
||||||
|
local p1 = anchorPos -- PortalShape_Traditional puts the anchorPos at p1 for backwards&forwards compatibility
|
||||||
|
local p2
|
||||||
|
|
||||||
|
if orientation == 0 then
|
||||||
|
p1 = {x = anchorPos.x - 3, y = anchorPos.y, z = anchorPos.z }
|
||||||
|
p2 = {x = p1.x + self.size.x - 1, y = p1.y + self.size.y - 1, z = p1.z }
|
||||||
|
else
|
||||||
|
p1 = {x = anchorPos.x, y = anchorPos.y, z = anchorPos.z - 3 }
|
||||||
|
p2 = {x = p1.x, y = p1.y + self.size.y - 1, z = p1.z + self.size.x - 1}
|
||||||
|
end
|
||||||
|
return p1, p2
|
||||||
|
end,
|
||||||
|
|
||||||
|
get_anchorPos_and_orientation_from_p1_and_p2 = function(p1, p2)
|
||||||
|
if p1.z == p2.z then
|
||||||
|
return {x= p1.x + 3, y = p1.y, z = p1.z }, 0
|
||||||
|
elseif p1.x == p2.x then
|
||||||
|
return {x= p1.x, y = p1.y, z = p1.z + 3}, 90
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
apply_func_to_frame_nodes = function(anchorPos, orientation, func)
|
||||||
|
local shortCircuited
|
||||||
|
if orientation == 0 then
|
||||||
|
-- use short-circuiting of boolean evaluation to allow func() to cause an abort by returning true
|
||||||
|
shortCircuited =
|
||||||
|
func({x = anchorPos.x + 0, y = anchorPos.y + 0, z = anchorPos.z}) or
|
||||||
|
func({x = anchorPos.x + 1, y = anchorPos.y + 0, z = anchorPos.z}) or func({x = anchorPos.x - 1, y = anchorPos.y + 0, z = anchorPos.z}) or
|
||||||
|
func({x = anchorPos.x + 2, y = anchorPos.y + 1, z = anchorPos.z}) or func({x = anchorPos.x - 2, y = anchorPos.y + 1, z = anchorPos.z}) or
|
||||||
|
func({x = anchorPos.x + 3, y = anchorPos.y + 2, z = anchorPos.z}) or func({x = anchorPos.x - 3, y = anchorPos.y + 2, z = anchorPos.z}) or
|
||||||
|
func({x = anchorPos.x + 3, y = anchorPos.y + 3, z = anchorPos.z}) or func({x = anchorPos.x - 3, y = anchorPos.y + 3, z = anchorPos.z}) or
|
||||||
|
func({x = anchorPos.x + 3, y = anchorPos.y + 4, z = anchorPos.z}) or func({x = anchorPos.x - 3, y = anchorPos.y + 4, z = anchorPos.z}) or
|
||||||
|
func({x = anchorPos.x + 2, y = anchorPos.y + 5, z = anchorPos.z}) or func({x = anchorPos.x - 2, y = anchorPos.y + 5, z = anchorPos.z}) or
|
||||||
|
func({x = anchorPos.x + 1, y = anchorPos.y + 6, z = anchorPos.z}) or func({x = anchorPos.x - 1, y = anchorPos.y + 6, z = anchorPos.z}) or
|
||||||
|
func({x = anchorPos.x + 0, y = anchorPos.y + 6, z = anchorPos.z})
|
||||||
|
else
|
||||||
|
shortCircuited =
|
||||||
|
func({x = anchorPos.x, y = anchorPos.y + 0, z = anchorPos.z + 0}) or
|
||||||
|
func({x = anchorPos.x, y = anchorPos.y + 0, z = anchorPos.z + 1}) or func({x = anchorPos.x, y = anchorPos.y + 0, z = anchorPos.z - 1}) or
|
||||||
|
func({x = anchorPos.x, y = anchorPos.y + 1, z = anchorPos.z + 2}) or func({x = anchorPos.x, y = anchorPos.y + 1, z = anchorPos.z - 2}) or
|
||||||
|
func({x = anchorPos.x, y = anchorPos.y + 2, z = anchorPos.z + 3}) or func({x = anchorPos.x, y = anchorPos.y + 2, z = anchorPos.z - 3}) or
|
||||||
|
func({x = anchorPos.x, y = anchorPos.y + 3, z = anchorPos.z + 3}) or func({x = anchorPos.x, y = anchorPos.y + 3, z = anchorPos.z - 3}) or
|
||||||
|
func({x = anchorPos.x, y = anchorPos.y + 4, z = anchorPos.z + 3}) or func({x = anchorPos.x, y = anchorPos.y + 4, z = anchorPos.z - 3}) or
|
||||||
|
func({x = anchorPos.x, y = anchorPos.y + 5, z = anchorPos.z + 2}) or func({x = anchorPos.x, y = anchorPos.y + 5, z = anchorPos.z - 2}) or
|
||||||
|
func({x = anchorPos.x, y = anchorPos.y + 6, z = anchorPos.z + 1}) or func({x = anchorPos.x, y = anchorPos.y + 6, z = anchorPos.z - 1}) or
|
||||||
|
func({x = anchorPos.x, y = anchorPos.y + 6, z = anchorPos.z + 0})
|
||||||
|
end
|
||||||
|
return not shortCircuited
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- returns true if function was applied to all wormhole nodes
|
||||||
|
apply_func_to_wormhole_nodes = function(anchorPos, orientation, func)
|
||||||
|
local xRange = 2
|
||||||
|
local zRange = 0
|
||||||
|
if orientation ~= 0 then
|
||||||
|
xRange = 0
|
||||||
|
zRange = 2
|
||||||
|
end
|
||||||
|
|
||||||
|
local xEdge, yEdge, zEdge
|
||||||
|
local pos = {}
|
||||||
|
for x = -xRange, xRange do
|
||||||
|
pos.x = anchorPos.x + x
|
||||||
|
xEdge = x == -xRange or x == xRange
|
||||||
|
for z = -zRange, zRange do
|
||||||
|
zEdge = z == -zRange or z == zRange
|
||||||
|
pos.z = anchorPos.z + z
|
||||||
|
for y = 1, 5 do
|
||||||
|
yEdge = y == 1 or y == 5
|
||||||
|
if not (yEdge and xEdge and zEdge) then
|
||||||
|
pos.y = anchorPos.y + y
|
||||||
|
if func(pos) then
|
||||||
|
-- func() caused an abort by returning true
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Check for whether the portal is blocked in, and if so then provide a safe way
|
||||||
|
-- on one side for the player to step out of the portal. Suggest including a roof
|
||||||
|
-- incase the portal was blocked with lava flowing from above.
|
||||||
|
-- If portal can appear in mid-air then can also check for that and add a platform.
|
||||||
|
disable_portal_trap = function(anchorPos, orientation)
|
||||||
|
assert(orientation, "no orientation passed")
|
||||||
|
|
||||||
|
-- Not implemented.
|
||||||
|
end
|
||||||
|
} -- End of PortalShape_Circular class
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--====================================================--
|
--====================================================--
|
||||||
--======== End of PortalShape implementations ========--
|
--======== End of PortalShape implementations ========--
|
||||||
--====================================================--
|
--====================================================--
|
||||||
@ -383,6 +538,10 @@ function ambient_sound_stop(timerNodeMeta)
|
|||||||
if timerNodeMeta ~= nil then
|
if timerNodeMeta ~= nil then
|
||||||
local soundHandle = timerNodeMeta:get_int("ambient_sound_handle")
|
local soundHandle = timerNodeMeta:get_int("ambient_sound_handle")
|
||||||
minetest.sound_fade(soundHandle, -3, 0)
|
minetest.sound_fade(soundHandle, -3, 0)
|
||||||
|
|
||||||
|
-- clear the metadata
|
||||||
|
timerNodeMeta:set_string("ambient_sound_handle", "")
|
||||||
|
timerNodeMeta:set_string("ambient_sound_last_played", "")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -919,7 +1078,8 @@ function run_wormhole(timerPos, time_elapsed)
|
|||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local destination_wormholePos = minetest.string_to_pos(meta:get_string("target"))
|
local destination_wormholePos = minetest.string_to_pos(meta:get_string("target"))
|
||||||
local local_p1 = minetest.string_to_pos(meta:get_string("p1"))
|
local local_p1 = minetest.string_to_pos(meta:get_string("p1"))
|
||||||
if destination_wormholePos ~= nil and local_p1 ~= nil then
|
local local_p2 = minetest.string_to_pos(meta:get_string("p2"))
|
||||||
|
if destination_wormholePos ~= nil and local_p1 ~= nil and local_p2 ~= nil then
|
||||||
|
|
||||||
-- force emerge of target area
|
-- force emerge of target area
|
||||||
minetest.get_voxel_manip():read_from_map(destination_wormholePos, destination_wormholePos) -- force load
|
minetest.get_voxel_manip():read_from_map(destination_wormholePos, destination_wormholePos) -- force load
|
||||||
@ -927,14 +1087,14 @@ function run_wormhole(timerPos, time_elapsed)
|
|||||||
minetest.emerge_area(vector.subtract(destination_wormholePos, 4), vector.add(destination_wormholePos, 4))
|
minetest.emerge_area(vector.subtract(destination_wormholePos, 4), vector.add(destination_wormholePos, 4))
|
||||||
end
|
end
|
||||||
|
|
||||||
local local_orientation = get_orientation_from_param2(minetest.get_node(pos).param2)
|
local local_anchorPos, local_orientation = portal_definition.shape.get_anchorPos_and_orientation_from_p1_and_p2(local_p1, local_p2)
|
||||||
minetest.after(
|
minetest.after(
|
||||||
3, -- hopefully target area is emerged in 3 seconds
|
3, -- hopefully target area is emerged in 3 seconds
|
||||||
function()
|
function()
|
||||||
ensure_remote_portal_then_teleport(
|
ensure_remote_portal_then_teleport(
|
||||||
obj,
|
obj,
|
||||||
portal_definition,
|
portal_definition,
|
||||||
local_p1,
|
local_anchorPos,
|
||||||
local_orientation,
|
local_orientation,
|
||||||
destination_wormholePos
|
destination_wormholePos
|
||||||
)
|
)
|
||||||
@ -954,10 +1114,11 @@ function run_wormhole(timerPos, time_elapsed)
|
|||||||
end
|
end
|
||||||
if p1 ~= nil and p2 ~= nil then
|
if p1 ~= nil and p2 ~= nil then
|
||||||
-- figure out the portal shape so we know where the wormhole nodes will be located
|
-- figure out the portal shape so we know where the wormhole nodes will be located
|
||||||
|
local frame_node_name
|
||||||
if portal_name ~= nil and nether.registered_portals[portal_name] ~= nil then
|
if portal_name ~= nil and nether.registered_portals[portal_name] ~= nil then
|
||||||
portal_definition = nether.registered_portals[portal_name]
|
portal_definition = nether.registered_portals[portal_name]
|
||||||
else
|
else
|
||||||
local frame_node_name = minetest.get_node(timerPos).name -- timerPos should be a frame node if the shape is traditionalPortalShape
|
frame_node_name = minetest.get_node(timerPos).name -- timerPos should be a frame node if the shape is traditionalPortalShape
|
||||||
portal_definition = get_portal_definition(frame_node_name, p1, p2)
|
portal_definition = get_portal_definition(frame_node_name, p1, p2)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1163,10 +1324,15 @@ end
|
|||||||
|
|
||||||
-- check for mistakes people might make in custom shape definitions
|
-- check for mistakes people might make in custom shape definitions
|
||||||
function test_shapedef_is_valid(shape_defintion)
|
function test_shapedef_is_valid(shape_defintion)
|
||||||
assert(shape_defintion ~= nil, "shape definition cannot be nil")
|
assert(shape_defintion ~= nil, "shape definition cannot be nil")
|
||||||
|
assert(shape_defintion.name ~= nil, "shape definition must have a name")
|
||||||
|
|
||||||
local result = true
|
local result = true
|
||||||
|
|
||||||
|
local origin = vector.new()
|
||||||
|
local p1, p2 = shape_defintion:get_p1_and_p2_from_anchorPos(origin, 0)
|
||||||
|
assert(vector.equals(shape_defintion.size, vector.add(vector.subtract(p2, p1), 1)), "p1 and p2 of shape definition '" .. shape_defintion.name .. "' don't match shapeDef.size")
|
||||||
|
|
||||||
-- todo
|
-- todo
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -1258,7 +1424,7 @@ local portaldef_default = {
|
|||||||
sounds = {
|
sounds = {
|
||||||
ambient = {name = "nether_portal_ambient", gain = 0.6, length = 3},
|
ambient = {name = "nether_portal_ambient", gain = 0.6, length = 3},
|
||||||
ignite = {name = "nether_portal_ignite", gain = 0.5},
|
ignite = {name = "nether_portal_ignite", gain = 0.5},
|
||||||
extinguish = {name = "nether_portal_extinguish", gain = 0.3},
|
extinguish = {name = "nether_portal_extinguish", gain = 0.5},
|
||||||
teleport = {name = "nether_portal_teleport", gain = 0.3}
|
teleport = {name = "nether_portal_teleport", gain = 0.3}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ Portal code is more efficient when each type of portal uses a different type
|
|||||||
of node to build its frame out of, however it is possible to register more than
|
of node to build its frame out of, however it is possible to register more than
|
||||||
one kind of portal with the same frame material — such as obsidian — provided
|
one kind of portal with the same frame material — such as obsidian — provided
|
||||||
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.
|
using the same node as its frame, and portal sizes remain small.
|
||||||
|
|
||||||
|
|
||||||
Helper functions
|
Helper functions
|
||||||
|
BIN
schematics/nether_portal_circular.mts
Normal file
BIN
schematics/nether_portal_circular.mts
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user