Lutro - Documentation

Useful libraries

This is a list of small lua libraries that work well for lutro games.

anim.lua

A minimal animation library.

  local animation = {}
  animation.__index = animation
  
  function NewAnimation(image, width, height, period, speed)
    local a = {}
    a.image = image
    a.timer = 0
    a.width = width
    a.height = height
    a.playing = true
    a.speed = speed
    a.period = period
    a.steps = a.image:getWidth() / a.width
    return setmetatable(a, animation)
  end
  
  function animation:update(dt)
    if not self.playing then return end
  
    self.timer = self.timer + dt * self.speed
  
    if self.timer > self.steps * self.period then
      self.timer = 0
    end
  end
  
  function animation:draw(x, y)
    local id = math.floor(self.timer / self.period + 1)
    local tw = self.width
    local th = self.height
    local sw = self.image:getWidth()
    local sh = self.image:getHeight()
  
    local q = love.graphics.newQuad(
      ((id-1)%(sw/tw))*tw,
      0,
      tw, th,
      sw, sh)
  
    love.graphics.draw(
      self.image,
      q,
      math.floor(x), math.floor(y)
    )
  end

Usage:

require "libs/anim"

function love.load()
  local IMG_shizuka_stand_left = love.graphics.newImage("assets/spr/shizuka_stand_left.png")
  local anim = NewAnimation(IMG_shizuka_stand_left, 24, 24, 1, 20)
end

function love.update(dt)
  anim:update(dt)
end

function love.draw()
  anim:draw(16, 32)
end

slam.lua

Simple LÖVE Audio Manager. Makes it easier to play the same audio effect concurrently.

  -- Simple LÖVE Audio Manager
  --
  -- Copyright (c) 2011 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 newInstance = love.audio.newSource
  local stop = love.audio.stop
  
  ------------------
  -- source class --
  ------------------
  local Source = {}
  Source.__index = Source
  Source.__newindex = function(_,k) error(('Cannot write key %s'):format(tostring(k))) end
  
  function NewSource(target, how)
    local s = {
      _paused   = false,
      target    = target,
      how       = how,
      instances = {},
      looping   = false,
      pitch     = 1,
      volume    = 1,
    }
    if how == 'static' and type(target) == 'string' then
      s.target = love.sound.newSoundData(target)
    end
    return setmetatable(s, Source)
  end
  
  local function remove_stopped(sources)
    local remove = {}
    for s in pairs(sources) do
      remove[s] = true
    end
    for s in pairs(remove) do
      sources[s] = nil
    end
  end
  
  local function get_target(target)
    if type(target) == 'table' then
      return target[math.random(1,#target)]
    end
    return target
  end
  
  local play_instance, stop_instance
  function Source:play()
    remove_stopped(self.instances)
    if self._paused then self:stop() end
    local instance = newInstance(get_target(self.target), self.how)
  
    -- overwrite instance:stop() and instance:play()
    if not (play_instance and stop_instance) then
      play_instance = getmetatable(instance).play
      getmetatable(instance).play = error
  
      stop_instance = getmetatable(instance).stop
      getmetatable(instance).stop = function(this)
        stop_instance(this)
        self.instances[this] = nil
      end
    end
  
    instance:setLooping(self.looping)
    instance:setPitch(self.pitch)
    instance:setVolume(self.volume)
  
    self.instances[instance] = instance
    play_instance(instance)
    return instance
  end
  
  function Source:stop()
    for s in pairs(self.instances) do
      s:stop()
    end
    self._paused = false
    self.instances = {}
  end
  
  function Source:pause()
    if self._paused then return end
    for s in pairs(self.instances) do
      s:pause()
    end
    self._paused = true
  end
  
  function Source:resume()
    if not self._paused then return end
    for s in pairs(self.instances) do
      s:resume()
    end
    self._paused = false
  end
  
  function Source:isStatic()
    return self.how ~= "stream"
  end
  
  function Source:isPlaying()
    for s in pairs(self.instances) do
      return s:isPlaying()
    end
  end
  
  function Source:tell(what)
    for s in pairs(self.instances) do
      return s:tell(what)
    end
  end
  
  function Source:seek(n, what)
    for s in pairs(self.instances) do
      return s:seek(n, what)
    end
  end
  
  -- getter/setter for looping, pitch and volume
  for _, property in ipairs{'looping', 'pitch', 'volume'} do
    local name = property:sub(1,1):upper() .. property:sub(2)
    Source['get' .. name] = function(self)
      return self[property]
    end
  
    Source['set' .. name] = function(self, val)
      self[property] = val
      for s in pairs(self.instances) do
        s['set' .. name](s, val)
      end
    end
  end
  Source.isLooping = Source.getLooping  

Usage:

require "libs/slam"

function love.load()
  SFX_ok = NewSource("assets/sfx/ok.wav", "static")
end

function love.update(dt)
  SFX_ok:play()
end