32 Commits

Author SHA1 Message Date
e1cffdedbf Gates: Modify appearance (#515) 2020-05-27 23:31:51 +02:00
d3aedd2b98 Speed up search for objects to be moved in MVPS (#512) 2020-04-25 00:33:00 +02:00
DS
68c1729990 Refactor actionqueue.lua (#501)
Reduce actionqueue complexity, thus faster execution
Improve code style
Add documentation/explanations
2020-01-06 21:59:02 +01:00
9b58f8db29 FPGA: Add 'unary' value to talbe. Document 2019-11-10 11:35:02 +01:00
7784b13da5 FPGA: Add NOR operand 2019-11-10 11:35:02 +01:00
0dd530312b FPGA: Remove formspec from metadata 2019-11-10 11:35:02 +01:00
e78bbd6f98 FPGA: Unify actions in single table 2019-11-10 11:35:02 +01:00
bfd952b51a Delayer: Combine shared definitions, add protection (#490) 2019-11-04 20:00:43 +01:00
b7873e8e02 Code tidy: Remove redundant params (#486) 2019-10-05 14:13:59 +02:00
d6b2a39c99 Set mvps_protection_mode default to the documented value (#484, base: #466) 2019-09-24 17:25:07 +03:00
1b54011b68 Use table.copy in mesecons.tablecopy
mesecons.tablecopy didn’t support recursive tables, while Minetest table.copy works well for them.
2019-09-23 19:50:04 +02:00
15e743629e Respect protection in MVPS (#466) 2019-09-20 23:04:52 +00:00
1bf862f932 Use modpack.conf instead of legacy modpack.txt (#475) 2019-08-25 23:36:21 +03:00
1a9704f184 Add digiline commands for operating node detector (#472) 2019-08-21 23:52:33 +03:00
8baa789eb1 Optimize images (#464)
Recompress losslessly using `optipng` and `advpng`
2019-06-27 22:33:11 +03:00
b0158f5674 Too many glasses in noteblock 2019-04-10 23:00:58 +02:00
DS
073c92d487 Revert "Fix sticky pistons (#403)" (#458)
This reverts commit d8f82e6771.
2019-03-17 10:29:04 +01:00
737f366741 LuaC: add lightweight interrupts (#449) 2018-12-29 23:48:32 +03:00
302a28934d Document inactive block behaviour (#447) 2018-12-21 22:10:08 +03:00
6e767a6c76 Make sticky piston stick falling things as well (#436) 2018-12-21 22:02:57 +03:00
9d239cbfff Fix typos (#442) 2018-12-09 16:38:23 +03:00
d3cabedbb0 Prevent long error message from covering the button 2018-12-09 13:59:49 +01:00
df4e880d8b Fix crash in microcontroller (#439)
Add check nil var with bug from crash server.
fixes #438
2018-11-06 09:48:44 +01:00
45bbd9f7e3 Don’t damage unloaded blocks (#435) 2018-10-29 23:58:07 +03:00
028c290cd7 Mark 'code' as private as well
Saves on bandwidth, however the code is still accessible via the formspec.
2018-09-18 13:01:18 +02:00
8808bb8911 Mark LuaController memory as private
If LuaControllers handle sensitive information, hacked clients could get this information from the LuaController. Marking the memory as private fixes this and saves a small amount of bandwidth.
2018-09-18 13:01:18 +02:00
fa040eb085 Fix vertical movestone textures (#430) 2018-09-09 00:35:15 +03:00
a4f5ae5b89 Remove tiny (+0.001) selection box oversize 2018-09-07 17:31:49 +02:00
9e6eac4285 Make insulated wires’ selection box fit in the node 2018-09-07 17:31:49 +02:00
c73b451f9b Update wiki links (#432)
wiki.minetest.net is the official Minetest wiki
2018-08-29 00:45:46 +03:00
DS
9ff2329253 Noteblock: use new fire sounds (#385) 2018-08-26 23:13:47 +03:00
444cd0f2f1 Replace usage of default.LIGHT_MAX with minetest.LIGHT_MAX
It was moved a long time ago and the former is not guaranteed to be available.
fixes #424
2018-07-24 21:30:04 +02:00
327 changed files with 675 additions and 356 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
*~ *~
*.patch
*.diff

View File

@ -24,7 +24,7 @@ Go get it!
[DOWNLOAD IT NOW](https://github.com/minetest-mods/mesecons/archive/master.zip) [DOWNLOAD IT NOW](https://github.com/minetest-mods/mesecons/archive/master.zip)
Now go ahead and install it like any other Minetest mod. Don't know how? Check out [the wonderful page about it](http://wiki.minetest.com/wiki/Mods) over at the Minetest Wiki. For your convenience, here's a quick summary: Now go ahead and install it like any other Minetest mod. Don't know how? Check out [the wonderful page about it](https://wiki.minetest.net/Mods) over at the official Minetest Wiki. For your convenience, here's a quick summary:
1. If Mesecons is still in a ZIP file, extract the folder inside to somewhere on the computer. 1. If Mesecons is still in a ZIP file, extract the folder inside to somewhere on the computer.
2. Make sure that when you open the folder, you can directly find `README.md` in the listing. If you just see another folder, move that folder up one level and delete the old one. 2. Make sure that when you open the folder, you can directly find `README.md` in the listing. If you just see another folder, move that folder up one level and delete the old one.
@ -43,6 +43,8 @@ Or maybe a [comprehensive reference](http://mesecons.net/items.html) is your sty
An overview for the very newest of new beginners? How does [this one](http://uberi.mesecons.net/projects/MeseconsBasics/index.html) look? An overview for the very newest of new beginners? How does [this one](http://uberi.mesecons.net/projects/MeseconsBasics/index.html) look?
There is also a [wiki page](https://wiki.minetest.net/Mods/Mesecons) dedicated to this mod.
Want to get more into building? Why not check out the [Mesecons Laboratory](http://uberi.mesecons.net/), a website dedicated to advanced Mesecons builders? Want to get more into building? Why not check out the [Mesecons Laboratory](http://uberi.mesecons.net/), a website dedicated to advanced Mesecons builders?
Want to contribute to Mesecons itself? Check out the [source code](https://github.com/minetest-mods/mesecons)! Want to contribute to Mesecons itself? Check out the [source code](https://github.com/minetest-mods/mesecons)!

View File

@ -1,96 +1,140 @@
mesecon.queue.actions={} -- contains all ActionQueue actions --[[
Mesecons uses something it calls an ActionQueue.
function mesecon.queue:add_function(name, func) The ActionQueue holds functions and actions.
mesecon.queue.funcs[name] = func Functions are added on load time with a specified name.
Actions are preserved over server restarts.
Each action consists of a position, the name of an added function to be called,
the params that should be used in this function call (additionally to the pos),
the time after which it should be executed, an optional overwritecheck and a
priority.
If time = 0, the action will be executed in the next globalstep, otherwise the
earliest globalstep when it will be executed is the after next globalstep.
It is guaranteed, that for two actions ac1, ac2 where ac1 ~= ac2,
ac1.time == ac2.time, ac1.priority == ac2.priority and ac1 was added earlier
than ac2, ac1 will be executed before ac2 (but in the same globalstep).
Note: Do not pass references in params, as they can not be preserved.
Also note: Some of the guarantees here might be dropped at some time.
]]
-- localize for speed
local queue = mesecon.queue
queue.actions = {} -- contains all ActionQueue actions
function queue:add_function(name, func)
queue.funcs[name] = func
end end
-- If add_action with twice the same overwritecheck and same position are called, the first one is overwritten -- If add_action with twice the same overwritecheck and same position are called, the first one is overwritten
-- use overwritecheck nil to never overwrite, but just add the event to the queue -- use overwritecheck nil to never overwrite, but just add the event to the queue
-- priority specifies the order actions are executed within one globalstep, highest first -- priority specifies the order actions are executed within one globalstep, highest first
-- should be between 0 and 1 -- should be between 0 and 1
function mesecon.queue:add_action(pos, func, params, time, overwritecheck, priority) function queue:add_action(pos, func, params, time, overwritecheck, priority)
-- Create Action Table: -- Create Action Table:
time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution
priority = priority or 1 priority = priority or 1
local action = { pos=mesecon.tablecopy(pos), local action = {
pos = mesecon.tablecopy(pos),
func = func, func = func,
params = mesecon.tablecopy(params or {}), params = mesecon.tablecopy(params or {}),
time = time, time = time,
owcheck = (overwritecheck and mesecon.tablecopy(overwritecheck)) or nil, owcheck = (overwritecheck and mesecon.tablecopy(overwritecheck)) or nil,
priority=priority} priority = priority
}
local toremove = nil -- check if old action has to be overwritten / removed:
-- Otherwise, add the action to the queue if overwritecheck then
if overwritecheck then -- check if old action has to be overwritten / removed: for i, ac in ipairs(queue.actions) do
for i, ac in ipairs(mesecon.queue.actions) do if vector.equals(pos, ac.pos)
if(vector.equals(pos, ac.pos) and mesecon.cmpAny(overwritecheck, ac.owcheck) then
and mesecon.cmpAny(overwritecheck, ac.owcheck)) then -- remove the old action
toremove = i table.remove(queue.actions, i)
break break
end end
end end
end end
if (toremove ~= nil) then table.insert(queue.actions, action)
table.remove(mesecon.queue.actions, toremove)
end
table.insert(mesecon.queue.actions, action)
end end
-- execute the stored functions on a globalstep -- execute the stored functions on a globalstep
-- if however, the pos of a function is not loaded (get_node_or_nil == nil), do NOT execute the function -- if however, the pos of a function is not loaded (get_node_or_nil == nil), do NOT execute the function
-- this makes sure that resuming mesecons circuits when restarting minetest works fine -- this makes sure that resuming mesecons circuits when restarting minetest works fine (hm, where do we do this?)
-- However, even that does not work in some cases, that's why we delay the time the globalsteps -- However, even that does not work in some cases, that's why we delay the time the globalsteps
-- start to be execute by 5 seconds -- start to be execute by 4 seconds
local get_highest_priority = function (actions)
local highestp = -1
local highesti
for i, ac in ipairs(actions) do
if ac.priority > highestp then
highestp = ac.priority
highesti = i
end
end
return highesti local function globalstep_func(dtime)
end local actions = queue.actions
-- split into two categories:
local m_time = 0 -- actions_now: actions to execute now
local resumetime = mesecon.setting("resumetime", 4) -- queue.actions: actions to execute later
minetest.register_globalstep(function (dtime)
m_time = m_time + dtime
-- don't even try if server has not been running for XY seconds; resumetime = time to wait
-- after starting the server before processing the ActionQueue, don't set this too low
if (m_time < resumetime) then return end
local actions = mesecon.tablecopy(mesecon.queue.actions)
local actions_now = {} local actions_now = {}
queue.actions = {}
mesecon.queue.actions = {} for _, ac in ipairs(actions) do
-- sort actions into two categories:
-- those toexecute now (actions_now) and those to execute later (mesecon.queue.actions)
for i, ac in ipairs(actions) do
if ac.time > 0 then if ac.time > 0 then
ac.time = ac.time - dtime -- executed later -- action ac is to be executed later
table.insert(mesecon.queue.actions, ac) -- ~> insert into queue.actions
ac.time = ac.time - dtime
table.insert(queue.actions, ac)
else else
-- action ac is to be executed now
-- ~> insert into actions_now
table.insert(actions_now, ac) table.insert(actions_now, ac)
end end
end end
while(#actions_now > 0) do -- execute highest priorities first, until all are executed -- stable-sort the executed actions after their priority
local hp = get_highest_priority(actions_now) -- some constructions might depend on the execution order, hence we first
mesecon.queue:execute(actions_now[hp]) -- execute the actions that had a lower index in actions_now
table.remove(actions_now, hp) local old_action_order = {}
for i, ac in ipairs(actions_now) do
old_action_order[ac] = i
end
table.sort(actions_now, function(ac1, ac2)
if ac1.priority ~= ac2.priority then
return ac1.priority > ac2.priority
else
return old_action_order[ac1] < old_action_order[ac2]
end end
end) end)
function mesecon.queue:execute(action) -- execute highest priorities first, until all are executed
for _, ac in ipairs(actions_now) do
queue:execute(ac)
end
end
-- delay the time the globalsteps start to be execute by 4 seconds
do
local m_time = 0
local resumetime = mesecon.setting("resumetime", 4)
local globalstep_func_index = #minetest.registered_globalsteps + 1
minetest.register_globalstep(function(dtime)
m_time = m_time + dtime
-- don't even try if server has not been running for XY seconds; resumetime = time to wait
-- after starting the server before processing the ActionQueue, don't set this too low
if m_time < resumetime then
return
end
-- replace this globalstep function
minetest.registered_globalsteps[globalstep_func_index] = globalstep_func
end)
end
function queue:execute(action)
-- ignore if action queue function name doesn't exist, -- ignore if action queue function name doesn't exist,
-- (e.g. in case the action queue savegame was written by an old mesecons version) -- (e.g. in case the action queue savegame was written by an old mesecons version)
if mesecon.queue.funcs[action.func] then if queue.funcs[action.func] then
mesecon.queue.funcs[action.func](action.pos, unpack(action.params)) queue.funcs[action.func](action.pos, unpack(action.params))
end end
end end
@ -98,8 +142,8 @@ end
-- Store and read the ActionQueue to / from a file -- Store and read the ActionQueue to / from a file
-- so that upcoming actions are remembered when the game -- so that upcoming actions are remembered when the game
-- is restarted -- is restarted
mesecon.queue.actions = mesecon.file2table("mesecon_actionqueue") queue.actions = mesecon.file2table("mesecon_actionqueue")
minetest.register_on_shutdown(function() minetest.register_on_shutdown(function()
mesecon.table2file("mesecon_actionqueue", mesecon.queue.actions) mesecon.table2file("mesecon_actionqueue", queue.actions)
end) end)

View File

@ -30,7 +30,7 @@ minetest.register_node("mesecons:mesecon_on", {
}, },
groups = {dig_immediate=3, not_in_creaive_inventory=1, mesecon=1}, groups = {dig_immediate=3, not_in_creaive_inventory=1, mesecon=1},
drop = "mesecons:mesecon_off 1", drop = "mesecons:mesecon_off 1",
light_source = default.LIGHT_MAX-11, light_source = minetest.LIGHT_MAX-11,
mesecons = {conductor={ mesecons = {conductor={
state = mesecon.state.on, state = mesecon.state.on,
offstate = "mesecons:mesecon_off" offstate = "mesecons:mesecon_off"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 B

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 B

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

After

Width:  |  Height:  |  Size: 362 B

View File

@ -186,19 +186,11 @@ function mesecon.invertRule(r)
return vector.multiply(r, -1) return vector.multiply(r, -1)
end end
function mesecon.tablecopy(table) -- deep table copy function mesecon.tablecopy(obj) -- deep copy
if type(table) ~= "table" then return table end -- no need to copy if type(obj) == "table" then
local newtable = {} return table.copy(obj)
for idx, item in pairs(table) do
if type(item) == "table" then
newtable[idx] = mesecon.tablecopy(item)
else
newtable[idx] = item
end end
end return obj
return newtable
end end
function mesecon.cmpAny(t1, t2) function mesecon.cmpAny(t1, t2)

View File

@ -1 +1,2 @@
The blinky plants toggles between on and off state every three seconds. Can be used to make clocks. Also works after having restarted the game. The blinky plants toggles between on and off state every three seconds. Can be used to make clocks. Also works after having restarted the game.
It stops blinking in an inactive block, then starts again when the block becomes active.

BIN
mesecons_blinkyplant/doc/blinkyplant/preview.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 463 B

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -73,7 +73,7 @@ minetest.register_node("mesecons_button:button_on", {
legacy_wallmounted = true, legacy_wallmounted = true,
walkable = false, walkable = false,
on_rotate = false, on_rotate = false,
light_source = default.LIGHT_MAX-7, light_source = minetest.LIGHT_MAX-7,
sunlight_propagates = true, sunlight_propagates = true,
selection_box = { selection_box = {
type = "fixed", type = "fixed",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 220 B

View File

@ -1 +1,2 @@
There is no crafting recipe as this should only be available for server admins. Quite similar to the Minecraft counterpart. Executes server commands. There is no crafting recipe as this should only be available for server admins. Quite similar to the Minecraft counterpart. Executes server commands.
It works in inactive blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 183 B

View File

@ -1 +1 @@
The delayer delays the signal from the input for a determined time. The time can be set by punching the delayer. Possible delays are: 0.1 seconds, 0.3 seconds, 0.5 seconds and 1 second. You may try to use it for creating songs with the noteblock. The delayer delays the signal from the input for a determined time. The time can be set by punching the delayer. Possible delays are: 0.1 seconds, 0.3 seconds, 0.5 seconds and 1 second. You may try to use it for creating songs with the noteblock. It works in unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -33,19 +33,9 @@ end
-- Register the 2 (states) x 4 (delay times) delayers -- Register the 2 (states) x 4 (delay times) delayers
for i = 1, 4 do local delaytime = { 0.1, 0.3, 0.5, 1.0 }
local groups = {}
if i == 1 then
groups = {bendy=2,snappy=1,dig_immediate=2}
else
groups = {bendy=2,snappy=1,dig_immediate=2, not_in_creative_inventory=1}
end
local delaytime for i = 1, 4 do
if i == 1 then delaytime = 0.1
elseif i == 2 then delaytime = 0.3
elseif i == 3 then delaytime = 0.5
elseif i == 4 then delaytime = 1.0 end
local boxes = { local boxes = {
{ -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab { -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab
@ -61,9 +51,36 @@ local boxes = {
{ 6/16, -8/16, -1/16, 8/16, -7/16, 1/16 } { 6/16, -8/16, -1/16, 8/16, -7/16, 1/16 }
} }
minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), { -- Delayer definition defaults
description = "Delayer", local def = {
drawtype = "nodebox", drawtype = "nodebox",
walkable = true,
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
},
node_box = {
type = "fixed",
fixed = boxes
},
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
is_ground_content = false,
delayer_time = delaytime[i],
sounds = default.node_sound_stone_defaults(),
on_blast = mesecon.on_blastnode,
drop = "mesecons_delayer:delayer_off_1",
}
-- Deactivated delayer definition defaults
local off_groups = {bendy=2,snappy=1,dig_immediate=2}
if i > 1 then
off_groups.not_in_creative_inventory = 1
end
local off_state = {
description = "Delayer",
tiles = { tiles = {
"mesecons_delayer_off_"..tostring(i)..".png", "mesecons_delayer_off_"..tostring(i)..".png",
"mesecons_delayer_bottom.png", "mesecons_delayer_bottom.png",
@ -74,35 +91,18 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
}, },
inventory_image = "mesecons_delayer_off_1.png", inventory_image = "mesecons_delayer_off_1.png",
wield_image = "mesecons_delayer_off_1.png", wield_image = "mesecons_delayer_off_1.png",
walkable = true, groups = off_groups,
selection_box = { on_punch = function(pos, node, puncher)
type = "fixed", if minetest.is_protected(pos, puncher and puncher:get_player_name()) then
fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, return
},
node_box = {
type = "fixed",
fixed = boxes
},
groups = groups,
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
is_ground_content = false,
drop = 'mesecons_delayer:delayer_off_1',
on_punch = function (pos, node)
if node.name=="mesecons_delayer:delayer_off_1" then
minetest.swap_node(pos, {name = "mesecons_delayer:delayer_off_2", param2=node.param2})
elseif node.name=="mesecons_delayer:delayer_off_2" then
minetest.swap_node(pos, {name = "mesecons_delayer:delayer_off_3", param2=node.param2})
elseif node.name=="mesecons_delayer:delayer_off_3" then
minetest.swap_node(pos, {name = "mesecons_delayer:delayer_off_4", param2=node.param2})
elseif node.name=="mesecons_delayer:delayer_off_4" then
minetest.swap_node(pos, {name = "mesecons_delayer:delayer_off_1", param2=node.param2})
end end
minetest.swap_node(pos, {
name = "mesecons_delayer:delayer_off_"..tostring(i % 4 + 1),
param2 = node.param2
})
end, end,
delayer_time = delaytime,
delayer_onstate = "mesecons_delayer:delayer_on_"..tostring(i), delayer_onstate = "mesecons_delayer:delayer_on_"..tostring(i),
sounds = default.node_sound_stone_defaults(),
mesecons = { mesecons = {
receptor = receptor =
{ {
@ -115,13 +115,15 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
action_on = delayer_activate action_on = delayer_activate
} }
}, },
on_blast = mesecon.on_blastnode, }
}) for k, v in pairs(def) do
off_state[k] = off_state[k] or v
end
minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), off_state)
-- Activated delayer definition defaults
minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), { local on_state = {
description = "You hacker you", description = "You hacker you",
drawtype = "nodebox",
tiles = { tiles = {
"mesecons_delayer_on_"..tostring(i)..".png", "mesecons_delayer_on_"..tostring(i)..".png",
"mesecons_delayer_bottom.png", "mesecons_delayer_bottom.png",
@ -130,35 +132,18 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
"mesecons_delayer_sides_on.png", "mesecons_delayer_sides_on.png",
"mesecons_delayer_sides_on.png" "mesecons_delayer_sides_on.png"
}, },
walkable = true,
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
},
node_box = {
type = "fixed",
fixed = boxes
},
groups = {bendy = 2, snappy = 1, dig_immediate = 2, not_in_creative_inventory = 1}, groups = {bendy = 2, snappy = 1, dig_immediate = 2, not_in_creative_inventory = 1},
paramtype = "light", on_punch = function(pos, node, puncher)
paramtype2 = "facedir", if minetest.is_protected(pos, puncher and puncher:get_player_name()) then
sunlight_propagates = true, return
is_ground_content = false,
drop = 'mesecons_delayer:delayer_off_1',
on_punch = function (pos, node)
if node.name=="mesecons_delayer:delayer_on_1" then
minetest.swap_node(pos, {name = "mesecons_delayer:delayer_on_2", param2=node.param2})
elseif node.name=="mesecons_delayer:delayer_on_2" then
minetest.swap_node(pos, {name = "mesecons_delayer:delayer_on_3", param2=node.param2})
elseif node.name=="mesecons_delayer:delayer_on_3" then
minetest.swap_node(pos, {name = "mesecons_delayer:delayer_on_4", param2=node.param2})
elseif node.name=="mesecons_delayer:delayer_on_4" then
minetest.swap_node(pos, {name = "mesecons_delayer:delayer_on_1", param2=node.param2})
end end
minetest.swap_node(pos, {
name = "mesecons_delayer:delayer_on_"..tostring(i % 4 + 1),
param2 = node.param2
})
end, end,
delayer_time = delaytime,
delayer_offstate = "mesecons_delayer:delayer_off_"..tostring(i), delayer_offstate = "mesecons_delayer:delayer_off_"..tostring(i),
sounds = default.node_sound_stone_defaults(),
mesecons = { mesecons = {
receptor = receptor =
{ {
@ -171,8 +156,12 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
action_off = delayer_deactivate action_off = delayer_deactivate
} }
}, },
on_blast = mesecon.on_blastnode, }
}) for k, v in pairs(def) do
on_state[k] = on_state[k] or v
end
minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), on_state)
end end
minetest.register_craft({ minetest.register_craft({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 561 B

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 556 B

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 635 B

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 632 B

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 635 B

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 B

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 B

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 B

After

Width:  |  Height:  |  Size: 181 B

View File

@ -1,7 +1,11 @@
The node detector is a receptor. It changes its state when either any node The node detector is a receptor. It changes its state when either any node
or a specific node is detected. Right-click it to set a nodename to scan for. or a specific node is detected. Right-click it to set a nodename to scan for.
It can also receive digiline signals. You can either send "GET" and it will It can also receive digiline signals. For example, you can send
respond with the detected nodename or you can send any other string and it will <code>{distance=4, scanname="default:dirt"}</code>
set this string as the node to scan for. to set distance to 4 and scan for dirt. You can omit either parameter.
There is also a command parameter: <code>{command="get"}</code> will respond
with the detected nodename and <code>{command="scan"}</code> will respond with
a boolean using the distance and nodename of the detector.
Nodenames must include the mod they reside in, so for instance default:dirt, not just dirt. Nodenames must include the mod they reside in, so for instance default:dirt, not just dirt.
The distance parameter specifies how many blocks are between the node detector and the node to detect. The distance parameter specifies how many blocks are between the node detector and the node to detect.
Automatic scanning with Mesecons output only works when the detector is in an active block, but Digilines queries always work.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -2,3 +2,4 @@ The object detector is a receptor. It changes its state when a player approaches
Right-click it to set a name to scan for. Right-click it to set a name to scan for.
You can also search for comma-separated lists of players where the detector gets activated if any of the names in the list are found. You can also search for comma-separated lists of players where the detector gets activated if any of the names in the list are found.
It can also receive digiline signals which are the name to scan for on the specified channel in the right-click menu. It can also receive digiline signals which are the name to scan for on the specified channel in the right-click menu.
Automatic scanning with Mesecons output only works when the detector is in an active block, but Digilines queries always work.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -189,29 +189,50 @@ local function node_detector_scan(pos)
(frontname ~= "air" and frontname ~= "ignore" and scanname == "") (frontname ~= "air" and frontname ~= "ignore" and scanname == "")
end end
local function node_detector_send_node_name(pos, node, channel, meta)
local distance = meta:get_int("distance")
local distance_max = mesecon.setting("node_detector_distance_max", 10)
if distance < 0 then distance = 0 end
if distance > distance_max then distance = distance_max end
local nodename = minetest.get_node(
vector.subtract(pos, vector.multiply(minetest.facedir_to_dir(node.param2), distance + 1))
).name
digiline:receptor_send(pos, digiline.rules.default, channel, nodename)
end
-- set player name when receiving a digiline signal on a specific channel -- set player name when receiving a digiline signal on a specific channel
local node_detector_digiline = { local node_detector_digiline = {
effector = { effector = {
action = function(pos, node, channel, msg) action = function(pos, node, channel, msg)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local distance = meta:get_int("distance")
local distance_max = mesecon.setting("node_detector_distance_max", 10)
if distance < 0 then distance = 0 end
if distance > distance_max then distance = distance_max end
if channel ~= meta:get_string("digiline_channel") then return end if channel ~= meta:get_string("digiline_channel") then return end
if type(msg) == "table" then
if msg.distance or msg.scanname then
if msg.distance then
meta:set_string("distance", msg.distance)
end
if msg.scanname then
meta:set_string("scanname", msg.scanname)
end
node_detector_make_formspec(pos)
end
if msg.command == "get" then
node_detector_send_node_name(pos, node, channel, meta)
elseif msg.command == "scan" then
local result = node_detector_scan(pos)
digiline:receptor_send(pos, digiline.rules.default, channel, result)
end
else
if msg == GET_COMMAND then if msg == GET_COMMAND then
local nodename = minetest.get_node( node_detector_send_node_name(pos, node, channel, meta)
vector.subtract(pos, vector.multiply(minetest.facedir_to_dir(node.param2), distance + 1))
).name
digiline:receptor_send(pos, digiline.rules.default, channel, nodename)
else else
meta:set_string("scanname", msg) meta:set_string("scanname", msg)
node_detector_make_formspec(pos) node_detector_make_formspec(pos)
end end
end
end, end,
}, },
receptor = {} receptor = {}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 727 B

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 712 B

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 B

After

Width:  |  Height:  |  Size: 693 B

View File

@ -2,13 +2,14 @@ local screwdriver_exists = minetest.global_exists("screwdriver")
local corner_nodebox = { local corner_nodebox = {
type = "fixed", type = "fixed",
-- ±0.001 is to prevent z-fighting
fixed = {{ -16/32-0.001, -17/32, -3/32, 0, -13/32, 3/32 }, fixed = {{ -16/32-0.001, -17/32, -3/32, 0, -13/32, 3/32 },
{ -3/32, -17/32, -16/32+0.001, 3/32, -13/32, 3/32}} { -3/32, -17/32, -16/32+0.001, 3/32, -13/32, 3/32}}
} }
local corner_selectionbox = { local corner_selectionbox = {
type = "fixed", type = "fixed",
fixed = { -16/32-0.001, -18/32, -16/32, 5/32, -12/32, 5/32 }, fixed = { -16/32, -16/32, -16/32, 5/32, -12/32, 5/32 },
} }
local corner_get_rules = function (node) local corner_get_rules = function (node)

View File

@ -32,7 +32,7 @@ minetest.register_node("mesecons_extrawires:crossover_off", {
is_ground_content = false, is_ground_content = false,
walkable = false, walkable = false,
stack_max = 99, stack_max = 99,
selection_box = {type="fixed", fixed={-16/32-0.0001, -18/32, -16/32-0.001, 16/32+0.001, -5/32, 16/32+0.001}}, selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
groups = {dig_immediate=3, mesecon=3}, groups = {dig_immediate=3, mesecon=3},
sounds = default.node_sound_defaults(), sounds = default.node_sound_defaults(),
mesecons = { mesecons = {
@ -59,7 +59,7 @@ minetest.register_node("mesecons_extrawires:crossover_01", {
is_ground_content = false, is_ground_content = false,
walkable = false, walkable = false,
stack_max = 99, stack_max = 99,
selection_box = {type="fixed", fixed={-16/32-0.0001, -18/32, -16/32-0.001, 16/32+0.001, -5/32, 16/32+0.001}}, selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1}, groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1},
sounds = default.node_sound_defaults(), sounds = default.node_sound_defaults(),
mesecons = { mesecons = {
@ -86,7 +86,7 @@ minetest.register_node("mesecons_extrawires:crossover_10", {
is_ground_content = false, is_ground_content = false,
walkable = false, walkable = false,
stack_max = 99, stack_max = 99,
selection_box = {type="fixed", fixed={-16/32-0.0001, -18/32, -16/32-0.001, 16/32+0.001, -5/32, 16/32+0.001}}, selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1}, groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1},
sounds = default.node_sound_defaults(), sounds = default.node_sound_defaults(),
mesecons = { mesecons = {
@ -113,7 +113,7 @@ minetest.register_node("mesecons_extrawires:crossover_on", {
is_ground_content = false, is_ground_content = false,
walkable = false, walkable = false,
stack_max = 99, stack_max = 99,
selection_box = {type="fixed", fixed={-16/32-0.0001, -18/32, -16/32-0.001, 16/32+0.001, -5/32, 16/32+0.001}}, selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1}, groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1},
sounds = default.node_sound_defaults(), sounds = default.node_sound_defaults(),
mesecons = { mesecons = {

View File

@ -1 +1 @@
Insulated corners are conductors that only conduct between the inputs (also not up or down). When placing they always point to the left in direction of your vision. Insulated corners are conductors that only conduct between the inputs (also not up or down). When placing they always point to the left in direction of your vision. Like uninsulated wires, they work through unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1 +1 @@
Insulated crossing are conductors that conduct two signals between the opposing sides, the signals are insulated to each other. Insulated crossing are conductors that conduct two signals between the opposing sides, the signals are insulated to each other. Like uninsulated wires, they work through unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1 +1 @@
The basic prerequesite for mesecons, can be crafted into wires and other stuff. Have a look at the <a href="http://wiki.minetest.net/Mese">Minetest Wiki</a> for more information. Mese is a conductor. It conducts in all six directions: Up/Down/Left/Right/Forward/Backward The basic prerequesite for mesecons, can be crafted into wires and other stuff. Have a look at the <a href="http://wiki.minetest.net/Mese">Minetest Wiki</a> for more information. Mese is a conductor. It conducts in all six directions: Up/Down/Left/Right/Forward/Backward. Like horizontal wires, Mese conduction works through unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1 +1 @@
Insulated T-Junctions are conductors that only conduct between the inputs (also not up or down). Insulated T-Junctions are conductors that only conduct between the inputs (also not up or down). Like uninsulated wires, they work through unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1 +1 @@
Vertical Mesecons only conduct up and down. Plates appear at the ends, at that place they also conduct to the side. Vertical Mesecons only conduct up and down. Plates appear at the ends, at that place they also conduct to the side. Like horizontal wires, they work through unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -2,13 +2,14 @@ local screwdriver_exists = minetest.global_exists("screwdriver")
local tjunction_nodebox = { local tjunction_nodebox = {
type = "fixed", type = "fixed",
-- ±0.001 is to prevent z-fighting
fixed = {{ -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 }, fixed = {{ -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 },
{ -3/32, -17/32, -16/32+0.001, 3/32, -13/32, -3/32},} { -3/32, -17/32, -16/32+0.001, 3/32, -13/32, -3/32},}
} }
local tjunction_selectionbox = { local tjunction_selectionbox = {
type = "fixed", type = "fixed",
fixed = { -16/32-0.001, -18/32, -16/32, 16/32+0.001, -12/32, 7/32 }, fixed = { -16/32, -16/32, -16/32, 16/32, -12/32, 7/32 },
} }
local tjunction_get_rules = function (node) local tjunction_get_rules = function (node)

View File

@ -1,5 +1,6 @@
FPGAs can be used to chain multiple logic gates together in a compact manner. FPGAs can be used to chain multiple logic gates together in a compact manner.
They come with 4 I/O ports and 10 internal registers, They come with 4 I/O ports and 10 internal registers,
which can then be connected with eachother to form logic circuits.<br /> which can then be connected with each other to form logic circuits.
They work fine in unloaded blocks.<br />
Supported gate types: <b>AND</b>, <b>OR</b>, <b>NOT</b>, <b>XOR</b>, <b>NAND</b>, <b>XNOR</b>, <b>Buffer</b> (=)<br /> Supported gate types: <b>AND</b>, <b>OR</b>, <b>NOT</b>, <b>XOR</b>, <b>NAND</b>, <b>XNOR</b>, <b>Buffer</b> (=)<br />
I/O ports: <b>A B C D</b>; Registers: numbered <b>0</b> to <b>9</b> I/O ports: <b>A B C D</b>; Registers: numbered <b>0</b> to <b>9</b>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,10 +1,11 @@
local plg = {} local plg = {}
plg.rules = {} plg.rules = {}
-- per-player formspec positions
plg.open_formspecs = {}
local lcore = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/logic.lua") local lcore = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/logic.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/tool.lua")(plg) dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/tool.lua")(plg)
plg.register_nodes = function(template) plg.register_nodes = function(template)
-- each loop is for one of the 4 IO ports -- each loop is for one of the 4 IO ports
for a = 0, 1 do for a = 0, 1 do
@ -93,16 +94,20 @@ plg.register_nodes({
meta:set_string("instr", lcore.serialize(is)) meta:set_string("instr", lcore.serialize(is))
meta:set_int("valid", 0) meta:set_int("valid", 0)
meta:set_string("formspec", plg.to_formspec_string(is))
meta:set_string("infotext", "FPGA") meta:set_string("infotext", "FPGA")
end, end,
on_receive_fields = function(pos, formname, fields, sender) on_rightclick = function(pos, node, clicker)
if fields.program == nil then return end -- we only care when the user clicks "Program" if not minetest.is_player(clicker) then
return
end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local is = plg.from_formspec_fields(fields) local name = clicker:get_player_name()
-- Erase formspecs of old FPGAs
meta:set_string("formspec", "")
meta:set_string("instr", lcore.serialize(is)) plg.open_formspecs[name] = pos
plg.update_formspec(pos, is) local is = lcore.deserialize(meta:get_string("instr"))
minetest.show_formspec(name, "mesecons:fpga", plg.to_formspec_string(is, nil))
end, end,
sounds = default.node_sound_stone_defaults(), sounds = default.node_sound_stone_defaults(),
mesecons = { mesecons = {
@ -116,6 +121,12 @@ plg.register_nodes({
}, },
after_dig_node = function(pos, node) after_dig_node = function(pos, node)
mesecon.receptor_off(pos, plg.rules[node.name]) mesecon.receptor_off(pos, plg.rules[node.name])
for name, open_pos in pairs(plg.open_formspecs) do
if vector.equals(pos, open_pos) then
minetest.close_formspec(name, "mesecons:fpga")
plg.open_formspecs[name] = nil
end
end
end, end,
on_blast = mesecon.on_blastnode, on_blast = mesecon.on_blastnode,
on_rotate = function(pos, node, user, mode) on_rotate = function(pos, node, user, mode)
@ -153,13 +164,12 @@ plg.register_nodes({
end end
meta:set_string("instr", lcore.serialize(instr)) meta:set_string("instr", lcore.serialize(instr))
plg.update_formspec(pos, instr) plg.update_meta(pos, instr)
return true return true
end, end,
}) })
plg.to_formspec_string = function(is, err)
plg.to_formspec_string = function(is)
local function dropdown_op(x, y, name, val) local function dropdown_op(x, y, name, val)
local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";" local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
.. "0.75,0.5;" .. name .. ";" -- the height seems to be ignored? .. "0.75,0.5;" .. name .. ";" -- the height seems to be ignored?
@ -180,26 +190,20 @@ plg.to_formspec_string = function(is)
return s .. "]" return s .. "]"
end end
local function dropdown_action(x, y, name, val) local function dropdown_action(x, y, name, val)
local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";" local selected = 0
.. "1.125,0.5;" .. name .. ";" -- the height seems to be ignored? local titles = { " " }
s = s .. " , AND, OR, NOT, XOR,NAND, =,XNOR;" for i, data in ipairs(lcore.get_operations()) do
if val == nil then titles[i + 1] = data.fs_name
return s .. "0]" -- actually selects no field at all if val == data.gate then
selected = i + 1
end end
local mapping = { end
["and"] = 1, return ("dropdown[%f,%f;1.125,0.5;%s;%s;%i]"):format(
["or"] = 2, x, y, name, table.concat(titles, ","), selected)
["not"] = 3,
["xor"] = 4,
["nand"] = 5,
["buf"] = 6,
["xnor"] = 7,
}
return s .. tostring(1 + mapping[val]) .. "]"
end end
local s = "size[9,9]".. local s = "size[9,9]"..
"label[3.4,-0.15;FPGA gate configuration]".. "label[3.4,-0.15;FPGA gate configuration]"..
"button_exit[7,7.5;2,2.5;program;Program]".. "button[7,7.5;2,2.5;program;Program]"..
"box[4.2,0.5;0.03,7;#ffffff]".. "box[4.2,0.5;0.03,7;#ffffff]"..
"label[0.25,0.25;op. 1]".. "label[0.25,0.25;op. 1]"..
"label[1.0,0.25;gate type]".. "label[1.0,0.25;gate type]"..
@ -225,6 +229,12 @@ plg.to_formspec_string = function(is)
y = 1 - 0.25 y = 1 - 0.25
end end
end end
if err then
local fmsg = minetest.colorize("#ff0000", minetest.formspec_escape(err.msg))
s = s .. plg.red_box_around(err.i) ..
"label[0.25,8.25;The gate configuration is erroneous in the marked area:]"..
"label[0.25,8.5;" .. fmsg .. "]"
end
return s return s
end end
@ -239,20 +249,11 @@ plg.from_formspec_fields = function(fields)
end end
end end
local function read_action(s) local function read_action(s)
if s == nil or s == " " then for i, data in ipairs(lcore.get_operations()) do
return nil if data.fs_name == s then
return data.gate
end
end end
local mapping = {
["AND"] = "and",
["OR"] = "or",
["NOT"] = "not",
["XOR"] = "xor",
["NAND"] = "nand",
["="] = "buf",
["XNOR"] = "xnor",
}
s = s:gsub("^%s*", "") -- remove leading spaces
return mapping[s]
end end
local is = {} local is = {}
for i = 1, 14 do for i = 1, 14 do
@ -266,12 +267,11 @@ plg.from_formspec_fields = function(fields)
return is return is
end end
plg.update_formspec = function(pos, is) plg.update_meta = function(pos, is)
if type(is) == "string" then -- serialized string if type(is) == "string" then -- serialized string
is = lcore.deserialize(is) is = lcore.deserialize(is)
end end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local form = plg.to_formspec_string(is)
local err = lcore.validate(is) local err = lcore.validate(is)
if err == nil then if err == nil then
@ -280,17 +280,20 @@ plg.update_formspec = function(pos, is)
else else
meta:set_int("valid", 0) meta:set_int("valid", 0)
meta:set_string("infotext", "FPGA") meta:set_string("infotext", "FPGA")
local fmsg = minetest.colorize("#ff0000", minetest.formspec_escape(err.msg))
form = form .. plg.red_box_around(err.i) ..
"label[0.25,8.25;The gate configuration is erroneous in the marked area:]"..
"label[0.25,8.5;" .. fmsg .. "]"
end end
meta:set_string("formspec", form)
-- reset ports and run programmed logic -- reset ports and run programmed logic
plg.setports(pos, false, false, false, false) plg.setports(pos, false, false, false, false)
plg.update(pos) plg.update(pos)
-- Refresh open formspecs
local form = plg.to_formspec_string(is, err)
for name, open_pos in pairs(plg.open_formspecs) do
if vector.equals(pos, open_pos) then
minetest.show_formspec(name, "mesecons:fpga", form)
end
end
return err
end end
plg.red_box_around = function(i) plg.red_box_around = function(i)
@ -409,6 +412,38 @@ plg.setports = function(pos, A, B, C, D) -- sets states of OUTPUT
end end
end end
minetest.register_on_player_receive_fields(function(player, formname, fields)
local player_name = player:get_player_name()
if formname ~= "mesecons:fpga" or fields.quit then
plg.open_formspecs[player_name] = nil -- potential garbage
return
end
if not fields.program then
return -- we only care when the user clicks "Program"
end
local pos = plg.open_formspecs[player_name]
if minetest.is_protected(pos, player_name) then
minetest.record_protection_violation(pos, player_name)
return
end
local meta = minetest.get_meta(pos)
local is = plg.from_formspec_fields(fields)
meta:set_string("instr", lcore.serialize(is))
local err = plg.update_meta(pos, is)
if not err then
plg.open_formspecs[player_name] = nil
-- Close on success
minetest.close_formspec(player_name, "mesecons:fpga")
end
end)
minetest.register_on_leaveplayer(function(player)
plg.open_formspecs[player:get_player_name()] = nil
end)
minetest.register_craft({ minetest.register_craft({
output = "mesecons_fpga:fpga0000 2", output = "mesecons_fpga:fpga0000 2",

View File

@ -1,5 +1,27 @@
local lg = {} local lg = {}
local operations = {
-- table index: Index in the formspec dropdown
-- gate: Internal name
-- short: Serialized form, single character
-- fs_name: Display name, padded to 4 characters
-- func: Function that applies the operation
-- unary: Whether this gate only has one input
{ gate = "and", short = "&", fs_name = " AND", func = function(a, b) return a and b end },
{ gate = "or", short = "|", fs_name = " OR", func = function(a, b) return a or b end },
{ gate = "not", short = "~", fs_name = " NOT", func = function(a, b) return not b end, unary = true },
{ gate = "xor", short = "^", fs_name = " XOR", func = function(a, b) return a ~= b end },
{ gate = "nand", short = "?", fs_name = "NAND", func = function(a, b) return not (a and b) end },
{ gate = "buf", short = "_", fs_name = " =", func = function(a, b) return b end, unary = true },
{ gate = "xnor", short = "=", fs_name = "XNOR", func = function(a, b) return a == b end },
{ gate = "nor", short = "!", fs_name = " NOR", func = function(a, b) return not (a or b) end },
}
lg.get_operations = function()
return operations
end
-- (de)serialize -- (de)serialize
lg.serialize = function(t) lg.serialize = function(t)
local function _op(t) local function _op(t)
@ -11,20 +33,14 @@ lg.serialize = function(t)
return tostring(t.n) return tostring(t.n)
end end
end end
local function _action(s) -- Serialize actions (gates) from eg. "and" to "&"
if s == nil then local function _action(action)
return " " for i, data in ipairs(operations) do
if data.gate == action then
return data.short
end end
local mapping = { end
["and"] = "&", return " "
["or"] = "|",
["not"] = "~",
["xor"] = "^",
["nand"] = "?", --dunno
["buf"] = "_",
["xnor"] = "=",
}
return mapping[s]
end end
local s = "" local s = ""
@ -48,18 +64,14 @@ lg.deserialize = function(s)
return {type = "reg", n = tonumber(c)} return {type = "reg", n = tonumber(c)}
end end
end end
local function _action(c) -- Deserialize actions (gates) from eg. "&" to "and"
local mapping = { local function _action(action)
["&"] = "and", for i, data in ipairs(operations) do
["|"] = "or", if data.short == action then
["~"] = "not", return data.gate
["^"] = "xor", end
["?"] = "nand", end
["_"] = "buf", -- nil
["="] = "xnor",
[" "] = nil,
}
return mapping[c]
end end
local ret = {} local ret = {}
@ -109,16 +121,25 @@ lg.validate_single = function(t, i)
return false return false
end end
local elem = t[i] local elem = t[i]
local gate_data
for j, data in ipairs(operations) do
if data.gate == elem.action then
gate_data = data
break
end
end
-- check for completeness -- check for completeness
if elem.action == nil then if not gate_data then
return {i = i, msg = "Gate type required"} return {i = i, msg = "Gate type is required"}
elseif elem.action == "not" or elem.action == "buf" then elseif gate_data.unary then
if elem.op1 ~= nil or elem.op2 == nil or elem.dst == nil then if elem.op1 ~= nil or elem.op2 == nil or elem.dst == nil then
return {i = i, msg = "Second operand (only) and destination required"} return {i = i, msg = "Second operand (only) and destination are required"}
end end
else else
if elem.op1 == nil or elem.op2 == nil or elem.dst == nil then if elem.op1 == nil or elem.op2 == nil or elem.dst == nil then
return {i = i, msg = "Operands and destination required"} return {i = i, msg = "Operands and destination are required"}
end end
end end
-- check whether operands/destination are identical -- check whether operands/destination are identical
@ -159,22 +180,13 @@ end
-- interpreter -- interpreter
lg.interpret = function(t, a, b, c, d) lg.interpret = function(t, a, b, c, d)
local function _action(s, v1, v2) local function _action(s, v1, v2)
if s == "and" then for i, data in ipairs(operations) do
return v1 and v2 if data.gate == s then
elseif s == "or" then return data.func(v1, v2)
return v1 or v2
elseif s == "not" then
return not v2
elseif s == "xor" then
return v1 ~= v2
elseif s == "nand" then
return not (v1 and v2)
elseif s == "buf" then
return v2
else -- s == "xnor"
return v1 == v2
end end
end end
return false -- unknown gate
end
local function _op(t, regs, io_in) local function _op(t, regs, io_in)
if t.type == "reg" then if t.type == "reg" then
return regs[t.n] return regs[t.n]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 311 B

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 536 B

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 816 B

After

Width:  |  Height:  |  Size: 760 B

View File

@ -34,17 +34,22 @@ minetest.register_tool("mesecons_fpga:programmer", {
if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then
return itemstack return itemstack
end end
local player_name = user:get_player_name()
if minetest.is_protected(pos, player_name) then
minetest.record_protection_violation(pos, player_name)
return itemstack
end
local imeta = itemstack:get_metadata() local imeta = itemstack:get_metadata()
if imeta == "" then if imeta == "" then
minetest.chat_send_player(user:get_player_name(), "Use shift+right-click to copy a gate configuration first.") minetest.chat_send_player(player_name, "Use shift+right-click to copy a gate configuration first.")
return itemstack return itemstack
end end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("instr", imeta) meta:set_string("instr", imeta)
plg.update_formspec(pos, imeta) plg.update_meta(pos, imeta)
minetest.chat_send_player(user:get_player_name(), "Gate configuration was successfully written to FPGA!") minetest.chat_send_player(player_name, "Gate configuration was successfully written to FPGA!")
return itemstack return itemstack
end end

View File

@ -1 +1,2 @@
AND gates power their output if both inputs (from left and right) are powered. AND gates power their output if both inputs (from left and right) are powered.
They work in unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1 +1,2 @@
Diodes conduct signals in one direction only. Diodes conduct signals in one direction only.
They work in unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +1,2 @@
NAND gates do not power their output if both inputs (from left and right) are powered, but power it in every other case. NAND gates do not power their output if both inputs (from left and right) are powered, but power it in every other case.
They work in unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1 +1,2 @@
NOR gates only power their output if none of their two inputs is powered. They are basically OR gates with a NOT gate at their output. NOR gates only power their output if none of their two inputs is powered. They are basically OR gates with a NOT gate at their output.
They work in unloaded blocks.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +1,2 @@
NOT gates invert signals, just like a mesecon torch does, but faster. The input is at the opposite side of the output. NOT gates invert signals, just like a mesecon torch does, but faster. The input is at the opposite side of the output.
They work in unloaded blocks.

Some files were not shown because too many files have changed in this diff Show More