diff --git a/init.lua b/init.lua index 06b0d5b..0786cb7 100644 --- a/init.lua +++ b/init.lua @@ -26,6 +26,7 @@ dofile(MP .. "/craft.lua") dofile(MP .. "/record.lua") dofile(MP .. "/playback.lua") dofile(MP .. "/registry.lua") +dofile(MP .. "/snap.lua") if minetest.get_modpath("mtt") and mtt.enabled then dofile(MP .. "/configure.spec.lua") diff --git a/place_tool.lua b/place_tool.lua index 295b08f..5511664 100644 --- a/place_tool.lua +++ b/place_tool.lua @@ -3,19 +3,6 @@ local FORMSPEC_NAME = "pick_and_place:place" local has_mapsync = minetest.get_modpath("mapsync") local has_blockexchange = minetest.get_modpath("blockexchange") -local function get_pos(meta, player) - local size = minetest.string_to_pos(meta:get_string("size")) - local distance = vector.distance(vector.new(), size) - local radius = math.ceil(distance / 2) - local offset = vector.round(vector.divide(size, 2)) - - local pos1 = pick_and_place.get_pointed_position(player, radius + 2) - pos1 = vector.subtract(pos1, offset) - local pos2 = vector.add(pos1, vector.subtract(size, 1)) - - return pos1, pos2 -end - -- notify supported mods of changes local function notify_change(pos1, pos2) if has_blockexchange then @@ -39,7 +26,8 @@ minetest.register_tool("pick_and_place:place", { local controls = player:get_player_control() local meta = itemstack:get_meta() - local pos1, pos2 = get_pos(meta, player) + local size = minetest.string_to_pos(meta:get_string("size")) + local pos1, pos2 = pick_and_place.get_placement_pos(size, player) if controls.aux1 then -- removal @@ -85,7 +73,8 @@ minetest.register_tool("pick_and_place:place", { local controls = player:get_player_control() local meta = itemstack:get_meta() - local pos1, pos2 = get_pos(meta, player) + local size = minetest.string_to_pos(meta:get_string("size")) + local pos1, pos2 = pick_and_place.get_placement_pos(size, player) if controls.aux1 then -- removal preview diff --git a/snap.lua b/snap.lua new file mode 100644 index 0000000..a49387a --- /dev/null +++ b/snap.lua @@ -0,0 +1,81 @@ + +-- playername -> pos +local origin_map = {} + +-- playername -> size +local size_map = {} +-- TODO: persistence per player (load on join, save on set) + +local function snap_axis(axis, origin, size, pos) + local half_size = math.ceil(size[axis] / 2) + + -- delta from origin + local diff = pos[axis] - origin[axis] + local o = diff % size[axis] + if o >= half_size then + -- round up + pos[axis] = pos[axis] + (size[axis] - o) + elseif o > 0 then + -- round down + pos[axis] = pos[axis] - o + end +end + +function pick_and_place.get_placement_pos(size, player) + local distance = vector.distance(vector.new(), size) + local radius = math.ceil(distance / 2) + local offset = vector.round(vector.divide(size, 2)) + local playername = player:get_player_name() + + local pos1 = pick_and_place.get_pointed_position(player, radius + 2) + pos1 = vector.subtract(pos1, offset) + + local origin = origin_map[playername] + local snap_size = size_map[playername] + if origin and snap_size then + -- apply grid-snapping + snap_axis("x", origin, size, pos1) + snap_axis("y", origin, size, pos1) + snap_axis("z", origin, size, pos1) + end + + local pos2 = vector.add(pos1, vector.subtract(size, 1)) + return pos1, pos2 +end + +minetest.register_chatcommand("pnp_snap", { + params = "[on|off]", + description = "enable or disable grid snapping with the current placement tool dimensions and origin", + func = function(name, param) + if param == "on" then + -- enable grid + local player = minetest.get_player_by_name(name) + if not player then + return false, "player not found" + end + local itemstack = player:get_wielded_item() + if itemstack:get_name() ~= "pick_and_place:place" then + return false, "no placement tool selected" + end + + local meta = itemstack:get_meta() + local size = minetest.string_to_pos(meta:get_string("size")) + local pos1 = pick_and_place.get_placement_pos(size, player) + + origin_map[name] = pos1 + size_map[name] = size + + elseif param == "off" then + -- disable grid + origin_map[name] = nil + size_map[name] = nil + end + + if origin_map[name] then + return true, "grid set with origin: " .. minetest.pos_to_string(origin_map[name]) .. + " and size: " .. minetest.pos_to_string(size_map[name]) + else + return true, "grid disabled" + end + end +}) \ No newline at end of file