Michal Cieslakiewicz 300ef43afa biogasmachines: get rid of public is_member_of() function.
biogasmachines.is_member_of() function replaced with sparse arrays.

Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
2019-01-15 19:24:09 +01:00

656 lines
20 KiB
Lua

--[[
=======================================================================
Tubelib Biogas Machines Mod
by Micu (c) 2018
Copyright (C) 2018 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 switches off automatically and has to
be powered on again manually after refilling Biogas
* if there is nothing to freeze, machine enters standby mode; it
will automatically pick up work as soon as any water source becomes
available again
* 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
License: LGPLv2.1+
=======================================================================
]]--
--[[
---------
Variables
---------
]]--
-- timing
local BIOGAS_TIME_SEC = 24 -- Biogas work time
local ICE_TIME_SEC = 4 -- Ice creation time
local TIMER_TICK_SEC = 1 -- Node timer tick
local TICKS_TO_SLEEP = 5 -- Tubelib 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_TIME_SEC * TIMER_TICK_SEC),
ice_time = tostring(ICE_TIME_SEC * TIMER_TICK_SEC),
ice_qty = tostring(BIOGAS_TIME_SEC / ICE_TIME_SEC)
}
-- Parameters:
-- state - tubelib state
-- water_pipe - water from pipeworks (bool)
-- fuel_percent - biogas used
-- item_percent - ice completion
-- show_icons - show image hints (bool)
local function formspec(state, water_pipe, fuel_percent, item_percent, show_icons)
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 .. ";]" ..
(show_icons and "item_image[0,0;1,1;bucket:bucket_water]" or "") ..
"list[context;cur;" .. fmxy.inv_in_w .. ",0;1,1;]" ..
"image[" .. fmxy.mid_x .. ",0;1,1;biogasmachines_freezer_pipe_inv_" ..
(water_pipe and "fg" or "bg") .. ".png]" ..
"image[" .. fmxy.inv_in_w ..
",1;1,1;biogasmachines_freezer_inv_bg.png^[lowpart:" ..
tostring(fuel_percent) ..
":biogasmachines_freezer_inv_fg.png]" ..
"image[" .. fmxy.mid_x .. ",1;1,1;gui_furnace_arrow_bg.png^[lowpart:" ..
tostring(item_percent) ..
":gui_furnace_arrow_fg.png^[transformR270]" ..
"list[context;fuel;" .. fmxy.inv_in_w .. ",2;1,1;]" ..
(show_icons and "item_image[" .. fmxy.inv_in_w ..
",2;1,1;tubelib_addons1:biogas]" or "") ..
"image_button[" .. fmxy.mid_x .. ",2;1,1;" ..
tubelib.state_button(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 .. "]" ..
(show_icons and "item_image[" .. fmxy.inv_out_x ..
",0;1,1;default:ice]" or "") ..
"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 not water_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
local function freezer_start(pos)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
local fuel = meta:get_int("fuel_ticks")
local label = minetest.registered_nodes[node.name].description
meta:set_int("source", SOURCE_EMPTY)
meta:set_int("item_ticks", ICE_TIME_SEC)
meta:set_int("running", TICKS_TO_SLEEP)
meta:set_string("infotext", label .. " " .. number .. ": running")
meta:set_string("formspec", formspec(tubelib.RUNNING, false,
100 * fuel / BIOGAS_TIME_SEC, 0, true))
node.name = "biogasmachines:freezer_active"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):start(TIMER_TICK_SEC)
return false
end
local function freezer_stop(pos)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
local fuel = meta:get_int("fuel_ticks")
local label = minetest.registered_nodes[node.name].description
meta:set_int("source", SOURCE_EMPTY)
meta:set_int("item_ticks", ICE_TIME_SEC)
meta:set_int("running", tubelib.STATE_STOPPED)
meta:set_string("infotext", label .. " " .. number .. ": stopped")
meta:set_string("formspec", formspec(tubelib.STOPPED, false,
100 * fuel / BIOGAS_TIME_SEC, 0, true))
node.name = "biogasmachines:freezer"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):stop()
return false
end
local function freezer_idle(pos)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
local fuel = meta:get_int("fuel_ticks")
local label = minetest.registered_nodes[node.name].description
meta:set_int("item_ticks", ICE_TIME_SEC)
meta:set_int("running", tubelib.STATE_STANDBY)
meta:set_string("infotext", label .. " " .. number .. ": standby")
meta:set_string("formspec", formspec(tubelib.STANDBY, false,
100 * fuel / BIOGAS_TIME_SEC, 0, true))
node.name = "biogasmachines:freezer"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):start(TIMER_TICK_SEC * TICKS_TO_SLEEP)
return false
end
local function freezer_fault(pos)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
local fuel = meta:get_int("fuel_ticks")
local label = minetest.registered_nodes[node.name].description
meta:set_int("source", SOURCE_EMPTY)
meta:set_int("item_ticks", ICE_TIME_SEC)
meta:set_int("running", tubelib.STATE_FAULT)
meta:set_string("infotext", label .. " " .. number .. ": fault")
meta:set_string("formspec", formspec(tubelib.FAULT, false,
100 * fuel / BIOGAS_TIME_SEC, 0, true))
node.name = "biogasmachines:freezer"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):stop()
return false
end
--[[
---------
Callbacks
---------
]]--
-- do not allow to dig non-empty machine
local function can_dig(pos, player)
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
-- 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 meta:get_int("running") > 0) 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 meta:get_int("running") > 0 then
return 0
end
end
return stack:get_count()
end
-- punch machine to see status info
local function on_punch(pos, node, puncher, pointed_thing)
local meta = minetest.get_meta(pos)
local player_name = puncher:get_player_name()
if meta:get_string("owner") ~= player_name then
return false
end
local msgclr = { ["fault"] = "#FFBFBF",
["standby"] = "#BFFFFF",
["stopped"] = "#BFBFFF",
["running"] = "#BFFFBF",
["true"] = "#BFFFBF",
["false"] = "#BFFFFF" }
local state = tubelib.statestring(meta:get_int("running"))
local pipe = tostring(biogasmachines.is_pipe_with_water(pos, node))
minetest.chat_send_player(player_name,
minetest.colorize("#FFFF00", "[WaterFreezer:" ..
meta:get_string("number") .. "]") .. " Status is " ..
minetest.colorize(msgclr[state], "\"" .. state .. "\"") ..
", water pipe is " ..
minetest.colorize(msgclr[pipe], "\"" .. pipe .. "\""))
return true
end
-- formspec callback
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local running = meta:get_int("running")
local label = minetest.registered_nodes[node.name].description
if fields and fields.button then
if running > 0 or running == tubelib.STATE_FAULT then
freezer_stop(pos)
else
freezer_start(pos)
end
end
end
-- default Tubelib 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("number")
local running = meta:get_int("running")
local source = meta:get_int("source")
local fuelcnt = meta:get_int("fuel_ticks")
local itemcnt = meta:get_int("item_ticks")
if fuelcnt == 0 and inv:is_empty("fuel") then
-- no fuel - no work
return freezer_stop(pos)
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
if running > 0 then
running = running - 1
meta:set_int("running", running)
if running == 0 then
return freezer_idle(pos)
end
end
return true
end
if running == tubelib.STATE_STANDBY then
-- something to do, wake up and re-entry
return freezer_start(pos)
end
if inv:is_empty("cur") then
-- check if there is space in output, if not - do nothing
for _, stack in ipairs(output) do
if not inv:room_for_item("dst", stack) then
return true
end
end
-- process another water unit
if source == SOURCE_BUCKET then
local inp = get_water_bucket(inv, "src")
if inp:is_empty() then
-- oops
return freezer_fault(pos)
end
inv:add_item("cur", inp)
end
end
meta:set_int("source", source)
itemcnt = ICE_TIME_SEC
meta:set_int("item_ticks", itemcnt)
else
-- continue freezing process - add item tick
itemcnt = itemcnt - 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
meta:set_int("source", SOURCE_EMPTY)
itemcnt = ICE_TIME_SEC
end
meta:set_int("item_ticks", itemcnt)
-- consume fuel tick
if fuelcnt == 0 then
if not inv:is_empty("fuel") then
inv:remove_item("fuel",
ItemStack("tubelib_addons1:biogas 1"))
fuelcnt = BIOGAS_TIME_SEC
else
-- oops
return freezer_fault(pos)
end
end
fuelcnt = fuelcnt - 1
meta:set_int("fuel_ticks", fuelcnt)
end
meta:set_int("running", TICKS_TO_SLEEP)
meta:set_string("infotext", label .. " " .. number ..
": running (water " ..
(pipe and "from pipe" or "in buckets") .. ")")
meta:set_string("formspec", formspec(tubelib.RUNNING, pipe,
100 * fuelcnt / BIOGAS_TIME_SEC,
100 * (ICE_TIME_SEC - itemcnt) / ICE_TIME_SEC, true))
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 },
can_dig = can_dig,
after_dig_node = after_dig_node,
on_punch = on_punch,
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 = function(pos, placer, itemstack, pointed_thing)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = tubelib.add_node(pos, "biogasmachines:freezer")
if minetest.get_modpath("pipeworks") then
pipeworks.scan_for_pipe_objects(pos)
end
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)
local label = minetest.registered_nodes[node.name].description
meta:set_string("number", number)
meta:set_string("owner", placer:get_player_name())
meta:set_int("running", tubelib.STATE_STOPPED)
meta:set_int("source", SOURCE_EMPTY)
meta:set_int("fuel_ticks", 0)
meta:set_int("item_ticks", 0)
meta:set_string("infotext", label .. " " .. number .. ": stopped")
meta:set_string("formspec", formspec(tubelib.STOPPED, false, 0, 0, true))
end,
})
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 },
can_dig = can_dig,
after_dig_node = after_dig_node,
on_punch = on_punch,
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,
drop = "biogasmachines:freezer",
})
tubelib.register_node("biogasmachines:freezer", { "biogasmachines:freezer_active" }, {
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 == "on" then
freezer_start(pos)
elseif topic == "off" then
freezer_stop(pos)
elseif topic == "state" then
return tubelib.statestring(meta:get_int("running"))
elseif topic == "fuel" then
return tubelib.fuelstate(meta, "fuel")
else
return "unsupported"
end
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") 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