Compare commits
19 Commits
nalc-1.2.0
...
e0656eacae
Author | SHA1 | Date | |
---|---|---|---|
e0656eacae | |||
5a2a6c63f1 | |||
00bb132edb | |||
89db416d09 | |||
c7776cc082 | |||
bfdd8d18b4 | |||
09ca1efd62 | |||
f3c6f2df23 | |||
2fb93b8c74 | |||
60d4f8c7df | |||
1268049706 | |||
c70a669709 | |||
281d6fc07f | |||
02d062b9c9 | |||
97cf3250e4 | |||
e326a94266 | |||
5b3b56ebec | |||
c0481ea4ca | |||
8769593d6f |
@ -23,6 +23,7 @@ read_globals = {
|
||||
"PseudoRandom",
|
||||
"stairs",
|
||||
"stairsplus",
|
||||
"string.split",
|
||||
table = { fields = { "copy", "getn" } },
|
||||
"vector",
|
||||
"VoxelArea",
|
||||
|
16
README.md
@ -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.
|
@ -5,3 +5,4 @@ mesecons?
|
||||
loot?
|
||||
dungeon_loot?
|
||||
doc_basics?
|
||||
fire?
|
||||
|
60
init.lua
@ -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
@ -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 d’obsidienne, qui peut être trouvée profondément sous terre, là où l’eau 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 s’estimer heureux que l’air 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 l’expédition n’ontr trouvé ni diamants ni or, et après qu’un groupe de recheche expérimenté n’ait pas réussi à retrouver la piste d’un membre de l’expédition disparu, je n’ai d’autre 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 l’air à l’inté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 d’autres mondes. Il peut parfois être trouvé dans des coffres de dongeons, cependant la copie de ce livre n’est pas nécessaire puisque son contenu est inclus dans l’encyclopé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 s’ouvrent vers d’autres 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 s’ouvrent vers d’autres 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 d’autres mondes peuvent être ouvert en construisant un cadre de la bonne forme avec les bons blocs, puis en utilisant un objet pour l’activer. 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 s’ils sont construits profondément sous terre. Ils ont l’air de préférer une direction étrange, avec le portail de sortie ne se reliant au portail d’entrée que tant qu’ils 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 j’ai 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 d’eau 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
@ -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.=
|
||||
|
22
mapgen.lua
@ -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
|
||||
|
@ -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"
|
||||
})
|
@ -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
|
||||
|
3
mod.conf
@ -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
@ -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"},
|
||||
}
|
||||
})
|
||||
|
248
portal_api.lua
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
BIN
textures/nether_brick_compressed.png
Normal file
After Width: | Height: | Size: 342 B |
BIN
textures/nether_nether_ingot.png
Normal file
After Width: | Height: | Size: 203 B |
BIN
textures/nether_nether_lump.png
Normal file
After Width: | Height: | Size: 164 B |
BIN
textures/nether_smoke_puff.png
Normal file
After Width: | Height: | Size: 243 B |
BIN
textures/nether_tool_netheraxe.png
Normal file
After Width: | Height: | Size: 201 B |
BIN
textures/nether_tool_netherpick.png
Normal file
After Width: | Height: | Size: 223 B |
BIN
textures/nether_tool_nethershovel.png
Normal file
After Width: | Height: | Size: 231 B |
BIN
textures/nether_tool_nethersword.png
Normal file
After Width: | Height: | Size: 181 B |
119
tools.lua
Normal 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"}
|
||||
}
|
||||
})
|