/*
===========================================================================
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 "../../doomclassic/doom/doomdef.h"
idCVar achievements_Verbose( "achievements_Verbose", "1", CVAR_BOOL, "debug spam" );
idCVar g_demoMode( "g_demoMode", "0", CVAR_INTEGER, "this is a demo" );
bool idAchievementManager::cheatingDialogShown = false;
const struct achievementInfo_t
{
int required;
bool lifetime; // true means the current count is stored on the player profile. Doesn't matter for single count achievements.
} achievementInfo [ACHIEVEMENTS_NUM] =
{
{ 50, true }, // ACHIEVEMENT_EARN_ALL_50_TROPHIES
{ 1, true }, // ACHIEVEMENT_COMPLETED_DIFFICULTY_0
{ 1, true }, // ACHIEVEMENT_COMPLETED_DIFFICULTY_1
{ 1, true }, // ACHIEVEMENT_COMPLETED_DIFFICULTY_2
{ 1, true }, // ACHIEVEMENT_COMPLETED_DIFFICULTY_3
{ 64, false }, // ACHIEVEMENT_PDAS_BASE
{ 14, false }, // ACHIEVEMENT_WATCH_ALL_VIDEOS
{ 1, false }, // ACHIEVEMENT_KILL_MONSTER_WITH_1_HEALTH_LEFT
{ 35, false }, // ACHIEVEMENT_OPEN_ALL_LOCKERS
{ 20, true }, // ACHIEVEMENT_KILL_20_ENEMY_FISTS_HANDS
{ 1, true }, // ACHIEVEMENT_KILL_SCI_NEXT_TO_RCR
{ 1, true }, // ACHIEVEMENT_KILL_TWO_IMPS_ONE_SHOTGUN
{ 1, true }, // ACHIEVEMENT_SCORE_25000_TURKEY_PUNCHER
{ 50, true }, // ACHIEVEMENT_DESTROY_BARRELS
{ 1, true }, // ACHIEVEMENT_GET_BFG_FROM_SECURITY_OFFICE
{ 1, true }, // ACHIEVEMENT_COMPLETE_LEVEL_WITHOUT_TAKING_DMG
{ 1, true }, // ACHIEVEMENT_FIND_RAGE_LOGO
{ 1, true }, // ACHIEVEMENT_SPEED_RUN
{ 1, true }, // ACHIEVEMENT_DEFEAT_VAGARY_BOSS
{ 1, true }, // ACHIEVEMENT_DEFEAT_GUARDIAN_BOSS
{ 1, true }, // ACHIEVEMENT_DEFEAT_SABAOTH_BOSS
{ 1, true }, // ACHIEVEMENT_DEFEAT_CYBERDEMON_BOSS
{ 1, true }, // ACHIEVEMENT_SENTRY_BOT_ALIVE_TO_DEST
{ 20, true }, // ACHIEVEMENT_KILL_20_ENEMY_WITH_CHAINSAW
{ 1, true }, // ACHIEVEMENT_ID_LOGO_SECRET_ROOM
{ 1, true }, // ACHIEVEMENT_BLOODY_HANDWORK_OF_BETRUGER
{ 1, true }, // ACHIEVEMENT_TWO_DEMONS_FIGHT_EACH_OTHER
{ 20, true }, // ACHIEVEMENT_USE_SOUL_CUBE_TO_DEFEAT_20_ENEMY
{ 1, true }, // ACHIEVEMENT_ROE_COMPLETED_DIFFICULTY_0
{ 1, true }, // ACHIEVEMENT_ROE_COMPLETED_DIFFICULTY_1
{ 1, true }, // ACHIEVEMENT_ROE_COMPLETED_DIFFICULTY_2
{ 1, true }, // ACHIEVEMENT_ROE_COMPLETED_DIFFICULTY_3
{ 22, false }, // ACHIEVEMENT_PDAS_ROE
{ 1, true }, // ACHIEVEMENT_KILL_5_ENEMY_HELL_TIME
{ 1, true }, // ACHIEVEMENT_DEFEAT_HELLTIME_HUNTER
{ 1, true }, // ACHIEVEMENT_DEFEAT_BERSERK_HUNTER
{ 1, true }, // ACHIEVEMENT_DEFEAT_INVULNERABILITY_HUNTER
{ 1, true }, // ACHIEVEMENT_DEFEAT_MALEDICT_BOSS
{ 20, true }, // ACHIEVEMENT_GRABBER_KILL_20_ENEMY
{ 20, true }, // ACHIEVEMENT_ARTIFACT_WITH_BERSERK_PUNCH_20
{ 1, true }, // ACHIEVEMENT_LE_COMPLETED_DIFFICULTY_0
{ 1, true }, // ACHIEVEMENT_LE_COMPLETED_DIFFICULTY_1
{ 1, true }, // ACHIEVEMENT_LE_COMPLETED_DIFFICULTY_2
{ 1, true }, // ACHIEVEMENT_LE_COMPLETED_DIFFICULTY_3
{ 10, false }, // ACHIEVEMENT_PDAS_LE
{ 1, true }, // ACHIEVEMENT_MP_KILL_PLAYER_VIA_TELEPORT
{ 1, true }, // ACHIEVEMENT_MP_CATCH_ENEMY_IN_ROFC
{ 5, true }, // ACHIEVEMENT_MP_KILL_5_PLAYERS_USING_INVIS
{ 1, true }, // ACHIEVEMENT_MP_COMPLETE_MATCH_WITHOUT_DYING
{ 1, true }, // ACHIEVEMENT_MP_USE_BERSERK_TO_KILL_PLAYER
{ 1, true }, // ACHIEVEMENT_MP_KILL_2_GUYS_IN_ROOM_WITH_BFG
};
/*
================================================================================================
idAchievementManager
================================================================================================
*/
/*
========================
idAchievementManager::idAchievementManager
========================
*/
idAchievementManager::idAchievementManager() :
lastImpKilledTime( 0 ),
lastPlayerKilledTime( 0 ),
playerTookDamage( false )
{
counts.Zero();
ResetHellTimeKills();
}
/*
========================
idAchievementManager::Init
========================
*/
void idAchievementManager::Init( idPlayer* player )
{
owner = player;
SyncAchievments();
}
/*
========================
idAchievementManager::SyncAchievments
========================
*/
void idAchievementManager::SyncAchievments()
{
idLocalUser* user = GetLocalUser();
if( user == NULL || user->GetProfile() == NULL )
{
return;
}
// Set achievement counts
for( int i = 0; i < counts.Num(); i++ )
{
if( user->GetProfile()->GetAchievement( i ) )
{
counts[i] = achievementInfo[i].required;
}
else if( achievementInfo[i].lifetime )
{
counts[i] = user->GetStatInt( i );
}
}
}
/*
========================
idAchievementManager::GetLocalUser
========================
*/
idLocalUser* idAchievementManager::GetLocalUser()
{
if( !verify( owner != NULL ) )
{
return NULL;
}
return session->GetGameLobbyBase().GetLocalUserFromLobbyUser( gameLocal.lobbyUserIDs[ owner->GetEntityNumber() ] );
}
/*
========================
idAchievementManager::Save
========================
*/
void idAchievementManager::Save( idSaveGame* savefile ) const
{
owner.Save( savefile );
for( int i = 0; i < ACHIEVEMENTS_NUM; i++ )
{
savefile->WriteInt( counts[i] );
}
savefile->WriteInt( lastImpKilledTime );
savefile->WriteInt( lastPlayerKilledTime );
savefile->WriteBool( playerTookDamage );
savefile->WriteInt( currentHellTimeKills );
}
/*
========================
idAchievementManager::Restore
========================
*/
void idAchievementManager::Restore( idRestoreGame* savefile )
{
owner.Restore( savefile );
for( int i = 0; i < ACHIEVEMENTS_NUM; i++ )
{
savefile->ReadInt( counts[i] );
}
savefile->ReadInt( lastImpKilledTime );
savefile->ReadInt( lastPlayerKilledTime );
savefile->ReadBool( playerTookDamage );
savefile->ReadInt( currentHellTimeKills );
SyncAchievments();
}
/*
========================
idAchievementManager::EventCompletesAchievement
========================
*/
void idAchievementManager::EventCompletesAchievement( const achievement_t eventId )
{
if( g_demoMode.GetBool() )
{
return;
}
idLocalUser* localUser = GetLocalUser();
if( localUser == NULL || localUser->GetProfile() == NULL )
{
// Send a Reliable Message to the User that needs to unlock this.
if( owner != NULL )
{
int playerId = owner->entityNumber;
const int bufferSize = sizeof( playerId ) + sizeof( eventId );
byte buffer[ bufferSize ];
idBitMsg msg;
msg.InitWrite( buffer, bufferSize );
msg.WriteByte( playerId );
msg.WriteByte( eventId );
msg.WriteByteAlign();
idLib::Printf( "Host Sending Achievement\n" );
session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[ owner->entityNumber ], GAME_RELIABLE_MESSAGE_ACHIEVEMENT_UNLOCK, msg );
}
return; // Remote user or build game
}
// Check to see if we've already given the achievement.
// If so, don't do again because we don't want to autosave every time a trigger is hit
if( localUser->GetProfile()->GetAchievement( eventId ) )
{
return;
}
#ifdef ID_RETAIL
if( common->GetConsoleUsed() )
{
if( !cheatingDialogShown )
{
common->Dialog().AddDialog( GDM_ACHIEVEMENTS_DISABLED_DUE_TO_CHEATING, DIALOG_ACCEPT, NULL, NULL, true );
cheatingDialogShown = true;
}
return;
}
#endif
counts[eventId]++;
if( counts[eventId] >= achievementInfo[eventId].required )
{
session->GetAchievementSystem().AchievementUnlock( localUser, eventId );
}
else
{
if( achievementInfo[eventId].lifetime )
{
localUser->SetStatInt( eventId, counts[eventId] );
}
}
}
/*
========================
idAchievementManager::IncrementHellTimeKills
========================
*/
void idAchievementManager::IncrementHellTimeKills()
{
currentHellTimeKills++;
if( currentHellTimeKills >= 5 )
{
EventCompletesAchievement( ACHIEVEMENT_KILL_5_ENEMY_HELL_TIME );
}
}
/*
========================
idAchievementManager::SavePersistentData
========================
*/
void idAchievementManager::SavePersistentData( idDict& playerInfo )
{
for( int i = 0; i < ACHIEVEMENTS_NUM; ++i )
{
playerInfo.SetInt( va( "ach_%d", i ), counts[i] );
}
}
/*
========================
idAchievementManager::RestorePersistentData
========================
*/
void idAchievementManager::RestorePersistentData( const idDict& spawnArgs )
{
for( int i = 0; i < ACHIEVEMENTS_NUM; ++i )
{
counts[i] = spawnArgs.GetInt( va( "ach_%d", i ), "0" );
}
}
/*
========================
idAchievementManager::LocalUser_CompleteAchievement
========================
*/
void idAchievementManager::LocalUser_CompleteAchievement( achievement_t id )
{
idLocalUser* localUser = session->GetSignInManager().GetMasterLocalUser();
// Check to see if we've already given the achievement.
// If so, don't do again because we don't want to autosave every time a trigger is hit
if( localUser == NULL || localUser->GetProfile()->GetAchievement( id ) )
{
return;
}
#ifdef ID_RETAIL
if( common->GetConsoleUsed() )
{
if( !cheatingDialogShown )
{
common->Dialog().AddDialog( GDM_ACHIEVEMENTS_DISABLED_DUE_TO_CHEATING, DIALOG_ACCEPT, NULL, NULL, true );
cheatingDialogShown = true;
}
return;
}
#endif
session->GetAchievementSystem().AchievementUnlock( localUser, id );
}
/*
========================
idAchievementManager::CheckDoomClassicsAchievements
Processed when the player finishes a level.
========================
*/
// RB begin
#if defined(USE_DOOMCLASSIC)
void idAchievementManager::CheckDoomClassicsAchievements( int killcount, int itemcount, int secretcount, int skill, int mission, int map, int episode, int totalkills, int totalitems, int totalsecret )
{
const skill_t difficulty = ( skill_t )skill;
const currentGame_t currentGame = common->GetCurrentGame();
const GameMission_t expansion = ( GameMission_t )mission;
idLocalUser* localUser = session->GetSignInManager().GetMasterLocalUser();
if( localUser != NULL && localUser->GetProfile() != NULL )
{
// GENERAL ACHIEVEMENT UNLOCKING.
if( currentGame == DOOM_CLASSIC )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM1_NEOPHYTE_COMPLETE_ANY_LEVEL );
}
else if( currentGame == DOOM2_CLASSIC )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM2_JUST_GETTING_STARTED_COMPLETE_ANY_LEVEL );
}
// Complete Any Level on Nightmare.
if( difficulty == sk_nightmare && currentGame == DOOM_CLASSIC )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM1_NIGHTMARE_COMPLETE_ANY_LEVEL_NIGHTMARE );
}
const bool gotAllKills = killcount >= totalkills;
const bool gotAllItems = itemcount >= totalitems;
const bool gotAllSecrets = secretcount >= totalsecret;
if( gotAllItems && gotAllKills && gotAllSecrets )
{
if( currentGame == DOOM_CLASSIC )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM1_BURNING_OUT_OF_CONTROL_COMPLETE_KILLS_ITEMS_SECRETS );
}
else if( currentGame == DOOM2_CLASSIC )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM2_BURNING_OUT_OF_CONTROL_COMPLETE_KILLS_ITEMS_SECRETS );
}
}
// DOOM EXPANSION ACHIEVEMENTS
if( expansion == doom )
{
if( map == 8 )
{
// Medium or higher skill level.
if( difficulty >= sk_medium )
{
localUser->SetStatInt( STAT_DOOM_COMPLETED_EPISODE_1_MEDIUM + ( episode - 1 ), 1 );
}
// Hard or higher skill level.
if( difficulty >= sk_hard )
{
localUser->SetStatInt( STAT_DOOM_COMPLETED_EPISODE_1_HARD + ( episode - 1 ), 1 );
localUser->SetStatInt( STAT_DOOM_COMPLETED_EPISODE_1_MEDIUM + ( episode - 1 ), 1 );
}
if( difficulty == sk_nightmare )
{
localUser->SetStatInt( STAT_DOOM_COMPLETED_EPISODE_1_HARD + ( episode - 1 ), 1 );
localUser->SetStatInt( STAT_DOOM_COMPLETED_EPISODE_1_MEDIUM + ( episode - 1 ), 1 );
}
// Save the Settings.
localUser->SaveProfileSettings();
}
// Check to see if we've completed all episodes.
const int episode1completed = localUser->GetStatInt( STAT_DOOM_COMPLETED_EPISODE_1_MEDIUM );
const int episode2completed = localUser->GetStatInt( STAT_DOOM_COMPLETED_EPISODE_2_MEDIUM );
const int episode3completed = localUser->GetStatInt( STAT_DOOM_COMPLETED_EPISODE_3_MEDIUM );
const int episode4completed = localUser->GetStatInt( STAT_DOOM_COMPLETED_EPISODE_4_MEDIUM );
const int episode1completed_hard = localUser->GetStatInt( STAT_DOOM_COMPLETED_EPISODE_1_HARD );
const int episode2completed_hard = localUser->GetStatInt( STAT_DOOM_COMPLETED_EPISODE_2_HARD );
const int episode3completed_hard = localUser->GetStatInt( STAT_DOOM_COMPLETED_EPISODE_3_HARD );
const int episode4completed_hard = localUser->GetStatInt( STAT_DOOM_COMPLETED_EPISODE_4_HARD );
if( currentGame == DOOM_CLASSIC )
{
if( episode1completed )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM1_EPISODE1_COMPLETE_MEDIUM );
}
if( episode2completed )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM1_EPISODE2_COMPLETE_MEDIUM );
}
if( episode3completed )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM1_EPISODE3_COMPLETE_MEDIUM );
}
if( episode4completed )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM1_EPISODE4_COMPLETE_MEDIUM );
}
if( episode1completed_hard && episode2completed_hard && episode3completed_hard && episode4completed_hard )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM1_RAMPAGE_COMPLETE_ALL_HARD );
}
}
}
else if( expansion == doom2 )
{
if( map == 30 )
{
if( currentGame == DOOM2_CLASSIC )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM2_FROM_EARTH_TO_HELL_COMPLETE_HELL_ON_EARTH );
if( difficulty >= sk_hard )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM2_SUPERIOR_FIREPOWER_COMPLETE_ALL_HARD );
}
}
}
}
else if( expansion == pack_nerve )
{
if( map == 8 )
{
if( currentGame == DOOM2_CLASSIC )
{
LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM2_AND_BACK_AGAIN_COMPLETE_NO_REST );
}
}
}
}
}
#endif // #if defined(USE_DOOMCLASSIC)
// RB end
/*
=================
AchievementsReset
=================
*/
CONSOLE_COMMAND( AchievementsReset, "Lock an achievement", NULL )
{
idLocalUser* user = session->GetSignInManager().GetMasterLocalUser();
if( user == NULL )
{
idLib::Printf( "Must be signed in\n" );
return;
}
if( args.Argc() == 1 )
{
for( int i = 0; i < ACHIEVEMENTS_NUM; i++ )
{
user->SetStatInt( i, 0 );
session->GetAchievementSystem().AchievementLock( user, i );
}
}
else
{
int i = atoi( args.Argv( 1 ) );
user->SetStatInt( i, 0 );
session->GetAchievementSystem().AchievementLock( user, i );
}
user->SaveProfileSettings();
}
/*
=================
AchievementsUnlock
=================
*/
CONSOLE_COMMAND( AchievementsUnlock, "Unlock an achievement", NULL )
{
idLocalUser* user = session->GetSignInManager().GetMasterLocalUser();
if( user == NULL )
{
idLib::Printf( "Must be signed in\n" );
return;
}
if( args.Argc() == 1 )
{
for( int i = 0; i < ACHIEVEMENTS_NUM; i++ )
{
user->SetStatInt( i, achievementInfo[i].required );
session->GetAchievementSystem().AchievementUnlock( user, i );
}
}
else
{
int i = atoi( args.Argv( 1 ) );
user->SetStatInt( i, achievementInfo[i].required );
session->GetAchievementSystem().AchievementUnlock( user, i );
}
user->SaveProfileSettings();
}
/*
=================
AchievementsList
=================
*/
CONSOLE_COMMAND( AchievementsList, "Lists achievements and status", NULL )
{
idPlayer* player = gameLocal.GetLocalPlayer();
idLocalUser* user = ( player == NULL ) ? session->GetSignInManager().GetMasterLocalUser() : session->GetGameLobbyBase().GetLocalUserFromLobbyUser( gameLocal.lobbyUserIDs[ player->GetEntityNumber() ] );
if( user == NULL )
{
idLib::Printf( "Must be signed in\n" );
return;
}
idPlayerProfile* profile = user->GetProfile();
idArray achievementState;
bool achievementStateValid = session->GetAchievementSystem().GetAchievementState( user, achievementState );
for( int i = 0; i < ACHIEVEMENTS_NUM; i++ )
{
const char* pInfo = "";
if( profile == NULL )
{
pInfo = S_COLOR_RED "unknown" S_COLOR_DEFAULT;
}
else if( !profile->GetAchievement( i ) )
{
pInfo = S_COLOR_YELLOW "locked" S_COLOR_DEFAULT;
}
else
{
pInfo = S_COLOR_GREEN "unlocked" S_COLOR_DEFAULT;
}
const char* sInfo = "";
if( !achievementStateValid )
{
sInfo = S_COLOR_RED "unknown" S_COLOR_DEFAULT;
}
else if( !achievementState[i] )
{
sInfo = S_COLOR_YELLOW "locked" S_COLOR_DEFAULT;
}
else
{
sInfo = S_COLOR_GREEN "unlocked" S_COLOR_DEFAULT;
}
int count = 0;
if( achievementInfo[i].lifetime )
{
count = user->GetStatInt( i );
}
else if( player != NULL )
{
count = player->GetAchievementManager().GetCount( ( achievement_t ) i );
}
else
{
count = 0;
}
achievementDescription_t data;
bool descriptionValid = session->GetAchievementSystem().GetAchievementDescription( user, i, data );
idLib::Printf( "%02d: %2d/%2d | %12.12s | %12.12s | %s%s\n", i, count, achievementInfo[i].required, pInfo, sInfo, descriptionValid ? data.hidden ? "(hidden) " : "" : "(unknown) ", descriptionValid ? data.name : "" );
}
}