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.
________________________________________________________________________________
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
----------
* [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
changed. [kyzentun]
* [GameState] CanSafelyEnterGameplay function added. [kyzentun]
In kickbox game mode, players can have different styles set, pass a
PlayerNumber when using SetCurrentStyle or GetCurrentStyle.
In kickbox game mode, players can have different styles set, pass a
PlayerNumber when using SetCurrentStyle or GetCurrentStyle.
* [kickbox] New game mode with 4 styles. [kyzentun]
* [ScreenGameplay] Notefield positioning is handled a bit differently now.
See comments in _fallback/metrics.ini above the [ScreenGameplay]
MarginFunction metric. Basically, you can define a lua function that
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
margins your function returns.
ScreenGameplay now handles zooming the notefield to fit in the space
instead of using the NeedsZoomOutWith2Players flag in the style.
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
is false. [kyzentun]
margins your function returns.
ScreenGameplay now handles zooming the notefield to fit in the space
instead of using the NeedsZoomOutWith2Players flag in the style.
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
is false. [kyzentun]
* [Style] NeedsZoomOutWith2Players decapitated. Always returns false now.
GetWidth added. [kyzentun]
* [techno] Techno game mode no longer has special column width for versus.
@@ -74,7 +100,7 @@ ________________________________________________________________________________
a new profile was created fixed. [kyzentun]
* [Everything] Actor and singleton functions that formerly returned nothing now
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
as an extra arg, and they can be chained.
+40
View File
@@ -107,6 +107,46 @@ function GameCompatibleModes()
return Modes[CurGameName()]
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()
local sGame = CurGameName()
if sGame == "pump" then
+6 -5
View File
@@ -1944,6 +1944,12 @@ PrevScreen=Branch.TitleMenu()
TimerSeconds=30
#
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()
#
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()
ChoiceDouble5="name,Double5;style,double5;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
PerChoiceIconElement=false
+5 -62
View File
@@ -771,10 +771,8 @@ FOV=90
PerChoiceScrollElement=false
PerChoiceIconElement=false
#
Choice7Keys="name,7Keys;style,single7;screen,"..Branch.AfterSelectStyle()
Choice10Keys="name,10Keys;style,double5;screen,"..Branch.AfterSelectStyle()
Choice14Keys="name,14Keys;style,double7;screen,"..Branch.AfterSelectStyle()
ChoiceKB7="name,kb7;style,single;screen,"..Branch.AfterSelectStyle()
# Having one lua function is so much better than dozens of metrics. -Kyz
ChoiceNames="lua,ScreenSelectStyleChoices()"
#
ShowScroller=false
ShowIcon=true
@@ -784,65 +782,10 @@ UseIconMetrics=true
IconGainFocusCommand=stoptweening;bounceend,0.05;zoom,1;
IconLoseFocusCommand=stoptweening;decelerate,0.1;zoom,0.8;
#
IconChoiceSingleX=SCREEN_CENTER_X-160
IconChoiceSingleY=SCREEN_CENTER_Y-96
IconChoiceSingleOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoiceSingleOffCommand=linear,0.05;zoomx,0
IconChoicePosFunction=ScreenSelectStylePositions
IconChoiceOnCommand=zoom,0;bounceend,0.35;zoom,1
IconChoiceOffCommand=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]
PersistScreens="ScreenSelectPlayMode,ScreenSelectMusic"
+2 -2
View File
@@ -1300,9 +1300,9 @@ void Actor::QueueMessage( const RString& 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";
LuaHelpers::ReportScriptError(sWarning, "COMMAND_DEFINED_TWICE");
+1 -1
View File
@@ -577,7 +577,7 @@ public:
virtual void PushContext( lua_State *L );
// 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;
const apActorCommands *GetCommand( const RString &sCommandName ) const;
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[] =
{
&g_Style_Kickbox_Human,
&g_Style_Kickbox_Human_Versus,
&g_Style_Kickbox_Quadarm,
&g_Style_Kickbox_Quadarm_Versus,
&g_Style_Kickbox_Insect,
&g_Style_Kickbox_Insect_Versus,
&g_Style_Kickbox_Arachnid,
&g_Style_Kickbox_Human_Versus,
&g_Style_Kickbox_Quadarm_Versus,
&g_Style_Kickbox_Insect_Versus,
&g_Style_Kickbox_Arachnid_Versus,
NULL
};
+17 -28
View File
@@ -998,7 +998,7 @@ void GameState::SetCompatibleStylesForPlayers()
SetCurrentStyle(style, PLAYER_INVALID);
}
}
else
else if(GetCurrentStyle(PLAYER_INVALID) == NULL)
{
vector<StepsType> vst;
GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst);
@@ -1009,37 +1009,26 @@ void GameState::SetCompatibleStylesForPlayers()
}
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;
if(m_pCurSteps[pn] != NULL)
{
st= m_pCurSteps[pn]->m_StepsType;
}
else if(m_pCurTrail[pn] != NULL)
{
st= m_pCurTrail[pn]->m_StepsType;
}
else
{
vector<StepsType> vst;
GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst);
st= vst[0];
}
const Style *style = GAMEMAN->GetFirstCompatibleStyle(
m_pCurGame, GetNumSidesJoined(), st);
SetCurrentStyle(style, pn);
st= m_pCurSteps[pn]->m_StepsType;
}
else if(m_pCurTrail[pn] != NULL)
{
st= m_pCurTrail[pn]->m_StepsType;
}
else
{
vector<StepsType> vst;
GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst);
st= vst[0];
}
}
else
{
vector<StepsType> vst;
GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst);
const Style *style = GAMEMAN->GetFirstCompatibleStyle(
m_pCurGame, GetNumSidesJoined(), vst[0]);
SetCurrentStyle(style, PLAYER_INVALID);
m_pCurGame, GetNumSidesJoined(), st);
SetCurrentStyle(style, pn);
}
}
}
+40
View File
@@ -31,6 +31,46 @@ void ScreenSelect::Init()
this->SubscribeToMessage( Message_PlayerJoined );
// 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.
// 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 )
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++ )
{
GameCommand& mc = m_aGameCommands[c];
@@ -122,7 +184,26 @@ void ScreenSelectMaster::Init()
RString sName = "Icon" "Choice" + mc.m_sName;
m_vsprIcon[c]->SetName( sName );
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] );
}