Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- This is an API to add much needed functions to the global libraries in
- -- ComputerCraft Lua. This code will only work in ComputerCraft.
- -- To get this program, run the following command:
- -- pastebin get Rac6Jxjg "/API/LibAppend.lua"
- -- This file must be saved in "/API/" in order for programs that use it to
- -- function properly.
- -- Add it to your program by including 'dofile("/API/LibAppend.lua")' or
- -- 'require("/API/LibAppend")' at the top of your code.
- -- This is so 'require' can be used, even if APIs are loaded with 'dofile'.
- require = dofile("/rom/modules/main/cc/require.lua").make(
- setmetatable({}, {__index = _ENV}),
- "/"
- )
- -- This loads the expect module into your program. I don't know why this isn't
- -- loaded into the global table by default.
- -- expect.expect(arg, value, ...) -> Throws an error if the 'value' isn't any of
- -- the given types. You can enter multiple type arguments after 'value'.
- -- expect.field(table, index, ...) -> Throws an error if a property of a table
- -- isn't any of the given types. 'index' must be a key string and not an index
- -- number. You can enter multiple type arguments after 'index'.
- -- expect.range(num, min, max) -> Thrown an error if a number is not between two
- -- values, inclusive.
- expect = require("cc.expect")
- -- Gets the "name" of the specified color.
- colors.name = function(color)
- local colorList = {
- ["0"] = "white",
- ["1"] = "orange",
- ["2"] = "magenta",
- ["3"] = "lightBlue",
- ["4"] = "yellow",
- ["5"] = "lime",
- ["6"] = "pink",
- ["7"] = "gray",
- ["8"] = "lightGray",
- ["9"] = "cyan",
- ["a"] = "purple",
- ["b"] = "blue",
- ["c"] = "brown",
- ["d"] = "green",
- ["e"] = "red",
- ["f"] = "black"
- }
- return colorList[colors.toBlit(expect.expect(1, color, "number"))]
- end
- -- Non-American version of colors.name().
- colours.name = function(colour)
- local name = colors.name(colour)
- if name == "gray" or name == "lightGray" then name = name:gsub("ray", "rey") end
- return name
- end
- math.round = function(n)
- return math.floor(n+0.5)
- end
- -- Correctly capitalize the first letter in each sentence in a string.
- string.cap = function(s)
- for a = 1, s:len() do
- if a == 1 or s:sub(a-2, a-1) == ". " or s:sub(a-2, a-1) == "? " or s:sub(a-2, a-1) == "! " then
- s = s:sub(1, a-1)..s:sub(a, a):upper()..s:sub(a+1, s:len())
- end
- end
- return s
- end
- -- Capitalizes the first letter in all words.
- string.capAll = function(s)
- for a = 1, s:len() do
- if a == 1 or s:sub(a-1, a-1) == " " then
- s = s:sub(1, a-1)..s:sub(a, a):upper()..s:sub(a+1, s:len())
- end
- end
- return s
- end
- string.concat = function(...)
- str = ""
- for _, v in ipairs({...}) do
- str = str .. tostring(v)
- end
- return str
- end
- -- Easily add padding to a string.
- -- 'str' is the string to add padding to.
- -- 'pad' is the string to use as padding.
- -- 's' adds padding to the start of str if the length of str is less than s.
- -- 'e' adds padding to the end of str if the length of str is less than e after adding s padding.
- string.pad = function(str, pad, s, e)
- str, pad = tostring(str), tostring(pad)
- s, e = tonumber(s) or 0, tonumber(e) or 0
- str = pad:rep(s-str:len()) .. str
- str = str .. pad:rep(e-str:len())
- return str
- end
- -- A single function option for swapping the values of two elements in a table.
- -- 't' is the table you want to change.
- -- 'i1' and 'i2' are the keys for the elements being swapped.
- -- The value of 't[i1]' is set to 't[i2]'.
- -- The value of 't[i2]' is set to 't[i1]'.
- table.swap = function(t, i1, i2)
- expect.expect(1, t, "table")
- expect.expect(2, i1, "number", "string")
- expect.expect(3, i2, "number", "string")
- e = t[i1]
- t[i1] = t[i2]
- t[i2] = e
- end
- -- Moves elements up or down through an ordered list.
- -- 't' is the table you want to change.
- -- 'i' is the index of the element being moved.
- -- 'n' is how many places the element will move.
- -- Only index numbers can be used for i. Strings will not work.
- -- To move elements with strings as keys, use 'table.swap'.
- table.shift = function(t, i, n)
- expect.expect(1, t, "table")
- expect.expect(2, i, "number")
- expect.expect(3, n, "number")
- repeat
- if n < 0 then
- table.swap(t, i, i-1)
- i = i - 1
- elseif n > 0 then
- table.swap(t, i, i+1)
- i = i + 1
- end
- until i == n
- return i
- end
- -- A combination of term.blit and print.
- term.printBlit = function(s, t, b)
- expect.expect(1, s, "string")
- expect.expect(2, t, "string")
- expect.expect(3, b, "string")
- if s:len() ~= t:len() or s:len() ~= b:len() then
- error("Arguments must be the same length", 2)
- end
- term.blit(s, t, b)
- print()
- end
- -- A combination of texutils.slowWrite and term.blit.
- textutils.slowBlit = function(s, t, b, d)
- d = d or 20
- expect.expect(1, s, "string")
- expect.expect(2, t, "string")
- expect.expect(3, b, "string")
- if string.len(s) ~= string.len(t) or string.len(s) ~= string.len(b) or string.len(t) ~= string.len(b) then
- error("Arguments #1-3 must be the same length ("..string.len(s)..", "..string.len(t)..", "..string.len(b)..")", 2)
- end
- expect.expect(4, d, "number")
- if d <= 0 then
- error("Text speed must be greater than 0", 2)
- end
- local lastSpace = 0
- local lastNL = 0
- local xPos, yPos = term.getCursorPos()
- local xSize, ySize = term.getSize()
- for i = 1, s:len() do
- if s:sub(i, i) == " " then
- lastSpace = i
- end
- if s:sub(i, i) == "\n" then
- lastNL = i
- lastSpace = i
- end
- if xPos+i-lastNL-1 > xSize then
- s = s:sub(1, lastSpace-1).."\n"..s:sub(lastSpace+1, s:len())
- lastNL = lastSpace
- xPos = 1
- end
- end
- local currentTextColor = term.getTextColor()
- local currentBackgroundColor = term.getBackgroundColor()
- for i = 1, s:len() do
- sleep(1/d)
- term.setTextColor(2^tonumber(t:sub(i, i), 16))
- term.setBackgroundColor(2^tonumber(b:sub(i, i), 16))
- write(s:sub(i, i))
- end
- term.setTextColor(currentTextColor)
- term.setBackgroundColor(currentBackgroundColor)
- end
- -- 'buffer = doubleBuffer.new(parent, ...)' creates double frame buffers and
- -- redirects the term to the first buffer.
- -- • 'parent' is the term that the buffer will draw to.
- -- • '...' is any windows that will be automatically parented to the buffers.
- -- 'buffer.swap()' redirects the from the current buffer to the other.
- -- 'buffer.resize()' resizes the buffers if the parent term size has changed.
- -- 'buffer.parent()' returns the parent term.
- -- 'buffer.reset()' redirects back to the parent term.
- doubleBuffer = {}
- doubleBuffer.new = function(parent, ...)
- local wins = {...}
- local oldTerm = expect.expect(1, parent, "table", "nil") or term.current()
- for k, v in pairs(term.native()) do
- if type(k) == "string" and type(v) == "function"
- and type(oldTerm[k]) ~= "function" then
- error("Redirect object is missing method " .. k .. ".", 2)
- end
- end
- local buffer = {
- [0] = window.create(oldTerm, 1, 1, 1, 1),
- [1] = window.create(oldTerm, 1, 1, 1, 1)
- }
- local win = 1
- local winRepos = function(p)
- for i, v in ipairs(wins) do
- local pos = vector.new(v.getPosition())
- local size = vector.new(v.getSize())
- v.reposition(pos.x, pos.y, size.x, size.y, p)
- end
- end
- swap = function()
- buffer[win].setVisible(true)
- win = 1 - win
- buffer[win].setVisible(false)
- winRepos(buffer[win])
- return term.redirect(buffer[win])
- end
- resize = function()
- local xSize, ySize = term.getSize()
- buffer[0].reposition(1, 1, xSize, ySize)
- buffer[1].reposition(1, 1, xSize, ySize)
- return xSize, ySize
- end
- parent = function()
- return oldTerm
- end
- reset = function()
- winRepos(oldTerm)
- return term.redirect(oldTerm)
- end
- resize()
- swap()
- return {
- swap = swap,
- resize = resize,
- parent = parent,
- reset = reset
- }
- end
- -- Converts decimial number to another base (binary, hexadecimal, etc.)
- tobase = function(n, b)
- local code = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" -- 2 - 36 values per digit
- local v = ""
- expect.expect(1, n, "number")
- expect.expect(2, b, "number")
- expect.range(b, 2, 36)
- repeat
- v = code:sub(n%b+1, n%b+1) .. v
- n = math.floor(n/b)
- until n == 0
- return v
- end
- -- This is the vector api completely rewritten to include 2D and 4D vectors, as
- -- well as error messages that make it easier to debug.
- -- A 2-dimensional vector with 'x' and 'y' values.
- -- A 3-dimensional vector with 'x', 'y' and 'z' values.
- -- A 4-dimensional vector with 'x', 'y', 'z', and 'w' values.
- vector = (function()
- local vect = {}
- local vectors = {[2] = {}, [3] = {}, [4] = {}}
- local checkValue = {}
- local err = function(v, t, e)
- if type(v) ~= t then error(e, 5) end
- end
- local checkVector = function(o, d)
- err(o, "table", "expected second operand to be a table (" .. d .. "D vector), got " .. type(o))
- end
- local checkField = function(n, m)
- err(n, "number", "expected '" .. m .. "' of second operand to be a number, got " .. type(n))
- end
- local checkNumber = function(n)
- err(n, "number", "expected second operand to be a number, got " .. type(n))
- end
- -- Checks operand values for errors.
- checkValue[2] = function(o)
- checkVector(o, 2)
- checkField(o.x, 'x')
- checkField(o.y, 'y')
- end
- checkValue[3] = function(o)
- checkVector(o, 3)
- checkField(o.x, 'x')
- checkField(o.y, 'y')
- checkField(o.z, 'z')
- end
- checkValue[4] = function(o)
- checkVector(o, 4)
- checkField(o.x, 'x')
- checkField(o.y, 'y')
- checkField(o.z, 'z')
- checkField(o.w, 'w')
- end
- -- Adds two vectors together.
- vectors[2].add = function(self, o)
- checkValue[2](o)
- return vect.new(
- self.x + o.x,
- self.y + o.y
- )
- end
- vectors[3].add = function(self, o)
- checkValue[3](o)
- return vect.new(
- self.x + o.x,
- self.y + o.y,
- self.z + o.z
- )
- end
- vectors[4].add = function(self, o)
- checkValue[4](o)
- return vect.new(
- self.x + o.x,
- self.y + o.y,
- self.z + o.z,
- self.w + o.w
- )
- end
- -- Subtracts one vector from another.
- vectors[2].sub = function(self, o)
- checkValue[2](o)
- return vect.new(
- self.x - o.x,
- self.y - o.y
- )
- end
- vectors[3].sub = function(self, o)
- checkValue[3](o)
- return vect.new(
- self.x - o.x,
- self.y - o.y,
- self.z - o.z
- )
- end
- vectors[4].sub = function(self, o)
- checkValue[4](o)
- return vect.new(
- self.x - o.x,
- self.y - o.y,
- self.z - o.z,
- self.w - o.w
- )
- end
- -- Multiplies a vector by a scalar value.
- vectors[2].mul = function(self, n)
- checkNumber(n)
- return vect.new(
- self.x * n,
- self.y * n
- )
- end
- vectors[3].mul = function(self, n)
- checkNumber(n)
- return vect.new(
- self.x * n,
- self.y * n,
- self.z * n
- )
- end
- vectors[4].mul = function(self, n)
- checkNumber(n)
- return vect.new(
- self.x * n,
- self.y * n,
- self.z * n,
- self.w * n
- )
- end
- -- Divides a vector by a scalar value.
- vectors[2].div = function(self, n)
- checkNumber(n)
- return vect.new(
- self.x / n,
- self.y / n
- )
- end
- vectors[3].div = function(self, n)
- checkNumber(n)
- return vect.new(
- self.x / n,
- self.y / n,
- self.z / n
- )
- end
- vectors[4].div = function(self, n)
- checkNumber(n)
- return vect.new(
- self.x / n,
- self.y / n,
- self.z / n,
- self.w / n
- )
- end
- -- Negates a vector.
- vectors[2].unm = function(self)
- return vect.new(
- -self.x,
- -self.y
- )
- end
- vectors[3].unm = function(self)
- return vect.new(
- -self.x,
- -self.y,
- -self.z
- )
- end
- vectors[4].unm = function(self)
- return vect.new(
- -self.x,
- -self.y,
- -self.z,
- -self.w
- )
- end
- -- Computes the dot product of two vectors.
- vectors[2].dot = function(self, o)
- checkValue[2](o)
- return self.x * o.x + self.y * o.y
- end
- vectors[3].dot = function(self, o)
- checkValue[3](o)
- return self.x * o.x + self.y * o.y + self.z * o.z
- end
- vectors[4].dot = function(self, o)
- checkValue[4](o)
- return self.x * o.x + self.y * o.y + self.z * o.z + self.w * o.w
- end
- -- Computes the cross product of two vectors.
- vectors[2].cross = function(self, o)
- checkValue[2](o)
- return vect.new(0, 0)
- end
- vectors[3].cross = function(self, o)
- checkValue[3](o)
- return vect.new(
- self.y * o.z - self.z * o.y,
- self.z * o.x - self.x * o.z,
- self.x * o.y - self.y * o.x
- )
- end
- vectors[4].cross = function(self, o)
- checkValue[4](o)
- local v = vectors[3].cross(self, o)
- return vect.new(v.x, v.y, v.z, 0)
- end
- -- Get the length (also called magnitude) of a vector.
- vectors[2].length = function(self)
- return math.sqrt(self.x ^ 2 + self.y ^ 2)
- end
- vectors[3].length = function(self)
- return math.sqrt(self.x ^ 2 + self.y ^ 2 + self.z ^ 2)
- end
- vectors[4].length = function(self)
- return math.sqrt(self.x ^ 2 + self.y ^ 2 + self.z ^ 2 + self.w ^ 2)
- end
- -- Computes a vector with the same direction, but of length 1.
- vectors[2].normalize = function(self) return self:mul(1 / self:length()) end
- vectors[3].normalize = function(self) return self:mul(1 / self:length()) end
- vectors[4].normalize = function(self) return self:mul(1 / self:length()) end
- -- Construct a vector with each dimension rounded to the nearest value.
- vectors[2].round = function(self, t)
- t = tonumber(t) or 1
- return vect.new(
- math.floor((self.x + t * 0.5) / t) * t,
- math.floor((self.y + t * 0.5) / t) * t
- )
- end
- vectors[3].round = function(self, t)
- t = tonumber(t) or 1
- return vect.new(
- math.floor((self.x + t * 0.5) / t) * t,
- math.floor((self.y + t * 0.5) / t) * t,
- math.floor((self.z + t * 0.5) / t) * t
- )
- end
- vectors[4].round = function(self, t)
- t = tonumber(t) or 1
- return vect.new(
- math.floor((self.x + t * 0.5) / t) * t,
- math.floor((self.y + t * 0.5) / t) * t,
- math.floor((self.z + t * 0.5) / t) * t,
- math.floor((self.w + t * 0.5) / t) * t
- )
- end
- -- Converts a vector into a string, for pretty printing.
- vectors[2].tostring = function(self)
- return self.x .. "," .. self.y
- end
- vectors[3].tostring = function(self)
- return self.x .. "," .. self.y .. "," .. self.z
- end
- vectors[4].tostring = function(self)
- return self.x .. "," .. self.y .. "," .. self.z .. "," .. self.w
- end
- -- Checks for equality between two vectors.
- vectors[2].eq = function(self, o)
- checkValue[2](o)
- return self.x == o.x and self.y == o.y
- end
- vectors[3].eq = function(self, o)
- checkValue[3](o)
- return self.x == o.x and self.y == o.y and self.z == o.z
- end
- vectors[4].eq = function(self, o)
- checkValue[4](o)
- return self.x == o.x and self.y == o.y and self.z == o.z and self.w == o.w
- end
- vect.new = function(x, y, z, w)
- x, y, z, w = tonumber(x) or 0, tonumber(y) or 0, tonumber(z), tonumber(w)
- local dim = 3
- if z == nil then dim = dim - 1
- elseif w ~= nil then dim = dim + 1
- end
- local main = vectors[dim]
- return setmetatable({x = x, y = y, z = z, w = w}, {
- __index = main,
- __add = main.add,
- __sub = main.sub,
- __mul = main.mul,
- __div = main.div,
- __unm = main.unm,
- __tostring = main.tostring,
- __eq = main.eq
- })
- end
- return {
- new = vect.new
- }
- end)()
- --[[ The smallest time that the 'sleep' function works is 1/20th of a second.
- That means programs can technically only work in 20 frames per second.
- However, stuff can be printed to the screen faster than that if the 'sleep'
- function is removed, but ComputerCraft programs can only go a little over 6
- seconds without sleeping before it throws the 'Too long without yielding' error.
- This 'wait' function is my first solution to that problem.
- This works like the sleep function, but instead of time, it counts ticks. It can
- even output a frame rate higher than what you would naturally see on your real
- world monitor, but it's not really meant to do that. Setting a tick rate that's
- too high will result in errors as detailed below.
- Use 'wait(n)' with a number argument to loop 'n' ticks per second.
- • This function will calculate and return the 'fps' value.
- • 20 is the default value for 'n' if no argument is given.
- • Enter 1-20 to sleep for '1/n' amount of time. You can also enter decimals
- between 0 and 1 or fractions to sleep for longer than 1 second.
- • Enter a number above 20 to set a tick rate faster than 20. Currently this is
- restricted to multiples of 20. This may be fixed in a future update.
- • Entering 0 will throw an out of range error.
- • Entering a value that is too high may result in the following:
- • You will get the 'Too long without yielding' error.
- • You will get calculation errors with 'fps' and 'frameTime'.
- • Your tick rate will become extremely unstable.
- • You will experience input lag.
- • Peformance varies depending on the speed of your real world CPU.
- Additional values can also be returned from this function's methods.
- • 'wait(n)' or 'wait.fps()' will return the current ticks per second.
- • 'wait.ticks()' will return the total number of ticks counted by the function.
- • 'wait.startTime()' will return the 'os.clock()' time that the program started.
- • 'wait.programClock()' will return the time since the program started.
- • 'wait.frameTime(real)' will return one of two values:
- • If 'real' is true or not nil, it will return the average time for 1 tick.
- • If 'real' is false or nil, it will return the same average time rounded to
- the nearest 1/20th of a second to adhere to ComputerCraft's timing system.
- The best way to use this function is with the parallel API.
- • Put 'wait(n)' and whatever else you want to run with it inside an infinite
- loop inside a function.
- • Add your created function to 'parallel.waitForAny' with another function
- that's taking user input and/or any other function you want to run with set
- tick rates.
- Run either of these programs for a proof of concept:
- https://pastebin.com/5pgLdX2D
- https://pastebin.com/U8DGAmie
- ]]
- wait = (function()
- local ticks = 0
- local startTime = os.clock()
- local frames = {os.clock()}
- local fps = 0
- return setmetatable({
- ticks = function() return ticks end,
- frameTime = function(real)
- local time = os.clock()-frames[1]
- if real then return time / fps
- else return math.round(time / fps * 20) / 20 end
- end,
- startTime = function() return startTime end,
- programClock = function() return os.clock() - startTime end,
- fps = function() return fps end
- }, {
- __call = function(self, n)
- n = tonumber(n) or 20
- if n <= 0 then error("Value out of range, must be greater than 0", 2) end
- ticks = ticks + 1
- frames[#frames+1] = os.clock()
- if n <= 20 then
- sleep(1/n)
- elseif ticks%math.round(n/20) == 0 then
- sleep(0.05)
- end
- while os.clock()-frames[1] > 1 and #frames > 1 do
- table.remove(frames, 1)
- end
- fps = #frames / (os.clock() - frames[1]) - (math.floor(n/20)-1)
- return fps
- end
- })
- end)()
- -- Check if a specific value is equal to any of multile values.
- OR = function(val, ...)
- local returnCode = false
- for _, v in ipairs({...}) do
- returnCode = returnCode or v == val
- end
- return returnCode
- end
- -- Check if a specific value is equal to all of multiple values.
- AND = function(val, ...)
- local returnCode = true
- for _, v in ipairs({...}) do
- returnCode = returnCode and v == val
- end
- return returnCode
- end
- -- Check if a number is between two numbers, inclusive or non-inclusive.
- isBetween = function(val, low, high, inclusive)
- expect.expect(1, val, "string", "number")
- expect.expect(2, low, "string", "number")
- expect.expect(3, high, "string", "number")
- expect.expect(4, inclusive, "nil", "boolean")
- if inclusive then return val >= low and val <= high
- else return val > low and val < high end
- end
- -- Freezes the program to check values for bug testing.
- -- The program will resume when any key is pressed.
- bugCheck = function(x, y, s)
- local oldX, oldY = term.getCursorPos()
- term.setCursorPos(x, y)
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.red)
- write(s)
- os.pullEvent("key")
- term.setCursorPos(oldX, oldY)
- end
- --[[ Changelog
- The changelog has been moved to its own file: https://pastebin.com/2cK0DcBk
- ]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement