diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt new file mode 100644 index 0000000..525287a --- /dev/null +++ b/MIT-LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2011 Enrique García Cota + +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. diff --git a/anim8.lua b/anim8.lua new file mode 100644 index 0000000..9969deb --- /dev/null +++ b/anim8.lua @@ -0,0 +1,302 @@ +local anim8 = { + _VERSION = 'anim8 v2.3.1', + _DESCRIPTION = 'An animation library for LÖVE', + _URL = 'https://github.com/kikito/anim8', + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2011 Enrique García Cota + + 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 Grid = {} + +local _frames = {} + +local function assertPositiveInteger(value, name) + if type(value) ~= 'number' then error(("%s should be a number, was %q"):format(name, tostring(value))) end + if value < 1 then error(("%s should be a positive number, was %d"):format(name, value)) end + if value ~= math.floor(value) then error(("%s should be an integer, was %d"):format(name, value)) end +end + +local function createFrame(self, x, y) + local fw, fh = self.frameWidth, self.frameHeight + return love.graphics.newQuad( + self.left + (x-1) * fw + x * self.border, + self.top + (y-1) * fh + y * self.border, + fw, + fh, + self.imageWidth, + self.imageHeight + ) +end + +local function getGridKey(...) + return table.concat( {...} ,'-' ) +end + +local function getOrCreateFrame(self, x, y) + if x < 1 or x > self.width or y < 1 or y > self.height then + error(("There is no frame for x=%d, y=%d"):format(x, y)) + end + local key = self._key + _frames[key] = _frames[key] or {} + _frames[key][x] = _frames[key][x] or {} + _frames[key][x][y] = _frames[key][x][y] or createFrame(self, x, y) + return _frames[key][x][y] +end + +local function parseInterval(str) + if type(str) == "number" then return str,str,1 end + str = str:gsub('%s', '') -- remove spaces + local min, max = str:match("^(%d+)-(%d+)$") + assert(min and max, ("Could not parse interval from %q"):format(str)) + min, max = tonumber(min), tonumber(max) + local step = min <= max and 1 or -1 + return min, max, step +end + +function Grid:getFrames(...) + local result, args = {}, {...} + local minx, maxx, stepx, miny, maxy, stepy + + for i=1, #args, 2 do + minx, maxx, stepx = parseInterval(args[i]) + miny, maxy, stepy = parseInterval(args[i+1]) + for y = miny, maxy, stepy do + for x = minx, maxx, stepx do + result[#result+1] = getOrCreateFrame(self,x,y) + end + end + end + + return result +end + +local Gridmt = { + __index = Grid, + __call = Grid.getFrames +} + +local function newGrid(frameWidth, frameHeight, imageWidth, imageHeight, left, top, border) + assertPositiveInteger(frameWidth, "frameWidth") + assertPositiveInteger(frameHeight, "frameHeight") + assertPositiveInteger(imageWidth, "imageWidth") + assertPositiveInteger(imageHeight, "imageHeight") + + left = left or 0 + top = top or 0 + border = border or 0 + + local key = getGridKey(frameWidth, frameHeight, imageWidth, imageHeight, left, top, border) + + local grid = setmetatable( + { frameWidth = frameWidth, + frameHeight = frameHeight, + imageWidth = imageWidth, + imageHeight = imageHeight, + left = left, + top = top, + border = border, + width = math.floor(imageWidth/frameWidth), + height = math.floor(imageHeight/frameHeight), + _key = key + }, + Gridmt + ) + return grid +end + +----------------------------------------------------------- + +local Animation = {} + +local function cloneArray(arr) + local result = {} + for i=1,#arr do result[i] = arr[i] end + return result +end + +local function parseDurations(durations, frameCount) + local result = {} + if type(durations) == 'number' then + for i=1,frameCount do result[i] = durations end + else + local min, max, step + for key,duration in pairs(durations) do + assert(type(duration) == 'number', "The value [" .. tostring(duration) .. "] should be a number") + min, max, step = parseInterval(key) + for i = min,max,step do result[i] = duration end + end + end + + if #result < frameCount then + error("The durations table has length of " .. tostring(#result) .. ", but it should be >= " .. tostring(frameCount)) + end + + return result +end + +local function parseIntervals(durations) + local result, time = {0},0 + for i=1,#durations do + time = time + durations[i] + result[i+1] = time + end + return result, time +end + +local Animationmt = { __index = Animation } +local nop = function() end + +local function newAnimation(frames, durations, onLoop) + local td = type(durations); + if (td ~= 'number' or durations <= 0) and td ~= 'table' then + error("durations must be a positive number. Was " .. tostring(durations) ) + end + onLoop = onLoop or nop + durations = parseDurations(durations, #frames) + local intervals, totalDuration = parseIntervals(durations) + return setmetatable({ + frames = cloneArray(frames), + durations = durations, + intervals = intervals, + totalDuration = totalDuration, + onLoop = onLoop, + timer = 0, + position = 1, + status = "playing", + flippedH = false, + flippedV = false + }, + Animationmt + ) +end + +function Animation:clone() + local newAnim = newAnimation(self.frames, self.durations, self.onLoop) + newAnim.flippedH, newAnim.flippedV = self.flippedH, self.flippedV + return newAnim +end + +function Animation:flipH() + self.flippedH = not self.flippedH + return self +end + +function Animation:flipV() + self.flippedV = not self.flippedV + return self +end + +local function seekFrameIndex(intervals, timer) + local high, low, i = #intervals-1, 1, 1 + + while(low <= high) do + i = math.floor((low + high) / 2) + if timer >= intervals[i+1] then low = i + 1 + elseif timer < intervals[i] then high = i - 1 + else + return i + end + end + + return i +end + +function Animation:update(dt) + if self.status ~= "playing" then return end + + self.timer = self.timer + dt + local loops = math.floor(self.timer / self.totalDuration) + if loops ~= 0 then + self.timer = self.timer - self.totalDuration * loops + local f = type(self.onLoop) == 'function' and self.onLoop or self[self.onLoop] + f(self, loops) + end + + self.position = seekFrameIndex(self.intervals, self.timer) +end + +function Animation:pause() + self.status = "paused" +end + +function Animation:gotoFrame(position) + self.position = position + self.timer = self.intervals[self.position] +end + +function Animation:pauseAtEnd() + self.position = #self.frames + self.timer = self.totalDuration + self:pause() +end + +function Animation:pauseAtStart() + self.position = 1 + self.timer = 0 + self:pause() +end + +function Animation:resume() + self.status = "playing" +end + +function Animation:draw(image, x, y, r, sx, sy, ox, oy, kx, ky) + love.graphics.draw(image, self:getFrameInfo(x, y, r, sx, sy, ox, oy, kx, ky)) +end + +function Animation:getFrameInfo(x, y, r, sx, sy, ox, oy, kx, ky) + local frame = self.frames[self.position] + if self.flippedH or self.flippedV then + r,sx,sy,ox,oy,kx,ky = r or 0, sx or 1, sy or 1, ox or 0, oy or 0, kx or 0, ky or 0 + local _,_,w,h = frame:getViewport() + + if self.flippedH then + sx = sx * -1 + ox = w - ox + kx = kx * -1 + ky = ky * -1 + end + + if self.flippedV then + sy = sy * -1 + oy = h - oy + kx = kx * -1 + ky = ky * -1 + end + end + return frame, x, y, r, sx, sy, ox, oy, kx, ky +end + +function Animation:getDimensions() + local _,_,w,h = self.frames[self.position]:getViewport() + return w,h +end + +----------------------------------------------------------- + +anim8.newGrid = newGrid +anim8.newAnimation = newAnimation + +return anim8 diff --git a/coin.lua b/coin.lua new file mode 100644 index 0000000..d51cca9 --- /dev/null +++ b/coin.lua @@ -0,0 +1,12 @@ +coins = {} + +function spawnCoin(x, y) + local coin = {} + coin.x = x + coin.y = y + + coin.grid = anim8.newGrid(41, 42, 123, 126) + coin.animation = anim8.newAnimation(coin.grid('1-3', 1, '1-3', 2, '1-2', 3), 0.1) + + table.insert(coins, coin) +end diff --git a/main.lua b/main.lua index 55b735d..2ab79ed 100644 --- a/main.lua +++ b/main.lua @@ -1,6 +1,8 @@ --[[ LOVE STUFF ]] + + function love.load() - myWorld = love.physics.newWorld(0, 500) + myWorld = love.physics.newWorld(0, 500, false) myWorld:setCallbacks(beginContact, endContact, preSolve, postSolve) sprites = {} @@ -9,10 +11,13 @@ function love.load() sprites.player_stand = love.graphics.newImage('sprites/player_stand.png') require('player') + require('coin') + anim8 = require 'anim8' platforms = {} spawnPlatform(50, 500, 300, 30) + spawnCoin(200, 100) DIRECTION_LEFT = -1 DIRECTION_RIGHT = 1 end @@ -20,6 +25,10 @@ end function love.update(dt) myWorld:update(dt) playerUpdate(dt) + + for i,c in ipairs(coins) do + c.animation:update(dt) + end end function love.draw() @@ -33,6 +42,9 @@ function love.draw() for i, p in ipairs(platforms) do love.graphics.rectangle("fill", p.body:getX(), p.body:getY(), p.width, p.height) end + for i, c in ipairs(coins) do + c.animation:draw(sprites.coin_sheet, c.x, c.y) + end end function love.keypressed(key, scancode, isrepeat) diff --git a/player.lua b/player.lua index f6ed798..4324393 100644 --- a/player.lua +++ b/player.lua @@ -3,6 +3,7 @@ player = {} player.body = love.physics.newBody(myWorld, 100, 100, 'dynamic') player.shape = love.physics.newRectangleShape(66, 92) player.fixture = love.physics.newFixture(player.body, player.shape) +player.body:setFixedRotation(true) -- player.sprite = sprites.player_jump player.speed = 200