diff --git a/main.lua b/main.lua index 32c5a22..d0fe3a2 100644 --- a/main.lua +++ b/main.lua @@ -9,6 +9,13 @@ ease = require 'lib.ease' bench = require 'lib.benchmark' tick = require 'lib.tick' +scenes = { + gameplay = require('scenes/gameplay/main') +} +local defaultscene = scenes.gameplay + +scene = defaultscene + sprites = {} sound_path = {} music_path = {} @@ -16,66 +23,7 @@ fonts = {} debug = false -food = {} -feesh = {} -money = {} - -local balance = 100 - -foodtier = 1 -foodcount = 1 - -headerbuttons = { - { - cost = 100, - sprite = 'guppy', - openanim = 0, - open = false, - closed = false, - func = function() - playSound('splash', 0.7, 0.8) - table.insert(feesh, constr.fish(math.random(), 0.3)) - end - }, - { - cost = 200, - sprite = 'food', - tier = foodtier, - openanim = 0, - open = false, - closed = false, - 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 = foodcount, - openanim = 0, - open = false, - closed = false, - func = function(self) - self.openanim = 0 - foodcount = foodcount + 1 - if foodcount >= 9 then - self.open = false - else - self.tier = foodcount - end - end - } -} - -local sheets = {} - -local function newAnimation(image, width, height) +function newAnimation(image, width, height) local animation = {} animation.spriteSheet = image; animation.quads = {}; @@ -91,623 +39,35 @@ local function newAnimation(image, width, height) 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()) - sheets.food2 = newAnimation(sprites['food/2'], sprites['food/2']:getWidth()/10, sprites['food/2']:getHeight()) - sheets.food3 = newAnimation(sprites['food/3'], sprites['food/3']:getWidth()/10, sprites['food/3']:getHeight()) - - sheets.buttonopen = newAnimation(sprites['header/button_open'], sprites['header/button_open']:getWidth()/3, sprites['header/button_open']:getHeight()) - - sheets.money1 = newAnimation(sprites['money/coin1'], sprites['money/coin1']:getWidth()/10, sprites['money/coin1']:getHeight()) - sheets.money2 = newAnimation(sprites['money/coin2'], sprites['money/coin2']:getWidth()/10, sprites['money/coin2']:getHeight()) - - for i = 1, 2 do - table.insert(feesh, constr.fish(math.random(), math.random())) - end - fonts.pix = love.graphics.newFont('assets/fonts/pix.ttf', 6) fonts.continuum = love.graphics.newFont('assets/fonts/cont.ttf', 14) fonts.default = love.graphics.newFont(12) + + if scene.load then scene.load() end end -local frame = 0 -local unfocusinterval = 5 +frame = 0 function love.update(dt) frame = frame + 1 bench.update() tick.update(dt) bench.startBenchmark('update') - bench.startBenchmark('update_buttons') - for _,btn in ipairs(headerbuttons) 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 - 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.deathtimer > 1 then - table.remove(food, i) - end - end - bench.stopBenchmark('update_food') - - bench.startBenchmark('update_money') - for i,f in ipairs(money) do - if not f.collected then - f.y = f.y + dt * f.speed - f.y = clamp(f.y, 0, 0.9) - end - f.time = f.time + dt - - if f.y == 0.9 and not f.collected then - f.deathtimer = f.deathtimer + dt - end - if f.collected then - f.collecttimer = f.collecttimer + dt - end - - if f.deathtimer > 1 or f.collecttimer > 1 then - table.remove(money, i) - end - end - bench.stopBenchmark('update_money') - - 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 - angle = mix(angle, math.deg(math.atan2((0.5 + math.sin(love.timer.getTime()/10 + fi) * 0.2) - n.y, 0)), 0.1) -- slightly head towards the middle, to prevent getting stuck at the bottom or top - - if n.eattimer <= 0 and not n.dead then -- needs to follow something - local mx, my - if n.eattimer <= 0 then - if n.shortestfood and food[n.shortestfood] then - local f = food[n.shortestfood] - mx, my = f.x, f.y - elseif frame % FISH_FOOD_CHECK_FREQ == 0 then - local minfood = 0 - local mindist = 9e9 - - for i,f in ipairs(food) do - local dist = math.sqrt(math.pow(math.abs(f.x - n.render.x), 2) + math.pow(math.abs(f.y - n.render.y), 2)) - if dist < mindist then - mindist = dist - minfood = i - end - end - - if minfood ~= 0 then - n.shortestfood = minfood - end - end - - end - if mx and my then - angle = math.deg(math.atan2(my - n.y, mx - n.x)) + math.random(-FISH_FOLLOW_RANDOM, FISH_FOLLOW_RANDOM) - str = math.random(70, 200)/200/8 - end - end - - local x = math.cos(math.rad(angle)) * str - local y = math.sin(math.rad(angle)) * str - - if not ((n.shortestfood and food[n.shortestfood]) or n.dead) 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 = (HEADER_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 - if n.eattimer < FISH_FOOD_HUNGRY or (n.shortestfood and food[n.shortestfood]) then - e.speed = e.speed * 1.3 - end - if n.dead then - e.speed = e.speed * 0.2 - end - 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 - n.moneytimer = n.moneytimer - dt - - if n.moneytimer < 0 and not n.dead then - n.moneytimer = n.moneytimer + math.random() * 5 + 5 - - if n.size > 0 then - local type = 2 - if n.size == 1 then type = 1 end - table.insert(money, constr.money(n.render.x, n.render.y, type)) - end - end - - 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) - bench.stopBenchmark('update_fish_position') - - bench.startBenchmark('update_fish_colission') - if n.shortestfood and food[n.shortestfood] and frame % FISH_COLISSION_CHECK_FREQ == 0 then - local f = food[n.shortestfood] - if f then - 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) - 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 and n.size == 0 then - n.size = 1 - playSound('grow') - - if not headerbuttons[1].open and not headerbuttons[1].closed then - headerbuttons[1].open = true - headerbuttons[1].openanim = 0 - end - end - if n.lifetime > FISH_AGE_BIG and n.size == 1 then - n.size = 2 - playSound('grow') - - if not headerbuttons[2].open and not headerbuttons[1].closed then - headerbuttons[2].open = true - headerbuttons[2].openanim = 0 - end - if not headerbuttons[3].open and not headerbuttons[1].closed then - headerbuttons[3].open = true - headerbuttons[3].openanim = 0 - end - end - end - end - end - bench.stopBenchmark('update_fish_colission') - - 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) * 250 + 0.5)) % 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 - - if not n.dead then - n.render.turndir = math.sign(n.render.x - n.render.prevx) - end - - 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') - - if n.eattimer <= FISH_FOOD_DEAD and not n.dead then - playSound('die') - end - if n.eattimer <= FISH_FOOD_DEAD then - n.dead = true - - local timeSinceDead = math.abs(n.eattimer - FISH_FOOD_DEAD) - n.render.y = n.render.y + timeSinceDead/5 * math.min(timeSinceDead, 1) - n.render.y = clamp(n.render.y, 0, 0.85) - if n.render.y == 0.85 then - n.render.deathanim = n.render.deathanim + dt - if n.render.deathanim > 1 then - table.remove(feesh, fi) - end - end - end - end - bench.stopBenchmark('update_fish') + if scene.update then scene.update(dt) end bench.stopBenchmark('update') end function love.draw() love.graphics.setFont(fonts.default) + love.graphics.setColor(1, 1, 1) + local sw, sh = love.graphics.getDimensions() bench.startBenchmark('render') - bench.startBenchmark('render_tank') - love.graphics.setColor(1, 1, 1) - - local sw, sh = love.graphics.getDimensions() - local headerheight = HEADER_HEIGHT * sw/640 - local yscale = (sh-headerheight)/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, headerheight - HEADER_HEIGHT, 0, sw, sh - (headerheight - HEADER_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, headerheight + 20, 0, wavescale * sizex, wavescale, sprites['wave/wavecenter']:getWidth()/2) - end - love.graphics.setBlendMode('alpha') - bench.stopBenchmark('render_wave') - - bench.startBenchmark('render_food') - for _,f in ipairs(food) do - 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 - - 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') - - -- shadow - bench.startBenchmark('render_shadow') - for i, n in ipairs(feesh) do - love.graphics.setColor(1, 1, 1, n.render.y + 0.2 - n.render.deathanim) - local sizes = {0.55, 0.7, 1} - local size = sizes[n.size + 1] or 1 - love.graphics.draw(sprites['shadow'], n.render.x * sw - (sprites['shadow']:getWidth() * spritescale * size)/2, sh - sh * 0.18 - (sprites['shadow']:getHeight() * spritescale * size)/2 + n.render.y * sh * 0.08, 0, spritescale * size, spritescale * size) - 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 == turnframe then - sizex = -1 * sizex - turnframe = 1 - end - - local frame = math.floor(n.render.swim * (#sample.quads - 1)) + 1 - - if turnframe ~= 1 and turnframe ~= #sample.quads 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 - - if n.dead then - anim = 'die' - local a = math.min(math.abs(n.eattimer - FISH_FOOD_DEAD) * 1.4, 1) - n.render.deathanim * 0.4 - frame = math.floor(a * (#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 - n.render.deathanim) - 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 - n.render.deathanim) - 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.stopBenchmark('render_tank') - - bench.startBenchmark('render_header') - local base = sprites['header/base'] - local size = headerheight / HEADER_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 - local y = 3 - for b = 1, 7 do - local hovered = mouseOverBox(x * size, y * size, sprites['header/buttonbg']:getWidth() * size, sprites['header/buttonbg']:getHeight() * size) - local btn = headerbuttons[b] - - if (btn and not btn.open) or not btn then - -- draw nothing - elseif hovered and love.mouse.isDown(1) then - love.graphics.draw(sprites['header/buttonbg_down'], x * size, y * size, 0, size, size) - elseif hovered then - love.graphics.draw(sprites['header/buttonbg_hover'], x * size, y * size, 0, size, size) - else - love.graphics.draw(sprites['header/buttonbg'], x * size, y * size, 0, size, size) - end - - 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['header/buttonbg']:getWidth() / sheet.width) * 0.9 - local offset = (sprites['header/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['header/buttonbg']:getWidth() / sheet.width) * 0.65 - local offset = (sprites['header/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['header/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['header/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['header/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.cost, round(x * size), round(y * size + 51 * size - font:getHeight()/2), round(sprites['header/buttonbg']:getWidth() * size), 'center') - love.graphics.setColor(1, 1, 1) - love.graphics.setFont(fonts.default) - - -- reflection - love.graphics.setBlendMode('add') - love.graphics.draw(sprites['header/reflection'], x * size, y * size, 0, size, size) - love.graphics.setBlendMode('alpha') - end - - -- open/close anim - if (btn and btn.openanim < 1) then - local sheet = sheets.buttonopen - local anim = 0 - if btn then anim = btn.openanim end - local frame = math.floor(anim % 1 * #sheet.quads) + 1 - love.graphics.draw(sheet.spriteSheet, sheet.quads[frame], x * size, y * size, 0, size, size) - end - end - - 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 - - -- money count - love.graphics.setFont(fonts.continuum) - love.graphics.setColor(179/255, 254/255, 89/255) - local leftpad = 100 - love.graphics.printf(balance, round(sw * 0.965 - leftpad), round(HEADER_HEIGHT - 25), leftpad, 'right') - love.graphics.setFont(fonts.default) - - bench.stopBenchmark('render_header') - - bench.startBenchmark('render_money') - for _,f in ipairs(money) do - local sheet = sheets['money' .. (f.type)] - local x = mix(f.x * sw, sw / 9 * 8, ease.outCubic(f.collecttimer)) - local y = mix(f.y * sh, HEADER_HEIGHT - 20, ease.outCubic(f.collecttimer)) - 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_money') + if scene.draw then scene.draw() end love.graphics.setColor(1, 1, 1, 1) love.graphics.print('FPS: ' .. 1 / love.timer.getDelta(), 0, sh - 16) @@ -717,62 +77,11 @@ function love.draw() end function love.mousepressed(x, y, b) - if b == 1 then - for _,m in ipairs(money) do - local dist = math.abs(x/love.graphics.getWidth() - m.x) + math.abs(y/love.graphics.getHeight() - m.y) - if dist < 0.1 and not m.collected then - m.collected = true - m.deathtimer = 0 - playSound('collect', 1, 1 + math.random() * 0.2 - 0.1) - - if m.type == 1 then balance = balance + 15 end - if m.type == 2 then balance = balance + 35 end - - return - end - end - end - - if b == 1 and y > HEADER_HEIGHT and #food < foodcount then - if balance >= 5 then - table.insert(food, constr.food(x/love.graphics.getWidth(), y/love.graphics.getHeight(), foodtier)) - playSound('dropfood') - balance = balance - 5 - else - playSound('buzzer') - end - end - - local headerheight = HEADER_HEIGHT * love.graphics.getWidth()/640 - local size = headerheight / HEADER_HEIGHT - - if b == 1 then - local x = 19 - for i = 1, 7 do - local hovered = mouseOverBox(x * size, 3 * size, sprites['header/buttonbg']:getWidth() * size, sprites['header/buttonbg']:getHeight() * size) - - if hovered then - if headerbuttons[i] and headerbuttons[i].open then - if balance >= headerbuttons[i].cost then - headerbuttons[i].func(headerbuttons[i]) - playSound('buttonclick') - balance = balance - headerbuttons[i].cost - else - playSound('buzzer') - end - end - end - - 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 - end + if scene.mousepressed then scene.mousepressed(x, y, b) end end function love.mousereleased(x, y, b) - + if scene.mousereleased then scene.mousereleased(x, y, b) end end function love.keypressed(key) diff --git a/scenes/gameplay/draw/fish.lua b/scenes/gameplay/draw/fish.lua new file mode 100644 index 0000000..0fa4f89 --- /dev/null +++ b/scenes/gameplay/draw/fish.lua @@ -0,0 +1,124 @@ +return function(feesh, spritescale, fishsprite) + local sw, sh = love.graphics.getDimensions() + 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 == turnframe then + sizex = -1 * sizex + turnframe = 1 + end + + local frame = math.floor(n.render.swim * (#sample.quads - 1)) + 1 + + if turnframe ~= 1 and turnframe ~= #sample.quads 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 + + if n.dead then + anim = 'die' + local a = math.min(math.abs(n.eattimer - FISH_FOOD_DEAD) * 1.4, 1) - n.render.deathanim * 0.4 + frame = math.floor(a * (#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 - n.render.deathanim) + 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 - n.render.deathanim) + 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 +end \ No newline at end of file diff --git a/scenes/gameplay/draw/food.lua b/scenes/gameplay/draw/food.lua new file mode 100644 index 0000000..99d0a2e --- /dev/null +++ b/scenes/gameplay/draw/food.lua @@ -0,0 +1,13 @@ +return function(food, sheets, spritescale) + local sw, sh = love.graphics.getDimensions() + for _,f in ipairs(food) do + 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 + + 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 +end \ No newline at end of file diff --git a/scenes/gameplay/draw/header.lua b/scenes/gameplay/draw/header.lua new file mode 100644 index 0000000..0f9dea1 --- /dev/null +++ b/scenes/gameplay/draw/header.lua @@ -0,0 +1,91 @@ +return function(headerheight, fishsprite, headerbuttons, sheets, balance) + local sw, sh = love.graphics.getDimensions() + local base = sprites['header/base'] + local size = headerheight / HEADER_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 + local y = 3 + for b = 1, 7 do + local hovered = mouseOverBox(x * size, y * size, sprites['header/buttonbg']:getWidth() * size, sprites['header/buttonbg']:getHeight() * size) + local btn = headerbuttons[b] + + if (btn and not btn.open) or not btn then + -- draw nothing + elseif hovered and love.mouse.isDown(1) then + love.graphics.draw(sprites['header/buttonbg_down'], x * size, y * size, 0, size, size) + elseif hovered then + love.graphics.draw(sprites['header/buttonbg_hover'], x * size, y * size, 0, size, size) + else + love.graphics.draw(sprites['header/buttonbg'], x * size, y * size, 0, size, size) + end + + 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['header/buttonbg']:getWidth() / sheet.width) * 0.9 + local offset = (sprites['header/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['header/buttonbg']:getWidth() / sheet.width) * 0.65 + local offset = (sprites['header/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['header/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['header/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['header/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.cost, round(x * size), round(y * size + 51 * size - font:getHeight()/2), round(sprites['header/buttonbg']:getWidth() * size), 'center') + love.graphics.setColor(1, 1, 1) + love.graphics.setFont(fonts.default) + + -- reflection + love.graphics.setBlendMode('add') + love.graphics.draw(sprites['header/reflection'], x * size, y * size, 0, size, size) + love.graphics.setBlendMode('alpha') + end + + -- open/close anim + if (btn and btn.openanim < 1) then + local sheet = sheets.buttonopen + local anim = 0 + if btn then anim = btn.openanim end + local frame = math.floor(anim % 1 * #sheet.quads) + 1 + love.graphics.draw(sheet.spriteSheet, sheet.quads[frame], x * size, y * size, 0, size, size) + end + end + + 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 + + -- money count + love.graphics.setFont(fonts.continuum) + love.graphics.setColor(179/255, 254/255, 89/255) + local leftpad = 100 + love.graphics.printf(balance, round(sw * 0.965 - leftpad), round(HEADER_HEIGHT - 25), leftpad, 'right') + love.graphics.setFont(fonts.default) +end \ No newline at end of file diff --git a/scenes/gameplay/draw/money.lua b/scenes/gameplay/draw/money.lua new file mode 100644 index 0000000..17ee213 --- /dev/null +++ b/scenes/gameplay/draw/money.lua @@ -0,0 +1,12 @@ +return function(money, sheets, spritescale) + local sw, sh = love.graphics.getDimensions() + for _,f in ipairs(money) do + local sheet = sheets['money' .. (f.type)] + local x = mix(f.x * sw, sw / 9 * 8, ease.outCubic(f.collecttimer)) + local y = mix(f.y * sh, HEADER_HEIGHT - 20, ease.outCubic(f.collecttimer)) + 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 +end \ No newline at end of file diff --git a/scenes/gameplay/draw/shadow.lua b/scenes/gameplay/draw/shadow.lua new file mode 100644 index 0000000..3b7a276 --- /dev/null +++ b/scenes/gameplay/draw/shadow.lua @@ -0,0 +1,9 @@ +return function(feesh, spritescale) + local sw, sh = love.graphics.getDimensions() + for i, n in ipairs(feesh) do + love.graphics.setColor(1, 1, 1, n.render.y + 0.2 - n.render.deathanim) + local sizes = {0.55, 0.7, 1} + local size = sizes[n.size + 1] or 1 + love.graphics.draw(sprites['shadow'], n.render.x * sw - (sprites['shadow']:getWidth() * spritescale * size)/2, sh - sh * 0.18 - (sprites['shadow']:getHeight() * spritescale * size)/2 + n.render.y * sh * 0.08, 0, spritescale * size, spritescale * size) + end +end \ No newline at end of file diff --git a/scenes/gameplay/draw/wave.lua b/scenes/gameplay/draw/wave.lua new file mode 100644 index 0000000..f78566f --- /dev/null +++ b/scenes/gameplay/draw/wave.lua @@ -0,0 +1,22 @@ +return function(headerheight, sheets) + local sw, sh = love.graphics.getDimensions() + 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, headerheight + 20, 0, wavescale * sizex, wavescale, sprites['wave/wavecenter']:getWidth()/2) + end + love.graphics.setBlendMode('alpha') +end \ No newline at end of file diff --git a/scenes/gameplay/main.lua b/scenes/gameplay/main.lua new file mode 100644 index 0000000..0d097cc --- /dev/null +++ b/scenes/gameplay/main.lua @@ -0,0 +1,207 @@ +local self = {} + +local sheets = {} + +local food = {} +local feesh = {} +local money = {} + +local balance = 100 + +local foodtier = 1 +local foodcount = 1 + +local headerbuttons = { + { + cost = 100, + sprite = 'guppy', + openanim = 1, + open = false, + closed = false, + func = function() + playSound('splash', 0.7, 0.8) + table.insert(feesh, constr.fish(math.random(), 0.3)) + end + }, + { + cost = 200, + sprite = 'food', + tier = foodtier, + openanim = 1, + open = false, + closed = false, + 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 = foodcount, + openanim = 1, + open = false, + closed = false, + func = function(self) + self.openanim = 0 + foodcount = foodcount + 1 + if foodcount >= 9 then + self.open = false + else + self.tier = foodcount + end + end + } +} + +local sheets = {} + +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 self.load() + 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()) + sheets.food2 = newAnimation(sprites['food/2'], sprites['food/2']:getWidth()/10, sprites['food/2']:getHeight()) + sheets.food3 = newAnimation(sprites['food/3'], sprites['food/3']:getWidth()/10, sprites['food/3']:getHeight()) + + sheets.buttonopen = newAnimation(sprites['header/button_open'], sprites['header/button_open']:getWidth()/3, sprites['header/button_open']:getHeight()) + + sheets.money1 = newAnimation(sprites['money/coin1'], sprites['money/coin1']:getWidth()/10, sprites['money/coin1']:getHeight()) + sheets.money2 = newAnimation(sprites['money/coin2'], sprites['money/coin2']:getWidth()/10, sprites['money/coin2']:getHeight()) + + for i = 1, 2 do + table.insert(feesh, constr.fish(math.random(), math.random())) + end +end + +function self.update(dt) + bench.startBenchmark('update_buttons') + for _,btn in ipairs(headerbuttons) do + btn.openanim = btn.openanim + dt * 6 + end + bench.stopBenchmark('update_buttons') + + bench.startBenchmark('update_food') + require('scenes.gameplay.update.food')(food, dt) + bench.stopBenchmark('update_food') + + bench.startBenchmark('update_money') + require('scenes.gameplay.update.money')(money, dt) + bench.stopBenchmark('update_money') + + bench.startBenchmark('update_fish') + require('scenes.gameplay.update.fish')(feesh, dt, food) + bench.stopBenchmark('update_fish') +end + +function self.draw() + bench.startBenchmark('render_tank') + + local sw, sh = love.graphics.getDimensions() + local headerheight = HEADER_HEIGHT * sw/640 + local yscale = (sh-headerheight)/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, headerheight - HEADER_HEIGHT, 0, sw, sh - (headerheight - HEADER_HEIGHT)) + + -- waves + bench.startBenchmark('render_wave') + require('scenes.gameplay.draw.wave')(headerheight, sheets) + bench.stopBenchmark('render_wave') + + -- shadow + bench.startBenchmark('render_shadow') + require('scenes.gameplay.draw.shadow')(feesh, spritescale) + bench.stopBenchmark('render_shadow') + + bench.startBenchmark('render_food') + require('scenes.gameplay.draw.food')(food, sheets, spritescale) + bench.stopBenchmark('render_food') + + -- all the fish + bench.startBenchmark('render_fish') + require('scenes.gameplay.draw.fish')(feesh, spritescale, fishsprite) + bench.stopBenchmark('render_fish') + bench.stopBenchmark('render_tank') + + bench.startBenchmark('render_header') + require('scenes.gameplay.draw.header')(headerheight, fishsprite, headerbuttons, sheets, balance) + bench.stopBenchmark('render_header') + + bench.startBenchmark('render_money') + require('scenes.gameplay.draw.money')(money, sheets, spritescale) + bench.stopBenchmark('render_money') +end + +function self.mousepressed(x, y, b) + if b == 1 then + for _,m in ipairs(money) do + local dist = math.abs(x/love.graphics.getWidth() - m.x) + math.abs(y/love.graphics.getHeight() - m.y) + if dist < 0.1 and not m.collected then + m.collected = true + m.deathtimer = 0 + playSound('collect', 1, 1 + math.random() * 0.2 - 0.1) + + if m.type == 1 then balance = balance + 15 end + if m.type == 2 then balance = balance + 35 end + + return + end + end + end + + if b == 1 and y > HEADER_HEIGHT and #food < foodcount then + if balance >= 5 then + table.insert(food, constr.food(x/love.graphics.getWidth(), y/love.graphics.getHeight(), foodtier)) + playSound('dropfood') + balance = balance - 5 + else + playSound('buzzer') + end + end + + local headerheight = HEADER_HEIGHT * love.graphics.getWidth()/640 + local size = headerheight / HEADER_HEIGHT + + if b == 1 then + local x = 19 + for i = 1, 7 do + local hovered = mouseOverBox(x * size, 3 * size, sprites['header/buttonbg']:getWidth() * size, sprites['header/buttonbg']:getHeight() * size) + + if hovered then + if headerbuttons[i] and headerbuttons[i].open then + if balance >= headerbuttons[i].cost then + headerbuttons[i].func(headerbuttons[i]) + playSound('buttonclick') + balance = balance - headerbuttons[i].cost + else + playSound('buzzer') + end + end + end + + 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 + end +end + +return self \ No newline at end of file diff --git a/scenes/gameplay/update/fish.lua b/scenes/gameplay/update/fish.lua new file mode 100644 index 0000000..995327d --- /dev/null +++ b/scenes/gameplay/update/fish.lua @@ -0,0 +1,239 @@ +return function(feesh, dt, food) + 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 + angle = mix(angle, math.deg(math.atan2((0.5 + math.sin(love.timer.getTime()/10 + fi) * 0.2) - n.y, 0)), 0.1) -- slightly head towards the middle, to prevent getting stuck at the bottom or top + + if n.eattimer <= 0 and not n.dead then -- needs to follow something + local mx, my + if n.eattimer <= 0 then + if n.shortestfood and food[n.shortestfood] then + local f = food[n.shortestfood] + mx, my = f.x, f.y + elseif frame % FISH_FOOD_CHECK_FREQ == 0 then + local minfood = 0 + local mindist = 9e9 + + for i,f in ipairs(food) do + local dist = math.sqrt(math.pow(math.abs(f.x - n.render.x), 2) + math.pow(math.abs(f.y - n.render.y), 2)) + if dist < mindist then + mindist = dist + minfood = i + end + end + + if minfood ~= 0 then + n.shortestfood = minfood + end + end + + end + if mx and my then + angle = math.deg(math.atan2(my - n.y, mx - n.x)) + math.random(-FISH_FOLLOW_RANDOM, FISH_FOLLOW_RANDOM) + str = math.random(70, 200)/200/8 + end + end + + local x = math.cos(math.rad(angle)) * str + local y = math.sin(math.rad(angle)) * str + + if not ((n.shortestfood and food[n.shortestfood]) or n.dead) 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 = (HEADER_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 + if n.eattimer < FISH_FOOD_HUNGRY or (n.shortestfood and food[n.shortestfood]) then + e.speed = e.speed * 1.3 + end + if n.dead then + e.speed = e.speed * 0.2 + end + 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 + n.moneytimer = n.moneytimer - dt + + if n.moneytimer < 0 and not n.dead then + n.moneytimer = n.moneytimer + math.random() * 5 + 5 + + if n.size > 0 then + local type = 2 + if n.size == 1 then type = 1 end + table.insert(money, constr.money(n.render.x, n.render.y, type)) + end + end + + 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) + bench.stopBenchmark('update_fish_position') + + bench.startBenchmark('update_fish_colission') + if n.shortestfood and food[n.shortestfood] and frame % FISH_COLISSION_CHECK_FREQ == 0 then + local f = food[n.shortestfood] + if f then + 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) + 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 and n.size == 0 then + n.size = 1 + playSound('grow') + + if not headerbuttons[1].open and not headerbuttons[1].closed then + headerbuttons[1].open = true + headerbuttons[1].openanim = 0 + end + end + if n.lifetime > FISH_AGE_BIG and n.size == 1 then + n.size = 2 + playSound('grow') + + if not headerbuttons[2].open and not headerbuttons[1].closed then + headerbuttons[2].open = true + headerbuttons[2].openanim = 0 + end + if not headerbuttons[3].open and not headerbuttons[1].closed then + headerbuttons[3].open = true + headerbuttons[3].openanim = 0 + end + end + end + end + end + bench.stopBenchmark('update_fish_colission') + + 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) * 250 + 0.5)) % 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 + + if not n.dead then + n.render.turndir = math.sign(n.render.x - n.render.prevx) + end + + 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') + + if n.eattimer <= FISH_FOOD_DEAD and not n.dead then + playSound('die') + end + if n.eattimer <= FISH_FOOD_DEAD then + n.dead = true + + local timeSinceDead = math.abs(n.eattimer - FISH_FOOD_DEAD) + n.render.y = n.render.y + timeSinceDead/5 * math.min(timeSinceDead, 1) + n.render.y = clamp(n.render.y, 0, 0.85) + if n.render.y == 0.85 then + n.render.deathanim = n.render.deathanim + dt + if n.render.deathanim > 1 then + table.remove(feesh, fi) + end + end + end + end +end \ No newline at end of file diff --git a/scenes/gameplay/update/food.lua b/scenes/gameplay/update/food.lua new file mode 100644 index 0000000..5b5c293 --- /dev/null +++ b/scenes/gameplay/update/food.lua @@ -0,0 +1,15 @@ +return function(food, dt) + 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.deathtimer > 1 then + table.remove(food, i) + end + end +end \ No newline at end of file diff --git a/scenes/gameplay/update/money.lua b/scenes/gameplay/update/money.lua new file mode 100644 index 0000000..1220fb1 --- /dev/null +++ b/scenes/gameplay/update/money.lua @@ -0,0 +1,20 @@ +return function(money, dt) + for i,f in ipairs(money) do + if not f.collected then + f.y = f.y + dt * f.speed + f.y = clamp(f.y, 0, 0.9) + end + f.time = f.time + dt + + if f.y == 0.9 and not f.collected then + f.deathtimer = f.deathtimer + dt + end + if f.collected then + f.collecttimer = f.collecttimer + dt + end + + if f.deathtimer > 1 or f.collecttimer > 1 then + table.remove(money, i) + end + end +end \ No newline at end of file