plugins: Reduced amnesia for autofire and inputmacro plugins.

Made autofire and inputmacro plugins capable of remembering settings if
the host input device for the binding is missing or if an input for a
slot device that isn't present is referenced.
This commit is contained in:
Vas Crabb 2021-11-05 02:46:04 +11:00
parent f196989f8b
commit de9ed12186
8 changed files with 131 additions and 89 deletions

View File

@ -71,9 +71,9 @@ local function populate_main_menu(buttons)
local ioport = manager.machine.ioport local ioport = manager.machine.ioport
local input = manager.machine.input local input = manager.machine.input
local menu = {} local menu = {}
menu[#menu + 1] = {_p('plugin-autofire', 'Autofire buttons'), '', 'off'} table.insert(menu, {_p('plugin-autofire', 'Autofire buttons'), '', 'off'})
menu[#menu + 1] = {string.format(_p('plugin-autofire', 'Press %s to delete'), manager.ui:get_general_input_setting(ioport:token_to_input_type('UI_CLEAR'))), '', 'off'} table.insert(menu, {string.format(_p('plugin-autofire', 'Press %s to delete'), manager.ui:get_general_input_setting(ioport:token_to_input_type('UI_CLEAR'))), '', 'off'})
menu[#menu + 1] = {'---', '', ''} table.insert(menu, {'---', '', ''})
header_height = #menu header_height = #menu
-- Use frame rate of first screen or 60Hz if no screens -- Use frame rate of first screen or 60Hz if no screens
@ -88,21 +88,25 @@ local function populate_main_menu(buttons)
-- Round rate to two decimal places -- Round rate to two decimal places
local rate = freq / (button.on_frames + button.off_frames) local rate = freq / (button.on_frames + button.off_frames)
rate = math.floor(rate * 100) / 100 rate = math.floor(rate * 100) / 100
local text = string.format(_p('plugin-autofire', '%s [%g Hz]'), _p('input-name', button.button.name), rate) local text
local subtext = input:seq_name(button.key) if button.button then
menu[#menu + 1] = {text, subtext, ''} text = string.format(_p('plugin-autofire', '%s [%g Hz]'), _p('input-name', button.button.name), rate)
else
text = string.format(_p('plugin-autofire', 'n/a [%g Hz]'), rate)
end
table.insert(menu, {text, input:seq_name(button.key), ''})
if index == initial_button then if index == initial_button then
main_selection_save = #menu main_selection_save = #menu
end end
end end
else else
menu[#menu + 1] = {_p('plugin-autofire', '[no autofire buttons]'), '', 'off'} table.insert(menu, {_p('plugin-autofire', '[no autofire buttons]'), '', 'off'})
end end
initial_button = nil initial_button = nil
content_height = #menu content_height = #menu
menu[#menu + 1] = {'---', '', ''} table.insert(menu, {'---', '', ''})
menu[#menu + 1] = {_p('plugin-autofire', 'Add autofire button'), '', ''} table.insert(menu, {_p('plugin-autofire', 'Add autofire button'), '', ''})
local selection = main_selection_save local selection = main_selection_save
main_selection_save = nil main_selection_save = nil
@ -139,15 +143,22 @@ end
-- Add/edit menus (mostly identical) -- Add/edit menus (mostly identical)
local function populate_configure_menu(menu) local function populate_configure_menu(menu)
local button_name = current_button.button and _p('input-name', current_button.button.name) or _p('plugin-autofire', '[not set]') local button_name
if current_button.button then
button_name = _p('input-name', current_button.button.name)
elseif current_button.port then
button_name = _p('plugin-autofire', 'n/a')
else
button_name = _p('plugin-autofire', '[not set]')
end
local key_name = current_button.key and manager.machine.input:seq_name(current_button.key) or _p('plugin-autofire', '[not set]') local key_name = current_button.key and manager.machine.input:seq_name(current_button.key) or _p('plugin-autofire', '[not set]')
menu[#menu + 1] = {_p('plugin-autofire', 'Input'), button_name, ''} table.insert(menu, {_p('plugin-autofire', 'Input'), button_name, ''})
if not (configure_menu_active or configure_selection_save) then if not (configure_menu_active or configure_selection_save) then
configure_selection_save = #menu configure_selection_save = #menu
end end
menu[#menu + 1] = {_p('plugin-autofire', 'Hotkey'), key_name, hotkey_poller and 'lr' or ''} table.insert(menu, {_p('plugin-autofire', 'Hotkey'), key_name, hotkey_poller and 'lr' or ''})
menu[#menu + 1] = {_p('plugin-autofire', 'On frames'), current_button.on_frames, current_button.on_frames > 1 and 'lr' or 'r'} table.insert(menu, {_p('plugin-autofire', 'On frames'), current_button.on_frames, current_button.on_frames > 1 and 'lr' or 'r'})
menu[#menu + 1] = {_p('plugin-autofire', 'Off frames'), current_button.off_frames, current_button.off_frames > 1 and 'lr' or 'r'} table.insert(menu, {_p('plugin-autofire', 'Off frames'), current_button.off_frames, current_button.off_frames > 1 and 'lr' or 'r'})
configure_menu_active = true configure_menu_active = true
end end
@ -157,6 +168,7 @@ local function handle_configure_menu(index, event)
if hotkey_poller:poll() then if hotkey_poller:poll() then
if hotkey_poller.sequence then if hotkey_poller.sequence then
current_button.key = hotkey_poller.sequence current_button.key = hotkey_poller.sequence
current_button.key_cfg = manager.machine.input:seq_to_tokens(hotkey_poller.sequence)
end end
hotkey_poller = nil hotkey_poller = nil
return true return true
@ -215,15 +227,15 @@ end
local function populate_edit_menu() local function populate_edit_menu()
local menu = {} local menu = {}
menu[#menu + 1] = {_p('plugin-autofire', 'Edit autofire button'), '', 'off'} table.insert(menu, {_p('plugin-autofire', 'Edit autofire button'), '', 'off'})
menu[#menu + 1] = {'---', '', ''} table.insert(menu, {'---', '', ''})
header_height = #menu header_height = #menu
populate_configure_menu(menu) populate_configure_menu(menu)
content_height = #menu content_height = #menu
menu[#menu + 1] = {'---', '', ''} table.insert(menu, {'---', '', ''})
menu[#menu + 1] = {_p('plugin-autofire', 'Done'), '', ''} table.insert(menu, {_p('plugin-autofire', 'Done'), '', ''})
local selection = configure_selection_save local selection = configure_selection_save
configure_selection_save = nil configure_selection_save = nil
@ -248,18 +260,18 @@ end
local function populate_add_menu() local function populate_add_menu()
local menu = {} local menu = {}
menu[#menu + 1] = {_p('plugin-autofire', 'Add autofire button'), '', 'off'} table.insert(menu, {_p('plugin-autofire', 'Add autofire button'), '', 'off'})
menu[#menu + 1] = {'---', '', ''} table.insert(menu, {'---', '', ''})
header_height = #menu header_height = #menu
populate_configure_menu(menu) populate_configure_menu(menu)
content_height = #menu content_height = #menu
menu[#menu + 1] = {'---', '', ''} table.insert(menu, {'---', '', ''})
if is_button_complete(current_button) then if is_button_complete(current_button) then
menu[#menu + 1] = {_p('plugin-autofire', 'Create'), '', ''} table.insert(menu, {_p('plugin-autofire', 'Create'), '', ''})
else else
menu[#menu + 1] = {_p('plugin-autofire', 'Cancel'), '', ''} table.insert(menu, {_p('plugin-autofire', 'Cancel'), '', ''})
end end
local selection = configure_selection_save local selection = configure_selection_save

View File

@ -16,6 +16,7 @@ local function initialize_button(settings)
mask = settings.mask, mask = settings.mask,
type = ioport:token_to_input_type(settings.type), type = ioport:token_to_input_type(settings.type),
key = manager.machine.input:seq_from_tokens(settings.key), key = manager.machine.input:seq_from_tokens(settings.key),
key_cfg = settings.key,
on_frames = settings.on_frames, on_frames = settings.on_frames,
off_frames = settings.off_frames, off_frames = settings.off_frames,
counter = 0 counter = 0
@ -25,9 +26,9 @@ local function initialize_button(settings)
local field = port:field(settings.mask) local field = port:field(settings.mask)
if field and (field.type == new_button.type) then if field and (field.type == new_button.type) then
new_button.button = field new_button.button = field
return new_button
end end
end end
return new_button
end end
return nil return nil
end end
@ -35,15 +36,15 @@ end
local function serialize_settings(button_list) local function serialize_settings(button_list)
local settings = {} local settings = {}
for index, button in ipairs(button_list) do for index, button in ipairs(button_list) do
setting = { local setting = {
port = button.port, port = button.port,
mask = button.button.mask, mask = button.mask,
type = manager.machine.ioport:input_type_to_token(button.button.type), type = manager.machine.ioport:input_type_to_token(button.type),
key = manager.machine.input:seq_to_tokens(button.key), key = button.key_cfg,
on_frames = button.on_frames, on_frames = button.on_frames,
off_frames = button.off_frames off_frames = button.off_frames
} }
settings[#settings + 1] = setting table.insert(settings, setting)
end end
return settings return settings
end end

View File

@ -16,6 +16,7 @@ function autofire.startplugin()
-- 'mask' - mask of the button field being autofired -- 'mask' - mask of the button field being autofired
-- 'type' - input type of the button being autofired -- 'type' - input type of the button being autofired
-- 'key' - input_seq of the keybinding -- 'key' - input_seq of the keybinding
-- 'key_cfg' - configuration string for the keybinding
-- 'on_frames' - number of frames button is pressed -- 'on_frames' - number of frames button is pressed
-- 'off_frames' - number of frames button is released -- 'off_frames' - number of frames button is released
-- 'button' - reference to ioport_field -- 'button' - reference to ioport_field
@ -43,10 +44,12 @@ function autofire.startplugin()
local button_states = {} local button_states = {}
for i, button in ipairs(buttons) do for i, button in ipairs(buttons) do
local key = button.port .. '\0' .. button.mask .. '.' .. button.type if button.button then
local state = button_states[key] or {0, button.button} local key = button.port .. '\0' .. button.mask .. '.' .. button.type
state[1] = process_button(button) | state[1] local state = button_states[key] or {0, button.button}
button_states[key] = state state[1] = process_button(button) | state[1]
button_states[key] = state
end
end end
for i, state in pairs(button_states) do for i, state in pairs(button_states) do
state[2]:set_value(state[1]) state[2]:set_value(state[1])

View File

@ -662,7 +662,7 @@ function cheat.startplugin()
menu[num][3] = "off" menu[num][3] = "off"
elseif cheat.is_oneshot then elseif cheat.is_oneshot then
menu[num][2] = _("Set") menu[num][2] = _("Set")
menu[num][3] = "" menu[num][3] = ""
else else
if cheat.enabled then if cheat.enabled then
menu[num][2] = _("On") menu[num][2] = _("On")

View File

@ -360,7 +360,7 @@ function cheatfind.startplugin()
local function menu_lim(val, min, max, menuitem) local function menu_lim(val, min, max, menuitem)
if min == max then if min == max then
menuitem[3] = "on" menuitem[3] = "on"
elseif val == min then elseif val == min then
menuitem[3] = "r" menuitem[3] = "r"
elseif val == max then elseif val == max then

View File

@ -15,11 +15,14 @@ function inputmacro.startplugin()
Configuration data: Configuration data:
* name: display name (string) * name: display name (string)
* binding: activation sequence (input sequence) * binding: activation sequence (input sequence)
* bindingcfg: activation sequence configuration (string)
* earlycancel: cancel or complete on release (Boolean) * earlycancel: cancel or complete on release (Boolean)
* loop: -1 = release, 0 = prolong, >0 = loop to step on hold (int) * loop: -1 = release, 0 = prolong, >0 = loop to step on hold (integer)
* steps: * steps:
* inputs: * inputs:
* port: port (I/O port) * port: port tag (string)
* mask: port field mask (integer)
* type: port field type (integer)
* field: field (I/O port field) * field: field (I/O port field)
* delay: delay before activating inputs in frames (integer) * delay: delay before activating inputs in frames (integer)
* duration: duration to activate inputs for (integer) * duration: duration to activate inputs for (integer)
@ -35,7 +38,9 @@ function inputmacro.startplugin()
local function activate_inputs(inputs) local function activate_inputs(inputs)
for index, input in ipairs(inputs) do for index, input in ipairs(inputs) do
active_inputs[string.format('%s.%d.%d', input.port.tag, input.field.mask, input.field.type)] = input.field if input.field then
active_inputs[string.format('%s.%d.%d', input.port, input.mask, input.type)] = input.field
end
end end
end end

View File

@ -37,6 +37,7 @@ local function new_macro()
return { return {
name = name, name = name,
binding = nil, binding = nil,
bindingcfg = '',
earlycancel = true, earlycancel = true,
loop = -1, loop = -1,
steps = { steps = {
@ -44,6 +45,8 @@ local function new_macro()
inputs = { inputs = {
{ {
port = nil, port = nil,
mask = nil,
type = nil,
field = nil } }, field = nil } },
delay = 0, delay = 0,
duration = 1 } } } duration = 1 } } }
@ -83,11 +86,11 @@ function start_input_menu(handler, start_field)
table.insert(menu_stack, MENU_TYPES.INPUT) table.insert(menu_stack, MENU_TYPES.INPUT)
end end
function handle_input(index, action) local function handle_input(index, action)
return input_menu:handle(index, action) return input_menu:handle(index, action)
end end
function populate_input() local function populate_input()
return input_menu:populate(input_start_field) return input_menu:populate(input_start_field)
end end
@ -120,6 +123,7 @@ local function handle_edit_items(index, event)
if edit_switch_poller:poll() then if edit_switch_poller:poll() then
if edit_switch_poller.sequence then if edit_switch_poller.sequence then
edit_current_macro.binding = edit_switch_poller.sequence edit_current_macro.binding = edit_switch_poller.sequence
edit_current_macro.bindingcfg = manager.machine.input:seq_to_tokens(edit_switch_poller.sequence)
end end
edit_switch_poller = nil edit_switch_poller = nil
return true return true
@ -233,11 +237,12 @@ local function handle_edit_items(index, event)
elseif command.action == 'input' then elseif command.action == 'input' then
local inputs = edit_current_macro.steps[command.step].inputs local inputs = edit_current_macro.steps[command.step].inputs
if event == 'select' then if event == 'select' then
local hanlder = local function hanlder(field)
function(field) inputs[command.input].port = field.port.tag
inputs[command.input].port = field.port inputs[command.input].mask = field.mask
inputs[command.input].field = field inputs[command.input].type = field.type
end inputs[command.input].field = field
end
start_input_menu(hanlder, inputs[command.input].field) start_input_menu(hanlder, inputs[command.input].field)
edit_start_selection = index edit_start_selection = index
return true return true
@ -250,12 +255,14 @@ local function handle_edit_items(index, event)
elseif command.action == 'addinput' then elseif command.action == 'addinput' then
if event == 'select' then if event == 'select' then
local inputs = edit_current_macro.steps[command.step].inputs local inputs = edit_current_macro.steps[command.step].inputs
local handler = local function handler(field)
function(field) local newinput = {
inputs[#inputs + 1] = { port = field.port.tag,
port = field.port, mask = field.mask,
field = field } type = field.type,
end field = field }
table.insert(inputs, newinput)
end
start_input_menu(handler) start_input_menu(handler)
edit_start_selection = index edit_start_selection = index
return true return true
@ -280,22 +287,23 @@ local function handle_edit_items(index, event)
elseif command.action == 'addstep' then elseif command.action == 'addstep' then
if event == 'select' then if event == 'select' then
local steps = edit_current_macro.steps local steps = edit_current_macro.steps
local handler = local function handler(field)
function(field) local newstep = {
local newstep = { inputs = {
inputs = { {
{ port = field.port.tag,
port = field.port, mask = field.mask,
field = field } }, type = field.type,
delay = 0, field = field } },
duration = 1 } delay = 0,
table.insert(steps, edit_insert_position, newstep) duration = 1 }
if edit_current_macro.loop >= edit_insert_position then table.insert(steps, edit_insert_position, newstep)
edit_current_macro.loop = edit_current_macro.loop + 1 if edit_current_macro.loop >= edit_insert_position then
end edit_current_macro.loop = edit_current_macro.loop + 1
edit_start_step = edit_insert_position
edit_insert_position = edit_insert_position + 1
end end
edit_start_step = edit_insert_position
edit_insert_position = edit_insert_position + 1
end
start_input_menu(handler) start_input_menu(handler)
edit_start_selection = index edit_start_selection = index
return true return true
@ -383,7 +391,14 @@ local function add_edit_items(items)
edit_items[#items] = { action = 'duration', step = i } edit_items[#items] = { action = 'duration', step = i }
for j, input in ipairs(step.inputs) do for j, input in ipairs(step.inputs) do
local inputname = input.field and _p('input-name', input.field.name) or _p('plugin-inputmacro', '[not set]') local inputname
if input.field then
inputname = _p('input-name', input.field.name)
elseif input.port then
inputname = _p('plugin-inputmacro', 'n/a')
else
inputname = _p('plugin-inputmacro', '[not set]')
end
items[#items + 1] = { string.format(_p('plugin-inputmacro', 'Input %d'), j), inputname, '' } items[#items + 1] = { string.format(_p('plugin-inputmacro', 'Input %d'), j), inputname, '' }
edit_items[#items] = { action = 'input', step = i, input = j } edit_items[#items] = { action = 'input', step = i, input = j }
end end
@ -456,16 +471,16 @@ end
local function populate_add() local function populate_add()
local items = { } local items = { }
items[#items + 1] = { _p('plugin-inputmacro', 'Add Input Macro'), '', 'off' } table.insert(items, { _p('plugin-inputmacro', 'Add Input Macro'), '', 'off' })
items[#items + 1] = { '---', '', '' } table.insert(items, { '---', '', '' })
add_edit_items(items) add_edit_items(items)
items[#items + 1] = { '---', '', '' } table.insert(items, { '---', '', '' })
if current_macro_complete() then if current_macro_complete() then
items[#items + 1] = { _p('plugin-inputmacro', 'Create'), '', '' } table.insert(items, { _p('plugin-inputmacro', 'Create'), '', '' })
else else
items[#items + 1] = { _p('plugin-inputmacro', 'Cancel'), '', '' } table.insert(items, { _p('plugin-inputmacro', 'Cancel'), '', '' })
end end
edit_item_exit = #items edit_item_exit = #items
@ -481,13 +496,13 @@ end
local function populate_edit() local function populate_edit()
local items = { } local items = { }
items[#items + 1] = { _p('plugin-inputmacro', 'Edit Input Macro'), '', 'off' } table.insert(items, { _p('plugin-inputmacro', 'Edit Input Macro'), '', 'off' })
items[#items + 1] = { '---', '', '' } table.insert(items, { '---', '', '' })
add_edit_items(items) add_edit_items(items)
items[#items + 1] = { '---', '', '' } table.insert(items, { '---', '', '' })
items[#items + 1] = { _p('plugin-inputmacro', 'Done'), '', '' } table.insert(items, { _p('plugin-inputmacro', 'Done'), '', '' })
edit_item_exit = #items edit_item_exit = #items
local selection = edit_start_selection local selection = edit_start_selection
@ -542,25 +557,25 @@ function populate_macros()
local ioport = manager.machine.ioport local ioport = manager.machine.ioport
local items = { } local items = { }
items[#items + 1] = { _p('plugin-inputmacro', 'Input Macros'), '', 'off' } table.insert(items, { _p('plugin-inputmacro', 'Input Macros'), '', 'off' })
items[#items + 1] = { string.format(_p('plugin-inputmacro', 'Press %s to delete'), manager.ui:get_general_input_setting(ioport:token_to_input_type('UI_CLEAR'))), '', 'off' } table.insert(items, { string.format(_p('plugin-inputmacro', 'Press %s to delete'), manager.ui:get_general_input_setting(ioport:token_to_input_type('UI_CLEAR'))), '', 'off' })
items[#items + 1] = { '---', '', '' } table.insert(items, { '---', '', '' })
macros_item_first_macro = #items + 1 macros_item_first_macro = #items + 1
if #macros > 0 then if #macros > 0 then
for index, macro in ipairs(macros) do for index, macro in ipairs(macros) do
items[#items + 1] = { macro.name, input:seq_name(macro.binding), '' } table.insert(items, { macro.name, input:seq_name(macro.binding), '' })
if macros_start_macro == index then if macros_start_macro == index then
macros_selection_save = #items macros_selection_save = #items
end end
end end
else else
items[#items + 1] = { _p('plugin-inputmacro', '[no macros]'), '', 'off' } table.insert(items, { _p('plugin-inputmacro', '[no macros]'), '', 'off' })
end end
macros_start_macro = nil macros_start_macro = nil
items[#items + 1] = { '---', '', '' } table.insert(items, { '---', '', '' })
items[#items + 1] = { _p('plugin-inputmacro', 'Add macro'), '', '' } table.insert(items, { _p('plugin-inputmacro', 'Add macro'), '', '' })
macros_item_add = #items macros_item_add = #items
local selection = macros_selection_save local selection = macros_selection_save

View File

@ -20,6 +20,7 @@ local function make_macro(setting)
local result = { local result = {
name = setting.name, name = setting.name,
binding = manager.machine.input:seq_from_tokens(setting.binding), binding = manager.machine.input:seq_from_tokens(setting.binding),
bindingcfg = setting.binding,
earlycancel = setting.earlycancel, earlycancel = setting.earlycancel,
loop = setting.loop, loop = setting.loop,
steps = { } } steps = { } }
@ -33,13 +34,18 @@ local function make_macro(setting)
duration = step.duration } duration = step.duration }
for j, input in ipairs(step.inputs) do for j, input in ipairs(step.inputs) do
if input.port and input.mask and input.type then if input.port and input.mask and input.type then
local ipt = {
port = input.port,
mask = input.mask,
type = ioport:token_to_input_type(input.type) }
local port = ioport.ports[input.port] local port = ioport.ports[input.port]
if port then if port then
local field = port:field(input.mask) local field = port:field(input.mask)
if field and (field.type == ioport:token_to_input_type(input.type)) then if field and (field.type == ipt.type) then
table.insert(s.inputs, { port = port, field = field }) ipt.field = field
end end
end end
table.insert(s.inputs, ipt)
end end
end end
if #s.inputs > 0 then if #s.inputs > 0 then
@ -66,7 +72,7 @@ local function make_settings(macros)
for i, macro in ipairs(macros) do for i, macro in ipairs(macros) do
local m = { local m = {
name = macro.name, name = macro.name,
binding = input:seq_to_tokens(macro.binding), binding = macro.bindingcfg,
earlycancel = macro.earlycancel, earlycancel = macro.earlycancel,
loop = macro.loop, loop = macro.loop,
steps = { } } steps = { } }
@ -79,9 +85,9 @@ local function make_settings(macros)
table.insert(m.steps, s) table.insert(m.steps, s)
for k, input in ipairs(step.inputs) do for k, input in ipairs(step.inputs) do
local b = { local b = {
port = input.port.tag, port = input.port,
mask = input.field.mask, mask = input.mask,
type = ioport:input_type_to_token(input.field.type) } type = ioport:input_type_to_token(input.type) }
table.insert(s.inputs, b) table.insert(s.inputs, b)
end end
end end