|
|
|
@ -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,35 +545,41 @@ 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)
|
|
|
|
|
playdate.timer.new(750, function()
|
|
|
|
|
tryToMakeAnOut(fielder)
|
|
|
|
|
end)
|
|
|
|
|
else
|
|
|
|
|
if outedSomeRunner then
|
|
|
|
|
playdate.timer.new(750, function()
|
|
|
|
|
tryToMakeAnOut(fielder)
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
else
|
|
|
|
|
tryToMakeAnOut(fielder)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
@ -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
|
|
|
|
|
|
|
|
|
|