]> git.lizzy.rs Git - Crafter.git/blobdiff - mods/redstone/init.lua
Make redstone do "y corrners" (diagonal facedir binary)
[Crafter.git] / mods / redstone / init.lua
index 3b6707a34b644ee7d27f04d8457f8dff649c50e3..fbc25a9944dcd08ff3b9d32f48251a6302273900 100644 (file)
@@ -1,6 +1,55 @@
---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")
@@ -20,306 +69,248 @@ dofile(path.."/space_maker.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
@@ -328,44 +319,47 @@ function redstone.calculate()
 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
 
 
 
@@ -419,7 +413,7 @@ minetest.register_craftitem("redstone:dust", {
                        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
@@ -440,16 +434,10 @@ minetest.register_craftitem("redstone:dust", {
        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",
@@ -475,8 +463,8 @@ for i = 0,8 do
                        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"},
        })