Michal Cieslakiewicz dc01c6cd0d biogasmachines: freezer: make code compatible with Tubelib v2.
Code updated to make machine fully compatible with Tubelib2 framework.

Major changes:

* supports Tubelib2 by using NodeStates class and new metadata
* BLOCKED state introduced when no space left in output tray
* set to FAULT when Biogas tank is empty
* on_punch diagnostics removed

Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
2019-01-26 15:54:41 +01:00

695 lines
21 KiB
Lua

--[[
=======================================================================
Tubelib Biogas Machines Mod
by Micu (c) 2018, 2019
Copyright (C) 2018, 2019 Michal Cieslakiewicz
Freezer - Biogas-powered machine that converts water to ice
(technically Biogas is a coolant here, not fuel, however it
is 'used' in the same way). Device changes water in bucket
to ice and empty bucket. If pipeworks mod is installed and pipe
with water is connected to device, in absence of water buckets
it produces ice from water supplied via pipes at the same rate.
Device automatically shuts off when there is nothing to freeze,
so Biogas is not wasted. However when machine is powered off via
button while item is being frozen, Biogas used to partially cool
down water is lost.
Internal water valve works only when device runs, so in off state
water pipe detector is also off. This is a design feature to save
CPU resources for inactive machine (timer is stopped).
Operational info:
* machine checks for buckets with water first and if there are none
tries to take water from pipeline
* when fuel ends, machine automatically enters fault mode and has to
be manually powered on again after refilling Biogas
* fault condition informs operator that there is no fuel in running
machine; when Biogas tank is emptied manually after machine enters
standby or blocked state nothing happens as device is not operating
* if output tray is full and no new items can be put there, freezer
changes state to blocked (special standby mode); it will resume
work as soon as there is space in output inventory
* if there is nothing to freeze but there is still Biogas in tank,
machine enters standby mode; it will automatically pick up work
as soon as any water source becomes available again; when Biogas
tank is also exhausted in the process, machine signals fault
instead to notify operator than it will not be able to resume work
* there is 1 tick gap between items to unload ice and load internal
water tank or freezing tray; this a design choice to make timer
callback run faster
* Biogas is used only when device actually freezes
* partially frozen items (due to work being interrupted by on/off
switch) have to be frozen again from beginning, Biogas used for
such partial freeze is not recoverable
* bucket freezing tray cannot be emptied manually when machine is
running, stop the device to take water bucket; please note that
tray cannot be loaded manually, please use input inventory
* when using pipes, internal water tank is filled completely before
process starts; shutting down water source during freezing does
not stop it
* machine cannot be recovered unless input, output and fuel trays
are all empty
Tubelib v2 implementation info:
* device updates itself every tick, so cycle_time must be set to 1
even though production takes longer (start method sets timer to
this value)
* keep_running function is called every time item is produced
(not every processing tick - function does not accept neither 0
nor fractional values for num_items parameter)
License: LGPLv2.1+
=======================================================================
]]--
--[[
---------
Variables
---------
]]--
-- timing
local BIOGAS_TICKS = 24 -- Biogas work time
local ICE_TICKS = 4 -- Ice creation time
local TIMER_TICK_SEC = 1 -- Node timer tick
local STANDBY_TICKS = 4 -- Standby mode timer frequency factor
local COUNTDOWN_TICKS = 4 -- Ticks to standby
-- machine inventory
local INV_H = 3 -- Inventory height
local INV_IN_W = 4 -- Input inventory width
local INV_OUT_W = (6 - INV_IN_W) -- Output inventory width
-- item processed
local SOURCE_EMPTY = 0
local SOURCE_BUCKET = 1
local SOURCE_PIPE = 2
-- water sources
local water_bucket = { ["bucket:bucket_water"] = true,
["bucket:bucket_river_water"] = true }
--[[
--------
Formspec
--------
]]--
-- static data for formspec
local fmxy = {
inv_h = tostring(INV_H),
inv_in_w = tostring(INV_IN_W),
mid_x = tostring(INV_IN_W + 1),
inv_out_w = tostring(INV_OUT_W),
inv_out_x = tostring(INV_IN_W + 2),
biogas_time = tostring(BIOGAS_TICKS * TIMER_TICK_SEC),
ice_time = tostring(ICE_TICKS * TIMER_TICK_SEC),
ice_qty = tostring(BIOGAS_TICKS / ICE_TICKS)
}
-- formspec
local function formspec(self, pos, meta)
local state = meta:get_int("tubelib_state")
local source = meta:get_int("source")
local fuel_pct = tostring(100 * meta:get_int("fuel_ticks") / BIOGAS_TICKS)
local item_pct = tostring(100 * (ICE_TICKS - meta:get_int("item_ticks")) / ICE_TICKS)
return "size[8,8.25]" ..
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
"list[context;src;0,0;" .. fmxy.inv_in_w .. "," .. fmxy.inv_h .. ";]" ..
"item_image[0,0;1,1;bucket:bucket_water]" ..
"list[context;cur;" .. fmxy.inv_in_w .. ",0;1,1;]" ..
"image[" .. fmxy.mid_x .. ",0;1,1;biogasmachines_freezer_pipe_inv_" ..
(source == SOURCE_PIPE and "fg" or "bg") .. ".png]" ..
"image[" .. fmxy.inv_in_w ..
",1;1,1;biogasmachines_freezer_inv_bg.png^[lowpart:" ..
fuel_pct .. ":biogasmachines_freezer_inv_fg.png]" ..
"image[" .. fmxy.mid_x .. ",1;1,1;gui_furnace_arrow_bg.png^[lowpart:" ..
item_pct .. ":gui_furnace_arrow_fg.png^[transformR270]" ..
"list[context;fuel;" .. fmxy.inv_in_w .. ",2;1,1;]" ..
"item_image[" .. fmxy.inv_in_w .. ",2;1,1;tubelib_addons1:biogas]" ..
"image_button[" .. fmxy.mid_x .. ",2;1,1;" ..
self:get_state_button_image(meta) .. ";state_button;]" ..
"item_image[1,3.25;0.5,0.5;tubelib_addons1:biogas]" ..
"label[1.5,3.25;= " .. fmxy.biogas_time .. " sec]" ..
"item_image[3,3.25;0.5,0.5;default:ice]" ..
"label[3.5,3.25;= " .. fmxy.ice_time .. " sec]" ..
"item_image[5.25,3.25;0.5,0.5;tubelib_addons1:biogas]" ..
"image[5.75,3.25;0.5,0.5;tubelib_gui_arrow.png^[resize:16x16]" ..
"item_image[6.25,3.25;0.5,0.5;default:ice]" ..
"label[6.75,3.25;x " .. fmxy.ice_qty .. "]" ..
"item_image[" .. fmxy.inv_out_x .. ",0;1,1;default:ice]" ..
"list[context;dst;" .. fmxy.inv_out_x .. ",0;" .. fmxy.inv_out_w ..
"," .. fmxy.inv_h .. ";]" ..
"list[current_player;main;0,4;8,1;]" ..
"list[current_player;main;0,5.25;8,3;8]" ..
"listring[context;dst]" ..
"listring[current_player;main]" ..
"listring[context;src]" ..
"listring[current_player;main]" ..
"listring[context;fuel]" ..
"listring[current_player;main]" ..
(state == tubelib.RUNNING and source ~= SOURCE_PIPE and
"box[" .. fmxy.inv_in_w .. ",0;0.82,0.9;#2F4FBF]" or
"listring[context;cur]listring[current_player;main]") ..
default.get_hotbar_bg(0, 4)
end
--[[
-------
Helpers
-------
]]--
-- get bucket with water (itemstack)
local function get_water_bucket(inv, listname)
local stack = ItemStack({})
for i, _ in pairs(water_bucket) do
stack = inv:remove_item(listname, ItemStack(i .. " 1"))
if not stack:is_empty() then break end
end
return stack
end
-- reset processing data
local function state_meta_reset(pos, meta, oldstate)
meta:set_int("source", SOURCE_EMPTY)
meta:set_int("item_ticks", ICE_TICKS)
end
--[[
-------------
State machine
-------------
]]--
local machine = tubelib.NodeStates:new({
node_name_passive = "biogasmachines:freezer",
node_name_active = "biogasmachines:freezer_active",
node_name_defect = "biogasmachines:freezer_defect",
infotext_name = "Tubelib Water Freezer",
cycle_time = TIMER_TICK_SEC,
standby_ticks = STANDBY_TICKS,
has_item_meter = true,
aging_factor = 8,
on_start = state_meta_reset,
on_stop = state_meta_reset,
formspec_func = formspec,
})
-- customized version of NodeStates:idle()
local function countdown_to_halt(pos, meta, target_state)
if target_state ~= tubelib.STANDBY and
target_state ~= tubelib.BLOCKED and
target_state ~= tubelib.STOPPED and
target_state ~= tubelib.FAULT then
return true
end
local countdown = meta:get_int("tubelib_countdown")
if countdown > 0 then
countdown = countdown - 1
meta:set_int("tubelib_countdown", countdown)
if countdown == 0 then
if target_state == tubelib.FAULT then
state_meta_reset(pos, meta)
machine:fault(pos, meta)
elseif target_state == tubelib.STOPPED then
machine:stop(pos, meta)
elseif target_state == tubelib.BLOCKED then
machine:blocked(pos, meta)
else
machine:standby(pos, meta)
end
return false
end
end
return true
end
--[[
---------
Callbacks
---------
]]--
-- do not allow to dig protected or non-empty machine
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
return inv:is_empty("src") and inv:is_empty("dst")
and inv:is_empty("fuel")
end
-- cleanup after digging
local function after_dig_node(pos, oldnode, oldmetadata, digger)
tubelib.remove_node(pos)
if minetest.get_modpath("pipeworks") then
pipeworks.scan_for_pipe_objects(pos)
end
end
-- init machine after placement
local function after_place_node(pos, placer, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('src', INV_H * INV_IN_W)
inv:set_size('cur', 1)
inv:set_size('fuel', 1)
inv:set_size('dst', INV_H * INV_OUT_W)
meta:set_string("owner", placer:get_player_name())
meta:set_int("fuel_ticks", 0)
state_meta_reset(pos, meta)
local number = tubelib.add_node(pos, "biogasmachines:freezer")
machine:node_init(pos, number)
if minetest.get_modpath("pipeworks") then
pipeworks.scan_for_pipe_objects(pos)
end
end
-- validate incoming items
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "src" then
if water_bucket[stack:get_name()] then
return stack:get_count()
else
return 0
end
elseif listname == "cur" or listname == "dst" then
return 0
elseif listname == "fuel" then
if stack:get_name() == "tubelib_addons1:biogas" then
return stack:get_count()
else
return 0
end
end
return 0
end
-- validate items move
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
if to_list == "cur" or
(from_list == "cur" and machine:get_state(meta) == tubelib.RUNNING) then
return 0
end
local inv = meta:get_inventory()
local stack = inv:get_stack(from_list, from_index)
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end
-- validate items retrieval
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "cur" then
local meta = minetest.get_meta(pos)
if machine:get_state(meta) == tubelib.RUNNING then
return 0
end
end
return stack:get_count()
end
-- formspec callback
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
machine:state_button_event(pos, fields)
end
-- tick-based item production
local function on_timer(pos, elapsed)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local label = minetest.registered_nodes[node.name].description
local number = meta:get_string("tubelib_number")
local source = meta:get_int("source")
local fuel = meta:get_int("fuel_ticks")
if fuel == 0 and inv:is_empty("fuel") and
machine:get_state(meta) == tubelib.RUNNING then
-- no fuel - no work
return countdown_to_halt(pos, meta, tubelib.FAULT)
end
local pipe = source == SOURCE_PIPE
if source == SOURCE_EMPTY then
-- try to start freezing bucket or water from pipe
pipe = biogasmachines.is_pipe_with_water(pos, node)
local output = { ItemStack("default:ice 1") }
if not inv:is_empty("cur") or not inv:is_empty("src") then
-- source: water bucket
source = SOURCE_BUCKET
pipe = false
output[#output + 1] = ItemStack("bucket:bucket_empty 1")
elseif pipe then
-- source: water pipe
source = SOURCE_PIPE
else
-- no source, count towards standby
return countdown_to_halt(pos, meta, tubelib.STANDBY)
end
if machine:get_state(meta) == tubelib.STANDBY then
-- something to do, wake up and re-entry
machine:start(pos, meta, true)
return false
end
if inv:is_empty("cur") then
-- check if there is space in output
for _, stack in ipairs(output) do
if not inv:room_for_item("dst", stack) then
return countdown_to_halt(pos, meta, tubelib.BLOCKED)
end
end
if machine:get_state(meta) == tubelib.BLOCKED then
-- new free output slots, wake up and re-entry
machine:start(pos, meta, true)
return false
end
-- process another water unit
if source == SOURCE_BUCKET then
local inp = get_water_bucket(inv, "src")
if inp:is_empty() then
-- oops
state_meta_reset(pos, meta)
machine:fault(pos, meta)
return false
end
inv:add_item("cur", inp)
end
end
meta:set_int("source", source)
meta:set_int("item_ticks", ICE_TICKS)
else
-- continue freezing process
if machine:get_state(meta) ~= tubelib.RUNNING then
-- exception, should not happen - oops
state_meta_reset(pos, meta)
machine:fault(pos, meta)
return false
end
-- add item tick
local itemcnt = meta:get_int("item_ticks") - 1
if itemcnt == 0 then
inv:add_item("dst", ItemStack("default:ice 1"))
if source == SOURCE_BUCKET then
inv:set_stack("cur", 1, ItemStack({}))
inv:add_item("dst", ItemStack("bucket:bucket_empty 1"))
end
state_meta_reset(pos, meta)
-- item produced, increase aging
machine:keep_running(pos, meta, COUNTDOWN_TICKS, 1)
else
meta:set_int("item_ticks", itemcnt)
end
-- consume fuel tick
if fuel == 0 then
if not inv:is_empty("fuel") then
inv:remove_item("fuel",
ItemStack("tubelib_addons1:biogas 1"))
fuel = BIOGAS_TICKS
else
-- oops
state_meta_reset(pos, meta)
machine:fault(pos, meta)
return false
end
end
meta:set_int("fuel_ticks", fuel - 1)
end
meta:set_int("tubelib_countdown", COUNTDOWN_TICKS)
meta:set_string("infotext", label .. " " .. number .. ": running (water " ..
(pipe and "from pipe" or "in buckets") .. ")")
meta:set_string("formspec", formspec(machine, pos, meta))
return true
end
--[[
-----------------
Node registration
-----------------
]]--
minetest.register_node("biogasmachines:freezer", {
description = "Tubelib Water Freezer",
tiles = {
-- up, down, right, left, back, front
"biogasmachines_freezer_top.png",
"biogasmachines_freezer_bottom.png",
"biogasmachines_freezer_side.png",
"biogasmachines_freezer_side.png",
"biogasmachines_freezer_side.png",
"biogasmachines_freezer_side.png"
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.375, -0.5, 0.5, 0.5, 0.5 },
{ -0.5, -0.5, -0.5, -0.375, -0.375, -0.375 },
{ 0.375, -0.5, -0.5, 0.5, -0.375, -0.375 },
{ 0.375, -0.5, 0.375, 0.5, -0.375, 0.5 },
{ -0.5, -0.5, 0.375, -0.375, -0.375, 0.5 },
}
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 },
},
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = { choppy = 2, cracky = 2, crumbly = 2 },
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
pipe_connections = { top = 1,
bottom = 1,
front = 1,
back = 1,
left = 1,
right = 1 },
drop = "",
can_dig = can_dig,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
machine:after_dig_node(pos, oldnode, oldmetadata, digger)
after_dig_node(pos, oldnode, oldmetadata, digger)
end,
on_rotate = screwdriver.disallow,
on_timer = on_timer,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
after_place_node = after_place_node,
})
minetest.register_node("biogasmachines:freezer_active", {
description = "Tubelib Water Freezer",
tiles = {
-- up, down, right, left, back, front
{
image = "biogasmachines_freezer_active_top.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 4.0,
},
},
"biogasmachines_freezer_bottom.png",
"biogasmachines_freezer_side.png",
"biogasmachines_freezer_side.png",
"biogasmachines_freezer_side.png",
"biogasmachines_freezer_side.png"
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.375, -0.5, 0.5, 0.5, 0.5 },
{ -0.5, -0.5, -0.5, -0.375, -0.375, -0.375 },
{ 0.375, -0.5, -0.5, 0.5, -0.375, -0.375 },
{ 0.375, -0.5, 0.375, 0.5, -0.375, 0.5 },
{ -0.5, -0.5, 0.375, -0.375, -0.375, 0.5 },
}
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 },
},
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = { crumbly = 0, not_in_creative_inventory = 1 },
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
pipe_connections = { top = 1,
bottom = 1,
front = 1,
back = 1,
left = 1,
right = 1 },
drop = "",
can_dig = can_dig,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
machine:after_dig_node(pos, oldnode, oldmetadata, digger)
after_dig_node(pos, oldnode, oldmetadata, digger)
end,
on_rotate = screwdriver.disallow,
on_timer = on_timer,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
})
minetest.register_node("biogasmachines:freezer_defect", {
description = "Tubelib Water Freezer",
tiles = {
-- up, down, right, left, back, front
"biogasmachines_freezer_top.png",
"biogasmachines_freezer_bottom.png",
"biogasmachines_freezer_side.png^tubelib_defect.png",
"biogasmachines_freezer_side.png^tubelib_defect.png",
"biogasmachines_freezer_side.png^tubelib_defect.png",
"biogasmachines_freezer_side.png^tubelib_defect.png"
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.375, -0.5, 0.5, 0.5, 0.5 },
{ -0.5, -0.5, -0.5, -0.375, -0.375, -0.375 },
{ 0.375, -0.5, -0.5, 0.5, -0.375, -0.375 },
{ 0.375, -0.5, 0.375, 0.5, -0.375, 0.5 },
{ -0.5, -0.5, 0.375, -0.375, -0.375, 0.5 },
}
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 },
},
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = { choppy = 2, cracky = 2, crumbly = 2, not_in_creative_inventory = 1 },
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
pipe_connections = { top = 1,
bottom = 1,
front = 1,
back = 1,
left = 1,
right = 1 },
can_dig = can_dig,
after_dig_node = after_dig_node,
on_rotate = screwdriver.disallow,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
after_place_node = function(pos, placer, itemstack, pointed_thing)
after_place_node(pos, placer, itemstack, pointed_thing)
machine:defect(pos, minetest.get_meta(pos))
end,
})
tubelib.register_node("biogasmachines:freezer",
{ "biogasmachines:freezer_active", "biogasmachines:freezer_defect" }, {
on_push_item = function(pos, side, item)
local meta = minetest.get_meta(pos)
if water_bucket[item:get_name()] then
return tubelib.put_item(meta, "src", item)
elseif item:get_name() == "tubelib_addons1:biogas" then
return tubelib.put_item(meta, "fuel", item)
end
return false
end,
on_pull_item = function(pos, side)
local meta = minetest.get_meta(pos)
return tubelib.get_item(meta, "dst")
end,
on_unpull_item = function(pos, side, item)
local meta = minetest.get_meta(pos)
return tubelib.put_item(meta, "dst", item)
end,
on_recv_message = function(pos, topic, payload)
local meta = minetest.get_meta(pos)
if topic == "fuel" then
return tubelib.fuelstate(meta, "fuel")
end
local resp = machine:on_receive_message(pos, topic, payload)
if resp then
return resp
else
return "unsupported"
end
end,
on_node_load = function(pos)
machine:on_node_load(pos)
end,
on_node_repair = function(pos)
return machine:on_node_repair(pos)
end,
})
--[[
--------
Crafting
--------
]]--
minetest.register_craft({
output = "biogasmachines:freezer",
recipe = {
{ "default:steelblock", "default:glass", "default:steelblock" },
{ "default:mese_crystal", "bucket:bucket_empty", "tubelib:tube1" },
{ "group:wood", "default:copper_ingot", "group:wood" },
},
})
if minetest.get_modpath("unified_inventory") and unified_inventory then
unified_inventory.register_craft_type("freezing", {
description = "Freezing",
icon = 'biogasmachines_freezer_inv_fg.png',
width = 1,
height = 1,
})
for i, _ in pairs(water_bucket) do
unified_inventory.register_craft({
type = "freezing",
items = { i },
output = "default:ice",
})
end
end