THE SCRIPTBLOCKS ARE HERE!!! Scriptblocks are blocks you can build scripts with - they're pretty cool, even if they're super complicated.

This commit is contained in:
rdococ 2017-08-06 05:46:25 +01:00
parent 2fb9eaaea8
commit 1c900a23f5
25 changed files with 542 additions and 6 deletions

View File

@ -1,8 +1,7 @@
# rmod
RMod mod for Minetest with various cool nodes.
Conveyors
-
## Conveyors
Conveyors are nodes that can carry entities, such as players and items. There are a few bugs with it, but it works fine for the most part.
@ -16,13 +15,11 @@ Digiconveyors
Digiconveyors can not only be turned on and off with digilines, but they can also be reversed (which flips their facing direction). The messages available at the moment are "on", "off", "toggle" (functions as both "on" and "off"), "reverse" (inverts the direction), "left" (turns left 90 degrees) and "right".
Crates
-
## Crates
Crates are like chests, but you can pick them up with the items inside. As a result, you can also stack crates indefinitely.
Grates
-
## Grates
Grates are nodes which let water flow through, but not players or items.
@ -35,3 +32,42 @@ Digigrates
-
Digigrates can be adjusted by sending messages - they include "on", "off", "toggle" and "set". That last one, "set", is sent as a table {command = "sent", value = x}, substituting x for the percentage of water you want to pass through (although, tbh, it's more complicated than that).
## Scriptblocks
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.
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).
Variables
-
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.
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.
Mathematical operators
-
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.
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.
Print
-
The purple blocks with a speech bubble on them will print the message specified inside to the chat.
Guide
-
The grey blocks which look like three arrows that converge into one are guides - they are used to aid looping by funnelling all execution in one direction - never will a guide execute nearby blocks other than the one it is pointing to.

View File

@ -4,6 +4,7 @@ local modpath = minetest.get_modpath("rmod")
dofile(modpath .. "/grate.lua")
dofile(modpath .. "/conveyor.lua")
dofile(modpath .. "/crate.lua")
dofile(modpath .. "/scriptblock.lua")
if minetest.get_modpath("mesecons") then
dofile(modpath .. "/meseconveyor.lua")

499
scriptblock.lua Normal file
View File

@ -0,0 +1,499 @@
--[[
scriptblock = function (pos, node, sender, info, last, main_channel)
-- "pos" and "node" are the position and the node information of the scriptblock being ran.
-- "sender" would be the position of the node responsible for activating it.
-- "info" is any information the previous node has sent to it.
-- "last" is the information that "info" /was/ before it was last changed.
-- "channel" is the channel in which variables are stored.
blah blah blah
return new_info, faces -- Information to pass to the next node(s), and information on which adjacent spaces we should even try to signal to.
end
]]
local do_debug = false
local function debug(text)
if do_debug then minetest.chat_send_all("[SB] " .. text) end
end
local storage = minetest.get_mod_storage()
-- Each section of the RMod gets its own storage, to prevent interference.
-- RMod sections can still affect other sections, though only when intended.
local function get_storage()
return minetest.deserialize(storage:get_string("scriptblock")) or {}
end
local function set_storage(data)
return storage:set_string("scriptblock", minetest.serialize(data))
end
-- To avoid lag and stack overflows, we add the data to a queue and then execute it with a globalstep.
local queue = {}
rmod.scriptblock = {}
rmod.scriptblock.run = function (pos, sender, info, last, channel)
local local_queue = {}
-- Get information about this script block we are told to execute.
local node = minetest.get_node(pos)
local name = node.name
local def = minetest.registered_nodes[name]
-- 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)
-- 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}
end
-- Check neighboring nodes; if they also have scriptblock and aren't the sender, execute them.
for i=1,6 do
if faces[i] then
local dir = vector.new(0, 0, 0)
if i == 1 then dir.y = 1
elseif i == 2 then dir.y = -1
elseif i == 3 then dir.x = 1
elseif i == 4 then dir.x = -1
elseif i == 5 then dir.z = 1
elseif i == 6 then dir.z = -1 end
local new_pos = vector.add(pos, dir)
-- This is required, otherwise you'd have an unintentional feedback loop.
-- Feedback loops can still be created intentionally, though.
if not vector.equals(new_pos, sender) then
local new_node = minetest.get_node(new_pos)
local new_name = new_node.name
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})
end
end
end
end
end
return local_queue
end
rmod.scriptblock.escape = function (text, info, last)
return text and text:gsub("@info", info):gsub("@last", last) or ""
end
local time = 0
minetest.register_globalstep(function (dtime)
time = time + dtime
if time > 0.05 then time = 0 else return end
local new_queue = {}
for i,data in pairs(queue) do
local new_list = rmod.scriptblock.run(unpack(data))
if new_list then
for _,new_item in pairs(new_list) do
table.insert(new_queue, new_item)
end
end
end
queue = new_queue
end)
minetest.register_node("rmod:scriptblock_set", {
description = "Scriptblock: Set",
tiles = {"rmod_scriptblock_set.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[channel;Program channel (optional);${channel}]
field[varname;Varname;${varname}]
field[value;Value;${value}]
]])
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.channel) then
minetest.get_meta(pos):set_string("channel", fields.channel)
end
if (fields.varname) then
minetest.get_meta(pos):set_string("varname", fields.varname)
end
if (fields.value) then
minetest.get_meta(pos):set_string("value", fields.value)
end
end,
scriptblock = function (pos, node, sender, info, last, main_channel)
local meta = minetest.get_meta(pos)
local channel = rmod.scriptblock.escape(meta:get_string("channel"), info, last)
local varname = rmod.scriptblock.escape(meta:get_string("varname"), info, last)
local value = rmod.scriptblock.escape(meta:get_string("value"), info, last)
local store = get_storage()
if channel == "" or not channel then channel = main_channel end
if not store[channel] then store[channel] = {} end
store[channel][varname] = value
set_storage(store)
debug("SET " .. channel .. "." .. varname .. ": " .. value)
return info
end
})
minetest.register_node("rmod:scriptblock_get", {
description = "Scriptblock: Get",
tiles = {"rmod_scriptblock_get.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[channel;Program channel (optional);${channel}]
field[varname;Varname;${varname}]
]])
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.channel) then
minetest.get_meta(pos):set_string("channel", fields.channel)
end
if (fields.varname) then
minetest.get_meta(pos):set_string("varname", fields.varname)
end
end,
scriptblock = function (pos, node, sender, info, last, main_channel)
local meta = minetest.get_meta(pos)
local channel = rmod.scriptblock.escape(meta:get_string("channel"), info, last)
local varname = rmod.scriptblock.escape(meta:get_string("varname"), info, last)
local store = get_storage()
if channel == "" or not channel then channel = main_channel end
if not store[channel] then store[channel] = {} end
debug("GET " .. channel .. "." .. varname .. ": " .. (store[channel][varname] or ""))
return store[channel][varname] or ""
end
})
minetest.register_node("rmod:scriptblock_mesecon", {
description = "Scriptblock: Mesecon Detector",
tiles = {"rmod_scriptblock_mesecon.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[channel;Program channel;${channel}]
]])
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.channel) then
minetest.get_meta(pos):set_string("channel", fields.channel)
end
end,
scriptblock = function (pos, node, sender, info, last, main_channel)
return info
end,
mesecons = {effector = {
action_on = function (pos, node)
local meta = minetest.get_meta(pos)
local channel = meta:get_string("channel")
debug("ACTIVATED")
table.insert(queue, {pos, pos, "", "", channel or ""})
end,
}}
})
minetest.register_node("rmod:scriptblock_print", {
description = "Scriptblock: Print",
tiles = {"rmod_scriptblock_print.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[player;Player;${player}]
field[message;Message;${message}]
]])
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.player) then
minetest.get_meta(pos):set_string("player", fields.player)
end
if (fields.message) then
minetest.get_meta(pos):set_string("message", fields.message)
end
end,
scriptblock = function (pos, node, sender, info, last, main_channel)
local meta = minetest.get_meta(pos)
local plr = rmod.scriptblock.escape(meta:get_string("player"), info, last)
local msg = rmod.scriptblock.escape(meta:get_string("message"), info, last)
if plr == "" then
minetest.chat_send_all("Scriptblock -> all: " .. msg)
else
minetest.chat_send_player(plr, "Scriptblock -> you: " .. msg)
end
return info
end
})
minetest.register_node("rmod:scriptblock_if", {
description = "Scriptblock: If Equals",
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)
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)
local facedir = node.param2
local dir = minetest.facedir_to_dir(facedir)
-- Y, -Y, X, -X, Z, -Z.
local truth = {}
local falsth = {}
if dir.x == 1 then truth[3] = true; falsth[4] = true
elseif dir.x == -1 then truth[4] = true; falsth[3] = true
elseif dir.z == 1 then truth[5] = true; falsth[6] = true
elseif dir.z == -1 then truth[6] = true; falsth[5] = true end
return unpack(a == b and {info, truth} or {info, falsth})
end
})
minetest.register_node("rmod:scriptblock_guide", {
description = "Scriptblock: One-Way Guide",
tiles = {"rmod_scriptblock_guide_top.png", "rmod_scriptblock_guide_bottom.png",
"rmod_scriptblock_guide_right.png", "rmod_scriptblock_guide_left.png",
"rmod_scriptblock_guide_front.png", "rmod_scriptblock_guide_back.png"},
groups = {oddly_breakable_by_hand = 1},
use_texture_alpha = true,
paramtype2 = "facedir",
scriptblock = function (pos, node, sender, info, last, main_channel)
local facedir = node.param2
local dir = minetest.facedir_to_dir(facedir)
-- Y, -Y, X, -X, Z, -Z.
local guide = {}
if dir.x == 1 then guide[3] = true
elseif dir.x == -1 then guide[4] = true
elseif dir.z == 1 then guide[5] = true
elseif dir.z == -1 then guide[6] = true end
return info, guide
end
})
minetest.register_node("rmod:scriptblock_add", {
description = "Scriptblock: Add",
tiles = {"rmod_scriptblock_add.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)
local facedir = node.param2
local dir = minetest.facedir_to_dir(facedir)
return tostring((tonumber(a) or 0) + (tonumber(b) or 0))
end
})
minetest.register_node("rmod:scriptblock_subtract", {
description = "Scriptblock: Subtract",
tiles = {"rmod_scriptblock_subtract.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)
local facedir = node.param2
local dir = minetest.facedir_to_dir(facedir)
return tostring((tonumber(a) or 0) - (tonumber(b) or 0))
end
})
minetest.register_node("rmod:scriptblock_multiply", {
description = "Scriptblock: Multiply",
tiles = {"rmod_scriptblock_multiply.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)
local facedir = node.param2
local dir = minetest.facedir_to_dir(facedir)
return tostring((tonumber(a) or 0) * (tonumber(b) or 0))
end
})
minetest.register_node("rmod:scriptblock_divide", {
description = "Scriptblock: Divide",
tiles = {"rmod_scriptblock_divide.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)
local facedir = node.param2
local dir = minetest.facedir_to_dir(facedir)
return tostring((tonumber(a) or 0) / (tonumber(b) or 0))
end
})
minetest.register_node("rmod:scriptblock_nearest", {
description = "Scriptblock: Get Nearest Player",
tiles = {"rmod_scriptblock_nearest.png"},
groups = {oddly_breakable_by_hand = 1},
use_texture_alpha = true,
scriptblock = function (pos, node, sender, info, last, main_channel)
local players = minetest.get_connected_players()
local nearest = nil
local min_distance = math.huge
for index, player in pairs(players) do
local distance = vector.distance(pos, player:getpos())
if distance < min_distance then
min_distance = distance
nearest = player:get_player_name()
end
end
return nearest or ""
end
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B