From 442d2fa977c90843b4f7b68131f459c40a793c2f Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Mon, 25 Mar 2019 19:31:12 +0200 Subject: [PATCH] Start working on the tinker's smeltery clone --- multifurnace/faucet.lua | 186 ++++++++++++++++++++++++++++++++ multifurnace/furnace.lua | 224 +++++++++++++++++++++++++++++++++++++++ multifurnace/init.lua | 3 + multifurnace/multi.lua | 64 +++++++++++ multifurnace/nodes.lua | 220 -------------------------------------- 5 files changed, 477 insertions(+), 220 deletions(-) create mode 100644 multifurnace/faucet.lua create mode 100644 multifurnace/multi.lua diff --git a/multifurnace/faucet.lua b/multifurnace/faucet.lua new file mode 100644 index 0000000..663b808 --- /dev/null +++ b/multifurnace/faucet.lua @@ -0,0 +1,186 @@ +-- Faucet is a simple liquid transfer node + +local FAUCET_PER_SECOND = 122 + +local function update_timer (pos) + local t = minetest.get_node_timer(pos) + if not t:is_started() then + t:start(1.0) + end +end + +local function faucet_flow (pos, meta) + local angled = minetest.get_node(pos) + local back = vector.add(pos, minetest.facedir_to_dir(angled.param2)) + local backnode = minetest.get_node(back) + local backreg = minetest.registered_nodes[backnode.name] + + if not backreg.node_io_can_take_liquid or not backreg.node_io_can_take_liquid(back, backnode, "front") + or not backreg.node_io_take_liquid then + return false + end + + local stack + if backreg.node_io_get_liquid_stack then + stack = backreg.node_io_get_liquid_stack(back, backnode, "front", 1) + end + + if not stack or stack:is_empty() then + return false + end + + return stack:get_name(), stack:get_count(), back +end + +local function faucet_activate (pos) + local meta = minetest.get_meta(pos) + local source, amount, bhind = faucet_flow(pos, meta) + + if meta:get_string("flowing") ~= "" then return false end + if not source or amount == 0 then return end + + local e = minetest.add_entity(pos, "multifurnace:faucet_flow") + e:get_luaentity():set_faucet(pos) + e:get_luaentity():set_source(source) + + meta:set_string("flowing", source) + meta:set_int("flowing_capacity", amount) + update_timer(pos) +end + +local function faucet_timer (pos, elapsed) + local refresh = false + local meta = minetest.get_meta(pos) + local liquid = meta:get_string("flowing") + local source, amount, bhind = faucet_flow(pos, meta) + + while true do + if liquid == "" then break end + if source ~= liquid or amount <= 0 then + liquid = "" + break + end + + local bhindnode = minetest.get_node(bhind) + local bhindreg = minetest.registered_nodes[bhindnode.name] + + local target = vector.subtract(pos, {x=0,y=1,z=0}) + local tnode = minetest.get_node(target) + local treg = minetest.registered_nodes[tnode.name] + if not treg.node_io_can_put_liquid or not treg.node_io_can_put_liquid(target, tnode, "top") + or not treg.node_io_room_for_liquid then + liquid = "" + break + end + + local flowcap = amount + if flowcap > FAUCET_PER_SECOND then + flowcap = FAUCET_PER_SECOND + end + + local room = treg.node_io_room_for_liquid(target, tnode, "top", liquid, flowcap) + if room > 0 and treg.node_io_put_liquid then + local over = treg.node_io_put_liquid(target, tnode, "top", nil, liquid, room) + + if treg.on_timer then + update_timer(target) + end + else + liquid = "" + break + end + + bhindreg.node_io_take_liquid(bhind, bhindnode, "front", nil, source, room) + if bhindreg.on_timer then + update_timer(bhind) + end + + refresh = true + break + end + + meta:set_string("flowing", liquid) + + return refresh +end +minetest.register_node("multifurnace:faucet", { + description = "Multifurnace Faucet", + tiles = {"metal_melter_heatbrick.png"}, + groups = {cracky = 3, multifurnace_accessory = 1}, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.1875, -0.1875, 0.1250, 0.1875, -0.1250, 0.5000}, + {-0.1875, -0.1250, 0.1250, -0.1250, 0.06250, 0.5000}, + {0.1250, -0.1250, 0.1250, 0.1875, 0.06250, 0.5000} + } + }, + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = false, + on_rightclick = faucet_activate, + on_timer = faucet_timer, +}) + +-- Flow entity + +minetest.register_entity("multifurnace:faucet_flow", { + initial_properties = { + physical = false, + collide_with_objects = false, + visual = "mesh", + mesh = "multifurnace_faucet_flow.obj", + visual_size = {x = 5, y = 5}, + textures = {}, + backface_culling = true, + pointable = false, + static_save = false, + }, + faucet = nil, + source = "", + timer = 0, + set_faucet = function (self, faucet) + self.faucet = faucet + + -- Set correct orientation in regards to the faucet + local node = minetest.get_node(faucet) + local orient = minetest.facedir_to_dir(node.param2) + orient = vector.multiply(orient, -1) + local angle = minetest.dir_to_yaw(orient) + self.object:set_yaw(angle) + end, + set_source = function (self, source) + self.source = source + + -- Set appropriate fluid texture + local tiles = minetest.registered_nodes[source] + if not tiles.liquid_alternative_flowing then return end + local flowing = minetest.registered_nodes[tiles.liquid_alternative_flowing] + if not flowing.tiles or type(flowing.tiles[1]) ~= "string" then return end + + self.object:set_properties({textures = { + flowing.tiles[1] + }}) + end, + on_step = function (self, dt) + self.timer = self.timer + 1 + + -- Remove self when the faucet is destroyed or is no longer marked as flowing + if self.timer >= 10 then + local node = minetest.get_node(self.faucet) + if not node or node.name ~= "multifurnace:faucet" then + self.object:remove() + return + end + + local meta = minetest.get_meta(self.faucet) + if meta:get_string("flowing") ~= self.source then + self.object:remove() + return + end + + self.timer = 0 + end + end +}) diff --git a/multifurnace/furnace.lua b/multifurnace/furnace.lua index e69de29..62108a0 100644 --- a/multifurnace/furnace.lua +++ b/multifurnace/furnace.lua @@ -0,0 +1,224 @@ + +local furnaces = {} + +local function update_timer (pos) + local t = minetest.get_node_timer(pos) + if not t:is_started() then + t:start(1.0) + end +end + +----------------------- +-- Buffer operations -- +----------------------- + +-- List liquids in the controller +local function all_liquids (pos) + local meta = minetest.get_meta(pos) + local count = meta:get_int("buffers") + local stacks = {} + local total = 0 + + if count == 0 then return stacks, total end + + for i = 1, count do + stacks[i] = ItemStack(meta:get_string("buffer" .. i)) + total = total + stacks[i]:get_count() + end + + return stacks, total +end + +-- Set the bottom-most buffer +local function set_hot (pos, buf) + local meta = minetest.get_meta(pos) + local stacks, total = all_liquids(pos) + + if not stacks[buf] or stacks[buf]:is_empty() then + return false + end + + local current_one = stacks[1] + local new_one = stacks[buf] + + meta:set_string("buffer1", new_one:to_string()) + meta:set_string("buffer" .. buf, current_one:to_string()) + + return true +end + +-- Reorganize the buffers, remove empty ones +local function clean_buffer_list (pos) + local meta = minetest.get_meta(pos) + local stacks, total = all_liquids(pos) + local new = {} + + for i,v in pairs(stacks) do + if not v:is_empty() then + table.insert(new, v) + end + end + + for i, v in pairs(new) do + meta:set_string("buffer" .. i, v:to_string()) + end + + meta:set_int("buffers", #new) +end + +-- Returns how much of the first buffer fluid can be extracted +local function can_take_liquid (pos, want_mb) + local meta = minetest.get_meta(pos) + local stacks = all_liquids(pos) + local found = stacks[1] + + if found and found:is_empty() then + clean_buffer_list(pos) + return "", 0 + end + + if not found then return "", 0 end + + local count = 0 + if found:get_count() < want_mb then + count = found:get_count() + else + count = want_mb + end + + return found:get_name(), count +end + + +-- Take liquid from the first buffer +local function take_liquid (pos, want_mb) + local meta = minetest.get_meta(pos) + local stacks = all_liquids(pos) + local found = stacks[1] + local fluid,count = can_take_liquid(pos, want_mb) + + if fluid == "" or count == 0 or fluid ~= found:get_name() then + return fluid, 0 + end + + found = ItemStack(fluid) + found:set_count(count) + + meta:set_string("buffer1", found:to_string()) + + return fluid, count +end + +-- Calculate furnace fluid capacity +local function total_capacity (pos) + return 8000 -- TODO +end + +-- Can you fit this liquid inside the furnace +local function can_put_liquid (pos, liquid) + local stacks, storage = all_liquids(pos) + local total = total_capacity(pos) + local append = liquid:get_count() + + if total == storage then + append = 0 + elseif storage + liquid:get_count() > total then + append = total - storage + end + + return append +end + +-- Returns leftovers +local function put_liquid (pos, liquid) + local stacks, storage = all_liquids(pos) + local total = total_capacity(pos) + local append = can_put_liquid(pos, liquid) + local leftovers = liquid:get_count() - append + + if append == 0 then + return leftovers + end + + -- Find a buffer, if not available, create a new one + local buf = nil + for i,v in pairs(stacks) do + if v:get_name() == liquid:get_name() then + buf = i + break + end + end + + if not buf then + buf = #stacks + 1 + end + + if stacks[buf] then + local st = stacks[buf] + local stc = st:get_count() + append + st:set_count(stc) + meta:set_string("buffer" .. buf, st:to_string()) + else + liquid:set_count(append) + meta:set_string("buffer" .. buf, liquid:to_string()) + end + + return leftovers +end + +-------------------------- +-- Controller Operation -- +-------------------------- + +-- Detect a structure based on controller +local function detect_structure (pos) + local node = minetest.get_node(pos) + local back = vector.add(pos, minetest.facedir_to_dir(node.param2)) + + local center = multifurnace.api.detect_center(back, 32) + + -- TODO... +end + +-- If pos is part of the structure, this will return a position +local function get_controller (pos) + -- body +end + +local function controller_timer (pos, elapsed) + local refresh = false + local meta = minetest.get_meta(pos) + + return refresh +end + +------------------- +-- Registrations -- +------------------- + +minetest.register_node("multifurnace:controller", { + description = "Multifurnace Controller", + tiles = { + "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", + "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", "metal_melter_heatbrick.png^multifurnace_controller_face.png", + }, + groups = {cracky = 3, multifurnace = 1}, + paramtype2 = "facedir", + is_ground_content = false, + on_timer = controller_timer, + on_rightclick = function (pos) + detect_structure(pos) + end +}) + +minetest.register_node("multifurnace:port", { + description = "Multifurnace Port", + tiles = { + "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", + "metal_melter_heatbrick.png", "metal_melter_heatbrick.png^multifurnace_intake_back.png", + "metal_melter_heatbrick.png^multifurnace_intake_face.png", + }, + groups = {cracky = 3, multifurnace = 2}, + paramtype2 = "facedir", + is_ground_content = false, +}) diff --git a/multifurnace/init.lua b/multifurnace/init.lua index 2cbd19e..1159968 100644 --- a/multifurnace/init.lua +++ b/multifurnace/init.lua @@ -6,6 +6,9 @@ multifurnace = rawget(_G, "multifurnace") or {} local modpath = minetest.get_modpath(minetest.get_current_modname()) multifurnace.modpath = modpath +dofile(modpath .. "/multi.lua") + +dofile(modpath .. "/faucet.lua") dofile(modpath .. "/furnace.lua") dofile(modpath .. "/nodes.lua") dofile(modpath .. "/crafting.lua") diff --git a/multifurnace/multi.lua b/multifurnace/multi.lua new file mode 100644 index 0000000..e98ba4b --- /dev/null +++ b/multifurnace/multi.lua @@ -0,0 +1,64 @@ +multifurnace.api = {} + +local function is_inner (pos) + local node = minetest.get_node_or_nil(pos) + return node and node.name == "air" +end + +function multifurnace.api.detect_center (inside, limit) + -- "inside" is the position behind the controller, "inside the furnace" + + -- adjust the x-position until the difference between the outer walls is at most 1 + -- basically this means we center the position inside the furnace on the x axis. + local xd1 = 1 -- x-difference + local xd2 = 1 + + local zd1 = 1 -- z-difference + local zd2 = 1 + + for i = 1, limit do -- don't check farther than needed + -- expand the range on the x axis as long as one side has not met a wall + if is_inner(vector.add(inside, {x = -xd1, y = 0, z = 0})) then + xd1 = xd1 + 1 + elseif is_inner(vector.add(inside, {x = xd2, y = 0, z = 0})) then + xd2 = xd2 + 1 + end + + -- if one side hit a wall and the other didn't we might have to re-center our x-position again + if xd1 - xd2 > 1 then + -- move x and offsets to the -x + xd1 = xd1 - 1 + inside = vector.add(inside, {x = -1, y = 0, z = 0}) + xd2 = xd2 + 1 + end + -- or the right + if xd2 - xd1 > 1 then + xd2 = xd2 - 1 + inside = vector.add(inside, {x = 1, y = 0, z = 0}) + xd1 = xd1 + 1 + end + + -- also do exactly the same on the z axis + if is_inner(vector.add(inside, {x = 0, y = 0, z = -zd1})) then + zd1 = zd1 + 1 + elseif is_inner(vector.add(inside, {x = 0, y = 0, z = zd2})) then + zd2 = zd2 + 1 + end + + if zd1 - zd2 > 1 then + -- move x and offsets to the -x + zd1 = zd1 - 1 + inside = vector.add(inside, {x = 0, y = 0, z = -1}) + zd2 = zd2 + 1 + end + + -- or the right + if zd2 - zd1 > 1 then + zd2 = zd2 - 1 + inside = vector.add(inside, {x = 0, y = 0, z = 1}) + zd1 = zd1 + 1 + end + end + + return inside +end diff --git a/multifurnace/nodes.lua b/multifurnace/nodes.lua index b1cbcad..00c115d 100644 --- a/multifurnace/nodes.lua +++ b/multifurnace/nodes.lua @@ -1,224 +1,4 @@ -local FAUCET_PER_SECOND = 122 - --- Node funtions - -local function update_timer (pos) - local t = minetest.get_node_timer(pos) - if not t:is_started() then - t:start(1.0) - end -end - -local function controller_timer (pos, elapsed) - local refresh = false - local meta = minetest.get_meta(pos) - - return refresh -end - -local function faucet_flow (pos, meta) - local angled = minetest.get_node(pos) - local back = vector.add(pos, minetest.facedir_to_dir(angled.param2)) - local backnode = minetest.get_node(back) - local backreg = minetest.registered_nodes[backnode.name] - - if not backreg.node_io_can_take_liquid or not backreg.node_io_can_take_liquid(back, backnode, "front") - or not backreg.node_io_take_liquid then - return false - end - - local stack - if backreg.node_io_get_liquid_stack then - stack = backreg.node_io_get_liquid_stack(back, backnode, "front", 1) - end - - if not stack or stack:is_empty() then - return false - end - - return stack:get_name(), stack:get_count(), back -end - -local function faucet_activate (pos) - local meta = minetest.get_meta(pos) - local source, amount, bhind = faucet_flow(pos, meta) - - if meta:get_string("flowing") ~= "" then return false end - if not source or amount == 0 then return end - - local e = minetest.add_entity(pos, "multifurnace:faucet_flow") - e:get_luaentity():set_faucet(pos) - e:get_luaentity():set_source(source) - - meta:set_string("flowing", source) - meta:set_int("flowing_capacity", amount) - update_timer(pos) -end - -local function faucet_timer (pos, elapsed) - local refresh = false - local meta = minetest.get_meta(pos) - local liquid = meta:get_string("flowing") - local source, amount, bhind = faucet_flow(pos, meta) - - while true do - if liquid == "" then break end - if source ~= liquid or amount <= 0 then - liquid = "" - break - end - - local bhindnode = minetest.get_node(bhind) - local bhindreg = minetest.registered_nodes[bhindnode.name] - - local target = vector.subtract(pos, {x=0,y=1,z=0}) - local tnode = minetest.get_node(target) - local treg = minetest.registered_nodes[tnode.name] - if not treg.node_io_can_put_liquid or not treg.node_io_can_put_liquid(target, tnode, "top") - or not treg.node_io_room_for_liquid then - liquid = "" - break - end - - local flowcap = amount - if flowcap > FAUCET_PER_SECOND then - flowcap = FAUCET_PER_SECOND - end - - local room = treg.node_io_room_for_liquid(target, tnode, "top", liquid, flowcap) - if room > 0 and treg.node_io_put_liquid then - local over = treg.node_io_put_liquid(target, tnode, "top", nil, liquid, room) - - if treg.on_timer then - update_timer(target) - end - else - liquid = "" - break - end - - bhindreg.node_io_take_liquid(bhind, bhindnode, "front", nil, source, room) - if bhindreg.on_timer then - update_timer(bhind) - end - - refresh = true - break - end - - meta:set_string("flowing", liquid) - - return refresh -end - --- Node definitions minetest.override_item("metal_melter:heated_bricks", { groups = {cracky = 3, multifurnace = 1}, }) - -minetest.register_node("multifurnace:controller", { - description = "Multifurnace Controller", - tiles = { - "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", - "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", "metal_melter_heatbrick.png^multifurnace_controller_face.png", - }, - groups = {cracky = 3, multifurnace = 1}, - paramtype2 = "facedir", - is_ground_content = false, - on_timer = controller_timer, -}) - -minetest.register_node("multifurnace:port", { - description = "Multifurnace Port", - tiles = { - "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", "metal_melter_heatbrick.png", - "metal_melter_heatbrick.png", "metal_melter_heatbrick.png^multifurnace_intake_back.png", - "metal_melter_heatbrick.png^multifurnace_intake_face.png", - }, - groups = {cracky = 3, multifurnace = 1}, - paramtype2 = "facedir", - is_ground_content = false, -}) - -minetest.register_node("multifurnace:faucet", { - description = "Multifurnace Faucet", - tiles = {"metal_melter_heatbrick.png"}, - groups = {cracky = 3, multifurnace_accessory = 1}, - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - {-0.1875, -0.1875, 0.1250, 0.1875, -0.1250, 0.5000}, - {-0.1875, -0.1250, 0.1250, -0.1250, 0.06250, 0.5000}, - {0.1250, -0.1250, 0.1250, 0.1875, 0.06250, 0.5000} - } - }, - paramtype = "light", - paramtype2 = "facedir", - is_ground_content = false, - on_rightclick = faucet_activate, - on_timer = faucet_timer, -}) - --- Entity definitions - -minetest.register_entity("multifurnace:faucet_flow", { - initial_properties = { - physical = false, - collide_with_objects = false, - visual = "mesh", - mesh = "multifurnace_faucet_flow.obj", - visual_size = {x = 5, y = 5}, - textures = {}, - backface_culling = true, - pointable = false, - static_save = false, - }, - faucet = nil, - source = "", - timer = 0, - set_faucet = function (self, faucet) - self.faucet = faucet - - -- Set correct orientation in regards to the faucet - local node = minetest.get_node(faucet) - local orient = minetest.facedir_to_dir(node.param2) - orient = vector.multiply(orient, -1) - local angle = minetest.dir_to_yaw(orient) - self.object:set_yaw(angle) - end, - set_source = function (self, source) - self.source = source - - -- Set appropriate fluid texture - local tiles = minetest.registered_nodes[source] - if not tiles.liquid_alternative_flowing then return end - local flowing = minetest.registered_nodes[tiles.liquid_alternative_flowing] - if not flowing.tiles or type(flowing.tiles[1]) ~= "string" then return end - - self.object:set_properties({textures = { - flowing.tiles[1] - }}) - end, - on_step = function (self, dt) - self.timer = self.timer + 1 - - -- Remove self when the faucet is destroyed or is no longer marked as flowing - if self.timer >= 10 then - local node = minetest.get_node(self.faucet) - if not node or node.name ~= "multifurnace:faucet" then - self.object:remove() - return - end - - local meta = minetest.get_meta(self.faucet) - if meta:get_string("flowing") ~= self.source then - self.object:remove() - return - end - - self.timer = 0 - end - end -})