362 lines
12 KiB
Lua
362 lines
12 KiB
Lua
-- BASIC_HARVEST: lightweight simple harvesting to be used with protector mod
|
|
-- minetest 0.4.14+
|
|
-- (c) 2015-2016 rnd
|
|
|
|
-- This program is free software: you can redistribute it and/or modify
|
|
-- it under the terms of the GNU General Public License as published by
|
|
-- the Free Software Foundation, either version 3 of the License, or
|
|
-- (at your option) any later version.
|
|
|
|
-- This program is distributed in the hope that it will be useful,
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
-- GNU General Public License for more details.
|
|
|
|
-- You should have received a copy of the GNU General Public License
|
|
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
local harvest = {};
|
|
|
|
-- SETTINGS
|
|
|
|
harvest.radius = 5; -- this should be protector radius
|
|
harvest.full = 5; -- how many steps before new harvest cycle begins: so harvester is activated every 5x20s by default
|
|
harvest.timestep = 20; -- how frequently to process harvesters ( default every 30 seconds )
|
|
harvest.fuel_cost = 0.1; -- how many coal units to harvest 1 piece
|
|
harvest.transport_cost = 0.01; -- how much to transport 1 unit of dug material 1 block distance to target inventory
|
|
-- for example: to transport 10 units over distance 10 costs: transport_cost*10*10 = 1
|
|
|
|
|
|
harvest.forbidden_nodes = {
|
|
["air"]=true,
|
|
["default:water_source"]=true,
|
|
["default:water_flowing"]=true,
|
|
["default:lava_source"]=true,
|
|
["default:lava_flowing"]=true,
|
|
}
|
|
|
|
-- END OF SETTINGS
|
|
|
|
|
|
|
|
local harvest_process = function(pos) -- burn fuel, dig, put materials in target container
|
|
|
|
local meta = minetest.get_meta(pos);
|
|
local search_node = meta:get_string("dig");
|
|
local place_node = meta:get_string("place");
|
|
|
|
if search_node == "" or harvest.forbidden_nodes[search_node] then
|
|
meta:set_string("infotext","error: right click harvester and set what to harvest by inserting item in DIG");
|
|
return
|
|
end
|
|
|
|
local x0,y0,z0; -- protector position
|
|
x0 = meta:get_int("x0");y0 = meta:get_int("y0");z0 = meta:get_int("z0");
|
|
local x1,y1,z1; -- container position
|
|
x1 = meta:get_int("x1");y1 = meta:get_int("y1");z1 = meta:get_int("z1");
|
|
local tpos = {x=x1,y=y1,z=z1};
|
|
local distance = meta:get_int("distance")
|
|
|
|
if minetest.is_protected(tpos, meta:get_string("owner")) then
|
|
meta:set_string("infotext", "error. target position " .. minetest.string_to_pos(tpos) .. " is protected.");
|
|
return
|
|
end
|
|
|
|
-- DIG PROCESS
|
|
local pos1 = {x=x0-harvest.radius,y=y0-harvest.radius,z=z0-harvest.radius}
|
|
local pos2 = {x=x0+harvest.radius,y=y0+harvest.radius,z=z0+harvest.radius}
|
|
|
|
-- load area data
|
|
local manip = minetest.get_voxel_manip() -- VoxelManip object
|
|
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) -- --Reads a chunk of map from the map containing the region formed by pos1 and pos2
|
|
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) -- create new VoxelArea instance, needed for iterating over area in loop
|
|
|
|
local data = manip:get_data() -- Gets the data read into the VoxelManip object
|
|
|
|
local search_id = minetest.get_content_id(search_node);
|
|
if place_node == "" then
|
|
place_node = "air"
|
|
elseif not minetest.registered_items[place_node] then
|
|
place_node = "air"
|
|
end
|
|
|
|
|
|
local replace_id = minetest.get_content_id(place_node);
|
|
|
|
local count = 0
|
|
|
|
for i in area:iterp(pos1, pos2) do -- returns an iterator that returns indices inside VoxelArea
|
|
if data[i] == search_id then
|
|
data[i] = replace_id
|
|
count = count + 1
|
|
end
|
|
end
|
|
|
|
if count == 0 then
|
|
meta:set_string("infotext","idle. nothing to harvest.");
|
|
return
|
|
end
|
|
|
|
|
|
-- CHECK FUEL
|
|
-- check for selected fuel source
|
|
|
|
|
|
-- CHECK TARGET INVENTORY
|
|
local tinv = minetest.get_meta(tpos):get_inventory();
|
|
if not tinv then
|
|
meta:set_string("infotext", "error. target position " .. minetest.pos_to_string(tpos) .. " does not have inventory ");
|
|
return
|
|
end
|
|
|
|
local inv = minetest.get_meta(pos):get_inventory();
|
|
local fuel_source = inv:get_stack("fuel_select",1):get_name();
|
|
|
|
local fueladd, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = inv:get_list("fuel_select") })
|
|
local fuel_need;
|
|
local caloric_value = fueladd.time or 0; -- equals 1 for default:coal_lump
|
|
caloric_value = caloric_value / 40;
|
|
fuel_need = (harvest.fuel_cost+harvest.transport_cost*distance)*count;
|
|
|
|
if caloric_value <= 0 then
|
|
meta:set_string("infotext","error. insert proper fuel into FUEL SELECTOR.")
|
|
return
|
|
end
|
|
|
|
fuel_need = math.ceil(fuel_need/caloric_value);
|
|
|
|
if not tinv:contains_item("main", ItemStack(fuel_source .. " " .. fuel_need)) then
|
|
meta:set_string("infotext", "not enough fuel. Need ".. fuel_need .. " pieces of " .. fuel_source .. " in selected main inventory at " .. minetest.pos_to_string(tpos));
|
|
return;
|
|
end
|
|
|
|
|
|
-- CHECK TARGET INVENTORY FOR REPLACEMENT NODES when place_node~="air"
|
|
if place_node~= "air" then
|
|
if not tinv:contains_item("main", ItemStack(place_node .. " " .. count)) then
|
|
meta:set_string("infotext", "error. can not find enough replacement nodes, need " .. place_node .. " " .. count .. " in target main inventory ");
|
|
return;
|
|
end
|
|
end
|
|
|
|
if not tinv:room_for_item("main", ItemStack(search_node .. " " .. count)) then
|
|
meta:set_string("infotext", "error. can not insert " .. search_node .. " " .. count .. " in target main inventory ");
|
|
return
|
|
end
|
|
|
|
-- ALL OK: add items to target, burn fuel
|
|
|
|
|
|
-- 1. add harvested items
|
|
-- drop code emulation
|
|
local table = minetest.registered_items[search_node];
|
|
|
|
if table~=nil then --put in chest
|
|
if table.drop~= nil then -- drop handling
|
|
|
|
if table.drop.items then
|
|
--handle drops better, emulation of drop code
|
|
local max_items = table.drop.max_items or 0;
|
|
if max_items==0 then -- just drop all the items (taking the rarity into consideration)
|
|
max_items = #table.drop.items or 0;
|
|
end
|
|
local drop = table.drop;
|
|
local i = 0;
|
|
for k,v in pairs(drop.items) do
|
|
if i > max_items then break end; i=i+1;
|
|
local rare = v.rarity or 1;
|
|
|
|
local count1 = math.floor(count/rare);
|
|
for _,v1 in pairs(v.items) do
|
|
local itemcount = string.find(v1, " "); -- handle cases where drop is written as "X itemcount"
|
|
if itemcount then
|
|
count1=count1*tonumber(string.sub(v1,itemcount));
|
|
v1 = string.sub(v1,1,itemcount-1);
|
|
end
|
|
tinv:add_item("main",ItemStack(v1 .. " " .. count1));
|
|
|
|
end
|
|
end
|
|
else
|
|
tinv:add_item("main",ItemStack(table.drop .. " " .. count));
|
|
end
|
|
else
|
|
tinv:add_item("main",ItemStack(search_node .. " " .. count));
|
|
end
|
|
end
|
|
|
|
-- 2. replace nodes if necessary
|
|
tinv:remove_item("main", ItemStack(place_node.. " " .. count));
|
|
|
|
-- 3. burn fuel
|
|
tinv:remove_item("main", ItemStack(fuel_source.. " " .. fuel_need));
|
|
|
|
-- 4. Update map
|
|
manip:set_data(data) -- Sets the data contents of the VoxelManip object
|
|
manip:write_to_map() -- Writes the data loaded from the VoxelManip back to the map
|
|
manip:update_map() -- Update map after writing chunk back to map
|
|
manip:update_liquids() -- this will trigger events like water flow
|
|
|
|
meta:set_string("infotext", "Harvest cycle completed. Processed ".. count .. " items ");
|
|
meta:set_int("process",0); -- resets harvester - new cycle begins
|
|
end
|
|
|
|
|
|
minetest.register_abm{
|
|
nodenames = {"basic_harvest:harvester"},
|
|
neighbors = {},
|
|
interval = harvest.timestep,
|
|
chance = 1,
|
|
action = function(pos)
|
|
local meta = minetest.get_meta(pos);
|
|
local player = minetest.get_player_by_name(meta:get_string("owner")); if not player then return end
|
|
local process = meta:get_int("process");
|
|
|
|
if process<harvest.full then
|
|
process=process+1;
|
|
end
|
|
|
|
if process>= harvest.full then
|
|
-- try harvest
|
|
harvest_process(pos);
|
|
else
|
|
meta:set_int("process", process);
|
|
meta:set_string("infotext","processing harvest: " .. process .. "/" .. harvest.full);
|
|
end
|
|
end,
|
|
}
|
|
|
|
|
|
local harvester_update_meta = function(pos)
|
|
local meta = minetest.get_meta(pos);
|
|
local x1,y1,z1;
|
|
x1 = meta:get_int("x1"); y1 = meta:get_int("y1");z1 = meta:get_int("z1");
|
|
local dig = meta:get_string("dig");
|
|
local place = meta:get_string("place");
|
|
|
|
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
|
|
local form =
|
|
"size[8,11]" .. -- width, height
|
|
--"size[6,10]" .. -- width, height
|
|
"label[0,0;FUEL SELECTOR]".."label[2,0;DIG]"..
|
|
"list["..list_name..";fuel_select;0.,0.5;1,1;]"..
|
|
"list["..list_name..";dig;2.,0.5;1,1;]".. "field[3.25,0.75;2,1;dig;dig;"..dig.."]" ..
|
|
"field[3.25,1.75;2,1;place;place;"..place.."]" ..
|
|
"field[5.25,0.75;1,1;x1;inventory;"..x1.."] field[6.25,0.75;1,1;y1;;"..y1.."] field[7.25,0.75;1,1;z1;;"..z1.."]"..
|
|
"list["..list_name..";main;0.,2.5;8,4;]"..
|
|
"list[current_player;main;0,7;8,4;]";
|
|
--"button[8.5,0.5;1,1;OK;OK]";
|
|
meta:set_string("formspec", form);
|
|
end
|
|
|
|
|
|
|
|
minetest.register_node("basic_harvest:harvester", {
|
|
description = "Harvester : harvest all selected nodes inside nearby protector space",
|
|
tiles = {"harvester.png"},
|
|
groups = {oddly_breakable_by_hand=2},
|
|
sounds = "",
|
|
after_place_node = function(pos, placer)
|
|
local meta = minetest.get_meta(pos);
|
|
|
|
local ppos = minetest.find_node_near(pos,harvest.radius,"protector:protect");
|
|
if not ppos then
|
|
meta:set_string("infotext", "error: need to place inside protector area.");
|
|
return
|
|
end
|
|
|
|
local name = placer:get_player_name();
|
|
if minetest.get_meta(ppos):get_string("owner")~=name then
|
|
meta:set_string("infotext", "error: must be placed by owner of protector");
|
|
end
|
|
|
|
meta:set_int("x1",pos.x);meta:set_int("y1",pos.y);meta:set_int("z1",pos.z);
|
|
|
|
meta:set_int("x0",ppos.x);meta:set_int("y0",ppos.y);meta:set_int("z0",ppos.z);
|
|
|
|
meta:set_string("infotext", "Harvester: To operate it insert fuel into FUEL_SELECTOR and main inventory, then insert item to be harvested into DIG. After that just wait.")
|
|
meta:set_string("owner", placer:get_player_name());
|
|
|
|
local inv = meta:get_inventory();
|
|
inv:set_size("dig", 1);
|
|
inv:set_size("main",32);
|
|
inv:set_size("fuel_select",1);
|
|
end,
|
|
|
|
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
|
|
local meta = minetest.get_meta(pos);
|
|
local privs = minetest.get_player_privs(player:get_player_name());
|
|
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return end
|
|
harvester_update_meta(pos);
|
|
end,
|
|
|
|
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
local meta = minetest.get_meta(pos);
|
|
local privs = minetest.get_player_privs(player:get_player_name());
|
|
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return 0 end
|
|
|
|
if listname == "dig" then
|
|
meta:set_string("dig", stack:get_name())
|
|
harvester_update_meta(pos);
|
|
end
|
|
return stack:get_count();
|
|
end,
|
|
|
|
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
|
local meta = minetest.get_meta(pos);
|
|
local privs = minetest.get_player_privs(player:get_player_name());
|
|
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return 0 end
|
|
|
|
if listname == "dig" then
|
|
meta:set_string("dig", "")
|
|
harvester_update_meta(pos);
|
|
end
|
|
return stack:get_count();
|
|
end,
|
|
|
|
|
|
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
|
return 0;
|
|
end,
|
|
|
|
on_receive_fields = function(pos, formname, fields, sender)
|
|
|
|
if minetest.is_protected(pos, sender:get_player_name()) then return end
|
|
|
|
local meta = minetest.get_meta(pos);
|
|
if fields.dig then
|
|
meta:set_string("dig",fields.dig)
|
|
end
|
|
|
|
if fields.place then
|
|
meta:set_string("place",fields.place)
|
|
end
|
|
|
|
local update_dist = false;
|
|
if fields.x1 then meta:set_int("x1",fields.x1);update_dist = true; end
|
|
if fields.y1 then meta:set_int("y1",fields.y1);update_dist = true; end
|
|
if fields.z1 then meta:set_int("z1",fields.z1);update_dist = true; end
|
|
|
|
if update_dist then
|
|
local dist = math.sqrt((pos.x - meta:get_int("x1"))^2+(pos.y - meta:get_int("y1"))^2+(pos.z - meta:get_int("z1"))^2);
|
|
meta:set_int("distance", dist);
|
|
end
|
|
|
|
harvester_update_meta(pos);
|
|
|
|
if fields.quit then return end
|
|
end,
|
|
|
|
})
|
|
|
|
|
|
minetest.register_craft({
|
|
output = "basic_harvest:harvester",
|
|
recipe = {
|
|
{"default:steel_ingot","default:mese","default:steel_ingot"},
|
|
{"default:mese","default:diamondblock","default:mese"},
|
|
{"default:steel_ingot","default:mese","default:steel_ingot"},
|
|
}
|
|
}) |