mirror of
				https://github.com/minetest-mods/nether.git
				synced 2025-10-31 16:05:23 +01:00 
			
		
		
		
	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:
		
							
								
								
									
										5
									
								
								init.lua
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								init.lua
									
									
									
									
									
								
							| @@ -31,13 +31,16 @@ nether.get_translator = S | ||||
| 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.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 | ||||
| dofile(nether.path .. "/portal_api.lua") | ||||
| dofile(nether.path .. "/nodes.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 | ||||
| nether.register_portal_ignition_item( | ||||
|   | ||||
							
								
								
									
										204
									
								
								portal_api.lua
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								portal_api.lua
									
									
									
									
									
								
							| @@ -25,25 +25,29 @@ 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 | ||||
|               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  | ||||
| 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. | ||||
|  | ||||
| AnchorPos     Introduced by the portal_api, this should  | ||||
| 			  Usually an orientation is required with an AnchorPos | ||||
| AnchorPos     Introduced by the portal_api. Coordinates for portals are normally given in terms of  | ||||
|               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  | ||||
|               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 | ||||
| ============= | ||||
|  | ||||
|  | ||||
|   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: | ||||
|                                           . | ||||
|     +--------+--------+--------+--------+ | ||||
| @@ -59,8 +63,8 @@ Portal shapes | ||||
|     |        |Wormhole         |        | | ||||
|     |        |  Pos            |        | | ||||
|     +--------+--------+--------+--------+ | ||||
|     AnchorPos|  Node  |        |        | | ||||
|     |   p1   | Timer  |        |        | | ||||
|     AnchorPos|TimerPos|        |        | | ||||
|     |   p1   |        |        |        | | ||||
|     +--------+--------+--------+--------+ | ||||
|  | ||||
|     +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 | ||||
| metadata). | ||||
|  | ||||
| Orientation is 0 or 90, 0 meaning a portal that faces north/south - i.e. obsidian running | ||||
| east/west. | ||||
| Orientation is yaw, either 0 or 90, 0 meaning a portal that faces north/south - i.e. obsidian  | ||||
| running east/west. | ||||
| ]] | ||||
|  | ||||
|  | ||||
| -- 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 | ||||
| -- functions if you wish to register a custom shaped portal in register_portal(). Examples follow | ||||
| -- after PortalShape_Traditional. | ||||
| -- functions if you wish to register a custom shaped portal in register_portal(). Examples of other | ||||
| -- shapes follow after PortalShape_Traditional. | ||||
| -- Since it's symmetric, this PortalShape definition has only implemented orientations of 0 and 90 | ||||
| nether.PortalShape_Traditional = { | ||||
| 	name = "Traditional", | ||||
| @@ -355,6 +359,92 @@ nether.PortalShape_Circular = { | ||||
| } -- 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 | ||||
|  | ||||
| -- 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 | ||||
| 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") | ||||
|  | ||||
| 	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 | ||||
| 	-- high 3 bits are palette, followed by 3 direction bits and 2 rotation bits. | ||||
| 	-- We set the palette bits and rotation | ||||
| 	return (orientation / 90) + color * 32 | ||||
| 	return rotation + axis_direction * 4 + color * 32 | ||||
| end | ||||
|  | ||||
| local function get_orientation_from_param2(param2) | ||||
| 	-- Strip off the top 6 bits, unfortunately MT lua has no bitwise '&' | ||||
| local function get_orientation_from_colorfacedir(param2) | ||||
|  | ||||
| 	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) | ||||
| 	if param2 >= 128 then param2 = param2 - 128 end | ||||
| 	if param2 >=  64 then param2 = param2 -  64 end | ||||
| 	if param2 >=  32 then param2 = param2 -  32 end | ||||
| 	if param2 >=  16 then param2 = param2 -  16 end | ||||
| 	if param2 >=   8 then param2 = param2 -   8 end | ||||
| 	if param2 >=  16 then param2 = param2 -  16; axis_direction = axis_direction + 4 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 | ||||
|  | ||||
| 	return param2 * 90 | ||||
| 	-- 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 | ||||
| 	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 | ||||
|  | ||||
| -- 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. | ||||
| 	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 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 | ||||
|  | ||||
| @@ -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 | ||||
| 	local wormholeNode = { | ||||
| 		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( | ||||
| 		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 | ||||
| 			-- 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) | ||||
| 			portal_definition.shape.disable_portal_trap(destination_anchorPos, destination_orientation) | ||||
|  | ||||
| @@ -1417,13 +1541,14 @@ end) | ||||
|  | ||||
| -- The fallback defaults for registered portaldef tables | ||||
| local portaldef_default = { | ||||
| 	shape                      = PortalShape_Traditional, | ||||
| 	wormhole_node_name         = "nether:portal", | ||||
| 	wormhole_node_color        = 0, | ||||
| 	frame_node_name            = "default:obsidian", | ||||
| 	particle_texture           = "nether_particle.png", | ||||
| 	particle_texture_animation = nil, | ||||
| 	particle_texture_scale     = 1, | ||||
| 	shape                        = PortalShape_Traditional, | ||||
| 	wormhole_node_name           = "nether:portal", | ||||
| 	wormhole_node_color          = 0, | ||||
| 	wormhole_node_is_horizontal  = false, | ||||
| 	frame_node_name              = "default:obsidian", | ||||
| 	particle_texture             = "nether_particle.png", | ||||
| 	particle_texture_animation   = nil, | ||||
| 	particle_texture_scale       = 1, | ||||
| 	sounds = { | ||||
| 		ambient    = {name = "nether_portal_ambient",    gain = 0.6, length = 3}, | ||||
| 		ignite     = {name = "nether_portal_ignite",     gain = 0.7}, | ||||
| @@ -1565,13 +1690,14 @@ function nether.volume_is_natural(minp, maxp) | ||||
| 		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 | ||||
| 			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 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 | ||||
| 					if node_groups == nil or (node_groups.tree == nil and node_groups.leaves == nil and node_groups.leafdecay == nil) then | ||||
| 						return false | ||||
| 					end | ||||
| 				end | ||||
| @@ -1602,7 +1728,15 @@ function nether.find_surface_target_y(target_x, target_z, portal_name) | ||||
| 				groundNode = minetest.get_node(shouldBeGroundPos) | ||||
| 			end | ||||
| 			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 | ||||
|  | ||||
| 				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 | ||||
| 			-- Check volume for non-natural nodes | ||||
| 			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 | ||||
| 			return portal_info.anchorPos, portal_info.orientation | ||||
| 		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 | ||||
| 			remove_portal_location_info(portal_name, portal_info.anchorPos) | ||||
| 		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 | ||||
| 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 | ||||
| ------ | ||||
|  | ||||
| @@ -152,7 +158,7 @@ Used by `nether.register_portal`. | ||||
|         find_realm_anchorPos = function(surface_anchorPos), | ||||
|         -- Required. Return a position in the realm that a portal created at | ||||
|         -- 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 | ||||
|         -- portal will be used. | ||||
|         -- 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) | ||||
|         -- invoked when a player or mesecon ignites a portal | ||||
|         on_created           = function(portalDef, anochorPos, orientation) | ||||
|         -- invoked when a portal creates a remote twin, usually when a | ||||
|         -- player travels through a portal for the first time. | ||||
|         -- invoked when a portal creates a remote twin, this is usually when | ||||
|         -- a player travels through a portal for the first time. | ||||
|     } | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
|  | ||||
|   Nether mod portal examples for Minetest | ||||
|  | ||||
|   To use this file, add the following line to init.lua: | ||||
|     dofile(nether.path .. "/portal_examples.lua") | ||||
|   These portal API examples are independent of the Nether.  | ||||
|   To use this file, set nether.ENABLE_EXAMPLE_PORTALS to true in init.lua | ||||
|  | ||||
|  | ||||
|   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 | ||||
|  | ||||
| nether.register_portal("floatlands_portal", { | ||||
| 	shape               = nether.PortalShape_Traditional, | ||||
| 	shape               = nether.PortalShape_Platform, | ||||
| 	frame_node_name     = "default:ice", | ||||
| 	wormhole_node_color = 7, -- 2 is blue | ||||
| 	wormhole_node_is_horizontal = true, -- indicate the wormhole surface is horizontal | ||||
| 	particle_texture    = { | ||||
| 		name      = "nether_particle_anim1.png", | ||||
| 		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. | ||||
| 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 | ||||
|  | ||||
| nether.register_portal("circular_portal", { | ||||
| nether.register_portal("surface_portal", { | ||||
| 	shape               = nether.PortalShape_Circular, | ||||
| 	frame_node_name     = "default:cobble", | ||||
| 	wormhole_node_color = 4, -- 4 is cyan | ||||
| @@ -117,7 +125,7 @@ nether.register_portal("circular_portal", { | ||||
| 	      └─╚═╤═╤═┼─┘ | ||||
| 	            └─┴─┴─┘ | ||||
|  | ||||
|  | ||||
| These | ||||
| ]] .. "\u{25A9}"), | ||||
|  | ||||
| 	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.  | ||||
|  | ||||
| 		local cellCount = 512 | ||||
| 		local travelDistanceInCells = 10 | ||||
| 		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  | ||||
| @@ -147,40 +154,44 @@ nether.register_portal("circular_portal", { | ||||
|  | ||||
| 		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 destination_distance = (distance + travelDistanceInCells) % (cellCount * cellCount) | ||||
| 		local destination_distance = (distance + SURFACE_TRAVEL_DISTANCE) % (cellCount * cellCount) | ||||
| 		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_z = moore_pos.y * divisor - maxDistFromOrigin | ||||
| 		local adj_x, adj_z = 0, 0 | ||||
|  | ||||
| 		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.y * 729   + | ||||
| 			minetest.get_mapgen_setting("seed") * 3 | ||||
| 		) | ||||
| 		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 | ||||
| 				moore_pos.x * 65732 + | ||||
| 				moore_pos.y * 729   + | ||||
| 				minetest.get_mapgen_setting("seed") * 3 | ||||
| 			) | ||||
|  | ||||
| 		local radius = divisor / 2 - 2 | ||||
| 		local attemptLimit = 10 | ||||
| 		local adj_x, adj_z | ||||
| 		for attempt = 1, attemptLimit do | ||||
| 			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) | ||||
| 			minetest.chat_send_all(attempt .. ": x " .. target_x + adj_x .. ", z " .. target_z + adj_z) | ||||
| 			if minetest.get_spawn_level(target_x + adj_x, target_z + adj_z)	~= nil then | ||||
| 				-- found a location which will be at ground level (unless a player has built there) | ||||
| 				minetest.chat_send_all("x " .. target_x + adj_x .. ", z " .. target_z + adj_z .. " is suitable") | ||||
| 				break | ||||
| 			local radius = divisor / 2 - 5 | ||||
| 			local attemptLimit = 10 -- how many attempts we'll make to find a good location | ||||
| 			for attempt = 1, attemptLimit do | ||||
| 				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) | ||||
| 				minetest.chat_send_all(attempt .. ": x " .. target_x + adj_x .. ", z " .. target_z + adj_z) | ||||
| 				if minetest.get_spawn_level(target_x + adj_x, target_z + adj_z)	~= nil then | ||||
| 					-- found a location which will be at ground level (unless a player has built there) | ||||
| 					minetest.chat_send_all("x " .. target_x + adj_x .. ", z " .. target_z + adj_z .. " is suitable") | ||||
| 					break | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
|  | ||||
| 		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 | ||||
| 		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 | ||||
| 			return existing_portal_location, existing_portal_orientation | ||||
| 		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 | ||||
| 		end | ||||
| 	end | ||||
| @@ -192,7 +203,7 @@ nether.register_portal("circular_portal", { | ||||
| -- 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 | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								schematics/nether_portal_platform.mts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								schematics/nether_portal_platform.mts
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user