2011-03-17 01:47:30 -04:00
#include "global.h"
#include "BPMDisplay.h"
#include "RageUtil.h"
#include "GameConstantsAndTypes.h"
#include "GameState.h"
#include "Course.h"
#include "Style.h"
#include "ActorUtil.h"
#include "CommonMetrics.h"
#include "LocalizedString.h"
#include "Song.h"
2011-06-02 11:33:08 -05:00
#include "Steps.h"
2011-03-17 01:47:30 -04:00
#include <limits.h>
REGISTER_ACTOR_CLASS ( BPMDisplay );
BPMDisplay :: BPMDisplay ()
{
m_fBPMFrom = m_fBPMTo = 0 ;
m_iCurrentBPM = 0 ;
m_BPMS . push_back ( 0 );
m_fPercentInState = 0 ;
m_fCycleTime = 1.0f ;
}
void BPMDisplay :: Load ()
{
SET_NO_BPM_COMMAND . Load ( m_sName , "SetNoBpmCommand" );
SET_NORMAL_COMMAND . Load ( m_sName , "SetNormalCommand" );
SET_CHANGING_COMMAND . Load ( m_sName , "SetChangeCommand" );
SET_RANDOM_COMMAND . Load ( m_sName , "SetRandomCommand" );
SET_EXTRA_COMMAND . Load ( m_sName , "SetExtraCommand" );
CYCLE . Load ( m_sName , "Cycle" );
RANDOM_CYCLE_SPEED . Load ( m_sName , "RandomCycleSpeed" );
2011-05-19 03:48:46 -05:00
COURSE_CYCLE_SPEED . Load ( m_sName , "CourseCycleSpeed" );
2011-03-17 01:47:30 -04:00
SEPARATOR . Load ( m_sName , "Separator" );
SHOW_QMARKS . Load ( m_sName , "ShowQMarksInRandomCycle" );
NO_BPM_TEXT . Load ( m_sName , "NoBpmText" );
QUESTIONMARKS_TEXT . Load ( m_sName , "QuestionMarksText" );
RANDOM_TEXT . Load ( m_sName , "RandomText" );
VARIOUS_TEXT . Load ( m_sName , "VariousText" );
BPM_FORMAT_STRING . Load ( m_sName , "FormatString" );
RunCommands ( SET_NORMAL_COMMAND );
}
void BPMDisplay :: LoadFromNode ( const XNode * pNode )
{
BitmapText :: LoadFromNode ( pNode );
Load ();
}
float BPMDisplay :: GetActiveBPM () const
{
return m_fBPMTo + ( m_fBPMFrom - m_fBPMTo ) * m_fPercentInState ;
}
void BPMDisplay :: Update ( float fDeltaTime )
{
BitmapText :: Update ( fDeltaTime );
if ( ! ( bool ) CYCLE )
return ;
if ( m_BPMS . size () == 0 )
return ; // no bpm
m_fPercentInState -= fDeltaTime / m_fCycleTime ;
if ( m_fPercentInState < 0 )
{
// go to next state
m_fPercentInState = 1 ; // reset timer
m_iCurrentBPM = ( m_iCurrentBPM + 1 ) % m_BPMS . size ();
m_fBPMFrom = m_fBPMTo ;
m_fBPMTo = m_BPMS [ m_iCurrentBPM ];
if ( m_fBPMTo == - 1 )
{
m_fBPMFrom = - 1 ;
if ( ( bool ) SHOW_QMARKS )
SetText ( ( RandomFloat ( 0 , 1 ) > 0.90f ) ? ( RString ) QUESTIONMARKS_TEXT : ssprintf (( RString ) BPM_FORMAT_STRING , RandomFloat ( 0 , 999 )) );
else
SetText ( ssprintf (( RString ) BPM_FORMAT_STRING , RandomFloat ( 0 , 999 )) );
}
else if ( m_fBPMFrom == - 1 )
{
m_fBPMFrom = m_fBPMTo ;
}
}
if ( m_fBPMTo != - 1 )
{
const float fActualBPM = GetActiveBPM ();
SetText ( ssprintf (( RString ) BPM_FORMAT_STRING , fActualBPM ) );
}
}
void BPMDisplay :: SetBPMRange ( const DisplayBpms & bpms )
{
ASSERT ( ! bpms . vfBpms . empty () );
m_BPMS . clear ();
const vector < float > & BPMS = bpms . vfBpms ;
bool AllIdentical = true ;
for ( unsigned i = 0 ; i < BPMS . size (); ++ i )
{
if ( i > 0 && BPMS [ i ] != BPMS [ i - 1 ] )
AllIdentical = false ;
}
if ( ! ( bool ) CYCLE )
{
int MinBPM = INT_MAX ;
int MaxBPM = INT_MIN ;
for ( unsigned i = 0 ; i < BPMS . size (); ++ i )
{
MinBPM = min ( MinBPM , ( int ) lrintf ( BPMS [ i ]) );
MaxBPM = max ( MaxBPM , ( int ) lrintf ( BPMS [ i ]) );
}
if ( MinBPM == MaxBPM )
{
if ( MinBPM == - 1 )
SetText ( RANDOM_TEXT ); // random (was "...") -aj
else
SetText ( ssprintf ( "%i" , MinBPM ) );
}
else
{
SetText ( ssprintf ( "%i%s%i" , MinBPM , SEPARATOR . GetValue (). c_str (), MaxBPM ) );
}
}
else
{
for ( unsigned i = 0 ; i < BPMS . size (); ++ i )
{
m_BPMS . push_back ( BPMS [ i ]);
if ( BPMS [ i ] != - 1 )
m_BPMS . push_back ( BPMS [ i ]); // hold
}
m_iCurrentBPM = min ( 1u , m_BPMS . size ()); // start on the first hold
m_fBPMFrom = BPMS [ 0 ];
m_fBPMTo = BPMS [ 0 ];
m_fPercentInState = 1 ;
}
if ( GAMESTATE -> IsAnExtraStageAndSelectionLocked () )
RunCommands ( SET_EXTRA_COMMAND );
else if ( ! AllIdentical )
RunCommands ( SET_CHANGING_COMMAND );
else
RunCommands ( SET_NORMAL_COMMAND );
}
void BPMDisplay :: CycleRandomly ()
{
DisplayBpms bpms ;
bpms . Add ( - 1 );
SetBPMRange ( bpms );
RunCommands ( SET_RANDOM_COMMAND );
m_fCycleTime = ( float ) RANDOM_CYCLE_SPEED ;
// Go to default value in event of a negative value in the metrics
if ( m_fCycleTime < 0 )
m_fCycleTime = 0.2f ;
}
void BPMDisplay :: NoBPM ()
{
m_BPMS . clear ();
SetText ( NO_BPM_TEXT );
RunCommands ( SET_NO_BPM_COMMAND );
}
void BPMDisplay :: SetBpmFromSong ( const Song * pSong )
{
ASSERT ( pSong );
switch ( pSong -> m_DisplayBPMType )
{
2011-04-05 22:40:33 -04:00
case DISPLAY_BPM_ACTUAL :
case DISPLAY_BPM_SPECIFIED :
2011-03-17 01:47:30 -04:00
{
DisplayBpms bpms ;
pSong -> GetDisplayBpms ( bpms );
SetBPMRange ( bpms );
m_fCycleTime = 1.0f ;
}
break ;
2011-04-05 22:40:33 -04:00
case DISPLAY_BPM_RANDOM :
2011-03-17 01:47:30 -04:00
CycleRandomly ();
break ;
default :
ASSERT ( 0 );
}
}
2011-06-02 11:33:08 -05:00
void BPMDisplay :: SetBpmFromSteps ( const Steps * pSteps )
{
ASSERT ( pSteps );
DisplayBpms bpms ;
float fMinBPM , fMaxBPM ;
pSteps -> m_Timing . GetActualBPM ( fMinBPM , fMaxBPM );
bpms . Add ( fMinBPM );
bpms . Add ( fMaxBPM );
m_fCycleTime = 1.0f ;
}
2011-03-17 01:47:30 -04:00
void BPMDisplay :: SetBpmFromCourse ( const Course * pCourse )
{
ASSERT ( pCourse );
ASSERT ( GAMESTATE -> GetCurrentStyle () );
StepsType st = GAMESTATE -> GetCurrentStyle () -> m_StepsType ;
Trail * pTrail = pCourse -> GetTrail ( st );
2011-05-10 09:24:31 -05:00
// GetTranslitFullTitle because "Crashinfo.txt is garbled because of the ANSI output as usual." -f
2011-05-11 16:08:29 -05:00
ASSERT_M ( pTrail , ssprintf ( "Course '%s' has no trail for StepsType '%s'" , pCourse -> GetTranslitFullTitle (). c_str (), StringConversion :: ToString ( st ). c_str () ) );
2011-03-17 01:47:30 -04:00
2011-05-19 03:48:46 -05:00
// todo: let themers define this. -aj
m_fCycleTime = ( float ) COURSE_CYCLE_SPEED ;
2011-03-17 01:47:30 -04:00
if ( ( int ) pTrail -> m_vEntries . size () > CommonMetrics :: MAX_COURSE_ENTRIES_BEFORE_VARIOUS )
{
SetVarious ();
return ;
}
DisplayBpms bpms ;
pTrail -> GetDisplayBpms ( bpms );
SetBPMRange ( bpms );
}
void BPMDisplay :: SetConstantBpm ( float fBPM )
{
DisplayBpms bpms ;
bpms . Add ( fBPM );
SetBPMRange ( bpms );
}
void BPMDisplay :: SetVarious ()
{
m_BPMS . clear ();
m_BPMS . push_back ( - 1 );
SetText ( VARIOUS_TEXT );
}
void BPMDisplay :: SetFromGameState ()
{
if ( GAMESTATE -> m_pCurSong . Get () )
{
if ( GAMESTATE -> IsAnExtraStageAndSelectionLocked () )
CycleRandomly ();
else
SetBpmFromSong ( GAMESTATE -> m_pCurSong );
return ;
}
if ( GAMESTATE -> m_pCurCourse . Get () )
{
if ( GAMESTATE -> GetCurrentStyle () == NULL )
; // This is true when backing out from ScreenSelectCourse to ScreenTitleMenu. So, don't call SetBpmFromCourse where an assert will fire.
else
SetBpmFromCourse ( GAMESTATE -> m_pCurCourse );
return ;
}
NoBPM ();
}
// SongBPMDisplay (in-game BPM display)
class SongBPMDisplay : public BPMDisplay
{
public :
SongBPMDisplay ();
virtual SongBPMDisplay * Copy () const ;
virtual void Update ( float fDeltaTime );
private :
float m_fLastGameStateBPM ;
};
SongBPMDisplay :: SongBPMDisplay ()
{
m_fLastGameStateBPM = 0 ;
}
void SongBPMDisplay :: Update ( float fDeltaTime )
{
2011-05-10 21:44:25 +07:00
float fGameStateBPM = GAMESTATE -> m_Position . m_fCurBPS * 60.0f ;
2011-03-17 01:47:30 -04:00
if ( m_fLastGameStateBPM != fGameStateBPM )
{
m_fLastGameStateBPM = fGameStateBPM ;
SetConstantBpm ( fGameStateBPM );
}
BPMDisplay :: Update ( fDeltaTime );
}
REGISTER_ACTOR_CLASS ( SongBPMDisplay );
#include "LuaBinding.h"
/** @brief Allow Lua to have access to the BPMDisplay. */
class LunaBPMDisplay : public Luna < BPMDisplay >
{
public :
static int SetFromGameState ( T * p , lua_State * L ) { p -> SetFromGameState (); return 0 ; }
2011-04-28 19:12:50 -05:00
static int SetFromSong ( T * p , lua_State * L )
{
if ( lua_isnil ( L , 1 ) ) { p -> NoBPM (); }
else
{
const Song * pSong = Luna < Song >:: check ( L , 1 , true );
p -> SetBpmFromSong ( pSong );
}
return 0 ;
}
2011-06-02 11:33:08 -05:00
static int SetFromSteps ( T * p , lua_State * L )
{
if ( lua_isnil ( L , 1 ) ) { p -> NoBPM (); }
else
{
const Steps * pSteps = Luna < Steps >:: check ( L , 1 , true );
p -> SetBpmFromSteps ( pSteps );
}
return 0 ;
}
2011-05-10 09:30:03 -05:00
static int SetFromCourse ( T * p , lua_State * L )
{
if ( lua_isnil ( L , 1 ) ) { p -> NoBPM (); }
else
{
const Course * pCourse = Luna < Course >:: check ( L , 1 , true );
p -> SetBpmFromCourse ( pCourse );
}
return 0 ;
}
2011-03-17 01:47:30 -04:00
static int GetText ( T * p , lua_State * L ) { lua_pushstring ( L , p -> GetText () ); return 1 ; }
LunaBPMDisplay ()
{
ADD_METHOD ( SetFromGameState );
2011-04-28 19:12:50 -05:00
ADD_METHOD ( SetFromSong );
2011-06-02 11:33:08 -05:00
ADD_METHOD ( SetFromSteps );
2011-05-10 09:30:03 -05:00
ADD_METHOD ( SetFromCourse );
2011-03-17 01:47:30 -04:00
ADD_METHOD ( GetText );
}
};
LUA_REGISTER_DERIVED_CLASS ( BPMDisplay , BitmapText )
/*
* (c) 2001-2002 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.
*/