/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see .
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../idlib/precompiled.h"
#pragma hdrstop
#include "Game_local.h"
#include "Leaderboards.h"
#include "MultiplayerGame.h"
/*
================================================================================================
Leaderboard stats column definitions - per Game Type.
================================================================================================
*/
static columnDef_t public_Deathmatch[] = {
{ "Frags", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
//{ "Deaths", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
//{ "Wins", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
//{ "Score", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
};
static columnDef_t public_Tourney[] = {
{ "Wins", 64, AGGREGATE_SUM, STATS_COLUMN_DISPLAY_NUMBER },
};
static columnDef_t public_TeamDeathmatch[] = {
{ "Wins", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
};
static columnDef_t public_LastmanStanding[] = {
{ "Wins", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
};
static columnDef_t public_CaptureTheFlag[] = {
{ "Wins", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
};
// This should match up to the ordering of the gameType_t. ( in MultiplayerGame.h )
const columnGameMode_t gameMode_columnDefs[] = {
{ public_Deathmatch, ARRAY_COUNT( public_Deathmatch ), RANK_GREATEST_FIRST, false, false, "DM" }, // DEATHMATCH
{ public_Tourney, ARRAY_COUNT( public_Tourney ), RANK_GREATEST_FIRST, false, false, "TOURNEY" }, // TOURNEY
{ public_TeamDeathmatch, ARRAY_COUNT( public_TeamDeathmatch ), RANK_GREATEST_FIRST, false, false, "TDM" }, // TEAM DEATHMATCH
{ public_LastmanStanding, ARRAY_COUNT( public_LastmanStanding ), RANK_GREATEST_FIRST, false, false, "LMS" }, // LASTMAN STANDING
{ public_CaptureTheFlag, ARRAY_COUNT( public_CaptureTheFlag ), RANK_GREATEST_FIRST, false, false, "CTF" }, // CAPTURE THE FLAG
};
/*
=====================================
RetreiveLeaderboardID
Each map will move in blocks of n*modes.
ex. map 0 will have 0 - 4 Leaderboard id's blocked out.
map 1 will have 5 - 10 leaderboard id's blocked out.
if gamemode is added it will move in blocks of ARRAY_COUNT( modes )
=====================================
*/
int LeaderboardLocal_GetID( int mapIndex, int gametype ) {
assert( gametype > GAME_RANDOM );
return mapIndex * ARRAY_COUNT( gameMode_columnDefs ) + gametype;
}
/*
=====================================
LeaderboardLocal_Init
=====================================
*/
void LeaderboardLocal_Init() {
const idList< mpMap_t > maps = common->GetMapList();
const char ** gameModes = NULL;
const char ** gameModesDisplay = NULL;
int numModes = game->GetMPGameModes( &gameModes, &gameModesDisplay );
// Iterate through all the available maps, and generate leaderboard Defs, and IDs for each.
for( int mapIdx = 0; mapIdx < maps.Num(); mapIdx++ ) {
for( int modeIdx = 0; modeIdx < numModes; modeIdx++ ) {
// Check the supported modes on the map.
if( maps[ mapIdx ].supportedModes & BIT( modeIdx ) ) {
const columnGameMode_t gamemode = gameMode_columnDefs[ modeIdx ];
// Generate a Leaderboard ID for the map/mode
int boardID = LeaderboardLocal_GetID( mapIdx, modeIdx );
// Create and Register the leaderboard with the sys_stats registered Leaderboards
leaderboardDefinition_t * newLeaderboardDef = Sys_CreateLeaderboardDef( boardID,
gamemode.numColumns,
gamemode.columnDef,
gamemode.rankOrder,
gamemode.supportsAttachments,
gamemode.checkAgainstCurrent );
// Set the leaderboard name.
const char* mapname = idLocalization::GetString( maps[ mapIdx ].mapName );
newLeaderboardDef->boardName.Format( "%s %s", mapname, gamemode.abrevName );
// sanity check.
if( Sys_FindLeaderboardDef( boardID ) != newLeaderboardDef ) {
idLib::Error( "Leaderboards_Init leaderboard creation failed" );
}
}
}
}
}
/*
=====================================
LeaderboardLocal_Shutdown
=====================================
*/
void LeaderboardLocal_Shutdown() {
Sys_DestroyLeaderboardDefs();
}
/*
=====================================
LeaderboardLocal_Upload
=====================================
*/
const static int FRAG_MULTIPLIER = 100;
const static int DEATH_MULTIPLIER = -50;
const static int WINS_MULTIPLIER = 20;
void LeaderboardLocal_Upload( lobbyUserID_t lobbyUserID,int gameType, leaderboardStats_t & stats ) {
assert( gameType > GAME_RANDOM );
int mapIdx = 0;
// Need to figure out What stat columns we want to base rank on. ( for now we'll use wins )
const column_t * gameTypeColumn = NULL;
const column_t defaultStats[] = { stats.wins };
// calculate DM score.
int dmScore = stats.frags * FRAG_MULTIPLIER + stats.deaths * DEATH_MULTIPLIER + stats.wins * WINS_MULTIPLIER ;
// TODO: Once leaderboard menu has correct columns- enable this -> const column_t dmStats[] = { stats.frags, stats.deaths, stats.wins, dmScore };
const column_t dmStats[] = { dmScore };
// Calculate TDM score.
int tdmScore = stats.frags * FRAG_MULTIPLIER + stats.teamfrags * FRAG_MULTIPLIER + stats.deaths * DEATH_MULTIPLIER + stats.wins * WINS_MULTIPLIER ;
const column_t tdmStats[] = { tdmScore };
// Calculate Tourney score.
int tourneyScore = stats.wins;
const column_t tnyStats[] = { tourneyScore };
// Calculate LMS score.
int lmsFrags = stats.frags;
if( lmsFrags < 0 ) {
lmsFrags = 0; // LMS NO LIVES LEFT = -20 on fragCount.
}
int lmsScore = lmsFrags * FRAG_MULTIPLIER + stats.wins * ( WINS_MULTIPLIER * 10 ) ;
const column_t lmsStats[] = { lmsScore };
// Calculate CTF score.
int ctfScore = stats.frags * FRAG_MULTIPLIER + stats.deaths * DEATH_MULTIPLIER + stats.wins * ( WINS_MULTIPLIER * 10 );
const column_t ctfStats[] = { ctfScore };
switch( gameType ) {
case GAME_DM:
gameTypeColumn = dmStats;
break;
case GAME_TDM:
gameTypeColumn = tdmStats;
break;
case GAME_TOURNEY:
gameTypeColumn = tnyStats;
break;
case GAME_LASTMAN:
gameTypeColumn = lmsStats;
break;
case GAME_CTF: {
gameTypeColumn = ctfStats;
break;
}
default:
gameTypeColumn = defaultStats;
}
const idMatchParameters & matchParameters = session->GetActingGameStateLobbyBase().GetMatchParms();
const idList< mpMap_t > maps = common->GetMapList();
// need to find the map Index number
for( mapIdx = 0; mapIdx < maps.Num(); mapIdx++ ) {
if( matchParameters.mapName.Cmp( maps[ mapIdx ].mapFile ) == 0 ) {
break;
}
}
int boardID = LeaderboardLocal_GetID( mapIdx, gameType );
const leaderboardDefinition_t * board = Sys_FindLeaderboardDef( boardID );
if( board ) {
session->LeaderboardUpload( lobbyUserID, board, gameTypeColumn );
} else {
idLib::Warning( "LeaderboardLocal_Upload invalid leaderboard with id of %d", boardID );
}
}
class idLeaderboardCallbackTest : public idLeaderboardCallback {
void Call() {
idLib::Printf( "Leaderboard information retrieved in user callback.\n" );
idLib::Printf( "%d total entries in leaderboard %d.\n", numRowsInLeaderboard, def->id );
for ( int i = 0; i < rows.Num(); i++ ) {
idLib::Printf( "%d: %s rank:%lld", i, rows[i].name.c_str(), rows[i].rank );
for ( int j = 0; j < def->numColumns; j++ ) {
idLib::Printf( ", score[%d]: %lld", j, rows[i].columns[j] );
}
idLib::Printf( "\n" );
}
}
idLeaderboardCallback * Clone() const {
return new (TAG_PSN) idLeaderboardCallbackTest( *this );
}
};
CONSOLE_COMMAND( testLeaderboardDownload, " ", 0 ) {
idLeaderboardCallbackTest leaderboardCallbackTest;
int leaderboardID = 0;
int start = 1;
int end = 100;
if ( args.Argc() > 1 ) {
leaderboardID = atoi( args.Argv( 1 ) );
}
if ( args.Argc() > 2 ) {
start = atoi( args.Argv( 2 ) );
}
if ( args.Argc() > 3 ) {
end = atoi( args.Argv( 3 ) );
}
const leaderboardDefinition_t * leaderboardDef = Sys_FindLeaderboardDef( leaderboardID );
if( leaderboardDef ) {
session->LeaderboardDownload( 0, leaderboardDef, start, end, leaderboardCallbackTest );
} else {
idLib::Warning( "Sys_FindLeaderboardDef() Unable to find board %d\n", leaderboardID );
}
}
CONSOLE_COMMAND( testLeaderboardUpload, " ", 0 ) {
idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
lobbyUserID_t user = lobby.GetLobbyUserIdByOrdinal( 0 );
gameType_t gameType = GAME_DM;
int frags = 0;
int wins = 1;
if ( args.Argc() > 1 ) {
gameType = static_cast( atoi( args.Argv( 1 ) ) );
}
if ( args.Argc() > 2 ) {
frags = atoi( args.Argv( 2 ) );
}
if ( args.Argc() > 3 ) {
wins = atoi( args.Argv( 3 ) );
}
leaderboardStats_t stats = { frags, wins, 0, 0 };
LeaderboardLocal_Upload( user, gameType , stats );
session->LeaderboardFlush();
}
CONSOLE_COMMAND( testLeaderboardUpload_SendToClients, " ", 0 ) {
for( int playerIdx = 0; playerIdx < gameLocal.numClients; playerIdx++ ) {
leaderboardStats_t stats = { 1, 1, 1, 1 };
LeaderboardLocal_Upload( gameLocal.lobbyUserIDs[ playerIdx ], gameLocal.gameType, stats );
}
// Dont do this more than once.
session->LeaderboardFlush();
}