74 Commits

Author SHA1 Message Date
3577fd1f5e Merge remote-tracking branch 'upstream/master' into nalc-1.2-dev 2020-06-20 15:53:12 +02:00
5cb9e5fb27 Merge pull request #24 from Treer/bugfix/OutOfMemory
Fix Out Of Memory issue
2020-06-18 00:47:03 +10:00
ab4a031c1c Fix Out Of Memory issue
Also another naming convention update - it's been get_3d_map_flat() sin Jan 2018.
2020-06-18 00:41:07 +10:00
9ab325fa8c Merge remote-tracking branch 'upstream/master' into nalc-1.2-dev 2020-06-14 17:04:59 +02:00
aac3ea6719 Merge pull request #13 from Treer/feature/biome_mapgen
Switch mapgen to using biomes (still with backwards compatibility)
2020-06-08 21:05:16 +10:00
f4255f5d1f Add biomes-based implementation of the mapgen, and decorations
Prevents the c++/native mapgen from placing ores or decorations (when useBiomes is true).
The biomes-based implementation is faster, more deterministic, and keeps its dungeons, but requires MT 5.1 features to work.

Also
 * Glowstone & Netherrack stalactites
 * Include the new decorations in mapgen_nobiomes
 * Decorate dungeons - Add windows and glowstone "chandeliers" to some rooms
 * Configurable Nether floor and ceiling depths
2020-06-08 21:00:58 +10:00
94222d44e0 Preserve original mapgen as a "nobiomes" version
For v6 mapgen which doesn't support biomes, or MT versions prior to 5.1 which don't support the necessary biome features
2020-06-05 19:36:30 +10:00
86105b4eb8 fix lighting bug in mapgen
Removes hard black edges appearing at emerge boundaries.
2020-06-05 19:32:14 +10:00
0e26770830 Merge pull request #18 from Treer/feature/portals-api-facedir-support
Add facedir support to Portals API
2020-06-01 22:08:15 +10:00
e5fbc2486b Allow a colorfacedir color to be specified for a portal's frame node
A color could also be specified via param2 in a portal schematic.
2020-05-31 19:48:26 +10:00
608d692401 Fix logic bug in parsing of nether_realm_enabled
The second argument of minetest.settings:get_bool is the default value (for when the configuration value is not set). Remove the superfluous `or nether.NETHER_REALM_ENABLED` which rendered the config option useless, because it always forced the variable to true.
2020-05-31 09:40:23 +02:00
f7ebd78614 New/extra portal animation
(new animation by Extex101)
The original texture is now "nether_portal_alt.png", and used to provide a "nether:portal_alt" node, which is used/demoed by the Surface-portal as the original animation still has a "it's full of stars" charm coming out when used for lighter coloured portals.
2020-05-31 09:31:16 +02:00
9e3d5bf997 Corrige crash au démarrage si moreblocks chargé 2020-05-08 20:15:12 +02:00
c5ef9136ec Modifie la profondeur du Nether à -25000 2020-03-06 12:04:32 +01:00
e113db1478 Add facedir support to Portals API
Portal shematics include facedir information for when new portals are spawned using frame nodes that are facedir or colorfacedir
2020-03-04 00:31:33 +11:00
7939161535 API improvements
Improvements found while using the portals API in another mod (cloudlands):
* remote_portal_checkup() will check the whole portal and frame for mapgen overwrites.
* Allow find_realm_anchorPos() to return nil if no realm location could be found (portal will fail to ignite).
* Allow create_book_of_portals() to be invoked [indirectly] by other mods.

Also lowers nether_book_close.png to be 4bpp
2020-02-15 10:55:37 +01:00
32e5d818e8 Use lua schematics instead of .mts files when building portals
Removes the need for the get_malleated_schematic_filename hack
2020-02-15 10:55:37 +01:00
aa2bfe9472 prevent obscure portal behaviour bug
If a portal is being ignited and portal frame near-but-not-at its destination was found that was active and already linked back to the local portal, it would cause the local portal to be extinguished. (Required the remote portal to somehow be in a different state from the portal it was linking to)
2020-02-15 10:55:37 +01:00
4c3545f17a drop portal shape images to 2bit
Save some bits. Unfortunately PNG can't do 3bit-indexed images :(
2020-02-15 10:55:37 +01:00
39d655b4c8 Add Help-modpack support
https://forum.minetest.net/viewtopic.php?t=15912
Don't place Book of Portals in dungeon chests if its contents can be written in the help modpack Encyclopedia instead, and don't add Book of Portals to dungeon chests if Nether portals are the only type of portal - A Nether mod doesn't need a Book of Portals if it's only being used to provide a Nether portal.
2020-02-15 10:55:37 +01:00
36d1f6f573 Images instead of ASCII diagrams in Book of Portals 2020-02-15 10:55:37 +01:00
0618325710 API exposes the registered portal count, and frame nodes 2020-02-15 10:55:37 +01:00
8f29a35175 use minetest.get_modpath("mesecon") instead of checking mesecon ~= nil 2020-02-15 10:55:37 +01:00
e3ac1ade79 blasts extinguish portals 2020-02-15 10:55:37 +01:00
363872c760 Avoid storing the playerRef
See https://rubenwardy.com/minetest_modding_book/en/quality/common_mistakes.html#never-store-objectrefs-ie-players-or-entities
Related: https://github.com/minetest-mods/nether/pull/12
2020-02-15 10:55:37 +01:00
40913ee841 text changes
Also a minor change to prevent surface portals from appearing in a grid pattern in MT 0.4
2020-02-15 10:55:37 +01:00
e765f5f504 text/docs/formatting changes 2020-02-15 10:55:37 +01:00
58da96549a Trim trailing spaces
This eliminates most luacheck formatting warnings
2020-02-15 10:55:37 +01:00
d0ce9dd633 Update .luacheckrc and fix warnings
Changes to reduce formatting warnings will be committed separately
2020-02-15 10:55:37 +01:00
1d19546345 Update copyrights & require Minetest v0.4.16 or greater
Also adds nether.register_wormhole_node(), moving the wormhole node template into portal_api.lua, and allowing wormhole nodes with custom post_effect_color to be more easily created.
2020-02-15 10:55:37 +01:00
3945467152 Add more mesecon support
wormholes now emit mesecon energy, allowing mesecons to know the state of the portal.
(mesecons could previously only set the state of the portal)
2020-02-15 10:55:37 +01:00
478684062d Add Minetest 0.4 support 2020-02-15 10:55:37 +01:00
4ebdf7f25f Handle edge-case when player quits the game while teleporting
Issue was raised as a pull request against the original code - https://github.com/minetest-mods/nether/pull/12
Also replaces deprecated getpos/setpos with get_pos/set_pos - https://github.com/minetest-mods/nether/pull/11
2020-02-15 10:55:37 +01:00
4985e199b1 check for portal frames on the surface
When a surface level was known, only the volume_is_natural check was being performed, this fixes that.
Also fixes bug where LBM didn't start the timer of example portals that had been disabled.
Allows portal ignite to repair a stopped timer.
Other misc cleanup - paramat's new Floatlands won't have lakes so I removed that from the flavortext.
2020-02-15 10:55:37 +01:00
48cbd87b59 Move wormhole_node_is_horizontal into the shape definition
out of the portal defintion
2020-02-15 10:55:37 +01:00
43f1f24d53 Add settingtypes.txt settings 2020-02-15 10:55:37 +01:00
a971e0359e Finish example portals
give nether_portal_circular.mts a flat floor - the new nether_portal_circular.mts allows its bottom nodes to be sunk into the ground.
Example portal basic behavior and book_of_portals_pagetext implemented.
Other changes are allowing portals corrupted by ABMs to be repaired, finding better ground level of surface portals.
2020-02-15 10:55:37 +01:00
6752964c96 Add PortalShape_Platform
and other work on the portal examples
also documentation and fixing issue where apples prevented volume_is_natural() from returning true
2020-02-15 10:55:37 +01:00
b9e85582f9 Add Surface-following portal using Moore curves 2020-02-15 10:55:37 +01:00
11a818212a Standardize files as LF without CR, and UTF-8 without BOM
UTF-8 with BOM crashes some systems according to 6551f5c120 comment, and there are several cases where Minetest is buggy with files that use CRLF line endings (though none I'm aware that affect these files), so strip CRs and BOM
2020-02-15 10:55:37 +01:00
58eb65c0c8 documentation 2020-02-15 10:55:37 +01:00
6e0cadb1ec Check no other mod is already using a portal definition 2020-02-15 10:55:37 +01:00
9bfbb64e96 Add portal_examples.lua
Currently the example portals just take you to the nether, like nether portals. In theory they could take players anywhere.
2020-02-15 10:55:37 +01:00
b8ec09f402 Add animated particle support
Also implements an ignition failure sound.
2020-02-15 10:55:37 +01:00
0f3f42e5c0 Add portal sounds 2020-02-15 10:55:37 +01:00
d5a551b4cf Add example alternative shape PortalShape_Circular
Also fixes up a few bugs that only became apparent when not using the traditional portal shape
2020-02-15 10:55:37 +01:00
31cf6a9bc3 Place_Schematic uses correct frame and wormhole nodes
This requires a cache-invalidation hack borrowed from cloudlands mod.
Also, null reference fixes in ensure_remote_portal_then_teleport and locate_or_build_portal, and minor improvements
2020-02-15 10:55:37 +01:00
7cff6b8cc2 Add portal ignition sparks 2020-02-15 10:55:37 +01:00
27a660b731 Unroll 32affba6 (stairs) (#9)
32affba6 re-registered stairs which have already been registered 20 lines earlier in the code, and registered them as "brick" causing the netherbrick stairs to replace Minetest's default:brick stairs. The rest of 32affba6 was already unrolled in 7a0e52da, but the stairs bug remained.
2020-02-15 10:55:37 +01:00
dd6c19d005 Keep portal type in metadata for non-traditional portals
Makes the portal system more flexible - new portal shapes don't have to ensure there's a frame node at the timerPos
2020-02-15 10:55:37 +01:00
835ad9686d rearrange code
eliminate need to assign extinguish_portal() to a local var, and group the helper functions together in the API section
2020-02-15 10:55:37 +01:00
e2666146ca Implement sound and events in API 2020-02-15 10:55:37 +01:00
0c7a6e95c5 Fix edge case
Fix bug where ensure_remote_portal_then_teleport() converted its destination_wormholePos to an anchorPos to pass to locate_or_build_portal() which converted it back to an wormholePos. Bug occurred when local_orientation was assumed for the target orientation, which could lead to locate_or_build_portal() ending up with a reconstituted wormholePos rotated outside the portal frame, not find a portal frame, so build a new portal there, griefing the old one.

locate_or_build_portal() has been refactored to take a wormholePos instead of an anchorPos, removing the need to know the orientation of the target portal (which can't be obtained from the param2 when the target portal is unlit)
2020-02-15 10:55:37 +01:00
52b20925d5 Unlinks target portal if it's linked to a different portal
This is effectively a missing part to the change that made locate_or_build_portal() enforce that portals only link together in mutual pairs. As target portals that didn't need to be located or built we not being updated to link back to where the player travelled from.

Also
* implements events: on_run_wormhole, on_extinguish
* adds a lot of debug info
2020-02-15 10:55:37 +01:00
6401052f92 fix bugs
fixes nullreference in find_surface_target_y, and ensure_remote_portal_then_teleport assuming a traditional portal shape.
minor documentation work.
2020-02-15 10:55:37 +01:00
c106b88e98 only perform remote_portal_checkup if portal schematic was placed 2020-02-15 10:55:37 +01:00
1a6d15def3 Portals connect to nearby targets
Records portal positions. More testing required
2020-02-15 10:55:37 +01:00
43b37e96b2 Add Book of Portals
Squashed commit of the following:

commit e80030750df91f75745c7d52729a0d4942eb32f8
Author: Treer <treer.git+github@the-bordello.com>
Date:   Sat Jul 13 13:52:02 2019 +1000

    Finish book of portals

commit 82ad3d6af683b6bb4cc107e8c76f44c0b791a28d
Author: Treer <treer.git@gmail.com>
Date:   Thu Jul 11 22:43:02 2019 +1000

    Book of Portals art

commit fac6ff1da89a1b06b78deb0b5b4e3bebe4166860
Author: Treer <treer.git@gmail.com>
Date:   Thu Jul 11 00:39:50 2019 +1000

    starting book of portals
2020-02-15 10:55:37 +01:00
b6e2a3335a provide Portal API 2020-02-15 10:55:37 +01:00
3126d067a1 Dynamic/hardware portal colouring
Currently proof of concept: Removes wormhole colour from .png texture and specifies it through MT API instead. The post_effect_color may call for registering a separate node per portal colour.
Palette 0 will have to be the traditional portal magenta (instead of 0 being black) for map compatibility with mod when colour was in the texture and not param2.
2020-02-15 10:55:37 +01:00
83c9ad574f Allow two or more portal types to be made with the same frame material
(provided they are different sized portals)
Removes direct references to the registered_portals table
2020-02-15 10:55:37 +01:00
f38211e55d Prevent portals from sharing frame nodes with other portals
While it's rather cute having portals that share parts of their frame with other portals, the way the nether mod implemented portals means a node being part of more than one portal enables a family of bugs.

Igniting a portal now extinguishes any portal that was using nodes now taken by the newly ignited portal.
2020-02-15 10:55:37 +01:00
2113d332f8 Spawn portals on the surface, when possible 2020-02-15 10:55:37 +01:00
9a4c7ed875 Fix call to node timer stop()
Part of e5a5db907, fixes an issue where node timers weren't stopped by extinguish_portal().
And tweaks some comments
2020-02-15 10:55:37 +01:00
59275bb242 tidy up some code 2020-02-15 10:55:37 +01:00
c7d83eceb7 Prevent concurrent mapgen from removing freshly placed portals 2020-02-15 10:55:37 +01:00
edf961907e Allow reignition of portals in player-built areas
e.g. allow remote ignition to a portal in a "portal room"
2020-02-15 10:55:37 +01:00
7a4d9ebf01 Issue #6 Replace ABM with single node timer
https://github.com/minetest-mods/nether/issues/6
2020-02-15 10:55:37 +01:00
a085e194fa Add mesecons support
Portals can be switched on and off with mesecons
(from a tech-tree perspective, mese is still required for portal ignition)
2020-02-15 10:55:37 +01:00
614a3f91a7 Separate portal shape code from portal behavior code, and refactor
Allows dev and testing/debugging to focus on one (shape or behavior) without getting mixed up in the other, will also enable different portal shapes later on. Other adjustments:
* Reuse/reignite an existing portal or portal frame if there is one at the remote destination, rather than the area being overwritten by a slightly offset portal schematic.
* Create remote portals in a matching orientation to the local portal.
* Preserve player position & facing relative to portal when traveling through wormhole (even if portals are at 90°)
* Players no longer bounce several times after teleporting
* Player is not teleported until after the portal at the other end is confirmed/built
2020-02-15 10:55:37 +01:00
b49f4ce73d Fix error caused by teleporting offline players (#12) 2020-01-08 18:56:49 +01:00
6551f5c120 Convert UTF-8 + BOM to UTF-8. Fixes crash on some systems 2019-08-01 18:25:25 +02:00
b8dab52e41 Unroll 32affba6 (stairs) (#9)
32affba6 re-registered stairs which have already been registered 20 lines earlier in the code, and registered them as "brick" causing the netherbrick stairs to replace Minetest's default:brick stairs. The rest of 32affba6 was already unrolled in 7a0e52da, but the stairs bug remained.
2019-07-20 08:56:32 +02:00
bf145d4c11 Fix portal particle spawners
Issue introduced in 0b6925f4b1. Fix tested in 4.17.1 and 5.1-dev
2019-06-26 19:04:02 -07:00
33 changed files with 4115 additions and 678 deletions

View File

@ -1,15 +1,25 @@
unused_args = false
allow_defined_top = true
globals = {
"nether"
}
read_globals = {
"core",
"default",
"DIR_DELIM",
"dump",
"dungeon_loot",
"intllib",
"ItemStack",
"loot",
"doc",
"math.hypot",
"mesecon",
"minetest",
"nodeupdate",
"PcgRandom",
"PseudoRandom",
"stairs",
"stairsplus",

View File

@ -1,4 +1,10 @@
Nether Mod for Minetest
Nether Mod for Minetest, with Portals API.
See portal_api.txt for how to create custom portals to your own realms.
See settingtypes.txt or go to "Settings"->"All Settings"->"Mods"->"nether"
in the game to view the options provided by this mod.
## License of source code:
@ -17,15 +23,26 @@ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
## License of media (textures and sounds)
## License and attribution of media (textures and sounds)
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by-sa/3.0/
### [Public Domain Dedication (CC0 1.0)](https://creativecommons.org/publicdomain/zero/1.0/)
## Authors of media files
* `nether_portal_teleport.ogg` is a timing adjusted version of "teleport" by outroelison (https://freesound.org/people/outroelison), used under CC0 1.0
Everything not listed in here:
Copyright (C) 2013 PilzAdam
### [Attribution 3.0 Unported (CC BY 3.0)](https://creativecommons.org/licenses/by/3.0/)
* `nether_portal_ambient.ogg` & `nether_portal_ambient.0.ogg` are extractions from "Deep Cinematic Rumble Stereo" by [Patrick Lieberkind](http://www.lieberkindvisuals.dk), used under CC BY 3.0
* `nether_portal_extinguish.ogg` is an extraction from "Tight Laser Weapon Hit Scifi" by damjancd (https://freesound.org/people/damjancd), used under CC BY 3.0
* `nether_portal_ignite.ogg` is a derivative of "Flame Ignition" by hykenfreak (https://freesound.org/people/hykenfreak), used under CC BY 3.0. "Nether Portal ignite" is licensed under CC BY 3.0 by Treer.
- nether_rack.png: Zeg9
- nether_glowstone.png: BlockMen
### [Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/)
* `nether_book_`* (files starting with "nether_book"): Treer, 2019-2020
* `nether_portal_ignition_failure.ogg`: Treer, 2019
* `nether_particle_anim`* (files starting with "nether_particle_anim"): Treer, 2019
### [Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)](http://creativecommons.org/licenses/by-sa/3.0/)
* `nether_rack.png`: Zeg9
* `nether_glowstone.png`: BlockMen
* `nether_portal.png`: [Extex101](https://github.com/Extex101), 2020
All other media: Copyright © 2013 PilzAdam, licensed under CC BY-SA 3.0 by PilzAdam.

View File

@ -1,3 +1,7 @@
stairs
default
moreblocks?
mesecons?
loot?
dungeon_loot?
doc_basics?

808
init.lua
View File

@ -1,4 +1,3 @@
--[[
Nether mod for minetest
@ -20,690 +19,161 @@
]]--
-- Parameters
local NETHER_DEPTH = -5000
local TCAVE = 0.6
local BLEND = 128
-- 3D noise
local np_cave = {
offset = 0,
scale = 1,
spread = {x = 384, y = 128, z = 384}, -- squashed 3:1
seed = 59033,
octaves = 5,
persist = 0.7,
lacunarity = 2.0,
--flags = ""
}
-- Stuff
local yblmax = NETHER_DEPTH - BLEND * 2
-- Functions
local function build_portal(pos, target)
local p1 = {x = pos.x - 1, y = pos.y - 1, z = pos.z}
local p2 = {x = p1.x + 3, y = p1.y + 4, z = p1.z}
local path = minetest.get_modpath("nether") .. "/schematics/nether_portal.mts"
minetest.place_schematic({x = p1.x, y = p1.y, z = p1.z - 2}, path, 0, nil, true)
for y = p1.y, p2.y do
for x = p1.x, p2.x do
local meta = minetest.get_meta({x = x, y = y, z = p1.z})
meta:set_string("p1", minetest.pos_to_string(p1))
meta:set_string("p2", minetest.pos_to_string(p2))
meta:set_string("target", minetest.pos_to_string(target))
end
local S
if minetest.get_translator ~= nil then
S = minetest.get_translator("nether")
else
-- mock the translator function for MT 0.4
S = function(str, ...)
local args={...}
return str:gsub(
"@%d+",
function(match) return args[tonumber(match:sub(2))] end
)
end
end
-- Global Nether namespace
nether = {}
nether.modname = minetest.get_current_modname()
nether.path = minetest.get_modpath(nether.modname)
nether.get_translator = S
-- nether.useBiomes allows other mods to know whether they can register ores etc. in the Nether.
-- See mapgen.lua for an explanation of why minetest.read_schematic is being checked
nether.useBiomes = minetest.get_mapgen_setting("mg_name") ~= "v6" and minetest.read_schematic ~= nil
local function 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()
-- Settings
nether.DEPTH_CEILING = -25000 -- The y location of the Nether's celing
nether.DEPTH_FLOOR = -31000 -- The y location of the Nether's floor
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.NETHER_REALM_ENABLED = true -- Setting to false disables the Nether and Nether portal
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)
if not minetest.registered_nodes[name].is_ground_content then
return false
end
end
vi = vi + 1
end
end
end
return true
-- Override default settings with values from the .conf file, if any are present.
nether.FASTTRAVEL_FACTOR = tonumber(minetest.settings:get("nether_fasttravel_factor") or nether.FASTTRAVEL_FACTOR)
nether.PORTAL_BOOK_LOOT_WEIGHTING = tonumber(minetest.settings:get("nether_portalBook_loot_weighting") or nether.PORTAL_BOOK_LOOT_WEIGHTING)
nether.NETHER_REALM_ENABLED = minetest.settings:get_bool("nether_realm_enabled", nether.NETHER_REALM_ENABLED)
nether.DEPTH_CEILING = tonumber(minetest.settings:get("nether_depth_ymax") or nether.DEPTH_CEILING)
nether.DEPTH_FLOOR = tonumber(minetest.settings:get("nether_depth_ymin") or nether.DEPTH_FLOOR)
if nether.DEPTH_FLOOR + 1000 > nether.DEPTH_CEILING then
error("The lower limit of the Nether must be set at least 1000 lower than the upper limit, and more than 3000 is recommended. Set settingtypes.txt, or 'All Settings' -> 'Mods' -> 'nether' -> 'Nether depth'", 0)
end
nether.DEPTH = nether.DEPTH_CEILING -- Deprecated, use nether.DEPTH_CEILING instead.
local function find_nether_target_y(target_x, target_z, start_y)
local nobj_cave_point = minetest.get_perlin(np_cave)
local air = 0 -- Consecutive air nodes found
for y = start_y, start_y - 4096, -1 do
local nval_cave = nobj_cave_point:get3d({x = target_x, y = y, z = target_z})
if nval_cave > TCAVE then -- Cavern
air = air + 1
else -- Not cavern, check if 4 nodes of space above
if air >= 4 then
-- 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 volume_is_natural(minp, maxp) then
return y + 2
else -- Restart search a little lower
find_nether_target_y(target_x, target_z, y - 16)
end
else -- Not enough space, reset air to zero
air = 0
end
end
end
return start_y -- Fallback
end
local function find_surface_target_y(target_x, target_z, start_y)
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 volume_is_natural(minp, maxp) then
return y
end
end
return start_y - 256 -- Fallback
end
local function move_check(p1, max, dir)
local p = {x = p1.x, y = p1.y, z = p1.z}
local d = math.abs(max - p1[dir]) / (max - p1[dir])
while p[dir] ~= max do
p[dir] = p[dir] + d
if minetest.get_node(p).name ~= "default:obsidian" then
return false
end
end
return true
end
local function check_portal(p1, p2)
if p1.x ~= p2.x then
if not move_check(p1, p2.x, "x") then
return false
end
if not move_check(p2, p1.x, "x") then
return false
end
elseif p1.z ~= p2.z then
if not move_check(p1, p2.z, "z") then
return false
end
if not move_check(p2, p1.z, "z") then
return false
end
-- Load files
dofile(nether.path .. "/portal_api.lua")
dofile(nether.path .. "/nodes.lua")
if nether.NETHER_REALM_ENABLED then
if nether.useBiomes then
dofile(nether.path .. "/mapgen.lua")
else
return false
end
if not move_check(p1, p2.y, "y") then
return false
end
if not move_check(p2, p1.y, "y") then
return false
end
return true
end
local function is_portal(pos)
for d = -3, 3 do
for y = -4, 4 do
local px = {x = pos.x + d, y = pos.y + y, z = pos.z}
local pz = {x = pos.x, y = pos.y + y, z = pos.z + d}
if check_portal(px, {x = px.x + 3, y = px.y + 4, z = px.z}) then
return px, {x = px.x + 3, y = px.y + 4, z = px.z}
end
if check_portal(pz, {x = pz.x, y = pz.y + 4, z = pz.z + 3}) then
return pz, {x = pz.x, y = pz.y + 4, z = pz.z + 3}
end
end
dofile(nether.path .. "/mapgen_nobiomes.lua")
end
end
dofile(nether.path .. "/portal_examples.lua")
local function make_portal(pos)
local p1, p2 = is_portal(pos)
if not p1 or not p2 then
return false
end
for d = 1, 2 do
for y = p1.y + 1, p2.y - 1 do
local p
if p1.z == p2.z then
p = {x = p1.x + d, y = y, z = p1.z}
else
p = {x = p1.x, y = y, z = p1.z + d}
end
if minetest.get_node(p).name ~= "air" then
return false
end
end
end
local param2
if p1.z == p2.z then
param2 = 0
else
param2 = 1
end
local target = {x = p1.x, y = p1.y, z = p1.z}
target.x = target.x + 1
if target.y < NETHER_DEPTH then
target.y = find_surface_target_y(target.x, target.z, -16)
else
local start_y = NETHER_DEPTH - math.random(500, 1500) -- Search start
target.y = find_nether_target_y(target.x, target.z, start_y)
end
for d = 0, 3 do
for y = p1.y, p2.y do
local p
if param2 == 0 then
p = {x = p1.x + d, y = y, z = p1.z}
else
p = {x = p1.x, y = y, z = p1.z + d}
end
if minetest.get_node(p).name == "air" then
minetest.set_node(p, {name = "nether:portal", param2 = param2})
end
local meta = minetest.get_meta(p)
meta:set_string("p1", minetest.pos_to_string(p1))
meta:set_string("p2", minetest.pos_to_string(p2))
meta:set_string("target", minetest.pos_to_string(target))
end
end
return true
end
-- ABMs
minetest.register_abm({
nodenames = {"nether:portal"},
interval = 1,
chance = 2,
action = function(pos, node)
minetest.add_particlespawner({
32, --amount
4, --time
{x = pos.x - 0.25, y = pos.y - 0.25, z = pos.z - 0.25}, --minpos
{x = pos.x + 0.25, y = pos.y + 0.25, z = pos.z + 0.25}, --maxpos
{x = -0.8, y = -0.8, z = -0.8}, --minvel
{x = 0.8, y = 0.8, z = 0.8}, --maxvel
{x = 0, y = 0, z = 0}, --minacc
{x = 0, y = 0, z = 0}, --maxacc
0.5, --minexptime
1, --maxexptime
1, --minsize
2, --maxsize
false, --collisiondetection
"nether_particle.png" --texture
})
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
if obj:is_player() then
local meta = minetest.get_meta(pos)
local target = minetest.string_to_pos(meta:get_string("target"))
if target then
-- force emerge of target area
minetest.get_voxel_manip():read_from_map(target, target)
if not minetest.get_node_or_nil(target) then
minetest.emerge_area(
vector.subtract(target, 4), vector.add(target, 4))
end
-- teleport the player
minetest.after(3, function(o, p, t)
local objpos = o:getpos()
objpos.y = objpos.y + 0.1 -- Fix some glitches at -8000
if minetest.get_node(objpos).name ~= "nether:portal" then
return
end
o:setpos(t)
local function check_and_build_portal(pp, tt)
local n = minetest.get_node_or_nil(tt)
if n and n.name ~= "nether:portal" then
build_portal(tt, pp)
minetest.after(2, check_and_build_portal, pp, tt)
minetest.after(4, check_and_build_portal, pp, tt)
elseif not n then
minetest.after(1, check_and_build_portal, pp, tt)
end
end
minetest.after(1, check_and_build_portal, p, t)
end, obj, pos, target)
end
end
end
end,
})
-- Nodes
minetest.register_node("nether:portal", {
description = "Nether Portal",
tiles = {
"nether_transparent.png",
"nether_transparent.png",
"nether_transparent.png",
"nether_transparent.png",
{
name = "nether_portal.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 0.5,
},
},
{
name = "nether_portal.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 0.5,
},
},
},
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
use_texture_alpha = true,
walkable = false,
diggable = false,
pointable = false,
buildable_to = false,
is_ground_content = false,
drop = "",
light_source = 5,
post_effect_color = {a = 180, r = 128, g = 0, b = 128},
alpha = 192,
node_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.1, 0.5, 0.5, 0.1},
},
},
groups = {not_in_creative_inventory = 1}
})
minetest.register_node(":default:obsidian", {
description = "Obsidian",
tiles = {"default_obsidian.png"},
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
groups = {cracky = 1, level = 2},
on_destruct = function(pos)
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 not p1 or not p2 then
return
end
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 == "default:obsidian" or nn == "nether:portal" then
if nn == "nether:portal" 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", "")
end
end
end
end
meta = minetest.get_meta(target)
if not meta then
return
end
p1 = minetest.string_to_pos(meta:get_string("p1"))
p2 = minetest.string_to_pos(meta:get_string("p2"))
if not p1 or not p2 then
return
end
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 == "default:obsidian" or nn == "nether:portal" then
if nn == "nether:portal" 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", "")
end
end
end
end
end,
})
minetest.register_node("nether:rack", {
description = "Netherrack",
tiles = {"nether_rack.png"},
is_ground_content = true,
groups = {cracky = 3, level = 2},
sounds = default.node_sound_stone_defaults(),
})
minetest.register_node("nether:sand", {
description = "Nethersand",
tiles = {"nether_sand.png"},
is_ground_content = true,
groups = {crumbly = 3, level = 2, falling_node = 1},
sounds = default.node_sound_gravel_defaults({
footstep = {name = "default_gravel_footstep", gain = 0.45},
}),
})
minetest.register_node("nether:glowstone", {
description = "Glowstone",
tiles = {"nether_glowstone.png"},
is_ground_content = true,
light_source = 14,
paramtype = "light",
groups = {cracky = 3, oddly_breakable_by_hand = 3},
sounds = default.node_sound_glass_defaults(),
})
minetest.register_node("nether:brick", {
description = "Nether Brick",
tiles = {"nether_brick.png"},
is_ground_content = false,
groups = {cracky = 2, level = 2},
sounds = default.node_sound_stone_defaults(),
})
local fence_texture =
"default_fence_overlay.png^nether_brick.png^default_fence_overlay.png^[makealpha:255,126,126"
minetest.register_node("nether:fence_nether_brick", {
description = "Nether Brick Fence",
drawtype = "fencelike",
tiles = {"nether_brick.png"},
inventory_image = fence_texture,
wield_image = fence_texture,
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
selection_box = {
type = "fixed",
fixed = {-1/7, -1/2, -1/7, 1/7, 1/2, 1/7},
},
groups = {cracky = 2, level = 2},
sounds = default.node_sound_stone_defaults(),
})
-- Register stair and slab
stairs.register_stair_and_slab(
"nether_brick",
"nether:brick",
{cracky = 2, level = 2},
{"nether_brick.png"},
"nether stair",
"nether slab",
default.node_sound_stone_defaults()
-- Portals are ignited by right-clicking with a mese crystal fragment
nether.register_portal_ignition_item(
"default:mese_crystal_fragment",
{name = "nether_portal_ignition_failure", gain = 0.3}
)
-- StairsPlus
if minetest.get_modpath("moreblocks") then
stairsplus:register_all(
"nether", "brick", "nether:brick", {
description = "Nether Brick",
groups = {cracky = 2, level = 2},
tiles = {"nether_brick.png"},
sounds = default.node_sound_stone_defaults(),
if nether.NETHER_REALM_ENABLED then
-- Use the Portal API to add a portal type which goes to the Nether
-- See portal_api.txt for documentation
nether.register_portal("nether_portal", {
shape = nether.PortalShape_Traditional,
frame_node_name = "default:obsidian",
wormhole_node_color = 0, -- 0 is magenta
title = S("Nether Portal"),
book_of_portals_pagetext = S([[Construction requires 14 blocks of obsidian, which we found deep underground where water had solidified molten rock. The finished frame is four blocks wide, five blocks high, and stands vertically, like a doorway.
This opens to a truly hellish place, though for small mercies the air there is still breathable. There is an intriguing dimensional mismatch happening between this realm and ours, as after opening the second portal into it we observed that 10 strides taken in the Nether appear to be an equivalent of @1 in the natural world.
The expedition parties have found no diamonds or gold, and after an experienced search party failed to return from the trail of a missing expedition party, I must conclude this is a dangerous place.]], 10 * nether.FASTTRAVEL_FACTOR),
is_within_realm = function(pos) -- return true if pos is inside the Nether
return pos.y < nether.DEPTH_CEILING
end,
find_realm_anchorPos = function(surface_anchorPos)
-- divide x and z by a factor of 8 to implement Nether fast-travel
local destination_pos = vector.divide(surface_anchorPos, nether.FASTTRAVEL_FACTOR)
destination_pos.x = math.floor(0.5 + destination_pos.x) -- round to int
destination_pos.z = math.floor(0.5 + destination_pos.z) -- round to int
destination_pos.y = nether.DEPTH_CEILING - 1000 -- temp value so find_nearest_working_portal() returns nether portals
-- a y_factor of 0 makes the search ignore the altitude of the portals (as long as they are in the Nether)
local existing_portal_location, existing_portal_orientation =
nether.find_nearest_working_portal("nether_portal", destination_pos, 8, 0)
if existing_portal_location ~= nil then
return existing_portal_location, existing_portal_orientation
else
local start_y = nether.DEPTH_CEILING - math.random(500, 1500) -- Search starting altitude
destination_pos.y = nether.find_nether_ground_y(destination_pos.x, destination_pos.z, start_y)
return destination_pos
end
end,
find_surface_anchorPos = function(realm_anchorPos)
-- A portal definition doesn't normally need to provide a find_surface_anchorPos() function,
-- since find_surface_target_y() will be used by default, but Nether portals also scale position
-- to create fast-travel.
-- Defining a custom function also means we can look for existing nearby portals.
-- Multiply x and z by a factor of 8 to implement Nether fast-travel
local destination_pos = vector.multiply(realm_anchorPos, nether.FASTTRAVEL_FACTOR)
destination_pos.x = math.min(30900, math.max(-30900, destination_pos.x)) -- clip to world boundary
destination_pos.z = math.min(30900, math.max(-30900, destination_pos.z)) -- clip to world boundary
destination_pos.y = 0 -- temp value so find_nearest_working_portal() doesn't return nether portals
-- a y_factor of 0 makes the search ignore the altitude of the portals (as long as they are outside the Nether)
local existing_portal_location, existing_portal_orientation =
nether.find_nearest_working_portal("nether_portal", destination_pos, 8 * nether.FASTTRAVEL_FACTOR, 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, "nether_portal")
return destination_pos
end
end,
on_ignite = function(portalDef, anchorPos, orientation)
-- make some sparks fly
local p1, p2 = portalDef.shape:get_p1_and_p2_from_anchorPos(anchorPos, orientation)
local pos = vector.divide(vector.add(p1, p2), 2)
local textureName = portalDef.particle_texture
if type(textureName) == "table" then textureName = textureName.name end
minetest.add_particlespawner({
amount = 110,
time = 0.1,
minpos = {x = pos.x - 0.5, y = pos.y - 1.2, z = pos.z - 0.5},
maxpos = {x = pos.x + 0.5, y = pos.y + 1.2, z = pos.z + 0.5},
minvel = {x = -5, y = -1, z = -5},
maxvel = {x = 5, y = 1, z = 5},
minacc = {x = 0, y = 0, z = 0},
maxacc = {x = 0, y = 0, z = 0},
minexptime = 0.1,
maxexptime = 0.5,
minsize = 0.2 * portalDef.particle_texture_scale,
maxsize = 0.8 * portalDef.particle_texture_scale,
collisiondetection = false,
texture = textureName .. "^[colorize:#F4F:alpha",
animation = portalDef.particle_texture_animation,
glow = 8
})
end
})
end
stairs.register_stair_and_slab("brick", "nether:brick",
{cracky=3, oddly_breakable_by_hand=1},
{"nether_brick.png"},
"nether stair",
"nether slab",
default.node_sound_stone_defaults())
-- Craftitems
minetest.override_item("default:mese_crystal_fragment", {
on_place = function(stack, _, pt)
if pt.under and minetest.get_node(pt.under).name == "default:obsidian" then
local done = make_portal(pt.under)
if done and not minetest.settings:get_bool("creative_mode") then
stack:take_item()
end
end
return stack
end,
})
-- Crafting
minetest.register_craft({
output = "nether:brick 4",
recipe = {
{"nether:rack", "nether:rack"},
{"nether:rack", "nether:rack"},
}
})
minetest.register_craft({
output = "nether:fence_nether_brick 6",
recipe = {
{"nether:brick", "nether:brick", "nether:brick"},
{"nether:brick", "nether:brick", "nether:brick"},
},
})
-- Mapgen
-- Initialize noise object, localise noise and data buffers
local nobj_cave = nil
local nbuf_cave = nil
local dbuf = nil
-- Content ids
local c_air = minetest.get_content_id("air")
--local c_stone_with_coal = minetest.get_content_id("default:stone_with_coal")
--local c_stone_with_iron = minetest.get_content_id("default:stone_with_iron")
local c_stone_with_mese = minetest.get_content_id("default:stone_with_mese")
local c_stone_with_diamond = minetest.get_content_id("default:stone_with_diamond")
local c_stone_with_gold = minetest.get_content_id("default:stone_with_gold")
--local c_stone_with_copper = minetest.get_content_id("default:stone_with_copper")
local c_mese = minetest.get_content_id("default:mese")
local c_gravel = minetest.get_content_id("default:gravel")
local c_dirt = minetest.get_content_id("default:dirt")
local c_sand = minetest.get_content_id("default:sand")
local c_cobble = minetest.get_content_id("default:cobble")
local c_mossycobble = minetest.get_content_id("default:mossycobble")
local c_stair_cobble = minetest.get_content_id("stairs:stair_cobble")
local c_lava_source = minetest.get_content_id("default:lava_source")
local c_lava_flowing = minetest.get_content_id("default:lava_flowing")
local c_water_source = minetest.get_content_id("default:water_source")
local c_water_flowing = minetest.get_content_id("default:water_flowing")
local c_glowstone = minetest.get_content_id("nether:glowstone")
local c_nethersand = minetest.get_content_id("nether:sand")
local c_netherbrick = minetest.get_content_id("nether:brick")
local c_netherrack = minetest.get_content_id("nether:rack")
-- On-generated function
minetest.register_on_generated(function(minp, maxp, seed)
if minp.y > NETHER_DEPTH then
return
end
local x1 = maxp.x
local y1 = maxp.y
local z1 = maxp.z
local x0 = minp.x
local y0 = minp.y
local z0 = minp.z
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}
local data = vm:get_data(dbuf)
local x11 = emax.x -- Limits of mapchunk plus mapblock shell
local y11 = emax.y
local z11 = emax.z
local x00 = emin.x
local y00 = emin.y
local z00 = emin.z
local ystride = x1 - x0 + 1
local zstride = ystride * ystride
local chulens = {x = ystride, y = ystride, z = ystride}
local minposxyz = {x = x0, y = y0, z = z0}
nobj_cave = nobj_cave or minetest.get_perlin_map(np_cave, chulens)
local nvals_cave = nobj_cave:get3dMap_flat(minposxyz, nbuf_cave)
for y = y00, y11 do -- Y loop first to minimise tcave calculations
local tcave
local in_chunk_y = false
if y >= y0 and y <= y1 then
if y > yblmax then
tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2
else
tcave = TCAVE
end
in_chunk_y = true
end
for z = z00, z11 do
local vi = area:index(x00, y, z) -- Initial voxelmanip index
local ni
local in_chunk_yz = in_chunk_y and z >= z0 and z <= z1
for x = x00, x11 do
if in_chunk_yz and x == x0 then
-- Initial noisemap index
ni = (z - z0) * zstride + (y - y0) * ystride + 1
end
local in_chunk_yzx = in_chunk_yz and x >= x0 and x <= x1 -- In mapchunk
local id = data[vi] -- Existing node
-- Cave air, cave liquids and dungeons are overgenerated,
-- convert these throughout mapchunk plus shell
if id == c_air or -- Air and liquids to air
id == c_lava_source or
id == c_lava_flowing or
id == c_water_source or
id == c_water_flowing then
data[vi] = c_air
-- Dungeons are preserved so we don't need
-- to check for cavern in the shell
elseif id == c_cobble or -- Dungeons (preserved) to netherbrick
id == c_mossycobble or
id == c_stair_cobble then
data[vi] = c_netherbrick
end
if in_chunk_yzx then -- In mapchunk
if nvals_cave[ni] > tcave then -- Only excavate cavern in mapchunk
data[vi] = c_air
elseif id == c_mese then -- Mese block to lava
data[vi] = c_lava_source
elseif id == c_stone_with_gold or -- Precious ores to glowstone
id == c_stone_with_mese or
id == c_stone_with_diamond then
data[vi] = c_glowstone
elseif id == c_gravel or -- Blob ore to nethersand
id == c_dirt or
id == c_sand then
data[vi] = c_nethersand
else -- All else to netherstone
data[vi] = c_netherrack
end
ni = ni + 1 -- Only increment noise index in mapchunk
end
vi = vi + 1
end
end
end
vm:set_data(data)
vm:set_lighting({day = 0, night = 0})
vm:calc_lighting()
vm:update_liquids()
vm:write_to_map()
end)

500
mapgen.lua Normal file
View File

@ -0,0 +1,500 @@
--[[
Nether mod for minetest
Copyright (C) 2013 PilzAdam
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
]]--
-- Parameters
local NETHER_CEILING = nether.DEPTH_CEILING
local NETHER_FLOOR = nether.DEPTH_FLOOR
local TCAVE = 0.6
local BLEND = 128
-- Stuff
local math_max, math_min = math.max, math.min -- avoid needing table lookups each time a common math function is invoked
if minetest.read_schematic == nil then
-- Using biomes to create the Nether requires the ability for biomes to set "node_cave_liquid = air".
-- This feature was introduced by paramat in b1b40fef1 on 2019-05-19, but we can't test for
-- it directly. However b2065756c was merged a few months later (in 2019-08-14) and it is easy
-- to directly test for - it adds minetest.read_schematic() - so we use this as a proxy-test
-- for whether the Minetest engine is recent enough to have implemented node_cave_liquid=air
error("This " .. nether.modname .. " mapgen requires Minetest v5.1 or greater, use mapgen_nobiomes.lua instead.", 0)
end
local function override_underground_biomes()
-- https://forum.minetest.net/viewtopic.php?p=257522#p257522
-- Q: Is there a way to override an already-registered biome so I can get it out of the
-- way of my own underground biomes without disturbing the other biomes registered by
-- default?
-- A: No, all you can do is use a mod to clear all biomes then re-register the complete
-- set but with your changes. It has been described as hacky but this is actually the
-- official way to alter biomes, most mods and subgames would want to completely change
-- all biomes anyway.
-- To avoid the engine side of mapgen becoming overcomplex the approach is to require mods
-- to do slightly more complex stuff in Lua.
-- take a copy of all biomes, decorations, and ores. Regregistering a biome changes its ID, so
-- any decorations or ores using the 'biomes' field must afterwards be cleared and re-registered.
-- https://github.com/minetest/minetest/issues/9288
local registered_biomes_copy = {}
local registered_decorations_copy = {}
local registered_ores_copy = {}
for old_biome_key, old_biome_def in pairs(minetest.registered_biomes) do
registered_biomes_copy[old_biome_key] = old_biome_def
end
for old_decoration_key, old_decoration_def in pairs(minetest.registered_decorations) do
registered_decorations_copy[old_decoration_key] = old_decoration_def
end
for old_ore_key, old_ore_def in pairs(minetest.registered_ores) do
registered_ores_copy[old_ore_key] = old_ore_def
end
-- clear biomes, decorations, and ores
minetest.clear_registered_decorations()
minetest.clear_registered_ores()
minetest.clear_registered_biomes()
-- Restore biomes, adjusted to not overlap the Nether
for biome_key, new_biome_def in pairs(registered_biomes_copy) do
local biome_y_max, biome_y_min = tonumber(new_biome_def.y_max), tonumber(new_biome_def.y_min)
if biome_y_max > NETHER_FLOOR and biome_y_min < NETHER_CEILING then
-- This biome occupies some or all of the depth of the Nether, shift/crop it.
local spaceOccupiedAbove = biome_y_max - NETHER_CEILING
local spaceOccupiedBelow = NETHER_FLOOR - biome_y_min
if spaceOccupiedAbove >= spaceOccupiedBelow or biome_y_min <= -30000 then
-- place the biome above the Nether
-- We also shift biomes which extend to the bottom of the map above the Nether, since they
-- likely only extend that deep as a catch-all, and probably have a role nearer the surface.
new_biome_def.y_min = NETHER_CEILING + 1
new_biome_def.y_max = math_max(biome_y_max, NETHER_CEILING + 2)
else
-- shift the biome to below the Nether
new_biome_def.y_max = NETHER_FLOOR - 1
new_biome_def.y_min = math_min(biome_y_min, NETHER_CEILING - 2)
end
end
minetest.register_biome(new_biome_def)
end
-- Restore biome decorations
for decoration_key, new_decoration_def in pairs(registered_decorations_copy) do
minetest.register_decoration(new_decoration_def)
end
-- Restore biome ores
for ore_key, new_ore_def in pairs(registered_ores_copy) do
minetest.register_ore(new_ore_def)
end
end
-- Shift any overlapping biomes out of the way before we create the Nether biomes
override_underground_biomes()
-- nether:native_mapgen is used to prevent ores and decorations being generated according
-- to landforms created by the native mapgen.
-- Ores and decorations are registered against "nether:rack" instead, and the lua
-- on_generate() callback will carve the Nether with nether:rack before invoking
-- generate_decorations and generate_ores.
minetest.register_node("nether:native_mapgen", {})
minetest.register_biome({
name = "nether_caverns",
node_stone = "nether:native_mapgen", -- nether:native_mapgen is used here to prevent the native mapgen from placing ores and decorations.
node_filler = "nether:native_mapgen", -- The lua on_generate will transform nether:rack_native into nether:rack then decorate and add ores.
node_dungeon = "nether:brick",
--node_dungeon_alt = "default:mossycobble",
node_dungeon_stair = "stairs:stair_nether_brick",
-- Setting node_cave_liquid to "air" avoids the need to filter lava and water out of the mapchunk and
-- surrounding shell (overdraw nodes beyond the mapchunk).
-- This feature was introduced by paramat in b1b40fef1 on 2019-05-19, and this mapgen.lua file should only
-- be run if the Minetest version includes it. The earliest tag made after 2019-05-19 is 5.1.0 on 2019-10-13,
-- however we shouldn't test version numbers. minetest.read_schematic() was added by b2065756c and merged in
-- 2019-08-14 and is easy to test for, we don't use it but it should make a good proxy-test for whether the
-- Minetest version is recent enough to have implemented node_cave_liquid=air
node_cave_liquid = "air",
y_max = NETHER_CEILING,
y_min = NETHER_FLOOR,
vertical_blend = 0,
heat_point = 50,
humidity_point = 50,
})
-- Ores and decorations
dofile(nether.path .. "/mapgen_decorations.lua")
minetest.register_ore({
ore_type = "scatter",
ore = "nether:glowstone",
wherein = "nether:rack",
clust_scarcity = 11 * 11 * 11,
clust_num_ores = 3,
clust_size = 2,
y_max = NETHER_CEILING,
y_min = NETHER_FLOOR,
})
minetest.register_ore({
ore_type = "scatter",
ore = "default:lava_source",
wherein = "nether:rack",
clust_scarcity = 32 * 32 * 32,
clust_num_ores = 4,
clust_size = 2,
y_max = NETHER_CEILING,
y_min = NETHER_FLOOR,
})
minetest.register_ore({
ore_type = "blob",
ore = "nether:sand",
wherein = "nether:rack",
clust_scarcity = 14 * 14 * 14,
clust_size = 8,
y_max = NETHER_CEILING,
y_min = NETHER_FLOOR
})
-- Mapgen
-- 3D noise
local np_cave = {
offset = 0,
scale = 1,
spread = {x = 384, y = 128, z = 384}, -- squashed 3:1
seed = 59033,
octaves = 5,
persist = 0.7,
lacunarity = 2.0,
--flags = ""
}
-- Buffers and objects we shouldn't recreate every on_generate
local nobj_cave = nil
local nbuf_cave = {}
local dbuf = {}
local yblmin = NETHER_FLOOR + BLEND * 2
local yblmax = NETHER_CEILING - BLEND * 2
-- Content ids
local c_air = minetest.get_content_id("air")
local c_netherrack = minetest.get_content_id("nether:rack")
local c_netherbrick = minetest.get_content_id("nether:brick")
local c_netherbrick_slab = minetest.get_content_id("stairs:slab_nether_brick")
local c_netherfence = minetest.get_content_id("nether:fence_nether_brick")
local c_glowstone = minetest.get_content_id("nether:glowstone")
local c_lava_source = minetest.get_content_id("default:lava_source")
local c_native_mapgen = minetest.get_content_id("nether:native_mapgen")
-- Dungeon excavation functions
function build_dungeon_room_list(data, area)
local result = {}
-- Unfortunately gennotify only returns dungeon rooms, not corridors.
-- We don't need to check for temples because only dungeons are generated in biomes
-- that define their own dungeon nodes.
local gennotify = minetest.get_mapgen_object("gennotify")
local roomLocations = gennotify["dungeon"] or {}
-- Excavation should still know to stop if a cave or corridor has removed the dungeon wall.
-- See MapgenBasic::generateDungeons in mapgen.cpp for max room sizes.
local maxRoomSize = 18
local maxRoomRadius = math.ceil(maxRoomSize / 2)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomPos in ipairs(roomLocations) do
if area:containsp(roomPos) then -- this safety check does not appear to be necessary, but lets make it explicit
local room_vi = area:indexp(roomPos)
--data[room_vi] = minetest.get_content_id("default:torch") -- debug
local startPos = vector.new(roomPos)
if roomPos.y + 1 <= maxEdge.y and data[room_vi + yStride] == c_air then
-- The roomPos coords given by gennotify are at floor level, but whenever possible we
-- want to be performing searches a node higher than floor level to avoids dungeon chests.
startPos.y = startPos.y + 1
room_vi = area:indexp(startPos)
end
local bound_min_x = math_max(minEdge.x, roomPos.x - maxRoomRadius)
local bound_min_y = math_max(minEdge.y, roomPos.y - 1) -- room coords given by gennotify are on the floor
local bound_min_z = math_max(minEdge.z, roomPos.z - maxRoomRadius)
local bound_max_x = math_min(maxEdge.x, roomPos.x + maxRoomRadius)
local bound_max_y = math_min(maxEdge.y, roomPos.y + maxRoomSize) -- room coords given by gennotify are on the floor
local bound_max_z = math_min(maxEdge.z, roomPos.z + maxRoomRadius)
local room_min = vector.new(startPos)
local room_max = vector.new(startPos)
local vi = room_vi
while room_max.y < bound_max_y and data[vi + yStride] == c_air do
room_max.y = room_max.y + 1
vi = vi + yStride
end
vi = room_vi
while room_min.y > bound_min_y and data[vi - yStride] == c_air do
room_min.y = room_min.y - 1
vi = vi - yStride
end
vi = room_vi
while room_max.z < bound_max_z and data[vi + zStride] == c_air do
room_max.z = room_max.z + 1
vi = vi + zStride
end
vi = room_vi
while room_min.z > bound_min_z and data[vi - zStride] == c_air do
room_min.z = room_min.z - 1
vi = vi - zStride
end
vi = room_vi
while room_max.x < bound_max_x and data[vi + xStride] == c_air do
room_max.x = room_max.x + 1
vi = vi + xStride
end
vi = room_vi
while room_min.x > bound_min_x and data[vi - xStride] == c_air do
room_min.x = room_min.x - 1
vi = vi - xStride
end
local roomInfo = vector.new(roomPos)
roomInfo.minp = room_min
roomInfo.maxp = room_max
result[#result + 1] = roomInfo
end
end
return result;
end
-- Only partially excavates dungeons, the rest is left as an exercise for the player ;)
-- (Corridors and the parts of rooms which extend beyond the emerge boundary will remain filled)
function excavate_dungeons(data, area, rooms)
-- any air from the native mapgen has been replaced by netherrack, but
-- we don't want this inside dungeons, so fill dungeon rooms with air
for _, roomInfo in ipairs(rooms) do
local room_min = roomInfo.minp
local room_max = roomInfo.maxp
for z = room_min.z, room_max.z do
for y = room_min.y, room_max.y do
local vi = area:index(room_min.x, y, z)
for x = room_min.x, room_max.x do
if data[vi] == c_netherrack then data[vi] = c_air end
vi = vi + 1
end
end
end
end
end
-- Since we already know where all the rooms and their walls are, and have all the nodes stored
-- in a voxelmanip already, we may as well add a little Nether flair to the dungeons found here.
function decorate_dungeons(data, area, rooms)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomInfo in ipairs(rooms) do
local room_min, room_max = roomInfo.minp, roomInfo.maxp
local room_size = vector.distance(room_min, room_max)
if room_size > 10 then
local room_seed = roomInfo.x + 3 * roomInfo.z + 13 * roomInfo.y
local window_y = roomInfo.y + math_min(2, room_max.y - roomInfo.y - 1)
if room_seed % 3 == 0 and room_max.y < maxEdge.y then
-- Glowstone chandelier
local vi = area:index(roomInfo.x, room_max.y + 1, roomInfo.z)
if data[vi] == c_netherbrick then data[vi] = c_glowstone end
elseif room_seed % 4 == 0 and room_min.y > minEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
-- lava well (feel free to replace with a fancy schematic)
local vi = area:index(roomInfo.x, room_min.y, roomInfo.z)
if data[vi - yStride] == c_netherbrick then data[vi - yStride] = c_lava_source end
if data[vi - zStride] == c_air then data[vi - zStride] = c_netherbrick_slab end
if data[vi + zStride] == c_air then data[vi + zStride] = c_netherbrick_slab end
if data[vi - xStride] == c_air then data[vi - xStride] = c_netherbrick_slab end
if data[vi + xStride] == c_air then data[vi + xStride] = c_netherbrick_slab end
end
-- Barred windows
if room_seed % 7 < 5 and room_max.x - room_min.x >= 4 and room_max.z - room_min.z >= 4
and window_y >= minEdge.y and window_y + 1 <= maxEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
--data[area:indexp(roomInfo)] = minetest.get_content_id("default:mese_post_light") -- debug
-- Until whisper glass is added, every window will be made of netherbrick fence (rather
-- than material depending on room_seed)
local window_node = c_netherfence
local vi_min = area:index(room_min.x - 1, window_y, roomInfo.z)
local vi_max = area:index(room_max.x + 1, window_y, roomInfo.z)
local locations = {-zStride, zStride, -zStride + yStride, zStride + yStride}
for _, offset in ipairs(locations) do
if data[vi_min + offset] == c_netherbrick then data[vi_min + offset] = window_node end
if data[vi_max + offset] == c_netherbrick then data[vi_max + offset] = window_node end
end
vi_min = area:index(roomInfo.x, window_y, room_min.z - 1)
vi_max = area:index(roomInfo.x, window_y, room_max.z + 1)
locations = {-xStride, xStride, -xStride + yStride, xStride + yStride}
for _, offset in ipairs(locations) do
if data[vi_min + offset] == c_netherbrick then data[vi_min + offset] = window_node end
if data[vi_max + offset] == c_netherbrick then data[vi_max + offset] = window_node end
end
end
-- Weeds on the floor once Nether weeds are added
end
end
end
-- On-generated function
local function on_generated(minp, maxp, seed)
if minp.y > NETHER_CEILING or maxp.y < NETHER_FLOOR then
return
end
local vm, emerge_min, emerge_max = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge=emerge_min, MaxEdge=emerge_max}
local data = vm:get_data(dbuf)
local x0, y0, z0 = minp.x, math_max(minp.y, NETHER_FLOOR), minp.z
local x1, y1, z1 = maxp.x, math_min(maxp.y, NETHER_CEILING), maxp.z
local yCaveStride = x1 - x0 + 1
local zCaveStride = yCaveStride * yCaveStride
local chulens = {x = yCaveStride, y = yCaveStride, z = yCaveStride}
nobj_cave = nobj_cave or minetest.get_perlin_map(np_cave, chulens)
local nvals_cave = nobj_cave:get_3d_map_flat(minp, nbuf_cave)
local dungeonRooms = build_dungeon_room_list(data, area)
for y = y0, y1 do -- Y loop first to minimise tcave calculations
local tcave = TCAVE
if y > yblmax then tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2 end
if y < yblmin then tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2 end
for z = z0, z1 do
local vi = area:index(x0, y, z) -- Initial voxelmanip index
local ni = (z - z0) * zCaveStride + (y - y0) * yCaveStride + 1
for x = x0, x1 do
local id = data[vi] -- Existing node
if nvals_cave[ni] > tcave then
data[vi] = c_air
elseif id == c_air or id == c_native_mapgen then
data[vi] = c_netherrack -- excavate_dungeons() will mostly reverse this inside dungeons
end
ni = ni + 1
vi = vi + 1
end
end
end
-- any air from the native mapgen has been replaced by netherrack, but we
-- don't want netherrack inside dungeons, so fill known dungeon rooms with air.
excavate_dungeons(data, area, dungeonRooms)
decorate_dungeons(data, area, dungeonRooms)
vm:set_data(data)
-- avoid generating decorations on the underside of the bottom of the nether
if minp.y > NETHER_FLOOR and maxp.y < NETHER_CEILING then minetest.generate_decorations(vm) end
minetest.generate_ores(vm)
vm:set_lighting({day = 0, night = 0}, minp, maxp)
vm:calc_lighting()
vm:update_liquids()
vm:write_to_map()
end
-- use knowledge of the nether mapgen algorithm to return a suitable ground level for placing a portal.
function nether.find_nether_ground_y(target_x, target_z, start_y)
local nobj_cave_point = minetest.get_perlin(np_cave)
local air = 0 -- Consecutive air nodes found
for y = start_y, math_max(NETHER_FLOOR + BLEND, start_y - 4096), -1 do
local nval_cave = nobj_cave_point:get3d({x = target_x, y = y, z = target_z})
if nval_cave > TCAVE then -- Cavern
air = air + 1
else -- Not cavern, check if 4 nodes of space above
if air >= 4 then
-- Check volume for non-natural nodes
local minp = {x = target_x - 1, y = y , z = target_z - 2}
local maxp = {x = target_x + 2, y = y + 4, z = target_z + 2}
if nether.volume_is_natural(minp, maxp) then
return y + 1
else -- Restart search a little lower
nether.find_nether_ground_y(target_x, target_z, y - 16)
end
else -- Not enough space, reset air to zero
air = 0
end
end
end
return math_max(start_y, NETHER_FLOOR + BLEND) -- Fallback
end
-- We don't need to be gen-notified of temples because only dungeons will be generated
-- if a biome defines the dungeon nodes
minetest.set_gen_notify({dungeon = true})
minetest.register_on_generated(on_generated)

138
mapgen_decorations.lua Normal file
View File

@ -0,0 +1,138 @@
--[[
Register decorations for Nether mapgen
Copyright (C) 2020 Treer
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
]]--
-- Lava is unreliable in the old Nether mapgen because it removes lava
-- from the overdraw areas, so any decorations involving lava will often
-- have the lava missing depending on whether nearby chunks were already
-- emerged or not before the decoration was placed.
local allow_lava_decorations = nether.useBiomes
local _ = {name = "air", prob = 0}
local A = {name = "air", prob = 255, force_place = true}
local G = {name = "nether:glowstone", prob = 255, force_place = true}
local N = {name = "nether:rack", prob = 255}
local S = {name = "nether:sand", prob = 255, force_place = true}
local L = {name = "default:lava_source", prob = 255, force_place = true}
-- =================
-- Stalactites
-- =================
local schematic_GlowstoneStalactite = {
size = {x = 5, y = 10, z = 5},
data = {
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, N, G, N, _,
_, N, N, N, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, G, _, _,
_, _, G, _, _,
_, G, G, G, _,
N, G, G, G, N,
N, N, G, N, N,
_, _, N, _, _, -- ypos 0, prob 25% (64/256)
_, _, G, _, _, -- ypos 1, prob 37% (96/256)
_, _, G, _, _, -- ypos 2, prob 100%
_, _, G, _, _, -- ypos 3, prob 100%
_, _, G, G, _, -- ypos 4, prob 50% (128/256) to make half of stalactites asymmetric
_, G, G, G, _, -- ypos 5, prob 75% (192/256)
_, G, G, G, _, -- ypos 6, prob 75% (192/256)
_, G, G, G, _, -- ypos 7, prob 100%
G, G, G, G, G, -- ypos 8, prob 100%
N, G, G, G, N, -- ypos 9, prob 75% (192/256)
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, G, _, _,
_, _, G, _, _,
_, _, G, _, _,
_, G, G, G, _,
N, G, G, G, N,
N, N, G, N, N,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
_, N, G, N, _,
_, N, N, N, _
},
-- Y-slice probabilities do not function correctly for ceiling schematic
-- decorations because they are inverted, so ypos numbers have been inverted
-- to match, and a larger offset in place_offset_y should be used (e.g. -3).
yslice_prob = {
{ypos = 9, prob = 192},
{ypos = 6, prob = 192},
{ypos = 5, prob = 192},
{ypos = 4, prob = 128},
{ypos = 1, prob = 96},
{ypos = 0, prob = 64}
}
}
minetest.register_decoration({
name = "Glowstone stalactite",
deco_type = "schematic",
place_on = "nether:rack",
sidelen = 80,
fill_ratio = 0.0004,
biomes = {"nether_caverns"},
y_max = nether.DEPTH_CEILING, -- keep compatibility with mapgen_nobiomes.lua
y_min = nether.DEPTH_FLOOR,
schematic = schematic_GlowstoneStalactite,
flags = "place_center_x,place_center_z,force_placement,all_ceilings",
place_offset_y=-3
})
minetest.register_decoration({
name = "Netherrack stalactite",
deco_type = "schematic",
place_on = "nether:rack",
sidelen = 80,
fill_ratio = 0.0007,
biomes = {"nether_caverns"},
y_max = nether.DEPTH_CEILING, -- keep compatibility with mapgen_nobiomes.lua
y_min = nether.DEPTH_FLOOR,
schematic = schematic_GlowstoneStalactite,
replacements = {["nether:glowstone"] = "nether:rack"},
flags = "place_center_x,place_center_z,all_ceilings",
place_offset_y=-3
})

230
mapgen_nobiomes.lua Normal file
View File

@ -0,0 +1,230 @@
--[[
Nether mod for minetest
Copyright (C) 2013 PilzAdam
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
]]--
-- Parameters
local NETHER_CEILING = nether.DEPTH_CEILING
local NETHER_FLOOR = nether.DEPTH_FLOOR
local TCAVE = 0.6
local BLEND = 128
-- 3D noise
local np_cave = {
offset = 0,
scale = 1,
spread = {x = 384, y = 128, z = 384}, -- squashed 3:1
seed = 59033,
octaves = 5,
persist = 0.7,
lacunarity = 2.0,
--flags = ""
}
-- Stuff
local yblmin = NETHER_FLOOR + BLEND * 2
local yblmax = NETHER_CEILING - BLEND * 2
-- Mapgen
dofile(nether.path .. "/mapgen_decorations.lua")
-- Initialize noise object, localise noise and data buffers
local nobj_cave = nil
local nbuf_cave = {}
local dbuf = {}
-- Content ids
local c_air = minetest.get_content_id("air")
--local c_stone_with_coal = minetest.get_content_id("default:stone_with_coal")
--local c_stone_with_iron = minetest.get_content_id("default:stone_with_iron")
local c_stone_with_mese = minetest.get_content_id("default:stone_with_mese")
local c_stone_with_diamond = minetest.get_content_id("default:stone_with_diamond")
local c_stone_with_gold = minetest.get_content_id("default:stone_with_gold")
--local c_stone_with_copper = minetest.get_content_id("default:stone_with_copper")
local c_mese = minetest.get_content_id("default:mese")
local c_gravel = minetest.get_content_id("default:gravel")
local c_dirt = minetest.get_content_id("default:dirt")
local c_sand = minetest.get_content_id("default:sand")
local c_cobble = minetest.get_content_id("default:cobble")
local c_mossycobble = minetest.get_content_id("default:mossycobble")
local c_stair_cobble = minetest.get_content_id("stairs:stair_cobble")
local c_lava_source = minetest.get_content_id("default:lava_source")
local c_lava_flowing = minetest.get_content_id("default:lava_flowing")
local c_water_source = minetest.get_content_id("default:water_source")
local c_water_flowing = minetest.get_content_id("default:water_flowing")
local c_glowstone = minetest.get_content_id("nether:glowstone")
local c_nethersand = minetest.get_content_id("nether:sand")
local c_netherbrick = minetest.get_content_id("nether:brick")
local c_netherrack = minetest.get_content_id("nether:rack")
-- On-generated function
minetest.register_on_generated(function(minp, maxp, seed)
if minp.y > NETHER_CEILING or maxp.y < NETHER_FLOOR then
return
end
local x1 = maxp.x
local y1 = math.min(maxp.y, NETHER_CEILING)
local z1 = maxp.z
local x0 = minp.x
local y0 = math.max(minp.y, NETHER_FLOOR)
local z0 = minp.z
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}
local data = vm:get_data(dbuf)
local x11 = emax.x -- Limits of mapchunk plus mapblock shell
local y11 = emax.y
local z11 = emax.z
local x00 = emin.x
local y00 = emin.y
local z00 = emin.z
local ystride = x1 - x0 + 1
local zstride = ystride * ystride
local chulens = {x = ystride, y = ystride, z = ystride}
local minposxyz = {x = x0, y = y0, z = z0}
nobj_cave = nobj_cave or minetest.get_perlin_map(np_cave, chulens)
local nvals_cave = nobj_cave:get_3d_map_flat(minposxyz, nbuf_cave)
for y = y00, y11 do -- Y loop first to minimise tcave calculations
local tcave
local in_chunk_y = false
if y >= y0 and y <= y1 then
tcave = TCAVE
if y > yblmax then tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2 end
if y < yblmin then tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2 end
in_chunk_y = true
end
for z = z00, z11 do
local vi = area:index(x00, y, z) -- Initial voxelmanip index
local ni
local in_chunk_yz = in_chunk_y and z >= z0 and z <= z1
for x = x00, x11 do
if in_chunk_yz and x == x0 then
-- Initial noisemap index
ni = (z - z0) * zstride + (y - y0) * ystride + 1
end
local in_chunk_yzx = in_chunk_yz and x >= x0 and x <= x1 -- In mapchunk
local id = data[vi] -- Existing node
-- Cave air, cave liquids and dungeons are overgenerated,
-- convert these throughout mapchunk plus shell
if id == c_air or -- Air and liquids to air
id == c_lava_source or
id == c_lava_flowing or
id == c_water_source or
id == c_water_flowing then
data[vi] = c_air
-- Dungeons are preserved so we don't need
-- to check for cavern in the shell
elseif id == c_cobble or -- Dungeons (preserved) to netherbrick
id == c_mossycobble or
id == c_stair_cobble then
data[vi] = c_netherbrick
end
if in_chunk_yzx then -- In mapchunk
if nvals_cave[ni] > tcave then -- Only excavate cavern in mapchunk
data[vi] = c_air
elseif id == c_mese then -- Mese block to lava
data[vi] = c_lava_source
elseif id == c_stone_with_gold or -- Precious ores to glowstone
id == c_stone_with_mese or
id == c_stone_with_diamond then
data[vi] = c_glowstone
elseif id == c_gravel or -- Blob ore to nethersand
id == c_dirt or
id == c_sand then
data[vi] = c_nethersand
else -- All else to netherstone
data[vi] = c_netherrack
end
ni = ni + 1 -- Only increment noise index in mapchunk
end
vi = vi + 1
end
end
end
vm:set_data(data)
-- avoid generating decorations on the underside of the bottom of the nether
if minp.y > NETHER_FLOOR and maxp.y < NETHER_CEILING then minetest.generate_decorations(vm) end
vm:set_lighting({day = 0, night = 0}, minp, maxp)
vm:calc_lighting()
vm:update_liquids()
vm:write_to_map()
end)
-- use knowledge of the nether mapgen algorithm to return a suitable ground level for placing a portal.
function nether.find_nether_ground_y(target_x, target_z, start_y)
local nobj_cave_point = minetest.get_perlin(np_cave)
local air = 0 -- Consecutive air nodes found
for y = start_y, start_y - 4096, -1 do
local nval_cave = nobj_cave_point:get3d({x = target_x, y = y, z = target_z})
if nval_cave > TCAVE then -- Cavern
air = air + 1
else -- Not cavern, check if 4 nodes of space above
if air >= 4 then
-- Check volume for non-natural nodes
local minp = {x = target_x - 1, y = y , z = target_z - 2}
local maxp = {x = target_x + 2, y = y + 4, z = target_z + 2}
if nether.volume_is_natural(minp, maxp) then
return y + 1
else -- Restart search a little lower
nether.find_nether_ground_y(target_x, target_z, y - 16)
end
else -- Not enough space, reset air to zero
air = 0
end
end
end
return start_y -- Fallback
end

170
nodes.lua Normal file
View File

@ -0,0 +1,170 @@
--[[
Nether mod for minetest
Copyright (C) 2013 PilzAdam
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
]]--
local S = nether.get_translator
-- Portal/wormhole nodes
nether.register_wormhole_node("nether:portal", {
description = S("Nether Portal"),
post_effect_color = {
-- post_effect_color can't be changed dynamically in Minetest like the portal colour is.
-- If you need a different post_effect_color then use register_wormhole_node to create
-- another wormhole node and set it as the wormhole_node_name in your portaldef.
-- Hopefully this colour is close enough to magenta to work with the traditional magenta
-- portals, close enough to red to work for a red portal, and also close enough to red to
-- work with blue & cyan portals - since blue portals are sometimes portrayed as being red
-- from the opposite side / from the inside.
a = 160, r = 128, g = 0, b = 80
}
})
local portal_animation2 = {
name = "nether_portal_alt.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 0.5,
},
}
nether.register_wormhole_node("nether:portal_alt", {
description = S("Portal"),
tiles = {
"nether_transparent.png",
"nether_transparent.png",
"nether_transparent.png",
"nether_transparent.png",
portal_animation2,
portal_animation2
},
post_effect_color = {
-- hopefully blue enough to work with blue portals, and green enough to
-- work with cyan portals.
a = 120, r = 0, g = 128, b = 188
}
})
-- Nether nodes
minetest.register_node("nether:rack", {
description = S("Netherrack"),
tiles = {"nether_rack.png"},
is_ground_content = true,
groups = {cracky = 3, level = 2},
sounds = default.node_sound_stone_defaults(),
})
minetest.register_node("nether:sand", {
description = S("Nethersand"),
tiles = {"nether_sand.png"},
is_ground_content = true,
groups = {crumbly = 3, level = 2, falling_node = 1},
sounds = default.node_sound_gravel_defaults({
footstep = {name = "default_gravel_footstep", gain = 0.45},
}),
})
minetest.register_node("nether:glowstone", {
description = S("Glowstone"),
tiles = {"nether_glowstone.png"},
is_ground_content = true,
light_source = 14,
paramtype = "light",
groups = {cracky = 3, oddly_breakable_by_hand = 3},
sounds = default.node_sound_glass_defaults(),
})
minetest.register_node("nether:brick", {
description = S("Nether Brick"),
tiles = {"nether_brick.png"},
is_ground_content = false,
groups = {cracky = 2, level = 2},
sounds = default.node_sound_stone_defaults(),
})
local fence_texture =
"default_fence_overlay.png^nether_brick.png^default_fence_overlay.png^[makealpha:255,126,126"
minetest.register_node("nether:fence_nether_brick", {
description = S("Nether Brick Fence"),
drawtype = "fencelike",
tiles = {"nether_brick.png"},
inventory_image = fence_texture,
wield_image = fence_texture,
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
selection_box = {
type = "fixed",
fixed = {-1/7, -1/2, -1/7, 1/7, 1/2, 1/7},
},
groups = {cracky = 2, level = 2},
sounds = default.node_sound_stone_defaults(),
})
-- Register stair and slab
stairs.register_stair_and_slab(
"nether_brick",
"nether:brick",
{cracky = 2, level = 2},
{"nether_brick.png"},
S("Nether Stair"),
S("Nether Slab"),
default.node_sound_stone_defaults()
)
-- StairsPlus
if minetest.get_modpath("moreblocks") then
stairsplus:register_all(
"nether", "brick", "nether:brick", {
description = S("Nether Brick"),
groups = {cracky = 2, level = 2},
tiles = {"nether_brick.png"},
sounds = default.node_sound_stone_defaults(),
})
end
-- Crafting
minetest.register_craft({
output = "nether:brick 4",
recipe = {
{"nether:rack", "nether:rack"},
{"nether:rack", "nether:rack"},
}
})
minetest.register_craft({
output = "nether:fence_nether_brick 6",
recipe = {
{"nether:brick", "nether:brick", "nether:brick"},
{"nether:brick", "nether:brick", "nether:brick"},
},
})

2304
portal_api.lua Normal file

File diff suppressed because it is too large Load Diff

249
portal_api.txt Normal file
View File

@ -0,0 +1,249 @@
Portal API Reference
====================
The portal system used to get to the Nether can be used to create portals
to other realms.
Pick a node type to have your portals built from, a shape in which the
portals must be built, and provide 3 functions for portals to find their
destination with:
* `find_realm_anchorPos(surface_anchorPos)`
* `find_surface_anchorPos(realm_anchorPos)`
* `is_within_realm(pos)`
Optionally decorate by choosing portal colors, particles, media etc.
See `init.lua` and `portal_examples.lua` for examples of 3 different portals.
Portal code is more efficient when each type of portal uses a different type
of node to build its frame out of - consider creating your own node for
players to build portals from. However it is possible to register more than
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
using the same node for its frame, and portal sizes remain small.
Realms
------
This API uses the concept of a realm for each type of portal. If a portal is
outside its realm then it links to a portal inside the realm, if a portal is
inside its realm then it links to the outside.
You get to decide what constitutes your realm by implementing the function
`is_within_realm(position)`.
For example, the Nether realm is defined as existing at a certain depth and
anything above that depth is considered outside the Realm.
In contrast, the "Surface portal" - an example in portal_examples.lua, only
uses one realm. Its `is_within_realm()` function always returns true so that
any time a portal is opened it will use `find_surface_anchorPos()`.
Note that the name "find_surface_anchorPos" is a Nether-centric misnomer, as
different types of portals are free to use different definitions of a realm
such that leaving the realm might not be synonymous with travelling to the
surface.
Helper functions
----------------
* `nether.volume_is_natural(minp, maxp)`: returns a boolean
* 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.
Water will fail this test, unless it is unemerged.
* `nether.find_surface_target_y(target_x, target_z, portal_name)`: returns a
suitable anchorPos
* 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.
* `nether.find_nearest_working_portal(portal_name, anchorPos, distance_limit, y_factor)`: returns
(anchorPos, orientation), or nil if no portal was found within the
distance_limit.
* A y_factor of 0 means y does not affect the distance_limit, a y_factor
of 1 means y is included (the default if y_factor is nil), and a y_factor
of 2 would squash the search-sphere by a factor of 2 on the y-axis, etc.
* Only portals in the same realm as the anchorPos will be returned, even if
y_factor is 0.
* Pass a nil (or negative) distance_limit to indicate no distance limit
API functions
-------------
Call these functions only at load time:
* `nether.register_portal(name, portal_definition)`
* Returns true on success. Can return false if the portal definition
clashes with a portal already registered by another mod, e.g. if the size
and frame node is not unique.
A false return value should be handled, you could:
* Fall back to using a secondary material for portals to be built with.
* Use error() to exit lua with a message explaining how two mods are
clashing and how it can be resolved.
* Continue without a portal (the reason will be logged for the user).
* `nether.register_portal_ignition_item(name, ignition_failure_sound)`
* ignition_failure_sound is optional, it plays any time an attempt to use
the item occurs if a portal is not ignited.
* `nether.register_wormhole_node(name, nodedef_overrides)`
* Can be used to register wormhole nodes with a different post_effect_color
from the "nether:portal" node. "Post effect color" is the tint the world
takes on when you are standing inside a portal. `post_effect_color` is the
only key/value that is needed in the nodedef_overrides table to achieve that,
but the function allows any nodedef key/value to be specified/overridden.
* After `register_wormhole_node()`, invoke `register_portal()` and include
`wormhole_node_name` in the portal_definition, assigning it the name of the
new wormhole node.
* `nether.unregister_portal(name)`
* Unregisters the portal from the engine, and deletes the entry with key
`name` from `nether.registered_portals` and associated internal tables.
* Returns true on success
* You will probably never need to call this, it exists only for completeness.
Portal definition
-----------------
Used by `nether.register_portal`.
{
frame_node_name = "default:obsidian",
-- Required. For best results, have your portal constructed of a
-- material nobody else is using.
frame_node_color = 0,
-- Optional.
-- A value from 0 to 7. Only used if the frame node's paramtype2 is
-- "colorfacedir", in which case this color will be used when a remote
-- portal is created.
shape = nether.PortalShape_Traditional,
-- Optional.
-- Shapes available are:
-- nether.PortalShape_Traditional (default)
-- nether.PortalShape_Circular
-- nether.PortalShape_Platform
-- New shapes can be created, but are beyond the scope of this guide.
wormhole_node_name = "nether:portal",
-- Optional. Allows a custom wormhole node to be specified.
-- Useful if you want the portals to have a different post_effect_color
-- or texture.
-- The Nether mod provides:
-- "nether:portal" (default)
-- "nether:portal_alt"
wormhole_node_color = 0,
-- Optional. Defaults to 0/magenta.
-- A value from 0 to 7 corresponding to the color of pixels in
-- nether_portals_palette.png:
-- 0 traditional/magenta
-- 1 black
-- 2 blue
-- 3 green
-- 4 cyan
-- 5 red
-- 6 yellow
-- 7 white
particle_color = "#808",
-- Optional. Will default to a colour matching the wormhole_node_color
-- if not specified.
particle_texture = "image.png",
-- Optional. Hardware colouring (i.e. particle_color) is applied to
-- this texture, use particle_texture_colored instead if you want to
-- use the colors of the image.
-- Animation and particle scale may also be specified, e.g:
-- particle_texture = {
-- name = "nether_particle_anim1.png",
-- animation = {
-- type = "vertical_frames",
-- aspect_w = 7,
-- aspect_h = 7,
-- length = 1,
-- },
-- scale = 1.5
-- },
-- See lua_api.txt for Tile Animation definition
-- Some animated and non-animated textures are provided by this mod:
-- nether_particle.png (original)
-- nether_particle_anim1.png (stars)
-- nether_particle_anim2.png (bubbles)
-- nether_particle_anim3.png (sparks)
-- nether_particle_anim4.png (particles)
title = "Gateway to Moria",
-- Optional. Provides a title for the portal.
-- Used in the Book of Portals or Help modpack.
book_of_portals_pagetext = "Everything I need the player to know",
-- Optional. Provides the text for the portal in the Book of Portals
-- and Help modpack.
-- The Book of Portals is a book that can be found in chests, and
-- provides players with instructions on how to build and use the
-- portal, so be sure to mention the node type the frame must be built
-- from.
-- This can also provide flavortext or details about where the portal
-- will take the player.
sounds = {
ambient = <SimpleSoundSpec>,
-- if the ambient SimpleSoundSpec is a table it can also contain a
-- "length" int, which is the number of seconds to wait before
-- repeating the ambient sound. Default is 3.
ignite = <SimpleSoundSpec>,
extinguish = <SimpleSoundSpec>,
teleport = <SimpleSoundSpec>,
}
-- sounds is optional
within_realm = function(pos),
-- Required. Return true if a portal at pos is in the realm, rather
-- than the surface world.
-- Ideally implementations are fast, as this function can be used to
-- sift through a list of portals.
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)
-- 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
-- orientation, otherwise the existing portal could be overwritten by
-- a new one with the orientation of the surface portal.
-- Return nil to prevent the portal from igniting.
find_surface_anchorPos = function(realm_anchorPos),
-- Optional. If you don't implement this then a position near the
-- surface will be picked.
-- Return an anchorPos or (anchorPos, orientation)
-- The name of this function is a Nether-centric misnomer. It should
-- return a position outside the realm, and different types of portals
-- are free to use different definitions of a realm such that leaving
-- the realm might not be synonymous with travelling to the surface.
-- If orientation is not specified then the orientation of the realm
-- portal will be used.
-- If the location of an existing portal is returned then include the
-- orientation, otherwise the existing portal could be overwritten by
-- a new one with the orientation of the realm portal.
-- Return nil to prevent the portal from igniting.
on_run_wormhole = function(portalDef, anochorPos, orientation),
-- invoked once per second per portal
on_extinguish = function(portalDef, anochorPos, orientation),
-- invoked when a portal is extinguished, including when the portal
-- it connected to was extinguished.
on_player_teleported = function(portalDef, player, oldPos, newPos),
-- invoked immediately after a player is teleported
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, this is usually when
-- a player travels through a portal for the first time.
}

320
portal_examples.lua Normal file
View File

@ -0,0 +1,320 @@
--[[
Nether mod portal examples for Minetest
These portal API examples work independently of the Nether realm
and Nether portal. To try these examples, enable them in:
Minetest -> Settings -> All settings -> Mods -> nether
Once enabled, details on how to build them can be found in dungeon
chests in the book of portals.
--
Copyright (C) 2020 Treer
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
]]--
local S = nether.get_translator
local ENABLE_PORTAL_EXAMPLE_FLOATLANDS = false
local ENABLE_PORTAL_EXAMPLE_SURFACETRAVEL = false
-- 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 this will not always be true due to the how the Moore curve
-- frequently doubles back upon itself.
-- This doubling-back prevents the surface portal from taking players easily accross the
-- map - the curve is 262144 cells long!
local SURFACE_TRAVEL_DISTANCE = 26
--=================================================--
-- Portal to the Floatlands, playable code example --
--==================================================--
local FLOATLANDS_ENABLED
local FLOATLAND_LEVEL = 1280
if minetest.settings:get_bool("nether_enable_portal_example_floatlands", ENABLE_PORTAL_EXAMPLE_FLOATLANDS) or ENABLE_PORTAL_EXAMPLE_FLOATLANDS then
local floatlands_flavortext = ""
if minetest.get_mapgen_setting("mg_name") == "v7" then
local mgv7_spflags = minetest.get_mapgen_setting("mgv7_spflags")
FLOATLANDS_ENABLED = mgv7_spflags ~= nil and mgv7_spflags:find("floatlands") ~= nil and mgv7_spflags:find("nofloatlands") == nil
FLOATLAND_LEVEL = minetest.get_mapgen_setting("mgv7_floatland_level") or 1280
if FLOATLANDS_ENABLED then
floatlands_flavortext = "\n\n " .. S("There is a floating land of hills and forests up there, over the edges of which is a perilous drop all the way back down to sea level. We have not found how far these pristine lands extend. I have half a mind to retire there one day.")
end
end
nether.register_portal("floatlands_portal", {
shape = nether.PortalShape_Platform,
frame_node_name = "default:ice",
wormhole_node_color = 7, -- 7 is white
particle_texture = {
name = "nether_particle_anim1.png",
animation = {
type = "vertical_frames",
aspect_w = 7,
aspect_h = 7,
length = 1,
},
scale = 1.5
},
title = S("Floatlands Portal"),
book_of_portals_pagetext = S([[Requiring 21 blocks of ice, and constructed in the shape of a 3 × 3 platform with walls, or like a bowl. A finished platform is 2 blocks high, and 5 blocks wide at the widest in both directions.
This portal is different to the others, rather than acting akin to a doorway it appears to the eye more like a small pool of water which can be stepped into. Upon setting foot in the portal we found ourselves at a tremendous altitude.@1]],
floatlands_flavortext),
is_within_realm = function(pos) -- return true if pos is inside the Nether
return pos.y > FLOATLAND_LEVEL - 200
end,
find_realm_anchorPos = function(surface_anchorPos)
-- TODO: Once paramat finishes adjusting the floatlands, implement a surface algorithm that finds land
local destination_pos = {x = surface_anchorPos.x ,y = FLOATLAND_LEVEL + 2, z = surface_anchorPos.z}
-- a y_factor of 0 makes the search ignore the altitude of the portals (as long as they are in the Floatlands)
local existing_portal_location, existing_portal_orientation = nether.find_nearest_working_portal("floatlands_portal", destination_pos, 10, 0)
if existing_portal_location ~= nil then
return existing_portal_location, existing_portal_orientation
else
return destination_pos
end
end
})
end
--==============================================--
-- Surface-travel portal, playable code example --
--==============================================--
-- 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
if minetest.settings:get_bool("nether_enable_portal_example_surfacetravel", ENABLE_PORTAL_EXAMPLE_SURFACETRAVEL) or ENABLE_PORTAL_EXAMPLE_SURFACETRAVEL then
nether.register_portal("surface_portal", {
shape = nether.PortalShape_Circular,
frame_node_name = "default:tinblock",
wormhole_node_name = "nether:portal_alt",
wormhole_node_color = 4, -- 4 is cyan
title = S("Surface Portal"),
book_of_portals_pagetext = S([[Requiring 16 blocks of tin and constructed in a circular fashion, a finished frame is seven blocks wide, seven blocks high, and stands vertically like a doorway.
These travel a distance along the ground, and even when constructed deep underground will link back up to the surface. They appear to favor a strange direction, with the exit portal linking back only for as long as the portal stays open — attempting to reopen a portal from the exit doorway leads to a new destination along this favored direction. It has stymied our ability to study the behavior of these portals because without constructing dual portals and keeping both open it's hard to step through more than one and still be able to return home.
Due to such difficulties, we never learned what determines the direction and distance where the matching twin portal will appear, and I have lost my friend and protégé. In cavalier youth and with little more than a rucksack, Coudreau has decided to follow the chain as far as it goes, and has not been seen since. Coudreau believes it works in epicycles, but I am not convinced. Still, I cling to the hope that one day the portal will open and Coudreau will step out from whichever place leads to this one, perhaps with an epic tale to tell.]]),
is_within_realm = function(pos)
-- Always return true, because these portals always just take you around the surface
-- rather than taking you to a different realm
return true
end,
find_realm_anchorPos = function(surface_anchorPos)
-- This function isn't needed, since this type of portal always goes to the surface
minetest.log("error" , "find_realm_anchorPos called for surface portal")
return {x=0, y=0, z=0}
end,
find_surface_anchorPos = function(realm_anchorPos)
-- A portal definition doesn't normally need to provide a find_surface_anchorPos() function,
-- since find_surface_target_y() will be used by default, but these portals travel around the
-- surface (following a Moore curve) so will be calculating a different x and z to realm_anchorPos.
local cellCount = 512
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
local x = math.min(maxDistFromOrigin, math.max(-maxDistFromOrigin, realm_anchorPos.x)) + maxDistFromOrigin
local z = math.min(maxDistFromOrigin, math.max(-maxDistFromOrigin, realm_anchorPos.z)) + maxDistFromOrigin
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 + SURFACE_TRAVEL_DISTANCE) % (cellCount * cellCount)
local moore_pos = get_moore_coords(cellCount, destination_distance)
local target_x = moore_pos.x * divisor - maxDistFromOrigin
local target_z = moore_pos.y * divisor - maxDistFromOrigin
local search_radius = divisor / 2 - 5 -- any portal within this area will do
-- 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("surface_portal", {x = target_x, y = 0, z = target_z}, search_radius, 0)
if existing_portal_location ~= nil then
-- use the existing portal that was found near target_x, target_z
return existing_portal_location, existing_portal_orientation
else
-- find a good location for the new portal, or if that isn't possible then at
-- least adjust the coords a little so portals don't line up in a grid
local adj_x, adj_z = 0, 0
-- 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 attemptLimit = 15 -- how many attempts we'll make at finding a good location
for attempt = 1, attemptLimit do
adj_x = math.floor(prng:rand_normal_dist(-search_radius, search_radius, 2) + 0.5)
adj_z = math.floor(prng:rand_normal_dist(-search_radius, search_radius, 2) + 0.5)
if minetest.get_spawn_level == nil or 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.
-- Or this is MT 0.4 which does not have get_spawn_level(), so there's no point looking
-- at any further further random locations.
break
end
end
local destination_pos = {x = target_x + adj_x, y = 0, z = target_z + adj_z}
destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, "surface_portal")
return destination_pos
end
end
})
end
--=========================================--
-- Hilbert curve and Moore curve functions --
--=========================================--
-- 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
-- Flip a quadrant on a diagonal axis
-- cell_count is the number of cells across the square is split into, and must be a power of 2
-- if flip_twice is true then pos does not change (even numbers of flips cancel out)
-- if flip_direction is true then the position is flipped along the \ diagonal
-- if flip_direction is false then the position is flipped along the / diagonal
local function hilbert_flip(cell_count, pos, flip_direction, flip_twice)
if not flip_twice then
if flip_direction then
pos.x = (cell_count - 1) - pos.x;
pos.y = (cell_count - 1) - pos.y;
end
local temp_x = pos.x;
pos.x = pos.y;
pos.y = temp_x;
end
end
local function test_bit(cell_count, value, flag)
local bit_value = cell_count / 2
while bit_value > flag and bit_value >= 1 do
if value >= bit_value then value = value - bit_value end
bit_value = bit_value / 2
end
return value >= bit_value
end
-- Converts (x,y) to distance
-- starts at bottom left corner, i.e. (0, 0)
-- ends at bottom right corner, i.e. (cell_count - 1, 0)
local function get_hilbert_distance (cell_count, x, y)
local distance = 0
local pos = {x=x, y=y}
local rx, ry
local s = cell_count / 2
while s > 0 do
if test_bit(cell_count, pos.x, s) then rx = 1 else rx = 0 end
if test_bit(cell_count, pos.y, s) then ry = 1 else ry = 0 end
local rx_XOR_ry = rx
if ry == 1 then rx_XOR_ry = 1 - rx_XOR_ry end -- XOR'd ry against rx
distance = distance + s * s * (2 * rx + rx_XOR_ry)
hilbert_flip(cell_count, pos, rx > 0, ry > 0);
s = math.floor(s / 2)
end
return distance;
end
-- Converts distance to (x,y)
local function get_hilbert_coords(cell_count, distance)
local pos = {x=0, y=0}
local rx, ry
local s = 1
while s < cell_count do
rx = math.floor(distance / 2) % 2
ry = distance % 2
if rx == 1 then ry = 1 - ry end -- XOR ry with rx
hilbert_flip(s, pos, rx > 0, ry > 0);
pos.x = pos.x + s * rx
pos.y = pos.y + s * ry
distance = math.floor(distance / 4)
s = s * 2
end
return pos
end
-- Converts (x,y) to distance
-- A Moore curve is a variation of the Hilbert curve that has the start and
-- end next to each other.
-- Top middle point is the start/end location
get_moore_distance = function(cell_count, x, y)
local quadLength = cell_count / 2
local quadrant = 1 - math.floor(y / quadLength)
if math.floor(x / quadLength) == 1 then quadrant = 3 - quadrant end
local flipDirection = x < quadLength
local pos = {x = x % quadLength, y = y % quadLength}
hilbert_flip(quadLength, pos, flipDirection, false)
return (quadrant * quadLength * quadLength) + get_hilbert_distance(quadLength, pos.x, pos.y)
end
-- Converts distance to (x,y)
-- A Moore curve is a variation of the Hilbert curve that has the start and
-- end next to each other.
-- Top middle point is the start/end location
get_moore_coords = function(cell_count, distance)
local quadLength = cell_count / 2
local quadDistance = quadLength * quadLength
local quadrant = math.floor(distance / quadDistance)
local flipDirection = distance * 2 < cell_count * cell_count
local pos = get_hilbert_coords(quadLength, distance % quadDistance)
hilbert_flip(quadLength, pos, flipDirection, false)
if quadrant >= 2 then pos.x = pos.x + quadLength end
if quadrant % 3 == 0 then pos.y = pos.y + quadLength end
return pos
end

Binary file not shown.

25
settingtypes.txt Normal file
View File

@ -0,0 +1,25 @@
# Travelling a short distance in the Nether can correspond to a much further distance on the surface.
#
# A factor of 10 might be a better value for Minetest, since there's no sprint, but ex-Minecraft players will be mathing for 8.
nether_fasttravel_factor (Nether fast-travel factor) int 8
# The likelyhood of finding a Book containing all the portal plans in a dungeon chest.
# Set to 0 to disable, or 10 to have it extremely common.
#
# (This value will be treated as 0 when the Nether portal is the only type of portal available, or when the help modpack is installed)
nether_portalBook_loot_weighting (Likelyhood of finding Book of Portals in dungeon chests) int 9
# Turn off to disable the Nether and Nether portal
nether_realm_enabled (Enable Nether realm & portal) bool true
# Enables the Floatlands portal api code example
nether_enable_portal_example_floatlands (Enable example portal: Floatlands) bool false
# Enables the Surface-travel portal api code example
nether_enable_portal_example_surfacetravel (Enable example portal: Surface-travel) bool false
[Nether depth]
#The depth where the Nether begins / the Nether ceiling
nether_depth_ymax (Upper limit of Nether) int -5000 -30000 32767
#The lower limit of the Nether must be at least 1000 lower than the upper limit, and more than 3000 lower is recommended.
nether_depth_ymin (Lower limit of Nether) int -11000 -32768 30000

BIN
sounds/nether_book_open.ogg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B