Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -------------------------------
- -- /lib/utils
- -------------------------------
- --- A collection of utiltity functions that are not depending on the variables and settings of the 20.000 BLOCKS project or on Minecraft stuff
- --sort table by random
- function shuffleTable( t )
- local rand = math.random
- assert( t, "shuffleTable() expected a table, got nil" )
- local iterations = #t
- local j
- for i = iterations, 2, -1 do
- j = rand(i)
- t[i], t[j] = t[j], t[i]
- end
- end
- --- returns how many items are in a table/list
- function tableLength(T)
- assert( T, "shuffleTable() expected a table, got nil" )
- local count = 0
- for _ in pairs(T) do
- count = count + 1
- end
- return count
- end
- --- check if the file exists
- -- TODO move this to an IO package
- function file_exists(filename)
- local f = fs.open(filename, "rb")
- if f then f:close() end
- return f ~= nil
- end
- --- creates a new text file and writes a string to it
- function file_write(text, filename, path)
- if path then fs.makeDir(path) end
- local fullname = (path or "")..filename
- local file = assert(fs.open(fullname, "w"), "error writing to file:"..fullname)
- file.write(text)
- file.close()
- end
- --- Appends a string to a text file
- function file_append(text, filename, path)
- if not file_exists(filename) then file_write(text, filename, path) return end
- local fullname = (path or "")..filename
- local file = assert(fs.open(fullname, "a"), "error appending to file:"..fullname)
- file.write(text)
- file.close()
- end
- --- table_print.
- -- does a table to a string.
- function table_print (tt, indent, done)
- done = done or {}
- indent = indent or 0
- if type(tt) == "table" then
- local sb = {}
- for key, value in pairs (tt) do
- table.insert(sb, string.rep (" ", indent)) -- indent it
- if type (value) == "table" and not done [value] then
- done [value] = true
- table.insert(sb, "{\n");
- table.insert(sb, table_print (value, indent + 2, done))
- table.insert(sb, string.rep (" ", indent)) -- indent it
- table.insert(sb, "}\n");
- elseif "number" == type(key) then
- table.insert(sb, string.format("\"%s\"\n", tostring(value)))
- else
- table.insert(sb, string.format(
- "%s = \"%s\"\n", tostring (key), tostring(value)))
- end
- end
- return table.concat(sb)
- else
- return tt .. "\n"
- end
- end
- --- universal to string.
- -- supports tables and other types
- function to_string( tbl )
- if "nil" == type( tbl ) then
- return tostring(nil)
- elseif "table" == type( tbl ) then
- return table_print(tbl)
- elseif "string" == type( tbl ) then
- return tbl
- else
- return tostring(tbl)
- end
- end
- --returns a point in the grid that is closest to the sample point defined with x, y and z
- function closestGridPoint(center, size, x, y, z)
- local closestPoint = {
- x = math.floor ((x-center.x) / size )*size+center.x,
- y = math.floor ((y-center.y) / size )*size+center.y,
- z = math.floor ((z-center.z) / size )*size+center.z
- }
- return closestPoint
- end
- --returns a coordinate in the grid rhythm that is closest to the sample coordinate
- function closestGridCoord(centerCoord, size, sampleCoord)
- local closestCoord = math.floor ((sampleCoord-centerCoord) / size )*size+centerCoord
- return closestCoord
- end
- --- Return 1 if the number is positive and -1 if its negative
- function signedOne(num)
- return math.abs(num)/num
- end
- --- Creates a box object
- -- if sizes are negative, moves the corner so that sizes are positive and it is still the same box
- function box(corner_x, corner_y, corner_z, size_x, size_y, size_z)
- if size_x ~= 0 and size_x ~= 0 and size_x ~= 0 then
- if size_x < 0 then corner_x = corner_x + size_x; size_x = -size_x; end
- if size_y < 0 then corner_y = corner_y + size_y; size_y = -size_y; end
- if size_z < 0 then corner_z = corner_z + size_z; size_z = -size_z; end
- return {
- corner_x = corner_x,
- corner_y = corner_y,
- corner_z = corner_z,
- size_x = size_x,
- size_y = size_y,
- size_z = size_z,
- base_center_x = corner_x + math.floor ( size_x/2 ),
- base_center_y = corner_y,
- base_center_z = corner_z + math.floor ( size_z/2 ),
- selector = "x="..corner_x..",y="..corner_y..",z="..corner_z..",dx="..size_x..",dy="..size_y..",dz="..size_z
- }
- else
- return nil
- end
- end
- --- moves the box up with 1 block and shortens its height with 1
- -- NOTE: the base of a box with negative size_y is actually the top layer of the box
- --returns a new box
- function box_remove_base(_box)
- --assert(type(box) == table , "box_remove_base(box): box must be a table")
- --if box.size_y == 1 then return nil end
- 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 )
- end
- --- Returns a new box representing the top numlayers of the original box
- -- NOTE: the top layers of a box with negative size_y are actually its bottom layers
- function box_top_layers(_box, numlayers)
- 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)
- local newbox = box ( _box.corner_x, _box.corner_y + _box.size_y - numlayers, _box.corner_z, _box.size_x, numlayers, _box.size_z )
- debug.log("box_top_layers(): numlayers="..numlayers.."; _box={".._box.selector.."}; newbox={"..newbox.selector.."}")
- return newbox
- end
- --- Returns a new box that represents the base of the given box
- -- NOTE: the base of a box with negative size_y is actually the top layer of the box
- function box_base( _box)
- --assert(type(box) == table , "box_base(box): box must be a table")
- return box( _box.corner_x, _box.corner_y, _box.corner_z, _box.size_x, 1,_box.size_z )
- end
- --- Moves a box numerically along the Y axis
- function box_move_y( _box, delta_y)
- return box( _box.corner_x, _box.corner_y+delta_y, _box.corner_z, _box.size_x, _box.size_y,_box.size_z )
- end
- --- Offsets a box object
- -- if only offset_x is provided than it is used to offset in all direction with it
- -- TODO unit test for this for boxes with negative sizes and negative pos of the corners etc.
- function box_offset( _box, offset_x, offset_y, offset_z )
- assert(type(offset_x)=='number', "box_offset(): argument 'offset_x' must be a number.")
- local ox, oz, oy = offset_x, offset_z, offset_y
- 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
- ox = ox or 0
- oy = oy or 0
- oz = oz or 0
- 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 )
- end
- --- Divides a box into 8 boxes by slicing the original box halfway on all three axes
- function box_subdivide(_box)
- if _box then
- local x, y, z = _box.corner_x, _box.corner_y, _box.corner_z
- local w, h, l = _box.size_x, _box.size_y, _box.size_z
- local halfW = math.floor(w/2)
- local restW = w-halfW
- local halfH = math.floor(h/2)
- local restH = h-halfH
- local halfL = math.floor(l/2)
- local restL = l-halfL
- local result = {}
- table.insert(result,box( x+halfW, y+halfH, z+halfL, restW, restH, restL ))
- table.insert(result,box( x, y+halfH, z+halfL, halfW, restH, restL ))
- table.insert(result,box( x+halfW, y, z+halfL, restW, halfH, restL ))
- table.insert(result,box( x+halfW, y+halfH, z, restW, restH, halfL ))
- table.insert(result,box( x, y, z+halfL, halfW, halfH, restL ))
- table.insert(result,box( x+halfW, y, z, restW, halfH, halfL ))
- table.insert(result,box( x, y+halfH, z, halfW, restH, halfL ))
- table.insert(result,box( x, y, z, halfW, halfH, halfL ))
- return result
- else
- return nil
- end
- end
- --TODO move this to Setter package
- --- Covers an area with chunkLoaders so that it stays always loaded
- -- requires the NEOTECH MOD
- -- The placement principle is:
- --
- -- . . . . . . . . . . . . . . . . . . . . . . . .
- -- . . . z . . . . . . . . . . . . . . . . . . . .
- -- . . . O x . . . . . . . . . . . . . . . . . . .
- -- . . . . . . . . . . . . . . . . . . . . . . . .
- -- . . . . . . . . . . . . . . . . . . . . . . . .
- -- . . . + + + C + + + + + C + + + + + . . . . . .
- -- . . . + + + + + + + + + + + + + + + . . . . . .
- -- . . . + + H H H H H H H H H H H + + . . . . . .
- -- . . . C + H H H H C H H H H H C + + . . . . . .
- -- . . . + + H H H H H H H H H H H + + . . . . . .
- -- . . . + + H H H H H H H H H H H + + . . . . . .
- -- . . . + + H C H H H H H C H H H + + . . . . . .
- -- . . . + + H H H H H H H H H H H + + . . . . . .
- -- . . . + + H H H H H H H H H H H + + . . . . . .
- -- . . . C + H H H H C H H H H H C + + . . . . . .
- -- . . . + + H H H H H H H H H H H + + . . . . . .
- -- . . . + + + C + + + + + C + + + + + . . . . . .
- -- . . . + + + + + + + + + + + + + + + . . . . . .
- -- . . . . . . . . . . . . . . . . . . . . . . . .
- -- . . . . . . . . . . . . . . . . . . . . . . . .
- --
- -- C - a chunkloader with radius 3 chunks
- -- . - a chunk that is not part of the desired area
- -- o - a chunk that is part of the desired area
- -- H - a chunk that is not part of the desired area but included in the ones considered for a chunkloader
- -- O - the chunk which is taken as origin for the criss-cross grid
- -- x - positive direction of x axis
- -- z - positive direction of z axis
- -- this criss-cross(or whatever is called) distribution leads to the least amount of required chunkloaders
- -- 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
- -- the chunkloaders are not placed asyncronously but directly (commands.setblock is used and not commands.async.setblock)
- -- the chunkloaders are placed in the 0,y,0 coordinate of a chunk
- --
- --
- -- @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
- -- x, y, z are the corner of the area and are also given in minecraft world coordinates
- -- sizeX and sizeZ are the width and length of the area and are given in minecraft blocks and can be negative
- -- @param remove If "remove" is set to true then the function removes the chunkloaders in the area and replaces them with AIR.
- function do_chunk_loaders(origin,x,y,z,sizeX,sizeZ,remove)
- --commands.setblock(-71, 56, -511, "neotech:chunkLoader", 0, "replace", '{id:"neotech:chunkLoader", Diameter:1}')
- local radius = 2 -- the radius in chunks that the chunkloaders cover
- local offset = 2 -- how many chunks to expand the area for placing of chunkloaders, expanding of 2 is needed with chunkLoaders with radius 3
- local ox = math.floor(origin.x/16) -- the grid origin chunk X index
- local oz = math.floor(origin.z/16) -- the grid origin chunk Z index
- --add chunkloaders in the area
- local uX = sizeX/math.abs(sizeX)
- local uZ = sizeZ/math.abs(sizeZ)
- local startChunk = {
- x = math.floor(x/16)-uX*offset,
- z= math.floor(z/16)-uZ*offset
- }
- local endChunk = {
- x = math.floor((x+sizeX)/16)+uX*offset,
- z = math.floor((z+sizeZ)/16)+uZ*offset
- }
- --this makes sure we start to cover with chunkloaders from the origin out
- --as if we do the oposite the further point might lie in a currently unloaded chunk and the whole thing will fail
- --print("x values before", ox, sx, ex, stepx)
- --print("sizes", sizeX, sizeZ)
- local sx, ex, stepx
- local sz, ez, stepz
- if math.abs(startChunk.x - ox) < math.abs(endChunk.x - ox) then
- sx = startChunk.x
- ex = endChunk.x
- stepx = uX
- else
- sx = endChunk.x
- ex = startChunk.x
- stepx = -uX
- end
- if math.abs(startChunk.z - oz) < math.abs(endChunk.z - oz) then
- sz = startChunk.z
- ez = endChunk.z
- stepz = uZ
- else
- sz = endChunk.z
- ez = startChunk.z
- stepz = -uZ
- end
- --print("x values", ox, sx, ex, stepx)
- --print("z values", oz, sz, ez, stepz)
- --now loop through the chunks
- --ix = sx
- --iz = sz
- --while ix and iz are not both equalt to ex and ez respecitvely
- --for kx from sx to ix
- --if chunkloader must be placed at kx,iz place it
- --end
- --for kz from sz to iz
- --if chunkloader must be placed at ix,kz then place it
- -- increase both with one
- -- if ix is bigger than ex make it ex
- -- if iz is bigger than ez make it ez
- --end while
- for ix = sx, ex, stepx do
- -- if the row is from the criss-cross grid
- if (ix - ox) % radius == 0 then
- for iz = sz, ez, stepz do
- --if it is an even row dont shift the grid
- local place = false
- --print (((ix -ox)/radius) % 2)
- --print("for chunk", ix,iz)
- if ((ix -ox)/radius) % 2 == 0 then
- -- if the column is from the criss-cross grid
- --if (iz - oz) % (2*radius) == 0 then
- if (iz - oz) % (radius) == 0 then --makes a normal grid with step radius
- --place a chunkloader
- place = true
- end
- else --else if it is an odd row shift the grid
- -- if the column is from the criss-cross grid
- --if (iz - oz + radius) % (2*radius) == 0 then
- if (iz - oz + radius) % (radius) == 0 then --makes a normal grid with step radius
- --place a chunk loader
- place = true
- end
- end
- if place then
- -- do the actual placing of hte chunkloader
- local wx = ix*16 -- the world X coordinate of the chunkloader
- local wy = y -- the world Y coordinate of the chunkloader
- local wz = iz*16 -- the world Zcoordinate of the chunkloader
- if remove then
- --remove chunkloaders in the area
- print("removing chunkloader",wx,wy,wz)
- commands.setblock(wx, wy, wz, "minecraft:air", 0, "replace")
- else
- print("adding chunkloader",wx,wy,wz)
- --add chunkloaders in the area
- commands.setblock(wx, wy, wz, "neotech:chunkLoader", 0, "replace", '{id:"neotech:chunkLoader", Diameter:'..(radius+1)..'}')
- --commands.setblock(wx, wy, wz, "minecraft:wool", 0, "replace") -- just for testing
- end
- --sleep(0.1)
- end
- end
- end
- end
- end
Add Comment
Please, Sign In to add comment