Much more fleshed-out constants.lua
* Move scoreboard.lua to draw/overlay.lua * Move minimap drawing into overlay.lua * Remove playdate imports from utils.lua
This commit is contained in:
parent
f07530623f
commit
4a4049996f
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
||||||
SOURCE_FILES := src/constants.lua src/draw/* src/utils.lua src/dbg.lua src/npc.lua src/announcer.lua src/graphics.lua src/scoreboard.lua src/main.lua
|
SOURCE_FILES := src/utils.lua src/constants.lua src/draw/* src/dbg.lua src/npc.lua src/announcer.lua src/graphics.lua src/main.lua
|
||||||
|
|
||||||
all:
|
all:
|
||||||
pdc src BatterUp.pdx
|
pdc src BatterUp.pdx
|
||||||
|
|
|
@ -11,3 +11,88 @@ C.Center = utils.xy(C.Screen.W / 2, C.Screen.H / 2)
|
||||||
C.StrikeZoneStartX = C.Center.x - 16
|
C.StrikeZoneStartX = C.Center.x - 16
|
||||||
C.StrikeZoneEndX = C.StrikeZoneStartX + 24
|
C.StrikeZoneEndX = C.StrikeZoneStartX + 24
|
||||||
C.StrikeZoneStartY = C.Screen.H - 35
|
C.StrikeZoneStartY = C.Screen.H - 35
|
||||||
|
|
||||||
|
--- @alias Base {
|
||||||
|
--- x: number,
|
||||||
|
--- y: number,
|
||||||
|
--- }
|
||||||
|
|
||||||
|
---@type Base[]
|
||||||
|
C.Bases = {
|
||||||
|
utils.xy(C.Screen.W * 0.93, C.Screen.H * 0.52),
|
||||||
|
utils.xy(C.Screen.W * 0.47, C.Screen.H * 0.19),
|
||||||
|
utils.xy(C.Screen.W * 0.03, C.Screen.H * 0.52),
|
||||||
|
utils.xy(C.Screen.W * 0.474, C.Screen.H * 0.79),
|
||||||
|
}
|
||||||
|
|
||||||
|
C.First, C.Second, C.Third, C.Home = 1, 2, 3, 4
|
||||||
|
C.FieldHeight = C.Bases[C.Home].y - C.Bases[C.Second].y
|
||||||
|
|
||||||
|
-- Pseudo-base for batter to target
|
||||||
|
C.RightHandedBattersBox = {
|
||||||
|
x = C.Bases[C.Home].x - 35,
|
||||||
|
y = C.Bases[C.Home].y,
|
||||||
|
}
|
||||||
|
|
||||||
|
---@type table<Base, Base | nil>
|
||||||
|
C.NextBaseMap = {
|
||||||
|
[C.RightHandedBattersBox] = nil, -- Runner should not escape the box before a hit!
|
||||||
|
[C.Bases[C.First]] = C.Bases[C.Second],
|
||||||
|
[C.Bases[C.Second]] = C.Bases[C.Third],
|
||||||
|
[C.Bases[C.Third]] = C.Bases[C.Home],
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Angle to align the bat to
|
||||||
|
C.CrankOffsetDeg = 90
|
||||||
|
|
||||||
|
C.DanceBounceMs = 500
|
||||||
|
C.DanceBounceCount = 4
|
||||||
|
|
||||||
|
--- Used to draw the ball well out of bounds, and
|
||||||
|
--- generally as a check for whether or not it's in play.
|
||||||
|
C.BallOffscreen = 999
|
||||||
|
|
||||||
|
C.PitchAfterSeconds = 7
|
||||||
|
C.PitchFlyMs = 1050
|
||||||
|
C.PitchStartX = 195
|
||||||
|
C.PitchStartY, C.PitchEndY = 105, 240
|
||||||
|
|
||||||
|
--- The max distance at which a fielder can tag out a runner.
|
||||||
|
C.TagDistance = 15
|
||||||
|
|
||||||
|
--- The max distance at which a runner can be considered on base.
|
||||||
|
C.BaseHitbox = 10
|
||||||
|
|
||||||
|
C.BattingPower = 20
|
||||||
|
|
||||||
|
C.SmallestBallRadius = 6
|
||||||
|
|
||||||
|
C.BatLength = 50
|
||||||
|
|
||||||
|
--- An enum for what state the offense is in
|
||||||
|
C.Offense = {
|
||||||
|
batting = {},
|
||||||
|
running = {},
|
||||||
|
walking = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
--- An enum for which side (offense or defense) a team is on.
|
||||||
|
C.Sides = {
|
||||||
|
offense = {},
|
||||||
|
defense = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
C.PitcherStartPos = {
|
||||||
|
x = C.Screen.W * 0.48,
|
||||||
|
y = C.Screen.H * 0.40,
|
||||||
|
}
|
||||||
|
|
||||||
|
C.ThrowMeterMax = 15
|
||||||
|
C.ThrowMeterDrainPerSec = 150
|
||||||
|
|
||||||
|
--- Controls how hard the ball can be hit, and
|
||||||
|
--- how fast the ball can be thrown.
|
||||||
|
C.CrankPower = 10
|
||||||
|
|
||||||
|
--- How fast baserunners move after a walk
|
||||||
|
C.WalkedRunnerSpeed = 10
|
||||||
|
|
20
src/dbg.lua
20
src/dbg.lua
|
@ -20,21 +20,21 @@ 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(baseConstants, runners)
|
function dbg.loadTheBases(runners)
|
||||||
newRunner()
|
newRunner()
|
||||||
newRunner()
|
newRunner()
|
||||||
newRunner()
|
newRunner()
|
||||||
runners[2].x = baseConstants[1].x
|
runners[2].x = C.Bases[C.First].x
|
||||||
runners[2].y = baseConstants[1].y
|
runners[2].y = C.Bases[C.First].y
|
||||||
runners[2].nextBase = baseConstants[2]
|
runners[2].nextBase = C.Bases[C.Second]
|
||||||
|
|
||||||
runners[3].x = baseConstants[2].x
|
runners[3].x = C.Bases[C.Second].x
|
||||||
runners[3].y = baseConstants[2].y
|
runners[3].y = C.Bases[C.Second].y
|
||||||
runners[3].nextBase = baseConstants[3]
|
runners[3].nextBase = C.Bases[C.Third]
|
||||||
|
|
||||||
runners[4].x = baseConstants[3].x
|
runners[4].x = C.Bases[C.Third].x
|
||||||
runners[4].y = baseConstants[3].y
|
runners[4].y = C.Bases[C.Third].y
|
||||||
runners[4].nextBase = baseConstants[4]
|
runners[4].nextBase = C.Bases[C.Home]
|
||||||
end
|
end
|
||||||
|
|
||||||
if not playdate then
|
if not playdate then
|
||||||
|
|
|
@ -2,27 +2,31 @@
|
||||||
local gfx = playdate.graphics
|
local gfx = playdate.graphics
|
||||||
|
|
||||||
local ScoreFont <const> = playdate.graphics.font.new("fonts/font-full-circle.pft")
|
local ScoreFont <const> = playdate.graphics.font.new("fonts/font-full-circle.pft")
|
||||||
local OutBubbleRadius <const> = 5
|
|
||||||
local ScoreboardMarginX <const> = 6
|
local MinimapBackground <const> = gfx.image.new("images/game/minimap.png") --[[@as pd_image]]
|
||||||
local ScoreboardMarginRight <const> = 4
|
|
||||||
local ScoreboardHeight <const> = 55
|
local MinimapSizeX, MinimapSizeY <const> = MinimapBackground:getSize()
|
||||||
local Indicator = "> "
|
local MinimapPosX, MinimapPosY = C.Screen.W - MinimapSizeX, C.Screen.H - MinimapSizeY
|
||||||
local IndicatorWidth <const> = ScoreFont:getTextWidth(Indicator)
|
|
||||||
|
local MinimapMultX <const> = 0.75 * MinimapSizeX / C.Screen.W
|
||||||
|
local MinimapOffsetX <const> = MinimapPosX + 5
|
||||||
|
local MinimapMultY <const> = 0.70 * MinimapSizeY / C.FieldHeight
|
||||||
|
local MinimapOffsetY <const> = MinimapPosY - 15
|
||||||
|
|
||||||
|
function drawMinimap(runners)
|
||||||
|
MinimapBackground:draw(MinimapPosX, MinimapPosY)
|
||||||
|
gfx.setColor(gfx.kColorBlack)
|
||||||
|
for _, runner in pairs(runners) do
|
||||||
|
local x = (MinimapMultX * runner.x) + MinimapOffsetX
|
||||||
|
local y = (MinimapMultY * runner.y) + MinimapOffsetY
|
||||||
|
gfx.fillRect(x, y, 8, 8)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local BallStrikeMarginY <const> = 4
|
local BallStrikeMarginY <const> = 4
|
||||||
local BallStrikeWidth <const> = 60
|
local BallStrikeWidth <const> = 60
|
||||||
local BallStrikeHeight <const> = (BallStrikeMarginY * 2) + ScoreFont:getHeight()
|
local BallStrikeHeight <const> = (BallStrikeMarginY * 2) + ScoreFont:getHeight()
|
||||||
|
|
||||||
---@param teams any
|
|
||||||
---@param battingTeam any
|
|
||||||
---@return string, number, string, number
|
|
||||||
function getIndicators(teams, battingTeam)
|
|
||||||
if teams.home == battingTeam then
|
|
||||||
return Indicator, 0, "", IndicatorWidth
|
|
||||||
end
|
|
||||||
return "", IndicatorWidth, Indicator, 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function drawBallsAndStrikes(x, y, balls, strikes)
|
function drawBallsAndStrikes(x, y, balls, strikes)
|
||||||
if balls == 0 and strikes == 0 then
|
if balls == 0 and strikes == 0 then
|
||||||
return
|
return
|
||||||
|
@ -40,6 +44,23 @@ function drawBallsAndStrikes(x, y, balls, strikes)
|
||||||
gfx.setImageDrawMode(originalDrawMode)
|
gfx.setImageDrawMode(originalDrawMode)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local OutBubbleRadius <const> = 5
|
||||||
|
local ScoreboardMarginX <const> = 6
|
||||||
|
local ScoreboardMarginRight <const> = 4
|
||||||
|
local ScoreboardHeight <const> = 55
|
||||||
|
local Indicator = "> "
|
||||||
|
local IndicatorWidth <const> = ScoreFont:getTextWidth(Indicator)
|
||||||
|
|
||||||
|
---@param teams any
|
||||||
|
---@param battingTeam any
|
||||||
|
---@return string, number, string, number
|
||||||
|
function getIndicators(teams, battingTeam)
|
||||||
|
if teams.home == battingTeam then
|
||||||
|
return Indicator, 0, "", IndicatorWidth
|
||||||
|
end
|
||||||
|
return "", IndicatorWidth, Indicator, 0
|
||||||
|
end
|
||||||
|
|
||||||
function drawScoreboard(x, y, teams, outs, battingTeam, inning)
|
function drawScoreboard(x, y, teams, outs, battingTeam, inning)
|
||||||
local homeScore = teams.home.score
|
local homeScore = teams.home.score
|
||||||
local awayScore = teams.away.score
|
local awayScore = teams.away.score
|
283
src/main.lua
283
src/main.lua
|
@ -7,16 +7,6 @@ import 'CoreLibs/object.lua'
|
||||||
import 'CoreLibs/timer.lua'
|
import 'CoreLibs/timer.lua'
|
||||||
import 'CoreLibs/ui.lua'
|
import 'CoreLibs/ui.lua'
|
||||||
|
|
||||||
--- @alias XYPair {
|
|
||||||
--- x: number,
|
|
||||||
--- y: number,
|
|
||||||
--- }
|
|
||||||
|
|
||||||
--- @alias Base {
|
|
||||||
--- x: number,
|
|
||||||
--- y: number,
|
|
||||||
--- }
|
|
||||||
|
|
||||||
--- @alias Runner {
|
--- @alias Runner {
|
||||||
--- x: number,
|
--- x: number,
|
||||||
--- y: number,
|
--- y: number,
|
||||||
|
@ -41,44 +31,37 @@ import 'announcer.lua'
|
||||||
import 'dbg.lua'
|
import 'dbg.lua'
|
||||||
import 'graphics.lua'
|
import 'graphics.lua'
|
||||||
import 'npc.lua'
|
import 'npc.lua'
|
||||||
import 'scoreboard.lua'
|
import 'draw/overlay'
|
||||||
import 'draw/fielder'
|
import 'draw/fielder'
|
||||||
-- stylua: ignore end
|
-- stylua: ignore end
|
||||||
|
|
||||||
-- selene: allow(shadowing)
|
-- selene: allow(shadowing)
|
||||||
local gfx <const> = playdate.graphics
|
local gfx <const> = playdate.graphics
|
||||||
|
|
||||||
|
-- selene: allow(shadowing)
|
||||||
|
local C <const> = C
|
||||||
|
|
||||||
local BootTune <const> = playdate.sound.sampleplayer.new("sounds/boot-tune.wav")
|
local BootTune <const> = playdate.sound.sampleplayer.new("sounds/boot-tune.wav")
|
||||||
-- local BootTune <const> = playdate.sound.sampleplayer.new("sounds/boot-tune-organy.wav")
|
-- local BootTune <const> = playdate.sound.sampleplayer.new("sounds/boot-tune-organy.wav")
|
||||||
local TinnyBackground <const> = playdate.sound.sampleplayer.new("sounds/tinny-background.wav")
|
local TinnyBackground <const> = playdate.sound.sampleplayer.new("sounds/tinny-background.wav")
|
||||||
local BatCrackSound <const> = playdate.sound.sampleplayer.new("sounds/bat-crack-reverb.wav")
|
local BatCrackSound <const> = playdate.sound.sampleplayer.new("sounds/bat-crack-reverb.wav")
|
||||||
local GrassBackground <const> = gfx.image.new("images/game/grass.png") --[[@as pd_image]]
|
local GrassBackground <const> = gfx.image.new("images/game/grass.png") --[[@as pd_image]]
|
||||||
local Minimap <const> = gfx.image.new("images/game/minimap.png") --[[@as pd_image]]
|
|
||||||
local PlayerFrown <const> = gfx.image.new("images/game/player-frown.png") --[[@as pd_image]]
|
local PlayerFrown <const> = gfx.image.new("images/game/player-frown.png") --[[@as pd_image]]
|
||||||
local PlayerSmile <const> = gfx.image.new("images/game/player.png") --[[@as pd_image]]
|
local PlayerSmile <const> = gfx.image.new("images/game/player.png") --[[@as pd_image]]
|
||||||
local PlayerBack <const> = gfx.image.new("images/game/player-back.png") --[[@as pd_image]]
|
local PlayerBack <const> = gfx.image.new("images/game/player-back.png") --[[@as pd_image]]
|
||||||
|
|
||||||
local PlayerImageBlipper <const> = blipper.new(100, "images/game/player.png", "images/game/player-lowhat.png")
|
local PlayerImageBlipper <const> = blipper.new(100, "images/game/player.png", "images/game/player-lowhat.png")
|
||||||
|
|
||||||
local DanceBounceMs <const> = 500
|
|
||||||
local DanceBounceCount <const> = 4
|
|
||||||
|
|
||||||
local FielderDanceAnimator <const> = gfx.animator.new(1, 10, 0, utils.easingHill)
|
local FielderDanceAnimator <const> = gfx.animator.new(1, 10, 0, utils.easingHill)
|
||||||
FielderDanceAnimator.repeatCount = DanceBounceCount - 1
|
FielderDanceAnimator.repeatCount = C.DanceBounceCount - 1
|
||||||
|
|
||||||
-- selene: allow(unused_variable)
|
-- selene: allow(unused_variable)
|
||||||
function fieldersDance()
|
function fieldersDance()
|
||||||
FielderDanceAnimator:reset(DanceBounceMs)
|
FielderDanceAnimator:reset(C.DanceBounceMs)
|
||||||
end
|
end
|
||||||
|
|
||||||
local BallOffscreen <const> = 999
|
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)
|
||||||
local PitchFlyMs <const> = 1050
|
|
||||||
local PitchStartX <const> = 195
|
|
||||||
local PitchStartY <const>, PitchEndY <const> = 105, 240
|
|
||||||
|
|
||||||
local ballAnimatorY = gfx.animator.new(0, BallOffscreen, BallOffscreen, playdate.easingFunctions.linear)
|
|
||||||
local ballAnimatorX = gfx.animator.new(0, BallOffscreen, 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 }
|
||||||
|
@ -87,64 +70,44 @@ local ballAnimatorX = gfx.animator.new(0, BallOffscreen, BallOffscreen, playdate
|
||||||
local Pitches <const> = {
|
local Pitches <const> = {
|
||||||
-- Fastball
|
-- Fastball
|
||||||
{
|
{
|
||||||
x = gfx.animator.new(0, PitchStartX, PitchStartX, playdate.easingFunctions.linear),
|
x = gfx.animator.new(0, C.PitchStartX, C.PitchStartX, playdate.easingFunctions.linear),
|
||||||
y = gfx.animator.new(PitchFlyMs / 1.3, PitchStartY, PitchEndY, playdate.easingFunctions.linear),
|
y = gfx.animator.new(C.PitchFlyMs / 1.3, C.PitchStartY, C.PitchEndY, playdate.easingFunctions.linear),
|
||||||
},
|
},
|
||||||
-- Curve ball
|
-- Curve ball
|
||||||
{
|
{
|
||||||
x = gfx.animator.new(PitchFlyMs, PitchStartX + 20, PitchStartX, utils.easingHill),
|
x = gfx.animator.new(C.PitchFlyMs, C.PitchStartX + 20, C.PitchStartX, utils.easingHill),
|
||||||
y = gfx.animator.new(PitchFlyMs, PitchStartY, PitchEndY, playdate.easingFunctions.linear),
|
y = gfx.animator.new(C.PitchFlyMs, C.PitchStartY, C.PitchEndY, playdate.easingFunctions.linear),
|
||||||
},
|
},
|
||||||
-- Slider
|
-- Slider
|
||||||
{
|
{
|
||||||
x = gfx.animator.new(PitchFlyMs, PitchStartX - 20, PitchStartX, utils.easingHill),
|
x = gfx.animator.new(C.PitchFlyMs, C.PitchStartX - 20, C.PitchStartX, utils.easingHill),
|
||||||
y = gfx.animator.new(PitchFlyMs, PitchStartY, PitchEndY, playdate.easingFunctions.linear),
|
y = gfx.animator.new(C.PitchFlyMs, C.PitchStartY, C.PitchEndY, playdate.easingFunctions.linear),
|
||||||
},
|
},
|
||||||
-- Wobbbleball
|
-- Wobbbleball
|
||||||
{
|
{
|
||||||
x = {
|
x = {
|
||||||
currentValue = function()
|
currentValue = function()
|
||||||
return PitchStartX + (10 * math.sin((ballAnimatorY:currentValue() - PitchStartY) / 10))
|
return C.PitchStartX + (10 * math.sin((ballAnimatorY:currentValue() - C.PitchStartY) / 10))
|
||||||
end,
|
end,
|
||||||
reset = function() end,
|
reset = function() end,
|
||||||
},
|
},
|
||||||
y = gfx.animator.new(PitchFlyMs * 1.3, PitchStartY, PitchEndY, playdate.easingFunctions.linear),
|
y = gfx.animator.new(C.PitchFlyMs * 1.3, C.PitchStartY, C.PitchEndY, playdate.easingFunctions.linear),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local CrankOffsetDeg <const> = 90
|
local BatterHandPos <const> = utils.xy(10, 25)
|
||||||
local BatOffset <const> = utils.xy(10, 25)
|
|
||||||
|
|
||||||
local batBase <const> = utils.xy(C.Center.x - 34, 215)
|
local batBase <const> = utils.xy(C.Center.x - 34, 215)
|
||||||
local batTip <const> = utils.xy(0, 0)
|
local batTip <const> = utils.xy(0, 0)
|
||||||
|
|
||||||
local TagDistance <const> = 15
|
|
||||||
|
|
||||||
local SmallestBallRadius <const> = 6
|
|
||||||
|
|
||||||
local ball <const> = {
|
local ball <const> = {
|
||||||
x = C.Center.x --[[@as number]],
|
x = C.Center.x --[[@as number]],
|
||||||
y = C.Center.y --[[@as number]],
|
y = C.Center.y --[[@as number]],
|
||||||
z = 0,
|
z = 0,
|
||||||
size = SmallestBallRadius,
|
size = C.SmallestBallRadius,
|
||||||
heldBy = nil --[[@type Runner | nil]],
|
heldBy = nil --[[@type Runner | nil]],
|
||||||
}
|
}
|
||||||
|
|
||||||
local BatLength <const> = 50
|
|
||||||
|
|
||||||
local Offense <const> = {
|
|
||||||
batting = {},
|
|
||||||
running = {},
|
|
||||||
walking = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
local Sides <const> = {
|
|
||||||
offense = {},
|
|
||||||
defense = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
local offenseMode = Offense.batting
|
|
||||||
|
|
||||||
---@alias Team { score: number, benchPosition: XYPair }
|
---@alias Team { score: number, benchPosition: XYPair }
|
||||||
|
|
||||||
---@type table<string, Team>
|
---@type table<string, Team>
|
||||||
|
@ -163,14 +126,15 @@ local PlayerTeam <const> = teams.home
|
||||||
local battingTeam = teams.away
|
local battingTeam = teams.away
|
||||||
local outs = 0
|
local outs = 0
|
||||||
local inning = 1
|
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
|
||||||
if PlayerTeam == battingTeam then
|
if PlayerTeam == battingTeam then
|
||||||
ret = side == Sides.offense
|
ret = side == C.Sides.offense
|
||||||
else
|
else
|
||||||
ret = side == Sides.defense
|
ret = side == C.Sides.defense
|
||||||
end
|
end
|
||||||
return ret, not ret
|
return ret, not ret
|
||||||
end
|
end
|
||||||
|
@ -179,33 +143,10 @@ end
|
||||||
-- ...that might lose some of the magic of both. Compromise available? idk
|
-- ...that might lose some of the magic of both. Compromise available? idk
|
||||||
local ballFloatAnimator = gfx.animator.new(2000, -60, 0, utils.easingHill)
|
local ballFloatAnimator = gfx.animator.new(2000, -60, 0, utils.easingHill)
|
||||||
local BallSizeMs = 2000
|
local BallSizeMs = 2000
|
||||||
local ballSizeAnimator = gfx.animator.new(BallSizeMs, 9, SmallestBallRadius, utils.easingHill)
|
local ballSizeAnimator = gfx.animator.new(BallSizeMs, 9, C.SmallestBallRadius, utils.easingHill)
|
||||||
|
|
||||||
local HitMult = 20
|
|
||||||
|
|
||||||
local deltaSeconds = 0
|
local deltaSeconds = 0
|
||||||
|
|
||||||
local First <const>, Second <const>, Third <const>, Home <const> = 1, 2, 3, 4
|
|
||||||
|
|
||||||
---@type Base[]
|
|
||||||
local Bases = {
|
|
||||||
utils.xy(C.Screen.W * 0.93, C.Screen.H * 0.52),
|
|
||||||
utils.xy(C.Screen.W * 0.47, C.Screen.H * 0.19),
|
|
||||||
utils.xy(C.Screen.W * 0.03, C.Screen.H * 0.52),
|
|
||||||
utils.xy(C.Screen.W * 0.474, C.Screen.H * 0.79),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Pseudo-base for batter to target
|
|
||||||
local RightHandedBattersBox <const> = utils.xy(Bases[Home].x - 35, Bases[Home].y)
|
|
||||||
|
|
||||||
---@type table<Base, Base | nil>
|
|
||||||
local NextBaseMap <const> = {
|
|
||||||
[RightHandedBattersBox] = nil, -- Runner should not escape the box before a hit!
|
|
||||||
[Bases[First]] = Bases[Second],
|
|
||||||
[Bases[Second]] = Bases[Third],
|
|
||||||
[Bases[Third]] = Bases[Home],
|
|
||||||
}
|
|
||||||
|
|
||||||
---@param name string
|
---@param name string
|
||||||
---@param speed number
|
---@param speed number
|
||||||
---@return Fielder
|
---@return Fielder
|
||||||
|
@ -229,11 +170,6 @@ local fielders <const> = {
|
||||||
right = newFielder("Right", 40),
|
right = newFielder("Right", 40),
|
||||||
}
|
}
|
||||||
|
|
||||||
local PitcherStartPos <const> = {
|
|
||||||
x = C.Screen.W * 0.48,
|
|
||||||
y = C.Screen.H * 0.40,
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Actually only benches the infield, because outfielders are far away!
|
--- Actually only benches the infield, because outfielders are far away!
|
||||||
---@param position XYPair
|
---@param position XYPair
|
||||||
function benchAllFielders(position)
|
function benchAllFielders(position)
|
||||||
|
@ -259,16 +195,13 @@ function resetFielderPositions(fromOffTheField)
|
||||||
fielders.second.target = utils.xy(C.Screen.W * 0.70, C.Screen.H * 0.30)
|
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.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.third.target = utils.xy(C.Screen.W * 0.1, C.Screen.H * 0.48)
|
||||||
fielders.pitcher.target = utils.xy(PitcherStartPos.x, PitcherStartPos.y)
|
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.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.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.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)
|
fielders.right.target = utils.xy(C.Screen.W * 2, fielders.left.target.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
local BatterStartingX <const> = Bases[Home].x - 40
|
|
||||||
local BatterStartingY <const> = Bases[Home].y - 3
|
|
||||||
|
|
||||||
--- @type Runner[]
|
--- @type Runner[]
|
||||||
local runners <const> = {}
|
local runners <const> = {}
|
||||||
|
|
||||||
|
@ -278,11 +211,11 @@ local outRunners <const> = {}
|
||||||
---@return Runner
|
---@return Runner
|
||||||
function newRunner()
|
function newRunner()
|
||||||
local new = {
|
local new = {
|
||||||
x = BatterStartingX - 60,
|
x = C.RightHandedBattersBox.x - 60,
|
||||||
y = BatterStartingY + 60,
|
y = C.RightHandedBattersBox.y + 60,
|
||||||
nextBase = RightHandedBattersBox,
|
nextBase = C.RightHandedBattersBox,
|
||||||
prevBase = nil,
|
prevBase = nil,
|
||||||
forcedTo = Bases[First],
|
forcedTo = C.Bases[C.First],
|
||||||
}
|
}
|
||||||
runners[#runners + 1] = new
|
runners[#runners + 1] = new
|
||||||
return new
|
return new
|
||||||
|
@ -292,7 +225,6 @@ end
|
||||||
local batter = newRunner()
|
local batter = newRunner()
|
||||||
|
|
||||||
local throwMeter = 0
|
local throwMeter = 0
|
||||||
local PitchMeterLimit = 15
|
|
||||||
|
|
||||||
--- "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
|
||||||
|
@ -302,35 +234,35 @@ local PitchMeterLimit = 15
|
||||||
---@param floaty boolean | nil
|
---@param floaty boolean | nil
|
||||||
---@param customBallScaler pd_animator | nil
|
---@param customBallScaler pd_animator | nil
|
||||||
function throwBall(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler)
|
function throwBall(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler)
|
||||||
|
ball.heldBy = nil
|
||||||
|
throwMeter = 0
|
||||||
|
|
||||||
if not flyTimeMs then
|
if not flyTimeMs then
|
||||||
flyTimeMs = utils.distanceBetween(ball.x, ball.y, destX, destY) * 5
|
flyTimeMs = utils.distanceBetween(ball.x, ball.y, destX, destY) * 5
|
||||||
end
|
end
|
||||||
ball.heldBy = nil
|
|
||||||
if customBallScaler then
|
if customBallScaler then
|
||||||
ballSizeAnimator = customBallScaler
|
ballSizeAnimator = customBallScaler
|
||||||
else
|
else
|
||||||
-- TODO? Scale based on distance?
|
-- TODO? Scale based on distance?
|
||||||
ballSizeAnimator = gfx.animator.new(flyTimeMs, 9, SmallestBallRadius, utils.easingHill)
|
ballSizeAnimator = gfx.animator.new(flyTimeMs, 9, C.SmallestBallRadius, utils.easingHill)
|
||||||
end
|
end
|
||||||
ballAnimatorY = gfx.animator.new(flyTimeMs, ball.y, destY, easingFunc)
|
ballAnimatorY = gfx.animator.new(flyTimeMs, ball.y, destY, easingFunc)
|
||||||
ballAnimatorX = gfx.animator.new(flyTimeMs, ball.x, destX, easingFunc)
|
ballAnimatorX = gfx.animator.new(flyTimeMs, ball.x, destX, easingFunc)
|
||||||
if floaty then
|
if floaty then
|
||||||
ballFloatAnimator:reset(flyTimeMs)
|
ballFloatAnimator:reset(flyTimeMs)
|
||||||
end
|
end
|
||||||
throwMeter = 0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local PitchAfterSeconds = 7
|
|
||||||
-- TODO: Replace with a timer, repeatedly reset instead of setting to 0
|
-- TODO: Replace with a timer, repeatedly reset instead of setting to 0
|
||||||
local secondsSincePitchAllowed = -5
|
local secondsSincePitchAllowed = -5
|
||||||
|
|
||||||
local catcherThrownBall = false
|
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)
|
||||||
catcherThrownBall = false
|
catcherThrownBall = false
|
||||||
offenseMode = Offense.batting
|
offenseMode = C.Offense.batting
|
||||||
|
|
||||||
local current = Pitches[pitchTypeIndex]
|
local current = Pitches[pitchTypeIndex]
|
||||||
ballAnimatorX = current.x
|
ballAnimatorX = current.x
|
||||||
|
@ -353,15 +285,13 @@ function pitch(pitchFlyTimeMs, pitchTypeIndex)
|
||||||
secondsSincePitchAllowed = 0
|
secondsSincePitchAllowed = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local BaseHitbox = 10
|
|
||||||
|
|
||||||
--- Returns the base being touched by the player at (x,y), or nil, if no base is being touched
|
--- Returns the base being touched by the player at (x,y), or nil, if no base is being touched
|
||||||
---@param x number
|
---@param x number
|
||||||
---@param y number
|
---@param y number
|
||||||
---@return Base | nil
|
---@return Base | nil
|
||||||
function isTouchingBase(x, y)
|
function isTouchingBase(x, y)
|
||||||
return utils.first(Bases, function(base)
|
return utils.first(C.Bases, function(base)
|
||||||
return utils.distanceBetween(x, y, base.x, base.y) < BaseHitbox
|
return utils.distanceBetween(x, y, base.x, base.y) < C.BaseHitbox
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -386,7 +316,7 @@ end
|
||||||
|
|
||||||
function updateForcedRunners()
|
function updateForcedRunners()
|
||||||
local stillForced = true
|
local stillForced = true
|
||||||
for _, base in ipairs(Bases) do
|
for _, base in ipairs(C.Bases) do
|
||||||
local runnerTargetingBase = getRunnerWithNextBase(base)
|
local runnerTargetingBase = getRunnerWithNextBase(base)
|
||||||
if runnerTargetingBase then
|
if runnerTargetingBase then
|
||||||
if stillForced then
|
if stillForced then
|
||||||
|
@ -462,7 +392,7 @@ end
|
||||||
---@return Base[]
|
---@return Base[]
|
||||||
function getForcedOutTargets()
|
function getForcedOutTargets()
|
||||||
local targets = {}
|
local targets = {}
|
||||||
for _, base in ipairs(Bases) do
|
for _, base in ipairs(C.Bases) do
|
||||||
local runnerTargetingBase = getRunnerWithNextBase(base)
|
local runnerTargetingBase = getRunnerWithNextBase(base)
|
||||||
if runnerTargetingBase then
|
if runnerTargetingBase then
|
||||||
targets[#targets + 1] = base
|
targets[#targets + 1] = base
|
||||||
|
@ -479,7 +409,7 @@ function getBaseOfStrandedRunner()
|
||||||
local farRunnersBase, farDistance
|
local farRunnersBase, farDistance
|
||||||
for _, runner in pairs(runners) do
|
for _, runner in pairs(runners) do
|
||||||
if runner ~= batter then
|
if runner ~= batter then
|
||||||
local nearestBase, distance = utils.getNearestOf(Bases, runner.x, runner.y)
|
local nearestBase, distance = utils.getNearestOf(C.Bases, runner.x, runner.y)
|
||||||
if farRunnersBase == nil or farDistance < distance then
|
if farRunnersBase == nil or farDistance < distance then
|
||||||
farRunnersBase = nearestBase
|
farRunnersBase = nearestBase
|
||||||
farDistance = distance
|
farDistance = distance
|
||||||
|
@ -520,8 +450,8 @@ function tryToMakeAnOut(fielder)
|
||||||
end
|
end
|
||||||
|
|
||||||
function readThrow()
|
function readThrow()
|
||||||
if throwMeter > PitchMeterLimit then
|
if throwMeter > C.ThrowMeterMax then
|
||||||
return (PitchFlyMs / (throwMeter / PitchMeterLimit))
|
return (C.PitchFlyMs / (throwMeter / C.ThrowMeterMax))
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
@ -532,13 +462,13 @@ end
|
||||||
function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome)
|
function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome)
|
||||||
local targetBase
|
local targetBase
|
||||||
if playdate.buttonIsPressed(playdate.kButtonLeft) then
|
if playdate.buttonIsPressed(playdate.kButtonLeft) then
|
||||||
targetBase = Bases[Third]
|
targetBase = C.Bases[C.Third]
|
||||||
elseif playdate.buttonIsPressed(playdate.kButtonUp) then
|
elseif playdate.buttonIsPressed(playdate.kButtonUp) then
|
||||||
targetBase = Bases[Second]
|
targetBase = C.Bases[C.Second]
|
||||||
elseif playdate.buttonIsPressed(playdate.kButtonRight) then
|
elseif playdate.buttonIsPressed(playdate.kButtonRight) then
|
||||||
targetBase = Bases[First]
|
targetBase = C.Bases[C.First]
|
||||||
elseif not forbidThrowHome and playdate.buttonIsPressed(playdate.kButtonDown) then
|
elseif not forbidThrowHome and playdate.buttonIsPressed(playdate.kButtonDown) then
|
||||||
targetBase = Bases[Home]
|
targetBase = C.Bases[C.Home]
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
@ -550,7 +480,7 @@ function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome)
|
||||||
throwBall(targetBase.x, targetBase.y, playdate.easingFunctions.linear, throwFlyMs)
|
throwBall(targetBase.x, targetBase.y, playdate.easingFunctions.linear, throwFlyMs)
|
||||||
closestFielder.target = targetBase
|
closestFielder.target = targetBase
|
||||||
secondsSinceLastRunnerMove = 0
|
secondsSinceLastRunnerMove = 0
|
||||||
offenseMode = Offense.running
|
offenseMode = C.Offense.running
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -566,7 +496,7 @@ function outEligibleRunners(fielder)
|
||||||
and runner.forcedTo == touchedBase
|
and runner.forcedTo == touchedBase
|
||||||
and touchedBase ~= runnerOnBase
|
and touchedBase ~= runnerOnBase
|
||||||
-- Tag out
|
-- Tag out
|
||||||
or not runnerOnBase and utils.distanceBetween(runner.x, runner.y, fielder.x, fielder.y) < TagDistance
|
or not runnerOnBase and utils.distanceBetween(runner.x, runner.y, fielder.x, fielder.y) < C.TagDistance
|
||||||
then
|
then
|
||||||
outRunner(i)
|
outRunner(i)
|
||||||
didOutRunner = true
|
didOutRunner = true
|
||||||
|
@ -577,7 +507,7 @@ function outEligibleRunners(fielder)
|
||||||
end
|
end
|
||||||
|
|
||||||
function updateNpcFielder(fielder, outedSomeRunner)
|
function updateNpcFielder(fielder, outedSomeRunner)
|
||||||
if offenseMode ~= Offense.running then
|
if offenseMode ~= C.Offense.running then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if outedSomeRunner then
|
if outedSomeRunner then
|
||||||
|
@ -603,7 +533,7 @@ function updateFielder(fielder)
|
||||||
|
|
||||||
local outedSomeRunner = outEligibleRunners(fielder)
|
local outedSomeRunner = outEligibleRunners(fielder)
|
||||||
|
|
||||||
if playerIsOn(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(fielders.pitcher, throwFly)
|
||||||
|
@ -626,14 +556,14 @@ function updateRunner(runner, runnerIndex, appliedSpeed)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local nearestBase, nearestBaseDistance = utils.getNearestOf(Bases, runner.x, runner.y)
|
local nearestBase, nearestBaseDistance = utils.getNearestOf(C.Bases, runner.x, runner.y)
|
||||||
|
|
||||||
if
|
if
|
||||||
nearestBaseDistance < 5
|
nearestBaseDistance < 5
|
||||||
and runnerIndex ~= nil
|
and runnerIndex ~= nil
|
||||||
and runner ~= batter --runner.prevBase
|
and runner ~= batter --runner.prevBase
|
||||||
and runner.nextBase == Bases[Home]
|
and runner.nextBase == C.Bases[C.Home]
|
||||||
and nearestBase == Bases[Home]
|
and nearestBase == C.Bases[C.Home]
|
||||||
then
|
then
|
||||||
score(runnerIndex)
|
score(runnerIndex)
|
||||||
end
|
end
|
||||||
|
@ -642,7 +572,7 @@ function updateRunner(runner, runnerIndex, appliedSpeed)
|
||||||
local x, y, distance = utils.normalizeVector(runner.x, runner.y, nb.x, nb.y)
|
local x, y, distance = utils.normalizeVector(runner.x, runner.y, nb.x, nb.y)
|
||||||
|
|
||||||
if distance < 2 then
|
if distance < 2 then
|
||||||
runner.nextBase = NextBaseMap[runner.nextBase]
|
runner.nextBase = C.NextBaseMap[runner.nextBase]
|
||||||
runner.forcedTo = nil
|
runner.forcedTo = nil
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
@ -683,10 +613,10 @@ end
|
||||||
|
|
||||||
function walk()
|
function walk()
|
||||||
announcer:say("Walk!")
|
announcer:say("Walk!")
|
||||||
fielders.first.target = Bases[First]
|
fielders.first.target = C.Bases[C.First]
|
||||||
batter.nextBase = Bases[First]
|
batter.nextBase = C.Bases[C.First]
|
||||||
batter.prevBase = Bases[Home]
|
batter.prevBase = C.Bases[C.Home]
|
||||||
offenseMode = Offense.walking
|
offenseMode = C.Offense.walking
|
||||||
batter = nil
|
batter = nil
|
||||||
updateForcedRunners()
|
updateForcedRunners()
|
||||||
nextBatter()
|
nextBatter()
|
||||||
|
@ -701,25 +631,25 @@ end
|
||||||
|
|
||||||
---@param batDeg number
|
---@param batDeg number
|
||||||
function updateBatting(batDeg, batSpeed)
|
function updateBatting(batDeg, batSpeed)
|
||||||
if ball.y < BallOffscreen then
|
if ball.y < C.BallOffscreen then
|
||||||
ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue()
|
ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue()
|
||||||
ball.size = SmallestBallRadius -- ballFloatAnimator:currentValue()
|
ball.size = C.SmallestBallRadius -- ballFloatAnimator:currentValue()
|
||||||
end
|
end
|
||||||
|
|
||||||
local batAngle = math.rad(batDeg)
|
local batAngle = math.rad(batDeg)
|
||||||
-- TODO: animate bat-flip or something
|
-- TODO: animate bat-flip or something
|
||||||
batBase.x = batter and (batter.x + BatOffset.x) or 0
|
batBase.x = batter and (batter.x + BatterHandPos.x) or 0
|
||||||
batBase.y = batter and (batter.y + BatOffset.y) or 0
|
batBase.y = batter and (batter.y + BatterHandPos.y) or 0
|
||||||
batTip.x = batBase.x + (BatLength * math.sin(batAngle))
|
batTip.x = batBase.x + (C.BatLength * math.sin(batAngle))
|
||||||
batTip.y = batBase.y + (BatLength * math.cos(batAngle))
|
batTip.y = batBase.y + (C.BatLength * math.cos(batAngle))
|
||||||
|
|
||||||
if
|
if
|
||||||
batSpeed > 0
|
batSpeed > 0
|
||||||
and utils.pointDirectlyUnderLine(ball.x, ball.y, batBase.x, batBase.y, batTip.x, batTip.y, C.Screen.H)
|
and utils.pointDirectlyUnderLine(ball.x, ball.y, batBase.x, batBase.y, batTip.x, batTip.y, C.Screen.H)
|
||||||
and ball.y < 232 --not isTouchingBall(fielders.catcher.x, fielders.catcher.y)
|
and ball.y < 232
|
||||||
then
|
then
|
||||||
BatCrackSound:play()
|
BatCrackSound:play()
|
||||||
offenseMode = Offense.running
|
offenseMode = C.Offense.running
|
||||||
local ballAngle = batAngle + math.rad(90)
|
local ballAngle = batAngle + math.rad(90)
|
||||||
|
|
||||||
local mult = math.abs(batSpeed / 15)
|
local mult = math.abs(batSpeed / 15)
|
||||||
|
@ -729,23 +659,17 @@ function updateBatting(batDeg, batSpeed)
|
||||||
ballVelX = ballVelX * -1
|
ballVelX = ballVelX * -1
|
||||||
ballVelY = ballVelY * -1
|
ballVelY = ballVelY * -1
|
||||||
end
|
end
|
||||||
local ballDestX = ball.x + (ballVelX * HitMult)
|
local ballDestX = ball.x + (ballVelX * C.BattingPower)
|
||||||
local ballDestY = ball.y + (ballVelY * HitMult)
|
local ballDestY = ball.y + (ballVelY * C.BattingPower)
|
||||||
-- Hit!
|
-- Hit!
|
||||||
throwBall(
|
local hitBallScaler = gfx.animator.new(2000, 9 + (mult * mult * 0.5), C.SmallestBallRadius, utils.easingHill)
|
||||||
ballDestX,
|
throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000, nil, hitBallScaler)
|
||||||
ballDestY,
|
|
||||||
playdate.easingFunctions.outQuint,
|
|
||||||
2000,
|
|
||||||
nil,
|
|
||||||
gfx.animator.new(2000, 9 + (mult * mult * 0.5), SmallestBallRadius, utils.easingHill)
|
|
||||||
)
|
|
||||||
|
|
||||||
fielders.first.target = Bases[First]
|
fielders.first.target = C.Bases[C.First]
|
||||||
batter.nextBase = Bases[First]
|
batter.nextBase = C.Bases[C.First]
|
||||||
batter.prevBase = Bases[Home]
|
batter.prevBase = C.Bases[C.Home]
|
||||||
updateForcedRunners()
|
updateForcedRunners()
|
||||||
batter.forcedTo = Bases[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(fielders, ballDestX, ballDestY)
|
||||||
|
@ -801,7 +725,7 @@ function updateGameState()
|
||||||
deltaSeconds = playdate.getElapsedTime() or 0
|
deltaSeconds = playdate.getElapsedTime() or 0
|
||||||
playdate.resetElapsedTime()
|
playdate.resetElapsedTime()
|
||||||
local crankChange = playdate.getCrankChange() --[[@as number]]
|
local crankChange = playdate.getCrankChange() --[[@as number]]
|
||||||
local crankLimited = crankChange == 0 and 0 or (math.log(math.abs(crankChange)) * 10)
|
local crankLimited = crankChange == 0 and 0 or (math.log(math.abs(crankChange)) * C.CrankPower)
|
||||||
if crankChange < 0 then
|
if crankChange < 0 then
|
||||||
crankLimited = crankLimited * -1
|
crankLimited = crankLimited * -1
|
||||||
end
|
end
|
||||||
|
@ -815,21 +739,22 @@ function updateGameState()
|
||||||
ball.y = ballAnimatorY:currentValue() + ball.z
|
ball.y = ballAnimatorY:currentValue() + ball.z
|
||||||
end
|
end
|
||||||
|
|
||||||
local playerOnOffense, playerOnDefense = playerIsOn(Sides.offense)
|
local playerOnOffense, playerOnDefense = playerIsOn(C.Sides.offense)
|
||||||
|
|
||||||
if playerOnDefense then
|
if playerOnDefense then
|
||||||
throwMeter = math.max(0, throwMeter - (deltaSeconds * 150))
|
throwMeter = math.max(0, throwMeter - (deltaSeconds * C.ThrowMeterDrainPerSec))
|
||||||
throwMeter = throwMeter + math.abs(crankLimited)
|
throwMeter = throwMeter + math.abs(crankLimited)
|
||||||
end
|
end
|
||||||
|
|
||||||
if offenseMode == Offense.batting then
|
if offenseMode == C.Offense.batting then
|
||||||
if ball.y < C.StrikeZoneStartY then
|
if ball.y < C.StrikeZoneStartY then
|
||||||
pitchTracker.recordedPitchX = nil
|
pitchTracker.recordedPitchX = nil
|
||||||
elseif not pitchTracker.recordedPitchX then
|
elseif not pitchTracker.recordedPitchX then
|
||||||
pitchTracker.recordedPitchX = ball.x
|
pitchTracker.recordedPitchX = ball.x
|
||||||
end
|
end
|
||||||
|
|
||||||
if utils.distanceBetween(fielders.pitcher.x, fielders.pitcher.y, PitchStartX, PitchStartY) < BaseHitbox then
|
local pitcher = fielders.pitcher
|
||||||
|
if utils.distanceBetween(pitcher.x, pitcher.y, C.PitchStartX, C.PitchStartY) < C.BaseHitbox then
|
||||||
secondsSincePitchAllowed = secondsSincePitchAllowed + deltaSeconds
|
secondsSincePitchAllowed = secondsSincePitchAllowed + deltaSeconds
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -840,13 +765,13 @@ function updateGameState()
|
||||||
elseif outcome == PitchOutcomes.Walk then
|
elseif outcome == PitchOutcomes.Walk then
|
||||||
walk()
|
walk()
|
||||||
end
|
end
|
||||||
throwBall(PitchStartX, PitchStartY, playdate.easingFunctions.linear, nil, true)
|
throwBall(C.PitchStartX, C.PitchStartY, playdate.easingFunctions.linear, nil, true)
|
||||||
catcherThrownBall = true
|
catcherThrownBall = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local batSpeed
|
local batSpeed
|
||||||
if playerOnOffense then
|
if playerOnOffense then
|
||||||
batAngleDeg = (playdate.getCrankPosition() + CrankOffsetDeg) % 360
|
batAngleDeg = (playdate.getCrankPosition() + C.CrankOffsetDeg) % 360
|
||||||
batSpeed = crankLimited
|
batSpeed = crankLimited
|
||||||
else
|
else
|
||||||
batAngleDeg = npc.updateBatAngle(ball, catcherThrownBall, deltaSeconds)
|
batAngleDeg = npc.updateBatAngle(ball, catcherThrownBall, deltaSeconds)
|
||||||
|
@ -858,35 +783,35 @@ function updateGameState()
|
||||||
-- TODO: Ensure batter can't be nil, here
|
-- TODO: Ensure batter can't be nil, here
|
||||||
updateRunner(batter, nil, crankLimited)
|
updateRunner(batter, nil, crankLimited)
|
||||||
|
|
||||||
if secondsSincePitchAllowed > PitchAfterSeconds then
|
if secondsSincePitchAllowed > C.PitchAfterSeconds then
|
||||||
if playerOnDefense then
|
if playerOnDefense then
|
||||||
local throwFly = readThrow()
|
local throwFly = readThrow()
|
||||||
if throwFly and not buttonControlledThrow(fielders.pitcher, throwFly, true) then
|
if throwFly and not buttonControlledThrow(pitcher, throwFly, true) then
|
||||||
playerPitch(throwFly)
|
playerPitch(throwFly)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
pitch(PitchFlyMs, math.random(#Pitches))
|
pitch(C.PitchFlyMs, math.random(#Pitches))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif offenseMode == Offense.running then
|
elseif offenseMode == C.Offense.running then
|
||||||
local appliedSpeed = playerOnOffense and crankLimited or npc.runningSpeed(Bases, runners)
|
local appliedSpeed = playerOnOffense and crankLimited or npc.runningSpeed(runners)
|
||||||
if updateRunning(appliedSpeed) then
|
if updateRunning(appliedSpeed) then
|
||||||
secondsSinceLastRunnerMove = 0
|
secondsSinceLastRunnerMove = 0
|
||||||
else
|
else
|
||||||
secondsSinceLastRunnerMove = secondsSinceLastRunnerMove + deltaSeconds
|
secondsSinceLastRunnerMove = secondsSinceLastRunnerMove + deltaSeconds
|
||||||
if secondsSinceLastRunnerMove > ResetFieldersAfterSeconds then
|
if secondsSinceLastRunnerMove > ResetFieldersAfterSeconds then
|
||||||
throwBall(PitchStartX, PitchStartY, playdate.easingFunctions.linear, nil, true)
|
throwBall(C.PitchStartX, C.PitchStartY, playdate.easingFunctions.linear, nil, true)
|
||||||
resetFielderPositions()
|
resetFielderPositions()
|
||||||
offenseMode = Offense.batting
|
offenseMode = C.Offense.batting
|
||||||
if not batter then
|
if not batter then
|
||||||
batter = newRunner()
|
batter = newRunner()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif offenseMode == Offense.walking then
|
elseif offenseMode == C.Offense.walking then
|
||||||
updateForcedRunners()
|
updateForcedRunners()
|
||||||
if not updateRunning(10, true) then
|
if not updateRunning(C.WalkedRunnerSpeed, true) then
|
||||||
offenseMode = Offense.batting
|
offenseMode = C.Offense.batting
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -896,26 +821,6 @@ function updateGameState()
|
||||||
walkAwayOutRunners()
|
walkAwayOutRunners()
|
||||||
end
|
end
|
||||||
|
|
||||||
local MinimapSizeX, MinimapSizeY <const> = Minimap:getSize()
|
|
||||||
local MinimapPosX, MinimapPosY = C.Screen.W - MinimapSizeX, C.Screen.H - MinimapSizeY
|
|
||||||
|
|
||||||
local FieldHeight <const> = Bases[Home].y - Bases[Second].y
|
|
||||||
|
|
||||||
local MinimapMultX <const> = 0.75 * MinimapSizeX / C.Screen.W
|
|
||||||
local MinimapOffsetX <const> = MinimapPosX + 5
|
|
||||||
local MinimapMultY <const> = 0.70 * MinimapSizeY / FieldHeight
|
|
||||||
local MinimapOffsetY <const> = MinimapPosY - 15
|
|
||||||
|
|
||||||
function drawMinimap()
|
|
||||||
Minimap:draw(MinimapPosX, MinimapPosY)
|
|
||||||
gfx.setColor(gfx.kColorBlack)
|
|
||||||
for _, runner in pairs(runners) do
|
|
||||||
local x = (MinimapMultX * runner.x) + MinimapOffsetX
|
|
||||||
local y = (MinimapMultY * runner.y) + MinimapOffsetY
|
|
||||||
gfx.fillRect(x, y, 8, 8)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function playdate.update()
|
function playdate.update()
|
||||||
playdate.timer.updateTimers()
|
playdate.timer.updateTimers()
|
||||||
gfx.animation.blinker.updateAll()
|
gfx.animation.blinker.updateAll()
|
||||||
|
@ -925,7 +830,7 @@ function playdate.update()
|
||||||
gfx.setColor(gfx.kColorBlack)
|
gfx.setColor(gfx.kColorBlack)
|
||||||
|
|
||||||
local offsetX, offsetY = 0, 0
|
local offsetX, offsetY = 0, 0
|
||||||
if ball.x < BallOffscreen then
|
if ball.x < C.BallOffscreen then
|
||||||
offsetX, offsetY = getDrawOffset(C.Screen.W, C.Screen.H, ball.x, ball.y)
|
offsetX, offsetY = getDrawOffset(C.Screen.W, C.Screen.H, ball.x, ball.y)
|
||||||
gfx.setDrawOffset(offsetX, offsetY)
|
gfx.setDrawOffset(offsetX, offsetY)
|
||||||
end
|
end
|
||||||
|
@ -938,7 +843,7 @@ function playdate.update()
|
||||||
ballIsHeld = drawFielder(ball, fielder.x, fielder.y + fielderDanceHeight) or ballIsHeld
|
ballIsHeld = drawFielder(ball, fielder.x, fielder.y + fielderDanceHeight) or ballIsHeld
|
||||||
end
|
end
|
||||||
|
|
||||||
if offenseMode == Offense.batting then
|
if offenseMode == C.Offense.batting then
|
||||||
gfx.setLineWidth(5)
|
gfx.setLineWidth(5)
|
||||||
gfx.drawLine(batBase.x, batBase.y, batTip.x, batTip.y)
|
gfx.drawLine(batBase.x, batBase.y, batTip.x, batTip.y)
|
||||||
end
|
end
|
||||||
|
@ -976,7 +881,7 @@ function playdate.update()
|
||||||
|
|
||||||
gfx.setDrawOffset(0, 0)
|
gfx.setDrawOffset(0, 0)
|
||||||
if math.abs(offsetX) > 10 or math.abs(offsetY) > 10 then
|
if math.abs(offsetX) > 10 or math.abs(offsetY) > 10 then
|
||||||
drawMinimap()
|
drawMinimap(runners)
|
||||||
end
|
end
|
||||||
drawScoreboard(0, C.Screen.H * 0.77, teams, outs, battingTeam, inning)
|
drawScoreboard(0, C.Screen.H * 0.77, teams, outs, battingTeam, inning)
|
||||||
drawBallsAndStrikes(290, C.Screen.H - 20, pitchTracker.balls, pitchTracker.strikes)
|
drawBallsAndStrikes(290, C.Screen.H - 20, pitchTracker.balls, pitchTracker.strikes)
|
||||||
|
@ -991,7 +896,7 @@ function init()
|
||||||
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()
|
||||||
throwBall(PitchStartX, PitchStartY, playdate.easingFunctions.linear, nil, false)
|
throwBall(C.PitchStartX, C.PitchStartY, playdate.easingFunctions.linear, nil, false)
|
||||||
end)
|
end)
|
||||||
BootTune:play()
|
BootTune:play()
|
||||||
BootTune:setFinishCallback(function()
|
BootTune:setFinishCallback(function()
|
||||||
|
|
|
@ -19,12 +19,12 @@ function npc.batSpeed()
|
||||||
return npcBatSpeed
|
return npcBatSpeed
|
||||||
end
|
end
|
||||||
|
|
||||||
function npc.runningSpeed(baseConstants, runners)
|
function npc.runningSpeed(runners)
|
||||||
if #runners == 0 then
|
if #runners == 0 then
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
local touchedBase = isTouchingBase(runners[1].x, runners[1].y)
|
local touchedBase = isTouchingBase(runners[1].x, runners[1].y)
|
||||||
if not touchedBase or touchedBase == baseConstants[4] then
|
if not touchedBase or touchedBase == C.Bases[C.Home] then
|
||||||
return 10
|
return 10
|
||||||
end
|
end
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
-- stylua: ignore start
|
|
||||||
import 'CoreLibs/animation.lua'
|
|
||||||
import 'CoreLibs/graphics.lua'
|
|
||||||
-- stylua: ignore end
|
|
||||||
|
|
||||||
-- selene: allow(unscoped_variables)
|
-- selene: allow(unscoped_variables)
|
||||||
utils = {}
|
utils = {}
|
||||||
|
|
||||||
|
@ -14,6 +9,11 @@ function utils.easingHill(t, b, c, d)
|
||||||
return (c * t) + b
|
return (c * t) + b
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @alias XYPair {
|
||||||
|
--- x: number,
|
||||||
|
--- y: number,
|
||||||
|
--- }
|
||||||
|
|
||||||
---@param x number
|
---@param x number
|
||||||
---@param y number
|
---@param y number
|
||||||
---@return XYPair
|
---@return XYPair
|
||||||
|
|
Loading…
Reference in New Issue