This repository has been archived on 2019-06-10. You can view files and clone it, but cannot push or open issues or pull requests.
scriptblocks/scriptblock.lua

500 lines
16 KiB
Lua
Raw Normal View History

--[[
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
})