Compare commits
263 Commits
Author | SHA1 | Date |
---|---|---|
bri cassa | 1fcfd2a98d | |
Luke aka SwissalpS | 8e30ee4113 | |
Wuzzy | b318aadd0a | |
Lejo | fef402e88a | |
DS | f98ea14023 | |
Johannes Fritz | 7f8758f17b | |
bri cassa | ec3218d6d9 | |
Wuzzy | 54de66b3e1 | |
fluxionary | 6890624f3d | |
HybridDog | 2589b391e5 | |
Jude Melton-Houghton | edcdc6817e | |
Jude Melton-Houghton | 60240ba268 | |
Jude Melton-Houghton | c10ce2dbc5 | |
jolesh | 2ede29df9c | |
fluxionary | a780298cfc | |
Niklp | bd07fb0c79 | |
Jude Melton-Houghton | 121082859f | |
bri cassa | 4ecc694518 | |
Jude Melton-Houghton | 68171b3d8d | |
fluxionary | da57a6214a | |
bri cassa | 3dd0eb7e4f | |
Oversword | c4f9336a26 | |
Jude Melton-Houghton | 0a4a88b1b9 | |
sfan5 | 58305f52bc | |
bri cassa | e4d7c07962 | |
Jude Melton-Houghton | 27c3c515b4 | |
Jude Melton-Houghton | 960b7c4915 | |
Jude Melton-Houghton | dfa43d6c0c | |
Jude Melton-Houghton | f4070d3e64 | |
sfan5 | 21ac966ee2 | |
Desour | b630ff9443 | |
Jude Melton-Houghton | 399ee9f5b5 | |
bri cassa | 69a4b6b332 | |
Jude Melton-Houghton | 0d9e0274ae | |
sfan5 | fef5c8cf68 | |
sfan5 | f6b0de64b8 | |
Jude Melton-Houghton | 1d30891349 | |
Jude Melton-Houghton | 4dfadd9276 | |
Jude Melton-Houghton | 3c27bb9350 | |
Jude Melton-Houghton | fb255d292e | |
Jude Melton-Houghton | c9dd323207 | |
Jude Melton-Houghton | 4c5b13a347 | |
DS | 4eea0834b5 | |
bri cassa | e38e4fe0c5 | |
Jude Melton-Houghton | ecea0a2896 | |
bri cassa | 5ad1e6bc4d | |
Jude Melton-Houghton | 9323445182 | |
bri cassa | b91fe92d13 | |
Jude Melton-Houghton | e15c55c066 | |
bri cassa | 6a87290ead | |
Jude Melton-Houghton | db5879706d | |
bri cassa | 1963bfcc0d | |
DS | 65793514fe | |
bri cassa | 6936c8c2e4 | |
Louis Royer | f5c3f798be | |
Desour | 69d4a9584d | |
DS | 3f75c06a3b | |
bri cassa | 2fc1682c04 | |
sys4-fr | 93aa24dc42 | |
wsor4035 | 93f155f604 | |
Sys Quatre | 9b835053c2 | |
Vitaliy | 583d2c1441 | |
auouymous | 397d449f1e | |
auouymous | 29ec26a4c8 | |
auouymous | 0d86f2c45e | |
Johannes Lundberg | d356f901a3 | |
Vitaliy | 6921909100 | |
tuedel | 3202bf6786 | |
Wuzzy | fedbf49372 | |
Vitaliy | 9fda51b650 | |
Vitaliy | 4750925eab | |
Wuzzy | 16836b16d6 | |
Vitaliy | c1eccba247 | |
tuedel | 01332a7ba1 | |
leucome | de4eeebd3b | |
mbartlett21 | 75bd1e8be6 | |
Wuzzy | 95e6e2e4a5 | |
David Leal | 168a923665 | |
Maksim | 8dec053996 | |
Awkor | e1cffdedbf | |
cheapie | d3aedd2b98 | |
DS | 68c1729990 | |
SmallJoker | 9b58f8db29 | |
SmallJoker | 7784b13da5 | |
SmallJoker | 0dd530312b | |
SmallJoker | e78bbd6f98 | |
SmallJoker | bfd952b51a | |
HybridDog | b7873e8e02 | |
fluxionary | d6b2a39c99 | |
Vitaliy | 1b54011b68 | |
Vitaliy | 15e743629e | |
Panquesito7 | 1bf862f932 | |
coil | 1a9704f184 | |
Hugo Locurcio | 8baa789eb1 | |
Piotr Halama | b0158f5674 | |
DS | 073c92d487 | |
Thomas Rudin | 737f366741 | |
Christopher Head | 302a28934d | |
Vitaliy | 6e767a6c76 | |
Christopher Head | 9d239cbfff | |
number Zero | d3cabedbb0 | |
ne-vlezay80 | df4e880d8b | |
Vitaliy | 45bbd9f7e3 | |
luk3yx | 028c290cd7 | |
luk3yx | 8808bb8911 | |
Vitaliy | fa040eb085 | |
number Zero | a4f5ae5b89 | |
number Zero | 9e6eac4285 | |
Vitaliy | c73b451f9b | |
DS | 9ff2329253 | |
sfan5 | 444cd0f2f1 | |
Vitaliy | d4e05f33af | |
Jordan Irwin | 334400a541 | |
SmallJoker | 5a0492b176 | |
Christopher Head | f61b1affea | |
Vitaliy | a234006592 | |
beyondlimits | 12fb09f452 | |
Vitaliy | ff73d0e60f | |
rubenwardy | 7013f2e5d4 | |
AntumDeluge | f70a308bb0 | |
Eduardo Trápani | d8f82e6771 | |
DS | 7537b9f381 | |
Wuzzy | 5ae3354227 | |
Wuzzy | 1b513d2111 | |
Wuzzy | 6f1ff24474 | |
20kdc | 065e870349 | |
Wuzzy | fec82ab246 | |
CloudyProton | 02f8d29170 | |
Christopher Head | 2b096f050d | |
DS | 993fdedd8c | |
DS | ff0bd76efe | |
DS-Minetest | db39eef2f6 | |
number Zero | f78937b351 | |
Vitaliy | cc860acf8a | |
DS | 748446b3cc | |
DS | 1b10610f92 | |
Diogo Gomes | 4f400f3fae | |
DS | 7094f0b606 | |
number Zero | b08e93fc8b | |
DS | 928f04fa9c | |
DS | c4a1aa0b98 | |
DS | 37eb7f00e8 | |
DS | 905260c8db | |
DS | 8999597df2 | |
DS | 0b3039e9a9 | |
DS | 1f5601661e | |
DS-Minetest | fa58607546 | |
DS | 1963d8b3d5 | |
Wuzzy | 954262d016 | |
DS | 3ef8ae01e7 | |
DS | 42cd973921 | |
DS | 36a6419481 | |
number Zero | 564bdc2d9b | |
DS | f9ebbdc0fb | |
sfan5 | 4dcc7fb32f | |
numberZero | 54b9eaffa2 | |
Josh Mars | 2554164674 | |
Wuzzy | 967bde284a | |
DS | d80c788fab | |
Wuzzy | 03ab151c87 | |
Vanessa Ezekowitz | dd2b36c41c | |
Vanessa Ezekowitz | 0cb286b425 | |
Vanessa Ezekowitz | 46cbc76988 | |
Vanessa Ezekowitz | fa3bd19270 | |
sfan5 | 2bc3c5d97c | |
sfan5 | 07d074075c | |
sfan5 | 5188853014 | |
sfan5 | 7667e7d8c5 | |
sfan5 | ececf525b6 | |
Jeija | 54daee236e | |
sfan5 | c2e3d7c4e5 | |
DS-Minetest | 7415036f5b | |
Carter Kolwey | 1bd936ad8c | |
Carter Kolwey | 79edbed8d7 | |
Jeija | 8743699298 | |
Jeija | 703e6fdadb | |
Yutao Yuan | 67cd17aa79 | |
Christopher Head | 203aaf3c90 | |
Jeija | 95fedc88bc | |
Jeija | 433778c2ec | |
Jeija | 54efc64558 | |
Jeija | dfeb070a20 | |
Jeija | 6dacdaee1f | |
Jeija | e561be7fa3 | |
Jeija | 89153f6909 | |
Christopher Head | 8e6536ca2e | |
Christopher Head | bc9d4c2d5a | |
Christopher Head | 6d79272ed4 | |
Christopher Head | 564cee346a | |
Carter Kolwey | 912f17f335 | |
Carter Kolwey | 4816dee396 | |
Florian Euchner | a3042b44fc | |
Christopher Head | 53eaf2af11 | |
Jeija | 778ee427f4 | |
Jeija | 7c7595fd7d | |
Carter Kolwey | acd41b5773 | |
number Zero | f9f7600017 | |
Carter Kolwey | 40487a65f4 | |
Florian Euchner | 75308f73e4 | |
Emon | 478680292c | |
Jeija | 85bc62a65d | |
Jeija | ed3964da80 | |
electrodude | 6cae381c27 | |
Jeija | 4249ed4986 | |
Florian Euchner | c4f0c5c169 | |
Carter Kolwey | 2dea21f9ef | |
Pedro Gimeno | b487783c23 | |
Jeija | 72e513ecbd | |
Jeija | 1e77b193dd | |
Jeija | 08b14e3af0 | |
Jeija | e5dba66c21 | |
Jeija | 417a136c5e | |
Jeija | 2dc8101fa1 | |
Samuel Sieb | 4b9039266e | |
Jeija | c7428d8135 | |
Jeija | cfd4f7c287 | |
Jeija | f099b43e11 | |
Jeija | 7b84105512 | |
Jeija | 80f72b2621 | |
Jeija | fe9c6871de | |
Jeija | 7865d42834 | |
Jeija | 809192f353 | |
Jeija | 6b54f025c1 | |
Jeija | c98805a5b5 | |
Auke Kok | fb4c440265 | |
HybridDog | cf45e24c57 | |
HybridDog | 3f00c1d1cb | |
HybridDog | c1d0481f8f | |
Jeija | f56c4ce35c | |
HybridDog | 0ec503f32c | |
Carter Kolwey | 35b647ed4a | |
Jeija | eab09b9d73 | |
Auke Kok | 2682d09291 | |
Jeija | a6b28d1f11 | |
Jeija | 0ec771bf7d | |
Jeija | 75bd081a67 | |
Jeija | b89fe52469 | |
Jeija | 97f7b5745f | |
Jeija | 24958e253b | |
Jeija | 0e7f68ea92 | |
Florian Euchner | 6ef3b965df | |
Marcin | 6e326a6a03 | |
Vanessa Ezekowitz | d95ccf86f8 | |
Jeija | d0354d0dab | |
Vanessa Ezekowitz | fec43a3243 | |
Vanessa Ezekowitz | 5612f84243 | |
Florian Euchner | 3066346d75 | |
Phil Hagelberg | 52ea2c3c66 | |
Jeija | b6ae419b00 | |
Jeija | a6d0a523ba | |
Jeija | aa5538a734 | |
Jeija | eb416475cc | |
Jeija | 1ee3b2107d | |
Jeija | 1df6e5a03b | |
Florian Euchner | b7076e635c | |
MT-Modder | 6d9f44455c | |
Jeija | 8bf5b7d0e4 | |
Florian Euchner | 14281a1bd4 | |
MT-Modder | 1d93a51f6e | |
Jeija | 639fc8988e | |
Florian Euchner | a9ec0a10c0 | |
Vanessa Ezekowitz | 775d250112 | |
MT-Modder | 3c06da7908 |
|
@ -0,0 +1,41 @@
|
|||
on: [push, pull_request]
|
||||
name: Check & Release
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- name: apt
|
||||
run: sudo apt-get install -y luarocks
|
||||
- name: luacheck install
|
||||
run: luarocks install --local luacheck
|
||||
- name: luacheck run
|
||||
run: $HOME/.luarocks/bin/luacheck ./
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- name: apt
|
||||
run: sudo apt-get install -y luarocks
|
||||
- name: busted install
|
||||
run: luarocks install --local busted
|
||||
- name: luacov install
|
||||
run: luarocks install --local luacov
|
||||
- name: mineunit install
|
||||
run: luarocks install --server=https://luarocks.org/dev --local mineunit
|
||||
- name: run mesecons tests
|
||||
working-directory: ./mesecons/
|
||||
run: $HOME/.luarocks/bin/mineunit -q
|
||||
- name: run mesecons_mvps tests
|
||||
working-directory: ./mesecons_mvps/
|
||||
run: $HOME/.luarocks/bin/mineunit -q
|
||||
- name: run mesecons_fpga tests
|
||||
working-directory: ./mesecons_fpga/
|
||||
run: $HOME/.luarocks/bin/mineunit -q
|
||||
- name: run mesecons_luacontroller tests
|
||||
working-directory: ./mesecons_luacontroller/
|
||||
run: $HOME/.luarocks/bin/mineunit -q
|
|
@ -1 +1,3 @@
|
|||
*~
|
||||
*.patch
|
||||
*.diff
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
std = "lua51c"
|
||||
|
||||
ignore = {
|
||||
"21/_+", -- Unused variable, except "_", "__", etc.
|
||||
"213", -- Unused loop variable
|
||||
"421", -- Shadowing a local variable
|
||||
"422", -- Shadowing an argument
|
||||
"423", -- Shadowing a loop variable
|
||||
"431", -- Shadowing an upvalue
|
||||
"432", -- Shadowing an upvalue argument
|
||||
"433", -- Shadowing an upvalue loop variable
|
||||
"542", -- Empty if branch
|
||||
}
|
||||
|
||||
max_line_length = 200
|
||||
|
||||
read_globals = {
|
||||
"default",
|
||||
"digiline",
|
||||
"doors",
|
||||
"dump",
|
||||
"jit",
|
||||
"minetest",
|
||||
"screwdriver",
|
||||
"string.split",
|
||||
"table.copy",
|
||||
"table.insert_all",
|
||||
"vector",
|
||||
"VoxelArea",
|
||||
}
|
||||
|
||||
globals = {"mesecon"}
|
||||
|
||||
files["mesecons/actionqueue.lua"] = {
|
||||
globals = {"minetest.registered_globalsteps"},
|
||||
}
|
||||
|
||||
-- Test-specific stuff follows.
|
||||
|
||||
local test_conf = {
|
||||
read_globals = {
|
||||
"assert",
|
||||
"fixture",
|
||||
"mineunit",
|
||||
"Player",
|
||||
"sourcefile",
|
||||
"world",
|
||||
},
|
||||
}
|
||||
files["*/spec/*.lua"] = test_conf
|
||||
files[".test_fixtures/*.lua"] = test_conf
|
||||
|
||||
files[".test_fixtures/screwdriver.lua"] = {
|
||||
globals = {"screwdriver"},
|
||||
}
|
||||
|
||||
files[".test_fixtures/mesecons_fpga.lua"] = {
|
||||
globals = {"minetest.register_on_player_receive_fields"},
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
mineunit("core")
|
||||
mineunit("server")
|
||||
mineunit("voxelmanip")
|
||||
|
||||
mineunit:set_current_modname("mesecons")
|
||||
mineunit:set_modpath("mesecons", "../mesecons")
|
||||
sourcefile("../mesecons/init")
|
||||
|
||||
-- Utility node: this conductor is used to test the connectivity and state of adjacent wires.
|
||||
do
|
||||
local off_spec = {conductor = {
|
||||
state = mesecon.state.off,
|
||||
rules = mesecon.rules.alldirs,
|
||||
onstate = "mesecons:test_conductor_on",
|
||||
}}
|
||||
local on_spec = {conductor = {
|
||||
state = mesecon.state.on,
|
||||
rules = mesecon.rules.alldirs,
|
||||
offstate = "mesecons:test_conductor_off",
|
||||
}}
|
||||
mesecon.register_node("mesecons:test_conductor", {
|
||||
description = "Test Conductor",
|
||||
}, {mesecons = off_spec}, {mesecons = on_spec})
|
||||
end
|
||||
|
||||
-- Utility node: this receptor is used to test power sources.
|
||||
do
|
||||
local off_spec = {receptor = {
|
||||
state = mesecon.state.off,
|
||||
rules = mesecon.rules.alldirs,
|
||||
}}
|
||||
local on_spec = {receptor = {
|
||||
state = mesecon.state.on,
|
||||
rules = mesecon.rules.alldirs,
|
||||
}}
|
||||
mesecon.register_node("mesecons:test_receptor", {
|
||||
description = "Test Receptor",
|
||||
}, {mesecons = off_spec}, {mesecons = on_spec})
|
||||
end
|
||||
|
||||
-- Utility node: this effector is used to test circuit outputs.
|
||||
do
|
||||
-- This is a list of actions in the form {<kind>, <pos>},
|
||||
-- where <kind> is "on", "off", or "overheat".
|
||||
mesecon._test_effector_events = {}
|
||||
local function action_on(pos, node)
|
||||
table.insert(mesecon._test_effector_events, {"on", pos})
|
||||
node.param2 = node.param2 % 64 + 128 -- Turn on bit 7
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
local function action_off(pos, node)
|
||||
table.insert(mesecon._test_effector_events, {"off", pos})
|
||||
node.param2 = node.param2 % 64 -- Turn off bit 7
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
local function action_change(pos, node, rule_name, new_state)
|
||||
if mesecon.do_overheat(pos) then
|
||||
table.insert(mesecon._test_effector_events, {"overheat", pos})
|
||||
minetest.remove_node(pos)
|
||||
return
|
||||
end
|
||||
-- Set the value of a bit in param2 according to the rule name and new state.
|
||||
local bit = tonumber(rule_name.name, 2)
|
||||
local bits_above = node.param2 - node.param2 % (bit * 2)
|
||||
local bits_below = node.param2 % bit
|
||||
local bits_flipped = new_state == mesecon.state.on and bit or 0
|
||||
node.param2 = bits_above + bits_flipped + bits_below
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
minetest.register_node("mesecons:test_effector", {
|
||||
description = "Test Effector",
|
||||
mesecons = {effector = {
|
||||
action_on = action_on,
|
||||
action_off = action_off,
|
||||
action_change = action_change,
|
||||
rules = {
|
||||
{x = 1, y = 0, z = 0, name = "000001"},
|
||||
{x = -1, y = 0, z = 0, name = "000010"},
|
||||
{x = 0, y = 1, z = 0, name = "000100"},
|
||||
{x = 0, y = -1, z = 0, name = "001000"},
|
||||
{x = 0, y = 0, z = 1, name = "010000"},
|
||||
{x = 0, y = 0, z = -1, name = "100000"},
|
||||
}
|
||||
}},
|
||||
})
|
||||
end
|
||||
|
||||
-- Utility node: this conductor is used to test rotation.
|
||||
do
|
||||
local get_rules = mesecon.horiz_rules_getter({{x = 1, y = 0, z = 0}, {x = -1, y = 0, z = 0}})
|
||||
local off_spec = {conductor = {
|
||||
state = mesecon.state.off,
|
||||
rules = get_rules,
|
||||
onstate = "mesecons:test_conductor_rot_on",
|
||||
}}
|
||||
local on_spec = {conductor = {
|
||||
state = mesecon.state.on,
|
||||
rules = get_rules,
|
||||
offstate = "mesecons:test_conductor_rot_off",
|
||||
}}
|
||||
mesecon.register_node("mesecons:test_conductor_rot", {
|
||||
description = "Rotatable Test Conductor",
|
||||
on_rotate = mesecon.on_rotate_horiz,
|
||||
}, {mesecons = off_spec}, {mesecons = on_spec})
|
||||
end
|
||||
|
||||
-- Utility node: this is used to test multiple conductors within a single node.
|
||||
do
|
||||
local mesecons_spec = {conductor = {
|
||||
rules = {
|
||||
{{x = 1, y = 0, z = 0}, {x = 0, y = -1, z = 0}},
|
||||
{{x = 0, y = 1, z = 0}, {x = 0, y = 0, z = -1}},
|
||||
{{x = 0, y = 0, z = 1}, {x = -1, y = 0, z = 0}},
|
||||
},
|
||||
states = {
|
||||
"mesecons:test_multiconductor_off", "mesecons:test_multiconductor_001",
|
||||
"mesecons:test_multiconductor_010", "mesecons:test_multiconductor_011",
|
||||
"mesecons:test_multiconductor_100", "mesecons:test_multiconductor_101",
|
||||
"mesecons:test_multiconductor_110", "mesecons:test_multiconductor_on",
|
||||
},
|
||||
}}
|
||||
for _, state in ipairs(mesecons_spec.conductor.states) do
|
||||
minetest.register_node(state, {
|
||||
description = "Test Multiconductor",
|
||||
mesecons = mesecons_spec,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
mesecon._test_autoconnects = {}
|
||||
mesecon.register_autoconnect_hook("test", function(pos, node)
|
||||
table.insert(mesecon._test_autoconnects, {pos, node})
|
||||
end)
|
||||
|
||||
function mesecon._test_dig(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
minetest.remove_node(pos)
|
||||
mesecon.on_dignode(pos, node)
|
||||
end
|
||||
|
||||
function mesecon._test_place(pos, node)
|
||||
world.set_node(pos, node)
|
||||
mesecon.on_placenode(pos, minetest.get_node(pos))
|
||||
end
|
||||
|
||||
function mesecon._test_reset()
|
||||
-- First let circuits settle by simulating many globalsteps.
|
||||
for i = 1, 10 do
|
||||
mineunit:execute_globalstep(60)
|
||||
end
|
||||
mesecon.queue.actions = {}
|
||||
mesecon._test_effector_events = {}
|
||||
mesecon._test_autoconnects = {}
|
||||
end
|
||||
|
||||
mineunit:execute_globalstep(mesecon.setting("resumetime", 4) + 1)
|
|
@ -0,0 +1,59 @@
|
|||
mineunit("player")
|
||||
|
||||
fixture("mesecons")
|
||||
fixture("mesecons_gamecompat")
|
||||
|
||||
local registered_on_player_receive_fields = {}
|
||||
local old_register_on_player_receive_fields = minetest.register_on_player_receive_fields
|
||||
function minetest.register_on_player_receive_fields(func)
|
||||
old_register_on_player_receive_fields(func)
|
||||
table.insert(registered_on_player_receive_fields, func)
|
||||
end
|
||||
|
||||
mineunit:set_current_modname("mesecons_fpga")
|
||||
mineunit:set_modpath("mesecons_fpga", "../mesecons_fpga")
|
||||
sourcefile("../mesecons_fpga/init")
|
||||
|
||||
local fpga_user = Player("mesecons_fpga_user")
|
||||
|
||||
function mesecon._test_program_fpga(pos, program)
|
||||
local node = minetest.get_node(pos)
|
||||
assert.equal("mesecons_fpga:fpga", node.name:sub(1, 18))
|
||||
|
||||
local fields = {program = true}
|
||||
for i, instr in ipairs(program) do
|
||||
-- Translate the instruction into formspec fields.
|
||||
local op1, act, op2, dst
|
||||
if #instr == 3 then
|
||||
act, op2, dst = unpack(instr)
|
||||
else
|
||||
assert.equal(4, #instr)
|
||||
op1, act, op2, dst = unpack(instr)
|
||||
end
|
||||
fields[i .. "op1"] = op1
|
||||
fields[i .. "act"] = (" "):rep(4 - #act) .. act
|
||||
fields[i .. "op2"] = op2
|
||||
fields[i .. "dst"] = dst
|
||||
end
|
||||
|
||||
minetest.registered_nodes[node.name].on_rightclick(pos, node, fpga_user)
|
||||
|
||||
for _, func in ipairs(registered_on_player_receive_fields) do
|
||||
if func(fpga_user, "mesecons:fpga", fields) then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mesecon._test_copy_fpga_program(pos)
|
||||
fpga_user:get_inventory():set_stack("main", 1, "mesecons_fpga:programmer")
|
||||
local pt = {type = "node", under = vector.new(pos), above = vector.offset(pos, 0, 1, 0)}
|
||||
fpga_user:do_place(pt)
|
||||
return fpga_user:get_wielded_item()
|
||||
end
|
||||
|
||||
function mesecon._test_paste_fpga_program(pos, tool)
|
||||
fpga_user:get_inventory():set_stack("main", 1, tool)
|
||||
local pt = {type = "node", under = vector.new(pos), above = vector.offset(pos, 0, 1, 0)}
|
||||
fpga_user:do_use(pt)
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
fixture("mesecons")
|
||||
|
||||
mineunit:set_current_modname("mesecons_gamecompat")
|
||||
mineunit:set_modpath("mesecons_gamecompat", "../mesecons_gamecompat")
|
||||
sourcefile("../mesecons_gamecompat/init")
|
|
@ -0,0 +1,12 @@
|
|||
fixture("mesecons")
|
||||
fixture("mesecons_gamecompat")
|
||||
|
||||
mineunit:set_current_modname("mesecons_luacontroller")
|
||||
mineunit:set_modpath("mesecons_luacontroller", "../mesecons_luacontroller")
|
||||
sourcefile("../mesecons_luacontroller/init")
|
||||
|
||||
function mesecon._test_program_luac(pos, code)
|
||||
local node = minetest.get_node(pos)
|
||||
assert.equal("mesecons_luacontroller:luacontroller", node.name:sub(1, 36))
|
||||
return minetest.registered_nodes[node.name].mesecons.luacontroller.set_program(pos, code)
|
||||
end
|
|
@ -0,0 +1,45 @@
|
|||
mineunit("protection")
|
||||
|
||||
fixture("mesecons")
|
||||
|
||||
mineunit:set_current_modname("mesecons_mvps")
|
||||
mineunit:set_modpath("mesecons_mvps", "../mesecons_mvps")
|
||||
sourcefile("../mesecons_mvps/init")
|
||||
|
||||
minetest.register_node("mesecons_mvps:test_stopper", {
|
||||
description = "Test Stopper",
|
||||
})
|
||||
mesecon.register_mvps_stopper("mesecons_mvps:test_stopper")
|
||||
|
||||
minetest.register_node("mesecons_mvps:test_stopper_cond", {
|
||||
description = "Test Stopper (Conditional)",
|
||||
})
|
||||
mesecon.register_mvps_stopper("mesecons_mvps:test_stopper_cond", function(node)
|
||||
return node.param2 == 0
|
||||
end)
|
||||
|
||||
minetest.register_node("mesecons_mvps:test_sticky", {
|
||||
description = "Test Sticky",
|
||||
mvps_sticky = function(pos)
|
||||
local connected = {}
|
||||
for i, rule in ipairs(mesecon.rules.alldirs) do
|
||||
connected[i] = vector.add(pos, rule)
|
||||
end
|
||||
return connected
|
||||
end,
|
||||
})
|
||||
|
||||
mesecon._test_moves = {}
|
||||
minetest.register_node("mesecons_mvps:test_on_move", {
|
||||
description = "Test Moveable",
|
||||
mesecon = {
|
||||
on_mvps_move = function(pos, node, oldpos, meta)
|
||||
table.insert(mesecon._test_moves, {pos, node, oldpos, meta})
|
||||
end
|
||||
},
|
||||
})
|
||||
local old_reset = mesecon._test_reset
|
||||
function mesecon._test_reset()
|
||||
mesecon._test_moves = {}
|
||||
old_reset()
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
mineunit:set_current_modname("screwdriver")
|
||||
|
||||
screwdriver = {}
|
||||
|
||||
screwdriver.ROTATE_FACE = 1
|
||||
screwdriver.ROTATE_AXIS = 2
|
|
@ -0,0 +1,30 @@
|
|||
The Mesecons Mod for Minetest is
|
||||
Copyright (C) 2011-2016 Mesecons Mod Developer Team and contributors
|
||||
|
||||
See the version control system log for information about other authors.
|
||||
|
||||
License of source code
|
||||
----------------------
|
||||
Copyright (C) 2011-2016 Mesecons Mod Developer Team and contributors
|
||||
|
||||
This program is free software; you can redistribute the Mesecons Mod and/or
|
||||
modify it under the terms of the GNU Lesser General Public License version 3
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
|
||||
License of media (textures, sounds and documentation)
|
||||
-----------------------------------------------------
|
||||
Copyright (C) 2011-2016 Mesecons Mod Developer Team and contributors
|
||||
|
||||
All textures, sounds and documentation files are licensed under the
|
||||
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
|
@ -1,6 +1,12 @@
|
|||
The LGPLv3 applies to all code in this project.
|
||||
The CC-BY-SA-3.0 license applies to textures and any other content in this project which is not source code.
|
||||
|
||||
The file mesecons_detector/textures/mesecons_detector_side.png is an unmodified copy of
|
||||
"default_steel_block.png" by Jean-Patrick Guerrero <https://github.com/kilbith>, which can be found at
|
||||
<https://github.com/minetest/minetest_game/blob/9528c0f8b93d6934930e99c3c116df275fb0e4bc/mods/default/textures/default_steel_block.png>.
|
||||
"default_steel_block.png" is licensed under a CC BY-SA 3.0 license. This license can be found later in this document, and can also be found at
|
||||
<https://creativecommons.org/licenses/by-sa/3.0/>. The artwork is reportedly copyright (C) 2010-2018 kilbith.
|
||||
|
||||
=================================================================
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
|
|
21
README.md
|
@ -12,9 +12,9 @@ MESECONS by Jeija and contributors
|
|||
|
||||
Mezzee-what?
|
||||
------------
|
||||
[Mesecons](http://mesecons.net/)! They're yellow, they're conductive, and they'll add a whole new dimension to Minetest's gameplay.
|
||||
[Mesecons](https://mesecons.net/)! They're yellow, they're conductive, and they'll add a whole new dimension to Minetest's gameplay.
|
||||
|
||||
Mesecons is a mod for [Minetest](http://minetest.net/) that implements a ton of items related to digital circuitry, such as wires, buttons, lights, and even programmable controllers. Among other things, there are also pistons, solar panels, pressure plates, and note blocks.
|
||||
Mesecons is a mod for [Minetest](https://www.minetest.net/) that implements a ton of items related to digital circuitry, such as wires, buttons, lights, and even programmable controllers. Among other things, there are also pistons, solar panels, pressure plates, and note blocks.
|
||||
|
||||
Mesecons has a similar goal to Redstone in Minecraft, but works in its own way, with different rules and mechanics.
|
||||
|
||||
|
@ -22,9 +22,9 @@ OK, I want in.
|
|||
--------------
|
||||
Go get it!
|
||||
|
||||
[DOWNLOADS PAGE](http://mesecons.net/downloads.php)
|
||||
[DOWNLOAD IT NOW](https://github.com/minetest-mods/mesecons/archive/master.zip)
|
||||
|
||||
Now go ahead and install it like any other Minetest mod. Don't know how? Check out [the wonderful page about it](http://wiki.minetest.com/wiki/Mods) over at the Minetest Wiki. For your convenience, here's a quick summary:
|
||||
Now go ahead and install it like any other Minetest mod. Don't know how? Check out [the wonderful page about it](https://wiki.minetest.net/Mods) over at the official Minetest Wiki. For your convenience, here's a quick summary:
|
||||
|
||||
1. If Mesecons is still in a ZIP file, extract the folder inside to somewhere on the computer.
|
||||
2. Make sure that when you open the folder, you can directly find `README.md` in the listing. If you just see another folder, move that folder up one level and delete the old one.
|
||||
|
@ -39,13 +39,15 @@ How do I use this thing?
|
|||
------------------------
|
||||
How about a [quick overview video](https://www.youtube.com/watch?v=6kmeQj6iW5k)?
|
||||
|
||||
Or maybe a [comprehensive reference](http://mesecons.net/items.php) is your style?
|
||||
Or maybe a [comprehensive reference](http://mesecons.net/items.html) is your style?
|
||||
|
||||
An overview for the very newest of new beginners? How does [this one](http://uberi.mesecons.net/projects/MeseconsBasics/index.html) look?
|
||||
|
||||
There is also a [wiki page](https://wiki.minetest.net/Mods/Mesecons) dedicated to this mod.
|
||||
|
||||
Want to get more into building? Why not check out the [Mesecons Laboratory](http://uberi.mesecons.net/), a website dedicated to advanced Mesecons builders?
|
||||
|
||||
Want to contribute to Mesecons itself? Check out the [source code](https://github.com/Jeija/minetest-mod-mesecons)!
|
||||
Want to contribute to Mesecons itself? Check out the [source code](https://github.com/minetest-mods/mesecons)!
|
||||
|
||||
Who wrote it anyways?
|
||||
---------------------
|
||||
|
@ -53,6 +55,7 @@ These awesome people made Mesecons possible!
|
|||
|
||||
| Contributor | Contribution |
|
||||
| --------------- | -------------------------------- |
|
||||
| Hawk777 | Code for VoxelManip caching |
|
||||
| Jat15 | Various tweaks. |
|
||||
| Jeija | **Main developer! Everything.** |
|
||||
| Jordach | Noteblock sounds. |
|
||||
|
@ -71,8 +74,10 @@ There are also a whole bunch of other people helping with everything from code t
|
|||
|
||||
Alright, how can I use it?
|
||||
--------------------------
|
||||
All textures in this project are licensed under the CC-BY-SA 3.0 (Creative Commons Attribution-ShareAlike 3.0 Generic). That means you can distribute and remix them as much as you want to, under the condition that you give credit to the authors and the project, and that if you remix and release them, they must be under the same or similar license to this one.
|
||||
All textures in this project are licensed under the CC-BY-SA 3.0 (Creative Commons Attribution-ShareAlike 3.0 Generic).
|
||||
That means you can distribute and remix them as much as you want to, under the condition that you give credit to the authors and the project, and that if you remix and release them, they must be under the same or similar license to this one.
|
||||
|
||||
All code in this project is licensed under the LGPL version 3 or later. That means you have unlimited freedom to distribute and modify the work however you see fit, provided that if you decide to distribute it or any modified versions of it, you must also use the same license. The LGPL also grants the additional freedom to write extensions for the software and distribute them without the extensions being subject to the terms of the LGPL, although the software itself retains its license.
|
||||
All code in this project is licensed under the LGPL version 3.
|
||||
That means you have unlimited freedom to distribute and modify the work however you see fit, provided that if you decide to distribute it or any modified versions of it, you must also use the same license. The LGPL also grants the additional freedom to write extensions for the software and distribute them without the extensions being subject to the terms of the LGPL, although the software itself retains its license.
|
||||
|
||||
No warranty is provided, express or implied, for any part of the project.
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "mesecons",
|
||||
"description": "Mesecons is a mod for Minetest that implements items related to digital circuitry: wires, buttons, lights, and programmable controllers.",
|
||||
"homepage": "https://mesecons.net",
|
||||
"authors": ["Jeija"],
|
||||
"license": "LGPL-3.0",
|
||||
"keywords": [
|
||||
"mesecons",
|
||||
"minetest",
|
||||
"mod"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"Conductors" : {
|
||||
"Mesecon" : "mesecons_wires/doc/mesecon",
|
||||
"Insulated Wire" : "mesecons_insulated/doc/insulated",
|
||||
"T-Junction" : "mesecons_extrawires/doc/tjunction",
|
||||
"Crossing" : "mesecons_extrawires/doc/crossing",
|
||||
"Corner" : "mesecons_extrawires/doc/corner",
|
||||
"Vertical Wire" : "mesecons_extrawires/doc/vertical",
|
||||
"Mese" : "mesecons_extrawires/doc/mese"
|
||||
},
|
||||
"Receptors" : {
|
||||
"Power Plant" : "mesecons_powerplant/doc/powerplant",
|
||||
"Blinky Plant" : "mesecons_blinkyplant/doc/blinkyplant",
|
||||
"Switch" : "mesecons_switch/doc/switch",
|
||||
"Object Detector" : "mesecons_detector/doc/objectdetector",
|
||||
"Node Detector" : "mesecons_detector/doc/nodedetector",
|
||||
"Wall Lever" : "mesecons_walllever/doc/walllever",
|
||||
"Pressure Plate" : "mesecons_pressureplates/doc/pressureplate_wood",
|
||||
"Pressure Plate" : "mesecons_pressureplates/doc/pressureplate_stone",
|
||||
"Water Turbine" : "mesecons_hydroturbine/doc/waterturbine",
|
||||
"Solar Panel" : "mesecons_solarpanel/doc/solarpanel",
|
||||
"Wall Button" : "mesecons_button/doc/button"
|
||||
},
|
||||
"Effectors" : {
|
||||
"Noteblock" : "mesecons_noteblock/doc/noteblock",
|
||||
"Lamp" : "mesecons_lamp/doc/lamp",
|
||||
"Piston" : "mesecons_pistons/doc/piston",
|
||||
"Sticky Piston" : "mesecons_pistons/doc/piston_sticky",
|
||||
"Movestone" : "mesecons_movestones/doc/movestone",
|
||||
"Sticky Movestone" : "mesecons_movestones/doc/movestone_sticky",
|
||||
"Removestone" : "mesecons_random/doc/removestone",
|
||||
"Ghoststone" : "mesecons_random/doc/ghoststone",
|
||||
"Command Block" : "mesecons_commandblock/doc/commandblock",
|
||||
"Lightstones" : {
|
||||
"Dark Grey" : "mesecons_lightstone/doc/lightstone_darkgrey",
|
||||
"Light Grey" : "mesecons_lightstone/doc/lightstone_lightgrey",
|
||||
"Green" : "mesecons_lightstone/doc/lightstone_green",
|
||||
"Red" : "mesecons_lightstone/doc/lightstone_red",
|
||||
"Blue" : "mesecons_lightstone/doc/lightstone_blue",
|
||||
"Yellow" : "mesecons_lightstone/doc/lightstone_yellow"
|
||||
}
|
||||
},
|
||||
"Logic" : {
|
||||
"Luacontroller" : "mesecons_luacontroller/doc/luacontroller",
|
||||
"FPGA" : "mesecons_fpga/doc/fpga",
|
||||
"FPGA Programmer" : "mesecons_fpga/doc/programmer",
|
||||
"Torch" : "mesecons_torch/doc/torch",
|
||||
"Delayer" : "mesecons_delayer/doc/delayer",
|
||||
"Gates" : {
|
||||
"Diode" : "mesecons_gates/doc/diode",
|
||||
"NOT Gate" : "mesecons_gates/doc/not",
|
||||
"AND Gate" : "mesecons_gates/doc/and",
|
||||
"NAND Gate" : "mesecons_gates/doc/nand",
|
||||
"OR Gate" : "mesecons_gates/doc/or",
|
||||
"NOR Gate" : "mesecons_gates/doc/nor",
|
||||
"XOR Gate" : "mesecons_gates/doc/xor"
|
||||
}
|
||||
},
|
||||
"Crafts" : {
|
||||
"Silicon" : "mesecons_materials/doc/silicon",
|
||||
"Glue" : "mesecons_materials/doc/glue",
|
||||
"Fiber" : "mesecons_materials/doc/fiber"
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
0.41 DEV
|
|
@ -1,118 +1,149 @@
|
|||
mesecon.queue.actions={} -- contains all ActionQueue actions
|
||||
--[[
|
||||
Mesecons uses something it calls an ActionQueue.
|
||||
|
||||
function mesecon.queue:add_function(name, func)
|
||||
mesecon.queue.funcs[name] = func
|
||||
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)
|
||||
self.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 mesecon.queue:add_action(pos, func, params, time, overwritecheck, priority)
|
||||
function 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
|
||||
}
|
||||
|
||||
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(mesecon.cmpPos(pos, ac.pos)
|
||||
and mesecon.cmpAny(overwritecheck, ac.owcheck)) then
|
||||
toremove = i
|
||||
-- check if old action has to be overwritten / removed:
|
||||
if overwritecheck then
|
||||
for i, ac in ipairs(self.actions) do
|
||||
if vector.equals(pos, ac.pos)
|
||||
and mesecon.cmpAny(overwritecheck, ac.owcheck) then
|
||||
-- remove the old action
|
||||
table.remove(self.actions, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (toremove ~= nil) then
|
||||
table.remove(mesecon.queue.actions, toremove)
|
||||
end
|
||||
|
||||
table.insert(mesecon.queue.actions, action)
|
||||
table.insert(self.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
|
||||
-- this makes sure that resuming mesecons circuits when restarting minetest works fine (hm, where do we do this?)
|
||||
-- However, even that does not work in some cases, that's why we delay the time the globalsteps
|
||||
-- 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
|
||||
-- start to be execute by 4 seconds
|
||||
|
||||
return highesti
|
||||
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 = {}
|
||||
|
||||
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
|
||||
for _, ac in ipairs(actions) do
|
||||
if ac.time > 0 then
|
||||
ac.time = ac.time - dtime -- executed later
|
||||
table.insert(mesecon.queue.actions, ac)
|
||||
-- action ac is to be executed later
|
||||
-- ~> insert into queue.actions
|
||||
ac.time = ac.time - dtime
|
||||
table.insert(queue.actions, ac)
|
||||
else
|
||||
-- action ac is to be executed now
|
||||
-- ~> insert into actions_now
|
||||
table.insert(actions_now, ac)
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
-- 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
|
||||
end
|
||||
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)
|
||||
|
||||
function mesecon.queue:execute(action)
|
||||
mesecon.queue.funcs[action.func](action.pos, unpack(action.params))
|
||||
-- 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)
|
||||
-- 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 self.funcs[action.func] then
|
||||
self.funcs[action.func](action.pos, unpack(action.params))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Store and read the ActionQueue to / from a file
|
||||
-- so that upcoming actions are remembered when the game
|
||||
-- is restarted
|
||||
|
||||
local wpath = minetest.get_worldpath()
|
||||
local function file2table(filename)
|
||||
local f = io.open(filename, "r")
|
||||
if f==nil then return {} end
|
||||
local t = f:read("*all")
|
||||
f:close()
|
||||
if t=="" or t==nil then return {} end
|
||||
return minetest.deserialize(t)
|
||||
end
|
||||
|
||||
local function table2file(filename, table)
|
||||
local f = io.open(filename, "w")
|
||||
f:write(minetest.serialize(table))
|
||||
f:close()
|
||||
end
|
||||
|
||||
mesecon.queue.actions = file2table(wpath.."/mesecon_actionqueue")
|
||||
queue.actions = mesecon.file2table("mesecon_actionqueue")
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
mesecon.queue.actions = table2file(wpath.."/mesecon_actionqueue", mesecon.queue.actions)
|
||||
mesecon.table2file("mesecon_actionqueue", queue.actions)
|
||||
end)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
default
|
|
@ -0,0 +1,62 @@
|
|||
|
||||
-- a simple first-in-first-out queue
|
||||
-- very similar to the one in https://github.com/minetest/minetest/pull/7683
|
||||
|
||||
local fifo_queue = {}
|
||||
|
||||
local metatable = {__index = fifo_queue}
|
||||
|
||||
-- creates a new empty queue
|
||||
function fifo_queue.new()
|
||||
local q = {n_in = 0, n_out = 0, i_out = 1, buf_in = {}, buf_out = {}}
|
||||
setmetatable(q, metatable)
|
||||
return q
|
||||
end
|
||||
|
||||
-- adds an element to the queue
|
||||
function fifo_queue.add(self, v)
|
||||
local n = self.n_in + 1
|
||||
self.n_in = n
|
||||
self.buf_in[n] = v
|
||||
end
|
||||
|
||||
-- removes and returns the next element, or nil of empty
|
||||
function fifo_queue.take(self)
|
||||
local i_out = self.i_out
|
||||
if i_out <= self.n_out then
|
||||
local v = self.buf_out[i_out]
|
||||
self.i_out = i_out + 1
|
||||
self.buf_out[i_out] = true
|
||||
return v
|
||||
end
|
||||
|
||||
-- buf_out is empty, try to swap
|
||||
self.i_out = 1
|
||||
self.n_out = 0
|
||||
if self.n_in == 0 then
|
||||
return nil -- empty
|
||||
end
|
||||
|
||||
-- swap
|
||||
self.n_out = self.n_in
|
||||
self.n_in = 0
|
||||
self.buf_out, self.buf_in = self.buf_in, self.buf_out
|
||||
|
||||
local v = self.buf_out[1]
|
||||
self.i_out = 2
|
||||
self.buf_out[1] = true
|
||||
return v
|
||||
end
|
||||
|
||||
-- returns whether the queue is empty
|
||||
function fifo_queue.is_empty(self)
|
||||
return self.n_out == self.i_out + 1 and self.n_in == 0
|
||||
end
|
||||
|
||||
-- returns stuff for iteration in a for loop, like pairs
|
||||
-- adding new elements while iterating is no problem
|
||||
function fifo_queue.iter(self)
|
||||
return fifo_queue.take, self, nil
|
||||
end
|
||||
|
||||
return fifo_queue
|
|
@ -3,7 +3,7 @@
|
|||
-- | \/ | |___ ____ |___ | | | | \ | |____
|
||||
-- | | | | | | | | | \ | |
|
||||
-- | | |___ ____| |___ |____ |____| | \| ____|
|
||||
-- by Jeija, Uberi (Temperest), sfan5, VanessaE
|
||||
-- by Jeija, Uberi (Temperest), sfan5, VanessaE, Hawk777 and contributors
|
||||
--
|
||||
--
|
||||
--
|
||||
|
@ -11,7 +11,7 @@
|
|||
-- See the documentation on the forum for additional information, especially about crafting
|
||||
--
|
||||
--
|
||||
-- For developer documentation see the Developers' section on mesecons.TK
|
||||
-- For basic development resources, see http://mesecons.net/developers.html
|
||||
--
|
||||
--
|
||||
--
|
||||
|
@ -30,7 +30,7 @@
|
|||
-- action_change = function
|
||||
-- rules = rules/get_rules
|
||||
-- },
|
||||
-- conductor =
|
||||
-- conductor =
|
||||
-- {
|
||||
-- state = mesecon.state.on/off
|
||||
-- offstate = opposite state (for state = on only)
|
||||
|
@ -70,26 +70,20 @@ dofile(minetest.get_modpath("mesecons").."/internal.lua");
|
|||
-- these are the only functions you need to remember
|
||||
|
||||
mesecon.queue:add_function("receptor_on", function (pos, rules)
|
||||
mesecon.vm_begin()
|
||||
|
||||
rules = rules or mesecon.rules.default
|
||||
|
||||
-- if area (any of the rule targets) is not loaded, keep trying and call this again later
|
||||
-- Call turnon on all linking positions
|
||||
for _, rule in ipairs(mesecon.flattenrules(rules)) do
|
||||
local np = mesecon.addPosRule(pos, rule)
|
||||
-- if area is not loaded, keep trying
|
||||
if minetest.get_node_or_nil(np) == nil then
|
||||
mesecon.queue:add_action(pos, "receptor_on", {rules}, nil, rules)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- execute action
|
||||
for _, rule in ipairs(mesecon.flattenrules(rules)) do
|
||||
local np = mesecon.addPosRule(pos, rule)
|
||||
local np = vector.add(pos, rule)
|
||||
local rulenames = mesecon.rules_link_rule_all(pos, rule)
|
||||
for _, rulename in ipairs(rulenames) do
|
||||
mesecon.turnon(np, rulename)
|
||||
end
|
||||
end
|
||||
|
||||
mesecon.vm_commit()
|
||||
end)
|
||||
|
||||
function mesecon.receptor_on(pos, rules)
|
||||
|
@ -99,23 +93,20 @@ end
|
|||
mesecon.queue:add_function("receptor_off", function (pos, rules)
|
||||
rules = rules or mesecon.rules.default
|
||||
|
||||
-- if area (any of the rule targets) is not loaded, keep trying and call this again later
|
||||
-- Call turnoff on all linking positions
|
||||
for _, rule in ipairs(mesecon.flattenrules(rules)) do
|
||||
local np = mesecon.addPosRule(pos, rule)
|
||||
if minetest.get_node_or_nil(np) == nil then
|
||||
mesecon.queue:add_action(pos, "receptor_off", {rules}, nil, rules)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
for _, rule in ipairs(mesecon.flattenrules(rules)) do
|
||||
local np = mesecon.addPosRule(pos, rule)
|
||||
local np = vector.add(pos, rule)
|
||||
local rulenames = mesecon.rules_link_rule_all(pos, rule)
|
||||
for _, rulename in ipairs(rulenames) do
|
||||
if not mesecon.connected_to_receptor(np, mesecon.invertRule(rule)) then
|
||||
mesecon.turnoff(np, rulename)
|
||||
mesecon.vm_begin()
|
||||
|
||||
-- Turnoff returns true if turnoff process was successful, no onstate receptor
|
||||
-- was found along the way. Commit changes that were made in voxelmanip. If turnoff
|
||||
-- returns true, an onstate receptor was found, abort voxelmanip transaction.
|
||||
if (mesecon.turnoff(np, rulename)) then
|
||||
mesecon.vm_commit()
|
||||
else
|
||||
mesecon.changesignal(np, minetest.get_node(np), rulename, mesecon.state.off, 2)
|
||||
mesecon.vm_abort()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -126,14 +117,9 @@ function mesecon.receptor_off(pos, rules)
|
|||
end
|
||||
|
||||
|
||||
print("[OK] Mesecons")
|
||||
|
||||
-- Deprecated stuff
|
||||
-- To be removed in future releases
|
||||
dofile(minetest.get_modpath("mesecons").."/legacy.lua");
|
||||
|
||||
--The actual wires
|
||||
dofile(minetest.get_modpath("mesecons").."/wires.lua");
|
||||
|
||||
--Services like turnoff receptor on dignode and so on
|
||||
dofile(minetest.get_modpath("mesecons").."/services.lua");
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
-- RECEPTORS
|
||||
-- mesecon.is_receptor(nodename) --> Returns true if nodename is a receptor
|
||||
-- mesecon.is_receptor_on(nodename --> Returns true if nodename is an receptor with state = mesecon.state.on
|
||||
-- mesecon.is_receptor_on(nodename) --> Returns true if nodename is an receptor with state = mesecon.state.on
|
||||
-- mesecon.is_receptor_off(nodename) --> Returns true if nodename is an receptor with state = mesecon.state.off
|
||||
-- mesecon.receptor_get_rules(node) --> Returns the rules of the receptor (mesecon.rules.default if none specified)
|
||||
|
||||
|
@ -37,11 +37,6 @@
|
|||
-- HIGH-LEVEL Internals
|
||||
-- mesecon.is_power_on(pos) --> Returns true if pos emits power in any way
|
||||
-- mesecon.is_power_off(pos) --> Returns true if pos does not emit power in any way
|
||||
-- mesecon.turnon(pos, link) --> link is the input rule that caused calling turnon, turns on every connected node, iterative
|
||||
-- mesecon.turnoff(pos, link) --> link is the input rule that caused calling turnoff, turns off every connected node, iterative
|
||||
-- mesecon.connected_to_receptor(pos, link) --> Returns true if pos is connected to a receptor directly or via conductors, iterative
|
||||
-- mesecon.rules_link(output, input, dug_outputrules) --> Returns true if outputposition + outputrules = inputposition and inputposition + inputrules = outputposition (if the two positions connect)
|
||||
-- mesecon.rules_link_anydir(outp., inp., d_outpr.) --> Same as rules mesecon.rules_link but also returns true if output and input are swapped
|
||||
-- mesecon.is_powered(pos) --> Returns true if pos is powered by a receptor or a conductor
|
||||
|
||||
-- RULES ROTATION helpers
|
||||
|
@ -51,6 +46,9 @@
|
|||
-- mesecon.rotate_rules_down(rules)
|
||||
-- These functions return rules that have been rotated in the specific direction
|
||||
|
||||
-- See fifo_queue.lua for documentation.
|
||||
mesecon.fifo_queue = dofile(minetest.get_modpath("mesecons").."/fifo_queue.lua")
|
||||
|
||||
-- General
|
||||
function mesecon.get_effector(nodename)
|
||||
if minetest.registered_nodes[nodename]
|
||||
|
@ -76,7 +74,9 @@ function mesecon.get_conductor(nodename)
|
|||
end
|
||||
end
|
||||
|
||||
function mesecon.get_any_outputrules (node)
|
||||
function mesecon.get_any_outputrules(node)
|
||||
if not node then return nil end
|
||||
|
||||
if mesecon.is_conductor(node.name) then
|
||||
return mesecon.conductor_get_rules(node)
|
||||
elseif mesecon.is_receptor(node.name) then
|
||||
|
@ -84,7 +84,9 @@ function mesecon.get_any_outputrules (node)
|
|||
end
|
||||
end
|
||||
|
||||
function mesecon.get_any_inputrules (node)
|
||||
function mesecon.get_any_inputrules(node)
|
||||
if not node then return nil end
|
||||
|
||||
if mesecon.is_conductor(node.name) then
|
||||
return mesecon.conductor_get_rules(node)
|
||||
elseif mesecon.is_effector(node.name) then
|
||||
|
@ -92,9 +94,9 @@ function mesecon.get_any_inputrules (node)
|
|||
end
|
||||
end
|
||||
|
||||
function mesecon.get_any_rules (node)
|
||||
return mesecon.mergetable(mesecon.get_any_inputrules(node) or {},
|
||||
mesecon.get_any_outputrules(node) or {})
|
||||
function mesecon.get_any_rules(node)
|
||||
return mesecon.merge_rule_sets(mesecon.get_any_inputrules(node),
|
||||
mesecon.get_any_outputrules(node))
|
||||
end
|
||||
|
||||
-- Receptors
|
||||
|
@ -182,7 +184,9 @@ end
|
|||
|
||||
-- Activation:
|
||||
mesecon.queue:add_function("activate", function (pos, rulename)
|
||||
local node = minetest.get_node(pos)
|
||||
local node = mesecon.get_node_force(pos)
|
||||
if not node then return end
|
||||
|
||||
local effector = mesecon.get_effector(node.name)
|
||||
|
||||
if effector and effector.action_on then
|
||||
|
@ -203,7 +207,9 @@ end
|
|||
|
||||
-- Deactivation
|
||||
mesecon.queue:add_function("deactivate", function (pos, rulename)
|
||||
local node = minetest.get_node(pos)
|
||||
local node = mesecon.get_node_force(pos)
|
||||
if not node then return end
|
||||
|
||||
local effector = mesecon.get_effector(node.name)
|
||||
|
||||
if effector and effector.action_off then
|
||||
|
@ -224,7 +230,9 @@ end
|
|||
|
||||
-- Change
|
||||
mesecon.queue:add_function("change", function (pos, rulename, changetype)
|
||||
local node = minetest.get_node(pos)
|
||||
local node = mesecon.get_node_force(pos)
|
||||
if not node then return end
|
||||
|
||||
local effector = mesecon.get_effector(node.name)
|
||||
|
||||
if effector and effector.action_change then
|
||||
|
@ -249,6 +257,8 @@ end
|
|||
-- Conductors
|
||||
|
||||
function mesecon.is_conductor_on(node, rulename)
|
||||
if not node then return false end
|
||||
|
||||
local conductor = mesecon.get_conductor(node.name)
|
||||
if conductor then
|
||||
if conductor.state then
|
||||
|
@ -263,10 +273,13 @@ function mesecon.is_conductor_on(node, rulename)
|
|||
return mesecon.get_bit(binstate, bit)
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function mesecon.is_conductor_off(node, rulename)
|
||||
if not node then return false end
|
||||
|
||||
local conductor = mesecon.get_conductor(node.name)
|
||||
if conductor then
|
||||
if conductor.state then
|
||||
|
@ -281,6 +294,7 @@ function mesecon.is_conductor_off(node, rulename)
|
|||
return not mesecon.get_bit(binstate, bit)
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -305,7 +319,7 @@ function mesecon.get_conductor_on(node_off, rulename)
|
|||
return conductor.states[tonumber(binstate,2)+1]
|
||||
end
|
||||
end
|
||||
return offstate
|
||||
return nil
|
||||
end
|
||||
|
||||
function mesecon.get_conductor_off(node_on, rulename)
|
||||
|
@ -321,7 +335,7 @@ function mesecon.get_conductor_off(node_on, rulename)
|
|||
return conductor.states[tonumber(binstate,2)+1]
|
||||
end
|
||||
end
|
||||
return onstate
|
||||
return nil
|
||||
end
|
||||
|
||||
function mesecon.conductor_get_rules(node)
|
||||
|
@ -340,233 +354,248 @@ end
|
|||
-- some more general high-level stuff
|
||||
|
||||
function mesecon.is_power_on(pos, rulename)
|
||||
local node = minetest.get_node(pos)
|
||||
if mesecon.is_conductor_on(node, rulename) or mesecon.is_receptor_on(node.name) then
|
||||
local node = mesecon.get_node_force(pos)
|
||||
if node and (mesecon.is_conductor_on(node, rulename) or mesecon.is_receptor_on(node.name)) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function mesecon.is_power_off(pos, rulename)
|
||||
local node = minetest.get_node(pos)
|
||||
if mesecon.is_conductor_off(node, rulename) or mesecon.is_receptor_off(node.name) then
|
||||
local node = mesecon.get_node_force(pos)
|
||||
if node and (mesecon.is_conductor_off(node, rulename) or mesecon.is_receptor_off(node.name)) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- The set of conductor states which require light updates when they change.
|
||||
local light_update_conductors
|
||||
|
||||
-- Calculate the contents of the above set if they have not been calculated.
|
||||
local function find_light_update_conductors()
|
||||
-- The expensive calculation is only done the first time.
|
||||
if light_update_conductors then return end
|
||||
|
||||
light_update_conductors = {}
|
||||
|
||||
-- Find conductors whose lighting characteristics change depending on their state.
|
||||
local checked = {}
|
||||
for name, def in pairs(minetest.registered_nodes) do
|
||||
local conductor = mesecon.get_conductor(name)
|
||||
if conductor and not checked[name] then
|
||||
-- Find the other states of the conductor besides the current one.
|
||||
local other_states
|
||||
if conductor.onstate then
|
||||
other_states = {conductor.onstate}
|
||||
elseif conductor.offstate then
|
||||
other_states = {conductor.offstate}
|
||||
else
|
||||
other_states = conductor.states
|
||||
end
|
||||
|
||||
-- Check the conductor. Other states are marked as checked.
|
||||
for _, other_state in ipairs(other_states) do
|
||||
local other_def = minetest.registered_nodes[other_state]
|
||||
if (def.paramtype == "light") ~= (other_def.paramtype == "light")
|
||||
or def.sunlight_propagates ~= other_def.sunlight_propagates
|
||||
or def.light_source ~= other_def.light_source then
|
||||
-- The light characteristics change depending on the state.
|
||||
-- The states are added to the set.
|
||||
light_update_conductors[name] = true
|
||||
for _, other_state in ipairs(other_states) do
|
||||
light_update_conductors[other_state] = true
|
||||
checked[other_state] = true
|
||||
end
|
||||
break
|
||||
end
|
||||
checked[other_state] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Turn off an equipotential section starting at `pos`, which outputs in the direction of `link`.
|
||||
-- Breadth-first search. Map is abstracted away in a voxelmanip.
|
||||
-- Follow all all conductor paths replacing conductors that were already
|
||||
-- looked at, activating / changing all effectors along the way.
|
||||
function mesecon.turnon(pos, link)
|
||||
local frontiers = {{pos = pos, link = link}}
|
||||
find_light_update_conductors()
|
||||
|
||||
local frontiers = mesecon.fifo_queue.new()
|
||||
frontiers:add({pos = pos, link = link})
|
||||
local pos_can_be_skipped = {}
|
||||
|
||||
local depth = 1
|
||||
while frontiers[depth] do
|
||||
local f = frontiers[depth]
|
||||
local node = minetest.get_node_or_nil(f.pos)
|
||||
for f in frontiers:iter() do
|
||||
local node = mesecon.get_node_force(f.pos)
|
||||
|
||||
-- area not loaded, postpone action
|
||||
if not node then
|
||||
mesecon.queue:add_action(f.pos, "turnon", {link}, nil, true)
|
||||
elseif mesecon.is_conductor_off(node, f.link) then
|
||||
-- Area does not exist; do nothing
|
||||
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
|
||||
elseif mesecon.is_conductor(node.name) then
|
||||
local rules = mesecon.conductor_get_rules(node)
|
||||
|
||||
minetest.swap_node(f.pos, {name = mesecon.get_conductor_on(node, f.link),
|
||||
param2 = node.param2})
|
||||
|
||||
-- call turnon on neighbors: normal rules
|
||||
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
|
||||
local np = mesecon.addPosRule(f.pos, r)
|
||||
|
||||
-- area not loaded, postpone action
|
||||
if not minetest.get_node_or_nil(np) then
|
||||
mesecon.queue:add_action(np, "turnon", {rulename},
|
||||
nil, true)
|
||||
else
|
||||
local links = mesecon.rules_link_rule_all(f.pos, r)
|
||||
for _, l in ipairs(links) do
|
||||
table.insert(frontiers, {pos = np, link = l})
|
||||
if mesecon.is_conductor_off(node, f.link) then
|
||||
-- Call turnon on neighbors
|
||||
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
|
||||
local np = vector.add(f.pos, r)
|
||||
if not pos_can_be_skipped[minetest.hash_node_position(np)] then
|
||||
for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
|
||||
frontiers:add({pos = np, link = l})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mesecon.swap_node_force(f.pos, mesecon.get_conductor_on(node, f.link), light_update_conductors[node.name] ~= nil)
|
||||
end
|
||||
|
||||
-- Only conductors with flat rules can be reliably skipped later
|
||||
if not rules[1] or rules[1].x then
|
||||
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
|
||||
end
|
||||
elseif mesecon.is_effector(node.name) then
|
||||
mesecon.changesignal(f.pos, node, f.link, mesecon.state.on, depth)
|
||||
if mesecon.is_effector_off(node.name) then
|
||||
mesecon.activate(f.pos, node, f.link, depth)
|
||||
end
|
||||
else
|
||||
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
|
||||
end
|
||||
depth = depth + 1
|
||||
end
|
||||
end
|
||||
|
||||
mesecon.queue:add_function("turnon", function (pos, rulename, recdepth)
|
||||
mesecon.turnon(pos, rulename, recdepth)
|
||||
end)
|
||||
|
||||
-- Turn on an equipotential section starting at `pos`, which outputs in the direction of `link`.
|
||||
-- Breadth-first search. Map is abstracted away in a voxelmanip.
|
||||
-- Follow all all conductor paths replacing conductors that were already
|
||||
-- looked at, deactivating / changing all effectors along the way.
|
||||
-- In case an onstate receptor is discovered, abort the process by returning false, which will
|
||||
-- cause `receptor_off` to discard all changes made in the voxelmanip.
|
||||
-- Contrary to turnon, turnoff has to cache all change and deactivate signals so that they will only
|
||||
-- be called in the very end when we can be sure that no conductor was found along the path.
|
||||
--
|
||||
-- Signal table entry structure:
|
||||
-- {
|
||||
-- pos = position of effector,
|
||||
-- node = node descriptor (name, param1 and param2),
|
||||
-- link = link the effector is connected to,
|
||||
-- depth = indicates order in which signals wire fired, higher is later
|
||||
-- }
|
||||
function mesecon.turnoff(pos, link)
|
||||
local frontiers = {{pos = pos, link = link}}
|
||||
find_light_update_conductors()
|
||||
|
||||
local frontiers = mesecon.fifo_queue.new()
|
||||
frontiers:add({pos = pos, link = link})
|
||||
local signals = {}
|
||||
local pos_can_be_skipped = {}
|
||||
|
||||
local depth = 1
|
||||
while frontiers[depth] do
|
||||
local f = frontiers[depth]
|
||||
local node = minetest.get_node_or_nil(f.pos)
|
||||
for f in frontiers:iter() do
|
||||
local node = mesecon.get_node_force(f.pos)
|
||||
|
||||
-- area not loaded, postpone action
|
||||
if not node then
|
||||
mesecon.queue:add_action(f.pos, "turnoff", {link}, nil, true)
|
||||
elseif mesecon.is_conductor_on(node, f.link) then
|
||||
-- Area does not exist; do nothing
|
||||
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
|
||||
elseif mesecon.is_conductor(node.name) then
|
||||
local rules = mesecon.conductor_get_rules(node)
|
||||
|
||||
minetest.swap_node(f.pos, {name = mesecon.get_conductor_off(node, f.link),
|
||||
param2 = node.param2})
|
||||
if mesecon.is_conductor_on(node, f.link) then
|
||||
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
|
||||
local np = vector.add(f.pos, r)
|
||||
|
||||
-- call turnoff on neighbors: normal rules
|
||||
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
|
||||
local np = mesecon.addPosRule(f.pos, r)
|
||||
if not pos_can_be_skipped[minetest.hash_node_position(np)] then
|
||||
-- Check if an onstate receptor is connected. If that is the case,
|
||||
-- abort this turnoff process by returning false. `receptor_off` will
|
||||
-- discard all the changes that we made in the voxelmanip:
|
||||
if mesecon.rules_link_rule_all_inverted(f.pos, r)[1] then
|
||||
if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- area not loaded, postpone action
|
||||
if not minetest.get_node_or_nil(np) then
|
||||
mesecon.queue:add_action(np, "turnoff", {rulename},
|
||||
nil, true)
|
||||
else
|
||||
local links = mesecon.rules_link_rule_all(f.pos, r)
|
||||
for _, l in ipairs(links) do
|
||||
table.insert(frontiers, {pos = np, link = l})
|
||||
-- Call turnoff on neighbors
|
||||
for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
|
||||
frontiers:add({pos = np, link = l})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link), light_update_conductors[node.name] ~= nil)
|
||||
end
|
||||
|
||||
-- Only conductors with flat rules can be reliably skipped later
|
||||
if not rules[1] or rules[1].x then
|
||||
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
|
||||
end
|
||||
elseif mesecon.is_effector(node.name) then
|
||||
mesecon.changesignal(f.pos, node, f.link, mesecon.state.off, depth)
|
||||
if mesecon.is_effector_on(node.name) and not mesecon.is_powered(f.pos) then
|
||||
mesecon.deactivate(f.pos, node, f.link, depth)
|
||||
end
|
||||
table.insert(signals, {
|
||||
pos = f.pos,
|
||||
node = node,
|
||||
link = f.link,
|
||||
depth = depth
|
||||
})
|
||||
else
|
||||
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
|
||||
end
|
||||
depth = depth + 1
|
||||
end
|
||||
end
|
||||
|
||||
mesecon.queue:add_function("turnoff", function (pos, rulename, recdepth)
|
||||
mesecon.turnoff(pos, rulename, recdepth)
|
||||
end)
|
||||
|
||||
|
||||
function mesecon.connected_to_receptor(pos, link)
|
||||
local node = minetest.get_node(pos)
|
||||
|
||||
-- Check if conductors around are connected
|
||||
local rules = mesecon.get_any_inputrules(node)
|
||||
if not rules then return false end
|
||||
|
||||
for _, rule in ipairs(mesecon.rule2meta(link, rules)) do
|
||||
local links = mesecon.rules_link_rule_all_inverted(pos, rule)
|
||||
for _, l in ipairs(links) do
|
||||
local np = mesecon.addPosRule(pos, l)
|
||||
if mesecon.find_receptor_on(np, mesecon.invertRule(l)) then
|
||||
return true
|
||||
for _, sig in ipairs(signals) do
|
||||
-- If sig.depth is 1, it has not yet been checked that the power source is actually off.
|
||||
if sig.depth > 1 or not mesecon.is_powered(sig.pos, sig.link) then
|
||||
mesecon.changesignal(sig.pos, sig.node, sig.link, mesecon.state.off, sig.depth)
|
||||
if mesecon.is_effector_on(sig.node.name) and not mesecon.is_powered(sig.pos) then
|
||||
mesecon.deactivate(sig.pos, sig.node, sig.link, sig.depth)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function mesecon.find_receptor_on(pos, link)
|
||||
local frontiers = {{pos = pos, link = link}}
|
||||
local checked = {}
|
||||
|
||||
-- List of positions that have been searched for onstate receptors
|
||||
local depth = 1
|
||||
while frontiers[depth] do
|
||||
local f = frontiers[depth]
|
||||
local node = minetest.get_node_or_nil(f.pos)
|
||||
|
||||
if not node then return false end
|
||||
if mesecon.is_receptor_on(node.name) then return true end
|
||||
if mesecon.is_conductor_on(node, f.link) then
|
||||
local rules = mesecon.conductor_get_rules(node)
|
||||
|
||||
-- call turnoff on neighbors: normal rules
|
||||
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
|
||||
local np = mesecon.addPosRule(f.pos, r)
|
||||
|
||||
local links = mesecon.rules_link_rule_all_inverted(f.pos, r)
|
||||
for _, l in ipairs(links) do
|
||||
local checkedstring = np.x..np.y..np.z..l.x..l.y..l.z
|
||||
if not checked[checkedstring] then
|
||||
table.insert(frontiers, {pos = np, link = l})
|
||||
checked[checkedstring] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
depth = depth + 1
|
||||
end
|
||||
end
|
||||
|
||||
function mesecon.rules_link(output, input, dug_outputrules) --output/input are positions (outputrules optional, used if node has been dug), second return value: the name of the affected input rule
|
||||
local outputnode = minetest.get_node(output)
|
||||
local inputnode = minetest.get_node(input)
|
||||
local outputrules = dug_outputrules or mesecon.get_any_outputrules (outputnode)
|
||||
local inputrules = mesecon.get_any_inputrules (inputnode)
|
||||
if not outputrules or not inputrules then
|
||||
return
|
||||
end
|
||||
|
||||
for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do
|
||||
-- Check if output sends to input
|
||||
if mesecon.cmpPos(mesecon.addPosRule(output, outputrule), input) then
|
||||
for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do
|
||||
-- Check if input accepts from output
|
||||
if mesecon.cmpPos(mesecon.addPosRule(input, inputrule), output) then
|
||||
return true, inputrule
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
return true
|
||||
end
|
||||
|
||||
-- Get all linking inputrules of inputnode (effector or conductor) that is connected to
|
||||
-- outputnode (receptor or conductor) at position `output` and has an output in direction `rule`
|
||||
function mesecon.rules_link_rule_all(output, rule)
|
||||
local input = mesecon.addPosRule(output, rule)
|
||||
local inputnode = minetest.get_node(input)
|
||||
local inputrules = mesecon.get_any_inputrules (inputnode)
|
||||
local input = vector.add(output, rule)
|
||||
local inputnode = mesecon.get_node_force(input)
|
||||
local inputrules = mesecon.get_any_inputrules(inputnode)
|
||||
if not inputrules then
|
||||
return {}
|
||||
end
|
||||
local rules = {}
|
||||
|
||||
|
||||
for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do
|
||||
-- Check if input accepts from output
|
||||
if mesecon.cmpPos(mesecon.addPosRule(input, inputrule), output) then
|
||||
if vector.equals(vector.add(input, inputrule), output) then
|
||||
table.insert(rules, inputrule)
|
||||
end
|
||||
end
|
||||
|
||||
return rules
|
||||
end
|
||||
|
||||
-- Get all linking outputnodes of outputnode (receptor or conductor) that is connected to
|
||||
-- inputnode (effector or conductor) at position `input` and has an input in direction `rule`
|
||||
function mesecon.rules_link_rule_all_inverted(input, rule)
|
||||
--local irule = mesecon.invertRule(rule)
|
||||
local output = mesecon.addPosRule(input, rule)
|
||||
local outputnode = minetest.get_node(output)
|
||||
local outputrules = mesecon.get_any_outputrules (outputnode)
|
||||
local output = vector.add(input, rule)
|
||||
local outputnode = mesecon.get_node_force(output)
|
||||
local outputrules = mesecon.get_any_outputrules(outputnode)
|
||||
if not outputrules then
|
||||
return {}
|
||||
end
|
||||
local rules = {}
|
||||
|
||||
|
||||
for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do
|
||||
if mesecon.cmpPos(mesecon.addPosRule(output, outputrule), input) then
|
||||
if vector.equals(vector.add(output, outputrule), input) then
|
||||
table.insert(rules, mesecon.invertRule(outputrule))
|
||||
end
|
||||
end
|
||||
return rules
|
||||
end
|
||||
|
||||
function mesecon.rules_link_anydir(pos1, pos2)
|
||||
return mesecon.rules_link(pos1, pos2) or mesecon.rules_link(pos2, pos1)
|
||||
end
|
||||
|
||||
function mesecon.is_powered(pos, rule)
|
||||
local node = minetest.get_node(pos)
|
||||
local node = mesecon.get_node_force(pos)
|
||||
local rules = mesecon.get_any_inputrules(node)
|
||||
if not rules then return false end
|
||||
|
||||
|
@ -577,10 +606,11 @@ function mesecon.is_powered(pos, rule)
|
|||
for _, rule in ipairs(mesecon.flattenrules(rules)) do
|
||||
local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
|
||||
for _, rname in ipairs(rulenames) do
|
||||
local np = mesecon.addPosRule(pos, rname)
|
||||
local nn = minetest.get_node(np)
|
||||
if (mesecon.is_conductor_on (nn, mesecon.invertRule(rname))
|
||||
or mesecon.is_receptor_on (nn.name)) then
|
||||
local np = vector.add(pos, rname)
|
||||
local nn = mesecon.get_node_force(np)
|
||||
|
||||
if (mesecon.is_conductor_on(nn, mesecon.invertRule(rname))
|
||||
or mesecon.is_receptor_on(nn.name)) then
|
||||
table.insert(sourcepos, np)
|
||||
end
|
||||
end
|
||||
|
@ -588,8 +618,8 @@ function mesecon.is_powered(pos, rule)
|
|||
else
|
||||
local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
|
||||
for _, rname in ipairs(rulenames) do
|
||||
local np = mesecon.addPosRule(pos, rname)
|
||||
local nn = minetest.get_node(np)
|
||||
local np = vector.add(pos, rname)
|
||||
local nn = mesecon.get_node_force(np)
|
||||
if (mesecon.is_conductor_on (nn, mesecon.invertRule(rname))
|
||||
or mesecon.is_receptor_on (nn.name)) then
|
||||
table.insert(sourcepos, np)
|
||||
|
@ -601,52 +631,3 @@ function mesecon.is_powered(pos, rule)
|
|||
if (#sourcepos == 0) then return false
|
||||
else return sourcepos end
|
||||
end
|
||||
|
||||
--Rules rotation Functions:
|
||||
function mesecon.rotate_rules_right(rules)
|
||||
local nr = {}
|
||||
for i, rule in ipairs(rules) do
|
||||
table.insert(nr, {
|
||||
x = -rule.z,
|
||||
y = rule.y,
|
||||
z = rule.x,
|
||||
name = rule.name})
|
||||
end
|
||||
return nr
|
||||
end
|
||||
|
||||
function mesecon.rotate_rules_left(rules)
|
||||
local nr = {}
|
||||
for i, rule in ipairs(rules) do
|
||||
table.insert(nr, {
|
||||
x = rule.z,
|
||||
y = rule.y,
|
||||
z = -rule.x,
|
||||
name = rule.name})
|
||||
end
|
||||
return nr
|
||||
end
|
||||
|
||||
function mesecon.rotate_rules_down(rules)
|
||||
local nr = {}
|
||||
for i, rule in ipairs(rules) do
|
||||
table.insert(nr, {
|
||||
x = -rule.y,
|
||||
y = rule.x,
|
||||
z = rule.z,
|
||||
name = rule.name})
|
||||
end
|
||||
return nr
|
||||
end
|
||||
|
||||
function mesecon.rotate_rules_up(rules)
|
||||
local nr = {}
|
||||
for i, rule in ipairs(rules) do
|
||||
table.insert(nr, {
|
||||
x = rule.y,
|
||||
y = -rule.x,
|
||||
z = rule.z,
|
||||
name = rule.name})
|
||||
end
|
||||
return nr
|
||||
end
|
||||
|
|
|
@ -1,30 +1,14 @@
|
|||
-- Ugly hack to prevent breaking compatibility with other mods
|
||||
-- Just remove the following two functions to delete the hack, to be done when other mods have updated
|
||||
function mesecon.receptor_on(self, pos, rules)
|
||||
if (self.receptor_on) then
|
||||
print("[Mesecons] Warning: A mod with mesecon support called mesecon:receptor_on.")
|
||||
print("[Mesecons] If you are the programmer of this mod, please update it ")
|
||||
print("[Mesecons] to use mesecon.receptor_on instead. mesecon:* is deprecated")
|
||||
print("[Mesecons] Otherwise, please make sure you're running the latest version")
|
||||
print("[Mesecons] of that mod and inform the mod creator.")
|
||||
else
|
||||
rules = pos
|
||||
pos = self
|
||||
end
|
||||
mesecon.queue:add_action(pos, "receptor_on", {rules}, nil, rules)
|
||||
-- Un-forceload any forceloaded mapblocks from older versions of Mesecons which
|
||||
-- used forceloading instead of VoxelManipulators.
|
||||
local BLOCKSIZE = 16
|
||||
|
||||
-- convert block hash --> node position
|
||||
local function unhash_blockpos(hash)
|
||||
return vector.multiply(minetest.get_position_from_hash(hash), BLOCKSIZE)
|
||||
end
|
||||
|
||||
function mesecon.receptor_off(self, pos, rules)
|
||||
if (self.receptor_off) then
|
||||
print("[Mesecons] Warning: A mod with mesecon support called mesecon:receptor_off.")
|
||||
print("[Mesecons] If you are the programmer of this mod, please update it ")
|
||||
print("[Mesecons] to use mesecon.receptor_off instead. mesecon:* is deprecated")
|
||||
print("[Mesecons] Otherwise, please make sure you're running the latest version")
|
||||
print("[Mesecons] of that mod and inform the mod creator.")
|
||||
else
|
||||
rules = pos
|
||||
pos = self
|
||||
end
|
||||
mesecon.queue:add_action(pos, "receptor_off", {rules}, nil, rules)
|
||||
local old_forceloaded_blocks = mesecon.file2table("mesecon_forceloaded")
|
||||
for hash, _ in pairs(old_forceloaded_blocks) do
|
||||
minetest.forceload_free_block(unhash_blockpos(hash))
|
||||
end
|
||||
|
||||
os.remove(minetest.get_worldpath().."/mesecon_forceloaded")
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# textdomain: mesecons
|
||||
|
||||
Mesecons=Mesecons
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: mesecons
|
||||
|
||||
### oldwires.lua ###
|
||||
Mesecons=Mesekonduktilo
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: mesecons
|
||||
|
||||
### oldwires.lua ###
|
||||
Mesecons=
|
|
@ -0,0 +1,4 @@
|
|||
name = mesecons
|
||||
# default is an optional dependency as some mods may expect it as a transitory
|
||||
# dependency when they depend on mesecons.
|
||||
optional_depends = default
|
|
@ -1,17 +1,19 @@
|
|||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
minetest.register_node("mesecons:mesecon_off", {
|
||||
drawtype = "raillike",
|
||||
tiles = {"jeija_mesecon_off.png", "jeija_mesecon_curved_off.png", "jeija_mesecon_t_junction_off.png", "jeija_mesecon_crossing_off.png"},
|
||||
inventory_image = "jeija_mesecon_off.png",
|
||||
wield_image = "jeija_mesecon_off.png",
|
||||
paramtype = "light",
|
||||
is_ground_content = true,
|
||||
is_ground_content = false,
|
||||
walkable = false,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, -0.5, 0.5, -0.45, 0.5},
|
||||
},
|
||||
groups = {dig_immediate=3, mesecon=1, mesecon_conductor_craftable=1},
|
||||
description="Mesecons",
|
||||
description= S("Mesecons"),
|
||||
mesecons = {conductor={
|
||||
state = mesecon.state.off,
|
||||
onstate = "mesecons:mesecon_on"
|
||||
|
@ -22,7 +24,7 @@ minetest.register_node("mesecons:mesecon_on", {
|
|||
drawtype = "raillike",
|
||||
tiles = {"jeija_mesecon_on.png", "jeija_mesecon_curved_on.png", "jeija_mesecon_t_junction_on.png", "jeija_mesecon_crossing_on.png"},
|
||||
paramtype = "light",
|
||||
is_ground_content = true,
|
||||
is_ground_content = false,
|
||||
walkable = false,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
|
@ -30,7 +32,7 @@ minetest.register_node("mesecons:mesecon_on", {
|
|||
},
|
||||
groups = {dig_immediate=3, not_in_creaive_inventory=1, mesecon=1},
|
||||
drop = "mesecons:mesecon_off 1",
|
||||
light_source = LIGHT_MAX-11,
|
||||
light_source = minetest.LIGHT_MAX-11,
|
||||
mesecons = {conductor={
|
||||
state = mesecon.state.on,
|
||||
offstate = "mesecons:mesecon_off"
|
||||
|
|
|
@ -1,46 +1,87 @@
|
|||
mesecon.rules = {}
|
||||
mesecon.state = {}
|
||||
|
||||
mesecon.rules.default =
|
||||
{{x=0, y=0, z=-1},
|
||||
{x=1, y=0, z=0},
|
||||
{x=-1, y=0, z=0},
|
||||
{x=0, y=0, z=1},
|
||||
{x=1, y=1, z=0},
|
||||
{x=1, y=-1, z=0},
|
||||
{x=-1, y=1, z=0},
|
||||
{x=-1, y=-1, z=0},
|
||||
{x=0, y=1, z=1},
|
||||
{x=0, y=-1, z=1},
|
||||
{x=0, y=1, z=-1},
|
||||
{x=0, y=-1, z=-1}}
|
||||
mesecon.rules.default = {
|
||||
{x = 0, y = 0, z = -1},
|
||||
{x = 1, y = 0, z = 0},
|
||||
{x = -1, y = 0, z = 0},
|
||||
{x = 0, y = 0, z = 1},
|
||||
{x = 1, y = 1, z = 0},
|
||||
{x = 1, y = -1, z = 0},
|
||||
{x = -1, y = 1, z = 0},
|
||||
{x = -1, y = -1, z = 0},
|
||||
{x = 0, y = 1, z = 1},
|
||||
{x = 0, y = -1, z = 1},
|
||||
{x = 0, y = 1, z = -1},
|
||||
{x = 0, y = -1, z = -1},
|
||||
}
|
||||
|
||||
mesecon.rules.pplate = mesecon.mergetable(mesecon.rules.default, {{x=0, y=-2, z=0}})
|
||||
mesecon.rules.floor = mesecon.merge_rule_sets(mesecon.rules.default, {{x = 0, y = -1, z = 0}})
|
||||
|
||||
mesecon.rules.buttonlike =
|
||||
{{x = 1, y = 0, z = 0},
|
||||
{x = 1, y = 1, z = 0},
|
||||
{x = 1, y =-1, z = 0},
|
||||
{x = 1, y =-1, z = 1},
|
||||
{x = 1, y =-1, z =-1},
|
||||
{x = 2, y = 0, z = 0}}
|
||||
mesecon.rules.pplate = mesecon.merge_rule_sets(mesecon.rules.floor, {{x = 0, y = -2, z = 0}})
|
||||
|
||||
mesecon.rules.buttonlike = {
|
||||
{x = 1, y = 0, z = 0},
|
||||
{x = 1, y = 1, z = 0},
|
||||
{x = 1, y = -1, z = 0},
|
||||
{x = 1, y = -1, z = 1},
|
||||
{x = 1, y = -1, z = -1},
|
||||
{x = 2, y = 0, z = 0},
|
||||
}
|
||||
|
||||
mesecon.rules.flat = {
|
||||
{x = 1, y = 0, z = 0},
|
||||
{x = -1, y = 0, z = 0},
|
||||
{x = 0, y = 0, z = 1},
|
||||
{x = 0, y = 0, z = -1},
|
||||
}
|
||||
|
||||
mesecon.rules.alldirs = {
|
||||
{x = 1, y = 0, z = 0},
|
||||
{x = -1, y = 0, z = 0},
|
||||
{x = 0, y = 1, z = 0},
|
||||
{x = 0, y = -1, z = 0},
|
||||
{x = 0, y = 0, z = 1},
|
||||
{x = 0, y = 0, z = -1},
|
||||
}
|
||||
|
||||
local rules_wallmounted = {
|
||||
xp = mesecon.rotate_rules_down(mesecon.rules.floor),
|
||||
xn = mesecon.rotate_rules_up(mesecon.rules.floor),
|
||||
yp = mesecon.rotate_rules_up(mesecon.rotate_rules_up(mesecon.rules.floor)),
|
||||
yn = mesecon.rules.floor,
|
||||
zp = mesecon.rotate_rules_left(mesecon.rotate_rules_up(mesecon.rules.floor)),
|
||||
zn = mesecon.rotate_rules_right(mesecon.rotate_rules_up(mesecon.rules.floor)),
|
||||
}
|
||||
|
||||
local rules_buttonlike = {
|
||||
xp = mesecon.rules.buttonlike,
|
||||
xn = mesecon.rotate_rules_right(mesecon.rotate_rules_right(mesecon.rules.buttonlike)),
|
||||
yp = mesecon.rotate_rules_down(mesecon.rules.buttonlike),
|
||||
yn = mesecon.rotate_rules_up(mesecon.rules.buttonlike),
|
||||
zp = mesecon.rotate_rules_right(mesecon.rules.buttonlike),
|
||||
zn = mesecon.rotate_rules_left(mesecon.rules.buttonlike),
|
||||
}
|
||||
|
||||
local function rules_from_dir(ruleset, dir)
|
||||
if not dir then return {} end
|
||||
|
||||
if dir.x == 1 then return ruleset.xp end
|
||||
if dir.y == 1 then return ruleset.yp end
|
||||
if dir.z == 1 then return ruleset.zp end
|
||||
if dir.x == -1 then return ruleset.xn end
|
||||
if dir.y == -1 then return ruleset.yn end
|
||||
if dir.z == -1 then return ruleset.zn end
|
||||
end
|
||||
|
||||
mesecon.rules.wallmounted_get = function(node)
|
||||
local dir = minetest.wallmounted_to_dir(node.param2)
|
||||
return rules_from_dir(rules_wallmounted, dir)
|
||||
end
|
||||
|
||||
mesecon.rules.flat =
|
||||
{{x = 1, y = 0, z = 0},
|
||||
{x =-1, y = 0, z = 0},
|
||||
{x = 0, y = 0, z = 1},
|
||||
{x = 0, y = 0, z =-1}}
|
||||
|
||||
mesecon.rules.buttonlike_get = function(node)
|
||||
local rules = mesecon.rules.buttonlike
|
||||
if node.param2 == 2 then
|
||||
rules=mesecon.rotate_rules_left(rules)
|
||||
elseif node.param2 == 3 then
|
||||
rules=mesecon.rotate_rules_right(mesecon.rotate_rules_right(rules))
|
||||
elseif node.param2 == 0 then
|
||||
rules=mesecon.rotate_rules_right(rules)
|
||||
end
|
||||
return rules
|
||||
local dir = minetest.facedir_to_dir(node.param2)
|
||||
return rules_from_dir(rules_buttonlike, dir)
|
||||
end
|
||||
|
||||
mesecon.state.on = "on"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
-- Dig and place services
|
||||
|
||||
mesecon.on_placenode = function (pos, node)
|
||||
mesecon.update_autoconnect(pos, node)
|
||||
mesecon.on_placenode = function(pos, node)
|
||||
mesecon.execute_autoconnect_hooks_now(pos, node)
|
||||
node = minetest.get_node(pos) -- Update the node in case it was just changed.
|
||||
|
||||
-- Receptors: Send on signal when active
|
||||
if mesecon.is_receptor_on(node.name) then
|
||||
|
@ -11,28 +12,33 @@ mesecon.on_placenode = function (pos, node)
|
|||
-- Conductors: Send turnon signal when powered or replace by respective offstate conductor
|
||||
-- if placed conductor is an onstate one
|
||||
if mesecon.is_conductor(node.name) then
|
||||
local conductor = mesecon.get_conductor(node.name)
|
||||
if conductor.state ~= mesecon.state.off then
|
||||
-- Turn the conductor off.
|
||||
node.name = conductor.offstate or conductor.states[1]
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
local sources = mesecon.is_powered(pos)
|
||||
if sources then
|
||||
-- also call receptor_on if itself is powered already, so that neighboring
|
||||
-- conductors will be activated (when pushing an on-conductor with a piston)
|
||||
mesecon.vm_begin()
|
||||
for _, s in ipairs(sources) do
|
||||
local rule = {x = pos.x - s.x, y = pos.y - s.y, z = pos.z - s.z}
|
||||
local rule = vector.subtract(s, pos)
|
||||
mesecon.turnon(pos, rule)
|
||||
end
|
||||
--mesecon.receptor_on (pos, mesecon.conductor_get_rules(node))
|
||||
elseif mesecon.is_conductor_on(node) then
|
||||
minetest.swap_node(pos, {name = mesecon.get_conductor_off(node)})
|
||||
mesecon.vm_commit()
|
||||
end
|
||||
end
|
||||
|
||||
-- Effectors: Send changesignal and activate or deactivate
|
||||
if mesecon.is_effector(node.name) then
|
||||
local powered_rules = {}
|
||||
local unpowered_rules = {}
|
||||
|
||||
-- for each input rule, check if powered
|
||||
for _, r in ipairs(mesecon.effector_get_rules(node)) do
|
||||
local powered = mesecon.is_powered(pos, r)
|
||||
if powered then table.insert(powered_rules, r) end
|
||||
if powered then table.insert(powered_rules, r)
|
||||
else table.insert(unpowered_rules, r) end
|
||||
|
||||
local state = powered and mesecon.state.on or mesecon.state.off
|
||||
mesecon.changesignal(pos, node, r, state, 1)
|
||||
|
@ -42,53 +48,92 @@ mesecon.on_placenode = function (pos, node)
|
|||
for _, r in ipairs(powered_rules) do
|
||||
mesecon.activate(pos, node, r, 1)
|
||||
end
|
||||
else
|
||||
for _, r in ipairs(unpowered_rules) do
|
||||
mesecon.deactivate(pos, node, r, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mesecon.on_dignode = function (pos, node)
|
||||
mesecon.on_dignode = function(pos, node)
|
||||
if mesecon.is_conductor_on(node) then
|
||||
mesecon.receptor_off(pos, mesecon.conductor_get_rules(node))
|
||||
elseif mesecon.is_receptor_on(node.name) then
|
||||
mesecon.receptor_off(pos, mesecon.receptor_get_rules(node))
|
||||
end
|
||||
mesecon.queue:add_action(pos, "update_autoconnect", {node})
|
||||
|
||||
mesecon.execute_autoconnect_hooks_queue(pos, node)
|
||||
end
|
||||
|
||||
mesecon.queue:add_function("update_autoconnect", mesecon.update_autoconnect)
|
||||
function mesecon.on_blastnode(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
minetest.remove_node(pos)
|
||||
mesecon.on_dignode(pos, node)
|
||||
return minetest.get_node_drops(node.name, "")
|
||||
end
|
||||
|
||||
minetest.register_on_placenode(mesecon.on_placenode)
|
||||
minetest.register_on_dignode(mesecon.on_dignode)
|
||||
|
||||
-- Overheating service for fast circuits
|
||||
local OVERHEAT_MAX = mesecon.setting("overheat_max", 20)
|
||||
local COOLDOWN_TIME = mesecon.setting("cooldown_time", 2.0)
|
||||
local COOLDOWN_STEP = mesecon.setting("cooldown_granularity", 0.5)
|
||||
local COOLDOWN_MULTIPLIER = OVERHEAT_MAX / COOLDOWN_TIME
|
||||
local cooldown_timer = 0.0
|
||||
local object_heat = {}
|
||||
|
||||
-- returns true if heat is too high
|
||||
mesecon.do_overheat = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local heat = meta:get_int("heat") or 0
|
||||
|
||||
heat = heat + 1
|
||||
meta:set_int("heat", heat)
|
||||
|
||||
if heat < mesecon.setting("overheat_max", 20) then
|
||||
mesecon.queue:add_action(pos, "cooldown", {}, 1, nil, 0)
|
||||
else
|
||||
function mesecon.do_overheat(pos)
|
||||
local id = minetest.hash_node_position(pos)
|
||||
local heat = (object_heat[id] or 0) + 1
|
||||
object_heat[id] = heat
|
||||
if heat >= OVERHEAT_MAX then
|
||||
minetest.log("action", "Node overheats at " .. minetest.pos_to_string(pos))
|
||||
object_heat[id] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function mesecon.do_cooldown(pos)
|
||||
local id = minetest.hash_node_position(pos)
|
||||
object_heat[id] = nil
|
||||
end
|
||||
|
||||
mesecon.queue:add_function("cooldown", function (pos)
|
||||
if minetest.get_item_group(minetest.get_node(pos).name, "overheat") == 0 then
|
||||
return -- node has been moved, this one does not use overheating - ignore
|
||||
function mesecon.get_heat(pos)
|
||||
local id = minetest.hash_node_position(pos)
|
||||
return object_heat[id] or 0
|
||||
end
|
||||
|
||||
function mesecon.move_hot_nodes(moved_nodes)
|
||||
local new_heat = {}
|
||||
for _, n in ipairs(moved_nodes) do
|
||||
local old_id = minetest.hash_node_position(n.oldpos)
|
||||
local new_id = minetest.hash_node_position(n.pos)
|
||||
new_heat[new_id] = object_heat[old_id]
|
||||
object_heat[old_id] = nil
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local heat = meta:get_int("heat")
|
||||
|
||||
if (heat > 0) then
|
||||
meta:set_int("heat", heat - 1)
|
||||
for id, heat in pairs(new_heat) do
|
||||
object_heat[id] = heat
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function global_cooldown(dtime)
|
||||
cooldown_timer = cooldown_timer + dtime
|
||||
if cooldown_timer < COOLDOWN_STEP then
|
||||
return -- don't overload the CPU
|
||||
end
|
||||
local cooldown = COOLDOWN_MULTIPLIER * cooldown_timer
|
||||
cooldown_timer = 0
|
||||
for id, heat in pairs(object_heat) do
|
||||
heat = heat - cooldown
|
||||
if heat <= 0 then
|
||||
object_heat[id] = nil -- free some RAM
|
||||
else
|
||||
object_heat[id] = heat
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.register_globalstep(global_cooldown)
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
-- SETTINGS
|
||||
function mesecon.setting(setting, default)
|
||||
if type(default) == "bool" then
|
||||
return minetest.setting_getbool("mesecon."..setting) or default
|
||||
elseif type(default) == "string" then
|
||||
return minetest.setting_get("mesecon."..setting) or default
|
||||
elseif type(default) == "number" then
|
||||
return tonumber(minetest.setting_get("mesecon."..setting) or default)
|
||||
end
|
||||
end
|
||||
-- SETTINGS
|
||||
function mesecon.setting(setting, default)
|
||||
if type(default) == "boolean" then
|
||||
local read = minetest.settings:get_bool("mesecon."..setting)
|
||||
if read == nil then
|
||||
return default
|
||||
else
|
||||
return read
|
||||
end
|
||||
elseif type(default) == "string" then
|
||||
return minetest.settings:get("mesecon."..setting) or default
|
||||
elseif type(default) == "number" then
|
||||
return tonumber(minetest.settings:get("mesecon."..setting) or default)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
require("mineunit")
|
||||
|
||||
fixture("mesecons")
|
||||
|
||||
describe("action queue", function()
|
||||
local layout = {
|
||||
{{x = 1, y = 0, z = 0}, "mesecons:test_receptor_off"},
|
||||
{{x = 0, y = 0, z = 0}, "mesecons:test_conductor_off"},
|
||||
{{x = -1, y = 0, z = 0}, "mesecons:test_conductor_off"},
|
||||
{{x = 0, y = 1, z = 0}, "mesecons:test_effector"},
|
||||
{{x = -1, y = 1, z = 0}, "mesecons:test_effector"},
|
||||
}
|
||||
|
||||
before_each(function()
|
||||
world.layout(layout)
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
mesecon._test_reset()
|
||||
world.clear()
|
||||
end)
|
||||
|
||||
it("executes in order", function()
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
mineunit:execute_globalstep() -- Execute activate/change actions
|
||||
assert.equal(2, #mesecon._test_effector_events)
|
||||
assert.same({"on", layout[4][1]}, mesecon._test_effector_events[1])
|
||||
assert.same({"on", layout[5][1]}, mesecon._test_effector_events[2])
|
||||
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_off")
|
||||
mesecon.receptor_off(layout[1][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_off action
|
||||
mineunit:execute_globalstep() -- Execute deactivate/change actions
|
||||
assert.equal(4, #mesecon._test_effector_events)
|
||||
assert.same({"off", layout[4][1]}, mesecon._test_effector_events[3])
|
||||
assert.same({"off", layout[5][1]}, mesecon._test_effector_events[4])
|
||||
end)
|
||||
|
||||
it("discards outdated/overwritten node events", function()
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs)
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_off")
|
||||
mesecon.receptor_off(layout[1][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_off action
|
||||
mineunit:execute_globalstep() -- Execute deactivate/change actions
|
||||
assert.equal(0, #mesecon._test_effector_events)
|
||||
end)
|
||||
|
||||
it("delays actions", function()
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_on")
|
||||
mesecon.queue:add_action(layout[1][1], "receptor_on", {mesecon.rules.alldirs}, 1, nil)
|
||||
mineunit:execute_globalstep(0.1)
|
||||
mineunit:execute_globalstep(1)
|
||||
assert.equal(0, #mesecon._test_effector_events)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
assert.equal(0, #mesecon._test_effector_events)
|
||||
mineunit:execute_globalstep() -- Execute activate/change actions
|
||||
assert.equal(2, #mesecon._test_effector_events)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1 @@
|
|||
fixture_paths = {"../.test_fixtures"}
|
|
@ -0,0 +1,192 @@
|
|||
require("mineunit")
|
||||
|
||||
fixture("mesecons")
|
||||
fixture("screwdriver")
|
||||
|
||||
describe("placement/digging service", function()
|
||||
local layout = {
|
||||
{{x = 1, y = 0, z = 0}, "mesecons:test_receptor_on"},
|
||||
{{x = 0, y = 0, z = 0}, "mesecons:test_conductor_on"},
|
||||
{{x = -1, y = 0, z = 0}, "mesecons:test_conductor_on"},
|
||||
{{x = 0, y = 1, z = 0}, "mesecons:test_effector"},
|
||||
{{x = -2, y = 0, z = 0}, "mesecons:test_effector"},
|
||||
{{x = 2, y = 0, z = 0}, "mesecons:test_effector"},
|
||||
}
|
||||
|
||||
before_each(function()
|
||||
world.layout(layout)
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
mesecon._test_reset()
|
||||
world.clear()
|
||||
end)
|
||||
|
||||
it("updates components when a receptor changes", function()
|
||||
-- Dig then replace a receptor and check that the connected effectors changed.
|
||||
|
||||
mesecon._test_dig(layout[1][1])
|
||||
mineunit:execute_globalstep() -- Execute receptor_off action
|
||||
assert.equal("mesecons:test_conductor_off", world.get_node(layout[2][1]).name)
|
||||
mineunit:execute_globalstep() -- Execute deactivate/change actions
|
||||
assert.equal(3, #mesecon._test_effector_events)
|
||||
|
||||
mesecon._test_place(layout[1][1], "mesecons:test_receptor_on")
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[2][1]).name)
|
||||
mineunit:execute_globalstep() -- Execute activate/change action
|
||||
assert.equal(6, #mesecon._test_effector_events)
|
||||
end)
|
||||
|
||||
it("updates components when a conductor changes", function()
|
||||
-- Dig then replace a powered conductor and check that the connected effectors changed.
|
||||
|
||||
mesecon._test_dig(layout[2][1])
|
||||
mineunit:execute_globalstep() -- Execute receptor_off action
|
||||
assert.equal("mesecons:test_conductor_off", world.get_node(layout[3][1]).name)
|
||||
mineunit:execute_globalstep() -- Execute deactivate/change actions
|
||||
assert.equal(2, #mesecon._test_effector_events)
|
||||
|
||||
mesecon._test_place(layout[2][1], "mesecons:test_conductor_off")
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[2][1]).name)
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[3][1]).name)
|
||||
mineunit:execute_globalstep() -- Execute activate/change actions
|
||||
assert.equal(4, #mesecon._test_effector_events)
|
||||
end)
|
||||
|
||||
it("updates effectors on placement", function()
|
||||
local pos = {x = 0, y = 0, z = 1}
|
||||
mesecon._test_place(pos, "mesecons:test_effector")
|
||||
mineunit:execute_globalstep() -- Execute activate/change actions
|
||||
assert.equal(tonumber("10100000", 2), world.get_node(pos).param2)
|
||||
end)
|
||||
|
||||
it("updates multiconductors on placement", function()
|
||||
local pos = {x = 0, y = 0, z = 1}
|
||||
mesecon._test_place(pos, "mesecons:test_multiconductor_off")
|
||||
assert.equal("mesecons:test_multiconductor_010", world.get_node(pos).name)
|
||||
end)
|
||||
|
||||
it("turns off conductors on placement", function()
|
||||
local pos = {x = 3, y = 0, z = 0}
|
||||
mesecon._test_place(pos, "mesecons:test_conductor_on")
|
||||
assert.equal("mesecons:test_conductor_off", world.get_node(pos).name)
|
||||
end)
|
||||
|
||||
it("turns off multiconductors on placement", function()
|
||||
local pos = {x = 3, y = 0, z = 0}
|
||||
mesecon._test_place(pos, "mesecons:test_multiconductor_on")
|
||||
assert.equal("mesecons:test_multiconductor_off", world.get_node(pos).name)
|
||||
end)
|
||||
|
||||
it("triggers autoconnect hooks", function()
|
||||
mesecon._test_dig(layout[2][1])
|
||||
mineunit:execute_globalstep() -- Execute delayed hook
|
||||
assert.equal(1, #mesecon._test_autoconnects)
|
||||
|
||||
mesecon._test_place(layout[2][1], layout[2][2])
|
||||
assert.equal(2, #mesecon._test_autoconnects)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("overheating service", function()
|
||||
local layout = {
|
||||
{{x = 0, y = 0, z = 0}, "mesecons:test_receptor_off"},
|
||||
{{x = 1, y = 0, z = 0}, "mesecons:test_effector"},
|
||||
{{x = 2, y = 0, z = 0}, "mesecons:test_receptor_on"},
|
||||
}
|
||||
|
||||
before_each(function()
|
||||
world.layout(layout)
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
mesecon._test_reset()
|
||||
world.clear()
|
||||
end)
|
||||
|
||||
it("tracks heat", function()
|
||||
mesecon.do_overheat(layout[2][1])
|
||||
assert.equal(1, mesecon.get_heat(layout[2][1]))
|
||||
mesecon.do_cooldown(layout[2][1])
|
||||
assert.equal(0, mesecon.get_heat(layout[2][1]))
|
||||
end)
|
||||
|
||||
it("cools over time", function()
|
||||
mesecon.do_overheat(layout[2][1])
|
||||
assert.equal(1, mesecon.get_heat(layout[2][1]))
|
||||
mineunit:execute_globalstep(60)
|
||||
mineunit:execute_globalstep(60)
|
||||
mineunit:execute_globalstep(60)
|
||||
assert.equal(0, mesecon.get_heat(layout[2][1]))
|
||||
end)
|
||||
|
||||
it("tracks movement", function()
|
||||
local oldpos = layout[2][1]
|
||||
local pos = vector.offset(oldpos, 0, 1, 0)
|
||||
mesecon.do_overheat(oldpos)
|
||||
mesecon.move_hot_nodes({{pos = pos, oldpos = oldpos}})
|
||||
assert.equal(0, mesecon.get_heat(oldpos))
|
||||
assert.equal(1, mesecon.get_heat(pos))
|
||||
end)
|
||||
|
||||
it("causes overheating", function()
|
||||
-- Switch the first receptor on and off until it overheats/breaks a receptor.
|
||||
repeat
|
||||
if mesecon.flipstate(layout[1][1], minetest.get_node(layout[1][1])) == "on" then
|
||||
mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs)
|
||||
else
|
||||
mesecon.receptor_off(layout[1][1], mesecon.rules.alldirs)
|
||||
end
|
||||
mineunit:execute_globalstep(0) -- Execute receptor_on/receptor_off/activate/deactivate/change actions
|
||||
until minetest.get_node(layout[2][1]).name ~= "mesecons:test_effector"
|
||||
assert.same({"overheat", layout[2][1]}, mesecon._test_effector_events[#mesecon._test_effector_events])
|
||||
assert.equal(0, mesecon.get_heat(layout[2][1]))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("screwdriver service", function()
|
||||
local layout = {
|
||||
{{x = 0, y = 0, z = 0}, "mesecons:test_conductor_rot_on"},
|
||||
{{x = 1, y = 0, z = 0}, "mesecons:test_receptor_on"},
|
||||
{{x = -1, y = 0, z = 0}, "mesecons:test_conductor_on"},
|
||||
{{x = 0, y = 0, z = 1}, "mesecons:test_receptor_on"},
|
||||
{{x = 0, y = 0, z = -1}, "mesecons:test_conductor_off"},
|
||||
}
|
||||
|
||||
local function rotate(new_param2)
|
||||
local pos = layout[1][1]
|
||||
local node = world.get_node(pos)
|
||||
local on_rotate = minetest.registered_nodes[node.name].on_rotate
|
||||
on_rotate(pos, node, nil, screwdriver.ROTATE_FACE, new_param2)
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
world.layout(layout)
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
mesecon._test_reset()
|
||||
world.clear()
|
||||
end)
|
||||
|
||||
it("updates conductors", function()
|
||||
-- Rotate a conductor and see that the circuit state changes.
|
||||
rotate(1)
|
||||
mineunit:execute_globalstep() -- Execute receptor_off action
|
||||
assert.equal("mesecons:test_conductor_off", world.get_node(layout[3][1]).name)
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[5][1]).name)
|
||||
rotate(2)
|
||||
mineunit:execute_globalstep() -- Execute receptor_off action
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[3][1]).name)
|
||||
assert.equal("mesecons:test_conductor_off", world.get_node(layout[5][1]).name)
|
||||
rotate(3)
|
||||
mineunit:execute_globalstep() -- Execute receptor_off action
|
||||
assert.equal("mesecons:test_conductor_off", world.get_node(layout[3][1]).name)
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[5][1]).name)
|
||||
rotate(0)
|
||||
mineunit:execute_globalstep() -- Execute receptor_off action
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[3][1]).name)
|
||||
assert.equal("mesecons:test_conductor_off", world.get_node(layout[5][1]).name)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,147 @@
|
|||
require("mineunit")
|
||||
|
||||
fixture("mesecons")
|
||||
|
||||
describe("state", function()
|
||||
local layout = {
|
||||
{{x = 1, y = 0, z = 0}, "mesecons:test_receptor_off"},
|
||||
{{x = 0, y = 1, z = 0}, "mesecons:test_receptor_off"},
|
||||
{{x = 0, y = 0, z = 0}, "mesecons:test_conductor_off"},
|
||||
{{x = -1, y = 0, z = 0}, "mesecons:test_effector"},
|
||||
{{x = 2, y = 0, z = 0}, "mesecons:test_effector"},
|
||||
{{x = 0, y = -1, z = 0}, "mesecons:test_effector"},
|
||||
}
|
||||
|
||||
before_each(function()
|
||||
world.layout(layout)
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
mesecon._test_reset()
|
||||
world.clear()
|
||||
end)
|
||||
|
||||
it("turns on", function()
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
mineunit:execute_globalstep() -- Execute activate/change actions
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[3][1]).name)
|
||||
assert.equal(tonumber("10000001", 2), world.get_node(layout[4][1]).param2)
|
||||
assert.equal(tonumber("10000010", 2), world.get_node(layout[5][1]).param2)
|
||||
assert.equal(tonumber("10000100", 2), world.get_node(layout[6][1]).param2)
|
||||
|
||||
world.set_node(layout[2][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[2][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
mineunit:execute_globalstep() -- Execute activate/change actions
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[3][1]).name)
|
||||
assert.equal(tonumber("10000001", 2), world.get_node(layout[4][1]).param2)
|
||||
assert.equal(tonumber("10000010", 2), world.get_node(layout[5][1]).param2)
|
||||
assert.equal(tonumber("10000100", 2), world.get_node(layout[6][1]).param2)
|
||||
end)
|
||||
|
||||
it("turns off", function()
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_on")
|
||||
world.set_node(layout[2][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs)
|
||||
mesecon.receptor_on(layout[2][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on actions
|
||||
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_off")
|
||||
mesecon.receptor_off(layout[1][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_off and activate/change actions
|
||||
mineunit:execute_globalstep() -- Execute deactivate/change actions
|
||||
assert.equal("mesecons:test_conductor_on", world.get_node(layout[3][1]).name)
|
||||
assert.equal(tonumber("10000001", 2), world.get_node(layout[4][1]).param2)
|
||||
assert.equal(tonumber("00000000", 2), world.get_node(layout[5][1]).param2)
|
||||
assert.equal(tonumber("10000100", 2), world.get_node(layout[6][1]).param2)
|
||||
|
||||
world.set_node(layout[2][1], "mesecons:test_receptor_off")
|
||||
mesecon.receptor_off(layout[2][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_off action
|
||||
mineunit:execute_globalstep() -- Execute deactivate/change actions
|
||||
assert.equal("mesecons:test_conductor_off", world.get_node(layout[3][1]).name)
|
||||
assert.equal(tonumber("00000000", 2), world.get_node(layout[4][1]).param2)
|
||||
assert.equal(tonumber("00000000", 2), world.get_node(layout[5][1]).param2)
|
||||
assert.equal(tonumber("00000000", 2), world.get_node(layout[6][1]).param2)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("rotation", function()
|
||||
local layout = {
|
||||
{{x = 0, y = 0, z = 0}, "mesecons:test_receptor_off"},
|
||||
{{x = 1, y = 0, z = 0}, {name = "mesecons:test_conductor_rot_off", param2 = 0}},
|
||||
{{x = 0, y = 0, z = 1}, {name = "mesecons:test_conductor_rot_off", param2 = 1}},
|
||||
{{x = -1, y = 0, z = 0}, {name = "mesecons:test_conductor_rot_off", param2 = 2}},
|
||||
{{x = 0, y = 0, z = -1}, {name = "mesecons:test_conductor_rot_off", param2 = 3}},
|
||||
}
|
||||
|
||||
before_each(function()
|
||||
for _, entry in ipairs(layout) do
|
||||
world.set_node(entry[1], entry[2])
|
||||
end
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
mesecon._test_reset()
|
||||
world.clear()
|
||||
end)
|
||||
|
||||
it("works", function()
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
assert.equal("mesecons:test_conductor_rot_on", world.get_node(layout[2][1]).name)
|
||||
assert.equal("mesecons:test_conductor_rot_on", world.get_node(layout[3][1]).name)
|
||||
assert.equal("mesecons:test_conductor_rot_on", world.get_node(layout[4][1]).name)
|
||||
assert.equal("mesecons:test_conductor_rot_on", world.get_node(layout[5][1]).name)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("multiconductor", function()
|
||||
local layout = {
|
||||
{{x = 1, y = 0, z = 0}, "mesecons:test_receptor_off"},
|
||||
{{x = 0, y = 1, z = 0}, "mesecons:test_receptor_off"},
|
||||
{{x = 0, y = 0, z = 1}, "mesecons:test_receptor_off"},
|
||||
{{x = 0, y = 0, z = 0}, "mesecons:test_multiconductor_off"},
|
||||
}
|
||||
|
||||
before_each(function()
|
||||
world.layout(layout)
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
world.clear()
|
||||
mesecon._test_reset()
|
||||
end)
|
||||
|
||||
it("separates its subparts", function()
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
assert.equal("mesecons:test_multiconductor_001", world.get_node(layout[4][1]).name)
|
||||
|
||||
world.set_node(layout[2][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[2][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
assert.equal("mesecons:test_multiconductor_011", world.get_node(layout[4][1]).name)
|
||||
|
||||
world.set_node(layout[3][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[3][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
assert.equal("mesecons:test_multiconductor_on", world.get_node(layout[4][1]).name)
|
||||
end)
|
||||
|
||||
it("loops through itself", function()
|
||||
-- Make a loop.
|
||||
world.set_node({x = 0, y = -1, z = 0}, "mesecons:test_conductor_off")
|
||||
world.set_node({x = -1, y = -1, z = 0}, "mesecons:test_conductor_off")
|
||||
world.set_node({x = -1, y = 0, z = 0}, "mesecons:test_conductor_off")
|
||||
|
||||
world.set_node(layout[1][1], "mesecons:test_receptor_on")
|
||||
mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs)
|
||||
mineunit:execute_globalstep() -- Execute receptor_on action
|
||||
assert.equal("mesecons:test_multiconductor_101", world.get_node(layout[4][1]).name)
|
||||
end)
|
||||
end)
|
After Width: | Height: | Size: 191 B |
Before Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 487 B |
Before Width: | Height: | Size: 598 B |
Before Width: | Height: | Size: 692 B |
Before Width: | Height: | Size: 553 B |
After Width: | Height: | Size: 844 B |
After Width: | Height: | Size: 838 B |
After Width: | Height: | Size: 851 B |
After Width: | Height: | Size: 846 B |
After Width: | Height: | Size: 222 B |
After Width: | Height: | Size: 504 B |
Before Width: | Height: | Size: 867 B |
Before Width: | Height: | Size: 204 B After Width: | Height: | Size: 144 B |
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 377 B |
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 362 B |
|
@ -2,10 +2,99 @@ function mesecon.move_node(pos, newpos)
|
|||
local node = minetest.get_node(pos)
|
||||
local meta = minetest.get_meta(pos):to_table()
|
||||
minetest.remove_node(pos)
|
||||
minetest.add_node(newpos, node)
|
||||
minetest.set_node(newpos, node)
|
||||
minetest.get_meta(pos):from_table(meta)
|
||||
end
|
||||
|
||||
-- An on_rotate callback for mesecons components.
|
||||
function mesecon.on_rotate(pos, node, _, _, new_param2)
|
||||
local new_node = {name = node.name, param1 = node.param1, param2 = new_param2}
|
||||
minetest.swap_node(pos, new_node)
|
||||
mesecon.on_dignode(pos, node)
|
||||
mesecon.on_placenode(pos, new_node)
|
||||
minetest.check_for_falling(pos)
|
||||
return true
|
||||
end
|
||||
|
||||
-- An on_rotate callback for components which stay horizontal.
|
||||
function mesecon.on_rotate_horiz(pos, node, user, mode, new_param2)
|
||||
if not minetest.global_exists("screwdriver") or mode ~= screwdriver.ROTATE_FACE then
|
||||
return false
|
||||
end
|
||||
return mesecon.on_rotate(pos, node, user, mode, new_param2)
|
||||
end
|
||||
|
||||
-- Rules rotation Functions:
|
||||
function mesecon.rotate_rules_right(rules)
|
||||
local nr = {}
|
||||
for i, rule in ipairs(rules) do
|
||||
table.insert(nr, {
|
||||
x = -rule.z,
|
||||
y = rule.y,
|
||||
z = rule.x,
|
||||
name = rule.name})
|
||||
end
|
||||
return nr
|
||||
end
|
||||
|
||||
function mesecon.rotate_rules_left(rules)
|
||||
local nr = {}
|
||||
for i, rule in ipairs(rules) do
|
||||
table.insert(nr, {
|
||||
x = rule.z,
|
||||
y = rule.y,
|
||||
z = -rule.x,
|
||||
name = rule.name})
|
||||
end
|
||||
return nr
|
||||
end
|
||||
|
||||
function mesecon.rotate_rules_down(rules)
|
||||
local nr = {}
|
||||
for i, rule in ipairs(rules) do
|
||||
table.insert(nr, {
|
||||
x = -rule.y,
|
||||
y = rule.x,
|
||||
z = rule.z,
|
||||
name = rule.name})
|
||||
end
|
||||
return nr
|
||||
end
|
||||
|
||||
function mesecon.rotate_rules_up(rules)
|
||||
local nr = {}
|
||||
for i, rule in ipairs(rules) do
|
||||
table.insert(nr, {
|
||||
x = rule.y,
|
||||
y = -rule.x,
|
||||
z = rule.z,
|
||||
name = rule.name})
|
||||
end
|
||||
return nr
|
||||
end
|
||||
|
||||
-- Returns a rules getter function that returns different rules depending on the node's horizontal rotation.
|
||||
-- If param2 % 4 == 0, then the rules returned by the getter are a copy of base_rules.
|
||||
function mesecon.horiz_rules_getter(base_rules)
|
||||
local rotations = {mesecon.tablecopy(base_rules)}
|
||||
for i = 2, 4 do
|
||||
local right_rules = rotations[i - 1]
|
||||
if not right_rules[1] or right_rules[1].x then
|
||||
-- flat rules
|
||||
rotations[i] = mesecon.rotate_rules_left(right_rules)
|
||||
else
|
||||
-- not flat
|
||||
rotations[i] = {}
|
||||
for j, rules in ipairs(right_rules) do
|
||||
rotations[i][j] = mesecon.rotate_rules_left(rules)
|
||||
end
|
||||
end
|
||||
end
|
||||
return function(node)
|
||||
return rotations[node.param2 % 4 + 1]
|
||||
end
|
||||
end
|
||||
|
||||
function mesecon.flattenrules(allrules)
|
||||
--[[
|
||||
{
|
||||
|
@ -50,7 +139,7 @@ function mesecon.rule2bit(findrule, allrules)
|
|||
end
|
||||
for m,metarule in ipairs( allrules) do
|
||||
for _, rule in ipairs(metarule ) do
|
||||
if mesecon.cmpPos(findrule, rule) then
|
||||
if vector.equals(findrule, rule) then
|
||||
return m
|
||||
end
|
||||
end
|
||||
|
@ -69,7 +158,7 @@ function mesecon.rule2metaindex(findrule, allrules)
|
|||
|
||||
for m, metarule in ipairs( allrules) do
|
||||
for _, rule in ipairs(metarule ) do
|
||||
if mesecon.cmpPos(findrule, rule) then
|
||||
if vector.equals(findrule, rule) then
|
||||
return m
|
||||
end
|
||||
end
|
||||
|
@ -114,7 +203,9 @@ end
|
|||
|
||||
function mesecon.get_bit(binary,bit)
|
||||
bit = bit or 1
|
||||
local c = binary:len()-(bit-1)
|
||||
local len = binary:len()
|
||||
if bit > len then return false end
|
||||
local c = len-(bit-1)
|
||||
return binary:sub(c,c) == "1"
|
||||
end
|
||||
|
||||
|
@ -129,49 +220,43 @@ function mesecon.set_bit(binary,bit,value)
|
|||
end
|
||||
end
|
||||
return binary
|
||||
|
||||
|
||||
end
|
||||
|
||||
function mesecon.invertRule(r)
|
||||
return {x = -r.x, y = -r.y, z = -r.z}
|
||||
return vector.multiply(r, -1)
|
||||
end
|
||||
|
||||
function mesecon.addPosRule(p, r)
|
||||
return {x = p.x + r.x, y = p.y + r.y, z = p.z + r.z}
|
||||
end
|
||||
|
||||
function mesecon.cmpPos(p1, p2)
|
||||
return (p1.x == p2.x and p1.y == p2.y and p1.z == p2.z)
|
||||
end
|
||||
|
||||
function mesecon.tablecopy(table) -- deep table copy
|
||||
if type(table) ~= "table" then return table end -- no need to copy
|
||||
local newtable = {}
|
||||
|
||||
for idx, item in pairs(table) do
|
||||
if type(item) == "table" then
|
||||
newtable[idx] = mesecon.tablecopy(item)
|
||||
else
|
||||
newtable[idx] = item
|
||||
end
|
||||
function mesecon.tablecopy(obj) -- deep copy
|
||||
if type(obj) == "table" then
|
||||
return table.copy(obj)
|
||||
end
|
||||
|
||||
return newtable
|
||||
return obj
|
||||
end
|
||||
|
||||
-- Returns whether two values are equal.
|
||||
-- In tables, keys are compared for identity but values are compared recursively.
|
||||
-- There is no protection from infinite recursion.
|
||||
function mesecon.cmpAny(t1, t2)
|
||||
if type(t1) ~= type(t2) then return false end
|
||||
if type(t1) ~= "table" and type(t2) ~= "table" then return t1 == t2 end
|
||||
if type(t1) ~= "table" then return t1 == t2 end
|
||||
|
||||
-- Check that for each key of `t1` both tables have the same value
|
||||
for i, e in pairs(t1) do
|
||||
if not mesecon.cmpAny(e, t2[i]) then return false end
|
||||
end
|
||||
|
||||
-- Check that all keys of `t2` are also keys of `t1` so were checked in the previous loop
|
||||
for i, _ in pairs(t2) do
|
||||
if t1[i] == nil then return false end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- does not overwrite values; number keys (ipairs) are appended, not overwritten
|
||||
-- Deprecated. Use `merge_tables` or `merge_rule_sets` as appropriate.
|
||||
function mesecon.mergetable(source, dest)
|
||||
minetest.log("warning", debug.traceback("Deprecated call to mesecon.mergetable"))
|
||||
local rval = mesecon.tablecopy(dest)
|
||||
|
||||
for k, v in pairs(source) do
|
||||
|
@ -184,14 +269,41 @@ function mesecon.mergetable(source, dest)
|
|||
return rval
|
||||
end
|
||||
|
||||
-- Merges several rule sets in one. Order may not be preserved. Nil arguments
|
||||
-- are ignored.
|
||||
-- The rule sets must be of the same kind (either all single-level or all two-level).
|
||||
-- The function may be changed to normalize the resulting set in some way.
|
||||
function mesecon.merge_rule_sets(...)
|
||||
local rval = {}
|
||||
for _, t in pairs({...}) do -- ignores nils automatically
|
||||
table.insert_all(rval, mesecon.tablecopy(t))
|
||||
end
|
||||
return rval
|
||||
end
|
||||
|
||||
-- Merges two tables, with entries from `replacements` taking precedence over
|
||||
-- those from `base`. Returns the new table.
|
||||
-- Values are deep-copied from either table, keys are referenced.
|
||||
-- Numerical indices aren’t handled specially.
|
||||
function mesecon.merge_tables(base, replacements)
|
||||
local ret = mesecon.tablecopy(replacements) -- these are never overriden so have to be copied in any case
|
||||
for k, v in pairs(base) do
|
||||
if ret[k] == nil then -- it could be `false`
|
||||
ret[k] = mesecon.tablecopy(v)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function mesecon.register_node(name, spec_common, spec_off, spec_on)
|
||||
spec_common.drop = spec_common.drop or name .. "_off"
|
||||
spec_common.on_blast = spec_common.on_blast or mesecon.on_blastnode
|
||||
spec_common.__mesecon_basename = name
|
||||
spec_on.__mesecon_state = "on"
|
||||
spec_off.__mesecon_state = "off"
|
||||
|
||||
spec_on = mesecon.mergetable(spec_common, spec_on);
|
||||
spec_off = mesecon.mergetable(spec_common, spec_off);
|
||||
spec_on = mesecon.merge_tables(spec_common, spec_on);
|
||||
spec_off = mesecon.merge_tables(spec_common, spec_off);
|
||||
|
||||
minetest.register_node(name .. "_on", spec_on)
|
||||
minetest.register_node(name .. "_off", spec_off)
|
||||
|
@ -203,9 +315,209 @@ function mesecon.flipstate(pos, node)
|
|||
local newstate
|
||||
if (nodedef.__mesecon_state == "on") then newstate = "off" end
|
||||
if (nodedef.__mesecon_state == "off") then newstate = "on" end
|
||||
|
||||
|
||||
minetest.swap_node(pos, {name = nodedef.__mesecon_basename .. "_" .. newstate,
|
||||
param2 = node.param2})
|
||||
|
||||
return newstate
|
||||
end
|
||||
|
||||
-- File writing / reading utilities
|
||||
local wpath = minetest.get_worldpath()
|
||||
function mesecon.file2table(filename)
|
||||
local f = io.open(wpath.."/"..filename, "r")
|
||||
if f == nil then return {} end
|
||||
local t = f:read("*all")
|
||||
f:close()
|
||||
if t == "" or t == nil then return {} end
|
||||
return minetest.deserialize(t)
|
||||
end
|
||||
|
||||
function mesecon.table2file(filename, table)
|
||||
local f = io.open(wpath.."/"..filename, "w")
|
||||
f:write(minetest.serialize(table))
|
||||
f:close()
|
||||
end
|
||||
|
||||
-- Block position "hashing" (convert to integer) functions for voxelmanip cache
|
||||
local BLOCKSIZE = 16
|
||||
|
||||
-- convert node position --> block hash
|
||||
local function hash_blockpos(pos)
|
||||
return minetest.hash_node_position({
|
||||
x = math.floor(pos.x/BLOCKSIZE),
|
||||
y = math.floor(pos.y/BLOCKSIZE),
|
||||
z = math.floor(pos.z/BLOCKSIZE)
|
||||
})
|
||||
end
|
||||
|
||||
-- Maps from a hashed mapblock position (as returned by hash_blockpos) to a
|
||||
-- table.
|
||||
--
|
||||
-- Contents of the table are:
|
||||
-- “vm” → the VoxelManipulator
|
||||
-- “dirty” → true if data has been modified
|
||||
--
|
||||
-- Nil if no VM-based transaction is in progress.
|
||||
local vm_cache = nil
|
||||
|
||||
-- Cache from node position hashes to nodes (represented as tables).
|
||||
local vm_node_cache = nil
|
||||
|
||||
-- Whether the current transaction will need a light update afterward.
|
||||
local vm_update_light = false
|
||||
|
||||
-- Starts a VoxelManipulator-based transaction.
|
||||
--
|
||||
-- During a VM transaction, calls to vm_get_node and vm_swap_node operate on a
|
||||
-- cached copy of the world loaded via VoxelManipulators. That cache can later
|
||||
-- be committed to the real map by means of vm_commit or discarded by means of
|
||||
-- vm_abort.
|
||||
function mesecon.vm_begin()
|
||||
vm_cache = {}
|
||||
vm_node_cache = {}
|
||||
vm_update_light = false
|
||||
end
|
||||
|
||||
-- Finishes a VoxelManipulator-based transaction, freeing the VMs and map data
|
||||
-- and writing back any modified areas.
|
||||
function mesecon.vm_commit()
|
||||
for hash, tbl in pairs(vm_cache) do
|
||||
if tbl.dirty then
|
||||
local vm = tbl.vm
|
||||
vm:write_to_map(vm_update_light)
|
||||
vm:update_map()
|
||||
end
|
||||
end
|
||||
vm_cache = nil
|
||||
vm_node_cache = nil
|
||||
end
|
||||
|
||||
-- Finishes a VoxelManipulator-based transaction, freeing the VMs and throwing
|
||||
-- away any modified areas.
|
||||
function mesecon.vm_abort()
|
||||
vm_cache = nil
|
||||
vm_node_cache = nil
|
||||
end
|
||||
|
||||
-- Gets the cache entry covering a position, populating it if necessary.
|
||||
local function vm_get_or_create_entry(pos)
|
||||
local hash = hash_blockpos(pos)
|
||||
local tbl = vm_cache[hash]
|
||||
if not tbl then
|
||||
tbl = {vm = minetest.get_voxel_manip(pos, pos), dirty = false}
|
||||
vm_cache[hash] = tbl
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- Gets the node at a given position during a VoxelManipulator-based
|
||||
-- transaction.
|
||||
function mesecon.vm_get_node(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
local node = vm_node_cache[hash]
|
||||
if not node then
|
||||
node = vm_get_or_create_entry(pos).vm:get_node_at(pos)
|
||||
vm_node_cache[hash] = node
|
||||
end
|
||||
return node.name ~= "ignore" and {name = node.name, param1 = node.param1, param2 = node.param2} or nil
|
||||
end
|
||||
|
||||
-- Sets a node’s name during a VoxelManipulator-based transaction.
|
||||
--
|
||||
-- Existing param1, param2, and metadata are left alone.
|
||||
--
|
||||
-- The swap will necessitate a light update unless update_light equals false.
|
||||
function mesecon.vm_swap_node(pos, name, update_light)
|
||||
-- If one node needs a light update, all VMs should use light updates to
|
||||
-- prevent newly calculated light from being overwritten by other VMs.
|
||||
vm_update_light = vm_update_light or update_light ~= false
|
||||
|
||||
local tbl = vm_get_or_create_entry(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
local node = vm_node_cache[hash]
|
||||
if not node then
|
||||
node = tbl.vm:get_node_at(pos)
|
||||
vm_node_cache[hash] = node
|
||||
end
|
||||
node.name = name
|
||||
tbl.vm:set_node_at(pos, node)
|
||||
tbl.dirty = true
|
||||
end
|
||||
|
||||
-- Gets the node at a given position, regardless of whether it is loaded or
|
||||
-- not, respecting a transaction if one is in progress.
|
||||
--
|
||||
-- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
|
||||
-- the server’s main map data cache and then accessed from there.
|
||||
--
|
||||
-- Inside a VM transaction, the transaction’s VM cache is used.
|
||||
function mesecon.get_node_force(pos)
|
||||
if vm_cache then
|
||||
return mesecon.vm_get_node(pos)
|
||||
else
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node == nil then
|
||||
-- Node is not currently loaded; use a VoxelManipulator to prime
|
||||
-- the mapblock cache and try again.
|
||||
minetest.get_voxel_manip(pos, pos)
|
||||
node = minetest.get_node_or_nil(pos)
|
||||
end
|
||||
return node
|
||||
end
|
||||
end
|
||||
|
||||
-- Swaps the node at a given position, regardless of whether it is loaded or
|
||||
-- not, respecting a transaction if one is in progress.
|
||||
--
|
||||
-- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
|
||||
-- the server’s main map data cache and then accessed from there.
|
||||
--
|
||||
-- Inside a VM transaction, the transaction’s VM cache is used.
|
||||
--
|
||||
-- This function can only be used to change the node’s name, not its parameters
|
||||
-- or metadata.
|
||||
--
|
||||
-- The swap will necessitate a light update unless update_light equals false.
|
||||
function mesecon.swap_node_force(pos, name, update_light)
|
||||
if vm_cache then
|
||||
return mesecon.vm_swap_node(pos, name, update_light)
|
||||
else
|
||||
-- This serves to both ensure the mapblock is loaded and also hand us
|
||||
-- the old node table so we can preserve param2.
|
||||
local node = mesecon.get_node_force(pos)
|
||||
node.name = name
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
end
|
||||
|
||||
-- Autoconnect Hooks
|
||||
-- Nodes like conductors may change their appearance and their connection rules
|
||||
-- right after being placed or after being dug, e.g. the default wires use this
|
||||
-- to automatically connect to linking nodes after placement.
|
||||
-- After placement, the update function will be executed immediately so that the
|
||||
-- possibly changed rules can be taken into account when recalculating the circuit.
|
||||
-- After digging, the update function will be queued and executed after
|
||||
-- recalculating the circuit. The update function must take care of updating the
|
||||
-- node at the given position itself, but also all of the other nodes the given
|
||||
-- position may have (had) a linking connection to.
|
||||
mesecon.autoconnect_hooks = {}
|
||||
|
||||
-- name: A unique name for the hook, e.g. "foowire". Used to name the actionqueue function.
|
||||
-- fct: The update function with parameters function(pos, node)
|
||||
function mesecon.register_autoconnect_hook(name, fct)
|
||||
mesecon.autoconnect_hooks[name] = fct
|
||||
mesecon.queue:add_function("autoconnect_hook_"..name, fct)
|
||||
end
|
||||
|
||||
function mesecon.execute_autoconnect_hooks_now(pos, node)
|
||||
for _, fct in pairs(mesecon.autoconnect_hooks) do
|
||||
fct(pos, node)
|
||||
end
|
||||
end
|
||||
|
||||
function mesecon.execute_autoconnect_hooks_queue(pos, node)
|
||||
for name in pairs(mesecon.autoconnect_hooks) do
|
||||
mesecon.queue:add_action(pos, "autoconnect_hook_"..name, {node})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
mesecons
|
|
@ -0,0 +1,2 @@
|
|||
name = mesecons_alias
|
||||
depends = mesecons
|
|
@ -1 +0,0 @@
|
|||
mesecons
|
|
@ -0,0 +1,2 @@
|
|||
The blinky plants toggles between on and off state every three seconds. Can be used to make clocks. Also works after having restarted the game.
|
||||
It stops blinking in an inactive block, then starts again when the block becomes active.
|
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 1.7 KiB |
|
@ -1,5 +1,7 @@
|
|||
-- The BLINKY_PLANT
|
||||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
local toggle_timer = function (pos)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
if timer:is_started() then
|
||||
|
@ -20,18 +22,25 @@ local on_timer = function (pos)
|
|||
end
|
||||
|
||||
mesecon.register_node("mesecons_blinkyplant:blinky_plant", {
|
||||
description="Blinky Plant",
|
||||
description= S("Blinky Plant"),
|
||||
drawtype = "plantlike",
|
||||
inventory_image = "jeija_blinky_plant_off.png",
|
||||
paramtype = "light",
|
||||
is_ground_content = false,
|
||||
walkable = false,
|
||||
sounds = default.node_sound_leaves_defaults(),
|
||||
sounds = mesecon.node_sound.leaves,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.3, -0.5, -0.3, 0.3, -0.5+0.7, 0.3},
|
||||
},
|
||||
on_timer = on_timer,
|
||||
on_rightclick = toggle_timer,
|
||||
on_rightclick = function(pos, _, clicker)
|
||||
if minetest.is_protected(pos, clicker and clicker:get_player_name() or "") then
|
||||
return
|
||||
end
|
||||
|
||||
toggle_timer(pos)
|
||||
end,
|
||||
on_construct = toggle_timer
|
||||
},{
|
||||
tiles = {"jeija_blinky_plant_off.png"},
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# textdomain: mesecons_blinkyplant
|
||||
Blinky Plant=Blinkpflanze
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: mesecons_blinkyplant
|
||||
|
||||
### init.lua ###
|
||||
Blinky Plant=Palpebruma Planto
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: mesecons_blinkyplant
|
||||
|
||||
### init.lua ###
|
||||
Blinky Plant=
|
|
@ -0,0 +1,2 @@
|
|||
name = mesecons_blinkyplant
|
||||
depends = mesecons, mesecons_gamecompat
|
Before Width: | Height: | Size: 454 B After Width: | Height: | Size: 367 B |
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 394 B |
|
@ -1,2 +0,0 @@
|
|||
mesecons
|
||||
mesecons_receiver
|
|
@ -0,0 +1 @@
|
|||
This receptor can be attached to walls. It turns on for 1 second if it's punched.
|
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 4.3 KiB |
|
@ -1,56 +1,64 @@
|
|||
-- WALL BUTTON
|
||||
-- A button that when pressed emits power for 1 second
|
||||
-- and then turns off again
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
mesecon.button_turnoff = function (pos)
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name=="mesecons_button:button_on" then --has not been dug
|
||||
minetest.swap_node(pos, {name = "mesecons_button:button_off", param2=node.param2})
|
||||
minetest.sound_play("mesecons_button_pop", {pos=pos})
|
||||
local rules = mesecon.rules.buttonlike_get(node)
|
||||
mesecon.receptor_off(pos, rules)
|
||||
if node.name ~= "mesecons_button:button_on" then -- has been dug
|
||||
return
|
||||
end
|
||||
minetest.swap_node(pos, {name = "mesecons_button:button_off", param2 = node.param2})
|
||||
minetest.sound_play("mesecons_button_pop", { pos = pos }, true)
|
||||
local rules = mesecon.rules.buttonlike_get(node)
|
||||
mesecon.receptor_off(pos, rules)
|
||||
end
|
||||
|
||||
local use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or nil
|
||||
|
||||
minetest.register_node("mesecons_button:button_off", {
|
||||
drawtype = "nodebox",
|
||||
tiles = {
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_off.png"
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_off.png"
|
||||
},
|
||||
use_texture_alpha = use_texture_alpha,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
legacy_wallmounted = true,
|
||||
walkable = false,
|
||||
on_rotate = mesecon.buttonlike_onrotate,
|
||||
sunlight_propagates = true,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = { -6/16, -6/16, 5/16, 6/16, 6/16, 8/16 }
|
||||
},
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -6/16, -6/16, 6/16, 6/16, 6/16, 8/16 }, -- the thin plate behind the button
|
||||
{ -4/16, -2/16, 4/16, 4/16, 2/16, 6/16 } -- the button itself
|
||||
}
|
||||
},
|
||||
groups = {dig_immediate=2, mesecon_needs_receiver = 1},
|
||||
description = "Button",
|
||||
on_punch = function (pos, node)
|
||||
description = S("Button"),
|
||||
on_rightclick = function (pos, node)
|
||||
minetest.swap_node(pos, {name = "mesecons_button:button_on", param2=node.param2})
|
||||
mesecon.receptor_on(pos, mesecon.rules.buttonlike_get(node))
|
||||
minetest.sound_play("mesecons_button_push", {pos=pos})
|
||||
minetest.after(1, mesecon.button_turnoff, pos)
|
||||
minetest.sound_play("mesecons_button_push", { pos = pos }, true)
|
||||
minetest.get_node_timer(pos):start(1)
|
||||
end,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sounds = mesecon.node_sound.stone,
|
||||
mesecons = {receptor = {
|
||||
state = mesecon.state.off,
|
||||
rules = mesecon.rules.buttonlike_get
|
||||
}}
|
||||
}},
|
||||
on_blast = mesecon.on_blastnode,
|
||||
})
|
||||
|
||||
minetest.register_node("mesecons_button:button_on", {
|
||||
|
@ -62,12 +70,15 @@ minetest.register_node("mesecons_button:button_on", {
|
|||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_sides.png",
|
||||
"jeija_wall_button_on.png"
|
||||
},
|
||||
},
|
||||
use_texture_alpha = use_texture_alpha,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
legacy_wallmounted = true,
|
||||
walkable = false,
|
||||
light_source = LIGHT_MAX-7,
|
||||
on_rotate = false,
|
||||
light_source = minetest.LIGHT_MAX-7,
|
||||
sunlight_propagates = true,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
|
@ -82,17 +93,19 @@ minetest.register_node("mesecons_button:button_on", {
|
|||
},
|
||||
groups = {dig_immediate=2, not_in_creative_inventory=1, mesecon_needs_receiver = 1},
|
||||
drop = 'mesecons_button:button_off',
|
||||
description = "Button",
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
description = S("Button"),
|
||||
sounds = mesecon.node_sound.stone,
|
||||
mesecons = {receptor = {
|
||||
state = mesecon.state.on,
|
||||
rules = mesecon.rules.buttonlike_get
|
||||
}}
|
||||
}},
|
||||
on_timer = mesecon.button_turnoff,
|
||||
on_blast = mesecon.on_blastnode,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mesecons_button:button_off 2",
|
||||
recipe = {
|
||||
{"group:mesecon_conductor_craftable","default:stone"},
|
||||
{"group:mesecon_conductor_craftable","mesecons_gamecompat:stone"},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# textdomain: mesecons_button
|
||||
Button=Taster
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: mesecons_button
|
||||
|
||||
### init.lua ###
|
||||
Button=Butono
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: mesecons_button
|
||||
|
||||
### init.lua ###
|
||||
Button=
|
|
@ -0,0 +1,2 @@
|
|||
name = mesecons_button
|
||||
depends = mesecons, mesecons_gamecompat, mesecons_receiver
|
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 371 B |
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 409 B |
Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 220 B |
|
@ -1 +0,0 @@
|
|||
mesecons
|
|
@ -0,0 +1,2 @@
|
|||
There is no crafting recipe as this should only be available for server admins. Quite similar to the Minecraft counterpart. Executes server commands.
|
||||
It works in inactive blocks.
|
After Width: | Height: | Size: 33 KiB |
|
@ -1,195 +1,216 @@
|
|||
minetest.register_chatcommand("say", {
|
||||
params = "<text>",
|
||||
description = "Say <text> as the server",
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
minetest.chat_send_all(name .. ": " .. param)
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("tell", {
|
||||
params = "<name> <text>",
|
||||
description = "Say <text> to <name> privately",
|
||||
func = function(name, param)
|
||||
local found, _, target, message = param:find("^([^%s]+)%s+(.*)$")
|
||||
if found == nil then
|
||||
minetest.chat_send_player(name, "Invalid usage: " .. param)
|
||||
return
|
||||
end
|
||||
if not minetest.get_player_by_name(target) then
|
||||
minetest.chat_send_player(name, "Invalid target: " .. target)
|
||||
end
|
||||
minetest.chat_send_player(target, name .. " whispers: " .. message, false)
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("hp", {
|
||||
params = "<name> <value>",
|
||||
description = "Set health of <name> to <value> hitpoints",
|
||||
privs = {ban=true},
|
||||
func = function(name, param)
|
||||
local found, _, target, value = param:find("^([^%s]+)%s+(%d+)$")
|
||||
if found == nil then
|
||||
minetest.chat_send_player(name, "Invalid usage: " .. param)
|
||||
return
|
||||
end
|
||||
local player = minetest.get_player_by_name(target)
|
||||
if player then
|
||||
player:set_hp(value)
|
||||
else
|
||||
minetest.chat_send_player(name, "Invalid target: " .. target)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
local function initialize_data(meta)
|
||||
local commands = meta:get_string("commands")
|
||||
meta:set_string("formspec",
|
||||
"invsize[9,5;]" ..
|
||||
"textarea[0.5,0.5;8.5,4;commands;Commands;"..commands.."]" ..
|
||||
"label[1,3.8;@nearest, @farthest, and @random are replaced by the respective player names]" ..
|
||||
"button_exit[3.3,4.5;2,1;submit;Submit]")
|
||||
local owner = meta:get_string("owner")
|
||||
if owner == "" then
|
||||
owner = "not owned"
|
||||
else
|
||||
owner = "owned by " .. owner
|
||||
end
|
||||
meta:set_string("infotext", "Command Block\n" ..
|
||||
"(" .. owner .. ")\n" ..
|
||||
"Commands: "..commands)
|
||||
end
|
||||
|
||||
local function construct(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
meta:set_string("commands", "tell @nearest Commandblock unconfigured")
|
||||
|
||||
meta:set_string("owner", "")
|
||||
|
||||
initialize_data(meta)
|
||||
end
|
||||
|
||||
local function after_place(pos, placer)
|
||||
if placer then
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
initialize_data(meta)
|
||||
end
|
||||
end
|
||||
|
||||
local function receive_fields(pos, formname, fields, sender)
|
||||
if not fields.submit then
|
||||
return
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
if owner ~= "" and sender:get_player_name() ~= owner then
|
||||
return
|
||||
end
|
||||
meta:set_string("commands", fields.commands)
|
||||
|
||||
initialize_data(meta)
|
||||
end
|
||||
|
||||
local function resolve_commands(commands, pos)
|
||||
local nearest, farthest = nil, nil
|
||||
local min_distance, max_distance = math.huge, -1
|
||||
local players = minetest.get_connected_players()
|
||||
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
|
||||
if distance > max_distance then
|
||||
max_distance = distance
|
||||
farthest = player:get_player_name()
|
||||
end
|
||||
end
|
||||
local random = players[math.random(#players)]:get_player_name()
|
||||
commands = commands:gsub("@nearest", nearest)
|
||||
commands = commands:gsub("@farthest", farthest)
|
||||
commands = commands:gsub("@random", random)
|
||||
return commands
|
||||
end
|
||||
|
||||
local function commandblock_action_on(pos, node)
|
||||
if node.name ~= "mesecons_commandblock:commandblock_off" then
|
||||
return
|
||||
end
|
||||
|
||||
minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_on"})
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
if owner == "" then
|
||||
return
|
||||
end
|
||||
|
||||
local commands = resolve_commands(meta:get_string("commands"), pos)
|
||||
for _, command in pairs(commands:split("\n")) do
|
||||
local pos = command:find(" ")
|
||||
local cmd, param = command, ""
|
||||
if pos then
|
||||
cmd = command:sub(1, pos - 1)
|
||||
param = command:sub(pos + 1)
|
||||
end
|
||||
local cmddef = minetest.chatcommands[cmd]
|
||||
if not cmddef then
|
||||
minetest.chat_send_player(owner, "The command "..cmd.." does not exist")
|
||||
return
|
||||
end
|
||||
local has_privs, missing_privs = minetest.check_player_privs(owner, cmddef.privs)
|
||||
if not has_privs then
|
||||
minetest.chat_send_player(owner, "You don't have permission "
|
||||
.."to run "..cmd
|
||||
.." (missing privileges: "
|
||||
..table.concat(missing_privs, ", ")..")")
|
||||
return
|
||||
end
|
||||
cmddef.func(owner, param)
|
||||
end
|
||||
end
|
||||
|
||||
local function commandblock_action_off(pos, node)
|
||||
if node.name == "mesecons_commandblock:commandblock_on" then
|
||||
minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_off"})
|
||||
end
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
return owner == "" or owner == player:get_player_name()
|
||||
end
|
||||
|
||||
minetest.register_node("mesecons_commandblock:commandblock_off", {
|
||||
description = "Command Block",
|
||||
tiles = {"jeija_commandblock_off.png"},
|
||||
inventory_image = minetest.inventorycube("jeija_commandblock_off.png"),
|
||||
groups = {cracky=2, mesecon_effector_off=1},
|
||||
on_construct = construct,
|
||||
after_place_node = after_place,
|
||||
on_receive_fields = receive_fields,
|
||||
can_dig = can_dig,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
mesecons = {effector = {
|
||||
action_on = commandblock_action_on
|
||||
}}
|
||||
})
|
||||
|
||||
minetest.register_node("mesecons_commandblock:commandblock_on", {
|
||||
tiles = {"jeija_commandblock_on.png"},
|
||||
groups = {cracky=2, mesecon_effector_on=1, not_in_creative_inventory=1},
|
||||
light_source = 10,
|
||||
drop = "mesecons_commandblock:commandblock_off",
|
||||
on_construct = construct,
|
||||
after_place_node = after_place,
|
||||
on_receive_fields = receive_fields,
|
||||
can_dig = can_dig,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
mesecons = {effector = {
|
||||
action_off = commandblock_action_off
|
||||
}}
|
||||
})
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
minetest.register_chatcommand("say", {
|
||||
params = "<text>",
|
||||
description = S("Say <text> as the server"),
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
minetest.chat_send_all(name .. ": " .. param)
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("tell", {
|
||||
params = "<name> <text>",
|
||||
description = S("Say <text> to <name> privately"),
|
||||
privs = {shout=true},
|
||||
func = function(name, param)
|
||||
local found, _, target, message = param:find("^([^%s]+)%s+(.*)$")
|
||||
if found == nil then
|
||||
minetest.chat_send_player(name, "Invalid usage: " .. param)
|
||||
return
|
||||
end
|
||||
if not minetest.get_player_by_name(target) then
|
||||
minetest.chat_send_player(name, "Invalid target: " .. target)
|
||||
end
|
||||
minetest.chat_send_player(target, name .. " whispers: " .. message, false)
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("hp", {
|
||||
params = "<name> <value>",
|
||||
description = S("Set health of <name> to <value> hitpoints"),
|
||||
privs = {ban=true},
|
||||
func = function(name, param)
|
||||
local found, _, target, value = param:find("^([^%s]+)%s+(%d+)$")
|
||||
if found == nil then
|
||||
minetest.chat_send_player(name, "Invalid usage: " .. param)
|
||||
return
|
||||
end
|
||||
local player = minetest.get_player_by_name(target)
|
||||
if player then
|
||||
player:set_hp(value)
|
||||
else
|
||||
minetest.chat_send_player(name, "Invalid target: " .. target)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
local function initialize_data(meta)
|
||||
local commands = minetest.formspec_escape(meta:get_string("commands"))
|
||||
meta:set_string("formspec",
|
||||
"invsize[9,5;]" ..
|
||||
"textarea[0.5,0.5;8.5,4;commands;Commands;"..commands.."]" ..
|
||||
"label[1,3.8;@nearest, @farthest, and @random are replaced by the respective player names]" ..
|
||||
"button_exit[3.3,4.5;2,1;submit;Submit]")
|
||||
local owner = meta:get_string("owner")
|
||||
if owner == "" then
|
||||
owner = "not owned"
|
||||
else
|
||||
owner = "owned by " .. owner
|
||||
end
|
||||
meta:set_string("infotext", "Command Block\n" ..
|
||||
"(" .. owner .. ")\n" ..
|
||||
"Commands: "..commands)
|
||||
end
|
||||
|
||||
local function construct(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
meta:set_string("commands", "tell @nearest Commandblock unconfigured")
|
||||
|
||||
meta:set_string("owner", "")
|
||||
|
||||
initialize_data(meta)
|
||||
end
|
||||
|
||||
local function after_place(pos, placer)
|
||||
if placer then
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
initialize_data(meta)
|
||||
end
|
||||
end
|
||||
|
||||
local function receive_fields(pos, _, fields, sender)
|
||||
if not fields.submit then
|
||||
return
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
if owner ~= "" and sender:get_player_name() ~= owner then
|
||||
return
|
||||
end
|
||||
meta:set_string("commands", fields.commands)
|
||||
|
||||
initialize_data(meta)
|
||||
end
|
||||
|
||||
local function resolve_commands(commands, pos)
|
||||
local players = minetest.get_connected_players()
|
||||
|
||||
-- No players online: remove all commands containing
|
||||
-- @nearest, @farthest and @random
|
||||
if #players == 0 then
|
||||
commands = commands:gsub("[^\r\n]+", function (line)
|
||||
if line:find("@nearest") then return "" end
|
||||
if line:find("@farthest") then return "" end
|
||||
if line:find("@random") then return "" end
|
||||
return line
|
||||
end)
|
||||
return commands
|
||||
end
|
||||
|
||||
local nearest, farthest = nil, nil
|
||||
local min_distance, max_distance = math.huge, -1
|
||||
for index, player in pairs(players) do
|
||||
local distance = vector.distance(pos, player:get_pos())
|
||||
if distance < min_distance then
|
||||
min_distance = distance
|
||||
nearest = player:get_player_name()
|
||||
end
|
||||
if distance > max_distance then
|
||||
max_distance = distance
|
||||
farthest = player:get_player_name()
|
||||
end
|
||||
end
|
||||
local random = players[math.random(#players)]:get_player_name()
|
||||
commands = commands:gsub("@nearest", nearest)
|
||||
commands = commands:gsub("@farthest", farthest)
|
||||
commands = commands:gsub("@random", random)
|
||||
return commands
|
||||
end
|
||||
|
||||
local function commandblock_action_on(pos, node)
|
||||
if node.name ~= "mesecons_commandblock:commandblock_off" then
|
||||
return
|
||||
end
|
||||
|
||||
minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_on"})
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
if owner == "" then
|
||||
return
|
||||
end
|
||||
|
||||
local commands = resolve_commands(meta:get_string("commands"), pos)
|
||||
for _, command in pairs(commands:split("\n")) do
|
||||
local pos = command:find(" ")
|
||||
local cmd, param = command, ""
|
||||
if pos then
|
||||
cmd = command:sub(1, pos - 1)
|
||||
param = command:sub(pos + 1)
|
||||
end
|
||||
local cmddef = minetest.chatcommands[cmd]
|
||||
if not cmddef then
|
||||
minetest.chat_send_player(owner, "The command "..cmd.." does not exist")
|
||||
return
|
||||
end
|
||||
local has_privs, missing_privs = minetest.check_player_privs(owner, cmddef.privs)
|
||||
if not has_privs then
|
||||
minetest.chat_send_player(owner, "You don't have permission "
|
||||
.."to run "..cmd
|
||||
.." (missing privileges: "
|
||||
..table.concat(missing_privs, ", ")..")")
|
||||
return
|
||||
end
|
||||
cmddef.func(owner, param)
|
||||
end
|
||||
end
|
||||
|
||||
local function commandblock_action_off(pos, node)
|
||||
if node.name == "mesecons_commandblock:commandblock_on" then
|
||||
minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_off"})
|
||||
end
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
return owner == "" or owner == player:get_player_name() or
|
||||
minetest.check_player_privs(player, "protection_bypass")
|
||||
end
|
||||
|
||||
minetest.register_node("mesecons_commandblock:commandblock_off", {
|
||||
description = S("Command Block"),
|
||||
tiles = {"jeija_commandblock_off.png"},
|
||||
inventory_image = minetest.inventorycube("jeija_commandblock_off.png"),
|
||||
is_ground_content = false,
|
||||
groups = {cracky=2, mesecon_effector_off=1},
|
||||
on_construct = construct,
|
||||
after_place_node = after_place,
|
||||
on_receive_fields = receive_fields,
|
||||
can_dig = can_dig,
|
||||
sounds = mesecon.node_sound.stone,
|
||||
mesecons = {effector = {
|
||||
action_on = commandblock_action_on
|
||||
}},
|
||||
on_blast = mesecon.on_blastnode,
|
||||
})
|
||||
|
||||
minetest.register_node("mesecons_commandblock:commandblock_on", {
|
||||
tiles = {"jeija_commandblock_on.png"},
|
||||
is_ground_content = false,
|
||||
groups = {cracky=2, mesecon_effector_on=1, not_in_creative_inventory=1},
|
||||
light_source = 10,
|
||||
drop = "mesecons_commandblock:commandblock_off",
|
||||
on_construct = construct,
|
||||
after_place_node = after_place,
|
||||
on_receive_fields = receive_fields,
|
||||
can_dig = can_dig,
|
||||
sounds = mesecon.node_sound.stone,
|
||||
mesecons = {effector = {
|
||||
action_off = commandblock_action_off
|
||||
}},
|
||||
on_blast = mesecon.on_blastnode,
|
||||
})
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# textdomain: mesecons_commandblock
|
||||
Say <text> as the server=<Text> als Server sagen
|
||||
Say <text> to <name> privately=<Text> an <Name> privat senden
|
||||
Set health of <name> to <value> hitpoints=Gesundheit von <Name> auf <Wert> Trefferpunkte setzen
|
||||
Command Block=Befehlsblock
|
|
@ -0,0 +1,7 @@
|
|||
# textdomain: mesecons_commandblock
|
||||
|
||||
### init.lua ###
|
||||
Say <text> as the server=Diru <teksto> kiel la servilo
|
||||
Say <text> to <name> privately=Diru <teksto> al <nomo> private
|
||||
Set health of <name> to <value> hitpoints=Agordu sanon de <nomo> al <valoro>
|
||||
Command Block=Komando-Bloko
|
|
@ -0,0 +1,7 @@
|
|||
# textdomain: mesecons_commandblock
|
||||
|
||||
### init.lua ###
|
||||
Say <text> as the server=
|
||||
Say <text> to <name> privately=
|
||||
Set health of <name> to <value> hitpoints=
|
||||
Command Block=
|
|
@ -0,0 +1,2 @@
|
|||
name = mesecons_commandblock
|
||||
depends = mesecons, mesecons_gamecompat
|
Before Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 183 B |
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 183 B |
|
@ -1 +0,0 @@
|
|||
mesecons
|
|
@ -0,0 +1 @@
|
|||
The delayer delays the signal from the input for a determined time. The time can be set by punching the delayer. Possible delays are: 0.1 seconds, 0.3 seconds, 0.5 seconds and 1 second. You may try to use it for creating songs with the noteblock. It works in unloaded blocks.
|
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 4.2 KiB |
|
@ -1,19 +1,9 @@
|
|||
-- Function that get the input/output rules of the delayer
|
||||
local delayer_get_output_rules = function(node)
|
||||
local rules = {{x = 0, y = 0, z = 1}}
|
||||
for i = 0, node.param2 do
|
||||
rules = mesecon.rotate_rules_left(rules)
|
||||
end
|
||||
return rules
|
||||
end
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
local delayer_get_input_rules = function(node)
|
||||
local rules = {{x = 0, y = 0, z = -1}}
|
||||
for i = 0, node.param2 do
|
||||
rules = mesecon.rotate_rules_left(rules)
|
||||
end
|
||||
return rules
|
||||
end
|
||||
-- Function that get the input/output rules of the delayer
|
||||
local delayer_get_output_rules = mesecon.horiz_rules_getter({{x = 1, y = 0, z = 0}})
|
||||
|
||||
local delayer_get_input_rules = mesecon.horiz_rules_getter({{x = -1, y = 0, z = 0}})
|
||||
|
||||
-- Functions that are called after the delay time
|
||||
|
||||
|
@ -33,21 +23,12 @@ 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
|
||||
|
||||
boxes = {{ -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab
|
||||
local boxes = {
|
||||
{ -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab
|
||||
|
||||
{ -2/16, -7/16, -4/16, 2/16, -26/64, -3/16 }, -- the jeweled "on" indicator
|
||||
{ -3/16, -7/16, -3/16, 3/16, -26/64, -2/16 },
|
||||
|
@ -57,21 +38,13 @@ boxes = {{ -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab
|
|||
|
||||
{ -6/16, -7/16, -6/16, -4/16, -27/64, -4/16 }, -- the timer indicator
|
||||
{ -8/16, -8/16, -1/16, -6/16, -7/16, 1/16 }, -- the two wire stubs
|
||||
{ 6/16, -8/16, -1/16, 8/16, -7/16, 1/16 }}
|
||||
{ 6/16, -8/16, -1/16, 8/16, -7/16, 1/16 }
|
||||
}
|
||||
|
||||
minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
|
||||
description = "Delayer",
|
||||
-- Delayer definition defaults
|
||||
local def = {
|
||||
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",
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or nil,
|
||||
walkable = true,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
|
@ -81,26 +54,46 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
|
|||
type = "fixed",
|
||||
fixed = boxes
|
||||
},
|
||||
groups = groups,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = true,
|
||||
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})
|
||||
is_ground_content = false,
|
||||
delayer_time = delaytime[i],
|
||||
sounds = mesecon.node_sound.stone,
|
||||
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 = S("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() or "") then
|
||||
return
|
||||
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 =
|
||||
{
|
||||
|
@ -112,13 +105,16 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
|
|||
rules = delayer_get_input_rules,
|
||||
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)
|
||||
|
||||
|
||||
minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
|
||||
description = "You hacker you",
|
||||
drawtype = "nodebox",
|
||||
-- Activated delayer definition defaults
|
||||
local on_state = {
|
||||
description = S("You hacker you"),
|
||||
tiles = {
|
||||
"mesecons_delayer_on_"..tostring(i)..".png",
|
||||
"mesecons_delayer_bottom.png",
|
||||
|
@ -126,34 +122,18 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
|
|||
"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},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = true,
|
||||
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})
|
||||
on_punch = function(pos, node, puncher)
|
||||
if minetest.is_protected(pos, puncher and puncher:get_player_name() or "") then
|
||||
return
|
||||
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),
|
||||
mesecons = {
|
||||
receptor =
|
||||
|
@ -166,14 +146,19 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
|
|||
rules = delayer_get_input_rules,
|
||||
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)
|
||||
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mesecons_delayer:delayer_off_1",
|
||||
recipe = {
|
||||
{"mesecons_torch:mesecon_torch_on", "group:mesecon_conductor_craftable", "mesecons_torch:mesecon_torch_on"},
|
||||
{"default:cobble","default:cobble", "default:cobble"},
|
||||
{"mesecons_gamecompat:cobble","mesecons_gamecompat:cobble", "mesecons_gamecompat:cobble"},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# textdomain: mesecons_delayer
|
||||
Delayer=Verzögerer
|
||||
You hacker you=Du Hacker, Du
|
|
@ -0,0 +1,5 @@
|
|||
# textdomain: mesecons_delayer
|
||||
|
||||
### init.lua ###
|
||||
Delayer=Prokrasto
|
||||
You hacker you=Vi hakisto
|
|
@ -0,0 +1,5 @@
|
|||
# textdomain: mesecons_delayer
|
||||
|
||||
### init.lua ###
|
||||
Delayer=
|
||||
You hacker you=
|
|
@ -0,0 +1,2 @@
|
|||
name = mesecons_delayer
|
||||
depends = mesecons, mesecons_gamecompat
|
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 221 B |
Before Width: | Height: | Size: 226 B After Width: | Height: | Size: 176 B |
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 187 B |
Before Width: | Height: | Size: 562 B After Width: | Height: | Size: 448 B |
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 449 B |