Compare commits

...

2 commits

Author SHA1 Message Date
jill
436d5f0301
readme 2021-01-23 01:33:42 +03:00
jill
ae65683e44
add current stuff yeha 2021-01-23 01:33:08 +03:00
82 changed files with 965 additions and 1 deletions

View file

@ -1,3 +1,5 @@
# notsanequarium
a remake/remaster of insaniquarium, the game published by popcap in 2004
a remake/remaster of insaniquarium, the game published by popcap in 2004
launch like any other love2d game

BIN
assets/sprites/bg/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 KiB

BIN
assets/sprites/bg/1.png~ Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
assets/sprites/food/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
assets/sprites/food/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
assets/sprites/food/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/sprites/shadow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

13
conf.lua Normal file
View file

@ -0,0 +1,13 @@
function love.conf(t)
t.version = "11.1"
t.release = false
t.window.title = 'notsanequarium'
t.window.resizable = true
t.window.vsync = false
t.window.minwidth = 640
t.window.minheight = 480
t.window.width = 640
t.window.height = 480
-- t.window.msaa = 2
end

28
const.lua Normal file
View file

@ -0,0 +1,28 @@
ease = require 'lib.ease'
FISH_SPLINE_QUALITY = 5 -- 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
FISH_RENDER_FREQ = 4 -- only update the quality renders once every x frames
FISH_COLISSION_CHECK_FREQ = 2 -- how often to update the colission
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_COOLDOWN = 10
FISH_FOOD_HUNGRY = -5
FISH_FOOD_DEAD = -15
FISH_AGE_MEDIUM = 25 -- the age, in seconds, needed for a guppy to become a medium guppy
FISH_AGE_BIG = 65 -- see above, but for big
FISH_SIZE = 6 -- how many large guppies can you fit on the screen
FOOTER_HEIGHT = 68
FOOD_HITBOX = 0.05
DEBUG_FISH_PATH_SUBDIVISIONS = 50
DEBUG_FISH_PREDICT_AMOUNT = 0.5

60
constructors.lua Normal file
View file

@ -0,0 +1,60 @@
require 'const'
local self = {}
function self.fish(x, y)
local fish = {
x = x,
y = y,
size = 0, -- 0 for small, 1 for medium, 2 for big, 3 for king
eattimer = 0 + math.random() * 2, -- starts out hungry, with a 2s delay
lifetime = 0,
eases = {
},
render = {
x = 0,
y = 0,
prevx = 0,
prevy = 0,
turn = 0,
swim = 0,
angle = 0,
xspeed = 0,
yspeed = 0,
eattimer = 1,
hungry = 0,
}
}
for i = 1, FISH_SPLINE_QUALITY do
table.insert(fish.eases, { -- we insert a fake ease that ends instantly and just hope the update loop replaces it with a valid, random one
x = x,
y = y,
fromx = x,
fromy = y,
a = 1,
speed = 1
})
end
return fish
end
function self.food(x, y, type)
local food = {
x = x,
y = y,
speed = 0.2,
time = math.random(),
deathtimer = 0,
type = 1,
goal = 0 -- assigns 0 as the fish so that hopefully the update loop replaces it with a valid, random one
}
return food
end
return self

84
lib/assets.lua Normal file
View file

@ -0,0 +1,84 @@
local assets = {}
function assets.clear()
print(" clearing assets")
sprites = {}
sound_path = {}
music_path = {}
end
function assets.load(base)
print(" loading " .. base)
assets.addSprites(base)
print("✓ added sprites")
assets.addAudio(base)
print("✓ added audio")
end
function assets.addSprites(base, d)
local dir = base.."/sprites"
if d then
dir = dir .. "/" .. d
end
local files = love.filesystem.getDirectoryItems(dir)
for _,file in ipairs(files) do
if string.sub(file, -4) == ".png" then
local spritename = string.sub(file, 1, -5)
local sprite = love.graphics.newImage(dir .. "/" .. file)
if d then
spritename = d .. "/" .. spritename
end
sprites[spritename] = sprite
--print(colr.cyan(" added sprite "..spritename))
elseif love.filesystem.getInfo(dir .. "/" .. file).type == "directory" then
--print(colr.cyan(" found sprite dir: " .. file))
local newdir = file
if d then
newdir = d .. "/" .. newdir
end
assets.addSprites(base, newdir)
end
end
end
function assets.addAudio(base, d, type)
local dir = base.."/audio"
if d then
dir = dir .. "/" .. d
end
local files = love.filesystem.getDirectoryItems(dir)
for _,file in ipairs(files) do
if love.filesystem.getInfo(dir .. "/" .. file).type == "directory" then
local newdir = file
if d then
newdir = d .. "/" .. newdir
end
assets.addAudio(base, newdir, type or file)
else
local audioname = file
if file:ends(".wav") then audioname = file:sub(1, -5) end
if file:ends(".mp3") then audioname = file:sub(1, -5) end
if file:ends(".ogg") then audioname = file:sub(1, -5) end
if file:ends(".flac") then audioname = file:sub(1, -5) end
if file:ends(".xm") then audioname = file:sub(1, -4) end
--[[if d then
audioname = d .. "/" .. audioname
end]]
if type == "sfx" then
sound_path[audioname] = dir .. "/" .. file
if sounds and sounds[audioname] then
registerSound(audioname, sounds[audioname].volume)
end
elseif type == "bgm" then
music_path[audioname] = dir .. "/" .. file
end
--print(" audio "..audioname.." added")
end
end
end
return assets

88
lib/benchmark.lua Normal file
View file

@ -0,0 +1,88 @@
local this = {}
local updateLimit = 50
local benchmarkingThings = {}
local latestBenchmark = {}
local doneBench = {}
local function shrt(num)
return math.floor(num * 10000) / 10000
end
this.update = function()
doneBench = {}
end
this.startBenchmark = function(name)
latestBenchmark[name] = love.timer.getTime()
end
this.stopBenchmark = function(name)
if not benchmarkingThings[name] then benchmarkingThings[name] = {} end
if doneBench[name] then
local tbl = benchmarkingThings[name]
tbl[#tbl] = tbl[#tbl] + love.timer.getTime() - latestBenchmark[name]
else
table.insert(benchmarkingThings[name], love.timer.getTime() - latestBenchmark[name])
end
doneBench[name] = true
if #benchmarkingThings[name] > updateLimit then table.remove(benchmarkingThings[name], 1) end
end
this.getAverage = function(name)
local things
if name then
things = benchmarkingThings[name]
else
things = {}
for key in pairs(this.benchmarks) do
for _,n in ipairs(benchmarkingThings[key]) do
table.insert(things, n)
end
end
end
local total = 0
for _,n in ipairs(things) do
total = total + n
end
return total / #things
end
this.renderBenchmark = function()
local font = love.graphics.getFont()
local result = ''
local rowNum = 0
local averages = {}
local max = 0
for n,_ in pairs(this.benchmarks) do
local average = this.getAverage(n)
max = math.max(average, max)
averages[n] = average
end
for n,_ in pairs(this.benchmarks) do
local average = averages[n]
result = result .. '\n' .. shrt(average) .. 'ms' .. ' ' .. n
rowNum = rowNum + 1
local yoff = rowNum * font:getHeight()
love.graphics.setColor(1, 1, 1, 0.7)
love.graphics.rectangle('fill', 0, yoff, average * 1/max * love.graphics.getWidth(), font:getHeight())
end
love.graphics.setColor(0, 0, 0)
love.graphics.print(result)
end
this.benchmarks = benchmarkingThings
return this

140
lib/ease.lua Normal file
View file

@ -0,0 +1,140 @@
local this = {}
local sqrt = math.sqrt
local sin = math.sin
local cos = math.cos
local pow = math.pow
local exp = math.exp
local pi = math.pi
local abs = math.abs
function this.linear(t) return t end
function this.inQuad(t) return t * t end
function this.outQuad(t) return -t * (t - 2) end
function this.inOutQuad(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 2
else
return 1 - 0.5 * (2 - t) ^ 2
end
end
function this.inCubic(t) return t * t * t end
function this.outCubic(t) return 1 - (1 - t) ^ 3 end
function this.inOutCubic(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 3
else
return 1 - 0.5 * (2 - t) ^ 3
end
end
function this.inQuart(t) return t * t * t * t end
function this.outQuart(t) return 1 - (1 - t) ^ 4 end
function this.inOutQuart(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 4
else
return 1 - 0.5 * (2 - t) ^ 4
end
end
function this.inQuint(t) return t ^ 5 end
function this.outQuint(t) return 1 - (1 - t) ^ 5 end
function this.inOutQuint(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 5
else
return 1 - 0.5 * (2 - t) ^ 5
end
end
function this.inExpo(t) return 1000 ^ (t - 1) - 0.001 end
function this.outExpo(t) return 0.999 - 1000 ^ -t end
function this.inOutExpo(t)
t = t * 2
if t < 1 then
return 0.5 * 1000 ^ (t - 1) - 0.0005
else
return 0.9995 - 0.5 * 1000 ^ (1 - t)
end
end
function this.inCirc(t) return 1 - sqrt(1 - t * t) end
function this.outCirc(t) return sqrt(-t * t + 2 * t) end
function this.inOutCirc(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * sqrt(1 - t * t)
else
t = t - 2
return 0.5 + 0.5 * sqrt(1 - t * t)
end
end
function this.inElastic(t)
t = t - 1
return -(pow(2, 10 * t) * sin((t - 0.075) * (2 * pi) / 0.3))
end
function this.outElastic(t)
return pow(2, -10 * t) * sin((t - 0.075) * (2 * pi) / 0.3) + 1
end
function this.inOutElastic(t)
t = t * 2 - 1
if t < 0 then
return -0.5 * pow(2, 10 * t) * sin((t - 0.1125) * 2 * pi / 0.45)
else
return pow(2, -10 * t) * sin((t - 0.1125) * 2 * pi / 0.45) * 0.5 + 1
end
end
function this.inBack(t) return t * t * (2.70158 * t - 1.70158) end
function this.outBack(t)
t = t - 1
return (t * t * (2.70158 * t + 1.70158)) + 1
end
function this.inOutBack(t)
t = t * 2
if t < 1 then
return 0.5 * (t * t * (3.5864016 * t - 2.5864016))
else
t = t - 2
return 0.5 * (t * t * (3.5864016 * t + 2.5864016) + 2)
end
end
function this.outBounce(t)
if t < 1 / 2.75 then
return 7.5625 * t * t
elseif t < 2 / 2.75 then
t = t - 1.5 / 2.75
return 7.5625 * t * t + 0.75
elseif t < 2.5 / 2.75 then
t = t - 2.25 / 2.75
return 7.5625 * t * t + 0.9375
else
t = t - 2.625 / 2.75
return 7.5625 * t * t + 0.984375
end
end
function this.inBounce(t) return 1 - this.outBounce(1 - t) end
function this.inOutBounce(t)
if t < 0.5 then
return this.inBounce(t * 2) * 0.5
else
return this.outBounce(t * 2 - 1) * 0.5 + 0.5
end
end
function this.inSine(x)
return 1 - cos(x * (pi * 0.5))
end
function this.outSine(x)
return sin(x * (pi * 0.5))
end
function this.inOutSine(x)
return 0.5 - 0.5 * cos(x * pi)
end
return this

491
main.lua Normal file
View file

@ -0,0 +1,491 @@
require 'const'
require 'util'
constr = require 'constructors'
assets = require 'lib.assets'
ease = require 'lib.ease'
bench = require 'lib.benchmark'
sprites = {}
sound_path = {}
music_path = {}
debug = false
food = {}
feesh = {}
local sheets = {}
local function newAnimation(image, width, height)
local animation = {}
animation.spriteSheet = image;
animation.quads = {};
animation.width = width
animation.height = height
for y = 0, image:getHeight() - height, height do
for x = 0, image:getWidth() - width, width do
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))
end
end
return animation
end
local function fishsprite(size, hungry, anim)
-- anim is turn, swim, eat or die
if anim == 'die' then hungry = false end
local spritename = size .. '_' .. (hungry and 'hungry_' or '') .. anim
local spr = sprites['fish/' .. spritename]
return newAnimation(spr, spr:getWidth()/10, spr:getHeight())
end
function love.load()
assets.clear()
assets.load('assets')
sheets.wavecenter = newAnimation(sprites['wave/wavecenter'], sprites['wave/wavecenter']:getWidth(), sprites['wave/wavecenter']:getHeight()/12)
sheets.waveside = newAnimation(sprites['wave/waveside'], sprites['wave/waveside']:getWidth(), sprites['wave/waveside']:getHeight()/12)
sheets.food1 = newAnimation(sprites['food/1'], sprites['food/1']:getWidth()/10, sprites['food/1']:getHeight())
for i = 1, 10 do
table.insert(feesh, constr.fish(math.random(), math.random()))
end
end
local frame = 0
local unfocusinterval = 5
function love.update(dt)
frame = frame + 1
bench.update()
bench.startBenchmark('update')
bench.startBenchmark('update_food')
local fishgoals = {}
for i,f in ipairs(food) do
f.y = f.y + dt * f.speed
f.time = f.time + dt
f.y = clamp(f.y, 0, 0.9)
if f.y == 0.9 then
f.deathtimer = f.deathtimer + dt
end
if f.goal == 0 or not f.goal then
for i,n in ipairs(feesh) do
if not fishgoals[i] and n.eattimer <= 0 then
f.goal = i
end
end
end
if f.deathtimer > 1 then
table.remove(food, i)
else
fishgoals[f.goal] = i
end
end
bench.stopBenchmark('update_food')
bench.startBenchmark('update_fish')
for fi, n in ipairs(feesh) do
bench.startBenchmark('update_fish_eases')
for _, e in ipairs(n.eases) do
e.a = e.a + dt * e.speed
end
for i, e in ipairs(n.eases) do
if e.a > 1 then
local sumx = 0
local sumy = 0
for i2, e2 in ipairs(n.eases) do
if i ~= i2 then
sumx = sumx + mix(e2.fromx, e2.x, FISH_EASE(math.min(e2.a, 1)))
sumy = sumy + mix(e2.fromy, e2.y, FISH_EASE(math.min(e2.a, 1)))
end
end
sumx = sumx + e.x
sumy = sumy + e.y
n.x = sumx / #n.eases
n.y = sumy / #n.eases
e.fromx = e.x
e.fromy = e.y
e.a = e.a - 1
local angle = math.random(-FISH_ANGLE, FISH_ANGLE)
local str = math.random(70, 200)/200/4
if n.eattimer < -15 then
str = str * 1.3
end
if --[[love.mouse.isDown(1) or]] fishgoals[fi] then
local mx, my = love.mouse.getPosition()
mx = mx / love.graphics.getWidth()
my = my / love.graphics.getHeight()
if fishgoals[fi] and food[fishgoals[fi]] then
mx = food[fishgoals[fi]].x
my = food[fishgoals[fi]].y
end
angle = math.deg(math.atan2(my - n.y, mx - n.x)) + math.random(-FISH_FOLLOW_RANDOM, FISH_FOLLOW_RANDOM)
end
local x = math.cos(math.rad(angle)) * str
local y = math.sin(math.rad(angle)) * str
if not (--[[love.mouse.isDown(1) or ]]fishgoals[fi]) then
x = x * math.sign(n.render.x - n.render.prevx)
end
e.x = n.x + x
e.y = n.y + y
e.x = 1 - math.abs(e.x%2-1)
e.y = 1 - math.abs(e.y%2-1)
local fheight = (FOOTER_HEIGHT * love.graphics.getWidth()/640)/love.graphics.getHeight()
if e.y < fheight then
e.y = e.y + (fheight - e.y) * 2
end
e.speed = 1 / (math.sqrt(math.pow(math.abs(e.x - e.fromx), 2) + math.pow(math.abs(e.y - e.fromy), 2))/2) / 15
end
end
bench.stopBenchmark('update_fish_eases')
bench.startBenchmark('update_fish_position')
n.x = clamp(n.x, 0, 1)
n.y = clamp(n.y, 0, 0.5)
n.lifetime = n.lifetime + dt
n.eattimer = n.eattimer - dt
local sumx = 0
local sumy = 0
for _, e2 in ipairs(n.eases) do
sumx = sumx + mix(e2.fromx, e2.x, FISH_EASE(e2.a))
sumy = sumy + mix(e2.fromy, e2.y, FISH_EASE(e2.a))
end
n.render.prevx = n.render.x
n.render.prevy = n.render.y
n.render.x = sumx / #n.eases
n.render.y = sumy / #n.eases
n.render.x = clamp(n.render.x, 0.05, 0.95)
n.render.y = clamp(n.render.y, 0.1, 0.85)
if fishgoals[fi] and frame % FISH_COLISSION_CHECK_FREQ == 0 then
local f = food[fishgoals[fi]]
if f then
local dist = math.abs(n.render.x - f.x) + math.abs(n.render.y - f.y)
if dist < FOOD_HITBOX then
table.remove(food, fishgoals[fi])
n.eattimer = FISH_FOOD_COOLDOWN
n.render.eattimer = 0
if n.lifetime > FISH_AGE_MEDIUM then
n.size = 1
end
if n.lifetime > FISH_AGE_BIG then
n.size = 2
end
end
end
end
bench.stopBenchmark('update_fish_position')
bench.startBenchmark('update_fish_render')
if frame % FISH_RENDER_FREQ == 0 then
local dt = dt * FISH_RENDER_FREQ
local iter = FISH_RENDER_ITERS
local iteroff = FISH_RENDER_ITEROFF
local xspeed = {}
local yspeed = {}
local angle = {}
local sumxiter = {}
local sumyiter = {}
for i = 1, iter do
local off = i * iteroff
local sumx2 = 0
local sumy2 = 0
for _, e2 in ipairs(n.eases) do
sumx2 = sumx2 + mix(e2.fromx, e2.x, FISH_EASE(e2.a + off))
sumy2 = sumy2 + mix(e2.fromy, e2.y, FISH_EASE(e2.a + off))
end
table.insert(xspeed, (sumx2 - (sumxiter[i - 1] or sumx)) / #n.eases)
table.insert(yspeed, (sumy2 - (sumyiter[i - 1] or sumy)) / #n.eases)
table.insert(angle, math.atan2(sumy2 - (sumyiter[i - 1] or sumy), sumx2 - (sumxiter[i - 1] or sumx)))
table.insert(sumxiter, sumx2)
table.insert(sumyiter, sumy2)
end
n.render.xspeed = sum(xspeed)
n.render.yspeed = sum(yspeed)
n.render.angle = sum(angle)
end
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) * 100) % 1
else
n.render.swim = 0
end
n.render.eattimer = n.render.eattimer + dt * 3
if n.render.eattimer < 1 then
n.render.swim = 0
end
n.render.turndir = math.sign(n.render.x - n.render.prevx)
local m = n.eattimer < FISH_FOOD_HUNGRY and 1 or -1
n.render.hungry = clamp(n.render.hungry + dt * m * 3, 0, 1)
bench.stopBenchmark('update_fish_render')
end
bench.stopBenchmark('update_fish')
bench.stopBenchmark('update')
end
function love.draw()
bench.startBenchmark('render')
bench.startBenchmark('render_tank')
love.graphics.setColor(1, 1, 1)
local sw, sh = love.graphics.getDimensions()
local footerheight = FOOTER_HEIGHT * sw/640
local yscale = (sh-footerheight)/sh
local sample = fishsprite('medium', false, 'swim')
local spritescale = (math.min(sw, sh)/FISH_SIZE) / math.min(sample.width, sample.height)
stretchto(sprites['bg/1'], 0, footerheight - FOOTER_HEIGHT, 0, sw, sh - (footerheight - FOOTER_HEIGHT))
-- waves
bench.startBenchmark('render_wave')
local wavecount = round(sw / sprites['wave/wavecenter']:getWidth())
local wavescale = sw / (wavecount * sprites['wave/wavecenter']:getWidth())
for i = 1, wavecount do
local a = (i - 1) / wavecount
local x = a * sw
local frame = round((1 - math.abs(love.timer.getTime()%2-1)) * #sheets.wavecenter.quads)
love.graphics.setBlendMode('add')
local sheet = sheets.wavecenter
local sizex = 1
if i == 1 or i == wavecount then
sheet = sheets.waveside
end
if i == wavecount then
sizex = -1
end
love.graphics.draw(sheet.spriteSheet, sheet.quads[math.max(frame, 1)], x + (sprites['wave/wavecenter']:getWidth() * wavescale)/2, footerheight + 20, 0, wavescale * sizex, wavescale, sprites['wave/wavecenter']:getWidth()/2)
end
love.graphics.setBlendMode('alpha')
bench.stopBenchmark('render_wave')
-- shadow
bench.startBenchmark('render_shadow')
for i, n in ipairs(feesh) do
love.graphics.setColor(1, 1, 1, n.render.y + 0.2)
love.graphics.draw(sprites['shadow'], n.render.x * sw - (sprites['shadow']:getWidth() * spritescale)/2, sh - sh * 0.18 - (sprites['shadow']:getHeight() * spritescale)/2 + n.render.y * sh * 0.08, 0, spritescale, spritescale)
end
bench.stopBenchmark('render_shadow')
-- all the fish
bench.startBenchmark('render_fish')
for i, n in ipairs(feesh) do
local x = n.render.x * sw
local y = n.render.y * sh
-- rest of feesh
local size = 'small'
local anim = 'swim'
local sample = fishsprite('medium', false, 'swim')
local sizex = 1
local turn = n.render.turn
if n.render.turndir == -1 then turn = 1 - turn; sizex = -1 end
local turnframe = math.floor(turn * (#sample.quads - 1)) + 1
if #sample.quads == 10 then
sizex = -1 * sizex
turnframe = 1
end
local frame = math.floor(n.render.swim * (#sample.quads - 1)) + 1
if turnframe ~= 1 and turnframe ~= 10 then
anim = 'turn'
frame = turnframe
end
if n.render.eattimer <= 1 then
anim = 'eat'
frame = math.floor(n.render.eattimer * (#sample.quads - 1)) + 1
end
local angle = n.render.angle
if angle > math.pi/2 then
angle = angle - math.pi
end
if angle < -math.pi/2 then
angle = angle + math.pi
end
angle = angle * math.max(math.min((-math.abs(angle) + math.pi * 0.5) / (math.pi * 0.5) * 2, 1), 0)
angle = angle * 0.5
if n.size == 0 then
size = 'small'
elseif n.size == 1 then
size = 'medium'
elseif n.size == 2 then
size = 'big'
elseif n.size == 3 then
size = 'king'
end
local sheet = fishsprite(size, false, anim)
local sadsheet = fishsprite(size, true, anim)
local alpha = n.render.hungry == 1 and 0 or 1
love.graphics.setColor(1, 1, 1, alpha)
love.graphics.draw(sheet.spriteSheet, sheet.quads[math.max(math.min(frame, #sample.quads), 1)], x, y, angle, sizex * spritescale, spritescale, sample.width/2, sample.height/2)
love.graphics.setColor(1, 1, 1, n.render.hungry)
love.graphics.draw(sadsheet.spriteSheet, sheet.quads[math.max(math.min(frame, #sample.quads), 1)], x, y, angle, sizex * spritescale, spritescale, sample.width/2, sample.height/2)
love.graphics.setColor(1, 1, 1)
if debug then love.graphics.print(shrt(n.eattimer), x + 20, y + 20) end
if debug then
for _,e in ipairs(n.eases) do
love.graphics.setColor(1, 0, 0, 0.75)
love.graphics.line(e.fromx * sw, e.fromy * sh, e.x * sw, e.y * sh)
love.graphics.setColor(0, 0, 1, 0.75)
love.graphics.line(mix(e.fromx, e.x, FISH_EASE(e.a)) * sw, mix(e.fromy, e.y, FISH_EASE(e.a)) * sh, n.render.x * sw, n.render.y * sh)
end
local subdiv = DEBUG_FISH_PATH_SUBDIVISIONS
local adv = DEBUG_FISH_PREDICT_AMOUNT
local pos = {}
local valid = {}
for i = 1, subdiv do
local a = ((i - 1) / (subdiv - 1)) * (adv * 2) - adv
local sumx = 0
local sumy = 0
local mina = 0
local maxa = 1
for _, e in ipairs(n.eases) do
local a = e.a + a
mina = math.min(mina, a)
maxa = math.max(maxa, a)
sumx = sumx + mix(e.fromx, e.x, FISH_EASE(a))
sumy = sumy + mix(e.fromy, e.y, FISH_EASE(a))
end
table.insert(pos, sumx / #n.eases * sw)
table.insert(pos, sumy / #n.eases * sh)
table.insert(valid, not (maxa > 1 or mina < 0))
end
for i = 0, #pos/2 - 1 do
local x1 = pos[i * 2 + 1]
local y1 = pos[i * 2 + 1 + 1]
local x2 = pos[i * 2 + 2 + 1]
local y2 = pos[i * 2 + 3 + 1]
local valid = valid[i + 1]
if not x2 or not y2 then break end
love.graphics.setColor(0, 1, 0, 1)
if not valid then love.graphics.setColor(0, 0.5, 1, 0.7) end
love.graphics.line(x1, y1, x2, y2)
end
end
end
bench.stopBenchmark('render_fish')
bench.startBenchmark('render_food')
for _,f in ipairs(food) do
local sheet = sheets.food1
local x = f.x * sw
local y = f.y * sh
local frame = math.floor((f.time%1) * #sheet.quads) + 1
love.graphics.setColor(1, 1, 1, 1 - f.deathtimer)
love.graphics.draw(sheet.spriteSheet, sheet.quads[math.max(math.min(frame, #sheet.quads), 1)], x, y, 0, spritescale, spritescale, sheet.width/2, sheet.height/2)
end
bench.stopBenchmark('render_food')
bench.stopBenchmark('render_tank')
bench.startBenchmark('render_footer')
local base = sprites['footer/base']
local size = footerheight / FOOTER_HEIGHT
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(base, 0, 0, 0, size, size)
-- the game is making me do this. im sorry
local x = 19
for b = 1, 7 do
local hovered = mouseOverBox(x, 3, sprites['footer/buttonbg']:getWidth(), sprites['footer/buttonbg']:getHeight())
if hovered and love.mouse.isDown(1) then
love.graphics.draw(sprites['footer/buttonbg_down'], x, 3)
elseif hovered then
love.graphics.draw(sprites['footer/buttonbg_hover'], x, 3)
else
love.graphics.draw(sprites['footer/buttonbg'], x, 3)
end
-- insert sprite of item here
-- insert price of item here
love.graphics.setBlendMode('add')
love.graphics.draw(sprites['footer/reflection'], x, 3)
love.graphics.setBlendMode('alpha')
-- insert closing/opening animation here
local incr = 69 -- its like button positions but forcefully shoved into a recursive function :D
if b == 2 then incr = 57 end
if b >= 3 then incr = 73 end
x = x + incr
end
bench.stopBenchmark('render_footer')
love.graphics.setColor(1, 1, 1, 1)
love.graphics.print('FPS: ' .. 1 / love.timer.getDelta(), 0, sh - 16)
if debug then bench.renderBenchmark() end
bench.stopBenchmark('render')
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))
end
end
function love.keypressed(key)
if key == 'f3' then
debug = not debug
end
end

58
util.lua Normal file
View file

@ -0,0 +1,58 @@
function mix(x, y, a)
return x * (1 - a) + y * a
end
function clamp(a, x, y)
return math.min(math.max(a, x), y)
end
function round(x)
if x%1 >= 0.5 then return math.ceil(x) end
return math.floor(x)
end
function sum(tbl)
local sum = 0
for _,v in ipairs(tbl) do
sum = sum + v
end
return sum/#tbl
end
function shrt(num)
return math.floor(num * 10000) / 10000
end
function pointInside(px,py,x,y,w,h)
return px > x and px < x+w and py > y and py < y+h
end
function mouseOverBox(x,y,w,h)
return pointInside(love.mouse.getX(), love.mouse.getY(), x, y, w, h)
end
function stretchto(sprite, x, y, r, sx, sy, ox, oy)
love.graphics.draw(sprite, x, y, r, sx/sprite:getWidth(), sy/sprite:getHeight(), ox, oy)
end
function drawcenteredquad(sprite, quad, x, y, r, sx, sy, ox, oy)
local sw, sh = quad:getTextureDimensions()
love.graphics.draw(sprite, quad, x - (sw * (sx or 1))/2, y - (sh * (sy or 1))/2, r, sx, sy, (ox or 0) + sw/2, (oy or 0) + sh/2)
end
function drawcentered(sprite, x, y, r, sx, sy, ox, oy)
love.graphics.draw(sprite, x - (sprite:getWidth() * (sx or 1))/2, y - (sprite:getHeight() * (sy or 1))/2, r, sx, sy, (ox or 0) + sprite:getWidth()/2, (oy or 0) + sprite:getHeight()/2)
end
function table.deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[table.deepcopy(orig_key)] = table.deepcopy(orig_value)
end
setmetatable(copy, table.deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
function math.sign(a)
if a >= 0 then return 1 else return -1 end
end