· 10 years ago · Sep 03, 2016, 02:52 PM
1--00_Readme.lua
2
3-- ********************************************************************************** --
4-- ** ** --
5-- ** Krutoy Turtle 2.0 (debug version) ** --
6-- ** http://pastebin.com/g2ZqawdP ** --
7-- ** ---------------------------------------------------- ** --
8-- ** ** --
9-- ** Run on turtle: ** --
10-- ** pastebin get g2ZqawdP startup ** --
11-- ** ** --
12-- ** Developing page: ** --
13-- ** http://computercraft.ru/topic/48-stroitelnaia-sistema-krutoyturtle/ ** --
14-- ** ** --
15-- ** ---------------------------------------------------- ** --
16-- ** Thanks for this peoples, i use theirs code: ** --
17-- ** - AustinKK (OreQuarry Turtle), NitrogenFingers (NPaintPro), ZeroGalaxy ** --
18-- ** ** --
19-- ********************************************************************************** --
20
21
22------------------------------------------------------
23-- Fun --
24------------------------------------------------------
25
26--[[
27 ____
28\ /o o \ /
29 \| ~ |/
30 \____/
31
32 ,+---+ ,+---+ ,+---+
33+---+'| +---+'| +---+'|
34|^_^| + |^_^| + |^_^| +
35+---+' +---+' +---+'
36
37]]--
38
39K_VERSION = 2.0691 -- Version of program. Need for auto-update
40
41--03_Utils.lua
42
43
44
45-- Redefine global function for faster acces
46local floor = math.floor
47local ceil = math.ceil
48local insert= table.insert
49local min = math.min
50local max = math.max
51
52
53--===========================================================
54-- Inline help functions
55--===========================================================
56local function clearScreen() term.clear(); term.setCursorPos(1,1) end
57local function tblLen(tbl) local n=0; for _,_ in pairs(tbl) do n = n + 1 end return n end
58local function makeSet(list) local set = {}; for _, v in pairs(list) do set[v] = true end return set end
59local function pressAnyKey() local event, param1 = os.pullEvent ("key") end
60local function getChar(str, pos) return string.sub(str, pos, pos) end
61
62
63function shallowcopy(orig)
64 local orig_type = type(orig)
65 local copy
66 if orig_type == 'table' then
67 copy = {}
68 for orig_key, orig_value in pairs(orig) do
69 copy[orig_key] = orig_value
70 end
71 else -- number, string, boolean, etc
72 copy = orig
73 end
74 return copy
75end
76
77
78-- ********************************************************************************** --
79-- ** 3D Vector ** --
80-- ** ** --
81-- ** Modified version of 2d vector from ** --
82-- ** https://github.com/vrld/hump/blob/master/vector.lua ** --
83-- ** ** --
84-- ** By Krutoy242 ** --
85-- ** ** --
86-- ********************************************************************************** --
87
88vec3 = (function()
89 local assert = assert
90 local sqrt, cos, sin, atan2 = math.sqrt, math.cos, math.sin, math.atan2
91
92 local vector = {}
93 vector.__index = vector
94
95 local function new(...)
96 local x,y,z = ...
97 if type(x) == "table" then x,y,z = x.x, x.y, x.z end
98 return setmetatable({x = x or 0, y = y or 0, z = z or 0}, vector)
99 end
100 local zero = new(0,0,0)
101
102 local function isvector(v)
103 return type(v) == 'table' and type(v.x) == 'number' and type(v.y) == 'number' and type(v.z) == 'number'
104 end
105
106 function vector:clone()
107 return new(self.x, self.y, self.z)
108 end
109
110 function vector:unpack()
111 return self.x, self.y, self.z
112 end
113
114 function vector:__tostring()
115 return "("..tonumber(self.x)..","..tonumber(self.y)..","..tonumber(self.z)..")"
116 end
117
118 function vector.__unm(a)
119 return new(-a.x, -a.y, -a.z)
120 end
121
122 function vector.__add(a,b)
123 assert(isvector(a) and isvector(b), "Add: wrong argument types (<vector> expected)")
124 return new(a.x+b.x, a.y+b.y, a.z+b.z)
125 end
126
127 function vector.__sub(a,b)
128 assert(isvector(a) and isvector(b), "Sub: wrong argument types (<vector> expected)")
129 return new(a.x-b.x, a.y-b.y, a.z-b.z)
130 end
131
132 function vector.__mul(a,b)
133 if type(a) == "number" then
134 return new(a*b.x, a*b.y, a*b.z)
135 elseif type(b) == "number" then
136 return new(b*a.x, b*a.y, b*a.z)
137 else
138 assert(isvector(a) and isvector(b), "Mul: wrong argument types (<vector> or <number> expected)")
139 return a.x*b.x + a.y*b.y + a.z*b.z
140 end
141 end
142
143 function vector.__div(a,b)
144 assert(isvector(a) and type(b) == "number", "wrong argument types (expected <vector> / <number>)")
145 return new(a.x / b, a.y / b, a.z / b)
146 end
147
148 function vector.__eq(a,b)
149 return a.x == b.x and a.y == b.y and a.z == b.z
150 end
151
152 function vector.__lt(a,b)
153 return a.x < b.x and a.y < b.y and a.z < b.z
154 end
155
156 function vector.__le(a,b)
157 return a.x <= b.x and a.y <= b.y and a.z <= b.z
158 end
159
160 function vector.permul(a,b)
161 assert(isvector(a) and isvector(b), "permul: wrong argument types (<vector> expected)")
162 return new(a.x*b.x, a.y*b.y, a.z*b.z)
163 end
164
165 function vector:len2()
166 return self.x * self.x + self.y * self.y + self.z * self.z
167 end
168
169 function vector:len()
170 return sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
171 end
172
173 function vector.dist(a, b)
174 assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
175 local dx = a.x - b.x
176 local dy = a.y - b.y
177 local dz = a.z - b.z
178 return sqrt(dx * dx + dy * dy + dz * dz)
179 end
180
181 function vector.dist2(a, b)
182 assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
183 local dx = a.x - b.x
184 local dy = a.y - b.y
185 local dz = a.z - b.z
186 return (dx * dx + dy * dy + dz * dz)
187 end
188
189 function vector:normalize_inplace()
190 local l = self:len()
191 if l > 0 then
192 self.x, self.y, self.z = self.x / l, self.y / l, self.z / l
193 end
194 return self
195 end
196
197 function vector:normalized()
198 return self:clone():normalize_inplace()
199 end
200
201 function vector:rotate_inplace_z(phi)
202 local c, s = cos(phi), sin(phi)
203 self.x, self.y = c * self.x - s * self.y, s * self.x + c * self.y
204 return self
205 end
206 function vector:rotate_inplace_x(phi)
207 local c, s = cos(phi), sin(phi)
208 self.y, self.z = c * self.y - s * self.z, s * self.y + c * self.z
209 return self
210 end
211 function vector:rotate_inplace_y(phi)
212 local c, s = cos(phi), sin(phi)
213 self.z, self.x = c * self.z - s * self.x, s * self.z + c * self.x
214 return self
215 end
216
217 function vector:rotated_z(phi)
218 local c, s = cos(phi), sin(phi)
219 return new(c * self.x - s * self.y, s * self.x + c * self.y, self.z)
220 end
221 function vector:rotated_x(phi)
222 local c, s = cos(phi), sin(phi)
223 return new(self.x, c * self.y - s * self.z, s * self.y + c * self.z)
224 end
225 function vector:rotated_y(phi)
226 local c, s = cos(phi), sin(phi)
227 return new(s * self.z + c * self.x, self.y, c * self.z - s * self.x)
228 end
229
230 function vector:cross(v)
231 assert(isvector(v), "cross: wrong argument types (<vector> expected)")
232 return new(self.Y * v.Z - self.Z * v.Y,
233 self.Z * v.X - self.X * v.Z,
234 self.X * v.Y - self.Y * v.X)
235 end
236
237 function vector:trim_inplace(maxLen)
238 local s = maxLen * maxLen / self:len2()
239 s = (s > 1 and 1) or math.sqrt(s)
240 self.x, self.y, self.z = self.x * s, self.y * s, self.z * s
241 return self
242 end
243
244 function vector:trimmed(maxLen)
245 return self:clone():trim_inplace(maxLen)
246 end
247
248
249 -- the module
250 return setmetatable({new = new, isvector = isvector, zero = zero},
251 {__call = function(_, ...) return new(...) end})
252end)()
253
254
255-- ********************************************************************************** --
256-- ** 3D Array ** --
257-- ** ** --
258-- ** By Krutoy242 ** --
259-- ** ** --
260-- ********************************************************************************** --
261arr3d = function() return setmetatable({
262 set = function(t,x,y,z,v)
263 t[z] = t[z] or {}
264 t[z][y] = t[z][y] or {}
265 t[z][y][x] = v
266 end,
267 setVolume = function(t, x1,y1,z1,x2,y2,z2, v)
268 for z=z1, z2 do
269 for y=y1, y2 do
270 for x=x1, x2 do
271 t:set(x,y,z, v)
272 end
273 end
274 end
275 end,
276 raw = function(t)
277 local raw = shallowcopy(t)
278 raw.set, raw.setVolume, raw.raw = nil,nil,nil
279 return raw
280 end
281 }, { __call = function(t, x, y, z)
282 if not t[z] or not t[z][y] then return nil end
283 return t[z][y][x]
284 end
285})end
286
287
288
289--===========================================================
290-- Devide giving space to parts
291--===========================================================
292local function splitJob(cargo, ...)
293 local args = {...}
294 assert(#args ~= 0, "Wrong splitJob parameters")
295
296 local cargoCount = #args
297 local arr = {}
298
299 if type(cargo) == "number" and cargoCount==1 then
300 -- We havent weights, just count of slots
301 local slices = args[1]
302 local intPart = floor(cargo/slices)
303 local residue = cargo % slices
304 for i=1,slices do
305 insert(arr, intPart + (residue>=1 and 1 or 0))
306 residue = residue - 1
307 end
308 elseif type(cargo) == "number" then
309 -- We have weight array, devide by weight
310 local fullWeight=0
311 for i=1, #args do fullWeight = fullWeight+args[i] end
312 local residue = cargo
313 -- Give each slot weight 1
314 for i=1, #args do
315 insert(arr, 1)
316 residue = residue - 1
317 end
318 local tmpResidue = residue
319 -- Give each slot by weights
320 for i=1, #args do
321 local prtVal = floor(args[i]/fullWeight*tmpResidue)
322 arr[i] = arr[i] + prtVal
323 residue = residue - prtVal
324 end
325 -- Give last parts to most heavyes slots, if they will still not have enought
326 local overage = fullWeight/cargoCount
327 local optima = 1
328 while residue>0 do
329 for i=1,#args do
330 if args[i]/arr[i] > args[optima]/arr[optima] then
331 optima = i
332 end
333 end
334 arr[optima] = arr[optima] + 1
335 residue = residue - 1
336 optima = (optima % #args) + 1
337 end
338 elseif type(cargo) == "table" then
339 -- Each cargo have weight
340
341 -- Make base 0 array
342 if cargo[0] == nil then
343 cargo[0] = cargo[1]
344 table.remove(cargo,1)
345 end
346
347 local parts = args[1]
348 local len = tblLen(cargo)
349 local sum = {}
350 local avg
351 local T = {}; for i=1,len do T[i]={}end
352 local K = {}; for i=1,len do K[i]={}end
353
354 local penalty = function(a,b)
355 local x = (sum[b]or 0) - (sum[a]or 0)
356 x = x-avg
357 return x*x
358 end
359
360 sum[0]=0
361 for n=1, len do
362 sum[n] = (sum[n-1]or 0) + (cargo[n-1]or 0)
363 end
364 avg = (sum[len]or 0)/parts
365
366 for n=0, len do
367 T[1][n] = penalty(0,n)
368 K[1][n] = n
369 end
370
371 for m=2,parts do
372 for n=0,len do
373 T[m][n] = math.huge
374 K[m][n] = -1
375 for k=0,n do
376 if (T[m][n]or 0) > (T[m-1][k]or 0) + penalty(k,n) then
377 T[m][n] = (T[m-1][k]or 0) + penalty(k,n)
378 K[m][n] = k
379 end
380 end
381 end
382 end
383
384
385 for m=parts,2,-1 do
386 local sep = K[m][len]
387 arr[m] = len-sep
388 len = sep
389 end
390 arr[1] = K[1][len]
391 else
392 error("Wrong splitJob parameters: cargo")
393 end
394
395 return arr
396end
397
398--05_KrutoyTurtle.lua
399
400-- ********************************************************************************** --
401-- ** Main file of KrutoyTurtle program ** --
402-- ** ** --
403-- ** User interface, fill algirithm code. ** --
404-- ** ** --
405-- ********************************************************************************** --
406
407
408------------------------------------------------------
409-- Filling function variables --
410------------------------------------------------------
411
412-- Main patterns, using in jobs.fill() function
413-- Numbers tell what the count block will be placed
414-- Exists: fillPattern[name][z][y][x]
415-- [space]: here will no block
416-- 0: remove block here
417fillPattern = {
418 ['Plain'] =
419 { {{1}} },
420 ['BoxGrid'] =
421 { {{3, 2, 2, 2},
422 {2, 1, 1, 1},
423 {2, 1, 1, 1},
424 {2, 1, 1, 1}},
425 {{2, 1, 1, 1},
426 {1},
427 {1},
428 {1}},
429 {{2, 1, 1, 1},
430 {1},
431 {1},
432 {1}},
433 {{2, 1, 1, 1},
434 {1},
435 {1},
436 {1}}},
437}
438
439
440-- List of avaliable flags
441local avaliableFlags = {
442 "hollow", -- Only box without bulk
443 "sides", -- Making boxes without corners
444 "corners", -- Making frames
445 "mirror", -- Pattern texture will mirrored
446 "mirror -1", -- Each mirrored pattern indexes will shif by one block
447 "mirror -1x",
448 "mirror -1y",
449 "mirror -1z",
450 "x++", "x--", -- Shift next coord. Userful for stairs
451 "y++", "y--",
452 "z++", "z--",
453 "wash", -- Replaces all nil to 0
454 "skip", -- Replaces all 0 to nil
455 "tunnel", "tube", -- tunnel without caps on start end end
456 "dense", -- Chest for item every 1 block
457
458 -- Flags changing fill iterator functions
459 "forth", -- Build first x-z blocks, then go to next y layer
460 "printer", -- Iterate block placing like printer
461 "down", -- Place all blocks down. Works with "printer" or "forth"
462 "up", -- Place all blocks up. Works with "printer" or "forth"
463
464 -- Flags for "plain" pattern
465 "clear", -- Replaces all pattern indexes to -1
466 "greedy", -- Save all items, putting them left ches
467 "mine", -- Duplicate tags clear+greedy
468 "ore", -- Duplicate "mine" but will crush only not cobble, dirt, gravel
469 "tidy", -- When inventory is full drops all thrash
470 "lava", -- Using bucket from first slot and refuels from it. Adds "clear"
471}
472
473
474------------------------------------------------------
475-- Other variables --
476------------------------------------------------------
477
478-- Enumeration to store names for the 6 directions
479local way = { FORWARD=0, RIGHT=1, BACK=2, LEFT=3, UP=4, DOWN=5 }
480local wayName = { [0]='Forward', ' Right', 'Back', 'Left', 'Up', 'Down'}
481
482local MAXTURTLES = 128 -- Maximum amount of turtles in swarm
483local idle = false -- Turtle is idle and will be moved when something ask get out
484local fillParamFile = "Krutoy/fillParams" -- Path to file with all fill parameters
485local scrW, scrH = term.getSize() -- Get screen params
486local serchOwnerThread, interfaceThread
487local KTurtle_source = "http://pastebin.com/raw.php?i=g2ZqawdP"
488
489local args = { ... }
490
491local surround = {
492[way.FORWARD] = vec3( 0, 1, 0),
493[way.RIGHT] = vec3( 1, 0, 0),
494[way.BACK] = vec3( 0,-1, 0),
495[way.LEFT] = vec3(-1, 0, 0),
496[way.UP] = vec3( 0, 0, 1),
497[way.DOWN] = vec3( 0, 0,-1)
498}
499
500local touch = {
501["getout"] = 14,
502["killMe"] = 13
503}
504
505------------------------------------------------------
506-- Debug in IDE --
507------------------------------------------------------
508IDE = (term==nil) and true or false
509--[[
510if IDE then
511 loadfile('CC_emulator.lua')()
512end
513
514local function BREACKPOINT()
515 print("DEBUG")
516 while true do
517 local dbgFnc = setfenv(loadstring(read()), getfenv(1))
518 local status, err = pcall(dbgFnc)
519 if err then print(); print(err) else return end
520 end
521end]]
522
523
524-- ################################################################################## --
525-- ## Utilities ## --
526-- ################################################################################## --
527
528--===========================================================
529-- Read user input and separate into table
530--===========================================================
531local function readTable(separator)
532 local resultStr = read()
533 if resultStr ~= '' then
534 local result = {}
535 for v in string.gmatch(resultStr, separator) do
536 insert(result,v)
537 end
538 return result
539 end
540 return nil
541end
542
543-- ################################################################################## --
544-- ## Startup functions ## --
545-- ################################################################################## --
546
547-- ==============================
548-- Auto-update
549-- Download and replace running file
550-- ==============================
551local function autoUpdate()
552 -- Get version of last
553 if not http then return end
554 local httpResponce = http.get(KTurtle_source)
555 local allText = httpResponce.readAll()
556 httpResponce.close()
557
558 local newVersion = 0
559 local _,verPos = string.find(allText, 'K_VERSION *= *')
560 if verPos then
561 newVersion = tonumber(string.match(allText, '%d+[%.?%d*]*', verPos+1))
562 end
563
564 -- Compare and replace
565 if K_VERSION < newVersion then
566 local sFile = shell.getRunningProgram()
567 local f = fs.open(sFile, "w")
568 f.write(allText)
569 f.close()
570 clearScreen()
571 print("New version downloaded!")
572 print("Rebooting to apply changes...")
573 sleep(0.1)
574 os.reboot()
575 sleep(10)
576 end
577end
578
579local function radiateRedstine(val)
580 for k,v in pairs(redstone.getSides()) do
581 redstone.setOutput(v, val)
582 end
583end
584
585local function waitingInHarbor()
586 -- Also, turn on nearest turtles
587 Turtle.turnRight()
588 -- Set redstone signal to nearest turtles
589 radiateRedstine(true)
590 pcall(peripheral.call,'front','turnOn')
591 pcall(peripheral.call,'back' ,'turnOn')
592end
593
594local function leaveHarbor()
595 -- Clear redstone signals
596 radiateRedstine(false)
597end
598
599
600local function workInTeamSetup(leftShift)
601 local harborHeight = 2
602 local maxWidth = 256
603 Turtle.setBeacon("storageStart", leftShift, 0, 0)
604 Turtle.addForbiddenWayZone(way.LEFT, way.UP, leftShift+1,0,0, leftShift+maxWidth,0,0)
605 Turtle.addForbiddenWayZone(way.RIGHT, way.DOWN, leftShift ,0,1, leftShift+maxWidth,0,1)
606
607 Turtle.addAlterZone(way.UP, leftShift+1,0,1, leftShift+maxWidth,0,harborHeight)
608 Turtle.addAlterZone(way.RIGHT, leftShift ,0,0, leftShift+maxWidth,0,0)
609
610 Turtle.addSafeZone(leftShift,0,0, leftShift+maxWidth+1,0,harborHeight)
611
612 -- Block for path finding back wall and volume over harbor
613 Turtle.world:setVolume(leftShift-1, -1, 0, leftShift+maxWidth+1, 0, 128, true)
614 Turtle.world:setVolume(leftShift , 0, 0, leftShift+maxWidth , 0, harborHeight, nil)
615end
616
617
618
619local function setupCommunications()
620 -- Send message to neiborhoods to get out from way
621 Turtle.onMoveAttempt = function(attempt, newX, newY, newZ, direction)
622 if attempt > 2 and newY==0 then
623 local _,data = Turtle.inspect(direction)
624 if data and data.name and data.name:find("Turtle") then
625 Turtle.touch(touch["getout"], direction)
626 end
627 end
628 end
629
630 -- Get out
631 thread.create(function()
632 while true do
633 local strenght = Turtle.sense(-1) -- -1 means from all sides
634 if strenght == touch["getout"] and idle then
635 local oldPos = Turtle.pos:clone()
636 local oldOrient = Turtle.orient
637 Turtle.up()
638 sleep(1)
639 if idle then Turtle.goTo(oldPos); Turtle.setOrient(oldOrient) end
640 end
641 end
642 end)
643end
644
645
646-- ==============================
647-- Slave behavior
648-- Manage slave actions
649-- ==============================
650local slaveBehavior = function()
651 local distToOwner
652
653 while true do
654 distToOwner = swarm.searchOwner()
655
656 -- Kill other thread
657 thread.kill(interfaceThread)
658
659 leaveHarbor()
660
661 -- Owner is found. Show this on screen
662 KUI.setWindow({{ id='slaveLabel', type='textPanel', text='This turtle\nis slave.\n\nWaiting orders.',
663 x=8,y=scrH/2-3, w=scrW-16,h=6}})
664
665 -- This turtle is slave. Waiting orders and run them
666 local fillParams
667 local firstShift = true
668
669 -- loop for orders from master
670 while true do
671
672 idle = true
673 local orderFromOwner = swarm.waitOrders()
674 idle = false
675
676 if not orderFromOwner then
677
678 elseif orderFromOwner.name == 'disassemble' then
679 -- Just go home and spam request to be picked up
680 while true do
681 Turtle.goTo("storageStart")
682 Turtle.setOrient(way.BACK)
683 Turtle.touch(touch["killMe"])
684 sleep(0.5)
685 end
686
687 elseif orderFromOwner.name == 'rightShift' then
688 -- If this turtle is fresh, suck fuel from down and refuel
689 while Turtle.isLowFuel() do
690 clearScreen()
691 print("Need Fuel! Please place in first slot or in chest under me.")
692 Turtle.selectEmptySlot()
693 turtle.suckDown()
694 turtle.refuel()
695 end
696
697 if not firstShift then distToOwner = distToOwner+1 end
698 firstShift=false
699
700 -- Correct the location
701 Turtle.setPos(-1,0,0)
702 Turtle.goTo(0,0,0)
703
704 elseif orderFromOwner.name == 'fillOptions' then
705 fillParams = orderFromOwner
706
707 if turtle.getFuelLevel() <= fillParams.stats.totalFuelNeeded then
708 -- Turtle have not anought fuel. It must send this to owner.
709 swarm.transmitToMaster({name='ERROR'})
710
711 -- Run refuel loop till be fueled
712 while turtle.getFuelLevel() < fillParams.stats.totalFuelNeeded do
713 clearScreen()
714 print("Need Fuel! Please place fuel in turtle to consume...")
715 print()
716 for i=1,16 do
717 turtle.select(i)
718 turtle.refuel()
719 end
720 end
721 end
722
723 elseif orderFromOwner.name == 'lash' then
724 -- IMPORTANT:
725 -- We changing self-position to turtle. It will think that we standing in negative coords
726 Turtle.setPos(-fillParams.shift.x + distToOwner, Turtle.pos.y, Turtle.pos.z)
727 workInTeamSetup(-fillParams.shift.x)
728
729 -- Fill
730 jobs.fill(fillParams.volume,
731 vec3(fillParams.pos.x, fillParams.pos.y, fillParams.pos.z),
732 vec3(fillParams.size.x, fillParams.size.y, fillParams.size.z), fillParams.fillFlags, fillParams.stats)
733 end
734 end
735 end
736end
737
738
739-- ==============================
740-- Resume
741-- ==============================
742local function resume()
743 -- Check if resume required
744 if not Turtle.loadData() then return end
745
746 -- Refstone signal means we dont need to comeback
747 for k,v in pairs(redstone.getSides()) do
748 if redstone.getInput(v) then
749 Turtle.removeData()
750 return
751 end
752 end
753
754 local waitAbortThread, startResumeThread
755 local comebackBreack = true
756
757 -- Wait if user press any key to abourt comeback
758 local waitAbortFnc = function()
759 clearScreen()
760 print("Seems like turtle is restarting!")
761 print("Press ANY KEY to abort.")
762 print()
763 pressAnyKey()
764
765 thread.kill(startResumeThread)
766
767 Turtle.removeData()
768 comebackBreack = false
769 end
770
771 -- Wait 10 seconds and go to start
772 local startResumeFnc = function()
773 sleep(2)
774 term.setCursorPos(scrW/2-7, scrH/2-1)
775 print("Come back in:")
776 for i=10,1 do
777 sleep(2)
778 term.setCursorPos(scrW/2-7, scrH/2-1)
779 print(i)
780 end
781 sleep(2)
782
783 thread.kill(waitAbortThread)
784
785 clearScreen()
786 print("Comebacking...")
787
788 if Turtle.beacons and Turtle.beacons["storageStart"] and Turtle.beacons["startPos"] then
789 workInTeamSetup(Turtle.beacons["storageStart"].x)
790 if Turtle.pos.y > 0 then
791 Turtle.goTo(Turtle.pos.x, Turtle.pos.y, 0)
792 Turtle.goTo(0, 0, 0)
793 end
794 Turtle.goTo("startPos")
795 end
796 Turtle.setOrient(way.FORWARD)
797
798 Turtle.removeData()
799 comebackBreack = false
800 end
801
802 -- Lunch both threads and return only in case of resume solved
803 startResumeThread = thread.create(startResumeFnc)
804 waitAbortThread = thread.create(waitAbortFnc)
805 while comebackBreack do sleep(0.1) end
806end
807
808-- ################################################################################## --
809-- ## Main ## --
810-- ################################################################################## --
811function main()
812
813 -- If we loaded from disk, copy startup file onboard
814 if fs.exists("disk/startup") and not fs.exists("startup") then
815 fs.copy("disk/startup", "startup")
816 end
817
818 -- Update
819 autoUpdate()
820
821 -- Not matter working this turtle alone or with others,
822 -- it should know how to communicate
823 setupCommunications()
824
825 -- Check and resume if required
826 resume()
827
828
829 -- Seems like we are in line on harbor now
830 waitingInHarbor()
831
832 -- ==============================
833 -- Main menu
834 -- Fill options
835 -- ==============================
836 local interfaceFnc = function()
837 idle = true
838
839 while true do
840 local nextBtn = { id='btn_next', type='button', text='Next>>',
841 x=scrW-10,y=scrH,w=8,h=1, borderStyle='none'}
842
843 local fillOptionsWindow = {}
844 insert(fillOptionsWindow,{ id='optionsLabel', type='textPanel', text='Fill options',
845 x=0,y=0, w=scrW+2,h=3})
846 local inputSize = { id='btn_size', type='input', text='',
847 x=3,y=6, w=scrW-13,h=1, borderStyle='none', align='left', padding={0,0,0,9}}
848
849 insert(fillOptionsWindow,{ id='btn_pattern', type='button', text='Pattern: ""',
850 x=3,y=4, w=scrW-4,h=1, borderStyle='none', align='left'})
851 insert(fillOptionsWindow,{ id='txt_size', type='text', text=' Size: ',
852 x=3,y=6, w=9,h=1, borderStyle='none', align='left'})
853 insert(fillOptionsWindow, inputSize)
854 insert(fillOptionsWindow,{ id='btn_flags', type='button', text=' Flags: _',
855 x=3,y=8, w=scrW-4,h=1, borderStyle='none', align='left'})
856 insert(fillOptionsWindow,nextBtn)
857
858
859 local sizeX,sizeY,sizeZ
860 local pattern = nil
861 local pos = vec3(0,1,0)
862 local fillFlags = {}
863 local optionId, sender = 'btn_pattern', nil
864
865
866 if IDE then
867 sizeX,sizeY,sizeZ, pattern, pos, fillFlags = 2, 3, 3,'BoxGrid', pos, {}
868 optionId = 'btn_next'
869 end
870
871 -- Size input
872 KUI.onKeyPressed = function(keyCode)
873 if KUI.selectedObj.id == 'btn_size' then
874 local inputText = KUI.selectedObj.text
875 if inputText ~= "" then
876 local result = {}
877 for s in string.gmatch(inputText, "%S+") do
878 if type(tonumber(s)) == "number" then
879 insert(result,tonumber(s))
880 end
881 end
882 if #result == 3 then
883 sizeX,sizeY,sizeZ = result[1],result[2],result[3]
884 end
885 end
886 end
887 end
888
889
890 local helpTab = "btn_pattern"
891 while optionId ~= 'btn_next' or not pattern or not sizeX or not sizeY or not sizeZ do
892 KUI.setWindow(fillOptionsWindow, helpTab)
893 optionId, sender = KUI.navigate()
894
895 if optionId == 'btn_pattern' then
896 -- Fish all vox files in root
897 local tmpPatternList = {}
898 for k,_ in pairs(fillPattern) do insert(tmpPatternList, k) end
899 for _,v in pairs(fs.list('')) do
900 local fileExtension = string.sub(v,#v-3,#v)
901 if fileExtension == '.vox' or fileExtension == '.nfa' and not fillPattern[v] then
902 insert(tmpPatternList, v)
903 end
904 end
905
906 -- Assemble string to show user and wait his choose
907 local currLine = ''
908 for k,v in pairs(tmpPatternList) do
909 currLine = currLine..' '..k..' - '..v..'\n'
910 end
911 clearScreen()
912 print(currLine)
913 local result
914 while true do
915 local event, n = os.pullEvent("char")
916 n = tonumber(n)
917 if type(n) == 'number' and n >= 1 and n <= #tmpPatternList then
918 result = n
919 break
920 end
921 end
922
923 -- Load needed files if we dont had them already
924 local pattLen = tblLen(fillPattern)
925 pattern = tmpPatternList[result]
926 if result > pattLen then -- This pattern is file. Load File
927 local fileExtension = string.sub(pattern,#pattern-3,#pattern)
928 if fileExtension == '.nfa' then
929 fillPattern[pattern],sizeX,sizeY,sizeZ = volumeLoader.load_nfa(pattern)
930 inputSize.text = sizeX..' '..sizeY..' '..sizeZ
931 end
932 if fileExtension == '.vox' then
933 fillPattern[pattern],sizeX,sizeY,sizeZ = volumeLoader.load_vox(pattern)
934 inputSize.text = sizeX..' '..sizeY..' '..sizeZ
935 end
936 end
937
938 -- Show pattern key to user
939 sender.text = 'Pattern: '..pattern
940 elseif optionId == 'btn_size' then
941
942 elseif optionId == 'btn_flags' then
943 local oldWnd = KUI.currentWindow
944
945 KUI.setWindow({{ id='flags_label', type='textPanel',
946 text='Add flags if need, separate\with commas, and press ENTER',
947 x=1,y=0, w=scrW,h=4}})
948 term.setCursorPos(5,7)
949 local result = readTable("[%a%d]+")
950 if result then
951 fillFlags = makeSet(result)
952 sender.text = ' Flags: '.. table.concat(result,", ")
953 end
954
955 KUI.setWindow(oldWnd, optionId)
956 end
957
958 if not pattern or not sizeX or not sizeY or not sizeZ then
959 inputSize.text = (sizeX or '0')..' '..(sizeY or '0')..' '..(sizeZ or '0')
960 end
961 KUI.nextTab()
962 helpTab = KUI.selectedObj.id
963 end
964 KUI.onKeyPressed = nil
965
966 -- Kill searching thread
967 thread.kill(serchOwnerThread)
968
969 -- Parse negative numbers
970 if sizeX < 0 then sizeX=math.abs(sizeX); pos.x=pos.x-sizeX end
971 if sizeY < 0 then sizeY=math.abs(sizeY); pos.y=pos.y-sizeY end
972 if sizeZ < 0 then sizeZ=math.abs(sizeZ); pos.z=pos.z-sizeZ end
973
974 -- Volume of "owner" or "master" turtle
975 local masterVolume, masterStats
976 local gsize = vec3(sizeX,sizeY,sizeZ)
977 local masterSize = gsize
978 local wholeVlume, wholeStats = jobs.computeFillVolume(vec3(), gsize, gsize, fillPattern[pattern], fillFlags)
979
980 -- ==============================
981 -- Find other turtles
982 -- ==============================
983 local errorsArray = {}
984 local slavesCount = 0
985
986
987 -- Loop while no errors sended from turtles
988 repeat
989
990 -- If we have a dock, we need to place turtles one by one
991 local alreadyHaveDock = false
992 if fillFlags['dock'] then
993 if not alreadyHaveDock then slavesCount = jobs.createDock(sizeX) end
994 alreadyHaveDock = true
995 else
996 -- Show box that we waiting for slaves
997 KUI.setWindow({{ id='findSlaves', type='textPanel', text='Trying to find slaves\nWait a sec...', x=8,y=scrH/2-3, w=scrW-16,h=6}})
998
999 -- Broadcast message and get all responses
1000 slavesCount = swarm.findSlaves(MAXTURTLES/200) ;if IDE then slavesCount = 1 end
1001 end
1002
1003 if(slavesCount > 0) then
1004 KUI.setWindow({{ id='slavesFound', type='textPanel', text='Slaves found!\nSending orders...', x=8,y=scrH/2-3, w=scrW-16,h=6}})
1005
1006 -- Slice whole wolume to equal stacks
1007 local slices = splitJob(wholeStats.shelfX, slavesCount+1)
1008
1009
1010 local partCursor = 0
1011 for i=0, slavesCount do
1012 local size = vec3(slices[i+1], sizeY, sizeZ)
1013 local prt_shift = vec3(partCursor, 0, 0)
1014 partCursor = partCursor + slices[i+1]
1015
1016 -- Get volume for this part
1017 local volumePart, statPart = jobs.computeFillVolume(prt_shift, size, gsize, fillPattern[pattern], fillFlags)
1018
1019 if i==0 then
1020 -- This turtle is master and we have slaves
1021 masterSize = size
1022 masterVolume, masterStats = volumePart, statPart
1023 workInTeamSetup(0)
1024 else
1025 -- Prepare table with parameters for send
1026 local taskObj = {name='fillOptions', volume=volumePart, size={x=size.x,y=size.y,z=size.z},
1027 shift={x=prt_shift.x,y=prt_shift.y,z=prt_shift.z}, pos={x=pos.x,y=pos.y,z=pos.z},
1028 fillFlags=fillFlags, stats=statPart}
1029
1030 swarm.transmitTask(i, taskObj)
1031 end
1032 end
1033
1034 -- Show that we sended volumes and wait errors
1035 KUI.setWindow({{ id='waitingWhine', type='textPanel', text='Orders sended.\nWait problems if had...', x=8,y=scrH/2-3, w=scrW-16,h=6}})
1036
1037 -- Make new array of errors
1038 errorsArray = {}
1039 local received = swarm.receiveFromSlave(slavesCount/100)
1040 while received do
1041 if received.name == "ERROR" then insert(errorsArray, received) end
1042 received = swarm.receiveFromSlave(slavesCount/100)
1043 end
1044
1045 -- Show errors
1046 if #errorsArray > 0 then
1047 local whineArrayStr = ""
1048 for k,v in pairs(errorsArray) do
1049 whineArrayStr = whineArrayStr .. 'Turt #' .. v.slaveId .. ': ' .. v.message
1050 end
1051 KUI.setWindow({{ id='showWhine', type='textPanel', text='Huston!\n We have a problems:\n' .. whineArrayStr .. '\nPress ENTER', x=8, y=scrH/2-2, w=scrW-10,h=10}})
1052 read()
1053 end
1054 else -- slavesCount == 0
1055 Turtle.setBeacon("storageStart", 0, 0, 0)
1056 masterVolume, masterStats = wholeVlume, wholeStats
1057 end
1058 until #errorsArray == 0
1059
1060 local needUseApprove = not fillFlags['dock']
1061
1062 -- ==============================
1063 -- Info screen
1064 -- ==============================
1065 local infoText = ' INFO \n' ..
1066 'Fuel level: ' .. (masterStats.totalFuelNeeded <= turtle.getFuelLevel() and 'OK' or 'NOT ENOUGHT!!') .. '\n' ..
1067 'Blocks by type:\n'
1068
1069 -- Print how much we need of blocks by each type
1070 for k,v in ipairs(wholeStats.totalBlockByIndex) do
1071 local stacks = floor(v/64)
1072 local modStacks= v%64
1073 infoText = infoText .. ' #'..k..': '..((stacks>0) and stacks..'x64' or '')..
1074 ((stacks>0 and modStacks>0) and ' + ' or '')..
1075 ((modStacks>0) and modStacks or '') .. '\n'
1076 end
1077 infoText = infoText ..'Press ENTER\n'
1078
1079
1080 KUI.setWindow({{ id='fillInfo', type='textPanel', text=infoText, x=1, y=1, w=scrW,h=scrH, align='left'}})
1081 if needUseApprove then read() end
1082 clearScreen()
1083
1084 -- Lash slaves
1085 for i=1, slavesCount do
1086 swarm.transmitTask(i, {name='lash'})
1087 end
1088
1089 leaveHarbor()
1090
1091 idle = false
1092 jobs.fill(masterVolume, pos, masterSize, fillFlags, masterStats)
1093
1094 -- Disassemble if we work with dock
1095 if fillFlags['dock'] then
1096 jobs.disassembleDock(slavesCount)
1097 end
1098
1099 idle = true
1100 end
1101 end
1102
1103
1104 -- Do not show anything. Just wait if we had owner
1105 serchOwnerThread = thread.create(slaveBehavior)
1106 interfaceThread = thread.create(interfaceFnc)
1107
1108 -- Make look like we working.
1109 while true do sleep(999999) end
1110
1111end
1112
1113-- Function, called in start of all.
1114if IDE then main() end
1115
1116--07_Jobs.lua
1117
1118
1119
1120-- ################################################################################## --
1121-- ## PROGRAMS ## --
1122-- ################################################################################## --
1123
1124
1125
1126-- Looks like a class
1127jobs = {}
1128jobs.__index = jobs
1129
1130--===========================================================
1131-- Computing volume by given pattern
1132-- pos is shift fo pattern
1133--===========================================================
1134function jobs.computeFillVolume(pos, size, gsize, pattern, fillFlags)
1135
1136 -- ==============================
1137 -- Variables
1138 -- ==============================
1139
1140 pos = pos or vec3()
1141 size= size or vec3()
1142
1143 local sizeX,sizeY,sizeZ = size:unpack()
1144 local gsizeX,gsizeY,gsizeZ = gsize:unpack()
1145
1146 local totalVolume = sizeX*sizeY*sizeZ -- Total volume of blocks
1147 local vol = arr3d() -- 3d array of whole volume filling territory
1148
1149 -- Statistics
1150 local stats = {}
1151 stats.totalBlocksToPlace = 0 -- Total count of blocks that must be placed
1152 stats.totalBlockByIndex = {} -- Blocks count by indexes
1153 stats.totalFuelNeeded = 0
1154 stats.shelfX = {} -- count of blocks on each X slice
1155 stats.shelfY = {} -- count of blocks on each Y slice
1156 stats.shelfZ = {} -- count of blocks on each Z slice
1157 stats.shelfZ_clear = {} -- Determine if this Z level have something but 0
1158
1159
1160 -- Auto flags
1161 if fillFlags['hollow'] then fillFlags['sides'] = true; fillFlags['corners'] = true end
1162 if fillFlags['mine'] then fillFlags['clear'] = true; fillFlags['greedy'] = true end
1163 if fillFlags['ore'] then fillFlags['clear'] = true; fillFlags['greedy'] = true; fillFlags['mine'] = true end
1164 if fillFlags['lava'] then fillFlags['clear'] = true end
1165
1166
1167
1168 -- ==============================
1169 -- Preparing
1170 -- ==============================
1171
1172 -- Pattern sizes per axis
1173 local ptSzX, ptSzY, ptSzZ = 0,0,0
1174
1175 -- Get sizes of pattern
1176 local rawPtrn = (pattern.raw and pattern:raw() or pattern)
1177 for z,vy in pairs(rawPtrn) do
1178 if ptSzZ < z then ptSzZ = z end
1179
1180 for y,vx in pairs(vy) do
1181 if ptSzY < y then ptSzY = y end
1182
1183 for x,v in pairs(vx) do
1184 if ptSzX < x then ptSzX = x end
1185 end
1186 end
1187 end
1188
1189
1190
1191 -- We must make a large
1192 -- array of all blocks in volume
1193 for O=0, totalVolume-1 do
1194 local s_u, s_v, s_w
1195 s_u = floor(O/(sizeX*sizeY)) -- z
1196 s_v = floor(O/sizeX) % sizeY -- y
1197 s_w = floor(O%sizeX) -- x
1198
1199 local u,v,w = s_u+pos.z, s_v+pos.y, s_w+pos.x
1200
1201
1202 -- Pattern picker must think we are on this 'imagined' or 'fabled' positions
1203 local fabled_u, fabled_v, fabled_w = u, v, w
1204 if(fillFlags['mirror -1'] or fillFlags['mirror -1z']) then
1205 fabled_u = fabled_u + floor( (u+ptSzZ-1) / (ptSzZ*2-1) )
1206 end
1207 if(fillFlags['mirror -1'] or fillFlags['mirror -1y']) then
1208 fabled_v = fabled_v + floor( (v+ptSzY-1) / (ptSzY*2-1) )
1209 end
1210 if(fillFlags['mirror -1'] or fillFlags['mirror -1x']) then
1211 fabled_w = fabled_w + floor( (w+ptSzX-1) / (ptSzX*2-1) )
1212 end
1213
1214 -- Compute pattern array indexes. Place on pattern that we want to take
1215 local ptX, ptY, ptZ = fabled_w%ptSzX, fabled_v%ptSzY, fabled_u%ptSzZ
1216
1217 -- Flag "mirror" must mirrored all coordinates on even step
1218 if(fillFlags['mirror'] or fillFlags['mirror -1'] or fillFlags['mirror -1x'] or
1219 fillFlags['mirror -1y'] or fillFlags['mirror -1z']) then
1220 if (floor(fabled_w/ptSzX) % 2) == 1 then ptX = ptSzX-ptX-1 end
1221 if (floor(fabled_v/ptSzY) % 2) == 1 then ptY = ptSzY-ptY-1 end
1222 if (floor(fabled_u/ptSzZ) % 2) == 1 then ptZ = ptSzZ-ptZ-1 end
1223 end
1224
1225
1226 -- Get block index from pattern, demands on position of turtle
1227 local blockIndex = nil -- nil: avoid this block
1228 if pattern[ptZ+1] and pattern[ptZ+1][ptY+1] then
1229 blockIndex = pattern[ptZ+1][ptY+1][ptX+1]
1230 end
1231
1232
1233 -- When we use 'sides' or 'corners' flags, we avoid all blocks inside volume
1234 if fillFlags['sides'] or fillFlags['corners'] then
1235 if(u>0 and u<gsizeZ-1) and (v>0 and v<gsizeY-1) and (w>0 and w<gsizeX-1) then
1236 blockIndex = 0
1237 end
1238 end
1239
1240 -- If only 'sides' flag enabled, clear all corners
1241 if fillFlags['sides'] and not fillFlags['corners'] then
1242 if not(((u>0 and u<gsizeZ-1) and (v>0 and v<gsizeY-1)) or
1243 ((u>0 and u<gsizeZ-1) and (w>0 and w<gsizeX-1)) or
1244 ((w>0 and w<gsizeX-1) and (v>0 and v<gsizeY-1))) then
1245 blockIndex = 0
1246 end
1247 end
1248
1249 -- If only 'corners', clear all sides
1250 if fillFlags['corners'] and not fillFlags['sides'] then
1251 if not(((u==0 or u==gsizeZ-1) and (v==0 or v==gsizeY-1)) or
1252 ((u==0 or u==gsizeZ-1) and (w==0 or w==gsizeX-1)) or
1253 ((w==0 or w==gsizeX-1) and (v==0 or v==gsizeY-1))) then
1254 blockIndex = 0
1255 end
1256 end
1257 if fillFlags['tunnel'] and (w>0 and w<gsizeX-1) and (u>0 and u<gsizeZ-1) then
1258 blockIndex = 0
1259 end
1260 if fillFlags['tube'] and (w>0 and w<gsizeX-1) and (v>0 and v<gsizeY-1) then
1261 blockIndex = 0
1262 end
1263 if fillFlags['wash'] then if blockIndex== nil then blockIndex = 0 end end
1264 if fillFlags['skip'] then if blockIndex== 0 then blockIndex = nil end end
1265
1266 -- Clear all blocks, ignoring any pattern
1267 if fillFlags['clear'] then blockIndex = 0 end
1268
1269 -- Put block index in volume array
1270 vol:set(s_w,s_v,s_u,blockIndex)
1271
1272 -- Statistics
1273 if blockIndex then
1274 stats.totalBlocksToPlace = stats.totalBlocksToPlace + 1
1275 stats.totalBlockByIndex[blockIndex] = (stats.totalBlockByIndex[blockIndex] or 0) + 1
1276
1277 -- Shelfes
1278 stats.shelfX[s_w] = (stats.shelfX[s_w] or 0) + 1
1279 stats.shelfY[s_v] = (stats.shelfY[s_v] or 0) + 1
1280 stats.shelfZ[s_u] = (stats.shelfZ[s_u] or 0) + 1
1281
1282 -- True if this level have other then 0
1283 if stats.shelfZ_clear[s_u] == nil then stats.shelfZ_clear[s_u] = true end
1284 stats.shelfZ_clear[s_u] = stats.shelfZ_clear[s_u] and (blockIndex == 0)
1285 end
1286 end
1287
1288 -- More statistics
1289 stats.totalFuelNeeded = sizeX*sizeY*(2 + floor(sizeZ/3))
1290
1291 -- Compute suggest pattern
1292 local suggestPattern = {}
1293 local blockList = {}
1294 local blockIdxs = {}
1295 local slotsToIndex = {}
1296 for i=1,16 do
1297 if stats.totalBlockByIndex[i] and stats.totalBlockByIndex[i] > 0 then
1298 insert(blockList, stats.totalBlockByIndex[i])
1299 insert(blockIdxs, i)
1300 end
1301 end
1302 if #blockList == 0 then
1303 slotsToIndex[0] = 16
1304 for j=1, 16 do insert(suggestPattern, 0) end
1305 elseif #blockList == 1 then
1306 slotsToIndex[1] = 16
1307 for j=1, 16 do insert(suggestPattern, blockIdxs[1]) end
1308 else
1309 local cargo = splitJob(16, unpack(blockList))
1310 for i=1,#cargo do
1311 slotsToIndex[blockIdxs[i]] = cargo[i]
1312 for j=1, cargo[i] do insert(suggestPattern,blockIdxs[i]) end
1313 end
1314 end
1315 stats.suggestPattern = suggestPattern
1316 stats.typesCont = #blockIdxs
1317 stats.slotsToIndex = slotsToIndex
1318
1319
1320
1321 return vol, stats
1322end
1323
1324
1325--===========================================================
1326-- Super-duper cool building program.
1327-- Using pre-made volume to fill it
1328--===========================================================
1329function jobs.fill(...)
1330 local vol, pos, size, fillFlags, stats = ...
1331
1332 -- ==============================
1333 -- Preparing
1334 -- ==============================
1335 local sizeX, sizeY, sizeZ = size:unpack()
1336
1337 -- Preparing world for path finding
1338 -- Note that world is only in filling volume and starts for [0,0,0]
1339 local p1,p2 = pos, size+pos
1340
1341 -- Block some cells to prevent gouing on forbidden places
1342 -- Bottom and Top
1343 Turtle.world:setVolume(p1.x, p1.y, p1.z-1, p2.x, p2.y, p1.z-1, true)
1344 Turtle.world:setVolume(p1.x, p1.y, p2.z+1, p2.x, p2.y, p2.z+1, true)
1345 -- Left and right
1346 Turtle.world:setVolume(p1.x-1, p1.y, p1.z, p1.x-1, p2.y, p2.z, true)
1347 Turtle.world:setVolume(p2.x+1, p1.y, p1.z, p2.x+1, p2.y, p2.z, true)
1348 -- Forth
1349 Turtle.world:setVolume(p1.x, p2.y+1, p1.z, p2.x, p2.y+1, p2.z, true)
1350 -- Inter point
1351 Turtle.world:set(0,1,0, nil)
1352
1353
1354 -- There we will storage what slots was deplited, out of building blocks
1355 -- 0: slot in use, have blocks.
1356 -- 1: slot deplited, haven't blocks or have thrash
1357 local blacklistPattern = {}
1358
1359 local isGoBackAfterFill = true
1360 local slotsPattern
1361 local armedByIndex = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
1362 Turtle.setBeacon("startPos", Turtle.pos:clone())
1363
1364 -- Make array representing how much blocks each type we need to place
1365 -- Each time block placed value will be decremented
1366 local totalBlockByIndexLeft = {}
1367 for k,v in ipairs(stats.totalBlockByIndex) do totalBlockByIndexLeft[k] = v end
1368
1369 -- We need to create array with cells where we already was, and then not need to dig
1370 -- in case we clearing then volume
1371 local clearedPionts = arr3d()
1372 if stats.totalBlockByIndex[0] then
1373 Turtle.onMove = function (attempt, newX, newY, newZ, direction)
1374 clearedPionts:set(Turtle.pos.x,Turtle.pos.y,Turtle.pos.z,true)
1375 end
1376 end
1377
1378 --[[ Saving fill parameters
1379 do
1380 local f = fs.open(fillParamFile, "w")
1381 f.write(table.toString({...}))
1382 f.close()
1383 end]]--
1384
1385
1386 -- ==============================
1387 -- Functions
1388 -- ==============================
1389
1390 local goThruGangway = function(...)
1391 if Turtle.pos.y ~= 0 or Turtle.pos.z~= 0 then
1392 Turtle.pathTo(0,1,0)
1393 Turtle.goTo(0,0,0)
1394 end
1395 Turtle.goTo(...)
1396 end
1397
1398 local fillGetPos = function(x,y,z)
1399 -- Shift next coord with flags
1400 -- Userful for stairs and diagonals
1401 local shift = vec3()
1402 if y then shift.x = y*(fillFlags['x++'] and 1 or (fillFlags['x--'] and -1 or 0)) end
1403 if x then shift.y = x*(fillFlags['y++'] and 1 or (fillFlags['y--'] and -1 or 0)) end
1404 if y then shift.z = y*(fillFlags['z++'] and 1 or (fillFlags['z--'] and -1 or 0)) end
1405
1406 local changeVec = pos + shift
1407
1408 -- nil means we dont need change current position
1409 if not x then x = Turtle.pos.x-changeVec.x end
1410 if not y then y = Turtle.pos.y-changeVec.y end
1411 if not z then z = Turtle.pos.z-changeVec.z end
1412
1413 local targetPos = vec3(x,y,z) + changeVec
1414 return targetPos
1415 end
1416
1417 -- Function same as GoTo, but consider position of building and incremental shifting
1418 local _fillGoToFnc = function(x,y,z)
1419 Turtle.goTo(fillGetPos(x,y,z))
1420 end
1421 local _fillPathToFnc = function(x,y,z)
1422 Turtle.pathTo(fillGetPos(x,y,z))
1423 end
1424
1425 -- Sometimes we need using safe way to go, so we will need to
1426 -- switch this handle to _fillPathToFnc
1427 local fillGoToFnc = _fillGoToFnc
1428
1429
1430 -- If we handle some unsolutionabled error, we can hope only on user.
1431 -- Going to start and waiting until he fix all
1432 local backToStartFnc = function(msg)
1433 print()
1434 print(msg)
1435
1436 goThruGangway("startPos")
1437 Turtle.setOrient(way.FORWARD)
1438
1439 idle = true
1440 pressAnyKey()
1441 idle = false
1442 end
1443
1444 local dropIfTidy = function()
1445 local itemsDropped = 0
1446 if fillFlags['tidy'] then
1447 for i=1,16 do
1448 turtle.select(i)
1449 local data = turtle.getItemDetail()
1450 if data and Turtle.isThrash(data.name) then
1451 itemsDropped = itemsDropped + turtle.getItemCount(i)
1452 turtle.dropDown()
1453 end
1454 end
1455 end
1456 return itemsDropped
1457 end
1458
1459 -- Go to start and wait user help for fuel
1460 local alreadyWaitFuel = false
1461 Turtle.onLowFuel = function(newX, newY, newZ, direction)
1462 if alreadyWaitFuel == false then
1463 alreadyWaitFuel = true
1464 local oldFuelLevel = turtle.getFuelLevel()
1465 while turtle.getFuelLevel() <= oldFuelLevel do
1466 backToStartFnc('Low fuel! Place something to refuel and press ANY KEY')
1467 shell.run("refuel all")
1468 end
1469 alreadyWaitFuel = false
1470 end
1471 end
1472
1473 --[[
1474 -- Show moving status on screen
1475 Turtle.onMoveAttempt = function(attempt, newX, newY, newZ, attDir)
1476 local scrW, scrH = term.getSize()
1477 term.setCursorPos (1,scrH-2)
1478 print(string.format('fill() size:%i,%i,%i pos:%i,%i,%i',sizeX, sizeY, sizeZ, pos.x, pos.y, pos.z))
1479 print(string.format('[Turtle] pos:%i,%i,%i orient:%i',Turtle.pos.x, Turtle.pos.y, Turtle.pos.z,Turtle.orient))
1480 write(string.format('[WantTo] pos:%i,%i,%i dir:%i att:%i',newX, newY, newZ, attDir, attempt))
1481 end
1482 ]]--
1483
1484
1485 -- Go to the storage and suck needed blocks. Storage must be defined
1486 local reloadForFilling = function()
1487 -- First, unload blacklisted slotls
1488 -- This is blocks that we dont using for building
1489 local strgPos = Turtle.beacons["storageStart"]
1490
1491 for i=1, 16 do
1492 if blacklistPattern[i] and turtle.getItemCount(i) > 0 then
1493 goThruGangway(strgPos)
1494 Turtle.setOrient(way.LEFT)
1495
1496 turtle.select(i)
1497 turtle.drop()
1498 blacklistPattern[i] = nil
1499 end
1500 end
1501
1502 -- Then move to storages and take blocks
1503 for i=1, 16 do
1504 local texelIndex = slotsPattern[i]
1505 local hungryForCount = (stats.totalBlockByIndex[texelIndex] or 0) - (armedByIndex[texelIndex] or 0)
1506 local itemSpace = turtle.getItemSpace(i)
1507 local already = turtle.getItemCount(i)
1508 local needToSuck= math.min(itemSpace, hungryForCount)
1509 if( needToSuck > 0 and texelIndex > 0) then
1510 local chestStep = (fillFlags['dense'] and 1 or 2)
1511 goThruGangway((texelIndex-1)*chestStep +strgPos.x, strgPos.y, strgPos.z)
1512 Turtle.setOrient(way.BACK)
1513 turtle.select(i)
1514 if turtle.suck(needToSuck) then
1515 local sucked = turtle.getItemCount(i) - already
1516
1517 -- Show how much items we already have on board
1518 armedByIndex[texelIndex] = armedByIndex[texelIndex] + sucked
1519 end
1520 end
1521 end
1522 end
1523
1524 -- Go to start and try reload while accomplished
1525 local reloadFnc = function()
1526 -- Collect how much items we armed in reload session
1527 armedByIndex = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
1528
1529 local isReloaded = false
1530
1531 while not isReloaded do
1532 reloadForFilling()
1533
1534 -- Check if we reloaded all blocks
1535 local lastIndex = 0
1536 isReloaded = true
1537 for i=1, 16 do
1538 if slotsPattern[i] ~= lastIndex then
1539 lastIndex = slotsPattern[i]
1540 if totalBlockByIndexLeft[lastIndex] > 0 and armedByIndex[lastIndex] == 0 then
1541 -- Seems like this index isn't reloaded
1542 isReloaded = false
1543 end
1544 end
1545 end
1546
1547 if not isReloaded then
1548 backToStartFnc('Error: Reloading failed. Please make storage and press any key')
1549 end
1550 end
1551 end
1552
1553 -- Get slot pattern.
1554 do
1555 -- Search if turtle have anought tipes is inventory
1556 local typesExist = 0
1557 local ptrn = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} -- Standart pattern. All slots empty
1558 local lastUnemptySlot = 0
1559 local lastEnum = 1
1560
1561 for i=1, 16 do
1562 turtle.select(i)
1563 if( turtle.getItemCount(i) > 0) then
1564 if (lastUnemptySlot == 0) then
1565 lastUnemptySlot = i
1566 elseif (i>1) and (turtle.compareTo(lastUnemptySlot) == false) then
1567 lastEnum = lastEnum + 1
1568 lastUnemptySlot = i
1569 end
1570
1571 typesExist = typesExist + 1
1572 ptrn[i] = lastEnum
1573 end
1574 end
1575
1576 if stats.typesCont > typesExist then
1577 -- We have not anought items in inventory and need to use suggested pattern
1578 -- So we need to reload
1579 slotsPattern = stats.suggestPattern
1580 reloadFnc()
1581 else
1582 -- we should use pattern that we get ourself.
1583 -- But if we clearing, pattern mus be clear
1584 if fillFlags['clear'] then
1585 slotsPattern = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
1586 else
1587 -- Just work with items we have inside
1588 slotsPattern = ptrn
1589 end
1590 end
1591 end
1592
1593 -- Searching if wee have needed item in inventory
1594 local findInSlotsArrayByPattern = function (arr, n, blacklist)
1595 for i=1, 16 do
1596 if not blacklist[i] and arr[i] == n and (turtle.getItemCount(i) > 0) then
1597 return i
1598 end
1599 end
1600 return 0
1601 end
1602
1603
1604
1605
1606 -- The main function of placing blocks with parameters
1607 local fillFnc = function(x,y,z,direct,orient, blockIndex)
1608 -- nil means skipping
1609 if blockIndex == nil then return nil end
1610
1611 -- Error text only for fatals, where program cant do nothing
1612 local fillError
1613
1614 repeat -- Repeat until no fillErrors
1615 fillError = nil
1616
1617 if blockIndex > 0 then
1618 local slotWithBlock = findInSlotsArrayByPattern(slotsPattern, blockIndex, blacklistPattern)
1619 if slotWithBlock ~= 0 then
1620 fillGoToFnc(x,y,z)
1621 if orient then Turtle.setOrient(orient) end -- Can be nil - orientation don't matter
1622
1623 -- We have block and can put it on place
1624 local placeSucces = Turtle.place(slotWithBlock, direct)
1625
1626 if(placeSucces == true)then
1627 -- Block this cell in world, make it unable to go throught
1628 local blockPos = Turtle.getRelativeCoord(direct)
1629 Turtle.world:set(blockPos.x, blockPos.y, blockPos.z, true)
1630
1631 -- Decrement block for placing
1632 totalBlockByIndexLeft[blockIndex] = totalBlockByIndexLeft[blockIndex] - 1
1633 end
1634
1635 -- If slot was emptyed, we must note, that now there will be thrash
1636 -- Each next time when turtle dig, empty slot will filling
1637 if(turtle.getItemCount(slotWithBlock) == 0) then
1638 blacklistPattern[slotWithBlock] = true
1639
1640 -- Check again if we have another slot with item
1641 if findInSlotsArrayByPattern(slotsPattern, blockIndex, blacklistPattern) == 0 and
1642 totalBlockByIndexLeft[blockIndex] > 0 then
1643 -- No avaliable blocks to build!
1644 -- Save coords, reload and return
1645 local stopPos = Turtle.pos:clone()
1646
1647 reloadFnc()
1648
1649 -- Go back to work
1650 Turtle.pathTo(stopPos)
1651 end
1652 end
1653 else
1654 -- Fatal fillError. We are probably reloaded, but still havent blocks to place
1655 -- This can happend only with bug in code
1656 fillError = 'Fatal Fill Error: No blocks to place on {'..x..','..y..','..z..'}. I dont know what went wrong. '
1657 backToStartFnc(fillError)
1658 end
1659 else -- blockIndex == 0
1660 -- Remove block here and do nothing
1661 local blockPos = fillGetPos(x,y,z) + (orient and surround[orient] or surround[direct])
1662 if not clearedPionts(blockPos:unpack()) then
1663 fillGoToFnc(x,y,z)
1664 if orient then Turtle.setOrient(orient) end -- Can be nil - orientation don't matter
1665
1666 -- Choose action method
1667 if fillFlags['ore'] then
1668 Turtle.mine(direct)
1669 elseif fillFlags['lava'] then
1670 Turtle.place(1, direct, true)
1671 turtle.select(1)
1672 turtle.refuel()
1673 -- TODO: Fully refueled event
1674 --if turtle.getFuelLevel()>= turtle.getFuelLimit() then
1675 -- backToStartFnc("Turtle is complitely refueled!")
1676 -- os.reboot()
1677 --end
1678 else
1679 Turtle.dig(direct)
1680 end
1681
1682 -- We cleared this point, so add to list
1683 clearedPionts:set(blockPos.x,blockPos.y,blockPos.z, true)
1684
1685 -- If we should be greedy, chech we have free slots. And, free cargo if we havent
1686 if fillFlags['greedy'] then
1687 local noFreeSlots = true
1688 for i=1,16 do
1689 if turtle.getItemCount(i) < 1 then
1690 noFreeSlots = false
1691 break
1692 end
1693 end
1694
1695 -- We have no free slots! Need to drop some items.
1696 if noFreeSlots then
1697 -- If we dropped nothing or dropped only 1 block, go to start and drop there
1698 if dropIfTidy() <= 1 then
1699 -- Save coords, reload and return
1700 local stopPos = Turtle.pos:clone()
1701
1702 -- Honestly, we not really reloading. Just clearing items in black list
1703 for i=1,16 do
1704 blacklistPattern[i] = (slotsPattern[i]>0 and blacklistPattern[i] or 1)
1705 end
1706 reloadFnc()
1707
1708 -- Go back to work
1709 Turtle.pathTo(stopPos)
1710 end
1711 end
1712 end
1713 end
1714 end
1715 until not fillError
1716
1717 return nil
1718 end -- fillFnc()
1719
1720
1721
1722 -- Check bucked if we are refueling from lava
1723 if fillFlags['lava'] then
1724 while true do
1725 turtle.select(1)
1726 local data = turtle.getItemDetail()
1727 if not data or not (data.name == "minecraft:bucket" or data.name == "minecraft:lava_bucket") then
1728 backToStartFnc("Place a bucket in first slot and press ANY KEY")
1729 else
1730 break
1731 end
1732 end
1733
1734 -- And try to suck firs bucket from down
1735 Turtle.place(1, way.DOWN, true)
1736 turtle.refuel()
1737 end
1738
1739
1740
1741 -- ==============================
1742 -- Iterators
1743 -- ==============================
1744
1745 -- move to start position
1746 fillGoToFnc(0, 0, ((sizeZ>1) and 1 or 0))
1747
1748 -- forth is printing method, when we fill from us to forward
1749 if fillFlags['forth'] then
1750 for _y=0, sizeY-1 do
1751 for _z=0, sizeZ-1 do
1752 for _x=0, sizeX-1 do
1753 local x,y,z = _x,_y,_z
1754 local placeWay = way.DOWN
1755 local shift = 1
1756
1757 -- Ping-pong
1758 if not fillFlags['up'] and not fillFlags['down'] then
1759 if(y%2==1) then z = sizeZ-z-1; x = sizeX-x-1; placeWay=way.UP; shift=-1 end
1760 end
1761 if(z%2==1) then x = sizeX-x-1 end
1762
1763 if fillFlags['up'] then
1764 fillFnc(x,y,z-1, way.UP, nil, vol[z][y][x])
1765 elseif fillFlags['down'] then
1766 fillFnc(x,y,z+1, way.DOWN, nil, vol[z][y][x])
1767 else
1768 fillFnc(x,y,z+shift, placeWay, nil, vol[z][y][x])
1769 end
1770 end
1771 end
1772 end
1773
1774 -- Printer working as usual building programs
1775 elseif fillFlags['printer'] then
1776 for _z=0, sizeZ-1 do
1777 for _y=0, sizeY-1 do
1778 for _x=0, sizeX-1 do
1779 local x,y,z = _x,_y,_z
1780 if(z%2==1) then y = sizeY-y-1; x = sizeX-x-1 end -- Ping-pong
1781 if(y%2==1) then x = sizeX-x-1 end -- Ping-pong
1782
1783 if fillFlags['up'] then
1784 fillFnc(x,y,z-1, way.UP, nil, vol[z][y][x])
1785 else
1786 fillFnc(x,y,z+1, way.DOWN, nil, vol[z][y][x])
1787 end
1788 end
1789 end
1790 end
1791
1792 -- And most awesome and fast filling method
1793 -- It filling 3 blocks per move
1794 else
1795 local zStepsCount = math.ceil(sizeZ/3)-1 -- Count of levels, where robot moves horisontally
1796 local lastZStepIsCap = (sizeZ%3 == 1) -- The top level of volume is last level where turtle moves hor-ly
1797 local zLastStepLevel = zStepsCount*3+(lastZStepIsCap and 0 or 1) -- Z level where turtle will move last
1798 local boostMode = stats.totalBlockByIndex[0] == stats.totalBlocksToPlace -- clear all
1799 local yFirst = (sizeY > sizeX) -- We will go to Y coords first, then to next X
1800 local yBoost = boostMode and yFirst
1801 local size_v, size_w = sizeY-1, sizeX-1
1802 fillGoToFnc = _fillPathToFnc
1803
1804
1805 if yBoost then
1806 size_v, size_w = size_w, size_v
1807 end
1808
1809
1810 for _z=0, zStepsCount do
1811 for v=0, size_v do
1812 for w=0, size_w do
1813 local z = _z*3+1
1814 local currZStepIsLast = (_z==zStepsCount)
1815 if currZStepIsLast then z = zLastStepLevel end -- Cap of volume
1816
1817
1818 -- Ping-pong
1819 local currZIsEven = (_z%2==0)
1820 local horisontDirect = way.BACK -- Specific orientation, when we move to next Y pos
1821 local horisontShift = -1 -- Y direction, when we move to next Y pos
1822 local x,y = w,v
1823
1824 if boostMode then
1825 if yFirst then
1826 x,y = v, w
1827 if(_z%2==1) then y = sizeY-y-1; x = sizeX-x-1 end
1828 if(x%2==1) then y = sizeY-y-1 end
1829 else
1830 if(_z%2==1) then y = sizeY-y-1; x = sizeX-x-1 end
1831 if(y%2==1) then x = sizeX-x-1 end
1832 end
1833
1834 -- Fill down
1835 if z>0 then
1836 fillFnc(x,y,z, way.DOWN, nil, vol[z-1][y][x])
1837 end
1838 -- Fill UP
1839 if z<sizeZ-1 then
1840 fillFnc(x,y,z, way.UP, nil, vol[z+1][y][x])
1841 end
1842 else
1843 if not currZIsEven then
1844 y = sizeY - y - 1
1845 horisontDirect = way.FORWARD
1846 horisontShift = 1
1847 end
1848 x = sizeX - x - 1 -- Revert X coordinates. Filling will be from right to left
1849
1850 local escapeShaftHere = (x==0 and y==0)
1851 local hereWeWillGoUp = ((x==0 and ((_z%2==0 and y==sizeY-1) or (_z%2==1 and y==0))) and _z < zStepsCount)
1852
1853
1854 -- Fill down
1855 if z>0 and not (lastZStepIsCap and currZStepIsLast) and not escapeShaftHere then
1856 fillFnc(x,y,z, way.DOWN, nil, vol[z-1][y][x])
1857 end
1858
1859 -- Fill forward
1860 if x < sizeX-1 then
1861 fillFnc(x,y,z, way.FORWARD, way.RIGHT, vol[z][y][x+1])
1862 end
1863
1864 -- Fill back previous line when we starting new x line
1865 if not currZStepIsLast or (not currZIsEven and currZStepIsLast) then
1866 if x==0 and ((currZIsEven and y>0) or ((not currZIsEven) and y<sizeY-1)) and
1867 not (x==0 and y==1 and currZIsEven) then
1868 fillFnc(x,y,z, way.FORWARD, horisontDirect, vol[z][y+horisontShift][x])
1869 end
1870 end
1871
1872 -- Fill UP
1873 if z<sizeZ-1 and not hereWeWillGoUp and (not escapeShaftHere or currZStepIsLast) then
1874 fillFnc(x,y,z, way.UP, nil, vol[z+1][y][x])
1875 end
1876
1877 -- Move up and fill bocks down, if we advance to next Z level
1878 if hereWeWillGoUp then
1879 local nextZLevel = (_z+1)*3 + 1
1880 if lastZStepIsCap and (_z+1==zStepsCount) then nextZLevel = nextZLevel-1 end -- Cap of volume
1881 if not escapeShaftHere then
1882 for zAdvance=z+1, nextZLevel do
1883 fillFnc(x,y,zAdvance, way.DOWN, nil, vol[zAdvance-1][y][x])
1884 end
1885 end
1886 end
1887 end
1888
1889 end
1890 end
1891 end
1892
1893
1894 -- We use our shaft to go back to x=0, y=0
1895 if not (zStepsCount%2==1) then
1896 for y=sizeY-2, 0, -1 do
1897 fillFnc(0, y, zLastStepLevel, way.FORWARD, way.FORWARD, vol[zLastStepLevel][y+1][0])
1898 end
1899 end
1900
1901 -- And then go down to z 0
1902 for z=zLastStepLevel-1, 0, -1 do
1903 fillFnc(0, 0, z, way.UP, nil, vol[z+1][0][0])
1904 end
1905
1906 -- And last block
1907 fillFnc(0, -1, 0, way.FORWARD, way.FORWARD, vol[0][0][0])
1908 end
1909
1910 -- Drop thrash if need
1911 dropIfTidy()
1912
1913 -- Drop all mined items
1914 if fillFlags['greedy'] then
1915 -- Honestly, we not really reloading. Just clearing items in black list
1916 blacklistPattern = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
1917 reloadFnc()
1918 end
1919
1920 -- ==============================
1921 -- Finishing
1922 -- ==============================
1923
1924 -- Now we finished filling territory. Just go home
1925 if isGoBackAfterFill == true then
1926 -- Spread message then we want die
1927 if fillFlags['dock'] then Turtle.touch(touch["killMe"], -1) end
1928
1929 -- And go to start pos to not interrupt others
1930 goThruGangway("startPos")
1931 Turtle.setOrient(way.FORWARD)
1932 end
1933
1934 return true
1935end
1936
1937
1938--===========================================================
1939-- Dock building
1940--===========================================================
1941function jobs.createDock(maxSize)
1942 -- Select turtle from inventory, or suck from down
1943 local selectTurtleFnc = function()
1944 if not Turtle.select("Turtle", true) then
1945 if not turtle.suckUp() then return false end
1946 return Turtle.select("Turtle", true)
1947 end
1948 return true
1949 end
1950
1951 -- Show box that we placing turtles
1952 KUI.setWindow({{ id='makeDock', type='textPanel', text='Placing turtles\nPlease click on\neach placed turtle', x=8,y=scrH/2-3, w=scrW-16,h=6}})
1953
1954 -- Set redstone signal to avoid comebacking
1955 Turtle.setOrient(way.FORWARD)
1956 redstone.setOutput('front', true)
1957
1958 -- Place turtles in front till all will be placed
1959 -- Loop untill we have turtles sucked from chest
1960 local slavesCount = 0
1961 while slavesCount < maxSize-1 and selectTurtleFnc() do
1962 -- Place turtle in front
1963 KUI.setWindow({{ id='placeTrtl', type='textPanel', text='Placing one turtle...', x=8,y=scrH/2-3, w=scrW-16,h=6}})
1964 while not Turtle.place(nil, nil, true) do sleep(1) end
1965
1966 -- Turn on this slave
1967 while not pcall(peripheral.call,'front','turnOn') do sleep(0.5) end
1968
1969 -- Add this turtle in slave list
1970 -- Loop until turtle will be activated and send message
1971 KUI.setWindow({{ id='placeTrtl', type='textPanel', text='Adding turtle\nto list...', x=8,y=scrH/2-3, w=scrW-16,h=6}})
1972 swarm.addSlave()
1973
1974 slavesCount = slavesCount + 1
1975
1976 -- Transmit location correction to older turtles
1977 KUI.setWindow({{ id='placeTrtl', type='textPanel', text='Send shift task to all', x=8,y=scrH/2-3, w=scrW-16,h=6}})
1978 swarm.transmitTask(nil, {name='rightShift'}, true)
1979 end
1980
1981 -- Put turtles back
1982 -- TODO: Working with more chests sides
1983 while Turtle.selectNonEmptySlot() do turtle.dropUp() end
1984
1985 redstone.setOutput('front', false)
1986
1987 -- Move like we are the boss
1988 Turtle.setPos(0,-1,0)
1989 Turtle.goTo(0,0,0)
1990
1991 return slavesCount
1992end
1993
1994
1995--===========================================================
1996-- Dock disassembling
1997--===========================================================
1998function jobs.disassembleDock(slavesCount)
1999 KUI.setWindow({{ id='destroyDock', type='textPanel', text='Waiting all\nturtles go home...', x=8,y=scrH/2-3, w=scrW-16,h=6}})
2000
2001 if Turtle.pos ~= vec3() then Turtle.goTo(0,0,0) end
2002 Turtle.goTo(0,-1,0)
2003 Turtle.setOrient(way.FORWARD)
2004
2005 local slavesPicked = 0
2006 while slavesPicked < slavesCount do
2007 for i=1, slavesCount do
2008 swarm.transmitTask(i, {name='disassemble'})
2009 end
2010
2011 -- Wait before someone throw kill request
2012 local strenght = Turtle.sense(way.FORWARD, 1)
2013 --local receivedObj = swarm.receiveFromSlave(1)
2014
2015 --if receivedObj and receivedObj.name == "PleaseKillMe" then
2016 if strenght == touch["killMe"] then
2017 Turtle.dig()
2018 if Turtle.select("Turtle", true) then
2019 if not turtle.dropUp() then turtle.dropDown() end
2020
2021 slavesPicked = slavesPicked + 1
2022 end
2023 end
2024 end
2025end
2026
2027
2028--10_KTurtle.lua
2029
2030-- ********************************************************************************** --
2031-- ** Krutoy Turtle Wrapper ** --
2032-- ** ---------------------------------------------------- ** --
2033-- ** ** --
2034-- ** Wrap standart turtle functions but monitoring position and orientation ** --
2035-- ** To get turtle pos and orient use Turtle.pos and Turtle.orient ** --
2036-- ** ** --
2037-- ********************************************************************************** --
2038
2039-- Enumeration to store names for the 6 directions
2040local way = {
2041 FORWARD=0,
2042 RIGHT =1,
2043 BACK =2,
2044 LEFT =3,
2045 UP =4,
2046 DOWN =5
2047}
2048
2049
2050
2051local oppositeWay = {[0]=2,3,0,1,5,4}
2052
2053local attemptsToAlternative = 5
2054local attemptsToFailure = 7
2055local attemptsToIndicate = 15
2056
2057local dataFilePath = "Krutoy/location"
2058
2059-- Global Turtle variable
2060Turtle = {}
2061Turtle.__index = Turtle
2062math.randomseed(os.time())
2063
2064-- Variables to store the current location and orientation of the turtle. x is right, left, z is up, down and
2065-- y is forward, back with relation to the starting orientation.
2066Turtle.orient = way.FORWARD
2067
2068-- If turtle start moving in some direction, store it here
2069Turtle.moving = nil
2070
2071Turtle.pos = vec3() -- Start Pos is 0,0,0
2072Turtle.beacons = {} -- User-made points to route
2073Turtle.safe = true -- In this mode turtle will newer crush forbidden blocks
2074Turtle.oneWay = arr3d() -- An 3d array of points where we cant move some special ways
2075Turtle.safeZone = arr3d() -- An 3d array of "safe" places, where we cant go with pickaxe
2076Turtle.alterZone= arr3d() --
2077Turtle.world = arr3d() -- World of blocked positions for path finding
2078
2079Turtle.lowFuelLevel = 300 -- Call onLowFuel function if above this
2080
2081local surround = {
2082[way.FORWARD] = vec3( 0, 1, 0),
2083[way.RIGHT] = vec3( 1, 0, 0),
2084[way.BACK] = vec3( 0,-1, 0),
2085[way.LEFT] = vec3(-1, 0, 0),
2086[way.UP] = vec3( 0, 0, 1),
2087[way.DOWN] = vec3( 0, 0,-1)
2088}
2089
2090local cc_sides = {
2091[way.FORWARD] = "front",
2092[way.RIGHT] = "right",
2093[way.BACK] = "back",
2094[way.LEFT] = "left",
2095[way.UP] = "top",
2096[way.DOWN] = "bottom"
2097}
2098
2099local trtlOperationBySide = {
2100[way.FORWARD] = {turtle.forward, turtle.detect, turtle.dig, turtle.attack, turtle.compare, turtle.suck, turtle.place, turtle.inspect},
2101[way.UP] = {turtle.up, turtle.detectUp, turtle.digUp, turtle.attackUp, turtle.compareUp, turtle.suckUp, turtle.placeUp, turtle.inspectUp},
2102[way.DOWN] = {turtle.down, turtle.detectDown, turtle.digDown, turtle.attackDown, turtle.compareDown, turtle.suckDown, turtle.placeDown, turtle.inspectDown}
2103}
2104
2105local thrash = {"minecraft:stone","minecraft:cobblestone","minecraft:dirt","minecraft:gravel","minecraft:grass","minecraft:sand"}
2106
2107-- Call function only if it is function
2108local function safeCall(fnc, ...)
2109 if type(fnc)=='function' then fnc(...) end
2110end
2111
2112-- Help function
2113local function addZoneInArray(arr, val, ...)
2114 local args = {...}
2115 local x1,y1,z1,x2,y2,z2
2116 -- Overload to working with vectors
2117 if type(args[1]) == 'table' and type(args[2]) == 'table' then
2118 x1,y1,z1,x2,y2,z2 = args[1]:unpack(), args[2]:unpack()
2119 else
2120 x1,y1,z1,x2,y2,z2 = ...
2121 end
2122 assert(x1 and y1 and z1 and x2 and y2 and z2, 'Wrong Turtle.addZone params')
2123
2124
2125 local sortFnc = function(n1,n2) if n2<n1 then return n2,n1 else return n1,n2 end end
2126 x1,x2=sortFnc(x1,x2); y1,y2=sortFnc(y1,y2); z1,z2=sortFnc(z1,z2);
2127
2128 for _z=0, z2-z1 do
2129 for _y=0, y2-y1 do
2130 for _x=0, x2-x1 do
2131 arr:set(x1+_x,y1+_y,z1+_z,val)
2132 end
2133 end
2134 end
2135end
2136
2137
2138-- Saving location and stats to file
2139function Turtle.removeData()
2140 fs.delete(dataFilePath)
2141
2142 Turtle.pos = vec3()
2143 Turtle.orient = way.FORWARD
2144 Turtle.moving = way.FORWARD
2145 Turtle.beacons= {}
2146end
2147
2148
2149-- Saving location and stats to file
2150function Turtle.saveData(movingDir)
2151 if movingDir then Turtle.moving = movingDir end
2152
2153 local saveData = {
2154 x=Turtle.pos.x,
2155 y=Turtle.pos.y,
2156 z=Turtle.pos.z,
2157 orient=Turtle.orient,
2158 fuel=turtle.getFuelLevel(),
2159 moving=Turtle.moving,
2160 beacons=Turtle.beacons}
2161 local f = fs.open(dataFilePath, "w")
2162 f.write(table.toString(saveData))
2163 f.close()
2164end
2165
2166-- Taken from
2167-- http://www.computercraft.info/forums2/index.php?/topic/19939
2168-- By Signify
2169function Turtle.loadData()
2170 --Checking for previous config...
2171 if fs.exists(dataFilePath) then
2172 local f = fs.open(dataFilePath, "r")
2173 local data = table.fromString(f.readAll())
2174 f.close()
2175
2176 --Config found. Loading data
2177 --Checks to make sure turtle didn't move or do anything wonky after reboot
2178 local currentFuel = turtle.getFuelLevel()
2179 if data.fuel == currentFuel or data.fuel == currentFuel+1 then
2180 local oldState = {pos=Turtle.pos, orient=Turtle.orient}
2181 --Loading in the data since nothig bad happened
2182 Turtle.pos = vec3(data.x, data.y, data.z)
2183 Turtle.orient = data.orient
2184 Turtle.moving = data.moving
2185
2186 if data.fuel == currentFuel+1 then
2187 --Here, we've gotta fix a few things since the turtle did in fact, move
2188 Turtle.setPos(Turtle.getRelativeCoord(Turtle.moving))
2189 end
2190
2191 -- Indicate that we are resuming
2192 if oldState.pos == Turtle.pos and Turtle.orient == oldState.orient then
2193 return false
2194 else
2195 Turtle.beacons = data.beacons
2196 for k,v in pairs(Turtle.beacons) do
2197 Turtle.beacons[k] = vec3(v)
2198 end
2199
2200 return true
2201 end
2202 else
2203 --Looks like the fuel level changed by more than one. Wiping location knowledge
2204 Turtle.removeData()
2205 end
2206 end
2207 return false
2208end
2209
2210-- ********************************************************************************** --
2211-- Add sector when we cant move in determined way
2212-- ********************************************************************************** --
2213function Turtle.addForbiddenWayZone(_way, _alter, ...)
2214 addZoneInArray(Turtle.oneWay, {_way, _alter}, ...)
2215end
2216
2217-- ********************************************************************************** --
2218-- Add sector when we cant crushing blocks with pickaxe
2219-- ********************************************************************************** --
2220function Turtle.addSafeZone(...)
2221 addZoneInArray(Turtle.safeZone, true, ...)
2222end
2223
2224-- ********************************************************************************** --
2225--
2226-- ********************************************************************************** --
2227function Turtle.addAlterZone(_alter, ...)
2228 addZoneInArray(Turtle.alterZone, _alter, ...)
2229end
2230
2231
2232-- ********************************************************************************** --
2233-- Sets the turtle coordinates
2234-- ********************************************************************************** --
2235function Turtle.setPos(...)
2236 local args = {...}
2237 local x,y,z
2238
2239 -- Overload to working with vectors
2240 if type(args[1]) == 'table' then
2241 x,y,z = args[1]:unpack()
2242 else
2243 x,y,z = ...
2244 end
2245 assert(x and y and z, 'Wrong Turtle.setPos() params')
2246
2247 Turtle.pos = vec3(x,y,z)
2248 Turtle.saveData()
2249end
2250
2251-- ********************************************************************************** --
2252-- Sets the turtle to a specific orientation, irrespective of its current orientation
2253-- ********************************************************************************** --
2254function Turtle.setOrient(newOrient)
2255
2256 -- Already turned
2257 if (Turtle.orient == newOrient) then return true end
2258
2259
2260 -- Wrong parameters - we cant turn up or down
2261 if newOrient < 0 or newOrient > way.LEFT then
2262 error("Invalid newOrient in Turtle.setOrient function")
2263 return false
2264 end
2265
2266 local turn = Turtle.orient - newOrient
2267 local turnFnc
2268
2269 if turn==1 or turn==-3 then
2270 turnFnc = Turtle.turnLeft
2271 else
2272 turnFnc = Turtle.turnRight
2273 end
2274
2275 turn = math.abs(turn)
2276 if(turn==3) then turn=1 end
2277
2278 for i=1, turn do turnFnc() end
2279
2280 return true
2281end
2282
2283-- ********************************************************************************** --
2284--
2285-- ********************************************************************************** --
2286function Turtle.turnRight()
2287 turtle.turnRight()
2288 Turtle.orient = (Turtle.orient + 1)%4
2289 Turtle.saveData()
2290 return true
2291end
2292function Turtle.turnLeft()
2293 turtle.turnLeft()
2294 Turtle.orient = (Turtle.orient + 3)%4
2295 Turtle.saveData()
2296 return true
2297end
2298
2299--===========================================================
2300-- Test name for thrashed ore
2301--===========================================================
2302function Turtle.isThrash(name)
2303 if type(name) == "string" then
2304 for i=1,#thrash do
2305 if thrash[i] == name then return true end
2306 end
2307 end
2308 return false
2309end
2310
2311
2312--===========================================================
2313-- Determine when turtle have very low fuel
2314--===========================================================
2315function Turtle.isLowFuel()
2316 return turtle.getFuelLevel() <= Turtle.lowFuelLevel
2317end
2318
2319--===========================================================
2320-- Dig, depending on direction
2321--===========================================================
2322local digFncArr = {[way.FORWARD]=turtle.dig, [way.DOWN]=turtle.digDown, [way.UP]=turtle.digUp}
2323function Turtle.dig(direction)
2324 direction = direction or way.FORWARD -- Optional param
2325 assert(direction == way.DOWN or direction == way.UP or direction == way.FORWARD, 'Wrong params in Turtle.dig()')
2326
2327 return digFncArr[direction]()
2328end
2329
2330--===========================================================
2331-- Dig only ore
2332--===========================================================
2333function Turtle.mine(direction)
2334 direction = direction or way.FORWARD -- Optional param
2335 assert(direction == way.DOWN or direction == way.UP or direction == way.FORWARD, 'Wrong params in Turtle.mine()')
2336
2337 local inspectFc = trtlOperationBySide[direction][8]
2338 local _,blockData = inspectFc()
2339 local blockName = blockData and blockData.name
2340 if Turtle.isThrash(blockName) then
2341 return false
2342 else
2343 return digFncArr[direction]()
2344 end
2345end
2346
2347-- ********************************************************************************** --
2348-- Generic function to move the Turtle (pushing through any gravel or other
2349-- things such as mobs that might get in the way).
2350--
2351-- The only thing that should stop the turtle moving is bedrock. Where this is
2352-- found, the function will return after 15 seconds returning false
2353-- ********************************************************************************** --
2354function Turtle.move(direction, doNotTurnBack)
2355 local straightDirection = (direction == way.FORWARD or direction == way.UP or direction == way.DOWN)
2356
2357 local isReceding = (direction == way.BACK)
2358 local tmpDir = straightDirection and direction or way.FORWARD
2359 local moveFn, detectFn, digFn, attackFn,_,_,_, inspectFn = unpack(trtlOperationBySide[tmpDir])
2360
2361
2362 local destination = Turtle.getRelativeCoord(direction)
2363 local newX, newY, newZ = destination:unpack()
2364
2365 -- Low fuel event
2366 if Turtle.isLowFuel() then
2367 safeCall(Turtle.onLowFuel, newX, newY, newZ, direction)
2368 end
2369
2370 -- Flag to determine whether digging has been tried yet. If it has
2371 -- then pause briefly before digging again to allow sand or gravel to drop
2372 local attempt = 0
2373
2374 -- Raise event if we try to move
2375 safeCall(Turtle.onMove, attempt, newX, newY, newZ, direction)
2376 safeCall(Turtle.onMoveAttempt, attempt, newX, newY, newZ, direction)
2377
2378 -- Check if this move is forbidden. In this case change move to other way
2379 local forbid = Turtle.oneWay(Turtle.pos:unpack())
2380 if forbid ~= nil and forbid[1] == Turtle.getIrrespectiveOrient(direction) then
2381 return Turtle.move(Turtle.getRelativeOrient(forbid[2]), doNotTurnBack)
2382 end
2383
2384 -- Speciall kind of sides
2385 local oldOrient= Turtle.orient
2386 if not straightDirection then
2387 if direction == way.RIGHT then Turtle.turnRight() end
2388 if direction == way.LEFT then Turtle.turnLeft() end
2389 direction = way.FORWARD
2390 end
2391
2392
2393 local moveWithSave = function(mFn)
2394 Turtle.saveData(direction)
2395 return mFn()
2396 end
2397
2398
2399 local moveSuccess = false
2400 if isReceding then
2401 moveSuccess = moveWithSave(turtle.back)
2402 else
2403 moveSuccess = moveWithSave(moveFn)
2404 end
2405
2406
2407 -- Check if we want to move into safe zone. In this case dig will be turned off
2408 local safe = Turtle.safe or Turtle.safeZone(destination:unpack())
2409
2410 -- Loop until we've successfully moved
2411 while not moveSuccess do
2412
2413 -- We had too many attempts. Now we can only make error
2414 if attempt >= attemptsToFailure then
2415 safeCall(Turtle.onMoveFailure, newX, newY, newZ, direction)
2416 end
2417
2418 -- Check if this move is forbidden. In this case change move to other way
2419 if attempt >= attemptsToAlternative then
2420 local alternative = Turtle.alterZone(Turtle.pos:unpack())
2421 local irrOrient = Turtle.getIrrespectiveOrient(direction)
2422 if alternative ~= nil and
2423 alternative ~= irrOrient and
2424 alternative ~= oppositeWay[irrOrient] then
2425 return Turtle.move(Turtle.getRelativeOrient(alternative), doNotTurnBack)
2426 end
2427 end
2428
2429 -- Show that we are stuked a bit. Turn 360*
2430 if attempt>1 and attempt % attemptsToIndicate == 0 then
2431 for i=1,4 do Turtle.turnRight() end
2432 end
2433
2434 -- Turn face to distraction to dig it if we are go backwards
2435 if isReceding then
2436 Turtle.turnRight(); Turtle.turnRight(); direction = way.FORWARD
2437 isReceding = false
2438 end
2439
2440 -- If there is a block in front, dig it
2441 if detectFn() == true then
2442 if safe then
2443 -- If this is safe zone, we can dig anything except turtles
2444 local _, data = inspectFn()
2445 if data and data.name and not data.name:find("Turtle") then
2446 digFn()
2447 end
2448 else
2449 digFn()
2450 end
2451 else
2452 -- Am being stopped from moving by a mob, attack it
2453 attackFn()
2454 end
2455
2456 -- Raise event if we try to move
2457 safeCall(Turtle.onMoveAttempt, attempt, newX, newY, newZ, direction)
2458
2459 -- Try the move again
2460 moveSuccess = moveWithSave(moveFn)
2461
2462 attempt = attempt + 1
2463
2464 -- Random sleep between attempts
2465 if not moveSuccess then sleep(math.random()/10+0.1) end
2466 end
2467
2468 Turtle.setPos(destination)
2469
2470 -- Turn turtle back on origin orient
2471 if not straightDirection and doNotTurnBack ~= true then
2472 Turtle.setOrient(oldOrient)
2473 end
2474
2475 -- Return the move success
2476 return moveSuccess
2477end
2478
2479function Turtle.forward() return Turtle.move(way.FORWARD) end
2480function Turtle.up() return Turtle.move(way.UP) end
2481function Turtle.down() return Turtle.move(way.DOWN) end
2482function Turtle.back(doNotTurnBack) return Turtle.move(way.BACK, doNotTurnBack) end
2483function Turtle.left(doNotTurnBack) return Turtle.move(way.LEFT, doNotTurnBack) end
2484function Turtle.right(doNotTurnBack) return Turtle.move(way.RIGHT, doNotTurnBack) end
2485
2486-- ********************************************************************************** --
2487-- Get relativety position from direction of turtle
2488-- ********************************************************************************** --
2489function Turtle.getRelativeCoord(direct)
2490 -- Return direction with displace
2491 return Turtle.pos + surround[Turtle.getIrrespectiveOrient(direct)]
2492end
2493
2494-- ********************************************************************************** --
2495-- Get relativety position from direction of turtle
2496-- ********************************************************************************** --
2497function Turtle.getIrrespectiveOrient(direct)
2498 if direct == way.UP or direct == way.DOWN then
2499 return direct
2500 else
2501 return (Turtle.orient + direct)%4
2502 end
2503end
2504
2505function Turtle.getRelativeOrient(direct)
2506 if direct == way.UP or direct == way.DOWN then
2507 return direct
2508 else
2509 return math.abs(Turtle.orient - direct)%4
2510 end
2511end
2512
2513
2514
2515--===========================================================
2516-- Find path to coordinates, avoid block listed cells
2517-- Using A* algorithm http://pastebin.com/CHCB8nDz
2518--===========================================================
2519function Turtle.pathTo(...)
2520 local args = {...}
2521 local x,y,z
2522
2523 -- Overload to working with vectors
2524 if type(args[1]) == 'string' then
2525 if Turtle.beacons[args[1]] then
2526 x,y,z = Turtle.beacons[args[1]]:unpack()
2527 else
2528 return false
2529 end
2530 elseif type(args[1]) == 'table' then
2531 x,y,z = args[1]:unpack()
2532 else
2533 x,y,z = ...
2534 end
2535 assert(x and y and z, 'Wrong Turtle.pathTo() params')
2536
2537 -- Already here!
2538 if Turtle.pos.x==x and Turtle.pos.y==y and Turtle.pos.z==z then return true end
2539
2540 -- Get first crumb of path
2541 local crumb = AStarFindPath(Turtle.world, Turtle.pos, vec3(x,y,z))
2542
2543 if crumb then
2544 -- Run over all crumbs
2545 while crumb.next ~= nil do
2546 crumb = crumb.next
2547 Turtle.goTo(crumb.pos.x,crumb.pos.y,crumb.pos.z)
2548 end
2549 else
2550 -- Path cant be found. Move straight
2551 error("Path cant be found! From:".. tostring(vec3(Turtle.pos.x,Turtle.pos.y,Turtle.pos.z)) .." To:" ..tostring(vec3(x,y,z)))
2552 end
2553
2554end
2555
2556--===========================================================
2557-- Move turtle on needed position
2558-- Simple move by x, then y, then z
2559-- args can be vector or three parameters x,y,z
2560--===========================================================
2561function Turtle.goTo(...)
2562 local args = {...}
2563 local x,y,z
2564
2565 -- Overload to working with vectors
2566 if type(args[1]) == 'string' then
2567 if Turtle.beacons[args[1]] then
2568 x,y,z = Turtle.beacons[args[1]]:unpack()
2569 else
2570 return false
2571 end
2572 elseif type(args[1]) == 'table' then
2573 x,y,z = args[1]:unpack()
2574 else
2575 x,y,z = ...
2576 end
2577 assert(x and y and z, 'Wrong Turtle.goTo() params')
2578
2579 local targetPos = vec3(x,y,z)
2580 local targetVec = targetPos - Turtle.pos
2581
2582 -- We need additional loop for cases of one way roads
2583 while not (targetPos == Turtle.pos) do
2584
2585 -- X
2586 while Turtle.pos.x ~= x do
2587 if (targetVec.x<0 and Turtle.orient==way.RIGHT) or
2588 (targetVec.x>0 and Turtle.orient==way.LEFT) then
2589 Turtle.back(true)
2590 elseif (x<Turtle.pos.x) then
2591 Turtle.setOrient(way.LEFT)
2592 Turtle.forward()
2593 elseif (x>Turtle.pos.x) then
2594 Turtle.setOrient(way.RIGHT)
2595 Turtle.forward()
2596 end
2597 end
2598
2599
2600
2601 -- Y
2602 while Turtle.pos.y ~= y do
2603 if(targetVec.y<0 and Turtle.orient==way.FORWARD) or
2604 (targetVec.y>0 and Turtle.orient==way.BACK) then
2605 Turtle.back(true)
2606 elseif (y<Turtle.pos.y) then
2607 Turtle.setOrient(way.BACK)
2608 Turtle.forward()
2609 elseif (y>Turtle.pos.y) then
2610 Turtle.setOrient(way.FORWARD)
2611 Turtle.forward()
2612 end
2613 end
2614
2615
2616 -- Z
2617 while (z<Turtle.pos.z) do
2618 Turtle.down()
2619 end
2620 while (z>Turtle.pos.z) do
2621 Turtle.up()
2622 end
2623 end
2624
2625 return true
2626end
2627
2628-- ********************************************************************************** --
2629-- Add point for easy acces
2630-- ********************************************************************************** --
2631function Turtle.setBeacon(beaconName, _x,_y,_z)
2632 Turtle.beacons[beaconName] = vec3(_x,_y,_z)
2633 Turtle.saveData()
2634end
2635
2636
2637-- ********************************************************************************** --
2638-- Select non-empty slot
2639-- ********************************************************************************** --
2640function Turtle.selectNonEmptySlot()
2641 for i=1, 16 do
2642 if( turtle.getItemCount(i) > 0) then
2643 turtle.select(i)
2644 return true
2645 end
2646 end
2647 return false
2648end
2649
2650
2651-- ********************************************************************************** --
2652-- Select empty slot
2653-- ********************************************************************************** --
2654function Turtle.selectEmptySlot()
2655 for i=1, 16 do
2656 if( turtle.getItemCount(i) == 0) then
2657 turtle.select(i)
2658 return true
2659 end
2660 end
2661 return false
2662end
2663
2664-- ********************************************************************************** --
2665-- Select slot with data name as pattern
2666-- ********************************************************************************** --
2667function Turtle.select(str, isPattern)
2668 for i=1, 16 do
2669 if turtle.getItemCount(i) > 0 then
2670 local data = turtle.getItemDetail(i)
2671 if data and data.name then
2672 if isPattern then
2673 if data.name:find(str) then turtle.select(i); return i end
2674 else
2675 if data.name == str then turtle.select(i); return i end
2676 end
2677 end
2678 end
2679 end
2680 return nil
2681end
2682
2683
2684-- ********************************************************************************** --
2685-- Inspect item using direction
2686-- ********************************************************************************** --
2687function Turtle.inspect(direction)
2688 assert(direction==way.FORWARD or direction==way.UP or direction==way.DOWN, 'Wrong Turtle.inspect() params')
2689 return trtlOperationBySide[direction][8]()
2690end
2691
2692-- ********************************************************************************** --
2693-- Place item. Check if item already placed.
2694-- ********************************************************************************** --
2695function Turtle.place(itemSlot, direction, noChecks)
2696 direction = direction or way.FORWARD
2697 assert(direction==way.FORWARD or direction==way.UP or direction==way.DOWN, 'Wrong Turtle.place() params')
2698
2699 local _, detectFnc, _, attackFnc, compareFnc, _, placeFnc = unpack(trtlOperationBySide[direction])
2700 noChecks = noChecks or false
2701
2702 -- slotsPattern is array of 16 nubbers that represent
2703 -- what kind of blocks lying in what kind of
2704 if itemSlot then
2705 turtle.select(itemSlot)
2706 end
2707
2708 local placeSucces = false
2709 local digCount = 0
2710 local maxDigCount = 20
2711 local attempt = 0
2712
2713
2714 -- Check if there is already item then try to place
2715 placeSucces = placeFnc()
2716
2717 if noChecks then return placeSucces end
2718
2719 if (not placeSucces) and detectFnc() then
2720 if(compareFnc()) then
2721 -- Item that we must set already here
2722 return true
2723 else
2724 -- There is something else. Dig/Attack and place item
2725 Turtle.dig(direction)
2726 digCount = digCount + 1
2727 end
2728 end
2729
2730 -- Now try to place item until item will placed
2731 while ((placeSucces == false) and (digCount < maxDigCount)) and attempt < attemptsToFailure do
2732 if (detectFnc()) then
2733 if(digCount > 0) then
2734 sleep(0.4)
2735 end
2736 Turtle.dig(direction)
2737 digCount = digCount + 1
2738 else
2739 -- Am being stopped from moving by a mob, attack it
2740 attackFnc()
2741 end
2742 -- Try the place again
2743 placeSucces = placeFnc()
2744
2745 attempt = attempt + 1
2746 end
2747
2748 return placeSucces
2749end
2750
2751
2752--===========================================================
2753-- Touching is way to communicate with other turtles
2754-- using redstone signals 0-15
2755--===========================================================
2756local sidesList = {"top", "bottom", "left", "right", "front", "back"}
2757function Turtle.touch(strenght, direction, sleepTime)
2758 direction = direction or way.FORWARD -- Optional param
2759 sleepTime = sleepTime or 0.1
2760
2761
2762 if direction == -1 then
2763 local oldOut = {}
2764 for i=1, #sidesList do
2765 insert(oldOut, rs.getAnalogOutput(sidesList[i]))
2766 rs.setAnalogOutput(sidesList[i], strenght)
2767 end
2768 sleep(sleepTime)
2769 for i=1, #sidesList do
2770 rs.setAnalogOutput(sidesList[i], oldOut[i])
2771 end
2772 else
2773 local oldOutput = rs.getAnalogOutput(cc_sides[direction])
2774 rs.setAnalogOutput(cc_sides[direction], strenght)
2775 sleep(sleepTime)
2776 rs.setAnalogOutput(cc_sides[direction], oldOutput)
2777 end
2778end
2779
2780--===========================================================
2781-- Feel the touches
2782--===========================================================
2783function Turtle.sense(direction, timeout)
2784 direction = direction or way.FORWARD -- Optional param
2785
2786 -- Start the timer
2787 local timer, sFilter
2788 if nTimeout then
2789 timer = os.startTimer( timeout )
2790 else
2791 sFilter = "redstone"
2792 end
2793
2794 while true do
2795 local sEvent, p1, p2, p3, p4, p5 = os.pullEvent( sFilter )
2796
2797 if sEvent == "redstone" then
2798 if direction == -1 then
2799 for i=1, #sidesList do
2800 local strenght = rs.getAnalogInput(sidesList[i])
2801 if strenght > 0 then return strenght, sidesList[i] end
2802 end
2803 else
2804 local strenght = rs.getAnalogInput(cc_sides[direction])
2805 if strenght > 0 then return strenght end
2806 end
2807 elseif sEvent == "timer" then
2808 -- Return nil if we timeout
2809 if p1 == timer then
2810 return nil
2811 end
2812 end
2813 end
2814end
2815
2816--20_KUI.lua
2817
2818-- ********************************************************************************** --
2819-- ** UI class for program "KrutoyTurtle" ** --
2820-- ** ** --
2821-- ********************************************************************************** --
2822
2823--[[
2824â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘
2825â–‘_____________Final info______________â–‘
2826â–‘-Slaves: 64 â–‘
2827â–‘-Total Volume:30000 â–‘
2828â–‘-Est. time: 90 min â–‘
2829â–‘-More info: bla bla â–‘
2830â–‘___________Blocks by type____________â–‘
2831â–‘#1 : 99s+50 #2 : 99s+50 #3 : 99s+50â–‘
2832â–‘#4 : 99s+50 #5 : 99s+50 #6 : 99s+50â–‘
2833â–‘#7 : 99s+50 #8 : 99s+50 #9 : 99s+50â–‘
2834â–‘#10: 99s+50 #11: 99s+50 #12: 99s+50â–‘
2835â–‘#13: 99s+50 #14: 99s+50 #15: 99s+50â–‘
2836â–‘#16: 99s+50 #16: 99s+50 #16: 99s+50â–‘
2837â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘
2838
2839â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘
2840â–‘ â–‘
2841â–‘ KRUTOY TURTLE â–‘
2842â–‘_____________________________________â–‘
2843â–‘ Fill options: â–‘
2844â–‘ â–‘
2845â–‘ Pattern: Plain â–‘
2846â–‘ Size: 5 5 6 â–‘
2847â–‘ Flags: mine, tidy, dock â–‘
2848â–‘ â–‘
2849â–‘Help: â–‘
2850â–‘ Navigate - TAB, arrows â–‘
2851â–‘ Refuel - R, Console - ~ [Next>>]â–‘
2852â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘
2853
2854â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘
2855â–‘__________Select pattern:____________â–‘
2856â–‘ 1 - Plain â–‘
2857â–‘ 2 - BoxGrid â–‘
2858â–‘ 3 - MyFirstTample.vox â–‘
2859â–‘ â–‘
2860â–‘ â–‘
2861â–‘ â–‘
2862â–‘ â–‘
2863â–‘ â–‘
2864â–‘ â–‘
2865â–‘ â–‘
2866â–‘ â–‘
2867â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘
2868
2869
2870]]
2871
2872KUI = {}
2873KUI.__index = KUI
2874
2875local sub = string.sub
2876
2877local borderStylesStr = {
2878 ['selectedBtn']= '/\\/\\ ||',
2879 ['standart'] = '++++--||',
2880 ['none'] = ' ',
2881 ['inlineBtn'] = '[]][ '
2882}
2883-- Transform string to array
2884local borderStyles = {}
2885for k,v in pairs(borderStylesStr) do
2886 borderStyles[k] = {}
2887 local styleStr = v
2888 for i = 1, #styleStr do
2889 borderStyles[k][i] = (styleStr:sub(i,i))
2890 end
2891end
2892
2893
2894local alignTypes = {'left', 'right', 'center'}
2895
2896KUI.items = {} -- Array of UI elements
2897KUI.selectedObj = nil -- Currently selected item
2898KUI.currentWindow = nil -- Current drawing window
2899
2900
2901
2902-- Repeat char many as need times to make string
2903local function repeatChar(char, count)
2904 local returnString = ''
2905 for _=0, count-1 do
2906 returnString = returnString..char
2907 end
2908 return returnString
2909end
2910
2911
2912function KUI.drawText(text, x,y, w,h, align)
2913 align = align or 'center'
2914
2915 -- String params
2916 local textLinesCount = 1 + select(2, text:gsub('\n', '\n'))
2917
2918 -- Top spaces
2919 local vertSpace = (h-textLinesCount)/2
2920 local clearLine = repeatChar(' ', w)
2921 for _=1, math.floor(vertSpace) do
2922 term.setCursorPos(x, y)
2923 term.write(clearLine)
2924 end
2925
2926 -- Bottom spaces
2927 for _=1, math.ceil (vertSpace) do
2928 term.setCursorPos(x, y+h-1)
2929 term.write(clearLine)
2930 end
2931
2932 -- Write lines
2933 local currLine = 0
2934 for l in text:gmatch('[^\r\n]+') do
2935 local margin = {0,0} -- Spaces from left and right
2936 local horisSpace = (w - #l)
2937
2938 -- Align styles
2939 if (align == 'left') then
2940 margin = {0, horisSpace}
2941 elseif(align == 'right')then
2942 margin = {horisSpace, 0}
2943 elseif(align == 'center')then
2944 margin = {math.floor(horisSpace/2), math.ceil(horisSpace/2)}
2945 end
2946
2947 term.setCursorPos(x, y+currLine)
2948 term.write( repeatChar(' ',margin[1])..l..repeatChar(' ',margin[2]))
2949 currLine = currLine+1
2950 end
2951end
2952
2953-- Draw simple panel with borders
2954function KUI.drawPanel(x,y, w,h, borderStyle)
2955 borderStyle = borderStyle or 'standart'
2956 local styleArr = borderStyles[borderStyle]
2957
2958 -- Horisontal lines
2959 if(h > 1) then
2960 term.setCursorPos(x, y)
2961 term.write(styleArr[1]..repeatChar(styleArr[5], w-2)..styleArr[2])
2962 term.setCursorPos(x, y+h-1)
2963 term.write(styleArr[4]..repeatChar(styleArr[6], w-2)..styleArr[3])
2964 elseif h==1 then
2965 term.setCursorPos(x, y); term.write(styleArr[1])
2966 term.setCursorPos(x+w-1, y); term.write(styleArr[2])
2967 end
2968
2969 -- Vertical lines
2970 for i=0,h-3 do
2971 term.setCursorPos(x, y+i+1)
2972 term.write(styleArr[7])
2973 term.setCursorPos(x+w-1, y+i+1)
2974 term.write(styleArr[8])
2975 end
2976
2977end
2978
2979-- Draw panel with text
2980function KUI.drawTextPanel(text, x,y, w,h, borderStyle, align)
2981 KUI.drawPanel(x,y, w,h, borderStyle)
2982
2983 if borderStyle == 'none' then
2984 KUI.drawText(text, x,y+((h>1) and 1 or 0), w,h, align)
2985 else
2986 KUI.drawText(text, x+1,y+((h>1) and 1 or 0), w-2,h, align)
2987 end
2988end
2989
2990-- ********************************************************************************** --
2991-- Add gui element to current window
2992-- ********************************************************************************** --
2993function KUI.add(obj)
2994 -- Standart values
2995 obj.borderStyle = obj.borderStyle or 'standart'
2996 obj.align = obj.align or 'center'
2997 obj.padding = obj.padding or {0,0,0,0}
2998
2999 -- Add additional info
3000 obj.center = {x=obj.x+obj.w/2, y=obj.y+obj.h/2}
3001
3002 obj.nextTab = KUI.items[1] or obj
3003 if(#KUI.items >= 1) then
3004 KUI.items[#KUI.items].nextTab = obj
3005 end
3006
3007 if obj.type == 'button' or obj.type == 'input' then
3008 obj.selectable = true
3009 end
3010
3011 table.insert(KUI.items, obj)
3012end
3013
3014-- ********************************************************************************** --
3015-- Set new window. Add all objects in list to screen
3016-- ********************************************************************************** --
3017function KUI.setWindow(window, selectedId)
3018 KUI.items = {}
3019 KUI.selectedObj = nil
3020 KUI.currentWindow = window
3021 for _,obj in pairs(window) do
3022 KUI.add(obj)
3023
3024 if obj.id == selectedId then KUI.selectedObj = obj end
3025 end
3026 KUI.selectedObj = KUI.selectedObj or KUI.items[1]
3027 KUI.draw()
3028end
3029
3030-- ********************************************************************************** --
3031-- Clears screen and write message in center
3032-- ********************************************************************************** --
3033function KUI.msgBox(str)
3034
3035end
3036
3037-- ********************************************************************************** --
3038-- Draw all objects in list
3039-- ********************************************************************************** --
3040function KUI.draw()
3041 term.clear()
3042 for _,obj in pairs(KUI.items) do
3043
3044 -- Switch type
3045 if obj.type == 'panel' then
3046 KUI.drawPanel(obj.x,obj.y, obj.w,obj.h, obj.borderStyle)
3047 elseif obj.type == 'text' then
3048 KUI.drawText(obj.text,
3049 obj.x+obj.padding[4],
3050 obj.y+obj.padding[1],
3051 obj.w-obj.padding[2]-obj.padding[4],
3052 obj.h-obj.padding[1]-obj.padding[3],
3053 obj.align)
3054 elseif obj.type == 'textPanel' or obj.type == 'button' or obj.type == 'input' then
3055 KUI.drawTextPanel(obj.text,
3056 obj.x+obj.padding[4],
3057 obj.y+obj.padding[1],
3058 obj.w-obj.padding[2]-obj.padding[4],
3059 obj.h-obj.padding[1]-obj.padding[3],
3060 obj.borderStyle, obj.align)
3061 else
3062
3063 end
3064
3065 -- This object is selected and selectable
3066 if KUI.selectedObj == obj and obj.selectable == true then
3067 KUI.drawPanel(obj.x-1,
3068 obj.y,
3069 obj.w+2+obj.padding[2]+obj.padding[4],
3070 obj.h +obj.padding[1]+obj.padding[3], 'inlineBtn')
3071 end
3072
3073 end
3074
3075 -- Set Cursor
3076 local selObj = KUI.selectedObj
3077 if selObj.type == 'input' then
3078 term.setCursorPos(selObj.x+selObj.padding[4]+#selObj.text, selObj.y+selObj.padding[1])
3079 term.setCursorBlink(true)
3080 else
3081 term.setCursorBlink(false)
3082 end
3083
3084 sleep(0)
3085end
3086
3087function KUI.navigate()
3088 while true do
3089 local e, keyCode = os.pullEvent('key')
3090
3091 -- Call user-defined event
3092 if KUI.onKeyPressed then KUI.onKeyPressed(keyCode) end
3093
3094 -- Input line
3095 local selObj = KUI.selectedObj
3096 if selObj ~= nil and selObj.type == 'input' then
3097 local keyMap = {"",1,2,3,4,5,6,7,8,9,0,"-",[57]=" "}
3098 if keyMap[keyCode] ~= nil then
3099 selObj.text = selObj.text .. keyMap[keyCode]
3100 KUI.draw()
3101 elseif keyCode == 14 then -- BACKSPACE
3102 selObj.text = selObj.text:sub(1,#selObj.text-1)
3103 KUI.draw()
3104 end
3105 end
3106
3107 if keyCode == 200 then --UP
3108 KUI.prevTab()
3109 elseif keyCode == 203 then --LEFT
3110
3111 elseif keyCode == 205 then --RIGHT
3112
3113 elseif keyCode == 15 or keyCode == 208 then --TAB --DOWN
3114 KUI.nextTab()
3115 elseif keyCode == 28 then --ENTER
3116 if selObj ~= nil then
3117 return selObj.id, selObj
3118 end
3119 end
3120 end
3121end
3122
3123function KUI.nextTab()
3124 local oldSelectedObj = KUI.selectedObj
3125 KUI.selectedObj = KUI.selectedObj.nextTab
3126 while KUI.selectedObj.selectable ~= true and oldSelectedObj ~= KUI.selectedObj do
3127 KUI.selectedObj = KUI.selectedObj.nextTab
3128 end
3129 KUI.draw()
3130end
3131
3132function KUI.prevTab()
3133 local oldSelectedObj = KUI.selectedObj
3134 local nextTab = KUI.selectedObj.nextTab
3135 local lastSelectable = (nextTab.selectable == true) and nextTab or oldSelectedObj
3136 while oldSelectedObj ~= nextTab.nextTab do
3137 nextTab = nextTab.nextTab
3138 if nextTab.selectable == true then lastSelectable = nextTab end
3139 end
3140 KUI.selectedObj = lastSelectable
3141 KUI.draw()
3142end
3143
3144--30_Swarm.lua
3145
3146-- ********************************************************************************** --
3147-- ** ZeroSwarm ** --
3148-- ** with help from ZeroGalaxy ** --
3149-- ** ---------------------------------------------------- ** --
3150-- ** ** --
3151-- ** Swapping messages with turtles to make band ** --
3152-- ** for KrutoyTurtle ** --
3153-- ** ** --
3154-- ********************************************************************************** --
3155
3156-- Looks like a class
3157swarm = {}
3158swarm.__index = swarm
3159
3160-- Locals
3161local CHANNEL_BROADCAST = 65535
3162local CHANNEL_REPEAT = 65533
3163local modemOpened = false
3164local MASTERMESSAGE = 'I AM YOUR MASTER. OBEY!'
3165local GETOUTMESSAGE = 'GET OUT!'
3166local masterId
3167local slaves = {}
3168local modem
3169local uniqueID = math.floor(os.time()*1000) % 65530 -- SIC!
3170if not os.getComputerLabel() then os.setComputerLabel("KT"..uniqueID) end
3171local hashes = {} -- Set of all hashes that had been received
3172
3173-- Required to open channels before working with modem
3174local function openModem()
3175 if modemOpened then return true end
3176
3177 for n,sSide in ipairs( rs.getSides() ) do
3178 if rednet.isOpen( sSide ) then
3179 modemOpened = true
3180 return true
3181 end
3182 end
3183 for n,sSide in ipairs( rs.getSides() ) do
3184 if peripheral.getType( sSide )=='modem' then
3185 peripheral.call( sSide, "open", uniqueID )
3186 peripheral.call( sSide, "open", CHANNEL_BROADCAST )
3187 modem = peripheral.wrap(sSide)
3188 modemOpened = true
3189 return true
3190 end
3191 end
3192 return false
3193end
3194openModem()
3195
3196-- Searching value in table by keys
3197local function tblContainsVal(table, key, val)
3198 for k, v in pairs(table) do
3199 if v[key] == val then
3200 return k
3201 end
3202 end
3203 return nil
3204end
3205
3206-- Receive message with timeout
3207local function receive(nTimeout)
3208 -- Start the timer
3209 local timer, sFilter
3210 if nTimeout then
3211 timer = os.startTimer( nTimeout )
3212 else
3213 sFilter = "modem_message"
3214 end
3215
3216 -- Wait for events
3217 while true do
3218 local sEvent, p1, p2, p3, p4, p5 = os.pullEvent( sFilter )
3219 if sEvent == "modem_message" then
3220 -- senderId, msg, channel
3221 return p3, p4, p5
3222 elseif sEvent == "timer" then
3223 -- Return nil if we timeout
3224 if p1 == timer then
3225 return nil
3226 end
3227 end
3228 end
3229end
3230
3231
3232------------------------------
3233-- Functions called from turtles
3234------------------------------
3235
3236-- Looking for broadcast from master. If so, sending answer with distance
3237function swarm.searchOwner()
3238
3239 local senderId, msg, distance
3240 while msg~=MASTERMESSAGE do
3241 _,_,_,senderId, msg,distance = os.pullEvent("modem_message")
3242 if msg==MASTERMESSAGE then
3243 -- We gained first message from master.
3244 -- Send back a distance
3245 masterId = senderId
3246 modem.transmit(senderId, uniqueID, distance)
3247 end
3248 end
3249
3250 return distance
3251end
3252
3253
3254-- Waiting for order message till find
3255function swarm.waitOrders()
3256
3257 local senderId, message, channel
3258 while true do
3259 _,_,channel,senderId, message = os.pullEvent("modem_message")
3260
3261 if senderId == masterId or channel == CHANNEL_BROADCAST then
3262 local obj = table.fromString(message)
3263
3264 if not (obj and obj.messageHash and hashes[obj.messageHash]) then
3265
3266 -- Send confirm
3267 swarm.transmitToMaster(message)
3268
3269 -- Add this hash to hash table
3270 if obj and obj.messageHash then hashes[obj.messageHash] = true end
3271
3272 return obj
3273 end
3274 end
3275 end
3276end
3277
3278
3279-- Call broadcast and waiting for responces, collecting them to array
3280function swarm.findSlaves(waitingResponceTime)
3281 if not modemOpened then return 0 end
3282
3283 -- Broadcast
3284 modem.transmit(CHANNEL_BROADCAST, uniqueID, MASTERMESSAGE)
3285
3286 local received={}
3287 local senderId, message = receive(waitingResponceTime); if IDE then senderId, message = "slave1", 1 end
3288 while senderId do
3289 if(type(tonumber(message)) == 'number') then
3290 table.insert(received, {id=senderId, dist=tonumber(message)})
3291 end
3292 senderId, message = receive(waitingResponceTime)
3293 end
3294 table.sort(received,function(s1,s2) return s1.dist < s2.dist end)
3295
3296 slaves = {}
3297 local k=1
3298 for _,v in pairs(received) do
3299 if(v.dist == k) then
3300 k = k+1
3301 table.insert(slaves, v)
3302 else
3303 break
3304 end
3305 end
3306
3307 return k-1
3308end
3309
3310-- Call broadcast and waiting for responces, and add only nearest
3311function swarm.addSlave()
3312 if not modemOpened then return nil end
3313
3314 while true do
3315
3316 -- Broadcast
3317 modem.transmit(CHANNEL_BROADCAST, uniqueID, MASTERMESSAGE)
3318
3319 local senderId, message = receive(0.5)
3320 while senderId do
3321 local n = tonumber(message)
3322 if type(n) == 'number' and n == 1 then
3323 table.insert(slaves, 1, {id=senderId, dist=nil})
3324 return true
3325 end
3326 senderId, message = receive(0.5)
3327 end
3328 end
3329 return nil
3330end
3331
3332
3333-- Send message from owner to slave
3334function swarm.transmitTask(turtleNumber, taskObj, needConfirm)
3335 local messageHash = math.floor(math.random()*(2^20))
3336 taskObj.messageHash = messageHash
3337 local slaveId
3338 local received = {} -- Set of all confirms
3339 local taskString = table.toString(taskObj)
3340
3341
3342 while true do
3343 -- Transmit message to all turtles or to one
3344 if turtleNumber == nil then
3345 for k,v in pairs(slaves) do
3346 slaveId = v.id
3347 modem.transmit(slaveId, uniqueID, taskString)
3348 end
3349 else
3350 slaveId = slaves[turtleNumber].id
3351 modem.transmit(slaveId, uniqueID, taskString)
3352 end
3353
3354 -- Instant leave function if not need to wait responce
3355 if not needConfirm then return true end
3356
3357 -- Receive loop
3358 local senderId, msg, channel = receive(0.5)
3359 while senderId do
3360 local receivedObj = table.fromString(msg)
3361
3362 if turtleNumber == nil then
3363 -- Check if we have this turtle in our list
3364 -- And if so, add to receive
3365 local n = tblContainsVal(slaves, 'id', senderId)
3366 if n~= nil and channel ~= CHANNEL_BROADCAST then
3367 received[senderId] = true
3368 end
3369
3370 -- If we received confirms from all turtles, return
3371 if tblLen(received) == tblLen(slaves) then return true end
3372 else
3373 if senderId == slaveId and receivedObj and receivedObj.messageHash == messageHash then
3374 return true
3375 end
3376 end
3377
3378 senderId, msg, channel = receive(0.5)
3379 end
3380 end
3381end
3382
3383-- If we have something
3384function swarm.transmitToMaster(obj)
3385 if type(obj) == 'table' then obj = table.toString(obj) end
3386 modem.transmit(masterId, uniqueID, obj)
3387end
3388
3389
3390-- Receive troubles
3391function swarm.receiveFromSlave(waitingResponceTime)
3392 local senderId, msg, channel = receive(waitingResponceTime)
3393
3394 if not senderId then return end
3395
3396 local n = tblContainsVal(slaves, 'id', senderId)
3397 if n~= nil and channel ~= CHANNEL_BROADCAST then
3398 local receivedObj = table.fromString(msg)
3399 if not receivedObj then return end
3400 receivedObj.slaveId = n
3401 return receivedObj
3402 end
3403end
3404
3405
3406-- Waiting broadcast that means i
3407function swarm.waitGetout()
3408 local senderId, msg, distance
3409 while true do
3410 _,_,_,senderId, msg,distance = os.pullEvent("modem_message")
3411 if msg==GETOUTMESSAGE and distance == 1 then
3412 return true
3413 end
3414 end
3415end
3416
3417-- Waiting broadcast that means i
3418function swarm.sendGetout()
3419 -- Broadcast
3420 modem.transmit(CHANNEL_BROADCAST, uniqueID, GETOUTMESSAGE)
3421end
3422
3423--40_AStar.lua
3424
3425-- ********************************************************************************** --
3426-- ** ** --
3427-- ** A-Star algorithm for 3d dimensional volume ** --
3428-- ** ** --
3429-- ** http://en.wikipedia.org/wiki/A*_search_algorithm ** --
3430-- ** ---------------------------------------------------- ** --
3431-- ** ** --
3432-- ** Developed to use in program "KrutoyTurtle" ** --
3433-- ** http://computercraft.ru/topic/48-stroitelnaia-sistema-krutoyturtle/ ** --
3434-- ** ** --
3435-- ********************************************************************************** --
3436
3437-- Redefine global function for faster acces
3438local abs,pairs,floor,time,insert,remove = math.abs,pairs,math.floor,os.time,table.insert,table.remove
3439
3440-- Heuristic estimate.
3441local function heuristic_cost_estimate(p1,p2)
3442 return abs(p1.x-p2.x) + abs(p1.y-p2.y) + abs(p1.z-p2.z)
3443end
3444
3445-- ********************************************************************************** --
3446-- ** Utils ** --
3447-- ********************************************************************************** --
3448
3449-- 3d array metatable
3450-- Use arr3d() as constructor, arr(x,y,z) as acces and arr:set(x,y,z,v) as set value
3451if not arr3d then arr3d = function() return setmetatable({
3452 set = function(t,x,y,z,v)
3453 t[z] = t[z] or {}
3454 t[z][y] = t[z][y] or {}
3455 t[z][y][x] = v
3456 end,
3457 setVolume = function(t, x1,y1,z1,x2,y2,z2, v)
3458 for z=z1, z2 do
3459 for y=y1, y2 do
3460 for x=x1, x2 do
3461 t:set(x,y,z, v)
3462 end
3463 end
3464 end
3465 end
3466 }, { __call = function(t, x, y, z)
3467 if not t[z] or not t[z][y] then return nil end
3468 return t[z][y][x]
3469 end
3470})end end
3471
3472
3473-- Checking time for avoid yeld exception
3474local chtime
3475local function checktime()
3476 -- os.queueEvent("")
3477 -- coroutine.yield()
3478
3479 if not chtime then chtime=time() end
3480 local ntime=time()
3481 if ntime-chtime>0.05 or ntime-chtime<0 then
3482 sleep(0)
3483 chtime=ntime
3484 end
3485end
3486
3487-- ********************************************************************************** --
3488-- ** BreadCrumb ** --
3489-- ********************************************************************************** --
3490
3491local BreadCrumb = {}
3492BreadCrumb.__index = BreadCrumb
3493
3494function BreadCrumb.new(x,y,z, parent)
3495 local self = setmetatable({}, BreadCrumb)
3496 self.pos = {x=x,y=y,z=z}
3497 self.next = parent
3498 self.cost = math.huge
3499 self.onClosedList = false
3500 self.onOpenList = false
3501 return self
3502end
3503
3504function BreadCrumb:Equals(b)
3505 return b.pos.x == self.pos.x and b.pos.y == self.pos.y and b.pos.z == self.pos.z
3506end
3507
3508-- ********************************************************************************** --
3509-- ** Path Finder ** --
3510-- ********************************************************************************** --
3511
3512-- Neigtbours of current point
3513local surrounding = {
3514 {x=0,y=0,z=-1}, {x=0,y=0,z= 1}, {x=0,y=-1,z=0}, {x=0,y= 1,z=0}, {x=-1,y=0,z=0}, {x=1, y=0,z=0},
3515}
3516
3517
3518-- Method that switfly finds the best path from p_start to end.
3519-- The starting breadcrumb traversable via .next to the end or nil if there is no path
3520-- Method that switfly finds the best path from p_start to end. Doesn't reverse outcome
3521-- The p_end breadcrump where each .next is a step back
3522function AStarFindPath(world, p_start, p_end)
3523 -- note we just flip p_start and end here so you don't have to.
3524 p_end, p_start = p_start, p_end
3525
3526 -- Destination point is bloked
3527 if world(p_start.x, p_start.y, p_start.z) then return end
3528
3529 local openList = {}
3530 local brWorld = arr3d()
3531
3532
3533 local current= BreadCrumb.new(p_start.x,p_start.y,p_start.z)
3534 current.cost = heuristic_cost_estimate(current.pos,p_end)
3535
3536 local finish = BreadCrumb.new(p_end.x,p_end.y,p_end.z)
3537 brWorld:set(current.pos.x,current.pos.y,current.pos.z,current)
3538 insert(openList,current)
3539
3540 --while openList.count > 0 do
3541 while #openList > 0 do
3542 --Find best item and switch it to the 'closedList'
3543 current = remove(openList)
3544 current.onClosedList = true
3545
3546 --Find neighbours
3547 for k,v in pairs(surrounding) do
3548 local tmpX,tmpY,tmpZ = current.pos.x + v.x, current.pos.y + v.y, current.pos.z + v.z
3549 if not world(tmpX,tmpY,tmpZ) then
3550 --Check if we've already examined a neighbour, if not create a new node for it.
3551 local node
3552 if brWorld(tmpX,tmpY,tmpZ) == nil then
3553 node = BreadCrumb.new(tmpX,tmpY,tmpZ)
3554 brWorld:set(tmpX,tmpY,tmpZ,node)
3555 else
3556 node = brWorld(tmpX,tmpY,tmpZ)
3557 end
3558
3559 --If the node is not on the 'closedList' check it's new score, keep the best
3560 if node.onClosedList == false then
3561 local cost = --[[current.cost +]] heuristic_cost_estimate(node.pos,p_end)
3562 --print(string.format("current.pos:%i,%i,%i nodePos:%i,%i,%i cost:%i current.cost:%i node.cost:%i", current.pos.x, current.pos.y, current.pos.z, tmpX,tmpY,tmpZ, cost, current.cost, node.cost))
3563
3564 if cost < node.cost then
3565 node.cost = cost
3566 node.next = current
3567 end
3568
3569 --If the node wasn't on the openList yet, add it
3570 if node.onOpenList == false then
3571 --Check to see if we're done
3572 if node:Equals(finish) == true then
3573 node.next = current
3574 return node
3575 end
3576 node.onOpenList = true
3577
3578 -- Sort and add to open list
3579 local pos = #openList
3580 while pos > 0 and openList[pos].cost <= node.cost do
3581 pos = pos - 1
3582 end
3583 insert(openList,pos+1,node)
3584 --for k,v in pairs(openList) do io.write(v.cost .." ") end
3585 --print()
3586 end
3587 end
3588 end
3589 end
3590 --print()
3591 checktime() -- Check yelding time for computerCraft
3592 end
3593
3594 return nil --no path found
3595end
3596
3597
3598-- ********************************************************************************** --
3599-- ** Usage ** --
3600-- ********************************************************************************** --
3601
3602
3603--[[
3604-- Create new world as 3d array
3605local world = arr3d()
3606
3607-- Block a cell, make it impassable. Indexes from [0]
3608-- Indexes is [x][y][z]
3609--world:setVolume(0,0,0,9,9,0,true)
3610--world:setVolume(0,0,9,9,9,9,true)
3611
3612--world:setVolume(0,0,0,9,0,9,true)
3613--world:setVolume(0,9,0,9,9,9,true)
3614
3615--world:setVolume(0,0,0,0,9,9,true)
3616--world:setVolume(9,0,0,9,9,9,true)
3617world:set(1,3,1,true)
3618world:set(1,7,1,true)
3619world:setVolume(-1,5,-1,3,5,3,true)
3620world:set(1,5,1,nil)
3621
3622
3623local p_start = {x=1,y=9,z=1} -- Start point.
3624local p_end = {x=1,y=1,z=1} -- End point
3625
3626-- Main path find function
3627-- Return the first bread crumb of path
3628local crumb = AStarFindPath(world, p_start, p_end)
3629
3630if crumb == nil then
3631 print('Path not found')
3632else
3633 io.write('['.. crumb.pos.x..","..crumb.pos.y..","..crumb.pos.z.."]->")
3634
3635 -- BreadCrumbs is connected list. To get next point in path use crumb.next
3636 while crumb.next ~= nil do
3637 crumb = crumb.next
3638 io.write('['.. crumb.pos.x..","..crumb.pos.y..","..crumb.pos.z..(crumb.next and "]->" or "]"))
3639 end
3640end
3641--]]
3642
3643
3644--50_TableToString.lua
3645
3646-- ********************************************************************************** --
3647-- ** Serialize table to string ** --
3648-- ** ** --
3649-- ********************************************************************************** --
3650
3651-- declare local variables
3652--// exportstring( string )
3653--// returns a "Lua" portable version of the string
3654local function exportstring( s )
3655 return string.format("%q", s)
3656end
3657
3658local insert = table.insert
3659local tostring= tostring
3660local ipairs = ipairs
3661local pairs = pairs
3662local type = type
3663
3664--// The Save Function
3665function table.toString(tbl)
3666 if type(tbl) ~= 'table' then return "" end
3667
3668 local charS,charE = " ","\n"
3669 local s_tbl = {}
3670
3671 -- initiate variables for save procedure
3672 local tables,lookup = { tbl },{ [tbl] = 1 }
3673 insert(s_tbl, "return {"..charE )
3674
3675 for idx,t in ipairs( tables ) do
3676 insert(s_tbl, "-- Table: {"..idx.."}"..charE )
3677 insert(s_tbl, "{"..charE )
3678 local thandled = {}
3679
3680 for i,v in ipairs( t ) do
3681 thandled[i] = true
3682 local stype = type( v )
3683 -- only handle value
3684 if stype == "table" then
3685 if not lookup[v] then
3686 insert( tables, v )
3687 lookup[v] = #tables
3688 end
3689 insert(s_tbl, charS.."{"..lookup[v].."},"..charE )
3690 elseif stype == "string" then
3691 insert(s_tbl, charS..exportstring( v )..","..charE )
3692 elseif stype == "number" or stype == "boolean" then
3693 insert(s_tbl, charS..tostring( v )..","..charE )
3694 end
3695 end
3696
3697 for i,v in pairs( t ) do
3698 -- escape handled values
3699 if (not thandled[i]) then
3700
3701 local str = ""
3702 local stype = type( i )
3703 -- handle index
3704 if stype == "table" then
3705 if not lookup[i] then
3706 insert( tables,i )
3707 lookup[i] = #tables
3708 end
3709 str = charS.."[{"..lookup[i].."}]="
3710 elseif stype == "string" then
3711 str = charS.."["..exportstring( i ).."]="
3712 elseif stype == "number" or stype == "boolean" then
3713 str = charS.."["..tostring( i ).."]="
3714 end
3715
3716 if str ~= "" then
3717 stype = type( v )
3718 -- handle value
3719 if stype == "table" then
3720 if not lookup[v] then
3721 insert( tables,v )
3722 lookup[v] = #tables
3723 end
3724 insert(s_tbl, str.."{"..lookup[v].."},"..charE )
3725 elseif stype == "string" then
3726 insert(s_tbl, str..exportstring( v )..","..charE )
3727 elseif stype == "number" or stype == "boolean" then
3728 insert(s_tbl, str..tostring( v )..","..charE )
3729 end
3730 end
3731 end
3732 end
3733 insert(s_tbl, "},"..charE )
3734 end
3735 insert(s_tbl, "}" )
3736
3737 return table.concat(s_tbl)
3738end
3739
3740--// The Load Function
3741function table.fromString(s)
3742 if not s then return end
3743 local ftables = loadstring(s)
3744 if not ftables then return end
3745 local tables = ftables()
3746 for idx = 1,#tables do
3747 local tolinki = {}
3748 for i,v in pairs( tables[idx] ) do
3749 if type( v ) == "table" then
3750 tables[idx][i] = tables[v[1]]
3751 end
3752 if type( i ) == "table" and tables[i[1]] then
3753 insert( tolinki,{ i,tables[i[1]] } )
3754 end
3755 end
3756 -- link indices
3757 for _,v in ipairs( tolinki ) do
3758 tables[idx][v[2]],tables[idx][v[1]] = tables[idx][v[1]],nil
3759 end
3760 end
3761 return tables[1]
3762end
3763
3764
3765--60_Thread.lua
3766
3767-- ********************************************************************************** --
3768-- ** Thread ** --
3769-- ** by ZeroGalaxy ** --
3770-- ** ---------------------------------------------------- ** --
3771-- ** ** --
3772-- ** Easy acces to parralel process in ComputerCraft ** --
3773-- ** http://computercraft.ru/topic/393 ** --
3774-- ** ** --
3775-- ********************************************************************************** --
3776
3777thread = {}
3778thread.__index = thread
3779
3780local mainThread=coroutine.running()
3781local filter={}
3782
3783local function SingleThread( _sFilter )
3784 return coroutine.yield( _sFilter )
3785end
3786
3787local function MultiThread( _sFilter )
3788 if coroutine.running()==mainThread then
3789 local event,co
3790 repeat
3791 event={coroutine.yield()}
3792 co=next(filter)
3793 if not co then os.pullEventRaw=SingleThread end
3794 while co do
3795 if coroutine.status( co ) == "dead" then
3796 filter[co],co=nil,next(filter,co)
3797 else
3798 if filter[co] == '' or filter[co] == event[1] or event[1] == "terminate" then
3799 local ok, param = coroutine.resume( co, unpack(event) )
3800 if not ok then filter={} error( param )
3801 else filter[co] = param or '' end
3802 end
3803 co=next(filter,co)
3804 end
3805 end
3806 until _sFilter == nil or _sFilter == event[1] or event[1] == "terminate"
3807 return unpack(event)
3808 else
3809 return coroutine.yield( _sFilter )
3810 end
3811end
3812
3813function thread.create(f,...)
3814 os.pullEventRaw=MultiThread
3815 local co=coroutine.create(f)
3816 filter[co]=''
3817 local ok, param = coroutine.resume( co, ... )
3818 if not ok then filter={} error( param )
3819 else filter[co] = param or '' end
3820 return co
3821end
3822
3823function thread.kill(co)
3824 filter[co]=nil
3825end
3826
3827function thread.killAll()
3828 filter={}
3829 os.pullEventRaw=SingleThread
3830end
3831
3832--70_VolumeLoaders.lua
3833
3834-- ********************************************************************************** --
3835-- ** Loader of .vox files from MagivaVoxels editor and .nfa files from NPaintPRO** --
3836-- ** https://voxel.codeplex.com ** --
3837-- ** ** --
3838-- ********************************************************************************** --
3839
3840volumeLoader = {}
3841volumeLoader.__index = volumeLoader
3842
3843
3844--===========================================================
3845-- Read string from binary file with
3846--===========================================================
3847local function readBString(file, len)
3848 local s = ''
3849 for i=1,len do
3850 s = s .. string.char(file.read())
3851 end
3852 return s
3853end
3854
3855--===========================================================
3856-- Read 4 bites and convert them to integer
3857--===========================================================
3858local function readBInt(file)
3859 local t = {}
3860 for i=1,4 do table.insert(t, file.read()) end
3861 local n=0
3862 for k=1,#t do
3863 n=n+t[k]*2^((k-1)*8)
3864 end
3865 return n
3866end
3867
3868--===========================================================
3869-- Locad voxel files from program MagcaVoxels
3870--===========================================================
3871function volumeLoader.load_vox(path)
3872 local bFile = fs.open(path, "rb")
3873
3874 local magic = readBString(bFile, 4)
3875 assert(magic=="VOX ")
3876
3877 local version = readBString(bFile, 4)
3878
3879 local mainChunkID = readBString(bFile, 4)
3880 assert(mainChunkID=="MAIN")
3881
3882 local mainChunkSize = readBInt(bFile)
3883 local mainchildChunks = readBInt(bFile)
3884 readBString(bFile, mainChunkSize)
3885
3886 local sizeID = readBString(bFile, 4)
3887 assert(sizeID=="SIZE")
3888 readBString(bFile, 8) -- Dont know what this two numbers means.
3889
3890 local sizeX = readBInt(bFile)
3891 local sizeY = readBInt(bFile)
3892 local sizeZ = readBInt(bFile)
3893
3894 local voxelID = readBString(bFile, 4)
3895 assert(voxelID=="XYZI")
3896 readBString(bFile, 8) -- Dont know what this two numbers means.
3897 local numVoxels= readBInt(bFile)
3898
3899 local vol = arr3d()
3900 for j=1, numVoxels do
3901 local x,y,z,i = bFile.read(),bFile.read(),bFile.read(),bFile.read()
3902
3903 vol:set(x+1,y+1,z+1, i)
3904 end
3905 bFile.close()
3906
3907 return vol, sizeX, sizeY, sizeZ
3908end
3909
3910
3911
3912--===========================================================
3913-- Load files from NPaintPro
3914--===========================================================
3915function volumeLoader.load_nfa(path)
3916 if fs.exists(path) == false then
3917 return nil
3918 end
3919
3920 local vol = arr3d()
3921
3922 local sizeX,sizeY,sizeZ = 1,1,1
3923
3924 local file = io.open(path, "r" )
3925 local sLine = file:read()
3926 local z = 1
3927 local y = 1
3928 while sLine do
3929 if sLine == "~" then
3930 z = z + 1
3931 sizeZ = math.max(sizeZ, z)
3932 y = 1
3933 else
3934 local x=1
3935 for i=1,#sLine do
3936 vol:set(x,y,z, tonumber(string.sub(sLine,i,i), 16))
3937 sizeX = math.max(sizeX, x)
3938 x=x+1
3939 end
3940 sizeY = math.max(sizeY, y)
3941 y = y+1
3942 end
3943 sLine = file:read()
3944 end
3945 file:close()
3946
3947 return vol, sizeX, sizeY, sizeZ
3948end
3949
3950--99_main.lua
3951
3952main()