Line endings...be normalized!
This commit is contained in:
+468
-468
@@ -1,468 +1,468 @@
|
||||
#include "global.h"
|
||||
#include "StatsManager.h"
|
||||
#include "RageFileManager.h"
|
||||
#include "GameState.h"
|
||||
#include "Foreach.h"
|
||||
#include "ProfileManager.h"
|
||||
#include "Profile.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "Steps.h"
|
||||
#include "StyleUtil.h"
|
||||
#include "LuaManager.h"
|
||||
#include "Profile.h"
|
||||
#include "XmlFile.h"
|
||||
#include "CryptManager.h"
|
||||
#include "XmlFileUtil.h"
|
||||
|
||||
StatsManager* STATSMAN = NULL; // global object accessable from anywhere in the program
|
||||
|
||||
|
||||
StatsManager::StatsManager()
|
||||
{
|
||||
// Register with Lua.
|
||||
{
|
||||
Lua *L = LUA->Get();
|
||||
lua_pushstring( L, "STATSMAN" );
|
||||
this->PushSelf( L );
|
||||
lua_settable(L, LUA_GLOBALSINDEX);
|
||||
LUA->Release( L );
|
||||
}
|
||||
}
|
||||
|
||||
StatsManager::~StatsManager()
|
||||
{
|
||||
// Unregister with Lua.
|
||||
LUA->UnsetGlobal( "STATSMAN" );
|
||||
}
|
||||
|
||||
void StatsManager::Reset()
|
||||
{
|
||||
m_CurStageStats.Init();
|
||||
m_vPlayedStageStats.clear();
|
||||
m_AccumPlayedStageStats.Init();
|
||||
|
||||
CalcAccumPlayedStageStats();
|
||||
}
|
||||
|
||||
static StageStats AccumPlayedStageStats( const vector<StageStats>& vss )
|
||||
{
|
||||
StageStats ssreturn;
|
||||
|
||||
if( !vss.empty() )
|
||||
{
|
||||
ssreturn.m_pStyle = vss[0].m_pStyle;
|
||||
ssreturn.m_playMode = vss[0].m_playMode;
|
||||
}
|
||||
|
||||
FOREACH_CONST( StageStats, vss, ss )
|
||||
ssreturn.AddStats( *ss );
|
||||
|
||||
unsigned uNumSongs = ssreturn.m_vpPlayedSongs.size();
|
||||
|
||||
if( uNumSongs == 0 )
|
||||
return ssreturn; // don't divide by 0 below
|
||||
|
||||
/* Scale radar percentages back down to roughly 0..1. Don't scale RadarCategory_TapsAndHolds
|
||||
* and the rest, which are counters. */
|
||||
// FIXME: Weight each song by the number of stages it took to account for
|
||||
// long, marathon.
|
||||
FOREACH_EnabledPlayer( p )
|
||||
{
|
||||
for( int r = 0; r < RadarCategory_TapsAndHolds; r++)
|
||||
{
|
||||
ssreturn.m_player[p].m_radarPossible[r] /= uNumSongs;
|
||||
ssreturn.m_player[p].m_radarActual[r] /= uNumSongs;
|
||||
}
|
||||
}
|
||||
FOREACH_EnabledMultiPlayer( p )
|
||||
{
|
||||
for( int r = 0; r < RadarCategory_TapsAndHolds; r++)
|
||||
{
|
||||
ssreturn.m_multiPlayer[p].m_radarPossible[r] /= uNumSongs;
|
||||
ssreturn.m_multiPlayer[p].m_radarActual[r] /= uNumSongs;
|
||||
}
|
||||
}
|
||||
return ssreturn;
|
||||
}
|
||||
|
||||
void StatsManager::GetFinalEvalStageStats( StageStats& statsOut ) const
|
||||
{
|
||||
statsOut.Init();
|
||||
|
||||
vector<StageStats> vssToCount;
|
||||
|
||||
// Show stats only for the latest 3 normal songs + passed extra stages
|
||||
int PassedRegularSongsLeft = 3;
|
||||
for( int i = (int)m_vPlayedStageStats.size()-1; i >= 0; --i )
|
||||
{
|
||||
const StageStats &ss = m_vPlayedStageStats[i];
|
||||
|
||||
if( !ss.OnePassed() )
|
||||
continue;
|
||||
|
||||
if( ss.m_Stage != Stage_Extra1 && ss.m_Stage != Stage_Extra2 )
|
||||
{
|
||||
if( PassedRegularSongsLeft == 0 )
|
||||
break;
|
||||
|
||||
--PassedRegularSongsLeft;
|
||||
}
|
||||
|
||||
vssToCount.push_back( ss );
|
||||
}
|
||||
|
||||
statsOut = AccumPlayedStageStats( vssToCount );
|
||||
}
|
||||
|
||||
|
||||
void StatsManager::CalcAccumPlayedStageStats()
|
||||
{
|
||||
m_AccumPlayedStageStats = AccumPlayedStageStats( m_vPlayedStageStats );
|
||||
}
|
||||
|
||||
/* This data is added to each player profile, and to the machine profile per-player. */
|
||||
void AddPlayerStatsToProfile( Profile *pProfile, const StageStats &ss, PlayerNumber pn )
|
||||
{
|
||||
ss.AssertValid( pn );
|
||||
CHECKPOINT;
|
||||
|
||||
StyleID sID;
|
||||
sID.FromStyle( ss.m_pStyle );
|
||||
|
||||
ASSERT( (int) ss.m_vpPlayedSongs.size() == ss.m_player[pn].m_iStepsPlayed );
|
||||
for( int i=0; i<ss.m_player[pn].m_iStepsPlayed; i++ )
|
||||
{
|
||||
Steps *pSteps = ss.m_player[pn].m_vpPossibleSteps[i];
|
||||
|
||||
pProfile->m_iNumSongsPlayedByPlayMode[ss.m_playMode]++;
|
||||
pProfile->m_iNumSongsPlayedByStyle[sID] ++;
|
||||
pProfile->m_iNumSongsPlayedByDifficulty[pSteps->GetDifficulty()] ++;
|
||||
|
||||
int iMeter = clamp( pSteps->GetMeter(), 0, MAX_METER );
|
||||
pProfile->m_iNumSongsPlayedByMeter[iMeter] ++;
|
||||
}
|
||||
|
||||
pProfile->m_iTotalDancePoints += ss.m_player[pn].m_iActualDancePoints;
|
||||
|
||||
if( ss.m_Stage == Stage_Extra1 || ss.m_Stage == Stage_Extra2 )
|
||||
{
|
||||
if( ss.m_player[pn].m_bFailed )
|
||||
++pProfile->m_iNumExtraStagesFailed;
|
||||
else
|
||||
++pProfile->m_iNumExtraStagesPassed;
|
||||
}
|
||||
|
||||
// If you fail in a course, you passed all but the final song.
|
||||
// FIXME: Not true. If playing with 2 players, one player could have failed earlier.
|
||||
if( !ss.m_player[pn].m_bFailed )
|
||||
{
|
||||
pProfile->m_iNumStagesPassedByPlayMode[ss.m_playMode] ++;
|
||||
pProfile->m_iNumStagesPassedByGrade[ss.m_player[pn].GetGrade()] ++;
|
||||
}
|
||||
}
|
||||
|
||||
XNode* MakeRecentScoreNode( const StageStats &ss, Trail *pTrail, const PlayerStageStats &pss, MultiPlayer mp )
|
||||
{
|
||||
XNode* pNode = NULL;
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
{
|
||||
pNode = new XNode( "HighScoreForACourseAndTrail" );
|
||||
|
||||
CourseID courseID;
|
||||
courseID.FromCourse(GAMESTATE->m_pCurCourse );
|
||||
pNode->AppendChild( courseID.CreateNode() );
|
||||
|
||||
TrailID trailID;
|
||||
trailID.FromTrail( pTrail );
|
||||
pNode->AppendChild( trailID.CreateNode() );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
pNode = new XNode( "HighScoreForASongAndSteps" );
|
||||
|
||||
SongID songID;
|
||||
songID.FromSong( ss.m_vpPossibleSongs[0] );
|
||||
pNode->AppendChild( songID.CreateNode() );
|
||||
|
||||
StepsID stepsID;
|
||||
stepsID.FromSteps( pss.m_vpPossibleSteps[0] );
|
||||
pNode->AppendChild( stepsID.CreateNode() );
|
||||
}
|
||||
|
||||
XNode* pHighScore = pss.m_HighScore.CreateNode();
|
||||
pHighScore->AppendChild("Pad", mp);
|
||||
pHighScore->AppendChild("StageGuid", GAMESTATE->m_sStageGUID);
|
||||
pHighScore->AppendChild("Guid", CryptManager::GenerateRandomUUID());
|
||||
|
||||
pNode->AppendChild( pHighScore );
|
||||
|
||||
return pNode;
|
||||
}
|
||||
|
||||
void StatsManager::CommitStatsToProfiles( const StageStats *pSS )
|
||||
{
|
||||
// Add step totals. Use radarActual, since the player might have failed part way
|
||||
// through the song, in which case we don't want to give credit for the rest of the
|
||||
// song.
|
||||
FOREACH_HumanPlayer( pn )
|
||||
{
|
||||
int iNumTapsAndHolds = (int) pSS->m_player[pn].m_radarActual[RadarCategory_TapsAndHolds];
|
||||
int iNumJumps = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Jumps];
|
||||
int iNumHolds = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Holds];
|
||||
int iNumRolls = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Rolls];
|
||||
int iNumMines = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Mines];
|
||||
int iNumHands = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Hands];
|
||||
int iNumLifts = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Lifts];
|
||||
float fCaloriesBurned = pSS->m_player[pn].m_fCaloriesBurned;
|
||||
PROFILEMAN->AddStepTotals( pn, iNumTapsAndHolds, iNumJumps, iNumHolds, iNumRolls, iNumMines, iNumHands, iNumLifts, fCaloriesBurned );
|
||||
}
|
||||
|
||||
// Update profile stats
|
||||
Profile* pMachineProfile = PROFILEMAN->GetMachineProfile();
|
||||
|
||||
int iGameplaySeconds = (int)truncf(pSS->m_fGameplaySeconds);
|
||||
|
||||
pMachineProfile->m_iTotalGameplaySeconds += iGameplaySeconds;
|
||||
pMachineProfile->m_iNumTotalSongsPlayed += pSS->m_vpPlayedSongs.size();
|
||||
|
||||
CHECKPOINT;
|
||||
if( !GAMESTATE->m_bMultiplayer ) // FIXME
|
||||
{
|
||||
FOREACH_HumanPlayer( pn )
|
||||
{
|
||||
CHECKPOINT;
|
||||
|
||||
Profile* pPlayerProfile = PROFILEMAN->GetProfile( pn );
|
||||
if( pPlayerProfile )
|
||||
{
|
||||
pPlayerProfile->m_iTotalGameplaySeconds += iGameplaySeconds;
|
||||
pPlayerProfile->m_iNumTotalSongsPlayed += pSS->m_vpPlayedSongs.size();
|
||||
}
|
||||
|
||||
AddPlayerStatsToProfile( pMachineProfile, *pSS, pn );
|
||||
|
||||
if( pPlayerProfile )
|
||||
AddPlayerStatsToProfile( pPlayerProfile, *pSS, pn );
|
||||
|
||||
CHECKPOINT;
|
||||
}
|
||||
}
|
||||
|
||||
// Save recent scores
|
||||
{
|
||||
auto_ptr<XNode> xml( new XNode("Stats") );
|
||||
xml->AppendChild( "MachineGuid", PROFILEMAN->GetMachineProfile()->m_sGuid );
|
||||
|
||||
XNode *recent = NULL;
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
recent = xml->AppendChild( new XNode("RecentCourseScores") );
|
||||
else
|
||||
recent = xml->AppendChild( new XNode("RecentSongScores") );
|
||||
|
||||
if(!GAMESTATE->m_bMultiplayer)
|
||||
{
|
||||
FOREACH_HumanPlayer( p )
|
||||
{
|
||||
if( pSS->m_player[p].m_HighScore.IsEmpty() )
|
||||
continue;
|
||||
recent->AppendChild( MakeRecentScoreNode( *pSS, GAMESTATE->m_pCurTrail[p], pSS->m_player[p], MultiPlayer_Invalid ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FOREACH_EnabledMultiPlayer( mp )
|
||||
{
|
||||
if( pSS->m_multiPlayer[mp].m_HighScore.IsEmpty() )
|
||||
continue;
|
||||
recent->AppendChild( MakeRecentScoreNode( *pSS, GAMESTATE->m_pCurTrail[GAMESTATE->m_MasterPlayerNumber], pSS->m_multiPlayer[mp], mp ) );
|
||||
}
|
||||
}
|
||||
|
||||
RString sDate = DateTime::GetNowDate().GetString();
|
||||
sDate.Replace(":","-");
|
||||
|
||||
const RString UPLOAD_DIR = "/Save/Upload/";
|
||||
RString sFileNameNoExtension = Profile::MakeUniqueFileNameNoExtension(UPLOAD_DIR, sDate + " " );
|
||||
RString fn = UPLOAD_DIR + sFileNameNoExtension + ".xml";
|
||||
|
||||
bool bSaved = XmlFileUtil::SaveToFile( xml.get(), fn, "", false );
|
||||
|
||||
if( bSaved )
|
||||
{
|
||||
RString sStatsXmlSigFile = fn + SIGNATURE_APPEND;
|
||||
CryptManager::SignFileToFile(fn, sStatsXmlSigFile);
|
||||
}
|
||||
}
|
||||
|
||||
//FileCopy( "Data/TempTestGroups.xml", "Save/Upload/data.xml" );
|
||||
}
|
||||
|
||||
void StatsManager::UnjoinPlayer( PlayerNumber pn )
|
||||
{
|
||||
/* A player has been unjoined. Clear his data from m_vPlayedStageStats, and
|
||||
* purge any m_vPlayedStageStats that no longer have any player data because
|
||||
* all of the players that were playing at the time have been unjoined. */
|
||||
FOREACH( StageStats, m_vPlayedStageStats, ss )
|
||||
ss->m_player[pn] = PlayerStageStats();
|
||||
|
||||
for( int i = 0; i < (int) m_vPlayedStageStats.size(); ++i )
|
||||
{
|
||||
StageStats &ss = m_vPlayedStageStats[i];
|
||||
bool bIsActive = false;
|
||||
FOREACH_PlayerNumber( p )
|
||||
if( ss.m_player[p].m_bJoined )
|
||||
bIsActive = true;
|
||||
FOREACH_MultiPlayer( mp )
|
||||
if( ss.m_multiPlayer[mp].m_bJoined )
|
||||
bIsActive = true;
|
||||
if( bIsActive )
|
||||
continue;
|
||||
|
||||
m_vPlayedStageStats.erase( m_vPlayedStageStats.begin()+i );
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsManager::GetStepsInUse( set<Steps*> &apInUseOut ) const
|
||||
{
|
||||
for( int i = 0; i < (int) m_vPlayedStageStats.size(); ++i )
|
||||
{
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
const PlayerStageStats &pss = m_vPlayedStageStats[i].m_player[pn];
|
||||
apInUseOut.insert( pss.m_vpPossibleSteps.begin(), pss.m_vpPossibleSteps.end() );
|
||||
}
|
||||
|
||||
FOREACH_MultiPlayer( mp )
|
||||
{
|
||||
const PlayerStageStats &pss = m_vPlayedStageStats[i].m_multiPlayer[mp];
|
||||
apInUseOut.insert( pss.m_vpPossibleSteps.begin(), pss.m_vpPossibleSteps.end() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lua start
|
||||
#include "LuaBinding.h"
|
||||
|
||||
/** @brief Allow Lua to have access to the StatsManager. */
|
||||
class LunaStatsManager: public Luna<StatsManager>
|
||||
{
|
||||
public:
|
||||
static int GetCurStageStats( T* p, lua_State *L ) { p->m_CurStageStats.PushSelf(L); return 1; }
|
||||
static int GetPlayedStageStats( T* p, lua_State *L )
|
||||
{
|
||||
int iAgo = IArg(1);
|
||||
int iIndex = p->m_vPlayedStageStats.size() - iAgo;
|
||||
if( iIndex < 0 || iIndex >= (int) p->m_vPlayedStageStats.size() )
|
||||
return 0;
|
||||
|
||||
p->m_vPlayedStageStats[iIndex].PushSelf(L);
|
||||
return 1;
|
||||
}
|
||||
static int Reset( T* p, lua_State *L ) { p->Reset(); return 0; }
|
||||
static int GetAccumPlayedStageStats( T* p, lua_State *L ) { p->GetAccumPlayedStageStats().PushSelf(L); return 1; }
|
||||
static int GetFinalGrade( T* p, lua_State *L )
|
||||
{
|
||||
PlayerNumber pn = Enum::Check<PlayerNumber>(L, 1);
|
||||
|
||||
if( !GAMESTATE->IsHumanPlayer(pn) )
|
||||
lua_pushnumber( L, Grade_NoData );
|
||||
else
|
||||
{
|
||||
StageStats stats;
|
||||
p->GetFinalEvalStageStats( stats );
|
||||
lua_pushnumber( L, stats.m_player[pn].GetGrade() );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static int GetStagesPlayed( T* p, lua_State *L ) { lua_pushnumber( L, p->m_vPlayedStageStats.size() ); return 1; }
|
||||
|
||||
static int GetBestGrade( T* p, lua_State *L )
|
||||
{
|
||||
Grade g = NUM_Grade;
|
||||
FOREACH_EnabledPlayer( pn )
|
||||
g = min( g, STATSMAN->m_CurStageStats.m_player[pn].GetGrade() );
|
||||
lua_pushnumber( L, g );
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetWorstGrade( T* p, lua_State *L )
|
||||
{
|
||||
Grade g = Grade_Tier01;
|
||||
FOREACH_EnabledPlayer( pn )
|
||||
g = max( g, STATSMAN->m_CurStageStats.m_player[pn].GetGrade() );
|
||||
lua_pushnumber( L, g );
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetBestFinalGrade( T* t, lua_State *L )
|
||||
{
|
||||
Grade top_grade = Grade_Failed;
|
||||
StageStats stats;
|
||||
t->GetFinalEvalStageStats( stats );
|
||||
FOREACH_HumanPlayer( p )
|
||||
{
|
||||
// If this player failed any stage, then their final grade is an F.
|
||||
bool bPlayerFailedOneStage = false;
|
||||
FOREACH_CONST( StageStats, STATSMAN->m_vPlayedStageStats, ss )
|
||||
{
|
||||
if( ss->m_player[p].m_bFailed )
|
||||
{
|
||||
bPlayerFailedOneStage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( bPlayerFailedOneStage )
|
||||
continue;
|
||||
|
||||
top_grade = min( top_grade, stats.m_player[p].GetGrade() );
|
||||
}
|
||||
|
||||
Enum::Push( L, top_grade );
|
||||
return 1;
|
||||
}
|
||||
|
||||
LunaStatsManager()
|
||||
{
|
||||
ADD_METHOD( GetCurStageStats );
|
||||
ADD_METHOD( GetPlayedStageStats );
|
||||
ADD_METHOD( GetAccumPlayedStageStats );
|
||||
ADD_METHOD( Reset );
|
||||
ADD_METHOD( GetFinalGrade );
|
||||
ADD_METHOD( GetStagesPlayed );
|
||||
ADD_METHOD( GetBestGrade );
|
||||
ADD_METHOD( GetWorstGrade );
|
||||
ADD_METHOD( GetBestFinalGrade );
|
||||
}
|
||||
};
|
||||
|
||||
LUA_REGISTER_CLASS( StatsManager )
|
||||
// lua end
|
||||
|
||||
|
||||
/*
|
||||
* (c) 2001-2004 Chris Danford
|
||||
* 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.
|
||||
*/
|
||||
#include "global.h"
|
||||
#include "StatsManager.h"
|
||||
#include "RageFileManager.h"
|
||||
#include "GameState.h"
|
||||
#include "Foreach.h"
|
||||
#include "ProfileManager.h"
|
||||
#include "Profile.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "Steps.h"
|
||||
#include "StyleUtil.h"
|
||||
#include "LuaManager.h"
|
||||
#include "Profile.h"
|
||||
#include "XmlFile.h"
|
||||
#include "CryptManager.h"
|
||||
#include "XmlFileUtil.h"
|
||||
|
||||
StatsManager* STATSMAN = NULL; // global object accessable from anywhere in the program
|
||||
|
||||
|
||||
StatsManager::StatsManager()
|
||||
{
|
||||
// Register with Lua.
|
||||
{
|
||||
Lua *L = LUA->Get();
|
||||
lua_pushstring( L, "STATSMAN" );
|
||||
this->PushSelf( L );
|
||||
lua_settable(L, LUA_GLOBALSINDEX);
|
||||
LUA->Release( L );
|
||||
}
|
||||
}
|
||||
|
||||
StatsManager::~StatsManager()
|
||||
{
|
||||
// Unregister with Lua.
|
||||
LUA->UnsetGlobal( "STATSMAN" );
|
||||
}
|
||||
|
||||
void StatsManager::Reset()
|
||||
{
|
||||
m_CurStageStats.Init();
|
||||
m_vPlayedStageStats.clear();
|
||||
m_AccumPlayedStageStats.Init();
|
||||
|
||||
CalcAccumPlayedStageStats();
|
||||
}
|
||||
|
||||
static StageStats AccumPlayedStageStats( const vector<StageStats>& vss )
|
||||
{
|
||||
StageStats ssreturn;
|
||||
|
||||
if( !vss.empty() )
|
||||
{
|
||||
ssreturn.m_pStyle = vss[0].m_pStyle;
|
||||
ssreturn.m_playMode = vss[0].m_playMode;
|
||||
}
|
||||
|
||||
FOREACH_CONST( StageStats, vss, ss )
|
||||
ssreturn.AddStats( *ss );
|
||||
|
||||
unsigned uNumSongs = ssreturn.m_vpPlayedSongs.size();
|
||||
|
||||
if( uNumSongs == 0 )
|
||||
return ssreturn; // don't divide by 0 below
|
||||
|
||||
/* Scale radar percentages back down to roughly 0..1. Don't scale RadarCategory_TapsAndHolds
|
||||
* and the rest, which are counters. */
|
||||
// FIXME: Weight each song by the number of stages it took to account for
|
||||
// long, marathon.
|
||||
FOREACH_EnabledPlayer( p )
|
||||
{
|
||||
for( int r = 0; r < RadarCategory_TapsAndHolds; r++)
|
||||
{
|
||||
ssreturn.m_player[p].m_radarPossible[r] /= uNumSongs;
|
||||
ssreturn.m_player[p].m_radarActual[r] /= uNumSongs;
|
||||
}
|
||||
}
|
||||
FOREACH_EnabledMultiPlayer( p )
|
||||
{
|
||||
for( int r = 0; r < RadarCategory_TapsAndHolds; r++)
|
||||
{
|
||||
ssreturn.m_multiPlayer[p].m_radarPossible[r] /= uNumSongs;
|
||||
ssreturn.m_multiPlayer[p].m_radarActual[r] /= uNumSongs;
|
||||
}
|
||||
}
|
||||
return ssreturn;
|
||||
}
|
||||
|
||||
void StatsManager::GetFinalEvalStageStats( StageStats& statsOut ) const
|
||||
{
|
||||
statsOut.Init();
|
||||
|
||||
vector<StageStats> vssToCount;
|
||||
|
||||
// Show stats only for the latest 3 normal songs + passed extra stages
|
||||
int PassedRegularSongsLeft = 3;
|
||||
for( int i = (int)m_vPlayedStageStats.size()-1; i >= 0; --i )
|
||||
{
|
||||
const StageStats &ss = m_vPlayedStageStats[i];
|
||||
|
||||
if( !ss.OnePassed() )
|
||||
continue;
|
||||
|
||||
if( ss.m_Stage != Stage_Extra1 && ss.m_Stage != Stage_Extra2 )
|
||||
{
|
||||
if( PassedRegularSongsLeft == 0 )
|
||||
break;
|
||||
|
||||
--PassedRegularSongsLeft;
|
||||
}
|
||||
|
||||
vssToCount.push_back( ss );
|
||||
}
|
||||
|
||||
statsOut = AccumPlayedStageStats( vssToCount );
|
||||
}
|
||||
|
||||
|
||||
void StatsManager::CalcAccumPlayedStageStats()
|
||||
{
|
||||
m_AccumPlayedStageStats = AccumPlayedStageStats( m_vPlayedStageStats );
|
||||
}
|
||||
|
||||
/* This data is added to each player profile, and to the machine profile per-player. */
|
||||
void AddPlayerStatsToProfile( Profile *pProfile, const StageStats &ss, PlayerNumber pn )
|
||||
{
|
||||
ss.AssertValid( pn );
|
||||
CHECKPOINT;
|
||||
|
||||
StyleID sID;
|
||||
sID.FromStyle( ss.m_pStyle );
|
||||
|
||||
ASSERT( (int) ss.m_vpPlayedSongs.size() == ss.m_player[pn].m_iStepsPlayed );
|
||||
for( int i=0; i<ss.m_player[pn].m_iStepsPlayed; i++ )
|
||||
{
|
||||
Steps *pSteps = ss.m_player[pn].m_vpPossibleSteps[i];
|
||||
|
||||
pProfile->m_iNumSongsPlayedByPlayMode[ss.m_playMode]++;
|
||||
pProfile->m_iNumSongsPlayedByStyle[sID] ++;
|
||||
pProfile->m_iNumSongsPlayedByDifficulty[pSteps->GetDifficulty()] ++;
|
||||
|
||||
int iMeter = clamp( pSteps->GetMeter(), 0, MAX_METER );
|
||||
pProfile->m_iNumSongsPlayedByMeter[iMeter] ++;
|
||||
}
|
||||
|
||||
pProfile->m_iTotalDancePoints += ss.m_player[pn].m_iActualDancePoints;
|
||||
|
||||
if( ss.m_Stage == Stage_Extra1 || ss.m_Stage == Stage_Extra2 )
|
||||
{
|
||||
if( ss.m_player[pn].m_bFailed )
|
||||
++pProfile->m_iNumExtraStagesFailed;
|
||||
else
|
||||
++pProfile->m_iNumExtraStagesPassed;
|
||||
}
|
||||
|
||||
// If you fail in a course, you passed all but the final song.
|
||||
// FIXME: Not true. If playing with 2 players, one player could have failed earlier.
|
||||
if( !ss.m_player[pn].m_bFailed )
|
||||
{
|
||||
pProfile->m_iNumStagesPassedByPlayMode[ss.m_playMode] ++;
|
||||
pProfile->m_iNumStagesPassedByGrade[ss.m_player[pn].GetGrade()] ++;
|
||||
}
|
||||
}
|
||||
|
||||
XNode* MakeRecentScoreNode( const StageStats &ss, Trail *pTrail, const PlayerStageStats &pss, MultiPlayer mp )
|
||||
{
|
||||
XNode* pNode = NULL;
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
{
|
||||
pNode = new XNode( "HighScoreForACourseAndTrail" );
|
||||
|
||||
CourseID courseID;
|
||||
courseID.FromCourse(GAMESTATE->m_pCurCourse );
|
||||
pNode->AppendChild( courseID.CreateNode() );
|
||||
|
||||
TrailID trailID;
|
||||
trailID.FromTrail( pTrail );
|
||||
pNode->AppendChild( trailID.CreateNode() );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
pNode = new XNode( "HighScoreForASongAndSteps" );
|
||||
|
||||
SongID songID;
|
||||
songID.FromSong( ss.m_vpPossibleSongs[0] );
|
||||
pNode->AppendChild( songID.CreateNode() );
|
||||
|
||||
StepsID stepsID;
|
||||
stepsID.FromSteps( pss.m_vpPossibleSteps[0] );
|
||||
pNode->AppendChild( stepsID.CreateNode() );
|
||||
}
|
||||
|
||||
XNode* pHighScore = pss.m_HighScore.CreateNode();
|
||||
pHighScore->AppendChild("Pad", mp);
|
||||
pHighScore->AppendChild("StageGuid", GAMESTATE->m_sStageGUID);
|
||||
pHighScore->AppendChild("Guid", CryptManager::GenerateRandomUUID());
|
||||
|
||||
pNode->AppendChild( pHighScore );
|
||||
|
||||
return pNode;
|
||||
}
|
||||
|
||||
void StatsManager::CommitStatsToProfiles( const StageStats *pSS )
|
||||
{
|
||||
// Add step totals. Use radarActual, since the player might have failed part way
|
||||
// through the song, in which case we don't want to give credit for the rest of the
|
||||
// song.
|
||||
FOREACH_HumanPlayer( pn )
|
||||
{
|
||||
int iNumTapsAndHolds = (int) pSS->m_player[pn].m_radarActual[RadarCategory_TapsAndHolds];
|
||||
int iNumJumps = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Jumps];
|
||||
int iNumHolds = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Holds];
|
||||
int iNumRolls = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Rolls];
|
||||
int iNumMines = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Mines];
|
||||
int iNumHands = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Hands];
|
||||
int iNumLifts = (int) pSS->m_player[pn].m_radarActual[RadarCategory_Lifts];
|
||||
float fCaloriesBurned = pSS->m_player[pn].m_fCaloriesBurned;
|
||||
PROFILEMAN->AddStepTotals( pn, iNumTapsAndHolds, iNumJumps, iNumHolds, iNumRolls, iNumMines, iNumHands, iNumLifts, fCaloriesBurned );
|
||||
}
|
||||
|
||||
// Update profile stats
|
||||
Profile* pMachineProfile = PROFILEMAN->GetMachineProfile();
|
||||
|
||||
int iGameplaySeconds = (int)truncf(pSS->m_fGameplaySeconds);
|
||||
|
||||
pMachineProfile->m_iTotalGameplaySeconds += iGameplaySeconds;
|
||||
pMachineProfile->m_iNumTotalSongsPlayed += pSS->m_vpPlayedSongs.size();
|
||||
|
||||
CHECKPOINT;
|
||||
if( !GAMESTATE->m_bMultiplayer ) // FIXME
|
||||
{
|
||||
FOREACH_HumanPlayer( pn )
|
||||
{
|
||||
CHECKPOINT;
|
||||
|
||||
Profile* pPlayerProfile = PROFILEMAN->GetProfile( pn );
|
||||
if( pPlayerProfile )
|
||||
{
|
||||
pPlayerProfile->m_iTotalGameplaySeconds += iGameplaySeconds;
|
||||
pPlayerProfile->m_iNumTotalSongsPlayed += pSS->m_vpPlayedSongs.size();
|
||||
}
|
||||
|
||||
AddPlayerStatsToProfile( pMachineProfile, *pSS, pn );
|
||||
|
||||
if( pPlayerProfile )
|
||||
AddPlayerStatsToProfile( pPlayerProfile, *pSS, pn );
|
||||
|
||||
CHECKPOINT;
|
||||
}
|
||||
}
|
||||
|
||||
// Save recent scores
|
||||
{
|
||||
auto_ptr<XNode> xml( new XNode("Stats") );
|
||||
xml->AppendChild( "MachineGuid", PROFILEMAN->GetMachineProfile()->m_sGuid );
|
||||
|
||||
XNode *recent = NULL;
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
recent = xml->AppendChild( new XNode("RecentCourseScores") );
|
||||
else
|
||||
recent = xml->AppendChild( new XNode("RecentSongScores") );
|
||||
|
||||
if(!GAMESTATE->m_bMultiplayer)
|
||||
{
|
||||
FOREACH_HumanPlayer( p )
|
||||
{
|
||||
if( pSS->m_player[p].m_HighScore.IsEmpty() )
|
||||
continue;
|
||||
recent->AppendChild( MakeRecentScoreNode( *pSS, GAMESTATE->m_pCurTrail[p], pSS->m_player[p], MultiPlayer_Invalid ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FOREACH_EnabledMultiPlayer( mp )
|
||||
{
|
||||
if( pSS->m_multiPlayer[mp].m_HighScore.IsEmpty() )
|
||||
continue;
|
||||
recent->AppendChild( MakeRecentScoreNode( *pSS, GAMESTATE->m_pCurTrail[GAMESTATE->m_MasterPlayerNumber], pSS->m_multiPlayer[mp], mp ) );
|
||||
}
|
||||
}
|
||||
|
||||
RString sDate = DateTime::GetNowDate().GetString();
|
||||
sDate.Replace(":","-");
|
||||
|
||||
const RString UPLOAD_DIR = "/Save/Upload/";
|
||||
RString sFileNameNoExtension = Profile::MakeUniqueFileNameNoExtension(UPLOAD_DIR, sDate + " " );
|
||||
RString fn = UPLOAD_DIR + sFileNameNoExtension + ".xml";
|
||||
|
||||
bool bSaved = XmlFileUtil::SaveToFile( xml.get(), fn, "", false );
|
||||
|
||||
if( bSaved )
|
||||
{
|
||||
RString sStatsXmlSigFile = fn + SIGNATURE_APPEND;
|
||||
CryptManager::SignFileToFile(fn, sStatsXmlSigFile);
|
||||
}
|
||||
}
|
||||
|
||||
//FileCopy( "Data/TempTestGroups.xml", "Save/Upload/data.xml" );
|
||||
}
|
||||
|
||||
void StatsManager::UnjoinPlayer( PlayerNumber pn )
|
||||
{
|
||||
/* A player has been unjoined. Clear his data from m_vPlayedStageStats, and
|
||||
* purge any m_vPlayedStageStats that no longer have any player data because
|
||||
* all of the players that were playing at the time have been unjoined. */
|
||||
FOREACH( StageStats, m_vPlayedStageStats, ss )
|
||||
ss->m_player[pn] = PlayerStageStats();
|
||||
|
||||
for( int i = 0; i < (int) m_vPlayedStageStats.size(); ++i )
|
||||
{
|
||||
StageStats &ss = m_vPlayedStageStats[i];
|
||||
bool bIsActive = false;
|
||||
FOREACH_PlayerNumber( p )
|
||||
if( ss.m_player[p].m_bJoined )
|
||||
bIsActive = true;
|
||||
FOREACH_MultiPlayer( mp )
|
||||
if( ss.m_multiPlayer[mp].m_bJoined )
|
||||
bIsActive = true;
|
||||
if( bIsActive )
|
||||
continue;
|
||||
|
||||
m_vPlayedStageStats.erase( m_vPlayedStageStats.begin()+i );
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsManager::GetStepsInUse( set<Steps*> &apInUseOut ) const
|
||||
{
|
||||
for( int i = 0; i < (int) m_vPlayedStageStats.size(); ++i )
|
||||
{
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
const PlayerStageStats &pss = m_vPlayedStageStats[i].m_player[pn];
|
||||
apInUseOut.insert( pss.m_vpPossibleSteps.begin(), pss.m_vpPossibleSteps.end() );
|
||||
}
|
||||
|
||||
FOREACH_MultiPlayer( mp )
|
||||
{
|
||||
const PlayerStageStats &pss = m_vPlayedStageStats[i].m_multiPlayer[mp];
|
||||
apInUseOut.insert( pss.m_vpPossibleSteps.begin(), pss.m_vpPossibleSteps.end() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lua start
|
||||
#include "LuaBinding.h"
|
||||
|
||||
/** @brief Allow Lua to have access to the StatsManager. */
|
||||
class LunaStatsManager: public Luna<StatsManager>
|
||||
{
|
||||
public:
|
||||
static int GetCurStageStats( T* p, lua_State *L ) { p->m_CurStageStats.PushSelf(L); return 1; }
|
||||
static int GetPlayedStageStats( T* p, lua_State *L )
|
||||
{
|
||||
int iAgo = IArg(1);
|
||||
int iIndex = p->m_vPlayedStageStats.size() - iAgo;
|
||||
if( iIndex < 0 || iIndex >= (int) p->m_vPlayedStageStats.size() )
|
||||
return 0;
|
||||
|
||||
p->m_vPlayedStageStats[iIndex].PushSelf(L);
|
||||
return 1;
|
||||
}
|
||||
static int Reset( T* p, lua_State *L ) { p->Reset(); return 0; }
|
||||
static int GetAccumPlayedStageStats( T* p, lua_State *L ) { p->GetAccumPlayedStageStats().PushSelf(L); return 1; }
|
||||
static int GetFinalGrade( T* p, lua_State *L )
|
||||
{
|
||||
PlayerNumber pn = Enum::Check<PlayerNumber>(L, 1);
|
||||
|
||||
if( !GAMESTATE->IsHumanPlayer(pn) )
|
||||
lua_pushnumber( L, Grade_NoData );
|
||||
else
|
||||
{
|
||||
StageStats stats;
|
||||
p->GetFinalEvalStageStats( stats );
|
||||
lua_pushnumber( L, stats.m_player[pn].GetGrade() );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static int GetStagesPlayed( T* p, lua_State *L ) { lua_pushnumber( L, p->m_vPlayedStageStats.size() ); return 1; }
|
||||
|
||||
static int GetBestGrade( T* p, lua_State *L )
|
||||
{
|
||||
Grade g = NUM_Grade;
|
||||
FOREACH_EnabledPlayer( pn )
|
||||
g = min( g, STATSMAN->m_CurStageStats.m_player[pn].GetGrade() );
|
||||
lua_pushnumber( L, g );
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetWorstGrade( T* p, lua_State *L )
|
||||
{
|
||||
Grade g = Grade_Tier01;
|
||||
FOREACH_EnabledPlayer( pn )
|
||||
g = max( g, STATSMAN->m_CurStageStats.m_player[pn].GetGrade() );
|
||||
lua_pushnumber( L, g );
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetBestFinalGrade( T* t, lua_State *L )
|
||||
{
|
||||
Grade top_grade = Grade_Failed;
|
||||
StageStats stats;
|
||||
t->GetFinalEvalStageStats( stats );
|
||||
FOREACH_HumanPlayer( p )
|
||||
{
|
||||
// If this player failed any stage, then their final grade is an F.
|
||||
bool bPlayerFailedOneStage = false;
|
||||
FOREACH_CONST( StageStats, STATSMAN->m_vPlayedStageStats, ss )
|
||||
{
|
||||
if( ss->m_player[p].m_bFailed )
|
||||
{
|
||||
bPlayerFailedOneStage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( bPlayerFailedOneStage )
|
||||
continue;
|
||||
|
||||
top_grade = min( top_grade, stats.m_player[p].GetGrade() );
|
||||
}
|
||||
|
||||
Enum::Push( L, top_grade );
|
||||
return 1;
|
||||
}
|
||||
|
||||
LunaStatsManager()
|
||||
{
|
||||
ADD_METHOD( GetCurStageStats );
|
||||
ADD_METHOD( GetPlayedStageStats );
|
||||
ADD_METHOD( GetAccumPlayedStageStats );
|
||||
ADD_METHOD( Reset );
|
||||
ADD_METHOD( GetFinalGrade );
|
||||
ADD_METHOD( GetStagesPlayed );
|
||||
ADD_METHOD( GetBestGrade );
|
||||
ADD_METHOD( GetWorstGrade );
|
||||
ADD_METHOD( GetBestFinalGrade );
|
||||
}
|
||||
};
|
||||
|
||||
LUA_REGISTER_CLASS( StatsManager )
|
||||
// lua end
|
||||
|
||||
|
||||
/*
|
||||
* (c) 2001-2004 Chris Danford
|
||||
* 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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user