simplify and fix up combo/life stats
This commit is contained in:
@@ -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 );
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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] );
|
||||
|
||||
@@ -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
@@ -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 )
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user