120 lines
2.7 KiB
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
|