parent
0b126919ac
commit
4fc37b133e
89
src/ecs.lua
89
src/ecs.lua
|
@ -1,4 +1,4 @@
|
|||
ecs = { }
|
||||
ecs = {}
|
||||
|
||||
local ALL_ENTITIES = {}
|
||||
|
||||
|
@ -12,9 +12,9 @@ function ecs.addEntity(entity)
|
|||
ALL_ENTITIES[entity] = true
|
||||
for _, system in pairs(SYSTEMS) do
|
||||
if entityMatchesShapes(entity, system.shapes) then
|
||||
system.entityCache[entity] = true
|
||||
system.entityCache[entity] = true
|
||||
else
|
||||
system.entityCache[entity] = nil
|
||||
system.entityCache[entity] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -22,7 +22,7 @@ end
|
|||
function ecs.removeEntity(entity)
|
||||
ALL_ENTITIES[entity] = nil
|
||||
for _, system in pairs(SYSTEMS) do
|
||||
system.entityCache[entity] = nil
|
||||
system.entityCache[entity] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -36,7 +36,7 @@ end
|
|||
function allKeysIncluded(entity, filter)
|
||||
for k, _ in pairs(filter) do
|
||||
if not entity[k] then
|
||||
return false
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
|
@ -45,7 +45,7 @@ end
|
|||
function entityMatchesShapes(entity, shapes)
|
||||
for _, shape in pairs(shapes) do
|
||||
if not allKeysIncluded(entity, shape) then
|
||||
return false
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
|
@ -61,25 +61,26 @@ end
|
|||
---@param wShape W?
|
||||
---@return fun(callback: fun(componentT: T, componentU: U, componentV: V, componentW: W))
|
||||
function ecs.entitiesHavingShapes(tShape, uShape, vShape, wShape)
|
||||
return function()
|
||||
end
|
||||
return function() end
|
||||
end
|
||||
|
||||
-- Print contents of `tbl`, with indentation.
|
||||
-- `indent` sets the initial level of indentation.
|
||||
function tprint (tbl, indent)
|
||||
if not indent then indent = 0 end
|
||||
for k, v in pairs(tbl) do
|
||||
formatting = string.rep(" ", indent) .. k .. ": "
|
||||
if type(v) == "table" then
|
||||
print(formatting)
|
||||
tprint(v, indent+1)
|
||||
elseif type(v) == 'boolean' then
|
||||
print(formatting .. tostring(v))
|
||||
else
|
||||
print(formatting .. v)
|
||||
end
|
||||
end
|
||||
function tprint(tbl, indent)
|
||||
if not indent then
|
||||
indent = 0
|
||||
end
|
||||
for k, v in pairs(tbl) do
|
||||
formatting = string.rep(" ", indent) .. k .. ": "
|
||||
if type(v) == "table" then
|
||||
print(formatting)
|
||||
tprint(v, indent + 1)
|
||||
elseif type(v) == "boolean" then
|
||||
print(formatting .. tostring(v))
|
||||
else
|
||||
print(formatting .. v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function addSystem(callback, keys, shapes)
|
||||
|
@ -87,7 +88,7 @@ function addSystem(callback, keys, shapes)
|
|||
callback = callback,
|
||||
keys = keys,
|
||||
shapes = shapes,
|
||||
entityCache = nil
|
||||
entityCache = nil,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -98,18 +99,26 @@ end
|
|||
|
||||
---@param deltaSeconds number
|
||||
function ecs.update(deltaSeconds)
|
||||
for _,system in pairs(SYSTEMS) do
|
||||
for _, system in pairs(SYSTEMS) do
|
||||
if not system.entityCache then
|
||||
system.entityCache = {}
|
||||
for entity,_ in pairs(ALL_ENTITIES) do
|
||||
system.entityCache = {}
|
||||
for entity, _ in pairs(ALL_ENTITIES) do
|
||||
if entityMatchesShapes(entity, system.shapes) then
|
||||
system.entityCache[entity] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
local keys = system.keys
|
||||
for entity,_ in pairs(system.entityCache) do
|
||||
system.callback(deltaSeconds, entity, entity[keys[1]], entity[keys[2]], entity[keys[3]], entity[keys[4]], entity)
|
||||
for entity, _ in pairs(system.entityCache) do
|
||||
system.callback(
|
||||
deltaSeconds,
|
||||
entity,
|
||||
entity[keys[1]],
|
||||
entity[keys[2]],
|
||||
entity[keys[3]],
|
||||
entity[keys[4]],
|
||||
entity
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -130,21 +139,21 @@ end
|
|||
---@param wShape { [WKey]: W } | fun(entity: any, componentT: T, componentU: U, componentV: V, any) | nil
|
||||
---@param finalFunc fun(entity: any, componentT: T, componentU: U, componentV: V, componentW: W, any) | nil
|
||||
function ecs.forEntitiesWith(tShape, uShape, vShape, wShape, finalFunc)
|
||||
local maybeShapes = {tShape, uShape, vShape, wShape, finalFunc}
|
||||
local maybeShapes = { tShape, uShape, vShape, wShape, finalFunc }
|
||||
local shapes = {}
|
||||
local callback
|
||||
|
||||
for _,maybeShape in pairs(maybeShapes) do
|
||||
for _, maybeShape in pairs(maybeShapes) do
|
||||
if type(maybeShape) == "table" then
|
||||
shapes[#shapes + 1] = maybeShape
|
||||
shapes[#shapes + 1] = maybeShape
|
||||
elseif type(maybeShape) == "function" then
|
||||
callback = maybeShape
|
||||
end
|
||||
end
|
||||
|
||||
local keys = {}
|
||||
for _,shape in pairs(shapes) do
|
||||
for key,_ in pairs(shape) do
|
||||
for _, shape in pairs(shapes) do
|
||||
for key, _ in pairs(shape) do
|
||||
keys[#keys + 1] = key
|
||||
end
|
||||
end
|
||||
|
@ -159,15 +168,15 @@ local Target = { target = XYPair }
|
|||
local Velocity = { velocity = XYPair }
|
||||
|
||||
function ecs.overlayOnto(entity, value)
|
||||
for key,v in pairs(value) do
|
||||
for key, v in pairs(value) do
|
||||
entity[key] = v
|
||||
end
|
||||
ecs.addEntity(entity)
|
||||
end
|
||||
|
||||
local data = {
|
||||
position = {x = 0, y = 0},
|
||||
velocity = {x = 1, y = 2},
|
||||
position = { x = 0, y = 0 },
|
||||
velocity = { x = 1, y = 2 },
|
||||
}
|
||||
|
||||
---@generic T
|
||||
|
@ -183,14 +192,14 @@ end
|
|||
---@param shape `T`
|
||||
---@param value `T`
|
||||
function ecs.set(entity, shape, value)
|
||||
for key,v in pairs(shape) do
|
||||
for key, v in pairs(shape) do
|
||||
entity[key] = value[v]
|
||||
end
|
||||
end
|
||||
|
||||
ecs.addEntity(data)
|
||||
|
||||
ecs.forEntitiesWith(Position, Velocity, function (delta, e, pos, vel)
|
||||
ecs.forEntitiesWith(Position, Velocity, function(delta, e, pos, vel)
|
||||
pos.x = pos.x + (delta * vel.x)
|
||||
pos.y = pos.y + (delta * vel.y)
|
||||
print("position")
|
||||
|
@ -200,12 +209,12 @@ ecs.forEntitiesWith(Position, Velocity, function (delta, e, pos, vel)
|
|||
})
|
||||
end)
|
||||
|
||||
ecs.forEntitiesWith(Target, function (delta, e, pos, vel)
|
||||
ecs.forEntitiesWith(Target, function(delta, e, pos, vel)
|
||||
pos.x = pos.x + (delta * vel.x)
|
||||
pos.y = pos.y + (delta * vel.y)
|
||||
print("position")
|
||||
tprint(pos, 1)
|
||||
ecs.set(e, Target, 'hallo')
|
||||
ecs.set(e, Target, "hallo")
|
||||
end)
|
||||
|
||||
ecs.update(1)
|
||||
ecs.update(1)
|
||||
|
|
|
@ -5,18 +5,18 @@ local ballBuffer = 5
|
|||
--- XOX
|
||||
--- Where each character is the size of the screen, and 'O' is the default view.
|
||||
function getDrawOffset(screenW, screenH, ballX, ballY)
|
||||
local offsetX, offsetY
|
||||
if ballY < ballBuffer then
|
||||
offsetY = math.max(ballBuffer, -1 * (ballY - ballBuffer))
|
||||
else
|
||||
offsetY = 0
|
||||
end
|
||||
if ballX > 0 and ballX < (screenW - ballBuffer) then
|
||||
offsetX = 0
|
||||
elseif ballX < ballBuffer then
|
||||
offsetX = math.max(-1 * screenW, -1 * (ballX - ballBuffer))
|
||||
elseif ballX > (screenW - ballBuffer) then
|
||||
offsetX = math.min(screenW * 2, -1 * (ballX - ballBuffer))
|
||||
end
|
||||
return offsetX, offsetY
|
||||
end
|
||||
local offsetX, offsetY
|
||||
if ballY < ballBuffer then
|
||||
offsetY = math.max(ballBuffer, -1 * (ballY - ballBuffer))
|
||||
else
|
||||
offsetY = 0
|
||||
end
|
||||
if ballX > 0 and ballX < (screenW - ballBuffer) then
|
||||
offsetX = 0
|
||||
elseif ballX < ballBuffer then
|
||||
offsetX = math.max(-1 * screenW, -1 * (ballX - ballBuffer))
|
||||
elseif ballX > (screenW - ballBuffer) then
|
||||
offsetX = math.min(screenW * 2, -1 * (ballX - ballBuffer))
|
||||
end
|
||||
return offsetX, offsetY
|
||||
end
|
||||
|
|
456
src/main.lua
456
src/main.lua
|
@ -1,3 +1,4 @@
|
|||
-- stylua: ignore start
|
||||
import 'CoreLibs/animation.lua'
|
||||
import 'CoreLibs/animator.lua'
|
||||
import 'CoreLibs/easing.lua'
|
||||
|
@ -7,6 +8,7 @@ import 'CoreLibs/ui.lua'
|
|||
|
||||
import 'graphics.lua'
|
||||
import 'utils.lua'
|
||||
-- stylua: ignore end
|
||||
|
||||
--- @alias XYPair { x: number, y: number }
|
||||
|
||||
|
@ -20,7 +22,7 @@ local gfx = playdate.graphics
|
|||
|
||||
local SCREEN = {
|
||||
W = playdate.display.getWidth(),
|
||||
H = playdate.display.getHeight()
|
||||
H = playdate.display.getHeight(),
|
||||
}
|
||||
|
||||
local CENTER = xy(SCREEN.W / 2, SCREEN.H / 2)
|
||||
|
@ -29,17 +31,13 @@ local batCrackSound = playdate.sound.sampleplayer.new("sounds/bat-crack-reverb.w
|
|||
local grassBackground = gfx.image.new("images/game/grass.png") --[[@as PlaydateGraphicsImage]]
|
||||
local playerFrown = gfx.image.new("images/game/player-frown.png") --[[@as PlaydateGraphicsImage]]
|
||||
|
||||
local playerImageBlipper = blipper.new(
|
||||
100,
|
||||
"images/game/player.png",
|
||||
"images/game/player-lowhat.png"
|
||||
)
|
||||
local playerImageBlipper = blipper.new(100, "images/game/player.png", "images/game/player-lowhat.png")
|
||||
|
||||
local BALL_OFFSCREEN = 999
|
||||
|
||||
local danceBounceMs = 500
|
||||
local danceBounceCount = 4
|
||||
local fielderDanceAnimator = gfx.animator.new(danceBounceMs, 10, 0, easingHill)--, -1 * danceBounceMs * danceBounceCount)
|
||||
local fielderDanceAnimator = gfx.animator.new(danceBounceMs, 10, 0, easingHill) --, -1 * danceBounceMs * danceBounceCount)
|
||||
fielderDanceAnimator.repeatCount = danceBounceCount - 1
|
||||
|
||||
local pitchFlyTimeMs = 2500
|
||||
|
@ -56,14 +54,14 @@ local TAG_DISTANCE = 20
|
|||
local ball = {
|
||||
x = CENTER.x,
|
||||
y = BALL_OFFSCREEN,
|
||||
size = 6
|
||||
size = 6,
|
||||
}
|
||||
|
||||
local BAT_LENGTH = 45
|
||||
|
||||
local MODES = {
|
||||
batting = {},
|
||||
running = {}
|
||||
batting = {},
|
||||
running = {},
|
||||
}
|
||||
|
||||
local currentMode = MODES.batting
|
||||
|
@ -84,88 +82,88 @@ local FIRST, SECOND, THIRD, HOME = 1, 2, 3, 4
|
|||
|
||||
---@type Base[]
|
||||
local bases = {
|
||||
xy(SCREEN.W * 0.93, SCREEN.H * 0.52),
|
||||
xy(SCREEN.W * 0.47, SCREEN.H * 0.19),
|
||||
xy(SCREEN.W * 0.03, SCREEN.H * 0.52),
|
||||
xy(SCREEN.W * 0.474, SCREEN.H * 0.79)
|
||||
xy(SCREEN.W * 0.93, SCREEN.H * 0.52),
|
||||
xy(SCREEN.W * 0.47, SCREEN.H * 0.19),
|
||||
xy(SCREEN.W * 0.03, SCREEN.H * 0.52),
|
||||
xy(SCREEN.W * 0.474, SCREEN.H * 0.79),
|
||||
}
|
||||
|
||||
---@type table<Base, Base>
|
||||
local nextBaseMap = {
|
||||
[bases[FIRST]] = bases[SECOND],
|
||||
[bases[SECOND]] = bases[THIRD],
|
||||
[bases[THIRD]] = bases[HOME]
|
||||
[bases[FIRST]] = bases[SECOND],
|
||||
[bases[SECOND]] = bases[THIRD],
|
||||
[bases[THIRD]] = bases[HOME],
|
||||
}
|
||||
|
||||
function newFielder(speed)
|
||||
return {
|
||||
speed = speed
|
||||
speed = speed,
|
||||
}
|
||||
end
|
||||
|
||||
---@type table<string, Fielder>
|
||||
local fielders = {
|
||||
first = newFielder(40),
|
||||
second = newFielder(40),
|
||||
shortstop = newFielder(40),
|
||||
third = newFielder(40),
|
||||
pitcher = newFielder(30),
|
||||
catcher = newFielder(20),
|
||||
left = newFielder(40),
|
||||
center = newFielder(40),
|
||||
right = newFielder(40)
|
||||
first = newFielder(40),
|
||||
second = newFielder(40),
|
||||
shortstop = newFielder(40),
|
||||
third = newFielder(40),
|
||||
pitcher = newFielder(30),
|
||||
catcher = newFielder(20),
|
||||
left = newFielder(40),
|
||||
center = newFielder(40),
|
||||
right = newFielder(40),
|
||||
}
|
||||
|
||||
local PITCHER_POS = {
|
||||
x = SCREEN.W * 0.48,
|
||||
y = SCREEN.H * 0.40
|
||||
y = SCREEN.H * 0.40,
|
||||
}
|
||||
|
||||
--- Resets the target positions of all fielders to their defaults (at their field positions).
|
||||
---@param fromOffTheField boolean If provided, also sets all runners' current position to one centralized location.
|
||||
function resetFielderPositions(fromOffTheField)
|
||||
if fromOffTheField then
|
||||
for _,fielder in pairs(fielders) do
|
||||
for _, fielder in pairs(fielders) do
|
||||
fielder.x = CENTER.x
|
||||
fielder.y = SCREEN.H
|
||||
end
|
||||
end
|
||||
|
||||
fielders.first.target = xy(SCREEN.W - 65, SCREEN.H * 0.48)
|
||||
fielders.second.target = xy(SCREEN.W * 0.70, SCREEN.H * 0.30)
|
||||
fielders.shortstop.target = xy(SCREEN.W * 0.30, SCREEN.H * 0.30)
|
||||
fielders.third.target = xy(SCREEN.W * 0.1, SCREEN.H * 0.48)
|
||||
fielders.pitcher.target = xy(PITCHER_POS.x, PITCHER_POS.y)
|
||||
fielders.catcher.target = xy(SCREEN.W * 0.475, SCREEN.H * 0.92)
|
||||
fielders.left.target = xy(SCREEN.W * -1, SCREEN.H * -0.2)
|
||||
fielders.center.target = xy(CENTER.x, SCREEN.H * -0.4)
|
||||
fielders.right.target = xy(SCREEN.W * 2, SCREEN.H * fielders.left.target.y)
|
||||
fielders.first.target = xy(SCREEN.W - 65, SCREEN.H * 0.48)
|
||||
fielders.second.target = xy(SCREEN.W * 0.70, SCREEN.H * 0.30)
|
||||
fielders.shortstop.target = xy(SCREEN.W * 0.30, SCREEN.H * 0.30)
|
||||
fielders.third.target = xy(SCREEN.W * 0.1, SCREEN.H * 0.48)
|
||||
fielders.pitcher.target = xy(PITCHER_POS.x, PITCHER_POS.y)
|
||||
fielders.catcher.target = xy(SCREEN.W * 0.475, SCREEN.H * 0.92)
|
||||
fielders.left.target = xy(SCREEN.W * -1, SCREEN.H * -0.2)
|
||||
fielders.center.target = xy(CENTER.x, SCREEN.H * -0.4)
|
||||
fielders.right.target = xy(SCREEN.W * 2, SCREEN.H * fielders.left.target.y)
|
||||
end
|
||||
|
||||
local PLAYER_STARTING_X = bases[HOME].x - 40
|
||||
local PLAYER_STARTING_Y = bases[HOME].y - 3
|
||||
|
||||
--- @type Runner[]
|
||||
local runners = { }
|
||||
local runners = {}
|
||||
|
||||
--- @type Runner[]
|
||||
local outRunners = { }
|
||||
local outRunners = {}
|
||||
|
||||
local nameI = 1
|
||||
local runnerNames = {"Barbara", "Steve", "Jerry", "Beatrice"}
|
||||
local runnerNames = { "Barbara", "Steve", "Jerry", "Beatrice" }
|
||||
|
||||
---@return Runner
|
||||
function newRunner()
|
||||
local new = {
|
||||
x = PLAYER_STARTING_X,
|
||||
y = PLAYER_STARTING_Y,
|
||||
nextBase = nil,
|
||||
prevBase = nil,
|
||||
name = runnerNames[nameI]
|
||||
}
|
||||
nameI = nameI + 1
|
||||
runners[#runners + 1] = new
|
||||
return new
|
||||
local new = {
|
||||
x = PLAYER_STARTING_X,
|
||||
y = PLAYER_STARTING_Y,
|
||||
nextBase = nil,
|
||||
prevBase = nil,
|
||||
name = runnerNames[nameI],
|
||||
}
|
||||
nameI = nameI + 1
|
||||
runners[#runners + 1] = new
|
||||
return new
|
||||
end
|
||||
|
||||
---@type Runner | nil
|
||||
|
@ -174,45 +172,45 @@ batter.nextBase = bases[FIRST]
|
|||
batter.forcedTo = bases[FIRST]
|
||||
|
||||
function throwBall(destX, destY, easingFunc, flyTimeMs, floaty)
|
||||
if not flyTimeMs then
|
||||
flyTimeMs = distanceBetween(ball.x, ball.y, destX, destY) * 5
|
||||
end
|
||||
ballSizeAnimator:reset(flyTimeMs)
|
||||
ballAnimatorY = gfx.animator.new(flyTimeMs, ball.y, destY, easingFunc)
|
||||
ballAnimatorX = gfx.animator.new(flyTimeMs, ball.x, destX, easingFunc)
|
||||
if floaty then
|
||||
ballFloatAnimator:reset(flyTimeMs)
|
||||
end
|
||||
if not flyTimeMs then
|
||||
flyTimeMs = distanceBetween(ball.x, ball.y, destX, destY) * 5
|
||||
end
|
||||
ballSizeAnimator:reset(flyTimeMs)
|
||||
ballAnimatorY = gfx.animator.new(flyTimeMs, ball.y, destY, easingFunc)
|
||||
ballAnimatorX = gfx.animator.new(flyTimeMs, ball.x, destX, easingFunc)
|
||||
if floaty then
|
||||
ballFloatAnimator:reset(flyTimeMs)
|
||||
end
|
||||
end
|
||||
|
||||
function pitch()
|
||||
currentMode = MODES.batting
|
||||
currentMode = MODES.batting
|
||||
ballAnimatorX = gfx.animator.new(0, PITCH_START_X, PITCH_START_X, playdate.easingFunctions.linear)
|
||||
ballAnimatorY = pitchAnimator
|
||||
pitchAnimator:reset()
|
||||
ballAnimatorY = pitchAnimator
|
||||
pitchAnimator:reset()
|
||||
end
|
||||
|
||||
function playdate.AButtonDown()
|
||||
if not batter then
|
||||
return
|
||||
end
|
||||
pitch()
|
||||
pitch()
|
||||
end
|
||||
|
||||
function playdate.upButtonDown()
|
||||
batBase.y = batBase.y - 1
|
||||
batBase.y = batBase.y - 1
|
||||
end
|
||||
|
||||
function playdate.downButtonDown()
|
||||
batBase.y = batBase.y + 1
|
||||
batBase.y = batBase.y + 1
|
||||
end
|
||||
|
||||
function playdate.rightButtonDown()
|
||||
batBase.x = batBase.x + 1
|
||||
batBase.x = batBase.x + 1
|
||||
end
|
||||
|
||||
function playdate.leftButtonDown()
|
||||
batBase.x = batBase.x - 1
|
||||
batBase.x = batBase.x - 1
|
||||
end
|
||||
|
||||
local elapsedSec = 0
|
||||
|
@ -221,23 +219,22 @@ local crankChange = 0
|
|||
local BASE_HITBOX = 13
|
||||
--- Returns the base being touched by the runner at (x,y), or nil, if no base is being touched
|
||||
function isTouchingBase(x, y)
|
||||
for _,base in ipairs(bases) do
|
||||
if distanceBetween(x, y, base.x, base.y) < BASE_HITBOX then
|
||||
return base
|
||||
end
|
||||
end
|
||||
for _, base in ipairs(bases) do
|
||||
if distanceBetween(x, y, base.x, base.y) < BASE_HITBOX then
|
||||
return base
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
return nil
|
||||
end
|
||||
|
||||
local BALL_CATCH_HITBOX = 3
|
||||
function isTouchingBall(x, y)
|
||||
local ballDistance = distanceBetween(x, y, ball.x, ball.y)
|
||||
return ballDistance < BALL_CATCH_HITBOX
|
||||
local ballDistance = distanceBetween(x, y, ball.x, ball.y)
|
||||
return ballDistance < BALL_CATCH_HITBOX
|
||||
end
|
||||
|
||||
function updateForcedTos()
|
||||
end
|
||||
function updateForcedTos() end
|
||||
|
||||
local outs = 0
|
||||
local homeScore = 0
|
||||
|
@ -246,106 +243,104 @@ local awayScore = 0
|
|||
function outRunner(runnerIndex)
|
||||
print("You're out, runner" .. runnerIndex .. "!")
|
||||
outs = math.min(3, outs + 1)
|
||||
outRunners[#outRunners+1] = runners[runnerIndex]
|
||||
table.remove(runners, runnerIndex)
|
||||
updateForcedTos()
|
||||
fielderDanceAnimator:reset()
|
||||
outRunners[#outRunners + 1] = runners[runnerIndex]
|
||||
table.remove(runners, runnerIndex)
|
||||
updateForcedTos()
|
||||
fielderDanceAnimator:reset()
|
||||
end
|
||||
|
||||
function updateFielders()
|
||||
local touchingBaseCache = buildCache(
|
||||
function(runner)
|
||||
return isTouchingBase(runner.x, runner.y)
|
||||
end
|
||||
)
|
||||
local touchingBaseCache = buildCache(function(runner)
|
||||
return isTouchingBase(runner.x, runner.y)
|
||||
end)
|
||||
|
||||
for _,fielder in pairs(fielders) do
|
||||
-- TODO: Target unforced runners (or their target bases) for tagging
|
||||
-- With new Position-based scheme, fielders are now able to set `fielder.target = runner` to track directly
|
||||
if fielder.target ~= nil then
|
||||
local x, y, distance = normalizeVector(fielder.x, fielder.y, fielder.target.x, fielder.target.y)
|
||||
for _, fielder in pairs(fielders) do
|
||||
-- TODO: Target unforced runners (or their target bases) for tagging
|
||||
-- With new Position-based scheme, fielders are now able to set `fielder.target = runner` to track directly
|
||||
if fielder.target ~= nil then
|
||||
local x, y, distance = normalizeVector(fielder.x, fielder.y, fielder.target.x, fielder.target.y)
|
||||
|
||||
if distance > 1 then
|
||||
fielder.x = fielder.x - (x * fielder.speed * deltaSeconds)
|
||||
fielder.y = fielder.y - (y * fielder.speed * deltaSeconds)
|
||||
else
|
||||
if fielder.onArrive then
|
||||
fielder.onArrive()
|
||||
end
|
||||
fielder.target = nil
|
||||
end
|
||||
end
|
||||
if distance > 1 then
|
||||
fielder.x = fielder.x - (x * fielder.speed * deltaSeconds)
|
||||
fielder.y = fielder.y - (y * fielder.speed * deltaSeconds)
|
||||
else
|
||||
if fielder.onArrive then
|
||||
fielder.onArrive()
|
||||
end
|
||||
fielder.target = nil
|
||||
end
|
||||
end
|
||||
|
||||
if currentMode == MODES.running and isTouchingBall(fielder.x, fielder.y) then
|
||||
local touchedBase = isTouchingBase(fielder.x, fielder.y)
|
||||
for i,runner in pairs(runners) do
|
||||
local runnerOnBase = touchingBaseCache.get(runner)
|
||||
if touchedBase and runner.forcedTo == touchedBase and touchedBase ~= runnerOnBase then
|
||||
if currentMode == MODES.running and isTouchingBall(fielder.x, fielder.y) then
|
||||
local touchedBase = isTouchingBase(fielder.x, fielder.y)
|
||||
for i, runner in pairs(runners) do
|
||||
local runnerOnBase = touchingBaseCache.get(runner)
|
||||
if touchedBase and runner.forcedTo == touchedBase and touchedBase ~= runnerOnBase then
|
||||
print("Force out of runner:")
|
||||
printTable(runner)
|
||||
outRunner(i)
|
||||
elseif not runnerOnBase then
|
||||
local fielderDistance = distanceBetween(runner.x, runner.y, fielder.x, fielder.y)
|
||||
if fielderDistance < TAG_DISTANCE then
|
||||
outRunner(i)
|
||||
elseif not runnerOnBase then
|
||||
local fielderDistance = distanceBetween(runner.x, runner.y, fielder.x, fielder.y)
|
||||
if fielderDistance < TAG_DISTANCE then
|
||||
print("Tagged out!")
|
||||
outRunner(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
outRunner(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns true if at least one runner is still moving
|
||||
---@return boolean
|
||||
function updateRunners()
|
||||
local autoRunSpeed = 20
|
||||
--autoRunSpeed = 140
|
||||
local nonPlayerRunners = filter(runners, function (runner)
|
||||
return runner ~= batter
|
||||
end)
|
||||
local autoRunSpeed = 20
|
||||
--autoRunSpeed = 140
|
||||
local nonPlayerRunners = filter(runners, function(runner)
|
||||
return runner ~= batter
|
||||
end)
|
||||
|
||||
local runnerMoved = false
|
||||
for _,runner in pairs(nonPlayerRunners) do
|
||||
local _, nearestBaseDistance = getNearestOf(bases, runner.x, runner.y)
|
||||
if runner.nextBase then
|
||||
local nb = runner.nextBase
|
||||
local x, y, distance = normalizeVector(runner.x, runner.y, nb.x, nb.y)
|
||||
local runnerMoved = false
|
||||
for _, runner in pairs(nonPlayerRunners) do
|
||||
local _, nearestBaseDistance = getNearestOf(bases, runner.x, runner.y)
|
||||
if runner.nextBase then
|
||||
local nb = runner.nextBase
|
||||
local x, y, distance = normalizeVector(runner.x, runner.y, nb.x, nb.y)
|
||||
|
||||
if distance > 1 then
|
||||
local mult = 1
|
||||
if crankChange < 0 then
|
||||
mult = -1
|
||||
end
|
||||
local prevX, prevY = runner.x, runner.y
|
||||
-- TODO: Drift toward nearest base?
|
||||
local autoRun = nearestBaseDistance > 5 and mult * autoRunSpeed * deltaSeconds or 0
|
||||
mult = autoRun + (crankChange / 20)
|
||||
runner.x = runner.x - (x * mult)
|
||||
if distance > 1 then
|
||||
local mult = 1
|
||||
if crankChange < 0 then
|
||||
mult = -1
|
||||
end
|
||||
local prevX, prevY = runner.x, runner.y
|
||||
-- TODO: Drift toward nearest base?
|
||||
local autoRun = nearestBaseDistance > 5 and mult * autoRunSpeed * deltaSeconds or 0
|
||||
mult = autoRun + (crankChange / 20)
|
||||
runner.x = runner.x - (x * mult)
|
||||
runner.y = runner.y - (y * mult)
|
||||
runnerMoved = runnerMoved or prevX ~= runner.x or prevY ~= runner.y
|
||||
else
|
||||
runner.nextBase = nextBaseMap[runner.nextBase]
|
||||
runner.forcedTo = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
runner.nextBase = nextBaseMap[runner.nextBase]
|
||||
runner.forcedTo = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return runnerMoved
|
||||
return runnerMoved
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
function ballIsBeingThrown()
|
||||
return false
|
||||
return false
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
function throwArrivedBeforeRunner()
|
||||
return false
|
||||
return false
|
||||
end
|
||||
|
||||
function getRunnerTargeting(base)
|
||||
for _,runner in pairs(runners) do
|
||||
for _, runner in pairs(runners) do
|
||||
if runner.nextBase == base then
|
||||
return runner
|
||||
end
|
||||
|
@ -356,44 +351,44 @@ end
|
|||
---@return Base[]
|
||||
function getForcedOutTargets()
|
||||
local targets = {}
|
||||
for _,base in ipairs(bases) do
|
||||
for _, base in ipairs(bases) do
|
||||
local runnerTargetingBase = getRunnerTargeting(base)
|
||||
if runnerTargetingBase then
|
||||
targets[#targets+1] = base
|
||||
targets[#targets + 1] = base
|
||||
else
|
||||
return targets
|
||||
end
|
||||
end
|
||||
return targets
|
||||
-- return { bases[FIRST] }
|
||||
-- return { bases[FIRST] }
|
||||
end
|
||||
|
||||
--- Returns the position,distance of the basest closest to the runner furthest from a base
|
||||
---@return { x: number, y: number } | nil, number | nil
|
||||
function getBaseOfStrandedRunner()
|
||||
local farRunnersBase, farDistance
|
||||
for _,runner in pairs(runners) do
|
||||
local base, distance = getNearestOf(bases, runner.x, runner.y, function(base)
|
||||
for _, runner in pairs(runners) do
|
||||
local base, distance = getNearestOf(bases, runner.x, runner.y, function(base)
|
||||
return runner.nextBase == base
|
||||
end)
|
||||
end)
|
||||
if farRunnersBase == nil then
|
||||
farRunnersBase = base
|
||||
farDistance = distance
|
||||
farRunnersBase = base
|
||||
farDistance = distance
|
||||
elseif farDistance < distance then
|
||||
farRunnersBase = base
|
||||
farDistance = distance
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return farRunnersBase, farDistance
|
||||
return farRunnersBase, farDistance
|
||||
end
|
||||
|
||||
--- Returns x,y of the throw target
|
||||
---@return number|nil, number|nil
|
||||
function getNextThrowTarget()
|
||||
-- TODO: Handle missed throws, check for fielders at target, etc.
|
||||
local targets = getForcedOutTargets()
|
||||
if #targets ~= 0 then
|
||||
local targets = getForcedOutTargets()
|
||||
if #targets ~= 0 then
|
||||
return targets[1].x, targets[1].y
|
||||
end
|
||||
|
||||
|
@ -427,42 +422,44 @@ function updateBatting()
|
|||
ball.size = 6
|
||||
end
|
||||
|
||||
local batAngle = math.rad(playdate.getCrankPosition() + CRANK_OFFSET_DEG)
|
||||
batTip.x = batBase.x + (BAT_LENGTH * math.sin(batAngle))
|
||||
batTip.y = batBase.y + (BAT_LENGTH * math.cos(batAngle))
|
||||
local batAngle = math.rad(playdate.getCrankPosition() + CRANK_OFFSET_DEG)
|
||||
batTip.x = batBase.x + (BAT_LENGTH * math.sin(batAngle))
|
||||
batTip.y = batBase.y + (BAT_LENGTH * math.cos(batAngle))
|
||||
|
||||
if acceleratedChange >= 0 and
|
||||
pointDirectlyUnderLine(ball.x, ball.y, batBase.x, batBase.y, batTip.x, batTip.y, SCREEN.H) then
|
||||
batCrackSound:play()
|
||||
currentMode = MODES.running
|
||||
ballAngle = batAngle + math.rad(90)
|
||||
if
|
||||
acceleratedChange >= 0
|
||||
and pointDirectlyUnderLine(ball.x, ball.y, batBase.x, batBase.y, batTip.x, batTip.y, SCREEN.H)
|
||||
then
|
||||
batCrackSound:play()
|
||||
currentMode = MODES.running
|
||||
ballAngle = batAngle + math.rad(90)
|
||||
|
||||
local mult = math.abs(acceleratedChange / 15)
|
||||
local ballVelX = mult * 10 * math.sin(ballAngle)
|
||||
local ballVelY = mult * 5 * math.cos(ballAngle)
|
||||
if ballVelY > 0 then
|
||||
ballVelX = ballVelX * -1
|
||||
ballVelY = ballVelY * -1
|
||||
end
|
||||
ballDestX = ball.x + (ballVelX * HIT_MULT)
|
||||
ballDestY = ball.y + (ballVelY * HIT_MULT)
|
||||
throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000)
|
||||
local mult = math.abs(acceleratedChange / 15)
|
||||
local ballVelX = mult * 10 * math.sin(ballAngle)
|
||||
local ballVelY = mult * 5 * math.cos(ballAngle)
|
||||
if ballVelY > 0 then
|
||||
ballVelX = ballVelX * -1
|
||||
ballVelY = ballVelY * -1
|
||||
end
|
||||
ballDestX = ball.x + (ballVelX * HIT_MULT)
|
||||
ballDestY = ball.y + (ballVelY * HIT_MULT)
|
||||
throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000)
|
||||
|
||||
fielders.first.target = bases[FIRST]
|
||||
batter.nextBase = bases[FIRST]
|
||||
batter.forcedTo = bases[FIRST]
|
||||
batter = nil -- Demote batter to a mere runner
|
||||
fielders.first.target = bases[FIRST]
|
||||
batter.nextBase = bases[FIRST]
|
||||
batter.forcedTo = bases[FIRST]
|
||||
batter = nil -- Demote batter to a mere runner
|
||||
|
||||
local chasingFielder = getNearestOf(fielders, ballDestX, ballDestY)
|
||||
chasingFielder.target = { x = ballDestX, y = ballDestY }
|
||||
chasingFielder.onArrive = function()
|
||||
local targetX, targetY = getNextThrowTarget()
|
||||
if targetX ~= nil then
|
||||
local chasingFielder = getNearestOf(fielders, ballDestX, ballDestY)
|
||||
chasingFielder.target = { x = ballDestX, y = ballDestY }
|
||||
chasingFielder.onArrive = function()
|
||||
local targetX, targetY = getNextThrowTarget()
|
||||
if targetX ~= nil then
|
||||
throwBall(targetX, targetY, playdate.easingFunctions.linear, nil, true)
|
||||
chasingFielder.onArrive = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function updateRunning()
|
||||
|
@ -495,19 +492,19 @@ function updateOutRunners()
|
|||
end
|
||||
|
||||
function updateGameState()
|
||||
deltaSeconds = playdate.getElapsedTime() or 0
|
||||
playdate.resetElapsedTime()
|
||||
elapsedSec = elapsedSec + deltaSeconds
|
||||
crankChange, acceleratedChange = playdate.getCrankChange() --[[@as number, number]]
|
||||
deltaSeconds = playdate.getElapsedTime() or 0
|
||||
playdate.resetElapsedTime()
|
||||
elapsedSec = elapsedSec + deltaSeconds
|
||||
crankChange, acceleratedChange = playdate.getCrankChange() --[[@as number, number]]
|
||||
|
||||
ball.x = ballAnimatorX:currentValue()
|
||||
ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue()
|
||||
|
||||
if currentMode == MODES.batting then
|
||||
if currentMode == MODES.batting then
|
||||
updateBatting()
|
||||
elseif currentMode == MODES.running then
|
||||
elseif currentMode == MODES.running then
|
||||
updateRunning()
|
||||
end
|
||||
end
|
||||
|
||||
updateFielders()
|
||||
updateOutRunners()
|
||||
|
@ -520,15 +517,15 @@ function drawScoreboard()
|
|||
local y = SCREEN.H * 0.95
|
||||
local x = SCREEN.W * 0.05
|
||||
|
||||
gfx.setLineWidth(1)
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
gfx.setLineWidth(1)
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
gfx.fillRect(x - 15, y - 44, 73, 55)
|
||||
|
||||
gfx.setColor(gfx.kColorWhite)
|
||||
for i=outs,2 do
|
||||
gfx.setColor(gfx.kColorWhite)
|
||||
for i = outs, 2 do
|
||||
gfx.drawCircleAtPoint(x + (i * 2.5 * OUT_BUBBLE_SIZE), y, OUT_BUBBLE_SIZE)
|
||||
end
|
||||
for i=0,(outs - 1) do
|
||||
for i = 0, (outs - 1) do
|
||||
gfx.fillCircleAtPoint(x + (i * 2.5 * OUT_BUBBLE_SIZE), y, OUT_BUBBLE_SIZE)
|
||||
end
|
||||
|
||||
|
@ -540,51 +537,48 @@ function drawScoreboard()
|
|||
end
|
||||
|
||||
function playdate.update()
|
||||
updateGameState()
|
||||
playdate.graphics.animation.blinker.updateAll()
|
||||
updateGameState()
|
||||
playdate.graphics.animation.blinker.updateAll()
|
||||
|
||||
gfx.clear()
|
||||
gfx.clear()
|
||||
|
||||
if ball.x < BALL_OFFSCREEN then
|
||||
-- TODO: Show baserunning minimap when panning?
|
||||
local offsetX, offsetY = getDrawOffset(SCREEN.W, SCREEN.H, ball.x, ball.y)
|
||||
gfx.setDrawOffset(offsetX, offsetY)
|
||||
end
|
||||
end
|
||||
|
||||
grassBackground:draw(-400, -240)
|
||||
grassBackground:draw(-400, -240)
|
||||
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
gfx.setLineWidth(2)
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
gfx.setLineWidth(2)
|
||||
|
||||
gfx.drawCircleAtPoint(ball.x, ball.y, ball.size)
|
||||
gfx.drawCircleAtPoint(ball.x, ball.y, ball.size)
|
||||
|
||||
local fielderDanceHeight = fielderDanceAnimator:currentValue()
|
||||
for _,fielder in pairs(fielders) do
|
||||
gfx.fillRect(fielder.x, fielder.y - fielderDanceHeight, 14, 25)
|
||||
end
|
||||
local fielderDanceHeight = fielderDanceAnimator:currentValue()
|
||||
for _, fielder in pairs(fielders) do
|
||||
gfx.fillRect(fielder.x, fielder.y - fielderDanceHeight, 14, 25)
|
||||
end
|
||||
|
||||
if currentMode == MODES.batting then
|
||||
if currentMode == MODES.batting then
|
||||
gfx.setLineWidth(5)
|
||||
gfx.drawLine(
|
||||
batBase.x, batBase.y,
|
||||
batTip.x, batTip.y
|
||||
)
|
||||
end
|
||||
gfx.drawLine(batBase.x, batBase.y, batTip.x, batTip.y)
|
||||
end
|
||||
|
||||
if playdate.isCrankDocked() then -- or (crankChange < 2 and currentMode == MODES.running) then
|
||||
playdate.ui.crankIndicator:draw()
|
||||
end
|
||||
if playdate.isCrankDocked() then -- or (crankChange < 2 and currentMode == MODES.running) then
|
||||
playdate.ui.crankIndicator:draw()
|
||||
end
|
||||
|
||||
-- TODO? Change blip speed depending on runner speed?
|
||||
for _,runner in pairs(runners) do
|
||||
-- TODO? Change blip speed depending on runner speed?
|
||||
for _, runner in pairs(runners) do
|
||||
-- TODO? Scale sprites down as y increases
|
||||
playerImageBlipper:draw(false, runner.x, runner.y)
|
||||
end
|
||||
for _,runner in pairs(outRunners) do
|
||||
playerImageBlipper:draw(false, runner.x, runner.y)
|
||||
end
|
||||
for _, runner in pairs(outRunners) do
|
||||
playerFrown:draw(runner.x, runner.y)
|
||||
end
|
||||
end
|
||||
|
||||
drawScoreboard()
|
||||
drawScoreboard()
|
||||
end
|
||||
|
||||
init()
|
||||
init()
|
||||
|
|
124
src/utils.lua
124
src/utils.lua
|
@ -1,5 +1,7 @@
|
|||
-- stylua: ignore start
|
||||
import 'CoreLibs/animation.lua'
|
||||
import 'CoreLibs/graphics.lua'
|
||||
-- stylua: ignore end
|
||||
|
||||
-- number = ((number * 2) - 1)
|
||||
-- return number * number
|
||||
|
@ -8,28 +10,28 @@ import 'CoreLibs/graphics.lua'
|
|||
-- return c * t / d + b
|
||||
|
||||
function easingHill(t, b, c, d)
|
||||
c = c + 0.0 -- convert to float to prevent integer overflow
|
||||
t = t / d
|
||||
t = ((t * 2) - 1)
|
||||
t = t * t
|
||||
return (c * t) + b
|
||||
c = c + 0.0 -- convert to float to prevent integer overflow
|
||||
t = t / d
|
||||
t = ((t * 2) - 1)
|
||||
t = t * t
|
||||
return (c * t) + b
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@return XYPair
|
||||
function xy(x, y)
|
||||
return {
|
||||
x = x,
|
||||
y = y
|
||||
}
|
||||
return {
|
||||
x = x,
|
||||
y = y,
|
||||
}
|
||||
end
|
||||
|
||||
--- Returns the normalized vector as two values, plus the distance between the given points.
|
||||
---@return number, number, number
|
||||
function normalizeVector(x1, y1, x2, y2)
|
||||
local distance, a, b = distanceBetween(x1, y1, x2, y2)
|
||||
return a / distance, b / distance, distance
|
||||
local distance, a, b = distanceBetween(x1, y1, x2, y2)
|
||||
return a / distance, b / distance, distance
|
||||
end
|
||||
|
||||
---@generic T
|
||||
|
@ -37,13 +39,13 @@ end
|
|||
---@param condition fun(T): boolean
|
||||
---@return T[]
|
||||
function filter(array, condition)
|
||||
local newArray = {}
|
||||
for _,element in pairs(array) do
|
||||
if condition(element) then
|
||||
newArray[#newArray + 1] = element
|
||||
end
|
||||
end
|
||||
return newArray
|
||||
local newArray = {}
|
||||
for _, element in pairs(array) do
|
||||
if condition(element) then
|
||||
newArray[#newArray + 1] = element
|
||||
end
|
||||
end
|
||||
return newArray
|
||||
end
|
||||
|
||||
---@generic TIn
|
||||
|
@ -52,39 +54,39 @@ end
|
|||
---@param mapper fun(TIn): TOut
|
||||
---@return TOut[]
|
||||
function map(array, mapper)
|
||||
local newArray = {}
|
||||
for _,element in pairs(array) do
|
||||
newArray[#newArray + 1] = mapper(element)
|
||||
end
|
||||
return newArray
|
||||
local newArray = {}
|
||||
for _, element in pairs(array) do
|
||||
newArray[#newArray + 1] = mapper(element)
|
||||
end
|
||||
return newArray
|
||||
end
|
||||
|
||||
---@return number, number, number
|
||||
function distanceBetween(x1, y1, x2, y2)
|
||||
local a = x1 - x2
|
||||
local b = y1 - y2
|
||||
return math.sqrt((a*a) + (b*b)), a, b
|
||||
local a = x1 - x2
|
||||
local b = y1 - y2
|
||||
return math.sqrt((a * a) + (b * b)), a, b
|
||||
end
|
||||
|
||||
--- Returns true only if the point is below the given line, within the x bounds of said line, and above the bottomBound
|
||||
--- @return boolean
|
||||
function pointDirectlyUnderLine(pointX, pointY, lineX1, lineY1, lineX2, lineY2, bottomBound)
|
||||
-- This check currently assumes right-handedness.
|
||||
-- I.e. it assumes the ball is to the right of batBaseX
|
||||
if pointX < lineX1 or pointX > lineX2 or pointY > bottomBound then
|
||||
return false
|
||||
end
|
||||
-- This check currently assumes right-handedness.
|
||||
-- I.e. it assumes the ball is to the right of batBaseX
|
||||
if pointX < lineX1 or pointX > lineX2 or pointY > bottomBound then
|
||||
return false
|
||||
end
|
||||
|
||||
local m = (lineY2 - lineY1) / (lineX2 - lineX1)
|
||||
local m = (lineY2 - lineY1) / (lineX2 - lineX1)
|
||||
|
||||
-- y = mx + b
|
||||
-- b = y1 - (m * x1)
|
||||
local b = lineY1 - (m * lineX1)
|
||||
local yOnLine = (m * pointX) + b
|
||||
local yP = pointY
|
||||
local yDelta = yOnLine - yP
|
||||
-- y = mx + b
|
||||
-- b = y1 - (m * x1)
|
||||
local b = lineY1 - (m * lineX1)
|
||||
local yOnLine = (m * pointX) + b
|
||||
local yP = pointY
|
||||
local yDelta = yOnLine - yP
|
||||
|
||||
return yDelta <= 0
|
||||
return yDelta <= 0
|
||||
end
|
||||
|
||||
--- Returns the nearest position object from the given point, as well as its distance from that point
|
||||
|
@ -94,9 +96,9 @@ end
|
|||
---@param y number
|
||||
---@return T,number|nil
|
||||
function getNearestOf(array, x, y, extraCondition)
|
||||
local nearest, nearestDistance = nil, nil
|
||||
for _, element in pairs(array) do
|
||||
if not extraCondition or extraCondition(element) then
|
||||
local nearest, nearestDistance = nil, nil
|
||||
for _, element in pairs(array) do
|
||||
if not extraCondition or extraCondition(element) then
|
||||
if nearest == nil then
|
||||
nearest = element
|
||||
nearestDistance = distanceBetween(element.x, element.y, x, y)
|
||||
|
@ -107,29 +109,29 @@ function getNearestOf(array, x, y, extraCondition)
|
|||
nearestDistance = distance
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nearest, nearestDistance
|
||||
return nearest, nearestDistance
|
||||
end
|
||||
|
||||
local NO_VALUE = {}
|
||||
|
||||
function buildCache(fetcher)
|
||||
local cacheData = {}
|
||||
return {
|
||||
cacheDate = cacheData,
|
||||
get = function(key)
|
||||
if cacheData[key] == NO_VALUE then
|
||||
return nil
|
||||
end
|
||||
if cacheData[key] ~= nil then
|
||||
return cacheData[key]
|
||||
end
|
||||
cacheData[key] = fetcher(key) or NO_VALUE
|
||||
return cacheData[key]
|
||||
end
|
||||
}
|
||||
local cacheData = {}
|
||||
return {
|
||||
cacheDate = cacheData,
|
||||
get = function(key)
|
||||
if cacheData[key] == NO_VALUE then
|
||||
return nil
|
||||
end
|
||||
if cacheData[key] ~= nil then
|
||||
return cacheData[key]
|
||||
end
|
||||
cacheData[key] = fetcher(key) or NO_VALUE
|
||||
return cacheData[key]
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
blipper = {}
|
||||
|
@ -146,6 +148,6 @@ function blipper.new(msInterval, imagePath1, imagePath2)
|
|||
draw = function(self, disableBlipping, x, y)
|
||||
local currentImage = (disableBlipping or self.blinker.on) and self.image2 or self.image1
|
||||
currentImage:draw(x, y)
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue