Files
GTASource/game/Stats/StatsInterface.cpp

2220 lines
73 KiB
C++
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
//
// StatsInterface.cpp
//
// Copyright (C) 1999-2009 Rockstar Games. All Rights Reserved.
//
// --- Include Files ------------------------------------------------------------
//Rage Header
#include <time.h>
#include "rline/profilestats/rlprofilestats.h"
#include "rline/rl.h"
#include "diag/output.h"
#include "system/param.h"
#include "rline/savemigration/rlsavemigration.h "
//Framework Header
#include "fwscene/world/WorldLimits.h"
#include "fwnet/netobject.h"
// Game headers
#include "Stats/StatsInterface.h"
#include "Stats/StatsMgr.h"
#include "Stats/StatsDataMgr.h"
#include "Stats/stats_channel.h"
#include "Stats/StatsUtils.h"
#include "Stats/StatsSavesMgr.h"
#include "game/ModelIndices.h"
#include "network/Live/livemanager.h"
#include "network/NetworkInterface.h"
#include "network/Objects/Entities/NetObjGame.h"
#include "network/Cloud/Tunables.h"
#include "scene/DynamicEntity.h"
#include "scene/world/GameWorld.h"
#include "peds/ped.h"
#include "peds/PedIntelligence.h"
#include "peds/Relationships.h"
#include "script/commands_stats.h"
#include "weapons/WeaponEnums.h"
#include "Modelinfo/PedModelInfo.h"
#include "Network/Objects/Synchronisation/SyncNodes/PlayerSyncNodes.h"
#include "weapons/info/WeaponInfo.h"
#include "weapons/WeaponTypes.h"
#if __ASSERT
#include "Text/TextFile.h"
#include "system/stack.h"
#include "scene/scene.h"
#include "control/gamelogic.h"
#include "script/thread.h"
#include "Stats/StatsTypes.h"
#endif // __ASSERT
FRONTEND_STATS_OPTIMISATIONS()
SAVEGAME_OPTIMISATIONS()
NETWORK_OPTIMISATIONS()
namespace StatsInterface
{
PARAM(mpSavegameUseSc, "[stat_savemgr] Use Social Club savegames for multiplayer.");
PARAM(netForceBadSport, "[stat_savemgr] Force local player to be a bad sport.");
PARAM(forceCESP, "[stat_savemgr] Simulate that the local player previously purchased Criminal Enterprise Starter Pack");
PARAM(forceNoCESP, "[stat_savemgr] Simulate that the local player did not previously purchase Criminal Enterprise Starter Pack");
//Default values for badsport thresholds
static const unsigned BADSPORT_THRESHOLD = 50;
static const unsigned BADSPORT_THRESHOLD_NOTCHEATER = 45;
//Represents the current character prefix that depends on the current player model.
static u32 s_StatsPlayerModelPrefix = 0;
static bool s_StatsPlayerModelValid = true;
static bool s_StatsPlayerModelMultiplayer = true;
//Total number of damage scars
static const int MAX_NUM_DAMAGE_SCARS = 24;
CompileTimeAssert(CPedDamageSetBase::kMaxDecorationBlits == MAX_NUM_DAMAGE_SCARS);
static const float SCAR_OFFSET_PACKING_MODIFIER = 10000.f; //use a multiplier here because it is stored in a position, and it is really an offset. Fix for losing precision (lavalley)
//Setup the multiplayer savegame cloud service.
static rlCloudOnlineService s_CloudSavegameService = RL_CLOUD_ONLINE_SERVICE_NATIVE;
//Cache weapon hash Id.
static StatId s_WeaponHashIdHealdTime;
static StatId s_WeaponHashIdDbHealdTime;
static StatId s_WeaponHashIdShots;
static StatId s_WeaponHashIdHits;
static StatId s_WeaponHashIdHitsCar;
static StatId s_WeaponHashIdEnemyKills;
static StatId s_WeaponHashIdKills;
static StatId s_WeaponHashIdDeaths;
static StatId s_WeaponHashIdHeadShots;
static u32 s_CurrWeaponHashUsed = 0;
static u32 s_CurrWeaponStatIndexUsed = 0;
//Cache well known stats iterators - Freemode BAD-SPORT
static CStatsDataMgr::StatsMap::Iterator s_StatIterIsBadSport;
static CStatsDataMgr::StatsMap::Iterator s_StatIterOverallBadSport;
#if __ASSERT
struct StatAssertHistory
{
private:
atArray < StatId > m_aKeys;
public:
StatAssertHistory()
{
Reset();
}
void Reset()
{
m_aKeys.Reset();
}
//Return the number of invalid stats already recorded.
unsigned GetCount() const { return m_aKeys.GetCount(); }
//Return TRUE if the error should be printed
bool PrintError(const StatId& keyHash)
{
bool ret = true;
for (int i=0; i<m_aKeys.GetCount(); i++)
{
if (m_aKeys[i].GetHash() == keyHash.GetHash())
{
ret = false;
break;
}
}
if (ret)
{
m_aKeys.PushAndGrow(keyHash);
}
return ret;
}
};
static StatAssertHistory s_AssertHistory;
#endif // __ASSERT
void ValidateKey(const StatId& ASSERT_ONLY(keyHash), const char* ASSERT_ONLY(msg))
{
#if __ASSERT
if(CStatsUtils::CanUpdateStats() && !CStatsMgr::GetStatsDataMgr().IsKeyValid(keyHash) && NetworkInterface::GetActiveGamerInfo() && s_AssertHistory.PrintError(keyHash))
{
int modelIdx = -1;
const char* modelName = NULL;
if (CGameWorld::FindLocalPlayerSafe() && CGameWorld::FindLocalPlayer()->GetPedModelInfo() )
{
modelName = CGameWorld::FindLocalPlayer()->GetPedModelInfo()->GetModelName();
modelIdx = CGameWorld::FindLocalPlayer()->GetModelIndex();
}
statErrorf("Invalid Key Stat hash=\"%d\", name=\"%s\", msg=\"%s\"", keyHash.GetHash(), keyHash.GetName(), msg);
statErrorf(".......... Model Index = \"%d\"", modelIdx);
statErrorf("........... Model Name = \"%s\"", modelName ? modelName : "Null");
statErrorf("........ IsNetworkOpen = \"%s\"", NetworkInterface::IsNetworkOpen() ? "Yes" : "No");
statErrorf("........... Stat Count = \"%u\"", s_AssertHistory.GetCount());
sysStack::PrintStackTrace();
}
#endif // __ASSERT
}
bool IsKeyValid(const StatId& keyHash)
{
return CStatsMgr::GetStatsDataMgr().IsKeyValid(keyHash);
}
eCharacterIndex GetCharacterIndex()
{
eCharacterIndex charIndex = CHAR_INVALID;
CPed* pPlayer = CGameWorld::FindLocalPlayer();
if(pPlayer)
{
u32 modelNameHash = pPlayer->GetPedModelInfo()->GetModelNameHash();
if( modelNameHash == MI_PLAYERPED_PLAYER_ZERO.GetName() )
{
charIndex = CHAR_MICHEAL; // Micheal
}
else if( modelNameHash == MI_PLAYERPED_PLAYER_ONE.GetName() )
{
charIndex = CHAR_FRANKLIN; // Franklin
}
else if( modelNameHash == MI_PLAYERPED_PLAYER_TWO.GetName() )
{
charIndex = CHAR_TREVOR; // Trevor
}
if (NetworkInterface::IsNetworkOpen())
{
//We must be using a Multiplayer Model
Assertf(StatsInterface::IsKeyValid("MPPLY_LAST_MP_CHAR"), "Stat MPPLY_LAST_MP_CHAR has changed name - fix this with new name");
const int mpSlot = StatsInterface::GetIntStat(STAT_MPPLY_LAST_MP_CHAR);
Assert(mpSlot<5);
charIndex = static_cast<eCharacterIndex>(CHAR_MP_START+mpSlot);
}
}
return charIndex;
}
int GetCharacterSlot()
{
const eCharacterIndex charIndex = GetCharacterIndex();
if (NetworkInterface::IsNetworkOpen())
{
return (charIndex-CHAR_MP_START);
}
return charIndex;
}
bool ValidCharacterSlot(const int slot)
{
if (NetworkInterface::IsNetworkOpen())
{
return (0 <= slot && slot < MAX_NUM_MP_CHARS);
}
return (0 <= slot && slot < MAX_NUM_SP_CHARS);
}
const char* GetKeyName(const StatId& keyHash)
{
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatDataPtr* stat = statsDataMgr.GetStatPtr(keyHash);
if (stat)
{
#if __BANK && !__NO_OUTPUT
return stat->GetKeyName();
#elif !__FINAL
static char s_statName[MAX_STAT_LABEL_SIZE];
snprintf(s_statName, MAX_STAT_LABEL_SIZE, "Hash=0x%08x", (u32)stat->GetKey());
return s_statName;
#endif // __BANK && !__NO_OUTPUT
}
return "UNKNOWN_STAT_ID";
}
bool IsKeyValid(const int keyHash)
{
return CStatsMgr::GetStatsDataMgr().IsKeyValid(STAT_ID(keyHash));
}
bool IsKeyValid(const char* str)
{
return CStatsMgr::GetStatsDataMgr().IsKeyValid(STAT_ID(str));
}
bool GetStatIsNotNull(const StatId& keyHash)
{
return CStatsMgr::GetStatsDataMgr().GetStatIsNotNull(keyHash);
}
StatType GetStatType(const StatId& keyHash)
{
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetType();
}
return STAT_TYPE_NONE;
}
rlProfileStatsType GetExpectedProfileStatType(const StatId& keyHash)
{
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetExpectedProfileStatType();
}
return RL_PROFILESTATS_TYPE_INVALID;
}
const char* GetStatTypeLabel(const StatId& keyHash)
{
return CStatsMgr::GetStatsDataMgr().GetStatTypeLabel(keyHash);
}
const sStatDescription* GetStatDesc(const StatId& keyHash)
{
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return &stat->GetDesc();
}
return NULL;
}
bool GetIsBaseType(const StatId& keyHash, const StatType type)
{
return CStatsMgr::GetStatsDataMgr().GetIsBaseType(keyHash, type);
}
void SignedOut()
{
CStatsMgr::SignedOut();
}
void SignedIn()
{
CStatsMgr::SignedIn();
}
bool CanAccessStat(const StatId& keyHash)
{
if(!CStatsMgr::GetStatsDataMgr().IsStatsDataValid())
return false;
const sStatDescription* statDesc = GetStatDesc(keyHash);
if(!statDesc)
return false;
if(!NetworkInterface::IsLocalPlayerOnline())
return false;
if(StatsInterface::CloudFileLoadPending(STAT_MP_CATEGORY_DEFAULT))
return false;
if(StatsInterface::CloudFileLoadFailed(STAT_MP_CATEGORY_DEFAULT))
return false;
if(statDesc->GetServerAuthoritative())
{
CProfileStats& profileStatsMgr = CLiveManager::GetProfileStatsMgr();
if(!CLiveManager::GetProfileStatsMgr().Synchronized(CProfileStats::PS_SYNC_MP))
return false;
int errorCode = 0;
if(profileStatsMgr.SynchronizedFailed(CProfileStats::PS_SYNC_MP, errorCode))
return false;
}
if(statDesc->GetIsCharacterStat())
{
const int selectedSlot = StatsInterface::GetCurrentMultiplayerCharaterSlot() + 1;
if(StatsInterface::CloudFileLoadPending(selectedSlot))
return false;
if(StatsInterface::CloudFileLoadFailed(selectedSlot))
return false;
}
return true;
}
bool GetStatData(const StatId& keyHash, void* pData, const unsigned SizeofData)
{
ValidateKey(keyHash, "SetStatData");
return CStatsMgr::GetStatsDataMgr().GetStatData(keyHash, pData, SizeofData);
}
bool SetStatData(const StatId& keyHash, void* pData, const unsigned SizeofData, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return false;
ValidateKey(keyHash, "SetStatData");
return CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, pData, SizeofData, flags);
}
void SetStatData(const StatId& keyHash, int data, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return;
ValidateKey(keyHash, "SetStatData");
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT) || StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT64)
|| (data >= 0 && (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT8)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT16)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT32)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT64)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_PACKED)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_PROFILE_SETTING))), "Invalid Stat key=\"%s\" hash=\"%d\" value=\"%d\"", keyHash.GetName(), keyHash.GetHash(), data);
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT) || StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_PROFILE_SETTING))
{
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &data, sizeof(data), flags);
}
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT64))
{
s64 datatemp = (s64)data;
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &datatemp, sizeof(s64), flags);
}
else if (data >= 0)
{
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT8))
{
u8 datatemp = (u8)data;
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &datatemp, sizeof(u8), flags);
}
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT16))
{
u16 datatemp = (u16)data;
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &datatemp, sizeof(u16), flags);
}
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT32))
{
u32 datatemp = (u32)data;
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &datatemp, sizeof(u32), flags);
}
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT64) || StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_PACKED))
{
u64 datatemp = (u64)data;
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &datatemp, sizeof(u64), flags);
}
}
}
void SetStatData(const StatId& keyHash, float data, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return;
ValidateKey(keyHash, "SetStatData");
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_FLOAT), "Invalid Float stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_FLOAT))
{
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &data, sizeof(data), flags);
}
}
void SetStatData(const StatId& keyHash, char* data, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return;
ValidateKey(keyHash, "SetStatData");
statAssert(data);
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_TEXTLABEL)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_STRING)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_USERID)
, "Invalid String stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
if (data)
{
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_TEXTLABEL))
{
const char* text = static_cast<const char*>(data);
int stringKeyHash = atStringHash(text);
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &stringKeyHash, sizeof(stringKeyHash), flags);
statAssertf(TheText.DoesTextLabelExist(text), "Label \"%s\" for stat key=\"%s\" hash=\"%d\" doesnt exist.", text, keyHash.GetName(), keyHash.GetHash());
}
else if(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_STRING) || StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_USERID))
{
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, data, ustrlen(data), flags);
}
}
}
void SetStatData(const StatId& keyHash, bool data, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return;
ValidateKey(keyHash, "SetStatData");
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_BOOLEAN), "Invalid Bool stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_BOOLEAN))
{
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &data, sizeof(data), flags);
}
}
void SetStatData(const StatId& keyHash, u8 data, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return;
ValidateKey(keyHash, "SetStatData");
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT8), "Invalid u8 stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT8))
{
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &data, sizeof(data), flags);
}
}
void SetStatData(const StatId& keyHash, u16 data, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return;
ValidateKey(keyHash, "SetStatData");
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT16), "Invalid u16 stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT16))
{
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &data, sizeof(data), flags);
}
}
void SetStatData(const StatId& keyHash, u32 data, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return;
ValidateKey(keyHash, "SetStatData");
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT32), "Invalid u32 stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT32))
{
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &data, sizeof(data), flags);
}
}
void SetStatData(const StatId& keyHash, u64 data, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return;
ValidateKey(keyHash, "SetStatData");
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT64)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_DATE)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_POS)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_PACKED)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_USERID), "Invalid u64 stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &data, sizeof(data), flags);
}
void SetStatData(const StatId& keyHash, s64 data, const u32 flags)
{
if (!CStatsUtils::CanUpdateStats())
return;
ValidateKey(keyHash, "SetStatData");
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT64), "Invalid s64 stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT64))
{
CStatsMgr::GetStatsDataMgr().SetStatData(keyHash, &data, sizeof(data), flags);
}
}
void SetSynched(const StatId& keyHash)
{
ValidateKey(keyHash, "SetStatData");
CStatsMgr::GetStatsDataMgr().SetStatSynched(keyHash);
}
void SetAllStatsToSynched(const int savegameSlot, const bool onlinestat, const bool includeServerAuthoritative)
{
CStatsMgr::GetStatsDataMgr().SetAllStatsToSynched(savegameSlot, onlinestat, includeServerAuthoritative);
}
void SetAllStatsToSynchedByGroup(const eProfileStatsGroups group, const bool onlinestat, const bool includeServerAuthoritative)
{
CStatsMgr::GetStatsDataMgr().SetAllStatsToSynchedByGroup(group, onlinestat, includeServerAuthoritative);
}
bool CloudFileLoadPending(const int file)
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().IsLoadPending(file);
}
void SetNoRetryOnFail(bool value)
{
CStatsMgr::GetStatsDataMgr().GetSavesMgr().SetNoRetryOnFail(value);
}
bool GetBlockSaveRequests()
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().GetBlockSaveRequests();
}
bool CloudFileLoadFailed(const int file)
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().CloudLoadFailed(file);
}
bool LoadPending(const int slot)
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().IsLoadInProgress(slot);
}
bool SavePending()
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().IsSaveInProgress();
}
bool CloudSaveFailed(const u32 slotNumber)
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().CloudSaveFailed(slotNumber);
}
bool PendingSaveRequests()
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().PendingSaveRequests();
}
void ClearSaveFailed(const int file)
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().ClearSaveFailed(file);
}
bool Load(const eStatsLoadType type, const int slotNumber)
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().Load(type, slotNumber);
}
void TryMultiplayerSave( const eSaveTypes savetype, u32 uSaveDelay )
{
if (!NetworkInterface::IsLocalPlayerOnline())
return;
if (!NetworkInterface::IsGameInProgress())
return;
const int selectedSlot = StatsInterface::GetCurrentMultiplayerCharaterSlot( ) + 1;
if (StatsInterface::CloudFileLoadPending(STAT_MP_CATEGORY_DEFAULT))
return;
if (StatsInterface::CloudFileLoadPending(selectedSlot))
return;
if (StatsInterface::CloudFileLoadFailed(STAT_MP_CATEGORY_DEFAULT))
return;
if (StatsInterface::CloudFileLoadFailed(selectedSlot))
return;
StatsInterface::Save(STATS_SAVE_CLOUD, STAT_MP_CATEGORY_DEFAULT, savetype, uSaveDelay);
}
bool Save(const eStatsSaveType type, const u32 slotNumber, const eSaveTypes savetype, const u32 uSaveDelay, const int reason)
{
return CStatsMgr::GetStatsDataMgr().GetSavesMgr().Save(type, slotNumber, savetype, uSaveDelay, reason);
}
int GetIntStat(const StatId& keyHash)
{
ValidateKey(keyHash, "GetIntStat");
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetInt();
}
return 0;
}
float GetFloatStat(const StatId& keyHash)
{
ValidateKey(keyHash, "GetFloatStat");
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetFloat();
}
return 0.0f;
}
bool GetBooleanStat(const StatId& keyHash)
{
ValidateKey(keyHash, "GetBooleanStat");
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetBoolean();
}
return false;
}
u8 GetUInt8Stat(const StatId& keyHash)
{
ValidateKey(keyHash, "GetUInt8Stat");
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetUInt8();
}
return 0;
}
u16 GetUInt16Stat(const StatId& keyHash)
{
ValidateKey(keyHash, "GetUInt16Stat");
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetUInt16();
}
return 0;
}
u32 GetUInt32Stat(const StatId& keyHash)
{
ValidateKey(keyHash, "GetUInt32Stat");
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetUInt32();
}
return 0;
}
u64 GetUInt64Stat(const StatId& keyHash)
{
ValidateKey(keyHash, "GetUInt64Stat");
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetUInt64();
}
return 0;
}
s64 GetInt64Stat(const StatId& keyHash)
{
ValidateKey(keyHash, "GetInt64Stat");
const CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
const sStatData* stat = statsDataMgr.GetStat(keyHash);
if (stat)
{
return stat->GetInt64();
}
return 0;
}
void IncrementStat(const StatId& keyHash, const float fAmount, const u32 flags)
{
statAssertf(CStatsMgr::GetStatsDataMgr().IsKeyValid(keyHash), "Invalid Stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
CStatsMgr::IncrementStat(keyHash, fAmount, flags);
}
void DecrementStat(const StatId& keyHash, const float fAmount, const u32 flags)
{
statAssertf(CStatsMgr::GetStatsDataMgr().IsKeyValid(keyHash), "Invalid Stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
CStatsMgr::DecrementStat(keyHash, fAmount, flags);
}
void SetGreater(const StatId& keyHash, const float fAmount, const u32 flags)
{
statAssertf(CStatsMgr::GetStatsDataMgr().IsKeyValid(keyHash), "Invalid Stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
CStatsMgr::SetGreater(keyHash, fAmount, flags);
}
void SetLesser(const StatId& keyHash, const float fAmount, const u32 flags)
{
statAssertf(CStatsMgr::GetStatsDataMgr().IsKeyValid(keyHash), "Invalid Stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash());
CStatsMgr::SetLesser(keyHash, fAmount, flags);
}
bool SyncProfileStats(const bool force, const bool multiplayer)
{
return CLiveManager::GetProfileStatsMgr().Synchronize(force, multiplayer);
}
bool CanSyncProfileStats()
{
return CLiveManager::GetProfileStatsMgr().CanSynchronize();
}
void UnPackDate(const u64 date, int& year, int& month, int& day, int& hour, int& min, int& sec, int& mill)
{
year = month = day = hour = min = sec = mill = 0;
year = (int)(date >> (52) & 0xFFF); // 12 bits (<=4095)
month = (int)(date >> (48) & 0xF); // 4 bits (<=15)
day = (int)(date >> (43) & 0x1F); // 5 bits (<=31)
hour = (int)(date >> (38) & 0x1F); // 5 bits (<=31)
min = (int)(date >> (32) & 0x3F); // 6 bits (<=63)
sec = (int)(date >> (26) & 0x3F); // 6 bits (<=63)
mill = (int)(date >> (0) & 0x3FF); // 10 bits (<=1023)
}
void GetBSTDate(int& year, int& month, int& day, int& hour, int& min, int& sec)
{
// year - years since 1900
// mon - months since January (0-11)
// day - day of the month (1-31)
// hour - hours since midnight (0-23)
// min - minutes after the hour (0-59)
// sec - seconds after the minute (0-61)
year = month = day = hour = min = sec = 0;
time_t date = (time_t)(rlGetPosixTime() + 3600);
struct tm* ptm;
ptm = gmtime(&date);
if (ptm)
{
year = ptm->tm_year+1900;
month = ptm->tm_mon+1;
day = ptm->tm_mday;
hour = ptm->tm_hour;
min = ptm->tm_min;
sec = ptm->tm_sec;
}
}
void GetPosixDate(int& year, int& month, int& day, int& hour, int& min, int& sec)
{
// year - years since 1900
// mon - months since January (0-11)
// day - day of the month (1-31)
// hour - hours since midnight (0-23)
// min - minutes after the hour (0-59)
// sec - seconds after the minute (0-61)
year = month = day = hour = min = sec = 0;
time_t date = (time_t)(rlGetPosixTime());
struct tm* ptm;
ptm = gmtime(&date);
if (ptm)
{
year = ptm->tm_year+1900;
month = ptm->tm_mon+1;
day = ptm->tm_mday;
hour = ptm->tm_hour;
min = ptm->tm_min;
sec = ptm->tm_sec;
}
//statDebugf2("Posix Time = %" I64FMT "d, %d:%d:%d, %d-%d-%d"
// ,date
// ,hour
// ,min
// ,sec
// ,day
// ,month
// ,year);
}
u64 PackDate(const u64 year, const u64 month, const u64 day, const u64 hour, const u64 min, const u64 sec, const u64 mill)
{
statAssert(year <= 0xFFF);
statAssert(month <= 0xF);
statAssert(day <= 0x1F);
statAssert(hour <= 0x1F);
statAssert(min <= 0x3F);
statAssert(sec <= 0x3F);
statAssert(mill <= 0x3FF);
return (year << 52 | month << 48 | day << 43 | hour << 38 | min << 32 | sec << 26 | mill);
}
#define DECIMAL_PRECISION (10) // 1 decimal
void UnPackPos(const u64 pos, float& x, float& y, float& z)
{
x = y = z = 0.0f;
float decimalX = 0;
float decimalY = 0;
float decimalZ = 0;
decimalX = (float) (pos >> 58 & 0x3F); // 6 bits
x = (float) (pos >> 44 & 0x3FFF); // 14 bits
decimalY = (float) (pos >> 38 & 0x3F); // 6 bits
y = (float) (pos >> 24 & 0x3FFF); // 14 bits
decimalZ = (float) (pos >> 18 & 0x3F); // 6 bits
z = (float) (pos >> 4 & 0x3FFF); // 14 bits
int sign = 0;
sign = (int)(pos >> (0) & 0xF); // 4 bits
x += ((float)1/DECIMAL_PRECISION)*(float)decimalX;
y += ((float)1/DECIMAL_PRECISION)*(float)decimalY;
z += ((float)1/DECIMAL_PRECISION)*(float)decimalZ;
if (sign&BIT(0)) x *= -1.0f; //First bit set
if (sign&BIT(1)) y *= -1.0f; //Second bit set
if (sign&BIT(2)) z *= -1.0f; //Third bit set
statAssert(x <= WORLDLIMITS_XMAX);
statAssert(y <= WORLDLIMITS_YMAX);
statAssert(z <= WORLDLIMITS_ZMAX);
statAssert(x >= WORLDLIMITS_XMIN);
statAssert(y >= WORLDLIMITS_YMIN);
statAssert(z >= WORLDLIMITS_ZMIN);
}
u64 PackPos(float x, float y, float z)
{
statAssert(x <= WORLDLIMITS_XMAX);
statAssert(y <= WORLDLIMITS_YMAX);
statAssert(z <= WORLDLIMITS_ZMAX);
statAssert(x >= WORLDLIMITS_XMIN);
statAssert(y >= WORLDLIMITS_YMIN);
statAssert(z >= WORLDLIMITS_ZMIN);
//Setup sign bits
int sign = 0;
if (0.0f > x) sign |= BIT(0); //First bit set
if (0.0f > y) sign |= BIT(1); //Second bit set
if (0.0f > z) sign |= BIT(2); //Third bit set
//Make number signed
if (0.0f > x) x *= -1.0f; //First bit set
if (0.0f > y) y *= -1.0f; //Second bit set
if (0.0f > z) z *= -1.0f; //Third bit set
u64 integerPartX = static_cast<u64> (x);
u64 decimalPartX = ((u64)(x*DECIMAL_PRECISION)%DECIMAL_PRECISION);
u64 integerPartY = static_cast<u64> (y);
u64 decimalPartY = ((u64)(y*DECIMAL_PRECISION)%DECIMAL_PRECISION);
u64 integerPartZ = static_cast<u64> (z);
u64 decimalPartZ = ((u64)(z*DECIMAL_PRECISION)%DECIMAL_PRECISION);
return (decimalPartX << 58 | integerPartX << 44 | decimalPartY << 38 | integerPartY << 24 | decimalPartZ << 18 | integerPartZ << 4 | sign);
}
bool IsGangMember(const CPed* ped)
{
statAssert(NetworkInterface::IsGameInProgress());
bool isGangMember = false;
if (statVerify(ped) && ped->GetNetworkObject())
{
if (ped->IsPlayer() && ped->GetNetworkObject()->GetPlayerOwner())
{
switch (ped->GetNetworkObject()->GetPlayerOwner()->GetTeam())
{
case MP_TEAM_CRIM_GANG_1:
case MP_TEAM_CRIM_GANG_2:
case MP_TEAM_CRIM_GANG_3:
case MP_TEAM_CRIM_GANG_4:
isGangMember = true;
break;
case MP_TEAM_COP:
default:
break;
}
}
else if (GetPedOfType(ped, MP_GROUP_GANG_VAGOS) || GetPedOfType(ped, MP_GROUP_GANG_LOST))
{
isGangMember = true;
}
}
return isGangMember;
}
bool GetPedOfType(const CPed* ped, const u32 type)
{
statAssert(NetworkInterface::IsGameInProgress());
statAssert(type == MP_GROUP_COP || type == MP_GROUP_GANG_VAGOS || type == MP_GROUP_GANG_LOST);
bool result = false;
if (statVerify(ped) && ped->GetNetworkObject())
{
CPedModelInfo* mi = ped->GetPedModelInfo();
if (statVerify(mi))
{
result = (mi->GetRelationshipGroupHash() == type);
}
}
return result;
}
bool IsSameTeam(const CPed* pedA, const CPed* pedB)
{
statAssert(NetworkInterface::IsGameInProgress());
bool isSameTeam = false;
if (statVerify(pedA) && statVerify(pedB) && pedA->GetNetworkObject() && pedB->GetNetworkObject())
{
if (pedA->IsPlayer() && pedB->IsPlayer() && pedA->GetNetworkObject()->GetPlayerOwner() && pedB->GetNetworkObject()->GetPlayerOwner())
{
isSameTeam = ( pedA->GetNetworkObject()->GetPlayerOwner()->GetTeam() == pedB->GetNetworkObject()->GetPlayerOwner()->GetTeam() );
}
else if(pedA->IsPlayer() && pedA->GetNetworkObject()->GetPlayerOwner())
{
if (IsPlayerCop(pedA) && StatsInterface::GetPedOfType(pedB, StatsInterface::MP_GROUP_COP))
{
isSameTeam = true;
}
else if (IsPlayerVagos(pedA) && StatsInterface::GetPedOfType(pedB, StatsInterface::MP_GROUP_GANG_VAGOS))
{
isSameTeam = true;
}
else if (IsPlayerLost(pedA) && StatsInterface::GetPedOfType(pedB, StatsInterface::MP_GROUP_GANG_LOST))
{
isSameTeam = true;
}
}
else if(pedB->IsPlayer() && pedB->GetNetworkObject()->GetPlayerOwner())
{
if (IsPlayerCop(pedB) && StatsInterface::GetPedOfType(pedA, StatsInterface::MP_GROUP_COP))
{
isSameTeam = true;
}
else if (IsPlayerVagos(pedB) && StatsInterface::GetPedOfType(pedA, StatsInterface::MP_GROUP_GANG_VAGOS))
{
isSameTeam = true;
}
else if (IsPlayerLost(pedB) && StatsInterface::GetPedOfType(pedA, StatsInterface::MP_GROUP_GANG_LOST))
{
isSameTeam = true;
}
}
else
{
CPedModelInfo* miA = pedA->GetPedModelInfo();
CPedModelInfo* miB = pedB->GetPedModelInfo();
if (statVerify(miA) && statVerify(miB))
{
isSameTeam = (miA->GetRelationshipGroupHash() == miB->GetRelationshipGroupHash());
}
}
}
return isSameTeam;
}
bool IsPlayerCop(const CPed* player)
{
statAssert(NetworkInterface::IsGameInProgress());
bool iscop = false;
if (statVerify(player && player->IsPlayer() && player->GetNetworkObject() && player->GetNetworkObject()->GetPlayerOwner()))
{
iscop = (player->GetNetworkObject()->GetPlayerOwner()->GetTeam() == MP_TEAM_COP);
}
return iscop;
}
bool IsPlayerVagos(const CPed* player)
{
statAssert(NetworkInterface::IsGameInProgress());
bool isvagos = false;
if (statVerify(player && player->IsPlayer() && player->GetNetworkObject() && player->GetNetworkObject()->GetPlayerOwner()))
{
isvagos = (player->GetNetworkObject()->GetPlayerOwner()->GetTeam() == MP_TEAM_CRIM_GANG_1);
}
return isvagos;
}
bool IsPlayerLost(const CPed* player)
{
statAssert(NetworkInterface::IsGameInProgress());
bool islost = false;
if (statVerify(player && player->IsPlayer() && player->GetNetworkObject() && player->GetNetworkObject()->GetPlayerOwner()))
{
islost = (player->GetNetworkObject()->GetPlayerOwner()->GetTeam() == MP_TEAM_CRIM_GANG_2);
}
return islost;
}
bool FindStatIterator(const StatId& keyHash, CStatsDataMgr::StatsMap::Iterator& statIter ASSERT_ONLY(, const char* debugText))
{
CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
statsDataMgr.StatIterFind(keyHash, statIter);
statAssertf(statIter.IsValid(), "Invalid stat %s - iterator is invalid, keyHash=%d", debugText, keyHash.GetHash());
if (!statIter.IsValid())
return false;
statAssertf(statIter.GetKey().IsValid(), "Invalid stat %s - key is not valid, keyHash=%d", debugText, keyHash.GetHash());
if (!statIter.GetKey().IsValid())
return false;
statAssertf(statIter.GetKey().GetHash() == keyHash.GetHash(), "Invalid stat %s - iterator key=%d != keyHash=%d", debugText, statIter.GetKey().GetHash(), keyHash.GetHash());
if (statIter.GetKey().GetHash() != keyHash.GetHash())
return false;
return true;
}
#if KEYBOARD_MOUSE_SUPPORT
void UpdateUsingKeyboard(bool usingKeyboard, const s32 lastFrameDeviceIndex, const s32 deviceIndex)
{
if(!NetworkInterface::IsGameInProgress())
return;
if(StatsInterface::CloudFileLoadPending(STAT_MP_CATEGORY_DEFAULT))
return;
if(StatsInterface::CloudFileLoadFailed(STAT_MP_CATEGORY_DEFAULT))
return;
static const StatId s_statId(ATSTRINGHASH("MPPLY_USING_KEYBOARD", 0xC88D7CFA));
static CStatsDataMgr::StatsMap::Iterator s_StatIter;
if (!s_StatIter.IsValid() || s_StatIter.GetKey().GetHash() != s_statId.GetHash())
{
StatsInterface::FindStatIterator(s_statId.GetHash(), s_StatIter ASSERT_ONLY(, "MPPLY_USING_KEYBOARD"));
}
if (s_StatIter.IsValid() && s_StatIter.GetKey().GetHash() == s_statId.GetHash())
{
const sStatData* statdata = *s_StatIter;
if (statdata)
{
if (lastFrameDeviceIndex != deviceIndex || statdata->GetBoolean() != usingKeyboard)
{
CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
statsDataMgr.SetStatIterData(s_StatIter, &usingKeyboard, statdata->GetSizeOfData());
}
}
}
}
#endif // KEYBOARD_MOUSE_SUPPORT
void RegisterParachuteDeployed(CPed* ped)
{
if(!ped)
return;
if(ped->IsNetworkClone())
return;
CStatsMgr::RegisterParachuteDeployed(ped);
}
void RegisterPrepareParachuteDeploy(CPed* ped)
{
if(!ped)
return;
if(ped->IsNetworkClone())
return;
CStatsMgr::RegisterPreOpenParachute(ped);
}
void RegisterStartSkydiving(CPed* pPed)
{
if(!pPed)
return;
if(pPed->IsNetworkClone())
return;
CStatsMgr::RegisterStartSkydiving(pPed);
}
void RegisterDestroyParachute(const CPed* pPed)
{
if(!pPed)
return;
if(pPed->IsNetworkClone())
return;
CStatsMgr::RegisterDestroyParachute(pPed);
}
void FallenThroughTheMap(CPed* pPed)
{
if(!pPed)
return;
if(pPed->IsNetworkClone())
return;
CStatsMgr::FallenThroughTheMap(pPed);
}
bool IsBadSport(bool fromScript OUTPUT_ONLY(, const bool displayOutput))
{
#if !__FINAL
int nBadSport = 0;
if(PARAM_netForceBadSport.Get(nBadSport))
return nBadSport > 0 ? true : false;
#endif
u32 posixTimeStart = 0;
if(CLiveManager::GetSCGamerDataMgr().BadSportOverrideStart(posixTimeStart))
{
u32 posixTimeEnd = 0;
if (statVerify(CLiveManager::GetSCGamerDataMgr().BadSportOverrideEnd(posixTimeEnd)))
{
const u64 currentPosixTime = rlGetPosixTime();
statDebugf1("IsBadSport:: BadSportOverrideStart=%d | BadSportOverrideEnd=%d | PosixTime=%" I64FMT "d", posixTimeStart, posixTimeEnd, currentPosixTime);
if (posixTimeStart <= (u64)currentPosixTime && currentPosixTime <= (u64)posixTimeEnd)
{
statDebugf1("IsBadSport:: override using GetSCGamerDataMgr().BadSportOverrideStart | GetSCGamerDataMgr().BadSportOverrideEnd");
if(CLiveManager::GetSCGamerDataMgr().HasBadSportOverride())
{
return false;
}
}
}
}
// check gamer data setting (only positive return value is effective)
if(CLiveManager::GetSCGamerDataMgr().IsBadSport())
{
statDebugf1("IsBadSport:: override using GetSCGamerDataMgr().IsBadSport()");
return true;
}
const bool statsLoaded = !StatsInterface::CloudFileLoadPending(STAT_MP_CATEGORY_DEFAULT);
if(!statsLoaded && !fromScript)
{
statDebugf1("IsBadSport:: Stats not loaded. Returning false.");
return false;
}
bool isBadSport = false;
#if __ASSERT
bool isTestLevel = (CScene::GetLevelsData().GetLevelIndexFromFriendlyName("gta5") != CGameLogic::GetCurrentLevelIndex());
statAssertf(statsLoaded || isTestLevel, "statsLoaded=%s, isTestLevel=%s", statsLoaded ? "true" : "false", isTestLevel ? "true" : "false");
#endif // __ASSERT
if (statsLoaded)
{
CStatsDataMgr& statsDataMgr = CStatsMgr::GetStatsDataMgr();
if (!s_StatIterIsBadSport.IsValid() || s_StatIterIsBadSport.GetKey().GetHash() != STAT_MPPLY_IS_BADSPORT.GetHash())
{
if (!FindStatIterator(STAT_MPPLY_IS_BADSPORT.GetHash(), s_StatIterIsBadSport ASSERT_ONLY(, "STAT_MPPLY_IS_BADSPORT")))
return isBadSport;
}
statAssert(s_StatIterIsBadSport.GetKey().GetHash() == STAT_MPPLY_IS_BADSPORT.GetHash());
const sStatData* statDataIsBadSport = *s_StatIterIsBadSport;
statAssert(statDataIsBadSport);
if (!statDataIsBadSport)
return isBadSport;
if (!s_StatIterOverallBadSport.IsValid() || s_StatIterOverallBadSport.GetKey().GetHash() != STAT_MPPLY_OVERALL_BADSPORT.GetHash())
{
if (!FindStatIterator(STAT_MPPLY_OVERALL_BADSPORT.GetHash(), s_StatIterOverallBadSport ASSERT_ONLY(, "STAT_MPPLY_OVERALL_BADSPORT")))
return isBadSport;
}
statAssert(s_StatIterOverallBadSport.GetKey().GetHash() == STAT_MPPLY_OVERALL_BADSPORT.GetHash());
const sStatData* statDataOverallBadSport = *s_StatIterOverallBadSport;
statAssert(statDataOverallBadSport);
if (!statDataOverallBadSport)
return isBadSport;
isBadSport = statDataIsBadSport->GetBoolean();
if (isBadSport)
{
int notBadSportTreshold = BADSPORT_THRESHOLD_NOTCHEATER;
Tunables::GetInstance().Access(MP_GLOBAL_HASH, ATSTRINGHASH("BADSPORT_THRESHOLD_NOTCHEATER", 0x727f6777), notBadSportTreshold);
if (statDataOverallBadSport->GetInt() <= notBadSportTreshold)
{
isBadSport = false;
}
}
else
{
int badsportTreshold = BADSPORT_THRESHOLD;
Tunables::GetInstance().Access(MP_GLOBAL_HASH, ATSTRINGHASH("BADSPORT_THRESHOLD", 0x4c507c3d), badsportTreshold);
isBadSport = (statDataOverallBadSport->GetInt() >= badsportTreshold);
}
if (isBadSport != statDataIsBadSport->GetBoolean())
{
statsDataMgr.SetStatIterData(s_StatIterIsBadSport, &isBadSport, statDataIsBadSport->GetSizeOfData());
}
#if !__NO_OUTPUT
if (displayOutput)
{
statDebugf3("Current bad sport value is < %d > - isBadSport=<%s>", statDataOverallBadSport->GetInt(), isBadSport ? "True" : "False");
}
#endif
}
return isBadSport;
}
int GetPedModelPrefix(CPed* playerPed)
{
int prefix = -1;
CPedModelInfo* mi = playerPed ? playerPed->GetPedModelInfo() : 0;
if(mi)
{
u32 modelNameHash = mi->GetModelNameHash();
if( modelNameHash == MI_PLAYERPED_PLAYER_ZERO.GetName() )
{
prefix = 0; // Micheal
}
else if( modelNameHash == MI_PLAYERPED_PLAYER_ONE.GetName() )
{
prefix = 1; // Franklin
}
else if( modelNameHash == MI_PLAYERPED_PLAYER_TWO.GetName() )
{
prefix = 2; // Trevor
}
//We must be using a Multiplayer Model
else if(!StatsInterface::CloudFileLoadPending(STAT_MP_CATEGORY_DEFAULT) && !StatsInterface::CloudFileLoadFailed(STAT_MP_CATEGORY_DEFAULT))
{
const int mpSlot = StatsInterface::GetCurrentMultiplayerCharaterSlot();
Assert(mpSlot < MAX_NUM_MP_CHARS);
prefix = mpSlot+CHAR_MP_START;
}
else
{
statWarningf("Assuming a multiplayer model %s - but stats are not loaded.", mi->GetModelName());
}
}
return prefix;
}
void SetStatsModelPrefix(CPed* playerPed)
{
CPedModelInfo* mi = playerPed && playerPed->IsLocalPlayer() ? playerPed->GetPedModelInfo() : 0;
if(mi)
{
const bool wasModelValid = s_StatsPlayerModelValid;
s_StatsPlayerModelValid = false;
int prefix = StatsInterface::GetPedModelPrefix(playerPed);
if (-1 < prefix && statVerify(prefix < MAX_STATS_CHAR_INDEXES))
{
const bool spCharChange = (wasModelValid && prefix != (int)s_StatsPlayerModelPrefix && prefix < CHAR_MP_START && s_StatsPlayerModelPrefix < CHAR_MP_START);
s_StatsPlayerModelValid = true;
s_StatsPlayerModelPrefix = (u32)prefix;
s_StatsPlayerModelMultiplayer = (s_StatsPlayerModelPrefix >= CHAR_MP_START);
if (spCharChange)
{
StatsInterface::IncrementStat(StatId("SP_TOTAL_SWITCHES"), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("TOTAL_SWITCHES"), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
}
const CWeaponInfo* wi = playerPed->GetEquippedWeaponInfo();
if (wi)
{
if(playerPed->GetIsInVehicle() && wi->GetIsVehicleWeapon())
{
const CVehicle* vehicle = playerPed->GetVehiclePedInside();
if (statVerify(vehicle) && statVerify(vehicle->GetVehicleModelInfo()))
{
#if __ASSERT
const char* vehicleName = vehicle->GetVehicleModelInfo()->GetGameName();
if (AssertVerify(vehicleName)) statAssertf(vehicleName[0] != '\0', "Vehicle name \"%s\" is empty for Vehicle Model Info \"%s:%d\"", vehicle->GetVehicleModelInfo()->GetVehicleMakeName(), vehicle->GetVehicleModelInfo()->GetModelName(), vehicle->GetVehicleModelInfo()->GetModelNameHash());
#endif //__ASSERT
UpdateLocalPlayerWeaponInfoStatId(wi, vehicle->GetVehicleModelInfo()->GetGameName());
}
}
else
{
UpdateLocalPlayerWeaponInfoStatId(wi, NULL);
}
}
}
statDebugf1("SetStatsModelPrefix: s_StatsPlayerModelValid=%s, perfix=%d", s_StatsPlayerModelValid?"true":"false", s_StatsPlayerModelPrefix);
}
}
u32 GetStatsModelPrefix()
{
return s_StatsPlayerModelPrefix;
}
StatId GetStatsModelHashId(const char *stat)
{
char statString[MAX_STAT_LABEL_SIZE];
statString[0] = '\0';
if (GetStatsModelPrefix())
{
safecatf(statString,MAX_STAT_LABEL_SIZE,"%s_%s",s_StatsModelPrefixes[GetStatsModelPrefix()],stat);
}
else
{
safecatf(statString,MAX_STAT_LABEL_SIZE,"%s_%s",s_StatsModelPrefixes[0],stat);
}
//Create a statid, that way we ensure that whatever is generating the hash in there will match this one.
return STAT_ID(statString);
}
bool GetStatsPlayerModelValid()
{
return s_StatsPlayerModelValid;
}
bool GetStatsPlayerModelIsMultiplayer()
{
return (s_StatsPlayerModelValid && s_StatsPlayerModelMultiplayer);
}
bool GetStatsPlayerModelIsSingleplayer()
{
return (s_StatsPlayerModelValid && !s_StatsPlayerModelMultiplayer);
}
bool SetMaskedInt(const StatId& keyHash, int data, int shift, int numberOfBits, const bool ASSERT_ONLY(/*coderAssert*/), const u32 flags)
{
ValidateKey(keyHash, "SetMaskedInt");
bool ret = false;
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT64)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_PACKED)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT), "SetMaskedInt - Invalid Stat=\"%d\" type.", keyHash.GetHash());
//STAT_SET_MASKED_INT(statHash, statValue, 8, 8) would set the second byte in a stat
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT64) || StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_PACKED))
{
u64 currValue = 0;
if (statVerify(CStatsMgr::GetStatsDataMgr().GetStatData(keyHash, &currValue, sizeof(currValue))))
{
statAssertf(numberOfBits <= 32, "StatSetMaskedInt - Stat %s Invalid number of bits %d, max value is 32", StatsInterface::GetKeyName(keyHash), numberOfBits);
statAssertf(shift <= 63, "StatSetMaskedInt - Stat %s Invalid offset %d, max value is 63", StatsInterface::GetKeyName(keyHash), shift);
statAssertf(numberOfBits+shift <= 64, "StatSetMaskedInt - Stat %s Invalid offset %d and numberOfBits %d, max value of both combined is 64", StatsInterface::GetKeyName(keyHash), shift, numberOfBits);
u64 mask = (1 << numberOfBits) - 1;
statAssertf(data <= mask, "StatSetMaskedInt - Stat %s Invalid data %d, max value is %" I64FMT "d", StatsInterface::GetKeyName(keyHash), data, mask);
u64 oldValue = currValue;
u64 dataInU64 = ((u64)data) << shift; //Data shifted to the position it should be
u64 maskInU64 = (mask) << shift; //Shifted to the position were we want the bits changed
oldValue = oldValue & ~maskInU64; //Old value with 0 on the bits we want to change
u64 newValue = oldValue | dataInU64;
ret = StatsInterface::SetStatData(keyHash, &newValue, sizeof(newValue), flags);
}
}
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT))
{
int currValue = 0;
if (statVerify(CStatsMgr::GetStatsDataMgr().GetStatData(keyHash, &currValue, sizeof(currValue))))
{
statAssertf(numberOfBits <= 32, "StatSetMaskedInt - Stat %s Invalid number of bits %d, max value is 32", StatsInterface::GetKeyName(keyHash), numberOfBits);
statAssertf(shift <= 31, "StatSetMaskedInt - Stat %s Invalid offset %d, max value is 31", StatsInterface::GetKeyName(keyHash), shift);
statAssertf(numberOfBits+shift <= 32, "StatSetMaskedInt - Stat %s Invalid offset %d and numberOfBits %d, max value of both combined is 32", StatsInterface::GetKeyName(keyHash), shift, numberOfBits);
u32 mask = (1 << numberOfBits) - 1;
u32 oldValue = (u32)currValue;
u32 dataInInt = ((u32)data) << shift; //Data shifted to the position it should be
u32 maskInInt = (mask) << shift; //Shifted to the position were we want the bits changed
oldValue = oldValue & ~maskInInt; //Old value with 0 on the bits we want to change
int newValue = (int)(oldValue | dataInInt);
ret = StatsInterface::SetStatData(keyHash, &newValue, sizeof(newValue), flags);
}
}
return ret;
}
bool GetMaskedInt(const int keyHash, int& data, int shift, int numberOfBits, int playerindex)
{
ValidateKey(keyHash, "GetMaskedInt");
bool ret = false;
statAssertf(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT64)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_PACKED)
|| StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT), "GetMaskedInt - Invalid Stat=\"%d\" type.", keyHash);
// STAT_GET_MASKED_INT(statHash, statValue, 8, 8) would get the second byte in a stat
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT64) || StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_PACKED))
{
const CNetGamePlayer* player = (NetworkInterface::IsGameInProgress() && -1 < playerindex) ? CTheScripts::FindNetworkPlayer(playerindex) : NULL;
u64 statValue = 0;
if (!player || (player && player->IsLocal()))
{
ret = CStatsMgr::GetStatsDataMgr().GetStatData(keyHash, &statValue, sizeof(statValue));
}
else if (statVerify(player && !player->IsLocal()))
{
ret = stats_commands::CommandGetRemotePlayerProfileStatValue(keyHash, player, &statValue, "STAT_GET_MASKED_INT");
}
if (statVerify(ret))
{
statAssertf(numberOfBits <= 32, "GetMaskedInt - Stat %s Invalid number of bits %d, max value is 32", StatsInterface::GetKeyName(keyHash), numberOfBits);
statAssertf(shift <= 63, "GetMaskedInt - Stat %s Invalid offset %d, max value is 63", StatsInterface::GetKeyName(keyHash), shift);
statAssertf(numberOfBits+shift <= 64, "GetMaskedInt - Stat %s Invalid offset %d and numberOfBits %d, max value of both combined is 64", StatsInterface::GetKeyName(keyHash), shift, numberOfBits);
u64 mask = (1 << numberOfBits) - 1;
u64 maskShifted = (mask << shift);
data = (int)((statValue & maskShifted) >> shift);
}
}
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT))
{
const CNetGamePlayer* player = (NetworkInterface::IsGameInProgress() && -1 < playerindex) ? CTheScripts::FindNetworkPlayer(playerindex) : NULL;
u32 statValue = 0;
if (!player || (player && player->IsLocal()))
{
ret = CStatsMgr::GetStatsDataMgr().GetStatData(keyHash, &statValue, sizeof(int));
}
else if (statVerify(player && !player->IsLocal()))
{
ret = stats_commands::CommandGetRemotePlayerProfileStatValue(keyHash, player, &statValue, "STAT_GET_MASKED_INT");
}
if (statVerify(ret))
{
statAssertf(numberOfBits <= 32, "GetMaskedInt - Stat %s Invalid number of bits %d, max value is 32", StatsInterface::GetKeyName(keyHash), numberOfBits);
statAssertf(shift <= 31, "GetMaskedInt - Stat %s Invalid offset %d, max value is 31", StatsInterface::GetKeyName(keyHash), shift);
statAssertf(numberOfBits+shift <= 32, "GetMaskedInt - Stat %s Invalid offset %d and numberOfBits %d, max value of both combined is 32", StatsInterface::GetKeyName(keyHash), shift, numberOfBits);
u32 mask = (1 << numberOfBits) - 1;
u32 maskShifted = (mask << shift);
data = (int)((statValue & maskShifted) >> shift);
}
}
return ret;
}
int GetCurrentMultiplayerCharaterSlot()
{
if (!statVerify(NetworkInterface::IsLocalPlayerOnline()))
return 0;
statAssertf(!StatsInterface::CloudFileLoadPending(STAT_MP_CATEGORY_DEFAULT), "Pending load of slot 0 multiplayer savegame.");
statAssertf(!StatsInterface::CloudFileLoadFailed(STAT_MP_CATEGORY_DEFAULT), "Load Failed of slot 0 multiplayer savegame.");
if (!StatsInterface::CloudFileLoadPending(STAT_MP_CATEGORY_DEFAULT) && !StatsInterface::CloudFileLoadFailed(STAT_MP_CATEGORY_DEFAULT))
{
static CStatsDataMgr::StatsMap::Iterator s_statIter;
if (!s_statIter.IsValid() || s_statIter.GetKey().GetHash() != STAT_MPPLY_LAST_MP_CHAR.GetHash())
{
if (!FindStatIterator(STAT_MPPLY_LAST_MP_CHAR.GetHash(), s_statIter ASSERT_ONLY(, "MPPLY_LAST_MP_CHAR")))
return 0;
}
statAssert(s_statIter.GetKey().GetHash() == STAT_MPPLY_LAST_MP_CHAR.GetHash());
const sStatData* statData = *s_statIter;
statAssert(statData);
if (statData)
return statData->GetInt();
}
return 0;
}
bool IsValidCharacterSlot(const int characterSlot)
{
return (characterSlot >= 0) && (characterSlot < MAX_NUM_MP_CHARS);
}
u64 GetBossGoonUUID(const int characterSlot)
{
const bool areStatsAvailable = !StatsInterface::CloudFileLoadPending(STAT_MP_CATEGORY_DEFAULT) && !StatsInterface::CloudFileLoadFailed(STAT_MP_CATEGORY_DEFAULT);
if(!statVerifyf(areStatsAvailable, "GetBossGoonUUID :: Stats are not loaded."))
return 0;
if(!statVerifyf(IsValidCharacterSlot(characterSlot), "GetBossGoonUUID :: Invalid characterSlot: %d", characterSlot))
return 0;
char statIdString[64];
formatf(statIdString, "MP%d_BOSS_GOON_UUID", characterSlot);
const StatId statId(statIdString);
return StatsInterface::GetUInt64Stat(statId);
}
void SavePedScarData(CPed* ped)
{
if (!NetworkInterface::IsGameInProgress())
return;
if (!NetworkInterface::IsInFreeMode())
return;
if (!statVerify(ped))
return;
if (!ped->IsLocalPlayer())
return;
const int character = StatsInterface::GetCurrentMultiplayerCharaterSlot();
Assert(character < MAX_NUM_MP_CHARS);
const int perfix = GetPedModelPrefix(ped);
if (-1==perfix || (character+CHAR_MP_START)!=perfix)
return;
const atArray<CPedScarNetworkData> *scarData = 0;
PEDDAMAGEMANAGER.GetScarDataForLocalPed(ped, scarData);
int numScars = 0;
if(scarData && scarData->GetCount() > 0)
{
char statScarName[MAX_STAT_LABEL_SIZE];
statDebugf3("Save Player Scar Data: ped=<%s>, character=<%d>", ped->GetModelName(), character);
int scarsCount = scarData->GetCount()-1;
statAssertf(scarsCount < MAX_NUM_DAMAGE_SCARS, "Number of scars %d is bigger that the allowed number in savegame %d", scarData->GetCount(), MAX_NUM_DAMAGE_SCARS);
if (scarsCount >= MAX_NUM_DAMAGE_SCARS)
{
scarsCount = MAX_NUM_DAMAGE_SCARS-1;
}
for(int index=0; index <= scarsCount && numScars<MAX_NUM_DAMAGE_SCARS; index++)
{
const CPedScarNetworkData &scarDataFrom = (*scarData)[index];
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_ZONE_", numScars);
StatId stat = STAT_ID(statScarName);
if (statVerifyf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName))
{
statDebugf3("..... Scar %d: zone=<%d>,scarNameHash=<%d>,scale=<%f>,rotation=<%f>,uvPos=<%f,%f>,forceFrame=<%d>,age=<%f>"
,numScars
,(int)scarDataFrom.zone
,(int)scarDataFrom.scarNameHash
,scarDataFrom.scale
,scarDataFrom.rotation
,scarDataFrom.uvPos.x, scarDataFrom.uvPos.y
,scarDataFrom.forceFrame
,scarDataFrom.age);
int zonevalue = scarDataFrom.zone;
StatsInterface::SetStatData(stat, &zonevalue, sizeof(int));
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_HASH_VALUE_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
int hashvalue = (int)scarDataFrom.scarNameHash.GetHash();
StatsInterface::SetStatData(stat, &hashvalue, sizeof(int));
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_SCALE_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
float scalevalue = scarDataFrom.scale;
StatsInterface::SetStatData(stat, &scalevalue, sizeof(float));
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_ROTATION_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
float rotationvalue = scarDataFrom.rotation;
StatsInterface::SetStatData(stat, &rotationvalue, sizeof(float));
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_UVPOS_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
u64 posvalue = StatsInterface::PackPos(scarDataFrom.uvPos.x * SCAR_OFFSET_PACKING_MODIFIER, scarDataFrom.uvPos.y * SCAR_OFFSET_PACKING_MODIFIER, 0.0f);
StatsInterface::SetStatData(stat, &posvalue, sizeof(u64));
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_FORCEFRAME_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
int forceframevalue = scarDataFrom.forceFrame;
StatsInterface::SetStatData(stat, &forceframevalue, sizeof(int));
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_AGE_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
float agevalue = scarDataFrom.age;
StatsInterface::SetStatData(stat, &agevalue, sizeof(float));
}
numScars++;
}
statAssertf(numScars<=MAX_NUM_DAMAGE_SCARS, "Max number of scars is %d, current is %d", MAX_NUM_DAMAGE_SCARS, numScars);
StatsInterface::SetStatData(STAT_DAMAGE_SCAR_NUMBER.GetHash(character+CHAR_MP_START), numScars);
}
}
void ClearPedScarData( )
{
statDebugf1(" ****** CLEARING ALL STATS FOR SCARS ****** ");
char statScarName[MAX_STAT_LABEL_SIZE];
for (int character=0;character<MAX_NUM_MP_CHARS;character++)
{
StatsInterface::SetStatData(STAT_DAMAGE_SCAR_NUMBER.GetHash(character+CHAR_MP_START), 0, STATUPDATEFLAG_LOAD_FROM_MP_SAVEGAME);
int ivalue = 0;
float fvalue = 0.0f;
s64 i64value = 0;
for (int numScars=0;numScars<MAX_NUM_DAMAGE_SCARS; numScars++)
{
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_ZONE_", numScars);
StatId stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
StatsInterface::SetStatData(stat, &ivalue, sizeof(int), STATUPDATEFLAG_LOAD_FROM_MP_SAVEGAME);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_HASH_VALUE_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
StatsInterface::SetStatData(stat, &ivalue, sizeof(int), STATUPDATEFLAG_LOAD_FROM_MP_SAVEGAME);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_SCALE_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
StatsInterface::SetStatData(stat, &fvalue, sizeof(float), STATUPDATEFLAG_LOAD_FROM_MP_SAVEGAME);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_ROTATION_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
StatsInterface::SetStatData(stat, &fvalue, sizeof(float), STATUPDATEFLAG_LOAD_FROM_MP_SAVEGAME);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_UVPOS_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
StatsInterface::SetStatData(stat, &i64value, sizeof(u64), STATUPDATEFLAG_LOAD_FROM_MP_SAVEGAME);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_FORCEFRAME_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
StatsInterface::SetStatData(stat, &ivalue, sizeof(int), STATUPDATEFLAG_LOAD_FROM_MP_SAVEGAME);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_AGE_", numScars);
stat = STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
StatsInterface::SetStatData(stat, &fvalue, sizeof(float), STATUPDATEFLAG_LOAD_FROM_MP_SAVEGAME);
}
}
}
void ApplyPedScarData(CPed* ped, const u32 character)
{
//Flag that this is the first boot.
static bool s_gameboot = true;
if (s_gameboot)
{
s_gameboot = false;
ClearPedScarData( );
return;
}
if (!NetworkInterface::IsInFreeMode())
return;
if (!statVerify(ped))
return;
//Only apply scar data for the local player - because the stats data for other remote players isn't present.
CPed* pLocalPlayer = CPedFactory::GetFactory() ? CPedFactory::GetFactory()->GetLocalPlayer() : NULL;
bool bSameAsLocalPlayer = (ped == pLocalPlayer);
if (!bSameAsLocalPlayer)
return;
if (statVerify(character < MAX_NUM_MP_CHARS))
{
const int numScars = StatsInterface::GetIntStat(STAT_DAMAGE_SCAR_NUMBER.GetHash(character+CHAR_MP_START));
statAssertf(numScars<=MAX_NUM_DAMAGE_SCARS, "Max number of scars is %d, current is %d", MAX_NUM_DAMAGE_SCARS, numScars);
statDebugf3("Apply Player Scar Data: ped=<%s>, numScars=<%d>, character=<%d>", ped->GetModelName(), numScars, character);
//Clear all currently resident scars so that actual stats based scars are emplaced and take precedence, otherwise multiple calls to AddPedScar and ApplyPedScarData can emplace n-times the scars - so 1 scar can become 3 or 4... (lavalley)
PEDDAMAGEMANAGER.ClearAllPlayerScars(ped);
char statScarName[MAX_STAT_LABEL_SIZE];
for(int index=0; index<numScars && index<MAX_NUM_DAMAGE_SCARS; index++)
{
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_ZONE_", index);
StatId stat = HASH_STAT_ID(statScarName);
const bool isKeyValid = StatsInterface::IsKeyValid(stat);
statAssertf(isKeyValid, "Stat %s is not valid", statScarName);
if (isKeyValid)
{
const int zone = StatsInterface::GetIntStat(stat);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_HASH_VALUE_", index);
stat = HASH_STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
const int scarNameHash = StatsInterface::GetIntStat(stat);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_SCALE_", index);
stat = HASH_STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
const float scale = StatsInterface::GetFloatStat(stat);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_ROTATION_", index);
stat = HASH_STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
const float rotation = StatsInterface::GetFloatStat(stat);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_UVPOS_", index);
stat = HASH_STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
float x,y,z;
StatsInterface::UnPackPos(StatsInterface::GetUInt64Stat(stat), x, y, z);
x /= SCAR_OFFSET_PACKING_MODIFIER;
y /= SCAR_OFFSET_PACKING_MODIFIER;
Vector2 uvPos(x, y);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_FORCEFRAME_", index);
stat = HASH_STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
const int forceFrame = StatsInterface::GetIntStat(stat);
formatf(statScarName, "%s%d_%s%d", "MP", character, "DAMAGE_SCAR_AGE_", index);
stat = HASH_STAT_ID(statScarName);
statAssertf(StatsInterface::IsKeyValid(stat), "Invalid ped scar stat=%s", statScarName);
const float age = StatsInterface::GetFloatStat(stat);
statDebugf3("..... Scar %d: zone=<%d>,uvPos=<%f,%f>,rotation=<%f>,scale=<%f>,scarNameHash=<%d>,forceFrame=<%d>,age=<%f>",index,zone,uvPos.x,uvPos.y,rotation,scale,scarNameHash,forceFrame,age);
PEDDAMAGEMANAGER.AddPedScar(ped, (ePedDamageZones)zone, uvPos, rotation, scale, scarNameHash, true, forceFrame, false, age);
}
}
}
}
//Setup the multiplayer savegame cloud service.
rlCloudOnlineService GetCloudSavegameService()
{
statAssert(NetworkInterface::IsCloudAvailable());
if (!NetworkInterface::HasValidRockstarId())
{
return RL_CLOUD_ONLINE_SERVICE_NATIVE;
}
#if __BANK
if(s_CloudSavegameService != RL_CLOUD_ONLINE_SERVICE_SC && PARAM_mpSavegameUseSc.Get())
{
s_CloudSavegameService = RL_CLOUD_ONLINE_SERVICE_SC;
}
#endif // __BANK
return s_CloudSavegameService;
}
void SetCloudSavegameService(const rlCloudOnlineService service)
{
#if __BANK
if (PARAM_mpSavegameUseSc.Get() && service == RL_CLOUD_ONLINE_SERVICE_NATIVE)
{
PARAM_mpSavegameUseSc.Set("0");
}
#endif // __BANK
s_CloudSavegameService = service;
}
bool CanUseEncrytionInMpSave()
{
// AF: These save games always need to be encrypted
// return (GetCloudSavegameService() == RL_CLOUD_ONLINE_SERVICE_NATIVE);
return true;
}
void UpdateLocalPlayerWeaponInfoStatId(const CWeaponInfo* wi, const char* vehicleName)
{
if (!CStatsUtils::IsStatsTrackingEnabled())
{
return;
}
if (!statVerify(wi))
return;
if (!statVerify(StatsInterface::GetStatsPlayerModelValid()))
return;
s_CurrWeaponHashUsed = wi->GetHash();
s_CurrWeaponStatIndexUsed = GetStatsModelPrefix();
char statString[MAX_STAT_LABEL_SIZE];
statString[0] = '\0';
if (vehicleName)
{
statAssertf(vehicleName[0] != '\0', "Vehicle name is empty, vehicle stats will be broken.");
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicleName, wi->GetStatName(), "HELDTIME");
s_WeaponHashIdHealdTime = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicleName, wi->GetStatName(), "DB_HELDTIME");
s_WeaponHashIdDbHealdTime = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicleName, wi->GetStatName(), "SHOTS");
s_WeaponHashIdShots = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicleName, wi->GetStatName(), "HITS");
s_WeaponHashIdHits = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicleName, wi->GetStatName(), "HITS_CAR");
s_WeaponHashIdHitsCar = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicleName, wi->GetStatName(), "ENEMY_KILLS");
s_WeaponHashIdEnemyKills = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicleName, wi->GetStatName(), "KILLS");
s_WeaponHashIdKills = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicleName, wi->GetStatName(), "DEATHS");
s_WeaponHashIdDeaths = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicleName, wi->GetStatName(), "HEADSHOTS");
s_WeaponHashIdHeadShots = StatId(statString);
}
else
{
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), "HELDTIME");
s_WeaponHashIdHealdTime = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), "DB_HELDTIME");
s_WeaponHashIdDbHealdTime = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), "SHOTS");
s_WeaponHashIdShots = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), "HITS");
s_WeaponHashIdHits = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), "HITS_CAR");
s_WeaponHashIdHitsCar = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), "ENEMY_KILLS");
s_WeaponHashIdEnemyKills = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), "KILLS");
s_WeaponHashIdKills = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), "DEATHS");
s_WeaponHashIdDeaths = StatId(statString);
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), "HEADSHOTS");
s_WeaponHashIdHeadShots = StatId(statString);
}
}
static StatId InvalidStatWeaponType("INVALID_STAT_WEAPON_TYPE");
StatId GetWeaponInfoHashId(const char* typeName, const CWeaponInfo* wi, const CPed* ped)
{
statAssert(typeName);
statAssert(ped);
statAssert(wi);
if (typeName && wi && ped)
{
if(ped->GetIsInVehicle() && wi->GetIsVehicleWeapon())
{
const CVehicle* vehicle = ped->GetVehiclePedInside();
if (statVerify(vehicle) && statVerify(vehicle->GetVehicleModelInfo()))
{
char statString[MAX_STAT_LABEL_SIZE];
statString[0] = '\0';
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], vehicle->GetVehicleModelInfo()->GetGameName(), wi->GetStatName(), typeName);
return StatId(statString);
}
}
else
{
char statString[MAX_STAT_LABEL_SIZE];
statString[0] = '\0';
snprintf(statString, MAX_STAT_LABEL_SIZE, "%s_%s_%s", s_StatsModelPrefixes[GetStatsModelPrefix()], wi->GetStatName(), typeName);
return StatId(statString);
}
}
return InvalidStatWeaponType;
}
StatId GetLocalPlayerWeaponInfoStatId(const eWeaponStatsTypes type, const CWeaponInfo* wi, const CPed* playerPed)
{
statAssert(wi);
statAssert(playerPed && playerPed->IsLocalPlayer());
if (!wi || !(playerPed && playerPed->IsLocalPlayer()))
return InvalidStatWeaponType;
if (wi->GetHash() != s_CurrWeaponHashUsed || s_CurrWeaponStatIndexUsed != GetStatsModelPrefix())
{
if(playerPed->GetIsInVehicle() && wi->GetIsVehicleWeapon())
{
const CVehicle* vehicle = playerPed->GetVehiclePedInside();
if (statVerify(vehicle) && statVerify(vehicle->GetVehicleModelInfo()))
{
#if __ASSERT
const char* vehicleName = vehicle->GetVehicleModelInfo()->GetGameName();
if (AssertVerify(vehicleName)) statAssertf(vehicleName[0] != '\0', "Vehicle name \"%s\" is empty for Vehicle Model Info \"%s:%d\"", vehicle->GetVehicleModelInfo()->GetVehicleMakeName(), vehicle->GetVehicleModelInfo()->GetModelName(), vehicle->GetVehicleModelInfo()->GetModelNameHash());
#endif //__ASSERT
//B*3127632: If the weapon hash is a tank rocket, always attribute to RHINO (because script are doing weird things with changing vehicle models)
if (wi->GetHash() == WEAPONTYPE_TANK)
{
UpdateLocalPlayerWeaponInfoStatId(wi, "RHINO");
}
else
{
UpdateLocalPlayerWeaponInfoStatId(wi, vehicle->GetVehicleModelInfo()->GetGameName());
}
}
}
else
{
//url:bugstar:6454734 - the periscope sub missile can be fired from the driving seat or a standalone periscope, so the player isn't a driver.
if (wi->GetHash() == WEAPONTYPE_VEHICLE_WEAPON_SUB_MISSILE_HOMING)
{
UpdateLocalPlayerWeaponInfoStatId(wi, "KOSATKA");
}
else
{
UpdateLocalPlayerWeaponInfoStatId(wi, NULL);
}
}
}
switch (type)
{
case WEAPON_STAT_HELDTIME: return s_WeaponHashIdHealdTime;
case WEAPON_STAT_DB_HELDTIME: return s_WeaponHashIdDbHealdTime;
case WEAPON_STAT_SHOTS: return s_WeaponHashIdShots;
case WEAPON_STAT_HITS: return s_WeaponHashIdHits;
case WEAPON_STAT_HITS_CAR: return s_WeaponHashIdHitsCar;
case WEAPON_STAT_ENEMY_KILLS: return s_WeaponHashIdEnemyKills;
case WEAPON_STAT_KILLS: return s_WeaponHashIdKills;
case WEAPON_STAT_DEATHS: return s_WeaponHashIdDeaths;
case WEAPON_STAT_HEADSHOTS: return s_WeaponHashIdHeadShots;
}
statAssertf(0, "Invalid weapon stat type=\"%d\"", type);
return InvalidStatWeaponType;
}
void UpdateProfileSetting(const int id)
{
char statString[MAX_STAT_LABEL_SIZE];
statString[0] = '\0';
snprintf(statString, MAX_STAT_LABEL_SIZE, "_PROFILE_SETTING_%d", id);
StatId stat(statString);
if (StatsInterface::IsKeyValid(stat))
{
int fakevalue = 0;
StatsInterface::SetStatData(stat, fakevalue);
}
}
u32 GetVehicleLeaderboardWriteInterval( )
{
static const int SP_LB_WRITE_INTERVAL = 60*60*1000;
static const int MP_LB_WRITE_INTERVAL = 30*60*1000;
if (NetworkInterface::IsGameInProgress())
return Tunables::GetInstance().TryAccess(MP_GLOBAL_HASH, ATSTRINGHASH("MP_VEHICLE_LB_WRITE_INTERVAL", 0xc0ef7953), SP_LB_WRITE_INTERVAL);
else
return Tunables::GetInstance().TryAccess(BASE_GLOBALS_HASH, ATSTRINGHASH("SP_VEHICLE_LB_WRITE_INTERVAL", 0xa246e188), MP_LB_WRITE_INTERVAL);
}
bool GetVehicleLeaderboardWriteOnSavegame()
{
bool write = false;
if (NetworkInterface::IsGameInProgress())
Tunables::GetInstance().Access(MP_GLOBAL_HASH, ATSTRINGHASH("MP_VEHICLE_LB_WRITE_ON_SAVE", 0xb52bc385), write);
else
Tunables::GetInstance().Access(BASE_GLOBALS_HASH, ATSTRINGHASH("SP_VEHICLE_LB_WRITE_ON_SAVE", 0x207af5a2), write);
return write;
}
bool GetVehicleLeaderboardKillWrite()
{
return true;
//bool killWrite = false;
//if (NetworkInterface::IsGameInProgress())
// Tunables::GetInstance().Access(MP_GLOBAL_HASH, ATSTRINGHASH("MP_VEHICLE_LB_NO_WRITE", 0x6d3007c5), killWrite);
//else
// Tunables::GetInstance().Access(BASE_GLOBALS_HASH, ATSTRINGHASH("SP_VEHICLE_LB_NO_WRITE", 0x5e6e8974), killWrite);
//return killWrite;
}
void TryVehicleLeaderboardWriteOnMatchEnd( )
{
bool write = false;
if (NetworkInterface::IsGameInProgress())
Tunables::GetInstance().Access(MP_GLOBAL_HASH, ATSTRINGHASH("MP_VEHICLE_LB_WRITE_ON_MATCHLBWRITE", 0xba0649d3), write);
if (write)
CStatsMgr::CheckWriteVehicleRecords( true );
}
bool TryVehicleLeaderboardWriteOnBoot( )
{
bool done = false;
if (NetworkInterface::IsLocalPlayerOnline() && NetworkInterface::IsCloudAvailable() && NetworkInterface::IsOnlineRos() && NetworkInterface::HasValidRosCredentials())
{
done = true;
bool write = false;
Tunables::GetInstance().Access(BASE_GLOBALS_HASH, ATSTRINGHASH("SP_VEHICLE_LB_WRITE_ON_BOOT", 0x133bb31d), write);
if (write)
CStatsMgr::CheckWriteVehicleRecords( true );
}
return done;
}
void CheckForProfileAwardStat(sStatDataPtr* ppStat)
{
statAssert(ppStat && ppStat->GetKey().IsValid());
StatId statId;
// Check for cash given to the player from scAdmin
if( ppStat->GetKey() == HASH_STAT_ID("SCADMIN_SP0_CASH") ||
ppStat->GetKey() == HASH_STAT_ID("SCADMIN_SP1_CASH") ||
ppStat->GetKey() == HASH_STAT_ID("SCADMIN_SP2_CASH") )
{
statId = ppStat->GetKey();
}
if(statId.IsValid())
{
u32 cashToAdd = StatsInterface::GetIntStat(statId);
if(cashToAdd>0)
{
statDebugf1("Adding money sent from the server to the player (%s) = %d $", ppStat->GetKeyName(), cashToAdd);
int character=-1;
if(statId == HASH_STAT_ID("SCADMIN_SP0_CASH")) character = CHAR_MICHEAL;
if(statId == HASH_STAT_ID("SCADMIN_SP1_CASH")) character = CHAR_FRANKLIN;
if(statId == HASH_STAT_ID("SCADMIN_SP2_CASH")) character = CHAR_TREVOR;
if(statVerify(character!=-1))
{
// Send an event to Script so they can add the cash and display a message
sNetworkScAdminReceivedCash eventData;
eventData.m_characterIndex.Int = character;
eventData.m_cashAmount.Int = cashToAdd;
GetEventScriptNetworkGroup()->Add(CEventNetworkScAdminReceivedCash(&eventData));
SetStatData(statId, 0); // Set the value to 0
}
}
}
}
bool HasLocalPlayerPurchasedCESP()
{
#if RSG_BANK
if (PARAM_forceCESP.Get())
{
return true;
}
else if (PARAM_forceNoCESP.Get())
{
return false;
}
else
#endif // RSG_BANK
{
/*const*/ CStatsDataMgr& dataMgr = CStatsMgr::GetStatsDataMgr();
if (dataMgr.GetAllOnlineStatsAreSynched())
{
// Retrieve player CESP purchase status
return StatsInterface::GetBooleanStat(STAT_MPPLY_SCADMIN_CESP);
}
#if IS_GEN9_PLATFORM
else
{
return NETWORK_LANDING_PAGE_STATS_MGR.HasCesp();
}
#else
return false;
#endif // IS_GEN9_PLATFORM
}
}
}
// EOF