Skeleton tracking/display of balls and strikes
This commit is contained in:
parent
2867b4a367
commit
a341abed31
|
@ -6,8 +6,14 @@ function dbg.label(value, name)
|
|||
if type(value) == "table" then
|
||||
print(name .. ":")
|
||||
printTable(value)
|
||||
else
|
||||
elseif type(value) == "boolean" then
|
||||
if value then
|
||||
print(name .. ": " .. (value and "true" or "false"))
|
||||
end
|
||||
elseif value ~= nil then
|
||||
print(name .. ": " .. value)
|
||||
else
|
||||
print(name .. ": nil")
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
|
167
src/main.lua
167
src/main.lua
|
@ -69,6 +69,7 @@ local PlayerImageBlipper <const> = blipper.new(100, "images/game/player.png", "i
|
|||
|
||||
local DanceBounceMs <const> = 500
|
||||
local DanceBounceCount <const> = 4
|
||||
|
||||
local FielderDanceAnimator <const> = gfx.animator.new(1, 10, 0, utils.easingHill)
|
||||
FielderDanceAnimator.repeatCount = DanceBounceCount - 1
|
||||
|
||||
|
@ -135,7 +136,7 @@ local ball <const> = {
|
|||
heldBy = nil --[[@type Runner | nil]],
|
||||
}
|
||||
|
||||
local BatLength <const> = 50 --45
|
||||
local BatLength <const> = 50
|
||||
|
||||
local Offense <const> = {
|
||||
batting = {},
|
||||
|
@ -168,12 +169,15 @@ local battingTeam = teams.away
|
|||
local outs = 0
|
||||
local inning = 1
|
||||
|
||||
---@return boolean playerIsOnSide, boolean playerIsOnOtherSide
|
||||
function playerIsOn(side)
|
||||
local ret
|
||||
if PlayerTeam == battingTeam then
|
||||
return side == Sides.offense
|
||||
ret = side == Sides.offense
|
||||
else
|
||||
return side == Sides.defense
|
||||
ret = side == Sides.defense
|
||||
end
|
||||
return ret, not ret
|
||||
end
|
||||
|
||||
-- TODO? Replace this AND ballSizeAnimator with a ballHeightAnimator
|
||||
|
@ -292,6 +296,9 @@ end
|
|||
---@type Runner | nil
|
||||
local batter = newRunner()
|
||||
|
||||
local throwMeter = 0
|
||||
local PitchMeterLimit = 15
|
||||
|
||||
--- "Throws" the ball from its current position to the given destination.
|
||||
---@param destX number
|
||||
---@param destY number
|
||||
|
@ -315,6 +322,7 @@ function throwBall(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler
|
|||
if floaty then
|
||||
ballFloatAnimator:reset(flyTimeMs)
|
||||
end
|
||||
throwMeter = 0
|
||||
end
|
||||
|
||||
local PitchAfterSeconds = 7
|
||||
|
@ -403,9 +411,10 @@ local secondsSinceLastRunnerMove = 0
|
|||
|
||||
---@param runnerIndex integer
|
||||
function outRunner(runnerIndex)
|
||||
outs = outs + 1
|
||||
outRunners[#outRunners + 1] = runners[runnerIndex]
|
||||
table.remove(runners, runnerIndex)
|
||||
|
||||
outs = outs + 1
|
||||
updateForcedRunners()
|
||||
|
||||
announcer:say("YOU'RE OUT!")
|
||||
|
@ -504,9 +513,6 @@ function tryToMakeAnOut(fielder)
|
|||
end
|
||||
end
|
||||
|
||||
local throwMeter = 0
|
||||
local PitchMeterLimit = 15
|
||||
|
||||
function readThrow()
|
||||
if throwMeter > PitchMeterLimit then
|
||||
return (PitchFlyMs / (throwMeter / PitchMeterLimit))
|
||||
|
@ -539,36 +545,42 @@ function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome)
|
|||
closestFielder.target = targetBase
|
||||
secondsSinceLastRunnerMove = 0
|
||||
offenseMode = Offense.running
|
||||
throwMeter = 0
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function updateNpcFielder(fielder)
|
||||
function outEligibleRunners(fielder)
|
||||
local touchedBase = isTouchingBase(fielder.x, fielder.y)
|
||||
local didOutRunner = false
|
||||
for i, runner in pairs(runners) do
|
||||
local runnerOnBase = isTouchingBase(runner.x, runner.y)
|
||||
if -- Force out
|
||||
touchedBase
|
||||
and runner.prevBase -- Make sure the runner is not standing at home
|
||||
and runner.forcedTo == touchedBase
|
||||
and touchedBase ~= runnerOnBase
|
||||
-- Tag out
|
||||
or not runnerOnBase and utils.distanceBetween(runner.x, runner.y, fielder.x, fielder.y) < TagDistance
|
||||
then
|
||||
outRunner(i)
|
||||
didOutRunner = true
|
||||
end
|
||||
end
|
||||
|
||||
return didOutRunner
|
||||
end
|
||||
|
||||
function updateNpcFielder(fielder, outedSomeRunner)
|
||||
if offenseMode ~= Offense.running then
|
||||
return
|
||||
end
|
||||
local touchedBase = isTouchingBase(fielder.x, fielder.y)
|
||||
for i, runner in pairs(runners) do
|
||||
local runnerOnBase = isTouchingBase(runner.x, runner.y)
|
||||
if
|
||||
( -- Force out
|
||||
touchedBase
|
||||
-- and runner.prevBase -- Make sure the runner is not standing at home
|
||||
and runner.forcedTo == touchedBase
|
||||
and touchedBase ~= runnerOnBase
|
||||
)
|
||||
-- Tag out
|
||||
or (not runnerOnBase and utils.distanceBetween(runner.x, runner.y, fielder.x, fielder.y) < TagDistance)
|
||||
then
|
||||
outRunner(i)
|
||||
if outedSomeRunner then
|
||||
playdate.timer.new(750, function()
|
||||
tryToMakeAnOut(fielder)
|
||||
end)
|
||||
else
|
||||
tryToMakeAnOut(fielder)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param fielder Fielder
|
||||
|
@ -583,30 +595,18 @@ function updateFielder(fielder)
|
|||
return
|
||||
end
|
||||
|
||||
local outedSomeRunner = outEligibleRunners(fielder)
|
||||
|
||||
if playerIsOn(Sides.defense) then
|
||||
local throwFly = readThrow()
|
||||
if throwFly then
|
||||
buttonControlledThrow(fielders.pitcher, throwFly)
|
||||
end
|
||||
else
|
||||
updateNpcFielder(fielder)
|
||||
updateNpcFielder(fielder, outedSomeRunner)
|
||||
end
|
||||
end
|
||||
|
||||
function updateFielders()
|
||||
for _, fielder in pairs(fielders) do
|
||||
updateFielder(fielder)
|
||||
end
|
||||
|
||||
-- if offenseMode == Offense.batting then
|
||||
-- utils.moveAtSpeed(
|
||||
-- fielders.catcher,
|
||||
-- fielders.catcher.speed * 2 * deltaSeconds,
|
||||
-- { x = math.min(Center.x + 15, ball.x), y = fielders.catcher.y }
|
||||
-- )
|
||||
-- end
|
||||
end
|
||||
|
||||
--- Returns true only if the given runner moved during this update.
|
||||
---@param runner Runner | nil
|
||||
---@param runnerIndex integer | nil May only be nil if runner == batter
|
||||
|
@ -665,6 +665,53 @@ end
|
|||
---@type number
|
||||
local batAngleDeg
|
||||
|
||||
-- Used for tracking whether or not a pitch was a strike
|
||||
local recordedPitchX = nil
|
||||
local balls = 0
|
||||
local strikes = 0
|
||||
|
||||
local StrikeZoneStartX <const> = Center.x - 16
|
||||
local StrikeZoneEndX <const> = StrikeZoneStartX + 24
|
||||
local StrikeZoneStartY <const> = Screen.H - 35
|
||||
|
||||
function recordStrikePosition()
|
||||
if not recordedPitchX and ball.y > StrikeZoneStartY then
|
||||
recordedPitchX = ball.x
|
||||
end
|
||||
end
|
||||
|
||||
function nextBatter()
|
||||
playdate.timer.new(2000, function()
|
||||
balls = 0
|
||||
strikes = 0
|
||||
batter = newRunner()
|
||||
end)
|
||||
end
|
||||
|
||||
function walk()
|
||||
-- TODO
|
||||
nextBatter()
|
||||
end
|
||||
|
||||
function strikeOut()
|
||||
-- TODO
|
||||
nextBatter()
|
||||
end
|
||||
|
||||
function recordPitch()
|
||||
if recordedPitchX > StrikeZoneStartX and recordedPitchX < StrikeZoneEndX then
|
||||
strikes = strikes + 1
|
||||
if strikes >= 3 then
|
||||
strikeOut()
|
||||
end
|
||||
else
|
||||
balls = balls + 1
|
||||
if balls >= 4 then
|
||||
walk()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param batDeg number
|
||||
function updateBatting(batDeg, batSpeed)
|
||||
if ball.y < BallOffscreen then
|
||||
|
@ -764,19 +811,21 @@ function playerPitch(throwFly)
|
|||
end
|
||||
|
||||
local npcBatDeg = 0
|
||||
local NpcBatSpeed <const> = 1500
|
||||
local BaseNpcBatSpeed <const> = 1500
|
||||
local npcBatSpeed = 1500
|
||||
|
||||
function npcBatAngle()
|
||||
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
|
||||
npcBatSpeed = (1 + math.random()) * BaseNpcBatSpeed
|
||||
npcBatDeg = 200
|
||||
end
|
||||
return npcBatDeg
|
||||
end
|
||||
|
||||
function npcBatChange()
|
||||
return deltaSeconds * NpcBatSpeed
|
||||
return deltaSeconds * npcBatSpeed
|
||||
end
|
||||
|
||||
function npcRunningSpeed()
|
||||
|
@ -785,7 +834,7 @@ function npcRunningSpeed()
|
|||
end
|
||||
local touchedBase = isTouchingBase(runners[1].x, runners[1].y)
|
||||
if not touchedBase or touchedBase == Bases[Home] then
|
||||
return 25
|
||||
return 10
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
@ -803,23 +852,27 @@ function updateGameState()
|
|||
ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue()
|
||||
end
|
||||
|
||||
local playerOnOffense = playerIsOn(Sides.offense)
|
||||
local playerOnOffense, playerOnDefense = playerIsOn(Sides.offense)
|
||||
|
||||
if playerOnDefense then
|
||||
throwMeter = math.max(0, throwMeter - (deltaSeconds * 150))
|
||||
throwMeter = throwMeter + math.abs(crankChange)
|
||||
end
|
||||
|
||||
if offenseMode == Offense.batting then
|
||||
recordStrikePosition()
|
||||
secondsSincePitchAllowed = secondsSincePitchAllowed + deltaSeconds
|
||||
|
||||
if secondsSincePitchAllowed > 3.5 and not catcherThrownBall then
|
||||
recordPitch()
|
||||
throwBall(PitchStartX, PitchStartY, playdate.easingFunctions.linear, nil, true)
|
||||
catcherThrownBall = true
|
||||
else
|
||||
throwMeter = 0
|
||||
end
|
||||
|
||||
local batSpeed
|
||||
if playerOnOffense then
|
||||
batAngleDeg, batSpeed = (playdate.getCrankPosition() + CrankOffsetDeg) % 360, crankChange
|
||||
else
|
||||
throwMeter = math.max(0, throwMeter - (deltaSeconds * 150))
|
||||
throwMeter = throwMeter + crankChange
|
||||
batAngleDeg, batSpeed = npcBatAngle(), npcBatChange()
|
||||
end
|
||||
|
||||
|
@ -829,21 +882,17 @@ function updateGameState()
|
|||
updateRunner(batter, nil, crankChange)
|
||||
|
||||
if secondsSincePitchAllowed > PitchAfterSeconds then
|
||||
if playerOnOffense then
|
||||
pitch(PitchFlyMs, math.random(#Pitches))
|
||||
else
|
||||
if playerOnDefense then
|
||||
local throwFly = readThrow()
|
||||
if throwFly and not buttonControlledThrow(fielders.pitcher, throwFly, true) then
|
||||
playerPitch(throwFly)
|
||||
end
|
||||
else
|
||||
pitch(PitchFlyMs, math.random(#Pitches))
|
||||
end
|
||||
end
|
||||
elseif offenseMode == Offense.running then
|
||||
if not playerOnOffense then
|
||||
throwMeter = math.max(0, throwMeter - (deltaSeconds * 150))
|
||||
throwMeter = throwMeter + crankChange
|
||||
end
|
||||
local appliedSpeed = playerIsOn(Sides.offense) and crankChange or npcRunningSpeed()
|
||||
local appliedSpeed = playerOnOffense and crankChange or npcRunningSpeed()
|
||||
if updateRunning(appliedSpeed) then
|
||||
secondsSinceLastRunnerMove = 0
|
||||
else
|
||||
|
@ -859,7 +908,9 @@ function updateGameState()
|
|||
end
|
||||
end
|
||||
|
||||
updateFielders()
|
||||
for _, fielder in pairs(fielders) do
|
||||
updateFielder(fielder)
|
||||
end
|
||||
walkAwayOutRunners()
|
||||
end
|
||||
|
||||
|
@ -901,7 +952,6 @@ end
|
|||
function playdate.update()
|
||||
playdate.timer.updateTimers()
|
||||
gfx.animation.blinker.updateAll()
|
||||
|
||||
updateGameState()
|
||||
|
||||
gfx.clear()
|
||||
|
@ -964,6 +1014,7 @@ function playdate.update()
|
|||
drawMinimap()
|
||||
end
|
||||
drawScoreboard(0, Screen.H * 0.77, teams, outs, battingTeam, inning)
|
||||
drawBallsAndStrikes(300, Screen.H * 0.77, balls, strikes)
|
||||
announcer:draw(Center.x, 10)
|
||||
end
|
||||
|
||||
|
|
|
@ -16,9 +16,15 @@ function getIndicators(teams, battingTeam)
|
|||
return "", IndicatorWidth, Indicator, 0
|
||||
end
|
||||
|
||||
function drawBallsAndStrikes(x, y, balls, strikes)
|
||||
local gfx = playdate.graphics
|
||||
print("" .. balls .. ":" .. strikes)
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
gfx.fillRect(x, y, 20, ScoreboardHeight)
|
||||
end
|
||||
|
||||
function drawScoreboard(x, y, teams, outs, battingTeam, inning)
|
||||
local gfx = playdate.graphics
|
||||
|
||||
local homeScore = teams.home.score
|
||||
local awayScore = teams.away.score
|
||||
|
||||
|
|
|
@ -14,18 +14,6 @@ function utils.easingHill(t, b, c, d)
|
|||
return (c * t) + b
|
||||
end
|
||||
|
||||
-- Useful for quick print-the-value-in-place debugging.
|
||||
-- selene: allow(unused_variable)
|
||||
function utils.label(value, name)
|
||||
if type(value) == "table" then
|
||||
print(name .. ":")
|
||||
printTable(value)
|
||||
else
|
||||
print(name .. ": " .. value)
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@return XYPair
|
||||
|
|
Loading…
Reference in New Issue