]> git.lizzy.rs Git - Crafter.git/blobdiff - mods/redstone/init.lua
Make levers and buttons work
[Crafter.git] / mods / redstone / init.lua
index 240ed1500fc71902b801b63b0c8a8a44b77c0251..423ec4c5e6f9c488933b39443d6df4e99d7f1e8f 100644 (file)
@@ -29,6 +29,37 @@ local add_vec         = vector.add
 local sub_vec         = vector.subtract
 local vector_distance = vector.distance
 
+local activator_table = {} -- this holds the translation data of activator tables (activator functions)
+
+-- redstone class
+redstone = {}
+
+-- enables mods to create data functions
+function redstone.register_activator(data)
+       activator_table[data.name] = {
+               activate   = data.activate,
+               deactivate = data.deactivate
+       }
+end
+
+local path = minetest.get_modpath("redstone")
+--dofile(path.."/functions.lua")
+--dofile(path.."/wire.lua")
+dofile(path.."/torch.lua")
+dofile(path.."/lever.lua")
+dofile(path.."/button.lua")
+dofile(path.."/repeater.lua")
+--dofile(path.."/light.lua")
+--dofile(path.."/piston.lua")
+--dofile(path.."/comparator.lua")
+--dofile(path.."/craft.lua")
+--dofile(path.."/ore.lua")
+dofile(path.."/inverter.lua")
+--dofile(path.."/player_detector.lua")
+--dofile(path.."/space_maker.lua")
+--dofile(path.."/pressure_plate.lua")
+
+
 --this is written out manually so that
 --math.abs is not needed
 local order = {
@@ -44,334 +75,260 @@ local order = {
        {x=0, y=-1, z=1}, {x= 0, y=-1, z=-1},
 }
 
+--thanks to RhodiumToad for helping me figure out a good method to do this
+
 local pool = {} -- this holds all redstone data (literal 3d virtual memory map)
 
+local table_3d
+local temp_pool
+local function create_boundary_box(pos)
+       table_3d = {}
+       for x = pos.x-9,pos.x+9 do
+               if pool[x] then
+                       for y = pos.y-9,pos.y+9 do
+                               if pool[x][y] then
+                                       for z = pos.z-9,pos.z+9 do
+                                               temp_pool = pool[x][y][z]
+                                               if temp_pool then
+                                                       if not table_3d[x] then table_3d[x] = {} end
+                                                       if not table_3d[x][y] then table_3d[x][y] = {} end
+
+                                                       if (x == pos.x-9 or x == pos.x+9 or 
+                                                       y == pos.y-9 or y == pos.y+9 or 
+                                                       z == pos.z-9 or z == pos.z+9) and 
+                                                       temp_pool.dust and temp_pool.dust > 1 then
+                                                               table_3d[x][y][z] = {torch=temp_pool.dust}
+                                                       else
+                                                               if temp_pool.dust then
+                                                                       table_3d[x][y][z] = {dust=0,origin=temp_pool.dust}
+                                                               else
+                                                                       table_3d[x][y][z] = temp_pool
+                                                               end
+                                                       end
+                                               end
+                                       end
+                               end
+                       end
+               end
+       end
+       return(table_3d)
+end
+
 local function data_injection(pos,data)
-       --add data to both maps
+       -- add data into 3d memory
        if data then
                if not pool[pos.x] then pool[pos.x] = {} end
                if not pool[pos.x][pos.y] then pool[pos.x][pos.y] = {} end
                pool[pos.x][pos.y][pos.z] = data
+       --delete data from 3d memory
        else
-               pool[pos.x][pos.y][pos.z] = data
-               if not next(pool[pos.x][pos.y]) then
-                       pool[pos.x][pos.y] = nil
-                       if not next(pool[pos.x]) then
-                               pool[pos.x] = nil
+               if pool and pool[pos.x] and pool[pos.x][pos.y] then
+                       pool[pos.x][pos.y][pos.z] = data
+                       if pool[pos.x][pos.y] and not next(pool[pos.x][pos.y]) then
+                               pool[pos.x][pos.y] = nil
+                               -- only run this if y axis is empty
+                               if pool[pos.x] and not next(pool[pos.x]) then
+                                       pool[pos.x] = nil
+                               end
                        end
                end
        end
-       print(dump(pool))
 end
 
 
-
--- redstone class
-redstone = {}
-
-local r_index = {}
-local a_index = {}
-local check_table = {}
-
---local path = minetest.get_modpath("redstone")
---dofile(path.."/functions.lua")
---dofile(path.."/wire.lua")
---dofile(path.."/torch.lua")
---dofile(path.."/lever.lua")
---dofile(path.."/button.lua")
---dofile(path.."/repeater.lua")
---dofile(path.."/light.lua")
---dofile(path.."/piston.lua")
---dofile(path.."/comparator.lua")
---dofile(path.."/craft.lua")
---dofile(path.."/ore.lua")
---dofile(path.."/inverter.lua")
---dofile(path.."/player_detector.lua")
---dofile(path.."/space_maker.lua")
---dofile(path.."/pressure_plate.lua")
-
-
---set the data for powered states
-local get_local_power = function(pos)
+-- activators
+local n
+local temp_pool
+local non_directional_activator = function(pos)
        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)
+               n_pos = add_vec(pos,order)
+               temp_pool = pool[n.x][n.y][n.z]
+               if temp_pool then
+                       --if temp_pool.dust and temp_pool.dust > 0 or 
+                       --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
                end
        end     
        return(0)
 end
 
-local power
-local pos
-local node
-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
-
-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
+-- directional activators
+local n_pos
+local temp_pool
+local temp_pool2
+local input
+local ignore
+local directional_activator = function(pos)
+       
+       ignore = false
+       input = nil
+       temp_pool2 = nil
 
-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
+       temp_pool = pool[pos.x][pos.y][pos.z]
+       
+       if not temp_pool then ignore = true end
 
---collect all nodes that are local to the modified
---node of redstone dust and store in memory
-local localredstone = {}
-local node
-localredstone.injector = function(i,origin)
-       node = get_node(i).name
-       if node == "air" then
-               return
+       if not ignore then
+               input = temp_pool.input
        end
 
-       if r_index[i.x] and r_index[i.x][i.y] and r_index[i.x][i.y][i.z] then
-               return
+       if not input then ignore = true end
+
+       if not ignore then
+               input = temp_pool.input
        end
 
-       if vector_distance(i,origin) > 9 then
-               return
+       if not ignore and pool and pool[input.x] and pool[input.x][input.y] and pool[input.x][input.y][input.z] then
+               temp_pool2 = pool[input.x][input.y][input.z]
+       else
+               ignore = true
        end
 
-       --index dust
-       if get_item_group(node,"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_item_group(node,"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)
+       if not temp_pool2 then ignore = true end
+
+       if not ignore and ((temp_pool2.dust and temp_pool2.dust > 0) or (temp_pool2.torch and temp_pool2.directional_activator and temp_pool2.dir == temp_pool.dir) or 
+       (not temp_pool2.directional_activator and temp_pool2.torch))  then
+               if activator_table[temp_pool.name].activate then
+                       activator_table[temp_pool.name].activate(pos)
                        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_item_group(node,"redstone_power")}
                end
-       end
-       --index power sources
-       if get_item_group(node,"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_item_group(node,"redstone_power")}
-       end     
-       --index directional power sources (Like repeaters/comparators)
-       --only outputs forwards
-       if get_item_group(node,"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_item_group(node,"redstone_power")}
-       end
-       
-       --index directional activators (Like repeaters/comparators)
-       --only accepts input from the back
-       if get_item_group(node,"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
-       
-       --index objects that activate
-       if get_item_group(node,"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
+               return
        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")}
+       if activator_table[temp_pool.name].deactivate then
+               activator_table[temp_pool.name].deactivate(pos)
        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)
+--make redstone wire pass on current one level lower than it is
+local i
+local index
+local passed_on_level
+local function redstone_pathfinder(source,source_level,boundary,output)
+       if not source_level then return end
+       --directional torches
+       if output then
+               i = output
+               if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
+                       index = boundary[i.x][i.y][i.z]
+                       --dust
+                       if index.dust then
+                               passed_on_level = source_level - 1
+                               if passed_on_level > 0 then
+                                       boundary[i.x][i.y][i.z].dust = passed_on_level
+                                       redstone_pathfinder(i,passed_on_level,boundary,nil)
+                               end
+                       end
+               end
+       else
+               --redstone and torch
+               for _,order in pairs(order) do
+                       i = add_vec(source,new_vec(order.x,order.y,order.z))
+                       if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
+                               index = boundary[i.x][i.y][i.z]
+                               if index.dust then
+                                       passed_on_level = source_level - 1
+                                       if passed_on_level > 0 and index.dust < source_level then
+                                               boundary[i.x][i.y][i.z].dust = passed_on_level
+                                               redstone_pathfinder(i,passed_on_level,boundary,nil)
+                                       end
+                               end
+                       end
+               end
        end
+       return(boundary)
 end
 
 
---check if index table contains items
---then execute an update
-local function redstone_algorithm()
-       --if indexes exist then calculate redstone
-       --create the old version to help with deactivation calculation
-       redstone.calculate()
-       --clear the index to avoid cpu looping wasting processing power
-       r_index = {}
-       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 pos
 local node
 local power
-function redstone.calculate()
-       --speed_test = minetest.get_us_time()/1000000
-
+local boundary
+local function calculate(pos)
+       boundary = create_boundary_box(pos)
        --pathfind through memory map   
-       for x,index_x in pairs(r_index) do
+       for x,index_x in pairs(boundary) 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(new_vec(x,y,z),data.power)
-                                       r_index[x][y][z] = nil
+                               if data.torch and not data.torch_directional then
+                                       redstone_pathfinder(new_vec(x,y,z),data.torch,boundary)
+                                       boundary[x][y][z] = nil
                                elseif data.torch_directional then
-                                       redstone.pathfind(new_vec(x,y,z),data.power,data.dir)
-                                       r_index[x][y][z] = nil
+                                       redstone_pathfinder(new_vec(x,y,z),data.torch,boundary,data.output)
                                end
                        end
                end
        end
-       
-       --print("total torch calc time:"..minetest.get_us_time()/1000000-speed_test)
-
-
-
        --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 x,datax in pairs(boundary) do
                for y,datay in pairs(datax) do
-                       for z,index in pairs(datay) do
-                               --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
-                                       swap_node(new_vec(x,y,z),{name="redstone:dust_"..index.level})
+                       for z,data in pairs(datay) do
+                               if data.dust and data.dust ~= data.origin then
+                                       swap_node(new_vec(x,y,z),{name="redstone:dust_"..data.dust})
                                end
-                       end
-               end
-       end
-       --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
-                               --directional activators
-                               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
-                               else
-                                       power = get_local_power(new_vec(x,y,z))
-                                       --print(power)
-                                       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
+                               --write data back to memory pool
+                               pool[x][y][z] = data
+
+                               if data.dust then
+                                       --delete the data to speed up next loop
+                                       boundary[x][y][z] = nil
                                end
                        end
                end
        end
-end
 
---make redstone wire pass on current one level lower than it is
-local i
-local index
-local passed_on_level
-local function redstone_pathfinder(source,source_level,direction)
-       --directional torches
-
-       if direction then
-               --print("starting direction")
-               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
-                       index = r_index[i.x][i.y][i.z]
-                       --dust
-                       if index.dust  then
-                               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_pathfinder(i,passed_on_level,nil)
-                               end
-                       end
-               end
-       else
-               --redstone and torch
-               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
-                               --print(minetest.get_node(i).name)
-                               index = r_index[i.x][i.y][i.z]                                  
-                               if index.dust  then
-                                       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_pathfinder(i,passed_on_level,nil)
-                                       end
+       
+       --this must be done after the memory is written
+       for x,datax in pairs(boundary) do
+               for y,datay in pairs(datax) do
+                       for z,data in pairs(datay) do
+                               if data.directional_activator then
+                                       directional_activator(new_vec(x,y,z))
+                               elseif data.activator then
+                                       non_directional_activator(new_vec(x,y,z))
                                end
                        end
                end
        end
 end
-function redstone.pathfind(source,source_level,direction)
-       redstone_pathfinder(source,source_level,direction)
-end
 
 
+function redstone.inject(pos,data)
+       data_injection(pos,data)
+end
 
 
+local recursion_check = {}
+function redstone.update(pos)
+       local s_pos = minetest.serialize(pos)
+       if not recursion_check[s_pos] then
+               recursion_check[s_pos] = 0
+       end
+       recursion_check[s_pos] = recursion_check[s_pos] + 1
+       --print(recursion_check[s_pos])
+       if recursion_check[s_pos] > 6 then
+               minetest.after(0,function()
+                       minetest.dig_node(pos)
+                       data_injection(pos,nil)
+                       redstone.update(pos)
+               end)
+               return
+       end
 
+       calculate(pos)
+end
 
+minetest.register_globalstep(function()
+       recursion_check = {}
+end)
 
 
 ----------------------------------------------------------------------------
@@ -467,11 +424,22 @@ for i = 0,8 do
                drop="redstone:dust",
                on_construct = function(pos)
                        data_injection(pos,{dust=i})
+                       calculate(pos)
                end,
                after_destruct = function(pos)
                        data_injection(pos,nil)
+                       calculate(pos)
                end,
                connects_to = {"group:redstone"},
        })
        color= color +31.875
+
+       minetest.register_lbm({
+        name = "redstone:"..i,
+               nodenames = {"redstone:dust_"..i},
+               run_at_every_load = true,
+        action = function(pos)
+            data_injection(pos,{dust=i})
+        end,
+    })
 end