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 = {}
43 redstone.player_detector_add = function(pos)
44 player_detection_table[minetest.serialize(pos)] = pos
47 redstone.player_detector_remove = function(pos)
48 player_detection_table[minetest.serialize(pos)] = nil
52 -- enables mods to create data functions
53 function redstone.register_activator(data)
54 activator_table[data.name] = {
55 activate = data.activate,
56 deactivate = data.deactivate
60 -- enables mods to create data functions
61 function redstone.register_capacitor(data)
62 capacitor_table[data.name] = {
68 local path = minetest.get_modpath("redstone")
69 dofile(path.."/functions.lua")
70 dofile(path.."/torch.lua")
71 dofile(path.."/lever.lua")
72 dofile(path.."/button.lua")
73 dofile(path.."/repeater.lua")
74 dofile(path.."/light.lua")
75 dofile(path.."/piston.lua")
76 --dofile(path.."/comparator.lua")
77 dofile(path.."/craft.lua")
78 --dofile(path.."/ore.lua")
79 dofile(path.."/inverter.lua")
80 dofile(path.."/player_detector.lua")
81 dofile(path.."/space_maker.lua")
82 --dofile(path.."/pressure_plate.lua")
83 dofile(path.."/capacitors.lua")
84 dofile(path.."/breaker.lua")
85 dofile(path.."/detector.lua")
88 --this is written out manually so that
89 --math.abs is not needed
93 {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
94 {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
96 {x=0, y=1, z=0}, {x= 0, y=-1, z=0},
98 {x=1, y=1, z=0}, {x=-1, y=1, z= 0},
99 {x=0, y=1, z=1}, {x= 0, y=1, z=-1},
101 {x=1, y=-1, z=0}, {x=-1, y=-1, z= 0},
102 {x=0, y=-1, z=1}, {x= 0, y=-1, z=-1},
105 --thanks to RhodiumToad for helping me figure out a good method to do this
107 local pool = {} -- this holds all redstone data (literal 3d virtual memory map)
110 local function data_injection(pos,data)
111 -- add data into 3d memory
113 if not pool[pos.x] then pool[pos.x] = {} end
114 if not pool[pos.x][pos.y] then pool[pos.x][pos.y] = {} end
115 pool[pos.x][pos.y][pos.z] = data
116 --delete data from 3d memory
118 if pool and pool[pos.x] and pool[pos.x][pos.y] then
119 pool[pos.x][pos.y][pos.z] = data
120 if pool[pos.x][pos.y] and not next(pool[pos.x][pos.y]) then
121 pool[pos.x][pos.y] = nil
122 -- only run this if y axis is empty
123 if pool[pos.x] and not next(pool[pos.x]) then
134 ___ \ / ___ ,'\_ | .-. \ /|
135 \ / | |,'__ \ ,'\_ | \ | | | | ,' |_ /|
136 _ | | | |\/ \ \ | \ | |\_| _ | |_| | _ '-. .-',' |_ _
137 // | | | |____| | | |\_|| |__ // | | ,'_`. | | '-. .-',' `. ,'\_
138 \\_| |_,' .-, _ | | | | |\ \ // .| |\_/ | / \ || | | | / |\ \| \
139 `-. .-'| |/ / | | | | | | \ \// | | | | | || | | | | |_\ || |\_|
140 | | | || \_| | | | /_\ \ / | |` | | | || | | | | .---'| |
141 | | | |\___,_\ /_\ _ // | | | \_/ || | | | | | /\| |
142 /_\ | | //_____// .||` _ `._,' | | | | \ `-' /| |
143 /_\ `------' \ | /-\ND _ `.\ | | `._,' /_\
146 / |__| /_\ |\ /| |_) |_ |_)
147 \__ | | / \ | \/ | |_) |__ | \
157 local function create_boundary_box(pos)
159 for x = pos.x-16,pos.x+16 do
161 for y = pos.y-16,pos.y+16 do
163 for z = pos.z-16,pos.z+16 do
164 temp_pool = pool[x][y][z]
166 if not table_3d[x] then table_3d[x] = {} end
167 if not table_3d[x][y] then table_3d[x][y] = {} end
169 if (x == pos.x-16 or x == pos.x+16 or
170 y == pos.y-16 or y == pos.y+16 or
171 z == pos.z-16 or z == pos.z+16) and
172 temp_pool.dust and temp_pool.dust > 1 then
173 table_3d[x][y][z] = {torch=temp_pool.dust}
175 if temp_pool.dust then
176 table_3d[x][y][z] = {dust=0,origin=temp_pool.dust}
178 table_3d[x][y][z] = temp_pool
192 local function capacitor_pathfind(source,mem_map)
193 for _,order in pairs(order) do
195 i = add_vec(source,order)
196 if not mem_map[i.x] then mem_map[i.x] = {} end
197 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
199 if not mem_map[i.x][i.y][i.z] then
201 if i and pool and pool[i.x] and pool[i.x][i.y] and pool[i.x][i.y][i.z] then
202 index = pool[i.x][i.y][i.z]
203 if index.capacitor then
204 mem_map[i.x][i.y][i.z] = {name = index.name, capacitor = 0, source = index.source}
208 capacitor_pathfind(i,mem_map)
219 local function capacitor_sniff(pos)
221 table_3d = capacitor_pathfind(pos,table_3d)
222 found = table_3d.found
225 for x,datax in pairs(table_3d) do
226 for y,datay in pairs(datax) do
227 for z,data in pairs(datay) do
228 temp_pool = pool[x][y][z]
230 temp_pool.capacitor = 1
231 if capacitor_table[temp_pool.name] then
232 swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].on})
233 redstone.update(new_vec(x,y,z))
240 for x,datax in pairs(table_3d) do
241 for y,datay in pairs(datax) do
242 for z,data in pairs(datay) do
243 temp_pool = pool[x][y][z]
245 temp_pool.capacitor = 0
246 if capacitor_table[temp_pool.name] then
247 swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].off})
248 redstone.update(new_vec(x,y,z))
263 local non_directional_activator = function(pos)
264 if pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z] then
265 temp_pool = pool[pos.x][pos.y][pos.z]
267 for _,order in pairs(order) do
268 n_pos = add_vec(pos,order)
269 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
270 temp_pool2 = pool[n_pos.x][n_pos.y][n_pos.z]
272 if (not temp_pool2.directional_activator and temp_pool2.torch) or
273 (temp_pool2.dust and temp_pool2.dust > 0) or
274 (temp_pool2.torch_directional and vector.equals(temp_pool2.output, pos)) then
275 if activator_table[temp_pool.name] and activator_table[temp_pool.name].activate then
276 activator_table[temp_pool.name].activate(pos)
283 if activator_table[temp_pool.name] and activator_table[temp_pool.name].deactivate then
284 activator_table[temp_pool.name].deactivate(pos)
290 -- directional activators
296 local directional_activator = function(pos)
303 if not (pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z]) then return end
305 temp_pool = pool[pos.x][pos.y][pos.z]
307 if not temp_pool then ignore = true end
310 input = temp_pool.input
313 if not input then ignore = true end
316 input = temp_pool.input
319 if not ignore and pool and pool[input.x] and pool[input.x][input.y] and pool[input.x][input.y][input.z] then
320 temp_pool2 = pool[input.x][input.y][input.z]
325 if not temp_pool2 then ignore = true end
327 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
328 (not temp_pool2.directional_activator and temp_pool2.torch) or (temp_pool2.capacitor and temp_pool2.capacitor > 0)) then
329 if activator_table[temp_pool.name] and activator_table[temp_pool.name].activate then
330 activator_table[temp_pool.name].activate(pos)
336 if activator_table[temp_pool.name] and activator_table[temp_pool.name].deactivate then
337 activator_table[temp_pool.name].deactivate(pos)
341 --make redstone wire pass on current one level lower than it is
344 local passed_on_level
346 local function redstone_distribute(pos,power,mem_map,output)
350 --directional torches
355 if mem_map.dust[x] and mem_map.dust[x][y] and mem_map.dust[x][y][z] then
356 if mem_map.dust[x][y][z].dust < power then
357 mem_map.dust[x][y][z].dust = power
358 redstone_distribute(new_vec(x,y,z),power,mem_map,nil)
363 for _,order in pairs(order) do
364 i = add_vec(pos,order)
368 if mem_map.dust[x] and mem_map.dust[x][y] and mem_map.dust[x][y][z] then
369 if mem_map.dust[x][y][z].dust < power then
370 mem_map.dust[x][y][z].dust = power
371 redstone_distribute(new_vec(x,y,z),power,mem_map,nil)
390 _,' _o\ ,::.`:' ; ; . '
394 `.______,-,----._ ,' ;: ,/ , ,`
395 / /,-';' \ ; `: ,'/,::.:::
396 ,',;-'-'_,--; ; :. ,',',;::::::
397 ( /___,-' `. ;::,,'o/ ,:::::::
406 local function dust_sniff(pos,mem_map,boundary,single,origin,ignore)
408 --print("all position index--")
409 for _,order in pairs(order) do
410 i = add_vec(pos,order)
412 if not mem_map[i.x] then mem_map[i.x] = {} end
413 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
415 if not mem_map[i.x][i.y][i.z] then
416 if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
417 index = boundary[i.x][i.y][i.z]
421 mem_map[i.x][i.y][i.z] = true
423 if not mem_map.dust[i.x] then mem_map.dust[i.x] = {} end
424 if not mem_map.dust[i.x][i.y] then mem_map.dust[i.x][i.y] = {} end
426 mem_map.dust[i.x][i.y][i.z] = index
428 dust_sniff(i,mem_map,boundary)
430 elseif index.torch and index.torch > 1 then
431 if index.torch_directional and vec_equals(pos,index.output) then
433 mem_map[i.x][i.y][i.z] = true
435 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
436 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
438 mem_map.torch[i.x][i.y][i.z] = index
441 elseif not index.torch_directional then
443 mem_map[i.x][i.y][i.z] = true
445 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
446 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
448 mem_map.torch[i.x][i.y][i.z] = index
452 if index.activator then
453 mem_map[i.x][i.y][i.z] = true
455 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
456 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
458 mem_map.activator[i.x][i.y][i.z] = index
459 elseif index.directional_activator and vec_equals(pos,index.input) then
461 mem_map[i.x][i.y][i.z] = true
463 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
464 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
466 mem_map.activator[i.x][i.y][i.z] = index
472 --print("single position index")
477 if not mem_map[i.x] then mem_map[i.x] = {} end
478 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
480 if not mem_map[i.x][i.y][i.z] then
481 if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
482 index = boundary[i.x][i.y][i.z]
485 mem_map[i.x][i.y][i.z] = true
487 if not mem_map.dust[i.x] then mem_map.dust[i.x] = {} end
488 if not mem_map.dust[i.x][i.y] then mem_map.dust[i.x][i.y] = {} end
490 mem_map.dust[i.x][i.y][i.z] = index
492 dust_sniff(i,mem_map,boundary)
494 elseif index.torch and index.torch > 1 then
495 if index.torch_directional and (vec_equals(origin,index.output) or ignore) then
497 mem_map[i.x][i.y][i.z] = true
499 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
500 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
502 mem_map.torch[i.x][i.y][i.z] = index
505 elseif not index.torch_directional then
507 mem_map[i.x][i.y][i.z] = true
509 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
510 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
512 mem_map.torch[i.x][i.y][i.z] = index
516 if index.activator then
517 mem_map[i.x][i.y][i.z] = true
519 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
520 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
522 mem_map.activator[i.x][i.y][i.z] = index
523 elseif index.directional_activator and (vec_equals(origin,index.input) or ignore) then
525 mem_map[i.x][i.y][i.z] = true
527 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
528 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
530 mem_map.activator[i.x][i.y][i.z] = index
538 --make all power sources push power out
548 local function calculate(pos,is_capacitor)
549 if not is_capacitor then
550 boundary = create_boundary_box(pos)
555 dust_map.activator = {}
557 dust_detected = false
561 if boundary[pos.x] and boundary[pos.x][pos.y] and boundary[pos.x][pos.y][pos.z] then
562 if boundary[pos.x][pos.y][pos.z].torch_directional or boundary[pos.x][pos.y][pos.z].directional_activator then
567 -- sniff all possible dust within boundaries
568 if not directional then
569 dust_sniff(pos,dust_map,boundary)
570 for _,pos2 in pairs(order) do
571 pos3 = add_vec(pos,pos2)
572 if boundary[pos3.x] and boundary[pos3.x][pos3.y] and boundary[pos3.x][pos3.y][pos3.z] and
573 not (dust_map[pos3.x] and dust_map[pos3.x][pos3.y] and dust_map[pos3.x][pos3.y][pos3.z]) then
574 temp_pool3 = boundary[pos3.x][pos3.y][pos3.z]
575 if temp_pool3.dust then
576 dust_sniff(pos3,dust_map,boundary)
581 dust_sniff(pos,dust_map,boundary,true,pos,true)
583 local input = boundary[pos.x][pos.y][pos.z].input
584 local output = boundary[pos.x][pos.y][pos.z].output
586 if input and boundary[input.x] and boundary[input.x][input.y] and boundary[input.x][input.y][input.z] then
587 dust_sniff(input,dust_map,boundary,true,pos)
589 if output and boundary[output.x] and boundary[output.x][output.y] and boundary[output.x][output.y][output.z] then
590 dust_sniff(output,dust_map,boundary,true,pos)
594 for x,datax in pairs(dust_map.torch) do
595 for y,datay in pairs(datax) do
596 for z,data in pairs(datay) do
598 if data.torch_directional then
599 redstone_distribute(new_vec(x,y,z),data.torch,dust_map,data.output)
601 redstone_distribute(new_vec(x,y,z),data.torch,dust_map)
608 --set dust, set pool memory
609 for x,datax in pairs(dust_map.dust) do
610 for y,datay in pairs(datax) do
611 for z,data in pairs(datay) do
612 if data.dust and data.dust ~= data.origin then
613 swap_node(new_vec(x,y,z),{name="redstone:dust_"..data.dust})
614 data_injection(new_vec(x,y,z),data)
621 --this must be run at the end
622 for x,datax in pairs(dust_map.activator) do
623 for y,datay in pairs(datax) do
624 for z,data in pairs(datay) do
625 if data.directional_activator then
626 directional_activator(new_vec(x,y,z))
627 elseif data.activator then
628 non_directional_activator(new_vec(x,y,z))
639 function redstone.inject(pos,data)
640 data_injection(pos,data)
649 local function player_detector_calculation()
650 for _,pos in pairs(player_detection_table) do
651 level = pool[pos.x][pos.y][pos.z].torch
653 for _,player in ipairs(minetest.get_connected_players()) do
654 pos2 = player:get_pos()
655 power = floor(17-vector_distance(pos2,pos))
658 elseif power < 0 then
668 swap_node(pos,{name="redstone:player_detector_"..max})
669 redstone.inject(pos,{
670 name = "redstone:player_detector_"..max,
679 local recursion_check = {}
682 function redstone.update(pos,is_capacitor)
683 local count = table.getn(queue)
684 queue[count+1] = {pos=pos,is_capacitor=is_capacitor}
686 local s_pos = minetest.serialize(pos)
687 if not recursion_check[s_pos] then
688 recursion_check[s_pos] = 0
690 recursion_check[s_pos] = recursion_check[s_pos] + 1
691 if recursion_check[s_pos] > 25 then
692 --print(recursion_check[s_pos])
693 minetest.after(0,function()
694 bad_node = minetest.get_node(pos).name
695 bad_node = minetest.get_node_drops(bad_node, "main:rubypick")
696 for _,nodey in pairs(bad_node) do
697 minetest.throw_item(pos,nodey)
699 minetest.remove_node(pos)
700 data_injection(pos,nil)
707 --calculate(pos,is_capacitor)
709 local dtime_goal = 0.02
711 minetest.register_globalstep(function(dtime)
714 if dtime > dtime_goal then
715 sleep = dtime - dtime_goal
719 player_detector_calculation()
721 for index,element in pairs(queue) do
722 calculate(element.pos,element.is_capacitor)
726 --recursion_check = {}
728 sleep = sleep - dtime
736 ----------------------------------------------------------------------------
770 minetest.register_craftitem("redstone:dust", {
771 description = "Redstone Dust",
772 inventory_image = "redstone_dust_item.png",
773 wield_image = "redstone_dust_item.png",
774 wield_scale = {x = 1, y = 1, z = 1 + 1/16},
775 liquids_pointable = false,
776 on_place = function(itemstack, placer, pointed_thing)
777 if not pointed_thing.type == "node" then
780 local sneak = placer:get_player_control().sneak
781 local noddef = registered_nodes[get_node(pointed_thing.under).name]
782 if not sneak and noddef.on_rightclick then
783 minetest.item_place(itemstack, placer, pointed_thing)
787 local _,worked = minetest.item_place(ItemStack("redstone:dust_0"), placer, pointed_thing)
789 itemstack:take_item()
795 --15 power levels 15 being the highest
798 local color = floor(255 * (i/15))
800 minetest.register_node("redstone:dust_"..i,{
801 description = "Redstone Dust",
802 wield_image = "redstone_dust_item.png",
804 "redstone_dust_main.png^[colorize:red:"..color, "redstone_turn.png^[colorize:red:"..color,
805 "redstone_t.png^[colorize:red:"..color, "redstone_cross.png^[colorize:red:"..color
808 drawtype = "raillike",
810 sunlight_propagates = true,
811 is_ground_content = false,
813 node_placement_prediction = "",
816 fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
818 sounds = main.stoneSound(),
819 groups={dig_immediate=1,attached_node=1,redstone_dust=1,redstone=1,redstone_power=i},
820 drop="redstone:dust",
821 on_construct = function(pos)
822 data_injection(pos,{dust=i})
825 after_destruct = function(pos)
826 data_injection(pos,nil)
829 connects_to = {"group:redstone"},
832 minetest.register_lbm({
833 name = "redstone:"..i,
834 nodenames = {"redstone:dust_"..i},
835 run_at_every_load = true,
836 action = function(pos)
837 data_injection(pos,{dust=i})