Add perfect-power indicator to throwMeter
Some other tightening-up in there. E.g. clears the lastReadThrow when on a new fielder. Add type annotations to assets files.
This commit is contained in:
parent
35c7754207
commit
56c0c27d75
|
@ -2,66 +2,109 @@
|
||||||
-- Instead, edit the source file directly: assets.lua2p.
|
-- Instead, edit the source file directly: assets.lua2p.
|
||||||
|
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
DarkPlayerBack = playdate.graphics.image.new("images/game/DarkPlayerBack.png")
|
DarkPlayerBack = playdate.graphics.image.new("images/game/DarkPlayerBack.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
Glove = playdate.graphics.image.new("images/game/Glove.png")
|
Glove = playdate.graphics.image.new("images/game/Glove.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
|
PerfectPowerFlickerRight = playdate.graphics.image.new("images/game/PerfectPowerFlickerRight.png")
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
|
DarkSkinFan = playdate.graphics.image.new("images/game/DarkSkinFan.png")
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
PlayerFrown = playdate.graphics.image.new("images/game/PlayerFrown.png")
|
PlayerFrown = playdate.graphics.image.new("images/game/PlayerFrown.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
|
PerfectPowerFlickerLeft = playdate.graphics.image.new("images/game/PerfectPowerFlickerLeft.png")
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
BigBat = playdate.graphics.image.new("images/game/BigBat.png")
|
BigBat = playdate.graphics.image.new("images/game/BigBat.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
|
LightSkinFan = playdate.graphics.image.new("images/game/LightSkinFan.png")
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
GloveHoldingBall = playdate.graphics.image.new("images/game/GloveHoldingBall.png")
|
GloveHoldingBall = playdate.graphics.image.new("images/game/GloveHoldingBall.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
GameLogo = playdate.graphics.image.new("images/game/GameLogo.png")
|
GameLogo = playdate.graphics.image.new("images/game/GameLogo.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
Hat = playdate.graphics.image.new("images/game/Hat.png")
|
Hat = playdate.graphics.image.new("images/game/Hat.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
DarkPlayerBase = playdate.graphics.image.new("images/game/DarkPlayerBase.png")
|
DarkPlayerBase = playdate.graphics.image.new("images/game/DarkPlayerBase.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
MenuImage = playdate.graphics.image.new("images/game/MenuImage.png")
|
MenuImage = playdate.graphics.image.new("images/game/MenuImage.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
PlayerSmile = playdate.graphics.image.new("images/game/PlayerSmile.png")
|
PlayerSmile = playdate.graphics.image.new("images/game/PlayerSmile.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
Minimap = playdate.graphics.image.new("images/game/Minimap.png")
|
Minimap = playdate.graphics.image.new("images/game/Minimap.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
GrassBackground = playdate.graphics.image.new("images/game/GrassBackground.png")
|
GrassBackground = playdate.graphics.image.new("images/game/GrassBackground.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
GrassBackgroundSmall = playdate.graphics.image.new("images/game/GrassBackgroundSmall.png")
|
GrassBackgroundSmall = playdate.graphics.image.new("images/game/GrassBackgroundSmall.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
LightPlayerBase = playdate.graphics.image.new("images/game/LightPlayerBase.png")
|
LightPlayerBase = playdate.graphics.image.new("images/game/LightPlayerBase.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
|
PerfectPowerBg = playdate.graphics.image.new("images/game/PerfectPowerBg.png")
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
LightPlayerBack = playdate.graphics.image.new("images/game/LightPlayerBack.png")
|
LightPlayerBack = playdate.graphics.image.new("images/game/LightPlayerBack.png")
|
||||||
|
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_sampleplayer
|
||||||
BatCrackReverb = playdate.sound.sampleplayer.new("sounds/BatCrackReverb.wav")
|
BatCrackReverb = playdate.sound.sampleplayer.new("sounds/BatCrackReverb.wav")
|
||||||
|
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_sampleplayer
|
||||||
BootTune = playdate.sound.sampleplayer.new("music/BootTune.wav")
|
BootTune = playdate.sound.sampleplayer.new("music/BootTune.wav")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_sampleplayer
|
||||||
BootTuneOrgany = playdate.sound.sampleplayer.new("music/BootTuneOrgany.wav")
|
BootTuneOrgany = playdate.sound.sampleplayer.new("music/BootTuneOrgany.wav")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_sampleplayer
|
||||||
TinnyBackground = playdate.sound.sampleplayer.new("music/TinnyBackground.wav")
|
TinnyBackground = playdate.sound.sampleplayer.new("music/TinnyBackground.wav")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_sampleplayer
|
||||||
MenuMusic = playdate.sound.sampleplayer.new("music/MenuMusic.wav")
|
MenuMusic = playdate.sound.sampleplayer.new("music/MenuMusic.wav")
|
||||||
|
|
||||||
Logos = {
|
Logos = {
|
||||||
{ name = "Base", image = playdate.graphics.image.new("images/game/logos/Base.png") },
|
{ name = "Base", image = playdate.graphics.image.new("images/game/logos/Base.png") },
|
||||||
|
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
{ name = "Cats", image = playdate.graphics.image.new("images/game/logos/Cats.png") },
|
{ name = "Cats", image = playdate.graphics.image.new("images/game/logos/Cats.png") },
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
{ name = "Hearts", image = playdate.graphics.image.new("images/game/logos/Hearts.png") },
|
{ name = "Hearts", image = playdate.graphics.image.new("images/game/logos/Hearts.png") },
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
{ name = "Checkmarks", image = playdate.graphics.image.new("images/game/logos/Checkmarks.png") },
|
{ name = "Checkmarks", image = playdate.graphics.image.new("images/game/logos/Checkmarks.png") },
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
{ name = "Smiles", image = playdate.graphics.image.new("images/game/logos/Smiles.png") },
|
{ name = "Smiles", image = playdate.graphics.image.new("images/game/logos/Smiles.png") },
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
{ name = "FingerGuns", image = playdate.graphics.image.new("images/game/logos/FingerGuns.png") },
|
{ name = "FingerGuns", image = playdate.graphics.image.new("images/game/logos/FingerGuns.png") },
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
{ name = "Frown", image = playdate.graphics.image.new("images/game/logos/Frown.png") },
|
{ name = "Frown", image = playdate.graphics.image.new("images/game/logos/Frown.png") },
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
{ name = "Arrows", image = playdate.graphics.image.new("images/game/logos/Arrows.png") },
|
{ name = "Arrows", image = playdate.graphics.image.new("images/game/logos/Arrows.png") },
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
---@type pd_image
|
||||||
{ name = "Turds", image = playdate.graphics.image.new("images/game/logos/Turds.png") },
|
{ name = "Turds", image = playdate.graphics.image.new("images/game/logos/Turds.png") },
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
!(function dirLookup(dir, extension, newFunc, sep, handle)
|
!(function dirLookup(dir, extension, newFunc, type, sep, handle)
|
||||||
sep = sep or "\n"
|
sep = sep or "\n"
|
||||||
handle = handle ~= nil and handle or function(varName, value)
|
handle = handle ~= nil and handle or function(varName, value)
|
||||||
return varName .. ' = ' .. value
|
return varName .. ' = ' .. value
|
||||||
|
@ -13,6 +13,7 @@
|
||||||
local varName = file:gsub(".*/(.*)." .. extension, "%1")
|
local varName = file:gsub(".*/(.*)." .. extension, "%1")
|
||||||
file = file:gsub("src/", "")
|
file = file:gsub("src/", "")
|
||||||
assetCode = assetCode .. '-- luacheck: ignore\n'
|
assetCode = assetCode .. '-- luacheck: ignore\n'
|
||||||
|
assetCode = assetCode .. '---@type ' .. type ..'\n'
|
||||||
assetCode = assetCode .. handle(varName, newFunc .. '("' .. file .. '")') .. sep
|
assetCode = assetCode .. handle(varName, newFunc .. '("' .. file .. '")') .. sep
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -23,13 +24,13 @@ function generatedFileWarning()
|
||||||
return "-- GENERATED FILE - DO NOT EDIT\n-- Instead, edit the source file directly: assets.lua2p."
|
return "-- GENERATED FILE - DO NOT EDIT\n-- Instead, edit the source file directly: assets.lua2p."
|
||||||
end)!!(generatedFileWarning())
|
end)!!(generatedFileWarning())
|
||||||
|
|
||||||
!!(dirLookup('images/game', 'png', 'playdate.graphics.image.new'))
|
!!(dirLookup('images/game', 'png', 'playdate.graphics.image.new', 'pd_image'))
|
||||||
!!(dirLookup('sounds', 'wav', 'playdate.sound.sampleplayer.new'))
|
!!(dirLookup('sounds', 'wav', 'playdate.sound.sampleplayer.new', 'pd_sampleplayer'))
|
||||||
!!(dirLookup('music', 'wav', 'playdate.sound.sampleplayer.new'))
|
!!(dirLookup('music', 'wav', 'playdate.sound.sampleplayer.new', 'pd_sampleplayer'))
|
||||||
Logos = {
|
Logos = {
|
||||||
{ name = "Base", image = playdate.graphics.image.new("images/game/logos/Base.png") },
|
{ name = "Base", image = playdate.graphics.image.new("images/game/logos/Base.png") },
|
||||||
|
|
||||||
!!(dirLookup('images/game/logos -not -name "Base.png"', 'png', 'playdate.graphics.image.new', ",\n", function(varName, value)
|
!!(dirLookup('images/game/logos -not -name "Base.png"', 'png', 'playdate.graphics.image.new', 'pd_image', ",\n", function(varName, value)
|
||||||
return '{ name = "' .. varName .. '", image = ' .. value .. ' }'
|
return '{ name = "' .. varName .. '", image = ' .. value .. ' }'
|
||||||
end))
|
end))
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,6 @@ end
|
||||||
---@param floaty boolean | nil
|
---@param floaty boolean | nil
|
||||||
---@param customBallScaler pd_animator | nil
|
---@param customBallScaler pd_animator | nil
|
||||||
function Ball:launch(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler)
|
function Ball:launch(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler)
|
||||||
throwMeter:reset()
|
|
||||||
self.heldBy = nil
|
self.heldBy = nil
|
||||||
|
|
||||||
-- Prevent silly insta-catches
|
-- Prevent silly insta-catches
|
||||||
|
|
|
@ -4,20 +4,33 @@ local gfx <const> = playdate.graphics
|
||||||
local ThrowMeterHeight <const> = 50
|
local ThrowMeterHeight <const> = 50
|
||||||
local ThrowMeterLingerSec <const> = 1.5
|
local ThrowMeterLingerSec <const> = 1.5
|
||||||
|
|
||||||
|
local flickerTimer = gfx.animation.blinker.new(50, 50, true)
|
||||||
|
flickerTimer:start()
|
||||||
|
|
||||||
---@param x number
|
---@param x number
|
||||||
---@param y number
|
---@param y number
|
||||||
function throwMeter:draw(x, y)
|
function throwMeter:draw(x, y)
|
||||||
gfx.setLineWidth(1)
|
gfx.setLineWidth(1)
|
||||||
gfx.drawRect(x, y, 14, ThrowMeterHeight)
|
gfx.drawRect(x, y, 14, ThrowMeterHeight)
|
||||||
if self.lastReadThrow then
|
if self.lastReadThrow then
|
||||||
-- TODO: If ratio is "perfect", show some additional effect
|
|
||||||
-- TODO: If meter has moved to a new fielder, empty it.
|
-- TODO: If meter has moved to a new fielder, empty it.
|
||||||
local ratio = (self.lastReadThrow - throwMeter.MinCharge) / (self.idealPower - throwMeter.MinCharge)
|
local ratio = 1
|
||||||
|
if not wasPerfect then
|
||||||
|
ratio = (self.lastReadThrow - throwMeter.MinCharge) / (self.idealPower - throwMeter.MinCharge)
|
||||||
|
end
|
||||||
local height = ThrowMeterHeight * ratio
|
local height = ThrowMeterHeight * ratio
|
||||||
gfx.fillRect(x + 2, y + ThrowMeterHeight - height, 10, height)
|
gfx.fillRect(x + 2, y + ThrowMeterHeight - height, 10, height)
|
||||||
end
|
|
||||||
-- TODO: Dither or bend if the user throws too hard
|
-- TODO: Dither or bend if the user throws too hard
|
||||||
-- Or maybe dither if it's too soft - bend if it's too hard
|
-- Or maybe dither if it's too soft - bend if it's too hard
|
||||||
|
if self.wasPerfect then
|
||||||
|
PerfectPowerBg:draw(x - 11, y - 9)
|
||||||
|
if flickerTimer.on then
|
||||||
|
PerfectPowerFlickerLeft:draw(x - 11, y - 9)
|
||||||
|
else
|
||||||
|
PerfectPowerFlickerRight:draw(x - 11, y - 9)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function throwMeter:drawNearFielder(fielder)
|
function throwMeter:drawNearFielder(fielder)
|
||||||
|
@ -25,6 +38,9 @@ function throwMeter:drawNearFielder(fielder)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if fielder then
|
if fielder then
|
||||||
|
if fielder ~= self.lastThrower then
|
||||||
|
self.lastReadThrow = nil
|
||||||
|
end
|
||||||
self.lastThrower = fielder
|
self.lastThrower = fielder
|
||||||
actionQueue:upsert("throwMeterLinger", 200 + ThrowMeterLingerSec * 1000, function()
|
actionQueue:upsert("throwMeterLinger", 200 + ThrowMeterLingerSec * 1000, function()
|
||||||
local dt = 0
|
local dt = 0
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 654 B |
Binary file not shown.
After Width: | Height: | Size: 611 B |
Binary file not shown.
After Width: | Height: | Size: 614 B |
|
@ -46,7 +46,8 @@ Pitches = {
|
||||||
currentValue = function()
|
currentValue = function()
|
||||||
return getPitchMissBy(accuracy) + C.PitchStart.x + (10 * math.sin((ball.yAnimator:currentValue() - C.PitchStart.y) / 10))
|
return getPitchMissBy(accuracy) + C.PitchStart.x + (10 * math.sin((ball.yAnimator:currentValue() - C.PitchStart.y) / 10))
|
||||||
end,
|
end,
|
||||||
reset = function() end,
|
reset = function()
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
y = gfx.animator.new(C.PitchFlyMs * 1.3, C.PitchStart.y, C.PitchEndY, playdate.easingFunctions.linear),
|
y = gfx.animator.new(C.PitchFlyMs * 1.3, C.PitchStart.y, C.PitchEndY, playdate.easingFunctions.linear),
|
||||||
}
|
}
|
||||||
|
@ -116,54 +117,66 @@ end
|
||||||
throwMeter = {
|
throwMeter = {
|
||||||
MinCharge = 25,
|
MinCharge = 25,
|
||||||
|
|
||||||
value = 0,
|
|
||||||
idealPower = 50,
|
idealPower = 50,
|
||||||
|
|
||||||
|
--- Used at draw-time only.
|
||||||
|
---@type number
|
||||||
lastReadThrow = nil,
|
lastReadThrow = nil,
|
||||||
|
|
||||||
--- Used at draw-time only.
|
--- Used at draw-time only.
|
||||||
---@type Fielder | nil
|
---@type Fielder | nil
|
||||||
lastThrower = nil,
|
lastThrower = nil,
|
||||||
|
|
||||||
|
--- Used at draw-time only.
|
||||||
|
---@type boolean
|
||||||
|
wasPerfect = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
function throwMeter:reset()
|
|
||||||
self.value = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local crankQueue = {}
|
local crankQueue = {}
|
||||||
|
|
||||||
|
local MaxPowerRatio <const> = 1.5
|
||||||
|
|
||||||
--- Returns nil when a throw is NOT requested.
|
--- Returns nil when a throw is NOT requested.
|
||||||
---@param chargeAmount number
|
---@param chargeAmount number
|
||||||
---@return number | nil powerRatio, number | nil accuracy, boolean isPerfect
|
---@return number | nil powerRatio, number | nil accuracy, boolean isPerfect
|
||||||
function throwMeter:readThrow(chargeAmount)
|
function throwMeter:readThrow(chargeAmount)
|
||||||
local ret = self:applyCharge(chargeAmount)
|
local power = self:readCharge(chargeAmount)
|
||||||
if ret then
|
if not power then
|
||||||
local ratio = ret / self.idealPower
|
|
||||||
local accuracy
|
|
||||||
if ratio >= 1 then
|
|
||||||
accuracy = 1 / ratio / 2
|
|
||||||
else
|
|
||||||
accuracy = 1 -- Accuracy is perfect on slow throws
|
|
||||||
end
|
|
||||||
return ratio * 1.5, accuracy, math.abs(ratio - 1) < 0.05
|
|
||||||
end
|
|
||||||
return nil, nil, false
|
return nil, nil, false
|
||||||
|
end
|
||||||
|
|
||||||
|
local ratio = math.min(power / self.idealPower, MaxPowerRatio)
|
||||||
|
self.wasPerfect = math.abs(ratio - 1) < 0.05
|
||||||
|
|
||||||
|
local accuracy = 1
|
||||||
|
-- Only throw off accuracy on slow throws
|
||||||
|
if ratio >= 1 and not self.wasPerfect then
|
||||||
|
accuracy = 1 / ratio
|
||||||
|
end
|
||||||
|
|
||||||
|
return ratio * 1.5, accuracy, self.wasPerfect
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local CrankRecordSec <const> = 0.33
|
||||||
|
|
||||||
--- If (within approx. a third of a second) the crank has moved more than 45 degrees, call that a throw.
|
--- If (within approx. a third of a second) the crank has moved more than 45 degrees, call that a throw.
|
||||||
---@param chargeAmount number
|
---@param chargeAmount number
|
||||||
function throwMeter:applyCharge(chargeAmount)
|
---@return number | nil
|
||||||
|
function throwMeter:readCharge(chargeAmount)
|
||||||
if chargeAmount == 0 then
|
if chargeAmount == 0 then
|
||||||
return
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local currentTimeMs = playdate.getCurrentTimeMilliseconds()
|
local currentTimeMs = playdate.getCurrentTimeMilliseconds()
|
||||||
local removedOne = false
|
local minTimeHasPassed = false
|
||||||
while #crankQueue ~= 0 and (currentTimeMs - crankQueue[1].time) > 0.33 do
|
while #crankQueue ~= 0 and (currentTimeMs - crankQueue[1].time) > CrankRecordSec do
|
||||||
table.remove(crankQueue, 1)
|
table.remove(crankQueue, 1)
|
||||||
removedOne = true -- At least 1/3 second has passed
|
minTimeHasPassed = true
|
||||||
end
|
end
|
||||||
|
|
||||||
crankQueue[#crankQueue + 1] = { time = currentTimeMs, chargeAmount = math.abs(chargeAmount) }
|
crankQueue[#crankQueue + 1] = { time = currentTimeMs, chargeAmount = math.abs(chargeAmount) }
|
||||||
|
|
||||||
if not removedOne then
|
if not minTimeHasPassed then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -174,7 +187,6 @@ function throwMeter:applyCharge(chargeAmount)
|
||||||
|
|
||||||
if currentCharge > throwMeter.MinCharge then
|
if currentCharge > throwMeter.MinCharge then
|
||||||
self.lastReadThrow = currentCharge
|
self.lastReadThrow = currentCharge
|
||||||
print(tostring(currentCharge) .. " from " .. #crankQueue)
|
|
||||||
crankQueue = {}
|
crankQueue = {}
|
||||||
return currentCharge
|
return currentCharge
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue