Compare commits
2 commits
0f0d092339
...
a22b6d1e22
Author | SHA1 | Date | |
---|---|---|---|
|
a22b6d1e22 | ||
|
737ae2efe0 |
BIN
assets/audio/sfx/buttonclick.ogg
Normal file
BIN
assets/audio/sfx/die.ogg
Executable file
BIN
assets/audio/sfx/dropfood.ogg
Executable file
BIN
assets/audio/sfx/grow.ogg
Normal file
BIN
assets/audio/sfx/slurp.ogg
Executable file
BIN
assets/audio/sfx/splash.ogg
Executable file
BIN
assets/audio/sfx/splashbig.ogg
Executable file
BIN
assets/audio/sfx/tapglass.ogg
Normal file
Before Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 222 B |
Before Width: | Height: | Size: 222 B |
Before Width: | Height: | Size: 311 B |
|
@ -1,6 +1,6 @@
|
|||
ease = require 'lib.ease'
|
||||
|
||||
FISH_SPLINE_QUALITY = 5 -- bigger number = more lag, smaller number = more noise
|
||||
FISH_SPLINE_QUALITY = 6 -- bigger number = more lag, smaller number = more noise
|
||||
FISH_RENDER_ITERS = 4 -- a bigger number allows for more smoother flipper movement and rotation, but is significantly laggier
|
||||
FISH_RENDER_ITEROFF = 0.025 -- should be 1/iters*0.1, lower numbers will make movement snappier and higher numbers will make it smoother but glitchier
|
||||
|
||||
|
@ -11,8 +11,10 @@ FISH_EASE = ease.inOutSine -- ease used for fish movement
|
|||
FISH_ANGLE = 30 -- bigger angle allows for fish to swim further down/up at once
|
||||
FISH_FOLLOW_RANDOM = 10
|
||||
|
||||
FISH_FOOD_CHECK_FREQ = 10 -- how often to check for food
|
||||
FISH_FOOD_COOLDOWN = 10
|
||||
FISH_FOOD_CHECK_FREQ = 1 -- how often to check for food
|
||||
FISH_FOOD_1_COOLDOWN = 10
|
||||
FISH_FOOD_2_COOLDOWN = 20
|
||||
FISH_FOOD_3_COOLDOWN = 25
|
||||
FISH_FOOD_HUNGRY = -5
|
||||
FISH_FOOD_DEAD = -15
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ function self.food(x, y, type)
|
|||
speed = 0.2,
|
||||
time = math.random(),
|
||||
deathtimer = 0,
|
||||
type = 1
|
||||
type = type
|
||||
}
|
||||
|
||||
return food
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'lib.audio'
|
||||
|
||||
local assets = {}
|
||||
|
||||
function assets.clear()
|
||||
|
@ -70,8 +72,8 @@ function assets.addAudio(base, d, type)
|
|||
if type == "sfx" then
|
||||
sound_path[audioname] = dir .. "/" .. file
|
||||
|
||||
if sounds and sounds[audioname] then
|
||||
registerSound(audioname, sounds[audioname].volume)
|
||||
if sounds then
|
||||
registerSound(audioname, (sounds[audioname] or {}).volume or 1)
|
||||
end
|
||||
elseif type == "bgm" then
|
||||
music_path[audioname] = dir .. "/" .. file
|
||||
|
|
34
lib/audio.lua
Normal file
|
@ -0,0 +1,34 @@
|
|||
local tick = require 'lib.tick'
|
||||
|
||||
sfx_volume = 1
|
||||
|
||||
sounds = {}
|
||||
local current_volume = 1
|
||||
local old_volume = 1
|
||||
local sound_instances = {}
|
||||
|
||||
function registerSound(sound, volume)
|
||||
sounds[sound] = {
|
||||
data = love.sound.newSoundData(sound_path[sound]),
|
||||
volume = volume or 1
|
||||
}
|
||||
end
|
||||
|
||||
function playSound(sound, volume, pitch)
|
||||
if sounds[sound] then
|
||||
if not sound_instances[sound] then
|
||||
sound_instances[sound] = 0
|
||||
end
|
||||
|
||||
local source = love.audio.newSource(sounds[sound].data, "static")
|
||||
|
||||
local adjusted_volume = 1/(2^sound_instances[sound])
|
||||
source:setVolume((volume or 1) * adjusted_volume * sounds[sound].volume * sfx_volume)
|
||||
source:setPitch(pitch or 1)
|
||||
|
||||
source:play()
|
||||
|
||||
sound_instances[sound] = sound_instances[sound] + 1
|
||||
tick.delay(function() sound_instances[sound] = sound_instances[sound] - 1 end, sounds[sound].data:getDuration()/4)
|
||||
end
|
||||
end
|
166
lib/tick.lua
Normal file
|
@ -0,0 +1,166 @@
|
|||
--
|
||||
-- tick
|
||||
--
|
||||
-- Copyright (c) 2015 rxi
|
||||
--
|
||||
-- This library is free software; you can redistribute it and/or modify it
|
||||
-- under the terms of the MIT license. See LICENSE for details.
|
||||
--
|
||||
|
||||
local tick = { _version = "0.1.1" }
|
||||
tick.__index = tick
|
||||
|
||||
|
||||
local iscallable = function(x)
|
||||
if type(x) == "function" then return true end
|
||||
local mt = getmetatable(x)
|
||||
return mt and mt.__call ~= nil
|
||||
end
|
||||
|
||||
local noop = function()
|
||||
end
|
||||
|
||||
|
||||
local event = {}
|
||||
event.__index = event
|
||||
|
||||
function event.new(parent, fn, delay, recur, err)
|
||||
err = err or 0
|
||||
-- Create and return event
|
||||
return setmetatable({
|
||||
parent = parent,
|
||||
delay = delay,
|
||||
timer = delay + err,
|
||||
fn = fn,
|
||||
recur = recur,
|
||||
}, event)
|
||||
end
|
||||
|
||||
|
||||
function event:after(fn, delay)
|
||||
-- Error check
|
||||
if self.recur then
|
||||
error("cannot chain a recurring event")
|
||||
end
|
||||
-- Chain event
|
||||
local oldfn = self.fn
|
||||
local e = event.new(self.parent, fn, delay, false)
|
||||
self.fn = function()
|
||||
oldfn()
|
||||
e.timer = e.timer + self.parent.err
|
||||
self.parent:add(e)
|
||||
end
|
||||
return e
|
||||
end
|
||||
|
||||
|
||||
function event:stop()
|
||||
tick.remove(self.parent, self)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function tick.group()
|
||||
return setmetatable({ err = 0 }, tick)
|
||||
end
|
||||
|
||||
|
||||
function tick:add(e)
|
||||
self[e] = true
|
||||
table.insert(self, e)
|
||||
return e
|
||||
end
|
||||
|
||||
|
||||
function tick:remove(e)
|
||||
if type(e) == "number" then
|
||||
-- Remove and return event
|
||||
local idx = e
|
||||
e = self[idx]
|
||||
self[e] = nil
|
||||
self[idx] = self[#self]
|
||||
table.remove(self)
|
||||
return e
|
||||
end
|
||||
self[e] = false
|
||||
for i, v in ipairs(self) do
|
||||
if v == e then
|
||||
return self:remove(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function tick:update(dt)
|
||||
for i = #self, 1, -1 do
|
||||
local e = self[i]
|
||||
e.timer = e.timer - dt
|
||||
while e.timer <= 0 do
|
||||
if e.recur then
|
||||
e.timer = e.timer + e.delay
|
||||
else
|
||||
self:remove(i)
|
||||
end
|
||||
self.err = e.timer
|
||||
e.fn()
|
||||
if not e.recur then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
self.err = 0
|
||||
end
|
||||
|
||||
|
||||
function tick:event(fn, delay, recur)
|
||||
delay = tonumber(delay)
|
||||
-- Error check
|
||||
if not iscallable(fn) then
|
||||
error("expected `fn` to be callable")
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
error("expected `delay` to be a number")
|
||||
end
|
||||
if delay < 0 then
|
||||
error("expected `delay` of zero or greater")
|
||||
end
|
||||
-- If, factoring in the timing error, the event should happen *now* the
|
||||
-- function is immediately called and the error is temporarily carried
|
||||
-- through. This assures nested events with delays shorter than the update()
|
||||
-- delta-time do not accumulate error; several nested events with very small
|
||||
-- delays may end up being called on the same frame. A dummy event is created
|
||||
-- and returned so :after() still functions correctly.
|
||||
local d = delay + self.err
|
||||
if d < 0 then
|
||||
local err = self.err
|
||||
self.err = d
|
||||
fn()
|
||||
self.err = err
|
||||
return self:add(event.new(self, noop, delay, recur, self.err))
|
||||
end
|
||||
-- Create, add and return a normal event
|
||||
return self:add(event.new(self, fn, delay, recur, self.err))
|
||||
end
|
||||
|
||||
|
||||
function tick:delay(fn, delay)
|
||||
return self:event(fn, delay, false)
|
||||
end
|
||||
|
||||
|
||||
function tick:recur(fn, delay)
|
||||
return self:event(fn, delay, true)
|
||||
end
|
||||
|
||||
|
||||
local group = tick.group()
|
||||
|
||||
local bound = {
|
||||
update = function(...) return tick.update(group, ...) end,
|
||||
delay = function(...) return tick.delay (group, ...) end,
|
||||
recur = function(...) return tick.recur (group, ...) end,
|
||||
remove = function(...) return tick.remove(group, ...) end,
|
||||
}
|
||||
setmetatable(bound, tick)
|
||||
|
||||
return bound
|
143
main.lua
|
@ -3,9 +3,11 @@ require 'util'
|
|||
|
||||
constr = require 'constructors'
|
||||
|
||||
require 'lib.audio'
|
||||
assets = require 'lib.assets'
|
||||
ease = require 'lib.ease'
|
||||
bench = require 'lib.benchmark'
|
||||
tick = require 'lib.tick'
|
||||
|
||||
sprites = {}
|
||||
sound_path = {}
|
||||
|
@ -17,6 +19,9 @@ debug = false
|
|||
food = {}
|
||||
feesh = {}
|
||||
|
||||
foodtier = 1
|
||||
foodcount = 1
|
||||
|
||||
footerbuttons = {
|
||||
{
|
||||
cost = 100,
|
||||
|
@ -24,27 +29,40 @@ footerbuttons = {
|
|||
openanim = 1,
|
||||
open = true,
|
||||
func = function()
|
||||
playSound('splash', 0.7, 0.8)
|
||||
table.insert(feesh, constr.fish(math.random(), 0.3))
|
||||
end
|
||||
},
|
||||
{
|
||||
cost = 200,
|
||||
sprite = 'food',
|
||||
tier = 1,
|
||||
tier = foodtier,
|
||||
openanim = 1,
|
||||
open = true,
|
||||
func = function()
|
||||
--table.insert(feesh, constr.fish(math.random(), 0.3))
|
||||
func = function(self)
|
||||
self.openanim = 0
|
||||
foodtier = foodtier + 1
|
||||
if foodtier >= 3 then
|
||||
self.open = false
|
||||
else
|
||||
self.tier = foodtier
|
||||
end
|
||||
end
|
||||
},
|
||||
{
|
||||
cost = 300,
|
||||
sprite = 'foodcount',
|
||||
tier = 3,
|
||||
tier = foodcount,
|
||||
openanim = 1,
|
||||
open = true,
|
||||
func = function()
|
||||
--table.insert(feesh, constr.fish(math.random(), 0.3))
|
||||
func = function(self)
|
||||
self.openanim = 0
|
||||
foodcount = foodcount + 1
|
||||
if foodcount >= 9 then
|
||||
self.open = false
|
||||
else
|
||||
self.tier = foodcount
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
|
@ -103,8 +121,15 @@ local unfocusinterval = 5
|
|||
function love.update(dt)
|
||||
frame = frame + 1
|
||||
bench.update()
|
||||
tick.update(dt)
|
||||
|
||||
bench.startBenchmark('update')
|
||||
bench.startBenchmark('update_buttons')
|
||||
for _,btn in ipairs(footerbuttons) do
|
||||
btn.openanim = btn.openanim + dt * 6
|
||||
end
|
||||
bench.stopBenchmark('update_buttons')
|
||||
|
||||
bench.startBenchmark('update_food')
|
||||
for i,f in ipairs(food) do
|
||||
f.y = f.y + dt * f.speed
|
||||
|
@ -240,16 +265,21 @@ function love.update(dt)
|
|||
local dist = math.abs(n.render.x - f.x) + math.abs(n.render.y - f.y)
|
||||
|
||||
if dist < FOOD_HITBOX then
|
||||
playSound('slurp')
|
||||
table.remove(food, n.shortestfood)
|
||||
n.eattimer = FISH_FOOD_COOLDOWN
|
||||
local cooldowns = {FISH_FOOD_1_COOLDOWN, FISH_FOOD_2_COOLDOWN, FISH_FOOD_3_COOLDOWN}
|
||||
|
||||
n.eattimer = cooldowns[f.type]
|
||||
n.render.eattimer = 0
|
||||
n.shortestfood = -1
|
||||
|
||||
if n.lifetime > FISH_AGE_MEDIUM then
|
||||
if n.lifetime > FISH_AGE_MEDIUM and n.size == 0 then
|
||||
n.size = 1
|
||||
playSound('grow')
|
||||
end
|
||||
if n.lifetime > FISH_AGE_BIG then
|
||||
if n.lifetime > FISH_AGE_BIG and n.size == 1 then
|
||||
n.size = 2
|
||||
playSound('grow')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -293,7 +323,7 @@ function love.update(dt)
|
|||
n.render.turn = n.render.turn + dt * math.sign(n.render.x - n.render.prevx) * 2
|
||||
n.render.turn = clamp(n.render.turn, 0, 1)
|
||||
if n.render.turn == 0 or n.render.turn == 1 then
|
||||
n.render.swim = (n.render.swim + dt * math.abs(n.render.xspeed) * 300) % 1
|
||||
n.render.swim = (n.render.swim + dt * (math.abs(n.render.xspeed) * 250 + 0.5)) % 1
|
||||
else
|
||||
n.render.swim = 0
|
||||
end
|
||||
|
@ -311,6 +341,9 @@ function love.update(dt)
|
|||
n.render.hungry = clamp(n.render.hungry + dt * m * 3, 0, 1)
|
||||
bench.stopBenchmark('update_fish_render')
|
||||
|
||||
if n.eattimer <= FISH_FOOD_DEAD and not n.dead then
|
||||
playSound('die')
|
||||
end
|
||||
if n.eattimer <= FISH_FOOD_DEAD then
|
||||
n.dead = true
|
||||
|
||||
|
@ -504,7 +537,7 @@ function love.draw()
|
|||
|
||||
bench.startBenchmark('render_food')
|
||||
for _,f in ipairs(food) do
|
||||
local sheet = sheets.food1
|
||||
local sheet = sheets['food' .. (f.type)]
|
||||
local x = f.x * sw
|
||||
local y = f.y * sh
|
||||
local frame = math.floor((f.time%1) * #sheet.quads) + 1
|
||||
|
@ -539,50 +572,52 @@ function love.draw()
|
|||
love.graphics.draw(sprites['footer/buttonbg'], x * size, y * size, 0, size, size)
|
||||
end
|
||||
|
||||
if btn and btn.open then
|
||||
-- sprite inside
|
||||
if btn.sprite == 'guppy' then
|
||||
local sheet = fishsprite('medium', false, 'swim')
|
||||
local frame = math.floor((love.timer.getTime() * 2) % 1 * #sheet.quads) + 1
|
||||
local scale = (sprites['footer/buttonbg']:getWidth() / sheet.width) * 0.9
|
||||
local offset = (sprites['footer/buttonbg']:getWidth() * size) / 2
|
||||
love.graphics.draw(sheet.spriteSheet, sheet.quads[frame], x * size + offset, y * size + offset*0.75, 0, size * scale, size * scale, sheet.width/2, sheet.width/2)
|
||||
elseif btn.sprite == 'food' then
|
||||
local sheets = {sheets.food2, sheets.food3}
|
||||
local sheet = sheets[btn.tier]
|
||||
if btn then
|
||||
if btn.open then
|
||||
-- sprite inside
|
||||
if btn.sprite == 'guppy' then
|
||||
local sheet = fishsprite('medium', false, 'swim')
|
||||
local frame = math.floor((love.timer.getTime() * 2) % 1 * #sheet.quads) + 1
|
||||
local scale = (sprites['footer/buttonbg']:getWidth() / sheet.width) * 0.9
|
||||
local offset = (sprites['footer/buttonbg']:getWidth() * size) / 2
|
||||
love.graphics.draw(sheet.spriteSheet, sheet.quads[frame], x * size + offset, y * size + offset*0.75, 0, size * scale, size * scale, sheet.width/2, sheet.width/2)
|
||||
elseif btn.sprite == 'food' then
|
||||
local sheets = {sheets.food2, sheets.food3}
|
||||
local sheet = sheets[btn.tier]
|
||||
|
||||
local scale = (sprites['footer/buttonbg']:getWidth() / sheet.width) * 0.65
|
||||
local offset = (sprites['footer/buttonbg']:getWidth() * size) / 2
|
||||
love.graphics.draw(sheet.spriteSheet, sheet.quads[1], x * size + offset, y * size + offset*0.75, 0, size * scale, size * scale, sheet.width/2, sheet.width/2)
|
||||
elseif btn.sprite == 'foodcount' then
|
||||
love.graphics.setFont(fonts.continuum)
|
||||
local offset = (sprites['footer/buttonbg']:getWidth() * size) / 2
|
||||
local scale = (sprites['footer/buttonbg']:getWidth() / sheet.width) * 0.65
|
||||
local offset = (sprites['footer/buttonbg']:getWidth() * size) / 2
|
||||
love.graphics.draw(sheet.spriteSheet, sheet.quads[1], x * size + offset, y * size + offset*0.75, 0, size * scale, size * scale, sheet.width/2, sheet.width/2)
|
||||
elseif btn.sprite == 'foodcount' then
|
||||
love.graphics.setFont(fonts.continuum)
|
||||
local offset = (sprites['footer/buttonbg']:getWidth() * size) / 2
|
||||
|
||||
local bordersize = 1
|
||||
for _,p in ipairs({{0, 1}, {1, 0}, {1, 1}, {-1, 0}, {0, -1}, {-1, -1}, {1, -1}, {-1, 1}}) do
|
||||
love.graphics.setColor(0, 0, 0, 0.5)
|
||||
love.graphics.printf(btn.tier + 1, round(x * size) + p[1] * bordersize, round(y * size + offset*0.75 - fonts.continuum:getHeight()/2) + p[2] * bordersize, round(sprites['footer/buttonbg']:getWidth() * size), 'center')
|
||||
local bordersize = 1
|
||||
for _,p in ipairs({{0, 1}, {1, 0}, {1, 1}, {-1, 0}, {0, -1}, {-1, -1}, {1, -1}, {-1, 1}}) do
|
||||
love.graphics.setColor(0, 0, 0, 0.5)
|
||||
love.graphics.printf(btn.tier + 1, round(x * size) + p[1] * bordersize, round(y * size + offset*0.75 - fonts.continuum:getHeight()/2) + p[2] * bordersize, round(sprites['footer/buttonbg']:getWidth() * size), 'center')
|
||||
end
|
||||
|
||||
love.graphics.setColor(0, 1, 0)
|
||||
love.graphics.printf(btn.tier + 1, round(x * size), round(y * size + offset*0.75 - fonts.continuum:getHeight()/2), round(sprites['footer/buttonbg']:getWidth() * size), 'center')
|
||||
end
|
||||
|
||||
-- price
|
||||
love.graphics.setFont(fonts.pix)
|
||||
local font = love.graphics.getFont()
|
||||
love.graphics.setColor(0, 1, 0)
|
||||
love.graphics.printf(btn.tier + 1, round(x * size), round(y * size + offset*0.75 - fonts.continuum:getHeight()/2), round(sprites['footer/buttonbg']:getWidth() * size), 'center')
|
||||
love.graphics.printf('$' .. btn.cost, round(x * size), round(y * size + 51 * size - font:getHeight()/2), round(sprites['footer/buttonbg']:getWidth() * size), 'center')
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
love.graphics.setFont(fonts.default)
|
||||
|
||||
-- reflection
|
||||
love.graphics.setBlendMode('add')
|
||||
love.graphics.draw(sprites['footer/reflection'], x * size, y * size, 0, size, size)
|
||||
love.graphics.setBlendMode('alpha')
|
||||
end
|
||||
|
||||
-- price
|
||||
love.graphics.setFont(fonts.pix)
|
||||
local font = love.graphics.getFont()
|
||||
love.graphics.setColor(0, 1, 0)
|
||||
love.graphics.printf('$' .. btn.cost, round(x * size), round(y * size + 51 * size - font:getHeight()/2), round(sprites['footer/buttonbg']:getWidth() * size), 'center')
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
love.graphics.setFont(fonts.default)
|
||||
|
||||
-- reflection
|
||||
love.graphics.setBlendMode('add')
|
||||
love.graphics.draw(sprites['footer/reflection'], x * size, y * size, 0, size, size)
|
||||
love.graphics.setBlendMode('alpha')
|
||||
|
||||
-- open/close anim
|
||||
if (btn and not btn.openanim == 1) then
|
||||
if (btn and btn.openanim < 1) then
|
||||
local sheet = sheets.buttonopen
|
||||
local anim = 0
|
||||
if btn then anim = btn.openanim end
|
||||
|
@ -606,12 +641,11 @@ function love.draw()
|
|||
end
|
||||
|
||||
function love.mousepressed(x, y, b)
|
||||
if b == 2 then
|
||||
table.insert(food, constr.food(x/love.graphics.getWidth(), y/love.graphics.getHeight(), 1))
|
||||
if b == 1 and y > FOOTER_HEIGHT and #food < foodcount then
|
||||
table.insert(food, constr.food(x/love.graphics.getWidth(), y/love.graphics.getHeight(), foodtier))
|
||||
playSound('dropfood')
|
||||
end
|
||||
end
|
||||
|
||||
function love.mousereleased(x, y, b)
|
||||
local footerheight = FOOTER_HEIGHT * love.graphics.getWidth()/640
|
||||
local size = footerheight / FOOTER_HEIGHT
|
||||
|
||||
|
@ -622,7 +656,8 @@ function love.mousereleased(x, y, b)
|
|||
|
||||
if hovered then
|
||||
if footerbuttons[i] and footerbuttons[i].open then
|
||||
footerbuttons[i].func()
|
||||
footerbuttons[i].func(footerbuttons[i])
|
||||
playSound('buttonclick')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -634,6 +669,10 @@ function love.mousereleased(x, y, b)
|
|||
end
|
||||
end
|
||||
|
||||
function love.mousereleased(x, y, b)
|
||||
|
||||
end
|
||||
|
||||
function love.keypressed(key)
|
||||
if key == 'f3' then
|
||||
debug = not debug
|
||||
|
|
8
util.lua
|
@ -55,4 +55,12 @@ end
|
|||
|
||||
function math.sign(a)
|
||||
if a >= 0 then return 1 else return -1 end
|
||||
end
|
||||
|
||||
function string.starts(str, start)
|
||||
return str:sub(1, #start) == start
|
||||
end
|
||||
|
||||
function string.ends(str, ending)
|
||||
return ending == "" or str:sub(-#ending) == ending
|
||||
end
|