nodes.lua 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  1. --[[
  2. Nether mod for minetest
  3. Copyright (C) 2013 PilzAdam
  4. Permission to use, copy, modify, and/or distribute this software for
  5. any purpose with or without fee is hereby granted, provided that the
  6. above copyright notice and this permission notice appear in all copies.
  7. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
  8. WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
  9. WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
  10. BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
  11. OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  12. WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  13. ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  14. SOFTWARE.
  15. ]]--
  16. local S = nether.get_translator
  17. -- Portal/wormhole nodes
  18. nether.register_wormhole_node("nether:portal", {
  19. description = S("Nether Portal"),
  20. post_effect_color = {
  21. -- post_effect_color can't be changed dynamically in Minetest like the portal colour is.
  22. -- If you need a different post_effect_color then use register_wormhole_node to create
  23. -- another wormhole node and set it as the wormhole_node_name in your portaldef.
  24. -- Hopefully this colour is close enough to magenta to work with the traditional magenta
  25. -- portals, close enough to red to work for a red portal, and also close enough to red to
  26. -- work with blue & cyan portals - since blue portals are sometimes portrayed as being red
  27. -- from the opposite side / from the inside.
  28. a = 160, r = 128, g = 0, b = 80
  29. }
  30. })
  31. local portal_animation2 = {
  32. name = "nether_portal_alt.png",
  33. animation = {
  34. type = "vertical_frames",
  35. aspect_w = 16,
  36. aspect_h = 16,
  37. length = 0.5,
  38. },
  39. }
  40. nether.register_wormhole_node("nether:portal_alt", {
  41. description = S("Portal"),
  42. tiles = {
  43. "nether_transparent.png",
  44. "nether_transparent.png",
  45. "nether_transparent.png",
  46. "nether_transparent.png",
  47. portal_animation2,
  48. portal_animation2
  49. },
  50. post_effect_color = {
  51. -- hopefully blue enough to work with blue portals, and green enough to
  52. -- work with cyan portals.
  53. a = 120, r = 0, g = 128, b = 188
  54. }
  55. })
  56. --== Transmogrification functions ==--
  57. -- Functions enabling selected nodes to be temporarily transformed into other nodes.
  58. -- (so the light staff can temporarily turn netherrack into glowstone)
  59. -- Swaps the node at `nodePos` with `newNode`, unless `newNode` is nil in which
  60. -- case the node is swapped back to its original type.
  61. -- `monoSimpleSoundSpec` is optional.
  62. -- returns true if a node was transmogrified
  63. nether.magicallyTransmogrify_node = function(nodePos, playerName, newNode, monoSimpleSoundSpec, isPermanent)
  64. local meta = minetest.get_meta(nodePos)
  65. local playerEyePos = nodePos -- fallback value in case the player no longer exists
  66. local player = minetest.get_player_by_name(playerName)
  67. if player ~= nil then
  68. local playerPos = player:get_pos()
  69. playerEyePos = vector.add(playerPos, {x = 0, y = 1.5, z = 0}) -- not always the cameraPos, e.g. 3rd person mode.
  70. end
  71. local oldNode = minetest.get_node(nodePos)
  72. if oldNode.name == "air" then
  73. -- the node has been mined or otherwise destroyed, abort the operation
  74. return false
  75. end
  76. local oldNodeDef = minetest.registered_nodes[oldNode.name] or minetest.registered_nodes["air"]
  77. local specialFXSize = 1 -- a specialFXSize of 1 is for full SFX, 0.5 is half-sized
  78. local returningToNormal = newNode == nil
  79. if returningToNormal then
  80. -- This is the transmogrified node returning back to normal - a more subdued animation
  81. specialFXSize = 0.5
  82. -- read what the node used to be from the metadata
  83. newNode = {
  84. name = meta:get_string("transmogrified_name"),
  85. param1 = meta:get_string("transmogrified_param1"),
  86. param2 = meta:get_string("transmogrified_param2")
  87. }
  88. if newNode.name == "" then
  89. minetest.log("warning", "nether.magicallyTransmogrify_node() invoked to restore node which wasn't transmogrified")
  90. return false
  91. end
  92. end
  93. local soundSpec = monoSimpleSoundSpec
  94. if soundSpec == nil and oldNodeDef.sounds ~= nil then
  95. soundSpec = oldNodeDef.sounds.dug or oldNodeDef.sounds.dig
  96. if soundSpec == "__group" then soundSpec = "default_dig_cracky" end
  97. end
  98. if soundSpec ~= nil then
  99. minetest.sound_play(soundSpec, {pos = nodePos, max_hear_distance = 50})
  100. end
  101. -- Start the particlespawner nearer the player's side of the node to create
  102. -- more initial occlusion for an illusion of the old node breaking apart / falling away.
  103. local dirToPlayer = vector.normalize(vector.subtract(playerEyePos, nodePos))
  104. local impactPos = vector.add(nodePos, vector.multiply(dirToPlayer, 0.5))
  105. local velocity = 1 + specialFXSize
  106. minetest.add_particlespawner({
  107. amount = 50 * specialFXSize,
  108. time = 0.1,
  109. minpos = vector.add(impactPos, -0.3),
  110. maxpos = vector.add(impactPos, 0.3),
  111. minvel = {x = -velocity, y = -velocity, z = -velocity},
  112. maxvel = {x = velocity, y = 3 * velocity, z = velocity}, -- biased upward to counter gravity in the initial stages
  113. minacc = {x=0, y=-10, z=0},
  114. maxacc = {x=0, y=-10, z=0},
  115. minexptime = 1.5 * specialFXSize,
  116. maxexptime = 3 * specialFXSize,
  117. minsize = 0.5,
  118. maxsize = 5,
  119. node = {name = oldNodeDef.name},
  120. glow = oldNodeDef.light_source
  121. })
  122. if returningToNormal or isPermanent then
  123. -- clear the metadata that indicates the node is transformed
  124. meta:set_string("transmogrified_name", "")
  125. meta:set_int("transmogrified_param1", 0)
  126. meta:set_int("transmogrified_param2", 0)
  127. else
  128. -- save the original node so it can be restored
  129. meta:set_string("transmogrified_name", oldNode.name)
  130. meta:set_int("transmogrified_param1", oldNode.param1)
  131. meta:set_int("transmogrified_param2", oldNode.param2)
  132. end
  133. minetest.swap_node(nodePos, newNode)
  134. return true
  135. end
  136. local function transmogrified_can_dig (pos, player)
  137. if minetest.get_meta(pos):get_string("transmogrified_name") ~= "" then
  138. -- This node was temporarily transformed into its current form
  139. -- revert it back, rather than allow the player to mine transmogrified nodes.
  140. local playerName = ""
  141. if player ~= nil then playerName = player:get_player_name() end
  142. nether.magicallyTransmogrify_node(pos, playerName)
  143. return false
  144. end
  145. return true
  146. end
  147. -- Nether nodes
  148. minetest.register_node("nether:rack", {
  149. description = S("Netherrack"),
  150. tiles = {"nether_rack.png"},
  151. is_ground_content = true,
  152. -- setting workable_with_nether_tools reduces the wear on nether:pick_nether when mining this node
  153. groups = {cracky = 3, level = 2, workable_with_nether_tools = 3},
  154. sounds = default.node_sound_stone_defaults(),
  155. })
  156. -- Geode crystals can only be introduced by the biomes-based mapgen, since it requires the
  157. -- MT 5.0 world-align texture features.
  158. minetest.register_node("nether:geode", {
  159. description = S("Nether Beryl"),
  160. _doc_items_longdesc = S("Nether geode crystal, found lining the interior walls of Nether geodes"),
  161. tiles = {{
  162. name = "nether_geode.png",
  163. align_style = "world",
  164. scale = 4
  165. }},
  166. is_ground_content = true,
  167. groups = {cracky = 3, oddly_breakable_by_hand = 3, nether_crystal = 1},
  168. sounds = default.node_sound_glass_defaults(),
  169. })
  170. -- Nether Berylite is a Beryl that can seen in the dark, used to light up the internal structure
  171. -- of the geode, so to avoid player confusion we'll just have it drop plain Beryl, and have only
  172. -- plain Beryl in the creative inventory.
  173. minetest.register_node("nether:geodelite", {
  174. description = S("Nether Berylite"),
  175. _doc_items_longdesc = S("Nether geode crystal. A crystalline structure with faint glow found inside large Nether geodes"),
  176. tiles = {{
  177. name = "nether_geode.png",
  178. align_style = "world",
  179. scale = 4
  180. }},
  181. light_source = 2,
  182. drop = "nether:geode",
  183. is_ground_content = true,
  184. groups = {cracky = 3, oddly_breakable_by_hand = 3, nether_crystal = 1, not_in_creative_inventory = 1},
  185. sounds = default.node_sound_glass_defaults(),
  186. })
  187. if minetest.get_modpath("xpanes") and minetest.global_exists("xpanes") and xpanes.register_pane ~= nil then
  188. xpanes.register_pane("nether_crystal_pane", {
  189. description = S("Nether Crystal Pane"),
  190. textures = {
  191. {
  192. name = "nether_geode_glass.png",
  193. align_style = "world",
  194. scale = 2
  195. },
  196. "",
  197. "xpanes_edge_obsidian.png"
  198. },
  199. 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",
  200. 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,
  201. sounds = default.node_sound_glass_defaults(),
  202. groups = {snappy=2, cracky=3, oddly_breakable_by_hand=3},
  203. recipe = {
  204. {"group:nether_crystal", "group:nether_crystal", "group:nether_crystal"},
  205. {"group:nether_crystal", "group:nether_crystal", "group:nether_crystal"}
  206. }
  207. })
  208. end
  209. -- Deep Netherrack, found in the mantle / central magma layers
  210. minetest.register_node("nether:rack_deep", {
  211. description = S("Deep Netherrack"),
  212. _doc_items_longdesc = S("Netherrack from deep in the mantle"),
  213. tiles = {"nether_rack_deep.png"},
  214. is_ground_content = true,
  215. -- setting workable_with_nether_tools reduces the wear on nether:pick_nether when mining this node
  216. groups = {cracky = 3, level = 2, workable_with_nether_tools = 3},
  217. sounds = default.node_sound_stone_defaults(),
  218. })
  219. minetest.register_node("nether:sand", {
  220. description = S("Nethersand"),
  221. tiles = {"nether_sand.png"},
  222. is_ground_content = true,
  223. groups = {crumbly = 3, level = 2, falling_node = 1},
  224. sounds = default.node_sound_gravel_defaults({
  225. footstep = {name = "default_gravel_footstep", gain = 0.45},
  226. }),
  227. })
  228. minetest.register_node("nether:glowstone", {
  229. description = S("Glowstone"),
  230. tiles = {"nether_glowstone.png"},
  231. is_ground_content = true,
  232. light_source = 14,
  233. paramtype = "light",
  234. groups = {cracky = 3, oddly_breakable_by_hand = 3},
  235. sounds = default.node_sound_glass_defaults(),
  236. can_dig = transmogrified_can_dig, -- to ensure glowstone temporarily created by the lightstaff can't be kept
  237. })
  238. -- Deep glowstone, found in the mantle / central magma layers
  239. minetest.register_node("nether:glowstone_deep", {
  240. description = S("Deep Glowstone"),
  241. tiles = {"nether_glowstone_deep.png"},
  242. is_ground_content = true,
  243. light_source = 14,
  244. paramtype = "light",
  245. groups = {cracky = 3, oddly_breakable_by_hand = 3},
  246. sounds = default.node_sound_glass_defaults(),
  247. can_dig = transmogrified_can_dig, -- to ensure glowstone temporarily created by the lightstaff can't be kept
  248. })
  249. minetest.register_node("nether:brick", {
  250. description = S("Nether Brick"),
  251. tiles = {"nether_brick.png"},
  252. is_ground_content = false,
  253. groups = {cracky = 2, level = 2},
  254. sounds = default.node_sound_stone_defaults(),
  255. })
  256. minetest.register_node("nether:brick_compressed", {
  257. description = S("Compressed Netherbrick"),
  258. tiles = {"nether_brick_compressed.png"},
  259. groups = {cracky = 3, level = 2},
  260. is_ground_content = false,
  261. sounds = default.node_sound_stone_defaults(),
  262. })
  263. -- A decorative node which can only be obtained from dungeons or structures
  264. minetest.register_node("nether:brick_cracked", {
  265. description = S("Cracked Nether Brick"),
  266. tiles = {"nether_brick_cracked.png"},
  267. is_ground_content = false,
  268. groups = {cracky = 2, level = 2},
  269. sounds = default.node_sound_stone_defaults(),
  270. })
  271. minetest.register_node("nether:brick_deep", {
  272. description = S("Deep Nether Brick"),
  273. tiles = {{
  274. name = "nether_brick_deep.png",
  275. align_style = "world",
  276. scale = 2
  277. }},
  278. is_ground_content = false,
  279. groups = {cracky = 2, level = 2},
  280. sounds = default.node_sound_stone_defaults()
  281. })
  282. -- Register fence and rails
  283. local fence_texture =
  284. "default_fence_overlay.png^nether_brick.png^default_fence_overlay.png^[makealpha:255,126,126"
  285. local rail_texture =
  286. "default_fence_rail_overlay.png^nether_brick.png^default_fence_rail_overlay.png^[makealpha:255,126,126"
  287. default.register_fence("nether:fence_nether_brick", {
  288. description = S("Nether Brick Fence"),
  289. texture = "nether_brick.png",
  290. inventory_image = fence_texture,
  291. wield_image = fence_texture,
  292. material = "nether:brick",
  293. groups = {cracky = 2, level = 2},
  294. sounds = default.node_sound_stone_defaults()
  295. })
  296. default.register_fence_rail("nether:fence_rail_nether_brick", {
  297. description = S("Nether Brick Fence Rail"),
  298. texture = "nether_brick.png",
  299. inventory_image = rail_texture,
  300. wield_image = rail_texture,
  301. material = "nether:brick",
  302. groups = {cracky = 2, level = 2},
  303. sounds = default.node_sound_stone_defaults()
  304. })
  305. -- Register stair and slab
  306. -- Nether bricks can be made into stairs, slabs, inner stairs, and outer stairs
  307. stairs.register_stair_and_slab( -- this function also registers inner and outer stairs
  308. "nether_brick", -- subname
  309. "nether:brick", -- recipeitem
  310. {cracky = 2, level = 2}, -- groups
  311. {"nether_brick.png"}, -- images
  312. S("Nether Stair"), -- desc_stair
  313. S("Nether Slab"), -- desc_slab
  314. minetest.registered_nodes["nether:brick"].sounds, -- sounds
  315. false, -- worldaligntex
  316. S("Inner Nether Stair"), -- desc_stair_inner
  317. S("Outer Nether Stair") -- desc_stair_outer
  318. )
  319. stairs.register_stair_and_slab( -- this function also registers inner and outer stairs
  320. "nether_brick_deep", -- subname
  321. "nether:brick_deep", -- recipeitem
  322. {cracky = 2, level = 2}, -- groups
  323. {"nether_brick_deep.png"}, -- images
  324. S("Deep Nether Stair"), -- desc_stair
  325. S("Deep Nether Slab"), -- desc_slab
  326. minetest.registered_nodes["nether:brick_deep"].sounds, -- sounds
  327. false, -- worldaligntex
  328. S("Inner Deep Nether Stair"), -- desc_stair_inner
  329. S("Outer Deep Nether Stair") -- desc_stair_outer
  330. )
  331. -- Netherrack can be shaped into stairs, slabs and walls
  332. stairs.register_stair(
  333. "netherrack",
  334. "nether:rack",
  335. {cracky = 2, level = 2},
  336. {"nether_rack.png"},
  337. S("Netherrack Stair"),
  338. minetest.registered_nodes["nether:rack"].sounds
  339. )
  340. stairs.register_slab( -- register a slab without adding inner and outer stairs
  341. "netherrack",
  342. "nether:rack",
  343. {cracky = 2, level = 2},
  344. {"nether_rack.png"},
  345. S("Netherrack Slab"),
  346. minetest.registered_nodes["nether:rack"].sounds
  347. )
  348. stairs.register_stair(
  349. "netherrack_deep",
  350. "nether:rack_deep",
  351. {cracky = 2, level = 2},
  352. {"nether_rack_deep.png"},
  353. S("Deep Netherrack Stair"),
  354. minetest.registered_nodes["nether:rack_deep"].sounds
  355. )
  356. stairs.register_slab( -- register a slab without adding inner and outer stairs
  357. "netherrack_deep",
  358. "nether:rack_deep",
  359. {cracky = 2, level = 2},
  360. {"nether_rack_deep.png"},
  361. S("Deep Netherrack Slab"),
  362. minetest.registered_nodes["nether:rack_deep"].sounds
  363. )
  364. -- Connecting walls
  365. if minetest.get_modpath("walls") and minetest.global_exists("walls") and walls.register ~= nil then
  366. walls.register("nether:rack_wall", S("A Netherrack Wall"), "nether_rack.png", "nether:rack", minetest.registered_nodes["nether:rack"].sounds)
  367. 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)
  368. end
  369. -- StairsPlus
  370. if minetest.get_modpath("moreblocks") then
  371. -- Registers about 49 different shapes of nether brick, replacing the stairs & slabs registered above.
  372. -- (This could also be done for deep nether brick, but I've left that out to avoid a precedent of 49 new
  373. -- nodes every time the nether gets a new material. Nether structures won't be able to use them because
  374. -- they can't depend on moreblocks)
  375. stairsplus:register_all(
  376. "nether", "brick", "nether:brick", {
  377. description = S("Nether Brick"),
  378. groups = {cracky = 2, level = 2},
  379. tiles = {"nether_brick.png"},
  380. sounds = minetest.registered_nodes["nether:brick"].sounds,
  381. })
  382. end
  383. -- Mantle nodes
  384. -- Nether basalt is intended as a valuable material and possible portalstone - an alternative to
  385. -- obsidian that's available for other mods to use.
  386. -- It cannot be found in the regions of the nether where Nether portals link to, so requires a journey to obtain.
  387. minetest.register_node("nether:basalt", {
  388. description = S("Nether Basalt"),
  389. _doc_items_longdesc = S("Columns of dark basalt found only in magma oceans deep within the Nether."),
  390. tiles = {
  391. "nether_basalt.png",
  392. "nether_basalt.png",
  393. "nether_basalt_side.png",
  394. "nether_basalt_side.png",
  395. "nether_basalt_side.png",
  396. "nether_basalt_side.png"
  397. },
  398. is_ground_content = true,
  399. groups = {cracky = 1, level = 3}, -- set proper digging times and uses, and maybe explosion immune if api handles that
  400. on_blast = function() --[[blast proof]] end,
  401. sounds = default.node_sound_stone_defaults(),
  402. })
  403. -- Potentially a portalstone, but will also be a stepping stone between basalt
  404. -- and chiseled basalt.
  405. -- It can only be introduced by the biomes-based mapgen, since it requires the
  406. -- MT 5.0 world-align texture features.
  407. minetest.register_node("nether:basalt_hewn", {
  408. description = S("Hewn Basalt"),
  409. _doc_items_longdesc = S("A rough cut solid block of Nether Basalt."),
  410. tiles = {{
  411. name = "nether_basalt_hewn.png",
  412. align_style = "world",
  413. scale = 2
  414. }},
  415. inventory_image = minetest.inventorycube(
  416. "nether_basalt_hewn.png^[sheet:2x2:0,0",
  417. "nether_basalt_hewn.png^[sheet:2x2:0,1",
  418. "nether_basalt_hewn.png^[sheet:2x2:1,1"
  419. ),
  420. is_ground_content = false,
  421. groups = {cracky = 1, level = 2},
  422. on_blast = function() --[[blast proof]] end,
  423. sounds = default.node_sound_stone_defaults(),
  424. })
  425. -- Chiselled basalt is intended as a portalstone - an alternative to obsidian that's
  426. -- available for other mods to use. It is crafted from Hewn Basalt.
  427. -- It should only be introduced by the biomes-based mapgen, since in future it may
  428. -- require the MT 5.0 world-align texture features.
  429. minetest.register_node("nether:basalt_chiselled", {
  430. description = S("Chiselled Basalt"),
  431. _doc_items_longdesc = S("A finely finished block of solid Nether Basalt."),
  432. tiles = {
  433. "nether_basalt_chiselled_top.png",
  434. "nether_basalt_chiselled_top.png" .. "^[transformFY",
  435. "nether_basalt_chiselled_side.png",
  436. "nether_basalt_chiselled_side.png",
  437. "nether_basalt_chiselled_side.png",
  438. "nether_basalt_chiselled_side.png"
  439. },
  440. inventory_image = minetest.inventorycube(
  441. "nether_basalt_chiselled_top.png",
  442. "nether_basalt_chiselled_side.png",
  443. "nether_basalt_chiselled_side.png"
  444. ),
  445. paramtype2 = "facedir",
  446. is_ground_content = false,
  447. groups = {cracky = 1, level = 2},
  448. on_blast = function() --[[blast proof]] end,
  449. sounds = default.node_sound_stone_defaults(),
  450. })
  451. minetest.register_node('nether:basalt_brick', {
  452. description = 'Basalt Brick',
  453. tiles = {{name='nether_basalt_brick.png', align_style='world', scale=4}},
  454. inventory_image = '[inventorycube{nether_basalt_brick.png&[sheet:4x4:1,1{nether_basalt_brick.png&[sheet:4x4:1,1{nether_basalt_brick.png&[sheet:4x4:1,1',
  455. groups = {cracky = 2, level = 3},
  456. sounds = default.node_sound_stone_defaults(),
  457. })
  458. -- Lava-sea source
  459. -- This is a lava source using a different animated texture so that each node
  460. -- is out of phase in its animation from its neighbor. This prevents the magma
  461. -- ocean from visually clumping together into a patchwork of 16x16 squares.
  462. -- It can only be used by the biomes-based mapgen, since it requires the MT 5.0
  463. -- world-align texture features.
  464. local lavasea_source = {}
  465. local lava_source = minetest.registered_nodes["default:lava_source"]
  466. for key, value in pairs(lava_source) do lavasea_source[key] = value end
  467. lavasea_source.name = nil
  468. lavasea_source.tiles = {
  469. {
  470. name = "nether_lava_source_animated.png",
  471. backface_culling = false,
  472. align_style = "world",
  473. scale = 2,
  474. animation = {
  475. type = "vertical_frames",
  476. aspect_w = 32,
  477. aspect_h = 32,
  478. length = 3.0,
  479. },
  480. },
  481. {
  482. name = "nether_lava_source_animated.png",
  483. backface_culling = true,
  484. align_style = "world",
  485. scale = 2,
  486. animation = {
  487. type = "vertical_frames",
  488. aspect_w = 32,
  489. aspect_h = 32,
  490. length = 3.0,
  491. },
  492. },
  493. }
  494. lavasea_source.groups = { not_in_creative_inventory = 1 } -- Avoid having two lava source blocks in the inv.
  495. for key, value in pairs(lava_source.groups) do lavasea_source.groups[key] = value end
  496. lavasea_source.liquid_alternative_source = "nether:lava_source"
  497. lavasea_source.inventory_image = minetest.inventorycube(
  498. "nether_lava_source_animated.png^[sheet:2x16:0,0",
  499. "nether_lava_source_animated.png^[sheet:2x16:0,1",
  500. "nether_lava_source_animated.png^[sheet:2x16:1,1"
  501. )
  502. minetest.register_node("nether:lava_source", lavasea_source)
  503. -- a place to store the original ABM function so nether.cool_lava() can call it
  504. local original_cool_lava_action
  505. nether.cool_lava = function(pos, node)
  506. local pos_above = {x = pos.x, y = pos.y + 1, z = pos.z}
  507. local node_above = minetest.get_node(pos_above)
  508. -- Evaporate water sitting above lava, if it's in the Nether.
  509. -- (we don't want Nether mod to affect overworld lava mechanics)
  510. if minetest.get_item_group(node_above.name, "water") > 0 and
  511. pos.y < nether.DEPTH_CEILING and pos.y > nether.DEPTH_FLOOR_LAYERS then
  512. -- cools_lava might be a better group to check for, but perhaps there's
  513. -- something in that group that isn't a liquid and shouldn't be evaporated?
  514. minetest.swap_node(pos_above, {name="air"})
  515. end
  516. -- add steam to cooling lava
  517. minetest.add_particlespawner({
  518. amount = 20,
  519. time = 0.15,
  520. minpos = {x=pos.x - 0.4, y=pos.y - 0, z=pos.z - 0.4},
  521. maxpos = {x=pos.x + 0.4, y=pos.y + 0.5, z=pos.z + 0.4},
  522. minvel = {x = -0.5, y = 0.5, z = -0.5},
  523. maxvel = {x = 0.5, y = 1.5, z = 0.5},
  524. minacc = {x = 0, y = 0.1, z = 0},
  525. maxacc = {x = 0, y = 0.2, z = 0},
  526. minexptime = 0.5,
  527. maxexptime = 1.3,
  528. minsize = 1.5,
  529. maxsize = 3.5,
  530. texture = "nether_particle_anim4.png",
  531. animation = {
  532. type = "vertical_frames",
  533. aspect_w = 7,
  534. aspect_h = 7,
  535. length = 1.4,
  536. }
  537. })
  538. if node.name == "nether:lava_source" or node.name == "nether:lava_crust" then
  539. -- use swap_node to avoid triggering the lava_crust's after_destruct
  540. minetest.swap_node(pos, {name = "nether:basalt"})
  541. minetest.sound_play("default_cool_lava",
  542. {pos = pos, max_hear_distance = 16, gain = 0.25}, true)
  543. else
  544. -- chain the original ABM action to handle conventional lava
  545. original_cool_lava_action(pos, node)
  546. end
  547. end
  548. minetest.register_on_mods_loaded(function()
  549. -- register a bucket of Lava-sea source - but make it just the same bucket as default lava.
  550. -- (by doing this in register_on_mods_loaded we don't need to declare a soft dependency)
  551. if minetest.get_modpath("bucket") and minetest.global_exists("bucket") and type(bucket.liquids) == "table" then
  552. local lava_bucket = bucket.liquids["default:lava_source"]
  553. if lava_bucket ~= nil then
  554. local lavasea_bucket = {}
  555. for key, value in pairs(lava_bucket) do lavasea_bucket[key] = value end
  556. lavasea_bucket.source = "nether:lava_source"
  557. bucket.liquids[lavasea_bucket.source] = lavasea_bucket
  558. end
  559. end
  560. -- include "nether:lava_source" in any "default:lava_source" ABMs
  561. local function include_nether_lava(set_of_nodes)
  562. if (type(set_of_nodes) == "table") then
  563. for _, nodename in pairs(set_of_nodes) do
  564. if nodename == "default:lava_source" then
  565. -- I'm amazed this works, but it does
  566. table.insert(set_of_nodes, "nether:lava_source")
  567. break;
  568. end
  569. end
  570. end
  571. end
  572. for _, abm in pairs(minetest.registered_abms) do
  573. include_nether_lava(abm.nodenames)
  574. include_nether_lava(abm.neighbors)
  575. if abm.label == "Lava cooling" and abm.action ~= nil then
  576. -- lets have lava_crust cool as well
  577. original_cool_lava_action = abm.action
  578. abm.action = nether.cool_lava
  579. table.insert(abm.nodenames, "nether:lava_crust")
  580. end
  581. end
  582. for _, lbm in pairs(minetest.registered_lbms) do
  583. include_nether_lava(lbm.nodenames)
  584. end
  585. --minetest.log("minetest.registered_abms" .. dump(minetest.registered_abms))
  586. --minetest.log("minetest.registered_lbms" .. dump(minetest.registered_lbms))
  587. end)
  588. -- creates a lava splash, and leaves lava_source in place of the lava_crust
  589. local function smash_lava_crust(pos, playsound)
  590. local lava_particlespawn_def = {
  591. amount = 6,
  592. time = 0.1,
  593. minpos = {x=pos.x - 0.5, y=pos.y + 0.3, z=pos.z - 0.5},
  594. maxpos = {x=pos.x + 0.5, y=pos.y + 0.5, z=pos.z + 0.5},
  595. minvel = {x = -1.5, y = 1.5, z = -1.5},
  596. maxvel = {x = 1.5, y = 5, z = 1.5},
  597. minacc = {x = 0, y = -10, z = 0},
  598. maxacc = {x = 0, y = -10, z = 0},
  599. minexptime = 1,
  600. maxexptime = 1,
  601. minsize = .2,
  602. maxsize = .8,
  603. texture = "^[colorize:#A00:255",
  604. glow = 8
  605. }
  606. minetest.add_particlespawner(lava_particlespawn_def)
  607. lava_particlespawn_def.texture = "^[colorize:#FB0:255"
  608. lava_particlespawn_def.maxvel.y = 3
  609. lava_particlespawn_def.glow = 12
  610. minetest.add_particlespawner(lava_particlespawn_def)
  611. if pos.y < 0 then
  612. minetest.set_node(pos, {name = "default:lava_source"})
  613. else
  614. minetest.remove_node(pos)
  615. end
  616. if math.random(1, 3) == 1 and minetest.registered_nodes["fire:basic_flame"] ~= nil then
  617. -- occasionally brief flames will be seen when breaking lava crust
  618. local posAbove = {x = pos.x, y = pos.y + 1, z = pos.z}
  619. if minetest.get_node(posAbove).name == "air" then
  620. minetest.set_node(posAbove, {name = "fire:basic_flame"})
  621. minetest.get_node_timer(posAbove):set(math.random(7, 15) / 10, 0)
  622. --[[ commented out because the flame sound plays for too long
  623. if minetest.global_exists("fire") and fire.update_player_sound ~= nil then
  624. -- The fire mod only updates its sound every 3 seconds, these flames will be
  625. -- out by then, so start the sound immediately
  626. local players = minetest.get_connected_players()
  627. for n = 1, #players do fire.update_player_sound(players[n]) end
  628. end]]
  629. end
  630. end
  631. if playsound then
  632. minetest.sound_play(
  633. "nether_lava_bubble",
  634. -- this sample was encoded at 3x speed to reduce .ogg file size
  635. -- at the expense of higher frequencies, so pitch it down ~3x
  636. {pos = pos, pitch = 0.3, max_hear_distance = 8, gain = 0.4}
  637. )
  638. end
  639. end
  640. -- lava_crust nodes can only be used in the biomes-based mapgen, since they require
  641. -- the MT 5.0 world-align texture features.
  642. minetest.register_node("nether:lava_crust", {
  643. description = S("Lava Crust"),
  644. _doc_items_longdesc = S("A thin crust of cooled lava with liquid lava beneath"),
  645. _doc_items_usagehelp = S("Lava crust is strong enough to walk on, but still hot enough to inflict burns."),
  646. tiles = {
  647. {
  648. name="nether_lava_crust_animated.png",
  649. backface_culling=true,
  650. tileable_vertical=true,
  651. tileable_horizontal=true,
  652. align_style="world",
  653. scale=2,
  654. animation = {
  655. type = "vertical_frames",
  656. aspect_w = 32,
  657. aspect_h = 32,
  658. length = 1.8,
  659. },
  660. }
  661. },
  662. inventory_image = minetest.inventorycube(
  663. "nether_lava_crust_animated.png^[sheet:2x48:0,0",
  664. "nether_lava_crust_animated.png^[sheet:2x48:0,1",
  665. "nether_lava_crust_animated.png^[sheet:2x48:1,1"
  666. ),
  667. collision_box = {
  668. type = "fixed",
  669. fixed = {
  670. -- Damage is calculated "starting 0.1 above feet
  671. -- and progressing upwards in 1 node intervals", so
  672. -- lower this node's collision box by more than 0.1
  673. -- to ensure damage will be taken when standing on
  674. -- the node.
  675. {-0.5, -0.5, -0.5, 0.5, 0.39, 0.5}
  676. },
  677. },
  678. selection_box = {
  679. type = "fixed",
  680. fixed = {
  681. -- Keep the selection box matching the visual node,
  682. -- rather than the collision_box.
  683. {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}
  684. },
  685. },
  686. after_destruct = function(pos, oldnode)
  687. smash_lava_crust(pos, true)
  688. end,
  689. after_dig_node = function(pos, oldnode, oldmetadata, digger)
  690. end,
  691. on_blast = function(pos, intensity)
  692. smash_lava_crust(pos, false)
  693. end,
  694. paramtype = "light",
  695. light_source = default.LIGHT_MAX - 3,
  696. buildable_to = false,
  697. walkable = true,
  698. is_ground_content = true,
  699. drop = {
  700. items = {{
  701. -- Allow SilkTouch-esque "pickaxes of preservation" to mine the lava crust intact, if PR #10141 gets merged.
  702. tools = {"this line will block early MT versions which don't respect the tool_groups restrictions"},
  703. tool_groups = {{"pickaxe", "preservation"}},
  704. items = {"nether:lava_crust"}
  705. }}
  706. },
  707. --liquid_viscosity = 7,
  708. damage_per_second = 2,
  709. groups = {oddly_breakable_by_hand = 3, cracky = 3, explody = 1, igniter = 1},
  710. sounds = default.node_sound_gravel_defaults(),
  711. })
  712. -- Fumaroles (Chimney's)
  713. local function fumarole_startTimer(pos, timeout_factor)
  714. if timeout_factor == nil then timeout_factor = 1 end
  715. local next_timeout = (math.random(50, 900) / 10) * timeout_factor
  716. minetest.get_meta(pos):set_float("expected_timeout", next_timeout)
  717. minetest.get_node_timer(pos):start(next_timeout)
  718. end
  719. -- Create an LBM to start fumarole node timers
  720. minetest.register_lbm({
  721. label = "Start fumarole smoke",
  722. name = "nether:start_fumarole",
  723. nodenames = {"nether:fumarole"},
  724. run_at_every_load = true,
  725. action = function(pos, node)
  726. local node_above = minetest.get_node({x = pos.x, y = pos.y + 1, z = pos.z})
  727. if node_above.name == "air" then --and node.param2 % 4 == 0 then
  728. fumarole_startTimer(pos)
  729. end
  730. end
  731. })
  732. local function set_fire(pos, extinguish)
  733. local posBelow = {x = pos.x, y = pos.y - 1, z = pos.z}
  734. if extinguish then
  735. if minetest.get_node(pos).name == "fire:permanent_flame" then minetest.set_node(pos, {name="air"}) end
  736. if minetest.get_node(posBelow).name == "fire:permanent_flame" then minetest.set_node(posBelow, {name="air"}) end
  737. elseif minetest.get_node(posBelow).name == "air" then
  738. minetest.set_node(posBelow, {name="fire:permanent_flame"})
  739. elseif minetest.get_node(pos).name == "air" then
  740. minetest.set_node(pos, {name="fire:permanent_flame"})
  741. end
  742. end
  743. local function fumarole_onTimer(pos, elapsed)
  744. local expected_timeout = minetest.get_meta(pos):get_float("expected_timeout")
  745. if elapsed > expected_timeout + 10 then
  746. -- The timer didn't fire when it was supposed to, so the chunk was probably inactive and has
  747. -- just been approached again, meaning *every* fumarole's on_timer is about to go off.
  748. -- Skip this event and restart the clock for a future random interval.
  749. fumarole_startTimer(pos, 1)
  750. return false
  751. end
  752. -- Fumaroles in the Nether can catch fire.
  753. -- (if taken to the surface and used as cottage chimneys, they don't catch fire)
  754. local inNether = pos.y <= nether.DEPTH and pos.y >= nether.DEPTH_FLOOR_LAYERS
  755. local canCatchFire = inNether and minetest.registered_nodes["fire:permanent_flame"] ~= nil
  756. local smoke_offset = 0
  757. local timeout_factor = 1
  758. local smoke_time_adj = 1
  759. local posAbove = {x = pos.x, y = pos.y + 1, z = pos.z}
  760. local extinguish = inNether and minetest.get_node(posAbove).name ~= "air"
  761. if extinguish or (canCatchFire and math.floor(elapsed) % 7 == 0) then
  762. if not extinguish then
  763. -- fumarole gasses are igniting
  764. smoke_offset = 1
  765. timeout_factor = 0.22 -- reduce burning time
  766. end
  767. set_fire(posAbove, extinguish)
  768. set_fire({x = pos.x + 1, y = pos.y + 1, z = pos.z}, extinguish)
  769. set_fire({x = pos.x - 1, y = pos.y + 1, z = pos.z}, extinguish)
  770. set_fire({x = pos.x, y = pos.y + 1, z = pos.z + 1}, extinguish)
  771. set_fire({x = pos.x, y = pos.y + 1, z = pos.z - 1}, extinguish)
  772. elseif inNether then
  773. if math.floor(elapsed) % 3 == 1 then
  774. -- throw up some embers / lava splash
  775. local embers_particlespawn_def = {
  776. amount = 6,
  777. time = 0.1,
  778. minpos = {x=pos.x - 0.1, y=pos.y + 0.0, z=pos.z - 0.1},
  779. maxpos = {x=pos.x + 0.1, y=pos.y + 0.2, z=pos.z + 0.1},
  780. minvel = {x = -.5, y = 4.5, z = -.5},
  781. maxvel = {x = .5, y = 7, z = .5},
  782. minacc = {x = 0, y = -10, z = 0},
  783. maxacc = {x = 0, y = -10, z = 0},
  784. minexptime = 1.4,
  785. maxexptime = 1.4,
  786. minsize = .2,
  787. maxsize = .8,
  788. texture = "^[colorize:#A00:255",
  789. glow = 8
  790. }
  791. minetest.add_particlespawner(embers_particlespawn_def)
  792. embers_particlespawn_def.texture = "^[colorize:#A50:255"
  793. embers_particlespawn_def.maxvel.y = 3
  794. embers_particlespawn_def.glow = 12
  795. minetest.add_particlespawner(embers_particlespawn_def)
  796. else
  797. -- gas noises
  798. minetest.sound_play("nether_fumarole", {
  799. pos = pos,
  800. max_hear_distance = 60,
  801. gain = 0.24,
  802. pitch = math.random(35, 95) / 100
  803. })
  804. end
  805. else
  806. -- we're not in the Nether, so can afford to be a bit more smokey
  807. timeout_factor = 0.4
  808. smoke_time_adj = 1.3
  809. end
  810. -- let out some smoke
  811. minetest.add_particlespawner({
  812. amount = 12 * smoke_time_adj,
  813. time = math.random(40, 60) / 10 * smoke_time_adj,
  814. minpos = {x=pos.x - 0.2, y=pos.y + smoke_offset, z=pos.z - 0.2},
  815. maxpos = {x=pos.x + 0.2, y=pos.y + smoke_offset, z=pos.z + 0.2},
  816. minvel = {x=0, y=0.7, z=-0},
  817. maxvel = {x=0, y=0.8, z=-0},
  818. minacc = {x=0.0,y=0.0,z=-0},
  819. maxacc = {x=0.0,y=0.1,z=-0},
  820. minexptime = 5,
  821. maxexptime = 5.5,
  822. minsize = 1.5,
  823. maxsize = 7,
  824. texture = "nether_smoke_puff.png",
  825. })
  826. fumarole_startTimer(pos, timeout_factor)
  827. return false
  828. end
  829. minetest.register_node("nether:fumarole", {
  830. description=S("Fumarolic Chimney"),
  831. _doc_items_longdesc = S("A vent in the earth emitting steam and gas"),
  832. _doc_items_usagehelp = S("Can be repurposed to provide puffs of smoke in a chimney"),
  833. tiles = {"nether_rack.png"},
  834. on_timer = fumarole_onTimer,
  835. after_place_node = function(pos, placer, itemstack, pointed_thing)
  836. fumarole_onTimer(pos, 1)
  837. return false
  838. end,
  839. is_ground_content = true,
  840. groups = {cracky = 3, level = 2, fumarole=1},
  841. paramtype = "light",
  842. drawtype = "nodebox",
  843. node_box = {
  844. type = "fixed",
  845. fixed = {
  846. {-0.5000, -0.5000, -0.5000, -0.2500, 0.5000, 0.5000},
  847. {-0.5000, -0.5000, -0.5000, 0.5000, 0.5000, -0.2500},
  848. {-0.5000, -0.5000, 0.2500, 0.5000, 0.5000, 0.5000},
  849. {0.2500, -0.5000, -0.5000, 0.5000, 0.5000, 0.5000}
  850. }
  851. },
  852. selection_box = {type = 'fixed', fixed = {-.5, -.5, -.5, .5, .5, .5}}
  853. })
  854. minetest.register_node("nether:fumarole_slab", {
  855. description=S("Fumarolic Chimney Slab"),
  856. _doc_items_longdesc = S("A vent in the earth emitting steam and gas"),
  857. _doc_items_usagehelp = S("Can be repurposed to provide puffs of smoke in a chimney"),
  858. tiles = {"nether_rack.png"},
  859. is_ground_content = true,
  860. on_timer = fumarole_onTimer,
  861. after_place_node = function(pos, placer, itemstack, pointed_thing)
  862. fumarole_onTimer(pos, 1)
  863. return false
  864. end,
  865. groups = {cracky = 3, level = 2, fumarole=1},
  866. paramtype = "light",
  867. drawtype = "nodebox",
  868. node_box = {
  869. type = "fixed",
  870. fixed = {
  871. {-0.5000, -0.5000, -0.5000, -0.2500, 0.000, 0.5000},
  872. {-0.5000, -0.5000, -0.5000, 0.5000, 0.000, -0.2500},
  873. {-0.5000, -0.5000, 0.2500, 0.5000, 0.000, 0.5000},
  874. {0.2500, -0.5000, -0.5000, 0.5000, 0.000, 0.5000}
  875. }
  876. },
  877. selection_box = {type = 'fixed', fixed = {-.5, -.5, -.5, .5, 0, .5}},
  878. collision_box = {type = 'fixed', fixed = {-.5, -.5, -.5, .5, 0, .5}}
  879. })
  880. minetest.register_node("nether:fumarole_corner", {
  881. description=S("Fumarolic Chimney Corner"),
  882. tiles = {"nether_rack.png"},
  883. is_ground_content = true,
  884. groups = {cracky = 3, level = 2, fumarole=1},
  885. paramtype = "light",
  886. paramtype2 = "facedir",
  887. drawtype = "nodebox",
  888. node_box = {
  889. type = "fixed",
  890. fixed = {
  891. {-0.2500, -0.5000, 0.5000, 0.000, 0.5000, 0.000},
  892. {-0.5000, -0.5000, 0.2500, 0.000, 0.5000, 0.000},
  893. {-0.5000, -0.5000, 0.2500, 0.000, 0.000, -0.5000},
  894. {0.000, -0.5000, -0.5000, 0.5000, 0.000, 0.5000}
  895. }
  896. },
  897. selection_box = {
  898. type = 'fixed',
  899. fixed = {
  900. {-.5, -.5, -.5, .5, 0, .5},
  901. {0, 0, .5, -.5, .5, 0},
  902. }
  903. }
  904. })
  905. -- nether:airlike_darkness is an air node through which light does not propagate.
  906. -- Use of it should be avoided when possible as it has the appearance of a lighting bug.
  907. -- Fumarole decorations use it to stop the propagation of light from the lava below,
  908. -- since engine limitations mean any mesh or nodebox node will light up if it has lava
  909. -- below it.
  910. local airlike_darkness = {}
  911. for k,v in pairs(minetest.registered_nodes["air"]) do airlike_darkness[k] = v end
  912. airlike_darkness.paramtype = "none"
  913. minetest.register_node("nether:airlike_darkness", airlike_darkness)