logistica-cd2025/api/lava_furnace.lua

398 lines
13 KiB
Lua

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 LAVA_UNIT = "logistica:lava_unit"
local INV_FUEL = "fuel"
local INV_INPT = "src"
local INV_OUTP = "dst"
local INV_ADDI = "input"
local GUIDE_BTN = "guide"
local UPDATE_INTERVAL = 1.0
local function is_lava_bucket(itemstackName)
return logistica.reservoir_get_full_buckets_for_liquid(logistica.liquids.lava)[itemstackName] ~= nil
end
local function fill_lava_tank_from_fuel(pos, meta, inv)
local itemstackName = inv:get_stack(INV_FUEL, 1):get_name()
if not is_lava_bucket(itemstackName) and itemstackName ~= LAVA_UNIT then return end
local returnStack = logistica.reservoir_get_empty_bucket_for_full_bucket(itemstackName) or ItemStack("")
local currLevel = meta:get_int(META_LAVA_IN_TANK)
local cap = logistica.lava_furnace_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<br>
-- 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 outputDefs = logistica.get_lava_furnace_recipes_for(currItemStack:get_name())
if not outputDefs then return nil end
for _, outputDef in ipairs(outputDefs) do
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
}
end
end
return nil
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 = logistica.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("Remaining: ")..(currLava/1000)..S(" Buckets").."]"
end
local function common_formspec(pos, meta)
local currLava = meta:get_int(META_LAVA_IN_TANK)
local lavaCap = logistica.lava_furnace_get_lava_capacity(pos) or 1
local lavaPercent = logistica.round(currLava / lavaCap * 100)
return "formspec_version[4]"..
"size["..logistica.inv_size(10.5, 11.25).."]" ..
logistica.ui.background_lava_furnace..
"listcolors[#00000069;#5A5A5A;#141318;#30434C;#FFF]"..
logistica.player_inv_formspec(0.5, 5.9)..
"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.2,0.5;Additives]"..
"label[2.0,4.8;"..S("Crafts unique recipes (click ? button in top right)").."]"..
"label[2.0,5.2;"..S("and can also cook regular recipes at twice the speed").."]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]"..
"button[9.2,0.4;0.8,0.8;"..GUIDE_BTN..";?]"..
"tooltip["..GUIDE_BTN..";"..S("Recipes").."]"..
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 = logistica.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, 1)
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 minetest.is_protected(pos, player:get_player_name()) then return 0 end
local stackName = stack:get_name()
if listname == INV_INPT or listname == INV_ADDI then
return stack:get_count()
elseif listname == INV_FUEL
and (is_lava_bucket(stackName) or stackName == LAVA_UNIT) then
return 1
else
return 0
end
end
local function lava_furnace_allow_metadata_inv_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then return 0 end
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 minetest.is_protected(pos, player:get_player_name()) then return 0 end
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)
logistica.start_node_timer(pos, UPDATE_INTERVAL)
end
local function lava_furnace_receive_fields(pos, formname, fields, sender)
if fields[GUIDE_BTN] and sender and sender:is_player() then
logistica.lava_furnace_show_guide(sender:get_player_name())
end
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<br>
`name`: lower case no spaces unique name<br>
`lavaCap`: Lava capacity, in buckets (min 1)<br>
`combinedTiles` - should have 2 entires: combinedTiles.inactive and combinedTiles.active<br>
]]
function logistica.register_lava_furnace(desc, name, lavaCapacity, combinedTiles)
local lname = name:gsub("%s", "_"):lower()
local def = {
description = S(desc),
drawtype = "nodebox",
tiles = combinedTiles.inactive,
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, -7/16, 5/16, -7/16},
{ 7/16, -8/16, -8/16, 8/16, 5/16, -7/16},
{-8/16, -8/16, -8/16, 8/16, -7/16, -7/16},
{-8/16, -1/16, -8/16, 8/16, 1/16, -7/16},
{-8/16, 5/16, -8/16, -6/16, 8/16, -7/16},
{ 6/16, 5/16, -8/16, 8/16, 8/16, -7/16},
{-6/16, 6/16, -8/16, -5/16, 8/16, -7/16},
{ 5/16, 6/16, -8/16, 6/16, 8/16, -7/16},
{-5/16, 7/16, -8/16, 5/16, 8/16, -7/16},
{-8/16, -8/16, -7/16, 8/16, 8/16, 8/16},
}
},
selection_box = { type = "normal" },
paramtype2 = "facedir",
groups = { cracky= 2, pickaxey = 2, },
is_ground_content = false,
sounds = logistica.sound_mod.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,
on_receive_fields = lava_furnace_receive_fields,
logistica = {
lava_capacity = lavaCapacity,
lava_furnace = true,
},
_mcl_hardness = 3,
_mcl_blast_resistance = 15
}
local lavaFurnaceName = "logistica:"..lname
minetest.register_node(lavaFurnaceName, def)
logistica.register_non_pushable(lavaFurnaceName)
local defActive = table.copy(def)
defActive.tiles = combinedTiles.active
defActive.groups.not_in_creative_inventory = 1
defActive.light_source = 9
minetest.register_node(lavaFurnaceName.."_active", defActive)
end