6648 lines
210 KiB
C++
6648 lines
210 KiB
C++
//
|
|
// StatsMgr.cpp
|
|
//
|
|
// Copyright (C) 1999-2009 Rockstar Games. All Rights Reserved.
|
|
//
|
|
|
|
// --- Include Files ------------------------------------------------------------
|
|
|
|
// C headers
|
|
|
|
// Rage headers
|
|
#include "system/param.h"
|
|
#include "file/asset.h"
|
|
#include "net/nethardware.h"
|
|
#include "grcore/device.h"
|
|
|
|
// Framework headers
|
|
#include "fwnet/netleaderboardmgr.h"
|
|
#include "fwnet/netscgameconfigparser.h"
|
|
#include "fwnet/netleaderboardwrite.h"
|
|
#include "fwnet/netleaderboardcommon.h"
|
|
|
|
// Stats Headers
|
|
#include "StatsMgr.h"
|
|
#include "Stats/StatsUtils.h"
|
|
#include "Stats/StatsDataMgr.h"
|
|
#include "Stats/StatsInterface.h"
|
|
#include "Stats/stats_channel.h"
|
|
#include "Stats/MoneyInterface.h"
|
|
//Parser headers
|
|
#include "Stats/StatsMgr_parser.h"
|
|
|
|
// Game headers
|
|
#include "Core/Game.h"
|
|
#include "game/user.h"
|
|
#include "ModelInfo/PedModelInfo.h"
|
|
#include "Modelinfo/VehicleModelInfo.h"
|
|
#include "PedGroup/PedGroup.h"
|
|
#include "Peds/ped.h"
|
|
#include "Peds/PlayerInfo.h"
|
|
#include "Peds/PedIntelligence.h"
|
|
#include "Peds/PopZones.h"
|
|
#include "Vehicles/Vehicle.h"
|
|
#include "Vehicles/Bike.h"
|
|
#include "Vehicles/Boat.h"
|
|
#include "Vehicles/Train.h"
|
|
#include "Vehicles/Planes.h"
|
|
#include "Vehicles/Submarine.h"
|
|
#include "Vehicles/Metadata/VehicleLayoutInfo.h"
|
|
#include "Weapons/Weapon.h"
|
|
#include "Scene/Entity.h"
|
|
#include "Scene/World/GameWorld.h"
|
|
#include "scene/world/GameWorldHeightMap.h"
|
|
#include "scene/playerswitch/PlayerSwitchInterface.h"
|
|
#include "Camera/CamInterface.h"
|
|
#include "Camera/cinematic/CinematicDirector.h"
|
|
#include "camera/base/BaseCamera.h"
|
|
#include "camera/cinematic/camera/mounted/CinematicMountedCamera.h"
|
|
#include "System/control.h"
|
|
#include "Event/EventDamage.h"
|
|
#include "Control/Gamelogic.h"
|
|
#include "task/System/AsyncProbeHelper.h"
|
|
#include "task/Default/TaskPlayer.h"
|
|
#include "physics/WorldProbe/shapetestresults.h"
|
|
#include "script/script_cars_and_peds.h"
|
|
#include "script/script.h"
|
|
#include "Network/Objects/Entities/NetObjVehicle.h"
|
|
#include "network/live/NetworkTelemetry.h"
|
|
#include "Network/Sessions/NetworkSession.h"
|
|
#include "network/Stats/NetworkLeaderboardSessionMgr.h"
|
|
#include "event/EventDamage.h"
|
|
#include "SaveLoad/GenericGameStorage.h"
|
|
|
|
#include "fwsys/fileExts.h"
|
|
#include "audio/ambience/ambientaudioentity.h"
|
|
#include "audio/ambience/water/audshorelineOcean.h"
|
|
|
|
#include "script/script_hud.h"
|
|
|
|
FRONTEND_STATS_OPTIMISATIONS()
|
|
SAVEGAME_OPTIMISATIONS()
|
|
|
|
// --- Defines ------------------------------------------------------------------
|
|
|
|
#undef OpenFile
|
|
|
|
RAGE_DEFINE_CHANNEL(stat, DIAG_SEVERITY_DEBUG3, DIAG_SEVERITY_DEBUG1, DIAG_SEVERITY_ASSERT)
|
|
|
|
// --- Constants/Static arrays --------------------------------------------------
|
|
|
|
const float CStatsVehicleUsage::VEHICLE_MIN_DRIVE_DIST = 0.5f;
|
|
|
|
//Minimum speed to consider movement
|
|
static const float MIN_SPEED_TO_CONSIDER_MOVEMENT = 0.1f;
|
|
static const float MIN_PLANE_SPEED_TO_CONSIDER_MOVEMENT = 1.0f;
|
|
static const float MIN_SPEED_TO_CONSIDER_JUMP = 2.0f;
|
|
|
|
// Flip up threshold
|
|
static const float VEHICLE_FLIP_UP_THRESHOLD = 0.60f;
|
|
//Up side down threshold
|
|
static const float VEHICLE_UPSIDEDOWN_UPZ_THRESHOLD = -0.90f;
|
|
|
|
//Vertical threshold
|
|
static const float VEHICLE_FRONT_THRESHOLD = 0.9f;
|
|
|
|
//How long to let less than 2 wheels count as 2.
|
|
static const u32 CAR_TWOWHEEL_BUFFERLIMIT = 500;
|
|
static const u32 CAR_TWOWHEEL_MINBEST = 1000; //Need to be at least this long to bother recording it.
|
|
//How long to let NO wheels count as 1
|
|
static const u32 BIKE_ONEWHEEL_BUFFERLIMIT = 500;
|
|
static const u32 BIKE_REARWHEEL_MINBEST = 3000; //Need to be at least this long to bother recording it
|
|
static const u32 BIKE_FRONTWHEEL_MINBEST = 1000; //Need to be at least this long to bother recording it
|
|
//How long to let NO wheels count as 1
|
|
static const u32 ZEROWHEEL_MINBEST = 500; //Need to be at least this long to bother recording it
|
|
static const u32 ZEROWHEEL_BUFFERLIMIT = 1000;
|
|
static const float ZEROWHEEL_MIN_DIST = 0.5f; //Need to be at least this long to bother recording it
|
|
static const u32 ZEROWHEEL_MIN_WHEELS = 2; //Need to be at least this Minimum number of contact wheels when landing
|
|
|
|
//Time between ground probes.
|
|
static const u32 TIME_BETWEEN_GROUND_PROBES = 3000;
|
|
|
|
//update rate for playing time
|
|
const float UPDATE_RATE_PLAYING_TIME = 10000.0f;
|
|
|
|
static const u32 STAT_NUM_CHECKS_DONE = ATSTRINGHASH("NUM_CHECKS_DONE", 0xFBDF6351);
|
|
static const u32 STAT_NUM_CHECKS_MISM_CURR = ATSTRINGHASH("NUM_CHECKS_MISM_CURR", 0x8D72E8CA);
|
|
static const u32 STAT_NUM_CHECKS_MISM_PERC = ATSTRINGHASH("NUM_CHECKS_MISM_PERC", 0x20D62AC7);
|
|
static const u32 STAT_NUM_CHECKS_ISSUED_CURR = ATSTRINGHASH("NUM_CHECKS_ISSUED_CURR", 0x8F1AAA90);
|
|
static const u32 STAT_NUM_CHECKS_RCVD_CURR = ATSTRINGHASH("NUM_CHECKS_RCVD_CURR", 0xF134773E);
|
|
static const u32 STAT_NUM_CHECKS_ISSUED = ATSTRINGHASH("NUM_CHECKS_ISSUED", 0x9E0CFC5);
|
|
static const u32 STAT_NUM_CHECKS_RCVD = ATSTRINGHASH("NUM_CHECKS_RCVD", 0x14BA8EBF);
|
|
// --- Static Class Members -----------------------------------------------------
|
|
|
|
PARAM(facebookvehiclesdriven, "[stat_savemgr] Setup the number of vehicles driven.");
|
|
|
|
sStatsMetadataTuning CStatsMgr::sm_TuneMetadata;
|
|
|
|
//Stats metadata manager
|
|
CStatsDataMgr CStatsMgr::sm_StatsData;
|
|
|
|
//Used to transitions of valid multiplayer characters
|
|
static bool s_isInSP = true;
|
|
|
|
//Track vehicle analytics that is flushed every sm_lbFlushDefaultInterval or when its full.
|
|
atFixedArray <CStatsVehicleUsage, CStatsMgr::NUM_VEHICLE_RECORDS> CStatsMgr::sm_vehicleRecords;
|
|
u32 CStatsMgr::sm_curVehIdx = CStatsMgr::INVALID_RECORD;
|
|
|
|
//Track Peds run down
|
|
atFixedArray < u16, CStatsMgr::NUM_PEDS_TO_TRACK > CStatsMgr::sm_PedsRunDownRecords;
|
|
|
|
//Track diferent Ped types killed
|
|
atFixedArray < int, PEDTYPE_LAST_PEDTYPE > CStatsMgr::sm_PedsKilledOfThisType;
|
|
|
|
//Track tyres popped by Gun shot
|
|
atArray< bool > CStatsMgr::sm_WheelStatus;
|
|
u16 CStatsMgr::sm_VehicleRandomSeed = 0;
|
|
|
|
float CStatsMgr::minInvertFlightDot = -0.3f;
|
|
|
|
//Track Session run time
|
|
float CStatsMgr::sm_SessionRunningTime = 0.0f;
|
|
float CStatsMgr::sm_PlayingTime = 0.0f;
|
|
float CStatsMgr::sm_StealthTime = 0.0f;
|
|
float CStatsMgr::sm_FirstPersonTime = 0.0f;
|
|
float CStatsMgr::sm_3rdPersonTime = 0.0f;
|
|
|
|
//interval between accepting a stats that depend on dead check counters
|
|
static const unsigned DEAD_CHECK_INTERVAL = 3*1000;
|
|
bool CStatsMgr::sm_VehicleDeadCheck = false;
|
|
float CStatsMgr::sm_VehicleDeadCheckCounter = 0.0f;
|
|
|
|
//Track Longest Drive Without a crash
|
|
float CStatsMgr::sm_DriveNoCrashTime = 0.0f;
|
|
float CStatsMgr::sm_DriveNoCrashDist = 0.0f;
|
|
|
|
//Track most near misses without crashing
|
|
u32 CStatsMgr::sm_NearMissNoCrash = 0;
|
|
float CStatsMgr::sm_JumpDistance = 0.0f;
|
|
float CStatsMgr::sm_LastJumpHeight = 0.0f;
|
|
float CStatsMgr::sm_CurrentSpeed = 0.0f;
|
|
|
|
// Track close encounters with vehicles(no crash)
|
|
u32 CStatsMgr::sm_NearMissNoCrashPrecise = 0;
|
|
u32 CStatsMgr::sm_NearMissCounterArray[MAXIMUM_NEAR_MISS_COUNTER];
|
|
|
|
//Track cover and cower stats
|
|
bool CStatsMgr::sm_bWasInCover = false;
|
|
bool CStatsMgr::sm_bShotFiredInCover = false;
|
|
bool CStatsMgr::sm_bWasCrouching = false;
|
|
bool CStatsMgr::sm_bShotFiredInCrouch = false;
|
|
|
|
//Track player in Vehicle
|
|
bool CStatsMgr::sm_WasInVehicle = false;
|
|
|
|
//Track ped in air stats
|
|
bool CStatsMgr::sm_WasInAir = false;
|
|
Vector3 CStatsMgr::sm_FreeFallStartPos = Vector3(VEC3_ZERO);
|
|
bool CStatsMgr::sm_FreeFallDueToPlayerDamage = false;
|
|
u32 CStatsMgr::sm_FreeFallStartTimer = false;
|
|
Vector3 CStatsMgr::sm_ChallengeSkydiveStartPos = Vector3(VEC3_ZERO);
|
|
|
|
//Track Last vehicle stolen index
|
|
u16 CStatsMgr::sm_LastVehicleStolenSeed = 0;
|
|
u16 CStatsMgr::sm_LastVehicleStolenForChallenge = 0;
|
|
|
|
//Track killing spree
|
|
static const unsigned KILLSPREE_INTERVAL = 10*1000; //interval between accepting a kill as spree
|
|
u32 CStatsMgr::sm_KillingSpreeTime = 0;
|
|
u32 CStatsMgr::sm_KillingSpreeTotalTime = 0;
|
|
u32 CStatsMgr::sm_KillingSpreeCounter = 0;
|
|
|
|
//Track vehicles destroyed in spree
|
|
static const unsigned DESTROYED_VEHICLES_SPREE_INTERVAL = 10*1000; //interval between accepting a destroyed vehicle as spree
|
|
u32 CStatsMgr::sm_VehicleDestroyedSpreeTime = 0;
|
|
u32 CStatsMgr::sm_VehicleDestroyedSpreeTotalTime = 0;
|
|
u32 CStatsMgr::sm_VehicleDestroyedSpreeCounter = 0;
|
|
|
|
//Track vehicles damage
|
|
static const unsigned CRASHED_VEHICLE_INTERVAL = 3000; //interval between accepting a crash of the same vehicle as a new crash
|
|
u32 CStatsMgr::sm_CrashedVehicleKeyHash = 0;
|
|
u32 CStatsMgr::sm_CrashedVehicleTimer = 0;
|
|
float CStatsMgr::sm_CrashedVehicleDamageAccumulator = 0.0f;
|
|
|
|
//Track vehicle models driven
|
|
atArray < u32 > CStatsMgr::sm_SpVehicleDriven;
|
|
atArray < u32 > CStatsMgr::sm_MpVehicleDriven;
|
|
u32 CStatsMgr::sm_currCountAsFacebookDriven = 0;
|
|
u32 CStatsMgr::sm_maxCountAsFacebookDriven = 0;
|
|
|
|
//Work out player vehicle stats
|
|
Vector3 CStatsMgr::sm_VehiclePreviousPosition;
|
|
u32 CStatsMgr::sm_CarTwoWheelCounter = 0;
|
|
float CStatsMgr::sm_CarTwoWheelDist = 0.0f;
|
|
float CStatsMgr::sm_CarLess3WheelCounter = 0.0f;
|
|
u32 CStatsMgr::sm_BikeRearWheelCounter = 0;
|
|
float CStatsMgr::sm_BikeRearWheelDist = 0.0f;
|
|
bool CStatsMgr::sm_IsWheelingOnPushBike = false;
|
|
bool CStatsMgr::sm_IsDoingAStoppieOnPushBike = false;
|
|
u32 CStatsMgr::sm_BikeFrontWheelCounter = 0;
|
|
float CStatsMgr::sm_BikeFrontWheelDist = 0.0f;
|
|
u32 CStatsMgr::sm_TempBufferCounter = 0;
|
|
u32 CStatsMgr::sm_BestCarTwoWheelsTimeMs = 0;
|
|
float CStatsMgr::sm_BestCarTwoWheelsDistM = 0.0f;
|
|
u32 CStatsMgr::sm_BestBikeWheelieTimeMs = 0;
|
|
float CStatsMgr::sm_BestBikeWheelieDistM = 0.0f;
|
|
u32 CStatsMgr::sm_BestBikeStoppieTimeMs = 0;
|
|
float CStatsMgr::sm_BestBikeStoppieDistM = 0.0f;
|
|
//Track vehicle air jumps
|
|
u32 CStatsMgr::sm_ZeroWheelCounter = 0;
|
|
Vector3 CStatsMgr::sm_ZeroWheelPosStart = Vector3(VEC3_ZERO);
|
|
Vector3 CStatsMgr::sm_ZeroWheelPosEnd = Vector3(VEC3_ZERO);
|
|
Vector3 CStatsMgr::sm_ZeroWheelPosHeight = Vector3(VEC3_ZERO);
|
|
u32 CStatsMgr::sm_ZeroWheelBufferCounter = 0;
|
|
|
|
//Track the parachute jump distance.
|
|
Vector3 CStatsMgr::sm_ParachuteStartPos = Vector3(VEC3_ZERO);
|
|
Vector3 CStatsMgr::sm_ParachuteDeployPos = Vector3(VEC3_ZERO);
|
|
u32 CStatsMgr::sm_ParachuteStartTime = 0;
|
|
bool CStatsMgr::sm_HasStartedParachuteDeploy = false;
|
|
bool CStatsMgr::sm_hasBailedFromParachuting = false;
|
|
|
|
//Track Longest time spent driving in cinematic camera
|
|
float CStatsMgr::sm_DriveInCinematic = 0.0f;
|
|
|
|
//Use an asynchronous probe helper to manage the probes used to determine the altitude of stats.
|
|
static CAsyncProbeHelper s_GroundProbeHelper;
|
|
CAsyncProbeHelper& CStatsMgr::sm_GroundProbeHelper(s_GroundProbeHelper);
|
|
u32 CStatsMgr::sm_TimeSinceLastGroundProbe = 0;
|
|
|
|
//Used to track number of vehicle flips.
|
|
bool CStatsMgr::sm_VehicleFlipsCounter = false;
|
|
u32 CStatsMgr::sm_VehicleFlipsAccumulator = 0;
|
|
float CStatsMgr::sm_VehicleFlipsHeadingLast = 0.0f;
|
|
|
|
//Used to track number of vehicle spins.
|
|
float CStatsMgr::sm_VehicleSpinsAccumulator = 0.0f;
|
|
float CStatsMgr::sm_VehicleSpinsHeadingLast = 0.0f;
|
|
|
|
//Used to track number of vehicle rotations.
|
|
float CStatsMgr::sm_VehicleRollsAccumulator = 0.0f;
|
|
float CStatsMgr::sm_VehicleRollsHeadingLast = 0.0f;
|
|
|
|
//Used to track good plane landings
|
|
u32 CStatsMgr::sm_InAirTimer = 0;
|
|
int CStatsMgr::sm_LandingTimer = 0;
|
|
|
|
//TRUE when the player has cheated recently
|
|
static const u32 CHEAT_REST_TIME = 2*1000; //After 2 seconds the cheat is reset
|
|
bool CStatsMgr::sm_cheatIsActive = false;
|
|
u32 CStatsMgr::sm_cheatIsActiveTimer = 0;
|
|
|
|
float CStatsMgr::sm_hydraulicJumpVehicleAltitude = 0.0f;
|
|
|
|
//TRUE when we want to check for leaderboard flush after game load
|
|
static bool s_writeLeaderboardOnboot = false;
|
|
|
|
// Stat recording for the challenges
|
|
CStatsMgr::RecordStat CStatsMgr::m_recordedStat = CStatsMgr::REC_STAT_NONE;
|
|
float CStatsMgr::m_recordedStatValue = (float)(0x7F800001); // Nan
|
|
CStatsMgr::RecordStatPolicy CStatsMgr::m_statRecordingPolicy = CStatsMgr::RSP_NONE;
|
|
|
|
Vector3 CStatsMgr::sm_ChallengeZeroWheelPosStart = Vector3(VEC3_ZERO);
|
|
Vector3 CStatsMgr::sm_ReverseDrivingPosCurrent = Vector3(VEC3_ZERO);
|
|
float CStatsMgr::sm_CurrentDrivingReverseDistance = 0.0f;
|
|
bool CStatsMgr::sm_invalidJump = false;
|
|
|
|
float CStatsMgr::sm_SkydiveDistance = 0.0f;
|
|
float CStatsMgr::sm_FreeFallAltitude = 0.0f;
|
|
|
|
// On foot altitude
|
|
Vector3 CStatsMgr::sm_OnFootPosStart = Vector3(VEC3_ZERO);
|
|
float CStatsMgr::sm_FootAltitude = 0.0f;
|
|
|
|
// Plane challenges
|
|
float CStatsMgr::sm_FlyingAltitude = 0.0f;
|
|
bool CStatsMgr::sm_ValidFlyingAltitude = false;
|
|
float CStatsMgr::sm_FlyingCounter = 0.0f;
|
|
float CStatsMgr::sm_FlyingDistance = 0.0f;
|
|
float CStatsMgr::sm_PlaneBarrelRollCounter = 0.0f;
|
|
float CStatsMgr::sm_PlaneRollAccumulator = 0.0f;
|
|
float CStatsMgr::sm_PlaneRollLast = 0.0f;
|
|
|
|
bool CStatsMgr::sm_drivingFlyingVehicle = false;
|
|
bool CStatsMgr::sm_drivingFlyingVehicleForProbe = false;
|
|
|
|
Vector3 CStatsMgr::sm_FlyingPlanePosHeight = Vector3(VEC3_ZERO);
|
|
Vector3 CStatsMgr::sm_LastFlyingPlanePosition = Vector3(VEC3_ZERO);
|
|
|
|
|
|
// Vehicle bail
|
|
Vector3 CStatsMgr::sm_StartVehicleBail = Vector3(VEC3_ZERO);
|
|
Vector3 CStatsMgr::sm_LastVehicleBailPosition = Vector3(VEC3_ZERO);
|
|
float CStatsMgr::sm_VehicleBailDistance = 0.0f;
|
|
|
|
float CStatsMgr::sm_PlayersVehiclesDamages = 0.0f;
|
|
|
|
// Boat on ground
|
|
Vector3 CStatsMgr::sm_LastBoatPositionInWater = Vector3(VEC3_ZERO);
|
|
Vector3 CStatsMgr::sm_BoatPositionOnGround = Vector3(VEC3_ZERO);
|
|
float CStatsMgr::sm_BoatDistanceOnGround = 0.0f;
|
|
|
|
bool CStatsMgr::sm_wasInWaterPreviously = false;
|
|
bool CStatsMgr::sm_outOfWater = false;
|
|
float CStatsMgr::sm_inAirAfterGroundTimer = 0.0f;
|
|
|
|
bool CStatsMgr::sm_HadAttachedParentVehicle = false;
|
|
|
|
float CStatsMgr::sm_FootOnVehicleTimer=0.0f;
|
|
|
|
u32 CStatsMgr::sm_TimerAfterOnAnotherVehicle = 0;
|
|
bool CStatsMgr::sm_IsOnAnotherVehicle = false;
|
|
bool CStatsMgr::sm_WasOnAnotherVehicleWhenJumped = false;
|
|
|
|
u32 CStatsMgr::sm_MostRecentCollisionTimeWithPlaneInAir = 0;
|
|
|
|
Vector3 CStatsMgr::sm_LastWallridePosition = Vector3(VEC3_ZERO);
|
|
float CStatsMgr::sm_WallrideDistance = 0.0f;
|
|
float CStatsMgr::sm_WallrideBufferTimer = 0.0f;
|
|
const float CStatsMgr::WALLRIDE_BUFFERTIMER_VALUE = 0.2f; // Time buffer when we consider that you're wallriding even if there's smalls bumps
|
|
|
|
|
|
static bool s_isTrackingStunts = false;
|
|
|
|
// Timer started after landing a flip. We want to wait a few ms before sending the event in case we crash right after landing.
|
|
static float s_delayedFlipCount = 0.0f;
|
|
static bool s_landedFlip = false;
|
|
|
|
// B*2850786 - Types of stunts tracked for Stunt DLC
|
|
enum StuntType
|
|
{
|
|
ST_FRONTFLIP,
|
|
ST_BACKFLIP,
|
|
ST_SPIN,
|
|
ST_WHEELIE,
|
|
ST_STOPPIE,
|
|
ST_BOWLING_PIN,
|
|
ST_FOOTBALL,
|
|
ST_ROLL
|
|
};
|
|
|
|
void SendStuntEvent(StuntType type, float stuntValue)
|
|
{
|
|
if(s_isTrackingStunts)
|
|
{
|
|
statDebugf3("Send stunt event : type = %d, value = %f", (int) type, stuntValue);
|
|
GetEventScriptNetworkGroup()->Add(CEventNetworkStuntPerformed((int)type, stuntValue));
|
|
}
|
|
}
|
|
|
|
// --- CStatsMgr ----------------------------------------------------------------
|
|
|
|
bool IS_LOCAL_PLAYER_DRIVER_OR_LAST_DRIVER(CVehicle* vehicle)
|
|
{
|
|
bool driverIsLocalPlayer = false;
|
|
|
|
if (vehicle)
|
|
{
|
|
if (!vehicle->GetDriver())
|
|
{
|
|
if (vehicle->GetSeatManager())
|
|
{
|
|
CPed* lastDriver = vehicle->GetSeatManager()->GetLastPedInSeat(0);
|
|
driverIsLocalPlayer = lastDriver && lastDriver->IsLocalPlayer();
|
|
|
|
//Vehicle is being toad
|
|
if (driverIsLocalPlayer)
|
|
{
|
|
CVehicle* pAttachParentVehicle = vehicle->GetAttachParentVehicle();
|
|
if (pAttachParentVehicle && pAttachParentVehicle->GetDriver() && pAttachParentVehicle->GetDriver()->IsLocalPlayer())
|
|
{
|
|
driverIsLocalPlayer = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
driverIsLocalPlayer = vehicle->GetDriver()->IsLocalPlayer();
|
|
}
|
|
}
|
|
|
|
return driverIsLocalPlayer;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::WriteAfterGameLoad()
|
|
{
|
|
s_writeLeaderboardOnboot = true;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::Init(unsigned initMode)
|
|
{
|
|
USE_MEMBUCKET(MEMBUCKET_SCRIPT);
|
|
|
|
if (initMode == INIT_CORE)
|
|
{
|
|
if (!CashPackInfo::IsInstantiated())
|
|
{
|
|
CashPackInfo::Instantiate();
|
|
}
|
|
}
|
|
else if (initMode == INIT_SESSION)
|
|
{
|
|
LoadMetadataFile();
|
|
|
|
sm_VehiclePreviousPosition.Zero();
|
|
|
|
sm_vehicleRecords.Reset();
|
|
sm_curVehIdx = CStatsMgr::INVALID_RECORD;
|
|
|
|
sm_PedsKilledOfThisType.clear();
|
|
sm_PedsKilledOfThisType.Reset();
|
|
|
|
sm_PedsKilledOfThisType.Resize(PEDTYPE_LAST_PEDTYPE);
|
|
for (s32 i=0; i<sm_PedsKilledOfThisType.GetCount(); i++)
|
|
{
|
|
sm_PedsKilledOfThisType[i] = 0;
|
|
}
|
|
|
|
sm_PedsRunDownRecords.clear();
|
|
sm_PedsRunDownRecords.Reset();
|
|
sm_PedsRunDownRecords.Resize(NUM_PEDS_TO_TRACK);
|
|
for (s32 i=0; i<sm_PedsRunDownRecords.GetCount(); i++)
|
|
{
|
|
sm_PedsRunDownRecords[i] = 0;
|
|
}
|
|
|
|
sm_SpVehicleDriven.clear();
|
|
sm_SpVehicleDriven.Reset();
|
|
sm_MpVehicleDriven.clear();
|
|
sm_MpVehicleDriven.Reset();
|
|
sm_currCountAsFacebookDriven = 0;
|
|
|
|
sm_WheelStatus.clear();
|
|
sm_WheelStatus.Reset();
|
|
|
|
sm_SessionRunningTime = 0.0f;
|
|
sm_PlayingTime = 0.0f;
|
|
sm_StealthTime = 0.0f;
|
|
sm_FirstPersonTime = 0.0f;
|
|
sm_3rdPersonTime = 0.0f;
|
|
|
|
sm_WasInVehicle = false;
|
|
|
|
sm_ParachuteStartTime = 0;
|
|
sm_ParachuteStartPos.Zero();
|
|
sm_ParachuteDeployPos.Zero();
|
|
sm_HasStartedParachuteDeploy = false;
|
|
|
|
sm_WasInAir = false;
|
|
sm_FreeFallStartPos.Zero();
|
|
sm_ChallengeSkydiveStartPos.Zero();
|
|
sm_FreeFallDueToPlayerDamage = false;
|
|
sm_FreeFallStartTimer = 0;
|
|
sm_OnFootPosStart.Zero();
|
|
sm_FootAltitude = 0.0f;
|
|
sm_FlyingCounter = 0.0f;
|
|
sm_FlyingDistance = 0.0f;
|
|
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
ClearPlayerVehicleStats();
|
|
|
|
sm_VehicleDeadCheckCounter = 0.0f;
|
|
sm_VehicleDeadCheck = false;
|
|
|
|
SetCheatIsActive( false );
|
|
|
|
sm_TimeSinceLastGroundProbe = 0;
|
|
|
|
CStatsUtils::ResetStatsTracking();
|
|
m_recordedStat = REC_STAT_NONE;
|
|
m_statRecordingPolicy = RSP_NONE;
|
|
ResetRecordingStats();
|
|
sm_MostRecentCollisionTimeWithPlaneInAir = 0;
|
|
|
|
sm_LastWallridePosition.Zero();
|
|
sm_WallrideDistance = 0.0f;
|
|
}
|
|
|
|
sm_StatsData.Init(initMode);
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::LoadMetadataFile()
|
|
{
|
|
bool done = false;
|
|
|
|
char metadataFilename[RAGE_MAX_PATH];
|
|
metadataFilename[0] = '\0';
|
|
|
|
//Try to load "statsmgr.pso" file.
|
|
const CDataFileMgr::DataFile* pData = DATAFILEMGR.GetFirstFile(CDataFileMgr::STATS_METADATA_PSO_FILE);
|
|
if(pData && DATAFILEMGR.IsValid(pData))
|
|
{
|
|
formatf(metadataFilename, RAGE_MAX_PATH, "%s.%s", pData->m_filename, META_FILE_EXT);
|
|
|
|
psoFile * pPsoFile = psoLoadFile(metadataFilename, PSOLOAD_PREP_FOR_PARSER_LOADING, atFixedBitSet32().Set(psoFile::REQUIRE_CHECKSUM));
|
|
if(pPsoFile)
|
|
{
|
|
done = psoLoadObject(*pPsoFile, sm_TuneMetadata);
|
|
delete pPsoFile;
|
|
}
|
|
}
|
|
|
|
statAssertf(done, "Error loading %s", metadataFilename);
|
|
|
|
return done;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::OpenNetwork()
|
|
{
|
|
for (int i=0; i<sm_vehicleRecords.GetCount(); i++)
|
|
sm_vehicleRecords[i].Clear(true);
|
|
sm_vehicleRecords.Resize(0);
|
|
sm_curVehIdx = CStatsMgr::INVALID_RECORD;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::CloseNetwork()
|
|
{
|
|
for (int i=0; i<sm_vehicleRecords.GetCount(); i++)
|
|
sm_vehicleRecords[i].Clear(true);
|
|
sm_vehicleRecords.Resize(0);
|
|
sm_curVehIdx = CStatsMgr::INVALID_RECORD;
|
|
|
|
// Stop the recording
|
|
m_recordedStat = REC_STAT_NONE;
|
|
m_statRecordingPolicy = RSP_NONE;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::Shutdown(unsigned shutdownMode)
|
|
{
|
|
if (shutdownMode == SHUTDOWN_CORE)
|
|
{
|
|
CashPackInfo::GetInstance().Shutdown();
|
|
}
|
|
else if (shutdownMode == SHUTDOWN_SESSION)
|
|
{
|
|
sm_vehicleRecords.Reset();
|
|
sm_curVehIdx = CStatsMgr::INVALID_RECORD;
|
|
|
|
sm_PedsKilledOfThisType.clear();
|
|
sm_PedsKilledOfThisType.Reset();
|
|
|
|
sm_WheelStatus.clear();
|
|
sm_WheelStatus.Reset();
|
|
|
|
sm_PedsRunDownRecords.clear();
|
|
sm_PedsRunDownRecords.Reset();
|
|
|
|
sm_SpVehicleDriven.clear();
|
|
sm_SpVehicleDriven.Reset();
|
|
sm_MpVehicleDriven.clear();
|
|
sm_MpVehicleDriven.Reset();
|
|
sm_currCountAsFacebookDriven = 0;
|
|
|
|
sm_PlayingTime = 0.0f;
|
|
sm_StealthTime = 0.0f;
|
|
sm_FirstPersonTime = 0.0f;
|
|
sm_3rdPersonTime = 0.0f;
|
|
sm_SessionRunningTime = 0.0f;
|
|
sm_NearMissNoCrash = 0;
|
|
sm_DriveInCinematic = 0.0f;
|
|
sm_JumpDistance=0.0f;
|
|
sm_OnFootPosStart.Zero();
|
|
sm_FootAltitude = 0.0f;
|
|
sm_FlyingCounter=0.0f;
|
|
sm_FlyingDistance = 0.0f;
|
|
sm_HadAttachedParentVehicle=false;
|
|
sm_NearMissNoCrashPrecise = 0;
|
|
ClearNearMissArray();
|
|
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
ClearPlayerVehicleStats();
|
|
|
|
sm_VehicleDeadCheckCounter = 0.0f;
|
|
sm_VehicleDeadCheck = false;
|
|
|
|
sm_CrashedVehicleTimer = 0;
|
|
sm_CrashedVehicleKeyHash = 0;
|
|
sm_CrashedVehicleDamageAccumulator = 0.0f;
|
|
|
|
SetCheatIsActive( false );
|
|
|
|
CStatsUtils::ResetStatsTracking();
|
|
sm_CurrentSpeed = 0.0f;
|
|
|
|
sm_LastWallridePosition.Zero();
|
|
sm_WallrideDistance = 0.0f;
|
|
}
|
|
|
|
sm_StatsData.Shutdown(shutdownMode);
|
|
}
|
|
|
|
u32
|
|
CStatsMgr::FindVehicleRecord( const u32 hashKey )
|
|
{
|
|
u32 idx = INVALID_RECORD;
|
|
|
|
const bool isOnline = NetworkInterface::IsGameInProgress();
|
|
u32 charId = StatsInterface::GetStatsModelPrefix();
|
|
static u32 s_charId = StatsInterface::GetStatsModelPrefix();
|
|
|
|
for (int i=0; i<sm_vehicleRecords.GetCount() && idx==CStatsMgr::INVALID_RECORD; i++)
|
|
{
|
|
if (sm_vehicleRecords[i].m_VehicleId == hashKey)
|
|
{
|
|
if (sm_vehicleRecords[i].m_CharacterId == charId)
|
|
{
|
|
if (sm_vehicleRecords[i].m_Online == isOnline)
|
|
{
|
|
idx = (u32)i;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
statDebugf3("FindVehicleRecord - Vehicle id='%d' found but online flag='%s' doesnt match.", hashKey, sm_vehicleRecords[i].m_Online ? "true":"false");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
statDebugf3("FindVehicleRecord - Vehicle id='%d' found but character id='%d : %d' doesnt match.", hashKey, sm_vehicleRecords[i].m_CharacterId, charId);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Lookup using previous character ID.
|
|
if (idx == INVALID_RECORD && s_charId != charId)
|
|
{
|
|
charId = s_charId;
|
|
s_charId = StatsInterface::GetStatsModelPrefix();
|
|
|
|
for (int i=0; i<sm_vehicleRecords.GetCount() && idx==CStatsMgr::INVALID_RECORD; i++)
|
|
{
|
|
if (sm_vehicleRecords[i].m_VehicleId == hashKey)
|
|
{
|
|
if (sm_vehicleRecords[i].m_CharacterId == charId)
|
|
{
|
|
if (sm_vehicleRecords[i].m_Online == isOnline)
|
|
{
|
|
idx = (u32)i;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
statDebugf3("FindVehicleRecord - Vehicle id='%d' found but online flag='%s' doesnt match.", hashKey, sm_vehicleRecords[i].m_Online ? "true":"false");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
statDebugf3("FindVehicleRecord - Vehicle id='%d' found but character id='%d : %d' doesnt match.", hashKey, sm_vehicleRecords[i].m_CharacterId, charId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
void CStatsVehicleUsage::Clear(const bool clearlastrecords /*= true*/)
|
|
{
|
|
if ( clearlastrecords )
|
|
{
|
|
EndVehicleStats( );
|
|
m_Online = false;
|
|
m_LastTimeSpentInVehicle = 0;
|
|
m_LastDistDriven = 0.0f;
|
|
m_HasSetNumDriven = false;
|
|
}
|
|
|
|
statDebugf3("Clear VehicleRecord %u", m_VehicleId);
|
|
|
|
m_VehicleId = 0;
|
|
m_CharacterId = CHAR_INVALID;
|
|
m_TimeDriven = 0;
|
|
m_DistDriven = 0.0f;
|
|
m_NumDriven = 0;
|
|
m_NumStolen = 0;
|
|
m_NumSpins = 0;
|
|
m_NumFlips = 0;
|
|
m_NumPlaneLandings = 0;
|
|
m_NumWheelies = 0;
|
|
m_NumAirLaunches = 0;
|
|
m_NumAirLaunchesOver5s = 0;
|
|
m_NumAirLaunchesOver5m = 0;
|
|
m_NumAirLaunchesOver40m = 0;
|
|
m_NumLargeAccidents = 0;
|
|
m_TimeSpentInVehicle = 0;
|
|
m_HighestSpeed = 0.0f;
|
|
m_LongestWheelieTime = 0;
|
|
m_LongestWheelieDist = 0.0f;
|
|
m_HighestJumpDistance = 0.0f;
|
|
m_NumPedsRundown = 0;
|
|
m_Location = 0;
|
|
m_Owned = false;
|
|
}
|
|
|
|
void
|
|
CStatsVehicleUsage::EndVehicleStats( )
|
|
{
|
|
const u32 timespentinvehicle = (m_LastTimeSpentInVehicle>0) ? sysTimer::GetSystemMsTime() - m_LastTimeSpentInVehicle : 0;
|
|
|
|
|
|
//Set metric with distance driven.
|
|
if (IsValid() && m_LastDistDriven>=VEHICLE_MIN_DRIVE_DIST)
|
|
{
|
|
CNetworkTelemetry::PlayerVehicleDistance(m_VehicleId, m_LastDistDriven, timespentinvehicle, CUserDisplay::AreaName.GetName(), CUserDisplay::DistrictName.GetName(), CUserDisplay::StreetName.GetName(), m_Owned);
|
|
}
|
|
|
|
if (m_LastTimeSpentInVehicle>0)
|
|
{
|
|
m_TimeSpentInVehicle += timespentinvehicle;
|
|
m_LastTimeSpentInVehicle = 0;
|
|
}
|
|
|
|
m_LastDistDriven = 0.0f;
|
|
m_HasSetNumDriven = false;
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::CanUpdateVehicleRecord(const u32 hashKey)
|
|
{
|
|
if (sm_curVehIdx >= INVALID_RECORD)
|
|
return false;
|
|
|
|
if (hashKey != sm_vehicleRecords[sm_curVehIdx].m_VehicleId)
|
|
return false;
|
|
|
|
if (StatsInterface::GetStatsModelPrefix() != sm_vehicleRecords[sm_curVehIdx].m_CharacterId)
|
|
return false;
|
|
|
|
if (!statVerify(sm_vehicleRecords[sm_curVehIdx].IsValid()))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::StartNewVehicleRecords(const u32 hashKey)
|
|
{
|
|
if (sm_vehicleRecords.GetCount() >= CStatsMgr::NUM_VEHICLE_RECORDS)
|
|
{
|
|
statWarningf("Force Flush Vehicle leaderboard data due to array being full.");
|
|
CheckWriteVehicleRecords( true );
|
|
}
|
|
|
|
//alredy tracking this vehicle hash key
|
|
if (CanUpdateVehicleRecord( hashKey ))
|
|
return;
|
|
|
|
sm_curVehIdx = CStatsMgr::INVALID_RECORD;
|
|
|
|
const bool isOnline = NetworkInterface::IsGameInProgress();
|
|
const u32 charId = StatsInterface::GetStatsModelPrefix();
|
|
|
|
for (int i=0; i<sm_vehicleRecords.GetCount() && sm_curVehIdx==CStatsMgr::INVALID_RECORD; i++)
|
|
{
|
|
if (sm_vehicleRecords[i].m_VehicleId == hashKey && sm_vehicleRecords[i].m_CharacterId == charId && sm_vehicleRecords[i].m_Online == isOnline)
|
|
{
|
|
sm_curVehIdx = (u32)i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sm_curVehIdx == CStatsMgr::INVALID_RECORD && statVerify(sm_vehicleRecords.GetCount()<CStatsMgr::NUM_VEHICLE_RECORDS))
|
|
{
|
|
const int index = sm_vehicleRecords.GetCount();
|
|
sm_vehicleRecords.Insert(index);
|
|
sm_vehicleRecords[index].Clear( true );
|
|
sm_vehicleRecords[index].m_VehicleId = hashKey;
|
|
sm_vehicleRecords[index].m_Online = NetworkInterface::IsGameInProgress() ? true : false;
|
|
sm_vehicleRecords[index].m_CharacterId = StatsInterface::GetStatsModelPrefix();
|
|
sm_curVehIdx = (u32)index;
|
|
statDebugf3("StartNewVehicleRecords - %u", hashKey);
|
|
}
|
|
|
|
statAssert(CanUpdateVehicleRecord(hashKey) );
|
|
}
|
|
|
|
void
|
|
CStatsMgr::CheckWriteVehicleRecords(const bool bforce)
|
|
{
|
|
bool shouldStartFlush = (bforce || sm_vehicleRecords.GetCount() >= CStatsMgr::NUM_VEHICLE_RECORDS);
|
|
|
|
static StatId STAT_MP_VEHICLE_RECORD_FLUSH = StatId("MP_VEHICLE_RECORD_FLUSH");
|
|
static StatId STAT_SP_VEHICLE_RECORD_FLUSH = StatId("SP_VEHICLE_RECORD_FLUSH");
|
|
u64 timepassed = 0;
|
|
u64 lastFlushTime = 0;
|
|
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
timepassed = StatsInterface::GetUInt64Stat(STAT_MP_PLAYING_TIME);
|
|
lastFlushTime = StatsInterface::GetUInt64Stat(STAT_MP_VEHICLE_RECORD_FLUSH);
|
|
}
|
|
else
|
|
{
|
|
timepassed = StatsInterface::GetUInt64Stat(STAT_PLAYING_TIME);
|
|
lastFlushTime = StatsInterface::GetUInt64Stat(STAT_SP_VEHICLE_RECORD_FLUSH);
|
|
}
|
|
|
|
if (!shouldStartFlush)
|
|
{
|
|
const u64 defaultInterval = (u64)StatsInterface::GetVehicleLeaderboardWriteInterval();
|
|
|
|
if ((lastFlushTime+defaultInterval) <= timepassed)
|
|
{
|
|
shouldStartFlush = true;
|
|
|
|
//Is a UGC activity in progress
|
|
if (CNetworkTelemetry::IsActivityInProgress())
|
|
shouldStartFlush = false;
|
|
}
|
|
}
|
|
|
|
if(shouldStartFlush)
|
|
{
|
|
statDebugf3("CheckWriteVehicleRecords - Flush vehicle records");
|
|
|
|
if (NetworkInterface::IsGameInProgress())
|
|
StatsInterface::SetStatData(STAT_MP_VEHICLE_RECORD_FLUSH, timepassed, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
else
|
|
StatsInterface::SetStatData(STAT_SP_VEHICLE_RECORD_FLUSH, timepassed, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
|
|
if (sm_curVehIdx<sm_vehicleRecords.GetCount() && sm_vehicleRecords[sm_curVehIdx].IsValid())
|
|
{
|
|
CStatsVehicleUsage currRecord;
|
|
currRecord = sm_vehicleRecords[sm_curVehIdx];
|
|
|
|
for (int i=0; i<sm_vehicleRecords.GetCount(); i++)
|
|
sm_vehicleRecords[i].Clear( true );
|
|
|
|
sm_vehicleRecords.Resize(1);
|
|
sm_curVehIdx = 0;
|
|
sm_vehicleRecords[sm_curVehIdx].m_VehicleId = currRecord.m_VehicleId;
|
|
sm_vehicleRecords[sm_curVehIdx].m_CharacterId = currRecord.m_CharacterId;
|
|
sm_vehicleRecords[sm_curVehIdx].m_Online = currRecord.m_Online;
|
|
sm_vehicleRecords[sm_curVehIdx].m_Owned = currRecord.m_Owned;
|
|
sm_vehicleRecords[sm_curVehIdx].m_Location = currRecord.m_Location;
|
|
}
|
|
else
|
|
{
|
|
for (int i=0; i<sm_vehicleRecords.GetCount(); i++)
|
|
sm_vehicleRecords[i].Clear( true );
|
|
|
|
sm_curVehIdx = CStatsMgr::INVALID_RECORD;
|
|
sm_vehicleRecords.Resize(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::IncrementCRCNumChecks(bool bCheckIssuedFromLocal)
|
|
{
|
|
const u32 STAT_NUM_CHECKS_ISSUED_CURR = ATSTRINGHASH("NUM_CHECKS_ISSUED_CURR", 0x8F1AAA90);
|
|
const u32 STAT_NUM_CHECKS_RCVD_CURR = ATSTRINGHASH("NUM_CHECKS_RCVD_CURR", 0xF134773E);
|
|
|
|
if(bCheckIssuedFromLocal)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_NUM_CHECKS_ISSUED_CURR, 1);
|
|
}
|
|
else
|
|
{
|
|
StatsInterface::IncrementStat(STAT_NUM_CHECKS_RCVD_CURR, 1);
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::ResetCRCStats()
|
|
{
|
|
// Reset all last window stats
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_MISM_PERC, 0.0f);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_ISSUED, 0);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_RCVD, 0);
|
|
|
|
// Set current window stats to either 0, or either some magic numbers so this guy can get detected earlier when back from the cheater pool
|
|
int numChecksDoneReset, numChecksMismReset, numChecksIssuedReset, numChecksRcvdReset = 0;
|
|
|
|
// By default we reset to get out of the pool as a clean player, but we can&should change that from the cloud
|
|
::Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NUM_CHECKS_DONE_RESET", 0xb329b3c8), numChecksDoneReset);
|
|
::Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NUM_CHECKS_MISM_CURR_RESET", 0x748ac144), numChecksMismReset);
|
|
::Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NUM_CHECKS_ISSUED_CURR_RESET", 0xeea84f34), numChecksIssuedReset);
|
|
::Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NUM_CHECKS_RCVD_CURR_RESET", 0x458318a2), numChecksRcvdReset);
|
|
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_DONE, (u32)numChecksDoneReset);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_MISM_CURR, (u32)numChecksMismReset);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_ISSUED_CURR, (u32)numChecksIssuedReset);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_RCVD_CURR, (u32)numChecksRcvdReset);
|
|
}
|
|
|
|
void CStatsMgr::UpdateCRCStats(bool bWasAMismatch)
|
|
{
|
|
if( CNetwork::IsNetworkSessionValid() )
|
|
{
|
|
static u64 lastCheckedSessionId = rlSession::INVALID_SESSION_ID;
|
|
static u64 lastReportedSessionId = rlSession::INVALID_SESSION_ID;
|
|
|
|
const u64 currentSessionId = CNetwork::GetNetworkSession().GetSnSession().GetSessionId();
|
|
|
|
if( bWasAMismatch && lastReportedSessionId != currentSessionId )
|
|
{
|
|
StatsInterface::IncrementStat(STAT_NUM_CHECKS_MISM_CURR, 1);
|
|
lastReportedSessionId = currentSessionId;
|
|
}
|
|
|
|
if(lastCheckedSessionId != currentSessionId)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_NUM_CHECKS_DONE, 1);
|
|
lastCheckedSessionId = currentSessionId;
|
|
|
|
// Do rolling if we already tracked more than X sessions, so we can detect a player who only switched to hacker mode lately or for short periods
|
|
int NUM_CHECKS_DONE_FOR_ROLLING_STAT = 100;
|
|
|
|
::Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NUM_CHECKS_DONE_FOR_ROLLING_STAT", 0xc1184646), NUM_CHECKS_DONE_FOR_ROLLING_STAT);
|
|
|
|
const u32 uNumChecksDone = StatsInterface::GetUInt32Stat(STAT_NUM_CHECKS_DONE);
|
|
|
|
if( uNumChecksDone >= NUM_CHECKS_DONE_FOR_ROLLING_STAT )
|
|
{
|
|
// If the tunable decreased drastically since last time we played (checks > 1.25x), forget about this tracking; otherwise we'll receive lots of false positives
|
|
if( uNumChecksDone <= ( NUM_CHECKS_DONE_FOR_ROLLING_STAT + (NUM_CHECKS_DONE_FOR_ROLLING_STAT>>2) ) )
|
|
{
|
|
const float fMismatchPerc = (((float)StatsInterface::GetUInt32Stat(STAT_NUM_CHECKS_MISM_CURR))/(float)NUM_CHECKS_DONE_FOR_ROLLING_STAT)*100.0f;
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_MISM_PERC, fMismatchPerc);
|
|
}
|
|
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_DONE, 0);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_MISM_CURR, 0);
|
|
|
|
const u32 uNumChecksIssuedInThisTimeWindow = StatsInterface::GetUInt32Stat(STAT_NUM_CHECKS_ISSUED_CURR);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_ISSUED, uNumChecksIssuedInThisTimeWindow);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_ISSUED_CURR, 0);
|
|
|
|
const u32 uNumChecksReceivedInThisTimeWindow = StatsInterface::GetUInt32Stat(STAT_NUM_CHECKS_RCVD_CURR);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_RCVD, uNumChecksReceivedInThisTimeWindow);
|
|
StatsInterface::SetStatData(STAT_NUM_CHECKS_RCVD_CURR, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::SignedOut()
|
|
{
|
|
sm_StatsData.SignedOut();
|
|
}
|
|
|
|
void
|
|
CStatsMgr::SignedOffline()
|
|
{
|
|
sm_StatsData.SignedOffline();
|
|
}
|
|
|
|
void
|
|
CStatsMgr::SignedIn()
|
|
{
|
|
sm_StatsData.SignedIn();
|
|
}
|
|
|
|
#if __BANK
|
|
void
|
|
CStatsMgr::InitWidgets()
|
|
{
|
|
sm_StatsData.Bank_InitWidgets();
|
|
}
|
|
#endif
|
|
|
|
void
|
|
CStatsMgr::DisplayScriptStatUpdateMessage(bool UNUSED_PARAM(IncOrDec), const StatId& keyHash, float UNUSED_PARAM(fValue))
|
|
{
|
|
// we dont want to overright a stat message for the standard script stat message....
|
|
if (StatsInterface::GetBooleanStat(STAT_MSG_BEING_DISPLAYED.GetHash()))
|
|
{
|
|
StatsInterface::SetStatData(STAT_MSG_BEING_DISPLAYED.GetHash(), false, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
return;
|
|
}
|
|
|
|
if (CStatsUtils::IsStatCapped(keyHash))
|
|
{
|
|
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_FLOAT) && StatsInterface::GetFloatStat(keyHash) >= STAT_LIMIT)
|
|
{
|
|
return;
|
|
}
|
|
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT) && StatsInterface::GetIntStat(keyHash) >= STAT_LIMIT)
|
|
{
|
|
return;
|
|
}
|
|
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT8) && StatsInterface::GetUInt8Stat(keyHash) >= STAT_LIMIT)
|
|
{
|
|
return;
|
|
}
|
|
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT16) && StatsInterface::GetUInt16Stat(keyHash) >= STAT_LIMIT)
|
|
{
|
|
return;
|
|
}
|
|
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT32) && StatsInterface::GetUInt32Stat(keyHash) >= STAT_LIMIT)
|
|
{
|
|
return;
|
|
}
|
|
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_UINT64) && StatsInterface::GetUInt64Stat(keyHash) >= STAT_LIMIT)
|
|
{
|
|
return;
|
|
}
|
|
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT64) && StatsInterface::GetInt64Stat(keyHash) >= STAT_LIMIT)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --- Modify Stats ---------------------------------------------
|
|
|
|
void
|
|
CStatsMgr::IncrementStat(const StatId& keyHash, const float fAmount, const u32 flags)
|
|
{
|
|
if (!CStatsUtils::CanUpdateStats() || !StatsInterface::IsKeyValid(keyHash) || 0.0f >= fAmount)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (StatsInterface::GetStatType(keyHash))
|
|
{
|
|
case STAT_TYPE_INT:
|
|
{
|
|
int iNewVal = static_cast<int>(fAmount);
|
|
iNewVal = iNewVal + StatsInterface::GetIntStat(keyHash);
|
|
if (CStatsUtils::IsStatCapped(keyHash))
|
|
{
|
|
iNewVal = (iNewVal > STAT_LIMIT) ? (int) STAT_LIMIT : iNewVal;
|
|
}
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(int), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_INT64:
|
|
{
|
|
s64 iNewVal = static_cast<s64>(fAmount);
|
|
iNewVal = iNewVal + StatsInterface::GetInt64Stat(keyHash);
|
|
if (CStatsUtils::IsStatCapped(keyHash))
|
|
{
|
|
iNewVal = (iNewVal > STAT_LIMIT) ? (int) STAT_LIMIT : iNewVal;
|
|
}
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(s64), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_FLOAT:
|
|
{
|
|
float fNewVal = fAmount + StatsInterface::GetFloatStat(keyHash);
|
|
if (CStatsUtils::IsStatCapped(keyHash))
|
|
{
|
|
fNewVal = rage::Min(fNewVal, STAT_LIMIT);
|
|
}
|
|
StatsInterface::SetStatData(keyHash, &fNewVal, sizeof(float), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_UINT8:
|
|
{
|
|
statAssertf(fAmount >= 0.0f, "Setting a negative value in a unsigned stat - type \"%d\" for stat \"%s\"", StatsInterface::GetStatType(keyHash), StatsInterface::GetKeyName(keyHash));
|
|
u8 iNewVal = static_cast<u8>(fAmount);
|
|
iNewVal = iNewVal + StatsInterface::GetUInt8Stat(keyHash);
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(u8), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_UINT16:
|
|
{
|
|
statAssertf(fAmount >= 0.0f, "Setting a negative value in a unsigned stat - type \"%d\" for stat \"%s\"", StatsInterface::GetStatType(keyHash), StatsInterface::GetKeyName(keyHash));
|
|
u16 iNewVal = static_cast<u16>(fAmount);
|
|
iNewVal = iNewVal + StatsInterface::GetUInt16Stat(keyHash);
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(u16), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_UINT32:
|
|
{
|
|
statAssertf(fAmount >= 0.0f, "Setting a negative value in a unsigned stat - type \"%d\" for stat \"%s\"", StatsInterface::GetStatType(keyHash), StatsInterface::GetKeyName(keyHash));
|
|
u32 iNewVal = static_cast<u32>(fAmount);
|
|
iNewVal = iNewVal + StatsInterface::GetUInt32Stat(keyHash);
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(u32), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_TIME:
|
|
case STAT_TYPE_UINT64:
|
|
{
|
|
statAssertf(fAmount >= 0.0f, "Setting a negative value in a unsigned stat - type \"%d\" for stat \"%s\"", StatsInterface::GetStatType(keyHash), StatsInterface::GetKeyName(keyHash));
|
|
u64 iNewVal = static_cast<u64>(fAmount);
|
|
iNewVal = iNewVal + StatsInterface::GetUInt64Stat(keyHash);
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(u64), flags);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
statErrorf("Invalid tag type \"%d\" for stat \"%s\"", StatsInterface::GetStatType(keyHash), StatsInterface::GetKeyName(keyHash));
|
|
break;
|
|
}
|
|
|
|
//Update accuracy.
|
|
if (keyHash == STAT_SHOTS || keyHash == STAT_HITS_PEDS_VEHICLES)
|
|
{
|
|
if (StatsInterface::IsKeyValid(STAT_WEAPON_ACCURACY))
|
|
{
|
|
float hits = (float)StatsInterface::GetIntStat(STAT_HITS_PEDS_VEHICLES);
|
|
if(hits>0.0f)
|
|
{
|
|
float shots = (float)StatsInterface::GetIntStat(STAT_SHOTS);
|
|
if(shots==0.0f)
|
|
shots=1.0f;
|
|
float accuracy = (hits*100.0f)/shots;
|
|
StatsInterface::SetStatData(STAT_WEAPON_ACCURACY.GetStatId(), accuracy, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
CStatsMgr::DecrementStat(const StatId& keyHash, const float fAmount, const u32 flags)
|
|
{
|
|
if (!CStatsUtils::CanUpdateStats() || !statVerifyf(StatsInterface::IsKeyValid(keyHash), "Invalid key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (StatsInterface::GetStatType(keyHash))
|
|
{
|
|
case STAT_TYPE_INT:
|
|
{
|
|
int iVal = static_cast<int>(fAmount);
|
|
iVal = Max(StatsInterface::GetIntStat(keyHash) - iVal, 0);
|
|
StatsInterface::SetStatData(keyHash, &iVal, sizeof(int), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_INT64:
|
|
{
|
|
s64 iOldVal = StatsInterface::GetInt64Stat(keyHash);
|
|
s64 iNewVal = static_cast<s64>(fAmount);
|
|
iNewVal = (iOldVal < iNewVal) ? 0 : iOldVal - iNewVal;
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(s64), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_FLOAT:
|
|
{
|
|
float fVal = Max(StatsInterface::GetFloatStat(keyHash) - fAmount, 0.0f);
|
|
StatsInterface::SetStatData(keyHash, &fVal, sizeof(float), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_UINT8:
|
|
{
|
|
u8 iOldVal = StatsInterface::GetUInt8Stat(keyHash);
|
|
u8 iNewVal = static_cast<u8>(fAmount);
|
|
iNewVal = (iOldVal < iNewVal) ? 0 : iOldVal - iNewVal;
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(u8), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_UINT16:
|
|
{
|
|
u16 iOldVal = StatsInterface::GetUInt16Stat(keyHash);
|
|
u16 iNewVal = static_cast<u16>(fAmount);
|
|
iNewVal = (iOldVal < iNewVal) ? 0 : iOldVal - iNewVal;
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(u16), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_UINT32:
|
|
{
|
|
u32 iOldVal = StatsInterface::GetUInt32Stat(keyHash);
|
|
u32 iNewVal = static_cast<u32>(fAmount);
|
|
iNewVal = (iOldVal < iNewVal) ? 0 : iOldVal - iNewVal;
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(u32), flags);
|
|
}
|
|
break;
|
|
case STAT_TYPE_TIME:
|
|
case STAT_TYPE_UINT64:
|
|
{
|
|
u64 iOldVal = StatsInterface::GetUInt64Stat(keyHash);
|
|
u64 iNewVal = static_cast<u64>(fAmount);
|
|
iNewVal = (iOldVal < iNewVal) ? 0 : iOldVal - iNewVal;
|
|
StatsInterface::SetStatData(keyHash, &iNewVal, sizeof(u64), flags);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
statErrorf("Invalid tag type \"%d\" for stat \"%s\"", StatsInterface::GetStatType(keyHash), StatsInterface::GetKeyName(keyHash));
|
|
break;
|
|
}
|
|
|
|
//Update accuracy.
|
|
if (keyHash == STAT_SHOTS || keyHash == STAT_HITS_PEDS_VEHICLES)
|
|
{
|
|
if (StatsInterface::IsKeyValid(STAT_WEAPON_ACCURACY))
|
|
{
|
|
float hits = (float)StatsInterface::GetIntStat(STAT_HITS_PEDS_VEHICLES);
|
|
if(hits>0.0f)
|
|
{
|
|
float shots = (float)StatsInterface::GetIntStat(STAT_SHOTS);
|
|
if(shots==0.0f)
|
|
shots=1.0f;
|
|
float accuracy = (hits*100.0f)/shots;
|
|
StatsInterface::SetStatData(STAT_WEAPON_ACCURACY.GetStatId(), accuracy, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::SetGreater(const StatId& keyHash, const float fNewAmount, const u32 flags)
|
|
{
|
|
if (!CStatsUtils::CanUpdateStats()
|
|
|| !statVerifyf(StatsInterface::IsKeyValid(keyHash), "Invalid Stat key=\"%s\" hash=\"%d\"", keyHash.GetName(), keyHash.GetHash())
|
|
|| 0.0f >= fNewAmount)
|
|
return;
|
|
|
|
const StatType iType = StatsInterface::GetStatType(keyHash);
|
|
switch (iType)
|
|
{
|
|
case STAT_TYPE_INT:
|
|
{
|
|
const int iNewVal = static_cast<int>(fNewAmount);
|
|
if(iNewVal > StatsInterface::GetIntStat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags);
|
|
}
|
|
}
|
|
break;
|
|
case STAT_TYPE_INT64:
|
|
{
|
|
const s64 iNewVal = static_cast<s64>(fNewAmount);
|
|
if(iNewVal > StatsInterface::GetInt64Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STAT_TYPE_FLOAT:
|
|
if(fNewAmount > StatsInterface::GetFloatStat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, fNewAmount, flags);
|
|
}
|
|
break;
|
|
|
|
case STAT_TYPE_UINT8:
|
|
{
|
|
const u8 iNewVal = static_cast<u8>(fNewAmount);
|
|
if(iNewVal > StatsInterface::GetUInt8Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STAT_TYPE_UINT16:
|
|
{
|
|
const u16 iNewVal = static_cast<u16>(fNewAmount);
|
|
if (iNewVal > StatsInterface::GetUInt16Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STAT_TYPE_UINT32:
|
|
{
|
|
const u32 iNewVal = static_cast<u32>(fNewAmount);
|
|
if (iNewVal > StatsInterface::GetUInt32Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags);
|
|
}
|
|
}
|
|
break;
|
|
case STAT_TYPE_TIME:
|
|
case STAT_TYPE_UINT64:
|
|
{
|
|
const u64 iNewVal = static_cast<u64>(fNewAmount);
|
|
if (iNewVal > StatsInterface::GetUInt64Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
statErrorf("Invalid tag type \"%d\" for stat \"%s\"", iType, StatsInterface::GetKeyName(keyHash));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::SetLesser(const StatId& keyHash, const float fNewAmount, const u32 flags)
|
|
{
|
|
if (!CStatsUtils::CanUpdateStats() || !statVerify(StatsInterface::IsKeyValid(keyHash)))
|
|
return;
|
|
|
|
const StatType iType = StatsInterface::GetStatType(keyHash);
|
|
switch (iType)
|
|
{
|
|
case STAT_TYPE_INT:
|
|
{
|
|
const int iNewVal = static_cast<int>(fNewAmount);
|
|
if(iNewVal < StatsInterface::GetIntStat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags) ;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STAT_TYPE_INT64:
|
|
{
|
|
const s64 iNewVal = static_cast<s64>(fNewAmount);
|
|
if(iNewVal < StatsInterface::GetInt64Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags) ;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STAT_TYPE_FLOAT:
|
|
if(fNewAmount < StatsInterface::GetFloatStat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, fNewAmount, flags);
|
|
}
|
|
break;
|
|
|
|
case STAT_TYPE_UINT8:
|
|
{
|
|
const u8 iNewVal = static_cast<u8>(fNewAmount);
|
|
if(iNewVal < StatsInterface::GetUInt8Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STAT_TYPE_UINT16:
|
|
{
|
|
const u16 iNewVal = static_cast<u16>(fNewAmount);
|
|
if (iNewVal < StatsInterface::GetUInt16Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STAT_TYPE_UINT32:
|
|
{
|
|
const u32 iNewVal = static_cast<u32>(fNewAmount);
|
|
if (iNewVal < StatsInterface::GetUInt32Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal, flags);
|
|
}
|
|
}
|
|
break;
|
|
case STAT_TYPE_TIME:
|
|
case STAT_TYPE_UINT64:
|
|
{
|
|
const u64 iNewVal = static_cast<u64>(fNewAmount);
|
|
if (iNewVal < StatsInterface::GetUInt64Stat(keyHash))
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewVal);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
statErrorf("Invalid tag type \"%d\" for stat \"%s\"", iType, StatsInterface::GetKeyName(keyHash));
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::IsFirstPersonShooterCamera(const bool vehicleCheck)
|
|
{
|
|
if (camInterface::IsRenderingFirstPersonShooterCamera())
|
|
return true;
|
|
|
|
if (vehicleCheck)
|
|
{
|
|
const camBaseCamera* dominantCam = camInterface::GetDominantRenderedCamera();
|
|
if(dominantCam && dominantCam->GetIsClassId(camCinematicMountedCamera::GetStaticClassId()))
|
|
{
|
|
// All bonnet and pov vehicle cameras are valid.
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateTimersInStartMenu()
|
|
{
|
|
static float s_timeInStartMenu = 0;
|
|
static bool s_startMenuOpen = false;
|
|
static bool s_isInMP = false;
|
|
|
|
if (NetworkInterface::IsGameInProgress()
|
|
&& NetworkInterface::IsInFreeMode()
|
|
&& CPauseMenu::IsActive( PM_WaitUntilFullyFadedInOnly )
|
|
&& CPauseMenu::GetCurrentMenuVersion() == FE_MENU_VERSION_MP_PAUSE)
|
|
{
|
|
s_startMenuOpen = true;
|
|
s_timeInStartMenu += fwTimer::GetSystemTimeStep() * 1000.0f;
|
|
}
|
|
else if (s_startMenuOpen)
|
|
{
|
|
s_startMenuOpen = false;
|
|
|
|
if (!s_isInMP && NetworkInterface::IsGameInProgress() && StatsInterface::GetStatsPlayerModelIsMultiplayer())
|
|
{
|
|
s_isInMP = true;
|
|
s_timeInStartMenu = 0.0f;
|
|
}
|
|
else if (s_isInMP && !NetworkInterface::IsNetworkOpen() && StatsInterface::GetStatsPlayerModelIsSingleplayer())
|
|
{
|
|
s_isInMP = false;
|
|
s_timeInStartMenu = 0.0f;
|
|
}
|
|
|
|
if (s_timeInStartMenu > 0.0f)
|
|
{
|
|
if ((StatsInterface::GetStatsPlayerModelIsMultiplayer() && StatsInterface::CloudFileLoadPending(STAT_MP_CATEGORY_DEFAULT))
|
|
|| StatsInterface::GetStatsPlayerModelIsSingleplayer())
|
|
{
|
|
static StatId s_stattime("TOTAL_STARTMENU_TIME");
|
|
StatsInterface::IncrementStat(s_stattime, s_timeInStartMenu, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
|
|
s_timeInStartMenu = 0.0f;
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::Update()
|
|
{
|
|
PF_START_TIMEBAR("stats mgr");
|
|
|
|
if (sm_StatsData.GetCount() == 0)
|
|
return;
|
|
|
|
CPed* playerPed = FindPlayerPed();
|
|
if (!playerPed)
|
|
return;
|
|
|
|
CPlayerInfo* pi = playerPed->GetPlayerInfo();
|
|
if (!pi)
|
|
return;
|
|
|
|
//Alternative update for Director mode and other situations where stats tracking is not enabled.
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
{
|
|
//Stats Data Manager update
|
|
sm_StatsData.Update();
|
|
|
|
CashPackInfo::GetInstance().Update();
|
|
|
|
return;
|
|
}
|
|
|
|
UpdateTimersInStartMenu( );
|
|
|
|
static bool s_updatingStats = true;
|
|
|
|
const bool gameIsPaused = fwTimer::IsGamePaused();
|
|
const bool playerStatePlaying = (pi->GetPlayerState() == CPlayerInfo::PLAYERSTATE_PLAYING);
|
|
|
|
if (!gameIsPaused && playerStatePlaying)
|
|
{
|
|
s_updatingStats = true;
|
|
|
|
//If the network is open then make sure the match has started.
|
|
const bool setPlayingTime = ( (NetworkInterface::IsGameInProgress() && NetworkInterface::IsLocalPlayerOnline()) || !NetworkInterface::IsNetworkOpen() );
|
|
|
|
if (setPlayingTime && StatsInterface::GetStatsPlayerModelValid())
|
|
{
|
|
if (s_isInSP && NetworkInterface::IsGameInProgress() && StatsInterface::GetStatsPlayerModelIsMultiplayer())
|
|
{
|
|
statDebugf1("Detected transition to multiplayer, clearing timers.");
|
|
s_isInSP = false;
|
|
sm_FirstPersonTime = 0.0f;
|
|
sm_3rdPersonTime = 0.0f;
|
|
sm_PlayingTime = 0.0f;
|
|
sm_SessionRunningTime = 0.0f;
|
|
sm_StealthTime = 0.0f;
|
|
}
|
|
else if (!s_isInSP && !NetworkInterface::IsNetworkOpen() && StatsInterface::GetStatsPlayerModelIsSingleplayer())
|
|
{
|
|
statDebugf1("Detected transition to singleplayer, clearing timers.");
|
|
s_isInSP = true;
|
|
sm_FirstPersonTime = 0.0f;
|
|
sm_3rdPersonTime = 0.0f;
|
|
sm_PlayingTime = 0.0f;
|
|
sm_SessionRunningTime = 0.0f;
|
|
sm_StealthTime = 0.0f;
|
|
}
|
|
|
|
const float fTimeStepInMs = fwTimer::GetSystemTimeStep() * 1000.0f;
|
|
statAssertf(fTimeStepInMs >= 0.0f, "fTimeStepInMs is below zero");
|
|
|
|
if (fTimeStepInMs > 0.0f)
|
|
{
|
|
if (IsFirstPersonShooterCamera())
|
|
sm_FirstPersonTime += fTimeStepInMs;
|
|
else
|
|
sm_3rdPersonTime += fTimeStepInMs;
|
|
|
|
sm_PlayingTime += fTimeStepInMs;
|
|
sm_SessionRunningTime += fTimeStepInMs;
|
|
|
|
const CPedMotionData* md = playerPed->GetMotionData();
|
|
if (md && md->GetUsingStealth())
|
|
{
|
|
sm_StealthTime += fTimeStepInMs;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s_writeLeaderboardOnboot)
|
|
s_writeLeaderboardOnboot = !StatsInterface::TryVehicleLeaderboardWriteOnBoot( );
|
|
}
|
|
|
|
if(s_updatingStats)
|
|
{
|
|
if (gameIsPaused || sm_PlayingTime >= UPDATE_RATE_PLAYING_TIME || !playerStatePlaying)
|
|
{
|
|
ClearPlayingTimeStats( );
|
|
s_updatingStats = false;
|
|
}
|
|
}
|
|
|
|
//time in milliseconds
|
|
const unsigned curTime = fwTimer::GetTimeInMilliseconds_NonScaledClipped();
|
|
|
|
//Reset Killing spree time now and start counting
|
|
if(0 < sm_KillingSpreeTotalTime && (curTime - sm_KillingSpreeTime) > KILLSPREE_INTERVAL)
|
|
{
|
|
sm_KillingSpreeTime = 0;
|
|
sm_KillingSpreeTotalTime = 0;
|
|
sm_KillingSpreeCounter = 0;
|
|
}
|
|
|
|
//Reset Destroyed vehicles spree time now and start counting
|
|
if(0 < sm_VehicleDestroyedSpreeTotalTime && (curTime - sm_VehicleDestroyedSpreeTime) > DESTROYED_VEHICLES_SPREE_INTERVAL)
|
|
{
|
|
sm_VehicleDestroyedSpreeTime = 0;
|
|
sm_VehicleDestroyedSpreeTotalTime = 0;
|
|
sm_VehicleDestroyedSpreeCounter = 0;
|
|
}
|
|
|
|
CVehicle* playerVehicle = playerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle) ? playerPed->GetMyVehicle() : NULL;
|
|
|
|
//Using a vehicle.
|
|
if (playerVehicle)
|
|
{
|
|
if(sm_WasInVehicle)
|
|
{
|
|
WorkOutPlayerVehicleStats(playerPed, playerVehicle);
|
|
}
|
|
else
|
|
{
|
|
sm_WasInVehicle = true;
|
|
sm_OnFootPosStart.Zero();
|
|
}
|
|
}
|
|
//Not using a vehicle.
|
|
else
|
|
{
|
|
EndVehicleStats();
|
|
|
|
if (IsPlayerActive())
|
|
{
|
|
ProcessOnFootVehicleChecks(playerPed);
|
|
|
|
if (!sm_cheatIsActive && !IsOnTrain() && !IsInsideTrain() && !IsOnBoat() && !IsOnCar() && MIN_SPEED_TO_CONSIDER_MOVEMENT < playerPed->GetVelocity().Mag2())
|
|
{
|
|
if (!sm_VehiclePreviousPosition.IsZero())
|
|
{
|
|
sm_VehiclePreviousPosition.Zero();
|
|
}
|
|
|
|
//-----------------------------
|
|
//----- SWIMMING
|
|
//-----------------------------
|
|
if (playerPed->GetIsSwimming())
|
|
{
|
|
//Checks if there is movement on the pad as if bobbing about on water, it increments this stat...
|
|
const CControl* control = playerPed->GetControlFromPlayer();
|
|
if (control && (control->GetPedWalkLeftRight().GetNorm() || control->GetPedWalkUpDown().GetNorm() || control->GetPedSprintIsDown()))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_DIST_SWIMMING.GetStatId(), playerPed->GetDistanceTravelled(), STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::IncrementStat(STAT_TIME_SWIMMING.GetStatId(), fwTimer::GetSystemTimeStep()*1000.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
// Stop bail out of vehicle when in water
|
|
if(!sm_StartVehicleBail.IsZero())
|
|
{
|
|
statDebugf3("Stop Bailing out, player is swimming");
|
|
sm_StartVehicleBail.Zero();
|
|
sm_LastVehicleBailPosition.Zero();
|
|
sm_VehicleBailDistance=0.0f;
|
|
}
|
|
}
|
|
|
|
//-----------------------------
|
|
//----- FREE FALLING
|
|
//-----------------------------
|
|
else if (playerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsInTheAir))
|
|
{
|
|
if (0 == sm_ParachuteStartTime && playerPed->GetPedResetFlag(CPED_RESET_FLAG_IsParachuting))
|
|
{
|
|
sm_ParachuteStartTime = fwTimer::GetSystemTimeInMilliseconds();
|
|
sm_ParachuteStartPos = VEC3V_TO_VECTOR3(playerPed->GetTransform().GetPosition());
|
|
statDebugf3("[Parachute] Start parachuting ParachuteStartPos <x=%f,y=%f,z=%f> ", sm_ParachuteStartPos.x, sm_ParachuteStartPos.y, sm_ParachuteStartPos.z);
|
|
}
|
|
|
|
if (!sm_WasInAir && playerPed->GetPedResetFlag(CPED_RESET_FLAG_IsFalling))
|
|
{
|
|
Vec3V coords = playerPed->GetTransform().GetPosition();
|
|
|
|
if (coords.GetZf() > sm_FreeFallStartPos.z)
|
|
{
|
|
const float groundZ = CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(coords.GetXf(), coords.GetYf());
|
|
const float altitude = coords.GetZf() - groundZ;
|
|
|
|
if (altitude > sm_TuneMetadata.m_FreefallThresold)
|
|
{
|
|
sm_WasInAir = true;
|
|
sm_FreeFallStartPos = VEC3V_TO_VECTOR3(coords);
|
|
statDebugf3("Start Freefall Position <x=%f,y=%f,z=%f> altitude=<%f>", sm_FreeFallStartPos.x, sm_FreeFallStartPos.y, sm_FreeFallStartPos.z, altitude);
|
|
|
|
sm_FreeFallStartTimer = fwTimer::GetTimeInMilliseconds();
|
|
sm_FreeFallDueToPlayerDamage = false;
|
|
|
|
if (playerPed->GetWeaponDamageEntity())
|
|
{
|
|
const u32 timeSinceLastDamage = fwTimer::GetTimeInMilliseconds() - playerPed->GetWeaponDamagedTime();
|
|
if (timeSinceLastDamage <= sm_TuneMetadata.m_FreefallTimeSinceLastDamage)
|
|
{
|
|
sm_FreeFallDueToPlayerDamage = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!sm_FreeFallStartPos.IsZero())
|
|
{
|
|
Vec3V coords = playerPed->GetTransform().GetPosition();
|
|
//Altitude from freefall start
|
|
if(coords.GetZf() > CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(coords.GetXf(), coords.GetYf()))
|
|
{
|
|
sm_FreeFallAltitude = sm_FreeFallStartPos.z - coords.GetZf();
|
|
}
|
|
}
|
|
|
|
if(!sm_ChallengeSkydiveStartPos.IsZero() && sm_ParachuteDeployPos.IsZero())
|
|
{
|
|
Vec3V coords = playerPed->GetTransform().GetPosition();
|
|
|
|
// If player is higher than they were when jump started, update the jump start position.
|
|
// This can happen when player is jumping from heli that moves upward
|
|
if(VEC3V_TO_VECTOR3(coords).GetZ() > sm_ChallengeSkydiveStartPos.GetZ())
|
|
{
|
|
sm_ChallengeSkydiveStartPos = VEC3V_TO_VECTOR3(coords);
|
|
sm_SkydiveDistance = 0.0f;
|
|
}
|
|
|
|
//Altitude from skydive start
|
|
if(coords.GetZf() > CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(coords.GetXf(), coords.GetYf()))
|
|
{
|
|
sm_SkydiveDistance = sm_ChallengeSkydiveStartPos.z - coords.GetZf();
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------
|
|
//----- ON FOOT
|
|
//-----------------------------
|
|
else if (!sm_WasInAir && playerPed->GetIsOnFoot() && playerPed->GetIsStanding() && playerPed->GetWasStanding())
|
|
{
|
|
// Update the altitude
|
|
if (sm_OnFootPosStart.IsZero())
|
|
{
|
|
sm_OnFootPosStart = VEC3V_TO_VECTOR3(playerPed->GetTransform().GetPosition());
|
|
}
|
|
|
|
if(!sm_OnFootPosStart.IsZero())
|
|
{
|
|
float currentZf = playerPed->GetTransform().GetPosition().GetZf();
|
|
|
|
if (currentZf >= sm_OnFootPosStart.GetZ())
|
|
{
|
|
sm_FootAltitude = currentZf - sm_OnFootPosStart.GetZ();
|
|
UpdateRecordedStat(REC_STAT_ON_FOOT_ALTITUDE, sm_FootAltitude);
|
|
}
|
|
//else if((sm_OnFootPosStart.GetZ()-currentZf) > 1.0f)
|
|
//{
|
|
// sm_OnFootPosStart.Zero();
|
|
//}
|
|
}
|
|
|
|
if(!sm_StartVehicleBail.IsZero())
|
|
{
|
|
statDebugf3("Stop Bailing out, player is standing");
|
|
UpdateRecordedStat(REC_STAT_DIST_BAILING_FROM_VEHICLE, sm_VehicleBailDistance);
|
|
sm_StartVehicleBail.Zero();
|
|
sm_LastVehicleBailPosition.Zero();
|
|
sm_VehicleBailDistance = 0.0f;
|
|
}
|
|
|
|
if (!CScriptPeds::IsPedStopped(playerPed))
|
|
{
|
|
const CPedMotionData* md = playerPed->GetMotionData();
|
|
if(md && !md->GetIsStill())
|
|
{
|
|
//Total Distance travelled
|
|
if (NetworkInterface::IsGameInProgress() && StatsInterface::IsKeyValid(STAT_MP_CHAR_DIST_TRAVELLED))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_MP_CHAR_DIST_TRAVELLED, playerPed->GetDistanceTravelled(), STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
//distance running
|
|
if(StatsInterface::IsKeyValid(STAT_DIST_RUNNING.GetHash()) && (md->GetIsRunning() || md->GetIsSprinting()))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_DIST_RUNNING.GetStatId(), playerPed->GetDistanceTravelled(), STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
else
|
|
{
|
|
//distance walked
|
|
StatsInterface::IncrementStat(STAT_DIST_WALKING.GetStatId(), playerPed->GetDistanceTravelled(), STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::IncrementStat(STAT_TIME_WALKING.GetStatId(), fwTimer::GetSystemTimeStep()*1000.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
|
|
//distance walked in stealth
|
|
if(md->GetUsingStealth() && StatsInterface::IsKeyValid(STAT_DIST_WALKING_STEALTH.GetHash()))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_DIST_WALKING_STEALTH.GetStatId(), playerPed->GetDistanceTravelled(), STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Bailing out of the vehicle
|
|
if(!sm_StartVehicleBail.IsZero())
|
|
{
|
|
Vec3V currentPosition = playerPed->GetTransform().GetPosition();
|
|
sm_VehicleBailDistance += Mag(currentPosition - VECTOR3_TO_VEC3V(sm_LastVehicleBailPosition)).Getf();
|
|
sm_LastVehicleBailPosition = VEC3V_TO_VECTOR3(currentPosition);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
const CCollisionRecord *pCollRecord = playerPed->GetFrameCollisionHistory()->GetMostSignificantCollisionRecordOfType(ENTITY_TYPE_VEHICLE);
|
|
if (pCollRecord && pCollRecord->m_pRegdCollisionEntity.Get())
|
|
{
|
|
CEntity* entity = pCollRecord->m_pRegdCollisionEntity.Get();
|
|
if(entity && entity->GetIsTypeVehicle())
|
|
{
|
|
CVehicle* vehicle = static_cast<CVehicle*>(entity);
|
|
if(vehicle->IsInAir())
|
|
{
|
|
sm_MostRecentCollisionTimeWithPlaneInAir = playerPed->GetFrameCollisionHistory()->GetMostRecentCollisionTime();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!playerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsInTheAir)
|
|
&& !playerPed->GetPedResetFlag(CPED_RESET_FLAG_IsParachuting))
|
|
{
|
|
static const u32 TIME_AFTER_COLLISION_WITH_PLANE = 1000;
|
|
if(fwTimer::GetTimeInMilliseconds() > sm_MostRecentCollisionTimeWithPlaneInAir + TIME_AFTER_COLLISION_WITH_PLANE)
|
|
{
|
|
CheckFreeFallResults(playerPed);
|
|
}
|
|
|
|
if(!playerPed->GetPedResetFlag(CPED_RESET_FLAG_IsFalling))
|
|
CheckParachuteResults(playerPed);
|
|
}
|
|
}
|
|
|
|
//Updates player in cover and crouch stats
|
|
UpdatePlayerInCoverCrouchStats();
|
|
|
|
//Stats Data Manager update
|
|
sm_StatsData.Update();
|
|
|
|
CashPackInfo::GetInstance().Update();
|
|
|
|
//Reset cheat status flag
|
|
if (sm_cheatIsActive)
|
|
{
|
|
if (0==sm_cheatIsActiveTimer || sm_cheatIsActiveTimer+CHEAT_REST_TIME <= fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
SetCheatIsActive( false );
|
|
}
|
|
}
|
|
|
|
u32 playerNearMissCounter = fwTimer::GetTimeInMilliseconds();
|
|
|
|
for(unsigned i = 0; i < MAXIMUM_NEAR_MISS_COUNTER; i++)
|
|
{
|
|
if(sm_NearMissCounterArray[i] != 0 && playerNearMissCounter > sm_NearMissCounterArray[i] + NEAR_MISS_TIMER)
|
|
{
|
|
sm_NearMissCounterArray[i] = 0;
|
|
|
|
if(!playerVehicle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
u32 mostRecentCollisionsTime = playerVehicle->GetFrameCollisionHistory()->GetMostRecentCollisionTime();
|
|
if ( mostRecentCollisionsTime == COLLISION_HISTORY_TIMER_NEVER_HIT
|
|
|| fwTimer::GetTimeInMilliseconds() > mostRecentCollisionsTime + NEAR_MISS_TIMER)
|
|
{
|
|
RegisterVehicleNearMissAccepted();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::VehicleHasBeenDriven(const u32 vehicleHash, const bool inMultiplayer)
|
|
{
|
|
bool result = false;
|
|
|
|
if (inMultiplayer)
|
|
{
|
|
for (int i=0; i<sm_MpVehicleDriven.GetCount() && !result; i++)
|
|
{
|
|
result = (vehicleHash == sm_MpVehicleDriven[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i=0; i<sm_SpVehicleDriven.GetCount() && !result; i++)
|
|
{
|
|
result = (vehicleHash == sm_SpVehicleDriven[i]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::SetMaxCountAsFacebookDriven(const u32 count)
|
|
{
|
|
if(0==sm_maxCountAsFacebookDriven)
|
|
{
|
|
sm_maxCountAsFacebookDriven = count;
|
|
|
|
int overridecount = -1;
|
|
if (PARAM_facebookvehiclesdriven.Get(overridecount))
|
|
{
|
|
if (overridecount > 0)
|
|
{
|
|
sm_maxCountAsFacebookDriven = (u32)overridecount;
|
|
statDebugf3("SetMaxCountAsFacebookDriven (overridecount) - %d", sm_maxCountAsFacebookDriven);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
statDebugf3("SetMaxCountAsFacebookDriven - %d", sm_maxCountAsFacebookDriven);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class MetricHourlyXP : public rlMetric
|
|
{
|
|
RL_DECLARE_METRIC(HOUR_XP, TELEMETRY_CHANNEL_AMBIENT, LOGLEVEL_LOW_PRIORITY);
|
|
|
|
public:
|
|
MetricHourlyXP(u32 xp, u32 playtime) : m_xp(xp), m_playtime(playtime) {};
|
|
|
|
virtual bool Write(RsonWriter* rw) const
|
|
{
|
|
bool result = true;
|
|
result = result && rw->WriteUns("xp", m_xp);
|
|
result = result && rw->WriteUns("pt", m_playtime);
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
u32 m_xp;
|
|
u32 m_playtime;
|
|
};
|
|
|
|
bool
|
|
CStatsMgr::PlayerCharIsValidAndPlayingFreemode()
|
|
{
|
|
return (!s_isInSP && NetworkInterface::IsGameInProgress() && StatsInterface::GetStatsPlayerModelIsMultiplayer());
|
|
}
|
|
|
|
void
|
|
CStatsMgr::ClearPlayingTimeStats()
|
|
{
|
|
if (sm_PlayingTime > 0.0f)
|
|
{
|
|
bool bTryToWriteVehicleStats = false;
|
|
|
|
if (!s_isInSP && NetworkInterface::IsGameInProgress() && StatsInterface::GetStatsPlayerModelIsMultiplayer())
|
|
{
|
|
if (!NetworkInterface::IsInSpectatorMode())
|
|
{
|
|
StatsInterface::IncrementStat(STAT_MP_FIRST_PERSON_CAM_TIME.GetHash(), sm_FirstPersonTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::IncrementStat(STAT_MP_THIRD_PERSON_CAM_TIME.GetHash(), sm_3rdPersonTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::IncrementStat(STAT_TOTAL_PLAYING_TIME.GetStatId(), sm_PlayingTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::SetGreater(STAT_LONGEST_PLAYING_TIME.GetStatId(), sm_SessionRunningTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::IncrementStat(STAT_MP_PLAYING_TIME.GetHash(), sm_PlayingTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
|
|
u64 average = (u64)StatsInterface::GetUInt64Stat(STAT_TOTAL_PLAYING_TIME);
|
|
|
|
if (StatsInterface::GetIntStat(STAT_NUMBER_OF_SESSIONS_FM) > 0 && average > 0)
|
|
{
|
|
average /= (u64)StatsInterface::GetIntStat(STAT_NUMBER_OF_SESSIONS_FM);
|
|
}
|
|
|
|
StatsInterface::SetStatData(STAT_AVERAGE_TIME_PER_SESSON, average);
|
|
|
|
if (StatsInterface::GetUInt64Stat(STAT_MP_PLAYING_TIME_NEW.GetHash()) == 0)
|
|
{
|
|
StatsInterface::SetStatData(STAT_MP_PLAYING_TIME_NEW.GetHash(), StatsInterface::GetUInt64Stat(STAT_MP_PLAYING_TIME.GetHash()), STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
else
|
|
{
|
|
StatsInterface::IncrementStat(STAT_MP_PLAYING_TIME_NEW.GetHash(), sm_PlayingTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
StatsInterface::IncrementStat(StatId("LEADERBOARD_PLAYING_TIME"), sm_PlayingTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
|
|
const u32 timepassed = StatsInterface::GetUInt32Stat(STAT_TOTAL_PLAYING_TIME.GetHash());
|
|
const s32 currHour = CStatsUtils::ConvertToHours(timepassed);
|
|
static s32 previousHour = currHour;
|
|
if (previousHour != currHour)
|
|
{
|
|
CNetworkTelemetry::AppendMetric(MetricHourlyXP((u32)StatsInterface::GetIntStat(STAT_CHAR_XP_FM.GetStatId()), timepassed));
|
|
previousHour = currHour;
|
|
}
|
|
|
|
::Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("MP_VEHICLE_LB_TRY_WRITE_ON_UPDATES", 0xd5a17534), bTryToWriteVehicleStats);
|
|
}
|
|
}
|
|
else if (s_isInSP && !NetworkInterface::IsNetworkOpen() && StatsInterface::GetStatsPlayerModelIsSingleplayer())
|
|
{
|
|
StatsInterface::IncrementStat(STAT_TOTAL_PLAYING_TIME.GetStatId(), sm_PlayingTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::SetGreater(STAT_LONGEST_PLAYING_TIME.GetStatId(), sm_SessionRunningTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::IncrementStat(STAT_PLAYING_TIME.GetHash(), sm_PlayingTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::IncrementStat(STAT_FIRST_PERSON_CAM_TIME.GetHash(), sm_FirstPersonTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::IncrementStat(STAT_THIRD_PERSON_CAM_TIME.GetHash(), sm_3rdPersonTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
|
|
::Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("SP_VEHICLE_LB_TRY_WRITE_ON_UPDATES", 0x662bc66c), bTryToWriteVehicleStats);
|
|
}
|
|
|
|
sm_3rdPersonTime = 0.0f;
|
|
sm_FirstPersonTime = 0.0f;
|
|
sm_PlayingTime = 0.0f;
|
|
|
|
// If this is set to true, system will write vehicle data every GetVehicleLeaderboardWriteInterval
|
|
if (bTryToWriteVehicleStats)
|
|
{
|
|
CheckWriteVehicleRecords( false );
|
|
}
|
|
}
|
|
|
|
if (sm_StealthTime > 0.0f)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_STEALTH_TIME.GetHash(), sm_StealthTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
sm_StealthTime = 0.0f;
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::SetVehicleHasBeenDriven(const u32 vehicleHash, const bool countAsFacebookDriven)
|
|
{
|
|
gnetAssert(!VehicleHasBeenDriven(vehicleHash, NetworkInterface::IsGameInProgress()));
|
|
|
|
if (!VehicleHasBeenDriven(vehicleHash, NetworkInterface::IsGameInProgress()))
|
|
{
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
sm_MpVehicleDriven.PushAndGrow(vehicleHash);
|
|
}
|
|
else
|
|
{
|
|
sm_SpVehicleDriven.PushAndGrow( vehicleHash );
|
|
|
|
if (countAsFacebookDriven)
|
|
{
|
|
++sm_currCountAsFacebookDriven;
|
|
|
|
#if !__NO_OUTPUT
|
|
fwModelId modelId;
|
|
CBaseModelInfo* mi = CModelInfo::GetBaseModelInfoFromHashKey(vehicleHash, &modelId);
|
|
if (mi)
|
|
{
|
|
statDebugf3("New Vehicle driven < %s >, current facebook count is < %d >.", mi->GetModelName(), sm_currCountAsFacebookDriven);
|
|
}
|
|
#endif // !__NO_OUTPUT
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdatePlayerInCoverCrouchStats( )
|
|
{
|
|
CPed* playerPed = FindPlayerPed();
|
|
if (!playerPed)
|
|
return;
|
|
|
|
if (!StatsInterface::IsKeyValid(STAT_TIME_IN_COVER))
|
|
return;
|
|
|
|
// assume not in cover
|
|
bool bIsInCover = false;
|
|
|
|
// Stat: TIME_IN_COVER
|
|
CPedIntelligence* pPI = playerPed->GetPedIntelligence();
|
|
if (pPI)
|
|
{
|
|
CQueriableInterface* pQI = pPI->GetQueriableInterface();
|
|
if (pQI && pQI->IsTaskCurrentlyRunning(CTaskTypes::TASK_IN_COVER))
|
|
{
|
|
if (StatsInterface::IsKeyValid(STAT_TIME_IN_COVER))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_TIME_IN_COVER.GetStatId(), fwTimer::GetSystemTimeStep()*1000.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
bIsInCover = true;
|
|
}
|
|
}
|
|
|
|
// Stat: ENTERED_COVER_AND_SHOT
|
|
if (sm_bWasInCover && !bIsInCover)
|
|
{
|
|
if (sm_bShotFiredInCover && StatsInterface::IsKeyValid(STAT_ENTERED_COVER_AND_SHOT))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_ENTERED_COVER_AND_SHOT.GetStatId(), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
// reset tracking variable
|
|
sm_bShotFiredInCover = false;
|
|
}
|
|
|
|
// Stat: ENTERED_COVER
|
|
if (bIsInCover && !sm_bWasInCover && StatsInterface::IsKeyValid(STAT_ENTERED_COVER))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_ENTERED_COVER.GetStatId(), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
// track player cover state
|
|
sm_bWasInCover = bIsInCover;
|
|
|
|
// crouch states
|
|
bool bIsCrouching = playerPed->GetIsCrouching();
|
|
|
|
// Stat: CROUCHED_AND_SHOT
|
|
if (sm_bWasCrouching && !bIsCrouching)
|
|
{
|
|
if (sm_bShotFiredInCrouch && StatsInterface::IsKeyValid(STAT_CROUCHED_AND_SHOT))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_CROUCHED_AND_SHOT.GetStatId(), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
// reset tracking variable
|
|
sm_bShotFiredInCrouch = false;
|
|
}
|
|
|
|
// Stat: CROUCHED
|
|
if (bIsCrouching && !sm_bWasCrouching && StatsInterface::IsKeyValid(STAT_CROUCHED))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_CROUCHED.GetStatId(), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
// track player crouch state
|
|
sm_bWasCrouching = bIsCrouching;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::RegisterMinBetterValue(const StatId& keyHash, int iNewTime, const u32 flags)
|
|
{
|
|
Assert(StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT) || StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_FLOAT));
|
|
|
|
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT) && StatsInterface::GetIntStat(keyHash) <= 0)
|
|
{
|
|
StatsInterface::SetStatData(keyHash, iNewTime, flags);
|
|
}
|
|
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_FLOAT) && StatsInterface::GetFloatStat(keyHash) <= 0.0f)
|
|
{
|
|
float fVal = static_cast<float>(iNewTime);
|
|
StatsInterface::SetStatData(keyHash, fVal, flags);
|
|
}
|
|
else
|
|
{
|
|
if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_INT) && StatsInterface::GetIntStat(keyHash) <= 0)
|
|
{
|
|
iNewTime = static_cast<int>(rage::Min((float)StatsInterface::GetIntStat(keyHash), (float)iNewTime));
|
|
StatsInterface::SetStatData(keyHash, iNewTime, flags);
|
|
}
|
|
else if (StatsInterface::GetIsBaseType(keyHash, STAT_TYPE_FLOAT) && StatsInterface::GetFloatStat(keyHash) <= 0.0f)
|
|
{
|
|
float fVal = rage::Min(StatsInterface::GetFloatStat(keyHash), (float)iNewTime);
|
|
StatsInterface::SetStatData(keyHash, fVal, flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::RegisterTyresPoppedByGunshot(u16 iRandomSeed, const u8 iWheel, const int iNumWheels)
|
|
{
|
|
if (IsPlayerActive() && sm_WheelStatus.GetCount()>0 && iRandomSeed == sm_VehicleRandomSeed)
|
|
{
|
|
if(iWheel<sm_WheelStatus.GetCount() && !sm_WheelStatus[iWheel])
|
|
{
|
|
sm_WheelStatus[iWheel] = true;
|
|
CStatsMgr::IncrementStat(STAT_TIRES_POPPED_BY_GUNSHOT.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sm_WheelStatus.Reset();
|
|
sm_WheelStatus.Resize(iNumWheels);
|
|
|
|
for (int i=0; i<sm_WheelStatus.GetCount(); i++)
|
|
sm_WheelStatus[i] = false;
|
|
|
|
sm_VehicleRandomSeed = iRandomSeed;
|
|
|
|
sm_WheelStatus[iWheel] = true;
|
|
CStatsMgr::IncrementStat(STAT_TIRES_POPPED_BY_GUNSHOT.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::IsPlayerActive()
|
|
{
|
|
static bool s_IsActive = false;
|
|
static u32 s_PreviousFrame = 0;
|
|
|
|
if (s_PreviousFrame != fwTimer::GetFrameCount())
|
|
{
|
|
CPlayerInfo* pi = CGameWorld::GetMainPlayerInfo();
|
|
|
|
if (!pi)
|
|
s_IsActive = false;
|
|
else if (NetworkInterface::IsInSinglePlayerPrivateSession())
|
|
s_IsActive = false;
|
|
else if (g_PlayerSwitch.IsActive())
|
|
s_IsActive = false;
|
|
else if (fwTimer::IsGamePaused())
|
|
s_IsActive = false;
|
|
else if (pi->GetPlayerState() != CPlayerInfo::PLAYERSTATE_PLAYING)
|
|
s_IsActive = false;
|
|
else if (!CGameLogic::IsGameStateInPlay())
|
|
s_IsActive = false;
|
|
else if (!camInterface::IsFadedIn())
|
|
s_IsActive = false;
|
|
else if (!NetworkInterface::IsGameInProgress() && (pi->IsControlsFrontendDisabled() || pi->IsControlsLoadingScreenDisabled()))
|
|
s_IsActive = false;
|
|
else
|
|
s_IsActive = true;
|
|
|
|
s_PreviousFrame = fwTimer::GetFrameCount();
|
|
}
|
|
|
|
return s_IsActive;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::WorkOutPlayerVehicleStats(CPed* playerPed, CVehicle* playerVehicle)
|
|
{
|
|
bool workOutPlayerStats = true;
|
|
|
|
if(!sm_HadAttachedParentVehicle)
|
|
{
|
|
sm_HadAttachedParentVehicle = playerVehicle->GetAttachParentVehicle()!=NULL;
|
|
}
|
|
else if (playerVehicle->HasContactWheels() && playerVehicle->GetAttachParentVehicle() == NULL)
|
|
{
|
|
sm_HadAttachedParentVehicle = false;
|
|
}
|
|
|
|
if (!statVerify(playerPed)
|
|
|| !statVerify(playerVehicle)
|
|
|| !CStatsMgr::IsPlayerActive()
|
|
|| !playerVehicle->m_nVehicleFlags.bIsThisADriveableCar
|
|
|| (!playerVehicle->m_nVehicleFlags.bEngineOn && !playerVehicle->InheritsFromPlane())
|
|
|| playerVehicle->GetAttachParentVehicle())
|
|
{
|
|
workOutPlayerStats = false;
|
|
|
|
if (!sm_ZeroWheelPosHeight.IsZero())
|
|
{
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
}
|
|
}
|
|
else if (playerVehicle->GetVelocity().Mag2() <= MIN_SPEED_TO_CONSIDER_MOVEMENT)
|
|
{
|
|
workOutPlayerStats = false;
|
|
}
|
|
else if (!(playerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle)) && CScriptPeds::IsPedStopped(playerPed))
|
|
{
|
|
workOutPlayerStats = false;
|
|
}
|
|
else if(playerVehicle->InheritsFromAutomobile())
|
|
{
|
|
// Parachuting with a vehicle makes it too easy to increase some stats, dont track stats at all when parachuting.
|
|
CAutomobile* automobile = static_cast<CAutomobile*>(playerVehicle);
|
|
if(automobile->IsParachuting())
|
|
{
|
|
workOutPlayerStats = CPhysics::ms_bInStuntMode;
|
|
}
|
|
}
|
|
|
|
CStatsMgr::CheckVehicleGroundProbeResults();
|
|
CStatsMgr::StartVehicleGroundProbe();
|
|
CStatsMgr::CheckCrashDamageAccumulator();
|
|
|
|
sm_CurrentSpeed = 0.0f;
|
|
|
|
// We want to update the hydraulics stats whether we're speeding or not
|
|
CStatsMgr::UpdateVehicleHydraulics(playerVehicle);
|
|
|
|
if (workOutPlayerStats)
|
|
{
|
|
CStatsMgr::UpdateVehicleStats(playerVehicle);
|
|
}
|
|
else
|
|
{
|
|
CStatsMgr::ClearPlayerVehicleStats();
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateVehicleWheelie(CVehicle* playerVehicle)
|
|
{
|
|
statAssert(playerVehicle);
|
|
if (!playerVehicle)
|
|
return;
|
|
else if (!playerVehicle->GetDriver())
|
|
return;
|
|
else if (!playerVehicle->GetDriver()->IsLocalPlayer())
|
|
return;
|
|
|
|
//CAR || BOAT
|
|
if(playerVehicle->InheritsFromAutomobile() && !playerVehicle->IsTrike() && !playerVehicle->InheritsFromQuadBike() )
|
|
{
|
|
CAutomobile* pCar = (CAutomobile *)playerVehicle;
|
|
|
|
if(pCar->GetNumContactWheels() < 3)
|
|
sm_CarLess3WheelCounter += fwTimer::GetSystemTimeStep() * 1000.0f;
|
|
else
|
|
sm_CarLess3WheelCounter = 0.0f;
|
|
|
|
// if both left wheels are OFF the ground
|
|
// remember to check wheel pointers exist!
|
|
// this stat is invalid for cars missing the corner wheels
|
|
const bool bHasAllWheels = pCar->GetWheelFromId(VEH_WHEEL_LF)
|
|
&& pCar->GetWheelFromId(VEH_WHEEL_RF)
|
|
&& pCar->GetWheelFromId(VEH_WHEEL_LR)
|
|
&& pCar->GetWheelFromId(VEH_WHEEL_RR);
|
|
|
|
if(bHasAllWheels && !pCar->GetWheelFromId(VEH_WHEEL_LF)->GetIsTouching() && !pCar->GetWheelFromId(VEH_WHEEL_LR)->GetIsTouching())
|
|
{
|
|
// if both right wheels are ON the ground
|
|
if(pCar->GetWheelFromId(VEH_WHEEL_RF)->GetIsTouching()
|
|
&& pCar->GetWheelFromId(VEH_WHEEL_RR)->GetIsTouching()
|
|
&& pCar->GetFrameCollisionHistory()->GetMaxCollisionImpulseMagLastFrame()==0.0f)
|
|
{
|
|
sm_CarTwoWheelCounter += (s32)fwTimer::GetSystemTimeStepInMilliseconds();
|
|
sm_CarTwoWheelDist += pCar->GetDistanceTravelled();
|
|
|
|
if(sm_TempBufferCounter > fwTimer::GetSystemTimeStepInMilliseconds() / 2)
|
|
sm_TempBufferCounter -= fwTimer::GetSystemTimeStepInMilliseconds() / 2;
|
|
else
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
else if(sm_CarTwoWheelCounter > 0 && sm_TempBufferCounter < CAR_TWOWHEEL_BUFFERLIMIT)
|
|
sm_TempBufferCounter += fwTimer::GetSystemTimeStepInMilliseconds();
|
|
else
|
|
{
|
|
if(sm_CarTwoWheelCounter >= CAR_TWOWHEEL_MINBEST)
|
|
{
|
|
sm_BestCarTwoWheelsTimeMs = sm_CarTwoWheelCounter;
|
|
sm_BestCarTwoWheelsDistM = sm_CarTwoWheelDist;
|
|
|
|
//Increment the stats:
|
|
StatsInterface::SetGreater(STAT_LONGEST_2WHEEL_TIME.GetStatId(), (float)sm_CarTwoWheelCounter, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::SetGreater(STAT_LONGEST_2WHEEL_DIST.GetStatId(), sm_CarTwoWheelDist, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
statDebugf3("Player 2-Wheels for %d ms and %f m\n", sm_BestCarTwoWheelsTimeMs, sm_BestCarTwoWheelsDistM);
|
|
|
|
const CVehicleModelInfo* vi = playerVehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ) && statVerify( CanUpdateVehicleRecord(vi->GetHashKey()) ))
|
|
{
|
|
++sm_vehicleRecords[sm_curVehIdx].m_NumWheelies;
|
|
|
|
if (sm_CarTwoWheelCounter > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime = sm_CarTwoWheelCounter;
|
|
|
|
if (sm_CarTwoWheelDist > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist = sm_CarTwoWheelDist;
|
|
}
|
|
}
|
|
|
|
// reset values
|
|
sm_CarTwoWheelCounter = 0;
|
|
sm_CarTwoWheelDist = 0.0f;
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
}
|
|
// if both right wheels are OFF the ground
|
|
else if(bHasAllWheels && !pCar->GetWheelFromId(VEH_WHEEL_RF)->GetIsTouching() && !pCar->GetWheelFromId(VEH_WHEEL_RR)->GetIsTouching())
|
|
{
|
|
// if both left wheels are ON the ground
|
|
if(pCar->GetWheelFromId(VEH_WHEEL_LF)->GetIsTouching()
|
|
&& pCar->GetWheelFromId(VEH_WHEEL_LR)->GetIsTouching()
|
|
&& pCar->GetFrameCollisionHistory()->GetMaxCollisionImpulseMagLastFrame()==0.0f)
|
|
{
|
|
sm_CarTwoWheelCounter += (s32)fwTimer::GetSystemTimeStepInMilliseconds();
|
|
sm_CarTwoWheelDist += pCar->GetDistanceTravelled();
|
|
|
|
if(sm_TempBufferCounter > fwTimer::GetSystemTimeStepInMilliseconds() / 2)
|
|
sm_TempBufferCounter -= fwTimer::GetSystemTimeStepInMilliseconds() / 2;
|
|
else
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
else if(sm_CarTwoWheelCounter > 0 && sm_TempBufferCounter < CAR_TWOWHEEL_BUFFERLIMIT)
|
|
sm_TempBufferCounter += fwTimer::GetSystemTimeStepInMilliseconds();
|
|
else
|
|
{
|
|
if(sm_CarTwoWheelCounter >= CAR_TWOWHEEL_MINBEST)
|
|
{
|
|
sm_BestCarTwoWheelsTimeMs = sm_CarTwoWheelCounter;
|
|
sm_BestCarTwoWheelsDistM = sm_CarTwoWheelDist;
|
|
|
|
//Increment the stats:
|
|
StatsInterface::SetGreater(STAT_LONGEST_2WHEEL_TIME.GetStatId(), (float)sm_CarTwoWheelCounter, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::SetGreater(STAT_LONGEST_2WHEEL_DIST.GetStatId(), sm_CarTwoWheelDist, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
statDebugf3("Player 2-Wheels for %d ms and %f m\n", sm_BestCarTwoWheelsTimeMs, sm_BestCarTwoWheelsDistM);
|
|
|
|
const CVehicleModelInfo* vi = playerVehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ) && statVerify( CanUpdateVehicleRecord(vi->GetHashKey()) ))
|
|
{
|
|
++sm_vehicleRecords[sm_curVehIdx].m_NumWheelies;
|
|
|
|
if (sm_CarTwoWheelCounter > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime = sm_CarTwoWheelCounter;
|
|
|
|
if (sm_CarTwoWheelDist > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist = sm_CarTwoWheelDist;
|
|
}
|
|
}
|
|
|
|
// reset values
|
|
sm_CarTwoWheelCounter = 0;
|
|
sm_CarTwoWheelDist = 0.0f;
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
}
|
|
else if(sm_CarTwoWheelCounter > 0)
|
|
{
|
|
if(sm_CarTwoWheelCounter >= CAR_TWOWHEEL_MINBEST)
|
|
{
|
|
sm_BestCarTwoWheelsTimeMs = sm_CarTwoWheelCounter;
|
|
sm_BestCarTwoWheelsDistM = sm_CarTwoWheelDist;
|
|
|
|
//Increment the stats:
|
|
StatsInterface::SetGreater(STAT_LONGEST_2WHEEL_TIME.GetStatId(), (float)sm_CarTwoWheelCounter, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::SetGreater(STAT_LONGEST_2WHEEL_DIST.GetStatId(), sm_CarTwoWheelDist, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
statDebugf3("Player 2-Wheels for %d ms and %f m\n", sm_BestCarTwoWheelsTimeMs, sm_BestCarTwoWheelsDistM);
|
|
|
|
const CVehicleModelInfo* vi = playerVehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ) && statVerify( CanUpdateVehicleRecord(vi->GetHashKey()) ))
|
|
{
|
|
++sm_vehicleRecords[sm_curVehIdx].m_NumWheelies;
|
|
|
|
if (sm_CarTwoWheelCounter > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime = sm_CarTwoWheelCounter;
|
|
|
|
if (sm_CarTwoWheelDist > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist = sm_CarTwoWheelDist;
|
|
}
|
|
}
|
|
|
|
// reset values
|
|
sm_CarTwoWheelCounter = 0;
|
|
sm_CarTwoWheelDist = 0.0f;
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
|
|
// not on a bike
|
|
sm_BikeRearWheelCounter = 0;
|
|
sm_BikeRearWheelDist = 0.0f;
|
|
sm_IsWheelingOnPushBike = false;
|
|
sm_IsDoingAStoppieOnPushBike = false;
|
|
sm_BikeFrontWheelCounter = 0;
|
|
sm_BikeFrontWheelDist = 0.0f;
|
|
sm_BestBikeWheelieTimeMs = 0;
|
|
sm_BestBikeWheelieDistM = 0.0f;
|
|
sm_BestBikeStoppieTimeMs = 0;
|
|
sm_BestBikeStoppieDistM = 0.0f;
|
|
}
|
|
else if(playerVehicle->InheritsFromBike() || playerVehicle->InheritsFromQuadBike() || playerVehicle->IsTrike())
|
|
{
|
|
bool rearWheelsTouching = false;
|
|
bool frontWheelsTouching = false;
|
|
|
|
if(playerVehicle->InheritsFromBike())
|
|
{
|
|
rearWheelsTouching = playerVehicle->GetWheelFromId(BIKE_WHEEL_R)->GetIsTouching();
|
|
frontWheelsTouching = playerVehicle->GetWheelFromId(BIKE_WHEEL_F)->GetIsTouching();
|
|
}
|
|
else
|
|
{
|
|
rearWheelsTouching = playerVehicle->GetWheelFromId(VEH_WHEEL_LR)->GetIsTouching() && ( playerVehicle->IsReverseTrike() || playerVehicle->GetWheelFromId(VEH_WHEEL_RR)->GetIsTouching() );
|
|
frontWheelsTouching = playerVehicle->GetWheelFromId(VEH_WHEEL_LF)->GetIsTouching();
|
|
}
|
|
|
|
// if front wheel off ground - do wheelie checks
|
|
if(!frontWheelsTouching && sm_BikeFrontWheelCounter==0 && !sm_IsOnAnotherVehicle)
|
|
{
|
|
if(rearWheelsTouching
|
|
&& (playerVehicle->GetFrameCollisionHistory()->GetMaxCollisionImpulseMagLastFrame() < 5.0f
|
|
|| playerVehicle->GetVelocity().Mag2() > MIN_SPEED_TO_CONSIDER_MOVEMENT))
|
|
{
|
|
sm_BikeRearWheelCounter += (s32)fwTimer::GetSystemTimeStepInMilliseconds();
|
|
sm_BikeRearWheelDist += playerVehicle->GetDistanceTravelled();
|
|
|
|
if(sm_TempBufferCounter > fwTimer::GetSystemTimeStepInMilliseconds() / 5)
|
|
sm_TempBufferCounter -= fwTimer::GetSystemTimeStepInMilliseconds() / 5;
|
|
else
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
else if(sm_BikeRearWheelCounter > 0 && sm_TempBufferCounter < BIKE_ONEWHEEL_BUFFERLIMIT)
|
|
sm_TempBufferCounter += fwTimer::GetSystemTimeStepInMilliseconds();
|
|
else
|
|
{
|
|
if(sm_BikeRearWheelCounter >= BIKE_REARWHEEL_MINBEST)
|
|
{
|
|
sm_BestBikeWheelieTimeMs = sm_BikeRearWheelCounter;
|
|
sm_BestBikeWheelieDistM = sm_BikeRearWheelDist;
|
|
|
|
//Increment the stats:
|
|
StatsInterface::SetGreater(STAT_LONGEST_WHEELIE_TIME.GetStatId(), (float)sm_BikeRearWheelCounter, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::SetGreater(STAT_LONGEST_WHEELIE_DIST.GetStatId(), sm_BikeRearWheelDist, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
statDebugf3("Player Wheelie for %d ms and %f m\n", sm_BestBikeWheelieTimeMs, sm_BestBikeWheelieDistM);
|
|
|
|
const CVehicleModelInfo* vi = playerVehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ) && statVerify( CanUpdateVehicleRecord(vi->GetHashKey()) ))
|
|
{
|
|
++sm_vehicleRecords[sm_curVehIdx].m_NumWheelies;
|
|
|
|
if (sm_BikeRearWheelCounter > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime = sm_BikeRearWheelCounter;
|
|
|
|
if (sm_BikeRearWheelDist > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist = sm_BikeRearWheelDist;
|
|
}
|
|
}
|
|
|
|
StatId stat = STAT_TOTAL_WHEELIE_TIME.GetStatId();
|
|
u64 uTotalWheelieTime = StatsInterface::GetUInt64Stat(stat);
|
|
uTotalWheelieTime += (u64)sm_BikeRearWheelCounter;
|
|
StatsInterface::SetStatData(stat, uTotalWheelieTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
|
|
// reset values
|
|
sm_BikeRearWheelCounter = 0;
|
|
sm_BikeRearWheelDist = 0.0f;
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
// Wheelies are too easy with push bikes, so don't count them towards challenge stats
|
|
if (playerVehicle->InheritsFromBicycle() == false)
|
|
{
|
|
UpdateRecordedStat(REC_STAT_LONGEST_WHEELIE_DIST, sm_BikeRearWheelDist);
|
|
}
|
|
else
|
|
{
|
|
sm_IsWheelingOnPushBike = true;
|
|
}
|
|
}
|
|
//Wheelie just ended
|
|
else if(sm_BikeRearWheelCounter > 0)
|
|
{
|
|
if(sm_BikeRearWheelCounter >= BIKE_REARWHEEL_MINBEST)
|
|
{
|
|
sm_BestBikeWheelieTimeMs = sm_BikeRearWheelCounter;
|
|
sm_BestBikeWheelieDistM = sm_BikeRearWheelDist;
|
|
|
|
//Increment the stats:
|
|
StatsInterface::SetGreater(STAT_LONGEST_WHEELIE_TIME.GetStatId(), (float)sm_BikeRearWheelCounter, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::SetGreater(STAT_LONGEST_WHEELIE_DIST.GetStatId(), sm_BikeRearWheelDist, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
|
|
// Wheelies are too easy with push bikes, so don't count them towards challenge stats
|
|
if (playerVehicle->InheritsFromBicycle() == false)
|
|
{
|
|
UpdateRecordedStat(REC_STAT_LONGEST_WHEELIE_DIST, sm_BikeRearWheelDist);
|
|
}
|
|
statDebugf3("Player Wheelie for %d ms and %f m\n", sm_BestBikeWheelieTimeMs, sm_BestBikeWheelieDistM);
|
|
|
|
const CVehicleModelInfo* vi = playerVehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ) && statVerify( CanUpdateVehicleRecord(vi->GetHashKey()) ))
|
|
{
|
|
++sm_vehicleRecords[sm_curVehIdx].m_NumWheelies;
|
|
|
|
if (sm_BikeRearWheelCounter > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieTime = sm_BikeRearWheelCounter;
|
|
|
|
if (sm_BikeRearWheelDist > sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist)
|
|
sm_vehicleRecords[sm_curVehIdx].m_LongestWheelieDist = sm_BikeRearWheelDist;
|
|
}
|
|
}
|
|
|
|
SendStuntEvent(ST_WHEELIE, (float)sm_BikeRearWheelCounter/1000.0f);
|
|
|
|
StatId stat = STAT_TOTAL_WHEELIE_TIME.GetStatId();
|
|
u64 uTotalWheelieTime = StatsInterface::GetUInt64Stat(stat);
|
|
uTotalWheelieTime += (u64)sm_BikeRearWheelCounter;
|
|
StatsInterface::SetStatData(stat, uTotalWheelieTime, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
|
|
// reset values
|
|
sm_BikeRearWheelCounter = 0;
|
|
sm_BikeRearWheelDist = 0.0f;
|
|
sm_IsWheelingOnPushBike = false;
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
//Front wheel on ground, so check for stoppie
|
|
else
|
|
{
|
|
if(!rearWheelsTouching && !sm_IsOnAnotherVehicle)
|
|
{
|
|
if(frontWheelsTouching && (playerVehicle->GetFrameCollisionHistory()->GetMaxCollisionImpulseMagLastFrame() < 5.0f || playerVehicle->GetVelocity().Mag2() > 0.1f))
|
|
{
|
|
sm_BikeFrontWheelCounter += (s32)fwTimer::GetSystemTimeStepInMilliseconds();
|
|
sm_BikeFrontWheelDist += playerVehicle->GetDistanceTravelled();
|
|
if(sm_TempBufferCounter > fwTimer::GetSystemTimeStepInMilliseconds() / 5)
|
|
sm_TempBufferCounter -= fwTimer::GetSystemTimeStepInMilliseconds() / 5;
|
|
else
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
else if(sm_BikeFrontWheelCounter > 0 && sm_TempBufferCounter < BIKE_ONEWHEEL_BUFFERLIMIT)
|
|
sm_TempBufferCounter += fwTimer::GetSystemTimeStepInMilliseconds();
|
|
else
|
|
{
|
|
if(sm_BikeFrontWheelCounter >= BIKE_FRONTWHEEL_MINBEST)
|
|
{
|
|
sm_BestBikeStoppieTimeMs = sm_BikeFrontWheelCounter;
|
|
sm_BestBikeStoppieDistM = sm_BikeFrontWheelDist;
|
|
|
|
//Increment the stats:
|
|
StatsInterface::SetGreater(STAT_LONGEST_STOPPIE_TIME.GetStatId(), (float)sm_BikeFrontWheelCounter);
|
|
StatsInterface::SetGreater(STAT_LONGEST_STOPPIE_DIST.GetStatId(), sm_BikeFrontWheelDist);
|
|
statDebugf3("Player Stoppie for %d ms and %f m\n", sm_BestBikeStoppieTimeMs, sm_BestBikeStoppieDistM);
|
|
}
|
|
|
|
// Stoppies are too easy with push bikes, so don't count them towards challenge stats
|
|
if (playerVehicle->InheritsFromBicycle() == false)
|
|
{
|
|
UpdateRecordedStat(REC_STAT_LONGEST_STOPPIE_DIST, sm_BikeFrontWheelDist);
|
|
}
|
|
// reset values
|
|
sm_BikeFrontWheelCounter = 0;
|
|
sm_BikeFrontWheelDist = 0.0f;
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(sm_BikeFrontWheelCounter >= BIKE_FRONTWHEEL_MINBEST)
|
|
{
|
|
sm_BestBikeStoppieTimeMs = sm_BikeFrontWheelCounter;
|
|
sm_BestBikeStoppieDistM = sm_BikeFrontWheelDist;
|
|
|
|
//Increment the stats:
|
|
StatsInterface::SetGreater(STAT_LONGEST_STOPPIE_TIME.GetStatId(), (float)sm_BikeFrontWheelCounter);
|
|
StatsInterface::SetGreater(STAT_LONGEST_STOPPIE_DIST.GetStatId(), sm_BikeFrontWheelDist);
|
|
statDebugf3("Player Stoppie for %d ms and %f m\n", sm_BestBikeStoppieTimeMs, sm_BestBikeStoppieDistM);
|
|
}
|
|
|
|
// Stoppies are too easy with push bikes, so don't count them towards challenge stats
|
|
if (playerVehicle->InheritsFromBicycle() == false)
|
|
{
|
|
UpdateRecordedStat(REC_STAT_LONGEST_STOPPIE_DIST, sm_BikeFrontWheelDist);
|
|
}
|
|
else
|
|
{
|
|
sm_IsDoingAStoppieOnPushBike = true;
|
|
}
|
|
|
|
if(sm_BikeFrontWheelCounter != 0.0f)
|
|
{
|
|
SendStuntEvent(ST_STOPPIE, (float)sm_BikeFrontWheelCounter/1000.0f);
|
|
}
|
|
|
|
// reset values
|
|
sm_BikeFrontWheelCounter = 0;
|
|
sm_BikeFrontWheelDist = 0.0f;
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
}
|
|
|
|
// not in a car
|
|
sm_CarTwoWheelCounter = 0;
|
|
sm_CarLess3WheelCounter = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
sm_CarTwoWheelCounter = 0;
|
|
sm_CarLess3WheelCounter = 0.0f;
|
|
sm_BikeRearWheelCounter = 0;
|
|
sm_BikeFrontWheelCounter = 0;
|
|
sm_TempBufferCounter = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateVehicleStats(CVehicle* playerVehicle)
|
|
{
|
|
statAssert(playerVehicle);
|
|
if (!playerVehicle)
|
|
return;
|
|
|
|
if(sm_VehiclePreviousPosition.IsZero())
|
|
sm_VehiclePreviousPosition = playerVehicle->GetPreviousPosition();
|
|
|
|
Vec3V currentPosition = playerVehicle->GetTransform().GetPosition();
|
|
|
|
const float distanceTravelled = Mag(currentPosition - VECTOR3_TO_VEC3V(sm_VehiclePreviousPosition)).Getf();
|
|
|
|
const bool driverIsPlayer = (playerVehicle->GetDriver() && playerVehicle->GetDriver()->IsLocalPlayer());
|
|
|
|
const float timeStepInMilliseconds = fwTimer::GetSystemTimeStep()*1000.0f;
|
|
|
|
StatId statDist;
|
|
StatId statDistDriving;
|
|
StatId statTimeDriving;
|
|
StatId statFastestSpeed;
|
|
StatId statPassengerDistDriving;
|
|
|
|
s32 vehType = playerVehicle->GetVehicleType();
|
|
|
|
if (VEHICLE_TYPE_BLIMP == vehType)
|
|
return;
|
|
|
|
// For bike-style trikes, we want to track the stats as BIKE despite the actual type being QUADBIKE (for physics purposes)
|
|
if (playerVehicle->IsTrike())
|
|
vehType = VEHICLE_TYPE_BIKE;
|
|
|
|
switch (vehType)
|
|
{
|
|
case VEHICLE_TYPE_CAR:
|
|
case VEHICLE_TYPE_AMPHIBIOUS_AUTOMOBILE:
|
|
|
|
// CAREFUL! --> check case VEHICLE_TYPE_SUBMARINECAR when updating this case
|
|
|
|
CStatsMgr::UpdateVehicleSpins(playerVehicle);
|
|
CStatsMgr::UpdateVehicleFlips(playerVehicle);
|
|
CStatsMgr::UpdateVehicleFlipsStunt(playerVehicle);
|
|
CStatsMgr::UpdateVehicleRolls(playerVehicle);
|
|
CStatsMgr::UpdateVehicleZeroWheel(playerVehicle);
|
|
CStatsMgr::UpdateVehicleWheelie(playerVehicle);
|
|
CStatsMgr::UpdateVehicleReverseDriving(playerVehicle);
|
|
CStatsMgr::UpdateVehicleWallride(playerVehicle);
|
|
statDist = STAT_DIST_CAR;
|
|
statFastestSpeed = STAT_FASTEST_SPEED;
|
|
if(driverIsPlayer)
|
|
{
|
|
if (!playerVehicle->IsInAir())
|
|
{
|
|
sm_DriveNoCrashDist += distanceTravelled;
|
|
sm_DriveNoCrashTime += timeStepInMilliseconds;
|
|
}
|
|
|
|
statDistDriving = STAT_DIST_DRIVING_CAR;
|
|
statTimeDriving = STAT_TIME_DRIVING_CAR;
|
|
|
|
CStatsMgr::UpdateAverageSpeed();
|
|
UpdateRecordedStat(REC_STAT_LONGEST_DRIVE_NOCRASH, sm_DriveNoCrashDist);
|
|
|
|
if(!sm_ChallengeZeroWheelPosStart.IsZero() && !sm_HadAttachedParentVehicle && !sm_WasOnAnotherVehicleWhenJumped)
|
|
{
|
|
Vector3 vehiclePosition = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetPosition());
|
|
vehiclePosition.z = 0.0f;
|
|
Vector3 startPos = sm_ChallengeZeroWheelPosStart;
|
|
startPos.z = 0;
|
|
if(playerVehicle->GetVehicleModelInfo() && !playerVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_HAS_PARACHUTE)
|
|
&& playerVehicle->GetModelIndex() != MI_CAR_DELUXO && playerVehicle->GetModelIndex() != MI_BIKE_OPPRESSOR2) // hover vehicles
|
|
{
|
|
sm_JumpDistance = Vector3(vehiclePosition - startPos).Mag();
|
|
}
|
|
}
|
|
}
|
|
else if(!NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (playerVehicle->IsTaxi())
|
|
{
|
|
statPassengerDistDriving = STAT_DIST_AS_PASSENGER_TAXI;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VEHICLE_TYPE_PLANE:
|
|
CStatsMgr::IncrementStat(STAT_FLIGHT_TIME.GetStatId(), timeStepInMilliseconds, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
statDist = STAT_DIST_PLANE;
|
|
if(driverIsPlayer)
|
|
{
|
|
statDistDriving = STAT_DIST_DRIVING_PLANE;
|
|
statTimeDriving = STAT_TIME_DRIVING_PLANE;
|
|
CStatsMgr::UpdatePlane(playerVehicle);
|
|
}
|
|
break;
|
|
|
|
case VEHICLE_TYPE_QUADBIKE:
|
|
case VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE:
|
|
CStatsMgr::UpdateVehicleSpins(playerVehicle);
|
|
CStatsMgr::UpdateVehicleFlips(playerVehicle);
|
|
CStatsMgr::UpdateVehicleFlipsStunt(playerVehicle);
|
|
CStatsMgr::UpdateVehicleRolls(playerVehicle);
|
|
CStatsMgr::UpdateVehicleZeroWheel(playerVehicle);
|
|
CStatsMgr::UpdateVehicleWheelie(playerVehicle);
|
|
CStatsMgr::UpdateVehicleReverseDriving(playerVehicle);
|
|
CStatsMgr::UpdateVehicleWallride(playerVehicle);
|
|
statDist = STAT_DIST_QUADBIKE;
|
|
statFastestSpeed = STAT_FASTEST_SPEED;
|
|
|
|
if (driverIsPlayer)
|
|
{
|
|
if (!playerVehicle->IsInAir())
|
|
{
|
|
sm_DriveNoCrashDist += distanceTravelled;
|
|
sm_DriveNoCrashTime += timeStepInMilliseconds;
|
|
}
|
|
UpdateRecordedStat(REC_STAT_LONGEST_DRIVE_NOCRASH, sm_DriveNoCrashDist);
|
|
|
|
statDistDriving = STAT_DIST_DRIVING_QUADBIKE;
|
|
statTimeDriving = STAT_TIME_DRIVING_QUADBIKE;
|
|
CStatsMgr::UpdateAverageSpeed();
|
|
}
|
|
|
|
if(!sm_ChallengeZeroWheelPosStart.IsZero() && !sm_HadAttachedParentVehicle && !sm_WasOnAnotherVehicleWhenJumped)
|
|
{
|
|
Vector3 vehiclePosition = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetPosition());
|
|
vehiclePosition.z = 0.0f;
|
|
Vector3 startPos = sm_ChallengeZeroWheelPosStart;
|
|
startPos.z = 0;
|
|
if (playerVehicle->GetVehicleModelInfo() && !playerVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_HAS_PARACHUTE) && playerVehicle->GetModelIndex() != MI_CAR_DELUXO && playerVehicle->GetModelIndex() != MI_BIKE_OPPRESSOR2)
|
|
{
|
|
sm_JumpDistance = Vector3(vehiclePosition - startPos).Mag();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VEHICLE_TYPE_HELI:
|
|
CStatsMgr::IncrementStat(STAT_FLIGHT_TIME.GetStatId(), timeStepInMilliseconds, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
statDist = STAT_DIST_HELI;
|
|
if(driverIsPlayer)
|
|
{
|
|
statDistDriving = STAT_DIST_DRIVING_HELI;
|
|
statTimeDriving = STAT_TIME_DRIVING_HELI;
|
|
CStatsMgr::UpdateHeli(playerVehicle);
|
|
}
|
|
break;
|
|
|
|
case VEHICLE_TYPE_BIKE:
|
|
CStatsMgr::UpdateVehicleSpins(playerVehicle);
|
|
CStatsMgr::UpdateVehicleFlips(playerVehicle);
|
|
CStatsMgr::UpdateVehicleFlipsStunt(playerVehicle);
|
|
CStatsMgr::UpdateVehicleRolls(playerVehicle);
|
|
CStatsMgr::UpdateVehicleZeroWheel(playerVehicle);
|
|
CStatsMgr::UpdateVehicleWheelie(playerVehicle);
|
|
CStatsMgr::UpdateVehicleWallride(playerVehicle);
|
|
statDist = STAT_DIST_BIKE;
|
|
statFastestSpeed = STAT_FASTEST_SPEED;
|
|
|
|
if(driverIsPlayer)
|
|
{
|
|
if (!playerVehicle->IsInAir())
|
|
{
|
|
sm_DriveNoCrashDist += distanceTravelled;
|
|
sm_DriveNoCrashTime += timeStepInMilliseconds;
|
|
}
|
|
statDistDriving = STAT_DIST_DRIVING_BIKE;
|
|
statTimeDriving = STAT_TIME_DRIVING_BIKE;
|
|
CStatsMgr::UpdateAverageSpeed();
|
|
UpdateRecordedStat(REC_STAT_LONGEST_DRIVE_NOCRASH, sm_DriveNoCrashDist);
|
|
}
|
|
|
|
if(!sm_ChallengeZeroWheelPosStart.IsZero() && !sm_HadAttachedParentVehicle && !sm_WasOnAnotherVehicleWhenJumped)
|
|
{
|
|
Vector3 vehiclePosition = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetPosition());
|
|
vehiclePosition.z = 0.0f;
|
|
Vector3 startPos = sm_ChallengeZeroWheelPosStart;
|
|
startPos.z = 0;
|
|
if(playerVehicle->GetVehicleModelInfo() && !playerVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_HAS_PARACHUTE) && playerVehicle->GetModelIndex() != MI_CAR_DELUXO && playerVehicle->GetModelIndex() != MI_BIKE_OPPRESSOR2)
|
|
{
|
|
sm_JumpDistance = Vector3(vehiclePosition - startPos).Mag();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VEHICLE_TYPE_BICYCLE:
|
|
CStatsMgr::UpdateVehicleWheelie(playerVehicle);
|
|
statDist = STAT_DIST_BICYCLE;
|
|
if(driverIsPlayer)
|
|
{
|
|
statDistDriving = STAT_DIST_DRIVING_BICYCLE;
|
|
statTimeDriving = STAT_TIME_DRIVING_BICYCLE;
|
|
}
|
|
break;
|
|
|
|
case VEHICLE_TYPE_BOAT:
|
|
CStatsMgr::UpdateVehicleZeroWheel(playerVehicle);
|
|
statDist = STAT_DIST_BOAT;
|
|
if(driverIsPlayer)
|
|
{
|
|
CStatsMgr::UpdateBoat(playerVehicle);
|
|
statDistDriving = STAT_DIST_DRIVING_BOAT;
|
|
statTimeDriving = STAT_TIME_DRIVING_BOAT;
|
|
}
|
|
break;
|
|
|
|
case VEHICLE_TYPE_TRAIN:
|
|
statAssert(!driverIsPlayer);
|
|
break;
|
|
|
|
case VEHICLE_TYPE_SUBMARINE:
|
|
statDist = STAT_DIST_SUBMARINE;
|
|
if(driverIsPlayer)
|
|
{
|
|
statDistDriving = STAT_DIST_DRIVING_SUBMARINE;
|
|
statTimeDriving = STAT_TIME_DRIVING_SUBMARINE;
|
|
}
|
|
break;
|
|
|
|
case VEHICLE_TYPE_SUBMARINECAR:
|
|
{
|
|
CSubmarineCar* submarineCar = static_cast< CSubmarineCar* >( playerVehicle );
|
|
|
|
if( submarineCar->IsInSubmarineMode() )
|
|
{
|
|
statDist = STAT_DIST_SUBMARINE;
|
|
if(driverIsPlayer)
|
|
{
|
|
statDistDriving = STAT_DIST_DRIVING_SUBMARINE;
|
|
statTimeDriving = STAT_TIME_DRIVING_SUBMARINE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CStatsMgr::UpdateVehicleSpins(playerVehicle);
|
|
CStatsMgr::UpdateVehicleFlips(playerVehicle);
|
|
CStatsMgr::UpdateVehicleFlipsStunt(playerVehicle);
|
|
CStatsMgr::UpdateVehicleRolls(playerVehicle);
|
|
CStatsMgr::UpdateVehicleZeroWheel(playerVehicle);
|
|
CStatsMgr::UpdateVehicleWheelie(playerVehicle);
|
|
CStatsMgr::UpdateVehicleReverseDriving(playerVehicle);
|
|
CStatsMgr::UpdateVehicleWallride(playerVehicle);
|
|
statDist = STAT_DIST_CAR;
|
|
statFastestSpeed = STAT_FASTEST_SPEED;
|
|
if(driverIsPlayer)
|
|
{
|
|
if (!playerVehicle->IsInAir())
|
|
{
|
|
sm_DriveNoCrashDist += distanceTravelled;
|
|
sm_DriveNoCrashTime += timeStepInMilliseconds;
|
|
}
|
|
statDistDriving = STAT_DIST_DRIVING_CAR;
|
|
statTimeDriving = STAT_TIME_DRIVING_CAR;
|
|
CStatsMgr::UpdateAverageSpeed();
|
|
UpdateRecordedStat(REC_STAT_LONGEST_DRIVE_NOCRASH, sm_DriveNoCrashDist);
|
|
}
|
|
else if(!NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (playerVehicle->IsTaxi())
|
|
{
|
|
statPassengerDistDriving = STAT_DIST_AS_PASSENGER_TAXI;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VEHICLE_TYPE_TRAILER:
|
|
case VEHICLE_TYPE_AUTOGYRO:
|
|
default:
|
|
statAssert(0);
|
|
}
|
|
|
|
const CVehicleModelInfo* vi = playerVehicle->GetVehicleModelInfo();
|
|
if (vi && driverIsPlayer)
|
|
{
|
|
if (!CanUpdateVehicleRecord(vi->GetHashKey()))
|
|
{
|
|
StartNewVehicleRecords(vi->GetHashKey());
|
|
}
|
|
|
|
if (statVerify(CanUpdateVehicleRecord(vi->GetHashKey())))
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_LastDistDriven += distanceTravelled;
|
|
sm_vehicleRecords[sm_curVehIdx].m_DistDriven += distanceTravelled;
|
|
sm_vehicleRecords[sm_curVehIdx].m_TimeDriven += (u32)timeStepInMilliseconds; //< loss of precision !!!1
|
|
sm_vehicleRecords[sm_curVehIdx].m_Owned = playerVehicle->IsPersonalVehicle();
|
|
|
|
if (!sm_vehicleRecords[sm_curVehIdx].m_HasSetNumDriven && sm_vehicleRecords[sm_curVehIdx].m_LastDistDriven>=CStatsVehicleUsage::VEHICLE_MIN_DRIVE_DIST)
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_HasSetNumDriven = true; //Lock further.
|
|
sm_vehicleRecords[sm_curVehIdx].m_NumDriven += 1;
|
|
}
|
|
}
|
|
|
|
//Total Distance travelled
|
|
if (NetworkInterface::IsGameInProgress() && StatsInterface::IsKeyValid(STAT_MP_CHAR_DIST_TRAVELLED))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_MP_CHAR_DIST_TRAVELLED, distanceTravelled, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
//If we are driving a car then make sure we set the model has been driven.
|
|
if (playerVehicle->InheritsFromAutomobile())
|
|
{
|
|
if (!CStatsMgr::VehicleHasBeenDriven(vi->GetHashKey(), NetworkInterface::IsGameInProgress()))
|
|
{
|
|
CStatsMgr::SetVehicleHasBeenDriven(vi->GetHashKey(), vi->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_COUNT_AS_FACEBOOK_DRIVEN));
|
|
CStatsMgr::IncrementStat( NetworkInterface::IsGameInProgress() ? STAT_MP_VEHICLE_MODELS_DRIVEN : STAT_SP_VEHICLE_MODELS_DRIVEN, 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
}
|
|
|
|
//If the player is Driving in cinematic camera
|
|
const camCinematicDirector& cinematicDirector = camInterface::GetCinematicDirector();
|
|
if (cinematicDirector.IsRenderingAnyInVehicleCinematicCamera())
|
|
{
|
|
sm_DriveInCinematic += timeStepInMilliseconds;
|
|
StatsInterface::SetGreater(STAT_LONGEST_CAM_TIME_DRIVING.GetStatId(), sm_DriveInCinematic, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
|
|
CPlayerSpecialAbilityManager::ChargeEvent(ACET_DRIVING_IN_CINEMATIC, FindPlayerPed());
|
|
}
|
|
else
|
|
{
|
|
StatsInterface::SetGreater(STAT_LONGEST_CAM_TIME_DRIVING.GetStatId(), sm_DriveInCinematic, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
sm_DriveInCinematic = 0.0f;
|
|
}
|
|
|
|
if(statDist.IsValid())
|
|
{
|
|
StatsInterface::IncrementStat(statDist, distanceTravelled, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
if (statPassengerDistDriving.IsValid())
|
|
{
|
|
StatsInterface::IncrementStat(statPassengerDistDriving, distanceTravelled, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
if(driverIsPlayer && statDistDriving.IsValid() && statVerify(statTimeDriving.IsValid()))
|
|
{
|
|
StatsInterface::IncrementStat(statDistDriving, distanceTravelled, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::IncrementStat(statTimeDriving, timeStepInMilliseconds, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
|
|
//Set fastest speed
|
|
const float NO_CONTACT_WHEELS_REST_TIME = 2000.0f;
|
|
static float s_noContactWheelsTimer = 0.0f;
|
|
|
|
if (!playerVehicle->HasContactWheels())
|
|
{
|
|
s_noContactWheelsTimer = 0.0f;
|
|
}
|
|
else if(s_noContactWheelsTimer < NO_CONTACT_WHEELS_REST_TIME)
|
|
{
|
|
s_noContactWheelsTimer += timeStepInMilliseconds;
|
|
}
|
|
|
|
if (driverIsPlayer)
|
|
{
|
|
const float speed = DotProduct(playerVehicle->GetVelocity(), VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetB())) * 3.6f; // km/h forward speed
|
|
|
|
//statFastestSpeed is only updated for land vehicles... statAssert( playerVehicle->GetIsLandVehicle() );
|
|
if (statFastestSpeed.IsValid() && playerVehicle->HasContactWheels() && s_noContactWheelsTimer >= NO_CONTACT_WHEELS_REST_TIME)
|
|
{
|
|
if (vi && CanUpdateVehicleRecord(vi->GetHashKey()))
|
|
{
|
|
if (speed > sm_vehicleRecords[sm_curVehIdx].m_HighestSpeed)
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_HighestSpeed = speed;
|
|
}
|
|
}
|
|
|
|
if (speed > StatsInterface::GetFloatStat(statFastestSpeed))
|
|
{
|
|
StatsInterface::SetStatData(STAT_TOP_SPEED_CAR.GetStatId(), vi->GetHashKey(), STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
StatsInterface::SetStatData(statFastestSpeed, speed, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
|
|
if(!sm_HadAttachedParentVehicle && statFastestSpeed.IsValid() && playerVehicle->HasContactWheels())
|
|
{
|
|
sm_CurrentSpeed = speed;
|
|
UpdateRecordedStat(REC_STAT_TOP_SPEED_CAR, sm_CurrentSpeed);
|
|
}
|
|
}
|
|
|
|
sm_VehiclePreviousPosition = VEC3V_TO_VECTOR3( currentPosition );
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateWeaponKill(const CPed* killer, const u32 weaponHash, const bool bHeadShot, const bool killedPlayer, const fwFlags32& flags, const CPed* victim)
|
|
{
|
|
statAssertf(killer, "Killer is NULL");
|
|
if (!killer || !killer->IsLocalPlayer())
|
|
return;
|
|
|
|
const CWeaponInfo* wi = CWeaponInfoManager::GetInfo<CWeaponInfo>(weaponHash);
|
|
statAssertf(wi, "NULL weapon info for weapon hash=[0x%X]", weaponHash);
|
|
if(!wi)
|
|
return;
|
|
|
|
//Update weapon kills
|
|
if (killedPlayer)
|
|
{
|
|
StatId stat = StatsInterface::GetLocalPlayerWeaponInfoStatId(StatsInterface::WEAPON_STAT_ENEMY_KILLS, wi, killer);
|
|
if (StatsInterface::IsKeyValid(stat))
|
|
{
|
|
StatsInterface::IncrementStat(stat, 1.0f);
|
|
}
|
|
}
|
|
|
|
//Update kills done with that weapon
|
|
StatId statWeaponKill = StatsInterface::GetLocalPlayerWeaponInfoStatId(StatsInterface::WEAPON_STAT_KILLS, wi, killer);
|
|
if (StatsInterface::IsKeyValid(statWeaponKill))
|
|
{
|
|
StatsInterface::IncrementStat(statWeaponKill, 1.0f);
|
|
}
|
|
#if __ASSERT
|
|
else if(StatsInterface::GetStatsPlayerModelValid() && killer->IsArchetypeSet())
|
|
{
|
|
CStatsMgr::MissingWeaponStatName(STATTYPE_WEAPON_KILLS, statWeaponKill, wi->GetHash(), wi->GetDamageType(), wi->GetName());
|
|
}
|
|
#endif // __ASSERT
|
|
|
|
//Update Total ped kills
|
|
StatsInterface::IncrementStat(STAT_KILLS.GetStatId(), 1.0f);
|
|
|
|
StatId statKillsSinceLastCheckpoint = STAT_KILLS_SINCE_LAST_CHECKPOINT.GetStatId();
|
|
if(StatsInterface::IsKeyValid(statKillsSinceLastCheckpoint))
|
|
{
|
|
StatsInterface::IncrementStat(statKillsSinceLastCheckpoint, 1);
|
|
StatsInterface::IncrementStat(STAT_KILLS_SINCE_SAFEHOUSE_VISIT.GetStatId(), 1);
|
|
}
|
|
|
|
//Melee kills dont update more stats
|
|
if (wi->GetIsMelee() || flags.IsFlagSet(CPedDamageCalculator::DF_MeleeDamage))
|
|
{
|
|
//Armed Kill
|
|
if (weaponHash != WEAPONTYPE_UNARMED && !wi->GetIsUnarmed() && weaponHash != WEAPONTYPE_RAMMEDBYVEHICLE && weaponHash != WEAPONTYPE_RUNOVERBYVEHICLE)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_KILLS_ARMED.GetStatId(), 1.0f);
|
|
}
|
|
|
|
if(killedPlayer)
|
|
UpdateRecordedStat(REC_STAT_MELEE_KILLED_PLAYERS, 1.0f);
|
|
|
|
return;
|
|
}
|
|
|
|
//Update armed kills
|
|
if (wi->GetIsGun())
|
|
{
|
|
StatsInterface::IncrementStat(STAT_KILLS_ARMED.GetStatId(), 1.0f);
|
|
|
|
if(killedPlayer && wi->GetGroup() == WEAPONGROUP_SNIPER && wi->GetWeaponWheelSlot() == WEAPON_WHEEL_SLOT_SNIPER)
|
|
{
|
|
float distance = Mag(killer->GetTransform().GetPosition() - victim->GetTransform().GetPosition()).Getf();
|
|
UpdateRecordedStat(REC_STAT_SNIPER_KILL_DISTANCE, distance);
|
|
UpdateRecordedStat(REC_STAT_SNIPER_KILL, 1.0f);
|
|
}
|
|
|
|
// url:bugstar:2398298 - If we are in the melee VS challenge, weapon kills count as -1
|
|
if(m_recordedStat==REC_STAT_MELEE_KILLED_PLAYERS && killedPlayer && m_recordedStatValue>0.0f)
|
|
UpdateRecordedStat(REC_STAT_MELEE_KILLED_PLAYERS, -1.0f);
|
|
}
|
|
|
|
//Is player free aiming
|
|
CPlayerInfo* pi = killer->GetPlayerInfo();
|
|
if ((pi && pi->GetPlayerDataFreeAiming()) || killer->GetPlayerResetFlag(CPlayerResetFlags::PRF_RUN_AND_GUN))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_KILLS_IN_FREE_AIM.GetStatId(), 1.0f);
|
|
}
|
|
|
|
CVehicle* pVehicle = killer->GetVehiclePedInside();
|
|
const bool isTheDriver = pVehicle ? (killer == pVehicle->GetDriver()) : false;
|
|
|
|
//Drive by kills
|
|
if(pVehicle && wi->GetIsGun() && pVehicle->GetIsLandVehicle())
|
|
{
|
|
if (isTheDriver)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_DB_KILLS.GetStatId(), 1.0f);
|
|
if (killedPlayer)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_DB_PLAYER_KILLS.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
StatsInterface::IncrementStat(STAT_PASS_DB_KILLS.GetStatId(), 1.0f);
|
|
if (killedPlayer)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_PASS_DB_PLAYER_KILLS.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
if(killedPlayer && !wi->GetIsVehicleWeapon())
|
|
{
|
|
UpdateRecordedStat(REC_STAT_DB_NON_TURRET_PLAYER_KILLS, 1.0f);
|
|
}
|
|
}
|
|
|
|
//HeadShots with a gun
|
|
if(bHeadShot && (wi->GetDamageType() == DAMAGE_TYPE_BULLET || wi->GetDamageType() == DAMAGE_TYPE_BULLET_RUBBER))
|
|
{
|
|
//Total number of weapon HeadShots
|
|
StatId statWeapon = StatsInterface::GetLocalPlayerWeaponInfoStatId(StatsInterface::WEAPON_STAT_HEADSHOTS, wi, killer);
|
|
if (StatsInterface::IsKeyValid(statWeapon))
|
|
{
|
|
StatsInterface::IncrementStat(statWeapon, 1.0f);
|
|
}
|
|
#if __ASSERT
|
|
else if(StatsInterface::GetStatsPlayerModelValid() && killer->IsArchetypeSet())
|
|
{
|
|
CStatsMgr::MissingWeaponStatName(STATTYPE_WEAPON_HEADSHOTS, statWeapon, wi->GetHash(), wi->GetDamageType(), wi->GetName());
|
|
}
|
|
#endif // __ASSERT
|
|
|
|
//Total number of HeadShots
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("HEADSHOTS"), 1.0f);
|
|
|
|
if (killedPlayer)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_PLAYER_HEADSHOTS.GetStatId(), 1.0f);
|
|
UpdateRecordedStat(REC_STAT_PLAYER_HEADSHOTS, 1.0f);
|
|
}
|
|
|
|
if(pVehicle)
|
|
{
|
|
if(isTheDriver)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_DB_HEADSHOTS.GetStatId(), 1.0f);
|
|
}
|
|
else
|
|
{
|
|
StatsInterface::IncrementStat(STAT_PASS_DB_HEADSHOTS.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::RegisterPlayerKilled(const CPed* killer, const CPed* killed, const u32 weaponHash, const int weaponDamageComponent, const bool withMeleeWeapon)
|
|
{
|
|
//A Player has been killed
|
|
CNetworkTelemetry::PlayerDied(killed, killer, weaponHash, weaponDamageComponent, withMeleeWeapon);
|
|
|
|
statAssertf(killed, "Null victim.");
|
|
if (!killed)
|
|
return;
|
|
|
|
//We only want to update the bellow stats for the local player
|
|
if (!killed->IsLocalPlayer())
|
|
return;
|
|
|
|
const CWeaponInfo* wi = CWeaponInfoManager::GetInfo<CWeaponInfo>(weaponHash);
|
|
statAssertf(wi, "NULL weapon info for weapon hash=[0x%X]", weaponHash);
|
|
if(!wi)
|
|
return;
|
|
|
|
//Make sure we clear cheat tracking.
|
|
SetCheatIsActive( false );
|
|
|
|
//Total number of deaths
|
|
StatsInterface::IncrementStat(STAT_DEATHS.GetStatId(), 1.0f);
|
|
|
|
if (!NetworkInterface::IsGameInProgress() && CTheScripts::GetPlayerIsOnAMission())
|
|
{
|
|
StatsInterface::IncrementStat(STAT_DIED_IN_MISSION.GetStatId(), 1.0f);
|
|
}
|
|
|
|
//Total number of deaths done by other players
|
|
if (killer && killer != killed && killer->IsPlayer() && !killer->IsLocalPlayer())
|
|
{
|
|
if (ShouldCountKills())
|
|
{
|
|
StatsInterface::IncrementStat(STAT_MP_DEATHS_PLAYER, 1.0f);
|
|
StatsInterface::IncrementStat(STAT_MPPLY_DEATHS_PLAYERS_CHEATER, 1.0f);
|
|
|
|
if (StatsInterface::IsKeyValid(STAT_DEATHS_PLAYER.GetHash()))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_DEATHS_PLAYER.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
ClearPlayerVehicleStats();
|
|
|
|
EndVehicleStats();
|
|
|
|
sm_CrashedVehicleTimer = 0;
|
|
sm_CrashedVehicleKeyHash = 0;
|
|
sm_CrashedVehicleDamageAccumulator = 0.0f;
|
|
sm_NearMissNoCrash = 0;
|
|
sm_NearMissNoCrashPrecise = 0;
|
|
sm_JumpDistance = 0.0f;
|
|
sm_VehicleBailDistance = 0.0f;
|
|
sm_VehicleDeadCheckCounter = 0;
|
|
sm_VehicleDeadCheck = false;
|
|
|
|
sm_StartVehicleBail.Zero();
|
|
sm_LastVehicleBailPosition.Zero();
|
|
sm_HadAttachedParentVehicle = false;
|
|
sm_HasStartedParachuteDeploy = false;
|
|
sm_hasBailedFromParachuting = false;
|
|
sm_ParachuteStartTime = 0;
|
|
sm_FlyingAltitude = 0.0f;
|
|
sm_ValidFlyingAltitude = false;
|
|
sm_CurrentSpeed = 0.0f;
|
|
|
|
ClearNearMissArray();
|
|
if (killer)
|
|
{
|
|
StatId stat = StatsInterface::GetWeaponInfoHashId("DEATHS", wi, killer);
|
|
if (StatsInterface::IsKeyValid(stat))
|
|
{
|
|
StatsInterface::IncrementStat(stat, 1.0f);
|
|
}
|
|
#if __ASSERT
|
|
else if (killed->IsArchetypeSet() && StatsInterface::GetStatsPlayerModelValid())
|
|
{
|
|
statWarningf("Specific Weapon %s doesnt have a valid stat name for DEATHS.", wi->GetName());
|
|
}
|
|
#endif // __ASSERT
|
|
}
|
|
|
|
eDamageType damageType = wi->GetDamageType();
|
|
|
|
if (WEAPONTYPE_DROWNING.GetHash() == weaponHash)
|
|
{
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("DIED_IN_DROWNING"), 1);
|
|
}
|
|
else if (WEAPONTYPE_DROWNINGINVEHICLE.GetHash() == weaponHash)
|
|
{
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("DIED_IN_DROWNING"), 1);
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("DIED_IN_DROWNINGINVEHICLE"), 1);
|
|
}
|
|
else if (damageType == DAMAGE_TYPE_EXPLOSIVE || WEAPONTYPE_EXPLOSION.GetHash() == weaponHash)
|
|
{
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("DIED_IN_EXPLOSION"), 1);
|
|
}
|
|
else if (damageType == DAMAGE_TYPE_FALL || WEAPONTYPE_FALL.GetHash() == weaponHash)
|
|
{
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("DIED_IN_FALL"), 1);
|
|
}
|
|
else if (damageType == DAMAGE_TYPE_FIRE || WEAPONTYPE_FIRE.GetHash() == weaponHash)
|
|
{
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("DIED_IN_FIRE"), 1);
|
|
}
|
|
else if (WEAPONTYPE_RAMMEDBYVEHICLE.GetHash() == weaponHash || WEAPONTYPE_RUNOVERBYVEHICLE.GetHash() == weaponHash)
|
|
{
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("DIED_IN_ROAD"), 1);
|
|
}
|
|
#if !__FINAL
|
|
else if (WEAPONTYPE_BUZZARD.GetHash() == weaponHash)
|
|
{
|
|
//Do nothing
|
|
}
|
|
else if (WEAPONTYPE_TANK.GetHash() == weaponHash)
|
|
{
|
|
//Do nothing
|
|
}
|
|
else if (WEAPONTYPE_ELECTRICFENCE.GetHash() == weaponHash)
|
|
{
|
|
//Do nothing
|
|
}
|
|
else
|
|
{
|
|
statWarningf("Specific death by Weapon %s DEATH is not being recorded.", wi->GetName());
|
|
}
|
|
#endif // !__FINAL
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateVehicleFlips(CVehicle* playerVehicle)
|
|
{
|
|
statAssert(playerVehicle);
|
|
if (!playerVehicle)
|
|
return;
|
|
else if (!playerVehicle->GetDriver())
|
|
return;
|
|
else if (!playerVehicle->GetDriver()->IsLocalPlayer())
|
|
return;
|
|
|
|
const u32 CRASHED_VEHICLE_FLIPS_INTERVAL = 3*1000;
|
|
|
|
// Is it valid to update the spinning stats?
|
|
if(playerVehicle->HasContactWheels() || (fwTimer::GetTimeInMilliseconds_NonScaledClipped() - sm_CrashedVehicleTimer) < CRASHED_VEHICLE_FLIPS_INTERVAL)
|
|
{
|
|
sm_VehicleFlipsAccumulator = 0;
|
|
return;
|
|
}
|
|
|
|
Vector3 vCarUp = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetC());
|
|
|
|
if (!sm_VehicleFlipsCounter)
|
|
{
|
|
if (vCarUp.z >= -VEHICLE_UPSIDEDOWN_UPZ_THRESHOLD)
|
|
{
|
|
sm_VehicleFlipsCounter = true;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (vCarUp.z > VEHICLE_UPSIDEDOWN_UPZ_THRESHOLD)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const CVehicleModelInfo* vi = playerVehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ) && statVerify( CanUpdateVehicleRecord(vi->GetHashKey()) ))
|
|
{
|
|
++sm_vehicleRecords[sm_curVehIdx].m_NumFlips;
|
|
}
|
|
|
|
sm_VehicleFlipsAccumulator += 1;
|
|
CStatsMgr::SetGreater(STAT_MOST_FLIPS_IN_ONE_JUMP.GetStatId(), (float)sm_VehicleFlipsAccumulator);
|
|
|
|
if(m_statRecordingPolicy==RSP_GREATEST || m_statRecordingPolicy==RSP_LOWEST)
|
|
UpdateRecordedStat(REC_STAT_MOST_FLIPS_IN_ONE_JUMP, (float) sm_VehicleFlipsAccumulator);
|
|
if(m_statRecordingPolicy==RSP_SUM)
|
|
UpdateRecordedStat(REC_STAT_MOST_FLIPS_IN_ONE_JUMP, 1.0f);
|
|
statDebugf3("Flips accumulator %d", sm_VehicleFlipsAccumulator);
|
|
|
|
sm_VehicleFlipsCounter = false;
|
|
}
|
|
|
|
|
|
void
|
|
CStatsMgr::UpdateVehicleFlipsStunt(CVehicle* playerVehicle)
|
|
{
|
|
statAssert(playerVehicle);
|
|
if (!playerVehicle)
|
|
return;
|
|
else if (!playerVehicle->GetDriver())
|
|
return;
|
|
else if (!playerVehicle->GetDriver()->IsLocalPlayer())
|
|
return;
|
|
|
|
const u32 CRASHED_VEHICLE_FLIPS_INTERVAL = 3*1000;
|
|
|
|
const u32 FLIP_DELAY_BUFFER = 500; // We wait 500ms before sending the event for a flip, in case we crash right after landing
|
|
static u32 s_flipDelayTimer = 0;
|
|
|
|
if(s_flipDelayTimer && s_flipDelayTimer + FLIP_DELAY_BUFFER <= fwTimer::GetSystemTimeInMilliseconds())
|
|
{
|
|
if(s_landedFlip)
|
|
{
|
|
int flips = (int) rage::round(Abs<float>(s_delayedFlipCount/TWO_PI));
|
|
if( flips > 0)
|
|
{
|
|
statDebugf3("Landed flip and didnt crash after delay, sending event : value = %f (%d)", s_delayedFlipCount, flips);
|
|
SendStuntEvent( (s_delayedFlipCount <= 0.0f) ? ST_FRONTFLIP : ST_BACKFLIP , (float) flips);
|
|
}
|
|
}
|
|
statDebugf3("Reset delay timer");
|
|
s_flipDelayTimer = 0;
|
|
s_delayedFlipCount = 0.0f;
|
|
sm_VehicleFlipsHeadingLast = 0.0f;
|
|
}
|
|
|
|
// Is it valid to update the spinning stats?
|
|
if(playerVehicle->HasContactWheels() || (fwTimer::GetTimeInMilliseconds_NonScaledClipped() - sm_CrashedVehicleTimer) < CRASHED_VEHICLE_FLIPS_INTERVAL)
|
|
{
|
|
if(!s_landedFlip)
|
|
{
|
|
s_landedFlip = true;
|
|
}
|
|
if(s_delayedFlipCount != 0.0f && s_flipDelayTimer == 0)
|
|
{
|
|
s_flipDelayTimer = fwTimer::GetSystemTimeInMilliseconds();
|
|
statDebugf3("Landed, FlipCount = %f, starting flip delay", s_delayedFlipCount);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(!s_flipDelayTimer)
|
|
{
|
|
s_landedFlip = false;
|
|
}
|
|
|
|
// Flips
|
|
float fFlip = playerVehicle->GetTransform().GetPitch();
|
|
float fFlipDifference = fFlip - sm_VehicleFlipsHeadingLast;
|
|
|
|
|
|
// Assume wrap around if spun more than 1 rad in a single frame
|
|
if(Abs<float>(fFlipDifference) > 1.0f)
|
|
{
|
|
fFlipDifference = 0.0f;
|
|
}
|
|
|
|
s_delayedFlipCount += fFlipDifference;
|
|
sm_VehicleFlipsHeadingLast = fFlip;
|
|
statDebugf3("Flip accum %.2f = %i flips", s_delayedFlipCount, (int)(s_delayedFlipCount/TWO_PI));
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateVehicleSpins(CVehicle* playerVehicle)
|
|
{
|
|
statAssert(playerVehicle);
|
|
if (!playerVehicle)
|
|
return;
|
|
else if (!playerVehicle->GetDriver())
|
|
return;
|
|
else if (!playerVehicle->GetDriver()->IsLocalPlayer())
|
|
return;
|
|
|
|
const u32 CRASHED_VEHICLE_SPIN_INTERVAL = 3*1000;
|
|
|
|
// Is it valid to update the spinning stats?
|
|
if(playerVehicle->HasContactWheels() || (fwTimer::GetTimeInMilliseconds_NonScaledClipped() - sm_CrashedVehicleTimer) < CRASHED_VEHICLE_SPIN_INTERVAL)
|
|
{
|
|
sm_VehicleSpinsAccumulator = rage::Abs(sm_VehicleSpinsAccumulator);
|
|
|
|
if (sm_VehicleSpinsAccumulator > 0.0f)
|
|
{
|
|
const CVehicleModelInfo* vi = playerVehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ) && statVerify( CanUpdateVehicleRecord(vi->GetHashKey()) ))
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_NumSpins += (u32)(sm_VehicleSpinsAccumulator/TWO_PI);
|
|
}
|
|
}
|
|
|
|
if(sm_VehicleSpinsAccumulator != 0.0f)
|
|
{
|
|
// We are on the ground
|
|
if(sm_VehicleSpinsAccumulator >= PI)
|
|
{
|
|
SendStuntEvent(ST_SPIN, (float)(sm_VehicleSpinsAccumulator / TWO_PI));
|
|
}
|
|
UpdateRecordedStat(REC_STAT_MOST_SPINS_IN_ONE_JUMP, sm_VehicleSpinsAccumulator / TWO_PI);
|
|
sm_VehicleSpinsAccumulator = 0.0f;
|
|
sm_VehicleSpinsHeadingLast = 0.0f;
|
|
}
|
|
return;
|
|
}
|
|
else if(Abs(playerVehicle->GetTransform().GetB().GetZf()) > VEHICLE_FRONT_THRESHOLD)
|
|
{
|
|
// We are vertical: don't reset the accumulator but don't add to it either
|
|
return;
|
|
}
|
|
|
|
statDebugf3("Sping heading accum %.2f = %i spins", sm_VehicleSpinsAccumulator, (int)(sm_VehicleSpinsAccumulator/TWO_PI));
|
|
|
|
float fHeading = playerVehicle->GetTransform().GetHeading();
|
|
float fHeadingDifference = 0.0f;
|
|
if(sm_VehicleSpinsHeadingLast != 0.0f)
|
|
{
|
|
fHeadingDifference = fHeading - sm_VehicleSpinsHeadingLast;
|
|
}
|
|
sm_VehicleSpinsHeadingLast = fHeading;
|
|
|
|
// Assume wrap around if spun more than 180 deg
|
|
if(fHeadingDifference > PI)
|
|
{
|
|
fHeadingDifference = TWO_PI - fHeadingDifference;
|
|
}
|
|
if(fHeadingDifference < -PI)
|
|
{
|
|
fHeadingDifference = TWO_PI + fHeadingDifference;
|
|
}
|
|
sm_VehicleSpinsAccumulator += fHeadingDifference;
|
|
|
|
// url:bugstar:3159843 - Wheels dont touch the ground, but we're in water with an amphibious vehicle
|
|
if(playerVehicle->GetIsInWater() && (VEHICLE_TYPE_AMPHIBIOUS_AUTOMOBILE == playerVehicle->GetVehicleType() || VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE == playerVehicle->GetVehicleType()) )
|
|
{
|
|
statWarningf("Ignore STAT_MOST_SPINS_IN_ONE_JUMP as vehicle is in the water");
|
|
sm_VehicleSpinsAccumulator = 0.0f; // Reset the accumulator so it doesnt update the stat if we jump on a wave
|
|
return;
|
|
}
|
|
|
|
//url:bugstar:4033108 - Dont allow this with the flying car
|
|
if(playerVehicle->GetModelIndex() == MI_CAR_DELUXO || playerVehicle->GetModelIndex() == MI_BIKE_OPPRESSOR2)
|
|
{
|
|
statWarningf("Ignore MI_CAR_DELUXO or MI_BIKE_OPPRESSOR2 as vehicle for spins");
|
|
sm_VehicleSpinsAccumulator = 0.0f;
|
|
return;
|
|
}
|
|
|
|
CStatsMgr::SetGreater(STAT_MOST_SPINS_IN_ONE_JUMP.GetStatId(), (sm_VehicleSpinsAccumulator / TWO_PI));
|
|
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateVehicleRolls(CVehicle* playerVehicle)
|
|
{
|
|
statAssert(playerVehicle);
|
|
if (!playerVehicle)
|
|
return;
|
|
else if (!playerVehicle->GetDriver())
|
|
return;
|
|
else if (!playerVehicle->GetDriver()->IsLocalPlayer())
|
|
return;
|
|
|
|
const u32 CRASHED_VEHICLE_SPIN_INTERVAL = 3*1000;
|
|
|
|
if(playerVehicle->HasContactWheels() || (fwTimer::GetTimeInMilliseconds_NonScaledClipped() - sm_CrashedVehicleTimer) < CRASHED_VEHICLE_SPIN_INTERVAL)
|
|
{
|
|
// We are on the ground
|
|
if(sm_VehicleRollsAccumulator != 0.0f)
|
|
{
|
|
int rolls = (int) rage::round(Abs<float>(sm_VehicleRollsAccumulator/TWO_PI));
|
|
if( rolls > 0)
|
|
{
|
|
SendStuntEvent(ST_ROLL, (float) rolls);
|
|
}
|
|
sm_VehicleRollsAccumulator = 0.0f;
|
|
sm_VehicleRollsHeadingLast = 0.0f;
|
|
}
|
|
return;
|
|
}
|
|
|
|
statDebugf3("Roll heading accum %.2f = %i rolls", sm_VehicleRollsAccumulator, (int)(sm_VehicleRollsAccumulator/TWO_PI));
|
|
|
|
// Barrel rolls
|
|
float fRoll = playerVehicle->GetTransform().GetRoll();
|
|
float fRollDifference = fRoll - sm_VehicleRollsHeadingLast;
|
|
sm_VehicleRollsHeadingLast = fRoll;
|
|
|
|
// Assume wrap around if spun more than 180 deg
|
|
if(fRollDifference > PI)
|
|
{
|
|
fRollDifference = TWO_PI - fRollDifference;
|
|
}
|
|
if(fRollDifference < -PI)
|
|
{
|
|
fRollDifference = TWO_PI + fRollDifference;
|
|
}
|
|
|
|
sm_VehicleRollsAccumulator += fRollDifference;
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::IsAboveOcean(const Vector3& position)
|
|
{
|
|
bool isAboveOcean = false;
|
|
|
|
float waterZ = -1.0f;
|
|
if (Water::GetWaterLevelNoWaves(position, &waterZ, POOL_DEPTH, 999999.9f, NULL))
|
|
{
|
|
phSegment seg;
|
|
phIntersection intersection;
|
|
seg.Set(position, Vector3(position.x, position.y, -999999.9f));
|
|
intersection.Reset();
|
|
|
|
if (CPhysics::GetLevel()->TestProbe(seg, &intersection, NULL, ArchetypeFlags::GTA_MAP_TYPE_MOVER))
|
|
{
|
|
if (waterZ > intersection.GetPosition().GetZf())
|
|
{
|
|
const float minZ = CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(position.x, position.y);
|
|
const float maxZ = CGameWorldHeightMap::GetMaxHeightFromWorldHeightMap(position.x, position.y);
|
|
|
|
if(waterZ == 0.0f && minZ == 0.0f)
|
|
{
|
|
//We are for sure in a Ocean
|
|
if (IsClose(minZ, maxZ, 0.1f))
|
|
{
|
|
isAboveOcean = true;
|
|
}
|
|
else if (audShoreLineOcean::GetClosestShore())
|
|
{
|
|
isAboveOcean = (audShoreLineOcean::IsListenerOverOcean() && audShoreLineOcean::GetSqdDistanceIntoWater() < 1000.0f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
isAboveOcean = true; // no ground here, we're far off shore
|
|
}
|
|
}
|
|
|
|
return isAboveOcean;
|
|
}
|
|
|
|
bool CStatsMgr::IsPlayerVehicleAboveOcean()
|
|
{
|
|
CPed* player = FindPlayerPed();
|
|
if(statVerify(player))
|
|
{
|
|
CVehicle* playerVehicle = player->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle) ? player->GetMyVehicle() : NULL;
|
|
if(statVerify(playerVehicle))
|
|
{
|
|
Vec3V coords = playerVehicle->GetTransform().GetPosition();
|
|
|
|
const Vec2V position = Vec2V(VEC3V_TO_VECTOR3(coords).x, VEC3V_TO_VECTOR3(coords).y);
|
|
return IsAboveForbiddenArea(position) || IsAboveOcean(VEC3V_TO_VECTOR3(coords));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CStatsMgr::IsAboveForbiddenArea(const Vec2V& position)
|
|
{
|
|
for(int i = 0; i < sm_TuneMetadata.m_nonFlyableAreas.m_areas.size(); i++)
|
|
{
|
|
const NonFlyableArea& area = sm_TuneMetadata.m_nonFlyableAreas.m_areas[i];
|
|
// simple inside rectangle check
|
|
bool contains = area.m_rectXYWH.x < position.GetXf()
|
|
&& area.m_rectXYWH.x + area.m_rectXYWH.w > position.GetXf()
|
|
&& area.m_rectXYWH.y < position.GetYf()
|
|
&& area.m_rectXYWH.y + area.m_rectXYWH.z > position.GetYf();
|
|
if(contains)
|
|
{
|
|
// we dont want to update the challenges
|
|
statDebugf3("Above an area excluded from the flying challenges");
|
|
sm_FlyingCounter = 0.0f;
|
|
sm_FlyingDistance = 0.0f;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateFlyingVehicle(CVehicle* playerVehicle)
|
|
{
|
|
statAssert(playerVehicle);
|
|
if (!playerVehicle)
|
|
return;
|
|
else if (!playerVehicle->GetDriver())
|
|
return;
|
|
else if (!playerVehicle->GetDriver()->IsLocalPlayer())
|
|
return;
|
|
|
|
bool isInAir = playerVehicle->IsInAir();
|
|
if(playerVehicle->GetFrameCollisionHistory()->GetMaxCollisionImpulseMagLastFrame() > 0.0f)
|
|
{
|
|
// we collided with something and it's not an object (then has to be the ground or a building)
|
|
const CCollisionRecord *pCollRecord = playerVehicle->GetFrameCollisionHistory()->GetMostSignificantCollisionRecordOfType(ENTITY_TYPE_OBJECT);
|
|
if(!pCollRecord)
|
|
{
|
|
isInAir = false;
|
|
}
|
|
}
|
|
|
|
Vec3V coords = playerVehicle->GetTransform().GetPosition();
|
|
const Vec2V position = Vec2V(VEC3V_TO_VECTOR3(coords).x, VEC3V_TO_VECTOR3(coords).y);
|
|
|
|
if(IsAboveForbiddenArea(position))
|
|
return;
|
|
|
|
if(isInAir)
|
|
{
|
|
sm_drivingFlyingVehicle = true;
|
|
sm_InAirTimer += fwTimer::GetTimeStepInMilliseconds();
|
|
|
|
if (!sm_GroundProbeHelper.IsProbeActive())
|
|
{
|
|
if (sm_FlyingPlanePosHeight.IsZero())
|
|
{
|
|
sm_FlyingPlanePosHeight = VEC3V_TO_VECTOR3(coords);
|
|
}
|
|
}
|
|
|
|
sm_InAirTimer += fwTimer::GetTimeStepInMilliseconds();
|
|
|
|
if(m_recordedStat == REC_STAT_FLIGHT_TIME_BELOW_20 || m_recordedStat == REC_STAT_FLIGHT_DIST_BELOW_20)
|
|
{
|
|
if(sm_ValidFlyingAltitude && sm_FlyingAltitude < 20.0f && !IsAboveOcean(VEC3V_TO_VECTOR3(coords))) // Under 20m and not above ocean
|
|
{
|
|
if(playerVehicle->GetVelocity().Mag2() > MIN_PLANE_SPEED_TO_CONSIDER_MOVEMENT)
|
|
{
|
|
sm_FlyingCounter += fwTimer::GetSystemTimeStepInMilliseconds();
|
|
UpdateRecordedStat(REC_STAT_FLIGHT_TIME_BELOW_20, sm_FlyingCounter);
|
|
}
|
|
|
|
if(!sm_LastFlyingPlanePosition.IsZero())
|
|
{
|
|
Vec3V position = coords;
|
|
position.SetZf(0.0f);
|
|
Vec3V lastPosition = VECTOR3_TO_VEC3V(sm_LastFlyingPlanePosition);
|
|
lastPosition.SetZf(0.0f);
|
|
sm_FlyingDistance += Mag(position - lastPosition).Getf();
|
|
}
|
|
|
|
UpdateRecordedStat(REC_STAT_FLIGHT_DIST_BELOW_20, sm_FlyingDistance);
|
|
}
|
|
else
|
|
{
|
|
sm_FlyingCounter = 0.0f;
|
|
sm_FlyingDistance = 0.0f;
|
|
}
|
|
}
|
|
if(m_recordedStat == REC_STAT_FLIGHT_TIME_BELOW_100 || m_recordedStat == REC_STAT_FLIGHT_DIST_BELOW_100)
|
|
{
|
|
if(sm_ValidFlyingAltitude && sm_FlyingAltitude < 100.0f && !IsAboveOcean(VEC3V_TO_VECTOR3(coords)))
|
|
{
|
|
Matrix34 planeMtx = MAT34V_TO_MATRIX34(playerVehicle->GetTransformPtr()->GetMatrix());
|
|
Vector3 planeUp = planeMtx.c;
|
|
|
|
if (planeUp.Dot(Vector3(0,0,1)) < CStatsMgr::minInvertFlightDot)
|
|
{
|
|
// Plane is on the back
|
|
if(playerVehicle->GetVelocity().Mag2() > MIN_SPEED_TO_CONSIDER_MOVEMENT)
|
|
{
|
|
sm_FlyingCounter += fwTimer::GetSystemTimeStepInMilliseconds();
|
|
UpdateRecordedStat(REC_STAT_FLIGHT_TIME_BELOW_100, sm_FlyingCounter);
|
|
}
|
|
|
|
float distanceThisFrame = 0.0f;
|
|
if(!sm_LastFlyingPlanePosition.IsZero())
|
|
{
|
|
Vec3V position = coords;
|
|
position.SetZf(0.0f);
|
|
Vec3V lastPosition = VECTOR3_TO_VEC3V(sm_LastFlyingPlanePosition);
|
|
lastPosition.SetZf(0.0f);
|
|
distanceThisFrame = Mag(position - lastPosition).Getf();
|
|
sm_FlyingDistance += distanceThisFrame;
|
|
}
|
|
if(m_statRecordingPolicy == RSP_SUM)
|
|
{
|
|
UpdateRecordedStat(REC_STAT_FLIGHT_DIST_BELOW_100, distanceThisFrame);
|
|
}
|
|
else
|
|
{
|
|
UpdateRecordedStat(REC_STAT_FLIGHT_DIST_BELOW_100, sm_FlyingDistance);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sm_FlyingCounter = 0.0f;
|
|
sm_FlyingDistance = 0.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sm_FlyingCounter = 0.0f;
|
|
sm_FlyingDistance = 0.0f;
|
|
}
|
|
}
|
|
|
|
sm_LastFlyingPlanePosition = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetPosition());
|
|
}
|
|
else
|
|
{
|
|
sm_PlaneRollAccumulator = 0.0f;
|
|
sm_PlaneRollLast = 0.0f;
|
|
sm_FlyingCounter = 0.0f;
|
|
sm_FlyingDistance = 0.0f;
|
|
sm_FlyingAltitude = 0.0f;
|
|
sm_ValidFlyingAltitude = false;
|
|
sm_drivingFlyingVehicle = false;
|
|
sm_LastFlyingPlanePosition.Zero();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
CStatsMgr::UpdatePlane(CVehicle* playerVehicle)
|
|
{
|
|
statAssert(playerVehicle);
|
|
if (!playerVehicle)
|
|
return;
|
|
else if (!playerVehicle->GetDriver())
|
|
return;
|
|
else if (!playerVehicle->GetDriver()->IsLocalPlayer())
|
|
return;
|
|
|
|
// Landing
|
|
static dev_u32 snMinLandingTime = 30000;
|
|
static dev_u32 snMinCollTimePassed = 2000;
|
|
if(!playerVehicle->IsInAir() || playerVehicle->GetFrameCollisionHistory()->GetMaxCollisionImpulseMagLastFrame() > 0.0f)
|
|
{
|
|
if(sm_InAirTimer > snMinLandingTime)
|
|
{
|
|
sm_LandingTimer = sm_InAirTimer = snMinLandingTime;
|
|
}
|
|
|
|
u32 nMinTimeSinceLastCollision = playerVehicle->GetFrameCollisionHistory()->GetMostRecentCollisionTime() + snMinCollTimePassed;
|
|
if(sm_LandingTimer > 0 && playerVehicle->GetNumContactWheels() == playerVehicle->GetNumWheels() &&
|
|
fwTimer::GetTimeInMilliseconds() > nMinTimeSinceLastCollision)
|
|
{
|
|
sm_LandingTimer = 0;
|
|
sm_InAirTimer = 0;
|
|
CStatsMgr::IncrementStat(StatsInterface::GetStatsModelHashId("PLANE_LANDINGS"), 1.0f);
|
|
|
|
const CVehicleModelInfo* vi = playerVehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ) && statVerify( CanUpdateVehicleRecord(vi->GetHashKey()) ))
|
|
{
|
|
++sm_vehicleRecords[sm_curVehIdx].m_NumPlaneLandings;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sm_LandingTimer -= fwTimer::GetTimeStepInMilliseconds();
|
|
if(sm_LandingTimer < 0)
|
|
{
|
|
sm_LandingTimer = 0;
|
|
sm_InAirTimer = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateFlyingVehicle(playerVehicle);
|
|
|
|
if(playerVehicle->IsInAir())
|
|
{
|
|
// Barrel rolls
|
|
float fRoll = playerVehicle->GetTransform().GetRoll();
|
|
float fRollDifference = fRoll - sm_PlaneRollLast;
|
|
sm_PlaneRollLast = fRoll;
|
|
|
|
// Assume wrap around if spun more than 180 deg
|
|
if(fRollDifference > PI)
|
|
{
|
|
fRollDifference = TWO_PI - fRollDifference;
|
|
}
|
|
if(fRollDifference < -PI)
|
|
{
|
|
fRollDifference = TWO_PI + fRollDifference;
|
|
}
|
|
|
|
sm_PlaneRollAccumulator += fRollDifference;
|
|
if(rage::Abs(sm_PlaneRollAccumulator) >= TWO_PI)
|
|
{
|
|
sm_PlaneBarrelRollCounter+=1.0f;
|
|
sm_PlaneRollAccumulator=0.0f;
|
|
}
|
|
UpdateRecordedStat(REC_STAT_PLANE_BARREL_ROLLS, sm_PlaneBarrelRollCounter);
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateHeli(CVehicle* playerVehicle)
|
|
{
|
|
statAssert(playerVehicle);
|
|
if (!playerVehicle)
|
|
return;
|
|
else if (!playerVehicle->GetDriver())
|
|
return;
|
|
else if (!playerVehicle->GetDriver()->IsLocalPlayer())
|
|
return;
|
|
|
|
UpdateFlyingVehicle(playerVehicle);
|
|
}
|
|
|
|
void
|
|
CStatsMgr::PlayerEnteredVehicle(const CVehicle* vehicle)
|
|
{
|
|
if (vehicle)
|
|
{
|
|
const u16 seed = vehicle->GetRandomSeed();
|
|
if (sm_LastVehicleStolenForChallenge == seed)
|
|
{
|
|
UpdateRecordedStat(REC_STAT_NUMBER_STOLEN_VEHICLE, 1.0f);
|
|
sm_LastVehicleStolenForChallenge=0;
|
|
}
|
|
sm_curVehIdx = INVALID_RECORD;
|
|
|
|
const CVehicleModelInfo* vi = vehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ))
|
|
{
|
|
StartNewVehicleRecords( vi->GetHashKey() );
|
|
if (statVerify( CanUpdateVehicleRecord( vi->GetHashKey() ) ))
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_LastDistDriven = 0.0f;
|
|
sm_vehicleRecords[sm_curVehIdx].m_LastTimeSpentInVehicle = sysTimer::GetSystemMsTime();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::PlayerLeftVehicle(const CVehicle* vehicle)
|
|
{
|
|
if (vehicle)
|
|
{
|
|
const CVehicleModelInfo* vi = vehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ))
|
|
{
|
|
const u32 idx = FindVehicleRecord( vi->GetHashKey() );
|
|
if (idx<CStatsMgr::INVALID_RECORD)
|
|
sm_vehicleRecords[idx].EndVehicleStats();
|
|
}
|
|
}
|
|
|
|
sm_CurrentSpeed = 0.0f;
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::UpdateVehiclesStolen(const CVehicle* vehicle)
|
|
{
|
|
if (!IsPlayerActive())
|
|
return false;
|
|
|
|
if (!statVerify(vehicle))
|
|
return false;
|
|
|
|
const u16 seed = vehicle->GetRandomSeed();
|
|
|
|
if (sm_LastVehicleStolenSeed == seed)
|
|
return false;
|
|
|
|
sm_LastVehicleStolenSeed = seed;
|
|
|
|
// Don't increment NUMBER_STOLEN_COP_VEHICLE if the cop vehicle is a player's personal vehicle (unless in the CNC mode).
|
|
if (vehicle->IsLawEnforcementVehicle() && (!const_cast<CVehicle*>(vehicle)->IsPersonalVehicle() || NetworkInterface::IsInCopsAndCrooks()))
|
|
{
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("NUMBER_STOLEN_COP_VEHICLE"), 1.0f);
|
|
}
|
|
|
|
const s32 type = vehicle->GetVehicleType();
|
|
bool countsAsVehicleStolen=false;
|
|
switch(type)
|
|
{
|
|
case VEHICLE_TYPE_CAR:
|
|
case VEHICLE_TYPE_AMPHIBIOUS_AUTOMOBILE:
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("NUMBER_STOLEN_CARS"), 1.0f); countsAsVehicleStolen=true; break;
|
|
case VEHICLE_TYPE_PLANE: StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("NUMBER_STOLEN_PLANES"), 1.0f); countsAsVehicleStolen=true; break;
|
|
case VEHICLE_TYPE_TRAILER: StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("NUMBER_STOLEN_TRAILERS"), 1.0f); countsAsVehicleStolen=true; break;
|
|
case VEHICLE_TYPE_QUADBIKE:
|
|
case VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE:
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("NUMBER_STOLEN_QUADBIKES"), 1.0f); countsAsVehicleStolen=true; break;
|
|
case VEHICLE_TYPE_HELI: StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("NUMBER_STOLEN_HELIS"), 1.0f); countsAsVehicleStolen=true; break;
|
|
case VEHICLE_TYPE_BIKE: StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("NUMBER_STOLEN_BIKES"), 1.0f); countsAsVehicleStolen=true; break;
|
|
case VEHICLE_TYPE_BICYCLE: StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("NUMBER_STOLEN_BICYCLES"), 1.0f); countsAsVehicleStolen=true; break;
|
|
case VEHICLE_TYPE_BOAT: StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("NUMBER_STOLEN_BOATS"), 1.0f); countsAsVehicleStolen=true; break;
|
|
|
|
case VEHICLE_TYPE_AUTOGYRO:
|
|
case VEHICLE_TYPE_SUBMARINE:
|
|
case VEHICLE_TYPE_TRAIN:
|
|
statWarningf("Vehicle %d has been stolen but hasn't been incremented", type);
|
|
break;
|
|
|
|
default:
|
|
statErrorf("Unknown vehicle type \"id=%d : type=%d\" - CStatsMgr::UpdateVehiclesStolen() ", seed, type);
|
|
break;
|
|
}
|
|
|
|
const CVehicleModelInfo* vi = vehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ))
|
|
{
|
|
StartNewVehicleRecords( vi->GetHashKey() );
|
|
if (statVerify( CanUpdateVehicleRecord( vi->GetHashKey() ) ))
|
|
{
|
|
++sm_vehicleRecords[sm_curVehIdx].m_NumStolen;
|
|
}
|
|
}
|
|
if(countsAsVehicleStolen)
|
|
sm_LastVehicleStolenForChallenge = seed;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::EndVehicleStats( )
|
|
{
|
|
if (sm_WasInVehicle)
|
|
{
|
|
sm_VehicleDeadCheckCounter = 0;
|
|
sm_VehicleDeadCheck = true;
|
|
|
|
sm_WasInVehicle = false;
|
|
|
|
//If the player is Driving in cinematic camera
|
|
if (sm_DriveInCinematic > 0.0f)
|
|
{
|
|
StatsInterface::SetGreater(STAT_LONGEST_CAM_TIME_DRIVING.GetStatId(), sm_DriveInCinematic);
|
|
}
|
|
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
|
|
CheckCrashDamageAccumulator( true );
|
|
|
|
ClearPlayerVehicleStats();
|
|
|
|
//End current checks.
|
|
sm_curVehIdx = CStatsMgr::INVALID_RECORD;
|
|
for (int i=0; i<sm_vehicleRecords.GetCount(); i++)
|
|
sm_vehicleRecords[i].EndVehicleStats();
|
|
|
|
//Check for stale records.
|
|
for (int i=0; i<sm_vehicleRecords.GetCount(); i++)
|
|
{
|
|
if (sm_vehicleRecords[i].CanBeCleared())
|
|
{
|
|
sm_vehicleRecords[i].Clear(true);
|
|
|
|
for (int j=i; j<sm_vehicleRecords.GetCount()-1; j++)
|
|
{
|
|
sm_vehicleRecords[j] = sm_vehicleRecords[j+1];
|
|
sm_vehicleRecords[j+1].Clear(true);
|
|
}
|
|
|
|
sm_vehicleRecords.Resize(sm_vehicleRecords.GetCount()-1);
|
|
}
|
|
}
|
|
|
|
sm_VehiclePreviousPosition.Zero();
|
|
sm_ReverseDrivingPosCurrent.Zero();
|
|
sm_CurrentDrivingReverseDistance=0.0f;
|
|
}
|
|
|
|
//Check for player dying and invalidate some stats.
|
|
if (sm_VehicleDeadCheck)
|
|
{
|
|
sm_VehicleDeadCheckCounter += fwTimer::GetSystemTimeStep() * 1000.0f;
|
|
if (sm_VehicleDeadCheckCounter > DEAD_CHECK_INTERVAL)
|
|
{
|
|
sm_VehicleDeadCheck = false;
|
|
sm_VehicleDeadCheckCounter = 0;
|
|
|
|
CPed* player = FindPlayerPed();
|
|
|
|
//Player is not dead
|
|
if (player && !player->IsDead())
|
|
{
|
|
//We are Tracking the longest drive without a crash
|
|
//Check if we are driving or not...
|
|
if (sm_DriveNoCrashTime > 0)
|
|
{
|
|
StatsInterface::SetGreater(STAT_LONGEST_DRIVE_NOCRASH.GetStatId(), sm_DriveNoCrashDist);
|
|
}
|
|
}
|
|
|
|
sm_DriveNoCrashTime = 0.0f;
|
|
sm_DriveNoCrashDist = 0.0f;
|
|
sm_NearMissNoCrash = 0;
|
|
sm_JumpDistance=0.0f;
|
|
sm_HadAttachedParentVehicle=false;
|
|
sm_NearMissNoCrashPrecise = 0;
|
|
ClearNearMissArray();
|
|
}
|
|
}
|
|
}
|
|
|
|
static const int FOOTBALL_HIT_HISTORY_SIZE = 15;
|
|
static fwEntity* s_LastHitFootballs[FOOTBALL_HIT_HISTORY_SIZE] = {0};
|
|
static int s_lastHitFoolballIndex = 0;
|
|
|
|
void
|
|
CStatsMgr::CheckCrashDamageAccumulator( const bool force )
|
|
{
|
|
|
|
CPed* playerPed = FindPlayerPed();
|
|
if (playerPed)
|
|
{
|
|
CVehicle* playerVehicle = playerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle) ? playerPed->GetMyVehicle() : NULL;
|
|
if(playerVehicle)
|
|
{
|
|
const CCollisionRecord* colRecord = (playerVehicle->GetFrameCollisionHistory()) ? playerVehicle->GetFrameCollisionHistory()->GetFirstObjectCollisionRecord() : nullptr;
|
|
if(colRecord)
|
|
{
|
|
static const atHashString PIN_MODEL_HASH = ATSTRINGHASH("stt_prop_stunt_bowling_pin", 0x5779131A);
|
|
static const atHashString FOOTBALL_MODEL_HASH = ATSTRINGHASH("stt_prop_stunt_soccer_lball", 0xDB2C3E38);
|
|
static const atHashString FOOTBALL_MODEL_HASH_2 = ATSTRINGHASH("stt_prop_stunt_soccer_ball", 0xC00C3530);
|
|
static const atHashString FOOTBALL_MODEL_HASH_3 = ATSTRINGHASH("stt_prop_stunt_soccer_sball", 0x286F5F0);
|
|
|
|
u32 hash = (colRecord->m_pRegdCollisionEntity.Get() && colRecord->m_pRegdCollisionEntity.Get()->GetArchetype()) ? colRecord->m_pRegdCollisionEntity.Get()->GetArchetype()->GetModelNameHash() : 0;
|
|
if(hash == PIN_MODEL_HASH.GetHash())
|
|
{
|
|
static fwEntity* s_lastKnockedDownPin = nullptr;
|
|
fwEntity* bowlingPin = colRecord->m_pRegdCollisionEntity.Get();
|
|
if(s_lastKnockedDownPin != bowlingPin)
|
|
{
|
|
Vector3 v = VEC3V_TO_VECTOR3(bowlingPin->GetTransform().GetUp());
|
|
Vector3 up(0,1,0);
|
|
static const float STANDING_PIN_THRESHOLD = 0.1f;
|
|
float dotProduct = v.Dot(up);
|
|
if(v.z > 0.0f && Abs(dotProduct) <= STANDING_PIN_THRESHOLD) // the pin is standing
|
|
{
|
|
statDebugf3("Crash into a bowling pin : up=(%f, %f, %f) - Dot = %f", v.x, v.y, v.z, dotProduct);
|
|
SendStuntEvent(ST_BOWLING_PIN, 1.0f);
|
|
s_lastKnockedDownPin = bowlingPin;
|
|
}
|
|
else
|
|
{
|
|
statDebugf3("Crash into a bowling pin but it's not standing : up=(%f, %f, %f) - Dot = %f", v.x, v.y, v.z, dotProduct);
|
|
}
|
|
}
|
|
}
|
|
else if(hash == FOOTBALL_MODEL_HASH.GetHash() || hash == FOOTBALL_MODEL_HASH_2.GetHash() || hash == FOOTBALL_MODEL_HASH_3.GetHash())
|
|
{
|
|
fwEntity* football = colRecord->m_pRegdCollisionEntity.Get();
|
|
bool alreadyHit = false;
|
|
for(int i=0; i<FOOTBALL_HIT_HISTORY_SIZE && !alreadyHit; i++)
|
|
{
|
|
if(football == s_LastHitFootballs[i])
|
|
{
|
|
alreadyHit = true;
|
|
statDebugf3("Football already hit");
|
|
break;
|
|
}
|
|
}
|
|
if(!alreadyHit)
|
|
{
|
|
statDebugf3("Crash into a football");
|
|
SendStuntEvent(ST_FOOTBALL, 1.0f);
|
|
s_LastHitFootballs[s_lastHitFoolballIndex] = football;
|
|
s_lastHitFoolballIndex++;
|
|
if(s_lastHitFoolballIndex >= FOOTBALL_HIT_HISTORY_SIZE)
|
|
{
|
|
s_lastHitFoolballIndex = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s_isTrackingStunts)
|
|
{
|
|
statDebugf3("Collided with '%s', ignored.", atHashString(hash).TryGetCStr());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sm_CrashedVehicleTimer > 0 && (sm_CrashedVehicleTimer+CRASHED_VEHICLE_INTERVAL <= sysTimer::GetSystemMsTime() || force))
|
|
{
|
|
if (sm_CrashedVehicleKeyHash > 0 && sm_CrashedVehicleDamageAccumulator > 0.0f)
|
|
{
|
|
float largeAccidenThresold = sm_TuneMetadata.m_SPLargeAccidenThresold;
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
largeAccidenThresold = sm_TuneMetadata.m_MPLargeAccidenThresold;
|
|
}
|
|
|
|
// We crashed, reset flips count
|
|
if(s_delayedFlipCount > 0.0f && largeAccidenThresold <= sm_CrashedVehicleDamageAccumulator)
|
|
{
|
|
s_delayedFlipCount = 0.0f;
|
|
statDebugf3("Crashed above large accident threshold, resetting flip count");
|
|
}
|
|
s_landedFlip = true;
|
|
|
|
|
|
CPed* playerPed = FindPlayerPed();
|
|
if (playerPed)
|
|
{
|
|
CVehicle* playerVehicle = playerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle) ? playerPed->GetMyVehicle() : NULL;
|
|
if(playerVehicle)
|
|
{
|
|
// Check if we are landing on our wheels (more or less) or not
|
|
Vector3 v = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetUp());
|
|
Vector3 up(0,1,0);
|
|
static const float FLIP_LANDING_THRESHOLD = 0.5f;
|
|
float dotProduct = v.Dot(up);
|
|
if(v.z > 0.0f && Abs(dotProduct) <= FLIP_LANDING_THRESHOLD)
|
|
{
|
|
statDebugf3("Crashed correctly - dotProduct = %f - v=(%f, %f, %f)", dotProduct, v.x, v.y, v.z);
|
|
}
|
|
else
|
|
{
|
|
statDebugf3("Crashed not flat - dotProduct = %f - v=(%f, %f, %f)", dotProduct, v.x, v.y, v.z);
|
|
s_delayedFlipCount = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
fwModelId modelId;
|
|
CBaseModelInfo* mi = CModelInfo::GetBaseModelInfoFromHashKey(sm_CrashedVehicleKeyHash, &modelId);
|
|
if (mi)
|
|
{
|
|
statDebugf3("CheckCrashDamageAccumulator Vehicle crash - \"%s : %u\" , Accumulator=%f", mi->GetModelName(), sm_CrashedVehicleKeyHash, sm_CrashedVehicleDamageAccumulator);
|
|
|
|
CVehicleModelInfo* vmi = static_cast< CVehicleModelInfo* >( mi );
|
|
|
|
switch(vmi->GetVehicleType())
|
|
{
|
|
case VEHICLE_TYPE_CAR:
|
|
case VEHICLE_TYPE_AMPHIBIOUS_AUTOMOBILE:
|
|
CStatsMgr::IncrementStat(STAT_TOTAL_DAMAGE_CARS.GetStatId(), sm_CrashedVehicleDamageAccumulator);
|
|
StatsInterface::IncrementStat(STAT_NUMBER_CRASHES_CARS.GetStatId(), 1.0f);
|
|
if (largeAccidenThresold <= sm_CrashedVehicleDamageAccumulator)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_LARGE_ACCIDENTS.GetStatId(), 1.0f);
|
|
|
|
u32 vehIdx = FindVehicleRecord( sm_CrashedVehicleKeyHash );
|
|
if (statVerifyf( vehIdx != INVALID_RECORD, "No record found for vehicle %u", sm_CrashedVehicleKeyHash) && statVerify(sm_vehicleRecords[vehIdx].IsValid()))
|
|
{
|
|
if (StatsInterface::GetStatsModelPrefix() == sm_vehicleRecords[vehIdx].m_CharacterId)
|
|
{
|
|
sm_vehicleRecords[vehIdx].m_NumLargeAccidents += 1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case VEHICLE_TYPE_QUADBIKE:
|
|
case VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE:
|
|
CStatsMgr::IncrementStat(STAT_TOTAL_DAMAGE_QUADBIKES.GetStatId(), sm_CrashedVehicleDamageAccumulator);
|
|
StatsInterface::IncrementStat(STAT_NUMBER_CRASHES_QUADBIKES.GetStatId(), 1.0f);
|
|
break;
|
|
case VEHICLE_TYPE_BIKE:
|
|
CStatsMgr::IncrementStat(STAT_TOTAL_DAMAGE_BIKES.GetStatId(), sm_CrashedVehicleDamageAccumulator);
|
|
StatsInterface::IncrementStat(STAT_NUMBER_CRASHES_BIKES.GetStatId(), 1.0f);
|
|
break;
|
|
|
|
case VEHICLE_TYPE_BICYCLE:
|
|
case VEHICLE_TYPE_AUTOGYRO:
|
|
case VEHICLE_TYPE_TRAILER:
|
|
case VEHICLE_TYPE_HELI:
|
|
case VEHICLE_TYPE_PLANE:
|
|
case VEHICLE_TYPE_BOAT:
|
|
case VEHICLE_TYPE_SUBMARINE:
|
|
case VEHICLE_TYPE_TRAIN:
|
|
break;
|
|
|
|
default:
|
|
statErrorf("Unknown vehicle type \"id=%s : type=%d\" - CStatsMgr::UpdateVehiclesStolen() ", vmi->GetGameName(), vmi->GetVehicleType());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
sm_CrashedVehicleTimer = 0;
|
|
sm_CrashedVehicleKeyHash = 0;
|
|
sm_CrashedVehicleDamageAccumulator = 0.0f;
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::ClearPlayerVehicleStats()
|
|
{
|
|
sm_BestCarTwoWheelsTimeMs = 0;
|
|
sm_BestCarTwoWheelsDistM = 0.0f;
|
|
sm_BestBikeWheelieTimeMs = 0;
|
|
sm_BestBikeWheelieDistM = 0.0f;
|
|
sm_BestBikeStoppieTimeMs = 0;
|
|
sm_BestBikeStoppieDistM = 0.0f;
|
|
sm_BikeRearWheelCounter = 0;
|
|
sm_BikeRearWheelDist = 0.0f;
|
|
sm_BikeFrontWheelCounter = 0;
|
|
sm_BikeFrontWheelDist = 0.0f;
|
|
sm_CarTwoWheelCounter = 0;
|
|
sm_CarTwoWheelDist = 0.0f;
|
|
sm_CarLess3WheelCounter = 0.0f;
|
|
sm_DriveNoCrashDist = 0.0f;
|
|
sm_DriveNoCrashTime = 0.0f;
|
|
sm_DriveInCinematic = 0.0f;
|
|
sm_FlyingAltitude = 0.0f;
|
|
sm_IsWheelingOnPushBike = false;
|
|
sm_IsDoingAStoppieOnPushBike = false;
|
|
sm_InAirTimer = 0;
|
|
sm_invalidJump = false;
|
|
sm_TempBufferCounter = 0;
|
|
sm_LandingTimer = 0;
|
|
sm_ZeroWheelPosStart.Zero();
|
|
sm_ZeroWheelPosEnd.Zero();
|
|
sm_ChallengeZeroWheelPosStart.Zero();
|
|
sm_VehiclePreviousPosition.Zero();
|
|
sm_ValidFlyingAltitude = false;
|
|
sm_VehicleFlipsCounter = false;
|
|
sm_VehicleFlipsAccumulator = 0;
|
|
sm_VehicleSpinsAccumulator = 0.0f;
|
|
sm_VehicleSpinsHeadingLast = 0.0f;
|
|
sm_VehicleRollsAccumulator = 0.0f;
|
|
sm_VehicleRollsHeadingLast = 0.0f;
|
|
sm_ZeroWheelCounter = 0;
|
|
sm_ZeroWheelBufferCounter = 0;
|
|
s_delayedFlipCount = 0.0f;
|
|
sm_VehicleFlipsHeadingLast = 0.0f;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::StartVehicleGroundProbe()
|
|
{
|
|
//A probe is already active
|
|
if (sm_GroundProbeHelper.IsProbeActive())
|
|
return;
|
|
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
|
|
sm_drivingFlyingVehicleForProbe = sm_drivingFlyingVehicle;
|
|
|
|
// Dont probe if the challenge is not running
|
|
if(sm_drivingFlyingVehicleForProbe &&
|
|
( m_recordedStat != REC_STAT_FLIGHT_TIME_BELOW_20 &&
|
|
m_recordedStat != REC_STAT_FLIGHT_TIME_BELOW_100 &&
|
|
m_recordedStat != REC_STAT_FLIGHT_DIST_BELOW_20 &&
|
|
m_recordedStat != REC_STAT_FLIGHT_DIST_BELOW_100 )
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Too soon to start another probe
|
|
if (!sm_drivingFlyingVehicleForProbe && sm_TimeSinceLastGroundProbe + TIME_BETWEEN_GROUND_PROBES >= fwTimer::GetTimeInMilliseconds())
|
|
return;
|
|
|
|
//Start the probe.
|
|
if (sm_ZeroWheelCounter == 0 && !sm_ZeroWheelPosHeight.IsZero())
|
|
{
|
|
statVerify( CStatsMgr::StartGroundProbe(sm_ZeroWheelPosHeight) );
|
|
}
|
|
//Start the probe for planes
|
|
if (sm_drivingFlyingVehicleForProbe)
|
|
{
|
|
statDebugf3("StartVehicleGroundProbe : sm_FlyingPlanePosHeight <x=%f,y=%f,z=%f> ", sm_FlyingPlanePosHeight.x, sm_FlyingPlanePosHeight.y, sm_FlyingPlanePosHeight.z);
|
|
if (!sm_FlyingPlanePosHeight.IsZero())
|
|
{
|
|
Vector3 startPos = sm_FlyingPlanePosHeight;
|
|
startPos.z = CGameWorldHeightMap::GetMaxHeightFromWorldHeightMap(sm_FlyingPlanePosHeight.x, sm_FlyingPlanePosHeight.y);
|
|
statVerify( CStatsMgr::StartGroundProbe(startPos) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::CheckVehicleGroundProbeResults()
|
|
{
|
|
//No probe is in progress
|
|
if (!sm_GroundProbeHelper.IsProbeActive())
|
|
return;
|
|
|
|
//No valid probe position
|
|
if(sm_ZeroWheelPosHeight.IsZero() && sm_FlyingPlanePosHeight.IsZero())
|
|
{
|
|
//Something is wrong and the Ground Probe must be Reset.
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
return;
|
|
}
|
|
|
|
//Check the probe
|
|
Vector3 probeStartPos = (sm_drivingFlyingVehicleForProbe) ? sm_FlyingPlanePosHeight : sm_ZeroWheelPosHeight;
|
|
if(sm_drivingFlyingVehicleForProbe)
|
|
{
|
|
// We start the probe from the position of the plane, or the max heightmap altitude if closer to the ground
|
|
float maxHeightMap = CGameWorldHeightMap::GetMaxHeightFromWorldHeightMap(sm_FlyingPlanePosHeight.x, sm_FlyingPlanePosHeight.y);
|
|
statDebugf3("Plane Position when starting probe Coords=<x=%f,y=%f,z=%f> maxHeightmap=%f", sm_FlyingPlanePosHeight.x, sm_FlyingPlanePosHeight.y, sm_FlyingPlanePosHeight.z, maxHeightMap);
|
|
probeStartPos.z = (maxHeightMap>sm_FlyingPlanePosHeight.z) ? sm_FlyingPlanePosHeight.z : maxHeightMap;
|
|
}
|
|
|
|
float altitude = 0.0f;
|
|
if (CheckGroundProbeResults(altitude, probeStartPos))
|
|
{
|
|
if(sm_drivingFlyingVehicleForProbe)
|
|
{
|
|
// Probe was started from max heightmap or plane position, compute the real altitude now
|
|
float heightmapz = CGameWorldHeightMap::GetMaxHeightFromWorldHeightMap(sm_FlyingPlanePosHeight.x, sm_FlyingPlanePosHeight.y);
|
|
|
|
if(heightmapz<sm_FlyingPlanePosHeight.z)
|
|
sm_FlyingAltitude = altitude+sm_FlyingPlanePosHeight.z-heightmapz;
|
|
else
|
|
sm_FlyingAltitude = altitude;
|
|
|
|
if(sm_FlyingAltitude < 0.0f) // We crashed too hard in the ground
|
|
sm_FlyingAltitude = 0.0f;
|
|
|
|
sm_ValidFlyingAltitude = true;
|
|
statDebugf3("Plane Ground Probe Results: Start Coords=<x=%f,y=%f,z=%f> altitude=<%f>", probeStartPos.x, probeStartPos.y, probeStartPos.z, sm_FlyingAltitude);
|
|
}
|
|
else
|
|
{
|
|
if(!sm_invalidJump)
|
|
{
|
|
statDebugf3("Vehicle Ground Probe Results: Start Coords=<x=%f,y=%f,z=%f> altitude=<%f>", probeStartPos.x, probeStartPos.y, probeStartPos.z, altitude);
|
|
|
|
CStatsMgr::SetGreater(STAT_HIGHEST_JUMP_REACHED.GetStatId(), altitude);
|
|
UpdateRecordedStat(REC_STAT_HIGHEST_JUMP_REACHED, altitude);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Probe has ended reset shyte
|
|
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
sm_FlyingPlanePosHeight.Zero();
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_ZeroWheelPosStart.Zero();
|
|
sm_ZeroWheelPosEnd.Zero();
|
|
sm_ChallengeZeroWheelPosStart.Zero();
|
|
|
|
sm_invalidJump = false;
|
|
}
|
|
void
|
|
CStatsMgr::CheckParachuteResults(CPed* playerPed)
|
|
{
|
|
//Not doing parachute probing
|
|
if (0 == sm_ParachuteStartTime)
|
|
return;
|
|
|
|
//Check always freefall 1st
|
|
if (sm_WasInAir)
|
|
return;
|
|
|
|
if (statVerify(playerPed) && !sm_ParachuteStartPos.IsZero() && !sm_ParachuteDeployPos.IsZero())
|
|
{
|
|
//Disable tracking for landing on water and if the player is dead.
|
|
if (!playerPed->GetIsSwimming() && !playerPed->IsInjured())
|
|
{
|
|
Vec3V coords = playerPed->GetTransform().GetPosition();
|
|
statDebugf3("[Parachute] CheckParachuteResults : player position: X:%f, Y:%f, Z:%f", coords.GetXf(), coords.GetYf(), coords.GetZf());
|
|
|
|
u32 parachutetotalTime = fwTimer::GetSystemTimeInMilliseconds() - sm_ParachuteStartTime;
|
|
|
|
//Altitude from parachuting start
|
|
float altitude = sm_ParachuteStartPos.z - coords.GetZf();
|
|
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (altitude >= sm_TuneMetadata.m_AwardParachuteJumpDistanceB && parachutetotalTime >= sm_TuneMetadata.m_AwardParachuteJumpTime)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_AWD_PARACHUTE_JUMPS_50M.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
|
|
if(StatsInterface::IsKeyValid(STAT_LONGEST_SKYDIVE.GetStatId()))
|
|
{
|
|
altitude = sm_ParachuteStartPos.z - sm_ParachuteDeployPos.z;
|
|
CStatsMgr::SetGreater(STAT_LONGEST_SKYDIVE.GetStatId(), altitude);
|
|
}
|
|
|
|
//Altitude from parachute deployment
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
altitude = sm_ParachuteDeployPos.z - coords.GetZf();
|
|
if(altitude > 0.0f)
|
|
{
|
|
// if deploy position was over the ground
|
|
if(CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(sm_ParachuteDeployPos.x, sm_ParachuteDeployPos.y) < sm_ParachuteDeployPos.z)
|
|
{
|
|
if (altitude < sm_TuneMetadata.m_AwardParachuteJumpDistanceA)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_AWD_PARACHUTE_JUMPS_10M.GetStatId(), 1.0f);
|
|
}
|
|
statDebugf3("[Parachute] CheckParachuteResults : parachute opened %f meters above the ground", altitude);
|
|
UpdateRecordedStat(REC_STAT_LOWEST_PARACHUTE_OPEN, altitude);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sm_ParachuteStartTime = 0;
|
|
sm_ParachuteStartPos.Zero();
|
|
sm_ParachuteDeployPos.Zero();
|
|
sm_HasStartedParachuteDeploy = false;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::CheckFreeFallResults(CPed* playerPed)
|
|
{
|
|
if (!sm_WasInAir)
|
|
return;
|
|
|
|
statDebugf3("CheckFreeFallResults player position: X:%f, Y:%f, Z:%f", playerPed->GetTransform().GetPosition().GetXf(), playerPed->GetTransform().GetPosition().GetYf(), playerPed->GetTransform().GetPosition().GetZf());
|
|
|
|
if (statVerify(playerPed) && !playerPed->IsInjured() && !sm_FreeFallStartPos.IsZero())
|
|
{
|
|
if ( (sm_ParachuteDeployPos.IsZero() && !sm_HasStartedParachuteDeploy) || sm_hasBailedFromParachuting )
|
|
{
|
|
statDebugf3("CheckFreeFallResults Altitude = %f", sm_FreeFallAltitude);
|
|
|
|
StatsInterface::SetGreater(StatsInterface::GetStatsModelHashId("LONGEST_SURVIVED_FREEFALL"), sm_FreeFallAltitude);
|
|
if(!playerPed->GetIsInWater())
|
|
{
|
|
UpdateRecordedStat(REC_STAT_LONGEST_SURVIVED_FREEFALL, sm_FreeFallAltitude);
|
|
}
|
|
sm_FreeFallAltitude = 0.0f;
|
|
}
|
|
}
|
|
|
|
if(m_recordedStat == REC_STAT_LONGEST_SURVIVED_SKYDIVE && !playerPed->IsInjured() && !playerPed->GetIsInWater())
|
|
{
|
|
UpdateRecordedStat(REC_STAT_LONGEST_SURVIVED_SKYDIVE, sm_SkydiveDistance);
|
|
}
|
|
sm_SkydiveDistance = 0.0f;
|
|
|
|
sm_FreeFallStartPos.Zero();
|
|
sm_ChallengeSkydiveStartPos.Zero();
|
|
sm_WasInAir = false;
|
|
sm_FreeFallStartTimer = 0;
|
|
sm_FreeFallDueToPlayerDamage = false;
|
|
sm_MostRecentCollisionTimeWithPlaneInAir = 0;
|
|
sm_hasBailedFromParachuting = false;
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::StartGroundProbe(Vector3& groundProbePosStart)
|
|
{
|
|
statAssert(!sm_GroundProbeHelper.IsProbeActive());
|
|
if(!sm_drivingFlyingVehicle)
|
|
{
|
|
statAssert(sm_TimeSinceLastGroundProbe + TIME_BETWEEN_GROUND_PROBES < fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
|
|
Vector3 vEnd = groundProbePosStart;
|
|
vEnd.z = CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(vEnd.x, vEnd.y);
|
|
|
|
float waterZ = 0.0f;
|
|
if(CVfxHelper::GetWaterZ(VECTOR3_TO_VEC3V(sm_FlyingPlanePosHeight), waterZ) != WATERTEST_TYPE_NONE)
|
|
{
|
|
if(waterZ > vEnd.z)
|
|
{
|
|
vEnd.z = waterZ;
|
|
}
|
|
}
|
|
|
|
if(groundProbePosStart.z < vEnd.z)
|
|
{
|
|
statWarningf("Ground Probe: Cannot probe from under the ground/water");
|
|
groundProbePosStart.z = vEnd.z;
|
|
}
|
|
|
|
//Create and fire the probe
|
|
WorldProbe::CShapeTestProbeDesc probeData;
|
|
probeData.SetStartAndEnd(groundProbePosStart, vEnd);
|
|
probeData.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
|
|
|
|
bool result = false;
|
|
|
|
if (sm_GroundProbeHelper.StartTestLineOfSight(probeData))
|
|
{
|
|
statDebugf3("Ground Probe: Start at coords=<x=%f,y=%f,z=%f>", groundProbePosStart.x, groundProbePosStart.y, groundProbePosStart.z);
|
|
statDebugf3("Ground Probe: End at coords=<x=%f,y=%f,z=%f>", vEnd.x, vEnd.y, vEnd.z);
|
|
|
|
sm_TimeSinceLastGroundProbe = fwTimer::GetTimeInMilliseconds();
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
statErrorf("Ground Probe: Failed to Start at coords=<x=%f,y=%f,z=%f>", groundProbePosStart.x, groundProbePosStart.y, groundProbePosStart.z);
|
|
statDebugf3("Ground Probe: Failed End at coords=<x=%f,y=%f,z=%f>", vEnd.x, vEnd.y, vEnd.z);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::CheckGroundProbeResults(float& altitude, Vector3& groundProbePosStart)
|
|
{
|
|
statAssert(sm_GroundProbeHelper.IsProbeActive());
|
|
|
|
//Check the probe
|
|
ProbeStatus probeStatus;
|
|
Vector3 intersectionPos;
|
|
if (sm_GroundProbeHelper.GetProbeResultsWithIntersection(probeStatus, &intersectionPos))
|
|
{
|
|
//Probe is done calculating
|
|
if (probeStatus == PS_Blocked)
|
|
{
|
|
altitude = groundProbePosStart.z - intersectionPos.z;
|
|
}
|
|
else
|
|
{
|
|
statWarningf("Ground Probe: Failed End Probe. probeStatus= '%d'.", probeStatus);
|
|
|
|
float fWaterHeight = 0.0f;
|
|
if(CWaterTestHelper::GetWaterHeightAtPositionIncludingRivers(groundProbePosStart, &fWaterHeight))
|
|
{
|
|
altitude = groundProbePosStart.z - fWaterHeight;
|
|
statWarningf("Ground Probe: Failed End Probe. Falling back to water level: %.2f", fWaterHeight);
|
|
}
|
|
else
|
|
{
|
|
statWarningf("Ground Probe: Failed End Probe. Not above water, return false");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
sm_LastJumpHeight = altitude;
|
|
//Another check can be done in 3 seconds.
|
|
sm_TimeSinceLastGroundProbe = fwTimer::GetTimeInMilliseconds();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateVehicleZeroWheel(CVehicle* pVehicle)
|
|
{
|
|
statAssert(pVehicle);
|
|
if (!pVehicle)
|
|
return;
|
|
else if (!pVehicle->GetDriver())
|
|
return;
|
|
else if (!pVehicle->GetDriver()->IsLocalPlayer())
|
|
return;
|
|
|
|
const s32 vehicleType = pVehicle->GetVehicleType();
|
|
const bool validVehicleType = (VEHICLE_TYPE_CAR==vehicleType || VEHICLE_TYPE_BIKE==vehicleType || VEHICLE_TYPE_QUADBIKE==vehicleType || VEHICLE_TYPE_BICYCLE==vehicleType);
|
|
|
|
// for the challenges, consider landing into water as landing.
|
|
if(pVehicle->GetWasInWater() && !sm_ChallengeZeroWheelPosStart.IsZero())
|
|
{
|
|
if(!sm_invalidJump)
|
|
{
|
|
// B*3150624 - Dont track this for vehicles that have a parachute
|
|
if(!pVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_HAS_PARACHUTE) && pVehicle->GetModelIndex() != MI_CAR_DELUXO && pVehicle->GetModelIndex() != MI_BIKE_OPPRESSOR2)
|
|
{
|
|
UpdateRecordedStat(REC_STAT_FARTHEST_JUMP_DIST, sm_JumpDistance);
|
|
}
|
|
}
|
|
|
|
sm_JumpDistance = 0.0f;
|
|
sm_ChallengeZeroWheelPosStart.Zero();
|
|
sm_HadAttachedParentVehicle = false;
|
|
sm_invalidJump = false;
|
|
}
|
|
|
|
bool collidingFragment = false;
|
|
if(statVerify(pVehicle->GetFrameCollisionHistory()))
|
|
{
|
|
const CCollisionRecord *pCollRecord = pVehicle->GetFrameCollisionHistory()->GetMostSignificantCollisionRecordOfType(ENTITY_TYPE_OBJECT);
|
|
if (pCollRecord && pCollRecord->m_pRegdCollisionEntity.Get())
|
|
{
|
|
CEntity* entity = pCollRecord->m_pRegdCollisionEntity.Get();
|
|
if(entity && entity->GetCurrentPhysicsInst() && entity->GetCurrentPhysicsInst()->GetClassType()==PH_INST_FRAG_CACHE_OBJECT)
|
|
{
|
|
collidingFragment = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!sm_GroundProbeHelper.IsProbeActive()
|
|
&& validVehicleType
|
|
&& !pVehicle->HasContactWheels()
|
|
&& ( pVehicle->GetFrameCollisionHistory()->GetMaxCollisionImpulseMagLastFrame()==0.0f || collidingFragment) )
|
|
{
|
|
const float speed = pVehicle->GetVelocity().Mag() * 3.6f; // km/h vehicle velocity
|
|
|
|
//Check if this is a glitch
|
|
if (speed < MIN_SPEED_TO_CONSIDER_JUMP)
|
|
{
|
|
statDebugf1("Invalid speed to consider Zero Wheel '%f'.", speed);
|
|
sm_invalidJump = true;
|
|
}
|
|
|
|
if(collidingFragment)
|
|
statDebugf3("Was only colliding with a fragment when jumping");
|
|
|
|
sm_ZeroWheelBufferCounter = 0;
|
|
sm_ZeroWheelCounter += fwTimer::GetSystemTimeStepInMilliseconds();
|
|
|
|
Vec3V coords = pVehicle->GetTransform().GetPosition();
|
|
if (coords.GetZf() > sm_ZeroWheelPosHeight.z && sm_ZeroWheelCounter >= ZEROWHEEL_MINBEST)
|
|
{
|
|
sm_ZeroWheelPosHeight = VEC3V_TO_VECTOR3(coords);
|
|
}
|
|
|
|
if (sm_ZeroWheelPosStart.IsZero())
|
|
{
|
|
sm_ZeroWheelPosStart = VEC3V_TO_VECTOR3(coords);
|
|
sm_ZeroWheelPosEnd.Zero();
|
|
}
|
|
|
|
if (sm_ChallengeZeroWheelPosStart.IsZero())
|
|
{
|
|
sm_ChallengeZeroWheelPosStart = VEC3V_TO_VECTOR3(coords);
|
|
sm_JumpDistance = 0.0f;
|
|
sm_WasOnAnotherVehicleWhenJumped = sm_IsOnAnotherVehicle;
|
|
|
|
if(sm_WasOnAnotherVehicleWhenJumped)
|
|
{
|
|
statDebugf1("Jumped from a vehicle");
|
|
}
|
|
}
|
|
}
|
|
else if(!sm_ChallengeZeroWheelPosStart.IsZero())
|
|
{
|
|
if (!sm_invalidJump)
|
|
{
|
|
// B*3150624 - Dont track this for vehicles that have a parachute
|
|
if (!pVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_HAS_PARACHUTE) && pVehicle->GetModelIndex() != MI_CAR_DELUXO && pVehicle->GetModelIndex() != MI_BIKE_OPPRESSOR2)
|
|
{
|
|
UpdateRecordedStat(REC_STAT_FARTHEST_JUMP_DIST, sm_JumpDistance);
|
|
}
|
|
}
|
|
|
|
sm_JumpDistance = 0.0f;
|
|
sm_ChallengeZeroWheelPosStart.Zero();
|
|
sm_invalidJump = false;
|
|
}
|
|
else if(sm_ZeroWheelCounter > 0 && sm_ZeroWheelBufferCounter < ZEROWHEEL_BUFFERLIMIT)
|
|
{
|
|
sm_ZeroWheelBufferCounter += fwTimer::GetSystemTimeStepInMilliseconds();
|
|
|
|
if (sm_ZeroWheelPosEnd.IsZero() && sm_ZeroWheelBufferCounter >= ZEROWHEEL_MINBEST)
|
|
{
|
|
if (pVehicle->GetNumContactWheels() >= ZEROWHEEL_MIN_WHEELS)
|
|
{
|
|
sm_ZeroWheelPosEnd = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
|
|
}
|
|
else
|
|
{
|
|
sm_ZeroWheelCounter = 0;
|
|
sm_ZeroWheelBufferCounter = 0;
|
|
sm_ZeroWheelPosStart.Zero();
|
|
sm_ZeroWheelPosEnd.Zero();
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_JumpDistance = 0.0f;
|
|
sm_HadAttachedParentVehicle=false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float distance = 0.0f;
|
|
if (sm_ZeroWheelCounter >= ZEROWHEEL_MINBEST && !sm_ZeroWheelPosStart.IsZero() && pVehicle->GetModelIndex() != MI_CAR_DELUXO && pVehicle->GetModelIndex() != MI_BIKE_OPPRESSOR2)
|
|
{
|
|
distance = Mag(VECTOR3_TO_VEC3V(sm_ZeroWheelPosEnd) - VECTOR3_TO_VEC3V(sm_ZeroWheelPosStart)).Getf();
|
|
}
|
|
|
|
if (sm_ZeroWheelCounter >= ZEROWHEEL_MINBEST && distance >= ZEROWHEEL_MIN_DIST)
|
|
{
|
|
statDebugf3("Zero Wheel: Start Coords=<x=%f,y=%f,z=%f>, End Coords=<x=%f,y=%f,z=%f>, time=<%u>, dist=<%f>, altitude=<%f>, wheels=<%d>"
|
|
,sm_ZeroWheelPosStart.x, sm_ZeroWheelPosStart.y, sm_ZeroWheelPosStart.z
|
|
,sm_ZeroWheelPosEnd.x, sm_ZeroWheelPosEnd.y, sm_ZeroWheelPosEnd.z
|
|
,sm_ZeroWheelCounter, distance, sm_ZeroWheelPosHeight.z - sm_ZeroWheelPosEnd.z
|
|
,pVehicle->GetNumContactWheels());
|
|
|
|
const CVehicleModelInfo* vi = pVehicle->GetVehicleModelInfo();
|
|
const bool updateLbRecords = (statVerify( vi ) && statVerify( CanUpdateVehicleRecord(vi->GetHashKey()) ));
|
|
|
|
if (sm_ZeroWheelCounter > sm_TuneMetadata.m_AwardVehicleJumpTime)
|
|
{
|
|
statDebugf3("Zero Wheel: Increment Stat AIR_LAUNCHES_OVER_5S");
|
|
CStatsMgr::IncrementStat(STAT_AIR_LAUNCHES_OVER_5S.GetStatId(), 1.0f);
|
|
|
|
if (updateLbRecords)
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_NumAirLaunchesOver5s += 1;
|
|
}
|
|
}
|
|
|
|
if (distance > sm_TuneMetadata.m_AwardVehicleJumpDistanceA)
|
|
{
|
|
statDebugf3("Zero Wheel: Increment Stat AIR_LAUNCHES_OVER_5M");
|
|
CStatsMgr::IncrementStat(STAT_AIR_LAUNCHES_OVER_5M.GetStatId(), 1.0f);
|
|
|
|
if (updateLbRecords)
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_NumAirLaunchesOver5m += 1;
|
|
}
|
|
}
|
|
|
|
if (distance > sm_TuneMetadata.m_AwardVehicleJumpDistanceB)
|
|
{
|
|
statDebugf3("Zero Wheel: Increment Stat AIR_LAUNCHES_OVER_40M");
|
|
CStatsMgr::IncrementStat(STAT_AIR_LAUNCHES_OVER_40M.GetStatId(), 1.0f);
|
|
if (updateLbRecords)
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_NumAirLaunchesOver40m += 1;
|
|
}
|
|
}
|
|
|
|
CStatsMgr::SetGreater(STAT_FARTHEST_JUMP_DIST.GetStatId(), distance);
|
|
CStatsMgr::IncrementStat(STAT_NUMBER_OF_AIR_LAUNCHES.GetStatId(), 1.0f);
|
|
|
|
if (updateLbRecords)
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_NumAirLaunches += 1;
|
|
|
|
if (distance > sm_vehicleRecords[sm_curVehIdx].m_HighestJumpDistance)
|
|
{
|
|
sm_vehicleRecords[sm_curVehIdx].m_HighestJumpDistance = distance;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if(!sm_GroundProbeHelper.IsProbeActive())
|
|
{
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
}
|
|
|
|
sm_ZeroWheelCounter = 0;
|
|
sm_ZeroWheelBufferCounter = 0;
|
|
sm_ZeroWheelPosStart.Zero();
|
|
sm_ZeroWheelPosEnd.Zero();
|
|
}
|
|
|
|
// Determine if the vehicle is on top of another one, with a small time buffer
|
|
bool isOnAnotherVehicle = false;
|
|
for(int i=0; i<pVehicle->GetNumWheels(); i++)
|
|
{
|
|
CWheel* wheel = pVehicle->GetWheel(i);
|
|
CPhysical* physical = wheel->GetHitPhysical();
|
|
if(physical)
|
|
{
|
|
isOnAnotherVehicle = isOnAnotherVehicle || physical->GetIsTypeVehicle();
|
|
}
|
|
}
|
|
|
|
if(isOnAnotherVehicle)
|
|
{
|
|
sm_IsOnAnotherVehicle = true;
|
|
sm_TimerAfterOnAnotherVehicle = 0;
|
|
}
|
|
|
|
if(sm_IsOnAnotherVehicle && !isOnAnotherVehicle)
|
|
{
|
|
sm_TimerAfterOnAnotherVehicle += fwTimer::GetTimeStepInMilliseconds();
|
|
if(sm_TimerAfterOnAnotherVehicle > TIME_CONSIDERED_ON_VEHICLE_AFTER_CONTACT)
|
|
{
|
|
sm_IsOnAnotherVehicle = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::UpdateVehicleReverseDriving(CVehicle* playerVehicle)
|
|
{
|
|
if(!statVerify(playerVehicle))
|
|
return;
|
|
|
|
const bool driverIsPlayer = (playerVehicle->GetDriver() && playerVehicle->GetDriver()->IsLocalPlayer());
|
|
Vector3 forward = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetForward());
|
|
const Vector3& velocity = playerVehicle->GetVelocity();
|
|
const bool reversing = DotProduct(velocity, forward) < 0;
|
|
|
|
if(reversing && !sm_IsOnAnotherVehicle && playerVehicle->GetNumContactWheels()>0 && driverIsPlayer)
|
|
{
|
|
if(sm_VehiclePreviousPosition.IsZero())
|
|
sm_VehiclePreviousPosition = playerVehicle->GetPreviousPosition();
|
|
|
|
sm_ReverseDrivingPosCurrent = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetPosition());
|
|
const float distanceTravelled = Mag(VECTOR3_TO_VEC3V(sm_ReverseDrivingPosCurrent) - VECTOR3_TO_VEC3V(sm_VehiclePreviousPosition)).Getf();
|
|
sm_CurrentDrivingReverseDistance += distanceTravelled;
|
|
|
|
UpdateRecordedStat(REC_STAT_DIST_DRIVING_CAR_REVERSE, sm_CurrentDrivingReverseDistance);
|
|
}
|
|
if( !reversing && !sm_ReverseDrivingPosCurrent.IsZero() )
|
|
{
|
|
sm_ReverseDrivingPosCurrent.Zero();
|
|
sm_VehiclePreviousPosition.Zero();
|
|
sm_CurrentDrivingReverseDistance=0.0f;
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::UpdateVehicleHydraulics(CVehicle* playerVehicle)
|
|
{
|
|
if(!statVerify(playerVehicle))
|
|
return;
|
|
if(playerVehicle->InheritsFromAutomobile())
|
|
{
|
|
CAutomobile* pCar = static_cast<CAutomobile*>(playerVehicle);
|
|
|
|
const bool bHasAllWheels = pCar->GetWheelFromId(VEH_WHEEL_LF) && pCar->GetWheelFromId(VEH_WHEEL_RF) && pCar->GetWheelFromId(VEH_WHEEL_LR) && pCar->GetWheelFromId(VEH_WHEEL_RR);
|
|
|
|
if(bHasAllWheels)
|
|
{
|
|
if(pCar->m_nAutomobileFlags.bHydraulicsBounceLanding )
|
|
{
|
|
if(!pCar->GetWheelFromId(VEH_WHEEL_LF)->GetIsTouching() && !pCar->GetWheelFromId(VEH_WHEEL_RF)->GetIsTouching() &&
|
|
!pCar->GetWheelFromId(VEH_WHEEL_LR)->GetIsTouching() && !pCar->GetWheelFromId(VEH_WHEEL_RR)->GetIsTouching() )
|
|
{
|
|
float currentAlt = pCar->GetTransform().GetPosition().GetZf();
|
|
statDebugf3("Hydraulic jump : altitudeOnGround = %f altitude = %f", sm_hydraulicJumpVehicleAltitude, currentAlt);
|
|
float altitude = currentAlt - sm_hydraulicJumpVehicleAltitude;
|
|
if(altitude > 0.0f)
|
|
{
|
|
CStatsMgr::SetGreater(STAT_HYDRAULIC_JUMP.GetStatId(), altitude);
|
|
statDebugf3("Hydraulic jump : altitude %f", altitude);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(pCar->GetWheelFromId(VEH_WHEEL_LF)->GetIsTouching() && pCar->GetWheelFromId(VEH_WHEEL_RF)->GetIsTouching() &&
|
|
pCar->GetWheelFromId(VEH_WHEEL_LR)->GetIsTouching() && pCar->GetWheelFromId(VEH_WHEEL_RR)->GetIsTouching() )
|
|
{
|
|
// position of the car on ground
|
|
sm_hydraulicJumpVehicleAltitude = pCar->GetTransform().GetPosition().GetZf();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::UpdateVehicleWallride(CVehicle* playerVehicle)
|
|
{
|
|
if(!statVerify(playerVehicle))
|
|
return;
|
|
|
|
Matrix34 vehicleMtx = MAT34V_TO_MATRIX34(playerVehicle->GetTransformPtr()->GetMatrix());
|
|
Vector3 vehicleUp = vehicleMtx.c;
|
|
|
|
static float minWallride = 0.5f;
|
|
|
|
float dot = vehicleUp.Dot(Vector3(0,0,1));
|
|
|
|
if( dot < minWallride && dot > 0.0f)
|
|
{
|
|
// We're sideways, are we on the ground?
|
|
bool isOnTheGround = true;
|
|
for(int i=0; i<playerVehicle->GetNumWheels() && isOnTheGround; i++)
|
|
{
|
|
CWheel* wheel = playerVehicle->GetWheel(i);
|
|
if(!wheel->GetIsTouching())
|
|
{
|
|
isOnTheGround = false;
|
|
}
|
|
}
|
|
if(isOnTheGround)
|
|
{
|
|
//Yes, we're wallriding!
|
|
Vec3V coords = playerVehicle->GetTransform().GetPosition();
|
|
if(!sm_LastWallridePosition.IsZero())
|
|
{
|
|
Vec3V lastPosition = VECTOR3_TO_VEC3V(sm_LastWallridePosition);
|
|
sm_WallrideDistance += Mag(coords - lastPosition).Getf();
|
|
}
|
|
sm_LastWallridePosition = VEC3V_TO_VECTOR3(coords);
|
|
sm_WallrideBufferTimer = WALLRIDE_BUFFERTIMER_VALUE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We were wallriding not that long ago
|
|
if(sm_WallrideBufferTimer > 0.0f)
|
|
{
|
|
sm_WallrideBufferTimer -= fwTimer::GetSystemTimeStep();
|
|
statDebugf3("UpdateVehicleWallride - not wallriding, but within the time buffer (%f)", sm_WallrideBufferTimer);
|
|
}
|
|
|
|
// We havent been wallriding for long enough, reset the values
|
|
if(sm_WallrideBufferTimer < 0.0f)
|
|
{
|
|
if(sm_WallrideDistance > 0.0f)
|
|
{
|
|
UpdateRecordedStat(REC_STAT_DIST_WALLRIDE, sm_WallrideDistance);
|
|
sm_WallrideDistance = 0.0f;
|
|
}
|
|
if(!sm_LastWallridePosition.IsZero())
|
|
{
|
|
sm_LastWallridePosition.Zero();
|
|
sm_WallrideBufferTimer = 0.0f;
|
|
statDebugf3("UpdateVehicleWallride - Stop wallriding");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateAverageSpeed()
|
|
{
|
|
const float totalDist = StatsInterface::GetFloatStat(STAT_DIST_DRIVING_CAR.GetStatId())
|
|
+ StatsInterface::GetFloatStat(STAT_DIST_DRIVING_BIKE.GetStatId()); // meters
|
|
|
|
const u64 totalTime = StatsInterface::GetUInt64Stat(STAT_TIME_DRIVING_CAR.GetStatId())
|
|
+ StatsInterface::GetUInt64Stat(STAT_TIME_DRIVING_BIKE.GetStatId()); // milliseconds
|
|
|
|
if ( totalTime == 0 || totalDist <= 0.0f )
|
|
return;
|
|
|
|
StatsInterface::SetStatData(STAT_AVERAGE_SPEED.GetStatId(), (totalDist/totalTime) * 3600.0f, STATUPDATEFLAG_ASSERTONLINESTATS); // km/h
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateStatsWhenPedRunDown(u16 iRandomSeed, CVehicle* vehicle)
|
|
{
|
|
bool bNewSeed = true;
|
|
for (int i=0; i<sm_PedsRunDownRecords.GetCount();i++)
|
|
{
|
|
if (sm_PedsRunDownRecords[i] == iRandomSeed)
|
|
{
|
|
bNewSeed = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bNewSeed)
|
|
return;
|
|
else
|
|
{
|
|
bool bStored = false;
|
|
for (int i=0; i<sm_PedsRunDownRecords.GetCount();i++)
|
|
{
|
|
if (sm_PedsRunDownRecords[i] == 0)
|
|
{
|
|
sm_PedsRunDownRecords[i] = iRandomSeed;
|
|
bStored = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bStored)
|
|
{
|
|
for (int i=0; i<sm_PedsRunDownRecords.GetCount();i++)
|
|
sm_PedsRunDownRecords[i] = 0;
|
|
sm_PedsRunDownRecords[0] = iRandomSeed;
|
|
}
|
|
}
|
|
|
|
CStatsMgr::IncrementStat(STAT_HIGHEST_SKITTLES.GetStatId(), 1.0f);
|
|
|
|
if (vehicle)
|
|
{
|
|
const CVehicleModelInfo* vi = vehicle->GetVehicleModelInfo();
|
|
if (statVerify( vi ) && CanUpdateVehicleRecord( vi->GetHashKey() ))
|
|
{
|
|
++sm_vehicleRecords[sm_curVehIdx].m_NumPedsRundown;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::UpdateStatsOnRespawn()
|
|
{
|
|
sm_VehicleDeadCheckCounter = 0.0f;
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
ClearPlayerVehicleStats();
|
|
EndVehicleStats();
|
|
sm_VehicleDeadCheckCounter = 0;
|
|
sm_VehicleDeadCheck = false;
|
|
sm_NearMissNoCrash = 0;
|
|
sm_JumpDistance=0.0f;
|
|
sm_OnFootPosStart.Zero();
|
|
sm_FootAltitude = 0.0f;
|
|
sm_FlyingCounter=0.0f;
|
|
sm_FlyingDistance=0.0f;
|
|
sm_wasInWaterPreviously = false;
|
|
sm_outOfWater = false;
|
|
sm_inAirAfterGroundTimer = 0.0f;
|
|
sm_HadAttachedParentVehicle=false;
|
|
sm_StartVehicleBail.Zero();
|
|
sm_LastVehicleBailPosition.Zero();
|
|
sm_VehicleBailDistance = 0.0f;
|
|
sm_NearMissNoCrashPrecise = 0;
|
|
ClearNearMissArray();
|
|
sm_FlyingAltitude = 0.0f;
|
|
sm_ValidFlyingAltitude = false;
|
|
sm_hasBailedFromParachuting = false;
|
|
sm_LastWallridePosition.Zero();
|
|
sm_WallrideDistance = 0.0f;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::PlayerFireWeapon(const CWeapon* weapon)
|
|
{
|
|
if(!IsPlayerActive())
|
|
return;
|
|
|
|
if (!CStatsUtils::CanUpdateStats())
|
|
{
|
|
return;
|
|
}
|
|
|
|
statAssert(weapon);
|
|
if (!weapon)
|
|
return;
|
|
|
|
CPed* playerPed = FindPlayerPed();
|
|
if (!playerPed)
|
|
return;
|
|
|
|
const CWeaponInfo* wi = weapon->GetWeaponInfo();
|
|
statAssert(wi);
|
|
if (!wi)
|
|
return;
|
|
|
|
const u32 weaponHash = wi->GetHash();
|
|
|
|
//Add exception for Weapon fire extinguisher because it is treated as a gun right now.
|
|
if (weaponHash == WEAPONTYPE_FIREEXTINGUISHER)
|
|
return;
|
|
|
|
StatId statWeaponFired = StatsInterface::GetLocalPlayerWeaponInfoStatId(StatsInterface::WEAPON_STAT_SHOTS, wi, playerPed);
|
|
if(StatsInterface::IsKeyValid(statWeaponFired))
|
|
{
|
|
StatsInterface::IncrementStat(statWeaponFired, 1.0f);
|
|
}
|
|
#if __ASSERT
|
|
else if(playerPed->IsArchetypeSet() && StatsInterface::GetStatsPlayerModelValid())
|
|
{
|
|
CStatsMgr::MissingWeaponStatName(STATTYPE_WEAPON_SHOTS
|
|
,statWeaponFired
|
|
,weaponHash
|
|
,wi->GetDamageType()
|
|
,wi->GetName());
|
|
}
|
|
#endif // __ASSERT
|
|
|
|
if (wi->GetDamageType() == DAMAGE_TYPE_EXPLOSIVE || /*wi->GetIsProjectile() ||*/ wi->GetIsVehicleWeapon())
|
|
{
|
|
if (wi->GetDamageType() == DAMAGE_TYPE_EXPLOSIVE)
|
|
{
|
|
if (StatsInterface::IsKeyValid(STAT_EXPLOSIVE_DAMAGE_SHOTS.GetHash()))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_EXPLOSIVE_DAMAGE_SHOTS.GetStatId(), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
|
|
if (weaponHash == WEAPONTYPE_STICKYBOMB || weaponHash == WEAPONTYPE_GRENADE || weaponHash == WEAPONTYPE_DLC_PROXMINE || weaponHash == WEAPONTYPE_DLC_PIPEBOMB)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_EXPLOSIVES_USED.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
else if (wi->GetIsGun() && (wi->GetDamageType() == DAMAGE_TYPE_BULLET || wi->GetDamageType() == DAMAGE_TYPE_BULLET_RUBBER))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_SHOTS.GetStatId(), 1.0f);
|
|
|
|
CVehicle* pVehicle = playerPed->GetVehiclePedInside();
|
|
if(pVehicle)
|
|
{
|
|
const bool playerIsDriver = (playerPed == pVehicle->GetDriver());
|
|
if(playerIsDriver)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_DB_SHOTS.GetStatId(), 1.0f);
|
|
StatsInterface::IncrementStat(STAT_DB_SHOTTIME.GetStatId(), fwTimer::GetSystemTimeStep()*1000.0f);
|
|
}
|
|
else
|
|
{
|
|
StatsInterface::IncrementStat(STAT_PASS_DB_SHOTS.GetStatId(), 1.0f);
|
|
StatsInterface::IncrementStat(STAT_PASS_DB_SHOTTIME.GetStatId(), fwTimer::GetSystemTimeStep()*1000.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// track if player has fired a shot while crouching
|
|
if (playerPed->GetIsCrouching())
|
|
{
|
|
sm_bShotFiredInCrouch = true;
|
|
}
|
|
|
|
// track if player has fired a shot whilst in cover
|
|
CPedIntelligence* pPI = playerPed->GetPedIntelligence();
|
|
if (pPI)
|
|
{
|
|
CQueriableInterface* pQI = pPI->GetQueriableInterface();
|
|
if (pQI && pQI->IsTaskCurrentlyRunning(CTaskTypes::TASK_IN_COVER))
|
|
{
|
|
sm_bShotFiredInCover = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
void
|
|
CStatsMgr::PlayerFiredWeaponToInvenciblePed(const CWeapon* weapon)
|
|
{
|
|
if(!IsPlayerActive())
|
|
return;
|
|
|
|
statAssert(weapon);
|
|
if (!weapon)
|
|
return;
|
|
|
|
CPed* playerPed = FindPlayerPed();
|
|
if (!playerPed)
|
|
return;
|
|
|
|
const CWeaponInfo* wi = weapon->GetWeaponInfo();
|
|
statAssert(wi);
|
|
if (!wi)
|
|
return;
|
|
|
|
const u32 weaponHash = wi->GetHash();
|
|
|
|
//Add exception for Weapon fire extinguisher because it is treated as a gun right now.
|
|
if (weaponHash == WEAPONTYPE_FIREEXTINGUISHER)
|
|
return;
|
|
// Another exception if the damage type is melee weapon
|
|
if(weapon->GetDamageType()==DAMAGE_TYPE_MELEE)
|
|
return;
|
|
|
|
StatId statWeaponFired = StatsInterface::GetLocalPlayerWeaponInfoStatId(StatsInterface::WEAPON_STAT_SHOTS, wi, playerPed);
|
|
if(StatsInterface::IsKeyValid(statWeaponFired))
|
|
{
|
|
StatsInterface::DecrementStat(statWeaponFired, 1.0f);
|
|
}
|
|
#if __ASSERT
|
|
else if(playerPed->IsArchetypeSet() && StatsInterface::GetStatsPlayerModelValid())
|
|
{
|
|
CStatsMgr::MissingWeaponStatName(STATTYPE_WEAPON_SHOTS
|
|
,statWeaponFired
|
|
,weaponHash
|
|
,wi->GetDamageType()
|
|
,wi->GetName());
|
|
}
|
|
#endif // __ASSERT
|
|
|
|
|
|
if (wi->GetDamageType() == DAMAGE_TYPE_EXPLOSIVE || /*wi->GetIsProjectile() ||*/ wi->GetIsVehicleWeapon())
|
|
{
|
|
if (wi->GetDamageType() == DAMAGE_TYPE_EXPLOSIVE)
|
|
{
|
|
if (StatsInterface::IsKeyValid(STAT_EXPLOSIVE_DAMAGE_SHOTS.GetHash()))
|
|
{
|
|
StatsInterface::DecrementStat(STAT_EXPLOSIVE_DAMAGE_SHOTS.GetStatId(), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
|
|
if (weaponHash == WEAPONTYPE_STICKYBOMB || weaponHash == WEAPONTYPE_GRENADE || weaponHash == WEAPONTYPE_DLC_PROXMINE || weaponHash == WEAPONTYPE_DLC_PIPEBOMB)
|
|
{
|
|
StatsInterface::DecrementStat(STAT_EXPLOSIVES_USED.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
else if (wi->GetIsGun() && (wi->GetDamageType() == DAMAGE_TYPE_BULLET || wi->GetDamageType() == DAMAGE_TYPE_BULLET_RUBBER))
|
|
{
|
|
StatsInterface::DecrementStat(STAT_SHOTS.GetStatId(), 1.0f);
|
|
|
|
CVehicle* pVehicle = playerPed->GetVehiclePedInside();
|
|
if(pVehicle)
|
|
{
|
|
const bool playerIsDriver = (playerPed == pVehicle->GetDriver());
|
|
if(playerIsDriver)
|
|
{
|
|
StatsInterface::DecrementStat(STAT_DB_SHOTS.GetStatId(), 1.0f);
|
|
StatsInterface::DecrementStat(STAT_DB_SHOTTIME.GetStatId(), fwTimer::GetSystemTimeStep()*1000.0f);
|
|
}
|
|
else
|
|
{
|
|
StatsInterface::DecrementStat(STAT_PASS_DB_SHOTS.GetStatId(), 1.0f);
|
|
StatsInterface::DecrementStat(STAT_PASS_DB_SHOTTIME.GetStatId(), fwTimer::GetSystemTimeStep()*1000.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// track if player has fired a shot while crouching
|
|
if (playerPed->GetIsCrouching())
|
|
{
|
|
sm_bShotFiredInCrouch = false;
|
|
}
|
|
|
|
// track if player has fired a shot whilst in cover
|
|
CPedIntelligence* pPI = playerPed->GetPedIntelligence();
|
|
if (pPI)
|
|
{
|
|
CQueriableInterface* pQI = pPI->GetQueriableInterface();
|
|
if (pQI && pQI->IsTaskCurrentlyRunning(CTaskTypes::TASK_IN_COVER))
|
|
{
|
|
sm_bShotFiredInCover = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
void
|
|
CStatsMgr::PlayerArrested()
|
|
{
|
|
ClearPlayingTimeStats();
|
|
|
|
CNetworkTelemetry::PlayerArrested();
|
|
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("BUSTED"), 1);
|
|
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_LastVehicleBailPosition.Zero();
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
ClearPlayerVehicleStats();
|
|
|
|
EndVehicleStats();
|
|
sm_VehicleDeadCheckCounter = 0;
|
|
sm_VehicleDeadCheck = false;
|
|
sm_DriveNoCrashTime = 0.0f;
|
|
sm_DriveNoCrashDist = 0.0f;
|
|
sm_NearMissNoCrash = 0;
|
|
sm_JumpDistance=0.0f;
|
|
sm_VehicleBailDistance = 0.0f;
|
|
sm_wasInWaterPreviously = false;
|
|
sm_outOfWater = false;
|
|
sm_inAirAfterGroundTimer = 0.0f;
|
|
sm_HadAttachedParentVehicle=false;
|
|
sm_NearMissNoCrashPrecise = 0;
|
|
ClearNearMissArray();
|
|
|
|
SetCheatIsActive( false );
|
|
}
|
|
|
|
void
|
|
CStatsMgr::PreSaveBaseStats(const bool bMultiplayerSave)
|
|
{
|
|
CStatsMgr::CheckWriteVehicleRecords( StatsInterface::GetVehicleLeaderboardWriteOnSavegame( ) );
|
|
|
|
sm_StatsData.PreSaveBaseStats( bMultiplayerSave );
|
|
|
|
if (CGenericGameStorage::GetSaveOperation() == OPERATION_AUTOSAVING)
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("AUTO_SAVED"), 1);
|
|
else
|
|
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("MANUAL_SAVED"), 1);
|
|
|
|
//Set mac address profile stat
|
|
if (!bMultiplayerSave)
|
|
{
|
|
static StatId s_screenWidth("_ScreenWidth");
|
|
static StatId s_screenHeight("_ScreenHeight");
|
|
if (StatsInterface::IsKeyValid(s_screenWidth))
|
|
{
|
|
StatsInterface::SetStatData(s_screenWidth, GRCDEVICE.GetWidth(), STATUPDATEFLAG_DIRTY_PROFILE);
|
|
StatsInterface::SetStatData(s_screenHeight, GRCDEVICE.GetHeight(), STATUPDATEFLAG_DIRTY_PROFILE);
|
|
}
|
|
}
|
|
|
|
ClearPlayingTimeStats();
|
|
|
|
CPed* pLocalPlayer = CGameWorld::FindLocalPlayer();
|
|
if (pLocalPlayer && !pLocalPlayer->GetIsDrivingVehicle())
|
|
{
|
|
ClearPlayerVehicleStats();
|
|
sm_ZeroWheelPosHeight.Zero();
|
|
sm_GroundProbeHelper.ResetProbe();
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::PlayerCompleteMission()
|
|
{
|
|
ClearPlayingTimeStats();
|
|
ClearPlayerVehicleStats();
|
|
}
|
|
|
|
void
|
|
CStatsMgr::PlayerFailedMission()
|
|
{
|
|
ClearPlayingTimeStats();
|
|
ClearPlayerVehicleStats();
|
|
}
|
|
|
|
|
|
// ---- Access Methods ----------------------------------------------
|
|
|
|
float
|
|
CStatsMgr::GetPercentageProgress(void)
|
|
{
|
|
statAssertf(!NetworkInterface::IsGameInProgress(), "Not tracked in MP - ask Brenda why not...");
|
|
|
|
/*
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
return StatsInterface::GetFloatStat(STAT_PROGRESS_MADE.GetStatId());
|
|
}
|
|
*/
|
|
|
|
return StatsInterface::GetFloatStat(STAT_TOTAL_PROGRESS_MADE.GetHash());
|
|
}
|
|
|
|
float
|
|
CStatsMgr::GetPercentageKillsMadeinFreeAim(void)
|
|
{
|
|
float iTotalArmedKills = static_cast<float>(StatsInterface::GetIntStat(STAT_KILLS_ARMED.GetHash()));
|
|
float iTotalKillsInFreeAim = StatsInterface::GetFloatStat(STAT_KILLS_IN_FREE_AIM.GetStatId());
|
|
|
|
return ((0.0f==iTotalArmedKills || 0.0f==iTotalKillsInFreeAim) ? 0.0f :rage::Floorf(iTotalKillsInFreeAim*100/iTotalArmedKills));
|
|
}
|
|
|
|
float
|
|
CStatsMgr::GetFatAndMuscleModifier(eStatModAbilities nAbility)
|
|
{
|
|
float fModifier = 1.0f;
|
|
switch(nAbility)
|
|
{
|
|
case STAT_MODIFIER_CLIMB_HEIGHT:
|
|
fModifier = 0.0;
|
|
break;
|
|
|
|
case STAT_MODIFIER_JUMP_SPEED:
|
|
fModifier = 1.0;
|
|
break;
|
|
|
|
case STAT_MODIFIER_SPRINT_ENERGY:
|
|
// if you change this, you need to update the ambient trigger for the player being tired as well (taskambient.cpp)
|
|
fModifier = 100.0f;
|
|
break;
|
|
|
|
case STAT_MODIFIER_BREATH_UNDERWATER:
|
|
fModifier = 1.0;
|
|
break;
|
|
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
return fModifier;
|
|
}
|
|
|
|
bool CStatsMgr::GetFlyingAltitude(float& value)
|
|
{
|
|
if(sm_ValidFlyingAltitude)
|
|
{
|
|
value = sm_FlyingAltitude;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::SetCheatIsActive(const bool active)
|
|
{
|
|
if (active)
|
|
{
|
|
statDebugf1("ACTIVATE CHEAT");
|
|
sm_cheatIsActive = true;
|
|
sm_cheatIsActiveTimer = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
else
|
|
{
|
|
statDebugf1("DE-ACTIVATE CHEAT");
|
|
sm_cheatIsActive = false;
|
|
sm_cheatIsActiveTimer = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::RegisterPedKill(const CEntity* inflictor, const CPed* victim, const u32 weaponHash, bool headShot, const int weaponDamageComponent, const bool withMeleeWeapon, const fwFlags32& flags)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
AssertEntityPointerValid_NotInWorld((CPed *)victim);
|
|
|
|
statAssertf(victim, "Null victim.");
|
|
if (!victim)
|
|
return;
|
|
|
|
if(!IsPlayerActive())
|
|
return;
|
|
|
|
bool killedByVehicle = false;
|
|
|
|
CVehicle* vehicleInflictor = 0;
|
|
|
|
CPed* pedInflictor = 0;
|
|
if (inflictor)
|
|
{
|
|
if (inflictor->GetIsTypePed())
|
|
{
|
|
pedInflictor = (CPed*)inflictor;
|
|
}
|
|
else if (inflictor->GetIsTypeVehicle())
|
|
{
|
|
vehicleInflictor = (CVehicle*)(inflictor);
|
|
pedInflictor = vehicleInflictor->GetDriver();
|
|
killedByVehicle = true;
|
|
}
|
|
else
|
|
{
|
|
//Search for the damage entity
|
|
CEntity* damageEntity = victim->GetWeaponDamageEntity();
|
|
if (damageEntity && ((fwTimer::GetTimeInMilliseconds() - victim->GetWeaponDamagedTime()) < PLAYER_WEAPON_DAMAGE_TIMEOUT))
|
|
{
|
|
if (damageEntity->GetIsTypePed())
|
|
{
|
|
pedInflictor = (CPed*)damageEntity;
|
|
}
|
|
else if (damageEntity->GetIsTypeVehicle())
|
|
{
|
|
vehicleInflictor = (CVehicle*)damageEntity;
|
|
pedInflictor = vehicleInflictor->GetDriver();
|
|
killedByVehicle = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//We only care about peds and the peds driving the vehicle
|
|
if (pedInflictor)
|
|
{
|
|
//Victim is not the local player.
|
|
if (!victim->IsLocalPlayer())
|
|
{
|
|
if (pedInflictor->IsLocalPlayer())
|
|
{
|
|
if (weaponDamageComponent == RagdollComponent::RAGDOLL_HEAD || weaponDamageComponent == RagdollComponent::RAGDOLL_NECK)
|
|
{
|
|
headShot = true;
|
|
}
|
|
// We don't want to register headshots if melee weapons/pistol whips/melee attacks with firearms were registered (B* 2450011)
|
|
if (withMeleeWeapon)
|
|
{
|
|
headShot = false;
|
|
}
|
|
RegisterKillByLocalPlayer(pedInflictor, victim, weaponHash, headShot, flags);
|
|
|
|
//Count UNARMED_PED_HITS in multiplayer.
|
|
if (weaponHash == WEAPONTYPE_UNARMED)
|
|
{
|
|
if(StatsInterface::IsKeyValid(STAT_UNARMED_PED_HITS.GetStatId()))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_UNARMED_PED_HITS.GetStatId(), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
|
|
//Update peds run down
|
|
if (WEAPONTYPE_RUNOVERBYVEHICLE == weaponHash || WEAPONTYPE_RAMMEDBYVEHICLE == weaponHash)
|
|
{
|
|
if (WEAPONTYPE_HIT_BY_WATER_CANNON != weaponHash && (killedByVehicle || pedInflictor->GetIsDrivingVehicle()))
|
|
{
|
|
if (!vehicleInflictor && !killedByVehicle && pedInflictor->GetIsDrivingVehicle())
|
|
{
|
|
vehicleInflictor = pedInflictor->GetMyVehicle();
|
|
}
|
|
|
|
if (statVerify(vehicleInflictor))
|
|
{
|
|
CStatsMgr::UpdateStatsWhenPedRunDown(victim->GetRandomSeed(), vehicleInflictor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_KILLS_BY_OTHERS.GetStatId(), 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Register that a player was killed.
|
|
if(victim->IsPlayer())
|
|
{
|
|
CStatsMgr::RegisterPlayerKilled(pedInflictor, victim, weaponHash, weaponDamageComponent, withMeleeWeapon);
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::RegisterVehicleNearMiss(const CPed* driver, const u32 time)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
if (IsPlayerActive() && driver && driver->IsLocalPlayer())
|
|
{
|
|
//Don't trigger near miss if car had just collided
|
|
if (time + 2000 < fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_NUMBER_NEAR_MISS.GetStatId(), 1);
|
|
sm_NearMissNoCrash++;
|
|
CStatsMgr::SetGreater(STAT_NUMBER_NEAR_MISS_NOCRASH.GetStatId(), (float) sm_NearMissNoCrash);
|
|
UpdateRecordedStat(REC_STAT_NUMBER_NEAR_MISS_NOCRASH, (float)sm_NearMissNoCrash);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::RegisterVehicleNearMissAccepted()
|
|
{
|
|
sm_NearMissNoCrashPrecise++;
|
|
CStatsMgr::SetGreater(STAT_NEAR_MISS_PRECISE.GetStatId(), (float) sm_NearMissNoCrashPrecise);
|
|
UpdateRecordedStat(REC_STAT_NEAR_MISS_PRECISE, (float)sm_NearMissNoCrashPrecise);
|
|
}
|
|
|
|
void CStatsMgr::RegisterVehicleNearMissPrecise(const CPed* playerDriver, const u32 time)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
if (IsPlayerActive() && playerDriver && playerDriver->IsLocalPlayer())
|
|
{
|
|
AddNearMissTimeToArray(time);
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::RegisterExplosionHit(const CEntity* inflictor, const CEntity* victim, const u32 weaponHash, const float damage)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
if (!inflictor || !victim)
|
|
return;
|
|
|
|
const float MIN_DAMAGE_TO_CONSIDER_HIT = 1.0f;
|
|
if (damage < MIN_DAMAGE_TO_CONSIDER_HIT)
|
|
return;
|
|
|
|
if (!inflictor->GetIsTypePed())
|
|
return;
|
|
|
|
if (!static_cast< const CPed* >( inflictor )->IsLocalPlayer())
|
|
return;
|
|
|
|
bool incrementStat = false;
|
|
|
|
if (victim->GetIsTypePed())
|
|
{
|
|
incrementStat = !(static_cast< const CPed* >( victim )->IsLocalPlayer());
|
|
}
|
|
else if (victim->GetIsTypeVehicle())
|
|
{
|
|
const CVehicle* vehicle = static_cast< const CVehicle* >( victim );
|
|
|
|
if (vehicle->GetLayoutInfo())
|
|
{
|
|
if (vehicle->GetDriver() || vehicle->GetNumberOfPassenger() > 0)
|
|
{
|
|
int numSeats = vehicle->GetLayoutInfo()->GetNumSeats();
|
|
|
|
for(int i=0; i<numSeats && !incrementStat; i++)
|
|
{
|
|
CPed* pPed = vehicle->GetPedInSeat(i);
|
|
|
|
if(pPed && !pPed->IsDead() && !pPed->IsLocalPlayer())
|
|
{
|
|
incrementStat = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!incrementStat)
|
|
return;
|
|
|
|
const CWeaponInfo* wi = CWeaponInfoManager::GetInfo< CWeaponInfo >( weaponHash );
|
|
statAssertf(wi, "NULL weapon info for weapon hash=[0x%X]", weaponHash);
|
|
if(!wi)
|
|
return;
|
|
|
|
if (wi->GetDamageType() == DAMAGE_TYPE_EXPLOSIVE)
|
|
{
|
|
if (StatsInterface::IsKeyValid(STAT_EXPLOSIVE_DAMAGE_HITS.GetHash()))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_EXPLOSIVE_DAMAGE_HITS.GetStatId(), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::RegisterExplosionHitAnything(const CEntity* inflictor, const u32 weaponHash)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
if (!inflictor)
|
|
return;
|
|
|
|
if (!inflictor->GetIsTypePed())
|
|
return;
|
|
|
|
if (!static_cast< const CPed* >( inflictor )->IsLocalPlayer())
|
|
return;
|
|
|
|
const CWeaponInfo* wi = CWeaponInfoManager::GetInfo< CWeaponInfo >( weaponHash );
|
|
if(!wi)
|
|
return;
|
|
|
|
if (wi->GetDamageType() != DAMAGE_TYPE_EXPLOSIVE)
|
|
return;
|
|
|
|
if (StatsInterface::IsKeyValid(STAT_EXPLOSIVE_DAMAGE_HITS_ANYTHING.GetHash()))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_EXPLOSIVE_DAMAGE_HITS_ANYTHING.GetStatId(), 1.0f, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::RegisterKillByLocalPlayer(const CPed* killer, const CPed* victim, const u32 weaponHash, const bool headShot, const fwFlags32& flags)
|
|
{
|
|
AssertEntityPointerValid_NotInWorld((CPed *)victim);
|
|
statAssertf(killer, "Null Killer.");
|
|
statAssertf(victim, "Null victim.");
|
|
|
|
if (!killer || !victim)
|
|
return;
|
|
|
|
statAssertf(killer->IsLocalPlayer(), "Killer is not the local player.");
|
|
if (!killer->IsLocalPlayer())
|
|
return;
|
|
|
|
//If this guy was in the players group the player will loose some respect.
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (killer->GetNetworkObject() && killer->GetNetworkObject()->GetPlayerOwner())
|
|
{
|
|
const bool isPlayerVagosMember = (victim->IsPlayer() && StatsInterface::IsPlayerVagos(victim));
|
|
const bool isPlayerLostMember = (victim->IsPlayer() && StatsInterface::IsPlayerLost(victim));
|
|
const bool isPedVagosMember = (!victim->IsPlayer() && StatsInterface::GetPedOfType(victim, StatsInterface::MP_GROUP_GANG_VAGOS));
|
|
const bool isPedLostMember = (!victim->IsPlayer() && StatsInterface::GetPedOfType(victim, StatsInterface::MP_GROUP_GANG_LOST));
|
|
|
|
if (isPlayerVagosMember || isPlayerLostMember || isPedLostMember || isPedVagosMember)
|
|
{
|
|
if (StatsInterface::IsSameTeam(victim, killer))
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_KILLS_FRIENDLY_GANG_MEMBERS.GetStatId(), 1);
|
|
}
|
|
else if (isPlayerVagosMember && StatsInterface::IsPlayerLost(killer))
|
|
{
|
|
statAssertf(!isPlayerLostMember && !isPedLostMember && !isPedVagosMember, "isPlayerVagosMember=%s, isPlayerLostMember=%s, isPedLostMember=%s,isPedVagosMember=%s", isPlayerVagosMember?"True":"False", isPlayerLostMember?"True":"False", isPedLostMember?"True":"False", isPedVagosMember?"True":"False");
|
|
CStatsMgr::IncrementStat(STAT_KILLS_ENEMY_GANG_MEMBERS.GetStatId(), 1);
|
|
}
|
|
else if (isPlayerLostMember && StatsInterface::IsPlayerVagos(killer))
|
|
{
|
|
statAssertf(!isPlayerVagosMember && !isPedLostMember && !isPedVagosMember, "isPlayerVagosMember=%s, isPlayerLostMember=%s, isPedLostMember=%s,isPedVagosMember=%s", isPlayerVagosMember?"True":"False", isPlayerLostMember?"True":"False", isPedLostMember?"True":"False", isPedVagosMember?"True":"False");
|
|
CStatsMgr::IncrementStat(STAT_KILLS_ENEMY_GANG_MEMBERS.GetStatId(), 1);
|
|
}
|
|
else if (isPedVagosMember && StatsInterface::IsPlayerLost(killer))
|
|
{
|
|
statAssertf(!isPlayerVagosMember && !isPlayerLostMember && !isPedLostMember, "isPlayerVagosMember=%s, isPlayerLostMember=%s, isPedLostMember=%s,isPedVagosMember=%s", isPlayerVagosMember?"True":"False", isPlayerLostMember?"True":"False", isPedLostMember?"True":"False", isPedVagosMember?"True":"False");
|
|
CStatsMgr::IncrementStat(STAT_KILLS_ENEMY_GANG_MEMBERS.GetStatId(), 1);
|
|
}
|
|
else if (isPedLostMember && StatsInterface::IsPlayerVagos(killer))
|
|
{
|
|
statAssertf(!isPlayerVagosMember && !isPlayerLostMember && !isPedVagosMember, "isPlayerVagosMember=%s, isPlayerLostMember=%s, isPedLostMember=%s,isPedVagosMember=%s", isPlayerVagosMember?"True":"False", isPlayerLostMember?"True":"False", isPedLostMember?"True":"False", isPedVagosMember?"True":"False");
|
|
CStatsMgr::IncrementStat(STAT_KILLS_ENEMY_GANG_MEMBERS.GetStatId(), 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CStatsMgr::UpdateStatPedsKilledOfThisType(victim->GetPedType());
|
|
|
|
const ePedType pedType = victim->GetPedType();
|
|
|
|
const bool killedPlayer = victim->IsPlayer() && statVerify(!victim->IsLocalPlayer());
|
|
if (!killedPlayer)
|
|
{
|
|
if (PEDTYPE_SWAT == pedType)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_KILLS_SWAT.GetStatId(), 1.0f);
|
|
}
|
|
else if (PEDTYPE_COP == pedType)
|
|
{
|
|
StatsInterface::IncrementStat(STAT_KILLS_COP.GetStatId(), 1.0f);
|
|
}
|
|
else if (!victim->IsLawEnforcementPed() && !victim->IsGangPed())
|
|
{
|
|
StatsInterface::IncrementStat(STAT_KILLS_INNOCENTS.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
|
|
if (victim->GetPedConfigFlag(CPED_CONFIG_FLAG_KilledByStealth))
|
|
{
|
|
StatsInterface::IncrementStat(STAT_KILLS_STEALTH.GetStatId(), 1.0f);
|
|
}
|
|
|
|
const unsigned curTime = fwTimer::GetTimeInMilliseconds_NonScaledClipped();
|
|
if((curTime - sm_KillingSpreeTime) < KILLSPREE_INTERVAL && 0 < (curTime - sm_KillingSpreeTime) && 0 < sm_KillingSpreeTime)
|
|
{
|
|
float killcounter = (sm_KillingSpreeCounter==0.0f) ? 2.0f : 1.0f;
|
|
|
|
sm_KillingSpreeCounter += (u32)killcounter;
|
|
sm_KillingSpreeTotalTime += (curTime - sm_KillingSpreeTime);
|
|
|
|
if(killedPlayer)
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_PLAYER_KILLS_ON_SPREE.GetStatId(), killcounter);
|
|
}
|
|
else if(CPedType::IsCopType(pedType))
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_COPS_KILLS_ON_SPREE.GetStatId(), killcounter);
|
|
}
|
|
else
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_PEDS_KILLS_ON_SPREE.GetStatId(), killcounter);
|
|
}
|
|
|
|
CStatsMgr::SetGreater(STAT_LONGEST_KILLING_SPREE.GetStatId(), (float)sm_KillingSpreeCounter);
|
|
CStatsMgr::SetGreater(STAT_LONGEST_KILLING_SPREE_TIME.GetStatId(), (float)sm_KillingSpreeTotalTime);
|
|
}
|
|
else
|
|
{
|
|
//Reset Killing spree time now and start counting
|
|
sm_KillingSpreeTotalTime = 0;
|
|
sm_KillingSpreeCounter = 0;
|
|
}
|
|
|
|
sm_KillingSpreeTime = curTime;
|
|
|
|
if (killedPlayer)
|
|
{
|
|
if(ShouldCountKills())
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_KILLS_PLAYERS.GetStatId(), 1.0f);
|
|
CStatsMgr::IncrementStat(STAT_MP_KILLS_PLAYERS, 1.0f);
|
|
StatsInterface::IncrementStat(STAT_MPPLY_KILLS_PLAYERS_CHEATER, 1.0f);
|
|
}
|
|
}
|
|
|
|
CStatsMgr::UpdateWeaponKill(killer, weaponHash, headShot, killedPlayer, flags, victim);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : RegisterCarBlownUpByPlayer
|
|
// PURPOSE : This is called by the game code every time a car is killed.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CStatsMgr::RegisterVehicleBlownUpByPlayer(CVehicle* vehicle, const float damage)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
AssertEntityPointerValid_NotInWorld(vehicle);
|
|
|
|
if(!IsPlayerActive())
|
|
return;
|
|
|
|
bool playerWasInsideTheVehicle = false;
|
|
bool updateChainReaction = true;
|
|
|
|
CPed* playerPed = FindPlayerPed();
|
|
const CSeatManager* sm = vehicle->GetSeatManager();
|
|
if (statVerify(sm) && statVerify(playerPed))
|
|
{
|
|
playerWasInsideTheVehicle = (-1 != sm->GetPedsSeatIndex(playerPed));
|
|
}
|
|
|
|
if (!playerWasInsideTheVehicle)
|
|
{
|
|
const bool isLawEnforcement = vehicle->IsLawEnforcementVehicleModelId(vehicle->GetModelId());
|
|
|
|
// increment the stats depending on what type of vehicle this is:
|
|
if (VEHICLE_TYPE_CAR == vehicle->GetVehicleType() || VEHICLE_TYPE_AMPHIBIOUS_AUTOMOBILE == vehicle->GetVehicleType())
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_CARS_EXPLODED.GetStatId(), 1.0f);
|
|
|
|
//The car belongs to the cops
|
|
if(isLawEnforcement && !vehicle->IsPersonalVehicle())
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_CARS_COPS_EXPLODED.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
else if (VEHICLE_TYPE_BIKE == vehicle->GetVehicleType())
|
|
CStatsMgr::IncrementStat(STAT_BIKES_EXPLODED.GetStatId(), 1.0f);
|
|
else if (VEHICLE_TYPE_BOAT==vehicle->GetVehicleType())
|
|
CStatsMgr::IncrementStat(STAT_BOATS_EXPLODED.GetStatId(), 1.0f);
|
|
else if (VEHICLE_TYPE_HELI==vehicle->GetVehicleType())
|
|
CStatsMgr::IncrementStat(STAT_HELIS_EXPLODED.GetStatId(), 1.0f);
|
|
else if (VEHICLE_TYPE_PLANE==vehicle->GetVehicleType())
|
|
CStatsMgr::IncrementStat(STAT_PLANES_EXPLODED.GetStatId(), 1.0f);
|
|
else if (VEHICLE_TYPE_QUADBIKE==vehicle->GetVehicleType() || VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE==vehicle->GetVehicleType())
|
|
CStatsMgr::IncrementStat(STAT_QUADBIKE_EXPLODED.GetStatId(), 1.0f);
|
|
else if (VEHICLE_TYPE_BICYCLE==vehicle->GetVehicleType())
|
|
CStatsMgr::IncrementStat(STAT_BICYCLE_EXPLODED.GetStatId(), 1.0f);
|
|
else if (VEHICLE_TYPE_SUBMARINE==vehicle->GetVehicleType())
|
|
CStatsMgr::IncrementStat(STAT_SUBMARINE_EXPLODED.GetStatId(), 1.0f);
|
|
else if (VEHICLE_TYPE_TRAIN==vehicle->GetVehicleType())
|
|
CStatsMgr::IncrementStat(STAT_TRAIN_EXPLODED.GetStatId(), 1.0f);
|
|
else
|
|
{
|
|
updateChainReaction = false;
|
|
}
|
|
|
|
if (updateChainReaction)
|
|
{
|
|
const unsigned curTime = fwTimer::GetTimeInMilliseconds_NonScaledClipped();
|
|
const unsigned timeSinceLastVehicleDestroyed = curTime - sm_VehicleDestroyedSpreeTime;
|
|
if(timeSinceLastVehicleDestroyed < DESTROYED_VEHICLES_SPREE_INTERVAL && 0 < timeSinceLastVehicleDestroyed && 0 < sm_VehicleDestroyedSpreeTime)
|
|
{
|
|
const bool isTankModel = vehicle->IsTank();
|
|
|
|
const float numVehiclesDestroyed = sm_VehicleDestroyedSpreeCounter == 0 ? 2.0f : 1.0f;
|
|
|
|
sm_VehicleDestroyedSpreeTotalTime += (curTime - sm_VehicleDestroyedSpreeTime);
|
|
sm_VehicleDestroyedSpreeCounter += (sm_VehicleDestroyedSpreeCounter == 0) ? 2 : 1;
|
|
|
|
CStatsMgr::IncrementStat(STAT_VEHICLES_DESTROYED_ON_SPREE.GetStatId(), numVehiclesDestroyed);
|
|
|
|
if(isLawEnforcement)
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_COP_VEHI_DESTROYED_ON_SPREE.GetStatId(), numVehiclesDestroyed);
|
|
}
|
|
else if(isTankModel)
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_TANKS_DESTROYED_ON_SPREE.GetStatId(), numVehiclesDestroyed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sm_VehicleDestroyedSpreeTotalTime = 0;
|
|
sm_VehicleDestroyedSpreeCounter = 0;
|
|
}
|
|
|
|
sm_VehicleDestroyedSpreeTime = curTime;
|
|
}
|
|
}
|
|
else if(vehicle->GetDriver() == playerPed)
|
|
{
|
|
// increment the stats depending on what type of vehicle this is:
|
|
if (VEHICLE_TYPE_CAR == vehicle->GetVehicleType() && damage > 0.0f && vehicle->GetVehicleModelInfo() && vehicle->IsInAir())
|
|
{
|
|
const u32 keyHash = vehicle->GetVehicleModelInfo()->GetHashKey();
|
|
StartNewVehicleRecords(keyHash);
|
|
sm_CrashedVehicleKeyHash = keyHash;
|
|
sm_CrashedVehicleTimer = fwTimer::GetSystemTimeInMilliseconds();
|
|
sm_CrashedVehicleDamageAccumulator += damage;
|
|
CheckCrashDamageAccumulator( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CStatsMgr::RegisterVehicleDamage(CVehicle* vehicle, CEntity* inflictor, eDamageType damageType, float damage, const bool wasWrecked)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
if(!IsPlayerActive())
|
|
return;
|
|
|
|
if (!vehicle || !inflictor)
|
|
return;
|
|
|
|
// we crashed with an armored vehicle, damage is zero but we still want to reset the no crash variables
|
|
if (0.0f >= damage && damageType==DAMAGE_TYPE_COLLISION && IS_LOCAL_PLAYER_DRIVER_OR_LAST_DRIVER(vehicle))
|
|
{
|
|
sm_DriveNoCrashTime = 0.0f;
|
|
sm_DriveNoCrashDist = 0.0f;
|
|
sm_NearMissNoCrashPrecise = 0;
|
|
ClearNearMissArray();
|
|
}
|
|
|
|
if (0.0f >= damage)
|
|
return;
|
|
|
|
const bool isWrecked = (vehicle->GetStatus() == STATUS_WRECKED);
|
|
|
|
//Trashed vehicles wont count.
|
|
if (isWrecked && wasWrecked)
|
|
return;
|
|
|
|
bool inflictorIsLocalPlayer = false;
|
|
if (inflictor->GetIsTypePed())
|
|
{
|
|
inflictorIsLocalPlayer = static_cast<CPed*>(inflictor)->IsLocalPlayer();
|
|
}
|
|
else if (inflictor->GetIsTypeVehicle())
|
|
{
|
|
CPed* driver = static_cast<CVehicle*>(inflictor)->GetDriver();
|
|
if (driver)
|
|
{
|
|
inflictorIsLocalPlayer = driver->IsLocalPlayer();
|
|
}
|
|
}
|
|
|
|
if (inflictorIsLocalPlayer && isWrecked)
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_CARS_WRECKED.GetStatId(), 1.0f);
|
|
|
|
if (vehicle->IsLawEnforcementVehicleModelId(vehicle->GetModelId()))
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_CARS_COPS_WRECKED.GetStatId(), 1.0f);
|
|
}
|
|
}
|
|
|
|
if(damageType == DAMAGE_TYPE_COLLISION)
|
|
{
|
|
CVehicleModelInfo* mi = vehicle->GetVehicleModelInfo();
|
|
if (statVerify(mi) && IS_LOCAL_PLAYER_DRIVER_OR_LAST_DRIVER(vehicle))
|
|
{
|
|
StatsInterface::SetGreater(STAT_LONGEST_DRIVE_NOCRASH.GetStatId(), sm_DriveNoCrashDist);
|
|
UpdateRecordedStat(REC_STAT_LONGEST_DRIVE_NOCRASH, sm_DriveNoCrashDist);
|
|
sm_DriveNoCrashTime = 0.0f;
|
|
sm_DriveNoCrashDist = 0.0f;
|
|
sm_NearMissNoCrash = 0;
|
|
sm_ReverseDrivingPosCurrent.Zero(); // Stop when we crash
|
|
sm_CurrentDrivingReverseDistance=0.0f;
|
|
CheckCrashDamageAccumulator( );
|
|
|
|
sm_CrashedVehicleKeyHash = mi->GetHashKey();
|
|
sm_CrashedVehicleTimer = fwTimer::GetSystemTimeInMilliseconds();
|
|
sm_CrashedVehicleDamageAccumulator += damage;
|
|
sm_NearMissNoCrashPrecise = 0;
|
|
ClearNearMissArray();
|
|
|
|
sm_JumpDistance=0.0f;
|
|
|
|
statAssertf(sm_CrashedVehicleKeyHash > 0, "Vehicle %s with invalid hash key = %u", mi->GetGameName(), sm_CrashedVehicleKeyHash);
|
|
statDebugf3("Vehicle crash - \"%s : %u\" , damage=%f, Accumulator=%f", mi->GetGameName(), sm_CrashedVehicleKeyHash, damage, sm_CrashedVehicleDamageAccumulator);
|
|
}
|
|
|
|
if (inflictor->GetIsTypeVehicle())
|
|
{
|
|
CPed* inflictorDriver = static_cast<CVehicle*>(inflictor)->GetDriver();
|
|
CPed* driver = vehicle->GetDriver();
|
|
if (inflictorDriver && driver)
|
|
{
|
|
if(inflictorDriver->IsNetworkPlayer() && driver->IsLocalPlayer())
|
|
{
|
|
bool cloneIsResponsibleForCollision = CEventNetworkEntityDamage::IsVehicleResponsibleForCollision(vehicle, static_cast<CVehicle*>(inflictor), WEAPONTYPE_RAMMEDBYVEHICLE);
|
|
if(!cloneIsResponsibleForCollision)
|
|
{
|
|
sm_PlayersVehiclesDamages = damage;
|
|
UpdateRecordedStat(REC_STAT_PLAYER_VEHICLE_DAMAGES, sm_PlayersVehiclesDamages);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::RegisterPlayerSetOnFire()
|
|
{
|
|
UpdateRecordedStat(REC_STAT_PLAYERS_SET_ON_FIRE, 1.0f);
|
|
}
|
|
|
|
void
|
|
CStatsMgr::RegisterParachuteDeployed(CPed* ped)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
if(!IsPlayerActive())
|
|
return;
|
|
|
|
statAssert(ped);
|
|
if (!ped)
|
|
return;
|
|
|
|
if (!ped->IsLocalPlayer())
|
|
return;
|
|
|
|
if (!ped->GetIsParachuting())
|
|
return;
|
|
|
|
if(sm_ParachuteDeployPos.IsZero() || sm_hasBailedFromParachuting)
|
|
{
|
|
sm_ParachuteDeployPos = VEC3V_TO_VECTOR3(ped->GetTransform().GetPosition());
|
|
statDebugf3("[Parachute] RegisterParachuteDeployed : sm_ParachuteDeployPos <x=%f,y=%f,z=%f> ", sm_ParachuteDeployPos.GetX(), sm_ParachuteDeployPos.GetY(), sm_ParachuteDeployPos.GetZ());
|
|
sm_hasBailedFromParachuting = false;
|
|
|
|
sm_FreeFallAltitude = 0.0f;
|
|
sm_FreeFallStartPos.Zero();
|
|
}
|
|
|
|
// Stop bail out of vehicle when a parachute is open
|
|
if(!sm_StartVehicleBail.IsZero())
|
|
{
|
|
sm_StartVehicleBail.Zero();
|
|
sm_LastVehicleBailPosition.Zero();
|
|
sm_VehicleBailDistance=0.0f;
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::RegisterPreOpenParachute(CPed* ped)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
if(!IsPlayerActive())
|
|
return;
|
|
|
|
statAssert(ped);
|
|
if (!ped)
|
|
return;
|
|
|
|
if (!ped->IsLocalPlayer())
|
|
return;
|
|
|
|
statDebugf3("[Parachute] RegisterPreOpenParachute");
|
|
sm_HasStartedParachuteDeploy = true;
|
|
}
|
|
|
|
|
|
void CStatsMgr::RegisterDestroyParachute(const CPed* pPed)
|
|
{
|
|
statDebugf3("[Parachute] RegisterDestroyParachute");
|
|
|
|
sm_hasBailedFromParachuting = pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsInTheAir);
|
|
|
|
if(sm_hasBailedFromParachuting)
|
|
{
|
|
statDebugf3("[Parachute] RegisterDestroyParachute : Bailing from parachute");
|
|
|
|
Vec3V coords = pPed->GetTransform().GetPosition();
|
|
|
|
const float groundZ = CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(coords.GetXf(), coords.GetYf());
|
|
const float altitude = coords.GetZf() - groundZ;
|
|
|
|
sm_ParachuteStartTime = 0;
|
|
sm_HasStartedParachuteDeploy = false;
|
|
|
|
if (altitude > sm_TuneMetadata.m_FreefallThresold)
|
|
{
|
|
sm_WasInAir = true;
|
|
sm_FreeFallStartPos = VEC3V_TO_VECTOR3(coords);
|
|
statDebugf3("[Parachute] RegisterDestroyParachute : Start Freefall Position <x=%f,y=%f,z=%f> altitude=<%f>", sm_FreeFallStartPos.x, sm_FreeFallStartPos.y, sm_FreeFallStartPos.z, altitude);
|
|
|
|
sm_FreeFallStartTimer = fwTimer::GetTimeInMilliseconds();
|
|
sm_FreeFallDueToPlayerDamage = false;
|
|
|
|
if (pPed->GetWeaponDamageEntity())
|
|
{
|
|
const u32 timeSinceLastDamage = fwTimer::GetTimeInMilliseconds() - pPed->GetWeaponDamagedTime();
|
|
if (timeSinceLastDamage <= sm_TuneMetadata.m_FreefallTimeSinceLastDamage)
|
|
{
|
|
sm_FreeFallDueToPlayerDamage = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::RegisterStartSkydiving(CPed* pPed)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
if(!IsPlayerActive())
|
|
return;
|
|
|
|
statAssert(pPed);
|
|
if (!pPed)
|
|
return;
|
|
|
|
if (!pPed->IsLocalPlayer())
|
|
return;
|
|
|
|
if (!pPed->GetIsParachuting())
|
|
return;
|
|
|
|
if(sm_ChallengeSkydiveStartPos.IsZero())
|
|
{
|
|
sm_ChallengeSkydiveStartPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
}
|
|
statDebugf3("Start Skydive Position <x=%f,y=%f,z=%f> ", sm_ChallengeSkydiveStartPos.x, sm_ChallengeSkydiveStartPos.y, sm_ChallengeSkydiveStartPos.z);
|
|
}
|
|
|
|
|
|
void CStatsMgr::FallenThroughTheMap(CPed* pPed)
|
|
{
|
|
statAssert(pPed);
|
|
if (!pPed)
|
|
return;
|
|
if (!pPed->IsLocalPlayer())
|
|
return;
|
|
|
|
ResetRecordingStats();
|
|
}
|
|
|
|
//True when the player is inside a Train
|
|
static bool s_IsInsideTrain = false;
|
|
//True when the player on a Train
|
|
static bool s_IsOnTrain = false;
|
|
//True when the player on a Boat
|
|
static bool s_IsOnBoat = false;
|
|
//True when the player on a Car
|
|
static bool s_IsOnCar = false;
|
|
|
|
bool
|
|
CStatsMgr::IsInsideTrain( )
|
|
{
|
|
return s_IsInsideTrain;
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::IsOnTrain()
|
|
{
|
|
return s_IsOnTrain;
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::IsOnCar()
|
|
{
|
|
return s_IsOnCar;
|
|
}
|
|
|
|
bool
|
|
CStatsMgr::IsOnBoat()
|
|
{
|
|
return s_IsOnBoat;
|
|
}
|
|
|
|
void
|
|
CStatsMgr::ProcessOnFootVehicleChecks(CPed* playerPed)
|
|
{
|
|
if (!CStatsUtils::IsStatsTrackingEnabled())
|
|
return;
|
|
|
|
static u32 s_PreviousFrame = 0;
|
|
|
|
if (playerPed && s_PreviousFrame != fwTimer::GetFrameCount())
|
|
{
|
|
s_IsInsideTrain = false;
|
|
|
|
CPedIntelligence* pPI = playerPed->GetPedIntelligence();
|
|
if (pPI)
|
|
{
|
|
CQueriableInterface* pQI = pPI->GetQueriableInterface();
|
|
if(pQI && pQI->IsTaskCurrentlyRunning(CTaskTypes::TASK_PLAYER_ON_FOOT))
|
|
{
|
|
const CTaskPlayerOnFoot* playerOnFootTask = static_cast<const CTaskPlayerOnFoot*>(pPI->FindTaskActiveByType(CTaskTypes::TASK_PLAYER_ON_FOOT));
|
|
if(playerOnFootTask && playerOnFootTask->GetState() == CTaskPlayerOnFoot::STATE_RIDE_TRAIN)
|
|
{
|
|
s_IsInsideTrain = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
s_IsOnTrain = false;
|
|
s_IsOnBoat = false;
|
|
s_IsOnCar = false;
|
|
const CEntity* entity = playerPed->GetGroundPhysical();
|
|
|
|
|
|
if(!entity)
|
|
{
|
|
entity = (const CEntity *)playerPed->GetAttachParent();
|
|
}
|
|
|
|
if (entity && entity->GetIsPhysical())
|
|
{
|
|
const CPhysical* physical = static_cast<const CPhysical *>(entity);
|
|
if(physical->GetIsTypeVehicle())
|
|
{
|
|
const CVehicle* vehicle = static_cast<const CVehicle *>(physical);
|
|
|
|
// If we're on a moving vehicle for more than 1 second, reset the start position for the challenge "highest altitude on foot"
|
|
static const float MIN_VEHICLE_SPEED2 = 2.0f;
|
|
static const float SECONDS_BEFORE_POSITION_RESET = 1000.0f;
|
|
if(vehicle->GetVelocity().Mag2()>MIN_VEHICLE_SPEED2)
|
|
{
|
|
sm_FootOnVehicleTimer+=fwTimer::GetTimeStepInMilliseconds();
|
|
if(sm_FootOnVehicleTimer>SECONDS_BEFORE_POSITION_RESET)
|
|
{
|
|
statDebugf3("Spent too much time on a moving vehicle, reset onFootStartPosition");
|
|
sm_OnFootPosStart.Zero();
|
|
}
|
|
}
|
|
|
|
Vector3 currentPosition = VEC3V_TO_VECTOR3(vehicle->GetTransform().GetPosition());
|
|
if (sm_VehiclePreviousPosition.IsZero())
|
|
{
|
|
sm_VehiclePreviousPosition = currentPosition;
|
|
}
|
|
|
|
const float distanceTravelled = Mag(vehicle->GetTransform().GetPosition() - VECTOR3_TO_VEC3V(sm_VehiclePreviousPosition)).Getf();
|
|
|
|
if(vehicle->InheritsFromTrain())
|
|
{
|
|
s_IsOnTrain = true;
|
|
if (vehicle->GetVelocity().Mag2() >= MIN_SPEED_TO_CONSIDER_MOVEMENT)
|
|
{
|
|
CStatsMgr::IncrementStat(STAT_DIST_AS_PASSENGER_TRAIN.GetStatId(), distanceTravelled, STATUPDATEFLAG_ASSERTONLINESTATS);
|
|
}
|
|
}
|
|
else if (vehicle->InheritsFromBoat())
|
|
{
|
|
s_IsOnBoat = true;
|
|
}
|
|
else if (vehicle->InheritsFromAutomobile())
|
|
{
|
|
s_IsOnCar = true;
|
|
}
|
|
|
|
sm_VehiclePreviousPosition = currentPosition;
|
|
}
|
|
else
|
|
{
|
|
sm_FootOnVehicleTimer=0.0f;
|
|
}
|
|
}
|
|
|
|
s_PreviousFrame = fwTimer::GetFrameCount();
|
|
}
|
|
}
|
|
|
|
#if __ASSERT
|
|
|
|
const atHashWithStringNotFinal WEAPONTYPE_DIGISCANNER("WEAPON_DIGISCANNER",0xFDBADCED);
|
|
const atHashWithStringNotFinal WEAPONTYPE_REMOTESNIPER("WEAPON_REMOTESNIPER",0x33058E22);
|
|
const atHashWithStringNotFinal WEAPONTYPE_BZGAS("WEAPON_BZGAS",0xa0973d5e);
|
|
const atHashWithStringNotFinal WEAPONTYPE_ELECTRIC_FENCE("WEAPON_ELECTRIC_FENCE",0x92BD4EBB);
|
|
|
|
const StatId_char STAT_ANNIHL_BULLET_SHOTS("ANNIHL_BULLET_SHOTS", false);
|
|
const StatId_char STAT_WEAPON_HIT_BY_WATER_CANNON("WEAPON_HIT_BY_WATER_CANNON", false);
|
|
|
|
void
|
|
CStatsMgr::MissingWeaponStatName(const u32 hashType, StatId& stat, const u32 weaponHash, const u32 damageType, const char* weaponname)
|
|
{
|
|
if (CScriptHud::bUsingMissionCreator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (STATTYPE_WEAPON_HELDTIME == hashType && weaponHash == WEAPONTYPE_UNARMED)
|
|
{
|
|
//Exception
|
|
}
|
|
else if (STATTYPE_WEAPON_SHOTS == hashType && weaponHash == WEAPONTYPE_UNARMED)
|
|
{
|
|
//Exception
|
|
}
|
|
else if (STATTYPE_WEAPON_HITS == hashType && damageType == DAMAGE_TYPE_EXPLOSIVE)
|
|
{
|
|
//Exception for punching people while holding a grenade or some sort of explosive
|
|
}
|
|
else if (stat.GetHash() == STAT_ANNIHL_BULLET_SHOTS.GetHash())
|
|
{
|
|
//Exception
|
|
}
|
|
else if (stat.GetHash() == STAT_WEAPON_HIT_BY_WATER_CANNON.GetHash())
|
|
{
|
|
//Exception
|
|
}
|
|
else if(stat.GetHash() == atHashWithStringNotFinal("MP0_ROCKET_KILLS", 0x9022A747))
|
|
{
|
|
//Exception
|
|
}
|
|
else if (statVerify(weaponname)
|
|
&& weaponHash != WEAPONTYPE_DROWNING.GetHash()
|
|
&& weaponHash != WEAPONTYPE_DROWNINGINVEHICLE.GetHash()
|
|
&& weaponHash != WEAPONTYPE_EXPLOSION.GetHash()
|
|
&& weaponHash != WEAPONTYPE_FALL.GetHash()
|
|
&& weaponHash != WEAPONTYPE_FIRE.GetHash()
|
|
&& weaponHash != WEAPONTYPE_RAMMEDBYVEHICLE.GetHash()
|
|
&& weaponHash != WEAPONTYPE_RUNOVERBYVEHICLE.GetHash()
|
|
&& weaponHash != WEAPONTYPE_DIGISCANNER.GetHash()
|
|
&& weaponHash != WEAPONTYPE_GOLFCLUB.GetHash()
|
|
&& weaponHash != WEAPONTYPE_REMOTESNIPER.GetHash()
|
|
&& weaponHash != WEAPONTYPE_BZGAS.GetHash()
|
|
&& weaponHash != WEAPONTYPE_ELECTRIC_FENCE.GetHash())
|
|
{
|
|
if (hashType == STATTYPE_WEAPON_HEADSHOTS.GetHash())
|
|
{
|
|
if (!(weaponHash == WEAPONTYPE_GRENADELAUNCHER.GetHash() || weaponHash == WEAPONTYPE_DLC_RAILGUN.GetHash() || weaponHash == WEAPONTYPE_VEHICLE_PLAYER_BULLET.GetHash()))
|
|
{
|
|
statAssertf(damageType <= DAMAGE_TYPE_NONE || damageType >= NUM_EDAMAGETYPE, "Missing Weapon \"%s\" with stat named \"%s\".", weaponname, stat.GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
statAssertf(damageType <= DAMAGE_TYPE_NONE || damageType >= NUM_EDAMAGETYPE, "Missing Weapon \"%s\" with stat named \"%s\" and damage type \"%u\".", weaponname, stat.GetName(), damageType);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#endif // __ASSERT
|
|
|
|
|
|
void CStatsMgr::StartBailVehicle(CPed* playerPed)
|
|
{
|
|
// Only start/reset bail stats variables if the local player bails, not any other peds/players
|
|
if (playerPed && playerPed->IsLocalPlayer())
|
|
{
|
|
CVehicle * playerVehicle = playerPed->GetMyVehicle();
|
|
if (playerVehicle != NULL)
|
|
{
|
|
if (!playerVehicle->GetIsLandVehicle())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
sm_CurrentSpeed = 0.0f;
|
|
|
|
if (g_PlayerSwitch.IsActive()) // We don't want to register bails from a vehicle if we are in sky cam
|
|
return;
|
|
|
|
if(sm_StartVehicleBail.IsZero())
|
|
{
|
|
sm_VehicleBailDistance = 0.0f;
|
|
sm_StartVehicleBail = VEC3V_TO_VECTOR3(playerPed->GetTransform().GetPosition());
|
|
sm_LastVehicleBailPosition = sm_StartVehicleBail;
|
|
statDebugf3("Bailing out of the vehicle at position <x=%f,y=%f,z=%f>", sm_StartVehicleBail.x, sm_StartVehicleBail.y, sm_StartVehicleBail.z);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CStatsMgr::ShouldCountKills()
|
|
{
|
|
// Since url:bugstar:7546782, we only count kills in instanced content
|
|
if (CountKillsOnlyInInstancedContent())
|
|
{
|
|
const bool isInstancedContent = CNetwork::GetNetworkSession().IsActivitySession();
|
|
return isInstancedContent;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CStatsMgr::CountKillsOnlyInInstancedContent()
|
|
{
|
|
bool onlyInInstancedContent = true;
|
|
::Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("COUNT_KILLS_ONLY_IN_INSTANCED_CONTENT", 0x57F41BE0), onlyInInstancedContent);
|
|
return onlyInInstancedContent;
|
|
}
|
|
|
|
void CStatsMgr::UpdateBoat(CVehicle* playerVehicle)
|
|
{
|
|
if(!statVerify(playerVehicle))
|
|
return;
|
|
if(!statVerifyf(playerVehicle->InheritsFromBoat(), "Vehicle is supposed to inherit from Boat"))
|
|
return;
|
|
|
|
CBoat* boat = static_cast<CBoat*>(playerVehicle);
|
|
|
|
bool isInAir = playerVehicle->GetFrameCollisionHistory()->GetNumCollidedEntities()==0;
|
|
|
|
// If we're dropped from a helicopter or something similar, doesnt count as beaching
|
|
if(sm_HadAttachedParentVehicle)
|
|
{
|
|
sm_wasInWaterPreviously=false;
|
|
sm_HadAttachedParentVehicle=false;
|
|
}
|
|
|
|
if(sm_wasInWaterPreviously && !boat->GetWasInWater())
|
|
{
|
|
sm_outOfWater=true;
|
|
sm_LastBoatPositionInWater = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetPosition());
|
|
}
|
|
// If we're not in water anymore, lets wait until we hit the ground to start counting
|
|
if(sm_outOfWater && sm_BoatPositionOnGround.IsZero() && !isInAir)
|
|
{
|
|
sm_BoatPositionOnGround = VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetPosition());
|
|
sm_inAirAfterGroundTimer=0.0f;
|
|
}
|
|
|
|
// If we're not in water anymore, we hit the ground before but we're in the air right after (we might just have bounced on the shore, doesnt count as beaching the boat)
|
|
if(sm_outOfWater && !sm_BoatPositionOnGround.IsZero())
|
|
{
|
|
if(isInAir)
|
|
{
|
|
sm_inAirAfterGroundTimer+=fwTimer::GetTimeStepInMilliseconds();
|
|
}
|
|
else
|
|
{
|
|
sm_inAirAfterGroundTimer=0.0f;
|
|
}
|
|
}
|
|
|
|
// Back in the water
|
|
if(sm_outOfWater && playerVehicle->GetWasInWater())
|
|
{
|
|
sm_BoatPositionOnGround.Zero();
|
|
sm_BoatDistanceOnGround = 0.0f;
|
|
sm_outOfWater=false;
|
|
sm_inAirAfterGroundTimer=0.0f;
|
|
}
|
|
// Spent too much time in the air, doesnt count as beaching
|
|
static const float TIME_IN_AIR_THRESHOLD = 1000.0f;
|
|
if(sm_inAirAfterGroundTimer>TIME_IN_AIR_THRESHOLD)
|
|
{
|
|
statDebugf3("Spent more than %.2f ms in the air, beaching doesnt count", TIME_IN_AIR_THRESHOLD);
|
|
sm_BoatPositionOnGround.Zero();
|
|
sm_BoatDistanceOnGround = 0.0f;
|
|
sm_outOfWater=false;
|
|
sm_inAirAfterGroundTimer=0.0f;
|
|
}
|
|
else if(!sm_BoatPositionOnGround.IsZero())
|
|
{
|
|
// we're still on the ground
|
|
sm_BoatDistanceOnGround = Mag(playerVehicle->GetTransform().GetPosition()-VECTOR3_TO_VEC3V(sm_LastBoatPositionInWater)).Getf();
|
|
UpdateRecordedStat(REC_STAT_DIST_BEACHING_BOAT, sm_BoatDistanceOnGround);
|
|
}
|
|
sm_wasInWaterPreviously = boat->GetWasInWater();
|
|
}
|
|
|
|
void CStatsMgr::UpdateRecordedStat(CStatsMgr::RecordStat stat, float value)
|
|
{
|
|
if(m_recordedStat != stat)
|
|
return;
|
|
Assertf(m_statRecordingPolicy != RSP_NONE, "Policy should be set");
|
|
|
|
if(!FPIsFinite(m_recordedStatValue))
|
|
{
|
|
m_recordedStatValue = value;
|
|
statDebugf3("Recorder : UpdateRecordedStat, new value : %f", m_recordedStatValue);
|
|
return;
|
|
}
|
|
Assertf(FPIsFinite(m_recordedStatValue), "The value should already have been set");
|
|
|
|
if(m_statRecordingPolicy == RSP_SUM)
|
|
{
|
|
m_recordedStatValue += value;
|
|
statDebugf3("Recorder : UpdateRecordedStat, new value : %f", m_recordedStatValue);
|
|
}
|
|
else if(m_statRecordingPolicy == RSP_GREATEST)
|
|
{
|
|
if(m_recordedStatValue < value)
|
|
{
|
|
m_recordedStatValue = value;
|
|
statDebugf3("Recorder : UpdateRecordedStat, new value : %f", m_recordedStatValue);
|
|
}
|
|
}
|
|
else if(m_statRecordingPolicy == RSP_LOWEST)
|
|
{
|
|
if(m_recordedStatValue > value)
|
|
{
|
|
m_recordedStatValue = value;
|
|
statDebugf3("Recorder : UpdateRecordedStat, new value : %f", m_recordedStatValue);
|
|
}
|
|
}
|
|
ASSERT_ONLY(else Assertf(0, "Cannot process a new value when no timeframe is started");)
|
|
}
|
|
|
|
bool CStatsMgr::GetRecordedStatValue(float& value)
|
|
{
|
|
if(FPIsFinite(m_recordedStatValue))
|
|
{
|
|
value = m_recordedStatValue;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
void CStatsMgr::StartRecordingStat(RecordStat stat, RecordStatPolicy policy)
|
|
{
|
|
statAssertf(policy != RSP_NONE, "Cannot record with a policy set to NONE");
|
|
m_recordedStat = stat;
|
|
m_statRecordingPolicy = policy;
|
|
MakeNan(m_recordedStatValue);
|
|
statDebugf3("Recorder : StartRecordingStat stat %d", (int)stat);
|
|
|
|
ResetRecordingStats();
|
|
}
|
|
|
|
void CStatsMgr::ResetRecordingStats()
|
|
{
|
|
statDebugf3("Recorder : ResetRecordingStats");
|
|
sm_JumpDistance = 0.0f;
|
|
sm_LastJumpHeight = 0.0f;
|
|
sm_CurrentSpeed = 0.0f;
|
|
sm_BikeRearWheelDist = 0.0f;
|
|
sm_IsWheelingOnPushBike = false;
|
|
sm_IsDoingAStoppieOnPushBike = false;
|
|
sm_BikeFrontWheelDist = 0.0f;
|
|
sm_DriveNoCrashTime = 0.0f;
|
|
sm_DriveNoCrashDist = 0.0f;
|
|
sm_VehicleFlipsAccumulator = 0;
|
|
sm_VehicleSpinsAccumulator = 0.0f;
|
|
sm_VehicleSpinsAccumulator = 0.0f;
|
|
sm_VehicleRollsAccumulator = 0.0f;
|
|
sm_VehicleRollsHeadingLast = 0.0f;
|
|
sm_NearMissNoCrash = 0;
|
|
sm_PlaneBarrelRollCounter = 0.0f;
|
|
sm_FlyingCounter = 0.0f;
|
|
sm_FlyingDistance = 0.0f;
|
|
sm_OnFootPosStart.Zero();
|
|
sm_FootAltitude = 0.0f;
|
|
sm_PlayersVehiclesDamages = 0.0f;
|
|
sm_VehicleBailDistance = 0.0f;
|
|
sm_CurrentDrivingReverseDistance = 0.0f;
|
|
sm_NearMissNoCrashPrecise = 0;
|
|
sm_StartVehicleBail.Zero();
|
|
sm_FlyingAltitude = 0.0f;
|
|
sm_ValidFlyingAltitude = false;
|
|
sm_HasStartedParachuteDeploy = false;
|
|
sm_FreeFallAltitude = 0.0f;
|
|
sm_LastWallridePosition.Zero();
|
|
sm_WallrideDistance = 0.0f;
|
|
ClearNearMissArray();
|
|
}
|
|
|
|
void CStatsMgr::StopRecordingStat()
|
|
{
|
|
float val = 0.0f;
|
|
if(GetRecordedStatValue(val))
|
|
{
|
|
statDebugf3("Recorder : StopRecordingStat, value = %f", val);
|
|
}
|
|
else
|
|
{
|
|
statDebugf3("Recorder : StopRecordingStat, nothing has been recorded");
|
|
}
|
|
m_statRecordingPolicy = RSP_NONE;
|
|
m_recordedStat = REC_STAT_NONE;
|
|
ResetRecordingStats();
|
|
}
|
|
|
|
bool CStatsMgr::IsRecordingStat()
|
|
{
|
|
return m_statRecordingPolicy != RSP_NONE;
|
|
}
|
|
|
|
void CStatsMgr::StartTrackingStunts()
|
|
{
|
|
statDebugf3("Start tracking stunts");
|
|
s_isTrackingStunts = true;
|
|
}
|
|
|
|
void CStatsMgr::StopTrackingStunts()
|
|
{
|
|
statDebugf3("Stop tracking stunts");
|
|
s_isTrackingStunts = false;
|
|
}
|
|
|
|
void CStatsMgr::ClearNearMissArray()
|
|
{
|
|
for(unsigned i = 0; i < MAXIMUM_NEAR_MISS_COUNTER; i++)
|
|
{
|
|
sm_NearMissCounterArray[i] = 0;
|
|
}
|
|
}
|
|
|
|
void CStatsMgr::AddNearMissTimeToArray(u32 nearMissTime)
|
|
{
|
|
for(unsigned i = 0; i < MAXIMUM_NEAR_MISS_COUNTER; i++)
|
|
{
|
|
if(sm_NearMissCounterArray[i] == 0)
|
|
{
|
|
sm_NearMissCounterArray[i] = nearMissTime;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
|
|
#define MAX_DISPATCH_DRAWABLES 100
|
|
static CDebugDrawStore ms_debugDraw(MAX_DISPATCH_DRAWABLES);
|
|
|
|
void CStatsMgr::drawNonFlyableAreas()
|
|
{
|
|
for(int i = 0; i < sm_TuneMetadata.m_nonFlyableAreas.m_areas.size(); i++)
|
|
{
|
|
const NonFlyableArea& area = sm_TuneMetadata.m_nonFlyableAreas.m_areas[i];
|
|
const ::rage::Vector4& rect = area.m_rectXYWH;
|
|
ms_debugDraw.AddVectorMapLine( Vec3V(rect.x, rect.y, 0.0f), Vec3V(rect.x+rect.w, rect.y, 0.0f), Color_red, 100);
|
|
ms_debugDraw.AddVectorMapLine( Vec3V(rect.x+rect.w, rect.y, 0.0f), Vec3V(rect.x+rect.w, rect.y+rect.z, 0.0f), Color_red, 100);
|
|
ms_debugDraw.AddVectorMapLine( Vec3V(rect.x+rect.w, rect.y+rect.z, 0.0f), Vec3V(rect.x, rect.y+rect.z, 0.0f), Color_red, 100);
|
|
ms_debugDraw.AddVectorMapLine( Vec3V(rect.x, rect.y+rect.z, 0.0f), Vec3V(rect.x, rect.y, 0.0f), Color_red, 100);
|
|
|
|
}
|
|
ms_debugDraw.Render();
|
|
}
|
|
|
|
#endif // __BANK
|
|
|
|
// eof
|