2011-03-17 01:47:30 -04:00
# include "global.h"
# include "NoteField.h"
# include "RageUtil.h"
# include "GameConstantsAndTypes.h"
# include "ArrowEffects.h"
# include "GameManager.h"
# include "GameState.h"
# include "RageTimer.h"
# include "RageLog.h"
# include "RageMath.h"
# include "ThemeManager.h"
# include "NoteSkinManager.h"
# include "Song.h"
# include "ScreenDimensions.h"
# include "PlayerState.h"
# include "Style.h"
# include "CommonMetrics.h"
# include <float.h>
# include "BackgroundUtil.h"
# include "Course.h"
# include "NoteData.h"
2012-03-20 00:19:28 -05:00
# include "RageDisplay.h"
2011-03-17 01:47:30 -04:00
2011-06-12 19:20:43 -04:00
float FindFirstDisplayedBeat ( const PlayerState * pPlayerState , int iDrawDistanceAfterTargetsPixels ) ;
float FindLastDisplayedBeat ( const PlayerState * pPlayerState , int iDrawDistanceBeforeTargetsPixels ) ;
2011-03-17 01:47:30 -04:00
static ThemeMetric < bool > SHOW_BOARD ( " NoteField " , " ShowBoard " ) ;
static ThemeMetric < bool > SHOW_BEAT_BARS ( " NoteField " , " ShowBeatBars " ) ;
static ThemeMetric < float > FADE_BEFORE_TARGETS_PERCENT ( " NoteField " , " FadeBeforeTargetsPercent " ) ;
static ThemeMetric < float > BAR_MEASURE_ALPHA ( " NoteField " , " BarMeasureAlpha " ) ;
static ThemeMetric < float > BAR_4TH_ALPHA ( " NoteField " , " Bar4thAlpha " ) ;
static ThemeMetric < float > BAR_8TH_ALPHA ( " NoteField " , " Bar8thAlpha " ) ;
static ThemeMetric < float > BAR_16TH_ALPHA ( " NoteField " , " Bar16thAlpha " ) ;
static ThemeMetric < float > FADE_FAIL_TIME ( " NoteField " , " FadeFailTime " ) ;
static RString RoutineNoteSkinName ( size_t i ) { return ssprintf ( " RoutineNoteSkinP%i " , int ( i + 1 ) ) ; }
static ThemeMetric1D < RString > ROUTINE_NOTESKIN ( " NoteField " , RoutineNoteSkinName , NUM_PLAYERS ) ;
NoteField : : NoteField ( )
{
m_pNoteData = NULL ;
m_pCurDisplay = NULL ;
2015-04-03 14:19:28 -06:00
m_drawing_board_primitive = false ;
2011-03-17 01:47:30 -04:00
m_textMeasureNumber . LoadFromFont ( THEME - > GetPathF ( " NoteField " , " MeasureNumber " ) ) ;
m_textMeasureNumber . SetZoom ( 1.0f ) ;
m_textMeasureNumber . SetShadowLength ( 2 ) ;
m_textMeasureNumber . SetWrapWidthPixels ( 300 ) ;
m_rectMarkerBar . SetEffectDiffuseShift ( 2 , RageColor ( 1 , 1 , 1 , 0.5f ) , RageColor ( 0.5f , 0.5f , 0.5f , 0.5f ) ) ;
m_sprBoard . Load ( THEME - > GetPathG ( " NoteField " , " board " ) ) ;
m_sprBoard - > SetName ( " Board " ) ;
m_sprBoard - > PlayCommand ( " On " ) ;
this - > AddChild ( m_sprBoard ) ;
m_fBoardOffsetPixels = 0 ;
m_fCurrentBeatLastUpdate = - 1 ;
m_fYPosCurrentBeatLastUpdate = - 1 ;
this - > SubscribeToMessage ( Message_CurrentSongChanged ) ;
m_sprBeatBars . Load ( THEME - > GetPathG ( " NoteField " , " bars " ) ) ;
m_sprBeatBars . StopAnimating ( ) ;
2014-12-26 01:54:34 -07:00
// I decided to do it this way because I don't want to dig through
// ScreenEdit to change all the places it touches the markers. -Kyz
2015-01-01 04:36:20 -07:00
m_FieldRenderArgs . selection_begin_marker = & m_iBeginMarker ;
m_FieldRenderArgs . selection_end_marker = & m_iEndMarker ;
2011-03-17 01:47:30 -04:00
m_iBeginMarker = m_iEndMarker = - 1 ;
2015-01-01 04:36:20 -07:00
m_FieldRenderArgs . fail_fade = - 1 ;
2014-11-30 23:17:12 -07:00
m_StepCallback . SetFromNil ( ) ;
m_SetPressedCallback . SetFromNil ( ) ;
m_DidTapNoteCallback . SetFromNil ( ) ;
m_DidHoldNoteCallback . SetFromNil ( ) ;
2011-03-17 01:47:30 -04:00
}
NoteField : : ~ NoteField ( )
{
Unload ( ) ;
}
void NoteField : : Unload ( )
{
for ( map < RString , NoteDisplayCols * > : : iterator it = m_NoteDisplays . begin ( ) ;
it ! = m_NoteDisplays . end ( ) ; + + it )
delete it - > second ;
m_NoteDisplays . clear ( ) ;
m_pCurDisplay = NULL ;
memset ( m_pDisplays , 0 , sizeof ( m_pDisplays ) ) ;
}
void NoteField : : CacheNoteSkin ( const RString & sNoteSkin_ )
{
RString sNoteSkinLower = sNoteSkin_ ;
sNoteSkinLower . MakeLower ( ) ;
if ( m_NoteDisplays . find ( sNoteSkinLower ) ! = m_NoteDisplays . end ( ) )
return ;
LockNoteSkin l ( sNoteSkinLower ) ;
LOG - > Trace ( " NoteField::CacheNoteSkin: cache %s " , sNoteSkinLower . c_str ( ) ) ;
2014-12-07 01:53:17 -07:00
NoteDisplayCols * nd = new NoteDisplayCols ( GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) - > m_iColsPerPlayer ) ;
2011-03-17 01:47:30 -04:00
2014-12-07 01:53:17 -07:00
for ( int c = 0 ; c < GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) - > m_iColsPerPlayer ; c + + )
2011-03-17 01:47:30 -04:00
nd - > display [ c ] . Load ( c , m_pPlayerState , m_fYReverseOffsetPixels ) ;
nd - > m_ReceptorArrowRow . Load ( m_pPlayerState , m_fYReverseOffsetPixels ) ;
nd - > m_GhostArrowRow . Load ( m_pPlayerState , m_fYReverseOffsetPixels ) ;
m_NoteDisplays [ sNoteSkinLower ] = nd ;
}
void NoteField : : UncacheNoteSkin ( const RString & sNoteSkin_ )
{
RString sNoteSkinLower = sNoteSkin_ ;
sNoteSkinLower . MakeLower ( ) ;
LOG - > Trace ( " NoteField::CacheNoteSkin: release %s " , sNoteSkinLower . c_str ( ) ) ;
ASSERT_M ( m_NoteDisplays . find ( sNoteSkinLower ) ! = m_NoteDisplays . end ( ) , sNoteSkinLower ) ;
delete m_NoteDisplays [ sNoteSkinLower ] ;
m_NoteDisplays . erase ( sNoteSkinLower ) ;
}
void NoteField : : CacheAllUsedNoteSkins ( )
{
// If we're in Routine mode, apply our per-player noteskins.
2014-12-07 01:53:17 -07:00
if ( GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) - > m_StyleType = = StyleType_TwoPlayersSharedSides )
2011-03-17 01:47:30 -04:00
{
FOREACH_EnabledPlayer ( pn )
GAMESTATE - > ApplyStageModifiers ( pn , ROUTINE_NOTESKIN . GetValue ( pn ) ) ;
}
/* Cache all note skins that we might need for the whole song, course or battle
* play, so we don't have to load them later (such as between course songs). */
vector < RString > asSkinsLower ;
GAMESTATE - > GetAllUsedNoteSkins ( asSkinsLower ) ;
asSkinsLower . push_back ( m_pPlayerState - > m_PlayerOptions . GetStage ( ) . m_sNoteSkin ) ;
FOREACH ( RString , asSkinsLower , s )
2015-01-31 00:23:54 -07:00
{
NOTESKIN - > ValidateNoteSkinName ( * s ) ;
2011-03-17 01:47:30 -04:00
s - > MakeLower ( ) ;
2015-01-31 00:23:54 -07:00
}
2011-03-17 01:47:30 -04:00
for ( unsigned i = 0 ; i < asSkinsLower . size ( ) ; + + i )
CacheNoteSkin ( asSkinsLower [ i ] ) ;
/* If we're changing note skins in the editor, we can have old note skins lying
* around. Remove them so they don't accumulate. */
set < RString > setNoteSkinsToUnload ;
FOREACHM ( RString , NoteDisplayCols * , m_NoteDisplays , d )
{
bool unused = find ( asSkinsLower . begin ( ) , asSkinsLower . end ( ) , d - > first ) = = asSkinsLower . end ( ) ;
if ( unused )
setNoteSkinsToUnload . insert ( d - > first ) ;
}
FOREACHS ( RString , setNoteSkinsToUnload , s )
UncacheNoteSkin ( * s ) ;
RString sCurrentNoteSkinLower = m_pPlayerState - > m_PlayerOptions . GetCurrent ( ) . m_sNoteSkin ;
2015-01-31 00:23:54 -07:00
NOTESKIN - > ValidateNoteSkinName ( sCurrentNoteSkinLower ) ;
2011-03-17 01:47:30 -04:00
sCurrentNoteSkinLower . MakeLower ( ) ;
map < RString , NoteDisplayCols * > : : iterator it = m_NoteDisplays . find ( sCurrentNoteSkinLower ) ;
ASSERT_M ( it ! = m_NoteDisplays . end ( ) , sCurrentNoteSkinLower ) ;
m_pCurDisplay = it - > second ;
memset ( m_pDisplays , 0 , sizeof ( m_pDisplays ) ) ;
FOREACH_EnabledPlayer ( pn )
{
RString sNoteSkinLower = GAMESTATE - > m_pPlayerState [ pn ] - > m_PlayerOptions . GetCurrent ( ) . m_sNoteSkin ;
2015-01-31 00:23:54 -07:00
NOTESKIN - > ValidateNoteSkinName ( sNoteSkinLower ) ;
2011-03-17 01:47:30 -04:00
sNoteSkinLower . MakeLower ( ) ;
it = m_NoteDisplays . find ( sNoteSkinLower ) ;
ASSERT_M ( it ! = m_NoteDisplays . end ( ) , sNoteSkinLower ) ;
m_pDisplays [ pn ] = it - > second ;
}
2014-12-26 01:54:34 -07:00
InitColumnRenderers ( ) ;
2011-03-17 01:47:30 -04:00
}
2014-12-07 01:53:17 -07:00
void NoteField : : Init ( const PlayerState * pPlayerState , float fYReverseOffsetPixels , bool use_states_zoom )
2011-03-17 01:47:30 -04:00
{
m_pPlayerState = pPlayerState ;
m_fYReverseOffsetPixels = fYReverseOffsetPixels ;
CacheAllUsedNoteSkins ( ) ;
2014-12-07 01:53:17 -07:00
// Design change: Instead of having a flag in the style that toggles a
// fixed zoom that is only applied to the columns, ScreenGameplay now
// calculates a zoom factor to apply to the notefield and puts it in the
// PlayerState. -Kyz
// use_states_zoom flag exists because edit mode has to set its own special
// zoom factor. -Kyz
if ( use_states_zoom )
{
SetZoom ( pPlayerState - > m_NotefieldZoom ) ;
}
2015-04-03 14:19:28 -06:00
// Pass the player state info down to children so that they can set
// per-player things. For example, if a screen filter is in the notefield
// board, this tells it what player it's for. -Kyz
Message msg ( " PlayerStateSet " ) ;
msg . SetParam ( " PlayerNumber " , pPlayerState - > m_PlayerNumber ) ;
HandleMessage ( msg ) ;
2011-03-17 01:47:30 -04:00
}
void NoteField : : Load (
const NoteData * pNoteData ,
2014-12-07 01:53:17 -07:00
int iDrawDistanceAfterTargetsPixels ,
2011-03-17 01:47:30 -04:00
int iDrawDistanceBeforeTargetsPixels )
{
2012-12-27 11:38:53 -05:00
ASSERT ( pNoteData ! = NULL ) ;
2011-03-17 01:47:30 -04:00
m_pNoteData = pNoteData ;
m_iDrawDistanceAfterTargetsPixels = iDrawDistanceAfterTargetsPixels ;
m_iDrawDistanceBeforeTargetsPixels = iDrawDistanceBeforeTargetsPixels ;
ASSERT ( m_iDrawDistanceBeforeTargetsPixels > = m_iDrawDistanceAfterTargetsPixels ) ;
2015-01-01 04:36:20 -07:00
m_FieldRenderArgs . fail_fade = - 1 ;
2011-03-17 01:47:30 -04:00
//int i1 = m_pNoteData->GetNumTracks();
2014-12-07 01:53:17 -07:00
//int i2 = GAMESTATE->GetCurrentStyle(m_pPlayerState->m_PlayerNumber)->m_iColsPerPlayer;
ASSERT_M ( m_pNoteData - > GetNumTracks ( ) = = GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) - > m_iColsPerPlayer ,
ssprintf ( " NumTracks %d = ColsPerPlayer %d " , m_pNoteData - > GetNumTracks ( ) ,
GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) - > m_iColsPerPlayer ) ) ;
2011-07-03 15:10:00 -04:00
2011-03-17 01:47:30 -04:00
// The NoteSkin may have changed at the beginning of a new course song.
RString sNoteSkinLower = m_pPlayerState - > m_PlayerOptions . GetCurrent ( ) . m_sNoteSkin ;
/* XXX: Combination of good idea and bad idea to ensure courses load
* regardless of noteskin content. This may take a while to fix. */
NoteDisplayCols * badIdea = m_pCurDisplay ;
2011-11-27 22:20:32 -06:00
if ( sNoteSkinLower . empty ( ) )
2011-03-17 01:47:30 -04:00
{
sNoteSkinLower = m_pPlayerState - > m_PlayerOptions . GetPreferred ( ) . m_sNoteSkin ;
2011-11-27 22:28:50 -06:00
if ( sNoteSkinLower . empty ( ) )
2011-03-17 01:47:30 -04:00
{
sNoteSkinLower = " default " ;
}
m_NoteDisplays . insert ( pair < RString , NoteDisplayCols * > ( sNoteSkinLower , badIdea ) ) ;
}
sNoteSkinLower . MakeLower ( ) ;
map < RString , NoteDisplayCols * > : : iterator it = m_NoteDisplays . find ( sNoteSkinLower ) ;
2011-11-27 22:20:32 -06:00
ASSERT_M ( it ! = m_NoteDisplays . end ( ) , ssprintf ( " iterator != m_NoteDisplays.end() [sNoteSkinLower = %s] " , sNoteSkinLower . c_str ( ) ) ) ;
2011-03-17 01:47:30 -04:00
memset ( m_pDisplays , 0 , sizeof ( m_pDisplays ) ) ;
FOREACH_EnabledPlayer ( pn )
{
sNoteSkinLower = GAMESTATE - > m_pPlayerState [ pn ] - > m_PlayerOptions . GetCurrent ( ) . m_sNoteSkin ;
// XXX: Re-setup sNoteSkinLower. Unsure if inserting the skin again is needed.
2011-11-27 22:28:50 -06:00
if ( sNoteSkinLower . empty ( ) )
2011-03-17 01:47:30 -04:00
{
sNoteSkinLower = GAMESTATE - > m_pPlayerState [ pn ] - > m_PlayerOptions . GetPreferred ( ) . m_sNoteSkin ;
2011-11-27 22:28:50 -06:00
if ( sNoteSkinLower . empty ( ) )
2011-03-17 01:47:30 -04:00
{
sNoteSkinLower = " default " ;
}
m_NoteDisplays . insert ( pair < RString , NoteDisplayCols * > ( sNoteSkinLower , badIdea ) ) ;
}
2011-11-27 22:28:50 -06:00
2011-03-17 01:47:30 -04:00
sNoteSkinLower . MakeLower ( ) ;
it = m_NoteDisplays . find ( sNoteSkinLower ) ;
ASSERT_M ( it ! = m_NoteDisplays . end ( ) , sNoteSkinLower ) ;
m_pDisplays [ pn ] = it - > second ;
}
2014-12-26 01:54:34 -07:00
InitColumnRenderers ( ) ;
}
void NoteField : : InitColumnRenderers ( )
{
2015-01-01 04:36:20 -07:00
m_FieldRenderArgs . player_state = m_pPlayerState ;
m_FieldRenderArgs . reverse_offset_pixels = m_fYReverseOffsetPixels ;
m_FieldRenderArgs . receptor_row = & ( m_pCurDisplay - > m_ReceptorArrowRow ) ;
m_FieldRenderArgs . ghost_row = & ( m_pCurDisplay - > m_GhostArrowRow ) ;
m_FieldRenderArgs . note_data = m_pNoteData ;
2015-01-27 18:03:41 -07:00
m_ColumnRenderers . resize ( GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) - > m_iColsPerPlayer ) ;
2014-12-26 01:54:34 -07:00
for ( size_t ncr = 0 ; ncr < m_ColumnRenderers . size ( ) ; + + ncr )
{
FOREACH_EnabledPlayer ( pn )
{
m_ColumnRenderers [ ncr ] . m_displays [ pn ] = & ( m_pDisplays [ pn ] - > display [ ncr ] ) ;
}
m_ColumnRenderers [ ncr ] . m_displays [ PLAYER_INVALID ] = & ( m_pCurDisplay - > display [ ncr ] ) ;
m_ColumnRenderers [ ncr ] . m_column = ncr ;
2015-01-01 04:36:20 -07:00
m_ColumnRenderers [ ncr ] . m_column_render_args . column = ncr ;
m_ColumnRenderers [ ncr ] . m_field_render_args = & m_FieldRenderArgs ;
2014-12-26 01:54:34 -07:00
}
m_pCurDisplay - > m_ReceptorArrowRow . SetColumnRenderers ( m_ColumnRenderers ) ;
m_pCurDisplay - > m_GhostArrowRow . SetColumnRenderers ( m_ColumnRenderers ) ;
2011-03-17 01:47:30 -04:00
}
void NoteField : : Update ( float fDeltaTime )
{
if ( m_bFirstUpdate )
{
m_pCurDisplay - > m_ReceptorArrowRow . PlayCommand ( " On " ) ;
}
ActorFrame : : Update ( fDeltaTime ) ;
2015-04-24 15:55:54 -06:00
ArrowEffects : : SetCurrentOptions ( & m_pPlayerState - > m_PlayerOptions . GetCurrent ( ) ) ;
2011-03-17 01:47:30 -04:00
2014-12-26 01:54:34 -07:00
for ( size_t c = 0 ; c < m_ColumnRenderers . size ( ) ; + + c )
{
m_ColumnRenderers [ c ] . Update ( fDeltaTime ) ;
}
2011-03-17 01:47:30 -04:00
// update m_fBoardOffsetPixels, m_fCurrentBeatLastUpdate, m_fYPosCurrentBeatLastUpdate
2011-09-14 12:20:42 +07:00
const float fCurrentBeat = m_pPlayerState - > GetDisplayedPosition ( ) . m_fSongBeat ;
2011-03-17 01:47:30 -04:00
bool bTweeningOn = m_sprBoard - > GetCurrentDiffuseAlpha ( ) > = 0.98 & & m_sprBoard - > GetCurrentDiffuseAlpha ( ) < 1.00 ; // HACK
if ( ! bTweeningOn & & m_fCurrentBeatLastUpdate ! = - 1 )
{
2015-04-24 15:55:54 -06:00
const float fYOffsetLast = ArrowEffects : : GetYOffset ( m_pPlayerState , 0 , m_fCurrentBeatLastUpdate ) ;
const float fYPosLast = ArrowEffects : : GetYPos ( 0 , fYOffsetLast , m_fYReverseOffsetPixels ) ;
2011-03-17 01:47:30 -04:00
const float fPixelDifference = fYPosLast - m_fYPosCurrentBeatLastUpdate ;
//LOG->Trace( "speed = %f, %f, %f, %f, %f, %f", fSpeed, fYOffsetAtCurrent, fYOffsetAtNext, fSecondsAtCurrent, fSecondsAtNext, fPixelDifference, fSecondsDifference );
m_fBoardOffsetPixels + = fPixelDifference ;
wrap ( m_fBoardOffsetPixels , m_sprBoard - > GetUnzoomedHeight ( ) ) ;
}
m_fCurrentBeatLastUpdate = fCurrentBeat ;
const float fYOffsetCurrent = ArrowEffects : : GetYOffset ( m_pPlayerState , 0 , m_fCurrentBeatLastUpdate ) ;
2015-04-24 15:55:54 -06:00
m_fYPosCurrentBeatLastUpdate = ArrowEffects : : GetYPos ( 0 , fYOffsetCurrent , m_fYReverseOffsetPixels ) ;
2011-03-17 01:47:30 -04:00
m_rectMarkerBar . Update ( fDeltaTime ) ;
NoteDisplayCols * cur = m_pCurDisplay ;
cur - > m_ReceptorArrowRow . Update ( fDeltaTime ) ;
cur - > m_GhostArrowRow . Update ( fDeltaTime ) ;
2015-01-01 04:36:20 -07:00
if ( m_FieldRenderArgs . fail_fade > = 0 )
m_FieldRenderArgs . fail_fade = min ( m_FieldRenderArgs . fail_fade + fDeltaTime / FADE_FAIL_TIME , 1 ) ;
2011-03-17 01:47:30 -04:00
// Update fade to failed
2015-01-01 04:36:20 -07:00
m_pCurDisplay - > m_ReceptorArrowRow . SetFadeToFailPercent ( m_FieldRenderArgs . fail_fade ) ;
2011-03-17 01:47:30 -04:00
NoteDisplay : : Update ( fDeltaTime ) ;
/* Update all NoteDisplays. Hack: We need to call this once per frame, not
* once per player. */
// TODO: Remove use of PlayerNumber.
PlayerNumber pn = m_pPlayerState - > m_PlayerNumber ;
2011-06-06 20:40:11 -04:00
if ( pn = = GAMESTATE - > GetMasterPlayerNumber ( ) )
2011-03-17 01:47:30 -04:00
NoteDisplay : : Update ( fDeltaTime ) ;
}
float NoteField : : GetWidth ( ) const
{
2014-12-07 01:53:17 -07:00
const Style * pStyle = GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) ;
2011-03-17 01:47:30 -04:00
float fMinX , fMaxX ;
// TODO: Remove use of PlayerNumber.
pStyle - > GetMinAndMaxColX ( m_pPlayerState - > m_PlayerNumber , fMinX , fMaxX ) ;
const float fYZoom = ArrowEffects : : GetZoom ( m_pPlayerState ) ;
return ( fMaxX - fMinX + ARROW_SIZE ) * fYZoom ;
}
void NoteField : : DrawBeatBar ( const float fBeat , BeatBarType type , int iMeasureIndex )
{
bool bIsMeasure = type = = measure ;
const float fYOffset = ArrowEffects : : GetYOffset ( m_pPlayerState , 0 , fBeat ) ;
2015-04-24 15:55:54 -06:00
const float fYPos = ArrowEffects : : GetYPos ( 0 , fYOffset , m_fYReverseOffsetPixels ) ;
2011-03-17 01:47:30 -04:00
float fAlpha ;
int iState ;
if ( bIsMeasure )
{
fAlpha = BAR_MEASURE_ALPHA ;
iState = 0 ;
}
else
{
2015-12-04 00:28:22 -07:00
PlayerOptions const & curr_ops = m_pPlayerState - > m_PlayerOptions . GetCurrent ( ) ;
float fScrollSpeed = curr_ops . m_fScrollSpeed ;
if ( curr_ops . m_fTimeSpacing > 0 )
{
2011-03-17 01:47:30 -04:00
fScrollSpeed = 4 ;
2015-12-04 00:28:22 -07:00
}
else if ( curr_ops . m_fMaxScrollBPM ! = 0 )
{
fScrollSpeed = curr_ops . m_fMaxScrollBPM / m_pPlayerState - > m_fReadBPM ;
}
2011-03-17 01:47:30 -04:00
switch ( type )
{
DEFAULT_FAIL ( type ) ;
case measure : // handled above
case beat : // fall through
fAlpha = BAR_4TH_ALPHA ;
iState = 1 ;
break ;
case half_beat :
fAlpha = SCALE ( fScrollSpeed , 1.0f , 2.0f , 0.0f , BAR_8TH_ALPHA ) ;
iState = 2 ;
break ;
case quarter_beat :
fAlpha = SCALE ( fScrollSpeed , 2.0f , 4.0f , 0.0f , BAR_16TH_ALPHA ) ;
iState = 3 ;
break ;
}
CLAMP ( fAlpha , 0 , 1 ) ;
}
float fWidth = GetWidth ( ) ;
float fFrameWidth = m_sprBeatBars . GetUnzoomedWidth ( ) ;
m_sprBeatBars . SetX ( 0 ) ;
m_sprBeatBars . SetY ( fYPos ) ;
m_sprBeatBars . SetDiffuse ( RageColor ( 1 , 1 , 1 , fAlpha ) ) ;
m_sprBeatBars . SetState ( iState ) ;
m_sprBeatBars . SetCustomTextureRect ( RectF ( 0 , SCALE ( iState , 0.f , 4.f , 0.f , 1.f ) , fWidth / fFrameWidth , SCALE ( iState + 1 , 0.f , 4.f , 0.f , 1.f ) ) ) ;
m_sprBeatBars . SetZoomX ( fWidth / m_sprBeatBars . GetUnzoomedWidth ( ) ) ;
m_sprBeatBars . Draw ( ) ;
if ( GAMESTATE - > IsEditing ( ) & & bIsMeasure )
{
int iMeasureNoDisplay = iMeasureIndex ;
m_textMeasureNumber . SetDiffuse ( RageColor ( 1 , 1 , 1 , 1 ) ) ;
m_textMeasureNumber . SetGlow ( RageColor ( 1 , 1 , 1 , 0 ) ) ;
m_textMeasureNumber . SetHorizAlign ( align_right ) ;
m_textMeasureNumber . SetText ( ssprintf ( " %d " , iMeasureNoDisplay ) ) ;
m_textMeasureNumber . SetXY ( - fWidth / 2 , fYPos ) ;
m_textMeasureNumber . Draw ( ) ;
}
}
void NoteField : : DrawBoard ( int iDrawDistanceAfterTargetsPixels , int iDrawDistanceBeforeTargetsPixels )
{
// todo: make this an AutoActor instead? -aj
Sprite * pSprite = dynamic_cast < Sprite * > ( ( Actor * ) m_sprBoard ) ;
if ( pSprite = = NULL )
{
2014-05-02 17:01:38 -05:00
m_sprBoard - > Draw ( ) ;
}
else
{
// Draw the board centered on fYPosAt0 so that the board doesn't slide as
// the draw distance changes with modifiers.
2015-04-24 15:55:54 -06:00
const float fYPosAt0 = ArrowEffects : : GetYPos ( 0 , 0 , m_fYReverseOffsetPixels ) ;
2014-05-02 17:01:38 -05:00
RectF rect = * pSprite - > GetCurrentTextureCoordRect ( ) ;
const float fBoardGraphicHeightPixels = pSprite - > GetUnzoomedHeight ( ) ;
float fTexCoordOffset = m_fBoardOffsetPixels / fBoardGraphicHeightPixels ;
2011-03-17 01:47:30 -04:00
// top half
const float fHeight = iDrawDistanceBeforeTargetsPixels - iDrawDistanceAfterTargetsPixels ;
const float fY = fYPosAt0 - ( ( iDrawDistanceBeforeTargetsPixels + iDrawDistanceAfterTargetsPixels ) / 2.0f ) ;
pSprite - > ZoomToHeight ( fHeight ) ;
pSprite - > SetY ( fY ) ;
// handle tex coord offset and fade
rect . top = - fTexCoordOffset - ( iDrawDistanceBeforeTargetsPixels / fBoardGraphicHeightPixels ) ;
rect . bottom = - fTexCoordOffset + ( - iDrawDistanceAfterTargetsPixels / fBoardGraphicHeightPixels ) ;
pSprite - > SetCustomTextureRect ( rect ) ;
float fFadeTop = FADE_BEFORE_TARGETS_PERCENT * iDrawDistanceBeforeTargetsPixels / ( iDrawDistanceBeforeTargetsPixels - iDrawDistanceAfterTargetsPixels ) ;
pSprite - > SetFadeTop ( fFadeTop ) ;
pSprite - > Draw ( ) ;
}
}
void NoteField : : DrawMarkerBar ( int iBeat )
{
float fBeat = NoteRowToBeat ( iBeat ) ;
const float fYOffset = ArrowEffects : : GetYOffset ( m_pPlayerState , 0 , fBeat ) ;
2015-04-24 15:55:54 -06:00
const float fYPos = ArrowEffects : : GetYPos ( 0 , fYOffset , m_fYReverseOffsetPixels ) ;
2011-03-17 01:47:30 -04:00
m_rectMarkerBar . StretchTo ( RectF ( - GetWidth ( ) / 2 , fYPos - ARROW_SIZE / 2 , GetWidth ( ) / 2 , fYPos + ARROW_SIZE / 2 ) ) ;
m_rectMarkerBar . Draw ( ) ;
}
2011-08-05 12:14:15 -05:00
static ThemeMetric < RageColor > AREA_HIGHLIGHT_COLOR ( " NoteField " , " AreaHighlightColor " ) ;
2011-03-17 01:47:30 -04:00
void NoteField : : DrawAreaHighlight ( int iStartBeat , int iEndBeat )
{
float fStartBeat = NoteRowToBeat ( iStartBeat ) ;
float fEndBeat = NoteRowToBeat ( iEndBeat ) ;
float fDrawDistanceAfterTargetsPixels = ArrowEffects : : GetYOffset ( m_pPlayerState , 0 , fStartBeat ) ;
2015-04-24 15:55:54 -06:00
float fYStartPos = ArrowEffects : : GetYPos ( 0 , fDrawDistanceAfterTargetsPixels , m_fYReverseOffsetPixels ) ;
2011-03-17 01:47:30 -04:00
float fDrawDistanceBeforeTargetsPixels = ArrowEffects : : GetYOffset ( m_pPlayerState , 0 , fEndBeat ) ;
2015-04-24 15:55:54 -06:00
float fYEndPos = ArrowEffects : : GetYPos ( 0 , fDrawDistanceBeforeTargetsPixels , m_fYReverseOffsetPixels ) ;
2011-03-17 01:47:30 -04:00
// The caller should have clamped these to reasonable values
ASSERT ( fYStartPos > - 1000 ) ;
ASSERT ( fYEndPos < + 5000 ) ;
m_rectAreaHighlight . StretchTo ( RectF ( - GetWidth ( ) / 2 , fYStartPos , GetWidth ( ) / 2 , fYEndPos ) ) ;
2011-08-05 12:14:15 -05:00
m_rectAreaHighlight . SetDiffuse ( AREA_HIGHLIGHT_COLOR ) ;
2011-03-17 01:47:30 -04:00
m_rectAreaHighlight . Draw ( ) ;
}
// todo: add DrawWarpAreaBG? -aj
static ThemeMetric < RageColor > BPM_COLOR ( " NoteField " , " BPMColor " ) ;
static ThemeMetric < RageColor > STOP_COLOR ( " NoteField " , " StopColor " ) ;
static ThemeMetric < RageColor > DELAY_COLOR ( " NoteField " , " DelayColor " ) ;
2011-03-24 20:41:35 -04:00
static ThemeMetric < RageColor > WARP_COLOR ( " NoteField " , " WarpColor " ) ;
2015-04-24 15:55:54 -06:00
static ThemeMetric < RageColor > TIME_SIG_COLOR ( " NoteField " , " TimeSignatureColor " ) ;
2011-03-17 01:47:30 -04:00
static ThemeMetric < RageColor > TICKCOUNT_COLOR ( " NoteField " , " TickcountColor " ) ;
static ThemeMetric < RageColor > COMBO_COLOR ( " NoteField " , " ComboColor " ) ;
2011-04-05 00:35:45 -04:00
static ThemeMetric < RageColor > LABEL_COLOR ( " NoteField " , " LabelColor " ) ;
2011-05-15 15:37:46 -04:00
static ThemeMetric < RageColor > SPEED_COLOR ( " NoteField " , " SpeedColor " ) ;
2011-05-25 22:44:41 +07:00
static ThemeMetric < RageColor > SCROLL_COLOR ( " NoteField " , " ScrollColor " ) ;
2011-05-16 12:02:17 -04:00
static ThemeMetric < RageColor > FAKE_COLOR ( " NoteField " , " FakeColor " ) ;
2011-03-17 01:47:30 -04:00
static ThemeMetric < bool > BPM_IS_LEFT_SIDE ( " NoteField " , " BPMIsLeftSide " ) ;
static ThemeMetric < bool > STOP_IS_LEFT_SIDE ( " NoteField " , " StopIsLeftSide " ) ;
static ThemeMetric < bool > DELAY_IS_LEFT_SIDE ( " NoteField " , " DelayIsLeftSide " ) ;
2011-03-24 20:41:35 -04:00
static ThemeMetric < bool > WARP_IS_LEFT_SIDE ( " NoteField " , " WarpIsLeftSide " ) ;
2015-04-24 15:55:54 -06:00
static ThemeMetric < bool > TIME_SIG_IS_LEFT_SIDE ( " NoteField " , " TimeSignatureIsLeftSide " ) ;
2011-03-17 01:47:30 -04:00
static ThemeMetric < bool > TICKCOUNT_IS_LEFT_SIDE ( " NoteField " , " TickcountIsLeftSide " ) ;
static ThemeMetric < bool > COMBO_IS_LEFT_SIDE ( " NoteField " , " ComboIsLeftSide " ) ;
2011-04-05 00:35:45 -04:00
static ThemeMetric < bool > LABEL_IS_LEFT_SIDE ( " NoteField " , " LabelIsLeftSide " ) ;
2011-05-15 15:37:46 -04:00
static ThemeMetric < bool > SPEED_IS_LEFT_SIDE ( " NoteField " , " SpeedIsLeftSide " ) ;
2011-05-25 22:44:41 +07:00
static ThemeMetric < bool > SCROLL_IS_LEFT_SIDE ( " NoteField " , " ScrollIsLeftSide " ) ;
2011-05-16 12:02:17 -04:00
static ThemeMetric < bool > FAKE_IS_LEFT_SIDE ( " NoteField " , " FakeIsLeftSide " ) ;
2011-03-17 01:47:30 -04:00
static ThemeMetric < float > BPM_OFFSETX ( " NoteField " , " BPMOffsetX " ) ;
static ThemeMetric < float > STOP_OFFSETX ( " NoteField " , " StopOffsetX " ) ;
static ThemeMetric < float > DELAY_OFFSETX ( " NoteField " , " DelayOffsetX " ) ;
2011-03-24 20:41:35 -04:00
static ThemeMetric < float > WARP_OFFSETX ( " NoteField " , " WarpOffsetX " ) ;
2015-04-24 15:55:54 -06:00
static ThemeMetric < float > TIME_SIG_OFFSETX ( " NoteField " , " TimeSignatureOffsetX " ) ;
2011-03-17 01:47:30 -04:00
static ThemeMetric < float > TICKCOUNT_OFFSETX ( " NoteField " , " TickcountOffsetX " ) ;
static ThemeMetric < float > COMBO_OFFSETX ( " NoteField " , " ComboOffsetX " ) ;
2011-04-05 00:35:45 -04:00
static ThemeMetric < float > LABEL_OFFSETX ( " NoteField " , " LabelOffsetX " ) ;
2011-05-15 15:37:46 -04:00
static ThemeMetric < float > SPEED_OFFSETX ( " NoteField " , " SpeedOffsetX " ) ;
2011-05-25 22:44:41 +07:00
static ThemeMetric < float > SCROLL_OFFSETX ( " NoteField " , " ScrollOffsetX " ) ;
2011-05-16 12:02:17 -04:00
static ThemeMetric < float > FAKE_OFFSETX ( " NoteField " , " FakeOffsetX " ) ;
2011-05-13 14:14:57 +07:00
2015-04-24 15:55:54 -06:00
void NoteField : : set_text_measure_number_for_draw (
const float beat , const float side_sign , float x_offset ,
const float horiz_align , const RageColor & color , const RageColor & glow )
2011-03-17 01:47:30 -04:00
{
2015-04-24 15:55:54 -06:00
const float y_offset = ArrowEffects : : GetYOffset ( m_pPlayerState , 0 , beat ) ;
const float y_pos = ArrowEffects : : GetYPos ( 0 , y_offset , m_fYReverseOffsetPixels ) ;
const float zoom = ArrowEffects : : GetZoom ( m_pPlayerState ) ;
const float x_base = GetWidth ( ) * .5f ;
x_offset * = zoom ;
m_textMeasureNumber . SetZoom ( zoom ) ;
m_textMeasureNumber . SetHorizAlign ( horiz_align ) ;
m_textMeasureNumber . SetDiffuse ( color ) ;
m_textMeasureNumber . SetGlow ( glow ) ;
m_textMeasureNumber . SetXY ( ( x_offset + x_base ) * side_sign , y_pos ) ;
2011-03-17 01:47:30 -04:00
}
2015-04-24 15:55:54 -06:00
void NoteField : : draw_timing_segment_text ( const RString & text ,
const float beat , const float side_sign , float x_offset ,
const float horiz_align , const RageColor & color , const RageColor & glow )
2011-03-17 01:47:30 -04:00
{
2015-04-24 15:55:54 -06:00
set_text_measure_number_for_draw ( beat , side_sign , x_offset , horiz_align ,
color , glow ) ;
m_textMeasureNumber . SetText ( text ) ;
2011-03-17 01:47:30 -04:00
m_textMeasureNumber . Draw ( ) ;
}
2015-04-24 15:55:54 -06:00
void NoteField : : DrawAttackText ( const float beat , const Attack & attack ,
const RageColor & glow )
2011-04-05 00:35:45 -04:00
{
2015-04-24 15:55:54 -06:00
set_text_measure_number_for_draw ( beat , 1 , 10 , align_left ,
RageColor ( 0 , 0.8f , 0.8f , 1 ) , glow ) ;
2011-03-17 01:47:30 -04:00
m_textMeasureNumber . SetText ( attack . GetTextDescription ( ) ) ;
m_textMeasureNumber . Draw ( ) ;
}
2015-04-24 15:55:54 -06:00
void NoteField : : DrawBGChangeText ( const float beat , const RString new_bg_name ,
const RageColor & glow )
2011-03-17 01:47:30 -04:00
{
2015-04-24 15:55:54 -06:00
set_text_measure_number_for_draw ( beat , 1 , 0 , align_left , RageColor ( 0 , 1 , 0 , 1 ) ,
glow ) ;
m_textMeasureNumber . SetText ( new_bg_name ) ;
2011-03-17 01:47:30 -04:00
m_textMeasureNumber . Draw ( ) ;
}
2011-11-11 18:40:34 +07:00
static CacheNoteStat GetNumNotesFromBeginning ( const PlayerState * pPlayerState , float beat )
2011-03-17 01:47:30 -04:00
{
2011-09-19 12:50:18 +07:00
// XXX: I realized that I have copied and pasted my binary search code 3 times already.
// how can we abstract this?
const vector < CacheNoteStat > & data = pPlayerState - > m_CacheNoteStat ;
int max = data . size ( ) - 1 ;
int l = 0 , r = max ;
while ( l < = r )
2011-03-17 01:47:30 -04:00
{
2011-09-19 12:50:18 +07:00
int m = ( l + r ) / 2 ;
if ( ( m = = 0 | | data [ m ] . beat < = beat ) & & ( m = = max | | beat < data [ m + 1 ] . beat ) )
{
return data [ m ] ;
}
else if ( data [ m ] . beat < = beat )
{
l = m + 1 ;
}
else
{
r = m - 1 ;
}
2011-03-17 01:47:30 -04:00
}
2011-09-19 12:50:18 +07:00
CacheNoteStat dummy = { 0 , 0 , 0 } ;
return dummy ;
}
2011-11-11 18:40:34 +07:00
static int GetNumNotesRange ( const PlayerState * pPlayerState , float fLow , float fHigh )
2011-09-19 12:50:18 +07:00
{
CacheNoteStat low = GetNumNotesFromBeginning ( pPlayerState , fLow ) ;
CacheNoteStat high = GetNumNotesFromBeginning ( pPlayerState , fHigh ) ;
return high . notesUpper - low . notesLower ;
}
2011-03-17 01:47:30 -04:00
2011-09-19 12:50:18 +07:00
float FindFirstDisplayedBeat ( const PlayerState * pPlayerState , int iDrawDistanceAfterTargetsPixels )
{
float fLow = 0 , fHigh = pPlayerState - > GetDisplayedPosition ( ) . m_fSongBeat ;
bool bHasCache = pPlayerState - > m_CacheNoteStat . size ( ) > 0 ;
if ( ! bHasCache )
{
fLow = fHigh - 4.0f ;
}
const int NUM_ITERATIONS = 24 ;
2011-11-11 18:40:34 +07:00
const int MAX_NOTES_AFTER = 64 ;
2011-09-19 12:50:18 +07:00
float fFirstBeatToDraw = fLow ;
for ( int i = 0 ; i < NUM_ITERATIONS ; i + + )
2011-03-17 01:47:30 -04:00
{
2011-09-19 12:50:18 +07:00
float fMid = ( fLow + fHigh ) / 2.0f ;
2011-03-17 01:47:30 -04:00
bool bIsPastPeakYOffset ;
float fPeakYOffset ;
2011-09-19 12:50:18 +07:00
float fYOffset = ArrowEffects : : GetYOffset ( pPlayerState , 0 , fMid , fPeakYOffset , bIsPastPeakYOffset , true ) ;
if ( fYOffset < iDrawDistanceAfterTargetsPixels | | ( bHasCache & & GetNumNotesRange ( pPlayerState , fMid , pPlayerState - > GetDisplayedPosition ( ) . m_fSongBeat ) > MAX_NOTES_AFTER ) ) // off screen / too many notes
{
fFirstBeatToDraw = fMid ; // move towards fSongBeat
fLow = fMid ;
}
else // on screen, move away!!
{
fHigh = fMid ;
}
2011-03-17 01:47:30 -04:00
}
2011-09-19 12:50:18 +07:00
2011-03-17 01:47:30 -04:00
return fFirstBeatToDraw ;
2011-09-19 12:50:18 +07:00
2011-03-17 01:47:30 -04:00
}
float FindLastDisplayedBeat ( const PlayerState * pPlayerState , int iDrawDistanceBeforeTargetsPixels )
{
// Probe for last note to draw. Worst case is 0.25x + boost.
// Adjust search distance so that notes don't pop onto the screen.
float fSearchDistance = 10 ;
2011-09-14 12:20:42 +07:00
float fLastBeatToDraw = pPlayerState - > GetDisplayedPosition ( ) . m_fSongBeat + fSearchDistance ;
float fSpeedMultiplier = pPlayerState - > GetDisplayedTiming ( ) . GetDisplayedSpeedPercent ( pPlayerState - > GetDisplayedPosition ( ) . m_fSongBeatVisible , pPlayerState - > GetDisplayedPosition ( ) . m_fMusicSecondsVisible ) ;
2011-03-17 01:47:30 -04:00
const int NUM_ITERATIONS = 20 ;
bool bBoomerang ;
{
const float * fAccels = pPlayerState - > m_PlayerOptions . GetCurrent ( ) . m_fAccels ;
bBoomerang = ( fAccels [ PlayerOptions : : ACCEL_BOOMERANG ] ! = 0 ) ;
}
for ( int i = 0 ; i < NUM_ITERATIONS ; i + + )
{
bool bIsPastPeakYOffset ;
float fPeakYOffset ;
float fYOffset = ArrowEffects : : GetYOffset ( pPlayerState , 0 , fLastBeatToDraw , fPeakYOffset , bIsPastPeakYOffset , true ) ;
if ( bBoomerang & & ! bIsPastPeakYOffset )
fLastBeatToDraw + = fSearchDistance ;
else if ( fYOffset > iDrawDistanceBeforeTargetsPixels ) // off screen
fLastBeatToDraw - = fSearchDistance ;
else // on screen
fLastBeatToDraw + = fSearchDistance ;
fSearchDistance / = 2 ;
}
2011-05-24 12:44:48 +07:00
if ( fSpeedMultiplier < 0.75 )
{
2011-09-14 12:20:42 +07:00
fLastBeatToDraw = min ( fLastBeatToDraw , pPlayerState - > GetDisplayedPosition ( ) . m_fSongBeat + 16 ) ;
2011-05-24 12:44:48 +07:00
}
2011-03-17 01:47:30 -04:00
return fLastBeatToDraw ;
}
bool NoteField : : IsOnScreen ( float fBeat , int iCol , int iDrawDistanceAfterTargetsPixels , int iDrawDistanceBeforeTargetsPixels ) const
{
2014-12-25 05:02:28 -07:00
// IMPORTANT: Do not modify this function without also modifying the
// version that is in NoteDisplay.cpp or coming up with a good way to
// merge them. -Kyz
2011-03-17 01:47:30 -04:00
// TRICKY: If boomerang is on, then ones in the range
// [iFirstRowToDraw,iLastRowToDraw] aren't necessarily visible.
// Test to see if this beat is visible before drawing.
float fYOffset = ArrowEffects : : GetYOffset ( m_pPlayerState , iCol , fBeat ) ;
if ( fYOffset > iDrawDistanceBeforeTargetsPixels ) // off screen
return false ;
if ( fYOffset < iDrawDistanceAfterTargetsPixels ) // off screen
return false ;
return true ;
}
2015-04-03 14:19:28 -06:00
void NoteField : : CalcPixelsBeforeAndAfterTargets ( )
2011-03-17 01:47:30 -04:00
{
2015-04-03 14:19:28 -06:00
const PlayerOptions & curr_options = m_pPlayerState - > m_PlayerOptions . GetCurrent ( ) ;
2011-03-17 01:47:30 -04:00
// Adjust draw range depending on some effects
2015-01-01 04:36:20 -07:00
m_FieldRenderArgs . draw_pixels_after_targets = m_iDrawDistanceAfterTargetsPixels ;
2011-03-17 01:47:30 -04:00
// HACK: If boomerang and centered are on, then we want to draw much
// earlier so that the notes don't pop on screen.
2015-04-03 14:19:28 -06:00
float centered_times_boomerang =
curr_options . m_fScrolls [ PlayerOptions : : SCROLL_CENTERED ] *
curr_options . m_fAccels [ PlayerOptions : : ACCEL_BOOMERANG ] ;
m_FieldRenderArgs . draw_pixels_after_targets + =
int ( SCALE ( centered_times_boomerang , 0.f , 1.f , 0.f , - SCREEN_HEIGHT / 2 ) ) ;
m_FieldRenderArgs . draw_pixels_before_targets =
m_iDrawDistanceBeforeTargetsPixels ;
float draw_scale = 1 ;
draw_scale * = 1 + 0.5f * fabsf ( curr_options . m_fPerspectiveTilt ) ;
draw_scale * = 1 + fabsf ( curr_options . m_fEffects [ PlayerOptions : : EFFECT_MINI ] ) ;
m_FieldRenderArgs . draw_pixels_after_targets =
( int ) ( m_FieldRenderArgs . draw_pixels_after_targets * draw_scale ) ;
m_FieldRenderArgs . draw_pixels_before_targets =
( int ) ( m_FieldRenderArgs . draw_pixels_before_targets * draw_scale ) ;
}
void NoteField : : DrawPrimitives ( )
{
//LOG->Trace( "NoteField::DrawPrimitives()" );
2011-03-17 01:47:30 -04:00
2015-04-03 14:19:28 -06:00
// This should be filled in on the first update.
ASSERT ( m_pCurDisplay ! = NULL ) ;
2011-03-17 01:47:30 -04:00
2015-04-03 14:19:28 -06:00
// ArrowEffects::Update call moved because having it happen once per
// NoteField (which means twice in two player) seemed wasteful. -Kyz
2011-03-17 01:47:30 -04:00
2015-04-03 14:19:28 -06:00
if ( m_drawing_board_primitive )
{
CalcPixelsBeforeAndAfterTargets ( ) ;
DrawBoard ( m_FieldRenderArgs . draw_pixels_after_targets ,
m_FieldRenderArgs . draw_pixels_before_targets ) ;
return ;
}
// Some might prefer an else block, instead of returning from the if, but I
// don't want to bump the indent on the entire remaining section. -Kyz
2015-05-02 18:53:00 -06:00
ArrowEffects : : SetCurrentOptions ( & m_pPlayerState - > m_PlayerOptions . GetCurrent ( ) ) ;
2011-03-17 01:47:30 -04:00
2015-04-03 14:19:28 -06:00
CalcPixelsBeforeAndAfterTargets ( ) ;
NoteDisplayCols * cur = m_pCurDisplay ;
2011-03-17 01:47:30 -04:00
// Probe for first and last notes on the screen
2015-04-03 14:19:28 -06:00
float first_beat_to_draw = FindFirstDisplayedBeat (
m_pPlayerState , m_FieldRenderArgs . draw_pixels_after_targets ) ;
float last_beat_to_draw = FindLastDisplayedBeat (
m_pPlayerState , m_FieldRenderArgs . draw_pixels_before_targets ) ;
2011-03-17 01:47:30 -04:00
2015-04-03 14:19:28 -06:00
m_pPlayerState - > m_fLastDrawnBeat = last_beat_to_draw ;
2011-03-17 01:47:30 -04:00
2015-04-03 14:19:28 -06:00
m_FieldRenderArgs . first_row = BeatToNoteRow ( first_beat_to_draw ) ;
m_FieldRenderArgs . last_row = BeatToNoteRow ( last_beat_to_draw ) ;
2011-03-17 01:47:30 -04:00
2015-04-03 14:19:28 -06:00
//LOG->Trace( "start = %f.1, end = %f.1", first_beat_to_draw-fSongBeat, last_beat_to_draw-fSongBeat );
2015-01-01 04:36:20 -07:00
//LOG->Trace( "Drawing elements %d through %d", m_FieldRenderArgs.first_row, m_FieldRenderArgs.last_row );
2011-03-17 01:47:30 -04:00
2015-04-03 14:19:28 -06:00
# define IS_ON_SCREEN(fBeat) (first_beat_to_draw <= (fBeat) && (fBeat) <= last_beat_to_draw && IsOnScreen(fBeat, 0, m_FieldRenderArgs.draw_pixels_after_targets, m_FieldRenderArgs.draw_pixels_before_targets))
2011-03-17 01:47:30 -04:00
// Draw Receptors
{
cur - > m_ReceptorArrowRow . Draw ( ) ;
}
2011-09-14 12:20:42 +07:00
const TimingData * pTiming = & m_pPlayerState - > GetDisplayedTiming ( ) ;
2011-09-15 03:28:58 +00:00
const vector < TimingSegment * > * segs [ NUM_TimingSegmentType ] ;
FOREACH_TimingSegmentType ( tst )
segs [ tst ] = & ( pTiming - > GetTimingSegments ( tst ) ) ;
2011-07-14 14:33:07 -04:00
unsigned i = 0 ;
2011-03-17 01:47:30 -04:00
// Draw beat bars
2011-05-13 14:14:57 +07:00
if ( ( GAMESTATE - > IsEditing ( ) | | SHOW_BEAT_BARS ) & & pTiming ! = NULL )
2011-03-17 01:47:30 -04:00
{
2011-09-15 03:28:58 +00:00
const vector < TimingSegment * > & tSigs = * segs [ SEGMENT_TIME_SIG ] ;
2011-03-17 01:47:30 -04:00
int iMeasureIndex = 0 ;
2011-07-14 14:33:07 -04:00
for ( i = 0 ; i < tSigs . size ( ) ; i + + )
2011-03-17 01:47:30 -04:00
{
2011-09-15 03:28:58 +00:00
const TimeSignatureSegment * ts = ToTimeSignature ( tSigs [ i ] ) ;
2015-01-01 04:36:20 -07:00
int iSegmentEndRow = ( i + 1 = = tSigs . size ( ) ) ? m_FieldRenderArgs . last_row : tSigs [ i + 1 ] - > GetRow ( ) ;
2011-08-24 12:54:30 -05:00
2011-03-17 01:47:30 -04:00
// beat bars every 16th note
2011-07-14 14:33:07 -04:00
int iDrawBeatBarsEveryRows = BeatToNoteRow ( ( ( float ) ts - > GetDen ( ) ) / 4 ) / 4 ;
2011-03-17 01:47:30 -04:00
// In 4/4, every 16th beat bar is a measure
2011-07-14 14:33:07 -04:00
int iMeasureBarFrequency = ts - > GetNum ( ) * 4 ;
2011-03-17 01:47:30 -04:00
int iBeatBarsDrawn = 0 ;
2011-07-14 14:33:07 -04:00
for ( int j = ts - > GetRow ( ) ; j < iSegmentEndRow ; j + = iDrawBeatBarsEveryRows )
2011-03-17 01:47:30 -04:00
{
bool bMeasureBar = iBeatBarsDrawn % iMeasureBarFrequency = = 0 ;
BeatBarType type = quarter_beat ;
if ( bMeasureBar )
type = measure ;
else if ( iBeatBarsDrawn % 4 = = 0 )
type = beat ;
else if ( iBeatBarsDrawn % 2 = = 0 )
type = half_beat ;
2011-07-14 14:33:07 -04:00
float fBeat = NoteRowToBeat ( j ) ;
2011-03-17 01:47:30 -04:00
if ( IS_ON_SCREEN ( fBeat ) )
{
DrawBeatBar ( fBeat , type , iMeasureIndex ) ;
}
iBeatBarsDrawn + + ;
if ( bMeasureBar )
iMeasureIndex + + ;
}
}
}
2011-05-13 14:14:57 +07:00
if ( GAMESTATE - > IsEditing ( ) & & pTiming ! = NULL )
2011-03-17 01:47:30 -04:00
{
2012-12-27 11:38:53 -05:00
ASSERT ( GAMESTATE - > m_pCurSong ! = NULL ) ;
2011-03-17 01:47:30 -04:00
2011-05-13 14:14:57 +07:00
const TimingData & timing = * pTiming ;
2016-02-07 21:04:10 -07:00
const RageColor text_glow = RageColor ( 1 , 1 , 1 , RageFastCos ( RageTimer : : GetTimeSinceStartFast ( ) * 2 ) / 2 + 0.5f ) ;
2015-04-24 15:55:54 -06:00
float horiz_align = align_right ;
float side_sign = 1 ;
# define draw_all_segments(str_exp, name, caps_name) \
horiz_align= caps_name##_IS_LEFT_SIDE ? align_right : align_left; \
side_sign= caps_name##_IS_LEFT_SIDE ? -1 : 1; \
for(unsigned int i= 0; i < segs[SEGMENT_##caps_name]->size(); ++i) \
{ \
const name##Segment* seg= To##name((*segs[SEGMENT_##caps_name])[i]); \
if(seg->GetRow() >= m_FieldRenderArgs.first_row && \
seg->GetRow() <= m_FieldRenderArgs.last_row && \
IS_ON_SCREEN(seg->GetBeat())) \
{ \
2015-05-10 01:04:06 -06:00
draw_timing_segment_text(str_exp, seg->GetBeat(), side_sign, \
2015-04-24 15:55:54 -06:00
caps_name##_OFFSETX, horiz_align, caps_name##_COLOR, text_glow); \
} \
2011-03-17 01:47:30 -04:00
}
2011-08-24 12:54:30 -05:00
2015-04-24 15:55:54 -06:00
draw_all_segments ( FloatToString ( seg - > GetRatio ( ) ) , Scroll , SCROLL ) ;
draw_all_segments ( FloatToString ( seg - > GetBPM ( ) ) , BPM , BPM ) ;
draw_all_segments ( FloatToString ( seg - > GetPause ( ) ) , Stop , STOP ) ;
draw_all_segments ( FloatToString ( seg - > GetPause ( ) ) , Delay , DELAY ) ;
draw_all_segments ( FloatToString ( seg - > GetLength ( ) ) , Warp , WARP ) ;
draw_all_segments ( ssprintf ( " %d \n -- \n %d " , seg - > GetNum ( ) , seg - > GetDen ( ) ) ,
TimeSignature , TIME_SIG ) ;
draw_all_segments ( ssprintf ( " %d " , seg - > GetTicks ( ) ) , Tickcount , TICKCOUNT ) ;
draw_all_segments (
ssprintf ( " %d/%d " , seg - > GetCombo ( ) , seg - > GetMissCombo ( ) ) , Combo , COMBO ) ;
draw_all_segments ( seg - > GetLabel ( ) , Label , LABEL ) ;
draw_all_segments ( ssprintf ( " %s \n %s \n %s " ,
FloatToString ( seg - > GetRatio ( ) ) . c_str ( ) ,
( seg - > GetUnit ( ) = = 1 ? " S " : " B " ) ,
FloatToString ( seg - > GetDelay ( ) ) . c_str ( ) ) , Speed , SPEED ) ;
draw_all_segments ( FloatToString ( seg - > GetLength ( ) ) , Fake , FAKE ) ;
# undef draw_all_segments
2011-03-17 01:47:30 -04:00
// Course mods text
const Course * pCourse = GAMESTATE - > m_pCurCourse ;
if ( pCourse )
{
ASSERT_M ( GAMESTATE - > m_iEditCourseEntryIndex > = 0 & & GAMESTATE - > m_iEditCourseEntryIndex < ( int ) pCourse - > m_vEntries . size ( ) ,
ssprintf ( " %i " , GAMESTATE - > m_iEditCourseEntryIndex . Get ( ) ) ) ;
const CourseEntry & ce = pCourse - > m_vEntries [ GAMESTATE - > m_iEditCourseEntryIndex ] ;
FOREACH_CONST ( Attack , ce . attacks , a )
{
float fSecond = a - > fStartSecond ;
2011-05-09 21:19:40 -04:00
float fBeat = timing . GetBeatFromElapsedTime ( fSecond ) ;
2011-03-17 01:47:30 -04:00
2015-01-01 04:36:20 -07:00
if ( BeatToNoteRow ( fBeat ) > = m_FieldRenderArgs . first_row & &
2015-04-24 15:55:54 -06:00
BeatToNoteRow ( fBeat ) < = m_FieldRenderArgs . last_row & &
IS_ON_SCREEN ( fBeat ) )
2011-03-17 01:47:30 -04:00
{
2015-04-24 15:55:54 -06:00
DrawAttackText ( fBeat , * a , text_glow ) ;
2011-03-17 01:47:30 -04:00
}
}
}
2011-06-24 18:07:47 -04:00
else
{
AttackArray & attacks = GAMESTATE - > m_bIsUsingStepTiming ?
GAMESTATE - > m_pCurSteps [ PLAYER_1 ] - > m_Attacks :
GAMESTATE - > m_pCurSong - > m_Attacks ;
2011-10-17 21:37:30 -04:00
// XXX: We're somehow getting here when attacks is null. Find the actual cause later.
if ( & attacks )
2011-06-24 18:07:47 -04:00
{
2011-10-17 21:37:30 -04:00
FOREACH_CONST ( Attack , attacks , a )
2011-06-24 18:07:47 -04:00
{
2011-10-17 21:37:30 -04:00
float fBeat = timing . GetBeatFromElapsedTime ( a - > fStartSecond ) ;
2015-01-01 04:36:20 -07:00
if ( BeatToNoteRow ( fBeat ) > = m_FieldRenderArgs . first_row & &
BeatToNoteRow ( fBeat ) < = m_FieldRenderArgs . last_row & &
2011-10-17 21:37:30 -04:00
IS_ON_SCREEN ( fBeat ) )
{
2015-04-24 15:55:54 -06:00
this - > DrawAttackText ( fBeat , * a , text_glow ) ;
2011-10-17 21:37:30 -04:00
}
2011-06-24 18:07:47 -04:00
}
}
}
2011-08-24 12:54:30 -05:00
2011-05-17 00:07:43 -04:00
if ( ! GAMESTATE - > m_bIsUsingStepTiming )
2011-03-17 01:47:30 -04:00
{
2011-05-12 22:34:16 -04:00
// BGChange text
2012-12-27 16:59:35 -05:00
EditMode mode = GAMESTATE - > m_EditMode ;
switch ( mode )
2011-05-12 22:34:16 -04:00
{
case EditMode_Home :
case EditMode_CourseMods :
case EditMode_Practice :
break ;
case EditMode_Full :
2011-03-17 01:47:30 -04:00
{
2011-05-12 22:34:16 -04:00
vector < BackgroundChange > : : iterator iter [ NUM_BackgroundLayer ] ;
2011-07-14 14:33:07 -04:00
FOREACH_BackgroundLayer ( j )
iter [ j ] = GAMESTATE - > m_pCurSong - > GetBackgroundChanges ( j ) . begin ( ) ;
2011-08-24 12:54:30 -05:00
2016-03-27 21:52:05 -04:00
for ( ; ; )
2011-03-17 01:47:30 -04:00
{
2011-05-12 22:34:16 -04:00
float fLowestBeat = FLT_MAX ;
vector < BackgroundLayer > viLowestIndex ;
2011-08-24 12:54:30 -05:00
2011-07-14 14:33:07 -04:00
FOREACH_BackgroundLayer ( j )
2011-03-17 01:47:30 -04:00
{
2011-07-14 14:33:07 -04:00
if ( iter [ j ] = = GAMESTATE - > m_pCurSong - > GetBackgroundChanges ( j ) . end ( ) )
2016-03-27 21:52:05 -04:00
{
2011-05-12 22:34:16 -04:00
continue ;
2016-03-27 21:52:05 -04:00
}
2011-07-14 14:33:07 -04:00
float fBeat = iter [ j ] - > m_fStartBeat ;
2011-05-12 22:34:16 -04:00
if ( fBeat < fLowestBeat )
{
fLowestBeat = fBeat ;
viLowestIndex . clear ( ) ;
2011-07-14 14:33:07 -04:00
viLowestIndex . push_back ( j ) ;
2011-05-12 22:34:16 -04:00
}
else if ( fBeat = = fLowestBeat )
{
2011-07-14 14:33:07 -04:00
viLowestIndex . push_back ( j ) ;
2011-05-12 22:34:16 -04:00
}
2011-03-17 01:47:30 -04:00
}
2011-05-12 22:34:16 -04:00
if ( viLowestIndex . empty ( ) )
2011-03-17 01:47:30 -04:00
{
2011-07-14 14:33:07 -04:00
FOREACH_BackgroundLayer ( j )
2016-03-27 21:52:05 -04:00
{
2011-07-14 14:33:07 -04:00
ASSERT ( iter [ j ] = = GAMESTATE - > m_pCurSong - > GetBackgroundChanges ( j ) . end ( ) ) ;
2016-03-27 21:52:05 -04:00
}
2011-05-12 22:34:16 -04:00
break ;
2011-03-17 01:47:30 -04:00
}
2011-09-15 03:28:58 +00:00
2011-05-12 22:34:16 -04:00
if ( IS_ON_SCREEN ( fLowestBeat ) )
2011-03-17 01:47:30 -04:00
{
2011-05-12 22:34:16 -04:00
vector < RString > vsBGChanges ;
2011-07-14 14:33:07 -04:00
FOREACH_CONST ( BackgroundLayer , viLowestIndex , bl )
2011-05-12 22:34:16 -04:00
{
2011-07-14 14:33:07 -04:00
ASSERT ( iter [ * bl ] ! = GAMESTATE - > m_pCurSong - > GetBackgroundChanges ( * bl ) . end ( ) ) ;
const BackgroundChange & change = * iter [ * bl ] ;
2011-05-12 22:34:16 -04:00
RString s = change . GetTextDescription ( ) ;
2011-07-14 14:33:07 -04:00
if ( * bl ! = 0 )
2016-03-27 21:52:05 -04:00
{
2011-07-14 14:33:07 -04:00
s = ssprintf ( " %d: " , * bl ) + s ;
2016-03-27 21:52:05 -04:00
}
2011-05-12 22:34:16 -04:00
vsBGChanges . push_back ( s ) ;
}
2015-04-24 15:55:54 -06:00
DrawBGChangeText ( fLowestBeat , join ( " \n " , vsBGChanges ) , text_glow ) ;
2011-03-17 01:47:30 -04:00
}
2011-07-14 14:33:07 -04:00
FOREACH_CONST ( BackgroundLayer , viLowestIndex , bl )
2016-03-27 21:52:05 -04:00
{
2011-07-14 14:33:07 -04:00
iter [ * bl ] + + ;
2016-03-27 21:52:05 -04:00
}
2011-03-17 01:47:30 -04:00
}
}
2011-05-12 22:34:16 -04:00
break ;
default :
2012-12-27 16:59:35 -05:00
FAIL_M ( ssprintf ( " Invalid edit mode: %i " , mode ) ) ;
2011-05-12 22:34:16 -04:00
}
2011-03-17 01:47:30 -04:00
}
// Draw marker bars
if ( m_iBeginMarker ! = - 1 & & m_iEndMarker ! = - 1 )
{
int iBegin = m_iBeginMarker ;
int iEnd = m_iEndMarker ;
2015-01-01 04:36:20 -07:00
CLAMP ( iBegin , m_FieldRenderArgs . first_row , m_FieldRenderArgs . last_row ) ;
CLAMP ( iEnd , m_FieldRenderArgs . first_row , m_FieldRenderArgs . last_row ) ;
2011-03-17 01:47:30 -04:00
DrawAreaHighlight ( iBegin , iEnd ) ;
}
else if ( m_iBeginMarker ! = - 1 )
{
2015-01-01 04:36:20 -07:00
if ( m_iBeginMarker > = m_FieldRenderArgs . first_row & &
m_iBeginMarker < = m_FieldRenderArgs . last_row )
2011-03-17 01:47:30 -04:00
DrawMarkerBar ( m_iBeginMarker ) ;
}
else if ( m_iEndMarker ! = - 1 )
{
2015-01-01 04:36:20 -07:00
if ( m_iEndMarker > = m_FieldRenderArgs . first_row & &
m_iEndMarker < = m_FieldRenderArgs . last_row )
2011-03-17 01:47:30 -04:00
DrawMarkerBar ( m_iEndMarker ) ;
}
}
// Optimization is very important here because there are so many arrows to draw.
// Draw the arrows in order of column. This minimizes texture switches and
// lets us draw in big batches.
2014-12-07 01:53:17 -07:00
const Style * pStyle = GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) ;
ASSERT_M ( m_pNoteData - > GetNumTracks ( ) = = GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) - > m_iColsPerPlayer ,
2015-01-27 18:03:41 -07:00
ssprintf ( " NumTracks %d != ColsPerPlayer %d " , m_pNoteData - > GetNumTracks ( ) ,
GAMESTATE - > GetCurrentStyle ( m_pPlayerState - > m_PlayerNumber ) - > m_iColsPerPlayer ) ) ;
2011-03-17 01:47:30 -04:00
2015-04-24 15:55:54 -06:00
if ( * m_FieldRenderArgs . selection_begin_marker ! = - 1 & &
* m_FieldRenderArgs . selection_end_marker ! = - 1 )
{
m_FieldRenderArgs . selection_glow = SCALE (
2016-02-07 21:04:10 -07:00
RageFastCos ( RageTimer : : GetTimeSinceStartFast ( ) * 2 ) , - 1 , 1 , 0.1f , 0.3f ) ;
2015-04-24 15:55:54 -06:00
}
2015-01-01 04:36:20 -07:00
m_FieldRenderArgs . fade_before_targets = FADE_BEFORE_TARGETS_PERCENT ;
2014-12-26 01:54:34 -07:00
2011-07-14 14:33:07 -04:00
for ( int j = 0 ; j < m_pNoteData - > GetNumTracks ( ) ; j + + ) // for each arrow column
2011-03-17 01:47:30 -04:00
{
2011-07-14 14:33:07 -04:00
const int c = pStyle - > m_iColumnDrawOrder [ j ] ;
2014-12-26 01:54:34 -07:00
m_ColumnRenderers [ c ] . Draw ( ) ;
2011-03-17 01:47:30 -04:00
}
cur - > m_GhostArrowRow . Draw ( ) ;
}
2015-04-03 14:19:28 -06:00
void NoteField : : DrawBoardPrimitive ( )
{
if ( ! SHOW_BOARD )
{
return ;
}
m_drawing_board_primitive = true ;
Draw ( ) ;
m_drawing_board_primitive = false ;
}
2011-03-17 01:47:30 -04:00
void NoteField : : FadeToFail ( )
{
2015-01-01 04:36:20 -07:00
m_FieldRenderArgs . fail_fade = max ( 0.0f , m_FieldRenderArgs . fail_fade ) ; // this will slowly increase every Update()
2011-03-17 01:47:30 -04:00
// don't fade all over again if this is called twice
}
2014-11-30 23:17:12 -07:00
// A few functions and macros to take care of processing the callback
// return values, since the code would be identical in all of them. -Kyz
# define OPEN_CALLBACK_BLOCK(member_name) \
if(!from_lua && !member_name.IsNil()) \
{ \
Lua* L= LUA->Get(); \
member_name.PushSelf(L);
# define OPEN_RUN_BLOCK(arg_count) \
RString error= "Error running callback: "; \
if(LuaHelpers::RunScriptOnStack(L, error, arg_count, arg_count, true)) \
{
# define CLOSE_RUN_AND_CALLBACK_BLOCKS } lua_settop(L, 0); LUA->Release(L); }
2014-12-18 02:36:45 -07:00
# define PUSH_COLUMN lua_pushnumber(L, col+1)
2014-11-30 23:17:12 -07:00
2014-12-10 18:14:00 -07:00
static void get_returned_column ( Lua * L , PlayerNumber pn , int index , int & col )
2014-11-30 23:17:12 -07:00
{
if ( lua_isnumber ( L , index ) )
{
// 1-indexed columns in lua
int tmpcol = lua_tonumber ( L , index ) - 1 ;
2014-12-10 18:14:00 -07:00
if ( tmpcol < 0 | | tmpcol > = GAMESTATE - > GetCurrentStyle ( pn ) - > m_iColsPerPlayer )
2014-11-30 23:17:12 -07:00
{
LuaHelpers : : ReportScriptErrorFmt (
" Column returned by callback must be between 1 and %d "
" (GAMESTATE:GetCurrentStyle():ColumnsPerPlayer()). " ,
2014-12-10 18:14:00 -07:00
GAMESTATE - > GetCurrentStyle ( pn ) - > m_iColsPerPlayer ) ;
2014-11-30 23:17:12 -07:00
}
else
{
col = tmpcol ;
}
}
}
// Templated so it can be used for TNS and HNS. -Kyz
template < class T > static void get_returned_score ( Lua * L , int index , T & score )
{
T maybe_score = Enum : : Check < T > ( L , index , true , true ) ;
if ( maybe_score ! = EnumTraits < T > : : Invalid )
{
score = maybe_score ;
}
}
static void get_returned_bright ( Lua * L , int index , bool & bright )
{
if ( lua_isboolean ( L , index ) )
{
bright = lua_toboolean ( L , index ) ;
}
}
void NoteField : : Step ( int col , TapNoteScore score , bool from_lua )
{
OPEN_CALLBACK_BLOCK ( m_StepCallback ) ;
2014-12-18 02:36:45 -07:00
PUSH_COLUMN ;
2014-11-30 23:17:12 -07:00
Enum : : Push ( L , score ) ;
OPEN_RUN_BLOCK ( 2 ) ;
2014-12-10 18:14:00 -07:00
get_returned_column ( L , m_pPlayerState - > m_PlayerNumber , 1 , col ) ;
2014-11-30 23:17:12 -07:00
get_returned_score ( L , 2 , score ) ;
CLOSE_RUN_AND_CALLBACK_BLOCKS ;
m_pCurDisplay - > m_ReceptorArrowRow . Step ( col , score ) ;
}
void NoteField : : SetPressed ( int col , bool from_lua )
{
OPEN_CALLBACK_BLOCK ( m_SetPressedCallback ) ;
2014-12-18 02:36:45 -07:00
PUSH_COLUMN ;
2014-11-30 23:17:12 -07:00
OPEN_RUN_BLOCK ( 1 ) ;
2014-12-10 18:14:00 -07:00
get_returned_column ( L , m_pPlayerState - > m_PlayerNumber , 1 , col ) ;
2014-11-30 23:17:12 -07:00
CLOSE_RUN_AND_CALLBACK_BLOCKS ;
m_pCurDisplay - > m_ReceptorArrowRow . SetPressed ( col ) ;
}
void NoteField : : DidTapNote ( int col , TapNoteScore score , bool bright , bool from_lua )
{
OPEN_CALLBACK_BLOCK ( m_DidTapNoteCallback ) ;
2014-12-18 02:36:45 -07:00
PUSH_COLUMN ;
2014-11-30 23:17:12 -07:00
Enum : : Push ( L , score ) ;
lua_pushboolean ( L , bright ) ;
OPEN_RUN_BLOCK ( 3 ) ;
2014-12-10 18:14:00 -07:00
get_returned_column ( L , m_pPlayerState - > m_PlayerNumber , 1 , col ) ;
2014-11-30 23:17:12 -07:00
get_returned_score ( L , 2 , score ) ;
get_returned_bright ( L , 3 , bright ) ;
CLOSE_RUN_AND_CALLBACK_BLOCKS ;
m_pCurDisplay - > m_GhostArrowRow . DidTapNote ( col , score , bright ) ;
}
void NoteField : : DidHoldNote ( int col , HoldNoteScore score , bool bright , bool from_lua )
{
OPEN_CALLBACK_BLOCK ( m_DidHoldNoteCallback ) ;
2014-12-18 02:36:45 -07:00
PUSH_COLUMN ;
2014-11-30 23:17:12 -07:00
Enum : : Push ( L , score ) ;
lua_pushboolean ( L , bright ) ;
OPEN_RUN_BLOCK ( 3 ) ;
2014-12-10 18:14:00 -07:00
get_returned_column ( L , m_pPlayerState - > m_PlayerNumber , 1 , col ) ;
2014-11-30 23:17:12 -07:00
get_returned_score ( L , 2 , score ) ;
get_returned_bright ( L , 3 , bright ) ;
CLOSE_RUN_AND_CALLBACK_BLOCKS ;
m_pCurDisplay - > m_GhostArrowRow . DidHoldNote ( col , score , bright ) ;
}
# undef OPEN_CALLBACK_BLOCK
# undef OPEN_RUN_BLOCK
# undef CLOSE_RUN_AND_CALLBACK_BLOCKS
2014-12-18 02:36:45 -07:00
# undef PUSH_COLUMN
2011-03-17 01:47:30 -04:00
void NoteField : : HandleMessage ( const Message & msg )
{
if ( msg = = Message_CurrentSongChanged )
{
m_fCurrentBeatLastUpdate = - 1 ;
m_fYPosCurrentBeatLastUpdate = - 1 ;
}
ActorFrame : : HandleMessage ( msg ) ;
}
2014-11-30 23:17:12 -07:00
// lua start
# include "LuaBinding.h"
/** @brief Allow Lua to have access to the Notefield. */
class LunaNoteField : public Luna < NoteField >
{
public :
# define SET_CALLBACK_GENERIC(callback_name, member_name) \
static int callback_name(T* p, lua_State* L) \
{ \
if(lua_isnoneornil(L, 1)) \
{ \
p->member_name.SetFromNil(); \
} \
else if(lua_isfunction(L, 1)) \
{ \
p->member_name.SetFromStack(L); \
} \
else \
{ \
luaL_error(L, #callback_name "Callback argument must be nil (to clear the callback) or a function (to set the callback)."); \
} \
return 0; \
}
2015-01-06 20:14:59 -07:00
SET_CALLBACK_GENERIC ( set_step_callback , m_StepCallback ) ;
SET_CALLBACK_GENERIC ( set_set_pressed_callback , m_SetPressedCallback ) ;
SET_CALLBACK_GENERIC ( set_did_tap_note_callback , m_DidTapNoteCallback ) ;
SET_CALLBACK_GENERIC ( set_did_hold_note_callback , m_DidHoldNoteCallback ) ;
2014-11-30 23:17:12 -07:00
# undef SET_CALLBACK_GENERIC
2014-12-10 18:14:00 -07:00
static int check_column ( lua_State * L , int index , PlayerNumber pn )
2014-11-30 23:17:12 -07:00
{
// 1-indexed columns in lua
int col = IArg ( 1 ) - 1 ;
2014-12-10 18:14:00 -07:00
if ( col < 0 | | col > = GAMESTATE - > GetCurrentStyle ( pn ) - > m_iColsPerPlayer )
2014-11-30 23:17:12 -07:00
{
luaL_error ( L , " Column must be between 1 and %d "
2014-12-10 18:14:00 -07:00
" (GAMESTATE:GetCurrentStyle(pn):ColumnsPerPlayer()). " ,
GAMESTATE - > GetCurrentStyle ( pn ) - > m_iColsPerPlayer ) ;
2014-11-30 23:17:12 -07:00
}
return col ;
}
2015-01-06 20:14:59 -07:00
static int step ( T * p , lua_State * L )
2014-11-30 23:17:12 -07:00
{
2014-12-10 18:14:00 -07:00
int col = check_column ( L , 1 , p - > GetPlayerState ( ) - > m_PlayerNumber ) ;
2014-11-30 23:17:12 -07:00
TapNoteScore tns = Enum : : Check < TapNoteScore > ( L , 2 ) ;
p - > Step ( col , tns , true ) ;
return 0 ;
}
2015-01-06 20:14:59 -07:00
static int set_pressed ( T * p , lua_State * L )
2014-11-30 23:17:12 -07:00
{
2014-12-10 18:14:00 -07:00
int col = check_column ( L , 1 , p - > GetPlayerState ( ) - > m_PlayerNumber ) ;
2014-11-30 23:17:12 -07:00
p - > SetPressed ( col , true ) ;
return 0 ;
}
2015-01-06 20:14:59 -07:00
static int did_tap_note ( T * p , lua_State * L )
2014-11-30 23:17:12 -07:00
{
2014-12-10 18:14:00 -07:00
int col = check_column ( L , 1 , p - > GetPlayerState ( ) - > m_PlayerNumber ) ;
2014-11-30 23:17:12 -07:00
TapNoteScore tns = Enum : : Check < TapNoteScore > ( L , 2 ) ;
bool bright = BArg ( 3 ) ;
p - > DidTapNote ( col , tns , bright , true ) ;
return 0 ;
}
2015-01-06 20:14:59 -07:00
static int did_hold_note ( T * p , lua_State * L )
2014-11-30 23:17:12 -07:00
{
2014-12-10 18:14:00 -07:00
int col = check_column ( L , 1 , p - > GetPlayerState ( ) - > m_PlayerNumber ) ;
2014-11-30 23:17:12 -07:00
HoldNoteScore hns = Enum : : Check < HoldNoteScore > ( L , 2 ) ;
bool bright = BArg ( 3 ) ;
p - > DidHoldNote ( col , hns , bright , true ) ;
return 0 ;
}
2015-01-01 04:36:20 -07:00
static int get_column_actors ( T * p , lua_State * L )
2014-12-26 01:54:34 -07:00
{
lua_createtable ( L , p - > m_ColumnRenderers . size ( ) , 0 ) ;
for ( size_t i = 0 ; i < p - > m_ColumnRenderers . size ( ) ; + + i )
{
p - > m_ColumnRenderers [ i ] . PushSelf ( L ) ;
lua_rawseti ( L , - 2 , i + 1 ) ;
}
return 1 ;
}
2014-11-30 23:17:12 -07:00
LunaNoteField ( )
{
2015-01-06 20:14:59 -07:00
ADD_METHOD ( set_step_callback ) ;
ADD_METHOD ( set_set_pressed_callback ) ;
ADD_METHOD ( set_did_tap_note_callback ) ;
ADD_METHOD ( set_did_hold_note_callback ) ;
ADD_METHOD ( step ) ;
ADD_METHOD ( set_pressed ) ;
ADD_METHOD ( did_tap_note ) ;
ADD_METHOD ( did_hold_note ) ;
2015-01-01 04:36:20 -07:00
ADD_METHOD ( get_column_actors ) ;
2014-11-30 23:17:12 -07:00
}
} ;
LUA_REGISTER_DERIVED_CLASS ( NoteField , ActorFrame )
// lua end
2011-03-17 01:47:30 -04:00
/*
* (c) 2001-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.
*/