Files
itgmania212121/stepmania/src/NoteDataWithScoring.cpp
T

442 lines
13 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2002-06-23 02:50:33 +00:00
#include "NoteDataWithScoring.h"
#include "GameState.h"
2003-07-31 01:18:52 +00:00
#include "RageUtil.h"
#include "StageStats.h"
2002-06-23 02:50:33 +00:00
NoteDataWithScoring::NoteDataWithScoring()
{
Init();
}
2002-12-18 22:24:34 +00:00
void NoteDataWithScoring::Init()
2002-08-01 21:55:40 +00:00
{
NoteData::Init();
2002-06-23 02:50:33 +00:00
for( int t=0; t<MAX_NOTE_TRACKS; t++ )
2002-12-17 23:31:35 +00:00
m_TapNoteScores[t].clear();
2002-06-23 02:50:33 +00:00
2002-11-03 21:38:22 +00:00
m_HoldNoteScores.clear();
2002-06-23 02:50:33 +00:00
}
2004-05-24 04:26:54 +00:00
int NoteDataWithScoring::GetNumTapNotesWithScore( TapNoteScore tns, const float fStartBeat, float fEndBeat ) const
2002-06-23 02:50:33 +00:00
{
2004-05-24 04:26:54 +00:00
int iNumSuccessfulTapNotes = 0;
2002-06-23 02:50:33 +00:00
2003-03-06 22:25:21 +00:00
if(fEndBeat == -1)
2004-09-11 03:50:37 +00:00
fEndBeat = GetNumBeats()+1;
2003-03-06 22:25:21 +00:00
2002-12-17 23:31:35 +00:00
unsigned iStartIndex = BeatToNoteRow( fStartBeat );
unsigned iEndIndex = BeatToNoteRow( fEndBeat );
2002-06-23 02:50:33 +00:00
2003-04-20 22:08:32 +00:00
for( unsigned i=iStartIndex; i<min(float(iEndIndex), float(m_TapNoteScores[0].size())); i++ )
2002-06-23 02:50:33 +00:00
{
2003-02-01 05:16:38 +00:00
for( int t=0; t<GetNumTracks(); t++ )
2002-06-23 02:50:33 +00:00
{
if( this->GetTapNote(t, i).type != TapNote::empty && GetTapNoteScore(t, i) >= tns )
2004-05-24 04:26:54 +00:00
iNumSuccessfulTapNotes++;
2002-06-23 02:50:33 +00:00
}
}
2004-05-24 04:26:54 +00:00
return iNumSuccessfulTapNotes;
2002-06-23 02:50:33 +00:00
}
int NoteDataWithScoring::GetNumNWithScore( TapNoteScore tns, int MinTaps, const float fStartBeat, float fEndBeat ) const
2002-06-23 02:50:33 +00:00
{
if( fEndBeat == -1 )
2004-09-11 03:50:37 +00:00
fEndBeat = GetNumBeats();
2002-06-23 02:50:33 +00:00
int iStartIndex = BeatToNoteRow( fStartBeat );
int iEndIndex = BeatToNoteRow( fEndBeat );
2003-03-06 22:25:21 +00:00
iStartIndex = max( iStartIndex, 0 );
2004-09-11 03:50:37 +00:00
iEndIndex = min( iEndIndex, GetNumRows()-1 );
2002-06-23 02:50:33 +00:00
int iNumSuccessfulDoubles = 0;
for( int i=iStartIndex; i<=iEndIndex; i++ )
2002-06-23 02:50:33 +00:00
{
int iNumNotesThisIndex = 0;
TapNoteScore minTapNoteScore = TNS_MARVELOUS;
2003-02-01 05:16:38 +00:00
for( int t=0; t<GetNumTracks(); t++ )
2002-06-23 02:50:33 +00:00
{
switch( GetTapNote(t, i).type )
2002-06-23 02:50:33 +00:00
{
case TapNote::tap:
case TapNote::hold_head:
2002-06-23 02:50:33 +00:00
iNumNotesThisIndex++;
2002-12-17 22:07:59 +00:00
minTapNoteScore = min( minTapNoteScore, GetTapNoteScore(t, i) );
2002-06-23 02:50:33 +00:00
}
}
if( iNumNotesThisIndex >= MinTaps && minTapNoteScore >= tns )
2002-06-23 02:50:33 +00:00
iNumSuccessfulDoubles++;
}
return iNumSuccessfulDoubles;
}
2003-03-26 17:53:31 +00:00
int NoteDataWithScoring::GetNumHoldNotesWithScore( HoldNoteScore hns, const float fStartBeat, float fEndBeat ) const
2002-06-23 02:50:33 +00:00
{
int iNumSuccessfulHolds = 0;
2003-03-06 22:25:21 +00:00
if(fEndBeat == -1)
2004-09-11 03:50:37 +00:00
fEndBeat = GetNumBeats();
2003-12-16 04:00:39 +00:00
int iStartIndex = BeatToNoteRow( fStartBeat );
int iEndIndex = BeatToNoteRow( fEndBeat );
2003-03-06 22:25:21 +00:00
2002-11-02 22:59:12 +00:00
for( int i=0; i<GetNumHoldNotes(); i++ )
2002-06-23 02:50:33 +00:00
{
2002-11-02 22:46:15 +00:00
const HoldNote &hn = GetHoldNote(i);
if( iStartIndex > hn.iStartRow || hn.iEndRow > iEndIndex )
continue;
if( GetHoldNoteScore(hn) == hns )
2002-06-23 02:50:33 +00:00
iNumSuccessfulHolds++;
}
return iNumSuccessfulHolds;
}
int NoteDataWithScoring::GetSuccessfulMines( float fStartBeat, float fEndBeat ) const
{
if( fEndBeat == -1 )
2004-09-11 03:50:37 +00:00
fEndBeat = GetNumBeats();
int iStartIndex = BeatToNoteRow( fStartBeat );
int iEndIndex = BeatToNoteRow( fEndBeat );
iStartIndex = max( iStartIndex, 0 );
2004-09-11 03:50:37 +00:00
iEndIndex = min( iEndIndex, GetNumRows()-1 );
int iNumSuccessfulMinesNotes = 0;
for( int i=iStartIndex; i<=iEndIndex; i++ )
{
for( int t=0; t<GetNumTracks(); t++ )
{
if( this->GetTapNote(t,i).type == TapNote::mine && GetTapNoteScore(t, i) != TNS_HIT_MINE )
iNumSuccessfulMinesNotes++;
}
}
return iNumSuccessfulMinesNotes;
}
2003-12-16 07:58:37 +00:00
/* See NoteData::GetNumHands(). */
int NoteDataWithScoring::GetSuccessfulHands( float fStartBeat, float fEndBeat ) const
2003-12-16 04:00:39 +00:00
{
2003-12-16 07:58:37 +00:00
if( fEndBeat == -1 )
2004-09-11 03:50:37 +00:00
fEndBeat = GetNumBeats();
2003-12-16 07:58:37 +00:00
int iStartIndex = BeatToNoteRow( fStartBeat );
int iEndIndex = BeatToNoteRow( fEndBeat );
/* Clamp to known-good ranges. */
iStartIndex = max( iStartIndex, 0 );
2004-09-11 03:50:37 +00:00
iEndIndex = min( iEndIndex, GetNumRows()-1 );
2003-12-16 07:58:37 +00:00
int iNum = 0;
for( int i=iStartIndex; i<=iEndIndex; i++ )
{
if( !RowNeedsHands(i) )
continue;
bool Missed = false;
for( int t=0; t<GetNumTracks(); t++ )
{
TapNote tn = GetTapNoteX(t, i);
if( tn.type == TapNote::empty )
continue;
if( tn.type == TapNote::mine ) // mines don't count
2003-12-16 07:58:37 +00:00
continue;
if( GetTapNoteScore(t, i) <= TNS_BOO )
Missed = true;
}
if( Missed )
continue;
/* Check hold scores. */
for( int j=0; j<GetNumHoldNotes(); j++ )
{
const HoldNote &hn = GetHoldNote(j);
HoldNoteResult hnr = GetHoldNoteResult( hn );
/* Check if the row we're checking is in range. */
if( !hn.RowIsInRange(i) )
continue;
/* If a hold is released *after* a hands containing it, the hands is
* still good. So, ignore the judgement and only examine iLastHeldRow
* to be sure that the hold was still held at the point of this row.
* (Note that if the hold head tap was missed, then iLastHeldRow == i
* and this won't fail--but the tap check above will have already failed.) */
if( hnr.iLastHeldRow < i )
2003-12-16 07:58:37 +00:00
Missed = true;
}
if( !Missed )
iNum++;
}
return iNum;
2003-12-16 04:00:39 +00:00
}
/* Return the minimum tap score of a row. If the row isn't complete (not all
* taps have been hit), return TNS_NONE or TNS_MISS. */
TapNoteScore NoteDataWithScoring::MinTapNoteScore(unsigned row) const
2002-12-17 22:05:17 +00:00
{
TapNoteScore score = TNS_MARVELOUS;
2003-02-01 05:16:38 +00:00
for( int t=0; t<GetNumTracks(); t++ )
{
2003-11-11 07:36:28 +00:00
/* Don't coun, or else the score
* will always be TNS_NONE. */
TapNote tn = GetTapNote(t, row);
if( tn.type == TapNote::empty || tn.type == TapNote::mine)
continue;
score = min( score, GetTapNoteScore(t, row) );
}
return score;
2002-12-17 22:05:17 +00:00
}
bool NoteDataWithScoring::IsRowCompletelyJudged(unsigned row) const
{
return MinTapNoteScore(row) >= TNS_MISS;
}
/* Return the last tap score of a row: the grade of the tap that completed
2003-03-27 18:40:58 +00:00
* the row. If the row has no tap notes, return -1. If any tap notes aren't
* graded (any tap is TNS_NONE) or are missed (TNS_MISS), return it. */
2003-03-27 00:39:17 +00:00
int NoteDataWithScoring::LastTapNoteScoreTrack(unsigned row) const
{
float scoretime = -9999;
2003-03-27 00:39:17 +00:00
int best_track = -1;
for( int t=0; t<GetNumTracks(); t++ )
{
2003-11-11 07:36:28 +00:00
/* Skip empty tracks and mines */
TapNote tn = GetTapNote(t, row);
if( tn.type == TapNote::empty || tn.type == TapNote::mine )
2003-11-11 07:36:28 +00:00
continue;
TapNoteScore tns = GetTapNoteScore(t, row);
2003-08-10 19:07:54 +00:00
if( tns == TNS_MISS || tns == TNS_NONE )
return t;
float tm = GetTapNoteOffset(t, row);
if(tm < scoretime) continue;
scoretime = tm;
2003-03-27 00:39:17 +00:00
best_track = t;
}
2003-03-27 00:39:17 +00:00
return best_track;
}
TapNoteScore NoteDataWithScoring::LastTapNoteScore(unsigned row) const
{
int track = LastTapNoteScoreTrack(row);
if(track == -1) return TNS_NONE;
return GetTapNoteScore(track, row);
}
/* From aaroninjapan.com (http://www.aaroninjapan.com/ddr2.html)
*
* Stream: The ratio of your number of Perfects to getting all Perfects
* Voltage: The ratio of your maximum combo to getting a Full Combo
* Air: The ratio of your number of Perfects on all your jumps (R+L, R+D, etc) to getting all Perfects on all Jumps
* Chaos: The ratio of your dance level compared to that of AAA (all Perfect) level
* Freeze: The ratio of your total number of "OK"s on all the Freeze arrows to getting all "OK" on all the Freeze arrows
*
* I don't think chaos is correct, at least relative to DDREX. You can AA songs and get a full Chaos graph.
* No idea what it actually could be, though.
*/
2004-07-11 07:21:33 +00:00
void NoteDataWithScoring::GetActualRadarValues( PlayerNumber pn, float fSongSeconds, RadarValues& out ) const
2002-12-17 22:05:17 +00:00
{
2004-07-11 07:21:33 +00:00
// The for loop and the assert are used to ensure that all fields of
// RadarValue get set in here.
FOREACH_RadarCategory( rc )
2002-12-17 22:05:17 +00:00
{
2004-07-11 07:21:33 +00:00
switch( rc )
{
case RADAR_STREAM: out[rc] = GetActualStreamRadarValue( fSongSeconds, pn ); break;
case RADAR_VOLTAGE: out[rc] = GetActualVoltageRadarValue( fSongSeconds, pn ); break;
case RADAR_AIR: out[rc] = GetActualAirRadarValue( fSongSeconds, pn ); break;
case RADAR_FREEZE: out[rc] = GetActualFreezeRadarValue( fSongSeconds, pn ); break;
case RADAR_CHAOS: out[rc] = GetActualChaosRadarValue( fSongSeconds, pn ); break;
case RADAR_NUM_TAPS_AND_HOLDS: out[rc] = (float) GetNumNWithScore( TNS_GOOD, 1 ); break;
case RADAR_NUM_JUMPS: out[rc] = (float) GetNumNWithScore( TNS_GOOD, 2 ); break;
case RADAR_NUM_HOLDS: out[rc] = (float) GetNumHoldNotesWithScore( HNS_OK ); break;
case RADAR_NUM_MINES: out[rc] = (float) GetSuccessfulMines(); break;
case RADAR_NUM_HANDS: out[rc] = (float) GetSuccessfulHands(); break;
default: ASSERT(0);
}
2002-12-17 22:05:17 +00:00
}
}
2002-06-23 02:50:33 +00:00
float NoteDataWithScoring::GetActualStreamRadarValue( float fSongSeconds, PlayerNumber pn ) const
2002-06-23 02:50:33 +00:00
{
2004-05-24 04:26:54 +00:00
int TotalSteps = GetNumTapNotes();
2003-09-25 01:21:01 +00:00
if( !TotalSteps )
return 1;
2004-05-24 04:26:54 +00:00
const int Perfects = GetNumTapNotesWithScore(TNS_PERFECT);
2003-09-25 01:21:01 +00:00
return clamp( float(Perfects)/TotalSteps, 0.0f, 1.0f );
2002-06-23 02:50:33 +00:00
}
float NoteDataWithScoring::GetActualVoltageRadarValue( float fSongSeconds, PlayerNumber pn ) const
2002-06-23 02:50:33 +00:00
{
2004-05-24 04:26:54 +00:00
/* g_CurStageStats.iMaxCombo is unrelated to GetNumTapNotes: m_bComboContinuesBetweenSongs
2003-11-02 17:06:25 +00:00
* might be on, and the way combo is counted varies depending on the mode and score
* keeper. Instead, let's use the length of the longest recorded combo. This is
* only subtly different: it's the percent of the song the longest combo took to get. */
const StageStats::Combo_t MaxCombo = g_CurStageStats.GetMaxCombo( pn );
float fComboPercent = SCALE( MaxCombo.fSizeSeconds, 0, g_CurStageStats.fLastSecond[pn]-g_CurStageStats.fFirstSecond[pn], 0.0f, 1.0f );
2004-02-23 23:21:14 +00:00
return clamp( fComboPercent, 0.0f, 1.0f );
2002-06-23 02:50:33 +00:00
}
float NoteDataWithScoring::GetActualAirRadarValue( float fSongSeconds, PlayerNumber pn ) const
2002-06-23 02:50:33 +00:00
{
2003-09-25 01:21:01 +00:00
const int iTotalDoubles = GetNumDoubles();
if (iTotalDoubles == 0)
return 1; // no jumps in song
2003-09-25 01:21:01 +00:00
// number of doubles
const int iNumDoubles = GetNumNWithScore( TNS_PERFECT, 2 );
2003-09-25 01:21:01 +00:00
return clamp( (float)iNumDoubles / iTotalDoubles, 0.0f, 1.0f );
2002-06-23 02:50:33 +00:00
}
float NoteDataWithScoring::GetActualChaosRadarValue( float fSongSeconds, PlayerNumber pn ) const
2002-06-23 02:50:33 +00:00
{
const int PossibleDP = g_CurStageStats.iPossibleDancePoints[pn];
2003-09-25 01:21:01 +00:00
if ( PossibleDP == 0 )
2003-11-02 17:06:25 +00:00
return 1;
const int ActualDP = g_CurStageStats.iActualDancePoints[pn];
2003-09-25 01:21:01 +00:00
return clamp( float(ActualDP)/PossibleDP, 0.0f, 1.0f );
2002-06-23 02:50:33 +00:00
}
float NoteDataWithScoring::GetActualFreezeRadarValue( float fSongSeconds, PlayerNumber pn ) const
2002-06-23 02:50:33 +00:00
{
// number of hold steps
2003-09-25 01:21:01 +00:00
const int TotalHolds = GetNumHoldNotes();
if ( TotalHolds == 0 )
return 1.0f;
const int ActualHolds = GetNumHoldNotesWithScore(HNS_OK);
return clamp( float(ActualHolds) / TotalHolds, 0.0f, 1.0f );
2002-06-23 02:50:33 +00:00
}
2002-12-18 22:24:34 +00:00
template<class T>
void extend(vector<T> &v, T val, unsigned pos)
{
int needed = pos - v.size() + 1;
if(needed > 0)
{
needed += 100; /* optimization: give it a little more than it needs */
v.insert(v.end(), needed, val);
}
}
TapNoteScore NoteDataWithScoring::GetTapNoteScore(unsigned track, unsigned row) const
{
if(row >= m_TapNoteScores[track].size())
return TNS_NONE;
return m_TapNoteScores[track][row];
}
void NoteDataWithScoring::SetTapNoteScore(unsigned track, unsigned row, TapNoteScore tns)
{
extend(m_TapNoteScores[track], TNS_NONE, row);
m_TapNoteScores[track][row] = tns;
}
float NoteDataWithScoring::GetTapNoteOffset(unsigned track, unsigned row) const
{
if(row >= m_TapNoteOffset[track].size())
return 0;
return m_TapNoteOffset[track][row];
}
void NoteDataWithScoring::SetTapNoteOffset(unsigned track, unsigned row, float offset)
{
extend(m_TapNoteOffset[track], 0.f, row);
m_TapNoteOffset[track][row] = offset;
}
/* We use the end row to index hold notes, instead of the start row, because the start row
* changes when hold notes are being stepped on, but end rows never change. */
HoldNoteScore NoteDataWithScoring::GetHoldNoteScore( const HoldNote &hn ) const
2002-12-18 22:24:34 +00:00
{
return GetHoldNoteResult(hn).hns;
2002-12-18 22:24:34 +00:00
}
void NoteDataWithScoring::SetHoldNoteScore( const HoldNote &hn, HoldNoteScore hns )
2002-12-18 22:24:34 +00:00
{
HoldNoteResult *hnr = CreateHoldNoteResult( hn );
hnr->hns = hns;
2002-12-18 22:24:34 +00:00
}
void NoteDataWithScoring::SetHoldNoteLife( const HoldNote &hn, float f )
2002-12-18 22:24:34 +00:00
{
HoldNoteResult *hnr = CreateHoldNoteResult( hn );
hnr->fLife = f;
2002-12-18 22:24:34 +00:00
}
float NoteDataWithScoring::GetHoldNoteLife( const HoldNote &hn ) const
2002-12-18 22:24:34 +00:00
{
return GetHoldNoteResult(hn).fLife;
}
const HoldNoteResult NoteDataWithScoring::GetHoldNoteResult( const HoldNote &hn ) const
{
map<RowTrack, HoldNoteResult>::const_iterator it = m_HoldNoteScores.find( RowTrack(hn) );
if( it == m_HoldNoteScores.end() )
return HoldNoteResult(hn);
return it->second;
2002-12-18 22:24:34 +00:00
}
HoldNoteResult *NoteDataWithScoring::CreateHoldNoteResult( const HoldNote &hn )
{
map<RowTrack, HoldNoteResult>::iterator it = m_HoldNoteScores.find( RowTrack(hn) );
if( it == m_HoldNoteScores.end() )
{
HoldNoteResult *ret = &m_HoldNoteScores[hn];
ret->iLastHeldRow = hn.iStartRow;
return ret;
}
return &it->second;
}
HoldNoteResult::HoldNoteResult( const HoldNote &hn )
{
hns = HNS_NONE;
fLife = 1.0f;
iLastHeldRow = hn.iStartRow;
}
2004-05-31 22:42:12 +00:00
/*
* (c) 2001-2004 Chris Danford, 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.
*/