forked from minetest-mods/mesecons
Compare commits
1 Commits
e1cffdedbf
...
numzero-fi
Author | SHA1 | Date | |
---|---|---|---|
7888566b2f |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1 @@
|
||||
*~
|
||||
*.patch
|
||||
*.diff
|
||||
|
@ -1,140 +1,96 @@
|
||||
--[[
|
||||
Mesecons uses something it calls an ActionQueue.
|
||||
mesecon.queue.actions={} -- contains all ActionQueue actions
|
||||
|
||||
The ActionQueue holds functions and actions.
|
||||
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
|
||||
function mesecon.queue:add_function(name, func)
|
||||
mesecon.queue.funcs[name] = func
|
||||
end
|
||||
|
||||
-- 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
|
||||
-- priority specifies the order actions are executed within one globalstep, highest first
|
||||
-- should be between 0 and 1
|
||||
function queue:add_action(pos, func, params, time, overwritecheck, priority)
|
||||
function mesecon.queue:add_action(pos, func, params, time, overwritecheck, priority)
|
||||
-- Create Action Table:
|
||||
time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution
|
||||
priority = priority or 1
|
||||
local action = {
|
||||
pos = mesecon.tablecopy(pos),
|
||||
func = func,
|
||||
params = mesecon.tablecopy(params or {}),
|
||||
time = time,
|
||||
owcheck = (overwritecheck and mesecon.tablecopy(overwritecheck)) or nil,
|
||||
priority = priority
|
||||
}
|
||||
local action = { pos=mesecon.tablecopy(pos),
|
||||
func=func,
|
||||
params=mesecon.tablecopy(params or {}),
|
||||
time=time,
|
||||
owcheck=(overwritecheck and mesecon.tablecopy(overwritecheck)) or nil,
|
||||
priority=priority}
|
||||
|
||||
-- check if old action has to be overwritten / removed:
|
||||
if overwritecheck then
|
||||
for i, ac in ipairs(queue.actions) do
|
||||
if vector.equals(pos, ac.pos)
|
||||
and mesecon.cmpAny(overwritecheck, ac.owcheck) then
|
||||
-- remove the old action
|
||||
table.remove(queue.actions, i)
|
||||
local toremove = nil
|
||||
-- Otherwise, add the action to the queue
|
||||
if overwritecheck then -- check if old action has to be overwritten / removed:
|
||||
for i, ac in ipairs(mesecon.queue.actions) do
|
||||
if(vector.equals(pos, ac.pos)
|
||||
and mesecon.cmpAny(overwritecheck, ac.owcheck)) then
|
||||
toremove = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(queue.actions, action)
|
||||
if (toremove ~= nil) then
|
||||
table.remove(mesecon.queue.actions, toremove)
|
||||
end
|
||||
|
||||
table.insert(mesecon.queue.actions, action)
|
||||
end
|
||||
|
||||
-- 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
|
||||
-- this makes sure that resuming mesecons circuits when restarting minetest works fine (hm, where do we do this?)
|
||||
-- this makes sure that resuming mesecons circuits when restarting minetest works fine
|
||||
-- However, even that does not work in some cases, that's why we delay the time the globalsteps
|
||||
-- start to be execute by 4 seconds
|
||||
-- start to be execute by 5 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
|
||||
|
||||
local function globalstep_func(dtime)
|
||||
local actions = queue.actions
|
||||
-- split into two categories:
|
||||
-- actions_now: actions to execute now
|
||||
-- queue.actions: actions to execute later
|
||||
local actions_now = {}
|
||||
queue.actions = {}
|
||||
return highesti
|
||||
end
|
||||
|
||||
for _, ac in ipairs(actions) do
|
||||
local m_time = 0
|
||||
local resumetime = mesecon.setting("resumetime", 4)
|
||||
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={}
|
||||
|
||||
mesecon.queue.actions = {}
|
||||
|
||||
-- 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
|
||||
-- action ac is to be executed later
|
||||
-- ~> insert into queue.actions
|
||||
ac.time = ac.time - dtime
|
||||
table.insert(queue.actions, ac)
|
||||
ac.time = ac.time - dtime -- executed later
|
||||
table.insert(mesecon.queue.actions, ac)
|
||||
else
|
||||
-- action ac is to be executed now
|
||||
-- ~> insert into actions_now
|
||||
table.insert(actions_now, ac)
|
||||
end
|
||||
end
|
||||
|
||||
-- stable-sort the executed actions after their priority
|
||||
-- some constructions might depend on the execution order, hence we first
|
||||
-- execute the actions that had a lower index in actions_now
|
||||
local old_action_order = {}
|
||||
for i, ac in ipairs(actions_now) do
|
||||
old_action_order[ac] = i
|
||||
while(#actions_now > 0) do -- execute highest priorities first, until all are executed
|
||||
local hp = get_highest_priority(actions_now)
|
||||
mesecon.queue:execute(actions_now[hp])
|
||||
table.remove(actions_now, hp)
|
||||
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)
|
||||
|
||||
-- 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)
|
||||
function mesecon.queue:execute(action)
|
||||
-- ignore if action queue function name doesn't exist,
|
||||
-- (e.g. in case the action queue savegame was written by an old mesecons version)
|
||||
if queue.funcs[action.func] then
|
||||
queue.funcs[action.func](action.pos, unpack(action.params))
|
||||
if mesecon.queue.funcs[action.func] then
|
||||
mesecon.queue.funcs[action.func](action.pos, unpack(action.params))
|
||||
end
|
||||
end
|
||||
|
||||
@ -142,8 +98,8 @@ end
|
||||
-- Store and read the ActionQueue to / from a file
|
||||
-- so that upcoming actions are remembered when the game
|
||||
-- is restarted
|
||||
queue.actions = mesecon.file2table("mesecon_actionqueue")
|
||||
mesecon.queue.actions = mesecon.file2table("mesecon_actionqueue")
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
mesecon.table2file("mesecon_actionqueue", queue.actions)
|
||||
mesecon.table2file("mesecon_actionqueue", mesecon.queue.actions)
|
||||
end)
|
||||
|
@ -33,9 +33,19 @@ end
|
||||
|
||||
-- Register the 2 (states) x 4 (delay times) delayers
|
||||
|
||||
local delaytime = { 0.1, 0.3, 0.5, 1.0 }
|
||||
|
||||
for i = 1, 4 do
|
||||
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
|
||||
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 = {
|
||||
{ -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab
|
||||
@ -51,9 +61,19 @@ local boxes = {
|
||||
{ 6/16, -8/16, -1/16, 8/16, -7/16, 1/16 }
|
||||
}
|
||||
|
||||
-- Delayer definition defaults
|
||||
local def = {
|
||||
minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
|
||||
description = "Delayer",
|
||||
drawtype = "nodebox",
|
||||
tiles = {
|
||||
"mesecons_delayer_off_"..tostring(i)..".png",
|
||||
"mesecons_delayer_bottom.png",
|
||||
"mesecons_delayer_ends_off.png",
|
||||
"mesecons_delayer_ends_off.png",
|
||||
"mesecons_delayer_sides_off.png",
|
||||
"mesecons_delayer_sides_off.png"
|
||||
},
|
||||
inventory_image = "mesecons_delayer_off_1.png",
|
||||
wield_image = "mesecons_delayer_off_1.png",
|
||||
walkable = true,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
@ -63,46 +83,26 @@ local def = {
|
||||
type = "fixed",
|
||||
fixed = boxes
|
||||
},
|
||||
groups = groups,
|
||||
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 = {
|
||||
"mesecons_delayer_off_"..tostring(i)..".png",
|
||||
"mesecons_delayer_bottom.png",
|
||||
"mesecons_delayer_ends_off.png",
|
||||
"mesecons_delayer_ends_off.png",
|
||||
"mesecons_delayer_sides_off.png",
|
||||
"mesecons_delayer_sides_off.png"
|
||||
},
|
||||
inventory_image = "mesecons_delayer_off_1.png",
|
||||
wield_image = "mesecons_delayer_off_1.png",
|
||||
groups = off_groups,
|
||||
on_punch = function(pos, node, puncher)
|
||||
if minetest.is_protected(pos, puncher and puncher:get_player_name()) then
|
||||
return
|
||||
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
|
||||
|
||||
minetest.swap_node(pos, {
|
||||
name = "mesecons_delayer:delayer_off_"..tostring(i % 4 + 1),
|
||||
param2 = node.param2
|
||||
})
|
||||
end,
|
||||
delayer_time = delaytime,
|
||||
delayer_onstate = "mesecons_delayer:delayer_on_"..tostring(i),
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
mesecons = {
|
||||
receptor =
|
||||
{
|
||||
@ -115,15 +115,13 @@ local off_state = {
|
||||
action_on = delayer_activate
|
||||
}
|
||||
},
|
||||
}
|
||||
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)
|
||||
on_blast = mesecon.on_blastnode,
|
||||
})
|
||||
|
||||
-- Activated delayer definition defaults
|
||||
local on_state = {
|
||||
|
||||
minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
|
||||
description = "You hacker you",
|
||||
drawtype = "nodebox",
|
||||
tiles = {
|
||||
"mesecons_delayer_on_"..tostring(i)..".png",
|
||||
"mesecons_delayer_bottom.png",
|
||||
@ -131,19 +129,36 @@ local on_state = {
|
||||
"mesecons_delayer_ends_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},
|
||||
on_punch = function(pos, node, puncher)
|
||||
if minetest.is_protected(pos, puncher and puncher:get_player_name()) then
|
||||
return
|
||||
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_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
|
||||
|
||||
minetest.swap_node(pos, {
|
||||
name = "mesecons_delayer:delayer_on_"..tostring(i % 4 + 1),
|
||||
param2 = node.param2
|
||||
})
|
||||
end,
|
||||
delayer_time = delaytime,
|
||||
delayer_offstate = "mesecons_delayer:delayer_off_"..tostring(i),
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
mesecons = {
|
||||
receptor =
|
||||
{
|
||||
@ -156,12 +171,8 @@ local on_state = {
|
||||
action_off = delayer_deactivate
|
||||
}
|
||||
},
|
||||
}
|
||||
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)
|
||||
|
||||
on_blast = mesecon.on_blastnode,
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
|
@ -1,11 +1,10 @@
|
||||
local plg = {}
|
||||
plg.rules = {}
|
||||
-- per-player formspec positions
|
||||
plg.open_formspecs = {}
|
||||
|
||||
local lcore = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/logic.lua")
|
||||
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/tool.lua")(plg)
|
||||
|
||||
|
||||
plg.register_nodes = function(template)
|
||||
-- each loop is for one of the 4 IO ports
|
||||
for a = 0, 1 do
|
||||
@ -94,20 +93,16 @@ plg.register_nodes({
|
||||
|
||||
meta:set_string("instr", lcore.serialize(is))
|
||||
meta:set_int("valid", 0)
|
||||
meta:set_string("formspec", plg.to_formspec_string(is))
|
||||
meta:set_string("infotext", "FPGA")
|
||||
end,
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
if not minetest.is_player(clicker) then
|
||||
return
|
||||
end
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
if fields.program == nil then return end -- we only care when the user clicks "Program"
|
||||
local meta = minetest.get_meta(pos)
|
||||
local name = clicker:get_player_name()
|
||||
-- Erase formspecs of old FPGAs
|
||||
meta:set_string("formspec", "")
|
||||
local is = plg.from_formspec_fields(fields)
|
||||
|
||||
plg.open_formspecs[name] = pos
|
||||
local is = lcore.deserialize(meta:get_string("instr"))
|
||||
minetest.show_formspec(name, "mesecons:fpga", plg.to_formspec_string(is, nil))
|
||||
meta:set_string("instr", lcore.serialize(is))
|
||||
plg.update_formspec(pos, is)
|
||||
end,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
mesecons = {
|
||||
@ -121,12 +116,6 @@ plg.register_nodes({
|
||||
},
|
||||
after_dig_node = function(pos, node)
|
||||
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,
|
||||
on_blast = mesecon.on_blastnode,
|
||||
on_rotate = function(pos, node, user, mode)
|
||||
@ -164,12 +153,13 @@ plg.register_nodes({
|
||||
end
|
||||
|
||||
meta:set_string("instr", lcore.serialize(instr))
|
||||
plg.update_meta(pos, instr)
|
||||
plg.update_formspec(pos, instr)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
plg.to_formspec_string = function(is, err)
|
||||
|
||||
plg.to_formspec_string = function(is)
|
||||
local function dropdown_op(x, y, name, val)
|
||||
local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
|
||||
.. "0.75,0.5;" .. name .. ";" -- the height seems to be ignored?
|
||||
@ -190,20 +180,26 @@ plg.to_formspec_string = function(is, err)
|
||||
return s .. "]"
|
||||
end
|
||||
local function dropdown_action(x, y, name, val)
|
||||
local selected = 0
|
||||
local titles = { " " }
|
||||
for i, data in ipairs(lcore.get_operations()) do
|
||||
titles[i + 1] = data.fs_name
|
||||
if val == data.gate then
|
||||
selected = i + 1
|
||||
end
|
||||
local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
|
||||
.. "1.125,0.5;" .. name .. ";" -- the height seems to be ignored?
|
||||
s = s .. " , AND, OR, NOT, XOR,NAND, =,XNOR;"
|
||||
if val == nil then
|
||||
return s .. "0]" -- actually selects no field at all
|
||||
end
|
||||
return ("dropdown[%f,%f;1.125,0.5;%s;%s;%i]"):format(
|
||||
x, y, name, table.concat(titles, ","), selected)
|
||||
local mapping = {
|
||||
["and"] = 1,
|
||||
["or"] = 2,
|
||||
["not"] = 3,
|
||||
["xor"] = 4,
|
||||
["nand"] = 5,
|
||||
["buf"] = 6,
|
||||
["xnor"] = 7,
|
||||
}
|
||||
return s .. tostring(1 + mapping[val]) .. "]"
|
||||
end
|
||||
local s = "size[9,9]"..
|
||||
"label[3.4,-0.15;FPGA gate configuration]"..
|
||||
"button[7,7.5;2,2.5;program;Program]"..
|
||||
"button_exit[7,7.5;2,2.5;program;Program]"..
|
||||
"box[4.2,0.5;0.03,7;#ffffff]"..
|
||||
"label[0.25,0.25;op. 1]"..
|
||||
"label[1.0,0.25;gate type]"..
|
||||
@ -229,12 +225,6 @@ plg.to_formspec_string = function(is, err)
|
||||
y = 1 - 0.25
|
||||
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
|
||||
end
|
||||
|
||||
@ -249,11 +239,20 @@ plg.from_formspec_fields = function(fields)
|
||||
end
|
||||
end
|
||||
local function read_action(s)
|
||||
for i, data in ipairs(lcore.get_operations()) do
|
||||
if data.fs_name == s then
|
||||
return data.gate
|
||||
end
|
||||
if s == nil or s == " " then
|
||||
return nil
|
||||
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
|
||||
local is = {}
|
||||
for i = 1, 14 do
|
||||
@ -267,11 +266,12 @@ plg.from_formspec_fields = function(fields)
|
||||
return is
|
||||
end
|
||||
|
||||
plg.update_meta = function(pos, is)
|
||||
plg.update_formspec = function(pos, is)
|
||||
if type(is) == "string" then -- serialized string
|
||||
is = lcore.deserialize(is)
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local form = plg.to_formspec_string(is)
|
||||
|
||||
local err = lcore.validate(is)
|
||||
if err == nil then
|
||||
@ -280,20 +280,17 @@ plg.update_meta = function(pos, is)
|
||||
else
|
||||
meta:set_int("valid", 0)
|
||||
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
|
||||
|
||||
meta:set_string("formspec", form)
|
||||
|
||||
-- reset ports and run programmed logic
|
||||
plg.setports(pos, false, false, false, false)
|
||||
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
|
||||
|
||||
plg.red_box_around = function(i)
|
||||
@ -412,38 +409,6 @@ plg.setports = function(pos, A, B, C, D) -- sets states of OUTPUT
|
||||
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({
|
||||
output = "mesecons_fpga:fpga0000 2",
|
||||
|
@ -1,27 +1,5 @@
|
||||
|
||||
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
|
||||
lg.serialize = function(t)
|
||||
local function _op(t)
|
||||
@ -33,14 +11,20 @@ lg.serialize = function(t)
|
||||
return tostring(t.n)
|
||||
end
|
||||
end
|
||||
-- Serialize actions (gates) from eg. "and" to "&"
|
||||
local function _action(action)
|
||||
for i, data in ipairs(operations) do
|
||||
if data.gate == action then
|
||||
return data.short
|
||||
end
|
||||
local function _action(s)
|
||||
if s == nil then
|
||||
return " "
|
||||
end
|
||||
return " "
|
||||
local mapping = {
|
||||
["and"] = "&",
|
||||
["or"] = "|",
|
||||
["not"] = "~",
|
||||
["xor"] = "^",
|
||||
["nand"] = "?", --dunno
|
||||
["buf"] = "_",
|
||||
["xnor"] = "=",
|
||||
}
|
||||
return mapping[s]
|
||||
end
|
||||
|
||||
local s = ""
|
||||
@ -64,14 +48,18 @@ lg.deserialize = function(s)
|
||||
return {type = "reg", n = tonumber(c)}
|
||||
end
|
||||
end
|
||||
-- Deserialize actions (gates) from eg. "&" to "and"
|
||||
local function _action(action)
|
||||
for i, data in ipairs(operations) do
|
||||
if data.short == action then
|
||||
return data.gate
|
||||
end
|
||||
end
|
||||
-- nil
|
||||
local function _action(c)
|
||||
local mapping = {
|
||||
["&"] = "and",
|
||||
["|"] = "or",
|
||||
["~"] = "not",
|
||||
["^"] = "xor",
|
||||
["?"] = "nand",
|
||||
["_"] = "buf",
|
||||
["="] = "xnor",
|
||||
[" "] = nil,
|
||||
}
|
||||
return mapping[c]
|
||||
end
|
||||
|
||||
local ret = {}
|
||||
@ -121,25 +109,16 @@ lg.validate_single = function(t, i)
|
||||
return false
|
||||
end
|
||||
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
|
||||
if not gate_data then
|
||||
return {i = i, msg = "Gate type is required"}
|
||||
elseif gate_data.unary then
|
||||
if elem.action == nil then
|
||||
return {i = i, msg = "Gate type required"}
|
||||
elseif elem.action == "not" or elem.action == "buf" then
|
||||
if elem.op1 ~= nil or elem.op2 == nil or elem.dst == nil then
|
||||
return {i = i, msg = "Second operand (only) and destination are required"}
|
||||
return {i = i, msg = "Second operand (only) and destination required"}
|
||||
end
|
||||
else
|
||||
if elem.op1 == nil or elem.op2 == nil or elem.dst == nil then
|
||||
return {i = i, msg = "Operands and destination are required"}
|
||||
return {i = i, msg = "Operands and destination required"}
|
||||
end
|
||||
end
|
||||
-- check whether operands/destination are identical
|
||||
@ -180,12 +159,21 @@ end
|
||||
-- interpreter
|
||||
lg.interpret = function(t, a, b, c, d)
|
||||
local function _action(s, v1, v2)
|
||||
for i, data in ipairs(operations) do
|
||||
if data.gate == s then
|
||||
return data.func(v1, v2)
|
||||
end
|
||||
if s == "and" then
|
||||
return v1 and v2
|
||||
elseif s == "or" then
|
||||
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
|
||||
return false -- unknown gate
|
||||
end
|
||||
local function _op(t, regs, io_in)
|
||||
if t.type == "reg" then
|
||||
|
@ -22,7 +22,7 @@ minetest.register_tool("mesecons_fpga:programmer", {
|
||||
end
|
||||
itemstack:set_metadata(meta:get_string("instr"))
|
||||
minetest.chat_send_player(placer:get_player_name(), "FPGA gate configuration was successfully copied!")
|
||||
|
||||
|
||||
return itemstack
|
||||
end,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@ -34,22 +34,17 @@ minetest.register_tool("mesecons_fpga:programmer", {
|
||||
if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then
|
||||
return itemstack
|
||||
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()
|
||||
if imeta == "" then
|
||||
minetest.chat_send_player(player_name, "Use shift+right-click to copy a gate configuration first.")
|
||||
minetest.chat_send_player(user:get_player_name(), "Use shift+right-click to copy a gate configuration first.")
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("instr", imeta)
|
||||
plg.update_meta(pos, imeta)
|
||||
minetest.chat_send_player(player_name, "Gate configuration was successfully written to FPGA!")
|
||||
plg.update_formspec(pos, imeta)
|
||||
minetest.chat_send_player(user:get_player_name(), "Gate configuration was successfully written to FPGA!")
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
@ -1,14 +1,6 @@
|
||||
local selection_box = {
|
||||
type = "fixed",
|
||||
fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }
|
||||
}
|
||||
|
||||
local nodebox = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- bottom slab
|
||||
{ -6/16, -7/16, -6/16, 6/16, -6/16, 6/16 }
|
||||
},
|
||||
fixed = {{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }},
|
||||
}
|
||||
|
||||
local function gate_rotate_rules(node, rules)
|
||||
@ -76,7 +68,7 @@ local function register_gate(name, inputnumber, assess, recipe, description)
|
||||
is_ground_content = false,
|
||||
drawtype = "nodebox",
|
||||
drop = basename.."_off",
|
||||
selection_box = selection_box,
|
||||
selection_box = nodebox,
|
||||
node_box = nodebox,
|
||||
walkable = true,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
@ -86,16 +78,8 @@ local function register_gate(name, inputnumber, assess, recipe, description)
|
||||
inputnumber = inputnumber,
|
||||
after_dig_node = mesecon.do_cooldown,
|
||||
},{
|
||||
tiles = {
|
||||
"jeija_microcontroller_bottom.png^".."jeija_gate_off.png^"..
|
||||
"jeija_gate_output_off.png^".."jeija_gate_"..name..".png",
|
||||
"jeija_microcontroller_bottom.png^".."jeija_gate_output_off.png^"..
|
||||
"[transformFY",
|
||||
"jeija_gate_side.png^".."jeija_gate_side_output_off.png",
|
||||
"jeija_gate_side.png",
|
||||
"jeija_gate_side.png",
|
||||
"jeija_gate_side.png"
|
||||
},
|
||||
tiles = {"jeija_microcontroller_bottom.png^".."jeija_gate_off.png^"..
|
||||
"jeija_gate_"..name..".png"},
|
||||
groups = {dig_immediate = 2, overheat = 1},
|
||||
mesecons = { receptor = {
|
||||
state = "off",
|
||||
@ -105,16 +89,8 @@ local function register_gate(name, inputnumber, assess, recipe, description)
|
||||
action_change = update_gate
|
||||
}}
|
||||
},{
|
||||
tiles = {
|
||||
"jeija_microcontroller_bottom.png^".."jeija_gate_on.png^"..
|
||||
"jeija_gate_output_on.png^".."jeija_gate_"..name..".png",
|
||||
"jeija_microcontroller_bottom.png^".."jeija_gate_output_on.png^"..
|
||||
"[transformFY",
|
||||
"jeija_gate_side.png^".."jeija_gate_side_output_on.png",
|
||||
"jeija_gate_side.png",
|
||||
"jeija_gate_side.png",
|
||||
"jeija_gate_side.png"
|
||||
},
|
||||
tiles = {"jeija_microcontroller_bottom.png^".."jeija_gate_on.png^"..
|
||||
"jeija_gate_"..name..".png"},
|
||||
groups = {dig_immediate = 2, not_in_creative_inventory = 1, overheat = 1},
|
||||
mesecons = { receptor = {
|
||||
state = "on",
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 98 B |
Binary file not shown.
Before Width: | Height: | Size: 99 B |
Binary file not shown.
Before Width: | Height: | Size: 136 B |
Binary file not shown.
Before Width: | Height: | Size: 109 B |
Binary file not shown.
Before Width: | Height: | Size: 110 B |
@ -163,7 +163,7 @@ local function add_pos(positions, pos)
|
||||
end
|
||||
|
||||
local function are_protected(positions, player_name)
|
||||
local mode = mesecon.setting("mvps_protection_mode", "compat")
|
||||
local mode = mesecon.setting("mvps_protection_mode", "normal")
|
||||
if mode == "ignore" then
|
||||
return false
|
||||
end
|
||||
@ -285,7 +285,7 @@ function mesecon.mvps_move_objects(pos, dir, nodestack, movefactor)
|
||||
end
|
||||
movefactor = movefactor or 1
|
||||
dir = vector.multiply(dir, movefactor)
|
||||
for id, obj in pairs(minetest.get_objects_inside_radius(pos, #nodestack + 1)) do
|
||||
for id, obj in pairs(minetest.object_refs) do
|
||||
local obj_pos = obj:get_pos()
|
||||
local cbox = obj:get_properties().collisionbox
|
||||
local min_pos = vector.add(obj_pos, vector.new(cbox[1], cbox[2], cbox[3]))
|
||||
|
@ -74,8 +74,8 @@ minetest.register_abm(
|
||||
{nodenames = {"mesecons_solarpanel:solar_panel_off"},
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
action = function(pos, node)
|
||||
local light = minetest.get_node_light(pos)
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
local light = minetest.get_node_light(pos, nil)
|
||||
|
||||
if light >= 12 then
|
||||
node.name = "mesecons_solarpanel:solar_panel_on"
|
||||
@ -89,8 +89,8 @@ minetest.register_abm(
|
||||
{nodenames = {"mesecons_solarpanel:solar_panel_on"},
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
action = function(pos, node)
|
||||
local light = minetest.get_node_light(pos)
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
local light = minetest.get_node_light(pos, nil)
|
||||
|
||||
if light < 12 then
|
||||
node.name = "mesecons_solarpanel:solar_panel_off"
|
||||
|
Reference in New Issue
Block a user