mirror of
https://github.com/Reuh/ubiquitousse.git
synced 2025-10-27 09:09:30 +00:00
Remove backend system and ctruLua support
Since I only use the LÖVE backend anyway, this simplifies the code. Tidied some code.
This commit is contained in:
parent
9f4c03a136
commit
4b75f21e52
17 changed files with 663 additions and 1067 deletions
|
|
@ -16,7 +16,6 @@ local asset_mt = {
|
||||||
-- @tparam assetName string the asset's full name
|
-- @tparam assetName string the asset's full name
|
||||||
-- @tparam ... number/string other arguments for the asset loader
|
-- @tparam ... number/string other arguments for the asset loader
|
||||||
-- @return the asset
|
-- @return the asset
|
||||||
-- @impl ubiquitousse
|
|
||||||
__call = function(self, assetName, ...)
|
__call = function(self, assetName, ...)
|
||||||
local cache = self.cache
|
local cache = self.cache
|
||||||
local hash = table.concat({assetName, ...}, ".")
|
local hash = table.concat({assetName, ...}, ".")
|
||||||
|
|
@ -37,7 +36,6 @@ local asset_mt = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Preload a list of assets.
|
--- Preload a list of assets.
|
||||||
-- @impl ubiquitousse
|
|
||||||
load = function(self, list)
|
load = function(self, list)
|
||||||
for _, asset in ipairs(list) do
|
for _, asset in ipairs(list) do
|
||||||
self(asset)
|
self(asset)
|
||||||
|
|
@ -46,7 +44,6 @@ local asset_mt = {
|
||||||
|
|
||||||
--- Allow loaded assets to be garbage collected.
|
--- Allow loaded assets to be garbage collected.
|
||||||
-- Only useful if the caching mode is set to "manual" duritng creation.
|
-- Only useful if the caching mode is set to "manual" duritng creation.
|
||||||
-- @impl ubiquitousse
|
|
||||||
clear = function(self)
|
clear = function(self)
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
end
|
end
|
||||||
|
|
@ -61,7 +58,6 @@ local asset = {
|
||||||
-- @tparam directory string the directory in which the assets will be loaded
|
-- @tparam directory string the directory in which the assets will be loaded
|
||||||
-- @tparam loaders table loaders table: {prefix = function, ...}
|
-- @tparam loaders table loaders table: {prefix = function, ...}
|
||||||
-- @tparam mode string[opt="auto"] caching mode
|
-- @tparam mode string[opt="auto"] caching mode
|
||||||
-- @impl ubiquitousse
|
|
||||||
new = function(dir, loaders, mode)
|
new = function(dir, loaders, mode)
|
||||||
local cache = {}
|
local cache = {}
|
||||||
if mode == nil or mode == "auto" then
|
if mode == nil or mode == "auto" then
|
||||||
|
|
@ -69,15 +65,12 @@ local asset = {
|
||||||
end
|
end
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
--- A prefix for asset names
|
--- A prefix for asset names
|
||||||
-- @impl ubiquitousse
|
|
||||||
prefix = dir..".",
|
prefix = dir..".",
|
||||||
|
|
||||||
--- The asset cache. Each cached asset is indexed with a string key "type.assetName".
|
--- The asset cache. Each cached asset is indexed with a string key "type.assetName".
|
||||||
-- @impl ubiquitousse
|
|
||||||
cache = cache,
|
cache = cache,
|
||||||
|
|
||||||
--- The loaders table.
|
--- The loaders table.
|
||||||
-- @impl ubiquitousse
|
|
||||||
loaders = loaders
|
loaders = loaders
|
||||||
}, asset_mt)
|
}, asset_mt)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
local uqt = require((...):match("^(.-ubiquitousse)%."))
|
|
||||||
local ctr = require("ctr")
|
|
||||||
local gfx = require("ctr.gfx")
|
|
||||||
|
|
||||||
local madeForCtr = "v1.0"
|
|
||||||
local madeForUqt = "0.0.1"
|
|
||||||
|
|
||||||
-- Check versions
|
|
||||||
local txt = ""
|
|
||||||
|
|
||||||
if ctr.version ~= madeForCtr then
|
|
||||||
txt = txt .. ("Ubiquitousse ctrµLua backend was made for ctrµLua %s but %s is used!\n")
|
|
||||||
:format(madeForCtr, uqt.version)
|
|
||||||
end
|
|
||||||
|
|
||||||
if uqt.version ~= madeForUqt then
|
|
||||||
txt = txt .. ("Ubiquitousse ctrµLua backend was made for Ubiquitousse %s but %s is used!\n")
|
|
||||||
:format(madeForUqt, uqt.version)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Show warnings
|
|
||||||
if txt ~= "" then
|
|
||||||
txt = txt .. "Things may not work as expected.\n"
|
|
||||||
print(txt)
|
|
||||||
for _=0,300 do
|
|
||||||
gfx.start(gfx.TOP)
|
|
||||||
gfx.wrappedText(0, 0, txt, gfx.TOP_WIDTH)
|
|
||||||
gfx.stop()
|
|
||||||
gfx.render()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
local uqt = require((...):match("^(.-ubiquitousse)%."))
|
|
||||||
|
|
||||||
local madeForLove = { 11, "x", "x" }
|
|
||||||
local madeForUqt = "0.0.1"
|
|
||||||
|
|
||||||
-- Check versions
|
|
||||||
local txt = ""
|
|
||||||
|
|
||||||
local actualLove = { love.getVersion() }
|
|
||||||
for i, v in ipairs(madeForLove) do
|
|
||||||
if v ~= "x" then
|
|
||||||
if actualLove[i] ~= v then
|
|
||||||
txt = txt .. ("Ubiquitousse Löve backend was made for LÖVE %s.%s.%s but %s.%s.%s is used!\n")
|
|
||||||
:format(madeForLove[1], madeForLove[2], madeForLove[3], actualLove[1], actualLove[2], actualLove[3])
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if uqt.version ~= madeForUqt then
|
|
||||||
txt = txt .. ("Ubiquitousse Löve backend was made for Ubiquitousse %s but %s is used!\n")
|
|
||||||
:format(madeForUqt, uqt.version)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Show warnings
|
|
||||||
if txt ~= "" then
|
|
||||||
txt = txt .. "Things may not work as expected.\n"
|
|
||||||
print(txt)
|
|
||||||
love.window.showMessageBox("Compatibility warning", txt, "warning")
|
|
||||||
end
|
|
||||||
|
|
@ -422,7 +422,6 @@ end
|
||||||
ecs = {
|
ecs = {
|
||||||
--- Create and returns a world system based on a list of systems.
|
--- Create and returns a world system based on a list of systems.
|
||||||
-- The systems will be instancied for this world.
|
-- The systems will be instancied for this world.
|
||||||
-- @impl ubiquitousse
|
|
||||||
world = (...)
|
world = (...)
|
||||||
let world = setmetatable({
|
let world = setmetatable({
|
||||||
filter = ecs.all(),
|
filter = ecs.all(),
|
||||||
|
|
@ -436,7 +435,6 @@ ecs = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Returns a filter that returns true if, for every argument, a field with the same name exists in the entity.
|
--- Returns a filter that returns true if, for every argument, a field with the same name exists in the entity.
|
||||||
-- @impl ubiquitousse
|
|
||||||
all = (...)
|
all = (...)
|
||||||
if ... then
|
if ... then
|
||||||
let l = {...}
|
let l = {...}
|
||||||
|
|
@ -454,7 +452,6 @@ ecs = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Returns a filter that returns true if one of the arguments if the name of a field in the entity.
|
--- Returns a filter that returns true if one of the arguments if the name of a field in the entity.
|
||||||
-- @impl ubiquitousse
|
|
||||||
any = (...)
|
any = (...)
|
||||||
if ... then
|
if ... then
|
||||||
let l = {...}
|
let l = {...}
|
||||||
|
|
@ -472,7 +469,6 @@ ecs = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- If uqt.scene is available, returns a new scene that will consist of a ECS world with the specified systems and entities.
|
--- If uqt.scene is available, returns a new scene that will consist of a ECS world with the specified systems and entities.
|
||||||
-- @impl ubiquitousse
|
|
||||||
scene = (name, systems={}, entities={})
|
scene = (name, systems={}, entities={})
|
||||||
assert(scene, "ubiquitousse.scene unavailable")
|
assert(scene, "ubiquitousse.scene unavailable")
|
||||||
let s = scene.new(name)
|
let s = scene.new(name)
|
||||||
|
|
|
||||||
52
init.lua
52
init.lua
|
|
@ -9,13 +9,8 @@
|
||||||
-- However, some modules may provide more feature when other modules are available.
|
-- However, some modules may provide more feature when other modules are available.
|
||||||
-- These dependencies are written at the top of every main module file.
|
-- These dependencies are written at the top of every main module file.
|
||||||
--
|
--
|
||||||
-- Ubiquitousse's goal is to run everywhere with the least porting effort possible, so Ubiquitousse tries to only use features that
|
-- Ubiquitousse's goal is to run everywhere with the least porting effort possible, so while the current version mainly focus LÖVE, it
|
||||||
-- are almost sure to be available everywhere.
|
-- should be easily modifiable to work with something else. Ubiquitousse should only require:
|
||||||
--
|
|
||||||
-- Some Ubiquitousse modules require functions that are not in the Lua standard library, and must therefore be implemented in a backend,
|
|
||||||
-- such as ubiquitousse.love. When required, modules will try to autodetect the engine it is running on, and load a correct backend.
|
|
||||||
--
|
|
||||||
-- Most Ubiquitousse module backends require a few things to be fully implemented:
|
|
||||||
-- * The backend needs to have access to some kind of main loop, or at least a function called very often (may or may not be the
|
-- * The backend needs to have access to some kind of main loop, or at least a function called very often (may or may not be the
|
||||||
-- same as the redraw screen callback).
|
-- same as the redraw screen callback).
|
||||||
-- * Some way of measuring time (preferably with millisecond-precision).
|
-- * Some way of measuring time (preferably with millisecond-precision).
|
||||||
|
|
@ -23,9 +18,12 @@
|
||||||
-- * Lua 5.1, 5.2, 5.3 or LuaJit.
|
-- * Lua 5.1, 5.2, 5.3 or LuaJit.
|
||||||
-- * Other requirement for specific modules should be described in the module's documentation.
|
-- * Other requirement for specific modules should be described in the module's documentation.
|
||||||
--
|
--
|
||||||
|
-- Functions that depends on LÖVE or anything that's not in the Lua standard libraries (and therefore the one you may want to port to
|
||||||
|
-- another framework) are indicated by a "-- @impl love" annotation.
|
||||||
|
--
|
||||||
-- Units used in the API documentation:
|
-- Units used in the API documentation:
|
||||||
-- * All distances are expressed in pixels (px)
|
-- * All distances are expressed in pixels (px)
|
||||||
-- * All durations are expressed in milliseconds (ms)
|
-- * All durations are expressed in seconds (ms)
|
||||||
-- These units are only used to make writing documentation easier; you can use other units if you want, as long as you're consistent.
|
-- These units are only used to make writing documentation easier; you can use other units if you want, as long as you're consistent.
|
||||||
--
|
--
|
||||||
-- Style:
|
-- Style:
|
||||||
|
|
@ -35,18 +33,6 @@
|
||||||
-- * CamelCase for class names.
|
-- * CamelCase for class names.
|
||||||
-- * lowerCamelCase is expected for everything else.
|
-- * lowerCamelCase is expected for everything else.
|
||||||
--
|
--
|
||||||
-- Implementation levels:
|
|
||||||
-- * backend: nothing defined in Ubiquitousse, must be implemented in backend
|
|
||||||
-- * mixed: partly implemented in Ubiquitousse but must be complemeted in backend.
|
|
||||||
-- * ubiquitousse: fully-working version in Ubiquitousse, may or may not be redefined in backend
|
|
||||||
-- The implementation level is indicated using the "@impl level" annotation.
|
|
||||||
--
|
|
||||||
-- For backend writers:
|
|
||||||
-- If a function defined here already contains some code, this means this code is mandatory and you must put/call
|
|
||||||
-- it in your implementation (except if the backend provides a more efficient implementation).
|
|
||||||
-- Also, a backend file shouldn't redefine the ubiquitousse table itself but only redefine the backend-dependant fields.
|
|
||||||
-- Lua 5.3: The API doesn't make the difference between numbers and integers, so convert to integers when needed.
|
|
||||||
--
|
|
||||||
-- For game writer:
|
-- For game writer:
|
||||||
-- Ubiquitousse works with Lua 5.1 to 5.3, including LuaJit, but doesn't provide any version checking or compatibility layer
|
-- Ubiquitousse works with Lua 5.1 to 5.3, including LuaJit, but doesn't provide any version checking or compatibility layer
|
||||||
-- between the different versions, so it's up to you to handle that in your game (or ignore the problem and sticks to your
|
-- between the different versions, so it's up to you to handle that in your game (or ignore the problem and sticks to your
|
||||||
|
|
@ -63,10 +49,23 @@ local ubiquitousse
|
||||||
|
|
||||||
ubiquitousse = {
|
ubiquitousse = {
|
||||||
--- Ubiquitousse version.
|
--- Ubiquitousse version.
|
||||||
-- @impl ubiquitousse
|
version = "0.1.0"
|
||||||
version = "0.0.1"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Check LÖVE version
|
||||||
|
local madeForLove = { 11, "x", "x" }
|
||||||
|
|
||||||
|
local actualLove = { love.getVersion() }
|
||||||
|
for i, v in ipairs(madeForLove) do
|
||||||
|
if v ~= "x" and actualLove[i] ~= v then
|
||||||
|
local txt = ("Ubiquitousse was made for LÖVE %s.%s.%s but %s.%s.%s is used!\nThings may not work as expected.")
|
||||||
|
:format(madeForLove[1], madeForLove[2], madeForLove[3], actualLove[1], actualLove[2], actualLove[3])
|
||||||
|
print(txt)
|
||||||
|
love.window.showMessageBox("Compatibility warning", txt, "warning")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- We're going to require modules requiring Ubiquitousse, so to avoid stack overflows we already register the ubiquitousse package
|
-- We're going to require modules requiring Ubiquitousse, so to avoid stack overflows we already register the ubiquitousse package
|
||||||
package.loaded[p] = ubiquitousse
|
package.loaded[p] = ubiquitousse
|
||||||
|
|
||||||
|
|
@ -80,13 +79,4 @@ for _, m in ipairs{"signal", "asset", "ecs", "input", "scene", "timer", "util"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Backend engine autodetect and load
|
|
||||||
if love then
|
|
||||||
require(p..".backend.love")
|
|
||||||
elseif package.loaded["ctr"] then
|
|
||||||
require(p..".backend.ctrulua")
|
|
||||||
elseif package.loaded["libretro"] then
|
|
||||||
error("NYI")
|
|
||||||
end
|
|
||||||
|
|
||||||
return ubiquitousse
|
return ubiquitousse
|
||||||
|
|
|
||||||
162
input/axis.lua
Normal file
162
input/axis.lua
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
local input = require((...):gsub("axis$", "input"))
|
||||||
|
local button_mt = require((...):gsub("axis$", "button"))
|
||||||
|
|
||||||
|
--- AxisInput methods
|
||||||
|
local axis_mt
|
||||||
|
axis_mt = {
|
||||||
|
-- Axis inputs --
|
||||||
|
-- Axis input is a container for axes detector. An axis input will return the value of the axis detector the most far away from their center (0).
|
||||||
|
-- Axis input provide a threshold setting; every axis which has a distance to the center below the threshold (none by default) will be ignored.
|
||||||
|
-- @tparam AxisDetectors ... all the axis detectors or axis identifiers
|
||||||
|
-- @tretrun AxisInput the object
|
||||||
|
_new = function(...)
|
||||||
|
local r = setmetatable({
|
||||||
|
hijackStack = {}, -- hijackers stack, last element is the object currently hijacking this input
|
||||||
|
hijacking = nil, -- object currently hijacking this input
|
||||||
|
detectors = {}, -- detectors list
|
||||||
|
val = 0, -- current value between -1 and 1
|
||||||
|
dval = 0, -- change between -2 and 2
|
||||||
|
raw = 0, -- raw value between -max and +max
|
||||||
|
max = 1, -- maximum for raw values
|
||||||
|
threshold = 0, -- ie., the deadzone
|
||||||
|
triggeringThreshold = 0.5 -- digital button threshold
|
||||||
|
}, axis_mt)
|
||||||
|
table.insert(r.hijackStack, r)
|
||||||
|
r.hijacking = r
|
||||||
|
r:bind(...)
|
||||||
|
r.positive = input.button(function() return r:value() > r.triggeringThreshold end)
|
||||||
|
r.negative = input.button(function() return r:value() < -r.triggeringThreshold end)
|
||||||
|
return r
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Returns a new AxisInput with the same properties.
|
||||||
|
-- @treturn AxisInput the cloned object
|
||||||
|
clone = function(self)
|
||||||
|
return input.axis(unpack(self.detectors))
|
||||||
|
:threshold(self.threshold)
|
||||||
|
:triggeringThreshold(self.triggeringThreshold)
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Bind new AxisDetector(s) to this input.
|
||||||
|
-- @tparam AxisDetectors ... axis detectors or axis identifiers to add
|
||||||
|
-- @treturn AxisInput this AxisInput object
|
||||||
|
bind = function(self, ...)
|
||||||
|
for _,d in ipairs({...}) do
|
||||||
|
table.insert(self.detectors, input.axisDetector(d))
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
--- Unbind AxisDetector(s).
|
||||||
|
-- @tparam AxisDetectors ... axis detectors or axis identifiers to remove
|
||||||
|
-- @treturn AxisInput this AxisInput object
|
||||||
|
unbind = button_mt.unbind,
|
||||||
|
--- Unbind all AxisDetector(s).
|
||||||
|
-- @treturn AxisInput this AxisInput object
|
||||||
|
clear = button_mt.clear,
|
||||||
|
|
||||||
|
--- Hijacks the input.
|
||||||
|
-- This function returns a new input object which mirrors the current object, except it will hijack every new input.
|
||||||
|
-- This means any value change will only be visible to the new object; the axis will always appear to be at 0 for the initial object.
|
||||||
|
-- An input can be hijacked several times; the one which hijacked it last will be the active one.
|
||||||
|
-- @treturn AxisInput the new input object which is hijacking the input
|
||||||
|
hijack = function(self)
|
||||||
|
local hijacked
|
||||||
|
hijacked = setmetatable({
|
||||||
|
positive = input.button(function() return hijacked:value() > self.triggeringThreshold end),
|
||||||
|
negative = input.button(function() return hijacked:value() < -self.triggeringThreshold end)
|
||||||
|
}, { __index = self, __newindex = self })
|
||||||
|
table.insert(self.hijackStack, hijacked)
|
||||||
|
self.hijacking = hijacked
|
||||||
|
return hijacked
|
||||||
|
end,
|
||||||
|
--- Release the input that was hijacked by this object.
|
||||||
|
-- Input will be given back to the previous object.
|
||||||
|
-- @treturn AxisInput this AxisInput object
|
||||||
|
free = button_mt.free,
|
||||||
|
|
||||||
|
--- Sets the default detection threshold (deadzone).
|
||||||
|
-- 0 by default.
|
||||||
|
-- @tparam number new the new detection threshold
|
||||||
|
-- @treturn AxisInput this AxisInput object
|
||||||
|
threshold = function(self, new)
|
||||||
|
self.threshold = tonumber(new)
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Returns the value of the input (between -1 and 1).
|
||||||
|
-- @tparam[opt=default threshold] number threshold value to use
|
||||||
|
-- @treturn number the input value
|
||||||
|
value = function(self, curThreshold)
|
||||||
|
if self.hijacking == self then
|
||||||
|
self:update()
|
||||||
|
local val = self.val
|
||||||
|
return math.abs(val) > math.abs(curThreshold or self.threshold) and val or 0
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
--- Returns the change in value of the input since last update (between -2 and 2).
|
||||||
|
-- @treturn number the value delta
|
||||||
|
delta = function(self)
|
||||||
|
if self.hijacking == self then
|
||||||
|
self:update()
|
||||||
|
return self.dval
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
--- Returns the raw value of the input (between -max and +max).
|
||||||
|
-- @tparam[opt=default threshold*max] number raw threshold value to use
|
||||||
|
-- @treturn number the input raw value
|
||||||
|
raw = function(self, rawThreshold)
|
||||||
|
if self.hijacking == self then
|
||||||
|
self:update()
|
||||||
|
local raw = self.raw
|
||||||
|
return math.abs(raw) > math.abs(rawThreshold or self.threshold*self.max) and raw or 0
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
--- Return the raw max of the input.
|
||||||
|
-- @treturn number the input raw max
|
||||||
|
max = function(self)
|
||||||
|
self:update()
|
||||||
|
return self.max
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Sets the default triggering threshold, i.e. how the minimal axis value for which the associated buttons will be considered down.
|
||||||
|
-- 0.5 by default.
|
||||||
|
-- @tparam number new the new triggering threshold
|
||||||
|
-- @treturn AxisInput this AxisInput object
|
||||||
|
triggeringThreshold = function(self, new)
|
||||||
|
self.triggeringThreshold = tonumber(new)
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- The associated button pressed when the axis reaches a positive value.
|
||||||
|
positive = nil,
|
||||||
|
--- The associated button pressed when the axis reaches a negative value.
|
||||||
|
negative = nil,
|
||||||
|
|
||||||
|
--- Update axis state.
|
||||||
|
-- Automatically called, don't call unless you know what you're doing.
|
||||||
|
update = function(self)
|
||||||
|
if not input.updated[self] then
|
||||||
|
local val, raw, max = 0, 0, 1
|
||||||
|
for _, d in ipairs(self.detectors) do
|
||||||
|
local v, r, m = d() -- v[-1,1], r[-m,+m]
|
||||||
|
if math.abs(v) > math.abs(val) then
|
||||||
|
val, raw, max = v, r or v, m or 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.dval = val - self.val
|
||||||
|
self.val, self.raw, self.max = val, raw, max
|
||||||
|
input.updated[self] = true
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- LÖVE note: other callbacks that are defined in backend/love.lua and need to be called in the associated LÖVE callbacks.
|
||||||
|
}
|
||||||
|
axis_mt.__index = axis_mt
|
||||||
|
|
||||||
|
return axis_mt
|
||||||
|
|
@ -1,277 +0,0 @@
|
||||||
local input = require((...):match("^(.-%.)backend").."input")
|
|
||||||
|
|
||||||
local loaded, signal = pcall(require, (...):match("^(.-)input").."signal")
|
|
||||||
if not loaded then signal = nil end
|
|
||||||
|
|
||||||
local gfx = require("ctr.gfx")
|
|
||||||
local hid = require("ctr.hid")
|
|
||||||
|
|
||||||
local keys = {}
|
|
||||||
local touchX, touchY, dTouchX, dTouchY
|
|
||||||
|
|
||||||
local oUpdate = input.update
|
|
||||||
input.update = function(dt)
|
|
||||||
hid.read()
|
|
||||||
|
|
||||||
keys = hid.keys()
|
|
||||||
|
|
||||||
local nTouchX, nTouchY = hid.touch()
|
|
||||||
dTouchX, dTouchY = nTouchX - touchX, nTouchY - touchY
|
|
||||||
touchX, touchY = nTouchX, nTouchY
|
|
||||||
|
|
||||||
oUpdate(dt)
|
|
||||||
end
|
|
||||||
|
|
||||||
input.buttonDetector = function(...)
|
|
||||||
local ret = {}
|
|
||||||
for _,id in ipairs({...}) do
|
|
||||||
-- Keys
|
|
||||||
if id:match("^key%.") then
|
|
||||||
local key = id:match("^key%.(.+)$")
|
|
||||||
table.insert(ret, function()
|
|
||||||
return keys.held[key]
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
error("Unknown button identifier: "..id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return table.unpack(ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
input.axisDetector = function(...)
|
|
||||||
local ret = {}
|
|
||||||
for _,id in ipairs({...}) do
|
|
||||||
-- Binary axis
|
|
||||||
if id:match(".+%,.+") then
|
|
||||||
local d1, d2 = input.buttonDetector(id:match("^(.+)%,(.+)$"))
|
|
||||||
table.insert(ret, function()
|
|
||||||
local b1, b2 = d1(), d2()
|
|
||||||
if b1 and b2 then return 0
|
|
||||||
elseif b1 then return -1
|
|
||||||
elseif b2 then return 1
|
|
||||||
else return 0 end
|
|
||||||
end)
|
|
||||||
-- Touch movement
|
|
||||||
elseif id:match("^touch%.move%.") then
|
|
||||||
local axis, threshold = id:match("^touch%.move%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^touch%.move%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, function()
|
|
||||||
local val, raw, max
|
|
||||||
if axis == "x" then
|
|
||||||
raw, max = dTouchX, gfx.BOTTOM_WIDTH
|
|
||||||
elseif axis == "y" then
|
|
||||||
raw, max = dTouchY, gfx.BOTTOM_HEIGHT
|
|
||||||
end
|
|
||||||
val = raw / max
|
|
||||||
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
|
|
||||||
end)
|
|
||||||
-- Touch position
|
|
||||||
elseif id:match("^touch%.position%.") then
|
|
||||||
local axis, threshold = id:match("^touch%.position%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^touch%.position%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, function()
|
|
||||||
local val, raw, max
|
|
||||||
if axis == "x" then
|
|
||||||
max = gfx.BOTTOM_WIDTH / 2 -- /2 because x=0,y=0 is the center of the screen (an axis value is in [-1,1])
|
|
||||||
raw = touchX - max
|
|
||||||
elseif axis == "y" then
|
|
||||||
max = gfx.BOTTOM_HEIGHT / 2
|
|
||||||
raw = touchY - max
|
|
||||||
end
|
|
||||||
val = raw / max
|
|
||||||
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
|
|
||||||
end)
|
|
||||||
-- Circle pad axis
|
|
||||||
elseif id:match("^circle%.") then
|
|
||||||
local axis, threshold = id:match("^circle%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^circle%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, function()
|
|
||||||
local x, y = hid.circle()
|
|
||||||
local val, raw, max = 0, 0, 156
|
|
||||||
if axis == "x" then raw = x
|
|
||||||
elseif axis == "y" then raw = y end
|
|
||||||
val = raw / max
|
|
||||||
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
|
|
||||||
end)
|
|
||||||
-- C-Stick axis
|
|
||||||
elseif id:match("^cstick%.") then
|
|
||||||
local axis, threshold = id:match("^cstick%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^cstick%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, function()
|
|
||||||
local x, y = hid.cstick()
|
|
||||||
local val, raw, max = 0, 0, 146
|
|
||||||
if axis == "x" then raw = x
|
|
||||||
elseif axis == "y" then raw = y end
|
|
||||||
val = raw / max
|
|
||||||
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
|
|
||||||
end)
|
|
||||||
-- Accelerometer axis
|
|
||||||
elseif id:match("^accel%.") then
|
|
||||||
local axis, threshold = id:match("^accel%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^accel%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, function()
|
|
||||||
local x, y, z = hid.accel()
|
|
||||||
local val, raw, max = 0, 0, 32768 -- no idea actually, but it's a s16
|
|
||||||
if axis == "x" then raw = x
|
|
||||||
elseif axis == "y" then raw = y
|
|
||||||
elseif axis == "z" then raw = z end
|
|
||||||
val = raw / max
|
|
||||||
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
|
|
||||||
end)
|
|
||||||
-- Gyroscope axis
|
|
||||||
elseif id:match("^gyro%.") then
|
|
||||||
local axis, threshold = id:match("^gyro%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^gyro%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, function()
|
|
||||||
local roll, pitch, yaw = hid.gyro()
|
|
||||||
local val, raw, max = 0, 0, 32768 -- no idea actually, but it's a s16
|
|
||||||
if axis == "roll" then raw = roll
|
|
||||||
elseif axis == "pitch" then raw = pitch
|
|
||||||
elseif axis == "yaw" then raw = yaw end
|
|
||||||
val = raw / max
|
|
||||||
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
error("Unknown axis identifier: "..id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return table.unpack(ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
input.buttonsInUse = function(threshold)
|
|
||||||
local r = {}
|
|
||||||
for key, held in pairs(keys.held) do
|
|
||||||
if held then table.insert(r, "key."..key) end
|
|
||||||
end
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
input.axesInUse = function(threshold)
|
|
||||||
local r = {}
|
|
||||||
threshold = threshold or 0.5
|
|
||||||
|
|
||||||
if math.abs(touchX) / gfx.BOTTOM_WIDTH > threshold then table.insert(r, "touch.position.x%"..threshold) end
|
|
||||||
if math.abs(touchY) / gfx.BOTTOM_HEIGHT > threshold then table.insert(r, "touch.position.y%"..threshold) end
|
|
||||||
|
|
||||||
if math.abs(dTouchX) / gfx.BOTTOM_WIDTH > threshold then table.insert(r, "touch.move.x%"..threshold) end
|
|
||||||
if math.abs(dTouchY) / gfx.BOTTOM_HEIGHT > threshold then table.insert(r, "touch.move.y%"..threshold) end
|
|
||||||
|
|
||||||
local circleX, circleY = hid.circle()
|
|
||||||
if math.abs(circleX) / 156 > threshold then table.insert(r, "circle.x%"..threshold) end
|
|
||||||
if math.abs(circleY) / 156 > threshold then table.insert(r, "circle.y%"..threshold) end
|
|
||||||
|
|
||||||
if ctr.apt.isNew3DS() then
|
|
||||||
local cstickX, cstickY = hid.cstick()
|
|
||||||
if math.abs(cstickY) / 146 > threshold then table.insert(r, "cstick.y%"..threshold) end
|
|
||||||
if math.abs(cstickX) / 146 > threshold then table.insert(r, "cstick.x%"..threshold) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local accelX, accelY, accelZ = hid.accel()
|
|
||||||
if math.abs(accelX) / 32768 > threshold then table.insert(r, "accel.x%"..threshold) end
|
|
||||||
if math.abs(accelY) / 32768 > threshold then table.insert(r, "accel.y%"..threshold) end
|
|
||||||
if math.abs(accelZ) / 32768 > threshold then table.insert(r, "accel.z%"..threshold) end
|
|
||||||
|
|
||||||
-- no gyro, because it is always in use
|
|
||||||
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
input.buttonName = function(...)
|
|
||||||
local ret = {}
|
|
||||||
for _,id in ipairs({...}) do
|
|
||||||
-- Key
|
|
||||||
if id:match("^key%.") then
|
|
||||||
local key = id:match("^key%.(.+)$")
|
|
||||||
table.insert(ret, key:sub(1,1):upper()..key:sub(2).." key")
|
|
||||||
else
|
|
||||||
table.insert(ret, id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return table.unpack(ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
input.axisName = function(...)
|
|
||||||
local ret = {}
|
|
||||||
for _,id in ipairs({...}) do
|
|
||||||
-- Binary axis
|
|
||||||
if id:match(".+%,.+") then
|
|
||||||
local b1, b2 = input.buttonName(id:match("^(.+)%,(.+)$"))
|
|
||||||
table.insert(ret, b1.." / "..b2)
|
|
||||||
-- Touch movement
|
|
||||||
elseif id:match("^touch%.move%.") then
|
|
||||||
local axis, threshold = id:match("^touch%.move%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^touch%.move%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, ("Touch %s movement (threshold %s%%)"):format(axis, math.abs(threshold*100)))
|
|
||||||
-- Touch position
|
|
||||||
elseif id:match("^touch%.position%.") then
|
|
||||||
local axis, threshold = id:match("^touch%.position%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^touch%.position%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, ("Touch %s position (threshold %s%%)"):format(axis, math.abs(threshold*100)))
|
|
||||||
-- Circle pad axis
|
|
||||||
elseif id:match("^circle%.") then
|
|
||||||
local axis, threshold = id:match("^circle%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^circle%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
if axis == "x" then
|
|
||||||
table.insert(ret, ("Circle pad horizontal axis (deadzone %s%%)"):format(math.abs(threshold*100)))
|
|
||||||
elseif axis == "y" then
|
|
||||||
table.insert(ret, ("Circle pad vertical axis (deadzone %s%%)"):format(math.abs(threshold*100)))
|
|
||||||
else
|
|
||||||
table.insert(ret, ("Circle pad %s axis (deadzone %s%%)"):format(axis, math.abs(threshold*100)))
|
|
||||||
end
|
|
||||||
-- C-Stick axis
|
|
||||||
elseif id:match("^cstick%.") then
|
|
||||||
local axis, threshold = id:match("^cstick%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^cstick%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
if axis == "x" then
|
|
||||||
table.insert(ret, ("C-Stick horizontal axis (deadzone %s%%)"):format(math.abs(threshold*100)))
|
|
||||||
elseif axis == "y" then
|
|
||||||
table.insert(ret, ("C-Stick vertical axis (deadzone %s%%)"):format(math.abs(threshold*100)))
|
|
||||||
else
|
|
||||||
table.insert(ret, ("C-Stick %s axis (deadzone %s%%)"):format(axis, math.abs(threshold*100)))
|
|
||||||
end
|
|
||||||
-- Accelerometer axis
|
|
||||||
elseif id:match("^accel%.") then
|
|
||||||
local axis, threshold = id:match("^accel%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^accel%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, ("Accelerometer %s axis (deadzone %s%%)"):format(axis, math.abs(threshold*100)))
|
|
||||||
-- Gyroscope axis
|
|
||||||
elseif id:match("^gyro%.") then
|
|
||||||
local axis, threshold = id:match("^gyro%.(.+)%%(.+)$")
|
|
||||||
if not axis then axis = id:match("^gyro%.(.+)$") end -- no threshold (=0)
|
|
||||||
threshold = tonumber(threshold) or 0
|
|
||||||
table.insert(ret, ("Gyroscope %s axis (deadzone %s%%)"):format(axis, math.abs(threshold*100)))
|
|
||||||
else
|
|
||||||
table.insert(ret, id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return table.unpack(ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Size
|
|
||||||
input.screenWidth, input.screenHeight = gfx.TOP_WIDTH, gfx.TOP_HEIGHT
|
|
||||||
|
|
||||||
-- Defaults
|
|
||||||
input.default.pointer:bind(
|
|
||||||
{ "absolute", "key.left,key.right", "key.up,key.down" },
|
|
||||||
{ "absolute", "circle.x", "circle.y" }
|
|
||||||
)
|
|
||||||
input.default.confirm:bind("key.a")
|
|
||||||
input.default.cancel:bind("key.b")
|
|
||||||
|
|
||||||
--- Register signals
|
|
||||||
if signal then
|
|
||||||
signal.event:replace("update", oUpdate, input.update)
|
|
||||||
end
|
|
||||||
|
|
||||||
return input
|
|
||||||
156
input/button.lua
Normal file
156
input/button.lua
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
local input = require((...):gsub("button$", "input"))
|
||||||
|
|
||||||
|
--- ButtonInput methods
|
||||||
|
local button_mt
|
||||||
|
button_mt = {
|
||||||
|
-- Buttons inputs --
|
||||||
|
-- Button input is a container for buttons detector. A button will be pressed when one of its detectors returns true.
|
||||||
|
-- Inputs also knows if the button was just pressed or released.
|
||||||
|
-- @tparam ButtonDetectors ... all the buttons detectors or buttons identifiers
|
||||||
|
-- @tretrun ButtonInput the object
|
||||||
|
_new = function(...)
|
||||||
|
local r = setmetatable({
|
||||||
|
hijackStack = {}, -- hijackers stack, last element is the object currently hijacking this input
|
||||||
|
hijacking = nil, -- object currently hijacking this input
|
||||||
|
detectors = {}, -- detectors list
|
||||||
|
state = "none" -- current state (none, pressed, down, released)
|
||||||
|
}, button_mt)
|
||||||
|
table.insert(r.hijackStack, r)
|
||||||
|
r.hijacking = r
|
||||||
|
r:bind(...)
|
||||||
|
return r
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Returns a new ButtonInput with the same properties.
|
||||||
|
-- @treturn ButtonInput the cloned object
|
||||||
|
clone = function(self)
|
||||||
|
return input.button(unpack(self.detectors))
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Bind new ButtonDetector(s) to this input.
|
||||||
|
-- @tparam ButtonDetectors ... buttons detectors or buttons identifiers to add
|
||||||
|
-- @treturn ButtonInput this ButtonInput object
|
||||||
|
bind = function(self, ...)
|
||||||
|
for _, d in ipairs({...}) do
|
||||||
|
table.insert(self.detectors, input.buttonDetector(d))
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
--- Unbind ButtonDetector(s).
|
||||||
|
-- @tparam ButtonDetectors ... buttons detectors or buttons identifiers to remove
|
||||||
|
-- @treturn ButtonInput this ButtonInput object
|
||||||
|
unbind = function(self, ...)
|
||||||
|
for _, d in ipairs({...}) do
|
||||||
|
for i=#self.detectors, 1, -1 do
|
||||||
|
if self.detectors[i] == d then
|
||||||
|
table.remove(self.detectors, i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
--- Unbind all ButtonDetector(s).
|
||||||
|
-- @treturn ButtonInput this ButtonInput object
|
||||||
|
clear = function(self)
|
||||||
|
self.detectors = {}
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Hijacks the input.
|
||||||
|
-- This function returns a new input object which mirrors the current object, except it will hijack every new input.
|
||||||
|
-- This means any new button press/down/release will only be visible to the new object; the button will always appear unpressed for the initial object.
|
||||||
|
-- This is useful for contextual input, for example if you want to display a menu without pausing the game: the menu
|
||||||
|
-- can hijack relevant inputs while it is open, so they don't trigger any action in the rest of the game.
|
||||||
|
-- An input can be hijacked several times; the one which hijacked it last will be the active one.
|
||||||
|
-- @treturn ButtonInput the new input object which is hijacking the input
|
||||||
|
hijack = function(self)
|
||||||
|
local hijacked = setmetatable({}, { __index = self, __newindex = self })
|
||||||
|
table.insert(self.hijackStack, hijacked)
|
||||||
|
self.hijacking = hijacked
|
||||||
|
return hijacked
|
||||||
|
end,
|
||||||
|
--- Release the input that was hijacked by this object.
|
||||||
|
-- Input will be given back to the previous object.
|
||||||
|
-- @treturn ButtonInput this ButtonInput object
|
||||||
|
free = function(self)
|
||||||
|
local hijackStack = self.hijackStack
|
||||||
|
for i, v in ipairs(hijackStack) do
|
||||||
|
if v == self then
|
||||||
|
table.remove(hijackStack, i)
|
||||||
|
self.hijacking = hijackStack[#hijackStack]
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error("This object is currently not hijacking this input")
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Returns true if the input was just pressed.
|
||||||
|
-- @treturn boolean true if the input was pressed, false otherwise
|
||||||
|
pressed = function(self)
|
||||||
|
if self.hijacking == self then
|
||||||
|
self:update()
|
||||||
|
return self.state == "pressed"
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
--- Returns true if the input was just released.
|
||||||
|
-- @treturn boolean true if the input was released, false otherwise
|
||||||
|
released = function(self)
|
||||||
|
if self.hijacking == self then
|
||||||
|
self:update()
|
||||||
|
return self.state == "released"
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
--- Returns true if the input is down.
|
||||||
|
-- @treturn boolean true if the input is currently down, false otherwise
|
||||||
|
down = function(self)
|
||||||
|
if self.hijacking == self then
|
||||||
|
self:update()
|
||||||
|
local state = self.state
|
||||||
|
return state == "down" or state == "pressed"
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
--- Returns true if the input is up.
|
||||||
|
-- @treturn boolean true if the input is currently up, false otherwise
|
||||||
|
up = function(self)
|
||||||
|
return not self:down()
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Update button state.
|
||||||
|
-- Automatically called, don't call unless you know what you're doing.
|
||||||
|
update = function(self)
|
||||||
|
if not input.updated[self] then
|
||||||
|
local down = false
|
||||||
|
for _, d in ipairs(self.detectors) do
|
||||||
|
if d() then
|
||||||
|
down = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local state = self.state
|
||||||
|
if down then
|
||||||
|
if state == "none" or state == "released" then
|
||||||
|
self.state = "pressed"
|
||||||
|
else
|
||||||
|
self.state = "down"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if state == "down" or state == "pressed" then
|
||||||
|
self.state = "released"
|
||||||
|
else
|
||||||
|
self.state = "none"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
input.updated[self] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
button_mt.__index = button_mt
|
||||||
|
|
||||||
|
return button_mt
|
||||||
|
|
@ -1,14 +1 @@
|
||||||
local input
|
return require((...)..".input")
|
||||||
|
|
||||||
local p = ...
|
|
||||||
if love then
|
|
||||||
input = require(p..".backend.love")
|
|
||||||
elseif package.loaded["ctr"] then
|
|
||||||
input = require(p..".backend.ctrulua")
|
|
||||||
elseif package.loaded["libretro"] then
|
|
||||||
error("NYI")
|
|
||||||
else
|
|
||||||
error("no backend for ubiquitousse.input")
|
|
||||||
end
|
|
||||||
|
|
||||||
return input
|
|
||||||
|
|
|
||||||
615
input/input.lua
615
input/input.lua
|
|
@ -10,495 +10,24 @@ if not loaded then signal = nil end
|
||||||
-- TODO: other, optional, default/generic inputs, and a way to know if they are binded.
|
-- TODO: other, optional, default/generic inputs, and a way to know if they are binded.
|
||||||
-- TODO: multiplayer input helpers? something like getting the same input for different players, or default inputs for different players
|
-- TODO: multiplayer input helpers? something like getting the same input for different players, or default inputs for different players
|
||||||
|
|
||||||
local input
|
-- FIXME https://love2d.org/forums/viewtopic.php?p=241434#p241434
|
||||||
local sqrt = math.sqrt
|
|
||||||
local unpack = table.unpack or unpack
|
|
||||||
local dt = 0
|
|
||||||
|
|
||||||
--- Used to store inputs which were updated this frame
|
local button_mt
|
||||||
-- { Input: true, ... }
|
local axis_mt
|
||||||
-- This table is for internal use and shouldn't be used from an external script.
|
local pointer_mt
|
||||||
local updated = {}
|
|
||||||
|
|
||||||
--- ButtonInput methods
|
|
||||||
-- @impl ubiquitousse
|
|
||||||
local button_mt = {
|
|
||||||
--- Returns a new ButtonInput with the same properties.
|
|
||||||
-- @treturn ButtonInput the cloned object
|
|
||||||
clone = function(self)
|
|
||||||
return input.button(unpack(self.detectors))
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Bind new ButtonDetector(s) to this input.
|
|
||||||
-- @tparam ButtonDetectors ... buttons detectors or buttons identifiers to add
|
|
||||||
-- @treturn ButtonInput this ButtonInput object
|
|
||||||
bind = function(self, ...)
|
|
||||||
for _, d in ipairs({...}) do
|
|
||||||
table.insert(self.detectors, input.buttonDetector(d))
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
--- Unbind ButtonDetector(s).
|
|
||||||
-- @tparam ButtonDetectors ... buttons detectors or buttons identifiers to remove
|
|
||||||
-- @treturn ButtonInput this ButtonInput object
|
|
||||||
unbind = function(self, ...)
|
|
||||||
for _, d in ipairs({...}) do
|
|
||||||
for i=#self.detectors, 1, -1 do
|
|
||||||
if self.detectors[i] == d then
|
|
||||||
table.remove(self.detectors, i)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
--- Unbind all ButtonDetector(s).
|
|
||||||
-- @treturn ButtonInput this ButtonInput object
|
|
||||||
clear = function(self)
|
|
||||||
self.detectors = {}
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Hijacks the input.
|
|
||||||
-- This function returns a new input object which mirrors the current object, except it will hijack every new input.
|
|
||||||
-- This means any new button press/down/release will only be visible to the new object; the button will always appear unpressed for the initial object.
|
|
||||||
-- This is useful for contextual input, for example if you want to display a menu without pausing the game: the menu
|
|
||||||
-- can hijack relevant inputs while it is open, so they don't trigger any action in the rest of the game.
|
|
||||||
-- An input can be hijacked several times; the one which hijacked it last will be the active one.
|
|
||||||
-- @treturn ButtonInput the new input object which is hijacking the input
|
|
||||||
hijack = function(self)
|
|
||||||
local hijacked = setmetatable({}, { __index = self, __newindex = self })
|
|
||||||
table.insert(self.hijackStack, hijacked)
|
|
||||||
self.hijacking = hijacked
|
|
||||||
return hijacked
|
|
||||||
end,
|
|
||||||
--- Release the input that was hijacked by this object.
|
|
||||||
-- Input will be given back to the previous object.
|
|
||||||
-- @treturn ButtonInput this ButtonInput object
|
|
||||||
free = function(self)
|
|
||||||
local hijackStack = self.hijackStack
|
|
||||||
for i, v in ipairs(hijackStack) do
|
|
||||||
if v == self then
|
|
||||||
table.remove(hijackStack, i)
|
|
||||||
self.hijacking = hijackStack[#hijackStack]
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
error("This object is currently not hijacking this input")
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Returns true if the input was just pressed.
|
|
||||||
-- @treturn boolean true if the input was pressed, false otherwise
|
|
||||||
pressed = function(self)
|
|
||||||
if self.hijacking == self then
|
|
||||||
self:update()
|
|
||||||
return self.state == "pressed"
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
--- Returns true if the input was just released.
|
|
||||||
-- @treturn boolean true if the input was released, false otherwise
|
|
||||||
released = function(self)
|
|
||||||
if self.hijacking == self then
|
|
||||||
self:update()
|
|
||||||
return self.state == "released"
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
--- Returns true if the input is down.
|
|
||||||
-- @treturn boolean true if the input is currently down, false otherwise
|
|
||||||
down = function(self)
|
|
||||||
if self.hijacking == self then
|
|
||||||
self:update()
|
|
||||||
local state = self.state
|
|
||||||
return state == "down" or state == "pressed"
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
--- Returns true if the input is up.
|
|
||||||
-- @treturn boolean true if the input is currently up, false otherwise
|
|
||||||
up = function(self)
|
|
||||||
return not self:down()
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Update button state.
|
|
||||||
-- Automatically called, don't call unless you know what you're doing.
|
|
||||||
-- @impl ubiquitousse
|
|
||||||
update = function(self)
|
|
||||||
if not updated[self] then
|
|
||||||
local down = false
|
|
||||||
for _, d in ipairs(self.detectors) do
|
|
||||||
if d() then
|
|
||||||
down = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local state = self.state
|
|
||||||
if down then
|
|
||||||
if state == "none" or state == "released" then
|
|
||||||
self.state = "pressed"
|
|
||||||
else
|
|
||||||
self.state = "down"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if state == "down" or state == "pressed" then
|
|
||||||
self.state = "released"
|
|
||||||
else
|
|
||||||
self.state = "none"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
updated[self] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
button_mt.__index = button_mt
|
|
||||||
|
|
||||||
--- AxisInput methods
|
|
||||||
-- @impl ubiquitousse
|
|
||||||
local axis_mt = {
|
|
||||||
--- Returns a new AxisInput with the same properties.
|
|
||||||
-- @treturn AxisInput the cloned object
|
|
||||||
clone = function(self)
|
|
||||||
return input.axis(unpack(self.detectors))
|
|
||||||
:threshold(self.threshold)
|
|
||||||
:triggeringThreshold(self.triggeringThreshold)
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Bind new AxisDetector(s) to this input.
|
|
||||||
-- @tparam AxisDetectors ... axis detectors or axis identifiers to add
|
|
||||||
-- @treturn AxisInput this AxisInput object
|
|
||||||
bind = function(self, ...)
|
|
||||||
for _,d in ipairs({...}) do
|
|
||||||
table.insert(self.detectors, input.axisDetector(d))
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
--- Unbind AxisDetector(s).
|
|
||||||
-- @tparam AxisDetectors ... axis detectors or axis identifiers to remove
|
|
||||||
-- @treturn AxisInput this AxisInput object
|
|
||||||
unbind = button_mt.unbind,
|
|
||||||
--- Unbind all AxisDetector(s).
|
|
||||||
-- @treturn AxisInput this AxisInput object
|
|
||||||
clear = button_mt.clear,
|
|
||||||
|
|
||||||
--- Hijacks the input.
|
|
||||||
-- This function returns a new input object which mirrors the current object, except it will hijack every new input.
|
|
||||||
-- This means any value change will only be visible to the new object; the axis will always appear to be at 0 for the initial object.
|
|
||||||
-- An input can be hijacked several times; the one which hijacked it last will be the active one.
|
|
||||||
-- @treturn AxisInput the new input object which is hijacking the input
|
|
||||||
hijack = function(self)
|
|
||||||
local hijacked
|
|
||||||
hijacked = setmetatable({
|
|
||||||
positive = input.button(function() return hijacked:value() > self.triggeringThreshold end),
|
|
||||||
negative = input.button(function() return hijacked:value() < -self.triggeringThreshold end)
|
|
||||||
}, { __index = self, __newindex = self })
|
|
||||||
table.insert(self.hijackStack, hijacked)
|
|
||||||
self.hijacking = hijacked
|
|
||||||
return hijacked
|
|
||||||
end,
|
|
||||||
--- Release the input that was hijacked by this object.
|
|
||||||
-- Input will be given back to the previous object.
|
|
||||||
-- @treturn AxisInput this AxisInput object
|
|
||||||
free = button_mt.free,
|
|
||||||
|
|
||||||
--- Sets the default detection threshold (deadzone).
|
|
||||||
-- 0 by default.
|
|
||||||
-- @tparam number new the new detection threshold
|
|
||||||
-- @treturn AxisInput this AxisInput object
|
|
||||||
threshold = function(self, new)
|
|
||||||
self.threshold = tonumber(new)
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Returns the value of the input (between -1 and 1).
|
|
||||||
-- @tparam[opt=default threshold] number threshold value to use
|
|
||||||
-- @treturn number the input value
|
|
||||||
value = function(self, curThreshold)
|
|
||||||
if self.hijacking == self then
|
|
||||||
self:update()
|
|
||||||
local val = self.val
|
|
||||||
return math.abs(val) > math.abs(curThreshold or self.threshold) and val or 0
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
--- Returns the change in value of the input since last update (between -2 and 2).
|
|
||||||
-- @treturn number the value delta
|
|
||||||
delta = function(self)
|
|
||||||
if self.hijacking == self then
|
|
||||||
self:update()
|
|
||||||
return self.dval
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
--- Returns the raw value of the input (between -max and +max).
|
|
||||||
-- @tparam[opt=default threshold*max] number raw threshold value to use
|
|
||||||
-- @treturn number the input raw value
|
|
||||||
raw = function(self, rawThreshold)
|
|
||||||
if self.hijacking == self then
|
|
||||||
self:update()
|
|
||||||
local raw = self.raw
|
|
||||||
return math.abs(raw) > math.abs(rawThreshold or self.threshold*self.max) and raw or 0
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
--- Return the raw max of the input.
|
|
||||||
-- @treturn number the input raw max
|
|
||||||
max = function(self)
|
|
||||||
self:update()
|
|
||||||
return self.max
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Sets the default triggering threshold, i.e. how the minimal axis value for which the associated buttons will be considered down.
|
|
||||||
-- 0.5 by default.
|
|
||||||
-- @tparam number new the new triggering threshold
|
|
||||||
-- @treturn AxisInput this AxisInput object
|
|
||||||
triggeringThreshold = function(self, new)
|
|
||||||
self.triggeringThreshold = tonumber(new)
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- The associated button pressed when the axis reaches a positive value.
|
|
||||||
positive = nil,
|
|
||||||
--- The associated button pressed when the axis reaches a negative value.
|
|
||||||
negative = nil,
|
|
||||||
|
|
||||||
--- Update axis state.
|
|
||||||
-- Automatically called, don't call unless you know what you're doing.
|
|
||||||
-- @impl ubiquitousse
|
|
||||||
update = function(self)
|
|
||||||
if not updated[self] then
|
|
||||||
local val, raw, max = 0, 0, 1
|
|
||||||
for _, d in ipairs(self.detectors) do
|
|
||||||
local v, r, m = d() -- v[-1,1], r[-m,+m]
|
|
||||||
if math.abs(v) > math.abs(val) then
|
|
||||||
val, raw, max = v, r or v, m or 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.dval = val - self.val
|
|
||||||
self.val, self.raw, self.max = val, raw, max
|
|
||||||
updated[self] = true
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- LÖVE note: other callbacks that are defined in backend/love.lua and need to be called in the associated LÖVE callbacks.
|
|
||||||
}
|
|
||||||
axis_mt.__index = axis_mt
|
|
||||||
|
|
||||||
--- PointerInput methods
|
|
||||||
-- @impl ubiquitousse
|
|
||||||
local pointer_mt = {
|
|
||||||
--- Returns a new PointerInput with the same properties.
|
|
||||||
-- @treturn PointerInput the cloned object
|
|
||||||
clone = function(self)
|
|
||||||
return input.pointer(unpack(self.detectors))
|
|
||||||
:dimensions(self.width, self.height)
|
|
||||||
:offset(self.offsetX, self.offsetY)
|
|
||||||
:speed(self.xSpeed, self.ySpeed)
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Bind new axis couples to this input.
|
|
||||||
-- @tparam table{mode,XAxis,YAxis} ... couples of axis detectors, axis identifiers or axis input to add and in which mode
|
|
||||||
-- @treturn PointerInput this PointerInput object
|
|
||||||
bind = function(self, ...)
|
|
||||||
for _, p in ipairs({...}) do
|
|
||||||
if type(p) == "table" then
|
|
||||||
local h, v = p[2], p[3]
|
|
||||||
if getmetatable(h) ~= axis_mt then
|
|
||||||
h = input.axis(h)
|
|
||||||
end
|
|
||||||
if getmetatable(v) ~= axis_mt then
|
|
||||||
v = input.axis(v)
|
|
||||||
end
|
|
||||||
table.insert(self.detectors, { p[1], h, v })
|
|
||||||
else
|
|
||||||
error("Pointer detector must be a table")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
--- Unbind axis couple(s).
|
|
||||||
-- @tparam table{mode,XAxis,YAxis} ... couples of axis detectors, axis identifiers or axis input to remove
|
|
||||||
-- @treturn PointerInput this PointerInput object
|
|
||||||
unbind = button_mt.unbind,
|
|
||||||
--- Unbind all axis couple(s).
|
|
||||||
-- @treturn PointerInput this PointerInput object
|
|
||||||
clear = button_mt.clear,
|
|
||||||
|
|
||||||
--- Hijacks the input.
|
|
||||||
-- This function returns a new input object which mirrors the current object, except it will hijack every new input.
|
|
||||||
-- This means any value change will only be visible to the new object; the pointer will always appear to be at offsetX,offsetY for the initial object.
|
|
||||||
-- An input can be hijacked several times; the one which hijacked it last will be the active one.
|
|
||||||
-- @treturn PointerInput the new input object which is hijacking the input
|
|
||||||
hijack = function(self)
|
|
||||||
local hijacked
|
|
||||||
hijacked = {
|
|
||||||
horizontal = input.axis(function()
|
|
||||||
local h = hijacked:x()
|
|
||||||
local width = hijacked.width
|
|
||||||
return h/width, h, width
|
|
||||||
end),
|
|
||||||
vertical = input.axis(function()
|
|
||||||
local v = hijacked:y()
|
|
||||||
local height = hijacked.height
|
|
||||||
return v/height, v, height
|
|
||||||
end)
|
|
||||||
}
|
|
||||||
hijacked.right, hijacked.left = hijacked.horizontal.positive, hijacked.horizontal.negative
|
|
||||||
hijacked.up, hijacked.down = hijacked.vertical.negative, hijacked.vertical.positive
|
|
||||||
setmetatable(hijacked, { __index = self, __newindex = self })
|
|
||||||
table.insert(self.hijackStack, hijacked)
|
|
||||||
self.hijacking = hijacked
|
|
||||||
return hijacked
|
|
||||||
end,
|
|
||||||
--- Free the input that was hijacked by this object.
|
|
||||||
-- Input will be given back to the previous object.
|
|
||||||
-- @treturn PointerInput this PointerInput object
|
|
||||||
free = button_mt.free,
|
|
||||||
|
|
||||||
--- Set the moving area half-dimensions.
|
|
||||||
-- Call without argument to use half the window dimensions.
|
|
||||||
-- It's the half dimensions because axes values goes from -1 to 1, so theses dimensions only
|
|
||||||
-- covers values from x=0,y=0 to x=1,y=1. The full moving area will be 4*newWidth*newHeight.
|
|
||||||
-- @tparam number newWidth new width
|
|
||||||
-- @tparam number newHeight new height
|
|
||||||
-- @treturn PointerInput this PointerInput object
|
|
||||||
dimensions = function(self, newWidth, newHeight)
|
|
||||||
self.width, self.height = newWidth, newHeight
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
--- Set the moving area coordinates offset.
|
|
||||||
-- The offset is a value automatically added to the x and y values when using the x() and y() methods.
|
|
||||||
-- Call without argument to automatically offset so 0,0 <= x(),y() <= width,height, i.e. offset to width,height.
|
|
||||||
-- @tparam number newOffX new X offset
|
|
||||||
-- @tparam number newOffY new Y offset
|
|
||||||
-- @treturn PointerInput this PointerInput object
|
|
||||||
offset = function(self, newOffX, newOffY)
|
|
||||||
self.offsetX, self.offsetY = newOffX, newOffY
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
--- Set maximal speed (pixels-per-milisecond)
|
|
||||||
-- Only used in relative mode.
|
|
||||||
-- Calls without argument to use the raw data and don't apply a speed modifier.
|
|
||||||
-- @tparam number newXSpeed new X speed
|
|
||||||
-- @tparam number newYSpeed new Y speed
|
|
||||||
-- @treturn PointerInput this PointerInput object
|
|
||||||
speed = function(self, newXSpeed, newYSpeed)
|
|
||||||
self.xSpeed, self.ySpeed = newXSpeed, newYSpeed or newXSpeed
|
|
||||||
return self
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Returns the current X value of the pointer.
|
|
||||||
-- @treturn number X value
|
|
||||||
x = function(self)
|
|
||||||
if self.hijacking == self then
|
|
||||||
self:update()
|
|
||||||
return self.valX + (self.offsetX or self.width or input.getDrawWidth()/2)
|
|
||||||
else
|
|
||||||
return self.offsetX or self.width or input.getDrawWidth()/2
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
--- Returns the current Y value of the pointer.
|
|
||||||
-- @treturn number Y value
|
|
||||||
y = function(self)
|
|
||||||
if self.hijacking == self then
|
|
||||||
self:update()
|
|
||||||
return self.valY + (self.offsetY or self.height or input.getDrawHeight()/2)
|
|
||||||
else
|
|
||||||
return self.offsetY or self.height or input.getDrawHeight()/2
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Returns the X and Y value of the pointer, clamped.
|
|
||||||
-- They are clamped to stay in the ellipse touching all 4 sides of the dimension rectangle, i.e. the
|
|
||||||
-- (x,y) vector's magnitude reached its maximum either in (0,height) or (width,0).
|
|
||||||
-- Typically, this is used with square dimensions for player movements: when moving diagonally, the magnitude
|
|
||||||
-- will be the same as when moving horiontally or vertically, thus avoiding faster diagonal movement, A.K.A. "straferunning".
|
|
||||||
-- If you're not conviced by my overly complicated explanation: just use this to retrieve x and y for movement and everything
|
|
||||||
-- will be fine.
|
|
||||||
-- @treturn number X value
|
|
||||||
-- @treturn number Y value
|
|
||||||
clamped = function(self)
|
|
||||||
local width, height = self.width, self.height
|
|
||||||
if self.hijacking == self then
|
|
||||||
self:update()
|
|
||||||
local x, y = self.valX, self.valY
|
|
||||||
local cx, cy = x, y
|
|
||||||
local normalizedMagnitude = (x*x)/(width*width) + (y*y)/(height*height) -- go back to a unit circle
|
|
||||||
if normalizedMagnitude > 1 then
|
|
||||||
local magnitude = sqrt(x*x + y*y)
|
|
||||||
cx, cy = cx / magnitude * width, cy / magnitude * height
|
|
||||||
end
|
|
||||||
return cx + (self.offsetX or width or input.getDrawWidth()/2), cy + (self.offsetY or height or input.getDrawHeight()/2)
|
|
||||||
else
|
|
||||||
return self.offsetX or width or input.getDrawWidth()/2, self.offsetY or height or input.getDrawHeight()/2
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- The associated horizontal axis.
|
|
||||||
horizontal = nil,
|
|
||||||
--- The associated vertical axis.
|
|
||||||
vertical = nil,
|
|
||||||
|
|
||||||
--- The associated button pressed when the pointer goes to the right.
|
|
||||||
right = nil,
|
|
||||||
--- The associated button pressed when the pointer goes to the left.
|
|
||||||
left = nil,
|
|
||||||
--- The associated button pressed when the pointer points up.
|
|
||||||
up = nil,
|
|
||||||
--- The associated button pressed when the pointer points down.
|
|
||||||
down = nil,
|
|
||||||
|
|
||||||
--- Update pointer state.
|
|
||||||
-- Automatically called, don't call unless you know what you're doing.
|
|
||||||
-- @impl ubiquitousse
|
|
||||||
update = function(self)
|
|
||||||
if not updated[self] then
|
|
||||||
local x, y = self.valX, self.valY
|
|
||||||
local xSpeed, ySpeed = self.xSpeed, self.ySpeed
|
|
||||||
local width, height = self.width or input.getDrawWidth()/2, self.height or input.getDrawHeight()/2
|
|
||||||
local newX, newY = x, y
|
|
||||||
local maxMovX, maxMovY = 0, 0 -- the maxium axis movement in a direction (used to determine which axes have the priority) (absolute value)
|
|
||||||
for _, pointer in ipairs(self.detectors) do
|
|
||||||
local mode, xAxis, yAxis = unpack(pointer)
|
|
||||||
if mode == "relative" then
|
|
||||||
local movX, movY = math.abs(xAxis:value()), math.abs(yAxis:value())
|
|
||||||
if movX > maxMovX then
|
|
||||||
newX = x + (xSpeed and (xAxis:value() * xSpeed * dt) or xAxis:raw())
|
|
||||||
maxMovX = movX
|
|
||||||
end
|
|
||||||
if movY > maxMovY then
|
|
||||||
newY = y + (ySpeed and (yAxis:value() * ySpeed * dt) or yAxis:raw())
|
|
||||||
maxMovY = movY
|
|
||||||
end
|
|
||||||
elseif mode == "absolute" then
|
|
||||||
local movX, movY = math.abs(xAxis:delta()), math.abs(yAxis:delta())
|
|
||||||
if movX > maxMovX then
|
|
||||||
newX = xAxis:value() * width
|
|
||||||
maxMovX = movX
|
|
||||||
end
|
|
||||||
if movY > maxMovY then
|
|
||||||
newY = yAxis:value() * height
|
|
||||||
maxMovY = movY
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.valX, self.valY = math.min(math.abs(newX), width) * (newX < 0 and -1 or 1), math.min(math.abs(newY), height) * (newY < 0 and -1 or 1)
|
|
||||||
updated[self] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
pointer_mt.__index = pointer_mt
|
|
||||||
|
|
||||||
--- Input stuff
|
--- Input stuff
|
||||||
-- Inspired by Tactile by Andrew Minnich (https://github.com/tesselode/tactile), under the MIT license.
|
-- Inspired by Tactile by Andrew Minnich (https://github.com/tesselode/tactile), under the MIT license.
|
||||||
-- Ubiquitousse considers two basic input methods, called buttons (binary input) and axes (analog input).
|
-- Ubiquitousse considers two basic input methods, called buttons (binary input) and axes (analog input).
|
||||||
|
local input
|
||||||
input = {
|
input = {
|
||||||
|
--- Used to store inputs which were updated this frame
|
||||||
|
-- { Input: true, ... }
|
||||||
|
-- This table is for internal use and shouldn't be used from an external script.
|
||||||
|
updated = {},
|
||||||
|
|
||||||
|
dt = 0,
|
||||||
|
|
||||||
---------------------------------
|
---------------------------------
|
||||||
--- Detectors (input sources) ---
|
--- Detectors (input sources) ---
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
@ -518,12 +47,11 @@ input = {
|
||||||
-- The function may error if the identifier is incorrect.
|
-- The function may error if the identifier is incorrect.
|
||||||
-- @tparam string button identifier, depends on the platform Ubiquitousse is running on
|
-- @tparam string button identifier, depends on the platform Ubiquitousse is running on
|
||||||
-- @treturn the new button detector
|
-- @treturn the new button detector
|
||||||
-- @impl backend
|
-- @impl love
|
||||||
basicButtonDetector = function(str) end,
|
basicButtonDetector = function(str) end,
|
||||||
|
|
||||||
--- Make a new button detector from a detector function, string, or list of buttons.
|
--- Make a new button detector from a detector function, string, or list of buttons.
|
||||||
-- @tparam string, function button identifier
|
-- @tparam string, function button identifier
|
||||||
-- @impl ubiquitousse
|
|
||||||
buttonDetector = function(obj)
|
buttonDetector = function(obj)
|
||||||
if type(obj) == "function" then
|
if type(obj) == "function" then
|
||||||
return obj
|
return obj
|
||||||
|
|
@ -557,12 +85,11 @@ input = {
|
||||||
-- The function may error if the identifier is incorrect.
|
-- The function may error if the identifier is incorrect.
|
||||||
-- @tparam string axis identifier, depends on the platform Ubiquitousse is running on
|
-- @tparam string axis identifier, depends on the platform Ubiquitousse is running on
|
||||||
-- @treturn the new axis detector
|
-- @treturn the new axis detector
|
||||||
-- @impl backend
|
-- @impl love
|
||||||
basicAxisDetector = function(str) end,
|
basicAxisDetector = function(str) end,
|
||||||
|
|
||||||
--- Make a new axis detector from a detector function, string, or a couple of buttons.
|
--- Make a new axis detector from a detector function, string, or a couple of buttons.
|
||||||
-- @tparam string, function or table axis identifier
|
-- @tparam string, function or table axis identifier
|
||||||
-- @impl ubiquitousse
|
|
||||||
axisDetector = function(obj)
|
axisDetector = function(obj)
|
||||||
if type(obj) == "function" then
|
if type(obj) == "function" then
|
||||||
return obj
|
return obj
|
||||||
|
|
@ -581,91 +108,6 @@ input = {
|
||||||
error(("Not a valid axis detector: %s"):format(obj))
|
error(("Not a valid axis detector: %s"):format(obj))
|
||||||
end,
|
end,
|
||||||
|
|
||||||
------------------------------------------
|
|
||||||
--- Inputs (the thing you want to use) ---
|
|
||||||
------------------------------------------
|
|
||||||
|
|
||||||
-- Buttons inputs --
|
|
||||||
-- Button input is a container for buttons detector. A button will be pressed when one of its detectors returns true.
|
|
||||||
-- Inputs also knows if the button was just pressed or released.
|
|
||||||
-- @tparam ButtonDetectors ... all the buttons detectors or buttons identifiers
|
|
||||||
-- @tretrun ButtonInput the object
|
|
||||||
-- @impl ubiquitousse
|
|
||||||
button = function(...)
|
|
||||||
local r = setmetatable({
|
|
||||||
hijackStack = {}, -- hijackers stack, last element is the object currently hijacking this input
|
|
||||||
hijacking = nil, -- object currently hijacking this input
|
|
||||||
detectors = {}, -- detectors list
|
|
||||||
state = "none" -- current state (none, pressed, down, released)
|
|
||||||
}, button_mt)
|
|
||||||
table.insert(r.hijackStack, r)
|
|
||||||
r.hijacking = r
|
|
||||||
r:bind(...)
|
|
||||||
return r
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Axis inputs --
|
|
||||||
-- Axis input is a container for axes detector. An axis input will return the value of the axis detector the most far away from their center (0).
|
|
||||||
-- Axis input provide a threshold setting; every axis which has a distance to the center below the threshold (none by default) will be ignored.
|
|
||||||
-- @tparam AxisDetectors ... all the axis detectors or axis identifiers
|
|
||||||
-- @tretrun AxisInput the object
|
|
||||||
-- @impl ubiquitousse
|
|
||||||
axis = function(...)
|
|
||||||
local r = setmetatable({
|
|
||||||
hijackStack = {}, -- hijackers stack, last element is the object currently hijacking this input
|
|
||||||
hijacking = nil, -- object currently hijacking this input
|
|
||||||
detectors = {}, -- detectors list
|
|
||||||
val = 0, -- current value between -1 and 1
|
|
||||||
dval = 0, -- change between -2 and 2
|
|
||||||
raw = 0, -- raw value between -max and +max
|
|
||||||
max = 1, -- maximum for raw values
|
|
||||||
threshold = 0, -- ie., the deadzone
|
|
||||||
triggeringThreshold = 0.5 -- digital button threshold
|
|
||||||
}, axis_mt)
|
|
||||||
table.insert(r.hijackStack, r)
|
|
||||||
r.hijacking = r
|
|
||||||
r:bind(...)
|
|
||||||
r.positive = input.button(function() return r:value() > r.triggeringThreshold end)
|
|
||||||
r.negative = input.button(function() return r:value() < -r.triggeringThreshold end)
|
|
||||||
return r
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Pointer inputs --
|
|
||||||
-- Pointer inputs are container for two axes input, in order to represent a two-dimensionnal pointing device, e.g. a mouse or a stick.
|
|
||||||
-- Each pointer detector is a table with 3 fields: mode(string), XAxis(axis), YAxis(axis). mode can either be "relative" or "absolute".
|
|
||||||
-- In relative mode, the pointer will return the movement since last update (for example to move a mouse pointer with a stick).
|
|
||||||
-- In absolute mode, the pointer will return the pointer position directly deduced of the current axes position.
|
|
||||||
-- @tparam table{mode,XAxis,YAxis} ... couples of axis detectors, axis identifiers or axis input to add and in which mode
|
|
||||||
-- @tretrun PointerInput the object
|
|
||||||
-- @impl ubiquitousse
|
|
||||||
pointer = function(...)
|
|
||||||
local r = setmetatable({
|
|
||||||
hijackStack = {}, -- hijackers stack, first element is the object currently hijacking this input
|
|
||||||
hijacking = nil, -- object currently hijacking this input
|
|
||||||
detectors = {}, -- pointers list (composite detectors)
|
|
||||||
valX = 0, valY = 0, -- pointer position
|
|
||||||
width = 1, height = 1, -- half-dimensions of the movement area
|
|
||||||
offsetX = 0, offsetY = 0, -- offsets
|
|
||||||
xSpeed = 1, ySpeed = 1, -- speed (pixels/milisecond); for relative mode
|
|
||||||
}, pointer_mt)
|
|
||||||
table.insert(r.hijackStack, r)
|
|
||||||
r.hijacking = r
|
|
||||||
r:bind(...)
|
|
||||||
r.horizontal = input.axis(function()
|
|
||||||
local h = r:x()
|
|
||||||
local width = r.width
|
|
||||||
return h/width, h, width
|
|
||||||
end)
|
|
||||||
r.vertical = input.axis(function()
|
|
||||||
local v = r:y()
|
|
||||||
local height = r.height
|
|
||||||
return v/height, v, height
|
|
||||||
end)
|
|
||||||
r.right, r.left = r.horizontal.positive, r.horizontal.negative
|
|
||||||
r.up, r.down = r.vertical.negative, r.vertical.positive
|
|
||||||
return r
|
|
||||||
end,
|
|
||||||
|
|
||||||
------------------------------
|
------------------------------
|
||||||
--- Input detection helpers --
|
--- Input detection helpers --
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
@ -675,13 +117,13 @@ input = {
|
||||||
-- This may also returns "axis threshold" buttons if an axis passes the threshold.
|
-- This may also returns "axis threshold" buttons if an axis passes the threshold.
|
||||||
-- @tparam[opt=0.5] number threshold the threshold to detect axes as button
|
-- @tparam[opt=0.5] number threshold the threshold to detect axes as button
|
||||||
-- @treturn string,... buttons identifiers list
|
-- @treturn string,... buttons identifiers list
|
||||||
-- @impl backend
|
-- @impl love
|
||||||
buttonUsed = function(threshold) end,
|
buttonUsed = function(threshold) end,
|
||||||
|
|
||||||
--- Returns a list of the axes currently in use, identified by their string axis identifier
|
--- Returns a list of the axes currently in use, identified by their string axis identifier
|
||||||
-- @tparam[opt=0.5] number threshold the threshold to detect axes
|
-- @tparam[opt=0.5] number threshold the threshold to detect axes
|
||||||
-- @treturn string,... axes identifiers list
|
-- @treturn string,... axes identifiers list
|
||||||
-- @impl backend
|
-- @impl love
|
||||||
axisUsed = function(threshold) end,
|
axisUsed = function(threshold) end,
|
||||||
|
|
||||||
--- Returns a nice name for the button identifier.
|
--- Returns a nice name for the button identifier.
|
||||||
|
|
@ -689,7 +131,7 @@ input = {
|
||||||
-- May returns the raw identifier if you're lazy.
|
-- May returns the raw identifier if you're lazy.
|
||||||
-- @tparam string... button identifier string(s)
|
-- @tparam string... button identifier string(s)
|
||||||
-- @treturn string... the displayable names
|
-- @treturn string... the displayable names
|
||||||
-- @impl backend
|
-- @impl love
|
||||||
buttonName = function(...) end,
|
buttonName = function(...) end,
|
||||||
|
|
||||||
--- Returns a nice name for the axis identifier.
|
--- Returns a nice name for the axis identifier.
|
||||||
|
|
@ -697,7 +139,7 @@ input = {
|
||||||
-- May returns the raw identifier if you're lazy.
|
-- May returns the raw identifier if you're lazy.
|
||||||
-- @tparam string... axis identifier string(s)
|
-- @tparam string... axis identifier string(s)
|
||||||
-- @treturn string... the displayable names
|
-- @treturn string... the displayable names
|
||||||
-- @impl backend
|
-- @impl love
|
||||||
axisName = function(...) end,
|
axisName = function(...) end,
|
||||||
|
|
||||||
-------------------
|
-------------------
|
||||||
|
|
@ -710,7 +152,7 @@ input = {
|
||||||
-- any platform without having to configure the keys.
|
-- any platform without having to configure the keys.
|
||||||
-- If some key function in your game match one of theses defaults, using it instead of creating a new
|
-- If some key function in your game match one of theses defaults, using it instead of creating a new
|
||||||
-- input would be a good idea.
|
-- input would be a good idea.
|
||||||
-- @impl mixed
|
-- @impl love
|
||||||
default = {
|
default = {
|
||||||
pointer = nil, -- Pointer: used to move and select. Example binds: arrow keys, WASD, stick.
|
pointer = nil, -- Pointer: used to move and select. Example binds: arrow keys, WASD, stick.
|
||||||
confirm = nil, -- Button: used to confirm something. Example binds: Enter, A button.
|
confirm = nil, -- Button: used to confirm something. Example binds: Enter, A button.
|
||||||
|
|
@ -719,7 +161,7 @@ input = {
|
||||||
|
|
||||||
--- Get draw area dimensions.
|
--- Get draw area dimensions.
|
||||||
-- Used for pointers.
|
-- Used for pointers.
|
||||||
-- @impl backend
|
-- @impl love
|
||||||
getDrawWidth = function() return 1 end,
|
getDrawWidth = function() return 1 end,
|
||||||
getDrawHeight = function() return 1 end,
|
getDrawHeight = function() return 1 end,
|
||||||
|
|
||||||
|
|
@ -727,10 +169,9 @@ input = {
|
||||||
-- Should be called at every game update. If ubiquitousse.signal is available, will be bound to the "update" signal in signal.event.
|
-- Should be called at every game update. If ubiquitousse.signal is available, will be bound to the "update" signal in signal.event.
|
||||||
-- The backend can hook into this function to to its input-related updates.
|
-- The backend can hook into this function to to its input-related updates.
|
||||||
-- @tparam numder dt the delta-time
|
-- @tparam numder dt the delta-time
|
||||||
-- @impl ubiquitousse
|
|
||||||
update = function(newDt)
|
update = function(newDt)
|
||||||
dt = newDt
|
input.dt = newDt
|
||||||
updated = {}
|
input.updated = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- If you use LÖVE, note that in order to provide every feature (especially key detection), several callbacks functions will
|
--- If you use LÖVE, note that in order to provide every feature (especially key detection), several callbacks functions will
|
||||||
|
|
@ -739,6 +180,16 @@ input = {
|
||||||
-- callbacks, minux the "love.").
|
-- callbacks, minux the "love.").
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package.loaded[...] = input
|
||||||
|
button_mt = require((...):gsub("input$", "button"))
|
||||||
|
axis_mt = require((...):gsub("input$", "axis"))
|
||||||
|
pointer_mt = require((...):gsub("input$", "pointer"))
|
||||||
|
|
||||||
|
-- Constructors
|
||||||
|
input.button = button_mt._new
|
||||||
|
input.axis = axis_mt._new
|
||||||
|
input.pointer = pointer_mt._new
|
||||||
|
|
||||||
-- Create default inputs
|
-- Create default inputs
|
||||||
input.default.pointer = input.pointer()
|
input.default.pointer = input.pointer()
|
||||||
input.default.confirm = input.button()
|
input.default.confirm = input.button()
|
||||||
|
|
@ -749,4 +200,6 @@ if signal then
|
||||||
signal.event:bind("update", input.update)
|
signal.event:bind("update", input.update)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require((...):gsub("input$", "love"))
|
||||||
|
|
||||||
return input
|
return input
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local input = require((...):match("^(.-%.)backend").."input")
|
local input = require((...):gsub("love$", "input"))
|
||||||
|
|
||||||
local loaded, signal = pcall(require, (...):match("^(.-)input").."signal")
|
local loaded, signal = pcall(require, (...):match("^(.-)input").."signal")
|
||||||
if not loaded then signal = nil end
|
if not loaded then signal = nil end
|
||||||
246
input/pointer.lua
Normal file
246
input/pointer.lua
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
local input = require((...):gsub("pointer$", "input"))
|
||||||
|
local button_mt = require((...):gsub("pointer$", "button"))
|
||||||
|
local axis_mt = require((...):gsub("pointer$", "axis"))
|
||||||
|
|
||||||
|
local sqrt = math.sqrt
|
||||||
|
|
||||||
|
--- PointerInput methods
|
||||||
|
local pointer_mt
|
||||||
|
pointer_mt = {
|
||||||
|
-- Pointer inputs --
|
||||||
|
-- Pointer inputs are container for two axes input, in order to represent a two-dimensionnal pointing device, e.g. a mouse or a stick.
|
||||||
|
-- Each pointer detector is a table with 3 fields: mode(string), XAxis(axis), YAxis(axis). mode can either be "relative" or "absolute".
|
||||||
|
-- In relative mode, the pointer will return the movement since last update (for example to move a mouse pointer with a stick).
|
||||||
|
-- In absolute mode, the pointer will return the pointer position directly deduced of the current axes position.
|
||||||
|
-- @tparam table{mode,XAxis,YAxis} ... couples of axis detectors, axis identifiers or axis input to add and in which mode
|
||||||
|
-- @tretrun PointerInput the object
|
||||||
|
_new = function(...)
|
||||||
|
local r = setmetatable({
|
||||||
|
hijackStack = {}, -- hijackers stack, first element is the object currently hijacking this input
|
||||||
|
hijacking = nil, -- object currently hijacking this input
|
||||||
|
detectors = {}, -- pointers list (composite detectors)
|
||||||
|
valX = 0, valY = 0, -- pointer position
|
||||||
|
width = 1, height = 1, -- half-dimensions of the movement area
|
||||||
|
offsetX = 0, offsetY = 0, -- offsets
|
||||||
|
xSpeed = 1, ySpeed = 1, -- speed (pixels/milisecond); for relative mode
|
||||||
|
}, pointer_mt)
|
||||||
|
table.insert(r.hijackStack, r)
|
||||||
|
r.hijacking = r
|
||||||
|
r:bind(...)
|
||||||
|
r.horizontal = input.axis(function()
|
||||||
|
local h = r:x()
|
||||||
|
local width = r.width
|
||||||
|
return h/width, h, width
|
||||||
|
end)
|
||||||
|
r.vertical = input.axis(function()
|
||||||
|
local v = r:y()
|
||||||
|
local height = r.height
|
||||||
|
return v/height, v, height
|
||||||
|
end)
|
||||||
|
r.right, r.left = r.horizontal.positive, r.horizontal.negative
|
||||||
|
r.up, r.down = r.vertical.negative, r.vertical.positive
|
||||||
|
return r
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Returns a new PointerInput with the same properties.
|
||||||
|
-- @treturn PointerInput the cloned object
|
||||||
|
clone = function(self)
|
||||||
|
return input.pointer(unpack(self.detectors))
|
||||||
|
:dimensions(self.width, self.height)
|
||||||
|
:offset(self.offsetX, self.offsetY)
|
||||||
|
:speed(self.xSpeed, self.ySpeed)
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Bind new axis couples to this input.
|
||||||
|
-- @tparam table{mode,XAxis,YAxis} ... couples of axis detectors, axis identifiers or axis input to add and in which mode
|
||||||
|
-- @treturn PointerInput this PointerInput object
|
||||||
|
bind = function(self, ...)
|
||||||
|
for _, p in ipairs({...}) do
|
||||||
|
if type(p) == "table" then
|
||||||
|
local h, v = p[2], p[3]
|
||||||
|
if getmetatable(h) ~= axis_mt then
|
||||||
|
h = input.axis(h)
|
||||||
|
end
|
||||||
|
if getmetatable(v) ~= axis_mt then
|
||||||
|
v = input.axis(v)
|
||||||
|
end
|
||||||
|
table.insert(self.detectors, { p[1], h, v })
|
||||||
|
else
|
||||||
|
error("Pointer detector must be a table")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
--- Unbind axis couple(s).
|
||||||
|
-- @tparam table{mode,XAxis,YAxis} ... couples of axis detectors, axis identifiers or axis input to remove
|
||||||
|
-- @treturn PointerInput this PointerInput object
|
||||||
|
unbind = button_mt.unbind,
|
||||||
|
--- Unbind all axis couple(s).
|
||||||
|
-- @treturn PointerInput this PointerInput object
|
||||||
|
clear = button_mt.clear,
|
||||||
|
|
||||||
|
--- Hijacks the input.
|
||||||
|
-- This function returns a new input object which mirrors the current object, except it will hijack every new input.
|
||||||
|
-- This means any value change will only be visible to the new object; the pointer will always appear to be at offsetX,offsetY for the initial object.
|
||||||
|
-- An input can be hijacked several times; the one which hijacked it last will be the active one.
|
||||||
|
-- @treturn PointerInput the new input object which is hijacking the input
|
||||||
|
hijack = function(self)
|
||||||
|
local hijacked
|
||||||
|
hijacked = {
|
||||||
|
horizontal = input.axis(function()
|
||||||
|
local h = hijacked:x()
|
||||||
|
local width = hijacked.width
|
||||||
|
return h/width, h, width
|
||||||
|
end),
|
||||||
|
vertical = input.axis(function()
|
||||||
|
local v = hijacked:y()
|
||||||
|
local height = hijacked.height
|
||||||
|
return v/height, v, height
|
||||||
|
end)
|
||||||
|
}
|
||||||
|
hijacked.right, hijacked.left = hijacked.horizontal.positive, hijacked.horizontal.negative
|
||||||
|
hijacked.up, hijacked.down = hijacked.vertical.negative, hijacked.vertical.positive
|
||||||
|
setmetatable(hijacked, { __index = self, __newindex = self })
|
||||||
|
table.insert(self.hijackStack, hijacked)
|
||||||
|
self.hijacking = hijacked
|
||||||
|
return hijacked
|
||||||
|
end,
|
||||||
|
--- Free the input that was hijacked by this object.
|
||||||
|
-- Input will be given back to the previous object.
|
||||||
|
-- @treturn PointerInput this PointerInput object
|
||||||
|
free = button_mt.free,
|
||||||
|
|
||||||
|
--- Set the moving area half-dimensions.
|
||||||
|
-- Call without argument to use half the window dimensions.
|
||||||
|
-- It's the half dimensions because axes values goes from -1 to 1, so theses dimensions only
|
||||||
|
-- covers values from x=0,y=0 to x=1,y=1. The full moving area will be 4*newWidth*newHeight.
|
||||||
|
-- @tparam number newWidth new width
|
||||||
|
-- @tparam number newHeight new height
|
||||||
|
-- @treturn PointerInput this PointerInput object
|
||||||
|
dimensions = function(self, newWidth, newHeight)
|
||||||
|
self.width, self.height = newWidth, newHeight
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
--- Set the moving area coordinates offset.
|
||||||
|
-- The offset is a value automatically added to the x and y values when using the x() and y() methods.
|
||||||
|
-- Call without argument to automatically offset so 0,0 <= x(),y() <= width,height, i.e. offset to width,height.
|
||||||
|
-- @tparam number newOffX new X offset
|
||||||
|
-- @tparam number newOffY new Y offset
|
||||||
|
-- @treturn PointerInput this PointerInput object
|
||||||
|
offset = function(self, newOffX, newOffY)
|
||||||
|
self.offsetX, self.offsetY = newOffX, newOffY
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
--- Set maximal speed (pixels-per-milisecond)
|
||||||
|
-- Only used in relative mode.
|
||||||
|
-- Calls without argument to use the raw data and don't apply a speed modifier.
|
||||||
|
-- @tparam number newXSpeed new X speed
|
||||||
|
-- @tparam number newYSpeed new Y speed
|
||||||
|
-- @treturn PointerInput this PointerInput object
|
||||||
|
speed = function(self, newXSpeed, newYSpeed)
|
||||||
|
self.xSpeed, self.ySpeed = newXSpeed, newYSpeed or newXSpeed
|
||||||
|
return self
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Returns the current X value of the pointer.
|
||||||
|
-- @treturn number X value
|
||||||
|
x = function(self)
|
||||||
|
if self.hijacking == self then
|
||||||
|
self:update()
|
||||||
|
return self.valX + (self.offsetX or self.width or input.getDrawWidth()/2)
|
||||||
|
else
|
||||||
|
return self.offsetX or self.width or input.getDrawWidth()/2
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
--- Returns the current Y value of the pointer.
|
||||||
|
-- @treturn number Y value
|
||||||
|
y = function(self)
|
||||||
|
if self.hijacking == self then
|
||||||
|
self:update()
|
||||||
|
return self.valY + (self.offsetY or self.height or input.getDrawHeight()/2)
|
||||||
|
else
|
||||||
|
return self.offsetY or self.height or input.getDrawHeight()/2
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Returns the X and Y value of the pointer, clamped.
|
||||||
|
-- They are clamped to stay in the ellipse touching all 4 sides of the dimension rectangle, i.e. the
|
||||||
|
-- (x,y) vector's magnitude reached its maximum either in (0,height) or (width,0).
|
||||||
|
-- Typically, this is used with square dimensions for player movements: when moving diagonally, the magnitude
|
||||||
|
-- will be the same as when moving horiontally or vertically, thus avoiding faster diagonal movement, A.K.A. "straferunning".
|
||||||
|
-- If you're not conviced by my overly complicated explanation: just use this to retrieve x and y for movement and everything
|
||||||
|
-- will be fine.
|
||||||
|
-- @treturn number X value
|
||||||
|
-- @treturn number Y value
|
||||||
|
clamped = function(self)
|
||||||
|
local width, height = self.width, self.height
|
||||||
|
if self.hijacking == self then
|
||||||
|
self:update()
|
||||||
|
local x, y = self.valX, self.valY
|
||||||
|
local cx, cy = x, y
|
||||||
|
local normalizedMagnitude = (x*x)/(width*width) + (y*y)/(height*height) -- go back to a unit circle
|
||||||
|
if normalizedMagnitude > 1 then
|
||||||
|
local magnitude = sqrt(x*x + y*y)
|
||||||
|
cx, cy = cx / magnitude * width, cy / magnitude * height
|
||||||
|
end
|
||||||
|
return cx + (self.offsetX or width or input.getDrawWidth()/2), cy + (self.offsetY or height or input.getDrawHeight()/2)
|
||||||
|
else
|
||||||
|
return self.offsetX or width or input.getDrawWidth()/2, self.offsetY or height or input.getDrawHeight()/2
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- The associated horizontal axis.
|
||||||
|
horizontal = nil,
|
||||||
|
--- The associated vertical axis.
|
||||||
|
vertical = nil,
|
||||||
|
|
||||||
|
--- The associated button pressed when the pointer goes to the right.
|
||||||
|
right = nil,
|
||||||
|
--- The associated button pressed when the pointer goes to the left.
|
||||||
|
left = nil,
|
||||||
|
--- The associated button pressed when the pointer points up.
|
||||||
|
up = nil,
|
||||||
|
--- The associated button pressed when the pointer points down.
|
||||||
|
down = nil,
|
||||||
|
|
||||||
|
--- Update pointer state.
|
||||||
|
-- Automatically called, don't call unless you know what you're doing.
|
||||||
|
update = function(self)
|
||||||
|
if not input.updated[self] then
|
||||||
|
local x, y = self.valX, self.valY
|
||||||
|
local xSpeed, ySpeed = self.xSpeed, self.ySpeed
|
||||||
|
local width, height = self.width or input.getDrawWidth()/2, self.height or input.getDrawHeight()/2
|
||||||
|
local newX, newY = x, y
|
||||||
|
local maxMovX, maxMovY = 0, 0 -- the maxium axis movement in a direction (used to determine which axes have the priority) (absolute value)
|
||||||
|
for _, pointer in ipairs(self.detectors) do
|
||||||
|
local mode, xAxis, yAxis = unpack(pointer)
|
||||||
|
if mode == "relative" then
|
||||||
|
local movX, movY = math.abs(xAxis:value()), math.abs(yAxis:value())
|
||||||
|
if movX > maxMovX then
|
||||||
|
newX = x + (xSpeed and (xAxis:value() * xSpeed * input.dt) or xAxis:raw())
|
||||||
|
maxMovX = movX
|
||||||
|
end
|
||||||
|
if movY > maxMovY then
|
||||||
|
newY = y + (ySpeed and (yAxis:value() * ySpeed * input.dt) or yAxis:raw())
|
||||||
|
maxMovY = movY
|
||||||
|
end
|
||||||
|
elseif mode == "absolute" then
|
||||||
|
local movX, movY = math.abs(xAxis:delta()), math.abs(yAxis:delta())
|
||||||
|
if movX > maxMovX then
|
||||||
|
newX = xAxis:value() * width
|
||||||
|
maxMovX = movX
|
||||||
|
end
|
||||||
|
if movY > maxMovY then
|
||||||
|
newY = yAxis:value() * height
|
||||||
|
maxMovY = movY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.valX, self.valY = math.min(math.abs(newX), width) * (newX < 0 and -1 or 1), math.min(math.abs(newY), height) * (newY < 0 and -1 or 1)
|
||||||
|
input.updated[self] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
pointer_mt.__index = pointer_mt
|
||||||
|
|
||||||
|
return pointer_mt
|
||||||
|
|
@ -22,28 +22,22 @@ if not loaded then timer = nil end
|
||||||
local scene
|
local scene
|
||||||
scene = {
|
scene = {
|
||||||
--- The current scene table.
|
--- The current scene table.
|
||||||
-- @impl ubiquitousse
|
|
||||||
current = nil,
|
current = nil,
|
||||||
|
|
||||||
--- Shortcut for scene.current.timer.
|
--- Shortcut for scene.current.timer.
|
||||||
-- @impl ubiquitousse
|
|
||||||
timer = nil,
|
timer = nil,
|
||||||
--- Shortcut for scene.current.signal.
|
--- Shortcut for scene.current.signal.
|
||||||
-- @impl ubiquitousse
|
|
||||||
signal = nil,
|
signal = nil,
|
||||||
|
|
||||||
--- The scene stack: list of scene, from the farest one to the nearest.
|
--- The scene stack: list of scene, from the farest one to the nearest.
|
||||||
-- @impl ubiquitousse
|
|
||||||
stack = {},
|
stack = {},
|
||||||
|
|
||||||
--- A prefix for scene modules names.
|
--- A prefix for scene modules names.
|
||||||
-- Will search in the "scene" directory by default. Redefine it to fit your own ridiculous filesystem.
|
-- Will search in the "scene" directory by default. Redefine it to fit your own ridiculous filesystem.
|
||||||
-- @impl ubiquitousse
|
|
||||||
prefix = "scene.",
|
prefix = "scene.",
|
||||||
|
|
||||||
--- Creates and returns a new Scene object.
|
--- Creates and returns a new Scene object.
|
||||||
-- @tparam[opt="unamed"] string name the new scene name
|
-- @tparam[opt="unamed"] string name the new scene name
|
||||||
-- @impl ubiquitousse
|
|
||||||
new = function(name)
|
new = function(name)
|
||||||
return {
|
return {
|
||||||
name = name or "unamed", -- The scene name.
|
name = name or "unamed", -- The scene name.
|
||||||
|
|
@ -68,7 +62,6 @@ scene = {
|
||||||
-- Then the stack is changed to replace the old scene with the new one.
|
-- Then the stack is changed to replace the old scene with the new one.
|
||||||
-- @tparam string/table scenePath the new scene module name, or the scene table directly
|
-- @tparam string/table scenePath the new scene module name, or the scene table directly
|
||||||
-- @param ... arguments to pass to the scene's enter function
|
-- @param ... arguments to pass to the scene's enter function
|
||||||
-- @impl ubiquitousse
|
|
||||||
switch = function(scenePath, ...)
|
switch = function(scenePath, ...)
|
||||||
local previous = scene.current
|
local previous = scene.current
|
||||||
scene.current = type(scenePath) == "string" and require(scene.prefix..scenePath) or scenePath
|
scene.current = type(scenePath) == "string" and require(scene.prefix..scenePath) or scenePath
|
||||||
|
|
@ -89,7 +82,6 @@ scene = {
|
||||||
-- will be reused.
|
-- will be reused.
|
||||||
-- @tparam string/table scenePath the new scene module name, or the scene table directly
|
-- @tparam string/table scenePath the new scene module name, or the scene table directly
|
||||||
-- @param ... arguments to pass to the scene's enter function
|
-- @param ... arguments to pass to the scene's enter function
|
||||||
-- @impl ubiquitousse
|
|
||||||
push = function(scenePath, ...)
|
push = function(scenePath, ...)
|
||||||
local previous = scene.current
|
local previous = scene.current
|
||||||
scene.current = type(scenePath) == "string" and require(scene.prefix..scenePath) or scenePath
|
scene.current = type(scenePath) == "string" and require(scene.prefix..scenePath) or scenePath
|
||||||
|
|
@ -104,7 +96,6 @@ scene = {
|
||||||
--- Pop the current scene from the scene stack.
|
--- Pop the current scene from the scene stack.
|
||||||
-- The previous scene will be set as the current scene, then the current scene exit function will be called,
|
-- The previous scene will be set as the current scene, then the current scene exit function will be called,
|
||||||
-- then the previous scene resume function will be called, and then the current scene will be removed from the stack.
|
-- then the previous scene resume function will be called, and then the current scene will be removed from the stack.
|
||||||
-- @impl ubiquitousse
|
|
||||||
pop = function()
|
pop = function()
|
||||||
local previous = scene.current
|
local previous = scene.current
|
||||||
scene.current = scene.stack[#scene.stack-1]
|
scene.current = scene.stack[#scene.stack-1]
|
||||||
|
|
@ -119,7 +110,6 @@ scene = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Pop all scenes.
|
--- Pop all scenes.
|
||||||
-- @impl ubiquitousse
|
|
||||||
popAll = function()
|
popAll = function()
|
||||||
while scene.current do
|
while scene.current do
|
||||||
scene.pop()
|
scene.pop()
|
||||||
|
|
@ -130,7 +120,6 @@ scene = {
|
||||||
-- Should be called at every game update. If ubiquitousse.signal is available, will be bound to the "update" signal in signal.event.
|
-- Should be called at every game update. If ubiquitousse.signal is available, will be bound to the "update" signal in signal.event.
|
||||||
-- @tparam number dt the delta-time (milisecond)
|
-- @tparam number dt the delta-time (milisecond)
|
||||||
-- @param ... arguments to pass to the scene's update function after dt
|
-- @param ... arguments to pass to the scene's update function after dt
|
||||||
-- @impl ubiquitousse
|
|
||||||
update = function(dt, ...)
|
update = function(dt, ...)
|
||||||
if scene.current then
|
if scene.current then
|
||||||
if timer then scene.current.timer:update(dt) end
|
if timer then scene.current.timer:update(dt) end
|
||||||
|
|
@ -141,7 +130,6 @@ scene = {
|
||||||
--- Draw the current scene.
|
--- Draw the current scene.
|
||||||
-- Should be called every time the game is draw. If ubiquitousse.signal is available, will be bound to the "draw" signal in signal.event.
|
-- Should be called every time the game is draw. If ubiquitousse.signal is available, will be bound to the "draw" signal in signal.event.
|
||||||
-- @param ... arguments to pass to the scene's draw function
|
-- @param ... arguments to pass to the scene's draw function
|
||||||
-- @impl ubiquitousse
|
|
||||||
draw = function(...)
|
draw = function(...)
|
||||||
if scene.current then scene.current:draw(...) end
|
if scene.current then scene.current:draw(...) end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
local signal = require((...):match("^(.-%.)backend").."signal")
|
|
||||||
|
|
||||||
function signal.registerEvents()
|
|
||||||
local callbacks = { -- everything except run, errorhandler, threaderror
|
|
||||||
"displayrotated", "draw", "load", "lowmemory", "quit", "update",
|
|
||||||
"directorydropped", "filedropped", "focus", "mousefocus", "resize", "visible",
|
|
||||||
"keypressed", "keyreleased", "textedited", "textinput",
|
|
||||||
"mousemoved", "mousepressed", "mousereleased", "wheelmoved",
|
|
||||||
"gamepadaxis", "gamepadpressed", "gamepadreleased",
|
|
||||||
"joystickadded", "joystickaxis", "joystickhat", "joystickpressed", "joystickreleased", "joystickremoved",
|
|
||||||
"touchmoved", "touchpressed", "touchreleased"
|
|
||||||
}
|
|
||||||
local event = signal.event
|
|
||||||
for _, callback in ipairs(callbacks) do
|
|
||||||
if callback == "update" then
|
|
||||||
if love[callback] then
|
|
||||||
local old = love[callback]
|
|
||||||
love[callback] = function(dt)
|
|
||||||
old(dt)
|
|
||||||
event:emit(callback, dt)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
love[callback] = function(dt)
|
|
||||||
event:emit(callback, dt)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if love[callback] then
|
|
||||||
local old = love[callback]
|
|
||||||
love[callback] = function(...)
|
|
||||||
old(...)
|
|
||||||
event:emit(callback, ...)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
love[callback] = function(...)
|
|
||||||
event:emit(callback, ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return signal
|
|
||||||
|
|
@ -1,14 +1 @@
|
||||||
local signal
|
return require((...)..".signal")
|
||||||
|
|
||||||
local p = ...
|
|
||||||
if love then
|
|
||||||
signal = require(p..".backend.love")
|
|
||||||
elseif package.loaded["ctr"] then
|
|
||||||
error("NYI")
|
|
||||||
elseif package.loaded["libretro"] then
|
|
||||||
error("NYI")
|
|
||||||
else
|
|
||||||
error("no backend for ubiquitousse.signal")
|
|
||||||
end
|
|
||||||
|
|
||||||
return signal
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
let registry_mt = {
|
let registry_mt = {
|
||||||
--- Map of signals to list of listeners.
|
--- Map of signals to list of listeners.
|
||||||
-- @impl ubiquitousse
|
|
||||||
signals = {},
|
signals = {},
|
||||||
|
|
||||||
--- Bind one or several functions to a signal name.
|
--- Bind one or several functions to a signal name.
|
||||||
-- @impl ubiquitousse
|
|
||||||
bind = :(name, fn, ...)
|
bind = :(name, fn, ...)
|
||||||
if not @signals[name] then
|
if not @signals[name] then
|
||||||
@signals[name] = {}
|
@signals[name] = {}
|
||||||
|
|
@ -18,7 +16,6 @@ let registry_mt = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Unbind one or several functions to a signal name.
|
--- Unbind one or several functions to a signal name.
|
||||||
-- @impl ubiquitousse
|
|
||||||
unbind = :(name, fn, ...)
|
unbind = :(name, fn, ...)
|
||||||
if not @signals[name] then
|
if not @signals[name] then
|
||||||
return
|
return
|
||||||
|
|
@ -34,13 +31,11 @@ let registry_mt = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Remove every bound function to a signal name.
|
--- Remove every bound function to a signal name.
|
||||||
-- @impl ubiquitousse
|
|
||||||
unbindAll = :(name)
|
unbindAll = :(name)
|
||||||
@signals[name] = nil
|
@signals[name] = nil
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Replace a bound function with another function.
|
--- Replace a bound function with another function.
|
||||||
-- @impl ubiquitousse
|
|
||||||
replace = :(name, sourceFn, destFn)
|
replace = :(name, sourceFn, destFn)
|
||||||
if not @signals[name] then
|
if not @signals[name] then
|
||||||
@signals[name] = {}
|
@signals[name] = {}
|
||||||
|
|
@ -54,13 +49,11 @@ let registry_mt = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Remove every bound function to every signal.
|
--- Remove every bound function to every signal.
|
||||||
-- @impl ubiquitousse
|
|
||||||
clear = :()
|
clear = :()
|
||||||
@signals = {}
|
@signals = {}
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Emit a signal, i.e. call every function bound to it, with the given arguments.
|
--- Emit a signal, i.e. call every function bound to it, with the given arguments.
|
||||||
-- @impl ubiquitousse
|
|
||||||
emit = :(name, ...)
|
emit = :(name, ...)
|
||||||
if @signals[name] then
|
if @signals[name] then
|
||||||
for _, fn in ipairs(@signals[name]) do
|
for _, fn in ipairs(@signals[name]) do
|
||||||
|
|
@ -74,13 +67,11 @@ registry_mt.__index = registry_mt
|
||||||
let signal = {
|
let signal = {
|
||||||
--- Creates and return a new SignalRegistry.
|
--- Creates and return a new SignalRegistry.
|
||||||
-- A SignalRegistry is a separate ubiquitousse.signal instance: its signals will be independant from other registries.
|
-- A SignalRegistry is a separate ubiquitousse.signal instance: its signals will be independant from other registries.
|
||||||
-- @impl ubiquitousse
|
|
||||||
new = ()
|
new = ()
|
||||||
return setmetatable({ signals = {} }, registry_mt)
|
return setmetatable({ signals = {} }, registry_mt)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Global SignalRegistry.
|
--- Global SignalRegistry.
|
||||||
-- @impl ubiquitousse
|
|
||||||
signals = {},
|
signals = {},
|
||||||
bind = (...)
|
bind = (...)
|
||||||
return registry_mt.bind(signal, ...)
|
return registry_mt.bind(signal, ...)
|
||||||
|
|
@ -102,13 +93,50 @@ let signal = {
|
||||||
-- * update(dt), should be called on every game update
|
-- * update(dt), should be called on every game update
|
||||||
-- * draw, should be called on every game draw
|
-- * draw, should be called on every game draw
|
||||||
-- * for LÖVE, there are callbacks for every LÖVE callback function that need to be called on their corresponding LÖVE callback
|
-- * for LÖVE, there are callbacks for every LÖVE callback function that need to be called on their corresponding LÖVE callback
|
||||||
-- @impl mixed
|
|
||||||
event = nil,
|
event = nil,
|
||||||
|
|
||||||
--- Call this function to hook signal.event signals to the current backend.
|
--- Call this function to hook signal.event signals to the current backend.
|
||||||
-- For LÖVE, this means overriding every existing LÖVE callback. If a callback is already defined, the new one will call the old function along with the signal:emit.
|
-- For LÖVE, this means overriding every existing LÖVE callback. If a callback is already defined, the new one will call the old function along with the signal:emit.
|
||||||
-- @impl backend
|
-- @impl love
|
||||||
registerEvents = () end
|
registerEvents = ()
|
||||||
|
local callbacks = { -- everything except run, errorhandler, threaderror
|
||||||
|
"displayrotated", "draw", "load", "lowmemory", "quit", "update",
|
||||||
|
"directorydropped", "filedropped", "focus", "mousefocus", "resize", "visible",
|
||||||
|
"keypressed", "keyreleased", "textedited", "textinput",
|
||||||
|
"mousemoved", "mousepressed", "mousereleased", "wheelmoved",
|
||||||
|
"gamepadaxis", "gamepadpressed", "gamepadreleased",
|
||||||
|
"joystickadded", "joystickaxis", "joystickhat", "joystickpressed", "joystickreleased", "joystickremoved",
|
||||||
|
"touchmoved", "touchpressed", "touchreleased"
|
||||||
|
}
|
||||||
|
local event = signal.event
|
||||||
|
for _, callback in ipairs(callbacks) do
|
||||||
|
if callback == "update" then
|
||||||
|
if love[callback] then
|
||||||
|
local old = love[callback]
|
||||||
|
love[callback] = function(dt)
|
||||||
|
old(dt)
|
||||||
|
event:emit(callback, dt)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
love[callback] = function(dt)
|
||||||
|
event:emit(callback, dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if love[callback] then
|
||||||
|
local old = love[callback]
|
||||||
|
love[callback] = function(...)
|
||||||
|
old(...)
|
||||||
|
event:emit(callback, ...)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
love[callback] = function(...)
|
||||||
|
event:emit(callback, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
signal.event = signal.new()
|
signal.event = signal.new()
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,6 @@ local timer_mt = {
|
||||||
--- Update the timer.
|
--- Update the timer.
|
||||||
-- Should be called at every game update.
|
-- Should be called at every game update.
|
||||||
-- @tparam number dt the delta-time (time spent since last time the function was called) (miliseconds)
|
-- @tparam number dt the delta-time (time spent since last time the function was called) (miliseconds)
|
||||||
-- @impl ubiquitousse
|
|
||||||
update = function(self, dt)
|
update = function(self, dt)
|
||||||
local t = self.t
|
local t = self.t
|
||||||
if not t.dead then
|
if not t.dead then
|
||||||
|
|
@ -199,7 +198,6 @@ local timer_mt = {
|
||||||
-- You shouldn't need to worry about this if your timer belongs to a registry.
|
-- You shouldn't need to worry about this if your timer belongs to a registry.
|
||||||
-- If you don't use registries, you probably should purge dead timers to free up some memory (dead timers don't do anything otherwise).
|
-- If you don't use registries, you probably should purge dead timers to free up some memory (dead timers don't do anything otherwise).
|
||||||
-- @treturn bool true if the timer can be discarded, false if it's still active.
|
-- @treturn bool true if the timer can be discarded, false if it's still active.
|
||||||
-- @impl ubiquitousse
|
|
||||||
dead = function(self)
|
dead = function(self)
|
||||||
return self.t.dead
|
return self.t.dead
|
||||||
end
|
end
|
||||||
|
|
@ -211,7 +209,6 @@ local registry_mt = {
|
||||||
--- Update all the timers in the registry.
|
--- Update all the timers in the registry.
|
||||||
-- Should be called at every game update; called by ubiquitousse.update.
|
-- Should be called at every game update; called by ubiquitousse.update.
|
||||||
-- @tparam number dt the delta-time (time spent since last time the function was called) (miliseconds)
|
-- @tparam number dt the delta-time (time spent since last time the function was called) (miliseconds)
|
||||||
-- @impl ubiquitousse
|
|
||||||
update = function(self, dt)
|
update = function(self, dt)
|
||||||
-- process timers
|
-- process timers
|
||||||
for _, timer in ipairs(self.timers) do
|
for _, timer in ipairs(self.timers) do
|
||||||
|
|
@ -228,7 +225,6 @@ local registry_mt = {
|
||||||
|
|
||||||
--- Create a new timer and add it to the registry.
|
--- Create a new timer and add it to the registry.
|
||||||
-- Same as timer_module.run, but add it to the registry.
|
-- Same as timer_module.run, but add it to the registry.
|
||||||
-- @impl ubiquitousse
|
|
||||||
run = function(self, func)
|
run = function(self, func)
|
||||||
local r = timer_module.run(func)
|
local r = timer_module.run(func)
|
||||||
table.insert(self.timers, r)
|
table.insert(self.timers, r)
|
||||||
|
|
@ -237,7 +233,6 @@ local registry_mt = {
|
||||||
|
|
||||||
--- Create a new tween timer and add it to the registry.
|
--- Create a new tween timer and add it to the registry.
|
||||||
-- Same as timer_module.tween, but add it to the registry.
|
-- Same as timer_module.tween, but add it to the registry.
|
||||||
-- @impl ubiquitousse
|
|
||||||
tween = function(self, duration, tbl, to, method)
|
tween = function(self, duration, tbl, to, method)
|
||||||
local r = timer_module.tween(duration, tbl, to, method)
|
local r = timer_module.tween(duration, tbl, to, method)
|
||||||
table.insert(self.timers, r)
|
table.insert(self.timers, r)
|
||||||
|
|
@ -245,7 +240,6 @@ local registry_mt = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Cancels all the running timers in this registry.
|
--- Cancels all the running timers in this registry.
|
||||||
-- @impl ubiquitousse
|
|
||||||
clear = function(self)
|
clear = function(self)
|
||||||
self.timers = {}
|
self.timers = {}
|
||||||
end
|
end
|
||||||
|
|
@ -258,7 +252,6 @@ timer_module = {
|
||||||
-- A timer registry provides an easy way to handle your timers; it will keep track of them,
|
-- A timer registry provides an easy way to handle your timers; it will keep track of them,
|
||||||
-- updating and removing them as needed. If you use the scene system, a scene-specific
|
-- updating and removing them as needed. If you use the scene system, a scene-specific
|
||||||
-- timer registry is available at ubiquitousse.scene.current.timer.
|
-- timer registry is available at ubiquitousse.scene.current.timer.
|
||||||
-- @impl ubiquitousse
|
|
||||||
new = function()
|
new = function()
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
--- Used to store all the functions delayed with ubiquitousse.time.delay
|
--- Used to store all the functions delayed with ubiquitousse.time.delay
|
||||||
|
|
@ -277,7 +270,6 @@ timer_module = {
|
||||||
-- don't want to handle your timers manually.
|
-- don't want to handle your timers manually.
|
||||||
-- @tparam[opt] function func the function to schedule
|
-- @tparam[opt] function func the function to schedule
|
||||||
-- @treturn timer the object
|
-- @treturn timer the object
|
||||||
-- @impl ubiquitousse
|
|
||||||
run = function(func)
|
run = function(func)
|
||||||
local r = setmetatable({
|
local r = setmetatable({
|
||||||
t = {
|
t = {
|
||||||
|
|
@ -316,7 +308,6 @@ timer_module = {
|
||||||
-- @tparam table to the new values
|
-- @tparam table to the new values
|
||||||
-- @tparam[opt="linear"] string/function method tweening method (string name or the actual function(time, start, change, duration))
|
-- @tparam[opt="linear"] string/function method tweening method (string name or the actual function(time, start, change, duration))
|
||||||
-- @treturn timer the object. A duration is already defined, and the :chain methods takes the same arguments as tween (and creates a tween).
|
-- @treturn timer the object. A duration is already defined, and the :chain methods takes the same arguments as tween (and creates a tween).
|
||||||
-- @impl ubiquitousse
|
|
||||||
tween = function(duration, tbl, to, method)
|
tween = function(duration, tbl, to, method)
|
||||||
method = method or "linear"
|
method = method or "linear"
|
||||||
method = type(method) == "string" and ease[method] or method
|
method = type(method) == "string" and ease[method] or method
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue