Fix flickering on return-to-pitcher.
Some linting. Prevent runners "sticking" to bases during walk/homeRun sequences. Tweak logo to remove some trailing pixels.
This commit is contained in:
parent
786f80b0df
commit
7b49603760
|
@ -25,6 +25,16 @@ function actionQueue:upsert(id, maxTimeMs, action)
|
|||
}
|
||||
end
|
||||
|
||||
function actionQueue:newOnly(id, maxTimeMs, action)
|
||||
if self.queue[id] then
|
||||
return
|
||||
end
|
||||
self.queue[id] = {
|
||||
coroutine = coroutine.create(action),
|
||||
expireTimeMs = maxTimeMs + playdate.getCurrentTimeMilliseconds(),
|
||||
}
|
||||
end
|
||||
|
||||
--- Must be called on every playdate.update() to check for (and run) any waiting tasks.
|
||||
--- Actions that return NeedsMoreTime will not be removed from the queue unless they have expired.
|
||||
function actionQueue:runWaiting(deltaSeconds)
|
||||
|
|
|
@ -179,7 +179,7 @@ end
|
|||
---@param appliedSpeed number
|
||||
---@param deltaSeconds number
|
||||
---@return boolean runnerMoved, boolean runnerScored
|
||||
function Baserunning:updateRunner(runner, runnerIndex, appliedSpeed, deltaSeconds)
|
||||
function Baserunning:updateRunner(runner, runnerIndex, appliedSpeed, isAutoRun, deltaSeconds)
|
||||
local autoRunSpeed = 20 * deltaSeconds
|
||||
|
||||
if not runner or not runner.nextBase then
|
||||
|
@ -226,9 +226,12 @@ function Baserunning:updateRunner(runner, runnerIndex, appliedSpeed, deltaSecond
|
|||
-- TODO: Make this less "sticky" for the user.
|
||||
-- Currently it can be a little hard to run *past* a base.
|
||||
|
||||
local autoRun = (nearestBaseDistance > 40 or runner.forcedTo) and mult * autoRunSpeed
|
||||
or nearestBaseDistance < 5 and 0
|
||||
or (nearestBase == runner.nextBase and autoRunSpeed or -1 * autoRunSpeed)
|
||||
local autoRun = 0
|
||||
if not isAutoRun then
|
||||
autoRun = (nearestBaseDistance > 40 or runner.forcedTo) and mult * autoRunSpeed
|
||||
or nearestBaseDistance < 5 and 0
|
||||
or (nearestBase == runner.nextBase and autoRunSpeed or -1 * autoRunSpeed)
|
||||
end
|
||||
|
||||
mult = autoRun + (appliedSpeed / 20)
|
||||
runner.x = runner.x - (x * mult)
|
||||
|
@ -240,8 +243,9 @@ end
|
|||
--- Update non-batter runners.
|
||||
--- Returns true only if at least one of the given runners moved during this update
|
||||
---@param appliedSpeed number | fun(runner: Runner): number
|
||||
---@param isAutoRun boolean If true, does not attempt to hug the bases
|
||||
---@return boolean runnersStillMoving, number runnersScored, number secondsSinceLastMove
|
||||
function Baserunning:updateNonBatterRunners(appliedSpeed, forcedOnly, deltaSeconds)
|
||||
function Baserunning:updateNonBatterRunners(appliedSpeed, forcedOnly, isAutoRun, deltaSeconds)
|
||||
local runnersStillMoving = false
|
||||
local runnersScored = 0
|
||||
|
||||
|
@ -253,7 +257,7 @@ function Baserunning:updateNonBatterRunners(appliedSpeed, forcedOnly, deltaSecon
|
|||
if speedIsFunction then
|
||||
speed = appliedSpeed(runner)
|
||||
end
|
||||
local thisRunnerMoved, thisRunnerScored = self:updateRunner(runner, runnerIndex, speed, deltaSeconds)
|
||||
local thisRunnerMoved, thisRunnerScored = self:updateRunner(runner, runnerIndex, speed, isAutoRun, deltaSeconds)
|
||||
runnersStillMoving = runnersStillMoving or thisRunnerMoved
|
||||
if thisRunnerScored then
|
||||
runnersScored = runnersScored + 1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local gfx <const>, C <const> = playdate.graphics, C
|
||||
local gfx <const> = playdate.graphics
|
||||
|
||||
fans = {}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ function throwMeter:draw(x, y)
|
|||
if self.lastReadThrow then
|
||||
-- TODO: If meter has moved to a new fielder, empty it.
|
||||
local ratio = 1
|
||||
if not wasPerfect then
|
||||
if not self.wasPerfect then
|
||||
ratio = (self.lastReadThrow - throwMeter.MinCharge) / (self.idealPower - throwMeter.MinCharge)
|
||||
end
|
||||
local height = ThrowMeterHeight * ratio
|
||||
|
|
|
@ -31,7 +31,7 @@ local function newFielder(name, speed)
|
|||
return {
|
||||
name = name,
|
||||
speed = speed * C.FielderRunMult,
|
||||
spriteIndex = math.random(#HomeTeamSpriteGroup)
|
||||
spriteIndex = math.random(#HomeTeamSpriteGroup),
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -32,12 +32,10 @@ function blipper.new(msInterval, spriteCollection)
|
|||
blinker:start()
|
||||
return {
|
||||
blinker = blinker,
|
||||
smiling = smiling,
|
||||
lowHat = lowHat,
|
||||
draw = function(self, disableBlipping, x, y, hasSpriteIndex)
|
||||
local spriteBundle = spriteCollection[hasSpriteIndex.spriteIndex]
|
||||
local currentImage = (disableBlipping or self.blinker.on) and spriteBundle.lowHat or spriteBundle.smiling
|
||||
local offsetY = currentImage == lowHat and -1 or 0
|
||||
local offsetY = currentImage == spriteBundle.lowHat and -1 or 0
|
||||
currentImage:draw(x, y + offsetY)
|
||||
end,
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 5.2 KiB |
70
src/main.lua
70
src/main.lua
|
@ -6,6 +6,7 @@ import 'CoreLibs/graphics.lua'
|
|||
import 'CoreLibs/object.lua'
|
||||
import 'CoreLibs/timer.lua'
|
||||
import 'CoreLibs/ui.lua'
|
||||
import 'CoreLibs/utilities/where.lua'
|
||||
-- stylua: ignore end
|
||||
|
||||
--- @alias Scene { update: fun(self: self) }
|
||||
|
@ -112,7 +113,7 @@ Game = {}
|
|||
function Game.new(settings, announcer, fielding, baserunning, npc, state)
|
||||
announcer = announcer or Announcer.new()
|
||||
fielding = fielding or Fielding.new()
|
||||
settings.userTeam = "away"
|
||||
settings.userTeam = nil -- "away"
|
||||
|
||||
local homeTeamBlipper = blipper.new(100, settings.homeTeamSpriteGroup)
|
||||
local awayTeamBlipper = blipper.new(100, settings.awayTeamSpriteGroup)
|
||||
|
@ -388,25 +389,31 @@ function Game:updateBatting(batDeg, batSpeed)
|
|||
ballVelX = ballVelX * -1
|
||||
ballVelY = ballVelY * -1
|
||||
end
|
||||
local ballDestX = self.state.ball.x + (ballVelX * C.BattingPower)
|
||||
local ballDestY = self.state.ball.y + (ballVelY * C.BattingPower)
|
||||
|
||||
local ballDest = utils.xy(
|
||||
self.state.ball.x + (ballVelX * C.BattingPower),
|
||||
-250 + self.state.ball.y + (ballVelY * C.BattingPower)
|
||||
)
|
||||
|
||||
pitchTracker:reset()
|
||||
local flyTimeMs = 2000
|
||||
-- TODO? A dramatic eye-level view on a home-run could be sick.
|
||||
local battingTeamStats = self:battingTeamCurrentInning()
|
||||
battingTeamStats.hits[#battingTeamStats.hits + 1] = utils.xy(ballDestX, ballDestY)
|
||||
battingTeamStats.hits[#battingTeamStats.hits + 1] = ballDest
|
||||
|
||||
if utils.isFoulBall(ballDestX, ballDestY) then
|
||||
if utils.isFoulBall(ballDest.x, ballDest.y) then
|
||||
self.announcer:say("Foul ball!")
|
||||
pitchTracker.strikes = math.min(pitchTracker.strikes + 1, 2)
|
||||
-- TODO: Have a fielder chase for the fly-out
|
||||
return
|
||||
end
|
||||
|
||||
if utils.pointIsSquarelyAboveLine(utils.xy(ballDestX, ballDestY), C.OutfieldWall) then
|
||||
if utils.pointIsSquarelyAboveLine(utils.xy(ballDest.x, ballDest.y), C.OutfieldWall) then
|
||||
playdate.timer.new(flyTimeMs, function()
|
||||
-- Verify that the home run wasn't intercepted
|
||||
if utils.within(1, self.state.ball.x, ballDestX) and utils.within(1, self.state.ball.y, ballDestY) then
|
||||
if
|
||||
utils.within(1, self.state.ball.x, ballDest.x) and utils.within(1, self.state.ball.y, ballDest.y)
|
||||
then
|
||||
self.announcer:say("HOME RUN!")
|
||||
self.state.offenseState = C.Offense.homeRun
|
||||
-- Linger on the home-run ball for a moment, before panning to the bases.
|
||||
|
@ -420,18 +427,19 @@ function Game:updateBatting(batDeg, batSpeed)
|
|||
end
|
||||
|
||||
local hitBallScaler = gfx.animator.new(2000, 9 + (mult * mult * 0.5), C.SmallestBallRadius, utils.easingHill)
|
||||
self.state.ball:launch(ballDestX, ballDestY, playdate.easingFunctions.outQuint, flyTimeMs, nil, hitBallScaler)
|
||||
self.state.ball:launch(ballDest.x, ballDest.y, playdate.easingFunctions.outQuint, flyTimeMs, nil, hitBallScaler)
|
||||
|
||||
self.baserunning:convertBatterToRunner()
|
||||
self.fielding:haveSomeoneChase(ballDestX, ballDestY)
|
||||
self.fielding:haveSomeoneChase(ballDest.x, ballDest.y)
|
||||
end
|
||||
end
|
||||
|
||||
---@param appliedSpeed number | fun(runner: Runner): number
|
||||
---
|
||||
---@return boolean runnersStillMoving, number secondsSinceLastRunnerMove
|
||||
function Game:updateNonBatterRunners(appliedSpeed, forcedOnly)
|
||||
function Game:updateNonBatterRunners(appliedSpeed, forcedOnly, isAutoRun)
|
||||
local runnersStillMoving, runnersScored, secondsSinceLastRunnerMove =
|
||||
self.baserunning:updateNonBatterRunners(appliedSpeed, forcedOnly, self.state.deltaSeconds)
|
||||
self.baserunning:updateNonBatterRunners(appliedSpeed, forcedOnly, isAutoRun, self.state.deltaSeconds)
|
||||
if runnersScored ~= 0 then
|
||||
self:score(runnersScored)
|
||||
end
|
||||
|
@ -440,7 +448,12 @@ end
|
|||
|
||||
function Game:returnToPitcher()
|
||||
self.fielding:resetFielderPositions()
|
||||
actionQueue:upsert("returnToPitcher", 60 * 1000, function()
|
||||
|
||||
if self:pitcherIsReady() then
|
||||
return -- Don't then!
|
||||
end
|
||||
|
||||
actionQueue:newOnly("returnToPitcher", 60 * 1000, function()
|
||||
while not self:pitcherIsOnTheMound() do
|
||||
coroutine.yield()
|
||||
end
|
||||
|
@ -521,11 +534,17 @@ function Game:updateGameState()
|
|||
self:updateBatting(self.state.batAngleDeg, batSpeed)
|
||||
|
||||
-- Walk batter to the plate
|
||||
self.baserunning:updateRunner(self.baserunning.batter, nil, userOnOffense and crankLimited or 0, self.state.deltaSeconds)
|
||||
self.baserunning:updateRunner(
|
||||
self.baserunning.batter,
|
||||
nil,
|
||||
userOnOffense and crankLimited or 0,
|
||||
false,
|
||||
self.state.deltaSeconds
|
||||
)
|
||||
|
||||
if pitchTracker.secondsSinceLastPitch > C.PitchAfterSeconds then
|
||||
if userOnDefense then
|
||||
local powerRatio, accuracy, isPerfect = throwMeter:readThrow(crankChange)
|
||||
local powerRatio, accuracy = throwMeter:readThrow(crankChange)
|
||||
if powerRatio then
|
||||
local throwFly = C.PitchFlyMs / powerRatio
|
||||
if throwFly and not self:buttonControlledThrow(throwFly, true) then
|
||||
|
@ -541,7 +560,7 @@ function Game:updateGameState()
|
|||
or function(runner)
|
||||
return self.npc:runningSpeed(runner, self.state.ball)
|
||||
end
|
||||
local _, secondsSinceLastRunnerMove = self:updateNonBatterRunners(appliedSpeed, false)
|
||||
local _, secondsSinceLastRunnerMove = self:updateNonBatterRunners(appliedSpeed, false, false)
|
||||
if secondsSinceLastRunnerMove > C.ResetFieldersAfterSeconds then
|
||||
-- End of play. Throw the ball back to the pitcher
|
||||
self.state.offenseState = C.Offense.batting
|
||||
|
@ -549,7 +568,7 @@ function Game:updateGameState()
|
|||
end
|
||||
|
||||
if userOnDefense then
|
||||
local powerRatio, accuracy, isPerfect = throwMeter:readThrow(crankChange)
|
||||
local powerRatio = throwMeter:readThrow(crankChange)
|
||||
if powerRatio then
|
||||
local throwFly = C.PitchFlyMs / powerRatio
|
||||
if throwFly then
|
||||
|
@ -558,11 +577,11 @@ function Game:updateGameState()
|
|||
end
|
||||
end
|
||||
elseif self.state.offenseState == C.Offense.walking then
|
||||
if not self:updateNonBatterRunners(C.WalkedRunnerSpeed, true) then
|
||||
if not self:updateNonBatterRunners(C.WalkedRunnerSpeed, true, true) then
|
||||
self.state.offenseState = C.Offense.batting
|
||||
end
|
||||
elseif self.state.offenseState == C.Offense.homeRun then
|
||||
self:updateNonBatterRunners(C.WalkedRunnerSpeed * 2, false)
|
||||
self:updateNonBatterRunners(C.WalkedRunnerSpeed * 2, false, true)
|
||||
if #self.baserunning.runners == 0 then
|
||||
-- Give the player a moment to enjoy their home run.
|
||||
playdate.timer.new(1500, function()
|
||||
|
@ -607,8 +626,12 @@ function Game:update()
|
|||
local ballHeldBy
|
||||
for _, fielder in pairs(self.fielding.fielders) do
|
||||
addDraw(fielder.y + danceOffset, function()
|
||||
local ballHeldByThisFielder =
|
||||
drawFielder(self.state.fieldingTeamSprites[fielder.spriteIndex], self.state.ball, fielder.x, fielder.y + danceOffset)
|
||||
local ballHeldByThisFielder = drawFielder(
|
||||
self.state.fieldingTeamSprites[fielder.spriteIndex],
|
||||
self.state.ball,
|
||||
fielder.x,
|
||||
fielder.y + danceOffset
|
||||
)
|
||||
if ballHeldByThisFielder then
|
||||
ballHeldBy = fielder
|
||||
end
|
||||
|
@ -622,7 +645,10 @@ function Game:update()
|
|||
if self.state.batAngleDeg > 50 and self.state.batAngleDeg < 200 then
|
||||
self.state.battingTeamSprites[runner.spriteIndex].back:draw(runner.x, runner.y - playerHeightOffset)
|
||||
else
|
||||
self.state.battingTeamSprites[runner.spriteIndex].smiling:draw(runner.x, runner.y - playerHeightOffset)
|
||||
self.state.battingTeamSprites[runner.spriteIndex].smiling:draw(
|
||||
runner.x,
|
||||
runner.y - playerHeightOffset
|
||||
)
|
||||
end
|
||||
else
|
||||
-- TODO? Change blip speed depending on runner speed?
|
||||
|
@ -650,7 +676,7 @@ function Game:update()
|
|||
self.state.battingTeamSprites[runner.spriteIndex].smiling:draw(runner.x, runner.y - playerHeightOffset)
|
||||
end
|
||||
|
||||
if self:userIsOn('defense') then
|
||||
if self:userIsOn("defense") then
|
||||
throwMeter:drawNearFielder(ballHeldBy)
|
||||
end
|
||||
|
||||
|
|
|
@ -21,21 +21,36 @@ Pitches = {
|
|||
-- Fastball
|
||||
function(accuracy)
|
||||
return {
|
||||
x = gfx.animator.new(0, C.PitchStart.x, getPitchMissBy(accuracy) + 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(accuracy)
|
||||
return {
|
||||
x = gfx.animator.new(C.PitchFlyMs, getPitchMissBy(accuracy) + 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(accuracy)
|
||||
return {
|
||||
x = gfx.animator.new(C.PitchFlyMs, getPitchMissBy(accuracy) + 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,
|
||||
|
@ -44,10 +59,11 @@ Pitches = {
|
|||
return {
|
||||
x = {
|
||||
currentValue = function()
|
||||
return getPitchMissBy(accuracy) + C.PitchStart.x + (10 * math.sin((ball.yAnimator:currentValue() - C.PitchStart.y) / 10))
|
||||
end,
|
||||
reset = function()
|
||||
return getPitchMissBy(accuracy)
|
||||
+ C.PitchStart.x
|
||||
+ (10 * math.sin((ball.yAnimator:currentValue() - C.PitchStart.y) / 10))
|
||||
end,
|
||||
reset = function() end,
|
||||
},
|
||||
y = gfx.animator.new(C.PitchFlyMs * 1.3, C.PitchStart.y, C.PitchEndY, playdate.easingFunctions.linear),
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue