Files
itgmania212121/stepmania/src/PlayerStageStats.cpp
T

774 lines
22 KiB
C++
Raw Normal View History

2005-02-16 00:37:50 +00:00
#include "global.h"
#include "PlayerStageStats.h"
#include "RageLog.h"
#include "ThemeManager.h"
2005-02-16 00:37:50 +00:00
#include "Foreach.h"
2005-03-31 06:13:40 +00:00
#include "LuaManager.h"
#include <float.h>
#include "GameState.h"
#include "Course.h"
#include "Steps.h"
#include "ScoreKeeperNormal.h"
2006-03-17 03:44:11 +00:00
#include "PrefsManager.h"
#include "CommonMetrics.h"
2005-02-16 00:37:50 +00:00
#define GRADE_PERCENT_TIER(i) THEME->GetMetricF("PlayerStageStats",ssprintf("GradePercent%s",GradeToString((Grade)i).c_str()))
#define GRADE_TIER02_IS_ALL_W2S THEME->GetMetricB("PlayerStageStats","GradeTier02IsAllW2s")
#define GRADE_TIER01_IS_ALL_W2S THEME->GetMetricB("PlayerStageStats","GradeTier01IsAllW2s")
#define GRADE_TIER02_IS_FULL_COMBO THEME->GetMetricB("PlayerStageStats","GradeTier02IsFullCombo")
static ThemeMetric<TapNoteScore> g_MinScoreToMaintainCombo( "Gameplay", "MinScoreToMaintainCombo" );
const float LESSON_PASS_THRESHOLD = 0.8f;
2006-03-17 03:44:11 +00:00
Grade GetGradeFromPercent( float fPercent, bool bMerciful );
2005-02-16 00:37:50 +00:00
void PlayerStageStats::Init()
{
2007-04-19 14:34:42 +00:00
m_bJoined = false;
m_vpPossibleSteps.clear();
m_iStepsPlayed = 0;
m_fAliveSeconds = 0;
m_bFailed = false;
m_iPossibleDancePoints = 0;
m_iCurPossibleDancePoints = 0;
m_iActualDancePoints = 0;
m_iPossibleGradePoints = 0;
m_iCurCombo = 0;
m_iMaxCombo = 0;
m_iCurMissCombo = 0;
2006-11-14 11:23:13 +00:00
m_iCurScoreMultiplier = 1;
m_iScore = 0;
m_iMaxScore = 0;
m_iCurMaxScore = 0;
m_iSongsPassed = 0;
m_iSongsPlayed = 0;
m_fLifeRemainingSeconds = 0;
2007-09-14 05:56:12 +00:00
m_iNumControllerSteps = 0;
m_fCaloriesBurned = 0;
ZERO( m_iTapNoteScores );
ZERO( m_iHoldNoteScores );
m_radarPossible.Zero();
m_radarActual.Zero();
m_fFirstSecond = FLT_MAX;
m_fLastSecond = 0;
2005-09-11 00:49:16 +00:00
2006-10-07 04:39:48 +00:00
m_pdaToShow = PerDifficultyAward_Invalid;
m_pcaToShow = PeakComboAward_Invalid;
2008-04-05 19:19:13 +00:00
m_iProfileHighScoreIndex = -1;
m_iMachineHighScoreIndex = -1;
m_bDisqualified = false;
2006-09-26 20:49:10 +00:00
m_rc = RankingCategory_Invalid;
m_HighScore = HighScore();
2005-02-16 00:37:50 +00:00
}
void PlayerStageStats::AddStats( const PlayerStageStats& other )
{
2007-04-19 14:34:42 +00:00
m_bJoined = other.m_bJoined;
FOREACH_CONST( Steps*, other.m_vpPossibleSteps, s )
m_vpPossibleSteps.push_back( *s );
m_iStepsPlayed += other.m_iStepsPlayed;
m_fAliveSeconds += other.m_fAliveSeconds;
m_bFailed |= other.m_bFailed;
m_iPossibleDancePoints += other.m_iPossibleDancePoints;
m_iActualDancePoints += other.m_iActualDancePoints;
m_iCurPossibleDancePoints += other.m_iCurPossibleDancePoints;
m_iPossibleGradePoints += other.m_iPossibleGradePoints;
2005-02-16 00:37:50 +00:00
for( int t=0; t<NUM_TapNoteScore; t++ )
m_iTapNoteScores[t] += other.m_iTapNoteScores[t];
for( int h=0; h<NUM_HoldNoteScore; h++ )
m_iHoldNoteScores[h] += other.m_iHoldNoteScores[h];
m_iCurCombo += other.m_iCurCombo;
m_iMaxCombo += other.m_iMaxCombo;
m_iCurMissCombo += other.m_iCurMissCombo;
m_iScore += other.m_iScore;
m_iMaxScore += other.m_iMaxScore;
m_iCurMaxScore += other.m_iCurMaxScore;
m_radarPossible += other.m_radarPossible;
m_radarActual += other.m_radarActual;
m_iSongsPassed += other.m_iSongsPassed;
m_iSongsPlayed += other.m_iSongsPlayed;
2007-09-14 05:56:12 +00:00
m_iNumControllerSteps += other.m_iNumControllerSteps;
m_fCaloriesBurned += other.m_fCaloriesBurned;
m_fLifeRemainingSeconds = other.m_fLifeRemainingSeconds; // don't accumulate
m_bDisqualified |= other.m_bDisqualified;
const float fOtherFirstSecond = other.m_fFirstSecond + m_fLastSecond;
const float fOtherLastSecond = other.m_fLastSecond + m_fLastSecond;
m_fLastSecond = fOtherLastSecond;
2005-02-16 00:37:50 +00:00
map<float,float>::const_iterator it;
for( it = other.m_fLifeRecord.begin(); it != other.m_fLifeRecord.end(); ++it )
2005-02-16 00:37:50 +00:00
{
const float pos = it->first;
const float life = it->second;
m_fLifeRecord[fOtherFirstSecond+pos] = life;
2005-02-16 00:37:50 +00:00
}
for( unsigned i=0; i<other.m_ComboList.size(); ++i )
2005-02-16 00:37:50 +00:00
{
const Combo_t &combo = other.m_ComboList[i];
2005-02-16 00:37:50 +00:00
Combo_t newcombo(combo);
newcombo.m_fStartSecond += fOtherFirstSecond;
m_ComboList.push_back( newcombo );
2005-02-16 00:37:50 +00:00
}
/* Merge identical combos. This normally only happens in course mode, when a combo
* continues between songs. */
for( unsigned i=1; i<m_ComboList.size(); ++i )
2005-02-16 00:37:50 +00:00
{
Combo_t &prevcombo = m_ComboList[i-1];
Combo_t &combo = m_ComboList[i];
const float PrevComboEnd = prevcombo.m_fStartSecond + prevcombo.m_fSizeSeconds;
const float ThisComboStart = combo.m_fStartSecond;
2005-02-16 00:37:50 +00:00
if( fabsf(PrevComboEnd - ThisComboStart) > 0.001 )
continue;
/* These are really the same combo. */
prevcombo.m_fSizeSeconds += combo.m_fSizeSeconds;
prevcombo.m_cnt += combo.m_cnt;
m_ComboList.erase( m_ComboList.begin()+i );
2005-02-16 00:37:50 +00:00
--i;
}
}
2006-03-17 03:44:11 +00:00
Grade GetGradeFromPercent( float fPercent, bool bMerciful )
2005-03-31 06:13:40 +00:00
{
2006-03-17 03:44:11 +00:00
if( bMerciful )
fPercent = SCALE( fPercent, 0.0f, 1.0f, 0.5f, 1.0f );
Grade grade = Grade_Failed;
2005-03-31 06:13:40 +00:00
FOREACH_ENUM( Grade,g)
2005-03-31 06:13:40 +00:00
{
if( fPercent >= GRADE_PERCENT_TIER(g) )
{
grade = g;
break;
}
}
2005-03-31 17:26:25 +00:00
return grade;
2005-03-31 06:13:40 +00:00
}
2005-02-16 00:37:50 +00:00
Grade PlayerStageStats::GetGrade() const
{
if( m_bFailed )
return Grade_Failed;
2005-02-16 00:37:50 +00:00
/* XXX: This entire calculation should be in ScoreKeeper, but final evaluation
* is tricky since at that point the ScoreKeepers no longer exist. */
float fActual = 0;
bool bIsBeginner = false;
if( m_iStepsPlayed > 0 && !GAMESTATE->IsCourseMode() )
bIsBeginner = m_vpPossibleSteps[0]->GetDifficulty() == Difficulty_Beginner;
FOREACH_ENUM( TapNoteScore, tns )
2005-02-16 00:37:50 +00:00
{
int iTapScoreValue = ScoreKeeperNormal::TapNoteScoreToGradePoints( tns, bIsBeginner );
fActual += m_iTapNoteScores[tns] * iTapScoreValue;
LOG->Trace( "GetGrade actual: %i * %i", m_iTapNoteScores[tns], iTapScoreValue );
2005-02-16 00:37:50 +00:00
}
FOREACH_ENUM( HoldNoteScore, hns )
2005-02-16 00:37:50 +00:00
{
int iHoldScoreValue = ScoreKeeperNormal::HoldNoteScoreToGradePoints( hns, bIsBeginner );
fActual += m_iHoldNoteScores[hns] * iHoldScoreValue;
LOG->Trace( "GetGrade actual: %i * %i", m_iHoldNoteScores[hns], iHoldScoreValue );
2005-02-16 00:37:50 +00:00
}
LOG->Trace( "GetGrade: fActual: %f, fPossible: %d", fActual, m_iPossibleGradePoints );
2005-02-16 00:37:50 +00:00
float fPercent = (m_iPossibleGradePoints == 0) ? 0 : fActual / m_iPossibleGradePoints;
2005-02-16 00:37:50 +00:00
2006-03-17 03:44:11 +00:00
bool bMerciful =
m_iStepsPlayed > 0 &&
2006-03-17 03:44:11 +00:00
GAMESTATE->m_PlayMode == PLAY_MODE_REGULAR &&
PREFSMAN->m_bMercifulBeginner;
for( int i = 0; i < m_iStepsPlayed; ++i )
2006-03-17 03:44:11 +00:00
{
if( m_vpPossibleSteps[i]->GetDifficulty() != Difficulty_Beginner )
2006-03-17 03:44:11 +00:00
bMerciful = false;
}
Grade grade = GetGradeFromPercent( fPercent, bMerciful );
2005-02-16 00:37:50 +00:00
2005-10-09 07:01:48 +00:00
LOG->Trace( "GetGrade: Grade: %s, %i", GradeToString(grade).c_str(), GRADE_TIER02_IS_ALL_W2S );
if( GRADE_TIER02_IS_ALL_W2S )
2005-02-16 00:37:50 +00:00
{
2007-03-17 07:55:00 +00:00
if( FullComboOfScore(TNS_W1) )
return Grade_Tier01;
2005-02-16 00:37:50 +00:00
2007-03-17 07:55:00 +00:00
if( FullComboOfScore(TNS_W2) )
return Grade_Tier02;
2005-02-16 00:37:50 +00:00
2007-03-17 07:55:00 +00:00
grade = max( grade, Grade_Tier03 );
2005-02-16 00:37:50 +00:00
}
if( GRADE_TIER01_IS_ALL_W2S )
{
if( FullComboOfScore(TNS_W2) )
return Grade_Tier01;
grade = max( grade, Grade_Tier02 );
}
if( GRADE_TIER02_IS_FULL_COMBO )
{
if( FullComboOfScore(g_MinScoreToMaintainCombo) )
return Grade_Tier02;
grade = max( grade, Grade_Tier03 );
}
2005-02-16 00:37:50 +00:00
return grade;
}
float PlayerStageStats::MakePercentScore( int iActual, int iPossible )
2005-02-16 00:37:50 +00:00
{
if( iPossible == 0 )
2005-02-16 00:37:50 +00:00
return 0; // div/0
if( iActual == iPossible )
2005-02-16 00:37:50 +00:00
return 1; // correct for rounding error
/* This can happen in battle, with transform attacks. */
//ASSERT_M( iActual <= iPossible, ssprintf("%i/%i", iActual, iPossible) );
float fPercent = iActual / (float)iPossible;
// don't allow negative
fPercent = max( 0, fPercent );
int iPercentTotalDigits = 3 + CommonMetrics::PERCENT_SCORE_DECIMAL_PLACES; // "100" + "." + "00"
// TRICKY: printf will round, but we want to truncate. Otherwise, we may display a percent
// score that's too high and doesn't match up with the calculated grade.
float fTruncInterval = powf( 0.1f, (float)iPercentTotalDigits-1 );
// TRICKY: ftruncf is rounding 1.0000000 to 0.99990004. Give a little boost to
// fPercentDancePoints to correct for this.
fPercent += 0.000001f;
fPercent = ftruncf( fPercent, fTruncInterval );
return fPercent;
}
2005-02-16 00:37:50 +00:00
RString PlayerStageStats::FormatPercentScore( float fPercentDancePoints )
{
int iPercentTotalDigits = 3 + CommonMetrics::PERCENT_SCORE_DECIMAL_PLACES; // "100" + "." + "00"
2005-02-16 00:37:50 +00:00
RString s = ssprintf( "%*.*f%%", iPercentTotalDigits, (int)CommonMetrics::PERCENT_SCORE_DECIMAL_PLACES, fPercentDancePoints*100 );
return s;
}
float PlayerStageStats::GetPercentDancePoints() const
{
return MakePercentScore( m_iActualDancePoints, m_iPossibleDancePoints );
2005-02-16 00:37:50 +00:00
}
float PlayerStageStats::GetCurMaxPercentDancePoints() const
{
if ( m_iPossibleDancePoints == 0 )
return 0; // div/0
if ( m_iCurPossibleDancePoints == m_iPossibleDancePoints )
return 1; // correct for rounding error
float fCurMaxPercentDancePoints = m_iCurPossibleDancePoints / (float)m_iPossibleDancePoints;
return fCurMaxPercentDancePoints;
}
int PlayerStageStats::GetLessonScoreActual() const
{
int iScore = 0;
FOREACH_ENUM( TapNoteScore, tns )
{
switch( tns )
{
case TNS_AvoidMine:
case TNS_W5:
case TNS_W4:
case TNS_W3:
case TNS_W2:
case TNS_W1:
iScore += m_iTapNoteScores[tns];
break;
}
}
FOREACH_ENUM( HoldNoteScore, hns )
{
switch( hns )
{
case HNS_Held:
iScore += m_iHoldNoteScores[hns];
break;
}
}
return iScore;
}
int PlayerStageStats::GetLessonScoreNeeded() const
{
2005-09-22 03:10:47 +00:00
float fScore = 0;
FOREACH_CONST( Steps*, m_vpPossibleSteps, steps )
fScore += (*steps)->GetRadarValues( PLAYER_1 ).m_Values.v.fNumTapsAndHolds;
2005-09-22 03:10:47 +00:00
return lrintf( fScore * LESSON_PASS_THRESHOLD );
}
void PlayerStageStats::ResetScoreForLesson()
{
m_iCurPossibleDancePoints = 0;
m_iActualDancePoints = 0;
FOREACH_ENUM( TapNoteScore, tns )
m_iTapNoteScores[tns] = 0;
FOREACH_ENUM( HoldNoteScore, hns )
m_iHoldNoteScores[hns] = 0;
m_iCurCombo = 0;
m_iMaxCombo = 0;
m_iCurMissCombo = 0;
m_iScore = 0;
m_iCurMaxScore = 0;
m_iMaxScore = 0;
}
void PlayerStageStats::SetLifeRecordAt( float fLife, float fStepsSecond )
2005-02-16 00:37:50 +00:00
{
// Don't save life stats in endless courses, or could run OOM in a few hours.
if( GAMESTATE->m_pCurCourse && GAMESTATE->m_pCurCourse->IsEndless() )
return;
if( fStepsSecond < 0 )
2005-02-16 00:37:50 +00:00
return;
m_fFirstSecond = min( fStepsSecond, m_fFirstSecond );
m_fLastSecond = max( fStepsSecond, m_fLastSecond );
//LOG->Trace( "fLastSecond = %f", m_fLastSecond );
2005-02-16 00:37:50 +00:00
2005-04-03 23:32:30 +00:00
// fSecond will always be greater than any value already in the map.
m_fLifeRecord[fStepsSecond] = fLife;
2005-04-03 23:32:30 +00:00
2006-11-16 12:25:55 +00:00
MESSAGEMAN->Broadcast( Message_LifeMeterChangedP1 );
2005-04-03 23:32:30 +00:00
//
// Memory optimization:
// If we have three consecutive records A, B, and C all with the same fLife,
// we can eliminate record B without losing data. Only check the last three
// records in the map since we're only inserting at the end, and we know all
// earlier redundant records have already been removed.
//
map<float,float>::iterator C = m_fLifeRecord.end();
2005-04-03 23:32:30 +00:00
--C;
if( C == m_fLifeRecord.begin() ) // no earlier records left
2005-04-03 23:32:30 +00:00
return;
map<float,float>::iterator B = C;
--B;
if( B == m_fLifeRecord.begin() ) // no earlier records left
2005-04-03 23:32:30 +00:00
return;
map<float,float>::iterator A = B;
--A;
if( A->second == B->second && B->second == C->second )
m_fLifeRecord.erase(B);
2005-02-16 00:37:50 +00:00
}
float PlayerStageStats::GetLifeRecordAt( float fStepsSecond ) const
2005-02-16 00:37:50 +00:00
{
if( m_fLifeRecord.empty() )
2005-04-03 23:32:30 +00:00
return 0;
/* Find the first element whose key is greater than k. */
map<float,float>::const_iterator it = m_fLifeRecord.upper_bound( fStepsSecond );
2005-02-16 00:37:50 +00:00
2005-04-03 23:32:30 +00:00
/* Find the last element whose key is less than or equal to k. */
if( it != m_fLifeRecord.begin() )
2005-02-16 00:37:50 +00:00
--it;
return it->second;
}
float PlayerStageStats::GetLifeRecordLerpAt( float fStepsSecond ) const
2005-02-16 00:37:50 +00:00
{
if( m_fLifeRecord.empty() )
2005-04-03 23:32:30 +00:00
return 0;
/* Find the first element whose key is greater than k. */
map<float,float>::const_iterator later = m_fLifeRecord.upper_bound( fStepsSecond );
2005-02-16 00:37:50 +00:00
2005-04-03 23:32:30 +00:00
/* Find the last element whose key is less than or equal to k. */
2005-02-16 00:37:50 +00:00
map<float,float>::const_iterator earlier = later;
if( earlier != m_fLifeRecord.begin() )
2005-02-16 00:37:50 +00:00
--earlier;
if( later == m_fLifeRecord.end() )
2005-02-16 00:37:50 +00:00
return earlier->second;
if( earlier->first == later->first ) // two samples from the same time. Don't divide by zero in SCALE
return earlier->second;
2005-02-16 00:37:50 +00:00
/* earlier <= pos <= later */
return SCALE( fStepsSecond, earlier->first, later->first, earlier->second, later->second );
2005-02-16 00:37:50 +00:00
}
void PlayerStageStats::GetLifeRecord( float *fLifeOut, int iNumSamples, float fStepsEndSecond ) const
2005-02-16 00:37:50 +00:00
{
for( int i = 0; i < iNumSamples; ++i )
{
float from = SCALE( i, 0, (float)iNumSamples, 0.0f, fStepsEndSecond );
2005-02-16 00:37:50 +00:00
fLifeOut[i] = GetLifeRecordLerpAt( from );
}
}
2006-11-16 12:04:52 +00:00
float PlayerStageStats::GetCurrentLife() const
{
if( m_fLifeRecord.empty() )
return 0;
map<float,float>::const_iterator iter = m_fLifeRecord.end();
--iter;
return iter->second;
}
2005-12-19 01:37:24 +00:00
/* If bRollover is true, we're being called before gameplay begins, so we can record
2005-02-16 00:37:50 +00:00
* the amount of the first combo that comes from the previous song. */
2005-12-19 01:28:33 +00:00
void PlayerStageStats::UpdateComboList( float fSecond, bool bRollover )
2005-02-16 00:37:50 +00:00
{
// Don't save combo stats in endless courses, or could run OOM in a few hours.
if( GAMESTATE->m_pCurCourse && GAMESTATE->m_pCurCourse->IsEndless() )
return;
2005-02-16 00:37:50 +00:00
if( fSecond < 0 )
return;
2005-12-19 01:28:33 +00:00
if( !bRollover )
2005-02-16 00:37:50 +00:00
{
m_fFirstSecond = min( fSecond, m_fFirstSecond );
m_fLastSecond = max( fSecond, m_fLastSecond );
2005-02-16 00:37:50 +00:00
//LOG->Trace( "fLastSecond = %f", fLastSecond );
}
int cnt = m_iCurCombo;
2005-02-16 00:37:50 +00:00
if( !cnt )
return; /* no combo */
if( m_ComboList.size() == 0 || m_ComboList.back().m_cnt >= cnt )
2005-02-16 00:37:50 +00:00
{
/* If the previous combo (if any) starts on -9999, then we rolled over some
* combo, but missed the first step. Remove it. */
if( m_ComboList.size() && m_ComboList.back().m_fStartSecond == -9999 )
m_ComboList.erase( m_ComboList.begin()+m_ComboList.size()-1, m_ComboList.end() );
2005-02-16 00:37:50 +00:00
/* 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.) */
2005-12-19 01:28:33 +00:00
if( bRollover )
NewCombo.m_fStartSecond = -9999;
2005-02-16 00:37:50 +00:00
else
NewCombo.m_fStartSecond = fSecond;
m_ComboList.push_back( NewCombo );
2005-02-16 00:37:50 +00:00
}
Combo_t &combo = m_ComboList.back();
if( !bRollover && combo.m_fStartSecond == -9999 )
combo.m_fStartSecond = 0;
combo.m_fSizeSeconds = fSecond - combo.m_fStartSecond;
combo.m_cnt = cnt;
2005-02-16 00:37:50 +00:00
2005-12-19 01:28:33 +00:00
if( bRollover )
combo.m_rollover = cnt;
2005-02-16 00:37:50 +00:00
}
/* This returns the largest combo contained within the song, as if
* m_bComboContinuesBetweenSongs is turned off. */
PlayerStageStats::Combo_t PlayerStageStats::GetMaxCombo() const
{
if( m_ComboList.size() == 0 )
2005-02-16 00:37:50 +00:00
return Combo_t();
int m = 0;
for( unsigned i = 1; i < m_ComboList.size(); ++i )
2005-02-16 00:37:50 +00:00
{
if( m_ComboList[i].m_cnt > m_ComboList[m].m_cnt )
2005-02-16 00:37:50 +00:00
m = i;
}
return m_ComboList[m];
2005-02-16 00:37:50 +00:00
}
int PlayerStageStats::GetComboAtStartOfStage() const
{
if( m_ComboList.empty() )
2005-02-16 00:37:50 +00:00
return 0;
else
return m_ComboList[0].m_rollover;
2005-02-16 00:37:50 +00:00
}
bool PlayerStageStats::FullComboOfScore( TapNoteScore tnsAllGreaterOrEqual ) const
{
2007-03-23 01:45:07 +00:00
ASSERT( tnsAllGreaterOrEqual >= TNS_W5 );
ASSERT( tnsAllGreaterOrEqual <= TNS_W1 );
// If missed any holds, then it's not a full combo
if( m_iHoldNoteScores[HNS_LetGo] > 0 )
2005-02-16 00:37:50 +00:00
return false;
// If has any of the judgments below, then not a full combo
for( int i=TNS_Miss; i<tnsAllGreaterOrEqual; i++ )
2005-02-16 00:37:50 +00:00
{
if( m_iTapNoteScores[i] > 0 )
2005-02-16 00:37:50 +00:00
return false;
}
// If has at least one of the judgments equal to or above, then is a full combo.
for( int i=tnsAllGreaterOrEqual; i<NUM_TapNoteScore; i++ )
{
if( m_iTapNoteScores[i] > 0 )
return true;
}
return false;
2005-02-16 00:37:50 +00:00
}
bool PlayerStageStats::SingleDigitsOfScore( TapNoteScore tnsAllGreaterOrEqual ) const
{
return FullComboOfScore( tnsAllGreaterOrEqual ) &&
m_iTapNoteScores[tnsAllGreaterOrEqual] < 10;
2005-02-16 00:37:50 +00:00
}
bool PlayerStageStats::OneOfScore( TapNoteScore tnsAllGreaterOrEqual ) const
{
return FullComboOfScore( tnsAllGreaterOrEqual ) &&
m_iTapNoteScores[tnsAllGreaterOrEqual] == 1;
2005-02-16 00:37:50 +00:00
}
int PlayerStageStats::GetTotalTaps() const
{
int iTotalTaps = 0;
for( int i=TNS_Miss; i<NUM_TapNoteScore; i++ )
2005-02-16 00:37:50 +00:00
{
iTotalTaps += m_iTapNoteScores[i];
2005-02-16 00:37:50 +00:00
}
return iTotalTaps;
}
float PlayerStageStats::GetPercentageOfTaps( TapNoteScore tns ) const
{
int iTotalTaps = 0;
for( int i=TNS_Miss; i<NUM_TapNoteScore; i++ )
2005-02-16 00:37:50 +00:00
{
iTotalTaps += m_iTapNoteScores[i];
2005-02-16 00:37:50 +00:00
}
return m_iTapNoteScores[tns] / (float)iTotalTaps;
2005-02-16 00:37:50 +00:00
}
2005-10-01 00:18:13 +00:00
void PlayerStageStats::CalcAwards( PlayerNumber p, bool bGaveUp, bool bUsedAutoplay )
2005-09-11 00:48:29 +00:00
{
LOG->Trace( "hand out awards" );
2006-10-07 04:39:48 +00:00
m_pcaToShow = PeakComboAward_Invalid;
2005-10-01 00:18:13 +00:00
if( bGaveUp || bUsedAutoplay )
return;
2005-09-11 00:48:29 +00:00
deque<PerDifficultyAward> &vPdas = GAMESTATE->m_vLastPerDifficultyAwards[p];
LOG->Trace( "per difficulty awards" );
// per-difficulty awards
// don't give per-difficutly awards if using easy mods
if( !IsDisqualified() )
2005-09-11 00:48:29 +00:00
{
if( FullComboOfScore( TNS_W3 ) )
vPdas.push_back( AWARD_FULL_COMBO_W3 );
if( SingleDigitsOfScore( TNS_W3 ) )
vPdas.push_back( AWARD_SINGLE_DIGIT_W3 );
if( FullComboOfScore( TNS_W2 ) )
vPdas.push_back( AWARD_FULL_COMBO_W2 );
if( SingleDigitsOfScore( TNS_W2 ) )
vPdas.push_back( AWARD_SINGLE_DIGIT_W2 );
if( FullComboOfScore( TNS_W1 ) )
vPdas.push_back( AWARD_FULL_COMBO_W1 );
2005-09-11 00:48:29 +00:00
if( OneOfScore( TNS_W3 ) )
vPdas.push_back( AWARD_ONE_W3 );
if( OneOfScore( TNS_W2 ) )
vPdas.push_back( AWARD_ONE_W2 );
float fPercentW3s = GetPercentageOfTaps( TNS_W3 );
if( fPercentW3s >= 0.8f )
vPdas.push_back( AWARD_PERCENT_80_W3 );
if( fPercentW3s >= 0.9f )
vPdas.push_back( AWARD_PERCENT_90_W3 );
if( fPercentW3s >= 1.f )
vPdas.push_back( AWARD_PERCENT_100_W3 );
2005-09-11 00:48:29 +00:00
}
// Max one PDA per stage
if( !vPdas.empty() )
vPdas.erase( vPdas.begin(), vPdas.end()-1 );
if( !vPdas.empty() )
m_pdaToShow = vPdas.back();
else
2006-10-07 04:39:48 +00:00
m_pdaToShow = PerDifficultyAward_Invalid;
2005-09-11 00:48:29 +00:00
LOG->Trace( "done with per difficulty awards" );
// DO give peak combo awards if using easy mods
int iComboAtStartOfStage = GetComboAtStartOfStage();
int iPeakCombo = GetMaxCombo().m_cnt;
2005-09-11 00:48:29 +00:00
FOREACH_ENUM( PeakComboAward,pca )
2005-09-11 00:48:29 +00:00
{
int iLevel = 1000 * (pca+1);
bool bCrossedLevel = iComboAtStartOfStage < iLevel && iPeakCombo >= iLevel;
LOG->Trace( "pca = %d, iLevel = %d, bCrossedLevel = %d", pca, iLevel, bCrossedLevel );
if( bCrossedLevel )
GAMESTATE->m_vLastPeakComboAwards[p].push_back( pca );
}
if( !GAMESTATE->m_vLastPeakComboAwards[p].empty() )
m_pcaToShow = GAMESTATE->m_vLastPeakComboAwards[p].back();
else
2006-10-07 04:39:48 +00:00
m_pcaToShow = PeakComboAward_Invalid;
2005-09-11 00:48:29 +00:00
LOG->Trace( "done with per combo awards" );
}
bool PlayerStageStats::IsDisqualified() const
{
if( !PREFSMAN->m_bDisqualification )
return false;
return m_bDisqualified;
}
2005-02-16 00:37:50 +00:00
2006-03-17 03:44:11 +00:00
LuaFunction( GetGradeFromPercent, GetGradeFromPercent( FArg(1), false ) )
LuaFunction( FormatPercentScore, PlayerStageStats::FormatPercentScore( FArg(1) ) )
2005-03-31 06:13:40 +00:00
2007-09-14 05:56:12 +00:00
2005-02-16 02:11:31 +00:00
// lua start
#include "LuaBinding.h"
2005-06-20 05:02:03 +00:00
class LunaPlayerStageStats: public Luna<PlayerStageStats>
2005-02-16 02:11:31 +00:00
{
public:
DEFINE_METHOD( GetCaloriesBurned, m_fCaloriesBurned )
DEFINE_METHOD( GetLifeRemainingSeconds, m_fLifeRemainingSeconds )
2006-03-11 02:49:11 +00:00
DEFINE_METHOD( GetSurvivalSeconds, GetSurvivalSeconds() )
2006-11-14 11:23:13 +00:00
DEFINE_METHOD( GetCurrentCombo, m_iCurCombo )
DEFINE_METHOD( GetCurrentScoreMultiplier, m_iCurScoreMultiplier )
2006-11-15 23:35:47 +00:00
DEFINE_METHOD( GetScore, m_iScore )
2007-03-02 04:50:51 +00:00
DEFINE_METHOD( GetTapNoteScores, m_iTapNoteScores[Enum::Check<TapNoteScore>(L, 1)] )
DEFINE_METHOD( GetHoldNoteScores, m_iHoldNoteScores[Enum::Check<HoldNoteScore>(L, 1)] )
2006-03-11 02:49:11 +00:00
DEFINE_METHOD( FullCombo, FullCombo() )
DEFINE_METHOD( MaxCombo, GetMaxCombo().m_cnt )
2006-11-16 12:04:52 +00:00
DEFINE_METHOD( GetCurrentLife, GetCurrentLife() )
2006-03-11 02:49:11 +00:00
DEFINE_METHOD( GetGrade, GetGrade() )
2007-03-04 03:03:40 +00:00
DEFINE_METHOD( GetActualDancePoints, m_iActualDancePoints )
DEFINE_METHOD( GetPossibleDancePoints, m_iPossibleDancePoints )
DEFINE_METHOD( GetPercentDancePoints, GetPercentDancePoints() )
DEFINE_METHOD( GetLessonScoreActual, GetLessonScoreActual() )
DEFINE_METHOD( GetLessonScoreNeeded, GetLessonScoreNeeded() )
2008-04-05 19:19:13 +00:00
DEFINE_METHOD( GetPersonalHighScoreIndex, m_iProfileHighScoreIndex )
DEFINE_METHOD( GetMachineHighScoreIndex, m_iMachineHighScoreIndex )
DEFINE_METHOD( GetPerDifficultyAward, m_pdaToShow )
2006-03-11 02:49:11 +00:00
DEFINE_METHOD( GetPeakComboAward, m_pcaToShow )
DEFINE_METHOD( IsDisqualified, IsDisqualified() )
2005-02-16 02:11:31 +00:00
2007-03-16 01:23:22 +00:00
static int GetPlayedSteps( T* p, lua_State *L )
{
lua_newtable(L);
for( int i = 0; i < (int) min(p->m_iStepsPlayed, (int) p->m_vpPossibleSteps.size()); ++i )
2007-03-16 01:23:22 +00:00
{
p->m_vpPossibleSteps[i]->PushSelf(L);
2007-03-16 01:23:22 +00:00
lua_rawseti( L, -2, i+1 );
}
return 1;
}
static int GetPossibleSteps( T* p, lua_State *L )
{
lua_newtable(L);
for( int i = 0; i < (int) p->m_vpPossibleSteps.size(); ++i )
{
p->m_vpPossibleSteps[i]->PushSelf(L);
lua_rawseti( L, -2, i+1 );
}
return 1;
}
2006-09-27 19:53:05 +00:00
LunaPlayerStageStats()
2005-02-16 02:11:31 +00:00
{
ADD_METHOD( GetCaloriesBurned );
ADD_METHOD( GetLifeRemainingSeconds );
ADD_METHOD( GetSurvivalSeconds );
2006-11-14 11:23:13 +00:00
ADD_METHOD( GetCurrentCombo );
ADD_METHOD( GetCurrentScoreMultiplier );
2006-11-15 23:35:47 +00:00
ADD_METHOD( GetScore );
2007-03-02 04:50:51 +00:00
ADD_METHOD( GetTapNoteScores );
ADD_METHOD( GetHoldNoteScores );
ADD_METHOD( FullCombo );
ADD_METHOD( MaxCombo );
2006-11-16 12:04:52 +00:00
ADD_METHOD( GetCurrentLife );
ADD_METHOD( GetGrade );
2007-03-04 03:03:40 +00:00
ADD_METHOD( GetActualDancePoints );
ADD_METHOD( GetPossibleDancePoints );
ADD_METHOD( GetPercentDancePoints );
ADD_METHOD( GetLessonScoreActual );
ADD_METHOD( GetLessonScoreNeeded );
2005-09-11 01:50:12 +00:00
ADD_METHOD( GetPersonalHighScoreIndex );
ADD_METHOD( GetMachineHighScoreIndex );
ADD_METHOD( GetPerDifficultyAward );
ADD_METHOD( GetPeakComboAward );
ADD_METHOD( IsDisqualified );
2007-03-16 01:23:22 +00:00
ADD_METHOD( GetPlayedSteps );
ADD_METHOD( GetPossibleSteps );
2005-02-16 02:11:31 +00:00
}
};
LUA_REGISTER_CLASS( PlayerStageStats )
// lua end
2005-02-16 00:37:50 +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.
*/