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" );
sprite->Load( THEME->GetPathToG(path) );
sprite->SetCropLeft ( SCALE( combo.size, 0.0f, 1.0f, 0.5f, 0.0f ) );
sprite->SetCropRight( 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 );
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->SetCropLeft( combo.start );
sprite->SetCropRight( 1 - (combo.size + combo.start) );
sprite->SetCropLeft( start );
sprite->SetCropRight( 1 - (size + start) );
if( width < 0 )
width = sprite->GetUnzoomedWidth();
@@ -56,7 +58,10 @@ void ComboGraph::Load( CString Path, const StageStats &s, PlayerNumber pn )
BitmapText *text = new BitmapText;
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 );
text->SetX( CenterXPos );
-2
View File
@@ -15,8 +15,6 @@ public:
void Unload();
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 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
* 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. */
const float beat = GAMESTATE->m_fSongBeat;
GAMESTATE->m_CurStageStats.UpdateComboList( m_PlayerNumber, GAMESTATE->GetSongPercent(beat) );
GAMESTATE->m_CurStageStats.UpdateComboList( m_PlayerNumber, GAMESTATE->m_fSongBeat );
float life = -1;
if( m_pLifeMeter )
@@ -1155,7 +1154,7 @@ void PlayerMinus::HandleTapRowScore( unsigned row )
life = 1.0f - life;
}
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)
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_CurStyle = STYLE_DANCE_VERSUS;
GAMESTATE->m_bSideIsJoined[PLAYER_1] = true;
GAMESTATE->m_bSideIsJoined[PLAYER_2] = true;
GAMESTATE->m_MasterPlayerNumber = PLAYER_1;
GAMESTATE->m_pCurSong = SONGMAN->GetAllSongs()[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_2].m_fScrollSpeed = 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_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()" );
@@ -146,13 +158,11 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName)
break;
case stage:
case course:
LOG->Trace("We made it to stage");
stageStats = GAMESTATE->m_CurStageStats;
break;
default:
ASSERT(0);
}
stageStats.Finish();
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 ) )
{
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]->SetName( ssprintf("FullComboP%d",p+1) );
SET_XY_AND_ON_COMMAND( m_FullCombo[p] );
-3
View File
@@ -1670,9 +1670,6 @@ void ScreenGameplay::ShowSavePrompt( ScreenMessage SM_SendWhenDone )
void ScreenGameplay::SongFinished()
{
/* Need to Finish before calling GetActualRadarValue, for GetMaxCombo. */
GAMESTATE->m_CurStageStats.Finish();
// save any statistics
int p;
for( p=0; p<NUM_PLAYERS; p++ )
+113 -52
View File
@@ -19,12 +19,31 @@
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 i = 0; i < LIFE_RECORD_RESOLUTION; ++i )
fLifeRecord[p][i] = -1;
fFirstPos[p] = 1;
pSteps[p] = NULL;
iMeter[p] = 0;
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;
}
}
@@ -69,8 +88,46 @@ void StageStats::AddStats( const StageStats& other )
iSongsPlayed[p] += other.iSongsPlayed[p];
iTotalError[p] += other.iTotalError[p];
for( int i = 0; i < LIFE_RECORD_RESOLUTION; ++i )
fLifeRecord[p][i] = -1;
const float fOtherFirst = other.fFirstPos[p] + fLastPos[p];
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 )
{
pos = clamp( pos, 0, 1 );
if( pos < 0 )
return;
int offset = (int) roundf( pos * LIFE_RECORD_RESOLUTION );
for( int i = 0; i <= offset; ++i )
if( fLifeRecord[pn][i] < 0 )
fLifeRecord[pn][i] = life;
fFirstPos[pn] = min( pos, fFirstPos[pn] );
fLastPos[pn] = max( pos, fLastPos[pn] );
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
{
for( int i = 0; i < nout; ++i )
{
int from = i * (LIFE_RECORD_RESOLUTION-1) / nout;
life[i] = fLifeRecord[pn][from];
float from = SCALE( i, 0, (float)nout, fFirstPos[pn], fLastPos[pn] );
LOG->Trace("get %i from %f", i, from);
life[i] = GetLifeRecordLerpAt( (PlayerNumber)pn, from );
}
}
void StageStats::UpdateComboList( PlayerNumber pn, float pos )
{
pos = clamp( pos, 0, 1 );
if( pos < 0 )
return;
fFirstPos[pn] = min( pos, fFirstPos[pn] );
fLastPos[pn] = max( pos, fLastPos[pn] );
@@ -245,44 +344,6 @@ StageStats::Combo_t StageStats::GetMaxCombo( PlayerNumber pn ) const
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
{
if( ComboList[pn].size() != 1 )
+5 -5
View File
@@ -16,6 +16,7 @@
#include "GameConstantsAndTypes.h"
#include "Grade.h"
#include "Style.h"
#include <map>
class Song;
class Steps;
@@ -63,10 +64,11 @@ struct StageStats
int iSongsPlayed[NUM_PLAYERS];
int iTotalError[NUM_PLAYERS];
enum { LIFE_RECORD_RESOLUTION=1000 };
float fLifeRecord[NUM_PLAYERS][LIFE_RECORD_RESOLUTION];
map<float,float> fLifeRecord[NUM_PLAYERS];
void SetLifeRecord( PlayerNumber pn, float life, float pos );
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 */
struct Combo_t
@@ -84,14 +86,12 @@ struct StageStats
Combo_t(): start(0), size(0), cnt(0), rollover(0) { }
bool IsZero() const { return start < 0; }
};
vector<Combo_t> ComboList[NUM_PLAYERS];
float fFirstPos[NUM_PLAYERS], fLastPos[NUM_PLAYERS];
bool FullCombo( PlayerNumber pn ) const;
void UpdateComboList( PlayerNumber pn, float pos );
Combo_t GetMaxCombo( PlayerNumber pn ) const;
void Finish();
vector<Combo_t> ComboList[NUM_PLAYERS];
};