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
27 local new_vec = vector.new
28 local add_vec = vector.add
29 local sub_vec = vector.subtract
30 local vector_distance = vector.distance
31 local vec_equals = vector.equals
33 local activator_table = {} -- this holds the translation data of activator tables (activator functions)
34 local capacitor_table = {}
35 local player_detection_table = {}
40 redstone.player_detector_add = function(pos)
41 player_detection_table[minetest.serialize(pos)] = pos
44 redstone.player_detector_remove = function(pos)
45 player_detection_table[minetest.serialize(pos)] = nil
49 -- enables mods to create data functions
50 function redstone.register_activator(data)
51 activator_table[data.name] = {
52 activate = data.activate,
53 deactivate = data.deactivate
57 -- enables mods to create data functions
58 function redstone.register_capacitor(data)
59 capacitor_table[data.name] = {
65 local path = minetest.get_modpath("redstone")
66 dofile(path.."/functions.lua")
67 dofile(path.."/torch.lua")
68 dofile(path.."/lever.lua")
69 dofile(path.."/button.lua")
70 dofile(path.."/repeater.lua")
71 dofile(path.."/light.lua")
72 dofile(path.."/piston.lua")
73 --dofile(path.."/comparator.lua")
74 dofile(path.."/craft.lua")
75 --dofile(path.."/ore.lua")
76 dofile(path.."/inverter.lua")
77 dofile(path.."/player_detector.lua")
78 dofile(path.."/space_maker.lua")
79 --dofile(path.."/pressure_plate.lua")
80 dofile(path.."/capacitors.lua")
83 --this is written out manually so that
84 --math.abs is not needed
86 {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
87 {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
89 {x=0, y=1, z=0}, {x= 0, y=-1, z=0},
91 {x=1, y=1, z=0}, {x=-1, y=1, z= 0},
92 {x=0, y=1, z=1}, {x= 0, y=1, z=-1},
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},
98 --thanks to RhodiumToad for helping me figure out a good method to do this
100 local pool = {} -- this holds all redstone data (literal 3d virtual memory map)
103 local function data_injection(pos,data)
104 -- add data into 3d memory
106 if not pool[pos.x] then pool[pos.x] = {} end
107 if not pool[pos.x][pos.y] then pool[pos.x][pos.y] = {} end
108 pool[pos.x][pos.y][pos.z] = data
109 --delete data from 3d memory
111 if pool and pool[pos.x] and pool[pos.x][pos.y] then
112 pool[pos.x][pos.y][pos.z] = data
113 if pool[pos.x][pos.y] and not next(pool[pos.x][pos.y]) then
114 pool[pos.x][pos.y] = nil
115 -- only run this if y axis is empty
116 if pool[pos.x] and not next(pool[pos.x]) then
126 local function create_boundary_box(pos)
128 for x = pos.x-9,pos.x+9 do
130 for y = pos.y-9,pos.y+9 do
132 for z = pos.z-9,pos.z+9 do
133 temp_pool = pool[x][y][z]
135 if not table_3d[x] then table_3d[x] = {} end
136 if not table_3d[x][y] then table_3d[x][y] = {} end
138 if (x == pos.x-9 or x == pos.x+9 or
139 y == pos.y-9 or y == pos.y+9 or
140 z == pos.z-9 or z == pos.z+9) and
141 temp_pool.dust and temp_pool.dust > 1 then
142 table_3d[x][y][z] = {torch=temp_pool.dust}
144 if temp_pool.dust then
145 table_3d[x][y][z] = {dust=0,origin=temp_pool.dust}
147 table_3d[x][y][z] = temp_pool
161 local function capacitor_pathfind(source,mem_map)
162 for _,order in pairs(order) do
164 i = add_vec(source,order)
165 if not mem_map[i.x] then mem_map[i.x] = {} end
166 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
168 if not mem_map[i.x][i.y][i.z] then
170 if i and pool and pool[i.x] and pool[i.x][i.y] and pool[i.x][i.y][i.z] then
171 index = pool[i.x][i.y][i.z]
172 if index.capacitor then
173 mem_map[i.x][i.y][i.z] = {name = index.name, capacitor = 0, source = index.source}
177 capacitor_pathfind(i,mem_map)
188 local function capacitor_sniff(pos)
190 table_3d = capacitor_pathfind(pos,table_3d)
191 found = table_3d.found
194 for x,datax in pairs(table_3d) do
195 for y,datay in pairs(datax) do
196 for z,data in pairs(datay) do
197 temp_pool = pool[x][y][z]
199 temp_pool.capacitor = 1
200 if capacitor_table[temp_pool.name] then
201 swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].on})
202 redstone.update(new_vec(x,y,z))
209 for x,datax in pairs(table_3d) do
210 for y,datay in pairs(datax) do
211 for z,data in pairs(datay) do
212 temp_pool = pool[x][y][z]
214 temp_pool.capacitor = 0
215 if capacitor_table[temp_pool.name] then
216 swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].off})
217 redstone.update(new_vec(x,y,z))
232 local non_directional_activator = function(pos)
233 if pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z] then
234 temp_pool = pool[pos.x][pos.y][pos.z]
235 for _,order in pairs(order) do
236 n_pos = add_vec(pos,order)
237 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
238 temp_pool2 = pool[n_pos.x][n_pos.y][n_pos.z]
240 if (not temp_pool2.directional_activator and temp_pool2.torch) or
241 (temp_pool2.dust and temp_pool2.dust > 0) or
242 (temp_pool2.torch_directional and vector.equals(temp_pool2.output, pos)) then
243 if activator_table[temp_pool.name].activate then
244 activator_table[temp_pool.name].activate(pos)
251 if activator_table[temp_pool.name].deactivate then
252 activator_table[temp_pool.name].deactivate(pos)
257 -- directional activators
263 local directional_activator = function(pos)
270 --if not (pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z]) then return end
272 temp_pool = pool[pos.x][pos.y][pos.z]
274 if not temp_pool then ignore = true end
277 input = temp_pool.input
280 if not input then ignore = true end
283 input = temp_pool.input
286 if not ignore and pool and pool[input.x] and pool[input.x][input.y] and pool[input.x][input.y][input.z] then
287 temp_pool2 = pool[input.x][input.y][input.z]
292 if not temp_pool2 then ignore = true end
294 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
295 (not temp_pool2.directional_activator and temp_pool2.torch) or (temp_pool2.capacitor and temp_pool2.capacitor > 0)) then
296 if activator_table[temp_pool.name].activate then
297 activator_table[temp_pool.name].activate(pos)
303 if activator_table[temp_pool.name].deactivate then
304 activator_table[temp_pool.name].deactivate(pos)
308 --make redstone wire pass on current one level lower than it is
311 local passed_on_level
312 local function redstone_pathfinder(source,source_level,mem_map,output)
313 if not source_level then return end
314 --directional torches
317 if i and mem_map and mem_map[i.x] and mem_map[i.x][i.y] and mem_map[i.x][i.y][i.z] then
318 index = mem_map[i.x][i.y][i.z]
321 passed_on_level = source_level - 1
322 if passed_on_level > 0 then
323 mem_map[i.x][i.y][i.z].dust = passed_on_level
324 redstone_pathfinder(i,passed_on_level,mem_map,nil)
330 for _,order in pairs(order) do
331 i = add_vec(source,order)
332 if i and mem_map and mem_map[i.x] and mem_map[i.x][i.y] and mem_map[i.x][i.y][i.z] then
333 index = mem_map[i.x][i.y][i.z]
335 passed_on_level = source_level - 1
336 if passed_on_level > 0 and index.dust < source_level then
337 mem_map[i.x][i.y][i.z].dust = passed_on_level
338 redstone_pathfinder(i,passed_on_level,mem_map,nil)
358 _,' _o\ ,::.`:' ; ; . '
362 `.______,-,----._ ,' ;: ,/ , ,`
363 / /,-';' \ ; `: ,'/,::.:::
364 ,',;-'-'_,--; ; :. ,',',;::::::
365 ( /___,-' `. ;::,,'o/ ,:::::::
374 local function dust_sniff(pos,mem_map,boundary)
375 for _,order in pairs(order) do
376 i = add_vec(pos,order)
378 if not mem_map[i.x] then mem_map[i.x] = {} end
379 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
381 if not mem_map[i.x][i.y][i.z] then
383 if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
384 index = boundary[i.x][i.y][i.z]
387 mem_map[i.x][i.y][i.z] = index
388 mem_map[i.x][i.y][i.z].sniffed = true
390 dust_sniff(i,mem_map,boundary)
392 elseif index.directional_activator and vec_equals(pos,index.input) then
393 mem_map[i.x][i.y][i.z] = index
394 mem_map[i.x][i.y][i.z].sniffed = true
395 elseif index.torch and index.torch > 1 then
396 if index.torch_directional and vec_equals(pos,index.output) then
397 mem_map[i.x][i.y][i.z] = index
398 mem_map[i.x][i.y][i.z].sniffed = true
399 elseif not index.torch_directional then
400 mem_map[i.x][i.y][i.z] = index
401 mem_map[i.x][i.y][i.z].sniffed = true
403 elseif index.activator then
404 mem_map[i.x][i.y][i.z] = index
405 mem_map[i.x][i.y][i.z].sniffed = true
413 --make all power sources push power out
423 local function calculate(pos,is_capacitor)
424 if not is_capacitor then
425 boundary = create_boundary_box(pos)
428 dust_detected = false
431 --update needs to sniff it's own position
432 if boundary[pos.x] and boundary[pos.x][pos.y] and boundary[pos.x][pos.y][pos.z] then
433 if not dust_map[pos.x] then dust_map[pos.x] = {} end
434 if not dust_map[pos.x][pos.y] then dust_map[pos.x][pos.y] = {} end
435 dust_map[pos.x][pos.y][pos.z] = boundary[pos.x][pos.y][pos.z]
438 dust_sniff(pos,dust_map,boundary)
440 -- sniff all possible dust within boundaries
441 for _,pos2 in pairs(order) do
442 pos3 = add_vec(pos,pos2)
443 if boundary[pos3.x] and boundary[pos3.x][pos3.y] and boundary[pos3.x][pos3.y][pos3.z] and
444 not (dust_map[pos3.x] and dust_map[pos3.x][pos3.y] and dust_map[pos3.x][pos3.y][pos3.z] and dust_map[pos3.x][pos3.y][pos3.z].sniffed) then
445 temp_pool3 = boundary[pos3.x][pos3.y][pos3.z]
446 if temp_pool3.dust then
447 dust_sniff(pos3,dust_map,boundary)
453 for x,datax in pairs(dust_map) do
454 for y,datay in pairs(datax) do
455 for z,data in pairs(datay) do
458 if data.torch_directional then
459 redstone_pathfinder(new_vec(x,y,z),data.torch,dust_map,data.output)
461 redstone_pathfinder(new_vec(x,y,z),data.torch,dust_map)
462 dust_map[x][y][z] = nil
469 --set dust, set pool memory
470 for x,datax in pairs(dust_map) do
471 for y,datay in pairs(datax) do
472 for z,data in pairs(datay) do
473 if data.dust and data.dust ~= data.origin then
474 swap_node(new_vec(x,y,z),{name="redstone:dust_"..data.dust})
475 data_injection(new_vec(x,y,z),data)
476 --delete the data to speed up next loop
477 dust_map[x][y][z] = nil
484 --this must be run at the end
485 for x,datax in pairs(dust_map) do
486 for y,datay in pairs(datax) do
487 for z,data in pairs(datay) do
488 if data.directional_activator then
489 directional_activator(new_vec(x,y,z))
490 elseif data.activator then
491 non_directional_activator(new_vec(x,y,z))
502 function redstone.inject(pos,data)
503 data_injection(pos,data)
507 local recursion_check = {}
509 function redstone.update(pos,is_capacitor)
510 local s_pos = minetest.serialize(pos)
511 if not recursion_check[s_pos] then
512 recursion_check[s_pos] = 0
514 recursion_check[s_pos] = recursion_check[s_pos] + 1
515 if recursion_check[s_pos] > 25 then
516 --print(recursion_check[s_pos])
517 minetest.after(0,function()
518 bad_node = minetest.get_node(pos).name
519 bad_node = minetest.get_node_drops(bad_node, "main:rubypick")
520 for _,nodey in pairs(bad_node) do
521 minetest.throw_item(pos,nodey)
523 minetest.remove_node(pos)
524 data_injection(pos,nil)
530 calculate(pos,is_capacitor)
538 local function player_detector_calculation()
539 for _,pos in pairs(player_detection_table) do
540 level = pool[pos.x][pos.y][pos.z].torch
542 for _,player in ipairs(minetest.get_connected_players()) do
543 pos2 = player:get_pos()
544 power = floor(10-vector_distance(pos2,pos))
547 elseif power < 0 then
557 swap_node(pos,{name="redstone:player_detector_"..max})
558 redstone.inject(pos,{
559 name = "redstone:player_detector_"..max,
567 minetest.register_globalstep(function()
568 player_detector_calculation()
573 ----------------------------------------------------------------------------
607 minetest.register_craftitem("redstone:dust", {
608 description = "Redstone Dust",
609 inventory_image = "redstone_dust_item.png",
610 wield_image = "redstone_dust_item.png",
611 wield_scale = {x = 1, y = 1, z = 1 + 1/16},
612 liquids_pointable = false,
613 on_place = function(itemstack, placer, pointed_thing)
614 if not pointed_thing.type == "node" then
617 local sneak = placer:get_player_control().sneak
618 local noddef = registered_nodes[get_node(pointed_thing.under).name]
619 if not sneak and noddef.on_rightclick then
620 minetest.item_place(itemstack, placer, pointed_thing)
624 local _,worked = minetest.item_place(ItemStack("redstone:dust_0"), placer, pointed_thing)
626 itemstack:take_item()
632 --8 power levels 8 being the highest
635 local coloring = floor(color)
636 minetest.register_node("redstone:dust_"..i,{
637 description = "Redstone Dust",
638 wield_image = "redstone_dust_item.png",
640 "redstone_dust_main.png^[colorize:red:"..coloring, "redstone_turn.png^[colorize:red:"..coloring,
641 "redstone_t.png^[colorize:red:"..coloring, "redstone_cross.png^[colorize:red:"..coloring
644 drawtype = "raillike",
646 sunlight_propagates = true,
647 is_ground_content = false,
649 node_placement_prediction = "",
652 fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
654 sounds = main.stoneSound(),
655 groups={dig_immediate=1,attached_node=1,redstone_dust=1,redstone=1,redstone_power=i},
656 drop="redstone:dust",
657 on_construct = function(pos)
658 data_injection(pos,{dust=i})
661 after_destruct = function(pos)
662 data_injection(pos,nil)
665 connects_to = {"group:redstone"},
669 minetest.register_lbm({
670 name = "redstone:"..i,
671 nodenames = {"redstone:dust_"..i},
672 run_at_every_load = true,
673 action = function(pos)
674 data_injection(pos,{dust=i})