Compare commits

..

35 Commits
v3 ... master

Author SHA1 Message Date
Treer
92431f2303
Fix client-side particle texture errors (#82)
Somewhere between minetest v5.8 and 5.9, it stopped allowing texture modifiers without a baseimg

ERROR[Main]: generateImage(): baseimg is NULL (attempted to create texture "")
ERROR[Main]: generateImagePart(): baseime == NUL for part_of_name="[colorize:#FB0:255", cancelling.
2024-12-19 20:01:41 +01:00
randomei
bd28f97946
Add Russian localization (#80) 2024-11-26 17:49:23 +01:00
Treer
b51b831483
Remove technic (fix issue #77) (#78)
nether added technic support on April 29, and technic added nether support on May 1, resulting in them both optionally depending on each other and becoming incompatible with each other.

To resolve this, technic will keep the nether support, the compressor recipe has been moved into technic (https://github.com/minetest-mods/technic/pull/644), and this commit reverts the technic-related parts of a6d1f55f, removing the technic dependency.
2024-07-16 21:21:15 +02:00
Mikita Wiśniewski
01b6f3c56d
No wear out in creative and replace add_wear with add_wear_by_uses (#76) 2024-05-20 20:22:39 +02:00
Diablosxm
a6d1f55f0a
Technic and Toolranks support (#73)
Co-authored-by: Treer <treer.git@gmail.com>
2024-04-28 18:53:46 +02:00
Jacob Lifshay
d16b530685
Call old on_place when not clicking on portal material (#74)
This is needed to work with telemosaic since telemosaic depends on `on_rightclick` being called on beacon node when wielding a mese crystal fragment.
2024-03-09 19:29:03 +01:00
Treer
ea677c5a1b
Hide unknown nodes under the nether bug (#72)
Hides bug #51 - the node_stone of the biome appears in the whole chunk under the Nether, so make nether:native_mapgen look the same as default:stone, and drop whatever default:stone drops
2023-05-06 14:44:54 +02:00
Treer
21c7e2801d restore string key in template
903025e accidently dropped a .
2023-04-16 01:38:43 +10:00
JoeEnderman
3470b1e22e add some French translations
I Hope all of the formatting is the same. I did check it multiple times.
2023-04-16 01:38:43 +10:00
Treer
3e809a811b
Merge pull request #65 from Treer/feature/fix-netherrack-slab-upstream
Update localization files
2023-04-15 12:51:51 +10:00
Treer
6c6a182366 Update localization files 2023-04-15 12:43:56 +10:00
JoeEnderman
9c1fa2c843
A fix for Netherrack slab being called Deep Netherrack slab (#63)
* fix netherrack slab being called Deep Netherrack
* Fix several lowercase block names

Several slabs and stairs were incorrectly named.
2023-04-15 12:34:20 +10:00
Treer
957fdc887b
Add giant crystal geodes (#35) 2023-01-26 15:59:05 +01:00
Treer
7dc32f6bb8
fix the sky in the nether caverns when using climate_api (#61)
The mantle and geode skys were working correctly, but not the sky in the main nether area - it would always switch back to stars.
Issue was found by process of elimination, so I don't know why climate_api doesn't accept "nether:nether" as a unique weather name identifier, or how this issue wasn't noticed earlier (did something change?).

climate_api's api_doc.md file says the name "should be prefixed with the mod's name in a way that could look like ``mymod:awesome_weather``. This name should only be used once."

so perhaps names of the form "mymod:mymod" cause issues? (I don't plan on figuring this climate_api behavior out though)
2022-11-20 01:05:03 +11:00
fluxionary
a8a6bb00c5
fumaroles: don't extinguish fires outside of the nether (#60)
thanks
2022-11-14 00:55:49 +11:00
fluxionary
12ca98c25a
Add gravel sounds to lava crust (#58) 2022-11-12 22:30:31 +11:00
Treer
a03375c5a4
Use Minetest_game fences API if it's available (#57)
Fixes #56, and adds netherbrick rails (for consistency with MTG) if the fences API is found.
Using the MTG fence system adds interop between different fence types, and support for the "Tall fences and walls" option.

I've left the nether's register_craft() call unchanged, it overrides the recipe that default.register_fence() registers, granting 6 fences per crafting instead of MTG's 4.
2022-09-11 14:51:23 +02:00
Treer
d3f79e961b Update locale templates 2022-02-13 14:14:03 +11:00
Treer
d8e6a6aca3
Treasures (#50)
* Add Netherrack slabs and walls

Also adds deep netherrack stairs, and deep nether brick stairs, slabs, inner stairs and outer stairs.
Adds 9 new nodes in total.

* Treasure: Nether pickaxe takes 10x less wear damage when mining netherrack

cracky-3 mining time of Nether pickaxe reduced from 0.4 to 0.3 to mine netherrack faster.
maxlevel of nether pickaxe dropped from 3 to 2 to increase wear damage with non-netherrack nodes.

* Treasure: Nether staff of light

Adds "Nether staff of Light" and "Nether staff of Eternal Light"
One is limited to 60 uses, and the other has unlimited uses but the glowstone it creates will only last for 40 seconds.
There are no crafting recipes as I hope these to eventually be treasure that can be found in the nether.

See the pull request (#50) for more discussion
2021-11-27 15:40:00 +11:00
Treer
247ca0844b Switch Deep-netherbrick to a texture with licence allowing adaption & redistribution (CC BY-SA 4.0)
To fix #52, while trying to capture some of the feel of the original texture.
2021-10-28 23:09:26 +11:00
Treer
52038017f3
Make ShiftExistingBiomes available via API (#40)
* bug fixes

Fixes wrong new_y_min when shift_existing_biomes() shifts a biome below the nether floor,
find_surface_anchorPos() no longer assumes y=0 will be outside the nether.
Nil-reference fixed when a mods tries to register a portal after mods are finished loading, but the portal shape+material was already registered by another mod.

* Make ShiftExistingBiomes available via API

makes the ShiftExistingBiomes function available to other mods via the nether global, since it's not a simple function and biomes would also need to be shifted if another mod wants to add a second nether layer.

* Allow layers to extend the depth of nether effects

Mods can set/lower nether.DEPTH_FLOOR_LAYERS when creating a layer under the nether. This allows multiple layer mods to know where their ceiling should start, and to be included in the effects which only happen in the nether.

* document nether API

More of a tentative interop guide than an API.
Use snake_case for API functions.
2021-07-18 16:44:37 +10:00
tenplus1
54613d673a Update README.md
add texture license info
2021-07-11 12:11:39 +10:00
tenplus1
023539c297
add deep nether brick (#47)
* add deep nether brick
* Update crafts.lua
2021-07-11 12:10:58 +10:00
tenplus1
b90afa0b86 fix nil bug in portal_api.lua
fixes a nil bug in portal_api.lua where nodedef.is_ground_content isn't available.
2021-07-08 10:14:24 +10:00
sys4-fr
fb6b666895
Fix deprecation warnings (#44)
Co-authored-by: sys4 <bricassa@sys4.fr>
2021-04-18 16:30:05 +02:00
Treer
fb3c358f74 #43 Ethereal compatibility
Ethereal clears every biome when it starts to get rid of the default mapgen, which breaks the nether and can cause crashes.
Adding Ethereal as an optional dependency ensures the nether is loaded after Ethereal has already cleared the biomes.
2021-03-14 16:37:48 +11:00
Treer
ecce6e27c9 #42 make lava crust walkable
Fixes issue #42 - mobs not being able to walk on lava_crust
2021-03-14 16:37:48 +11:00
Treer
3f4b014298 #41 Allow nether to be in the sky
fixes issue #41
(Now that the Nether no longer extends all the way to -31000)
2021-03-14 16:37:48 +11:00
Treer
bafa4e8ea1 Improve dungeons
* clears netherrack from dungeon stairways
* Adds pillars or mezzanine floors to large rooms
* slightly more careful with window placement
* glowstone sometimes added behind in windows
2021-02-13 16:47:26 +11:00
Treer
b30272d2dd Move dungeon excavation functions to separate file 2021-02-13 16:47:26 +11:00
Treer
87f0ea7a51 Implement PR feedback 2021-02-13 13:00:41 +11:00
Treer
3008b167b2 climate_api support - add distance fog
Add more appropriate/atmosphere distance fog to the nether, via climate_api mod to avoid conflicting with other mods.
Any game or server with climate_api mod installed will be expecting climate_api to take control of sky values.
2021-02-13 13:00:41 +11:00
Treer
00099f2aa2 Fix #36 - better handling of biome definition tables
Add support for min_pos and max_pos in biome definition tables, and follow the same interpretation logic as Minetest, same defaults/fallbacks etc.
2021-02-06 00:25:27 +11:00
Treer
89a467698a minor fixes
Fixes issue #34 (get_node_group deprecated), and removes nether:lava_source from creative_inventory
2021-02-02 17:15:22 +11:00
Treer
979493ed64 Fix nil reference
The error below was reported, so handle the existence of a mod called "bucket" which doesn't have any liquids field:

Runtime error from mod 'nether' in callback on_mods_loaded(): /mods/environment/nether/nodes.lua:378: attempt to index field 'liquids' (a nil value)
stack traceback:
/mods/environment/nether/nodes.lua:378: in function /mods/environment/nether/nodes.lua:373
/minetest/builtin/game/register.lua:429: in function /minetest/builtin/game/register.lua:413
2021-01-26 19:49:10 +11:00
26 changed files with 1733 additions and 382 deletions

View File

@ -2,10 +2,12 @@ unused_args = false
allow_defined_top = true
globals = {
"bucket",
"nether"
}
read_globals = {
"climate_api",
"core",
"default",
"DIR_DELIM",
@ -25,8 +27,11 @@ read_globals = {
"stairsplus",
"string.split",
table = { fields = { "copy", "getn" } },
"toolranks",
"vector",
"VoxelArea",
"VoxelManip",
"walls",
xpanes = { fields = { "register_pane" } },
}

View File

@ -16,6 +16,8 @@ right-click/used on the frame to activate it.
See portal_api.txt for how to create custom portals to your own realms.
Nether Portals can allow surface fast-travel.
This mod provides Nether basalts (natural, hewn, and chiseled) as nodes which
require a player to journey to the magma ocean to obtain, so these can be used
for gating progression through a game. For example, a portal to another realm
@ -24,8 +26,7 @@ the nether first, or basalt might be a crafting ingredient required to reach
a particular branch of the tech-tree.
Netherbrick tools are provided (pick, shovel, axe, & sword), see tools.lua
Nether Portals can allow surface fast-travel.
The Nether pickaxe has a 10x bonus against wear when mining netherrack.
## License of source code:
@ -50,9 +51,11 @@ 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_rack_destroy.ogg` is from "Rock destroy" by [Bertsz](https://freesound.org/people/Bertsz/), used under CC0 1.0
### [Attribution 3.0 Unported (CC BY 3.0)](https://creativecommons.org/licenses/by/3.0/)
* `nether_lightstaff.ogg` is "Fire Burst" by [SilverIllusionist](https://freesound.org/people/SilverIllusionist/), 2019
* `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.
@ -60,9 +63,13 @@ SOFTWARE.
### [Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/)
* `nether_basalt`* (files starting with "nether_basalt"): Treer, 2020
* `nether_book_`* (files starting with "nether_book"): Treer, 2019-2020
* `nether_brick_deep.png`: Treer, 2021
* `nether_fumarole.ogg`: Treer, 2020
* `nether_geode.png`: Treer, 2021
* `nether_geode_glass.png`: Treer, 2021
* `nether_lava_bubble`* (files starting with "nether_lava_bubble"): Treer, 2020
* `nether_lava_crust_animated.png`: Treer, 2019-2020
* `nether_lightstaff.png`: Treer, 2021
* `nether_particle_anim`* (files starting with "nether_particle_anim"): Treer, 2019
* `nether_portal_ignition_failure.ogg`: Treer, 2019
* `nether_smoke_puff.png`: Treer, 2020

View File

@ -44,6 +44,14 @@ minetest.register_craft({
}
})
minetest.register_craft({
output = "nether:brick_deep 4",
recipe = {
{"nether:rack_deep", "nether:rack_deep"},
{"nether:rack_deep", "nether:rack_deep"}
}
})
minetest.register_craft({
output = "nether:basalt_hewn",
type = "shapeless",

View File

@ -6,3 +6,5 @@ fire?
loot?
mesecons?
moreblocks?
climate_api?
xpanes?

111
init.lua
View File

@ -42,12 +42,18 @@ end
-- Global Nether namespace
nether = {}
nether.mapgen = {} -- Shared Nether mapgen namespace, for mapgen files to expose functions and constants
nether.modname = minetest.get_current_modname()
nether.path = minetest.get_modpath(nether.modname)
nether.get_translator = S
-- nether.useBiomes allows other mods to know whether they can register ores etc. in the Nether.
-- See mapgen.lua for an explanation of why minetest.read_schematic is being checked
nether.useBiomes = minetest.get_mapgen_setting("mg_name") ~= "v6" and minetest.read_schematic ~= nil
nether.fogColor = { -- only used if climate_api is installed
netherCaverns = "#1D0504", -- Distance-fog colour for classic nether
mantle = "#070916", -- Distance-fog colour for the Mantle region
geodes = "#300530" -- Distance-fog colour for secondary region
}
-- Settings
@ -70,6 +76,19 @@ if nether.DEPTH_FLOOR + 1000 > nether.DEPTH_CEILING then
end
nether.DEPTH = nether.DEPTH_CEILING -- Deprecated, use nether.DEPTH_CEILING instead.
-- DEPTH_FLOOR_LAYERS gives the bottom Y of all locations that wish to be
-- considered part of the Nether.
-- DEPTH_FLOOR_LAYERS Allows mods to insert extra layers below the
-- Nether, by knowing where their layer ceiling should start, and letting
-- the layers be included in effects which only happen in the Nether.
-- If a mod wishes to add a layer below the Nether it should read
-- nether.DEPTH_FLOOR_LAYERS to find the bottom Y of the Nether and any
-- other layers already under the Nether. The mod should leave a small gap
-- between DEPTH_FLOOR_LAYERS and its ceiling (e.g. use DEPTH_FLOOR_LAYERS - 6
-- for its ceiling Y, so there is room to shift edge-case biomes), then set
-- nether.DEPTH_FLOOR_LAYERS to reflect the mod's floor Y value, and call
-- shift_existing_biomes() with DEPTH_FLOOR_LAYERS as the floor_y argument.
nether.DEPTH_FLOOR_LAYERS = nether.DEPTH_FLOOR
-- A debug-print function that understands vectors etc. and does not
-- evaluate when debugging is turned off.
@ -149,7 +168,7 @@ This opens to a truly hellish place, though for small mercies the air there is s
The expedition parties have found no diamonds or gold, and after an experienced search party failed to return from the trail of a missing expedition party, I must conclude this is a dangerous place.]], 10 * nether.FASTTRAVEL_FACTOR),
is_within_realm = function(pos) -- return true if pos is inside the Nether
return pos.y < nether.DEPTH_CEILING
return pos.y < nether.DEPTH_CEILING and pos.y > nether.DEPTH_FLOOR
end,
find_realm_anchorPos = function(surface_anchorPos, player_name)
@ -157,7 +176,7 @@ The expedition parties have found no diamonds or gold, and after an experienced
local destination_pos = vector.divide(surface_anchorPos, nether.FASTTRAVEL_FACTOR)
destination_pos.x = math.floor(0.5 + destination_pos.x) -- round to int
destination_pos.z = math.floor(0.5 + destination_pos.z) -- round to int
destination_pos.y = nether.DEPTH_CEILING - 1000 -- temp value so find_nearest_working_portal() returns nether portals
destination_pos.y = nether.DEPTH_CEILING - 1 -- temp value so find_nearest_working_portal() returns nether portals
-- a y_factor of 0 makes the search ignore the altitude of the portals (as long as they are in the Nether)
local existing_portal_location, existing_portal_orientation =
@ -182,7 +201,7 @@ The expedition parties have found no diamonds or gold, and after an experienced
local destination_pos = vector.multiply(realm_anchorPos, nether.FASTTRAVEL_FACTOR)
destination_pos.x = math.min(30900, math.max(-30900, destination_pos.x)) -- clip to world boundary
destination_pos.z = math.min(30900, math.max(-30900, destination_pos.z)) -- clip to world boundary
destination_pos.y = 0 -- temp value so find_nearest_working_portal() doesn't return nether portals
destination_pos.y = nether.DEPTH_CEILING + 1 -- temp value so find_nearest_working_portal() doesn't return nether portals
-- a y_factor of 0 makes the search ignore the altitude of the portals (as long as they are outside the Nether)
local existing_portal_location, existing_portal_orientation =
@ -227,7 +246,91 @@ The expedition parties have found no diamonds or gold, and after an experienced
end
})
end
-- Set appropriate nether distance-fog if climate_api is available
--
-- Delegating to a mod like climate_api means nether won't unexpectedly stomp on the sky of
-- any other mod.
-- Skylayer is another mod which can perform this role, and skylayer support could be added
-- here as well. However skylayer doesn't provide a position-based method of specifying sky
-- colours out-of-the-box, so the nether mod will have to monitor when players enter and
-- leave the nether.
if minetest.get_modpath("climate_api") and minetest.global_exists("climate_api") and climate_api.register_weather ~= nil then
climate_api.register_influence(
"nether_biome",
function(pos)
local result = "surface"
if pos.y <= nether.DEPTH_CEILING and pos.y >= nether.DEPTH_FLOOR then
result = "nether"
-- since mapgen_nobiomes.lua has no regions it doesn't implement get_region(),
-- so only use get_region() if it exists
if nether.mapgen.get_region ~= nil then
-- the biomes-based mapgen supports 2 extra regions
local regions = nether.mapgen.RegionEnum
local region = nether.mapgen.get_region(pos)
if region == regions.CENTER or region == regions.CENTERSHELL then
result = "mantle"
elseif region == regions.NEGATIVE or region == regions.NEGATIVESHELL then
result = "geode"
end
end
end
return result
end
)
-- using sky type "plain" unfortunately means we don't get smooth fading transitions when
-- the color of the sky changes, but it seems to be the only way to obtain a sky colour
-- which doesn't brighten during the daytime.
local undergroundSky = {
sky_data = {
base_color = nil,
type = "plain",
textures = nil,
clouds = false,
},
sun_data = {
visible = false,
sunrise_visible = false
},
moon_data = {
visible = false
},
star_data = {
visible = false
}
}
local netherSky, mantleSky, geodeSky = table.copy(undergroundSky), table.copy(undergroundSky), table.copy(undergroundSky)
netherSky.sky_data.base_color = nether.fogColor.netherCaverns
mantleSky.sky_data.base_color = nether.fogColor.mantle
geodeSky.sky_data.base_color = nether.fogColor.geodes
climate_api.register_weather(
"nether:caverns",
{ nether_biome = "nether" },
{ ["climate_api:skybox"] = netherSky }
)
climate_api.register_weather(
"nether:mantle",
{ nether_biome = "mantle" },
{ ["climate_api:skybox"] = mantleSky }
)
climate_api.register_weather(
"nether:geode",
{ nether_biome = "geode" },
{ ["climate_api:skybox"] = geodeSky }
)
end
end -- end of "if nether.NETHER_REALM_ENABLED..."
-- Play bubbling lava sounds if player killed by lava

View File

@ -1,6 +1,6 @@
# textdomain: nether
# Translation FR by Louis Royer
# Translation FR by Louis Royer and JoeEnderman
### init.lua ###
@ -13,43 +13,78 @@ Construction requires 14 blocks of obsidian, which we found deep underground whe
Nether Portal=Portail du Nether
### mapgen_mantle.lua ###
, @1m above lava-sea level=, @1m au-dessus du niveau de la mer de lave
, @1m below lava-sea level=, @1m en-dessous du niveau de la mer de lave
, approaching y boundary of Nether=, approchant de la limite y du Nether
@1@2@3@4=
Center/Mantle, but outside the caverns=Centre/Manteau, mais à l'extérieur des cavernes
Center/Mantle, inside cavern=Centre/Manteau, à l'intérieur d'une caverne
Describes which region of the nether the player is in=Indique dans quelle région du Nether se trouve le joueur
Negative nether=Nether négatif
Positive nether=Nether positif
Shell between negative nether and center region=Coquille entre le Nether négatif et la région centrale
Shell between positive nether and center region=Coquille entre le Nether positif et la région centrale
The Overworld=L'Overworld
Unknown player position=Position du joueur inconnue
[Perlin @1] =
### nodes.lua ###
=
A finely finished block of solid Nether Basalt.=
A rough cut solid block of Nether Basalt.=
A thin crust of cooled lava with liquid lava beneath=
A vent in the earth emitting steam and gas=
Can be repurposed to provide puffs of smoke in a chimney=
Chiselled Basalt=
A Deep Netherrack Wall=Un mur profond en Netherrack
A Netherrack Wall=Un mur en Netherrack
A finely finished block of solid Nether Basalt.=Un bloc fini de basalte du Nether solide.
A rough cut solid block of Nether Basalt.=Un bloc solide de basalte du Nether taillé à la hache.
A thin crust of cooled lava with liquid lava beneath=Une croûte fine de lave refroidie avec de la lave liquide en dessous
A vent in the earth emitting steam and gas=Une fissure dans la terre émettant de la vapeur et du gaz
Can be repurposed to provide puffs of smoke in a chimney=Peut être réutilisé pour produire des bouffées de fumée dans une cheminée
Chiselled Basalt=Basalte sculpté
Columns of dark basalt found only in magma oceans deep within the Nether.=
Columns of dark basalt found only in magma oceans deep within the Nether.=Colonnes de basalte noir que l'on trouve uniquement dans les océans de magma profonds du Nether.
Compressed Netherbrick=
Cracked Nether Brick=
Deep Glowstone=
Deep Netherrack=
Fumarolic Chimney=
Fumarolic Chimney Corner=
Fumarolic Chimney Slab=
Compressed Netherbrick=Briques du Nether compressées
Cracked Nether Brick=Briques du Nether craquelées
Deep Glowstone=Pierre lumineuse profonde
Deep Nether Brick=Briques du Nether profondes
Deep Nether Slab=Dalle du Nether profonde
Deep Nether Stair=Escalier du Nether profond
Deep Netherrack=Netherrack profond
Deep Netherrack Slab=Dalle de Netherrack profonde
Deep Netherrack Stair=Escalier de Netherrack profond
Fumarolic Chimney=Cheminée fumarolique
Fumarolic Chimney Corner=Coin de cheminée fumarolique
Fumarolic Chimney Slab=Dalle de cheminée fumarolique
Glowstone=Pierre lumineuse
Hewn Basalt=
Hewn Basalt=Basalte taillé
Inner Deep Nether Stair=Escalier intérieur du Nether profond
Inner Nether Stair=Escalier intérieur du Nether
Lava Crust=
Lava Crust=Croûte de lave
Lava crust is strong enough to walk on, but still hot enough to inflict burns.=
Lava crust is strong enough to walk on, but still hot enough to inflict burns.=La croûte de lave est assez solide pour marcher dessus, mais encore assez chaude pour causer des brûlures
Nether Basalt=
Nether Basalt=Basalte du Nether
Nether Beryl=Béryl du Nether
Nether Berylite=Bérylite du Nether
Nether Brick=Brique du Nether
Nether Brick Fence=Barrière en briques du Nether
Nether Brick Fence Rail=Rail de barrière en briques du Nether
Nether Crystal Pane=Panneau de cristal du Nether
Nether Slab=Dalle du Nether
Nether Stair=Escalier du Nether
Nether geode crystal, found lining the interior walls of Nether geodes=Cristal de géode du Nether, trouvé le long des parois intérieures des géodes du Nether
Nether geode crystal. A crystalline structure with faint glow found inside large Nether geodes=Cristal de géode du Nether. Une structure cristalline avec une faible lueur trouvée à l'intérieur des grandes géodes du Nether.
Netherrack=Roche du Nether
Netherrack from deep in the mantle=
Netherrack stair=
Netherrack from deep in the mantle=Roche du Nether provenant des profondeurs du manteau
Netherrack Slab=Dalle du Nether
Netherrack Stair=Escalier du Nether
Nethersand=Sable du Nether
Outer Deep Nether Stair=Escalier extérieur profond du Nether
Outer Nether Stair=Escalier extérieur du Nether
Portal=
Portal=Portail
### portal_api.lua ###
@ -66,7 +101,7 @@ In all my travels, and time spent in the Great Libraries, I have encountered no
In all my travels, and time spent in the Great Libraries, I have encountered no shortage of legends surrounding preternatural doorways said to open into other worlds, yet only one can I confirm as being more than merely a story.=Après tous mes voyages, et le temps passé dans les Grandes Bibliothèques, je ne manque pas de légendes sur les portes surnaturelles qui, dit-on souvrent vers dautres mondes, mais seul une personne peut confirmer que ce sont plus que de simples histoires.
Mysterious forces prevented you from opening that portal. Please try another location=
Mysterious forces prevented you from opening that portal. Please try another location=Des forces mystérieuses vous empêchent d'ouvrir ce portail. Veuillez essayer un autre emplacement.
Portal wormhole=Vortex du portail
@ -91,9 +126,16 @@ There is a floating land of hills and forests up there, over the edges of which
### tools.lua ###
Nether Axe=
Nether Ingot=
Nether Lump=
Nether Pickaxe=
Nether Shovel=
Nether Sword=
Nether Axe=Hache du Nether
Nether Ingot=Lingot du Nether
Nether Lump=Morceau du Nether
Nether Pickaxe@nWell suited for mining netherrack=Pioche du Nether@nBien adaptée pour miner la roche du Nether
Nether Shovel=Pelle du Nether
Nether Sword=Épée du Nether
Nether staff of Eternal Light@nCreates glowstone from netherrack=Bâton du Nether de lumière éternelle@nCrée des blocs de pierre lumineuse à partir de roche du Nether
Nether staff of Light@nTemporarily transforms the netherrack into glowstone=Bâton du Nether de lumière@nTransforme temporairement la roche du Nether en blocs de pierre lumineuse
Uniquely suited for mining netherrack, with minimal wear when doing so. Blunts quickly on other materials.=Convient parfaitement pour miner la roche du Nether avec une usure minimale. S'émousse rapidement sur les autres matériaux.

140
locale/nether.ru.tr Normal file
View File

@ -0,0 +1,140 @@
# 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.=Конструкция требует 14 блоков обсидиана, который можно найти глубоко под землей, где с помощью воды затвердевает расплавленный камень. Законченная рамка в 4 блока шириной, 5 в высоту, стоящая вертикально как дверной проем.@n@nОткрывает действительно адское место, к счастью воздух здесь все еще пригоден для дыхания. После открытия второго портала в Незер мы обнаружили, что между этим миром и нашим происходит некое искривление простанства, благодаря которому 10 шагов в Незере равны @1 в нашем мире.@n@nРазведывательные команды не нашли золота или алмазов, и после того как опытная исследовательская команда сбилась со следа пропавшей команды, я вынужден констатировать, что это действительно опасное место.
### init.lua ###
### nodes.lua ###
Nether Portal=Незер-портал
### mapgen_mantle.lua ###
, @1m above lava-sea level=, @1м над уровнем моря лавы
, @1m below lava-sea level=, @1м под уровнем моря лавы
, approaching y boundary of Nether=, приближаетесь к y границы Незера
@1@2@3@4=
Center/Mantle, but outside the caverns=Центр/мантия, но вне пещер
Center/Mantle, inside cavern=Центр/мантия, внутри пещеры
Describes which region of the nether the player is in=Описывает в каком регионе Незера находится игрок
Negative nether=Отрицательный Незер
Positive nether=Положительный Незер
Shell between negative nether and center region=Оболочка между отрицательным Незер и центральным регионом
Shell between positive nether and center region=Оболочка между положительным Незер и центральным регионом
The Overworld=Внешний мир
Unknown player position=Неизвестная положение игрока
[Perlin @1] =
### nodes.lua ###
A Deep Netherrack Wall=Стена глубинного адского камня
A Netherrack Wall=Стена адского камня
A finely finished block of solid Nether Basalt.=Прекрасно обработанный блок незер-базальта.
A rough cut solid block of Nether Basalt.=Грубо разрезанный блок незер-базальта.
A thin crust of cooled lava with liquid lava beneath=Тонкая корка охлажденной лавы с жидкой лавой под ней.
A vent in the earth emitting steam and gas=Отверстие в земле, испускающее пар и газ
Can be repurposed to provide puffs of smoke in a chimney=Может быть использован для создания клубов дыма в дымоходе
Chiselled Basalt=Высеченный базальт
Columns of dark basalt found only in magma oceans deep within the Nether.=Колонный темного базальта можно наяти только в океанах магмы глубоко внутри Незера.
Compressed Netherbrick=Сжаты незер-кирпич
Cracked Nether Brick=Треснутый незер-кирпич
Deep Glowstone=Глубинный светокамень
Deep Nether Brick=Глубинный незер-кирпич
Deep Nether Slab=Глубинные незер (плита)
Deep Nether Stair=Глубинные незер (ступени)
Deep Netherrack=Глубинный адский камень
Deep Netherrack Slab=Глубинный адский камень (плита)
Deep Netherrack Stair=Глубинный адский камень (ступени)
Fumarolic Chimney=Фумарольный дымоход
Fumarolic Chimney Corner=Уголь фумарольного дымохода
Fumarolic Chimney Slab=Плита фумарольного дымохода
Glowstone=Светокамень
Hewn Basalt=Высеченный базальт
Inner Deep Nether Stair=Внутренние ступени из глубинного незера
Inner Nether Stair=Внутренние ступени из незера
Lava Crust=Лавовая корка
Lava crust is strong enough to walk on, but still hot enough to inflict burns.=Лавовая кока достаточно прочна, чтобы по ней ходить, однако также достаточно горяча, чтобы вызвать ожоги.
Nether Basalt=Незер-базальт
Nether Beryl=Незер-берилл
Nether Berylite=Незер-бериллит
Nether Brick=Незер-кирпич
Nether Brick Fence=Забор из незер-кирпича
Nether Brick Fence Rail=Перила забора из незер-кирпича
Nether Crystal Pane=Панель из незер-кристалла
Nether Slab=Незер-плита
Nether Stair=Незер-ступени
Nether geode crystal, found lining the interior walls of Nether geodes=Кристалл жеоды Незера, выстилает изнутри стены жеод Незера.
Nether geode crystal. A crystalline structure with faint glow found inside large Nether geodes=Кристалл жеоды Незера. Кристаллическая структура со слабым свечением, которую можно обнаружить внутри больших жеод Незера.
Netherrack=Адский камень
Netherrack from deep in the mantle=Адский камень из глубины мантии
Netherrack Slab=Адский камень (плита)
Netherrack Stair=Адский камень (ступени)
Nethersand=Незер-песок
Outer Deep Nether Stair=Внешние ступени из глубинного незера
Outer Nether Stair=Внешние ступени из незера
Portal=Портал
### 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@nКлюч к открытию такого прохода - это высечь пламя с помощью @1, при этом сам воздух внутри начинает трещать и светиться.
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.=Во время своих путешествий и долгих часов, проведённых в Великих Библиотеках, я обнаружил множество легенд о сверхъестественных порталах в иные миры. Лишь в @n случаях я могу подтвердить, что это не просто сказки.
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.=Во время своих путешествий и долгих часов, проведённых в Великих Библиотеках, я обнаружил множество легенд о сверхъестественных порталах в иные миры. Лишь в одном случае я могу подтвердить, что это не просто сказки.
Mysterious forces prevented you from opening that portal. Please try another location=Мистические силы не дают Вам открыть этот портал. Пожалуйста, попробуйте в другом месте.
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=Порталы в другие миры могут быть открыты с помощью рамки нужной формы из правильных блоков, а также с помощью предмета для активации. Локальная копия руководства по порталам находится ниже.@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.=Для создания портала требуется 16 блоков олова, выложенных по кругу; готовая рама достигает семи блоков в ширину и высоту, стоя вертикально, подобно двери.@n@nЭти порталы могут вести на большое расстояние, и даже если они построены глубоко под землёй, их выход зачастую находится на поверхности. Порталы, похоже, следуют странному направлению, где выход ведёт обратно только до тех пор, пока портал остаётся открытым. Если попытаться открыть портал заново из выходной точки, он приведёт в новое место по этому загадочному маршруту. Это осложняет наше изучение этих порталов, ведь без одновременного поддержания работы двух порталов трудно пройти через один из них и всё же вернуться обратно.@n@nИз-за таких трудностей мы так и не выяснили, что определяет направление и расстояние, на котором появится связанный портал. Я потерял друга и ученика. С юношеской бесшабашностью и с одним лишь рюкзаком на плечах, Кудро решил следовать по цепи порталов до самого конца и с тех пор не давал о себе знать. Кудро полагает, что порталы работают по принципу эпициклов, но я не убеждён. Тем не менее, я надеюсь, что однажды портал откроется и Кудро выйдет из него — оттуда, куда ведёт этот портал, — и, возможно, расскажет нам эпическую историю о своих странствиях.
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=Для создания этого портала требуется 21 блок льда, выложенных в форме платформы 3 × 3 с боковыми стенками, напоминающей чашу. Завершённая конструкция достигает высоты двух блоков и в самой широкой части составляет пять блоков в обе стороны.@n@nВ отличие от других порталов, этот скорее напоминает маленький водоём, в который можно шагнуть, чем обычную дверную арку. Когда мы ступили на поверхность портала, то внезапно оказались на невероятной высоте.@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.=Там, наверху, простирается парящий мир холмов и лесов, с опасными обрывами, за которыми — головокружительное падение обратно до самого уровня моря. Насколько далеко раскинулись эти нетронутые земли, мы пока не знаем. Порой я ловлю себя на мысли, что однажды стоило бы уйти туда навсегда.
### tools.lua ###
Nether Axe=Незер-топор
Nether Ingot=Незер-слиток
Nether Lump=Кусок незера
Nether Pickaxe@nWell suited for mining netherrack=Незер-кирка@nХорошо подходит для копания адского камня
Nether Shovel=Незер-лопата
Nether Sword=Незер-меч
Nether staff of Eternal Light@nCreates glowstone from netherrack=Незер-посох Вечного Света@nСоздает светокамень из адского камня
Nether staff of Light@nTemporarily transforms the netherrack into glowstone=Незер-посох Света@nВременно превращает адский камень в светокамень
Uniquely suited for mining netherrack, with minimal wear when doing so. Blunts quickly on other materials.=Идеально подходит для добычи адского камня, почти не изнашиваясь при этом. Однако быстро тупится при работе с другими материалами.

View File

@ -12,9 +12,27 @@ Construction requires 14 blocks of obsidian, which we found deep underground whe
Nether Portal=
### mapgen_mantle.lua ###
, @1m above lava-sea level=
, @1m below lava-sea level=
, approaching y boundary of Nether=
@1@2@3@4=
Center/Mantle, but outside the caverns=
Center/Mantle, inside cavern=
Describes which region of the nether the player is in=
Negative nether=
Positive nether=
Shell between negative nether and center region=
Shell between positive nether and center region=
The Overworld=
Unknown player position=
[Perlin @1] =
### nodes.lua ###
=
A Deep Netherrack Wall=
A Netherrack Wall=
A finely finished block of solid Nether Basalt.=
A rough cut solid block of Nether Basalt.=
A thin crust of cooled lava with liquid lava beneath=
@ -27,26 +45,43 @@ Columns of dark basalt found only in magma oceans deep within the Nether.=
Compressed Netherbrick=
Cracked Nether Brick=
Deep Glowstone=
Deep Nether Brick=
Deep Nether Slab=
Deep Nether Stair=
Deep Netherrack=
Deep Netherrack Slab=
Deep Netherrack Stair=
Fumarolic Chimney=
Fumarolic Chimney Corner=
Fumarolic Chimney Slab=
Glowstone=
Hewn Basalt=
Inner Deep Nether Stair=
Inner Nether Stair=
Lava Crust=
Lava crust is strong enough to walk on, but still hot enough to inflict burns.=
Nether Basalt=
Nether Beryl=
Nether Berylite=
Nether Brick=
Nether Brick Fence=
Nether Brick Fence Rail=
Nether Crystal Pane=
Nether Slab=
Nether Stair=
Nether geode crystal, found lining the interior walls of Nether geodes=
Nether geode crystal. A crystalline structure with faint glow found inside large Nether geodes=
Netherrack=
Netherrack from deep in the mantle=
Netherrack stair=
Netherrack Slab=
Netherrack Stair=
Nethersand=
Outer Deep Nether Stair=
Outer Nether Stair=
Portal=
@ -93,6 +128,13 @@ There is a floating land of hills and forests up there, over the edges of which
Nether Axe=
Nether Ingot=
Nether Lump=
Nether Pickaxe=
Nether Pickaxe@nWell suited for mining netherrack=
Nether Shovel=
Nether Sword=
Nether staff of Eternal Light@nCreates glowstone from netherrack=
Nether staff of Light@nTemporarily transforms the netherrack into glowstone=
Uniquely suited for mining netherrack, with minimal wear when doing so. Blunts quickly on other materials.=

View File

@ -45,7 +45,6 @@ local BASALT_COLUMN_LOWER_LIMIT = CENTER_CAVERN_LIMIT * 0.25 -- This value
-- Shared Nether mapgen namespace
-- For mapgen files to share functions and constants
nether.mapgen = {}
local mapgen = nether.mapgen
mapgen.TCAVE = TCAVE -- const needed in mapgen_mantle.lua
@ -69,8 +68,10 @@ if minetest.read_schematic == nil then
error("This " .. nether.modname .. " mapgen requires Minetest v5.1 or greater, use mapgen_nobiomes.lua instead.", 0)
end
-- Load helper functions for generating the mantle / center region
-- Load specialty helper functions
dofile(nether.path .. "/mapgen_dungeons.lua")
dofile(nether.path .. "/mapgen_mantle.lua")
dofile(nether.path .. "/mapgen_geodes.lua")
-- Misc math functions
@ -81,7 +82,8 @@ local math_max, math_min, math_abs, math_floor = math.max, math.min, math.abs, m
-- Inject nether_caverns biome
local function override_underground_biomes()
-- Move any existing biomes out of the y-range specified by 'floor_y' and 'ceiling_y'
mapgen.shift_existing_biomes = function(floor_y, ceiling_y)
-- https://forum.minetest.net/viewtopic.php?p=257522#p257522
-- Q: Is there a way to override an already-registered biome so I can get it out of the
-- way of my own underground biomes without disturbing the other biomes registered by
@ -108,7 +110,7 @@ local function override_underground_biomes()
end
for old_ore_key, old_ore_def in pairs(minetest.registered_ores) do
registered_ores_copy[old_ore_key] = old_ore_def
end
end
-- clear biomes, decorations, and ores
minetest.clear_registered_decorations()
@ -117,23 +119,36 @@ local function override_underground_biomes()
-- Restore biomes, adjusted to not overlap the Nether
for biome_key, new_biome_def in pairs(registered_biomes_copy) do
local biome_y_max, biome_y_min = tonumber(new_biome_def.y_max), tonumber(new_biome_def.y_min)
-- follow similar min_pos/max_pos processing logic as read_biome_def() in l_mapgen.cpp
local biome_y_max, biome_y_min = 31000, -31000
if type(new_biome_def.min_pos) == 'table' and type(new_biome_def.min_pos.y) == 'number' then biome_y_min = new_biome_def.min_pos.y end
if type(new_biome_def.max_pos) == 'table' and type(new_biome_def.max_pos.y) == 'number' then biome_y_max = new_biome_def.max_pos.y end
if type(new_biome_def.y_min) == 'number' then biome_y_min = new_biome_def.y_min end
if type(new_biome_def.y_max) == 'number' then biome_y_max = new_biome_def.y_max end
if biome_y_max > NETHER_FLOOR and biome_y_min < NETHER_CEILING then
if biome_y_max > floor_y and biome_y_min < ceiling_y then
-- This biome occupies some or all of the depth of the Nether, shift/crop it.
local spaceOccupiedAbove = biome_y_max - NETHER_CEILING
local spaceOccupiedBelow = NETHER_FLOOR - biome_y_min
local new_y_min, new_y_max
local spaceOccupiedAbove = biome_y_max - ceiling_y
local spaceOccupiedBelow = floor_y - biome_y_min
if spaceOccupiedAbove >= spaceOccupiedBelow or biome_y_min <= -30000 then
-- place the biome above the Nether
-- We also shift biomes which extend to the bottom of the map above the Nether, since they
-- likely only extend that deep as a catch-all, and probably have a role nearer the surface.
new_biome_def.y_min = NETHER_CEILING + 1
new_biome_def.y_max = math_max(biome_y_max, NETHER_CEILING + 2)
new_y_min = ceiling_y + 1
new_y_max = math_max(biome_y_max, ceiling_y + 2)
else
-- shift the biome to below the Nether
new_biome_def.y_max = NETHER_FLOOR - 1
new_biome_def.y_min = math_min(biome_y_min, NETHER_CEILING - 2)
new_y_max = floor_y - 1
new_y_min = math_min(biome_y_min, floor_y - 2)
end
debugf("Moving biome \"%s\" from %s..%s to %s..%s", new_biome_def.name, new_biome_def.y_min, new_biome_def.y_max, new_y_min, new_y_max)
if type(new_biome_def.min_pos) == 'table' and type(new_biome_def.min_pos.y) == 'number' then new_biome_def.min_pos.y = new_y_min end
if type(new_biome_def.max_pos) == 'table' and type(new_biome_def.max_pos.y) == 'number' then new_biome_def.max_pos.y = new_y_max end
new_biome_def.y_min = new_y_min -- Ensure the new heights are saved, even if original biome never specified one
new_biome_def.y_max = new_y_max
end
minetest.register_biome(new_biome_def)
end
@ -149,14 +164,18 @@ local function override_underground_biomes()
end
-- Shift any overlapping biomes out of the way before we create the Nether biomes
override_underground_biomes()
mapgen.shift_existing_biomes(NETHER_FLOOR, NETHER_CEILING)
-- nether:native_mapgen is used to prevent ores and decorations being generated according
-- to landforms created by the native mapgen.
-- 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", {})
-- It is disguised as stone to hide any bug where it leaks out of the nether, such as
-- https://github.com/minetest/minetest/issues/13440 or if on_generated() somehow was aborted.
local stone_copy_def = table.copy(minetest.registered_nodes["default:stone"] or {})
stone_copy_def.drop = stone_copy_def.drop or "default:stone" -- probably already defined as cobblestone
minetest.register_node("nether:native_mapgen", stone_copy_def)
minetest.register_biome({
name = "nether_caverns",
@ -244,6 +263,19 @@ mapgen.np_cave = {
--flags = ""
}
local cavePointPerlin = nil
mapgen.get_cave_point_perlin = function()
cavePointPerlin = cavePointPerlin or minetest.get_perlin(mapgen.np_cave)
return cavePointPerlin
end
mapgen.get_cave_perlin_at = function(pos)
cavePointPerlin = cavePointPerlin or minetest.get_perlin(mapgen.np_cave)
return cavePointPerlin:get_3d(pos)
end
-- Buffers and objects we shouldn't recreate every on_generate
local nobj_cave = nil
@ -256,206 +288,12 @@ local dbuf = {}
local c_air = minetest.get_content_id("air")
local c_netherrack = minetest.get_content_id("nether:rack")
local c_netherrack_deep = minetest.get_content_id("nether:rack_deep")
local c_dungeonbrick = minetest.get_content_id("nether:brick")
local c_dungeonbrick_alt = minetest.get_content_id("nether:brick_cracked")
local c_netherbrick_slab = minetest.get_content_id("stairs:slab_nether_brick")
local c_netherfence = minetest.get_content_id("nether:fence_nether_brick")
local c_glowstone = minetest.get_content_id("nether:glowstone")
local c_lava_source = minetest.get_content_id("default:lava_source")
local c_crystaldark = minetest.get_content_id("nether:geode")
local c_lavasea_source = minetest.get_content_id("nether:lava_source") -- same as lava but with staggered animation to look better as an ocean
local c_lava_crust = minetest.get_content_id("nether:lava_crust")
local c_native_mapgen = minetest.get_content_id("nether:native_mapgen")
-- Dungeon excavation functions
function is_dungeon_brick(node_id)
return node_id == c_dungeonbrick or node_id == c_dungeonbrick_alt
end
function build_dungeon_room_list(data, area)
local result = {}
-- Unfortunately gennotify only returns dungeon rooms, not corridors.
-- We don't need to check for temples because only dungeons are generated in biomes
-- that define their own dungeon nodes.
local gennotify = minetest.get_mapgen_object("gennotify")
local roomLocations = gennotify["dungeon"] or {}
-- Excavation should still know to stop if a cave or corridor has removed the dungeon wall.
-- See MapgenBasic::generateDungeons in mapgen.cpp for max room sizes.
local maxRoomSize = 18
local maxRoomRadius = math.ceil(maxRoomSize / 2)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomPos in ipairs(roomLocations) do
if area:containsp(roomPos) then -- this safety check does not appear to be necessary, but lets make it explicit
local room_vi = area:indexp(roomPos)
--data[room_vi] = minetest.get_content_id("default:torch") -- debug
local startPos = vector.new(roomPos)
if roomPos.y + 1 <= maxEdge.y and data[room_vi + yStride] == c_air then
-- The roomPos coords given by gennotify are at floor level, but whenever possible we
-- want to be performing searches a node higher than floor level to avoids dungeon chests.
startPos.y = startPos.y + 1
room_vi = area:indexp(startPos)
end
local bound_min_x = math_max(minEdge.x, roomPos.x - maxRoomRadius)
local bound_min_y = math_max(minEdge.y, roomPos.y - 1) -- room coords given by gennotify are on the floor
local bound_min_z = math_max(minEdge.z, roomPos.z - maxRoomRadius)
local bound_max_x = math_min(maxEdge.x, roomPos.x + maxRoomRadius)
local bound_max_y = math_min(maxEdge.y, roomPos.y + maxRoomSize) -- room coords given by gennotify are on the floor
local bound_max_z = math_min(maxEdge.z, roomPos.z + maxRoomRadius)
local room_min = vector.new(startPos)
local room_max = vector.new(startPos)
local vi = room_vi
while room_max.y < bound_max_y and data[vi + yStride] == c_air do
room_max.y = room_max.y + 1
vi = vi + yStride
end
vi = room_vi
while room_min.y > bound_min_y and data[vi - yStride] == c_air do
room_min.y = room_min.y - 1
vi = vi - yStride
end
vi = room_vi
while room_max.z < bound_max_z and data[vi + zStride] == c_air do
room_max.z = room_max.z + 1
vi = vi + zStride
end
vi = room_vi
while room_min.z > bound_min_z and data[vi - zStride] == c_air do
room_min.z = room_min.z - 1
vi = vi - zStride
end
vi = room_vi
while room_max.x < bound_max_x and data[vi + xStride] == c_air do
room_max.x = room_max.x + 1
vi = vi + xStride
end
vi = room_vi
while room_min.x > bound_min_x and data[vi - xStride] == c_air do
room_min.x = room_min.x - 1
vi = vi - xStride
end
local roomInfo = vector.new(roomPos)
roomInfo.minp = room_min
roomInfo.maxp = room_max
result[#result + 1] = roomInfo
end
end
return result;
end
-- Only partially excavates dungeons, the rest is left as an exercise for the player ;)
-- (Corridors and the parts of rooms which extend beyond the emerge boundary will remain filled)
function excavate_dungeons(data, area, rooms)
local vi, node_id
-- any air from the native mapgen has been replaced by netherrack, but
-- we don't want this inside dungeons, so fill dungeon rooms with air
for _, roomInfo in ipairs(rooms) do
local room_min = roomInfo.minp
local room_max = roomInfo.maxp
for z = room_min.z, room_max.z do
for y = room_min.y, room_max.y do
vi = area:index(room_min.x, y, z)
for x = room_min.x, room_max.x do
node_id = data[vi]
if node_id == c_netherrack or node_id == c_netherrack_deep then data[vi] = c_air end
vi = vi + 1
end
end
end
end
end
-- Since we already know where all the rooms and their walls are, and have all the nodes stored
-- in a voxelmanip already, we may as well add a little Nether flair to the dungeons found here.
function decorate_dungeons(data, area, rooms)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomInfo in ipairs(rooms) do
local room_min, room_max = roomInfo.minp, roomInfo.maxp
local room_size = vector.distance(room_min, room_max)
if room_size > 10 then
local room_seed = roomInfo.x + 3 * roomInfo.z + 13 * roomInfo.y
local window_y = roomInfo.y + math_min(2, room_max.y - roomInfo.y - 1)
if room_seed % 3 == 0 and room_max.y < maxEdge.y then
-- Glowstone chandelier (feel free to replace with a fancy schematic)
local vi = area:index(roomInfo.x, room_max.y + 1, roomInfo.z)
if is_dungeon_brick(data[vi]) then data[vi] = c_glowstone end
elseif room_seed % 4 == 0 and room_min.y > minEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
-- lava well (feel free to replace with a fancy schematic)
local vi = area:index(roomInfo.x, room_min.y, roomInfo.z)
if is_dungeon_brick(data[vi - yStride]) then
data[vi - yStride] = c_lava_source
if data[vi - zStride] == c_air then data[vi - zStride] = c_netherbrick_slab end
if data[vi + zStride] == c_air then data[vi + zStride] = c_netherbrick_slab end
if data[vi - xStride] == c_air then data[vi - xStride] = c_netherbrick_slab end
if data[vi + xStride] == c_air then data[vi + xStride] = c_netherbrick_slab end
end
end
-- Barred windows
if room_seed % 7 < 5 and room_max.x - room_min.x >= 4 and room_max.z - room_min.z >= 4
and window_y >= minEdge.y and window_y + 1 <= maxEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
--data[area:indexp(roomInfo)] = minetest.get_content_id("default:mese_post_light") -- debug
-- Until whisper glass is added, every window will be made of netherbrick fence (rather
-- than material depending on room_seed)
local window_node = c_netherfence
local vi_min = area:index(room_min.x - 1, window_y, roomInfo.z)
local vi_max = area:index(room_max.x + 1, window_y, roomInfo.z)
local locations = {-zStride, zStride, -zStride + yStride, zStride + yStride}
for _, offset in ipairs(locations) do
if is_dungeon_brick(data[vi_min + offset]) then data[vi_min + offset] = window_node end
if is_dungeon_brick(data[vi_max + offset]) then data[vi_max + offset] = window_node end
end
vi_min = area:index(roomInfo.x, window_y, room_min.z - 1)
vi_max = area:index(roomInfo.x, window_y, room_max.z + 1)
locations = {-xStride, xStride, -xStride + yStride, xStride + yStride}
for _, offset in ipairs(locations) do
if is_dungeon_brick(data[vi_min + offset]) then data[vi_min + offset] = window_node end
if is_dungeon_brick(data[vi_max + offset]) then data[vi_max + offset] = window_node end
end
end
-- Weeds on the floor once Nether weeds are added
end
end
end
local yblmin = NETHER_FLOOR + BLEND * 2
local yblmax = NETHER_CEILING - BLEND * 2
@ -514,27 +352,29 @@ local function on_generated(minp, maxp, seed)
nobj_cave = nobj_cave or minetest.get_perlin_map(mapgen.np_cave, chulens)
local nvals_cave = nobj_cave:get_3d_map_flat(minp, nbuf_cave)
local dungeonRooms = build_dungeon_room_list(data, area)
local dungeonRooms = mapgen.build_dungeon_room_list(data, area) -- function from mapgen_dungeons.lua
local abs_cave_noise, abs_cave_noise_adjusted
local contains_nether = false
local contains_shell = false
local contains_mantle = false
local contains_ocean = false
local contains_ocean = false
for y = y0, y1 do -- Y loop first to minimise tcave & lava-sea calculations
local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(y) -- function provided by mapgen_mantle.lua
local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(y) -- function from mapgen_mantle.lua
local above_lavasea = y > sea_level
local below_lavasea = y < sea_level
local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(y)
local tcave = TCAVE + tcave_adj
local tmantle = CENTER_REGION_LIMIT + centerRegionLimit_adj -- cavern_noise_adj already contains central_region_limit_adj, so tmantle is only for comparisons when cavern_noise_adj hasn't been added to the noise value
-- cavern_noise_adj gets added to noise value instead of added to the limit np_noise
-- is compared against, so subtract centerRegionLimit_adj instead of adding
local cavern_noise_adj =
CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) -
centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj instead of adding
centerRegionLimit_adj
for z = z0, z1 do
local vi = area:index(x0, y, z) -- Initial voxelmanip index
@ -548,7 +388,8 @@ local function on_generated(minp, maxp, seed)
if cave_noise > tcave then
-- Prime region
-- This was the only region in initial versions of the Nether mod.
-- It is the only region which portals from the surface will open into.
-- It is the only region which portals from the surface will open into,
-- getting to any other regions in the Nether will require Shanks' Pony.
data[vi] = c_air
contains_nether = true
@ -558,12 +399,11 @@ local function on_generated(minp, maxp, seed)
-- Reaching here would require the player to first find and journey through the central region,
-- as it's always separated from the Prime region by the central region.
data[vi] = c_netherrack -- For now I've just left this region as solid netherrack instead of air.
data[vi] = mapgen.getGeodeInteriorNodeId(x, y, z)-- function from mapgen_geodes.lua
-- Only set contains_nether to true here if you want tunnels created between the secondary region
-- and the central region.
--contains_nether = true
--data[vi] = c_air
contains_nether = true
else
-- netherrack walls and/or center region/mantle
abs_cave_noise = math_abs(cave_noise)
@ -580,8 +420,11 @@ local function on_generated(minp, maxp, seed)
data[vi] = c_netherrack_deep
else
-- the shell seperating the mantle from the rest of the nether...
data[vi] = c_netherrack -- excavate_dungeons() will mostly reverse this inside dungeons
contains_shell = true
if cave_noise > 0 then
data[vi] = c_netherrack -- excavate_dungeons() will mostly reverse this inside dungeons
else
data[vi] = c_crystaldark
end
end
end
@ -605,12 +448,12 @@ local function on_generated(minp, maxp, seed)
end
if contains_mantle or contains_ocean then
mapgen.add_basalt_columns(data, area, minp, maxp) -- function provided by mapgen_mantle.lua
mapgen.add_basalt_columns(data, area, minp, maxp) -- function from mapgen_mantle.lua
end
if contains_nether and contains_mantle then
tunnelCandidate_count = tunnelCandidate_count + 1
local success = mapgen.excavate_tunnel_to_center_of_the_nether(data, area, nvals_cave, minp, maxp) -- function provided by mapgen_mantle.lua
local success = mapgen.excavate_tunnel_to_center_of_the_nether(data, area, nvals_cave, minp, maxp) -- function from mapgen_mantle.lua
if success then tunnel_count = tunnel_count + 1 end
end
total_chunk_count = total_chunk_count + 1
@ -627,8 +470,8 @@ local function on_generated(minp, maxp, seed)
-- any air from the native mapgen has been replaced by netherrack, but we
-- don't want netherrack inside dungeons, so fill known dungeon rooms with air.
excavate_dungeons(data, area, dungeonRooms)
decorate_dungeons(data, area, dungeonRooms)
mapgen.excavate_dungeons(data, area, dungeonRooms) -- function from mapgen_dungeons.lua
mapgen.decorate_dungeons(data, area, dungeonRooms) -- function from mapgen_dungeons.lua
vm:set_data(data)
@ -645,7 +488,7 @@ end
-- use knowledge of the nether mapgen algorithm to return a suitable ground level for placing a portal.
-- 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(mapgen.np_cave)
local nobj_cave_point = mapgen.get_cave_point_perlin()
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")
@ -653,7 +496,7 @@ function nether.find_nether_ground_y(target_x, target_z, start_y, player_name)
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})
local nval_cave = nobj_cave_point:get_3d({x = target_x, y = y, z = target_z})
if nval_cave > TCAVE then -- Cavern
air = air + 1
@ -677,8 +520,4 @@ function nether.find_nether_ground_y(target_x, target_z, start_y, player_name)
return math_max(start_y, NETHER_FLOOR + BLEND) -- Fallback
end
-- We don't need to be gen-notified of temples because only dungeons will be generated
-- if a biome defines the dungeon nodes
minetest.set_gen_notify({dungeon = true})
minetest.register_on_generated(on_generated)

334
mapgen_dungeons.lua Normal file
View File

@ -0,0 +1,334 @@
--[[
Nether mod for minetest
All the Dungeon related functions used by the biomes-based mapgen are here.
Copyright (C) 2021 Treer
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
]]--
-- We don't need to be gen-notified of temples because only dungeons will be generated
-- if a biome defines the dungeon nodes
minetest.set_gen_notify({dungeon = true})
-- Content ids
local c_air = minetest.get_content_id("air")
local c_netherrack = minetest.get_content_id("nether:rack")
local c_netherrack_deep = minetest.get_content_id("nether:rack_deep")
local c_crystaldark = minetest.get_content_id("nether:geode")
local c_dungeonbrick = minetest.get_content_id("nether:brick")
local c_dungeonbrick_alt = minetest.get_content_id("nether:brick_cracked")
local c_netherbrick_slab = minetest.get_content_id("stairs:slab_nether_brick")
local c_netherfence = minetest.get_content_id("nether:fence_nether_brick")
local c_glowstone = minetest.get_content_id("nether:glowstone")
local c_glowstone_deep = minetest.get_content_id("nether:glowstone_deep")
local c_lava_source = minetest.get_content_id("default:lava_source")
-- Misc math functions
-- avoid needing table lookups each time a common math function is invoked
local math_max, math_min = math.max, math.min
-- Dungeon excavation functions
function is_dungeon_brick(node_id)
return node_id == c_dungeonbrick or node_id == c_dungeonbrick_alt
end
nether.mapgen.build_dungeon_room_list = function(data, area)
local result = {}
-- Unfortunately gennotify only returns dungeon rooms, not corridors.
-- We don't need to check for temples because only dungeons are generated in biomes
-- that define their own dungeon nodes.
local gennotify = minetest.get_mapgen_object("gennotify")
local roomLocations = gennotify["dungeon"] or {}
-- Excavation should still know to stop if a cave or corridor has removed the dungeon wall.
-- See MapgenBasic::generateDungeons in mapgen.cpp for max room sizes.
local maxRoomSize = 18
local maxRoomRadius = math.ceil(maxRoomSize / 2)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomPos in ipairs(roomLocations) do
if area:containsp(roomPos) then -- this safety check does not appear to be necessary, but lets make it explicit
local room_vi = area:indexp(roomPos)
--data[room_vi] = minetest.get_content_id("default:torch") -- debug
local startPos = vector.new(roomPos)
if roomPos.y + 1 <= maxEdge.y and data[room_vi + yStride] == c_air then
-- The roomPos coords given by gennotify are at floor level, but whenever possible we
-- want to be performing searches a node higher than floor level to avoids dungeon chests.
startPos.y = startPos.y + 1
room_vi = area:indexp(startPos)
end
local bound_min_x = math_max(minEdge.x, roomPos.x - maxRoomRadius)
local bound_min_y = math_max(minEdge.y, roomPos.y - 1) -- room coords given by gennotify are on the floor
local bound_min_z = math_max(minEdge.z, roomPos.z - maxRoomRadius)
local bound_max_x = math_min(maxEdge.x, roomPos.x + maxRoomRadius)
local bound_max_y = math_min(maxEdge.y, roomPos.y + maxRoomSize) -- room coords given by gennotify are on the floor
local bound_max_z = math_min(maxEdge.z, roomPos.z + maxRoomRadius)
local room_min = vector.new(startPos)
local room_max = vector.new(startPos)
local vi = room_vi
while room_max.y < bound_max_y and data[vi + yStride] == c_air do
room_max.y = room_max.y + 1
vi = vi + yStride
end
vi = room_vi
while room_min.y > bound_min_y and data[vi - yStride] == c_air do
room_min.y = room_min.y - 1
vi = vi - yStride
end
vi = room_vi
while room_max.z < bound_max_z and data[vi + zStride] == c_air do
room_max.z = room_max.z + 1
vi = vi + zStride
end
vi = room_vi
while room_min.z > bound_min_z and data[vi - zStride] == c_air do
room_min.z = room_min.z - 1
vi = vi - zStride
end
vi = room_vi
while room_max.x < bound_max_x and data[vi + xStride] == c_air do
room_max.x = room_max.x + 1
vi = vi + xStride
end
vi = room_vi
while room_min.x > bound_min_x and data[vi - xStride] == c_air do
room_min.x = room_min.x - 1
vi = vi - xStride
end
local roomInfo = vector.new(roomPos)
roomInfo.minp = room_min
roomInfo.maxp = room_max
result[#result + 1] = roomInfo
end
end
return result;
end
-- Only partially excavates dungeons, the rest is left as an exercise for the player ;)
-- (Corridors and the parts of rooms which extend beyond the emerge boundary will remain filled)
nether.mapgen.excavate_dungeons = function(data, area, rooms)
local vi, node_id
-- any air from the native mapgen has been replaced by netherrack, but
-- we don't want this inside dungeons, so fill dungeon rooms with air
for _, roomInfo in ipairs(rooms) do
local room_min = roomInfo.minp
local room_max = roomInfo.maxp
for z = room_min.z, room_max.z do
for y = room_min.y, room_max.y do
vi = area:index(room_min.x, y, z)
for x = room_min.x, room_max.x do
node_id = data[vi]
if node_id == c_netherrack or node_id == c_netherrack_deep or node_id == c_crystaldark then data[vi] = c_air end
vi = vi + 1
end
end
end
end
-- clear netherrack from dungeon stairways
if #rooms > 0 then
local stairPositions = minetest.find_nodes_in_area(area.MinEdge, area.MaxEdge, minetest.registered_biomes["nether_caverns"].node_dungeon_stair)
for _, stairPos in ipairs(stairPositions) do
vi = area:indexp(stairPos)
for i = 1, 4 do
if stairPos.y + i > area.MaxEdge.y then break end
vi = vi + area.ystride
node_id = data[vi]
-- searching forward of the stairs could also be done
if node_id == c_netherrack or node_id == c_netherrack_deep or node_id == c_crystaldark then data[vi] = c_air end
end
end
end
end
-- Since we already know where all the rooms and their walls are, and have all the nodes stored
-- in a voxelmanip already, we may as well add a little Nether flair to the dungeons found here.
nether.mapgen.decorate_dungeons = function(data, area, rooms)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomInfo in ipairs(rooms) do
local room_min, room_max = roomInfo.minp, roomInfo.maxp
local room_size = vector.distance(room_min, room_max)
if room_size > 10 then
local room_seed = roomInfo.x + 3 * roomInfo.z + 13 * roomInfo.y
local window_y = roomInfo.y + math_min(2, room_max.y - roomInfo.y - 1)
local roomWidth = room_max.x - room_min.x + 1
local roomLength = room_max.z - room_min.z + 1
if room_seed % 3 == 0 and room_max.y < maxEdge.y then
-- Glowstone chandelier (feel free to replace with a fancy schematic)
local vi = area:index(roomInfo.x, room_max.y + 1, roomInfo.z)
if is_dungeon_brick(data[vi]) then data[vi] = c_glowstone end
elseif room_seed % 4 == 0 and room_min.y > minEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
-- lava well (feel free to replace with a fancy schematic)
local vi = area:index(roomInfo.x, room_min.y, roomInfo.z)
if is_dungeon_brick(data[vi - yStride]) then
data[vi - yStride] = c_lava_source
if data[vi - zStride] == c_air then data[vi - zStride] = c_netherbrick_slab end
if data[vi + zStride] == c_air then data[vi + zStride] = c_netherbrick_slab end
if data[vi - xStride] == c_air then data[vi - xStride] = c_netherbrick_slab end
if data[vi + xStride] == c_air then data[vi + xStride] = c_netherbrick_slab end
end
end
-- Barred windows
if room_seed % 7 < 5 and roomWidth >= 5 and roomLength >= 5
and window_y >= minEdge.y and window_y + 1 <= maxEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
--data[area:indexp(roomInfo)] = minetest.get_content_id("default:mese_post_light") -- debug
-- Glass panes can't go in the windows because we aren't setting param data.
-- Until a Nether glass is added, every window will be made of netherbrick fence rather
-- than material depending on room_seed.
local window_node = c_netherfence
--if c_netherglass ~= nil and room_seed % 20 >= 12 then window_node = c_netherglass end
local function placeWindow(vi, viOutsideOffset, windowNo)
if is_dungeon_brick(data[vi]) and is_dungeon_brick(data[vi + yStride]) then
data[vi] = window_node
if room_seed % 19 == windowNo then
-- place a glowstone light behind the window
local node_id = data[vi + viOutsideOffset]
if node_id == c_netherrack then
data[vi + viOutsideOffset] = c_glowstone
elseif node_id == c_netherrack_deep then
data[vi + viOutsideOffset] = c_glowstone_deep
end
end
end
end
local vi_min = area:index(room_min.x - 1, window_y, roomInfo.z)
local vi_max = area:index(room_max.x + 1, window_y, roomInfo.z)
local locations = {-zStride, zStride, -zStride + yStride, zStride + yStride}
for i, offset in ipairs(locations) do
placeWindow(vi_min + offset, -1, i)
placeWindow(vi_max + offset, 1, i + #locations)
end
vi_min = area:index(roomInfo.x, window_y, room_min.z - 1)
vi_max = area:index(roomInfo.x, window_y, room_max.z + 1)
locations = {-xStride, xStride, -xStride + yStride, xStride + yStride}
for i, offset in ipairs(locations) do
placeWindow(vi_min + offset, -zStride, i + #locations * 2)
placeWindow(vi_max + offset, zStride, i + #locations * 3)
end
end
-- pillars or mezzanine floor
if room_seed % 43 > 10 and roomWidth >= 6 and roomLength >= 6 then
local pillar_vi = {}
local pillarHeight = 0
local wallDist = 1 + math.floor((roomWidth + roomLength) / 14)
local roomHeight = room_max.y - room_min.y
if roomHeight >= 7 then
-- mezzanine floor
local mezzMax = {
x = room_min.x + math.floor(roomWidth / 7 * 4),
y = room_min.y + math.floor(roomHeight / 5 * 3),
z = room_max.z
}
pillarHeight = mezzMax.y - room_min.y - 1
pillar_vi = {
area:index(mezzMax.x, room_min.y, room_min.z + wallDist),
area:index(mezzMax.x, room_min.y, room_max.z - wallDist),
}
if is_dungeon_brick(data[pillar_vi[1] - yStride]) and is_dungeon_brick(data[pillar_vi[2] - yStride]) then
-- The floor of the dungeon looks like it exists (i.e. not erased by nether
-- cavern), so add the mezzanine floor
for z = 0, roomLength - 1 do
local vi = area:index(room_min.x, mezzMax.y, room_min.z + z)
for x = room_min.x, mezzMax.x do
if data[vi] == c_air then data[vi] = c_dungeonbrick end
vi = vi + 1
end
end
end
elseif roomHeight >= 4 then
-- 4 pillars
pillarHeight = roomHeight
pillar_vi = {
area:index(room_min.x + wallDist, room_min.y, room_min.z + wallDist),
area:index(room_min.x + wallDist, room_min.y, room_max.z - wallDist),
area:index(room_max.x - wallDist, room_min.y, room_min.z + wallDist),
area:index(room_max.x - wallDist, room_min.y, room_max.z - wallDist)
}
end
for i = #pillar_vi, 1, -1 do
if not is_dungeon_brick(data[pillar_vi[i] - yStride]) then
-- there's no dungeon floor under this pillar so skip it, it's probably been cut away by nether cavern.
table.remove(pillar_vi, i)
end
end
for y = 0, pillarHeight do
for _, vi in ipairs(pillar_vi) do
if data[vi + y * yStride] == c_air then data[vi + y * yStride] = c_dungeonbrick end
end
end
end
-- Weeds on the floor once Nether weeds are added
end
end
end

221
mapgen_geodes.lua Normal file
View File

@ -0,0 +1,221 @@
--[[
Nether mod for minetest
This file contains helper functions for generating geode interiors,
a proof-of-concept to demonstrate how the secondary/spare region
in the nether might be put to use by someone.
Copyright (C) 2021 Treer
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
]]--
local debugf = nether.debug
local mapgen = nether.mapgen
-- Content ids
local c_air = minetest.get_content_id("air")
local c_crystal = minetest.get_content_id("nether:geodelite") -- geodelite has a faint glow
local c_netherrack = minetest.get_content_id("nether:rack")
local c_glowstone = minetest.get_content_id("nether:glowstone")
-- Math funcs
local math_max, math_min, math_abs, math_floor, math_pi = math.max, math.min, math.abs, math.floor, math.pi -- avoid needing table lookups each time a common math function is invoked
-- Create a tiling space of close-packed spheres, using Hexagonal close packing
-- of spheres with radius 0.5.
-- With a layer of spheres on a flat surface, if the pack-z distance is 1 due to 0.5
-- radius then the pack-x distance will be the height of an equilateral triangle: sqrt(3) / 2,
-- and the pack-y distance between each layer will be sqrt(6) / 3,
-- The tessellating space will be a rectangular box of 2*pack-x by 1*pack-z by 3*pack-y
local xPack = math.sqrt(3)/2 -- 0.866, height of an equalateral triangle
local xPack2 = xPack * 2 -- 1.732
local yPack = math.sqrt(6) / 3 -- 0.816, y height of each layer
local yPack2 = yPack * 2
local yPack3 = yPack * 3
local layer2offsetx = xPack / 3 -- 0.289, height to center of equalateral triangle
local layer3offsetx = xPack2 / 3 -- 0.577
local structureSize = 50 -- magic numbers may need retuning if this changes too much
local layer1 = {
{0, 0, 0},
{0, 0, 1},
{xPack, 0, -0.5},
{xPack, 0, 0.5},
{xPack, 0, 1.5},
{xPack2, 0, 0},
{xPack2, 0, 1},
}
local layer2 = {
{layer2offsetx - xPack, yPack, 0},
{layer2offsetx - xPack, yPack, 1},
{layer2offsetx, yPack, -0.5},
{layer2offsetx, yPack, 0.5},
{layer2offsetx, yPack, 1.5},
{layer2offsetx + xPack, yPack, 0},
{layer2offsetx + xPack, yPack, 1},
{layer2offsetx + xPack2, yPack, -0.5},
{layer2offsetx + xPack2, yPack, 0.5},
{layer2offsetx + xPack2, yPack, 1.5},
}
local layer3 = {
{layer3offsetx - xPack, yPack2, -0.5},
{layer3offsetx - xPack, yPack2, 0.5},
{layer3offsetx - xPack, yPack2, 1.5},
{layer3offsetx, yPack2, 0},
{layer3offsetx, yPack2, 1},
{layer3offsetx + xPack, yPack2, -0.5},
{layer3offsetx + xPack, yPack2, 0.5},
{layer3offsetx + xPack, yPack2, 1.5},
{layer3offsetx + xPack2, yPack2, 0},
{layer3offsetx + xPack2, yPack2, 1},
}
local layer4 = {
{0, yPack3, 0},
{0, yPack3, 1},
{xPack, yPack3, -0.5},
{xPack, yPack3, 0.5},
{xPack, yPack3, 1.5},
{xPack2, yPack3, 0},
{xPack2, yPack3, 1},
}
local layers = {
{y = layer1[1][2], points = layer1}, -- layer1[1][2] is the y value of the first point in layer1, and all spheres in a layer have the same y
{y = layer2[1][2], points = layer2},
{y = layer3[1][2], points = layer3},
{y = layer4[1][2], points = layer4},
}
-- Geode mapgen functions (AKA proof of secondary/spare region concept)
-- fast for small lists
function insertionSort(array)
local i
for i = 2, #array do
local key = array[i]
local j = i - 1
while j > 0 and array[j] > key do
array[j + 1] = array[j]
j = j - 1
end
array[j + 1] = key
end
return array
end
local distSquaredList = {}
local adj_x = 0
local adj_y = 0
local adj_z = 0
local lasty, lastz
local warpx, warpz
-- It's quite a lot to calculate for each air node, but its not terribly slow and
-- it'll be pretty darn rare for chunks in the secondary region to ever get emerged.
mapgen.getGeodeInteriorNodeId = function(x, y, z)
if z ~= lastz then
lastz = z
-- Calculate structure warping
-- To avoid calculating this for each node there's no warping as you look along the x axis :(
adj_y = math.sin(math_pi / 222 * y) * 30
if y ~= lasty then
lasty = y
warpx = math.sin(math_pi / 100 * y) * 10
warpz = math.sin(math_pi / 43 * y) * 15
end
local twistRadians = math_pi / 73 * y
local sinTwist, cosTwist = math.sin(twistRadians), math.cos(twistRadians)
adj_x = cosTwist * warpx - sinTwist * warpz
adj_z = sinTwist * warpx + cosTwist * warpz
end
-- convert x, y, z into a position in the tessellating space
local cell_x = (((x + adj_x) / xPack2 + 0.5) % structureSize) / structureSize * xPack2
local cell_y = (((y + adj_y) / yPack3 + 0.5) % structureSize) / structureSize * yPack3
local cell_z = (((z + adj_z) + 0.5) % structureSize) / structureSize -- zPack = 1, so can be omitted
local iOut = 1
local i, j
local canSkip = false
for i = 1, #layers do
local layer = layers[i]
local dy = cell_y - layer.y
if dy > -0.71 and dy < 0.71 then -- optimization - don't include points to far away to make a difference. (0.71 comes from sin(45°))
local points = layer.points
for j = 1, #points do
local point = points[j]
local dx = cell_x - point[1]
local dz = cell_z - point[3]
local distSquared = dx*dx + dy*dy + dz*dz
if distSquared < 0.25 then
-- optimization - point is inside a sphere, so cannot be a wall edge. (0.25 comes from radius of 0.5 squared)
return c_air
end
distSquaredList[iOut] = distSquared
iOut = iOut + 1
end
end
end
-- clear the rest of the array instead of creating a new one to hopefully reduce luajit mem leaks.
while distSquaredList[iOut] ~= nil do
rawset(distSquaredList, iOut, nil)
iOut = iOut + 1
end
insertionSort(distSquaredList)
local d3_1 = distSquaredList[3] - distSquaredList[1]
local d3_2 = distSquaredList[3] - distSquaredList[2]
--local d4_1 = distSquaredList[4] - distSquaredList[1]
--local d4_3 = distSquaredList[4] - distSquaredList[3]
-- Some shape formulas (tuned for a structureSize of 50)
-- (d3_1 < 0.05) gives connective lines
-- (d3_1 < 0.05 or d3_2 < .02) give fancy elven bridges - prob doesn't need the d3_1 part
-- ((d3_1 < 0.05 or d3_2 < .02) and distSquaredList[1] > .3) tapers the fancy connections in the middle
-- (d4_3 < 0.03 and d3_2 < 0.03) produces caltrops at intersections
-- (d4_1 < 0.1) produces spherish balls at intersections
-- The idea is voronoi based - edges in a voronoi diagram are where each nearby point is at equal distance.
-- In this case we use squared distances to avoid calculating square roots.
if (d3_1 < 0.05 or d3_2 < .02) and distSquaredList[1] > .3 then
return c_crystal
elseif (distSquaredList[4] - distSquaredList[1]) < 0.08 then
return c_glowstone
else
return c_air
end
end

View File

@ -27,6 +27,7 @@
local debugf = nether.debug
local mapgen = nether.mapgen
local S = nether.get_translator
local BASALT_COLUMN_UPPER_LIMIT = mapgen.BASALT_COLUMN_UPPER_LIMIT
local BASALT_COLUMN_LOWER_LIMIT = mapgen.BASALT_COLUMN_LOWER_LIMIT
@ -49,7 +50,6 @@ local np_basalt = {
local nobj_basalt = nil
local nbuf_basalt = {}
local cavePerlin = nil
-- Content ids
@ -119,7 +119,7 @@ mapgen.add_basalt_columns = function(data, area, minp, maxp)
local yStride = area.ystride
local yCaveStride = x1 - x0 + 1
cavePerlin = cavePerlin or minetest.get_perlin(mapgen.np_cave)
local cavePerlin = mapgen.get_cave_point_perlin()
nobj_basalt = nobj_basalt or minetest.get_perlin_map(np_basalt, {x = yCaveStride, y = yCaveStride})
local nvals_basalt = nobj_basalt:get_2d_map_flat({x=minp.x, y=minp.z}, {x=yCaveStride, y=yCaveStride}, nbuf_basalt)
@ -136,7 +136,7 @@ mapgen.add_basalt_columns = function(data, area, minp, maxp)
if basaltNoise > 0 then
-- a basalt column is here
local abs_sealevel_cave_noise = math_abs(cavePerlin:get3d({x = x, y = nearest_sea_level, z = z}))
local abs_sealevel_cave_noise = math_abs(cavePerlin:get_3d({x = x, y = nearest_sea_level, z = z}))
-- Add Some quick deterministic noise to the column heights
-- This is probably not good noise, but it doesn't have to be.
@ -399,11 +399,14 @@ mapgen.excavate_tunnel_to_center_of_the_nether = function(data, area, nvals_cave
if lowest < mapgen.CENTER_CAVERN_LIMIT and highest > mapgen.TCAVE + 0.03 then
local mantle_y = area:position(lowest_vi).y
local sealevel, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(mantle_y)
local _, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(mantle_y)
local _, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(mantle_y)
-- cavern_noise_adj gets added to noise value instead of added to the limit np_noise
-- is compared against, so subtract centerRegionLimit_adj instead of adding
local cavern_noise_adj =
mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) -
centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj instead of adding
centerRegionLimit_adj
if lowest + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then
excavate_pathway(data, area, area:position(highest_vi), area:position(lowest_vi), minp, maxp)
@ -414,63 +417,96 @@ mapgen.excavate_tunnel_to_center_of_the_nether = function(data, area, nvals_cave
end
-- an enumerated list of the different regions in the nether
mapgen.RegionEnum = {
OVERWORLD = {name = "overworld", desc = S("The Overworld") }, -- Outside the Nether / none of the regions in the Nether
POSITIVE = {name = "positive", desc = S("Positive nether") }, -- The classic nether caverns are here - where cavePerlin > 0.6
POSITIVESHELL = {name = "positive shell", desc = S("Shell between positive nether and center region") }, -- the nether side of the wall/buffer area separating classic nether from the mantle
CENTER = {name = "center", desc = S("Center/Mantle, inside cavern") },
CENTERSHELL = {name = "center shell", desc = S("Center/Mantle, but outside the caverns") }, -- the mantle side of the wall/buffer area separating the positive and negative regions from the center region
NEGATIVE = {name = "negative", desc = S("Negative nether") }, -- Secondary/spare region - where cavePerlin < -0.6
NEGATIVESHELL = {name = "negative shell", desc = S("Shell between negative nether and center region") } -- the spare region side of the wall/buffer area separating the negative region from the mantle
}
-- Returns (region, noise) where region is a value from mapgen.RegionEnum
-- and noise is the unadjusted cave perlin value
mapgen.get_region = function(pos)
if pos.y > nether.DEPTH_CEILING or pos.y < nether.DEPTH_FLOOR then
return mapgen.RegionEnum.OVERWORLD, nil
end
local caveNoise = mapgen.get_cave_perlin_at(pos)
local sealevel, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(pos.y)
local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(pos.y)
local tcave = mapgen.TCAVE + tcave_adj
local tmantle = mapgen.CENTER_REGION_LIMIT + centerRegionLimit_adj
-- cavern_noise_adj gets added to noise value instead of added to the limit np_noise
-- is compared against, so subtract centerRegionLimit_adj instead of adding
local cavern_noise_adj =
mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) -
centerRegionLimit_adj
local region
if caveNoise > tcave then
region = mapgen.RegionEnum.POSITIVE
elseif -caveNoise > tcave then
region = mapgen.RegionEnum.NEGATIVE
elseif math_abs(caveNoise) < tmantle then
if math_abs(caveNoise) + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then
region = mapgen.RegionEnum.CENTER
else
region = mapgen.RegionEnum.CENTERSHELL
end
elseif caveNoise > 0 then
region = mapgen.RegionEnum.POSITIVESHELL
else
region = mapgen.RegionEnum.NEGATIVESHELL
end
return region, caveNoise
end
minetest.register_chatcommand("nether_whereami",
{
description = "Describes which region of the nether the player is in",
{
description = S("Describes which region of the nether the player is in"),
privs = {debug = true},
func = function(name, param)
local player = minetest.get_player_by_name(name)
if player == nil then return false, "Unknown player position" end
if player == nil then return false, S("Unknown player position") end
local playerPos = vector.round(player:get_pos())
local pos = vector.round(player:get_pos())
if pos.y > nether.DEPTH_CEILING or pos.y < nether.DEPTH_FLOOR then
return true, "The Overworld"
end
local region, caveNoise = mapgen.get_region(playerPos)
local seaLevel, cavernLimitDistance = mapgen.find_nearest_lava_sealevel(playerPos.y)
local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(playerPos.y)
cavePerlin = cavePerlin or minetest.get_perlin(mapgen.np_cave)
local densityNoise = cavePerlin:get_3d(pos)
local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(pos.y)
local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(pos.y)
local tcave = mapgen.TCAVE + tcave_adj
local tmantle = mapgen.CENTER_REGION_LIMIT + centerRegionLimit_adj
local cavern_noise_adj =
mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) -
centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj so subtract centerRegionLimit_adj instead of adding
local seaDesc = ""
local boundaryDesc = ""
local perlinDesc = ""
local desc
if region ~= mapgen.RegionEnum.OVERWORLD then
if densityNoise > tcave then
desc = "Positive nether"
elseif -densityNoise > tcave then
desc = "Negative nether"
elseif math_abs(densityNoise) < tmantle then
desc = "Mantle"
if math_abs(densityNoise) + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then
desc = desc .. " inside cavern"
local seaPos = playerPos.y - seaLevel
if seaPos > 0 then
seaDesc = S(", @1m above lava-sea level", seaPos)
else
desc = desc .. " but outside cavern"
seaDesc = S(", @1m below lava-sea level", seaPos)
end
elseif densityNoise > 0 then
desc = "Shell between positive nether and center region"
else
desc = "Shell between negative nether and center region"
if tcave_adj > 0 then
boundaryDesc = S(", approaching y boundary of Nether")
end
perlinDesc = S("[Perlin @1] ", (math_floor(caveNoise * 1000) / 1000))
end
local sea_pos = pos.y - sea_level
if sea_pos > 0 then
desc = desc .. ", " .. sea_pos .. "m above lava-sea level"
else
desc = desc .. ", " .. sea_pos .. "m below lava-sea level"
end
if tcave_adj > 0 then
desc = desc .. ", approaching y boundary of Nether"
end
return true, "[Perlin " .. (math_floor(densityNoise * 1000) / 1000) .. "] " .. desc
return true, S("@1@2@3@4", perlinDesc, region.desc, seaDesc, boundaryDesc)
end
}
)

View File

@ -216,7 +216,7 @@ function nether.find_nether_ground_y(target_x, target_z, start_y, player_name)
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})
local nval_cave = nobj_cave_point:get_3d({x = target_x, y = y, z = target_z})
if nval_cave > TCAVE then -- Cavern
air = air + 1

View File

@ -1,4 +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
optional_depends = toolranks, moreblocks, mesecons, loot, dungeon_loot, doc_basics, fire, climate_api, ethereal, xpanes, walls

78
nether_api.txt Normal file
View File

@ -0,0 +1,78 @@
Modding/interop guide to Nether
===============================
For portals API see portal_api.txt
The Nether mod exposes some of its functions and data via the lua global
`nether` and `nether.mapgen`
* `nether.DEPTH_CEILING`: [read-only] Y value of the top of the Nether.
* `nether.DEPTH_FLOOR`: [read-only] Y value of the bottom of the Nether.
* `nether.DEPTH_FLOOR_LAYERS`: [writable] Gives the bottom Y of all
locations that wish to be considered part of the Nether.
DEPTH_FLOOR_LAYERS Allows mods to insert extra layers below the
Nether, by knowing where their layer ceiling should start, and letting
the layers be included in effects which only happen in the Nether.
If a mod wishes to add a layer below the Nether it should read
`nether.DEPTH_FLOOR_LAYERS` to find the bottom Y of the Nether and any
other layers already under the Nether. The mod should leave a small gap
between DEPTH_FLOOR_LAYERS and its ceiling (e.g. use DEPTH_FLOOR_LAYERS - 6
for its ceiling Y, so there is room to shift edge-case biomes), then set
`nether.DEPTH_FLOOR_LAYERS` to reflect the mod's floor Y value, and call
`shift_existing_biomes()` with DEPTH_FLOOR_LAYERS as the `floor_y` argument.
* `nether.NETHER_REALM_ENABLED`: [read-only] Gets the value of the "Enable
Nether realm & portal" setting the nether mod exposes in Minetest's
"All Settings" -> "Mods" -> "nether" options.
When false, the entire nether mapgen is disabled (not run), and the portal
to it is not registered. Reasons someone might disable the Nether realm
include if a nether-layer mod was to be used as the Nether instead, or if
the portal mechanic was desired in a game without the Nether, etc.
* `nether.useBiomes`: [read-only] When this is false, the Nether interop
functions below are not available (nil).
Indicates that the biomes-enabled mapgen is in use. The Nether mod falls back
to older mapgen code for v6 maps and old versions of Minetest, the older
mapgen code doesn't use biomes and doesn't provide API/interop functions.
Mapgen functions available when nether.useBiomes is true
--------------------------------------------------------
The following functions are nil if `nether.useBiomes` is false,
and also nil if `nether.NETHER_REALM_ENABLED` is false.
* `nether.mapgen.shift_existing_biomes(floor_y, ceiling_y)` Move any existing
biomes out of the y-range specified by `floor_y` and `ceiling_y`.
* `nether.mapgen.get_region(pos)`: Returns two values, (region, noise) where
`region` is a value from `nether.mapgen.RegionEnum` and `noise` is the
unadjusted cave perlin value.
* `nether.mapgen.RegionEnum` values are tables which contain an invariant
`name` and a localized `desc`. Current region names include overworld,
positive, positive shell, center, center shell, negative, and negative
shell.
"positive" corresponds to conventional Nether caverns, and "center"
corresponds to the Mantle region.
* `nether.mapgen.get_cave_point_perlin()`: Returns the PerlinNoise object for
the Nether's cavern noise.
* `nether.mapgen.get_cave_perlin_at(pos)`: Returns the Nether cavern noise
value at a given 3D position.
Other mapgen functions
-------------------------------------------
If the Nether realm is enabled, then this function will be available
regardless of whether `nether.useBiomes` is true:
* `nether.find_nether_ground_y(target_x, target_z, start_y, player_name)`
Uses knowledge of the nether mapgen algorithm to return a suitable ground
level for placing a portal.
* `player_name` is optional, allowing a player to spawn a remote portal
in their own protected areas.

313
nodes.lua
View File

@ -65,23 +65,186 @@ nether.register_wormhole_node("nether:portal_alt", {
})
--== Transmogrification functions ==--
-- Functions enabling selected nodes to be temporarily transformed into other nodes.
-- (so the light staff can temporarily turn netherrack into glowstone)
-- Swaps the node at `nodePos` with `newNode`, unless `newNode` is nil in which
-- case the node is swapped back to its original type.
-- `monoSimpleSoundSpec` is optional.
-- returns true if a node was transmogrified
nether.magicallyTransmogrify_node = function(nodePos, playerName, newNode, monoSimpleSoundSpec, isPermanent)
local meta = minetest.get_meta(nodePos)
local playerEyePos = nodePos -- fallback value in case the player no longer exists
local player = minetest.get_player_by_name(playerName)
if player ~= nil then
local playerPos = player:get_pos()
playerEyePos = vector.add(playerPos, {x = 0, y = 1.5, z = 0}) -- not always the cameraPos, e.g. 3rd person mode.
end
local oldNode = minetest.get_node(nodePos)
if oldNode.name == "air" then
-- the node has been mined or otherwise destroyed, abort the operation
return false
end
local oldNodeDef = minetest.registered_nodes[oldNode.name] or minetest.registered_nodes["air"]
local specialFXSize = 1 -- a specialFXSize of 1 is for full SFX, 0.5 is half-sized
local returningToNormal = newNode == nil
if returningToNormal then
-- This is the transmogrified node returning back to normal - a more subdued animation
specialFXSize = 0.5
-- read what the node used to be from the metadata
newNode = {
name = meta:get_string("transmogrified_name"),
param1 = meta:get_string("transmogrified_param1"),
param2 = meta:get_string("transmogrified_param2")
}
if newNode.name == "" then
minetest.log("warning", "nether.magicallyTransmogrify_node() invoked to restore node which wasn't transmogrified")
return false
end
end
local soundSpec = monoSimpleSoundSpec
if soundSpec == nil and oldNodeDef.sounds ~= nil then
soundSpec = oldNodeDef.sounds.dug or oldNodeDef.sounds.dig
if soundSpec == "__group" then soundSpec = "default_dig_cracky" end
end
if soundSpec ~= nil then
minetest.sound_play(soundSpec, {pos = nodePos, max_hear_distance = 50})
end
-- Start the particlespawner nearer the player's side of the node to create
-- more initial occlusion for an illusion of the old node breaking apart / falling away.
local dirToPlayer = vector.normalize(vector.subtract(playerEyePos, nodePos))
local impactPos = vector.add(nodePos, vector.multiply(dirToPlayer, 0.5))
local velocity = 1 + specialFXSize
minetest.add_particlespawner({
amount = 50 * specialFXSize,
time = 0.1,
minpos = vector.add(impactPos, -0.3),
maxpos = vector.add(impactPos, 0.3),
minvel = {x = -velocity, y = -velocity, z = -velocity},
maxvel = {x = velocity, y = 3 * velocity, z = velocity}, -- biased upward to counter gravity in the initial stages
minacc = {x=0, y=-10, z=0},
maxacc = {x=0, y=-10, z=0},
minexptime = 1.5 * specialFXSize,
maxexptime = 3 * specialFXSize,
minsize = 0.5,
maxsize = 5,
node = {name = oldNodeDef.name},
glow = oldNodeDef.light_source
})
if returningToNormal or isPermanent then
-- clear the metadata that indicates the node is transformed
meta:set_string("transmogrified_name", "")
meta:set_int("transmogrified_param1", 0)
meta:set_int("transmogrified_param2", 0)
else
-- save the original node so it can be restored
meta:set_string("transmogrified_name", oldNode.name)
meta:set_int("transmogrified_param1", oldNode.param1)
meta:set_int("transmogrified_param2", oldNode.param2)
end
minetest.swap_node(nodePos, newNode)
return true
end
local function transmogrified_can_dig (pos, player)
if minetest.get_meta(pos):get_string("transmogrified_name") ~= "" then
-- This node was temporarily transformed into its current form
-- revert it back, rather than allow the player to mine transmogrified nodes.
local playerName = ""
if player ~= nil then playerName = player:get_player_name() end
nether.magicallyTransmogrify_node(pos, playerName)
return false
end
return true
end
-- Nether nodes
minetest.register_node("nether:rack", {
description = S("Netherrack"),
tiles = {"nether_rack.png"},
is_ground_content = true,
groups = {cracky = 3, level = 2},
-- setting workable_with_nether_tools reduces the wear on nether:pick_nether when mining this node
groups = {cracky = 3, level = 2, workable_with_nether_tools = 3},
sounds = default.node_sound_stone_defaults(),
})
-- Geode crystals can only be introduced by the biomes-based mapgen, since it requires the
-- MT 5.0 world-align texture features.
minetest.register_node("nether:geode", {
description = S("Nether Beryl"),
_doc_items_longdesc = S("Nether geode crystal, found lining the interior walls of Nether geodes"),
tiles = {{
name = "nether_geode.png",
align_style = "world",
scale = 4
}},
is_ground_content = true,
groups = {cracky = 3, oddly_breakable_by_hand = 3, nether_crystal = 1},
sounds = default.node_sound_glass_defaults(),
})
-- Nether Berylite is a Beryl that can seen in the dark, used to light up the internal structure
-- of the geode, so to avoid player confusion we'll just have it drop plain Beryl, and have only
-- plain Beryl in the creative inventory.
minetest.register_node("nether:geodelite", {
description = S("Nether Berylite"),
_doc_items_longdesc = S("Nether geode crystal. A crystalline structure with faint glow found inside large Nether geodes"),
tiles = {{
name = "nether_geode.png",
align_style = "world",
scale = 4
}},
light_source = 2,
drop = "nether:geode",
is_ground_content = true,
groups = {cracky = 3, oddly_breakable_by_hand = 3, nether_crystal = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_glass_defaults(),
})
if minetest.get_modpath("xpanes") and minetest.global_exists("xpanes") and xpanes.register_pane ~= nil then
xpanes.register_pane("nether_crystal_pane", {
description = S("Nether Crystal Pane"),
textures = {
{
name = "nether_geode_glass.png",
align_style = "world",
scale = 2
},
"",
"xpanes_edge_obsidian.png"
},
inventory_image = "([combine:32x32:-8,-8=nether_geode_glass.png:24,-8=nether_geode_glass.png:-8,24=nether_geode_glass.png:24,24=nether_geode_glass.png)^[resize:16x16^[multiply:#922^default_obsidian_glass.png",
wield_image = "([combine:32x32:-8,-8=nether_geode_glass.png:24,-8=nether_geode_glass.png:-8,24=nether_geode_glass.png:24,24=nether_geode_glass.png)^[resize:16x16^[multiply:#922^default_obsidian_glass.png", use_texture_alpha = true,
sounds = default.node_sound_glass_defaults(),
groups = {snappy=2, cracky=3, oddly_breakable_by_hand=3},
recipe = {
{"group:nether_crystal", "group:nether_crystal", "group:nether_crystal"},
{"group:nether_crystal", "group:nether_crystal", "group:nether_crystal"}
}
})
end
-- Deep Netherrack, found in the mantle / central magma layers
minetest.register_node("nether:rack_deep", {
description = S("Deep Netherrack"),
_doc_items_longdesc = S("Netherrack from deep in the mantle"),
tiles = {"nether_rack_deep.png"},
is_ground_content = true,
groups = {cracky = 3, level = 2},
-- setting workable_with_nether_tools reduces the wear on nether:pick_nether when mining this node
groups = {cracky = 3, level = 2, workable_with_nether_tools = 3},
sounds = default.node_sound_stone_defaults(),
})
@ -103,6 +266,7 @@ minetest.register_node("nether:glowstone", {
paramtype = "light",
groups = {cracky = 3, oddly_breakable_by_hand = 3},
sounds = default.node_sound_glass_defaults(),
can_dig = transmogrified_can_dig, -- to ensure glowstone temporarily created by the lightstaff can't be kept
})
-- Deep glowstone, found in the mantle / central magma layers
@ -114,6 +278,7 @@ minetest.register_node("nether:glowstone_deep", {
paramtype = "light",
groups = {cracky = 3, oddly_breakable_by_hand = 3},
sounds = default.node_sound_glass_defaults(),
can_dig = transmogrified_can_dig, -- to ensure glowstone temporarily created by the lightstaff can't be kept
})
minetest.register_node("nether:brick", {
@ -141,60 +306,131 @@ minetest.register_node("nether:brick_cracked", {
sounds = default.node_sound_stone_defaults(),
})
minetest.register_node("nether:brick_deep", {
description = S("Deep Nether Brick"),
tiles = {{
name = "nether_brick_deep.png",
align_style = "world",
scale = 2
}},
is_ground_content = false,
groups = {cracky = 2, level = 2},
sounds = default.node_sound_stone_defaults()
})
-- Register fence and rails
local fence_texture =
"default_fence_overlay.png^nether_brick.png^default_fence_overlay.png^[makealpha:255,126,126"
minetest.register_node("nether:fence_nether_brick", {
local rail_texture =
"default_fence_rail_overlay.png^nether_brick.png^default_fence_rail_overlay.png^[makealpha:255,126,126"
default.register_fence("nether:fence_nether_brick", {
description = S("Nether Brick Fence"),
drawtype = "fencelike",
tiles = {"nether_brick.png"},
texture = "nether_brick.png",
inventory_image = fence_texture,
wield_image = fence_texture,
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
selection_box = {
type = "fixed",
fixed = {-1/7, -1/2, -1/7, 1/7, 1/2, 1/7},
},
material = "nether:brick",
groups = {cracky = 2, level = 2},
sounds = default.node_sound_stone_defaults(),
sounds = default.node_sound_stone_defaults()
})
default.register_fence_rail("nether:fence_rail_nether_brick", {
description = S("Nether Brick Fence Rail"),
texture = "nether_brick.png",
inventory_image = rail_texture,
wield_image = rail_texture,
material = "nether:brick",
groups = {cracky = 2, level = 2},
sounds = default.node_sound_stone_defaults()
})
-- Register stair and slab
stairs.register_stair_and_slab(
"nether_brick",
"nether:brick",
{cracky = 2, level = 2},
{"nether_brick.png"},
S("Nether Stair"),
S("Nether Slab"),
default.node_sound_stone_defaults(),
nil,
S("Inner Nether Stair"),
S("Outer Nether Stair")
-- Nether bricks can be made into stairs, slabs, inner stairs, and outer stairs
stairs.register_stair_and_slab( -- this function also registers inner and outer stairs
"nether_brick", -- subname
"nether:brick", -- recipeitem
{cracky = 2, level = 2}, -- groups
{"nether_brick.png"}, -- images
S("Nether Stair"), -- desc_stair
S("Nether Slab"), -- desc_slab
minetest.registered_nodes["nether:brick"].sounds, -- sounds
false, -- worldaligntex
S("Inner Nether Stair"), -- desc_stair_inner
S("Outer Nether Stair") -- desc_stair_outer
)
stairs.register_stair_and_slab( -- this function also registers inner and outer stairs
"nether_brick_deep", -- subname
"nether:brick_deep", -- recipeitem
{cracky = 2, level = 2}, -- groups
{"nether_brick_deep.png"}, -- images
S("Deep Nether Stair"), -- desc_stair
S("Deep Nether Slab"), -- desc_slab
minetest.registered_nodes["nether:brick_deep"].sounds, -- sounds
false, -- worldaligntex
S("Inner Deep Nether Stair"), -- desc_stair_inner
S("Outer Deep Nether Stair") -- desc_stair_outer
)
-- Netherrack can be shaped into stairs, slabs and walls
stairs.register_stair(
"netherrack",
"nether:rack",
{cracky = 2, level = 2},
{"nether_rack.png"},
S("Netherrack stair"),
default.node_sound_stone_defaults()
S("Netherrack Stair"),
minetest.registered_nodes["nether:rack"].sounds
)
stairs.register_slab( -- register a slab without adding inner and outer stairs
"netherrack",
"nether:rack",
{cracky = 2, level = 2},
{"nether_rack.png"},
S("Netherrack Slab"),
minetest.registered_nodes["nether:rack"].sounds
)
stairs.register_stair(
"netherrack_deep",
"nether:rack_deep",
{cracky = 2, level = 2},
{"nether_rack_deep.png"},
S("Deep Netherrack Stair"),
minetest.registered_nodes["nether:rack_deep"].sounds
)
stairs.register_slab( -- register a slab without adding inner and outer stairs
"netherrack_deep",
"nether:rack_deep",
{cracky = 2, level = 2},
{"nether_rack_deep.png"},
S("Deep Netherrack Slab"),
minetest.registered_nodes["nether:rack_deep"].sounds
)
-- Connecting walls
if minetest.get_modpath("walls") and minetest.global_exists("walls") and walls.register ~= nil then
walls.register("nether:rack_wall", S("A Netherrack Wall"), "nether_rack.png", "nether:rack", minetest.registered_nodes["nether:rack"].sounds)
walls.register("nether:rack_deep_wall", S("A Deep Netherrack Wall"), "nether_rack_deep.png", "nether:rack_deep", minetest.registered_nodes["nether:rack_deep"].sounds)
end
-- StairsPlus
if minetest.get_modpath("moreblocks") then
-- Registers about 49 different shapes of nether brick, replacing the stairs & slabs registered above.
-- (This could also be done for deep nether brick, but I've left that out to avoid a precedent of 49 new
-- nodes every time the nether gets a new material. Nether structures won't be able to use them because
-- they can't depend on moreblocks)
stairsplus:register_all(
"nether", "brick", "nether:brick", {
description = S("Nether Brick"),
groups = {cracky = 2, level = 2},
tiles = {"nether_brick.png"},
sounds = default.node_sound_stone_defaults(),
sounds = minetest.registered_nodes["nether:brick"].sounds,
})
end
@ -308,6 +544,8 @@ lavasea_source.tiles = {
},
},
}
lavasea_source.groups = { not_in_creative_inventory = 1 } -- Avoid having two lava source blocks in the inv.
for key, value in pairs(lava_source.groups) do lavasea_source.groups[key] = value end
lavasea_source.liquid_alternative_source = "nether:lava_source"
lavasea_source.inventory_image = minetest.inventorycube(
"nether_lava_source_animated.png^[sheet:2x16:0,0",
@ -327,8 +565,8 @@ nether.cool_lava = function(pos, node)
-- Evaporate water sitting above lava, if it's in the Nether.
-- (we don't want Nether mod to affect overworld lava mechanics)
if minetest.get_node_group(node_above.name, "water") > 0 and
pos.y < nether.DEPTH_CEILING and pos.y > nether.DEPTH_FLOOR then
if minetest.get_item_group(node_above.name, "water") > 0 and
pos.y < nether.DEPTH_CEILING and pos.y > nether.DEPTH_FLOOR_LAYERS then
-- cools_lava might be a better group to check for, but perhaps there's
-- something in that group that isn't a liquid and shouldn't be evaporated?
minetest.swap_node(pos_above, {name="air"})
@ -374,7 +612,7 @@ minetest.register_on_mods_loaded(function()
-- register a bucket of Lava-sea source - but make it just the same bucket as default lava.
-- (by doing this in register_on_mods_loaded we don't need to declare a soft dependency)
if minetest.get_modpath("bucket") and minetest.global_exists("bucket") then
if minetest.get_modpath("bucket") and minetest.global_exists("bucket") and type(bucket.liquids) == "table" then
local lava_bucket = bucket.liquids["default:lava_source"]
if lava_bucket ~= nil then
local lavasea_bucket = {}
@ -430,11 +668,11 @@ local function smash_lava_crust(pos, playsound)
maxexptime = 1,
minsize = .2,
maxsize = .8,
texture = "^[colorize:#A00:255",
texture = "blank.png^[noalpha^[colorize:#A00:255",
glow = 8
}
minetest.add_particlespawner(lava_particlespawn_def)
lava_particlespawn_def.texture = "^[colorize:#FB0:255"
lava_particlespawn_def.texture = "blank.png^[noalpha^[colorize:#FB0:255"
lava_particlespawn_def.maxvel.y = 3
lava_particlespawn_def.glow = 12
minetest.add_particlespawner(lava_particlespawn_def)
@ -527,7 +765,7 @@ minetest.register_node("nether:lava_crust", {
paramtype = "light",
light_source = default.LIGHT_MAX - 3,
buildable_to = false,
walkable_to = true,
walkable = true,
is_ground_content = true,
drop = {
items = {{
@ -540,6 +778,7 @@ minetest.register_node("nether:lava_crust", {
--liquid_viscosity = 7,
damage_per_second = 2,
groups = {oddly_breakable_by_hand = 3, cracky = 3, explody = 1, igniter = 1},
sounds = default.node_sound_gravel_defaults(),
})
@ -595,14 +834,14 @@ local function fumarole_onTimer(pos, elapsed)
-- 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 inNether = pos.y <= nether.DEPTH and pos.y >= nether.DEPTH_FLOOR_LAYERS
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"
local extinguish = inNether and minetest.get_node(posAbove).name ~= "air"
if extinguish or (canCatchFire and math.floor(elapsed) % 7 == 0) then
@ -635,11 +874,11 @@ local function fumarole_onTimer(pos, elapsed)
maxexptime = 1.4,
minsize = .2,
maxsize = .8,
texture = "^[colorize:#A00:255",
texture = "blank.png^[noalpha^[colorize:#A00:255",
glow = 8
}
minetest.add_particlespawner(embers_particlespawn_def)
embers_particlespawn_def.texture = "^[colorize:#A50:255"
embers_particlespawn_def.texture = "blank.png^[noalpha^[colorize:#A50:255"
embers_particlespawn_def.maxvel.y = 3
embers_particlespawn_def.glow = 12
minetest.add_particlespawner(embers_particlespawn_def)

View File

@ -2007,7 +2007,8 @@ local wormhole_nodedef_default = {
a = 160, r = 128, g = 0, b = 80
},
sunlight_propagates = true,
use_texture_alpha = true,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes
and "blend" or true,
walkable = false,
diggable = false,
pointable = false,
@ -2015,7 +2016,6 @@ local wormhole_nodedef_default = {
is_ground_content = false,
drop = "",
light_source = 5,
alpha = 192,
node_box = {
type = "fixed",
fixed = {
@ -2073,7 +2073,7 @@ function nether.register_portal(name, portaldef)
end
portaldef.name = name
portaldef.mod_name = minetest.get_current_modname()
portaldef.mod_name = minetest.get_current_modname() or "<mod name not recorded>"
-- use portaldef_default for any values missing from portaldef or portaldef.sounds
if portaldef.sounds ~= nil then setmetatable(portaldef.sounds, {__index = portaldef_default.sounds}) end
@ -2166,22 +2166,20 @@ function nether.unregister_portal(name)
end
function nether.register_portal_ignition_item(item_name, ignition_failure_sound)
local old_on_place = minetest.registered_items[item_name].on_place or minetest.item_place
minetest.override_item(item_name, {
on_place = function(stack, placer, pt)
local done = false
on_place = function(stack, placer, pt, ...)
if pt.under and nether.is_frame_node[minetest.get_node(pt.under).name] then
done = ignite_portal(pt.under, placer:get_player_name())
local done = ignite_portal(pt.under, placer:get_player_name())
if done and not minetest.settings:get_bool("creative_mode") then
stack:take_item()
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
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
return old_on_place(stack, placer, pt, ...)
end,
})
@ -2211,7 +2209,7 @@ function nether.volume_is_natural_and_unprotected(minp, maxp, player_name)
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]
if not nodedef.is_ground_content then
if nodedef and not nodedef.is_ground_content then
-- trees are natural but not "ground content"
local node_groups = nodedef.groups
if node_groups == nil or (node_groups.tree == nil and node_groups.leaves == nil and node_groups.leafdecay == nil) then

View File

@ -250,16 +250,16 @@ Used by `nether.register_portal`.
-- 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),
on_run_wormhole = function(portalDef, anchorPos, orientation),
-- invoked once per second per portal
on_extinguish = function(portalDef, anochorPos, orientation),
on_extinguish = function(portalDef, anchorPos, orientation),
-- invoked when a portal is extinguished, including when the portal
-- it connected to was extinguished.
on_player_teleported = function(portalDef, player, oldPos, newPos),
-- invoked immediately after a player is teleported
on_ignite = function(portalDef, anochorPos, orientation)
on_ignite = function(portalDef, anchorPos, orientation)
-- invoked when a player or mesecon ignites a portal
on_created = function(portalDef, anochorPos, orientation)
on_created = function(portalDef, anchorPos, orientation)
-- invoked when a portal creates a remote twin, this is usually when
-- a player travels through a portal for the first time.
}

View File

@ -106,7 +106,7 @@ end
-- Surface-travel portal, playable code example --
--==============================================--
-- These Moore Curve functions requred by surface_portal's find_surface_anchorPos() will
-- These Moore Curve functions required by surface_portal's find_surface_anchorPos() will
-- be assigned later in this file.
local get_moore_distance -- will be function get_moore_distance(cell_count, x, y): integer
local get_moore_coords -- will be function get_moore_coords(cell_count, distance): pos2d

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

BIN
textures/nether_geode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

263
tools.lua
View File

@ -20,18 +20,33 @@
local S = nether.get_translator
minetest.register_tool("nether:pick_nether", {
description = S("Nether Pickaxe"),
description = S("Nether Pickaxe\nWell suited for mining netherrack"),
_doc_items_longdesc = S("Uniquely suited for mining netherrack, with minimal wear when doing so. Blunts quickly on other materials."),
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},
cracky = {times={[1]=1.90, [2]=0.9, [3]=0.3}, uses=35, maxlevel=2},
},
damage_groups = {fleshy=4},
},
sound = {breaks = "default_tool_breaks"},
groups = {pickaxe = 1}
groups = {pickaxe = 1},
after_use = function(itemstack, user, node, digparams)
local wearDivisor = 1
local nodeDef = minetest.registered_nodes[node.name]
if nodeDef ~= nil and nodeDef.groups ~= nil then
-- The nether pick hardly wears out when mining netherrack
local workable = nodeDef.groups.workable_with_nether_tools or 0
wearDivisor = 1 + (3 * workable) -- 10 for netherrack, 1 otherwise. Making it able to mine 350 netherrack nodes, instead of 35.
end
local wear = math.floor(digparams.wear / wearDivisor)
itemstack:add_wear(wear) -- apply the adjusted wear as usual
return itemstack
end
})
minetest.register_tool("nether:shovel_nether", {
@ -136,3 +151,245 @@ minetest.register_craft({
{"group:stick"}
}
})
if minetest.get_modpath("toolranks") then
local function add_toolranks(name)
local nethertool_after_use = ItemStack(name):get_definition().after_use
toolranks.add_tool(name)
local toolranks_after_use = ItemStack(name):get_definition().after_use
if nethertool_after_use == nil or nethertool_after_use == toolranks_after_use then
return
end
minetest.override_item(name, {
after_use = function(itemstack, user, node, digparams)
-- combine nethertool_after_use and toolranks_after_use by allowing
-- nethertool_after_use() to calculate the wear...
local initial_wear = itemstack:get_wear()
itemstack = nethertool_after_use(itemstack, user, node, digparams)
local wear = itemstack:get_wear() - initial_wear
itemstack:set_wear(initial_wear) -- restore/undo the wear
-- ...and have toolranks_after_use() apply the wear.
digparams.wear = wear
return toolranks_after_use(itemstack, user, node, digparams)
end
})
end
add_toolranks("nether:pick_nether")
add_toolranks("nether:shovel_nether")
add_toolranks("nether:axe_nether")
add_toolranks("nether:sword_nether")
end
--===========================--
--== Nether Staff of Light ==--
--===========================--
nether.lightstaff_recipes = {
["nether:rack"] = "nether:glowstone",
["nether:brick"] = "nether:glowstone",
["nether:brick_cracked"] = "nether:glowstone",
["nether:brick_compressed"] = "nether:glowstone",
["stairs:slab_netherrack"] = "nether:glowstone",
["nether:rack_deep"] = "nether:glowstone_deep",
["nether:brick_deep"] = "nether:glowstone_deep",
["stairs:slab_netherrack_deep"] = "nether:glowstone_deep"
}
nether.lightstaff_range = 100
nether.lightstaff_velocity = 60
nether.lightstaff_gravity = 0 -- using 0 instead of 10 because projectile arcs look less magical - magic isn't affected by gravity ;) (but set this to 10 if you're making a crossbow etc.)
nether.lightstaff_uses = 60 -- number of times the Eternal Lightstaff can be used before wearing out
nether.lightstaff_duration = 40 -- lifespan of glowstone created by the termporay Lightstaff
-- 'serverLag' is a rough amount to reduce the projected impact-time the server must wait before initiating the
-- impact events (i.e. node changing to glowstone with explosion particle effect).
-- In tests using https://github.com/jagt/clumsy to simulate network lag I've found this value to not noticeably
-- matter. A large network lag is noticeable in the time between clicking fire and when the shooting-particleEffect
-- begins, as well as the time between when the impact sound/particleEffect start and when the netherrack turns
-- into glowstone. The synchronization that 'serverLag' adjusts seems to already tolerate network lag well enough (at
-- least when lag is consistent, as I have not simulated random lag)
local serverLag = 0.05 -- in seconds. Larger values makes impact events more premature/early.
-- returns a pointed_thing, or nil if no solid node intersected the ray
local function raycastForSolidNode(rayStartPos, rayEndPos)
local raycast = minetest.raycast(
rayStartPos,
rayEndPos,
false, -- objects - if false, only nodes will be returned. Default is `true`
true -- liquids - if false, liquid nodes won't be returned. Default is `false`
)
local next_pointed = raycast:next()
while next_pointed do
local under_node = minetest.get_node(next_pointed.under)
local under_def = minetest.registered_nodes[under_node.name]
if (under_def and not under_def.buildable_to) or not under_def then
return next_pointed
end
next_pointed = raycast:next(next_pointed)
end
return nil
end
-- Turns a node into a light source
-- `lightDuration` 0 is considered permanent, lightDuration is in seconds
-- returns true if a node is transmogrified into a glowstone
local function light_node(pos, playerName, lightDuration)
local result = false
if minetest.is_protected(pos, playerName) then
minetest.record_protection_violation(pos, playerName)
return false
end
local oldNode = minetest.get_node(pos)
local litNodeName = nether.lightstaff_recipes[oldNode.name]
if litNodeName ~= nil then
result = nether.magicallyTransmogrify_node(
pos,
playerName,
{name=litNodeName},
{name = "nether_rack_destroy", gain = 0.8},
lightDuration == 0 -- isPermanent
)
if lightDuration > 0 then
minetest.after(lightDuration,
function()
-- Restore the node to its original type.
--
-- If the server crashes or shuts down before this is invoked, the node
-- will remain in its transmogrified state. These could be cleaned up
-- with an LBM, but I don't think that's necessary: if this functionality
-- is only being used for the Nether Lightstaff then I don't think it
-- matters if there's occasionally an extra glowstone left in the
-- netherrack.
nether.magicallyTransmogrify_node(pos, playerName)
end
)
end
end
return result
end
-- a lightDuration of 0 is considered permanent, lightDuration is in seconds
-- returns true if a node is transmogrified into a glowstone
local function lightstaff_on_use(user, boltColorString, lightDuration)
if not user then return false end
local playerName = user:get_player_name()
local playerlookDir = user:get_look_dir()
local playerPos = user:get_pos()
local playerEyePos = vector.add(playerPos, {x = 0, y = 1.5, z = 0}) -- not always the cameraPos, e.g. 3rd person mode.
local target = vector.add(playerEyePos, vector.multiply(playerlookDir, nether.lightstaff_range))
local targetHitPos = nil
local targetNodePos = nil
local target_pointed = raycastForSolidNode(playerEyePos, target)
if target_pointed then
targetNodePos = target_pointed.under
targetHitPos = vector.divide(vector.add(target_pointed.under, target_pointed.above), 2)
end
local wieldOffset = {x= 0.5, y = -0.2, z= 0.8}
local lookRotation = ({x = -user:get_look_vertical(), y = user:get_look_horizontal(), z = 0})
local wieldPos = vector.add(playerEyePos, vector.rotate(wieldOffset, lookRotation))
local aimPos = targetHitPos or target
local distance = math.abs(vector.length(vector.subtract(aimPos, wieldPos)))
local flightTime = distance / nether.lightstaff_velocity
local dropDistance = nether.lightstaff_gravity * 0.5 * (flightTime * flightTime)
aimPos.y = aimPos.y + dropDistance
local boltDir = vector.normalize(vector.subtract(aimPos, wieldPos))
minetest.sound_play("nether_lightstaff", {to_player = playerName, gain = 0.8}, true)
-- animate a "magic bolt" from wieldPos to aimPos
local particleSpawnDef = {
amount = 20,
time = 0.4,
minpos = vector.add(wieldPos, -0.13),
maxpos = vector.add(wieldPos, 0.13),
minvel = vector.multiply(boltDir, nether.lightstaff_velocity - 0.3),
maxvel = vector.multiply(boltDir, nether.lightstaff_velocity + 0.3),
minacc = {x=0, y=-nether.lightstaff_gravity, z=0},
maxacc = {x=0, y=-nether.lightstaff_gravity, z=0},
minexptime = 1,
maxexptime = 2,
minsize = 4,
maxsize = 5,
collisiondetection = true,
collision_removal = true,
texture = "nether_particle_anim3.png",
animation = { type = "vertical_frames", aspect_w = 7, aspect_h = 7, length = 0.8 },
glow = 15
}
minetest.add_particlespawner(particleSpawnDef)
particleSpawnDef.texture = "nether_particle_anim3.png^[colorize:" .. boltColorString .. ":alpha"
particleSpawnDef.amount = 12
particleSpawnDef.time = 0.2
particleSpawnDef.minsize = 6
particleSpawnDef.maxsize = 7
particleSpawnDef.minpos = vector.add(wieldPos, -0.35)
particleSpawnDef.maxpos = vector.add(wieldPos, 0.35)
minetest.add_particlespawner(particleSpawnDef)
local result = false
if targetNodePos then
-- delay the impact until roughly when the particle effects will have reached the target
minetest.after(
math.max(0, (distance / nether.lightstaff_velocity) - serverLag),
function()
light_node(targetNodePos, playerName, lightDuration)
end
)
if lightDuration ~= 0 then
-- we don't need to care whether the transmogrify will be successful
result = true
else
-- check whether the transmogrify will be successful
local targetNode = minetest.get_node(targetNodePos)
result = nether.lightstaff_recipes[targetNode.name] ~= nil
end
end
return result
end
-- Inspired by FaceDeer's torch crossbow and Xanthin's Staff of Light
minetest.register_tool("nether:lightstaff", {
description = S("Nether staff of Light\nTemporarily transforms the netherrack into glowstone"),
inventory_image = "nether_lightstaff.png",
wield_image = "nether_lightstaff.png",
light_source = 11, -- used by wielded_light mod etc.
stack_max = 1,
on_use = function(itemstack, user, pointed_thing)
lightstaff_on_use(user, "#F70", nether.lightstaff_duration)
end
})
minetest.register_tool("nether:lightstaff_eternal", {
description = S("Nether staff of Eternal Light\nCreates glowstone from netherrack"),
inventory_image = "nether_lightstaff.png^[colorize:#55F:90",
wield_image = "nether_lightstaff.png^[colorize:#55F:90",
light_source = 11, -- used by wielded_light mod etc.
sound = {breaks = "default_tool_breaks"},
stack_max = 1,
on_use = function(itemstack, user, pointed_thing)
if lightstaff_on_use(user, "#23F", 0) -- was "#8088FF" or "#13F"
and not minetest.is_creative_enabled(user) then
-- The staff of Eternal Light wears out, to limit how much
-- a player can alter the nether with it.
itemstack:add_wear_by_uses(nether.lightstaff_uses)
end
return itemstack
end
})