Compare commits
No commits in common. "04d25127fcb6ec4e0c79af0c8df233fec5d81ba5" and "80015dbe621b41ccef165bdf5fbed7fc74764e4d" have entirely different histories.
04d25127fc
...
80015dbe62
|
@ -1,67 +0,0 @@
|
||||||
---@class BatRenderState
|
|
||||||
---@field batBase XyPair
|
|
||||||
---@field batTip XyPair
|
|
||||||
---@field batAngleDeg number
|
|
||||||
---@field batSpeed number
|
|
||||||
|
|
||||||
---@class Batting
|
|
||||||
---@field private Baserunning
|
|
||||||
---@field state BatRenderState Is updated by checkForHit()
|
|
||||||
Batting = {}
|
|
||||||
|
|
||||||
local SwingBackDeg <const> = 30
|
|
||||||
local SwingForwardDeg <const> = 170
|
|
||||||
local OffscreenPos <const> = utils.xy(-999, -999)
|
|
||||||
|
|
||||||
---@param baserunning Baserunning
|
|
||||||
function Batting.new(baserunning)
|
|
||||||
return setmetatable({
|
|
||||||
baserunning = baserunning,
|
|
||||||
state = {
|
|
||||||
batAngleDeg = 0,
|
|
||||||
batSpeed = 0,
|
|
||||||
batTip = OffscreenPos,
|
|
||||||
batBase = OffscreenPos,
|
|
||||||
},
|
|
||||||
}, { __index = Batting })
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO? Make the bat angle work more like the throw meter.
|
|
||||||
-- Would instead constantly drift toward a default value, giving us a little more control,
|
|
||||||
-- and letting the user find a crank position and direction that works for them
|
|
||||||
|
|
||||||
--- Assumes the bat is being held by self.baserunning.batter
|
|
||||||
---@param batDeg number
|
|
||||||
---@param batSpeed number
|
|
||||||
---@param ball Point3d
|
|
||||||
---@return XyPair | nil, boolean, number | nil Ball destination or nil if no hit, true only if batter swung, power mult
|
|
||||||
function Batting:checkForHit(batDeg, batSpeed, ball)
|
|
||||||
local batter = self.baserunning.batter
|
|
||||||
local isSwinging = batDeg > SwingBackDeg and batDeg < SwingForwardDeg
|
|
||||||
local batRadians = math.rad(batDeg)
|
|
||||||
|
|
||||||
local base = batter and utils.xy(batter.x + C.BatterHandPos.x, batter.y + C.BatterHandPos.y) or OffscreenPos
|
|
||||||
local tip = utils.xy(base.x + (C.BatLength * math.sin(batRadians)), base.y + (C.BatLength * math.cos(batRadians)))
|
|
||||||
|
|
||||||
self.state.batSpeed = batSpeed
|
|
||||||
self.state.batAngleDeg = batDeg
|
|
||||||
self.state.batTip = tip
|
|
||||||
self.state.batBase = base
|
|
||||||
|
|
||||||
local ballWasHit = batSpeed > 0 and ball.y < 232 and utils.pointOnOrUnderLine(ball, base, tip, C.Screen.H)
|
|
||||||
|
|
||||||
if not ballWasHit then
|
|
||||||
return nil, isSwinging
|
|
||||||
end
|
|
||||||
|
|
||||||
local ballAngle = batRadians + math.rad(90)
|
|
||||||
local mult = math.abs(batSpeed / 15)
|
|
||||||
local ballVelX = mult * C.BattingPower * 10 * math.sin(ballAngle)
|
|
||||||
local ballVelY = mult * C.BattingPower * 5 * math.cos(ballAngle)
|
|
||||||
if ballVelY > 0 then
|
|
||||||
ballVelX = ballVelX * -1
|
|
||||||
ballVelY = ballVelY * -1
|
|
||||||
end
|
|
||||||
|
|
||||||
return utils.xy(ball.x + ballVelX, ball.y + ballVelY), isSwinging, mult
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
local gfx <const> = playdate.graphics
|
|
||||||
|
|
||||||
function Ball:draw()
|
|
||||||
gfx.setLineWidth(2)
|
|
||||||
|
|
||||||
gfx.setColor(gfx.kColorWhite)
|
|
||||||
gfx.fillCircleAtPoint(self.x, self.y, self.size)
|
|
||||||
|
|
||||||
gfx.setColor(gfx.kColorBlack)
|
|
||||||
gfx.drawCircleAtPoint(self.x, self.y, self.size)
|
|
||||||
end
|
|
|
@ -7,6 +7,8 @@ Characters = {}
|
||||||
|
|
||||||
local gfx <const> = playdate.graphics
|
local gfx <const> = playdate.graphics
|
||||||
|
|
||||||
|
---@alias BatState { batBase: XyPair, batTip: XyPair, batAngleDeg: number }
|
||||||
|
|
||||||
local GloveSizeX, GloveSizeY <const> = Glove:getSize()
|
local GloveSizeX, GloveSizeY <const> = Glove:getSize()
|
||||||
local GloveOffX, GloveOffY <const> = GloveSizeX / 2, GloveSizeY / 2
|
local GloveOffX, GloveOffY <const> = GloveSizeX / 2, GloveSizeY / 2
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@ function drawFielder(fieldingTeamSprites, fielder, ball, flip)
|
||||||
return drawFielderGlove(ball, x, y)
|
return drawFielderGlove(ball, x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param batState BatRenderState
|
---@param batState BatState
|
||||||
local function drawBat(batState)
|
local function drawBat(batState)
|
||||||
gfx.setLineWidth(7)
|
gfx.setLineWidth(7)
|
||||||
gfx.drawLine(batState.batBase.x, batState.batBase.y, batState.batTip.x, batState.batTip.y)
|
gfx.drawLine(batState.batBase.x, batState.batBase.y, batState.batTip.x, batState.batTip.y)
|
||||||
|
@ -66,7 +68,7 @@ end
|
||||||
|
|
||||||
---@param battingTeamSprites SpriteCollection
|
---@param battingTeamSprites SpriteCollection
|
||||||
---@param batter Runner
|
---@param batter Runner
|
||||||
---@param batState BatRenderState
|
---@param batState BatState
|
||||||
local function drawBatter(battingTeamSprites, batter, batState)
|
local function drawBatter(battingTeamSprites, batter, batState)
|
||||||
local spriteCollection = battingTeamSprites[batter.spriteIndex]
|
local spriteCollection = battingTeamSprites[batter.spriteIndex]
|
||||||
if batState.batAngleDeg > 50 and batState.batAngleDeg < 200 then
|
if batState.batAngleDeg > 50 and batState.batAngleDeg < 200 then
|
||||||
|
@ -89,7 +91,7 @@ end
|
||||||
|
|
||||||
---@param fielding Fielding
|
---@param fielding Fielding
|
||||||
---@param baserunning Baserunning
|
---@param baserunning Baserunning
|
||||||
---@param batState BatRenderState
|
---@param batState BatState
|
||||||
---@param battingTeam TeamId
|
---@param battingTeam TeamId
|
||||||
---@param ball Point3d
|
---@param ball Point3d
|
||||||
---@return Fielder | nil ballHeldBy
|
---@return Fielder | nil ballHeldBy
|
||||||
|
|
|
@ -145,8 +145,7 @@ end
|
||||||
|
|
||||||
local newStats = stats
|
local newStats = stats
|
||||||
|
|
||||||
function drawScoreboard(x, y, statistics, outs, battingTeam, inning)
|
function drawScoreboard(x, y, homeScore, awayScore, outs, battingTeam, inning)
|
||||||
local homeScore, awayScore = utils.totalScores(statistics)
|
|
||||||
if
|
if
|
||||||
newStats.homeScore ~= homeScore
|
newStats.homeScore ~= homeScore
|
||||||
or newStats.awayScore ~= awayScore
|
or newStats.awayScore ~= awayScore
|
||||||
|
|
|
@ -96,7 +96,7 @@ local function updateFielderPosition(deltaSeconds, fielder, ball)
|
||||||
local currentTarget = fielder.targets[#fielder.targets]
|
local currentTarget = fielder.targets[#fielder.targets]
|
||||||
local willMove = utils.moveAtSpeed(nextFielderPos, fielder.speed * deltaSeconds, currentTarget)
|
local willMove = utils.moveAtSpeed(nextFielderPos, fielder.speed * deltaSeconds, currentTarget)
|
||||||
|
|
||||||
if willMove and utils.pointIsAboveLine(nextFielderPos, C.BottomOfOutfieldWall) then
|
if willMove and utils.pointIsSquarelyAboveLine(nextFielderPos, C.BottomOfOutfieldWall) then
|
||||||
local targetCount = #fielder.targets
|
local targetCount = #fielder.targets
|
||||||
-- Back up a little
|
-- Back up a little
|
||||||
fielder.targets[targetCount + 2] = utils.xy(fielder.x, fielder.y + 5)
|
fielder.targets[targetCount + 2] = utils.xy(fielder.x, fielder.y + 5)
|
||||||
|
|
81
src/main.lua
81
src/main.lua
|
@ -15,7 +15,7 @@ import 'CoreLibs/utilities/where.lua'
|
||||||
|
|
||||||
---@class InputHandler
|
---@class InputHandler
|
||||||
---@field update fun(self, deltaSeconds: number)
|
---@field update fun(self, deltaSeconds: number)
|
||||||
---@field updateBatAngle fun(self, ball: Ball, pitchIsOver: boolean, deltaSeconds: number)
|
---@field updateBat fun(self, ball: Ball, pitchIsOver: boolean, deltaSeconds: number)
|
||||||
---@field runningSpeed fun(self, runner: Runner, ball: Ball)
|
---@field runningSpeed fun(self, runner: Runner, ball: Ball)
|
||||||
---@field pitch fun(self)
|
---@field pitch fun(self)
|
||||||
---@field fielderAction fun(self, fielderHoldingBall: Fielder | nil, outedSomeRunner: boolean, ball: Ball)
|
---@field fielderAction fun(self, fielderHoldingBall: Fielder | nil, outedSomeRunner: boolean, ball: Ball)
|
||||||
|
@ -40,7 +40,6 @@ import 'action-queue.lua'
|
||||||
import 'announcer.lua'
|
import 'announcer.lua'
|
||||||
import 'ball.lua'
|
import 'ball.lua'
|
||||||
import 'baserunning.lua'
|
import 'baserunning.lua'
|
||||||
import 'batting.lua'
|
|
||||||
import 'dbg.lua'
|
import 'dbg.lua'
|
||||||
import 'fielding.lua'
|
import 'fielding.lua'
|
||||||
import 'graphics.lua'
|
import 'graphics.lua'
|
||||||
|
@ -49,7 +48,6 @@ import 'pitching.lua'
|
||||||
import 'statistics.lua'
|
import 'statistics.lua'
|
||||||
import 'user-input.lua'
|
import 'user-input.lua'
|
||||||
|
|
||||||
import 'draw/ball.lua'
|
|
||||||
import 'draw/box-score.lua'
|
import 'draw/box-score.lua'
|
||||||
import 'draw/fans.lua'
|
import 'draw/fans.lua'
|
||||||
import 'draw/characters.lua'
|
import 'draw/characters.lua'
|
||||||
|
@ -95,17 +93,15 @@ local teams <const> = {
|
||||||
---@field offenseState OffenseState
|
---@field offenseState OffenseState
|
||||||
---@field inning number
|
---@field inning number
|
||||||
---@field stats Statistics
|
---@field stats Statistics
|
||||||
|
---@field batBase XyPair
|
||||||
--- Ephemeral data ONLY used during rendering
|
---@field batTip XyPair
|
||||||
---@class RenderState
|
---@field batAngleDeg number
|
||||||
---@field bat BatRenderState
|
|
||||||
|
|
||||||
---@class Game
|
---@class Game
|
||||||
---@field private settings Settings
|
---@field private settings Settings
|
||||||
---@field private announcer Announcer
|
---@field private announcer Announcer
|
||||||
---@field private fielding Fielding
|
---@field private fielding Fielding
|
||||||
---@field private baserunning Baserunning
|
---@field private baserunning Baserunning
|
||||||
---@field private batting Batting
|
|
||||||
---@field private characters Characters
|
---@field private characters Characters
|
||||||
---@field private npc InputHandler
|
---@field private npc InputHandler
|
||||||
---@field private userInput InputHandler
|
---@field private userInput InputHandler
|
||||||
|
@ -127,12 +123,16 @@ function Game.new(settings, announcer, fielding, baserunning, npc, state)
|
||||||
|
|
||||||
local battingTeam = "away"
|
local battingTeam = "away"
|
||||||
local ball = Ball.new(gfx.animator)
|
local ball = Ball.new(gfx.animator)
|
||||||
|
|
||||||
local o = setmetatable({
|
local o = setmetatable({
|
||||||
settings = settings,
|
settings = settings,
|
||||||
announcer = announcer,
|
announcer = announcer,
|
||||||
fielding = fielding,
|
fielding = fielding,
|
||||||
panner = Panner.new(ball),
|
panner = Panner.new(ball),
|
||||||
state = state or {
|
state = state or {
|
||||||
|
batBase = utils.xy(C.Center.x - 34, 215),
|
||||||
|
batTip = utils.xy(0, 0),
|
||||||
|
batAngleDeg = C.CrankOffsetDeg,
|
||||||
deltaSeconds = 0,
|
deltaSeconds = 0,
|
||||||
ball = ball,
|
ball = ball,
|
||||||
battingTeam = battingTeam,
|
battingTeam = battingTeam,
|
||||||
|
@ -147,7 +147,6 @@ function Game.new(settings, announcer, fielding, baserunning, npc, state)
|
||||||
o.baserunning = baserunning or Baserunning.new(announcer, function()
|
o.baserunning = baserunning or Baserunning.new(announcer, function()
|
||||||
o:nextHalfInning()
|
o:nextHalfInning()
|
||||||
end)
|
end)
|
||||||
o.batting = Batting.new(o.baserunning)
|
|
||||||
o.userInput = UserInput.new(function(throwFly, forbidThrowHome)
|
o.userInput = UserInput.new(function(throwFly, forbidThrowHome)
|
||||||
return o:buttonControlledThrow(throwFly, forbidThrowHome)
|
return o:buttonControlledThrow(throwFly, forbidThrowHome)
|
||||||
end)
|
end)
|
||||||
|
@ -362,7 +361,7 @@ function Game:strikeOut()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Game:saveToFile()
|
function Game:saveToFile()
|
||||||
playdate.datastore.write({ currentGame = self.state }, "data", true)
|
playdate.datastore.write({ currentGame = self }, "data", true)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Game.load()
|
function Game.load()
|
||||||
|
@ -380,41 +379,71 @@ function Game.load()
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local SwingBackDeg <const> = 30
|
||||||
|
local SwingForwardDeg <const> = 170
|
||||||
|
|
||||||
---@param offenseHandler InputHandler
|
---@param offenseHandler InputHandler
|
||||||
function Game:updateBatting(offenseHandler)
|
function Game:updateBatting(offenseHandler)
|
||||||
local ball = self.state.ball
|
local ball = self.state.ball
|
||||||
local batDeg, batSpeed = offenseHandler:updateBatAngle(ball, self.state.pitchIsOver, self.state.deltaSeconds)
|
local batDeg, batSpeed = offenseHandler:updateBat(ball, self.state.pitchIsOver, self.state.deltaSeconds)
|
||||||
local ballDest, isSwinging, mult = self.batting:checkForHit(batDeg, batSpeed, ball)
|
self.state.batAngleDeg = batDeg
|
||||||
self.state.didSwing = self.state.didSwing or (isSwinging and not self.state.pitchIsOver)
|
|
||||||
|
|
||||||
if not ballDest then
|
if not self.state.pitchIsOver and batDeg > SwingBackDeg and batDeg < SwingForwardDeg then
|
||||||
|
self.state.didSwing = true
|
||||||
|
end
|
||||||
|
-- TODO? Make the bat angle work more like the throw meter.
|
||||||
|
-- Would instead constantly drift toward a default value, giving us a little more control,
|
||||||
|
-- and letting the user find a crank position and direction that works for them
|
||||||
|
local batAngle = math.rad(batDeg)
|
||||||
|
-- TODO: animate bat-flip or something
|
||||||
|
local batter = self.baserunning.batter
|
||||||
|
self.state.batBase.x = batter and (batter.x + C.BatterHandPos.x) or -999
|
||||||
|
self.state.batBase.y = batter and (batter.y + C.BatterHandPos.y) or -999
|
||||||
|
self.state.batTip.x = self.state.batBase.x + (C.BatLength * math.sin(batAngle))
|
||||||
|
self.state.batTip.y = self.state.batBase.y + (C.BatLength * math.cos(batAngle))
|
||||||
|
|
||||||
|
local ballWasHit = batSpeed > 0
|
||||||
|
and ball.y < 232
|
||||||
|
and utils.pointDirectlyUnderLine(ball, self.state.batBase, self.state.batTip, C.Screen.H)
|
||||||
|
|
||||||
|
if not ballWasHit then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Hit!
|
-- Hit!
|
||||||
-- TODO: animate bat-flip or something
|
|
||||||
self:saveToFile()
|
self:saveToFile()
|
||||||
BatCrackReverb:play()
|
BatCrackReverb:play()
|
||||||
self.state.offenseState = C.Offense.running
|
self.state.offenseState = C.Offense.running
|
||||||
|
|
||||||
|
local ballAngle = batAngle + math.rad(90)
|
||||||
|
local mult = math.abs(batSpeed / 15)
|
||||||
|
local ballVelX = mult * C.BattingPower * 10 * math.sin(ballAngle)
|
||||||
|
local ballVelY = mult * C.BattingPower * 5 * math.cos(ballAngle)
|
||||||
|
if ballVelY > 0 then
|
||||||
|
ballVelX = ballVelX * -1
|
||||||
|
ballVelY = ballVelY * -1
|
||||||
|
end
|
||||||
|
|
||||||
|
local ballDest = utils.xy(ball.x + ballVelX, ball.y + ballVelY)
|
||||||
|
|
||||||
pitchTracker:reset()
|
pitchTracker:reset()
|
||||||
local flyTimeMs = 2000
|
local flyTimeMs = 2000
|
||||||
-- TODO? A dramatic eye-level view on a home-run could be sick.
|
-- TODO? A dramatic eye-level view on a home-run could be sick.
|
||||||
local battingTeamStats = self:battingTeamCurrentInning()
|
local battingTeamStats = self:battingTeamCurrentInning()
|
||||||
battingTeamStats.hits[#battingTeamStats.hits + 1] = ballDest
|
battingTeamStats.hits[#battingTeamStats.hits + 1] = ballDest
|
||||||
|
|
||||||
if utils.isFoulBall(ballDest) then
|
if utils.isFoulBall(ballDest.x, ballDest.y) then
|
||||||
self.announcer:say("Foul ball!")
|
self.announcer:say("Foul ball!")
|
||||||
pitchTracker.strikes = math.min(pitchTracker.strikes + 1, 2)
|
pitchTracker.strikes = math.min(pitchTracker.strikes + 1, 2)
|
||||||
-- TODO: Have a fielder chase for the fly-out
|
-- TODO: Have a fielder chase for the fly-out
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local isHomeRun = utils.pointIsAboveLine(ballDest, C.OutfieldWall)
|
local isHomeRun = utils.pointIsSquarelyAboveLine(ballDest, C.OutfieldWall)
|
||||||
if isHomeRun then
|
if isHomeRun then
|
||||||
playdate.timer.new(flyTimeMs, function()
|
playdate.timer.new(flyTimeMs, function()
|
||||||
-- Verify that the home run wasn't intercepted
|
-- Verify that the home run wasn't intercepted
|
||||||
if utils.distanceBetweenPoints(ball, ballDest) < 2 then
|
if utils.within(1, ball.x, ballDest.x) and utils.within(1, ball.y, ballDest.y) then
|
||||||
self.announcer:say("HOME RUN!")
|
self.announcer:say("HOME RUN!")
|
||||||
self.state.offenseState = C.Offense.homeRun
|
self.state.offenseState = C.Offense.homeRun
|
||||||
-- Linger on the home-run ball for a moment, before panning to the bases.
|
-- Linger on the home-run ball for a moment, before panning to the bases.
|
||||||
|
@ -562,15 +591,22 @@ function Game:update()
|
||||||
fans.draw()
|
fans.draw()
|
||||||
GrassBackground:draw(-400, -720)
|
GrassBackground:draw(-400, -720)
|
||||||
|
|
||||||
local ballHeldBy =
|
local ball = state.ball
|
||||||
self.characters:drawAll(self.fielding, self.baserunning, self.batting.state, state.battingTeam, state.ball)
|
|
||||||
|
local ballHeldBy = self.characters:drawAll(self.fielding, self.baserunning, state, state.battingTeam, ball)
|
||||||
|
|
||||||
if self:userIsOn("defense") then
|
if self:userIsOn("defense") then
|
||||||
throwMeter:drawNearFielder(ballHeldBy)
|
throwMeter:drawNearFielder(ballHeldBy)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not ballHeldBy then
|
if not ballHeldBy then
|
||||||
state.ball:draw()
|
gfx.setLineWidth(2)
|
||||||
|
|
||||||
|
gfx.setColor(gfx.kColorWhite)
|
||||||
|
gfx.fillCircleAtPoint(ball.x, ball.y, ball.size)
|
||||||
|
|
||||||
|
gfx.setColor(gfx.kColorBlack)
|
||||||
|
gfx.drawCircleAtPoint(ball.x, ball.y, ball.size)
|
||||||
end
|
end
|
||||||
|
|
||||||
gfx.setDrawOffset(0, 0)
|
gfx.setDrawOffset(0, 0)
|
||||||
|
@ -578,7 +614,8 @@ function Game:update()
|
||||||
drawMinimap(self.baserunning.runners, self.fielding.fielders)
|
drawMinimap(self.baserunning.runners, self.fielding.fielders)
|
||||||
end
|
end
|
||||||
|
|
||||||
drawScoreboard(0, C.Screen.H * 0.77, state.stats, self.baserunning.outs, state.battingTeam, state.inning)
|
local homeScore, awayScore = utils.totalScores(state.stats)
|
||||||
|
drawScoreboard(0, C.Screen.H * 0.77, homeScore, awayScore, self.baserunning.outs, state.battingTeam, state.inning)
|
||||||
drawBallsAndStrikes(290, C.Screen.H - 20, pitchTracker.balls, pitchTracker.strikes)
|
drawBallsAndStrikes(290, C.Screen.H - 20, pitchTracker.balls, pitchTracker.strikes)
|
||||||
self.announcer:draw(C.Center.x, 10)
|
self.announcer:draw(C.Center.x, 10)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ function Npc.update() end
|
||||||
---@param pitchIsOver boolean
|
---@param pitchIsOver boolean
|
||||||
---@param deltaSec number
|
---@param deltaSec number
|
||||||
---@return number batAngleDeg, number batSpeed
|
---@return number batAngleDeg, number batSpeed
|
||||||
function Npc:updateBatAngle(ball, pitchIsOver, deltaSec)
|
function Npc:updateBat(ball, pitchIsOver, deltaSec)
|
||||||
if not pitchIsOver and ball.y > 200 and ball.y < 230 and (ball.x < C.Center.x + 15) then
|
if not pitchIsOver and ball.y > 200 and ball.y < 230 and (ball.x < C.Center.x + 15) then
|
||||||
npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed)
|
npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed)
|
||||||
else
|
else
|
||||||
|
|
|
@ -11,16 +11,14 @@ end
|
||||||
|
|
||||||
function UserInput:update()
|
function UserInput:update()
|
||||||
self.crankChange = playdate.getCrankChange()
|
self.crankChange = playdate.getCrankChange()
|
||||||
self.crankLimited = self.crankChange == 0 and 0 or (math.log(math.abs(self.crankChange)) * C.CrankPower)
|
local crankLimited = self.crankChange == 0 and 0 or (math.log(math.abs(self.crankChange)) * C.CrankPower)
|
||||||
if self.crankChange < 0 then
|
self.crankLimited = math.abs(crankLimited)
|
||||||
self.crankLimited = self.crankLimited * -1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return number batAngleDeg, number batSpeed
|
---@return number batAngleDeg, number batSpeed
|
||||||
function UserInput:updateBatAngle()
|
function UserInput:updateBat()
|
||||||
local batAngleDeg = (playdate.getCrankPosition() + C.CrankOffsetDeg) % 360
|
local batAngleDeg = (playdate.getCrankPosition() + C.CrankOffsetDeg) % 360
|
||||||
local batSpeed = math.abs(self.crankLimited)
|
local batSpeed = self.crankLimited
|
||||||
return batAngleDeg, batSpeed
|
return batAngleDeg, batSpeed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,14 @@ function utils.moveAtSpeed(mover, speed, target, tau)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param acceptableGap number
|
||||||
|
---@param n1 number
|
||||||
|
---@param n2 number
|
||||||
|
---@return boolean n1 is within acceptableGap of n2
|
||||||
|
function utils.within(acceptableGap, n1, n2)
|
||||||
|
return math.abs(n1 - n2) < acceptableGap
|
||||||
|
end
|
||||||
|
|
||||||
---@generic T
|
---@generic T
|
||||||
---@param array T[]
|
---@param array T[]
|
||||||
---@param condition fun(T): boolean
|
---@param condition fun(T): boolean
|
||||||
|
@ -175,26 +183,22 @@ end
|
||||||
---@param line2 XyPair
|
---@param line2 XyPair
|
||||||
---@param bottomBound number
|
---@param bottomBound number
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function utils.pointOnOrUnderLine(point, line1, line2, bottomBound)
|
function utils.pointDirectlyUnderLine(point, line1, line2, bottomBound)
|
||||||
-- This check currently assumes right-handedness.
|
-- This check currently assumes right-handedness.
|
||||||
-- I.e. it assumes the ball is to the right of batBaseX
|
-- I.e. it assumes the ball is to the right of batBaseX
|
||||||
if point.x < line1.x or point.x > line2.x or point.y > bottomBound then
|
if point.x < line1.x or point.x > line2.x or point.y > bottomBound then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return utils.pointUnderLine(point.x, point.y, line1.x, line1.y - 2, line2.x, line2.y - 2)
|
return utils.pointUnderLine(point.x, point.y, line1.x, line1.y, line2.x, line2.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns true if the given point is anywhere above the given line, with no upper bound.
|
--- Returns true if the given point is anywhere above the given line, with no upper bound.
|
||||||
--- This, used for home run calculations, does not *precesely* take into account balls that curve around the foul poles.
|
--- This, if used for home run calculations, would not take into account balls that curve around the foul poles.
|
||||||
--- If left of first linePoint and above it, returns true. Similarly if right of the last linePoint.
|
|
||||||
---@param point XyPair
|
---@param point XyPair
|
||||||
---@param linePoints XyPair[]
|
---@param linePoints XyPair[]
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function utils.pointIsAboveLine(point, linePoints)
|
function utils.pointIsSquarelyAboveLine(point, linePoints)
|
||||||
if point.x < linePoints[1].x and point.y < linePoints[1].y then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
for i = 2, #linePoints do
|
for i = 2, #linePoints do
|
||||||
local prev = linePoints[i - 1]
|
local prev = linePoints[i - 1]
|
||||||
local next = linePoints[i]
|
local next = linePoints[i]
|
||||||
|
@ -202,9 +206,6 @@ function utils.pointIsAboveLine(point, linePoints)
|
||||||
return not utils.pointUnderLine(point.x, point.y, prev.x, prev.y, next.x, next.y)
|
return not utils.pointUnderLine(point.x, point.y, prev.x, prev.y, next.x, next.y)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if point.x > linePoints[#linePoints].x and point.y < linePoints[#linePoints].y then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -230,13 +231,14 @@ function utils.pointUnderLine(pointX, pointY, lineX1, lineY1, lineX2, lineY2)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns true if a ball landing at destX,destY will be foul.
|
--- Returns true if a ball landing at destX,destY will be foul.
|
||||||
---@param dest XyPair
|
---@param destX number
|
||||||
function utils.isFoulBall(dest)
|
---@param destY number
|
||||||
|
function utils.isFoulBall(destX, destY)
|
||||||
local leftLine = C.LeftFoulLine
|
local leftLine = C.LeftFoulLine
|
||||||
local rightLine = C.RightFoulLine
|
local rightLine = C.RightFoulLine
|
||||||
|
|
||||||
return utils.pointUnderLine(dest.x, dest.y, leftLine.x1, leftLine.y1, leftLine.x2, leftLine.y2)
|
return utils.pointUnderLine(destX, destY, leftLine.x1, leftLine.y1, leftLine.x2, leftLine.y2)
|
||||||
or utils.pointUnderLine(dest.x, dest.y, rightLine.x1, rightLine.y1, rightLine.x2, rightLine.y2)
|
or utils.pointUnderLine(destX, destY, rightLine.x1, rightLine.y1, rightLine.x2, rightLine.y2)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the nearest position object from the given point, as well as its distance from that point
|
--- Returns the nearest position object from the given point, as well as its distance from that point
|
||||||
|
|
Loading…
Reference in New Issue