Files
itgmania212121/stepmania/src/DifficultyList.cpp
T

417 lines
10 KiB
C++
Raw Normal View History

2003-11-17 04:19:29 +00:00
#include "global.h"
#include "DifficultyList.h"
#include "GameState.h"
2003-11-17 04:28:19 +00:00
#include "song.h"
2003-12-18 03:40:57 +00:00
#include "Steps.h"
2004-06-28 07:26:00 +00:00
#include "Style.h"
2003-11-17 04:19:29 +00:00
#include "DifficultyMeter.h"
#include "RageLog.h"
#include "BitmapText.h"
#include "SongManager.h"
#include "ThemeManager.h"
#include "StepsUtil.h"
#include "CommonMetrics.h"
2005-03-10 19:57:43 +00:00
#include "Foreach.h"
#include "SongUtil.h"
#include "XmlFile.h"
2003-11-17 04:19:29 +00:00
2006-01-07 04:11:29 +00:00
#define MAX_METERS NUM_Difficulty + MAX_EDITS_PER_SONG
2004-04-23 00:25:42 +00:00
REGISTER_ACTOR_CLASS( DifficultyList )
DifficultyList::DifficultyList()
2003-11-17 04:19:29 +00:00
{
2004-01-11 07:12:53 +00:00
m_bShown = true;
2003-11-17 04:19:29 +00:00
}
DifficultyList::~DifficultyList()
{
}
void DifficultyList::LoadFromNode( const XNode* pNode )
2005-02-02 05:07:49 +00:00
{
ActorFrame::LoadFromNode( pNode );
2005-02-02 05:07:49 +00:00
2005-04-03 09:48:50 +00:00
ITEMS_SPACING_Y.Load( m_sName, "ItemsSpacingY" );
2005-02-02 05:07:49 +00:00
NUM_SHOWN_ITEMS.Load( m_sName, "NumShownItems" );
CAPITALIZE_DIFFICULTY_NAMES.Load( m_sName, "CapitalizeDifficultyNames" );
MOVE_COMMAND.Load( m_sName, "MoveCommand" );
2004-04-23 00:25:42 +00:00
m_Lines.resize( MAX_METERS );
2003-11-17 04:19:29 +00:00
m_CurSong = NULL;
2004-06-06 20:57:13 +00:00
FOREACH_HumanPlayer( pn )
{
const XNode *pChild = pNode->GetChild( ssprintf("CursorP%i",pn+1) );
if( pChild == NULL )
2006-10-09 08:08:59 +00:00
RageException::Throw( "%s: ComboGraph: missing the node \"CursorP%d\"", ActorUtil::GetWhere(pNode).c_str(), pn+1 );
m_Cursors[pn].LoadFromNode( pChild );
2004-06-06 20:57:13 +00:00
/* Hack: we need to tween cursors both up to down (cursor motion) and visible to
2005-04-03 09:48:50 +00:00
* invisible (fading). Cursor motion needs to stoptweening, so multiple motions
* don't queue and look unresponsive. However, that stpotweening interrupts fading,
* resulting in the cursor remaining invisible or partially invisible. So, do them
* in separate tweening stacks. This means the Cursor command can't change diffuse
* colors; I think we do need a diffuse color stack ... */
pChild = pNode->GetChild( ssprintf("CursorP%iFrame",pn+1) );
if( pChild == NULL )
2006-10-09 08:08:59 +00:00
RageException::Throw( "%s: ComboGraph: missing the node \"CursorP%dFrame\"", ActorUtil::GetWhere(pNode).c_str(), pn+1 );
m_CursorFrames[pn].LoadFromNode( pChild );
2004-06-06 20:57:13 +00:00
m_CursorFrames[pn].AddChild( m_Cursors[pn] );
this->AddChild( &m_CursorFrames[pn] );
2003-11-17 04:19:29 +00:00
}
2004-06-06 20:57:13 +00:00
for( unsigned m = 0; m < m_Lines.size(); ++m )
2003-11-17 04:19:29 +00:00
{
m_Lines[m].m_Meter.SetName( "Row" );
m_Lines[m].m_Meter.Load( "DifficultyListRow" );
2004-06-06 20:57:13 +00:00
this->AddChild( &m_Lines[m].m_Meter );
2003-11-17 04:19:29 +00:00
}
2004-01-11 07:12:53 +00:00
UpdatePositions();
2004-02-18 23:58:33 +00:00
PositionItems();
2003-11-17 04:19:29 +00:00
}
int DifficultyList::GetCurrentRowIndex( PlayerNumber pn ) const
2003-11-17 04:19:29 +00:00
{
Difficulty ClosestDifficulty = GAMESTATE->GetClosestShownDifficulty(pn);
for( unsigned i=0; i<m_Rows.size(); i++ )
2003-11-17 04:19:29 +00:00
{
const Row &row = m_Rows[i];
2003-11-17 04:19:29 +00:00
2004-05-24 06:12:17 +00:00
if( GAMESTATE->m_pCurSteps[pn] == NULL )
{
if( row.m_dc == ClosestDifficulty )
return i;
}
else
{
if( GAMESTATE->m_pCurSteps[pn].Get() == row.m_Steps )
return i;
}
2004-01-11 07:12:53 +00:00
}
return 0;
2004-01-11 07:12:53 +00:00
}
/* Update m_fY and m_bHidden[]. */
void DifficultyList::UpdatePositions()
{
int iCurrentRow[NUM_PLAYERS];
FOREACH_HumanPlayer( p )
iCurrentRow[p] = GetCurrentRowIndex( p );
2004-01-11 07:12:53 +00:00
const int total = NUM_SHOWN_ITEMS;
const int halfsize = total / 2;
int first_start, first_end, second_start, second_end;
/* Choices for each player. If only one player is active, it's the same for both. */
2006-08-31 19:30:14 +00:00
ASSERT( GAMESTATE->IsHumanPlayer(PLAYER_1) || GAMESTATE->IsHumanPlayer(PLAYER_2) );
2004-01-11 07:12:53 +00:00
int P1Choice = GAMESTATE->IsHumanPlayer(PLAYER_1)? iCurrentRow[PLAYER_1]: iCurrentRow[PLAYER_2];
int P2Choice = GAMESTATE->IsHumanPlayer(PLAYER_2)? iCurrentRow[PLAYER_2]: iCurrentRow[PLAYER_1];
vector<Row> &Rows = m_Rows;
2004-01-11 07:12:53 +00:00
const bool BothPlayersActivated = GAMESTATE->IsHumanPlayer(PLAYER_1) && GAMESTATE->IsHumanPlayer(PLAYER_2);
if( !BothPlayersActivated )
{
/* Simply center the cursor. */
first_start = max( P1Choice - halfsize, 0 );
first_end = first_start + total;
second_start = second_end = first_end;
2005-03-13 06:41:42 +00:00
}
else
{
2004-01-11 07:12:53 +00:00
/* First half: */
const int earliest = min( P1Choice, P2Choice );
first_start = max( earliest - halfsize/2, 0 );
first_end = first_start + halfsize;
/* Second half: */
const int latest = max( P1Choice, P2Choice );
second_start = max( latest - halfsize/2, 0 );
/* Don't overlap. */
second_start = max( second_start, first_end );
second_end = second_start + halfsize;
}
first_end = min( first_end, (int) Rows.size() );
second_end = min( second_end, (int) Rows.size() );
/* If less than total (and Rows.size()) are displayed, fill in the empty
* space intelligently. */
while(1)
{
const int sum = (first_end - first_start) + (second_end - second_start);
if( sum >= (int) Rows.size() || sum >= total)
break; /* nothing more to display, or no room */
/* First priority: expand the top of the second half until it meets
* the first half. */
if( second_start > first_end )
second_start--;
/* Otherwise, expand either end. */
else if( first_start > 0 )
first_start--;
else if( second_end < (int) Rows.size() )
second_end++;
else
ASSERT(0); /* do we have room to grow or don't we? */
}
int pos = 0;
for( int i=0; i<(int) Rows.size(); i++ ) // foreach row
{
2003-11-17 04:19:29 +00:00
float ItemPosition;
2004-01-11 07:12:53 +00:00
if( i < first_start )
ItemPosition = -0.5f;
else if( i < first_end )
ItemPosition = (float) pos++;
else if( i < second_start )
ItemPosition = halfsize - 0.5f;
else if( i < second_end )
2003-11-17 04:19:29 +00:00
ItemPosition = (float) pos++;
else
2004-01-11 07:12:53 +00:00
ItemPosition = (float) total - 0.5f;
Row &row = Rows[i];
2004-01-11 07:12:53 +00:00
float fY = ITEMS_SPACING_Y*ItemPosition;
row.m_fY = fY;
row.m_bHidden = i < first_start ||
(i >= first_end && i < second_start) ||
i >= second_end;
}
}
2004-02-18 23:58:33 +00:00
void DifficultyList::PositionItems()
2004-01-11 07:12:53 +00:00
{
for( int i = 0; i < MAX_METERS; ++i )
{
bool bUnused = ( i >= (int)m_Rows.size() );
2004-04-23 00:25:42 +00:00
m_Lines[i].m_Meter.SetHidden( bUnused );
2004-01-11 07:12:53 +00:00
}
2005-03-13 06:41:42 +00:00
for( int m = 0; m < (int)m_Rows.size(); ++m )
2004-01-11 07:12:53 +00:00
{
Row &row = m_Rows[m];
2004-02-18 23:58:33 +00:00
bool bHidden = row.m_bHidden;
if( !m_bShown )
bHidden = true;
2004-01-11 07:12:53 +00:00
2005-03-13 06:41:42 +00:00
const float fDiffuseAlpha = bHidden? 0.0f:1.0f;
if( m_Lines[m].m_Meter.GetDestY() != row.m_fY ||
m_Lines[m].m_Meter.DestTweenState().diffuse[0][3] != fDiffuseAlpha )
2003-11-17 04:19:29 +00:00
{
m_Lines[m].m_Meter.RunCommands( MOVE_COMMAND.GetValue() );
m_Lines[m].m_Meter.RunCommandsOnChildren( MOVE_COMMAND.GetValue() );
2003-11-17 04:19:29 +00:00
}
2004-04-23 00:25:42 +00:00
m_Lines[m].m_Meter.SetY( row.m_fY );
2004-02-18 23:58:33 +00:00
}
2005-03-13 06:41:42 +00:00
for( int m=0; m < MAX_METERS; ++m )
2004-02-18 23:58:33 +00:00
{
bool bHidden = true;
if( m_bShown && m < (int)m_Rows.size() )
bHidden = m_Rows[m].m_bHidden;
2004-01-11 07:12:53 +00:00
2005-03-13 06:41:42 +00:00
float fDiffuseAlpha = bHidden?0.0f:1.0f;
m_Lines[m].m_Meter.SetDiffuseAlpha( fDiffuseAlpha );
2003-11-17 04:19:29 +00:00
}
2004-01-11 07:12:53 +00:00
2004-04-23 00:26:51 +00:00
FOREACH_HumanPlayer( pn )
2003-11-17 04:19:29 +00:00
{
int iCurrentRow = GetCurrentRowIndex( pn );
2004-01-11 07:12:53 +00:00
float fY = 0;
if( iCurrentRow < (int) m_Rows.size() )
fY = m_Rows[iCurrentRow].m_fY;
2003-11-17 04:19:29 +00:00
2005-03-13 06:41:42 +00:00
m_CursorFrames[pn].PlayCommand( "Change" );
2003-11-18 16:40:01 +00:00
m_CursorFrames[pn].SetY( fY );
2003-11-17 04:19:29 +00:00
}
}
void DifficultyList::SetFromGameState()
{
const Song *pSong = GAMESTATE->m_pCurSong;
2004-01-11 07:12:53 +00:00
2005-04-24 20:32:03 +00:00
const bool bSongChanged = (pSong != m_CurSong);
2004-01-11 07:12:53 +00:00
2003-11-17 04:19:29 +00:00
/* If the song has changed, update displays: */
2004-01-11 07:12:53 +00:00
if( bSongChanged )
2003-11-17 04:19:29 +00:00
{
2005-04-24 20:32:03 +00:00
m_CurSong = pSong;
2004-01-11 07:12:53 +00:00
for( int m = 0; m < MAX_METERS; ++m )
{
2004-04-23 00:25:42 +00:00
m_Lines[m].m_Meter.Unset();
2004-01-11 07:12:53 +00:00
}
m_Rows.clear();
2005-04-24 20:32:03 +00:00
if( pSong == NULL )
2003-11-17 04:19:29 +00:00
{
// FIXME: This clamps to between the min and the max difficulty, but
// it really should round to the nearest difficulty that's in
// DIFFICULTIES_TO_SHOW.
2005-03-10 19:57:43 +00:00
unsigned i=0;
FOREACH_CONST( Difficulty, CommonMetrics::DIFFICULTIES_TO_SHOW.GetValue(), d )
{
m_Rows.resize( m_Rows.size()+1 );
Row &row = m_Rows.back();
2005-03-10 19:57:43 +00:00
row.m_dc = *d;
2005-04-03 09:48:50 +00:00
m_Lines[i].m_Meter.SetFromMeterAndDifficulty( 0, *d );
2005-03-10 19:57:43 +00:00
i++;
}
}
else
{
vector<Steps*> CurSteps;
SongUtil::GetSteps( pSong, CurSteps, GAMESTATE->GetCurrentStyle()->m_StepsType );
/* Should match the sort in ScreenSelectMusic::AfterMusicChange. */
2005-04-24 20:32:03 +00:00
StepsUtil::RemoveLockedSteps( pSong, CurSteps );
StepsUtil::SortNotesArrayByDifficulty( CurSteps );
m_Rows.resize( CurSteps.size() );
for( unsigned i = 0; i < CurSteps.size(); ++i )
{
Row &row = m_Rows[i];
row.m_Steps = CurSteps[i];
2004-05-29 04:50:47 +00:00
m_Lines[i].m_Meter.SetFromSteps( m_Rows[i].m_Steps );
row.m_dc = row.m_Steps->GetDifficulty();
}
2003-11-17 04:19:29 +00:00
}
}
2004-01-11 07:12:53 +00:00
UpdatePositions();
2004-02-18 23:58:33 +00:00
PositionItems();
2004-01-11 07:12:53 +00:00
if( bSongChanged )
{
for( int m = 0; m < MAX_METERS; ++m )
2004-04-23 00:25:42 +00:00
m_Lines[m].m_Meter.FinishTweening();
2004-01-11 07:12:53 +00:00
}
2003-11-17 04:19:29 +00:00
}
2004-02-18 23:58:33 +00:00
void DifficultyList::HideRows()
{
for( unsigned m = 0; m < m_Rows.size(); ++m )
{
2005-03-29 19:01:52 +00:00
Line &l = m_Lines[m];
l.m_Meter.FinishTweening();
l.m_Meter.SetDiffuseAlpha(0);
2004-02-18 23:58:33 +00:00
}
}
2003-11-17 04:19:29 +00:00
void DifficultyList::TweenOnScreen()
{
2005-07-22 22:40:36 +00:00
FOREACH_HumanPlayer( pn )
ON_COMMAND( m_Cursors[pn] );
for( int m = 0; m < MAX_METERS; ++m )
ON_COMMAND( m_Lines[m].m_Meter );
2004-02-18 23:58:33 +00:00
this->SetHibernate( 0.5f );
2004-01-11 07:12:53 +00:00
m_bShown = true;
for( unsigned m = 0; m < m_Rows.size(); ++m )
2003-11-17 04:19:29 +00:00
{
2005-03-29 19:01:52 +00:00
Line &l = m_Lines[m];
l.m_Meter.FinishTweening();
2003-11-17 04:19:29 +00:00
}
2004-02-18 23:58:33 +00:00
HideRows();
PositionItems();
2004-01-11 07:12:53 +00:00
2004-04-23 00:26:51 +00:00
FOREACH_HumanPlayer( pn )
2003-11-17 04:19:29 +00:00
COMMAND( m_Cursors[pn], "TweenOn" );
}
void DifficultyList::TweenOffScreen()
{
}
void DifficultyList::Show()
{
2004-01-11 07:12:53 +00:00
m_bShown = true;
2004-02-18 23:58:33 +00:00
SetFromGameState();
HideRows();
PositionItems();
2003-11-17 04:19:29 +00:00
2004-04-23 00:26:51 +00:00
FOREACH_HumanPlayer( pn )
2003-11-17 04:19:29 +00:00
COMMAND( m_Cursors[pn], "Show" );
}
void DifficultyList::Hide()
{
2004-01-11 07:12:53 +00:00
m_bShown = false;
2004-02-18 23:58:33 +00:00
PositionItems();
2003-11-17 04:19:29 +00:00
2004-04-23 00:26:51 +00:00
FOREACH_HumanPlayer( pn )
2003-11-17 04:19:29 +00:00
COMMAND( m_Cursors[pn], "Hide" );
2004-05-08 08:09:47 +00:00
}
// lua start
#include "LuaBinding.h"
class LunaDifficultyList: public Luna<DifficultyList>
{
public:
static int setfromgamestate( T* p, lua_State *L ) { p->SetFromGameState(); return 0; }
2006-09-27 20:03:31 +00:00
LunaDifficultyList()
{
2006-09-27 20:03:31 +00:00
ADD_METHOD( setfromgamestate );
}
};
LUA_REGISTER_DERIVED_CLASS( DifficultyList, ActorFrame )
// lua end
2004-01-11 07:12:53 +00:00
/*
2004-06-07 21:14:03 +00:00
* (c) 2003-2004 Glenn Maynard
* 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.
2004-01-11 07:12:53 +00:00
*/