simplify and fix up combo/life stats

This commit is contained in:
Glenn Maynard
2003-12-22 01:55:03 +00:00
parent 13d6ec2284
commit a1458b2095
7 changed files with 145 additions and 74 deletions
+10 -5
View File
@@ -29,12 +29,14 @@ void ComboGraph::Load( CString Path, const StageStats &s, PlayerNumber pn )
const CString path = ssprintf( "%s %s", Path.c_str(), IsMax? "max":"normal" ); const CString path = ssprintf( "%s %s", Path.c_str(), IsMax? "max":"normal" );
sprite->Load( THEME->GetPathToG(path) ); sprite->Load( THEME->GetPathToG(path) );
sprite->SetCropLeft ( SCALE( combo.size, 0.0f, 1.0f, 0.5f, 0.0f ) ); const float start = SCALE( combo.start, s.fFirstPos[pn], s.fLastPos[pn], 0.0f, 1.0f );
sprite->SetCropRight( SCALE( combo.size, 0.0f, 1.0f, 0.5f, 0.0f ) ); const float size = SCALE( combo.size, 0, s.fLastPos[pn]-s.fFirstPos[pn], 0.0f, 1.0f );
sprite->SetCropLeft ( SCALE( size, 0.0f, 1.0f, 0.5f, 0.0f ) );
sprite->SetCropRight( SCALE( size, 0.0f, 1.0f, 0.5f, 0.0f ) );
sprite->BeginTweening( .5f ); sprite->BeginTweening( .5f );
sprite->SetCropLeft( combo.start ); sprite->SetCropLeft( start );
sprite->SetCropRight( 1 - (combo.size + combo.start) ); sprite->SetCropRight( 1 - (size + start) );
if( width < 0 ) if( width < 0 )
width = sprite->GetUnzoomedWidth(); width = sprite->GetUnzoomedWidth();
@@ -56,7 +58,10 @@ void ComboGraph::Load( CString Path, const StageStats &s, PlayerNumber pn )
BitmapText *text = new BitmapText; BitmapText *text = new BitmapText;
text->LoadFromFont( THEME->GetPathToF(Path) ); text->LoadFromFont( THEME->GetPathToF(Path) );
const float CenterPercent = combo.start + combo.size/2; const float start = SCALE( combo.start, s.fFirstPos[pn], s.fLastPos[pn], 0.0f, 1.0f );
const float size = SCALE( combo.size, 0, s.fLastPos[pn]-s.fFirstPos[pn], 0.0f, 1.0f );
const float CenterPercent = start + size/2;
const float CenterXPos = SCALE( CenterPercent, 0.0f, 1.0f, -width/2.0f, width/2.0f ); const float CenterXPos = SCALE( CenterPercent, 0.0f, 1.0f, -width/2.0f, width/2.0f );
text->SetX( CenterXPos ); text->SetX( CenterXPos );
-2
View File
@@ -15,8 +15,6 @@ public:
void Unload(); void Unload();
void LoadFromStageStats( const StageStats &s, PlayerNumber pn ); void LoadFromStageStats( const StageStats &s, PlayerNumber pn );
void SetValues( const float *values, int cnt );
void SetValue( float value, int cnt ) { SetValues( &value, 1 ); }
void Update( float fDeltaTime ); void Update( float fDeltaTime );
void DrawPrimitives(); void DrawPrimitives();
+2 -3
View File
@@ -1142,8 +1142,7 @@ void PlayerMinus::HandleTapRowScore( unsigned row )
/* Use the real current beat, not the beat we've been passed. That's because we /* Use the real current beat, not the beat we've been passed. That's because we
* want to record the current life/combo to the current time; eg. if it's a MISS, * want to record the current life/combo to the current time; eg. if it's a MISS,
* the beat we're registering is in the past, but the life is changing now. */ * the beat we're registering is in the past, but the life is changing now. */
const float beat = GAMESTATE->m_fSongBeat; GAMESTATE->m_CurStageStats.UpdateComboList( m_PlayerNumber, GAMESTATE->m_fSongBeat );
GAMESTATE->m_CurStageStats.UpdateComboList( m_PlayerNumber, GAMESTATE->GetSongPercent(beat) );
float life = -1; float life = -1;
if( m_pLifeMeter ) if( m_pLifeMeter )
@@ -1155,7 +1154,7 @@ void PlayerMinus::HandleTapRowScore( unsigned row )
life = 1.0f - life; life = 1.0f - life;
} }
if( life != -1 ) if( life != -1 )
GAMESTATE->m_CurStageStats.SetLifeRecord( m_PlayerNumber, life, GAMESTATE->GetSongPercent(beat) ); GAMESTATE->m_CurStageStats.SetLifeRecord( m_PlayerNumber, life, GAMESTATE->m_fSongBeat );
if (m_pScoreDisplay) if (m_pScoreDisplay)
m_pScoreDisplay->SetScore(GAMESTATE->m_CurStageStats.iScore[m_PlayerNumber]); m_pScoreDisplay->SetScore(GAMESTATE->m_CurStageStats.iScore[m_PlayerNumber]);
+15 -4
View File
@@ -95,6 +95,8 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName)
{ {
GAMESTATE->m_PlayMode = PLAY_MODE_RAVE; GAMESTATE->m_PlayMode = PLAY_MODE_RAVE;
GAMESTATE->m_CurStyle = STYLE_DANCE_VERSUS; GAMESTATE->m_CurStyle = STYLE_DANCE_VERSUS;
GAMESTATE->m_bSideIsJoined[PLAYER_1] = true;
GAMESTATE->m_bSideIsJoined[PLAYER_2] = true;
GAMESTATE->m_MasterPlayerNumber = PLAYER_1; GAMESTATE->m_MasterPlayerNumber = PLAYER_1;
GAMESTATE->m_pCurSong = SONGMAN->GetAllSongs()[0]; GAMESTATE->m_pCurSong = SONGMAN->GetAllSongs()[0];
// GAMESTATE->m_pCurCourse = SONGMAN->m_pCourses[0]; // GAMESTATE->m_pCurCourse = SONGMAN->m_pCourses[0];
@@ -103,12 +105,22 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName)
GAMESTATE->m_PlayerOptions[PLAYER_1].m_fScrollSpeed = 2; GAMESTATE->m_PlayerOptions[PLAYER_1].m_fScrollSpeed = 2;
GAMESTATE->m_PlayerOptions[PLAYER_2].m_fScrollSpeed = 2; GAMESTATE->m_PlayerOptions[PLAYER_2].m_fScrollSpeed = 2;
GAMESTATE->m_iCurrentStageIndex = 2; GAMESTATE->m_iCurrentStageIndex = 2;
for( float f = 0; f < 1.0f; f += .005f )
for( float f = 0; f < 100.0f; f += 1.0f )
{ {
float fP1 = fmodf(f*4+.3f,1); float fP1 = fmodf(f/100*4+.3f,1);
GAMESTATE->m_CurStageStats.SetLifeRecord( PLAYER_1, fP1, f ); GAMESTATE->m_CurStageStats.SetLifeRecord( PLAYER_1, fP1, f );
GAMESTATE->m_CurStageStats.SetLifeRecord( PLAYER_2, 1-fP1, f ); GAMESTATE->m_CurStageStats.SetLifeRecord( PLAYER_2, 1-fP1, f );
} }
GAMESTATE->m_CurStageStats.iCurCombo[PLAYER_1] = 0;
GAMESTATE->m_CurStageStats.UpdateComboList( PLAYER_1, 0 );
GAMESTATE->m_CurStageStats.iCurCombo[PLAYER_1] = 1;
GAMESTATE->m_CurStageStats.UpdateComboList( PLAYER_1, 1 );
GAMESTATE->m_CurStageStats.iCurCombo[PLAYER_1] = 50;
GAMESTATE->m_CurStageStats.UpdateComboList( PLAYER_1, 25 );
GAMESTATE->m_CurStageStats.iCurCombo[PLAYER_1] = 250;
GAMESTATE->m_CurStageStats.UpdateComboList( PLAYER_1, 100 );
} }
LOG->Trace( "ScreenEvaluation::ScreenEvaluation()" ); LOG->Trace( "ScreenEvaluation::ScreenEvaluation()" );
@@ -146,13 +158,11 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName)
break; break;
case stage: case stage:
case course: case course:
LOG->Trace("We made it to stage");
stageStats = GAMESTATE->m_CurStageStats; stageStats = GAMESTATE->m_CurStageStats;
break; break;
default: default:
ASSERT(0); ASSERT(0);
} }
stageStats.Finish();
LOG->Trace( "total error: %i, %i", stageStats.iTotalError[0], stageStats.iTotalError[1] ); LOG->Trace( "total error: %i, %i", stageStats.iTotalError[0], stageStats.iTotalError[1] );
@@ -745,6 +755,7 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName)
if( SHOW_FULL_COMBO && stageStats.FullCombo( (PlayerNumber) p ) ) if( SHOW_FULL_COMBO && stageStats.FullCombo( (PlayerNumber) p ) )
{ {
LOG->Trace( "Full combo for P%i", p+1 );
m_FullCombo[p].Load( THEME->GetPathToG(ssprintf("ScreenEvaluation full combo P%i",p+1)) ); m_FullCombo[p].Load( THEME->GetPathToG(ssprintf("ScreenEvaluation full combo P%i",p+1)) );
m_FullCombo[p]->SetName( ssprintf("FullComboP%d",p+1) ); m_FullCombo[p]->SetName( ssprintf("FullComboP%d",p+1) );
SET_XY_AND_ON_COMMAND( m_FullCombo[p] ); SET_XY_AND_ON_COMMAND( m_FullCombo[p] );
-3
View File
@@ -1670,9 +1670,6 @@ void ScreenGameplay::ShowSavePrompt( ScreenMessage SM_SendWhenDone )
void ScreenGameplay::SongFinished() void ScreenGameplay::SongFinished()
{ {
/* Need to Finish before calling GetActualRadarValue, for GetMaxCombo. */
GAMESTATE->m_CurStageStats.Finish();
// save any statistics // save any statistics
int p; int p;
for( p=0; p<NUM_PLAYERS; p++ ) for( p=0; p<NUM_PLAYERS; p++ )
+113 -52
View File
@@ -19,12 +19,31 @@
StageStats::StageStats() StageStats::StageStats()
{ {
memset( this, 0, sizeof(StageStats) ); playMode = PLAY_MODE_INVALID;
style = STYLE_INVALID;
pSong = NULL;
StageType = STAGE_INVALID;
fGameplaySeconds = 0;
for( int p=0; p<NUM_PLAYERS; p++ ) for( int p=0; p<NUM_PLAYERS; p++ )
{ {
for( int i = 0; i < LIFE_RECORD_RESOLUTION; ++i ) pSteps[p] = NULL;
fLifeRecord[p][i] = -1; iMeter[p] = 0;
fFirstPos[p] = 1; 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;
fFirstPos[p] = fLastPos[p] = 0;
memset( iTapNoteScores[p], 0, sizeof(iTapNoteScores[p]) );
memset( iHoldNoteScores[p], 0, sizeof(iHoldNoteScores[p]) );
memset( fRadarPossible[p], 0, sizeof(fRadarPossible[p]) );
memset( fRadarActual[p], 0, sizeof(fRadarActual[p]) );
fFirstPos[p] = 999999;
fLastPos[p] = 0; fLastPos[p] = 0;
} }
} }
@@ -69,8 +88,46 @@ void StageStats::AddStats( const StageStats& other )
iSongsPlayed[p] += other.iSongsPlayed[p]; iSongsPlayed[p] += other.iSongsPlayed[p];
iTotalError[p] += other.iTotalError[p]; iTotalError[p] += other.iTotalError[p];
for( int i = 0; i < LIFE_RECORD_RESOLUTION; ++i ) const float fOtherFirst = other.fFirstPos[p] + fLastPos[p];
fLifeRecord[p][i] = -1; const float fOtherLast = other.fLastPos[p] + fLastPos[p];
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;
fLifeRecord[p][fOtherFirst+pos] = life;
}
unsigned i;
for( i=0; i<other.ComboList[p].size(); ++i )
{
const Combo_t &combo = other.ComboList[p][i];
Combo_t newcombo(combo);
newcombo.start += fOtherFirst;
ComboList[p].push_back( newcombo );
}
/* Merge identical combos. This normally only happens in course mode, when a combo
* continues between songs. */
for( i=1; i<ComboList[p].size(); ++i )
{
Combo_t &prevcombo = ComboList[p][i-1];
Combo_t &combo = ComboList[p][i];
const float PrevComboEnd = prevcombo.start + prevcombo.size;
const float ThisComboStart = combo.start;
if( fabsf(PrevComboEnd - ThisComboStart) > 0.001 )
continue;
/* These are really the same combo. */
prevcombo.size += combo.size;
prevcombo.cnt += combo.cnt;
ComboList[p].erase( ComboList[p].begin()+i );
--i;
}
fLastPos[p] = fOtherLast;
} }
} }
@@ -178,26 +235,68 @@ float StageStats::GetPercentDancePoints( PlayerNumber pn ) const
void StageStats::SetLifeRecord( PlayerNumber pn, float life, float pos ) void StageStats::SetLifeRecord( PlayerNumber pn, float life, float pos )
{ {
pos = clamp( pos, 0, 1 ); if( pos < 0 )
return;
int offset = (int) roundf( pos * LIFE_RECORD_RESOLUTION ); fFirstPos[pn] = min( pos, fFirstPos[pn] );
for( int i = 0; i <= offset; ++i ) fLastPos[pn] = max( pos, fLastPos[pn] );
if( fLifeRecord[pn][i] < 0 )
fLifeRecord[pn][i] = life; if( !fLifeRecord[pn].empty() )
{
const float old = GetLifeRecordAt( pn, pos );
if( fabsf(old-pos) < 0.001f )
return; /* no change */
} }
fLifeRecord[pn][pos] = life;
}
float StageStats::GetLifeRecordAt( PlayerNumber pn, float pos ) const
{
/* Find the first element whose key is not less than k. */
map<float,float>::const_iterator it = fLifeRecord[pn].lower_bound( pos );
/* Find the first element whose key is less than k. */
if( it != fLifeRecord[pn].begin() )
--it;
return it->second;
}
float StageStats::GetLifeRecordLerpAt( PlayerNumber pn, float pos ) const
{
/* Find the first element whose key is not less than k. */
map<float,float>::const_iterator later = fLifeRecord[pn].lower_bound( pos );
/* 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 */
const float f = SCALE( pos, earlier->first, later->first, 1, 0 );
return earlier->second * f + later->second * (1-f);
}
void StageStats::GetLifeRecord( PlayerNumber pn, float *life, int nout ) const void StageStats::GetLifeRecord( PlayerNumber pn, float *life, int nout ) const
{ {
for( int i = 0; i < nout; ++i ) for( int i = 0; i < nout; ++i )
{ {
int from = i * (LIFE_RECORD_RESOLUTION-1) / nout; float from = SCALE( i, 0, (float)nout, fFirstPos[pn], fLastPos[pn] );
life[i] = fLifeRecord[pn][from]; LOG->Trace("get %i from %f", i, from);
life[i] = GetLifeRecordLerpAt( (PlayerNumber)pn, from );
} }
} }
void StageStats::UpdateComboList( PlayerNumber pn, float pos ) void StageStats::UpdateComboList( PlayerNumber pn, float pos )
{ {
pos = clamp( pos, 0, 1 ); if( pos < 0 )
return;
fFirstPos[pn] = min( pos, fFirstPos[pn] ); fFirstPos[pn] = min( pos, fFirstPos[pn] );
fLastPos[pn] = max( pos, fLastPos[pn] ); fLastPos[pn] = max( pos, fLastPos[pn] );
@@ -245,44 +344,6 @@ StageStats::Combo_t StageStats::GetMaxCombo( PlayerNumber pn ) const
return ComboList[pn][m]; return ComboList[pn][m];
} }
/* SetLifeRecord and UpdateComboList take a percentage (0..1) in pos, but the values
* will actually be somewhat off as they're based on Song::m_fFirstBeat and m_fLastBeat,
* which are per-song, not per-Steps. Scale and shift our records so that they're
* really 0..1. */
void StageStats::Finish()
{
for( int pn = 0; pn < NUM_PLAYERS; ++pn )
{
if( ComboList[pn].size() == 0 )
continue;
const float First = fFirstPos[pn];
const float Last = fLastPos[pn];
if( fabsf(First-Last) < 0.0001f )
continue;
unsigned i;
for( i = 0; i < ComboList[pn].size(); ++i )
{
ComboList[pn][i].start = SCALE( ComboList[pn][i].start, First, Last, 0.0f, 1.0f );
ComboList[pn][i].size /= Last - First;
}
float NewLifeRecord[LIFE_RECORD_RESOLUTION];
for( i = 0; i < LIFE_RECORD_RESOLUTION; ++i )
{
int from = (int) roundf( SCALE( i, 0.0f, 1.0f, First, Last ) );
from = clamp( from, 0, LIFE_RECORD_RESOLUTION-1 );
NewLifeRecord[i] = fLifeRecord[pn][from];
}
memcpy( fLifeRecord[pn], NewLifeRecord, sizeof(fLifeRecord[pn]) );
fFirstPos[pn] = 0;
fLastPos[pn] = 1;
}
}
bool StageStats::FullCombo( PlayerNumber pn ) const bool StageStats::FullCombo( PlayerNumber pn ) const
{ {
if( ComboList[pn].size() != 1 ) if( ComboList[pn].size() != 1 )
+5 -5
View File
@@ -16,6 +16,7 @@
#include "GameConstantsAndTypes.h" #include "GameConstantsAndTypes.h"
#include "Grade.h" #include "Grade.h"
#include "Style.h" #include "Style.h"
#include <map>
class Song; class Song;
class Steps; class Steps;
@@ -63,10 +64,11 @@ struct StageStats
int iSongsPlayed[NUM_PLAYERS]; int iSongsPlayed[NUM_PLAYERS];
int iTotalError[NUM_PLAYERS]; int iTotalError[NUM_PLAYERS];
enum { LIFE_RECORD_RESOLUTION=1000 }; map<float,float> fLifeRecord[NUM_PLAYERS];
float fLifeRecord[NUM_PLAYERS][LIFE_RECORD_RESOLUTION];
void SetLifeRecord( PlayerNumber pn, float life, float pos ); void SetLifeRecord( PlayerNumber pn, float life, float pos );
void GetLifeRecord( PlayerNumber pn, float *life, int nout ) const; void GetLifeRecord( PlayerNumber pn, float *life, int nout ) const;
float GetLifeRecordAt( PlayerNumber pn, float pos ) const;
float GetLifeRecordLerpAt( PlayerNumber pn, float pos ) const;
/* pos,combo */ /* pos,combo */
struct Combo_t struct Combo_t
@@ -84,14 +86,12 @@ struct StageStats
Combo_t(): start(0), size(0), cnt(0), rollover(0) { } Combo_t(): start(0), size(0), cnt(0), rollover(0) { }
bool IsZero() const { return start < 0; } bool IsZero() const { return start < 0; }
}; };
vector<Combo_t> ComboList[NUM_PLAYERS];
float fFirstPos[NUM_PLAYERS], fLastPos[NUM_PLAYERS]; float fFirstPos[NUM_PLAYERS], fLastPos[NUM_PLAYERS];
bool FullCombo( PlayerNumber pn ) const; bool FullCombo( PlayerNumber pn ) const;
void UpdateComboList( PlayerNumber pn, float pos ); void UpdateComboList( PlayerNumber pn, float pos );
Combo_t GetMaxCombo( PlayerNumber pn ) const; Combo_t GetMaxCombo( PlayerNumber pn ) const;
void Finish();
vector<Combo_t> ComboList[NUM_PLAYERS];
}; };