More type hints.
Also, move pointDirectlyUnderLine() to XyPair-based.
This commit is contained in:
parent
ddfdc8947a
commit
55a3a7b0ee
|
@ -26,6 +26,10 @@ function actionQueue:upsert(id, maxTimeMs, action)
|
|||
}
|
||||
end
|
||||
|
||||
--- The new action will not be added if an entry with the current id already exists in the queue.
|
||||
---@param id any
|
||||
---@param maxTimeMs number
|
||||
---@param action Action
|
||||
function actionQueue:newOnly(id, maxTimeMs, action)
|
||||
if self.queue[id] then
|
||||
return
|
||||
|
@ -38,6 +42,7 @@ end
|
|||
|
||||
--- Must be called on every playdate.update() to check for (and run) any waiting tasks.
|
||||
--- Actions that return NeedsMoreTime will not be removed from the queue unless they have expired.
|
||||
---@param deltaSeconds number
|
||||
function actionQueue:runWaiting(deltaSeconds)
|
||||
local currentTimeMs = playdate.getCurrentTimeMilliseconds()
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ function Announcer:say(text)
|
|||
end
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
function Announcer:draw(x, y)
|
||||
if #self.textQueue == 0 then
|
||||
return
|
||||
|
|
|
@ -21,8 +21,9 @@ Baserunning = {}
|
|||
-- TODO: Implement slides? Would require making fielders' gloves "real objects" whose state is tracked.
|
||||
|
||||
---@param announcer Announcer
|
||||
---@param onThirdOutCallback fun()
|
||||
---@return Baserunning
|
||||
function Baserunning.new(announcer, onThirdOut)
|
||||
function Baserunning.new(announcer, onThirdOutCallback)
|
||||
local o = setmetatable({
|
||||
runners = {},
|
||||
outRunners = {},
|
||||
|
@ -32,7 +33,7 @@ function Baserunning.new(announcer, onThirdOut)
|
|||
--- it seems sensible to store the value here.
|
||||
outs = 0,
|
||||
announcer = announcer,
|
||||
onThirdOut = onThirdOut,
|
||||
onThirdOut = onThirdOutCallback,
|
||||
}, { __index = Baserunning })
|
||||
|
||||
o:pushNewBatter()
|
||||
|
@ -133,6 +134,9 @@ function Baserunning:convertBatterToRunner()
|
|||
self.batter = nil -- Demote batter to a mere runner
|
||||
end
|
||||
|
||||
---@param deltaSeconds number
|
||||
---@param runner Runner
|
||||
---@return boolean isStillWalking
|
||||
local function walkWayOutRunner(deltaSeconds, runner)
|
||||
if runner.x < C.Screen.W + 50 and runner.y < C.Screen.H + 50 then
|
||||
runner.x = runner.x + (deltaSeconds * 25)
|
||||
|
@ -156,7 +160,7 @@ function Baserunning:walkAwayOutRunners(deltaSeconds)
|
|||
end
|
||||
end
|
||||
|
||||
---@return Runner
|
||||
---@return Runner theBatterPushed
|
||||
function Baserunning:pushNewBatter()
|
||||
local new = {
|
||||
-- imageSet = math.random() < C.WokeMeter and FemmeSet or MascSet, -- TODO? lol.
|
||||
|
@ -182,6 +186,7 @@ end
|
|||
---@param runner Runner | nil
|
||||
---@param runnerIndex number | nil May only be nil if runner == batter
|
||||
---@param appliedSpeed number
|
||||
---@param isAutoRun boolean
|
||||
---@param deltaSeconds number
|
||||
---@return boolean runnerMoved, boolean runnerScored
|
||||
function Baserunning:updateRunner(runner, runnerIndex, appliedSpeed, isAutoRun, deltaSeconds)
|
||||
|
@ -248,7 +253,9 @@ end
|
|||
--- Update non-batter runners.
|
||||
--- Returns true only if at least one of the given runners moved during this update
|
||||
---@param appliedSpeed number | fun(runner: Runner): number
|
||||
---@param forcedOnly boolean If true, only move forced runners (e.g. for a walk)
|
||||
---@param isAutoRun boolean If true, does not attempt to hug the bases
|
||||
---@param deltaSeconds number
|
||||
---@return boolean runnersStillMoving, number runnersScored, number secondsSinceLastMove
|
||||
function Baserunning:updateNonBatterRunners(appliedSpeed, forcedOnly, isAutoRun, deltaSeconds)
|
||||
local runnersStillMoving = false
|
||||
|
|
|
@ -52,7 +52,7 @@ local function detail(text)
|
|||
return { text = text, font = DetailFont }
|
||||
end
|
||||
|
||||
---@class
|
||||
---@class ControlScreen
|
||||
---@field sceneToReturnTo Scene
|
||||
---@field private renderedImage pd_image Static image doesn't need to be constantly re-rendered.
|
||||
ControlScreen = {}
|
||||
|
@ -81,6 +81,7 @@ local function draw()
|
|||
end
|
||||
|
||||
---@param sceneToReturnTo Scene
|
||||
---@return ControlScreen
|
||||
function ControlScreen.new(sceneToReturnTo)
|
||||
return setmetatable({
|
||||
sceneToReturnTo = sceneToReturnTo,
|
||||
|
|
|
@ -16,7 +16,8 @@ function dbg.label(value, name)
|
|||
return value
|
||||
end
|
||||
|
||||
-- Only works if called with the bases empty (i.e. the only runner should be the batter.
|
||||
--- Only works if called with the bases empty (i.e. the only runner should be the batter.
|
||||
---@param br Baserunning
|
||||
function dbg.loadTheBases(br)
|
||||
br:pushNewBatter()
|
||||
br:pushNewBatter()
|
||||
|
@ -73,6 +74,7 @@ local hitSamples = {
|
|||
},
|
||||
}
|
||||
|
||||
---@param inningCount number Number of innings to mock
|
||||
---@return Statistics
|
||||
function dbg.mockStatistics(inningCount)
|
||||
inningCount = inningCount or 9
|
||||
|
|
|
@ -41,6 +41,8 @@ local function startGame()
|
|||
MenuMusic:setPaused(true)
|
||||
end
|
||||
|
||||
---@param baseEaser EasingFunc
|
||||
---@return EasingFunc
|
||||
local function pausingEaser(baseEaser)
|
||||
--- t: elapsedTime
|
||||
--- d: duration
|
||||
|
@ -65,6 +67,7 @@ local animatorY = gfx.animator.new(2000, 60, 200, pausingEaser(utils.easingHill)
|
|||
animatorY.repeatCount = -1
|
||||
animatorY.reverses = true
|
||||
|
||||
---@type number
|
||||
local crankStartPos
|
||||
|
||||
---@generic T
|
||||
|
@ -76,6 +79,7 @@ local function arrayElementFromCrank(array, crankPosition)
|
|||
return array[i]
|
||||
end
|
||||
|
||||
---@type pd_image
|
||||
local currentLogo
|
||||
|
||||
--luacheck: ignore
|
||||
|
|
15
src/main.lua
15
src/main.lua
|
@ -406,15 +406,7 @@ function Game:updateBatting(offenseHandler)
|
|||
|
||||
local ballWasHit = batSpeed > 0
|
||||
and ball.y < 232
|
||||
and utils.pointDirectlyUnderLine(
|
||||
ball.x,
|
||||
ball.y,
|
||||
self.state.batBase.x,
|
||||
self.state.batBase.y,
|
||||
self.state.batTip.x,
|
||||
self.state.batTip.y,
|
||||
C.Screen.H
|
||||
)
|
||||
and utils.pointDirectlyUnderLine(ball, self.state.batBase, self.state.batTip, C.Screen.H)
|
||||
|
||||
if not ballWasHit then
|
||||
return
|
||||
|
@ -472,6 +464,8 @@ function Game:updateBatting(offenseHandler)
|
|||
end
|
||||
|
||||
---@param appliedSpeed number | fun(runner: Runner): number
|
||||
---@param forcedOnly boolean
|
||||
---@param isAutoRun boolean
|
||||
---@return boolean runnersStillMoving, number secondsSinceLastRunnerMove
|
||||
function Game:updateNonBatterRunners(appliedSpeed, forcedOnly, isAutoRun)
|
||||
local runnersStillMoving, runnersScored, secondsSinceLastRunnerMove =
|
||||
|
@ -649,10 +643,12 @@ function Game:update()
|
|||
if self.state.offenseState == C.Offense.batting then
|
||||
gfx.setLineWidth(7)
|
||||
gfx.drawLine(self.state.batBase.x, self.state.batBase.y, self.state.batTip.x, self.state.batTip.y)
|
||||
|
||||
gfx.setColor(gfx.kColorWhite)
|
||||
gfx.setLineCapStyle(gfx.kLineCapStyleRound)
|
||||
gfx.setLineWidth(3)
|
||||
gfx.drawLine(self.state.batBase.x, self.state.batBase.y, self.state.batTip.x, self.state.batTip.y)
|
||||
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
end
|
||||
|
||||
|
@ -681,6 +677,7 @@ function Game:update()
|
|||
if math.abs(offsetX) > 10 or math.abs(offsetY) > 10 then
|
||||
drawMinimap(self.baserunning.runners, self.fielding.fielders)
|
||||
end
|
||||
|
||||
local homeScore, awayScore = utils.totalScores(self.state.stats)
|
||||
drawScoreboard(
|
||||
0,
|
||||
|
|
|
@ -21,11 +21,11 @@ end
|
|||
function Npc.update() end
|
||||
|
||||
-- TODO: FAR more nuanced NPC batting.
|
||||
-- luacheck: no unused
|
||||
---@param ball XyPair
|
||||
---@param pitchIsOver boolean
|
||||
---@param deltaSec number
|
||||
---@return number batAngleDeg, number batSpeed
|
||||
-- luacheck: no unused
|
||||
function Npc:updateBat(ball, pitchIsOver, deltaSec)
|
||||
if not pitchIsOver and ball.y > 200 and ball.y < 230 and (ball.x < C.Center.x + 15) then
|
||||
npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed)
|
||||
|
@ -36,10 +36,12 @@ function Npc:updateBat(ball, pitchIsOver, deltaSec)
|
|||
return npcBatDeg, (self:batSpeed() * deltaSec)
|
||||
end
|
||||
|
||||
---@return number
|
||||
function Npc:batSpeed()
|
||||
return npcBatSpeed / 1.5
|
||||
end
|
||||
|
||||
---@return number flyTimeMs, number pitchId, number accuracy
|
||||
function Npc:pitch()
|
||||
return C.PitchFlyMs / self:pitchSpeed(), math.random(#Pitches), 0.9
|
||||
end
|
||||
|
|
|
@ -7,6 +7,8 @@ local gfx <const> = playdate.graphics
|
|||
local StrikeZoneWidth <const> = C.StrikeZoneEndX - C.StrikeZoneStartX
|
||||
|
||||
-- TODO? Also degrade speed
|
||||
---@param accuracy number
|
||||
---@return number xValueToMissBy
|
||||
function getPitchMissBy(accuracy)
|
||||
accuracy = accuracy or 1.0
|
||||
local missBy = (1 - accuracy) * StrikeZoneWidth * 3
|
||||
|
@ -71,6 +73,9 @@ Pitches = {
|
|||
end,
|
||||
}
|
||||
|
||||
---@alias PitchOutcome "StrikeOut" | "Walk"
|
||||
|
||||
---@type table<string, PitchOutcome>
|
||||
PitchOutcomes = {
|
||||
StrikeOut = "StrikeOut",
|
||||
Walk = "Walk",
|
||||
|
@ -93,6 +98,7 @@ function pitchTracker:reset()
|
|||
self.balls = 0
|
||||
end
|
||||
|
||||
---@param ball XyPair
|
||||
function pitchTracker:recordIfPassed(ball)
|
||||
if ball.y < C.StrikeZoneStartY then
|
||||
self.recordedPitchX = nil
|
||||
|
@ -103,6 +109,7 @@ end
|
|||
|
||||
---@param didSwing boolean
|
||||
---@param fieldingTeamInningData TeamInningData
|
||||
---@return PitchOutcome | nil
|
||||
function pitchTracker:updatePitchCounts(didSwing, fieldingTeamInningData)
|
||||
if not self.recordedPitchX then
|
||||
return
|
||||
|
@ -149,8 +156,6 @@ throwMeter = {
|
|||
wasPerfect = false,
|
||||
}
|
||||
|
||||
local crankQueue = {}
|
||||
|
||||
local MaxPowerRatio <const> = 1.5
|
||||
|
||||
--- Returns nil when a throw is NOT requested.
|
||||
|
@ -176,6 +181,11 @@ end
|
|||
|
||||
local CrankRecordSec <const> = 0.33
|
||||
|
||||
---@alias CrankQueueEntry { time: number, chargeAmount: number }
|
||||
|
||||
---@type CrankQueueEntry[]
|
||||
local crankQueue = {}
|
||||
|
||||
--- If (within approx. a third of a second) the crank has moved more than 45 degrees, call that a throw.
|
||||
---@param chargeAmount number
|
||||
---@return number | nil
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
-- + Batting average
|
||||
-- + Farthest hit ball
|
||||
|
||||
---@return TeamInningData
|
||||
local function newTeamInning()
|
||||
return {
|
||||
score = 0,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
---@field buttonControlledThrow: fun(throwFlyMs: number, forbidThrowHome: boolean): boolean didThrow
|
||||
UserInput = {}
|
||||
|
||||
---@return UserInput
|
||||
function UserInput.new(buttonControlledThrow)
|
||||
return setmetatable({
|
||||
buttonControlledThrow = buttonControlledThrow,
|
||||
|
@ -14,6 +15,7 @@ function UserInput:update()
|
|||
self.crankLimited = math.abs(crankLimited)
|
||||
end
|
||||
|
||||
---@return number batAngleDeg, number batSpeed
|
||||
function UserInput:updateBat()
|
||||
local batAngleDeg = (playdate.getCrankPosition() + C.CrankOffsetDeg) % 360
|
||||
local batSpeed = self.crankLimited
|
||||
|
@ -43,6 +45,7 @@ local function userPitch(throwFlyMs, accuracy)
|
|||
return nil, nil, nil
|
||||
end
|
||||
|
||||
---@return number | nil pitchFlyTimeMs, number | nil pitchTypeIndex, number | nil accuracy
|
||||
function UserInput:pitch()
|
||||
local powerRatio, accuracy = throwMeter:readThrow(self.crankChange)
|
||||
if powerRatio then
|
||||
|
|
|
@ -90,8 +90,12 @@ function utils.moveAtSpeed(mover, speed, target, tau)
|
|||
return true
|
||||
end
|
||||
|
||||
function utils.within(within, n1, n2)
|
||||
return math.abs(n1 - n2) < within
|
||||
---@param acceptableGap number
|
||||
---@param n1 number
|
||||
---@param n2 number
|
||||
---@return boolean n1 is within acceptableGap of n2
|
||||
function utils.within(acceptableGap, n1, n2)
|
||||
return math.abs(n1 - n2) < acceptableGap
|
||||
end
|
||||
|
||||
---@generic T
|
||||
|
@ -121,6 +125,10 @@ function utils.first(array, condition)
|
|||
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
|
||||
|
@ -137,6 +145,12 @@ function utils.distanceBetweenPoints(point1, point2)
|
|||
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
|
||||
|
@ -164,15 +178,19 @@ function utils.getRunnerWithNextBase(runners, base)
|
|||
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(pointX, pointY, lineX1, lineY1, lineX2, lineY2, bottomBound)
|
||||
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 pointX < lineX1 or pointX > lineX2 or pointY > bottomBound then
|
||||
if point.x < line1.x or point.x > line2.x or point.y > bottomBound then
|
||||
return false
|
||||
end
|
||||
|
||||
return utils.pointUnderLine(pointX, pointY, lineX1, lineY1, lineX2, lineY2)
|
||||
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.
|
||||
|
@ -192,6 +210,12 @@ function utils.pointIsSquarelyAboveLine(point, linePoints)
|
|||
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)
|
||||
|
@ -222,6 +246,7 @@ end
|
|||
---@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
|
||||
|
|
Loading…
Reference in New Issue