]> git.lizzy.rs Git - Crafter.git/blob - mods/redstone/init.lua
Add in advanced player detectors
[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 local player_detection_table = {}
36
37 -- redstone class
38 redstone = {}
39
40 redstone.player_detector_add = function(pos)
41         player_detection_table[minetest.serialize(pos)] = pos
42 end
43
44 redstone.player_detector_remove = function(pos)
45         player_detection_table[minetest.serialize(pos)] = nil
46 end
47
48
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
54         }
55 end
56
57 -- enables mods to create data functions
58 function redstone.register_capacitor(data)
59         capacitor_table[data.name] = {
60                 off = data.off,
61                 on  = data.on
62         }
63 end
64
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")
81
82
83 --this is written out manually so that
84 --math.abs is not needed
85 local order = {
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},
88
89         {x=0, y=1, z=0}, {x= 0, y=-1, z=0},
90
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},
93
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},
96 }
97
98 --thanks to RhodiumToad for helping me figure out a good method to do this
99
100 local pool = {} -- this holds all redstone data (literal 3d virtual memory map)
101
102
103 local function data_injection(pos,data)
104         -- add data into 3d memory
105         if data then
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
110         else
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
117                                         pool[pos.x] = nil
118                                 end
119                         end
120                 end
121         end
122 end
123
124 local table_3d
125 local temp_pool
126 local function create_boundary_box(pos)
127         table_3d = {}
128         for x = pos.x-9,pos.x+9 do
129                 if pool[x] then
130                         for y = pos.y-9,pos.y+9 do
131                                 if pool[x][y] then
132                                         for z = pos.z-9,pos.z+9 do
133                                                 temp_pool = pool[x][y][z]
134                                                 if temp_pool then
135                                                         if not table_3d[x] then table_3d[x] = {} end
136                                                         if not table_3d[x][y] then table_3d[x][y] = {} end
137
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}
143                                                         else
144                                                                 if temp_pool.dust then
145                                                                         table_3d[x][y][z] = {dust=0,origin=temp_pool.dust}
146                                                                 else
147                                                                         table_3d[x][y][z] = temp_pool
148                                                                 end
149                                                         end
150                                                 end
151                                         end
152                                 end
153                         end
154                 end
155         end
156         return(table_3d)
157 end
158
159 local i
160 local index
161 local function capacitor_pathfind(source,mem_map)
162         for _,order in pairs(order) do
163
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
167
168                 if not mem_map[i.x][i.y][i.z] then
169
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}
174                                         if index.source then
175                                                 mem_map.found = true
176                                         end
177                                         capacitor_pathfind(i,mem_map)
178                                 end
179                         end
180                 end
181         end
182         return mem_map
183 end
184
185 local table_3d
186 local found
187 local temp_pool
188 local function capacitor_sniff(pos)
189         table_3d = {}
190         table_3d = capacitor_pathfind(pos,table_3d)
191         found = table_3d.found
192         table_3d.found = nil
193         if found then
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]
198                                         if temp_pool then
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))
203                                                 end
204                                         end
205                                 end
206                         end
207                 end
208         else
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]
213                                         if temp_pool then
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))
218                                                 end
219                                         end
220                                 end
221                         end
222                 end
223         end
224 end
225
226
227
228 -- activators
229 local n_pos
230 local temp_pool
231 local temp_pool2
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]
239                                 if temp_pool2 then
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)
245                                                 end
246                                                 return
247                                         end
248                                 end
249                         end
250                 end     
251                 if activator_table[temp_pool.name].deactivate then
252                         activator_table[temp_pool.name].deactivate(pos)
253                 end
254         end
255 end
256
257 -- directional activators
258 local n_pos
259 local temp_pool
260 local temp_pool2
261 local input
262 local ignore
263 local directional_activator = function(pos)
264         
265         ignore = false
266         input = nil
267         temp_pool = nil
268         temp_pool2 = nil
269
270         --if not (pool[pos.x] and pool[pos.x][pos.y] and pool[pos.x][pos.y][pos.z]) then return end
271         
272         temp_pool = pool[pos.x][pos.y][pos.z]
273         
274         if not temp_pool then ignore = true end
275
276         if not ignore then
277                 input = temp_pool.input
278         end
279
280         if not input then ignore = true end
281
282         if not ignore then
283                 input = temp_pool.input
284         end
285
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]
288         else
289                 ignore = true
290         end
291
292         if not temp_pool2 then ignore = true end
293
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)
298                         return
299                 end
300                 return
301         end
302
303         if activator_table[temp_pool.name].deactivate then
304                 activator_table[temp_pool.name].deactivate(pos)
305         end
306 end
307
308 --make redstone wire pass on current one level lower than it is
309 local i
310 local index
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
315         if output then
316                 i = output
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]
319                         --dust
320                         if index.dust then
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)
325                                 end
326                         end
327                 end
328         else
329                 --redstone and torch
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]
334                                 if index.dust then
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)
339                                         end
340                                 end
341                         end
342                 end
343         end
344         return(mem_map)
345 end
346
347
348 --[[
349                      , 
350                 ,.  | \ 
351                |: \ ; :\ 
352                :' ;\| ::\ 
353                 \ : | `::\ 
354                 _)  |   `:`. 
355               ,' , `.    ;: ; 
356             ,' ;:  ;"'  ,:: |_ 
357            /,   ` .    ;::: |:`-.__ 
358         _,' _o\  ,::.`:' ;  ;   . ' 
359     _,-'           `:.          ;""\, 
360  ,-'                     ,:         `-;, 
361  \,                       ;:           ;--._ 
362   `.______,-,----._     ,' ;:        ,/ ,  ,` 
363          / /,-';'  \     ; `:      ,'/,::.::: 
364        ,',;-'-'_,--;    ;   :.   ,',',;:::::: 
365       ( /___,-'     `.     ;::,,'o/  ,::::::: 
366        `'             )    ;:,'o /  ;"-   -:: 
367                       \__ _,'o ,'         ,:: 
368                          ) `--'       ,..:::: 
369                          ; `.        ,::::::: 
370                           ;  ``::.    ::::::: 
371 ]]-- sic em boy!
372 local i
373 local index
374 local function dust_sniff(pos,mem_map,boundary)
375         for _,order in pairs(order) do
376                 i = add_vec(pos,order)
377
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
380
381                 if not mem_map[i.x][i.y][i.z] then
382                         
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]
385
386                                 if index.dust then
387                                         mem_map[i.x][i.y][i.z] = index
388                                         mem_map[i.x][i.y][i.z].sniffed = true
389
390                                         dust_sniff(i,mem_map,boundary)
391
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
402                                         end
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
406                                 end
407                         end
408                 end
409         end
410         return mem_map
411 end
412
413 --make all power sources push power out
414 local pos
415 local node
416 local power
417 local boundary
418 local dust_detected
419 local dust_map
420 local count
421 local pos3
422 local temp_pool3
423 local function calculate(pos,is_capacitor)
424         if not is_capacitor then
425                 boundary = create_boundary_box(pos)
426                 dust_map = {}
427
428                 dust_detected = false
429                 count = 0
430                 
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]
436                 end
437
438                 dust_sniff(pos,dust_map,boundary)
439
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)
448                                 end
449                         end
450                 end
451
452                 --do torches
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
456                                         count = count + 1
457                                         if data.torch then
458                                                 if data.torch_directional then
459                                                         redstone_pathfinder(new_vec(x,y,z),data.torch,dust_map,data.output)
460                                                 else
461                                                         redstone_pathfinder(new_vec(x,y,z),data.torch,dust_map)
462                                                         dust_map[x][y][z] = nil
463                                                 end
464                                         end
465                                 end
466                         end
467                 end
468
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
478                                         end
479                                 end
480                         end
481                 end
482
483                 --activators
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))
492                                         end
493                                 end
494                         end
495                 end
496         else
497                 capacitor_sniff(pos)
498         end
499 end
500
501
502 function redstone.inject(pos,data)
503         data_injection(pos,data)
504 end
505
506
507 local recursion_check = {}
508 local bad_node
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
513         end
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)
522                         end
523                         minetest.remove_node(pos)
524                         data_injection(pos,nil)
525                         redstone.update(pos)
526                 end)
527                 return
528         end
529
530         calculate(pos,is_capacitor)
531 end
532
533
534 local level
535 local pos2
536 local power
537 local max
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
541                 max = 0
542                 for _,player in ipairs(minetest.get_connected_players()) do
543                         pos2 = player:get_pos()
544                         power = floor(10-vector_distance(pos2,pos))
545                         if power > 9 then
546                                 power = 9
547                         elseif power < 0 then
548                                 power = 0
549                         end
550
551                         if power > max then
552                                 max = power
553                         end
554                 end
555
556                 if max ~= level then
557                         swap_node(pos,{name="redstone:player_detector_"..max})
558                         redstone.inject(pos,{
559                                 name = "redstone:player_detector_"..max,
560                                 torch = max,
561                         })
562                         redstone.update(pos)
563                 end
564         end
565 end
566
567 minetest.register_globalstep(function()
568         player_detector_calculation()
569         recursion_check = {}
570 end)
571
572
573 ----------------------------------------------------------------------------
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
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
615                         return
616                 end             
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)
621                         return
622                 end
623                 
624                 local _,worked = minetest.item_place(ItemStack("redstone:dust_0"), placer, pointed_thing)
625                 if worked then
626                         itemstack:take_item()
627                         return(itemstack)
628                 end
629         end,
630 })
631
632 --8 power levels 8 being the highest
633 local color = 0
634 for i = 0,8 do
635         local coloring = floor(color)
636         minetest.register_node("redstone:dust_"..i,{
637                 description = "Redstone Dust",
638                 wield_image = "redstone_dust_item.png",
639                 tiles = {
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
642                 },
643                 power=i,
644                 drawtype = "raillike",
645                 paramtype = "light",
646                 sunlight_propagates = true,
647                 is_ground_content = false,
648                 walkable = false,
649                 node_placement_prediction = "",
650                 selection_box = {
651                         type = "fixed",
652                         fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
653                 },
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})
659                         calculate(pos)
660                 end,
661                 after_destruct = function(pos)
662                         data_injection(pos,nil)
663                         calculate(pos)
664                 end,
665                 connects_to = {"group:redstone"},
666         })
667         color= color +31.875
668
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})
675         end,
676     })
677 end