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