286 lines
8.0 KiB
Lua
286 lines
8.0 KiB
Lua
-- luacheck no new globals
|
|
utils = {}
|
|
|
|
--- @alias XyPair {
|
|
--- x: number,
|
|
--- y: number,
|
|
--- }
|
|
|
|
--- @alias Point3d {
|
|
--- x: number,
|
|
--- y: number,
|
|
--- z: number,
|
|
--- }
|
|
|
|
local sqrt <const> = math.sqrt
|
|
|
|
function utils.easingHill(t, b, c, d)
|
|
c = c + 0.0 -- convert to float to prevent integer overflow
|
|
t = t / d
|
|
t = ((t * 2) - 1)
|
|
t = t * t
|
|
return (c * t) + b
|
|
end
|
|
|
|
--- @alias StaticAnimator {
|
|
--- currentValue: fun(self): number;
|
|
--- reset: fun(self, durationMs: number | nil);
|
|
--- ended: fun(self): boolean;
|
|
--- }
|
|
|
|
--- Build an "animator" whose `:currentValue()` always returns the given value.
|
|
--- Essentially an "empty object" pattern for initial object positions.
|
|
---@param value number
|
|
---@return StaticAnimator
|
|
function utils.staticAnimator(value)
|
|
return {
|
|
currentValue = function(_)
|
|
return value
|
|
end,
|
|
reset = function(_) end,
|
|
ended = function(_)
|
|
return true
|
|
end,
|
|
}
|
|
end
|
|
|
|
---@param x number
|
|
---@param y number
|
|
---@return XyPair
|
|
function utils.xy(x, y)
|
|
return {
|
|
x = x,
|
|
y = y,
|
|
}
|
|
end
|
|
|
|
--- Returns the normalized vector as two values, plus the distance between the given points.
|
|
---@param x1 number
|
|
---@param y1 number
|
|
---@param x2 number
|
|
---@param y2 number
|
|
---@return number x, number y, number distance
|
|
function utils.normalizeVector(x1, y1, x2, y2)
|
|
local distance, x, y = utils.distanceBetween(x1, y1, x2, y2)
|
|
return x / distance, y / distance, distance
|
|
end
|
|
|
|
--- Push the given obect at the given speed toward a target. Speed should be pre-multiplied by the frame's delta time.
|
|
--- Stops when within 1. Returns true only if the object did actually move.
|
|
---@param mover { x: number, y: number }
|
|
---@param speed number
|
|
---@param target { x: number, y: number }
|
|
---@param tau number | nil
|
|
---@return boolean isStillMoving
|
|
function utils.moveAtSpeed(mover, speed, target, tau)
|
|
local x, y, distance = utils.normalizeVector(mover.x, mover.y, target.x, target.y)
|
|
|
|
if distance == 0 then
|
|
return false
|
|
end
|
|
|
|
if distance > (tau or 1) then
|
|
mover.x = mover.x - (x * speed)
|
|
mover.y = mover.y - (y * speed)
|
|
else
|
|
mover.x = target.x
|
|
mover.y = target.y
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
---@generic T
|
|
---@param array T[]
|
|
---@param condition fun(T): boolean
|
|
---@return T[]
|
|
function utils.filter(array, condition)
|
|
local newArray = {}
|
|
for _, element in pairs(array) do
|
|
if condition(element) then
|
|
newArray[#newArray + 1] = element
|
|
end
|
|
end
|
|
return newArray
|
|
end
|
|
|
|
---@generic T
|
|
---@param array T[]
|
|
---@param condition fun(T): boolean
|
|
---@return T | nil
|
|
function utils.first(array, condition)
|
|
for _, element in ipairs(array) do
|
|
if condition(element) then
|
|
return element
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
---@param x1 number
|
|
---@param y1 number
|
|
---@param x2 number
|
|
---@param y2 number
|
|
---@return number distance, number x, number y
|
|
function utils.distanceBetween(x1, y1, x2, y2)
|
|
local x = x1 - x2
|
|
local y = y1 - y2
|
|
return sqrt((x * x) + (y * y)), x, y
|
|
end
|
|
|
|
---@param point1 XyPair
|
|
---@param point2 XyPair
|
|
---@return number distance, number x, number y
|
|
function utils.distanceBetweenPoints(point1, point2)
|
|
local x = point1.x - point2.x
|
|
local y = point1.y - point2.y
|
|
return sqrt((x * x) + (y * y)), x, y
|
|
end
|
|
|
|
---@param x1 number
|
|
---@param y1 number
|
|
---@param z1 number
|
|
---@param x2 number
|
|
---@param y2 number
|
|
---@param z2 number
|
|
---@return number distance, number x, number y, number z
|
|
function utils.distanceBetweenZ(x1, y1, z1, x2, y2, z2)
|
|
local x = x1 - x2
|
|
local y = y1 - y2
|
|
local z = z1 - z2
|
|
return sqrt((x * x) + (y * y) + (z * z)), x, y, z
|
|
end
|
|
|
|
--- Returns the base being touched by the player at (x,y), or nil, if no base is being touched
|
|
---@param x number
|
|
---@param y number
|
|
---@return Base | nil
|
|
function utils.isTouchingBase(x, y)
|
|
return utils.first(C.Bases, function(base)
|
|
return utils.distanceBetween(x, y, base.x, base.y) < C.BaseHitbox
|
|
end)
|
|
end
|
|
|
|
---@param base Base
|
|
---@return Runner | nil runner The runner whose next base matches the given base.
|
|
function utils.getRunnerWithNextBase(runners, base)
|
|
return utils.first(runners, function(runner)
|
|
return runner.nextBase == base
|
|
end)
|
|
end
|
|
|
|
--- Returns true only if the point is below the given line, within the x bounds of said line, and above the bottomBound.
|
|
---@param point XyPair
|
|
---@param line1 XyPair
|
|
---@param line2 XyPair
|
|
---@param bottomBound number
|
|
---@return boolean
|
|
function utils.pointDirectlyUnderLine(point, line1, line2, bottomBound)
|
|
-- This check currently assumes right-handedness.
|
|
-- I.e. it assumes the ball is to the right of batBaseX
|
|
if point.x < line1.x or point.x > line2.x or point.y > bottomBound then
|
|
return false
|
|
end
|
|
|
|
return utils.pointUnderLine(point.x, point.y, line1.x, line1.y, line2.x, line2.y)
|
|
end
|
|
|
|
--- Returns true if the given point is anywhere above the given line, with no upper bound.
|
|
--- This, used for home run calculations, does not *precesely* take into account balls that curve around the foul poles.
|
|
--- If left of first linePoint and above it, returns true. Similarly if right of the last linePoint.
|
|
---@param point XyPair
|
|
---@param linePoints XyPair[]
|
|
---@return boolean
|
|
function utils.pointIsAboveLine(point, linePoints)
|
|
if point.x < linePoints[1].x and point.y < linePoints[1].y then
|
|
return true
|
|
end
|
|
for i = 2, #linePoints do
|
|
local prev = linePoints[i - 1]
|
|
local next = linePoints[i]
|
|
if point.x >= prev.x and point.x <= next.x then
|
|
return not utils.pointUnderLine(point.x, point.y, prev.x, prev.y, next.x, next.y)
|
|
end
|
|
end
|
|
if point.x > linePoints[#linePoints].x and point.y < linePoints[#linePoints].y then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- Returns true only if the point is below the given line.
|
|
---@param pointX number
|
|
---@param pointY number
|
|
---@param lineX1 number
|
|
---@param lineY1 number
|
|
---@param lineX2 number
|
|
---@param lineY2 number
|
|
---@return boolean
|
|
function utils.pointUnderLine(pointX, pointY, lineX1, lineY1, lineX2, lineY2)
|
|
local m = (lineY2 - lineY1) / (lineX2 - lineX1)
|
|
|
|
-- y = mx + b
|
|
-- b = y1 - (m * x1)
|
|
local b = lineY1 - (m * lineX1)
|
|
local yOnLine = (m * pointX) + b
|
|
local yP = pointY
|
|
local yDelta = yOnLine - yP
|
|
|
|
return yDelta <= 0
|
|
end
|
|
|
|
--- Returns true if a ball landing at destX,destY will be foul.
|
|
---@param destX number
|
|
---@param destY number
|
|
function utils.isFoulBall(destX, destY)
|
|
local leftLine = C.LeftFoulLine
|
|
local rightLine = C.RightFoulLine
|
|
|
|
return utils.pointUnderLine(destX, destY, leftLine.x1, leftLine.y1, leftLine.x2, leftLine.y2)
|
|
or utils.pointUnderLine(destX, destY, rightLine.x1, rightLine.y1, rightLine.x2, rightLine.y2)
|
|
end
|
|
|
|
--- Returns the nearest position object from the given point, as well as its distance from that point
|
|
---@generic T : { x: number, y: number | nil }
|
|
---@param array T[]
|
|
---@param x number
|
|
---@param y number
|
|
---@param extraCondition fun(t: T): boolean
|
|
---@return T nearest,number |nil distance
|
|
function utils.getNearestOf(array, x, y, extraCondition)
|
|
local nearest, nearestDistance = nil, nil
|
|
for _, element in pairs(array) do
|
|
if not extraCondition or extraCondition(element) then
|
|
if nearest == nil then
|
|
nearest = element
|
|
nearestDistance = utils.distanceBetween(element.x, element.y, x, y)
|
|
else
|
|
local distance = utils.distanceBetween(element.x, element.y, x, y)
|
|
if distance < nearestDistance then
|
|
nearest = element
|
|
nearestDistance = distance
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return nearest, nearestDistance
|
|
end
|
|
|
|
---@param stats Statistics
|
|
---@return number homeScore, number awayScore
|
|
function utils.totalScores(stats)
|
|
local homeScore = 0
|
|
local awayScore = 0
|
|
for _, inning in pairs(stats.innings) do
|
|
homeScore = homeScore + inning.home.score
|
|
awayScore = awayScore + inning.away.score
|
|
end
|
|
|
|
return homeScore, awayScore
|
|
end
|
|
|
|
if not playdate then
|
|
return utils
|
|
end
|