2 minetest,vector,math,table,pairs,next
4 minetest,vector,math,table,pairs,next
7 local get_node = minetest.get_node
8 local get_item_group = minetest.get_item_group
9 local get_meta = minetest.get_meta
10 local facedir_to_dir = minetest.facedir_to_dir
11 local content_id = minetest.get_name_from_content_id
12 local get_content_id = minetest.get_content_id
13 local get_voxel_manip = minetest.get_voxel_manip
14 local after = minetest.after
16 local swap_node = minetest.swap_node
17 local registered_nodes
18 minetest.register_on_mods_loaded(function()
19 registered_nodes = minetest.registered_nodes
24 local floor = math.floor
25 local ceil = math.ceil
28 local new_vec = vector.new
29 local add_vec = vector.add
30 local sub_vec = vector.subtract
31 local vector_distance = vector.distance
32 local vec_equals = vector.equals
34 local activator_table = {} -- this holds the translation data of activator tables (activator functions)
35 local capacitor_table = {}
36 local player_detection_table = {}
41 redstone.player_detector_add = function(pos)
42 player_detection_table[minetest.serialize(pos)] = pos
45 redstone.player_detector_remove = function(pos)
46 player_detection_table[minetest.serialize(pos)] = nil
50 -- enables mods to create data functions
51 function redstone.register_activator(data)
52 activator_table[data.name] = {
53 activate = data.activate,
54 deactivate = data.deactivate
58 -- enables mods to create data functions
59 function redstone.register_capacitor(data)
60 capacitor_table[data.name] = {
66 local path = minetest.get_modpath("redstone")
67 dofile(path.."/functions.lua")
68 dofile(path.."/torch.lua")
69 dofile(path.."/lever.lua")
70 dofile(path.."/button.lua")
71 dofile(path.."/repeater.lua")
72 dofile(path.."/light.lua")
73 dofile(path.."/piston.lua")
74 --dofile(path.."/comparator.lua")
75 dofile(path.."/craft.lua")
76 --dofile(path.."/ore.lua")
77 dofile(path.."/inverter.lua")
78 dofile(path.."/player_detector.lua")
79 dofile(path.."/space_maker.lua")
80 --dofile(path.."/pressure_plate.lua")
81 dofile(path.."/capacitors.lua")
84 --this is written out manually so that
85 --math.abs is not needed
89 {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
90 {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
92 {x=0, y=1, z=0}, {x= 0, y=-1, z=0},
94 {x=1, y=1, z=0}, {x=-1, y=1, z= 0},
95 {x=0, y=1, z=1}, {x= 0, y=1, z=-1},
97 {x=1, y=-1, z=0}, {x=-1, y=-1, z= 0},
98 {x=0, y=-1, z=1}, {x= 0, y=-1, z=-1},
101 --thanks to RhodiumToad for helping me figure out a good method to do this
103 local pool = {} -- this holds all redstone data (literal 3d virtual memory map)
106 local function data_injection(pos,data)
107 -- add data into 3d memory
109 if not pool[pos.x] then pool[pos.x] = {} end
110 if not pool[pos.x][pos.y] then pool[pos.x][pos.y] = {} end
111 pool[pos.x][pos.y][pos.z] = data
112 --delete data from 3d memory
114 if pool and pool[pos.x] and pool[pos.x][pos.y] then
115 pool[pos.x][pos.y][pos.z] = data
116 if pool[pos.x][pos.y] and not next(pool[pos.x][pos.y]) then
117 pool[pos.x][pos.y] = nil
118 -- only run this if y axis is empty
119 if pool[pos.x] and not next(pool[pos.x]) then
129 local function create_boundary_box(pos)
131 for x = pos.x-16,pos.x+16 do
133 for y = pos.y-16,pos.y+16 do
135 for z = pos.z-16,pos.z+16 do
136 temp_pool = pool[x][y][z]
138 if not table_3d[x] then table_3d[x] = {} end
139 if not table_3d[x][y] then table_3d[x][y] = {} end
141 if (x == pos.x-16 or x == pos.x+16 or
142 y == pos.y-16 or y == pos.y+16 or
143 z == pos.z-16 or z == pos.z+16) and
144 temp_pool.dust and temp_pool.dust > 1 then
145 table_3d[x][y][z] = {torch=temp_pool.dust}
147 if temp_pool.dust then
148 table_3d[x][y][z] = {dust=0,origin=temp_pool.dust}
150 table_3d[x][y][z] = temp_pool
164 local function capacitor_pathfind(source,mem_map)
165 for _,order in pairs(order) do
167 i = add_vec(source,order)
168 if not mem_map[i.x] then mem_map[i.x] = {} end
169 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
171 if not mem_map[i.x][i.y][i.z] then
173 if i and pool and pool[i.x] and pool[i.x][i.y] and pool[i.x][i.y][i.z] then
174 index = pool[i.x][i.y][i.z]
175 if index.capacitor then
176 mem_map[i.x][i.y][i.z] = {name = index.name, capacitor = 0, source = index.source}
180 capacitor_pathfind(i,mem_map)
191 local function capacitor_sniff(pos)
193 table_3d = capacitor_pathfind(pos,table_3d)
194 found = table_3d.found
197 for x,datax in pairs(table_3d) do
198 for y,datay in pairs(datax) do
199 for z,data in pairs(datay) do
200 temp_pool = pool[x][y][z]
202 temp_pool.capacitor = 1
203 if capacitor_table[temp_pool.name] then
204 swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].on})
205 redstone.update(new_vec(x,y,z))
212 for x,datax in pairs(table_3d) do
213 for y,datay in pairs(datax) do
214 for z,data in pairs(datay) do
215 temp_pool = pool[x][y][z]
217 temp_pool.capacitor = 0
218 if capacitor_table[temp_pool.name] then
219 swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].off})
220 redstone.update(new_vec(x,y,z))
235 local non_directional_activator = function(pos)
236 if pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z] then
237 temp_pool = pool[pos.x][pos.y][pos.z]
239 for _,order in pairs(order) do
240 n_pos = add_vec(pos,order)
241 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
242 temp_pool2 = pool[n_pos.x][n_pos.y][n_pos.z]
244 if (not temp_pool2.directional_activator and temp_pool2.torch) or
245 (temp_pool2.dust and temp_pool2.dust > 0) or
246 (temp_pool2.torch_directional and vector.equals(temp_pool2.output, pos)) then
247 if activator_table[temp_pool.name] and activator_table[temp_pool.name].activate then
248 activator_table[temp_pool.name].activate(pos)
255 if activator_table[temp_pool.name] and activator_table[temp_pool.name].deactivate then
256 activator_table[temp_pool.name].deactivate(pos)
262 -- directional activators
268 local directional_activator = function(pos)
275 if not (pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z]) then return end
277 temp_pool = pool[pos.x][pos.y][pos.z]
279 if not temp_pool then ignore = true end
282 input = temp_pool.input
285 if not input then ignore = true end
288 input = temp_pool.input
291 if not ignore and pool and pool[input.x] and pool[input.x][input.y] and pool[input.x][input.y][input.z] then
292 temp_pool2 = pool[input.x][input.y][input.z]
297 if not temp_pool2 then ignore = true end
299 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
300 (not temp_pool2.directional_activator and temp_pool2.torch) or (temp_pool2.capacitor and temp_pool2.capacitor > 0)) then
301 if activator_table[temp_pool.name].activate then
302 activator_table[temp_pool.name].activate(pos)
308 if activator_table[temp_pool.name].deactivate then
309 activator_table[temp_pool.name].deactivate(pos)
313 --make redstone wire pass on current one level lower than it is
316 local passed_on_level
318 local function redstone_distribute(pos,power,mem_map,output)
322 --directional torches
327 if mem_map.dust[x] and mem_map.dust[x][y] and mem_map.dust[x][y][z] then
328 if mem_map.dust[x][y][z].dust < power then
329 mem_map.dust[x][y][z].dust = power
330 redstone_distribute(new_vec(x,y,z),power,mem_map,nil)
335 for _,order in pairs(order) do
336 i = add_vec(pos,order)
340 if mem_map.dust[x] and mem_map.dust[x][y] and mem_map.dust[x][y][z] then
341 if mem_map.dust[x][y][z].dust < power then
342 mem_map.dust[x][y][z].dust = power
343 redstone_distribute(new_vec(x,y,z),power,mem_map,nil)
362 _,' _o\ ,::.`:' ; ; . '
366 `.______,-,----._ ,' ;: ,/ , ,`
367 / /,-';' \ ; `: ,'/,::.:::
368 ,',;-'-'_,--; ; :. ,',',;::::::
369 ( /___,-' `. ;::,,'o/ ,:::::::
378 local function dust_sniff(pos,mem_map,boundary,single,origin,ignore)
380 --print("all position index--")
381 for _,order in pairs(order) do
382 i = add_vec(pos,order)
384 if not mem_map[i.x] then mem_map[i.x] = {} end
385 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
387 if not mem_map[i.x][i.y][i.z] then
388 if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
389 index = boundary[i.x][i.y][i.z]
393 mem_map[i.x][i.y][i.z] = true
395 if not mem_map.dust[i.x] then mem_map.dust[i.x] = {} end
396 if not mem_map.dust[i.x][i.y] then mem_map.dust[i.x][i.y] = {} end
398 mem_map.dust[i.x][i.y][i.z] = index
400 dust_sniff(i,mem_map,boundary)
402 elseif index.torch and index.torch > 1 then
403 if index.torch_directional and vec_equals(pos,index.output) then
405 mem_map[i.x][i.y][i.z] = true
407 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
408 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
410 mem_map.torch[i.x][i.y][i.z] = index
413 elseif not index.torch_directional then
415 mem_map[i.x][i.y][i.z] = true
417 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
418 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
420 mem_map.torch[i.x][i.y][i.z] = index
424 if index.activator then
425 mem_map[i.x][i.y][i.z] = true
427 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
428 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
430 mem_map.activator[i.x][i.y][i.z] = index
431 elseif index.directional_activator and vec_equals(pos,index.input) then
433 mem_map[i.x][i.y][i.z] = true
435 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
436 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
438 mem_map.activator[i.x][i.y][i.z] = index
444 --print("single position index")
449 if not mem_map[i.x] then mem_map[i.x] = {} end
450 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
452 if not mem_map[i.x][i.y][i.z] then
453 if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
454 index = boundary[i.x][i.y][i.z]
457 mem_map[i.x][i.y][i.z] = true
459 if not mem_map.dust[i.x] then mem_map.dust[i.x] = {} end
460 if not mem_map.dust[i.x][i.y] then mem_map.dust[i.x][i.y] = {} end
462 mem_map.dust[i.x][i.y][i.z] = index
464 dust_sniff(i,mem_map,boundary)
466 elseif index.torch and index.torch > 1 then
467 if index.torch_directional and (vec_equals(origin,index.output) or ignore) then
469 mem_map[i.x][i.y][i.z] = true
471 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
472 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
474 mem_map.torch[i.x][i.y][i.z] = index
477 elseif not index.torch_directional then
479 mem_map[i.x][i.y][i.z] = true
481 if not mem_map.torch[i.x] then mem_map.torch[i.x] = {} end
482 if not mem_map.torch[i.x][i.y] then mem_map.torch[i.x][i.y] = {} end
484 mem_map.torch[i.x][i.y][i.z] = index
488 if index.activator then
489 mem_map[i.x][i.y][i.z] = true
491 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
492 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
494 mem_map.activator[i.x][i.y][i.z] = index
495 elseif index.directional_activator and (vec_equals(origin,index.input) or ignore) then
497 mem_map[i.x][i.y][i.z] = true
499 if not mem_map.activator[i.x] then mem_map.activator[i.x] = {} end
500 if not mem_map.activator[i.x][i.y] then mem_map.activator[i.x][i.y] = {} end
502 mem_map.activator[i.x][i.y][i.z] = index
510 --make all power sources push power out
520 local function calculate(pos,is_capacitor)
521 if not is_capacitor then
522 boundary = create_boundary_box(pos)
527 dust_map.activator = {}
529 dust_detected = false
533 if boundary[pos.x] and boundary[pos.x][pos.y] and boundary[pos.x][pos.y][pos.z] then
534 if boundary[pos.x][pos.y][pos.z].torch_directional or boundary[pos.x][pos.y][pos.z].directional_activator then
539 -- sniff all possible dust within boundaries
540 if not directional then
541 dust_sniff(pos,dust_map,boundary)
542 for _,pos2 in pairs(order) do
543 pos3 = add_vec(pos,pos2)
544 if boundary[pos3.x] and boundary[pos3.x][pos3.y] and boundary[pos3.x][pos3.y][pos3.z] and
545 not (dust_map[pos3.x] and dust_map[pos3.x][pos3.y] and dust_map[pos3.x][pos3.y][pos3.z]) then
546 temp_pool3 = boundary[pos3.x][pos3.y][pos3.z]
547 if temp_pool3.dust then
548 dust_sniff(pos3,dust_map,boundary)
553 dust_sniff(pos,dust_map,boundary,true,pos,true)
555 local input = boundary[pos.x][pos.y][pos.z].input
556 local output = boundary[pos.x][pos.y][pos.z].output
558 if input and boundary[input.x] and boundary[input.x][input.y] and boundary[input.x][input.y][input.z] then
559 dust_sniff(input,dust_map,boundary,true,pos)
561 if output and boundary[output.x] and boundary[output.x][output.y] and boundary[output.x][output.y][output.z] then
562 dust_sniff(output,dust_map,boundary,true,pos)
566 for x,datax in pairs(dust_map.torch) do
567 for y,datay in pairs(datax) do
568 for z,data in pairs(datay) do
570 if data.torch_directional then
571 redstone_distribute(new_vec(x,y,z),data.torch,dust_map,data.output)
573 redstone_distribute(new_vec(x,y,z),data.torch,dust_map)
580 --set dust, set pool memory
581 for x,datax in pairs(dust_map.dust) do
582 for y,datay in pairs(datax) do
583 for z,data in pairs(datay) do
584 if data.dust and data.dust ~= data.origin then
585 swap_node(new_vec(x,y,z),{name="redstone:dust_"..data.dust})
586 data_injection(new_vec(x,y,z),data)
593 --this must be run at the end
594 for x,datax in pairs(dust_map.activator) do
595 for y,datay in pairs(datax) do
596 for z,data in pairs(datay) do
597 if data.directional_activator then
598 directional_activator(new_vec(x,y,z))
599 elseif data.activator then
600 non_directional_activator(new_vec(x,y,z))
611 function redstone.inject(pos,data)
612 data_injection(pos,data)
616 local recursion_check = {}
618 function redstone.update(pos,is_capacitor)
619 local s_pos = minetest.serialize(pos)
620 if not recursion_check[s_pos] then
621 recursion_check[s_pos] = 0
623 recursion_check[s_pos] = recursion_check[s_pos] + 1
624 if recursion_check[s_pos] > 25 then
625 --print(recursion_check[s_pos])
626 minetest.after(0,function()
627 bad_node = minetest.get_node(pos).name
628 bad_node = minetest.get_node_drops(bad_node, "main:rubypick")
629 for _,nodey in pairs(bad_node) do
630 minetest.throw_item(pos,nodey)
632 minetest.remove_node(pos)
633 data_injection(pos,nil)
639 calculate(pos,is_capacitor)
647 local function player_detector_calculation()
648 for _,pos in pairs(player_detection_table) do
649 level = pool[pos.x][pos.y][pos.z].torch
651 for _,player in ipairs(minetest.get_connected_players()) do
652 pos2 = player:get_pos()
653 power = floor(17-vector_distance(pos2,pos))
656 elseif power < 0 then
666 swap_node(pos,{name="redstone:player_detector_"..max})
667 redstone.inject(pos,{
668 name = "redstone:player_detector_"..max,
676 minetest.register_globalstep(function()
677 player_detector_calculation()
682 ----------------------------------------------------------------------------
716 minetest.register_craftitem("redstone:dust", {
717 description = "Redstone Dust",
718 inventory_image = "redstone_dust_item.png",
719 wield_image = "redstone_dust_item.png",
720 wield_scale = {x = 1, y = 1, z = 1 + 1/16},
721 liquids_pointable = false,
722 on_place = function(itemstack, placer, pointed_thing)
723 if not pointed_thing.type == "node" then
726 local sneak = placer:get_player_control().sneak
727 local noddef = registered_nodes[get_node(pointed_thing.under).name]
728 if not sneak and noddef.on_rightclick then
729 minetest.item_place(itemstack, placer, pointed_thing)
733 local _,worked = minetest.item_place(ItemStack("redstone:dust_0"), placer, pointed_thing)
735 itemstack:take_item()
741 --15 power levels 15 being the highest
744 local color = floor(255 * (i/15))
746 minetest.register_node("redstone:dust_"..i,{
747 description = "Redstone Dust",
748 wield_image = "redstone_dust_item.png",
750 "redstone_dust_main.png^[colorize:red:"..color, "redstone_turn.png^[colorize:red:"..color,
751 "redstone_t.png^[colorize:red:"..color, "redstone_cross.png^[colorize:red:"..color
754 drawtype = "raillike",
756 sunlight_propagates = true,
757 is_ground_content = false,
759 node_placement_prediction = "",
762 fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
764 sounds = main.stoneSound(),
765 groups={dig_immediate=1,attached_node=1,redstone_dust=1,redstone=1,redstone_power=i},
766 drop="redstone:dust",
767 on_construct = function(pos)
768 data_injection(pos,{dust=i})
771 after_destruct = function(pos)
772 data_injection(pos,nil)
775 connects_to = {"group:redstone"},
778 minetest.register_lbm({
779 name = "redstone:"..i,
780 nodenames = {"redstone:dust_"..i},
781 run_at_every_load = true,
782 action = function(pos)
783 data_injection(pos,{dust=i})