diff --git a/api/lava_furnace.lua b/api/lava_furnace.lua index e69de29..ac4e848 100644 --- a/api/lava_furnace.lua +++ b/api/lava_furnace.lua @@ -0,0 +1,365 @@ +local S = logistica.TRANSLATOR + +local META_LAVA_IN_TANK = "lavam" +local META_RUNNING_TIME = "cktm" +local META_LAST_ITEM = "lstitm" +local META_LAVA_USED = "ufuel" + +local get_meta = minetest.get_meta + +local BUCKET_LAVA = "bucket:bucket_lava" +local BUCKET_EMPTY = "bucket:bucket_empty" +local LAVA_UNIT = "logistica:lava_unit" + +local INV_FUEL = "fuel" +local INV_INPT = "src" +local INV_OUTP = "dst" +local INV_ADDI = "input" + +local UPDATE_INTERVAL = 1.0 + +-- returns the lava cap in milibuckets +local function get_lava_capacity(pos) + local nodeName = minetest.get_node(pos).name + local nodeDef = minetest.registered_nodes[nodeName] + if not nodeDef or not nodeDef.logistica or not nodeDef.logistica.lava_capacity then + return nil + end + return nodeDef.logistica.lava_capacity * 1000 +end + +local function fill_lava_tank_from_fuel(pos, meta, inv) + local itemstackName = inv:get_stack(INV_FUEL, 1):get_name() + if itemstackName ~= BUCKET_LAVA and itemstackName ~= LAVA_UNIT then return end + + local returnStack = ItemStack("") + if itemstackName == BUCKET_LAVA then + returnStack = ItemStack(BUCKET_EMPTY) + end + local currLevel = meta:get_int(META_LAVA_IN_TANK) + local cap = get_lava_capacity(pos) + if cap - currLevel < 1000 then return end + currLevel = currLevel + 1000 + meta:set_int(META_LAVA_IN_TANK, currLevel) + inv:set_stack(INV_FUEL, 1, returnStack) +end + +-- returns running time in secs +local function load_running_time(meta) + return meta:get_int(META_RUNNING_TIME) / 1000.0 +end + +-- newTime is in seconds +local function save_running_time(meta, newTime) + meta:set_int(META_RUNNING_TIME, math.floor(newTime * 1000)) +end + +local function is_new_item(meta, currItemStack) + local currStackName = currItemStack:get_name() + local lastItem = meta:get_string(META_LAST_ITEM) + meta:set_string(META_LAST_ITEM, currStackName) + return currStackName ~= lastItem +end + +local function get_running_time(meta, isNewItem) + if isNewItem then + save_running_time(meta, 0) + return 0 + else + return load_running_time(meta) + end +end + +-- returns nil if recipe for `currItemStack` cannot be fulfilled
+-- otherwise returns a table: +-- `{input = ItemStack, output = ItemStack, lava = #, additive = ItemStack, additive_use_chance = #, time = #}` +local function get_valid_config(meta, currItemStack) + local inv = meta:get_inventory() + local outputDef = logistica.get_lava_furnace_recipe_for(currItemStack:get_name()) + if not outputDef then return nil end + local inputStack = ItemStack(currItemStack) ; inputStack:set_count(outputDef.input_count) + local outputStack = ItemStack(outputDef.output) + local additiveStack = ItemStack(outputDef.additive or "") + if inv:contains_item(INV_INPT, inputStack) + and inv:contains_item(INV_ADDI, additiveStack) + and inv:room_for_item(INV_OUTP, outputStack) then + return { + input = inputStack, + output = outputStack, + lava = outputDef.lava, + additive = additiveStack, + additive_use_chance = outputDef.additive_use_chance, + time = outputDef.time + } + else + return nil + end +end + +local function save_lava_used(meta, amount) + meta:set_int(META_LAVA_USED, amount) +end + +local function get_lava_used_so_far(meta, isNewItem) + if isNewItem then + save_lava_used(meta, 0) + return 0 + else + return meta:get_int(META_LAVA_USED) + end +end + +-- returns nil if there isn't enough lava (and it won't use it) +-- otherwise uses the lava and returns the time left to completion (which may < 0 if we overshot) +local function useLava(meta, totalLavaUse, totalTime, runningTime, elapsed, isNewItem) + local lavaUsedSoFar = get_lava_used_so_far(meta, isNewItem) + local currAmount = meta:get_int(META_LAVA_IN_TANK) + local lavaUse = 0 + local remainigTime = totalTime - runningTime - elapsed + if remainigTime <= 0 then + lavaUse = math.max(0, totalLavaUse - lavaUsedSoFar) -- use up all that's left + else + lavaUse = math.round(totalLavaUse * elapsed / totalTime) + end + if currAmount - lavaUse < 0 then + return nil -- not enough lava in tank + end + meta:set_int(META_LAVA_IN_TANK, currAmount - lavaUse) + save_lava_used(meta, lavaUsedSoFar + lavaUse) + return remainigTime +end + +-------------------------------- +-- Formspec +-------------------------------- + +local function get_lava_img(currLava, lavaPercent) + local img = "" + if lavaPercent > 0 then + img = "image[0.4,1.4;1,3;logistica_lava_furnace_tank_bg.png^[lowpart:".. + lavaPercent..":logistica_lava_furnace_tank.png]" + else + img = "image[0.4,1.4;1,3;logistica_lava_furnace_tank_bg.png]" + end + return img.."tooltip[0.4,1.4;1,3;"..S("Remaminig: ")..(currLava/1000)..S(" Buckets").."]" +end + +local function common_formspec(pos, meta) + local currLava = meta:get_int(META_LAVA_IN_TANK) + local lavaCap = get_lava_capacity(pos) or 1 + local lavaPercent = math.round(currLava / lavaCap * 100) + return "formspec_version[6]".. + "size[10.5,11]".. + "list[current_player;main;0.4,5.9;8,4;0]".. + "list[context;fuel;0.4,4.5;1,1;0]".. + "list[context;src;2.2,2.3;1,1;0]".. + "list[context;dst;7.8,2.3;2,2;0]".. + "list[context;input;4.3,0.9;2,1;0]".. + "label[0.5,1.1;Lava]".. + "label[4.7,0.5;Additives]".. + get_lava_img(currLava, lavaPercent) +end + +local function get_inactive_formspec(pos, meta) + return common_formspec(pos, meta).. + "image[4,2.3;3,1;logistica_lava_furnace_arrow_bg.png^[transformR270]" +end + +local function get_active_formspec(pos, meta, runningTime, totalTime) + local timePercent = math.round(runningTime / totalTime * 100) + local progressImg = "" + if timePercent > 0 then + progressImg = "image[4,2.3;3,1;logistica_lava_furnace_arrow_bg.png^[lowpart:"..timePercent.. + ":logistica_lava_furnace_arrow.png^[transformR270]" + else + progressImg = "image[4,2.3;3,1;logistica_lava_furnace_arrow_bg.png^[transformR270]" + end + return common_formspec(pos, meta)..progressImg +end + +local function reset_furnace(pos, meta) + save_running_time(meta, 0) + save_lava_used(meta, 0) + meta:set_string("formspec", get_inactive_formspec(pos, meta)) + local node = minetest.get_node(pos) + local inactiveNodeName = string.gsub(node.name, "_active", "") + logistica.swap_node(pos, inactiveNodeName) +end + +local function set_furnace_active(pos, meta, runningTime, totalTime) + meta:set_string("formspec", get_active_formspec(pos, meta, runningTime, totalTime)) + local node = minetest.get_node(pos) + if not string.find(node.name, "_active") then + local activeNodeName = node.name.."_active" + logistica.swap_node(pos, activeNodeName) + end +end + +-------------------------------- +-- Callbacks +-------------------------------- + +local function lava_furnace_node_timer(pos, elapsed) + local meta = get_meta(pos) + local inv = meta:get_inventory() + fill_lava_tank_from_fuel(pos, meta, inv) + + repeat + local currItemStack = inv:get_stack(INV_INPT, 1) + local isNewItem = is_new_item(meta, currItemStack) + local runningTime = get_running_time(meta, isNewItem) + local config = get_valid_config(meta, currItemStack) + + if not config then -- invalid input, or not enough additives or space + reset_furnace(pos, meta) + return false + end + + + local timeLeft = useLava(meta, config.lava, config.time, runningTime, elapsed, isNewItem) + if timeLeft == nil then --not enough lava left + reset_furnace(pos, meta) + return false + end + if timeLeft <= 0 then + elapsed = -timeLeft -- becase we overshot the target time + end + + fill_lava_tank_from_fuel(pos, meta, inv) + if timeLeft <= 0 then + -- cook is ready + inv:remove_item(INV_INPT, config.input) + inv:add_item(INV_OUTP, config.output) + if config.additive + and config.additive_use_chance + and logistica.random_chance(config.additive_use_chance) then + inv:remove_item(INV_ADDI, config.additive) + end + save_lava_used(meta, 0) + save_running_time(meta, 0) + set_furnace_active(pos, meta, 0, config.time) + else -- we're still cooking, used entire elapsed time + runningTime = runningTime + elapsed + save_running_time(meta, runningTime) + elapsed = 0 + set_furnace_active(pos, meta, runningTime, config.time) + end + until (elapsed <= 0) + return true +end + +local function lava_furnace_on_construct(pos) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + inv:set_size(INV_INPT, 1) + inv:set_size(INV_FUEL, 1) + inv:set_size(INV_OUTP, 4) + inv:set_size(INV_ADDI, 2) + meta:set_string("formspec", get_inactive_formspec(pos, meta)) + lava_furnace_node_timer(pos, 0) +end + +local function lava_furnace_on_destruct(pos) + local meta = get_meta(pos) + local amount = meta:get_int(META_LAVA_IN_TANK) + amount = math.floor(amount / 1000) + if amount > 0 then + for i = 1, amount do + minetest.item_drop( + ItemStack("logistica:lava_unit"), + nil, + vector.add(pos, vector.new(math.random() - 0.5, 0.2, math.random() - 0.5)) + ) + end + end +end + +local function lava_furnace_can_dig(pos) + local inv = get_meta(pos):get_inventory() + return (inv:is_empty(INV_INPT) and inv:is_empty(INV_FUEL) + and inv:is_empty(INV_OUTP) and inv:is_empty(INV_ADDI)) +end + +local function lava_furnace_allow_metadata_inv_put(pos, listname, index, stack, player) + if listname == INV_INPT or listname == INV_ADDI then + return stack:get_count() + elseif listname == INV_FUEL + and (stack:get_name() == BUCKET_LAVA or stack:get_name() == LAVA_UNIT) then + return 1 + else + return 0 + end +end + +local function lava_furnace_allow_metadata_inv_take(pos, listname, index, stack, player) + return stack:get_count() +end + +local function lava_furnace_allow_metadata_inv_move(pos, from_list, from_index, to_list, to_index, count, player) + if to_list == INV_ADDI or to_list == INV_INPT then + return count + elseif to_list == INV_FUEL then + if get_meta(pos):get_inventory():get_stack(from_list, from_index):get_name() == BUCKET_LAVA then + return count + else + return 0 + end + else + return 0 + end +end + +local function lava_furnace_on_inv_change(pos) + -- local timer = minetest.get_node_timer(pos) + -- if not timer:is_started() and lava_furnace_node_timer(pos, 0) then + -- timer:start(UPDATE_INTERVAL) + -- end + logistica.start_node_timer(pos, UPDATE_INTERVAL) +end + +-------------------------------- +-- Public API +-------------------------------- + +--[[ +The Lava Furnace does not require nor does it connect to any networks - but it can still be used via injector/ +`desc`: item description
+`name`: lower case no spaces unique name
+`lavaCap`: Lava capacity, in buckets (min 1)
+`combinedTiles` - should have 2 entires: combinedTiles.inactive and combinedTiles.active
+]] +function logistica.register_lava_furnace(desc, name, lavaCapacity, combinedTiles) + local lname = name:gsub("%s", "_"):lower() + local def = { + description = S(desc), + tiles = combinedTiles.inactive, + paramtype2 = "facedir", + groups = { cracky= 2 }, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), + can_dig = lava_furnace_can_dig, + on_timer = lava_furnace_node_timer, + on_construct = lava_furnace_on_construct, + on_destruct = lava_furnace_on_destruct, + on_metadata_inventory_move = lava_furnace_on_inv_change, + on_metadata_inventory_put = lava_furnace_on_inv_change, + on_metadata_inventory_take = lava_furnace_on_inv_change, + allow_metadata_inventory_put = lava_furnace_allow_metadata_inv_put, + allow_metadata_inventory_move = lava_furnace_allow_metadata_inv_move, + allow_metadata_inventory_take = lava_furnace_allow_metadata_inv_take, + logistica = { + lava_capacity = lavaCapacity + } + } + + minetest.register_node("logistica:"..lname, def) + + local defActive = table.copy(def) + + defActive.tiles = combinedTiles.active + defActive.groups.not_in_creative_inventory = 1 + defActive.light_source = 9 + + minetest.register_node("logistica:"..lname.."_active", defActive) + +end diff --git a/api/lava_furnace_recipe.lua b/api/lava_furnace_recipe.lua index bd4e3c8..525434e 100644 --- a/api/lava_furnace_recipe.lua +++ b/api/lava_furnace_recipe.lua @@ -1,16 +1,20 @@ -logistica.lava_furance_recipes = {} +local lava_furance_recipes = {} +local NORMAL_COOK_LAVA_USAGE_PER_SEC = 5 -- in millibuckets +local NORMAL_COOK_REDUCTION_FACTOR = 2 +local MIN_TIME = 0.5 --[[ The Lava Furnace recipes format is indexed for item stack.
-`name`: The input item, count N is optional, and may be omitted (assumed 1)
+`name`: The input item `def`: A table in the following format: { + input_count = N, -- optional; how many of the input items are needed output = "item_name N", -- the result of the crystalization lava = 1000, -- how much lava is consumed. 1000 units = 1 bucket additive = "item_name N", -- optional; the additive that is required to be present for this recipe additive_use_chance = 100, -- optional; the chance that the additive will be consumed (0 = never, 100 = always) - time = 10, -- approximate time, in seconds, this recipe takes to complete, min 0.2 + time = 10, -- approximate time, in seconds, this recipe takes to complete, min is defined by MIN_TIME (or 1sec in practice) } ]] function logistica.register_lava_furnace_recipe(name, def) @@ -18,12 +22,36 @@ function logistica.register_lava_furnace_recipe(name, def) return end - local useChance = (def.additive ~= nil and logistica.clamp(def.additive, 0, 100)) or nil - logistica.lava_furance_recipes[name] = { + local useChance = (def.additive_use_chance ~= nil and logistica.clamp(def.additive_use_chance, 0, 100)) or 100 + lava_furance_recipes[name] = { + input_count = def.input_count or 1, output = def.output, - lava = math.max(1, def.output), + lava = math.max(1, def.lava), additive = def.additive, additive_use_chance = useChance, - time = math.max(0.2, def.time), + time = math.max(MIN_TIME, def.time or 1), } end + +function logistica.get_lava_furnace_recipe_for(itemName) + local preset = lava_furance_recipes[itemName] + if preset then return preset end + + -- else, try to adopt the real one + local output, decrOut = minetest.get_craft_result({ + method = "cooking", width = 1, items = { ItemStack(itemName) } + }) + + if output.time > 0 and decrOut.items[1]:is_empty() then + local lavaTime = math.max(MIN_TIME, output.time / NORMAL_COOK_REDUCTION_FACTOR) + return { + input_count = 1, + output = output.item:to_string(), + lava = lavaTime * NORMAL_COOK_LAVA_USAGE_PER_SEC, + time = lavaTime + } + end + + -- nothing found + return nil +end diff --git a/item/craft_item.lua b/item/craft_item.lua index 34e6fa7..76aedc8 100644 --- a/item/craft_item.lua +++ b/item/craft_item.lua @@ -3,6 +3,12 @@ local S = logistica.TRANSLATOR logistica.craftitem.general = {} local items = logistica.craftitem.general +items["logistica:lava_unit"] = { + description = S("A Unit of Lava\nUse in Lava Furnace or with Empty Bucket"), + inventory_image = "logistica_lava_unit.png", + stack_max = 1, +} + items["logistica:silverin_slice"] = { description = S("Silverin Slice"), inventory_image = "logistica_silverin_slice.png", diff --git a/registration/crystalizer_recipes.lua b/registration/crystalizer_recipes.lua deleted file mode 100644 index e69de29..0000000 diff --git a/registration/lava_furnace_recipes.lua b/registration/lava_furnace_recipes.lua new file mode 100644 index 0000000..9cafa02 --- /dev/null +++ b/registration/lava_furnace_recipes.lua @@ -0,0 +1,8 @@ + +logistica.register_lava_furnace_recipe("default:silver_sand", { + output = "logistica:silverin", + lava = 50, + additive = "default:ice", + additive_use_chance = 90, + time = 5 +}) diff --git a/registration/nodes.lua b/registration/nodes.lua index 0683195..06d7721 100644 --- a/registration/nodes.lua +++ b/registration/nodes.lua @@ -94,7 +94,7 @@ logistica.register_item_storage("Tool Box\nStores Tools Only", "simple", { -------------------------------- logistica.register_mass_storage("basic", "Mass Storage", 8, 1024, 4, { - "logistica_basic_mass_storage.png", "logistica_basic_mass_storage.png", + "logistica_basic_mass_storage_top.png", "logistica_basic_mass_storage_top.png", "logistica_basic_mass_storage.png", "logistica_basic_mass_storage.png", "logistica_basic_mass_storage.png", "logistica_basic_mass_storage_front.png" }) @@ -132,3 +132,25 @@ logistica.register_supplier("Passive Supplier Chest", "simple", 16, { -- Lava Furnace -------------------------------- +logistica.register_lava_furnace("Lava Furnace", "lava_furnace", 4, { + inactive = { + "logistica_lava_furnace_side.png", "logistica_lava_furnace_side.png", + "logistica_lava_furnace_side.png", "logistica_lava_furnace_side.png", + "logistica_lava_furnace_side.png", "logistica_lava_furnace_front_off.png" + }, + active = { + "logistica_lava_furnace_side.png", "logistica_lava_furnace_side.png", + "logistica_lava_furnace_side.png", "logistica_lava_furnace_side.png", + "logistica_lava_furnace_side.png", + { + image = "logistica_lava_furnace_front_on_anim.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 1.5 + }, + } + } +}) diff --git a/registration/registration.lua b/registration/registration.lua index 89cc2e9..03208bb 100644 --- a/registration/registration.lua +++ b/registration/registration.lua @@ -1,4 +1,4 @@ local path = logistica.MODPATH .. "/registration" -- once again, order is important dofile(path.."/nodes.lua") -dofile(path.."/crystalizer_recipes.lua") +dofile(path.."/lava_furnace_recipes.lua") diff --git a/textures/logistica_basic_mass_storage.png b/textures/logistica_basic_mass_storage.png index 3e5029f..c7a1fb8 100644 Binary files a/textures/logistica_basic_mass_storage.png and b/textures/logistica_basic_mass_storage.png differ diff --git a/textures/logistica_basic_mass_storage_front.png b/textures/logistica_basic_mass_storage_front.png index 7c041d5..6fff35b 100644 Binary files a/textures/logistica_basic_mass_storage_front.png and b/textures/logistica_basic_mass_storage_front.png differ diff --git a/textures/logistica_basic_mass_storage_top.png b/textures/logistica_basic_mass_storage_top.png new file mode 100644 index 0000000..1eee3bb Binary files /dev/null and b/textures/logistica_basic_mass_storage_top.png differ diff --git a/textures/logistica_lava_furnace_arrow.png b/textures/logistica_lava_furnace_arrow.png new file mode 100644 index 0000000..a5495af Binary files /dev/null and b/textures/logistica_lava_furnace_arrow.png differ diff --git a/textures/logistica_lava_furnace_arrow_bg.png b/textures/logistica_lava_furnace_arrow_bg.png new file mode 100644 index 0000000..2fbbf21 Binary files /dev/null and b/textures/logistica_lava_furnace_arrow_bg.png differ diff --git a/textures/logistica_lava_furnace_front_off.png b/textures/logistica_lava_furnace_front_off.png new file mode 100644 index 0000000..8eea8d6 Binary files /dev/null and b/textures/logistica_lava_furnace_front_off.png differ diff --git a/textures/logistica_lava_furnace_front_on_anim.png b/textures/logistica_lava_furnace_front_on_anim.png new file mode 100644 index 0000000..0907ea6 Binary files /dev/null and b/textures/logistica_lava_furnace_front_on_anim.png differ diff --git a/textures/logistica_lava_furnace_side.png b/textures/logistica_lava_furnace_side.png new file mode 100644 index 0000000..cdea5ac Binary files /dev/null and b/textures/logistica_lava_furnace_side.png differ diff --git a/textures/logistica_lava_furnace_tank.png b/textures/logistica_lava_furnace_tank.png new file mode 100644 index 0000000..a550abf Binary files /dev/null and b/textures/logistica_lava_furnace_tank.png differ diff --git a/textures/logistica_lava_furnace_tank_bg.png b/textures/logistica_lava_furnace_tank_bg.png new file mode 100644 index 0000000..844cd9e Binary files /dev/null and b/textures/logistica_lava_furnace_tank_bg.png differ diff --git a/textures/logistica_lava_unit.png b/textures/logistica_lava_unit.png new file mode 100644 index 0000000..acc432b Binary files /dev/null and b/textures/logistica_lava_unit.png differ diff --git a/util/common.lua b/util/common.lua index aa19af4..97d1cf5 100644 --- a/util/common.lua +++ b/util/common.lua @@ -190,4 +190,10 @@ end function logistica.table_is_empty(table) return table == nil or (next(table) == nil) -end \ No newline at end of file +end + +-- returns true a given percentage of the time +function logistica.random_chance(percent) + return percent >= math.random(1, 100) +end +