Files
technic/technic/machines/HV/quarry.lua
Alexander Ried 35e989ba2e Fix quarry rotation (#68)
Previously the model was rotated but the dig position of the quarry was
not updated. If the player did not restart the quarry through the
formspec, it dug at the previous location.
2020-07-06 12:59:21 +10:00

342 lines
12 KiB
Lua

local S = technic.getter
local quarry_dig_above_nodes = tonumber(minetest.settings:get("technic.quarry.dig_above_nodes") or "3")
local quarry_max_depth = tonumber(minetest.settings:get("technic.quarry.maxdepth") or "100")
local quarry_time_limit = tonumber(minetest.settings:get("technic.quarry.time_limit") or "5000")
local quarry_demand = 10000
local quarry_eject_dir = vector.new(0, 1, 0)
local machine_name = S("%s Quarry"):format("HV")
local quarry_formspec =
"size[8,9]"..
"item_image[7,0;1,1;technic:quarry]"..
"list[context;cache;1,1;4,3;]"..
"listring[context;cache]"..
"list[current_player;main;0,5;8,4;]"..
"listring[current_player;main]"..
"label[0,0;"..machine_name.."]"..
"button[5,1.9;2,1;restart;"..S("Restart").."]"
-- hard-coded spiral dig pattern for up to 17x17 dig area
local quarry_dig_pattern = {
0,1,2,2,3,3,0,0,0,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,1,1,1,1,1,2,2,
2,2,2,2,3,3,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,
2,2,2,2,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,
1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,
0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
}
local function is_player_allowed(pos, name)
local owner = minetest.get_meta(pos):get_string("owner")
if owner == "" or owner == name then
return true
end
if not minetest.is_protected(pos, name) then
return true
end
return false
end
local function reset_quarry(pos)
local meta = minetest.get_meta(pos)
local node = technic.get_or_load_node(pos) or minetest.get_node(pos)
meta:set_int("quarry_dir", node.param2)
meta:set_string("quarry_pos", minetest.pos_to_string(pos))
meta:set_string("dig_pos", "")
meta:set_int("dig_level", pos.y + quarry_dig_above_nodes)
local radius = meta:get_int("size")
local diameter = (radius*2)+1
meta:set_int("dig_steps", diameter*diameter)
meta:set_int("dig_index", 0)
meta:set_int("purge_on", 1)
meta:set_int("finished", 0)
meta:set_int("dug", 0)
end
local function set_quarry_status(pos)
local meta = minetest.get_meta(pos)
local formspec = quarry_formspec.."field[5.3,3.4;2,1;size;"..S("Radius:")..";"..meta:get_int("size").."]"
local status = S("Digging not started")
if meta:get_int("enabled") == 1 then
formspec = formspec.."button[5,0.9;2,1;disable;"..S("Enabled").."]"
if meta:get_int("purge_on") == 1 then
status = S("Purging cache")
meta:set_string("infotext", S("%s purging cache"):format(machine_name))
meta:set_int("HV_EU_demand", 0)
elseif meta:get_int("finished") == 1 then
status = S("Digging finished")
meta:set_string("infotext", S("%s Finished"):format(machine_name))
meta:set_int("HV_EU_demand", 0)
else
local rel_y = meta:get_int("dig_level") - pos.y
status = S("Digging %d m "..(rel_y > 0 and "above" or "below").." machine"):format(math.abs(rel_y))
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
else
formspec = formspec.."button[5,0.9;2,1;enable;"..S("Disabled").."]"
meta:set_string("infotext", S("%s Disabled"):format(machine_name))
meta:set_int("HV_EU_demand", 0)
end
meta:set_string("formspec", formspec.."label[0,4.1;"..minetest.formspec_escape(status).."]")
end
local function quarry_receive_fields(pos, formname, fields, sender)
local player_name = sender:get_player_name()
if not is_player_allowed(pos, player_name) then
minetest.chat_send_player(player_name, "You are not allowed to edit this!")
minetest.record_protection_violation(pos, player_name)
return
end
local meta = minetest.get_meta(pos)
if fields.size and string.find(fields.size, "^[0-9]+$") then
local size = tonumber(fields.size)
if size >= 1 and size <= 8 and size ~= meta:get_int("size") then
meta:set_int("size", size)
reset_quarry(pos)
end
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 reset_quarry(pos) end
set_quarry_status(pos)
end
local function quarry_handle_purge(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local cache = inv:get_list("cache")
if not cache then
inv:set_size("cache", 12)
return
end
local i = 0
for _,stack in ipairs(cache) do
i = i + 1
if stack then
local item = stack:to_table()
if item then
technic.tube_inject_item(pos, pos, quarry_eject_dir, item)
stack:clear()
inv:set_stack("cache", i, stack)
break
end
end
end
if inv:is_empty("cache") then
meta:set_int("purge_on", 0)
end
end
local function can_dig_node(pos, node_name, owner, digger)
if node_name == "air" or node_name == "vacuum:vacuum" then
return false
end
local def = minetest.registered_nodes[node_name]
if not def or not def.diggable or (def.can_dig and not def.can_dig(pos, digger)) then
return false
end
if minetest.is_protected(pos, owner) then
return false
end
return true
end
local function find_ground(quarry_pos, quarry_dir, meta)
local dir = minetest.facedir_to_dir(quarry_dir % 4)
local radius = meta:get_int("size")
local dig_level = meta:get_int("dig_level")
local middle_pos = vector.add(quarry_pos, vector.multiply(dir, radius + 1))
local pos1 = vector.new(middle_pos.x - radius, dig_level, middle_pos.z - radius)
local pos2 = vector.new(middle_pos.x + radius, dig_level, middle_pos.z + radius)
local nodes = minetest.find_nodes_in_area(pos1, pos2, {"air", "vacuum:vacuum"})
if #nodes < meta:get_int("dig_steps") then
return vector.new(middle_pos.x, dig_level, middle_pos.z)
end
meta:set_int("dig_level", dig_level - 1)
return nil
end
local function get_dig_pos(quarry_pos, quarry_dir, dig_pos, dig_index, dig_steps, meta)
if dig_index > 0 and dig_index < dig_steps then
local facedir = (quarry_dir + quarry_dig_pattern[dig_index]) % 4
dig_pos = vector.add(dig_pos, minetest.facedir_to_dir(facedir))
elseif dig_index >= dig_steps then
local dig_level = meta:get_int("dig_level")
if (quarry_pos.y - dig_level) >= quarry_max_depth then
return nil, dig_index
end
local dir = minetest.facedir_to_dir(quarry_dir % 4)
dig_pos = vector.add(quarry_pos, vector.multiply(dir, meta:get_int("size") + 1))
dig_pos.y = dig_level - 1
meta:set_int("dig_level", dig_pos.y)
dig_index = 0
end
dig_index = dig_index + 1
return dig_pos, dig_index
end
local function execute_dig(pos, node, meta)
local dig_pos = minetest.string_to_pos(meta:get_string("dig_pos"))
local quarry_dir = meta:get_int("quarry_dir")
if not dig_pos then
-- quarry has not hit ground yet
dig_pos = find_ground(pos, quarry_dir, meta)
else
local owner = meta:get_string("owner")
local digger = pipeworks.create_fake_player({name = owner})
local dig_steps = meta:get_int("dig_steps")
local dig_index = meta:get_int("dig_index")
local t0 = minetest.get_us_time()
local us_used = 0
-- search for something to dig
while us_used < quarry_time_limit do
dig_pos, dig_index = get_dig_pos(pos, quarry_dir, dig_pos, dig_index, dig_steps, meta)
if not dig_pos then
-- finished digging
meta:set_int("finished", 1)
meta:set_int("purge_on", 1)
break
end
local dig_node = technic.get_or_load_node(dig_pos) or minetest.get_node(dig_pos)
if can_dig_node(dig_pos, dig_node.name, owner, digger) then
-- found something to dig, dig it and stop searching
minetest.remove_node(dig_pos)
local inv = meta:get_inventory()
local drops = minetest.get_node_drops(dig_node.name, "")
for _, dropped_item in ipairs(drops) do
local left = inv:add_item("cache", dropped_item)
while not left:is_empty() do
meta:set_int("purge_on", 1)
quarry_handle_purge(pos)
left = inv:add_item("cache", left)
end
end
local dug_nodes = meta:get_int("dug") + 1
meta:set_int("dug", dug_nodes)
if dug_nodes % 100 == 0 then
meta:set_int("purge_on", 1)
end
break
end
us_used = minetest.get_us_time() - t0
end
meta:set_int("dig_index", dig_index)
end
if dig_pos then
meta:set_string("dig_pos", minetest.pos_to_string(dig_pos))
end
end
local function quarry_run(pos, node)
local meta = minetest.get_meta(pos)
if minetest.pos_to_string(pos) ~= meta:get_string("quarry_pos") then
-- quarry has been moved since last dig
reset_quarry(pos)
elseif meta:get_int("purge_on") == 1 then
quarry_handle_purge(pos)
elseif meta:get_int("enabled") and meta:get_int("HV_EU_input") >= quarry_demand and meta:get_int("finished") == 0 then
execute_dig(pos, node, meta)
elseif not meta:get_inventory():is_empty("cache") then
meta:set_int("purge_on", 1)
end
set_quarry_status(pos)
end
minetest.register_node("technic:quarry", {
description = S("%s Quarry"):format("HV"),
tiles = {
"technic_carbon_steel_block.png^pipeworks_tube_connection_metallic.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png",
"technic_carbon_steel_block.png^default_tool_mesepick.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png"
},
paramtype2 = "facedir",
groups = {cracky=2, tubedevice=1, technic_machine=1, technic_hv=1},
connect_sides = {"bottom", "front", "left", "right"},
tube = {
connect_sides = {top = 1},
-- lower priority than other tubes, so that quarries will prefer any
-- other tube to another quarry, which could lead to server freezes
-- in certain quarry placements (2x2 for example would never eject)
priority = 10,
can_go = function(pos, node, velocity, stack)
-- always eject the same, even if items came in another way
-- this further mitigates loops and generally avoids random sideway movement
-- that can be expected in certain quarry placements
return { quarry_eject_dir }
end
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("size", 4)
meta:get_inventory():set_size("cache", 12)
reset_quarry(pos)
set_quarry_status(pos)
end,
on_rotate = function(pos, node, player, click, new_param2)
local meta = minetest.get_meta(pos)
if meta:get_int("enabled") == 1 then
return false
end
-- only allow rotation around y-axis
node.param2 = new_param2 % 4
minetest.swap_node(pos, node)
reset_quarry(pos)
set_quarry_status(pos)
return true
end,
after_place_node = function(pos, placer, itemstack)
minetest.get_meta(pos):set_string("owner", placer:get_player_name())
pipeworks.scan_for_tube_objects(pos)
end,
can_dig = function(pos,player)
return minetest.get_meta(pos):get_inventory():is_empty("cache")
end,
after_dig_node = pipeworks.scan_for_tube_objects,
on_receive_fields = quarry_receive_fields,
technic_run = quarry_run,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return is_player_allowed(pos, player:get_player_name()) and count or 0
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
return is_player_allowed(pos, player:get_player_name()) and stack:get_count() or 0
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
return is_player_allowed(pos, player:get_player_name()) and stack:get_count() or 0
end,
mesecons = {
effector = {
action_on = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("enabled", 1)
set_quarry_status(pos)
end,
action_off = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("enabled", 0)
set_quarry_status(pos)
end
}
}
})
minetest.register_craft({
recipe = {
{"technic:carbon_plate", "pipeworks:filter", "technic:composite_plate"},
{"basic_materials:motor", "technic:machine_casing", "technic:diamond_drill_head"},
{"technic:carbon_steel_block", "technic:hv_cable", "technic:carbon_steel_block"}},
output = "technic:quarry"
})
technic.register_machine("HV", "technic:quarry", technic.receiver)