| @@ -0,0 +1,16 @@ | |||
| function buttonClutter() | |||
| table.insert(buttons, menu:addButton("Practice", | |||
| function() | |||
| gameState = "practice" | |||
| end)) | |||
| table.insert(buttons, menu:addButton("Levels", | |||
| function() | |||
| gameState = "selectlv" | |||
| end | |||
| )) | |||
| end | |||
| local t=0 | |||
| function explode(x, y) | |||
| t = t+10 | |||
| love.graphics.circle("fill", x, y, t) | |||
| end | |||
| @@ -0,0 +1,6 @@ | |||
| rm game.love | |||
| rm game.zip | |||
| zip -r game * | |||
| mv game.zip game.love | |||
| love game.love | |||
| rm game.love | |||
| @@ -0,0 +1,22 @@ | |||
| base = Class{} | |||
| G = 6.67e-5 | |||
| function base:init(x, y) | |||
| self.x = x | |||
| self.y = y | |||
| self.image = love.graphics.newImage("entities/base/base.png") | |||
| self.w = self.image:getWidth() | |||
| self.h = self.image:getHeight() | |||
| end | |||
| function base:update(dt) | |||
| local distanceToShip = math.sqrt((firstShip.x - self.x)^2 + (firstShip.y - self.y)^2) | |||
| if distanceToShip < self.w/2 then | |||
| reachedGoal = true | |||
| end | |||
| end | |||
| function base:draw() | |||
| love.graphics.draw(self.image, self.x, self.y, 0, 1, 1, self.w/2, self.w/2) | |||
| end | |||
| @@ -0,0 +1,11 @@ | |||
| VCAM = Class{} | |||
| function VCAM:init(x, y) | |||
| self.x = x | |||
| self.y = y | |||
| end | |||
| function VCAM:update(x,y) | |||
| self.x = x | |||
| self.y = y | |||
| end | |||
| @@ -0,0 +1,45 @@ | |||
| planet = Class{} | |||
| G = 6.67e-5 | |||
| function planet:init(x, y, mass, radius, img) | |||
| self.x = x | |||
| self.y = y | |||
| self.mass = mass | |||
| self.r = radius | |||
| self.w = img:getWidth() | |||
| self.image = img | |||
| self.angle = 0 | |||
| self.color = {1,1,1,1} | |||
| end | |||
| function planet:update(dt) | |||
| local distanceToShip = math.sqrt((firstShip.x - self.x)^2 + (firstShip.y - self.y)^2) | |||
| local gravitationalAttraction = G*self.mass/(distanceToShip^2) | |||
| print((firstShip.x - self.x) .. " " .. (firstShip.y - self.y)) | |||
| self.angle = math.atan( (firstShip.y - self.y)/ (firstShip.x - self.x)) | |||
| if self.x < firstShip.x then | |||
| self.angle = self.angle - 3.14159 | |||
| end | |||
| --print("Angle is:" .. self.angle*57.29) | |||
| self.attractionY = math.sin(self.angle) * gravitationalAttraction | |||
| self.attractionX = math.cos(self.angle) * gravitationalAttraction | |||
| love.window.setTitle(self.attractionX) | |||
| firstShip.dx = firstShip.dx + self.attractionX | |||
| firstShip.dy = firstShip.dy + self.attractionY | |||
| if distanceToShip < self.w/4 then | |||
| shipIsHit = true | |||
| end | |||
| end | |||
| function planet:draw() | |||
| --love.graphics.rectangle("fill", firstShip.x, firstShip.y, 30, 30) | |||
| love.graphics.push() | |||
| love.graphics.translate(firstShip.x, firstShip.y) -- move relative (0,0) to (x,y) | |||
| love.graphics.rotate(self.angle) -- rotate coordinate system around relative (0,0) (absolute (x,y)) | |||
| --love.graphics.rectangle("fill", -(firstShip.x - self.x)/2, -20/2, (firstShip.x - self.x), 20) VECTOR | |||
| love.graphics.pop() | |||
| love.graphics.setColor(unpack(self.color)) | |||
| love.graphics.draw(self.image, self.x, self.y, 1.5708, self.r, self.r, self.w/2, self.w/2) | |||
| end | |||
| @@ -0,0 +1,103 @@ | |||
| ship = Class{} | |||
| function ship:init(x, y, image) | |||
| self.x = x | |||
| self.y = y | |||
| self.ox = x | |||
| self.oy = y | |||
| self.dy = 0 | |||
| self.dx = 5 | |||
| self.image = love.graphics.newImage(image) | |||
| self.width = self.image:getWidth() | |||
| self.height = self.image:getHeight() | |||
| self.rotation = 1.5708 | |||
| self.vector = 1.5708 | |||
| self.color = {1,1,1,1} | |||
| self.path = {} | |||
| end | |||
| function ship:newPathDot(dotx, doty) | |||
| return { | |||
| x = dotx, | |||
| y = doty | |||
| } | |||
| end | |||
| function ship:update(dt) | |||
| if not shipIsHit then | |||
| table.insert(self.path, self:newPathDot(self.x, self.y)) | |||
| self.x = self.x + self.dx/2 | |||
| self.y = self.y + self.dy/2 | |||
| if self.dx ~= 0 then | |||
| self.vector = math.atan( self.dy/ self.dx) | |||
| end | |||
| if love.keyboard.isDown('s') then | |||
| self.speed = math.sqrt(self.dx^2 + self.dy^2) | |||
| self.speed = self.speed - 0.5 | |||
| self.dx = math.cos(self.vector) * self.speed | |||
| self.dy = math.sin(self.vector) * self.speed | |||
| end | |||
| if love.keyboard.isDown('w') then | |||
| self.speed = math.sqrt(self.dx^2 + self.dy^2) | |||
| self.speed = self.speed + 0.5 | |||
| self.dx = math.cos(self.vector) * self.speed | |||
| self.dy = math.sin(self.vector) * self.speed | |||
| end | |||
| if love.keyboard.isDown('left') then | |||
| self.dx = self.dx - 0.5 | |||
| end | |||
| if love.keyboard.isDown('right') then | |||
| self.dx = self.dx + 0.5 | |||
| end | |||
| if love.keyboard.isDown('up') then | |||
| self.dy = self.dy - 0.5 | |||
| end | |||
| if love.keyboard.isDown('down') then | |||
| self.dy = self.dy + 0.5 | |||
| end | |||
| print(self.speed) | |||
| --[[ | |||
| if love.keyboard.isDown('right') then | |||
| self.rotation = self.rotation + 10 | |||
| elseif love.keyboard.isDown('left') then | |||
| self.rotation = self.rotation - 10 | |||
| end]]-- | |||
| print("rotation:" .. self.rotation) | |||
| print("speed:" .. self.dx .. " " .. self.dy) | |||
| if self.dx < 0 then | |||
| self.vector = self.vector - 3.14159 | |||
| end | |||
| self.vector = self.vector + 1.5708 | |||
| end | |||
| end | |||
| function ship:draw() | |||
| -- Draw the `self.canvas` to screen | |||
| love.graphics.setColor(unpack(self.color)) | |||
| print("DAW" .. camera.x) | |||
| love.graphics.draw(self.image, self.x, self.y, self.vector, 1, 1, self.width/2, self.height/2) | |||
| for i in ipairs(self.path) do | |||
| if i > 1 then | |||
| love.graphics.setColor(0,1,0,1) | |||
| print("DOING".. i) | |||
| love.graphics.line(self.path[i].x, self.path[i].y, self.path[i-1].x, self.path[i-1].y) | |||
| end | |||
| end | |||
| love.graphics.setColor(1,1,1,1) | |||
| end | |||
| function ship:reset() | |||
| self.x = self.ox | |||
| self.y = self.oy | |||
| self.dy = 0 | |||
| self.dx = 20 | |||
| self.rotation = 1.57 | |||
| self.canvas = love.graphics.newCanvas(WINDOW_WIDTH, WINDOW_HEIGHT) | |||
| self.vector = 1.56 | |||
| self.speed = 0 | |||
| self.path = {} | |||
| end | |||
| @@ -0,0 +1,27 @@ | |||
| explosion = Class{} | |||
| function explosion:init(x, y, v, color) | |||
| self.color = color | |||
| self.x = x | |||
| self.y = y | |||
| self.v = v | |||
| self.range = 0 | |||
| self.killed = false | |||
| print(self.i) | |||
| end | |||
| function explosion:update(dt) | |||
| self.range = self.range + dt * 24 | |||
| if self.range * self.v > WINDOW_WIDTH * 2 then | |||
| print("killing myself with range" .. self.range) | |||
| self.killed = true | |||
| end | |||
| end | |||
| function explosion:render() | |||
| print("rendering myself" .. self.x .. " " .. self.y .. " " .. self.range .. " " .. self.v) | |||
| love.graphics.setColor(unpack(self.color)) | |||
| love.graphics.circle("fill", self.x, self.y, self.range * self.v, 100) | |||
| end | |||
| @@ -0,0 +1,134 @@ | |||
| level1 = Class{} | |||
| local levelLoaded = false | |||
| local M = {} | |||
| function level1.update(dt) | |||
| if not levelLoaded then | |||
| shipsleft = 1 | |||
| local planetImage = love.graphics.newImage("entities/planet/planet" .. math.random(1, 18) .. ".png") | |||
| planetsleft = 3 | |||
| gameStatus = "setup" | |||
| playbutts = {} | |||
| guibutts = {} | |||
| VCAM.x, VCAM.y = WINDOW_WIDTH/2, WINDOW_HEIGHT/2 | |||
| explosions = {} | |||
| shipIsHit = false | |||
| guimenu = mainMenu() | |||
| reachedGoal = false | |||
| lvlbase = base(900, 200) | |||
| levelLoaded = true | |||
| table.insert(playbutts, menu:addButton("Return to setup", function() | |||
| gameStatus = "setup" | |||
| level1.reset() | |||
| end )) | |||
| table.insert(guibutts, menu:addButton("Release brake!", function () | |||
| selectedItem = "none" | |||
| gameStatus = "play" | |||
| end | |||
| )) | |||
| table.insert(guibutts, menu:addButton("To menu", function () | |||
| level1.goBack() | |||
| end)) | |||
| table.insert(planets, planet(700, 200, 50, 0.3, planetImage)) | |||
| end | |||
| if reachedGoal then | |||
| if levelsBeaten < 1 then | |||
| levelsBeaten = 1 | |||
| end | |||
| print("levelsBeaten is " .. levelsBeaten) | |||
| love.filesystem.write("save", levelsBeaten) | |||
| level1.goBack() | |||
| end | |||
| camera:update(dt) | |||
| lvlbase:update(dt) | |||
| --print(camera.x .. " " .. camera.y) | |||
| for i, explosion in ipairs(explosions) do | |||
| explosion:update(dt) | |||
| if explosion.killed then | |||
| table.remove(explosions, i) | |||
| gameStatus = "setup" | |||
| level1.reset() | |||
| end | |||
| end | |||
| if gameStatus == "play" then | |||
| camera.x, camera.y = firstShip.x - firstShip.height*4, firstShip.y- firstShip.width | |||
| --print(camera.x .. firstShip.x) | |||
| if shipIsHit then | |||
| if #explosions == 0 then | |||
| table.insert(explosions, explosion(firstShip.x, firstShip.y, 100, {1,1,1,1})) | |||
| end | |||
| end | |||
| firstShip:update(dt) | |||
| for i in ipairs(planets) do | |||
| planets[i]:update(dt) | |||
| end | |||
| else | |||
| camera:follow(VCAM.x, VCAM.y) | |||
| end | |||
| level1.GUIControl() | |||
| end | |||
| function level1.draw() | |||
| love.graphics.setColor(1,1,1,1) | |||
| camera:attach() | |||
| firstShip:draw() | |||
| lvlbase:draw() | |||
| for i in ipairs(planets) do | |||
| planets[i]:draw(dt) | |||
| end | |||
| --love.graphics.rectangle("fill",VCAM.x,VCAM.y,30,30) | |||
| if shipIsHit then | |||
| for i, explosion in ipairs(explosions) do | |||
| explosion:render() | |||
| --print("exploding") | |||
| end | |||
| end | |||
| camera:detach() | |||
| camera:draw() | |||
| if gameStatus == "setup" then | |||
| GUIDraw("left") | |||
| elseif gameStatus == "play" then | |||
| guimenu:butt(playbutts, WINDOW_WIDTH, WINDOW_HEIGHT, 1100, WINDOW_HEIGHT-50, 40, WINDOW_WIDTH/3) | |||
| end | |||
| end | |||
| function level1.goBack() | |||
| level1.reset() | |||
| gameStatus = "setup" | |||
| firstShip.path = {} | |||
| levelLoaded = false | |||
| for k in pairs(planets) do | |||
| planets[k] = nil | |||
| end | |||
| gameState = "selectlv" | |||
| end | |||
| function level1.reset() | |||
| firstShip:reset() | |||
| for k in pairs(planets) do | |||
| planets[k] = nil | |||
| end | |||
| local planetImage = love.graphics.newImage("entities/planet/planet" .. math.random(1, 18) .. ".png") | |||
| table.insert(planets, planet(700, 200, 50, 0.3, planetImage)) | |||
| shipsleft = 1 | |||
| shipIsHit = false | |||
| planetsleft = 3 | |||
| end | |||
| function level1.GUIControl() | |||
| if (love.keyboard.isDown('a') and VCAM.x > WINDOW_WIDTH/2) then | |||
| VCAM.x = VCAM.x - 10 | |||
| end | |||
| if (love.keyboard.isDown('d')) then | |||
| VCAM.x = VCAM.x + 10 | |||
| end | |||
| end | |||
| return level1 | |||
| @@ -0,0 +1,11 @@ | |||
| menu = Class{} | |||
| local M = {} | |||
| function menu.update(dt) | |||
| end | |||
| function menu.draw(dt) | |||
| menu:butt(buttons, WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_WIDTH/2, WINDOW_HEIGHT/2, 40, WINDOW_WIDTH/3) | |||
| end | |||
| return menu | |||
| @@ -0,0 +1,119 @@ | |||
| practice = Class{} | |||
| local levelLoaded = false | |||
| local M = {} | |||
| function practice.update(dt) | |||
| if not levelLoaded then | |||
| shipsleft = 1 | |||
| planetsleft = 10 | |||
| gameStatus = "setup" | |||
| playbutts = {} | |||
| guibutts = {} | |||
| XCAM = 0 | |||
| YCAM = 0 | |||
| explosions = {} | |||
| shipIsHit = false | |||
| guimenu = mainMenu() | |||
| table.insert(playbutts, menu:addButton("Return to setup", function() | |||
| gameStatus = "setup" | |||
| practice.reset() | |||
| end )) | |||
| table.insert(guibutts, menu:addButton("Release brake!", function () | |||
| selectedItem = "none" | |||
| gameStatus = "play" | |||
| end | |||
| )) | |||
| table.insert(guibutts, menu:addButton("To menu", function () | |||
| practice.goBack() | |||
| end | |||
| )) | |||
| levelLoaded = true | |||
| end | |||
| camera:update(dt) | |||
| print(camera.x .. " " .. camera.y) | |||
| for i, explosion in ipairs(explosions) do | |||
| explosion:update(dt) | |||
| if explosion.killed then | |||
| table.remove(explosions, i) | |||
| gameStatus = "setup" | |||
| practice.reset() | |||
| end | |||
| end | |||
| if gameStatus == "play" then | |||
| camera.x, camera.y = firstShip.x - firstShip.height*4, firstShip.y- firstShip.width | |||
| print(camera.x .. firstShip.x) | |||
| if shipIsHit then | |||
| if #explosions == 0 then | |||
| table.insert(explosions, explosion(firstShip.x, firstShip.y, 100, {1,1,1,1})) | |||
| end | |||
| end | |||
| firstShip:update(dt) | |||
| for i in ipairs(planets) do | |||
| planets[i]:update(dt) | |||
| end | |||
| else | |||
| camera:follow(VCAM.x, VCAM.y) | |||
| end | |||
| practice.GUIControl() | |||
| end | |||
| function practice.draw() | |||
| camera:attach() | |||
| firstShip:draw() | |||
| for i in ipairs(planets) do | |||
| planets[i]:draw(dt) | |||
| end | |||
| --love.graphics.rectangle("fill",VCAM.x,VCAM.y,30,30) | |||
| if shipIsHit then | |||
| for i, explosion in ipairs(explosions) do | |||
| explosion:render() | |||
| --print("exploding") | |||
| end | |||
| end | |||
| camera:detach() | |||
| camera:draw() | |||
| if gameStatus == "setup" then | |||
| GUIDraw("anywhere") | |||
| elseif gameStatus == "play" then | |||
| guimenu:butt(playbutts, WINDOW_WIDTH, WINDOW_HEIGHT, 1100, WINDOW_HEIGHT-50, 40, WINDOW_WIDTH/3) | |||
| end | |||
| end | |||
| function practice.goBack() | |||
| practice.reset() | |||
| gameStatus = "setup" | |||
| levelLoaded = false | |||
| gameState = "menu" | |||
| end | |||
| function practice.reset() | |||
| firstShip:reset() | |||
| for k in pairs(planets) do | |||
| planets[k] = nil | |||
| end | |||
| shipsleft = 1 | |||
| shipIsHit = false | |||
| planetsleft = 10 | |||
| end | |||
| function practice.GUIControl() | |||
| if (love.keyboard.isDown('w')) then | |||
| VCAM.y = VCAM.y - 10 | |||
| end | |||
| if (love.keyboard.isDown('a')) then | |||
| VCAM.x = VCAM.x - 10 | |||
| end | |||
| if (love.keyboard.isDown('s')) then | |||
| VCAM.y = VCAM.y + 10 | |||
| end | |||
| if (love.keyboard.isDown('d')) then | |||
| VCAM.x = VCAM.x + 10 | |||
| end | |||
| end | |||
| return practice | |||
| @@ -0,0 +1,22 @@ | |||
| selectlv = Class{} | |||
| levels = {} | |||
| table.insert(levels, menu:addButton("Level 1", function () | |||
| gameState = "level1" | |||
| end )) | |||
| table.insert(levels, menu:addButton("Go Back", function () | |||
| gameState = "menu" | |||
| end )) | |||
| local M = {} | |||
| function selectlv.update(dt) | |||
| end | |||
| function selectlv.draw(dt) | |||
| menu:butt(levels, WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_WIDTH/2, WINDOW_HEIGHT/2, 40, WINDOW_WIDTH/3, "beatenGreen") | |||
| end | |||
| return selectlv | |||
| @@ -0,0 +1,57 @@ | |||
| gameState = "menu" | |||
| require 'src/dependencies' | |||
| math.randomseed(os.time()) | |||
| --VARIABLES | |||
| RESOLUTION_SET = 0 | |||
| isFullscreen = 0 | |||
| DIFFERENCE_X = 1 | |||
| DIFFERENCE_Y = 1 | |||
| WINDOW_WIDTH = 1280 | |||
| WINDOW_HEIGHT = 720 | |||
| OFFSET_X = 0 | |||
| OFFSET_Y = 0 | |||
| levelsBeaten = 0 | |||
| planets = {} | |||
| buttons = {} | |||
| menu = mainMenu() | |||
| function love.load() | |||
| testwalls = love.filesystem.load("save") | |||
| if testwalls ~= nil then | |||
| levelsBeaten = love.filesystem.load("save") | |||
| print("Save file found") | |||
| else | |||
| print("No save file found!") | |||
| end | |||
| tick.framerate = 60 | |||
| camera = Camera() | |||
| BG = love.graphics.newImage("entities/background.jpg") | |||
| simpleScale.setWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT) | |||
| firstShip = ship(-500, -500, "entities/ship/smol_red_01.png") | |||
| VCAM = VCAM(WINDOW_WIDTH/2, WINDOW_HEIGHT/2) | |||
| smallfont = love.graphics.newFont("font.ttf", 25) | |||
| --table.insert(planets, planet(100, WINDOW_HEIGHT/2-100, 1010000000, 1)) | |||
| buttonClutter() | |||
| --planet2 = planet(1000, 300, 1000000000, 20) | |||
| end | |||
| function love.update(dt) | |||
| stateUpdate(dt) | |||
| end | |||
| function love.draw() | |||
| simpleScale.set() | |||
| --love.graphics.draw(BG, 0, 0) | |||
| stateDraw() | |||
| simpleScale.unSet() | |||
| end | |||
| function love.mousereleased(x, y, button) | |||
| love.keyboard.mouseisReleased = true | |||
| end | |||
| @@ -0,0 +1,369 @@ | |||
| --[[ | |||
| MIT License | |||
| Copyright (c) 2017 SSYGEN | |||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| of this software and associated documentation files (the "Software"), to deal | |||
| in the Software without restriction, including without limitation the rights | |||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| copies of the Software, and to permit persons to whom the Software is | |||
| furnished to do so, subject to the following conditions: | |||
| The above copyright notice and this permission notice shall be included in all | |||
| copies or substantial portions of the Software. | |||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| SOFTWARE. | |||
| ]]-- | |||
| local function lerp(a, b, x) return a + (b - a)*x end | |||
| local function csnap(v, x) return math.ceil(v/x)*x - x/2 end | |||
| -- Shake according to https://jonny.morrill.me/en/blog/gamedev-how-to-implement-a-camera-shake-effect/ | |||
| local function newShake(amplitude, duration, frequency) | |||
| local self = { | |||
| amplitude = amplitude or 0, | |||
| duration = duration or 0, | |||
| frequency = frequency or 60, | |||
| samples = {}, | |||
| start_time = love.timer.getTime()*1000, | |||
| t = 0, | |||
| shaking = true, | |||
| } | |||
| local sample_count = (self.duration/1000)*self.frequency | |||
| for i = 1, sample_count do self.samples[i] = 2*love.math.random()-1 end | |||
| return self | |||
| end | |||
| local function updateShake(self, dt) | |||
| self.t = love.timer.getTime()*1000 - self.start_time | |||
| if self.t > self.duration then self.shaking = false end | |||
| end | |||
| local function shakeNoise(self, s) | |||
| if s >= #self.samples then return 0 end | |||
| return self.samples[s] or 0 | |||
| end | |||
| local function shakeDecay(self, t) | |||
| if t > self.duration then return 0 end | |||
| return (self.duration - t)/self.duration | |||
| end | |||
| local function getShakeAmplitude(self, t) | |||
| if not t then | |||
| if not self.shaking then return 0 end | |||
| t = self.t | |||
| end | |||
| local s = (t/1000)*self.frequency | |||
| local s0 = math.floor(s) | |||
| local s1 = s0 + 1 | |||
| local k = shakeDecay(self, t) | |||
| return self.amplitude*(shakeNoise(self, s0) + (s - s0)*(shakeNoise(self, s1) - shakeNoise(self, s0)))*k | |||
| end | |||
| -- Camera | |||
| local Camera = {} | |||
| Camera.__index = Camera | |||
| local function new(x, y, w, h, scale, rotation) | |||
| return setmetatable({ | |||
| x = x or (w or love.graphics.getWidth())/2, y = y or (h or love.graphics.getHeight())/2, | |||
| mx = x or (w or love.graphics.getWidth())/2, my = y or (h or love.graphics.getHeight())/2, | |||
| screen_x = x or (w or love.graphics.getWidth())/2, screen_y = y or (h or love.graphics.getHeight())/2, | |||
| w = w or love.graphics.getWidth(), h = h or love.graphics.getHeight(), | |||
| scale = scale or 1, | |||
| rotation = rotation or 0, | |||
| horizontal_shakes = {}, vertical_shakes = {}, | |||
| target_x = nil, target_y = nil, | |||
| scroll_x = 0, scroll_y = 0, | |||
| last_target_x = nil, last_target_y = nil, | |||
| follow_lerp_x = 1, follow_lerp_y = 1, | |||
| follow_lead_x = 0, follow_lead_y = 0, | |||
| deadzone = nil, bound = nil, | |||
| draw_deadzone = false, | |||
| flash_duration = 1, flash_timer = 0, flash_color = {0, 0, 0, 1}, | |||
| last_horizontal_shake_amount = 0, last_vertical_shake_amount = 0, | |||
| fade_duration = 1, fade_timer = 0, fade_color = {0, 0, 0, 0}, | |||
| }, Camera) | |||
| end | |||
| function Camera:attach() | |||
| love.graphics.push() | |||
| love.graphics.translate(self.w/2, self.h/2) | |||
| love.graphics.scale(self.scale) | |||
| love.graphics.rotate(self.rotation) | |||
| love.graphics.translate(-self.x, -self.y) | |||
| end | |||
| function Camera:detach() | |||
| love.graphics.pop() | |||
| end | |||
| function Camera:move(dx, dy) | |||
| self.x, self.y = self.x + dx, self.y + dy | |||
| end | |||
| function Camera:toWorldCoords(x, y) | |||
| local c, s = math.cos(self.rotation), math.sin(self.rotation) | |||
| x, y = (x - self.w/2)/self.scale, (y - self.h/2)/self.scale | |||
| x, y = c*x - s*y, s*x + c*y | |||
| return x + self.x, y + self.y | |||
| end | |||
| function Camera:toCameraCoords(x, y) | |||
| local c, s = math.cos(self.rotation), math.sin(self.rotation) | |||
| x, y = x - self.x, y - self.y | |||
| x, y = c*x - s*y, s*x + c*y | |||
| return x*self.scale + self.w/2, y*self.scale + self.h/2 | |||
| end | |||
| function Camera:getMousePosition() | |||
| return self:toWorldCoords(love.mouse.getPosition()) | |||
| end | |||
| function Camera:shake(intensity, duration, frequency, axes) | |||
| if not axes then axes = 'XY' end | |||
| axes = string.upper(axes) | |||
| if string.find(axes, 'X') then table.insert(self.horizontal_shakes, newShake(intensity, duration*1000, frequency)) end | |||
| if string.find(axes, 'Y') then table.insert(self.vertical_shakes, newShake(intensity, duration*1000, frequency)) end | |||
| end | |||
| function Camera:update(dt) | |||
| self.mx, self.my = self:toWorldCoords(love.mouse.getPosition()) | |||
| -- Flash -- | |||
| if self.flashing then | |||
| self.flash_timer = self.flash_timer + dt | |||
| if self.flash_timer > self.flash_duration then | |||
| self.flash_timer = 0 | |||
| self.flashing = false | |||
| end | |||
| end | |||
| -- Fade -- | |||
| if self.fading then | |||
| self.fade_timer = self.fade_timer + dt | |||
| self.fade_color = { | |||
| lerp(self.base_fade_color[1], self.target_fade_color[1], self.fade_timer/self.fade_duration), | |||
| lerp(self.base_fade_color[2], self.target_fade_color[2], self.fade_timer/self.fade_duration), | |||
| lerp(self.base_fade_color[3], self.target_fade_color[3], self.fade_timer/self.fade_duration), | |||
| lerp(self.base_fade_color[4], self.target_fade_color[4], self.fade_timer/self.fade_duration), | |||
| } | |||
| if self.fade_timer > self.fade_duration then | |||
| self.fade_timer = 0 | |||
| self.fading = false | |||
| if self.fade_action then self.fade_action() end | |||
| end | |||
| end | |||
| -- Shake -- | |||
| local horizontal_shake_amount, vertical_shake_amount = 0, 0 | |||
| for i = #self.horizontal_shakes, 1, -1 do | |||
| updateShake(self.horizontal_shakes[i], dt) | |||
| horizontal_shake_amount = horizontal_shake_amount + getShakeAmplitude(self.horizontal_shakes[i]) | |||
| if not self.horizontal_shakes[i].shaking then table.remove(self.horizontal_shakes, i) end | |||
| end | |||
| for i = #self.vertical_shakes, 1, -1 do | |||
| updateShake(self.vertical_shakes[i], dt) | |||
| vertical_shake_amount = vertical_shake_amount + getShakeAmplitude(self.vertical_shakes[i]) | |||
| if not self.vertical_shakes[i].shaking then table.remove(self.vertical_shakes, i) end | |||
| end | |||
| self.x, self.y = self.x - self.last_horizontal_shake_amount, self.y - self.last_vertical_shake_amount | |||
| self:move(horizontal_shake_amount, vertical_shake_amount) | |||
| self.last_horizontal_shake_amount, self.last_vertical_shake_amount = horizontal_shake_amount, vertical_shake_amount | |||
| -- Follow -- | |||
| if not self.target_x and not self.target_y then return end | |||
| -- Set follow style deadzones | |||
| if self.follow_style == 'LOCKON' then | |||
| local w, h = self.w/16, self.w/16 | |||
| self:setDeadzone((self.w - w)/2, (self.h - h)/2, w, h) | |||
| elseif self.follow_style == 'PLATFORMER' then | |||
| local w, h = self.w/8, self.h/3 | |||
| self:setDeadzone((self.w - w)/2, (self.h - h)/2 - h*0.25, w, h) | |||
| elseif self.follow_style == 'TOPDOWN' then | |||
| local s = math.max(self.w, self.h)/4 | |||
| self:setDeadzone((self.w - s)/2, (self.h - s)/2, s, s) | |||
| elseif self.follow_style == 'TOPDOWN_TIGHT' then | |||
| local s = math.max(self.w, self.h)/8 | |||
| self:setDeadzone((self.w - s)/2, (self.h - s)/2, s, s) | |||
| elseif self.follow_style == 'SCREEN_BY_SCREEN' then | |||
| self:setDeadzone(0, 0, 0, 0) | |||
| elseif self.follow_style == 'NO_DEADZONE' then | |||
| self.deadzone = nil | |||
| end | |||
| -- No deadzone means we just track the target with no lerp | |||
| if not self.deadzone then | |||
| self.x, self.y = self.target_x, self.target_y | |||
| if self.bound then | |||
| self.x = math.min(math.max(self.x, self.bounds_min_x + self.w/2), self.bounds_max_x - self.w/2) | |||
| self.y = math.min(math.max(self.y, self.bounds_min_y + self.h/2), self.bounds_max_y - self.h/2) | |||
| end | |||
| return | |||
| end | |||
| -- Convert appropriate variables to camera coordinates since the deadzone is applied in terms of the camera and not the world | |||
| local dx1, dy1, dx2, dy2 = self.deadzone_x, self.deadzone_y, self.deadzone_x + self.deadzone_w, self.deadzone_y + self.deadzone_h | |||
| local scroll_x, scroll_y = 0, 0 | |||
| local target_x, target_y = self:toCameraCoords(self.target_x, self.target_y) | |||
| local x, y = self:toCameraCoords(self.x, self.y) | |||
| -- Screen by screen follow mode needs to be handled a bit differently | |||
| if self.follow_style == 'SCREEN_BY_SCREEN' then | |||
| -- Don't change self.screen_x/y if already at the boundaries | |||
| if self.bound then | |||
| if self.x > self.bounds_min_x + self.w/2 and target_x < 0 then self.screen_x = csnap(self.screen_x - self.w/self.scale, self.w/self.scale) end | |||
| if self.x < self.bounds_max_x - self.w/2 and target_x >= self.w then self.screen_x = csnap(self.screen_x + self.w/self.scale, self.w/self.scale) end | |||
| if self.y > self.bounds_min_y + self.h/2 and target_y < 0 then self.screen_y = csnap(self.screen_y - self.h/self.scale, self.h/self.scale) end | |||
| if self.y < self.bounds_max_y - self.h/2 and target_y >= self.h then self.screen_y = csnap(self.screen_y + self.h/self.scale, self.h/self.scale) end | |||
| -- Move to the next screen if the target is outside the screen boundaries | |||
| else | |||
| if target_x < 0 then self.screen_x = csnap(self.screen_x - self.w/self.scale, self.w/self.scale) end | |||
| if target_x >= self.w then self.screen_x = csnap(self.screen_x + self.w/self.scale, self.w/self.scale) end | |||
| if target_y < 0 then self.screen_y = csnap(self.screen_y - self.h/self.scale, self.h/self.scale) end | |||
| if target_y >= self.h then self.screen_y = csnap(self.screen_y + self.h/self.scale, self.h/self.scale) end | |||
| end | |||
| self.x = lerp(self.x, self.screen_x, self.follow_lerp_x) | |||
| self.y = lerp(self.y, self.screen_y, self.follow_lerp_y) | |||
| -- Apply bounds | |||
| if self.bound then | |||
| self.x = math.min(math.max(self.x, self.bounds_min_x + self.w/2), self.bounds_max_x - self.w/2) | |||
| self.y = math.min(math.max(self.y, self.bounds_min_y + self.h/2), self.bounds_max_y - self.h/2) | |||
| end | |||
| -- All other follow modes | |||
| else | |||
| -- Figure out how much the camera needs to scroll | |||
| if target_x < x + (dx1 + dx2 - x) then | |||
| local d = target_x - dx1 | |||
| if d < 0 then scroll_x = d end | |||
| end | |||
| if target_x > x - (dx1 + dx2 - x) then | |||
| local d = target_x - dx2 | |||
| if d > 0 then scroll_x = d end | |||
| end | |||
| if target_y < y + (dy1 + dy2 - y) then | |||
| local d = target_y - dy1 | |||
| if d < 0 then scroll_y = d end | |||
| end | |||
| if target_y > y - (dy1 + dy2 - y) then | |||
| local d = target_y - dy2 | |||
| if d > 0 then scroll_y = d end | |||
| end | |||
| -- Apply lead | |||
| if not self.last_target_x and not self.last_target_y then self.last_target_x, self.last_target_y = self.target_x, self.target_y end | |||
| scroll_x = scroll_x + (self.target_x - self.last_target_x)*self.follow_lead_x | |||
| scroll_y = scroll_y + (self.target_y - self.last_target_y)*self.follow_lead_y | |||
| self.last_target_x, self.last_target_y = self.target_x, self.target_y | |||
| -- Scroll towards target with lerp | |||
| self.x = lerp(self.x, self.x + scroll_x, self.follow_lerp_x) | |||
| self.y = lerp(self.y, self.y + scroll_y, self.follow_lerp_y) | |||
| -- Apply bounds | |||
| if self.bound then | |||
| self.x = math.min(math.max(self.x, self.bounds_min_x + self.w/2), self.bounds_max_x - self.w/2) | |||
| self.y = math.min(math.max(self.y, self.bounds_min_y + self.h/2), self.bounds_max_y - self.h/2) | |||
| end | |||
| end | |||
| end | |||
| function Camera:draw() | |||
| if self.draw_deadzone and self.deadzone then | |||
| local n = love.graphics.getLineWidth() | |||
| love.graphics.setLineWidth(2) | |||
| love.graphics.line(self.deadzone_x - 1, self.deadzone_y, self.deadzone_x + 6, self.deadzone_y) | |||
| love.graphics.line(self.deadzone_x, self.deadzone_y, self.deadzone_x, self.deadzone_y + 6) | |||
| love.graphics.line(self.deadzone_x - 1, self.deadzone_y + self.deadzone_h, self.deadzone_x + 6, self.deadzone_y + self.deadzone_h) | |||
| love.graphics.line(self.deadzone_x, self.deadzone_y + self.deadzone_h, self.deadzone_x, self.deadzone_y + self.deadzone_h - 6) | |||
| love.graphics.line(self.deadzone_x + self.deadzone_w + 1, self.deadzone_y + self.deadzone_h, self.deadzone_x + self.deadzone_w - 6, self.deadzone_y + self.deadzone_h) | |||
| love.graphics.line(self.deadzone_x + self.deadzone_w, self.deadzone_y + self.deadzone_h, self.deadzone_x + self.deadzone_w, self.deadzone_y + self.deadzone_h - 6) | |||
| love.graphics.line(self.deadzone_x + self.deadzone_w + 1, self.deadzone_y, self.deadzone_x + self.deadzone_w - 6, self.deadzone_y) | |||
| love.graphics.line(self.deadzone_x + self.deadzone_w, self.deadzone_y, self.deadzone_x + self.deadzone_w, self.deadzone_y + 6) | |||
| love.graphics.setLineWidth(n) | |||
| end | |||
| if self.flashing then | |||
| local r, g, b, a = love.graphics.getColor() | |||
| love.graphics.setColor(self.flash_color) | |||
| love.graphics.rectangle('fill', 0, 0, self.w, self.h) | |||
| love.graphics.setColor(r, g, b, a) | |||
| end | |||
| local r, g, b, a = love.graphics.getColor() | |||
| love.graphics.setColor(self.fade_color) | |||
| love.graphics.rectangle('fill', 0, 0, self.w, self.h) | |||
| love.graphics.setColor(r, g, b, a) | |||
| end | |||
| function Camera:follow(x, y) | |||
| self.target_x, self.target_y = x, y | |||
| end | |||
| function Camera:setDeadzone(x, y, w, h) | |||
| self.deadzone = true | |||
| self.deadzone_x = x | |||
| self.deadzone_y = y | |||
| self.deadzone_w = w | |||
| self.deadzone_h = h | |||
| end | |||
| function Camera:setBounds(x, y, w, h) | |||
| self.bound = true | |||
| self.bounds_min_x = x | |||
| self.bounds_min_y = y | |||
| self.bounds_max_x = x + w | |||
| self.bounds_max_y = y + h | |||
| end | |||
| function Camera:setFollowStyle(follow_style) | |||
| self.follow_style = follow_style | |||
| end | |||
| function Camera:setFollowLerp(x, y) | |||
| self.follow_lerp_x = x | |||
| self.follow_lerp_y = y or x | |||
| end | |||
| function Camera:setFollowLead(x, y) | |||
| self.follow_lead_x = x | |||
| self.follow_lead_y = y or x | |||
| end | |||
| function Camera:flash(duration, color) | |||
| self.flash_duration = duration | |||
| self.flash_color = color or self.flash_color | |||
| self.flash_timer = 0 | |||
| self.flashing = true | |||
| end | |||
| function Camera:fade(duration, color, action) | |||
| self.fade_duration = duration | |||
| self.base_fade_color = self.fade_color | |||
| self.target_fade_color = color | |||
| self.fade_timer = 0 | |||
| self.fade_action = action | |||
| self.fading = true | |||
| end | |||
| return setmetatable({new = new}, {__call = function(_, ...) return new(...) end}) | |||
| @@ -0,0 +1,221 @@ | |||
| selectedItem = "none" | |||
| local planetImage = love.graphics.newImage("entities/planet/planet" .. math.random(1, 18) .. ".png") | |||
| function GUIDraw(mode) | |||
| --MAIN | |||
| love.graphics.setColor(1,1,1,1) | |||
| love.graphics.rectangle("fill", WINDOW_WIDTH*0.7-3, 0, WINDOW_WIDTH*0.3-3, WINDOW_HEIGHT) | |||
| love.graphics.setColor(0,0,0,1) | |||
| local menuX = WINDOW_WIDTH*0.7 | |||
| local menuW = WINDOW_WIDTH*0.3 | |||
| local menuY = 3 | |||
| love.graphics.rectangle("fill", WINDOW_WIDTH*0.7, 3, WINDOW_WIDTH*0.3, WINDOW_HEIGHT-6) | |||
| love.graphics.setFont(smallfont) | |||
| love.graphics.setColor(1,1,1,1) | |||
| local textW = smallfont:getWidth("Control Panel") | |||
| love.graphics.print("Control Panel",menuX + menuW/2-textW/2,menuY+10) | |||
| --MAIN | |||
| --SHIP | |||
| local shipImage = love.graphics.newImage("entities/ship/smol_red_01.png") | |||
| GUIButton(shipsleft, shipImage, menuX + 60, menuY+WINDOW_HEIGHT*0.2, function() selectedItem = "ship" end, 1, 1, {1,1,1,1}, 1.57) | |||
| --SHIP | |||
| --PLANET | |||
| GUIButton(planetsleft, planetImage, menuX + 60, menuY+WINDOW_HEIGHT*0.4, function() selectedItem = "planet" end, 0.5, 0.5, {1,1,1,1}, 1.57) | |||
| --PLANET | |||
| --PLACING | |||
| local mx, my = love.mouse.getPosition() | |||
| local vmx, vmy = camera:getMousePosition() | |||
| local mx = mx * DIFFERENCE_X | |||
| local my = my * DIFFERENCE_Y | |||
| if mode == "anywhere" then | |||
| love.graphics.setColor(1,1,1,0.5) | |||
| if selectedItem == "ship" and mx < menuX then | |||
| local shipW = shipImage:getWidth() | |||
| local shipH = shipImage:getHeight() | |||
| love.graphics.draw(shipImage,mx,my, 1.5708, 1, 1, shipW/2, shipH/2) | |||
| if love.keyboard.mouseisReleased then | |||
| love.keyboard.mouseisReleased = false | |||
| firstShip.x = vmx | |||
| firstShip.y = vmy | |||
| shipsleft = shipsleft - 1 | |||
| end | |||
| if shipsleft == 0 then | |||
| selectedItem = "none" | |||
| end | |||
| end | |||
| elseif mode == "left" then | |||
| love.graphics.setColor(1,1,1,0.5) | |||
| if selectedItem == "ship" and mx < menuX then | |||
| local shipW = shipImage:getWidth() | |||
| local shipH = shipImage:getHeight() | |||
| love.graphics.draw(shipImage,10,my, 1.5708, 1, 1, shipW/2, shipH/2) | |||
| if love.keyboard.mouseisReleased then | |||
| love.keyboard.mouseisReleased = false | |||
| firstShip.x = 250 | |||
| firstShip.y = vmy | |||
| shipsleft = shipsleft - 1 | |||
| end | |||
| if shipsleft == 0 then | |||
| selectedItem = "none" | |||
| end | |||
| end | |||
| end | |||
| local dx, dy = camera:toCameraCoords(firstShip.x, firstShip.y) | |||
| if VCAM.x - WINDOW_WIDTH/2+250 > firstShip.x and shipsleft == 0 then | |||
| love.graphics.setColor(1,0,0,1) | |||
| love.graphics.rectangle("fill", 0, dy-firstShip.width/2, 10, firstShip.width) | |||
| end | |||
| if VCAM.y - WINDOW_HEIGHT/2+50 > firstShip.y and shipsleft == 0 then | |||
| love.graphics.setColor(1,0,0,1) | |||
| love.graphics.rectangle("fill", dx-firstShip.height/2, 0, firstShip.height, 10) | |||
| end | |||
| if VCAM.y + WINDOW_HEIGHT/2+50 < firstShip.y and shipsleft == 0 then | |||
| love.graphics.setColor(1,0,0,1) | |||
| love.graphics.rectangle("fill", dx-firstShip.height/2, WINDOW_HEIGHT-10, firstShip.height, 10) | |||
| end | |||
| if VCAM.x + WINDOW_WIDTH/2-150< firstShip.x and shipsleft == 0 then | |||
| love.graphics.setColor(1,0,0,1) | |||
| love.graphics.rectangle("fill", menuX-10, dy-firstShip.width/2, 10, firstShip.width) | |||
| end | |||
| for k in ipairs(planets) do | |||
| local dx, dy = camera:toCameraCoords(planets[k].x, planets[k].y) | |||
| if VCAM.x - WINDOW_WIDTH/2+250 > planets[k].x then | |||
| love.graphics.setColor(0,0,1,1) | |||
| love.graphics.rectangle("fill", 0, dy-planets[k].w*0.3/2, 10, planets[k].w*0.3) | |||
| end | |||
| if VCAM.y - WINDOW_HEIGHT/2+50 > planets[k].y then | |||
| love.graphics.setColor(0,0,1,1) | |||
| love.graphics.rectangle("fill", dx-planets[k].w*0.3/2, 0, planets[k].w*0.3, 10) | |||
| end | |||
| if VCAM.y + WINDOW_HEIGHT/2+50 < planets[k].y then | |||
| love.graphics.setColor(0,0,1,1) | |||
| love.graphics.rectangle("fill", dx-planets[k].w*0.3/2, WINDOW_HEIGHT-10, planets[k].w*0.3, 10) | |||
| end | |||
| if VCAM.x + WINDOW_WIDTH/2-150< planets[k].x then | |||
| love.graphics.setColor(0,0,1,1) | |||
| love.graphics.rectangle("fill", menuX-10, dy-planets[k].w*0.3/2, 10, planets[k].w*0.3) | |||
| end | |||
| end | |||
| if lvlbase ~= nil then | |||
| local dx, dy = camera:toCameraCoords(lvlbase.x, lvlbase.y) | |||
| print("YESYEYSYSYADSYADYASD") | |||
| if VCAM.x - WINDOW_WIDTH/2+250 > lvlbase.x then | |||
| love.graphics.setColor(0,1,0,1) | |||
| love.graphics.rectangle("fill", 0, dy-lvlbase.w/2, 10, lvlbase.w) | |||
| end | |||
| if VCAM.y - WINDOW_HEIGHT/2+50 > lvlbase.y then | |||
| love.graphics.setColor(0,1,0,1) | |||
| love.graphics.rectangle("fill", dx-lvlbase.w/2, 0, lvlbase.w, 10) | |||
| end | |||
| if VCAM.y + WINDOW_HEIGHT/2+50 < lvlbase.y then | |||
| love.graphics.setColor(0,1,0,1) | |||
| love.graphics.rectangle("fill", dx-lvlbase.w/2, WINDOW_HEIGHT-10, lvlbase.w, 10) | |||
| end | |||
| if VCAM.x + WINDOW_WIDTH/2-150< lvlbase.x then | |||
| love.graphics.setColor(0,1,0,1) | |||
| love.graphics.rectangle("fill", menuX-10, dy-lvlbase.w/2, 10, lvlbase.w) | |||
| end | |||
| end | |||
| if selectedItem == "planet" and mx < menuX then | |||
| local shipW = planetImage:getWidth() | |||
| local shipH = planetImage:getHeight() | |||
| love.graphics.draw(planetImage,mx,my,0, 0.3, 0.3, shipW/2, shipH/2) | |||
| if love.keyboard.mouseisReleased then | |||
| love.keyboard.mouseisReleased = false | |||
| table.insert(planets, planet(vmx, vmy, 1000000000, 0.3, planetImage)) | |||
| planetImage = love.graphics.newImage("entities/planet/planet" .. math.random(1, 18) .. ".png") | |||
| planetsleft = planetsleft-1 | |||
| end | |||
| if planetsleft == 0 then | |||
| selectedItem = "none" | |||
| end | |||
| end | |||
| if selectedItem == "eraser" then | |||
| local hot = (vmx > firstShip.x-firstShip.height/2 and vmx < firstShip.x+firstShip.height and vmy > firstShip.y-firstShip.width/2 and vmy < firstShip.y - firstShip.width/2 + firstShip.width) | |||
| if hot then | |||
| firstShip.color = {1,0,0,1} | |||
| print("hot") | |||
| else | |||
| firstShip.color = {1,1,1,1} | |||
| print(mx .. " " .. my .. " " .. firstShip.x .. " " .. firstShip.y .. " " .. firstShip.width .. firstShip.height) | |||
| end | |||
| local pressed = love.keyboard.mouseisReleased | |||
| if location == "android" then | |||
| pressed = love.mouse.isDown(1) | |||
| end | |||
| if pressed and hot then | |||
| love.keyboard.mouseisReleased = false | |||
| firstShip.x = -9000 | |||
| shipsleft = shipsleft + 1 | |||
| end | |||
| for j in ipairs(planets) do | |||
| local hot = (vmx > planets[j].x-planets[j].w*0.3/2 and vmx < planets[j].x+planets[j].w*0.3 and vmy > planets[j].y-planets[j].w*0.3/2 and vmy < planets[j].y + planets[j].w*0.3) | |||
| if hot then | |||
| planets[j].color = {1,0,0,1} | |||
| print("hot") | |||
| else | |||
| planets[j].color = {1,1,1,1} | |||
| --print(mx .. " " .. my .. " " .. firstShip.x .. " " .. firstShip.y .. " " .. firstShip.width .. firstShip.height) | |||
| end | |||
| local pressed = love.keyboard.mouseisReleased | |||
| if location == "android" then | |||
| pressed = love.mouse.isDown(1) | |||
| end | |||
| if pressed and hot then | |||
| love.keyboard.mouseisReleased = false | |||
| table.remove(planets, j) | |||
| planetsleft = planetsleft + 1 | |||
| break | |||
| end | |||
| end | |||
| end | |||
| --PLACING | |||
| --REMOVE TOOL | |||
| trashbin = love.graphics.newImage("entities/trashbin.png") | |||
| GUIButton("inf", trashbin, menuX + 60, menuY+WINDOW_HEIGHT*0.6, function() selectedItem = "eraser" end, 1, 1, {1,1,1,1}, 0) | |||
| --REMOVE TOOL | |||
| --START BUTTON | |||
| if shipsleft == 0 then | |||
| guimenu:butt(guibutts, WINDOW_WIDTH, WINDOW_HEIGHT, menuX + 200, WINDOW_HEIGHT-100, 40, WINDOW_WIDTH/3.7) | |||
| end | |||
| --START BUTTON | |||
| love.window.setTitle(selectedItem) | |||
| end | |||
| function GUIButton(num, shipImage, x, y, fn, sx, sy, color, r) | |||
| love.graphics.setColor(unpack(color)) | |||
| local shipW = shipImage:getWidth() | |||
| local shipH = shipImage:getHeight() | |||
| local mx, my = love.mouse.getPosition() | |||
| local vmx, vmy = camera:getMousePosition() | |||
| local mx = mx * DIFFERENCE_X | |||
| local my = my * DIFFERENCE_Y | |||
| local hot = (mx > x-shipW/2*sx and mx < x-shipW/2*sx + shipW*sx and my > y-shipH/2*sy and my < y -shipH/2*sy + shipH*sy) | |||
| if (hot) then | |||
| love.graphics.setColor(1,0,0,0.5) | |||
| --love.graphics.rectangle("fill", x, y, shipW, shipH) | |||
| end | |||
| local pressed = love.keyboard.mouseisReleased | |||
| if location == "android" then | |||
| pressed = love.mouse.isDown(1) | |||
| end | |||
| if pressed and hot then | |||
| love.keyboard.mouseisReleased = false | |||
| fn() | |||
| end | |||
| love.graphics.draw(shipImage,x,y, r, sx, sy, shipW/2, shipH/2) | |||
| love.graphics.print("x" .. num,x+10,y) | |||
| love.graphics.setColor(1,1,1,1) | |||
| end | |||
| @@ -0,0 +1,94 @@ | |||
| --[[ | |||
| Copyright (c) 2010-2013 Matthias Richter | |||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| of this software and associated documentation files (the "Software"), to deal | |||
| in the Software without restriction, including without limitation the rights | |||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| copies of the Software, and to permit persons to whom the Software is | |||
| furnished to do so, subject to the following conditions: | |||
| The above copyright notice and this permission notice shall be included in | |||
| all copies or substantial portions of the Software. | |||
| Except as contained in this notice, the name(s) of the above copyright holders | |||
| shall not be used in advertising or otherwise to promote the sale, use or | |||
| other dealings in this Software without prior written authorization. | |||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| THE SOFTWARE. | |||
| ]]-- | |||
| local function include_helper(to, from, seen) | |||
| if from == nil then | |||
| return to | |||
| elseif type(from) ~= 'table' then | |||
| return from | |||
| elseif seen[from] then | |||
| return seen[from] | |||
| end | |||
| seen[from] = to | |||
| for k,v in pairs(from) do | |||
| k = include_helper({}, k, seen) -- keys might also be tables | |||
| if to[k] == nil then | |||
| to[k] = include_helper({}, v, seen) | |||
| end | |||
| end | |||
| return to | |||
| end | |||
| -- deeply copies `other' into `class'. keys in `other' that are already | |||
| -- defined in `class' are omitted | |||
| local function include(class, other) | |||
| return include_helper(class, other, {}) | |||
| end | |||
| -- returns a deep copy of `other' | |||
| local function clone(other) | |||
| return setmetatable(include({}, other), getmetatable(other)) | |||
| end | |||
| local function new(class) | |||
| -- mixins | |||
| class = class or {} -- class can be nil | |||
| local inc = class.__includes or {} | |||
| if getmetatable(inc) then inc = {inc} end | |||
| for _, other in ipairs(inc) do | |||
| if type(other) == "string" then | |||
| other = _G[other] | |||
| end | |||
| include(class, other) | |||
| end | |||
| -- class implementation | |||
| class.__index = class | |||
| class.init = class.init or class[1] or function() end | |||
| class.include = class.include or include | |||
| class.clone = class.clone or clone | |||
| -- constructor call | |||
| return setmetatable(class, {__call = function(c, ...) | |||
| local o = setmetatable({}, c) | |||
| o:init(...) | |||
| return o | |||
| end}) | |||
| end | |||
| -- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons). | |||
| if class_commons ~= false and not common then | |||
| common = {} | |||
| function common.class(name, prototype, parent) | |||
| return new{__includes = {prototype, parent}} | |||
| end | |||
| function common.instance(class, ...) | |||
| return class(...) | |||
| end | |||
| end | |||
| -- the module | |||
| return setmetatable({new = new, include = include, clone = clone}, | |||
| {__call = function(_,...) return new(...) end}) | |||
| @@ -0,0 +1,17 @@ | |||
| Class = require 'src/class' | |||
| require 'src/simpleScale' | |||
| require 'src/fullScreener' | |||
| require 'src/mainMenu' | |||
| require 'src/pongDisplayControl' | |||
| require 'entities/ship/ship' | |||
| require 'entities/planet/planet' | |||
| require 'buttonClutter' | |||
| require 'explosion' | |||
| require 'src/GUI' | |||
| require 'stateMachine' | |||
| require 'entities/base/base' | |||
| require 'entities/camera/VCAM' | |||
| tick = require 'src/tick' | |||
| utf8 = require("utf8") | |||
| serialize = require 'src/ser' | |||
| Camera = require 'src/Camera' | |||
| @@ -0,0 +1,39 @@ | |||
| fullScreener = Class{} | |||
| function fullScreener:init(a,b,c,d,e,f) | |||
| self.a = a | |||
| self.b = b | |||
| self.c = c | |||
| self.d = d | |||
| self.e = e | |||
| self.f = f | |||
| end | |||
| function fullScreener:toggle(vh, vw) | |||
| self.a = self.a + 1 | |||
| if (self.a > 1) then | |||
| self.a = 0 | |||
| end | |||
| if (self.a == 0 ) then | |||
| if (self.b == 1) then | |||
| self.c = 1 | |||
| self.d = 1 | |||
| self.e = 0 | |||
| self.f = 0 | |||
| simpleScale.updateWindow(WINDOW_WIDTH, WINDOW_HEIGHT,{fullscreen = false}) | |||
| self.b = 0 | |||
| end | |||
| end | |||
| if (self.a == 1) then | |||
| if (self.b == 0) then | |||
| simpleScale.updateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, {fullscreen = true}) | |||
| local newWidth = love.graphics.getWidth() | |||
| local newHeight = love.graphics.getHeight() | |||
| self.c = VIRTUAL_WIDTH / newWidth | |||
| self.d = VIRTUAL_HEIGHT / newHeight | |||
| self.e = math.fmod(newWidth * self.d, VIRTUAL_WIDTH) / 2 | |||
| self.f = math.fmod(newHeight * self.d, VIRTUAL_HEIGHT) / 2 | |||
| self.b = 1 | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,53 @@ | |||
| mainMenu = Class{} | |||
| function mainMenu:butt(buttons, VIRTUAL_WIDTH, VIRTUAL_HEIGHT, locationx, locationy, ev_BUTTON_HEIGHT, ev_button_width, arg) | |||
| local margin = 16 | |||
| local hot = false | |||
| local cursor_y = 0 | |||
| local total_height = (ev_BUTTON_HEIGHT + margin) * #buttons | |||
| local ev_bx, ev_by | |||
| for i, button in ipairs(buttons) do | |||
| button.last = button.now | |||
| ev_bx = locationx - (ev_button_width * 0.5) | |||
| ev_by = locationy - (total_height * 0.5) + cursor_y | |||
| local color = {255, 255, 255, 255} | |||
| local mx, my = love.mouse.getPosition() | |||
| local mx = mx * DIFFERENCE_X | |||
| local my = my * DIFFERENCE_Y | |||
| local hot = (mx > ev_bx and mx < ev_bx + ev_button_width and my > ev_by and my < ev_by + ev_BUTTON_HEIGHT) and i | |||
| if (hot == i) then | |||
| color = {10, 10, 0, 255} | |||
| end | |||
| button.now = love.keyboard.mouseisReleased | |||
| if location == "android" then | |||
| button.now = love.mouse.isDown(1) | |||
| end | |||
| if button.now and hot == i then | |||
| love.keyboard.mouseisReleased = false | |||
| button.fn() | |||
| break | |||
| end | |||
| if arg == "beatenGreen" then | |||
| if i <= levelsBeaten then | |||
| color = {0,1,0,1} | |||
| end | |||
| end | |||
| love.graphics.setColor(unpack(color)) | |||
| love.graphics.rectangle("fill", ev_bx,ev_by, ev_button_width, ev_BUTTON_HEIGHT) | |||
| love.graphics.setColor(0, 0, 0, 255) | |||
| local textW = smallfont:getWidth(button.text) | |||
| local textH = smallfont:getHeight(button.text) | |||
| love.graphics.print(button.text, smallfont, ev_bx + ev_button_width*0.5 - textW*0.5, ev_by+textH*0.5) | |||
| love.graphics.setColor(255, 255, 255, 255) | |||
| cursor_y = cursor_y + (ev_BUTTON_HEIGHT + margin) | |||
| end | |||
| end | |||
| function mainMenu:addButton(text, fn) | |||
| return { | |||
| text = text, | |||
| fn = fn, | |||
| now = false, | |||
| last = false | |||
| } | |||
| end | |||
| @@ -0,0 +1,27 @@ | |||
| function resolutionChanger() | |||
| if (RESOLUTION_SET > 1) then | |||
| RESOLUTION_SET = 0 | |||
| end | |||
| if (RESOLUTION_SET == 0) then | |||
| if (isFullscreen == 1) then | |||
| DIFFERENCE_X = 1 | |||
| DIFFERENCE_Y = 1 | |||
| OFFSET_X = 0 | |||
| OFFSET_Y = 0 | |||
| simpleScale.updateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, {fullscreen = false}) | |||
| isFullscreen = 0 | |||
| end | |||
| end | |||
| if (RESOLUTION_SET == 1) then | |||
| if (isFullscreen == 0) then | |||
| simpleScale.updateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, {fullscreen = true}) | |||
| local newWidth = love.graphics.getWidth() | |||
| local newHeight = love.graphics.getHeight() | |||
| DIFFERENCE_X = VIRTUAL_WIDTH / newWidth | |||
| DIFFERENCE_Y = VIRTUAL_HEIGHT / newHeight | |||
| OFFSET_X = math.fmod(newWidth, VIRTUAL_WIDTH) / 2 | |||
| OFFSET_Y = math.fmod(newHeight, VIRTUAL_HEIGHT) / 2 | |||
| isFullscreen = 1 | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,272 @@ | |||
| local love11 = love.getVersion() == 11 | |||
| local getDPI = love11 and love.window.getDPIScale or love.window.getPixelScale | |||
| local windowUpdateMode = love11 and love.window.updateMode or function(width, height, settings) | |||
| local _, _, flags = love.window.getMode() | |||
| for k, v in pairs(settings) do flags[k] = v end | |||
| love.window.setMode(width, height, flags) | |||
| end | |||
| local push = { | |||
| defaults = { | |||
| fullscreen = false, | |||
| resizable = false, | |||
| pixelperfect = false, | |||
| highdpi = true, | |||
| canvas = true, | |||
| stencil = true | |||
| } | |||
| } | |||
| setmetatable(push, push) | |||
| function push:applySettings(settings) | |||
| for k, v in pairs(settings) do | |||
| self["_" .. k] = v | |||
| end | |||
| end | |||
| function push:resetSettings() return self:applySettings(self.defaults) end | |||
| function push:setupScreen(WWIDTH, WHEIGHT, RWIDTH, RHEIGHT, settings) | |||
| settings = settings or {} | |||
| self._WWIDTH, self._WHEIGHT = WWIDTH, WHEIGHT | |||
| self._RWIDTH, self._RHEIGHT = RWIDTH, RHEIGHT | |||
| self:applySettings(self.defaults) --set defaults first | |||
| self:applySettings(settings) --then fill with custom settings | |||
| windowUpdateMode(self._RWIDTH, self._RHEIGHT, { | |||
| fullscreen = self._fullscreen, | |||
| resizable = self._resizable, | |||
| highdpi = self._highdpi | |||
| }) | |||
| self:initValues() | |||
| if self._canvas then | |||
| self:setupCanvas({ "default" }) --setup canvas | |||
| end | |||
| self._borderColor = {0, 0, 0} | |||
| self._drawFunctions = { | |||
| ["start"] = self.start, | |||
| ["end"] = self.finish | |||
| } | |||
| return self | |||
| end | |||
| function push:setupCanvas(canvases) | |||
| table.insert(canvases, { name = "_render", private = true }) --final render | |||
| self._canvas = true | |||
| self.canvases = {} | |||
| for i = 1, #canvases do | |||
| push:addCanvas(canvases[i]) | |||
| end | |||
| return self | |||
| end | |||
| function push:addCanvas(params) | |||
| table.insert(self.canvases, { | |||
| name = params.name, | |||
| private = params.private, | |||
| shader = params.shader, | |||
| canvas = love.graphics.newCanvas(self._WWIDTH, self._WHEIGHT), | |||
| stencil = params.stencil or self._stencil | |||
| }) | |||
| end | |||
| function push:setCanvas(name) | |||
| if not self._canvas then return true end | |||
| return love.graphics.setCanvas(self:getCanvasTable(name).canvas) | |||
| end | |||
| function push:getCanvasTable(name) | |||
| for i = 1, #self.canvases do | |||
| if self.canvases[i].name == name then | |||
| return self.canvases[i] | |||
| end | |||
| end | |||
| end | |||
| function push:setShader(name, shader) | |||
| if not shader then | |||
| self:getCanvasTable("_render").shader = name | |||
| else | |||
| self:getCanvasTable(name).shader = shader | |||
| end | |||
| end | |||
| function push:initValues() | |||
| self._PSCALE = (not love11 and self._highdpi) and getDPI() or 1 | |||
| self._SCALE = { | |||
| x = self._RWIDTH/self._WWIDTH * self._PSCALE, | |||
| y = self._RHEIGHT/self._WHEIGHT * self._PSCALE | |||
| } | |||
| if self._stretched then --if stretched, no need to apply offset | |||
| self._OFFSET = {x = 0, y = 0} | |||
| else | |||
| local scale = math.min(self._SCALE.x, self._SCALE.y) | |||
| if self._pixelperfect then scale = math.floor(scale) end | |||
| self._OFFSET = {x = (self._SCALE.x - scale) * (self._WWIDTH/2), y = (self._SCALE.y - scale) * (self._WHEIGHT/2)} | |||
| self._SCALE.x, self._SCALE.y = scale, scale --apply same scale to X and Y | |||
| end | |||
| self._GWIDTH = self._RWIDTH * self._PSCALE - self._OFFSET.x * 2 | |||
| self._GHEIGHT = self._RHEIGHT * self._PSCALE - self._OFFSET.y * 2 | |||
| end | |||
| function push:apply(operation, shader) | |||
| self._drawFunctions[operation](self, shader) | |||
| end | |||
| function push:start() | |||
| if self._canvas then | |||
| love.graphics.push() | |||
| love.graphics.setCanvas({ self.canvases[1].canvas, stencil = self.canvases[1].stencil }) | |||
| else | |||
| love.graphics.translate(self._OFFSET.x, self._OFFSET.y) | |||
| love.graphics.setScissor(self._OFFSET.x, self._OFFSET.y, self._WWIDTH*self._SCALE.x, self._WHEIGHT*self._SCALE.y) | |||
| love.graphics.push() | |||
| love.graphics.scale(self._SCALE.x, self._SCALE.y) | |||
| end | |||
| end | |||
| function push:applyShaders(canvas, shaders) | |||
| local _shader = love.graphics.getShader() | |||
| if #shaders <= 1 then | |||
| love.graphics.setShader(shaders[1]) | |||
| love.graphics.draw(canvas) | |||
| else | |||
| local _canvas = love.graphics.getCanvas() | |||
| local _tmp = self:getCanvasTable("_tmp") | |||
| if not _tmp then --create temp canvas only if needed | |||
| self:addCanvas({ name = "_tmp", private = true, shader = nil }) | |||
| _tmp = self:getCanvasTable("_tmp") | |||
| end | |||
| love.graphics.push() | |||
| love.graphics.origin() | |||
| local outputCanvas | |||
| for i = 1, #shaders do | |||
| local inputCanvas = i % 2 == 1 and canvas or _tmp.canvas | |||
| outputCanvas = i % 2 == 0 and canvas or _tmp.canvas | |||
| love.graphics.setCanvas(outputCanvas) | |||
| love.graphics.clear() | |||
| love.graphics.setShader(shaders[i]) | |||
| love.graphics.draw(inputCanvas) | |||
| love.graphics.setCanvas(inputCanvas) | |||
| end | |||
| love.graphics.pop() | |||
| love.graphics.setCanvas(_canvas) | |||
| love.graphics.draw(outputCanvas) | |||
| end | |||
| love.graphics.setShader(_shader) | |||
| end | |||
| function push:finish(shader) | |||
| love.graphics.setBackgroundColor(unpack(self._borderColor)) | |||
| if self._canvas then | |||
| local _render = self:getCanvasTable("_render") | |||
| love.graphics.pop() | |||
| local white = love11 and 1 or 255 | |||
| love.graphics.setColor(white, white, white) | |||
| --draw canvas | |||
| love.graphics.setCanvas(_render.canvas) | |||
| for i = 1, #self.canvases do --do not draw _render yet | |||
| local _table = self.canvases[i] | |||
| if not _table.private then | |||
| local _canvas = _table.canvas | |||
| local _shader = _table.shader | |||
| self:applyShaders(_canvas, type(_shader) == "table" and _shader or { _shader }) | |||
| end | |||
| end | |||
| love.graphics.setCanvas() | |||
| --draw render | |||
| love.graphics.translate(self._OFFSET.x, self._OFFSET.y) | |||
| local shader = shader or _render.shader | |||
| love.graphics.push() | |||
| love.graphics.scale(self._SCALE.x, self._SCALE.y) | |||
| self:applyShaders(_render.canvas, type(shader) == "table" and shader or { shader }) | |||
| love.graphics.pop() | |||
| --clear canvas | |||
| for i = 1, #self.canvases do | |||
| love.graphics.setCanvas(self.canvases[i].canvas) | |||
| love.graphics.clear() | |||
| end | |||
| love.graphics.setCanvas() | |||
| love.graphics.setShader() | |||
| else | |||
| love.graphics.pop() | |||
| love.graphics.setScissor() | |||
| end | |||
| end | |||
| function push:setBorderColor(color, g, b) | |||
| self._borderColor = g and {color, g, b} or color | |||
| end | |||
| function push:toGame(x, y) | |||
| x, y = x - self._OFFSET.x, y - self._OFFSET.y | |||
| local normalX, normalY = x / self._GWIDTH, y / self._GHEIGHT | |||
| x = (x >= 0 and x <= self._WWIDTH * self._SCALE.x) and normalX * self._WWIDTH or nil | |||
| y = (y >= 0 and y <= self._WHEIGHT * self._SCALE.y) and normalY * self._WHEIGHT or nil | |||
| return x, y | |||
| end | |||
| --doesn't work - TODO | |||
| function push:toReal(x, y) | |||
| return x + self._OFFSET.x, y + self._OFFSET.y | |||
| end | |||
| function push:switchFullscreen(winw, winh) | |||
| self._fullscreen = not self._fullscreen | |||
| local windowWidth, windowHeight = love.window.getDesktopDimensions() | |||
| if self._fullscreen then --save windowed dimensions for later | |||
| self._WINWIDTH, self._WINHEIGHT = self._RWIDTH, self._RHEIGHT | |||
| elseif not self._WINWIDTH or not self._WINHEIGHT then | |||
| self._WINWIDTH, self._WINHEIGHT = windowWidth * .5, windowHeight * .5 | |||
| end | |||
| self._RWIDTH = self._fullscreen and windowWidth or winw or self._WINWIDTH | |||
| self._RHEIGHT = self._fullscreen and windowHeight or winh or self._WINHEIGHT | |||
| self:initValues() | |||
| love.window.setFullscreen(self._fullscreen, "desktop") | |||
| if not self._fullscreen and (winw or winh) then | |||
| windowUpdateMode(self._RWIDTH, self._RHEIGHT) --set window dimensions | |||
| end | |||
| end | |||
| function push:resize(w, h) | |||
| if self._highdpi then w, h = w / self._PSCALE, h / self._PSCALE end | |||
| self._RWIDTH = w | |||
| self._RHEIGHT = h | |||
| self:initValues() | |||
| end | |||
| function push:getWidth() return self._WWIDTH end | |||
| function push:getHeight() return self._WHEIGHT end | |||
| function push:getDimensions() return self._WWIDTH, self._WHEIGHT end | |||
| return push | |||
| @@ -0,0 +1,121 @@ | |||
| local pairs, ipairs, tostring, type, concat, dump, floor, format = pairs, ipairs, tostring, type, table.concat, string.dump, math.floor, string.format | |||
| local function getchr(c) | |||
| return "\\" .. c:byte() | |||
| end | |||
| local function make_safe(text) | |||
| return ("%q"):format(text):gsub('\n', 'n'):gsub("[\128-\255]", getchr) | |||
| end | |||
| local oddvals = {[tostring(1/0)] = '1/0', [tostring(-1/0)] = '-1/0', [tostring(-(0/0))] = '-(0/0)', [tostring(0/0)] = '0/0'} | |||
| local function write(t, memo, rev_memo) | |||
| local ty = type(t) | |||
| if ty == 'number' then | |||
| t = format("%.17g", t) | |||
| return oddvals[t] or t | |||
| elseif ty == 'boolean' or ty == 'nil' then | |||
| return tostring(t) | |||
| elseif ty == 'string' then | |||
| return make_safe(t) | |||
| elseif ty == 'table' or ty == 'function' then | |||
| if not memo[t] then | |||
| local index = #rev_memo + 1 | |||
| memo[t] = index | |||
| rev_memo[index] = t | |||
| end | |||
| return '_[' .. memo[t] .. ']' | |||
| else | |||
| error("Trying to serialize unsupported type " .. ty) | |||
| end | |||
| end | |||
| local kw = {['and'] = true, ['break'] = true, ['do'] = true, ['else'] = true, | |||
| ['elseif'] = true, ['end'] = true, ['false'] = true, ['for'] = true, | |||
| ['function'] = true, ['goto'] = true, ['if'] = true, ['in'] = true, | |||
| ['local'] = true, ['nil'] = true, ['not'] = true, ['or'] = true, | |||
| ['repeat'] = true, ['return'] = true, ['then'] = true, ['true'] = true, | |||
| ['until'] = true, ['while'] = true} | |||
| local function write_key_value_pair(k, v, memo, rev_memo, name) | |||
| if type(k) == 'string' and k:match '^[_%a][_%w]*$' and not kw[k] then | |||
| return (name and name .. '.' or '') .. k ..'=' .. write(v, memo, rev_memo) | |||
| else | |||
| return (name or '') .. '[' .. write(k, memo, rev_memo) .. ']=' .. write(v, memo, rev_memo) | |||
| end | |||
| end | |||
| -- fun fact: this function is not perfect | |||
| -- it has a few false positives sometimes | |||
| -- but no false negatives, so that's good | |||
| local function is_cyclic(memo, sub, super) | |||
| local m = memo[sub] | |||
| local p = memo[super] | |||
| return m and p and m < p | |||
| end | |||
| local function write_table_ex(t, memo, rev_memo, srefs, name) | |||
| if type(t) == 'function' then | |||
| return '_[' .. name .. ']=loadstring' .. make_safe(dump(t)) | |||
| end | |||
| local m = {} | |||
| local mi = 1 | |||
| for i = 1, #t do -- don't use ipairs here, we need the gaps | |||
| local v = t[i] | |||
| if v == t or is_cyclic(memo, v, t) then | |||
| srefs[#srefs + 1] = {name, i, v} | |||
| m[mi] = 'nil' | |||
| mi = mi + 1 | |||
| else | |||
| m[mi] = write(v, memo, rev_memo) | |||
| mi = mi + 1 | |||
| end | |||
| end | |||
| for k,v in pairs(t) do | |||
| if type(k) ~= 'number' or floor(k) ~= k or k < 1 or k > #t then | |||
| if v == t or k == t or is_cyclic(memo, v, t) or is_cyclic(memo, k, t) then | |||
| srefs[#srefs + 1] = {name, k, v} | |||
| else | |||
| m[mi] = write_key_value_pair(k, v, memo, rev_memo) | |||
| mi = mi + 1 | |||
| end | |||
| end | |||
| end | |||
| return '_[' .. name .. ']={' .. concat(m, ',') .. '}' | |||
| end | |||
| return function(t) | |||
| local memo = {[t] = 0} | |||
| local rev_memo = {[0] = t} | |||
| local srefs = {} | |||
| local result = {} | |||
| -- phase 1: recursively descend the table structure | |||
| local n = 0 | |||
| while rev_memo[n] do | |||
| result[n + 1] = write_table_ex(rev_memo[n], memo, rev_memo, srefs, n) | |||
| n = n + 1 | |||
| end | |||
| -- phase 2: reverse order | |||
| for i = 1, n*.5 do | |||
| local j = n - i + 1 | |||
| result[i], result[j] = result[j], result[i] | |||
| end | |||
| -- phase 3: add all the tricky cyclic stuff | |||
| for i, v in ipairs(srefs) do | |||
| n = n + 1 | |||
| result[n] = write_key_value_pair(v[2], v[3], memo, rev_memo, '_[' .. v[1] .. ']') | |||
| end | |||
| -- phase 4: add something about returning the main table | |||
| if result[n]:sub(1, 5) == '_[0]=' then | |||
| result[n] = 'return ' .. result[n]:sub(6) | |||
| else | |||
| result[n + 1] = 'return _[0]' | |||
| end | |||
| -- phase 5: just concatenate everything | |||
| result = concat(result, '\n') | |||
| return n > 1 and 'local _={}\n' .. result or result | |||
| end | |||
| @@ -0,0 +1,123 @@ | |||
| simpleScale = {} | |||
| --Your Game's Aspect Ratio | |||
| local gAspectRatio | |||
| --The Window's Aspect Ratio | |||
| local wAspectRatio | |||
| --The scale between the game and the window's aspect ratio | |||
| simpleScale.scale = 1 | |||
| local xt, yt = 0, 0, 1 | |||
| local gameW, gameH, windowW, windowH = 800, 600, 800, 600 | |||
| -- Declares your game's width and height, and sets the window size/settings | |||
| -- To be used instead of love.window.setMode | |||
| -- [gw] and [gh] are the width and height of the initial game | |||
| -- [sw] and [sh] (optional) are the width and height of the final window | |||
| -- [settings] (optional) are settings for love.window.setMode | |||
| function simpleScale.setWindow(gw, gh, sw, sh, settings) | |||
| sw = sw or gw | |||
| sh = sh or gh | |||
| gAspectRatio = gw/gh | |||
| gameW = gw | |||
| gameH = gh | |||
| simpleScale.updateWindow(sw, sh, settings) | |||
| end | |||
| -- Updates the Window size/settings | |||
| -- To be used instead of love.window.setMode | |||
| -- [sw] and [sh] are the width and height of the new Window | |||
| -- [settings] (optional) are settings for love.window.setMode | |||
| function simpleScale.updateWindow(sw, sh, settings) | |||
| love.window.setMode(sw, sh, settings) | |||
| windowW, windowH = love.graphics.getWidth(), love.graphics.getHeight() | |||
| wAspectRatio = windowW/windowH | |||
| --Window aspect ratio is TALLER than game | |||
| if gAspectRatio > wAspectRatio then | |||
| scale = windowW/gameW | |||
| xt = 0 | |||
| yt = windowH/2 - (scale*gameH)/2 | |||
| --Window aspect ratio is WIDER than game | |||
| elseif gAspectRatio < wAspectRatio then | |||
| scale = windowH/gameH | |||
| xt = windowW/2 - (scale*gameW)/2 | |||
| yt = 0 | |||
| --Window and game aspect ratios are EQUAL | |||
| else | |||
| scale = windowW/gameW | |||
| xt = 0 | |||
| yt = 0 | |||
| end | |||
| simpleScale.scale = scale | |||
| end | |||
| -- If you screen is resizable on drag, you'll need to call this to make sure | |||
| -- the appropriate screen values stay updated | |||
| -- You can call it on love.update() with no trouble | |||
| function simpleScale.resizeUpdate() | |||
| windowW, windowH = love.graphics.getWidth(), love.graphics.getHeight() | |||
| wAspectRatio = windowW/windowH | |||
| --Window aspect ratio is TALLER than game | |||
| if gAspectRatio > wAspectRatio then | |||
| scale = windowW/gameW | |||
| xt = 0 | |||
| yt = windowH/2 - (scale*gameH)/2 | |||
| --Window aspect ratio is WIDER than game | |||
| elseif gAspectRatio < wAspectRatio then | |||
| scale = windowH/gameH | |||
| xt = windowW/2 - (scale*gameW)/2 | |||
| yt = 0 | |||
| --Window and game aspect ratios are EQUAL | |||
| else | |||
| scale = windowW/gameW | |||
| xt = 0 | |||
| yt = 0 | |||
| end | |||
| simpleScale.scale = scale | |||
| end | |||
| -- Transforms the game's window relative to the entire window | |||
| -- Call this at the beginning of love.draw() | |||
| function simpleScale.set() | |||
| love.graphics.push() | |||
| love.graphics.translate(xt, yt) | |||
| love.graphics.scale(scale, scale) | |||
| end | |||
| -- Untransforms the game's window | |||
| -- Call this at the end of love.draw | |||
| -- You can optionally make the letterboxes a specific color by passing | |||
| -- [color] (optional) a table of color values | |||
| function simpleScale.unSet(color) | |||
| love.graphics.scale(1/scale, 1/scale) | |||
| love.graphics.translate(-xt, -yt) | |||
| love.graphics.pop() | |||
| --Draw the Letterboxes | |||
| local r,g,b,a = love.graphics.getColor() | |||
| local originalColor = love.graphics.getColor() | |||
| local boxColor | |||
| if color == nil then | |||
| boxColor = {0,0,0} | |||
| else | |||
| boxColor = color | |||
| end | |||
| love.graphics.setColor(boxColor) | |||
| --Horizontal bars | |||
| if gAspectRatio > wAspectRatio then | |||
| love.graphics.rectangle("fill", 0, 0, windowW, math.abs((gameH*scale - (windowH))/2)) | |||
| love.graphics.rectangle("fill", 0, windowH, windowW, -math.abs((gameH*scale - (windowH))/2)) | |||
| --Vertical bars | |||
| elseif gAspectRatio < wAspectRatio then | |||
| love.graphics.rectangle("fill", 0, 0, math.abs((gameW*scale - (windowW))/2),windowH) | |||
| love.graphics.rectangle("fill", windowW, 0, -math.abs((gameW*scale - (windowW))/2),windowH) | |||
| end | |||
| love.graphics.setColor(r,g,b,a) | |||
| end | |||
| @@ -0,0 +1,70 @@ | |||
| -- tick | |||
| -- https://github.com/bjornbytes/tick | |||
| -- MIT License | |||
| local tick = { | |||
| framerate = nil, | |||
| rate = .03, | |||
| timescale = 1, | |||
| sleep = .001, | |||
| dt = 0, | |||
| accum = 0, | |||
| tick = 1, | |||
| frame = 1 | |||
| } | |||
| local timer = love.timer | |||
| local graphics = love.graphics | |||
| love.run = function() | |||
| if not timer then | |||
| error('love.timer is required for tick') | |||
| end | |||
| if love.load then love.load(love.arg.parseGameArguments(arg), arg) end | |||
| timer.step() | |||
| local lastframe = 0 | |||
| love.update(0) | |||
| return function() | |||
| tick.dt = timer.step() * tick.timescale | |||
| tick.accum = tick.accum + tick.dt | |||
| while tick.accum >= tick.rate do | |||
| tick.accum = tick.accum - tick.rate | |||
| if love.event then | |||
| love.event.pump() | |||
| for name, a, b, c, d, e, f in love.event.poll() do | |||
| if name == 'quit' then | |||
| if not love.quit or not love.quit() then | |||
| return a or 0 | |||
| end | |||
| end | |||
| love.handlers[name](a, b, c, d, e, f) | |||
| end | |||
| end | |||
| tick.tick = tick.tick + 1 | |||
| if love.update then love.update(tick.rate) end | |||
| end | |||
| while tick.framerate and timer.getTime() - lastframe < 1 / tick.framerate do | |||
| timer.sleep(.0005) | |||
| end | |||
| lastframe = timer.getTime() | |||
| if graphics and graphics.isActive() then | |||
| graphics.origin() | |||
| graphics.clear(graphics.getBackgroundColor()) | |||
| tick.frame = tick.frame + 1 | |||
| if love.draw then love.draw() end | |||
| graphics.present() | |||
| end | |||
| timer.sleep(tick.sleep) | |||
| end | |||
| end | |||
| return tick | |||
| @@ -0,0 +1,11 @@ | |||
| state = require("levels/"..gameState) | |||
| print("levels/"..gameState) | |||
| function stateUpdate(dt) | |||
| state = require("levels/"..gameState) | |||
| state.update(dt) | |||
| end | |||
| function stateDraw() | |||
| state.draw() | |||
| end | |||