---@class Characters ---@field homeSprites SpriteCollection ---@field awaySprites SpriteCollection ---@field homeBlipper table ---@field awayBlipper table Characters = {} local gfx = playdate.graphics local GloveSizeX, GloveSizeY = Glove:getSize() local GloveOffX, GloveOffY = GloveSizeX / 2, GloveSizeY / 2 ---@param homeSprites SpriteCollection ---@param awaySprites SpriteCollection function Characters.new(homeSprites, awaySprites) return setmetatable({ homeSprites = homeSprites, awaySprites = awaySprites, homeBlipper = blipper.new(100, homeSprites), awayBlipper = blipper.new(100, awaySprites), }, { __index = Characters }) end ---@param ball Point3d ---@param fielderX number ---@param fielderY number ---@return boolean isHoldingBall local function drawFielderGlove(ball, fielderX, fielderY, flip) local distanceFromBall = utils.distanceBetweenZ(fielderX, fielderY, 0, ball.x, ball.y, ball.z) local shoulderX, shoulderY = fielderX + 10, fielderY - 5 if distanceFromBall > 20 then Glove:draw(shoulderX, shoulderY, flip) return false else GloveHoldingBall:draw(ball.x - GloveOffX, ball.y - GloveOffY, flip) return true end end ---@param fieldingTeamSprites SpriteCollection ---@param fielder Fielder ---@param ball Point3d ---@param flip boolean | nil ---@return boolean isHoldingBall function drawFielder(fieldingTeamSprites, fielder, ball, flip) local danceOffset = FielderDanceAnimator:currentValue() local x = fielder.x local y = fielder.y + danceOffset fieldingTeamSprites[fielder.spriteIndex].smiling:draw(fielder.x, y - 20, flip) return drawFielderGlove(ball, x, y) end ---@param batState BatRenderState local function drawBat(batState) gfx.setLineWidth(7) gfx.drawLine(batState.batBase.x, batState.batBase.y, batState.batTip.x, batState.batTip.y) gfx.setColor(gfx.kColorWhite) gfx.setLineCapStyle(gfx.kLineCapStyleRound) gfx.setLineWidth(3) gfx.drawLine(batState.batBase.x, batState.batBase.y, batState.batTip.x, batState.batTip.y) gfx.setColor(gfx.kColorBlack) end ---@param battingTeamSprites SpriteCollection ---@param batter Runner ---@param batState BatRenderState local function drawBatter(battingTeamSprites, batter, batState) local spriteCollection = battingTeamSprites[batter.spriteIndex] if batState.batAngleDeg > 50 and batState.batAngleDeg < 200 then drawBat(batState) spriteCollection.back:draw(batter.x, batter.y - C.PlayerHeightOffset) else spriteCollection.smiling:draw(batter.x, batter.y - C.PlayerHeightOffset) drawBat(batState) end end ---@param battingTeam TeamId ---@return SpriteCollection battingTeam, SpriteCollection fieldingTeam, table runnerBlipper function Characters:getSpriteCollections(battingTeam) if battingTeam == "home" then return self.homeSprites, self.awaySprites, self.homeBlipper end return self.awaySprites, self.homeSprites, self.awayBlipper end ---@param fielding Fielding ---@param baserunning Baserunning ---@param batState BatRenderState ---@param battingTeam TeamId ---@param ball Point3d ---@return Fielder | nil ballHeldBy function Characters:drawAll(fielding, baserunning, batState, battingTeam, ball) ---@type { y: number, drawAction: fun() }[] local characterDraws = {} function addDraw(y, drawAction) characterDraws[#characterDraws + 1] = { y = y, drawAction = drawAction } end local battingTeamSprites, fieldingTeamSprites, runnerBlipper = self:getSpriteCollections(battingTeam) ---@type Fielder | nil local ballHeldBy for _, fielder in pairs(fielding.fielders) do addDraw(fielder.y, function() local ballHeldByThisFielder = drawFielder(fieldingTeamSprites, fielder, ball) if ballHeldByThisFielder then ballHeldBy = fielder end end) end for _, runner in pairs(baserunning.runners) do addDraw(runner.y, function() local currentBatter = baserunning.batter if runner == currentBatter then drawBatter(battingTeamSprites, currentBatter, batState) else -- TODO? Change blip speed depending on runner speed? runnerBlipper:draw(false, runner.x, runner.y - C.PlayerHeightOffset, runner) end end) end for _, runner in pairs(baserunning.outRunners) do addDraw(runner.y, function() battingTeamSprites[runner.spriteIndex].frowning:draw(runner.x, runner.y - C.PlayerHeightOffset) end) end for _, runner in pairs(baserunning.scoredRunners) do addDraw(runner.y, function() runnerBlipper:draw(false, runner.x, runner.y - C.PlayerHeightOffset, runner) end) end table.sort(characterDraws, function(a, b) return a.y < b.y end) for _, character in pairs(characterDraws) do character.drawAction() end return ballHeldBy end -- luacheck: ignore if not playdate or playdate.TEST_MODE then return Characters end