]> git.lizzy.rs Git - Crafter.git/blob - mods/redstone/init.lua
Only index node once in redstone update
[Crafter.git] / mods / redstone / init.lua
1 local 
2 minetest,vector,math,table,pairs
3 =
4 minetest,vector,math,table,pairs
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 local set_node        = minetest.set_node
16 local registered_nodes
17 minetest.register_on_mods_loaded(function()
18         registered_nodes  = minetest.registered_nodes
19 end)
20
21 -- math class
22 local abs   = math.abs
23 local floor = math.floor
24
25 -- vector library
26 local new_vec         = vector.new
27 local add_vec         = vector.add
28 local sub_vec         = vector.subtract
29 local vector_distance = vector.distance
30
31 --this is written out manually so that
32 --math.abs is not needed
33 local order = {
34         {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
35         {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
36
37         {x=0, y=1, z=0}, {x= 0, y=-1, z=0},
38
39         {x=1, y=1, z=0}, {x=-1, y=1, z= 0},
40         {x=0, y=1, z=1}, {x= 0, y=1, z=-1},
41
42         {x=1, y=-1, z=0}, {x=-1, y=-1, z= 0},
43         {x=0, y=-1, z=1}, {x= 0, y=-1, z=-1},
44 }
45
46 -- redstone class
47 redstone = {}
48
49 local speed_test
50
51 local r_index = {}
52 local a_index = {}
53 local check_table = {}
54
55 local path = minetest.get_modpath("redstone")
56 dofile(path.."/functions.lua")
57 dofile(path.."/wire.lua")
58 dofile(path.."/torch.lua")
59 dofile(path.."/lever.lua")
60 dofile(path.."/button.lua")
61 dofile(path.."/repeater.lua")
62 dofile(path.."/light.lua")
63 dofile(path.."/piston.lua")
64 dofile(path.."/comparator.lua")
65 dofile(path.."/craft.lua")
66 dofile(path.."/ore.lua")
67 dofile(path.."/inverter.lua")
68 dofile(path.."/player_detector.lua")
69 dofile(path.."/space_maker.lua")
70 dofile(path.."/pressure_plate.lua")
71
72
73 --set the data for powered states
74 local get_local_power = function(pos)
75         if not pos then
76                 return
77         end
78         for _,order in pairs(order) do
79                 --print(get_node(add_vec(new_vec(x,y,z),pos)).name)
80                 if get_item_group(get_node(add_vec(new_vec(order.x,order.y,order.z),pos)).name, "redstone_power") > 0 then
81                         return(1)
82                 end
83                 if get_meta(add_vec(new_vec(order.x,order.y,order.z),pos)):get_int("redstone_power") > 0 then
84                         return(1)
85                 end
86         end     
87         return(0)
88 end
89
90 local power
91 local pos
92 local get_powered_state_directional = function(pos)
93         pos = sub_vec(pos,facedir_to_dir(get_node(pos).param2))
94         power = get_item_group(get_node(pos).name, "redstone_power")
95         if power == 0 then
96                 power = get_meta(pos):get_int("redstone_power")
97         end
98         return(power)
99 end
100
101 local node
102 local redstone_activate = function(pos,power)
103         after(0,function()
104                 node = get_node(pos).name
105                 if registered_nodes[node].redstone_activation then
106                         registered_nodes[node].redstone_activation(pos)
107                 end
108         end)
109 end
110
111 local node
112 local redstone_deactivate = function(pos,power)
113         after(0,function()
114                 node = get_node(pos).name
115                 if registered_nodes[node].redstone_deactivation then
116                         registered_nodes[node].redstone_deactivation(pos)
117                 end
118         end)
119 end
120
121 --collect all nodes that are local to the modified
122 --node of redstone dust and store in memory
123 local localredstone = {}
124 local node
125 localredstone.injector = function(i,origin)
126         node = get_node(i).name
127         if node == "air" then
128                 return
129         end
130
131         if r_index[i.x] and r_index[i.x][i.y] and r_index[i.x][i.y][i.z] then
132                 return
133         end
134
135         if vector_distance(i,origin) > 9 then
136                 return
137         end
138
139         --index dust
140         if get_item_group(node,"redstone_dust") > 0 then
141                 if vector_distance(i,origin) <= 8 then
142                         --add data to both maps
143                         if not r_index[i.x] then r_index[i.x] = {} end
144                         if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
145                         r_index[i.x][i.y][i.z] = {dust = true,level = 0}
146
147                         if not check_table[i.x] then check_table[i.x] = {} end
148                         if not check_table[i.x][i.y] then check_table[i.x][i.y] = {} end
149                         check_table[i.x][i.y][i.z] = get_item_group(node,"redstone_power")
150                         
151                         --the data to the 3d array must be written to memory before this is executed
152                         --or a stack overflow occurs!!!
153                         localredstone.collector(i,origin)
154                         return
155                 else
156                         if not r_index[i.x] then r_index[i.x] = {} end
157                         if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
158                         r_index[i.x][i.y][i.z] = {torch = true,power=get_item_group(node,"redstone_power")}
159                 end
160         end
161         --index power sources
162         if get_item_group(node,"redstone_torch") > 0 then
163                 if not r_index[i.x] then r_index[i.x] = {} end
164                 if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
165                 r_index[i.x][i.y][i.z] = {torch = true,power=get_item_group(node,"redstone_power")}
166         end     
167         --index directional power sources (Like repeaters/comparators)
168         --only outputs forwards
169         if get_item_group(node,"torch_directional") > 0 then
170                 if not r_index[i.x] then r_index[i.x] = {} end
171                 if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
172                 r_index[i.x][i.y][i.z] = {torch_directional = true, dir = get_node(i).param2 , power = get_item_group(node,"redstone_power")}
173         end
174         
175         --index directional activators (Like repeaters/comparators)
176         --only accepts input from the back
177         if get_item_group(node,"redstone_activation_directional") > 0 then
178                 --print("indexing directional")
179                 if not a_index[i.x] then a_index[i.x] = {} end
180                 if not a_index[i.x][i.y] then a_index[i.x][i.y] = {} end
181                 if not a_index[i.x][i.y][i.z] then a_index[i.x][i.y][i.z] = {} end
182
183                 a_index[i.x][i.y][i.z].redstone_activation = true
184                 a_index[i.x][i.y][i.z].directional = true
185         end
186         
187         --index objects that activate
188         if get_item_group(node,"redstone_activation") > 0 then
189                 if not a_index[i.x] then a_index[i.x] = {} end
190                 if not a_index[i.x][i.y] then a_index[i.x][i.y] = {} end
191                 if not a_index[i.x][i.y][i.z] then a_index[i.x][i.y][i.z] = {} end
192                 a_index[i.x][i.y][i.z].redstone_activation = true
193         end
194
195         --sneaky way to make levers and buttons work
196         if get_meta(i):get_int("redstone_power") > 0 then
197                 if not r_index[i.x] then r_index[i.x] = {} end
198                 if not r_index[i.x][i.y] then r_index[i.x][i.y] = {} end
199                 r_index[i.x][i.y][i.z] = {torch = true,power=get_meta(i):get_int("redstone_power")}
200         end
201 end
202
203 localredstone.collector = function(pos,origin)
204         for _,order in pairs(order) do
205                 localredstone.injector(add_vec(pos,new_vec(order.x,order.y,order.z)),origin)
206         end
207 end
208
209
210 --check if index table contains items
211 --then execute an update
212 local function redstone_algorithm()
213         --if indexes exist then calculate redstone
214         --create the old version to help with deactivation calculation
215         redstone.calculate()
216         --clear the index to avoid cpu looping wasting processing power
217         r_index = {}
218         a_index = {}
219         check_table = {}
220 end
221
222
223 function redstone.collect_info(pos)
224         if r_index[pos.x] and r_index[pos.x][pos.y] and r_index[pos.x][pos.y][pos.z] then
225                 return
226         end
227         localredstone.injector(pos,pos)
228         localredstone.collector(pos,pos)
229
230         redstone_algorithm()
231 end
232
233
234 --make all power sources push power out
235 local pos
236 local node
237 local power
238 function redstone.calculate()
239         --speed_test = minetest.get_us_time()/1000000
240
241         --pathfind through memory map   
242         for x,index_x in pairs(r_index) do
243                 for y,index_y in pairs(index_x) do
244                         for z,data in pairs(index_y) do
245                                 --allow data values for torches
246                                 if data.torch then
247                                         redstone.pathfind(new_vec(x,y,z),data.power)
248                                         r_index[x][y][z] = nil
249                                 elseif data.torch_directional then
250                                         redstone.pathfind(new_vec(x,y,z),data.power,data.dir)
251                                         r_index[x][y][z] = nil
252                                 end
253                         end
254                 end
255         end
256         
257         --print("total torch calc time:"..minetest.get_us_time()/1000000-speed_test)
258
259
260
261         --reassemble the table into a position list minetest can understand
262         --run through and set dust
263         --local count = 0
264
265         for x,datax in pairs(r_index) do
266                 for y,datay in pairs(datax) do
267                         for z,index in pairs(datay) do
268                                 --print(get_node(new_vec(x,y,z)).name)
269                                 if index and index.dust and index.level ~= check_table[x][y][z] then
270                                         --count = count + 1
271                                         set_node(new_vec(x,y,z),{name="redstone:dust_"..index.level})
272                                 end
273                         end
274                 end
275         end
276         --print("set "..count.." nodes")
277
278         for x,datax in pairs(a_index) do
279                 for y,datay in pairs(datax) do
280                         for z,index in pairs(datay) do
281                                 --directional activators
282                                 if index.directional == true then
283                                         power = get_powered_state_directional(new_vec(x,y,z))
284                                         if power then
285                                                 if power > 0 then
286                                                         redstone_activate(new_vec(x,y,z),power)
287                                                 elseif power == 0 then
288                                                         redstone_deactivate(new_vec(x,y,z),power)
289                                                 end
290                                         end
291                                 --non directional activators
292                                 else
293                                         power = get_local_power(new_vec(x,y,z))
294                                         --print(power)
295                                         if power then
296                                                 if power > 0 then
297                                                         redstone_activate(new_vec(x,y,z),power)
298                                                 elseif power == 0 then
299                                                         redstone_deactivate(new_vec(x,y,z),power)
300                                                 end
301                                         end
302                                 end
303                         end
304                 end
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,direction)
313         --directional torches
314
315         if direction then
316                 --print("starting direction")
317                 i = add_vec(source,facedir_to_dir(direction))
318                 if r_index and r_index[i.x] and r_index[i.x][i.y] and r_index[i.x][i.y][i.z] then
319                         index = r_index[i.x][i.y][i.z]
320                         --dust
321                         if index.dust  then
322                                 passed_on_level = source_level - 1
323                                 if passed_on_level > 0 then
324                                         r_index[i.x][i.y][i.z].level = passed_on_level
325                                         redstone_pathfinder(i,passed_on_level,nil)
326                                 end
327                         end
328                 end
329         else
330                 --redstone and torch
331                 for _,order in pairs(order) do
332                         i = add_vec(source,new_vec(order.x,order.y,order.z))
333                         if r_index and r_index[i.x] and r_index[i.x][i.y] and r_index[i.x][i.y][i.z] then
334                                 --print(minetest.get_node(i).name)
335                                 index = r_index[i.x][i.y][i.z]                                  
336                                 if index.dust  then
337                                         passed_on_level = source_level - 1
338                                         if passed_on_level > 0 and index.level < source_level then
339                                                 r_index[i.x][i.y][i.z].level = passed_on_level
340                                                 redstone_pathfinder(i,passed_on_level,nil)
341                                         end
342                                 end
343                         end
344                 end
345         end
346 end
347 function redstone.pathfind(source,source_level,direction)
348         redstone_pathfinder(source,source_level,direction)
349 end
350
351
352
353
354
355
356
357
358 ----------------------------------------------------------------------------
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392 minetest.register_craftitem("redstone:dust", {
393         description = "Redstone Dust",
394         inventory_image = "redstone_dust_item.png",
395         wield_image = "redstone_dust_item.png",
396         wield_scale = {x = 1, y = 1, z = 1 + 1/16},
397         liquids_pointable = false,
398         on_place = function(itemstack, placer, pointed_thing)
399                 if not pointed_thing.type == "node" then
400                         return
401                 end             
402                 local sneak = placer:get_player_control().sneak
403                 local noddef = registered_nodes[get_node(pointed_thing.under).name]
404                 if not sneak and noddef.on_rightclick then
405                         minetest.item_place(itemstack, placer, pointed_thing)
406                         return
407                 end
408                 
409                 local _,worked = minetest.item_place(ItemStack("redstone:dust_0"), placer, pointed_thing)
410                 if worked then
411                         itemstack:take_item()
412                         return(itemstack)
413                 end
414
415
416                         --minetest.add_node(pointed_thing.above, {name="redstone:dust_0"})
417                         --itemstack:take_item(1)
418                         --minetest.sound_play("stone", {pos=pointed_thing.above})
419                         --return(itemstack)
420                 --end
421         end,
422 })
423
424 --8 power levels 8 being the highest
425 local color = 0
426 for i = 0,8 do
427         local coloring = floor(color)
428         minetest.register_node("redstone:dust_"..i,{
429                 description = "Redstone Dust",
430                 wield_image = "redstone_dust_item.png",
431                 tiles = {
432                         "redstone_dust_main.png^[colorize:red:"..coloring, "redstone_turn.png^[colorize:red:"..coloring,
433                         "redstone_t.png^[colorize:red:"..coloring, "redstone_cross.png^[colorize:red:"..coloring
434                 },
435                 power=i,
436                 drawtype = "raillike",
437                 paramtype = "light",
438                 sunlight_propagates = true,
439                 is_ground_content = false,
440                 walkable = false,
441                 node_placement_prediction = "",
442                 selection_box = {
443                         type = "fixed",
444                         fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
445                 },
446                 sounds = main.stoneSound(),
447                 groups={dig_immediate=1,attached_node=1,redstone_dust=1,redstone=1,redstone_power=i},
448                 drop="redstone:dust",
449                 on_construct = function(pos)
450                         redstone.collect_info(pos)
451                 end,
452                 after_destruct = function(pos)
453                         --redstone.remove(pos,registered_nodes[get_node(pos).name].power)
454                         redstone.collect_info(pos)
455                 end,
456                 connects_to = {"group:redstone"},
457         })
458         color= color +31.875
459 end