Effectively I just got rid of the methods like .add and stuff by using metatables and added a few other things. In my version, it is possible to directly add, subtract, multiply, or divide vectors without the usage of a method (If you use the vec2.new() function) vec2.new(10, 10) * vec2.new(2, 2) is acceptable. Shortcuts exist with multiply and divide: vec2.new(10, 10) * 2 --OR-- vec2.new(10, 10) * vec2.new(2, 2) Furthermore, there are really handy functions: You can find the angle (in radians) between two points, as well as the normal and magnitude. Even better, mine comes with a function to interpolate between two points, as well as the ability to print Vectors (using tostring())! Code: local start = Vector2.new(10, 10) local end = Vector2.new(20, 10) local point = start:lerp(end, 0.5) print(tostring(point)) > [15, 10] For those interested, you can see the source code at the bottom. Notice: This can NOT replace the already existing vec2 library because Starbound needs it the way it is internally! Use mine in a separate lua file. Code: local vec2 = {} function vec2.new(x, y) local x = x or 0 local y = y or 0 local v = { [1] = x; --First index x [2] = y; --Second index y X = x; --Uppercase X property Y = y; --Uppercase Y property x = x; --Lowercase x property y = y; --Lowercase y property } v.mt ={} --Our metatable setmetatable(v, v.mt) v.Add = function (a, b) if getmetatable(a) ~= "Vector2" or getmetatable(b) ~= "Vector2" then return vec2.new() end local x = a.X + b.X local y = a.Y + b.Y return vec2.new(x, y) end v.Subtract = function (a, b) if getmetatable(a) ~= "Vector2" or getmetatable(b) ~= "Vector2" then return vec2.new() end local x = a.X - b.X local y = a.Y - b.Y return vec2.new(x, y) end v.Multiply = function (a, b) if getmetatable(a) ~= "Vector2" or getmetatable(b) ~= "Vector2" then return vec2.new() end local x = a.X * b.X local y = a.Y * b.Y return vec2.new(x, y) end v.Divide = function (a, b) if getmetatable(a) ~= "Vector2" or getmetatable(b) ~= "Vector2" then return vec2.new() end local x = a.X / b.X local y = a.Y / b.Y return vec2.new(x, y) end v.Print = function (a) if getmetatable(a) ~= "Vector2" then return "[NOT A VECTOR]" end return "["..tostring(a.X)..", "..tostring(a.Y).."]" end -- v.Magnitude = function () local x = v.X local y = v.Y return math.sqrt((x^2)+(y^2)) end v.Unit = function () local x = v.X local y = v.Y local d = math.sqrt((x^2)+(y^2)) local dir = v / vec2.new(d, d) return dir end function v:lerp(a, frac) if getmetatable(a) ~= "Vector2" then return vec2.new() end local frac = frac or 0 if frac < 0 then frac = 0 elseif frac > 1 then frac = 1 end local a = (a - v) * vec2.new(frac, frac) return a + v end function v:toAngle(a) if getmetatable(a) ~= "Vector2" then return vec2.new() end return math.atan2(a[2] - v[2], a[1] - v[1]) - math.pi end v.mt.__add = v.Add v.mt.__sub = v.Subtract v.mt.__div = v.Divide v.mt.__mul = v.Multiply v.mt.__tostring = v.Print v.mt.__metatable = "Vector2" return v end --Some collision system just because Starbound has one in vec2 as well. function vec2.intersects(a, b, c, d) --[[ a = top left corner of rectangle 1 (as Vector2) b = rectangle 1 size (as Vector2) c = top left corner of rectangle 2 (as Vector2) d = rectangle 2 size (as Vector2) --]] local surface = "" local collide = false local x1 = a[1] local y1 = a[2] local x2 = c[1] local y2 = c[2] local w1 = b[1] local h1 = b[2] local w2 = d[1] local h2 = d[2] if x1 < x2 + w2 + 1 and --Right x1 + w1 > x2 - 1 and --Left y1 < y2 + h2 + 1 and y1 + h1 > y2 - 1 then collide = true if y1 + h1 > y2 - 1 and y1 + h1 < y2 then surface = "top" elseif y1 > y2 + h2 and y1 < y2 + h2 + 1 then surface = "bottom" end if x1 + w1 > x2 - 1 and x1 + w1 < x2 then surface = "left" elseif x1 > x2 + w2 and x1 < x2 + w2 + 1 then surface = "right" end --At this point we need to check if we have mixed surfaces (When this happens, we get stuck on corners) if y1 + h1 > y2 - 1 and y1 + h1 < y2 and x1 + w1 > x2 - 1 and x1 + w1 < x2 then surface = "topleft" elseif y1 + h1 > y2 - 1 and y1 + h1 < y2 and x1 > x2 + w2 and x1 < x2 + w2 + 1 then surface = "topright" elseif y1 > y2 + h2 and y1 < y2 + h2 + 1 and x1 + w1 > x2 - 1 and x1 + w1 < x2 then surface = "bottomleft" elseif y1 > y2 + h2 and y1 < y2 + h2 + 1 and x1 > x2 + w2 and x1 < x2 + w2 + 1 then surface = "bottomright" end end return collide, surface end return vec2

The reason I liked the vec2 library was it being easy to use it with return values from various Lua API functions, such as tech.aimPosition(). These functions return objects such as { 10, 20}. I believe this rewrite takes away the ability to pass the coordinates directly (as {x,y}), and instead requires users to manually pass those values. An example: Code: local aimPos = tech.aimPosition() local pos = mcontroller.position() local offset = vec2.new(aimPos[1], aimPos[2]) - vec2.new(pos[1], pos[2]) 'Old' code: Code: local offset = vec2.sub(tech.aimPosition(), mcontroller.position()) Although I really like the idea of using metatables to create extension methods, for me this additional step before being able to use vec2 methods sort of ruins it :/. Edit: my apologies, I didn't read your last note before looking at the code. Although I didn't factor in the idea to use this script besides the existing one, I'd love to see the functionality to pass {x,y} objects rather than numbers as separate values.

Yeah, I didn't factor that in, it seems! Unfortunately that may not work, since the vec2.new() function creates the metatable there, and manually creating it would be a useless hassle.

What I meant was parsing the arguments differently based on one or two being passed; vec2.new(x_or_point, y_or_nil). The function calls vec2.new(10,15) and vec2.new({10,15}) would both work. This would at least allow for vec2.new(tech.aimPosition()), rather than vec2.new(tech.aimPosition()[1], tech.aimPosition()[2]) or the earlier given example.