Files
itgmania212121/stepmania/src/NoteField.cpp
T

573 lines
18 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2002-04-16 17:31:00 +00:00
/*
-----------------------------------------------------------------------------
Class: NoteField
Desc: See header.
2002-05-19 01:59:48 +00:00
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
2002-04-16 17:31:00 +00:00
Chris Danford
-----------------------------------------------------------------------------
*/
#include "NoteField.h"
#include "RageUtil.h"
2002-05-01 19:14:55 +00:00
#include "GameConstantsAndTypes.h"
2002-07-23 01:41:40 +00:00
#include "PrefsManager.h"
2002-04-16 17:31:00 +00:00
#include "ArrowEffects.h"
#include "GameManager.h"
2002-07-23 01:41:40 +00:00
#include "GameState.h"
2002-07-27 19:29:51 +00:00
#include "RageException.h"
#include "RageTimer.h"
#include "RageLog.h"
2003-07-04 18:03:20 +00:00
#include "RageMath.h"
#include <math.h>
#include "ThemeManager.h"
2003-04-02 21:58:27 +00:00
#include "NoteFieldPositioning.h"
#include "NoteSkinManager.h"
#include "song.h"
2002-04-16 17:31:00 +00:00
NoteField::NoteField()
{
m_textMeasureNumber.LoadFromFont( THEME->GetPathToF("Common normal") );
2002-04-16 17:31:00 +00:00
m_textMeasureNumber.SetZoom( 1.0f );
2003-03-02 01:43:33 +00:00
m_rectMarkerBar.EnableShadow( false );
2003-02-18 23:15:38 +00:00
m_rectMarkerBar.SetEffectDiffuseShift( 2, RageColor(1,1,1,0.5f), RageColor(0.5f,0.5f,0.5f,0.5f) );
2002-04-16 17:31:00 +00:00
m_sprBars.Load( THEME->GetPathToG("NoteField bars") );
m_sprBars.StopAnimating();
2002-04-16 17:31:00 +00:00
m_fBeginMarker = m_fEndMarker = -1;
2002-06-27 17:49:10 +00:00
2002-08-01 21:55:40 +00:00
m_fPercentFadeToFail = -1;
2002-04-16 17:31:00 +00:00
}
NoteField::~NoteField()
{
Unload();
}
void NoteField::Unload()
{
for( map<CString, NoteDisplayCols *>::iterator it = m_NoteDisplays.begin();
it != m_NoteDisplays.end(); ++it )
delete it->second;
m_NoteDisplays.clear();
}
void NoteField::CacheNoteSkin( CString skin )
{
if( m_NoteDisplays.find(skin) != m_NoteDisplays.end() )
return;
LOG->Trace("NoteField::CacheNoteSkin: cache %s", skin.c_str() );
NoteDisplayCols *nd = new NoteDisplayCols;
for( int c=0; c<GetNumTracks(); c++ )
nd->display[c].Load( c, m_PlayerNumber, skin, m_fYReverseOffsetPixels );
m_NoteDisplays[ skin ] = nd;
}
2002-04-16 17:31:00 +00:00
2003-08-18 17:19:34 +00:00
void NoteField::Load( NoteData* pNoteData, PlayerNumber pn, int iFirstPixelToDraw, int iLastPixelToDraw, float fYReverseOffsetPixels )
2002-04-16 17:31:00 +00:00
{
Unload();
2002-07-28 20:28:37 +00:00
m_PlayerNumber = pn;
m_iStartDrawingPixel = iFirstPixelToDraw;
m_iEndDrawingPixel = iLastPixelToDraw;
2003-08-18 17:19:34 +00:00
m_fYReverseOffsetPixels = fYReverseOffsetPixels;
2002-07-28 20:28:37 +00:00
2002-08-01 21:55:40 +00:00
m_fPercentFadeToFail = -1;
m_LastSeenBeatToNoteSkinRev = -1;
2002-08-01 21:55:40 +00:00
2002-12-18 22:25:24 +00:00
NoteDataWithScoring::Init();
2002-08-01 03:15:27 +00:00
2002-11-03 21:45:26 +00:00
m_bIsHoldingHoldNote.clear();
m_bIsHoldingHoldNote.insert(m_bIsHoldingHoldNote.end(), pNoteData->GetNumTapNotes(), false);
2002-08-01 03:15:27 +00:00
2002-04-16 17:31:00 +00:00
this->CopyAll( pNoteData );
2003-02-01 05:16:38 +00:00
ASSERT( GetNumTracks() == GAMESTATE->GetCurrentStyleDef()->m_iColsPerPlayer );
/* Cache the default note skin. It's up to other code to cache any other skins
* that might be used, such as ones assigned as attacks in battle modes. */
LOG->Trace("cache '%s'", GAMESTATE->m_PlayerOptions[pn].m_sNoteSkin.c_str());
CacheNoteSkin( GAMESTATE->m_PlayerOptions[pn].m_sNoteSkin );
RefreshBeatToNoteSkin();
2002-04-16 17:31:00 +00:00
}
void NoteField::RefreshBeatToNoteSkin()
2003-04-22 04:54:04 +00:00
{
if( GAMESTATE->m_BeatToNoteSkinRev == m_LastSeenBeatToNoteSkinRev )
return;
m_LastSeenBeatToNoteSkinRev = GAMESTATE->m_BeatToNoteSkinRev;
/* Set by GameState::ResetNoteSkins(): */
ASSERT( !GAMESTATE->m_BeatToNoteSkin[m_PlayerNumber].empty() );
m_BeatToNoteDisplays.clear();
/* GAMESTATE->m_BeatToNoteSkin[pn] maps from song beats to note skins. Maintain
* m_BeatToNoteDisplays, to map from song beats to NoteDisplay*s, so we don't
* have to do it while rendering. */
map<float,CString>::iterator it;
for( it = GAMESTATE->m_BeatToNoteSkin[m_PlayerNumber].begin();
it != GAMESTATE->m_BeatToNoteSkin[m_PlayerNumber].end(); ++it )
{
const float Beat = it->first;
const CString &Skin = it->second;
map<CString, NoteDisplayCols *>::iterator display = m_NoteDisplays.find( Skin );
/* If this happens, we tried to use a note skin that wasn't pre-cached. */
RAGE_ASSERT_M( display != m_NoteDisplays.end(), ssprintf("Couldn't find %s", Skin.c_str()) );
NoteDisplayCols *cols = display->second;
m_BeatToNoteDisplays[Beat] = cols;
}
2003-04-22 04:54:04 +00:00
}
2002-07-28 20:28:37 +00:00
void NoteField::Update( float fDeltaTime )
2002-04-16 17:31:00 +00:00
{
ActorFrame::Update( fDeltaTime );
2002-04-16 17:31:00 +00:00
m_rectMarkerBar.Update( fDeltaTime );
2002-08-01 21:55:40 +00:00
if( m_fPercentFadeToFail >= 0 )
2002-09-05 20:42:49 +00:00
m_fPercentFadeToFail = min( m_fPercentFadeToFail + fDeltaTime/1.5f, 1 ); // take 1.5 seconds to totally fade
RefreshBeatToNoteSkin();
2002-04-16 17:31:00 +00:00
}
float NoteField::GetWidth()
2002-04-16 17:31:00 +00:00
{
const StyleDef* pStyleDef = GAMESTATE->GetCurrentStyleDef();
float fMinX, fMaxX;
pStyleDef->GetMinAndMaxColX( m_PlayerNumber, fMinX, fMaxX );
return fMaxX - fMinX + ARROW_SIZE;
2003-02-18 23:15:38 +00:00
}
void NoteField::DrawBeatBar( const float fBeat )
{
bool bIsMeasure = fmodf( fBeat, (float)BEATS_PER_MEASURE ) == 0;
int iMeasureIndex = (int)fBeat / BEATS_PER_MEASURE;
int iMeasureNoDisplay = iMeasureIndex+1;
NoteType nt = BeatToNoteType( fBeat );
const float fYOffset = ArrowGetYOffset( m_PlayerNumber, 0, fBeat );
2003-08-18 17:19:34 +00:00
const float fYPos = ArrowGetYPos( m_PlayerNumber, 0, fYOffset, m_fYReverseOffsetPixels );
2002-04-16 17:31:00 +00:00
float fAlpha;
int iState;
if( bIsMeasure )
2003-02-18 23:15:38 +00:00
{
fAlpha = 1;
iState = 0;
2003-02-18 23:15:38 +00:00
}
else
2003-02-18 23:15:38 +00:00
{
float fScrollSpeed = GAMESTATE->m_CurrentPlayerOptions[m_PlayerNumber].m_fScrollSpeed;
switch( nt )
{
default: ASSERT(0);
case NOTE_TYPE_4TH: fAlpha = 1; iState = 1; break;
case NOTE_TYPE_8TH: fAlpha = SCALE(fScrollSpeed,1.f,2.f,0.f,1.f); iState = 2; break;
case NOTE_TYPE_16TH:fAlpha = SCALE(fScrollSpeed,2.f,4.f,0.f,1.f); iState = 3; break;
}
CLAMP( fAlpha, 0, 1 );
}
float fWidth = GetWidth();
float fFrameWidth = m_sprBars.GetUnzoomedWidth();
m_sprBars.SetX( 0 );
m_sprBars.SetY( fYPos );
m_sprBars.SetDiffuse( RageColor(1,1,1,fAlpha) );
m_sprBars.SetState( iState );
m_sprBars.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_sprBars.SetZoomX( fWidth/m_sprBars.GetUnzoomedWidth() );
m_sprBars.Draw();
2003-02-18 23:15:38 +00:00
if( bIsMeasure )
{
m_textMeasureNumber.SetDiffuse( RageColor(1,1,1,1) );
m_textMeasureNumber.SetGlow( RageColor(1,1,1,0) );
m_textMeasureNumber.SetHorizAlign( Actor::align_right );
2003-02-18 23:15:38 +00:00
m_textMeasureNumber.SetText( ssprintf("%d", iMeasureNoDisplay) );
m_textMeasureNumber.SetXY( -fWidth/2, fYPos );
2003-02-18 23:15:38 +00:00
m_textMeasureNumber.Draw();
}
2002-04-16 17:31:00 +00:00
}
void NoteField::DrawMarkerBar( const float fBeat )
2002-04-16 17:31:00 +00:00
{
const float fYOffset = ArrowGetYOffset( m_PlayerNumber, 0, fBeat );
2003-08-18 17:19:34 +00:00
const float fYPos = ArrowGetYPos( m_PlayerNumber, 0, fYOffset, m_fYReverseOffsetPixels );
2002-04-16 17:31:00 +00:00
m_rectMarkerBar.StretchTo( RectF(-GetWidth()/2, fYPos-ARROW_SIZE/2, GetWidth()/2, fYPos+ARROW_SIZE/2) );
2002-04-16 17:31:00 +00:00
m_rectMarkerBar.Draw();
}
2003-02-18 23:15:38 +00:00
void NoteField::DrawAreaHighlight( const float fStartBeat, const float fEndBeat )
{
float fYStartOffset = ArrowGetYOffset( m_PlayerNumber, 0, fStartBeat );
2003-08-18 17:19:34 +00:00
float fYStartPos = ArrowGetYPos( m_PlayerNumber, 0, fYStartOffset, m_fYReverseOffsetPixels );
float fYEndOffset = ArrowGetYOffset( m_PlayerNumber, 0, fEndBeat );
2003-08-18 17:19:34 +00:00
float fYEndPos = ArrowGetYPos( m_PlayerNumber, 0, fYEndOffset, m_fYReverseOffsetPixels );
// Something in OpenGL crashes if this is values are too large. Strange. -Chris
fYStartPos = max( fYStartPos, -1000 );
fYEndPos = min( fYEndPos, +5000 );
2003-02-18 23:15:38 +00:00
m_rectAreaHighlight.StretchTo( RectF(-GetWidth()/2, fYStartPos-ARROW_SIZE/2, GetWidth()/2, fYEndPos+ARROW_SIZE/2) );
2003-02-18 23:15:38 +00:00
m_rectAreaHighlight.SetDiffuse( RageColor(1,0,0,0.3f) );
m_rectAreaHighlight.Draw();
}
void NoteField::DrawBPMText( const float fBeat, const float fBPM )
2002-06-29 11:59:09 +00:00
{
const float fYOffset = ArrowGetYOffset( m_PlayerNumber, 0, fBeat );
2003-08-18 17:19:34 +00:00
const float fYPos = ArrowGetYPos( m_PlayerNumber, 0, fYOffset, m_fYReverseOffsetPixels );
2002-06-29 11:59:09 +00:00
m_textMeasureNumber.SetHorizAlign( Actor::align_right );
m_textMeasureNumber.SetDiffuse( RageColor(1,0,0,1) );
2002-12-19 23:07:20 +00:00
m_textMeasureNumber.SetGlow( RageColor(1,1,1,cosf(RageTimer::GetTimeSinceStart()*2)/2+0.5f) );
2002-06-29 11:59:09 +00:00
m_textMeasureNumber.SetText( ssprintf("%.2f", fBPM) );
2003-02-18 23:15:38 +00:00
m_textMeasureNumber.SetXY( -GetWidth()/2.f - 60, fYPos );
2002-06-29 11:59:09 +00:00
m_textMeasureNumber.Draw();
}
void NoteField::DrawFreezeText( const float fBeat, const float fSecs )
2002-06-29 11:59:09 +00:00
{
const float fYOffset = ArrowGetYOffset( m_PlayerNumber, 0, fBeat );
2003-08-18 17:19:34 +00:00
const float fYPos = ArrowGetYPos( m_PlayerNumber, 0, fYOffset, m_fYReverseOffsetPixels );
2002-06-29 11:59:09 +00:00
m_textMeasureNumber.SetHorizAlign( Actor::align_right );
m_textMeasureNumber.SetDiffuse( RageColor(0.8f,0.8f,0,1) );
2002-12-19 23:07:20 +00:00
m_textMeasureNumber.SetGlow( RageColor(1,1,1,cosf(RageTimer::GetTimeSinceStart()*2)/2+0.5f) );
2002-06-29 11:59:09 +00:00
m_textMeasureNumber.SetText( ssprintf("%.2f", fSecs) );
2003-02-18 23:15:38 +00:00
m_textMeasureNumber.SetXY( -GetWidth()/2.f - 10, fYPos );
2002-06-29 11:59:09 +00:00
m_textMeasureNumber.Draw();
}
void NoteField::DrawBGChangeText( const float fBeat, const CString sNewBGName )
{
const float fYOffset = ArrowGetYOffset( m_PlayerNumber, 0, fBeat );
2003-08-18 17:19:34 +00:00
const float fYPos = ArrowGetYPos( m_PlayerNumber, 0, fYOffset, m_fYReverseOffsetPixels );
m_textMeasureNumber.SetHorizAlign( Actor::align_left );
m_textMeasureNumber.SetDiffuse( RageColor(0,1,0,1) );
2002-12-19 23:07:20 +00:00
m_textMeasureNumber.SetGlow( RageColor(1,1,1,cosf(RageTimer::GetTimeSinceStart()*2)/2+0.5f) );
m_textMeasureNumber.SetText( sNewBGName );
m_textMeasureNumber.SetXY( +GetWidth()/2.f, fYPos );
m_textMeasureNumber.Draw();
}
/* cur is an iterator within m_NoteDisplays. next is ++cur. Advance or rewind cur to
* point to the block that contains beat. We maintain next as an optimization, as this
* is called for every tap. */
void NoteField::SearchForBeat( NDMap::iterator &cur, NDMap::iterator &next, float Beat )
{
/* cur is too far ahead: */
while( cur != m_BeatToNoteDisplays.begin() && cur->first > Beat )
{
next = cur;
--cur;
}
/* cur is too far behind: */
while( next != m_BeatToNoteDisplays.end() && next->first < Beat )
{
cur = next;
++next;
}
}
// CPU OPTIMIZATION OPPORTUNITY:
// change this probing to binary search
float FindFirstDisplayedBeat( PlayerNumber pn, int iFirstPixelToDraw )
{
float fFirstBeatToDraw = GAMESTATE->m_fSongBeat-4; // Adjust to balance off performance and showing enough notes.
while( fFirstBeatToDraw < GAMESTATE->m_fSongBeat )
{
float fYOffset = ArrowGetYOffset(pn, 0, fFirstBeatToDraw);
float fYPosWOReverse = ArrowGetYPosWithoutReverse(pn, 0, fYOffset );
if( fYPosWOReverse < iFirstPixelToDraw ) // off screen
fFirstBeatToDraw += 0.1f; // move toward fSongBeat
else // on screen
break; // stop probing
}
fFirstBeatToDraw -= 0.1f; // rewind if we intentionally overshot
return fFirstBeatToDraw;
}
float FindLastDisplayedBeat( PlayerNumber pn, int iLastPixelToDraw )
{
2003-09-16 07:22:54 +00:00
//
// Probe for last note to draw.
// worst case is 0.25x + boost. Adjust search distance to
// so that notes don't pop onto the screen.
//
float fSearchDistance = 20;
float fLastBeatToDraw = GAMESTATE->m_fSongBeat+fSearchDistance;
2003-09-17 22:25:29 +00:00
/* With a song that starts with a negative offset, fSearchDistance here can put
* the result out of range. XXX: 0 does, too; try setting an #OFFSET to -100. */
2003-09-17 23:00:22 +00:00
// if( GAMESTATE->m_fSongBeat < 0 )
// fLastBeatToDraw = 0; //fSearchDistance;
2003-09-16 07:22:54 +00:00
const int NUM_ITERATIONS = 15;
for( int i=0; i<NUM_ITERATIONS; i++ )
{
float fYOffset = ArrowGetYOffset( pn, 0, fLastBeatToDraw );
float fYPosWOReverse = ArrowGetYPosWithoutReverse( pn, 0, fYOffset );
2003-09-16 07:22:54 +00:00
if( fYPosWOReverse > iLastPixelToDraw ) // off screen
2003-09-16 07:22:54 +00:00
fLastBeatToDraw -= fSearchDistance;
else // on screen
2003-09-16 07:22:54 +00:00
fLastBeatToDraw += fSearchDistance;
float fOffBy = fYPosWOReverse - iLastPixelToDraw;
if( i==NUM_ITERATIONS-1 )
ASSERT( fabs(fOffBy)<10 ); // accurate to 10 pixels
fSearchDistance /= 2;
}
2003-09-16 07:22:54 +00:00
return fLastBeatToDraw;
}
float g_fNoteFieldLastBeatToDraw = -1;
2002-05-19 01:59:48 +00:00
void NoteField::DrawPrimitives()
2002-04-16 17:31:00 +00:00
{
//LOG->Trace( "NoteField::DrawPrimitives()" );
2002-04-16 17:31:00 +00:00
/* This should be filled in on the first update. */
ASSERT( !m_BeatToNoteDisplays.empty() );
2003-01-25 11:05:12 +00:00
2003-04-01 19:31:27 +00:00
//
// Adjust draw range depending on some effects
//
int iFirstPixelToDraw = m_iStartDrawingPixel;
int iLastPixelToDraw = m_iEndDrawingPixel;
2003-04-01 19:31:27 +00:00
float fDrawScale = 1;
2003-08-14 08:13:08 +00:00
fDrawScale *= 1 + 0.5f * fabsf( GAMESTATE->m_CurrentPlayerOptions[m_PlayerNumber].m_fPerspectiveTilt );
fDrawScale *= 1 + fabsf( GAMESTATE->m_CurrentPlayerOptions[m_PlayerNumber].m_fEffects[PlayerOptions::EFFECT_MINI] );
2003-04-17 21:16:56 +00:00
float fFirstDrawScale = g_NoteFieldMode[m_PlayerNumber].m_fFirstPixelToDrawScale;
float fLastDrawScale = g_NoteFieldMode[m_PlayerNumber].m_fLastPixelToDrawScale;
2003-04-01 19:31:27 +00:00
2003-04-17 21:16:56 +00:00
iFirstPixelToDraw = (int)(iFirstPixelToDraw * fFirstDrawScale * fDrawScale);
iLastPixelToDraw = (int)(iLastPixelToDraw * fLastDrawScale * fDrawScale);
2003-04-01 19:31:27 +00:00
// probe for first and last notes on the screen
float fFirstBeatToDraw = FindFirstDisplayedBeat( m_PlayerNumber, iFirstPixelToDraw );
float fLastBeatToDraw = FindLastDisplayedBeat( m_PlayerNumber, iLastPixelToDraw );
2003-01-25 11:05:12 +00:00
/* Hack: */
g_fNoteFieldLastBeatToDraw = fLastBeatToDraw;
2003-01-25 11:05:12 +00:00
const int iFirstIndexToDraw = BeatToNoteRow(fFirstBeatToDraw);
const int iLastIndexToDraw = BeatToNoteRow(fLastBeatToDraw);
2002-04-16 17:31:00 +00:00
2003-01-25 11:05:12 +00:00
// LOG->Trace( "start = %f.1, end = %f.1", fFirstBeatToDraw-fSongBeat, fLastBeatToDraw-fSongBeat );
2002-09-11 04:49:07 +00:00
// LOG->Trace( "Drawing elements %d through %d", iFirstIndexToDraw, iLastIndexToDraw );
2002-04-16 17:31:00 +00:00
2002-07-28 20:28:37 +00:00
if( GAMESTATE->m_bEditing )
2002-04-16 17:31:00 +00:00
{
2003-01-31 02:37:23 +00:00
ASSERT(GAMESTATE->m_pCurSong);
unsigned i;
2002-06-29 11:59:09 +00:00
//
2003-02-18 23:15:38 +00:00
// Draw beat bars
2002-06-29 11:59:09 +00:00
//
2003-02-18 23:15:38 +00:00
{
float fStartDrawingMeasureBars = max( 0, froundf(fFirstBeatToDraw-0.25f,0.25f) );
for( float f=fStartDrawingMeasureBars; f<fLastBeatToDraw; f+=0.25f )
DrawBeatBar( f );
}
2002-04-16 17:31:00 +00:00
2002-06-29 11:59:09 +00:00
//
// BPM text
//
2003-01-03 05:56:28 +00:00
vector<BPMSegment> &aBPMSegments = GAMESTATE->m_pCurSong->m_BPMSegments;
for( i=0; i<aBPMSegments.size(); i++ )
2003-01-30 20:12:39 +00:00
{
if(aBPMSegments[i].m_fStartBeat >= fFirstBeatToDraw &&
aBPMSegments[i].m_fStartBeat <= fLastBeatToDraw)
DrawBPMText( aBPMSegments[i].m_fStartBeat, aBPMSegments[i].m_fBPM );
}
2002-06-29 11:59:09 +00:00
//
// Freeze text
//
2003-01-03 05:56:28 +00:00
vector<StopSegment> &aStopSegments = GAMESTATE->m_pCurSong->m_StopSegments;
for( i=0; i<aStopSegments.size(); i++ )
2003-01-30 20:12:39 +00:00
{
if(aStopSegments[i].m_fStartBeat >= fFirstBeatToDraw &&
aStopSegments[i].m_fStartBeat <= fLastBeatToDraw)
DrawFreezeText( aStopSegments[i].m_fStartBeat, aStopSegments[i].m_fStopSeconds );
2003-01-30 20:12:39 +00:00
}
2002-06-29 11:59:09 +00:00
//
// BGChange text
//
2003-01-03 05:56:28 +00:00
vector<BackgroundChange> &aBackgroundChanges = GAMESTATE->m_pCurSong->m_BackgroundChanges;
for( i=0; i<aBackgroundChanges.size(); i++ )
2003-01-30 20:12:39 +00:00
{
if(aBackgroundChanges[i].m_fStartBeat >= fFirstBeatToDraw &&
aBackgroundChanges[i].m_fStartBeat <= fLastBeatToDraw)
{
const BackgroundChange& change = aBackgroundChanges[i];
CString sChangeText = ssprintf("%s\n%.0f%%%s%s%s",
2003-04-25 00:27:30 +00:00
change.m_sBGName.c_str(),
change.m_fRate*100,
change.m_bFadeLast ? " Fade" : "",
change.m_bRewindMovie ? " Rewind" : "",
change.m_bLoop ? " Loop" : "" );
DrawBGChangeText( change.m_fStartBeat, sChangeText );
}
2003-01-30 20:12:39 +00:00
}
2002-06-29 11:59:09 +00:00
//
// Draw marker bars
//
2003-02-18 23:15:38 +00:00
if( m_fBeginMarker != -1 && m_fEndMarker != -1 )
DrawAreaHighlight( m_fBeginMarker, m_fEndMarker );
else if( m_fBeginMarker != -1 )
DrawMarkerBar( m_fBeginMarker );
2003-02-18 23:15:38 +00:00
else if( m_fEndMarker != -1 )
DrawMarkerBar( m_fEndMarker );
2002-06-29 11:59:09 +00:00
2002-04-16 17:31:00 +00:00
}
2002-06-29 11:59:09 +00:00
2002-04-16 17:31:00 +00:00
//
// Optimization is very important here because there are so many arrows to draw.
// Draw the arrows in order of column. This minimize texture switches and let us
// draw in big batches.
//
2003-02-19 06:27:20 +00:00
float fSelectedRangeGlow = SCALE( cosf(RageTimer::GetTimeSinceStart()*2), -1, 1, 0.1f, 0.3f );
2003-02-01 05:16:38 +00:00
for( int c=0; c<GetNumTracks(); c++ ) // for each arrow column
2002-04-28 20:42:32 +00:00
{
g_NoteFieldMode[m_PlayerNumber].BeginDrawTrack(c);
2003-04-02 21:58:27 +00:00
2002-06-14 22:25:22 +00:00
/////////////////////////////////
// Draw all HoldNotes in this column (so that they appear under the tap notes)
/////////////////////////////////
2002-11-16 09:12:55 +00:00
int i;
NDMap::iterator CurDisplay = m_BeatToNoteDisplays.begin();
ASSERT( CurDisplay != m_BeatToNoteDisplays.end() );
NDMap::iterator NextDisplay = CurDisplay; ++NextDisplay;
2002-11-16 09:12:55 +00:00
for( i=0; i < GetNumHoldNotes(); i++ )
2002-04-16 17:31:00 +00:00
{
2002-11-02 22:46:15 +00:00
const HoldNote &hn = GetHoldNote(i);
2002-12-18 22:25:24 +00:00
const HoldNoteScore hns = GetHoldNoteScore(i);
2002-12-17 23:09:10 +00:00
const float fLife = GetHoldNoteLife(i);
const bool bIsHoldingNote = (i < int(m_bIsHoldingHoldNote.size()))?
2003-01-31 02:37:23 +00:00
m_bIsHoldingHoldNote[i]: false;
2002-08-01 03:15:27 +00:00
if( hns == HNS_OK ) // if this HoldNote was completed
continue; // don't draw anything
if( hn.iTrack != c ) // this HoldNote doesn't belong to this column
2002-04-28 20:42:32 +00:00
continue;
2002-04-16 17:31:00 +00:00
2002-04-28 20:42:32 +00:00
// If no part of this HoldNote is on the screen, skip it
if( !( fFirstBeatToDraw <= hn.fEndBeat && hn.fEndBeat <= fLastBeatToDraw ||
fFirstBeatToDraw <= hn.fStartBeat && hn.fStartBeat <= fLastBeatToDraw ||
hn.fStartBeat < fFirstBeatToDraw && hn.fEndBeat > fLastBeatToDraw ) )
2002-04-28 20:42:32 +00:00
{
2002-06-14 22:25:22 +00:00
continue; // skip
2002-04-28 20:42:32 +00:00
}
2002-04-16 17:31:00 +00:00
SearchForBeat( CurDisplay, NextDisplay, hn.fStartBeat );
2003-02-19 06:27:20 +00:00
bool bIsInSelectionRange = false;
if( m_fBeginMarker!=-1 && m_fEndMarker!=-1 )
bIsInSelectionRange = m_fBeginMarker<=hn.fStartBeat && hn.fStartBeat<=m_fEndMarker && m_fBeginMarker<=hn.fEndBeat && hn.fEndBeat<=m_fEndMarker;
2002-04-16 17:31:00 +00:00
NoteDisplayCols *nd = CurDisplay->second;
nd->display[c].DrawHold( hn, bIsHoldingNote, fLife, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail, false, m_fYReverseOffsetPixels );
2002-04-16 17:31:00 +00:00
}
2002-06-14 22:25:22 +00:00
///////////////////////////////////
// Draw all TapNotes in this column
///////////////////////////////////
CurDisplay = m_BeatToNoteDisplays.begin();
NextDisplay = CurDisplay; ++NextDisplay;
2003-09-16 07:22:54 +00:00
// draw notes from furthest to closest
for( i=iLastIndexToDraw; i>=iFirstIndexToDraw; --i ) // for each row
2002-06-14 22:25:22 +00:00
{
TapNote tn = GetTapNote(c, i);
if( tn == TAP_EMPTY ) // no note here
continue; // skip
2002-06-14 22:25:22 +00:00
if( tn == TAP_HOLD_HEAD ) // this is a HoldNote begin marker. Grade it, but don't draw
continue; // skip
// See if there is a hold step that begins on this index.
bool bHoldNoteBeginsOnThisBeat = false;
2003-02-01 05:16:38 +00:00
for( int c2=0; c2<GetNumTracks(); c2++ )
2002-06-14 22:25:22 +00:00
{
2003-08-16 17:28:15 +00:00
if( GetTapNote(c2, i) == TAP_HOLD_HEAD )
2002-06-14 22:25:22 +00:00
{
bHoldNoteBeginsOnThisBeat = true;
2002-06-14 22:25:22 +00:00
break;
}
}
2003-02-18 23:15:38 +00:00
bool bIsInSelectionRange = false;
if( m_fBeginMarker!=-1 && m_fEndMarker!=-1 )
{
float fBeat = NoteRowToBeat(i);
bIsInSelectionRange = m_fBeginMarker<=fBeat && fBeat<=m_fEndMarker;
}
bool bIsAddition = (tn == TAP_ADDITION);
2003-08-10 10:12:50 +00:00
bool bIsMine = (tn == TAP_MINE);
SearchForBeat( CurDisplay, NextDisplay, NoteRowToBeat(i) );
NoteDisplayCols *nd = CurDisplay->second;
nd->display[c].DrawTap( c, NoteRowToBeat(i), bHoldNoteBeginsOnThisBeat, bIsAddition, bIsMine, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail, 1, m_fYReverseOffsetPixels );
}
2003-04-02 21:58:27 +00:00
g_NoteFieldMode[m_PlayerNumber].EndDrawTrack(c);
2002-04-16 17:31:00 +00:00
}
2002-04-28 20:42:32 +00:00
2002-04-16 17:31:00 +00:00
}
void NoteField::RemoveTapNoteRow( int iIndex )
{
2003-02-01 05:16:38 +00:00
for( int c=0; c<GetNumTracks(); c++ )
2002-10-25 04:59:12 +00:00
SetTapNote(c, iIndex, TAP_EMPTY);
2002-04-16 17:31:00 +00:00
}
2002-08-01 21:55:40 +00:00
void NoteField::FadeToFail()
{
m_fPercentFadeToFail = max( 0.0f, m_fPercentFadeToFail ); // this will slowly increase every Update()
// don't fade all over again if this is called twice
2002-11-16 09:12:55 +00:00
}