diff --git a/.gitignore b/.gitignore index e00b594..66f1578 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pdx +.idea diff --git a/src/images/game/grass.png b/src/images/game/grass.png index 4da3e6e..ab545fb 100644 Binary files a/src/images/game/grass.png and b/src/images/game/grass.png differ diff --git a/src/images/game/menu-image.png b/src/images/game/menu-image.png new file mode 100644 index 0000000..baa6ddb Binary files /dev/null and b/src/images/game/menu-image.png differ diff --git a/src/images/game/player-lowhat.png b/src/images/game/player-lowhat.png new file mode 100644 index 0000000..985867e Binary files /dev/null and b/src/images/game/player-lowhat.png differ diff --git a/src/main.lua b/src/main.lua index 6cd6b2b..996fe1d 100644 --- a/src/main.lua +++ b/src/main.lua @@ -7,9 +7,16 @@ import 'CoreLibs/ui.lua' local gfx = playdate.graphics playdate.display.setRefreshRate(50) gfx.setBackgroundColor(gfx.kColorWhite) +playdate.setMenuImage(gfx.image.new("images/game/menu-image.png")) local grassBackground = gfx.image.new("images/game/grass.png") -local playerImage = gfx.image.new("images/game/player.png") +local playerHighHat = gfx.image.new("images/game/player.png") +local playerLowHat = gfx.image.new("images/game/player-lowhat.png") +local playerImage = playerHighHat +local secPerFrame = 0.1 +local playerFrameElapsed = 0 + +local backgroundPan = -240 local pitchFlyTimeMs = 2500 local ballStartY, endY = 90, 250 @@ -26,7 +33,6 @@ local batBaseX, batBaseY = centerX - 34, 215 local batLength = 45 local ballY = ballStartY -local lastBallY = ballY local ballX = 200 local ballVelX, ballVelY = 0, 0 local ballSize = 6 @@ -34,7 +40,6 @@ local ballSize = 6 local batTipX = 0 local batTipY = 0 -local hit = false local MODES = { batting = 0, running = 1 @@ -80,13 +85,17 @@ function resetFielderPositions() end resetFielderPositions() +local playerStartingX = basePositions.home.x - 40 +local playerStartingY = basePositions.home.y - 3 local player = { - x = basePositions.home.x - 40, - y = basePositions.home.y - 3, + x = playerStartingX, + y = playerStartingY, nextBase = nil, prevBase = nil, } +local runners = { } + function hitBall(destX, destY, hitFlyTime) ballSizeAnimator:reset(ballSizeMs) hitAnimatorY = gfx.animator.new(hitFlyTime, ballY, destY, playdate.easingFunctions.outQuint) @@ -100,13 +109,22 @@ function throwBall(destX, destY) hitAnimatorX = gfx.animator.new(throwFlyTime, ballX, destX, playdate.easingFunctions.linear) end +function getNextThrowTarget() + return basePositions.first.x, basePositions.first.y +end + function pitch() pitchAnimator:reset() resetFielderPositions() ballVelX = 0 ballVelY = 0 ballX = 200 - hit = false + currentMode = MODES.batting + + -- TODO: Add new runners, instead + runners = {} + player.x = playerStartingX + player.y = playerStartingY end function playdate.AButtonDown() @@ -131,6 +149,7 @@ end local pitchClockSec = 99 local elapsedTime = 0 +local crankChange function ballPassedThruBat(ballX, ballY, batBaseX, batBaseY, batTipX, batTipY) -- This check currently assumes right-handedness. @@ -152,22 +171,21 @@ function ballPassedThruBat(ballX, ballY, batBaseX, batBaseY, batTipX, batTipY) end function updateInfield() - if not hit or ballDestX == nil or ballDestY == nil then + if ballDestX == nil or ballDestY == nil then return end - local fielderSpeed = 50 + local fielderSpeed = 40 for title,fielder in pairs(fielders) do if fielder.targetX ~= nil and fielder.targetY ~= nil then - local distance, a, b = distanceBetween(fielder.x, fielder.y, fielder.targetX, fielder.targetY) - local normalizer = fielderSpeed / distance + local x, y, distance = normalizeVector(fielder.x, fielder.y, fielder.targetX, fielder.targetY) if distance > 1 then - fielder.x -= a * normalizer * deltaTime - fielder.y -= b * normalizer * deltaTime + fielder.x -= x * fielderSpeed * deltaTime + fielder.y -= y * fielderSpeed * deltaTime else if fielder.onArrive then - fielder.onArrive(fielder.targetX, fielder.targetY) + fielder.onArrive() end fielder.targetX = nil fielder.targetY = nil @@ -176,6 +194,36 @@ function updateInfield() end end +function updateRunners() + local runnerSpeed = 20 + for i,runner in pairs(runners) do + if runner.nextBase then + local nb = runner.nextBase + local x, y, distance = normalizeVector(runner.x, runner.y, nb.x, nb.y) + if distance > 1 then + local mult = 1 + if crankChange < 0 then + mult = -1 + end + mult = (mult * runnerSpeed * deltaTime) + (crankChange / 20) + runner.x -= x * mult + runner.y -= y * mult + else + if runner.onArrive then + runner.onArrive() + end + runner.targetX = nil + runner.targetY = nil + end + end + end +end + +function normalizeVector(x1, y1, x2, y2) + local distance, a, b = distanceBetween(x1, y1, x2, y2) + return a / distance, b / distance, distance +end + function distanceBetween(x1, y1, x2, y2) local a = x1 - x2 local b = y1 - y2 @@ -200,6 +248,14 @@ function getNearestFielder(x, y) return nearestFielder end +function ballIsBeingThrown() + return false +end + +function throwArrivedBeforeRunner() + return false +end + function updateGameState() deltaTime = playdate.getElapsedTime() playdate.resetElapsedTime() @@ -210,7 +266,7 @@ function updateGameState() pitch() end - if hit then + if currentMode == MODES.running then ballX = hitAnimatorX:currentValue() ballY = hitAnimatorY:currentValue() ballSize = ballSizeAnimator:currentValue() @@ -224,7 +280,7 @@ function updateGameState() batTipY = batBaseY + (batLength * math.cos(batAngle)) crankChange, acceleratedChange = playdate.getCrankChange() - if hit == false and acceleratedChange >= 0 and + if currentMode == MODES.batting and acceleratedChange >= 0 and ballPassedThruBat(ballX, ballY, batBaseX, batBaseY, batTipX, batTipY) then ballAngle = batAngle + math.rad(90) @@ -242,22 +298,26 @@ function updateGameState() chasingFielder.targetX = ballDestX chasingFielder.targetY = ballDestY chasingFielder.onArrive = function() - throwBall(basePositions.first.x, basePositions.first.y) + throwBall(getNextThrowTarget()) + chasingFielder.onArrive = nil end fielders.first.targetX = basePositions.first.x fielders.first.targetY = basePositions.first.y - hit = true + currentMode = MODES.running + player.nextBase = basePositions.first + runners[#runners+1] = player end - lastBallY = ballY - updateInfield() + if currentMode == MODES.running then + updateRunners() + updateInfield() + end end function playdate.update() updateGameState() - grassBackground:draw(0, 0) - playerImage:draw(player.x, player.y) + grassBackground:draw(0, backgroundPan) gfx.setColor(gfx.kColorBlack) gfx.setLineWidth(2) @@ -268,9 +328,30 @@ function playdate.update() end gfx.setLineWidth(5) - gfx.drawLine(batBaseX, batBaseY, batTipX, batTipY) + + if currentMode == MODES.batting then + gfx.drawLine(batBaseX, batBaseY, batTipX, batTipY) + end if playdate.isCrankDocked() then playdate.ui.crankIndicator:draw() end + + playerImage:draw(player.x, player.y) + + -- TODO: Use gfx.animation.blinker instead + if currentMode == MODES.running and playerFrameElapsed > secPerFrame then + playerFrameElapsed = 0 + if playerImage == playerHighHat then + playerImage = playerLowHat + else + playerImage = playerHighHat + end + else + playerFrameElapsed += deltaTime + end + + -- for i,runner in pairs(runners) do + -- playerImage:draw(runner.x, runner.y) + -- end end