diff --git a/README.md b/README.md index 030fc7a..691ffaf 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,8 @@ This 2D game was made by [Rasmus Nordling](https://github.com/happystinson) and ## Dependencies - Lua 5.1 -- [LÖVE](https://www.love2d.org/) 11.1 +- [LÖVE](https://www.love2d.org/) 11.4 ## Downloads -Check the [releases](https://github.com/HappyStinson/downhill-willy/releases) tab for this repo or view the game at [itch.io](https://rasmusnordling.itch.io/downhill-willy). \ No newline at end of file +Check the [releases](https://github.com/HappyStinson/downhill-willy/releases) tab for this repo or view the game at [itch.io](https://rasmusnordling.itch.io/downhill-willy). diff --git a/conf.lua b/conf.lua index 2565774..f7ba026 100644 --- a/conf.lua +++ b/conf.lua @@ -3,7 +3,7 @@ function love.conf(t) t.title = "Downhill Willy - BOSS Jam 2014" t.window.icon = "assets/logo.png" - t.version = "11.1" + t.version = "11.4" t.window.width = 1280 t.window.height = 720 end diff --git a/level.lua b/level.lua deleted file mode 100644 index 6ba621f..0000000 --- a/level.lua +++ /dev/null @@ -1,394 +0,0 @@ --- This file describes our level - -level = {} -require 'whale' -require 'constants' -local audio = require 'audio' -local colors = require 'colors' - -local function loadImages() - img_fn = {"bg_forest", "bg_mnt1", "bg_mnt2", "fg_snow", "ui_hiscore", "lanes", "logo", "obj_log", "obj_snowman", "obj_stone", "obj_tree", "player_idle", "player_run1", "player_run2", "ui_score", "sky", "vall"} - images = {} - for _, v in ipairs(img_fn) do - images[v] = love.graphics.newImage("assets/"..v..".png") - end - - -- Create a quad for the background - mapWidth = images.bg_mnt1:getWidth() * 2 - mountainQuad = love.graphics.newQuad(0, 0, mapWidth, 704, 1547, 704) - forestQuad = love.graphics.newQuad(0, 0, mapWidth, 423, 1547, 423) - images.bg_mnt1:setWrap("repeat") - images.bg_mnt2:setWrap("repeat") - images.bg_forest:setWrap("repeat") -end - -local function initFont() - fonts = { - score = love.graphics.newFont("assets/font_score.otf", 22), - high_score = love.graphics.newFont("assets/font_hiscore.otf", 27), - instructions = love.graphics.newFont("assets/font_score.otf", 27), - } -end - -local function initLanes() - level.lanes = { - laneWidth = GAME_WIDTH, - laneHeight = 20, - laneLayers = 3, - laneY = 50 - } -end - -function level.load() - loadImages() - initFont() - initLanes() - level.objects = {} - - isRunning = false - - time = 0 - speed = 10 - - offsets = { - obj_log = {112, 47, 50}, - obj_snowman = {38, 105, 100}, - obj_stone = {33, 53, 55}, - obj_tree = {45, 107, 80} - } - - bgOffsets = { - mnt1 = 0, - mnt2 = 0, - forest = 0 - } - - laneYPos = {330, 250, 175} - - -- Initialize audio - audioSources = { - idle = audio.streamLooped("yodel_idle"), - yodel_intro = audio.stream("yodel_intro"), - yodel_loop = audio.streamLooped("yodel_loop") - } - audioSources.idle:play() - - soundEffects = { - fanfare = audio.soundEffect("fanfare"), - obj_log = audio.soundEffect("crash-log"), - obj_snowman = audio.soundEffect("crash-snowman"), - obj_stone = audio.soundEffect("crash-stone"), - obj_tree = audio.soundEffect("crash-tree") - } - - -- Keep track of current and best score - score = 0 - hiscore = 0 - playerGotNewHighScore = false - - whale.load() -end - -local function updateBackground(dt) - bgOffsets.mnt1 = bgOffsets.mnt1 + dt * 5 * speed - bgOffsets.mnt2 = bgOffsets.mnt2 + dt * 10 * speed - bgOffsets.forest = bgOffsets.forest + dt * 40 * speed - - if bgOffsets.mnt1 >= (mapWidth / 2) then - bgOffsets.mnt1 = 0 - end - if bgOffsets.mnt2 >= (mapWidth / 2) then - bgOffsets.mnt2 = 0 - end - if bgOffsets.forest >= (mapWidth / 2) then - bgOffsets.forest = 0 - end -end - -local function spawnRandomObject() - -- Randomize object type and lane - lane = love.math.random(1, 3) - objType = love.math.random(1, 100) - local object = {} - object.lane = lane - - if lane == 2 or lane == 3 then - if objType <= 10 then - object.ID = "obj_log" - elseif objType <= 30 then - object.ID = "obj_snowman" - elseif objType <= 60 then - object.ID = "obj_stone" - else - object.ID = "obj_tree" - end - else - if objType <= 20 then - object.ID = "obj_snowman" - elseif objType <= 60 then - object.ID = "obj_stone" - else - object.ID = "obj_tree" - end - end - - object.x = 1400 - object.y = getY(2, 1400) - table.insert(level.objects, object) -end - -local function leaveRunningState() - isRunning = false - time = 0 - - -- Check if player got new high score - if score == hiscore then - playerGotNewHighScore = true - soundEffects.fanfare:play() - end - - score = 0 -end - -local function checkCollision() - -- Only check collision with objects on same lane - for _, v in ipairs(level.objects) do - if v.lane == whale.lane or ((v.ID == "obj_log") and (v.lane == (whale.lane + 1))) then - distance = math.abs(whale.x - v.x) - - if ((v.ID == "obj_log") and (v.lane == (whale.lane + 1))) and (distance < (offsets[v.ID][3])) or - distance < (offsets[v.ID][3] / 2) then - soundEffects[v.ID]:play() - leaveRunningState() - end - end - end -end - -local function removeObjects() - -- Remove all objects that have left the screen - for _, v in ipairs(level.objects) do - if v.x < -100 then - table.remove(level.objects, _) - end - end -end - -function level.update(dt) - if not isRunning then - audioSources.yodel_intro:stop() - audioSources.yodel_loop:stop() - audioSources.yodel_loop:setPitch(1.0) - audioSources.idle:play() - end - - if isRunning == true then - isPaused = false - - -- Speed changes with time - -- Better to only update speed if less than 30 ... to avoid a lot of heavy calculation. - time = time + dt - speed = 10 + time / 15 - if speed >= 30 then - speed = 30 - end - audioSources.yodel_loop:setPitch(1 + (speed - 10) / 200) -- 1 -> 1.1 - - score = score + (speed / 2) * dt - if score > hiscore then - hiscore = score - end - - updateBackground(dt) - - -- Spawn new objects - if love.math.random(1, 100) <= 1 then - spawnRandomObject() - end - - -- Update object positions - for _, v in ipairs(level.objects) do - v.x = v.x - dt * 100 * speed - v.y = laneYPos[v.lane] + 0.335 * v.x - end - - -- Update whale position - whale.update(dt) - - if time > 2 then - checkCollision() - end - - removeObjects() - - if (not audioSources.yodel_intro:isPlaying()) and isRunning == true then - audioSources.yodel_loop:play() - end - end -end - -local function drawBackground() - -- Draw the beautiful sky - love.graphics.draw(images.sky, 0, 0) - love.graphics.draw(images.bg_mnt1, mountainQuad, 0, 0, 0, 1, 1, bgOffsets.mnt1, 0) - love.graphics.draw(images.bg_mnt2, mountainQuad, 0, 0, 0, 1, 1, bgOffsets.mnt2, 0) - love.graphics.draw(images.bg_forest, forestQuad, 0, 191, 0, 1, 1, bgOffsets.forest, 0) -end - -local function drawImage(image, x, y) - love.graphics.draw(image, x, y) -end - -local function drawForeground() - -- Draw at bottom left - drawImage(images.fg_snow, 0, 272) -end - -local function drawLanes() - drawImage(images.lanes, 0, 130) - drawImage(images.vall, 0, 105) - drawImage(images.vall, 0, 345) -end - -local function drawObjects() - table.sort(level.objects, function(a, b) return a.lane > b.lane end) - playerDrawn = true - - for _, v in ipairs(level.objects) do - if v.lane == 3 then - love.graphics.draw(images[v.ID], v.x, v.y, 0, 1, 1, offsets[v.ID][1], offsets[v.ID][2]) - end - if (whale.lane == 3) and isRunning then - whale.playAnimation() - playerDrawn = false - end - end - for _, v in ipairs(level.objects) do - if v.lane == 2 then - love.graphics.draw(images[v.ID], v.x, v.y, 0, 1, 1, offsets[v.ID][1], offsets[v.ID][2]) - end - if whale.lane == 2 and isRunning then - whale.playAnimation() - playerDrawn = false - end - end - for _, v in ipairs(level.objects) do - if v.lane == 1 then - love.graphics.draw(images[v.ID], v.x, v.y, 0, 1, 1, offsets[v.ID][1], offsets[v.ID][2]) - end - if whale.lane == 1 and isRunning then - whale.playAnimation() - playerDrawn = false - end - end -end - -local function setColor(color, alpha) - if alpha then - local color_table = colors[color] - table.insert(color_table, alpha) - love.graphics.setColor(color_table) - else - love.graphics.setColor(colors[color]) - end -end - -local function drawGUI(controls) - local center = { - x = GAME_WIDTH / 2, - y = GAME_HEIGHT / 2 - } - - -- Score and hiscore - local limit = 200 -- Wrap the line after this many horizontal pixels - drawImage(images.ui_score, center.x - (images.ui_score:getWidth() / 2), 0) - drawImage(images.ui_hiscore, 1280 - images.ui_hiscore:getWidth(), 100) - drawImage(images.logo, 50, GAME_HEIGHT - images.logo:getHeight() * 1.3) - - local score_text = string.format("%.0f M", score) - love.graphics.setFont(fonts.score) - love.graphics.printf(score_text, center.x - 135, 53, limit, "right") - - setColor("black") - score_text = string.format("%.0f M", hiscore) - love.graphics.setFont(fonts.high_score) - love.graphics.printf(score_text, 1075, 145, limit, "right") - setColor("white") - - if not isRunning then - if playerGotNewHighScore then - setColor("black") - love.graphics.printf("New High Score! " .. score_text, 0, center.y, GAME_WIDTH, "center") - end - - -- Show info centered on the screen - local instructions = { - string.format("Press %s to start skiing", string.upper(controls.start)), - string.format("%s toggles fullscreen", string.upper(controls.toggle_fullscreen)), - string.format("%s quits the game", string.upper(controls.quit)) - } - - local font_height = fonts.instructions:getHeight() - - setColor("light-blue accent-4", .3) - love.graphics.rectangle("fill", 0, center.y, GAME_WIDTH, font_height * (#instructions + 2)) - setColor("white") - love.graphics.setFont(fonts.instructions) - - for i = 1, #instructions do - love.graphics.printf(instructions[i], 0, center.y + (font_height * i), GAME_WIDTH, "center") - end - end -end - -function level.draw(controls) - drawBackground() - drawForeground() - drawLanes() - drawObjects() - - -- Draw whale according to game running state - if not isRunning then - love.graphics.draw(images.player_idle, whale.x, whale.y, 0, 1, 1, whale.offsetX, whale.offsetY) - else - if (#level.objects == 0) and isRunning then - whale.playAnimation() - end - end - - -- Draw user interface - drawGUI(controls) -end - -local function toggleFullscreen() - local isFullscreen = not love.window.getFullscreen() - love.window.setFullscreen(isFullscreen, "desktop") -end - -local function toggleMouseVisibility() - local state = not love.mouse.isVisible() - love.mouse.setVisible(state) -end - -local function startGame() - isRunning = true - playerGotNewHighScore = false - audioSources.idle:stop() - if not audioSources.yodel_loop:isPlaying() then - audioSources.yodel_intro:play() - end -end - -function level.keypressed(key, controls) - if not isRunning then - if key == controls.toggle_fullscreen then - toggleFullscreen() - toggleMouseVisibility() - end - if key == controls.start then - startGame() - end - else - whale.keypressed(key) - end -end \ No newline at end of file diff --git a/main.lua b/main.lua index 3b7d586..2168499 100644 --- a/main.lua +++ b/main.lua @@ -1,51 +1,10 @@ --- just a comment --- This game requires level.lua -require('level') -require('constants') +require 'src.level' +Graphics = require 'src.graphics' +Input = require 'src.input' function love.load() - love.mouse.setVisible(false) - gameCanvas = love.graphics.newCanvas(GAME_WIDTH, GAME_HEIGHT) + Graphics:createWindow(true) + Input:initJoystick() level.load() - -- Start the game in fullscreen - love.window.setFullscreen(true, "desktop") -end - -function love.update(dt) - level.update(dt) -end - -function love.draw() - love.graphics.setCanvas(gameCanvas) - level.draw(controls) - love.graphics.setCanvas() - local scale = getScale() - love.graphics.draw(gameCanvas, getMarginX(scale), getMarginY(scale), 0, scale, scale) -end - -function getScale() - local scaleX = love.graphics.getWidth() / GAME_WIDTH - local scaleY = love.graphics.getHeight() / GAME_HEIGHT - if scaleY < 1 and scaleY < scaleX then - return scaleY - else - return scaleX - end -end - -function getMarginY(scale) - return (love.graphics.getHeight() - GAME_HEIGHT * scale) / 2 -end - -function getMarginX(scale) - return (love.graphics.getWidth() - GAME_WIDTH * scale) / 2 -end - -function love.keypressed(key) - if key == controls.quit then - love.event.quit() - end - - level.keypressed(key, controls) end \ No newline at end of file diff --git a/audio.lua b/src/audio.lua similarity index 100% rename from audio.lua rename to src/audio.lua diff --git a/colors.lua b/src/colors.lua similarity index 100% rename from colors.lua rename to src/colors.lua diff --git a/constants.lua b/src/constants.lua similarity index 100% rename from constants.lua rename to src/constants.lua diff --git a/src/graphics.lua b/src/graphics.lua new file mode 100644 index 0000000..0e3cca3 --- /dev/null +++ b/src/graphics.lua @@ -0,0 +1,214 @@ +require 'src.constants' +local colors = require 'src.colors' + +Graphics = {} + +function Graphics:createWindow(isFullscreen) + gameCanvas = love.graphics.newCanvas(GAME_WIDTH, GAME_HEIGHT) + + -- Start the game in fullscreen + love.window.setFullscreen(isFullscreen, "desktop") + love.mouse.setVisible(false) +end + +function Graphics:toggleFullscreen() + local isFullscreen = not love.window.getFullscreen() + love.window.setFullscreen(isFullscreen, "desktop") +end + +function Graphics:toggleMouseVisibility() + local state = not love.mouse.isVisible() + love.mouse.setVisible(state) +end + +local function initFont() + fonts = { + score = love.graphics.newFont("assets/font_score.otf", 22), + high_score = love.graphics.newFont("assets/font_hiscore.otf", 27), + instructions = love.graphics.newFont("assets/font_score.otf", 27), + } +end + +function Graphics:loadLevelAssets() + initFont() +end + +function Graphics:loadImages(filenames) + images = {} + for _, v in ipairs(filenames) do + images[v] = love.graphics.newImage("assets/"..v..".png") + end +end + +function Graphics:backgroundQuad(height) + mapWidth = images.bg_mnt1:getWidth() * 2 + return love.graphics.newQuad(0, 0, mapWidth, height, 1547, height) +end + +local function drawImage(image, x, y) + love.graphics.draw(image, x, y) +end + +function Graphics:drawImage(drawable, x, y, offsetX, offsetY) + + love.graphics.draw(drawable, x, y, 0, 1, 1, offsetX, offsetY) +end + +function Graphics:drawIdlePlayer(x, y, offsetX, offsetY) + love.graphics.draw(images.player_idle, x, y, 0, 1, 1, offsetX, offsetY) +end + +local function drawLevelBackground() + -- Draw the beautiful sky + love.graphics.draw(images.sky, 0, 0) + love.graphics.draw(images.bg_mnt1, mountainQuad, 0, 0, 0, 1, 1, bgOffsets.mnt1, 0) + love.graphics.draw(images.bg_mnt2, mountainQuad, 0, 0, 0, 1, 1, bgOffsets.mnt2, 0) + love.graphics.draw(images.bg_forest, forestQuad, 0, 191, 0, 1, 1, bgOffsets.forest, 0) +end + +local function drawImage(image, x, y) + love.graphics.draw(image, x, y) +end + +local function drawLevelForeground() + -- Draw at bottom left + drawImage(images.fg_snow, 0, 272) +end + +local function drawLanes() + drawImage(images.lanes, 0, 130) + drawImage(images.vall, 0, 105) + drawImage(images.vall, 0, 345) +end + +local function drawObjects() + table.sort(level.objects, function(a, b) return a.lane > b.lane end) + playerDrawn = true + + for _, v in ipairs(level.objects) do + if v.lane == 3 then + love.graphics.draw(images[v.ID], v.x, v.y, 0, 1, 1, offsets[v.ID][1], offsets[v.ID][2]) + end + if (whale.lane == 3) and isRunning then + whale.playAnimation() + playerDrawn = false + end + end + for _, v in ipairs(level.objects) do + if v.lane == 2 then + love.graphics.draw(images[v.ID], v.x, v.y, 0, 1, 1, offsets[v.ID][1], offsets[v.ID][2]) + end + if whale.lane == 2 and isRunning then + whale.playAnimation() + playerDrawn = false + end + end + for _, v in ipairs(level.objects) do + if v.lane == 1 then + love.graphics.draw(images[v.ID], v.x, v.y, 0, 1, 1, offsets[v.ID][1], offsets[v.ID][2]) + end + if whale.lane == 1 and isRunning then + whale.playAnimation() + playerDrawn = false + end + end +end + +local function setColor(color, alpha) + if alpha then + local color_table = colors[color] + table.insert(color_table, alpha) + love.graphics.setColor(color_table) + else + love.graphics.setColor(colors[color]) + end +end + +local function drawGUI(controls) + local center = { + x = GAME_WIDTH / 2, + y = GAME_HEIGHT / 2 + } + + -- Score and hiscore + local limit = 200 -- Wrap the line after this many horizontal pixels + drawImage(images.ui_score, center.x - (images.ui_score:getWidth() / 2), 0) + drawImage(images.ui_hiscore, 1280 - images.ui_hiscore:getWidth(), 100) + drawImage(images.logo, 50, GAME_HEIGHT - images.logo:getHeight() * 1.3) + + local score_text = string.format("%.0f M", score) + love.graphics.setFont(fonts.score) + love.graphics.printf(score_text, center.x - 135, 53, limit, "right") + + setColor("black") + score_text = string.format("%.0f M", hiscore) + love.graphics.setFont(fonts.high_score) + love.graphics.printf(score_text, 1075, 145, limit, "right") + setColor("white") + + if not isRunning then + if playerGotNewHighScore then + setColor("black") + love.graphics.printf("New High Score! " .. score_text, 0, center.y, GAME_WIDTH, "center") + end + + -- Show info centered on the screen + local instructions = { + string.format("Press %s to start skiing", string.upper(controls.start)), + string.format("%s toggles fullscreen", string.upper(controls.toggle_fullscreen)), + string.format("%s quits the game", string.upper(controls.quit)) + } + + local font_height = fonts.instructions:getHeight() + + setColor("light-blue accent-4", .3) + love.graphics.rectangle("fill", 0, center.y, GAME_WIDTH, font_height * (#instructions + 2)) + setColor("white") + love.graphics.setFont(fonts.instructions) + + for i = 1, #instructions do + love.graphics.printf(instructions[i], 0, center.y + (font_height * i), GAME_WIDTH, "center") + end + end +end + +local function drawLevelLayers() + drawLevelBackground() + drawLevelForeground() + drawLanes() + drawObjects() + + level.drawPlayer() +end + +local function getScale() + local scaleX = love.graphics.getWidth() / GAME_WIDTH + local scaleY = love.graphics.getHeight() / GAME_HEIGHT + + if scaleY < 1 and scaleY < scaleX then + return scaleY + else + return scaleX + end +end + +local function getMarginY(scale) + return (love.graphics.getHeight() - GAME_HEIGHT * scale) / 2 +end + +local function getMarginX(scale) + return (love.graphics.getWidth() - GAME_WIDTH * scale) / 2 +end + +function love.draw() + love.graphics.setCanvas(gameCanvas) + + drawLevelLayers() + drawGUI(controls) + + love.graphics.setCanvas() + local scale = getScale() + love.graphics.draw(gameCanvas, getMarginX(scale), getMarginY(scale), 0, scale, scale) +end + +return Graphics \ No newline at end of file diff --git a/src/input.lua b/src/input.lua new file mode 100644 index 0000000..ab2ee00 --- /dev/null +++ b/src/input.lua @@ -0,0 +1,81 @@ +-- Simple LÖVE Joystick and Keyboard input +Graphics = require 'src.graphics' + +local Input = {} + +local inputState = { + keyPressed = { + up = false, + down = false, + }, + gamepadButtonPressed = { + dpadUp = false, + dpadDown = false, + } +} + +function Input:initJoystick() + local joysticks = love.joystick.getJoysticks() + joystick = joysticks[1] +end + +local function startGame() + level.startGame() +end + +function Input:keyPressed(key) + if key == "up" or key == "w" then + inputState.keyPressed.up = true + elseif key == "down" or key == "s" then + inputState.keyPressed.down = true + + elseif key == controls.toggle_fullscreen then + Graphics:toggleFullscreen() + Graphics:toggleMouseVisibility() + + elseif key == controls.start then + startGame() + end +end + +function Input:gamepadButtonPressed(button) + if button == "dpup" then + inputState.gamepadButtonPressed.dpadUp = true + elseif button == "dpdown" then + inputState.gamepadButtonPressed.dpadDown = true + + elseif button == "start" then + startGame() + end +end + +function Input:processCharacterMovementInput() + local upReleased = inputState.keyPressed.up or inputState.gamepadButtonPressed.dpadUp + local downReleased = inputState.keyPressed.down or inputState.gamepadButtonPressed.dpadDown + + if upReleased then + whale.moveUp() + inputState.keyPressed.up = false + inputState.gamepadButtonPressed.dpadUp = false + end + + if downReleased then + whale.moveDown() + inputState.keyPressed.down = false + inputState.gamepadButtonPressed.dpadDown = false + end +end + +function love.keypressed(key) + if key == controls.quit then + love.event.quit() + end + + Input:keyPressed(key) +end + +function love.gamepadpressed(joystick, button) + Input:gamepadButtonPressed(button) +end + +return Input \ No newline at end of file diff --git a/src/level.lua b/src/level.lua new file mode 100644 index 0000000..54660d3 --- /dev/null +++ b/src/level.lua @@ -0,0 +1,245 @@ +require 'src.whale' +require 'src.constants' +local audio = require 'src.audio' +Graphics = require 'src.graphics' + +level = {} + +-- LOAD +local function createBackgroundQuad() + mountainQuad = Graphics:backgroundQuad(704) + forestQuad = Graphics:backgroundQuad(423) + images.bg_mnt1:setWrap("repeat") + images.bg_mnt2:setWrap("repeat") + images.bg_forest:setWrap("repeat") +end + +local function loadImages() + img_fn = {"bg_forest", "bg_mnt1", "bg_mnt2", "fg_snow", "ui_hiscore", "lanes", "logo", "obj_log", "obj_snowman", "obj_stone", "obj_tree", "player_idle", "player_run1", "player_run2", "ui_score", "sky", "vall"} + Graphics:loadImages(img_fn) + createBackgroundQuad() +end + +local function initLanes() + level.lanes = { + laneWidth = GAME_WIDTH, + laneHeight = 20, + laneLayers = 3, + laneY = 50 + } +end + +local function initAudio() + audioSources = { + idle = audio.streamLooped("yodel_idle"), + yodel_intro = audio.stream("yodel_intro"), + yodel_loop = audio.streamLooped("yodel_loop") + } + audioSources.idle:play() + + soundEffects = { + fanfare = audio.soundEffect("fanfare"), + obj_log = audio.soundEffect("crash-log"), + obj_snowman = audio.soundEffect("crash-snowman"), + obj_stone = audio.soundEffect("crash-stone"), + obj_tree = audio.soundEffect("crash-tree") + } +end + +function level.load() + Graphics:loadLevelAssets() + loadImages() + initLanes() + initAudio() + level.objects = {} + + isRunning = false + + time = 0 + speed = 10 + + offsets = { + obj_log = {112, 47, 50}, + obj_snowman = {38, 105, 100}, + obj_stone = {33, 53, 55}, + obj_tree = {45, 107, 80} + } + + bgOffsets = { + mnt1 = 0, + mnt2 = 0, + forest = 0 + } + + laneYPos = {330, 250, 175} + + -- Keep track of current and best score + score = 0 + hiscore = 0 + playerGotNewHighScore = false + + whale.load() +end + +-- UPDATE +local function updateBackground(dt) + bgOffsets.mnt1 = bgOffsets.mnt1 + dt * 5 * speed + bgOffsets.mnt2 = bgOffsets.mnt2 + dt * 10 * speed + bgOffsets.forest = bgOffsets.forest + dt * 40 * speed + + if bgOffsets.mnt1 >= (mapWidth / 2) then + bgOffsets.mnt1 = 0 + end + if bgOffsets.mnt2 >= (mapWidth / 2) then + bgOffsets.mnt2 = 0 + end + if bgOffsets.forest >= (mapWidth / 2) then + bgOffsets.forest = 0 + end +end + +local function spawnRandomObject() + -- Randomize object type and lane + lane = love.math.random(1, 3) + objType = love.math.random(1, 100) + local object = {} + object.lane = lane + + if lane == 2 or lane == 3 then + if objType <= 10 then + object.ID = "obj_log" + elseif objType <= 30 then + object.ID = "obj_snowman" + elseif objType <= 60 then + object.ID = "obj_stone" + else + object.ID = "obj_tree" + end + else + if objType <= 20 then + object.ID = "obj_snowman" + elseif objType <= 60 then + object.ID = "obj_stone" + else + object.ID = "obj_tree" + end + end + + object.x = 1400 + object.y = getY(2, 1400) + table.insert(level.objects, object) +end + +local function leaveRunningState() + isRunning = false + time = 0 + + -- Check if player got new high score + if score == hiscore then + playerGotNewHighScore = true + soundEffects.fanfare:play() + end + + score = 0 +end + +local function checkCollision() + -- Only check collision with objects on same lane + for _, v in ipairs(level.objects) do + if v.lane == whale.lane or ((v.ID == "obj_log") and (v.lane == (whale.lane + 1))) then + distance = math.abs(whale.x - v.x) + + if ((v.ID == "obj_log") and (v.lane == (whale.lane + 1))) and (distance < (offsets[v.ID][3])) or + distance < (offsets[v.ID][3] / 2) then + soundEffects[v.ID]:play() + leaveRunningState() + end + end + end +end + +local function removeObjects() + -- Remove all objects that have left the screen + for _, v in ipairs(level.objects) do + if v.x < -100 then + table.remove(level.objects, _) + end + end +end + +function love.update(dt) + if not isRunning then + audioSources.yodel_intro:stop() + audioSources.yodel_loop:stop() + audioSources.yodel_loop:setPitch(1.0) + audioSources.idle:play() + end + + if isRunning == true then + isPaused = false + + -- Speed changes with time + -- Better to only update speed if less than 30 ... to avoid a lot of heavy calculation. + time = time + dt + speed = 10 + time / 15 + if speed >= 30 then + speed = 30 + end + audioSources.yodel_loop:setPitch(1 + (speed - 10) / 200) -- 1 -> 1.1 + + score = score + (speed / 2) * dt + if score > hiscore then + hiscore = score + end + + updateBackground(dt) + + -- Spawn new objects + if love.math.random(1, 100) <= 1 then + spawnRandomObject() + end + + -- Update object positions + for _, v in ipairs(level.objects) do + v.x = v.x - dt * 100 * speed + v.y = laneYPos[v.lane] + 0.335 * v.x + end + + -- Update whale position + Input:processCharacterMovementInput() + whale.update(dt) + + if time > 2 then + checkCollision() + end + + removeObjects() + + if (not audioSources.yodel_intro:isPlaying()) and isRunning == true then + audioSources.yodel_loop:play() + end + end +end + +function level.drawPlayer() + -- Draw whale according to game running state + if not isRunning then + Graphics:drawIdlePlayer(whale.x, whale.y, whale.offsetX, whale.offsetY) + else + if (#level.objects == 0) and isRunning then + whale.playAnimation() + end + end +end + +function level.startGame() + if not isRunning then + isRunning = true + playerGotNewHighScore = false + audioSources.idle:stop() + + if not audioSources.yodel_loop:isPlaying() then + audioSources.yodel_intro:play() + end + end +end \ No newline at end of file diff --git a/whale.lua b/src/whale.lua similarity index 68% rename from whale.lua rename to src/whale.lua index 8427bc5..d8541ae 100644 --- a/whale.lua +++ b/src/whale.lua @@ -20,24 +20,27 @@ function whale.update(dt) whale.y = getY(whale.lane, whale.x) end -function whale.keypressed(key) - if key == "up" and whale.lane < 3 then +function whale.moveUp() + if whale.lane < 3 then whale.lane = whale.lane + 1 - elseif key == "down" and whale.lane > 1 then + end +end + +function whale.moveDown() + if whale.lane > 1 then whale.lane = whale.lane - 1 end end function whale.playAnimation() if whale.animationTime % 1 <= 0.5 then - love.graphics.draw(images.player_run1, whale.x, whale.y, 0, 1, 1, whale.offsetX, whale.offsetY) + Graphics:drawImage(images.player_run1, whale.x, whale.y, whale.offsetX, whale.offsetY) else - love.graphics.draw(images.player_run2, whale.x, whale.y, 0, 1, 1, whale.offsetX, whale.offsetY) + Graphics:drawImage(images.player_run2, whale.x, whale.y, whale.offsetX, whale.offsetY) end end function getY(lane, x) local laneYPos = {330, 250, 175} return laneYPos[lane] + (0.335 * x) -end - +end \ No newline at end of file