MASSIVE UPDATE. Added booleans, boolean comparison blocks and modified the IF block's behavior (old scripts are still just about supported, though).
14
README.md
|
@ -33,6 +33,10 @@ Digigrates can be adjusted by sending messages - they include "on", "off", "togg
|
|||
|
||||
Scriptblocks are blocks that you can use for creating simple programs. They are one of the most complicated parts of this mod, which can be a good thing or a bad thing depending on your viewpoint.
|
||||
|
||||
### An important note
|
||||
|
||||
Scriptblocks can handle values of various types, such as objects (tables), strings, numbers and booleans. Recent changes mean that scriptblocks no longer convert between them when it is unnecessary. As a result, attempting to compare the equality of, say, a boolean with the string "true" will result in false, because a boolean is not a string. Please keep this in mind as you program with scriptblocks, to avoid messy coding (building? :P) and frustration.
|
||||
|
||||
### Basics
|
||||
|
||||
When the Mesecon Detector scriptblock (which is yellow with an exclamation mark on it) receives mesecon power, it triggers any scriptblocks adjacent to it. Each scriptblock will then trigger each scriptblock adjacent to itself (excluding the one that triggered it in the first place).
|
||||
|
@ -41,6 +45,8 @@ When the Mesecon Detector scriptblock (which is yellow with an exclamation mark
|
|||
|
||||
You can store data in these scripts with the SET (looks like :=) and GET (looks like []) blocks. Each script can keep track of up to two values during execution (@info and @last), and the GET block will update @last to the previous @info, while updating @info to the value of the chosen variable. All scriptblock inputs may have "@info" or "@last" written inside them, which will be substituted for the corresponding values at runtime.
|
||||
|
||||
@last usually updates to the previous @info when @info gets changed, however there are exceptions for when the change is small (e.g. SET ATTRIBUTE OF OBJECT and the upcoming NOT) - this change probably broke any scripts larger than a few blocks, but I think it's a positive change in the long run.
|
||||
|
||||
### Program channels
|
||||
|
||||
Program channels are channels you can set to avoid clashing with other programs that may use similar or equal variable names. You can still set the variables of other channels by entering them into SET and GET variable blocks.
|
||||
|
@ -49,9 +55,15 @@ Program channels are channels you can set to avoid clashing with other programs
|
|||
|
||||
The mathematical operators (add, subtract, multiply, divide) work in much the same way as GET - they update @last to the previous @info, and update @info to the result of the calculation. To add two values together, you would do (or rather, build) something along the lines of "GET a; GET b; ADD @last @info; SET c @info;", which will set c to the sum of a and b.
|
||||
|
||||
### Booleans
|
||||
|
||||
There are comparison operators which will return true or false depending on whether their condition is true. For example, a "LESS THAN" block with A = 3 and B = 2 will return false, while one with A = 1 instead will return true. Booleans themselves can be manipulated with NOT (turns false into true and vice versa), AND (only true if both operands are true) and OR (only false if both operands are false).
|
||||
|
||||
Note that in the case of AND and OR, the two operands are taken to be @info and @last respectively, since they are expected to be booleans - and being able to input "true" or "false" directly into one doesn't make sense to me (what's the point of "true OR x" or "false AND y"?).
|
||||
|
||||
### Conditional
|
||||
|
||||
The teal "?" block will lend execution in one direction if the two input values are equal, and the other if they are not equal.
|
||||
The teal "?" block will lend execution towards the green side if the value reaching it (@info) is non-nil, and non-false. If it /is/ nil or false, execution is lent towards the red side instead. Formerly this took two operands and compared them, but that functionality has been replaced by the comparison operators described above.
|
||||
|
||||
### Print
|
||||
|
||||
|
|
194
scriptblock.lua
|
@ -29,6 +29,7 @@ local function set_storage(data)
|
|||
end
|
||||
|
||||
local function stringify(t)
|
||||
if type(t) ~= "table" then return tostring(t) end
|
||||
return minetest.serialize(t):sub(("return "):len()+1, -1)
|
||||
end
|
||||
|
||||
|
@ -62,7 +63,8 @@ rmod.scriptblock.run = function (pos, sender, info, last, channel)
|
|||
|
||||
-- If the block is a script block...
|
||||
if def and def.scriptblock then
|
||||
local new_info, faces = def.scriptblock(pos, node, sender, info, last, channel)
|
||||
-- The flag tells the code NOT to update @last, even if there is a change in @info.
|
||||
local new_info, faces, flag = def.scriptblock(pos, node, sender, info, last, channel)
|
||||
-- if new_info == rmod.scriptblock.stop_signal then return end -- Looks like the block doesn't want to conduct.
|
||||
if not faces then
|
||||
faces = {true, true, true, true, true, true}
|
||||
|
@ -88,7 +90,13 @@ rmod.scriptblock.run = function (pos, sender, info, last, channel)
|
|||
local new_def = minetest.registered_nodes[new_name]
|
||||
|
||||
if new_def and new_def.scriptblock then
|
||||
table.insert(local_queue, {new_pos, pos, new_info, info == new_info and last or info, channel})
|
||||
local new_last
|
||||
if flag or info == new_info then
|
||||
new_last = last
|
||||
else
|
||||
new_last = info
|
||||
end
|
||||
table.insert(local_queue, {new_pos, pos, new_info, new_last, channel})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -103,7 +111,7 @@ rmod.scriptblock.escape = function (text, info, last)
|
|||
|
||||
if type(info) == "table" then info = stringify(info) or "" end
|
||||
if type(last) == "table" then last = stringify(last) or "" end
|
||||
return text and text:gsub("@info", info or ""):gsub("@last", last or "") or ""
|
||||
return text and text:gsub("@info", tostring(info) or ""):gsub("@last", tostring(last) or "")-- or ""
|
||||
end
|
||||
|
||||
local time = 0
|
||||
|
@ -162,6 +170,8 @@ field[value;Value;${value}]
|
|||
|
||||
if channel == "" or not channel then channel = main_channel end
|
||||
|
||||
if not varname then varname = "" end
|
||||
|
||||
if not store[channel] then store[channel] = {} end
|
||||
store[channel][varname] = value
|
||||
|
||||
|
@ -205,12 +215,13 @@ field[varname;Varname;${varname}]
|
|||
local store = get_storage()
|
||||
|
||||
if channel == "" or not channel then channel = main_channel end
|
||||
if not varname then return end
|
||||
|
||||
if not store[channel] then store[channel] = {} end
|
||||
|
||||
debug("GET " .. tostring(channel) .. "." .. tostring(varname) .. ": " .. tostring(store[channel][varname] or ""))
|
||||
|
||||
return store[channel][varname] or ""
|
||||
return store[channel][varname]
|
||||
end
|
||||
})
|
||||
minetest.register_node("rmod:scriptblock_mesecon", {
|
||||
|
@ -222,6 +233,7 @@ minetest.register_node("rmod:scriptblock_mesecon", {
|
|||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", [[
|
||||
field[channel;]] .. program_channel .. [[;${channel}]
|
||||
field[info;Starting @info;${info}]
|
||||
]])
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
@ -233,6 +245,9 @@ field[channel;]] .. program_channel .. [[;${channel}]
|
|||
if (fields.channel) then
|
||||
minetest.get_meta(pos):set_string("channel", fields.channel)
|
||||
end
|
||||
if (fields.info) then
|
||||
minetest.get_meta(pos):set_string("info", fields.info)
|
||||
end
|
||||
end,
|
||||
scriptblock = function (pos, node, sender, info, last, main_channel)
|
||||
return info
|
||||
|
@ -240,10 +255,11 @@ field[channel;]] .. program_channel .. [[;${channel}]
|
|||
mesecons = {effector = {
|
||||
action_on = function (pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local channel = meta:get_string("channel")
|
||||
local channel = meta:get_string("channel")
|
||||
local info = meta:get_string("info")
|
||||
|
||||
debug("ACTIVATED")
|
||||
table.insert(queue, {pos, pos, "", "", channel or ""})
|
||||
table.insert(queue, {pos, pos, info or "", "", channel or ""})
|
||||
end,
|
||||
}}
|
||||
})
|
||||
|
@ -277,6 +293,9 @@ field[message;Message;${message}]
|
|||
local plr = rmod.scriptblock.escape(meta:get_string("player"), info, last)
|
||||
local msg = rmod.scriptblock.escape(meta:get_string("message"), info, last)
|
||||
|
||||
if not plr then return info end
|
||||
if not msg then return info end
|
||||
|
||||
if type(msg) == "table" then msg = stringify(msg) end
|
||||
|
||||
if plr == "" then
|
||||
|
@ -289,34 +308,15 @@ field[message;Message;${message}]
|
|||
end
|
||||
})
|
||||
minetest.register_node("rmod:scriptblock_if", {
|
||||
description = "Scriptblock: If Equals",
|
||||
description = "Scriptblock: If",
|
||||
tiles = {"rmod_scriptblock_if_top.png", "rmod_scriptblock_if_bottom.png",
|
||||
"rmod_scriptblock_if_right.png", "rmod_scriptblock_if_left.png",
|
||||
"rmod_scriptblock_if_truth.png", "rmod_scriptblock_if_falsth.png"},
|
||||
groups = {oddly_breakable_by_hand = 1},
|
||||
use_texture_alpha = true,
|
||||
paramtype2 = "facedir",
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", [[
|
||||
field[a;A;${a}]
|
||||
field[b;B;${b}]
|
||||
]])
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, 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.a) then
|
||||
minetest.get_meta(pos):set_string("a", fields.a)
|
||||
end
|
||||
if (fields.b) then
|
||||
minetest.get_meta(pos):set_string("b", fields.b)
|
||||
end
|
||||
end,
|
||||
scriptblock = function (pos, node, sender, info, last, main_channel)
|
||||
-- compatibility for back when this was "IF EQUALS"
|
||||
local meta = minetest.get_meta(pos)
|
||||
local a = rmod.scriptblock.escape(meta:get_string("a"), info, last)
|
||||
local b = rmod.scriptblock.escape(meta:get_string("b"), info, last)
|
||||
|
@ -340,7 +340,11 @@ field[b;B;${b}]
|
|||
b = stringify(b) or b
|
||||
end]]
|
||||
|
||||
return unpack(compare(a, b) and {info, truth} or {info, falsth})
|
||||
if a == "" and b == "" then
|
||||
return unpack(info and {info, truth} or {info, falsth})
|
||||
else
|
||||
return unpack(compare(a, b) and {info, truth} or {info, falsth})
|
||||
end
|
||||
end
|
||||
})
|
||||
minetest.register_node("rmod:scriptblock_guide", {
|
||||
|
@ -566,6 +570,8 @@ field[value;Value;${value}]
|
|||
local propname = rmod.scriptblock.escape(meta:get_string("propname"), info, last)
|
||||
local value = rmod.scriptblock.escape(meta:get_string("value"), info, last)
|
||||
|
||||
if not propname then return end
|
||||
|
||||
if type(info) ~= "table" then
|
||||
--[[if type(info) == "string" then
|
||||
local deserialized = minetest.deserialize(info)
|
||||
|
@ -585,7 +591,7 @@ field[value;Value;${value}]
|
|||
|
||||
info[propname] = value
|
||||
|
||||
return info
|
||||
return info, nil, true -- Flag to avoid losing @last.
|
||||
end
|
||||
})
|
||||
minetest.register_node("rmod:scriptblock_get_attribute", {
|
||||
|
@ -613,6 +619,8 @@ field[propname;Attribute Name;${propname}]
|
|||
local meta = minetest.get_meta(pos)
|
||||
local propname = rmod.scriptblock.escape(meta:get_string("propname"), info, last)
|
||||
|
||||
if not propname then return end
|
||||
|
||||
if type(info) ~= "table" then
|
||||
--[[if type(info) == "string" then
|
||||
local deserialized = minetest.deserialize(info)
|
||||
|
@ -680,3 +688,131 @@ field[digichannel;Digiline channel;${digichannel}]
|
|||
end,
|
||||
}}
|
||||
})
|
||||
|
||||
minetest.register_node("rmod:scriptblock_not", {
|
||||
description = "Scriptblock: Not Gate",
|
||||
tiles = {"rmod_scriptblock_not.png"},
|
||||
groups = {oddly_breakable_by_hand = 1},
|
||||
use_texture_alpha = true,
|
||||
scriptblock = function (pos, node, sender, info, last, main_channel)
|
||||
return not info
|
||||
end,
|
||||
})
|
||||
minetest.register_node("rmod:scriptblock_and", {
|
||||
description = "Scriptblock: And Gate",
|
||||
tiles = {"rmod_scriptblock_and.png"},
|
||||
groups = {oddly_breakable_by_hand = 1},
|
||||
use_texture_alpha = true,
|
||||
scriptblock = function (pos, node, sender, info, last, main_channel)
|
||||
return info and last
|
||||
end,
|
||||
})
|
||||
minetest.register_node("rmod:scriptblock_or", {
|
||||
description = "Scriptblock: Or Gate",
|
||||
tiles = {"rmod_scriptblock_or.png"},
|
||||
groups = {oddly_breakable_by_hand = 1},
|
||||
use_texture_alpha = true,
|
||||
scriptblock = function (pos, node, sender, info, last, main_channel)
|
||||
return info or last
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("rmod:scriptblock_equals", {
|
||||
description = "Scriptblock: Equals",
|
||||
tiles = {"rmod_scriptblock_equals.png"},
|
||||
groups = {oddly_breakable_by_hand = 1},
|
||||
use_texture_alpha = true,
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", [[
|
||||
field[a;A;${a}]
|
||||
field[b;B;${b}]
|
||||
]])
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, 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.a) then
|
||||
minetest.get_meta(pos):set_string("a", fields.a)
|
||||
end
|
||||
if (fields.b) then
|
||||
minetest.get_meta(pos):set_string("b", fields.b)
|
||||
end
|
||||
end,
|
||||
scriptblock = function (pos, node, sender, info, last, main_channel)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local a = rmod.scriptblock.escape(meta:get_string("a"), info, last)
|
||||
local b = rmod.scriptblock.escape(meta:get_string("b"), info, last)
|
||||
|
||||
return compare(a, b)
|
||||
end,
|
||||
})
|
||||
minetest.register_node("rmod:scriptblock_lt", {
|
||||
description = "Scriptblock: Less than",
|
||||
tiles = {"rmod_scriptblock_lt.png"},
|
||||
groups = {oddly_breakable_by_hand = 1},
|
||||
use_texture_alpha = true,
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", [[
|
||||
field[a;A;${a}]
|
||||
field[b;B;${b}]
|
||||
]])
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, 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.a) then
|
||||
minetest.get_meta(pos):set_string("a", fields.a)
|
||||
end
|
||||
if (fields.b) then
|
||||
minetest.get_meta(pos):set_string("b", fields.b)
|
||||
end
|
||||
end,
|
||||
scriptblock = function (pos, node, sender, info, last, main_channel)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local a = rmod.scriptblock.escape(meta:get_string("a"), info, last)
|
||||
local b = rmod.scriptblock.escape(meta:get_string("b"), info, last)
|
||||
|
||||
return (tonumber(a) or 0) < (tonumber(b) or 0)
|
||||
end,
|
||||
})
|
||||
minetest.register_node("rmod:scriptblock_gt", {
|
||||
description = "Scriptblock: Greater than",
|
||||
tiles = {"rmod_scriptblock_gt.png"},
|
||||
groups = {oddly_breakable_by_hand = 1},
|
||||
use_texture_alpha = true,
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", [[
|
||||
field[a;A;${a}]
|
||||
field[b;B;${b}]
|
||||
]])
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, 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.a) then
|
||||
minetest.get_meta(pos):set_string("a", fields.a)
|
||||
end
|
||||
if (fields.b) then
|
||||
minetest.get_meta(pos):set_string("b", fields.b)
|
||||
end
|
||||
end,
|
||||
scriptblock = function (pos, node, sender, info, last, main_channel)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local a = rmod.scriptblock.escape(meta:get_string("a"), info, last)
|
||||
local b = rmod.scriptblock.escape(meta:get_string("b"), info, last)
|
||||
|
||||
return (tonumber(a) or 0) > (tonumber(b) or 0)
|
||||
end,
|
||||
})
|
||||
|
|
After Width: | Height: | Size: 251 B |
After Width: | Height: | Size: 215 B |
After Width: | Height: | Size: 269 B |
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 265 B |
Before Width: | Height: | Size: 217 B After Width: | Height: | Size: 264 B |
After Width: | Height: | Size: 263 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 216 B |