Merge branch 'biomes'
This commit is contained in:
commit
4f03f1c516
85 changed files with 4562 additions and 703 deletions
|
@ -203,3 +203,5 @@ mesecon:register_mvps_stopper("mesecons_solarpanel:solar_panel_inverted_off")
|
|||
mesecon:register_mvps_stopper("mesecons_solarpanel:solar_panel_inverted_on")
|
||||
mesecon:register_mvps_stopper("mesecons_noteblock:noteblock")
|
||||
mesecon:register_mvps_stopper("3d_armor_stand:armor_stand")
|
||||
mesecon:register_mvps_stopper("mcl_portals:portal")
|
||||
mesecon:register_mvps_stopper("mcl_portals:portal_end")
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
mcl_sounds?
|
||||
mcl_util?
|
||||
mcl_wool?
|
||||
mcl_dye?
|
||||
mcl_tnt?
|
||||
|
|
|
@ -136,6 +136,17 @@ function mcl_beds.skip_night()
|
|||
end
|
||||
|
||||
function mcl_beds.on_rightclick(pos, player)
|
||||
if minetest.get_modpath("mcl_init") then
|
||||
local _, dim = mcl_util.y_to_layer(pos.y)
|
||||
if dim == "nether" or dim == "end" then
|
||||
-- Bed goes BOOM in the Nether or End.
|
||||
minetest.remove_node(pos)
|
||||
if minetest.get_modpath("mcl_tnt") then
|
||||
tnt.boom(pos, {radius = 4, damage_radius = 4})
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
local name = player:get_player_name()
|
||||
local ppos = player:getpos()
|
||||
local tod = minetest.get_timeofday() * 24000
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
-- Minetest 0.4 mod: bucket
|
||||
-- See README.txt for licensing and other information.
|
||||
|
||||
local LIQUID_MAX = 8 --The number of water levels when liquid_finite is enabled
|
||||
|
||||
minetest.register_alias("bucket:bucket_empty", "mcl_buckets:bucket_empty")
|
||||
minetest.register_alias("bucket:bucket_water", "mcl_buckets:bucket_water")
|
||||
minetest.register_alias("bucket:bucket_lava", "mcl_buckets:bucket_lava")
|
||||
|
@ -39,18 +37,29 @@ local sound_take = function(itemname, pos)
|
|||
end
|
||||
|
||||
-- Register a new liquid
|
||||
-- source = name of the source node
|
||||
-- flowing = name of the flowing node
|
||||
-- itemname = name of the new bucket item (or nil if liquid is not takeable)
|
||||
-- source_place = a string or function.
|
||||
-- * string: name of the node to place
|
||||
-- * function(pos): will returns name of the node to place with pos being the placement position
|
||||
-- source_take = table of liquid source node names to take
|
||||
-- itemname = itemstring of the new bucket item (or nil if liquid is not takeable)
|
||||
-- inventory_image = texture of the new bucket item (ignored if itemname == nil)
|
||||
-- This function can be called from any mod (that depends on bucket).
|
||||
function mcl_buckets.register_liquid(source, flowing, itemname, inventory_image, name, longdesc, usagehelp)
|
||||
mcl_buckets.liquids[source] = {
|
||||
source = source,
|
||||
flowing = flowing,
|
||||
itemname = itemname,
|
||||
}
|
||||
mcl_buckets.liquids[flowing] = mcl_buckets.liquids[source]
|
||||
-- name = user-visible bucket description
|
||||
-- longdesc = long explanatory description (for help)
|
||||
-- usagehelp = short usage explanation (for help)
|
||||
-- extra_check = optional function(pos) which can returns false to avoid placing the liquid
|
||||
--
|
||||
-- This function can be called from any mod (which depends on this one)
|
||||
function mcl_buckets.register_liquid(source_place, source_take, itemname, inventory_image, name, longdesc, usagehelp, extra_check)
|
||||
for i=1, #source_take do
|
||||
mcl_buckets.liquids[source_take[i]] = {
|
||||
source_place = source_place,
|
||||
source_take = source_take[i],
|
||||
itemname = itemname,
|
||||
}
|
||||
if type(source_place) == "string" then
|
||||
mcl_buckets.liquids[source_place] = mcl_buckets.liquids[source_take[i]]
|
||||
end
|
||||
end
|
||||
|
||||
if itemname ~= nil then
|
||||
minetest.register_craftitem(itemname, {
|
||||
|
@ -67,43 +76,38 @@ function mcl_buckets.register_liquid(source, flowing, itemname, inventory_image,
|
|||
end
|
||||
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
local place_pos = pointed_thing.under
|
||||
local nn = node.name
|
||||
-- Call on_rightclick if the pointed node defines it
|
||||
if user and not user:get_player_control().sneak then
|
||||
if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].on_rightclick then
|
||||
return minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
|
||||
return minetest.registered_nodes[nn].on_rightclick(place_pos, node, user, itemstack) or itemstack
|
||||
end
|
||||
end
|
||||
|
||||
local place_liquid = function(pos, node, source, flowing, fullness)
|
||||
sound_place(source, pos)
|
||||
if math.floor(fullness/128) == 1 or (not minetest.settings:get_bool("liquid_finite")) then
|
||||
minetest.add_node(pos, {name=source, param2=fullness})
|
||||
return
|
||||
elseif node.name == flowing then
|
||||
fullness = fullness + node.param2
|
||||
elseif node.name == source then
|
||||
fullness = LIQUID_MAX
|
||||
end
|
||||
|
||||
if fullness >= LIQUID_MAX then
|
||||
minetest.add_node(pos, {name=source, param2=LIQUID_MAX})
|
||||
else
|
||||
minetest.add_node(pos, {name=flowing, param2=fullness})
|
||||
end
|
||||
local place_liquid = function(pos, itemstring)
|
||||
local fullness = minetest.registered_nodes[itemstring].liquid_range
|
||||
sound_place(itemstring, pos)
|
||||
minetest.add_node(pos, {name=itemstring, param2=fullness})
|
||||
end
|
||||
|
||||
local node_place
|
||||
if type(source_place) == "function" then
|
||||
node_place = source_place(place_pos)
|
||||
else
|
||||
node_place = source_place
|
||||
end
|
||||
-- Check if pointing to a buildable node
|
||||
local fullness = tonumber(itemstack:get_metadata())
|
||||
if not fullness then fullness = LIQUID_MAX end
|
||||
local item = itemstack:get_name()
|
||||
|
||||
if item == "mcl_buckets:bucket_water" and
|
||||
if extra_check and extra_check(place_pos) == false then
|
||||
-- Fail placement of liquid
|
||||
elseif item == "mcl_buckets:bucket_water" and
|
||||
(nn == "mcl_cauldrons:cauldron" or
|
||||
nn == "mcl_cauldrons:cauldron_1" or
|
||||
nn == "mcl_cauldrons:cauldron_2") then
|
||||
-- Put water into cauldron
|
||||
minetest.set_node(pointed_thing.under, {name="mcl_cauldrons:cauldron_3"})
|
||||
minetest.set_node(place_pos, {name="mcl_cauldrons:cauldron_3"})
|
||||
|
||||
sound_place("mcl_core:water_source", pos)
|
||||
elseif item == "mcl_buckets:bucket_water" and nn == "mcl_cauldrons:cauldron_3" then
|
||||
|
@ -111,12 +115,12 @@ function mcl_buckets.register_liquid(source, flowing, itemname, inventory_image,
|
|||
elseif minetest.registered_nodes[nn] and minetest.registered_nodes[nn].buildable_to then
|
||||
-- buildable; replace the node
|
||||
local pns = user:get_player_name()
|
||||
if minetest.is_protected(pointed_thing.under, pns) then
|
||||
if minetest.is_protected(place_pos, pns) then
|
||||
return itemstack
|
||||
end
|
||||
place_liquid(pointed_thing.under, node, source, flowing, fullness)
|
||||
if mod_doc and doc.entry_exists("nodes", source) then
|
||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", source)
|
||||
place_liquid(place_pos, node_place)
|
||||
if mod_doc and doc.entry_exists("nodes", node_place) then
|
||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", node_place)
|
||||
end
|
||||
else
|
||||
-- not buildable to; place the liquid above
|
||||
|
@ -127,9 +131,9 @@ function mcl_buckets.register_liquid(source, flowing, itemname, inventory_image,
|
|||
if minetest.is_protected(pointed_thing.above, pn) then
|
||||
return itemstack
|
||||
end
|
||||
place_liquid(pointed_thing.above, node, source, flowing, fullness)
|
||||
if mod_doc and doc.entry_exists("nodes", source) then
|
||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", source)
|
||||
place_liquid(pointed_thing.above, node_place)
|
||||
if mod_doc and doc.entry_exists("nodes", node_place) then
|
||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", node_place)
|
||||
end
|
||||
else
|
||||
-- do not remove the bucket with the liquid
|
||||
|
@ -188,8 +192,7 @@ minetest.register_craftitem("mcl_buckets:bucket_empty", {
|
|||
-- Check if pointing to a liquid source
|
||||
liquiddef = mcl_buckets.liquids[nn]
|
||||
local new_bucket
|
||||
if liquiddef ~= nil and liquiddef.itemname ~= nil and (nn == liquiddef.source or
|
||||
(nn == liquiddef.flowing and minetest.settings:get_bool("liquid_finite"))) then
|
||||
if liquiddef ~= nil and liquiddef.itemname ~= nil and (nn == liquiddef.source_take) then
|
||||
|
||||
-- Fill bucket, but not in Creative Mode
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
|
@ -234,19 +237,37 @@ minetest.register_craftitem("mcl_buckets:bucket_empty", {
|
|||
})
|
||||
|
||||
if mod_mcl_core then
|
||||
-- Water bucket
|
||||
mcl_buckets.register_liquid(
|
||||
"mcl_core:water_source",
|
||||
"mcl_core:water_flowing",
|
||||
{"mcl_core:water_source"},
|
||||
"mcl_buckets:bucket_water",
|
||||
"bucket_water.png",
|
||||
"Water Bucket",
|
||||
"A bucket can be used to collect and release liquids. This one is filled with water.",
|
||||
"Right-click on any block to empty the bucket and put a water source on this spot."
|
||||
"Right-click on any block to empty the bucket and put a water source on this spot.",
|
||||
function(pos)
|
||||
local _, dim = mcl_util.y_to_layer(pos.y)
|
||||
if dim == "nether" then
|
||||
minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16})
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
-- Lava bucket
|
||||
mcl_buckets.register_liquid(
|
||||
"mcl_core:lava_source",
|
||||
"mcl_core:lava_flowing",
|
||||
function(pos)
|
||||
local _, dim = mcl_util.y_to_layer(pos.y)
|
||||
if dim == "nether" then
|
||||
return "mcl_nether:nether_lava_source"
|
||||
else
|
||||
return "mcl_core:lava_source"
|
||||
end
|
||||
end,
|
||||
{"mcl_core:lava_source", "mcl_nether:nether_lava_source"},
|
||||
"mcl_buckets:bucket_lava",
|
||||
"bucket_lava.png",
|
||||
"Lava Bucket",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
mcl_core
|
||||
mcl_init
|
||||
mcl_util
|
||||
mesecons
|
||||
doc?
|
||||
|
|
|
@ -12,10 +12,17 @@ mcl_clock.stereotype = "mcl_clock:clock"
|
|||
local watch = {}
|
||||
watch.old_time = -1
|
||||
|
||||
-- Image of all 64 possible faces
|
||||
local clock_frames = 64
|
||||
|
||||
-- Timer for random clock spinning
|
||||
local random_timer = 0.0
|
||||
local random_timer_trigger = 1.0 -- random clock spinning tick in seconds. Increase if there are performance problems
|
||||
local random_frame = math.random(0, clock_frames-1)
|
||||
|
||||
-- Image of all possible faces
|
||||
watch.images = {}
|
||||
for frame=0,63 do
|
||||
table.insert(watch.images, "mcl_clock_clock.png^[verticalframe:64:"..frame)
|
||||
for frame=0, clock_frames-1 do
|
||||
table.insert(watch.images, "mcl_clock_clock.png^[verticalframe:"..clock_frames..":"..frame)
|
||||
end
|
||||
|
||||
local function round(num)
|
||||
|
@ -23,9 +30,9 @@ local function round(num)
|
|||
end
|
||||
|
||||
function watch.get_clock_frame()
|
||||
local t = 64 * minetest.get_timeofday()
|
||||
local t = clock_frames * minetest.get_timeofday()
|
||||
t = round(t)
|
||||
if t == 64 then t = 0 end
|
||||
if t == clock_frames then t = 0 end
|
||||
return tostring(t)
|
||||
end
|
||||
|
||||
|
@ -65,6 +72,12 @@ local force_clock_update_timer = 0
|
|||
minetest.register_globalstep(function(dtime)
|
||||
local now = watch.get_clock_frame()
|
||||
force_clock_update_timer = force_clock_update_timer + dtime
|
||||
random_timer = random_timer + dtime
|
||||
-- This causes the random spinning of the clock
|
||||
if random_timer >= random_timer_trigger then
|
||||
random_frame = (random_frame + math.random(-4, 4)) % clock_frames
|
||||
random_timer = 0
|
||||
end
|
||||
|
||||
if watch.old_time == now and force_clock_update_timer < 60 then
|
||||
return
|
||||
|
@ -76,11 +89,19 @@ minetest.register_globalstep(function(dtime)
|
|||
local players = minetest.get_connected_players()
|
||||
for p, player in ipairs(players) do
|
||||
for s, stack in ipairs(player:get_inventory():get_list("main")) do
|
||||
local _, dim = mcl_util.y_to_layer(player:getpos().y)
|
||||
local frame
|
||||
-- Clocks do not work in the End, Nether or the Void
|
||||
if dim == "end" or dim == "nether" or dim == "void" then
|
||||
frame = random_frame
|
||||
else
|
||||
frame = now
|
||||
end
|
||||
local count = stack:get_count()
|
||||
if stack:get_name() == mcl_clock.stereotype then
|
||||
player:get_inventory():set_stack("main", s, "mcl_clock:clock_"..now.." "..count)
|
||||
elseif string.sub(stack:get_name(), 1, 16) == "mcl_clock:clock_" then
|
||||
player:get_inventory():set_stack("main", s, "mcl_clock:clock_"..now.." "..count)
|
||||
player:get_inventory():set_stack("main", s, "mcl_clock:clock_"..frame.." "..count)
|
||||
elseif minetest.get_item_group(stack:get_name(), "clock") ~= 0 then
|
||||
player:get_inventory():set_stack("main", s, "mcl_clock:clock_"..frame.." "..count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -107,7 +128,7 @@ minetest.register_craft({
|
|||
watch.register_item(mcl_clock.stereotype, watch.images[1], true, 1)
|
||||
|
||||
-- Faces
|
||||
for a=0,63,1 do
|
||||
for a=0,clock_frames-1,1 do
|
||||
local b = a
|
||||
if b > 31 then
|
||||
b = b - 32
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
mcl_core
|
||||
mcl_util
|
||||
mesecons
|
||||
doc?
|
||||
|
|
|
@ -1,9 +1,23 @@
|
|||
mcl_compass = {}
|
||||
|
||||
local compass_frames = 32
|
||||
|
||||
local default_spawn_settings = minetest.settings:get("static_spawnpoint")
|
||||
|
||||
-- Timer for random compass spinning
|
||||
local random_timer = 0
|
||||
local random_timer_trigger = 0.5 -- random compass spinning tick in seconds. Increase if there are performance problems
|
||||
|
||||
local random_frame = math.random(0, compass_frames-1)
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
random_timer = random_timer + dtime
|
||||
local players = minetest.get_connected_players()
|
||||
|
||||
if random_timer >= random_timer_trigger then
|
||||
random_frame = (random_frame + math.random(-1, 1)) % compass_frames
|
||||
random_timer = 0
|
||||
end
|
||||
for i,player in ipairs(players) do
|
||||
local function has_compass(player)
|
||||
for _,stack in ipairs(player:get_inventory():get_list("main")) do
|
||||
|
@ -14,24 +28,31 @@ minetest.register_globalstep(function(dtime)
|
|||
return false
|
||||
end
|
||||
if has_compass(player) then
|
||||
local spawn = {x=0,y=0,z=0}
|
||||
local s = minetest.settings:get("static_spawnpoint")
|
||||
if s then
|
||||
local numbers = string.split(s, ",")
|
||||
spawn.x = tonumber(numbers[1])
|
||||
spawn.y = tonumber(numbers[2])
|
||||
spawn.z = tonumber(numbers[3])
|
||||
if type(spawn.x) ~= "number" and type(spawn.y) ~= "number" and type(spawn.z) ~= "number" then
|
||||
spawn = {x=0,y=0,z=0}
|
||||
end
|
||||
end
|
||||
local pos = player:getpos()
|
||||
local dir = player:get_look_horizontal()
|
||||
local angle_north = math.deg(math.atan2(spawn.x - pos.x, spawn.z - pos.z))
|
||||
if angle_north < 0 then angle_north = angle_north + 360 end
|
||||
local angle_dir = -math.deg(dir)
|
||||
local angle_relative = (angle_north - angle_dir + 180) % 360
|
||||
local compass_image = math.floor((angle_relative/11.25) + 0.5)%32
|
||||
local _, dim = mcl_util.y_to_layer(pos.y)
|
||||
local compass_image
|
||||
-- Compasses do not work in the End, Nether or the Void
|
||||
if dim == "end" or dim == "nether" or dim == "void" then
|
||||
compass_image = random_frame
|
||||
else
|
||||
local spawn = {x=0,y=0,z=0}
|
||||
local s = minetest.settings:get("static_spawnpoint")
|
||||
if s then
|
||||
local numbers = string.split(s, ",")
|
||||
spawn.x = tonumber(numbers[1])
|
||||
spawn.y = tonumber(numbers[2])
|
||||
spawn.z = tonumber(numbers[3])
|
||||
if type(spawn.x) ~= "number" and type(spawn.y) ~= "number" and type(spawn.z) ~= "number" then
|
||||
spawn = {x=0,y=0,z=0}
|
||||
end
|
||||
end
|
||||
local dir = player:get_look_horizontal()
|
||||
local angle_north = math.deg(math.atan2(spawn.x - pos.x, spawn.z - pos.z))
|
||||
if angle_north < 0 then angle_north = angle_north + 360 end
|
||||
local angle_dir = -math.deg(dir)
|
||||
local angle_relative = (angle_north - angle_dir + 180) % 360
|
||||
compass_image = math.floor((angle_relative/11.25) + 0.5) % compass_frames
|
||||
end
|
||||
|
||||
for j,stack in ipairs(player:get_inventory():get_list("main")) do
|
||||
if minetest.get_item_group(stack:get_name(), "compass") ~= 0 and
|
||||
|
@ -45,7 +66,7 @@ minetest.register_globalstep(function(dtime)
|
|||
end)
|
||||
|
||||
local images = {}
|
||||
for frame=0,31 do
|
||||
for frame = 0, compass_frames-1 do
|
||||
local s = string.format("%02d", frame)
|
||||
table.insert(images, "mcl_compass_compass_"..s..".png")
|
||||
end
|
||||
|
|
|
@ -642,7 +642,7 @@ end
|
|||
|
||||
|
||||
|
||||
local grass_spread_randomizer = PseudoRandom(minetest.get_mapgen_params().seed)
|
||||
local grass_spread_randomizer = PseudoRandom(minetest.get_mapgen_setting("seed"))
|
||||
|
||||
------------------------------
|
||||
-- Spread grass blocks and mycelium on neighbor dirt
|
||||
|
|
|
@ -153,6 +153,8 @@ minetest.register_node("mcl_core:realm_barrier", {
|
|||
wield_image = "mcl_core_barrier.png^[colorize:#FF00FF:127^[transformFX",
|
||||
tiles = { "blank.png" },
|
||||
stack_max = 64,
|
||||
-- To avoid players getting stuck forever between realms
|
||||
damage_per_second = 8,
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
pointable = false,
|
||||
|
|
BIN
mods/ITEMS/mcl_core/schematics/apple_tree.mts
Normal file
BIN
mods/ITEMS/mcl_core/schematics/apple_tree.mts
Normal file
Binary file not shown.
BIN
mods/ITEMS/mcl_core/schematics/aspen_tree.mts
Normal file
BIN
mods/ITEMS/mcl_core/schematics/aspen_tree.mts
Normal file
Binary file not shown.
BIN
mods/ITEMS/mcl_core/schematics/jungle_tree.mts
Normal file
BIN
mods/ITEMS/mcl_core/schematics/jungle_tree.mts
Normal file
Binary file not shown.
BIN
mods/ITEMS/mcl_core/schematics/pine_tree.mts
Normal file
BIN
mods/ITEMS/mcl_core/schematics/pine_tree.mts
Normal file
Binary file not shown.
|
@ -15,17 +15,16 @@ minetest.register_craftitem("mcl_fire:fire_charge", {
|
|||
end
|
||||
end
|
||||
|
||||
-- Ignite/light fire
|
||||
if pointed_thing.type == "node" then
|
||||
if minetest.get_node(pointed_thing.under).name == "mcl_tnt:tnt" then
|
||||
tnt.ignite(pointed_thing.under)
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
itemstack:take_item()
|
||||
end
|
||||
local nodedef = minetest.registered_nodes[node.name]
|
||||
if nodedef and nodedef._on_ignite then
|
||||
nodedef._on_ignite(user, pointed_thing)
|
||||
else
|
||||
mcl_fire.set_fire(pointed_thing)
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
itemstack:take_item()
|
||||
end
|
||||
end
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
itemstack:take_item()
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
minetest.register_tool("mcl_fire:flint_and_steel", {
|
||||
description = "Flint and Steel",
|
||||
_doc_items_longdesc = "Flint and steel is a tool to start fires and ignite blocks.",
|
||||
_doc_items_usagehelp = "Rightclick the surface of a block to attempt to light a fire in front of it. On netherrack and magma blocks it will start an eternal fire. Using it on TNT will ignite it.",
|
||||
_doc_items_usagehelp = "Rightclick the surface of a block to attempt to light a fire in front of it or ignite the block. A few blocks have an unique reaction when ignited.",
|
||||
inventory_image = "mcl_fire_flint_and_steel.png",
|
||||
liquids_pointable = false,
|
||||
stack_max = 1,
|
||||
|
@ -25,7 +25,7 @@ minetest.register_tool("mcl_fire:flint_and_steel", {
|
|||
if pointed_thing.type == "node" then
|
||||
local nodedef = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name]
|
||||
if nodedef and nodedef._on_ignite then
|
||||
nodedef._on_ignite(pointed_thing.under, user)
|
||||
nodedef._on_ignite(user, pointed_thing)
|
||||
else
|
||||
mcl_fire.set_fire(pointed_thing)
|
||||
end
|
||||
|
|
|
@ -376,10 +376,14 @@ local eternal_override = {
|
|||
minetest.remove_node(pos)
|
||||
end
|
||||
end,
|
||||
_on_ignite = function(pos, player)
|
||||
_on_ignite = function(player, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
local flame_pos = {x = pos.x, y = pos.y + 1, z = pos.z}
|
||||
if minetest.get_node(flame_pos).name == "air" then
|
||||
local fn = minetest.get_node(flame_pos)
|
||||
if fn.name == "air" and not minetest.is_protected(flame_pos, "fire") and pointed_thing.under.y < pointed_thing.above.y then
|
||||
minetest.set_node(flame_pos, {name = "mcl_fire:eternal_fire"})
|
||||
else
|
||||
mcl_fire.set_fire(pointed_thing)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
@ -392,7 +396,7 @@ end
|
|||
-- Set pointed_thing on (normal) fire
|
||||
mcl_fire.set_fire = function(pointed_thing)
|
||||
local n = minetest.get_node(pointed_thing.above)
|
||||
if n.name ~= "" and n.name == "air" and not minetest.is_protected(pointed_thing.above, "fire") then
|
||||
if n.name == "air" and not minetest.is_protected(pointed_thing.above, "fire") then
|
||||
minetest.add_node(pointed_thing.above, {name="mcl_fire:fire"})
|
||||
end
|
||||
end
|
||||
|
|
7
mods/ITEMS/mcl_portals/LICENSE
Normal file
7
mods/ITEMS/mcl_portals/LICENSE
Normal file
|
@ -0,0 +1,7 @@
|
|||
The MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
15
mods/ITEMS/mcl_portals/README.md
Normal file
15
mods/ITEMS/mcl_portals/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Portals mod for MineClone 2
|
||||
## How to create portals
|
||||
|
||||
Nether portal: Build an upright frame of obsidian, 4 blocks wide and 5 blocks high, and use a flint and steel inside it.
|
||||
End portal: Build an upright frame of red nether brick blocks, 4 blocks wide and 5 blocks high, and use an eye of ender inside it.
|
||||
|
||||
## Credits
|
||||
Created by maikerumine and Wuzzy.
|
||||
Code license: MIT License (see `LICENSE`).
|
||||
|
||||
Texture license: See main MineClone 2 directory.
|
||||
|
||||
License of sound: [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)
|
||||
Authors: [FreqMan](https://freesound.org/people/FreqMan/) and Wuzzy
|
||||
Source: <https://freesound.org/people/FreqMan/sounds/32541/>
|
7
mods/ITEMS/mcl_portals/depends.txt
Normal file
7
mods/ITEMS/mcl_portals/depends.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
mcl_init
|
||||
mcl_util
|
||||
mcl_core
|
||||
mcl_fire
|
||||
mcl_nether
|
||||
mcl_end
|
||||
doc?
|
1
mods/ITEMS/mcl_portals/description.txt
Normal file
1
mods/ITEMS/mcl_portals/description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Adds buildable portals to the Nether and End dimensions.
|
10
mods/ITEMS/mcl_portals/init.lua
Normal file
10
mods/ITEMS/mcl_portals/init.lua
Normal file
|
@ -0,0 +1,10 @@
|
|||
-- Load files
|
||||
|
||||
-- Nether portal:
|
||||
-- Obsidian frame, activated by flint and steel
|
||||
dofile(minetest.get_modpath("mcl_portals").."/portal_nether.lua")
|
||||
|
||||
-- End portal (W.I.P):
|
||||
-- Red nether brick block frame, activated by an eye of ender
|
||||
dofile(minetest.get_modpath("mcl_portals").."/portal_end.lua")
|
||||
|
1
mods/ITEMS/mcl_portals/mod.conf
Normal file
1
mods/ITEMS/mcl_portals/mod.conf
Normal file
|
@ -0,0 +1 @@
|
|||
name = mcl_portals
|
448
mods/ITEMS/mcl_portals/portal_end.lua
Normal file
448
mods/ITEMS/mcl_portals/portal_end.lua
Normal file
|
@ -0,0 +1,448 @@
|
|||
-- Parameters
|
||||
|
||||
local TCAVE = 0.6
|
||||
local nobj_cave = nil
|
||||
|
||||
local SPAWN_MIN = mcl_vars.mg_end_min+70
|
||||
local SPAWN_MAX = mcl_vars.mg_end_min+98
|
||||
|
||||
-- 3D noise
|
||||
|
||||
local np_cave = {
|
||||
offset = 0,
|
||||
scale = 1,
|
||||
spread = {x = 384, y = 128, z = 384}, -- squashed 3:1
|
||||
seed = 59033,
|
||||
octaves = 5,
|
||||
persist = 0.7
|
||||
}
|
||||
-- Portal frame material
|
||||
local portal_frame = "mcl_nether:quartz_block"
|
||||
|
||||
-- Table of objects (including players) which recently teleported by a
|
||||
-- End portal. Those objects have a brief cooloff period before they
|
||||
-- can teleport again. This prevents annoying back-and-forth teleportation.
|
||||
local portal_cooloff = {}
|
||||
|
||||
-- Destroy portal if pos (portal frame or portal node) got destroyed
|
||||
local destroy_portal = function(pos)
|
||||
-- Deactivate Nether portal
|
||||
local meta = minetest.get_meta(pos)
|
||||
local p1 = minetest.string_to_pos(meta:get_string("portal_frame1"))
|
||||
local p2 = minetest.string_to_pos(meta:get_string("portal_frame2"))
|
||||
if not p1 or not p2 then
|
||||
return
|
||||
end
|
||||
|
||||
local first = true
|
||||
|
||||
-- p1 metadata of first node
|
||||
local mp1
|
||||
for x = p1.x, p2.x do
|
||||
for y = p1.y, p2.y do
|
||||
for z = p1.z, p2.z do
|
||||
local p = vector.new(x, y, z)
|
||||
local m = minetest.get_meta(p)
|
||||
if first then
|
||||
--[[ Only proceed if the first node still has metadata.
|
||||
If it doesn't have metadata, another node propably triggred the delection
|
||||
routine earlier, so we bail out earlier to avoid an infinite cascade
|
||||
of on_destroy events. ]]
|
||||
mp1 = minetest.string_to_pos(m:get_string("portal_frame1"))
|
||||
if not mp1 then
|
||||
return
|
||||
end
|
||||
end
|
||||
local nn = minetest.get_node(p).name
|
||||
if nn == portal_frame or nn == "mcl_portals:portal_end" then
|
||||
-- Remove portal nodes, but not myself
|
||||
if nn == "mcl_portals:portal_end" and not vector.equals(p, pos) then
|
||||
minetest.remove_node(p)
|
||||
end
|
||||
-- Clear metadata of portal nodes and the frame
|
||||
m:set_string("portal_frame1", "")
|
||||
m:set_string("portal_frame2", "")
|
||||
m:set_string("portal_target", "")
|
||||
end
|
||||
first = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Nodes
|
||||
minetest.register_node("mcl_portals:portal_end", {
|
||||
description = "End Portal",
|
||||
_doc_items_longdesc = "An End portal teleports creatures and objects to the mysterious End dimension (and back!).",
|
||||
_doc_items_usagehelp = "Stand in the portal for a moment to activate the teleportation. Entering such a portal for the first time will create a new portal in your destination. End portal which were built in the End will lead back to the Overworld. An End portal is destroyed if any of its surrounding frame blocks is destroyed.",
|
||||
tiles = {
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
{
|
||||
name = "mcl_portals_end_portal.png",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 16,
|
||||
aspect_h = 16,
|
||||
length = 2.0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "mcl_portals_end_portal.png",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 16,
|
||||
aspect_h = 16,
|
||||
length = 2.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
use_texture_alpha = true,
|
||||
walkable = false,
|
||||
diggable = false,
|
||||
pointable = false,
|
||||
buildable_to = false,
|
||||
is_ground_content = false,
|
||||
drop = "",
|
||||
-- This is 15 in MC.
|
||||
light_source = 14,
|
||||
post_effect_color = {a = 192, r = 0, g = 0, b = 0},
|
||||
alpha = 192,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.1, 0.5, 0.5, 0.1},
|
||||
},
|
||||
},
|
||||
groups = {not_in_creative_inventory = 1},
|
||||
on_destruct = destroy_portal,
|
||||
|
||||
_mcl_hardness = -1,
|
||||
_mcl_blast_resistance = 18000000,
|
||||
})
|
||||
|
||||
local function build_end_portal(pos, target3)
|
||||
local p = {x = pos.x - 1, y = pos.y - 1, z = pos.z}
|
||||
local p1 = {x = pos.x - 1, y = pos.y - 1, z = pos.z}
|
||||
local p2 = {x = p1.x + 3, y = p1.y + 4, z = p1.z}
|
||||
|
||||
for i = 1, 4 do
|
||||
minetest.set_node(p, {name = portal_frame})
|
||||
p.y = p.y + 1
|
||||
end
|
||||
for i = 1, 3 do
|
||||
minetest.set_node(p, {name = portal_frame})
|
||||
p.x = p.x + 1
|
||||
end
|
||||
for i = 1, 4 do
|
||||
minetest.set_node(p, {name = portal_frame})
|
||||
p.y = p.y - 1
|
||||
end
|
||||
for i = 1, 3 do
|
||||
minetest.set_node(p, {name = portal_frame})
|
||||
p.x = p.x - 1
|
||||
end
|
||||
|
||||
for x = p1.x, p2.x do
|
||||
for y = p1.y, p2.y do
|
||||
p = {x = x, y = y, z = p1.z}
|
||||
if not (x == p1.x or x == p2.x or y == p1.y or y == p2.y) then
|
||||
minetest.set_node(p, {name = "mcl_portals:portal_end", param2 = 0})
|
||||
end
|
||||
local meta = minetest.get_meta(p)
|
||||
meta:set_string("portal_frame1", minetest.pos_to_string(p1))
|
||||
meta:set_string("portal_frame2", minetest.pos_to_string(p2))
|
||||
meta:set_string("portal_target", minetest.pos_to_string(target3))
|
||||
|
||||
for z = -2, 2 do
|
||||
if z ~= 0 then
|
||||
local newp = {x=p.x, y=p.y, z=p.z+z}
|
||||
if y ~= p1.y then
|
||||
if minetest.registered_nodes[
|
||||
minetest.get_node(newp).name].is_ground_content then
|
||||
minetest.remove_node(newp)
|
||||
end
|
||||
else
|
||||
if minetest.get_node(newp).name == "air" then
|
||||
minetest.set_node(newp, {name="mcl_core:obsidian"})
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function find_end_target3_y2(target3_x, target3_z)
|
||||
local start_y = math.random(SPAWN_MIN, SPAWN_MAX) -- Search start
|
||||
if not nobj_cave then
|
||||
nobj_cave = minetest.get_perlin(np_cave)
|
||||
end
|
||||
local air = 0 -- Consecutive air nodes found
|
||||
|
||||
for y = start_y, SPAWN_MIN, -1 do
|
||||
local nval_cave = nobj_cave:get3d({x = target3_x, y = y, z = target3_z})
|
||||
|
||||
if nval_cave > TCAVE then -- Cavern
|
||||
air = air + 1
|
||||
else -- Not cavern, check if 4 nodes of space above
|
||||
if air >= 4 then
|
||||
return y + 2
|
||||
else -- Not enough space, reset air to zero
|
||||
air = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return start_y -- Fallback
|
||||
end
|
||||
|
||||
local function move_check2(p1, max, dir)
|
||||
local p = {x = p1.x, y = p1.y, z = p1.z}
|
||||
local d = math.abs(max - p1[dir]) / (max - p1[dir])
|
||||
|
||||
while p[dir] ~= max do
|
||||
p[dir] = p[dir] + d
|
||||
if minetest.get_node(p).name ~= portal_frame then
|
||||
return false
|
||||
end
|
||||
-- Abort if any of the portal frame blocks already has metadata.
|
||||
-- This mod does not yet portals which neighbor each other directly.
|
||||
-- TODO: Reorganize the way how portal frame coordinates are stored.
|
||||
local meta = minetest.get_meta(p)
|
||||
local p1 = meta:get_string("portal_frame1")
|
||||
if minetest.string_to_pos(p1) ~= nil then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function check_end_portal(p1, p2)
|
||||
if p1.x ~= p2.x then
|
||||
if not move_check2(p1, p2.x, "x") then
|
||||
return false
|
||||
end
|
||||
if not move_check2(p2, p1.x, "x") then
|
||||
return false
|
||||
end
|
||||
elseif p1.z ~= p2.z then
|
||||
if not move_check2(p1, p2.z, "z") then
|
||||
return false
|
||||
end
|
||||
if not move_check2(p2, p1.z, "z") then
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
if not move_check2(p1, p2.y, "y") then
|
||||
return false
|
||||
end
|
||||
if not move_check2(p2, p1.y, "y") then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function is_end_portal(pos)
|
||||
for d = -3, 3 do
|
||||
for y = -4, 4 do
|
||||
local px = {x = pos.x + d, y = pos.y + y, z = pos.z}
|
||||
local pz = {x = pos.x, y = pos.y + y, z = pos.z + d}
|
||||
|
||||
if check_end_portal(px, {x = px.x + 3, y = px.y + 4, z = px.z}) then
|
||||
return px, {x = px.x + 3, y = px.y + 4, z = px.z}
|
||||
end
|
||||
if check_end_portal(pz, {x = pz.x, y = pz.y + 4, z = pz.z + 3}) then
|
||||
return pz, {x = pz.x, y = pz.y + 4, z = pz.z + 3}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function make_end_portal(pos)
|
||||
local p1, p2 = is_end_portal(pos)
|
||||
if not p1 or not p2 then
|
||||
return false
|
||||
end
|
||||
|
||||
for d = 1, 2 do
|
||||
for y = p1.y + 1, p2.y - 1 do
|
||||
local p
|
||||
if p1.z == p2.z then
|
||||
p = {x = p1.x + d, y = y, z = p1.z}
|
||||
else
|
||||
p = {x = p1.x, y = y, z = p1.z + d}
|
||||
end
|
||||
if minetest.get_node(p).name ~= "air" then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local param2
|
||||
if p1.z == p2.z then
|
||||
param2 = 0
|
||||
else
|
||||
param2 = 1
|
||||
end
|
||||
|
||||
local target3 = {x = p1.x, y = p1.y, z = p1.z}
|
||||
target3.x = target3.x + 1
|
||||
if target3.y < mcl_vars.mg_end_max and target3.y > mcl_vars.mg_end_min then
|
||||
target3.y = math.random(mcl_vars.mg_overworld_min + 40, mcl_vars.mg_overworld_min + 96)
|
||||
else
|
||||
target3.y = find_end_target3_y2(target3.x, target3.z)
|
||||
end
|
||||
|
||||
for d = 0, 3 do
|
||||
for y = p1.y, p2.y do
|
||||
local p = {}
|
||||
if param2 == 0 then
|
||||
p = {x = p1.x + d, y = y, z = p1.z}
|
||||
else
|
||||
p = {x = p1.x, y = y, z = p1.z + d}
|
||||
end
|
||||
if minetest.get_node(p).name == "air" then
|
||||
minetest.set_node(p, {name = "mcl_portals:portal_end", param2 = param2})
|
||||
end
|
||||
local meta = minetest.get_meta(p)
|
||||
|
||||
-- Portal frame corners
|
||||
meta:set_string("portal_frame1", minetest.pos_to_string(p1))
|
||||
meta:set_string("portal_frame2", minetest.pos_to_string(p2))
|
||||
|
||||
-- Portal target coordinates
|
||||
meta:set_string("portal_target", minetest.pos_to_string(target3))
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
label = "End portal teleportation",
|
||||
nodenames = {"mcl_portals:portal_end"},
|
||||
interval = 1,
|
||||
chance = 2,
|
||||
action = function(pos, node)
|
||||
for _,obj in ipairs(minetest.get_objects_inside_radius(pos,1)) do --maikerumine added for objects to travel
|
||||
local lua_entity = obj:get_luaentity() --maikerumine added for objects to travel
|
||||
if obj:is_player() or lua_entity then
|
||||
-- No rapid back-and-forth teleportatio
|
||||
if portal_cooloff[obj] then
|
||||
return
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local target3 = minetest.string_to_pos(meta:get_string("portal_target"))
|
||||
if target3 then
|
||||
-- force emerge of target3 area
|
||||
minetest.get_voxel_manip():read_from_map(target3, target3)
|
||||
if not minetest.get_node_or_nil(target3) then
|
||||
minetest.emerge_area(
|
||||
vector.subtract(target3, 4), vector.add(target3, 4))
|
||||
end
|
||||
|
||||
-- teleport the object
|
||||
minetest.after(3, function(obj, pos, target3)
|
||||
-- No rapid back-and-forth teleportatio
|
||||
if portal_cooloff[obj] then
|
||||
return
|
||||
end
|
||||
local objpos = obj:getpos()
|
||||
if objpos == nil then
|
||||
return
|
||||
end
|
||||
-- If player stands, player is at ca. something+0.5
|
||||
-- which might cause precision problems, so we used ceil.
|
||||
objpos.y = math.ceil(objpos.y)
|
||||
if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Build destination
|
||||
local function check_and_build_end_portal(pos, target3)
|
||||
local n = minetest.get_node_or_nil(target3)
|
||||
if n and n.name ~= "mcl_portals:portal_end" then
|
||||
build_end_portal(target3, pos)
|
||||
minetest.after(2, check_and_build_end_portal, pos, target3)
|
||||
elseif not n then
|
||||
minetest.after(1, check_and_build_end_portal, pos, target3)
|
||||
end
|
||||
end
|
||||
|
||||
check_and_build_end_portal(pos, target3)
|
||||
|
||||
-- Teleport
|
||||
obj:setpos(target3)
|
||||
minetest.sound_play("mcl_portals_teleport", {pos=target3, gain=0.5, max_hear_distance = 16})
|
||||
|
||||
-- Enable teleportation cooloff to prevent frequent back-and-forth teleportation
|
||||
portal_cooloff[obj] = true
|
||||
minetest.after(3, function(o)
|
||||
portal_cooloff[o] = false
|
||||
end, obj)
|
||||
|
||||
end, obj, pos, target3)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
--[[ ITEM OVERRIDES ]]
|
||||
|
||||
local portal_open_help = "To open an End portal, place an upright frame of quartz blocks with a length of 4 blocks and a height of 5 blocks, leaving only air in the center. After placing this frame, use an eye of ender on the frame. The eye of ender is destroyed in the process."
|
||||
|
||||
-- Frame material
|
||||
minetest.override_item(portal_frame, {
|
||||
_doc_items_longdesc = "A block of quartz can be used to create End portals.",
|
||||
_doc_items_usagehelp = portal_open_help,
|
||||
on_destruct = destroy_portal,
|
||||
})
|
||||
|
||||
-- Portal opener
|
||||
minetest.override_item("mcl_end:ender_eye", {
|
||||
_doc_items_longdesc = "An eye of ender can be used to open End portals.",
|
||||
_doc_items_usagehelp = portal_open_help,
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
-- Use pointed node's on_rightclick function first, if present
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
if user and not user:get_player_control().sneak then
|
||||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
|
||||
end
|
||||
end
|
||||
|
||||
-- If used on portal frame, open a portal
|
||||
if pointed_thing.under and node.name == portal_frame then
|
||||
local opened = make_end_portal(pointed_thing.under)
|
||||
if opened then
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:portal_end")
|
||||
end
|
||||
minetest.sound_play(
|
||||
"fire_flint_and_steel",
|
||||
{pos = pointed_thing.above, gain = 0.5, max_hear_distance = 16})
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
itemstack:take_item() -- 1 use
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
453
mods/ITEMS/mcl_portals/portal_nether.lua
Normal file
453
mods/ITEMS/mcl_portals/portal_nether.lua
Normal file
|
@ -0,0 +1,453 @@
|
|||
-- Parameters
|
||||
|
||||
local TCAVE = 0.6
|
||||
local nobj_cave = nil
|
||||
|
||||
-- Portal frame sizes
|
||||
local FRAME_SIZE_X_MIN = 4
|
||||
local FRAME_SIZE_Y_MIN = 5
|
||||
local FRAME_SIZE_X_MAX = 23
|
||||
local FRAME_SIZE_Y_MAX = 23
|
||||
|
||||
-- 3D noise
|
||||
local np_cave = {
|
||||
offset = 0,
|
||||
scale = 1,
|
||||
spread = {x = 384, y = 128, z = 384},
|
||||
seed = 59033,
|
||||
octaves = 5,
|
||||
persist = 0.7
|
||||
}
|
||||
|
||||
-- Table of objects (including players) which recently teleported by a
|
||||
-- Nether portal. Those objects have a brief cooloff period before they
|
||||
-- can teleport again. This prevents annoying back-and-forth teleportation.
|
||||
local portal_cooloff = {}
|
||||
|
||||
-- Destroy portal if pos (portal frame or portal node) got destroyed
|
||||
local destroy_portal = function(pos)
|
||||
-- Deactivate Nether portal
|
||||
local meta = minetest.get_meta(pos)
|
||||
local p1 = minetest.string_to_pos(meta:get_string("portal_frame1"))
|
||||
local p2 = minetest.string_to_pos(meta:get_string("portal_frame2"))
|
||||
if not p1 or not p2 then
|
||||
return
|
||||
end
|
||||
|
||||
local counter = 1
|
||||
|
||||
local mp1
|
||||
for x = p1.x, p2.x do
|
||||
for y = p1.y, p2.y do
|
||||
for z = p1.z, p2.z do
|
||||
local p = vector.new(x, y, z)
|
||||
local m = minetest.get_meta(p)
|
||||
if counter == 2 then
|
||||
--[[ Only proceed if the second node still has metadata.
|
||||
(first node is a corner and not needed for the portal)
|
||||
If it doesn't have metadata, another node propably triggred the delection
|
||||
routine earlier, so we bail out earlier to avoid an infinite cascade
|
||||
of on_destroy events. ]]
|
||||
mp1 = minetest.string_to_pos(m:get_string("portal_frame1"))
|
||||
if not mp1 then
|
||||
return
|
||||
end
|
||||
end
|
||||
local nn = minetest.get_node(p).name
|
||||
if nn == "mcl_core:obsidian" or nn == "mcl_portals:portal" then
|
||||
-- Remove portal nodes, but not myself
|
||||
if nn == "mcl_portals:portal" and not vector.equals(p, pos) then
|
||||
minetest.remove_node(p)
|
||||
end
|
||||
-- Clear metadata of portal nodes and the frame
|
||||
m:set_string("portal_frame1", "")
|
||||
m:set_string("portal_frame2", "")
|
||||
m:set_string("portal_target", "")
|
||||
end
|
||||
counter = counter + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("mcl_portals:portal", {
|
||||
description = "Nether Portal",
|
||||
_doc_items_longdesc = "A Nether portal teleports creatures and objects to the hot and dangerous Nether dimension (and back!). Enter at your own risk!",
|
||||
_doc_items_usagehelp = "Stand in the portal for a moment to activate the teleportation. Entering a Nether portal for the first time will also create a new portal in the other dimension. If a Nether portal has been built in the Nether, it will lead to the Overworld. A Nether portal is destroyed if the any of the obsidian which surrounds it is destroyed, or if it was caught in an explosion.",
|
||||
|
||||
tiles = {
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
{
|
||||
name = "mcl_portals_portal.png",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 16,
|
||||
aspect_h = 16,
|
||||
length = 0.5,
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "mcl_portals_portal.png",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 16,
|
||||
aspect_h = 16,
|
||||
length = 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
use_texture_alpha = true,
|
||||
walkable = false,
|
||||
diggable = false,
|
||||
pointable = false,
|
||||
buildable_to = false,
|
||||
is_ground_content = false,
|
||||
drop = "",
|
||||
light_source = 11,
|
||||
post_effect_color = {a = 180, r = 128, g = 23, b = 23},
|
||||
alpha = 192,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.1, 0.5, 0.5, 0.1},
|
||||
},
|
||||
},
|
||||
groups = {not_in_creative_inventory = 1},
|
||||
on_destruct = destroy_portal,
|
||||
|
||||
_mcl_hardness = -1,
|
||||
_mcl_blast_resistance = 0,
|
||||
})
|
||||
|
||||
-- Functions
|
||||
--Build arrival portal
|
||||
local function build_portal(pos, target)
|
||||
local p = {x = pos.x - 1, y = pos.y - 1, z = pos.z}
|
||||
local p1 = {x = pos.x - 1, y = pos.y - 1, z = pos.z}
|
||||
local p2 = {x = p1.x + 3, y = p1.y + 4, z = p1.z}
|
||||
|
||||
for i = 1, FRAME_SIZE_Y_MIN - 1 do
|
||||
minetest.set_node(p, {name = "mcl_core:obsidian"})
|
||||
p.y = p.y + 1
|
||||
end
|
||||
for i = 1, FRAME_SIZE_X_MIN - 1 do
|
||||
minetest.set_node(p, {name = "mcl_core:obsidian"})
|
||||
p.x = p.x + 1
|
||||
end
|
||||
for i = 1, FRAME_SIZE_Y_MIN - 1 do
|
||||
minetest.set_node(p, {name = "mcl_core:obsidian"})
|
||||
p.y = p.y - 1
|
||||
end
|
||||
for i = 1, FRAME_SIZE_X_MIN - 1 do
|
||||
minetest.set_node(p, {name = "mcl_core:obsidian"})
|
||||
p.x = p.x - 1
|
||||
end
|
||||
|
||||
for x = p1.x, p2.x do
|
||||
for y = p1.y, p2.y do
|
||||
p = {x = x, y = y, z = p1.z}
|
||||
if not ((x == p1.x or x == p2.x) and (y == p1.y or y == p2.y)) then
|
||||
if not (x == p1.x or x == p2.x or y == p1.y or y == p2.y) then
|
||||
minetest.set_node(p, {name = "mcl_portals:portal", param2 = 0})
|
||||
end
|
||||
local meta = minetest.get_meta(p)
|
||||
meta:set_string("portal_frame1", minetest.pos_to_string(p1))
|
||||
meta:set_string("portal_frame2", minetest.pos_to_string(p2))
|
||||
meta:set_string("portal_target", minetest.pos_to_string(target))
|
||||
end
|
||||
|
||||
if y ~= p1.y then
|
||||
for z = -2, 2 do
|
||||
if z ~= 0 then
|
||||
p.z = p.z + z
|
||||
if minetest.registered_nodes[
|
||||
minetest.get_node(p).name].is_ground_content then
|
||||
minetest.remove_node(p)
|
||||
end
|
||||
p.z = p.z - z
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function find_nether_target_y(target_x, target_z)
|
||||
local start_y = mcl_vars.mg_nether_min + math.random(38, 117) -- Search start
|
||||
if not nobj_cave then
|
||||
nobj_cave = minetest.get_perlin(np_cave)
|
||||
end
|
||||
local air = 4
|
||||
|
||||
for y = start_y, start_y -117, -1 do
|
||||
local nval_cave = nobj_cave:get3d({x = target_x, y = y, z = target_z})
|
||||
|
||||
if nval_cave > TCAVE then -- Cavern
|
||||
air = air + 1
|
||||
else -- Not cavern, check if 4 nodes of space above
|
||||
if air >= 4 then
|
||||
return y + 2
|
||||
else -- Not enough space, reset air to zero
|
||||
air = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return start_y -- Fallback
|
||||
end
|
||||
|
||||
local function move_check(p1, max, dir)
|
||||
local p = {x = p1.x, y = p1.y, z = p1.z}
|
||||
local d = math.sign(max - p1[dir])
|
||||
local min = p[dir]
|
||||
|
||||
for k = min, max, d do
|
||||
p[dir] = k
|
||||
local node = minetest.get_node(p)
|
||||
-- Check for obsidian (except at corners)
|
||||
if k ~= min and k ~= max and node.name ~= "mcl_core:obsidian" then
|
||||
return false
|
||||
end
|
||||
-- Abort if any of the portal frame blocks already has metadata.
|
||||
-- This mod does not yet portals which neighbor each other directly.
|
||||
-- TODO: Reorganize the way how portal frame coordinates are stored.
|
||||
if node.name == "mcl_core:obsidian" then
|
||||
local meta = minetest.get_meta(p)
|
||||
local pframe1 = meta:get_string("portal_frame1")
|
||||
if minetest.string_to_pos(pframe1) ~= nil then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function check_portal(p1, p2)
|
||||
if p1.x ~= p2.x then
|
||||
if not move_check(p1, p2.x, "x") then
|
||||
return false
|
||||
end
|
||||
if not move_check(p2, p1.x, "x") then
|
||||
return false
|
||||
end
|
||||
elseif p1.z ~= p2.z then
|
||||
if not move_check(p1, p2.z, "z") then
|
||||
return false
|
||||
end
|
||||
if not move_check(p2, p1.z, "z") then
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
if not move_check(p1, p2.y, "y") then
|
||||
return false
|
||||
end
|
||||
if not move_check(p2, p1.y, "y") then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function is_portal(pos)
|
||||
local xsize, ysize = FRAME_SIZE_X_MIN-1, FRAME_SIZE_Y_MIN-1
|
||||
for d = -xsize, xsize do
|
||||
for y = -ysize, ysize do
|
||||
local px = {x = pos.x + d, y = pos.y + y, z = pos.z}
|
||||
local pz = {x = pos.x, y = pos.y + y, z = pos.z + d}
|
||||
|
||||
if check_portal(px, {x = px.x + xsize, y = px.y + ysize, z = px.z}) then
|
||||
return px, {x = px.x + xsize, y = px.y + ysize, z = px.z}
|
||||
end
|
||||
if check_portal(pz, {x = pz.x, y = pz.y + ysize, z = pz.z + xsize}) then
|
||||
return pz, {x = pz.x, y = pz.y + ysize, z = pz.z + xsize}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function make_portal(pos)
|
||||
local p1, p2 = is_portal(pos)
|
||||
if not p1 or not p2 then
|
||||
return false
|
||||
end
|
||||
|
||||
for d = 1, 2 do
|
||||
for y = p1.y + 1, p2.y - 1 do
|
||||
local p
|
||||
if p1.z == p2.z then
|
||||
p = {x = p1.x + d, y = y, z = p1.z}
|
||||
else
|
||||
p = {x = p1.x, y = y, z = p1.z + d}
|
||||
end
|
||||
if minetest.get_node(p).name ~= "air" then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local param2
|
||||
if p1.z == p2.z then
|
||||
param2 = 0
|
||||
else
|
||||
param2 = 1
|
||||
end
|
||||
|
||||
local target = {x = p1.x, y = p1.y, z = p1.z}
|
||||
target.x = target.x + 1
|
||||
if target.y < mcl_vars.mg_nether_max and target.y > mcl_vars.mg_nether_min then
|
||||
target.y = math.random(mcl_vars.mg_overworld_min + 40, mcl_vars.mg_overworld_min + 96)
|
||||
else
|
||||
target.y = find_nether_target_y(target.x, target.z)
|
||||
end
|
||||
|
||||
local dmin, dmax, ymin, ymax = 0, FRAME_SIZE_X_MIN - 1, p1.y, p2.y
|
||||
for d = dmin, dmax do
|
||||
for y = ymin, ymax do
|
||||
if not ((d == dmin or d == dmax) and (y == ymin or y == ymax)) then
|
||||
local p
|
||||
if param2 == 0 then
|
||||
p = {x = p1.x + d, y = y, z = p1.z}
|
||||
else
|
||||
p = {x = p1.x, y = y, z = p1.z + d}
|
||||
end
|
||||
if minetest.get_node(p).name == "air" then
|
||||
minetest.set_node(p, {name = "mcl_portals:portal", param2 = param2})
|
||||
end
|
||||
local meta = minetest.get_meta(p)
|
||||
|
||||
-- Portal frame corners
|
||||
meta:set_string("portal_frame1", minetest.pos_to_string(p1))
|
||||
meta:set_string("portal_frame2", minetest.pos_to_string(p2))
|
||||
|
||||
-- Portal target coordinates
|
||||
meta:set_string("portal_target", minetest.pos_to_string(target))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
minetest.register_abm({
|
||||
label = "Nether portal teleportation and particles",
|
||||
nodenames = {"mcl_portals:portal"},
|
||||
interval = 1,
|
||||
chance = 2,
|
||||
action = function(pos, node)
|
||||
minetest.add_particlespawner(
|
||||
32, --amount
|
||||
4, --time
|
||||
{x = pos.x - 0.25, y = pos.y - 0.25, z = pos.z - 0.25}, --minpos
|
||||
{x = pos.x + 0.25, y = pos.y + 0.25, z = pos.z + 0.25}, --maxpos
|
||||
{x = -0.8, y = -0.8, z = -0.8}, --minvel
|
||||
{x = 0.8, y = 0.8, z = 0.8}, --maxvel
|
||||
{x = 0, y = 0, z = 0}, --minacc
|
||||
{x = 0, y = 0, z = 0}, --maxacc
|
||||
0.5, --minexptime
|
||||
1, --maxexptime
|
||||
1, --minsize
|
||||
2, --maxsize
|
||||
false, --collisiondetection
|
||||
"mcl_portals_particle.png" --texture
|
||||
)
|
||||
for _,obj in ipairs(minetest.get_objects_inside_radius(pos,1)) do --maikerumine added for objects to travel
|
||||
local lua_entity = obj:get_luaentity() --maikerumine added for objects to travel
|
||||
if obj:is_player() or lua_entity then
|
||||
-- Prevent quick back-and-forth teleportation
|
||||
if portal_cooloff[obj] then
|
||||
return
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local target = minetest.string_to_pos(meta:get_string("portal_target"))
|
||||
if target then
|
||||
-- force emerge of target area
|
||||
minetest.get_voxel_manip():read_from_map(target, target)
|
||||
if not minetest.get_node_or_nil(target) then
|
||||
minetest.emerge_area(
|
||||
vector.subtract(target, 4), vector.add(target, 4))
|
||||
end
|
||||
-- teleport the object
|
||||
minetest.after(3, function(obj, pos, target)
|
||||
-- Prevent quick back-and-forth teleportation
|
||||
if portal_cooloff[obj] then
|
||||
return
|
||||
end
|
||||
local objpos = obj:getpos()
|
||||
if objpos == nil then
|
||||
return
|
||||
end
|
||||
-- If player stands, player is at ca. something+0.5
|
||||
-- which might cause precision problems, so we used ceil.
|
||||
objpos.y = math.ceil(objpos.y)
|
||||
|
||||
if minetest.get_node(objpos).name ~= "mcl_portals:portal" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Build target portal
|
||||
local function check_and_build_portal(pos, target)
|
||||
local n = minetest.get_node_or_nil(target)
|
||||
if n and n.name ~= "mcl_portals:portal" then
|
||||
build_portal(target, pos)
|
||||
minetest.after(2, check_and_build_portal, pos, target)
|
||||
elseif not n then
|
||||
minetest.after(1, check_and_build_portal, pos, target)
|
||||
end
|
||||
end
|
||||
|
||||
check_and_build_portal(pos, target)
|
||||
|
||||
-- Teleport
|
||||
obj:setpos(target)
|
||||
minetest.sound_play("mcl_portals_teleport", {pos=target, gain=0.5, max_hear_distance = 16})
|
||||
|
||||
-- Enable teleportation cooloff for 4 seconds, to prevent back-and-forth teleportation
|
||||
portal_cooloff[obj] = true
|
||||
minetest.after(4, function(o)
|
||||
portal_cooloff[o] = false
|
||||
end, obj)
|
||||
|
||||
end, obj, pos, target)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
--[[ ITEM OVERRIDES ]]
|
||||
|
||||
local longdesc = minetest.registered_nodes["mcl_core:obsidian"]._doc_items_longdesc
|
||||
longdesc = longdesc .. "\n" .. "Obsidian is also used as the frame of Nether portals."
|
||||
local usagehelp = "To open a Nether portal, place an upright frame of obsidian with a width of 4 blocks and a height of 5 blocks, leaving only air in the center. After placing this frame, ignite the obsidian with an appropriate tool, such as flint of steel."
|
||||
|
||||
minetest.override_item("mcl_core:obsidian", {
|
||||
_doc_items_longdesc = longdesc,
|
||||
_doc_items_usagehelp = usagehelp,
|
||||
on_destruct = destroy_portal,
|
||||
_on_ignite = function(user, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
local portal_placed = make_portal(pos)
|
||||
if portal_placed and minetest.get_modpath("doc") then
|
||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:portal")
|
||||
else
|
||||
local node = minetest.get_node(pointed_thing.above)
|
||||
if node.name ~= "mcl_portals:portal" then
|
||||
mcl_fire.set_fire(pointed_thing)
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
BIN
mods/ITEMS/mcl_portals/sounds/mcl_portals_teleport.ogg
Normal file
BIN
mods/ITEMS/mcl_portals/sounds/mcl_portals_teleport.ogg
Normal file
Binary file not shown.
BIN
mods/ITEMS/mcl_portals/textures/mcl_portals_end_portal.png
Normal file
BIN
mods/ITEMS/mcl_portals/textures/mcl_portals_end_portal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
BIN
mods/ITEMS/mcl_portals/textures/mcl_portals_particle.png
Normal file
BIN
mods/ITEMS/mcl_portals/textures/mcl_portals_particle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 226 B |
BIN
mods/ITEMS/mcl_portals/textures/mcl_portals_portal.png
Normal file
BIN
mods/ITEMS/mcl_portals/textures/mcl_portals_portal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
|
@ -71,8 +71,8 @@ minetest.register_node("mcl_tnt:tnt", {
|
|||
mesecons = {effector = {
|
||||
action_on = tnt.ignite
|
||||
}},
|
||||
_on_ignite = function(pos, player)
|
||||
tnt.ignite(pos)
|
||||
_on_ignite = function(player, pointed_thing)
|
||||
tnt.ignite(pointed_thing.under)
|
||||
end,
|
||||
sounds = sounds,
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue