mirror of
https://github.com/holub/mame
synced 2025-04-26 10:13:37 +03:00

* Fixed bugs related to reloading roms Soft resets would reload autofire settings without saving them first, causing the settings to be lost. This commit adds a check to only reload from the settings file if loading a different rom than before. Hard resets would leave bad references lying around, causing MAME to crash under certain circumstances (i.e. resetting while in the edit menu and entering the menu again). This commit makes sure to properly clean up and reinitialize menu and button states when resetting. * Used set_folder to avoid hardcoding plugin name in settings path * Bumped autofire plugin version
304 lines
8.4 KiB
Lua
304 lines
8.4 KiB
Lua
local lib = {}
|
|
|
|
-- Set of all menus
|
|
local MENU_TYPES = { MAIN = 0, EDIT = 1, ADD = 2, BUTTON = 3 }
|
|
|
|
-- Set of sections within a menu
|
|
local MENU_SECTIONS = { HEADER = 0, CONTENT = 1, FOOTER = 2 }
|
|
|
|
-- Last index of header items (above main content) in menu
|
|
local header_height = 0
|
|
|
|
-- Last index of content items (below header, above footer) in menu
|
|
local content_height = 0
|
|
|
|
-- Stack of menus (see MENU_TYPES)
|
|
local menu_stack = { MENU_TYPES.MAIN }
|
|
|
|
-- Button being created/edited
|
|
local current_button = {}
|
|
|
|
-- Inputs that can be autofired (to list in BUTTON menu)
|
|
local inputs = {}
|
|
|
|
-- Returns the section (from MENU_SECTIONS) and the index within that section
|
|
local function menu_section(index)
|
|
if index <= header_height then
|
|
return MENU_SECTIONS.HEADER, index
|
|
elseif index <= content_height then
|
|
return MENU_SECTIONS.CONTENT, index - header_height
|
|
else
|
|
return MENU_SECTIONS.FOOTER, index - content_height
|
|
end
|
|
end
|
|
|
|
local function create_new_button()
|
|
return {
|
|
on_frames = 1,
|
|
off_frames = 1,
|
|
counter = 0
|
|
}
|
|
end
|
|
|
|
local function is_button_complete(button)
|
|
return button.port and button.field and button.key and button.on_frames and button.off_frames and button.button and button.counter
|
|
end
|
|
|
|
local function is_supported_input(ioport_field)
|
|
-- IPT_BUTTON1 through IPT_BUTTON16 in ioport_type enum (ioport.h)
|
|
return ioport_field.type >= 64 and ioport_field.type <= 79
|
|
end
|
|
|
|
-- Main menu
|
|
|
|
local function populate_main_menu(buttons)
|
|
local menu = {}
|
|
menu[#menu + 1] = {_('Autofire buttons'), '', 'off'}
|
|
menu[#menu + 1] = {'---', '', ''}
|
|
header_height = #menu
|
|
|
|
for index, button in ipairs(buttons) do
|
|
-- Assume refresh rate of 60 Hz; maybe better to use screen_device refresh()?
|
|
local rate = 60 / (button.on_frames + button.off_frames)
|
|
-- Round to two decimal places
|
|
rate = math.floor(rate * 100) / 100
|
|
local text = button.button.name .. ' [' .. rate .. ' Hz]'
|
|
local subtext = manager:machine():input():code_name(button.key)
|
|
menu[#menu + 1] = {text, subtext, ''}
|
|
end
|
|
content_height = #menu
|
|
|
|
menu[#menu + 1] = {'---', '', ''}
|
|
menu[#menu + 1] = {_('Add autofire button'), '', ''}
|
|
return menu
|
|
end
|
|
|
|
local function handle_main_menu(index, event, buttons)
|
|
local section, adjusted_index = menu_section(index)
|
|
if section == MENU_SECTIONS.CONTENT then
|
|
if event == 'select' then
|
|
current_button = buttons[adjusted_index]
|
|
table.insert(menu_stack, MENU_TYPES.EDIT)
|
|
return true
|
|
elseif event == 'clear' then
|
|
table.remove(buttons, adjusted_index)
|
|
return true
|
|
end
|
|
elseif section == MENU_SECTIONS.FOOTER then
|
|
if event == 'select' then
|
|
current_button = create_new_button()
|
|
table.insert(menu_stack, MENU_TYPES.ADD)
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Add/edit menus (mostly identical)
|
|
|
|
local function populate_configure_menu(menu)
|
|
local button_name = current_button.button and current_button.button.name or _('NOT SET')
|
|
local key_name = current_button.key and manager:machine():input():code_name(current_button.key) or _('NOT SET')
|
|
menu[#menu + 1] = {_('Input'), button_name, ''}
|
|
menu[#menu + 1] = {_('Hotkey'), key_name, ''}
|
|
menu[#menu + 1] = {_('On frames'), current_button.on_frames, current_button.on_frames > 1 and 'lr' or 'r'}
|
|
menu[#menu + 1] = {_('Off frames'), current_button.off_frames, current_button.off_frames > 1 and 'lr' or 'r'}
|
|
end
|
|
|
|
-- Borrowed from the cheat plugin
|
|
local function poll_for_hotkey()
|
|
local input = manager:machine():input()
|
|
manager:machine():popmessage(_('Press button for hotkey or wait to leave unchanged'))
|
|
manager:machine():video():frame_update(true)
|
|
input:seq_poll_start('switch')
|
|
local time = os.clock()
|
|
while (not input:seq_poll()) and (os.clock() < time + 1) do end
|
|
local tokens = input:seq_to_tokens(input:seq_poll_final())
|
|
manager:machine():popmessage()
|
|
manager:machine():video():frame_update(true)
|
|
|
|
local final_token = nil
|
|
for token in tokens:gmatch('%S+') do
|
|
final_token = token
|
|
end
|
|
return final_token and input:code_from_token(final_token) or nil
|
|
end
|
|
|
|
local function handle_configure_menu(index, event)
|
|
-- Input
|
|
if index == 1 then
|
|
if event == 'select' then
|
|
table.insert(menu_stack, MENU_TYPES.BUTTON)
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
-- Hotkey
|
|
elseif index == 2 then
|
|
if event == 'select' then
|
|
local keycode = poll_for_hotkey()
|
|
if keycode then
|
|
current_button.key = keycode
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
-- On frames
|
|
elseif index == 3 then
|
|
manager:machine():popmessage(_('Number of frames button will be pressed'))
|
|
if event == 'left' then
|
|
current_button.on_frames = current_button.on_frames - 1
|
|
elseif event == 'right' then
|
|
current_button.on_frames = current_button.on_frames + 1
|
|
end
|
|
-- Off frames
|
|
elseif index == 4 then
|
|
manager:machine():popmessage(_('Number of frames button will be released'))
|
|
if event == 'left' then
|
|
current_button.off_frames = current_button.off_frames - 1
|
|
elseif event == 'right' then
|
|
current_button.off_frames = current_button.off_frames + 1
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function populate_edit_menu()
|
|
local menu = {}
|
|
menu[#menu + 1] = {_('Edit autofire button'), '', 'off'}
|
|
menu[#menu + 1] = {'---', '', ''}
|
|
header_height = #menu
|
|
|
|
populate_configure_menu(menu)
|
|
content_height = #menu
|
|
|
|
menu[#menu + 1] = {'---', '', ''}
|
|
menu[#menu + 1] = {_('Done'), '', ''}
|
|
return menu
|
|
end
|
|
|
|
local function handle_edit_menu(index, event, buttons)
|
|
local section, adjusted_index = menu_section(index)
|
|
if section == MENU_SECTIONS.CONTENT then
|
|
return handle_configure_menu(adjusted_index, event)
|
|
elseif section == MENU_SECTIONS.FOOTER then
|
|
if event == 'select' then
|
|
table.remove(menu_stack)
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function populate_add_menu()
|
|
local menu = {}
|
|
menu[#menu + 1] = {_('Add autofire button'), '', 'off'}
|
|
menu[#menu + 1] = {'---', '', ''}
|
|
header_height = #menu
|
|
|
|
populate_configure_menu(menu)
|
|
content_height = #menu
|
|
|
|
menu[#menu + 1] = {'---', '', ''}
|
|
if is_button_complete(current_button) then
|
|
menu[#menu + 1] = {_('Create'), '', ''}
|
|
else
|
|
menu[#menu + 1] = {_('Cancel'), '', ''}
|
|
end
|
|
return menu
|
|
end
|
|
|
|
local function handle_add_menu(index, event, buttons)
|
|
local section, adjusted_index = menu_section(index)
|
|
if section == MENU_SECTIONS.CONTENT then
|
|
return handle_configure_menu(adjusted_index, event)
|
|
elseif section == MENU_SECTIONS.FOOTER then
|
|
if event == 'select' then
|
|
table.remove(menu_stack)
|
|
if is_button_complete(current_button) then
|
|
buttons[#buttons + 1] = current_button
|
|
end
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Button selection menu
|
|
|
|
local function populate_button_menu()
|
|
menu = {}
|
|
inputs = {}
|
|
menu[#menu + 1] = {_('Select an input for autofire'), '', 'off'}
|
|
menu[#menu + 1] = {'---', '', ''}
|
|
header_height = #menu
|
|
|
|
for port_key, port in pairs(manager:machine():ioport().ports) do
|
|
for field_key, field in pairs(port.fields) do
|
|
if is_supported_input(field) then
|
|
menu[#menu + 1] = {field.name, '', ''}
|
|
inputs[#inputs + 1] = {
|
|
port_name = port_key,
|
|
field_name = field_key,
|
|
ioport_field = field
|
|
}
|
|
end
|
|
end
|
|
end
|
|
content_height = #menu
|
|
return menu
|
|
end
|
|
|
|
local function handle_button_menu(index, event)
|
|
local section, adjusted_index = menu_section(index)
|
|
if section == MENU_SECTIONS.CONTENT and event == 'select' then
|
|
local selected_input = inputs[adjusted_index]
|
|
current_button.port = selected_input.port_name
|
|
current_button.field = selected_input.field_name
|
|
current_button.button = selected_input.ioport_field
|
|
table.remove(menu_stack)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
function lib:init_menu(buttons)
|
|
header_height = 0
|
|
content_height = 0
|
|
menu_stack = { MENU_TYPES.MAIN }
|
|
current_button = {}
|
|
inputs = {}
|
|
end
|
|
|
|
function lib:populate_menu(buttons)
|
|
local current_menu = menu_stack[#menu_stack]
|
|
if current_menu == MENU_TYPES.MAIN then
|
|
return populate_main_menu(buttons)
|
|
elseif current_menu == MENU_TYPES.EDIT then
|
|
return populate_edit_menu()
|
|
elseif current_menu == MENU_TYPES.ADD then
|
|
return populate_add_menu()
|
|
elseif current_menu == MENU_TYPES.BUTTON then
|
|
return populate_button_menu()
|
|
end
|
|
end
|
|
|
|
function lib:handle_menu_event(index, event, buttons)
|
|
manager:machine():popmessage()
|
|
local current_menu = menu_stack[#menu_stack]
|
|
if current_menu == MENU_TYPES.MAIN then
|
|
return handle_main_menu(index, event, buttons)
|
|
elseif current_menu == MENU_TYPES.EDIT then
|
|
return handle_edit_menu(index, event, buttons)
|
|
elseif current_menu == MENU_TYPES.ADD then
|
|
return handle_add_menu(index, event, buttons)
|
|
elseif current_menu == MENU_TYPES.BUTTON then
|
|
return handle_button_menu(index, event)
|
|
end
|
|
end
|
|
|
|
return lib
|