local menu = {} --;=========================================================== --; PAUSE MENU --;=========================================================== -- Associative elements table storing arrays with training menu option names. -- Can be appended via external module. menu.t_valuename = { dummycontrol = { {itemname = 'cooperative', displayname = motif.pause_menu.training_pause_menu.menu.valuename.dummycontrol_cooperative}, {itemname = 'ai', displayname = motif.pause_menu.training_pause_menu.menu.valuename.dummycontrol_ai}, {itemname = 'manual', displayname = motif.pause_menu.training_pause_menu.menu.valuename.dummycontrol_manual}, }, ailevel = { {itemname = '1', displayname = motif.pause_menu.training_pause_menu.menu.valuename.ailevel_1}, {itemname = '2', displayname = motif.pause_menu.training_pause_menu.menu.valuename.ailevel_2}, {itemname = '3', displayname = motif.pause_menu.training_pause_menu.menu.valuename.ailevel_3}, {itemname = '4', displayname = motif.pause_menu.training_pause_menu.menu.valuename.ailevel_4}, {itemname = '5', displayname = motif.pause_menu.training_pause_menu.menu.valuename.ailevel_5}, {itemname = '6', displayname = motif.pause_menu.training_pause_menu.menu.valuename.ailevel_6}, {itemname = '7', displayname = motif.pause_menu.training_pause_menu.menu.valuename.ailevel_7}, {itemname = '8', displayname = motif.pause_menu.training_pause_menu.menu.valuename.ailevel_8}, }, dummymode = { {itemname = 'stand', displayname = motif.pause_menu.training_pause_menu.menu.valuename.dummymode_stand}, {itemname = 'crouch', displayname = motif.pause_menu.training_pause_menu.menu.valuename.dummymode_crouch}, {itemname = 'jump', displayname = motif.pause_menu.training_pause_menu.menu.valuename.dummymode_jump}, {itemname = 'wjump', displayname = motif.pause_menu.training_pause_menu.menu.valuename.dummymode_wjump}, }, guardmode = { {itemname = 'none', displayname = motif.pause_menu.training_pause_menu.menu.valuename.guardmode_none}, {itemname = 'auto', displayname = motif.pause_menu.training_pause_menu.menu.valuename.guardmode_auto}, {itemname = 'all', displayname = motif.pause_menu.training_pause_menu.menu.valuename.guardmode_all}, {itemname = 'random', displayname = motif.pause_menu.training_pause_menu.menu.valuename.guardmode_random}, }, fallrecovery = { {itemname = 'none', displayname = motif.pause_menu.training_pause_menu.menu.valuename.fallrecovery_none}, {itemname = 'ground', displayname = motif.pause_menu.training_pause_menu.menu.valuename.fallrecovery_ground}, {itemname = 'air', displayname = motif.pause_menu.training_pause_menu.menu.valuename.fallrecovery_air}, {itemname = 'random', displayname = motif.pause_menu.training_pause_menu.menu.valuename.fallrecovery_random}, }, distance = { {itemname = 'any', displayname = motif.pause_menu.training_pause_menu.menu.valuename.distance_any}, {itemname = 'close', displayname = motif.pause_menu.training_pause_menu.menu.valuename.distance_close}, {itemname = 'medium', displayname = motif.pause_menu.training_pause_menu.menu.valuename.distance_medium}, {itemname = 'far', displayname = motif.pause_menu.training_pause_menu.menu.valuename.distance_far}, }, buttonjam = { {itemname = 'none', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_none}, {itemname = 'a', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_a}, {itemname = 'b', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_b}, {itemname = 'c', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_c}, {itemname = 'x', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_x}, {itemname = 'y', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_y}, {itemname = 'z', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_z}, {itemname = 's', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_s}, {itemname = 'd', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_d}, {itemname = 'w', displayname = motif.pause_menu.training_pause_menu.menu.valuename.buttonjam_w}, }, } -- Shared logic for pause menu option change, returns 2 values: -- * boolean depending if option has changed (via right/left button press) -- * itemname read from t_valuename table based on currently active option -- (or nil, if there was no option change in this frame) function menu.f_valueChanged(t, sec) local valueitem = menu[t.itemname] or 1 local chk = valueitem if getInput(-1, sec.menu.add.key) then valueitem = valueitem + 1 elseif getInput(-1, sec.menu.subtract.key) then valueitem = valueitem - 1 end if valueitem > #menu.t_valuename[t.itemname] then valueitem = 1 elseif valueitem < 1 then valueitem = #menu.t_valuename[t.itemname] end -- true upon option change if chk ~= valueitem then sndPlay(motif.Snd, sec.cursor.move.snd[1], sec.cursor.move.snd[2]) t.vardisplay = menu.t_valuename[t.itemname][valueitem].displayname menu[t.itemname] = valueitem menu.itemname = t.itemname return true, menu.t_valuename[t.itemname][valueitem].itemname end return false, nil end -- Current pause menu itemname for internal use (key from menu.t_itemname table) menu.itemname = '' -- Associative elements table storing functions controlling behaviour of each -- pause menu item. Can be appended via external module. menu.t_itemname = { --Back ['back'] = function(t, item, cursorPosY, moveTxt, sec) if getInput(-1, sec.menu.done.key) then if menu.currentMenu[1] == menu.currentMenu[2] then sndPlay(motif.Snd, sec.exit.snd[1], sec.exit.snd[2]) togglePause(false) main.pauseMenu = false else sndPlay(motif.Snd, sec.cancel.snd[1], sec.cancel.snd[2]) end if menu.currentMenu[1] ~= menu.currentMenu[2] then main.f_menuSnap(sec) end menu.currentMenu[1] = menu.currentMenu[2] return false end return true end, --Dummy Control ['dummycontrol'] = function(t, item, cursorPosY, moveTxt, sec) local ok, name = menu.f_valueChanged(t.items[item], sec) if ok then if name == 'cooperative' or name == 'manual' then player(2) setAILevel(0) elseif name == 'ai' then player(2) setAILevel(menu.ailevel) end player(2) mapSet('_iksys_trainingDummyControl', menu.dummycontrol - 1) end return true end, --AI Level ['ailevel'] = function(t, item, cursorPosY, moveTxt, sec) if menu.f_valueChanged(t.items[item], sec) then if menu.t_valuename.dummycontrol[menu.dummycontrol or 1].itemname == 'ai' then player(2) setAILevel(menu.ailevel) end end return true end, --Dummy Mode ['dummymode'] = function(t, item, cursorPosY, moveTxt, sec) if menu.f_valueChanged(t.items[item], sec) then player(2) mapSet('_iksys_trainingDummyMode', menu.dummymode - 1) end return true end, --Guard Mode ['guardmode'] = function(t, item, cursorPosY, moveTxt, sec) if menu.f_valueChanged(t.items[item], sec) then player(2) mapSet('_iksys_trainingGuardMode', menu.guardmode - 1) end return true end, --Fall Recovery ['fallrecovery'] = function(t, item, cursorPosY, moveTxt, sec) if menu.f_valueChanged(t.items[item], sec) then player(2) mapSet('_iksys_trainingFallRecovery', menu.fallrecovery - 1) end return true end, --Distance ['distance'] = function(t, item, cursorPosY, moveTxt, sec) if menu.f_valueChanged(t.items[item], sec) then player(2) mapSet('_iksys_trainingDistance', menu.distance - 1) end return true end, --Button Jam ['buttonjam'] = function(t, item, cursorPosY, moveTxt, sec) if menu.f_valueChanged(t.items[item], sec) then player(2) mapSet('_iksys_trainingButtonJam', menu.buttonjam - 1) end return true end, --Key Config ['keyboard'] = function(t, item, cursorPosY, moveTxt, sec) if getInput(-1, sec.menu.done.key) --[[or getKey('F1')]] then sndPlay(motif.Snd, sec.cursor.done.snd[1], sec.cursor.done.snd[2]) options.f_keyCfgInit('Keys', t.submenu[t.items[item].itemname].title) menu.itemname = t.items[item].itemname end return true end, --Joystick Config ['gamepad'] = function(t, item, cursorPosY, moveTxt, sec) if getInput(-1, sec.menu.done.key) --[[or getKey('F2')]] then sndPlay(motif.Snd, sec.cursor.done.snd[1], sec.cursor.done.snd[2]) options.f_keyCfgInit('Joystick', t.submenu[t.items[item].itemname].title) menu.itemname = t.items[item].itemname end return true end, --Default ['inputdefault'] = function(t, item, cursorPosY, moveTxt, sec) if getInput(-1, sec.menu.done.key) then sndPlay(motif.Snd, sec.cursor.done.snd[1], sec.cursor.done.snd[2]) options.f_keyDefault() options.f_setKeyConfig('Keys') if getCommandLineValue("-nojoy") == nil then options.f_setKeyConfig('Joystick') end options.f_saveCfg(false) end return true end, --Round Reset ['reset'] = function(t, item, cursorPosY, moveTxt, sec) if getInput(-1, sec.menu.done.key) then sndPlay(motif.Snd, sec.cursor.done.snd[1], sec.cursor.done.snd[2]) --togglePause(false) roundReset() main.pauseMenu = false return false end return true end, --Reload (Rematch) ['reload'] = function(t, item, cursorPosY, moveTxt, sec) if getInput(-1, sec.menu.done.key) then sndPlay(motif.Snd, sec.cursor.done.snd[1], sec.cursor.done.snd[2]) --togglePause(false) reload() main.pauseMenu = false return false end return true end, --Command List ['commandlist'] = function(t, item, cursorPosY, moveTxt, sec) if getInput(-1, sec.menu.done.key) then sndPlay(motif.Snd, sec.cursor.done.snd[1], sec.cursor.done.snd[2]) menu.f_commandlistParse() menu.itemname = t.items[item].itemname end return true end, --Character Change ['characterchange'] = function(t, item, cursorPosY, moveTxt, sec) if getInput(-1, sec.menu.done.key) then sndPlay(motif.Snd, sec.cursor.done.snd[1], sec.cursor.done.snd[2]) --togglePause(false) endMatch() start.characterchange = true main.pauseMenu = false start.f_selectReset(false) return false end return true end, --Exit ['exit'] = function(t, item, cursorPosY, moveTxt, sec) if getInput(-1, sec.menu.done.key) then sndPlay(motif.Snd, sec.cursor.done.snd[1], sec.cursor.done.snd[2]) --togglePause(false) endMatch() start.exit = true main.pauseMenu = false return false end return true end, } -- options.t_itemname table functions are also appended to this table, to make -- option screen settings logic accessible from within pause menu. for k, v in pairs(options.t_itemname) do if menu.t_itemname[k] == nil then menu.t_itemname[k] = v end end -- Shared menu loop logic function menu.f_createMenu(tbl, sec, bg, bool_main) return function() hook.run("menu.menu.loop") if menu._lastMenuTbl ~= tbl then menu._lastMenuTbl = tbl main.f_menuItemBgAnimReset(sec) end local t = tbl.items if tbl.reset then tbl.reset = false else main.f_menuCommonDraw(t, tbl.item, tbl.cursorPosY, tbl.moveTxt, sec, bg, true) end tbl.cursorPosY, tbl.moveTxt, tbl.item = main.f_menuCommonCalc(t, tbl.item, tbl.cursorPosY, tbl.moveTxt, sec, sec.cursor) textImgReset(sec.title.TextSpriteData) textImgSetText(sec.title.TextSpriteData, tbl.title) if esc() or getInput(-1, sec.menu.cancel.key) then if bool_main then sndPlay(motif.Snd, sec.exit.snd[1], sec.exit.snd[2]) togglePause(false) main.pauseMenu = false else sndPlay(motif.Snd, sec.cancel.snd[1], sec.cancel.snd[2]) end if menu.currentMenu[1] ~= menu.currentMenu[2] then main.f_menuSnap(sec) end menu.currentMenu[1] = menu.currentMenu[2] return elseif menu.t_itemname[t[tbl.item].itemname] ~= nil then if not menu.t_itemname[t[tbl.item].itemname](tbl, tbl.item, tbl.cursorPosY, tbl.moveTxt, sec) then return end elseif getInput(-1, sec.menu.done.key) then local f = t[tbl.item].itemname if tbl.submenu[f].loop ~= nil then sndPlay(motif.Snd, sec.cursor.done.snd[1], sec.cursor.done.snd[2]) menu.currentMenu[1] = tbl.submenu[f].loop main.f_menuSnap(sec) elseif not menu.t_itemname[f](tbl, tbl.item, tbl.cursorPosY, tbl.moveTxt, sec) then return end end end end menu.t_vardisplayPointers = {} -- Associative elements table storing functions returning current setting values -- rendered alongside menu item name. Can be appended via external module. menu.t_vardisplay = { ['dummycontrol'] = function() return menu.t_valuename.dummycontrol[menu.dummycontrol or 1].displayname end, ['ailevel'] = function() return menu.t_valuename.ailevel[menu.ailevel or gameOption('Options.Difficulty')].displayname end, ['dummymode'] = function() return menu.t_valuename.dummymode[menu.dummymode or 1].displayname end, ['guardmode'] = function() return menu.t_valuename.guardmode[menu.guardmode or 1].displayname end, ['fallrecovery'] = function() return menu.t_valuename.fallrecovery[menu.fallrecovery or 1].displayname end, ['distance'] = function() return menu.t_valuename.distance[menu.distance or 1].displayname end, ['buttonjam'] = function() return menu.t_valuename.buttonjam[menu.buttonjam or 1].displayname end, } -- Returns setting value rendered alongside menu item name (calls appropriate -- function from menu or options t_vardisplay table) function menu.f_vardisplay(itemname) if menu.t_vardisplay[itemname] ~= nil then return menu.t_vardisplay[itemname]() end if options.t_vardisplay[itemname] ~= nil then return options.t_vardisplay[itemname]() end return '' end -- Table storing arrays with data used for different pause menu types generation. -- Can be appended via external module but not necessary - can specify gamemode-specific [ Pause Menu] sections in `system.def` instead. menu.t_menus = { {id = 'menu', sec = motif.pause_menu.pause_menu, bg = motif.pausebgdef.pausebgdef, movelist = true}, {id = 'training', sec = motif.pause_menu.training_pause_menu, bg = motif.pausebgdef.trainingpausebgdef, movelist = true}, } menu.t_menuIndex = {} local function f_pauseMenuKey(gamemodeName) gamemodeName = tostring(gamemodeName or ''):lower() gamemodeName = gamemodeName:gsub('%s+', '_') if gamemodeName == '' then return '' end return gamemodeName .. '_pause_menu' end local function f_pauseMenuIdFromKey(key) key = tostring(key or ''):lower() if key:sub(-11) == '_pause_menu' then key = key:sub(1, -12) end return key end local function f_pauseMenuBgKey(gamemodeName) gamemodeName = tostring(gamemodeName or ''):lower() gamemodeName = gamemodeName:gsub('%s+', '_') if gamemodeName == '' then return '' end return gamemodeName .. 'pausebgdef' end local function f_registerPauseMenu(id, sec, bg, movelist) if id == '' or sec == nil then return end for i = 1, #menu.t_menus do if menu.t_menus[i].id == id then return end end table.insert(menu.t_menus, {id = id, sec = sec, bg = bg, movelist = movelist}) end local function f_indexPauseMenus() menu.t_menuIndex = {} for i = 1, #menu.t_menus do menu.t_menuIndex[menu.t_menus[i].id] = menu.t_menus[i] end end -- Dynamically generates all menus and submenus function menu.f_start() if motif.pause_menu ~= nil then for k, sec in pairs(motif.pause_menu) do local id = f_pauseMenuIdFromKey(k) if id ~= '' and id ~= 'pause_menu' then local bg = motif.pausebgdef.pausebgdef local bgKey = f_pauseMenuBgKey(id) if bgKey ~= '' and motif.pausebgdef ~= nil and motif.pausebgdef[bgKey] ~= nil then bg = motif.pausebgdef[bgKey] end f_registerPauseMenu(id, sec, bg, true) end end end f_indexPauseMenus() for k, v in ipairs(menu.t_menus) do menu[v.id] = { title = main.f_itemnameUpper(v.sec.title.text, v.sec.menu.title.uppercase), cursorPosY = 1, moveTxt = 0, item = 1, submenu = {}, items = {} } menu[v.id].loop = menu.f_createMenu(menu[v.id], v.sec, v.bg, true) local w = main.f_menuWindow(v.sec.menu) local t_pos = {} --for storing current table position local lastNum = 0 for i, suffix in ipairs(v.sec.menu.itemname_order) do for j, c in ipairs(main.f_strsplit('_', suffix)) do --split using "_" delimiter --appending the menu table if j == 1 then --first string after menu.itemname (either reserved one or custom submenu assignment) if menu[v.id].submenu[c] == nil or c:match("^spacer%d*$") then menu[v.id].submenu[c] = {} menu[v.id].submenu[c].title = main.f_itemnameUpper(v.sec.menu.itemname[suffix], v.sec.menu.title.uppercase) if menu.t_itemname[c] == nil and not c:match("^spacer%d*$") then menu[v.id].submenu[c].cursorPosY = 1 menu[v.id].submenu[c].moveTxt = 0 menu[v.id].submenu[c].item = 1 menu[v.id].submenu[c].submenu = {} menu[v.id].submenu[c].items = {} menu[v.id].submenu[c].loop = menu.f_createMenu(menu[v.id].submenu[c], v.sec, v.bg, false) end if not suffix:match(c .. '_') then table.insert(menu[v.id].items, { itemname = c, displayname = v.sec.menu.itemname[suffix], paramname = suffix, vardisplay = menu.f_vardisplay(c), selected = false, }) table.insert(menu.t_vardisplayPointers, menu[v.id].items[#menu[v.id].items]) end end t_pos = menu[v.id].submenu[c] t_pos.name = c else --following strings if t_pos.submenu[c] == nil or c:match("^spacer%d*$") then t_pos.submenu[c] = {} t_pos.submenu[c].title = main.f_itemnameUpper(v.sec.menu.itemname[suffix], v.sec.menu.title.uppercase) if menu.t_itemname[c] == nil and not c:match("^spacer%d*$") then t_pos.submenu[c].cursorPosY = 1 t_pos.submenu[c].moveTxt = 0 t_pos.submenu[c].item = 1 t_pos.submenu[c].submenu = {} t_pos.submenu[c].items = {} t_pos.submenu[c].loop = menu.f_createMenu(t_pos.submenu[c], v.sec, v.bg, false) end table.insert(t_pos.items, { itemname = c, displayname = v.sec.menu.itemname[suffix], paramname = suffix, vardisplay = menu.f_vardisplay(c), selected = false, }) table.insert(menu.t_vardisplayPointers, t_pos.items[#t_pos.items]) end if j > lastNum then t_pos = t_pos.submenu[c] t_pos.name = c end end lastNum = j end end textImgSetWindow(v.sec.menu.item.selected.active.TextSpriteData, w[1], w[2], w[3], w[4]) textImgSetWindow(v.sec.menu.item.active.TextSpriteData, w[1], w[2], w[3], w[4]) textImgSetWindow(v.sec.menu.item.value.active.TextSpriteData, w[1], w[2], w[3], w[4]) textImgSetWindow(v.sec.menu.item.selected.TextSpriteData, w[1], w[2], w[3], w[4]) textImgSetWindow(v.sec.menu.item.TextSpriteData, w[1], w[2], w[3], w[4]) textImgSetWindow(v.sec.menu.item.value.TextSpriteData, w[1], w[2], w[3], w[4]) --for _, v2 in pairs(v.sec.menu.item.bg) do -- animSetWindow(v2.AnimData, w[1], w[2], w[3], w[4]) --end --for _, v2 in pairs(v.sec.menu.item.active.bg) do -- animSetWindow(v2.AnimData, w[1], w[2], w[3], w[4]) --end if gameOption('Debug.DumpLuaTables') then main.f_printTable(menu[v.id], 'debug/t_' .. v.id .. 'Menu.txt') end -- Move list if v.movelist then local t = v.sec.movelist if t.window.margins.y[1] ~= 0 or t.window.margins.y[2] ~= 0 then local fontProps = motif.files.font['font' .. v.sec.movelist.text.font[1]] if fontProps ~= nil then menu.movelistW = { 0, math.max(0, t.pos[2] + t.text.offset[2] - t.window.margins.y[1]), t.pos[1] + t.text.offset[1] + t.window.width, t.pos[2] + t.text.offset[2] + (t.window.visibleitems - 1) * main.f_round((fontProps.size[2] + fontProps.spacing[2]) * t.text.scale[2] + t.text.spacing[2]) + t.window.margins.y[2] + math.max(0, t.glyphs.offset[2]) } textImgSetWindow(t.text.TextSpriteData, menu.movelistW[1], menu.movelistW[2], menu.movelistW[3], menu.movelistW[4]) end end end end end -- Called from global.lua loop() function, at the start of first round, to reset -- training menu values and p2 settings for a new match function menu.f_trainingReset() for k, _ in pairs(menu.t_valuename) do menu[k] = 1 end menu.ailevel = gameOption('Options.Difficulty') for _, v in ipairs(menu.t_vardisplayPointers) do v.vardisplay = menu.f_vardisplay(v.itemname) end player(2) setAILevel(0) end menu.movelistChar = 1 function menu.f_init() main.f_cmdBufReset() esc(false) togglePause(true) main.pauseMenu = true bgReset(motif.optionbgdef.BGDef) local id = f_pauseMenuIdFromKey(f_pauseMenuKey(gamemode())) if id == '' or menu.t_menuIndex == nil or menu.t_menuIndex[id] == nil then id = 'menu' end if menu.t_menuIndex ~= nil and menu.t_menuIndex[id] ~= nil then local entry = menu.t_menuIndex[id] sndPlay(motif.Snd, entry.sec.enter.snd[1], entry.sec.enter.snd[2]) bgReset(entry.bg.BGDef) main.f_fadeReset('fadein', entry.sec) if menu[id] ~= nil and menu[id].loop ~= nil then menu.currentMenu = {menu[id].loop, menu[id].loop} menu.currentMenuId = id else menu.currentMenu = {menu.menu.loop, menu.menu.loop} menu.currentMenuId = 'menu' end else sndPlay(motif.Snd, motif.pause_menu.pause_menu.enter.snd[1], motif.pause_menu.pause_menu.enter.snd[2]) bgReset(motif.pausebgdef.pausebgdef.BGDef) main.f_fadeReset('fadein', motif.pause_menu.pause_menu) menu.currentMenu = {menu.menu.loop, menu.menu.loop} menu.currentMenuId = 'menu' end end function menu.f_run() local entry = nil if menu.t_menuIndex ~= nil then if menu.currentMenuId ~= nil and menu.t_menuIndex[menu.currentMenuId] ~= nil then entry = menu.t_menuIndex[menu.currentMenuId] else local id = f_pauseMenuIdFromKey(f_pauseMenuKey(gamemode())) if id ~= '' and menu.t_menuIndex[id] ~= nil then entry = menu.t_menuIndex[id] elseif menu.t_menuIndex['menu'] ~= nil then entry = menu.t_menuIndex['menu'] end end end local sec = motif.pause_menu.pause_menu local bg = motif.pausebgdef.pausebgdef if entry ~= nil and entry.sec ~= nil and entry.bg ~= nil then sec = entry.sec bg = entry.bg end --draw overlay rectDraw(sec.overlay.RectData) --Button Config if menu.itemname == 'keyboard' or menu.itemname == 'gamepad' then if menu.itemname == 'keyboard' then options.f_keyCfg('Keys', menu.itemname, bg, true) else options.f_keyCfg('Joystick', menu.itemname, bg, true) end --Command List elseif menu.itemname == 'commandlist' then menu.f_commandlistRender(sec, menu.t_movelists[menu.movelistChar]) --Menu else menu.currentMenu[1]() end return main.pauseMenu end -- Reset selection/scroll state recursively for a menu table (root + submenus) local function f_resetMenuState(tbl) if type(tbl) ~= "table" then return end -- Reset cursor/scroll tbl.cursorPosY = 1 tbl.moveTxt = 0 tbl.item = 1 -- Force one-frame input refresh path used by f_createMenu tbl.reset = true -- Clear any "selected" flags on items (if used by custom menus) if type(tbl.items) == "table" then for i = 1, #tbl.items do if type(tbl.items[i]) == "table" then tbl.items[i].selected = false end end end -- Recurse into submenus if type(tbl.submenu) == "table" then for _, sub in pairs(tbl.submenu) do f_resetMenuState(sub) end end end function menu.f_reset() -- exit any special screens rendered by menu.f_run() menu.itemname = '' -- Reset movelist selector (command list screen) menu.movelistChar = 1 -- Reset tween caches if menu.t_menus ~= nil then for i = 1, #menu.t_menus do local sec = menu.t_menus[i].sec if sec ~= nil then sec.menuTweenData = nil sec.boxCursorData = nil end end end -- Determine which root menu is active and reset its cursor/item/scroll (and all submenus) if menu.currentMenu ~= nil and menu.currentMenu[2] ~= nil then if menu.currentMenuId ~= nil and menu[menu.currentMenuId] ~= nil then f_resetMenuState(menu[menu.currentMenuId]) elseif menu.training ~= nil and menu.training.loop ~= nil and menu.currentMenu[2] == menu.training.loop then f_resetMenuState(menu.training) else f_resetMenuState(menu.menu) end end -- Force snap on next calc so we don't keep old scroll offsets. main.menuSnap = true -- restore root menu loop if menu.currentMenu ~= nil and menu.currentMenu[2] ~= nil then menu.currentMenu[1] = menu.currentMenu[2] end end function menuInit() return menu.f_init() end function menuRun() return menu.f_run() end function menuReset() return menu.f_reset() end --;=========================================================== --; COMMAND LIST --;=========================================================== function colorFromHex(h) h = tostring(h) if h:sub(0, 1) =="#" then h = h:sub(2, -1) end if h:sub(0, 2) =="0x" then h = h:sub(3, -1) end local r = tonumber(h:sub(1, 2), 16) or 255 local g = tonumber(h:sub(3, 4), 16) or 255 local b = tonumber(h:sub(5, 6), 16) or 255 local src = tonumber(h:sub(7, 8), 16) or 255 local dst = tonumber(h:sub(9, 10), 16) or 0 return {r = r, g = g, b = b, src = src, dst = dst} end local function f_commandlistData(t, str, align, col) local t_insert = {} str = str .. '<#>' for m1, m2 in str:gmatch('(.-)<([^<>%s]+)>') do if m1 ~= '' then table.insert(t_insert, {glyph = false, text = m1, align = align, col = col}) end if not m2:match('^#[A-Za-z0-9]+$') and not m2:match('^/$') and not m2:match('^#$') then table.insert(t_insert, {glyph = true, text = m2, align = align, col = col}) elseif m2:match('^#[A-Za-z0-9]+$') then col = colorFromHex(m2) elseif m2:match('^/$') then col = {} end end if align == -1 then for i = #t_insert, 1, -1 do table.insert(t, t_insert[i]) end else for i = 1, #t_insert do table.insert(t, t_insert[i]) end end return t, col end function menu.f_commandlistParse() menu.t_movelists = {} for side, tbl in ipairs({start.p[1].t_selected, start.p[2].t_selected}) do for member, sel in ipairs(tbl) do if sel.movelistLine == nil then sel.movelistLine = 1 end local pn = side if member > 1 then pn = pn + (member - 1) * 2 end if player(pn) and ailevel() == 0 then local ref = selectno() if start.f_getCharData(ref).commandlist == nil then local movelist = getCharMovelist(ref) if movelist ~= '' then -- Replace glyph tokens with for later lookup in motif.glyphs. for k, v in main.f_sortKeys(motif.glyphs, function(t, a, b) return string.len(a) > string.len(b) end) do local s = movelist movelist = s:gsub('()' .. main.f_escapePattern(k), function(pos) -- If the match starts immediately after a '<', it's already inside a tag. -- Leave it unchanged to prevent nested replacements. if pos > 1 and s:sub(pos - 1, pos - 1) == '<' then return k end return '<' .. k .. '>' end) end local t = {} local col = {} for line in movelist:gmatch('([^\n]*)\n?') do line = line:gsub('%s+$', '') local subt = {} for m in line:gmatch('( *[^ ]+)') do local tabs = 0 m = m:gsub('^( *)', function(m1) tabs = string.len(m1) return '' end) local align = 1 --left align if tabs == 1 then align = 0 --center align elseif tabs > 1 then align = -1 --right align end subt, col = f_commandlistData(subt, m, align, col) end table.insert(t, subt) end t[#t] = nil --blank line produced by regexp matching start.f_getCharData(ref).commandlist = t end end table.insert(menu.t_movelists, { pn = pn, name = start.f_getCharData(ref).name, tbl = sel, commandlist = start.f_getCharData(ref).commandlist, }) end end end if #menu.t_movelists == 0 then table.insert(menu.t_movelists, { pn = 1, name = "", tbl = {movelistLine = 1}, commandlist = nil, }) end if menu.movelistChar > #menu.t_movelists then menu.movelistChar = 1 end if gameOption('Debug.DumpLuaTables') then main.f_printTable(menu.t_movelists, "debug/t_movelists.txt") end end function menu.f_commandlistRender(sec, t) local cmdList = {} if t.commandlist ~= nil then cmdList = t.commandlist else table.insert(cmdList, {{glyph = false, text = sec.movelist.text.text, align = 1, col = {}}}) end if esc() or getInput(-1, sec.menu.cancel.key, sec.menu.done.key) then sndPlay(motif.Snd, sec.cancel.snd[1], sec.cancel.snd[2]) menu.itemname = '' return elseif getInput(-1, sec.menu.subtract.key) and #menu.t_movelists > 1 then sndPlay(motif.Snd, sec.cursor.move.snd[1], sec.cursor.move.snd[2]) menu.movelistChar = menu.movelistChar - 1 if menu.movelistChar < 1 then menu.movelistChar = #menu.t_movelists end elseif getInput(-1, sec.menu.add.key) and #menu.t_movelists > 1 then sndPlay(motif.Snd, sec.cursor.move.snd[1], sec.cursor.move.snd[2]) menu.movelistChar = menu.movelistChar + 1 if menu.movelistChar > #menu.t_movelists then menu.movelistChar = 1 end elseif getInput(-1, sec.menu.previous.key) and t.tbl.movelistLine > 1 then sndPlay(motif.Snd, sec.cursor.move.snd[1], sec.cursor.move.snd[2]) t.tbl.movelistLine = t.tbl.movelistLine - 1 elseif getInput(-1, sec.menu.next.key) and t.tbl.movelistLine <= #cmdList - sec.movelist.window.visibleitems then sndPlay(motif.Snd, sec.cursor.move.snd[1], sec.cursor.move.snd[2]) t.tbl.movelistLine = t.tbl.movelistLine + 1 end --draw overlay rectDraw(sec.movelist.overlay.RectData) --draw title textImgReset(sec.movelist.title.TextSpriteData) textImgSetText(sec.movelist.title.TextSpriteData, main.f_itemnameUpper(string.format(sec.movelist.title.text, t.name), sec.movelist.title.uppercase)) textImgDraw(sec.movelist.title.TextSpriteData) --draw commands local i = 0 for n = t.tbl.movelistLine, math.min(t.tbl.movelistLine + sec.movelist.window.visibleitems + 1, #cmdList) do i = i + 1 local alignOffset = 0 local lengthOffset = 0 local align = 1 local width = 0 for k, v in ipairs(cmdList[n]) do if v.text ~= '' then alignOffset = 0 if v.align == 0 then --center align alignOffset = sec.movelist.window.width * 0.5 elseif v.align == -1 then --right align alignOffset = sec.movelist.window.width end if v.align ~= align then lengthOffset = 0 align = v.align end local fontProps = motif.files.font['font' .. sec.movelist.text.font[1]] --render glyph if v.glyph and motif.glyphs[v.text] ~= nil then local g = motif.glyphs[v.text] if g.Size ~= nil and g.AnimData ~= nil then local scaleX = fontProps.size[2] * sec.movelist.text.scale[2] / g.Size[2] * sec.movelist.glyphs.scale[1] local scaleY = fontProps.size[2] * sec.movelist.text.scale[2] / g.Size[2] * sec.movelist.glyphs.scale[2] if v.align == -1 then alignOffset = alignOffset - g.Size[1] * scaleX end animSetScale(g.AnimData, scaleX, scaleY) --animSetFacing(g.AnimData, facing) animSetPos( g.AnimData, math.floor(sec.movelist.pos[1] + sec.movelist.text.offset[1] + sec.movelist.glyphs.offset[1] + alignOffset + lengthOffset), sec.movelist.pos[2] + sec.movelist.text.offset[2] + sec.movelist.glyphs.offset[2] + main.f_round((fontProps.size[2] + fontProps.spacing[2]) * sec.movelist.text.scale[2] + sec.movelist.text.spacing[2]) * (i - 1) ) animSetWindow(g.AnimData, menu.movelistW[1], menu.movelistW[2], menu.movelistW[3], menu.movelistW[4]) --animUpdate(g.AnimData) animDraw(g.AnimData) if k < #cmdList[n] then width = g.Size[1] * scaleX + sec.movelist.glyphs.spacing[1] end end --render text else textImgReset(sec.movelist.text.TextSpriteData) textImgSetAlign(sec.movelist.text.TextSpriteData, v.align) textImgSetColor( sec.movelist.text.TextSpriteData, v.col.r or sec.movelist.text.font[4], v.col.g or sec.movelist.text.font[5], v.col.b or sec.movelist.text.font[6], v.col.a or sec.movelist.text.font[7] ) textImgAddPos( sec.movelist.text.TextSpriteData, alignOffset + lengthOffset, main.f_round((fontProps.size[2] + fontProps.spacing[2]) * sec.movelist.text.scale[2] + sec.movelist.text.spacing[2]) * (i - 1) ) textImgSetText(sec.movelist.text.TextSpriteData, v.text) textImgDraw(sec.movelist.text.TextSpriteData) if k < #cmdList[n] then width = textImgGetTextWidth(sec.movelist.text.TextSpriteData, v.text) * sec.movelist.text.scale[1] + sec.movelist.text.spacing[1] end end if v.align == 0 then lengthOffset = lengthOffset + width / 2 elseif v.align == -1 then lengthOffset = lengthOffset - width else lengthOffset = lengthOffset + width end end end end --draw scroll arrows if #cmdList > sec.movelist.window.visibleitems then if t.tbl.movelistLine > 1 then animUpdate(sec.movelist.arrow.up.AnimData) animDraw(sec.movelist.arrow.up.AnimData) end if t.tbl.movelistLine <= #cmdList - sec.movelist.window.visibleitems then animUpdate(sec.movelist.arrow.down.AnimData) animDraw(sec.movelist.arrow.down.AnimData) end end end return menu