local gfx = playdate.graphics local ButtonFont = FontFullCircle --- Assumes that background image is of size: --- --- XXX --- XOX --- --- Where each character is the size of the screen, and 'O' is the default view. function getDrawOffset(ballX, ballY) local offsetX, offsetY if ballY > C.Screen.H or ballX >= C.BallOffscreen then return 0, 0 end -- Keep the ball approximately in the center, once it's past C.Center.y - 30 offsetY = math.max(0, (-1 * ballY) + C.Center.y - 30) if ballX >= 0 and ballX <= C.Screen.W then offsetX = 0 elseif ballX < 0 then offsetX = math.max(-1 * C.Screen.W, ballX * -1) elseif ballX > C.Screen.W then offsetX = math.min(C.Screen.W * 2, (ballX * -1) + C.Screen.W) end return offsetX * 1.3, offsetY end local buttonBlinker = gfx.animation.blinker.new(750, 500, true) buttonBlinker:start() --- Requires calling `playdate.graphics.animation.blinker.updateAll()` during `update()` to blink correctly. function drawButton(buttonLabel, x, y) gfx.setColor(gfx.kColorWhite) gfx.fillCircleAtPoint(x + 4, y + 7, 12) gfx.setColor(gfx.kColorBlack) if buttonBlinker.on then gfx.setLineWidth(1) gfx.drawCircleAtPoint(x + 4, y + 7, 10) end ButtonFont:drawText(buttonLabel, x, y) end ---@class Blipper ---@field draw fun(self: self, disableBlipping: boolean, x: number, y: number) blipper = {} --- Build an object that simply "blips" between the given images at the given interval. --- Expects `playdate.graphics.animation.blinker.updateAll()` to be called on every update. function blipper.new(msInterval, spriteCollection) local blinker = gfx.animation.blinker.new(msInterval, msInterval, true) blinker:start() return { blinker = blinker, 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 == spriteBundle.lowHat and -1 or 0 currentImage:draw(x, y + offsetY) end, } end