antonsavov

WIP utils

Dec 11th, 2017
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.94 KB | None | 0 0
  1. -------------------------------
  2. -- /lib/utils
  3. -------------------------------
  4. --- A collection of utiltity functions that are not depending on the variables and settings of the 20.000 BLOCKS project or on Minecraft stuff
  5.  
  6.  
  7. --sort table by random
  8. function shuffleTable( t )
  9.     local rand = math.random
  10.     assert( t, "shuffleTable() expected a table, got nil" )
  11.     local iterations = #t
  12.     local j
  13.    
  14.     for i = iterations, 2, -1 do
  15.         j = rand(i)
  16.         t[i], t[j] = t[j], t[i]
  17.     end
  18. end
  19.  
  20. --- returns how many items are in a table/list
  21. function tableLength(T)
  22.     assert( T, "shuffleTable() expected a table, got nil" )
  23.     local count = 0
  24.     for _ in pairs(T) do
  25.         count = count + 1
  26.     end
  27.     return count
  28. end
  29.  
  30. --- check if the file exists
  31. -- TODO move this to an IO package
  32. function file_exists(filename)
  33.   local f = fs.open(filename, "rb")
  34.   if f then f:close() end
  35.   return f ~= nil
  36. end
  37.  
  38. --- creates a new text file and writes a string to it
  39. function file_write(text, filename, path)
  40.     if path then fs.makeDir(path) end
  41.     local fullname = (path or "")..filename
  42.     local file = assert(fs.open(fullname, "w"), "error writing to file:"..fullname)
  43.     file.write(text)
  44.     file.close()
  45. end
  46.  
  47. --- Appends a string to a text file
  48. function file_append(text, filename, path)
  49.     if not file_exists(filename) then file_write(text, filename, path) return end
  50.     local fullname = (path or "")..filename
  51.     local file = assert(fs.open(fullname, "a"), "error appending to file:"..fullname)
  52.     file.write(text)
  53.     file.close()
  54. end
  55.  
  56.  
  57. --- table_print.
  58. -- does a table to a string.
  59. function table_print (tt, indent, done)
  60.   done = done or {}
  61.   indent = indent or 0
  62.   if type(tt) == "table" then
  63.     local sb = {}
  64.     for key, value in pairs (tt) do
  65.       table.insert(sb, string.rep (" ", indent)) -- indent it
  66.       if type (value) == "table" and not done [value] then
  67.         done [value] = true
  68.         table.insert(sb, "{\n");
  69.         table.insert(sb, table_print (value, indent + 2, done))
  70.         table.insert(sb, string.rep (" ", indent)) -- indent it
  71.         table.insert(sb, "}\n");
  72.       elseif "number" == type(key) then
  73.         table.insert(sb, string.format("\"%s\"\n", tostring(value)))
  74.       else
  75.         table.insert(sb, string.format(
  76.             "%s = \"%s\"\n", tostring (key), tostring(value)))
  77.        end
  78.     end
  79.     return table.concat(sb)
  80.   else
  81.     return tt .. "\n"
  82.   end
  83. end
  84.  
  85. --- universal to string.
  86. -- supports tables and other types
  87. function to_string( tbl )
  88.     if  "nil"       == type( tbl ) then
  89.         return tostring(nil)
  90.     elseif  "table" == type( tbl ) then
  91.         return table_print(tbl)
  92.     elseif  "string" == type( tbl ) then
  93.         return tbl
  94.     else
  95.         return tostring(tbl)
  96.     end
  97. end
  98.  
  99. --returns a point in the grid that is closest to the sample point defined with x, y and z
  100. function closestGridPoint(center, size, x, y, z)
  101.     local closestPoint = {
  102.         x = math.floor ((x-center.x) / size )*size+center.x,
  103.         y = math.floor ((y-center.y) / size )*size+center.y,
  104.         z = math.floor ((z-center.z) / size )*size+center.z
  105.     }
  106.     return closestPoint
  107. end
  108.  
  109. --returns a coordinate in the grid rhythm that is closest to the sample coordinate
  110. function closestGridCoord(centerCoord, size, sampleCoord)
  111.     local closestCoord = math.floor ((sampleCoord-centerCoord) / size )*size+centerCoord
  112.     return closestCoord
  113. end
  114.  
  115. --- Return 1 if the number is positive and -1 if its negative
  116. function signedOne(num)
  117.     return math.abs(num)/num
  118. end
  119.  
  120. --- Creates a box object
  121. -- if sizes are negative, moves the corner so that sizes are positive and it is still the same box
  122. function box(corner_x, corner_y, corner_z, size_x, size_y, size_z)
  123.     if size_x ~= 0 and size_x ~= 0 and size_x ~= 0 then
  124.         if size_x < 0 then corner_x = corner_x + size_x; size_x = -size_x; end
  125.         if size_y < 0 then corner_y = corner_y + size_y; size_y = -size_y; end
  126.         if size_z < 0 then corner_z = corner_z + size_z; size_z = -size_z; end             
  127.         return {
  128.             corner_x = corner_x,
  129.             corner_y = corner_y,
  130.             corner_z = corner_z,
  131.             size_x = size_x,
  132.             size_y = size_y,
  133.             size_z = size_z,
  134.             base_center_x = corner_x + math.floor ( size_x/2 ),
  135.             base_center_y = corner_y,
  136.             base_center_z = corner_z + math.floor ( size_z/2 ),
  137.             selector = "x="..corner_x..",y="..corner_y..",z="..corner_z..",dx="..size_x..",dy="..size_y..",dz="..size_z
  138.         }
  139.     else
  140.         return nil
  141.     end
  142. end
  143.  
  144. --- moves the box up with 1 block and shortens its height with 1
  145. -- NOTE: the base of a box with negative size_y is actually the top layer of the box
  146. --returns a new box
  147. function box_remove_base(_box)
  148.     --assert(type(box) == table , "box_remove_base(box): box must be a table")
  149.     --if box.size_y == 1 then return nil end
  150.     return box( _box.corner_x, _box.corner_y + 1 --[[TODO this is wrong for boxes with negative size_y, unit test it]], _box.corner_z, _box.size_x, _box.size_y - 1, _box.size_z )
  151. end
  152.  
  153. --- Returns a new box representing the top numlayers of the original box
  154. -- NOTE: the top layers of a box with negative size_y are actually its bottom layers
  155. function box_top_layers(_box, numlayers)
  156.     assert(_box.size_y >= numlayers, "box_top_layers(): original box must be same or taller as the number of layers wanted. size:".._box.size_y.." wanted:"..numlayers)
  157.     local newbox = box ( _box.corner_x, _box.corner_y + _box.size_y - numlayers, _box.corner_z, _box.size_x, numlayers, _box.size_z )
  158.     debug.log("box_top_layers(): numlayers="..numlayers.."; _box={".._box.selector.."}; newbox={"..newbox.selector.."}")
  159.     return newbox
  160. end
  161.  
  162. --- Returns a new box that represents the base of the given box
  163. -- NOTE: the base of a box with negative size_y is actually the top layer of the box
  164. function box_base( _box)
  165.     --assert(type(box) == table , "box_base(box): box must be a table")
  166.     return box( _box.corner_x, _box.corner_y, _box.corner_z,  _box.size_x, 1,_box.size_z )
  167. end
  168.  
  169. --- Moves a box numerically along the Y axis
  170. function box_move_y( _box, delta_y)
  171.     return box( _box.corner_x, _box.corner_y+delta_y, _box.corner_z,  _box.size_x, _box.size_y,_box.size_z )
  172. end
  173.  
  174. --- Offsets a box object
  175. -- if only offset_x is provided than it is used to offset in all direction with it
  176. -- TODO unit test for this for boxes with negative sizes and negative pos of the corners etc.
  177. function box_offset( _box, offset_x, offset_y, offset_z )
  178.     assert(type(offset_x)=='number', "box_offset(): argument 'offset_x' must be a number.")
  179.     local ox, oz, oy = offset_x, offset_z, offset_y
  180.     if oz == nil and oy == nil then oz = ox; oy = ox; end --if only offset_x is provided than it is used to offset in all direction with it
  181.     ox = ox or 0
  182.     oy = oy or 0
  183.     oz = oz or 0
  184.     return box( _box.corner_x-ox, _box.corner_y-oy, _box.corner_y-oy, _box.size_x+ox*2, _box.size_y+oy*2, _box.size_z+oz*2 )
  185. end
  186.  
  187. --- Divides a box into 8 boxes by slicing the original box halfway on all three axes
  188. function box_subdivide(_box)
  189.     if _box then
  190.         local x, y, z = _box.corner_x, _box.corner_y, _box.corner_z
  191.         local w, h, l = _box.size_x, _box.size_y, _box.size_z
  192.         local halfW = math.floor(w/2)
  193.         local restW = w-halfW
  194.         local halfH = math.floor(h/2)
  195.         local restH = h-halfH
  196.         local halfL = math.floor(l/2)
  197.         local restL = l-halfL
  198.         local result = {}  
  199.         table.insert(result,box( x+halfW, y+halfH, z+halfL, restW, restH, restL ))
  200.         table.insert(result,box( x, y+halfH, z+halfL, halfW, restH, restL ))
  201.         table.insert(result,box( x+halfW, y, z+halfL, restW, halfH, restL ))
  202.         table.insert(result,box( x+halfW, y+halfH, z, restW, restH, halfL ))
  203.         table.insert(result,box( x, y, z+halfL, halfW, halfH, restL ))
  204.         table.insert(result,box( x+halfW, y, z, restW, halfH, halfL ))
  205.         table.insert(result,box( x, y+halfH, z, halfW, restH, halfL ))
  206.         table.insert(result,box( x, y, z, halfW, halfH, halfL ))
  207.         return result
  208.     else
  209.         return nil
  210.     end
  211. end
  212.  
  213.  
  214. --TODO move this to Setter package
  215. --- Covers an area with chunkLoaders so that it stays always loaded
  216. -- requires the NEOTECH MOD
  217. -- The placement principle is:
  218. --
  219. --  . . . . . . . . . . . . . . . . . . . . . . . .
  220. --  . . . z . . . . . . . . . . . . . . . . . . . .
  221. --  . . . O x . . . . . . . . . . . . . . . . . . .
  222. --  . . . . . . . . . . . . . . . . . . . . . . . .
  223. --  . . . . . . . . . . . . . . . . . . . . . . . .
  224. --  . . . + + + C + + + + + C + + + + + . . . . . .
  225. --  . . . + + + + + + + + + + + + + + + . . . . . .
  226. --  . . . + + H H H H H H H H H H H + + . . . . . .
  227. --  . . . C + H H H H C H H H H H C + + . . . . . .
  228. --  . . . + + H H H H H H H H H H H + + . . . . . .
  229. --  . . . + + H H H H H H H H H H H + + . . . . . .
  230. --  . . . + + H C H H H H H C H H H + + . . . . . .
  231. --  . . . + + H H H H H H H H H H H + + . . . . . .
  232. --  . . . + + H H H H H H H H H H H + + . . . . . .
  233. --  . . . C + H H H H C H H H H H C + + . . . . . .
  234. --  . . . + + H H H H H H H H H H H + + . . . . . .
  235. --  . . . + + + C + + + + + C + + + + + . . . . . .
  236. --  . . . + + + + + + + + + + + + + + + . . . . . .
  237. --  . . . . . . . . . . . . . . . . . . . . . . . .
  238. --  . . . . . . . . . . . . . . . . . . . . . . . .
  239. --
  240. -- C - a chunkloader with radius 3 chunks
  241. -- . - a chunk that is not part of the desired area
  242. -- o - a chunk that is part of the desired area
  243. -- H - a chunk that is not part of the desired area but included in the ones considered for a chunkloader
  244. -- O - the chunk which is taken as origin for the criss-cross grid
  245. -- x - positive direction of x axis
  246. -- z - positive direction of z axis
  247. -- this criss-cross(or whatever is called) distribution leads to the least amount of required chunkloaders
  248. -- the placement is in a loop starting from closest to the origin out and sleeping 0.1 so that the latest placed chunkloader takes action
  249. -- the chunkloaders are not placed asyncronously but directly (commands.setblock is used and not commands.async.setblock)
  250. -- the chunkloaders are placed in the 0,y,0 coordinate of a chunk
  251. --
  252. --
  253. -- @param origin A block in the chunk which is taken as origin for the criss-cross grid. It is a tuple with .x, .y and .z fields - the coordinates are in minecraft world coordinates
  254. -- x, y, z are the corner of the area and are also given in minecraft world coordinates
  255. -- sizeX and sizeZ are the width and length of the area and are given in minecraft blocks and can be negative
  256. -- @param remove If "remove" is set to true then the function removes the chunkloaders in the area and replaces them with AIR.
  257. function do_chunk_loaders(origin,x,y,z,sizeX,sizeZ,remove)
  258.     --commands.setblock(-71, 56, -511, "neotech:chunkLoader", 0, "replace", '{id:"neotech:chunkLoader", Diameter:1}')
  259.     local radius = 2 -- the radius in chunks that the chunkloaders cover
  260.     local offset = 2 -- how many chunks to expand the area for placing of chunkloaders, expanding of 2 is needed with chunkLoaders with radius 3
  261.    
  262.     local ox = math.floor(origin.x/16) -- the grid origin chunk X index
  263.     local oz = math.floor(origin.z/16) -- the grid origin chunk Z index
  264.     --add chunkloaders in the area
  265.     local uX = sizeX/math.abs(sizeX)
  266.     local uZ = sizeZ/math.abs(sizeZ)
  267.     local startChunk = {
  268.         x = math.floor(x/16)-uX*offset,
  269.         z= math.floor(z/16)-uZ*offset
  270.     }
  271.     local endChunk = {
  272.         x = math.floor((x+sizeX)/16)+uX*offset,
  273.         z = math.floor((z+sizeZ)/16)+uZ*offset
  274.     }
  275.     --this makes sure we start to cover with chunkloaders from the origin out
  276.     --as if we do the oposite the further point might lie in a currently unloaded chunk and the whole thing will fail
  277.     --print("x values before", ox, sx, ex, stepx)
  278.     --print("sizes", sizeX, sizeZ)
  279.     local sx, ex, stepx
  280.     local sz, ez, stepz
  281.     if math.abs(startChunk.x - ox) < math.abs(endChunk.x - ox) then
  282.         sx = startChunk.x
  283.         ex = endChunk.x
  284.         stepx = uX
  285.     else
  286.         sx = endChunk.x
  287.         ex = startChunk.x
  288.         stepx = -uX
  289.     end
  290.     if math.abs(startChunk.z - oz) < math.abs(endChunk.z - oz) then
  291.         sz = startChunk.z
  292.         ez = endChunk.z    
  293.         stepz = uZ
  294.     else
  295.         sz = endChunk.z
  296.         ez = startChunk.z
  297.         stepz = -uZ
  298.     end
  299.     --print("x values", ox, sx, ex, stepx)
  300.     --print("z values", oz, sz, ez, stepz)
  301.     --now loop through the chunks
  302.     --ix = sx
  303.     --iz = sz
  304.     --while ix and iz are not both equalt to ex and ez respecitvely
  305.     --for kx from sx to ix
  306.     --if chunkloader must be placed at kx,iz place it
  307.     --end
  308.     --for kz from sz to iz
  309.     --if chunkloader must be placed at ix,kz then place it
  310.     -- increase both with one
  311.     -- if ix is bigger than ex make it ex
  312.     -- if iz is bigger than ez make it ez
  313.     --end while
  314.     for ix = sx, ex, stepx do
  315.         -- if the row is from the criss-cross grid
  316.  
  317.         if (ix - ox) % radius == 0 then
  318.             for iz = sz, ez, stepz do
  319.                 --if it is an even row dont shift the grid
  320.                 local place = false
  321.                 --print (((ix -ox)/radius) % 2)
  322.                 --print("for chunk", ix,iz)
  323.                 if ((ix -ox)/radius) % 2 == 0 then
  324.                     -- if the column is from the criss-cross grid
  325.                     --if (iz - oz) % (2*radius) == 0 then
  326.                     if (iz - oz) % (radius) == 0 then --makes a normal grid with step radius
  327.                         --place a chunkloader
  328.                         place = true
  329.                     end
  330.                 else --else if it is an odd row shift the grid
  331.                     -- if the column is from the criss-cross grid
  332.                     --if (iz - oz + radius) % (2*radius) == 0 then
  333.                     if (iz - oz + radius) % (radius) == 0 then --makes a normal grid with step radius                      
  334.                         --place a chunk loader
  335.                         place = true
  336.                     end
  337.                 end
  338.                 if place then
  339.                     -- do the actual placing of hte chunkloader
  340.                     local wx = ix*16 -- the world X coordinate of the chunkloader
  341.                     local wy = y -- the world Y coordinate of the chunkloader                          
  342.                     local wz = iz*16 -- the world Zcoordinate of the chunkloader                       
  343.                    
  344.                     if remove then
  345.                         --remove chunkloaders in the area
  346.                         print("removing chunkloader",wx,wy,wz)
  347.                         commands.setblock(wx, wy, wz, "minecraft:air", 0, "replace")
  348.                     else
  349.                         print("adding chunkloader",wx,wy,wz)
  350.                         --add chunkloaders in the area
  351.                         commands.setblock(wx, wy, wz, "neotech:chunkLoader", 0, "replace", '{id:"neotech:chunkLoader", Diameter:'..(radius+1)..'}')
  352.                         --commands.setblock(wx, wy, wz, "minecraft:wool", 0, "replace") -- just for testing
  353.                     end
  354.                     --sleep(0.1)
  355.                 end
  356.                
  357.             end
  358.         end
  359.  
  360.     end
  361. end
Add Comment
Please, Sign In to add comment