autoplacer
This commit is contained in:
parent
c9913e1df1
commit
38b0767048
17
api.lua
17
api.lua
@ -56,4 +56,21 @@ end
|
||||
|
||||
function building_lib.get_condition(name)
|
||||
return conditions[name]
|
||||
end
|
||||
|
||||
-- name -> autoplace_def
|
||||
local autoplacers = {}
|
||||
|
||||
function building_lib.register_autoplacer(name, def)
|
||||
assert(type(def.buildings) == "table")
|
||||
def.name = name
|
||||
autoplacers[name] = def
|
||||
end
|
||||
|
||||
function building_lib.get_autoplacers()
|
||||
return autoplacers
|
||||
end
|
||||
|
||||
function building_lib.get_autoplacer(name)
|
||||
return autoplacers[name]
|
||||
end
|
67
autoplace.lua
Normal file
67
autoplace.lua
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
function building_lib.can_autoplace(mapblock_pos, _, autoplacer_name)
|
||||
local autoplacer = building_lib.get_autoplacer(autoplacer_name)
|
||||
if not autoplacer then
|
||||
return false, "autoplacer not found: '" .. autoplacer_name .. "'"
|
||||
end
|
||||
|
||||
local fallback_building_name
|
||||
for _, building_condition in ipairs(autoplacer.buildings) do
|
||||
if building_condition.fallback then
|
||||
fallback_building_name = building_condition.name
|
||||
end
|
||||
|
||||
for _, rotation in ipairs(building_condition.rotations or {0}) do
|
||||
local rotated_condition_group = building_lib.rotate_condition_group(building_condition.conditions, rotation)
|
||||
local success = building_lib.check_condition_group(mapblock_pos, mapblock_pos, rotated_condition_group)
|
||||
if success then
|
||||
return true, building_condition.name, rotation
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if fallback_building_name then
|
||||
return true, fallback_building_name, 0
|
||||
else
|
||||
return false, "no conditions matched"
|
||||
end
|
||||
end
|
||||
|
||||
local default_propagation_dirs = {
|
||||
{x=1, y=0, z=0},
|
||||
{x=-1, y=0, z=0},
|
||||
{x=0, y=0, z=1},
|
||||
{x=0, y=0, z=-1},
|
||||
{x=1, y=1, z=0},
|
||||
{x=-1, y=1, z=0},
|
||||
{x=0, y=1, z=1},
|
||||
{x=0, y=1, z=-1},
|
||||
}
|
||||
|
||||
function building_lib.autoplace(mapblock_pos, playername, autoplacer_name, enable_propagation)
|
||||
local success, building_name, rotation = building_lib.can_autoplace(mapblock_pos, playername, autoplacer_name)
|
||||
if not success then
|
||||
return false, building_name
|
||||
end
|
||||
|
||||
local err
|
||||
success, err = building_lib.build(mapblock_pos, playername, building_name, rotation)
|
||||
if not success then
|
||||
return success, err
|
||||
end
|
||||
|
||||
local autoplacer = building_lib.get_autoplacer(autoplacer_name)
|
||||
if enable_propagation and autoplacer.propagate then
|
||||
-- propagate changes
|
||||
for _, dir in ipairs(autoplacer.propagation_dirs or default_propagation_dirs) do
|
||||
local offset_mapblock_pos = vector.add(mapblock_pos, dir)
|
||||
success = building_lib.check_condition_table(autoplacer.propagate, offset_mapblock_pos)
|
||||
if success then
|
||||
-- selector matches, propagate autobuild
|
||||
building_lib.autoplace(offset_mapblock_pos, playername, autoplacer_name, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
131
autoplace_tool.lua
Normal file
131
autoplace_tool.lua
Normal file
@ -0,0 +1,131 @@
|
||||
local formname = "building_lib_autoplacer_configure"
|
||||
|
||||
local function get_autoplacer_list()
|
||||
local autoplacer_list = {}
|
||||
for name in pairs(building_lib.get_autoplacers()) do
|
||||
table.insert(autoplacer_list, name)
|
||||
end
|
||||
table.sort(autoplacer_list, function(a,b) return a < b end)
|
||||
return autoplacer_list
|
||||
end
|
||||
|
||||
local function get_formspec(itemstack)
|
||||
local meta = itemstack:get_meta()
|
||||
local autoplacer_list = get_autoplacer_list()
|
||||
|
||||
local selected_autoplacer_name = meta:get_string("autoplacer_name")
|
||||
if not selected_autoplacer_name or selected_autoplacer_name == "" then
|
||||
selected_autoplacer_name = autoplacer_list[1]
|
||||
end
|
||||
|
||||
local selected_autoplacer = 1
|
||||
local textlist = ""
|
||||
|
||||
for i, autoplacer_name in ipairs(autoplacer_list) do
|
||||
if selected_autoplacer_name == autoplacer_name then
|
||||
selected_autoplacer = i
|
||||
end
|
||||
|
||||
textlist = textlist .. autoplacer_name
|
||||
if i < #autoplacer_list then
|
||||
textlist = textlist .. ","
|
||||
end
|
||||
end
|
||||
|
||||
return "size[8,7;]" ..
|
||||
"textlist[0,0.1;8,6;autoplacer_name;" .. textlist .. ";" .. selected_autoplacer .. "]" ..
|
||||
"button_exit[0.1,6.5;8,1;back;Back]"
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, f, fields)
|
||||
if not minetest.check_player_privs(player, { mapblock_lib = true }) then
|
||||
return
|
||||
end
|
||||
if formname ~= f then
|
||||
return
|
||||
end
|
||||
if fields.quit then
|
||||
return
|
||||
end
|
||||
|
||||
if fields.autoplacer_name then
|
||||
local parts = fields.autoplacer_name:split(":")
|
||||
if parts[1] == "CHG" then
|
||||
local itemstack = player:get_wielded_item()
|
||||
local meta = itemstack:get_meta()
|
||||
|
||||
local selected = tonumber(parts[2])
|
||||
local autoplacer_list = get_autoplacer_list()
|
||||
|
||||
local autoplacer_name = autoplacer_list[selected]
|
||||
if not autoplacer_name then
|
||||
return
|
||||
end
|
||||
|
||||
meta:set_string("autoplacer_name", autoplacer_name)
|
||||
meta:set_string("description", "Selected autoplacer: '" .. autoplacer_name .. "'")
|
||||
player:set_wielded_item(itemstack)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_tool("building_lib:autoplace", {
|
||||
description = "building_lib autoplacer",
|
||||
inventory_image = "building_lib_autoplace.png^[colorize:#00ff00",
|
||||
stack_max = 1,
|
||||
range = 0,
|
||||
on_secondary_use = function(itemstack, player)
|
||||
minetest.show_formspec(player:get_player_name(), formname, get_formspec(itemstack))
|
||||
end,
|
||||
on_use = function(itemstack, player)
|
||||
local meta = itemstack:get_meta()
|
||||
local playername = player:get_player_name()
|
||||
local autoplacer_name = meta:get_string("autoplacer_name")
|
||||
local pointed_mapblock_pos = building_lib.get_pointed_mapblock(player)
|
||||
local success, err = building_lib.autoplace(pointed_mapblock_pos, playername, autoplacer_name, true)
|
||||
if not success then
|
||||
minetest.chat_send_player(playername, err)
|
||||
end
|
||||
end,
|
||||
on_step = function(itemstack, player)
|
||||
local playername = player:get_player_name()
|
||||
local mapblock_pos1 = building_lib.get_pointed_mapblock(player)
|
||||
local meta = itemstack:get_meta()
|
||||
local autoplacer_name = meta:get_string("autoplacer_name")
|
||||
|
||||
local success, building_name, rotation = building_lib.can_autoplace(mapblock_pos1, playername, autoplacer_name)
|
||||
if not success then
|
||||
building_lib.clear_preview(playername)
|
||||
return
|
||||
end
|
||||
|
||||
local building_def = building_lib.get_building(building_name)
|
||||
if not building_def then
|
||||
building_lib.clear_preview(playername)
|
||||
return
|
||||
end
|
||||
|
||||
local size = building_lib.get_building_size(building_def, rotation)
|
||||
local mapblock_pos2 = vector.add(mapblock_pos1, vector.subtract(size, 1))
|
||||
|
||||
local color = "#00ff00"
|
||||
local can_build = building_lib.can_build(mapblock_pos1, playername, building_def.name, rotation)
|
||||
if not can_build then
|
||||
color = "#ffff00"
|
||||
end
|
||||
|
||||
building_lib.show_preview(
|
||||
playername,
|
||||
"building_lib_autoplace.png",
|
||||
color,
|
||||
building_def,
|
||||
mapblock_pos1,
|
||||
mapblock_pos2,
|
||||
rotation
|
||||
)
|
||||
end,
|
||||
on_blur = function(player)
|
||||
local playername = player:get_player_name()
|
||||
building_lib.clear_preview(playername)
|
||||
end
|
||||
})
|
@ -30,7 +30,7 @@ function building_lib.can_build(mapblock_pos, _, building_name, rotation)
|
||||
local mapblock_pos2 = vector.add(mapblock_pos, vector.subtract(size, 1))
|
||||
|
||||
local success
|
||||
success, message = building_lib.check_conditions(mapblock_pos, mapblock_pos2, building_def)
|
||||
success, message = building_lib.check_condition_groups(mapblock_pos, mapblock_pos2, building_def.conditions)
|
||||
if not success then
|
||||
return false, message
|
||||
end
|
||||
|
@ -135,53 +135,3 @@ minetest.register_tool("building_lib:place", {
|
||||
building_lib.clear_preview(playername)
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_tool("building_lib:remove", {
|
||||
description = "building_lib remover",
|
||||
inventory_image = "building_lib_remove.png^[colorize:#ff0000",
|
||||
stack_max = 1,
|
||||
range = 0,
|
||||
on_use = function(_, player)
|
||||
local mapblock_pos = building_lib.get_pointed_mapblock(player)
|
||||
local success, err = building_lib.remove(mapblock_pos)
|
||||
if not success then
|
||||
minetest.chat_send_player(player:get_player_name(), err)
|
||||
end
|
||||
end,
|
||||
on_step = function(_, player)
|
||||
local playername = player:get_player_name()
|
||||
local pointed_mapblock_pos = building_lib.get_pointed_mapblock(player)
|
||||
|
||||
local building_info, origin = building_lib.get_placed_building_info(pointed_mapblock_pos)
|
||||
if not building_info then
|
||||
building_lib.clear_preview(playername)
|
||||
return
|
||||
end
|
||||
|
||||
local building_def = building_lib.get_building(building_info.name)
|
||||
|
||||
local size = building_lib.get_building_size(building_def, building_info.rotation or 0)
|
||||
local mapblock_pos2 = vector.add(origin, vector.subtract(size, 1))
|
||||
|
||||
local color = "#ff0000"
|
||||
local can_remove = building_lib.can_remove(origin)
|
||||
if not can_remove then
|
||||
color = "#ffff00"
|
||||
end
|
||||
|
||||
building_lib.show_preview(
|
||||
playername,
|
||||
"building_lib_remove.png",
|
||||
color,
|
||||
building_def,
|
||||
origin,
|
||||
mapblock_pos2,
|
||||
building_info.rotation
|
||||
)
|
||||
end,
|
||||
on_blur = function(player)
|
||||
local playername = player:get_player_name()
|
||||
building_lib.clear_preview(playername)
|
||||
end
|
||||
})
|
||||
|
136
conditions.lua
136
conditions.lua
@ -1,18 +1,18 @@
|
||||
|
||||
-- checks a single condition
|
||||
local function check_condition(key, value, mapblock_pos, building_def)
|
||||
local function check_condition(key, value, mapblock_pos)
|
||||
local condition = building_lib.get_condition(key)
|
||||
if condition and type(condition.can_build) == "function" then
|
||||
return condition.can_build(mapblock_pos, building_def, value)
|
||||
return condition.can_build(mapblock_pos, value)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- checks a table of conditions with the given mapblock_pos
|
||||
-- all entries have to match
|
||||
local function check_table(map, mapblock_pos, building_def)
|
||||
function building_lib.check_condition_table(map, mapblock_pos)
|
||||
for key, value in pairs(map) do
|
||||
local success, msg = check_condition(key, value, mapblock_pos, building_def)
|
||||
local success, msg = check_condition(key, value, mapblock_pos)
|
||||
if not success then
|
||||
-- failure and in AND mode, return immediately
|
||||
return false, msg or "condition failed: '" .. key .. "'"
|
||||
@ -21,69 +21,100 @@ local function check_table(map, mapblock_pos, building_def)
|
||||
return true
|
||||
end
|
||||
|
||||
local default_conditions = {
|
||||
local default_condition_groups = {
|
||||
{["*"] = { empty = true }}
|
||||
}
|
||||
|
||||
function building_lib.check_conditions(mapblock_pos1, mapblock_pos2, building_def)
|
||||
for _, condition_group in ipairs(building_def.conditions or default_conditions) do
|
||||
local group_match = true
|
||||
-- rotates a condition group by given rotation
|
||||
-- TODO: only supports sizes of 1
|
||||
function building_lib.rotate_condition_group(condition_group, rotation)
|
||||
if rotation == 0 then
|
||||
-- no rotation
|
||||
return condition_group
|
||||
end
|
||||
|
||||
for selector, conditions in pairs(condition_group) do
|
||||
local it
|
||||
if selector == "*" then
|
||||
-- match all
|
||||
it = mapblock_lib.pos_iterator(mapblock_pos1, mapblock_pos2)
|
||||
elseif selector == "base" then
|
||||
-- match only base positions
|
||||
it = mapblock_lib.pos_iterator(mapblock_pos1, {x=mapblock_pos2.x, y=mapblock_pos1.y, z=mapblock_pos2.z})
|
||||
elseif selector == "underground" then
|
||||
-- match only underground positions
|
||||
it = mapblock_lib.pos_iterator({
|
||||
x=mapblock_pos1.x, y=mapblock_pos1.y-1, z=mapblock_pos1.z
|
||||
},{
|
||||
x=mapblock_pos2.x, y=mapblock_pos1.y-1, z=mapblock_pos2.z
|
||||
})
|
||||
local new_condition_group = {}
|
||||
for selector, condition in pairs(condition_group) do
|
||||
local pos = minetest.string_to_pos(selector)
|
||||
if pos then
|
||||
-- rotate position
|
||||
local rotated_pos = mapblock_lib.rotate_pos(pos, {x=0, y=0, z=0}, rotation)
|
||||
new_condition_group[minetest.pos_to_string(rotated_pos)] = condition
|
||||
else
|
||||
-- keep selector name
|
||||
new_condition_group[pos] = condition
|
||||
end
|
||||
end
|
||||
return new_condition_group
|
||||
end
|
||||
|
||||
-- go through all condition groups and return true if any of them matches
|
||||
function building_lib.check_condition_groups(mapblock_pos1, mapblock_pos2, condition_groups)
|
||||
for _, condition_group in ipairs(condition_groups or default_condition_groups) do
|
||||
local success = building_lib.check_condition_group(mapblock_pos1, mapblock_pos2, condition_group)
|
||||
if success then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false, "no matching condition found"
|
||||
end
|
||||
|
||||
function building_lib.check_condition_group(mapblock_pos1, mapblock_pos2, condition_group)
|
||||
local group_match = true
|
||||
|
||||
for selector, conditions in pairs(condition_group) do
|
||||
local it
|
||||
if selector == "*" then
|
||||
-- match all
|
||||
it = mapblock_lib.pos_iterator(mapblock_pos1, mapblock_pos2)
|
||||
elseif selector == "base" then
|
||||
-- match only base positions
|
||||
it = mapblock_lib.pos_iterator(mapblock_pos1, {x=mapblock_pos2.x, y=mapblock_pos1.y, z=mapblock_pos2.z})
|
||||
elseif selector == "underground" then
|
||||
-- match only underground positions
|
||||
it = mapblock_lib.pos_iterator({
|
||||
x=mapblock_pos1.x, y=mapblock_pos1.y-1, z=mapblock_pos1.z
|
||||
},{
|
||||
x=mapblock_pos2.x, y=mapblock_pos1.y-1, z=mapblock_pos2.z
|
||||
})
|
||||
else
|
||||
-- try to parse a manual position
|
||||
local rel_pos = minetest.string_to_pos(selector)
|
||||
if rel_pos then
|
||||
-- single position
|
||||
local abs_pos = vector.add(mapblock_pos1, rel_pos)
|
||||
it = mapblock_lib.pos_iterator(abs_pos, abs_pos)
|
||||
else
|
||||
-- try to parse a manual position
|
||||
local pos = minetest.string_to_pos(selector)
|
||||
if pos then
|
||||
-- single position
|
||||
it = mapblock_lib.pos_iterator(pos, pos)
|
||||
else
|
||||
return false, "unknown selector: " .. selector
|
||||
end
|
||||
return false, "unknown selector: " .. selector
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
local mapblock_pos = it()
|
||||
if not mapblock_pos then
|
||||
break
|
||||
end
|
||||
|
||||
while true do
|
||||
local mapblock_pos = it()
|
||||
if not mapblock_pos then
|
||||
break
|
||||
end
|
||||
|
||||
local success = check_table(conditions, mapblock_pos, building_def)
|
||||
if not success then
|
||||
group_match = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not group_match then
|
||||
local success = building_lib.check_condition_table(conditions, mapblock_pos)
|
||||
if not success then
|
||||
group_match = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if group_match then
|
||||
return true
|
||||
if not group_match then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return false, "no matching condition found"
|
||||
if group_match then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- checks if a building with specified group is placed there already
|
||||
building_lib.register_condition("group", {
|
||||
can_build = function(mapblock_pos, _, value)
|
||||
can_build = function(mapblock_pos, value)
|
||||
local building_info = building_lib.get_placed_building_info(mapblock_pos)
|
||||
if building_info then
|
||||
local building_def = building_lib.get_building(building_info.name)
|
||||
@ -95,6 +126,13 @@ building_lib.register_condition("group", {
|
||||
end
|
||||
})
|
||||
|
||||
building_lib.register_condition("name", {
|
||||
can_build = function(mapblock_pos, value)
|
||||
local building_info = building_lib.get_placed_building_info(mapblock_pos)
|
||||
return building_info and building_info.name == value
|
||||
end
|
||||
})
|
||||
|
||||
-- checks if the mapblock position is empty
|
||||
building_lib.register_condition("empty", {
|
||||
can_build = function(mapblock_pos)
|
||||
|
@ -13,19 +13,15 @@ local test_map = {
|
||||
}
|
||||
|
||||
building_lib.register_condition("test_map", {
|
||||
can_build = function(mapblock_pos, _, flag_value)
|
||||
can_build = function(mapblock_pos, flag_value)
|
||||
return test_map[minetest.pos_to_string(mapblock_pos)] == flag_value
|
||||
end
|
||||
})
|
||||
|
||||
local function run_conditions(conditions)
|
||||
local function run_conditions(condition_groups)
|
||||
local mapblock_pos1 = {x = 0, y = 0, z = 0}
|
||||
local mapblock_pos2 = vector.add(mapblock_pos1, 2)
|
||||
return building_lib.check_conditions(mapblock_pos1, mapblock_pos2, {
|
||||
name = "something:test1",
|
||||
placement = "dummy",
|
||||
conditions = conditions
|
||||
})
|
||||
return building_lib.check_condition_groups(mapblock_pos1, mapblock_pos2, condition_groups)
|
||||
end
|
||||
|
||||
mtt.register("check_conditions", function(callback)
|
||||
|
5
init.lua
5
init.lua
@ -16,9 +16,12 @@ dofile(MP .. "/common.lua")
|
||||
dofile(MP .. "/placements/mapblock_lib.lua")
|
||||
dofile(MP .. "/conditions.lua")
|
||||
dofile(MP .. "/build.lua")
|
||||
dofile(MP .. "/build_tool.lua")
|
||||
dofile(MP .. "/autoplace.lua")
|
||||
dofile(MP .. "/autoplace_tool.lua")
|
||||
dofile(MP .. "/remove.lua")
|
||||
dofile(MP .. "/remove_tool.lua")
|
||||
dofile(MP .. "/chat.lua")
|
||||
dofile(MP .. "/tools.lua")
|
||||
dofile(MP .. "/events.lua")
|
||||
dofile(MP .. "/hacks.lua")
|
||||
dofile(MP .. "/mapgen.lua")
|
||||
|
@ -66,7 +66,7 @@ building_lib.register_placement("simple", {
|
||||
|
||||
-- registers a condition that checks for certain world conditions
|
||||
building_lib.register_condition("on_flat_surface", {
|
||||
can_build = function(mapblock_pos, building_def, flag_value)
|
||||
can_build = function(mapblock_pos, flag_value)
|
||||
return false, msg
|
||||
end
|
||||
})
|
||||
|
50
remove_tool.lua
Normal file
50
remove_tool.lua
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
minetest.register_tool("building_lib:remove", {
|
||||
description = "building_lib remover",
|
||||
inventory_image = "building_lib_remove.png^[colorize:#ff0000",
|
||||
stack_max = 1,
|
||||
range = 0,
|
||||
on_use = function(_, player)
|
||||
local mapblock_pos = building_lib.get_pointed_mapblock(player)
|
||||
local success, err = building_lib.remove(mapblock_pos)
|
||||
if not success then
|
||||
minetest.chat_send_player(player:get_player_name(), err)
|
||||
end
|
||||
end,
|
||||
on_step = function(_, player)
|
||||
local playername = player:get_player_name()
|
||||
local pointed_mapblock_pos = building_lib.get_pointed_mapblock(player)
|
||||
|
||||
local building_info, origin = building_lib.get_placed_building_info(pointed_mapblock_pos)
|
||||
if not building_info then
|
||||
building_lib.clear_preview(playername)
|
||||
return
|
||||
end
|
||||
|
||||
local building_def = building_lib.get_building(building_info.name)
|
||||
|
||||
local size = building_lib.get_building_size(building_def, building_info.rotation or 0)
|
||||
local mapblock_pos2 = vector.add(origin, vector.subtract(size, 1))
|
||||
|
||||
local color = "#ff0000"
|
||||
local can_remove = building_lib.can_remove(origin)
|
||||
if not can_remove then
|
||||
color = "#ffff00"
|
||||
end
|
||||
|
||||
building_lib.show_preview(
|
||||
playername,
|
||||
"building_lib_remove.png",
|
||||
color,
|
||||
building_def,
|
||||
origin,
|
||||
mapblock_pos2,
|
||||
building_info.rotation
|
||||
)
|
||||
end,
|
||||
on_blur = function(player)
|
||||
local playername = player:get_player_name()
|
||||
building_lib.clear_preview(playername)
|
||||
end
|
||||
})
|
||||
|
BIN
textures/building_lib_autoplace.png
Normal file
BIN
textures/building_lib_autoplace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
Loading…
x
Reference in New Issue
Block a user