Files
itgmania212121/stepmania/src/ScreenSelectMaster.cpp
T

959 lines
26 KiB
C++
Raw Normal View History

2003-06-20 23:07:45 +00:00
#include "global.h"
#include "ScreenSelectMaster.h"
#include "ScreenManager.h"
#include "GameManager.h"
#include "ThemeManager.h"
2004-07-08 00:10:34 +00:00
#include "GameSoundManager.h"
2003-06-20 23:07:45 +00:00
#include "GameState.h"
#include "AnnouncerManager.h"
2004-12-02 06:29:20 +00:00
#include "GameCommand.h"
2003-06-20 23:07:45 +00:00
#include "ActorUtil.h"
#include "RageLog.h"
2004-04-13 23:11:32 +00:00
#include <set>
#include "Foreach.h"
#include "InputEventPlus.h"
2003-06-20 23:07:45 +00:00
2006-07-11 06:51:46 +00:00
static const char *MenuDirNames[] = {
"Up",
"Down",
"Left",
"Right",
"Auto",
};
2006-10-15 00:09:18 +00:00
XToString( MenuDir );
2006-07-11 06:51:46 +00:00
AutoScreenMessage( SM_PlayPostSwitchPage )
2003-06-20 23:07:45 +00:00
2007-06-09 23:52:10 +00:00
static RString CURSOR_OFFSET_X_FROM_ICON_NAME( size_t p ) { return ssprintf("CursorP%dOffsetXFromIcon",int(p+1)); }
static RString CURSOR_OFFSET_Y_FROM_ICON_NAME( size_t p ) { return ssprintf("CursorP%dOffsetYFromIcon",int(p+1)); }
2005-02-06 05:15:57 +00:00
/* e.g. "OptionOrderLeft=0:1,1:2,2:3,3:4" */
2007-06-09 23:52:10 +00:00
static RString OPTION_ORDER_NAME( size_t dir ) { return "OptionOrder"+MenuDirToString((MenuDir)dir); }
2006-01-15 20:46:15 +00:00
REGISTER_SCREEN_CLASS( ScreenSelectMaster );
2008-11-24 04:51:00 +00:00
#define GetActiveElementPlayerNumbers( vpns ) \
if( SHARED_SELECTION ) { \
vpns.push_back( PLAYER_1 ); \
} else { \
FOREACH_HumanPlayer( p ) \
vpns.push_back( p ); \
}
2006-03-23 00:16:18 +00:00
ScreenSelectMaster::ScreenSelectMaster()
{
ZERO( m_iChoice );
ZERO( m_bChosen );
ZERO( m_bDoubleChoice );
2006-03-23 00:16:18 +00:00
}
void ScreenSelectMaster::Init()
{
2006-01-15 19:22:17 +00:00
SHOW_ICON.Load( m_sName, "ShowIcon" );
SHOW_SCROLLER.Load( m_sName, "ShowScroller" );
SHOW_CURSOR.Load( m_sName, "ShowCursor" );
SHARED_SELECTION.Load( m_sName, "SharedSelection" );
USE_ICON_METRICS.Load( m_sName, "UseIconMetrics" );
2006-01-15 19:22:17 +00:00
NUM_CHOICES_ON_PAGE_1.Load( m_sName, "NumChoicesOnPage1" );
CURSOR_OFFSET_X_FROM_ICON.Load( m_sName, CURSOR_OFFSET_X_FROM_ICON_NAME, NUM_PLAYERS );
CURSOR_OFFSET_Y_FROM_ICON.Load( m_sName, CURSOR_OFFSET_Y_FROM_ICON_NAME, NUM_PLAYERS );
PER_CHOICE_ICON_ELEMENT.Load( m_sName, "PerChoiceIconElement" );
2006-01-15 19:22:17 +00:00
PRE_SWITCH_PAGE_SECONDS.Load( m_sName, "PreSwitchPageSeconds" );
POST_SWITCH_PAGE_SECONDS.Load( m_sName, "PostSwitchPageSeconds" );
2006-07-11 06:51:46 +00:00
OPTION_ORDER.Load( m_sName, OPTION_ORDER_NAME, NUM_MenuDir );
2006-01-15 19:22:17 +00:00
WRAP_CURSOR.Load( m_sName, "WrapCursor" );
WRAP_SCROLLER.Load( m_sName, "WrapScroller" );
LOOP_SCROLLER.Load( m_sName, "LoopScroller" );
2006-01-29 00:27:39 +00:00
PER_CHOICE_SCROLL_ELEMENT.Load( m_sName, "PerChoiceScrollElement" );
2006-01-15 19:22:17 +00:00
ALLOW_REPEATING_INPUT.Load( m_sName, "AllowRepeatingInput" );
SCROLLER_SECONDS_PER_ITEM.Load( m_sName, "ScrollerSecondsPerItem" );
SCROLLER_NUM_ITEMS_TO_DRAW.Load( m_sName, "ScrollerNumItemsToDraw" );
SCROLLER_TRANSFORM.Load( m_sName, "ScrollerTransform" );
SCROLLER_SUBDIVISIONS.Load( m_sName, "ScrollerSubdivisions" );
DEFAULT_CHOICE.Load( m_sName, "DefaultChoice" );
DOUBLE_PRESS_TO_SELECT.Load(m_sName,"DoublePressToSelect");
2006-01-15 19:22:17 +00:00
ScreenSelect::Init();
2005-01-04 10:47:28 +00:00
2007-01-13 03:47:36 +00:00
m_TrackingRepeatingInput = GameButton_Invalid;
2005-06-29 09:22:24 +00:00
vector<PlayerNumber> vpns;
2008-11-24 04:51:00 +00:00
GetActiveElementPlayerNumbers( vpns );
2005-06-29 09:22:24 +00:00
2006-01-22 01:00:06 +00:00
#define PLAYER_APPEND_NO_SPACE(p) (SHARED_SELECTION ? RString() : ssprintf("P%d",(p)+1))
this->SubscribeToMessage( SM_MenuTimer );
2005-06-29 09:22:24 +00:00
// init cursor
2005-07-14 08:47:12 +00:00
if( SHOW_CURSOR )
2005-06-29 09:22:24 +00:00
{
FOREACH( PlayerNumber, vpns, p )
2003-06-20 23:07:45 +00:00
{
2007-05-04 04:40:17 +00:00
RString sElement = "Cursor" + PLAYER_APPEND_NO_SPACE(*p);
2005-07-14 20:15:37 +00:00
m_sprCursor[*p].Load( THEME->GetPathG(m_sName,sElement) );
sElement.Replace( " ", "" );
m_sprCursor[*p]->SetName( sElement );
2005-07-14 08:47:12 +00:00
this->AddChild( m_sprCursor[*p] );
2007-05-04 04:52:31 +00:00
LOAD_ALL_COMMANDS( m_sprCursor[*p] );
2003-06-20 23:07:45 +00:00
}
}
2005-10-04 02:48:33 +00:00
// Resize vectors depending on how many choices there are
m_vsprIcon.resize( m_aGameCommands.size() );
FOREACH( PlayerNumber, vpns, p )
m_vsprScroll[*p].resize( m_aGameCommands.size() );
2004-12-02 06:29:20 +00:00
for( unsigned c=0; c<m_aGameCommands.size(); c++ )
2003-06-20 23:07:45 +00:00
{
2005-07-14 20:15:37 +00:00
GameCommand& mc = m_aGameCommands[c];
2007-05-27 19:09:18 +00:00
LuaThreadVariable var( "GameCommand", LuaReference::Create(&mc) );
2003-06-20 23:07:45 +00:00
2004-01-17 23:14:56 +00:00
// init icon
2005-07-14 08:47:12 +00:00
if( SHOW_ICON )
2003-06-20 23:07:45 +00:00
{
vector<RString> vs;
vs.push_back( "Icon" );
if( PER_CHOICE_ICON_ELEMENT )
vs.push_back( "Choice" + mc.m_sName );
RString sElement = join( " ", vs );
2005-10-04 02:48:33 +00:00
m_vsprIcon[c].Load( THEME->GetPathG(m_sName,sElement) );
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] );
2005-10-04 02:48:33 +00:00
this->AddChild( m_vsprIcon[c] );
2003-06-20 23:07:45 +00:00
}
2005-07-14 20:15:37 +00:00
// init scroll
if( SHOW_SCROLLER )
2003-06-20 23:07:45 +00:00
{
2005-07-14 08:47:12 +00:00
FOREACH( PlayerNumber, vpns, p )
2003-06-20 23:07:45 +00:00
{
2006-01-29 00:27:39 +00:00
vector<RString> vs;
vs.push_back( "Scroll" );
2006-01-29 00:27:39 +00:00
if( PER_CHOICE_SCROLL_ELEMENT )
vs.push_back( "Choice" + mc.m_sName );
2006-01-29 00:27:39 +00:00
if( !SHARED_SELECTION )
vs.push_back( PLAYER_APPEND_NO_SPACE(*p) );
RString sElement = join( " ", vs );
2005-10-04 02:48:33 +00:00
m_vsprScroll[*p][c].Load( THEME->GetPathG(m_sName,sElement) );
RString sName = "Scroll" "Choice" + mc.m_sName;
if( !SHARED_SELECTION )
sName += PLAYER_APPEND_NO_SPACE(*p);
m_vsprScroll[*p][c]->SetName( sName );
2005-10-04 02:48:33 +00:00
m_Scroller[*p].AddChild( m_vsprScroll[*p][c] );
2003-06-20 23:07:45 +00:00
}
2003-06-20 23:07:45 +00:00
}
}
2005-09-27 05:40:09 +00:00
// init scroll
if( SHOW_SCROLLER )
{
FOREACH( PlayerNumber, vpns, p )
{
2006-08-16 18:19:38 +00:00
m_Scroller[*p].SetLoop( LOOP_SCROLLER );
2006-08-16 18:31:00 +00:00
m_Scroller[*p].SetNumItemsToDraw( SCROLLER_NUM_ITEMS_TO_DRAW );
m_Scroller[*p].Load2();
m_Scroller[*p].SetTransformFromReference( SCROLLER_TRANSFORM );
2005-12-07 03:53:49 +00:00
m_Scroller[*p].SetSecondsPerItem( SCROLLER_SECONDS_PER_ITEM );
m_Scroller[*p].SetNumSubdivisions( SCROLLER_SUBDIVISIONS );
2005-09-27 05:40:09 +00:00
m_Scroller[*p].SetName( "Scroller"+PLAYER_APPEND_NO_SPACE(*p) );
LOAD_ALL_COMMANDS_AND_SET_XY( m_Scroller[*p] );
2005-09-27 05:40:09 +00:00
this->AddChild( &m_Scroller[*p] );
}
}
FOREACH_ENUM( Page, page )
2003-06-20 23:07:45 +00:00
{
2005-02-06 03:32:53 +00:00
m_sprMore[page].Load( THEME->GetPathG(m_sName, ssprintf("more page%d",page+1)) );
2005-01-04 10:47:28 +00:00
m_sprMore[page]->SetName( ssprintf("MorePage%d",page+1) );
LOAD_ALL_COMMANDS_AND_SET_XY( m_sprMore[page] );
2005-01-04 10:47:28 +00:00
this->AddChild( m_sprMore[page] );
2003-06-20 23:07:45 +00:00
2005-02-06 03:32:53 +00:00
m_sprExplanation[page].Load( THEME->GetPathG(m_sName, ssprintf("explanation page%d",page+1)) );
2003-11-05 19:35:57 +00:00
m_sprExplanation[page]->SetName( ssprintf("ExplanationPage%d",page+1) );
LOAD_ALL_COMMANDS_AND_SET_XY( m_sprExplanation[page] );
2003-11-05 19:35:57 +00:00
this->AddChild( m_sprExplanation[page] );
2003-06-20 23:07:45 +00:00
}
2005-02-06 03:32:53 +00:00
m_soundChange.Load( THEME->GetPathS(m_sName,"change"), true );
2003-06-20 23:07:45 +00:00
m_soundDifficult.Load( ANNOUNCER->GetPathTo("select difficulty challenge") );
2005-02-06 03:32:53 +00:00
m_soundStart.Load( THEME->GetPathS(m_sName,"start") );
2003-06-20 23:07:45 +00:00
2004-01-17 23:14:56 +00:00
// init m_Next order info
2005-02-06 05:15:57 +00:00
FOREACH_MenuDir( dir )
2003-11-12 02:53:28 +00:00
{
2006-01-22 01:00:06 +00:00
const RString order = OPTION_ORDER.GetValue( dir );
vector<RString> parts;
2003-11-12 02:53:28 +00:00
split( order, ",", parts, true );
for( unsigned part = 0; part < parts.size(); ++part )
{
2005-10-04 02:48:33 +00:00
int from, to;
if( sscanf( parts[part], "%d:%d", &from, &to ) != 2 )
2003-11-12 02:53:28 +00:00
{
2005-02-06 05:15:57 +00:00
LOG->Warn( "%s::OptionOrder%s parse error", m_sName.c_str(), MenuDirToString(dir).c_str() );
2003-11-12 02:53:28 +00:00
continue;
}
--from;
--to;
m_mapCurrentChoiceToNextChoice[dir][from] = to;
}
if( m_mapCurrentChoiceToNextChoice[dir].empty() ) // Didn't specify any mappings
{
// Fill with reasonable defaults
for( unsigned c = 0; c < m_aGameCommands.size(); ++c )
2003-11-12 02:53:28 +00:00
{
int add;
switch( dir )
{
2006-07-11 06:51:46 +00:00
case MenuDir_Up:
case MenuDir_Left:
add = -1;
break;
default:
add = +1;
break;
}
m_mapCurrentChoiceToNextChoice[dir][c] = c + add;
2006-07-11 06:51:46 +00:00
/* Always wrap around MenuDir_Auto. */
if( dir == MenuDir_Auto || (bool)WRAP_CURSOR )
wrap( m_mapCurrentChoiceToNextChoice[dir][c], m_aGameCommands.size() );
else
m_mapCurrentChoiceToNextChoice[dir][c] = clamp( m_mapCurrentChoiceToNextChoice[dir][c], 0, (int)m_aGameCommands.size()-1 );
2003-11-12 02:53:28 +00:00
}
}
}
2009-04-29 21:11:54 +00:00
m_bDoubleChoiceNoSound = false;
2005-09-21 17:23:11 +00:00
}
2006-05-07 09:25:48 +00:00
RString ScreenSelectMaster::GetDefaultChoice()
{
2006-05-07 10:02:27 +00:00
return DEFAULT_CHOICE.GetValue();
2006-05-07 09:25:48 +00:00
}
2005-09-21 17:23:11 +00:00
void ScreenSelectMaster::BeginScreen()
{
// TODO: Move default choice to ScreenSelect
int iDefaultChoice = -1;
for( unsigned c=0; c<m_aGameCommands.size(); c++ )
{
const GameCommand& mc = m_aGameCommands[c];
2006-01-22 01:00:06 +00:00
if( mc.m_sName == (RString) DEFAULT_CHOICE )
2005-09-21 17:23:11 +00:00
{
iDefaultChoice = c;
break;
}
}
FOREACH_PlayerNumber( p )
{
m_iChoice[p] = (iDefaultChoice!=-1) ? iDefaultChoice : 0;
CLAMP( m_iChoice[p], 0, (int)m_aGameCommands.size()-1 );
m_bChosen[p] = false;
m_bDoubleChoice[p] = false;
2005-09-21 17:23:11 +00:00
}
if( !SHARED_SELECTION )
2005-09-21 17:23:11 +00:00
{
FOREACH_ENUM( PlayerNumber, pn )
{
if( GAMESTATE->IsHumanPlayer(pn) )
continue;
if( SHOW_CURSOR )
m_sprCursor[pn]->SetVisible( false );
if( SHOW_SCROLLER )
m_Scroller[pn].SetVisible( false );
}
2005-09-21 17:23:11 +00:00
}
this->UpdateSelectableChoices();
ScreenSelect::BeginScreen();
// Call GetTweenTimeLeft after the base BeginScreen has started the in Transition.
m_fLockInputSecs = this->GetTweenTimeLeft();
2003-06-20 23:07:45 +00:00
}
void ScreenSelectMaster::HandleScreenMessage( const ScreenMessage SM )
{
ScreenSelect::HandleScreenMessage( SM );
2005-06-29 09:22:24 +00:00
vector<PlayerNumber> vpns;
2008-11-24 04:51:00 +00:00
GetActiveElementPlayerNumbers( vpns );
2005-06-29 09:22:24 +00:00
if( SM == SM_PlayPostSwitchPage )
2003-06-20 23:07:45 +00:00
{
int iNewChoice = m_iChoice[ GAMESTATE->m_MasterPlayerNumber ];
Page newPage = GetPage( iNewChoice );
Message msg("PostSwitchPage");
msg.SetParam( "NewPageIndex", (int)newPage );
2005-07-14 08:47:12 +00:00
if( SHOW_CURSOR )
2003-06-20 23:07:45 +00:00
{
2005-06-29 09:22:24 +00:00
FOREACH( PlayerNumber, vpns, p )
m_sprCursor[*p]->HandleMessage( msg );
}
2003-06-20 23:07:45 +00:00
2005-07-14 20:15:37 +00:00
if( SHOW_SCROLLER )
2004-02-01 03:14:37 +00:00
{
2005-06-29 09:22:24 +00:00
FOREACH( PlayerNumber, vpns, p )
{
int iChoice = m_iChoice[*p];
m_vsprScroll[*p][iChoice]->HandleMessage( msg );
2005-06-29 09:22:24 +00:00
}
2004-02-01 03:14:37 +00:00
}
2005-06-29 09:22:24 +00:00
m_fLockInputSecs = POST_SWITCH_PAGE_SECONDS;
}
else if( SM == SM_MenuTimer )
{
if(DOUBLE_PRESS_TO_SELECT)
{
FOREACH_HumanPlayer(p)
{
2009-04-29 21:11:54 +00:00
m_bDoubleChoiceNoSound = true;
m_bDoubleChoice[p] = true;
InputEventPlus iep;
iep.pn = p;
MenuStart( iep );
}
}
}
2003-06-20 23:07:45 +00:00
}
int ScreenSelectMaster::GetSelectionIndex( PlayerNumber pn )
{
return m_iChoice[pn];
}
void ScreenSelectMaster::UpdateSelectableChoices()
{
2005-06-29 09:22:24 +00:00
vector<PlayerNumber> vpns;
2008-11-24 04:51:00 +00:00
GetActiveElementPlayerNumbers( vpns );
2005-06-29 09:22:24 +00:00
2004-12-02 06:29:20 +00:00
for( unsigned c=0; c<m_aGameCommands.size(); c++ )
{
2005-07-14 08:47:12 +00:00
if( SHOW_ICON )
2005-10-04 02:48:33 +00:00
m_vsprIcon[c]->PlayCommand( m_aGameCommands[c].IsPlayable()? "Enabled":"Disabled" );
2005-06-29 09:22:24 +00:00
FOREACH( PlayerNumber, vpns, p )
2005-10-04 02:48:33 +00:00
if( m_vsprScroll[*p][c].IsLoaded() )
m_vsprScroll[*p][c]->PlayCommand( m_aGameCommands[c].IsPlayable()? "Enabled":"Disabled" );
}
2003-06-20 23:07:45 +00:00
2005-03-10 03:53:00 +00:00
/*
* If no options are playable at all, just wait. Some external
* stimulus may make options available (such as coin insertion).
*
* If any options are playable, make sure one is selected.
*/
2005-01-04 10:47:28 +00:00
FOREACH_HumanPlayer( p )
2004-12-02 06:29:20 +00:00
if( !m_aGameCommands[m_iChoice[p]].IsPlayable() )
2006-07-11 06:51:46 +00:00
Move( p, MenuDir_Auto );
2005-03-10 03:53:00 +00:00
}
bool ScreenSelectMaster::AnyOptionsArePlayable() const
{
for( unsigned i = 0; i < m_aGameCommands.size(); ++i )
if( m_aGameCommands[i].IsPlayable() )
2005-03-10 03:53:00 +00:00
return true;
2005-03-10 03:53:00 +00:00
return false;
}
2005-02-06 05:15:57 +00:00
bool ScreenSelectMaster::Move( PlayerNumber pn, MenuDir dir )
{
2005-03-10 03:53:00 +00:00
if( !AnyOptionsArePlayable() )
return false;
int iSwitchToIndex = m_iChoice[pn];
2004-04-13 23:11:32 +00:00
set<int> seen;
try_again:
map<int,int>::const_iterator iter = m_mapCurrentChoiceToNextChoice[dir].find( iSwitchToIndex );
if( iter != m_mapCurrentChoiceToNextChoice[dir].end() )
iSwitchToIndex = iter->second;
if( iSwitchToIndex < 0 || iSwitchToIndex >= (int) m_aGameCommands.size() ) // out of choice range
return false; // can't go that way
if( seen.find(iSwitchToIndex) != seen.end() )
return false; // went full circle and none found
seen.insert( iSwitchToIndex );
if( !m_aGameCommands[iSwitchToIndex].IsPlayable() )
goto try_again;
return ChangeSelection( pn, dir, iSwitchToIndex );
2003-06-20 23:07:45 +00:00
}
void ScreenSelectMaster::MenuLeft( const InputEventPlus &input )
2003-06-20 23:07:45 +00:00
{
2006-09-14 03:18:16 +00:00
PlayerNumber pn = input.pn;
2003-11-12 02:53:28 +00:00
if( m_fLockInputSecs > 0 || m_bChosen[pn] )
2003-06-20 23:07:45 +00:00
return;
if( input.type == IET_RELEASE )
2005-04-04 02:34:56 +00:00
return;
if( input.type != IET_FIRST_PRESS )
{
if( !ALLOW_REPEATING_INPUT )
return;
if( m_TrackingRepeatingInput != input.MenuI )
return;
}
2006-07-11 06:51:46 +00:00
if( Move(pn, MenuDir_Left) )
2005-05-02 21:33:02 +00:00
{
m_TrackingRepeatingInput = input.MenuI;
m_soundChange.Play();
2006-11-13 22:36:39 +00:00
MESSAGEMAN->Broadcast( (MessageID)(Message_MenuLeftP1+pn) );
MESSAGEMAN->Broadcast( (MessageID)(Message_MenuSelectionChanged) );
// if they use double select
if(DOUBLE_PRESS_TO_SELECT)
{
m_bDoubleChoice[pn] = false; // player has cancelled their selection
}
2005-05-02 21:33:02 +00:00
}
2003-06-20 23:07:45 +00:00
}
void ScreenSelectMaster::MenuRight( const InputEventPlus &input )
2003-06-20 23:07:45 +00:00
{
2006-09-14 03:18:16 +00:00
PlayerNumber pn = input.pn;
2003-11-12 02:53:28 +00:00
if( m_fLockInputSecs > 0 || m_bChosen[pn] )
return;
if( input.type == IET_RELEASE )
2005-04-04 02:34:56 +00:00
return;
if( input.type != IET_FIRST_PRESS )
{
if( !ALLOW_REPEATING_INPUT )
return;
if( m_TrackingRepeatingInput != input.MenuI )
return;
}
2006-07-11 06:51:46 +00:00
if( Move(pn, MenuDir_Right) )
2005-05-02 21:33:02 +00:00
{
m_TrackingRepeatingInput = input.MenuI;
2003-11-12 02:53:28 +00:00
m_soundChange.Play();
2006-11-13 22:36:39 +00:00
MESSAGEMAN->Broadcast( (MessageID)(Message_MenuRightP1+pn) );
MESSAGEMAN->Broadcast( (MessageID)(Message_MenuSelectionChanged) );
// if they use double select
if(DOUBLE_PRESS_TO_SELECT)
{
m_bDoubleChoice[pn] = false; // player has cancelled their selection
}
2005-05-02 21:33:02 +00:00
}
2003-11-12 02:53:28 +00:00
}
void ScreenSelectMaster::MenuUp( const InputEventPlus &input )
2003-11-12 02:53:28 +00:00
{
2006-09-14 03:18:16 +00:00
PlayerNumber pn = input.pn;
2003-11-12 02:53:28 +00:00
if( m_fLockInputSecs > 0 || m_bChosen[pn] )
2003-06-20 23:07:45 +00:00
return;
if( input.type == IET_RELEASE )
2005-04-04 02:34:56 +00:00
return;
if( input.type != IET_FIRST_PRESS )
{
if( !ALLOW_REPEATING_INPUT )
return;
if( m_TrackingRepeatingInput != input.MenuI )
return;
}
2006-07-11 06:51:46 +00:00
if( Move(pn, MenuDir_Up) )
2005-05-02 21:33:02 +00:00
{
m_TrackingRepeatingInput = input.MenuI;
2003-11-12 02:53:28 +00:00
m_soundChange.Play();
2006-11-13 22:36:39 +00:00
MESSAGEMAN->Broadcast( (MessageID)(Message_MenuUpP1+pn) );
MESSAGEMAN->Broadcast( (MessageID)(Message_MenuSelectionChanged) );
// if they use double select
if(DOUBLE_PRESS_TO_SELECT)
{
m_bDoubleChoice[pn] = false; // player has cancelled their selection
}
2005-05-02 21:33:02 +00:00
}
2003-11-12 02:53:28 +00:00
}
void ScreenSelectMaster::MenuDown( const InputEventPlus &input )
2003-11-12 02:53:28 +00:00
{
2006-09-14 03:18:16 +00:00
PlayerNumber pn = input.pn;
2003-11-12 02:53:28 +00:00
if( m_fLockInputSecs > 0 || m_bChosen[pn] )
2003-06-20 23:07:45 +00:00
return;
if( input.type == IET_RELEASE )
2005-04-04 02:34:56 +00:00
return;
if( input.type != IET_FIRST_PRESS )
{
if( !ALLOW_REPEATING_INPUT )
return;
if( m_TrackingRepeatingInput != input.MenuI )
return;
}
2006-07-11 06:51:46 +00:00
if( Move(pn, MenuDir_Down) )
2005-05-02 21:33:02 +00:00
{
m_TrackingRepeatingInput = input.MenuI;
m_soundChange.Play();
2006-11-13 22:36:39 +00:00
MESSAGEMAN->Broadcast( (MessageID)(Message_MenuDownP1+pn) );
MESSAGEMAN->Broadcast( (MessageID)(Message_MenuSelectionChanged) );
// if they use double select
if(DOUBLE_PRESS_TO_SELECT)
{
m_bDoubleChoice[pn] = false; // player has cancelled their selection
}
2005-05-02 21:33:02 +00:00
}
2003-06-20 23:07:45 +00:00
}
bool ScreenSelectMaster::ChangePage( int iNewChoice )
2003-06-20 23:07:45 +00:00
{
Page oldPage = GetPage( m_iChoice[GAMESTATE->m_MasterPlayerNumber] );
Page newPage = GetPage( iNewChoice );
2003-06-20 23:07:45 +00:00
// If anyone has already chosen, don't allow changing of pages
FOREACH_PlayerNumber( p )
2005-06-29 09:22:24 +00:00
{
2003-06-20 23:07:45 +00:00
if( GAMESTATE->IsHumanPlayer(p) && m_bChosen[p] )
return false;
2005-06-29 09:22:24 +00:00
}
// change both players
FOREACH_PlayerNumber( p )
m_iChoice[p] = iNewChoice;
const RString sIconAndExplanationCommand = ssprintf( "SwitchToPage%d", newPage+1 );
if( SHOW_ICON )
for( unsigned c = 0; c < m_aGameCommands.size(); ++c )
m_vsprIcon[c]->PlayCommand( sIconAndExplanationCommand );
FOREACH_ENUM( Page, page )
{
m_sprExplanation[page]->PlayCommand( sIconAndExplanationCommand );
m_sprMore[page]->PlayCommand( sIconAndExplanationCommand );
}
2003-06-20 23:07:45 +00:00
2005-06-29 09:22:24 +00:00
vector<PlayerNumber> vpns;
2008-11-24 04:51:00 +00:00
GetActiveElementPlayerNumbers( vpns );
2003-06-20 23:07:45 +00:00
Message msg("PreSwitchPage");
msg.SetParam( "OldPageIndex", (int)oldPage );
msg.SetParam( "NewPageIndex", (int)newPage );
FOREACH( PlayerNumber, vpns, p )
2003-06-20 23:07:45 +00:00
{
if( SHOW_CURSOR )
2005-07-14 08:47:12 +00:00
{
m_sprCursor[*p]->HandleMessage( msg );
m_sprCursor[*p]->SetXY( GetCursorX(*p), GetCursorY(*p) );
2005-07-14 08:47:12 +00:00
}
if( SHOW_SCROLLER )
m_vsprScroll[*p][m_iChoice[*p]]->HandleMessage( msg );
2003-06-20 23:07:45 +00:00
}
if( newPage == PAGE_2 )
{
// XXX: only play this once (I thought we already did that?)
// Play it on every change to page 2. -Chris
2003-11-04 20:57:19 +00:00
/* That sounds ugly if you go back and forth quickly. -g */
// Should we lock input while it's scrolling? -Chris
2003-06-20 23:07:45 +00:00
m_soundDifficult.Stop();
m_soundDifficult.PlayRandom();
}
m_fLockInputSecs = PRE_SWITCH_PAGE_SECONDS;
this->PostScreenMessage( SM_PlayPostSwitchPage, GetTweenTimeLeft() );
return true;
2003-06-20 23:07:45 +00:00
}
bool ScreenSelectMaster::ChangeSelection( PlayerNumber pn, MenuDir dir, int iNewChoice )
2003-06-20 23:07:45 +00:00
{
2004-04-13 23:11:32 +00:00
if( iNewChoice == m_iChoice[pn] )
return false; // already there
Page page = GetPage( iNewChoice );
if( GetPage(m_iChoice[pn]) != page )
return ChangePage( iNewChoice );
vector<PlayerNumber> vpns;
if( SHARED_SELECTION || page != PAGE_1 )
{
/* Set the new m_iChoice even for disabled players, since a player might
* join on a SHARED_SELECTION after the cursor has been moved. */
2007-06-18 10:53:37 +00:00
FOREACH_ENUM( PlayerNumber, p )
vpns.push_back( p );
}
else
{
vpns.push_back( pn );
}
FOREACH( PlayerNumber, vpns, p )
{
const int iOldChoice = m_iChoice[*p];
m_iChoice[*p] = iNewChoice;
2004-01-21 02:11:23 +00:00
2005-07-14 08:47:12 +00:00
if( SHOW_ICON )
2003-11-05 04:09:10 +00:00
{
/* XXX: If !SharedPreviewAndCursor, this is incorrect. (Nothing uses
* both icon focus and !SharedPreviewAndCursor right now.)
* What is SharedPreviewAndCursor? -- Steve */
bool bOldStillHasFocus = false;
bool bNewAlreadyHadFocus = false;
FOREACH_HumanPlayer( p2 )
{
if( p2 == *p )
continue;
bOldStillHasFocus |= m_iChoice[p2] == iOldChoice;
bNewAlreadyHadFocus |= m_iChoice[p2] == iNewChoice;
}
if(DOUBLE_PRESS_TO_SELECT)
{
// this player is currently on a single press, which they are cancelling
if(m_bDoubleChoice[pn])
{
if( !bOldStillHasFocus )
m_vsprIcon[iOldChoice]->PlayCommand( "LoseFocus" );
if( !bNewAlreadyHadFocus )
m_vsprIcon[iNewChoice]->PlayCommand( "GainFocus" );
}
else
{
if( !bOldStillHasFocus )
m_vsprIcon[iOldChoice]->PlayCommand( "LoseFocus" );
if( !bNewAlreadyHadFocus )
m_vsprIcon[iNewChoice]->PlayCommand( "GainFocus" );
}
}
else // not using double selection
{
if( !bOldStillHasFocus )
m_vsprIcon[iOldChoice]->PlayCommand( "LostSelectedLoseFocus" );
if( !bNewAlreadyHadFocus )
m_vsprIcon[iNewChoice]->PlayCommand( "LostSelectedGainFocus" );
}
2003-11-05 04:09:10 +00:00
}
2005-07-14 20:15:37 +00:00
if( SHOW_CURSOR )
2003-06-20 23:07:45 +00:00
{
m_sprCursor[*p]->PlayCommand( "Change" );
m_sprCursor[*p]->SetXY( GetCursorX(*p), GetCursorY(*p) );
2003-06-20 23:07:45 +00:00
}
2004-01-17 23:14:56 +00:00
if( SHOW_SCROLLER )
{
2007-06-18 10:53:37 +00:00
ActorScroller &scroller = SHARED_SELECTION ? m_Scroller[0] : m_Scroller[*p];
vector<AutoActor> &vScroll = SHARED_SELECTION ? m_vsprScroll[0] : m_vsprScroll[*p];
if( WRAP_SCROLLER )
{
// HACK: We can't tell from the option orders whether or not we wrapped.
// For now, assume that the order is increasing left to right.
2006-07-11 06:51:46 +00:00
int iPressedDir = (dir == MenuDir_Left) ? -1 : +1;
int iActualDir = (iOldChoice < iNewChoice) ? +1 : -1;
if( iPressedDir != iActualDir ) // wrapped
{
float fItem = scroller.GetCurrentItem();
int iNumChoices = m_aGameCommands.size();
fItem += iActualDir * iNumChoices;
scroller.SetCurrentAndDestinationItem( fItem );
}
}
2007-06-18 10:53:37 +00:00
scroller.SetDestinationItem( (float)iNewChoice );
2005-07-14 20:15:37 +00:00
// using double selections
if(DOUBLE_PRESS_TO_SELECT)
{
// this player is currently on a single press, which they are cancelling
if(m_bDoubleChoice[pn])
{
vScroll[iOldChoice]->PlayCommand( "LostSelectedLoseFocus" );
vScroll[iNewChoice]->PlayCommand( "LostSelectedGainFocus" );
}
else // the player hasn't made any selections yet
{
vScroll[iOldChoice]->PlayCommand( "LoseFocus" );
vScroll[iNewChoice]->PlayCommand( "GainFocus" );
}
}
else // regular lose/gain focus
{
vScroll[iOldChoice]->PlayCommand( "LoseFocus" );
vScroll[iNewChoice]->PlayCommand( "GainFocus" );
}
2004-01-17 23:14:56 +00:00
}
2003-06-20 23:07:45 +00:00
}
return true;
2003-06-20 23:07:45 +00:00
}
PlayerNumber ScreenSelectMaster::GetSharedPlayer()
{
if( GAMESTATE->m_MasterPlayerNumber != PLAYER_INVALID )
return GAMESTATE->m_MasterPlayerNumber;
return PLAYER_1;
}
ScreenSelectMaster::Page ScreenSelectMaster::GetPage( int iChoiceIndex ) const
2003-06-20 23:07:45 +00:00
{
return iChoiceIndex < NUM_CHOICES_ON_PAGE_1? PAGE_1:PAGE_2;
2003-06-20 23:07:45 +00:00
}
ScreenSelectMaster::Page ScreenSelectMaster::GetCurrentPage() const
2003-06-20 23:07:45 +00:00
{
// Both players are guaranteed to be on the same page.
return GetPage( m_iChoice[GetSharedPlayer()] );
2003-06-20 23:07:45 +00:00
}
2004-02-01 05:03:45 +00:00
float ScreenSelectMaster::DoMenuStart( PlayerNumber pn )
2003-06-20 23:07:45 +00:00
{
2004-09-06 21:28:56 +00:00
if( m_bChosen[pn] )
2004-02-01 05:03:45 +00:00
return 0;
bool bAnyChosen = false;
FOREACH_PlayerNumber( p )
bAnyChosen |= m_bChosen[p];
2003-06-20 23:07:45 +00:00
m_bChosen[pn] = true;
this->PlayCommand( "MadeChoice"+PlayerNumberToString(pn) );
2005-05-04 09:58:12 +00:00
bool bIsFirstToChoose = bAnyChosen;
2004-02-01 05:03:45 +00:00
float fSecs = 0;
if( bIsFirstToChoose )
2004-02-01 05:03:45 +00:00
{
FOREACH_ENUM( Page, page )
{
m_sprMore[page]->PlayCommand( "Off" );
fSecs = max( fSecs, m_sprMore[page]->GetTweenTimeLeft() );
}
}
if( SHOW_CURSOR )
{
m_sprCursor[pn]->PlayCommand( "Choose" );
fSecs = max( fSecs, m_sprCursor[pn]->GetTweenTimeLeft() );
2004-02-01 05:03:45 +00:00
}
return fSecs;
}
2003-06-20 23:07:45 +00:00
2006-09-15 01:13:39 +00:00
void ScreenSelectMaster::MenuStart( const InputEventPlus &input )
{
2006-09-15 01:13:39 +00:00
if( input.type != IET_FIRST_PRESS )
return;
PlayerNumber pn = input.pn;
if( m_fLockInputSecs > 0 )
return;
2004-09-06 21:28:56 +00:00
if( m_bChosen[pn] )
return;
if( !ProcessMenuStart( pn ) )
return;
// double press is enabled and the player hasn't made their first press
if(DOUBLE_PRESS_TO_SELECT && !m_bDoubleChoice[pn])
{
m_soundStart.PlayCopy();
m_bDoubleChoice[pn] = true;
if(SHOW_SCROLLER)
{
vector<AutoActor> &vScroll = SHARED_SELECTION ? m_vsprScroll[0] : m_vsprScroll[pn];
vScroll[m_iChoice[pn]]->PlayCommand( "InitialSelection" );
}
return;
}
2005-07-25 04:47:51 +00:00
const GameCommand &mc = m_aGameCommands[m_iChoice[pn]];
2005-03-10 03:53:00 +00:00
/* If no options are playable, then we're just waiting for one to become available.
* If any options are playable, then the selection must be playable. */
if( !AnyOptionsArePlayable() )
return;
SOUND->PlayOnceFromDir( ANNOUNCER->GetPathTo(ssprintf("%s comment %s",m_sName.c_str(), mc.m_sName.c_str())) );
2005-01-08 09:50:59 +00:00
2005-01-08 16:59:59 +00:00
/* Play a copy of the sound, so it'll finish playing even if we leave the screen immediately. */
if( mc.m_sSoundPath.empty() && !m_bDoubleChoiceNoSound )
2007-01-18 08:43:26 +00:00
m_soundStart.PlayCopy();
2005-01-08 09:50:59 +00:00
if( mc.m_sScreen.empty() )
{
mc.ApplyToAllPlayers();
return;
}
2005-07-25 04:47:51 +00:00
2004-02-01 05:03:45 +00:00
float fSecs = 0;
bool bAllDone = true;
2005-07-14 21:51:22 +00:00
if( (bool)SHARED_SELECTION || GetCurrentPage() == PAGE_2 )
{
/* Only one player has to pick. Choose this for all the other players, too. */
FOREACH_PlayerNumber( p )
2004-07-28 16:05:39 +00:00
{
ASSERT( !m_bChosen[p] );
fSecs = max( fSecs, DoMenuStart(p) ); // no harm in calling this for an unjoined player
2004-07-28 16:05:39 +00:00
}
2004-02-01 03:14:37 +00:00
}
else
{
2004-02-01 05:03:45 +00:00
fSecs = max( fSecs, DoMenuStart(pn) );
// check to see if everyone has chosen
2004-07-28 16:05:39 +00:00
FOREACH_HumanPlayer( p )
bAllDone &= m_bChosen[p];
}
2003-06-20 23:07:45 +00:00
if( bAllDone )
2004-02-01 05:03:45 +00:00
this->PostScreenMessage( SM_BeginFadingOut, fSecs );// tell our owner it's time to move on
2003-06-20 23:07:45 +00:00
}
/*
* We want all items to always run OnCommand and either GainFocus or LoseFocus on
* tween-in. If we only run OnCommand, then it has to contain a copy of either
* GainFocus or LoseFocus, which implies that the default setting is hard-coded in
* the theme. Always run both.
*
* However, the actual tween-in is OnCommand; we don't always want to actually run
* through the Gain/LoseFocus tweens during initial tween-in. So, we run the focus
* command first, do a FinishTweening to pop it in place, and then run OnCommand.
* This means that the focus command should be position neutral; eg. only use "addx",
* not "x".
*/
2005-10-14 03:11:50 +00:00
void ScreenSelectMaster::TweenOnScreen()
2003-06-20 23:07:45 +00:00
{
2005-06-29 09:22:24 +00:00
vector<PlayerNumber> vpns;
2008-11-24 04:51:00 +00:00
GetActiveElementPlayerNumbers( vpns );
2005-06-29 09:22:24 +00:00
2005-07-15 01:51:05 +00:00
if( SHOW_ICON )
2003-06-20 23:07:45 +00:00
{
2005-07-14 20:15:37 +00:00
for( unsigned c=0; c<m_aGameCommands.size(); c++ )
2003-11-05 04:09:10 +00:00
{
2005-10-04 02:48:33 +00:00
m_vsprIcon[c]->PlayCommand( (int(c) == m_iChoice[0])? "GainFocus":"LoseFocus" );
m_vsprIcon[c]->FinishTweening();
2003-11-05 04:09:10 +00:00
}
2003-06-20 23:07:45 +00:00
}
2004-01-17 23:14:56 +00:00
if( SHOW_SCROLLER )
{
2005-06-29 09:22:24 +00:00
FOREACH( PlayerNumber, vpns, p )
2004-01-17 23:14:56 +00:00
{
2005-06-29 09:22:24 +00:00
// Play Gain/LoseFocus before playing the on command. Gain/Lose will
// often stop tweening, which ruins the OnCommand.
2004-12-02 06:29:20 +00:00
for( unsigned c=0; c<m_aGameCommands.size(); c++ )
2005-07-14 20:15:37 +00:00
{
2005-10-04 02:48:33 +00:00
m_vsprScroll[*p][c]->PlayCommand( int(c) == m_iChoice[*p]? "GainFocus":"LoseFocus" );
m_vsprScroll[*p][c]->FinishTweening();
2005-07-14 20:15:37 +00:00
}
2005-06-29 09:22:24 +00:00
m_Scroller[*p].SetCurrentAndDestinationItem( (float)m_iChoice[*p] );
2004-01-17 23:14:56 +00:00
}
}
2003-06-20 23:07:45 +00:00
2005-07-15 01:51:05 +00:00
// Need to SetXY of Cursor after Icons since it depends on the Icons' positions.
if( SHOW_CURSOR )
{
FOREACH( PlayerNumber, vpns, p )
{
m_sprCursor[*p]->SetXY( GetCursorX(*p), GetCursorY(*p) );
}
}
2005-10-14 03:18:30 +00:00
ScreenSelect::TweenOnScreen();
2003-06-20 23:07:45 +00:00
}
void ScreenSelectMaster::TweenOffScreen()
2003-06-20 23:07:45 +00:00
{
ScreenSelect::TweenOffScreen();
2005-06-29 09:22:24 +00:00
vector<PlayerNumber> vpns;
2008-11-24 04:51:00 +00:00
GetActiveElementPlayerNumbers( vpns );
2003-06-20 23:07:45 +00:00
2004-12-02 06:29:20 +00:00
for( unsigned c=0; c<m_aGameCommands.size(); c++ )
2003-06-20 23:07:45 +00:00
{
if( GetPage(c) != GetCurrentPage() )
continue; // skip
bool bSelectedByEitherPlayer = false;
2005-06-29 09:22:24 +00:00
FOREACH( PlayerNumber, vpns, p )
2003-11-24 03:59:09 +00:00
{
2005-06-29 09:22:24 +00:00
if( m_iChoice[*p] == (int)c )
bSelectedByEitherPlayer = true;
2003-11-24 03:59:09 +00:00
}
2003-11-05 21:34:14 +00:00
2005-07-14 08:47:12 +00:00
if( SHOW_ICON )
m_vsprIcon[c]->PlayCommand( bSelectedByEitherPlayer? "OffFocused":"OffUnfocused" );
2005-07-14 20:15:37 +00:00
if( SHOW_SCROLLER )
2003-06-20 23:07:45 +00:00
{
2005-06-29 09:22:24 +00:00
FOREACH( PlayerNumber, vpns, p )
m_vsprScroll[*p][c]->PlayCommand( bSelectedByEitherPlayer? "OffFocused":"OffUnfocused" );
2003-06-20 23:07:45 +00:00
}
}
}
// Use DestX and DestY so that the cursor can move to where the icon will be rather than where it is.
2005-07-14 08:47:12 +00:00
float ScreenSelectMaster::GetCursorX( PlayerNumber pn )
2003-06-20 23:07:45 +00:00
{
2005-06-29 09:22:24 +00:00
int iChoice = m_iChoice[pn];
AutoActor &spr = m_vsprIcon[iChoice];
return spr->GetDestX() + CURSOR_OFFSET_X_FROM_ICON.GetValue(pn);
2003-06-20 23:07:45 +00:00
}
2005-07-14 08:47:12 +00:00
float ScreenSelectMaster::GetCursorY( PlayerNumber pn )
2003-06-20 23:07:45 +00:00
{
2005-06-29 09:22:24 +00:00
int iChoice = m_iChoice[pn];
2005-10-04 02:48:33 +00:00
AutoActor &spr = m_vsprIcon[iChoice];
return spr->GetDestY() + CURSOR_OFFSET_Y_FROM_ICON.GetValue(pn);
2003-06-20 23:07:45 +00:00
}
2004-06-08 05:22:33 +00:00
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/