In quarry, skip past undiggable nodes
authorZefram <zefram@fysh.org>
Sun, 17 Aug 2014 18:19:11 +0000 (19:19 +0100)
committerZefram <zefram@fysh.org>
Mon, 18 Aug 2014 14:14:15 +0000 (15:14 +0100)
The quarry used to get stuck when it encountered an undiggable node.
Change it to skip past that node, digging whatever later stuff it can.
Necessarily, the current digging position becomes semantically-significant
state: it is no longer sufficient to search the quarry cuboid from the top
on each iteration.  The current digging height is reported in the quarry's
interaction form, and can be reset to the top using a button on the form.

Where there is a non-air node within the quarry directly above the
next node to dig, it blocks the quarry's access to that node, even if
everything involved is diggable.  Thus an undiggable node casts a shadow
of undug nodes below it.  Resolving undiggability of a node is a major
reason to use the restart button.

technic/locale/de.txt
technic/locale/es.txt
technic/locale/it.txt
technic/locale/template.txt
technic/machines/HV/quarry.lua

index 31ea1073ebbc9be8cdb42d37af045be362cdcf25..654f46b5d8b1fc23aeecac0d6162d0abbb92d1c6 100644 (file)
@@ -115,6 +115,10 @@ Slim Elements half / normal height: = Schmale Elemente von halber / normaler Hoe
 Current track %s = Aktueller Titel %s
 Stopped =
 Keeping %d/%d map blocks loaded =
+Digging not started =
+Digging finished =
+Digging %d m above machine =
+Digging %d m below machine =
 
 ## CNC
 Cylinder = Zylinder
index 80a5736dc4c3647f4602ab8bd872c07812e4285a..b122a8ad6567ed13fc2f317f9575534970f2da3b 100644 (file)
@@ -109,6 +109,10 @@ Power level = Nivel de Poder
 Production at %d%% = Produccion en %d%%
 Stopped =
 Keeping %d/%d map blocks loaded =
+Digging not started =
+Digging finished =
+Digging %d m above machine =
+Digging %d m below machine =
 
 ## CNC Machine
 Element Edge = Elemento Borde
index 4ad5a97dfc34332c7b073a25d92f97fb9b98a2f1..e3c0866614fe434dc6574352ad959456c04a052c 100644 (file)
@@ -112,6 +112,10 @@ Slim Elements half / normal height: = Metà elementi sottili / altezza normale:
 Current track %s = Traccia corrente %s
 Stopped =
 Keeping %d/%d map blocks loaded =
+Digging not started =
+Digging finished =
+Digging %d m above machine =
+Digging %d m below machine =
 
 ## CNC
 Cylinder = Cilindro
index 4df358608e453712afd45c4da52a401b97140702..1fea4ce2afe277f7c27eaa6e0fef1a83060293c8 100644 (file)
@@ -120,6 +120,10 @@ Slim Elements half / normal height: =
 Current track %s =
 Stopped =
 Keeping %d/%d map blocks loaded =
+Digging not started =
+Digging finished =
+Digging %d m above machine =
+Digging %d m below machine =
 
 ## CNC
 Cylinder =
index 10c0ee0d045a382346f392584f9052cf4dc1ee25..4b845c5adbb0107a64bc1e80cb6296eb0c9e5f47 100644 (file)
@@ -11,146 +11,133 @@ minetest.register_craft({
 
 local quarry_dig_above_nodes = 3 -- How far above the quarry we will dig nodes
 local quarry_max_depth       = 100
+local quarry_demand = 10000
 
 local function set_quarry_formspec(meta)
-       local formspec = "size[3,1.5]"..
-               "field[1,0.5;2,1;size;Radius;"..meta:get_int("size").."]"
+       local radius = meta:get_int("size")
+       local formspec = "size[6,2.5]"..
+               "item_image[0,0;1,1;technic:quarry]"..
+               "label[1,0;"..S("%s Quarry"):format("HV").."]"..
+               "field[0.3,1.5;2,1;size;"..S("Radius:")..";"..radius.."]"
        if meta:get_int("enabled") == 0 then
-               formspec = formspec.."button[0,1;3,1;enable;"..S("%s Disabled"):format(S("%s Quarry"):format("HV")).."]"
+               formspec = formspec.."button[4,1.2;2,1;enable;"..S("Disabled").."]"
        else
-               formspec = formspec.."button[0,1;3,1;disable;"..S("%s Enabled"):format(S("%s Quarry"):format("HV")).."]"
+               formspec = formspec.."button[4,1.2;2,1;disable;"..S("Enabled").."]"
        end
+       local diameter = radius*2 + 1
+       local nd = meta:get_int("dug")
+       local rel_y = quarry_dig_above_nodes - math.floor(nd / (diameter*diameter))
+       formspec = formspec.."label[0,2;"..minetest.formspec_escape(
+                       nd == 0 and S("Digging not started") or
+                       (rel_y < -quarry_max_depth and S("Digging finished") or
+                               S("Digging %d m "..(rel_y > 0 and "above" or "below").." machine")
+                                       :format(math.abs(rel_y)))
+                       ).."]"
+       formspec = formspec.."button[4,2;2,1;restart;"..S("Restart").."]"
        meta:set_string("formspec", formspec)
 end
 
-local function quarry_receive_fields(pos, formname, fields, sender)
-       local meta = minetest.get_meta(pos)
-       if fields.size then
-               local size = tonumber(fields.size) or 0
-               size = math.max(size, 2)
-               size = math.min(size, 8)
-               meta:set_int("size", size)
-       end
-       if fields.enable then meta:set_int("enabled", 1) end
-       if fields.disable then meta:set_int("enabled", 0) end
-       set_quarry_formspec(meta)
-end
-
-local function get_quarry_center(pos, size)
-       local node     = minetest.get_node(pos)
-       local back_dir = minetest.facedir_to_dir(node.param2)
-       local relative_center = vector.multiply(back_dir, size + 1)
-       local center = vector.add(pos, relative_center)
-       return center
-end
-
-local function gen_next_digpos(center, digpos, size)
-       digpos.x = digpos.x + 1
-       if digpos.x > center.x + size then
-               digpos.x = center.x - size
-               digpos.z = digpos.z + 1
-       end
-       if digpos.z > center.z + size then
-               digpos.x = center.x - size
-               digpos.z = center.z - size
-               digpos.y = digpos.y - 1
-       end
-end
-
-local function find_next_digpos(data, area, center, dig_y, size)
-       local c_air = minetest.get_content_id("air")
-
-       for y = center.y + quarry_dig_above_nodes, dig_y - 1, -1 do
-       for z = center.z - size, center.z + size do
-       for x = center.x - size, center.x + size do
-               if data[area:index(x, y, z)] ~= c_air then
-                       return vector.new(x, y, z)
-               end
-       end
-       end
+local function set_quarry_demand(meta)
+       local radius = meta:get_int("size")
+       local diameter = radius*2 + 1
+       local machine_name = S("%s Quarry"):format("HV")
+       if meta:get_int("enabled") == 0 then
+               meta:set_string("infotext", S("%s Disabled"):format(machine_name))
+               meta:set_int("HV_EU_demand", 0)
+       elseif meta:get_int("dug") == diameter*diameter * (quarry_dig_above_nodes+1+quarry_max_depth) then
+               meta:set_string("infotext", S("%s Finished"):format(machine_name))
+               meta:set_int("HV_EU_demand", 0)
+       else
+               meta:set_string("infotext", S(meta:get_int("HV_EU_input") >= quarry_demand and "%s Active" or "%s Unpowered"):format(machine_name))
+               meta:set_int("HV_EU_demand", quarry_demand)
        end
 end
 
-local function quarry_dig(pos, center, size)
+local function quarry_receive_fields(pos, formname, fields, sender)
        local meta = minetest.get_meta(pos)
-       local drops = {}
-       local dig_y = meta:get_int("dig_y")
-       local owner = meta:get_string("owner")
-
-       local vm = VoxelManip()
-       local p1 = vector.new(
-                       center.x - size,
-                       center.y + quarry_dig_above_nodes,
-                       center.z - size)
-       local p2 = vector.new(
-                       center.x + size,
-                       dig_y - 1, -- One node lower in case we have finished the current layer
-                       center.z + size)
-       local e1, e2 = vm:read_from_map(p1, p2)
-       local area = VoxelArea:new({MinEdge=e1, MaxEdge=e2})
-       local data = vm:get_data()
-
-       local digpos = find_next_digpos(data, area, center, dig_y, size)
-
-       if digpos then
-               if digpos.y < pos.y - quarry_max_depth then
-                       meta:set_int("dig_y", digpos.y)
-                       return drops
-               end
-               if minetest.is_protected and minetest.is_protected(digpos, owner) then
-                       meta:set_int("enabled", 0)
-                       set_quarry_formspec(meta)
-                       return {}
-               end
-               dig_y = digpos.y
-               local node = minetest.get_node(digpos)
-               local node_def = minetest.registered_nodes[node.name] or { diggable = false }
-               if node_def.diggable and ((not node_def.can_dig) or node_def.can_dig(digpos, nil)) then
-                       minetest.remove_node(digpos)
-                       drops = minetest.get_node_drops(node.name, "")
+       if fields.size and string.find(fields.size, "^[0-9]+$") then
+               local size = tonumber(fields.size)
+               if size >= 2 and size <= 8 and size ~= meta:get_int("size") then
+                       meta:set_int("size", size)
+                       meta:set_int("dug", 0)
                end
-       elseif not (dig_y < pos.y - quarry_max_depth) then
-               dig_y = dig_y - 16
-       end
-
-       meta:set_int("dig_y", dig_y)
-       return drops
-end
-
-local function send_items(items, pos, node)
-       for _, item in pairs(items) do
-               technic.tube_inject_item(pos, pos, vector.new(0, 1, 0), item)
        end
+       if fields.enable then meta:set_int("enabled", 1) end
+       if fields.disable then meta:set_int("enabled", 0) end
+       if fields.restart then meta:set_int("dug", 0) end
+       set_quarry_formspec(meta)
+       set_quarry_demand(meta)
 end
 
-local run = function(pos, node)
+local function quarry_run(pos, node)
        local meta = minetest.get_meta(pos)
-       local size = meta:get_int("size")
-       local eu_input = meta:get_int("HV_EU_input")
-       local demand = 10000
-       local center = get_quarry_center(pos, size)
-       local dig_y = meta:get_int("dig_y")
-       local machine_name = S("%s Quarry"):format("HV")
-
-       if meta:get_int("enabled") == 0 then
-               meta:set_string("infotext", S("%s Disabled"):format(machine_name))
-               meta:set_int("HV_EU_demand", 0)
-               return
-       end
-
-       if eu_input < demand then
-               meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
-       elseif eu_input >= demand then
-               meta:set_string("infotext", S("%s Active"):format(machine_name))
-
-               local items = quarry_dig(pos, center, size)
-               send_items(items, pos, node)
-
-               if dig_y < pos.y - quarry_max_depth then
-                       meta:set_string("infotext", S("%s Finished"):format(machine_name))
+       if meta:get_int("enabled") and meta:get_int("HV_EU_input") >= quarry_demand then
+               local pdir = minetest.facedir_to_dir(node.param2)
+               local qdir = pdir.x == 1 and vector.new(0,0,-1) or
+                       (pdir.z == -1 and vector.new(-1,0,0) or
+                       (pdir.x == -1 and vector.new(0,0,1) or
+                       vector.new(1,0,0)))
+               local radius = meta:get_int("size")
+               local diameter = radius*2 + 1
+               local startpos = vector.add(vector.add(vector.add(pos,
+                       vector.new(0, quarry_dig_above_nodes, 0)),
+                       pdir),
+                       vector.multiply(qdir, -radius))
+               local endpos = vector.add(vector.add(vector.add(startpos,
+                       vector.new(0, -quarry_dig_above_nodes-quarry_max_depth, 0)),
+                       vector.multiply(pdir, diameter-1)),
+                       vector.multiply(qdir, diameter-1))
+               local vm = VoxelManip()
+               local minpos, maxpos = vm:read_from_map(startpos, endpos)
+               local area = VoxelArea:new({MinEdge=minpos, MaxEdge=maxpos})
+               local data = vm:get_data()
+               local c_air = minetest.get_content_id("air")
+               local owner = meta:get_string("owner")
+               local nd = meta:get_int("dug")
+               while nd ~= diameter*diameter * (quarry_dig_above_nodes+1+quarry_max_depth) do
+                       local ry = math.floor(nd / (diameter*diameter))
+                       local ndl = nd % (diameter*diameter)
+                       if ry % 2 == 1 then
+                               ndl = diameter*diameter - 1 - ndl
+                       end
+                       local rq = math.floor(ndl / diameter)
+                       local rp = ndl % diameter
+                       if rq % 2 == 1 then rp = diameter - 1 - rp end
+                       local digpos = vector.add(vector.add(vector.add(startpos,
+                               vector.new(0, -ry, 0)),
+                               vector.multiply(pdir, rp)),
+                               vector.multiply(qdir, rq))
+                       local can_dig = true
+                       for ay = startpos.y, digpos.y+1, -1 do
+                               if data[area:index(digpos.x, ay, digpos.z)] ~= c_air then
+                                       can_dig = false
+                                       break
+                               end
+                       end
+                       if can_dig and minetest.is_protected and minetest.is_protected(digpos, owner) then
+                               can_dig = false
+                       end
+                       local dignode
+                       if can_dig then
+                               dignode = minetest.get_node(digpos)
+                               local dignodedef = minetest.registered_nodes[dignode.name] or {diggable=false}
+                               if not dignodedef.diggable or (dignodedef.can_dig and not dignodedef.can_dig(digpos, nil)) then
+                                       can_dig = false
+                               end
+                       end
+                       nd = nd + 1
+                       if can_dig then
+                               minetest.remove_node(digpos)
+                               for _, item in ipairs(minetest.get_node_drops(dignode.name, "")) do
+                                       technic.tube_inject_item(pos, pos, vector.new(0, 1, 0), item)
+                               end
+                               break
+                       end
                end
+               meta:set_int("dug", nd)
        end
-       meta:set_int("HV_EU_demand", demand)
+       set_quarry_formspec(meta)
+       set_quarry_demand(meta)
 end
 
 minetest.register_node("technic:quarry", {
@@ -168,7 +155,7 @@ minetest.register_node("technic:quarry", {
                meta:set_string("infotext", S("%s Quarry"):format("HV"))
                meta:set_int("size", 4)
                set_quarry_formspec(meta)
-               meta:set_int("dig_y", pos.y)
+               set_quarry_demand(meta)
        end,
        after_place_node = function(pos, placer, itemstack)
                local meta = minetest.get_meta(pos)
@@ -177,7 +164,7 @@ minetest.register_node("technic:quarry", {
        end,
        after_dig_node = pipeworks.scan_for_tube_objects,
        on_receive_fields = quarry_receive_fields,
-       technic_run = run,
+       technic_run = quarry_run,
 })
 
 technic.register_machine("HV", "technic:quarry", technic.receiver)