From 08a31897809e50ffdff887c981f4edaae2c56a65 Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Thu, 20 Feb 2025 00:06:43 -0500 Subject: [PATCH] Start supporting less accurate pitches Fix secondsSinceRunnerLastMove nil bug --- src/baserunning.lua | 2 +- src/main.lua | 23 ++++++++++++----------- src/npc.lua | 1 + src/pitching.lua | 40 +++++++++++++++++++++++++++++----------- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/baserunning.lua b/src/baserunning.lua index 7f26082..ebb9168 100644 --- a/src/baserunning.lua +++ b/src/baserunning.lua @@ -264,7 +264,7 @@ function Baserunning:updateNonBatterRunners(appliedSpeed, forcedOnly, deltaSecon self.secondsSinceLastRunnerMove = 0 self:updateForcedRunners() else - self.secondsSinceLastRunnerMove = self.secondsSinceLastRunnerMove + deltaSeconds + self.secondsSinceLastRunnerMove = (self.secondsSinceLastRunnerMove or 0) + deltaSeconds end return runnersStillMoving, runnersScored, self.secondsSinceLastRunnerMove diff --git a/src/main.lua b/src/main.lua index 467ac82..9da90a6 100644 --- a/src/main.lua +++ b/src/main.lua @@ -196,13 +196,14 @@ end ---@param pitchFlyTimeMs number | nil ---@param pitchTypeIndex number | nil -function Game:pitch(pitchFlyTimeMs, pitchTypeIndex) +---@param accuracy number The closer to 1.0, the better +function Game:pitch(pitchFlyTimeMs, pitchTypeIndex, accuracy) self.state.ball:markUncatchable() self.state.ball.heldBy = nil self.state.pitchIsOver = false self.state.offenseState = C.Offense.batting - local current = Pitches[pitchTypeIndex](self.state.ball) + local current = Pitches[pitchTypeIndex](accuracy, self.state.ball) self.state.ball.xAnimator = current.x self.state.ball.yAnimator = current.y or Pitches[1](self.state.ball).y @@ -450,17 +451,17 @@ function Game:returnToPitcher() end ---@param throwFly number -function Game:userPitch(throwFly) +function Game:userPitch(throwFly, accuracy) local aPressed = playdate.buttonIsPressed(playdate.kButtonA) local bPressed = playdate.buttonIsPressed(playdate.kButtonB) if not aPressed and not bPressed then - self:pitch(throwFly, 1) + self:pitch(throwFly, 1, accuracy) elseif aPressed and not bPressed then - self:pitch(throwFly, 2) + self:pitch(throwFly, 2, accuracy) elseif not aPressed and bPressed then - self:pitch(throwFly, 3) + self:pitch(throwFly, 3, accuracy) elseif aPressed and bPressed then - self:pitch(throwFly, 4) + self:pitch(throwFly, 4, accuracy) end end @@ -519,15 +520,15 @@ function Game:updateGameState() self:updateBatting(self.state.batAngleDeg, batSpeed) -- Walk batter to the plate - self.baserunning:updateRunner(self.baserunning.batter, nil, crankLimited, self.state.deltaSeconds) + self.baserunning:updateRunner(self.baserunning.batter, nil, userOnOffense and crankLimited or 0, self.state.deltaSeconds) if pitchTracker.secondsSinceLastPitch > C.PitchAfterSeconds then if userOnDefense then - local powerRatio, isPerfect = throwMeter:readThrow(crankChange) + local powerRatio, accuracy, isPerfect = throwMeter:readThrow(crankChange) if powerRatio then local throwFly = C.PitchFlyMs / powerRatio if throwFly and not self:buttonControlledThrow(throwFly, true) then - self:userPitch(throwFly) + self:userPitch(throwFly, accuracy) end end else @@ -547,7 +548,7 @@ function Game:updateGameState() end if userOnDefense then - local powerRatio, isPerfect = throwMeter:readThrow(crankChange) + local powerRatio, accuracy, isPerfect = throwMeter:readThrow(crankChange) if powerRatio then local throwFly = C.PitchFlyMs / powerRatio if throwFly then diff --git a/src/npc.lua b/src/npc.lua index 6c657c3..921c4a4 100644 --- a/src/npc.lua +++ b/src/npc.lua @@ -18,6 +18,7 @@ function Npc.new(runners, fielders) }, { __index = Npc }) end +-- TODO: FAR more nuanced NPC batting. ---@param ball XyPair ---@param pitchIsOver boolean ---@param deltaSec number diff --git a/src/pitching.lua b/src/pitching.lua index 449aaf2..0ee818f 100644 --- a/src/pitching.lua +++ b/src/pitching.lua @@ -4,35 +4,47 @@ ---@type pd_graphics_lib local gfx = playdate.graphics +local StrikeZoneWidth = C.StrikeZoneEndX - C.StrikeZoneStartX + +-- TODO? Also degrade speed +function getPitchMissBy(accuracy) + accuracy = accuracy or 1.0 + local missBy = (1 - accuracy) * StrikeZoneWidth * 3 + if math.random() > 0.5 then + missBy = missBy * -1 + end + return missBy +end + ---@type Pitch[] Pitches = { -- Fastball - function() + function(accuracy) return { - x = gfx.animator.new(0, C.PitchStart.x, C.PitchStart.x, playdate.easingFunctions.linear), + x = gfx.animator.new(0, C.PitchStart.x, getPitchMissBy(accuracy) + C.PitchStart.x, playdate.easingFunctions.linear), y = gfx.animator.new(C.PitchFlyMs / 1.3, C.PitchStart.y, C.PitchEndY, playdate.easingFunctions.linear), } end, -- Curve ball - function() + function(accuracy) return { - x = gfx.animator.new(C.PitchFlyMs, C.PitchStart.x + 20, C.PitchStart.x, utils.easingHill), + x = gfx.animator.new(C.PitchFlyMs, getPitchMissBy(accuracy) + C.PitchStart.x + 20, C.PitchStart.x, utils.easingHill), y = gfx.animator.new(C.PitchFlyMs, C.PitchStart.y, C.PitchEndY, playdate.easingFunctions.linear), } end, -- Slider - function() + function(accuracy) return { - x = gfx.animator.new(C.PitchFlyMs, C.PitchStart.x - 20, C.PitchStart.x, utils.easingHill), + x = gfx.animator.new(C.PitchFlyMs, getPitchMissBy(accuracy) + C.PitchStart.x - 20, C.PitchStart.x, utils.easingHill), y = gfx.animator.new(C.PitchFlyMs, C.PitchStart.y, C.PitchEndY, playdate.easingFunctions.linear), } end, -- Wobbbleball - function(ball) + function(accuracy, ball) return { x = { currentValue = function() - return C.PitchStart.x + (10 * math.sin((ball.yAnimator:currentValue() - C.PitchStart.y) / 10)) + return getPitchMissBy(accuracy) + C.PitchStart.x + (10 * math.sin((ball.yAnimator:currentValue() - C.PitchStart.y) / 10)) end, reset = function() end, }, @@ -117,14 +129,20 @@ local crankQueue = {} --- Returns nil when a throw is NOT requested. ---@param chargeAmount number ----@return number | nil powerRatio, boolean isPerfect +---@return number | nil powerRatio, number | nil accuracy, boolean isPerfect function throwMeter:readThrow(chargeAmount) local ret = self:applyCharge(chargeAmount) if ret then local ratio = ret / self.idealPower - return ratio, math.abs(ratio - 1) < 0.05 + local accuracy + if ratio >= 1 then + accuracy = 1 / ratio / 2 + else + accuracy = 1 -- Accuracy is perfect on slow throws + end + return ratio, accuracy, math.abs(ratio - 1) < 0.05 end - return nil, false + return nil, nil, false end --- If (within approx. a third of a second) the crank has moved more than 45 degrees, call that a throw.