diff --git a/Docs/Themerdocs/Examples/Example_Actors/ActorScroller.lua b/Docs/Themerdocs/Examples/Example_Actors/ActorScroller.lua new file mode 100644 index 0000000000..27a0a1817a --- /dev/null +++ b/Docs/Themerdocs/Examples/Example_Actors/ActorScroller.lua @@ -0,0 +1,62 @@ +-- A simple ActorScroller example with a couple of BitmapText children. +-- ActorScrollers are used to create scrolling displays of other Actors. + +Def.ActorScroller{ + -- (float) Number of items to have visible at one time. + -- ScrollThroughAllItems divides this value by 2. + NumItemsToDraw=14, -- Default value is 7 if this parameter is omitted. + + -- (float) The number of seconds to show each item. + SecondsPerItem=1, -- Default value is 1 if this parameter is omitted. + + -- Transforms the ActorScroller's children. Usually used for scrolling. + -- The most important part of an ActorScroller. + -- This example spaces each item out 32 pixels vertically. + + -- "offset" represents the offset from the center of the scroller. + TransformFunction=function(self,offset,itemIndex,numItems) + self:y(32*offset) + end, + + -- (int) "1 == one evaluation per position" + -- default value is 1 if this parameter is omitted. + --Subdivisions=1, + + -- (bool) A built-in mask for hiding elements. Disabled by default. + --UseMask=false, + + -- (float) Set the width and height of the mask. Only used if UseMask is true. + -- Defaults for both are 0. + --MaskWidth=0, + --MaskHeight=0, + + -- children (items to scroll) + -- CAVEAT: BitmapTexts have to be wrapped in ActorFrames in order to be colored. + -- This is a long standing issue (ITG? ITG2?) + Def.ActorFrame{ + Def.BitmapText{ + Name="ScrollItem1", + Font="Common normal", + Text="Scroll Item 1", + InitCommand=cmd(diffuse,color("#FF0000")), + }, + }, + Def.ActorFrame{ + Def.BitmapText{ + Name="ScrollItem2", + Font="Common normal", + Text="Scroll Item 2", + InitCommand=cmd(glow,color("#00FF0088")), + }, + }, + Def.BitmapText{ + Name="ScrollItem3", + Font="Common normal", + Text="Scroll Item 3", + InitCommand=cmd(bob;effectmagnitude,8,0,4), + }, + + -- Scroller commands + InitCommand=cmd(Center), + OnCommand=cmd(scrollwithpadding,8,8), +} diff --git a/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/BGAnimations/ScreenSimpleExample overlay.lua b/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/BGAnimations/ScreenSimpleExample overlay.lua new file mode 100644 index 0000000000..a8a4cd280c --- /dev/null +++ b/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/BGAnimations/ScreenSimpleExample overlay.lua @@ -0,0 +1,57 @@ +-- This screen file returns an ActorFrame with two actors. + +-- A screen file must return an ActorFrame. +-- All actors on the screen must be inside this ActorFrame. +return Def.ActorFrame{ + -- An actor is added to a frame by calling a function that returns an actor. + -- Most of the functions for creating actors are in the Def table. + + -- All of the functions in the Def table take a table with the parameters + -- to construct the actor. + + -- For convenience, Lua makes the parentheses normally used when calling a + -- function optional when that function's sole argument is a table. + -- "foo({...})" and "foo{...}" are equivalent in lua. + + -- Quad is a very simple actor, a colored rectangle. + Def.Quad{ + -- The parameters for an actor are set through entries in a table. + -- This is just a simple example, so it only sets the required parts. + + -- Every actor should have a name. + -- The name can be used to find the actor later. + -- If you don't set the name, the actor will have a blank name. + -- The name should be unique within the ActorFrame the actor is inside. + -- See the entries for ActorFrame:GetChild and ActorFrame:GetChildren in + -- Lua.xml for details on what happens when two actors in the same + -- ActorFrame have the same name. + -- Actors that are inside different ActorFrames can have the same name + -- without causing any problems. + Name= "example", + + -- The InitCommand for an actor should initialize any parts of the actor + -- that should be set before the first time it draws. + -- If you don't set the InitCommand, the actor will be in its default + -- state. Details of the default state of an actor can be found in the + -- example for that actor. + InitCommand= function(self) + -- The details of these functions can be found in Lua.xml. + -- Position, size, and color should be initialized for a quad. + self:xy(SCREEN_CENTER_X, SCREEN_CENTER_Y) + self:setsize(20, 20) + self:diffuse(color("1,1,1,1")) + end, + }, + -- The contents of an ActorFrame are entries in a table, so we separate + -- them with commas. + Def.Quad{ + -- This quad is almost identical to the previous one, so there is nothing + -- new here to explain. + Name= "example2", + InitCommand= function(self) + self:xy(SCREEN_CENTER_X, SCREEN_CENTER_Y-40) + self:setsize(20, 20) + self:diffuse(color("0,0,1,1")) + end, + }, +} diff --git a/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/ThemeInfo.ini b/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/ThemeInfo.ini new file mode 100644 index 0000000000..e003ac2ceb --- /dev/null +++ b/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/ThemeInfo.ini @@ -0,0 +1,4 @@ +# This file gives a bit of basic information about the theme. +[ThemeInfo] +DisplayName=One Screen Example Theme +Author=Kyzentun diff --git a/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/metrics.ini b/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/metrics.ini new file mode 100644 index 0000000000..b93aec4bb7 --- /dev/null +++ b/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/metrics.ini @@ -0,0 +1,19 @@ +# This metrics.ini declares a single screen and makes it the first (and only) screen in the theme. + +# Metrics are organized into groups. +# The next line is a group name. All the lines after it until the next group name will set metrics in this group. +[Common] +# The Common group is used for setting common metrics. See _fallback/metrics.ini for a list of all things that can be set in the Common group. +# Since this is a simple example, it only sets the initial screen. +InitialScreen="ScreenSimpleExample" + +# Every screen in a theme must have a group declared for it. +[ScreenSimpleExample] +# Since ScreenSimpleExample does not exist in _fallback, the fallback group and class for it must be specified. +# ScreenWithMenuElements is the simplest screen you will want to use, it has practically nothing on it. +Class="ScreenWithMenuElements" +# Setting the Fallback metric for a group means that whenever a metric is not found in the group, the group named by the Fallback metric will be searched. +# This means that everything set in the ScreenWithMenuElements group is also set in the ScreenSimpleExample group. +Fallback="ScreenWithMenuElements" + +# That's all. Other metrics could be set, but this is a minimal example. diff --git a/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/readme.txt b/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/readme.txt new file mode 100644 index 0000000000..cc8c7af063 --- /dev/null +++ b/Docs/Themerdocs/Examples/Example_Themes/One_Screen_Example_Theme/readme.txt @@ -0,0 +1,11 @@ +This is the "One Screen Example Theme". +It's a very minimal theme with a single screen showing two rectangles and the decorations that ScreenWithMenuElements has built in by default. +You can try it out by copying it into the Themes folder. + +Since this theme only has one screen, and no code to handle transitions or a normal method of accessing the options, in order to change to another theme you'll have to access the options menu by pressing the service key, which is mapped to Scroll Lock by default. + + +Parts of this theme: +metrics.ini +ThemeInfo.ini +BGAnimations/ScreenSimpleExample overlay.lua diff --git a/Docs/Themerdocs/ScreenTextEntry.txt b/Docs/Themerdocs/ScreenTextEntry.txt index ccec70543b..a9ff329916 100644 --- a/Docs/Themerdocs/ScreenTextEntry.txt +++ b/Docs/Themerdocs/ScreenTextEntry.txt @@ -1,5 +1,20 @@ -- how to connect to a server with the new ScreenTextEntry Lua bindings -- (this is the base code; I'll come up with a full tutorial later.) + +-- possible text entry settings: +local teSettings = { + SendOnPop = "", -- ScreenMessage to send on pop (optional, "SM_None" if omitted) + Question = "", -- The question to display + InitialAnswer = "", -- Initial answer text + MaxInputLength = 0, -- Maximum amount of characters + Password = false, -- Mask character input (optional) + Validate = nil, -- Validation function; function(answer, errorOut), must return boolean, string. + OnOK = nil, -- On OK; function(answer) + OnCancel = nil, -- On Cancel; function() + ValidateAppend = nil, -- Validate appending a character; function(answer,append), must return boolean + FormatAnswerForDisplay = nil -- Format answer for display; function(answer), must return string +}; + SCREENMAN:AddNewScreenToTop("ScreenTextEntry"); local serverSettings = { Question = "Connect to server:", diff --git a/Themes/_fallback/BGAnimations/ScreenSystemLayer error.lua b/Themes/_fallback/BGAnimations/ScreenSystemLayer error.lua index 4193ed54eb..6772731280 100644 --- a/Themes/_fallback/BGAnimations/ScreenSystemLayer error.lua +++ b/Themes/_fallback/BGAnimations/ScreenSystemLayer error.lua @@ -53,6 +53,4 @@ local log_args= { Font= "Common Error", } -Trace("Loaded error layer.") - return Def.LogDisplay(log_args) diff --git a/src/NoteSkinManager.cpp b/src/NoteSkinManager.cpp index 2148154829..68fd330073 100644 --- a/src/NoteSkinManager.cpp +++ b/src/NoteSkinManager.cpp @@ -200,14 +200,23 @@ void NoteSkinManager::GetNoteSkinNames( const Game* pGame, vector &AddT GetAllNoteSkinNamesForGame( pGame, AddTo ); } +bool NoteSkinManager::NoteSkinNameInList(const RString name, vector name_list) +{ + for(size_t i= 0; i < name_list.size(); ++i) + { + if(0 == stricmp(name, name_list[i])) + { + return true; + } + } + return false; +} + bool NoteSkinManager::DoesNoteSkinExist( const RString &sSkinName ) { vector asSkinNames; GetAllNoteSkinNamesForGame( GAMESTATE->m_pCurGame, asSkinNames ); - for( unsigned i=0; iGetMetric("Common", "DefaultNoteSkinName"); + vector all_names; + GetAllNoteSkinNamesForGame(GAMESTATE->m_pCurGame, all_names); + if(all_names.empty()) + { + return ""; + } + if(!NoteSkinNameInList(name, all_names)) + { + name= "default"; + if(!NoteSkinNameInList(name, all_names)) + { + name= all_names[1]; + } + } + return name; +} + void NoteSkinManager::GetAllNoteSkinNamesForGame( const Game *pGame, vector &AddTo ) { if( pGame == m_pCurGame ) diff --git a/src/NoteSkinManager.h b/src/NoteSkinManager.h index c3cd1ef55f..f5ad944020 100644 --- a/src/NoteSkinManager.h +++ b/src/NoteSkinManager.h @@ -20,8 +20,10 @@ public: void RefreshNoteSkinData( const Game* game ); void GetNoteSkinNames( const Game* game, vector &AddTo ); void GetNoteSkinNames( vector &AddTo ); // looks up current const Game* in GAMESTATE + bool NoteSkinNameInList(const RString name, vector name_list); bool DoesNoteSkinExist( const RString &sNoteSkin ); // looks up current const Game* in GAMESTATE bool DoNoteSkinsExistForGame( const Game *pGame ); + RString GetDefaultNoteSkinName(); // looks up current const Game* in GAMESTATE void SetCurrentNoteSkin( const RString &sNoteSkin ) { m_sCurrentNoteSkin = sNoteSkin; } const RString &GetCurrentNoteSkin() { return m_sCurrentNoteSkin; } diff --git a/src/PlayerOptions.cpp b/src/PlayerOptions.cpp index c25ddb2672..18fd05f357 100644 --- a/src/PlayerOptions.cpp +++ b/src/PlayerOptions.cpp @@ -376,7 +376,11 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut m_fTimeSpacing = 0; } - else if( sBit == "clearall" ) Init(); + else if( sBit == "clearall" ) + { + Init(); + m_sNoteSkin= NOTESKIN->GetDefaultNoteSkinName(); + } else if( sBit == "resetspeed" ) { /* level is set to the values from Init() because all speed related diff --git a/src/RollingNumbers.cpp b/src/RollingNumbers.cpp index 95c48e5926..f267ed87b3 100644 --- a/src/RollingNumbers.cpp +++ b/src/RollingNumbers.cpp @@ -72,7 +72,7 @@ void RollingNumbers::DrawPrimitives() void RollingNumbers::Update( float fDeltaTime ) { - if(m_fCurrentNumber != m_fTargetNumber && m_fScoreVelocity > 0) + if(m_fCurrentNumber != m_fTargetNumber) { fapproach( m_fCurrentNumber, m_fTargetNumber, fabsf(m_fScoreVelocity) * fDeltaTime ); UpdateText(); diff --git a/src/ScreenGameplaySyncMachine.cpp b/src/ScreenGameplaySyncMachine.cpp index 24456d85bf..399904f26d 100644 --- a/src/ScreenGameplaySyncMachine.cpp +++ b/src/ScreenGameplaySyncMachine.cpp @@ -94,6 +94,10 @@ void ScreenGameplaySyncMachine::HandleScreenMessage( const ScreenMessage SM ) if( SM == SM_GoToPrevScreen || SM == SM_GoToNextScreen ) { + FOREACH_PlayerNumber( pn ) + { + GAMESTATE->m_pCurSteps[pn].Set( NULL ); + } GAMESTATE->m_PlayMode.Set( PlayMode_Invalid ); GAMESTATE->SetCurrentStyle( NULL ); GAMESTATE->m_pCurSong.Set( NULL ); diff --git a/src/ThemeManager.cpp b/src/ThemeManager.cpp index c80866f4da..8a9d0625de 100644 --- a/src/ThemeManager.cpp +++ b/src/ThemeManager.cpp @@ -864,12 +864,20 @@ RString ThemeManager::GetMetricsIniPath( const RString &sThemeName ) bool ThemeManager::HasMetric( const RString &sMetricsGroup, const RString &sValueName ) { RString sThrowAway; + if(sMetricsGroup == "" || sValueName == "") + { + return false; + } return GetMetricRawRecursive( g_pLoadedThemeData->iniMetrics, sMetricsGroup, sValueName, sThrowAway ); } bool ThemeManager::HasString( const RString &sMetricsGroup, const RString &sValueName ) { RString sThrowAway; + if(sMetricsGroup == "" || sValueName == "") + { + return false; + } return GetMetricRawRecursive( g_pLoadedThemeData->iniStrings, sMetricsGroup, sValueName, sThrowAway ); } @@ -1045,6 +1053,11 @@ LuaReference ThemeManager::GetMetricR( const RString &sMetricsGroup, const RStri void ThemeManager::PushMetric( Lua *L, const RString &sMetricsGroup, const RString &sValueName ) { + if(sMetricsGroup == "" || sValueName == "") + { + LuaHelpers::ReportScriptError("PushMetric: Attempted to fetch metric with empty group name or empty value name."); + return; + } RString sValue = GetMetricRaw( g_pLoadedThemeData->iniMetrics, sMetricsGroup, sValueName ); RString sName = ssprintf( "%s::%s", sMetricsGroup.c_str(), sValueName.c_str() ); @@ -1179,6 +1192,11 @@ static RString PseudoLocalize( RString s ) RString ThemeManager::GetString( const RString &sMetricsGroup, const RString &sValueName_ ) { RString sValueName = sValueName_; + if(sMetricsGroup == "" || sValueName == "") + { + LuaHelpers::ReportScriptError("PushMetric: Attempted to fetch metric with empty group name or empty value name."); + return ""; + } // TODO: Handle escaping = with \= DEBUG_ASSERT( sValueName.find('=') == sValueName.npos ); @@ -1267,9 +1285,29 @@ public: static int ReloadMetrics( T* p, lua_State *L ) { p->ReloadMetrics(); return 0; } static int HasMetric( T* p, lua_State *L ) { lua_pushboolean(L, p->HasMetric(SArg(1),SArg(2))); return 1; } - static int GetMetric( T* p, lua_State *L ) { p->PushMetric( L, SArg(1),SArg(2) ); return 1; } + static int GetMetric( T* p, lua_State *L ) + { + RString group= SArg(1); + RString name= SArg(2); + if(group == "" || name == "") + { + luaL_error(L, "Cannot fetch metric with empty group name or empty value name."); + } + p->PushMetric(L, group, name); + return 1; + } static int HasString( T* p, lua_State *L ) { lua_pushboolean(L, p->HasString(SArg(1),SArg(2))); return 1; } - static int GetString( T* p, lua_State *L ) { lua_pushstring(L, p->GetString(SArg(1),SArg(2)) ); return 1; } + static int GetString( T* p, lua_State *L ) + { + RString group= SArg(1); + RString name= SArg(2); + if(group == "" || name == "") + { + luaL_error(L, "Cannot fetch string with empty group name or empty value name."); + } + lua_pushstring(L, p->GetString(group, name)); + return 1; + } static int GetPathInfoB( T* p, lua_State *L ) { ThemeManager::PathInfo pi;