diff --git a/Docs/Themerdocs/Examples/OptionRowHandlerLua.lua b/Docs/Themerdocs/Examples/OptionRowHandlerLua.lua index 4072233bdc..20eef74c10 100644 --- a/Docs/Themerdocs/Examples/OptionRowHandlerLua.lua +++ b/Docs/Themerdocs/Examples/OptionRowHandlerLua.lua @@ -18,13 +18,18 @@ -- The function "FooMods" returns a table containing all the information the -- option row handler needs to build the option row. function FooMods() - return { + return { -- A string with the name of the row. This name will be localized using -- the entry in the "OptionTitles" section of the language file. - Name= "Foo", + Name= "Foo", + + -- A boolean that controls whether this row goes to the "next row" + -- element when NavigationMode for the screen is "toggle" and the + -- ArcadeOptionsNavigation preference is 1. + GoToFirstOnStart= false, -- A boolean controlling whether the choice affects all players. - OneChoiceForAllPlayers= false, + OneChoiceForAllPlayers= false, -- A boolean controlling whether SaveSelections is called after every -- change. If this is true, SaveSelections will be called every time @@ -35,12 +40,12 @@ function FooMods() -- "ShowOneInRow" shows only the choice with focus is shown. -- "ShowOneInRow" is forced if there are enough choices that they would go -- off screen. - LayoutType= "ShowAllInRow", + LayoutType= "ShowAllInRow", -- A SelectType enum value. "SelectOne" allows only one choice to be -- selected. "SelectMultiple" allows multiple to be selected. -- "SelectNone" allows none to be selected. - SelectType= "SelectMultiple", + SelectType= "SelectMultiple", -- Optional function. If non-nil, this function must return a table of -- PlayerNumbers that are allowed to use the row. @@ -56,7 +61,7 @@ function FooMods() -- A table of strings that are the names of choices. Choice names are not -- localized. - Choices= {"a", "b", "c", "d"}, + Choices= {"a", "b", "c", "d"}, -- Optional table. If non-nil, this table must contain a list of messages -- this row should listen for. If one of the messages is recieved, the @@ -68,31 +73,31 @@ function FooMods() -- on the row the player has selected. -- self is the table returned by the original function used to create the -- option row. (the table being created right now). - -- list is a table of bools, all initially false. Set them to true to + -- list is a table of bools, all initially false. Set them to true to -- indicate which options are on. -- pn is the PlayerNumber of the player the selections are for. - LoadSelections= function(self, list, pn) + LoadSelections= function(self, list, pn) Trace("FooMods:LoadSelections(" .. pn .. ")") - for i, choice in ipairs(self.Choices) do + for i, choice in ipairs(self.Choices) do -- Randomly set some to true just for an example. if math.random(0, 1) == 1 then Trace(choice .. " (" .. i .. ")" .. " set to true.") list[i]= true end - end - end, + end + end, -- SaveSelections should examine the list of what the player has selected -- and apply the appropriate modifiers to the player. -- Same args as LoadSelections. - SaveSelections= function(self, list, pn) + SaveSelections= function(self, list, pn) Trace("FooMods:SaveSelections(" .. pn .. ")") - for i, choice in ipairs(self.Choices) do - if list[i] then + for i, choice in ipairs(self.Choices) do + if list[i] then Trace(choice .. " (" .. i .. ")" .. " set to true.") end - end - end, + end + end, -- Optional function. If non-nil, this function must take 3 parameters -- (self, pn, choice), and return a bool. It is called when a player @@ -137,5 +142,5 @@ function FooMods() end return change >= 2 end - } + } end diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 0369decce2..c82211fe60 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -489,6 +489,7 @@ Test Input=Test the responsiveness of connected joysticks. Test Lights=Test the responsiveness of connected lights. TextureColorDepth=Choose the color depth of textures. 32 bit textures use more memory, but look nicer. Theme=Choose from this list of installed theme packs. +ThreeKeyNavigation=&oq;Five Key&oq;: L/R/D/U menu buttons and Start. &oq;Three Key&oq;: L/R menu buttons and Start. TimingWindowScale=Increase this value to cause your steps to be judged more strictly (i.e. shrink the timing window). Toggle Song=Toggle Song Turn=Turn @@ -663,6 +664,7 @@ Expand 2->3=Expand 2->3 Expand 2x=Expand 2x Expand 3->4=Expand 3->4 Fast=Fast +Five Key Menu=Five Key Flat=Flat Flip=Flip Floored=Floored @@ -702,6 +704,7 @@ Mirror=Mirror N/A=N/A Native Language=Native Language Never=Never +NextRow=&nextrow; New=New NewEdit=-New Edit- No=No @@ -784,6 +787,7 @@ Swap Sides=Swap Sides Sync Machine=Sync Machine Sync Song=Sync Song Sync Tempo=Sync Tempo +Three Key Menu=Three Key Tiny=Tiny Tipsy=Tipsy Title Only=Title Only @@ -1119,6 +1123,7 @@ Test Input=Test Input Test Lights=Test Lights TextureColorDepth=Texture Color Theme=Theme +ThreeKeyNavigation=Menu Navigation TimingWindowScale=Judge Difficulty Transform=Transform Transfer Edits to USB=Transfer Edits to USB diff --git a/Themes/_fallback/Scripts/02 Other.lua b/Themes/_fallback/Scripts/02 Other.lua index 21044760a0..16e263388c 100644 --- a/Themes/_fallback/Scripts/02 Other.lua +++ b/Themes/_fallback/Scripts/02 Other.lua @@ -133,6 +133,14 @@ function Song:GetStageCost() return self:IsMarathon() and 3 or self:IsLong() and 2 or 1 end +function OptionsNavigationMode() + if PREFSMAN:GetPreference("ThreeKeyNavigation") then + return "toggle" + else + return "normal" + end +end + -- (c) 2005 Chris Danford -- All rights reserved. -- diff --git a/Themes/_fallback/Scripts/03 CustomSpeedMods.lua b/Themes/_fallback/Scripts/03 CustomSpeedMods.lua index ffa7ee2eb2..707595f7aa 100644 --- a/Themes/_fallback/Scripts/03 CustomSpeedMods.lua +++ b/Themes/_fallback/Scripts/03 CustomSpeedMods.lua @@ -439,21 +439,23 @@ function ArbitrarySpeedMods() NumPlayers= 0 -- for ease when adjusting for the status elements. } for i, pn in ipairs(GAMESTATE:GetEnabledPlayers()) do - local poptions= GAMESTATE:GetPlayerState(pn):GetPlayerOptions("ModsLevel_Preferred") - local speed= nil - local mode= nil - if poptions:MaxScrollBPM() > 0 then - mode= "m" - speed= math.round(poptions:MaxScrollBPM()) - elseif poptions:TimeSpacing() > 0 then - mode= "C" - speed= math.round(poptions:ScrollBPM()) - else - mode= "x" - speed= math.round(poptions:ScrollSpeed() * 100) + if GAMESTATE:IsHumanPlayer(pn) then + local poptions= GAMESTATE:GetPlayerState(pn):GetPlayerOptions("ModsLevel_Preferred") + local speed= nil + local mode= nil + if poptions:MaxScrollBPM() > 0 then + mode= "m" + speed= math.round(poptions:MaxScrollBPM()) + elseif poptions:TimeSpacing() > 0 then + mode= "C" + speed= math.round(poptions:ScrollBPM()) + else + mode= "x" + speed= math.round(poptions:ScrollSpeed() * 100) + end + ret.CurValues[pn]= {mode= mode, speed= speed} + ret.NumPlayers= ret.NumPlayers + 1 end - ret.CurValues[pn]= {mode= mode, speed= speed} - ret.NumPlayers= ret.NumPlayers + 1 end ret:GenChoices() return ret diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index cbee4808d9..b99685446d 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -2218,7 +2218,7 @@ ScreenBeginCommand= [ScreenOptions] Fallback="ScreenWithMenuElements" -NavigationMode="normal" +NavigationMode=OptionsNavigationMode() InputMode="individual" ForceAllPlayers=false # @@ -3002,12 +3002,13 @@ Line14="conf,ShowSongOptions" Fallback="ScreenOptionsServiceChild" NextScreen="ScreenOptionsService" PrevScreen="ScreenOptionsService" -LineNames="1,2,3,4,5" +LineNames="1,2,3,4,5,6" Line1="conf,AutoMapOnJoyChange" Line2="conf,OnlyDedicatedMenuButtons" Line3="conf,DelayedBack" Line4="conf,ArcadeOptionsNavigation" -Line5="conf,MusicWheelSwitchSpeed" +Line5="conf,ThreeKeyNavigation" +Line6="conf,MusicWheelSwitchSpeed" [ScreenOptionsArcade] Fallback="ScreenOptionsServiceChild" diff --git a/Themes/default/BGAnimations/ScreenSelectProfile overlay.lua b/Themes/default/BGAnimations/ScreenSelectProfile overlay.lua index deb078f929..790c54b52e 100644 --- a/Themes/default/BGAnimations/ScreenSelectProfile overlay.lua +++ b/Themes/default/BGAnimations/ScreenSelectProfile overlay.lua @@ -193,53 +193,67 @@ function UpdateInternal3(self, Player) end; end; +-- Will be set to the main ActorFrame for the screen in its OnCommand. +local main_frame= false + +local function input(event) + if event.type == "InputEventType_Release" then return end + local pn= event.PlayerNumber + local code= event.GameButton + if not pn or not code then return end + local input_functions= { + Start= function() + MESSAGEMAN:Broadcast("StartButton") + if not GAMESTATE:IsHumanPlayer(pn) then + SCREENMAN:GetTopScreen():SetProfileIndex(pn, -1) + else + SCREENMAN:GetTopScreen():Finish() + end + end, + Back= function() + if GAMESTATE:GetNumPlayersEnabled()==0 then + SCREENMAN:GetTopScreen():Cancel() + else + MESSAGEMAN:Broadcast("BackButton") + SCREENMAN:GetTopScreen():SetProfileIndex(pn, -2) + end + end, + MenuUp= function() + if GAMESTATE:IsHumanPlayer(pn) then + local ind = SCREENMAN:GetTopScreen():GetProfileIndex(pn) + if ind > 1 then + if SCREENMAN:GetTopScreen():SetProfileIndex(pn, ind - 1) then + MESSAGEMAN:Broadcast("DirectionButton") + main_frame:queuecommand('UpdateInternal2') + end + end + end + end, + MenuDown= function() + if GAMESTATE:IsHumanPlayer(pn) then + local ind = SCREENMAN:GetTopScreen():GetProfileIndex(pn) + if ind > 0 then + if SCREENMAN:GetTopScreen():SetProfileIndex(pn, ind + 1) then + MESSAGEMAN:Broadcast("DirectionButton") + main_frame:queuecommand('UpdateInternal2') + end + end + end + end + } + input_functions.MenuLeft= input_functions.MenuUp + input_functions.MenuRight= input_functions.MenuDown + if input_functions[code] then + input_functions[code]() + end +end + local t = Def.ActorFrame { StorageDevicesChangedMessageCommand=function(self, params) self:queuecommand('UpdateInternal2'); end; - CodeMessageCommand = function(self, params) - if params.Name == 'Start' or params.Name == 'Center' then - MESSAGEMAN:Broadcast("StartButton"); - if not GAMESTATE:IsHumanPlayer(params.PlayerNumber) then - SCREENMAN:GetTopScreen():SetProfileIndex(params.PlayerNumber, -1); - else - SCREENMAN:GetTopScreen():Finish(); - end; - end; - if params.Name == 'Up' or params.Name == 'Up2' or params.Name == 'DownLeft' then - if GAMESTATE:IsHumanPlayer(params.PlayerNumber) then - local ind = SCREENMAN:GetTopScreen():GetProfileIndex(params.PlayerNumber); - if ind > 1 then - if SCREENMAN:GetTopScreen():SetProfileIndex(params.PlayerNumber, ind - 1 ) then - MESSAGEMAN:Broadcast("DirectionButton"); - self:queuecommand('UpdateInternal2'); - end; - end; - end; - end; - if params.Name == 'Down' or params.Name == 'Down2' or params.Name == 'DownRight' then - if GAMESTATE:IsHumanPlayer(params.PlayerNumber) then - local ind = SCREENMAN:GetTopScreen():GetProfileIndex(params.PlayerNumber); - if ind > 0 then - if SCREENMAN:GetTopScreen():SetProfileIndex(params.PlayerNumber, ind + 1 ) then - MESSAGEMAN:Broadcast("DirectionButton"); - self:queuecommand('UpdateInternal2'); - end; - end; - end; - end; - if params.Name == 'Back' then - if GAMESTATE:GetNumPlayersEnabled()==0 then - SCREENMAN:GetTopScreen():Cancel(); - else - MESSAGEMAN:Broadcast("BackButton"); - SCREENMAN:GetTopScreen():SetProfileIndex(params.PlayerNumber, -2); - end; - end; - end; - PlayerJoinedMessageCommand=function(self, params) self:queuecommand('UpdateInternal2'); end; @@ -249,6 +263,8 @@ local t = Def.ActorFrame { end; OnCommand=function(self, params) + main_frame= self:GetParent() + SCREENMAN:GetTopScreen():AddInputCallback(input) self:queuecommand('UpdateInternal2'); end; diff --git a/src/OptionRow.cpp b/src/OptionRow.cpp index 60f7fa1e66..e5b11340f1 100644 --- a/src/OptionRow.cpp +++ b/src/OptionRow.cpp @@ -803,7 +803,7 @@ bool OptionRow::SetSelected( PlayerNumber pn, int iChoice, bool b ) bool OptionRow::NotifyHandlerOfSelection(PlayerNumber pn, int choice) { - bool changed= m_pHand->NotifyOfSelection(pn, choice); + bool changed= m_pHand->NotifyOfSelection(pn, choice - m_bFirstItemGoesDown); if(changed) { ChoicesChanged(m_RowType, false); @@ -820,6 +820,11 @@ bool OptionRow::NotifyHandlerOfSelection(PlayerNumber pn, int choice) return changed; } +bool OptionRow::GoToFirstOnStart() +{ + return m_pHand->GoToFirstOnStart(); +} + void OptionRow::SetExitText( RString sExitText ) { BitmapText *bt = m_textItems.back(); diff --git a/src/OptionRow.h b/src/OptionRow.h index 7a2c825bca..2992d97f5b 100644 --- a/src/OptionRow.h +++ b/src/OptionRow.h @@ -112,6 +112,7 @@ public: // ScreenOptions calls positions m_FrameDestination, then m_Frame tween to that same TweenState. unsigned GetTextItemsSize() const { return m_textItems.size(); } bool GetFirstItemGoesDown() const { return m_bFirstItemGoesDown; } + bool GoToFirstOnStart(); RString GetThemedItemText( int iChoice ) const; diff --git a/src/OptionRowHandler.cpp b/src/OptionRowHandler.cpp index 9d2893e2b4..be167fbc2a 100644 --- a/src/OptionRowHandler.cpp +++ b/src/OptionRowHandler.cpp @@ -833,8 +833,9 @@ public: LuaReference m_EnabledForPlayersFunc; bool m_TableIsSane; + bool m_GoToFirstOnStart; - OptionRowHandlerLua(): m_TableIsSane(false) + OptionRowHandlerLua(): m_TableIsSane(false), m_GoToFirstOnStart(false) { m_pLuaTable = new LuaReference; Init(); } virtual ~OptionRowHandlerLua() { delete m_pLuaTable; } void Init() @@ -1030,6 +1031,10 @@ public: m_Def.m_sName = pStr; lua_pop( L, 1 ); + lua_getfield(L, -1, "GoToFirstOnStart"); + m_GoToFirstOnStart= lua_toboolean(L, -1); + lua_pop(L, 1); + lua_getfield(L, -1, "OneChoiceForAllPlayers"); m_Def.m_bOneChoiceForAllPlayers = lua_toboolean( L, -1 ); lua_pop( L, 1 ); @@ -1253,6 +1258,10 @@ public: LUA->Release(L); return changed; } + virtual bool GoToFirstOnStart() + { + return m_GoToFirstOnStart; + } }; class OptionRowHandlerConfig : public OptionRowHandler diff --git a/src/OptionRowHandler.h b/src/OptionRowHandler.h index f041e8768d..28cbc8b0dd 100644 --- a/src/OptionRowHandler.h +++ b/src/OptionRowHandler.h @@ -180,6 +180,7 @@ public: virtual RString GetScreen( int /* iChoice */ ) const { return RString(); } // Exists so that a lua function can act on the selection. Returns true if the choices should be reloaded. virtual bool NotifyOfSelection(PlayerNumber pn, int choice) { return false; } + virtual bool GoToFirstOnStart() { return true; } }; /** @brief Utilities for the OptionRowHandlers. */ diff --git a/src/PrefsManager.cpp b/src/PrefsManager.cpp index 1a967e2745..27522d3628 100644 --- a/src/PrefsManager.cpp +++ b/src/PrefsManager.cpp @@ -199,6 +199,7 @@ PrefsManager::PrefsManager() : m_bShowCaution ( "ShowCaution", true ), m_bShowNativeLanguage ( "ShowNativeLanguage", true ), m_iArcadeOptionsNavigation ( "ArcadeOptionsNavigation", 0 ), + m_ThreeKeyNavigation("ThreeKeyNavigation", false), m_MusicWheelUsesSections ( "MusicWheelUsesSections", MusicWheelUsesSections_ALWAYS ), m_iMusicWheelSwitchSpeed ( "MusicWheelSwitchSpeed", 15 ), m_AllowW1 ( "AllowW1", ALLOW_W1_EVERYWHERE ), diff --git a/src/PrefsManager.h b/src/PrefsManager.h index 6ce1acfd68..509acf8d8d 100644 --- a/src/PrefsManager.h +++ b/src/PrefsManager.h @@ -191,6 +191,7 @@ public: Preference m_bShowCaution; Preference m_bShowNativeLanguage; Preference m_iArcadeOptionsNavigation; + Preference m_ThreeKeyNavigation; Preference m_MusicWheelUsesSections; Preference m_iMusicWheelSwitchSpeed; Preference m_AllowW1; // this should almost always be on, given use cases. -aj diff --git a/src/RageTexture.cpp b/src/RageTexture.cpp index 0d9e17fd90..944f54c180 100644 --- a/src/RageTexture.cpp +++ b/src/RageTexture.cpp @@ -79,6 +79,11 @@ public: lua_pushnumber( L, pRect->bottom ); return 4; } + static int GetNumFrames(T* p, lua_State* L) + { + lua_pushnumber(L, p->GetNumFrames()); + return 1; + } LunaRageTexture() { @@ -86,6 +91,7 @@ public: ADD_METHOD( loop ); ADD_METHOD( rate ); ADD_METHOD( GetTextureCoordRect ); + ADD_METHOD( GetNumFrames ); } }; diff --git a/src/ScreenOptions.cpp b/src/ScreenOptions.cpp index ecb23a5d5a..c91c44d520 100644 --- a/src/ScreenOptions.cpp +++ b/src/ScreenOptions.cpp @@ -916,7 +916,7 @@ void ScreenOptions::ProcessMenuStart( const InputEventPlus &input ) msg.SetParam( "Selected", bSelected ); MESSAGEMAN->Broadcast( msg ); - if( row.GetFirstItemGoesDown() ) + if(row.GetFirstItemGoesDown() && row.GoToFirstOnStart()) { // move to the first choice in the row ChangeValueInRowRelative( m_iCurrentRow[pn], pn, -row.GetChoiceInRowWithFocus(pn), input.type != IET_FIRST_PRESS ); diff --git a/src/ScreenOptionsMasterPrefs.cpp b/src/ScreenOptionsMasterPrefs.cpp index 942540c69e..75ea161ea1 100644 --- a/src/ScreenOptionsMasterPrefs.cpp +++ b/src/ScreenOptionsMasterPrefs.cpp @@ -705,6 +705,7 @@ static void InitializeConfOptions() ADD( ConfOption( "AutoPlay", MovePref, "Off","On","CPU-Controlled" ) ); ADD( ConfOption( "DelayedBack", MovePref, "Instant","Hold" ) ); ADD( ConfOption( "ArcadeOptionsNavigation", MovePref, "StepMania Style","Arcade Style" ) ); + ADD( ConfOption( "ThreeKeyNavigation", MovePref, "Five Key Menu", "Three Key Menu" ) ); ADD( ConfOption( "MusicWheelSwitchSpeed", MusicWheelSwitchSpeed, "Slow","Normal","Fast","Really Fast" ) ); // Gameplay options diff --git a/src/Sprite.h b/src/Sprite.h index 4e4ba791ac..f6f95e5fba 100644 --- a/src/Sprite.h +++ b/src/Sprite.h @@ -14,7 +14,7 @@ public: Sprite(); Sprite( const Sprite &cpy ); virtual ~Sprite(); - + // See explanation in source. static Sprite* NewBlankSprite();