18 Commits

Author SHA1 Message Date
dd8432ef34 Merge remote-tracking branch 'upstream/master' 2021-01-24 12:54:09 +01:00
af4a699e19 Fix LuaCheck warning (#69) 2021-01-24 10:42:01 +01:00
c3f1b4ef41 Merge remote-tracking branch 'upstream/master' 2021-01-23 12:57:25 +01:00
ff525c09a4 Fix wires not connecting to nodes using digilines def (#68) 2021-01-21 17:47:55 +01:00
45991bf124 Merge remote-tracking branch 'upstream/master' 2021-01-19 23:26:45 +01:00
dc6cc0b04a [LuaCheck]: Various changes and improvements (#65)
* Add GitHub workflow
* Fix LuaCheck warnings
* Add build status badge on README.md
2021-01-16 09:59:19 +01:00
4e6b34da34 Merge remote-tracking branch 'upstream/master' 2020-12-15 23:43:58 +01:00
ab2eb4af43 LCDs: Rework line breaking algorithm, include spaces, show unknown symbols as spaces (#64)
Typeset the lines according to these rules (in order of subjective significance):

 * words that fit on the screen but would let the current line overflow are placed on a new line instead
 *  " | " always forces a linebreak
 *  spaces are included, except when there is a linebreak anyway
 * words with more characters than fit on screen are just chopped up, filling the lines as full as possible
 * don't bother typesetting more lines than fit on screen
 * if we are on the last line that will fit on screen
2020-12-09 17:48:18 +01:00
2800b237c5 Merge remote-tracking branch 'upstream/master' 2020-10-31 12:00:29 +01:00
021c521c65 Use mod.conf for dependencies and description (#59) 2020-10-30 15:50:26 +01:00
a6e79e6a25 Merge branch 'master' into nalc-1.2 2019-05-09 21:27:50 +02:00
434010bdd0 Implement slot tracking
Whenever putting or taking items, tell the listeners which slot they
were put into or taken from. Also tell listeners when the user moves
items from one slot to another.
2019-03-05 22:04:56 -08:00
9e4f1a5bab Rework Digilines chest
Fix various bugs regarding spurious messages being sent, item stack
swapping not being reported properly, etc.. Change from sending strings
to sending tables. Enhance Pipeworks tube support by reporting which
side an item is added or removed via tube or filter-injector.
2019-03-05 22:04:56 -08:00
DS
925b318800 change digiline to digilines in nodedef (#47)
* change digiline to digilines in nodedef

* choose the other variant
2019-03-05 22:01:12 -08:00
930ca4779a Update .luacheckrc 2019-03-05 22:00:20 -08:00
a2230bdeb1 Fix entity overloading and add missing entity check
- Remove bad clear_screen()
- Use minetest.is_player()
- Fix entity with rotated LCD
- Use on_rotate and ad new rotate function, refactor code
2019-03-05 22:00:20 -08:00
c7f4185e92 Fix duplicate messages (#52)
* Fixes duplicate messages
2019-03-05 21:41:50 -08:00
a5e2f05d92 Use new minetest.settings (#51)
Use new minetest.settings.
2019-03-05 21:35:36 -08:00
14 changed files with 448 additions and 147 deletions

11
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,11 @@
on: [push, pull_request]
name: build
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: lint
uses: Roang-zero1/factorio-mod-luacheck@master
with:
luacheckrc_url: ""

View File

@ -1,10 +1,13 @@
read_globals = { read_globals = {
"vector",
"screwdriver",
"minetest", "minetest",
"default", "default",
"pipeworks", "pipeworks",
"dump", "dump",
"VoxelArea", "VoxelArea",
"ItemStack",
} }
globals = { globals = {

View File

@ -1,7 +1,10 @@
Digilines Digilines
========== ==========
- The minetest counterpart for bus systems like i2c, SPI, RS232, USB -
[![Build status](https://github.com/minetest-mods/digilines/workflows/build/badge.svg)](https://github.com/minetest-mods/digilines/actions)
- The minetest counterpart for bus systems like i2c, SPI, RS232, USB -
- Minetest 5.0.0+ is required to use this mod.
This mod adds digiline wires, an RTC (Real Time Clock), a light sensor as well as an LCD Screen. This mod adds digiline wires, an RTC (Real Time Clock), a light sensor as well as an LCD Screen.
Can be used together with the luacontroller from mesecons. See the luacontroller manual for more information. Can be used together with the luacontroller from mesecons. See the luacontroller manual for more information.

View File

@ -1 +0,0 @@
default

View File

@ -1 +0,0 @@
This mod adds digiline wires, an RTC (Real Time Clock), a light sensor as well as an LCD Screen. Can be used together with the luacontroller from mesecons.

View File

@ -64,20 +64,20 @@ minetest.register_craft({
} }
}) })
-- former submods -- For minetest 0.4 support returned nil are also tested: ~= false
if minetest.is_yes(minetest.setting_get("digilines_enable_inventory") or true) then if minetest.settings:get_bool("digilines_enable_inventory", true) ~= false then
dofile(modpath .. "/inventory.lua") dofile(modpath .. "/inventory.lua")
end end
if minetest.is_yes(minetest.setting_get("digilines_enable_lcd") or true) then if minetest.settings:get_bool("digilines_enable_lcd", true) ~= false then
dofile(modpath .. "/lcd.lua") dofile(modpath .. "/lcd.lua")
end end
if minetest.is_yes(minetest.setting_get("digilines_enable_lightsensor") or true) then if minetest.settings:get_bool("digilines_enable_lightsensor", true) ~= false then
dofile(modpath .. "/lightsensor.lua") dofile(modpath .. "/lightsensor.lua")
end end
if minetest.is_yes(minetest.setting_get("digilines_enable_rtc") or true) then if minetest.settings:get_bool("digilines_enable_rtc", true) ~= false then
dofile(modpath .. "/rtc.lua") dofile(modpath .. "/rtc.lua")
end end

View File

@ -1,6 +1,7 @@
function digilines.getspec(node) function digilines.getspec(node)
if not minetest.registered_nodes[node.name] then return false end local def = minetest.registered_nodes[node.name]
return minetest.registered_nodes[node.name].digiline if not def then return false end
return def.digilines or def.digiline
end end
function digilines.importrules(spec, node) function digilines.importrules(spec, node)
@ -86,6 +87,12 @@ local function queue_dequeue(queue)
end end
function digilines.transmit(pos, channel, msg, checked) function digilines.transmit(pos, channel, msg, checked)
local checkedID = minetest.hash_node_position(pos)
if checked[checkedID] then
return
end
checked[checkedID] = true
digilines.vm_begin() digilines.vm_begin()
local queue = queue_new() local queue = queue_new()
queue_enqueue(queue, pos) queue_enqueue(queue, pos)
@ -105,9 +112,9 @@ function digilines.transmit(pos, channel, msg, checked)
for _, rule in ipairs(rules) do for _, rule in ipairs(rules) do
local nextPos = digilines.addPosRule(curPos, rule) local nextPos = digilines.addPosRule(curPos, rule)
if digilines.rules_link(curPos, nextPos) then if digilines.rules_link(curPos, nextPos) then
local checkedID = minetest.hash_node_position(nextPos) local checkedID2 = minetest.hash_node_position(nextPos)
if not checked[checkedID] then if not checked[checkedID2] then
checked[checkedID] = true checked[checkedID2] = true
queue_enqueue(queue, nextPos) queue_enqueue(queue, nextPos)
end end
end end

View File

@ -1,33 +1,56 @@
local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil
local function sendMessage(pos, msg, channel) -- Sends a message onto the Digilines network.
if channel == nil then -- pos: the position of the Digilines chest node.
channel = minetest.get_meta(pos):get_string("channel") -- action: the action string indicating what happened.
end -- stack: the ItemStack that the action acted on (optional).
digilines.receptor_send(pos,digilines.rules.default,channel,msg) -- from_slot: the slot number that is taken from (optional).
-- to_slot: the slot number that is put into (optional).
-- side: which side of the chest the action occurred (optional).
local function send_message(pos, action, stack, from_slot, to_slot, side)
local channel = minetest.get_meta(pos):get_string("channel")
local msg = {
action = action,
stack = stack and stack:to_table(),
from_slot = from_slot,
to_slot = to_slot,
-- Duplicate the vector in case the caller expects it not to change.
side = side and vector.new(side)
}
digilines.receptor_send(pos, digilines.rules.default, channel, msg)
end end
local function maybeString(stack) -- Checks if the inventory has become empty and, if so, sends an empty message.
if type(stack)=='string' then return stack local function check_empty(pos)
elseif type(stack)=='table' then return dump(stack) if minetest.get_meta(pos):get_inventory():is_empty("main") then
else return stack:to_string() send_message(pos, "empty")
end end
end end
local function can_insert(pos, stack) -- Checks if the inventory has become full for a particular type of item and,
local can = minetest.get_meta(pos):get_inventory():room_for_item("main", stack) -- if so, sends a full message.
if can then local function check_full(pos, stack)
sendMessage(pos,"put "..maybeString(stack)) local one_item_stack = ItemStack(stack)
else one_item_stack:set_count(1)
-- overflow and lost means that items are gonna be out as entities :/ if not minetest.get_meta(pos):get_inventory():room_for_item("main", one_item_stack) then
sendMessage(pos,"lost "..maybeString(stack)) send_message(pos, "full", one_item_stack)
end end
return can
end end
local tubeconn = pipeworks_enabled and "^pipeworks_tube_connection_wooden.png" or "" local tubeconn = pipeworks_enabled and "^pipeworks_tube_connection_wooden.png" or ""
local tubescan = pipeworks_enabled and function(pos) pipeworks.scan_for_tube_objects(pos) end or nil local tubescan = pipeworks_enabled and function(pos) pipeworks.scan_for_tube_objects(pos) end or nil
-- A place to remember things from allow_metadata_inventory_put to
-- on_metadata_inventory_put. This is a hack due to issue
-- minetest/minetest#6534 that should be removed once thats fixed.
local last_inventory_put_index
local last_inventory_put_stack
-- A place to remember things from allow_metadata_inventory_take to
-- tube.remove_items. This is a hack due to issue minetest-mods/pipeworks#205
-- that should be removed once thats fixed.
local last_inventory_take_index
minetest.register_alias("digilines_inventory:chest", "digilines:chest") minetest.register_alias("digilines_inventory:chest", "digilines:chest")
minetest.register_node("digilines:chest", { minetest.register_node("digilines:chest", {
description = "Digiline Chest", description = "Digiline Chest",
@ -74,7 +97,7 @@ minetest.register_node("digilines:chest", {
minetest.get_meta(pos):set_string("channel",fields.channel) minetest.get_meta(pos):set_string("channel",fields.channel)
end end
end, end,
digiline = { digilines = {
receptor = {}, receptor = {},
effector = { effector = {
action = function() end action = function() end
@ -86,63 +109,203 @@ minetest.register_node("digilines:chest", {
return not pipeworks.connects.facingFront(i,param2) return not pipeworks.connects.facingFront(i,param2)
end, end,
input_inventory = "main", input_inventory = "main",
can_insert = function(pos, _, stack) can_insert = function(pos, _, stack, direction)
return can_insert(pos, stack) local ret = minetest.get_meta(pos):get_inventory():room_for_item("main", stack)
end, if not ret then
insert_object = function(pos, _, stack) -- The stack cannot be accepted. It will never be passed to
local inv = minetest.get_meta(pos):get_inventory() -- insert_object, but it should be reported as a toverflow.
local leftover = inv:add_item("main", stack) -- Here, direction = direction item is moving, which is into
local count = leftover:get_count() -- side.
if count == 0 then local side = vector.multiply(direction, -1)
local derpstack = stack:get_name()..' 1' send_message(pos, "toverflow", stack, nil, nil, side)
if not inv:room_for_item("main", derpstack) then
-- when you can't put a single more of whatever you just put,
-- you'll get a put for it, then a full
sendMessage(pos,"full "..maybeString(stack)..' '..tostring(count))
end
else
-- this happens when the chest has received two stacks in a row and
-- filled up exactly with the first one.
-- You get a put for the first stack, a put for the second
-- and then a overflow with the first in stack and the second in leftover
-- and NO full?
sendMessage(pos,"overflow "..maybeString(stack)..' '..tostring(count))
end end
return leftover return ret
end,
insert_object = function(pos, _, original_stack, direction)
-- Here, direction = direction item is moving, which is into side.
local side = vector.multiply(direction, -1)
local inv = minetest.get_meta(pos):get_inventory()
local inv_contents = inv:get_list("main")
local any_put = false
local stack = original_stack
local stack_name = stack:get_name()
local stack_count = stack:get_count()
-- Walk the inventory, adding items to existing stacks of the same
-- type.
for i = 1, #inv_contents do
local existing_stack = inv_contents[i]
if not existing_stack:is_empty() and existing_stack:get_name() == stack_name then
local leftover = existing_stack:add_item(stack)
local leftover_count = leftover:get_count()
if leftover_count ~= stack_count then
-- We put some items into the slot. Update the slot in
-- the inventory, tell Digilines listeners about it,
-- and keep looking for the a place to put the
-- leftovers if any.
any_put = true
inv:set_stack("main", i, existing_stack)
local stack_that_was_put
if leftover_count == 0 then
stack_that_was_put = stack
else
stack_that_was_put = ItemStack(stack)
stack_that_was_put:set_count(stack_count - leftover_count)
end
send_message(pos, "tput", stack_that_was_put, nil, i, side)
stack = leftover
stack_count = leftover_count
if stack_count == 0 then
break
end
end
end
end
if stack_count ~= 0 then
-- Walk the inventory, adding items to empty slots.
for i = 1, #inv_contents do
local existing_stack = inv_contents[i]
if existing_stack:is_empty() then
local leftover = existing_stack:add_item(stack)
local leftover_count = leftover:get_count()
if leftover_count ~= stack_count then
-- We put some items into the slot. Update the slot in
-- the inventory, tell Digilines listeners about it,
-- and keep looking for the a place to put the
-- leftovers if any.
any_put = true
inv:set_stack("main", i, existing_stack)
local stack_that_was_put
if leftover_count == 0 then
stack_that_was_put = stack
else
stack_that_was_put = ItemStack(stack)
stack_that_was_put:set_count(stack_count - leftover_count)
end
send_message(pos, "tput", stack_that_was_put, nil, i, side)
stack = leftover
stack_count = leftover_count
if stack_count == 0 then
break
end
end
end
end
end
if any_put then
check_full(pos, original_stack)
end
if stack_count ~= 0 then
-- Some items could not be added and bounced back. Report them.
send_message(pos, "toverflow", stack, nil, nil, side)
end
return stack
end,
remove_items = function(pos, _, stack, dir, count)
-- Here, stack is the ItemStack in our own inventory that is being
-- pulled from, NOT the stack that is actually pulled out.
-- Combining it with count gives the stack that is pulled out.
-- Also, note that Pipeworks doesnt pass the index to this
-- function, so we use the one recorded in
-- allow_metadata_inventory_take; because we dont implement
-- tube.can_remove, Pipeworks will call
-- allow_metadata_inventory_take instead and will pass it the
-- index.
local taken = stack:take_item(count)
minetest.get_meta(pos):get_inventory():set_stack("main", last_inventory_take_index, stack)
send_message(pos, "ttake", taken, last_inventory_take_index, nil, dir)
check_empty(pos)
return taken
end, end,
}, },
allow_metadata_inventory_put = function(pos, _, _, stack) allow_metadata_inventory_put = function(pos, _, index, stack)
if not can_insert(pos, stack) then -- Remember what was in the target slot before the put; see
sendMessage(pos,"uoverflow "..maybeString(stack)) -- on_metadata_inventory_put for why we care.
end last_inventory_put_index = index
last_inventory_put_stack = minetest.get_meta(pos):get_inventory():get_stack("main", index)
return stack:get_count() return stack:get_count()
end, end,
on_metadata_inventory_move = function(pos, _, _, _, _, _, player) allow_metadata_inventory_take = function(_, _, index, stack)
-- Remember the index value; see tube.remove_items for why we care.
last_inventory_take_index = index
return stack:get_count()
end,
on_metadata_inventory_move = function(pos, _, from_index, _, to_index, count, player)
-- See what would happen if we were to move the items back from in the
-- opposite direction. In the event of a normal move, this must
-- succeed, because a normal move subtracts some items from the from
-- stack and adds them to the to stack; the two stacks naturally must
-- be compatible and so the reverse operation must succeed. However, if
-- the user *swaps* the two stacks instead, then due to issue
-- minetest/minetest#6534, this function is only called once; however,
-- when it is called, the stack that used to be in the to stack has
-- already been moved to the from stack, so we can detect the situation
-- by the fact that the reverse move will fail due to the from stack
-- being incompatible with its former contents.
local inv = minetest.get_meta(pos):get_inventory()
local from_stack = inv:get_stack("main", from_index)
local to_stack = inv:get_stack("main", to_index)
local reverse_move_stack = ItemStack(to_stack)
reverse_move_stack:set_count(count)
local swapped = from_stack:add_item(reverse_move_stack):get_count() == count
if swapped then
local channel = minetest.get_meta(pos):get_string("channel")
to_stack:set_count(count)
local msg = {
action = "uswap",
-- The slot and stack do not match because this function is
-- called after the action has taken place, but the Digilines
-- message is from the perspective of a viewer who hasnt
-- observed the movement yet.
x_stack = to_stack:to_table(),
x_slot = from_index,
y_stack = from_stack:to_table(),
y_slot = to_index,
}
digilines.receptor_send(pos, digilines.rules.default, channel, msg)
else
to_stack:set_count(count)
send_message(pos, "umove", to_stack, from_index, to_index)
end
minetest.log("action", player:get_player_name().." moves stuff in chest at "..minetest.pos_to_string(pos)) minetest.log("action", player:get_player_name().." moves stuff in chest at "..minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_put = function(pos, _, _, stack, player) on_metadata_inventory_put = function(pos, _, index, stack, player)
local channel = minetest.get_meta(pos):get_string("channel") -- Get what was in the target slot before the put; it has disappeared
local send = function(msg) -- by now (been replaced by the result of the put action) but we saved
sendMessage(pos,msg,channel) -- it in allow_metadata_inventory_put. This should always work
end -- (allow_metadata_inventory_put should AFAICT always be called
-- direction is only for furnaces -- immediately before on_metadata_inventory_put), but in case of
-- as the item has already been put, can_insert should return false if the chest is now full. -- something weird happening, just fall back to using an empty
local derpstack = stack:get_name()..' 1' -- ItemStack rather than crashing.
if can_insert(pos,derpstack) then local old_stack
send("uput "..maybeString(stack)) if last_inventory_put_index == index then
old_stack = last_inventory_put_stack
last_inventory_put_index = nil
last_inventory_put_stack = nil
else else
send("ufull "..maybeString(stack)) old_stack = ItemStack(nil)
end end
-- If the player tries to place a stack into an inventory, theres
-- already a stack there, and the existing stack is either of a
-- different item type or full, then obviously the stacks cant be
-- merged; instead the stacks are swapped. This information is not
-- reported to mods (Minetest core neither tells us that a particular
-- action was a swap, nor tells us a take followed by a put). In core,
-- the condition for swapping is that you try to add the new stack to
-- the existing stack and the leftovers are as big as the original
-- stack to put. Replicate that logic here using the old stack saved in
-- allow_metadata_inventory_put. If a swap happened, report it to the
-- Digilines network as a utake followed by a uput.
local leftovers = old_stack:add_item(stack)
if leftovers:get_count() == stack:get_count() then
send_message(pos, "utake", old_stack, index)
end
send_message(pos, "uput", stack, nil, index)
check_full(pos, stack)
minetest.log("action", player:get_player_name().." puts stuff into chest at "..minetest.pos_to_string(pos)) minetest.log("action", player:get_player_name().." puts stuff into chest at "..minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_take = function(pos, listname, _, stack, player) on_metadata_inventory_take = function(pos, _, index, stack, player)
local meta = minetest.get_meta(pos) send_message(pos, "utake", stack, index)
local channel = meta:get_string("channel") check_empty(pos)
local inv = meta:get_inventory()
if inv:is_empty(listname) then
sendMessage(pos, "empty", channel)
end
sendMessage(pos,"utake "..maybeString(stack))
minetest.log("action", player:get_player_name().." takes stuff from chest at "..minetest.pos_to_string(pos)) minetest.log("action", player:get_player_name().." takes stuff from chest at "..minetest.pos_to_string(pos))
end end
}) })

227
lcd.lua
View File

@ -5,7 +5,6 @@
-- load characters map -- load characters map
local chars_file = io.open(minetest.get_modpath("digilines").."/characters", "r") local chars_file = io.open(minetest.get_modpath("digilines").."/characters", "r")
local charmap = {} local charmap = {}
local max_chars = 12
if not chars_file then if not chars_file then
print("[digilines] E: LCD: character map file not found") print("[digilines] E: LCD: character map file not found")
else else
@ -21,7 +20,7 @@ else
end end
-- CONSTANTS -- CONSTANTS
local LCD_WITH = 100 local LCD_WIDTH = 100
local LCD_PADDING = 8 local LCD_PADDING = 8
local LINE_LENGTH = 12 local LINE_LENGTH = 12
@ -30,31 +29,95 @@ local NUMBER_OF_LINES = 5
local LINE_HEIGHT = 14 local LINE_HEIGHT = 14
local CHAR_WIDTH = 5 local CHAR_WIDTH = 5
assert((CHAR_WIDTH+1) * LINE_LENGTH <= LCD_WIDTH - LCD_PADDING*2, "LCD: Lines set too long!")
assert((LINE_HEIGHT+1) * NUMBER_OF_LINES <= LCD_WIDTH - LCD_PADDING*2, "LCD: Too many lines!")
local split = function(s, pat)
-- adapted from https://stackoverflow.com/a/1647577/4067384
-- simplified for our only usecase
local st, g = 1, s:gmatch("()("..pat..")")
local function getter()
if st then
local segs, seps, sep = st, g()
st = sep and seps + #sep
return s:sub(segs, (seps or 0) - 1)
end
end
return getter
end
local create_lines = function(text) local create_lines = function(text)
--[[
Typeset the lines according to these rules (in order of subjective significance):
- words that fit on the screen but would let the current line overflow are placed on a new line instead
- " | " always forces a linebreak
- spaces are included, except when there is a linebreak anyway
- words with more characters than fit on screen are just chopped up, filling the lines as full as possible
- don't bother typesetting more lines than fit on screen
- if we are on the last line that will fit on screen
]]--
local line = "" local line = ""
local line_num = 1 local line_num = 1
local tab = {} local tab = {}
for word in string.gmatch(text, "%S+") do local flush_line_and_check_for_return = function()
if string.len(line)+string.len(word) < LINE_LENGTH and word ~= "|" then table.insert(tab, line)
if line ~= "" then line_num = line_num+1
line = line.." "..word if line_num > NUMBER_OF_LINES then
return true
end
line = ""
end
for par in split(text, " | ") do
for word in split(par, "%s") do
if string.len(word) <= LINE_LENGTH and line_num < NUMBER_OF_LINES then
local line_len = string.len(line)
if line_len > 0 then
-- remember the space
line_len = line_len + 1
end
if line_len + string.len(word) <= LINE_LENGTH then
if line_len > 0 then
line = line.." "..word
else
line = word
end
else
-- don't add the space since we have a line break
if word ~= " " then
if line_len > 0 then
-- ok, we need the new line
if flush_line_and_check_for_return() then return tab end
end
line = word
end
end
else else
line = word -- chop up word to make it fit
end local remaining
else while true do
table.insert(tab, line) remaining = LINE_LENGTH - string.len(line)
if word ~= "|" then if remaining < LINE_LENGTH then
line = word line = line .. " "
else remaining = remaining - 1
line = "" end
end if remaining < string.len(word) then
line_num = line_num+1 line = line .. string.sub(word, 1, remaining)
if line_num > NUMBER_OF_LINES then word = string.sub(word, remaining+1)
return tab if flush_line_and_check_for_return() then return tab end
else
-- used up the word
line = line .. word
break
end
end
end end
end end
-- end of paragraph
if flush_line_and_check_for_return() then return tab end
line = ""
end end
table.insert(tab, line)
return tab return tab
end end
@ -63,7 +126,7 @@ local generate_line = function(s, ypos)
local parsed = {} local parsed = {}
local width = 0 local width = 0
local chars = 0 local chars = 0
while chars < max_chars and i <= #s do while chars < LINE_LENGTH and i <= #s do
local file = nil local file = nil
if charmap[s:sub(i, i)] ~= nil then if charmap[s:sub(i, i)] ~= nil then
file = charmap[s:sub(i, i)] file = charmap[s:sub(i, i)]
@ -73,10 +136,13 @@ local generate_line = function(s, ypos)
i = i + 2 i = i + 2
else else
print("[digilines] W: LCD: unknown symbol in '"..s.."' at "..i) print("[digilines] W: LCD: unknown symbol in '"..s.."' at "..i)
if charmap[" "] ~= nil then
file = charmap[" "]
end
i = i + 1 i = i + 1
end end
if file ~= nil then if file ~= nil then
width = width + CHAR_WIDTH width = width + CHAR_WIDTH + 1
table.insert(parsed, file) table.insert(parsed, file)
chars = chars + 1 chars = chars + 1
end end
@ -84,7 +150,7 @@ local generate_line = function(s, ypos)
width = width - 1 width = width - 1
local texture = "" local texture = ""
local xpos = math.floor((LCD_WITH - 2 * LCD_PADDING - width) / 2 + LCD_PADDING) local xpos = math.floor((LCD_WIDTH - width) / 2)
for ii = 1, #parsed do for ii = 1, #parsed do
texture = texture..":"..xpos..","..ypos.."="..parsed[ii]..".png" texture = texture..":"..xpos..","..ypos.."="..parsed[ii]..".png"
xpos = xpos + CHAR_WIDTH + 1 xpos = xpos + CHAR_WIDTH + 1
@ -93,8 +159,8 @@ local generate_line = function(s, ypos)
end end
local generate_texture = function(lines) local generate_texture = function(lines)
local texture = "[combine:"..LCD_WITH.."x"..LCD_WITH local texture = "[combine:"..LCD_WIDTH.."x"..LCD_WIDTH
local ypos = 16 local ypos = math.floor((LCD_WIDTH - LINE_HEIGHT*NUMBER_OF_LINES) / 2)
for i = 1, #lines do for i = 1, #lines do
texture = texture..generate_line(lines[i], ypos) texture = texture..generate_line(lines[i], ypos)
ypos = ypos + LINE_HEIGHT ypos = ypos + LINE_HEIGHT
@ -128,16 +194,59 @@ local clearscreen = function(pos)
end end
end end
local set_texture = function(ent)
local meta = minetest.get_meta(ent.object:get_pos())
local text = meta:get_string("text")
ent.object:set_properties({
textures = {
generate_texture(create_lines(text))
}
})
end
local get_entity = function(pos)
local lcd_entity
local objects = minetest.get_objects_inside_radius(pos, 0.5)
for _, o in ipairs(objects) do
local o_entity = o:get_luaentity()
if o_entity and o_entity.name == "digilines_lcd:text" then
if not lcd_entity then
lcd_entity = o_entity
else
-- Remove extras, if any
o:remove()
end
end
end
return lcd_entity
end
local rotate_text = function(pos, param)
local entity = get_entity(pos)
if not entity then
return
end
local lcd_info = lcds[param or minetest.get_node(pos).param2]
if not lcd_info then
return
end
entity.object:set_pos(vector.add(pos, lcd_info.delta))
entity.object:set_yaw(lcd_info.yaw or 0)
end
local prepare_writing = function(pos) local prepare_writing = function(pos)
local lcd_info = lcds[minetest.get_node(pos).param2] local entity = get_entity(pos)
if lcd_info == nil then return end if entity then
local text = minetest.add_entity( set_texture(entity)
{x = pos.x + lcd_info.delta.x, rotate_text(pos)
y = pos.y + lcd_info.delta.y, end
z = pos.z + lcd_info.delta.z}, "digilines_lcd:text") end
text:setyaw(lcd_info.yaw or 0)
--* text:setpitch(lcd_info.yaw or 0) local spawn_entity = function(pos)
return text if not get_entity(pos) then
minetest.add_entity(pos, "digilines_lcd:text")
rotate_text(pos)
end
end end
local on_digiline_receive = function(pos, _, channel, msg) local on_digiline_receive = function(pos, _, channel, msg)
@ -147,7 +256,7 @@ local on_digiline_receive = function(pos, _, channel, msg)
meta:set_string("text", msg) meta:set_string("text", msg)
meta:set_string("infotext", msg) meta:set_string("infotext", msg)
clearscreen(pos)
if msg ~= "" then if msg ~= "" then
prepare_writing(pos) prepare_writing(pos)
end end
@ -165,30 +274,34 @@ minetest.register_node("digilines:lcd", {
inventory_image = "lcd_lcd.png", inventory_image = "lcd_lcd.png",
wield_image = "lcd_lcd.png", wield_image = "lcd_lcd.png",
tiles = {"lcd_anyside.png"}, tiles = {"lcd_anyside.png"},
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,
light_source = 6,
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
node_box = lcd_box, node_box = lcd_box,
selection_box = lcd_box, selection_box = lcd_box,
groups = {choppy = 3, dig_immediate = 2}, groups = {choppy = 3, dig_immediate = 2},
after_place_node = function(pos)
after_place_node = function (pos)
local param2 = minetest.get_node(pos).param2 local param2 = minetest.get_node(pos).param2
if param2 == 0 or param2 == 1 then if param2 == 0 or param2 == 1 then
minetest.add_node(pos, {name = "digilines:lcd", param2 = 3}) minetest.add_node(pos, {name = "digilines:lcd", param2 = 3})
end end
prepare_writing (pos) spawn_entity(pos)
prepare_writing(pos)
end, end,
on_construct = reset_meta,
on_construct = function(pos) on_destruct = clearscreen,
reset_meta(pos) on_punch = function(pos, _, puncher, _)
if minetest.is_player(puncher) then
spawn_entity(pos)
end
end, end,
on_rotate = function(pos, _, _, mode, new_param2)
on_destruct = function(pos) if mode ~= screwdriver.ROTATE_FACE then
clearscreen(pos) return false
end
rotate_text(pos, new_param2)
end, end,
on_receive_fields = function(pos, _, fields, sender) on_receive_fields = function(pos, _, fields, sender)
local name = sender:get_player_name() local name = sender:get_player_name()
if minetest.is_protected(pos, name) and not minetest.check_player_privs(name, {protection_bypass=true}) then if minetest.is_protected(pos, name) and not minetest.check_player_privs(name, {protection_bypass=true}) then
@ -199,35 +312,37 @@ minetest.register_node("digilines:lcd", {
minetest.get_meta(pos):set_string("channel", fields.channel) minetest.get_meta(pos):set_string("channel", fields.channel)
end end
end, end,
digilines = {
digiline =
{
receptor = {}, receptor = {},
effector = { effector = {
action = on_digiline_receive action = on_digiline_receive
}, },
}, },
})
light_source = 6, minetest.register_lbm({
label = "Replace Missing Text Entities",
name = "digilines:replace_text",
nodenames = {"digilines:lcd"},
run_at_every_load = true,
action = spawn_entity,
}) })
minetest.register_entity(":digilines_lcd:text", { minetest.register_entity(":digilines_lcd:text", {
collisionbox = { 0, 0, 0, 0, 0, 0 }, collisionbox = { 0, 0, 0, 0, 0, 0 },
visual = "upright_sprite", visual = "upright_sprite",
textures = {}, textures = {},
on_activate = set_texture,
on_activate = function(self)
local meta = minetest.get_meta(self.object:getpos())
local text = meta:get_string("text")
self.object:set_properties({textures={generate_texture(create_lines(text))}})
end
}) })
minetest.register_craft({ minetest.register_craft({
output = "digilines:lcd 2", output = "digilines:lcd 2",
recipe = { recipe = {
{"default:steel_ingot", "digilines:wire_std_00000000", "default:steel_ingot"}, {"default:steel_ingot", "digilines:wire_std_00000000", "default:steel_ingot"},
{"mesecons_lightstone:lightstone_green_off","mesecons_lightstone:lightstone_green_off","mesecons_lightstone:lightstone_green_off"}, {"mesecons_lightstone:lightstone_green_off",
"mesecons_lightstone:lightstone_green_off",
"mesecons_lightstone:lightstone_green_off"},
{"default:glass","default:glass","default:glass"} {"default:glass","default:glass","default:glass"}
} }
}) })

View File

@ -39,7 +39,7 @@ minetest.register_node("digilines:lightsensor", {
groups = {dig_immediate=2}, groups = {dig_immediate=2},
selection_box = lsensor_selbox, selection_box = lsensor_selbox,
node_box = lsensor_nodebox, node_box = lsensor_nodebox,
digiline = digilines =
{ {
receptor = {}, receptor = {},
effector = { effector = {

View File

@ -1 +1,6 @@
name = digilines name = digilines
depends = default
description = """
This mod adds digiline wires, an RTC (Real Time Clock), a light sensor as well as an LCD Screen.
Can be used together with the luacontroller from mesecons.
"""

View File

@ -35,7 +35,7 @@ minetest.register_node("digilines:rtc", {
groups = {dig_immediate=2}, groups = {dig_immediate=2},
selection_box = rtc_selbox, selection_box = rtc_selbox,
node_box = rtc_nodebox, node_box = rtc_nodebox,
digiline = digilines =
{ {
receptor = {}, receptor = {},
effector = { effector = {

View File

@ -90,7 +90,7 @@ for zmy=0, 1 do
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", paramtype2 = "facedir",
sunlight_propagates = true, sunlight_propagates = true,
digiline = digilines =
{ {
wire = wire =
{ {

View File

@ -1,15 +1,12 @@
minetest.register_on_placenode(function(pos, node)
if minetest.registered_nodes[node.name].digiline then
digilines.update_autoconnect(pos)
end
end)
minetest.register_on_dignode(function(pos, node) local function check_and_update(pos, node)
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].digiline then if digilines.getspec(node) then
-- need to make sure that node exists (unknown nodes!)
digilines.update_autoconnect(pos) digilines.update_autoconnect(pos)
end end
end) end
minetest.register_on_placenode(check_and_update)
minetest.register_on_dignode(check_and_update)
function digilines.update_autoconnect(pos, secondcall) function digilines.update_autoconnect(pos, secondcall)
local xppos = {x=pos.x+1, y=pos.y, z=pos.z} local xppos = {x=pos.x+1, y=pos.y, z=pos.z}
@ -42,8 +39,7 @@ function digilines.update_autoconnect(pos, secondcall)
digilines.update_autoconnect(zmympos, true) digilines.update_autoconnect(zmympos, true)
end end
local def = minetest.registered_nodes[minetest.get_node(pos).name] local digilinespec = digilines.getspec(minetest.get_node(pos))
local digilinespec = def and def.digiline
if not (digilinespec and digilinespec.wire and if not (digilinespec and digilinespec.wire and
digilinespec.wire.use_autoconnect) then digilinespec.wire.use_autoconnect) then
return nil return nil