Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scanner node #540

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions mesecons/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ function mesecon.getstate(nodename, states)
error(nodename.." doesn't mention itself in "..dump(states))
end

function mesecon.setstate(pos, node, newstate)
local nodedef = minetest.registered_nodes[node.name]

minetest.swap_node(pos, {name = nodedef.__mesecon_basename .. "_" .. newstate,
param2 = node.param2})
end

function mesecon.getbinstate(nodename, states)
return mesecon.dec2bin(mesecon.getstate(nodename, states)-1)
end
Expand Down
1 change: 1 addition & 0 deletions mesecons_alias/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ minetest.register_alias("mesecons:noteblock", "mesecons_noteblock:noteblock")
minetest.register_alias("mesecons:microcontroller", "mesecons_microcontroller:microcontroller0000")
minetest.register_alias("mesecons:delayer", "mesecons_delayer:delayer_off_1")
minetest.register_alias("mesecons:solarpanel", "mesecons_solarpanel:solar_panel_off")
minetest.register_alias("mesecons:scanner", "mesecons_scanner:mesecon_scanner_off")


--Backwards compatibility
Expand Down
6 changes: 6 additions & 0 deletions mesecons_scanner/doc/scanner/description.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
The Scanner detects how many slots are occupied in the inventory of the node behind it
and outputs a signal according to its configuration. Right click on the Scanner to configure it.
The default configuration is to output a signal if the scanned inventory contain any items.

The Scanner will return its output signal status upon receiving a Digiline "GET" command and
send a message on the channel when the output state changes.
Binary file added mesecons_scanner/doc/scanner/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mesecons_scanner/doc/scanner/recipe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
366 changes: 366 additions & 0 deletions mesecons_scanner/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,366 @@
-- mesecons_scanner

local digilines_enabled = minetest.get_modpath("digilines") ~= nil

local scanner_get_output_rules = function(node)
local rules = {
{ x = 0, y = 0, z = 1 },
{ x = 1, y = 0, z = 0 },
{ x = -1, y = 0, z = 0 },
}
for i = 0, node.param2 do
rules = mesecon.rotate_rules_left(rules)
end
return rules
end

local scanner_get_input_rules = function(node)
local rules = {{x = 0, y = 0, z = -1}}
for i = 0, node.param2 do
rules = mesecon.rotate_rules_left(rules)
end
return rules
end

local function rotated_facedir(node)
local rules = scanner_get_input_rules(node)
local dir = rules[1]
return dir
end

local function update_keys(pos, node)
local meta = minetest.get_meta(pos)
local dir = rotated_facedir(node)
local target = { x=pos.x+dir.x, y=pos.y+dir.y, z=pos.z+dir.z }
local invs = minetest.get_meta(target):get_inventory():get_lists()
local keys = {}
for k,v in pairs(invs) do
keys[#keys + 1] = k
if k == meta:get_string("selected_inv") then
meta:set_string("selected_key", #keys)
end
end
meta:set_string("keys", table.concat(keys, ","))
end

local function update_formspec(pos, meta)
if digilines_enabled then
meta:set_string("formspec", "size[6,7.2]"..
((default and default.gui_bg) or "")..
((default and default.gui_bg_img) or "")..
"label[0,0;Inventory Scanner]"..

"label[5,0;(?)]"..
"tooltip[5,0;1,1;Watermark values are in percent of slots occupied with one or more items.;black;white]"..

"label[0,1.05;Select inventory]"..
"dropdown[3,1;3,1;inventory;" .. meta:get_string("keys") .. ";" .. meta:get_string("selected_key") .. "]"..
"field[0.3,2.6;2.9,1;low;Low watermark;" .. meta:get_int("low_pct") .. "]"..
"field[3.3,2.6;2.9,1;high;High watermark;" .. meta:get_int("high_pct") .. "]"..

"label[0,3.35;Presets]"..
"button[0,3.8;2,1;empty;Empty]"..
"button[2,3.8;2,1;has_items;Has items]"..
"button[4,3.8;2,1;full;Full]"..

"field[0.3,5.5;6,1;channel;Digiline Channel (optional);" .. meta:get_string("channel") .. "]"..

"checkbox[0.1,6.4;invert;Invert output;".. meta:get_string("invert") .."]"..
"button_exit[4,6.4;2,1;save;Save]"..
""
)
else
meta:set_string("formspec", "size[6,6.2]"..
((default and default.gui_bg) or "")..
((default and default.gui_bg_img) or "")..
"label[0,0;Inventory Scanner]"..

"label[5,0;(?)]"..
"tooltip[5,0;1,1;Watermark values are in percent of slots occupied with one or more items.;black;white]"..

"label[0,1.05;Select inventory]"..
"dropdown[3,1;3,1;inventory;" .. meta:get_string("keys") .. ";" .. meta:get_string("selected_key") .. "]"..
"field[0.3,2.6;2.9,1;low;Low watermark;" .. meta:get_int("low_pct") .. "]"..
"field[3.3,2.6;2.9,1;high;High watermark;" .. meta:get_int("high_pct") .. "]"..

"label[0,3.35;Presets]"..
"button[0,3.8;2,1;empty;Empty]"..
"button[2,3.8;2,1;has_items;Has items]"..
"button[4,3.8;2,1;full;Full]"..

"checkbox[0.1,5.3;invert;Invert output;".. meta:get_string("invert") .."]"..
"button_exit[4,5.3;2,1;save;Save]"..
""
)
end
end

-- Convert input percent to actual inventory slot counts and cache the values
local function update_watermarks(pos, meta)
local node = minetest.get_node(pos)
local dir = rotated_facedir(node)
local i_pos = { x=pos.x+dir.x, y=pos.y+dir.y, z=pos.z+dir.z }
local i_meta = minetest.get_meta(i_pos)
local i_name = meta:get_string("selected_inv")
local i_inv = i_meta:get_inventory()
local i_size = i_inv:get_size(i_name)

local low_pct = meta:get_int("low_pct")
local high_pct = meta:get_int("high_pct")

local low = math.ceil(i_size * (low_pct / 100.0))
local high = math.ceil(i_size * (high_pct / 100.0))

-- We want watermark % > 0 to enable output if a single slot has items
-- regardless of inventory size so add 1 here if needed.
if low_pct > 0 and low == 0 then
low = 1
end
if high_pct > 0 and high == 0 then
high = 1
end

meta:set_int("low", low)
meta:set_int("high", high)
end

local function update_state(pos, node, state)
mesecon.setstate(pos, node, state)
if state == "on" then
mesecon.receptor_on(pos, scanner_get_output_rules(node))
else
mesecon.receptor_off(pos, scanner_get_output_rules(node))
end
end

local function on_receive_fields(pos, form_name, fields, sender)
local meta = minetest.get_meta(pos)
if fields.inventory and fields.inventory ~= "" then
meta:set_string("selected_inv", fields.inventory)
end
if fields.channel then
meta:set_string("channel", fields.channel)
end
if fields.low then
meta:set_int("low_pct", fields.low)
end
if fields.high then
meta:set_int("high_pct", fields.high)
end
if fields.invert then
meta:set_string("invert", fields.invert)
end
-- Buttons
if fields.empty then
meta:set_int("low_pct", 0)
meta:set_int("high_pct", 0)
meta:set_string("invert", "false")
elseif fields.has_items then
meta:set_int("low_pct", 1)
meta:set_int("high_pct", 100)
meta:set_string("invert", "false")
elseif fields.full then
meta:set_int("low_pct", 100)
meta:set_int("high_pct", 100)
meta:set_string("invert", "false")
end
update_formspec(pos, meta)
update_watermarks(pos, meta)
end

local boxes = {
{ -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- the bottom slab
{ -7/16, -7/16, -4/16, 4/16, -4/16, 4/16 }, -- the "box"
{ -8/16, -7/16, -5/16, -7/16, -3/16, 5/16 }, -- the back plate in YZ plane
}

mesecon.register_node("mesecons_scanner:mesecon_scanner", {
paramtype2="facedir",
description = "Inventory scanner",
is_ground_content = false,
sunlight_propagates = false,
inventory_image = "mesecons_scanner_preview.png",
drawtype = "nodebox",
selection_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, -8/16, 8/16, -3/16, 8/16 },
}
},
node_box = {
type = "fixed",
fixed = boxes
},
after_dig_node = function (pos, node)
mesecon.do_cooldown(pos)
mesecon.receptor_off(pos, scanner_get_output_rules(node))
end,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
meta:set_string("selected_inv", "main")
-- Default behavior is output signal on "has items"
meta:set_int("low_pct", 1)
meta:set_int("high_pct", 100)
meta:set_string("invert", "false")
meta:set_string("output", "off")
update_keys(pos, node)
update_watermarks(pos, meta)
update_formspec(pos, meta)
minetest.get_node_timer(pos):start(1)
end,
on_receive_fields = on_receive_fields,
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
-- Update in case the node behind the scanner has changed
update_keys(pos, node)
update_formspec(pos, meta)
end,
on_timer = function (pos, elapsed)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local dir = rotated_facedir(node)
local invert = meta:get_string("invert") == "true"
local old_output = meta:get_string("output") == "on"

-- Get watermark values in converted from percent to # slots
local low = meta:get_int("low")
local high = meta:get_int("high")

-- Get the inventory of the node behind our scanner
local i_pos = { x=pos.x+dir.x, y=pos.y+dir.y, z=pos.z+dir.z }
local i_meta = minetest.get_meta(i_pos)
local i_inv = i_meta:get_inventory()
local output = false
local i_name = meta:get_string("selected_inv")
local i_size = i_inv:get_size(i_name)

-- Re-calculate low/high if inventory size changed
if meta:get_int("inventory_size") ~= i_size then
meta:set_int("inventory_size", i_size)
update_watermarks(pos, meta)
end

local count = 0
if i_size > 0 then
-- Get number of slots with items in them
for i = 1, i_size do
if not i_inv:get_stack(i_name, i):is_empty() then count = count + 1 end
end
-- Calculate our output
output = (count >= low and count <= high)
if invert then
output = not output
end
end
meta:set_int("current", count)

-- Do update if output changed
if old_output ~= output then
-- Update node
local output_string = (output and "on") or "off"
update_state(pos, node, output_string)

-- Save the new state
meta:set_string("output", output_string)

-- Send digiline message
if digilines_enabled and meta:get_string("channel") ~= "" then
digilines.receptor_send(pos, digilines.rules.default,
meta:get_string("channel"),
{ output = output_string }
)
end
end
return true
end,
digiline = {
receptor = {action = function() end},
effector = {
action = function(pos, node, channel, msg)
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
if type(msg) == "table" then
if msg.inventory then
meta:set_string("selected_inv", msg.inventory)
end
if msg.low then
-- 0-100
meta:set_int("low_pct", msg.low)
end
if msg.high then
-- 0-100
meta:set_int("high_pct", msg.high)
end
if msg.invert then
-- "true" or "false"
meta:set_string("invert", msg.invert)
end
update_watermarks(pos, meta)
else
if msg == "GET" or msg == "get" then
local size = meta:get_int("inventory_size")
local current = meta:get_int("current")
local current_pct = 0
if size > 0 and current > 0 then
current_pct = math.floor(0.5 + ((100 * current) / size))
end
digilines.receptor_send(pos, digilines.rules.default, channel, {
output = meta:get_string("output"),
inventory = meta:get_string("selected_inv"),
low = meta:get_int("low_pct"),
high = meta:get_int("high_pct"),
current = current_pct,
})
end
end
end
},
},

},{
groups = { dig_immediate=2 },
tiles = {
-- top
"mesecons_scanner_top_off.png",
-- bottom
"mesecons_scanner_bottom.png",
-- side 1 (since we are rotated names don't match)
"mesecons_scanner_front_off.png",
-- side 2
"mesecons_scanner_back.png",
-- front
"mesecons_scanner_right_off.png",
-- back
"mesecons_scanner_left_off.png",
},
mesecons = { receptor = {
state = mesecon.state.off,
rules = scanner_get_output_rules,
} }
},{
groups = { dig_immediate=2, not_in_creative_inventory=1 },
tiles = {
"mesecons_scanner_top_on.png",
"mesecons_scanner_bottom.png",
"mesecons_scanner_front_on.png",
"mesecons_scanner_back.png",
"mesecons_scanner_right_on.png",
"mesecons_scanner_left_on.png",
},
mesecons = { receptor = {
state = mesecon.state.on,
rules = scanner_get_output_rules,
} }
})

minetest.register_craft({
output = "mesecons_scanner:mesecon_scanner_off 1",
recipe = {
{"", "group:mesecon_conductor_craftable", ""},
{"", "mesecons_microcontroller:microcontroller0000", ""},
{"default:stone", "default:stone", "default:stone"},
}
})
2 changes: 2 additions & 0 deletions mesecons_scanner/mod.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name = mesecons_scanner
depends = mesecons
Binary file added mesecons_scanner/textures/mesecons_scanner_back.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.