Add basic player-controlled fielding.
Pull out a dbg.lua for functions that can be called for testing certain scenarios.
This commit is contained in:
parent
4fc49d3631
commit
2867b4a367
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
|||
SOURCE_FILES := src/utils.lua src/announcer.lua src/graphics.lua src/scoreboard.lua src/main.lua
|
||||
SOURCE_FILES := src/utils.lua src/dbg.lua src/announcer.lua src/graphics.lua src/scoreboard.lua src/main.lua
|
||||
|
||||
all:
|
||||
pdc src BatterUp.pdx
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
-- selene: allow(unscoped_variables)
|
||||
dbg = {}
|
||||
|
||||
-- selene: allow(unused_variable)
|
||||
function dbg.label(value, name)
|
||||
if type(value) == "table" then
|
||||
print(name .. ":")
|
||||
printTable(value)
|
||||
else
|
||||
print(name .. ": " .. value)
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
-- Only works if called with the bases empty (i.e. the only runner should be the batter.
|
||||
-- selene: allow(unused_variable)
|
||||
function dbg.loadTheBases(baseConstants, runners)
|
||||
newRunner()
|
||||
newRunner()
|
||||
newRunner()
|
||||
runners[2].x = baseConstants[1].x
|
||||
runners[2].y = baseConstants[1].y
|
||||
runners[2].nextBase = baseConstants[2]
|
||||
|
||||
runners[3].x = baseConstants[2].x
|
||||
runners[3].y = baseConstants[2].y
|
||||
runners[3].nextBase = baseConstants[3]
|
||||
|
||||
runners[4].x = baseConstants[3].x
|
||||
runners[4].y = baseConstants[3].y
|
||||
runners[4].nextBase = baseConstants[4]
|
||||
end
|
||||
|
||||
if not playdate then
|
||||
return dbg
|
||||
end
|
158
src/main.lua
158
src/main.lua
|
@ -35,6 +35,7 @@ import 'CoreLibs/ui.lua'
|
|||
--- @alias EasingFunc fun(number, number, number, number): number
|
||||
|
||||
import 'announcer.lua'
|
||||
import 'dbg.lua'
|
||||
import 'graphics.lua'
|
||||
import 'scoreboard.lua'
|
||||
import 'utils.lua'
|
||||
|
@ -503,18 +504,50 @@ function tryToMakeAnOut(fielder)
|
|||
end
|
||||
end
|
||||
|
||||
---@param fielder Fielder
|
||||
function updateFielder(fielder)
|
||||
if fielder.target ~= nil then
|
||||
if not utils.moveAtSpeed(fielder, fielder.speed * deltaSeconds, fielder.target) then
|
||||
fielder.target = nil
|
||||
local throwMeter = 0
|
||||
local PitchMeterLimit = 15
|
||||
|
||||
function readThrow()
|
||||
if throwMeter > PitchMeterLimit then
|
||||
return (PitchFlyMs / (throwMeter / PitchMeterLimit))
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param thrower Fielder
|
||||
---@param throwFlyMs number
|
||||
---@return boolean didThrow
|
||||
function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome)
|
||||
local targetBase
|
||||
if playdate.buttonIsPressed(playdate.kButtonLeft) then
|
||||
targetBase = Bases[Third]
|
||||
elseif playdate.buttonIsPressed(playdate.kButtonUp) then
|
||||
targetBase = Bases[Second]
|
||||
elseif playdate.buttonIsPressed(playdate.kButtonRight) then
|
||||
targetBase = Bases[First]
|
||||
elseif not forbidThrowHome and playdate.buttonIsPressed(playdate.kButtonDown) then
|
||||
targetBase = Bases[Home]
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
if offenseMode ~= Offense.running or not isTouchingBall(fielder.x, fielder.y) then
|
||||
local closestFielder = utils.getNearestOf(fielders, targetBase.x, targetBase.y, function(fielder)
|
||||
return fielder ~= thrower
|
||||
end)
|
||||
|
||||
throwBall(targetBase.x, targetBase.y, playdate.easingFunctions.linear, throwFlyMs)
|
||||
closestFielder.target = targetBase
|
||||
secondsSinceLastRunnerMove = 0
|
||||
offenseMode = Offense.running
|
||||
throwMeter = 0
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function updateNpcFielder(fielder)
|
||||
if offenseMode ~= Offense.running then
|
||||
return
|
||||
end
|
||||
|
||||
local touchedBase = isTouchingBase(fielder.x, fielder.y)
|
||||
for i, runner in pairs(runners) do
|
||||
local runnerOnBase = isTouchingBase(runner.x, runner.y)
|
||||
|
@ -538,6 +571,28 @@ function updateFielder(fielder)
|
|||
end
|
||||
end
|
||||
|
||||
---@param fielder Fielder
|
||||
function updateFielder(fielder)
|
||||
if fielder.target ~= nil then
|
||||
if not utils.moveAtSpeed(fielder, fielder.speed * deltaSeconds, fielder.target) then
|
||||
fielder.target = nil
|
||||
end
|
||||
end
|
||||
|
||||
if not isTouchingBall(fielder.x, fielder.y) then
|
||||
return
|
||||
end
|
||||
|
||||
if playerIsOn(Sides.defense) then
|
||||
local throwFly = readThrow()
|
||||
if throwFly then
|
||||
buttonControlledThrow(fielders.pitcher, throwFly)
|
||||
end
|
||||
else
|
||||
updateNpcFielder(fielder)
|
||||
end
|
||||
end
|
||||
|
||||
function updateFielders()
|
||||
for _, fielder in pairs(fielders) do
|
||||
updateFielder(fielder)
|
||||
|
@ -694,6 +749,20 @@ function walkAwayOutRunners()
|
|||
end
|
||||
end
|
||||
|
||||
function playerPitch(throwFly)
|
||||
local aButton = playdate.buttonIsPressed(playdate.kButtonA)
|
||||
local bButton = playdate.buttonIsPressed(playdate.kButtonB)
|
||||
if not aButton and not bButton then
|
||||
pitch(throwFly, 1)
|
||||
elseif aButton and not bButton then
|
||||
pitch(throwFly, 2)
|
||||
elseif not aButton and bButton then
|
||||
pitch(throwFly, 3)
|
||||
elseif aButton and bButton then
|
||||
pitch(throwFly, 4)
|
||||
end
|
||||
end
|
||||
|
||||
local npcBatDeg = 0
|
||||
local NpcBatSpeed <const> = 1500
|
||||
|
||||
|
@ -721,45 +790,6 @@ function npcRunningSpeed()
|
|||
return 0
|
||||
end
|
||||
|
||||
local throwMeter = 0
|
||||
local PitchMeterLimit = 25
|
||||
|
||||
function readThrow()
|
||||
if throwMeter > PitchMeterLimit then
|
||||
return (PitchFlyMs / (throwMeter / PitchMeterLimit))
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param thrower Fielder
|
||||
---@param throwFlyMs number
|
||||
---@return boolean didThrow
|
||||
function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome)
|
||||
local targetBase
|
||||
if playdate.buttonIsPressed(playdate.kButtonLeft) then
|
||||
targetBase = Bases[Third]
|
||||
elseif playdate.buttonIsPressed(playdate.kButtonUp) then
|
||||
targetBase = Bases[Second]
|
||||
elseif playdate.buttonIsPressed(playdate.kButtonRight) then
|
||||
targetBase = Bases[First]
|
||||
elseif not forbidThrowHome and playdate.buttonIsPressed(playdate.kButtonDown) then
|
||||
targetBase = Bases[Home]
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
local closestFielder = utils.getNearestOf(fielders, targetBase.x, targetBase.y, function(fielder)
|
||||
return fielder ~= thrower
|
||||
end)
|
||||
|
||||
throwBall(targetBase.x, targetBase.y, playdate.easingFunctions.linear, throwFlyMs)
|
||||
closestFielder.target = targetBase
|
||||
secondsSinceLastRunnerMove = 0
|
||||
offenseMode = Offense.running
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function updateGameState()
|
||||
deltaSeconds = playdate.getElapsedTime() or 0
|
||||
playdate.resetElapsedTime()
|
||||
|
@ -773,8 +803,8 @@ function updateGameState()
|
|||
ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue()
|
||||
end
|
||||
|
||||
if offenseMode == Offense.batting then
|
||||
local playerOnOffense = playerIsOn(Sides.offense)
|
||||
if offenseMode == Offense.batting then
|
||||
secondsSincePitchAllowed = secondsSincePitchAllowed + deltaSeconds
|
||||
|
||||
if secondsSincePitchAllowed > 3.5 and not catcherThrownBall then
|
||||
|
@ -784,37 +814,35 @@ function updateGameState()
|
|||
throwMeter = 0
|
||||
end
|
||||
|
||||
if not playerOnOffense then
|
||||
local batSpeed
|
||||
if playerOnOffense then
|
||||
batAngleDeg, batSpeed = (playdate.getCrankPosition() + CrankOffsetDeg) % 360, crankChange
|
||||
else
|
||||
throwMeter = math.max(0, throwMeter - (deltaSeconds * 150))
|
||||
throwMeter = throwMeter + crankChange
|
||||
batAngleDeg, batSpeed = npcBatAngle(), npcBatChange()
|
||||
end
|
||||
|
||||
updateBatting(batAngleDeg, batSpeed)
|
||||
|
||||
-- TODO: Ensure batter can't be nil, here
|
||||
updateRunner(batter, nil, crankChange)
|
||||
|
||||
if secondsSincePitchAllowed > PitchAfterSeconds then
|
||||
if playerOnOffense then
|
||||
pitch(PitchFlyMs, math.random(#Pitches))
|
||||
else
|
||||
local throwFly = readThrow()
|
||||
if throwFly and not buttonControlledThrow(fielders.pitcher, throwFly, true) then
|
||||
local aButton = playdate.buttonIsPressed(playdate.kButtonA)
|
||||
local bButton = playdate.buttonIsPressed(playdate.kButtonB)
|
||||
if not aButton and not bButton then
|
||||
pitch(throwFly, 1)
|
||||
elseif aButton and not bButton then
|
||||
pitch(throwFly, 2)
|
||||
elseif not aButton and bButton then
|
||||
pitch(throwFly, 3)
|
||||
elseif aButton and bButton then
|
||||
pitch(throwFly, 4)
|
||||
playerPitch(throwFly)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
batAngleDeg = playerOnOffense and ((playdate.getCrankPosition() + CrankOffsetDeg) % 360) or npcBatAngle()
|
||||
local batSpeed = playerOnOffense and crankChange or npcBatChange()
|
||||
updateBatting(batAngleDeg, batSpeed)
|
||||
-- TODO: Ensure batter can't be nil, here
|
||||
updateRunner(batter, nil, crankChange)
|
||||
elseif offenseMode == Offense.running then
|
||||
if not playerOnOffense then
|
||||
throwMeter = math.max(0, throwMeter - (deltaSeconds * 150))
|
||||
throwMeter = throwMeter + crankChange
|
||||
end
|
||||
local appliedSpeed = playerIsOn(Sides.offense) and crankChange or npcRunningSpeed()
|
||||
if updateRunning(appliedSpeed) then
|
||||
secondsSinceLastRunnerMove = 0
|
||||
|
@ -872,9 +900,9 @@ end
|
|||
|
||||
function playdate.update()
|
||||
playdate.timer.updateTimers()
|
||||
gfx.animation.blinker.updateAll()
|
||||
|
||||
updateGameState()
|
||||
gfx.animation.blinker.updateAll()
|
||||
|
||||
gfx.clear()
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
|
|
Loading…
Reference in New Issue