MASSIVE UPDATE. Added booleans, boolean comparison blocks and modified the IF block's behavior (old scripts are still just about supported, though).

This commit is contained in:
rdococ 2017-08-07 01:42:04 +01:00
parent 9e12f87a25
commit 2036096d3d
10 changed files with 178 additions and 30 deletions

View File

@ -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

View File

@ -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,
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 B

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B