blockgame/mods/bg_api/util_node.lua

120 lines
2.7 KiB
Lua

function blockgame.air_flows_through (pos, node)
node = node or minetest.get_node(pos)
return blockgame.item_matches(node.name, {
"air",
"group:air_flowable",
})
end
function blockgame.attempt_place (pos, node, replacable)
local replacable = replacable or {}
local node_name = minetest.get_node(pos).name
local can_place = false
if node_name == "air" then can_place = true end
if blockgame.item_matches(node_name, replacable) then can_place = true end
if can_place then
minetest.set_node(pos, node)
end
return can_place
end
function blockgame.random_walk (data)
local pos = vector.copy(data.pos)
local min_steps = data.min_steps or 0
local max_steps = data.max_steps or 8
local check = data.check
local neighborhood = data.neighborhood or blockgame.vector.dirs
local steps = math.random(min_steps, max_steps)
local step = 0
while step < steps do
local dir = blockgame.random_element(neighborhood)
local new_pos = pos + dir
if check(new_pos, minetest.get_node(new_pos)) then pos = new_pos end
step = step + 1
end
return pos
end
function blockgame.flood_fill (start, step, max_range)
max_range = max_range or 4
local queue = { { pos = start, range = 0 } }
local visited = {}
local matches = {}
local rejected = {}
local cancel = false
while #queue > 0 and not cancel do
local current = table.remove(queue, 1)
local pos = current.pos
local range = current.range
local hash = minetest.hash_node_position(pos)
if not visited[hash] and range <= max_range and step(pos, range) then
local step_result = step(pos, range)
if step_result then
table.insert(matches, pos)
local neighbors = blockgame.vector.get_neighbors(pos)
for _, p in pairs(neighbors) do
table.insert(queue, { pos = p, range = range + 1 })
end
elseif step_result == nil then
table.insert(rejected, pos)
else cancel = true end
end
visited[hash] = true
end
return matches, rejected
end
function blockgame.score_nearby_nodes (pos, max_distance, group_scores, score_fn)
local score = 0
local source_pos = pos
blockgame.flood_fill(pos, function (pos, distance)
if pos == source_pos then return true end
local name = minetest.get_node(pos).name
local gain = 0
-- TODO: add support for individual nodes and not just groups.
for group, value in pairs(group_scores) do
local group = minetest.get_item_group(name, group)
if group > 0 then
gain = math.max(gain, value * group)
end
end
gain = score_fn(gain, distance, name, pos)
-- TODO: add support for optionally counting negative scores.
if gain > 0 then
score = score + gain
return true
end
-- TODO: add support for optionally going through non-scored nodes.
return false
end, max_distance)
return score
end