Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -----------------------------------
- -- Build API by Matthew Cenance. --
- -----------------------------------
- -- Requires a Command Computer to function.
- if not commands then
- error("The Build API can only be used on a Command Computer.")
- end
- local API = {}
- local errorIfValueTypeIsInvalid = function(value, typesAllowed, variableNum, errLevel)
- if type(typesAllowed) == "table" then
- for key, validType in ipairs(typesAllowed) do
- if type(value) == validType then
- return true
- end
- end
- error("bad argument #"..variableNum..": "..table.concat(typesAllowed, " or ").." expected, got "..type(value), errLevel or 3)
- end
- if type(value) ~= typesAllowed then
- error("bad argument #"..variableNum..": "..typesAllowed.." expected, got "..type(value), errLevel or 3)
- end
- end
- -- Metatable for the block table.
- local AllowAnyMixedCasing = {
- __index = function(self, key)
- if type(key) == "string" then
- if rawget(self, key:lower()) then
- return rawget(self, key:lower())
- end
- end
- end
- }
- -- Blocks table, which contains strings for various types of blocks.
- local blocks = setmetatable({
- air = "minecraft:air",
- stone = "minecraft:stone",
- grass = "minecraft:grass",
- wool = "minecraft:wool",
- stainedclay = "minecraft:stained_hardened_clay",
- blockofredstone = "minecraft:redstone_block",
- redstoneblock = "minecraft:redstone_block",
- }, AllowAnyMixedCasing)
- -- Structure API. --
- local Structure = {}
- Structure.__index = Structure
- -- Creates a new Structure object with the selected dimensions.
- -- Arguments:
- -- width, height, and depth are the dimensions of the Structure.
- function Structure.new(width, height, depth)
- -- Create a new Structure object.
- local self = setmetatable({}, Structure)
- self.data = {{{1, 0}}}
- self.numberToTilename = {"minecraft:air"}
- self.tilenameToNumber = {["minecraft:air"] = 1}
- -- Create an empty data table.
- for y = 1, height do
- self.data[y] = {{}}
- for x = 1, width do
- self.data[y][x] = {{1, 0}}
- for z = 1, depth do
- self.data[y][x][z] = {1, 0}
- end
- end
- end
- -- Set the width, height, and depth of the Structure, and mark the loading as finished.
- self.loadFinished = true
- self.width = width
- self.height = height
- self.depth = depth
- return self
- end
- -- Creates a new Structure object based off of a region of the Terrain.
- -- Arguments:
- -- x1, y1, z1 is the first corner of the region.
- -- x2, y2, z2 is the second corner of the region.
- function Structure.loadFromWorld(x1, y1, z1, x2, y2, z2)
- -- Error checking.
- errorIfValueTypeIsInvalid(x1, "number", 1)
- errorIfValueTypeIsInvalid(y1, "number", 2)
- errorIfValueTypeIsInvalid(z1, "number", 3)
- errorIfValueTypeIsInvalid(x2, "number", 4)
- errorIfValueTypeIsInvalid(y2, "number", 5)
- errorIfValueTypeIsInvalid(z2, "number", 6)
- -- Create a new Structure object.
- local self = setmetatable({}, Structure)
- self.data = {{{}}}
- self.numberToTilename = {"minecraft:air"}
- self.tilenameToNumber = {["minecraft:air"] = 1}
- -- Create a coroutine table to hold all of our coroutines.
- local coroutines = {}
- -- Create an empty data table, and also create coroutines that will obtain the block info from the world Terrain.
- for y = 1, y2 - y1 do
- self.data[y] = {}
- for x = 1, x2 - x1 do
- self.data[y][x] = {}
- for z = 1, z2 - z1 do
- self.data[y][x][z] = {1, 0}
- table.insert(coroutines, function()
- local value = commands.getBlockInfo(x + x1, y + y1, z + z1)
- self:BuildBlock(x, y, z, value.name, value.metadata or 0)
- end)
- end
- end
- end
- -- Start pulling information from the world.
- parallel.waitForAll(unpack(coroutines))
- -- Set the width, height, and depth of the Structure, and mark the loading as finished.
- self.loadFinished = true
- self.width = x2 - x1
- self.height = y2 - y1
- self.depth = z2 - z1
- return self
- end
- local FileIdentifier = "error('This is a Model and cannot be run.', 0)"
- -- Loads a structure from a path.
- -- Arguments:
- -- path is the location of the file you want to load.
- function Structure.load(path)
- -- Error checking.
- errorIfValueTypeIsInvalid(path, "string", 1)
- -- Create a new Structure object.
- local self = setmetatable({}, Structure)
- self.numberToTilename = {"minecraft:air"}
- self.tilenameToNumber = {["minecraft:air"] = 1}
- local file = fs.open(path, "r")
- local width, height, depth
- -- Attempt to load the structure from a file.
- if file then
- local str = file.readLine()
- if str ~= FileIdentifier then
- error(path.." is not a Model file.", 2)
- end
- -- Load the model tilename table.
- local sLine = file.readLine()
- while sLine do
- if sLine == "-- Start of Structure Data --" then
- break
- end
- local id = #self.numberToTilename + 1
- self.numberToTilename[id] = sLine
- self.tilenameToNumber[sLine] = id
- sLine = file.readLine()
- end
- -- Load the model data.
- sLine = file.readLine()
- local myData = {}
- local curY, curX = 0, 0
- while sLine do
- if sLine:find("Table") then
- curY = curY + 1
- width = curX
- curX = 0
- myData[curY] = {}
- else
- curX = curX + 1
- local sLine2 = file.readLine()
- myData[curY][curX] = {}
- local values1 = {}
- local values2 = {}
- for value in string.gmatch(sLine, "%d+") do
- values1[#values1 + 1] = tonumber(value)
- end
- for value in string.gmatch(sLine2, "%d+") do
- values2[#values2 + 1] = tonumber(value)
- end
- for key, value in ipairs(values1) do
- myData[curY][curX][key] = {value, values2[key]}
- end
- depth = #values1
- end
- sLine = file.readLine()
- end
- self.data = myData
- height = #myData
- file.close()
- else
- error(path.." is not a valid path or file name.", 2)
- end
- -- Set the width, height, and depth of the Structure, and mark the loading as finished.
- self.loadFinished = true
- self.width = width
- self.height = height
- self.depth = depth
- return self
- end
- -- Saves the structure to a file.
- -- Arguments:
- -- path is the location of the new file.
- function Structure:Save(path)
- local file = fs.open(path, "w")
- if file then
- file.writeLine(FileIdentifier)
- for key = 2, #self.numberToTilename do
- file.writeLine(self.numberToTilename[key])
- end
- file.writeLine("-- Start of Structure Data --")
- for y, yTable in ipairs(self.data) do
- file.writeLine("Table "..y)
- for x, xTable in ipairs(yTable) do
- local str = ""
- local str2 = ""
- for z, zBlock in ipairs(xTable) do
- str = str..zBlock[1].." "
- str2 = str2..zBlock[2].." "
- end
- file.writeLine(str)
- file.writeLine(str2)
- end
- end
- file.close()
- return true
- end
- error(path.." is not a valid path or file name.", 2)
- end
- -- Sets a block in the Structure to the selected tilename and data string.
- -- Arguments:
- -- x, y, z is the absolute position you want to place the block.
- -- tilename is the tilename of the block you want to use.
- -- data is the metadata number of the block.
- function Structure:BuildBlock(x, y, z, tilename, data)
- -- Error checking.
- errorIfValueTypeIsInvalid(x, "number", 1)
- errorIfValueTypeIsInvalid(y, "number", 2)
- errorIfValueTypeIsInvalid(z, "number", 3)
- errorIfValueTypeIsInvalid(tilename, "string", 4)
- if type(tonumber(data)) ~= "number" and type(data) ~= "nil" then
- error("bad argument #8: number expected, got "..type(data), 2)
- end
- data = tonumber(data)
- -- If the tilename has not been used before, add it to the tilename table.
- if not self.tilenameToNumber[tilename] then
- local id = #self.numberToTilename + 1
- self.numberToTilename[id] = tilename
- self.tilenameToNumber[tilename] = id
- end
- -- Set the block.
- self.data[y][x][z] = {self.tilenameToNumber[tilename], data or 0}
- end
- -- Fills a 3D region of the structure with the selected block.
- -- Arguments:
- -- x1, y1, z1 is the first corner of the region.
- -- x2, y2, z2 is the second corner of the region.
- -- tilename is the tilename of the block you want to use.
- -- data is the metadata number of the block.
- function Structure:BuildBlocksInRegion(x1, y1, z1, x2, y2, z2, tilename, data)
- -- Error checking.
- errorIfValueTypeIsInvalid(x1, "number", 1)
- errorIfValueTypeIsInvalid(y1, "number", 2)
- errorIfValueTypeIsInvalid(z1, "number", 3)
- errorIfValueTypeIsInvalid(x2, "number", 4)
- errorIfValueTypeIsInvalid(y2, "number", 5)
- errorIfValueTypeIsInvalid(z2, "number", 6)
- errorIfValueTypeIsInvalid(tilename, "string", 7)
- if type(tonumber(data)) ~= "number" and type(data) ~= "nil" then
- error("bad argument #8: number expected, got "..type(data), 2)
- end
- data = tonumber(data)
- -- If the tilename has not been used before, add it to the tilename table.
- if not self.tilenameToNumber[tilename] then
- local id = #self.numberToTilename + 1
- self.numberToTilename[id] = tilename
- self.tilenameToNumber[tilename] = id
- end
- local id = self.tilenameToNumber[tilename]
- for y = y1, y2 do
- for x = x1, x2 do
- for z = z1, z2 do
- self.data[y][x][z] = {id, data or 0}
- end
- end
- end
- end
- -- Pastes another Structure to this Structure.
- -- Arguments:
- -- structure2 is the Structure with the contents you want to paste.
- -- x, y, z is the position you want to paste the other Structure.
- -- placeAirBlocks sets if Air blocks in the other Structure will overwrite and remove blocks in this Structure.
- function Structure:PasteOtherStructure(structure2, xP, yP, zP, placeAirBlocks)
- -- Error checking.
- errorIfValueTypeIsInvalid(xP, "number", 2)
- errorIfValueTypeIsInvalid(yP, "number", 3)
- errorIfValueTypeIsInvalid(zP, "number", 4)
- -- Paste the other structure.
- local blocksChanged = 0
- for y = 1, self.height do
- for x = 1, self.width do
- for z = 1, self.depth do
- if structure2.data[y][x][z][1] ~= 1 or placeAirBlocks then
- blocksChanged = blocksChanged + 1
- self:BuildBlock(x + xP, y + yP, z + zP, structure2.numberToTilename[structure2.data[y][x][z][1]], structure2.data[y][x][z][2])
- end
- end
- end
- end
- return blocksChanged
- end
- -- Pastes the contents of the Structure to the world.
- -- Arguments:
- -- x, y, z is the position you want to place the Structure.
- -- placeAirBlocks sets if Air blocks in the Structure will overwrite and remove blocks in the world.
- -- Returns the number of blocks changed in the world.
- function Structure:PlaceInWorld(xP, yP, zP, placeAirBlocks)
- -- Error checking.
- errorIfValueTypeIsInvalid(xP, "number", 1)
- errorIfValueTypeIsInvalid(yP, "number", 2)
- errorIfValueTypeIsInvalid(zP, "number", 3)
- -- Since this will set a large amount of blocks, disable the output until this operation completes.
- local v, str = commands.exec("gamerule commandBlockOutput")
- str = str[1]
- local commandBlockOutput = (str:sub(#("commandBlockOutput = ") + 1) == "true")
- commands.exec("gamerule commandBlockOutput false")
- -- Set all blocks in the Structure.
- local blocksChanged = 0
- for y = 1, self.height do
- for x = 1, self.width do
- for z = 1, self.depth do
- if self.data[y][x][z][1] ~= 1 or placeAirBlocks then
- blocksChanged = blocksChanged + 1
- API.buildBlock(x + xP - math.floor(self.width*.5), y + yP, z + zP - math.floor(self.depth*.5), self.numberToTilename[self.data[y][x][z][1]], self.data[y][x][z][2])
- end
- end
- end
- end
- -- Reset the commandBlockOutput if it was already set.
- if commandBlockOutput then
- sleep(math.ceil(blocksChanged/1000)/20)
- commands.exec("say "..blocksChanged.." blocks changed.")
- commands.exec("gamerule commandBlockOutput "..tostring(commandBlockOutput))
- end
- return blocksChanged
- end
- -- Build API. --
- -- Returns an absolute world position from a position relative to the center of the Command Computer.
- -- Arguments:
- -- x, y, z is the position relative to the center of the Command Computer.
- -- All three variables are optional and can be set to nil.
- -- Returns a position relative to the center of the world.
- function API.getAbsolutePosition(x, y, z)
- x, y, z = x or 0, y or 0, z or 0
- local myX, myY, myZ = commands.getBlockPosition()
- return x + myX, y + myY, z + myZ
- end
- -- Returns a relative position from a position that is relative to the center of the world.
- -- Arguments:
- -- x, y, z is the position relative to the center of the world.
- -- All three variables are optional and can be set to nil.
- -- Returns a position relative to the center of the Command Computer.
- function API.getRelativePosition(x, y, z)
- x, y, z = x or 0, y or 0, z or 0
- local myX, myY, myZ = commands.getBlockPosition()
- return x - myX, y + myY, z + myZ
- end
- -- Sets a block in the world to a selected tilename and metadata value.
- -- Arguments:
- -- x, y, z is the position you want to place the block.
- -- tilename is the tilename of the block you want to use.
- -- data is the metadata number of the block.
- function API.buildBlock(x, y, z, tilename, data)
- -- Error checking.
- errorIfValueTypeIsInvalid(x, "number", 1)
- errorIfValueTypeIsInvalid(y, "number", 2)
- errorIfValueTypeIsInvalid(z, "number", 3)
- errorIfValueTypeIsInvalid(tilename, "string", 4)
- -- Set the block.
- commands.execAsync("setblock "..x.." "..y.." "..z.." "..tilename.." "..(data or ""))
- end
- -- Fills a region of the world Terrain with the selected block.
- -- Arguments:
- -- x1, y1, z1 is the first corner of the region.
- -- x2, y2, z2 is the second corner of the region.
- -- tilename is the tilename of the block you want to use.
- -- data is the metadata number of the block.
- -- Returns the number of blocks changed in the world.
- function API.buildBlocksInRegion(x1, y1, z1, x2, y2, z2, tilename, data)
- -- Since this will set a large amount of blocks, disable the output until this operation completes.
- local v, str = commands.exec("gamerule commandBlockOutput")
- str = str[1]
- local commandBlockOutput = (str:sub(#("commandBlockOutput = ") + 1) == "true")
- commands.exec("gamerule commandBlockOutput false")
- -- Set all blocks in the Structure.
- local blocksChanged = 0
- for y = y1, y2 do
- for x = x1, x2 do
- for z = z1, z2 do
- blocksChanged = blocksChanged + 1
- API.buildBlock(x, y, z, tilename, data)
- end
- end
- end
- -- Reset the commandBlockOutput if it was already set.
- if commandBlockOutput then
- sleep(math.ceil(blocksChanged/1000)/20)
- commands.exec("say "..blocksChanged.." blocks changed.")
- commands.exec("gamerule commandBlockOutput "..tostring(commandBlockOutput))
- end
- return blocksChanged
- end
- _G.blocks = blocks
- _G.Structure = Structure
- _G.build = API
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement