re-wrote score/statistics tracking
This commit is contained in:
+146
-112
@@ -21,6 +21,7 @@
|
||||
#include "InputMapper.h"
|
||||
#include "SongManager.h"
|
||||
#include "GameState.h"
|
||||
#include "RageLog.h"
|
||||
|
||||
|
||||
// these two items are in the
|
||||
@@ -28,6 +29,8 @@ const float FRAME_JUDGE_AND_COMBO_Y = CENTER_Y;
|
||||
const float JUDGEMENT_Y_OFFSET = -26;
|
||||
const float COMBO_Y_OFFSET = +26;
|
||||
|
||||
const float FRAME_JUDGE_AND_COMBO_BEAT_TIME = 0.2f;
|
||||
|
||||
const float ARROWS_Y = SCREEN_TOP + ARROW_SIZE * 1.5f;
|
||||
const float HOLD_JUDGEMENT_Y = ARROWS_Y + 80;
|
||||
|
||||
@@ -36,8 +39,6 @@ const float HOLD_ARROW_NG_TIME = 0.18f;
|
||||
|
||||
Player::Player()
|
||||
{
|
||||
|
||||
m_fSongBeat = 0;
|
||||
m_PlayerNumber = PLAYER_INVALID;
|
||||
|
||||
m_pLifeMeter = NULL;
|
||||
@@ -58,58 +59,69 @@ Player::Player()
|
||||
}
|
||||
|
||||
|
||||
void Player::Load( PlayerNumber player_no, StyleDef* pStyleDef, NoteData* pNoteData, const PlayerOptions& po, LifeMeter* pLM, ScoreDisplay* pScore, int iOriginalNumNotes, int iNotesMeter )
|
||||
void Player::Load( PlayerNumber pn, NoteData* pNoteData, LifeMeter* pLM, ScoreDisplay* pScore )
|
||||
{
|
||||
//LOG->WriteLine( "Player::Load()", );
|
||||
|
||||
m_PlayerNumber = pn;
|
||||
m_pLifeMeter = pLM;
|
||||
m_pScore = pScore;
|
||||
|
||||
StyleDef* pStyleDef = GAMESTATE->GetCurrentStyleDef();
|
||||
|
||||
// copy note data
|
||||
this->CopyAll( pNoteData );
|
||||
|
||||
|
||||
m_iNumTapNotes = pNoteData->GetNumTapNotes();
|
||||
m_iTapNotesHit = 0;
|
||||
m_iMeter = GAMESTATE->m_pCurNotes[m_PlayerNumber]->m_iMeter;
|
||||
|
||||
// init scoring
|
||||
NoteDataWithScoring::Init();
|
||||
for( int i=0; i<MAX_TAP_NOTE_ROWS; i++ )
|
||||
m_fHoldNoteLife[i] = 1.0f;
|
||||
|
||||
m_PlayerNumber = player_no;
|
||||
m_PlayerOptions = po;
|
||||
|
||||
m_pLifeMeter = pLM;
|
||||
m_pScore = pScore;
|
||||
if( m_pScore )
|
||||
m_pScore->Init( player_no, m_PlayerOptions, iOriginalNumNotes, iNotesMeter );
|
||||
m_pScore->Init( pn );
|
||||
|
||||
if( !po.m_bHoldNotes )
|
||||
if( !GAMESTATE->m_PlayerOptions[pn].m_bHoldNotes )
|
||||
this->RemoveHoldNotes();
|
||||
|
||||
this->Turn( po.m_TurnType );
|
||||
this->Turn( GAMESTATE->m_PlayerOptions[pn].m_TurnType );
|
||||
|
||||
if( po.m_bLittle )
|
||||
if( GAMESTATE->m_PlayerOptions[pn].m_bLittle )
|
||||
this->MakeLittle();
|
||||
|
||||
int iPixelsToDrawBefore = 64;
|
||||
int iPixelsToDrawAfter = 320;
|
||||
switch( po.m_EffectType )
|
||||
switch( GAMESTATE->m_PlayerOptions[pn].m_EffectType )
|
||||
{
|
||||
case PlayerOptions::EFFECT_MINI: iPixelsToDrawBefore *= 2; iPixelsToDrawAfter *= 2; break;
|
||||
case PlayerOptions::EFFECT_SPACE: iPixelsToDrawBefore *= 2; iPixelsToDrawAfter *= 2; break;
|
||||
}
|
||||
|
||||
m_NoteField.Load( (NoteData*)this, player_no, pStyleDef, po, iPixelsToDrawBefore, iPixelsToDrawAfter, NoteField::MODE_DANCING );
|
||||
m_NoteField.Load( (NoteData*)this, pn, iPixelsToDrawBefore, iPixelsToDrawAfter );
|
||||
|
||||
m_GrayArrowRow.Load( player_no, pStyleDef, po );
|
||||
m_GhostArrowRow.Load( player_no, pStyleDef, po );
|
||||
m_GrayArrowRow.Load( pn );
|
||||
m_GhostArrowRow.Load( pn );
|
||||
|
||||
m_frameJudgement.SetY( FRAME_JUDGE_AND_COMBO_Y );
|
||||
m_frameCombo.SetY( FRAME_JUDGE_AND_COMBO_Y );
|
||||
m_Combo.SetY( po.m_bReverseScroll ? -COMBO_Y_OFFSET : COMBO_Y_OFFSET );
|
||||
m_Judgement.SetY( po.m_bReverseScroll ? -JUDGEMENT_Y_OFFSET : JUDGEMENT_Y_OFFSET );
|
||||
m_Combo.SetY( GAMESTATE->m_PlayerOptions[pn].m_bReverseScroll ? -COMBO_Y_OFFSET : COMBO_Y_OFFSET );
|
||||
m_Judgement.SetY( GAMESTATE->m_PlayerOptions[pn].m_bReverseScroll ? -JUDGEMENT_Y_OFFSET : JUDGEMENT_Y_OFFSET );
|
||||
|
||||
for( int c=0; c<pStyleDef->m_iColsPerPlayer; c++ )
|
||||
m_HoldJudgement[c].SetY( po.m_bReverseScroll ? SCREEN_HEIGHT - HOLD_JUDGEMENT_Y : HOLD_JUDGEMENT_Y );
|
||||
m_HoldJudgement[c].SetY( GAMESTATE->m_PlayerOptions[pn].m_bReverseScroll ? SCREEN_HEIGHT - HOLD_JUDGEMENT_Y : HOLD_JUDGEMENT_Y );
|
||||
for( c=0; c<pStyleDef->m_iColsPerPlayer; c++ )
|
||||
m_HoldJudgement[c].SetX( (float)pStyleDef->m_ColumnInfo[player_no][c].fXOffset );
|
||||
m_HoldJudgement[c].SetX( (float)pStyleDef->m_ColumnInfo[pn][c].fXOffset );
|
||||
|
||||
m_NoteField.SetY( po.m_bReverseScroll ? SCREEN_HEIGHT - ARROWS_Y : ARROWS_Y );
|
||||
m_GrayArrowRow.SetY( po.m_bReverseScroll ? SCREEN_HEIGHT - ARROWS_Y : ARROWS_Y );
|
||||
m_GhostArrowRow.SetY( po.m_bReverseScroll ? SCREEN_HEIGHT - ARROWS_Y : ARROWS_Y );
|
||||
m_NoteField.SetY( GAMESTATE->m_PlayerOptions[pn].m_bReverseScroll ? SCREEN_HEIGHT - ARROWS_Y : ARROWS_Y );
|
||||
m_GrayArrowRow.SetY( GAMESTATE->m_PlayerOptions[pn].m_bReverseScroll ? SCREEN_HEIGHT - ARROWS_Y : ARROWS_Y );
|
||||
m_GhostArrowRow.SetY( GAMESTATE->m_PlayerOptions[pn].m_bReverseScroll ? SCREEN_HEIGHT - ARROWS_Y : ARROWS_Y );
|
||||
|
||||
if( po.m_EffectType == PlayerOptions::EFFECT_MINI )
|
||||
if( GAMESTATE->m_PlayerOptions[pn].m_EffectType == PlayerOptions::EFFECT_MINI )
|
||||
{
|
||||
m_NoteField.SetZoom( 0.5f );
|
||||
m_GrayArrowRow.SetZoom( 0.5f );
|
||||
@@ -117,16 +129,16 @@ void Player::Load( PlayerNumber player_no, StyleDef* pStyleDef, NoteData* pNoteD
|
||||
}
|
||||
}
|
||||
|
||||
void Player::Update( float fDeltaTime, float fSongBeat, float fMaxBeatDifference )
|
||||
void Player::Update( float fDeltaTime )
|
||||
{
|
||||
//LOG->WriteLine( "Player::Update(%f, %f, %f)", fDeltaTime, fSongBeat, fMaxBeatDifference );
|
||||
//LOG->WriteLine( "Player::Update(%f)", fDeltaTime );
|
||||
|
||||
m_fSongBeat = fSongBeat; // save song beat
|
||||
const float fSongBeat = GAMESTATE->m_fSongBeat;
|
||||
|
||||
//
|
||||
// Check for TapNote misses
|
||||
//
|
||||
UpdateTapNotesMissedOlderThan( m_fSongBeat-fMaxBeatDifference );
|
||||
UpdateTapNotesMissedOlderThan( GAMESTATE->m_fSongBeat - GetMaxBeatDifference() );
|
||||
|
||||
|
||||
//
|
||||
@@ -149,7 +161,7 @@ void Player::Update( float fDeltaTime, float fSongBeat, float fMaxBeatDifference
|
||||
|
||||
|
||||
// update the life
|
||||
if( fStartBeat < m_fSongBeat && m_fSongBeat < fEndBeat ) // if the song beat is in the range of this hold
|
||||
if( fStartBeat < fSongBeat && fSongBeat < fEndBeat ) // if the song beat is in the range of this hold
|
||||
{
|
||||
const bool bIsHoldingButton = INPUTMAPPER->IsButtonDown( GameI ) || PREFSMAN->m_bAutoPlay;
|
||||
// if they got a bad score or haven't stepped on the corresponding tap yet
|
||||
@@ -162,13 +174,13 @@ void Player::Update( float fDeltaTime, float fSongBeat, float fMaxBeatDifference
|
||||
fLife += fDeltaTime/HOLD_ARROW_NG_TIME;
|
||||
fLife = min( fLife, 1 ); // clamp
|
||||
|
||||
m_NoteField.m_HoldNotes[i].m_iStartIndex = BeatToNoteRow( m_fSongBeat ); // move the start of this Hold
|
||||
m_NoteField.m_HoldNotes[i].m_iStartIndex = BeatToNoteRow( fSongBeat ); // move the start of this Hold
|
||||
|
||||
m_GhostArrowRow.HoldNote( hn.m_iTrack ); // update the "electric ghost" effect
|
||||
}
|
||||
else // !bIsHoldingButton
|
||||
{
|
||||
if( m_fSongBeat-fStartBeat > fMaxBeatDifference )
|
||||
if( fSongBeat-fStartBeat > GetMaxBeatDifference() )
|
||||
{
|
||||
// Decrease life
|
||||
fLife -= fDeltaTime/HOLD_ARROW_NG_TIME;
|
||||
@@ -182,18 +194,18 @@ void Player::Update( float fDeltaTime, float fSongBeat, float fMaxBeatDifference
|
||||
if( fLife == 0 ) // the player has not pressed the button for a long time!
|
||||
{
|
||||
hns = HNS_NG;
|
||||
GAMESTATE->GetLatestGameplayStatistics().iActualDancePoints[m_PlayerNumber] += HoldNoteScoreToDancePoints( hns );
|
||||
HandleNoteScore( hns );
|
||||
m_Combo.EndCombo();
|
||||
m_HoldJudgement[hn.m_iTrack].SetHoldJudgement( HNS_NG );
|
||||
}
|
||||
|
||||
// check for OK
|
||||
if( m_fSongBeat > fEndBeat ) // if this HoldNote is in the past
|
||||
if( fSongBeat > fEndBeat ) // if this HoldNote is in the past
|
||||
{
|
||||
// At this point fLife > 0, or else we would have marked it NG above
|
||||
fLife = 1;
|
||||
hns = HNS_OK;
|
||||
GAMESTATE->GetLatestGameplayStatistics().iActualDancePoints[m_PlayerNumber] += HoldNoteScoreToDancePoints( hns );
|
||||
HandleNoteScore( hns );
|
||||
m_HoldJudgement[hn.m_iTrack].SetHoldJudgement( HNS_OK );
|
||||
m_NoteField.SetHoldNoteLife( i, fLife ); // update the NoteField display
|
||||
}
|
||||
@@ -202,23 +214,13 @@ void Player::Update( float fDeltaTime, float fSongBeat, float fMaxBeatDifference
|
||||
|
||||
|
||||
ActorFrame::Update( fDeltaTime );
|
||||
|
||||
m_frameJudgement.Update( fDeltaTime );
|
||||
m_frameCombo.Update( fDeltaTime );
|
||||
|
||||
if( m_pLifeMeter )
|
||||
m_pLifeMeter->SetBeat( fSongBeat );
|
||||
|
||||
m_GrayArrowRow.Update( fDeltaTime, fSongBeat );
|
||||
m_NoteField.Update( fDeltaTime, fSongBeat );
|
||||
m_GhostArrowRow.Update( fDeltaTime, fSongBeat );
|
||||
}
|
||||
|
||||
void Player::DrawPrimitives()
|
||||
{
|
||||
D3DXMATRIX matOldView, matOldProj;
|
||||
|
||||
if( m_PlayerOptions.m_EffectType == PlayerOptions::EFFECT_SPACE )
|
||||
if( GAMESTATE->m_PlayerOptions[m_PlayerNumber].m_EffectType == PlayerOptions::EFFECT_SPACE )
|
||||
{
|
||||
// save old view and projection
|
||||
DISPLAY->GetDevice()->GetTransform( D3DTS_VIEW, &matOldView );
|
||||
@@ -227,7 +229,7 @@ void Player::DrawPrimitives()
|
||||
|
||||
// construct view and project matrix
|
||||
D3DXMATRIX matNewView;
|
||||
if( m_PlayerOptions.m_bReverseScroll )
|
||||
if( GAMESTATE->m_PlayerOptions[m_PlayerNumber].m_bReverseScroll )
|
||||
D3DXMatrixLookAtLH(
|
||||
&matNewView,
|
||||
&D3DXVECTOR3( CENTER_X, GetY()-300.0f, 400.0f ),
|
||||
@@ -255,7 +257,7 @@ void Player::DrawPrimitives()
|
||||
m_NoteField.Draw();
|
||||
m_GhostArrowRow.Draw();
|
||||
|
||||
if( m_PlayerOptions.m_EffectType == PlayerOptions::EFFECT_SPACE )
|
||||
if( GAMESTATE->m_PlayerOptions[m_PlayerNumber].m_EffectType == PlayerOptions::EFFECT_SPACE )
|
||||
{
|
||||
// restire old view and projection
|
||||
DISPLAY->GetDevice()->SetTransform( D3DTS_VIEW, &matOldView );
|
||||
@@ -268,16 +270,17 @@ void Player::DrawPrimitives()
|
||||
m_HoldJudgement[c].Draw();
|
||||
}
|
||||
|
||||
void Player::HandlePlayerStep( float fSongBeat, int col, float fMaxBeatDiff )
|
||||
void Player::Step( int col )
|
||||
{
|
||||
//LOG->WriteLine( "Player::HandlePlayerStep()" );
|
||||
|
||||
ASSERT( col >= 0 && col <= m_iNumTracks );
|
||||
|
||||
const float fSongBeat = GAMESTATE->m_fSongBeat;
|
||||
|
||||
// look for the closest matching step
|
||||
int iIndexStartLookingAt = BeatToNoteRow( fSongBeat );
|
||||
int iNumElementsToExamine = BeatToNoteRow( fMaxBeatDiff ); // number of elements to examine on either end of iIndexStartLookingAt
|
||||
int iIndexStartLookingAt = BeatToNoteRow( GAMESTATE->m_fSongBeat );
|
||||
int iNumElementsToExamine = BeatToNoteRow( GetMaxBeatDifference() ); // number of elements to examine on either end of iIndexStartLookingAt
|
||||
|
||||
//LOG->WriteLine( "iIndexStartLookingAt = %d, iNumElementsToExamine = %d", iIndexStartLookingAt, iNumElementsToExamine );
|
||||
|
||||
@@ -324,7 +327,7 @@ void Player::HandlePlayerStep( float fSongBeat, int col, float fMaxBeatDiff )
|
||||
// compute the score for this hit
|
||||
const float fStepBeat = NoteRowToBeat( (float)iIndexOverlappingNote );
|
||||
const float fBeatsUntilStep = fStepBeat - fSongBeat;
|
||||
const float fPercentFromPerfect = fabsf( fBeatsUntilStep / fMaxBeatDiff );
|
||||
const float fPercentFromPerfect = fabsf( fBeatsUntilStep / GetMaxBeatDifference() );
|
||||
|
||||
TapNoteScore &score = m_TapNoteScores[col][iIndexOverlappingNote];
|
||||
|
||||
@@ -347,14 +350,14 @@ void Player::HandlePlayerStep( float fSongBeat, int col, float fMaxBeatDiff )
|
||||
}
|
||||
}
|
||||
if( bRowDestroyed )
|
||||
OnRowDestroyed( fSongBeat, col, fMaxBeatDiff, iIndexOverlappingNote );
|
||||
OnRowDestroyed( col, iIndexOverlappingNote );
|
||||
}
|
||||
|
||||
if( !bDestroyedNote )
|
||||
m_GrayArrowRow.Step( col );
|
||||
}
|
||||
|
||||
void Player::OnRowDestroyed( float fSongBeat, int col, float fMaxBeatDiff, int iIndexThatWasSteppedOn )
|
||||
void Player::OnRowDestroyed( int col, int iIndexThatWasSteppedOn )
|
||||
{
|
||||
//LOG->WriteLine( "fBeatsUntilStep: %f, fPercentFromPerfect: %f",
|
||||
// fBeatsUntilStep, fPercentFromPerfect );
|
||||
@@ -365,12 +368,6 @@ void Player::OnRowDestroyed( float fSongBeat, int col, float fMaxBeatDiff, int i
|
||||
if( m_TapNoteScores[t][iIndexThatWasSteppedOn] >= TNS_BOO )
|
||||
score = min( score, m_TapNoteScores[t][iIndexThatWasSteppedOn] );
|
||||
|
||||
// update the judgement, score, and life
|
||||
m_Judgement.SetJudgement( score );
|
||||
if( m_pLifeMeter )
|
||||
m_pLifeMeter->ChangeLife( score );
|
||||
|
||||
|
||||
// remove this row from the NoteField
|
||||
if ( ( score == TNS_PERFECT ) || ( score == TNS_GREAT ) )
|
||||
m_NoteField.RemoveTapNoteRow( iIndexThatWasSteppedOn );
|
||||
@@ -381,12 +378,7 @@ void Player::OnRowDestroyed( float fSongBeat, int col, float fMaxBeatDiff, int i
|
||||
{
|
||||
m_GhostArrowRow.TapNote( c, score, m_Combo.GetCurrentCombo()>100 ); // show the ghost arrow for this column
|
||||
|
||||
if( m_pScore )
|
||||
m_pScore->AddToScore( score, m_Combo.GetCurrentCombo() ); // update score - called once per note in this row
|
||||
|
||||
// update dance points for Oni lifemeter
|
||||
if( GAMESTATE->m_aGameplayStatistics.GetSize() > 0 )
|
||||
GAMESTATE->GetLatestGameplayStatistics().iActualDancePoints[m_PlayerNumber] += TapNoteScoreToDancePoints( score );
|
||||
HandleNoteScore( score ); // update score - called once per note in this row
|
||||
|
||||
// update combo - called once per note in this row
|
||||
switch( score )
|
||||
@@ -394,6 +386,7 @@ void Player::OnRowDestroyed( float fSongBeat, int col, float fMaxBeatDiff, int i
|
||||
case TNS_PERFECT:
|
||||
case TNS_GREAT:
|
||||
m_Combo.ContinueCombo();
|
||||
GAMESTATE->m_iMaxCombo[m_PlayerNumber] = max( GAMESTATE->m_iMaxCombo[m_PlayerNumber], m_Combo.GetCurrentCombo() );
|
||||
break;
|
||||
case TNS_GOOD:
|
||||
case TNS_BOO:
|
||||
@@ -403,21 +396,26 @@ void Player::OnRowDestroyed( float fSongBeat, int col, float fMaxBeatDiff, int i
|
||||
}
|
||||
}
|
||||
|
||||
// update the judgement, score, and life
|
||||
m_Judgement.SetJudgement( score );
|
||||
if( m_pLifeMeter )
|
||||
m_pLifeMeter->ChangeLife( score );
|
||||
|
||||
// zoom the judgement and combo like a heart beat
|
||||
float fStartZoom;
|
||||
switch( score )
|
||||
{
|
||||
case TNS_PERFECT: fStartZoom = 1.5f; break;
|
||||
case TNS_GREAT: fStartZoom = 1.3f; break;
|
||||
case TNS_GOOD: fStartZoom = 1.2f; break;
|
||||
case TNS_PERFECT: fStartZoom = 1.3f; break;
|
||||
case TNS_GREAT: fStartZoom = 1.2f; break;
|
||||
case TNS_GOOD: fStartZoom = 1.1f; break;
|
||||
case TNS_BOO: fStartZoom = 1.0f; break;
|
||||
}
|
||||
m_frameJudgement.SetZoom( fStartZoom );
|
||||
m_frameJudgement.BeginTweening( 0.2f );
|
||||
m_frameJudgement.BeginTweening( 0.1f );
|
||||
m_frameJudgement.SetTweenZoom( 1 );
|
||||
|
||||
m_frameCombo.SetZoom( fStartZoom );
|
||||
m_frameCombo.BeginTweening( 0.2f );
|
||||
m_frameCombo.BeginTweening( 0.1f );
|
||||
m_frameCombo.SetTweenZoom( 1 );
|
||||
}
|
||||
|
||||
@@ -444,6 +442,7 @@ int Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanThisBeat )
|
||||
m_TapNoteScores[t][r] = TNS_MISS;
|
||||
iNumMissesFound++;
|
||||
bFoundAMissInThisRow = true;
|
||||
HandleNoteScore( TNS_MISS );
|
||||
}
|
||||
if( bFoundAMissInThisRow )
|
||||
if( m_pLifeMeter )
|
||||
@@ -461,7 +460,7 @@ int Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanThisBeat )
|
||||
}
|
||||
|
||||
|
||||
void Player::CrossedRow( int iNoteRow, float fSongBeat, float fMaxBeatDiff )
|
||||
void Player::CrossedRow( int iNoteRow )
|
||||
{
|
||||
if( PREFSMAN->m_bAutoPlay )
|
||||
{
|
||||
@@ -469,59 +468,94 @@ void Player::CrossedRow( int iNoteRow, float fSongBeat, float fMaxBeatDiff )
|
||||
for( int t=0; t<m_iNumTracks; t++ )
|
||||
{
|
||||
if( m_TapNotes[t][iNoteRow] != '0' )
|
||||
this->HandlePlayerStep( fSongBeat, t, fMaxBeatDiff );
|
||||
this->Step( t );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Player::SaveGameplayStatistics()
|
||||
void Player::HandleNoteScore( TapNoteScore score )
|
||||
{
|
||||
GameplayStatistics& GS = GAMESTATE->m_aGameplayStatistics[GAMESTATE->m_aGameplayStatistics.GetSize()-1];
|
||||
int p = m_PlayerNumber;
|
||||
// update dance points for Oni lifemeter
|
||||
GAMESTATE->m_iActualDancePoints[m_PlayerNumber] += TapNoteScoreToDancePoints( score );
|
||||
GAMESTATE->m_TapNoteScores[m_PlayerNumber][score]++;
|
||||
if( m_pLifeMeter )
|
||||
m_pLifeMeter->OnDancePointsChange();
|
||||
|
||||
GS.iActualDancePoints[p] = 0;
|
||||
|
||||
for( int t=0; t<MAX_NOTE_TRACKS; t++ )
|
||||
//A single step's points are calculated as follows:
|
||||
//
|
||||
//Let p = score multiplier (Perfect = 10, Great = 5, other = 0)
|
||||
//N = total number of steps and freeze steps
|
||||
//n = number of the current step or freeze step (varies from 1 to N)
|
||||
//B = Base value of the song (1,000,000 X the number of feet difficulty) - All edit data is rated as 5 feet
|
||||
//So, the score for one step is:
|
||||
//one_step_score = p * (B/S) * n
|
||||
//Where S = The sum of all integers from 1 to N (the total number of steps/freeze steps)
|
||||
//
|
||||
//*IMPORTANT* : Double steps (U+L, D+R, etc.) count as two steps instead of one, so if you get a double L+R on the 112th step of a song, you score is calculated with a Perfect/Great/whatever for both the 112th and 113th steps. Got it? Now, through simple algebraic manipulation
|
||||
//S = 1+...+N = (1+N)*N/2 (1 through N added together)
|
||||
//Okay, time for an example:
|
||||
//
|
||||
//So, for example, suppose we wanted to calculate the step score of a "Great" on the 57th step of a 441 step, 8-foot difficulty song (I'm just making this one up):
|
||||
//
|
||||
//S = (1 + 441)*441 / 2
|
||||
//= 194,222 / 2
|
||||
//= 97,461
|
||||
//StepScore = p * (B/S) * n
|
||||
//= 5 * (8,000,000 / 97,461) * 57
|
||||
//= 5 * (82) * 57 (The 82 is rounded down from 82.08411...)
|
||||
//= 23,370
|
||||
//Remember this is just the score for the step, not the cumulative score up to the 57th step. Also, please note that I am currently checking into rounding errors with the system and if there are any, how they are resolved in the system.
|
||||
//
|
||||
//Note: if you got all Perfect on this song, you would get (p=10)*B, which is 80,000,000. In fact, the maximum possible score for any song is the number of feet difficulty X 10,000,000.
|
||||
|
||||
int p; // score multiplier
|
||||
switch( score )
|
||||
{
|
||||
for( int r=0; r<MAX_TAP_NOTE_ROWS; r++ )
|
||||
{
|
||||
if( m_TapNotes[t][r] == '0' )
|
||||
continue; // no note here
|
||||
|
||||
switch( m_TapNoteScores[t][r] )
|
||||
{
|
||||
case TNS_PERFECT: GS.perfect[p]++; break;
|
||||
case TNS_GREAT: GS.great[p]++; break;
|
||||
case TNS_GOOD: GS.good[p]++; break;
|
||||
case TNS_BOO: GS.boo[p]++; break;
|
||||
case TNS_MISS: GS.miss[p]++; break;
|
||||
case TNS_NONE: break;
|
||||
default: ASSERT( false );
|
||||
}
|
||||
GS.iActualDancePoints[p] += TapNoteScoreToDancePoints( m_TapNoteScores[t][r] );
|
||||
}
|
||||
case TNS_PERFECT: p = 10; break;
|
||||
case TNS_GREAT: p = 5; break;
|
||||
default: p = 0; break;
|
||||
}
|
||||
for( int i=0; i<m_iNumHoldNotes; i++ )
|
||||
{
|
||||
switch( m_HoldNoteScores[i] )
|
||||
{
|
||||
case HNS_NG: GS.ng[p]++; break;
|
||||
case HNS_OK: GS.ok[p]++; break;
|
||||
case HNS_NONE: break;
|
||||
default: ASSERT( false );
|
||||
}
|
||||
GS.iActualDancePoints[p] += HoldNoteScoreToDancePoints( m_HoldNoteScores[i] );
|
||||
}
|
||||
GS.max_combo[p] = m_Combo.GetMaxCombo();
|
||||
GS.score[p] = m_pScore ? m_pScore->GetScore() : 0;
|
||||
|
||||
int N = m_iNumTapNotes;
|
||||
int n = m_iTapNotesHit+1;
|
||||
int B = m_iMeter * 1000000;
|
||||
float S = (1+N)*N/2.0f;
|
||||
|
||||
GS.failed[p] = m_pLifeMeter ? m_pLifeMeter->FailedEarlier() : false;
|
||||
printf( "m_iNumTapNotes %d, m_iTapNotesHit %d\n", m_iNumTapNotes, m_iTapNotesHit );
|
||||
|
||||
for( int r=0; r<NUM_RADAR_VALUES; r++ )
|
||||
{
|
||||
GS.fRadarActual[p][r] = this->GetActualRadarValue( (RadarCategory)r, GAMESTATE->m_pCurSong->m_fMusicLengthSeconds );
|
||||
CLAMP( GS.fRadarActual[p][r], 0, 1 );
|
||||
}
|
||||
float one_step_score = p * (B/S) * n;
|
||||
|
||||
float& fScore = GAMESTATE->m_fScore[m_PlayerNumber];
|
||||
ASSERT( fScore >= 0 );
|
||||
|
||||
fScore += one_step_score;
|
||||
|
||||
m_iTapNotesHit++;
|
||||
ASSERT( m_iTapNotesHit <= m_iNumTapNotes );
|
||||
|
||||
// HACK: Correct for rounding errors that cause a 100% perfect score to be slightly off
|
||||
if( m_iTapNotesHit == m_iNumTapNotes &&
|
||||
fabsf( fScore - froundf(fScore,1000000) ) < 50.0f ) // close to a multiple of 1,000,000
|
||||
fScore = froundf(fScore,1000000);
|
||||
|
||||
if( m_pScore )
|
||||
m_pScore->SetScore( fScore );
|
||||
}
|
||||
|
||||
void Player::HandleNoteScore( HoldNoteScore score )
|
||||
{
|
||||
// update dance points for Oni lifemeter
|
||||
GAMESTATE->m_iActualDancePoints[m_PlayerNumber] += HoldNoteScoreToDancePoints( score );
|
||||
GAMESTATE->m_HoldNoteScores[m_PlayerNumber][score]++;
|
||||
if( m_pLifeMeter )
|
||||
m_pLifeMeter->OnDancePointsChange();
|
||||
|
||||
// HoldNoteScores don't effect m_fScore
|
||||
}
|
||||
float Player::GetMaxBeatDifference()
|
||||
{
|
||||
return GAMESTATE->m_fCurBPS * PREFSMAN->m_fJudgeWindow * GAMESTATE->m_SongOptions.m_fMusicRate;
|
||||
}
|
||||
Reference in New Issue
Block a user