1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
-- license:BSD-3-Clause
-- copyright-holders:Vas Crabb
local exports = {
name = 'inputmacro',
version = '0.0.1',
description = 'Input macro plugin',
license = 'BSD-3-Clause',
author = { name = 'Vas Crabb' } }
local inputmacro = exports
local frame_subscription, stop_subscription
function inputmacro.startplugin()
--[[
Configuration data:
* name: display name (string)
* binding: activation sequence (input sequence)
* bindingcfg: activation sequence configuration (string)
* earlycancel: cancel or complete on release (Boolean)
* loop: -1 = release, 0 = prolong, >0 = loop to step on hold (integer)
* steps:
* inputs:
* port: port tag (string)
* mask: port field mask (integer)
* type: port field type (integer)
* field: field (I/O port field)
* delay: delay before activating inputs in frames (integer)
* duration: duration to activate inputs for (integer)
Live state:
* step: current step (integer or nil)
* frame: frame of current step, starting at 1 (integer)
]]
local macros = { }
local active_inputs = { }
local menu
local input
local function activate_inputs(inputs)
for index, input in ipairs(inputs) do
if input.field then
active_inputs[string.format('%s.%d.%d', input.port, input.mask, input.type)] = input.field
end
end
end
local function process_frame()
local previous_inputs = active_inputs
active_inputs = { }
for index, macro in ipairs(macros) do
if macro.step then
if macro.earlycancel and (not input:seq_pressed(macro.binding)) then
-- stop immediately on release if early cancel set
macro.step = nil
else
-- advance frame
macro.frame = macro.frame + 1
local step = macro.steps[macro.step]
if macro.frame > (step.delay + step.duration) then
if macro.step < #macro.steps then
-- not the last step, advance step
macro.step = macro.step + 1
macro.frame = 1
step = macro.steps[macro.step]
elseif not input:seq_pressed(macro.binding) then
-- input released and macro completed
macro.step = nil
step = nil
elseif macro.loop > 0 then
-- loop to step
macro.step = macro.loop
macro.frame = 1
elseif macro.loop < 0 then
-- release if held
step = nil
end
end
if step and (macro.frame > step.delay) then
activate_inputs(step.inputs)
end
end
elseif input:seq_pressed(macro.binding) then
-- initial activation
macro.step = 1
macro.frame = 1
local step = macro.steps[1]
if step.delay == 0 then
-- no delay on first step, activate inputs
activate_inputs(step.inputs)
end
end
end
for key, field in pairs(active_inputs) do
field:set_value(1)
end
for key, field in pairs(previous_inputs) do
if not active_inputs[key] then
field:clear_value()
end
end
end
local function start()
input = manager.machine.input
local persister = require('inputmacro/inputmacro_persist')
macros = persister.load_settings()
end
local function stop()
local persister = require('inputmacro/inputmacro_persist')
persister:save_settings(macros)
macros = { }
active_inputs = { }
menu = nil
end
local function menu_callback(index, event)
return menu:handle_event(index, event)
end
local function menu_populate()
if not menu then
menu = require('inputmacro/inputmacro_menu')
menu:init(macros)
end
return menu:populate()
end
frame_subscription = emu.add_machine_frame_notifier(process_frame)
emu.register_prestart(start)
stop_subscription = emu.add_machine_stop_notifier(stop)
emu.register_menu(menu_callback, menu_populate, _p('plugin-inputmacro', 'Input Macros'))
end
return exports
|