From 839715cae3690c70e6ab00c9812c0cd3ed287ed3 Mon Sep 17 00:00:00 2001 From: Andrew Minnich Date: Thu, 8 Mar 2018 23:35:20 -0500 Subject: [PATCH] readd source functions and demo --- baton.lua | 37 ++++++ inspect.lua | 343 ---------------------------------------------------- main.lua | 112 ++++++++++++++--- 3 files changed, 133 insertions(+), 359 deletions(-) delete mode 100644 inspect.lua diff --git a/baton.lua b/baton.lua index 598e3d5..5fb85ea 100644 --- a/baton.lua +++ b/baton.lua @@ -6,6 +6,14 @@ local function parseSource(source) return source:match '(.+):(.+)' end +local function parseAxis(value) + return value:match '(.+)([%+%-])' +end + +local function parseHat(value) + return value:match '(%d)(.+)' +end + -- source functions -- local sf = {kbm = {}, joy = {}} @@ -14,6 +22,25 @@ function sf.kbm.key(key) return love.keyboard.isDown(key) and 1 or 0 end +function sf.kbm.sc(sc) + return love.keyboard.isScancodeDown(sc) and 1 or 0 +end + +function sf.kbm.mouse(button) + return love.mouse.isDown(tonumber(button)) and 1 or 0 +end + +function sf.joy.axis(joystick, value) + local axis, direction = parseAxis(value) + if tonumber(axis) then + value = joystick:getAxis(tonumber(axis)) + else + value = joystick:getGamepadAxis(axis) + end + if direction == '-' then value = -value end + return value > 0 and value or 0 +end + function sf.joy.button(joystick, button) if tonumber(button) then return joystick:isDown(tonumber(button)) and 1 or 0 @@ -22,6 +49,11 @@ function sf.joy.button(joystick, button) end end +function sf.joy.hat(joystick, value) + local hat, direction = parseHat(value) + return joystick:getHat(hat) == direction and 1 or 0 +end + -- player class - internal functions -- local Player = {} @@ -72,6 +104,7 @@ function Player:_init(config) self:_loadConfig(config) self:_initControls() self:_initPairs() + self._activeDevice = 'none' end function Player:_setActiveDevice() @@ -214,6 +247,10 @@ function Player:released(name) end end +function Player:getActiveDevice() + return self._activeDevice +end + -- baton functions -- function baton.new(config) diff --git a/inspect.lua b/inspect.lua deleted file mode 100644 index 72be4c1..0000000 --- a/inspect.lua +++ /dev/null @@ -1,343 +0,0 @@ -local inspect ={ - _VERSION = 'inspect.lua 3.1.0', - _URL = 'http://github.com/kikito/inspect.lua', - _DESCRIPTION = 'human-readable representations of tables', - _LICENSE = [[ - MIT LICENSE - - Copyright (c) 2013 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 tostring = tostring - -inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) -inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) - --- Apostrophizes the string if it has quotes, but not aphostrophes --- Otherwise, it returns a regular quoted string -local function smartQuote(str) - if str:match('"') and not str:match("'") then - return "'" .. str .. "'" - end - return '"' .. str:gsub('"', '\\"') .. '"' -end - --- \a => '\\a', \0 => '\\0', 31 => '\31' -local shortControlCharEscapes = { - ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", - ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v" -} -local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 -for i=0, 31 do - local ch = string.char(i) - if not shortControlCharEscapes[ch] then - shortControlCharEscapes[ch] = "\\"..i - longControlCharEscapes[ch] = string.format("\\%03d", i) - end -end - -local function escape(str) - return (str:gsub("\\", "\\\\") - :gsub("(%c)%f[0-9]", longControlCharEscapes) - :gsub("%c", shortControlCharEscapes)) -end - -local function isIdentifier(str) - return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" ) -end - -local function isSequenceKey(k, sequenceLength) - return type(k) == 'number' - and 1 <= k - and k <= sequenceLength - and math.floor(k) == k -end - -local defaultTypeOrders = { - ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, - ['function'] = 5, ['userdata'] = 6, ['thread'] = 7 -} - -local function sortKeys(a, b) - local ta, tb = type(a), type(b) - - -- strings and numbers are sorted numerically/alphabetically - if ta == tb and (ta == 'string' or ta == 'number') then return a < b end - - local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] - -- Two default types are compared according to the defaultTypeOrders table - if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb] - elseif dta then return true -- default types before custom ones - elseif dtb then return false -- custom types after default ones - end - - -- custom types are sorted out alphabetically - return ta < tb -end - --- For implementation reasons, the behavior of rawlen & # is "undefined" when --- tables aren't pure sequences. So we implement our own # operator. -local function getSequenceLength(t) - local len = 1 - local v = rawget(t,len) - while v ~= nil do - len = len + 1 - v = rawget(t,len) - end - return len - 1 -end - -local function getNonSequentialKeys(t) - local keys = {} - local sequenceLength = getSequenceLength(t) - for k,_ in pairs(t) do - if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end - end - table.sort(keys, sortKeys) - return keys, sequenceLength -end - -local function getToStringResultSafely(t, mt) - local __tostring = type(mt) == 'table' and rawget(mt, '__tostring') - local str, ok - if type(__tostring) == 'function' then - ok, str = pcall(__tostring, t) - str = ok and str or 'error: ' .. tostring(str) - end - if type(str) == 'string' and #str > 0 then return str end -end - -local function countTableAppearances(t, tableAppearances) - tableAppearances = tableAppearances or {} - - if type(t) == 'table' then - if not tableAppearances[t] then - tableAppearances[t] = 1 - for k,v in pairs(t) do - countTableAppearances(k, tableAppearances) - countTableAppearances(v, tableAppearances) - end - countTableAppearances(getmetatable(t), tableAppearances) - else - tableAppearances[t] = tableAppearances[t] + 1 - end - end - - return tableAppearances -end - -local copySequence = function(s) - local copy, len = {}, #s - for i=1, len do copy[i] = s[i] end - return copy, len -end - -local function makePath(path, ...) - local keys = {...} - local newPath, len = copySequence(path) - for i=1, #keys do - newPath[len + i] = keys[i] - end - return newPath -end - -local function processRecursive(process, item, path, visited) - - if item == nil then return nil end - if visited[item] then return visited[item] end - - local processed = process(item, path) - if type(processed) == 'table' then - local processedCopy = {} - visited[item] = processedCopy - local processedKey - - for k,v in pairs(processed) do - processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) - if processedKey ~= nil then - processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) - end - end - - local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) - if type(mt) ~= 'table' then mt = nil end -- ignore not nil/table __metatable field - setmetatable(processedCopy, mt) - processed = processedCopy - end - return processed -end - - - -------------------------------------------------------------------- - -local Inspector = {} -local Inspector_mt = {__index = Inspector} - -function Inspector:puts(...) - local args = {...} - local buffer = self.buffer - local len = #buffer - for i=1, #args do - len = len + 1 - buffer[len] = args[i] - end -end - -function Inspector:down(f) - self.level = self.level + 1 - f() - self.level = self.level - 1 -end - -function Inspector:tabify() - self:puts(self.newline, string.rep(self.indent, self.level)) -end - -function Inspector:alreadyVisited(v) - return self.ids[v] ~= nil -end - -function Inspector:getId(v) - local id = self.ids[v] - if not id then - local tv = type(v) - id = (self.maxIds[tv] or 0) + 1 - self.maxIds[tv] = id - self.ids[v] = id - end - return tostring(id) -end - -function Inspector:putKey(k) - if isIdentifier(k) then return self:puts(k) end - self:puts("[") - self:putValue(k) - self:puts("]") -end - -function Inspector:putTable(t) - if t == inspect.KEY or t == inspect.METATABLE then - self:puts(tostring(t)) - elseif self:alreadyVisited(t) then - self:puts('') - elseif self.level >= self.depth then - self:puts('{...}') - else - if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end - - local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t) - local mt = getmetatable(t) - local toStringResult = getToStringResultSafely(t, mt) - - self:puts('{') - self:down(function() - if toStringResult then - self:puts(' -- ', escape(toStringResult)) - if sequenceLength >= 1 then self:tabify() end - end - - local count = 0 - for i=1, sequenceLength do - if count > 0 then self:puts(',') end - self:puts(' ') - self:putValue(t[i]) - count = count + 1 - end - - for _,k in ipairs(nonSequentialKeys) do - if count > 0 then self:puts(',') end - self:tabify() - self:putKey(k) - self:puts(' = ') - self:putValue(t[k]) - count = count + 1 - end - - if type(mt) == 'table' then - if count > 0 then self:puts(',') end - self:tabify() - self:puts(' = ') - self:putValue(mt) - end - end) - - if #nonSequentialKeys > 0 or type(mt) == 'table' then -- result is multi-lined. Justify closing } - self:tabify() - elseif sequenceLength > 0 then -- array tables have one extra space before closing } - self:puts(' ') - end - - self:puts('}') - end -end - -function Inspector:putValue(v) - local tv = type(v) - - if tv == 'string' then - self:puts(smartQuote(escape(v))) - elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or - tv == 'cdata' or tv == 'ctype' then - self:puts(tostring(v)) - elseif tv == 'table' then - self:putTable(v) - else - self:puts('<',tv,' ',self:getId(v),'>') - end -end - -------------------------------------------------------------------- - -function inspect.inspect(root, options) - options = options or {} - - local depth = options.depth or math.huge - local newline = options.newline or '\n' - local indent = options.indent or ' ' - local process = options.process - - if process then - root = processRecursive(process, root, {}, {}) - end - - local inspector = setmetatable({ - depth = depth, - level = 0, - buffer = {}, - ids = {}, - maxIds = {}, - newline = newline, - indent = indent, - tableAppearances = countTableAppearances(root) - }, Inspector_mt) - - inspector:putValue(root) - - return table.concat(inspector.buffer) -end - -setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) - -return inspect - diff --git a/main.lua b/main.lua index 5487a24..3488c0a 100644 --- a/main.lua +++ b/main.lua @@ -1,25 +1,105 @@ local baton = require 'baton' -local inspect = require 'inspect' -local input = baton.new { - controls = { - left = {'key:left', 'axis:leftx-', 'button:dpleft'}, - right = {'key:right', 'axis:leftx+', 'button:dpright'}, - up = {'key:up', 'axis:lefty-', 'button:dpup'}, - down = {'key:down', 'axis:lefty+', 'button:dpdown'}, - action = {'key:x', 'button:a'}, - }, - pairs = { - move = {'left', 'right', 'up', 'down'} - }, - joystick = love.joystick.getJoysticks()[1], +local player = baton.new { + controls = { + left = {'key:left', 'axis:leftx-', 'button:dpleft'}, + right = {'key:right', 'axis:leftx+', 'button:dpright'}, + up = {'key:up', 'axis:lefty-', 'button:dpup'}, + down = {'key:down', 'axis:lefty+', 'button:dpdown'}, + action = {'key:x', 'button:a', 'mouse:1'}, + }, + pairs = { + move = {'left', 'right', 'up', 'down'} + }, + joystick = love.joystick.getJoysticks()[1], + deadzone = .33, } +local pairDisplayAlpha = 0 +local pairDisplayTargetAlpha = 0 +local buttonDisplayAlpha = 0 +local buttonDisplayTargetAlpha = 0 + +local updates = 0 +local updateTime = 0 + function love.update(dt) - input:update() + local time = love.timer.getTime() + + player:update() + + pairDisplayTargetAlpha = player:pressed 'move' and 255 + or player:released 'move' and 255 + or player:down 'move' and 100 + or 0 + if pairDisplayAlpha > pairDisplayTargetAlpha then + pairDisplayAlpha = pairDisplayAlpha - 1000 * dt + end + if pairDisplayAlpha < pairDisplayTargetAlpha then + pairDisplayAlpha = pairDisplayTargetAlpha + end + + buttonDisplayTargetAlpha = player:pressed 'action' and 255 + or player:released 'action' and 255 + or player:down 'action' and 100 + or 0 + if buttonDisplayAlpha > buttonDisplayTargetAlpha then + buttonDisplayAlpha = buttonDisplayAlpha - 1000 * dt + end + if buttonDisplayAlpha < buttonDisplayTargetAlpha then + buttonDisplayAlpha = buttonDisplayTargetAlpha + end + + updateTime = updateTime + (love.timer.getTime() - time) + updates = updates + 1 end +function love.keypressed(key) + if key == 'escape' then + love.event.quit() + end + if key == 'tab' then + player.controls.action[1] = 'key:z' + end +end + +local pairDisplayRadius = 128 + function love.draw() - local x, y = input:get 'move' - love.graphics.print(tostring(x) .. '\n' .. tostring(y)) + love.graphics.setColor(255, 255, 255) + love.graphics.print('Current active device: ' .. tostring(player:getActiveDevice())) + love.graphics.print('Average update time (us): ' .. math.floor(updateTime/updates*1000000), 0, 16) + love.graphics.print('Memory usage (kb): ' .. math.floor(collectgarbage 'count'), 0, 32) + + love.graphics.push() + love.graphics.translate(400, 300) + + love.graphics.setColor(50, 50, 50, pairDisplayAlpha) + love.graphics.circle('fill', 0, 0, pairDisplayRadius) + + love.graphics.setColor(255, 255, 255) + love.graphics.circle('line', 0, 0, pairDisplayRadius) + + local r = pairDisplayRadius * player.config.deadzone + if player.config.squareDeadzone then + love.graphics.rectangle('line', -r, -r, r*2, r*2) + else + love.graphics.circle('line', 0, 0, r) + end + + love.graphics.setColor(150, 150, 150) + local x, y = player:getRaw 'move' + love.graphics.circle('fill', x*pairDisplayRadius, + y*pairDisplayRadius, 4) + love.graphics.setColor(255, 255, 255) + local x, y = player:get 'move' + love.graphics.circle('fill', x*pairDisplayRadius, + y*pairDisplayRadius, 4) + + love.graphics.setColor(255, 255, 255) + love.graphics.rectangle('line', -50, 150, 100, 100) + love.graphics.setColor(255, 255, 255, buttonDisplayAlpha) + love.graphics.rectangle('fill', -50, 150, 100, 100) + + love.graphics.pop() end \ No newline at end of file