Changed default ScreenSelecStyle to use functions for choice names and positions instead of metrics. _fallback SSS remains the same for compatibility. Fixed style stomping bug in SetCompatibleStylesForPlayers.

This commit is contained in:
Kyzentun
2015-01-20 18:40:59 -07:00
parent f4a718a824
commit aeaead95c9
10 changed files with 231 additions and 111 deletions
+35 -9
View File
@@ -4,6 +4,32 @@ The StepMania 5 Changelog covers all post-sm-ssc changes. For a list of changes
from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt. from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt.
________________________________________________________________________________ ________________________________________________________________________________
2015/01/20
----------
* [ScreenSelect] ChoiceNames can be a lua command that is evaluated to get a
list of the gamecommands. The main use of this is to get rid of the huge
list of metrics that ScreenSelectStyle requires. A single lua function
instead of a metric for every style for every game type. [kyzentun]
* [ScreenSelectMaster] IconChoicePosFunction metric added. This metric is
optional, if it's set, it must be set to a function that returns a table
of positions. Each position is a table of the form "{x, y, z}", where x,
y, and z are numbers and any that is not a number defaults to 0.
The function is passed the number of choices for the screen.
IconChoiceOnCommand and IconChoiceOffCommand metrics added so each choice
doesn't require its own On/OffCommand pair. These are also optional, but
if they are set, they will override the individual commands if they exist.
(If "IconChoiceCactusOnCommand" and "IconChoiceOnCommand" both exist,
"IconChoiceOnCommand" will be used.) [kyzentun]
Example:
# in metrics.ini
IconChoicePosFunction=choice_positions
-- In a file in Scripts/
function choice_positions(count)
local ret= {}
for i= 1, ret do ret[i]= {i*24, i*48} end
return ret
end
2014/12/10 2014/12/10
---------- ----------
* [EditMode] Play whole song and play from current beat will now play until * [EditMode] Play whole song and play from current beat will now play until
@@ -22,20 +48,20 @@ ________________________________________________________________________________
* [GameManager] Lua Scripts/ folders are now reloaded when the Game mode is * [GameManager] Lua Scripts/ folders are now reloaded when the Game mode is
changed. [kyzentun] changed. [kyzentun]
* [GameState] CanSafelyEnterGameplay function added. [kyzentun] * [GameState] CanSafelyEnterGameplay function added. [kyzentun]
In kickbox game mode, players can have different styles set, pass a In kickbox game mode, players can have different styles set, pass a
PlayerNumber when using SetCurrentStyle or GetCurrentStyle. PlayerNumber when using SetCurrentStyle or GetCurrentStyle.
* [kickbox] New game mode with 4 styles. [kyzentun] * [kickbox] New game mode with 4 styles. [kyzentun]
* [ScreenGameplay] Notefield positioning is handled a bit differently now. * [ScreenGameplay] Notefield positioning is handled a bit differently now.
See comments in _fallback/metrics.ini above the [ScreenGameplay] See comments in _fallback/metrics.ini above the [ScreenGameplay]
MarginFunction metric. Basically, you can define a lua function that MarginFunction metric. Basically, you can define a lua function that
returns the space you want at the edges and in the center instead of returns the space you want at the edges and in the center instead of
setting position metrics. Do NOT use Style:GetWidth to calculate the setting position metrics. Do NOT use Style:GetWidth to calculate the
margins your function returns. margins your function returns.
ScreenGameplay now handles zooming the notefield to fit in the space ScreenGameplay now handles zooming the notefield to fit in the space
instead of using the NeedsZoomOutWith2Players flag in the style. instead of using the NeedsZoomOutWith2Players flag in the style.
If the notefield doesn't fit in the space between the margins, and there is If the notefield doesn't fit in the space between the margins, and there is
only one player, the player will be centered even if the Center1Player pref only one player, the player will be centered even if the Center1Player pref
is false. [kyzentun] is false. [kyzentun]
* [Style] NeedsZoomOutWith2Players decapitated. Always returns false now. * [Style] NeedsZoomOutWith2Players decapitated. Always returns false now.
GetWidth added. [kyzentun] GetWidth added. [kyzentun]
* [techno] Techno game mode no longer has special column width for versus. * [techno] Techno game mode no longer has special column width for versus.
@@ -74,7 +100,7 @@ ________________________________________________________________________________
a new profile was created fixed. [kyzentun] a new profile was created fixed. [kyzentun]
* [Everything] Actor and singleton functions that formerly returned nothing now * [Everything] Actor and singleton functions that formerly returned nothing now
return the actor or singleton. Example (set zoom, x, y, and diffuse in one): return the actor or singleton. Example (set zoom, x, y, and diffuse in one):
self:zoom(.5):xy(_screen.cx, _screen.h-60):diffuse(color("#dc322f")) self:zoom(.5):xy(_screen.cx, _screen.h-60):diffuse(color("#dc322f"))
This also works for PlayerOptions and SongOptions API functions. Pass true This also works for PlayerOptions and SongOptions API functions. Pass true
as an extra arg, and they can be chained. as an extra arg, and they can be chained.
+40
View File
@@ -107,6 +107,46 @@ function GameCompatibleModes()
return Modes[CurGameName()] return Modes[CurGameName()]
end end
local function upper_first_letter(s)
local first_letter= s:match("([a-zA-Z])")
return s:gsub(first_letter, first_letter:upper(), 1)
end
-- No more having a metric for every style for every game mode. -Kyz
function ScreenSelectStyleChoices()
local styles= GAMEMAN:GetStylesForGame(GAMESTATE:GetCurrentGame():GetName())
local choices= {}
for i, style in ipairs(styles) do
local name= style:GetName()
local cap_name= upper_first_letter(name)
choices[#choices+1]= "name," .. cap_name .. ";style," .. name .. ";text,"
.. cap_name .. ";screen," .. Branch.AfterSelectStyle()
end
return choices
end
-- No more having an xy for every style for every game mode. -Kyz
function ScreenSelectStylePositions(count)
local poses= {}
local columns= 1
if count > 4 then columns= 2 end
local start_y= _screen.cy - (_screen.h / (math.ceil(count/columns) / 2))
for i= 1, count do
poses[i]= {}
if i <= count/columns then
poses[i][1]= _screen.cx - 160
poses[i][2]= start_y + (96 * i)
else
poses[i][1]= _screen.cx + 160
poses[i][2]= start_y + (96 * (i-(count/2)))
end
if columns == 1 then
poses[i][1]= _screen.cx
end
end
return poses
end
function SelectProfileKeys() function SelectProfileKeys()
local sGame = CurGameName() local sGame = CurGameName()
if sGame == "pump" then if sGame == "pump" then
+6 -5
View File
@@ -1944,6 +1944,12 @@ PrevScreen=Branch.TitleMenu()
TimerSeconds=30 TimerSeconds=30
# #
DefaultChoice="Single" DefaultChoice="Single"
# Giant metric list left in for backwards compatibility.
# A much better way to handle this is:
# ChoiceNames="lua,ScreenSelectStyleChoices()"
# That will list all the styles for the current game without needing a huge
# list of metrics. _fallback still uses the old method to avoid the
# possibility of breaking themes. -Kyz
ChoiceNames=GameCompatibleModes() ChoiceNames=GameCompatibleModes()
# #
OptionOrderAuto="1:2,2:1" OptionOrderAuto="1:2,2:1"
@@ -1975,11 +1981,6 @@ ChoiceVersus8="name,Versus8;style,versus8;screen,"..Branch.AfterSelectStyle()
ChoiceDouble4="name,Double4;style,double4;screen,"..Branch.AfterSelectStyle() ChoiceDouble4="name,Double4;style,double4;screen,"..Branch.AfterSelectStyle()
ChoiceDouble5="name,Double5;style,double5;screen,"..Branch.AfterSelectStyle() ChoiceDouble5="name,Double5;style,double5;screen,"..Branch.AfterSelectStyle()
ChoiceDouble8="name,Double8;style,double8;screen,"..Branch.AfterSelectStyle() ChoiceDouble8="name,Double8;style,double8;screen,"..Branch.AfterSelectStyle()
# kickbox
ChoiceHuman="name,Human;style,human;screen,"..Branch.AfterSelectStyle()
ChoiceQuadarm="name,Quadarm;style,quadarm;screen,"..Branch.AfterSelectStyle()
ChoiceInsect="name,Insect;style,insect;screen,"..Branch.AfterSelectStyle()
ChoiceArachnid="name,Arachnid;style,arachnid;screen,"..Branch.AfterSelectStyle()
# #
PerChoiceScrollElement=false PerChoiceScrollElement=false
PerChoiceIconElement=false PerChoiceIconElement=false
+5 -62
View File
@@ -771,10 +771,8 @@ FOV=90
PerChoiceScrollElement=false PerChoiceScrollElement=false
PerChoiceIconElement=false PerChoiceIconElement=false
# #
Choice7Keys="name,7Keys;style,single7;screen,"..Branch.AfterSelectStyle() # Having one lua function is so much better than dozens of metrics. -Kyz
Choice10Keys="name,10Keys;style,double5;screen,"..Branch.AfterSelectStyle() ChoiceNames="lua,ScreenSelectStyleChoices()"
Choice14Keys="name,14Keys;style,double7;screen,"..Branch.AfterSelectStyle()
ChoiceKB7="name,kb7;style,single;screen,"..Branch.AfterSelectStyle()
# #
ShowScroller=false ShowScroller=false
ShowIcon=true ShowIcon=true
@@ -784,65 +782,10 @@ UseIconMetrics=true
IconGainFocusCommand=stoptweening;bounceend,0.05;zoom,1; IconGainFocusCommand=stoptweening;bounceend,0.05;zoom,1;
IconLoseFocusCommand=stoptweening;decelerate,0.1;zoom,0.8; IconLoseFocusCommand=stoptweening;decelerate,0.1;zoom,0.8;
# #
IconChoiceSingleX=SCREEN_CENTER_X-160 IconChoicePosFunction=ScreenSelectStylePositions
IconChoiceSingleY=SCREEN_CENTER_Y-96 IconChoiceOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoiceSingleOnCommand=zoom,0;bounceend,0.35;zoom,1 IconChoiceOffCommand=linear,0.05;zoomx,0
IconChoiceSingleOffCommand=linear,0.05;zoomx,0
# #
IconChoiceDoubleX=SCREEN_CENTER_X-160
IconChoiceDoubleY=SCREEN_CENTER_Y
IconChoiceDoubleOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoiceDoubleOffCommand=linear,0.05;zoomx,0
####
IconChoiceSoloX=SCREEN_CENTER_X-160
IconChoiceSoloY=SCREEN_CENTER_Y+96
IconChoiceSoloOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoiceSoloOffCommand=linear,0.05;zoomx,0
#
IconChoiceHalfDoubleX=SCREEN_CENTER_X-160
IconChoiceHalfDoubleY=SCREEN_CENTER_Y+96
IconChoiceHalfDoubleOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoiceHalfDoubleOffCommand=linear,0.05;zoomx,0
####
IconChoiceVersusX=SCREEN_CENTER_X+160
IconChoiceVersusY=string.find(THEME:GetMetric("ScreenSelectStyle","ChoiceNames"),"Routine") and SCREEN_CENTER_Y-96 or SCREEN_CENTER_Y-48
IconChoiceVersusOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoiceVersusOffCommand=linear,0.05;zoomx,0
#
IconChoiceCoupleX=SCREEN_CENTER_X+160
IconChoiceCoupleY=string.find(THEME:GetMetric("ScreenSelectStyle","ChoiceNames"),"Routine") and SCREEN_CENTER_Y or SCREEN_CENTER_Y+48
IconChoiceCoupleOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoiceCoupleOffCommand=linear,0.05;zoomx,0
#
IconChoiceRoutineX=SCREEN_CENTER_X+160
IconChoiceRoutineY=SCREEN_CENTER_Y+96
IconChoiceRoutineOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoiceRoutineOffCommand=linear,0.05;zoomx,0
#
IconChoicekb7X=SCREEN_CENTER_X
IconChoicekb7Y=SCREEN_CENTER_Y
IconChoicekb7OnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoicekb7OffCommand=linear,0.05;zoomx,0
#
IconChoice5KeysX=SCREEN_CENTER_X-160
IconChoice5KeysY=SCREEN_CENTER_Y-48
IconChoice5KeysOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoice5KeysOffCommand=linear,0.05;zoomx,0
#
IconChoice7KeysX=SCREEN_CENTER_X-160
IconChoice7KeysY=SCREEN_CENTER_Y+48
IconChoice7KeysOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoice7KeysOffCommand=linear,0.05;zoomx,0
#
IconChoice10KeysX=SCREEN_CENTER_X+160
IconChoice10KeysY=SCREEN_CENTER_Y-48
IconChoice10KeysOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoice10KeysOffCommand=linear,0.05;zoomx,0
#
IconChoice14KeysX=SCREEN_CENTER_X+160
IconChoice14KeysY=SCREEN_CENTER_Y+48
IconChoice14KeysOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoice14KeysOffCommand=linear,0.05;zoomx,0
[ScreenSelectPlayMode] [ScreenSelectPlayMode]
PersistScreens="ScreenSelectPlayMode,ScreenSelectMusic" PersistScreens="ScreenSelectPlayMode,ScreenSelectMusic"
+2 -2
View File
@@ -1300,9 +1300,9 @@ void Actor::QueueMessage( const RString& sMessageName )
TI.m_sCommandName = "!" + sMessageName; TI.m_sCommandName = "!" + sMessageName;
} }
void Actor::AddCommand( const RString &sCmdName, apActorCommands apac ) void Actor::AddCommand( const RString &sCmdName, apActorCommands apac, bool warn )
{ {
if( HasCommand(sCmdName) ) if( HasCommand(sCmdName) && warn)
{ {
RString sWarning = GetLineage()+"'s command '"+sCmdName+"' defined twice"; RString sWarning = GetLineage()+"'s command '"+sCmdName+"' defined twice";
LuaHelpers::ReportScriptError(sWarning, "COMMAND_DEFINED_TWICE"); LuaHelpers::ReportScriptError(sWarning, "COMMAND_DEFINED_TWICE");
+1 -1
View File
@@ -577,7 +577,7 @@ public:
virtual void PushContext( lua_State *L ); virtual void PushContext( lua_State *L );
// Named commands // Named commands
void AddCommand( const RString &sCmdName, apActorCommands apac ); void AddCommand( const RString &sCmdName, apActorCommands apac, bool warn= true );
bool HasCommand( const RString &sCmdName ) const; bool HasCommand( const RString &sCmdName ) const;
const apActorCommands *GetCommand( const RString &sCommandName ) const; const apActorCommands *GetCommand( const RString &sCommandName ) const;
void PlayCommand( const RString &sCommandName ) { HandleMessage( Message(sCommandName) ); } // convenience void PlayCommand( const RString &sCommandName ) { HandleMessage( Message(sCommandName) ); } // convenience
+3 -3
View File
@@ -3163,12 +3163,12 @@ static const Style g_Style_Kickbox_Arachnid_Versus=
static const Style* g_apGame_Kickbox_Styles[] = static const Style* g_apGame_Kickbox_Styles[] =
{ {
&g_Style_Kickbox_Human, &g_Style_Kickbox_Human,
&g_Style_Kickbox_Human_Versus,
&g_Style_Kickbox_Quadarm, &g_Style_Kickbox_Quadarm,
&g_Style_Kickbox_Quadarm_Versus,
&g_Style_Kickbox_Insect, &g_Style_Kickbox_Insect,
&g_Style_Kickbox_Insect_Versus,
&g_Style_Kickbox_Arachnid, &g_Style_Kickbox_Arachnid,
&g_Style_Kickbox_Human_Versus,
&g_Style_Kickbox_Quadarm_Versus,
&g_Style_Kickbox_Insect_Versus,
&g_Style_Kickbox_Arachnid_Versus, &g_Style_Kickbox_Arachnid_Versus,
NULL NULL
}; };
+17 -28
View File
@@ -998,7 +998,7 @@ void GameState::SetCompatibleStylesForPlayers()
SetCurrentStyle(style, PLAYER_INVALID); SetCurrentStyle(style, PLAYER_INVALID);
} }
} }
else else if(GetCurrentStyle(PLAYER_INVALID) == NULL)
{ {
vector<StepsType> vst; vector<StepsType> vst;
GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst); GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst);
@@ -1009,37 +1009,26 @@ void GameState::SetCompatibleStylesForPlayers()
} }
if(!style_set) if(!style_set)
{ {
if(GetCurrentGame()->m_PlayersHaveSeparateStyles) FOREACH_EnabledPlayer(pn)
{ {
FOREACH_EnabledPlayer(pn) StepsType st= StepsType_Invalid;
if(m_pCurSteps[pn] != NULL)
{ {
StepsType st= StepsType_Invalid; st= m_pCurSteps[pn]->m_StepsType;
if(m_pCurSteps[pn] != NULL) }
{ else if(m_pCurTrail[pn] != NULL)
st= m_pCurSteps[pn]->m_StepsType; {
} st= m_pCurTrail[pn]->m_StepsType;
else if(m_pCurTrail[pn] != NULL) }
{ else
st= m_pCurTrail[pn]->m_StepsType; {
} vector<StepsType> vst;
else GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst);
{ st= vst[0];
vector<StepsType> vst;
GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst);
st= vst[0];
}
const Style *style = GAMEMAN->GetFirstCompatibleStyle(
m_pCurGame, GetNumSidesJoined(), st);
SetCurrentStyle(style, pn);
} }
}
else
{
vector<StepsType> vst;
GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst);
const Style *style = GAMEMAN->GetFirstCompatibleStyle( const Style *style = GAMEMAN->GetFirstCompatibleStyle(
m_pCurGame, GetNumSidesJoined(), vst[0]); m_pCurGame, GetNumSidesJoined(), st);
SetCurrentStyle(style, PLAYER_INVALID); SetCurrentStyle(style, pn);
} }
} }
} }
+40
View File
@@ -31,6 +31,46 @@ void ScreenSelect::Init()
this->SubscribeToMessage( Message_PlayerJoined ); this->SubscribeToMessage( Message_PlayerJoined );
// Load choices // Load choices
// Allow lua as an alternative to metrics.
RString choice_names= CHOICE_NAMES;
if(choice_names.Left(4) == "lua,")
{
RString command= choice_names.Right(choice_names.size()-4);
Lua* L= LUA->Get();
if(LuaHelpers::RunExpression(L, command, m_sName + "::ChoiceNames"))
{
if(!lua_istable(L, 1))
{
LuaHelpers::ReportScriptError(m_sName + "::ChoiceNames expression did not return a table of gamecommands.");
}
else
{
size_t len= lua_objlen(L, 1);
for(size_t i= 1; i <= len; ++i)
{
lua_rawgeti(L, 1, i);
if(!lua_isstring(L, -1))
{
LuaHelpers::ReportScriptErrorFmt(m_sName + "::ChoiceNames element %zu is not a string.", i);
}
else
{
RString com= SArg(-1);
GameCommand mc;
mc.ApplyCommitsScreens(false);
mc.m_sName = ssprintf("%zu", i);
Commands cmd= ParseCommands(com);
mc.Load(i, cmd);
m_aGameCommands.push_back(mc);
}
lua_pop(L, 1);
}
}
}
lua_settop(L, 0);
LUA->Release(L);
}
else
{ {
// Instead of using NUM_CHOICES, use a comma-separated list of choices. // Instead of using NUM_CHOICES, use a comma-separated list of choices.
// Each element in the list is a choice name. This level of indirection // Each element in the list is a choice name. This level of indirection
+82 -1
View File
@@ -104,6 +104,68 @@ void ScreenSelectMaster::Init()
FOREACH( PlayerNumber, vpns, p ) FOREACH( PlayerNumber, vpns, p )
m_vsprScroll[*p].resize( m_aGameCommands.size() ); m_vsprScroll[*p].resize( m_aGameCommands.size() );
vector<RageVector3> positions;
bool positions_set_by_lua= false;
if(THEME->HasMetric(m_sName, "IconChoicePosFunction"))
{
positions_set_by_lua= true;
LuaReference command= THEME->GetMetricR(m_sName, "IconChoicePosFunction");
if(command.GetLuaType() != LUA_TFUNCTION)
{
LuaHelpers::ReportScriptError(m_sName+"::IconChoicePosFunction must be a function.");
positions_set_by_lua= false;
}
else
{
Lua* L= LUA->Get();
command.PushSelf(L);
lua_pushnumber(L, m_aGameCommands.size());
RString err= m_sName + "::IconChoicePosFunction: ";
if(!LuaHelpers::RunScriptOnStack(L, err, 1, 1, true))
{
positions_set_by_lua= false;
}
else
{
if(!lua_istable(L, -1))
{
LuaHelpers::ReportScriptError(m_sName+"::IconChoicePosFunction did not return a table of positions.");
positions_set_by_lua= false;
}
else
{
size_t poses= lua_objlen(L, -1);
for(size_t p= 1; p <= poses; ++p)
{
lua_rawgeti(L, -1, p);
RageVector3 pos(0.0f, 0.0f, 0.0f);
if(!lua_istable(L, -1))
{
LuaHelpers::ReportScriptErrorFmt("Position %zu is not a table.", p);
}
else
{
#define SET_POS_PART(i, part) \
lua_rawgeti(L, -1, i); \
pos.part= lua_tonumber(L, -1); \
lua_pop(L, 1);
// If part of the position is not provided, we want it to
// default to zero, which lua_tonumber does. -Kyz
SET_POS_PART(1, x);
SET_POS_PART(2, y);
SET_POS_PART(3, z);
#undef SET_POS_PART
}
lua_pop(L, 1);
positions.push_back(pos);
}
}
}
lua_settop(L, 0);
LUA->Release(L);
}
}
for( unsigned c=0; c<m_aGameCommands.size(); c++ ) for( unsigned c=0; c<m_aGameCommands.size(); c++ )
{ {
GameCommand& mc = m_aGameCommands[c]; GameCommand& mc = m_aGameCommands[c];
@@ -122,7 +184,26 @@ void ScreenSelectMaster::Init()
RString sName = "Icon" "Choice" + mc.m_sName; RString sName = "Icon" "Choice" + mc.m_sName;
m_vsprIcon[c]->SetName( sName ); m_vsprIcon[c]->SetName( sName );
if( USE_ICON_METRICS ) if( USE_ICON_METRICS )
LOAD_ALL_COMMANDS_AND_SET_XY( m_vsprIcon[c] ); {
if(positions_set_by_lua)
{
LOAD_ALL_COMMANDS(m_vsprIcon[c]);
m_vsprIcon[c]->SetXY(positions[c].x, positions[c].y);
m_vsprIcon[c]->SetZ(positions[c].z);
}
else
{
LOAD_ALL_COMMANDS_AND_SET_XY( m_vsprIcon[c] );
}
#define OPTIONAL_COMMAND(onoff) \
if(THEME->HasMetric(m_sName, "IconChoice" onoff "Command")) \
{ \
m_vsprIcon[c]->AddCommand(onoff, THEME->GetMetricA(m_sName, "IconChoice" onoff "Command"), false); \
}
OPTIONAL_COMMAND("On");
OPTIONAL_COMMAND("Off");
#undef OPTIONAL_COMMAND
}
this->AddChild( m_vsprIcon[c] ); this->AddChild( m_vsprIcon[c] );
} }