move PlayerStageStats into a separate file
This commit is contained in:
@@ -75,6 +75,7 @@ NoteFieldPositioning.cpp NoteFieldPositioning.h NoteTypes.cpp NoteTypes.h NotesL
|
||||
NotesLoaderBMS.cpp NotesLoaderBMS.h NotesLoaderDWI.cpp NotesLoaderDWI.h NotesLoaderKSF.cpp NotesLoaderKSF.h \
|
||||
NotesLoaderSM.cpp NotesLoaderSM.h NotesWriterDWI.cpp NotesWriterDWI.h NotesWriterSM.cpp NotesWriterSM.h \
|
||||
PlayerAI.cpp PlayerAI.h PlayerNumber.cpp PlayerNumber.h PlayerOptions.cpp PlayerOptions.h \
|
||||
PlayerStageStats.cpp PlayerStageStats.h \
|
||||
PlayerState.cpp PlayerState.h Preference.cpp Preference.h Profile.cpp Profile.h \
|
||||
RandomSample.cpp RandomSample.h RadarValues.cpp RadarValues.h ScreenDimensions.h ScreenDimensions.cpp \
|
||||
ScoreKeeper.h ScoreKeeperMAX2.cpp ScoreKeeperMAX2.h \
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
#include "global.h"
|
||||
#include "PlayerStageStats.h"
|
||||
#include "RageLog.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "Foreach.h"
|
||||
|
||||
void PlayerStageStats::Init()
|
||||
{
|
||||
vpSteps.clear();
|
||||
fAliveSeconds = 0;
|
||||
bFailed = bFailedEarlier = false;
|
||||
iPossibleDancePoints = iActualDancePoints = 0;
|
||||
iCurCombo = iMaxCombo = iCurMissCombo = iScore = iBonus = iMaxScore = iCurMaxScore = 0;
|
||||
fSecondsBeforeFail = 0;
|
||||
iSongsPassed = iSongsPlayed = 0;
|
||||
iTotalError = 0;
|
||||
fCaloriesBurned = 0;
|
||||
|
||||
ZERO( iTapNoteScores );
|
||||
ZERO( iHoldNoteScores );
|
||||
radarPossible.Zero();
|
||||
radarActual.Zero();
|
||||
|
||||
fFirstSecond = 999999;
|
||||
fLastSecond = 0;
|
||||
}
|
||||
|
||||
void PlayerStageStats::AddStats( const PlayerStageStats& other )
|
||||
{
|
||||
FOREACH_CONST( Steps*, other.vpSteps, s )
|
||||
vpSteps.push_back( *s );
|
||||
fAliveSeconds += other.fAliveSeconds;
|
||||
bFailed |= other.bFailed;
|
||||
bFailedEarlier |= other.bFailedEarlier;
|
||||
iPossibleDancePoints += other.iPossibleDancePoints;
|
||||
iActualDancePoints += other.iActualDancePoints;
|
||||
|
||||
for( int t=0; t<NUM_TAP_NOTE_SCORES; t++ )
|
||||
iTapNoteScores[t] += other.iTapNoteScores[t];
|
||||
for( int h=0; h<NUM_HOLD_NOTE_SCORES; h++ )
|
||||
iHoldNoteScores[h] += other.iHoldNoteScores[h];
|
||||
iCurCombo += other.iCurCombo;
|
||||
iMaxCombo += other.iMaxCombo;
|
||||
iCurMissCombo += other.iCurMissCombo;
|
||||
iScore += other.iScore;
|
||||
iMaxScore += other.iMaxScore;
|
||||
iCurMaxScore += other.iCurMaxScore;
|
||||
radarPossible += other.radarPossible;
|
||||
radarActual += other.radarActual;
|
||||
fSecondsBeforeFail += other.fSecondsBeforeFail;
|
||||
iSongsPassed += other.iSongsPassed;
|
||||
iSongsPlayed += other.iSongsPlayed;
|
||||
iTotalError += other.iTotalError;
|
||||
fCaloriesBurned += other.fCaloriesBurned;
|
||||
|
||||
const float fOtherFirstSecond = other.fFirstSecond + fLastSecond;
|
||||
const float fOtherLastSecond = other.fLastSecond + fLastSecond;
|
||||
fLastSecond = fOtherLastSecond;
|
||||
|
||||
map<float,float>::const_iterator it;
|
||||
for( it = other.fLifeRecord.begin(); it != other.fLifeRecord.end(); ++it )
|
||||
{
|
||||
const float pos = it->first;
|
||||
const float life = it->second;
|
||||
fLifeRecord[fOtherFirstSecond+pos] = life;
|
||||
}
|
||||
|
||||
for( unsigned i=0; i<other.ComboList.size(); ++i )
|
||||
{
|
||||
const Combo_t &combo = other.ComboList[i];
|
||||
|
||||
Combo_t newcombo(combo);
|
||||
newcombo.fStartSecond += fOtherFirstSecond;
|
||||
ComboList.push_back( newcombo );
|
||||
}
|
||||
|
||||
/* Merge identical combos. This normally only happens in course mode, when a combo
|
||||
* continues between songs. */
|
||||
for( unsigned i=1; i<ComboList.size(); ++i )
|
||||
{
|
||||
Combo_t &prevcombo = ComboList[i-1];
|
||||
Combo_t &combo = ComboList[i];
|
||||
const float PrevComboEnd = prevcombo.fStartSecond + prevcombo.fSizeSeconds;
|
||||
const float ThisComboStart = combo.fStartSecond;
|
||||
if( fabsf(PrevComboEnd - ThisComboStart) > 0.001 )
|
||||
continue;
|
||||
|
||||
/* These are really the same combo. */
|
||||
prevcombo.fSizeSeconds += combo.fSizeSeconds;
|
||||
prevcombo.cnt += combo.cnt;
|
||||
ComboList.erase( ComboList.begin()+i );
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
Grade PlayerStageStats::GetGrade() const
|
||||
{
|
||||
if( bFailedEarlier )
|
||||
return GRADE_FAILED;
|
||||
|
||||
/* XXX: This entire calculation should be in ScoreKeeper, but final evaluation
|
||||
* is tricky since at that point the ScoreKeepers no longer exist.
|
||||
*
|
||||
* See http://www.aaroninjapan.com/ddr2.html ("Regular play scoring") */
|
||||
float Possible = 0, Actual = 0;
|
||||
FOREACH_TapNoteScore( tns )
|
||||
{
|
||||
int iTapScoreValue;
|
||||
switch( tns )
|
||||
{
|
||||
case TNS_NONE: iTapScoreValue = 0; break;
|
||||
case TNS_HIT_MINE: iTapScoreValue = PREFSMAN->m_iGradeWeightHitMine; break;
|
||||
case TNS_MISS: iTapScoreValue = PREFSMAN->m_iGradeWeightMiss; break;
|
||||
case TNS_BOO: iTapScoreValue = PREFSMAN->m_iGradeWeightBoo; break;
|
||||
case TNS_GOOD: iTapScoreValue = PREFSMAN->m_iGradeWeightGood; break;
|
||||
case TNS_GREAT: iTapScoreValue = PREFSMAN->m_iGradeWeightGreat; break;
|
||||
case TNS_PERFECT: iTapScoreValue = PREFSMAN->m_iGradeWeightPerfect; break;
|
||||
case TNS_MARVELOUS: iTapScoreValue = PREFSMAN->m_iGradeWeightMarvelous; break;
|
||||
default: FAIL_M( ssprintf("%i", tns) ); break;
|
||||
}
|
||||
Actual += iTapNoteScores[tns] * iTapScoreValue;
|
||||
Possible += iTapNoteScores[tns] * PREFSMAN->m_iGradeWeightMarvelous;
|
||||
}
|
||||
|
||||
FOREACH_HoldNoteScore( hns )
|
||||
{
|
||||
int iHoldScoreValue;
|
||||
switch( hns )
|
||||
{
|
||||
case HNS_NONE: iHoldScoreValue = 0; break;
|
||||
case HNS_NG: iHoldScoreValue = PREFSMAN->m_iGradeWeightNG; break;
|
||||
case HNS_OK: iHoldScoreValue = PREFSMAN->m_iGradeWeightOK; break;
|
||||
default: FAIL_M( ssprintf("%i", hns) ); break;
|
||||
}
|
||||
Actual += iHoldNoteScores[hns] * iHoldScoreValue;
|
||||
Possible += iHoldNoteScores[hns] * PREFSMAN->m_iGradeWeightOK;
|
||||
}
|
||||
|
||||
LOG->Trace( "GetGrade: Actual: %f, Possible: %f", Actual, Possible );
|
||||
|
||||
Grade grade = GRADE_FAILED;
|
||||
|
||||
float fPercent = (Possible == 0) ? 0 : Actual / Possible;
|
||||
|
||||
FOREACH_Grade(g)
|
||||
{
|
||||
if( fPercent >= PREFSMAN->m_fGradePercent[g] )
|
||||
{
|
||||
grade = g;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG->Trace( "GetGrade: Grade: %s, %i", GradeToString(grade).c_str(), PREFSMAN->m_bGradeTier02IsAllPerfects );
|
||||
if( PREFSMAN->m_bGradeTier02IsAllPerfects )
|
||||
{
|
||||
if( iTapNoteScores[TNS_MARVELOUS] > 0 &&
|
||||
iTapNoteScores[TNS_PERFECT] == 0 &&
|
||||
iTapNoteScores[TNS_GREAT] == 0 &&
|
||||
iTapNoteScores[TNS_GOOD] == 0 &&
|
||||
iTapNoteScores[TNS_BOO] == 0 &&
|
||||
iTapNoteScores[TNS_MISS] == 0 &&
|
||||
iTapNoteScores[TNS_HIT_MINE] == 0 &&
|
||||
iHoldNoteScores[HNS_NG] == 0 )
|
||||
return GRADE_TIER_1;
|
||||
|
||||
if( iTapNoteScores[TNS_PERFECT] > 0 &&
|
||||
iTapNoteScores[TNS_GREAT] == 0 &&
|
||||
iTapNoteScores[TNS_GOOD] == 0 &&
|
||||
iTapNoteScores[TNS_BOO] == 0 &&
|
||||
iTapNoteScores[TNS_MISS] == 0 &&
|
||||
iTapNoteScores[TNS_HIT_MINE] == 0 &&
|
||||
iHoldNoteScores[HNS_NG] == 0 )
|
||||
return GRADE_TIER_2;
|
||||
|
||||
return max( grade, GRADE_TIER_3 );
|
||||
}
|
||||
|
||||
return grade;
|
||||
}
|
||||
|
||||
float PlayerStageStats::GetPercentDancePoints() const
|
||||
{
|
||||
if( iPossibleDancePoints == 0 )
|
||||
return 0; // div/0
|
||||
|
||||
if( iActualDancePoints == iPossibleDancePoints )
|
||||
return 1; // correct for rounding error
|
||||
|
||||
/* This can happen in battle, with transform attacks. */
|
||||
//ASSERT_M( iActualDancePoints <= iPossibleDancePoints, ssprintf("%i/%i", iActualDancePoints, iPossibleDancePoints) );
|
||||
|
||||
float fPercentDancePoints = iActualDancePoints / (float)iPossibleDancePoints;
|
||||
|
||||
return fPercentDancePoints;
|
||||
}
|
||||
|
||||
void PlayerStageStats::SetLifeRecordAt( float fLife, float fSecond )
|
||||
{
|
||||
if( fSecond < 0 )
|
||||
return;
|
||||
|
||||
fFirstSecond = min( fSecond, fFirstSecond );
|
||||
fLastSecond = max( fSecond, fLastSecond );
|
||||
//LOG->Trace( "fLastSecond = %f", fLastSecond );
|
||||
|
||||
if( !fLifeRecord.empty() )
|
||||
{
|
||||
const float old = GetLifeRecordAt( fSecond );
|
||||
if( fabsf(old-fSecond) < 0.001f )
|
||||
return; /* no change */
|
||||
}
|
||||
|
||||
fLifeRecord[fSecond] = fLife;
|
||||
}
|
||||
|
||||
float PlayerStageStats::GetLifeRecordAt( float fSecond ) const
|
||||
{
|
||||
/* Find the first element whose key is not less than k. */
|
||||
map<float,float>::const_iterator it = fLifeRecord.lower_bound( fSecond );
|
||||
|
||||
/* Find the first element whose key is less than k. */
|
||||
if( it != fLifeRecord.begin() )
|
||||
--it;
|
||||
|
||||
return it->second;
|
||||
|
||||
}
|
||||
|
||||
float PlayerStageStats::GetLifeRecordLerpAt( float fSecond ) const
|
||||
{
|
||||
/* Find the first element whose key is not less than k. */
|
||||
map<float,float>::const_iterator later = fLifeRecord.lower_bound( fSecond );
|
||||
|
||||
/* Find the first element whose key is less than k. */
|
||||
map<float,float>::const_iterator earlier = later;
|
||||
if( earlier != fLifeRecord.begin() )
|
||||
--earlier;
|
||||
|
||||
if( earlier->first == later->first )
|
||||
return earlier->second;
|
||||
|
||||
/* earlier <= pos <= later */
|
||||
const float f = SCALE( fSecond, earlier->first, later->first, 1, 0 );
|
||||
return earlier->second * f + later->second * (1-f);
|
||||
}
|
||||
|
||||
|
||||
void PlayerStageStats::GetLifeRecord( float *fLifeOut, int iNumSamples ) const
|
||||
{
|
||||
for( int i = 0; i < iNumSamples; ++i )
|
||||
{
|
||||
float from = SCALE( i, 0, (float)iNumSamples, fFirstSecond, fLastSecond );
|
||||
fLifeOut[i] = GetLifeRecordLerpAt( from );
|
||||
}
|
||||
}
|
||||
|
||||
/* If "rollover" is true, we're being called before gameplay begins, so we can record
|
||||
* the amount of the first combo that comes from the previous song. */
|
||||
void PlayerStageStats::UpdateComboList( float fSecond, bool rollover )
|
||||
{
|
||||
if( fSecond < 0 )
|
||||
return;
|
||||
|
||||
if( !rollover )
|
||||
{
|
||||
fFirstSecond = min( fSecond, fFirstSecond );
|
||||
fLastSecond = max( fSecond, fLastSecond );
|
||||
//LOG->Trace( "fLastSecond = %f", fLastSecond );
|
||||
}
|
||||
|
||||
int cnt = iCurCombo;
|
||||
if( !cnt )
|
||||
return; /* no combo */
|
||||
|
||||
if( ComboList.size() == 0 || ComboList.back().cnt >= cnt )
|
||||
{
|
||||
/* If the previous combo (if any) starts on -9999, then we rolled over some
|
||||
* combo, but missed the first step. Remove it. */
|
||||
if( ComboList.size() && ComboList.back().fStartSecond == -9999 )
|
||||
ComboList.erase( ComboList.begin()+ComboList.size()-1, ComboList.end() );
|
||||
|
||||
/* This is a new combo. */
|
||||
Combo_t NewCombo;
|
||||
/* "start" is the position that the combo started within this song. If we're
|
||||
* recording rollover, the combo hasn't started yet (within this song), so put
|
||||
* a placeholder in and set it on the next call. (Otherwise, start will be less
|
||||
* than fFirstPos.) */
|
||||
if( rollover )
|
||||
NewCombo.fStartSecond = -9999;
|
||||
else
|
||||
NewCombo.fStartSecond = fSecond;
|
||||
ComboList.push_back( NewCombo );
|
||||
}
|
||||
|
||||
Combo_t &combo = ComboList.back();
|
||||
combo.fSizeSeconds = fSecond - combo.fStartSecond;
|
||||
combo.cnt = cnt;
|
||||
if( !rollover && combo.fStartSecond == -9999 )
|
||||
combo.fStartSecond = fSecond;
|
||||
|
||||
if( rollover )
|
||||
combo.rollover = cnt;
|
||||
}
|
||||
|
||||
/* This returns the largest combo contained within the song, as if
|
||||
* m_bComboContinuesBetweenSongs is turned off. */
|
||||
PlayerStageStats::Combo_t PlayerStageStats::GetMaxCombo() const
|
||||
{
|
||||
if( ComboList.size() == 0 )
|
||||
return Combo_t();
|
||||
|
||||
int m = 0;
|
||||
for( unsigned i = 1; i < ComboList.size(); ++i )
|
||||
{
|
||||
if( ComboList[i].cnt > ComboList[m].cnt )
|
||||
m = i;
|
||||
}
|
||||
|
||||
return ComboList[m];
|
||||
}
|
||||
|
||||
int PlayerStageStats::GetComboAtStartOfStage() const
|
||||
{
|
||||
if( ComboList.empty() )
|
||||
return 0;
|
||||
else
|
||||
return ComboList[0].rollover;
|
||||
}
|
||||
|
||||
bool PlayerStageStats::FullComboOfScore( TapNoteScore tnsAllGreaterOrEqual ) const
|
||||
{
|
||||
ASSERT( tnsAllGreaterOrEqual >= TNS_GREAT );
|
||||
ASSERT( tnsAllGreaterOrEqual <= TNS_MARVELOUS );
|
||||
|
||||
if( iHoldNoteScores[HNS_NG] > 0 )
|
||||
return false;
|
||||
|
||||
for( int i=TNS_MISS; i<tnsAllGreaterOrEqual; i++ )
|
||||
{
|
||||
if( iTapNoteScores[i] > 0 )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlayerStageStats::SingleDigitsOfScore( TapNoteScore tnsAllGreaterOrEqual ) const
|
||||
{
|
||||
return FullComboOfScore( tnsAllGreaterOrEqual ) &&
|
||||
iTapNoteScores[tnsAllGreaterOrEqual] < 10;
|
||||
}
|
||||
|
||||
bool PlayerStageStats::OneOfScore( TapNoteScore tnsAllGreaterOrEqual ) const
|
||||
{
|
||||
return FullComboOfScore( tnsAllGreaterOrEqual ) &&
|
||||
iTapNoteScores[tnsAllGreaterOrEqual] == 1;
|
||||
}
|
||||
|
||||
int PlayerStageStats::GetTotalTaps() const
|
||||
{
|
||||
int iTotalTaps = 0;
|
||||
for( int i=TNS_MISS; i<NUM_TAP_NOTE_SCORES; i++ )
|
||||
{
|
||||
iTotalTaps += iTapNoteScores[i];
|
||||
}
|
||||
return iTotalTaps;
|
||||
}
|
||||
|
||||
float PlayerStageStats::GetPercentageOfTaps( TapNoteScore tns ) const
|
||||
{
|
||||
int iTotalTaps = 0;
|
||||
for( int i=TNS_MISS; i<NUM_TAP_NOTE_SCORES; i++ )
|
||||
{
|
||||
iTotalTaps += iTapNoteScores[i];
|
||||
}
|
||||
return iTapNoteScores[tns] / (float)iTotalTaps;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (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.
|
||||
*/
|
||||
@@ -0,0 +1,119 @@
|
||||
/* PlayerStageStats - Contains statistics for one stage of play - either one song, or a whole course. */
|
||||
|
||||
#ifndef PlayerStageStats_H
|
||||
#define PlayerStageStats_H
|
||||
|
||||
#include "Grade.h"
|
||||
#include "RadarValues.h"
|
||||
#include <map>
|
||||
class Steps;
|
||||
|
||||
struct PlayerStageStats
|
||||
{
|
||||
PlayerStageStats() { Init(); }
|
||||
void Init();
|
||||
|
||||
void AddStats( const PlayerStageStats& other ); // accumulate
|
||||
|
||||
Grade GetGrade() const;
|
||||
float GetPercentDancePoints() const;
|
||||
vector<Steps*> vpSteps;
|
||||
float fAliveSeconds; // how far into the music did they last before failing? Updated by Gameplay, scaled by music rate.
|
||||
|
||||
/* Set if the player actually failed at any point during the song. This is always
|
||||
* false in FAIL_OFF. If recovery is enabled and two players are playing,
|
||||
* this is only set if both players were failing at the same time. */
|
||||
bool bFailed;
|
||||
|
||||
/* This indicates whether the player bottomed out his bar/ran out of lives at some
|
||||
* point during the song. It's set in all fail modes. */
|
||||
bool bFailedEarlier;
|
||||
int iPossibleDancePoints;
|
||||
int iActualDancePoints;
|
||||
int iTapNoteScores[NUM_TAP_NOTE_SCORES];
|
||||
int iHoldNoteScores[NUM_HOLD_NOTE_SCORES];
|
||||
int iCurCombo;
|
||||
int iMaxCombo;
|
||||
int iCurMissCombo;
|
||||
int iScore;
|
||||
int iCurMaxScore;
|
||||
int iMaxScore;
|
||||
int iBonus; // bonus to be added on screeneval
|
||||
RadarValues radarPossible; // filled in by ScreenGameplay on start of notes
|
||||
RadarValues radarActual;
|
||||
float fSecondsBeforeFail; // -1 means didn't/hasn't failed
|
||||
/* The number of songs played and passed, respectively. */
|
||||
int iSongsPassed;
|
||||
int iSongsPlayed;
|
||||
int iTotalError;
|
||||
|
||||
// workout
|
||||
float fCaloriesBurned;
|
||||
|
||||
map<float,float> fLifeRecord;
|
||||
void SetLifeRecordAt( float fLife, float fSecond );
|
||||
void GetLifeRecord( float *fLifeOut, int iNumSamples ) const;
|
||||
float GetLifeRecordAt( float fSecond ) const;
|
||||
float GetLifeRecordLerpAt( float fSecond ) const;
|
||||
|
||||
struct Combo_t
|
||||
{
|
||||
/* Start and size of this combo, in the same scale as the combo list mapping and
|
||||
* the life record. */
|
||||
float fStartSecond, fSizeSeconds;
|
||||
|
||||
/* Combo size, in steps. */
|
||||
int cnt;
|
||||
|
||||
/* Size of the combo that didn't come from this stage (rollover from the last song).
|
||||
* (This is a subset of cnt.) */
|
||||
int rollover;
|
||||
|
||||
/* Get the size of the combo that came from this song. */
|
||||
int GetStageCnt() const { return cnt - rollover; }
|
||||
|
||||
Combo_t(): fStartSecond(0), fSizeSeconds(0), cnt(0), rollover(0) { }
|
||||
bool IsZero() const { return fStartSecond < 0; }
|
||||
};
|
||||
vector<Combo_t> ComboList;
|
||||
float fFirstSecond;
|
||||
float fLastSecond;
|
||||
|
||||
int GetComboAtStartOfStage() const;
|
||||
bool FullComboOfScore( TapNoteScore tnsAllGreaterOrEqual ) const;
|
||||
bool FullCombo() const { return FullComboOfScore(TNS_GREAT); }
|
||||
bool SingleDigitsOfScore( TapNoteScore tnsAllGreaterOrEqual ) const;
|
||||
bool OneOfScore( TapNoteScore tnsAllGreaterOrEqual ) const;
|
||||
int GetTotalTaps() const;
|
||||
float GetPercentageOfTaps( TapNoteScore tns ) const;
|
||||
void UpdateComboList( float fSecond, bool rollover );
|
||||
Combo_t GetMaxCombo() const;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (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.
|
||||
*/
|
||||
@@ -1,10 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "StageStats.h"
|
||||
#include "GameState.h"
|
||||
#include "RageLog.h"
|
||||
#include "SongManager.h"
|
||||
#include "RageUtil.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "Foreach.h"
|
||||
#include "Steps.h"
|
||||
#include "song.h"
|
||||
@@ -12,182 +8,6 @@
|
||||
StageStats g_CurStageStats;
|
||||
vector<StageStats> g_vPlayedStageStats;
|
||||
|
||||
void PlayerStageStats::Init()
|
||||
{
|
||||
vpSteps.clear();
|
||||
fAliveSeconds = 0;
|
||||
bFailed = bFailedEarlier = false;
|
||||
iPossibleDancePoints = iActualDancePoints = 0;
|
||||
iCurCombo = iMaxCombo = iCurMissCombo = iScore = iBonus = iMaxScore = iCurMaxScore = 0;
|
||||
fSecondsBeforeFail = 0;
|
||||
iSongsPassed = iSongsPlayed = 0;
|
||||
iTotalError = 0;
|
||||
fCaloriesBurned = 0;
|
||||
|
||||
ZERO( iTapNoteScores );
|
||||
ZERO( iHoldNoteScores );
|
||||
radarPossible.Zero();
|
||||
radarActual.Zero();
|
||||
|
||||
fFirstSecond = 999999;
|
||||
fLastSecond = 0;
|
||||
}
|
||||
|
||||
void PlayerStageStats::AddStats( const PlayerStageStats& other )
|
||||
{
|
||||
FOREACH_CONST( Steps*, other.vpSteps, s )
|
||||
vpSteps.push_back( *s );
|
||||
fAliveSeconds += other.fAliveSeconds;
|
||||
bFailed |= other.bFailed;
|
||||
bFailedEarlier |= other.bFailedEarlier;
|
||||
iPossibleDancePoints += other.iPossibleDancePoints;
|
||||
iActualDancePoints += other.iActualDancePoints;
|
||||
|
||||
for( int t=0; t<NUM_TAP_NOTE_SCORES; t++ )
|
||||
iTapNoteScores[t] += other.iTapNoteScores[t];
|
||||
for( int h=0; h<NUM_HOLD_NOTE_SCORES; h++ )
|
||||
iHoldNoteScores[h] += other.iHoldNoteScores[h];
|
||||
iCurCombo += other.iCurCombo;
|
||||
iMaxCombo += other.iMaxCombo;
|
||||
iCurMissCombo += other.iCurMissCombo;
|
||||
iScore += other.iScore;
|
||||
iMaxScore += other.iMaxScore;
|
||||
iCurMaxScore += other.iCurMaxScore;
|
||||
radarPossible += other.radarPossible;
|
||||
radarActual += other.radarActual;
|
||||
fSecondsBeforeFail += other.fSecondsBeforeFail;
|
||||
iSongsPassed += other.iSongsPassed;
|
||||
iSongsPlayed += other.iSongsPlayed;
|
||||
iTotalError += other.iTotalError;
|
||||
fCaloriesBurned += other.fCaloriesBurned;
|
||||
|
||||
const float fOtherFirstSecond = other.fFirstSecond + fLastSecond;
|
||||
const float fOtherLastSecond = other.fLastSecond + fLastSecond;
|
||||
fLastSecond = fOtherLastSecond;
|
||||
|
||||
map<float,float>::const_iterator it;
|
||||
for( it = other.fLifeRecord.begin(); it != other.fLifeRecord.end(); ++it )
|
||||
{
|
||||
const float pos = it->first;
|
||||
const float life = it->second;
|
||||
fLifeRecord[fOtherFirstSecond+pos] = life;
|
||||
}
|
||||
|
||||
for( unsigned i=0; i<other.ComboList.size(); ++i )
|
||||
{
|
||||
const Combo_t &combo = other.ComboList[i];
|
||||
|
||||
Combo_t newcombo(combo);
|
||||
newcombo.fStartSecond += fOtherFirstSecond;
|
||||
ComboList.push_back( newcombo );
|
||||
}
|
||||
|
||||
/* Merge identical combos. This normally only happens in course mode, when a combo
|
||||
* continues between songs. */
|
||||
for( unsigned i=1; i<ComboList.size(); ++i )
|
||||
{
|
||||
Combo_t &prevcombo = ComboList[i-1];
|
||||
Combo_t &combo = ComboList[i];
|
||||
const float PrevComboEnd = prevcombo.fStartSecond + prevcombo.fSizeSeconds;
|
||||
const float ThisComboStart = combo.fStartSecond;
|
||||
if( fabsf(PrevComboEnd - ThisComboStart) > 0.001 )
|
||||
continue;
|
||||
|
||||
/* These are really the same combo. */
|
||||
prevcombo.fSizeSeconds += combo.fSizeSeconds;
|
||||
prevcombo.cnt += combo.cnt;
|
||||
ComboList.erase( ComboList.begin()+i );
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
Grade PlayerStageStats::GetGrade() const
|
||||
{
|
||||
if( bFailedEarlier )
|
||||
return GRADE_FAILED;
|
||||
|
||||
/* XXX: This entire calculation should be in ScoreKeeper, but final evaluation
|
||||
* is tricky since at that point the ScoreKeepers no longer exist.
|
||||
*
|
||||
* See http://www.aaroninjapan.com/ddr2.html ("Regular play scoring") */
|
||||
float Possible = 0, Actual = 0;
|
||||
FOREACH_TapNoteScore( tns )
|
||||
{
|
||||
int iTapScoreValue;
|
||||
switch( tns )
|
||||
{
|
||||
case TNS_NONE: iTapScoreValue = 0; break;
|
||||
case TNS_HIT_MINE: iTapScoreValue = PREFSMAN->m_iGradeWeightHitMine; break;
|
||||
case TNS_MISS: iTapScoreValue = PREFSMAN->m_iGradeWeightMiss; break;
|
||||
case TNS_BOO: iTapScoreValue = PREFSMAN->m_iGradeWeightBoo; break;
|
||||
case TNS_GOOD: iTapScoreValue = PREFSMAN->m_iGradeWeightGood; break;
|
||||
case TNS_GREAT: iTapScoreValue = PREFSMAN->m_iGradeWeightGreat; break;
|
||||
case TNS_PERFECT: iTapScoreValue = PREFSMAN->m_iGradeWeightPerfect; break;
|
||||
case TNS_MARVELOUS: iTapScoreValue = PREFSMAN->m_iGradeWeightMarvelous; break;
|
||||
default: FAIL_M( ssprintf("%i", tns) ); break;
|
||||
}
|
||||
Actual += iTapNoteScores[tns] * iTapScoreValue;
|
||||
Possible += iTapNoteScores[tns] * PREFSMAN->m_iGradeWeightMarvelous;
|
||||
}
|
||||
|
||||
FOREACH_HoldNoteScore( hns )
|
||||
{
|
||||
int iHoldScoreValue;
|
||||
switch( hns )
|
||||
{
|
||||
case HNS_NONE: iHoldScoreValue = 0; break;
|
||||
case HNS_NG: iHoldScoreValue = PREFSMAN->m_iGradeWeightNG; break;
|
||||
case HNS_OK: iHoldScoreValue = PREFSMAN->m_iGradeWeightOK; break;
|
||||
default: FAIL_M( ssprintf("%i", hns) ); break;
|
||||
}
|
||||
Actual += iHoldNoteScores[hns] * iHoldScoreValue;
|
||||
Possible += iHoldNoteScores[hns] * PREFSMAN->m_iGradeWeightOK;
|
||||
}
|
||||
|
||||
LOG->Trace( "GetGrade: Actual: %f, Possible: %f", Actual, Possible );
|
||||
|
||||
Grade grade = GRADE_FAILED;
|
||||
|
||||
float fPercent = (Possible == 0) ? 0 : Actual / Possible;
|
||||
|
||||
FOREACH_Grade(g)
|
||||
{
|
||||
if( fPercent >= PREFSMAN->m_fGradePercent[g] )
|
||||
{
|
||||
grade = g;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG->Trace( "GetGrade: Grade: %s, %i", GradeToString(grade).c_str(), PREFSMAN->m_bGradeTier02IsAllPerfects );
|
||||
if( PREFSMAN->m_bGradeTier02IsAllPerfects )
|
||||
{
|
||||
if( iTapNoteScores[TNS_MARVELOUS] > 0 &&
|
||||
iTapNoteScores[TNS_PERFECT] == 0 &&
|
||||
iTapNoteScores[TNS_GREAT] == 0 &&
|
||||
iTapNoteScores[TNS_GOOD] == 0 &&
|
||||
iTapNoteScores[TNS_BOO] == 0 &&
|
||||
iTapNoteScores[TNS_MISS] == 0 &&
|
||||
iTapNoteScores[TNS_HIT_MINE] == 0 &&
|
||||
iHoldNoteScores[HNS_NG] == 0 )
|
||||
return GRADE_TIER_1;
|
||||
|
||||
if( iTapNoteScores[TNS_PERFECT] > 0 &&
|
||||
iTapNoteScores[TNS_GREAT] == 0 &&
|
||||
iTapNoteScores[TNS_GOOD] == 0 &&
|
||||
iTapNoteScores[TNS_BOO] == 0 &&
|
||||
iTapNoteScores[TNS_MISS] == 0 &&
|
||||
iTapNoteScores[TNS_HIT_MINE] == 0 &&
|
||||
iHoldNoteScores[HNS_NG] == 0 )
|
||||
return GRADE_TIER_2;
|
||||
|
||||
return max( grade, GRADE_TIER_3 );
|
||||
}
|
||||
|
||||
return grade;
|
||||
}
|
||||
|
||||
|
||||
void StageStats::Init()
|
||||
{
|
||||
playMode = PLAY_MODE_INVALID;
|
||||
@@ -261,203 +81,6 @@ bool StageStats::AllFailedEarlier() const
|
||||
return true;
|
||||
}
|
||||
|
||||
float PlayerStageStats::GetPercentDancePoints() const
|
||||
{
|
||||
if( iPossibleDancePoints == 0 )
|
||||
return 0; // div/0
|
||||
|
||||
if( iActualDancePoints == iPossibleDancePoints )
|
||||
return 1; // correct for rounding error
|
||||
|
||||
/* This can happen in battle, with transform attacks. */
|
||||
//ASSERT_M( iActualDancePoints <= iPossibleDancePoints, ssprintf("%i/%i", iActualDancePoints, iPossibleDancePoints) );
|
||||
|
||||
float fPercentDancePoints = iActualDancePoints / (float)iPossibleDancePoints;
|
||||
|
||||
return fPercentDancePoints;
|
||||
}
|
||||
|
||||
void PlayerStageStats::SetLifeRecordAt( float fLife, float fSecond )
|
||||
{
|
||||
if( fSecond < 0 )
|
||||
return;
|
||||
|
||||
fFirstSecond = min( fSecond, fFirstSecond );
|
||||
fLastSecond = max( fSecond, fLastSecond );
|
||||
//LOG->Trace( "fLastSecond = %f", fLastSecond );
|
||||
|
||||
if( !fLifeRecord.empty() )
|
||||
{
|
||||
const float old = GetLifeRecordAt( fSecond );
|
||||
if( fabsf(old-fSecond) < 0.001f )
|
||||
return; /* no change */
|
||||
}
|
||||
|
||||
fLifeRecord[fSecond] = fLife;
|
||||
}
|
||||
|
||||
float PlayerStageStats::GetLifeRecordAt( float fSecond ) const
|
||||
{
|
||||
/* Find the first element whose key is not less than k. */
|
||||
map<float,float>::const_iterator it = fLifeRecord.lower_bound( fSecond );
|
||||
|
||||
/* Find the first element whose key is less than k. */
|
||||
if( it != fLifeRecord.begin() )
|
||||
--it;
|
||||
|
||||
return it->second;
|
||||
|
||||
}
|
||||
|
||||
float PlayerStageStats::GetLifeRecordLerpAt( float fSecond ) const
|
||||
{
|
||||
/* Find the first element whose key is not less than k. */
|
||||
map<float,float>::const_iterator later = fLifeRecord.lower_bound( fSecond );
|
||||
|
||||
/* Find the first element whose key is less than k. */
|
||||
map<float,float>::const_iterator earlier = later;
|
||||
if( earlier != fLifeRecord.begin() )
|
||||
--earlier;
|
||||
|
||||
if( earlier->first == later->first )
|
||||
return earlier->second;
|
||||
|
||||
/* earlier <= pos <= later */
|
||||
const float f = SCALE( fSecond, earlier->first, later->first, 1, 0 );
|
||||
return earlier->second * f + later->second * (1-f);
|
||||
}
|
||||
|
||||
|
||||
void PlayerStageStats::GetLifeRecord( float *fLifeOut, int iNumSamples ) const
|
||||
{
|
||||
for( int i = 0; i < iNumSamples; ++i )
|
||||
{
|
||||
float from = SCALE( i, 0, (float)iNumSamples, fFirstSecond, fLastSecond );
|
||||
fLifeOut[i] = GetLifeRecordLerpAt( from );
|
||||
}
|
||||
}
|
||||
|
||||
/* If "rollover" is true, we're being called before gameplay begins, so we can record
|
||||
* the amount of the first combo that comes from the previous song. */
|
||||
void PlayerStageStats::UpdateComboList( float fSecond, bool rollover )
|
||||
{
|
||||
if( fSecond < 0 )
|
||||
return;
|
||||
|
||||
if( !rollover )
|
||||
{
|
||||
fFirstSecond = min( fSecond, fFirstSecond );
|
||||
fLastSecond = max( fSecond, fLastSecond );
|
||||
//LOG->Trace( "fLastSecond = %f", fLastSecond );
|
||||
}
|
||||
|
||||
int cnt = iCurCombo;
|
||||
if( !cnt )
|
||||
return; /* no combo */
|
||||
|
||||
if( ComboList.size() == 0 || ComboList.back().cnt >= cnt )
|
||||
{
|
||||
/* If the previous combo (if any) starts on -9999, then we rolled over some
|
||||
* combo, but missed the first step. Remove it. */
|
||||
if( ComboList.size() && ComboList.back().fStartSecond == -9999 )
|
||||
ComboList.erase( ComboList.begin()+ComboList.size()-1, ComboList.end() );
|
||||
|
||||
/* This is a new combo. */
|
||||
Combo_t NewCombo;
|
||||
/* "start" is the position that the combo started within this song. If we're
|
||||
* recording rollover, the combo hasn't started yet (within this song), so put
|
||||
* a placeholder in and set it on the next call. (Otherwise, start will be less
|
||||
* than fFirstPos.) */
|
||||
if( rollover )
|
||||
NewCombo.fStartSecond = -9999;
|
||||
else
|
||||
NewCombo.fStartSecond = fSecond;
|
||||
ComboList.push_back( NewCombo );
|
||||
}
|
||||
|
||||
Combo_t &combo = ComboList.back();
|
||||
combo.fSizeSeconds = fSecond - combo.fStartSecond;
|
||||
combo.cnt = cnt;
|
||||
if( !rollover && combo.fStartSecond == -9999 )
|
||||
combo.fStartSecond = fSecond;
|
||||
|
||||
if( rollover )
|
||||
combo.rollover = cnt;
|
||||
}
|
||||
|
||||
/* This returns the largest combo contained within the song, as if
|
||||
* m_bComboContinuesBetweenSongs is turned off. */
|
||||
PlayerStageStats::Combo_t PlayerStageStats::GetMaxCombo() const
|
||||
{
|
||||
if( ComboList.size() == 0 )
|
||||
return Combo_t();
|
||||
|
||||
int m = 0;
|
||||
for( unsigned i = 1; i < ComboList.size(); ++i )
|
||||
{
|
||||
if( ComboList[i].cnt > ComboList[m].cnt )
|
||||
m = i;
|
||||
}
|
||||
|
||||
return ComboList[m];
|
||||
}
|
||||
|
||||
int PlayerStageStats::GetComboAtStartOfStage() const
|
||||
{
|
||||
if( ComboList.empty() )
|
||||
return 0;
|
||||
else
|
||||
return ComboList[0].rollover;
|
||||
}
|
||||
|
||||
bool PlayerStageStats::FullComboOfScore( TapNoteScore tnsAllGreaterOrEqual ) const
|
||||
{
|
||||
ASSERT( tnsAllGreaterOrEqual >= TNS_GREAT );
|
||||
ASSERT( tnsAllGreaterOrEqual <= TNS_MARVELOUS );
|
||||
|
||||
if( iHoldNoteScores[HNS_NG] > 0 )
|
||||
return false;
|
||||
|
||||
for( int i=TNS_MISS; i<tnsAllGreaterOrEqual; i++ )
|
||||
{
|
||||
if( iTapNoteScores[i] > 0 )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlayerStageStats::SingleDigitsOfScore( TapNoteScore tnsAllGreaterOrEqual ) const
|
||||
{
|
||||
return FullComboOfScore( tnsAllGreaterOrEqual ) &&
|
||||
iTapNoteScores[tnsAllGreaterOrEqual] < 10;
|
||||
}
|
||||
|
||||
bool PlayerStageStats::OneOfScore( TapNoteScore tnsAllGreaterOrEqual ) const
|
||||
{
|
||||
return FullComboOfScore( tnsAllGreaterOrEqual ) &&
|
||||
iTapNoteScores[tnsAllGreaterOrEqual] == 1;
|
||||
}
|
||||
|
||||
int PlayerStageStats::GetTotalTaps() const
|
||||
{
|
||||
int iTotalTaps = 0;
|
||||
for( int i=TNS_MISS; i<NUM_TAP_NOTE_SCORES; i++ )
|
||||
{
|
||||
iTotalTaps += iTapNoteScores[i];
|
||||
}
|
||||
return iTotalTaps;
|
||||
}
|
||||
|
||||
float PlayerStageStats::GetPercentageOfTaps( TapNoteScore tns ) const
|
||||
{
|
||||
int iTotalTaps = 0;
|
||||
for( int i=TNS_MISS; i<NUM_TAP_NOTE_SCORES; i++ )
|
||||
{
|
||||
iTotalTaps += iTapNoteScores[i];
|
||||
}
|
||||
return iTapNoteScores[tns] / (float)iTotalTaps;
|
||||
}
|
||||
|
||||
static Grade GetBestGrade()
|
||||
{
|
||||
Grade g = NUM_GRADES;
|
||||
|
||||
@@ -1,98 +1,13 @@
|
||||
/* StageStats - Contains statistics for one stage of play - either one song, or a whole course. */
|
||||
|
||||
#ifndef STAGE_STATS_H
|
||||
#define STAGE_STATS_H
|
||||
#ifndef StageStats_H
|
||||
#define StageStats_H
|
||||
|
||||
#include "PlayerNumber.h"
|
||||
#include "GameConstantsAndTypes.h"
|
||||
#include "Grade.h"
|
||||
#include "RadarValues.h"
|
||||
#include <map>
|
||||
#include "PlayerStageStats.h"
|
||||
class Song;
|
||||
class Steps;
|
||||
class Style;
|
||||
|
||||
struct PlayerStageStats
|
||||
{
|
||||
PlayerStageStats() { Init(); }
|
||||
void Init();
|
||||
|
||||
void AddStats( const PlayerStageStats& other ); // accumulate
|
||||
|
||||
Grade GetGrade() const;
|
||||
float GetPercentDancePoints() const;
|
||||
vector<Steps*> vpSteps;
|
||||
float fAliveSeconds; // how far into the music did they last before failing? Updated by Gameplay, scaled by music rate.
|
||||
|
||||
/* Set if the player actually failed at any point during the song. This is always
|
||||
* false in FAIL_OFF. If recovery is enabled and two players are playing,
|
||||
* this is only set if both players were failing at the same time. */
|
||||
bool bFailed;
|
||||
|
||||
/* This indicates whether the player bottomed out his bar/ran out of lives at some
|
||||
* point during the song. It's set in all fail modes. */
|
||||
bool bFailedEarlier;
|
||||
int iPossibleDancePoints;
|
||||
int iActualDancePoints;
|
||||
int iTapNoteScores[NUM_TAP_NOTE_SCORES];
|
||||
int iHoldNoteScores[NUM_HOLD_NOTE_SCORES];
|
||||
int iCurCombo;
|
||||
int iMaxCombo;
|
||||
int iCurMissCombo;
|
||||
int iScore;
|
||||
int iCurMaxScore;
|
||||
int iMaxScore;
|
||||
int iBonus; // bonus to be added on screeneval
|
||||
RadarValues radarPossible; // filled in by ScreenGameplay on start of notes
|
||||
RadarValues radarActual;
|
||||
float fSecondsBeforeFail; // -1 means didn't/hasn't failed
|
||||
/* The number of songs played and passed, respectively. */
|
||||
int iSongsPassed;
|
||||
int iSongsPlayed;
|
||||
int iTotalError;
|
||||
|
||||
// workout
|
||||
float fCaloriesBurned;
|
||||
|
||||
map<float,float> fLifeRecord;
|
||||
void SetLifeRecordAt( float fLife, float fSecond );
|
||||
void GetLifeRecord( float *fLifeOut, int iNumSamples ) const;
|
||||
float GetLifeRecordAt( float fSecond ) const;
|
||||
float GetLifeRecordLerpAt( float fSecond ) const;
|
||||
|
||||
struct Combo_t
|
||||
{
|
||||
/* Start and size of this combo, in the same scale as the combo list mapping and
|
||||
* the life record. */
|
||||
float fStartSecond, fSizeSeconds;
|
||||
|
||||
/* Combo size, in steps. */
|
||||
int cnt;
|
||||
|
||||
/* Size of the combo that didn't come from this stage (rollover from the last song).
|
||||
* (This is a subset of cnt.) */
|
||||
int rollover;
|
||||
|
||||
/* Get the size of the combo that came from this song. */
|
||||
int GetStageCnt() const { return cnt - rollover; }
|
||||
|
||||
Combo_t(): fStartSecond(0), fSizeSeconds(0), cnt(0), rollover(0) { }
|
||||
bool IsZero() const { return fStartSecond < 0; }
|
||||
};
|
||||
vector<Combo_t> ComboList;
|
||||
float fFirstSecond;
|
||||
float fLastSecond;
|
||||
|
||||
int GetComboAtStartOfStage() const;
|
||||
bool FullComboOfScore( TapNoteScore tnsAllGreaterOrEqual ) const;
|
||||
bool FullCombo() const { return FullComboOfScore(TNS_GREAT); }
|
||||
bool SingleDigitsOfScore( TapNoteScore tnsAllGreaterOrEqual ) const;
|
||||
bool OneOfScore( TapNoteScore tnsAllGreaterOrEqual ) const;
|
||||
int GetTotalTaps() const;
|
||||
float GetPercentageOfTaps( TapNoteScore tns ) const;
|
||||
void UpdateComboList( float fSecond, bool rollover );
|
||||
Combo_t GetMaxCombo() const;
|
||||
};
|
||||
|
||||
struct StageStats
|
||||
{
|
||||
|
||||
@@ -824,6 +824,12 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)"\
|
||||
<File
|
||||
RelativePath="PlayerOptions.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="PlayerStageStats.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="PlayerStageStats.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="PlayerState.cpp">
|
||||
</File>
|
||||
|
||||
Reference in New Issue
Block a user