diff --git a/src/images/game/grass.png b/src/images/game/grass.png index ab545fb..f19739d 100644 Binary files a/src/images/game/grass.png and b/src/images/game/grass.png differ diff --git a/src/main.lua b/src/main.lua index 996fe1d..76d42e1 100644 --- a/src/main.lua +++ b/src/main.lua @@ -3,6 +3,7 @@ import 'CoreLibs/easing.lua' import 'CoreLibs/graphics.lua' import 'CoreLibs/object.lua' import 'CoreLibs/ui.lua' +import 'utils.lua' local gfx = playdate.graphics playdate.display.setRefreshRate(50) @@ -16,7 +17,10 @@ local playerImage = playerHighHat local secPerFrame = 0.1 local playerFrameElapsed = 0 -local backgroundPan = -240 +local backgroundPan = { + x = 0, + y = 0, +} local pitchFlyTimeMs = 2500 local ballStartY, endY = 90, 250 @@ -41,8 +45,8 @@ local batTipX = 0 local batTipY = 0 local MODES = { - batting = 0, - running = 1 + batting = {}, + running = {} } local currentMode = MODES.batting @@ -53,7 +57,7 @@ local hitMult = 10 local deltaTime = 0 -local basePositions = { +local bases = { first = { x = screenW * 0.93, y = screenH * 0.52 }, second = { x = screenW * 0.47, y = screenH * 0.19 }, third = { x = screenW * 0.03, y = screenH * 0.52 }, @@ -85,8 +89,8 @@ function resetFielderPositions() end resetFielderPositions() -local playerStartingX = basePositions.home.x - 40 -local playerStartingY = basePositions.home.y - 3 +local playerStartingX = bases.home.x - 40 +local playerStartingY = bases.home.y - 3 local player = { x = playerStartingX, y = playerStartingY, @@ -109,10 +113,6 @@ 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() @@ -121,6 +121,9 @@ function pitch() ballX = 200 currentMode = MODES.batting + backgroundPan.y = 0 + backgroundPan.x = 0 + -- TODO: Add new runners, instead runners = {} player.x = playerStartingX @@ -151,25 +154,6 @@ local pitchClockSec = 99 local elapsedTime = 0 local crankChange -function ballPassedThruBat(ballX, ballY, batBaseX, batBaseY, batTipX, batTipY) - -- This check currently assumes right-handedness. - -- I.e. it assumes the ball is to the right of batBaseX - if ballX < batBaseX or ballX > batTipX or ballY > screenH then - return false - end - - local m = (batTipY - batBaseY) / (batTipX - batBaseX) - - -- y = mx + b - -- b = y1 - (m * x1) - local b = batBaseY - (m * batBaseX) - local yOnLine = (m * ballX) + b - local yP = ballY - local yDelta = yOnLine - yP - - return yDelta <= 0 -end - function updateInfield() if ballDestX == nil or ballDestY == nil then return @@ -200,7 +184,15 @@ function updateRunners() 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 + runner.hasArrived = distance <= 1 + + if runner.hasArrived then + -- if runner.onArrive then + -- runner.onArrive() + -- end + runner.targetX = nil + runner.targetY = nil + else local mult = 1 if crankChange < 0 then mult = -1 @@ -208,31 +200,14 @@ function updateRunners() 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 - return math.sqrt((a*a) + (b*b)), a, b -end - function getNearestFielder(x, y) local nearestFielder, nearestDistance = nil, nil - for title,fielder in pairs(fielders) do + for _, fielder in pairs(fielders) do if nearestFielder == nil then nearestFielder = fielder nearestDistance = distanceBetween(fielder.x, fielder.y, x, y) @@ -256,6 +231,15 @@ function throwArrivedBeforeRunner() return false end +function getForcedOutTargets() + return { bases.first } +end + +function getNextThrowTarget() + local targets = getForcedOutTargets() + return targets[1].x, targets[1].y +end + function updateGameState() deltaTime = playdate.getElapsedTime() playdate.resetElapsedTime() @@ -281,7 +265,7 @@ function updateGameState() crankChange, acceleratedChange = playdate.getCrankChange() if currentMode == MODES.batting and acceleratedChange >= 0 and - ballPassedThruBat(ballX, ballY, batBaseX, batBaseY, batTipX, batTipY) then + pointDirectlyUnderLine(ballX, ballY, batBaseX, batBaseY, batTipX, batTipY, screenH) then ballAngle = batAngle + math.rad(90) mult = math.abs(acceleratedChange / 15) @@ -301,10 +285,10 @@ function updateGameState() throwBall(getNextThrowTarget()) chasingFielder.onArrive = nil end - fielders.first.targetX = basePositions.first.x - fielders.first.targetY = basePositions.first.y + fielders.first.targetX = bases.first.x + fielders.first.targetY = bases.first.y currentMode = MODES.running - player.nextBase = basePositions.first + player.nextBase = bases.first runners[#runners+1] = player end @@ -312,32 +296,52 @@ function updateGameState() updateRunners() updateInfield() end + + -- TODO: Show baserunning minimap when panning? + local ballBuffer = 5 + if ballY < ballBuffer then + backgroundPan.y = math.max(ballBuffer, -1 * (ballY - ballBuffer)) + else backgroundPan.y = 0 + end + if ballX < ballBuffer then + backgroundPan.x = math.max(-400, -1 * (ballX - ballBuffer)) + elseif ballX > (screenW - ballBuffer) then + backgroundPan.x = math.min(800, -1 * (ballX - ballBuffer)) + end + + if ballX > 0 and ballX < (screenW - ballBuffer) then + backgroundPan.x = 0 + end end function playdate.update() updateGameState() - grassBackground:draw(0, backgroundPan) + gfx.clear() + grassBackground:draw(backgroundPan.x - 400, backgroundPan.y - 240) gfx.setColor(gfx.kColorBlack) gfx.setLineWidth(2) - gfx.drawCircleAtPoint(ballX, ballY, ballSize) + gfx.drawCircleAtPoint(ballX + backgroundPan.x, ballY + backgroundPan.y, ballSize) for title,fielder in pairs(fielders) do - gfx.fillRect(fielder.x, fielder.y, 14, 25) + gfx.fillRect(fielder.x + backgroundPan.x, fielder.y + backgroundPan.y, 14, 25) end gfx.setLineWidth(5) if currentMode == MODES.batting then - gfx.drawLine(batBaseX, batBaseY, batTipX, batTipY) + gfx.drawLine( + batBaseX + backgroundPan.x, batBaseY + backgroundPan.y, + batTipX + backgroundPan.x, batTipY + backgroundPan.y + ) end if playdate.isCrankDocked() then playdate.ui.crankIndicator:draw() end - playerImage:draw(player.x, player.y) + playerImage:draw(player.x + backgroundPan.x, player.y + backgroundPan.y) -- TODO: Use gfx.animation.blinker instead if currentMode == MODES.running and playerFrameElapsed > secPerFrame then diff --git a/src/utils.lua b/src/utils.lua new file mode 100644 index 0000000..f53fff3 --- /dev/null +++ b/src/utils.lua @@ -0,0 +1,31 @@ +--- Returns the normalized vector as two values, plus the distance between the given points. +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 + return math.sqrt((a*a) + (b*b)), a, b +end + +function pointDirectlyUnderLine(pointX, pointY, lineX1, lineY1, lineX2, lineY2, bottomBound) + -- This check currently assumes right-handedness. + -- I.e. it assumes the ball is to the right of batBaseX + if pointX < lineX1 or pointX > lineX2 or pointY > bottomBound then + return false + end + + local m = (lineY2 - lineY1) / (lineX2 - lineX1) + + -- y = mx + b + -- b = y1 - (m * x1) + local b = lineY1 - (m * lineX1) + local yOnLine = (m * pointX) + b + local yP = pointY + local yDelta = yOnLine - yP + + return yDelta <= 0 +end +