2003-02-16 04:01:45 +00:00
|
|
|
#include "global.h"
|
2003-01-24 02:43:07 +00:00
|
|
|
#include "StageStats.h"
|
2003-02-03 05:53:59 +00:00
|
|
|
#include "GameState.h"
|
|
|
|
|
#include "RageLog.h"
|
2003-09-06 02:05:39 +00:00
|
|
|
#include "SongManager.h"
|
2003-10-12 21:48:05 +00:00
|
|
|
#include "RageUtil.h"
|
2004-01-02 01:39:14 +00:00
|
|
|
#include "PrefsManager.h"
|
2004-08-30 04:09:23 +00:00
|
|
|
#include "Foreach.h"
|
|
|
|
|
#include "Steps.h"
|
2004-08-30 05:08:08 +00:00
|
|
|
#include "song.h"
|
2003-01-24 02:43:07 +00:00
|
|
|
|
2003-12-23 00:26:00 +00:00
|
|
|
StageStats g_CurStageStats;
|
|
|
|
|
vector<StageStats> g_vPlayedStageStats;
|
|
|
|
|
|
2004-06-06 21:30:47 +00:00
|
|
|
void StageStats::Init()
|
2003-01-24 02:43:07 +00:00
|
|
|
{
|
2003-12-22 01:55:03 +00:00
|
|
|
playMode = PLAY_MODE_INVALID;
|
2004-06-28 07:26:00 +00:00
|
|
|
pStyle = NULL;
|
2004-08-30 04:09:23 +00:00
|
|
|
vpSongs.clear();
|
2003-12-22 01:55:03 +00:00
|
|
|
StageType = STAGE_INVALID;
|
|
|
|
|
fGameplaySeconds = 0;
|
|
|
|
|
|
2004-05-24 06:10:11 +00:00
|
|
|
FOREACH_PlayerNumber( p )
|
2003-10-30 04:41:03 +00:00
|
|
|
{
|
2004-08-30 04:09:23 +00:00
|
|
|
vpSteps[p].clear();
|
2003-12-22 01:55:03 +00:00
|
|
|
fAliveSeconds[p] = 0;
|
|
|
|
|
bFailed[p] = bFailedEarlier[p] = false;
|
|
|
|
|
iPossibleDancePoints[p] = iActualDancePoints[p] = 0;
|
|
|
|
|
iCurCombo[p] = iMaxCombo[p] = iCurMissCombo[p] = iScore[p] = iBonus[p] = 0;
|
|
|
|
|
fSecondsBeforeFail[p] = 0;
|
|
|
|
|
iSongsPassed[p] = iSongsPlayed[p] = 0;
|
|
|
|
|
iTotalError[p] = 0;
|
|
|
|
|
|
2004-08-21 06:46:49 +00:00
|
|
|
ZERO( iTapNoteScores[p] );
|
|
|
|
|
ZERO( iHoldNoteScores[p] );
|
2004-08-22 17:23:08 +00:00
|
|
|
radarPossible[p].Zero();
|
|
|
|
|
radarActual[p].Zero();
|
2003-12-22 01:55:03 +00:00
|
|
|
|
2004-07-24 18:11:04 +00:00
|
|
|
fFirstSecond[p] = 999999;
|
|
|
|
|
fLastSecond[p] = 0;
|
2003-10-30 04:41:03 +00:00
|
|
|
}
|
2003-01-24 02:43:07 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-30 04:09:23 +00:00
|
|
|
void StageStats::AssertValid( PlayerNumber pn ) const
|
|
|
|
|
{
|
|
|
|
|
if( vpSongs[0] )
|
|
|
|
|
CHECKPOINT_M( vpSongs[0]->GetFullTranslitTitle() );
|
|
|
|
|
ASSERT( vpSteps[pn][0] );
|
|
|
|
|
ASSERT_M( playMode < NUM_PLAY_MODES, ssprintf("playmode %i", playMode) );
|
|
|
|
|
ASSERT( pStyle != NULL );
|
|
|
|
|
ASSERT_M( vpSteps[pn][0]->GetDifficulty() < NUM_DIFFICULTIES, ssprintf("difficulty %i", vpSteps[pn][0]->GetDifficulty()) );
|
|
|
|
|
ASSERT( vpSongs.size() == vpSteps[pn].size() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int StageStats::GetAverageMeter( PlayerNumber pn ) const
|
|
|
|
|
{
|
|
|
|
|
int iTotalMeter = 0;
|
|
|
|
|
int iTotalCount = 0;
|
|
|
|
|
ASSERT( vpSongs.size() == vpSteps[pn].size() );
|
|
|
|
|
|
|
|
|
|
for( unsigned i=0; i<vpSongs.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
Song* pSong = vpSongs[i];
|
|
|
|
|
Steps* pSteps = vpSteps[pn][i];
|
|
|
|
|
|
|
|
|
|
// weight long and marathon songs
|
|
|
|
|
int iWeight = SongManager::GetNumStagesForSong( pSong );
|
|
|
|
|
int iMeter = pSteps->GetMeter();
|
|
|
|
|
|
|
|
|
|
iTotalMeter += iMeter;
|
|
|
|
|
iTotalCount += iWeight;
|
|
|
|
|
}
|
|
|
|
|
return iTotalMeter / iTotalCount; // round down
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-06 01:35:29 +00:00
|
|
|
void StageStats::AddStats( const StageStats& other )
|
2003-01-24 02:43:07 +00:00
|
|
|
{
|
2004-08-30 04:09:23 +00:00
|
|
|
ASSERT( !other.vpSongs.empty() );
|
|
|
|
|
FOREACH_CONST( Song*, other.vpSongs, s )
|
|
|
|
|
vpSongs.push_back( *s );
|
2003-09-06 01:35:29 +00:00
|
|
|
StageType = STAGE_INVALID; // meaningless
|
2004-08-30 04:09:23 +00:00
|
|
|
memset( fAliveSeconds, 0, sizeof(fAliveSeconds) ); // why not accumulate? -Chris
|
2003-01-24 02:43:07 +00:00
|
|
|
|
2003-12-09 10:20:18 +00:00
|
|
|
fGameplaySeconds += other.fGameplaySeconds;
|
|
|
|
|
|
2004-05-24 06:10:11 +00:00
|
|
|
FOREACH_PlayerNumber( p )
|
2003-01-24 02:43:07 +00:00
|
|
|
{
|
2004-08-30 04:09:23 +00:00
|
|
|
FOREACH_CONST( Steps*, other.vpSteps[p], s )
|
|
|
|
|
vpSteps[p].push_back( *s );
|
2003-01-24 02:43:07 +00:00
|
|
|
fAliveSeconds[p] += other.fAliveSeconds[p];
|
|
|
|
|
bFailed[p] |= other.bFailed[p];
|
2003-09-15 22:48:37 +00:00
|
|
|
bFailedEarlier[p] |= other.bFailedEarlier[p];
|
2003-01-24 02:43:07 +00:00
|
|
|
iPossibleDancePoints[p] += other.iPossibleDancePoints[p];
|
2003-02-03 05:53:59 +00:00
|
|
|
iActualDancePoints[p] += other.iActualDancePoints[p];
|
2003-01-24 02:43:07 +00:00
|
|
|
|
|
|
|
|
for( int t=0; t<NUM_TAP_NOTE_SCORES; t++ )
|
|
|
|
|
iTapNoteScores[p][t] += other.iTapNoteScores[p][t];
|
|
|
|
|
for( int h=0; h<NUM_HOLD_NOTE_SCORES; h++ )
|
2003-02-03 05:53:59 +00:00
|
|
|
iHoldNoteScores[p][h] += other.iHoldNoteScores[p][h];
|
2003-03-16 20:55:45 +00:00
|
|
|
iCurCombo[p] += other.iCurCombo[p];
|
2003-01-24 02:43:07 +00:00
|
|
|
iMaxCombo[p] += other.iMaxCombo[p];
|
2003-09-06 02:26:06 +00:00
|
|
|
iCurMissCombo[p] += other.iCurMissCombo[p];
|
2003-06-18 20:08:39 +00:00
|
|
|
iScore[p] += other.iScore[p];
|
2004-07-11 07:21:33 +00:00
|
|
|
radarPossible[p] += other.radarPossible[p];
|
|
|
|
|
radarActual[p] += other.radarActual[p];
|
2003-01-24 02:43:07 +00:00
|
|
|
fSecondsBeforeFail[p] += other.fSecondsBeforeFail[p];
|
|
|
|
|
iSongsPassed[p] += other.iSongsPassed[p];
|
2003-07-31 23:01:02 +00:00
|
|
|
iSongsPlayed[p] += other.iSongsPlayed[p];
|
|
|
|
|
iTotalError[p] += other.iTotalError[p];
|
2003-10-16 06:49:02 +00:00
|
|
|
|
2004-07-24 18:11:04 +00:00
|
|
|
const float fOtherFirstSecond = other.fFirstSecond[p] + fLastSecond[p];
|
|
|
|
|
const float fOtherLastSecond = other.fLastSecond[p] + fLastSecond[p];
|
|
|
|
|
fLastSecond[p] = fOtherLastSecond;
|
2003-12-22 01:55:03 +00:00
|
|
|
|
|
|
|
|
map<float,float>::const_iterator it;
|
|
|
|
|
for( it = other.fLifeRecord[p].begin(); it != other.fLifeRecord[p].end(); ++it )
|
|
|
|
|
{
|
|
|
|
|
const float pos = it->first;
|
|
|
|
|
const float life = it->second;
|
2004-07-24 18:11:04 +00:00
|
|
|
fLifeRecord[p][fOtherFirstSecond+pos] = life;
|
2003-12-22 01:55:03 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-21 07:53:39 +00:00
|
|
|
for( unsigned i=0; i<other.ComboList[p].size(); ++i )
|
2003-12-22 01:55:03 +00:00
|
|
|
{
|
|
|
|
|
const Combo_t &combo = other.ComboList[p][i];
|
|
|
|
|
|
|
|
|
|
Combo_t newcombo(combo);
|
2004-07-24 18:11:04 +00:00
|
|
|
newcombo.fStartSecond += fOtherFirstSecond;
|
2003-12-22 01:55:03 +00:00
|
|
|
ComboList[p].push_back( newcombo );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Merge identical combos. This normally only happens in course mode, when a combo
|
|
|
|
|
* continues between songs. */
|
2004-09-21 07:53:39 +00:00
|
|
|
for( unsigned i=1; i<ComboList[p].size(); ++i )
|
2003-12-22 01:55:03 +00:00
|
|
|
{
|
|
|
|
|
Combo_t &prevcombo = ComboList[p][i-1];
|
|
|
|
|
Combo_t &combo = ComboList[p][i];
|
2004-07-24 18:11:04 +00:00
|
|
|
const float PrevComboEnd = prevcombo.fStartSecond + prevcombo.fSizeSeconds;
|
|
|
|
|
const float ThisComboStart = combo.fStartSecond;
|
2003-12-22 01:55:03 +00:00
|
|
|
if( fabsf(PrevComboEnd - ThisComboStart) > 0.001 )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* These are really the same combo. */
|
2004-07-24 18:11:04 +00:00
|
|
|
prevcombo.fSizeSeconds += combo.fSizeSeconds;
|
2003-12-22 01:55:03 +00:00
|
|
|
prevcombo.cnt += combo.cnt;
|
|
|
|
|
ComboList[p].erase( ComboList[p].begin()+i );
|
|
|
|
|
--i;
|
|
|
|
|
}
|
2003-01-24 02:43:07 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-18 03:22:58 +00:00
|
|
|
Grade StageStats::GetGrade( PlayerNumber pn ) const
|
2003-02-03 05:53:59 +00:00
|
|
|
{
|
2003-12-03 04:29:25 +00:00
|
|
|
ASSERT( GAMESTATE->IsPlayerEnabled(pn) ); // shouldn't be calling this is player isn't joined!
|
2003-02-03 05:53:59 +00:00
|
|
|
|
2003-10-13 03:24:28 +00:00
|
|
|
if( bFailedEarlier[pn] )
|
2004-01-20 03:32:48 +00:00
|
|
|
return GRADE_FAILED;
|
2003-02-03 05:53:59 +00:00
|
|
|
|
2003-10-01 23:47:17 +00:00
|
|
|
/* XXX: This entire calculation should be in ScoreKeeper, but final evaluation
|
2003-10-02 02:03:29 +00:00
|
|
|
* is tricky since at that point the ScoreKeepers no longer exist.
|
|
|
|
|
*
|
|
|
|
|
* See http://www.aaroninjapan.com/ddr2.html ("Regular play scoring") */
|
2003-10-01 23:47:17 +00:00
|
|
|
float Possible = 0, Actual = 0;
|
2004-09-11 17:05:06 +00:00
|
|
|
FOREACH_TapNoteScore( tns )
|
2003-10-01 23:47:17 +00:00
|
|
|
{
|
2004-09-11 17:05:06 +00:00
|
|
|
int iTapScoreValue;
|
|
|
|
|
switch( tns )
|
|
|
|
|
{
|
|
|
|
|
case TNS_NONE: iTapScoreValue = 0; break;
|
2004-09-17 09:36:57 +00:00
|
|
|
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;
|
2004-09-11 17:05:06 +00:00
|
|
|
default: FAIL_M( ssprintf("%i", tns) ); break;
|
|
|
|
|
}
|
|
|
|
|
Actual += iTapNoteScores[pn][tns] * iTapScoreValue;
|
2004-09-17 09:36:57 +00:00
|
|
|
Possible += iTapNoteScores[pn][tns] * PREFSMAN->m_iGradeWeightMarvelous;
|
2003-10-01 23:47:17 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-11 17:05:06 +00:00
|
|
|
FOREACH_HoldNoteScore( hns )
|
2003-10-01 23:47:17 +00:00
|
|
|
{
|
2004-09-11 17:05:06 +00:00
|
|
|
int iHoldScoreValue;
|
|
|
|
|
switch( hns )
|
|
|
|
|
{
|
|
|
|
|
case HNS_NONE: iHoldScoreValue = 0; break;
|
2004-09-17 09:36:57 +00:00
|
|
|
case HNS_NG: iHoldScoreValue = PREFSMAN->m_iGradeWeightNG; break;
|
|
|
|
|
case HNS_OK: iHoldScoreValue = PREFSMAN->m_iGradeWeightOK; break;
|
2004-09-11 17:05:06 +00:00
|
|
|
default: FAIL_M( ssprintf("%i", hns) ); break;
|
|
|
|
|
}
|
|
|
|
|
Actual += iHoldNoteScores[pn][hns] * iHoldScoreValue;
|
|
|
|
|
Possible += iHoldNoteScores[pn][hns] * PREFSMAN->m_iPercentScoreWeightOK;
|
2003-10-01 23:47:17 +00:00
|
|
|
}
|
|
|
|
|
|
2004-01-20 07:38:48 +00:00
|
|
|
LOG->Trace( "GetGrade: Actual: %f, Possible: %f", Actual, Possible );
|
2004-01-20 07:09:44 +00:00
|
|
|
|
|
|
|
|
#define ROUNDING_ERROR 0.00001f
|
2004-05-22 01:27:10 +00:00
|
|
|
Grade grade = GRADE_FAILED;
|
2003-02-03 05:53:59 +00:00
|
|
|
|
2004-05-02 09:57:28 +00:00
|
|
|
float fPercent = (Possible == 0) ? 0 : Actual / Possible;
|
2004-01-20 07:38:48 +00:00
|
|
|
|
2004-05-02 09:57:28 +00:00
|
|
|
FOREACH_Grade(g)
|
2004-01-20 07:38:48 +00:00
|
|
|
{
|
2004-03-13 18:35:25 +00:00
|
|
|
if( fPercent >= PREFSMAN->m_fGradePercent[g]-ROUNDING_ERROR )
|
2004-01-20 07:38:48 +00:00
|
|
|
{
|
2004-05-02 09:57:28 +00:00
|
|
|
grade = g;
|
2004-01-20 07:38:48 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-01-20 07:09:44 +00:00
|
|
|
|
2004-02-01 05:21:51 +00:00
|
|
|
LOG->Trace( "GetGrade: Grade: %s, %i", GradeToString(grade).c_str(), PREFSMAN->m_bGradeTier02IsAllPerfects );
|
|
|
|
|
if( PREFSMAN->m_bGradeTier02IsAllPerfects )
|
|
|
|
|
{
|
|
|
|
|
if( iTapNoteScores[pn][TNS_MARVELOUS] > 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_PERFECT] == 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_GREAT] == 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_GOOD] == 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_BOO] == 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_MISS] == 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_HIT_MINE] == 0 &&
|
|
|
|
|
iHoldNoteScores[pn][HNS_NG] == 0 )
|
|
|
|
|
return GRADE_TIER_1;
|
|
|
|
|
|
|
|
|
|
if( iTapNoteScores[pn][TNS_PERFECT] > 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_GREAT] == 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_GOOD] == 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_BOO] == 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_MISS] == 0 &&
|
|
|
|
|
iTapNoteScores[pn][TNS_HIT_MINE] == 0 &&
|
|
|
|
|
iHoldNoteScores[pn][HNS_NG] == 0 )
|
|
|
|
|
return GRADE_TIER_2;
|
|
|
|
|
|
|
|
|
|
return max( grade, GRADE_TIER_3 );
|
|
|
|
|
}
|
2004-01-20 07:38:48 +00:00
|
|
|
|
2004-01-20 07:09:44 +00:00
|
|
|
return grade;
|
2003-02-03 05:53:59 +00:00
|
|
|
}
|
2003-09-06 00:33:09 +00:00
|
|
|
|
|
|
|
|
bool StageStats::OnePassed() const
|
|
|
|
|
{
|
2004-05-24 06:10:11 +00:00
|
|
|
FOREACH_PlayerNumber( p )
|
2003-09-06 01:18:55 +00:00
|
|
|
if( GAMESTATE->IsHumanPlayer(p) && !bFailed[p] )
|
2003-09-06 00:33:09 +00:00
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2003-11-03 23:55:58 +00:00
|
|
|
bool StageStats::AllFailed() const
|
|
|
|
|
{
|
2004-05-24 06:10:11 +00:00
|
|
|
FOREACH_PlayerNumber( pn )
|
2004-08-30 04:35:14 +00:00
|
|
|
if( GAMESTATE->IsPlayerEnabled(pn) )
|
2003-12-23 00:26:00 +00:00
|
|
|
if( !bFailed[pn] )
|
2003-11-03 23:55:58 +00:00
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-31 20:58:14 +00:00
|
|
|
bool StageStats::AllFailedEarlier() const
|
|
|
|
|
{
|
2004-05-24 06:10:11 +00:00
|
|
|
FOREACH_PlayerNumber( p )
|
2003-12-31 20:58:14 +00:00
|
|
|
if( GAMESTATE->IsPlayerEnabled(p) && !bFailedEarlier[p] )
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2003-10-12 21:48:05 +00:00
|
|
|
float StageStats::GetPercentDancePoints( PlayerNumber pn ) const
|
|
|
|
|
{
|
|
|
|
|
if( iPossibleDancePoints[pn] == 0 )
|
|
|
|
|
return 0; // div/0
|
|
|
|
|
|
|
|
|
|
if( iActualDancePoints[pn] == iPossibleDancePoints[pn] )
|
|
|
|
|
return 1; // correct for rounding error
|
|
|
|
|
|
2004-02-01 00:24:24 +00:00
|
|
|
/* This can happen in battle, with transform attacks. */
|
2004-06-16 00:38:31 +00:00
|
|
|
//ASSERT_M( iActualDancePoints[pn] <= iPossibleDancePoints[pn], ssprintf("%i/%i", iActualDancePoints[pn], iPossibleDancePoints[pn]) );
|
2004-01-12 06:45:36 +00:00
|
|
|
|
2003-10-12 21:48:05 +00:00
|
|
|
float fPercentDancePoints = iActualDancePoints[pn] / (float)iPossibleDancePoints[pn];
|
2004-01-12 06:45:36 +00:00
|
|
|
|
|
|
|
|
return fPercentDancePoints;
|
2003-10-12 21:48:05 +00:00
|
|
|
}
|
2003-10-16 06:49:02 +00:00
|
|
|
|
2004-07-24 18:11:04 +00:00
|
|
|
void StageStats::SetLifeRecordAt( PlayerNumber pn, float fLife, float fSecond )
|
2003-10-16 06:49:02 +00:00
|
|
|
{
|
2004-07-24 18:11:04 +00:00
|
|
|
if( fSecond < 0 )
|
2003-12-22 01:55:03 +00:00
|
|
|
return;
|
|
|
|
|
|
2004-07-24 18:11:04 +00:00
|
|
|
fFirstSecond[pn] = min( fSecond, fFirstSecond[pn] );
|
|
|
|
|
fLastSecond[pn] = max( fSecond, fLastSecond[pn] );
|
2004-07-24 18:37:25 +00:00
|
|
|
//LOG->Trace( "fLastSecond = %f", fLastSecond[pn] );
|
2003-12-22 01:55:03 +00:00
|
|
|
|
|
|
|
|
if( !fLifeRecord[pn].empty() )
|
|
|
|
|
{
|
2004-07-24 18:11:04 +00:00
|
|
|
const float old = GetLifeRecordAt( pn, fSecond );
|
|
|
|
|
if( fabsf(old-fSecond) < 0.001f )
|
2003-12-22 01:55:03 +00:00
|
|
|
return; /* no change */
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-24 18:11:04 +00:00
|
|
|
fLifeRecord[pn][fSecond] = fLife;
|
2003-12-22 01:55:03 +00:00
|
|
|
}
|
|
|
|
|
|
2004-07-24 18:11:04 +00:00
|
|
|
float StageStats::GetLifeRecordAt( PlayerNumber pn, float fSecond ) const
|
2003-12-22 01:55:03 +00:00
|
|
|
{
|
|
|
|
|
/* Find the first element whose key is not less than k. */
|
2004-07-24 18:11:04 +00:00
|
|
|
map<float,float>::const_iterator it = fLifeRecord[pn].lower_bound( fSecond );
|
2003-10-23 06:17:12 +00:00
|
|
|
|
2003-12-22 01:55:03 +00:00
|
|
|
/* Find the first element whose key is less than k. */
|
|
|
|
|
if( it != fLifeRecord[pn].begin() )
|
|
|
|
|
--it;
|
|
|
|
|
|
|
|
|
|
return it->second;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-24 18:11:04 +00:00
|
|
|
float StageStats::GetLifeRecordLerpAt( PlayerNumber pn, float fSecond ) const
|
2003-12-22 01:55:03 +00:00
|
|
|
{
|
|
|
|
|
/* Find the first element whose key is not less than k. */
|
2004-07-24 18:11:04 +00:00
|
|
|
map<float,float>::const_iterator later = fLifeRecord[pn].lower_bound( fSecond );
|
2003-12-22 01:55:03 +00:00
|
|
|
|
|
|
|
|
/* Find the first element whose key is less than k. */
|
|
|
|
|
map<float,float>::const_iterator earlier = later;
|
|
|
|
|
if( earlier != fLifeRecord[pn].begin() )
|
|
|
|
|
--earlier;
|
|
|
|
|
|
|
|
|
|
if( earlier->first == later->first )
|
|
|
|
|
return earlier->second;
|
|
|
|
|
|
|
|
|
|
/* earlier <= pos <= later */
|
2004-07-24 18:11:04 +00:00
|
|
|
const float f = SCALE( fSecond, earlier->first, later->first, 1, 0 );
|
2003-12-22 01:55:03 +00:00
|
|
|
return earlier->second * f + later->second * (1-f);
|
2003-10-16 06:49:02 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-22 01:55:03 +00:00
|
|
|
|
2004-07-24 18:11:04 +00:00
|
|
|
void StageStats::GetLifeRecord( PlayerNumber pn, float *fLifeOut, int iNumSamples ) const
|
2003-10-16 06:49:02 +00:00
|
|
|
{
|
2004-07-24 18:11:04 +00:00
|
|
|
for( int i = 0; i < iNumSamples; ++i )
|
2003-10-16 06:49:02 +00:00
|
|
|
{
|
2004-07-24 18:11:04 +00:00
|
|
|
float from = SCALE( i, 0, (float)iNumSamples, fFirstSecond[pn], fLastSecond[pn] );
|
|
|
|
|
fLifeOut[i] = GetLifeRecordLerpAt( pn, from );
|
2003-10-16 06:49:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
2003-10-23 06:17:12 +00:00
|
|
|
|
2003-12-23 04:44:34 +00:00
|
|
|
/* 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. */
|
2004-07-24 18:11:04 +00:00
|
|
|
void StageStats::UpdateComboList( PlayerNumber pn, float fSecond, bool rollover )
|
2003-10-23 06:17:12 +00:00
|
|
|
{
|
2004-07-24 18:11:04 +00:00
|
|
|
if( fSecond < 0 )
|
2003-12-22 01:55:03 +00:00
|
|
|
return;
|
2003-10-30 04:41:03 +00:00
|
|
|
|
2003-12-23 06:03:22 +00:00
|
|
|
if( !rollover )
|
|
|
|
|
{
|
2004-07-24 18:11:04 +00:00
|
|
|
fFirstSecond[pn] = min( fSecond, fFirstSecond[pn] );
|
|
|
|
|
fLastSecond[pn] = max( fSecond, fLastSecond[pn] );
|
2004-07-24 18:37:25 +00:00
|
|
|
//LOG->Trace( "fLastSecond = %f", fLastSecond[pn] );
|
2003-12-23 06:03:22 +00:00
|
|
|
}
|
|
|
|
|
|
2003-11-02 17:05:11 +00:00
|
|
|
int cnt = iCurCombo[pn];
|
2003-10-23 06:17:12 +00:00
|
|
|
if( !cnt )
|
|
|
|
|
return; /* no combo */
|
|
|
|
|
|
|
|
|
|
if( ComboList[pn].size() == 0 || ComboList[pn].back().cnt >= cnt )
|
|
|
|
|
{
|
2004-02-01 02:09:36 +00:00
|
|
|
/* If the previous combo (if any) starts on -9999, then we rolled over some
|
|
|
|
|
* combo, but missed the first step. Remove it. */
|
2004-07-24 18:11:04 +00:00
|
|
|
if( ComboList[pn].size() && ComboList[pn].back().fStartSecond == -9999 )
|
2004-02-01 02:09:36 +00:00
|
|
|
ComboList[pn].erase( ComboList[pn].begin()+ComboList[pn].size()-1, ComboList[pn].end() );
|
|
|
|
|
|
2003-10-23 06:17:12 +00:00
|
|
|
/* This is a new combo. */
|
|
|
|
|
Combo_t NewCombo;
|
2004-02-01 02:09:36 +00:00
|
|
|
/* "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 )
|
2004-07-24 18:11:04 +00:00
|
|
|
NewCombo.fStartSecond = -9999;
|
2004-02-01 02:09:36 +00:00
|
|
|
else
|
2004-07-24 18:11:04 +00:00
|
|
|
NewCombo.fStartSecond = fSecond;
|
2003-10-23 06:17:12 +00:00
|
|
|
ComboList[pn].push_back( NewCombo );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Combo_t &combo = ComboList[pn].back();
|
2004-07-24 18:11:04 +00:00
|
|
|
combo.fSizeSeconds = fSecond - combo.fStartSecond;
|
2003-10-23 06:17:12 +00:00
|
|
|
combo.cnt = cnt;
|
2004-07-24 18:11:04 +00:00
|
|
|
if( !rollover && combo.fStartSecond == -9999 )
|
|
|
|
|
combo.fStartSecond = fSecond;
|
2003-12-23 04:44:34 +00:00
|
|
|
|
|
|
|
|
if( rollover )
|
|
|
|
|
combo.rollover = cnt;
|
2003-10-23 06:17:12 +00:00
|
|
|
}
|
|
|
|
|
|
2003-11-02 17:05:11 +00:00
|
|
|
/* This returns the largest combo contained within the song, as if
|
|
|
|
|
* m_bComboContinuesBetweenSongs is turned off. */
|
|
|
|
|
StageStats::Combo_t StageStats::GetMaxCombo( PlayerNumber pn ) const
|
|
|
|
|
{
|
|
|
|
|
if( ComboList[pn].size() == 0 )
|
|
|
|
|
return Combo_t();
|
|
|
|
|
|
|
|
|
|
int m = 0;
|
|
|
|
|
for( unsigned i = 1; i < ComboList[pn].size(); ++i )
|
|
|
|
|
{
|
|
|
|
|
if( ComboList[pn][i].cnt > ComboList[pn][m].cnt )
|
|
|
|
|
m = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ComboList[pn][m];
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-07 04:34:49 +00:00
|
|
|
int StageStats::GetComboAtStartOfStage( PlayerNumber pn ) const
|
2003-10-30 04:41:03 +00:00
|
|
|
{
|
2004-03-07 04:34:49 +00:00
|
|
|
if( ComboList[pn].empty() )
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return ComboList[pn][0].rollover;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool StageStats::FullComboOfScore( PlayerNumber pn, TapNoteScore tnsAllGreaterOrEqual ) const
|
|
|
|
|
{
|
|
|
|
|
ASSERT( tnsAllGreaterOrEqual >= TNS_GREAT );
|
|
|
|
|
ASSERT( tnsAllGreaterOrEqual <= TNS_MARVELOUS );
|
|
|
|
|
|
2004-03-25 08:32:47 +00:00
|
|
|
if( iHoldNoteScores[pn][HNS_NG] > 0 )
|
|
|
|
|
return false;
|
|
|
|
|
|
2004-03-07 04:34:49 +00:00
|
|
|
for( int i=TNS_MISS; i<tnsAllGreaterOrEqual; i++ )
|
2003-10-30 04:41:03 +00:00
|
|
|
{
|
2004-03-07 04:34:49 +00:00
|
|
|
if( iTapNoteScores[pn][i] > 0 )
|
|
|
|
|
return false;
|
2003-10-23 06:17:12 +00:00
|
|
|
}
|
2004-03-07 04:34:49 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2003-10-30 04:41:03 +00:00
|
|
|
|
2004-03-07 04:34:49 +00:00
|
|
|
bool StageStats::SingleDigitsOfScore( PlayerNumber pn, TapNoteScore tnsAllGreaterOrEqual ) const
|
|
|
|
|
{
|
|
|
|
|
return FullComboOfScore( pn, tnsAllGreaterOrEqual ) &&
|
|
|
|
|
iTapNoteScores[pn][tnsAllGreaterOrEqual] < 10;
|
|
|
|
|
}
|
2003-10-30 04:41:03 +00:00
|
|
|
|
2004-03-25 08:32:47 +00:00
|
|
|
bool StageStats::OneOfScore( PlayerNumber pn, TapNoteScore tnsAllGreaterOrEqual ) const
|
|
|
|
|
{
|
|
|
|
|
return FullComboOfScore( pn, tnsAllGreaterOrEqual ) &&
|
|
|
|
|
iTapNoteScores[pn][tnsAllGreaterOrEqual] == 1;
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-07 04:34:49 +00:00
|
|
|
int StageStats::GetTotalTaps( PlayerNumber pn ) const
|
|
|
|
|
{
|
|
|
|
|
int iTotalTaps = 0;
|
|
|
|
|
for( int i=TNS_MISS; i<NUM_TAP_NOTE_SCORES; i++ )
|
|
|
|
|
{
|
|
|
|
|
iTotalTaps += iTapNoteScores[pn][i];
|
|
|
|
|
}
|
|
|
|
|
return iTotalTaps;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float StageStats::GetPercentageOfTaps( PlayerNumber pn, TapNoteScore tns ) const
|
|
|
|
|
{
|
|
|
|
|
int iTotalTaps = 0;
|
|
|
|
|
for( int i=TNS_MISS; i<NUM_TAP_NOTE_SCORES; i++ )
|
|
|
|
|
{
|
|
|
|
|
iTotalTaps += iTapNoteScores[pn][i];
|
|
|
|
|
}
|
|
|
|
|
return iTapNoteScores[pn][tns] / (float)iTotalTaps;
|
2003-10-23 06:17:12 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-14 23:26:32 +00:00
|
|
|
static Grade GetBestGrade()
|
|
|
|
|
{
|
|
|
|
|
Grade g = NUM_GRADES;
|
2004-08-30 04:35:14 +00:00
|
|
|
FOREACH_EnabledPlayer( pn )
|
|
|
|
|
g = min( g, g_CurStageStats.GetGrade( pn ) );
|
2004-02-14 23:26:32 +00:00
|
|
|
return g;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Grade GetWorstGrade()
|
|
|
|
|
{
|
|
|
|
|
Grade g = GRADE_TIER_1;
|
2004-08-30 04:35:14 +00:00
|
|
|
FOREACH_EnabledPlayer( pn )
|
|
|
|
|
g = max( g, g_CurStageStats.GetGrade( pn ) );
|
2004-02-14 23:26:32 +00:00
|
|
|
return g;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#include "LuaFunctions.h"
|
2004-05-11 06:12:40 +00:00
|
|
|
LuaFunction_NoArgs( GetStagesPlayed, (int) g_vPlayedStageStats.size() );
|
2004-02-14 23:26:32 +00:00
|
|
|
LuaFunction_NoArgs( GetBestGrade, GetBestGrade() );
|
|
|
|
|
LuaFunction_NoArgs( GetWorstGrade, GetWorstGrade() );
|
|
|
|
|
LuaFunction_NoArgs( OnePassed, g_CurStageStats.OnePassed() );
|
|
|
|
|
LuaFunction_PlayerNumber( FullCombo, g_CurStageStats.FullCombo(pn) )
|
|
|
|
|
LuaFunction_PlayerNumber( MaxCombo, g_CurStageStats.GetMaxCombo(pn).cnt )
|
|
|
|
|
LuaFunction_PlayerNumber( GetGrade, g_CurStageStats.GetGrade(pn) )
|
|
|
|
|
LuaFunction_Str( Grade, StringToGrade(str) );
|
|
|
|
|
|
2004-05-11 06:12:40 +00:00
|
|
|
const StageStats *GetStageStatsN( int n )
|
2004-02-14 23:26:32 +00:00
|
|
|
{
|
2004-05-11 06:12:40 +00:00
|
|
|
if( n == (int) g_vPlayedStageStats.size() )
|
|
|
|
|
return &g_CurStageStats;
|
|
|
|
|
if( n > (int) g_vPlayedStageStats.size() )
|
|
|
|
|
return NULL;
|
|
|
|
|
return &g_vPlayedStageStats[n];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* GetGrade(0) returns the first grade; GetGrade(1) returns the second grade, and
|
|
|
|
|
* and so on. GetGrade(GetStagesPlayed()) returns the current grade (from g_CurStageStats).
|
|
|
|
|
* If beyond the current song played, return GRADE_NO_DATA. */
|
|
|
|
|
Grade GetGrade( int n, PlayerNumber pn )
|
|
|
|
|
{
|
|
|
|
|
const StageStats *pStats = GetStageStatsN( n );
|
|
|
|
|
if( pStats == NULL )
|
2004-02-14 23:26:32 +00:00
|
|
|
return GRADE_NO_DATA;
|
2004-05-11 06:12:40 +00:00
|
|
|
return pStats->GetGrade( pn );
|
2004-02-14 23:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool OneGotGrade( int n, Grade g )
|
|
|
|
|
{
|
2004-05-11 06:12:40 +00:00
|
|
|
FOREACH_HumanPlayer( pn )
|
2004-08-30 04:35:14 +00:00
|
|
|
if( GetGrade( n, pn ) == g )
|
2004-05-11 06:12:40 +00:00
|
|
|
return true;
|
2004-02-14 23:26:32 +00:00
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LuaFunction_IntInt( OneGotGrade, OneGotGrade( a1, (Grade) a2 ) );
|
|
|
|
|
|
|
|
|
|
Grade GetFinalGrade( PlayerNumber pn )
|
|
|
|
|
{
|
|
|
|
|
if( !GAMESTATE->IsHumanPlayer(pn) )
|
|
|
|
|
return GRADE_NO_DATA;
|
|
|
|
|
StageStats stats;
|
2004-08-30 04:09:23 +00:00
|
|
|
GAMESTATE->GetFinalEvalStats( stats );
|
2004-02-14 23:26:32 +00:00
|
|
|
return stats.GetGrade( pn );
|
|
|
|
|
}
|
2004-08-30 04:09:23 +00:00
|
|
|
LuaFunction_PlayerNumber( GetFinalGrade, GetFinalGrade(pn) );
|
2004-02-14 23:26:32 +00:00
|
|
|
|
|
|
|
|
Grade GetBestFinalGrade()
|
|
|
|
|
{
|
|
|
|
|
Grade top_grade = GRADE_FAILED;
|
|
|
|
|
StageStats stats;
|
2004-08-30 04:09:23 +00:00
|
|
|
GAMESTATE->GetFinalEvalStats( stats );
|
2004-05-24 06:10:11 +00:00
|
|
|
FOREACH_PlayerNumber( p )
|
2004-02-14 23:26:32 +00:00
|
|
|
if( GAMESTATE->IsHumanPlayer(p) )
|
2004-08-30 04:09:23 +00:00
|
|
|
top_grade = min( top_grade, stats.GetGrade(p) );
|
2004-02-14 23:26:32 +00:00
|
|
|
return top_grade;
|
|
|
|
|
}
|
|
|
|
|
LuaFunction_NoArgs( GetBestFinalGrade, GetBestFinalGrade() );
|
2004-05-31 21:35:31 +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.
|
|
|
|
|
*/
|