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:
|
all:
|
||||||
pdc src BatterUp.pdx
|
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
|
--- @alias EasingFunc fun(number, number, number, number): number
|
||||||
|
|
||||||
import 'announcer.lua'
|
import 'announcer.lua'
|
||||||
|
import 'dbg.lua'
|
||||||
import 'graphics.lua'
|
import 'graphics.lua'
|
||||||
import 'scoreboard.lua'
|
import 'scoreboard.lua'
|
||||||
import 'utils.lua'
|
import 'utils.lua'
|
||||||
|
@ -503,18 +504,50 @@ function tryToMakeAnOut(fielder)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param fielder Fielder
|
local throwMeter = 0
|
||||||
function updateFielder(fielder)
|
local PitchMeterLimit = 15
|
||||||
if fielder.target ~= nil then
|
|
||||||
if not utils.moveAtSpeed(fielder, fielder.speed * deltaSeconds, fielder.target) then
|
function readThrow()
|
||||||
fielder.target = nil
|
if throwMeter > PitchMeterLimit then
|
||||||
|
return (PitchFlyMs / (throwMeter / PitchMeterLimit))
|
||||||
end
|
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
|
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
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local touchedBase = isTouchingBase(fielder.x, fielder.y)
|
local touchedBase = isTouchingBase(fielder.x, fielder.y)
|
||||||
for i, runner in pairs(runners) do
|
for i, runner in pairs(runners) do
|
||||||
local runnerOnBase = isTouchingBase(runner.x, runner.y)
|
local runnerOnBase = isTouchingBase(runner.x, runner.y)
|
||||||
|
@ -538,6 +571,28 @@ function updateFielder(fielder)
|
||||||
end
|
end
|
||||||
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()
|
function updateFielders()
|
||||||
for _, fielder in pairs(fielders) do
|
for _, fielder in pairs(fielders) do
|
||||||
updateFielder(fielder)
|
updateFielder(fielder)
|
||||||
|
@ -694,6 +749,20 @@ function walkAwayOutRunners()
|
||||||
end
|
end
|
||||||
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 npcBatDeg = 0
|
||||||
local NpcBatSpeed <const> = 1500
|
local NpcBatSpeed <const> = 1500
|
||||||
|
|
||||||
|
@ -721,45 +790,6 @@ function npcRunningSpeed()
|
||||||
return 0
|
return 0
|
||||||
end
|
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()
|
function updateGameState()
|
||||||
deltaSeconds = playdate.getElapsedTime() or 0
|
deltaSeconds = playdate.getElapsedTime() or 0
|
||||||
playdate.resetElapsedTime()
|
playdate.resetElapsedTime()
|
||||||
|
@ -773,8 +803,8 @@ function updateGameState()
|
||||||
ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue()
|
ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue()
|
||||||
end
|
end
|
||||||
|
|
||||||
if offenseMode == Offense.batting then
|
|
||||||
local playerOnOffense = playerIsOn(Sides.offense)
|
local playerOnOffense = playerIsOn(Sides.offense)
|
||||||
|
if offenseMode == Offense.batting then
|
||||||
secondsSincePitchAllowed = secondsSincePitchAllowed + deltaSeconds
|
secondsSincePitchAllowed = secondsSincePitchAllowed + deltaSeconds
|
||||||
|
|
||||||
if secondsSincePitchAllowed > 3.5 and not catcherThrownBall then
|
if secondsSincePitchAllowed > 3.5 and not catcherThrownBall then
|
||||||
|
@ -784,37 +814,35 @@ function updateGameState()
|
||||||
throwMeter = 0
|
throwMeter = 0
|
||||||
end
|
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 = math.max(0, throwMeter - (deltaSeconds * 150))
|
||||||
throwMeter = throwMeter + crankChange
|
throwMeter = throwMeter + crankChange
|
||||||
|
batAngleDeg, batSpeed = npcBatAngle(), npcBatChange()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
updateBatting(batAngleDeg, batSpeed)
|
||||||
|
|
||||||
|
-- TODO: Ensure batter can't be nil, here
|
||||||
|
updateRunner(batter, nil, crankChange)
|
||||||
|
|
||||||
if secondsSincePitchAllowed > PitchAfterSeconds then
|
if secondsSincePitchAllowed > PitchAfterSeconds then
|
||||||
if playerOnOffense then
|
if playerOnOffense then
|
||||||
pitch(PitchFlyMs, math.random(#Pitches))
|
pitch(PitchFlyMs, math.random(#Pitches))
|
||||||
else
|
else
|
||||||
local throwFly = readThrow()
|
local throwFly = readThrow()
|
||||||
if throwFly and not buttonControlledThrow(fielders.pitcher, throwFly, true) then
|
if throwFly and not buttonControlledThrow(fielders.pitcher, throwFly, true) then
|
||||||
local aButton = playdate.buttonIsPressed(playdate.kButtonA)
|
playerPitch(throwFly)
|
||||||
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
|
||||||
end
|
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
|
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()
|
local appliedSpeed = playerIsOn(Sides.offense) and crankChange or npcRunningSpeed()
|
||||||
if updateRunning(appliedSpeed) then
|
if updateRunning(appliedSpeed) then
|
||||||
secondsSinceLastRunnerMove = 0
|
secondsSinceLastRunnerMove = 0
|
||||||
|
@ -872,9 +900,9 @@ end
|
||||||
|
|
||||||
function playdate.update()
|
function playdate.update()
|
||||||
playdate.timer.updateTimers()
|
playdate.timer.updateTimers()
|
||||||
|
gfx.animation.blinker.updateAll()
|
||||||
|
|
||||||
updateGameState()
|
updateGameState()
|
||||||
gfx.animation.blinker.updateAll()
|
|
||||||
|
|
||||||
gfx.clear()
|
gfx.clear()
|
||||||
gfx.setColor(gfx.kColorBlack)
|
gfx.setColor(gfx.kColorBlack)
|
||||||
|
|
Loading…
Reference in New Issue