]> git.lizzy.rs Git - Crafter.git/blob - mods/redstone/init.lua
78afbb40fe38794f759e735ddead4ad49e9e16bf
[Crafter.git] / mods / redstone / init.lua
1 local 
2 minetest,vector,math,table,pairs,next
3 =
4 minetest,vector,math,table,pairs,next
5
6 -- minetest class
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
15
16 local swap_node       = minetest.swap_node
17 local registered_nodes
18 minetest.register_on_mods_loaded(function()
19         registered_nodes  = minetest.registered_nodes
20 end)
21
22 -- math class
23 local abs   = math.abs
24 local floor = math.floor
25
26 -- vector library
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
32
33 local activator_table = {} -- this holds the translation data of activator tables (activator functions)
34 local capacitor_table = {}
35
36 -- redstone class
37 redstone = {}
38
39 -- enables mods to create data functions
40 function redstone.register_activator(data)
41         activator_table[data.name] = {
42                 activate   = data.activate,
43                 deactivate = data.deactivate
44         }
45 end
46
47 -- enables mods to create data functions
48 function redstone.register_capacitor(data)
49         capacitor_table[data.name] = {
50                 off = data.off,
51                 on  = data.on
52         }
53 end
54
55 local path = minetest.get_modpath("redstone")
56 dofile(path.."/functions.lua")
57 dofile(path.."/torch.lua")
58 dofile(path.."/lever.lua")
59 dofile(path.."/button.lua")
60 dofile(path.."/repeater.lua")
61 dofile(path.."/light.lua")
62 dofile(path.."/piston.lua")
63 --dofile(path.."/comparator.lua")
64 dofile(path.."/craft.lua")
65 --dofile(path.."/ore.lua")
66 dofile(path.."/inverter.lua")
67 --dofile(path.."/player_detector.lua")
68 dofile(path.."/space_maker.lua")
69 --dofile(path.."/pressure_plate.lua")
70 dofile(path.."/capacitors.lua")
71
72
73 --this is written out manually so that
74 --math.abs is not needed
75 local order = {
76         {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
77         {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
78
79         {x=0, y=1, z=0}, {x= 0, y=-1, z=0},
80
81         {x=1, y=1, z=0}, {x=-1, y=1, z= 0},
82         {x=0, y=1, z=1}, {x= 0, y=1, z=-1},
83
84         {x=1, y=-1, z=0}, {x=-1, y=-1, z= 0},
85         {x=0, y=-1, z=1}, {x= 0, y=-1, z=-1},
86 }
87
88 --thanks to RhodiumToad for helping me figure out a good method to do this
89
90 local pool = {} -- this holds all redstone data (literal 3d virtual memory map)
91
92
93 local function data_injection(pos,data)
94         -- add data into 3d memory
95         if data then
96                 if not pool[pos.x] then pool[pos.x] = {} end
97                 if not pool[pos.x][pos.y] then pool[pos.x][pos.y] = {} end
98                 pool[pos.x][pos.y][pos.z] = data
99         --delete data from 3d memory
100         else
101                 if pool and pool[pos.x] and pool[pos.x][pos.y] then
102                         pool[pos.x][pos.y][pos.z] = data
103                         if pool[pos.x][pos.y] and not next(pool[pos.x][pos.y]) then
104                                 pool[pos.x][pos.y] = nil
105                                 -- only run this if y axis is empty
106                                 if pool[pos.x] and not next(pool[pos.x]) then
107                                         pool[pos.x] = nil
108                                 end
109                         end
110                 end
111         end
112 end
113
114 local table_3d
115 local temp_pool
116 local function create_boundary_box(pos)
117         table_3d = {}
118         for x = pos.x-9,pos.x+9 do
119                 if pool[x] then
120                         for y = pos.y-9,pos.y+9 do
121                                 if pool[x][y] then
122                                         for z = pos.z-9,pos.z+9 do
123                                                 temp_pool = pool[x][y][z]
124                                                 if temp_pool then
125                                                         if not table_3d[x] then table_3d[x] = {} end
126                                                         if not table_3d[x][y] then table_3d[x][y] = {} end
127
128                                                         if (x == pos.x-9 or x == pos.x+9 or 
129                                                         y == pos.y-9 or y == pos.y+9 or 
130                                                         z == pos.z-9 or z == pos.z+9) and 
131                                                         temp_pool.dust and temp_pool.dust > 1 then
132                                                                 table_3d[x][y][z] = {torch=temp_pool.dust}
133                                                         else
134                                                                 if temp_pool.dust then
135                                                                         table_3d[x][y][z] = {dust=0,origin=temp_pool.dust}
136                                                                 else
137                                                                         table_3d[x][y][z] = temp_pool
138                                                                 end
139                                                         end
140                                                 end
141                                         end
142                                 end
143                         end
144                 end
145         end
146         return(table_3d)
147 end
148
149 local i
150 local index
151 local function capacitor_pathfind(source,mem_map)
152         for _,order in pairs(order) do
153
154                 i = add_vec(source,order)
155                 if not mem_map[i.x] then mem_map[i.x] = {} end
156                 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
157
158                 if not mem_map[i.x][i.y][i.z] then
159
160                         if i and pool and pool[i.x] and pool[i.x][i.y] and pool[i.x][i.y][i.z] then
161                                 index = pool[i.x][i.y][i.z]
162                                 if index.capacitor then
163                                         mem_map[i.x][i.y][i.z] = {name = index.name, capacitor = 0, source = index.source}
164                                         if index.source then
165                                                 mem_map.found = true
166                                         end
167                                         capacitor_pathfind(i,mem_map)
168                                 end
169                         end
170                 end
171         end
172         return mem_map
173 end
174
175 local table_3d
176 local found
177 local temp_pool
178 local function capacitor_sniff(pos)
179         table_3d = {}
180         table_3d = capacitor_pathfind(pos,table_3d)
181         found = table_3d.found
182         table_3d.found = nil
183         if found then
184                 for x,datax in pairs(table_3d) do
185                         for y,datay in pairs(datax) do
186                                 for z,data in pairs(datay) do
187                                         temp_pool = pool[x][y][z]
188                                         if temp_pool then
189                                                 temp_pool.capacitor = 1
190                                                 if capacitor_table[temp_pool.name] then
191                                                         swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].on})
192                                                         redstone.update(new_vec(x,y,z))
193                                                 end
194                                         end
195                                 end
196                         end
197                 end
198         else
199                 for x,datax in pairs(table_3d) do
200                         for y,datay in pairs(datax) do
201                                 for z,data in pairs(datay) do
202                                         temp_pool = pool[x][y][z]
203                                         if temp_pool then
204                                                 temp_pool.capacitor = 0
205                                                 if capacitor_table[temp_pool.name] then
206                                                         swap_node(new_vec(x,y,z),{name=capacitor_table[temp_pool.name].off})
207                                                         redstone.update(new_vec(x,y,z))
208                                                 end
209                                         end
210                                 end
211                         end
212                 end
213         end
214 end
215
216
217
218 -- activators
219 local n_pos
220 local temp_pool
221 local temp_pool2
222 local non_directional_activator = function(pos)
223         if pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z] then
224                 temp_pool = pool[pos.x][pos.y][pos.z]
225                 for _,order in pairs(order) do
226                         n_pos = add_vec(pos,order)
227                         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
228                                 temp_pool2 = pool[n_pos.x][n_pos.y][n_pos.z]
229                                 if temp_pool2 then
230                                         if (not temp_pool2.directional_activator and temp_pool2.torch) or 
231                                         (temp_pool2.dust and temp_pool2.dust > 0) or 
232                                         (temp_pool2.torch_directional and vector.equals(temp_pool2.output, pos)) then
233                                                 if activator_table[temp_pool.name].activate then
234                                                         activator_table[temp_pool.name].activate(pos)
235                                                 end
236                                                 return
237                                         end
238                                 end
239                         end
240                 end     
241                 if activator_table[temp_pool.name].deactivate then
242                         activator_table[temp_pool.name].deactivate(pos)
243                 end
244         end
245 end
246
247 -- directional activators
248 local n_pos
249 local temp_pool
250 local temp_pool2
251 local input
252 local ignore
253 local directional_activator = function(pos)
254         
255         ignore = false
256         input = nil
257         temp_pool = nil
258         temp_pool2 = nil
259
260         --if not (pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z]) then return end
261         
262         temp_pool = pool[pos.x][pos.y][pos.z]
263         
264         if not temp_pool then ignore = true end
265
266         if not ignore then
267                 input = temp_pool.input
268         end
269
270         if not input then ignore = true end
271
272         if not ignore then
273                 input = temp_pool.input
274         end
275
276         if not ignore and pool and pool[input.x] and pool[input.x][input.y] and pool[input.x][input.y][input.z] then
277                 temp_pool2 = pool[input.x][input.y][input.z]
278         else
279                 ignore = true
280         end
281
282         if not temp_pool2 then ignore = true end
283
284         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 
285         (not temp_pool2.directional_activator and temp_pool2.torch) or (temp_pool2.capacitor and temp_pool2.capacitor > 0))  then
286                 if activator_table[temp_pool.name].activate then
287                         activator_table[temp_pool.name].activate(pos)
288                         return
289                 end
290                 return
291         end
292
293         if activator_table[temp_pool.name].deactivate then
294                 activator_table[temp_pool.name].deactivate(pos)
295         end
296 end
297
298 --make redstone wire pass on current one level lower than it is
299 local i
300 local index
301 local passed_on_level
302 local function redstone_pathfinder(source,source_level,mem_map,output)
303         if not source_level then return end
304         --directional torches
305         if output then
306                 i = output
307                 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
308                         index = mem_map[i.x][i.y][i.z]
309                         --dust
310                         if index.dust then
311                                 passed_on_level = source_level - 1
312                                 if passed_on_level > 0 then
313                                         mem_map[i.x][i.y][i.z].dust = passed_on_level
314                                         redstone_pathfinder(i,passed_on_level,mem_map,nil)
315                                 end
316                         end
317                 end
318         else
319                 --redstone and torch
320                 for _,order in pairs(order) do
321                         i = add_vec(source,order)
322                         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
323                                 index = mem_map[i.x][i.y][i.z]
324                                 if index.dust then
325                                         passed_on_level = source_level - 1
326                                         if passed_on_level > 0 and index.dust < source_level then
327                                                 mem_map[i.x][i.y][i.z].dust = passed_on_level
328                                                 redstone_pathfinder(i,passed_on_level,mem_map,nil)
329                                         end
330                                 end
331                         end
332                 end
333         end
334         return(mem_map)
335 end
336
337
338 --[[
339                      , 
340                 ,.  | \ 
341                |: \ ; :\ 
342                :' ;\| ::\ 
343                 \ : | `::\ 
344                 _)  |   `:`. 
345               ,' , `.    ;: ; 
346             ,' ;:  ;"'  ,:: |_ 
347            /,   ` .    ;::: |:`-.__ 
348         _,' _o\  ,::.`:' ;  ;   . ' 
349     _,-'           `:.          ;""\, 
350  ,-'                     ,:         `-;, 
351  \,                       ;:           ;--._ 
352   `.______,-,----._     ,' ;:        ,/ ,  ,` 
353          / /,-';'  \     ; `:      ,'/,::.::: 
354        ,',;-'-'_,--;    ;   :.   ,',',;:::::: 
355       ( /___,-'     `.     ;::,,'o/  ,::::::: 
356        `'             )    ;:,'o /  ;"-   -:: 
357                       \__ _,'o ,'         ,:: 
358                          ) `--'       ,..:::: 
359                          ; `.        ,::::::: 
360                           ;  ``::.    ::::::: 
361 ]]-- sic em boy!
362 local i
363 local index
364 local function dust_sniff(pos,mem_map,boundary)
365         for _,order in pairs(order) do
366                 i = add_vec(pos,order)
367
368                 if not mem_map[i.x] then mem_map[i.x] = {} end
369                 if not mem_map[i.x][i.y] then mem_map[i.x][i.y] = {} end
370
371                 if not mem_map[i.x][i.y][i.z] then
372                         
373                         if i and boundary and boundary[i.x] and boundary[i.x][i.y] and boundary[i.x][i.y][i.z] then
374                                 index = boundary[i.x][i.y][i.z]
375
376                                 if index.dust then
377                                         mem_map[i.x][i.y][i.z] = index
378                                         mem_map[i.x][i.y][i.z].sniffed = true
379
380                                         dust_sniff(i,mem_map,boundary)
381
382                                 elseif index.directional_activator and vec_equals(pos,index.input) then
383                                         mem_map[i.x][i.y][i.z] = index
384                                         mem_map[i.x][i.y][i.z].sniffed = true
385                                 elseif index.torch and index.torch > 1 then
386                                         if index.torch_directional and vec_equals(pos,index.output) then
387                                                 mem_map[i.x][i.y][i.z] = index
388                                                 mem_map[i.x][i.y][i.z].sniffed = true
389                                         elseif not index.torch_directional then
390                                                 mem_map[i.x][i.y][i.z] = index
391                                                 mem_map[i.x][i.y][i.z].sniffed = true
392                                         end
393                                 elseif index.activator then
394                                         mem_map[i.x][i.y][i.z] = index
395                                         mem_map[i.x][i.y][i.z].sniffed = true
396                                 end
397                         end
398                 end
399         end
400         return mem_map
401 end
402
403 --make all power sources push power out
404 local pos
405 local node
406 local power
407 local boundary
408 local dust_detected
409 local dust_map
410 local count
411 local pos3
412 local temp_pool3
413 local function calculate(pos,is_capacitor)
414         if not is_capacitor then
415                 boundary = create_boundary_box(pos)
416                 dust_map = {}
417
418                 dust_detected = false
419                 count = 0
420                 
421                 --update needs to sniff it's own position
422                 if boundary[pos.x] and boundary[pos.x][pos.y] and boundary[pos.x][pos.y][pos.z] then
423                         if not dust_map[pos.x] then dust_map[pos.x] = {} end
424                         if not dust_map[pos.x][pos.y] then dust_map[pos.x][pos.y] = {} end
425                         dust_map[pos.x][pos.y][pos.z] = boundary[pos.x][pos.y][pos.z]
426                 end
427
428                 dust_sniff(pos,dust_map,boundary)
429
430                 -- sniff all possible dust within boundaries
431                 for _,pos2 in pairs(order) do
432                         pos3 = add_vec(pos,pos2)
433                         if boundary[pos3.x] and boundary[pos3.x][pos3.y] and boundary[pos3.x][pos3.y][pos3.z] and
434                                 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
435                                 temp_pool3 = boundary[pos3.x][pos3.y][pos3.z]
436                                 if temp_pool3.dust then
437                                         dust_sniff(pos3,dust_map,boundary)
438                                 end
439                         end
440                 end
441
442                 --do torches
443                 for x,datax in pairs(dust_map) do
444                         for y,datay in pairs(datax) do
445                                 for z,data in pairs(datay) do
446                                         count = count + 1
447                                         if data.torch then
448                                                 if data.torch_directional then
449                                                         redstone_pathfinder(new_vec(x,y,z),data.torch,dust_map,data.output)
450                                                 else
451                                                         redstone_pathfinder(new_vec(x,y,z),data.torch,dust_map)
452                                                         dust_map[x][y][z] = nil
453                                                 end
454                                         end
455                                 end
456                         end
457                 end
458
459                 --set dust, set pool memory
460                 for x,datax in pairs(dust_map) do
461                         for y,datay in pairs(datax) do
462                                 for z,data in pairs(datay) do
463                                         if data.dust and data.dust ~= data.origin then
464                                                 swap_node(new_vec(x,y,z),{name="redstone:dust_"..data.dust})
465                                                 data_injection(new_vec(x,y,z),data)
466                                                 --delete the data to speed up next loop
467                                                 dust_map[x][y][z] = nil
468                                         end
469                                 end
470                         end
471                 end
472
473                 --activators
474                 --this must be run at the end
475                 for x,datax in pairs(dust_map) do
476                         for y,datay in pairs(datax) do
477                                 for z,data in pairs(datay) do
478                                         if data.directional_activator then
479                                                 directional_activator(new_vec(x,y,z))
480                                         elseif data.activator then
481                                                 non_directional_activator(new_vec(x,y,z))
482                                         end
483                                 end
484                         end
485                 end
486         else
487                 capacitor_sniff(pos)
488         end
489 end
490
491
492 function redstone.inject(pos,data)
493         data_injection(pos,data)
494 end
495
496
497 local recursion_check = {}
498 local bad_node
499 function redstone.update(pos,is_capacitor)
500         local s_pos = minetest.serialize(pos)
501         if not recursion_check[s_pos] then
502                 recursion_check[s_pos] = 0
503         end
504         recursion_check[s_pos] = recursion_check[s_pos] + 1
505         if recursion_check[s_pos] > 25 then
506                 --print(recursion_check[s_pos])
507                 minetest.after(0,function()
508                         bad_node = minetest.get_node(pos).name
509                         bad_node = minetest.get_node_drops(bad_node, "main:rubypick")
510                         for _,nodey in pairs(bad_node) do
511                                 minetest.throw_item(pos,nodey)
512                         end
513                         minetest.remove_node(pos)
514                         data_injection(pos,nil)
515                         redstone.update(pos)
516                 end)
517                 return
518         end
519
520         calculate(pos,is_capacitor)
521 end
522
523 minetest.register_globalstep(function()
524         recursion_check = {}
525 end)
526
527
528 ----------------------------------------------------------------------------
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562 minetest.register_craftitem("redstone:dust", {
563         description = "Redstone Dust",
564         inventory_image = "redstone_dust_item.png",
565         wield_image = "redstone_dust_item.png",
566         wield_scale = {x = 1, y = 1, z = 1 + 1/16},
567         liquids_pointable = false,
568         on_place = function(itemstack, placer, pointed_thing)
569                 if not pointed_thing.type == "node" then
570                         return
571                 end             
572                 local sneak = placer:get_player_control().sneak
573                 local noddef = registered_nodes[get_node(pointed_thing.under).name]
574                 if not sneak and noddef.on_rightclick then
575                         minetest.item_place(itemstack, placer, pointed_thing)
576                         return
577                 end
578                 
579                 local _,worked = minetest.item_place(ItemStack("redstone:dust_0"), placer, pointed_thing)
580                 if worked then
581                         itemstack:take_item()
582                         return(itemstack)
583                 end
584         end,
585 })
586
587 --8 power levels 8 being the highest
588 local color = 0
589 for i = 0,8 do
590         local coloring = floor(color)
591         minetest.register_node("redstone:dust_"..i,{
592                 description = "Redstone Dust",
593                 wield_image = "redstone_dust_item.png",
594                 tiles = {
595                         "redstone_dust_main.png^[colorize:red:"..coloring, "redstone_turn.png^[colorize:red:"..coloring,
596                         "redstone_t.png^[colorize:red:"..coloring, "redstone_cross.png^[colorize:red:"..coloring
597                 },
598                 power=i,
599                 drawtype = "raillike",
600                 paramtype = "light",
601                 sunlight_propagates = true,
602                 is_ground_content = false,
603                 walkable = false,
604                 node_placement_prediction = "",
605                 selection_box = {
606                         type = "fixed",
607                         fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
608                 },
609                 sounds = main.stoneSound(),
610                 groups={dig_immediate=1,attached_node=1,redstone_dust=1,redstone=1,redstone_power=i},
611                 drop="redstone:dust",
612                 on_construct = function(pos)
613                         data_injection(pos,{dust=i})
614                         calculate(pos)
615                 end,
616                 after_destruct = function(pos)
617                         data_injection(pos,nil)
618                         calculate(pos)
619                 end,
620                 connects_to = {"group:redstone"},
621         })
622         color= color +31.875
623
624         minetest.register_lbm({
625         name = "redstone:"..i,
626                 nodenames = {"redstone:dust_"..i},
627                 run_at_every_load = true,
628         action = function(pos)
629                         data_injection(pos,{dust=i})
630         end,
631     })
632 end