19 Commits

Author SHA1 Message Date
e0656eacae Merge remote-tracking branch 'upstream/master' 2021-01-05 21:39:15 +01:00
5a2a6c63f1 Add tools attributions 2021-01-04 18:05:48 +11:00
00bb132edb add "fire" optional dependency
fumaroles use flame nodes if available
2021-01-04 18:05:48 +11:00
89db416d09 Merge remote-tracking branch 'upstream/master' 2021-01-02 14:26:25 +01:00
c7776cc082 Add nether based tools to mod (#31)
* textures for new tool items

* axe, shovel, pick, sword based on nether

* run also tools.lua

* Textture for Compressed Netherbrick

* new node "Compressed Netherbrick"

* define how to craft "Compressed Netherbrick"

* change recipe for NetherLump and remove old node

* Delete nether_nether_compressed.png
2021-01-01 23:54:48 +11:00
bfdd8d18b4 Merge remote-tracking branch 'upstream/master' 2020-12-30 22:28:44 +01:00
09ca1efd62 Square smoke puffs
The round ones look nice and cartoony, but not the right art direction for Minetest Nether.
Making use of the existing default_item_smoke.png is also a close contender for smoke puffs, but it's not translucent and I think the roundness of it reduces how much the puffs form into plumes.
2020-12-28 20:38:55 +11:00
f3c6f2df23 Add fumaroles (Nether chimneys)
Or they can be used on the surface as cottage chimneys.
2020-12-28 20:38:55 +11:00
2fb93b8c74 Add Concealed crevice / Lava sinkhole 2020-12-28 20:38:55 +11:00
60d4f8c7df Merge remote-tracking branch 'upstream/master' 2020-09-28 20:48:55 +02:00
1268049706 Remove string.lower so item name is correctly translated 2020-09-28 00:07:03 +10:00
c70a669709 Add french translation 2020-09-28 00:07:03 +10:00
281d6fc07f Merge remote-tracking branch 'upstream/master' 2020-08-02 14:07:44 +02:00
02d062b9c9 Handle large protected depths
Portals will now fail to ignite instead of defaulting to a fallback depth of (starty - 256) when no unprotected target can be found.
The search depth is also increased from (starty - 256) to (starty - 646)

This is to properly handle large protected areas that are sometimes set up at spawn - see issue #26
2020-08-02 12:19:30 +10:00
97cf3250e4 Merge remote-tracking branch 'upstream/master' 2020-07-31 22:23:04 +02:00
e326a94266 Add nether.debug() (#28) 2020-07-26 15:07:39 +10:00
5b3b56ebec Respect protected areas when spawning remote portals (#27)
* respect protected areas when spawning remote portals

* Use portal_shape to determine area to check for natural/protected

* Log warning on deprecated function call
2020-07-26 14:34:06 +10:00
c0481ea4ca Fix mods comp. using callback on_rightclick with mese fragment 2020-07-15 01:46:52 +02:00
8769593d6f Reduce lava in biomes-based mapgen (#25)
The biomes-based mapgen was creating the same amount of lava as the original nether mapgen, but it doesn't have the same issue of chunk emerge order sometimes causing lava in the overdraw regions to get removed. This adjustment will hopefully balance that a little.

Also makes glowstone stalactite a bit rarer.

Adjusting lava ore scarcity in the biomes-mapgen doesn't cause forwards or backwards compatibilty issues with existing maps, likewise with schematic rarety like glowstone stalactites, so can afford to fiddle and tune.
2020-07-12 12:12:00 +10:00
24 changed files with 1059 additions and 127 deletions

View File

@ -23,6 +23,7 @@ read_globals = {
"PseudoRandom",
"stairs",
"stairsplus",
"string.split",
table = { fields = { "copy", "getn" } },
"vector",
"VoxelArea",

View File

@ -27,22 +27,26 @@ SOFTWARE.
### [Public Domain Dedication (CC0 1.0)](https://creativecommons.org/publicdomain/zero/1.0/)
* `nether_portal_teleport.ogg` is a timing adjusted version of "teleport" by outroelison (https://freesound.org/people/outroelison), used under CC0 1.0
* `nether_portal_teleport.ogg` is a timing adjusted version of "teleport" by [outroelison](https://freesound.org/people/outroelison), used under CC0 1.0
### [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_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.
### [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_fumarole.ogg`: Treer, 2020
* `nether_particle_anim`* (files starting with "nether_particle_anim"): Treer, 2019
* `nether_portal_ignition_failure.ogg`: Treer, 2019
* `nether_smoke_puff.png`: Treer, 2020
### [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_nether_ingot.png` & `nether_nether_lump.png`: color adjusted versions from "[default](https://github.com/minetest/minetest_game/tree/master/mods/default)" mod, originally by Gambit
* `nether_portal.png`: [Extex101](https://github.com/Extex101), 2020
* `nether_rack.png`: Zeg9
* `nether_tool_`* (files starting with "nether_tool_"): color adjusted versions from "[default](https://github.com/minetest/minetest_game/tree/master/mods/default)" mod, originals by BlockMen
All other media: Copyright © 2013 PilzAdam, licensed under CC BY-SA 3.0 by PilzAdam.

View File

@ -5,3 +5,4 @@ mesecons?
loot?
dungeon_loot?
doc_basics?
fire?

View File

@ -19,6 +19,13 @@
]]--
-- Set DEBUG_FLAGS to determine the behavior of nether.debug():
-- 0 = off
-- 1 = print(...)
-- 2 = minetest.chat_send_all(...)
-- 4 = minetest.log("info", ...)
local DEBUG_FLAGS = 0
local S
if minetest.get_translator ~= nil then
S = minetest.get_translator("nether")
@ -58,14 +65,57 @@ nether.NETHER_REALM_ENABLED = minetest.settings:get_bool("nether_realm_ena
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
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.
-- A debug-print function that understands vectors etc. and does not
-- evaluate when debugging is turned off.
-- Works like string.format(), treating the message as a format string.
-- nils, tables, and vectors passed as arguments to nether.debug() are
-- converted to strings and can be included inside the message with %s
function nether.debug(message, ...)
local args = {...}
local argCount = select("#", ...)
for i = 1, argCount do
local arg = args[i]
if arg == nil then
-- convert nils to strings
args[i] = '<nil>'
elseif type(arg) == "table" then
local tableCount = 0
for _,_ in pairs(arg) do tableCount = tableCount + 1 end
if tableCount == 3 and arg.x ~= nil and arg.y ~= nil and arg.z ~= nil then
-- convert vectors to strings
args[i] = minetest.pos_to_string(arg)
else
-- convert tables to strings
-- (calling function can use dump() if a multi-line listing is desired)
args[i] = string.gsub(dump(arg, ""), "\n", " ")
end
end
end
local composed_message = "nether: " .. string.format(message, unpack(args))
if math.floor(DEBUG_FLAGS / 1) % 2 == 1 then print(composed_message) end
if math.floor(DEBUG_FLAGS / 2) % 2 == 1 then minetest.chat_send_all(composed_message) end
if math.floor(DEBUG_FLAGS / 4) % 2 == 1 then minetest.log("info", composed_message) end
end
if DEBUG_FLAGS == 0 then
-- do as little evaluation as possible
nether.debug = function() end
end
-- Load files
dofile(nether.path .. "/portal_api.lua")
dofile(nether.path .. "/nodes.lua")
dofile(nether.path .. "/tools.lua")
if nether.NETHER_REALM_ENABLED then
if nether.useBiomes then
dofile(nether.path .. "/mapgen.lua")
@ -101,7 +151,7 @@ The expedition parties have found no diamonds or gold, and after an experienced
return pos.y < nether.DEPTH_CEILING
end,
find_realm_anchorPos = function(surface_anchorPos)
find_realm_anchorPos = function(surface_anchorPos, player_name)
-- 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
@ -116,12 +166,12 @@ The expedition parties have found no diamonds or gold, and after an experienced
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)
destination_pos.y = nether.find_nether_ground_y(destination_pos.x, destination_pos.z, start_y, player_name)
return destination_pos
end
end,
find_surface_anchorPos = function(realm_anchorPos)
find_surface_anchorPos = function(realm_anchorPos, player_name)
-- 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.
@ -140,7 +190,7 @@ The expedition parties have found no diamonds or gold, and after an experienced
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")
destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, "nether_portal", player_name)
return destination_pos
end
end,

62
locale/nether.fr.tr Normal file
View File

@ -0,0 +1,62 @@
# textdomain: nether
# Translation FR by Louis Royer
### init.lua ###
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.@n@nThis 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.@n@nThe 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.=Cette construction nécessite 14 blocs dobsidienne, qui peut être trouvée profondément sous terre, là où leau a solidifié de la roche fondue. Une fois terminé, le cadre fait quatre blocs de large, cinq blocs de haut, et se tient verticalement comme une porte.@n@nElle ouvre sur un lieu vraiment infernal, mais on peut sestimer heureux que lair y soit quand même respirable. Il y a un décalage dimensionnel intrigant entre ce monde et le nôtre, car après avoir ouvert un deuxième portail, nous avons observé que 10 pas effectués dans le Nether semblent être léquivalent de @1 dans notre monde.@n@nLes membres de lexpédition nontr trouvé ni diamants ni or, et après quun groupe de recheche expérimenté nait pas réussi à retrouver la piste dun membre de lexpédition disparu, je nai dautre choix que de conclure que cet endroit est trop dangereux pour nous.
### init.lua ###
### nodes.lua ###
Nether Portal=Portail du Nether
### nodes.lua ###
Glowstone=Pierre lumineuse
Inner Nether Stair=Escalier intérieur du Nether
Nether Brick=Brique du Nether
Nether Brick Fence=Barrière en briques du Nether
Nether Slab=Dalle du Nether
Nether Stair=Escalier du Nether
Netherrack=Roche du Nether
Nethersand=Sable du Nether
Outer Nether Stair=Escalier extérieur du Nether
### portal_api.lua ###
@n@nThe key to opening such a doorway is to strike the frame with a @1, at which point the very air inside begins to crackle and glow.=@n@nLa méthode pour ouvrir une telle porte est de frapper son cadre avec un @1, jusquà ce que tout lair à lintérieur commence à crépiter et briller.
A definitive guide to Rifts and Portals=Un guide détaillé des failles et des portails
A guidebook for how to build portals to other realms. It can sometimes be found in dungeon chests, however a copy of this book is not needed as its contents are included in this Encyclopedia.=Un guide sur comment construire des portails vers dautres mondes. Il peut parfois être trouvé dans des coffres de dongeons, cependant la copie de ce livre nest pas nécessaire puisque son contenu est inclus dans lencyclopédie.
Book of Portals=Livre des portails
Building Portals=Construire des portails
In all my travels, and time spent in the Great Libraries, I have encountered no shortage of legends surrounding preternatural doorways said to open into other worlds, yet only @1 can I confirm as being more than merely stories.=Après tous mes voyages, et le temps passé dans les Grandes Bibliothèques, je ne manque pas de légendes sur les portes surnaturelles qui, dit-on souvrent vers dautres mondes, mais seul @1 peut confirmer que ce sont plus que de simples histoires.
In all my travels, and time spent in the Great Libraries, I have encountered no shortage of legends surrounding preternatural doorways said to open into other worlds, yet only one can I confirm as being more than merely a story.=Après tous mes voyages, et le temps passé dans les Grandes Bibliothèques, je ne manque pas de légendes sur les portes surnaturelles qui, dit-on souvrent vers dautres mondes, mais seul une personne peut confirmer que ce sont plus que de simples histoires.
Portal wormhole=Vortex du portail
Portals to other realms can be opened by building a frame in the right shape with the right blocks, then using an item to activate it. A local copy of the guidebook to portals is published below.@n---@n@n=Les portails vers dautres mondes peuvent être ouvert en construisant un cadre de la bonne forme avec les bons blocs, puis en utilisant un objet pour lactiver. Une copie du guide des portails est ci-dessous.@n---@n@n
Refer: "Help" > "Basics" > "Building Portals"=Voir : Aide > Bases > Construire des portails
Untitled portal=Portail sans nom
We know almost nothing about this portal=Nous ne savons presque rien sur les portails
### portal_examples.lua ###
Floatlands Portal=Portail du monde flottant
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.@n@nThese 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.@n@nDue 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.=Nécessite 16 blocs détain placés de manière circulaire, le cadre final fait sept blocs de large, sept blocs de haut, et se tient verticalement comme une porte.@n@nIls permettent de voyager une distance sous le sol et se relieent à la surface même sils sont construits profondément sous terre. Ils ont lair de préférer une direction étrange, avec le portail de sortie ne se reliant au portail dentrée que tant quils restent tous deux ouverts tenter de réouvrir le portail de sortie mènera à une nouvelle destination dans cette direction privilégiée.Cela a entravé notre capacité à étudier le comportement de ces portails, car sans la construction de doubles portails et en gardant les deux ouverts, il est difficile d'en traverser plus d'un et de pouvoir rentrer chez soi.@n@nEn raison de ces difficultés, nous n'avons jamais appris ce qui détermine la direction et la distance à laquelle le portail jumeau correspondant apparaîtra, et jai perdu mon ami et mon protégé. Dans sa jeunesse cavalière et avec à peine plus qu'un sac à dos, Coudreau a décidé de suivre la chaîne jusqu'au bout, et n'a pas été vu depuis. Coudreau croit qu'elle fonctionne sur les épicycles, mais je n'en suis pas convaincu. Je m'accroche néanmoins à l'espoir qu'un jour le portail s'ouvrira et que Coudreau sortira de l'endroit qui mène à celui-ci, peut-être avec un récit épique à raconter.
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.@n@nThis 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=Nécessite 21 blocs de glace placés pour former une plateforme de 3 × 3 avec des murs, ou comme un bol. La plateforme finale fait 2 blocs de haut, et 5 blocs de large à sa largeur maximale dans les deux directions.@n@nCe portail est différent des autres, au lieu de ressembler à une porte, il ressemble plus à un petit bassin deau dans lequel on peut entrer. En mettant les pieds dans le portail, nous nous sommes retrouvés à une altitude énorme.@1
Surface Portal=Portail de surface
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.=Il y a là un monde flottant remplis de collines et de forêts, sur les bords duquel se trouve une chute périlleuse jusqu'au niveau de la mer. Nous n'avons pas encore trouvé jusqu'où s'étendent ces terres vierges. J'ai à moitié envie de m'y retirer un jour.

61
locale/template.txt Normal file
View File

@ -0,0 +1,61 @@
# textdomain: nether
### init.lua ###
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.@n@nThis 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.@n@nThe 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.=
### init.lua ###
### nodes.lua ###
Nether Portal=
### nodes.lua ###
Glowstone=
Inner Nether Stair=
Nether Brick=
Nether Brick Fence=
Nether Slab=
Nether Stair=
Netherrack=
Nethersand=
Outer Nether Stair=
### portal_api.lua ###
@n@nThe key to opening such a doorway is to strike the frame with a @1, at which point the very air inside begins to crackle and glow.=
A definitive guide to Rifts and Portals=
A guidebook for how to build portals to other realms. It can sometimes be found in dungeon chests, however a copy of this book is not needed as its contents are included in this Encyclopedia.=
Book of Portals=
Building Portals=
In all my travels, and time spent in the Great Libraries, I have encountered no shortage of legends surrounding preternatural doorways said to open into other worlds, yet only @1 can I confirm as being more than merely stories.=
In all my travels, and time spent in the Great Libraries, I have encountered no shortage of legends surrounding preternatural doorways said to open into other worlds, yet only one can I confirm as being more than merely a story.=
Portal wormhole=
Portals to other realms can be opened by building a frame in the right shape with the right blocks, then using an item to activate it. A local copy of the guidebook to portals is published below.@n---@n@n=
Refer: "Help" > "Basics" > "Building Portals"=
Untitled portal=
We know almost nothing about this portal=
### portal_examples.lua ###
Floatlands Portal=
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.@n@nThese 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.@n@nDue 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.=
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.@n@nThis 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=
Surface Portal=
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.=

View File

@ -113,7 +113,7 @@ 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
-- Ores and decorations can be 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", {})
@ -160,7 +160,7 @@ minetest.register_ore({
ore_type = "scatter",
ore = "default:lava_source",
wherein = "nether:rack",
clust_scarcity = 32 * 32 * 32,
clust_scarcity = 36 * 36 * 36,
clust_num_ores = 4,
clust_size = 2,
y_max = NETHER_CEILING,
@ -465,10 +465,15 @@ 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)
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
function nether.find_nether_ground_y(target_x, target_z, start_y, player_name)
local nobj_cave_point = minetest.get_perlin(np_cave)
local air = 0 -- Consecutive air nodes found
local minp_schem, maxp_schem = nether.get_schematic_volume({x = target_x, y = 0, z = target_z}, nil, "nether_portal")
local minp = {x = minp_schem.x, y = 0, z = minp_schem.z}
local maxp = {x = maxp_schem.x, y = 0, z = maxp_schem.z}
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})
@ -476,13 +481,14 @@ function nether.find_nether_ground_y(target_x, target_z, start_y)
air = air + 1
else -- Not cavern, check if 4 nodes of space above
if air >= 4 then
local portal_y = y + 1
-- 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
minp.y = minp_schem.y + portal_y
maxp.y = maxp_schem.y + portal_y
if nether.volume_is_natural_and_unprotected(minp, maxp, player_name) then
return portal_y
else -- Restart search a little lower
nether.find_nether_ground_y(target_x, target_z, y - 16)
nether.find_nether_ground_y(target_x, target_z, y - 16, player_name)
end
else -- Not enough space, reset air to zero
air = 0

View File

@ -25,12 +25,22 @@
-- 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}
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}
local F = {name = "nether:fumarole", prob = 255, force_place = true}
local FS = {name = "nether:fumarole_slab", prob = 255, force_place = true}
local F1 = {name = "nether:fumarole_corner", prob = 255, force_place = true, param2 = 0}
local F2 = {name = "nether:fumarole_corner", prob = 255, force_place = true, param2 = 1}
local F3 = {name = "nether:fumarole_corner", prob = 255, force_place = true, param2 = 2}
local F4 = {name = "nether:fumarole_corner", prob = 255, force_place = true, param2 = 3}
local S1 = {name = "stairs:stair_netherrack", prob = 255, force_place = true, param2 = 5}
local S2 = {name = "stairs:stair_netherrack", prob = 255, force_place = true, param2 = 7}
local S3 = {name = "stairs:stair_netherrack", prob = 255, force_place = true, param2 = 12}
local S4 = {name = "stairs:stair_netherrack", prob = 255, force_place = true, param2 = 16}
-- =================
@ -39,7 +49,7 @@ local L = {name = "default:lava_source", prob = 255, force_place = true}
local schematic_GlowstoneStalactite = {
size = {x = 5, y = 10, z = 5},
data = {
data = { -- note that data is upside down
_, _, _, _, _,
_, _, _, _, _,
_, _, _, _, _,
@ -113,7 +123,7 @@ minetest.register_decoration({
deco_type = "schematic",
place_on = "nether:rack",
sidelen = 80,
fill_ratio = 0.0004,
fill_ratio = 0.0003,
biomes = {"nether_caverns"},
y_max = nether.DEPTH_CEILING, -- keep compatibility with mapgen_nobiomes.lua
y_min = nether.DEPTH_FLOOR,
@ -127,7 +137,7 @@ minetest.register_decoration({
deco_type = "schematic",
place_on = "nether:rack",
sidelen = 80,
fill_ratio = 0.0007,
fill_ratio = 0.0008,
biomes = {"nether_caverns"},
y_max = nether.DEPTH_CEILING, -- keep compatibility with mapgen_nobiomes.lua
y_min = nether.DEPTH_FLOOR,
@ -136,3 +146,254 @@ minetest.register_decoration({
flags = "place_center_x,place_center_z,all_ceilings",
place_offset_y=-3
})
-- =======================================
-- Concealed crevice / Lava sinkhole
-- =======================================
-- if player places a torch/block on this sand or digs it while standing on it, it sinks into lava
if allow_lava_decorations then
minetest.register_decoration({
name = "Weak trap",
deco_type = "schematic",
place_on = "nether:rack",
sidelen = 80,
fill_ratio = 0.002,
biomes = {"nether_caverns"},
y_max = nether.DEPTH, -- keep compatibility with mapgen_nobiomes.lua
y_min = nether.DEPTH_FLOOR,
schematic = {
size = {x = 4, y = 7, z = 4},
data = { -- note that data is upside down
_, _, _, _,
_, _, _, _,
_, N, _, _,
_, N, N, _,
_, N, N, _,
_, N, N, _,
_, _, _, _,
_, N, _, _, -- make it look like a stalactite if it protrudes out the bottom of a landform
_, N, _, _,
N, L, N, _,
N, L, L, N,
N, L, L, N,
N, A, A, N,
_, S, S, _,
_, _, _, _,
_, _, _, _,
_, N, N, _,
N, L, L, N,
N, L, L, N,
N, A, A, N,
_, S, S, _,
_, _, _, _,
_, _, _, _,
_, _, _, _,
_, N, N, _,
_, N, N, _,
_, N, N, _,
_, _, _, _,
}
},
replacements = {["nether:glowstone"] = "nether:rack"},
flags = "place_center_x,place_center_z,force_placement, all_floors",
place_offset_y=-6,
rotation = "random"
})
end
-- ==========================
-- Fumaroles (Chimneys)
-- ==========================
local replacements_slab = {}
local replacements_full = {["nether:fumarole_slab"] = "nether:fumarole"}
if allow_lava_decorations then
-- Minetest engine limitations mean any mesh or nodebox node (like nether:fumarole)
-- will light up if it has lava below it, so replace the air node over the lava with
-- a node that prevents light propagation.
-- (Unfortunately this also means if a player digs down to get the lava block it'll
-- look like the lighting wasn't set in the block above the lava)
replacements_slab["air"] = "nether:airlike_darkness"
replacements_full["air"] = "nether:airlike_darkness"
else
-- Lava is frequently removed by the old mapgen, so put sand at the bottom
-- of fumaroles.
replacements_slab["default:lava_source"] = "nether:sand"
replacements_full["default:lava_source"] = "nether:sand"
end
local schematic_fumarole = {
size = {x = 3, y = 5, z = 3},
data = { -- note that data is upside down
_, _, _,
_, N, _,
_, N, _,
_, _, _,
_, _, _,
_, N, _,
N, L, N,
N, A, N,
_, F, _,
_,FS, _,
_, _, _,
_, N, _,
_, N, _,
_, _, _,
_, _, _,
},
}
-- Common fumarole decoration that's flush with the floor and spawns everywhere
minetest.register_decoration({
name = "Sunken nether fumarole",
deco_type = "schematic",
place_on = {"nether:rack"},
sidelen = 80,
fill_ratio = 0.005,
biomes = {"nether_caverns"},
y_max = nether.DEPTH,
y_min = nether.DEPTH_FLOOR,
schematic = schematic_fumarole,
replacements = replacements_full,
flags = "place_center_x,place_center_z,all_floors",
place_offset_y=-4
})
-- Rarer formations of raised fumaroles in clumps
local fumarole_clump_noise_offset = -0.58
local fumarole_clump_noise = {
offset = fumarole_clump_noise_offset,
scale = 0.5,
spread = {x = 40, y = 40, z = 15},
octaves = 4,
persist = 0.65,
lacunarity = 2.0,
}
fumarole_clump_noise.offset = fumarole_clump_noise_offset - 0.035
minetest.register_decoration({
name = "Raised Nether fumarole",
deco_type = "schematic",
place_on = {"nether:rack"},
sidelen = 8,
noise_params = fumarole_clump_noise,
biomes = {"nether_caverns"},
y_max = nether.DEPTH,
y_min = nether.DEPTH_FLOOR,
schematic = schematic_fumarole,
replacements = replacements_full,
flags = "place_center_x,place_center_z,all_floors",
place_offset_y=-3
})
fumarole_clump_noise.offset = fumarole_clump_noise_offset
minetest.register_decoration({
name = "Half-raised Nether fumarole",
deco_type = "schematic",
place_on = {"nether:rack"},
sidelen = 8,
noise_params = fumarole_clump_noise,
biomes = {"nether_caverns"},
y_max = nether.DEPTH,
y_min = nether.DEPTH_FLOOR,
schematic = schematic_fumarole,
replacements = replacements_slab,
flags = "place_center_x,place_center_z,all_floors",
place_offset_y=-3
})
fumarole_clump_noise.offset = fumarole_clump_noise_offset - 0.035
minetest.register_decoration({
name = "Nether fumarole mound",
deco_type = "schematic",
place_on = {"nether:rack"},
sidelen = 8,
noise_params = fumarole_clump_noise,
biomes = {"nether_caverns"},
y_max = nether.DEPTH,
y_min = nether.DEPTH_FLOOR,
schematic = {
size = {x = 4, y = 4, z = 4},
data = { -- note that data is upside down
_, _, _, _,
_, N, N, _,
_, _, _, _,
_, _, _, _,
_, S, S, _,
N, A, A, N,
_, S2, S1, _,
_, F2, F1, _,
_, S, S, _,
N, A, A, N,
_, S3, S4, _,
_, F3, F4, _,
_, _, _, _,
_, N, N, _,
_, _, _, _,
_, _, _, _
},
yslice_prob = {{ypos = 3, prob = 192}} -- occasionally leave the fumarole cap off
},
flags = "place_center_x,place_center_z,all_floors",
place_offset_y = -2
})
fumarole_clump_noise.offset = fumarole_clump_noise_offset - 0.01
minetest.register_decoration({
name = "Double Nether fumarole",
deco_type = "schematic",
place_on = {"nether:rack"},
sidelen = 8,
noise_params = fumarole_clump_noise,
biomes = {"nether_caverns"},
y_max = nether.DEPTH,
y_min = nether.DEPTH_FLOOR,
schematic = {
size = {x = 4, y = 5, z = 4},
data = { -- note that data is upside down
_, _, _, _,
_, N, N, _,
_, _, _, _,
_, _, _, _,
_, _, _, _,
_, S, S, _,
N, A, A, N,
_, S2, S1, _,
_, F2, F, _,
_, _, FS, _,
_, S, S, _,
F, A, A, N, -- the F may add slight variance in landforms where it gets exposed
_, S3, S4, _,
_, F3, F4, _,
_, _, _, _,
_, _, _, _,
_, N, N, _,
_, _, _, _,
_, _, _, _,
_, _, _, _
}
},
flags = "place_center_x,place_center_z,all_floors",
place_offset_y = -2,
rotation = "random"
})

View File

@ -201,24 +201,30 @@ 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)
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
function nether.find_nether_ground_y(target_x, target_z, start_y, player_name)
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 minp_schem, maxp_schem = nether.get_schematic_volume({x = target_x, y = 0, z = target_z}, nil, "nether_portal")
local minp = {x = minp_schem.x, y = 0, z = minp_schem.z}
local maxp = {x = maxp_schem.x, y = 0, z = maxp_schem.z}
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
local portal_y = y + 1
-- 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
minp.y = minp_schem.y + portal_y
maxp.y = maxp_schem.y + portal_y
if nether.volume_is_natural_and_unprotected(minp, maxp, player_name) then
return portal_y
else -- Restart search a little lower
nether.find_nether_ground_y(target_x, target_z, y - 16)
nether.find_nether_ground_y(target_x, target_z, y - 16, player_name)
end
else -- Not enough space, reset air to zero
air = 0
@ -226,5 +232,5 @@ function nether.find_nether_ground_y(target_x, target_z, start_y)
end
end
return start_y -- Fallback
end
return math.max(start_y, NETHER_FLOOR + BLEND) -- Fallback
end

View File

@ -1 +1,4 @@
name = nether
description = Adds a deep underground realm with different mapgen that you can reach with obsidian portals.
depends = stairs, default
optional_depends = moreblocks, mesecons, loot, dungeon_loot, doc_basics, fire

255
nodes.lua
View File

@ -103,6 +103,14 @@ minetest.register_node("nether:brick", {
sounds = default.node_sound_stone_defaults(),
})
minetest.register_node("nether:brick_compressed", {
description = S("Compressed Netherbrick"),
tiles = {"nether_brick_compressed.png"},
groups = {cracky = 3, level = 2},
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
})
local fence_texture =
"default_fence_overlay.png^nether_brick.png^default_fence_overlay.png^[makealpha:255,126,126"
@ -133,6 +141,18 @@ stairs.register_stair_and_slab(
{"nether_brick.png"},
S("Nether Stair"),
S("Nether Slab"),
default.node_sound_stone_defaults(),
nil,
S("Inner Nether Stair"),
S("Outer Nether Stair")
)
stairs.register_stair(
"netherrack",
"nether:rack",
{cracky = 2, level = 2},
{"nether_rack.png"},
S("Netherrack stair"),
default.node_sound_stone_defaults()
)
@ -149,6 +169,232 @@ if minetest.get_modpath("moreblocks") then
end
-- Fumaroles (Chimney's)
local function fumarole_startTimer(pos, timeout_factor)
if timeout_factor == nil then timeout_factor = 1 end
local next_timeout = (math.random(50, 900) / 10) * timeout_factor
minetest.get_meta(pos):set_float("expected_timeout", next_timeout)
minetest.get_node_timer(pos):start(next_timeout)
end
-- Create an LBM to start fumarole node timers
minetest.register_lbm({
label = "Start fumarole smoke",
name = "nether:start_fumarole",
nodenames = {"nether:fumarole"},
run_at_every_load = true,
action = function(pos, node)
local node_above = minetest.get_node({x = pos.x, y = pos.y + 1, z = pos.z})
if node_above.name == "air" then --and node.param2 % 4 == 0 then
fumarole_startTimer(pos)
end
end
})
local function set_fire(pos, extinguish)
local posBelow = {x = pos.x, y = pos.y - 1, z = pos.z}
if extinguish then
if minetest.get_node(pos).name == "fire:permanent_flame" then minetest.set_node(pos, {name="air"}) end
if minetest.get_node(posBelow).name == "fire:permanent_flame" then minetest.set_node(posBelow, {name="air"}) end
elseif minetest.get_node(posBelow).name == "air" then
minetest.set_node(posBelow, {name="fire:permanent_flame"})
elseif minetest.get_node(pos).name == "air" then
minetest.set_node(pos, {name="fire:permanent_flame"})
end
end
local function fumarole_onTimer(pos, elapsed)
local expected_timeout = minetest.get_meta(pos):get_float("expected_timeout")
if elapsed > expected_timeout + 10 then
-- The timer didn't fire when it was supposed to, so the chunk was probably inactive and has
-- just been approached again, meaning *every* fumarole's on_timer is about to go off.
-- Skip this event and restart the clock for a future random interval.
fumarole_startTimer(pos, 1)
return false
end
-- Fumaroles in the Nether can catch fire.
-- (if taken to the surface and used as cottage chimneys, they don't catch fire)
local inNether = pos.y <= nether.DEPTH and pos.y >= nether.DEPTH_FLOOR
local canCatchFire = inNether and minetest.registered_nodes["fire:permanent_flame"] ~= nil
local smoke_offset = 0
local timeout_factor = 1
local smoke_time_adj = 1
local posAbove = {x = pos.x, y = pos.y + 1, z = pos.z}
local extinguish = minetest.get_node(posAbove).name ~= "air"
if extinguish or (canCatchFire and math.floor(elapsed) % 7 == 0) then
if not extinguish then
-- fumarole gasses are igniting
smoke_offset = 1
timeout_factor = 0.22 -- reduce burning time
end
set_fire(posAbove, extinguish)
set_fire({x = pos.x + 1, y = pos.y + 1, z = pos.z}, extinguish)
set_fire({x = pos.x - 1, y = pos.y + 1, z = pos.z}, extinguish)
set_fire({x = pos.x, y = pos.y + 1, z = pos.z + 1}, extinguish)
set_fire({x = pos.x, y = pos.y + 1, z = pos.z - 1}, extinguish)
elseif inNether then
if math.floor(elapsed) % 3 == 1 then
-- throw up some embers / lava splash
local embers_particlespawn_def = {
amount = 6,
time = 0.1,
minpos = {x=pos.x - 0.1, y=pos.y + 0.0, z=pos.z - 0.1},
maxpos = {x=pos.x + 0.1, y=pos.y + 0.2, z=pos.z + 0.1},
minvel = {x = -.5, y = 4.5, z = -.5},
maxvel = {x = .5, y = 7, z = .5},
minacc = {x = 0, y = -10, z = 0},
maxacc = {x = 0, y = -10, z = 0},
minexptime = 1.4,
maxexptime = 1.4,
minsize = .2,
maxsize = .8,
texture = "^[colorize:#A00:255",
glow = 8
}
minetest.add_particlespawner(embers_particlespawn_def)
embers_particlespawn_def.texture = "^[colorize:#A50:255"
embers_particlespawn_def.maxvel.y = 3
embers_particlespawn_def.glow = 12
minetest.add_particlespawner(embers_particlespawn_def)
else
-- gas noises
minetest.sound_play("nether_fumarole", {
pos = pos,
max_hear_distance = 60,
gain = 0.24,
pitch = math.random(35, 95) / 100
})
end
else
-- we're not in the Nether, so can afford to be a bit more smokey
timeout_factor = 0.4
smoke_time_adj = 1.3
end
-- let out some smoke
minetest.add_particlespawner({
amount = 12 * smoke_time_adj,
time = math.random(40, 60) / 10 * smoke_time_adj,
minpos = {x=pos.x - 0.2, y=pos.y + smoke_offset, z=pos.z - 0.2},
maxpos = {x=pos.x + 0.2, y=pos.y + smoke_offset, z=pos.z + 0.2},
minvel = {x=0, y=0.7, z=-0},
maxvel = {x=0, y=0.8, z=-0},
minacc = {x=0.0,y=0.0,z=-0},
maxacc = {x=0.0,y=0.1,z=-0},
minexptime = 5,
maxexptime = 5.5,
minsize = 1.5,
maxsize = 7,
texture = "nether_smoke_puff.png",
})
fumarole_startTimer(pos, timeout_factor)
return false
end
minetest.register_node("nether:fumarole", {
description="Fumarolic Chimney",
tiles = {"nether_rack.png"},
on_timer = fumarole_onTimer,
after_place_node = function(pos, placer, itemstack, pointed_thing)
fumarole_onTimer(pos, 1)
return false
end,
is_ground_content = true,
groups = {cracky = 3, level = 2, fumarole=1},
paramtype = "light",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.5000, -0.2500, 0.5000, 0.5000},
{-0.5000, -0.5000, -0.5000, 0.5000, 0.5000, -0.2500},
{-0.5000, -0.5000, 0.2500, 0.5000, 0.5000, 0.5000},
{0.2500, -0.5000, -0.5000, 0.5000, 0.5000, 0.5000}
}
},
selection_box = {type = 'fixed', fixed = {-.5, -.5, -.5, .5, .5, .5}}
})
minetest.register_node("nether:fumarole_slab", {
description="Fumarolic Chimney Slab",
tiles = {"nether_rack.png"},
is_ground_content = true,
on_timer = fumarole_onTimer,
after_place_node = function(pos, placer, itemstack, pointed_thing)
fumarole_onTimer(pos, 1)
return false
end,
groups = {cracky = 3, level = 2, fumarole=1},
paramtype = "light",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.5000, -0.2500, 0.000, 0.5000},
{-0.5000, -0.5000, -0.5000, 0.5000, 0.000, -0.2500},
{-0.5000, -0.5000, 0.2500, 0.5000, 0.000, 0.5000},
{0.2500, -0.5000, -0.5000, 0.5000, 0.000, 0.5000}
}
},
selection_box = {type = 'fixed', fixed = {-.5, -.5, -.5, .5, 0, .5}},
collision_box = {type = 'fixed', fixed = {-.5, -.5, -.5, .5, 0, .5}}
})
minetest.register_node("nether:fumarole_corner", {
description="Fumarolic Chimney Corner",
tiles = {"nether_rack.png"},
is_ground_content = true,
groups = {cracky = 3, level = 2, fumarole=1},
paramtype = "light",
paramtype2 = "facedir",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.2500, -0.5000, 0.5000, 0.000, 0.5000, 0.000},
{-0.5000, -0.5000, 0.2500, 0.000, 0.5000, 0.000},
{-0.5000, -0.5000, 0.2500, 0.000, 0.000, -0.5000},
{0.000, -0.5000, -0.5000, 0.5000, 0.000, 0.5000}
}
},
selection_box = {
type = 'fixed',
fixed = {
{-.5, -.5, -.5, .5, 0, .5},
{0, 0, .5, -.5, .5, 0},
}
}
})
-- nether:airlike_darkness is an air node through which light does not propagate.
-- Use of it should be avoided when possible as it has the appearance of a lighting bug.
-- Fumarole decorations use it to stop the propagation of light from the lava below,
-- since engine limitations mean any mesh or nodebox node will light up if it has lava
-- below it.
local airlike_darkness = {}
for k,v in pairs(minetest.registered_nodes["air"]) do airlike_darkness[k] = v end
airlike_darkness.paramtype = "none"
minetest.register_node("nether:airlike_darkness", airlike_darkness)
-- Crafting
minetest.register_craft({
@ -167,4 +413,11 @@ minetest.register_craft({
},
})
minetest.register_craft({
output = "nether:brick_compressed",
recipe = {
{"nether:brick","nether:brick","nether:brick"},
{"nether:brick","nether:brick","nether:brick"},
{"nether:brick","nether:brick","nether:brick"},
}
})

View File

@ -23,8 +23,9 @@
]]--
local DEBUG = false
local DEBUG_IGNORE_MODSTORAGE = false -- setting true prevents portals from knowing where other portals are, forcing find_realm_anchorpos() etc. to be executed every time
-- setting DEBUG_IGNORE_MODSTORAGE true prevents portals from knowing where other
-- portals are, forcing find_realm_anchorpos() etc. to be executed every time.
local DEBUG_IGNORE_MODSTORAGE = false
nether.registered_portals = {}
nether.registered_portals_count = 0
@ -51,6 +52,11 @@ if minetest.get_mod_storage == nil then
error(nether.modname .. " does not support Minetest versions earlier than 0.4.16", 0)
end
local S = nether.get_translator
nether.portal_destination_not_found_message =
S("Mysterious forces prevented you from opening that portal. Please try another location")
--[[
Positions
@ -662,8 +668,8 @@ nether.PortalShape_Platform = {
-- Portal implementation functions --
-- =============================== --
local debugf = nether.debug
local ignition_item_name
local S = nether.get_translator
local mod_storage = minetest.get_mod_storage()
local meseconsAvailable = minetest.get_modpath("mesecon") ~= nil and minetest.global_exists("mesecon")
local book_added_as_treasure = false
@ -821,7 +827,7 @@ end
local function store_portal_location_info(portal_name, anchorPos, orientation, ignited)
if not DEBUG_IGNORE_MODSTORAGE then
local key = minetest.pos_to_string(anchorPos) .. " is " .. portal_name
if DEBUG then minetest.chat_send_all("Adding/updating portal in mod_storage: " .. key) end
debugf("Adding/updating portal in mod_storage: " .. key)
mod_storage:set_string(
key,
minetest.serialize({orientation = orientation, active = ignited})
@ -834,7 +840,7 @@ end
local function remove_portal_location_info(portal_name, anchorPos)
if not DEBUG_IGNORE_MODSTORAGE then
local key = minetest.pos_to_string(anchorPos) .. " is " .. portal_name
if DEBUG then minetest.chat_send_all("Removing portal from mod_storage: " .. key) end
debugf("Removing portal from mod_storage: " .. key)
mod_storage:set_string(key, "")
end
end
@ -872,7 +878,7 @@ local function list_closest_portals(portal_definition, anchorPos, distance_limit
local distance = math.hypot(y * y_factor, math.hypot(x, z))
if distance <= distance_limit or distance_limit < 0 then
local info = minetest.deserialize(value) or {}
if DEBUG then minetest.chat_send_all("found " .. found_name .. " listed at distance " .. distance .. " (within " .. distance_limit .. ") from dest " .. minetest.pos_to_string(anchorPos) .. ", found: " .. minetest.pos_to_string(found_anchorPos) .. " orientation " .. info.orientation) end
debugf("found %s listed at distance %.2f (within %.2f) from dest %s, found: %s orientation %s", found_name, distance, distance_limit, anchorPos, found_anchorPos, info.orientation)
info.anchorPos = found_anchorPos
info.distance = distance
result[distance] = info
@ -924,14 +930,14 @@ end
function extinguish_portal(pos, node_name, frame_was_destroyed)
-- mesecons seems to invoke action_off() 6 times every time you place a block?
if DEBUG then minetest.chat_send_all("extinguish_portal" .. minetest.pos_to_string(pos) .. " " .. node_name) end
debugf("extinguish_portal %s %s", pos, node_name)
local meta = minetest.get_meta(pos)
local p1 = minetest.string_to_pos(meta:get_string("p1"))
local p2 = minetest.string_to_pos(meta:get_string("p2"))
local target = minetest.string_to_pos(meta:get_string("target"))
if p1 == nil or p2 == nil then
if DEBUG then minetest.chat_send_all(" no active portal found to extinguish") end
debugf(" no active portal found to extinguish")
return false
end
@ -983,7 +989,7 @@ function extinguish_portal(pos, node_name, frame_was_destroyed)
end
if target ~= nil then
if DEBUG then minetest.chat_send_all(" attempting to also extinguish target with wormholePos " .. minetest.pos_to_string(target)) end
debugf(" attempting to also extinguish target with wormholePos %s", target)
extinguish_portal(target, node_name)
end
@ -1000,7 +1006,8 @@ end
-- Note: will extinguish any portal using the same nodes that are being set
local function set_portal_metadata(portal_definition, anchorPos, orientation, destination_wormholePos, ignite)
if DEBUG then minetest.chat_send_all("set_portal_metadata(ignite=" .. tostring(ignite) .. ") at " .. minetest.pos_to_string(anchorPos) .. " orient " .. orientation .. ", setting to target " .. minetest.pos_to_string(destination_wormholePos)) end
ignite = ignite or false;
debugf("set_portal_metadata(ignite=%s) at %s orient %s, setting to target %s", ignite, anchorPos, orientation, destination_wormholePos)
-- Portal position is stored in metadata as p1 and p2 to keep maps compatible with earlier versions of this mod.
-- p1 is the bottom/west/south corner of the portal, and p2 is the opposite corner, together
@ -1028,7 +1035,7 @@ local function set_portal_metadata(portal_definition, anchorPos, orientation, de
if existing_p1 ~= "" then
local existing_p2 = meta:get_string("p2")
if existing_p1 ~= p1_string or existing_p2 ~= p2_string then
if DEBUG then minetest.chat_send_all("set_portal_metadata() found existing metadata from another portal: existing_p1 " .. existing_p1 .. ", existing_p2" .. existing_p2 .. ", p1 " .. p1_string .. ", p2 " .. p2_string .. ", will existinguish existing portal...") end
debugf("set_portal_metadata() found existing metadata from another portal: existing_p1 %s, existing_p2 %s, p1 %s, p2 %s, will extinguish existing portal...", existing_p1, existing_p2, p1_string, p2_string)
-- this node is already part of another portal, so extinguish that, because nodes only
-- contain a link in the metadata to one portal, and being part of two allows a slew of bugs
extinguish_portal(pos, node_name, false)
@ -1092,7 +1099,7 @@ local function is_portal_at_anchorPos(portal_definition, anchorPos, orientation,
-- area isn't loaded, force loading/emerge of check area
minetest.get_voxel_manip():read_from_map(check_pos, check_pos)
foundName = minetest.get_node(check_pos).name
if DEBUG then minetest.chat_send_all("Forced loading of 'ignore' node at " .. minetest.pos_to_string(check_pos) .. ", got " .. foundName) end
debugf("Forced loading of 'ignore' node at %s, got %s", check_pos, foundName)
if foundName ~= frame_node_name then
nodes_are_valid = false
@ -1202,7 +1209,7 @@ local function build_portal(portal_definition, anchorPos, orientation, destinati
function(pos) minetest.swap_node(pos, wormholeNode) end
)
if DEBUG then minetest.chat_send_all("Placed " .. portal_definition.name .. " portal schematic at " .. minetest.pos_to_string(portal_definition.shape.get_schematicPos_from_anchorPos(anchorPos, orientation)) .. ", orientation " .. orientation) end
debugf("Placed %s portal schematic at %s, orientation %s", portal_definition.name, portal_definition.shape.get_schematicPos_from_anchorPos(anchorPos, orientation), orientation)
set_portal_metadata(portal_definition, anchorPos, orientation, destination_wormholePos)
@ -1216,7 +1223,7 @@ end
-- Make portals immortal for ~20 seconds after creation
local function remote_portal_checkup(elapsed, portal_definition, anchorPos, orientation, destination_wormholePos)
if DEBUG then minetest.chat_send_all("portal checkup at " .. elapsed .. " seconds") end
debugf("portal checkup at %d seconds", elapsed)
local wormholePos = portal_definition.shape.get_wormholePos_from_anchorPos(anchorPos, orientation)
local wormhole_node = minetest.get_node_or_nil(wormholePos)
@ -1231,7 +1238,7 @@ local function remote_portal_checkup(elapsed, portal_definition, anchorPos, orie
-- ruh roh
local message = "Newly created portal at " .. minetest.pos_to_string(anchorPos) .. " was overwritten. Attempting to recreate. Issue spotted after " .. elapsed .. " seconds"
minetest.log("warning", message)
if DEBUG then minetest.chat_send_all("!!! " .. message) end
debugf("!!! " .. message)
-- A pre-existing portal frame wouldn't have been immediately overwritten, so no need to check for one, just place the portal.
build_portal(portal_definition, anchorPos, orientation, destination_wormholePos)
@ -1259,7 +1266,7 @@ end
-- specified if an existing portal was already found there.
local function locate_or_build_portal(portal_definition, suggested_wormholePos, suggested_orientation, destination_wormholePos)
if DEBUG then minetest.chat_send_all("locate_or_build_portal() called at wormholePos" .. minetest.pos_to_string(suggested_wormholePos) .. " with suggested orient " .. suggested_orientation .. ", targetted to " .. minetest.pos_to_string(destination_wormholePos)) end
debugf("locate_or_build_portal() called at wormholePos%s with suggested orient %s, targeted to %s", suggested_wormholePos, suggested_orientation, destination_wormholePos)
local result_anchorPos;
local result_orientation;
@ -1280,13 +1287,13 @@ local function locate_or_build_portal(portal_definition, suggested_wormholePos,
if result_target ~= nil and vector.equals(result_target, destination_wormholePos) then
-- It already links back to the portal the player is teleporting from, so don't
-- extinguish it or the player's portal will also extinguish.
if DEBUG then minetest.chat_send_all(" Build unnecessary: already a lit portal that links back here at " .. minetest.pos_to_string(found_anchorPos) .. ", orientation " .. result_orientation) end
debugf(" Build unnecessary: already a lit portal that links back here at %s, orientation %s", found_anchorPos, result_orientation)
else
if DEBUG then minetest.chat_send_all(" Build unnecessary: already a lit portal at " .. minetest.pos_to_string(found_anchorPos) .. ", orientation " .. result_orientation .. ", linking to " .. result_target_str .. ". Extinguishing...") end
debugf(" Build unnecessary: already a lit portal at %s, orientation %s, linking to %s. Extinguishing...", found_anchorPos, result_orientation, result_target_str)
extinguish_portal(found_anchorPos, portal_definition.frame_node_name, false)
end
else
if DEBUG then minetest.chat_send_all(" Build unnecessary: already an unlit portal at " .. minetest.pos_to_string(found_anchorPos) .. ", orientation " .. result_orientation) end
debugf(" Build unnecessary: already an unlit portal at %s, orientation %s", found_anchorPos, result_orientation)
end
-- ignite the portal
set_portal_metadata_and_ignite(portal_definition, result_anchorPos, result_orientation, destination_wormholePos)
@ -1303,11 +1310,12 @@ end
-- invoked when a player attempts to turn obsidian nodes into an open portal
-- player_name is optional, allowing a player to spawn a remote portal in their own protected area
-- ignition_node_name is optional
local function ignite_portal(ignition_pos, ignition_node_name)
local function ignite_portal(ignition_pos, player_name, ignition_node_name)
if ignition_node_name == nil then ignition_node_name = minetest.get_node(ignition_pos).name end
if DEBUG then minetest.chat_send_all("IGNITE the " .. ignition_node_name .. " at " .. minetest.pos_to_string(ignition_pos)) end
debugf("IGNITE the %s at %s", ignition_node_name, ignition_pos)
-- find which sort of portals are made from the node that was clicked on
local portal_definition_list = list_portal_definitions_for_frame_node(ignition_node_name)
@ -1318,7 +1326,7 @@ local function ignite_portal(ignition_pos, ignition_node_name)
-- check it was a portal frame that the player is trying to ignite
local anchorPos, orientation, is_ignited = is_within_portal_frame(portal_definition, ignition_pos)
if anchorPos == nil then
if DEBUG then minetest.chat_send_all("No " .. portal_definition.name .. " portal frame found at " .. minetest.pos_to_string(ignition_pos)) end
debugf("No %s portal frame found at ", portal_definition.name, ignition_pos)
continue = true -- no portal is here, but perhaps there's more than one portal type we need to search for
elseif is_ignited then
-- Found a portal, check its metadata and timer is healthy.
@ -1330,10 +1338,10 @@ local function ignite_portal(ignition_pos, ignition_node_name)
-- metadata is missing, the portal frame node must have been removed without calling
-- on_destruct - perhaps by an ABM, then replaced - presumably by a player.
-- allowing reigniting will repair the portal
if DEBUG then minetest.chat_send_all("Broken portal detected, allowing reignition/repair") end
debugf("Broken portal detected, allowing reignition/repair")
repair = true
else
if DEBUG then minetest.chat_send_all("This portal links to " .. meta:get_string("target") .. ". p1=" .. meta:get_string("p1") .. " p2=" .. meta:get_string("p2")) end
debugf("This portal links to %s. p1=%s p2=%s", meta:get_string("target"), meta:get_string("p1"), meta:get_string("p2"))
-- Check the portal's timer is running, and fix if it's not.
-- A portal's timer can stop running if the game is played without that portal type being
@ -1341,7 +1349,7 @@ local function ignite_portal(ignition_pos, ignition_node_name)
-- (if this is a frequent problem, then change the value of "run_at_every_load" in the lbm)
local timer = minetest.get_node_timer(get_timerPos_from_p1_and_p2(minetest.string_to_pos(p1), minetest.string_to_pos(p2)))
if timer ~= nil and timer:get_timeout() == 0 then
if DEBUG then minetest.chat_send_all("Portal timer was not running: restarting the timer.") end
debugf("Portal timer was not running: restarting the timer.")
timer:start(1)
end
end
@ -1350,23 +1358,30 @@ local function ignite_portal(ignition_pos, ignition_node_name)
end
if continue == false then
if DEBUG then minetest.chat_send_all("Found portal frame. Looked at " .. minetest.pos_to_string(ignition_pos) .. ", found at " .. minetest.pos_to_string(anchorPos) .. " orientation " .. orientation) end
debugf("Found portal frame. Looked at %s, found at %s orientation %s", ignition_pos, anchorPos, orientation)
local destination_anchorPos, destination_orientation
if portal_definition.is_within_realm(ignition_pos) then
destination_anchorPos, destination_orientation = portal_definition.find_surface_anchorPos(anchorPos)
destination_anchorPos, destination_orientation = portal_definition.find_surface_anchorPos(anchorPos, player_name or "")
else
destination_anchorPos, destination_orientation = portal_definition.find_realm_anchorPos(anchorPos)
destination_anchorPos, destination_orientation = portal_definition.find_realm_anchorPos(anchorPos, player_name or "")
end
if destination_orientation == nil then
debugf("No destination_orientation given")
destination_orientation = orientation
end
if DEBUG and destination_orientation == nil then minetest.chat_send_all("No destination_orientation given") end
if destination_orientation == nil then destination_orientation = orientation end
if destination_anchorPos == nil then
if DEBUG then minetest.chat_send_all("No portal destination available here!") end
if destination_anchorPos == nil or destination_anchorPos.y == nil then
-- destination_anchorPos.y was also checked for nil in case portal_definition.find_surface_anchorPos()
-- had used nether.find_surface_target_y() and that had returned nil.
debugf("No portal destination available here!")
if (player_name or "") ~= "" then
minetest.chat_send_player(player_name, nether.portal_destination_not_found_message)
end
return false
else
local destination_wormholePos = portal_definition.shape.get_wormholePos_from_anchorPos(destination_anchorPos, destination_orientation)
if DEBUG then minetest.chat_send_all("Destination set to " .. minetest.pos_to_string(destination_anchorPos)) end
debugf("Destination set to %s", destination_anchorPos)
-- ignition/BURN_BABY_BURN
set_portal_metadata_and_ignite(portal_definition, anchorPos, orientation, destination_wormholePos)
@ -1405,7 +1420,7 @@ local function ensure_remote_portal_then_teleport(playerName, portal_definition,
local local_p1, local_p2 = portal_definition.shape:get_p1_and_p2_from_anchorPos(local_anchorPos, local_orientation)
local p1_at_playerPos = minetest.string_to_pos(meta:get_string("p1"))
if p1_at_playerPos == nil or not vector.equals(local_p1, p1_at_playerPos) then
if DEBUG then minetest.chat_send_all("the player already teleported from " .. minetest.pos_to_string(local_anchorPos) .. ", and is now standing in a different portal - " .. meta:get_string("p1")) end
debugf("the player already teleported from %s, and is now standing in a different portal - %s", local_anchorPos, meta:get_string("p1"))
return -- the player already teleported, and is now standing in a different portal
end
@ -1413,7 +1428,7 @@ local function ensure_remote_portal_then_teleport(playerName, portal_definition,
if dest_wormhole_node == nil then
-- area not emerged yet, delay and retry
if DEBUG then minetest.chat_send_all("ensure_remote_portal_then_teleport() could not find anything yet at " .. minetest.pos_to_string(destination_wormholePos)) end
debugf("ensure_remote_portal_then_teleport() could not find anything yet at %s", destination_wormholePos)
minetest.after(1, ensure_remote_portal_then_teleport, playerName, portal_definition, local_anchorPos, local_orientation, destination_wormholePos)
else
local local_wormholePos = portal_definition.shape.get_wormholePos_from_anchorPos(local_anchorPos, local_orientation)
@ -1430,9 +1445,9 @@ local function ensure_remote_portal_then_teleport(playerName, portal_definition,
local remoteMeta = minetest.get_meta(destination_wormholePos)
local remoteTarget = minetest.string_to_pos(remoteMeta:get_string("target"))
if remoteTarget == nil then
if DEBUG then minetest.chat_send_all("Failed to test whether target portal links back to this one") end
debugf("Failed to test whether target portal links back to this one")
elseif not vector.equals(remoteTarget, local_wormholePos) then
if DEBUG then minetest.chat_send_all("Target portal is already linked, extinguishing then relighting to point back at this one") end
debugf("Target portal is already linked, extinguishing then relighting to point back at this one")
extinguish_portal(remoteTarget, portal_definition.frame_node_name, false)
set_portal_metadata_and_ignite(
portal_definition,
@ -1442,7 +1457,7 @@ local function ensure_remote_portal_then_teleport(playerName, portal_definition,
)
end
if DEBUG then minetest.chat_send_all("Teleporting player from wormholePos" .. minetest.pos_to_string(local_wormholePos) .. " to wormholePos" .. minetest.pos_to_string(destination_wormholePos)) end
debugf("Teleporting player from wormholePos%s to wormholePos%s", local_wormholePos, destination_wormholePos)
-- play the teleport sound
if portal_definition.sounds.teleport ~= nil then
@ -1469,7 +1484,7 @@ local function ensure_remote_portal_then_teleport(playerName, portal_definition,
-- which will leave a confused player.
-- I don't think this is worth preventing, but I document it incase someone describes entering a portal
-- and then the portal turning off.
if DEBUG then minetest.chat_send_all("ensure_remote_portal_then_teleport() saw " .. dest_wormhole_node.name .. " at " .. minetest.pos_to_string(destination_wormholePos) .. " rather than a wormhole. Calling locate_or_build_portal()") end
debugf("ensure_remote_portal_then_teleport() saw %s at %s rather than a wormhole. Calling locate_or_build_portal()", dest_wormhole_node.name, destination_wormholePos)
local new_dest_anchorPos, new_dest_orientation = locate_or_build_portal(portal_definition, destination_wormholePos, local_orientation, local_wormholePos)
local new_dest_wormholePos = portal_definition.shape.get_wormholePos_from_anchorPos(new_dest_anchorPos, new_dest_orientation)
@ -1486,10 +1501,10 @@ local function ensure_remote_portal_then_teleport(playerName, portal_definition,
-- local portal to also be extinguished.
local message = "Local portal at " .. minetest.pos_to_string(local_anchorPos) .. " was extinguished while linking to existing portal at " .. minetest.pos_to_string(new_dest_anchorPos)
minetest.log("error", message)
if DEBUG then minetest.chat_send_all("!ERROR! - " .. message) end
debugf("!ERROR! - " .. message)
else
destination_wormholePos = new_dest_wormholePos
if DEBUG then minetest.chat_send_all(" updating target to where remote portal was found - " .. minetest.pos_to_string(destination_wormholePos)) end
debugf(" updating target to where remote portal was found - %s", destination_wormholePos)
set_portal_metadata(
portal_definition,
@ -1765,7 +1780,7 @@ local function create_book_of_portals()
ignition_item_description = minetest.registered_items[ignition_item_name].description
end
intro_text = intro_text ..
S("\n\nThe key to opening such a doorway is to strike the frame with a @1, at which point the very air inside begins to crackle and glow.", string.lower(ignition_item_description))
S("\n\nThe key to opening such a doorway is to strike the frame with a @1, at which point the very air inside begins to crackle and glow.", ignition_item_description)
chapters[#chapters + 1] = {text = intro_text}
@ -1824,22 +1839,22 @@ function register_frame_node(frame_node_name)
extended_node_def.replaced_by_portalapi.mesecons = extended_node_def.mesecons
extended_node_def.mesecons = {effector = {
action_on = function (pos, node)
if DEBUG then minetest.chat_send_all("portal frame material: mesecons action ON") end
ignite_portal(pos, node.name)
debugf("portal frame material: mesecons action ON")
ignite_portal(pos, nil, node.name)
end,
action_off = function (pos, node)
if DEBUG then minetest.chat_send_all("portal frame material: mesecons action OFF") end
debugf("portal frame material: mesecons action OFF")
extinguish_portal(pos, node.name, false)
end
}}
extended_node_def.replaced_by_portalapi.on_destruct = extended_node_def.on_destruct
extended_node_def.on_destruct = function(pos)
if DEBUG then minetest.chat_send_all("portal frame material: destruct") end
debugf("portal frame material: destruct")
extinguish_portal(pos, frame_node_name, true)
end
extended_node_def.replaced_by_portalapi.on_blast = extended_node_def.on_blast
extended_node_def.on_blast = function(pos, intensity)
if DEBUG then minetest.chat_send_all("portal frame material: blast") end
debugf("portal frame material: blast")
extinguish_portal(pos, frame_node_name, extended_node_def.replaced_by_portalapi.on_blast == nil)
if extended_node_def.replaced_by_portalapi.on_blast ~= nil then
extended_node_def.replaced_by_portalapi.on_blast(pos, intensity)
@ -1935,9 +1950,9 @@ minetest.register_lbm({
local timer = minetest.get_node_timer(timerPos)
if timer ~= nil then
timer:start(1)
if DEBUG then minetest.chat_send_all("LBM started portal timer " .. minetest.pos_to_string(timerPos)) end
elseif DEBUG then
minetest.chat_send_all("get_node_timer" .. minetest.pos_to_string(timerPos) .. " returned null")
debugf("LBM started portal timer %s", timerPos)
else
debugf("get_node_timer%s returned null", timerPos)
end
end
end
@ -2081,7 +2096,7 @@ function nether.register_portal(name, portaldef)
end
if portaldef.find_surface_anchorPos == nil then -- default to using find_surface_target_y()
portaldef.find_surface_anchorPos = function(pos)
portaldef.find_surface_anchorPos = function(pos, player_name)
local destination_pos = {x = pos.x, y = 0, z = pos.z}
local existing_portal_location, existing_portal_orientation =
@ -2089,7 +2104,7 @@ function nether.register_portal(name, portaldef)
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, name)
destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, name, player_name)
return destination_pos
end
end
@ -2153,19 +2168,24 @@ end
function nether.register_portal_ignition_item(item_name, ignition_failure_sound)
minetest.override_item(item_name, {
on_place = function(stack, _, pt)
on_place = function(stack, placer, pt)
local node = minetest.get_node(pt.under)
local def = minetest.registered_nodes[node.name]
local done = false
if pt.under and nether.is_frame_node[minetest.get_node(pt.under).name] then
done = ignite_portal(pt.under)
if pt.under and nether.is_frame_node[node.name] then
done = ignite_portal(pt.under, placer:get_player_name())
if done and not minetest.settings:get_bool("creative_mode") then
stack:take_item()
end
elseif def and def.on_rightclick then
def.on_rightclick(pt.under, node, placer, stack, pt)
end
if not done and ignition_failure_sound ~= nil then
minetest.sound_play(ignition_failure_sound, {pos = pt.under, max_hear_distance = 10})
end
return stack
end,
})
@ -2175,24 +2195,24 @@ end
-- use this when determining where to spawn a portal, to avoid overwriting player builds
-- It checks the area for any nodes that aren't ground or trees.
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
-- (Water also fails this test, unless it is unemerged)
function nether.volume_is_natural(minp, maxp)
function nether.volume_is_natural_and_unprotected(minp, maxp, player_name)
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 emin, emax = vm:read_from_map(minp, maxp)
local area = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
local data = vm:get_data()
for z = pos1.z, pos2.z do
for y = pos1.y, pos2.y do
local vi = area:index(pos1.x, y, z)
for x = pos1.x, pos2.x do
for z = minp.z, maxp.z do
for y = minp.y, maxp.y do
local vi = area:index(minp.x, y, z)
for x = minp.x, maxp.x do
local id = data[vi] -- Existing node
if DEBUG and id == nil then minetest.chat_send_all("nil block at index " .. vi) end
if id == nil then debugf("nil block at index " .. vi) end
if id ~= c_air and id ~= c_ignore and id ~= nil then -- checked for common natural or not emerged
local name = minetest.get_name_from_content_id(id)
local nodedef = minetest.registered_nodes[name]
@ -2200,7 +2220,7 @@ function nether.volume_is_natural(minp, maxp)
-- trees are natural but not "ground content"
local node_groups = nodedef.groups
if node_groups == nil or (node_groups.tree == nil and node_groups.leaves == nil and node_groups.leafdecay == nil) then
if DEBUG then minetest.chat_send_all("volume_is_natural() found unnatural node " .. name) end
debugf("volume_is_natural_and_unprotected() found unnatural node %s", name)
return false
end
end
@ -2210,13 +2230,73 @@ function nether.volume_is_natural(minp, maxp)
end
end
if DEBUG then minetest.chat_send_all("Volume is natural") end
if minetest.is_area_protected(minp, maxp, player_name or "") then
debugf("Volume is protected against player '%s', %s-%s", player_name, minp, maxp)
return false;
end
debugf("Volume is natural and unprotected for player '%s', %s-%s", player_name, minp, maxp)
return true
end
-- Deprecated, use nether.volume_is_natural_and_unprotected() instead.
function nether.volume_is_natural(minp, maxp)
if nether.deprecation_warning_volume_is_natural == nil then
local stack = debug.traceback("", 2);
local calling_func = (string.split(stack, "\n", false, 2, false)[2] or ""):trim()
minetest.log("warning",
"Deprecated function \"nether.volume_is_natural()\" invoked, use \"nether.volume_is_natural_and_unprotected()\" instead. " ..
calling_func)
nether.deprecation_warning_volume_is_natural = true;
end
return nether.volume_is_natural_and_unprotected(minp, maxp)
end
-- Gets the volume that may be altered if a portal is placed at the anchor_pos
-- orientation is optional, but specifying it will reduce the volume returned
-- portal_name is optional, but specifying it will reduce the volume returned
-- returns minp, maxp
function nether.get_schematic_volume(anchor_pos, orientation, portal_name)
if orientation == nil then
-- Return a volume large enough for any orientation
local minp0, maxp0 = nether.get_schematic_volume(anchor_pos, 0, portal_name)
local minp1, maxp1 = nether.get_schematic_volume(anchor_pos, 1, portal_name)
-- ToDo: If an asymmetric portal is used with an anchor not at the center of the
-- schematic then we will also need to check orientations 3 and 4.
-- (The currently existing portal-shapes are not affected)
return
{x = math.min(minp0.x, minp1.x), y = math.min(minp0.y, minp1.y), z = math.min(minp0.z, minp1.z)},
{x = math.max(maxp0.x, maxp1.x), y = math.max(maxp0.y, maxp1.y), z = math.max(maxp0.z, maxp1.z)}
end
-- Assume the largest possible portal shape unless we know it's a smaller one.
local shape_defintion = nether.PortalShape_Circular
if portal_name ~= nil and nether.registered_portals[portal_name] ~= nil then
shape_defintion = nether.registered_portals[portal_name].shape
end
local size = shape_defintion.schematic.size
local minp = shape_defintion.get_schematicPos_from_anchorPos(anchor_pos, orientation);
local maxp
if (orientation % 2) == 0 then
maxp = {x = minp.x + size.x - 1, y = minp.y + size.y - 1, z = minp.z + size.z - 1}
else
maxp = {x = minp.x + size.z - 1, y = minp.y + size.y - 1, z = minp.z + size.x - 1}
end
return minp, maxp
end
-- Can be used when implementing custom find_surface_anchorPos() functions
-- portal_name is optional, providing it allows existing portals on the surface to be reused.
function nether.find_surface_target_y(target_x, target_z, portal_name)
-- portal_name is optional, providing it allows existing portals on the surface to be reused, and
-- a potentially smaller volume to be checked by volume_is_natural_and_unprotected().
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
function nether.find_surface_target_y(target_x, target_z, portal_name, player_name)
assert(target_x ~= nil and target_z ~= nil, "Arguments `target_x` and `target_z` cannot be nil when calling find_surface_target_y()")
@ -2246,24 +2326,35 @@ function nether.find_surface_target_y(target_x, target_z, portal_name)
end
end
for y = start_y, start_y - 256, -16 do
local minp_schem, maxp_schem = nether.get_schematic_volume({x = target_x, y = 0, z = target_z}, nil, portal_name)
local minp = {x = minp_schem.x, y = 0, z = minp_schem.z}
local maxp = {x = maxp_schem.x, y = 0, z = maxp_schem.z}
-- Starting searchstep at -16 and making it larger by 2 after each step gives a 20-step search range down to -646:
-- 0, -16, -34, -54, -76, -100, -126, -154, -184, -216, -250, -286, -324, -364, -406, -450, -496, -544, -594, -646
local searchstep = -16;
local y = start_y
while y > start_y - 650 do
-- Check volume for non-natural nodes
local minp = {x = target_x - 1, y = y - 1, z = target_z - 2}
local maxp = {x = target_x + 2, y = y + 3, z = target_z + 2}
if nether.volume_is_natural(minp, maxp) then
minp.y = minp_schem.y + y
maxp.y = maxp_schem.y + y
if nether.volume_is_natural_and_unprotected(minp, maxp, player_name) then
return y
elseif portal_name ~= nil and nether.registered_portals[portal_name] ~= nil then
-- players have built here - don't grief.
-- but reigniting existing portals in portal rooms is fine - desirable even.
local anchorPos, orientation, is_ignited = is_within_portal_frame(nether.registered_portals[portal_name], {x = target_x, y = y, z = target_z})
if anchorPos ~= nil then
if DEBUG then minetest.chat_send_all("Volume_is_natural check failed, but a portal frame is here " .. minetest.pos_to_string(anchorPos) .. ", so this is still a good target y level") end
debugf("volume_is_natural_and_unprotected check failed, but a portal frame is here %s, so this is still a good target y level", anchorPos)
return y
end
end
y = y + searchstep
searchstep = searchstep - 2
end
return start_y - 256 -- Fallback
return nil -- Portal ignition failure. Possibly due to a large protected area.
end
@ -2286,7 +2377,7 @@ function nether.find_nearest_working_portal(portal_name, anchorPos, distance_lim
for _, dist in ipairs(dist_list) do
local portal_info = contenders[dist]
if DEBUG then minetest.chat_send_all("checking portal from mod_storage at " .. minetest.pos_to_string(portal_info.anchorPos) .. " orientation " .. portal_info.orientation) end
debugf("checking portal from mod_storage at %s orientation %s", portal_info.anchorPos, portal_info.orientation)
-- the mod_storage list of portals is unreliable - e.g. it won't know if inactive portals have been
-- destroyed, so check the portal is still there
@ -2295,10 +2386,11 @@ function nether.find_nearest_working_portal(portal_name, anchorPos, distance_lim
if portalFound then
return portal_info.anchorPos, portal_info.orientation
else
if DEBUG then minetest.chat_send_all("Portal wasn't found, removing portal from mod_storage at " .. minetest.pos_to_string(portal_info.anchorPos) .. " orientation " .. portal_info.orientation) end
debugf("Portal wasn't found, removing portal from mod_storage at %s orientation %s",
portal_info.anchorPos, portal_info.orientation)
-- The portal at that location must have been destroyed
remove_portal_location_info(portal_name, portal_info.anchorPos)
end
end
return nil
end
end

View File

@ -49,17 +49,24 @@ surface.
Helper functions
----------------
* `nether.volume_is_natural(minp, maxp)`: returns a boolean
* `nether.volume_is_natural_and_unprotected(minp, maxp, player_name)`: 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.
* player_name is optional, providing it allows the player's own protected
areas to be treated as unprotected.
* `nether.find_surface_target_y(target_x, target_z, portal_name)`: returns a
suitable anchorPos
* `nether.find_surface_target_y(target_x, target_z, portal_name, player_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.
* player_name is optional, providing it prevents the exclusion of surface
target areas which are protected by the player.
* May return nil in extreme circumstances, such as the surface being
protected down to a great depth.
* `nether.find_nearest_working_portal(portal_name, anchorPos, distance_limit, y_factor)`: returns
(anchorPos, orientation), or nil if no portal was found within the
@ -208,7 +215,7 @@ Used by `nether.register_portal`.
-- Ideally implementations are fast, as this function can be used to
-- sift through a list of portals.
find_realm_anchorPos = function(surface_anchorPos),
find_realm_anchorPos = function(surface_anchorPos, player_name),
-- Required. Return a position in the realm that a portal created at
-- surface_anchorPos will link to.
-- Return an anchorPos or (anchorPos, orientation)
@ -217,9 +224,12 @@ Used by `nether.register_portal`.
-- 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.
-- Return nil, or a position with a nil y component, to prevent the
-- portal from igniting.
-- player_name may be "", e.g. if the portal was ignited by a mesecon,
-- and is provided for use with volume_is_natural_and_unprotected() etc.
find_surface_anchorPos = function(realm_anchorPos),
find_surface_anchorPos = function(realm_anchorPos, player_name),
-- Optional. If you don't implement this then a position near the
-- surface will be picked.
-- Return an anchorPos or (anchorPos, orientation)
@ -232,7 +242,10 @@ Used by `nether.register_portal`.
-- 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.
-- Return nil, or a position with a nil y component, to prevent the
-- portal from igniting.
-- player_name may be "", e.g. if the portal was ignited by a mesecon,
-- and is provided for use with volume_is_natural_and_unprotected() etc.
on_run_wormhole = function(portalDef, anochorPos, orientation),
-- invoked once per second per portal

View File

@ -85,7 +85,7 @@ This portal is different to the others, rather than acting akin to a doorway it
return pos.y > FLOATLAND_LEVEL - 200
end,
find_realm_anchorPos = function(surface_anchorPos)
find_realm_anchorPos = function(surface_anchorPos, player_name)
-- 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}
@ -131,13 +131,13 @@ Due to such difficulties, we never learned what determines the direction and dis
return true
end,
find_realm_anchorPos = function(surface_anchorPos)
find_realm_anchorPos = function(surface_anchorPos, player_name)
-- 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)
find_surface_anchorPos = function(realm_anchorPos, player_name)
-- 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.
@ -192,7 +192,7 @@ Due to such difficulties, we never learned what determines the direction and dis
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")
destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, "surface_portal", player_name)
return destination_pos
end

BIN
sounds/nether_fumarole.ogg Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

119
tools.lua Normal file
View File

@ -0,0 +1,119 @@
local S = nether.get_translator
minetest.register_tool("nether:pick_nether", {
description = S("Nether Pickaxe"),
inventory_image = "nether_tool_netherpick.png",
tool_capabilities = {
full_punch_interval = 0.8,
max_drop_level=3,
groupcaps={
cracky = {times={[1]=1.90, [2]=0.9, [3]=0.4}, uses=35, maxlevel=3},
},
damage_groups = {fleshy=4},
},
sound = {breaks = "default_tool_breaks"},
groups = {pickaxe = 1}
})
minetest.register_tool("nether:shovel_nether", {
description = S("Nether Shovel"),
inventory_image = "nether_tool_nethershovel.png",
wield_image = "nether_tool_nethershovel.png^[transformR90",
tool_capabilities = {
full_punch_interval = 1.0,
max_drop_level=3,
groupcaps={
crumbly = {times={[1]=1.0, [2]=0.4, [3]=0.25}, uses=35, maxlevel=3},
},
damage_groups = {fleshy=4},
},
sound = {breaks = "default_tool_breaks"},
groups = {shovel = 1}
})
minetest.register_tool("nether:axe_nether", {
description = S("Nether Axe"),
inventory_image = "nether_tool_netheraxe.png",
tool_capabilities = {
full_punch_interval = 0.8,
max_drop_level=1,
groupcaps={
choppy={times={[1]=1.9, [2]=0.7, [3]=0.4}, uses=35, maxlevel=3},
},
damage_groups = {fleshy=7},
},
sound = {breaks = "default_tool_breaks"},
groups = {axe = 1}
})
minetest.register_tool("nether:sword_nether", {
description = S("Nether Sword"),
inventory_image = "nether_tool_nethersword.png",
tool_capabilities = {
full_punch_interval = 0.7,
max_drop_level=1,
groupcaps={
snappy={times={[1]=1.5, [2]=0.6, [3]=0.2}, uses=45, maxlevel=3},
},
damage_groups = {fleshy=10},
},
sound = {breaks = "default_tool_breaks"},
groups = {sword = 1}
})
minetest.register_craftitem("nether:nether_ingot", {
description = S("Nether Ingot"),
inventory_image = "nether_nether_ingot.png"
})
minetest.register_craftitem("nether:nether_lump", {
description = S("Nether Lump"),
inventory_image = "nether_nether_lump.png",
})
minetest.register_craft({
type = "cooking",
output = "nether:nether_ingot",
recipe = "nether:nether_lump",
cooktime = 30,
})
minetest.register_craft({
output = "nether:nether_lump",
recipe = {
{"nether:brick_compressed","nether:brick_compressed","nether:brick_compressed"},
{"nether:brick_compressed","nether:brick_compressed","nether:brick_compressed"},
{"nether:brick_compressed","nether:brick_compressed","nether:brick_compressed"},
}
})
minetest.register_craft({
output = "nether:pick_nether",
recipe = {
{"nether:nether_ingot","nether:nether_ingot","nether:nether_ingot"},
{"", "group:stick", ""},
{"", "group:stick", ""}
}
})
minetest.register_craft({
output = "nether:shovel_nether",
recipe = {
{"nether:nether_ingot"},
{"group:stick"},
{"group:stick"}
}
})
minetest.register_craft({
output = "nether:axe_nether",
recipe = {
{"nether:nether_ingot","nether:nether_ingot"},
{"nether:nether_ingot","group:stick"},
{"","group:stick"}
}
})
minetest.register_craft({
output = "nether:sword_nether",
recipe = {
{"nether:nether_ingot"},
{"nether:nether_ingot"},
{"group:stick"}
}
})