forked from Minetest/dynamic_liquid
splitting up and genericizing some code, localizing default-dependent stuff in one file
parent
186d8743b4
commit
849660e6d4
|
@ -0,0 +1,133 @@
|
|||
# A quick-and-dirty script to run untranslated text through Google Translate's API.
|
||||
# The result will likely include comical errors a native speaker will laugh at you for
|
||||
# or that will puzzle them, and some manual correction of escaped codes such as @1 and @= may be
|
||||
# required, but hopefully it will serve as a start to something useful
|
||||
|
||||
# Copyright (C) 2020 FaceDeer
|
||||
# LGPLv2.1+
|
||||
|
||||
# See https://github.com/minetest-tools/update_translations for
|
||||
# potential future updates to this script.
|
||||
|
||||
from googletrans import Translator, LANGUAGES
|
||||
import os, re, shutil
|
||||
|
||||
pattern_tr_filename = re.compile(r'\.tr$')
|
||||
pattern_tr_id = re.compile(r'\.([^.]*)\.tr$')
|
||||
pattern_line_to_translate = re.compile(r'^([^#].*[^@])=$') #finds lines that don't have a translation
|
||||
|
||||
translator = Translator()
|
||||
|
||||
def translate(tr_filename):
|
||||
lang_id = pattern_tr_id.search(tr_filename)
|
||||
if not lang_id:
|
||||
print("Could not find language ID in tr filename " + tr_filename)
|
||||
return
|
||||
|
||||
lang_id = lang_id.group(1)
|
||||
|
||||
if not lang_id in LANGUAGES:
|
||||
print("language ID " + lang_id + " is not supported by Google Translate's API")
|
||||
return
|
||||
|
||||
lines_to_translate = [] # this list of strings will ultimately be sent to Google for translation
|
||||
with open(tr_filename, "r", encoding="utf-8") as tr_file_handle:
|
||||
for line in tr_file_handle:
|
||||
# Look for lines that end in "=", ie, that don't have a valid translation added to them
|
||||
line_lacking_translation = pattern_line_to_translate.search(line)
|
||||
if line_lacking_translation:
|
||||
#break the line up at @n markers, this is not ideal for Google
|
||||
#as it may remove some context but it's necessary to allow the
|
||||
#@n markers to be preserved in the output later
|
||||
lines_to_translate = lines_to_translate + line_lacking_translation.group(1).split("@n")
|
||||
|
||||
# Remove duplicates, and the empty string (a common artefact of splitting)
|
||||
line_set = set(lines_to_translate)
|
||||
line_set.discard("")
|
||||
lines_to_translate = list(line_set)
|
||||
|
||||
# Only do more work if there are lines in need of translation
|
||||
if lines_to_translate:
|
||||
print("Calling Google API for " + tr_filename)
|
||||
output = translator.translate(lines_to_translate, src="en", dest=lang_id)
|
||||
|
||||
#convert the output translations into a dictionary for easy substitution later
|
||||
translation_dictionary = dict()
|
||||
for out_line in output:
|
||||
#Google's API sometimes seems to fail to translate a line for no apparent reason
|
||||
#Don't put them in the dictionary, we can leave those untranslated and maybe try again
|
||||
if out_line.origin != out_line.text:
|
||||
translation_dictionary[out_line.origin] = out_line.text
|
||||
|
||||
translation_dictionary["@n"] = "@n" #These are to be left unchanged
|
||||
|
||||
tr_file_handle.seek(0)
|
||||
with open(tr_filename + ".temp", "w", encoding="utf-8") as tr_file_new:
|
||||
for line in tr_file_handle:
|
||||
line_lacking_translation = pattern_line_to_translate.search(line)
|
||||
if line_lacking_translation:
|
||||
line = line.rstrip('\n') #remove trailing newline so we can add the translated string to the same line
|
||||
line_split = re.split("(@n)", line[:-1]) #slice to leave off the "=" that's the last character of the line
|
||||
translated_line = ""
|
||||
|
||||
#After splitting the line up on @n again, as was done before, we should have
|
||||
#line segments that match the strings that were sent to Google.
|
||||
for line_piece in line_split:
|
||||
if line_piece in translation_dictionary:
|
||||
translated_line = translated_line + translation_dictionary[line_piece]
|
||||
else:
|
||||
print("Google returned string unchanged in file " + tr_filename + ":")
|
||||
print(line_piece)
|
||||
translated_line = None
|
||||
break
|
||||
|
||||
if translated_line:
|
||||
tr_file_new.write("#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE\n")
|
||||
tr_file_new.write(line)
|
||||
tr_file_new.write(translated_line)
|
||||
tr_file_new.write("\n")
|
||||
else:
|
||||
tr_file_new.write(line)
|
||||
tr_file_new.write("\n")
|
||||
else:
|
||||
tr_file_new.write(line)
|
||||
shutil.move(tr_filename + ".temp", tr_filename) # Overwrite the original file with the new one
|
||||
|
||||
pattern_domain = re.compile(r'^# textdomain: (.+)$')
|
||||
|
||||
def create_tr_files_from_template(folder, lang_id):
|
||||
if not lang_id in LANGUAGES:
|
||||
print("language ID " + lang_id + " is not supported by Google Translate's API")
|
||||
return
|
||||
for root, dirs, files in os.walk(folder):
|
||||
if root == "." or os.path.split(root)[1] == "locale":
|
||||
for name in files:
|
||||
if name == "template.txt":
|
||||
template_filename = os.path.join(root,name)
|
||||
with open(template_filename, "r", encoding="utf-8") as template_file:
|
||||
first_line = template_file.readline()
|
||||
domain = pattern_domain.search(first_line)
|
||||
if domain:
|
||||
translation_filename = domain.group(1) + "." + lang_id + ".tr"
|
||||
translation_filename = os.path.join(root,translation_filename)
|
||||
if not os.path.isfile(translation_filename):
|
||||
print("Copying template.txt to " + translation_filename)
|
||||
shutil.copy(template_filename, translation_filename)
|
||||
else:
|
||||
print(translation_filename + " already exists")
|
||||
|
||||
#If there are already .tr files in /locale, returns a list of their names
|
||||
def get_existing_tr_files(folder):
|
||||
out = []
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for name in files:
|
||||
if pattern_tr_filename.search(name):
|
||||
out.append(os.path.join(root,name))
|
||||
return out
|
||||
|
||||
#create_tr_files_from_template(".", "de")
|
||||
#create_tr_files_from_template(".", "it")
|
||||
|
||||
tr_files = get_existing_tr_files(".")
|
||||
for tr_file in tr_files:
|
||||
translate(tr_file)
|
|
@ -0,0 +1,171 @@
|
|||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
local water = dynamic_liquid.config.water
|
||||
local river_water = dynamic_liquid.config.river_water
|
||||
local lava = dynamic_liquid.config.lava
|
||||
local water_probability = dynamic_liquid.config.water_probability
|
||||
local river_water_probability = dynamic_liquid.config.river_water_probability
|
||||
local lava_probability = dynamic_liquid.config.lava_probability
|
||||
local water_level = dynamic_liquid.config.water_level
|
||||
local springs = dynamic_liquid.config.springs
|
||||
local flow_through = dynamic_liquid.config.flow_through
|
||||
local mapgen_prefill = dynamic_liquid.config.mapgen_prefill
|
||||
|
||||
if water then
|
||||
-- override water_source and water_flowing with liquid_renewable set to false
|
||||
local override_def = {liquid_renewable = false}
|
||||
minetest.override_item("default:water_source", override_def)
|
||||
minetest.override_item("default:water_flowing", override_def)
|
||||
end
|
||||
|
||||
if lava then
|
||||
dynamic_liquid.liquid_abm("default:lava_source", "default:lava_flowing", lava_probability)
|
||||
end
|
||||
if water then
|
||||
dynamic_liquid.liquid_abm("default:water_source", "default:water_flowing", water_probability)
|
||||
end
|
||||
if river_water then
|
||||
dynamic_liquid.liquid_abm("default:river_water_source", "default:river_water_flowing", river_water_probability)
|
||||
end
|
||||
|
||||
-- Flow-through nodes
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
if flow_through then
|
||||
|
||||
local flow_through_nodes = {"group:flow_through", "group:leaves", "group:sapling", "group:grass", "group:dry_grass", "group:flora", "groups:rail", "groups:flower",
|
||||
|
||||
"default:apple", "default:papyrus", "default:dry_shrub", "default:bush_stem", "default:acacia_bush_stem","default:sign_wall_wood", "default:sign_wall_steel", "default:ladder_wood", "default:ladder_steel", "default:fence_wood", "default:fence_acacia_wood", "default:fence_junglewood", "default:fence_pine_wood","default:fence_aspen_wood"}
|
||||
|
||||
if minetest.get_modpath("xpanes") then
|
||||
table.insert(flow_through_nodes, "xpanes:bar")
|
||||
table.insert(flow_through_nodes, "xpanes:bar_flat")
|
||||
end
|
||||
|
||||
if minetest.get_modpath("carts") then
|
||||
table.insert(flow_through_nodes, "carts:rail")
|
||||
table.insert(flow_through_nodes, "carts:powerrail")
|
||||
table.insert(flow_through_nodes, "carts:brakerail")
|
||||
end
|
||||
|
||||
dynamic_liquid.flow_through_abm({nodenames = flow_through_nodes})
|
||||
end
|
||||
|
||||
if mapgen_prefill then
|
||||
dynamic_liquid.mapgen_prefill({liquid="default:water_source", liquid_level=water_level})
|
||||
end
|
||||
|
||||
-- Springs
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
local function deep_copy(table_in)
|
||||
local table_out = {}
|
||||
for index, value in pairs(table_in) do
|
||||
if type(value) == "table" then
|
||||
table_out[index] = deep_copy(value)
|
||||
else
|
||||
table_out[index] = value
|
||||
end
|
||||
end
|
||||
return table_out
|
||||
end
|
||||
|
||||
local duplicate_def = function (name)
|
||||
local old_def = minetest.registered_nodes[name]
|
||||
return deep_copy(old_def)
|
||||
end
|
||||
|
||||
-- register damp clay whether we're going to set the ABM or not, if the user disables this feature we don't want existing
|
||||
-- spring clay to turn into unknown nodes.
|
||||
local clay_def = duplicate_def("default:clay")
|
||||
clay_def.description = S("Damp Clay")
|
||||
if not springs then
|
||||
clay_def.groups.not_in_creative_inventory = 1 -- take it out of creative inventory though
|
||||
end
|
||||
minetest.register_node("dynamic_liquid:clay", clay_def)
|
||||
|
||||
local data = {}
|
||||
|
||||
if springs then
|
||||
local c_clay = minetest.get_content_id("default:clay")
|
||||
local c_spring_clay = minetest.get_content_id("dynamic_liquid:clay")
|
||||
|
||||
-- Turn mapgen clay into spring clay
|
||||
minetest.register_on_generated(function(minp, maxp, seed)
|
||||
if minp.y >= water_level or maxp.y <= -15 then
|
||||
return
|
||||
end
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
vm:get_data(data)
|
||||
|
||||
for voxelpos, voxeldata in pairs(data) do
|
||||
if voxeldata == c_clay then
|
||||
data[voxelpos] = c_spring_clay
|
||||
end
|
||||
end
|
||||
vm:set_data(data)
|
||||
vm:write_to_map()
|
||||
end)
|
||||
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid damp clay spring",
|
||||
nodenames = {"dynamic_liquid:clay"},
|
||||
neighbors = {"air", "default:water_source", "default:water_flowing"},
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
catch_up = false,
|
||||
action = function(pos,node)
|
||||
local check_node
|
||||
local check_node_name
|
||||
while pos.y < water_level do
|
||||
pos.y = pos.y + 1
|
||||
check_node = get_node(pos)
|
||||
check_node_name = check_node.name
|
||||
if check_node_name == "air" or check_node_name == "default:water_flowing" then
|
||||
set_node(pos, {name="default:water_source"})
|
||||
elseif check_node_name ~= "default:water_source" then
|
||||
--Something's been put on top of this clay, don't send water through it
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
local spring_sounds = nil
|
||||
if default.node_sound_gravel_defaults ~= nil then
|
||||
spring_sounds = default.node_sound_gravel_defaults()
|
||||
elseif default.node_sound_sand_defaults ~= nil then
|
||||
spring_sounds = default.node_sound_dirt_defaults()
|
||||
end
|
||||
|
||||
-- This is a creative-mode only node that produces a modest amount of water continuously no matter where it is.
|
||||
-- Allow this one to turn into "unknown node" when this feature is disabled, since players had to explicitly place it.
|
||||
minetest.register_node("dynamic_liquid:spring", {
|
||||
description = S("Spring"),
|
||||
_doc_items_longdesc = S("A natural spring that generates an endless stream of water source blocks"),
|
||||
_doc_items_usagehelp = S("Generates one source block of water directly on top of itself once per second, provided the space is clear. If this natural spring is dug out the flow stops and it is turned into ordinary cobble."),
|
||||
drops = "default:gravel",
|
||||
tiles = {"default_cobble.png^[combine:16x80:0,-48=crack_anylength.png",
|
||||
"default_cobble.png","default_cobble.png","default_cobble.png","default_cobble.png","default_cobble.png",
|
||||
},
|
||||
is_ground_content = false,
|
||||
groups = {cracky = 3, stone = 2},
|
||||
sounds = spring_sounds,
|
||||
})
|
||||
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid creative spring",
|
||||
nodenames = {"dynamic_liquid:spring"},
|
||||
neighbors = {"air", "default:water_flowing"},
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
catch_up = false,
|
||||
action = function(pos,node)
|
||||
pos.y = pos.y + 1
|
||||
local check_node = get_node(pos)
|
||||
local check_node_name = check_node.name
|
||||
if check_node_name == "air" or check_node_name == "default:water_flowing" then
|
||||
set_node(pos, {name="default:water_source"})
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
|
@ -0,0 +1,184 @@
|
|||
-- By making this table of all possible permutations of horizontal direction we can avoid
|
||||
-- lots of redundant calculations.
|
||||
|
||||
local all_direction_permutations = {
|
||||
{{x=0,z=1},{x=0,z=-1},{x=1,z=0},{x=-1,z=0}},
|
||||
{{x=0,z=1},{x=0,z=-1},{x=-1,z=0},{x=1,z=0}},
|
||||
{{x=0,z=1},{x=1,z=0},{x=0,z=-1},{x=-1,z=0}},
|
||||
{{x=0,z=1},{x=1,z=0},{x=-1,z=0},{x=0,z=-1}},
|
||||
{{x=0,z=1},{x=-1,z=0},{x=0,z=-1},{x=1,z=0}},
|
||||
{{x=0,z=1},{x=-1,z=0},{x=1,z=0},{x=0,z=-1}},
|
||||
{{x=0,z=-1},{x=0,z=1},{x=-1,z=0},{x=1,z=0}},
|
||||
{{x=0,z=-1},{x=0,z=1},{x=1,z=0},{x=-1,z=0}},
|
||||
{{x=0,z=-1},{x=1,z=0},{x=-1,z=0},{x=0,z=1}},
|
||||
{{x=0,z=-1},{x=1,z=0},{x=0,z=1},{x=-1,z=0}},
|
||||
{{x=0,z=-1},{x=-1,z=0},{x=1,z=0},{x=0,z=1}},
|
||||
{{x=0,z=-1},{x=-1,z=0},{x=0,z=1},{x=1,z=0}},
|
||||
{{x=1,z=0},{x=0,z=1},{x=0,z=-1},{x=-1,z=0}},
|
||||
{{x=1,z=0},{x=0,z=1},{x=-1,z=0},{x=0,z=-1}},
|
||||
{{x=1,z=0},{x=0,z=-1},{x=0,z=1},{x=-1,z=0}},
|
||||
{{x=1,z=0},{x=0,z=-1},{x=-1,z=0},{x=0,z=1}},
|
||||
{{x=1,z=0},{x=-1,z=0},{x=0,z=1},{x=0,z=-1}},
|
||||
{{x=1,z=0},{x=-1,z=0},{x=0,z=-1},{x=0,z=1}},
|
||||
{{x=-1,z=0},{x=0,z=1},{x=1,z=0},{x=0,z=-1}},
|
||||
{{x=-1,z=0},{x=0,z=1},{x=0,z=-1},{x=1,z=0}},
|
||||
{{x=-1,z=0},{x=0,z=-1},{x=1,z=0},{x=0,z=1}},
|
||||
{{x=-1,z=0},{x=0,z=-1},{x=0,z=1},{x=1,z=0}},
|
||||
{{x=-1,z=0},{x=1,z=0},{x=0,z=-1},{x=0,z=1}},
|
||||
{{x=-1,z=0},{x=1,z=0},{x=0,z=1},{x=0,z=-1}},
|
||||
}
|
||||
|
||||
local get_node = minetest.get_node
|
||||
local set_node = minetest.swap_node
|
||||
|
||||
-- Dynamic liquids
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
local disable_flow_above = dynamic_liquid.config.disable_flow_above
|
||||
if disable_flow_above == nil or disable_flow_above >= 31000 then
|
||||
|
||||
-- version without altitude check
|
||||
dynamic_liquid.liquid_abm = function(liquid, flowing_liquid, chance)
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid " .. liquid .. " and " .. flowing_liquid,
|
||||
nodenames = {liquid},
|
||||
neighbors = {flowing_liquid},
|
||||
interval = 1,
|
||||
chance = chance or 1,
|
||||
catch_up = false,
|
||||
action = function(pos,node) -- Do everything possible to optimize this method
|
||||
local check_pos = {x=pos.x, y=pos.y-1, z=pos.z}
|
||||
local check_node = get_node(check_pos)
|
||||
local check_node_name = check_node.name
|
||||
if check_node_name == flowing_liquid or check_node_name == "air" then
|
||||
set_node(pos, check_node)
|
||||
set_node(check_pos, node)
|
||||
return
|
||||
end
|
||||
local perm = all_direction_permutations[math.random(24)]
|
||||
local dirs -- declare outside of loop so it won't keep entering/exiting scope
|
||||
for i=1,4 do
|
||||
dirs = perm[i]
|
||||
-- reuse check_pos to avoid allocating a new table
|
||||
check_pos.x = pos.x + dirs.x
|
||||
check_pos.y = pos.y
|
||||
check_pos.z = pos.z + dirs.z
|
||||
check_node = get_node(check_pos)
|
||||
check_node_name = check_node.name
|
||||
if check_node_name == flowing_liquid or check_node_name == "air" then
|
||||
set_node(pos, check_node)
|
||||
set_node(check_pos, node)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
dynamic_liquid.registered_liquids[liquid] = flowing_liquid
|
||||
table.insert(dynamic_liquid.registered_liquid_neighbors, liquid)
|
||||
end
|
||||
|
||||
else
|
||||
-- version with altitude check
|
||||
dynamic_liquid.liquid_abm = function(liquid, flowing_liquid, chance)
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid " .. liquid .. " and " .. flowing_liquid .. " with altitude check",
|
||||
nodenames = {liquid},
|
||||
neighbors = {flowing_liquid},
|
||||
interval = 1,
|
||||
chance = chance or 1,
|
||||
catch_up = false,
|
||||
action = function(pos,node) -- Do everything possible to optimize this method
|
||||
-- This altitude check is the only difference from the version above.
|
||||
-- If the altitude check is disabled we don't ever need to make the comparison,
|
||||
-- hence the two different versions.
|
||||
if pos.y > disable_flow_above then
|
||||
return
|
||||
end
|
||||
local check_pos = {x=pos.x, y=pos.y-1, z=pos.z}
|
||||
local check_node = get_node(check_pos)
|
||||
local check_node_name = check_node.name
|
||||
if check_node_name == flowing_liquid or check_node_name == "air" then
|
||||
set_node(pos, check_node)
|
||||
set_node(check_pos, node)
|
||||
return
|
||||
end
|
||||
local perm = all_direction_permutations[math.random(24)]
|
||||
local dirs -- declare outside of loop so it won't keep entering/exiting scope
|
||||
for i=1,4 do
|
||||
dirs = perm[i]
|
||||
-- reuse check_pos to avoid allocating a new table
|
||||
check_pos.x = pos.x + dirs.x
|
||||
check_pos.y = pos.y
|
||||
check_pos.z = pos.z + dirs.z
|
||||
check_node = get_node(check_pos)
|
||||
check_node_name = check_node.name
|
||||
if check_node_name == flowing_liquid or check_node_name == "air" then
|
||||
set_node(pos, check_node)
|
||||
set_node(check_pos, node)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
dynamic_liquid.registered_liquids[liquid] = flowing_liquid
|
||||
table.insert(dynamic_liquid.registered_liquid_neighbors, liquid)
|
||||
end
|
||||
end
|
||||
|
||||
if dynamic_liquid.config.displace_liquid then
|
||||
|
||||
local cardinal_dirs = {
|
||||
{x= 0, y=0, z= 1},
|
||||
{x= 1, y=0, z= 0},
|
||||
{x= 0, y=0, z=-1},
|
||||
{x=-1, y=0, z= 0},
|
||||
{x= 0, y=-1, z= 0},
|
||||
{x= 0, y=1, z= 0},
|
||||
}
|
||||
-- breadth-first search passing through liquid searching for air or flowing liquid.
|
||||
local flood_search_outlet = function(start_pos, source, flowing)
|
||||
local start_node = minetest.get_node(start_pos)
|
||||
local start_node_name = start_node.name
|
||||
if start_node_name == "air" or start_node_name == flowing then
|
||||
return start_pos
|
||||
end
|
||||
|
||||
local visited = {}
|
||||
visited[minetest.hash_node_position(start_pos)] = true
|
||||
local queue = {start_pos}
|
||||
local queue_pointer = 1
|
||||
|
||||
while #queue >= queue_pointer do
|
||||
local current_pos = queue[queue_pointer]
|
||||
queue_pointer = queue_pointer + 1
|
||||
for _, cardinal_dir in ipairs(cardinal_dirs) do
|
||||
local new_pos = vector.add(current_pos, cardinal_dir)
|
||||
local new_hash = minetest.hash_node_position(new_pos)
|
||||
if visited[new_hash] == nil then
|
||||
local new_node = minetest.get_node(new_pos)
|
||||
local new_node_name = new_node.name
|
||||
if new_node_name == "air" or new_node_name == flowing then
|
||||
return new_pos
|
||||
end
|
||||
visited[new_hash] = true
|
||||
if new_node_name == source then
|
||||
table.insert(queue, new_pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Conserve liquids, when placing nodes in liquids try to find a place to displace the liquid to.
|
||||
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
|
||||
local flowing = dynamic_liquid.registered_liquids[oldnode.name]
|
||||
if flowing ~= nil then
|
||||
local dest = flood_search_outlet(pos, oldnode.name, flowing)
|
||||
if dest ~= nil then
|
||||
minetest.swap_node(dest, oldnode)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
local flow_through_directions = {
|
||||
{{x=1,z=0},{x=0,z=1}},
|
||||
{{x=0,z=1},{x=1,z=0}},
|
||||
}
|
||||
|
||||
dynamic_liquid.flow_through_abm = function(def)
|
||||
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid flow-through",
|
||||
nodenames = def.nodenames,
|
||||
neighbors = dynamic_liquid.registered_liquid_neighbors,
|
||||
interval = 1,
|
||||
chance = 2, -- since liquid is teleported two nodes by this abm, halve the chance
|
||||
catch_up = false,
|
||||
action = function(pos)
|
||||
local source_pos = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||
local dest_pos = {x=pos.x, y=pos.y-1, z=pos.z}
|
||||
local source_node = get_node(source_pos)
|
||||
local dest_node
|
||||
local source_flowing_node = dynamic_liquid.registered_liquids[source_node.name]
|
||||
local dest_flowing_node
|
||||
if source_flowing_node ~= nil then
|
||||
dest_node = minetest.get_node(dest_pos)
|
||||
if dest_node.name == source_flowing_node or dest_node.name == "air" then
|
||||
set_node(dest_pos, source_node)
|
||||
set_node(source_pos, dest_node)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local perm = flow_through_directions[math.random(2)]
|
||||
local dirs -- declare outside of loop so it won't keep entering/exiting scope
|
||||
for i=1,2 do
|
||||
dirs = perm[i]
|
||||
-- reuse to avoid allocating a new table
|
||||
source_pos.x = pos.x + dirs.x
|
||||
source_pos.y = pos.y
|
||||
source_pos.z = pos.z + dirs.z
|
||||
|
||||
dest_pos.x = pos.x - dirs.x
|
||||
dest_pos.y = pos.y
|
||||
dest_pos.z = pos.z - dirs.z
|
||||
|
||||
source_node = get_node(source_pos)
|
||||
dest_node = get_node(dest_pos)
|
||||
source_flowing_node = dynamic_liquid.registered_liquids[source_node.name]
|
||||
dest_flowing_node = dynamic_liquid.registered_liquids[dest_node.name]
|
||||
|
||||
if (source_flowing_node ~= nil and (dest_node.name == source_flowing_node or dest_node.name == "air")) or
|
||||
(dest_flowing_node ~= nil and (source_node.name == dest_flowing_node or source_node.name == "air"))
|
||||
then
|
||||
set_node(source_pos, dest_node)
|
||||
set_node(dest_pos, source_node)
|
||||
return
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
582
init.lua
582
init.lua
|
@ -1,567 +1,33 @@
|
|||
dynamic_liquid = {} -- global table to expose liquid_abm for other mods' usage
|
||||
|
||||
dynamic_liquid.config = {}
|
||||
|
||||
dynamic_liquid.config.water = minetest.settings:get_bool("dynamic_liquid_water", true)
|
||||
dynamic_liquid.config.river_water = minetest.settings:get_bool("dynamic_liquid_river_water", false)
|
||||
dynamic_liquid.config.lava = minetest.settings:get_bool("dynamic_liquid_lava", true)
|
||||
|
||||
dynamic_liquid.config.water_probability = tonumber(minetest.settings:get("dynamic_liquid_water_flow_propability")) or 1
|
||||
dynamic_liquid.config.river_water_probability = tonumber(minetest.settings:get("dynamic_liquid_river_water_flow_propability")) or 1
|
||||
dynamic_liquid.config.lava_probability = tonumber(minetest.settings:get("dynamic_liquid_lava_flow_propability")) or 5
|
||||
dynamic_liquid.config.water_level = tonumber(minetest.get_mapgen_setting("water_level")) or 0
|
||||
dynamic_liquid.config.springs = minetest.settings:get_bool("dynamic_liquid_springs", true)
|
||||
dynamic_liquid.config.flow_through = minetest.settings:get_bool("dynamic_liquid_flow_through", true)
|
||||
dynamic_liquid.config.mapgen_prefill = minetest.settings:get_bool("dynamic_liquid_mapgen_prefill", true)
|
||||
dynamic_liquid.config.disable_flow_above = tonumber(minetest.settings:get("dynamic_liquid_disable_flow_above")) -- this one can be nil
|
||||
dynamic_liquid.config.displace_liquid = minetest.settings:get_bool("dynamic_liquid_displace_liquid", true)
|
||||
|
||||
|
||||
|
||||
dynamic_liquid.registered_liquids = {} -- used by the flow-through node abm
|
||||
dynamic_liquid.registered_liquid_neighbors = {}
|
||||
|
||||
local water_level = tonumber(minetest.get_mapgen_setting("water_level"))
|
||||
|
||||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
local S = minetest.get_translator(modname)
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
dofile(modpath.."/cooling_lava.lua")
|
||||
dofile(modpath.."/dynamic_liquids.lua")
|
||||
dofile(modpath.."/flow_through.lua")
|
||||
dofile(modpath.."/mapgen_prefill.lua")
|
||||
|
||||
-- By making this giant table of all possible permutations of horizontal direction we can avoid
|
||||
-- lots of redundant calculations.
|
||||
|
||||
local all_direction_permutations = {
|
||||
{{x=0,z=1},{x=0,z=-1},{x=1,z=0},{x=-1,z=0}},
|
||||
{{x=0,z=1},{x=0,z=-1},{x=-1,z=0},{x=1,z=0}},
|
||||
{{x=0,z=1},{x=1,z=0},{x=0,z=-1},{x=-1,z=0}},
|
||||
{{x=0,z=1},{x=1,z=0},{x=-1,z=0},{x=0,z=-1}},
|
||||
{{x=0,z=1},{x=-1,z=0},{x=0,z=-1},{x=1,z=0}},
|
||||
{{x=0,z=1},{x=-1,z=0},{x=1,z=0},{x=0,z=-1}},
|
||||
{{x=0,z=-1},{x=0,z=1},{x=-1,z=0},{x=1,z=0}},
|
||||
{{x=0,z=-1},{x=0,z=1},{x=1,z=0},{x=-1,z=0}},
|
||||
{{x=0,z=-1},{x=1,z=0},{x=-1,z=0},{x=0,z=1}},
|
||||
{{x=0,z=-1},{x=1,z=0},{x=0,z=1},{x=-1,z=0}},
|
||||
{{x=0,z=-1},{x=-1,z=0},{x=1,z=0},{x=0,z=1}},
|
||||
{{x=0,z=-1},{x=-1,z=0},{x=0,z=1},{x=1,z=0}},
|
||||
{{x=1,z=0},{x=0,z=1},{x=0,z=-1},{x=-1,z=0}},
|
||||
{{x=1,z=0},{x=0,z=1},{x=-1,z=0},{x=0,z=-1}},
|
||||
{{x=1,z=0},{x=0,z=-1},{x=0,z=1},{x=-1,z=0}},
|
||||
{{x=1,z=0},{x=0,z=-1},{x=-1,z=0},{x=0,z=1}},
|
||||
{{x=1,z=0},{x=-1,z=0},{x=0,z=1},{x=0,z=-1}},
|
||||
{{x=1,z=0},{x=-1,z=0},{x=0,z=-1},{x=0,z=1}},
|
||||
{{x=-1,z=0},{x=0,z=1},{x=1,z=0},{x=0,z=-1}},
|
||||
{{x=-1,z=0},{x=0,z=1},{x=0,z=-1},{x=1,z=0}},
|
||||
{{x=-1,z=0},{x=0,z=-1},{x=1,z=0},{x=0,z=1}},
|
||||
{{x=-1,z=0},{x=0,z=-1},{x=0,z=1},{x=1,z=0}},
|
||||
{{x=-1,z=0},{x=1,z=0},{x=0,z=-1},{x=0,z=1}},
|
||||
{{x=-1,z=0},{x=1,z=0},{x=0,z=1},{x=0,z=-1}},
|
||||
}
|
||||
|
||||
local get_node = minetest.get_node
|
||||
local set_node = minetest.swap_node
|
||||
|
||||
-- Dynamic liquids
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
local disable_flow_above = tonumber(minetest.settings:get("dynamic_liquid_disable_flow_above"))
|
||||
|
||||
if disable_flow_above == nil or disable_flow_above >= 31000 then
|
||||
|
||||
-- version without altitude check
|
||||
dynamic_liquid.liquid_abm = function(liquid, flowing_liquid, chance)
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid " .. liquid .. " and " .. flowing_liquid,
|
||||
nodenames = {liquid},
|
||||
neighbors = {flowing_liquid},
|
||||
interval = 1,
|
||||
chance = chance or 1,
|
||||
catch_up = false,
|
||||
action = function(pos,node) -- Do everything possible to optimize this method
|
||||
local check_pos = {x=pos.x, y=pos.y-1, z=pos.z}
|
||||
local check_node = get_node(check_pos)
|
||||
local check_node_name = check_node.name
|
||||
if check_node_name == flowing_liquid or check_node_name == "air" then
|
||||
set_node(pos, check_node)
|
||||
set_node(check_pos, node)
|
||||
return
|
||||
end
|
||||
local perm = all_direction_permutations[math.random(24)]
|
||||
local dirs -- declare outside of loop so it won't keep entering/exiting scope
|
||||
for i=1,4 do
|
||||
dirs = perm[i]
|
||||
-- reuse check_pos to avoid allocating a new table
|
||||
check_pos.x = pos.x + dirs.x
|
||||
check_pos.y = pos.y
|
||||
check_pos.z = pos.z + dirs.z
|
||||
check_node = get_node(check_pos)
|
||||
check_node_name = check_node.name
|
||||
if check_node_name == flowing_liquid or check_node_name == "air" then
|
||||
set_node(pos, check_node)
|
||||
set_node(check_pos, node)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
dynamic_liquid.registered_liquids[liquid] = flowing_liquid
|
||||
table.insert(dynamic_liquid.registered_liquid_neighbors, liquid)
|
||||
end
|
||||
|
||||
else
|
||||
-- version with altitude check
|
||||
dynamic_liquid.liquid_abm = function(liquid, flowing_liquid, chance)
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid " .. liquid .. " and " .. flowing_liquid .. " with altitude check",
|
||||
nodenames = {liquid},
|
||||
neighbors = {flowing_liquid},
|
||||
interval = 1,
|
||||
chance = chance or 1,
|
||||
catch_up = false,
|
||||
action = function(pos,node) -- Do everything possible to optimize this method
|
||||
-- This altitude check is the only difference from the version above.
|
||||
-- If the altitude check is disabled we don't ever need to make the comparison,
|
||||
-- hence the two different versions.
|
||||
if pos.y > disable_flow_above then
|
||||
return
|
||||
end
|
||||
local check_pos = {x=pos.x, y=pos.y-1, z=pos.z}
|
||||
local check_node = get_node(check_pos)
|
||||
local check_node_name = check_node.name
|
||||
if check_node_name == flowing_liquid or check_node_name == "air" then
|
||||
set_node(pos, check_node)
|
||||
set_node(check_pos, node)
|
||||
return
|
||||
end
|
||||
local perm = all_direction_permutations[math.random(24)]
|
||||
local dirs -- declare outside of loop so it won't keep entering/exiting scope
|
||||
for i=1,4 do
|
||||
dirs = perm[i]
|
||||
-- reuse check_pos to avoid allocating a new table
|
||||
check_pos.x = pos.x + dirs.x
|
||||
check_pos.y = pos.y
|
||||
check_pos.z = pos.z + dirs.z
|
||||
check_node = get_node(check_pos)
|
||||
check_node_name = check_node.name
|
||||
if check_node_name == flowing_liquid or check_node_name == "air" then
|
||||
set_node(pos, check_node)
|
||||
set_node(check_pos, node)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
dynamic_liquid.registered_liquids[liquid] = flowing_liquid
|
||||
table.insert(dynamic_liquid.registered_liquid_neighbors, liquid)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if not minetest.get_modpath("default") then
|
||||
return
|
||||
end
|
||||
|
||||
local water = minetest.settings:get_bool("dynamic_liquid_water", true)
|
||||
local river_water = minetest.settings:get_bool("dynamic_liquid_river_water", false)
|
||||
local lava = minetest.settings:get_bool("dynamic_liquid_lava", true)
|
||||
|
||||
local water_probability = tonumber(minetest.settings:get("dynamic_liquid_water_flow_propability"))
|
||||
if water_probability == nil then
|
||||
water_probability = 1
|
||||
end
|
||||
|
||||
local river_water_probability = tonumber(minetest.settings:get("dynamic_liquid_river_water_flow_propability"))
|
||||
if river_water_probability == nil then
|
||||
river_water_probability = 1
|
||||
end
|
||||
|
||||
local lava_probability = tonumber(minetest.settings:get("dynamic_liquid_lava_flow_propability"))
|
||||
if lava_probability == nil then
|
||||
lava_probability = 5
|
||||
end
|
||||
|
||||
local springs = minetest.settings:get_bool("dynamic_liquid_springs", true)
|
||||
|
||||
if water then
|
||||
-- override water_source and water_flowing with liquid_renewable set to false
|
||||
local override_def = {liquid_renewable = false}
|
||||
minetest.override_item("default:water_source", override_def)
|
||||
minetest.override_item("default:water_flowing", override_def)
|
||||
end
|
||||
|
||||
if lava then
|
||||
dynamic_liquid.liquid_abm("default:lava_source", "default:lava_flowing", lava_probability)
|
||||
end
|
||||
if water then
|
||||
dynamic_liquid.liquid_abm("default:water_source", "default:water_flowing", water_probability)
|
||||
end
|
||||
if river_water then
|
||||
dynamic_liquid.liquid_abm("default:river_water_source", "default:river_water_flowing", river_water_probability)
|
||||
end
|
||||
|
||||
-- Flow-through nodes
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
local flow_through = minetest.settings:get_bool("dynamic_liquid_flow_through", true)
|
||||
|
||||
if flow_through then
|
||||
|
||||
local flow_through_directions = {
|
||||
{{x=1,z=0},{x=0,z=1}},
|
||||
{{x=0,z=1},{x=1,z=0}},
|
||||
}
|
||||
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid flow-through",
|
||||
nodenames = {"group:flow_through", "group:leaves", "group:sapling", "group:grass", "group:dry_grass", "group:flora", "groups:rail", "groups:flower"},
|
||||
neighbors = dynamic_liquid.registered_liquid_neighbors,
|
||||
interval = 1,
|
||||
chance = 2, -- since liquid is teleported two nodes by this abm, halve the chance
|
||||
catch_up = false,
|
||||
action = function(pos)
|
||||
local source_pos = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||
local dest_pos = {x=pos.x, y=pos.y-1, z=pos.z}
|
||||
local source_node = get_node(source_pos)
|
||||
local dest_node
|
||||
local source_flowing_node = dynamic_liquid.registered_liquids[source_node.name]
|
||||
local dest_flowing_node
|
||||
if source_flowing_node ~= nil then
|
||||
dest_node = minetest.get_node(dest_pos)
|
||||
if dest_node.name == source_flowing_node or dest_node.name == "air" then
|
||||
set_node(dest_pos, source_node)
|
||||
set_node(source_pos, dest_node)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local perm = flow_through_directions[math.random(2)]
|
||||
local dirs -- declare outside of loop so it won't keep entering/exiting scope
|
||||
for i=1,2 do
|
||||
dirs = perm[i]
|
||||
-- reuse to avoid allocating a new table
|
||||
source_pos.x = pos.x + dirs.x
|
||||
source_pos.y = pos.y
|
||||
source_pos.z = pos.z + dirs.z
|
||||
|
||||
dest_pos.x = pos.x - dirs.x
|
||||
dest_pos.y = pos.y
|
||||
dest_pos.z = pos.z - dirs.z
|
||||
|
||||
source_node = get_node(source_pos)
|
||||
dest_node = get_node(dest_pos)
|
||||
source_flowing_node = dynamic_liquid.registered_liquids[source_node.name]
|
||||
dest_flowing_node = dynamic_liquid.registered_liquids[dest_node.name]
|
||||
|
||||
if (source_flowing_node ~= nil and (dest_node.name == source_flowing_node or dest_node.name == "air")) or
|
||||
(dest_flowing_node ~= nil and (source_node.name == dest_flowing_node or source_node.name == "air"))
|
||||
then
|
||||
set_node(source_pos, dest_node)
|
||||
set_node(dest_pos, source_node)
|
||||
return
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local add_flow_through = function(node_name)
|
||||
local node_def = minetest.registered_nodes[node_name]
|
||||
if node_def == nil then
|
||||
minetest.log("warning", "dynamic_liquid attempted to call add_flow_through on the node name "
|
||||
.. node_name .. ", which was not found in minetest.registered_nodes.")
|
||||
return
|
||||
end
|
||||
local new_groups = node_def.groups
|
||||
new_groups.flow_through = 1
|
||||
minetest.override_item(node_name,{groups = new_groups})
|
||||
end
|
||||
|
||||
if minetest.get_modpath("default") then
|
||||
for _, name in pairs({
|
||||
"default:apple",
|
||||
"default:papyrus",
|
||||
"default:dry_shrub",
|
||||
"default:bush_stem",
|
||||
"default:acacia_bush_stem",
|
||||
"default:sign_wall_wood",
|
||||
"default:sign_wall_steel",
|
||||
"default:ladder_wood",
|
||||
"default:ladder_steel",
|
||||
"default:fence_wood",
|
||||
"default:fence_acacia_wood",
|
||||
"default:fence_junglewood",
|
||||
"default:fence_pine_wood",
|
||||
"default:fence_aspen_wood",
|
||||
}) do
|
||||
add_flow_through(name)
|
||||
end
|
||||
end
|
||||
|
||||
if minetest.get_modpath("xpanes") then
|
||||
add_flow_through("xpanes:bar")
|
||||
add_flow_through("xpanes:bar_flat")
|
||||
end
|
||||
|
||||
if minetest.get_modpath("carts") then
|
||||
add_flow_through("carts:rail")
|
||||
add_flow_through("carts:powerrail")
|
||||
add_flow_through("carts:brakerail")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Springs
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
local function deep_copy(table_in)
|
||||
local table_out = {}
|
||||
for index, value in pairs(table_in) do
|
||||
if type(value) == "table" then
|
||||
table_out[index] = deep_copy(value)
|
||||
else
|
||||
table_out[index] = value
|
||||
end
|
||||
end
|
||||
return table_out
|
||||
end
|
||||
|
||||
local duplicate_def = function (name)
|
||||
local old_def = minetest.registered_nodes[name]
|
||||
return deep_copy(old_def)
|
||||
end
|
||||
|
||||
-- register damp clay whether we're going to set the ABM or not, if the user disables this feature we don't want existing
|
||||
-- spring clay to turn into unknown nodes.
|
||||
local clay_def = duplicate_def("default:clay")
|
||||
clay_def.description = S("Damp Clay")
|
||||
if not springs then
|
||||
clay_def.groups.not_in_creative_inventory = 1 -- take it out of creative inventory though
|
||||
end
|
||||
minetest.register_node("dynamic_liquid:clay", clay_def)
|
||||
|
||||
local data = {}
|
||||
|
||||
if springs then
|
||||
local c_clay = minetest.get_content_id("default:clay")
|
||||
local c_spring_clay = minetest.get_content_id("dynamic_liquid:clay")
|
||||
|
||||
-- Turn mapgen clay into spring clay
|
||||
minetest.register_on_generated(function(minp, maxp, seed)
|
||||
if minp.y >= water_level or maxp.y <= -15 then
|
||||
return
|
||||
end
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
vm:get_data(data)
|
||||
|
||||
for voxelpos, voxeldata in pairs(data) do
|
||||
if voxeldata == c_clay then
|
||||
data[voxelpos] = c_spring_clay
|
||||
end
|
||||
end
|
||||
vm:set_data(data)
|
||||
vm:write_to_map()
|
||||
end)
|
||||
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid damp clay spring",
|
||||
nodenames = {"dynamic_liquid:clay"},
|
||||
neighbors = {"air", "default:water_source", "default:water_flowing"},
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
catch_up = false,
|
||||
action = function(pos,node)
|
||||
local check_node
|
||||
local check_node_name
|
||||
while pos.y < water_level do
|
||||
pos.y = pos.y + 1
|
||||
check_node = get_node(pos)
|
||||
check_node_name = check_node.name
|
||||
if check_node_name == "air" or check_node_name == "default:water_flowing" then
|
||||
set_node(pos, {name="default:water_source"})
|
||||
elseif check_node_name ~= "default:water_source" then
|
||||
--Something's been put on top of this clay, don't send water through it
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
local spring_sounds = nil
|
||||
if default.node_sound_gravel_defaults ~= nil then
|
||||
spring_sounds = default.node_sound_gravel_defaults()
|
||||
elseif default.node_sound_sand_defaults ~= nil then
|
||||
spring_sounds = default.node_sound_dirt_defaults()
|
||||
end
|
||||
|
||||
-- This is a creative-mode only node that produces a modest amount of water continuously no matter where it is.
|
||||
-- Allow this one to turn into "unknown node" when this feature is disabled, since players had to explicitly place it.
|
||||
minetest.register_node("dynamic_liquid:spring", {
|
||||
description = S("Spring"),
|
||||
_doc_items_longdesc = S("A natural spring that generates an endless stream of water source blocks"),
|
||||
_doc_items_usagehelp = S("Generates one source block of water directly on top of itself once per second, provided the space is clear. If this natural spring is dug out the flow stops and it is turned into ordinary cobble."),
|
||||
drops = "default:gravel",
|
||||
tiles = {"default_cobble.png^[combine:16x80:0,-48=crack_anylength.png",
|
||||
"default_cobble.png","default_cobble.png","default_cobble.png","default_cobble.png","default_cobble.png",
|
||||
},
|
||||
is_ground_content = false,
|
||||
groups = {cracky = 3, stone = 2},
|
||||
sounds = spring_sounds,
|
||||
})
|
||||
|
||||
minetest.register_abm({
|
||||
label = "dynamic_liquid creative spring",
|
||||
nodenames = {"dynamic_liquid:spring"},
|
||||
neighbors = {"air", "default:water_flowing"},
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
catch_up = false,
|
||||
action = function(pos,node)
|
||||
pos.y = pos.y + 1
|
||||
local check_node = get_node(pos)
|
||||
local check_node_name = check_node.name
|
||||
if check_node_name == "air" or check_node_name == "default:water_flowing" then
|
||||
set_node(pos, {name="default:water_source"})
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
local mapgen_prefill = minetest.settings:get_bool("dynamic_liquid_mapgen_prefill", true)
|
||||
|
||||
local waternodes
|
||||
|
||||
if mapgen_prefill then
|
||||
local c_water = minetest.get_content_id("default:water_source")
|
||||
local c_air = minetest.get_content_id("air")
|
||||
waternodes = {}
|
||||
|
||||
local fill_to = function (vi, data, area)
|
||||
if area:containsi(vi) and area:position(vi).y <= water_level then
|
||||
if data[vi] == c_air then
|
||||
data[vi] = c_water
|
||||
table.insert(waternodes, vi)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- local count = 0
|
||||
local drop_liquid = function(vi, data, area, min_y)
|
||||
if data[vi] ~= c_water then
|
||||
-- we only care about water.
|
||||
return
|
||||
end
|
||||
local start = vi -- remember the water node we started from
|
||||
local ystride = area.ystride
|
||||
vi = vi - ystride
|
||||
if data[vi] ~= c_air then
|
||||
-- if there's no air below this water node, give up immediately.
|
||||
return
|
||||
end
|
||||
vi = vi - ystride -- There's air below the water, so move down one.
|
||||
while data[vi] == c_air and area:position(vi).y > min_y do
|
||||
-- the min_y check is here to ensure that we don't put water into the mapgen
|
||||
-- border zone below our current map chunk where it might get erased by future mapgen activity.
|
||||
-- if there's more air, keep going.
|
||||
vi = vi - ystride
|
||||
end
|
||||
vi = vi + ystride -- Move back up one. vi is now pointing at the last air node above the first non-air node.
|
||||
data[vi] = c_water
|
||||
data[start] = c_air
|
||||
-- count = count + 1
|
||||
-- if count % 100 == 0 then
|
||||
-- minetest.chat_send_all("dropped water " .. (start-vi)/ystride .. " at " .. minetest.pos_to_string(area:position(vi)))
|
||||
-- end
|
||||
end
|
||||
|
||||
minetest.register_on_generated(function(minp, maxp, seed)
|
||||
if minp.y > water_level then
|
||||
-- we're in the sky.
|
||||
return
|
||||
end
|
||||
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
|
||||
vm:get_data(data)
|
||||
local maxp_y = maxp.y
|
||||
local minp_y = minp.y
|
||||
|
||||
if maxp_y > -70 then
|
||||
local top = vector.new(maxp.x, math.min(maxp_y, water_level), maxp.z) -- prevents flood fill from affecting any water above sea level
|
||||
for vi in area:iterp(minp, top) do
|
||||
if data[vi] == c_water then
|
||||
table.insert(waternodes, vi)
|
||||
end
|
||||
end
|
||||
|
||||
while table.getn(waternodes) > 0 do
|
||||
local vi = table.remove(waternodes)
|
||||
local below = vi - area.ystride
|
||||
local left = vi - area.zstride
|
||||
local right = vi + area.zstride
|
||||
local front = vi - 1
|
||||
local back = vi + 1
|
||||
|
||||
fill_to(below, data, area)
|
||||
fill_to(left, data, area)
|
||||
fill_to(right, data, area)
|
||||
fill_to(front, data, area)
|
||||
fill_to(back, data, area)
|
||||
end
|
||||
else
|
||||
-- Caves sometimes generate with liquid nodes hovering in mid air.
|
||||
-- This immediately drops them straight down as far as they can go, reducing the ABM thrashing.
|
||||
-- We only iterate down to minp.y+1 because anything at minp.y will never be dropped farther anyway.
|
||||
for vi in area:iter(minp.x, minp_y+1, minp.z, maxp.x, maxp_y, maxp.z) do
|
||||
-- fortunately, area:iter iterates through y columns going upward. Just what we need!
|
||||
-- We could possibly be a bit more efficient by remembering how far we dropped then
|
||||
-- last liquid node in a column and moving stuff down that far,
|
||||
-- but for now let's keep it simple.
|
||||
drop_liquid(vi, data, area, minp_y)
|
||||
end
|
||||
end
|
||||
|
||||
vm:set_data(data)
|
||||
vm:write_to_map()
|
||||
vm:update_liquids()
|
||||
end)
|
||||
end
|
||||
|
||||
local displace_liquid = minetest.settings:get_bool("dynamic_liquid_displace_liquid", true)
|
||||
if displace_liquid then
|
||||
|
||||
local cardinal_dirs = {
|
||||
{x= 0, y=0, z= 1},
|
||||
{x= 1, y=0, z= 0},
|
||||
{x= 0, y=0, z=-1},
|
||||
{x=-1, y=0, z= 0},
|
||||
{x= 0, y=-1, z= 0},
|
||||
{x= 0, y=1, z= 0},
|
||||
}
|
||||
-- breadth-first search passing through liquid searching for air or flowing liquid.
|
||||
local flood_search_outlet = function(start_pos, source, flowing)
|
||||
local start_node = minetest.get_node(start_pos)
|
||||
local start_node_name = start_node.name
|
||||
if start_node_name == "air" or start_node_name == flowing then
|
||||
return start_pos
|
||||
end
|
||||
|
||||
local visited = {}
|
||||
visited[minetest.hash_node_position(start_pos)] = true
|
||||
local queue = {start_pos}
|
||||
local queue_pointer = 1
|
||||
|
||||
while #queue >= queue_pointer do
|
||||
local current_pos = queue[queue_pointer]
|
||||
queue_pointer = queue_pointer + 1
|
||||
for _, cardinal_dir in ipairs(cardinal_dirs) do
|
||||
local new_pos = vector.add(current_pos, cardinal_dir)
|
||||
local new_hash = minetest.hash_node_position(new_pos)
|
||||
if visited[new_hash] == nil then
|
||||
local new_node = minetest.get_node(new_pos)
|
||||
local new_node_name = new_node.name
|
||||
if new_node_name == "air" or new_node_name == flowing then
|
||||
return new_pos
|
||||
end
|
||||
visited[new_hash] = true
|
||||
if new_node_name == source then
|
||||
table.insert(queue, new_pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Conserve liquids, when placing nodes in liquids try to find a place to displace the liquid to.
|
||||
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
|
||||
local flowing = dynamic_liquid.registered_liquids[oldnode.name]
|
||||
if flowing ~= nil then
|
||||
local dest = flood_search_outlet(pos, oldnode.name, flowing)
|
||||
if dest ~= nil then
|
||||
minetest.swap_node(dest, oldnode)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if minetest.get_modpath("default") then
|
||||
dofile(modpath.."/default.lua")
|
||||
end
|
|
@ -0,0 +1,97 @@
|
|||
dynamic_liquid.mapgen_prefill = function(def)
|
||||
|
||||
local water_level = def.liquid_level
|
||||
local c_water = minetest.get_content_id(def.liquid)
|
||||
local c_air = minetest.get_content_id("air")
|
||||
local waternodes = {}
|
||||
|
||||
local fill_to = function (vi, data, area)
|
||||
if area:containsi(vi) and area:position(vi).y <= water_level then
|
||||
if data[vi] == c_air then
|
||||
data[vi] = c_water
|
||||
table.insert(waternodes, vi)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- local count = 0
|
||||
local drop_liquid = function(vi, data, area, min_y)
|
||||
if data[vi] ~= c_water then
|
||||
-- we only care about water.
|
||||
return
|
||||
end
|
||||
local start = vi -- remember the water node we started from
|
||||
local ystride = area.ystride
|
||||
vi = vi - ystride
|
||||
if data[vi] ~= c_air then
|
||||
-- if there's no air below this water node, give up immediately.
|
||||
return
|
||||
end
|
||||
vi = vi - ystride -- There's air below the water, so move down one.
|
||||
while data[vi] == c_air and area:position(vi).y > min_y do
|
||||
-- the min_y check is here to ensure that we don't put water into the mapgen
|
||||
-- border zone below our current map chunk where it might get erased by future mapgen activity.
|
||||
-- if there's more air, keep going.
|
||||
vi = vi - ystride
|
||||
end
|
||||
vi = vi + ystride -- Move back up one. vi is now pointing at the last air node above the first non-air node.
|
||||
data[vi] = c_water
|
||||
data[start] = c_air
|
||||
-- count = count + 1
|
||||
-- if count % 100 == 0 then
|
||||
-- minetest.chat_send_all("dropped water " .. (start-vi)/ystride .. " at " .. minetest.pos_to_string(area:position(vi)))
|
||||
-- end
|
||||
end
|
||||
|
||||
minetest.register_on_generated(function(minp, maxp, seed)
|
||||
if minp.y > water_level then
|
||||
-- we're in the sky.
|
||||
return
|
||||
end
|
||||
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
|
||||
vm:get_data(data)
|
||||
local maxp_y = maxp.y
|
||||
local minp_y = minp.y
|
||||
|
||||
if maxp_y > -70 then
|
||||
local top = vector.new(maxp.x, math.min(maxp_y, water_level), maxp.z) -- prevents flood fill from affecting any water above sea level
|
||||
for vi in area:iterp(minp, top) do
|
||||
if data[vi] == c_water then
|
||||
table.insert(waternodes, vi)
|
||||
end
|
||||
end
|
||||
|
||||
while table.getn(waternodes) > 0 do
|
||||
local vi = table.remove(waternodes)
|
||||
local below = vi - area.ystride
|
||||
local left = vi - area.zstride
|
||||
local right = vi + area.zstride
|
||||
local front = vi - 1
|
||||
local back = vi + 1
|
||||
|
||||
fill_to(below, data, area)
|
||||
fill_to(left, data, area)
|
||||
fill_to(right, data, area)
|
||||
fill_to(front, data, area)
|
||||
fill_to(back, data, area)
|
||||
end
|
||||
else
|
||||
-- Caves sometimes generate with liquid nodes hovering in mid air.
|
||||
-- This immediately drops them straight down as far as they can go, reducing the ABM thrashing.
|
||||
-- We only iterate down to minp.y+1 because anything at minp.y will never be dropped farther anyway.
|
||||
for vi in area:iter(minp.x, minp_y+1, minp.z, maxp.x, maxp_y, maxp.z) do
|
||||
-- fortunately, area:iter iterates through y columns going upward. Just what we need!
|
||||
-- We could possibly be a bit more efficient by remembering how far we dropped then
|
||||
-- last liquid node in a column and moving stuff down that far,
|
||||
-- but for now let's keep it simple.
|
||||
drop_liquid(vi, data, area, minp_y)
|
||||
end
|
||||
end
|
||||
|
||||
vm:set_data(data)
|
||||
vm:write_to_map()
|
||||
vm:update_liquids()
|
||||
end)
|
||||
end
|
Loading…
Reference in New Issue