forked from minetest-mods/digilines
Compare commits
18 Commits
nalc-1.0
...
dd8432ef34
Author | SHA1 | Date | |
---|---|---|---|
dd8432ef34 | |||
af4a699e19 | |||
c3f1b4ef41 | |||
ff525c09a4 | |||
45991bf124 | |||
dc6cc0b04a | |||
4e6b34da34 | |||
ab2eb4af43 | |||
2800b237c5 | |||
021c521c65 | |||
a6e79e6a25 | |||
434010bdd0 | |||
9e4f1a5bab | |||
925b318800 | |||
930ca4779a | |||
a2230bdeb1 | |||
c7f4185e92 | |||
a5e2f05d92 |
11
.github/workflows/build.yml
vendored
Normal file
11
.github/workflows/build.yml
vendored
Normal 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: ""
|
@ -1,10 +1,13 @@
|
||||
|
||||
read_globals = {
|
||||
"vector",
|
||||
"screwdriver",
|
||||
"minetest",
|
||||
"default",
|
||||
"pipeworks",
|
||||
"dump",
|
||||
"VoxelArea",
|
||||
"ItemStack",
|
||||
}
|
||||
|
||||
globals = {
|
||||
|
@ -1,7 +1,10 @@
|
||||
Digilines
|
||||
==========
|
||||
- The minetest counterpart for bus systems like i2c, SPI, RS232, USB -
|
||||
|
||||
[](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.
|
||||
Can be used together with the luacontroller from mesecons. See the luacontroller manual for more information.
|
||||
|
@ -1 +0,0 @@
|
||||
default
|
@ -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.
|
10
init.lua
10
init.lua
@ -64,20 +64,20 @@ minetest.register_craft({
|
||||
}
|
||||
})
|
||||
|
||||
-- former submods
|
||||
if minetest.is_yes(minetest.setting_get("digilines_enable_inventory") or true) then
|
||||
-- For minetest 0.4 support returned nil are also tested: ~= false
|
||||
if minetest.settings:get_bool("digilines_enable_inventory", true) ~= false then
|
||||
dofile(modpath .. "/inventory.lua")
|
||||
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")
|
||||
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")
|
||||
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")
|
||||
end
|
||||
|
||||
|
17
internal.lua
17
internal.lua
@ -1,6 +1,7 @@
|
||||
function digilines.getspec(node)
|
||||
if not minetest.registered_nodes[node.name] then return false end
|
||||
return minetest.registered_nodes[node.name].digiline
|
||||
local def = minetest.registered_nodes[node.name]
|
||||
if not def then return false end
|
||||
return def.digilines or def.digiline
|
||||
end
|
||||
|
||||
function digilines.importrules(spec, node)
|
||||
@ -86,6 +87,12 @@ local function queue_dequeue(queue)
|
||||
end
|
||||
|
||||
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()
|
||||
local queue = queue_new()
|
||||
queue_enqueue(queue, pos)
|
||||
@ -105,9 +112,9 @@ function digilines.transmit(pos, channel, msg, checked)
|
||||
for _, rule in ipairs(rules) do
|
||||
local nextPos = digilines.addPosRule(curPos, rule)
|
||||
if digilines.rules_link(curPos, nextPos) then
|
||||
local checkedID = minetest.hash_node_position(nextPos)
|
||||
if not checked[checkedID] then
|
||||
checked[checkedID] = true
|
||||
local checkedID2 = minetest.hash_node_position(nextPos)
|
||||
if not checked[checkedID2] then
|
||||
checked[checkedID2] = true
|
||||
queue_enqueue(queue, nextPos)
|
||||
end
|
||||
end
|
||||
|
291
inventory.lua
291
inventory.lua
@ -1,33 +1,56 @@
|
||||
local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil
|
||||
|
||||
local function sendMessage(pos, msg, channel)
|
||||
if channel == nil then
|
||||
channel = minetest.get_meta(pos):get_string("channel")
|
||||
end
|
||||
digilines.receptor_send(pos,digilines.rules.default,channel,msg)
|
||||
-- Sends a message onto the Digilines network.
|
||||
-- pos: the position of the Digilines chest node.
|
||||
-- action: the action string indicating what happened.
|
||||
-- stack: the ItemStack that the action acted on (optional).
|
||||
-- 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
|
||||
|
||||
local function maybeString(stack)
|
||||
if type(stack)=='string' then return stack
|
||||
elseif type(stack)=='table' then return dump(stack)
|
||||
else return stack:to_string()
|
||||
-- Checks if the inventory has become empty and, if so, sends an empty message.
|
||||
local function check_empty(pos)
|
||||
if minetest.get_meta(pos):get_inventory():is_empty("main") then
|
||||
send_message(pos, "empty")
|
||||
end
|
||||
end
|
||||
|
||||
local function can_insert(pos, stack)
|
||||
local can = minetest.get_meta(pos):get_inventory():room_for_item("main", stack)
|
||||
if can then
|
||||
sendMessage(pos,"put "..maybeString(stack))
|
||||
else
|
||||
-- overflow and lost means that items are gonna be out as entities :/
|
||||
sendMessage(pos,"lost "..maybeString(stack))
|
||||
-- Checks if the inventory has become full for a particular type of item and,
|
||||
-- if so, sends a full message.
|
||||
local function check_full(pos, stack)
|
||||
local one_item_stack = ItemStack(stack)
|
||||
one_item_stack:set_count(1)
|
||||
if not minetest.get_meta(pos):get_inventory():room_for_item("main", one_item_stack) then
|
||||
send_message(pos, "full", one_item_stack)
|
||||
end
|
||||
return can
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
-- 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 that’s 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 that’s fixed.
|
||||
local last_inventory_take_index
|
||||
|
||||
minetest.register_alias("digilines_inventory:chest", "digilines:chest")
|
||||
minetest.register_node("digilines:chest", {
|
||||
description = "Digiline Chest",
|
||||
@ -74,7 +97,7 @@ minetest.register_node("digilines:chest", {
|
||||
minetest.get_meta(pos):set_string("channel",fields.channel)
|
||||
end
|
||||
end,
|
||||
digiline = {
|
||||
digilines = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = function() end
|
||||
@ -86,63 +109,203 @@ minetest.register_node("digilines:chest", {
|
||||
return not pipeworks.connects.facingFront(i,param2)
|
||||
end,
|
||||
input_inventory = "main",
|
||||
can_insert = function(pos, _, stack)
|
||||
return can_insert(pos, stack)
|
||||
end,
|
||||
insert_object = function(pos, _, stack)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local leftover = inv:add_item("main", stack)
|
||||
local count = leftover:get_count()
|
||||
if count == 0 then
|
||||
local derpstack = stack:get_name()..' 1'
|
||||
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))
|
||||
can_insert = function(pos, _, stack, direction)
|
||||
local ret = minetest.get_meta(pos):get_inventory():room_for_item("main", stack)
|
||||
if not ret then
|
||||
-- The stack cannot be accepted. It will never be passed to
|
||||
-- insert_object, but it should be reported as a toverflow.
|
||||
-- Here, direction = direction item is moving, which is into
|
||||
-- side.
|
||||
local side = vector.multiply(direction, -1)
|
||||
send_message(pos, "toverflow", stack, nil, nil, side)
|
||||
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 doesn’t pass the index to this
|
||||
-- function, so we use the one recorded in
|
||||
-- allow_metadata_inventory_take; because we don’t 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,
|
||||
},
|
||||
allow_metadata_inventory_put = function(pos, _, _, stack)
|
||||
if not can_insert(pos, stack) then
|
||||
sendMessage(pos,"uoverflow "..maybeString(stack))
|
||||
end
|
||||
allow_metadata_inventory_put = function(pos, _, index, stack)
|
||||
-- Remember what was in the target slot before the put; see
|
||||
-- on_metadata_inventory_put for why we care.
|
||||
last_inventory_put_index = index
|
||||
last_inventory_put_stack = minetest.get_meta(pos):get_inventory():get_stack("main", index)
|
||||
return stack:get_count()
|
||||
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 hasn’t
|
||||
-- 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))
|
||||
end,
|
||||
on_metadata_inventory_put = function(pos, _, _, stack, player)
|
||||
local channel = minetest.get_meta(pos):get_string("channel")
|
||||
local send = function(msg)
|
||||
sendMessage(pos,msg,channel)
|
||||
end
|
||||
-- direction is only for furnaces
|
||||
-- as the item has already been put, can_insert should return false if the chest is now full.
|
||||
local derpstack = stack:get_name()..' 1'
|
||||
if can_insert(pos,derpstack) then
|
||||
send("uput "..maybeString(stack))
|
||||
on_metadata_inventory_put = function(pos, _, index, stack, player)
|
||||
-- Get what was in the target slot before the put; it has disappeared
|
||||
-- by now (been replaced by the result of the put action) but we saved
|
||||
-- it in allow_metadata_inventory_put. This should always work
|
||||
-- (allow_metadata_inventory_put should AFAICT always be called
|
||||
-- immediately before on_metadata_inventory_put), but in case of
|
||||
-- something weird happening, just fall back to using an empty
|
||||
-- ItemStack rather than crashing.
|
||||
local old_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
|
||||
send("ufull "..maybeString(stack))
|
||||
old_stack = ItemStack(nil)
|
||||
end
|
||||
-- If the player tries to place a stack into an inventory, there’s
|
||||
-- already a stack there, and the existing stack is either of a
|
||||
-- different item type or full, then obviously the stacks can’t 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))
|
||||
end,
|
||||
on_metadata_inventory_take = function(pos, listname, _, stack, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local channel = meta:get_string("channel")
|
||||
local inv = meta:get_inventory()
|
||||
if inv:is_empty(listname) then
|
||||
sendMessage(pos, "empty", channel)
|
||||
end
|
||||
sendMessage(pos,"utake "..maybeString(stack))
|
||||
on_metadata_inventory_take = function(pos, _, index, stack, player)
|
||||
send_message(pos, "utake", stack, index)
|
||||
check_empty(pos)
|
||||
minetest.log("action", player:get_player_name().." takes stuff from chest at "..minetest.pos_to_string(pos))
|
||||
end
|
||||
})
|
||||
|
227
lcd.lua
227
lcd.lua
@ -5,7 +5,6 @@
|
||||
-- load characters map
|
||||
local chars_file = io.open(minetest.get_modpath("digilines").."/characters", "r")
|
||||
local charmap = {}
|
||||
local max_chars = 12
|
||||
if not chars_file then
|
||||
print("[digilines] E: LCD: character map file not found")
|
||||
else
|
||||
@ -21,7 +20,7 @@ else
|
||||
end
|
||||
|
||||
-- CONSTANTS
|
||||
local LCD_WITH = 100
|
||||
local LCD_WIDTH = 100
|
||||
local LCD_PADDING = 8
|
||||
|
||||
local LINE_LENGTH = 12
|
||||
@ -30,31 +29,95 @@ local NUMBER_OF_LINES = 5
|
||||
local LINE_HEIGHT = 14
|
||||
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)
|
||||
--[[
|
||||
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_num = 1
|
||||
local tab = {}
|
||||
for word in string.gmatch(text, "%S+") do
|
||||
if string.len(line)+string.len(word) < LINE_LENGTH and word ~= "|" then
|
||||
if line ~= "" then
|
||||
line = line.." "..word
|
||||
local flush_line_and_check_for_return = function()
|
||||
table.insert(tab, line)
|
||||
line_num = line_num+1
|
||||
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
|
||||
line = word
|
||||
end
|
||||
else
|
||||
table.insert(tab, line)
|
||||
if word ~= "|" then
|
||||
line = word
|
||||
else
|
||||
line = ""
|
||||
end
|
||||
line_num = line_num+1
|
||||
if line_num > NUMBER_OF_LINES then
|
||||
return tab
|
||||
-- chop up word to make it fit
|
||||
local remaining
|
||||
while true do
|
||||
remaining = LINE_LENGTH - string.len(line)
|
||||
if remaining < LINE_LENGTH then
|
||||
line = line .. " "
|
||||
remaining = remaining - 1
|
||||
end
|
||||
if remaining < string.len(word) then
|
||||
line = line .. string.sub(word, 1, remaining)
|
||||
word = string.sub(word, remaining+1)
|
||||
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 of paragraph
|
||||
if flush_line_and_check_for_return() then return tab end
|
||||
line = ""
|
||||
end
|
||||
table.insert(tab, line)
|
||||
return tab
|
||||
end
|
||||
|
||||
@ -63,7 +126,7 @@ local generate_line = function(s, ypos)
|
||||
local parsed = {}
|
||||
local width = 0
|
||||
local chars = 0
|
||||
while chars < max_chars and i <= #s do
|
||||
while chars < LINE_LENGTH and i <= #s do
|
||||
local file = nil
|
||||
if charmap[s:sub(i, i)] ~= nil then
|
||||
file = charmap[s:sub(i, i)]
|
||||
@ -73,10 +136,13 @@ local generate_line = function(s, ypos)
|
||||
i = i + 2
|
||||
else
|
||||
print("[digilines] W: LCD: unknown symbol in '"..s.."' at "..i)
|
||||
if charmap[" "] ~= nil then
|
||||
file = charmap[" "]
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
if file ~= nil then
|
||||
width = width + CHAR_WIDTH
|
||||
width = width + CHAR_WIDTH + 1
|
||||
table.insert(parsed, file)
|
||||
chars = chars + 1
|
||||
end
|
||||
@ -84,7 +150,7 @@ local generate_line = function(s, ypos)
|
||||
width = width - 1
|
||||
|
||||
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
|
||||
texture = texture..":"..xpos..","..ypos.."="..parsed[ii]..".png"
|
||||
xpos = xpos + CHAR_WIDTH + 1
|
||||
@ -93,8 +159,8 @@ local generate_line = function(s, ypos)
|
||||
end
|
||||
|
||||
local generate_texture = function(lines)
|
||||
local texture = "[combine:"..LCD_WITH.."x"..LCD_WITH
|
||||
local ypos = 16
|
||||
local texture = "[combine:"..LCD_WIDTH.."x"..LCD_WIDTH
|
||||
local ypos = math.floor((LCD_WIDTH - LINE_HEIGHT*NUMBER_OF_LINES) / 2)
|
||||
for i = 1, #lines do
|
||||
texture = texture..generate_line(lines[i], ypos)
|
||||
ypos = ypos + LINE_HEIGHT
|
||||
@ -128,16 +194,59 @@ local clearscreen = function(pos)
|
||||
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 lcd_info = lcds[minetest.get_node(pos).param2]
|
||||
if lcd_info == nil then return end
|
||||
local text = minetest.add_entity(
|
||||
{x = pos.x + lcd_info.delta.x,
|
||||
y = pos.y + lcd_info.delta.y,
|
||||
z = pos.z + lcd_info.delta.z}, "digilines_lcd:text")
|
||||
text:setyaw(lcd_info.yaw or 0)
|
||||
--* text:setpitch(lcd_info.yaw or 0)
|
||||
return text
|
||||
local entity = get_entity(pos)
|
||||
if entity then
|
||||
set_texture(entity)
|
||||
rotate_text(pos)
|
||||
end
|
||||
end
|
||||
|
||||
local spawn_entity = function(pos)
|
||||
if not get_entity(pos) then
|
||||
minetest.add_entity(pos, "digilines_lcd:text")
|
||||
rotate_text(pos)
|
||||
end
|
||||
end
|
||||
|
||||
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("infotext", msg)
|
||||
clearscreen(pos)
|
||||
|
||||
if msg ~= "" then
|
||||
prepare_writing(pos)
|
||||
end
|
||||
@ -165,30 +274,34 @@ minetest.register_node("digilines:lcd", {
|
||||
inventory_image = "lcd_lcd.png",
|
||||
wield_image = "lcd_lcd.png",
|
||||
tiles = {"lcd_anyside.png"},
|
||||
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
light_source = 6,
|
||||
paramtype2 = "wallmounted",
|
||||
node_box = lcd_box,
|
||||
selection_box = lcd_box,
|
||||
groups = {choppy = 3, dig_immediate = 2},
|
||||
|
||||
after_place_node = function (pos)
|
||||
after_place_node = function(pos)
|
||||
local param2 = minetest.get_node(pos).param2
|
||||
if param2 == 0 or param2 == 1 then
|
||||
minetest.add_node(pos, {name = "digilines:lcd", param2 = 3})
|
||||
end
|
||||
prepare_writing (pos)
|
||||
spawn_entity(pos)
|
||||
prepare_writing(pos)
|
||||
end,
|
||||
|
||||
on_construct = function(pos)
|
||||
reset_meta(pos)
|
||||
on_construct = reset_meta,
|
||||
on_destruct = clearscreen,
|
||||
on_punch = function(pos, _, puncher, _)
|
||||
if minetest.is_player(puncher) then
|
||||
spawn_entity(pos)
|
||||
end
|
||||
end,
|
||||
|
||||
on_destruct = function(pos)
|
||||
clearscreen(pos)
|
||||
on_rotate = function(pos, _, _, mode, new_param2)
|
||||
if mode ~= screwdriver.ROTATE_FACE then
|
||||
return false
|
||||
end
|
||||
rotate_text(pos, new_param2)
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, _, fields, sender)
|
||||
local name = sender:get_player_name()
|
||||
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)
|
||||
end
|
||||
end,
|
||||
|
||||
digiline =
|
||||
{
|
||||
digilines = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
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", {
|
||||
collisionbox = { 0, 0, 0, 0, 0, 0 },
|
||||
visual = "upright_sprite",
|
||||
textures = {},
|
||||
|
||||
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
|
||||
on_activate = set_texture,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "digilines:lcd 2",
|
||||
recipe = {
|
||||
{"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"}
|
||||
}
|
||||
})
|
||||
|
@ -39,7 +39,7 @@ minetest.register_node("digilines:lightsensor", {
|
||||
groups = {dig_immediate=2},
|
||||
selection_box = lsensor_selbox,
|
||||
node_box = lsensor_nodebox,
|
||||
digiline =
|
||||
digilines =
|
||||
{
|
||||
receptor = {},
|
||||
effector = {
|
||||
|
5
mod.conf
5
mod.conf
@ -1 +1,6 @@
|
||||
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.
|
||||
"""
|
||||
|
2
rtc.lua
2
rtc.lua
@ -35,7 +35,7 @@ minetest.register_node("digilines:rtc", {
|
||||
groups = {dig_immediate=2},
|
||||
selection_box = rtc_selbox,
|
||||
node_box = rtc_nodebox,
|
||||
digiline =
|
||||
digilines =
|
||||
{
|
||||
receptor = {},
|
||||
effector = {
|
||||
|
@ -90,7 +90,7 @@ for zmy=0, 1 do
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
digiline =
|
||||
digilines =
|
||||
{
|
||||
wire =
|
||||
{
|
||||
|
@ -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)
|
||||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].digiline then
|
||||
-- need to make sure that node exists (unknown nodes!)
|
||||
local function check_and_update(pos, node)
|
||||
if digilines.getspec(node) then
|
||||
digilines.update_autoconnect(pos)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
minetest.register_on_placenode(check_and_update)
|
||||
minetest.register_on_dignode(check_and_update)
|
||||
|
||||
function digilines.update_autoconnect(pos, secondcall)
|
||||
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)
|
||||
end
|
||||
|
||||
local def = minetest.registered_nodes[minetest.get_node(pos).name]
|
||||
local digilinespec = def and def.digiline
|
||||
local digilinespec = digilines.getspec(minetest.get_node(pos))
|
||||
if not (digilinespec and digilinespec.wire and
|
||||
digilinespec.wire.use_autoconnect) then
|
||||
return nil
|
||||
|
Reference in New Issue
Block a user