Commits

Danny Fritz committed f50f0fc

What we got done at the club meeting.

Comments (0)

Files changed (22)

cat.jpg

Added
New image

lib/middleclass-extras/Apply.lua

+-----------------------------------------------------------------------------------
+-- Apply.lua
+-- Enrique GarcĂ­a ( enrique.garcia.cota [AT] gmail [DOT] com ) - 4 Mar 2010
+-- Makes possible to treat all instances of a class
+-----------------------------------------------------------------------------------
+
+assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using Apply')
+assert(Invoker~=nil, 'The Apply module requires the Invoker module in order to work. Please require Invoker before requiring Apply')
+
+--[[ Usage:
+
+  require 'middleclass' -- or similar
+  require 'middleclass-extras.init' -- or 'middleclass-extras'
+
+  MyClass = class('MyClass')
+  MyClass:include(Apply)
+
+  function MyClass:initialize()
+    self.counter = 0
+  end -- the instance will be automatically added to the list here (after initialize)
+
+  function MyClass:count() self.counter = self.counter + 1  end
+  
+  local obj1 = MyClass:new()
+  
+  MyClass:apply('count')
+  
+  local obj2 = MyClass:new()
+  
+  MyClass:apply('count')
+  
+  print(obj1.counter, obj2.counter) -- prints 2   1
+  
+  -- instances will be automatically removed from the list after invoking destroy (obj1:destroy() or obj2:destroy())
+  
+  
+]]
+
+
+--------------------------------
+--      PRIVATE STUFF
+--------------------------------
+
+-- Creates the list of instances for a class
+local function _modifyClass(theClass)
+  theClass._instances = setmetatable({}, {__mode = "kv"})
+end
+
+-- subclasses should also have the _istances list
+local function _modifySubclassMethod(theClass)
+  local prevSubclass = theClass.subclass
+  
+  theClass.subclass = function(aClass, ...)
+    local theSubClass = prevSubclass(aClass, ...)
+    _modifyClass(theSubClass)
+    return theSubClass
+  end
+end
+
+-- Adds an instance to the "list of instances" of its class
+local function _add(theClass, instance)
+  if not includes(Apply, theClass) then return end
+  theClass._instances[instance] = instance
+  _add(theClass.superclass, instance)
+end
+
+-- Removes from the "list of instances"
+local function _remove(theClass, instance)
+  if not includes(Apply, theClass) then return end
+  _remove(theClass.superclass, instance)
+  theClass._instances[instance] = nil
+end
+
+local function _copyTable(t)
+  local copy,i = {},1
+  for _,item in pairs(t) do
+    copy[i] = item
+    i = i + 1
+  end
+  return copy
+end
+
+--------------------------------
+--      PUBLIC STUFF
+--------------------------------
+
+-- The Apply module
+Apply = {}
+
+-- Applies some method to all the instances of this class, including subclasses
+function Apply.apply(theClass, methodOrName, ...)
+  return Apply.applySorted(theClass, nil, methodOrName, ... )
+end
+
+-- Applies some method to all the instances of this class, including subclasses
+-- Notes:
+--   * sortFunc can be provided as a meaning of sorting (table.sort will be used). Can be nil (no order)
+--   * a copy of the instances table is always made so calling removeFromApply is safe inside apply
+function Apply.applySorted(theClass, sortFunc, methodOrName, ...)
+
+  -- this copy is needed in case the invoked function results in an item deletion
+  local instances = _copyTable(theClass._instances)
+
+  if type(sortFunc)=='function' then
+    table.sort(instances, sortFunc)
+  end
+
+  for _,instance in ipairs(instances) do
+    if Invoker.invoke(instance, methodOrName, ...) == false then return false end
+  end
+  return true
+end
+
+--------------------------------
+--      INCLUDED
+--------------------------------
+
+-- modifies the class that includes this module. For internal use only.
+function Apply:included(theClass)
+  if includes(Apply, theClass) then return end
+
+  theClass:include(Callbacks)
+  theClass:before('initialize', function(instance) _add(instance.class, instance) end)
+  theClass:after('destroy', function(instance) _remove(instance.class, instance) end)
+
+  _modifyClass(theClass)
+  _modifySubclassMethod(theClass)
+
+end
+
+
+

lib/middleclass-extras/BSD-LICENSE.txt

+Copyright (c) 2010, Enrique GarcĂ­a Cota
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice, 
+     this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright notice, 
+     this list of conditions and the following disclaimer in the documentation 
+     and/or other materials provided with the distribution.
+  3. Neither the name of middleclass-extras nor the names of its contributors 
+     may be used to endorse or promote products derived from this software 
+     without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.

lib/middleclass-extras/Beholder.lua

+-----------------------------------------------------------------------------------
+-- Beholder.lua
+-- Enrique GarcĂ­a ( enrique.garcia.cota [AT] gmail [DOT] com ) - 4 Mar 2010
+-- Small framework for event observers
+-----------------------------------------------------------------------------------
+
+assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using Beholder')
+assert(Invoker~=nil, 'The Beholder module requires the Invoker module in order to work. Please require Invoker before requiring Beholder')
+assert(Branchy~=nil, 'The Beholder module requires the Branchy module in order to work. Please require Branchy before requiring Beholder')
+
+--[[ Usage:
+
+  require 'middleclass' -- or similar
+  require 'middleclass-extras.init' -- or 'middleclass-extras'
+
+  MyClass = class('MyClass')
+  MyClass:includes(Beholder)
+  function MyClass:foo(x,y) ... end
+  
+  local obj = MyClass:new()
+  
+  -- when the 'newgame' event is fired, call method foo with parameters 100 and 200
+  obj:observe('newgame', 'foo', 100, 200)
+  
+  -- you can add more than one callbacks to the same event:
+  obj:observe('newgame', 'foo', 300, 400)
+  
+  -- alternatively, use a function
+  obj:observe('endgame', function(myself) myself.blah = 0 end)
+  
+  -- trigger the event:
+  Beholder.trigger('newgame')
+  
+  -- stop observing an event:
+  obj:stopObserving('newgame')
+
+
+]]
+
+--------------------------------
+--    PRIVATE NODE CLASS
+--------------------------------
+
+local Node = class('Node'):include(Branchy)
+
+function Node:initialize()
+  self.objects=setmetatable({}, {__mode='k'})
+end
+
+function Node:getOrCreateChild(key)
+  local child = self.children[key]
+  if child == nil then
+    child = self:addChild(Node:new(), key)
+  end
+  return child
+end
+
+function Node:getOrCreateDescendant(key)
+  if type(key) ~= 'table' then return self:getOrCreateChild(key) end
+  local node = self
+  for _,v in ipairs(key) do node = node:getOrCreateChild(v) end
+  return node
+end
+
+function Node:getDescendant(key)
+  if type(key) ~= 'table' then return self.children[key] end
+  local node = self
+  for _,v in ipairs(key) do
+    node = node.children[v]
+    if node == nil then return nil end
+  end
+  return node
+end
+
+function Node:getOrRegisterObject(object)
+  self.objects[object] = self.objects[object] or {}
+  return self.objects[object]
+end
+
+function Node:addAction(object, method, ...)
+  local actions = self:getOrRegisterObject(object)
+  table.insert(actions, { method = method, params = {...} })
+end
+
+function Node:removeAction(object, method)
+  if method == nil then self.objects[object] = nil end
+  local actions = self.objects[object]
+  if actions==nil then return end
+
+  local index = 1
+  for i,v in ipairs(actions) do
+    if v == method then index = i break end
+  end
+
+  if(index~=nil) then table.remove(actions, index) end
+end
+
+
+-- Private variable storing the list of event callbacks that can be used
+--[[ structure:
+  _root = {                      -- root node
+    children = 
+      'a' = {                    -- root->a node
+        children = {
+          'b' = {                -- root->a->b node
+            children = {},
+            objects = {          -- list of objects registered on node root->a->b
+              obj1 = {           -- list of actions to perform on object1
+                { method = 'method1', params = {} },
+                { method = 'method2', params = {1,2}}
+              }
+            }
+          }
+        },
+        objects = {}             -- node root->a does not have any object registered
+      }
+      'b' = {                    -- root->b node
+        children = {},           -- no children nor objects
+        objects = {}
+      }
+    }
+  }
+]]
+local _root = Node:new()
+
+-- The Beholder module
+Beholder = {}
+
+function Beholder:observe(eventId, methodOrName, ...)
+
+  assert(self~=nil, "self is nil. invoke object:observe instead of object.observe")
+  assert(eventId~=nil, "eventId can not be nil")
+  local t = type(methodOrName)
+  assert(t=='string' or t=='function', 'methodOrName must be a function or string')
+
+  local node = _root:getOrCreateDescendant(eventId)
+
+  node:addAction(self, methodOrName, ...)
+end
+
+function Beholder:stopObserving(eventId, methodOrName)
+  local node = _root:getDescendant(eventId)
+  if node==nil then return end
+  node:removeAction(self, methodOrName)
+end
+
+
+--[[ Triggers events
+   Usage:
+     Beholder.trigger('passion.update', dt)
+   All objects that are "observing" passion.update events will get their associated actions called.
+]]
+
+function Beholder.trigger(eventId, ...)
+
+  local node = _root:getDescendant(eventId)
+  if node==nil then return end
+  
+  for object,actions in pairs(node.objects) do
+    for _,action in ipairs(actions) do
+      local params = {}
+      for k,v in ipairs(action.params) do params[k] = v end
+      for _,v in ipairs({...}) do table.insert(params, v) end
+      
+      Invoker.invoke(object, action.method, unpack(params))
+    end
+  end
+end
+

lib/middleclass-extras/Branchy.lua

+-----------------------------------------------------------------------------------
+-- Branchy.lua
+-- Enrique GarcĂ­a ( enrique.garcia.cota [AT] gmail [DOT] com ) - 24 Oct 2010
+-- Allows a class to act like a tree - getting children, ancestors, etc.
+-----------------------------------------------------------------------------------
+
+--[[
+  --Example:
+  --root
+  --  \_ child1
+  --       \_ subchild1
+  --       \_ subchild2
+
+  require 'middleclass' -- or similar
+  require 'middleclass-extras.init' -- or 'middleclass-extras'
+
+  Tree = class('Tree'):include(Branchy)
+  
+  local root = Tree:new()
+  local child1 = root:addChild(Tree:new())
+  local subchild1 = child1:addChild(Tree:new())
+
+  root.parent -- => nil
+  child1.parent -- => root
+  root.children -- => { child1 }
+  root.children[1].children[1] => subchild1
+]]
+
+--------------------------------
+--    PRIVATE STUFF
+--------------------------------
+
+local function _copyTable(collection)
+  local copy, i = {}, 1
+  for _,c in pairs(collection) do
+    copy[i] = c
+    i = i+1
+  end
+  return copy
+end
+
+local function _applySorted(collection, sortFunc, methodOrName, ...)
+  local copy = _copyTable(collection)
+
+  if type(sortFunc)=='function' then
+    table.sort(copy, sortFunc)
+  end
+
+  for i=1, #copy do
+    Invoker.invoke(copy[i], methodOrName, ...)
+  end
+end
+
+local function _modifyAllocateMethod(theClass)
+  local oldAllocate = theClass.allocate
+
+  function theClass.allocate(theClass)
+    local instance = oldAllocate(theClass)
+    instance.children = {}
+    return instance
+  end
+end
+
+
+--------------------------------
+--    PUBLIC STUFF
+--------------------------------
+
+Branchy = {}
+
+-- add a child to the children list
+-- the key parameter is optional. If not given, it will be #(self.children)
+function Branchy:addChild(child, key)
+  assert(includes(Branchy, child.class, true), tostring(child.class) .. " must include Branchy")
+  if child.parent ~= nil then
+    child.parent:removeChild(child)
+  end
+
+  key = key or #self.children + 1
+
+  self:removeChild(self.children[key])
+
+  self.children[key] = child
+  child.parent = self
+  return child
+end
+
+function Branchy:getRoot()
+  local root = self.parent
+  if root == nil then return self end
+  while root.parent ~= nil do root = root.parent end
+  return root
+end
+
+-- gets the position of a child on the children list
+-- returns nil if not found
+function Branchy:getChildKey(child)
+  for k,c in pairs(self.children) do
+    if c==child then return k end
+  end
+end
+
+-- removes a child from the children list
+function Branchy:removeChild(child)
+  local key = self:getChildKey(child)
+
+  if key then
+    child.parent = nil
+    if type(key)=='number' then
+      table.remove(self.children, position)
+    else
+      self.children[key]= nil
+    end
+  end
+end
+
+-- empties the children list
+function Branchy:removeAllChildren()
+  for _,c in pairs(self.children) do c.parent = nil end
+  self.children = {}
+end
+
+-- returns the number of levels that a node has until it reaches root (or self)
+-- returns 0 if root
+function Branchy:getDepth()
+  local level = 0
+  local parent = self.parent
+  while parent~=nil and parent~=self do
+    parent = parent.parent
+    level = level + 1
+  end
+  return level
+end
+
+-- returns a list with { parent, grantparent, ... , root } of a brancy node
+function Branchy:getAncestors()
+  local ancestors = {}
+  
+  local parent = self.parent
+  while parent ~= nil do
+    table.insert(ancestors, parent)
+    parent = parent.parent
+  end
+  
+  return ancestors
+end
+
+-- returns all the children, grandchildren, etc a branchy object
+function Branchy:getDescendants()
+  local descendants = {}
+  for _,child in pairs(self.children) do
+    table.insert(descendants, child)
+    for _,descendant in ipairs(child:getDescendants()) do
+      table.insert(descendants, descendant)
+    end
+  end
+  return descendants
+end
+
+-- applies a method or a function to all children
+function Branchy:applyToChildren(methodOrName, ...)
+  _applySorted(self.children, nil, methodOrName, ...)
+end
+
+-- applies a method to all children, sorting them first
+function Branchy:applyToChildrenSorted(sortFunc, methodOrName, ...)
+  _applySorted(self.children, sortFunc, methodOrName, ...)
+end
+
+-- applies a method or a function to all descendants
+function Branchy:applyToDescendants(methodOrName, ...)
+  _applySorted(self:getDescendants(), nil, methodOrName, ...)
+end
+
+-- applies a method to all descendants, sorting them first
+function Branchy:applyToDescendantsSorted(sortFunc, methodOrName, ...)
+  _applySorted(self:getDescendants(), sortFunc, methodOrName, ...)
+end
+
+-- returns the 'brothers' of a brancy object (children of self.parent that are ~= self)
+function Branchy:getSiblings()
+  local siblings = {}
+  if self.parent then
+    for _,sibling in pairs(self.parent.children) do
+      if sibling ~= self then table.insert(siblings, sibling) end
+    end
+  end
+  return siblings
+end
+
+--------------------------------
+--    INCLUDED callback
+--------------------------------
+
+function Branchy:included(theClass)
+  theClass:include(Callbacks)
+
+  _modifyAllocateMethod(theClass)
+
+  theClass:after('destroy', 'removeAllChildren')
+end
+
+
+
+

lib/middleclass-extras/Callbacks.lua

+-----------------------------------------------------------------------------------
+-- Callbacks.lua
+-- Enrique GarcĂ­a ( enrique.garcia.cota [AT] gmail [DOT] com )
+-- Mixin that adds callbacks support (i.e. beforeXXX or afterYYY) to classes)
+-----------------------------------------------------------------------------------
+
+assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before requiring Callbacks')
+assert(Invoker~=nil, 'Invoker not detected. Please require it before requiring Callbacks')
+--[[ Usage:
+
+  require 'middleclass' -- or similar
+  require 'middleclass-extras.init' -- or 'middleclass-extras'
+  
+  MyClass = class('MyClass')
+  MyClass:include(Callbacks)
+
+  function MyClass:foo() print 'foo' end
+  function MyClass:bar() print 'bar' end
+
+  -- The following lines modify method bar so:
+  MyClass:before('bar', 'foo') -- foo is executed before
+  MyClass:after('bar', function() print('baz') end) -- a function invoking bar is executed after
+
+  local obj = MyClass:new()
+
+  obj:bar() -- prints 'foo bar baz'
+  obj:barWithoutCallbacks() -- prints 'bar'
+
+  -- It is possible to add more callbacks before or after any method
+]]
+
+--------------------------------
+--      PRIVATE STUFF
+--------------------------------
+
+--[[ holds all the callbacks entries.
+     callback entries are just lists of methods to be called before / after some other method is called
+
+  -- m1, m2, m3 & m4 can be method names (strings) or functions
+  _entries = {
+    Actor = {                           -- class
+      update = {                          -- method
+        before = {                          -- 'before' actions
+          { method = m1, params={} },
+          { method = m2, params={'blah', 'bleh'} },
+          { method = m3, params={'foo', 'bar'} }
+        },
+        after = {                           -- 'after' actions
+          { method = 'm4', params={1,2} }
+        }
+
+      }
+    }
+  }
+
+]]
+local _entries = setmetatable({}, {__mode = "k"}) -- weak table
+local _methodCache = setmetatable({}, {__mode = "k"}) -- weak table
+
+local _metamethods = { -- all metamethods except __index
+  '__add', '__call', '__concat', '__div', '__le', '__lt', '__mod', '__mul', '__pow', '__sub', '__tostring', '__unm' 
+}
+
+-- private class methods
+
+local function _getEntry(theClass, methodName)
+  if  _entries[theClass] ~= nil and _entries[theClass][methodName] ~= nil then
+    return _entries[theClass][methodName]
+  end
+end
+
+local function _hasEntry(theClass, methodName)
+  if not includes(Callbacks, theClass) then return false end
+  if _getEntry(theClass, methodName) ~= nil then return true end
+  return _hasEntry(theClass.superclass, methodName)
+end
+
+local function _getOrCreateEntry(theClass, methodName)
+  if  _entries[theClass] == nil then
+    _entries[theClass] = {}
+  end
+  if _entries[theClass][methodName] == nil then
+    _entries[theClass][methodName] = { before = {}, after = {} }
+  end
+  return _entries[theClass][methodName]
+end
+
+--[[
+Returns all the actions that should be called when a callback is invoked, parsing superclasses
+Warning: it returns two separate lists
+Format:
+{ -- before
+  { method='m1', params={1,2} }, 
+  { method='m2', params={3,4} }
+},
+{ -- after
+  { method='m3', params={'a','b'} }, 
+  { method='m4', params={'foo'} }
+}
+]]
+local function _getActions(instance, methodName)
+  local theClass = instance.class
+  local before, after = {}, {}
+  
+  while theClass~=nil do
+    local entry = _getEntry(theClass, methodName)
+    if entry~=nil then
+      for _,action in ipairs(entry.before) do table.insert(before, action) end
+      for _,action in ipairs(entry.after) do table.insert(after, action) end
+    end
+    theClass = theClass.superclass
+  end
+
+  return before, after
+end
+
+-- invokes the 'before' or 'after' actions obtained with _getActions
+function _invokeActions(instance, actions)
+  for _,action in ipairs(actions) do
+    if Invoker.invoke(instance, action.method, unpack(action.params)) == false then return false end
+  end
+end
+
+local function _createCallcackizedMethod(methodName)
+  return function(instance, ...)
+    local before, after = _getActions(instance, methodName)
+
+    if _invokeActions(instance, before) == false then return false end
+
+    local result = { instance[methodName .. 'WithoutCallbacks'](instance, ...) }
+
+    if _invokeActions(instance, after) == false then return false end
+    return unpack(result)
+  end
+end
+
+-- returns a function that executes "method", but with before and after actions
+-- it also does some optimizations. It uses a cache, and returns the method itself when it
+-- doesn't have any entries on the entry list (hence no callbacks)
+local function _callbackizeMethod(theClass, methodName, method)
+
+  if type(method)~='function' or not _hasEntry(theClass, methodName) then return method end
+
+  _methodCache[theClass] = _methodCache[theClass] or {}
+  _methodCache[theClass][method] = _methodCache[theClass][method] or _createCallcackizedMethod(methodName)
+  
+  return _methodCache[theClass][method]
+end
+
+local function _assertFunctionOrTable(classDict)
+  local tcd = type(classDict)
+  assert(tcd == 'function' or tcd == 'table', 'invalid type for an index; must be function or table, was ' .. tcd)
+end
+
+local function _createIndexFunction(theClass, methodName, classDictFunction)
+  return function(instance, methodName)
+    local method = classDictFunction(instance, methodName)
+
+    if method then return _callbackizeMethod(theClass, methodName, method) end
+
+    -- If method not found, test if methoName ends in "WithoutCallbacks".
+    -- If it does yes, return the method without callbacks
+    methodName = methodName:match('(.+)WithoutCallbacks')
+    if methodName ~= nil then return classDictFunction(instance, methodName) end
+  end
+end
+
+local function _buildClassDictFunction(classDict)
+  local classDictFunction = classDict
+  if type(classDict) == 'table' then classDictFunction = function(_, x) return classDict[x] end end
+  return classDictFunction
+end
+
+local function _createInstanceDict(theClass)
+  local classDict = theClass.__classDict
+  _assertFunctionOrTable(classDict)
+
+  -- a copy of classDict, with a modified __index that adds/removes callbacks when needed
+  local instanceDict = {}
+  for k,v in pairs(_metamethods) do instanceDict[k] = v end
+  instanceDict.__index = _createIndexFunction(theClass, methodName, _buildClassDictFunction(classDict))
+  return instanceDict
+end
+
+local function _modifyAllocateMethod(theClass)
+  local oldAllocate = theClass.allocate
+  function theClass.allocate(theClass, ...)
+    return setmetatable(oldAllocate(theClass, ...), _createInstanceDict(theClass))
+  end
+end
+
+local function _modifySubclassMethod(theClass)
+  local prevSubclass = theClass.subclass
+  theClass.subclass = function(aClass, name, ...)
+    local theSubClass = prevSubclass(aClass, name, ...)
+    _modifyAllocateMethod(theSubClass)
+    return theSubClass
+  end
+end
+
+
+function _assertFunctionOrString(callback)
+  local tCallback = type(callback)
+  assert(tCallback == 'string' or tCallback == 'function', 'callback must be a method name or a function')
+end
+
+-- adds callbacks to a method. Used by addCallbacksBefore and addCallbacksAfter, below
+local function _addCallback( theClass, beforeOrAfter, methodName, callback, ...)
+  assert(type(methodName)=='string', 'methodName must be a string')
+  _assertFunctionOrString(callback)
+
+  local entry = _getOrCreateEntry(theClass, methodName)
+
+  table.insert(entry[beforeOrAfter], {method = callback, params = {...}})
+end
+
+--------------------------------
+--      PUBLIC STUFF
+--------------------------------
+
+Callbacks = {}
+
+function Callbacks:included(theClass)
+  if includes(Callbacks, theClass) then return end
+
+  _modifyAllocateMethod(theClass)
+  _modifySubclassMethod(theClass)
+
+end
+
+--[[ before class method
+Usage (the following two are equivalent):
+
+    Actor:before('update', 'doSomething', 1, 2)
+    Actor:before('update', function(actor, x,y) actor:doSomething(x,y) end, 1, 2)
+
+  * methodName must be a string designatign a method (can be non-existing)
+  * callback can be either a method name or a function
+]]
+function Callbacks.before(theClass, methodName, callback, ...)
+  _addCallback( theClass, 'before', methodName, callback, ... )
+end
+
+--Same as before, but for adding callbacks *after* a method
+function Callbacks.after(theClass, methodName, callback, ...)
+  _addCallback( theClass, 'after', methodName, callback, ... )
+end

lib/middleclass-extras/GetterSetter.lua

+-----------------------------------------------------------------------------------
+-- GetterSetter.lua
+-- Enrique GarcĂ­a ( enrique.garcia.cota [AT] gmail [DOT] com ) - 11 Aug 2010
+-- Small mixin for classes with getters and setters
+-----------------------------------------------------------------------------------
+
+--[[ Usage:
+
+  require 'middleclass' -- or similar
+  require 'middleclass-extras.init' -- or 'middleclass-extras'
+
+  MyClass = class('MyClass')
+  MyClass:include(GetterSetter)
+  
+  MyClass:getter('name', 'pete') -- default value
+  MyClass:setter('age')
+  MyClass:getterSetter('color', 'blue') -- default value
+
+]]
+
+assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using GetterSetter')
+
+GetterSetter = {}
+
+function GetterSetter.getterFor(theClass, attr) return 'get' .. attr:gsub("^%l", string.upper) end
+function GetterSetter.setterFor(theClass, attr) return 'set' .. attr:gsub("^%l", string.upper) end
+function GetterSetter.getter(theClass, attributeName, defaultValue)
+  theClass[theClass:getterFor(attributeName)] = function(self)
+    if(self[attributeName]~=nil) then return self[attributeName] end
+    return defaultValue
+  end
+end
+function GetterSetter.setter(theClass, attributeName)
+  theClass[theClass:setterFor(attributeName)] = function(self, value) self[attributeName] = value end
+end
+function GetterSetter.getterSetter(theClass, attributeName, defaultValue)
+  theClass:getter(attributeName, defaultValue)
+  theClass:setter(attributeName)
+end

lib/middleclass-extras/Indexable.lua

+-----------------------------------------------------------------------------------
+-- Indexable.lua
+-- Enrique GarcĂ­a ( enrique.garcia.cota [AT] gmail [DOT] com ) - 11 Aug 2010
+-- mixin that includes index method (*NOT* an __index metamethod) on instances of middleclass
+-----------------------------------------------------------------------------------
+
+--[[ Usage:
+
+  require 'middleclass' -- or similar
+  require 'middleclass-extras.init' -- or 'middleclass-extras'
+
+  MyClass = class('MyClass'):include(Indexable)
+  function MyClass:initialize(a,b,c)
+    self.a, self.b, self.c = a,b,c
+  end
+  
+  function MyClass:index(name) -- attention! index, not __index !
+    return 'could not find ' .. tostring(name)
+  end
+  
+  local x = MyClass:new(1,2,3)
+  
+  print(x.a) -- 1
+  print(x.b) -- 2
+  print(x.c) -- 3
+  print(x.d) -- 'could not find d'
+
+]]
+
+assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using Indexable')
+
+local _metamethods = { -- all metamethods except __index
+  '__add', '__call', '__concat', '__div', '__le', '__lt', '__mod', '__mul', '__pow', '__sub', '__tostring', '__unm' 
+}
+
+
+local function _createInstanceDict(theClass)
+  local classDict = theClass.__classDict
+  local instanceDict = {}
+  
+  for _,mmName in ipairs(_metamethods) do
+    instanceDict[mmName] = function(...) return classDict[mmName](...) end
+  end
+  
+  instanceDict.__index = function(instance, name) return classDict[name] or instance:index(name) end
+  setmetatable(instanceDict, {__index = classDict})
+  return instanceDict
+end
+
+local function _modifyAllocateMethod(theClass)
+  local instanceDict = _createInstanceDict(theClass)
+  local classDict = theClass.__classDict
+
+  rawset(theClass, '__instanceDict', instanceDict)
+  
+  -- modify the instance creator so instances use __instanceDict and not __classDict
+  local oldAllocate = theClass.allocate
+  function theClass.allocate(theClass, ...)
+    return setmetatable(oldAllocate(theClass, ...), theClass.__instanceDict)
+  end
+  
+  return theClass
+end
+
+local function _modifySubclassMethod(theClass)
+  local prevSubclass = theClass.subclass
+  theClass.subclass = function(aClass, name, ...)
+    return _modifyAllocateMethod(prevSubclass(aClass, name, ...))
+  end
+end
+
+Indexable = {}
+
+function Indexable:included(theClass) 
+  if includes(Indexable, theClass) then return end
+  _modifyAllocateMethod(theClass)
+  _modifySubclassMethod(theClass)
+end

lib/middleclass-extras/Invoker.lua

+-----------------------------------------------------------------------------------
+-- Invoker.lua
+-- Enrique GarcĂ­a ( enrique.garcia.cota [AT] gmail [DOT] com ) - 4 Mar 2010
+-- Helper function that simplifies method invocation via method names or functions
+-----------------------------------------------------------------------------------
+
+--[[ Usage:
+
+  require 'middleclass' -- or similar
+  require 'middleclass-extras.init' -- or 'middleclass-extras'
+
+  MyClass = class('MyClass')
+  MyClass:includes(Invoker)
+  function MyClass:foo(x,y) print('foo executed with params', x, y) end
+
+  local obj = MyClass:new()
+
+  obj:invoke('foo', 1,2) -- foo executed with params 1 2
+  obj:invoke( function(self, x, y)
+    print('nameless function executed with params', x, y)
+  , 3, 4) -- nameless function executed with params 3, 4
+  
+  Notes:
+   * The function first parameter must allways be self
+   * You can use Invoker independently: Invoker.invoke(obj, 'method')
+]]
+
+assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using Beholder')
+
+
+local function _invokeString(self, methodName, ...)
+  local method = self[methodName]
+  assert(type(method)=='function', 'Could not find ' .. methodName .. ' in ' .. tostring(self))
+  return method(self, ...)
+end
+
+local function _invokeFunction(self, func, ...)
+  return func(self, ...)
+end
+
+local _functionByType = { ['string'] = _invokeString, ['function'] = _invokeFunction }
+
+Invoker = {
+
+  invoke = function(self, methodOrName, ...)
+    local f = _functionByType[type(methodOrName)]
+    if f then return f(self, methodOrName, ...) end
+
+    error('methodOrName should be either a function or string. It was a '.. type(methodOrName) .. ': ' .. tostring(methodOrName))
+    
+  end
+
+}
+
+

lib/middleclass-extras/Stateful.lua

+-----------------------------------------------------------------------------------
+-- Stateful.lua
+-- Enrique García ( enrique.garcia.cota [AT] gmail [DOT] com ) - 19 Oct 2009
+-- Adds stateful behaviour to classes. Based on Unrealscript's stateful objects
+-----------------------------------------------------------------------------------
+--[[ Usage:
+
+  require 'middleclass' -- or similar
+  require 'middleclass-extras.init' -- or 'middleclass-extras'
+
+  -- create a normal class, and make it implement Stateful
+  MyClass = class('MyClass')
+  MyClass:include(Stateful)
+
+  function MyClass:foo() print('Normal foo') end
+  
+  -- add a state to that class using addState, and re-define the method
+  local Hidden = MyClass:addState('Hidden')
+  function Hidden:foo() print('Hidden foo') end
+  
+  -- create an instance, and test the different behaviour when changing the state
+  local obj = MyClass:new()
+  obj:foo() -- prints 'Normal foo'
+  obj:gotoState('Hidden')
+  obj:foo() -- prints 'Hidden foo'
+
+]]
+
+-- There's much more! States are inherited by subclasses. States can inherit from other states.
+-- States can be stacked
+-- There are hook methods (ex: exitState) called when the state information of an object changes
+
+
+assert(Object~=nil and class~=nil, 'MiddleClass not detected. Please require it before using Stateful')
+
+--[[ Stateful Mixin declaration
+  * Stateful classes have a list of states (accesible through class.states).
+  * When a method is invoked on an instance of such classes, it is first looked up on the class current state (accesible through class.currentState)
+  * If a method is not found on the current state, or if current state is nil, the method is looked up on the class itself
+  * It is possible to change states by doing class:gotoState(stateName)
+]]
+Stateful = {}
+
+------------------------------------
+-- PRIVATE ATTRIBUTES AND METHODS
+------------------------------------
+-- helper function used to call state callbacks (enterState, exitState, etc)
+local function _invokeCallback(self, state, callbackName, ... )
+  if state == nil then return end
+  local callback = state[callbackName]
+  if(type(callback)=='function') then callback(self, ...) end
+end
+
+-- returns the instance's state with the given name. Errors if state not found
+local function _getStateFromClass(self, stateName)
+  if stateName == nil then return nil end
+  local state = self.class.states[stateName]
+  assert(state~=nil, "State '" .. tostring(stateName) .. "' not found")
+  return state
+end
+
+-- looks for a state on the instance stack. Returns the state + position on the stack
+-- if stateName is nil, it returns the top of the stack + stackSize
+local function _getStateFromStack(self, stateName)
+  local stack = self._stateStack
+  local stackSize = #stack
+  if stateName then
+    local state
+    for i=1, stackSize do
+      state = stack[i]
+      if state.name == stateName then 
+        return state, i
+      end
+    end
+  end
+  return stack[stackSize], stackSize
+end
+
+local function _assertString(value, name)
+  assert(type(value)=='string', name .. " must be either a string")
+end
+
+local function _assertStringOrNil(value, name)
+  local tvalue = type(value)
+  assert(tvalue=='string' or tvalue=='nil', name .. " must be either a string or nil")
+end
+
+-- looks for a method "going up" on the stack
+local function _lookUpMethodstatefully(self, methodName)
+  local stack = rawget(self, '_stateStack') -- needs rawget here, else infinite loop with class methods
+  if stack then
+    for i = #stack,1,-1 do -- reversal loop
+      local method = stack[i][methodName]
+      if method ~= nil then return method end
+    end
+  end
+end
+
+-- makes instances to use the stack before "moving up" on the ladder
+local function _modifyClassDictionaryLookup(theClass)
+  local classDict = theClass.__classDict
+  local prevIndex = classDict.__index
+  local tpi = type(prevIndex)
+  classDict.__index = function(instance, methodName)
+    local method = _lookUpMethodstatefully(instance, methodName)
+    if method then return method end
+    if tpi=='function' then return prevIndex(instance, methodName) end
+    return prevIndex[methodName]
+  end
+end
+
+-- adds a _stateStack method to instances, before calling initialize
+local function _modifyClassAllocate(theClass)
+  local oldAllocate = theClass.allocate
+  function theClass.allocate(theClass, ...)
+    local instance = oldAllocate(theClass, ...)
+    instance._stateStack = {} -- adds a stateStack to all instances
+    return instance
+  end
+end
+
+-- Changes a class by:
+-- * adding a 'states' field to it
+-- * re-defining the class __index method so it looks on the state stack before 'going up'
+local function _modifyClass(theClass)
+  theClass.states = {}
+  _modifyClassDictionaryLookup(theClass)
+  _modifyClassAllocate(theClass)
+end
+
+-- makes sure that the subclasses are stateful, and they inherit states
+local function _modifySubclassMethod(theClass)
+  local prevSubclass = theClass.subclass
+  theClass.subclass = function(aClass, name)
+    local theSubClass = prevSubclass(aClass, name)
+
+    _modifyClass(theSubClass)
+
+    -- the states of the subclass are subclasses of the superclass' states
+    for stateName,state in pairs(aClass.states) do
+      theSubClass:addState(stateName, state)
+    end
+
+    return theSubClass
+  end
+end
+
+-- re-define includes so it accepts 'stateful mixins'
+-- stateful mixins can add states to a class. They must have a 'states' field, with mixins inside them.
+-- for each key,value inside mixin.state:
+--   if the class has a state called 'key', make it implement value
+--   else create a new state called 'key' and make it implement value
+local function _modifyIncludeMethod(theClass)
+  local oldInclude = theClass.include
+  theClass.include = function(theClass, module, ...)
+    local states = module.states -- make sure that states are not overriden
+    module.states = nil          -- temporarily removing states from the module
+    oldInclude(theClass, module, ...)
+    if type(states)=="table" then
+      for stateName,moduleState in pairs(states) do 
+        local state = theClass.states[stateName]
+        if state == nil then state = theClass:addState(stateName) end
+        state:include(moduleState, ...)
+      end
+    end
+    module.states = states       -- add states back to module
+    return theClass
+  end
+end
+
+-- true if state is on the stack, false otherwise
+local function _inStack(self, stateName)
+  for i=1, #self._stateStack do
+    local state = self._stateStack[i]
+    if state.name == stateName then return true end
+  end
+  return false
+end
+
+-- the state at the top of the stack
+local function _getTopState(self)
+  return self._stateStack[#self._stateStack]
+end
+
+local function _setTopStateWithoutCallbacks(self, nextState)
+  local stackSize = #self._stateStack
+  local position = stackSize == 0 and 1 or stackSize
+  self._stateStack[position] = nextState
+end
+
+local function _setTopState(self, newStateName)
+  local prevState = _getTopState(self)
+
+  local prevStateName = prevState~=nil and prevState.name or nil
+  _invokeCallback(self, prevState, 'exitState', newStateName)
+
+  local nextState = _getStateFromClass(self, newStateName)
+  _setTopStateWithoutCallbacks(self, nextState)
+
+  _invokeCallback(self, nextState, 'enterState', prevStateName)
+end
+
+------------------------------------
+-- STATE CLASS
+------------------------------------
+
+-- The State class; is the father of all State objects
+Stateful.State = class('Stateful.State')
+
+------------------------------------
+-- INSTANCE METHODS
+------------------------------------
+
+--[[ Changes the current state.
+  If the current state has a method called onExitState, it will be called, with the instance as a parameter.
+  If the "next" state exists and has a method called onExitState, it will be called, with the instance as a parameter.
+  use gotoState(nil) for setting states to nothing
+  This method invokes the exitState and enterState functions if they exist on the current state
+  Second parameter is optional. If true, the stack will be conserved (the top state will be replaced).
+  Otherwise, all the states on the stack will be popped (with the corresponding callbacks being executed).
+]]
+function Stateful:gotoState(newStateName, keepStack)
+  _assertStringOrNil(newStateName, 'newStateName')
+  if(_inStack(self, newStateName)) then return end
+
+  if not keepStack then self:popAllStates() end
+
+  _setTopState(self, newStateName)
+end
+
+--[[ Changes the current state, by pushing a new state on the stack.
+  If the pushed state is already on the stack, this function does nothing.
+  Invokes 'pausedState' on the previous state, if existing
+  The new state is pushed on the top of the stack and then
+  Invokes 'pushedState' and 'enterState' on the new state, if existing
+]]
+function Stateful:pushState(newStateName)
+  _assertString(newStateName, 'newStateName')
+  if(_inStack(self, newStateName)) then return end
+
+  _invokeCallback(self, _getTopState(self), 'pausedState')
+
+  local nextState = _getStateFromClass(self, newStateName)
+  table.insert(self._stateStack, nextState)
+  _invokeCallback(self, nextState, 'pushedState')
+  _invokeCallback(self, nextState, 'enterState')
+
+  return nextState
+end
+
+--[[ Removes a state from the state stack
+   If a state name is given, it will attempt to remove it from the stack. If not found on the stack it will do nothing.
+   If no state name is give, this pops the top state from the stack, if any. Otherwise it does nothing.
+   Callbacks will be called when needed.
+   Returns the length of the state stack after the pop
+]]
+function Stateful:popState(stateName)
+  _assertStringOrNil(stateName, 'stateName')
+
+  local prevState, position = _getStateFromStack(self, stateName)
+
+  if prevState ~= nil then
+    _invokeCallback(self, prevState, 'exitState')
+    _invokeCallback(self, prevState, 'poppedState')
+
+    table.remove(self._stateStack, position)
+
+    if position == #self._stateStack + 1 then
+      _invokeCallback(self, _getTopState(self), 'continuedState')
+    end
+  end
+
+  return #self._stateStack
+end
+
+--[[ Empties the state stack
+   This function will invoke all the popState, exitState callbacks on all the states as they pop out.
+]]
+function Stateful:popAllStates()
+  local sl = self:popState()
+  while sl > 0 do sl = self:popState() end
+end
+
+--[[
+  Returns true if the object is in the state named 'stateName'
+  If testStack true, this method returns true if the state is on the stack instead
+]]
+function Stateful:isInState(stateName, testStack)
+  local state = testStack and _getStateFromStack(self, stateName) or _getTopState(self)
+  if state~=nil and state.name == stateName then return true end
+  return false
+end
+
+-- Returns the name of the state on top of the stack or nil if no state
+function Stateful:getCurrentStateName()
+  local state = _getTopState(self)
+  return state ~= nil and state.name or nil
+end
+
+------------------------------------
+-- CLASS METHODS
+------------------------------------
+
+--[[ Adds a new state to the "states" class member.
+  superState is optional. If nil, State will be the parent class of the new state
+  returns the newly created state, or the existing one if it existed
+]]
+function Stateful.addState(theClass, stateName, superState)
+  assert(includes(Stateful, theClass), "Invalid class. Make sure you used class:addState instead of class.addState")
+  assert(type(stateName)=="string", "stateName must be a string")
+
+  local existingState = theClass.states[stateName]
+  if existingState then return existingState end
+
+  -- states are just regular classes. If superState is nil, this uses State as superClass
+  local superState = superState or theClass.State
+  local state = superState:subclass(stateName, theClass)
+  theClass.states[stateName] = state
+  return state
+end
+
+------------------------------------
+-- INCLUDED
+------------------------------------
+
+-- When the mixin is included by a class, modify it properly
+function Stateful:included(theClass)
+  if includes(Stateful, theClass) then return end
+  
+  _modifyClass(theClass)
+  _modifySubclassMethod(theClass)
+  _modifyIncludeMethod(theClass)
+end

lib/middleclass-extras/init.lua

+-----------------------------------------------------------------------------------------------------------------------
+-- middleclass-extras.lua - v0.8
+-- Enrique GarcĂ­a ( enrique.garcia.cota [AT] gmail [DOT] com ) - 19 Oct 2009
+-- Complementary lib for middleclass
+-----------------------------------------------------------------------------------------------------------------------
+
+local _path = ({...})[1]:gsub("%.init", "")
+local _modules = {
+  'Invoker', 'GetterSetter', 'Branchy', 'Callbacks', 'Apply', 'Beholder', 'Stateful', 'Indexable'
+}
+
+for _,module in ipairs(_modules) do
+  require(_path .. '.' .. module)
+end

lib/middleclass/BSD-LICENSE.txt

+Copyright (c) 2010, Enrique GarcĂ­a Cota
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice, 
+     this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright notice, 
+     this list of conditions and the following disclaimer in the documentation 
+     and/or other materials provided with the distribution.
+  3. Neither the name of MiddleClass nor the names of its contributors 
+     may be used to endorse or promote products derived from this software 
+     without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+

lib/middleclass/init.lua

+local _path = ({...})[1]:gsub("%.init", "")
+
+require( _path .. '.middleclass' )

lib/middleclass/middleclass.lua

+-----------------------------------------------------------------------------------------------------------------------
+-- middleclass.lua - v1.4 (2011-03)
+-- Enrique García Cota - enrique.garcia.cota [AT] gmail [DOT] com
+-- Based on YaciCode, from Julien Patte and LuaObject, from Sébastien Rocca-Serra
+-----------------------------------------------------------------------------------------------------------------------
+
+local _nilf = function() end -- empty function
+
+local _classes = setmetatable({}, {__mode = "k"})   -- keeps track of all the tables that are classes
+
+Object = { name = "Object", __modules = {} }
+
+Object.__classDict = {
+  initialize = _nilf, destroy = _nilf, subclassed = _nilf,
+  __tostring = function(instance) return ("instance of ".. instance.class.name) end, -- root of __tostring method,
+  __metamethods = { '__add', '__call', '__concat', '__div', '__le', '__lt', 
+    '__mod', '__mul', '__pow', '__sub', '__tostring', '__unm' 
+  }
+}
+Object.__classDict.__index = Object.__classDict -- instances of Object need this
+
+setmetatable(Object, { 
+  __index = Object.__classDict,    -- look up methods in the classDict
+  __newindex = Object.__classDict, -- any new Object methods will be defined in classDict
+  __call = Object.new,             -- allows instantiation via Object()
+  __tostring = function() return "class Object" end -- allows tostring(obj)
+})
+
+_classes[Object] = true -- register Object on the list of classes.
+
+-- creates the instance based of the class, but doesn't initialize it
+function Object.allocate(theClass)
+  assert(_classes[theClass], "Use Class:allocate instead of Class.allocate")
+  return setmetatable({ class = theClass }, theClass.__classDict)
+end
+
+-- both creates and initializes an instance
+function Object.new(theClass, ...)
+  local instance = theClass:allocate()
+  instance:initialize(...)
+  return instance
+end
+
+-- creates a subclass
+function Object.subclass(theClass, name)
+  assert(_classes[theClass], "Use Class:subclass instead of Class.subclass")
+  assert( type(name)=="string", "You must provide a name(string) for your class")
+
+  local theSubClass = { name = name, superclass = theClass, __classDict = {}, __modules={} }
+  
+  local dict = theSubClass.__classDict   -- classDict contains all the [meta]methods of the class
+  dict.__index = dict                    -- It "points to itself" so instances can use it as a metatable.
+  local superDict = theClass.__classDict -- The superclass' classDict
+
+  setmetatable(dict, superDict) -- when a method isn't found on classDict, 'escalate upwards'.
+
+  setmetatable(theSubClass, {
+    __index = dict,                              -- look for stuff on the dict
+    __newindex = function(_, methodName, method) -- ensure that __index isn't modified by mistake
+        assert(methodName ~= '__index', "Can't modify __index. Include middleclass-extras.Indexable and use 'index' instead")
+        rawset(dict, methodName , method)
+      end,
+    __tostring = function() return ("class ".. name) end,      -- allows tostring(MyClass)
+    __call = function(_, ...) return theSubClass:new(...) end  -- allows MyClass(...) instead of MyClass:new(...)
+  })
+
+  for _,mmName in ipairs(theClass.__metamethods) do -- Creates the initial metamethods
+    dict[mmName]= function(...)           -- by default, they just 'look up' for an implememtation
+      local method = superDict[mmName]    -- and if none found, they throw an error
+      assert( type(method)=='function', tostring(theSubClass) .. " doesn't implement metamethod '" .. mmName .. "'" )
+      return method(...)
+    end
+  end
+
+  theSubClass.initialize = function(instance,...) theClass.initialize(instance, ...) end
+  _classes[theSubClass]= true -- registers the new class on the list of _classes
+  theClass:subclassed(theSubClass)   -- hook method. By default it does nothing
+
+  return theSubClass
+end
+
+-- Mixin extension function - simulates very basically ruby's include. Receives a table table, probably with functions.
+-- Its contents are copied to theClass, with one exception: the included() method will be called instead of copied
+function Object.include(theClass, module, ... )
+  assert(_classes[theClass], "Use class:include instead of class.include")
+  assert(type(module)=='table', "module must be a table")
+  for methodName,method in pairs(module) do
+    if methodName ~="included" then theClass[methodName] = method end
+  end
+  if type(module.included)=="function" then module:included(theClass, ... ) end
+  theClass.__modules[module] = module
+  return theClass
+end
+
+-- Returns true if aClass is a subclass of other, false otherwise
+function subclassOf(other, aClass)
+  if not _classes[aClass] or not _classes[other] then return false end
+  if aClass.superclass==nil then return false end -- aClass is Object, or a non-class
+  return aClass.superclass == other or subclassOf(other, aClass.superclass)
+end
+
+-- Returns true if obj is an instance of aClass (or one of its subclasses) false otherwise
+function instanceOf(aClass, obj)
+  if not _classes[aClass] or type(obj)~='table' or not _classes[obj.class] then return false end
+  if obj.class==aClass then return true end
+  return subclassOf(aClass, obj.class)
+end
+
+-- Returns true if the a module has already been included on a class (or a superclass of that class)
+function includes(module, aClass)
+  if not _classes[aClass] then return false end
+  if aClass.__modules[module]==module then return true end
+  return includes(module, aClass.superclass)
+end
+
+-- Creates a new class named 'name'. Uses Object if no baseClass is specified. Additional parameters for compatibility
+function class(name, baseClass, ...)
+  baseClass = baseClass or Object
+  return baseClass:subclass(name, ...)
+end

llama.gif

Added
New image
+
+Ball = class("Ball")
+
+function Ball:initialize()
+	self.x = 400
+	self.y = 500
+	self.collied = false
+	self.velocity_x = 0
+	self.velocity_y = 0
+	self.radius = 10
+end
+
+function Ball:update(dt)
+	self.collided = false
+	self.x = self.x + self.velocity_x*dt
+	self.y = self.y + self.velocity_y*dt
+	if self.x >= 800-self.radius or self.x <= self.radius then
+		self.velocity_x = -self.velocity_x
+	end
+	if self.y <= self.radius then
+		self.velocity_y = -self.velocity_y
+	end
+	if self.y >= 600 then
+		self.x = 400
+		self.y = 500
+		self.collied = false
+		self.velocity_x = 0
+		self.velocity_y = 0
+		self.radius = 10
+	end
+end
+
+function Ball:draw()
+	love.graphics.circle('fill', self.x, self.y, self.radius, 32)
+end
+
+function Ball:collide_with_rect(rect)
+	if self.x-self.radius <= rect.x+rect.width and
+			self.x+self.radius >= rect.x and
+			self.y-self.radius <= rect.y+rect.height and
+			self.y+self.radius >= rect.y then
+		return true
+	end
+	return false
+end
+
+Block = class("Block")
+
+function Block:initialize(x, y, color)
+	self.alive = true
+	self.width = 32
+	self.height = 12
+	self.color = color
+	self.x = x
+	self.y = y
+end
+
+function Block:update(dt, ball)
+	if ball:collide_with_rect(self) and not ball.collided then
+		ball.collided = true
+		ball.velocity_y = -ball.velocity_y
+		self:explode()
+	end
+end
+
+function Block:explode()
+	self.alive = false
+end
+
+function Block:draw()
+	love.graphics.setColor(0,0,0)
+	love.graphics.rectangle('fill', self.x, self.y, self.width, self.height)
+	love.graphics.setColor(unpack(self.color))
+	love.graphics.rectangle('fill', self.x-1, self.y-1, self.width-2, self.height-2)
+	love.graphics.setColor(255,255,255)
+end
+--[[----------------------------------------------------------------------------
+Copyright (c) 2011
+Programmer: Danny Fritz <dannyfritz@gmail.com>
+Artist: Josh Brinkley <computercodemonkey@gmx.com>
+http://creativecommons.org/licenses/by/3.0/
+Licensed under the Creative Commons Attribution 3.0 Unported License.
+--]]----------------------------------------------------------------------------
+
+--[[ Menu:
+
+	This is the screen presented when the game is started up.
+	It presents the user with choices.
+
+]]
+
+require 'lua.Paddle'
+require 'lua.Block'
+require 'lua.Ball'
+
+local music = love.audio.newSource("nyan-looped.mp3", "stream")
+music:setLooping(true)
+
+local blocks = {}
+local ball
+
+Breakout = Gamestate:addState("Breakout")
+
+--------------------------------------------------------------------------------
+function Breakout:enterState()
+	paddle = Paddle:new()
+	ball = Ball:new()
+	createBlocks()
+end
+
+function createBlocks()
+	local image = love.image.newImageData('llama.gif')
+	local width, height = image:getWidth(), image:getHeight()
+	for i=0, 25-1 do
+		for j=0, 30-1 do
+			local color = {image:getPixel(width/25*i+width/25/2, height/30*j+height/30/2)}
+			local average_color = (color[1] + color[2] + color[3]) / 3
+			if average_color > 20 then
+				table.insert(blocks, Block:new(i*32,j*12, color))
+			end
+		end
+	end
+end
+
+--------------------------------------------------------------------------------
+function Breakout:exitState()
+	
+end
+
+--------------------------------------------------------------------------------
+function Breakout:update(dt)
+	ball:update(dt)
+	paddle:update(dt, ball)
+	for i=1, #blocks do
+		blocks[i]:update(dt, ball)
+	end
+	for i=#blocks, 1, -1 do
+		if not blocks[i].alive then
+			table.remove(blocks, i)
+		end
+	end
+end
+
+--------------------------------------------------------------------------------
+function Breakout:draw()
+	paddle:draw()
+	ball:draw()
+	for i=1, #blocks do
+		blocks[i]:draw()
+	end
+end
+
+--------------------------------------------------------------------------------
+function Breakout:keypressed(key)
+	if key == 'escape' then
+		love.event.push('q')
+	end
+	if key == ' ' then
+		music:play()
+		ball.velocity_x = 200
+		ball.velocity_y = -200
+	end
+end
+
+--------------------------------------------------------------------------------
+function Breakout:mousepressed(x, y, button)
+	
+end

lua/Gamestate.lua

+--[[----------------------------------------------------------------------------
+Copyright (c) 2011
+Programmer: Danny Fritz <dannyfritz@gmail.com>
+Artist: Josh Brinkley <computercodemonkey@gmx.com>
+http://creativecommons.org/licenses/by/3.0/
+Licensed under the Creative Commons Attribution 3.0 Unported License.
+--]]----------------------------------------------------------------------------
+
+--[[ Gamestate:
+
+	This class controls the current state of the program.
+
+]]
+
+Gamestate = class('Gamestate'):include(Stateful)
+
+require 'lua.Breakout'
+
+--------------------------------------------------------------------------------
+function Gamestate:initialize()
+	--FIRST RUN
+	--STAT FILE
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:update(dt)
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:draw()
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:focus(f)
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:joystickpressed(joystick, button)
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:joystickreleased(joystick, button)
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:keypressed(key, unicode)
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:keyreleased(key)
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:mousepressed(x, y, button)
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:mousereleased(x, y, button)
+end
+
+--------------------------------------------------------------------------------
+function Gamestate:quit()
+end
+
+Paddle = class("Paddle")
+
+function Paddle:initialize()
+	self.width = 75
+	self.height = 15
+	self.speed = 500
+	self.x = 400
+	self.y = 600-self.height-3
+end
+
+function Paddle:update(dt, ball)
+	if ball:collide_with_rect(self) and not ball.collided then
+		if ball.x == self.x then
+			ball.x = ball.x + 0.001
+		end
+		ball.collided = true
+		local magnitude = math.sqrt(math.pow(ball.velocity_x,2)+math.pow(ball.velocity_y,2))*1.05
+		ball.velocity_x = ((((ball.x-self.x)/self.width) - 0.5 ) * 2) * magnitude
+		ball.velocity_y = -math.abs(math.sqrt(math.pow(magnitude,2)-math.pow(ball.velocity_x,2)))
+		print(ball.velocity_x, ball.velocity_y)
+	end
+	if love.keyboard.isDown('left') then
+		self.x = self.x - self.speed * dt
+	elseif love.keyboard.isDown('right') then
+		self.x = self.x + self.speed * dt
+	end
+	if self.x > 800 - self.width then
+		self.x = 800 -self.width
+	elseif self.x < 0 then
+		self.x = 0
+	end
+end
+
+function Paddle:draw()
+	love.graphics.rectangle('fill', self.x, self.y, self.width, self.height)
+end
+--[[----------------------------------------------------------------------------
+Copyright (c) 2011
+Programmer: Danny Fritz <dannyfritz@gmail.com>
+Artist: Josh Brinkley <computercodemonkey@gmx.com>
+http://creativecommons.org/licenses/by/3.0/
+Licensed under the Creative Commons Attribution 3.0 Unported License.
+--]]----------------------------------------------------------------------------
+
+require 'lib.middleclass'
+require 'lib.middleclass-extras'
+
+require 'lua.Gamestate'
+
+math.randomseed(os.time())
+math.random(); math.random();
+
+--------------------------------------------------------------------------------
+function love.load()
+	gamestate = Gamestate:new()
+	love.graphics.setFont(32)
+	gamestate:gotoState('Breakout')
+end
+
+--------------------------------------------------------------------------------
+function love.update(dt)
+	gamestate:update(dt)
+end
+
+--------------------------------------------------------------------------------
+function love.draw()
+	gamestate:draw()
+end
+
+--------------------------------------------------------------------------------
+function love.focus(f)
+	gamestate:focus(f)
+end
+
+--------------------------------------------------------------------------------
+function love.joystickpressed(joystick, button)
+	gamestate:joystickpressed(joystick, button)
+end
+
+--------------------------------------------------------------------------------
+function love.joystickreleased(joystick, button)
+	gamestate:joystickreleased(joystick, button)
+end
+
+--------------------------------------------------------------------------------
+function love.keypressed(key, unicode)
+	gamestate:keypressed(key, unicode)
+end
+
+--------------------------------------------------------------------------------
+function love.keyreleased(key)
+	gamestate:keyreleased(key)
+end
+
+--------------------------------------------------------------------------------
+function love.mousepressed(x, y, button)
+	gamestate:mousepressed(x, y, button)
+end
+
+--------------------------------------------------------------------------------
+function love.mousereleased(x, y, button)
+	gamestate:mousereleased(x, y, button)
+end
+
+--------------------------------------------------------------------------------
+function love.quit()
+	gamestate:quit()
+end

nyan-looped.mp3

Binary file added.