Pitcher can throw to bases.

Logic should be re-usable generally for throwing between fielders.
This commit is contained in:
Sage Vaillancourt 2025-02-05 23:42:17 -05:00
parent 57625a9b80
commit 8319b554ec
2 changed files with 81 additions and 40 deletions

View File

@ -94,16 +94,16 @@ local Pitches <const> = {
x = gfx.animator.new(0, PitchStartX, PitchStartX, playdate.easingFunctions.linear), x = gfx.animator.new(0, PitchStartX, PitchStartX, playdate.easingFunctions.linear),
y = gfx.animator.new(PitchFlyMs / 1.3, PitchStartY, PitchEndY, playdate.easingFunctions.linear), y = gfx.animator.new(PitchFlyMs / 1.3, PitchStartY, PitchEndY, playdate.easingFunctions.linear),
}, },
-- Slider
{
x = gfx.animator.new(PitchFlyMs, PitchStartX - 20, PitchStartX, utils.easingHill),
y = gfx.animator.new(PitchFlyMs, PitchStartY, PitchEndY, playdate.easingFunctions.linear),
},
-- Curve ball -- Curve ball
{ {
x = gfx.animator.new(PitchFlyMs, PitchStartX + 20, PitchStartX, utils.easingHill), x = gfx.animator.new(PitchFlyMs, PitchStartX + 20, PitchStartX, utils.easingHill),
y = gfx.animator.new(PitchFlyMs, PitchStartY, PitchEndY, playdate.easingFunctions.linear), y = gfx.animator.new(PitchFlyMs, PitchStartY, PitchEndY, playdate.easingFunctions.linear),
}, },
-- Slider
{
x = gfx.animator.new(PitchFlyMs, PitchStartX - 20, PitchStartX, utils.easingHill),
y = gfx.animator.new(PitchFlyMs, PitchStartY, PitchEndY, playdate.easingFunctions.linear),
},
-- Wobbbleball -- Wobbbleball
{ {
x = { x = {
@ -322,11 +322,12 @@ local secondsSincePitchAllowed = -5
local catcherThrownBall = false local catcherThrownBall = false
---@param pitchFlyTimeMs number | nil ---@param pitchFlyTimeMs number | nil
function pitch(pitchFlyTimeMs) ---@param pitchTypeIndex number | nil
function pitch(pitchFlyTimeMs, pitchTypeIndex)
catcherThrownBall = false catcherThrownBall = false
offenseMode = Offense.batting offenseMode = Offense.batting
local current = Pitches[math.random(#Pitches)] local current = Pitches[pitchTypeIndex or math.random(#Pitches)]
ballAnimatorX = current.x ballAnimatorX = current.x
ballAnimatorY = current.y or Pitches[1].y ballAnimatorY = current.y or Pitches[1].y
@ -467,12 +468,14 @@ end
function getBaseOfStrandedRunner() function getBaseOfStrandedRunner()
local farRunnersBase, farDistance local farRunnersBase, farDistance
for _, runner in pairs(runners) do for _, runner in pairs(runners) do
if runner ~= batter then
local nearestBase, distance = utils.getNearestOf(Bases, runner.x, runner.y) local nearestBase, distance = utils.getNearestOf(Bases, runner.x, runner.y)
if farRunnersBase == nil or farDistance < distance then if farRunnersBase == nil or farDistance < distance then
farRunnersBase = nearestBase farRunnersBase = nearestBase
farDistance = distance farDistance = distance
end end
end end
end
return farRunnersBase, farDistance return farRunnersBase, farDistance
end end
@ -721,10 +724,10 @@ function walkAwayOutRunners()
end end
local npcBatDeg = 0 local npcBatDeg = 0
local NpcBatSpeed <const> = 1200 local NpcBatSpeed <const> = 1500
function npcBatAngle() function npcBatAngle()
if not catcherThrownBall and ball.y > 190 and ball.y < 230 and (ball.x < Center.x + 15) then if not catcherThrownBall and ball.y > 200 and ball.y < 230 and (ball.x < Center.x + 15) then
npcBatDeg = npcBatDeg + (deltaSeconds * NpcBatSpeed) npcBatDeg = npcBatDeg + (deltaSeconds * NpcBatSpeed)
else else
npcBatDeg = 200 npcBatDeg = 200
@ -742,21 +745,51 @@ function npcRunningSpeed()
end end
local touchedBase = isTouchingBase(runners[1].x, runners[1].y) local touchedBase = isTouchingBase(runners[1].x, runners[1].y)
if not touchedBase or touchedBase == Bases[Home] then if not touchedBase or touchedBase == Bases[Home] then
return 35 return 25
end end
return 0 return 0
end end
local pitchMeter = 0 local throwMeter = 0
local PitchMeterLimit = 25 local PitchMeterLimit = 25
function readPitch() function readThrow()
if pitchMeter > PitchMeterLimit then if throwMeter > PitchMeterLimit then
return (PitchFlyMs / (pitchMeter / PitchMeterLimit)) return (PitchFlyMs / (throwMeter / PitchMeterLimit))
end end
return nil return nil
end end
---@param thrower Fielder
---@param throwFlyMs number
---@return boolean didThrow
function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome)
local targetBase
if playdate.buttonIsPressed(playdate.kButtonLeft) then
throwBall(Bases[Third].x, Bases[Third].y, playdate.easingFunctions.linear, throwFlyMs)
targetBase = Bases[Third]
elseif playdate.buttonIsPressed(playdate.kButtonUp) then
throwBall(Bases[Second].x, Bases[Second].y, playdate.easingFunctions.linear, throwFlyMs)
targetBase = Bases[Second] -- TODO - or shortstop - whoever's closer
elseif playdate.buttonIsPressed(playdate.kButtonRight) then
throwBall(Bases[First].x, Bases[First].y, playdate.easingFunctions.linear, throwFlyMs)
targetBase = Bases[First]
elseif not forbidThrowHome and playdate.buttonIsPressed(playdate.kButtonDown) then
throwBall(Bases[Home].x, Bases[Home].y, playdate.easingFunctions.linear, throwFlyMs)
targetBase = Bases[Home]
else
return false
end
local closestFielder = utils.getNearestOf(fielders, targetBase.x, targetBase.y, function(fielder)
return fielder ~= thrower
end)
closestFielder.target = targetBase
secondsSinceLastRunnerMove = 0
offenseMode = Offense.running
return true
end
function updateGameState() function updateGameState()
deltaSeconds = playdate.getElapsedTime() or 0 deltaSeconds = playdate.getElapsedTime() or 0
playdate.resetElapsedTime() playdate.resetElapsedTime()
@ -778,24 +811,31 @@ function updateGameState()
throwBall(PitchStartX, PitchStartY, playdate.easingFunctions.linear, nil, true) throwBall(PitchStartX, PitchStartY, playdate.easingFunctions.linear, nil, true)
catcherThrownBall = true catcherThrownBall = true
else else
pitchMeter = 0 throwMeter = 0
end end
if not playerOnOffense then if not playerOnOffense then
pitchMeter = math.max(0, pitchMeter - (deltaSeconds * 150)) throwMeter = math.max(0, throwMeter - (deltaSeconds * 150))
pitchMeter = pitchMeter + crankChange throwMeter = throwMeter + crankChange
if pitchMeter > PitchMeterLimit then
printTable({ pitchMeter = pitchMeter })
end
end end
if secondsSincePitchAllowed > PitchAfterSeconds then if secondsSincePitchAllowed > PitchAfterSeconds then
if playerOnOffense then if playerOnOffense then
pitch(PitchFlyMs) pitch(PitchFlyMs)
else else
local pitchFly = readPitch() local throwFly = readThrow()
if pitchFly then if throwFly and not buttonControlledThrow(fielders.pitcher, throwFly, true) then
pitch(pitchFly) local aButton = playdate.buttonIsPressed(playdate.kButtonA)
local bButton = playdate.buttonIsPressed(playdate.kButtonB)
if not aButton and not bButton then
pitch(throwFly, 1)
elseif aButton and not bButton then
pitch(throwFly, 2)
elseif not aButton and bButton then
pitch(throwFly, 3)
elseif aButton and bButton then
pitch(throwFly, 4)
end
end end
end end
end end
@ -814,10 +854,12 @@ function updateGameState()
throwBall(PitchStartX, PitchStartY, playdate.easingFunctions.linear, nil, true) throwBall(PitchStartX, PitchStartY, playdate.easingFunctions.linear, nil, true)
resetFielderPositions() resetFielderPositions()
offenseMode = Offense.batting offenseMode = Offense.batting
if not batter then
batter = newRunner() batter = newRunner()
end end
end end
end end
end
updateFielders() updateFielders()
walkAwayOutRunners() walkAwayOutRunners()
@ -829,11 +871,10 @@ function drawMinimap() end
---@param fielder Fielder ---@param fielder Fielder
---@return boolean isHoldingBall ---@return boolean isHoldingBall
function drawFielderGlove(fielder) function drawFielderGlove(fielder)
printTable({ ballFloatValue = ballFloatAnimator:currentValue() })
local distanceFromBall = local distanceFromBall =
utils.distanceBetweenZ(fielder.x, fielder.y, 0, ball.x, ball.y, ballFloatAnimator:currentValue()) utils.distanceBetweenZ(fielder.x, fielder.y, 0, ball.x, ball.y, ballFloatAnimator:currentValue())
local shoulderX, shoulderY = fielder.x + 10, fielder.y + FielderDanceAnimator:currentValue() + 5 local shoulderX, shoulderY = fielder.x + 10, fielder.y + FielderDanceAnimator:currentValue() + 5
if distanceFromBall > 30 then if distanceFromBall > 20 then
Glove:draw(shoulderX, shoulderY) Glove:draw(shoulderX, shoulderY)
return false return false
else else

View File

@ -43,8 +43,8 @@ end
---@param y2 number ---@param y2 number
---@return number x, number y, number distance ---@return number x, number y, number distance
function utils.normalizeVector(x1, y1, x2, y2) function utils.normalizeVector(x1, y1, x2, y2)
local distance, a, b = utils.distanceBetween(x1, y1, x2, y2) local distance, x, y = utils.distanceBetween(x1, y1, x2, y2)
return a / distance, b / distance, distance return x / distance, y / distance, distance
end end
---@generic T ---@generic T
@ -63,17 +63,17 @@ end
---@return number distance, number x, number y ---@return number distance, number x, number y
function utils.distanceBetween(x1, y1, x2, y2) function utils.distanceBetween(x1, y1, x2, y2)
local a = x1 - x2 local x = x1 - x2
local b = y1 - y2 local y = y1 - y2
return math.sqrt((a * a) + (b * b)), a, b return math.sqrt((x * x) + (y * y)), x, y
end end
---@return number distance, number x, number y ---@return number distance, number x, number y, number z
function utils.distanceBetweenZ(x1, y1, z1, x2, y2, z2) function utils.distanceBetweenZ(x1, y1, z1, x2, y2, z2)
local a = x1 - x2 local x = x1 - x2
local b = y1 - y2 local y = y1 - y2
local c = z1 - z2 local z = z1 - z2
return math.sqrt((a * a) + (b * b) + (c * c)), a, b return math.sqrt((x * x) + (y * y) + (z * z)), x, y, z
end end
--- Returns true only if the point is below the given line, within the x bounds of said line, and above the bottomBound --- Returns true only if the point is below the given line, within the x bounds of said line, and above the bottomBound