---define the class
+local
+minetest,vector,math,table,pairs
+=
+minetest,vector,math,table,pairs
+
+-- minetest class
+local get_node = minetest.get_node
+local get_item_group = minetest.get_item_group
+local get_meta = minetest.get_meta
+local facedir_to_dir = minetest.facedir_to_dir
+local content_id = minetest.get_name_from_content_id
+local get_content_id = minetest.get_content_id
+local get_voxel_manip = minetest.get_voxel_manip
+local after = minetest.after
+local registered_nodes
+minetest.register_on_mods_loaded(function()
+ registered_nodes = minetest.registered_nodes
+end)
+
+-- math class
+local abs = math.abs
+local floor = math.floor
+
+-- vector library
+local new_vec = vector.new
+local add_vec = vector.add
+local sub_vec = vector.subtract
+local vector_distance = vector.distance
+
+--this is written out manually so that
+--math.abs is not needed
+local order = {
+ {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
+ {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
+
+ {x=0, y=1, z=0}, {x= 0, 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=1, y=-1, z=0}, {x=-1, y=-1, z= 0},
+ {x=0, y=-1, z=1}, {x= 0, y=-1, z=-1},
+}
+
+-- redstone class
redstone = {}
+
+local speed_test
+
local r_index = {}
+local a_index = {}
+local check_table = {}
local path = minetest.get_modpath("redstone")
dofile(path.."/functions.lua")
dofile(path.."/pressure_plate.lua")
-get_old_power = function(pos)
- local meta = minetest.get_meta(pos)
- local oldpower = meta:get_int("old_power")
- return(oldpower)
-end
--set the data for powered states
-get_local_power = function(pos)
- local max_level = 0
- for x = -1,1 do
- for y = -1,1 do
- for z = -1,1 do
- --index only direct neighbors
- if not (math.abs(x)+math.abs(z) > 1) or (math.abs(x)+math.abs(z) == 0) then
- --print(minetest.get_node(vector.add(vector.new(x,y,z),pos)).name)
- local level = minetest.get_node_group(minetest.get_node(vector.add(vector.new(x,y,z),pos)).name, "redstone_power")
- if level > max_level then
- max_level = level
- end
- end
- end
+local get_local_power = function(pos)
+ if not pos then
+ return
end
+ for _,order in pairs(order) do
+ --print(get_node(add_vec(new_vec(x,y,z),pos)).name)
+ if get_item_group(get_node(add_vec(new_vec(order.x,order.y,order.z),pos)).name, "redstone_power") > 0 then
+ return(1)
+ end
+ if get_meta(add_vec(new_vec(order.x,order.y,order.z),pos)):get_int("redstone_power") > 0 then
+ return(1)
+ end
end
- return(max_level)
+ return(0)
end
---this is used for a power state comparison
-set_old_power = function(pos,level)
- local meta = minetest.get_meta(pos)
- meta:set_int("old_power",level)
+local power
+local pos
+local get_powered_state_directional = function(pos)
+ pos = sub_vec(pos,facedir_to_dir(get_node(pos).param2))
+ power = get_item_group(get_node(pos).name, "redstone_power")
+ if power == 0 then
+ power = get_meta(pos):get_int("redstone_power")
+ end
+ return(power)
end
-get_powered_state_directional = function(pos)
- local meta = minetest.get_meta(pos)
- local node = minetest.get_node(pos)
- local param2 = node.param2
- local dir = minetest.facedir_to_dir(param2)
- local input_pos = vector.subtract(pos,dir)
- local behind_node = minetest.get_node(input_pos)
- local level = minetest.get_node_group(behind_node.name, "redstone_power")
- return(level)
+local node
+local redstone_activate = function(pos,power)
+ after(0,function()
+ node = get_node(pos).name
+ if registered_nodes[node].redstone_activation then
+ registered_nodes[node].redstone_activation(pos)
+ end
+ end)
end
-
-local redstone_activate = function(name,pos,power)
- minetest.after(0,function(name,pos)
- if minetest.registered_nodes[name].redstone_activation then
- minetest.registered_nodes[name].redstone_activation(pos)
- end
- set_old_power(pos,power)
- end,name,pos,power)
+local node
+local redstone_deactivate = function(pos,power)
+ after(0,function()
+ node = get_node(pos).name
+ if registered_nodes[node].redstone_deactivation then
+ registered_nodes[node].redstone_deactivation(pos)
+ end
+ end)
end
-local redstone_deactivate = function(name,pos,power)
- minetest.after(0,function(name,pos)
- if minetest.registered_nodes[name].redstone_deactivation then
- minetest.registered_nodes[name].redstone_deactivation(pos)
- end
- set_old_power(pos,power)
- end,name,pos,power)
+--collect all nodes that are local to the modified
+--node of redstone dust and store in memory
+local function get_group(i,gotten_group)
+ return(get_item_group(get_node(i).name, gotten_group))
end
-local redstone_update = function(name,pos,power)
- minetest.after(0,function(name,pos,power)
- if minetest.registered_nodes[name].redstone_update then
- minetest.registered_nodes[name].redstone_update(pos)
- end
- set_old_power(pos,power)
- end,name,pos,power)
-end
+local localredstone = {}
+localredstone.injector = function(i,origin)
+ if get_node(i).name == "air" then
+ return
+ end
+ if r_index[i.x] and r_index[i.x][i.y] then
+ if r_index[i.x][i.y][i.z] then
+ return
+ end
+ end
+ if vector_distance(i,origin) > 9 then
+ return
+ end
---collect all nodes that are local to the modified
---node of redstone dust and store in memory
-function redstone.collect_info(pos)
- --if table.getn(r_index) == 0 then
- --print("-----------------------")
- --print("started indexing")
- --end
- local get_node = minetest.get_node
- local group = minetest.get_node_group
+ --index dust
+ if get_group(i,"redstone_dust") > 0 then
+ if vector_distance(i,origin) <= 8 then
+ --add data to both maps
+ if not r_index[i.x] then r_index[i.x] = {} end
+ if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
+ r_index[i.x][i.y][i.z] = {dust = true,level = 0}
+
+ if not check_table[i.x] then check_table[i.x] = {} end
+ if not check_table[i.x][i.y] then check_table[i.x][i.y] = {} end
+ check_table[i.x][i.y][i.z] = get_group(i,"redstone_power")
+
+ --the data to the 3d array must be written to memory before this is executed
+ --or a stack overflow occurs!!!
+ localredstone.collector(i,origin)
+ return
+ else
+ if not r_index[i.x] then r_index[i.x] = {} end
+ if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
+ r_index[i.x][i.y][i.z] = {torch = true,power=get_group(i,"redstone_power")}
+ end
+ end
+ --index power sources
+ if get_group(i,"redstone_torch") > 0 then
+ if not r_index[i.x] then r_index[i.x] = {} end
+ if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
+ r_index[i.x][i.y][i.z] = {torch = true,power=get_group(i,"redstone_power")}
+ end
+ --index directional power sources (Like repeaters/comparators)
+ --only outputs forwards
+ if get_group(i,"torch_directional") > 0 then
+ if not r_index[i.x] then r_index[i.x] = {} end
+ if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
+ r_index[i.x][i.y][i.z] = {torch_directional = true, dir = get_node(i).param2 , power = get_group(i,"redstone_power")}
+ end
- local function get_group(i,gotten_group)
- return(group(get_node(i).name, gotten_group))
+ --index directional activators (Like repeaters/comparators)
+ --only accepts input from the back
+ if get_group(i,"redstone_activation_directional") > 0 then
+ --print("indexing directional")
+ if not a_index[i.x] then a_index[i.x] = {} end
+ if not a_index[i.x][i.y] then a_index[i.x][i.y] = {} end
+ if not a_index[i.x][i.y][i.z] then a_index[i.x][i.y][i.z] = {} end
+
+ a_index[i.x][i.y][i.z].redstone_activation = true
+ a_index[i.x][i.y][i.z].directional = true
end
- for x = -1,1 do
- for y = -1,1 do
- for z = -1,1 do
- --index only direct neighbors
- if not (math.abs(x)+math.abs(z) > 1) or (math.abs(x)+math.abs(z) == 0) then
- local r_type = ""
- local i = vector.add(pos,vector.new(x,y,z))
- local execute_collection = true
- if r_index[i.x] and r_index[i.x][i.y] then
- if r_index[i.x][i.y][i.z] then
- execute_collection = false
- end
- end
- --[[ EXPLANATION
- we run through the groups
- 1 redstone_torch overrides dust if defined
- 2 torch_directional overrides torch if defined
- 3 redstone activation is bolted on with directional overriding general
-
- this is to prevent weird behavior
-
-
- This new method also uses a table bolted onto the x,y,z positional data value
- what does this mean?
- It's much easier to work with and modify
- ]]--
-
- if execute_collection == true then
- --index dust
- if get_group(i,"redstone_dust") > 0 then
- --add data to both maps
- if not r_index[i.x] then r_index[i.x] = {} end
- if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
- r_index[i.x][i.y][i.z] = {dust = true,level = 0} --get_group(i,"redstone_power")}
- --the data to the 3d array must be written to memory before this is executed
- --or a stack overflow occurs!!!
- --pass down info for activators
- redstone.collect_info(i,get_group(i,"redstone_power"))
- end
- --index power sources
- if get_group(i,"redstone_torch") > 0 then
- if not r_index[i.x] then r_index[i.x] = {} end
- if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
- r_index[i.x][i.y][i.z] = {torch = true,power=get_group(i,"redstone_power")}
- end
- --index directional power sources (Like repeaters/comparators)
- --only outputs forwards
- if get_group(i,"torch_directional") > 0 then
- if not r_index[i.x] then r_index[i.x] = {} end
- if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
- r_index[i.x][i.y][i.z] = {torch_directional = true, dir = get_node(i).param2 , power = get_group(i,"redstone_power")}
- end
-
- --index directional activators (Like repeaters/comparators)
- --only accepts input from the back
- if get_group(i,"redstone_activation_directional") > 0 then
- --print("indexing directional")
- if not r_index[i.x] then r_index[i.x] = {} end
- if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
- if not r_index[i.x][i.y][i.z] then r_index[i.x][i.y][i.z] = {} end
- --r_index[i.x][i.y][i.z].activate = false
- r_index[i.x][i.y][i.z].redstone_activation = true
- r_index[i.x][i.y][i.z].directional = true
- r_index[i.x][i.y][i.z].dir = get_node(i).param2
- end
-
- --index objects that activate
- if get_group(i,"redstone_activation") > 0 then
- if not r_index[i.x] then r_index[i.x] = {} end
- if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
- if not r_index[i.x][i.y][i.z] then r_index[i.x][i.y][i.z] = {} end
- --r_index[i.x][i.y][i.z].activate = false
- r_index[i.x][i.y][i.z].redstone_activation = true
- r_index[i.x][i.y][i.z].name = get_node(i).name
-
-
- --set_powered_state(i)
-
- --local powered = get_powered_state(i)
-
-
- --print("powered:"..powered,"oldpowered:"..old_powered)
-
- --r_index[i.x][i.y][i.z].powered = powered
- --r_index[i.x][i.y][i.z].old_powered = old_powered
-
- --split this into update up and down
- --if powered > old_powered then
- -- r_index[i.x][i.y][i.z].deactivate = true
- ---elseif powered > 0 and old_powered == 0 then
- -- r_index[i.x][i.y][i.z].deactivate = true
- --end
- end
- end
- end
+ --index objects that activate
+ if get_group(i,"redstone_activation") > 0 then
+ if not a_index[i.x] then a_index[i.x] = {} end
+ if not a_index[i.x][i.y] then a_index[i.x][i.y] = {} end
+ if not a_index[i.x][i.y][i.z] then a_index[i.x][i.y][i.z] = {} end
+ a_index[i.x][i.y][i.z].redstone_activation = true
end
+
+ --sneaky way to make levers and buttons work
+ if get_meta(i):get_int("redstone_power") > 0 then
+ if not r_index[i.x] then r_index[i.x] = {} end
+ if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
+ r_index[i.x][i.y][i.z] = {torch = true,power=get_meta(i):get_int("redstone_power")}
end
+end
+
+localredstone.collector = function(pos,origin)
+ for _,order in pairs(order) do
+ localredstone.injector(add_vec(pos,new_vec(order.x,order.y,order.z)),origin)
end
end
--check if index table contains items
--then execute an update
-minetest.register_globalstep(function(dtime)
+local function redstone_algorithm()
--if indexes exist then calculate redstone
- if r_index and next(r_index) then
- --create the old version to help with deactivation calculation
- r_copy = table.copy(r_index)
- redstone.calculate()
- end
+ --create the old version to help with deactivation calculation
+ redstone.calculate()
--clear the index to avoid cpu looping wasting processing power
r_index = {}
-end)
+ a_index = {}
+ check_table = {}
+end
+
+
+function redstone.collect_info(pos)
+ if r_index[pos.x] and r_index[pos.x][pos.y] and r_index[pos.x][pos.y][pos.z] then
+ return
+ end
+ localredstone.injector(pos,pos)
+ localredstone.collector(pos,pos)
+
+ redstone_algorithm()
+end
+
--make all power sources push power out
+local x_min
+local x_max
+local y_min
+local y_max
+local z_min
+local z_max
+local initial_check
+
+
+local pos
+local node
+local power
function redstone.calculate()
+ speed_test = minetest.get_us_time()/1000000
+
--pathfind through memory map
for x,index_x in pairs(r_index) do
for y,index_y in pairs(index_x) do
for z,data in pairs(index_y) do
--allow data values for torches
if data.torch then
- redstone.pathfind(vector.new(x,y,z),data.power)
+ redstone.pathfind(new_vec(x,y,z),data.power)
+ r_index[x][y][z] = nil
elseif data.torch_directional then
- redstone.pathfind(vector.new(x,y,z),data.power,data.dir)
- end
- end
- end
- end
-
- --calculate values for voxel manip
- local x_min,x_max,y_min,y_max,z_min,z_max
- for x,index_x in pairs(r_index) do
- for y,index_y in pairs(index_x) do
- for z,_ in pairs(index_y) do
- --do this because the root (x) will always come first
- if not x_min then
- x_min = x
- x_max = x
- y_min = y
- y_max = y
- z_min = z
- z_max = z
+ redstone.pathfind(new_vec(x,y,z),data.power,data.dir)
+ r_index[x][y][z] = nil
end
- if x < x_min then x_min = x end
- if x > x_max then x_max = x end
- if y < y_min then y_min = y end
- if y > y_max then y_max = y end
- if z < z_min then z_min = z end
- if z > z_max then z_max = z end
end
end
end
+
+ print("total torch calc time:"..minetest.get_us_time()/1000000-speed_test)
+
+
- local min = vector.new(x_min,y_min,z_min)
- local max = vector.new(x_max,y_max,z_max)
- local vm = minetest.get_voxel_manip()
- local emin, emax = vm:read_from_map(min,max)
- local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
- local data = vm:get_data()
- local content_id = minetest.get_name_from_content_id
--reassemble the table into a position list minetest can understand
-
--run through and set dust
+ local count = 0
+
for x,datax in pairs(r_index) do
for y,datay in pairs(datax) do
for z,index in pairs(datay) do
- local p_pos = area:index(x,y,z)
- if index.dust then
- data[p_pos] = minetest.get_content_id("redstone:dust_"..index.level)
+ --print(get_node(new_vec(x,y,z)).name)
+ if index and index.dust and index.level ~= check_table[x][y][z] then
+ count = count + 1
+ minetest.set_node(new_vec(x,y,z),{name="redstone:dust_"..index.level})
end
end
end
end
- vm:set_data(data)
- vm:write_to_map()
-
-
-
- --run through activators
- for x,datax in pairs(r_index) do
+ print("set "..count.." nodes")
+
+ for x,datax in pairs(a_index) do
for y,datay in pairs(datax) do
for z,index in pairs(datay) do
- local pos = vector.new(x,y,z)
- local node = minetest.get_node(pos)
--directional activators
- if index.redstone_activation == true and index.directional == true then
- local power = get_powered_state_directional(pos)
- local old_power = get_old_power(pos)
- if power > 0 and old_power == 0 then
- redstone_activate(node.name,pos,power)
- elseif power == 0 and old_power > 0 then
- redstone_deactivate(node.name,pos,power)
- --do an update if state has not changed
- elseif power > 0 and old_power > 0 then
- redstone_update(node.name,pos,power)
- elseif power == 0 and old_power == 0 then
- redstone_update(node.name,pos,power)
+ if index.directional == true then
+ power = get_powered_state_directional(new_vec(x,y,z))
+ if power then
+ if power > 0 then
+ redstone_activate(new_vec(x,y,z),power)
+ elseif power == 0 then
+ redstone_deactivate(new_vec(x,y,z),power)
+ end
end
--non directional activators
- elseif index.redstone_activation == true then
- local power = get_local_power(pos)
- local old_power = get_old_power(pos)
- if power > 0 and old_power == 0 then
- redstone_activate(node.name,pos,power)
- elseif power == 0 and old_power > 0 then
- redstone_deactivate(node.name,pos,power)
- --do an update if state has not changed
- elseif power > 0 and old_power > 0 then
- redstone_update(node.name,pos,power)
- elseif power == 0 and old_power == 0 then
- redstone_update(node.name,pos,power)
+ else
+ power = get_local_power(new_vec(x,y,z))
+ if power then
+ if power > 0 then
+ redstone_activate(new_vec(x,y,z),power)
+ elseif power == 0 then
+ redstone_deactivate(new_vec(x,y,z),power)
+ end
end
end
end
end
--make redstone wire pass on current one level lower than it is
-function redstone.pathfind(source,source_level,direction)
+local i
+local index
+local passed_on_level
+local function redstone_pathfinder(source,source_level,direction)
--directional torches
+
if direction then
--print("starting direction")
- local dir = minetest.facedir_to_dir(direction)
- local i = vector.add(source,dir)
+ i = add_vec(source,facedir_to_dir(direction))
if r_index and r_index[i.x] and r_index[i.x][i.y] and r_index[i.x][i.y][i.z] then
- local index = r_index[i.x][i.y][i.z]
+ index = r_index[i.x][i.y][i.z]
--dust
if index.dust then
- local passed_on_level = source_level - 1
+ passed_on_level = source_level - 1
if passed_on_level > 0 then
r_index[i.x][i.y][i.z].level = passed_on_level
- redstone.pathfind(i,passed_on_level)
+ redstone_pathfinder(i,passed_on_level,nil)
end
end
end
else
--redstone and torch
- for x = -1,1 do
- for y = -1,1 do
- for z = -1,1 do
- local i = vector.add(source,vector.new(x,y,z))
+ for _,order in pairs(order) do
+ i = add_vec(source,new_vec(order.x,order.y,order.z))
if r_index and r_index[i.x] and r_index[i.x][i.y] and r_index[i.x][i.y][i.z] then
- local index = r_index[i.x][i.y][i.z]
+ --print(minetest.get_node(i).name)
+ index = r_index[i.x][i.y][i.z]
if index.dust then
- local passed_on_level = source_level - 1
+ passed_on_level = source_level - 1
if passed_on_level > 0 and index.level < source_level then
r_index[i.x][i.y][i.z].level = passed_on_level
- redstone.pathfind(i,passed_on_level)
+ redstone_pathfinder(i,passed_on_level,nil)
end
end
end
end
- end
- end
end
end
+function redstone.pathfind(source,source_level,direction)
+ redstone_pathfinder(source,source_level,direction)
+end
return
end
local sneak = placer:get_player_control().sneak
- local noddef = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name]
+ local noddef = registered_nodes[get_node(pointed_thing.under).name]
if not sneak and noddef.on_rightclick then
minetest.item_place(itemstack, placer, pointed_thing)
return
end,
})
-minetest.register_craft({
- type = "shapeless",
- output = "redstone:dust",
- recipe = {"redstone:dust"},
-})
-
--8 power levels 8 being the highest
local color = 0
for i = 0,8 do
- local coloring = math.floor(color)
+ local coloring = floor(color)
minetest.register_node("redstone:dust_"..i,{
description = "Redstone Dust",
wield_image = "redstone_dust_item.png",
redstone.collect_info(pos)
end,
after_destruct = function(pos)
- --redstone.remove(pos,minetest.registered_nodes[minetest.get_node(pos).name].power)
- redstone.collect_info(pos,i)
+ --redstone.remove(pos,registered_nodes[get_node(pos).name].power)
+ redstone.collect_info(pos)
end,
connects_to = {"group:redstone"},
})