Fielders can run the ball towards a base themselves.

Ball can be heldBy a fielder.
Fix likely bug in buildCache() and document it (including contemplating future removal).
Add missing Full Circle font png.
This commit is contained in:
Sage Vaillancourt 2025-02-02 13:32:22 -05:00
parent b9fc7ee09e
commit 66e3d2bb01
3 changed files with 37 additions and 20 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -60,9 +60,10 @@ local ball <const> = {
x = Center.x, x = Center.x,
y = Center.y, y = Center.y,
size = 6, size = 6,
heldBy = nil --[[@type Runner | nil]],
} }
local BatLength <const> = 45 local BatLength <const> = 50 --45
local Modes <const> = { local Modes <const> = {
batting = {}, batting = {},
@ -146,7 +147,7 @@ function resetFielderPositions(fromOffTheField)
fielders.catcher.target = xy(Screen.W * 0.475, Screen.H * 0.92) fielders.catcher.target = xy(Screen.W * 0.475, Screen.H * 0.92)
fielders.left.target = xy(Screen.W * -1, Screen.H * -0.2) fielders.left.target = xy(Screen.W * -1, Screen.H * -0.2)
fielders.center.target = xy(Center.x, Screen.H * -0.4) fielders.center.target = xy(Center.x, Screen.H * -0.4)
fielders.right.target = xy(Screen.W * 2, Screen.H * fielders.left.target.y) fielders.right.target = xy(Screen.W * 2, fielders.left.target.y)
end end
local BatterStartingX <const> = Bases[Home].x - 40 local BatterStartingX <const> = Bases[Home].x - 40
@ -179,6 +180,7 @@ function throwBall(destX, destY, easingFunc, flyTimeMs, floaty)
if not flyTimeMs then if not flyTimeMs then
flyTimeMs = distanceBetween(ball.x, ball.y, destX, destY) * 5 flyTimeMs = distanceBetween(ball.x, ball.y, destX, destY) * 5
end end
ball.heldBy = nil
ballSizeAnimator:reset(flyTimeMs) ballSizeAnimator:reset(flyTimeMs)
ballAnimatorY = gfx.animator.new(flyTimeMs, ball.y, destY, easingFunc) ballAnimatorY = gfx.animator.new(flyTimeMs, ball.y, destY, easingFunc)
ballAnimatorX = gfx.animator.new(flyTimeMs, ball.x, destX, easingFunc) ballAnimatorX = gfx.animator.new(flyTimeMs, ball.x, destX, easingFunc)
@ -264,25 +266,24 @@ function updateFielders()
fielder.x = fielder.x - (x * fielder.speed * deltaSeconds) fielder.x = fielder.x - (x * fielder.speed * deltaSeconds)
fielder.y = fielder.y - (y * fielder.speed * deltaSeconds) fielder.y = fielder.y - (y * fielder.speed * deltaSeconds)
else else
fielder.target = nil
if fielder.onArrive then if fielder.onArrive then
fielder.onArrive() fielder.onArrive()
end end
fielder.target = nil
end end
end end
if currentMode == Modes.running and isTouchingBall(fielder.x, fielder.y) then if currentMode == Modes.running and isTouchingBall(fielder.x, fielder.y) then
local touchedBase = isTouchingBase(fielder.x, fielder.y) local touchedBase = isTouchingBase(fielder.x, fielder.y)
for i, runner in pairs(runners) do for i, runner in pairs(runners) do
local runnerOnBase = touchingBaseCache.get(runner)
if if
touchedBase touchedBase
and runner.prevBase and runner.prevBase -- Check if the runner is standing at home
and runner.forcedTo == touchedBase and runner.forcedTo == touchedBase
and touchedBase ~= runnerOnBase and touchedBase ~= touchingBaseCache.get(runner)
then then
outRunner(i) outRunner(i)
elseif not runnerOnBase then elseif not touchingBaseCache.get(runner) then
if distanceBetween(runner.x, runner.y, fielder.x, fielder.y) < TagDistance then if distanceBetween(runner.x, runner.y, fielder.x, fielder.y) < TagDistance then
outRunner(i) outRunner(i)
end end
@ -468,10 +469,16 @@ function updateBatting()
local chasingFielder = getNearestOf(fielders, ballDestX, ballDestY) local chasingFielder = getNearestOf(fielders, ballDestX, ballDestY)
chasingFielder.target = { x = ballDestX, y = ballDestY } chasingFielder.target = { x = ballDestX, y = ballDestY }
chasingFielder.onArrive = function() chasingFielder.onArrive = function()
local targetX, targetY = getNextThrowTarget()
if targetX ~= nil then
throwBall(targetX, targetY, playdate.easingFunctions.linear, nil, true)
chasingFielder.onArrive = nil chasingFielder.onArrive = nil
local targetX, targetY = getNextThrowTarget()
if targetX ~= nil and targetY ~= nil then
local nearestFielder = getNearestOf(fielders, targetX, targetY)
nearestFielder.target = xy(targetX, targetY)
if nearestFielder == chasingFielder then
ball.heldBy = chasingFielder
else
throwBall(targetX, targetY, playdate.easingFunctions.linear, nil, true)
end
end end
end end
end end
@ -510,8 +517,13 @@ function updateGameState()
elapsedSec = elapsedSec + deltaSeconds elapsedSec = elapsedSec + deltaSeconds
crankChange, acceleratedChange = playdate.getCrankChange() --[[@as number, number]] crankChange, acceleratedChange = playdate.getCrankChange() --[[@as number, number]]
if ball.heldBy then
ball.x = ball.heldBy.x
ball.y = ball.heldBy.y
else
ball.x = ballAnimatorX:currentValue() ball.x = ballAnimatorX:currentValue()
ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue() ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue()
end
if currentMode == Modes.batting then if currentMode == Modes.batting then
updateBatting() updateBatting()

View File

@ -3,12 +3,6 @@ import 'CoreLibs/animation.lua'
import 'CoreLibs/graphics.lua' import 'CoreLibs/graphics.lua'
-- stylua: ignore end -- stylua: ignore end
-- number = ((number * 2) - 1)
-- return number * number
-- c = c + 0.0 -- convert to float to prevent integer overflow
-- return c * t / d + b
function easingHill(t, b, c, d) function easingHill(t, b, c, d)
c = c + 0.0 -- convert to float to prevent integer overflow c = c + 0.0 -- convert to float to prevent integer overflow
t = t / d t = t / d
@ -109,12 +103,22 @@ function getNearestOf(array, x, y, extraCondition)
return nearest, nearestDistance return nearest, nearestDistance
end end
--- Marker used by buildCache to indicate a cached `nil` value.
local NoValue <const> = {} local NoValue <const> = {}
--- Build a simple fetcher cache. On calling `get()`, if no value has already
--- been fetched, calls `fetcher(key)`, then caches and returns that value.
---
--- On reflection, it's probably pretty early for this optimization, and it's
--- optimizing in favor of CPU at the expense of memory, which is probably not
--- where the playdate's limitaitons lie. But it can stay for now.
---
---@generic Key
---@generic Value
---@return { get: fun(key: Key): Value }
function buildCache(fetcher) function buildCache(fetcher)
local cacheData = {} local cacheData = {}
return { return {
cacheDate = cacheData,
get = function(key) get = function(key)
if cacheData[key] == NoValue then if cacheData[key] == NoValue then
return nil return nil
@ -122,7 +126,8 @@ function buildCache(fetcher)
if cacheData[key] ~= nil then if cacheData[key] ~= nil then
return cacheData[key] return cacheData[key]
end end
cacheData[key] = fetcher(key) or NoValue local fetched = fetcher(key)
cacheData[key] = fetched ~= nil and fetched or NoValue
return cacheData[key] return cacheData[key]
end, end,
} }