Change middleclass to hump
This commit is contained in:
parent
ae5ed1747c
commit
72dd42119d
11 changed files with 1057 additions and 182 deletions
47
hump/README.md
Normal file
47
hump/README.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
HUMP - Helper Utilities for Massive Progression
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
__HUMP__ is a small collection of tools for developing games with LÖVE.
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
------------
|
||||||
|
|
||||||
|
* *gamestate.lua*: Easy gamestate management.
|
||||||
|
* *timer.lua*: Delayed and time-limited function calls and tweening functionality.
|
||||||
|
* *vector.lua*: 2D vector math.
|
||||||
|
* *vector-light.lua*: Lightweight 2D vector math (for optimisation purposes - leads to potentially ugly code).
|
||||||
|
* *class.lua*: Lightweight object orientation (class or prototype based).
|
||||||
|
* *signal.lua*: Simple Signal/Slot (aka. Observer) implementation.
|
||||||
|
* *camera.lua*: Move-, zoom- and rotatable camera.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
=============
|
||||||
|
|
||||||
|
You can find the documentation here: [http://vrld.github.com/hump/](http://vrld.github.com/hump/)
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
> 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.
|
117
hump/camera.lua
Normal file
117
hump/camera.lua
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
--[[
|
||||||
|
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 _PATH = (...):match('^(.*[%./])[^%.%/]+$') or ''
|
||||||
|
local cos, sin = math.cos, math.sin
|
||||||
|
|
||||||
|
local camera = {}
|
||||||
|
camera.__index = camera
|
||||||
|
|
||||||
|
local function new(x,y, zoom, rot)
|
||||||
|
x,y = x or love.graphics.getWidth()/2, y or love.graphics.getHeight()/2
|
||||||
|
zoom = zoom or 1
|
||||||
|
rot = rot or 0
|
||||||
|
return setmetatable({x = x, y = y, scale = zoom, rot = rot}, camera)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:lookAt(x,y)
|
||||||
|
self.x, self.y = x,y
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:move(x,y)
|
||||||
|
self.x, self.y = self.x + x, self.y + y
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:pos()
|
||||||
|
return self.x, self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:rotate(phi)
|
||||||
|
self.rot = self.rot + phi
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:rotateTo(phi)
|
||||||
|
self.rot = phi
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:zoom(mul)
|
||||||
|
self.scale = self.scale * mul
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:zoomTo(zoom)
|
||||||
|
self.scale = zoom
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:attach()
|
||||||
|
local cx,cy = love.graphics.getWidth()/(2*self.scale), love.graphics.getHeight()/(2*self.scale)
|
||||||
|
love.graphics.push()
|
||||||
|
love.graphics.scale(self.scale)
|
||||||
|
love.graphics.translate(cx, cy)
|
||||||
|
love.graphics.rotate(self.rot)
|
||||||
|
love.graphics.translate(-self.x, -self.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:detach()
|
||||||
|
love.graphics.pop()
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:draw(func)
|
||||||
|
self:attach()
|
||||||
|
func()
|
||||||
|
self:detach()
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:cameraCoords(x,y)
|
||||||
|
-- x,y = ((x,y) - (self.x, self.y)):rotated(self.rot) * self.scale + center
|
||||||
|
local w,h = love.graphics.getWidth(), love.graphics.getHeight()
|
||||||
|
local c,s = cos(self.rot), sin(self.rot)
|
||||||
|
x,y = x - self.x, y - self.y
|
||||||
|
x,y = c*x - s*y, s*x + c*y
|
||||||
|
return x*self.scale + w/2, y*self.scale + h/2
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:worldCoords(x,y)
|
||||||
|
-- x,y = (((x,y) - center) / self.scale):rotated(-self.rot) + (self.x,self.y)
|
||||||
|
local w,h = love.graphics.getWidth(), love.graphics.getHeight()
|
||||||
|
local c,s = cos(-self.rot), sin(-self.rot)
|
||||||
|
x,y = (x - w/2) / self.scale, (y - h/2) / self.scale
|
||||||
|
x,y = c*x - s*y, s*x + c*y
|
||||||
|
return x+self.x, y+self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:mousepos()
|
||||||
|
return self:worldCoords(love.mouse.getPosition())
|
||||||
|
end
|
||||||
|
|
||||||
|
-- the module
|
||||||
|
return setmetatable({new = new},
|
||||||
|
{__call = function(_, ...) return new(...) end})
|
94
hump/class.lua
Normal file
94
hump/class.lua
Normal file
|
@ -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 not to[k] 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
|
||||||
|
local inc = class.__includes or {}
|
||||||
|
if getmetatable(inc) then inc = {inc} end
|
||||||
|
|
||||||
|
for _, other in ipairs(inc) do
|
||||||
|
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})
|
97
hump/gamestate.lua
Normal file
97
hump/gamestate.lua
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
--[[
|
||||||
|
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 __NULL__() end
|
||||||
|
|
||||||
|
-- default gamestate produces error on every callback
|
||||||
|
local state_init = setmetatable({leave = __NULL__},
|
||||||
|
{__index = function() error("Gamestate not initialized. Use Gamestate.switch()") end})
|
||||||
|
local stack = {state_init}
|
||||||
|
|
||||||
|
local GS = {}
|
||||||
|
function GS.new(t) return t or {} end -- constructor - deprecated!
|
||||||
|
|
||||||
|
function GS.switch(to, ...)
|
||||||
|
assert(to, "Missing argument: Gamestate to switch to")
|
||||||
|
assert(to ~= GS, "Can't call switch with colon operator")
|
||||||
|
local pre = stack[#stack]
|
||||||
|
;(pre.leave or __NULL__)(pre)
|
||||||
|
;(to.init or __NULL__)(to)
|
||||||
|
to.init = nil
|
||||||
|
stack[#stack] = to
|
||||||
|
return (to.enter or __NULL__)(to, pre, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function GS.push(to, ...)
|
||||||
|
assert(to, "Missing argument: Gamestate to switch to")
|
||||||
|
assert(to ~= GS, "Can't call push with colon operator")
|
||||||
|
local pre = stack[#stack]
|
||||||
|
;(to.init or __NULL__)(to)
|
||||||
|
to.init = nil
|
||||||
|
stack[#stack+1] = to
|
||||||
|
return (to.enter or __NULL__)(to, pre, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function GS.pop(...)
|
||||||
|
assert(#stack > 1, "No more states to pop!")
|
||||||
|
local pre = stack[#stack]
|
||||||
|
stack[#stack] = nil
|
||||||
|
;(pre.leave or __NULL__)(pre)
|
||||||
|
return (stack[#stack].resume or __NULL__)(pre, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function GS.current()
|
||||||
|
return stack[#stack]
|
||||||
|
end
|
||||||
|
|
||||||
|
local all_callbacks = {
|
||||||
|
'draw', 'errhand', 'focus', 'keypressed', 'keyreleased', 'mousefocus',
|
||||||
|
'mousepressed', 'mousereleased', 'quit', 'resize', 'textinput',
|
||||||
|
'threaderror', 'update', 'visible', 'gamepadaxis', 'gamepadpressed',
|
||||||
|
'gamepadreleased', 'joystickadded', 'joystickaxis', 'joystickhat',
|
||||||
|
'joystickpressed', 'joystickreleased', 'joystickremoved'
|
||||||
|
}
|
||||||
|
|
||||||
|
function GS.registerEvents(callbacks)
|
||||||
|
local registry = {}
|
||||||
|
callbacks = callbacks or all_callbacks
|
||||||
|
for _, f in ipairs(callbacks) do
|
||||||
|
registry[f] = love[f] or __NULL__
|
||||||
|
love[f] = function(...)
|
||||||
|
registry[f](...)
|
||||||
|
return GS[f](...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- forward any undefined functions
|
||||||
|
setmetatable(GS, {__index = function(_, func)
|
||||||
|
return function(...)
|
||||||
|
return (stack[#stack][func] or __NULL__)(stack[#stack], ...)
|
||||||
|
end
|
||||||
|
end})
|
||||||
|
|
||||||
|
return GS
|
93
hump/signal.lua
Normal file
93
hump/signal.lua
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2012-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 Registry = {}
|
||||||
|
Registry.__index = function(self, key)
|
||||||
|
return Registry[key] or (function()
|
||||||
|
local t = {}
|
||||||
|
rawset(self, key, t)
|
||||||
|
return t
|
||||||
|
end)()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:register(s, f)
|
||||||
|
self[s][f] = f
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:emit(s, ...)
|
||||||
|
for f in pairs(self[s]) do
|
||||||
|
f(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:remove(s, ...)
|
||||||
|
local f = {...}
|
||||||
|
for i = 1,select('#', ...) do
|
||||||
|
self[s][f[i]] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:clear(...)
|
||||||
|
local s = {...}
|
||||||
|
for i = 1,select('#', ...) do
|
||||||
|
self[s[i]] = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:emit_pattern(p, ...)
|
||||||
|
for s in pairs(self) do
|
||||||
|
if s:match(p) then self:emit(s, ...) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:remove_pattern(p, ...)
|
||||||
|
for s in pairs(self) do
|
||||||
|
if s:match(p) then self:remove(s, ...) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:clear_pattern(p)
|
||||||
|
for s in pairs(self) do
|
||||||
|
if s:match(p) then self[s] = {} end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function new()
|
||||||
|
return setmetatable({}, Registry)
|
||||||
|
end
|
||||||
|
local default = new()
|
||||||
|
|
||||||
|
return setmetatable({
|
||||||
|
new = new,
|
||||||
|
register = function(...) return default:register(...) end,
|
||||||
|
emit = function(...) default:emit(...) end,
|
||||||
|
remove = function(...) default:remove(...) end,
|
||||||
|
clear = function(...) default:clear(...) end,
|
||||||
|
emit_pattern = function(...) default:emit_pattern(...) end,
|
||||||
|
remove_pattern = function(...) default:remove_pattern(...) end,
|
||||||
|
clear_pattern = function(...) default:clear_pattern(...) end,
|
||||||
|
}, {__call = new})
|
190
hump/timer.lua
Normal file
190
hump/timer.lua
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
--[[
|
||||||
|
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 Timer = {}
|
||||||
|
Timer.__index = Timer
|
||||||
|
|
||||||
|
local function _nothing_() end
|
||||||
|
|
||||||
|
local function new()
|
||||||
|
return setmetatable({functions = {}, tween = Timer.tween}, Timer)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:update(dt)
|
||||||
|
local to_remove = {}
|
||||||
|
for handle, delay in pairs(self.functions) do
|
||||||
|
delay = delay - dt
|
||||||
|
if delay <= 0 then
|
||||||
|
to_remove[#to_remove+1] = handle
|
||||||
|
end
|
||||||
|
self.functions[handle] = delay
|
||||||
|
handle.func(dt, delay)
|
||||||
|
end
|
||||||
|
for _,handle in ipairs(to_remove) do
|
||||||
|
self.functions[handle] = nil
|
||||||
|
handle.after(handle.after)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:do_for(delay, func, after)
|
||||||
|
local handle = {func = func, after = after or _nothing_}
|
||||||
|
self.functions[handle] = delay
|
||||||
|
return handle
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:add(delay, func)
|
||||||
|
return self:do_for(delay, _nothing_, func)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:addPeriodic(delay, func, count)
|
||||||
|
local count, handle = count or math.huge -- exploit below: math.huge - 1 = math.huge
|
||||||
|
|
||||||
|
handle = self:add(delay, function(f)
|
||||||
|
if func(func) == false then return end
|
||||||
|
count = count - 1
|
||||||
|
if count > 0 then
|
||||||
|
self.functions[handle] = delay
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return handle
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:cancel(handle)
|
||||||
|
self.functions[handle] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:clear()
|
||||||
|
self.functions = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
Timer.tween = setmetatable({
|
||||||
|
-- helper functions
|
||||||
|
out = function(f) -- 'rotates' a function
|
||||||
|
return function(s, ...) return 1 - f(1-s, ...) end
|
||||||
|
end,
|
||||||
|
chain = function(f1, f2) -- concatenates two functions
|
||||||
|
return function(s, ...) return (s < .5 and f1(2*s, ...) or 1 + f2(2*s-1, ...)) * .5 end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- useful tweening functions
|
||||||
|
linear = function(s) return s end,
|
||||||
|
quad = function(s) return s*s end,
|
||||||
|
cubic = function(s) return s*s*s end,
|
||||||
|
quart = function(s) return s*s*s*s end,
|
||||||
|
quint = function(s) return s*s*s*s*s end,
|
||||||
|
sine = function(s) return 1-math.cos(s*math.pi/2) end,
|
||||||
|
expo = function(s) return 2^(10*(s-1)) end,
|
||||||
|
circ = function(s) return 1 - math.sqrt(1-s*s) end,
|
||||||
|
|
||||||
|
back = function(s,bounciness)
|
||||||
|
bounciness = bounciness or 1.70158
|
||||||
|
return s*s*((bounciness+1)*s - bounciness)
|
||||||
|
end,
|
||||||
|
|
||||||
|
bounce = function(s) -- magic numbers ahead
|
||||||
|
local a,b = 7.5625, 1/2.75
|
||||||
|
return math.min(a*s^2, a*(s-1.5*b)^2 + .75, a*(s-2.25*b)^2 + .9375, a*(s-2.625*b)^2 + .984375)
|
||||||
|
end,
|
||||||
|
|
||||||
|
elastic = function(s, amp, period)
|
||||||
|
amp, period = amp and math.max(1, amp) or 1, period or .3
|
||||||
|
return (-amp * math.sin(2*math.pi/period * (s-1) - math.asin(1/amp))) * 2^(10*(s-1))
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
|
||||||
|
-- register new tween
|
||||||
|
__call = function(tween, self, len, subject, target, method, after, ...)
|
||||||
|
-- recursively collects fields that are defined in both subject and target into a flat list
|
||||||
|
local function tween_collect_payload(subject, target, out)
|
||||||
|
for k,v in pairs(target) do
|
||||||
|
local ref = subject[k]
|
||||||
|
assert(type(v) == type(ref), 'Type mismatch in field "'..k..'".')
|
||||||
|
if type(v) == 'table' then
|
||||||
|
tween_collect_payload(ref, v, out)
|
||||||
|
else
|
||||||
|
local ok, delta = pcall(function() return (v-ref)*1 end)
|
||||||
|
assert(ok, 'Field "'..k..'" does not support arithmetic operations')
|
||||||
|
out[#out+1] = {subject, k, delta}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
method = tween[method or 'linear'] -- see __index
|
||||||
|
local payload, t, args = tween_collect_payload(subject, target, {}), 0, {...}
|
||||||
|
|
||||||
|
local last_s = 0
|
||||||
|
return self:do_for(len, function(dt)
|
||||||
|
t = t + dt
|
||||||
|
local s = method(math.min(1, t/len), unpack(args))
|
||||||
|
local ds = s - last_s
|
||||||
|
last_s = s
|
||||||
|
for _, info in ipairs(payload) do
|
||||||
|
local ref, key, delta = unpack(info)
|
||||||
|
ref[key] = ref[key] + delta * ds
|
||||||
|
end
|
||||||
|
end, after)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- fetches function and generated compositions for method `key`
|
||||||
|
__index = function(tweens, key)
|
||||||
|
if type(key) == 'function' then return key end
|
||||||
|
|
||||||
|
assert(type(key) == 'string', 'Method must be function or string.')
|
||||||
|
if rawget(tweens, key) then return rawget(tweens, key) end
|
||||||
|
|
||||||
|
local function construct(pattern, f)
|
||||||
|
local method = rawget(tweens, key:match(pattern))
|
||||||
|
if method then return f(method) end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local out, chain = rawget(tweens,'out'), rawget(tweens,'chain')
|
||||||
|
return construct('^in%-([^-]+)$', function(...) return ... end)
|
||||||
|
or construct('^out%-([^-]+)$', out)
|
||||||
|
or construct('^in%-out%-([^-]+)$', function(f) return chain(f, out(f)) end)
|
||||||
|
or construct('^out%-in%-([^-]+)$', function(f) return chain(out(f), f) end)
|
||||||
|
or error('Unknown interpolation method: ' .. key)
|
||||||
|
end})
|
||||||
|
|
||||||
|
-- default timer
|
||||||
|
local default = new()
|
||||||
|
|
||||||
|
-- the module
|
||||||
|
return setmetatable({
|
||||||
|
new = new,
|
||||||
|
update = function(...) return default:update(...) end,
|
||||||
|
do_for = function(...) return default:do_for(...) end,
|
||||||
|
add = function(...) return default:add(...) end,
|
||||||
|
addPeriodic = function(...) return default:addPeriodic(...) end,
|
||||||
|
cancel = function(...) return default:cancel(...) end,
|
||||||
|
clear = function(...) return default:clear(...) end,
|
||||||
|
tween = setmetatable({}, {
|
||||||
|
__index = Timer.tween,
|
||||||
|
__newindex = function(_,k,v) Timer.tween[k] = v end,
|
||||||
|
__call = function(t,...) return default:tween(...) end,
|
||||||
|
})
|
||||||
|
}, {__call = new})
|
161
hump/vector-light.lua
Normal file
161
hump/vector-light.lua
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2012-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 sqrt, cos, sin, atan2 = math.sqrt, math.cos, math.sin, math.atan2
|
||||||
|
|
||||||
|
local function str(x,y)
|
||||||
|
return "("..tonumber(x)..","..tonumber(y)..")"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mul(s, x,y)
|
||||||
|
return s*x, s*y
|
||||||
|
end
|
||||||
|
|
||||||
|
local function div(s, x,y)
|
||||||
|
return x/s, y/s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add(x1,y1, x2,y2)
|
||||||
|
return x1+x2, y1+y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sub(x1,y1, x2,y2)
|
||||||
|
return x1-x2, y1-y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function permul(x1,y1, x2,y2)
|
||||||
|
return x1*x2, y1*y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dot(x1,y1, x2,y2)
|
||||||
|
return x1*x2 + y1*y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function det(x1,y1, x2,y2)
|
||||||
|
return x1*y2 - y1*x2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function eq(x1,y1, x2,y2)
|
||||||
|
return x1 == x2 and y1 == y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function lt(x1,y1, x2,y2)
|
||||||
|
return x1 < x2 or (x1 == x2 and y1 < y2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function le(x1,y1, x2,y2)
|
||||||
|
return x1 <= x2 and y1 <= y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function len2(x,y)
|
||||||
|
return x*x + y*y
|
||||||
|
end
|
||||||
|
|
||||||
|
local function len(x,y)
|
||||||
|
return sqrt(x*x + y*y)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dist2(x1,y1, x2,y2)
|
||||||
|
return len2(x1-x2, y1-y2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dist(x1,y1, x2,y2)
|
||||||
|
return len(x1-x2, y1-y2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function normalize(x,y)
|
||||||
|
local l = len(x,y)
|
||||||
|
if l > 0 then
|
||||||
|
return x/l, y/l
|
||||||
|
end
|
||||||
|
return x,y
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rotate(phi, x,y)
|
||||||
|
local c, s = cos(phi), sin(phi)
|
||||||
|
return c*x - s*y, s*x + c*y
|
||||||
|
end
|
||||||
|
|
||||||
|
local function perpendicular(x,y)
|
||||||
|
return -y, x
|
||||||
|
end
|
||||||
|
|
||||||
|
local function project(x,y, u,v)
|
||||||
|
local s = (x*u + y*v) / (u*u + v*v)
|
||||||
|
return s*u, s*v
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mirror(x,y, u,v)
|
||||||
|
local s = 2 * (x*u + y*v) / (u*u + v*v)
|
||||||
|
return s*u - x, s*v - y
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ref.: http://blog.signalsondisplay.com/?p=336
|
||||||
|
local function trim(maxLen, x, y)
|
||||||
|
local s = maxLen * maxLen / len2(x, y)
|
||||||
|
s = s > 1 and 1 or math.sqrt(s)
|
||||||
|
return x * s, y * s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function angleTo(x,y, u,v)
|
||||||
|
if u and v then
|
||||||
|
return atan2(y, x) - atan2(v, u)
|
||||||
|
end
|
||||||
|
return atan2(y, x)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- the module
|
||||||
|
return {
|
||||||
|
str = str,
|
||||||
|
|
||||||
|
-- arithmetic
|
||||||
|
mul = mul,
|
||||||
|
div = div,
|
||||||
|
add = add,
|
||||||
|
sub = sub,
|
||||||
|
permul = permul,
|
||||||
|
dot = dot,
|
||||||
|
det = det,
|
||||||
|
cross = det,
|
||||||
|
|
||||||
|
-- relation
|
||||||
|
eq = eq,
|
||||||
|
lt = lt,
|
||||||
|
le = le,
|
||||||
|
|
||||||
|
-- misc operations
|
||||||
|
len2 = len2,
|
||||||
|
len = len,
|
||||||
|
dist2 = dist2,
|
||||||
|
dist = dist,
|
||||||
|
normalize = normalize,
|
||||||
|
rotate = rotate,
|
||||||
|
perpendicular = perpendicular,
|
||||||
|
project = project,
|
||||||
|
mirror = mirror,
|
||||||
|
trim = trim,
|
||||||
|
angleTo = angleTo,
|
||||||
|
}
|
191
hump/vector.lua
Normal file
191
hump/vector.lua
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
--[[
|
||||||
|
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 assert = assert
|
||||||
|
local sqrt, cos, sin, atan2 = math.sqrt, math.cos, math.sin, math.atan2
|
||||||
|
|
||||||
|
local vector = {}
|
||||||
|
vector.__index = vector
|
||||||
|
|
||||||
|
local function new(x,y)
|
||||||
|
return setmetatable({x = x or 0, y = y or 0}, vector)
|
||||||
|
end
|
||||||
|
local zero = new(0,0)
|
||||||
|
|
||||||
|
local function isvector(v)
|
||||||
|
return type(v) == 'table' and type(v.x) == 'number' and type(v.y) == 'number'
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:clone()
|
||||||
|
return new(self.x, self.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:unpack()
|
||||||
|
return self.x, self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:__tostring()
|
||||||
|
return "("..tonumber(self.x)..","..tonumber(self.y)..")"
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__unm(a)
|
||||||
|
return new(-a.x, -a.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__add(a,b)
|
||||||
|
assert(isvector(a) and isvector(b), "Add: wrong argument types (<vector> expected)")
|
||||||
|
return new(a.x+b.x, a.y+b.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__sub(a,b)
|
||||||
|
assert(isvector(a) and isvector(b), "Sub: wrong argument types (<vector> expected)")
|
||||||
|
return new(a.x-b.x, a.y-b.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__mul(a,b)
|
||||||
|
if type(a) == "number" then
|
||||||
|
return new(a*b.x, a*b.y)
|
||||||
|
elseif type(b) == "number" then
|
||||||
|
return new(b*a.x, b*a.y)
|
||||||
|
else
|
||||||
|
assert(isvector(a) and isvector(b), "Mul: wrong argument types (<vector> or <number> expected)")
|
||||||
|
return a.x*b.x + a.y*b.y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__div(a,b)
|
||||||
|
assert(isvector(a) and type(b) == "number", "wrong argument types (expected <vector> / <number>)")
|
||||||
|
return new(a.x / b, a.y / b)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__eq(a,b)
|
||||||
|
return a.x == b.x and a.y == b.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__lt(a,b)
|
||||||
|
return a.x < b.x or (a.x == b.x and a.y < b.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__le(a,b)
|
||||||
|
return a.x <= b.x and a.y <= b.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.permul(a,b)
|
||||||
|
assert(isvector(a) and isvector(b), "permul: wrong argument types (<vector> expected)")
|
||||||
|
return new(a.x*b.x, a.y*b.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:len2()
|
||||||
|
return self.x * self.x + self.y * self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:len()
|
||||||
|
return sqrt(self.x * self.x + self.y * self.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.dist(a, b)
|
||||||
|
assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
|
||||||
|
local dx = a.x - b.x
|
||||||
|
local dy = a.y - b.y
|
||||||
|
return sqrt(dx * dx + dy * dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.dist2(a, b)
|
||||||
|
assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
|
||||||
|
local dx = a.x - b.x
|
||||||
|
local dy = a.y - b.y
|
||||||
|
return (dx * dx + dy * dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:normalize_inplace()
|
||||||
|
local l = self:len()
|
||||||
|
if l > 0 then
|
||||||
|
self.x, self.y = self.x / l, self.y / l
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:normalized()
|
||||||
|
return self:clone():normalize_inplace()
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:rotate_inplace(phi)
|
||||||
|
local c, s = cos(phi), sin(phi)
|
||||||
|
self.x, self.y = c * self.x - s * self.y, s * self.x + c * self.y
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:rotated(phi)
|
||||||
|
local c, s = cos(phi), sin(phi)
|
||||||
|
return new(c * self.x - s * self.y, s * self.x + c * self.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:perpendicular()
|
||||||
|
return new(-self.y, self.x)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:projectOn(v)
|
||||||
|
assert(isvector(v), "invalid argument: cannot project vector on " .. type(v))
|
||||||
|
-- (self * v) * v / v:len2()
|
||||||
|
local s = (self.x * v.x + self.y * v.y) / (v.x * v.x + v.y * v.y)
|
||||||
|
return new(s * v.x, s * v.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:mirrorOn(v)
|
||||||
|
assert(isvector(v), "invalid argument: cannot mirror vector on " .. type(v))
|
||||||
|
-- 2 * self:projectOn(v) - self
|
||||||
|
local s = 2 * (self.x * v.x + self.y * v.y) / (v.x * v.x + v.y * v.y)
|
||||||
|
return new(s * v.x - self.x, s * v.y - self.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:cross(v)
|
||||||
|
assert(isvector(v), "cross: wrong argument types (<vector> expected)")
|
||||||
|
return self.x * v.y - self.y * v.x
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ref.: http://blog.signalsondisplay.com/?p=336
|
||||||
|
function vector:trim_inplace(maxLen)
|
||||||
|
local s = maxLen * maxLen / self:len2()
|
||||||
|
s = (s > 1 and 1) or math.sqrt(s)
|
||||||
|
self.x, self.y = self.x * s, self.y * s
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:angleTo(other)
|
||||||
|
if other then
|
||||||
|
return atan2(self.y, self.x) - atan2(other.y, other.x)
|
||||||
|
end
|
||||||
|
return atan2(self.y, self.x)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:trimmed(maxLen)
|
||||||
|
return self:clone():trim_inplace(maxLen)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- the module
|
||||||
|
return setmetatable({new = new, isvector = isvector, zero = zero},
|
||||||
|
{__call = function(_, ...) return new(...) end})
|
20
main.lua
Normal file
20
main.lua
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Gamestate = require 'hump.gamestate'
|
||||||
|
require 'menu_state'
|
||||||
|
|
||||||
|
-- Initialize game global variables and switch to menu state
|
||||||
|
|
||||||
|
function love.load()
|
||||||
|
|
||||||
|
width = love.graphics.getWidth()
|
||||||
|
height = love.graphics.getHeight()
|
||||||
|
|
||||||
|
love.mouse.setVisible(true)
|
||||||
|
love.physics.setMeter(30)
|
||||||
|
|
||||||
|
Gamestate.registerEvents()
|
||||||
|
Gamestate.switch(menu_state)
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.quit()
|
||||||
|
|
||||||
|
end
|
47
menu_state.lua
Normal file
47
menu_state.lua
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
Gamestate = require 'hump.gamestate'
|
||||||
|
Class = require 'hump.class'
|
||||||
|
|
||||||
|
menu_state = {}
|
||||||
|
|
||||||
|
local font = love.graphics.newFont(18)
|
||||||
|
|
||||||
|
function menu_state:enter()
|
||||||
|
love.graphics.setBackgroundColor(0, 0, 0, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function menu_state:draw()
|
||||||
|
love.graphics.setColor(255,255,255,255)
|
||||||
|
love.graphics.setFont(font)
|
||||||
|
love.graphics.printf("WASD to move", 50, height-100, width, "left")
|
||||||
|
love.graphics.printf("Left mouse button to freeze, right mouse button to speed", width-250, height-100, 200, "right")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function menu_state:update(dt)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function menu_state:focus(bool)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function menu_state:keypressed(key, unicode)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function menu_state:keyreleased(key, unicode)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function menu_state:mousepressed(x, y, button)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function menu_state:mousereleased(x, y, button)
|
||||||
|
|
||||||
|
end
|
182
middleclass.lua
182
middleclass.lua
|
@ -1,182 +0,0 @@
|
||||||
local middleclass = {
|
|
||||||
_VERSION = 'middleclass v3.0.1',
|
|
||||||
_DESCRIPTION = 'Object Orientation for Lua',
|
|
||||||
_URL = 'https://github.com/kikito/middleclass',
|
|
||||||
_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 function _setClassDictionariesMetatables(aClass)
|
|
||||||
local dict = aClass.__instanceDict
|
|
||||||
dict.__index = dict
|
|
||||||
|
|
||||||
local super = aClass.super
|
|
||||||
if super then
|
|
||||||
local superStatic = super.static
|
|
||||||
setmetatable(dict, super.__instanceDict)
|
|
||||||
setmetatable(aClass.static, { __index = function(_,k) return dict[k] or superStatic[k] end })
|
|
||||||
else
|
|
||||||
setmetatable(aClass.static, { __index = function(_,k) return dict[k] end })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function _setClassMetatable(aClass)
|
|
||||||
setmetatable(aClass, {
|
|
||||||
__tostring = function() return "class " .. aClass.name end,
|
|
||||||
__index = aClass.static,
|
|
||||||
__newindex = aClass.__instanceDict,
|
|
||||||
__call = function(self, ...) return self:new(...) end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function _createClass(name, super)
|
|
||||||
local aClass = { name = name, super = super, static = {}, __mixins = {}, __instanceDict={} }
|
|
||||||
aClass.subclasses = setmetatable({}, {__mode = "k"})
|
|
||||||
|
|
||||||
_setClassDictionariesMetatables(aClass)
|
|
||||||
_setClassMetatable(aClass)
|
|
||||||
|
|
||||||
return aClass
|
|
||||||
end
|
|
||||||
|
|
||||||
local function _createLookupMetamethod(aClass, name)
|
|
||||||
return function(...)
|
|
||||||
local method = aClass.super[name]
|
|
||||||
assert( type(method)=='function', tostring(aClass) .. " doesn't implement metamethod '" .. name .. "'" )
|
|
||||||
return method(...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function _setClassMetamethods(aClass)
|
|
||||||
for _,m in ipairs(aClass.__metamethods) do
|
|
||||||
aClass[m]= _createLookupMetamethod(aClass, m)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function _setDefaultInitializeMethod(aClass, super)
|
|
||||||
aClass.initialize = function(instance, ...)
|
|
||||||
return super.initialize(instance, ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function _includeMixin(aClass, mixin)
|
|
||||||
assert(type(mixin)=='table', "mixin must be a table")
|
|
||||||
for name,method in pairs(mixin) do
|
|
||||||
if name ~= "included" and name ~= "static" then aClass[name] = method end
|
|
||||||
end
|
|
||||||
if mixin.static then
|
|
||||||
for name,method in pairs(mixin.static) do
|
|
||||||
aClass.static[name] = method
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if type(mixin.included)=="function" then mixin:included(aClass) end
|
|
||||||
aClass.__mixins[mixin] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local Object = _createClass("Object", nil)
|
|
||||||
|
|
||||||
Object.static.__metamethods = { '__add', '__call', '__concat', '__div', '__ipairs', '__le',
|
|
||||||
'__len', '__lt', '__mod', '__mul', '__pairs', '__pow', '__sub',
|
|
||||||
'__tostring', '__unm'}
|
|
||||||
|
|
||||||
function Object.static:allocate()
|
|
||||||
assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
|
|
||||||
return setmetatable({ class = self }, self.__instanceDict)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Object.static:new(...)
|
|
||||||
local instance = self:allocate()
|
|
||||||
instance:initialize(...)
|
|
||||||
return instance
|
|
||||||
end
|
|
||||||
|
|
||||||
function Object.static:subclass(name)
|
|
||||||
assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
|
|
||||||
assert(type(name) == "string", "You must provide a name(string) for your class")
|
|
||||||
|
|
||||||
local subclass = _createClass(name, self)
|
|
||||||
_setClassMetamethods(subclass)
|
|
||||||
_setDefaultInitializeMethod(subclass, self)
|
|
||||||
self.subclasses[subclass] = true
|
|
||||||
self:subclassed(subclass)
|
|
||||||
|
|
||||||
return subclass
|
|
||||||
end
|
|
||||||
|
|
||||||
function Object.static:subclassed(other) end
|
|
||||||
|
|
||||||
function Object.static:isSubclassOf(other)
|
|
||||||
return type(other) == 'table' and
|
|
||||||
type(self) == 'table' and
|
|
||||||
type(self.super) == 'table' and
|
|
||||||
( self.super == other or
|
|
||||||
type(self.super.isSubclassOf) == 'function' and
|
|
||||||
self.super:isSubclassOf(other)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Object.static:include( ... )
|
|
||||||
assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'")
|
|
||||||
for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
function Object.static:includes(mixin)
|
|
||||||
return type(mixin) == 'table' and
|
|
||||||
type(self) == 'table' and
|
|
||||||
type(self.__mixins) == 'table' and
|
|
||||||
( self.__mixins[mixin] or
|
|
||||||
type(self.super) == 'table' and
|
|
||||||
type(self.super.includes) == 'function' and
|
|
||||||
self.super:includes(mixin)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Object:initialize() end
|
|
||||||
|
|
||||||
function Object:__tostring() return "instance of " .. tostring(self.class) end
|
|
||||||
|
|
||||||
function Object:isInstanceOf(aClass)
|
|
||||||
return type(self) == 'table' and
|
|
||||||
type(self.class) == 'table' and
|
|
||||||
type(aClass) == 'table' and
|
|
||||||
( aClass == self.class or
|
|
||||||
type(aClass.isSubclassOf) == 'function' and
|
|
||||||
self.class:isSubclassOf(aClass)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function middleclass.class(name, super, ...)
|
|
||||||
super = super or Object
|
|
||||||
return super:subclass(name, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
middleclass.Object = Object
|
|
||||||
|
|
||||||
setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end })
|
|
||||||
|
|
||||||
return middleclass
|
|
Loading…
Reference in a new issue