Last active
August 29, 2015 13:56
-
-
Save Lerg/8791421 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local _M = {} | |
_M.deviceID = system.getInfo('deviceID') | |
if system.getInfo('environment') ~= 'simulator' then | |
io.output():setvbuf('no') | |
else | |
_M.isSimulator = true | |
end | |
local platform = system.getInfo('platformName') | |
if platform == 'Android' then | |
_M.isAndroid = true | |
elseif platform == 'iPhone OS' then | |
_M.isiOS = true | |
end | |
if _M.isSimulator then | |
-- Prevent global missuse | |
local mt = getmetatable(_G) | |
if mt == nil then | |
mt = {} | |
setmetatable(_G, mt) | |
end | |
mt.__declared = {} | |
mt.__newindex = function (t, n, v) | |
if not mt.__declared[n] then | |
local w = debug.getinfo(2, 'S').what | |
if w ~= 'main' and w ~= 'C' then | |
error('assign to undeclared variable \'' .. n .. '\'', 2) | |
end | |
mt.__declared[n] = true | |
end | |
rawset(t, n, v) | |
end | |
mt.__index = function (t, n) | |
if not mt.__declared[n] and debug.getinfo(2, 'S').what ~= 'C' then | |
error('variable \'' .. n .. '\' is not declared', 2) | |
end | |
return rawget(t, n) | |
end | |
end | |
local locals = { | |
_W = display.contentWidth, | |
_H = display.contentHeight, | |
_T = display.screenOriginY, | |
_B = display.viewableContentHeight - display.screenOriginY, | |
_L = display.screenOriginX, | |
_R = display.viewableContentWidth - display.screenOriginX, | |
_CX = math.floor(display.contentWidth * 0.5), | |
_CY = math.floor(display.contentHeight * 0.5), | |
mFloor = math.floor, | |
tInsert = table.insert, | |
mCeil = math.ceil, | |
mFloor = math.floor, | |
mAbs = math.abs, | |
mAtan2 = math.atan2, | |
mSin = math.sin, | |
mCos = math.cos, | |
mPi = math.pi, | |
mSqrt = math.sqrt, | |
mRandom = math.random, | |
tInsert = table.insert, | |
tRemove = table.remove, | |
tForeach = table.foreach, | |
tShuffle = table.shuffle, | |
sSub = string.sub, | |
sLower = string.lower} | |
locals._SW = locals._R - locals._L | |
locals._SH = locals._B - locals._T | |
function _M.setLocals() | |
local i = 1 | |
repeat | |
local k, v = debug.getlocal(2, i) | |
if k and v == nil then | |
if locals[k] ~= nil then | |
debug.setlocal(2, i, locals[k]) | |
else | |
error('No value for a local variable: ' .. k, 2) | |
end | |
end | |
i = i + 1 | |
until not k | |
end | |
local colors = {} | |
colors['white'] = {1, 1, 1} | |
colors['grey'] = {0.6, 0.6, 0.6} | |
colors['black'] = {0, 0, 0} | |
colors['red'] = {1, 0, 0} | |
colors['green'] = {0, 1, 0} | |
colors['blue'] = {0, 0, 1} | |
colors['yellow'] = {1, 1, 0} | |
colors['cyan'] = {0, 1, 1} | |
colors['magenta'] = {1, 0, 1} | |
colors['orange'] = {1, 0.75, 0} | |
colors['dark_green'] = {0, 0.5, 0} | |
colors['res_green'] = {0, 0.3, 0} | |
colors['res_red'] = {0.7, 0, 0} | |
local ext = (_M.isAndroid or _M.isSimulator) and '.ogg' or '.m4a' | |
local sounds = { | |
button = 'sounds/button.wav', | |
swipe = 'sounds/swipe.wav', | |
wrong = 'sounds/wrong.wav', | |
correct = 'sounds/correct.wav', | |
pop = 'sounds/pop.wav', | |
music = 'sounds/music' .. ext | |
} | |
_M.duration = 200 | |
local referencePoints = { | |
TopLeft = {0, 0}, | |
TopRight = {1, 0}, | |
TopCenter = {0.5, 0}, | |
BottomLeft = {0, 1}, | |
BottomRight = {1, 1}, | |
BottomCenter = {0.5, 1}, | |
CenterLeft = {0, 0.5}, | |
CenterRight = {1, 0.5}, | |
Center = {0.5, 0.5} | |
} | |
function _M.setRP(object, rp) | |
local anchor = referencePoints[rp] | |
if anchor then | |
object.anchorX, object.anchorY = anchor[1], anchor[2] | |
else | |
error('No such reference point: ' .. tostring(rp), 2) | |
end | |
end | |
function _M.setFillColor(object, color) | |
local rgb = colors[color] | |
if rgb then | |
object:setFillColor(unpack(rgb)) | |
else | |
error('No such color: ' .. tostring(color), 2) | |
end | |
end | |
function _M.setStrokeColor(object, color) | |
local rgb = colors[color] | |
if rgb then | |
object:setStrokeColor(unpack(rgb)) | |
else | |
error('No such color: ' .. tostring(color), 2) | |
end | |
end | |
function _M.enableRemoteConsole(hostname, port) | |
if not hostname then | |
error('Hostname is required', 2) | |
end | |
port = port or 22000 | |
local udp = require('socket').udp() | |
udp:setsockname('*', port + 1) | |
udp:settimeout(1) | |
_G.print = function(...) | |
local t = {...} | |
local s = '' | |
for i = 1, #t do | |
local v = t[i] | |
if v == nil then v = 'nil' end | |
s = s .. tostring(v) .. '\t' | |
end | |
udp:sendto(s .. '\n', hostname, port) | |
end | |
Runtime:addEventListener('unhandledError', function(event) | |
local message = 'Runtime error\n' .. event.errorMessage .. event.stackTrace .. '\n' | |
udp:sendto(message, hostname, port) | |
end) | |
end | |
function _M.newImage(filename, params) | |
params = params or {} | |
local w, h = params.w or _W, params.h or _H | |
local image = display.newImageRect(filename, params.dir or system.ResourceDirectory, w, h) | |
if not image then return end | |
if params.rp then | |
_M.setRP(image, params.rp) | |
end | |
image.x = params.x or 0 | |
image.y = params.y or 0 | |
if params.g then | |
params.g:insert(image) | |
end | |
return image | |
end | |
function _M.newText(params) | |
params = params or {} | |
local text | |
if params.align then | |
text = display.newText{text = params.text or '', | |
x = params.x or 0, y = params.y or 0, | |
width = params.w, height = params.h or (params.w and 0), | |
font = params.font or _M.font, | |
fontSize = params.size or 16, | |
align = params.align or 'center'} | |
elseif params.w then | |
text = display.newEmbossedText(params.text or '', 0, 0, params.w, params.h or 0, params.font or _M.font, params.size or 16) | |
else | |
text = display.newEmbossedText(params.text or '', 0, 0, params.font or _M.font, params.size or 16) | |
end | |
if params.rp then | |
_M.setRP(text, params.rp) | |
end | |
text.x = params.x or 0 | |
text.y = params.y or 0 | |
if params.g then | |
params.g:insert(text) | |
end | |
if params.color then | |
_M.setColor(text, params.color) | |
end | |
return text | |
end | |
function _M.newButton(params) | |
local button = widget.newButton { | |
width = params.w or 32, height = params.h or 32, | |
defaultFile = params.image or 'images/button.png', | |
overFile = params.imageOver or 'images/button-over.png', | |
label = params.text, | |
labelColor = params.fontColor or {default = {1, 1, 1}, over = {0.8, 0.8, 0.8}}, | |
fontSize = params.fontSize or 14, | |
onPress = params.onPress, | |
onRelease = params.onRelease, | |
onEvent = params.onEvent} | |
button.x, button.y = params.x or 0, params.y or 0 | |
if params.rp then | |
_M.setRP(button, params.rp) | |
end | |
params.g:insert(button) | |
return button | |
end | |
function _M.transition(object, params) | |
params = params or {} | |
params.delay = params.delay or 0 | |
object.alpha = 0 | |
local transParams = {time = 800, alpha = 1, transition = function (a,b,c,d) local v = easing.outExpo(a,b,c,d); if v > 1 then return 1 else return v end end} | |
if params.delay > 0 then | |
object.isVisible = false | |
transParams.onStart = function (obj) obj.isVisible = true end | |
transParams.delay = params.delay * 30 | |
end | |
transition.to(object, transParams) | |
end | |
function _M.alert(txt) | |
if type(txt) == 'string' then | |
native.showAlert(_M.name, txt, {'OK'}) | |
end | |
end | |
function _M.returnTrue(obj) | |
if obj then | |
local function rt() return true end | |
obj:addEventListener('touch', rt) | |
obj:addEventListener('tap', rt) | |
obj.isHitTestable = true | |
else | |
return true | |
end | |
end | |
local audioChannel, otherAudioChannel, currentMusicPath = 1, 2 | |
audio.crossFadeBackground = function(path, force) | |
if not _M.music_on then return end | |
path = sounds[path] | |
if currentMusicPath == path and audio.getVolume{channel = audioChannel} > 0.1 and not force then return false end | |
audio.fadeOut{channel = audioChannel, time = 1000} | |
audioChannel, otherAudioChannel = otherAudioChannel, audioChannel | |
audio.setVolume(0.5, {channel = audioChannel}) | |
audio.play(audio.loadStream(path), {channel = audioChannel, loops = -1, fadein = 1000}) | |
currentMusicPath = path | |
end | |
audio.reserveChannels(2) | |
local loadedSounds = {} | |
local function loadSound(snd) | |
if not loadedSounds[snd] then | |
loadedSounds[snd] = audio.loadSound(sounds[snd]) | |
end | |
return loadedSounds[snd] | |
end | |
audio.playSFX = function(snd, params) | |
if not _M.sound_on then return end | |
local channel = (type(snd) == 'string') and audio.play(loadSound(snd), params) or audio.play(snd, params) | |
audio.setVolume(1, {channel = channel}) | |
return channel | |
end | |
function _M.initUser(t) | |
_M.user = json.decode(_M.readFile('user.txt')) | |
if not _M.user then | |
_M.user = t | |
_M.saveUser() | |
end | |
end | |
function _M.saveUser() | |
_M.saveFile('user.txt', json.encode(_M.user)) | |
end | |
function _M.nextFrame(f) | |
timer.performWithDelay(1, f) | |
end | |
function _M.enterFrame() | |
for i = 1, #_M.enterFrameFunctions do | |
_M.enterFrameFunctions[i]() | |
end | |
end | |
function _M.eachFrame(f) | |
if not _M.enterFrameFunctions then | |
_M.enterFrameFunctions = {} | |
Runtime:addEventListener('enterFrame', _M.enterFrame) | |
end | |
table.insert(_M.enterFrameFunctions, f) | |
return f | |
end | |
function _M.eachFrameRemove(f) | |
if not f or not _M.enterFrameFunctions then return end | |
local ind = table.indexOf(_M.enterFrameFunctions, f) | |
if ind then | |
table.remove(_M.enterFrameFunctions, ind) | |
if #_M.enterFrameFunctions == 0 then | |
Runtime:removeEventListener('enterFrame', _M.enterFrame) | |
_M.enterFrameFunctions = nil | |
end | |
end | |
end | |
function _M.extend(target, source) | |
for k, v in pairs(source) do | |
target[k] = v | |
end | |
end | |
-- Fix problem that finalize event is not called for children objects when group is removed | |
local function finalize(event) | |
local g = event.target | |
for i = 1, g.numChildren do | |
if g[i]._tableListeners and g[i]._tableListeners.finalize then | |
for j = 1, #g[i]._tableListeners.finalize do | |
g[i]._tableListeners.finalize[j]:dispatchEvent{name = 'finalize', target = g[i]} | |
end | |
end | |
if g[i]._functionListeners and g[i]._functionListeners.finalize then | |
for j = 1, #g[i]._functionListeners.finalize do | |
g[i]._functionListeners.finalize[j]({name = 'finalize', target = g[i]}) | |
end | |
end | |
if g[i].numChildren then | |
finalize{target = g[i]} | |
end | |
end | |
end | |
local newGroup = display.newGroup | |
function display.newGroup() | |
local g = newGroup() | |
g:addEventListener('finalize', finalize) | |
return g | |
end | |
return _M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment