1 --if you attempt to read this, god bless you
4 minetest,vector,math,table,pairs,next
6 minetest,vector,math,table,pairs,next
9 local get_node = minetest.get_node
10 local get_item_group = minetest.get_item_group
11 local get_meta = minetest.get_meta
12 local facedir_to_dir = minetest.facedir_to_dir
13 local content_id = minetest.get_name_from_content_id
14 local get_content_id = minetest.get_content_id
15 local get_voxel_manip = minetest.get_voxel_manip
16 local after = minetest.after
18 local swap_node = minetest.swap_node
19 local registered_nodes
20 minetest.register_on_mods_loaded(function()
21 registered_nodes = minetest.registered_nodes
26 local floor = math.floor
27 local ceil = math.ceil
30 local new_vec = vector.new
31 local add_vec = vector.add
32 local sub_vec = vector.subtract
33 local vector_distance = vector.distance
34 local vec_equals = vector.equals
36 local activator_table = {} -- this holds the translation data of activator tables (activator functions)
37 local capacitor_table = {}
38 local player_detection_table = {}
39 --local instructions = 0
44 redstone.max_state = 9 -- the limit to power transmission
46 redstone.player_detector_add = function(pos)
47 player_detection_table[minetest.serialize(pos)] = pos
50 redstone.player_detector_remove = function(pos)
51 player_detection_table[minetest.serialize(pos)] = nil
55 -- enables mods to create data functions
56 function redstone.register_activator(data)
57 activator_table[data.name] = {
58 activate = data.activate,
59 deactivate = data.deactivate
63 -- enables mods to create data functions
64 function redstone.register_capacitor(data)
65 capacitor_table[data.name] = {
71 local path = minetest.get_modpath("redstone")
72 dofile(path.."/functions.lua")
73 dofile(path.."/torch.lua")
74 dofile(path.."/lever.lua")
75 dofile(path.."/button.lua")
76 dofile(path.."/repeater.lua")
77 dofile(path.."/light.lua")
78 dofile(path.."/piston.lua")
79 --dofile(path.."/comparator.lua")
80 dofile(path.."/craft.lua")
81 dofile(path.."/ore.lua")
82 dofile(path.."/inverter.lua")
83 dofile(path.."/player_detector.lua")
84 dofile(path.."/space_maker.lua")
85 --dofile(path.."/pressure_plate.lua")
86 dofile(path.."/capacitors.lua")
87 dofile(path.."/breaker.lua")
88 dofile(path.."/detector.lua")
89 --dofile(path.."/dispenser.lua")
92 --this is written out manually so that
93 --math.abs is not needed
112 --thanks to RhodiumToad for helping me figure out a good method to do this
114 local pool = {} -- this holds all redstone data (literal 3d virtual memory map)
117 local function data_injection(pos,data)
118 --instructions = instructions + 1
119 -- add data into 3d memory
121 if not pool[pos.x] then pool[pos.x] = {} end
122 if not pool[pos.x][pos.y] then pool[pos.x][pos.y] = {} end
123 pool[pos.x][pos.y][pos.z] = data
124 --instructions = instructions + 1
125 --print("building 3d memory")
126 --delete data from 3d memory
128 if pool and pool[pos.x] and pool[pos.x][pos.y] then
129 pool[pos.x][pos.y][pos.z] = data
130 --instructions = instructions + 1
131 --print("deleting 3d memory")
132 if pool[pos.x][pos.y] and not next(pool[pos.x][pos.y]) then
133 pool[pos.x][pos.y] = nil
134 --instructions = instructions + 1
135 -- only run this if y axis is empty
136 if pool[pos.x] and not next(pool[pos.x]) then
138 --instructions = instructions + 1
148 ___ \ / ___ ,'\_ | .-. \ /|
149 \ / | |,'__ \ ,'\_ | \ | | | | ,' |_ /|
150 _ | | | |\/ \ \ | \ | |\_| _ | |_| | _ '-. .-',' |_ _
151 // | | | |____| | | |\_|| |__ // | | ,'_`. | | '-. .-',' `. ,'\_
152 \\_| |_,' .-, _ | | | | |\ \ // .| |\_/ | / \ || | | | / |\ \| \
153 `-. .-'| |/ / | | | | | | \ \// | | | | | || | | | | |_\ || |\_|
154 | | | || \_| | | | /_\ \ / | |` | | | || | | | | .---'| |
155 | | | |\___,_\ /_\ _ // | | | \_/ || | | | | | /\| |
156 /_\ | | //_____// .||` _ `._,' | | | | \ `-' /| |
157 /_\ `------' \ | /-\ND _ `.\ | | `._,' /_\
160 / |__| /_\ |\ /| |_) |_ |_)
161 \__ | | / \ | \/ | |_) |__ | \
171 local r_max = redstone.max_state
172 local function create_boundary_box(pos)
173 --instructions = instructions + 1
175 for x = pos.x-r_max,pos.x+r_max do
176 --instructions = instructions + 1
178 for y = pos.y-r_max,pos.y+r_max do
179 --instructions = instructions + 1
181 for z = pos.z-r_max,pos.z+r_max do
182 --instructions = instructions + 1
183 temp_pool = pool[x][y][z]
184 --instructions = instructions + 1
186 if not table_3d[x] then table_3d[x] = {} end
187 if not table_3d[x][y] then table_3d[x][y] = {} end
189 if (x == pos.x-r_max or x == pos.x+r_max or
190 y == pos.y-r_max or y == pos.y+r_max or
191 z == pos.z-r_max or z == pos.z+r_max) and
192 temp_pool.dust and temp_pool.dust > 1 then
193 table_3d[x][y][z] = {torch=temp_pool.dust}
195 if temp_pool.dust then
196 table_3d[x][y][z] = {dust=0,origin=temp_pool.dust}
198 table_3d[x][y][z] = temp_pool
212 local function capacitor_pathfind(source,mem_map)
213 for _,order in pairs(order) do
215 i = add_vec(source,order)
216 if not mem_map[i.x] then mem_map[i.x] = {} end
217 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
219 if not mem_map[i.x][i.y][i.z] then
221 if i and pool and pool[i.x] and pool[i.x][i.y] and pool[i.x][i.y][i.z] then
222 index = pool[i.x][i.y][i.z]
223 if index.capacitor then
224 mem_map[i.x][i.y][i.z] = {name = index.name, capacitor = 0, source = index.source}
228 capacitor_pathfind(i,mem_map)
239 local function capacitor_sniff(pos)
241 table_3d = capacitor_pathfind(pos,table_3d)
242 found = table_3d.found
245 for x,datax in pairs(table_3d) do
246 for y,datay in pairs(datax) do
247 for z,data in pairs(datay) do
248 temp_pool = pool[x][y][z]
250 temp_pool.capacitor = 1
251 if capacitor_table[temp_pool.name] then
252 swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].on})
253 redstone.update(new_vec(x,y,z))
260 for x,datax in pairs(table_3d) do
261 for y,datay in pairs(datax) do
262 for z,data in pairs(datay) do
263 temp_pool = pool[x][y][z]
265 temp_pool.capacitor = 0
266 if capacitor_table[temp_pool.name] then
267 swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].off})
268 redstone.update(new_vec(x,y,z))
283 local non_directional_activator = function(pos)
284 if pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z] then
285 temp_pool = pool[pos.x][pos.y][pos.z]
287 for _,order in pairs(order) do
288 n_pos = add_vec(pos,order)
289 if pool[n_pos.x] and pool[n_pos.x][n_pos.y] and pool[n_pos.x][n_pos.y][n_pos.z] then
290 temp_pool2 = pool[n_pos.x][n_pos.y][n_pos.z]
292 if (not temp_pool2.directional_activator and temp_pool2.torch) or
293 (temp_pool2.dust and temp_pool2.dust > 0) or
294 (temp_pool2.torch_directional and vector.equals(temp_pool2.output, pos)) then
295 if activator_table[temp_pool.name] and activator_table[temp_pool.name].activate then
296 activator_table[temp_pool.name].activate(pos)
303 if activator_table[temp_pool.name] and activator_table[temp_pool.name].deactivate then
304 activator_table[temp_pool.name].deactivate(pos)
310 -- directional activators
316 local directional_activator = function(pos)
323 if not (pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z]) then return end
325 temp_pool = pool[pos.x][pos.y][pos.z]
327 if not temp_pool then ignore = true end
330 input = temp_pool.input
333 if not input then ignore = true end
336 input = temp_pool.input
339 if not ignore and pool and pool[input.x] and pool[input.x][input.y] and pool[input.x][input.y][input.z] then
340 temp_pool2 = pool[input.x][input.y][input.z]
345 if not temp_pool2 then ignore = true end
347 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
348 (not temp_pool2.directional_activator and temp_pool2.torch) or (temp_pool2.capacitor and temp_pool2.capacitor > 0)) then
349 if activator_table[temp_pool.name] and activator_table[temp_pool.name].activate then
350 activator_table[temp_pool.name].activate(pos)
356 if activator_table[temp_pool.name] and activator_table[temp_pool.name].deactivate then
357 activator_table[temp_pool.name].deactivate(pos)
361 --make redstone wire pass on current one level lower than it is
364 local passed_on_level
366 local function redstone_distribute(pos,power,mem_map,output)
370 --directional torches
375 if mem_map.dust[x] and mem_map.dust[x][y] and mem_map.dust[x][y][z] then
376 if mem_map.dust[x][y][z].dust < power then
377 mem_map.dust[x][y][z].dust = power
378 redstone_distribute(new_vec(x,y,z),power,mem_map,nil)
383 for _,order in pairs(order) do
384 --instructions = instructions + 1
385 i = add_vec(pos,order)
389 if mem_map.dust[x] and mem_map.dust[x][y] and mem_map.dust[x][y][z] then
390 if mem_map.dust[x][y][z].dust < power then
391 mem_map.dust[x][y][z].dust = power
392 redstone_distribute(new_vec(x,y,z),power,mem_map,nil)
411 _,' _o\ ,::.`:' ; ; . '
415 `.______,-,----._ ,' ;: ,/ , ,`
416 / /,-';' \ ; `: ,'/,::.:::
417 ,',;-'-'_,--; ; :. ,',',;::::::
418 ( /___,-' `. ;::,,'o/ ,:::::::
427 local function dust_sniff(pos,mem_map,boundary,single,origin,ignore)
429 --print("all position index--")
430 for _,order in pairs(order) do
431 --instructions = instructions + 1
432 i = add_vec(pos,order)
434 if not mem_map[i.x] then mem_map[i.x] = {} end
435 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
437 if not mem_map[i.x][i.y][i.z] then
438 if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
439 index = boundary[i.x][i.y][i.z]
443 mem_map[i.x][i.y][i.z] = true
445 if not mem_map.dust[i.x] then mem_map.dust[i.x] = {} end
446 if not mem_map.dust[i.x][i.y] then mem_map.dust[i.x][i.y] = {} end
448 mem_map.dust[i.x][i.y][i.z] = index
450 dust_sniff(i,mem_map,boundary)
452 elseif index.torch and index.torch > 1 then
453 if index.torch_directional and vec_equals(pos,index.output) then
455 mem_map[i.x][i.y][i.z] = true
457 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
458 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
460 mem_map.torch[i.x][i.y][i.z] = index
463 elseif not index.torch_directional then
465 mem_map[i.x][i.y][i.z] = true
467 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
468 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
470 mem_map.torch[i.x][i.y][i.z] = index
474 if index.activator then
475 mem_map[i.x][i.y][i.z] = true
477 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
478 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
480 mem_map.activator[i.x][i.y][i.z] = index
481 elseif index.directional_activator and vec_equals(pos,index.input) then
483 mem_map[i.x][i.y][i.z] = true
485 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
486 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
488 mem_map.activator[i.x][i.y][i.z] = index
494 --print("single position index")
499 if not mem_map[i.x] then mem_map[i.x] = {} end
500 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
502 if not mem_map[i.x][i.y][i.z] then
503 if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
504 index = boundary[i.x][i.y][i.z]
507 mem_map[i.x][i.y][i.z] = true
509 if not mem_map.dust[i.x] then mem_map.dust[i.x] = {} end
510 if not mem_map.dust[i.x][i.y] then mem_map.dust[i.x][i.y] = {} end
512 mem_map.dust[i.x][i.y][i.z] = index
514 dust_sniff(i,mem_map,boundary)
516 elseif index.torch and index.torch > 1 then
517 if index.torch_directional and (vec_equals(origin,index.output) or ignore) then
519 mem_map[i.x][i.y][i.z] = true
521 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
522 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
524 mem_map.torch[i.x][i.y][i.z] = index
527 elseif not index.torch_directional then
529 mem_map[i.x][i.y][i.z] = true
531 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
532 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
534 mem_map.torch[i.x][i.y][i.z] = index
538 if index.activator then
539 mem_map[i.x][i.y][i.z] = true
541 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
542 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
544 mem_map.activator[i.x][i.y][i.z] = index
545 elseif index.directional_activator and (vec_equals(origin,index.input) or ignore) then
547 mem_map[i.x][i.y][i.z] = true
549 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
550 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
552 mem_map.activator[i.x][i.y][i.z] = index
560 --make all power sources push power out
570 local function calculate(pos,is_capacitor)
571 if not is_capacitor then
572 boundary = create_boundary_box(pos)
577 dust_map.activator = {}
579 dust_detected = false
583 if boundary[pos.x] and boundary[pos.x][pos.y] and boundary[pos.x][pos.y][pos.z] then
584 if boundary[pos.x][pos.y][pos.z].torch_directional or boundary[pos.x][pos.y][pos.z].directional_activator then
589 -- sniff all possible dust within boundaries
590 if not directional then
591 dust_sniff(pos,dust_map,boundary)
592 for _,pos2 in pairs(order) do
593 pos3 = add_vec(pos,pos2)
594 if boundary[pos3.x] and boundary[pos3.x][pos3.y] and boundary[pos3.x][pos3.y][pos3.z] and
595 not (dust_map[pos3.x] and dust_map[pos3.x][pos3.y] and dust_map[pos3.x][pos3.y][pos3.z]) then
596 temp_pool3 = boundary[pos3.x][pos3.y][pos3.z]
597 if temp_pool3.dust then
598 dust_sniff(pos3,dust_map,boundary)
603 dust_sniff(pos,dust_map,boundary,true,pos,true)
605 local input = boundary[pos.x][pos.y][pos.z].input
606 local output = boundary[pos.x][pos.y][pos.z].output
608 if input and boundary[input.x] and boundary[input.x][input.y] and boundary[input.x][input.y][input.z] then
609 dust_sniff(input,dust_map,boundary,true,pos)
611 if output and boundary[output.x] and boundary[output.x][output.y] and boundary[output.x][output.y][output.z] then
612 dust_sniff(output,dust_map,boundary,true,pos)
616 for x,datax in pairs(dust_map.torch) do
617 for y,datay in pairs(datax) do
618 for z,data in pairs(datay) do
620 if data.torch_directional then
621 redstone_distribute(new_vec(x,y,z),data.torch,dust_map,data.output)
623 redstone_distribute(new_vec(x,y,z),data.torch,dust_map)
630 --set dust, set pool memory
631 for x,datax in pairs(dust_map.dust) do
632 for y,datay in pairs(datax) do
633 for z,data in pairs(datay) do
634 if data.dust and data.dust ~= data.origin then
635 swap_node(new_vec(x,y,z),{name="redstone:dust_"..data.dust})
636 data_injection(new_vec(x,y,z),data)
643 --this must be run at the end
644 for x,datax in pairs(dust_map.activator) do
645 for y,datay in pairs(datax) do
646 for z,data in pairs(datay) do
647 if data.directional_activator then
648 directional_activator(new_vec(x,y,z))
649 elseif data.activator then
650 non_directional_activator(new_vec(x,y,z))
661 function redstone.inject(pos,data)
662 data_injection(pos,data)
671 local function player_detector_calculation()
672 for _,pos in pairs(player_detection_table) do
673 level = pool[pos.x][pos.y][pos.z].torch
675 for _,player in ipairs(minetest.get_connected_players()) do
676 pos2 = player:get_pos()
677 power = floor(11-vector_distance(pos2,pos))
678 if power > r_max then
680 elseif power < 0 then
690 swap_node(pos,{name="redstone:player_detector_"..max})
691 redstone.inject(pos,{
692 name = "redstone:player_detector_"..max,
701 local recursion_check = {}
704 function redstone.update(pos,is_capacitor)
705 local count = table.getn(queue)
706 local s_pos = minetest.serialize(pos)
707 if not recursion_check[s_pos] then
708 recursion_check[s_pos] = 0
710 recursion_check[s_pos] = recursion_check[s_pos] + 1
711 if recursion_check[s_pos] > 25 then
712 --print(recursion_check[s_pos])
713 minetest.after(0,function()
714 bad_node = minetest.get_node(pos).name
715 bad_node = minetest.get_node_drops(bad_node, "main:rubypick")
716 for _,nodey in pairs(bad_node) do
717 minetest.throw_item(pos,nodey)
719 minetest.remove_node(pos)
720 data_injection(pos,nil)
725 calculate(pos,is_capacitor)
727 local dtime_goal = 0.02
729 minetest.register_globalstep(function(dtime)
730 player_detector_calculation()
733 if dtime > dtime_goal then
734 sleep = dtime - dtime_goal
739 for index,element in pairs(queue) do
740 calculate(element.pos,element.is_capacitor)
746 sleep = sleep - dtime
752 --if instructions and instructions > 0 then
753 -- print(instructions)
759 ----------------------------------------------------------------------------
763 local instruction_order = {
781 -- this is used for dynamic instruction set rebuilds
782 local function instruction_rebuild(pos,delete)
784 local instruction_set = {}
785 for _,pos2 in pairs(instruction_order) do
786 local pos3 = vector.add(pos,pos2)
787 if pool[pos3.x] and pool[pos3.x][pos3.y] and pool[pos3.x][pos3.y][pos3.z] then
788 table.insert(instruction_set,pos2)
792 for _,pos2 in pairs(instruction_order) do
797 print(dump(instruction_set))
802 -- this is used for creating fast data for the game to utilize
803 local function initial_instruction_build(pos)
804 local instruction_set = {}
805 for _,pos2 in pairs(instruction_order) do
806 local pos3 = vector.add(pos,pos2)
807 if pool[pos3.x] and pool[pos3.x][pos3.y] and pool[pos3.x][pos3.y][pos3.z] then
808 table.insert(instruction_set,pos2)
811 pool[pos.x][pos.y][pos.z].instruction_set = instruction_set
834 minetest.register_craftitem("redstone:dust", {
835 description = "Redstone Dust",
836 inventory_image = "redstone_dust_item.png",
837 wield_image = "redstone_dust_item.png",
838 wield_scale = {x = 1, y = 1, z = 1 + 1/16},
839 liquids_pointable = false,
840 on_place = function(itemstack, placer, pointed_thing)
841 if not pointed_thing.type == "node" then
844 local sneak = placer:get_player_control().sneak
845 local noddef = registered_nodes[get_node(pointed_thing.under).name]
846 if not sneak and noddef.on_rightclick then
847 minetest.item_place(itemstack, placer, pointed_thing)
851 local _,worked = minetest.item_place(ItemStack("redstone:dust_0"), placer, pointed_thing)
853 itemstack:take_item()
859 --power levels r_max-1 being the highest
860 local d_max = r_max-1
863 local color = floor(255 * (i/d_max))
865 minetest.register_node("redstone:dust_"..i,{
866 description = "Redstone Dust",
867 wield_image = "redstone_dust_item.png",
869 "redstone_dust_main.png^[colorize:red:"..color, "redstone_turn.png^[colorize:red:"..color,
870 "redstone_t.png^[colorize:red:"..color, "redstone_cross.png^[colorize:red:"..color
873 drawtype = "raillike",
875 sunlight_propagates = true,
876 is_ground_content = false,
878 node_placement_prediction = "",
881 fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
883 sounds = main.stoneSound(),
884 groups={dig_immediate=1,attached_node=1,redstone_dust=1,redstone=1,redstone_power=i},
885 drop="redstone:dust",
886 on_construct = function(pos)
887 data_injection(pos,{dust=i})
888 --instruction_rebuild(pos)
891 after_destruct = function(pos)
892 data_injection(pos,nil)
893 --instruction_rebuild(pos,true)
896 connects_to = {"group:redstone"},
899 minetest.register_lbm({
900 name = "redstone:"..i,
901 nodenames = {"redstone:dust_"..i},
902 run_at_every_load = true,
903 action = function(pos)
904 data_injection(pos,{dust=i})
905 --minetest.after(0,function()
906 --initial_instruction_build(pos)