Try to cluster global state together.
Start peeling out fielder functions into a new file. A bit more constant use. In Makefile, parse main.lua imports for source files.
This commit is contained in:
parent
324673ea98
commit
66bd97499a
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
||||||
SOURCE_FILES := src/utils.lua src/constants.lua src/assets.lua src/draw/* src/dbg.lua src/npc.lua src/announcer.lua src/graphics.lua src/main.lua
|
SOURCE_FILES := $(shell grep "import '" src/main.lua | grep -v CoreLibs | sed "s/.*'\(.*\)'.*/\1/") main.lua
|
||||||
GENERATED_FILES := src/assets.lua
|
GENERATED_FILES := src/assets.lua
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
|
|
@ -98,3 +98,5 @@ C.CrankPower = 10
|
||||||
|
|
||||||
--- How fast baserunners move after a walk
|
--- How fast baserunners move after a walk
|
||||||
C.WalkedRunnerSpeed = 10
|
C.WalkedRunnerSpeed = 10
|
||||||
|
|
||||||
|
C.ResetFieldersAfterSeconds = 3
|
||||||
|
|
|
@ -21,9 +21,9 @@ end
|
||||||
-- Only works if called with the bases empty (i.e. the only runner should be the batter.
|
-- Only works if called with the bases empty (i.e. the only runner should be the batter.
|
||||||
-- selene: allow(unused_variable)
|
-- selene: allow(unused_variable)
|
||||||
function dbg.loadTheBases(runners)
|
function dbg.loadTheBases(runners)
|
||||||
newRunner()
|
utils.newRunner()
|
||||||
newRunner()
|
utils.newRunner()
|
||||||
newRunner()
|
utils.newRunner()
|
||||||
runners[2].x = C.Bases[C.First].x
|
runners[2].x = C.Bases[C.First].x
|
||||||
runners[2].y = C.Bases[C.First].y
|
runners[2].y = C.Bases[C.First].y
|
||||||
runners[2].nextBase = C.Bases[C.Second]
|
runners[2].nextBase = C.Bases[C.Second]
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
---@param name string
|
||||||
|
---@param speed number
|
||||||
|
---@return Fielder
|
||||||
|
function newFielder(name, speed)
|
||||||
|
return {
|
||||||
|
name = name,
|
||||||
|
speed = speed,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- selene: allow(unscoped_variables)
|
||||||
|
Field = {
|
||||||
|
fielders = {
|
||||||
|
first = newFielder("First", 40),
|
||||||
|
second = newFielder("Second", 40),
|
||||||
|
shortstop = newFielder("Shortstop", 40),
|
||||||
|
third = newFielder("Third", 40),
|
||||||
|
pitcher = newFielder("Pitcher", 30),
|
||||||
|
catcher = newFielder("Catcher", 35),
|
||||||
|
left = newFielder("Left", 40),
|
||||||
|
center = newFielder("C.Center", 40),
|
||||||
|
right = newFielder("Right", 40),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Actually only benches the infield, because outfielders are far away!
|
||||||
|
---@param position XYPair
|
||||||
|
function Field.benchTo(self, position)
|
||||||
|
self.fielders.first.target = position
|
||||||
|
self.fielders.second.target = position
|
||||||
|
self.fielders.shortstop.target = position
|
||||||
|
self.fielders.third.target = position
|
||||||
|
self.fielders.pitcher.target = position
|
||||||
|
self.fielders.catcher.target = position
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Resets the target positions of all fielders to their defaults (at their field positions).
|
||||||
|
---@param fromOffTheField XYPair | nil If provided, also sets all runners' current position to one centralized location.
|
||||||
|
function Field.resetFielderPositions(self, fromOffTheField)
|
||||||
|
if fromOffTheField then
|
||||||
|
for _, fielder in pairs(self.fielders) do
|
||||||
|
fielder.x = fromOffTheField.x
|
||||||
|
fielder.y = fromOffTheField.y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.fielders.first.target = utils.xy(C.Screen.W - 65, C.Screen.H * 0.48)
|
||||||
|
self.fielders.second.target = utils.xy(C.Screen.W * 0.70, C.Screen.H * 0.30)
|
||||||
|
self.fielders.shortstop.target = utils.xy(C.Screen.W * 0.30, C.Screen.H * 0.30)
|
||||||
|
self.fielders.third.target = utils.xy(C.Screen.W * 0.1, C.Screen.H * 0.48)
|
||||||
|
self.fielders.pitcher.target = utils.xy(C.PitcherStartPos.x, C.PitcherStartPos.y)
|
||||||
|
self.fielders.catcher.target = utils.xy(C.Screen.W * 0.475, C.Screen.H * 0.92)
|
||||||
|
self.fielders.left.target = utils.xy(C.Screen.W * -1, C.Screen.H * -0.2)
|
||||||
|
self.fielders.center.target = utils.xy(C.Center.x, C.Screen.H * -0.4)
|
||||||
|
self.fielders.right.target = utils.xy(C.Screen.W * 2, self.fielders.left.target.y)
|
||||||
|
end
|
|
@ -2,19 +2,19 @@
|
||||||
--- XXX
|
--- XXX
|
||||||
--- XOX
|
--- XOX
|
||||||
--- Where each character is the size of the screen, and 'O' is the default view.
|
--- Where each character is the size of the screen, and 'O' is the default view.
|
||||||
function getDrawOffset(screenW, screenH, ballX, ballY)
|
function getDrawOffset(ballX, ballY)
|
||||||
local offsetX, offsetY
|
local offsetX, offsetY
|
||||||
if ballY > screenH then
|
if ballY > C.Screen.H or ballX >= C.BallOffscreen then
|
||||||
return 0, 0
|
return 0, 0
|
||||||
end
|
end
|
||||||
offsetY = math.max(0, -1 * ballY)
|
offsetY = math.max(0, -1 * ballY)
|
||||||
|
|
||||||
if ballX > 0 and ballX < screenW then
|
if ballX > 0 and ballX < C.Screen.W then
|
||||||
offsetX = 0
|
offsetX = 0
|
||||||
elseif ballX < 0 then
|
elseif ballX < 0 then
|
||||||
offsetX = math.max(-1 * screenW, ballX * -1)
|
offsetX = math.max(-1 * C.Screen.W, ballX * -1)
|
||||||
elseif ballX > screenW then
|
elseif ballX > C.Screen.W then
|
||||||
offsetX = math.min(screenW * 2, (ballX * -1) + screenW)
|
offsetX = math.min(C.Screen.W * 2, (ballX * -1) + C.Screen.W)
|
||||||
end
|
end
|
||||||
|
|
||||||
return offsetX * 1.3, offsetY * 1.5
|
return offsetX * 1.3, offsetY * 1.5
|
||||||
|
|
248
src/main.lua
248
src/main.lua
|
@ -30,10 +30,11 @@ import 'assets.lua'
|
||||||
|
|
||||||
import 'announcer.lua'
|
import 'announcer.lua'
|
||||||
import 'dbg.lua'
|
import 'dbg.lua'
|
||||||
|
import 'field.lua'
|
||||||
import 'graphics.lua'
|
import 'graphics.lua'
|
||||||
import 'npc.lua'
|
import 'npc.lua'
|
||||||
import 'draw/overlay'
|
import 'draw/overlay.lua'
|
||||||
import 'draw/fielder'
|
import 'draw/fielder.lua'
|
||||||
-- stylua: ignore end
|
-- stylua: ignore end
|
||||||
|
|
||||||
-- selene: allow(shadowing)
|
-- selene: allow(shadowing)
|
||||||
|
@ -44,16 +45,72 @@ local PlayerImageBlipper <const> = blipper.new(100, Player, PlayerLowHat)
|
||||||
local FielderDanceAnimator <const> = gfx.animator.new(1, 10, 0, utils.easingHill)
|
local FielderDanceAnimator <const> = gfx.animator.new(1, 10, 0, utils.easingHill)
|
||||||
FielderDanceAnimator.repeatCount = C.DanceBounceCount - 1
|
FielderDanceAnimator.repeatCount = C.DanceBounceCount - 1
|
||||||
|
|
||||||
function fieldersDance()
|
|
||||||
FielderDanceAnimator:reset(C.DanceBounceMs)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ballAnimatorY = gfx.animator.new(0, C.BallOffscreen, C.BallOffscreen, playdate.easingFunctions.linear)
|
|
||||||
local ballAnimatorX = gfx.animator.new(0, C.BallOffscreen, C.BallOffscreen, playdate.easingFunctions.linear)
|
|
||||||
|
|
||||||
---@alias PseudoAnimator { currentValue: fun(self): number; reset: fun(self, durationMs: number | nil) }
|
---@alias PseudoAnimator { currentValue: fun(self): number; reset: fun(self, durationMs: number | nil) }
|
||||||
---@alias Pitch { x: PseudoAnimator, y: PseudoAnimator, z: PseudoAnimator | nil }
|
---@alias Pitch { x: PseudoAnimator, y: PseudoAnimator, z: PseudoAnimator | nil }
|
||||||
|
|
||||||
|
local ballAnimatorX = utils.staticAnimator(C.BallOffscreen)
|
||||||
|
local ballAnimatorY = utils.staticAnimator(C.BallOffscreen)
|
||||||
|
local ballSizeAnimator = utils.staticAnimator(C.SmallestBallRadius)
|
||||||
|
|
||||||
|
-- TODO? Replace this AND ballSizeAnimator with a ballAnimatorZ?
|
||||||
|
-- ...that might lose some of the magic of both. Compromise available? idk
|
||||||
|
local ballFloatAnimator = gfx.animator.new(2000, -60, 0, utils.easingHill)
|
||||||
|
|
||||||
|
local deltaSeconds = 0
|
||||||
|
|
||||||
|
local BatterHandPos <const> = utils.xy(10, 25)
|
||||||
|
|
||||||
|
local batBase <const> = utils.xy(C.Center.x - 34, 215)
|
||||||
|
local batTip <const> = utils.xy(0, 0)
|
||||||
|
|
||||||
|
local ball <const> = {
|
||||||
|
x = C.Center.x --[[@as number]],
|
||||||
|
y = C.Center.y --[[@as number]],
|
||||||
|
z = 0,
|
||||||
|
size = C.SmallestBallRadius,
|
||||||
|
heldBy = nil --[[@type Runner | nil]],
|
||||||
|
}
|
||||||
|
|
||||||
|
---@alias Team { score: number, benchPosition: XYPair }
|
||||||
|
|
||||||
|
---@type table<string, Team>
|
||||||
|
local teams <const> = {
|
||||||
|
home = {
|
||||||
|
score = 0,
|
||||||
|
benchPosition = utils.xy(C.Screen.W + 10, C.Center.y),
|
||||||
|
},
|
||||||
|
away = {
|
||||||
|
score = 0,
|
||||||
|
benchPosition = utils.xy(-10, C.Center.y),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local PlayerTeam <const> = teams.away
|
||||||
|
local battingTeam = teams.away
|
||||||
|
local outs = 0
|
||||||
|
local inning = 1
|
||||||
|
local offenseMode = C.Offense.batting
|
||||||
|
|
||||||
|
--- @type Runner[]
|
||||||
|
local runners <const> = {}
|
||||||
|
|
||||||
|
--- @type Runner[]
|
||||||
|
local outRunners <const> = {}
|
||||||
|
|
||||||
|
---@type Runner | nil
|
||||||
|
local batter = utils.newRunner(runners)
|
||||||
|
|
||||||
|
local throwMeter = 0
|
||||||
|
|
||||||
|
-- TODO: Replace with a timer, repeatedly reset, instead of setting to 0
|
||||||
|
local secondsSinceLastRunnerMove = 0
|
||||||
|
|
||||||
|
-- TODO: Replace with a timer, repeatedly reset instead of setting to 0
|
||||||
|
local secondsSincePitchAllowed = -5
|
||||||
|
local catcherThrownBall = false
|
||||||
|
|
||||||
|
local batAngleDeg = C.CrankOffsetDeg
|
||||||
|
|
||||||
---@type Pitch[]
|
---@type Pitch[]
|
||||||
local Pitches <const> = {
|
local Pitches <const> = {
|
||||||
-- Fastball
|
-- Fastball
|
||||||
|
@ -83,39 +140,6 @@ local Pitches <const> = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local BatterHandPos <const> = utils.xy(10, 25)
|
|
||||||
|
|
||||||
local batBase <const> = utils.xy(C.Center.x - 34, 215)
|
|
||||||
local batTip <const> = utils.xy(0, 0)
|
|
||||||
|
|
||||||
local ball <const> = {
|
|
||||||
x = C.Center.x --[[@as number]],
|
|
||||||
y = C.Center.y --[[@as number]],
|
|
||||||
z = 0,
|
|
||||||
size = C.SmallestBallRadius,
|
|
||||||
heldBy = nil --[[@type Runner | nil]],
|
|
||||||
}
|
|
||||||
|
|
||||||
---@alias Team { score: number, benchPosition: XYPair }
|
|
||||||
|
|
||||||
---@type table<string, Team>
|
|
||||||
local teams <const> = {
|
|
||||||
home = {
|
|
||||||
score = 0,
|
|
||||||
benchPosition = utils.xy(C.Screen.W + 10, C.Center.y),
|
|
||||||
},
|
|
||||||
away = {
|
|
||||||
score = 0,
|
|
||||||
benchPosition = utils.xy(-10, C.Center.y),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local PlayerTeam <const> = teams.home
|
|
||||||
local battingTeam = teams.away
|
|
||||||
local outs = 0
|
|
||||||
local inning = 1
|
|
||||||
local offenseMode = C.Offense.batting
|
|
||||||
|
|
||||||
---@return boolean playerIsOnSide, boolean playerIsOnOtherSide
|
---@return boolean playerIsOnSide, boolean playerIsOnOtherSide
|
||||||
function playerIsOn(side)
|
function playerIsOn(side)
|
||||||
local ret
|
local ret
|
||||||
|
@ -127,93 +151,6 @@ function playerIsOn(side)
|
||||||
return ret, not ret
|
return ret, not ret
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO? Replace this AND ballSizeAnimator with a ballHeightAnimator
|
|
||||||
-- ...that might lose some of the magic of both. Compromise available? idk
|
|
||||||
local ballFloatAnimator = gfx.animator.new(2000, -60, 0, utils.easingHill)
|
|
||||||
local BallSizeMs = 2000
|
|
||||||
local ballSizeAnimator = gfx.animator.new(BallSizeMs, 9, C.SmallestBallRadius, utils.easingHill)
|
|
||||||
|
|
||||||
local deltaSeconds = 0
|
|
||||||
|
|
||||||
---@param name string
|
|
||||||
---@param speed number
|
|
||||||
---@return Fielder
|
|
||||||
function newFielder(name, speed)
|
|
||||||
return {
|
|
||||||
name = name,
|
|
||||||
speed = speed,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
---@type table<string, Fielder>
|
|
||||||
local fielders <const> = {
|
|
||||||
first = newFielder("First", 40),
|
|
||||||
second = newFielder("Second", 40),
|
|
||||||
shortstop = newFielder("Shortstop", 40),
|
|
||||||
third = newFielder("Third", 40),
|
|
||||||
pitcher = newFielder("Pitcher", 30),
|
|
||||||
catcher = newFielder("Catcher", 35),
|
|
||||||
left = newFielder("Left", 40),
|
|
||||||
center = newFielder("C.Center", 40),
|
|
||||||
right = newFielder("Right", 40),
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Actually only benches the infield, because outfielders are far away!
|
|
||||||
---@param position XYPair
|
|
||||||
function benchAllFielders(position)
|
|
||||||
fielders.first.target = position
|
|
||||||
fielders.second.target = position
|
|
||||||
fielders.shortstop.target = position
|
|
||||||
fielders.third.target = position
|
|
||||||
fielders.pitcher.target = position
|
|
||||||
fielders.catcher.target = position
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Resets the target positions of all fielders to their defaults (at their field positions).
|
|
||||||
---@param fromOffTheField XYPair | nil If provided, also sets all runners' current position to one centralized location.
|
|
||||||
function resetFielderPositions(fromOffTheField)
|
|
||||||
if fromOffTheField then
|
|
||||||
for _, fielder in pairs(fielders) do
|
|
||||||
fielder.x = fromOffTheField.x
|
|
||||||
fielder.y = fromOffTheField.y
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
fielders.first.target = utils.xy(C.Screen.W - 65, C.Screen.H * 0.48)
|
|
||||||
fielders.second.target = utils.xy(C.Screen.W * 0.70, C.Screen.H * 0.30)
|
|
||||||
fielders.shortstop.target = utils.xy(C.Screen.W * 0.30, C.Screen.H * 0.30)
|
|
||||||
fielders.third.target = utils.xy(C.Screen.W * 0.1, C.Screen.H * 0.48)
|
|
||||||
fielders.pitcher.target = utils.xy(C.PitcherStartPos.x, C.PitcherStartPos.y)
|
|
||||||
fielders.catcher.target = utils.xy(C.Screen.W * 0.475, C.Screen.H * 0.92)
|
|
||||||
fielders.left.target = utils.xy(C.Screen.W * -1, C.Screen.H * -0.2)
|
|
||||||
fielders.center.target = utils.xy(C.Center.x, C.Screen.H * -0.4)
|
|
||||||
fielders.right.target = utils.xy(C.Screen.W * 2, fielders.left.target.y)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @type Runner[]
|
|
||||||
local runners <const> = {}
|
|
||||||
|
|
||||||
--- @type Runner[]
|
|
||||||
local outRunners <const> = {}
|
|
||||||
|
|
||||||
---@return Runner
|
|
||||||
function newRunner()
|
|
||||||
local new = {
|
|
||||||
x = C.RightHandedBattersBox.x - 60,
|
|
||||||
y = C.RightHandedBattersBox.y + 60,
|
|
||||||
nextBase = C.RightHandedBattersBox,
|
|
||||||
prevBase = nil,
|
|
||||||
forcedTo = C.Bases[C.First],
|
|
||||||
}
|
|
||||||
runners[#runners + 1] = new
|
|
||||||
return new
|
|
||||||
end
|
|
||||||
|
|
||||||
---@type Runner | nil
|
|
||||||
local batter = newRunner()
|
|
||||||
|
|
||||||
local throwMeter = 0
|
|
||||||
|
|
||||||
--- "Throws" the ball from its current position to the given destination.
|
--- "Throws" the ball from its current position to the given destination.
|
||||||
---@param destX number
|
---@param destX number
|
||||||
---@param destY number
|
---@param destY number
|
||||||
|
@ -242,10 +179,6 @@ function throwBall(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: Replace with a timer, repeatedly reset instead of setting to 0
|
|
||||||
local secondsSincePitchAllowed = -5
|
|
||||||
local catcherThrownBall = false
|
|
||||||
|
|
||||||
---@param pitchFlyTimeMs number | nil
|
---@param pitchFlyTimeMs number | nil
|
||||||
---@param pitchTypeIndex number | nil
|
---@param pitchTypeIndex number | nil
|
||||||
function pitch(pitchFlyTimeMs, pitchTypeIndex)
|
function pitch(pitchFlyTimeMs, pitchTypeIndex)
|
||||||
|
@ -316,10 +249,6 @@ function updateForcedRunners()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local ResetFieldersAfterSeconds <const> = 5
|
|
||||||
-- TODO: Replace with a timer, repeatedly reset, instead of setting to 0
|
|
||||||
local secondsSinceLastRunnerMove = 0
|
|
||||||
|
|
||||||
---@param runner integer | Runner
|
---@param runner integer | Runner
|
||||||
function outRunner(runner, message)
|
function outRunner(runner, message)
|
||||||
if type(runner) ~= "number" then
|
if type(runner) ~= "number" then
|
||||||
|
@ -346,9 +275,9 @@ function outRunner(runner, message)
|
||||||
local currentlyFieldingTeam = battingTeam == teams.home and teams.away or teams.home
|
local currentlyFieldingTeam = battingTeam == teams.home and teams.away or teams.home
|
||||||
local gameOver = inning == 9 and teams.away.score ~= teams.home.score
|
local gameOver = inning == 9 and teams.away.score ~= teams.home.score
|
||||||
if not gameOver then
|
if not gameOver then
|
||||||
fieldersDance()
|
FielderDanceAnimator:reset(C.DanceBounceMs)
|
||||||
secondsSinceLastRunnerMove = -7
|
secondsSinceLastRunnerMove = -7
|
||||||
benchAllFielders(currentlyFieldingTeam.benchPosition)
|
Field:benchTo(currentlyFieldingTeam.benchPosition)
|
||||||
announcer:say("SWITCHING SIDES...")
|
announcer:say("SWITCHING SIDES...")
|
||||||
end
|
end
|
||||||
while #runners > 0 do
|
while #runners > 0 do
|
||||||
|
@ -361,7 +290,7 @@ function outRunner(runner, message)
|
||||||
if gameOver then
|
if gameOver then
|
||||||
announcer:say("AND THAT'S THE BALL GAME!")
|
announcer:say("AND THAT'S THE BALL GAME!")
|
||||||
else
|
else
|
||||||
resetFielderPositions()
|
Field:resetFielderPositions()
|
||||||
if battingTeam == teams.home then
|
if battingTeam == teams.home then
|
||||||
inning = inning + 1
|
inning = inning + 1
|
||||||
end
|
end
|
||||||
|
@ -427,7 +356,7 @@ end
|
||||||
function tryToMakeAnOut(fielder)
|
function tryToMakeAnOut(fielder)
|
||||||
local targetX, targetY = getNextOutTarget()
|
local targetX, targetY = getNextOutTarget()
|
||||||
if targetX ~= nil and targetY ~= nil then
|
if targetX ~= nil and targetY ~= nil then
|
||||||
local nearestFielder = utils.getNearestOf(fielders, targetX, targetY)
|
local nearestFielder = utils.getNearestOf(Field.fielders, targetX, targetY)
|
||||||
nearestFielder.target = utils.xy(targetX, targetY)
|
nearestFielder.target = utils.xy(targetX, targetY)
|
||||||
if nearestFielder == fielder then
|
if nearestFielder == fielder then
|
||||||
ball.heldBy = fielder
|
ball.heldBy = fielder
|
||||||
|
@ -461,7 +390,7 @@ function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local closestFielder = utils.getNearestOf(fielders, targetBase.x, targetBase.y, function(fielder)
|
local closestFielder = utils.getNearestOf(Field.fielders, targetBase.x, targetBase.y, function(fielder)
|
||||||
return fielder ~= thrower
|
return fielder ~= thrower
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -524,7 +453,7 @@ function updateFielder(fielder)
|
||||||
if playerIsOn(C.Sides.defense) then
|
if playerIsOn(C.Sides.defense) then
|
||||||
local throwFly = readThrow()
|
local throwFly = readThrow()
|
||||||
if throwFly then
|
if throwFly then
|
||||||
buttonControlledThrow(fielders.pitcher, throwFly)
|
buttonControlledThrow(Field.fielders.pitcher, throwFly)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
updateNpcFielder(fielder, outedSomeRunner)
|
updateNpcFielder(fielder, outedSomeRunner)
|
||||||
|
@ -586,22 +515,19 @@ function updateRunner(runner, runnerIndex, appliedSpeed)
|
||||||
return prevX ~= runner.x or prevY ~= runner.y
|
return prevX ~= runner.x or prevY ~= runner.y
|
||||||
end
|
end
|
||||||
|
|
||||||
---@type number
|
|
||||||
local batAngleDeg
|
|
||||||
|
|
||||||
function nextBatter()
|
function nextBatter()
|
||||||
batter = nil
|
batter = nil
|
||||||
playdate.timer.new(2000, function()
|
playdate.timer.new(2000, function()
|
||||||
pitchTracker:reset()
|
pitchTracker:reset()
|
||||||
if not batter then
|
if not batter then
|
||||||
batter = newRunner()
|
batter = utils.newRunner(runners)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function walk()
|
function walk()
|
||||||
announcer:say("Walk!")
|
announcer:say("Walk!")
|
||||||
fielders.first.target = C.Bases[C.First]
|
Field.fielders.first.target = C.Bases[C.First]
|
||||||
batter.nextBase = C.Bases[C.First]
|
batter.nextBase = C.Bases[C.First]
|
||||||
batter.prevBase = C.Bases[C.Home]
|
batter.prevBase = C.Bases[C.Home]
|
||||||
offenseMode = C.Offense.walking
|
offenseMode = C.Offense.walking
|
||||||
|
@ -653,14 +579,14 @@ function updateBatting(batDeg, batSpeed)
|
||||||
local hitBallScaler = gfx.animator.new(2000, 9 + (mult * mult * 0.5), C.SmallestBallRadius, utils.easingHill)
|
local hitBallScaler = gfx.animator.new(2000, 9 + (mult * mult * 0.5), C.SmallestBallRadius, utils.easingHill)
|
||||||
throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000, nil, hitBallScaler)
|
throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000, nil, hitBallScaler)
|
||||||
|
|
||||||
fielders.first.target = C.Bases[C.First]
|
Field.fielders.first.target = C.Bases[C.First]
|
||||||
batter.nextBase = C.Bases[C.First]
|
batter.nextBase = C.Bases[C.First]
|
||||||
batter.prevBase = C.Bases[C.Home]
|
batter.prevBase = C.Bases[C.Home]
|
||||||
updateForcedRunners()
|
updateForcedRunners()
|
||||||
batter.forcedTo = C.Bases[C.First]
|
batter.forcedTo = C.Bases[C.First]
|
||||||
batter = nil -- Demote batter to a mere runner
|
batter = nil -- Demote batter to a mere runner
|
||||||
|
|
||||||
local chasingFielder = utils.getNearestOf(fielders, ballDestX, ballDestY)
|
local chasingFielder = utils.getNearestOf(Field.fielders, ballDestX, ballDestY)
|
||||||
chasingFielder.target = { x = ballDestX, y = ballDestY }
|
chasingFielder.target = { x = ballDestX, y = ballDestY }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -721,10 +647,12 @@ function updateGameState()
|
||||||
if ball.heldBy then
|
if ball.heldBy then
|
||||||
ball.x = ball.heldBy.x
|
ball.x = ball.heldBy.x
|
||||||
ball.y = ball.heldBy.y
|
ball.y = ball.heldBy.y
|
||||||
|
ball.size = C.SmallestBallRadius
|
||||||
else
|
else
|
||||||
ball.x = ballAnimatorX:currentValue()
|
ball.x = ballAnimatorX:currentValue()
|
||||||
ball.z = ballFloatAnimator:currentValue()
|
ball.z = ballFloatAnimator:currentValue()
|
||||||
ball.y = ballAnimatorY:currentValue() + ball.z
|
ball.y = ballAnimatorY:currentValue() + ball.z
|
||||||
|
ball.size = ballSizeAnimator:currentValue()
|
||||||
end
|
end
|
||||||
|
|
||||||
local playerOnOffense, playerOnDefense = playerIsOn(C.Sides.offense)
|
local playerOnOffense, playerOnDefense = playerIsOn(C.Sides.offense)
|
||||||
|
@ -741,7 +669,7 @@ function updateGameState()
|
||||||
pitchTracker.recordedPitchX = ball.x
|
pitchTracker.recordedPitchX = ball.x
|
||||||
end
|
end
|
||||||
|
|
||||||
local pitcher = fielders.pitcher
|
local pitcher = Field.fielders.pitcher
|
||||||
if utils.distanceBetween(pitcher.x, pitcher.y, C.PitchStartX, C.PitchStartY) < C.BaseHitbox then
|
if utils.distanceBetween(pitcher.x, pitcher.y, C.PitchStartX, C.PitchStartY) < C.BaseHitbox then
|
||||||
secondsSincePitchAllowed = secondsSincePitchAllowed + deltaSeconds
|
secondsSincePitchAllowed = secondsSincePitchAllowed + deltaSeconds
|
||||||
end
|
end
|
||||||
|
@ -787,12 +715,12 @@ function updateGameState()
|
||||||
secondsSinceLastRunnerMove = 0
|
secondsSinceLastRunnerMove = 0
|
||||||
else
|
else
|
||||||
secondsSinceLastRunnerMove = secondsSinceLastRunnerMove + deltaSeconds
|
secondsSinceLastRunnerMove = secondsSinceLastRunnerMove + deltaSeconds
|
||||||
if secondsSinceLastRunnerMove > ResetFieldersAfterSeconds then
|
if secondsSinceLastRunnerMove > C.ResetFieldersAfterSeconds then
|
||||||
throwBall(C.PitchStartX, C.PitchStartY, playdate.easingFunctions.linear, nil, true)
|
throwBall(C.PitchStartX, C.PitchStartY, playdate.easingFunctions.linear, nil, true)
|
||||||
resetFielderPositions()
|
Field:resetFielderPositions()
|
||||||
offenseMode = C.Offense.batting
|
offenseMode = C.Offense.batting
|
||||||
if not batter then
|
if not batter then
|
||||||
batter = newRunner()
|
batter = utils.newRunner(runners)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -803,7 +731,7 @@ function updateGameState()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, fielder in pairs(fielders) do
|
for _, fielder in pairs(Field.fielders) do
|
||||||
updateFielder(fielder)
|
updateFielder(fielder)
|
||||||
end
|
end
|
||||||
walkAwayOutRunners()
|
walkAwayOutRunners()
|
||||||
|
@ -817,17 +745,14 @@ function playdate.update()
|
||||||
gfx.clear()
|
gfx.clear()
|
||||||
gfx.setColor(gfx.kColorBlack)
|
gfx.setColor(gfx.kColorBlack)
|
||||||
|
|
||||||
local offsetX, offsetY = 0, 0
|
local offsetX, offsetY = getDrawOffset(ball.x, ball.y)
|
||||||
if ball.x < C.BallOffscreen then
|
|
||||||
offsetX, offsetY = getDrawOffset(C.Screen.W, C.Screen.H, ball.x, ball.y)
|
|
||||||
gfx.setDrawOffset(offsetX, offsetY)
|
gfx.setDrawOffset(offsetX, offsetY)
|
||||||
end
|
|
||||||
|
|
||||||
GrassBackground:draw(-400, -240)
|
GrassBackground:draw(-400, -240)
|
||||||
|
|
||||||
local fielderDanceHeight = FielderDanceAnimator:currentValue()
|
local fielderDanceHeight = FielderDanceAnimator:currentValue()
|
||||||
local ballIsHeld = false
|
local ballIsHeld = false
|
||||||
for _, fielder in pairs(fielders) do
|
for _, fielder in pairs(Field.fielders) do
|
||||||
ballIsHeld = drawFielder(ball, fielder.x, fielder.y + fielderDanceHeight) or ballIsHeld
|
ballIsHeld = drawFielder(ball, fielder.x, fielder.y + fielderDanceHeight) or ballIsHeld
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -853,6 +778,7 @@ function playdate.update()
|
||||||
PlayerImageBlipper:draw(false, runner.x, runner.y)
|
PlayerImageBlipper:draw(false, runner.x, runner.y)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, runner in pairs(outRunners) do
|
for _, runner in pairs(outRunners) do
|
||||||
PlayerFrown:draw(runner.x, runner.y)
|
PlayerFrown:draw(runner.x, runner.y)
|
||||||
end
|
end
|
||||||
|
@ -880,7 +806,7 @@ function init()
|
||||||
playdate.display.setRefreshRate(50)
|
playdate.display.setRefreshRate(50)
|
||||||
gfx.setBackgroundColor(gfx.kColorWhite)
|
gfx.setBackgroundColor(gfx.kColorWhite)
|
||||||
playdate.setMenuImage(gfx.image.new("images/game/menu-image.png"))
|
playdate.setMenuImage(gfx.image.new("images/game/menu-image.png"))
|
||||||
resetFielderPositions(teams.home.benchPosition)
|
Field:resetFielderPositions(teams.home.benchPosition)
|
||||||
playdate.getSystemMenu():addMenuItem("Restart game", function() end) -- TODO?
|
playdate.getSystemMenu():addMenuItem("Restart game", function() end) -- TODO?
|
||||||
|
|
||||||
playdate.timer.new(2000, function()
|
playdate.timer.new(2000, function()
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
-- selene: allow(unscoped_variables)
|
-- selene: allow(unscoped_variables)
|
||||||
utils = {}
|
utils = {}
|
||||||
|
|
||||||
|
--- @alias XYPair {
|
||||||
|
--- x: number,
|
||||||
|
--- y: number,
|
||||||
|
--- }
|
||||||
|
|
||||||
|
local sqrt <const> = math.sqrt
|
||||||
|
|
||||||
function utils.easingHill(t, b, c, d)
|
function utils.easingHill(t, b, c, d)
|
||||||
c = c + 0.0 -- convert to float to prevent integer overflow
|
c = c + 0.0 -- convert to float to prevent integer overflow
|
||||||
t = t / d
|
t = t / d
|
||||||
|
@ -9,10 +16,18 @@ function utils.easingHill(t, b, c, d)
|
||||||
return (c * t) + b
|
return (c * t) + b
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @alias XYPair {
|
--- Build an "animator" whose `:currentValue()` always returns the given value.
|
||||||
--- x: number,
|
--- Essentially an "empty object" pattern for initial object positions.
|
||||||
--- y: number,
|
---@param value number
|
||||||
--- }
|
---@return PseudoAnimator
|
||||||
|
function utils.staticAnimator(value)
|
||||||
|
return {
|
||||||
|
currentValue = function(_)
|
||||||
|
return value
|
||||||
|
end,
|
||||||
|
reset = function(_) end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
---@param x number
|
---@param x number
|
||||||
---@param y number
|
---@param y number
|
||||||
|
@ -84,7 +99,7 @@ end
|
||||||
function utils.distanceBetween(x1, y1, x2, y2)
|
function utils.distanceBetween(x1, y1, x2, y2)
|
||||||
local x = x1 - x2
|
local x = x1 - x2
|
||||||
local y = y1 - y2
|
local y = y1 - y2
|
||||||
return math.sqrt((x * x) + (y * y)), x, y
|
return sqrt((x * x) + (y * y)), x, y
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return number distance, number x, number y, number z
|
---@return number distance, number x, number y, number z
|
||||||
|
@ -92,7 +107,20 @@ function utils.distanceBetweenZ(x1, y1, z1, x2, y2, z2)
|
||||||
local x = x1 - x2
|
local x = x1 - x2
|
||||||
local y = y1 - y2
|
local y = y1 - y2
|
||||||
local z = z1 - z2
|
local z = z1 - z2
|
||||||
return math.sqrt((x * x) + (y * y) + (z * z)), x, y, z
|
return sqrt((x * x) + (y * y) + (z * z)), x, y, z
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return Runner
|
||||||
|
function utils.newRunner(runners)
|
||||||
|
local new = {
|
||||||
|
x = C.RightHandedBattersBox.x - 60,
|
||||||
|
y = C.RightHandedBattersBox.y + 60,
|
||||||
|
nextBase = C.RightHandedBattersBox,
|
||||||
|
prevBase = nil,
|
||||||
|
forcedTo = C.Bases[C.First],
|
||||||
|
}
|
||||||
|
runners[#runners + 1] = new
|
||||||
|
return new
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns true only if the point is below the given line, within the x bounds of said line, and above the bottomBound
|
--- Returns true only if the point is below the given line, within the x bounds of said line, and above the bottomBound
|
||||||
|
|
Loading…
Reference in New Issue