NPCs can play each other
Some timing tweaks and TODOs. Cluster global state tighter together.
This commit is contained in:
parent
b9d25e18d8
commit
1926960c86
|
@ -23,7 +23,7 @@ function Announcer.new()
|
||||||
}, { __index = Announcer })
|
}, { __index = Announcer })
|
||||||
end
|
end
|
||||||
|
|
||||||
local DurationMs <const> = 3000
|
local DurationMs <const> = 2000
|
||||||
|
|
||||||
function Announcer:popIn()
|
function Announcer:popIn()
|
||||||
self.animatorY = AnnouncerAnimatorInY
|
self.animatorY = AnnouncerAnimatorInY
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
---@field onThirdOut fun()
|
---@field onThirdOut fun()
|
||||||
Baserunning = {}
|
Baserunning = {}
|
||||||
|
|
||||||
|
-- TODO: Implement slides. Would require making fielders' gloves "real objects" whose state is tracked.
|
||||||
|
|
||||||
---@param announcer any
|
---@param announcer any
|
||||||
---@return Baserunning
|
---@return Baserunning
|
||||||
function Baserunning.new(announcer, onThirdOut)
|
function Baserunning.new(announcer, onThirdOut)
|
||||||
|
@ -196,6 +198,9 @@ function Baserunning:updateRunner(runner, runnerIndex, appliedSpeed, deltaSecond
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- TODO: Make this less "sticky" for the user.
|
||||||
|
-- Currently it can be a little hard to run *past* a base.
|
||||||
|
|
||||||
local autoRun = (nearestBaseDistance > 40 or runner.forcedTo) and mult * autoRunSpeed
|
local autoRun = (nearestBaseDistance > 40 or runner.forcedTo) and mult * autoRunSpeed
|
||||||
or nearestBaseDistance < 5 and 0
|
or nearestBaseDistance < 5 and 0
|
||||||
or (nearestBase == runner.nextBase and autoRunSpeed or -1 * autoRunSpeed)
|
or (nearestBase == runner.nextBase and autoRunSpeed or -1 * autoRunSpeed)
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
--- speed: number,
|
--- speed: number,
|
||||||
--- }
|
--- }
|
||||||
|
|
||||||
|
-- TODO: Run down baserunners in a pickle.
|
||||||
|
|
||||||
-- selene: allow(unscoped_variables)
|
-- selene: allow(unscoped_variables)
|
||||||
---@class Fielding
|
---@class Fielding
|
||||||
---@field fielders table<string, Fielder>
|
---@field fielders table<string, Fielder>
|
||||||
|
@ -117,6 +119,9 @@ function Fielding:updateFielderPositions(ball, deltaSeconds)
|
||||||
fielderTouchingBall = fielder
|
fielderTouchingBall = fielder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
-- TODO: The need is growing for a distinction between touching the ball and holding the ball.
|
||||||
|
-- Or, at least, fielders need to start *stopping* the ball when they make contact with it.
|
||||||
|
-- Right now, a line-drive *through* first will be counted as an out.
|
||||||
self.fielderTouchingBall = fielderTouchingBall
|
self.fielderTouchingBall = fielderTouchingBall
|
||||||
return fielderTouchingBall
|
return fielderTouchingBall
|
||||||
end
|
end
|
||||||
|
|
73
src/main.lua
73
src/main.lua
|
@ -42,15 +42,23 @@ local gfx <const>, C <const> = playdate.graphics, C
|
||||||
|
|
||||||
local announcer = Announcer.new()
|
local announcer = Announcer.new()
|
||||||
local fielding = Fielding.new()
|
local fielding = Fielding.new()
|
||||||
|
-- TODO: Find a way to get baserunning and npc instantiated closer to the top, here.
|
||||||
|
-- Currently difficult because they depend on nextHalfInning/each other.
|
||||||
|
|
||||||
---@alias SimpleAnimator { currentValue: fun(self): number; reset: fun(self, durationMs: number | nil) }
|
------------------
|
||||||
|
-- GLOBAL STATE --
|
||||||
|
------------------
|
||||||
|
|
||||||
local deltaSeconds = 0
|
local deltaSeconds = 0
|
||||||
|
|
||||||
local ball = Ball.new(gfx.animator)
|
local ball = Ball.new(gfx.animator)
|
||||||
|
|
||||||
---@alias Team { score: number, benchPosition: XyPair }
|
local batBase <const> = utils.xy(C.Center.x - 34, 215)
|
||||||
|
local batTip <const> = utils.xy(0, 0)
|
||||||
|
local batAngleDeg = C.CrankOffsetDeg
|
||||||
|
|
||||||
|
local catcherThrownBall = false
|
||||||
|
|
||||||
|
---@alias Team { score: number, benchPosition: XyPair }
|
||||||
---@type table<string, Team>
|
---@type table<string, Team>
|
||||||
local teams <const> = {
|
local teams <const> = {
|
||||||
home = {
|
home = {
|
||||||
|
@ -63,25 +71,28 @@ local teams <const> = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local UserTeam <const> = teams.away
|
|
||||||
local battingTeam = teams.away
|
|
||||||
local battingTeamSprites = AwayTeamSprites
|
|
||||||
local fieldingTeamSprites = HomeTeamSprites
|
|
||||||
local runnerBlipper = battingTeam == teams.away and AwayTeamBlipper or HomeTeamBlipper
|
|
||||||
local inning = 1
|
local inning = 1
|
||||||
|
|
||||||
|
local battingTeam = teams.away
|
||||||
local offenseState = C.Offense.batting
|
local offenseState = C.Offense.batting
|
||||||
|
|
||||||
-- TODO: Replace with timers, repeatedly reset, instead of constantly setting to 0
|
-- TODO: Replace with timers, repeatedly reset, instead of constantly setting to 0
|
||||||
local secondsSinceLastRunnerMove = 0
|
local secondsSinceLastRunnerMove = 0
|
||||||
local secondsSincePitchAllowed = -5
|
local secondsSincePitchAllowed = 0
|
||||||
|
|
||||||
local catcherThrownBall = false
|
-- These are only sort-of global state. They are purely graphical,
|
||||||
|
-- but they need to be kept in sync with the rest of the globals.
|
||||||
|
local runnerBlipper = battingTeam == teams.away and AwayTeamBlipper or HomeTeamBlipper
|
||||||
|
local battingTeamSprites = AwayTeamSprites
|
||||||
|
local fieldingTeamSprites = HomeTeamSprites
|
||||||
|
|
||||||
local batBase <const> = utils.xy(C.Center.x - 34, 215)
|
-------------------------
|
||||||
local batTip <const> = utils.xy(0, 0)
|
-- END OF GLOBAL STATE --
|
||||||
|
-------------------------
|
||||||
|
|
||||||
local batAngleDeg = C.CrankOffsetDeg
|
local UserTeam <const> = teams.away
|
||||||
|
|
||||||
|
---@alias SimpleAnimator { currentValue: fun(self): number; reset: fun(self, durationMs: number | nil) }
|
||||||
---@alias Pitch { x: SimpleAnimator, y: SimpleAnimator, z: SimpleAnimator | nil }
|
---@alias Pitch { x: SimpleAnimator, y: SimpleAnimator, z: SimpleAnimator | nil }
|
||||||
|
|
||||||
---@type Pitch[]
|
---@type Pitch[]
|
||||||
|
@ -113,8 +124,12 @@ local Pitches <const> = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
---@return boolean userIsOnSide, boolean playerIsOnOtherSide
|
---@return boolean userIsOnSide, boolean userIsOnOtherSide
|
||||||
local function userIsOn(side)
|
local function userIsOn(side)
|
||||||
|
if UserTeam == nil then
|
||||||
|
-- Both teams are NPC-driven
|
||||||
|
return false, false
|
||||||
|
end
|
||||||
local ret
|
local ret
|
||||||
if UserTeam == battingTeam then
|
if UserTeam == battingTeam then
|
||||||
ret = side == C.Sides.offense
|
ret = side == C.Sides.offense
|
||||||
|
@ -124,13 +139,7 @@ local function userIsOn(side)
|
||||||
return ret, not ret
|
return ret, not ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Launches the ball from its current position to the given destination.
|
---@type LaunchBall
|
||||||
---@param destX number
|
|
||||||
---@param destY number
|
|
||||||
---@param easingFunc EasingFunc
|
|
||||||
---@param flyTimeMs number | nil
|
|
||||||
---@param floaty boolean | nil
|
|
||||||
---@param customBallScaler pd_animator | nil
|
|
||||||
local function launchBall(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler)
|
local function launchBall(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler)
|
||||||
throwMeter:reset()
|
throwMeter:reset()
|
||||||
ball:launch(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler)
|
ball:launch(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler)
|
||||||
|
@ -174,10 +183,15 @@ local function nextHalfInning()
|
||||||
announcer:say("SWITCHING SIDES...")
|
announcer:say("SWITCHING SIDES...")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: Make the overlay handle its own dang delay.
|
if gameOver then
|
||||||
-- Delay to keep end-of-inning on the scoreboard for a few seconds
|
announcer:say("AND THAT'S THE BALL GAME!")
|
||||||
playdate.timer.new(3000, function()
|
else
|
||||||
|
fielding:resetFielderPositions()
|
||||||
|
if battingTeam == teams.home then
|
||||||
|
inning = inning + 1
|
||||||
|
end
|
||||||
battingTeam = currentlyFieldingTeam
|
battingTeam = currentlyFieldingTeam
|
||||||
|
playdate.timer.new(2000, function()
|
||||||
if battingTeam == teams.home then
|
if battingTeam == teams.home then
|
||||||
battingTeamSprites = HomeTeamSprites
|
battingTeamSprites = HomeTeamSprites
|
||||||
runnerBlipper = HomeTeamBlipper
|
runnerBlipper = HomeTeamBlipper
|
||||||
|
@ -187,15 +201,8 @@ local function nextHalfInning()
|
||||||
fieldingTeamSprites = HomeTeamSprites
|
fieldingTeamSprites = HomeTeamSprites
|
||||||
runnerBlipper = AwayTeamBlipper
|
runnerBlipper = AwayTeamBlipper
|
||||||
end
|
end
|
||||||
if gameOver then
|
|
||||||
announcer:say("AND THAT'S THE BALL GAME!")
|
|
||||||
else
|
|
||||||
fielding:resetFielderPositions()
|
|
||||||
if battingTeam == teams.home then
|
|
||||||
inning = inning + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local baserunning = Baserunning.new(announcer, nextHalfInning)
|
local baserunning = Baserunning.new(announcer, nextHalfInning)
|
||||||
|
@ -443,7 +450,7 @@ local function updateGameState()
|
||||||
end
|
end
|
||||||
if fielderHoldingBall then
|
if fielderHoldingBall then
|
||||||
local outedSomeRunner = baserunning:outEligibleRunners(fielderHoldingBall)
|
local outedSomeRunner = baserunning:outEligibleRunners(fielderHoldingBall)
|
||||||
if userOnOffense then
|
if not userOnDefense then
|
||||||
npc:fielderAction(offenseState, fielderHoldingBall, outedSomeRunner, ball, launchBall)
|
npc:fielderAction(offenseState, fielderHoldingBall, outedSomeRunner, ball, launchBall)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,7 +48,7 @@ function Npc:runningSpeed(ball)
|
||||||
|
|
||||||
local runner1 = self.runners[1]
|
local runner1 = self.runners[1]
|
||||||
|
|
||||||
local ballIsFar = utils.distanceBetweenZ(ball.x, ball.y, ball.z, runner1.x, runner1.y, 0) > 250
|
local ballIsFar = utils.distanceBetweenZ(ball.x, ball.y, ball.z, runner1.x, runner1.y, 0) > 300
|
||||||
|
|
||||||
if ballIsFar or runner1.forcedTo then
|
if ballIsFar or runner1.forcedTo then
|
||||||
return baseRunningSpeed
|
return baseRunningSpeed
|
||||||
|
|
Loading…
Reference in New Issue