diff --git a/README.md b/README.md index 6c28818..c706e2a 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,15 @@ See LICENSE for license information. Authors of source code ---------------------- -SFENCE (MIT) +Copyright (c) 2021 SFENCE +MIT - check LICENSE file Authors of media ---------------- -SFENCE (CC BY-SA 3.0): -All textures not mentioned below. +CC BY-SA 3.0 - check LICENSE file +Copyright (c) 2021 SFENCE (CC BY-SA 3.0): +All textures not mentioned below. Introduction ------------ diff --git a/appliance.lua b/appliance.lua index e2e3ea7..09b4671 100644 --- a/appliance.lua +++ b/appliance.lua @@ -17,8 +17,26 @@ function appliance:new(def) return def; end +-- stacks +appliance.input_stack = "input"; +appliance.input_stack_size = 1; +appliance.use_stack = "use_in"; +appliance.use_stack_size = 1; +appliance.output_stack = "output"; +appliance.output_stack_size = 4; + -- is appliance connected to water? +local pipe_connections = { + left = minetest.dir_to_facedir({x=0,y=0,z=-1}), + right = minetest.dir_to_facedir({x=0,y=0,z=1}), + top = nil, + bottom = nil, + front = minetest.dir_to_facedir({x=-1,y=0,z=0}), + back = minetest.dir_to_facedir({x=1,y=0,z=0}), +} +minetest.log("warning", dump(pipe_connections)) appliance.need_water = false; +appliance.pipe_side = "top"; -- right, left, front, back, bottom if appliances.have_pipeworks then local pipeworks_pipe_loaded = { ["pipeworks:pipe_1_loaded"] = true, @@ -115,18 +133,33 @@ if appliances.have_technic then return eu_data.run_speed; end end + -- punch + local eu_data = self.power_data["punch"]; + if (eu_data~=nil) then + local is_punched = meta:get_int("is_punched"); + if (is_punched~=0) then + return eu_data.run_speed; + end + end return 0; end elseif appliances.have_mesecons then -- mesecon powered function appliance:is_powered(meta) - local is_powered = meta:get_int("is_powered"); - if (is_powered~=0) then - local eu_data = self.power_data["no_technic"]; - if (eu_data~=nil) then + local eu_data = self.power_data["no_technic"]; + if (eu_data~=nil) then + local is_powered = meta:get_int("is_powered"); + if (is_powered~=0) then + return eu_data.run_speed; + end + end + -- punch + local eu_data = self.power_data["punch"]; + if (eu_data~=nil) then + local is_punched = meta:get_int("is_punched"); + if (is_punched~=0) then return eu_data.run_speed; end - return 1; end return 0; end @@ -137,7 +170,15 @@ else if (eu_data~=nil) then return eu_data.run_speed; end - return 1; + -- punch + local eu_data = self.power_data["punch"]; + if (eu_data~=nil) then + local is_punched = meta:get_int("is_punched"); + if (is_punched~=0) then + return eu_data.run_speed; + end + end + return 0; end end @@ -196,10 +237,26 @@ appliance.recipes = { inputs = {}, usages = nil, } - +appliance.have_input = true; +appliance.have_usage = true; +appliance.stoppable_usage = true; function appliance:recipe_register_input(input_name, input_def) - self.recipes.inputs[input_name] = input_def; + if (self.input_stack_size <= 1) then + self.recipes.inputs[input_name] = input_def; + else + local register = true; + for index, value in pairs(input_def.inputs) do + if ((index < 1) or ( index > self.input_stack_size)) then + minetest.log("error", "Input definition is not compatible with size of input stack: "..dump(input_def)); + register = false; + break; + end + end + if register then + table.insert(self.recipes.inputs, input_def); + end + end end function appliance:recipe_register_usage(usage_name, usage_def) if (not self.recipes.usages) then @@ -209,27 +266,60 @@ function appliance:recipe_register_usage(usage_name, usage_def) end function appliance:recipe_aviable_input(inventory) - local input_stack = inventory:get_stack("input", 1) - local input_name = input_stack:get_name(); - local input = self.recipes.inputs[input_name]; - if (input==nil) then - return nil, nil - end - if (input_stack:get_count()1 then - local inv_list = table.copy(inventory:get_list("output")); + local inv_list = table.copy(inventory:get_list(self.output_stack)); for index = 1,#output do - if (inventory:room_for_item("output", output[index])~=true) then - inventory:set_list("output", inv_list); + if (inventory:room_for_item(self.output_stack, output[index])~=true) then + inventory:set_list(self.output_stack, inv_list); return false; end - inventory:add_item("output", output[index]); + inventory:add_item(self.output_stack, output[index]); end - inventory:set_list("output", inv_list); + inventory:set_list(self.output_stack, inv_list); else - if (inventory:room_for_item("output", output[1])~=true) then + if (inventory:room_for_item(self.output_stack, output[1])~=true) then return false; end end @@ -276,20 +366,31 @@ end function appliance:recipe_output_to_stack(inventory, output) for index = 1,#output do - inventory:add_item("output", output[index]); + inventory:add_item(self.output_stack, output[index]); end end function appliance:recipe_input_from_stack(inventory, input) - local remove_stack = inventory:get_stack("input", 1); - remove_stack:set_count(input.inputs); - inventory:remove_item("input", remove_stack); + if (self.input_stack_size <= 1) then + local remove_stack = inventory:get_stack(self.input_stack, 1); + remove_stack:set_count(input.inputs); + inventory:remove_item(self.input_stack, remove_stack); + else + for rem_i, rem_val in pairs(input.inputs) do + local remove_stack = inventory:get_stack(self.input_stack, rem_i); + local item_stack = ItemStack(rem_val); + if (item_stack:get_count()>0) then + remove_stack:take_item(item_stack:get_count()); + inventory:set_stack(self.input_stack, rem_i, remove_stack); + end + end + end end function appliance:recipe_usage_from_stack(inventory, usage) - local remove_stack = inventory:get_stack("use_in", 1); + local remove_stack = inventory:get_stack(self.use_stack, 1); remove_stack:set_count(1); - inventory:remove_item("use_in", remove_stack); + inventory:remove_item(self.use_stack, remove_stack); end function appliance:recipe_step_size(step_size) @@ -310,11 +411,40 @@ function appliance:recipe_inventory_can_put(pos, listname, index, stack, player, end end - if listname == "input" then - return self.recipes.inputs[stack:get_name()] and - stack:get_count() or 0 + if listname == self.input_stack then + if (self.input_stack_size <= 1) then + return self.recipes.inputs[stack:get_name()] and + stack:get_count() or 0 + else + local meta = minetest.get_meta(pos); + local inventory = meta:get_inventory(); + + for i, check in pairs(self.recipes.inputs) do + local valid = true; + for ch_i, ch_val in pairs(check.inputs) do + local input_stack = inventory:get_stack(self.input_stack, ch_i); + local input_name = input_stack:get_name(); + local check_stack = ItemStack(ch_val); + if ((input_name~="") and (input_name~=check_stack:get_name())) then + valid = false; + break; + end + end + if valid then + local check_stack = check.inputs[index]; + if check_stack then + check_stack = ItemStack(check_stack); + if (stack:get_name()==check_stack:get_name()) then + return stack:get_count(); + end + end + end + end + + return 0; + end end - if listname == "use_in" then + if listname == self.use_stack then return self.recipes.usages[stack:get_name()] and stack:get_count() or 0 end @@ -329,7 +459,7 @@ function appliance:recipe_inventory_can_take(pos, listname, index, stack, player end local count = stack:get_count(); local meta = minetest.get_meta(pos); - if (listname=="input") then + if (listname==self.input_stack) then local production_time = meta:get_int("production_time") or 0 if (production_time>0) then local input = self.recipes.inputs[stack:get_name()]; @@ -340,7 +470,7 @@ function appliance:recipe_inventory_can_take(pos, listname, index, stack, player minetest.log("error", "Input item missing in recipes list.") end end - elseif (listname=="use_in") then + elseif (listname==self.use_stack) then local consumption_time = meta:get_int("consumption_time") or 0 if (consumption_time>0) then count = count - 1; @@ -356,11 +486,11 @@ function appliance:tube_can_insert (pos, node, stack, direction, owner) if self.recipes then local input = self.recipes.inputs[stack:get_name()]; if input then - return appliances.recipe_inventory_can_put(pos, "input", 1, stack, nil, recipes); + return appliances.recipe_inventory_can_put(pos, self.input_stack, 1, stack, nil, recipes); end local usage = self.recipes.usages[stack:get_name()]; if usage then - return appliances.recipe_inventory_can_put(pos, "use_in", 1, stack, nil, recipes); + return appliances.recipe_inventory_can_put(pos, self.use_stack, 1, stack, nil, recipes); end end return false; @@ -372,11 +502,11 @@ function appliance:tube_insert (pos, node, stack, direction, owner) local input = self.recipes.inputs[stack:get_name()]; if input then - return inv:add_item("input", stack); + return inv:add_item(self.input_stack, stack); end local usages = self.recipes.usages[stack:get_name()]; if usages then - return inv:add_item("use_in", stack); + return inv:add_item(self.use_stack, stack); end end @@ -387,7 +517,7 @@ end -- form spec -function appliance:get_formspec(production_percent, consumption_percent) +function appliance:get_formspec(meta, production_percent, consumption_percent) local progress = "image[3.6,0.5;5.5,0.95;appliances_production_progress_bar.png^[transformR270]]"; if production_percent then progress = "image[3.6,0.5;5.5,0.95;appliances_production_progress_bar.png^[lowpart:" .. @@ -406,15 +536,15 @@ function appliance:get_formspec(production_percent, consumption_percent) "background[-1.25,-1.25;15,10;appliances_appliance_formspec.png]" .. progress.. "list[current_player;main;1.5,3;8,4;]" .. - "list[context;input;2,0.25;1,1;]" .. - "list[context;use_in;2,1.5;1,1;]" .. - "list[context;output;9.75,0.25;2,2;]" .. + "list[context;"..self.input_stack..";2,0.25;1,1;]" .. + "list[context;"..self.use_stack..";2,1.5;1,1;]" .. + "list[context;"..self.output_stack..";9.75,0.25;2,2;]" .. "listring[current_player;main]" .. - "listring[context;input]" .. + "listring[context;"..self.input_stack.."]" .. "listring[current_player;main]" .. - "listring[context;use_in]" .. + "listring[context;"..self.use_stack.."]" .. "listring[current_player;main]" .. - "listring[context;output]" .. + "listring[context;"..self.output_stack.."]" .. "listring[current_player;main]"; return formspec; end @@ -428,7 +558,7 @@ function appliance:update_formspec(meta, production_time, production_goal, consu if (consumption_time and consumption_goal) then consumption_percent = math.floor(consumption_time / consumption_goal * 100); end - meta:set_string("formspec", self:get_formspec(production_percent, consumption_percent)); + meta:set_string("formspec", self:get_formspec(meta, production_percent, consumption_percent)); end -- Inactive/Active @@ -438,7 +568,7 @@ function appliance:activate(pos, meta) if (not timer:is_started()) then timer:start(1) self:power_need(meta) - meta:set_string(self.meta_infotext, self.node_description.." - active") + meta:set_string(self.meta_infotext, self.node_description.." - active") end end @@ -465,6 +595,40 @@ function appliance:no_power(pos, meta) meta:set_string(self.meta_infotext, self.node_description.." - unpowered") end +-- +function appliance:need_wait(pos, meta, inv) + -- have aviable production recipe? + local use_input, use_usage = self:recipe_aviable_input(inv); + if (use_input==nil) then + return use_input, use_usage, true; + end + + -- space for production outputs? + if (#use_input.outputs==1) then + local output = self:recipe_select_output(use_input.outputs); + if (not self:recipe_room_for_output(inv, output)) then + return use_input, use_usage, true; + end + end + + -- check for water pipe connection + if (self.need_water) then + if (self:have_water(pos)~=true) then + return use_input, use_usage, true; + end + end + + return use_input, use_usage, false; +end +function appliance:have_power(pos, meta, inv) + -- check if node is powered + local speed = self:is_powered(meta) + if (speed==0) then + return speed, false; + end + return speed, true; +end + -- appliance node callbacks for mesecons function appliance:cb_mesecons_effector_action_on(pos, node) minetest.get_meta(pos):set_int("is_powered", 1); @@ -499,31 +663,40 @@ end -- appliance node callbacks function appliance:cb_can_dig(pos) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:is_empty("input") and inv:is_empty("output") - end + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + return inv:is_empty(self.input_stack) and inv:is_empty(self.output_stack) +end + function appliance:cb_after_dig_node(pos, oldnode, oldmetadata, digger) - pipeworks.scan_for_pipe_objects(pos); - pipeworks.scan_for_tube_objects(pos); - - local stack = oldmetadata.inventory["use_in"][1]; - local consumption_time = tonumber(oldmetadata.fields["consumption_time"] or "0"); - if (consumption_time>0) then - stack:take_item(1); - end - minetest.item_drop(stack, digger, pos) + pipeworks.scan_for_pipe_objects(pos); + pipeworks.scan_for_tube_objects(pos); + + local stack = oldmetadata.inventory[self.use_stack][1]; + local consumption_time = tonumber(oldmetadata.fields["consumption_time"] or "0"); + if (consumption_time>0) then + stack:take_item(1); end + minetest.item_drop(stack, digger, pos) +end + +function appliance:cb_on_punch(pos, node, puncher, pointed_thing) + local eu_data = self.power_data["punch"]; + if (eu_data~=nil) then + local meta = minetest.get_meta(pos); + meta:set_int("is_punched", 1); + end +end function appliance:cb_on_blast(pos) - local drops = {} - default.get_inventory_drops(pos, "input", drops) - default.get_inventory_drops(pos, "use_in", drops) - default.get_inventory_drops(pos, "output", drops) - table.insert(drops, self.node_name_inactive) - minetest.remove_node(pos) - return drops - end + local drops = {} + default.get_inventory_drops(pos, self.input_stack, drops) + default.get_inventory_drops(pos, self.use_stack, drops) + default.get_inventory_drops(pos, self.output_stack, drops) + table.insert(drops, self.node_name_inactive) + minetest.remove_node(pos) + return drops +end function appliance:cb_on_timer(pos, elapsed) local meta = minetest.get_meta(pos); @@ -533,37 +706,23 @@ function appliance:cb_on_timer(pos, elapsed) local consumption_time = meta:get_int("consumption_time") or 0; local sound_time = meta:get_int("sound_time") or 0; - -- have aviable production recipe? - local use_input, use_usage = self:recipe_aviable_input(inv); - if (use_input==nil) then + local use_input, use_usage, need_wait = self:need_wait(pos, meta, inv); + if need_wait then self:waiting(pos, meta); return true; end - -- space for production outputs? - if (#use_input.outputs==1) then - local output = self:recipe_select_output(use_input.outputs); - if (not self:recipe_room_for_output(inv, output)) then - self:waiting(pos, meta); - return true; - end - end - - -- check for water pipe connection - if (self.need_water) then - if (self:have_water(pos)~=true) then - self:waiting(pos, meta); - return true; - end - end - -- check if node is powered - local speed = self:is_powered(meta) - if (speed==0) then + local speed, have_power = self:have_power(pos, meta, inv) + if (not have_power) then self:no_power(pos, meta); return true; end + if (self.power_data["punch"]~=nil) then + meta:set_int("is_punched", 0); + end + -- time update local production_step_size = 0; local consumption_step_size = 0; @@ -578,16 +737,18 @@ function appliance:cb_on_timer(pos, elapsed) consumption_time = consumption_time + consumption_step_size; -- remove used item - if (consumption_time>=use_usage.consumption_time) then - local output = self:recipe_select_output(use_usage.outputs); - if (not self:recipe_room_for_output(inv, output)) then - self:waiting(pos, meta); - return true; + if use_usage then + if (consumption_time>=use_usage.consumption_time) then + local output = self:recipe_select_output(use_usage.outputs); + if (not self:recipe_room_for_output(inv, output)) then + self:waiting(pos, meta); + return true; + end + self:recipe_output_to_stack(inv, output); + self:recipe_usage_from_stack(inv, use_usage); + consumption_time = 0; + meta:set_int("consumption_time", 0); end - self:recipe_output_to_stack(inv, output); - self:recipe_usage_from_stack(inv, use_usage); - consumption_time = 0; - meta:set_int("consumption_time", 0); end -- production done @@ -603,9 +764,14 @@ function appliance:cb_on_timer(pos, elapsed) meta:set_int("production_time", 0); end - self:update_formspec(meta, production_time, use_input.production_time, consumption_time, use_usage.consumption_time) - meta:set_int("production_time", production_time) - meta:set_int("consumption_time", consumption_time) + if use_usage then + self:update_formspec(meta, production_time, use_input.production_time, consumption_time, use_usage.consumption_time) + meta:set_int("production_time", production_time) + meta:set_int("consumption_time", consumption_time) + else + self:update_formspec(meta, production_time, use_input.production_time, 0, 1) + meta:set_int("production_time", production_time) + end -- have aviable production recipe? local use_input, use_usage = self:recipe_aviable_input(inv) @@ -671,12 +837,23 @@ end function appliance:cb_on_construct(pos) local meta = minetest.get_meta(pos) - meta:set_string("formspec", self:get_formspec(0, 0)) + meta:set_string("formspec", self:get_formspec(meta, 0, 0)) meta:set_string("infotext", self.node_description) local inv = meta:get_inventory() - inv:set_size("input", 1) - inv:set_size("use_in", 1) - inv:set_size("output", 4) + + if self.input_stack_size>0 then + inv:set_size(self.input_stack, self.input_stack_size) + end + if self.use_stack_size>0 then + if inv:get_size(self.use_stack)==0 then + inv:set_size(self.use_stack, self.use_stack_size) + end + end + if self.output_stack_size>0 then + if inv:get_size(self.output_stack)==0 then + inv:set_size(self.output_stack, self.output_stack_size) + end + end end function appliance:cb_after_place_node(pos, placer, itemstack, pointed_thing) @@ -690,16 +867,15 @@ function appliance:cb_after_place_node(pos, placer, itemstack, pointed_thing) end -- register appliance -function appliance:register_nodes(node_def, inactive_tiles, active_tiles) +function appliance:register_nodes(shared_def, inactive_def, active_def) -- default connection of pipe on top -- default connection of tubes from sides -- default connection of power from back -- may be, late, this will be configurable - local node_def_inactive = table.copy(node_def); + local node_def_inactive = table.copy(shared_def); node_def_inactive.description = self.node_description; - node_def_inactive.tiles = inactive_tiles; local need_power = false; local technic_power = false; @@ -764,7 +940,10 @@ function appliance:register_nodes(node_def, inactive_tiles, active_tiles) end if self.need_water then -- pipe connect - node_def_inactive.pipe_connections = { top = true }; + + node_def_inactive.pipe_connections = {}; + node_def_inactive.pipe_connections[self.pipe_side] = true; + node_def_inactive.pipe_connections[self.pipe_side.."_param2"] = pipe_connections[self.pipe_side]; end if appliances.have_pipeworks then node_def_inactive.groups.tubedevice = 1; @@ -778,7 +957,7 @@ function appliance:register_nodes(node_def, inactive_tiles, active_tiles) return self:cb_tube_can_insert(pos, node, stack, direction, owner); end, connect_sides = {left = 1, right = 1}, - input_inventory = "output", + input_inventory = self.output_stack, }; end node_def_inactive.can_dig = function (pos, player) @@ -787,6 +966,9 @@ function appliance:register_nodes(node_def, inactive_tiles, active_tiles) node_def_inactive.after_dig_node = function (pos, oldnode, oldmetadata, digger) return self:cb_after_dig_node(pos, oldnode, oldmetadata, digger) end + node_def_inactive.on_punch = function (pos, node, puncher, pointed_thing) + return self:cb_on_punch(pos, node, puncher, pointed_thing); + end node_def_inactive.on_blast = function (pos, intensity) return self:cb_on_blast(pos, intensity); end @@ -821,10 +1003,19 @@ function appliance:register_nodes(node_def, inactive_tiles, active_tiles) return self:cb_after_place_node(pos, placer, itemstack, pointed_thing); end - node_def_active.tiles = active_tiles; - node_def_active.groups.not_in_creative_inventory = 1; + if inactive_def then + for key, value in pairs(inactive_def) do + node_def_inactive[key] = value; + end + end + if active_def then + for key, value in pairs(active_def) do + node_def_active[key] = value; + end + end + minetest.register_node(self.node_name_inactive, node_def_inactive); minetest.register_node(self.node_name_active, node_def_active);