Compare commits
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
4ed6d10a75 | |||
caa7ff6677 | |||
e77c1e7df1 | |||
7d4895d5d4 | |||
7c343b0ce1 | |||
d40edf79df | |||
982cfa1fda | |||
9dc03641f4 | |||
9ba4e3d308 | |||
98f364c0cc | |||
f781039f43 | |||
d480fee891 | |||
722ff851dd | |||
fa4f7a949c | |||
9c2b980938 | |||
f5b2521e4b | |||
d35f7f12c4 | |||
b36dd31f0f | |||
f03cd02854 | |||
5a04699b3e | |||
a055b5045a | |||
7a5cc43280 | |||
660bd62528 | |||
327c96cba8 | |||
0aa935c271 | |||
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
@ -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,12 +1,19 @@
|
||||
|
||||
read_globals = {
|
||||
"vector",
|
||||
"screwdriver",
|
||||
"minetest",
|
||||
"default",
|
||||
"pipeworks",
|
||||
"dump",
|
||||
"VoxelArea",
|
||||
"ItemStack",
|
||||
}
|
||||
|
||||
globals = {
|
||||
"digilines",
|
||||
"tubelib",
|
||||
"tubelib2",
|
||||
"mcl_sounds",
|
||||
"mcl_formspec"
|
||||
}
|
||||
|
@ -1,62 +0,0 @@
|
||||
Basic idea: place this chest down at the end of a pipeline. Feed stuff into it. Attach a luacontroller to it with a digiline in between. Program the luacontroller to turn on a wire to stop the pipeline when the chest is full. Don't put more than one kind of item down the pipeline into the chest, unless you want weird tricksy behavior.
|
||||
|
||||
...[F]===[C]--{L}
|
||||
|__________|
|
||||
[B]
|
||||
F: filter
|
||||
B: blinky plant
|
||||
C: digiline chest
|
||||
L: luacontroller
|
||||
==: pipe
|
||||
__: mesecons
|
||||
--: digiline
|
||||
|
||||
Make sure you use mem in the luacontroller to keep the mesecon wire on indefinitely after the chest signals full. When it signals "take" someone took an item out and you can start the pipeline up again.
|
||||
This may be a bad idea if they only take half a stack or something. Oh well.
|
||||
|
||||
When you put something in, you get a "put" to indicate it's OK (sort of (still buggy)) and can fit inside the chest. When you get a "lost", that means it won't fit inside the chest, and will bounce, possibly popping out. When you get a "overflow" that means you tried to add something to a chest that just filled up, and the stack will bounce. When you get a "full" that means the chest just filled up.
|
||||
|
||||
"Filled up" for all intents and purposes, means taking the type of whatever just went into the chest, and seeing if at least 1 of that type can be added to the chest. If it cannot, then it's filled up. That means a chest full of stacks of 30 copper and one spot of 98 gold, and you add one gold to that, it will say it's full. Add 30 copper to it, and it won't.
|
||||
|
||||
Generally you'll be working with full stacks in pipeworks. Even the non-mese filters will accumulate items in a chest, and not spread them out into multiple stacks. And generally your destination chests will each have one of any kind of item. So all you have to worry about is when you've got 99 everywhere, and one space possibly free. A mese filter can fail on that in fact, for instance if you have room for 45 gold, and it tries to push a stack of 50 gold. All 50 will bounce! The code for chests isn't smart enough to add the 45 and bounce the leftover 5. So only use mese filters when you're sure a full stack has accumulated for the filter to push, and there are no partial stacks at the destination.
|
||||
|
||||
For some reason when an "overflow" happens, when it checks if the stack can fit inside the chest, the answer is yes it can fit! Then it doesn't. Sort of buggy.
|
||||
|
||||
itemstack - the qualified name, a space, then the amount. If the space and amount are ommitted, that represents just 1.
|
||||
|
||||
examples: "default:chest 33" "default:coal" "default:axe_diamond"
|
||||
|
||||
bounce
|
||||
- when an item cannot enter the destination container it will travel back up the tube it came from, going along alternate routes if they exist, but otherwise popping out of the tube.
|
||||
|
||||
popping out
|
||||
- the little thingy travelling through the tube disappears, and an entity of the item appears, as if it was just dropped. This entity can expire, losing your items permanently!
|
||||
|
||||
|
||||
The messages are:
|
||||
|
||||
|
||||
put <itemstack>
|
||||
- this means that the inventory claimed it could fit that stack of items inside it. Inventories lie. But with the above caveats this is quite usable.
|
||||
lost <itemstack>
|
||||
- the stack couldn't fit in the chest and bounces.
|
||||
full <itemstack> <count>
|
||||
- the chest has been filled. The itemstack is what filled it. count is what's leftover.
|
||||
until chests can suck up part of a stack and not bounce the whole stack, that count will
|
||||
always be 0
|
||||
overflow <itemstack> <count>
|
||||
- generally this is the same as lost, except that the "put" event happened, meaning that the
|
||||
itemstack was allowed in the chest.
|
||||
- this will happen with two stacks in rapid succession, the first filling the chest, and the
|
||||
second bouncing. the <count> in this case will be the amount of the second.
|
||||
- overflow can't happen with two different kinds of items, you'll get a put, maybe full, then lost for the second kind
|
||||
|
||||
Tricky situation:
|
||||
if you have a blank spot and put say 82 torches down your pipeline, followed by 99 coal, the 82 torches will go in the chest, and the chest will see that 1 more torch can fit since that would only go to 83. Since 1 more torch can fit, no "full" message will fire off. Then when the coal hits the chest, the "fail" message will fire and the coal will bounce out. The chest couldn't predict that coal would be coming next, so it couldn't know that the chest is full, for coal, while not full for torches.
|
||||
|
||||
TODO:
|
||||
- make chest.lua a mixin that gets both default and locked chests
|
||||
- digiline aware furnaces
|
||||
- digiline aware technic machines, grinders, alloy furnaces, etc
|
||||
- the pipes going into the chests don't snap to the pipe holes in the digiline chests. They still act fine as pipeworks destinations though.
|
||||
- digiline chests seem to be immune to filters. But it's late and I'm shipping this. Someone else can figure out why the chests aren't acting like pipeworks chests, despite cloning the pipeworks chest's object. Oh who am I kidding. I'll do it myself I guess, once I've lost hope of aid again.
|
@ -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.
|
123
docs/chest.md
Normal file
@ -0,0 +1,123 @@
|
||||
Digiline Chest is a chest which allows to notify about different inventory actions via Digiline messages.
|
||||
|
||||
## Available messages
|
||||
|
||||
```
|
||||
{
|
||||
action = "empty"
|
||||
}
|
||||
```
|
||||
The message is sent when the chest has just become empty.
|
||||
|
||||
----------------------------
|
||||
|
||||
```
|
||||
{
|
||||
action = "full",
|
||||
stack = <stack>
|
||||
}
|
||||
```
|
||||
The message is sent when the chest has become full for a particular item specified in `<stack>`.
|
||||
|
||||
----------------------------
|
||||
|
||||
```
|
||||
{
|
||||
action = "toverflow",
|
||||
stack = <stack>,
|
||||
side = <side>
|
||||
}
|
||||
```
|
||||
The message is sent when the chest cannot accept a `<stack>` delivered by tube connected to the `<side>` because the chest is full.
|
||||
|
||||
----------------------------
|
||||
|
||||
```
|
||||
{
|
||||
action = "tput",
|
||||
stack = <stack>,
|
||||
to_slot = <input slot>,
|
||||
side = <side>
|
||||
}
|
||||
```
|
||||
The message is sent when the chest accepts a `<stack>`, delivered by tube which is connected to `<side>`, to slot `<input slot>`.
|
||||
|
||||
----------------------------
|
||||
|
||||
```
|
||||
{
|
||||
action = "ttake",
|
||||
stack = <stack>,
|
||||
from_slot = <output slot>,
|
||||
side = <side>
|
||||
}
|
||||
```
|
||||
The message is sent when a tube connected to the chest to `<side>` extracts a `<stack>` from slot `<output slot>`.
|
||||
|
||||
----------------------------
|
||||
|
||||
```
|
||||
{
|
||||
action = "umove",
|
||||
stack = <stack>,
|
||||
from_slot = <output slot>,
|
||||
to_slot = <input slot>
|
||||
}
|
||||
```
|
||||
The message is sent when user moves `<stack>` from `<output slot>` to `<input slot>` within the chest.
|
||||
|
||||
----------------------------
|
||||
|
||||
```
|
||||
{
|
||||
action = "uswap",
|
||||
x_stack = <stack1>,
|
||||
x_slot = <slot1>,
|
||||
y_stack = <stack2>,
|
||||
y_slot = <slot2>
|
||||
}
|
||||
```
|
||||
The message is sent when user swaps `<stack1>` in `<slot1>` with `<stack2>` in `<slot2>` within the chest.
|
||||
|
||||
----------------------------
|
||||
|
||||
```
|
||||
{
|
||||
action = "utake",
|
||||
stack = <stack>,
|
||||
from_slot = <output slot>
|
||||
}
|
||||
```
|
||||
The message is sent when user takes `<stack>` from `<output slot>` in the chest.
|
||||
|
||||
----------------------------
|
||||
|
||||
```
|
||||
{
|
||||
action = "uput",
|
||||
stack = <stack>,
|
||||
to_slot = <input slot>
|
||||
}
|
||||
```
|
||||
The message is sent when user puts `<stack>` to the chest to `<input slot>`
|
||||
|
||||
### Fields used within the messages
|
||||
|
||||
| Field | Description |
|
||||
| ----- | ----------- |
|
||||
| `<stack>` | A table which contains data about the stack, and corresponds to the format returned by the :to_table() method of ItemStack (check the Minetest API documentation). |
|
||||
| `<input slot>`, `<output slot>`, `<slot1>`, `<slot2>` | The index of the corresponding slot starting from 1. |
|
||||
| `<side>` | A vector represented as a table of format `{ x = <x>, y = <y>, z = <z> }` which represent the direction from which the tube is connected to the chest. |
|
||||
|
||||
## Additional information
|
||||
|
||||
The inventory is also compatible with [`tubelib`](https://github.com/joe7575/techpack/tree/master/tubelib), which generally works in the same way as [`pipeworks`](https://gitlab.com/VanessaE/pipeworks) but transfers happen immediately and do not "bounce". This means that the messages should be identical to the messages sent by [`pipeworks`](https://gitlab.com/VanessaE/pipeworks), except that items will not send the "toverflow" message when they cannot fit.
|
||||
One oddity is that "ttake" messages will be asynchronous because, if an item does not fit in the chest, the event will need to be canceled. This means that it is possible (though highly unlikely) to recieve a "tput" message into a slot which you have not yet recieved a "ttake" message for, there will not actually be two stacks in the same slot, though it may briefly appear that way until the "ttake" message is recieved.
|
||||
|
||||
## To do
|
||||
|
||||
- make chest.lua a mixin that gets both default and locked chests
|
||||
- digiline aware furnaces
|
||||
- digiline aware technic machines, grinders, alloy furnaces, etc
|
||||
- the pipes going into the chests don't snap to the pipe holes in the digiline chests. They still act fine as pipeworks destinations though.
|
||||
- digiline chests seem to be immune to filters. But it's late and I'm shipping this. Someone else can figure out why the chests aren't acting like pipeworks chests, despite cloning the pipeworks chest's object. Oh who am I kidding. I'll do it myself I guess, once I've lost hope of aid again.
|
41
init.lua
@ -1,5 +1,16 @@
|
||||
|
||||
digilines = {}
|
||||
digilines.S = minetest.get_translator("digilines")
|
||||
|
||||
-- formspec escape translation
|
||||
digilines.FS = function (...)
|
||||
return minetest.formspec_escape(digilines.S(...))
|
||||
end
|
||||
|
||||
digilines.mcl = minetest.get_modpath("mcl_core")
|
||||
|
||||
-- sounds check
|
||||
if minetest.get_modpath("default") then digilines.sounds = default end
|
||||
if digilines.mcl then digilines.sounds = mcl_sounds end
|
||||
|
||||
-- Backwards compatibility code.
|
||||
-- We define a proxy table whose methods can be called with the
|
||||
@ -55,29 +66,41 @@ function digilines.receptor_send(pos, rules, channel, msg)
|
||||
end
|
||||
end
|
||||
|
||||
local fiber = "mesecons_materials:fiber"
|
||||
local insulated = "mesecons_insulated:insulated_off"
|
||||
local gold_ingot = "default:gold_ingot"
|
||||
|
||||
if digilines.mcl then
|
||||
gold_ingot = "mcl_core:gold_ingot"
|
||||
-- MCL dont support mesecons insulated
|
||||
if not minetest.get_modpath("mesecons_insulated") then
|
||||
insulated = "mesecons:redstone"
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'digilines:wire_std_00000000 2',
|
||||
recipe = {
|
||||
{'mesecons_materials:fiber', 'mesecons_materials:fiber', 'mesecons_materials:fiber'},
|
||||
{'mesecons_insulated:insulated_off', 'mesecons_insulated:insulated_off', 'default:gold_ingot'},
|
||||
{'mesecons_materials:fiber', 'mesecons_materials:fiber', 'mesecons_materials:fiber'},
|
||||
{fiber, fiber, fiber},
|
||||
{insulated, insulated, gold_ingot},
|
||||
{fiber, fiber, fiber},
|
||||
}
|
||||
})
|
||||
|
||||
-- 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
@ -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
|
||||
|
425
inventory.lua
@ -1,36 +1,162 @@
|
||||
local S = digilines.S
|
||||
|
||||
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
|
||||
|
||||
local tube_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 ret
|
||||
end
|
||||
|
||||
local tube_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
|
||||
|
||||
local formspec_header = ""
|
||||
|
||||
if minetest.get_modpath("mcl_formspec") then
|
||||
formspec_header = mcl_formspec.get_itemslot_bg(0,1,8,4)..
|
||||
mcl_formspec.get_itemslot_bg(0,6,8,4)
|
||||
end
|
||||
|
||||
minetest.register_alias("digilines_inventory:chest", "digilines:chest")
|
||||
|
||||
minetest.register_node("digilines:chest", {
|
||||
description = "Digiline Chest",
|
||||
description = S("Digiline Chest"),
|
||||
tiles = {
|
||||
"default_chest_top.png"..tubeconn,
|
||||
"default_chest_top.png"..tubeconn,
|
||||
@ -41,18 +167,19 @@ minetest.register_node("digilines:chest", {
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
legacy_facedir_simple = true,
|
||||
groups = {choppy=2, oddly_breakable_by_hand=2, tubedevice=1, tubedevice_receiver=1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
groups = {choppy=2, oddly_breakable_by_hand=2, tubedevice=1, tubedevice_receiver=1, axey=1, handy=1},
|
||||
is_ground_content = false,
|
||||
sounds = digilines.sounds.node_sound_wood_defaults(),
|
||||
_mcl_blast_resistance = 1,
|
||||
_mcl_hardness = 0.8,
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "Digiline Chest")
|
||||
meta:set_string("infotext", S("Digiline Chest"))
|
||||
meta:set_string("formspec", "size[8,10]"..
|
||||
((default and default.gui_bg) or "")..
|
||||
((default and default.gui_bg_img) or "")..
|
||||
((default and default.gui_slots) or "")..
|
||||
"label[0,0;Digiline Chest]"..
|
||||
formspec_header..
|
||||
"label[0,0;" .. S("Digiline Chest") .. "]" ..
|
||||
"list[current_name;main;0,1;8,4;]"..
|
||||
"field[2,5.5;5,1;channel;Channel;${channel}]"..
|
||||
"field[2,5.5;5,1;channel;" .. S("Channel") .. ";${channel}]"..
|
||||
((default and default.get_hotbar_bg) and default.get_hotbar_bg(0,6) or "")..
|
||||
"list[current_player;main;0,6;8,4;]"..
|
||||
"listring[]")
|
||||
@ -67,14 +194,13 @@ minetest.register_node("digilines:chest", {
|
||||
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
|
||||
minetest.record_protection_violation(pos, name)
|
||||
return
|
||||
end
|
||||
if fields.channel ~= nil then
|
||||
minetest.get_meta(pos):set_string("channel",fields.channel)
|
||||
end
|
||||
end,
|
||||
digiline = {
|
||||
digilines = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = function() end
|
||||
@ -86,69 +212,216 @@ 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))
|
||||
end
|
||||
return leftover
|
||||
can_insert = tube_can_insert,
|
||||
insert_object = tube_insert_object,
|
||||
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
|
||||
})
|
||||
|
||||
if minetest.global_exists("tubelib") then
|
||||
local speculative_pull = nil
|
||||
local pull_succeeded = function(passed_speculative_pull)
|
||||
if passed_speculative_pull.canceled then return end
|
||||
|
||||
send_message(passed_speculative_pull.pos, "ttake", passed_speculative_pull.taken,
|
||||
passed_speculative_pull.index, nil, vector.multiply(passed_speculative_pull.dir, -1))
|
||||
check_empty(passed_speculative_pull.pos)
|
||||
end
|
||||
local function tube_side(pos, side)
|
||||
if side == "U" then return {x=0,y=-1,z=0}
|
||||
elseif side == "D" then return {x=0,y=1,z=0}
|
||||
end
|
||||
local param2 = minetest.get_node(pos).param2
|
||||
return vector.multiply(
|
||||
minetest.facedir_to_dir(
|
||||
tubelib2.side_to_dir(side, param2) - 1),
|
||||
-1)
|
||||
end
|
||||
tubelib.register_node("digilines:chest", {}, {
|
||||
on_pull_stack = function(pos, side, _)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
for i, stack in pairs(inv:get_list("main")) do
|
||||
if not stack:is_empty() then
|
||||
speculative_pull = {
|
||||
canceled = false,
|
||||
pos = pos,
|
||||
taken = stack,
|
||||
index = i,
|
||||
dir = tube_side(pos, side)
|
||||
}
|
||||
minetest.after(0, pull_succeeded, speculative_pull)
|
||||
inv:set_stack("main", i, nil)
|
||||
return stack
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
on_pull_item = function(pos, side, _)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
for i, stack in pairs(inv:get_list("main")) do
|
||||
if not stack:is_empty() then
|
||||
local taken = stack:take_item(1)
|
||||
speculative_pull = {
|
||||
canceled = false,
|
||||
pos = pos,
|
||||
taken = taken,
|
||||
index = i,
|
||||
dir = tube_side(pos, side)
|
||||
}
|
||||
minetest.after(0, pull_succeeded, speculative_pull)
|
||||
inv:set_stack("main", i, stack)
|
||||
return taken
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
on_push_item = function(pos, side, item, _)
|
||||
local dir_vec = tube_side(pos, side)
|
||||
if not tube_can_insert(pos, nil, item, dir_vec) then
|
||||
return false
|
||||
end
|
||||
tube_insert_object(pos, nil, item, dir_vec)
|
||||
return true
|
||||
end,
|
||||
on_unpull_item = function(pos, _, item, _)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
if not inv:room_for_item("main", item) then
|
||||
return false
|
||||
end
|
||||
|
||||
local existing_stack = inv:get_stack("main", speculative_pull.index)
|
||||
local leftover = existing_stack:add_item(item)
|
||||
if not leftover:is_empty() then
|
||||
return false
|
||||
end
|
||||
|
||||
inv:set_stack("main", speculative_pull.index, existing_stack)
|
||||
|
||||
-- Cancel speculative pull
|
||||
-- this on_unpull_item callback should be called
|
||||
-- immediately after on_pull_item if it fails
|
||||
-- so this should be procedural
|
||||
speculative_pull.canceled = true
|
||||
speculative_pull = nil
|
||||
return true
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local chest = "default:chest"
|
||||
|
||||
if minetest.get_modpath("mcl_chests") then
|
||||
chest = "mcl_chests:chest"
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "digilines:chest",
|
||||
recipe = {"default:chest", "digilines:wire_std_00000000"}
|
||||
recipe = {chest, "digilines:wire_std_00000000"}
|
||||
})
|
||||
|
270
lcd.lua
@ -1,3 +1,6 @@
|
||||
local S = digilines.S
|
||||
local FS = digilines.FS
|
||||
|
||||
--* parts are currently not possible because you cannot set the pitch of an entity from lua
|
||||
|
||||
-- Font: 04.jp.org
|
||||
@ -5,7 +8,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 +23,7 @@ else
|
||||
end
|
||||
|
||||
-- CONSTANTS
|
||||
local LCD_WITH = 100
|
||||
local LCD_WIDTH = 100
|
||||
local LCD_PADDING = 8
|
||||
|
||||
local LINE_LENGTH = 12
|
||||
@ -30,31 +32,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 +129,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 +139,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 +153,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 +162,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
|
||||
@ -108,14 +177,17 @@ local lcds = {
|
||||
-- on ground
|
||||
--* [1] = {delta = {x = 0, y =-0.4, z = 0}, pitch = math.pi / 2},
|
||||
-- sides
|
||||
[2] = {delta = {x = 0.437, y = 0, z = 0}, yaw = math.pi / -2},
|
||||
[3] = {delta = {x = -0.437, y = 0, z = 0}, yaw = math.pi / 2},
|
||||
[4] = {delta = {x = 0, y = 0, z = 0.437}, yaw = 0},
|
||||
[5] = {delta = {x = 0, y = 0, z = -0.437}, yaw = math.pi},
|
||||
|
||||
-- Note: 0.437 is on the surface but we need some space to avoid
|
||||
-- z-fighting in distant places (e.g. 30000,10,0)
|
||||
[2] = {delta = {x = 0.43, y = 0, z = 0}, yaw = math.pi / -2},
|
||||
[3] = {delta = {x = -0.43, y = 0, z = 0}, yaw = math.pi / 2},
|
||||
[4] = {delta = {x = 0, y = 0, z = 0.43}, yaw = 0},
|
||||
[5] = {delta = {x = 0, y = 0, z = -0.43}, yaw = math.pi},
|
||||
}
|
||||
|
||||
local reset_meta = function(pos)
|
||||
minetest.get_meta(pos):set_string("formspec", "field[channel;Channel;${channel}]")
|
||||
minetest.get_meta(pos):set_string("formspec", "field[channel;"..FS("Channel")..";${channel}]")
|
||||
end
|
||||
|
||||
local clearscreen = function(pos)
|
||||
@ -128,16 +200,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)
|
||||
@ -145,9 +260,11 @@ local on_digiline_receive = function(pos, _, channel, msg)
|
||||
local setchan = meta:get_string("channel")
|
||||
if setchan ~= channel then return end
|
||||
|
||||
if type(msg) ~= "string" and type(msg) ~= "number" then return end
|
||||
|
||||
meta:set_string("text", msg)
|
||||
meta:set_string("infotext", msg)
|
||||
clearscreen(pos)
|
||||
|
||||
if msg ~= "" then
|
||||
prepare_writing(pos)
|
||||
end
|
||||
@ -161,73 +278,90 @@ local lcd_box = {
|
||||
minetest.register_alias("digilines_lcd:lcd", "digilines:lcd")
|
||||
minetest.register_node("digilines:lcd", {
|
||||
drawtype = "nodebox",
|
||||
description = "Digiline LCD",
|
||||
description = S("Digiline 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)
|
||||
is_ground_content = false,
|
||||
_mcl_blast_resistance = 1,
|
||||
_mcl_hardness = 0.8,
|
||||
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
|
||||
minetest.record_protection_violation(pos, name)
|
||||
return
|
||||
end
|
||||
if (fields.channel) then
|
||||
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
|
||||
initial_properties = {
|
||||
collisionbox = { 0, 0, 0, 0, 0, 0 },
|
||||
visual = "upright_sprite",
|
||||
textures = {},
|
||||
},
|
||||
on_activate = set_texture,
|
||||
})
|
||||
|
||||
local steel_ingot = "default:steel_ingot"
|
||||
local glass = "default:glass"
|
||||
local lightstone = "mesecons_lightstone:lightstone_green_off"
|
||||
|
||||
if digilines.mcl then
|
||||
steel_ingot = "mcl_core:iron_ingot"
|
||||
glass = "mcl_core:glass"
|
||||
lightstone = "mesecons_lightstone:lightstone_off"
|
||||
end
|
||||
|
||||
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"},
|
||||
{"default:glass","default:glass","default:glass"}
|
||||
{steel_ingot, "digilines:wire_std_00000000", steel_ingot},
|
||||
{lightstone, lightstone, lightstone},
|
||||
{glass, glass, glass}
|
||||
}
|
||||
})
|
||||
|
@ -1,3 +1,6 @@
|
||||
local S = digilines.S
|
||||
local FS = digilines.FS
|
||||
|
||||
local GET_COMMAND = "GET"
|
||||
|
||||
local lsensor_nodebox =
|
||||
@ -31,15 +34,18 @@ end
|
||||
|
||||
minetest.register_alias("digilines_lightsensor:lightsensor", "digilines:lightsensor")
|
||||
minetest.register_node("digilines:lightsensor", {
|
||||
description = "Digiline Lightsensor",
|
||||
description = S("Digiline Lightsensor"),
|
||||
drawtype = "nodebox",
|
||||
tiles = {"digilines_lightsensor.png"},
|
||||
|
||||
paramtype = "light",
|
||||
groups = {dig_immediate=2},
|
||||
is_ground_content = false,
|
||||
_mcl_blast_resistance = 1,
|
||||
_mcl_hardness = 0.8,
|
||||
selection_box = lsensor_selbox,
|
||||
node_box = lsensor_nodebox,
|
||||
digiline =
|
||||
digilines =
|
||||
{
|
||||
receptor = {},
|
||||
effector = {
|
||||
@ -48,12 +54,11 @@ minetest.register_node("digilines:lightsensor", {
|
||||
},
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", "field[channel;Channel;${channel}]")
|
||||
meta:set_string("formspec", "field[channel;"..FS("Channel")..";${channel}]")
|
||||
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
|
||||
minetest.record_protection_violation(pos, name)
|
||||
return
|
||||
end
|
||||
if (fields.channel) then
|
||||
@ -61,3 +66,19 @@ minetest.register_node("digilines:lightsensor", {
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local steel_ingot = "default:steel_ingot"
|
||||
local glass = "default:glass"
|
||||
|
||||
if digilines.mcl then
|
||||
steel_ingot = "mcl_core:iron_ingot"
|
||||
glass = "mcl_core:glass"
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "digilines:lightsensor",
|
||||
recipe = {
|
||||
{glass, glass, glass},
|
||||
{steel_ingot, "digilines:wire_std_00000000", steel_ingot},
|
||||
}
|
||||
})
|
||||
|
23
locale/digilines.es.tr
Normal file
@ -0,0 +1,23 @@
|
||||
# textdomain: digilines
|
||||
|
||||
|
||||
### inventory.lua ###
|
||||
|
||||
Channel=Canal
|
||||
Digiline Chest=Cofre Digiline
|
||||
|
||||
### lcd.lua ###
|
||||
|
||||
Digiline LCD=LCD Digiline
|
||||
|
||||
### lightsensor.lua ###
|
||||
|
||||
Digiline Lightsensor=Sensor de Luz Digiline
|
||||
|
||||
### rtc.lua ###
|
||||
|
||||
Digiline Real Time Clock (RTC)=Reloj de Tiempo Real Digiline
|
||||
|
||||
### wire_std.lua ###
|
||||
|
||||
Digiline=Digiline
|
17
locale/digilines.zh_TW.tr
Normal file
@ -0,0 +1,17 @@
|
||||
# textdomain: digilines
|
||||
|
||||
### inventory.lua ###
|
||||
Digiline Chest=訊纜儲物箱
|
||||
Channel=頻道
|
||||
|
||||
### lcd.lua ###
|
||||
Digiline LCD=訊纜 LCD
|
||||
|
||||
### lightsensor.lua ###
|
||||
Digiline Lightsensor=訊纜光感應器
|
||||
|
||||
### rtc.lua ###
|
||||
Digiline Real Time Clock (RTC)=訊纜實時時鐘
|
||||
|
||||
### wire_std.lua ###
|
||||
Digiline=訊纜
|
23
locale/diglines.fr.tr
Normal file
@ -0,0 +1,23 @@
|
||||
# textdomain: digilines
|
||||
|
||||
|
||||
### inventory.lua ###
|
||||
|
||||
Channel=Canal
|
||||
Digiline Chest=Coffre de digiline
|
||||
|
||||
### lcd.lua ###
|
||||
|
||||
Digiline LCD=Ecran à cristaux liquides Digiline
|
||||
|
||||
### lightsensor.lua ###
|
||||
|
||||
Digiline Lightsensor=Capteur de lumière Digiline
|
||||
|
||||
### rtc.lua ###
|
||||
|
||||
Digiline Real Time Clock (RTC)=Horloge du temps réel Digiline (Rtc)
|
||||
|
||||
### wire_std.lua ###
|
||||
|
||||
Digiline=Digiline
|
23
locale/diglines.ru.tr
Normal file
@ -0,0 +1,23 @@
|
||||
# textdomain: digilines
|
||||
|
||||
|
||||
### inventory.lua ###
|
||||
|
||||
Channel=Канал
|
||||
Digiline Chest=Диджилайн-сундук
|
||||
|
||||
### lcd.lua ###
|
||||
|
||||
Digiline LCD=Диджилайн LCD
|
||||
|
||||
### lightsensor.lua ###
|
||||
|
||||
Digiline Lightsensor=Сенсор света диджилайн
|
||||
|
||||
### rtc.lua ###
|
||||
|
||||
Digiline Real Time Clock (RTC)=Часы реального времени диджилайн (RTC)
|
||||
|
||||
### wire_std.lua ###
|
||||
|
||||
Digiline=Диджилайн
|
23
locale/template.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# textdomain: digilines
|
||||
|
||||
|
||||
### inventory.lua ###
|
||||
|
||||
Channel=
|
||||
Digiline Chest=
|
||||
|
||||
### lcd.lua ###
|
||||
|
||||
Digiline LCD=
|
||||
|
||||
### lightsensor.lua ###
|
||||
|
||||
Digiline Lightsensor=
|
||||
|
||||
### rtc.lua ###
|
||||
|
||||
Digiline Real Time Clock (RTC)=
|
||||
|
||||
### wire_std.lua ###
|
||||
|
||||
Digiline=
|
6
mod.conf
@ -1 +1,7 @@
|
||||
name = digilines
|
||||
depends =
|
||||
optional_depends = mcl_core, mcl_sounds, default, tubelib, tubelib2
|
||||
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.
|
||||
"""
|
||||
|
40
rtc.lua
@ -1,3 +1,6 @@
|
||||
local S = digilines.S
|
||||
local FS = digilines.FS
|
||||
|
||||
local GET_COMMAND = "GET"
|
||||
|
||||
local rtc_nodebox =
|
||||
@ -26,16 +29,22 @@ end
|
||||
|
||||
minetest.register_alias("digilines_rtc:rtc", "digilines:rtc")
|
||||
minetest.register_node("digilines:rtc", {
|
||||
description = "Digiline Real Time Clock (RTC)",
|
||||
description = S("Digiline Real Time Clock (RTC)"),
|
||||
drawtype = "nodebox",
|
||||
tiles = {"digilines_rtc.png"},
|
||||
tiles = {
|
||||
"digilines_rtc.png", "digilines_rtc_bottom.png", "digilines_rtc.png",
|
||||
"digilines_rtc.png", "digilines_rtc.png", "digilines_rtc.png"
|
||||
},
|
||||
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
groups = {dig_immediate=2},
|
||||
is_ground_content = false,
|
||||
_mcl_blast_resistance = 1,
|
||||
_mcl_hardness = 0.8,
|
||||
selection_box = rtc_selbox,
|
||||
node_box = rtc_nodebox,
|
||||
digiline =
|
||||
digilines =
|
||||
{
|
||||
receptor = {},
|
||||
effector = {
|
||||
@ -44,12 +53,11 @@ minetest.register_node("digilines:rtc", {
|
||||
},
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", "field[channel;Channel;${channel}]")
|
||||
meta:set_string("formspec", "field[channel;"..FS("Channel")..";${channel}]")
|
||||
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
|
||||
minetest.record_protection_violation(pos, name)
|
||||
return
|
||||
end
|
||||
if (fields.channel) then
|
||||
@ -57,3 +65,25 @@ minetest.register_node("digilines:rtc", {
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local steel_ingot = "default:steel_ingot"
|
||||
local mese_crystal = "default:mese_crystal_fragment"
|
||||
local dye_black = "dye:black"
|
||||
|
||||
if digilines.mcl then
|
||||
steel_ingot = "mcl_core:iron_ingot"
|
||||
mese_crystal = "mesecons:redstone"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("mcl_dye") then
|
||||
dye_black = "mcl_dye:black"
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "digilines:rtc",
|
||||
recipe = {
|
||||
{"", dye_black, ""},
|
||||
{steel_ingot, mese_crystal, steel_ingot},
|
||||
{"", "digilines:wire_std_00000000", ""}
|
||||
}
|
||||
})
|
||||
|
BIN
textures/_sp.png
Before Width: | Height: | Size: 215 B After Width: | Height: | Size: 95 B |
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 250 B |
Before Width: | Height: | Size: 410 B After Width: | Height: | Size: 257 B |
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 194 B |
Before Width: | Height: | Size: 248 B After Width: | Height: | Size: 215 B |
BIN
textures/digilines_rtc_bottom.png
Normal file
After Width: | Height: | Size: 98 B |
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 132 B |
Before Width: | Height: | Size: 189 B After Width: | Height: | Size: 143 B |
@ -1,3 +1,5 @@
|
||||
local S = digilines.S
|
||||
|
||||
-- naming scheme: wire:(xp)(zp)(xm)(zm)_on/off
|
||||
-- The conditions in brackets define whether there is a digiline at that place or not
|
||||
-- 1 = there is one; 0 = there is none
|
||||
@ -36,7 +38,7 @@ for zmy=0, 1 do
|
||||
|
||||
if nodeid == "00000000" then
|
||||
groups = {dig_immediate = 3}
|
||||
wiredesc = "Digiline"
|
||||
wiredesc = S("Digiline")
|
||||
else
|
||||
groups = {dig_immediate = 3, not_in_creative_inventory = 1}
|
||||
end
|
||||
@ -90,7 +92,7 @@ for zmy=0, 1 do
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
digiline =
|
||||
digilines =
|
||||
{
|
||||
wire =
|
||||
{
|
||||
@ -107,6 +109,7 @@ for zmy=0, 1 do
|
||||
fixed = nodebox
|
||||
},
|
||||
groups = groups,
|
||||
is_ground_content = false,
|
||||
walkable = false,
|
||||
stack_max = 99,
|
||||
drop = "digilines:wire_std_00000000"
|
||||
|
@ -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
|
||||
|