7786 lines
263 KiB
C++
7786 lines
263 KiB
C++
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FILE : player.cpp
|
|
// PURPOSE : Specific information for each player is stored here.
|
|
// AUTHOR : Obbe
|
|
// CREATED : 3-11-99
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef _PLAYER_H_
|
|
#include "peds/PlayerInfo.h"
|
|
#endif
|
|
|
|
// Framework headers
|
|
#include "grcore/debugdraw.h"
|
|
#include "fwmaths/angle.h"
|
|
#include "fwmaths/Vector.h"
|
|
#include "fwscene/world/WorldLimits.h"
|
|
|
|
// Game headers
|
|
#include "ai/debug/system/AIDebugLogManager.h"
|
|
#include "ai/debug/types/VehicleDebugInfo.h"
|
|
#include "audio/emitteraudioentity.h"
|
|
#include "audio/frontendaudioentity.h"
|
|
#include "audio/vehicleaudioentity.h"
|
|
#include "camera/CamInterface.h"
|
|
#include "camera/gameplay/GameplayDirector.h"
|
|
#include "camera/gameplay/aim/FirstPersonShooterCamera.h"
|
|
#include "camera/helpers/ControlHelper.h"
|
|
#include "camera/switch/SwitchDirector.h"
|
|
#include "control/gamelogic.h"
|
|
#include "control/remote.h"
|
|
#include "control/replay/replay.h"
|
|
#include "debug/debugscene.h"
|
|
#include "event/eventdamage.h"
|
|
#include "event/eventnetwork.h"
|
|
#include "event/events.h"
|
|
#include "event/eventshocking.h"
|
|
#include "event/shockingevents.h"
|
|
#include "frontend/loadingscreens.h"
|
|
#include "frontend/MobilePhone.h"
|
|
#include "frontend/NewHud.h"
|
|
#include "fwscene/search/searchvolumes.h"
|
|
#include "game/cheat.h"
|
|
#include "game/clock.h"
|
|
#include "game/crimeinformation.h"
|
|
#include "game/modelindices.h"
|
|
#include "game/weather.h"
|
|
#include "modelinfo/vehiclemodelinfo.h"
|
|
#include "network/objects/entities/netobjplayer.h"
|
|
#include "objects/object.h"
|
|
#include "peds/ped.h"
|
|
#include "peds/ped_channel.h"
|
|
#include "peds/pedcapsule.h"
|
|
#include "peds/peddebugvisualiser.h"
|
|
#include "peds/pedfactory.h"
|
|
#include "peds/pedgeometryanalyser.h"
|
|
#include "peds/pedintelligence.h"
|
|
#include "peds/pedmoveblend/pedmoveblendinwater.h"
|
|
#include "peds/pedtype.h"
|
|
#include "peds/queriableinterface.h"
|
|
#include "peds/rendering/pedvariationstream.h"
|
|
#include "peds/wildlifemanager.h"
|
|
#include "physics/physics.h"
|
|
#include "pickups/pickupmanager.h"
|
|
#include "renderer/zonecull.h"
|
|
#include "scene/EntityIterator.h"
|
|
#include "scene/world/gameworld.h"
|
|
#include "script/script.h"
|
|
#include "script/script_hud.h"
|
|
#include "stats/stats_channel.h"
|
|
#include "stats/statsinterface.h"
|
|
#include "stats/statsmgr.h"
|
|
#include "streaming/streaming.h"
|
|
#include "system/appcontent.h"
|
|
#include "system/control.h"
|
|
#include "system/control_channel.h"
|
|
#include "system/controlMgr.h"
|
|
#include "system/pad.h"
|
|
#include "task/combat/cover/taskcover.h"
|
|
#include "task/combat/combatmanager.h"
|
|
#include "task/combat/taskcombat.h"
|
|
#include "task/combat/taskdamagedeath.h"
|
|
#include "task/combat/tasksharkattack.h"
|
|
#include "task/default/taskplayer.h"
|
|
#include "task/general/tasksecondary.h"
|
|
#include "Task/General/Phone/TaskMobilePhone.h"
|
|
#include "task/motion/locomotion/taskmotionped.h"
|
|
#include "task/Movement/TaskParachute.h"
|
|
#include "task/Movement/TaskJetpack.h"
|
|
#include "task/Physics/TaskNMHighFall.h"
|
|
#include "task/Physics/TaskNMJumpRollFromRoadVehicle.h"
|
|
#include "task/vehicle/taskcar.h"
|
|
#include "task/vehicle/taskcarutils.h"
|
|
#include "task/vehicle/taskentervehicle.h"
|
|
#include "task/vehicle/taskinvehicle.h"
|
|
#include "task/vehicle/TaskMountAnimalWeapon.h"
|
|
#include "task/weapons/gun/TaskAimGunOnFoot.h"
|
|
#include "text/messages.h"
|
|
#include "text/text.h"
|
|
#include "vehicleai/pathfind.h"
|
|
#include "vehicleai/task/taskvehicletempaction.h"
|
|
#include "vehicleai/vehicleintelligence.h"
|
|
#include "vehicles/automobile.h"
|
|
#include "vehicles/bike.h"
|
|
#include "vehicles/boat.h"
|
|
#include "vehicles/heli.h"
|
|
#include "vehicles/metadata/vehiclelayoutinfo.h"
|
|
#include "vehicles/metadata/vehiclemetadatamanager.h"
|
|
#include "vehicles/metadata/vehicleseatinfo.h"
|
|
#include "vehicles/Planes.h"
|
|
#include "vehicles/submarine.h"
|
|
#include "vehicles/train.h"
|
|
#include "vehicles/vehicle.h"
|
|
#include "vehicles/vehiclegadgets.h"
|
|
#include "vfx/misc/fire.h"
|
|
#include "weapons/explosion.h"
|
|
#include "weapons/projectiles/projectilemanager.h"
|
|
|
|
// Parser headers
|
|
#include "Peds/PlayerInfo_parser.h"
|
|
|
|
//#include "zones.h"
|
|
|
|
AI_OPTIMISATIONS()
|
|
AI_VEHICLE_OPTIMISATIONS()
|
|
|
|
// we need one additional slot to account for GTAO on Xbox One where we have to add
|
|
// players to a temporary slot whilst we retrieve their display name
|
|
// when we fully add the player, they are added fully before the temporary slot is removed
|
|
// so we have a maximum of our full complement of MAX_NUM_PHYSICAL_PLAYERS plus one slot to
|
|
// account for the brief (same frame) period where one player is in the player pool twice
|
|
static const unsigned MAX_NUM_PLAYER_INFOS = MAX_NUM_PHYSICAL_PLAYERS + 1;
|
|
|
|
FW_INSTANTIATE_CLASS_POOL(CPlayerInfo, MAX_NUM_PLAYER_INFOS, atHashString("CPlayerInfo",0x3ef71716));
|
|
|
|
PARAM(invincible, "Make player invincible");
|
|
|
|
#define MIN_SPRINT_ENERGY (-150.0f)
|
|
|
|
//
|
|
// Statics
|
|
CDynamicCoverHelper CPlayerInfo::ms_DynamicCoverHelper;
|
|
Vector3 CPlayerInfo::ms_cachedMainPlayerPos(0.0f,0.0f,0.0f);
|
|
bool CPlayerInfo::ms_bHasDisplayedPlayerQuitEnterCarHelpText=false;
|
|
|
|
#if !__FINAL
|
|
bool CPlayerInfo::ms_bFakeWorldExtentsTresspassing = false;
|
|
#endif
|
|
|
|
//
|
|
// Constants
|
|
const float CPlayerInfo::ms_fInteriorShockingEventFreq = 2.0f;
|
|
|
|
// Melee
|
|
dev_u32 CPlayerInfo::ms_nMeleeClipSetReleaseDuration = 30000;
|
|
|
|
//
|
|
// Tune data
|
|
bank_float CPlayerInfo::ms_PlayerSoftAimBoundary = 0.78f;
|
|
#if FPS_MODE_SUPPORTED
|
|
bank_float CPlayerInfo::ms_PlayerFPSFireBoundaryLow = 0.1f;
|
|
bank_float CPlayerInfo::ms_PlayerFPSFireBoundaryHigh = 0.78f;
|
|
#endif
|
|
|
|
#if FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
bool CPlayerInfo::sm_bShouldUpdateScopeStateFromMenu = false;
|
|
#endif
|
|
|
|
bank_float CPlayerInfo::ms_PlayerLowAimBoundry = ioValue::ANALOG_BUTTON_DOWN_THRESHOLD; // roughly 10 in a 0-255 scale.
|
|
bank_bool CPlayerInfo::ms_ReverseAimBoundarys = false;
|
|
|
|
bank_float CPlayerInfo::ms_PlayerSoftTargetSwitchBoundary = 0.98f;
|
|
|
|
bank_float CPlayerInfo::ms_StealthNoiseExponentialDecayTimeConstant = 1.5f;
|
|
bank_float CPlayerInfo::ms_StealthNoiseMinValToCareAbout = 1.0f;
|
|
|
|
bank_float CPlayerInfo::ms_fDampenRootRate = 5.0f;
|
|
spdAABB CPlayerInfo::ms_WorldLimitsPlayerAABB = spdAABB();
|
|
bool CPlayerInfo::ms_bPlayerBoundaryInitialized = false;
|
|
|
|
// Make timers different when we are really far out (only do this for player)
|
|
// This means if we crash a plane / heli far out then we'll die before we have to swim all the way back.
|
|
dev_float PLAYER_SWIMMING_RANGE = 10000.0f;
|
|
dev_float PLAYER_MAX_TIME_IN_WATER_LONG_RANGE = 60.0f;
|
|
|
|
// Timer to avoid player getting stuck permanently hit by water cannon
|
|
dev_float PLAYER_MAX_TIME_HIT_BY_WATER_CANNON = 20.0f;
|
|
dev_float PLAYER_WATER_CANNON_COOLDOWN_RATE = 0.1f; // Hit by timer decreases by rate * t every frame if > 0.0f
|
|
dev_float PLAYER_WATER_CANNON_IMMUNE_TIME = 10.0f;
|
|
|
|
//
|
|
// Debug
|
|
#if !__FINAL
|
|
bool CPlayerInfo::ms_bDebugPlayerInvincible = false;
|
|
bool CPlayerInfo::ms_bDebugPlayerInvincibleRestoreHealth = false;
|
|
bool CPlayerInfo::ms_bDebugPlayerInvincibleRestoreArmorWithHealth = false;
|
|
bool CPlayerInfo::ms_bDebugPlayerInvisible = false;
|
|
bool CPlayerInfo::ms_bDebugPlayerInfiniteStamina = false;
|
|
#endif // !__FINAL
|
|
#if __ASSERT
|
|
bool CPlayerInfo::ms_ChangingPlayerModel = false;
|
|
#endif // __ASSERT
|
|
#if __BANK
|
|
bool CPlayerInfo::ms_bDisplayRecklessDriving = false;
|
|
bool CPlayerInfo::ms_bDisplayNumEnemiesInCombat = false;
|
|
bool CPlayerInfo::ms_bDisplayCoverTracking = false;
|
|
bool CPlayerInfo::ms_bDisplayCombatLoitering = false;
|
|
bool CPlayerInfo::ms_bDebugCombatLoitering = false;
|
|
bool CPlayerInfo::ms_bDisplayNumEnemiesShootingInCombat = false;
|
|
bool CPlayerInfo::ms_bDebugCandidateChargeGoalPositions = false;
|
|
bool CPlayerInfo::ms_bDebugDrawCandidateChargePositions = false;
|
|
#endif // __BANK
|
|
|
|
|
|
// Structure used in searches for closest cars/trains
|
|
struct FindClosestCarCBData{
|
|
CPed* pPed;
|
|
CVehicle* pVehicle;
|
|
float Closeness;
|
|
Vector3 vSearchDir;
|
|
bool bStickInput;
|
|
};
|
|
|
|
// Structure used in searches for closest mounts
|
|
struct FindClosestRidableAnimalCBData{
|
|
CPed* pPed;
|
|
CPed* pAnimal;
|
|
float Closeness;
|
|
Vector3 vSearchDir;
|
|
bool bStickInput;
|
|
bool bDepthCheck;
|
|
};
|
|
|
|
// Player health recharging
|
|
bool CPlayerHealthRecharge::ms_bActivated = true;
|
|
float CPlayerHealthRecharge::ms_fRechargeSpeed = 4.0f;
|
|
float CPlayerHealthRecharge::ms_fRechargeSpeedWhileCrouchedOrCover = 8.0f;
|
|
float CPlayerHealthRecharge::ms_fTimeSinceDamageToStartRecharding = 5.0f;
|
|
float CPlayerHealthRecharge::ms_fTimeSinceDamageToStartRechardingCrouchedOrCover = 5.0f;
|
|
|
|
void CPlayerHealthRecharge::Process(CPed* pPed)
|
|
{
|
|
// Dont process for network clones or if deactivated
|
|
if( pPed->IsNetworkClone() || !CPlayerHealthRecharge::ms_bActivated)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If we're injured we can't recharge
|
|
if( pPed->IsInjured() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if getting taken down via melee, no recharge.
|
|
if( pPed->IsDeadByMelee() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If no health is needed, early out
|
|
const float fMaxHealthToRestore = pPed->GetInjuredHealthThreshold() + ((pPed->GetMaxHealth() - pPed->GetInjuredHealthThreshold()) * m_fMaxHealthToRechargeAsPercentage);
|
|
const float fHealthNeeded = fMaxHealthToRestore - pPed->GetHealth();
|
|
if( fHealthNeeded <= 0.0f )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Dont recharge if underwater
|
|
if( pPed->GetIsInWater() && pPed->GetCurrentMotionTask()->IsUnderWater() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Dont recharge if on fire
|
|
if( g_fireMan.IsEntityOnFire(pPed) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Dont recharge if underwater in a vehicle
|
|
if( pPed->GetPedIntelligence()->GetEventScanner()->GetInWaterEventScanner().IsDrowningInVehicle(*pPed) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Dont recharge if we've been in the water for a long time and are slowly losing health
|
|
if( pPed->GetPedIntelligence()->GetEventScanner()->GetInWaterEventScanner().IsTakingDamageDueToFatigue(*pPed) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Dont recharge if we've disabled health regeneration while we are being electrocuted by WEAPON_STUNGUN_MP
|
|
if( pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_DisableHealthRegenerationWhenStunned) && pPed->GetPedResetFlag(CPED_RESET_FLAG_BeingElectrocuted) && NetworkInterface::IsGameInProgress())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Work out if we are crouched and stationary.
|
|
bool bStill = false;
|
|
bool bCrouched = false;
|
|
if( pPed->GetIsOnFoot() && pPed->GetPrimaryMotionTask() )
|
|
{
|
|
Vector2 vMBR;
|
|
pPed->GetMotionData()->GetCurrentMoveBlendRatio(vMBR);
|
|
bStill = vMBR.Mag2() < rage::square(MBR_WALK_BOUNDARY);
|
|
bCrouched = pPed->GetIsCrouching();
|
|
}
|
|
|
|
// Work out if we are idle in cover
|
|
bool bInCoverIdle = (pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_IN_COVER) &&
|
|
pPed->GetPedIntelligence()->GetQueriableInterface()->GetStateForTaskType(CTaskTypes::TASK_IN_COVER) == CTaskInCover::State_Idle );
|
|
|
|
// Work out if we are idle in cover
|
|
const bool bStandingStillOrInCover = bStill || bInCoverIdle;
|
|
const bool bInVehicleOrOnMount = pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) || pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_OnMount );
|
|
const bool bCrouchedOrInCover = bCrouched || bInCoverIdle;
|
|
|
|
// Cant recharge if moving
|
|
if( !bStandingStillOrInCover && !bInVehicleOrOnMount )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Work out the recharge variables based on our current state
|
|
const float fTimeSinceDamageToRecharge = bCrouchedOrInCover ? CPlayerHealthRecharge::ms_fTimeSinceDamageToStartRechardingCrouchedOrCover : CPlayerHealthRecharge::ms_fTimeSinceDamageToStartRecharding;
|
|
float fRechargeSpeed = bCrouchedOrInCover ? CPlayerHealthRecharge::ms_fRechargeSpeedWhileCrouchedOrCover : CPlayerHealthRecharge::ms_fRechargeSpeed;
|
|
|
|
fRechargeSpeed *= GetRechargeScriptMultiplier();
|
|
|
|
const CPedModelInfo* mi = pPed->GetPedModelInfo();
|
|
Assert(mi);
|
|
const CPedModelInfo::PersonalityData& pd = mi->GetPersonalitySettings();
|
|
|
|
fRechargeSpeed *= pd.GetHealthRegenEfficiency();
|
|
|
|
u32 uLastDamageTime = pPed->GetWeaponDamagedTime();
|
|
if (NetworkInterface::IsInCopsAndCrooks() && m_uLastTimeHealthDamageTaken > 0)
|
|
{
|
|
uLastDamageTime = m_uLastTimeHealthDamageTaken;
|
|
}
|
|
|
|
float fTimeSinceLastDamage = ((float)fwTimer::GetTimeInMilliseconds() - uLastDamageTime)/1000.0f;
|
|
|
|
// Only start recharging a certain amount of time after being damaged
|
|
if( fTimeSinceLastDamage > fTimeSinceDamageToRecharge && !pPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_DISABLE_HEALTH_RECHARGE))
|
|
{
|
|
// Clamp the health increase so we dont recharge over the maximum amount
|
|
float fHealthIncrease = fRechargeSpeed*fwTimer::GetTimeStep();
|
|
fHealthIncrease = Clamp(fHealthIncrease, 0.0f, fHealthNeeded);
|
|
|
|
// Apply the health increase
|
|
pPed->ChangeHealth(fHealthIncrease);
|
|
}
|
|
}
|
|
|
|
void CPlayerHealthRecharge::NotifyHealthDamageTaken()
|
|
{
|
|
m_uLastTimeHealthDamageTaken = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPlayerEnduranceManager::Process(CPed* CNC_MODE_ENABLED_ONLY(pPed))
|
|
{
|
|
#if CNC_MODE_ENABLED
|
|
if(pPed->GetEndurance() > 0.0f)
|
|
{
|
|
ProcessRegen(pPed);
|
|
}
|
|
else if(CPlayerInfo::ms_Tunables.m_EnduranceManagerSettings.m_CanEnduranceIncapacitate && pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_CanBeIncapacitated))
|
|
{
|
|
//Kick off incapacitation event on owner if we're not running it already
|
|
const CTask* incapacitationTask = pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_INCAPACITATED);
|
|
if(!pPed->IsNetworkClone() && incapacitationTask == nullptr)
|
|
{
|
|
if(!pPed->GetPedIntelligence()->HasEventOfType(EVENT_INCAPACITATED))
|
|
{
|
|
const CEventIncapacitated eventIncapacitated;
|
|
pPed->GetPedIntelligence()->AddEvent(eventIncapacitated);
|
|
}
|
|
}
|
|
}
|
|
ProcessEnduranceOverlayFX(pPed);
|
|
#endif //#if CNC_MODE_ENABLED
|
|
}
|
|
|
|
void CPlayerEnduranceManager::NotifyEnduranceDamageTaken()
|
|
{
|
|
m_uLastTimeEnduranceDamageTaken = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPlayerEnduranceManager::NotifyEnduranceRecovered()
|
|
{
|
|
m_uLastTimeEnduranceRecovered = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
bool CPlayerEnduranceManager::ShouldIgnoreEnduranceDamage() const
|
|
{
|
|
#if CNC_MODE_ENABLED
|
|
const int fTimeSinceRecovered = fwTimer::GetTimeInMilliseconds() - m_uLastTimeEnduranceRecovered;
|
|
return fTimeSinceRecovered < CTaskIncapacitated::GetTimeSinceRecoveryToIgnoreEnduranceDamage();
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void CPlayerEnduranceManager::ProcessRegen(CPed* pPed)
|
|
{
|
|
//Early out if at max
|
|
if (pPed->GetEndurance() >= pPed->GetMaxEndurance())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Do not regen if hit recently
|
|
const int timeSinceLastDamage = fwTimer::GetTimeInMilliseconds() - m_uLastTimeEnduranceDamageTaken;
|
|
if (timeSinceLastDamage < CPlayerInfo::GetEnduranceRegenDamageCooldown())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const float fDeltaTime = fwTimer::GetTimeStep();
|
|
const float fMaxRegenTime = CPlayerInfo::GetEnduranceMaxRegenTime() / 1000.0f;
|
|
const float fRegenRate = fMaxRegenTime > SMALL_FLOAT ? pPed->GetMaxEndurance() / fMaxRegenTime : 0.0f;
|
|
|
|
float fRegenMod = 1.0f;
|
|
// check for arcade ability effect
|
|
if ((pPed->GetPlayerInfo() != nullptr) && (pPed->GetPlayerInfo()->GetArcadeAbilityEffects().GetIsActive(AAE_SECOND_WIND)))
|
|
{
|
|
fRegenMod = pPed->GetPlayerInfo()->GetArcadeAbilityEffects().GetModifier(AAE_SECOND_WIND);
|
|
}
|
|
|
|
pPed->SetEndurance(pPed->GetEndurance() + fRegenRate * fRegenMod * fDeltaTime);
|
|
}
|
|
|
|
|
|
void CPlayerEnduranceManager::ProcessEnduranceOverlayFX(CPed* pPed)
|
|
{
|
|
//Early out if the ped cannot be incapacitated (e.g. is a player on the C&C cop team or a simple ped)
|
|
if (!pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_CanBeIncapacitated) || !CPlayerInfo::ms_Tunables.m_EnduranceManagerSettings.m_CanEnduranceIncapacitate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (scriptVerifyf(pPed->GetPlayerInfo(), "Player with no player info!"))
|
|
{
|
|
bool bAreSpecialAbilityFXRunning = false;
|
|
for (int i = 0; i < PSAS_MAX; ++i)
|
|
{
|
|
if (CPlayerSpecialAbility* pSpecialAbility = pPed->GetSpecialAbility((ePlayerSpecialAbilitySlot)i))
|
|
{
|
|
if (pSpecialAbility->IsPlayingFx())
|
|
{
|
|
bAreSpecialAbilityFXRunning = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
float EndurancePercentage = pPed->GetEndurance() / pPed->GetMaxEndurance();
|
|
|
|
// If a special ability FX are running, stop all endurance related FX
|
|
if (bAreSpecialAbilityFXRunning || pPed->GetArrestState() != ArrestState_None || pPed->IsDead())
|
|
{
|
|
if (ANIMPOSTFXMGR.IsRunning(ATSTRINGHASH("CnC_LowEndurance", 0x3A8D7228)))
|
|
{
|
|
ANIMPOSTFXMGR.Stop(ATSTRINGHASH("CnC_LowEndurance", 0x3A8D7228), AnimPostFXManager::kDefault);
|
|
}
|
|
|
|
if (ANIMPOSTFXMGR.IsRunning(ATSTRINGHASH("CnC_LowEnduranceOut", 0xFDEAAEE6)))
|
|
{
|
|
ANIMPOSTFXMGR.Stop(ATSTRINGHASH("CnC_LowEnduranceOut", 0xFDEAAEE6), AnimPostFXManager::kDefault);
|
|
}
|
|
}
|
|
else if (EndurancePercentage >= 0.5)
|
|
{
|
|
if (ANIMPOSTFXMGR.IsRunning(ATSTRINGHASH("CnC_LowEndurance", 0x3A8D7228)) && !ANIMPOSTFXMGR.IsRunning(ATSTRINGHASH("CnC_LowEnduranceOut", 0xFDEAAEE6)))
|
|
{
|
|
// DStop Endurance effect and start it's outro
|
|
ANIMPOSTFXMGR.Stop(ATSTRINGHASH("CnC_LowEndurance", 0x3A8D7228), AnimPostFXManager::kDefault);
|
|
ANIMPOSTFXMGR.Start(ATSTRINGHASH("CnC_LowEnduranceOut", 0xFDEAAEE6), 0U, false, false, false, 0U, AnimPostFXManager::kDefault);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ANIMPOSTFXMGR.IsRunning(ATSTRINGHASH("CnC_LowEndurance", 0x3A8D7228)))
|
|
{
|
|
ANIMPOSTFXMGR.Start(ATSTRINGHASH("CnC_LowEndurance", 0x3A8D7228), 0U, false, false, false, 0U, AnimPostFXManager::kDefault);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPlayerDamageOverTime::Process(CPed* pPed)
|
|
{
|
|
if (!pPed || !pPed->IsLocalPlayer() || m_DOTEffects.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
u32 uCurrentTime = fwTimer::GetTimeInMilliseconds();
|
|
|
|
int iCount = m_DOTEffects.GetCount();
|
|
for(int i = 0; i < iCount; i++)
|
|
{
|
|
const CWeaponInfo* pWeaponInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(m_DOTEffects[i].m_uWeaponHash);
|
|
if (pWeaponInfo)
|
|
{
|
|
bool bApplyDamage = false;
|
|
bool bExpired = false;
|
|
|
|
u32 uTickRate = (u32)(pWeaponInfo->GetTimeBetweenShots() * 1000.0f);
|
|
u32 uTimeSinceLastDamage = uCurrentTime - m_DOTEffects[i].m_uLastDamageTime;
|
|
|
|
if (uCurrentTime >= m_DOTEffects[i].m_uExpiryTime)
|
|
{
|
|
bApplyDamage = true;
|
|
bExpired = true;
|
|
}
|
|
else if (uTimeSinceLastDamage >= uTickRate)
|
|
{
|
|
bApplyDamage = true;
|
|
}
|
|
|
|
if (bApplyDamage)
|
|
{
|
|
float fHealthDamagePerSecond = pWeaponInfo->GetDamage();
|
|
float fEnduranceDamagePerSecond = pWeaponInfo->GetEnduranceDamage();
|
|
float fSecondsSinceLastDamage = (float)uTimeSinceLastDamage / 1000.0f;
|
|
|
|
// Send as two individual events (only way to apply raw damage values on both health and endurance)
|
|
if (fHealthDamagePerSecond > 0.0f)
|
|
{
|
|
float fScaledHealthDamage = fHealthDamagePerSecond * fSecondsSinceLastDamage;
|
|
CWeaponDamage::GeneratePedDamageEvent(m_DOTEffects[i].m_LatestInflicter, pPed, m_DOTEffects[i].m_uWeaponHash, fScaledHealthDamage, VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition()), NULL, CPedDamageCalculator::DF_HealthDamageOnly);
|
|
}
|
|
if (fEnduranceDamagePerSecond > 0.0f)
|
|
{
|
|
float fScaledEnduranceDamage = fEnduranceDamagePerSecond * fSecondsSinceLastDamage;
|
|
CWeaponDamage::GeneratePedDamageEvent(m_DOTEffects[i].m_LatestInflicter, pPed, m_DOTEffects[i].m_uWeaponHash, fScaledEnduranceDamage, VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition()), NULL, CPedDamageCalculator::DF_EnduranceDamageOnly);
|
|
}
|
|
|
|
m_DOTEffects[i].m_uLastDamageTime = uCurrentTime;
|
|
}
|
|
|
|
if (bExpired)
|
|
{
|
|
m_DOTEffects.Delete(i);
|
|
iCount--;
|
|
i--;
|
|
}
|
|
else
|
|
{
|
|
ProcessAdditionalEffects(pPed, pWeaponInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPlayerDamageOverTime::Start(u32 uWeaponHash, CPed* pVictimPed, CEntity* pInflicterEntity)
|
|
{
|
|
// Check params
|
|
if(!pedVerifyf(uWeaponHash != 0, "CPlayerDamageOverTime::StartEffect - No weapon hash set for damage over time, not applying"))
|
|
{
|
|
return;
|
|
}
|
|
const CWeaponInfo* pWeaponInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(uWeaponHash);
|
|
if(pWeaponInfo == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if(!pedVerifyf(pVictimPed != NULL, "CPlayerDamageOverTime::StartEffect - Invalid victim pointer, not applying"))
|
|
{
|
|
return;
|
|
}
|
|
if(!pedVerifyf(pInflicterEntity != NULL, "CPlayerDamageOverTime::StartEffect - Invalid inflictor pointer, not applying"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
CPed* pInflicterPed = NULL;
|
|
if (pInflicterEntity->GetIsTypePed())
|
|
{
|
|
pInflicterPed = static_cast<CPed*>(pInflicterEntity);
|
|
}
|
|
else
|
|
{
|
|
// Ignoring anything that isn't a ped inflicter (for now)
|
|
return;
|
|
}
|
|
|
|
// Validate data
|
|
if(!pedVerifyf(pWeaponInfo->GetDamageTime() > 0.0f, "CPlayerDamageOverTime::StartEffect - DamageTime invalid for %s, not applying", pWeaponInfo->GetName()))
|
|
{
|
|
return;
|
|
}
|
|
if(!pedVerifyf(pWeaponInfo->GetTimeBetweenShots() > 0.0f, "CPlayerDamageOverTime::StartEffect - TimeBetweenShots invalid for %s, not applying", pWeaponInfo->GetName()))
|
|
{
|
|
return;
|
|
}
|
|
if(!pedVerifyf(pWeaponInfo->GetDamage() > 0.0f || pWeaponInfo->GetEnduranceDamage() > 0.0f, "CPlayerDamageOverTime::StartEffect - %s has no damage value set, not applying", pWeaponInfo->GetName()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
CPlayerDamageOverTime::DOTEffect effect;
|
|
effect.m_uWeaponHash = uWeaponHash;
|
|
effect.m_uLastDamageTime = fwTimer::GetTimeInMilliseconds();
|
|
effect.m_uExpiryTime = effect.m_uLastDamageTime + (u32)(pWeaponInfo->GetDamageTime() * 1000.0f);
|
|
effect.m_OriginalInflicter = pInflicterPed;
|
|
effect.m_LatestInflicter = pInflicterPed;
|
|
|
|
bool bAddNewEntry = true;
|
|
|
|
if (pWeaponInfo->GetDamageOverTimeAllowStacking())
|
|
{
|
|
int iExistingCount = 0;
|
|
|
|
for(int i = 0; i < m_DOTEffects.GetCount(); i++)
|
|
{
|
|
if (m_DOTEffects[i].m_uWeaponHash == uWeaponHash)
|
|
{
|
|
// If an entry already exists for weapon/inflicter, extend duration instead of adding new one
|
|
if(m_DOTEffects[i].m_OriginalInflicter == effect.m_OriginalInflicter)
|
|
{
|
|
m_DOTEffects[i].m_uExpiryTime = effect.m_uExpiryTime;
|
|
bAddNewEntry = false;
|
|
}
|
|
|
|
// Set new inflicter as 'latest' on all existing of this weapon type, so we attribute death/incapacitation to last person who applied effect
|
|
m_DOTEffects[i].m_LatestInflicter = effect.m_OriginalInflicter;
|
|
|
|
iExistingCount++;
|
|
}
|
|
}
|
|
|
|
// Enforce a stacking limit for the number of different inflicters
|
|
if (iExistingCount >= MAX_STACK)
|
|
{
|
|
bAddNewEntry = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Find and extend the duration of an existing effect instead of adding new one
|
|
for(int i = 0; i < m_DOTEffects.GetCount(); i++)
|
|
{
|
|
if (m_DOTEffects[i].m_uWeaponHash == uWeaponHash)
|
|
{
|
|
m_DOTEffects[i].m_uExpiryTime = effect.m_uExpiryTime;
|
|
bAddNewEntry = false;
|
|
|
|
// Can early out, there's only ever going to be one
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bAddNewEntry && pedVerifyf(!m_DOTEffects.IsFull(), "CPlayerDamageOverTime::StartEffect - Full! Increase array size MAX_ENTRIES to match potential (currently %i)", m_DOTEffects.GetMaxCount()))
|
|
{
|
|
m_DOTEffects.Push(effect);
|
|
}
|
|
|
|
// Do anything we need to do when an effect is applied/extended
|
|
StartAdditionalEffects(pVictimPed, pWeaponInfo);
|
|
}
|
|
|
|
void CPlayerDamageOverTime::Stop(u32 uWeaponHash)
|
|
{
|
|
// Remove all entries matching this weapon hash
|
|
int iCount = m_DOTEffects.GetCount();
|
|
for(int i = 0; i < iCount; i++)
|
|
{
|
|
if (m_DOTEffects[i].m_uWeaponHash == uWeaponHash)
|
|
{
|
|
m_DOTEffects.Delete(i);
|
|
iCount--;
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPlayerDamageOverTime::StopAll()
|
|
{
|
|
// Remove all entries
|
|
for(int i = 0; i < m_DOTEffects.GetCount(); i++)
|
|
{
|
|
m_DOTEffects.Delete(i);
|
|
}
|
|
}
|
|
|
|
// Note - It's preferable to check active Player Reset Flags instead of using this function!
|
|
bool CPlayerDamageOverTime::IsActive(u32 uWeaponHash)
|
|
{
|
|
for(int i = 0; i < m_DOTEffects.GetCount(); i++)
|
|
{
|
|
if (m_DOTEffects[i].m_uWeaponHash == uWeaponHash)
|
|
{
|
|
return m_DOTEffects[i].m_uExpiryTime < fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#if AI_DEBUG_OUTPUT_ENABLED
|
|
atString CPlayerDamageOverTime::GetDebugString(s32 iEffectIndex)
|
|
{
|
|
if(pedVerifyf(iEffectIndex >= 0 && iEffectIndex < m_DOTEffects.GetCount(), "CPlayerDamageOverTime::GetDebugStringForEffect - Bad index!"))
|
|
{
|
|
static const u32 uBUFFER_SIZE = 256;
|
|
static char strBuffer[uBUFFER_SIZE];
|
|
|
|
const CWeaponInfo* pWeaponInfo = CWeaponInfoManager::GetInfo<CWeaponInfo>(m_DOTEffects[iEffectIndex].m_uWeaponHash);
|
|
if (pWeaponInfo)
|
|
{
|
|
const float fTimeLeft = (float)(m_DOTEffects[iEffectIndex].m_uExpiryTime - fwTimer::GetTimeInMilliseconds()) / 1000.0f;
|
|
const float fTimeSinceLastTick = (float)(fwTimer::GetTimeInMilliseconds() - m_DOTEffects[iEffectIndex].m_uLastDamageTime) / 1000.0f;
|
|
const float fTimeUntilNextTick = pWeaponInfo->GetTimeBetweenShots() - fTimeSinceLastTick;
|
|
const CPed* pInflictor = m_DOTEffects[iEffectIndex].m_OriginalInflicter;
|
|
const char* inflictorName = pInflictor ? (pInflictor->GetNetworkObject() ? pInflictor->GetNetworkObject()->GetLogName() : pInflictor->GetModelName()) : "NULL";
|
|
|
|
snprintf(strBuffer, uBUFFER_SIZE, " %s, %s, TimeLeft:%.2f, NextTick:%.2f, DPS:%.2fH/%.2fE", pWeaponInfo->GetName(), inflictorName, fTimeLeft, fTimeUntilNextTick, pWeaponInfo->GetDamage(), pWeaponInfo->GetEnduranceDamage());
|
|
return atString(strBuffer);
|
|
}
|
|
}
|
|
|
|
return atString("Invalid");
|
|
}
|
|
#endif //AI_DEBUG_OUTPUT_ENABLED
|
|
|
|
void CPlayerDamageOverTime::StartAdditionalEffects(CPed* pPed, const CWeaponInfo* pWeaponInfo)
|
|
{
|
|
if (!pPed || !pWeaponInfo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Do one-shot things when effect is applied / refreshed
|
|
if (pWeaponInfo->GetDamageOverTimeShockedEffect())
|
|
{
|
|
//TODO
|
|
}
|
|
|
|
if (pWeaponInfo->GetDamageOverTimeChokingEffect())
|
|
{
|
|
//TODO
|
|
}
|
|
}
|
|
|
|
void CPlayerDamageOverTime::ProcessAdditionalEffects(CPed* pPed, const CWeaponInfo* pWeaponInfo)
|
|
{
|
|
if (!pPed || !pWeaponInfo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Do things every frame while effect is active
|
|
if (pWeaponInfo->GetDamageOverTimeShockedEffect())
|
|
{
|
|
pPed->GetPlayerInfo()->GetPlayerResetFlags().SetFlag(CPlayerResetFlags::PRF_DOT_SHOCKED);
|
|
}
|
|
|
|
if (pWeaponInfo->GetDamageOverTimeChokingEffect())
|
|
{
|
|
pPed->GetPlayerInfo()->GetPlayerResetFlags().SetFlag(CPlayerResetFlags::PRF_DOT_SHOCKED);
|
|
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if __BANK
|
|
void CPlayerInfo::sPlayerStatInfo::PreAddWidgets(bkBank& bank)
|
|
{
|
|
bank.PushGroup(m_Name.GetCStr(), false);
|
|
}
|
|
|
|
void CPlayerInfo::sPlayerStatInfo::PostAddWidgets(bkBank& bank)
|
|
{
|
|
bank.PopGroup();
|
|
}
|
|
#endif // __BANK
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const CPlayerInfo::sPlayerStatInfo& CPlayerInfo::GetPlayerStatInfoForPed(const CPed& ped)
|
|
{
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
aiDebugf2("MP SLOT : %i", StatsInterface::GetCurrentMultiplayerCharaterSlot());
|
|
|
|
//Team is stored in the first 8 bits of this stat.
|
|
s32 iTeam = 0;
|
|
if (aiVerify(StatsInterface::GetMaskedInt(STAT_PACKED_STAT_INT0.GetStatId(), iTeam, 0, 8, -1)))
|
|
{
|
|
switch (iTeam)
|
|
{
|
|
case MP0_CHAR_TEAM: return ms_Tunables.m_PlayerStatInfos[PT_MP0_CHAR_TEAM];
|
|
case MP1_CHAR_TEAM: return ms_Tunables.m_PlayerStatInfos[PT_MP1_CHAR_TEAM];
|
|
case MP2_CHAR_TEAM: return ms_Tunables.m_PlayerStatInfos[PT_MP2_CHAR_TEAM];
|
|
case MP3_CHAR_TEAM: return ms_Tunables.m_PlayerStatInfos[PT_MP3_CHAR_TEAM];
|
|
case MP4_CHAR_TEAM: return ms_Tunables.m_PlayerStatInfos[PT_MP4_CHAR_TEAM];
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
return ms_Tunables.m_PlayerStatInfos[PT_MP0_CHAR_TEAM];
|
|
}
|
|
else
|
|
{
|
|
switch (ped.GetPedType())
|
|
{
|
|
case PEDTYPE_PLAYER_0: return ms_Tunables.m_PlayerStatInfos[PT_SP0];
|
|
case PEDTYPE_PLAYER_1: return ms_Tunables.m_PlayerStatInfos[PT_SP1];
|
|
case PEDTYPE_PLAYER_2: return ms_Tunables.m_PlayerStatInfos[PT_SP2];
|
|
default: break;
|
|
}
|
|
|
|
return ms_Tunables.m_PlayerStatInfos[PT_SP0];
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CPlayerInfo::GetStatValuesForSprintType(const CPed& ped, float& fSprintStatValue, float& fMinSprintDuration, float& fMaxSprintDuration, eSprintType nSprintType)
|
|
{
|
|
switch (nSprintType)
|
|
{
|
|
// Intentional fall-thru
|
|
case SPRINT_ON_FOOT:
|
|
case SPRINT_ON_BICYCLE:
|
|
case SPRINT_ON_WATER:
|
|
case SPRINT_UNDER_WATER:
|
|
{
|
|
fSprintStatValue = rage::Clamp(static_cast<float>(StatsInterface::GetIntStat(STAT_STAMINA.GetStatId())) / 100.0f, 0.0f, 1.0f);
|
|
fMinSprintDuration = GetPlayerStatInfoForPed(ped).m_MinStaminaDuration;
|
|
fMaxSprintDuration = GetPlayerStatInfoForPed(ped).m_MaxStaminaDuration;
|
|
return true;
|
|
}
|
|
default: break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const float CPlayerInfo::GetRunSprintSpeedMultiplier() const
|
|
{
|
|
const CPlayerSpecialAbility* pAbility = m_pPlayerPed ? m_pPlayerPed->GetSpecialAbility() : NULL;
|
|
|
|
if (pAbility != NULL)
|
|
{
|
|
// We only want to apply this when we're actually sprinting
|
|
if (pAbility->IsActive() && m_pPlayerPed->GetMotionData()->GetIsSprinting())
|
|
{
|
|
float fAbilityMultiplier = pAbility->GetSprintMultiplier();
|
|
if (fAbilityMultiplier > 0.0f)
|
|
{
|
|
return fAbilityMultiplier;
|
|
}
|
|
}
|
|
}
|
|
|
|
return m_fRunSprintSpeedMultiplier;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if !__FINAL
|
|
PARAM(enableCNCResponsivenessChanges, "[CNC] Enable CNC responsiveness changes for player.");
|
|
PARAM(forceEnableCNCResponsivenessChanges, "[CNC] Force enable CNC responsiveness changes for player, even if not in CNC mode.");
|
|
#endif // !__FINAL
|
|
|
|
bool CPlayerInfo::AreCNCResponsivenessChangesEnabled(const CPed* CNC_MODE_ENABLED_ONLY(pPed))
|
|
{
|
|
#if CNC_MODE_ENABLED
|
|
|
|
if (!pPed || !pPed->IsPlayer())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if !__FINAL
|
|
if (PARAM_forceEnableCNCResponsivenessChanges.Get())
|
|
{
|
|
return true;
|
|
}
|
|
#endif // !__FINAL
|
|
|
|
if (NetworkInterface::IsInCopsAndCrooks())
|
|
{
|
|
TUNE_GROUP_BOOL(CNC_RESPONSIVENESS, bEnableCNCResponsivenessChanges, true);
|
|
if (bEnableCNCResponsivenessChanges)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#if !__FINAL
|
|
if (PARAM_enableCNCResponsivenessChanges.Get())
|
|
{
|
|
return true;
|
|
}
|
|
#endif // !__FINAL
|
|
}
|
|
|
|
#endif // CNC_MODE_ENABLED
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
s32 CPlayerInfo::GetPlayerIndex(const CPed& ped)
|
|
{
|
|
switch (ped.GetPedType())
|
|
{
|
|
case PEDTYPE_PLAYER_0 : return 0;
|
|
case PEDTYPE_PLAYER_1 : return 1;
|
|
case PEDTYPE_PLAYER_2 : return 2;
|
|
default: break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
s32 CPlayerInfo::ms_EnduranceMaxRegenTime = -1;
|
|
s32 CPlayerInfo::ms_EnduranceRegenDamageCooldown = -1;
|
|
float CPlayerInfo::ms_fStaminaDepletionRateModifierCNC = 1.25f;
|
|
float CPlayerInfo::ms_fStaminaReplenishRateModifierCNC = 0.75f;
|
|
|
|
void CPlayerInfo::InitTunables()
|
|
{
|
|
ms_EnduranceMaxRegenTime = ::Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("CNC_ENDURANCE_MAX_REGEN_TIME", 0xBDA9083B), -1);
|
|
ms_EnduranceRegenDamageCooldown = ::Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("CNC_ENDURANCE_REGEN_DAMAGE_COOLDOWN", 0xEC265896), -1);
|
|
ms_fStaminaDepletionRateModifierCNC = ::Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("CNC_STAMINA_DEPLETION_RATE_MODIFIER", 0xa7ba62ee), ms_fStaminaDepletionRateModifierCNC);
|
|
ms_fStaminaReplenishRateModifierCNC = ::Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("CNC_STAMINA_REPLENISH_RATE_MODIFIER", 0x3cec399e), ms_fStaminaReplenishRateModifierCNC);
|
|
}
|
|
|
|
u32 CPlayerInfo::GetEnduranceMaxRegenTime()
|
|
{
|
|
return ms_EnduranceMaxRegenTime != -1 ? (u32)ms_EnduranceMaxRegenTime : ms_Tunables.m_EnduranceManagerSettings.m_MaxRegenTime;
|
|
}
|
|
|
|
u32 CPlayerInfo::GetEnduranceRegenDamageCooldown()
|
|
{
|
|
return ms_EnduranceRegenDamageCooldown != -1 ? (u32)ms_EnduranceRegenDamageCooldown : ms_Tunables.m_EnduranceManagerSettings.m_RegenDamageCooldown;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPlayerInfo::GetMaxTimeUnderWaterForPed(const CPed& ped)
|
|
{
|
|
float fLungStatValue = 0.0f;
|
|
|
|
TUNE_GROUP_BOOL(SPRINT_DEBUG, USE_SIMULATED_LUNG_STAT, false);
|
|
if (USE_SIMULATED_LUNG_STAT)
|
|
{
|
|
TUNE_GROUP_FLOAT(SPRINT_DEBUG, LUNG_STAT_SIMULATED, 0.0f, 0.0f, 1.0f, 0.01f);
|
|
fLungStatValue = LUNG_STAT_SIMULATED;
|
|
}
|
|
else if (ped.GetPedModelInfo())
|
|
{
|
|
fLungStatValue = rage::Clamp(static_cast<float>(StatsInterface::GetIntStat(STAT_LUNG_CAPACITY.GetStatId())) / 100.0f, 0.0f, 1.0f);
|
|
}
|
|
return ComputeDurationFromStat(fLungStatValue, GetPlayerStatInfoForPed(ped).m_MinHoldBreathDuration, GetPlayerStatInfoForPed(ped).m_MaxHoldBreathDuration);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPlayerInfo::GetFallHeightForPed(const CPed& ped)
|
|
{
|
|
// Script overridden fall height
|
|
CPlayerInfo* pPlayerInfo = ped.GetPlayerInfo();
|
|
if (pPlayerInfo && pPlayerInfo->m_fFallHeightOverride >= 0.0f)
|
|
{
|
|
return pPlayerInfo->m_fFallHeightOverride;
|
|
}
|
|
|
|
float fStrengthValue = 0.0f;
|
|
|
|
if (ped.GetPedModelInfo())
|
|
{
|
|
fStrengthValue = rage::Clamp(static_cast<float>(StatsInterface::GetIntStat(STAT_STRENGTH.GetStatId())) / 100.0f, 0.0f, 1.0f);
|
|
}
|
|
|
|
float fMin = GetPlayerStatInfoForPed(ped).m_MinFallHeight;
|
|
float fMax = GetPlayerStatInfoForPed(ped).m_MaxFallHeight;
|
|
return fMin + ((fMax - fMin)*fStrengthValue);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPlayerInfo::GetDiveHeightForPed(const CPed& ped)
|
|
{
|
|
float fStrengthValue = 0.0f;
|
|
|
|
if (ped.GetPedModelInfo())
|
|
{
|
|
fStrengthValue = rage::Clamp(static_cast<float>(StatsInterface::GetIntStat(STAT_LUNG_CAPACITY.GetStatId())) / 100.0f, 0.0f, 1.0f);
|
|
}
|
|
|
|
//! Infinite if maxed out stat.
|
|
if(fStrengthValue >= 1.0f)
|
|
{
|
|
return FLT_MAX;
|
|
}
|
|
|
|
//! ramp stat
|
|
fStrengthValue = powf(fStrengthValue, GetPlayerStatInfoForPed(ped).m_DiveRampPow);
|
|
|
|
float fMin = GetPlayerStatInfoForPed(ped).m_MinDiveHeight;
|
|
float fMax = GetPlayerStatInfoForPed(ped).m_MaxDiveHeight;
|
|
return fMin + ((fMax - fMin)*fStrengthValue);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPlayerInfo::ComputeDurationFromStat(float fCurrentStatPercentage, float fMinDuration, float fMaxDuration)
|
|
{
|
|
TUNE_GROUP_BOOL(SPRINT_DEBUG, USE_STAMINA_STAT_SIMULATED, false);
|
|
if (USE_STAMINA_STAT_SIMULATED)
|
|
{
|
|
TUNE_GROUP_FLOAT(SPRINT_DEBUG, STAMINA_STAT_SIMULATED, 0.0f, 0.0f, 1.0f, 0.01f);
|
|
fCurrentStatPercentage = STAMINA_STAT_SIMULATED;
|
|
}
|
|
aiAssertf(fCurrentStatPercentage >= 0.0f && fCurrentStatPercentage <= 1.0f, "Expected fCurrentStatPercentage to be between 0 and 1");
|
|
return (1.0f - fCurrentStatPercentage) * fMinDuration + fCurrentStatPercentage * fMaxDuration;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CPlayerInfo::CPlayerInfo(CPed* ped, const rlGamerInfo* gamerInfo) :
|
|
m_pPlayerPed(ped),
|
|
m_pOnlyEnterThisVehicle(NULL),
|
|
m_pPreferRearSeatsVehicle(NULL),
|
|
m_pPreferFrontPassengerSeatVehicle(NULL),
|
|
m_pSpotterOfStolenVehicle(NULL),
|
|
Team(-1),
|
|
m_PlayerState(PLAYERSTATE_PLAYING),
|
|
CarDensityForCurrentZone(0),
|
|
//bAfterRemoteVehicleExplosion(false),
|
|
//bCreateRemoteVehicleExplosion(false),
|
|
//bFadeAfterRemoteVehicleExplosion(false),
|
|
m_fForceAirDragMult(0.0f),
|
|
m_fSwimSpeedMultiplier(1.f),
|
|
m_fRunSprintSpeedMultiplier(1.f),
|
|
CollectablesPickedUp(0),
|
|
TotalNumCollectables(3),
|
|
PortablePickupPending(false),
|
|
m_LastTimeHeliWasDestroyed(0),
|
|
LastTimeEnergyLost(0),
|
|
LastTimeArmourLost(0),
|
|
//TimesUpsideDownInARow(0),
|
|
//TimesStuckInARow(0),
|
|
bDoesNotGetTired(false),
|
|
bFastReload(false),
|
|
bFireProof(false),
|
|
MaxHealth(PLAYER_DEFAULT_MAX_HEALTH),
|
|
MaxArmour(PLAYER_DEFAULT_MAX_ARMOUR),
|
|
JackSpeed(NETWORK_PLAYER_DEFAULT_JACK_SPEED),
|
|
DetonateDownTime(0),
|
|
bExplodedTriggeredProjectilesWithThisButtonPress(false),
|
|
m_bIsShelteredAbove(false),
|
|
m_iTimeTillNextShelterLineTest(0),
|
|
m_iNumEnemiesInCombat(0),
|
|
m_iNumEnemiesShootingInCombat(0),
|
|
#if LIGHT_EFFECTS_SUPPORT
|
|
m_PlayerLightEffect(),
|
|
m_WantedLightEffect(),
|
|
#if LIGHT_EFFECT_FLASH_FOR_LOW_HEALTH
|
|
m_FlashingPlayerLightEffect(),
|
|
#endif // LIGHT_EFFECT_FLASH_FOR_LOW_HEALTH
|
|
#endif // LIGHT_EFFECT_SUPPORT
|
|
bCanDoDriveBy(true),
|
|
bCanBeHassledByGangs(false),
|
|
bCanUseCover(true),
|
|
bEnableControlOnDeath(true),
|
|
bHasDamagedAtLeastOnePed(false),
|
|
bHasDamagedAtLeastOneNonAnimalPed(false),
|
|
bHasDamagedAtLeastOneVehicle(false),
|
|
bSpecifyInitialCoverDirection(false),
|
|
bInitialCoverDirectionFaceLeft(false),
|
|
HavocCaused(0),
|
|
m_fLastPlayerNearbyTimer(0.0f),
|
|
StealthRate(1.f),
|
|
m_nLastBustMessageNumber(1),
|
|
m_bCoverGeneratedByDynamicEntity(false),
|
|
m_bCanMoveLeft(false),
|
|
m_bCanMoveRight(false),
|
|
m_bInsideCorner(false),
|
|
m_bIsRoundCover(false),
|
|
m_fInsideCornerDistance(-1.0f),
|
|
m_vRoundCoverCenter(Vector3::ZeroType),
|
|
m_fRoundCoverRadius(0),
|
|
m_fCoverEdgeDistance(-1.0f),
|
|
m_vDynamicCoverPointLastKnownPosition(Vector3::ZeroType),
|
|
m_fTimeElapsedUsingCoverSeconds(0.0f),
|
|
m_fTimeElapsedHidingInCoverSeconds(0.0f),
|
|
m_CandidateRegistrationEndTimeMS(0),
|
|
m_BestCandidateAcceptanceEndTimeMS(0),
|
|
m_BestEnemyChargerCandidatePed(NULL),
|
|
m_BestEnemySmokeThrowerCandidatePed(NULL),
|
|
m_vChargeGoalPlayerPosition(V_ZERO),
|
|
m_vCombatLoiteringPosition(V_ZERO),
|
|
m_fTimeElapsedLoiteringSeconds(-1.0f),
|
|
m_uNextLoiterDistCheckTimeMS(0),
|
|
m_aPlayerLOSInfo(MAX_NUM_PHYSICAL_PLAYERS),
|
|
m_nearVehiclePut(0),
|
|
m_bCanLeaveParachuteSmokeTrail(false),
|
|
m_bIsInsideMovingTrain(false),
|
|
m_bAllowControlInsideMovingTrain(true),
|
|
m_pTrainPedIsInside(NULL),
|
|
m_StealthNoise(0.0f),
|
|
m_fNormalStealthMultiplier(1.0f),
|
|
m_fSneakingStealthMultiplier(1.0f),
|
|
m_fDampenRootWeight(0.0f),
|
|
m_fDampenRootTargetWeight(0.0f),
|
|
m_fDampenRootTargetHeight(0.0f),
|
|
m_uLastTimePedMadeMobileCommentOnWeirdPlayer(0),
|
|
m_uLastTimeDamagedLocalPlayer(0),
|
|
m_bSimulateGaitInput(false),
|
|
m_fSimulateGaitMoveBlendRatio(0.0f),
|
|
m_fSimulateGaitHeading(0.0f),
|
|
m_fSimulateGaitDuration(-1.0f),
|
|
m_fSimulateGaitTimerCount(0.0f),
|
|
m_fSimulateGaitStartHeading(0.0f),
|
|
m_bSimulateGaitNoInputInterruption(false),
|
|
m_fStealthPerceptionModifier(1.0f),
|
|
m_uTimeExitVehicleTaskFinished(0),
|
|
m_fCachedSprintMultThisFrame(-1.0f),
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
m_uTimeBikeSprintPressed(0),
|
|
#endif
|
|
m_bAutoGiveParachuteWhenEnterPlane(false),
|
|
m_bAutoGiveScubaGearWhenExitVehicle(false),
|
|
m_uNumAiAttemptingArrestOnPlayer(0),
|
|
m_LastTimeBustInVehicleFailed(0),
|
|
m_LastTimeArrestAttempted(0),
|
|
m_LastTimeWarpedIntoVehicle(0),
|
|
m_LastTimeWarpedOutOfVehicle(0),
|
|
m_LastShoutTargetPositionTime(0),
|
|
m_fLaggedYawControl(0.0f),
|
|
m_fLaggedPitchControl(0.0f),
|
|
m_fLaggedRollControl(0.0f),
|
|
m_fLaggedSteerControl(0.0f),
|
|
m_fTimeBetweenRandomControlInputs(0.0f),
|
|
m_fRandomControlYaw(0.0f),
|
|
m_fRandomControlPitch(0.0f),
|
|
m_fRandomControlRoll(0.0f),
|
|
m_fRandomControlThrottle(0.0f),
|
|
m_fRandomControlSteerModifier(0.0f),
|
|
m_fControlSettledTime(0.0f),
|
|
m_fPreviousSteerValue(0.0f),
|
|
m_fTimeSinceApplyingRandomControl(0.0f),
|
|
m_fTimeSinceLastDrivingAtSpeed(0.0f),
|
|
m_fSwimBoundsEscapeTimer(ms_Tunables.m_MaxTimeToTrespassWhileSwimmingBeforeDeath),
|
|
m_uSprintAimBreakOutTime(0),
|
|
m_bHasWorldExtentsSpawnedAShark(false),
|
|
m_maxPortablePickupsCarried(-1),
|
|
m_numPortablePickupsCarried(0),
|
|
m_sFireProofTimer(0),
|
|
m_uCollidedWithPedTimer(0),
|
|
m_uLastTimeBumpedPedInVehicle(0),
|
|
m_bDisableAgitation(true),
|
|
m_uTimeToEnableAgitation(0),
|
|
m_uLastTimeStopAiming(0),
|
|
m_bWasAiming(false),
|
|
m_bWasLightAttackPressed(false),
|
|
m_bWasAlternateAttackPressed(false),
|
|
#if FPS_MODE_SUPPORTED
|
|
m_fRNGTimerFPS(0.0f),
|
|
m_bFiredInFPSFullAimState(false),
|
|
m_bFiredGunInFPS(false),
|
|
m_bFiredDuringFPSSprint(false),
|
|
m_bWasRunAndGunning(false),
|
|
m_bInFPSScopeState(false),
|
|
#endif
|
|
m_nMeleeEquippedWeaponHash(0),
|
|
m_nMeleeClipSetReleaseTimer(0),
|
|
m_bEnableMeleeClipSetReleaseTimer(false),
|
|
m_bEnableBlueToothHeadset(false),
|
|
m_TimeOfPlayerLastRespawn(0),
|
|
m_PreviousVariationComponent(PV_COMP_INVALID),
|
|
m_PreviousVariationDrawableId(0),
|
|
m_PreviousVariationDrawableAltId(0),
|
|
m_PreviousVariationTexId(0),
|
|
m_PreviousVariationPaletteId(0),
|
|
m_bPlayerLeavePedBehind(true),
|
|
m_ParachuteVariationComponent(PV_COMP_INVALID),
|
|
m_ParachuteVariationDrawable(0),
|
|
m_ParachuteVariationAltDrawable(0),
|
|
m_ParachuteVariationTexId(0),
|
|
m_ParachuteModelHash(0),
|
|
m_ReserveParachuteModelHash(0),
|
|
m_ParachutePackModelHash(0),
|
|
m_bRetainParachuteAfterTeleport(false),
|
|
m_SwitchedToOrFromFirstPersonCount(0),
|
|
m_SwitchedToFirstPersonCount(0),
|
|
m_SwitchedToFirstPersonFromThirdPersonCoverCount(0),
|
|
m_fFallHeightOverride(-1.0f),
|
|
#if FPS_MODE_SUPPORTED
|
|
m_fExitCombatRollToScopeTimerInFPS(0.0f),
|
|
m_fCoverHeadingCorrectionAimHeading(FLT_MAX),
|
|
m_bForceFPSRNGState(false),
|
|
#endif
|
|
#if ENABLE_HORSE
|
|
m_InputHorse(ped),
|
|
#endif
|
|
m_vFollowTargetOffset(V_ZERO),
|
|
m_fFollowTargetRadius(1.f),
|
|
m_fMaxFollowDistance(-1.f),
|
|
m_bMayFollowTargetGroupMembers(false),
|
|
m_pFollowTarget(NULL),
|
|
m_FriendStatus(0),
|
|
m_nearMissedDriverQueueIndex(0),
|
|
m_bVehMeleePerformingRightHit(true)
|
|
#if FPS_MODE_SUPPORTED
|
|
, m_vLeftHandPositionFPS(V_ZERO)
|
|
#endif // FPS_MODE_SUPPORTED
|
|
{
|
|
if (gamerInfo)
|
|
m_GamerInfo = *gamerInfo;
|
|
|
|
EnableAllControls();
|
|
|
|
for (int i = 0; i < PSAS_MAX; ++i)
|
|
{
|
|
m_eSpecialAbilitiesMP[i] = SAT_NONE;
|
|
}
|
|
|
|
// these used to be called in CPlayerPed constructor:
|
|
m_Wanted.SetPed (ped);
|
|
m_Wanted.Initialise();
|
|
|
|
m_PlayerGroup = -1;
|
|
m_bDisplayMobilePhone = false;
|
|
m_bStopNearbyVehicles = true;
|
|
|
|
m_bUseHighFiringAttackBoundary = false;
|
|
|
|
// all below moved from (defunct) CPlayerPed
|
|
m_fHitByWaterCannonTimer = 0.0f;
|
|
m_fHitByWaterCannonImmuneTimer = 0.0f;
|
|
|
|
m_fTimeBeenSprinting = 0.0f;
|
|
m_fSprintStaminaUpdatePeriod = 0.0f;
|
|
m_fSprintStaminaDurationMultiplier = 1.0f;
|
|
m_fSprintApplyDamageTimer = 0;
|
|
|
|
m_fTimeToNextCreateInteriorShockingEvents = ms_fInteriorShockingEventFreq;
|
|
|
|
MultiplayerReset();
|
|
|
|
#if !__FINAL
|
|
if(PARAM_invincible.Get())
|
|
ms_bDebugPlayerInvincible = true;
|
|
#endif
|
|
|
|
for (s32 i = 0; i < MAX_NEAR_VEHICLES; ++i)
|
|
{
|
|
m_nearVehicles[i].veh = NULL;
|
|
}
|
|
|
|
for( s32 i = 0; i < CHARGER_HISTORY_SIZE; ++i )
|
|
{
|
|
m_ChargedByEnemyHistoryMS[i] = 0;
|
|
}
|
|
|
|
for( s32 i = 0; i < SMOKE_THROWER_HISTORY_SIZE; ++i )
|
|
{
|
|
m_SmokeThrownByEnemyHistoryMS[i] = 0;
|
|
}
|
|
|
|
if (!ms_bPlayerBoundaryInitialized)
|
|
{
|
|
// Initialize the player restriction range - but only once.
|
|
ms_WorldLimitsPlayerAABB = spdAABB(Vec3V(ms_Tunables.m_MinWorldLimitsPlayerX, ms_Tunables.m_MinWorldLimitsPlayerY, WORLDLIMITS_ZMIN),
|
|
Vec3V(ms_Tunables.m_MaxWorldLimitsPlayerX, ms_Tunables.m_MaxWorldLimitsPlayerY, WORLDLIMITS_ZMAX));
|
|
ms_bPlayerBoundaryInitialized = true;
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::MultiplayerReset()
|
|
{
|
|
SetInitialState();
|
|
|
|
m_PlayerState = PLAYERSTATE_PLAYING;
|
|
|
|
PortablePickupPending = false;
|
|
|
|
for (u32 i=0; i<MAX_PORTABLE_PICKUP_INFOS; i++)
|
|
{
|
|
PortablePickupsInfo[i].modelIndex = -1;
|
|
PortablePickupsInfo[i].maxPermitted = 0;
|
|
PortablePickupsInfo[i].numCarried = 0;
|
|
}
|
|
|
|
m_maxPortablePickupsCarried = -1;
|
|
m_numPortablePickupsCarried = 0;
|
|
|
|
m_bPlayerLeavePedBehind = true;
|
|
}
|
|
|
|
void CPlayerInfo::SetInitialState()
|
|
{
|
|
m_nLastChangeWeaponFrame = 0;
|
|
m_fWeaponDamageModifier = 1.f;
|
|
m_fWeaponDefenseModifier = 1.f;
|
|
m_fWeaponMinigunDefenseModifier = 1.f;
|
|
m_fMeleeWeaponDamageModifier = 1.f;
|
|
m_fMeleeUnarmedDamageModifier = 1.f;
|
|
m_fMeleeWeaponDefenseModifier = 1.f;
|
|
m_fMeleeWeaponForceModifier = 1.f;
|
|
m_fVehicleDamageModifier = 1.f;
|
|
m_fVehicleDefenseModifier = 1.f;
|
|
m_fMaxExplosiveDamage = -1.0f;
|
|
m_fExplosiveDamageModifier = 1.0f;
|
|
m_fWeaponTakedownDefenseModifier = 1.f;
|
|
|
|
m_bFreeAiming = false;
|
|
|
|
m_bCanBeDamaged = true;
|
|
m_DamageAllowedFromSetPlayerControl = false;
|
|
|
|
m_bPlayerSprintDisabled = false;
|
|
m_bPlayerOnHighway = false;
|
|
m_bPlayerOnWideRoad = false;
|
|
m_bNodesNearbyInZ = true;
|
|
m_bCanUseSearchLight = false;
|
|
|
|
SetPlayerDataMaxSprintEnergy(CStatsMgr::GetFatAndMuscleModifier(STAT_MODIFIER_SPRINT_ENERGY));
|
|
m_fSprintEnergy = GetPlayerDataMaxSprintEnergy();
|
|
|
|
m_fSprintControlCounter = 0.0f;
|
|
m_bSprintReplenishing = false;
|
|
m_bAllowedToPickUpNonMissionObjects = true;
|
|
m_bCanCollectMoneyPickups = true;
|
|
m_bHasControlOfRagdoll = false;
|
|
m_bAmbientMeleeMoveDisabled = false;
|
|
|
|
m_iNumEnemiesInCombat = 0;
|
|
m_iNumEnemiesShootingInCombat = 0;
|
|
|
|
m_Wanted.Initialise();
|
|
|
|
m_LastTimePlayerHitCar = 0;
|
|
m_LastTimePlayerHitPed = 0;
|
|
m_LastTimePlayerHitBuilding = 0;
|
|
m_LastTimePlayerHitObject = 0;
|
|
m_LastTimePlayerDroveOnPavement = 0;
|
|
m_LastTimePlayerRanLight = 0;
|
|
m_LastTimePlayerDroveAgainstTraffic = 0;
|
|
|
|
m_uLastTimeDamagedLocalPlayer = 0;
|
|
|
|
m_LastWeaponHashPickedUp = 0;
|
|
m_TimeLastWeaponPickedUp = 0;
|
|
|
|
m_StealthNoise = 0.0f;
|
|
|
|
m_fPutPedDirectlyIntoCoverNetworkBlendInDuration = INSTANT_BLEND_DURATION;
|
|
|
|
m_fLastPlayerNearbyTimer = 0.0f;
|
|
|
|
ms_DynamicCoverHelper.Reset();
|
|
|
|
m_LastTimeBustInVehicleFailed = 0;
|
|
m_LastTimeArrestAttempted = 0;
|
|
m_LastTimeWarpedIntoVehicle = 0;
|
|
m_LastTimeWarpedOutOfVehicle = 0;
|
|
m_uNumAiAttemptingArrestOnPlayer = 0;
|
|
|
|
m_LastShoutTargetPositionTime = 0;
|
|
|
|
// Charging enemies
|
|
m_CandidateRegistrationEndTimeMS = 0;
|
|
m_BestCandidateAcceptanceEndTimeMS = 0;
|
|
m_BestEnemyChargerCandidatePed = NULL;
|
|
for(int i=0; i < CHARGER_HISTORY_SIZE; ++i)
|
|
{
|
|
m_ChargedByEnemyHistoryMS[i] = 0;
|
|
}
|
|
ResetChargingEnemyGoalPositions();
|
|
|
|
// Smoke thrower enemies
|
|
m_BestEnemySmokeThrowerCandidatePed = NULL;
|
|
for(int i=0; i < SMOKE_THROWER_HISTORY_SIZE; ++i)
|
|
{
|
|
m_SmokeThrownByEnemyHistoryMS[i] = 0;
|
|
}
|
|
|
|
m_PreviousVariationComponent = PV_COMP_INVALID;
|
|
m_PreviousVariationDrawableId = 0;
|
|
m_PreviousVariationDrawableAltId = 0;
|
|
m_PreviousVariationTexId = 0;
|
|
m_PreviousVariationPaletteId = 0;
|
|
|
|
m_bPlayerLeavePedBehind = true;
|
|
|
|
m_vScriptedWeaponFiringPos.ZeroComponents();
|
|
m_bHasScriptedWeaponFirePos = false;
|
|
|
|
m_bAllowStuntJumpCamera = false;
|
|
}
|
|
|
|
|
|
CPlayerInfo::~CPlayerInfo()
|
|
{
|
|
CPed * pPed = GetPlayerPed();
|
|
|
|
DEV_BREAK_IF_FOCUS( CDebugScene::ShouldDebugBreakOnDestroyOfFocusEntity(), pPed );
|
|
DEV_BREAK_ON_PROXIMITY( CDebugScene::ShouldDebugBreakOnProximityOfDestroyCallingEntity(), VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition()) );
|
|
|
|
if (pPed)
|
|
{
|
|
#if __DEV
|
|
Assertf(CPedFactory::IsCurrentlyInDestroyPlayerPedFunction(), "Deleting a player ped from outside of the factory destroy method, this will cause problems for networked player peds");
|
|
#endif
|
|
// moved from CGameWorld::DeletePlayer(): If this player was driving a car we have to tidy up
|
|
CVehicle * pMyVehicle = pPed->GetMyVehicle();
|
|
if (pMyVehicle && pMyVehicle->GetDriver() == pPed)
|
|
{
|
|
if (pMyVehicle->GetStatus() != STATUS_WRECKED)
|
|
{
|
|
pMyVehicle->SetStatus(STATUS_PHYSICS);
|
|
}
|
|
|
|
if (!pMyVehicle->IsNetworkClone())
|
|
{
|
|
pMyVehicle->SetThrottle(0.0f);
|
|
pMyVehicle->SetBrake(HIGHEST_BRAKING_WITHOUT_LIGHT);
|
|
}
|
|
}
|
|
|
|
GetWanted().SetPed (NULL);
|
|
|
|
if (GetPlayerDataPlayerGroup() >= 0)
|
|
{
|
|
CPedGroups::ms_groups[GetPlayerDataPlayerGroup()].Flush(false, false);
|
|
}
|
|
}
|
|
|
|
#if LIGHT_EFFECTS_SUPPORT
|
|
// Even if control is disabled, still want to update light-effect color.
|
|
CControl& control = CControlMgr::GetPlayerMappingControl();
|
|
control.ClearLightEffect(&m_WantedLightEffect, CControl::CODE_LIGHT_EFFECT);
|
|
control.ClearLightEffect(&m_PlayerLightEffect, CControl::PLAYER_LIGHT_EFFECT);
|
|
|
|
#if LIGHT_EFFECT_FLASH_FOR_LOW_HEALTH
|
|
control.ClearLightEffect(&m_FlashingPlayerLightEffect, CControl::PLAYER_LIGHT_EFFECT);
|
|
#endif // LIGHT_EFFECT_FLASH_FOR_LOW_HEALTH
|
|
|
|
#endif // LIGHT_EFFECT_SUPPORT
|
|
|
|
#if __BANK
|
|
// ensure no other peds are using this player info
|
|
CPed::Pool *pedPool = CPed::GetPool();
|
|
|
|
for(int index = 0; index < pedPool->GetSize(); index++)
|
|
{
|
|
CPed *pPedTemp = pedPool->GetSlot(index);
|
|
|
|
if(pPedTemp && pPedTemp->GetPlayerInfo() == this)
|
|
{
|
|
Assertf(0, "CPlayerInfo::~CPlayerInfo() - Deleting a player info pointer (%p) when it is still referenced by %s", this, pPedTemp->GetDebugName());
|
|
}
|
|
}
|
|
#endif // BANK
|
|
}
|
|
|
|
void CPlayerInfo::SetPlayerPed(CPed* pPlayerPed, bool bClearPedDamage)
|
|
{
|
|
Assert(m_pPlayerPed==NULL || m_pPlayerPed->GetPlayerInfo()==NULL); // make sure the last user of this CPlayerInfo is no longer pointing to it
|
|
|
|
m_pPlayerPed=pPlayerPed;
|
|
m_Wanted.SetPed (pPlayerPed);
|
|
m_targeting.SetOwner(pPlayerPed);
|
|
ResetAllPlayerLOS();
|
|
m_healthRecharge.SetRechargeScriptMultiplier(1.0f);
|
|
|
|
//
|
|
// init all the contained components now we are attaching to a ped
|
|
//
|
|
Assert(CPedFactory::IsCurrentlyWithinFactory() || CPlayerInfo::IsChangingPlayerModel() || !pPlayerPed);
|
|
|
|
m_targeting.SetOwner(pPlayerPed);
|
|
|
|
if (pPlayerPed)
|
|
{
|
|
Assert(pPlayerPed->GetPortalTracker());
|
|
pPlayerPed->GetPortalTracker()->SetLoadsCollisions(true); //force loading of interior collisions
|
|
CPortalTracker::AddToActivatingTrackerList(pPlayerPed->GetPortalTracker()); // causes interiors to activate & stay active
|
|
|
|
// the player can have a cop model in the multiplayer game, which gives him a PEDTYPE_COP - this confuses the dispatch stuff
|
|
if (NetworkInterface::IsInSession() && !CPedType::IsAnimalType(pPlayerPed->GetPedType()))
|
|
{
|
|
pPlayerPed->SetPedType(PEDTYPE_NETWORK_PLAYER);
|
|
}
|
|
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_UpperBodyDamageAnimsOnly, true );
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_DisableLockonToRandomPeds, false );
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_GeneratesSoundEvents, true );
|
|
|
|
// Have to update the max health here so that it is correct for the 2nd player in 2-player games.
|
|
pPlayerPed->SetMaxHealth(CPlayerInfo::PLAYER_DEFAULT_MAX_HEALTH); //CStatsMgr::GetFatAndMuscleModifier(STAT_MODIFIER_MAX_HEALTH);
|
|
pPlayerPed->SetHealth(pPlayerPed->GetMaxHealth(), NetworkInterface::IsNetworkOpen());
|
|
|
|
if (bClearPedDamage)
|
|
{
|
|
pPlayerPed->ClearDamage();
|
|
}
|
|
// pPlayerPed->ClearDecorations(); // should we also clear all the scars and tattoos?
|
|
|
|
// these used to be set in CGameWorld just after the ped was created:
|
|
//mjc-moved m_targeting.SetOwner(this);
|
|
//pPlayerPed->SetHeading(0.0f);
|
|
|
|
//Its legit for some characters to not be
|
|
if (pPlayerPed->GetWeaponManager())
|
|
{
|
|
pPlayerPed->GetPedIntelligence()->GetCombatBehaviour().SetCombatFloat(kAttribFloatWeaponAccuracy, 1.0f);
|
|
}
|
|
|
|
pPlayerPed->GetPedIntelligence()->SetInformRespectedFriends(30.0f, 2);
|
|
|
|
pPlayerPed->m_PedConfigFlags.SetPedLegIkMode( CIkManager::GetDefaultLegIkMode(pPlayerPed) );
|
|
|
|
// The player ped is always treated as suspicious
|
|
pPlayerPed->GetPedIntelligence()->GetPedStealth().GetFlags().SetFlag(CPedStealth::SF_TreatedAsActingSuspiciously);
|
|
// Player's footsteps always generate events
|
|
pPlayerPed->GetPedIntelligence()->GetPedStealth().GetFlags().SetFlag(CPedStealth::SF_PedGeneratesFootStepEvents);
|
|
|
|
// Re setup the players group. We can't do this for players in a network game that have just been created and have no network object (this
|
|
// will be done later)
|
|
if (!NetworkInterface::IsNetworkOpen() || pPlayerPed->GetNetworkObject())
|
|
{
|
|
SetupPlayerGroup();
|
|
}
|
|
|
|
// Make sure the vehicle knows its under player control
|
|
if (pPlayerPed->GetMyVehicle() && m_pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && pPlayerPed->GetMyVehicle()->GetStatus()!=STATUS_WRECKED)
|
|
{
|
|
pPlayerPed->GetMyVehicle()->SetStatus(STATUS_PLAYER);
|
|
}
|
|
|
|
// prevent dead players being revived
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_AllowMedicsToReviveMe, FALSE );
|
|
|
|
if( pPlayerPed->IsLocalPlayer() )
|
|
{
|
|
pPlayerPed->m_nFlags.bAddtoMotionBlurMask = true;
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->m_nFlags.bAddtoMotionBlurMask = false;
|
|
}
|
|
}
|
|
|
|
#if ENABLE_HORSE
|
|
m_InputHorse.SetPlayerPed(m_pPlayerPed);
|
|
#endif
|
|
}
|
|
|
|
// PURPOSE: To get the PhysicalPlayerIndex, if there is one
|
|
PhysicalPlayerIndex CPlayerInfo::GetPhysicalPlayerIndex()
|
|
{
|
|
if(m_pPlayerPed)
|
|
{
|
|
if(CNetObjGame* pNetworkObject = m_pPlayerPed->GetNetworkObject())
|
|
{
|
|
if(CNetGamePlayer* pNetGamePlayer = pNetworkObject->GetPlayerOwner())
|
|
{
|
|
return pNetGamePlayer->GetPhysicalPlayerIndex();
|
|
}
|
|
}
|
|
}
|
|
|
|
return INVALID_PLAYER_INDEX;
|
|
}
|
|
|
|
void CPlayerInfo::SetLastTargetVehicle(CVehicle* pTargetVehicle)
|
|
{
|
|
m_pLastTargetVehicle=pTargetVehicle;
|
|
}
|
|
|
|
|
|
/*
|
|
void CPlayerInfo::SetRemoteVehicle(CVehicle* pVehicle)
|
|
{
|
|
m_pRemoteVehicle=pVehicle;
|
|
}
|
|
*/
|
|
|
|
void CPlayerInfo::SetMayOnlyEnterThisVehicle(CVehicle * pVehicle)
|
|
{
|
|
m_pOnlyEnterThisVehicle = pVehicle;
|
|
}
|
|
|
|
void CPlayerInfo::BumpedInVehicle(const CPed& UNUSED_PARAM(rPed))
|
|
{
|
|
m_uLastTimeBumpedPedInVehicle = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPlayerInfo::DisableAgitation()
|
|
{
|
|
//Set the flag.
|
|
m_bDisableAgitation = true;
|
|
|
|
//Set the timer.
|
|
m_uTimeToEnableAgitation = 0;
|
|
}
|
|
|
|
bool CPlayerInfo::IsSprintAimBreakOutOver() const
|
|
{
|
|
TUNE_GROUP_INT(SPRINT_DEBUG, MIN_SPRINT_BREAKOUT_TIME, 600, 0, 5000, 1);
|
|
return (m_uSprintAimBreakOutTime + MIN_SPRINT_BREAKOUT_TIME) < fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPlayerInfo::CollidedWithPed(CPed& rPed)
|
|
{
|
|
//Ensure the ped is not a player.
|
|
if(rPed.IsPlayer())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure the ped is not already agitated.
|
|
if(rPed.GetPedConfigFlag(CPED_CONFIG_FLAG_IsAgitated))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Check if the peds do not match.
|
|
if(&rPed != m_pCollidedWithPed)
|
|
{
|
|
//Set the ped and timer.
|
|
m_pCollidedWithPed = &rPed;
|
|
m_uCollidedWithPedTimer = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
else
|
|
{
|
|
//Check if we are in the ignore window.
|
|
static dev_u32 s_uIgnoreWindow = 1000;
|
|
if((m_uCollidedWithPedTimer + s_uIgnoreWindow) > fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
//Ignore.
|
|
}
|
|
else
|
|
{
|
|
// Potentially add rumble for this collision.
|
|
HandleCollisionWithRumblePed(rPed);
|
|
|
|
//Check if we are in the agitated window.
|
|
static dev_u32 s_uAgitatedWindow = 6000;
|
|
if((m_uCollidedWithPedTimer + s_uIgnoreWindow + s_uAgitatedWindow) > fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
//Give the ped an agitated event.
|
|
CEventAgitated event(m_pPlayerPed, AT_Bumped);
|
|
rPed.GetPedIntelligence()->AddEvent(event);
|
|
|
|
//Clear the ped and timer.
|
|
m_pCollidedWithPed = NULL;
|
|
m_uCollidedWithPedTimer = 0;
|
|
}
|
|
else
|
|
{
|
|
//Set the timer.
|
|
m_uCollidedWithPedTimer = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::HandleCollisionWithRumblePed(CPed& rPed)
|
|
{
|
|
CPedModelInfo* pModelInfo = rPed.GetPedModelInfo();
|
|
if (!rPed.GetMotionData()->GetIsStill() && pModelInfo && pModelInfo->GetPersonalitySettings().GetCausesRumbleWhenCollidesWithPlayer())
|
|
{
|
|
CControlMgr::StartPlayerPadShakeByIntensity(1000, 0.5f);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Process
|
|
// PURPOSE : Processes the player stuff for one player. The player might jump
|
|
// into a car or leave a car etc.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPlayerInfo::SCANNEARBYVEHICLES = 10.0f;
|
|
dev_u32 CPlayerInfo::DETONATE_TIMER = 800; // milliseconds.
|
|
|
|
#define HEALTH_TAKEN_FOR_JUMPING_OUT_AP_HIGH_SPEED (20.0f) // Health taken for jumping out of car at high speed
|
|
|
|
void CPlayerInfo::Process()
|
|
{
|
|
#if RSG_ASSERT
|
|
const CPed* pPlayer = CGameWorld::FindLocalPlayer();
|
|
|
|
// If the ped does not have player control
|
|
if(pPlayer == NULL || pPlayer->GetControlFromPlayer() == NULL)
|
|
{
|
|
CPed* pControlledPed = NULL;
|
|
|
|
// Find the ped that does have control.
|
|
CEntityIterator entityIterator(IteratePeds);
|
|
CEntity* pEntity = entityIterator.GetNext();
|
|
while(pControlledPed == NULL && pEntity != NULL)
|
|
{
|
|
if(pEntity->GetIsTypePed())
|
|
{
|
|
CPed* pPed = static_cast<CPed*>(pEntity);
|
|
if(pPed->GetControlFromPlayer())
|
|
{
|
|
pControlledPed = pPed;
|
|
}
|
|
}
|
|
|
|
pEntity = entityIterator.GetNext();
|
|
}
|
|
|
|
Assertf(false,
|
|
"Player Ped: '%s' does not have control, '%s' does!",
|
|
(pPlayer ? pPlayer->GetModelName() : "NO PED"),
|
|
(pControlledPed ? pControlledPed->GetModelName() : "NO PED") );
|
|
}
|
|
#endif // RSG_ASSERT
|
|
|
|
// don't process the player info for network bot controlled objects
|
|
if(m_pPlayerPed->IsNetworkBot())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_pPlayerPed && (CGameWorld::GetMainPlayerInfo() == this)){
|
|
ms_cachedMainPlayerPos = VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetPosition());
|
|
}
|
|
|
|
DecayStealthNoise(fwTimer::GetTimeStep());
|
|
|
|
Vector3 DropCoors, Delta;
|
|
|
|
#ifdef DEBUG
|
|
// Last updated 24.11.00
|
|
|
|
AssertEntityPointerValid(m_pPlayerPed);
|
|
|
|
if (GetRemoteVehicle())
|
|
{
|
|
AssertEntityPointerValid_NotInWorld(GetRemoteVehicle());
|
|
}
|
|
|
|
// End last updated 24.11.00
|
|
#endif
|
|
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::IsEditModeActive())
|
|
return;
|
|
#endif
|
|
|
|
Assert(m_pPlayerPed);
|
|
|
|
// Update Targeting
|
|
GetTargeting().Process();
|
|
|
|
CPlayerInfo::UpdateRecklessDriving();
|
|
|
|
ProcessTestForShelter();
|
|
|
|
// player health doesn't recharge when out of
|
|
// sprint energy
|
|
TUNE_GROUP_FLOAT(SPRINT_DEBUG, s_minSprintEnergyForHealthRecharge, 0.0f, 0.0f, 100.0f, 0.01f);
|
|
if (m_fSprintEnergy>s_minSprintEnergyForHealthRecharge)
|
|
{
|
|
// process player health recharging
|
|
m_healthRecharge.Process(m_pPlayerPed);
|
|
}
|
|
|
|
// process player endurance recharging
|
|
m_enduranceManager.Process(m_pPlayerPed);
|
|
|
|
// process player damage over time effects
|
|
m_damageOverTime.Process(m_pPlayerPed);
|
|
|
|
m_fDampenRootTargetWeight = 0.0f;
|
|
|
|
ProcessIsInsideMovingTrain();
|
|
|
|
ProcessWorldExtentsCheck();
|
|
|
|
ProcessNumEnemiesInCombat();
|
|
|
|
ProcessCoverStateTracking();
|
|
|
|
ProcessCombatLoitering();
|
|
|
|
ProcessChargingEnemyGoalPositions();
|
|
|
|
ProcessEnemyElections();
|
|
|
|
ProcessStolenVehicleSpotted();
|
|
|
|
ProcessCollisions();
|
|
|
|
ProcessDisableAgitation();
|
|
|
|
ProcessPlayerVehicleSpeedTimer();
|
|
|
|
m_bRetainParachuteAfterTeleport = m_pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_DisableTakeOffParachutePack) && (GetPedParachuteVariationComponentOverride() != PV_COMP_INVALID);
|
|
|
|
if (CPlayerInfo::AreCNCResponsivenessChangesEnabled(m_pPlayerPed))
|
|
{
|
|
m_pPlayerPed->SetPedResetFlag(CPED_RESET_FLAG_UseFastEnterExitVehicleRates, true);
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ProcessCollisions()
|
|
{
|
|
//Ensure we collided with a ped.
|
|
const CCollisionRecord* pRecord = m_pPlayerPed->GetFrameCollisionHistory()->GetFirstPedCollisionRecord();
|
|
if(!pRecord)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure the entity is valid.
|
|
if(!pRecord->m_pRegdCollisionEntity)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Note that we collided with a ped.
|
|
aiAssert(pRecord->m_pRegdCollisionEntity->GetIsTypePed());
|
|
CollidedWithPed(*static_cast<CPed *>(pRecord->m_pRegdCollisionEntity.Get()));
|
|
}
|
|
|
|
void CPlayerInfo::ProcessDisableAgitation()
|
|
{
|
|
//Ensure agitation is disabled.
|
|
if(!m_bDisableAgitation)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Check if the timer has not been set.
|
|
if(m_uTimeToEnableAgitation == 0)
|
|
{
|
|
//Ensure controls are not disabled.
|
|
if(AreControlsDisabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Check if the player is in a vehicle.
|
|
if(m_pPlayerPed->GetIsInVehicle())
|
|
{
|
|
//Check if the vehicle is still.
|
|
float fSpeedSq = m_pPlayerPed->GetVehiclePedInside()->GetVelocity().XYMag2();
|
|
static dev_float s_fMaxSpeed = 0.1f;
|
|
float fMaxSpeedSq = square(s_fMaxSpeed);
|
|
if(fSpeedSq < fMaxSpeedSq)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
//Check if the player is on a horse.
|
|
else if(m_pPlayerPed->GetIsOnMount())
|
|
{
|
|
//Check if the mount is still.
|
|
if(m_pPlayerPed->GetMountPedOn()->GetMotionData()->GetIsStill())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Check if the player is still.
|
|
if(m_pPlayerPed->GetMotionData()->GetIsStill())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Set the time.
|
|
m_uTimeToEnableAgitation = fwTimer::GetTimeInMilliseconds() + 5000;
|
|
}
|
|
//Check if the timer has expired.
|
|
else if(fwTimer::GetTimeInMilliseconds() > m_uTimeToEnableAgitation)
|
|
{
|
|
//Set the flag.
|
|
m_bDisableAgitation = false;
|
|
|
|
//Clear the time.
|
|
m_uTimeToEnableAgitation = 0;
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ProcessPlayerVehicleSpeedTimer()
|
|
{
|
|
if( CNetwork::IsGameInProgress() )
|
|
{
|
|
if( m_pPlayerPed && m_pPlayerPed->GetIsInVehicle() && m_pPlayerPed->GetMyVehicle() )
|
|
{
|
|
//Ensure the vehicle is moving slow.
|
|
float fSpeedSq = m_pPlayerPed->GetMyVehicle()->GetVelocity().Mag2();
|
|
static dev_float s_fMaxSpeed = 5.0f;
|
|
float fMaxSpeedSq = square(s_fMaxSpeed);
|
|
if(fSpeedSq < fMaxSpeedSq)
|
|
{
|
|
m_fTimeSinceLastDrivingAtSpeed += fwTimer::GetTimeStep();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_fTimeSinceLastDrivingAtSpeed = 0.0f;
|
|
}
|
|
|
|
void CPlayerInfo::ProcessIsInsideMovingTrain()
|
|
{
|
|
//Clear the flags.
|
|
m_bIsInsideMovingTrain = false;
|
|
m_bAllowControlInsideMovingTrain = true;
|
|
m_pTrainPedIsInside = NULL;
|
|
|
|
//Ensure the entity is valid.
|
|
const CEntity* pEntity = m_pPlayerPed->GetGroundPhysical();
|
|
if(!pEntity)
|
|
{
|
|
pEntity = (const CEntity *)m_pPlayerPed->GetAttachParent();
|
|
if(!pEntity)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Ensure the entity is physical.
|
|
if(!pEntity->GetIsPhysical())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure the physical is a vehicle.
|
|
const CPhysical* pPhysical = static_cast<const CPhysical *>(pEntity);
|
|
if(!pPhysical->GetIsTypeVehicle())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure the vehicle is a train.
|
|
const CVehicle* pVehicle = static_cast<const CVehicle *>(pPhysical);
|
|
if(!pVehicle->InheritsFromTrain())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const CTrain* pTrain = static_cast<const CTrain *>(pVehicle);
|
|
|
|
//Ensure the player is inside the train.
|
|
spdAABB boundBox;
|
|
if(!pTrain->GetBoundBox(boundBox).ContainsPoint(m_pPlayerPed->GetTransform().GetPosition()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//TODO: HACK: The metro train bounding box extends well over the top of the car, and we want to
|
|
// be able to stand on top of it and move around. Inside, though, we want to disable control.
|
|
static atHashWithStringNotFinal s_hMetroTrain("metrotrain",0x33C9E158);
|
|
if(pTrain->GetArchetype()->GetModelNameHash() == s_hMetroTrain.GetHash())
|
|
{
|
|
if(m_pPlayerPed->GetTransform().GetPosition().GetZf() > (pTrain->GetTransform().GetPosition().GetZf() + 2.0f))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Don't allow control - in SP, in MP always keep the player in control
|
|
if (!NetworkInterface::IsGameInProgress())
|
|
m_bAllowControlInsideMovingTrain = false;
|
|
|
|
m_pTrainPedIsInside = (CVehicle*)pTrain;
|
|
}
|
|
|
|
//Ensure the train is moving.
|
|
if(!pTrain->IsMoving() && pTrain->GetTrainState() != CTrain::TS_ArrivingAtStation)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Set the flag.
|
|
m_bIsInsideMovingTrain = true;
|
|
}
|
|
|
|
void CPlayerInfo::ProcessStolenVehicleSpotted()
|
|
{
|
|
//Check if the player is in a stolen vehicle, and is not wanted.
|
|
const CVehicle* pVehicle = m_pPlayerPed->GetVehiclePedInside();
|
|
if(pVehicle && pVehicle->m_nVehicleFlags.bIsStolen &&
|
|
(m_pPlayerPed->GetPlayerWanted()->GetWantedLevel() == WANTED_CLEAN))
|
|
{
|
|
//Create the flags.
|
|
s32 iFlags = TargetOption_IgnoreVehicles | TargetOption_TargetInVehicle | TargetOption_UseFOVPerception;
|
|
|
|
//Check if someone spotted the vehicle last frame.
|
|
if(m_pSpotterOfStolenVehicle && !m_pSpotterOfStolenVehicle->IsInjured())
|
|
{
|
|
//Check if the last spotter can target the vehicle.
|
|
ECanTargetResult nCanTargetResult = CPedGeometryAnalyser::CanPedTargetPedAsync(*m_pSpotterOfStolenVehicle, *m_pPlayerPed, iFlags);
|
|
switch(nCanTargetResult)
|
|
{
|
|
case ECanNotTarget:
|
|
{
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Iterate over the nearby peds.
|
|
CEntityScannerIterator it = m_pPlayerPed->GetPedIntelligence()->GetNearbyPeds();
|
|
for(CEntity* pEntity = it.GetFirst(); pEntity != NULL; pEntity = it.GetNext())
|
|
{
|
|
//Ensure the other ped is alive.
|
|
CPed* pOtherPed = static_cast<CPed *>(pEntity);
|
|
if(pOtherPed->IsInjured())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Ensure the other ped is a cop.
|
|
if(!pOtherPed->IsRegularCop())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Ensure the other ped is random.
|
|
if(!pOtherPed->PopTypeIsRandom())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Check if the other ped can see the player.
|
|
ECanTargetResult nCanTargetResult = CPedGeometryAnalyser::CanPedTargetPedAsync(*pOtherPed, *m_pPlayerPed, iFlags);
|
|
if(nCanTargetResult == ECanTarget)
|
|
{
|
|
//Set the spotter.
|
|
m_pSpotterOfStolenVehicle = pOtherPed;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Clear the spotter.
|
|
m_pSpotterOfStolenVehicle = NULL;
|
|
}
|
|
|
|
// Name : ProcessControl
|
|
// Purpose : Processes control for player ped object
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
bool CPlayerInfo::ProcessControl()
|
|
{
|
|
if(DisableControls != 0)
|
|
{
|
|
controlDebugf2("Some player controls are disabled, disabled control flags: 0x%8X", DisableControls);
|
|
}
|
|
|
|
CPed* pPlayerPed = GetPlayerPed();
|
|
pedFatalAssertf( pPlayerPed, "No player ped!" );
|
|
|
|
GetPlayerResetFlags().ResetPreAI();
|
|
if(m_SwitchedToFirstPersonFromThirdPersonCoverCount > 0)
|
|
--m_SwitchedToFirstPersonFromThirdPersonCoverCount;
|
|
|
|
if(m_SwitchedToOrFromFirstPersonCount > 0)
|
|
{
|
|
--m_SwitchedToOrFromFirstPersonCount;
|
|
|
|
if(m_SwitchedToFirstPersonCount > 0)
|
|
--m_SwitchedToFirstPersonCount;
|
|
|
|
if(m_SwitchedToOrFromFirstPersonCount == 1)
|
|
GetPlayerResetFlags().SetFlag(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_OR_FROM_FIRST_PERSON);
|
|
if(m_SwitchedToFirstPersonCount == 1)
|
|
GetPlayerResetFlags().SetFlag(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_FIRST_PERSON);
|
|
|
|
// B*2038645 - Reset simulating aiming flag if we've changed camera mode (in case it doesn't get reset in CPlayerInfo::ProcessFPSState).
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_SimulatingAiming) && (GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_OR_FROM_FIRST_PERSON) || GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_FIRST_PERSON)))
|
|
{
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_SimulatingAiming, false);
|
|
}
|
|
}
|
|
|
|
m_fCachedSprintMultThisFrame = -1.0f;
|
|
|
|
Vector3 vPlayerPos = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
DEV_BREAK_IF_FOCUS( CDebugScene::ShouldDebugBreakOnProcessControlOfFocusEntity(), pPlayerPed );
|
|
DEV_BREAK_ON_PROXIMITY( (CDebugScene::ShouldDebugBreakOnProximityOfProcessControlCallingEntity() || CDebugScene::ShouldDebugBreakOnProximityOfProcessControlCallingPed()), vPlayerPos );
|
|
|
|
if (pPlayerPed->IsControlledByNetworkPlayer())
|
|
{
|
|
// we have control of another network player, just do a simple ped process (and do none of the player processing in here)
|
|
return true;
|
|
}
|
|
|
|
Assertf((pPlayerPed->PopTypeGet() == POPTYPE_PERMANENT), "CPlayerPed::ProcessControl - Player Ped should always be a permanent character");
|
|
|
|
Assertf(pPlayerPed->GetRagdollState() != RAGDOLL_STATE_ANIM_LOCKED || AreControlsDisabled(),"CPlayerPed: Player is in control of locked ragdoll. Has script called UNLOCK_RAGDOLL on playerped?");
|
|
|
|
// Decrement hit by water cannon timer. Timer gets increased whenever player gets hit by water cannon
|
|
// This ensures player can't get trapped permanently by water cannon
|
|
if(m_fHitByWaterCannonImmuneTimer > 0.0f)
|
|
m_fHitByWaterCannonImmuneTimer -= fwTimer::GetTimeStep();
|
|
|
|
if(m_fHitByWaterCannonTimer > 0.0f)
|
|
m_fHitByWaterCannonTimer -= fwTimer::GetTimeStep()*PLAYER_WATER_CANNON_COOLDOWN_RATE;
|
|
|
|
Process(); //potentially we want to pull all the code in Process into here (or split the two functions into more logical blocks). //TODO
|
|
|
|
CControl* pControl = pPlayerPed->GetControlFromPlayer();
|
|
|
|
|
|
// If a player ped stands on a police car he will get a wanted level
|
|
CPhysical * pGroundPhysical = pPlayerPed->GetGroundPhysical();
|
|
if (pGroundPhysical && pGroundPhysical->GetIsTypeVehicle())
|
|
{
|
|
CVehicle *pVeh = static_cast<CVehicle*>(pGroundPhysical);
|
|
|
|
// Make sure the vehicle is a cop car and the player isn't a cop.
|
|
if ( !(fwTimer::GetSystemFrameCount()&63) &&
|
|
pVeh->ReportCrimeIfStandingOn() && !pPlayerPed->IsLawEnforcementPed() && !pVeh->IsPersonalVehicle())
|
|
{
|
|
|
|
// The vehicle must have alive cops in it or have police within the detection range
|
|
if ( pVeh->HasAliveLawPedsInIt() ||
|
|
CWanted::WorkOutPolicePresence(pPlayerPed->GetTransform().GetPosition(),
|
|
CCrimeInformationManager::GetInstance().GetImmediateDetectionRange(CRIME_STAND_ON_POLICE_CAR)) )
|
|
{
|
|
CCrime::ReportCrime(CRIME_STAND_ON_POLICE_CAR, pVeh, pPlayerPed);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get PlayerPed Position again just in case it could have changed inside Process()
|
|
vPlayerPos = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition());
|
|
|
|
m_Wanted.Update( vPlayerPos, pPlayerPed->GetVehiclePedInside(), true, true );
|
|
|
|
// let the player fire his gun when ragdolling
|
|
CPedWeaponManager * pWeaponManager = pPlayerPed->GetWeaponManager();
|
|
if( pWeaponManager )
|
|
{
|
|
//Check on weapon cook timers
|
|
const CWeaponInfo* pEquippedWeaponInfo = pWeaponManager->GetEquippedWeaponInfo();
|
|
if( pEquippedWeaponInfo )
|
|
{
|
|
CWeapon* pEquippedWeapon = pWeaponManager->GetEquippedWeapon();
|
|
if( pEquippedWeapon )
|
|
{
|
|
if( pControl && pControl->GetPedAttack().IsDown() && pPlayerPed->GetUsingRagdoll() )
|
|
{
|
|
CObject* pWeaponObject = pWeaponManager->GetEquippedWeaponObject();
|
|
if( pWeaponObject && pWeaponObject->GetIsVisibleForModule(SETISVISIBLE_MODULE_GAMEPLAY) )
|
|
{
|
|
// We're pretty sure this is the actual player weapon, so we can force that one to true
|
|
pWeaponObject->m_nFlags.bAddtoMotionBlurMask = true;
|
|
|
|
if (pEquippedWeaponInfo->GetIsGunOrCanBeFiredLikeGun() && !pPlayerPed->IsInjured() )
|
|
{
|
|
bool bCanFire = true;
|
|
if(m_pPlayerPed->m_Buoyancy.GetStatus() == FULLY_IN_WATER)
|
|
{
|
|
bCanFire = false;
|
|
}
|
|
else
|
|
{
|
|
CTask * pTaskDrive = pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_PLAYER_DRIVE);
|
|
CTask * pTaskJumpRollFromVehicle = pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_NM_JUMP_ROLL_FROM_ROAD_VEHICLE);
|
|
if(pTaskDrive || pTaskJumpRollFromVehicle)
|
|
bCanFire = false;
|
|
}
|
|
|
|
if(bCanFire)
|
|
{
|
|
Matrix34 m = MAT34V_TO_MATRIX34( pWeaponObject->GetMatrix() );
|
|
pEquippedWeapon->Fire( CWeapon::sFireParams( pPlayerPed, m ) );
|
|
}
|
|
}
|
|
else if (pEquippedWeaponInfo->GetCookWhileAiming() && pEquippedWeapon && pEquippedWeapon->GetCookTime()==0 && pPlayerPed->IsInjured())
|
|
{
|
|
pEquippedWeapon->StartCookTimer(fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
}
|
|
}
|
|
|
|
// if( pEquippedWeapon->GetIsCooking() ) //Removing grenade disarms per B* 983722
|
|
// {
|
|
// if (!pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsAiming) && !pPlayerPed->GetUsingRagdoll() && !IsFiring())
|
|
// pEquippedWeapon->CancelCookTimer();
|
|
// }
|
|
}
|
|
}
|
|
|
|
// Take care of all hand placed bombs
|
|
// Unfortunately we can never eliminate this hard coded value to STICKYBOMB since
|
|
// we cannot always have sticky bombs equipped when attempting to detonate
|
|
if( pPlayerPed && pControl )
|
|
{
|
|
CVehicle* pPlayerVehicle = pPlayerPed->GetMyVehicle();
|
|
// If we are not in a vehicle the use is down to detonate. Otherwise detonate the button was released within a time frame.
|
|
// i.e. button tapped (to stop conflicts with other vehicle controls).
|
|
if( (pControl->GetPedDetonate().IsDown() && (!pPlayerVehicle || !pPlayerVehicle->GetVehicleAudioEntity()->GetHasNormalRadio() || !pPlayerVehicle->GetVehicleAudioEntity()->IsRadioEnabled() ))
|
|
|| (pControl->GetPedDetonate().IsReleased() && DetonateDownTime + DETONATE_TIMER > fwTimer::GetTimeInMilliseconds()) )
|
|
{
|
|
DetonateDownTime = 0;
|
|
|
|
const u32 uTimeAllowToDetonateAfterDeath = 1000;
|
|
if( (!pPlayerPed->GetIsDeadOrDying() || fwTimer::GetTimeInMilliseconds() - pPlayerPed->GetDeathTime() <= uTimeAllowToDetonateAfterDeath)
|
|
&& !pPlayerPed->GetCustodian()
|
|
&& !pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsHandCuffed)
|
|
&& CProjectileManager::CanExplodeTriggeredProjectiles(m_pPlayerPed) )
|
|
{
|
|
// This is so conflicting inputs remains disabled until detonate is released.
|
|
pControl->SetInputExclusive(INPUT_DETONATE);
|
|
CProjectileManager::ExplodeTriggeredProjectiles( m_pPlayerPed );
|
|
|
|
bExplodedTriggeredProjectilesWithThisButtonPress = true;
|
|
}
|
|
}
|
|
// if we are here then we are in a car or the button is not down. if the button has just been pressed start the timer to see
|
|
// if it is released in time (i.e. to detect tapped or held).
|
|
else if( pControl->GetPedDetonate().IsPressed() && CProjectileManager::CanExplodeTriggeredProjectiles(m_pPlayerPed) )
|
|
{
|
|
DetonateDownTime = fwTimer::GetTimeInMilliseconds();
|
|
pControl->SetInputExclusive(INPUT_DETONATE, ioValue::DisableOptions(ioValue::DisableOptions::F_DISABLE_UPDATE_WHEN_DISABLED));
|
|
}
|
|
// whilst the button is down and we are in a vehicle, continue suppressing conflicting inputs unless the timer has expired
|
|
// (i.e. button is held down so do not detonate).
|
|
else if( pControl->GetPedDetonate().IsDown() && DetonateDownTime + DETONATE_TIMER > fwTimer::GetTimeInMilliseconds() )
|
|
{
|
|
// This is so conflicting inputs remains disabled until detonate is released.
|
|
pControl->SetInputExclusive(INPUT_DETONATE, ioValue::DisableOptions(ioValue::DisableOptions::F_DISABLE_UPDATE_WHEN_DISABLED));
|
|
}
|
|
else if( ( ( pControl->GetPedDetonate().IsDown() || pControl->GetPedDetonate().IsReleased() ) && bExplodedTriggeredProjectilesWithThisButtonPress ) )
|
|
{
|
|
// This conflicts with grenade toss
|
|
pControl->SetInputExclusive(INPUT_DETONATE, ioValue::DisableOptions(ioValue::DisableOptions::F_DISABLE_UPDATE_WHEN_DISABLED));
|
|
|
|
if( pControl->GetPedDetonate().IsReleased() )
|
|
{
|
|
bExplodedTriggeredProjectilesWithThisButtonPress = false;
|
|
}
|
|
|
|
DetonateDownTime = 0;
|
|
}
|
|
else if( bExplodedTriggeredProjectilesWithThisButtonPress )
|
|
{
|
|
// Fallback to fix up this bool if it doesn't get fixed up above
|
|
bExplodedTriggeredProjectilesWithThisButtonPress = false;
|
|
|
|
DetonateDownTime = 0;
|
|
}
|
|
|
|
else
|
|
{
|
|
DetonateDownTime = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DetonateDownTime = 0;
|
|
}
|
|
}
|
|
|
|
// RECHARGE SPRINT ENERGY - Done here before tasks are processed
|
|
TUNE_GROUP_BOOL(SPRINT_DEBUG, DISABLE_RECHARGE, false);
|
|
if (!DISABLE_RECHARGE)
|
|
{
|
|
// Full restoration durations are longer the higher the stat, maybe should scale with the stat so the duration is fixed?
|
|
TUNE_GROUP_FLOAT(SPRINT_DEBUG, RUNNING_RESTORE_DURATION, 50.0f, 1.0f, 100.0f, 1.0f);
|
|
TUNE_GROUP_FLOAT(SPRINT_DEBUG, NOT_RUNNING_RESTORE_DURATION, 15.0f, 1.0f, 100.0f, 1.0f);
|
|
CTaskMotionBase* pBaseTask = pPlayerPed->GetCurrentMotionTask();
|
|
const bool bUnderwater = pBaseTask->IsUnderWater();
|
|
|
|
if (pPlayerPed->GetIsInVehicle() && pPlayerPed->GetMyVehicle()->InheritsFromBicycle())
|
|
{
|
|
const bool bIsStillOnBicycle = pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsStillOnBicycle);
|
|
const float fSprintStatValue = Clamp(static_cast<float>(StatsInterface::GetIntStat(STAT_STAMINA.GetStatId())) / 100.0f, 0.0f, 1.0f);
|
|
float fRestoreDuration = bIsStillOnBicycle ? ms_Tunables.m_BikeMinRestoreDuration : ms_Tunables.m_BikeMaxRestoreDuration;
|
|
if (!bIsStillOnBicycle)
|
|
{
|
|
fRestoreDuration = (1.0f - fSprintStatValue) * ms_Tunables.m_BikeMinRestoreDuration + fSprintStatValue * ms_Tunables.m_BikeMaxRestoreDuration;
|
|
}
|
|
//aiDisplayf("fRestoreDuration = %.2f, fSprintStatValue = %.2f, rate = %.2f", fRestoreDuration, fSprintStatValue, GetPlayerDataMaxSprintEnergy() / (fRestoreDuration));
|
|
TUNE_GROUP_BOOL(SPRINT_DEBUG, ALLOW_RESTORE_WHEN_HELD_ON_ZERO_ENERGY, true);
|
|
TUNE_GROUP_INT(SPRINT_DEBUG, MIN_HELD_DURATION_TO_HANDLE_SPRINT_ENERGY, 100, 0, 1000, 10);
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
const bool bBlockHandleSprintWhenBikeSprintToggleEnabled = pControl && pControl->GetToggleBikeSprintOn();
|
|
#else // KEYBOARD_MOUSE_SUPPORT
|
|
const bool bBlockHandleSprintWhenBikeSprintToggleEnabled = false;
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
const bool bSprintHeldDown = ALLOW_RESTORE_WHEN_HELD_ON_ZERO_ENERGY && pControl && pControl->GetVehiclePushbikePedal().HistoryHeldDown(MIN_HELD_DURATION_TO_HANDLE_SPRINT_ENERGY);
|
|
if (((!bBlockHandleSprintWhenBikeSprintToggleEnabled && bSprintHeldDown) || m_fSprintEnergy > 0.0f) || m_fSprintControlCounter == 0.0f) //don't recharge when 0 and sprinting
|
|
HandleSprintEnergy(false, GetPlayerDataMaxSprintEnergy() / (fRestoreDuration));
|
|
}
|
|
else
|
|
{
|
|
CPedMotionData& motionData = *pPlayerPed->GetMotionData();
|
|
if (bUnderwater)
|
|
{
|
|
// Restore sprint energy at a lower rate if running but not sprinting
|
|
// or walking, for some reason the mbr underwater is capped
|
|
if(motionData.GetIsWalking() || motionData.GetIsRunning())
|
|
HandleSprintEnergy(false, GetPlayerDataMaxSprintEnergy() / RUNNING_RESTORE_DURATION);
|
|
// Restore sprint energy if not running or sprinting
|
|
else if(!motionData.GetIsSprinting())
|
|
HandleSprintEnergy(false, GetPlayerDataMaxSprintEnergy() / NOT_RUNNING_RESTORE_DURATION);
|
|
}
|
|
else
|
|
{
|
|
// Restore sprint energy at a lower rate if running but not sprinting
|
|
if(motionData.GetIsRunning())
|
|
HandleSprintEnergy(false, GetPlayerDataMaxSprintEnergy() / RUNNING_RESTORE_DURATION);
|
|
// Restore sprint energy if not running or sprinting
|
|
else if(!motionData.GetIsSprinting())
|
|
HandleSprintEnergy(false, GetPlayerDataMaxSprintEnergy() / NOT_RUNNING_RESTORE_DURATION);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pPlayerPed->GetIsDeadOrDying())
|
|
{
|
|
GetTargeting().ClearLockOnTarget();
|
|
return true;
|
|
}
|
|
|
|
// Cache off the button inputs since we cannot check for inputs in ProcessPostRender as they have already been reset by that time
|
|
if( pControl && !CControlMgr::IsDisabledControl( const_cast<CControl*>(pControl) ) )
|
|
{
|
|
m_bWasLightAttackPressed = pControl->GetMeleeAttackLight().IsPressed();
|
|
m_bWasAlternateAttackPressed = pControl->GetMeleeAttackAlternate().IsPressed();
|
|
}
|
|
else
|
|
{
|
|
m_bWasLightAttackPressed = false;
|
|
m_bWasAlternateAttackPressed = false;
|
|
}
|
|
|
|
// Every so often if we are inside an interior & standing on an object, we create a shocking event.
|
|
// This is intended to make peds look if we're standing on tables, etc. (however it might not work
|
|
// quite as planned as many of the interior furniture is placed as part of the building)
|
|
if(pPlayerPed->GetPortalTracker()->IsInsideInterior() && pGroundPhysical && pGroundPhysical->GetIsTypeObject())
|
|
{
|
|
m_fTimeToNextCreateInteriorShockingEvents -= fwTimer::GetTimeStep();
|
|
if(m_fTimeToNextCreateInteriorShockingEvents <= 0.0f)
|
|
{
|
|
CEventShockingRunningPed ev(*pPlayerPed);
|
|
CShockingEventsManager::Add(ev);
|
|
m_fTimeToNextCreateInteriorShockingEvents = ms_fInteriorShockingEventFreq;
|
|
}
|
|
}
|
|
|
|
// Store whether we were firing this frame
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_WasFiring, pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_IsFiring ) );
|
|
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_IsFiring, pPlayerPed->GetPlayerInfo()->IsFiring() );
|
|
|
|
// Store whether we are aiming
|
|
bool bIsAiming = pPlayerPed->GetPlayerInfo()->IsAiming();
|
|
|
|
if(!IsSprintAimBreakOutOver())
|
|
{
|
|
if((pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsFiring) && !pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_WasFiring)) || (bIsAiming && !m_bWasAiming))
|
|
{
|
|
// Clear timer
|
|
m_uSprintAimBreakOutTime = 0;
|
|
}
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_ExitingFPSCombatRoll) && !pPlayerPed->GetMotionData()->GetCombatRoll())
|
|
{
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_ExitingFPSCombatRoll, false);
|
|
m_fExitCombatRollToScopeTimerInFPS = 0.0f;
|
|
}
|
|
|
|
bool bFPSMode = pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false);
|
|
if (bFPSMode)
|
|
{
|
|
if(pPlayerPed->GetMotionData()->GetCombatRoll() && pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InFPSUnholsterTransition))
|
|
{
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_ExitingFPSCombatRoll, true);
|
|
}
|
|
|
|
if(pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_ExitingFPSCombatRoll))
|
|
{
|
|
m_fExitCombatRollToScopeTimerInFPS += fwTimer::GetTimeStep();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_fCoverHeadingCorrectionAimHeading = FLT_MAX;
|
|
}
|
|
#endif
|
|
|
|
m_bWasAiming = bIsAiming;
|
|
|
|
ProcessFireProofTimer();
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
TUNE_GROUP_BOOL(FIRST_PERSON_TUNE, USE_FPS_FIRING_BOUNDARIES, false);
|
|
if(USE_FPS_FIRING_BOUNDARIES && pPlayerPed->IsLocalPlayer() && pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
float fFiringAttackValue = GetFiringAttackValue();
|
|
if(fFiringAttackValue > ms_PlayerFPSFireBoundaryHigh)
|
|
{
|
|
m_bUseHighFiringAttackBoundary = true;
|
|
}
|
|
|
|
if(fFiringAttackValue < ms_PlayerFPSFireBoundaryLow)
|
|
{
|
|
m_bUseHighFiringAttackBoundary = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_bUseHighFiringAttackBoundary = true;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
} // end - CPlayerInfo::ProcessControl
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
void CPlayerInfo::ProcessFPSState(bool bIgnoreWeaponWheel)
|
|
{
|
|
CPed* pPlayerPed = GetPlayerPed();
|
|
pedFatalAssertf( pPlayerPed, "No player ped!" );
|
|
|
|
|
|
#if FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
// B*2318618: Set scope state based on menu preference if it's been modified.
|
|
if (sm_bShouldUpdateScopeStateFromMenu)
|
|
{
|
|
sm_bShouldUpdateScopeStateFromMenu = false;
|
|
eDefaultAimTypeFPS menuPref = static_cast<eDefaultAimTypeFPS>(CPauseMenu::GetMenuPreference(PREF_FPS_DEFAULT_AIM_TYPE));
|
|
switch(menuPref)
|
|
{
|
|
case FPS_AIM_NORMAL:
|
|
m_bInFPSScopeState = false;
|
|
break;
|
|
case FPS_AIM_IRON_SIGHTS:
|
|
m_bInFPSScopeState = true;
|
|
break;
|
|
default:
|
|
m_bInFPSScopeState = false;
|
|
break;
|
|
}
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED && KEYBOARD_MOUSE_SUPPORT
|
|
|
|
if(!bIgnoreWeaponWheel && CNewHud::IsShowingHUDMenu())
|
|
{
|
|
// Update the previous fps state to current state to avoid looping in transitions.
|
|
if(pPlayerPed->GetMotionData() && pPlayerPed->GetMotionData()->GetPreviousFPSState() == pPlayerPed->GetMotionData()->GetFPSState())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Do not update FPS state when controls are disabled
|
|
TUNE_GROUP_BOOL(FIRST_PERSON_TUNE, UPDATE_FPS_STATE_WHEN_CONTROLS_DISABLED, true);
|
|
if (!UPDATE_FPS_STATE_WHEN_CONTROLS_DISABLED && pPlayerPed->GetPlayerInfo() && pPlayerPed->GetPlayerInfo()->AreControlsDisabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int viewMode = camControlHelper::GetViewModeForContext(camControlHelperMetadataViewMode::ON_FOOT);
|
|
bool bFPSMode = pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false) || (m_bForceFPSRNGState && viewMode == camControlHelperMetadataViewMode::FIRST_PERSON);
|
|
|
|
if(pPlayerPed->GetMotionData())
|
|
{
|
|
pPlayerPed->GetMotionData()->SetUsingFPSMode(pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false));
|
|
}
|
|
|
|
// Set current FPS state based on player input
|
|
|
|
CPedWeaponManager* pWeapMgr = pPlayerPed->GetWeaponManager();
|
|
const CWeaponInfo* pEquippedWeaponInfo = pWeapMgr && pWeapMgr->GetEquippedWeapon() ? pWeapMgr->GetEquippedWeapon()->GetWeaponInfo() : NULL;
|
|
|
|
if (bFPSMode && pEquippedWeaponInfo)
|
|
{
|
|
int iState = CPedMotionData::FPS_IDLE; //Relaxed/idle pose
|
|
bool bSprint = ControlButtonSprint(CPlayerInfo::SPRINT_ON_FOOT) > 1.0f;
|
|
bool bPlayerIsInCutscene = (pPlayerPed->GetPedIntelligence() && pPlayerPed->GetPedIntelligence()->FindTaskByType(CTaskTypes::TASK_CUTSCENE));
|
|
bool bIsUnarmed = pEquippedWeaponInfo->GetIsUnarmed() || pEquippedWeaponInfo->GetIsMeleeFist();
|
|
bool bUseIdleStateOnly = (pEquippedWeaponInfo->GetIsMelee() && !pEquippedWeaponInfo->GetCanBeAimedLikeGunWithoutFiring() && !bIsUnarmed) || pEquippedWeaponInfo->GetEnableFPSIdleOnly() || bPlayerIsInCutscene;
|
|
const bool bFirstPersonLookBehind = camInterface::GetGameplayDirector().GetFirstPersonShooterCamera() ? camInterface::GetGameplayDirector().GetFirstPersonShooterCamera()->IsLookingBehind() : false;
|
|
TUNE_GROUP_FLOAT(AIMING_DEBUG, fTimeToStayInRunAndGunFPS, 5.0f, 0.0f, 20.0f, 0.01f);
|
|
bool bStayInRNG = false;
|
|
bool bFiring = IsFiring();
|
|
|
|
//If locking on to friendly targets while unarmed, disable FPS LT state and allow idle only
|
|
if(bIsUnarmed)
|
|
{
|
|
CEntity* pTargetEntity = GetTargeting().GetLockOnTarget();
|
|
CPed* pTargetPed = (pTargetEntity && pTargetEntity->GetIsTypePed()) ? static_cast<CPed*>(pTargetEntity) : NULL;
|
|
if( IsSoftAiming(false) || (pTargetPed && pTargetPed->GetPedConfigFlag(CPED_CONFIG_FLAG_AllowPlayerLockOnIfFriendly) && pTargetPed->GetPedType() == PEDTYPE_ANIMAL))
|
|
{
|
|
bUseIdleStateOnly = true;
|
|
}
|
|
}
|
|
|
|
if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_FiringWeapon) || pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_FiringWeaponWhenReady))
|
|
{
|
|
bFiring = true;
|
|
m_bFiredGunInFPS = true;
|
|
m_fRNGTimerFPS = 0.0f; //Reset run and gun timer when firing
|
|
if(bSprint)
|
|
{
|
|
m_bFiredDuringFPSSprint = true;
|
|
}
|
|
}
|
|
|
|
// B*2038645 - Stay in RNG mode if we swap between characters if the character we're switching to is aiming. Force flag/timer reset in CGameWorld::ChangePlayerPed.
|
|
if (m_bForceFPSRNGState)
|
|
{
|
|
m_bFiredGunInFPS = true;
|
|
bStayInRNG = true;
|
|
|
|
// Clear the force aiming/rng flags once we're fully in FPS camera mode again
|
|
if (pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
m_bForceFPSRNGState = false;
|
|
pPlayerPed->SetPedConfigFlag(CPED_CONFIG_FLAG_SimulatingAiming, false);
|
|
}
|
|
}
|
|
|
|
bool bDisableRNG = bIsUnarmed;
|
|
bool bThrownWeapon = pEquippedWeaponInfo->GetIsThrownWeapon() && pEquippedWeaponInfo->GetUseFPSAimIK() && !pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_ThrowingGrenadeWhileAiming);
|
|
bool bJerryCan = pEquippedWeaponInfo->GetGroup() == WEAPONGROUP_PETROLCAN;
|
|
// Stay in RNG if timer is still going
|
|
if ((m_bWasRunAndGunning || m_bFiredInFPSFullAimState) && !bDisableRNG && !bThrownWeapon && m_bFiredGunInFPS && (m_fRNGTimerFPS <= fTimeToStayInRunAndGunFPS))
|
|
{
|
|
if(pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsReloading))
|
|
{
|
|
m_fRNGTimerFPS = 0.0f; //Reset run and gun timer when reloading
|
|
bStayInRNG = true;
|
|
}
|
|
else if (!bFiring && bSprint)
|
|
{
|
|
// Leave RNG mode immediately if sprinting
|
|
bStayInRNG = false;
|
|
}
|
|
else
|
|
{
|
|
bStayInRNG = true;
|
|
}
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(FIRST_PERSON_COVER_TUNE, DISABLE_STAY_IN_RNG_WHEN_NOT_AIMING_DIRECT_FROM_COVER, true);
|
|
bool bShouldForceAimingStateDueToAimingOutFromCover = false;
|
|
bool bAimingDirectlyInCover = false;
|
|
if (DISABLE_STAY_IN_RNG_WHEN_NOT_AIMING_DIRECT_FROM_COVER && pPlayerPed->GetIsInCover())
|
|
{
|
|
bAimingDirectlyInCover = CTaskCover::IsPlayerAimingDirectlyInFirstPerson(*pPlayerPed);
|
|
if (!bAimingDirectlyInCover)
|
|
{
|
|
if (pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsAiming))
|
|
{
|
|
bShouldForceAimingStateDueToAimingOutFromCover = true;
|
|
}
|
|
bStayInRNG = false;
|
|
}
|
|
}
|
|
|
|
if(bJerryCan && !IsFiring())
|
|
{
|
|
// Quit RNG mode immediately if player stops dumping fuel from the jerry can
|
|
bStayInRNG = false;
|
|
m_bFiredGunInFPS = false;
|
|
m_bWasRunAndGunning = false;
|
|
m_bFiredDuringFPSSprint = false;
|
|
m_fRNGTimerFPS = 0.0f;
|
|
}
|
|
|
|
if (!CTaskMobilePhone::IsRunningMobilePhoneTask(*pPlayerPed) && !bUseIdleStateOnly)
|
|
{
|
|
|
|
// B*2555616: Stay in same FPS state while firing and not at an interruptible point (was causing a pop when restarting aiming tasks, v noticable with long fire anims ie the revolver).
|
|
// Also don't allow toggling between scope/iron sights while firing.
|
|
// Only do this for certain weapons at this point. Most weapon fire anims (particularly automatic weapons) are short and don't tend to move the gun around as much.
|
|
bool bFiredWhileAiming = false;
|
|
bool bFiredWhileRNG = false;
|
|
if (pEquippedWeaponInfo->GetBlockFirstPersonStateTransitionWhileFiring())
|
|
{
|
|
CTaskAimGunOnFoot* pAimGunOnFootTask = pPlayerPed->GetPedIntelligence() ? static_cast<CTaskAimGunOnFoot*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_AIM_GUN_ON_FOOT)) : NULL;
|
|
if (pAimGunOnFootTask && pAimGunOnFootTask->GetIsFiring() && !pAimGunOnFootTask->GetIsFireInterruptible())
|
|
{
|
|
bFiredWhileRNG = (pPlayerPed->GetMotionData()->GetWasFPSRNG() || pPlayerPed->GetMotionData()->GetWasFPSIdle());
|
|
bFiredWhileAiming = (pPlayerPed->GetMotionData()->GetWasFPSLT() || pPlayerPed->GetMotionData()->GetWasFPSScope());
|
|
}
|
|
}
|
|
|
|
const bool bAimingInCoverButNotDirectly = pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsAimingFromCover) && !bAimingDirectlyInCover;
|
|
if ((bAimingInCoverButNotDirectly || IsAiming(!bThrownWeapon) || bFiredWhileAiming)
|
|
&& !IsRunAndGunning(bThrownWeapon || bJerryCan, bThrownWeapon)
|
|
&& !pEquippedWeaponInfo->GetEnableFPSRNGOnly() &&
|
|
(!pEquippedWeaponInfo->GetOnlyAllowFiring() || IsFiring())
|
|
&& !pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsEnteringCover)
|
|
&& !bFiredWhileRNG)
|
|
{
|
|
iState = CPedMotionData::FPS_LT; //Left trigger full aim
|
|
|
|
// Toggle scope if R3 pressed while aiming
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
|
|
// B*2204503: Don't process scope state while using a sniper rifle.
|
|
const bool bWeaponHasFirstPersonScope = pPlayerPed->GetWeaponManager() && pPlayerPed->GetWeaponManager()->GetEquippedWeaponHasFirstPersonScope();
|
|
if(!pEquippedWeaponInfo->GetDisableFPSScope() && pControl && !bWeaponHasFirstPersonScope && !bFiredWhileAiming)
|
|
{
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
// B* 2032581: The way the mouse wheel works we have to always toggle regardless of meta data settings. The reason
|
|
// for this is that the wheel cannot be held forwards or backwards. To make this work more like the sniper rifle on
|
|
// the mouse, we will use wheel forward for enter accurate aim and wheel backwards for exit accurate aim. On the
|
|
// keyboard, this will be two different buttons.
|
|
if(pControl->GetPedAccurateAim().GetSource().m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE)
|
|
{
|
|
const float aimValue = pControl->GetPedAccurateAim().GetUnboundNorm();
|
|
|
|
// Positive is wheel up/forwards.
|
|
if(aimValue > ioValue::BUTTON_DOWN_THRESHOLD)
|
|
{
|
|
m_bInFPSScopeState = false;
|
|
}
|
|
else if(aimValue < -ioValue::BUTTON_DOWN_THRESHOLD)
|
|
{
|
|
m_bInFPSScopeState = true;
|
|
}
|
|
}
|
|
else
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
if(pControl && pControl->GetPedAccurateAim().IsPressed())
|
|
{
|
|
m_bInFPSScopeState = !m_bInFPSScopeState;
|
|
}
|
|
}
|
|
TUNE_GROUP_FLOAT(FIRST_PERSON_TUNE, fCombatRollExitSwitchToScopeZoomTime, 0.15f, 0.0f, 5.0f, 0.01f);
|
|
bool bCombatRollIgnoreScope = pPlayerPed->GetMotionData()->GetCombatRoll() && m_fExitCombatRollToScopeTimerInFPS < fCombatRollExitSwitchToScopeZoomTime;
|
|
bool bParachuteIgnoresScope = pPlayerPed->GetIsParachuting();
|
|
const bool bReloadingInCover = pPlayerPed->GetIsInCover() && pPlayerPed->GetPedIntelligence()->FindTaskSecondaryByType(CTaskTypes::TASK_RELOAD_GUN);
|
|
const bool bShouldDisableFPSAimForScope = bWeaponHasFirstPersonScope && pEquippedWeaponInfo->GetDisableFPSAimForScope();
|
|
|
|
// Go to scope state directly if we are aiming a sniper rifle.
|
|
/*if(pEquippedWeaponInfo->GetHasFirstPersonScope() && IsAiming())
|
|
{
|
|
bCombatRollIgnoreScope = false;
|
|
}*/
|
|
|
|
if (!pEquippedWeaponInfo->GetDisableFPSScope() && (m_bInFPSScopeState || bShouldDisableFPSAimForScope) && !bCombatRollIgnoreScope && !bReloadingInCover && !bParachuteIgnoresScope)
|
|
{
|
|
iState = CPedMotionData::FPS_SCOPE; //Scoped/iron sights aim
|
|
}
|
|
|
|
// Start a timer if we're firing so we go to RNG if we let go of LT
|
|
if (IsFiring())
|
|
{
|
|
m_bFiredInFPSFullAimState = true;
|
|
}
|
|
if (m_bFiredInFPSFullAimState)
|
|
{
|
|
m_fRNGTimerFPS += fwTimer::GetTimeStep();
|
|
// Don't go back to RNG if we've surpassed the max time. Go straight back to idle instead.
|
|
if (m_fRNGTimerFPS > fTimeToStayInRunAndGunFPS)
|
|
{
|
|
m_bFiredInFPSFullAimState = false;
|
|
m_fRNGTimerFPS = 0.0f;
|
|
}
|
|
}
|
|
//B*2014264: Reset the timer if we come back into an aim state whilst the RNG timer is ticking
|
|
if (!m_bFiredInFPSFullAimState && m_fRNGTimerFPS > 0.0f)
|
|
{
|
|
m_fRNGTimerFPS = 0.0f;
|
|
}
|
|
}
|
|
else if (!bFirstPersonLookBehind && !bDisableRNG && (IsRunAndGunning(bThrownWeapon || bJerryCan, bThrownWeapon) || bStayInRNG || m_bFiredInFPSFullAimState))
|
|
{
|
|
iState = CPedMotionData::FPS_RNG; //Run and gun
|
|
m_bWasRunAndGunning = true;
|
|
|
|
// If the sprint button is held, we defer to decision of whether to add more time for later (still keeping the timer running)
|
|
// This is also applies to LT firing as we could do aim->fire->sprint_held->rng (and other cases), in which case we want to add time back on the timer (if needed)
|
|
// during the sprint_held->rng transition and not earlier.
|
|
bool bFiredBeforeSwitchingToRNG = (m_bFiredInFPSFullAimState || m_bFiredDuringFPSSprint) && !bSprint;
|
|
|
|
// Put 2 seconds back on the timer if we've swapped from full aiming/sprinting to ensure we don't just flick into the RNG state for a v short amount of time
|
|
if (bFiredBeforeSwitchingToRNG && (fTimeToStayInRunAndGunFPS - m_fRNGTimerFPS < 3.0f) )
|
|
{
|
|
m_fRNGTimerFPS = fTimeToStayInRunAndGunFPS - 3.0f;
|
|
m_bFiredInFPSFullAimState = false;
|
|
m_bFiredDuringFPSSprint = false;
|
|
}
|
|
else if (bFiredBeforeSwitchingToRNG)
|
|
{
|
|
// Else un-set the fired from full aim flag
|
|
m_bFiredInFPSFullAimState = false;
|
|
m_bFiredDuringFPSSprint = false;
|
|
}
|
|
// Only start timer once we've stopped RNG'ing
|
|
if (bStayInRNG || m_bFiredInFPSFullAimState)
|
|
{
|
|
m_fRNGTimerFPS += fwTimer::GetTimeStep();
|
|
if(m_fRNGTimerFPS > fTimeToStayInRunAndGunFPS)
|
|
{
|
|
m_bFiredInFPSFullAimState = false;
|
|
m_bFiredDuringFPSSprint = false;
|
|
}
|
|
}
|
|
}
|
|
else if (bShouldForceAimingStateDueToAimingOutFromCover && !bThrownWeapon)
|
|
{
|
|
// Stay in our current state when aiming out from cover (cover task handles outros)
|
|
// Don't do this for thrown weapons, we don't need to and it causes B*2028622
|
|
iState = pPlayerPed->GetMotionData()->GetFPSState();
|
|
}
|
|
}
|
|
|
|
pPlayerPed->GetMotionData()->SetCurrentFPSState(iState);
|
|
pPlayerPed->GetMotionData()->SetIsUsingStealthInFPS(pPlayerPed->GetMotionData()->GetUsingStealth());
|
|
|
|
if (pPlayerPed->GetMotionData()->GetPreviousFPSState() != iState)
|
|
{
|
|
// Reset flags when idle
|
|
if (pPlayerPed->GetMotionData()->GetIsFPSIdle())
|
|
{
|
|
// If we are changing to FPS_Idle, reset RNG state, unless we were forced to FPS_Idle by look behind.
|
|
if(!bFirstPersonLookBehind || !bStayInRNG)
|
|
{
|
|
m_bFiredGunInFPS = false;
|
|
m_bWasRunAndGunning = false;
|
|
m_bFiredDuringFPSSprint = false;
|
|
m_fRNGTimerFPS = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
m_fRNGTimerFPS = Min(m_fRNGTimerFPS, fTimeToStayInRunAndGunFPS*0.50f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Reset timers if switching third/first person
|
|
m_bFiredGunInFPS = false;
|
|
m_bWasRunAndGunning = false;
|
|
m_bFiredDuringFPSSprint = false;
|
|
m_bFiredInFPSFullAimState = false;
|
|
m_fRNGTimerFPS = 0.0f;
|
|
}
|
|
}
|
|
|
|
bool CPlayerInfo::IsFirstPersonModeSupportedForPed(const CPed& ped)
|
|
{
|
|
// Not for animals.
|
|
if (ped.GetPedType() == PEDTYPE_ANIMAL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Not for the sasquatch.
|
|
const CPedModelInfo* pModelInfo = ped.GetPedModelInfo();
|
|
if (pModelInfo && pModelInfo->GetModelNameHash() == MI_PED_ORLEANS.GetName().GetHash())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
bool CPlayerInfo::CanUseCover() const
|
|
{
|
|
return bCanUseCover && !m_pPlayerPed->GetHasJetpackEquipped() && !m_pPlayerPed->GetPlayerResetFlag(CPlayerResetFlags::PRF_DISABLE_CAN_USE_COVER) && !CPedType::IsAnimalType(m_pPlayerPed->GetPedType());
|
|
}
|
|
|
|
|
|
// Name : ProcessPreCamera
|
|
// Purpose : Processes before camera update.
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
bool CPlayerInfo::ProcessPreCamera()
|
|
{
|
|
if(m_pPlayerPed)
|
|
{
|
|
GetTargeting().ProcessPreCamera();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// PURPOSE: To reset some variable prior to script running
|
|
void CPlayerInfo::ResetPreScript()
|
|
{
|
|
// Reset the suppressed crimes for this player
|
|
m_Wanted.ResetSuppressedCrimes();
|
|
m_Wanted.ResetFlags();
|
|
m_Wanted.m_DisableCallPoliceThisFrame = false;
|
|
}
|
|
|
|
void CPlayerInfo::ProcessLightEffects()
|
|
{
|
|
#if LIGHT_EFFECTS_SUPPORT
|
|
//Only adjust the control light effects for the Local Player
|
|
if (m_pPlayerPed && m_pPlayerPed->IsLocalPlayer())
|
|
{
|
|
const u32 c_MaxFlashDuration = 1000;
|
|
const u32 c_MinFlashDuration = 300;
|
|
const u32 c_LevelFlashScale = 165;
|
|
|
|
// Even if control is disabled, still want to update light-effect color.
|
|
CControl& control = CControlMgr::GetPlayerMappingControl();
|
|
const bool gameWorldHidden = camInterface::IsFadedOut() || CLoadingScreens::AreActive();
|
|
if (gameWorldHidden &&
|
|
#if LIGHT_EFFECT_FLASH_FOR_LOW_HEALTH
|
|
control.IsCurrentLightEffect(&m_FlashingPlayerLightEffect, CControl::PLAYER_LIGHT_EFFECT) == false &&
|
|
#endif
|
|
control.IsCurrentLightEffect(&m_WantedLightEffect, CControl::CODE_LIGHT_EFFECT) == false)
|
|
{
|
|
// Do not change light effect color when world is not rendered, unless we are flashing for wanted level.
|
|
return;
|
|
}
|
|
|
|
{
|
|
#if LIGHT_EFFECT_FLASH_FOR_LOW_HEALTH
|
|
const float c_fBadlyHurtThreshold = m_pPlayerPed->GetInjuredHealthThreshold() + ((m_pPlayerPed->GetHurtHealthThreshold() - m_pPlayerPed->GetInjuredHealthThreshold()) * 0.50f);
|
|
if ( m_pPlayerPed->GetHealth() > c_fBadlyHurtThreshold &&
|
|
(m_pPlayerPed->GetPlayerWanted()->GetWantedLevel() != WANTED_CLEAN || CScriptHud::iFakeWantedLevel > 0) &&
|
|
!CPauseMenu::IsActive() &&
|
|
!camInterface::GetSwitchDirector().IsRendering() &&
|
|
!m_pPlayerPed->GetIsArrested() &&
|
|
!m_pPlayerPed->IsDead() )
|
|
#else
|
|
if ( (m_pPlayerPed->GetPlayerWanted()->GetWantedLevel() != WANTED_CLEAN || CScriptHud::iFakeWantedLevel > 0) &&
|
|
!CPauseMenu::IsActive() &&
|
|
!camInterface::GetSwitchDirector().IsRendering() &&
|
|
!m_pPlayerPed->GetIsArrested() &&
|
|
!m_pPlayerPed->IsDead() )
|
|
#endif
|
|
{
|
|
// TODO: un-hardcode values?
|
|
u32 uWantedLevel;
|
|
|
|
if(m_pPlayerPed->GetPlayerWanted()->GetWantedLevel() != WANTED_CLEAN)
|
|
{
|
|
uWantedLevel = (u32)m_pPlayerPed->GetPlayerWanted()->GetWantedLevel() - 1; // 0 to 4 inclusive
|
|
}
|
|
else
|
|
{
|
|
uWantedLevel = (u32)CScriptHud::iFakeWantedLevel - 1; // 0 to 4 inclusive
|
|
}
|
|
|
|
u32 uToggleTime = c_MaxFlashDuration - (uWantedLevel) * c_LevelFlashScale;
|
|
if (uToggleTime < c_MinFlashDuration)
|
|
{
|
|
uToggleTime = c_MinFlashDuration; // Avoid triggering seizure. (5Hz-70Hz, which is 14ms to 200ms, is bad, http://www.birket.com/technical-library/144)
|
|
}
|
|
|
|
if(control.IsCurrentLightEffect(&m_WantedLightEffect, CControl::CODE_LIGHT_EFFECT) == false || uToggleTime != m_WantedLightEffect.GetDuration())
|
|
{
|
|
m_WantedLightEffect = fwWantedLightEffect(uToggleTime);
|
|
control.SetLightEffect(&m_WantedLightEffect, CControl::CODE_LIGHT_EFFECT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
control.ClearLightEffect(&m_WantedLightEffect, CControl::CODE_LIGHT_EFFECT);
|
|
|
|
// Note: cannot turn off the light bar.
|
|
Color32 oPlayerColor(160, 160, 160, 255); // arbitrary default
|
|
bool bColorSet = false;
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (NetworkInterface::GetLocalPlayer())
|
|
{
|
|
int sPlayerTeam = NetworkInterface::GetLocalPlayer()->GetTeam();
|
|
if (sPlayerTeam != INVALID_TEAM)
|
|
{
|
|
NetworkColours::NetworkColour eTeamColour = NetworkColours::GetTeamColour(sPlayerTeam);
|
|
if (eTeamColour != NetworkColours::INVALID_COLOUR)
|
|
{
|
|
oPlayerColor = (eTeamColour == NetworkColours::NETWORK_COLOUR_CUSTOM) ?
|
|
NetworkColours::GetCustomTeamColour(sPlayerTeam) :
|
|
NetworkColours::GetNetworkColour(eTeamColour);
|
|
bColorSet = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const rlClanDesc& clan = NetworkInterface::GetLocalPlayer()->GetClanDesc();
|
|
if(clan.IsValid())
|
|
{
|
|
oPlayerColor.Set(clan.m_clanColor);
|
|
bColorSet = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (m_pPlayerPed->GetPedType())
|
|
{
|
|
// TODO: for now hardcode, need to read from timecycle modifier eventually.
|
|
case PEDTYPE_PLAYER_0: oPlayerColor = CHudColour::GetRGBA(HUD_COLOUR_CONTROLLER_MICHAEL); bColorSet = true; break; // Michael light cyan
|
|
case PEDTYPE_PLAYER_1: oPlayerColor = CHudColour::GetRGBA(HUD_COLOUR_CONTROLLER_FRANKLIN); bColorSet = true; break; // Franklin light green
|
|
case PEDTYPE_PLAYER_2: oPlayerColor = CHudColour::GetRGBA(HUD_COLOUR_CONTROLLER_TREVOR); bColorSet = true; break; // Trevor light orange
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
#if LIGHT_EFFECT_FLASH_FOR_LOW_HEALTH
|
|
const float c_fHurtThreshold = m_pPlayerPed->GetHurtHealthThreshold();
|
|
if (bColorSet && m_pPlayerPed->GetHealth() < c_fHurtThreshold && !m_pPlayerPed->IsDead())
|
|
{
|
|
u32 uToggleTime = c_MinFlashDuration;
|
|
if (m_pPlayerPed->GetPlayerWanted()->GetWantedLevel() == WANTED_CLEAN)
|
|
{
|
|
// TODO: un-hardcode values?
|
|
float fHealthRatio = Min(5.0f * m_pPlayerPed->GetHealth() / c_fHurtThreshold, 4.99f);
|
|
u32 uHealthLevel = (u32)fHealthRatio; // 0 to 4 inclusive
|
|
u32 uToggleTime = c_MaxFlashDuration - (uHealthLevel) * c_LevelFlashScale;
|
|
if (uToggleTime < c_MinFlashDuration)
|
|
{
|
|
uToggleTime = c_MinFlashDuration; // Avoid triggering seizure. (5Hz-70Hz, which is 14ms to 200ms, is bad, http://www.birket.com/technical-library/144)
|
|
}
|
|
}
|
|
|
|
if( control.IsCurrentLightEffect(&m_FlashingPlayerLightEffect, CControl::PLAYER_LIGHT_EFFECT) == false ||
|
|
uToggleTime != m_FlashingPlayerLightEffect.GetDuration() ||
|
|
oPlayerColor.GetRed() != m_FlashingPlayerLightEffect.GetRed() ||
|
|
oPlayerColor.GetGreen() != m_FlashingPlayerLightEffect.GetGreen() ||
|
|
oPlayerColor.GetBlue() != m_FlashingPlayerLightEffect.GetBlue() )
|
|
{
|
|
m_FlashingPlayerLightEffect = ioFlashingLightEffect(uToggleTime, 0.40f, oPlayerColor);
|
|
control.SetLightEffect(&m_FlashingPlayerLightEffect, CControl::PLAYER_LIGHT_EFFECT);
|
|
}
|
|
}
|
|
else
|
|
#endif // LIGHT_EFFECT_FLASH_FOR_LOW_HEALTH
|
|
{
|
|
if (bColorSet)
|
|
{
|
|
if( control.IsCurrentLightEffect(&m_PlayerLightEffect, CControl::PLAYER_LIGHT_EFFECT) == false ||
|
|
oPlayerColor.GetRed() != m_PlayerLightEffect.GetRed() ||
|
|
oPlayerColor.GetGreen() != m_PlayerLightEffect.GetGreen() ||
|
|
oPlayerColor.GetBlue() != m_PlayerLightEffect.GetBlue() )
|
|
{
|
|
m_PlayerLightEffect = ioConstantLightEffect(oPlayerColor);
|
|
control.SetLightEffect(&m_PlayerLightEffect, CControl::PLAYER_LIGHT_EFFECT);
|
|
}
|
|
}
|
|
//Dont clear previous colour: B*1763685 - during transition's if the player is NULL ( m_pPlayerPed )
|
|
// it will flash the controller. So not clearing the color will maintain it to the Crew/Team colour.
|
|
//else
|
|
//{
|
|
// control.ResetLightDeviceColor();
|
|
//}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // LIGHT_EFFECTS_SUPPORT
|
|
}
|
|
|
|
// PURPOSE: Used to dampen the vertical motion on the local root (to stop bobbing when the camera is in close)
|
|
void CPlayerInfo::ProcessDampenRoot()
|
|
{
|
|
TUNE_GROUP_BOOL(DAMPEN_ROOT, bOverrideDampenRootTargetWeight, false);
|
|
TUNE_GROUP_FLOAT ( DAMPEN_ROOT, fOverridenDampenRootTargetWeight, 1.0f, 0.0f, 1.0f, 0.001f);
|
|
if(bOverrideDampenRootTargetWeight)
|
|
{
|
|
m_fDampenRootTargetWeight = fOverridenDampenRootTargetWeight;
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(DAMPEN_ROOT, bOverrideDampenRootTargetHeight, false);
|
|
TUNE_GROUP_FLOAT ( DAMPEN_ROOT, fOverridenDampenRootTargetHeight, 0.0f, -1.0f, 1.0f, 0.001f);
|
|
if(bOverrideDampenRootTargetHeight)
|
|
{
|
|
m_fDampenRootTargetHeight = fOverridenDampenRootTargetHeight;
|
|
}
|
|
|
|
if(GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_CAMERA_VIEW_MODE_SWITCHED_TO_OR_FROM_FIRST_PERSON))
|
|
{
|
|
// Snap
|
|
m_fDampenRootWeight = m_fDampenRootTargetWeight;
|
|
}
|
|
else
|
|
{
|
|
if (m_fDampenRootWeight <= m_fDampenRootTargetWeight)
|
|
{
|
|
m_fDampenRootWeight += ms_fDampenRootRate*fwTimer::GetTimeStep();
|
|
m_fDampenRootWeight = MIN(m_fDampenRootWeight, m_fDampenRootTargetWeight);
|
|
}
|
|
else
|
|
{
|
|
m_fDampenRootWeight -= ms_fDampenRootRate*fwTimer::GetTimeStep();
|
|
m_fDampenRootWeight = MAX(m_fDampenRootWeight, m_fDampenRootTargetWeight);
|
|
}
|
|
}
|
|
|
|
if (m_fDampenRootWeight > 0.0f)
|
|
{
|
|
CPed* pPlayerPed = GetPlayerPed();
|
|
if (pPlayerPed)
|
|
{
|
|
float fDampenedRootZ = Lerp(m_fDampenRootWeight, RC_MATRIX34(pPlayerPed->GetSkeleton()->GetLocalMtx(0)).d.z, m_fDampenRootTargetHeight);
|
|
RC_MATRIX34(pPlayerPed->GetSkeleton()->GetLocalMtx(0)).d.z = fDampenedRootZ;
|
|
RC_MATRIX34(pPlayerPed->GetSkeleton()->GetObjectMtx(0)).d.z = fDampenedRootZ;
|
|
pPlayerPed->GetSkeleton()->Update();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CPlayerInfo::FindClosestCarCB(CEntity* pEntity, void* data)
|
|
{
|
|
// NB : Return 'true' to continue enumerating vehicles from CGameWorld::ForAllEntitiesIntersecting()..
|
|
if (!Verifyf(pEntity->GetIsTypeVehicle(), "Entity %s is not of type vehicle", pEntity->GetModelName()))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#if __ASSERT
|
|
if (!Verifyf(static_cast<CVehicle*>(pEntity)->GetLayoutInfo(), "Vehicle %s has no layout info, are your vehicles.meta and vehiclelayouts.meta out of sync?", static_cast<CVehicle*>(pEntity)->GetDebugName()))
|
|
{
|
|
return true;
|
|
}
|
|
#endif // __ASSERT
|
|
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pEntity);
|
|
if (!pVehicle->IsCollisionEnabled())
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "disabled collision");
|
|
return true;
|
|
}
|
|
|
|
if (!pVehicle->m_nVehicleFlags.bConsideredByPlayer)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "vehicle isn't considered by player");
|
|
return true;
|
|
}
|
|
|
|
FindClosestCarCBData* pCBData = static_cast<FindClosestCarCBData*>(data);
|
|
const bool bStuckInWater = IsVehicleStuckInWater(pVehicle);
|
|
if (bStuckInWater)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "stuck in water");
|
|
return true;
|
|
}
|
|
|
|
if (!pVehicle->GetVehicleDamage()->GetIsDriveable(true, true, true))
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "vehicle undriveable");
|
|
return true;
|
|
}
|
|
|
|
const CPed* pPed = pCBData->pPed;
|
|
const bool bCanBoardVehicleWhenStoodOnTop = CanBoardVehicleWhenStoodOnTop(pVehicle);
|
|
const bool bDisableVehicleEntryDueToLocks = (bCanBoardVehicleWhenStoodOnTop && !pVehicle->CanPedOpenLocks(pPed)); // Condition seems a bit odd, originally this was for boats only
|
|
|
|
if (bDisableVehicleEntryDueToLocks)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "ped can't open locks");
|
|
return true;
|
|
}
|
|
|
|
if (ShouldDisableEntryForTrain(pVehicle, pPed))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (ShouldDisableEntryForPlane(pVehicle, pPed))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (ShouldDisableEntryForSyncedScene(pVehicle))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (ShouldDisableEntryBecauseUpsideDown(pVehicle))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool bPassedOriginalHeightCondition = false;
|
|
const Vector3 vPedPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
const bool bPedIsStandingOnThisVehicle = pPed->GetGroundPhysical() == pVehicle;
|
|
if (ShouldDisableEntryDueToHeightCondition(bPassedOriginalHeightCondition, bPedIsStandingOnThisVehicle, bCanBoardVehicleWhenStoodOnTop, vPedPos, pPed, pVehicle))
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "height condition failed");
|
|
return true;
|
|
}
|
|
|
|
// B*1649000
|
|
if (pPed->IsLocalPlayer() && pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PreventUsingLowerPrioritySeats))
|
|
{
|
|
aiAssertf(0, "Player shouldn't have this flag set ever");
|
|
pCBData->pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_PreventUsingLowerPrioritySeats, false);
|
|
}
|
|
|
|
int iSeatIndex = -1;
|
|
Vector3 vSeatPos;
|
|
if (!CCarEnterExit::GetNearestCarSeat(*pPed,*pVehicle,vSeatPos,iSeatIndex))
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "couldn't find nearest seat");
|
|
return true;
|
|
}
|
|
|
|
// If we didn't pass the original height condition, we must check that the seat we're potentially entering
|
|
// should actually have used the altered height condition and reject it if it shouldn't have
|
|
if (!bPassedOriginalHeightCondition && pVehicle->IsSeatIndexValid(iSeatIndex))
|
|
{
|
|
const s32 iEntryIndex = pVehicle->GetDirectEntryPointIndexForSeat(iSeatIndex);
|
|
if (!CTaskVehicleFSM::CanPedWarpIntoHoveringVehicle(pCBData->pPed, *pVehicle, iEntryIndex))
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "couldn't warp into hovering vehicle");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Vector3 vDiff = vPedPos - vSeatPos;
|
|
vDiff.z = 0;
|
|
|
|
// Don't consider vehicles going too fast away from us unless we are standing on top of a vehicle we can board
|
|
if (!bPedIsStandingOnThisVehicle || !bCanBoardVehicleWhenStoodOnTop)
|
|
{
|
|
if (ShouldDisableEntryBecauseVehicleMovingAway(vDiff, pPed, pVehicle))
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "vehicle moving away");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Test the distance to this vehicle
|
|
TUNE_GROUP_FLOAT(VEHICLE_ENTRY_TUNE, MAX_DISTANCE, 15.0f, 0.0f, 25.0f, 0.01f);
|
|
float Distance = vDiff.Mag();
|
|
float fDistanceLimit = MAX_DISTANCE;
|
|
|
|
// if this is a vehicle we can enter from standing on, reduce the distance by the bounding radius (cos some of them are massive)
|
|
if (bCanBoardVehicleWhenStoodOnTop)
|
|
{
|
|
fDistanceLimit += pEntity->GetBoundRadius();
|
|
}
|
|
|
|
if (Distance > fDistanceLimit)
|
|
{
|
|
CEntityBoundAI bound(*pEntity,vPedPos.z,pCBData->pPed->GetCapsuleInfo()->GetHalfWidth());
|
|
if (bound.LiesInside(vPedPos))
|
|
{
|
|
Distance = fDistanceLimit;
|
|
}
|
|
}
|
|
|
|
if (Distance > fDistanceLimit)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicle, "vehicle too far away");
|
|
return true;
|
|
}
|
|
|
|
// Now actually evaluate the position of this car
|
|
EvaluateCarPosition(pEntity, pCBData->pPed, vSeatPos, Distance, &pCBData->Closeness, &pCBData->pVehicle, pCBData->vSearchDir, pCBData->bStickInput);
|
|
return true;
|
|
}
|
|
|
|
bool CPlayerInfo::CanBoardVehicleWhenStoodOnTop(const CVehicle* pVehicle)
|
|
{
|
|
return pVehicle->InheritsFromBoat() || pVehicle->InheritsFromSubmarine() || pVehicle->GetLayoutInfo()->GetWarpInWhenStoodOnTop();
|
|
}
|
|
|
|
bool CPlayerInfo::ShouldDisableEntryForTrain(const CVehicle* pVehicle, const CPed* pPed)
|
|
{
|
|
if (pVehicle->GetVehicleType() != VEHICLE_TYPE_TRAIN)
|
|
return false;
|
|
|
|
// The script deals with entering cable cars
|
|
if (pVehicle->GetModelIndex() == MI_CAR_CABLECAR)
|
|
{
|
|
AI_LOG_REJECTION_WITH_REASON(pVehicle, "is a cablecar");
|
|
return true;
|
|
}
|
|
|
|
// Stop the player entering trains from the wrong side
|
|
const CTrain* pTrain = static_cast<const CTrain*>(pVehicle);
|
|
const Vector3 vTrainToPlayer = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition() - pTrain->GetTransform().GetPosition());
|
|
if (pTrain->m_nTrainFlags.iStationPlatformSides & CTrainTrack::Right)
|
|
{
|
|
if (DotProduct(vTrainToPlayer, VEC3V_TO_VECTOR3(pTrain->GetTransform().GetA())) < 0.0f)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ShouldDisableEntryForTrain]",pVehicle, "on right side platform, train wrong way");
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (DotProduct(vTrainToPlayer, VEC3V_TO_VECTOR3(pTrain->GetTransform().GetA())) > 0.0f)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ShouldDisableEntryForTrain]",pVehicle, "on left side platform, train wrong way");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::ShouldDisableEntryForPlane(const CVehicle* pVehicle, const CPed* pPed)
|
|
{
|
|
if (pVehicle->GetVehicleType() != VEHICLE_TYPE_PLANE)
|
|
return false;
|
|
|
|
// Don't try and enter planes with door above wing when landing gears are up (climb animation forces them through ground). Player can still walk on the wing and enter from there.
|
|
const CPlane* pPlane = static_cast<const CPlane*>(pVehicle);
|
|
bool bLandingGearUp = pPlane->GetLandingGear().GetPublicState() != CLandingGear::STATE_LOCKED_DOWN;
|
|
bool bStandingOnPlane = pPed->GetGroundPhysical() == pPlane;
|
|
bool bDoorAboveWing = pPlane->GetModelIndex() == MI_PLANE_CUBAN.GetModelIndex() ||
|
|
pPlane->GetModelIndex() == MI_PLANE_VELUM.GetModelIndex();
|
|
|
|
if (bLandingGearUp && bDoorAboveWing && !bStandingOnPlane)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ShouldDisableEntryForPlane]", pVehicle, "landing gear up, but ped on ground");
|
|
return true;
|
|
}
|
|
|
|
// Plane vehicles can't be boarded when hovering, unless we are already on top
|
|
// of the vehicle
|
|
if (pVehicle->IsInAir() && pPed->GetGroundPhysical() != pVehicle)
|
|
{
|
|
// If there is a height difference bigger than half a meter from our position
|
|
// to the entry point of the driver's seat, discard the vehicle
|
|
s32 iDriverEntryPoint = pVehicle->GetDirectEntryPointIndexForSeat(0);
|
|
if (pVehicle->IsEntryIndexValid(iDriverEntryPoint))
|
|
{
|
|
// Get the drivers entry point position
|
|
Vector3 vEntryPosition; Quaternion qEntryOrientation;
|
|
CModelSeatInfo::CalculateEntrySituation(pVehicle, pPed, vEntryPosition, qEntryOrientation, iDriverEntryPoint);
|
|
|
|
// Set the max tolerable height to enter the vehicle
|
|
const float fMaxTolerableEntryHeight = 0.5f;
|
|
const Vector3 vPedPosition = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
// If the vehicle is too high in the air discard it
|
|
if (abs(vPedPosition.z - vEntryPosition.z) > fMaxTolerableEntryHeight)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ShouldDisableEntryForPlane]",pVehicle, "plane hovering and ped is on ground");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::ShouldDisableEntryForSyncedScene(const CVehicle* pVehicle)
|
|
{
|
|
// if the vehicle is running the synchronized scene task we shouldn't be trying to get into it.
|
|
if (pVehicle->GetIntelligence() && pVehicle->GetIntelligence()->GetTaskManager())
|
|
{
|
|
const CTaskSynchronizedScene *pTask = static_cast<const CTaskSynchronizedScene*>(pVehicle->GetIntelligence()->GetTaskManager()->FindTaskByTypeActive(VEHICLE_TASK_TREE_SECONDARY, CTaskTypes::TASK_SYNCHRONIZED_SCENE));
|
|
if (pTask)
|
|
{
|
|
// unless we really should be trying to get into it.
|
|
if (!pTask->IsSceneFlagSet(SYNCED_SCENE_VEHICLE_ALLOW_PLAYER_ENTRY))
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ShouldDisableEntryForSyncedScene]",pVehicle, "playing synced scene, entry disallowed");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::ShouldDisableEntryBecauseUpsideDown(const CVehicle* pVehicle)
|
|
{
|
|
// Make sure the vehicle is not upside down
|
|
if (pVehicle->InheritsFromBike())
|
|
{
|
|
// We don't want to do all these checks for bike vehicles meant for missions
|
|
// since we always want to allow to get in those
|
|
if (!pVehicle->PopTypeIsMission())
|
|
{
|
|
// How much can we incline the front of the motorbike? 90 would mean that the bike
|
|
// is in vertical position
|
|
TUNE_GROUP_FLOAT( VEHICLE_ENTRY_TUNE, fBikeFrontDegreesAllowance, 55.0f, 0.0f, 90.0f, 0.1f );
|
|
// How much can the bike be turned on the side? a value of 0.0f indicates that the bike
|
|
// will only be allowed to go as much as flat. -90 would allow the bike completely upside down
|
|
// (imagine the handles touching the ground and the wheels in the air)
|
|
TUNE_GROUP_FLOAT( VEHICLE_ENTRY_TUNE, fBikeUpDegreesAllowance, -45.0f, -90.0f, 0.0f, 0.1f );
|
|
const float fDotBetweenBikeFrontAndWorldUp = pVehicle->GetTransform().GetB().GetZf();
|
|
const float fDotBetweenBikeUpAndWorldUp = pVehicle->GetTransform().GetC().GetZf();
|
|
bool bVehicleIsUpsideDown = fDotBetweenBikeFrontAndWorldUp > cos( abs(HALF_PI - (fBikeFrontDegreesAllowance * DtoR)) ) ||
|
|
fDotBetweenBikeUpAndWorldUp < cos( (fBikeUpDegreesAllowance - 90.0f) * DtoR );
|
|
|
|
if (bVehicleIsUpsideDown)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ShouldDisableEntryBecauseUpsideDown]",pVehicle, "bike upside down, entry disallowed");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// For cars we only care about the up vector
|
|
const float fDotBetweenVehicleUpAndWorldUp = pVehicle->GetTransform().GetC().GetZf();
|
|
bool bVehicleIsUpsideDown = fDotBetweenVehicleUpAndWorldUp < 0.3f;
|
|
if (bVehicleIsUpsideDown)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ShouldDisableEntryBecauseUpsideDown]",pVehicle, "vehicle upside down, entry disallowed");
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::ShouldDisableEntryDueToHeightCondition(bool& bPassedOriginalHeightCondition, bool bPedIsStandingOnThisVehicle, bool bCanBoardVehicleWhenStoodOnTop, const Vector3& vPedPos, const CPed* pPed, const CVehicle* pVehicle)
|
|
{
|
|
// Make sure we're in a certain z-range (don't want cars on bridges etc)
|
|
Vector3 vecBoundCentre = pVehicle->GetBoundCentre();
|
|
vecBoundCentre = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
|
|
vecBoundCentre.z = vecBoundCentre.z - pVehicle->GetHeightAboveRoad() + 1.0f;
|
|
|
|
Vector3 vDiff = vPedPos - vecBoundCentre;
|
|
|
|
float fHeightDiff=DotProduct(vDiff, Vector3(0,0,1.0f));
|
|
|
|
TUNE_GROUP_FLOAT(VEHICLE_ENTRY_TUNE, MAX_HEIGHT_DIFF, 3.0f, 0.0f, 5.0f, 0.01f);
|
|
bPassedOriginalHeightCondition = rage::Abs(fHeightDiff)<MAX_HEIGHT_DIFF || (bPedIsStandingOnThisVehicle && bCanBoardVehicleWhenStoodOnTop);
|
|
|
|
// Increase the z tolerance for heli's as we may potentially enter the rear seats via a warp
|
|
bool bPassedAlteredHeightCondition = false;
|
|
if (CTaskVehicleFSM::CanPedWarpIntoHoveringVehicle(pPed, *pVehicle))
|
|
{
|
|
bPassedAlteredHeightCondition = rage::Abs(fHeightDiff)<CTaskVehicleFSM::ms_Tunables.m_MaxHoverHeightDistToWarpIntoHeli;
|
|
}
|
|
|
|
static bool bAllowBoatBoardingFromLand = true; // JB: enabled this for url:bugstar:203053
|
|
const bool bPassedHeightCondition = ( (bPassedOriginalHeightCondition || bPassedAlteredHeightCondition) && !bCanBoardVehicleWhenStoodOnTop ) ||
|
|
( bCanBoardVehicleWhenStoodOnTop && ( bPedIsStandingOnThisVehicle || pPed->GetIsInWater() || bAllowBoatBoardingFromLand ) );
|
|
return !bPassedHeightCondition;
|
|
}
|
|
|
|
bool CPlayerInfo::ShouldDisableEntryBecauseVehicleMovingAway(Vector3 vPedVehDiff, const CPed* pPed, const CVehicle* pVehicle)
|
|
{
|
|
static const float COS15 = 0.96593f;
|
|
static const float VehicleVelLimit = rage::square(7.0f); // Roughly max sprint speed
|
|
Vector3 VehicleVel = pVehicle->GetRelativeVelocity(*pPed);
|
|
if (VehicleVel.Mag2() > VehicleVelLimit)
|
|
{
|
|
VehicleVel.z = 0.f;
|
|
VehicleVel.Normalize();
|
|
vPedVehDiff.Normalize();
|
|
if (vPedVehDiff.Dot(VehicleVel) < COS15)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::IsVehicleStuckInWater(const CVehicle* pVehicle)
|
|
{
|
|
bool isSeaPlane = pVehicle->pHandling && pVehicle->pHandling->GetSeaPlaneHandlingData();
|
|
|
|
return pVehicle->m_nVehicleFlags.bIsDrowning
|
|
|| (pVehicle->InheritsFromAutomobile() && !pVehicle->InheritsFromAmphibiousAutomobile() && !pVehicle->InheritsFromSubmarineCar() && !isSeaPlane && pVehicle->HasContactWheels()==false && pVehicle->GetIsInWater())
|
|
|| (pVehicle->InheritsFromBike() && pVehicle->GetTransform().GetC().GetZf() > 0.707f && !pVehicle->HasContactWheels() && pVehicle->GetIsInWater());
|
|
}
|
|
|
|
bool CPlayerInfo::FindClosestRidableAnimalCB(CEntity* pEntity, void* data)
|
|
{
|
|
Assert(pEntity);
|
|
Assert(data);
|
|
Assert(pEntity->GetIsTypePed());
|
|
|
|
float Distance;
|
|
FindClosestRidableAnimalCBData* pCBData = static_cast<FindClosestRidableAnimalCBData*>(data);
|
|
|
|
if (pEntity->GetIsTypePed())
|
|
{
|
|
// Make sure I am ridable (for now just using IsQuadruped and has a vehicle layout until we have species checks)
|
|
CPed* pPed = static_cast<CPed*>(pEntity);
|
|
if ((pPed->GetHealth() > 0.f) && pPed->GetCapsuleInfo()->IsQuadruped() && pPed->GetPedModelInfo()->GetLayoutInfo()) {
|
|
|
|
//Ignore mounted animals mounted by friendlies
|
|
if (pPed->GetSeatManager()->GetDriver())
|
|
{
|
|
if (pCBData->pPed->GetPedIntelligence()->IsFriendlyWith(*pPed->GetSeatManager()->GetDriver()))
|
|
return true;
|
|
}
|
|
|
|
//no ragdolling or recovering animals
|
|
if (pPed->GetRagdollState() > RAGDOLL_STATE_ANIM || pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_NM_CONTROL) != NULL)
|
|
return true;
|
|
|
|
// Make sure we're in a certain z-range (don't want animals on bridges etc)
|
|
Vector3 vecBoundCentre = pEntity->GetBoundCentre();
|
|
const Vector3 vCBPedPos = VEC3V_TO_VECTOR3(pCBData->pPed->GetTransform().GetPosition());
|
|
Vector3 vDiff = vCBPedPos - vecBoundCentre;
|
|
|
|
float fHeightDiff=DotProduct(vDiff, Vector3(0,0,1.0f));
|
|
|
|
if( pCBData->bDepthCheck || rage::Abs(fHeightDiff)<3.0f )
|
|
{
|
|
// Test the distance to this animal
|
|
vDiff.z=0;
|
|
Distance=vDiff.Mag();
|
|
float fDistanceLimit = ms_Tunables.m_ScanNearbyMountsDistance;
|
|
if(Distance <= fDistanceLimit)
|
|
{
|
|
// Now actually evaluate the position of this mount
|
|
EvaluateAnimalPosition(pEntity, pCBData->pPed, Distance, &pCBData->Closeness, &pCBData->pAnimal, pCBData->vSearchDir, pCBData->bStickInput);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// NB : Return 'true' to continue enumerating vehicles from CGameWorld::ForAllEntitiesIntersecting()..
|
|
return true;
|
|
}
|
|
|
|
// FindClosestTrainCB is only there so that we can quickly find out whether we are near a train.
|
|
// using FindClosestCarCB takes up to 20 msecs.
|
|
// This is only used by a script that prints 'Press 'Y' to enter train.
|
|
// I guess it should be removed at some point (Obbe, March 2008)
|
|
bool CPlayerInfo::FindClosestTrainCB(CEntity* pEntity, void* data)
|
|
{
|
|
Assert(pEntity);
|
|
Assert(data);
|
|
Assert(pEntity->GetIsTypeVehicle());
|
|
|
|
FindClosestCarCBData* pCBData = static_cast<FindClosestCarCBData*>(data);
|
|
|
|
if (pEntity->IsCollisionEnabled() && pEntity->GetIsTypeVehicle() )
|
|
{
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pEntity);
|
|
bool bDisableCarEntry = false;
|
|
|
|
// Stop the player entering trains from the wrong side
|
|
if( pVehicle->GetVehicleType() == VEHICLE_TYPE_TRAIN )
|
|
{
|
|
CTrain* pTrain = static_cast<CTrain*>(pEntity);
|
|
Vector3 vTrainToPlayer = VEC3V_TO_VECTOR3(pCBData->pPed->GetTransform().GetPosition() - pTrain->GetTransform().GetPosition());
|
|
if( pTrain->m_nTrainFlags.iStationPlatformSides & CTrainTrack::Right )
|
|
{
|
|
if( DotProduct(vTrainToPlayer, VEC3V_TO_VECTOR3(pTrain->GetTransform().GetA())) < 0.0f )
|
|
{
|
|
bDisableCarEntry = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( DotProduct(vTrainToPlayer, VEC3V_TO_VECTOR3(pTrain->GetTransform().GetA())) > 0.0f )
|
|
{
|
|
bDisableCarEntry = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (!bDisableCarEntry) // && pEntity->GetStatus() != STATUS_TRAIN_MOVING) // Wrecked cars are of no interest to us
|
|
{
|
|
// Make sure this car isn't upside down
|
|
// if(pEntity->GetC().z > 0.3f || (pVehicle->GetBaseVehicleType()==VEHICLE_TYPE_BIKE && pEntity->GetC().z > -0.5f) )
|
|
{
|
|
const Vector3 vEntityPos = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
|
|
|
|
// Make sure we're in a certain z-range (don't want cars on bridges etc)
|
|
Vector3 vecBoundCentre = pEntity->GetBoundCentre();
|
|
|
|
vecBoundCentre = vEntityPos;
|
|
vecBoundCentre.z = vecBoundCentre.z - pVehicle->GetHeightAboveRoad() + 1.0f;
|
|
|
|
const Vector3 vCBPedPos = VEC3V_TO_VECTOR3(pCBData->pPed->GetTransform().GetPosition());
|
|
Vector3 vDiff;
|
|
vDiff = vCBPedPos - vecBoundCentre;
|
|
|
|
float fHeightDiff=DotProduct(vDiff, Vector3(0,0,1.0f));
|
|
|
|
|
|
if(rage::Abs(fHeightDiff)<2.0f)
|
|
{
|
|
if (((CVehicle *)pEntity)->m_nVehicleFlags.bConsideredByPlayer)
|
|
{
|
|
CBaseModelInfo *pModelInfo = pEntity->GetBaseModelInfo();
|
|
float dist = 0.0f;
|
|
float distX = rage::Abs(DotProduct( (vCBPedPos - vEntityPos), VEC3V_TO_VECTOR3(pEntity->GetTransform().GetA())));
|
|
dist += rage::Max(0.0f, distX - pModelInfo->GetBoundingBoxMax().x);
|
|
float distY = rage::Abs(DotProduct( (vCBPedPos - vEntityPos), VEC3V_TO_VECTOR3(pEntity->GetTransform().GetB())));
|
|
dist += rage::Max(0.0f, distY - pModelInfo->GetBoundingBoxMax().y);
|
|
float distZ = rage::Abs(DotProduct( (vCBPedPos - vEntityPos), VEC3V_TO_VECTOR3(pEntity->GetTransform().GetC())));
|
|
dist += rage::Max(0.0f, distZ - pModelInfo->GetBoundingBoxMax().z);
|
|
|
|
if(dist <= SCANNEARBYVEHICLES)
|
|
{
|
|
// Now actually evaluate the position of this car
|
|
Vector3 vSeatPos;
|
|
// eHierarchyId iSeat=(eHierarchyId)0;
|
|
|
|
// if(CCarEnterExit::GetNearestCarSeat(*(pCBData->pPed),*pVehicle,vSeatPos,iSeat))
|
|
{
|
|
vSeatPos = vEntityPos;
|
|
EvaluateCarPosition(pEntity, pCBData->pPed, vSeatPos, dist, &pCBData->Closeness, &pCBData->pVehicle, pCBData->vSearchDir, pCBData->bStickInput);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// NB : Return 'true' to continue enumerating vehicles from CGameWorld::ForAllEntitiesIntersecting()..
|
|
return true;
|
|
}
|
|
|
|
|
|
CVehicle *
|
|
CPlayerInfo::ScanForVehicleToEnter(CPed * pPlayerPed, const Vector3& vCarSearchDirection, const bool bUsingStickInput )
|
|
{
|
|
// Scripts can now specify a car which overrides the vehicle selection.
|
|
CPlayerInfo * pInfo = pPlayerPed->GetPlayerInfo();
|
|
|
|
if(pInfo->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_NOT_ALLOWED_TO_ENTER_ANY_CAR))
|
|
{
|
|
AI_FUNCTION_LOG("not allowed to enter any car");
|
|
return NULL;
|
|
}
|
|
|
|
if(pInfo->m_pOnlyEnterThisVehicle)
|
|
{
|
|
Vec3V vVehTestPos = pInfo->m_pOnlyEnterThisVehicle->GetVehiclePosition();
|
|
|
|
Vector3 vAwayFromVehicle = VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition() - vVehTestPos);
|
|
float fDist = vAwayFromVehicle.Mag();
|
|
|
|
if (fDist > CPlayerInfo::SCANNEARBYVEHICLES*1.5f)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]",pInfo->m_pOnlyEnterThisVehicle, "ped can only enter this vehicle which is too far away");
|
|
return NULL;
|
|
}
|
|
else if(pInfo->m_pOnlyEnterThisVehicle->CanBeDriven())
|
|
{
|
|
AI_LOG_ACCEPTANCE_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pInfo->m_pOnlyEnterThisVehicle, "ped can only enter this vehicle which can be driven");
|
|
return pInfo->m_pOnlyEnterThisVehicle;
|
|
}
|
|
else
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]",pInfo->m_pOnlyEnterThisVehicle, "ped can only enter this vehicle which can't be driven");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// If the player is already standing on a boat or plane, then get into this
|
|
if (pPlayerPed->GetGroundPhysical() && pPlayerPed->GetGroundPhysical()->GetIsTypeVehicle())
|
|
{
|
|
CVehicle* pVehicleStoodOn = static_cast<CVehicle*>(pPlayerPed->GetGroundPhysical());
|
|
if (CVehicleModelInfo::AllowsVehicleEntryWhenStoodOn(*pVehicleStoodOn) && pVehicleStoodOn->CanPedOpenLocks(pPlayerPed))
|
|
{
|
|
//! If vehicle is wrecked, don't consider this vehicle or anything else as we may struggle to get off it (e.g. when inside a boat).
|
|
if(pVehicleStoodOn->GetStatus() == STATUS_WRECKED)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicleStoodOn, "stood on wrecked vehicle");
|
|
return NULL;
|
|
}
|
|
|
|
if (pVehicleStoodOn->m_nVehicleFlags.bConsideredByPlayer)
|
|
{
|
|
TUNE_GROUP_FLOAT(TITAN_TUNE, MAX_DIST_FROM_OTHER_VEHICLE_TO_IGNORE_ENTRY, 3.0f, 0.0f, 10.0f, 0.01f);
|
|
// Is there a vehicle within MAX_DIST_FROM_OTHER_VEHICLE_TO_IGNORE_ENTRY meters of the player, prefer to get on that instead
|
|
bool bValidForEntry = true;
|
|
if (pVehicleStoodOn->GetModelIndex() == MI_PLANE_TITAN.GetModelIndex())
|
|
{
|
|
const Vec3V vPlayerPos = pPlayerPed->GetTransform().GetPosition();
|
|
CVehicleScanner& vehScanner = *pPlayerPed->GetPedIntelligence()->GetVehicleScanner();
|
|
CEntityScannerIterator entityList = vehScanner.GetIterator();
|
|
for (CEntity* pEnt = static_cast<CVehicle*>(entityList.GetFirst()); pEnt; pEnt = entityList.GetNext())
|
|
{
|
|
CVehicle* pNearbyVeh = static_cast<CVehicle*>(pEnt);
|
|
const float fDistSqd = MagSquared(pNearbyVeh->GetTransform().GetPosition() - vPlayerPos).Getf();
|
|
if (fDistSqd < square(MAX_DIST_FROM_OTHER_VEHICLE_TO_IGNORE_ENTRY))
|
|
{
|
|
bValidForEntry = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bValidForEntry)
|
|
{
|
|
AI_LOG_ACCEPTANCE_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pVehicleStoodOn, "stood on vehicle");
|
|
return pVehicleStoodOn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CVehicle *pTargetVehicle = NULL;
|
|
float CarCloseness = 0.0f;
|
|
|
|
spdAABB testBox;
|
|
Vector3 corner1(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()));
|
|
Vector3 corner2 = corner1;
|
|
|
|
corner1.x += SCANNEARBYVEHICLES;
|
|
corner1.y += SCANNEARBYVEHICLES;
|
|
corner2.x -= SCANNEARBYVEHICLES;
|
|
corner2.y -= SCANNEARBYVEHICLES;
|
|
|
|
testBox.Invalidate();
|
|
testBox.GrowPoint(RCC_VEC3V(corner1));
|
|
testBox.GrowPoint(RCC_VEC3V(corner2));
|
|
|
|
fwIsBoxIntersectingApprox searchBox(testBox);
|
|
|
|
FindClosestCarCBData callBackData = {pPlayerPed, pTargetVehicle, CarCloseness, vCarSearchDirection, bUsingStickInput};
|
|
CGameWorld::ForAllEntitiesIntersecting(&searchBox, FindClosestCarCB, static_cast<void*>(&callBackData),
|
|
ENTITY_TYPE_MASK_VEHICLE, (SEARCH_LOCATION_EXTERIORS|SEARCH_LOCATION_INTERIORS),
|
|
SEARCH_LODTYPE_HIGHDETAIL, SEARCH_OPTION_NONE, WORLDREP_SEARCHMODULE_PEDS);
|
|
/*false, true, false, false, false, false, false, true);*/ // intersecting cars only
|
|
|
|
pTargetVehicle = callBackData.pVehicle;
|
|
CarCloseness = callBackData.Closeness;
|
|
|
|
|
|
if (pTargetVehicle)
|
|
{
|
|
if(!pTargetVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_CANNOT_BE_DRIVEN_BY_PLAYER) && pTargetVehicle->CanBeDriven())
|
|
{
|
|
// DMKH. Do not allow player to get onto a bike that is overturned whilst handcuffed. -> This should probably be rejected in FindClosestCarCB?
|
|
if (pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsHandCuffed) && pTargetVehicle->InheritsFromBike() )
|
|
{
|
|
CBike *pBike = static_cast<CBike*>(pTargetVehicle);
|
|
if(!pBike->m_nBikeFlags.bOnSideStand)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pBike, "bike overturned and player is handcuffed");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
CVehicleModelInfo* pModelInfo = static_cast<CVehicleModelInfo*>(pTargetVehicle->GetBaseModelInfo());
|
|
const bool bIgnoreOnSideTest = pModelInfo && pModelInfo->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_IGNORE_ON_SIDE_CHECK);
|
|
if (pTargetVehicle->IsInEnterableState(bIgnoreOnSideTest)) // -> This should probably be rejected in FindClosestCarCB?
|
|
{
|
|
AI_LOG_ACCEPTANCE_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]", pTargetVehicle, "vehicle is enterable");
|
|
return pTargetVehicle;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CPed *
|
|
CPlayerInfo::ScanForAnimalToRide(CPed * pPlayerPed, const Vector3& vAnimalSearchDirection, const bool bUsingStickInput, const bool bDoDepthTest )
|
|
{
|
|
// Scripts can now specify an animal which overrides the selection.
|
|
CPlayerInfo * pInfo = pPlayerPed->GetPlayerInfo();
|
|
|
|
if(pInfo->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_NOT_ALLOWED_TO_ENTER_ANY_CAR)) //should apply for animals too?
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
CPed *pTargetAnimal = NULL;
|
|
float AnimalCloseness = 0.0f;
|
|
|
|
spdAABB testBox;
|
|
Vector3 corner1(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()));
|
|
Vector3 corner2 = corner1;
|
|
static dev_float sf_MountAnimDepthTestDistance = 5.0f;
|
|
if (bDoDepthTest)
|
|
corner2.z -= sf_MountAnimDepthTestDistance;
|
|
|
|
corner1.x += ms_Tunables.m_ScanNearbyMountsDistance;
|
|
corner1.y += ms_Tunables.m_ScanNearbyMountsDistance;
|
|
corner2.x -= ms_Tunables.m_ScanNearbyMountsDistance;
|
|
corner2.y -= ms_Tunables.m_ScanNearbyMountsDistance;
|
|
|
|
testBox.Invalidate();
|
|
testBox.GrowPoint(RCC_VEC3V(corner1));
|
|
testBox.GrowPoint(RCC_VEC3V(corner2));
|
|
|
|
fwIsBoxIntersectingApprox searchBox(testBox);
|
|
|
|
FindClosestRidableAnimalCBData callBackData = {pPlayerPed, pTargetAnimal, AnimalCloseness, vAnimalSearchDirection, bUsingStickInput, bDoDepthTest};
|
|
CGameWorld::ForAllEntitiesIntersecting(&searchBox, FindClosestRidableAnimalCB, static_cast<void*>(&callBackData),
|
|
ENTITY_TYPE_MASK_PED, (SEARCH_LOCATION_EXTERIORS|SEARCH_LOCATION_INTERIORS),
|
|
SEARCH_LODTYPE_HIGHDETAIL, SEARCH_OPTION_NONE, WORLDREP_SEARCHMODULE_PEDS);
|
|
|
|
pTargetAnimal = callBackData.pAnimal;
|
|
AnimalCloseness = callBackData.Closeness;
|
|
return pTargetAnimal;
|
|
}
|
|
|
|
CVehicle *
|
|
CPlayerInfo::ScanForTrainToEnter(CPed * pPlayerPed, const Vector3& vCarSearchDirection, const bool bUsingStickInput )
|
|
{
|
|
|
|
CVehicle *pTargetVehicle = NULL;
|
|
float CarCloseness = 0.0f;
|
|
|
|
CVehicle::Pool *VehiclePool = CVehicle::GetPool();
|
|
s32 i = (s32) VehiclePool->GetSize();
|
|
FindClosestCarCBData callBackData = {pPlayerPed, pTargetVehicle, CarCloseness, vCarSearchDirection, bUsingStickInput};
|
|
while(i--)
|
|
{
|
|
CVehicle *pVeh = VehiclePool->GetSlot(i);
|
|
if (pVeh && pVeh->InheritsFromTrain())
|
|
{
|
|
FindClosestTrainCB(pVeh, &callBackData);
|
|
}
|
|
}
|
|
pTargetVehicle = callBackData.pVehicle;
|
|
CarCloseness = callBackData.Closeness;
|
|
|
|
if (pTargetVehicle && pTargetVehicle->CanBeDriven())
|
|
{
|
|
return pTargetVehicle;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION : IsPlayerInRemoteMode
|
|
// PURPOSE : Returns true or false depending on whether teh player is controlling
|
|
// a remotely controlled car
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CPlayerInfo::IsPlayerInRemoteMode(void)
|
|
{
|
|
// if (GetRemoteVehicle()) return true;
|
|
// if (bAfterRemoteVehicleExplosion) return true;
|
|
return (false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPlayerInfo::ProcessPostMovement()
|
|
{
|
|
// Update the vehicle clip streaming request helper
|
|
// this searches nearby vehicles and tries to stream the clips
|
|
m_VehicleClipRequestHelper.SetPed(m_pPlayerPed);
|
|
m_VehicleClipRequestHelper.Update(fwTimer::GetTimeStep());
|
|
|
|
const CWeaponInfo* pEquippedWeaponInfo = m_pPlayerPed->GetWeaponManager() ? m_pPlayerPed->GetWeaponManager()->GetEquippedWeaponInfo() : NULL;
|
|
if( pEquippedWeaponInfo )
|
|
{
|
|
bool bChangingWeapons = false;
|
|
|
|
// Switch the cached weapon hash and force clip set functionality
|
|
if( pEquippedWeaponInfo->GetHash() != m_nMeleeEquippedWeaponHash )
|
|
{
|
|
m_nMeleeEquippedWeaponHash = pEquippedWeaponInfo->GetHash();
|
|
bChangingWeapons = true;
|
|
}
|
|
|
|
bool bRunningMelee = m_pPlayerPed->GetPedIntelligence() ? m_pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning( CTaskTypes::TASK_MELEE ) : false;
|
|
if( bRunningMelee )
|
|
{
|
|
// Turn off the melee clip set release timer
|
|
m_bEnableMeleeClipSetReleaseTimer = false;
|
|
|
|
// Melee variation streaming request
|
|
if( bChangingWeapons || !m_meleeVariationClipSetHelper.IsLoaded() )
|
|
{
|
|
m_meleeVariationClipSetHelper.Request( pEquippedWeaponInfo->GetMeleeVariationClipSetId( *m_pPlayerPed ) );
|
|
}
|
|
|
|
// Melee taunt streaming request
|
|
if( bChangingWeapons || !m_meleeTauntClipSetHelper.IsLoaded() )
|
|
{
|
|
m_meleeTauntClipSetHelper.Request( pEquippedWeaponInfo->GetMeleeTauntClipSetId( *m_pPlayerPed ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check to see if we should reset the clip set release timer?
|
|
if( !m_bEnableMeleeClipSetReleaseTimer )
|
|
{
|
|
m_bEnableMeleeClipSetReleaseTimer = true;
|
|
m_nMeleeClipSetReleaseTimer = fwTimer::GetTimeInMilliseconds() + ms_nMeleeClipSetReleaseDuration;
|
|
}
|
|
|
|
// Release the variation clip set if necessary
|
|
if( m_meleeVariationClipSetHelper.IsLoaded() && ( bChangingWeapons || m_nMeleeClipSetReleaseTimer < fwTimer::GetTimeInMilliseconds() ) )
|
|
{
|
|
m_meleeVariationClipSetHelper.Release();
|
|
}
|
|
|
|
// Release the taunt clip set if necessary
|
|
if( m_meleeTauntClipSetHelper.IsLoaded() && ( bChangingWeapons || m_nMeleeClipSetReleaseTimer < fwTimer::GetTimeInMilliseconds() ) )
|
|
{
|
|
m_meleeTauntClipSetHelper.Release();
|
|
}
|
|
}
|
|
}
|
|
// Do not hesitate to release the melee clip sets
|
|
else
|
|
{
|
|
m_meleeVariationClipSetHelper.Release();
|
|
m_meleeTauntClipSetHelper.Release();
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ProcessTestForShelter()
|
|
{
|
|
static const int iLineTestFreqMs = 10000;
|
|
static const float fDistAbovePlayer = 30.0f;
|
|
|
|
// Process retrieving the async probe result
|
|
if(m_hShelterLineTestHandle.GetWaitingOnResults())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(m_hShelterLineTestHandle.GetResultsReady())
|
|
{
|
|
m_bIsShelteredAbove = m_hShelterLineTestHandle.GetNumHits() > 0u;
|
|
m_hShelterLineTestHandle.Reset();
|
|
return;
|
|
}
|
|
|
|
// If we're in an interior then we can probably assume we're in shelter (can't we??)
|
|
if(m_pPlayerPed->m_nFlags.bInMloRoom)
|
|
{
|
|
m_iTimeTillNextShelterLineTest = iLineTestFreqMs;
|
|
m_bIsShelteredAbove = true;
|
|
return;
|
|
}
|
|
// If its not raining then don't bother with any of these linetests
|
|
if(!g_weather.IsRaining())
|
|
return;
|
|
|
|
m_iTimeTillNextShelterLineTest -= fwTimer::GetTimeStepInMilliseconds();
|
|
if(m_iTimeTillNextShelterLineTest <= 0)
|
|
{
|
|
m_iTimeTillNextShelterLineTest = iLineTestFreqMs;
|
|
Assert(m_hShelterLineTestHandle.GetResultsStatus() == WorldProbe::TEST_NOT_SUBMITTED);
|
|
|
|
const Vector3 vPlayerPos = VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetPosition());
|
|
WorldProbe::CShapeTestProbeDesc probeData;
|
|
probeData.SetResultsStructure(&m_hShelterLineTestHandle);
|
|
probeData.SetStartAndEnd(vPlayerPos,vPlayerPos + Vector3(0.0f,0.0f,fDistAbovePlayer));
|
|
probeData.SetContext(WorldProbe::ENotSpecified);
|
|
probeData.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
|
|
probeData.SetIsDirected(true);
|
|
WorldProbe::GetShapeTestManager()->SubmitTest(probeData, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ProcessWorldExtentsCheck()
|
|
{
|
|
if (HasPlayerLeftTheWorld())
|
|
{
|
|
HandlePlayerWorldBreach();
|
|
}
|
|
else
|
|
{
|
|
// Reset the escape timer as the player is back inside the gameplay zone.
|
|
m_fSwimBoundsEscapeTimer = ms_Tunables.m_MaxTimeToTrespassWhileSwimmingBeforeDeath;
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ProcessNumEnemiesInCombat()
|
|
{
|
|
m_iNumEnemiesInCombat = 0;
|
|
m_iNumEnemiesShootingInCombat = 0;
|
|
|
|
CCombatTaskManager* pCombatTaskManager = CCombatManager::GetCombatTaskManager();
|
|
if( pCombatTaskManager && m_pPlayerPed )
|
|
{
|
|
m_iNumEnemiesInCombat = pCombatTaskManager->CountPedsInCombatWithTarget(*m_pPlayerPed, BANK_ONLY(ms_bDisplayNumEnemiesInCombat ? CCombatTaskManager::OF_DebugRender :) 0);
|
|
fwFlags8 combatManagerFlags(CCombatTaskManager::OF_MustHaveClearLOS|CCombatTaskManager::OF_MustHaveGunWeaponEquipped);
|
|
|
|
#if __BANK
|
|
if(ms_bDisplayNumEnemiesShootingInCombat)
|
|
{
|
|
combatManagerFlags.SetFlag(CCombatTaskManager::OF_DebugRender);
|
|
}
|
|
#endif
|
|
|
|
m_iNumEnemiesShootingInCombat = pCombatTaskManager->CountPedsInCombatWithTarget(*m_pPlayerPed, combatManagerFlags);
|
|
}
|
|
#if __BANK
|
|
bool bDisplayNumEnemiesInCombat = (ms_bDisplayNumEnemiesInCombat && m_iNumEnemiesInCombat > 0);
|
|
bool bDisplayNumEnemiesShootingInCombat = (ms_bDisplayNumEnemiesShootingInCombat && m_iNumEnemiesShootingInCombat > 0);
|
|
if( bDisplayNumEnemiesInCombat || bDisplayNumEnemiesShootingInCombat )
|
|
{
|
|
int lineNum = 0;
|
|
const int DEBUG_STRING_SIZE = 128;
|
|
char debugString[DEBUG_STRING_SIZE];
|
|
const Vector3 vDebugPos = VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetPosition());
|
|
if( bDisplayNumEnemiesInCombat )
|
|
{
|
|
formatf(debugString, DEBUG_STRING_SIZE, "NumEnemiesInCombat: %d", m_iNumEnemiesInCombat);
|
|
grcDebugDraw::Text(vDebugPos, Color_red, 0, lineNum * grcDebugDraw::GetScreenSpaceTextHeight(), debugString );
|
|
lineNum++;
|
|
}
|
|
if( bDisplayNumEnemiesShootingInCombat )
|
|
{
|
|
formatf(debugString, DEBUG_STRING_SIZE, "NumEnemiesShootingInCombat: %d", m_iNumEnemiesShootingInCombat);
|
|
grcDebugDraw::Text(vDebugPos, Color_red, 0, lineNum * grcDebugDraw::GetScreenSpaceTextHeight(), debugString );
|
|
lineNum++;
|
|
}
|
|
}
|
|
#endif // __BANK
|
|
|
|
}
|
|
|
|
void CPlayerInfo::ProcessCoverStateTracking()
|
|
{
|
|
if( m_pPlayerPed )
|
|
{
|
|
// Get the player's running cover task, if any
|
|
CTaskInfo* pCoverTaskInfo = m_pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->FindTaskInfoIfCurrentlyRunning(CTaskTypes::TASK_IN_COVER);
|
|
if( pCoverTaskInfo )
|
|
{
|
|
// in cover, so increment the using cover counter
|
|
m_fTimeElapsedUsingCoverSeconds += fwTimer::GetTimeStep();
|
|
|
|
// Check if the current state is any that qualifies as NOT hiding (active in combat)
|
|
s32 currentCoverState = pCoverTaskInfo->GetState();
|
|
switch(currentCoverState)
|
|
{
|
|
case CTaskInCover::State_Aim:
|
|
case CTaskInCover::State_AimIntro:
|
|
case CTaskInCover::State_AimOutro:
|
|
case CTaskInCover::State_BlindFiring:
|
|
case CTaskInCover::State_ThrowingProjectile:
|
|
// active in cover, reset hiding time
|
|
m_fTimeElapsedHidingInCoverSeconds = 0.0f;
|
|
break;
|
|
default:
|
|
// otherwise the player is considered as hiding in cover
|
|
m_fTimeElapsedHidingInCoverSeconds += fwTimer::GetTimeStep();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// not in cover, so reset the elapsed times
|
|
m_fTimeElapsedUsingCoverSeconds = 0.0f;
|
|
m_fTimeElapsedHidingInCoverSeconds = 0.0f;
|
|
}
|
|
|
|
#if __DEV
|
|
bool bDebugCoverTracking = ( ms_bDisplayCoverTracking && m_fTimeElapsedUsingCoverSeconds > 0.0f );
|
|
if( bDebugCoverTracking )
|
|
{
|
|
int lineNum = 0;
|
|
const int DEBUG_STRING_SIZE = 256;
|
|
char debugString[DEBUG_STRING_SIZE];
|
|
const Vector3 vDebugPos = VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetPosition());
|
|
formatf(debugString, DEBUG_STRING_SIZE, "UsingCover: %.1f s", m_fTimeElapsedUsingCoverSeconds);
|
|
grcDebugDraw::Text(vDebugPos, Color_green, 0, lineNum * grcDebugDraw::GetScreenSpaceTextHeight(), debugString );
|
|
lineNum++;
|
|
formatf(debugString, DEBUG_STRING_SIZE, "HidingInCover: %.1f s", m_fTimeElapsedHidingInCoverSeconds);
|
|
grcDebugDraw::Text(vDebugPos, m_fTimeElapsedHidingInCoverSeconds > 0.0f ? Color_green : Color_grey, 0, lineNum * grcDebugDraw::GetScreenSpaceTextHeight(), debugString );
|
|
lineNum++;
|
|
}
|
|
#endif // __DEV
|
|
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ProcessCombatLoitering()
|
|
{
|
|
if( m_pPlayerPed )
|
|
{
|
|
// If player has enemies in combat
|
|
if( BANK_ONLY(ms_bDebugCombatLoitering ||) m_iNumEnemiesInCombat > 0 )
|
|
{
|
|
bool bResetLoitering = false;
|
|
|
|
// if loitering has not been set
|
|
if( m_fTimeElapsedLoiteringSeconds < 0.0f )
|
|
{
|
|
bResetLoitering = true;
|
|
}
|
|
else
|
|
{
|
|
const u32 currentTimeMS = fwTimer::GetTimeInMilliseconds();
|
|
|
|
// if enough time has elapsed to check distance
|
|
if( currentTimeMS >= m_uNextLoiterDistCheckTimeMS )
|
|
{
|
|
// set next time to check distance
|
|
m_uNextLoiterDistCheckTimeMS = currentTimeMS + ms_Tunables.m_CombatLoitering.m_uDistanceCheckPeriodMS;
|
|
|
|
// compute distance between player and stored loiter position
|
|
const ScalarV movementDeltaSq = DistSquared(m_pPlayerPed->GetTransform().GetPosition(), m_vCombatLoiteringPosition);
|
|
const ScalarV movementDeltaSq_MAX = LoadScalar32IntoScalarV(rage::square(ms_Tunables.m_CombatLoitering.m_fPlayerMoveDistToResetLoiterPosition));
|
|
if( IsGreaterThanAll(movementDeltaSq, movementDeltaSq_MAX) )
|
|
{
|
|
bResetLoitering = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bResetLoitering)
|
|
{
|
|
// update loitering position
|
|
m_vCombatLoiteringPosition = m_pPlayerPed->GetTransform().GetPosition();
|
|
|
|
// reset loitering time to none
|
|
m_fTimeElapsedLoiteringSeconds = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
// increment the time loitering
|
|
m_fTimeElapsedLoiteringSeconds += fwTimer::GetTimeStep();
|
|
}
|
|
|
|
#if __DEV
|
|
bool bDebugCombatLoitering = (ms_bDisplayCombatLoitering && m_fTimeElapsedLoiteringSeconds > 0.0f);
|
|
if( bDebugCombatLoitering )
|
|
{
|
|
int lineNum = 0;
|
|
const int DEBUG_STRING_SIZE = 256;
|
|
char debugString[DEBUG_STRING_SIZE];
|
|
const Vector3 vDebugPos = VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetPosition());
|
|
formatf(debugString, DEBUG_STRING_SIZE, "Loitering: %.1f s", m_fTimeElapsedLoiteringSeconds);
|
|
grcDebugDraw::Text(vDebugPos, Color_grey, 0, lineNum * grcDebugDraw::GetScreenSpaceTextHeight(), debugString );
|
|
|
|
// indicate tracked loitering position
|
|
const float fSphereRadius = 0.3f;
|
|
grcDebugDraw::Sphere(m_vCombatLoiteringPosition, fSphereRadius, Color_grey);
|
|
|
|
// indicate loitering radius
|
|
const bool bSolid = false;
|
|
grcDebugDraw::Sphere(m_vCombatLoiteringPosition, ms_Tunables.m_CombatLoitering.m_fPlayerMoveDistToResetLoiterPosition, Color_grey, bSolid);
|
|
}
|
|
#endif // __DEV
|
|
|
|
// exit this method
|
|
return;
|
|
}
|
|
}
|
|
|
|
// by default reset loitering time to none
|
|
m_fTimeElapsedLoiteringSeconds = -1.0f;
|
|
}
|
|
|
|
void CPlayerInfo::ProcessEnemyElections()
|
|
{
|
|
// Check if there is an open registration or acceptance period
|
|
const bool bOpenRegistration = (m_CandidateRegistrationEndTimeMS > 0);
|
|
const bool bOpenAcceptance = (m_BestCandidateAcceptanceEndTimeMS > 0);
|
|
if( bOpenRegistration || bOpenAcceptance )
|
|
{
|
|
// Get current time
|
|
const u32 currentTimeMS = fwTimer::GetTimeInMilliseconds();
|
|
|
|
// Check if the registration period has expired
|
|
if( bOpenRegistration && currentTimeMS > m_CandidateRegistrationEndTimeMS )
|
|
{
|
|
Assert(bOpenAcceptance == false);
|
|
|
|
// Clear the registration period
|
|
m_CandidateRegistrationEndTimeMS = 0;
|
|
|
|
// Start the best candidate acceptance period
|
|
const u32 uCandidateAcceptancePeriodMS = 2000;
|
|
m_BestCandidateAcceptanceEndTimeMS = currentTimeMS + uCandidateAcceptancePeriodMS;
|
|
|
|
aiDebugf3("ELECTION: CPlayerInfo::ProcessEnemyElections registration ended, acceptance scheduled [%d]", m_BestCandidateAcceptanceEndTimeMS);
|
|
}
|
|
// Check if the acceptance period has expired
|
|
// NOTE: We don't expect this to happen often, it's a safety in case the winning ped is
|
|
// killed off or otherwise vanished before they can report starting
|
|
else if( bOpenAcceptance && currentTimeMS > m_BestCandidateAcceptanceEndTimeMS )
|
|
{
|
|
Assert(bOpenRegistration == false);
|
|
|
|
// Clear the acceptance period
|
|
m_BestCandidateAcceptanceEndTimeMS = 0;
|
|
|
|
// Clear the winning ped, they didn't report start in time
|
|
m_BestEnemyChargerCandidatePed = NULL;
|
|
m_BestEnemySmokeThrowerCandidatePed = NULL;
|
|
|
|
aiDebugf3("ELECTION: CPlayerInfo::ProcessEnemyElections acceptance period timed out, clearing!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ProcessChargingEnemyGoalPositions()
|
|
{
|
|
// By default consider resetting charge goal position variables
|
|
bool bResetChargeGoalPositions = true;
|
|
|
|
// Check if the player has hostiles targeting him
|
|
if( BANK_ONLY(ms_bDebugCandidateChargeGoalPositions ||) m_iNumEnemiesInCombat > 0 )
|
|
{
|
|
// Check if the player is using cover according to the member time
|
|
if( m_fTimeElapsedUsingCoverSeconds > 0.0f )
|
|
{
|
|
// Player is in combat and using cover, do not reset the variables
|
|
bResetChargeGoalPositions = false;
|
|
|
|
// Check if the player charge position has not been set
|
|
if( IsZeroAll(m_vChargeGoalPlayerPosition) )
|
|
{
|
|
// Calculate and adjust charge goal candidate positions according to player cover
|
|
CCoverPoint* pPlayerCoverPoint = NULL;
|
|
if( m_pPlayerPed )
|
|
{
|
|
pPlayerCoverPoint = m_pPlayerPed->GetCoverPoint();
|
|
}
|
|
if( pPlayerCoverPoint )
|
|
{
|
|
// Calculate candidate charge goal positions
|
|
Vec3V vChargeGoalPosBase;
|
|
|
|
// First adjust the goal along the cover direction axis, away from the cover
|
|
const Vec3V vCoverDirection = pPlayerCoverPoint->GetCoverDirectionVector();
|
|
Vector3 vCoverPosition;
|
|
if( pPlayerCoverPoint->GetCoverPointPosition(vCoverPosition) )
|
|
{
|
|
vChargeGoalPosBase = Vec3V(VECTOR3_TO_VEC3V(vCoverPosition)) + ( -ScalarV(ms_Tunables.m_EnemyCharging.m_fChargeGoalBehindCoverCentralOffset) * vCoverDirection );
|
|
}
|
|
else // we don't have a valid cover position, bail out
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Set the position of the player for charging
|
|
// NOTE: This also serves as indicator that this process is active
|
|
m_vChargeGoalPlayerPosition = m_pPlayerPed->GetTransform().GetPosition();
|
|
|
|
// Adjust a bit further back from this for the rear position
|
|
m_CandidateChargeGoals[CGP_Rear].m_Position = vChargeGoalPosBase - (ScalarV(ms_Tunables.m_EnemyCharging.m_fChargeGoalRearOffset) * vCoverDirection);
|
|
|
|
// Adjust using lateral offsets to either side
|
|
const ScalarV radiansOrthogonalRight = ScalarV(-90.0f * DtoR);
|
|
Vec3V vCoverRightDirection = RotateAboutZAxis(vCoverDirection, radiansOrthogonalRight);
|
|
m_CandidateChargeGoals[CGP_Left].m_Position = vChargeGoalPosBase - (ScalarV(ms_Tunables.m_EnemyCharging.m_fChargeGoalLateralOffset) * vCoverRightDirection);
|
|
m_CandidateChargeGoals[CGP_Right].m_Position = vChargeGoalPosBase + (ScalarV(ms_Tunables.m_EnemyCharging.m_fChargeGoalLateralOffset) * vCoverRightDirection);
|
|
#if __BANK
|
|
// For debugging stash the original offset positions
|
|
m_CandidateChargeGoals[CGP_Left].m_InitialPosition = m_CandidateChargeGoals[CGP_Left].m_Position;
|
|
m_CandidateChargeGoals[CGP_Right].m_InitialPosition = m_CandidateChargeGoals[CGP_Right].m_Position;
|
|
m_CandidateChargeGoals[CGP_Rear].m_InitialPosition = m_CandidateChargeGoals[CGP_Rear].m_Position;
|
|
#endif // __BANK
|
|
// Mark all of the goals as pending navmesh check
|
|
for(int i=0; i < CGP_MAX_NUM; i++)
|
|
{
|
|
m_CandidateChargeGoals[i].m_Status = CGPS_PendingNavmeshCheck;
|
|
}
|
|
}
|
|
}
|
|
else // the player charge position has been set, process is active
|
|
{
|
|
// Check to see if the player has moved enough to reset the charge goal position process
|
|
const ScalarV DistPlayerActualToStoredPositionSq_MAX = ScalarVFromF32(rage::square(ms_Tunables.m_EnemyCharging.m_fPlayerMoveDistToResetChargeGoals));
|
|
const ScalarV DistPlayerActualToStoredPositionSq = DistSquared(m_pPlayerPed->GetTransform().GetPosition(), m_vChargeGoalPlayerPosition);
|
|
if( IsGreaterThanAll(DistPlayerActualToStoredPositionSq, DistPlayerActualToStoredPositionSq_MAX) )
|
|
{
|
|
// make sure we reset below
|
|
bResetChargeGoalPositions = true;
|
|
}
|
|
else // ongoing process of charge goal positions for current player position
|
|
{
|
|
// Traverse the candidates
|
|
for(int i=0; i < CGP_MAX_NUM; i++)
|
|
{
|
|
// If the candidate is waiting on navmesh check and adjustment
|
|
if( m_CandidateChargeGoals[i].m_Status == CGPS_PendingNavmeshCheck )
|
|
{
|
|
// If the search is already active
|
|
if( m_CandidateChargeGoalNavmeshHelpers[i].IsSearchActive() )
|
|
{
|
|
// Check for search results
|
|
SearchStatus searchResult;
|
|
int numPositionsFound;
|
|
const int maxPositionsFound = 1;
|
|
Vector3 foundPositions[maxPositionsFound];
|
|
if( m_CandidateChargeGoalNavmeshHelpers[i].GetSearchResults(searchResult, numPositionsFound, foundPositions, maxPositionsFound) )
|
|
{
|
|
if( SS_SearchFailed == searchResult || (SS_SearchSuccessful == searchResult && numPositionsFound <= 0))
|
|
{
|
|
m_CandidateChargeGoals[i].m_Status = CGPS_Invalid;
|
|
}
|
|
else // search successful and position found
|
|
{
|
|
aiAssert( SS_SearchSuccessful == searchResult );// if still searching, GetSearchResults should return false
|
|
|
|
// Use the closest found position as the new goal position
|
|
m_CandidateChargeGoals[i].m_Position = VECTOR3_TO_VEC3V(foundPositions[0]);
|
|
|
|
// Mark the candidate for line of sight check
|
|
m_CandidateChargeGoals[i].m_Status = CGPS_PendingProbeCheck;
|
|
}
|
|
}
|
|
}
|
|
else // need to kick off the search
|
|
{
|
|
// Try to kick off the process of adjusting on navmesh
|
|
const float fMaxAdjustRadius = ms_Tunables.m_EnemyCharging.m_fChargeGoalMaxAdjustRadius;
|
|
u32 iFlags = CNavmeshClosestPositionHelper::Flag_ConsiderDynamicObjects|CNavmeshClosestPositionHelper::Flag_ConsiderInterior|CNavmeshClosestPositionHelper::Flag_ConsiderExterior;
|
|
// NOTE: the request to start search may fail, so keep trying if necessary
|
|
m_CandidateChargeGoalNavmeshHelpers[i].StartClosestPositionSearch(VEC3V_TO_VECTOR3(m_CandidateChargeGoals[i].m_Position), fMaxAdjustRadius, iFlags);
|
|
}
|
|
}
|
|
// If the candidate is waiting on line of sight check
|
|
else if( m_CandidateChargeGoals[i].m_Status == CGPS_PendingProbeCheck )
|
|
{
|
|
// If the probe is already active
|
|
if( m_CandidateChargeGoalProbeHelpers[i].IsProbeActive() )
|
|
{
|
|
// Check to see if pending results are now ready
|
|
ProbeStatus probeResultStatus;
|
|
if( m_CandidateChargeGoalProbeHelpers[i].GetProbeResults(probeResultStatus) )
|
|
{
|
|
// Mark the status as valid or invalid according to results
|
|
if( probeResultStatus == PS_Clear )
|
|
{
|
|
m_CandidateChargeGoals[i].m_Status = CGPS_Valid;
|
|
}
|
|
else
|
|
{
|
|
m_CandidateChargeGoals[i].m_Status = CGPS_Invalid;
|
|
}
|
|
}
|
|
}
|
|
else // need to start the probe
|
|
{
|
|
// Kick off the process of validating LOS from candidate charge goal position to player position
|
|
WorldProbe::CShapeTestProbeDesc probeDescriptor;
|
|
probeDescriptor.SetStartAndEnd(VEC3V_TO_VECTOR3(m_CandidateChargeGoals[i].m_Position), VEC3V_TO_VECTOR3(m_vChargeGoalPlayerPosition));
|
|
probeDescriptor.SetContext(WorldProbe::ELosCombatAI);
|
|
probeDescriptor.SetExcludeEntity(m_pPlayerPed);
|
|
probeDescriptor.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES|ArchetypeFlags::GTA_OBJECT_TYPE|ArchetypeFlags::GTA_VEHICLE_TYPE);
|
|
probeDescriptor.SetOptions(WorldProbe::LOS_IGNORE_SEE_THRU);
|
|
// NOTE: the request to start the probe may be denied, keep trying if necessary
|
|
m_CandidateChargeGoalProbeHelpers[i].StartTestLineOfSight(probeDescriptor);
|
|
}
|
|
}
|
|
}// END for candidate points list
|
|
}// END if processing candidate points
|
|
}// END if process active
|
|
}// END if using cover
|
|
}// END if in combat
|
|
|
|
// If we should reset the charge goal positions, do so
|
|
if( bResetChargeGoalPositions && !IsZeroAll(m_vChargeGoalPlayerPosition) )
|
|
{
|
|
ResetChargingEnemyGoalPositions();
|
|
}
|
|
|
|
#if __BANK
|
|
if( ms_bDebugDrawCandidateChargePositions && !IsZeroAll(m_vChargeGoalPlayerPosition) )
|
|
{
|
|
// Draw blue sphere at player position
|
|
Color32 debugColor = Color_blue;
|
|
float fDebugRadiusA = 0.5f;
|
|
float fDebugRadiusB = 0.6f;
|
|
bool bSolid = false;
|
|
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(m_vChargeGoalPlayerPosition), fDebugRadiusB, debugColor, bSolid);
|
|
|
|
for(int i=0; i < CGP_MAX_NUM; i++)
|
|
{
|
|
debugColor = Color_white; // initial position
|
|
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(m_CandidateChargeGoals[i].m_InitialPosition), fDebugRadiusA, debugColor, bSolid);
|
|
grcDebugDraw::Line(VEC3V_TO_VECTOR3(m_CandidateChargeGoals[i].m_InitialPosition), VEC3V_TO_VECTOR3(m_CandidateChargeGoals[i].m_Position), debugColor, debugColor);
|
|
|
|
debugColor = Color_yellow;// CGPS_Unknown
|
|
if( m_CandidateChargeGoals[i].m_Status == CGPS_Valid) { debugColor = Color_green; }
|
|
else if( m_CandidateChargeGoals[i].m_Status == CGPS_Invalid) { debugColor = Color_red; }
|
|
else if( m_CandidateChargeGoals[i].m_Status == CGPS_PendingNavmeshCheck) { debugColor = Color_blue;}
|
|
else if( m_CandidateChargeGoals[i].m_Status == CGPS_PendingProbeCheck) { debugColor = Color_orange;}
|
|
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(m_CandidateChargeGoals[i].m_Position), fDebugRadiusB, debugColor, bSolid);
|
|
}
|
|
}
|
|
#endif // __BANK
|
|
}
|
|
|
|
bool CPlayerInfo::HasValidatedChargeGoalPosition() const
|
|
{
|
|
for(int i=0; i < CGP_MAX_NUM; i++)
|
|
{
|
|
if( m_CandidateChargeGoals[i].m_Status == CGPS_Valid )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::GetValidatedChargeGoalPosition(Vec3V_In inQueryPosition, Vec3V_InOut outChargeGoalPosition) const
|
|
{
|
|
// First check if any positions are validated
|
|
if( !HasValidatedChargeGoalPosition() )
|
|
{
|
|
// none valid
|
|
return false;
|
|
}
|
|
|
|
// Check player's cover to see if left or right would be best
|
|
if( m_pPlayerPed && (m_CandidateChargeGoals[CGP_Left].m_Status == CGPS_Valid || m_CandidateChargeGoals[CGP_Right].m_Status == CGPS_Valid))
|
|
{
|
|
CCoverPoint* pPlayerCoverPoint = m_pPlayerPed->GetCoverPoint();
|
|
if( pPlayerCoverPoint )
|
|
{
|
|
const CCoverPoint::eCoverUsage ePlayerCoverUsage = pPlayerCoverPoint->GetUsage();
|
|
if( ePlayerCoverUsage == CCoverPoint::COVUSE_WALLTOLEFT )
|
|
{
|
|
if( m_CandidateChargeGoals[CGP_Left].m_Status == CGPS_Valid )
|
|
{
|
|
outChargeGoalPosition = m_CandidateChargeGoals[CGP_Left].m_Position;
|
|
return true;
|
|
}
|
|
}
|
|
else if( ePlayerCoverUsage == CCoverPoint::COVUSE_WALLTORIGHT )
|
|
{
|
|
if( m_CandidateChargeGoals[CGP_Right].m_Status == CGPS_Valid )
|
|
{
|
|
outChargeGoalPosition = m_CandidateChargeGoals[CGP_Right].m_Position;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use the closest valid position to query position
|
|
ScalarV closestDistSq = LoadScalar32IntoScalarV(FLT_MAX);
|
|
int iClosestIndex = -1;
|
|
for(int i=0; i < CGP_MAX_NUM; i++)
|
|
{
|
|
if( m_CandidateChargeGoals[i].m_Status == CGPS_Valid )
|
|
{
|
|
ScalarV distSq = DistSquared(m_CandidateChargeGoals[i].m_Position, inQueryPosition);
|
|
if( IsLessThanAll(distSq, closestDistSq) )
|
|
{
|
|
iClosestIndex = i;
|
|
closestDistSq = distSq;
|
|
}
|
|
}
|
|
}
|
|
if( iClosestIndex >= 0 )
|
|
{
|
|
outChargeGoalPosition = m_CandidateChargeGoals[iClosestIndex].m_Position;
|
|
return true;
|
|
}
|
|
|
|
// no valid charge goal by default
|
|
return false;
|
|
}
|
|
|
|
void CPlayerInfo::ResetChargingEnemyGoalPositions()
|
|
{
|
|
m_vChargeGoalPlayerPosition.ZeroComponents();
|
|
for(int i=0; i < CGP_MAX_NUM; i++)
|
|
{
|
|
m_CandidateChargeGoals[i].Reset();
|
|
m_CandidateChargeGoalNavmeshHelpers[i].ResetSearch();
|
|
m_CandidateChargeGoalProbeHelpers[i].ResetProbe();
|
|
}
|
|
}
|
|
|
|
bool CPlayerInfo::HasPlayerLeftTheWorld() const
|
|
{
|
|
return ms_Tunables.m_GuardWorldExtents && !ms_WorldLimitsPlayerAABB.ContainsPointFlat(m_pPlayerPed->GetTransform().GetPosition()) NOTFINAL_ONLY( || ms_bFakeWorldExtentsTresspassing);
|
|
}
|
|
|
|
// Look at the player and decide how to deal with them crossing the world boundary.
|
|
void CPlayerInfo::HandlePlayerWorldBreach()
|
|
{
|
|
CVehicle* pVehicle = m_pPlayerPed->GetVehiclePedInside();
|
|
|
|
// If the player was not inside a vehicle, they could still be standing on one. Treat these vehicles in the same way to
|
|
// prevent the player riding an AI vehicle of some kind to freedom.
|
|
if (!pVehicle)
|
|
{
|
|
CPhysical* pGroundPhysical = m_pPlayerPed->GetGroundPhysical();
|
|
if (pGroundPhysical && pGroundPhysical->GetIsTypeVehicle())
|
|
{
|
|
pVehicle = static_cast<CVehicle*>(pGroundPhysical);
|
|
}
|
|
}
|
|
|
|
if (pVehicle)
|
|
{
|
|
// Disable the vehicle.
|
|
HandlePlayerWorldBreachVehicle(*pVehicle);
|
|
}
|
|
else if (m_pPlayerPed->GetIsParachuting())
|
|
{
|
|
// Disable the parachute.
|
|
HandlePlayerWorldBreachParachuting();
|
|
}
|
|
else if (m_pPlayerPed->GetIsSwimming() || m_pPlayerPed->GetPedType() == PEDTYPE_ANIMAL)
|
|
{
|
|
// Spawn a shark if possible, otherwise just kill them.
|
|
HandlePlayerWorldBreachSwimming();
|
|
}
|
|
}
|
|
|
|
// Make the parachute drop.
|
|
void CPlayerInfo::HandlePlayerWorldBreachParachuting()
|
|
{
|
|
CTaskParachute* pTaskParachute = static_cast<CTaskParachute*>(m_pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_PARACHUTE));
|
|
if (pTaskParachute)
|
|
{
|
|
pTaskParachute->RemoveParachuteForScript();
|
|
}
|
|
}
|
|
|
|
// Damage plane/heli/blimp engines and sink boats/subs.
|
|
void CPlayerInfo::HandlePlayerWorldBreachVehicle(CVehicle& rVehicle)
|
|
{
|
|
CVehicleDamage* pDamage = rVehicle.GetVehicleDamage();
|
|
CSubmarineHandling* subHandling = rVehicle.GetSubHandling();
|
|
CBoatHandling* boatHandling = rVehicle.GetBoatHandling();
|
|
|
|
if( subHandling )
|
|
{
|
|
if (rVehicle.GetStatus() != STATUS_WRECKED)
|
|
{
|
|
// Tell the submarine to sink when wrecked.
|
|
subHandling->SetSinksWhenWrecked(true);
|
|
|
|
// Wreck the submarine.
|
|
rVehicle.SetIsWrecked();
|
|
}
|
|
}
|
|
else if( boatHandling )
|
|
{
|
|
if (rVehicle.GetStatus() != STATUS_WRECKED)
|
|
{
|
|
// Tell the boat to sink when wrecked.
|
|
boatHandling->SetWreckedAction( BWA_SINK );
|
|
|
|
// Wreck the boat.
|
|
rVehicle.SetIsWrecked();
|
|
|
|
if(!rVehicle.IsNetworkClone())
|
|
{
|
|
// Apparently jetskis will still go forward if you wreck them.
|
|
rVehicle.SwitchEngineOff(true);
|
|
}
|
|
}
|
|
}
|
|
else if (rVehicle.InheritsFromPlane())
|
|
{
|
|
// [HACK_GTAV]
|
|
// B* 1673245 - it is possible to glide to other code crashing (CNetworkWorldGridManager, etc) if the player gets too far away from 0,0,0.
|
|
// So, if they are in a plane and attempting this we perform more destructive code on them.
|
|
// Do this once the engine is destroyed from the code below.
|
|
CPlane& rPlane = static_cast<CPlane&>(rVehicle);
|
|
if (!pDamage || pDamage->GetEngineHealth() <= SMALL_FLOAT)
|
|
{
|
|
rPlane.DamageForWorldExtents();
|
|
|
|
if( rVehicle.GetModelIndex() == MI_PLANE_MICROLIGHT )
|
|
{
|
|
rVehicle.BlowUpCar( NULL );
|
|
}
|
|
}
|
|
}
|
|
else if(rVehicle.InheritsFromAutomobile() && rVehicle.HasParachute())
|
|
{
|
|
CAutomobile* pCar = static_cast<CAutomobile*>(&rVehicle);
|
|
|
|
if(pCar->IsParachuting() && !pCar->IsFinishParachutingRequested())
|
|
{
|
|
pCar->RequestFinishParachuting();
|
|
}
|
|
|
|
rVehicle.SetIsWrecked();
|
|
}
|
|
else if( rVehicle.HasGlider() )
|
|
{
|
|
rVehicle.FinishGliding();
|
|
if(!rVehicle.IsNetworkClone())
|
|
{
|
|
rVehicle.SwitchEngineOff();
|
|
}
|
|
rVehicle.BlowUpCar( NULL );
|
|
}
|
|
else if( rVehicle.pHandling->GetSpecialFlightHandlingData() )
|
|
{
|
|
if(!rVehicle.IsNetworkClone())
|
|
{
|
|
rVehicle.SwitchEngineOff();
|
|
}
|
|
rVehicle.SetIsWrecked();
|
|
|
|
if( rVehicle.InheritsFromBike() )
|
|
{
|
|
rVehicle.BlowUpCar( NULL );
|
|
}
|
|
}
|
|
else if (!(rVehicle.InheritsFromHeli() || rVehicle.InheritsFromBlimp()))
|
|
{
|
|
Assertf(false, "Unexpected vehicle type escaping the world extents!");
|
|
rVehicle.SetIsWrecked();
|
|
}
|
|
|
|
// Continuously damage the engine.
|
|
if (Verifyf(pDamage, "No vehicle damage pointer to destroy vehicle escaping world bounds!"))
|
|
{
|
|
pDamage->SetEngineHealth(rage::Max(0.0f, pDamage->GetEngineHealth() - 10.0f)); //magic number - doesn't really matter how much as long as it happens continuously
|
|
}
|
|
}
|
|
|
|
// Try to spawn a shark to kill the player, or in the worst case just force kill them with a death event.
|
|
void CPlayerInfo::HandlePlayerWorldBreachSwimming()
|
|
{
|
|
bool bJustKillThePlayer = false;
|
|
|
|
if (m_bHasWorldExtentsSpawnedAShark)
|
|
{
|
|
if (!CTaskSharkAttack::SharksArePresent())
|
|
{
|
|
// Here, we've already spawned a shark.
|
|
// But somehow it either died or gave up. So count down until the max trespassing timer expires.
|
|
m_fSwimBoundsEscapeTimer -= fwTimer::GetTimeStep();
|
|
}
|
|
|
|
if (m_fSwimBoundsEscapeTimer <= 0.0f)
|
|
{
|
|
// Reset this so we don't keep spamming death events if death takes a while to achieve.
|
|
m_fSwimBoundsEscapeTimer = ms_Tunables.m_MaxTimeToTrespassWhileSwimmingBeforeDeath;
|
|
|
|
// Something went wrong, the shark was unable to kill the player. Default to just autokill.
|
|
bJustKillThePlayer = true;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
// No sharks in MP, so just kill the player.
|
|
bJustKillThePlayer = true;
|
|
}
|
|
else if (m_pPlayerPed->GetPedType() == PEDTYPE_ANIMAL)
|
|
{
|
|
// Force death if the player is an animal.
|
|
bJustKillThePlayer = true;
|
|
}
|
|
else if (!bJustKillThePlayer)
|
|
{
|
|
// Create a shark.
|
|
|
|
CWildlifeManager& rManager = CWildlifeManager::GetInstance();
|
|
|
|
bool bSuccess = rManager.DispatchASharkNearPlayer(true);
|
|
|
|
if (bSuccess)
|
|
{
|
|
m_bHasWorldExtentsSpawnedAShark = true;
|
|
}
|
|
else
|
|
{
|
|
if (!rManager.CanSpawnSharkModels())
|
|
{
|
|
// The wildlife manager failed to spawn in some unrecoverable way (not just a streaming failure) - don't bother with trying to spawn one again.
|
|
bJustKillThePlayer = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bJustKillThePlayer)
|
|
{
|
|
// Just give the player the death task.
|
|
CDeathSourceInfo info(m_pPlayerPed);
|
|
CTask* pTaskDie = rage_new CTaskDyingDead(&info, CTaskDyingDead::Flag_ragdollDeath);
|
|
CEventGivePedTask deathEvent(PED_TASK_PRIORITY_PRIMARY, pTaskDie);
|
|
m_pPlayerPed->GetPedIntelligence()->AddEvent(deathEvent);
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ExtendWorldBoundary(const Vector3 &vCoordExtendsTo) {ms_WorldLimitsPlayerAABB.GrowPoint(VECTOR3_TO_VEC3V(vCoordExtendsTo));}
|
|
|
|
void CPlayerInfo::SetMaxPortablePickupsOfTypePermitted(int modelIndex, int maxPermitted)
|
|
{
|
|
Assert(modelIndex != -1);
|
|
|
|
int freeSlot = -1;
|
|
|
|
for (u32 i=0; i<MAX_PORTABLE_PICKUP_INFOS; i++)
|
|
{
|
|
if (PortablePickupsInfo[i].modelIndex == modelIndex)
|
|
{
|
|
if (maxPermitted == -1)
|
|
{
|
|
// clear this slot
|
|
PortablePickupsInfo[i].modelIndex = -1;
|
|
PortablePickupsInfo[i].maxPermitted = 0;
|
|
PortablePickupsInfo[i].numCarried = 0;
|
|
}
|
|
else
|
|
{
|
|
PortablePickupsInfo[i].maxPermitted = (u16)maxPermitted;
|
|
}
|
|
return;
|
|
}
|
|
else if (PortablePickupsInfo[i].modelIndex == -1 && freeSlot == -1)
|
|
{
|
|
freeSlot = i;
|
|
}
|
|
}
|
|
|
|
if (maxPermitted != -1)
|
|
{
|
|
if (freeSlot == -1)
|
|
{
|
|
Assertf(0, "CPlayerInfo::SetMaxPortablePickupsOfTypePermitted - ran out of portable pickup infos. Increase size of MAX_PORTABLE_PICKUP_INFOS");
|
|
}
|
|
else
|
|
{
|
|
PortablePickupsInfo[freeSlot].modelIndex = modelIndex;
|
|
PortablePickupsInfo[freeSlot].maxPermitted = (u16)maxPermitted;
|
|
PortablePickupsInfo[freeSlot].numCarried = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CPlayerInfo::CanCarryPortablePickupOfType(int modelIndex, bool bAlreadyCollected)
|
|
{
|
|
if (m_maxPortablePickupsCarried != -1)
|
|
{
|
|
if (bAlreadyCollected)
|
|
{
|
|
if (m_numPortablePickupsCarried > m_maxPortablePickupsCarried)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_numPortablePickupsCarried >= m_maxPortablePickupsCarried)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (u32 i=0; i<MAX_PORTABLE_PICKUP_INFOS; i++)
|
|
{
|
|
if (PortablePickupsInfo[i].modelIndex == modelIndex)
|
|
{
|
|
if (bAlreadyCollected)
|
|
{
|
|
if (PortablePickupsInfo[i].numCarried <= PortablePickupsInfo[i].maxPermitted)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PortablePickupsInfo[i].numCarried < PortablePickupsInfo[i].maxPermitted)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CPlayerInfo::PortablePickupCollected(int modelIndex)
|
|
{
|
|
PortablePickupPending = false;
|
|
|
|
if(NetworkInterface::IsNetworkOpen())
|
|
{
|
|
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Portable pickup type collected", "%d", modelIndex);
|
|
}
|
|
|
|
for (u32 i=0; i<MAX_PORTABLE_PICKUP_INFOS; i++)
|
|
{
|
|
if (PortablePickupsInfo[i].modelIndex == modelIndex)
|
|
{
|
|
Assert (PortablePickupsInfo[i].numCarried < PortablePickupsInfo[i].maxPermitted);
|
|
|
|
PortablePickupsInfo[i].numCarried++;
|
|
|
|
if(NetworkInterface::IsNetworkOpen())
|
|
{
|
|
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Number of this type collected", "%d", PortablePickupsInfo[i].numCarried);
|
|
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Maximum permitted", "%d", PortablePickupsInfo[i].maxPermitted);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_numPortablePickupsCarried++;
|
|
}
|
|
|
|
void CPlayerInfo::PortablePickupDropped(int modelIndex)
|
|
{
|
|
if(NetworkInterface::IsNetworkOpen())
|
|
{
|
|
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Portable pickup type dropped", "%d", modelIndex);
|
|
}
|
|
|
|
for (u32 i=0; i<MAX_PORTABLE_PICKUP_INFOS; i++)
|
|
{
|
|
if (PortablePickupsInfo[i].modelIndex == modelIndex)
|
|
{
|
|
if (AssertVerify(PortablePickupsInfo[i].numCarried > 0))
|
|
{
|
|
PortablePickupsInfo[i].numCarried--;
|
|
|
|
if(NetworkInterface::IsNetworkOpen())
|
|
{
|
|
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Remaining pickups of this type collected", "%d", PortablePickupsInfo[i].numCarried);
|
|
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Maximum permitted", "%d", PortablePickupsInfo[i].maxPermitted);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_numPortablePickupsCarried > 0)
|
|
{
|
|
m_numPortablePickupsCarried--;
|
|
}
|
|
}
|
|
|
|
bool CPlayerInfo::CanPlayerPedCollectAnyPickups(const CPed& playerPed)
|
|
{
|
|
if (playerPed.GetPedType() == PEDTYPE_ANIMAL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION : EvaluateCarPosition
|
|
// PURPOSE : How much should this car slow down taking into account that other car
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPlayerInfo::EvaluateCarPosition(CEntity* pEntity, CPed *pPed, Vector3& vSeatPos, float Distance, float *pCloseness, CVehicle **ppClosestVehicle, Vector3& vCarSearchDirection, const bool bUsingStickInput)
|
|
{
|
|
float PedOrientation, CarOrientation, AngleDiff, Closeness;
|
|
Vector3 vTargetPos = vSeatPos; // pEntity->GetPosition();
|
|
|
|
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pEntity);
|
|
float fDistanceLimit = SCANNEARBYVEHICLES;
|
|
// if this is a boat, reduce the distance by the bounding radius (cos some of them are massive)
|
|
if ( pVehicle->GetVehicleType() == VEHICLE_TYPE_BOAT )
|
|
{
|
|
fDistanceLimit += pEntity->GetBoundRadius();;
|
|
}
|
|
|
|
const Vector3 vPedPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
// Calculate the angle difference between the pedestrian and the car.
|
|
PedOrientation = fwAngle::GetATanOfXY(vCarSearchDirection.x, vCarSearchDirection.y);
|
|
CarOrientation = fwAngle::GetATanOfXY( (vTargetPos.x - vPedPos.x), (vTargetPos.y - vPedPos.y));
|
|
AngleDiff = PedOrientation - CarOrientation;
|
|
AngleDiff = fwAngle::LimitRadianAngle(AngleDiff);
|
|
AngleDiff = ABS(AngleDiff);
|
|
|
|
float fCameraScore = 0.0f;
|
|
float fMovementAwayScore = 1.0f; // Decreases if we're moving away from a close vehicle
|
|
|
|
if (bUsingStickInput)
|
|
{
|
|
bool bConsiderVehicleValidAnyway = false;
|
|
// Unless its close to the player and we're pointing the cam at it, B*984581
|
|
Vector3 vVehPos = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
|
|
const float fDistToVehSqd = (vVehPos - vPedPos).Mag2();
|
|
float fDistTolerance = ms_Tunables.m_MaxDistToConsiderVehicleValid;
|
|
if (pEntity->GetIsTypeVehicle())
|
|
{
|
|
fDistTolerance += static_cast<CVehicle*>(pEntity)->GetVehicleModelInfo()->GetBoundingSphereRadius();
|
|
}
|
|
|
|
Vector3 vCamFront = camInterface::GetFront();
|
|
Vector3 vCamPos = camInterface::GetPos();
|
|
Vector3 vCamToVeh = vTargetPos - vCamPos;
|
|
vCamToVeh.Normalize();
|
|
fCameraScore = vCamFront.Dot(vCamToVeh);
|
|
|
|
if (fDistToVehSqd < rage::square(fDistTolerance))
|
|
{
|
|
if (fCameraScore >= ms_Tunables.m_MinDotToConsiderVehicleValid)
|
|
{
|
|
bConsiderVehicleValidAnyway = true;
|
|
}
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(CHOOSE_VEHICLE_TUNE, ENABLE_MOVEMENT_AWAY_SCORE, true);
|
|
if (ENABLE_MOVEMENT_AWAY_SCORE)
|
|
{
|
|
// If the ped would need to double back to get in the vehicle, penalize them for it
|
|
const float fDesiredHeading = fwAngle::LimitRadianAngleSafe(pPed->GetDesiredHeading());
|
|
Vector3 vPedDesiredDirection = Vector3(0.0f, 1.0f, 0.0f);
|
|
vPedDesiredDirection.RotateZ(fDesiredHeading);
|
|
|
|
TUNE_GROUP_FLOAT(CHOOSE_VEHICLE_TUNE, MOVEMENT_AWAY_REJECT_COS_ANGLE, 0.0f, -1.0f, 1.0f, 0.01f);
|
|
Vector3 vPedToTarget = (vTargetPos - vPedPos);
|
|
vPedToTarget.Normalize();
|
|
const float fCosAngle = vPedToTarget.Dot(vPedDesiredDirection);
|
|
if (fCosAngle < MOVEMENT_AWAY_REJECT_COS_ANGLE)
|
|
{
|
|
const float fMbrNorm = pPed->GetMotionData()->GetCurrentMbrY() * ONE_OVER_MOVEBLENDRATIO_SPRINT;
|
|
fMovementAwayScore -= fMbrNorm;
|
|
}
|
|
}
|
|
|
|
// Ignore vehicles outside 90 degrees when using the stick
|
|
if (AngleDiff > HALF_PI)
|
|
{
|
|
if (!bConsiderVehicleValidAnyway)
|
|
{
|
|
// Stick bounce back detection B*1617109
|
|
if (pPed->IsLocalPlayer() && bUsingStickInput)
|
|
{
|
|
const CControl* pControl = pPed->GetControlFromPlayer();
|
|
if (pControl)
|
|
{
|
|
Vector2 vecStickInput(pControl->GetPedWalkLeftRight().GetNorm(), pControl->GetPedWalkUpDown().GetNorm());
|
|
TUNE_GROUP_INT(CHOOSE_VEHICLE_TUNE, MAX_STICK_MAG_TO_TRY_ACCEPT, 80, 0, 255, 1);
|
|
if (Abs(vecStickInput.x) < MAX_STICK_MAG_TO_TRY_ACCEPT && Abs(vecStickInput.y) < MAX_STICK_MAG_TO_TRY_ACCEPT)
|
|
{
|
|
// Calculate the angle difference between the pedestrian and the car.
|
|
Vector3 vCarSearchDirection = VEC3V_TO_VECTOR3(pPed->GetTransform().GetB());
|
|
float PedOrientation = fwAngle::GetATanOfXY(vCarSearchDirection.x, vCarSearchDirection.y);
|
|
float CarOrientation = fwAngle::GetATanOfXY( (vTargetPos.x - vPedPos.x), (vTargetPos.y - vPedPos.y));
|
|
float AngleDiff = PedOrientation - CarOrientation;
|
|
AngleDiff = fwAngle::LimitRadianAngle(AngleDiff);
|
|
AngleDiff = ABS(AngleDiff);
|
|
|
|
if (AngleDiff < HALF_PI)
|
|
{
|
|
bConsiderVehicleValidAnyway = true;
|
|
}
|
|
|
|
AI_FUNCTION_LOG_WITH_ARGS("%s vehicle %s because AngleDiff = %.2f, fDistToVehSqd = %.2f, fDistTolerance = %.2f", bConsiderVehicleValidAnyway ? "Considering" : "Rejecting", AILogging::GetDynamicEntityNameSafe(pVehicle), AngleDiff, fDistToVehSqd, fDistTolerance);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bConsiderVehicleValidAnyway)
|
|
{
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::ScanForVehicleToEnter]",pVehicle, "Not considered valid");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const float fDistanceWeighting = bUsingStickInput ? ms_Tunables.m_DistanceWeighting : ms_Tunables.m_DistanceWeightingNoStick;
|
|
const float fHeadingWeighting = bUsingStickInput ? ms_Tunables.m_HeadingWeighting : ms_Tunables.m_HeadingWeightingNoStick;
|
|
const float fCameraWeighting = bUsingStickInput ? ms_Tunables.m_CameraWeighting : 0.0f;
|
|
|
|
const float fAngleScore = fHeadingWeighting * (1.0f - (AngleDiff / ms_Tunables.m_MaxAngleConsidered));
|
|
const float fDistanceScore = fDistanceWeighting * ((fDistanceLimit - Distance) / fDistanceLimit);
|
|
fCameraScore *= fCameraWeighting;
|
|
fMovementAwayScore *= ms_Tunables.m_MovementAwayWeighting;
|
|
|
|
Closeness = fDistanceScore + fAngleScore + fCameraScore + fMovementAwayScore;
|
|
|
|
// Make sure the score is non negative as the distance score can go heavily negative as it doesn't get clamped
|
|
if (Closeness < 0.0f && pPed->GetGroundPhysical() == pVehicle)
|
|
{
|
|
Closeness = 0.0f;
|
|
}
|
|
|
|
// if car is on fire we might still want to get in to drive it and jump out again, so just lower the weighting for this vehicle
|
|
if(pVehicle->GetVehicleDamage()->GetEngineHealth() <= 0.0f || pVehicle->GetVehicleDamage()->GetPetrolTankHealth() <= 0.0f)
|
|
Closeness *= ms_Tunables.m_OnFireWeightingMult;
|
|
|
|
// Make sure there is a clear los to the target seat (maybe too restrictive?)
|
|
WorldProbe::CShapeTestProbeDesc probeDesc;
|
|
WorldProbe::CShapeTestFixedResults<> probeResults;
|
|
probeDesc.SetResultsStructure(&probeResults);
|
|
TUNE_GROUP_BOOL(CHOOSE_VEHICLE_TUNE, TEST_VEHICLES_AND_USE_PED_POS, true);
|
|
Vector3 vTestPos = TEST_VEHICLES_AND_USE_PED_POS ? vPedPos : pPed->GetBonePositionCached(BONETAG_HEAD);
|
|
if (TEST_VEHICLES_AND_USE_PED_POS)
|
|
{
|
|
TUNE_GROUP_FLOAT(CHOOSE_VEHICLE_TUNE, EXTRA_Z_OFFSET, 0.0f, -1.0f, 1.0f, 0.01f);
|
|
vTestPos.z += EXTRA_Z_OFFSET;
|
|
}
|
|
|
|
if (pVehicle->InheritsFromBike() && pVehicle->GetWheel(0))
|
|
{
|
|
Vector3 vWheelPos;
|
|
pVehicle->GetWheel(0)->GetWheelPosAndRadius(vWheelPos);
|
|
vSeatPos.x = vWheelPos.x;
|
|
vSeatPos.y = vWheelPos.y;
|
|
}
|
|
|
|
probeDesc.SetStartAndEnd(vTestPos, vSeatPos);
|
|
s32 iFlags = ArchetypeFlags::GTA_MAP_TYPE_MOVER | ArchetypeFlags::GTA_OBJECT_TYPE;
|
|
if (TEST_VEHICLES_AND_USE_PED_POS)
|
|
{
|
|
iFlags |= ArchetypeFlags::GTA_VEHICLE_TYPE;
|
|
}
|
|
probeDesc.SetIncludeFlags(iFlags);
|
|
probeDesc.SetExcludeEntity(NULL);
|
|
|
|
if(WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc, WorldProbe::PERFORM_SYNCHRONOUS_TEST))
|
|
{
|
|
const CEntity* pHitEntity = probeResults[0].GetHitEntity();
|
|
if (pHitEntity && (!pHitEntity->GetIsTypeVehicle() || pHitEntity != pEntity))
|
|
{
|
|
bool bShouldEarlyOut = true;
|
|
|
|
// Check if the other vehicle we hit is drivable, if not, then ignore it
|
|
if (pHitEntity->GetIsTypeVehicle())
|
|
{
|
|
if (static_cast<const CVehicle*>(pHitEntity)->GetStatus() == STATUS_WRECKED)
|
|
{
|
|
bShouldEarlyOut = false;
|
|
}
|
|
}
|
|
|
|
// Don't count things that are attached to us
|
|
if (pHitEntity->GetAttachParent() == pPed)
|
|
{
|
|
bShouldEarlyOut = false;
|
|
}
|
|
|
|
// The below will only return false if we're hit a pickup exclusively, in this case we don't want to prevent entry
|
|
if (!CPedGeometryAnalyser::TestIfCollisionTypeIsValid(pPed, iFlags, probeResults))
|
|
{
|
|
bShouldEarlyOut = false;
|
|
}
|
|
|
|
if (pHitEntity->GetIsTypeVehicle() && !pHitEntity->IsCollisionEnabled())
|
|
{
|
|
bShouldEarlyOut = false;
|
|
#if __BANK
|
|
CVehicleLockDebugInfo debugInfo(pPed, static_cast<const CVehicle*>(pHitEntity));
|
|
debugInfo.Print();
|
|
aiWarningf("Ignoring vehicle %s because it's collision isn't enabled, is visible %s, please add a bug with full logs for default ai code", AILogging::GetDynamicEntityNameSafe(static_cast<const CVehicle*>(pHitEntity)), AILogging::GetBooleanAsString(pHitEntity->GetIsVisible()));
|
|
#endif // __BANK
|
|
}
|
|
|
|
if (pHitEntity->GetIsTypeObject() && pHitEntity->GetAttachParent() == pVehicle && static_cast<const CObject*>(pHitEntity)->PopTypeIsMission())
|
|
{
|
|
bShouldEarlyOut = false;
|
|
}
|
|
|
|
if (bShouldEarlyOut)
|
|
{
|
|
#if __BANK
|
|
char szDebugText[256];
|
|
formatf(szDebugText, "Probe hit invalid collision, hit entity : %s", pHitEntity->GetIsDynamic() ? AILogging::GetDynamicEntityNameSafe(static_cast<const CDynamicEntity*>(pHitEntity)) : pHitEntity->GetModelName());
|
|
AI_LOG_REJECTION_WITH_IDENTIFIER_AND_REASON("[VehicleEntryExit][CPlayerInfo::EvaluateCarPosition]", pVehicle, szDebugText);
|
|
#endif // __BANK
|
|
#if DEBUG_DRAW
|
|
if( CTaskPlayerOnFoot::ms_bRenderPlayerVehicleSearch || CPedDebugVisualiser::GetDebugDisplay()==CPedDebugVisualiser::eVehicleEntryDebug)
|
|
{
|
|
CTask::ms_debugDraw.AddLine(RCC_VEC3V(vTestPos), RCC_VEC3V(vTargetPos), Color_red, 5000);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DEBUG_DRAW
|
|
if( CTaskPlayerOnFoot::ms_bRenderPlayerVehicleSearch || CPedDebugVisualiser::GetDebugDisplay()==CPedDebugVisualiser::eVehicleEntryDebug)
|
|
{
|
|
char szString[512];
|
|
CTask::ms_debugDraw.AddLine( RCC_VEC3V(vTestPos), RCC_VEC3V(vTargetPos), Color_green, 5000);
|
|
sprintf(szString, "%s, T(%.2f) = H(%.2f) + D(%.2f) + C(%.2f) + M(%.2f)", bUsingStickInput ? "S" : "N", Closeness, fAngleScore, fDistanceScore, fCameraScore, fMovementAwayScore );
|
|
CTask::ms_debugDraw.AddText(RCC_VEC3V(vTargetPos), 0, 0, szString, Color_blue, 5000);
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
|
|
AI_LOG_WITH_ARGS("[VehicleEntryExit][CPlayerInfo::EvaluateCarPosition] - Vehicle %s, Scores (distance = %.2f | angle = %.2f | camera = %.2f | movement = %.2f), Total = %.2f\n", AILogging::GetDynamicEntityNameSafe(pVehicle), fDistanceScore, fAngleScore, fCameraScore, fMovementAwayScore, Closeness);
|
|
|
|
if (Closeness >= (*pCloseness) )
|
|
{
|
|
*pCloseness = Closeness;
|
|
*ppClosestVehicle = (CVehicle *)pEntity;
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::EvaluateAnimalPosition(CEntity* pEntity, CPed *m_pPed, float Distance, float *pCloseness, CPed **ppClosestAnimal, Vector3& vAnimalSearchDirection, const bool bUsingStickInput)
|
|
{
|
|
float PedOrientation, AnimalOrientation, AngleDiff, Closeness;
|
|
Vector3 vTargetPos = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
|
|
|
|
float fDistanceLimit = ms_Tunables.m_ScanNearbyMountsDistance;
|
|
|
|
const Vector3 vPedPos = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition());
|
|
// Calculate the angle difference between the pedestrian and the car.
|
|
PedOrientation = fwAngle::GetATanOfXY(vAnimalSearchDirection.x, vAnimalSearchDirection.y);
|
|
AnimalOrientation = fwAngle::GetATanOfXY( (vTargetPos.x - vPedPos.x), (vTargetPos.y - vPedPos.y));
|
|
AngleDiff = PedOrientation - AnimalOrientation;
|
|
AngleDiff = fwAngle::LimitRadianAngle(AngleDiff);
|
|
AngleDiff = ABS(AngleDiff);
|
|
|
|
// Ignore vehicles outside 90 degrees when using the stick
|
|
if( bUsingStickInput && AngleDiff > HALF_PI )
|
|
{
|
|
return;
|
|
}
|
|
|
|
#define MAXANGLECONSIDERED (2.0f*PI)
|
|
|
|
const float fAngleScore = 1.0f - (AngleDiff / MAXANGLECONSIDERED);
|
|
const float fDistanceScore = (fDistanceLimit - Distance) / fDistanceLimit;
|
|
if( bUsingStickInput )
|
|
Closeness = (fDistanceScore * ms_Tunables.m_DistanceWeighting) + (fAngleScore * ms_Tunables.m_HeadingWeighting);
|
|
else
|
|
Closeness = (fDistanceScore * ms_Tunables.m_DistanceWeightingNoStick) + (fAngleScore * ms_Tunables.m_HeadingWeightingNoStick);
|
|
|
|
if (Closeness >= (*pCloseness) )
|
|
{
|
|
*pCloseness = Closeness;
|
|
*ppClosestAnimal = (CPed *)pEntity;
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : GetSpeed
|
|
// PURPOSE : Returns the speed of this player
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
Vector3 CPlayerInfo::GetSpeed(void)
|
|
{
|
|
if (m_pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && m_pPlayerPed->GetMyVehicle())
|
|
{
|
|
Assertf(m_pPlayerPed->GetMyVehicle(), "CPlayerInfo::GetSpeed - Player is not in a vehicle");
|
|
|
|
return (m_pPlayerPed->GetMyVehicle()->GetVelocity());
|
|
}
|
|
else
|
|
{
|
|
return (m_pPlayerPed->GetVelocity());
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : GetPos
|
|
// PURPOSE : Returns the coordinates of this player
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
Vector3 CPlayerInfo::GetPos(void)
|
|
{
|
|
if (m_pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && m_pPlayerPed->GetMyVehicle())
|
|
{
|
|
Assertf(m_pPlayerPed->GetMyVehicle(), "CPlayerInfo::GetPos - Player is not in a vehicle");
|
|
|
|
return (VEC3V_TO_VECTOR3(m_pPlayerPed->GetMyVehicle()->GetTransform().GetPosition()));
|
|
}
|
|
else
|
|
{
|
|
return (VEC3V_TO_VECTOR3(m_pPlayerPed->GetTransform().GetPosition()));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CAnimSpeedUps::ShouldUseMPAnimRates()
|
|
{
|
|
#if !__FINAL
|
|
return NetworkInterface::IsGameInProgress() || ms_Tunables.m_ForceMPAnimRatesInSP;
|
|
#else // !__FINAL
|
|
return NetworkInterface::IsGameInProgress();
|
|
#endif // !__FINAL
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : FindPlayerVehicle
|
|
// PURPOSE : Returns a pointer to the player car (or NULL if there isn't one)
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CVehicle *FindPlayerVehicle(CPlayerInfo* pPlayer, bool UNUSED_PARAM(bReturnRemoteVehicle))
|
|
{
|
|
// you can't use this in a non-update thread - you are dereffing an entity ptr!
|
|
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE)||CSystem::IsThisThreadId(SYS_THREAD_AUDIO));
|
|
|
|
if (!pPlayer)
|
|
pPlayer = CGameWorld::GetMainPlayerInfo();
|
|
|
|
Assert(pPlayer);
|
|
|
|
CPed* pPlayerPed = pPlayer ? pPlayer->GetPlayerPed() : NULL;
|
|
|
|
if (pPlayerPed)
|
|
{
|
|
if (pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
|
|
{
|
|
//RAGE if (!CReplay::ReplayGoingOn()) Assertf(CGameWorld::Players[Player].m_pPlayerPed->m_pMyVehicle, "FindPlayerVehicle - Player not in vehicle");
|
|
// if(bReturnRemoteVehicle && pPlayerPed->GetPlayerInfo()->GetRemoteVehicle())
|
|
// return pPlayerPed->GetPlayerInfo()->GetRemoteVehicle();
|
|
// else
|
|
return (pPlayerPed->GetMyVehicle());
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : FindPlayerPed
|
|
// PURPOSE : Returns a pointer to the player ped
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CPed *FindPlayerPed(CPlayerInfo* pPlayer)
|
|
{
|
|
// you can't use this in a non-update thread - you are dereffing an entity ptr!
|
|
// Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
|
|
if (pPlayer)
|
|
{
|
|
return pPlayer->GetPlayerPed();
|
|
}
|
|
|
|
return FindPlayerPed();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : FindPlayerPed
|
|
// PURPOSE : Returns a pointer to the main player ped
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CPed *FindPlayerPed()
|
|
{
|
|
// you can't use this in a non-update thread - you are dereffing an entity ptr!
|
|
// Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
|
|
return CGameWorld::FindLocalPlayer();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : FindSpectatorVehicle
|
|
// PURPOSE : Returns a pointer to the spectator car (or NULL if there isn't one)
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
CVehicle*
|
|
FindFollowVehicle(CPlayerInfo* pPlayer)
|
|
{
|
|
// you can't use this in a non-update thread - you are dereffing an entity ptr!
|
|
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE)||CSystem::IsThisThreadId(SYS_THREAD_AUDIO));
|
|
|
|
CPed* pPlayerPed = NULL;
|
|
|
|
if (!pPlayer)
|
|
{
|
|
CPed * player = CGameWorld::FindFollowPlayer();
|
|
pPlayer = player ? player->GetPlayerInfo() : NULL;
|
|
|
|
//We are spectating a Ped not a PlayerPed
|
|
if (player && !player->IsAPlayerPed() && NetworkInterface::IsInSpectatorMode())
|
|
{
|
|
pPlayerPed = player;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed = pPlayer->GetPlayerPed();
|
|
}
|
|
|
|
Assert(pPlayerPed);
|
|
|
|
if (pPlayerPed && pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
|
|
{
|
|
return (pPlayerPed->GetMyVehicle());
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : FindSpectatorPed
|
|
// PURPOSE : Returns a pointer to the spectator ped
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
CPed*
|
|
FindFollowPed(CPlayerInfo* pPlayer)
|
|
{
|
|
// you can't use this in a non-update thread - you are dereffing an entity ptr!
|
|
// Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
|
|
|
|
CPed* pPlayerPed = NULL;
|
|
|
|
if (!pPlayer)
|
|
{
|
|
CPed* player = CGameWorld::FindFollowPlayer();
|
|
pPlayer = player ? player->GetPlayerInfo() : NULL;
|
|
|
|
//We are spectating a Ped not a PlayerPed
|
|
if (player && !player->IsAPlayerPed() && NetworkInterface::IsInSpectatorMode())
|
|
{
|
|
pPlayerPed = player;
|
|
}
|
|
else if (pPlayer)
|
|
{
|
|
pPlayerPed = pPlayer->GetPlayerPed();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed = pPlayer->GetPlayerPed();
|
|
}
|
|
|
|
return pPlayerPed;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : IsRestartingAfterDeath
|
|
// PURPOSE : Should return TRUE if the player is restarting due to being killed.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
bool CPlayerInfo::IsRestartingAfterDeath(void)
|
|
{
|
|
if (this->m_PlayerState == PLAYERSTATE_HASDIED)
|
|
{
|
|
return (TRUE);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : IsRestartingAfterArrest
|
|
// PURPOSE : Not implemented yet. Should return TRUE if the player is restarting
|
|
// due to being arrested.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
bool CPlayerInfo::IsRestartingAfterArrest(void)
|
|
{
|
|
if (this->m_PlayerState == PLAYERSTATE_HASBEENARRESTED)
|
|
{
|
|
return (TRUE);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
void CPlayerInfo::KillPlayer( void )
|
|
{
|
|
m_PlayerState = CPlayerInfo::PLAYERSTATE_HASDIED;
|
|
}
|
|
|
|
void CPlayerInfo::ArrestPlayer( void )
|
|
{
|
|
m_PlayerState = CPlayerInfo::PLAYERSTATE_HASBEENARRESTED;
|
|
}
|
|
|
|
void CPlayerInfo::ResurrectPlayer( void )
|
|
{
|
|
//
|
|
// RESET PED STATE
|
|
//
|
|
|
|
//A clone always has its state updated via sync messages
|
|
if (!GetPlayerPed()->IsNetworkClone())
|
|
{
|
|
ResetSprintEnergy();
|
|
GetTargeting().ClearLockOnTarget();
|
|
GetPlayerPed()->SetInitialState();
|
|
|
|
if (m_PlayerState == CPlayerInfo::PLAYERSTATE_IN_MP_CUTSCENE)
|
|
{
|
|
NetworkInterface::SetInMPCutscene(false, false);
|
|
}
|
|
|
|
SetPlayerState(CPlayerInfo::PLAYERSTATE_PLAYING);
|
|
}
|
|
|
|
m_TimeOfPlayerStateChange = fwTimer::GetTimeInMilliseconds_NonScaledClipped();
|
|
|
|
m_fTimeBeenSprinting = 0.0f;
|
|
m_fSprintStaminaUpdatePeriod = 0.0f;
|
|
m_fSprintStaminaDurationMultiplier = 1.0f;
|
|
m_fSprintApplyDamageTimer = 0;
|
|
|
|
EnableAllControls();
|
|
SetPlayerControl(false, false);
|
|
|
|
// If the script has requested that player controls are not re-enabled on death
|
|
if(IsControlsScriptDisabled() && !bEnableControlOnDeath)
|
|
{
|
|
DisableControlsScript();
|
|
}
|
|
|
|
// Reset these as after the player has spawned they have left the world extents.
|
|
m_bHasWorldExtentsSpawnedAShark = false;
|
|
m_fSwimBoundsEscapeTimer = ms_Tunables.m_MaxTimeToTrespassWhileSwimmingBeforeDeath;
|
|
NOTFINAL_ONLY(ms_bFakeWorldExtentsTresspassing = false);
|
|
}
|
|
|
|
dev_float sfPlayerMaxRange = 40000.0f*40000.0f;
|
|
dev_float sfPlayerMaxRangeHealthLoss = 1.0f;
|
|
|
|
void CPlayerInfo::SetPlayerControl(bool bAmbientScript, bool bDisableControls, float ExtinguishRange, bool bClearTasks, bool bRemoveFiresAndProjectiles, bool bAllowDamage)
|
|
{
|
|
u32 iFlags = 0;
|
|
if(bAmbientScript) iFlags |= SPC_AmbientScript;
|
|
if(bDisableControls) iFlags |= SPC_DisableControls;
|
|
if(bClearTasks) iFlags |= SPC_ClearTasks;
|
|
if(bRemoveFiresAndProjectiles) iFlags |= (SPC_RemoveFires | SPC_RemoveExplosions | SPC_RemoveProjectiles);
|
|
if(bAllowDamage) iFlags |= SPC_AllowPlayerToBeDamaged;
|
|
|
|
SetPlayerControl(iFlags, ExtinguishRange);
|
|
}
|
|
|
|
void CPlayerInfo::SetPlayerControl(const u32 iFlags, const float fRemovalRange)
|
|
{
|
|
const bool bAmbientScript = ((iFlags&SPC_AmbientScript)!=0);
|
|
const bool bDisableControls = ((iFlags&SPC_DisableControls)!=0);
|
|
const bool bClearTasks = ((iFlags&SPC_ClearTasks)!=0);
|
|
const bool bRemoveFires = ((iFlags&SPC_RemoveFires)!=0);
|
|
const bool bRemoveExplosions = ((iFlags&SPC_RemoveExplosions)!=0);
|
|
const bool bRemoveProjectiles = ((iFlags&SPC_RemoveProjectiles)!=0);
|
|
const bool bDeactivateGadgets = ((iFlags&SPC_DeactivateGadgets)!=0);
|
|
const bool bLeaveCameraControlsOn = ((iFlags&SPC_LeaveCameraControlsOn)!=0);
|
|
const bool bAllowDamage = ((iFlags&SPC_AllowPlayerToBeDamaged)!=0);
|
|
const bool bDontStopOtherCarsAroundPlayer = ((iFlags&SPC_DontStopOtherCarsAroundPlayer)!=0);
|
|
const bool bAllowPadShake = ((iFlags&SPC_AllowPadShake)!=0);
|
|
if (bAmbientScript)
|
|
{
|
|
if (bDisableControls)
|
|
{
|
|
DisableControlsScriptAmbient();
|
|
}
|
|
else
|
|
{
|
|
EnableControlsScriptAmbient();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bDisableControls)
|
|
{
|
|
DisableControlsScript();
|
|
}
|
|
else
|
|
{
|
|
EnableControlsScript();
|
|
}
|
|
}
|
|
|
|
if ( IsControlsScriptDisabled() || IsControlsScriptAmbientDisabled() )
|
|
{
|
|
// If we haven't been told to prevent everybody from backing off, then set it to TRUE
|
|
if((iFlags & SPC_PreventEverybodyBackoff) == 0)
|
|
{
|
|
m_Wanted.m_EverybodyBackOff = TRUE;
|
|
}
|
|
|
|
// Stop cars so that their momentum doesn't get us in trouble.
|
|
CGameWorld::StopAllLawEnforcersInTheirTracks();
|
|
if (!bAllowPadShake)
|
|
{
|
|
CControlMgr::StopPadsShaking();
|
|
}
|
|
//m_pPlayerPed->m_nPhysicalFlags.bNotDamagedByAnything = !bAllowDamage;
|
|
|
|
m_pPlayerPed->GetPlayerInfo()->SetPlayerCanBeDamaged(bAllowDamage);
|
|
if(bAllowDamage)
|
|
{
|
|
m_DamageAllowedFromSetPlayerControl = true;
|
|
}
|
|
|
|
if(m_fSprintEnergy < 0.0f)
|
|
m_fSprintEnergy = 0.0f;
|
|
|
|
// Get the player to quit all his tasks
|
|
// (a networked player controlled by another machine will not be running any tasks)
|
|
if( bClearTasks && !m_pPlayerPed->IsNetworkClone())
|
|
{
|
|
m_pPlayerPed->GetPedIntelligence()->ClearTasks(true,false);
|
|
}
|
|
|
|
// Network players are sometimes remaining on fire when they are respawned - so extinguish the fire directly
|
|
g_fireMan.ExtinguishEntityFires(m_pPlayerPed, true);
|
|
|
|
if(bRemoveFires)
|
|
{
|
|
// Extinguish all fires in area
|
|
// Only remove fires within 10m from player.
|
|
// It looks bad when large fires just extinguish. (If it is dangerous change back to 4000, Obbe)
|
|
Vector3 pos = GetPos();
|
|
g_fireMan.ExtinguishArea(RCC_VEC3V(pos), fRemovalRange, true);
|
|
CGameWorld::ExtinguishAllCarFiresInArea(GetPos(), fRemovalRange, true);
|
|
}
|
|
if(bRemoveExplosions)
|
|
{
|
|
// Remove all explosions in area
|
|
CExplosionManager::RemoveExplosionsInArea(EXP_TAG_DONTCARE, GetPos(), 4000.0f);
|
|
}
|
|
if(bRemoveProjectiles)
|
|
{
|
|
// Remove all projectiles in the world
|
|
CProjectileManager::RemoveAllProjectiles();
|
|
}
|
|
if (bDontStopOtherCarsAroundPlayer)
|
|
{
|
|
m_bStopNearbyVehicles = false;
|
|
}
|
|
|
|
m_pPlayerPed->GetPlayerInfo()->GetTargeting().ClearLockOnTarget();
|
|
|
|
// Disable any pending bullets
|
|
CTaskAimGunOnFoot* pAimGunOnFootTask = static_cast<CTaskAimGunOnFoot*>(m_pPlayerPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_AIM_GUN_ON_FOOT));
|
|
if(pAimGunOnFootTask)
|
|
{
|
|
pAimGunOnFootTask->ResetWillFire();
|
|
}
|
|
}
|
|
else if ( !IsControlsScriptDisabled() && !IsControlsScriptAmbientDisabled() )
|
|
{
|
|
m_Wanted.m_EverybodyBackOff = FALSE;
|
|
m_pPlayerPed->m_nPhysicalFlags.bNotDamagedByAnything = FALSE;
|
|
m_pPlayerPed->GetPlayerInfo()->SetPlayerCanBeDamaged(TRUE);
|
|
m_bStopNearbyVehicles = true;
|
|
}
|
|
|
|
// Handle deactivating all the player's equipped gadgets
|
|
if(bDeactivateGadgets)
|
|
{
|
|
weaponAssert(m_pPlayerPed->GetWeaponManager());
|
|
m_pPlayerPed->GetWeaponManager()->DestroyEquippedGadgets();
|
|
}
|
|
|
|
// If reenabling controls, always reenable camera controls
|
|
if (!bDisableControls)
|
|
{
|
|
EnableControlsCamera();
|
|
}
|
|
else
|
|
{
|
|
if(!bLeaveCameraControlsOn)
|
|
DisableControlsCamera();
|
|
else
|
|
EnableControlsCamera();
|
|
}
|
|
}
|
|
|
|
bool CPlayerInfo::AreAnyControlsOtherThanFrontendDisabled()
|
|
{
|
|
#if !GTA_REPLAY
|
|
Assertf(NetworkInterface::IsGameInProgress(), "CPlayerInfo::AreAnyControlsOtherThanFrontendDisabled - only expected this to be called during network games");
|
|
#endif
|
|
|
|
u32 TempControlsFlag = (DisableControls & ~DISABLE_FRONTEND);
|
|
return (TempControlsFlag != 0);
|
|
}
|
|
|
|
//
|
|
// name: AddHealth
|
|
// description: Adds a certain amount of health to this player taking into account the maximum health
|
|
//
|
|
void CPlayerInfo::AddHealth(s32 Amount)
|
|
{
|
|
Assert(m_pPlayerPed);
|
|
|
|
m_pPlayerPed->SetHealth(Clamp(m_pPlayerPed->GetHealth() + Amount, m_pPlayerPed->GetHealth(), (float)MaxHealth));
|
|
}
|
|
|
|
//
|
|
// name: SetPlayerState
|
|
// description: Sets the new player state and also executes a bit of logic related to player state changes.
|
|
//
|
|
void
|
|
CPlayerInfo::SetPlayerState(const State newState)
|
|
{
|
|
if (newState != m_PlayerState)
|
|
{
|
|
if (m_pPlayerPed)
|
|
{
|
|
if(m_pPlayerPed->GetNetworkObject())
|
|
{
|
|
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetPlayerMgr().GetLog(), "NEW_PLAYER_STATE", "%s", m_pPlayerPed->GetNetworkObject()->GetLogName());
|
|
|
|
if (m_pPlayerPed->IsLocalPlayer() && m_PlayerState == PLAYERSTATE_IN_MP_CUTSCENE && newState != PLAYERSTATE_PLAYING)
|
|
{
|
|
Assertf(0, "Trying to alter the state of the local player during an MP cutscene");
|
|
return;
|
|
}
|
|
|
|
switch (newState)
|
|
{
|
|
case PLAYERSTATE_INVALID:
|
|
NetworkInterface::GetPlayerMgr().GetLog().WriteDataValue("State", "Invalid");
|
|
break;
|
|
case PLAYERSTATE_PLAYING:
|
|
NetworkInterface::GetPlayerMgr().GetLog().WriteDataValue("State", "Playing");
|
|
break;
|
|
case PLAYERSTATE_HASDIED:
|
|
NetworkInterface::GetPlayerMgr().GetLog().WriteDataValue("State", "Died");
|
|
break;
|
|
case PLAYERSTATE_HASBEENARRESTED:
|
|
NetworkInterface::GetPlayerMgr().GetLog().WriteDataValue("State", "Arrested");
|
|
break;
|
|
case PLAYERSTATE_FAILEDMISSION:
|
|
NetworkInterface::GetPlayerMgr().GetLog().WriteDataValue("State", "Failed Mission");
|
|
break;
|
|
case PLAYERSTATE_LEFTGAME:
|
|
NetworkInterface::GetPlayerMgr().GetLog().WriteDataValue("State", "Left Game");
|
|
break;
|
|
case PLAYERSTATE_RESPAWN:
|
|
NetworkInterface::GetPlayerMgr().GetLog().WriteDataValue("State", "Respawn");
|
|
break;
|
|
case PLAYERSTATE_IN_MP_CUTSCENE:
|
|
NetworkInterface::GetPlayerMgr().GetLog().WriteDataValue("State", "In MP Cutscene");
|
|
break;
|
|
default:
|
|
Assertf(0, "Unrecognised player state");
|
|
}
|
|
}
|
|
}
|
|
|
|
if( m_PlayerState == PLAYERSTATE_RESPAWN ||
|
|
(m_pPlayerPed && m_pPlayerPed->IsNetworkClone() && m_PlayerState == PLAYERSTATE_HASDIED) )
|
|
{
|
|
m_TimeOfPlayerLastRespawn = fwTimer::GetTimeInMilliseconds_NonScaledClipped();
|
|
}
|
|
|
|
m_PlayerState = newState;
|
|
m_TimeOfPlayerStateChange = fwTimer::GetTimeInMilliseconds_NonScaledClipped();
|
|
}
|
|
}
|
|
|
|
const char*
|
|
CPlayerInfo::PlayerStateToString() const
|
|
{
|
|
const char* state = NULL;
|
|
|
|
switch (m_PlayerState)
|
|
{
|
|
case PLAYERSTATE_INVALID: state = "Invalid"; break;
|
|
case PLAYERSTATE_PLAYING: state = "Playing"; break;
|
|
case PLAYERSTATE_HASDIED: state = "Died"; break;
|
|
case PLAYERSTATE_HASBEENARRESTED: state = "Arrested"; break;
|
|
case PLAYERSTATE_FAILEDMISSION: state = "Failed Mission"; break;
|
|
case PLAYERSTATE_LEFTGAME: state = "Left Game"; break;
|
|
case PLAYERSTATE_RESPAWN: state = "Respawn"; break;
|
|
case PLAYERSTATE_IN_MP_CUTSCENE: state = "In MP Cutscene"; break;
|
|
default:
|
|
state = "Unknown";
|
|
break;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
bool CPlayerInfo::GetHasRespawnedWithinTime(u32 uTimeLastRespawnedCheck) const
|
|
{
|
|
if(m_TimeOfPlayerLastRespawn != 0)
|
|
{
|
|
u32 timeSinceLastHappened;
|
|
u32 currTime = fwTimer::GetTimeInMilliseconds_NonScaledClipped();
|
|
|
|
// handle the time wrapping
|
|
if (currTime < m_TimeOfPlayerLastRespawn)
|
|
{
|
|
timeSinceLastHappened = (MAX_UINT32 - m_TimeOfPlayerLastRespawn) + currTime;
|
|
}
|
|
else
|
|
{
|
|
timeSinceLastHappened = currTime - m_TimeOfPlayerLastRespawn;
|
|
}
|
|
|
|
return timeSinceLastHappened < uTimeLastRespawnedCheck;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//Player Reset Flags
|
|
/////////////////////////////////////////////////////////////////
|
|
CPlayerResetFlags::CPlayerResetFlags()
|
|
{
|
|
m_iPlayerResetFlags = 0;
|
|
|
|
#if RSG_PC
|
|
for(int i = 0; i < fwRandom::GetRandomNumberInRange(MIN_NUM_SYSOBF_LINKS, MAX_NUM_SYSOBF_LINKS); i++)
|
|
{
|
|
m_iPlayerResetFlags.AddLink(rage_new sysLinkedData<u32, ReportPlayerResetMismatch>(m_iPlayerResetFlags));
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
CPlayerResetFlags::~CPlayerResetFlags()
|
|
{
|
|
}
|
|
|
|
void CPlayerResetFlags::ResetPreAI()
|
|
{
|
|
UnsetFlag(PRF_ASSISTED_AIMING_ON);
|
|
UnsetFlag(PRF_RUN_AND_GUN);
|
|
UnsetFlag(PRF_NO_RETICULE_AIM_ASSIST_ON);
|
|
UnsetFlag(PRF_DISABLE_AIM_CAMERA);
|
|
UnsetFlag(PRF_DISABLE_CAMERA_VIEW_MODE_CYCLE);
|
|
UnsetFlag(PRF_CAMERA_VIEW_MODE_SWITCHED_TO_OR_FROM_FIRST_PERSON);
|
|
UnsetFlag(PRF_CAMERA_VIEW_MODE_SWITCHED_TO_FIRST_PERSON);
|
|
UnsetFlag(PRF_DISABLE_CAN_USE_COVER);
|
|
UnsetFlag(PRF_DOT_SHOCKED);
|
|
UnsetFlag(PRF_DOT_CHOKING);
|
|
}
|
|
|
|
void CPlayerResetFlags::ResetPostAI()
|
|
{
|
|
UnsetFlag(PRF_NOT_ALLOWED_TO_ENTER_ANY_CAR); //A script controlled flag.
|
|
UnsetFlag(PRF_FORCED_ZOOM); //A script controlled flag.
|
|
UnsetFlag(PRF_FORCED_AIMING);
|
|
UnsetFlag(PRF_DISABLE_HEALTH_RECHARGE); //A script controlled flag.
|
|
UnsetFlag(PRF_EXPLOSIVE_AMMO_ON); //A script controlled flag.
|
|
UnsetFlag(PRF_FIRE_AMMO_ON); //A script controlled flag.
|
|
UnsetFlag(PRF_EXPLOSIVE_MELEE_ON); //A script controlled flag.
|
|
UnsetFlag(PRF_SUPER_JUMP_ON); //A script controlled flag.
|
|
UnsetFlag(PRF_INCREASE_JUMP_SUPPRESSION_RANGE); //A script controlled flag.
|
|
UnsetFlag(PRF_DISABLE_VEHICLE_REWARDS); //A script controlled flag.
|
|
UnsetFlag(PRF_BEAST_JUMP_ON); //A script controlled flag.
|
|
UnsetFlag(PRF_FORCED_JUMP); //A script controlled flag.
|
|
}
|
|
|
|
void CPlayerResetFlags::ResetPostPhysics()
|
|
{
|
|
// Script controlled flags
|
|
UnsetFlag(PRF_USE_COVER_THREAT_WEIGHTING);
|
|
UnsetFlag(PRF_DISABLE_DISPATCHED_HELI_REFUEL);
|
|
UnsetFlag(PRF_DISABLE_DISPATCHED_HELI_DESTROYED_SPAWN_DELAY);
|
|
UnsetFlag(PRF_PREFER_REAR_SEATS);
|
|
UnsetFlag(PRF_PREFER_FRONT_PASSENGER_SEAT);
|
|
}
|
|
|
|
/*
|
|
void CPlayerPedData::AllocateData()
|
|
{
|
|
if(m_Wanted == NULL)
|
|
m_Wanted = rage_new CWanted();
|
|
m_Wanted->Initialise();
|
|
|
|
//RAGE if(m_pClothes == NULL)
|
|
// m_pClothes = new CPedClothesDesc;
|
|
// m_pClothes->Initialise();
|
|
}
|
|
|
|
void CPlayerPedData::DeAllocateData()
|
|
{
|
|
if (m_Wanted)
|
|
{
|
|
delete m_Wanted;
|
|
}
|
|
m_Wanted = NULL;
|
|
|
|
//RAGE if (m_pClothes)
|
|
// {
|
|
// delete m_pClothes;
|
|
// }
|
|
// m_pClothes = NULL;
|
|
}
|
|
*/
|
|
|
|
// Static member initialisation
|
|
CPlayerInfo::Tunables CPlayerInfo::ms_Tunables;
|
|
IMPLEMENT_PLAYER_INFO_TUNABLES(CPlayerInfo, 0x3ef71716);
|
|
|
|
void CPlayerInfo::ResetSprintEnergy()
|
|
{
|
|
m_fSprintEnergy = GetPlayerDataMaxSprintEnergy(); //CStatsMgr::GetFatAndMuscleModifier(STAT_MODIFIER_SPRINT_ENERGY);
|
|
}
|
|
|
|
void CPlayerInfo::RestoreSprintEnergy(float fPercent)
|
|
{
|
|
if ( aiVerify(fPercent >= 0.f && fPercent <= 100.f) )
|
|
{
|
|
const float fMaxSprintEnergy = GetPlayerDataMaxSprintEnergy();
|
|
m_fSprintEnergy += fMaxSprintEnergy * fPercent;
|
|
if (m_fSprintEnergy >= fMaxSprintEnergy)
|
|
{
|
|
m_fSprintEnergy = fMaxSprintEnergy;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool CPlayerInfo::HandleSprintEnergy(bool bSprint, float fRate)
|
|
{
|
|
if (m_pPlayerPed && m_pPlayerPed->GetUsingRagdoll())
|
|
return true;
|
|
|
|
const float fTimeStep = fwTimer::GetTimeStep();
|
|
if(bSprint)
|
|
{
|
|
const CPlayerSpecialAbility* pAbility = m_pPlayerPed ? m_pPlayerPed->GetSpecialAbility() : NULL;
|
|
bool bHasRageAbilityEquipped = pAbility && pAbility->GetType() == SAT_RAGE && pAbility->IsActive();
|
|
bool bHasSprintSpeedBoostAbilityEquipped = pAbility && pAbility->GetType() == SAT_SPRINT_SPEED_BOOST && pAbility->IsActive();
|
|
const bool bDoesNotGetTiredResetFlag = m_pPlayerPed ? m_pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_DontUseSprintEnergy) : false;
|
|
|
|
// Sprint indefinitely if full stamina stat in B*983326
|
|
if(bDoesNotGetTiredResetFlag || bDoesNotGetTired || fRate==0.0f || bHasRageAbilityEquipped || bHasSprintSpeedBoostAbilityEquipped
|
|
|| StatsInterface::GetIntStat(STAT_STAMINA.GetStatId()) == 100 || GetArcadeAbilityEffects().GetIsActive(AAE_MARATHON_MAN)
|
|
#if !__FINAL
|
|
|| CPlayerInfo::ms_bDebugPlayerInfiniteStamina
|
|
#endif // !__FINAL
|
|
)
|
|
{
|
|
// CNC speed boost ability: instantly replenish sprint energy.
|
|
if (bHasSprintSpeedBoostAbilityEquipped)
|
|
{
|
|
m_fSprintEnergy = GetPlayerDataMaxSprintEnergy();
|
|
}
|
|
|
|
// can sprint but doesn't use any energy
|
|
return true;
|
|
}
|
|
else if (m_fSprintEnergy > 0.0f)
|
|
{
|
|
m_fTimeBeenSprinting += fTimeStep;
|
|
m_fSprintEnergy = rage::Max(0.0f, m_fSprintEnergy - fRate * fTimeStep);
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Increase sprint energy
|
|
const float fMaxSprintEnergy = GetPlayerDataMaxSprintEnergy();
|
|
if (m_fSprintEnergy < fMaxSprintEnergy)
|
|
{
|
|
m_fTimeBeenSprinting = 0.0f;
|
|
|
|
const bool bOnBicyle = m_pPlayerPed ? (m_pPlayerPed->GetMyVehicle() && m_pPlayerPed->GetMyVehicle()->InheritsFromBicycle()) : false;
|
|
float fReplenishRateMultiplier = bOnBicyle ? ms_Tunables.m_SprintReplenishRateMultiplierBike : ms_Tunables.m_SprintReplenishRateMultiplier;
|
|
|
|
// CNC: Increase replenish rate slightly.
|
|
if (CPlayerInfo::AreCNCResponsivenessChangesEnabled(m_pPlayerPed))
|
|
{
|
|
fReplenishRateMultiplier *= ms_fStaminaDepletionRateModifierCNC;
|
|
}
|
|
|
|
m_fSprintEnergy += fRate * fTimeStep * fReplenishRateMultiplier;
|
|
m_fSprintEnergy = rage::Clamp(m_fSprintEnergy, 0.0f, fMaxSprintEnergy);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Accessor function for the stealth multiplier, which is different depending on the state of the player.
|
|
float CPlayerInfo::GetStealthMultiplier() const
|
|
{
|
|
const CPed* pPed = GetPlayerPed();
|
|
|
|
if (Verifyf(pPed, "No player ped in the player info!"))
|
|
{
|
|
return pPed->GetMotionData()->GetUsingStealth() ? GetSneakingStealthMultiplier() : GetNormalStealthMultiplier();
|
|
}
|
|
else
|
|
{
|
|
return GetNormalStealthMultiplier();
|
|
}
|
|
}
|
|
|
|
float PLAYER_SPRINT_THRESHOLD = 5.0f;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPlayerInfo::GetDefaultHoldBreathDurationForPed(const CPed& UNUSED_PARAM(ped)) const
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPlayerInfo::ComputeSprintDepletionRateForPed(const CPed& ped, float fMaxSprintEnergy, eSprintType nSprintType) const
|
|
{
|
|
float fSprintDuration = 10.0f;
|
|
float fMinSprintDuration = 10.0f;
|
|
float fMaxSprintDuration = 10.0f;
|
|
float fSprintStat = 0.0f;
|
|
|
|
if (GetStatValuesForSprintType(ped, fSprintStat, fMinSprintDuration, fMaxSprintDuration, nSprintType))
|
|
{
|
|
fSprintDuration = ComputeDurationFromStat(fSprintStat, fMinSprintDuration, fMaxSprintDuration);
|
|
}
|
|
|
|
float fDepletionRate = fMaxSprintEnergy / fSprintDuration;
|
|
const CPlayerSpecialAbility* pSpecialAbility = ped.GetSpecialAbility();
|
|
|
|
if (pSpecialAbility && pSpecialAbility->IsActive())
|
|
{
|
|
if (pSpecialAbility->GetStaminaMultiplier() > 0.0f)
|
|
{
|
|
controlDebugf1("Adjusting sprint energy depletion rate for ped: fDepletionRate (%f) * fStaminaMultiplier (%f)", fDepletionRate, pSpecialAbility->GetStaminaMultiplier());
|
|
|
|
fDepletionRate *= pSpecialAbility->GetStaminaMultiplier();
|
|
}
|
|
}
|
|
|
|
if (CPlayerInfo::AreCNCResponsivenessChangesEnabled(m_pPlayerPed))
|
|
{
|
|
fDepletionRate *= ms_fStaminaReplenishRateModifierCNC;
|
|
controlDebugf3("Adjusting sprint energy depletion rate for ped (CNC Multiplier): fDepletionRate (%f) * ms_fStaminaReplenishRateModifierCNC (%f)", fDepletionRate, ms_fStaminaReplenishRateModifierCNC);
|
|
}
|
|
|
|
return fDepletionRate;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CPlayerInfo::ProcessCanSprint(CPed& ped, const bool bCheckDisabled)
|
|
{
|
|
CVehicle* pVeh = ped.GetVehiclePedInside();
|
|
const bool bOnBicycle = pVeh && pVeh->GetVehicleType()==VEHICLE_TYPE_BICYCLE;
|
|
const float fReplenishFinishedPercentage = bOnBicycle ? ms_Tunables.m_SprintReplenishFinishedPercentageBicycle : ms_Tunables.m_SprintReplenishFinishedPercentage;
|
|
// If we're currently replenishing our sprint energy and we've recovered more then the required percentage to stop replenishing
|
|
// then do so (and allow sprinting again)
|
|
if (m_bSprintReplenishing && m_fSprintEnergy > m_fMaxSprintEnergy * fReplenishFinishedPercentage)
|
|
{
|
|
m_bSprintReplenishing = false;
|
|
}
|
|
|
|
bool bInFpsMode = false;
|
|
#if FPS_MODE_SUPPORTED
|
|
bInFpsMode = ped.IsFirstPersonShooterModeEnabledForPlayer(false);
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
// Sprinting can be disabled externally (via script for instance)
|
|
if (m_bPlayerSprintDisabled && bCheckDisabled)
|
|
{
|
|
return false;
|
|
}
|
|
// Can disable sprinting if in an interior, don't do this if on a bicycle B*2014189
|
|
else if (!ped.GetPedConfigFlag(CPED_CONFIG_FLAG_IgnoreInteriorCheckForSprinting) && !bOnBicycle && CTaskMovePlayer::ms_bDefaultNoSprintingInInteriors && ped.m_nFlags.bInMloRoom && !ped.GetPortalTracker()->IsAllowedToRunInInterior() // not generally allowed to sprint in interiors
|
|
#if FPS_MODE_SUPPORTED
|
|
&& (!bInFpsMode || !CTaskPlayerOnFoot::sm_Tunables.m_AllowFPSAnalogStickRunInInteriors)
|
|
#endif // FPS_MODE_SUPPORTED
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
//! Disable sprinting if aiming in 1st person.
|
|
if(bInFpsMode)
|
|
{
|
|
if(IsAiming() || IsFiring())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(ped.GetPedResetFlag(CPED_RESET_FLAG_IsReloading))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// CNC speed boost ability: skip energy checks if ability is active.
|
|
const CPlayerSpecialAbility* pAbility = m_pPlayerPed ? m_pPlayerPed->GetSpecialAbility() : NULL;
|
|
bool bHasSprintSpeedBoostAbilityEquipped = pAbility && pAbility->GetType() == SAT_SPRINT_SPEED_BOOST && pAbility->IsActive();
|
|
|
|
if (!bDoesNotGetTired && !ped.GetPedResetFlag(CPED_RESET_FLAG_DisableSprintDamage) && !bHasSprintSpeedBoostAbilityEquipped)
|
|
{
|
|
TUNE_GROUP_BOOL(SPRINT_TUNE, NO_DAMAGE_AND_NO_SPRINT_IN_MP, true);
|
|
if (NO_DAMAGE_AND_NO_SPRINT_IN_MP && NetworkInterface::IsGameInProgress())
|
|
{
|
|
return !m_bSprintReplenishing;
|
|
}
|
|
|
|
if (m_fSprintApplyDamageTimer > 0.0f)
|
|
{
|
|
m_fSprintApplyDamageTimer -= fwTimer::GetTimeStep();
|
|
}
|
|
else if (m_fSprintEnergy <=0.0f && !ped.GetUsingRagdoll())
|
|
{
|
|
static dev_float s_fOverSprintDamage = 2.5f;
|
|
static dev_float s_fOverSprintDamageInterval = 0.75f;
|
|
TUNE_GROUP_FLOAT(SPRINT_DEBUG, fExhaustionNmHealthThreshold, 8.0f, 0.0f, 100.0f, 0.001f);
|
|
if ((ped.GetHealth()-s_fOverSprintDamage)>(ped.GetInjuredHealthThreshold()+fExhaustionNmHealthThreshold))
|
|
{
|
|
//Apply health damage B* 988313
|
|
u32 nWeaponUsedHash = WEAPONTYPE_EXHAUSTION;
|
|
m_fSprintApplyDamageTimer = s_fOverSprintDamageInterval;
|
|
CPedDamageCalculator damageCalculator(NULL,s_fOverSprintDamage,nWeaponUsedHash, 0, false);
|
|
CEventDamage event(NULL, fwTimer::GetTimeInMilliseconds(), nWeaponUsedHash);
|
|
damageCalculator.ApplyDamageAndComputeResponse(&ped, event.GetDamageResponseData(), CPedDamageCalculator::DF_None);
|
|
bool bWasKilledOrInjured = (event.GetDamageResponseData().m_bKilled || event.GetDamageResponseData().m_bInjured);
|
|
if( bWasKilledOrInjured )
|
|
{
|
|
ped.GetPedIntelligence()->AddEvent(event);
|
|
}
|
|
}
|
|
else if (CTaskNMBehaviour::CanUseRagdoll(&ped, RAGDOLL_TRIGGER_FALL))
|
|
{
|
|
TUNE_GROUP_INT(SPRINT_DEBUG, iMinExhaustionNmTime, 3000, 0, 10000, 1);
|
|
if (bOnBicycle && pVeh->GetDriver()==&ped)
|
|
{
|
|
// do the jump roll from road vehicle behaviour
|
|
CTaskNMJumpRollFromRoadVehicle* pRollTask = rage_new CTaskNMJumpRollFromRoadVehicle(iMinExhaustionNmTime, 10000, false, false, atHashString::Null(), pVeh);
|
|
CEventSwitch2NM event(10000, pRollTask,false, iMinExhaustionNmTime);
|
|
ped.GetPedIntelligence()->AddEvent(event);
|
|
|
|
// Let the ragdoll damage system know that it should go easy on the damage for a bit.
|
|
ped.SetPedConfigFlag(CPED_CONFIG_FLAG_ThrownFromVehicleDueToExhaustion, true);
|
|
ped.SetExhaustionDamageTimer(fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
else
|
|
{
|
|
// do the ragdoll exhaustion nm behaviour
|
|
CTaskNMHighFall* pFallTask = rage_new CTaskNMHighFall(iMinExhaustionNmTime, NULL, CTaskNMHighFall::HIGHFALL_SPRINT_EXHAUSTED, NULL, true);
|
|
CEventSwitch2NM event(10000, pFallTask,false, iMinExhaustionNmTime);
|
|
ped.GetPedIntelligence()->AddEvent(event);
|
|
}
|
|
|
|
TUNE_GROUP_FLOAT(SPRINT_DEBUG, fExhaustionNmEnergyRecoverMult, 0.01f, 0.0f, 1.0f, 0.001f);
|
|
// restore a little sprint energy so the nm behaviour doesn't get immediately kicked off again
|
|
m_fSprintEnergy = GetPlayerDataMaxSprintEnergy()*fExhaustionNmEnergyRecoverMult;
|
|
}
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Don't yet have the minimum energy to sprint
|
|
// else if (m_bSprintReplenishing)
|
|
// {
|
|
// return false;
|
|
// }
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
float CPlayerInfo::ProcessSprintControl(const CControl& control, const CPed& FPS_MODE_SUPPORTED_ONLY(ped), eSprintType nSprintType, bool bUseButtonBashing)
|
|
{
|
|
bool bIsSprintButtonPressed;
|
|
bool bIsSprintButtonDown;
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
bool bFirstPersonRunToggleInsideStickLimits = true;
|
|
#endif
|
|
|
|
bool bUsingKeyboardAndMouse = false;
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
bUsingKeyboardAndMouse = control.WasKeyboardMouseLastKnownSource();
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
|
|
// B*2259601: Allow sprint breakout to strafe if player is using an FPS control scheme (not just if scheme uses ToggleRun).
|
|
bool bUsingFPSControlLayoutInFirstPerson = ped.IsFirstPersonShooterModeEnabledForPlayer(false) && camFirstPersonShooterCamera::WhenSprintingUseRightStick(&ped, control);
|
|
|
|
if (nSprintType == SPRINT_ON_BICYCLE)
|
|
{
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
if(bUsingKeyboardAndMouse)
|
|
{
|
|
bIsSprintButtonDown = control.GetVehiclePushbikeSprintIsDown();
|
|
bIsSprintButtonPressed = false;
|
|
|
|
// B*2254606: Simulate a sprint press every 150ms so sprint/energy behaves like button bashing.
|
|
static dev_u32 uTimeBetweenPresses = 150;
|
|
if (bIsSprintButtonDown && (fwTimer::GetTimeInMilliseconds() > m_uTimeBikeSprintPressed + uTimeBetweenPresses))
|
|
{
|
|
m_uTimeBikeSprintPressed = fwTimer::GetTimeInMilliseconds();
|
|
bIsSprintButtonPressed = true;
|
|
}
|
|
}
|
|
else
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
{
|
|
bIsSprintButtonPressed = control.GetVehiclePushbikePedal().IsPressed();
|
|
bIsSprintButtonDown = control.GetVehiclePushbikePedal().IsDown();
|
|
}
|
|
}
|
|
else if(control.CanUseToggleRun() || bUsingFPSControlLayoutInFirstPerson)
|
|
{
|
|
// The keyboard controls works differently to the controller for sprinting. Tapping the button toggles run/walk whilst holding sprints.
|
|
// use use CControl::KEYBOARD_START_SPRINT_HELD_TIME to distinguish when we should sprint.
|
|
bIsSprintButtonPressed = control.GetValue(INPUT_SPRINT).IsPressed();
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
if(ped.IsFirstPersonShooterModeEnabledForPlayer(false) && !ped.GetMotionData()->GetUsingStealth())
|
|
{
|
|
static dev_float s_fSprintThreshold = 0.75f;
|
|
float fStickY = -control.GetPedWalkUpDown().GetNorm();
|
|
bFirstPersonRunToggleInsideStickLimits = fStickY > s_fSprintThreshold;
|
|
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
// B*2259601: PC: Clear sprint if player is trying to strafe.
|
|
if (bUsingKeyboardAndMouse)
|
|
{
|
|
static dev_u32 uDurationReleased = 100;
|
|
bool bPressingStrafeLeft = control.GetValue(INPUT_MOVE_LEFT_ONLY).IsPressed() || control.GetValue(INPUT_MOVE_LEFT_ONLY).IsDown() || control.GetValue(INPUT_MOVE_LEFT_ONLY).HistoryReleased(uDurationReleased);
|
|
bool bPressingStrafeRight = control.GetValue(INPUT_MOVE_RIGHT_ONLY).IsPressed() || control.GetValue(INPUT_MOVE_RIGHT_ONLY).IsDown() || control.GetValue(INPUT_MOVE_RIGHT_ONLY).HistoryReleased(uDurationReleased);
|
|
bFirstPersonRunToggleInsideStickLimits = !bPressingStrafeLeft && !bPressingStrafeRight;
|
|
}
|
|
#endif //KEYBOARD_MOUSE_SUPPORT
|
|
|
|
// B*2312353: PC FPS: Check input is actually down (and has been down for 0.1ms) for sprinting with mouse/keyboard (ignore the "m_ToggleRunOn" value). Allows us to toggle between run/walk without flickering to sprint for a few frames.
|
|
static dev_u32 fSprintWasDownTime = 100;
|
|
bool bSprintInputDown = bUsingKeyboardAndMouse ? (control.GetPedSprint().IsDown() && control.GetPedSprintHistoryHeldDown(fSprintWasDownTime)) : control.GetPedSprintIsDown();
|
|
bIsSprintButtonDown = bSprintInputDown && bFirstPersonRunToggleInsideStickLimits;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
bIsSprintButtonDown = control.GetValue(INPUT_SPRINT).HistoryHeldDown(CControl::KEYBOARD_START_SPRINT_HELD_TIME);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Use the inputs directly here as we are interested in the state of the actual buttons not the toggle state!
|
|
bIsSprintButtonPressed = control.GetValue(INPUT_SPRINT).IsPressed();
|
|
bIsSprintButtonDown = control.GetValue(INPUT_SPRINT).IsDown();
|
|
}
|
|
|
|
const sSprintControlData& rSprintControlData = ms_Tunables.m_SprintControlData[nSprintType];
|
|
|
|
// If we allow button bashing to control sprint behavior, fill or reduce the sprint meter depending on what the player is doing
|
|
// On PC we do not use button mashing for the ped due to the placement of keys on the keyboard.
|
|
if ( (bUseButtonBashing && !control.CanUseToggleRun() && bFirstPersonRunToggleInsideStickLimits && !bUsingKeyboardAndMouse) || (nSprintType == SPRINT_ON_BICYCLE))
|
|
{
|
|
// Just tapped sprint button, add to meter
|
|
if (bIsSprintButtonPressed)
|
|
{
|
|
m_fSprintControlCounter = rage::Min(rSprintControlData.m_MaxLimit, m_fSprintControlCounter + rSprintControlData.m_TapAdd);
|
|
}
|
|
// Holding sprint button down, reduce slightly
|
|
else if (bIsSprintButtonDown)
|
|
{
|
|
m_fSprintControlCounter = rage::Max(1.0f, m_fSprintControlCounter - rSprintControlData.m_HoldSub * fwTimer::GetTimeStep());
|
|
}
|
|
// Released sprint button, reduce if not empty
|
|
else if (m_fSprintControlCounter > 0.0f)
|
|
{
|
|
m_fSprintControlCounter = rage::Max(0.0f, m_fSprintControlCounter - rSprintControlData.m_ReleaseSub * fwTimer::GetTimeStep());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not using bashing, just set to max, no finger pain
|
|
m_fSprintControlCounter = bIsSprintButtonDown ? rSprintControlData.m_MaxLimit : 0.0f;
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
const bool bWeaponWheelIsActive = CNewHud::IsWeaponWheelActive();
|
|
if( ped.IsFirstPersonShooterModeEnabledForPlayer(false) &&
|
|
nSprintType != SPRINT_ON_BICYCLE &&
|
|
!ped.GetMotionData()->GetUsingStealth() &&
|
|
((!control.CanUseToggleRun() && !bUsingFPSControlLayoutInFirstPerson) || bFirstPersonRunToggleInsideStickLimits) )
|
|
{
|
|
TUNE_GROUP_INT(FIRST_PERSON_TUNE, SPRINT_AIM_DELAY, 100, 0, 1000, 100);
|
|
bool bForceMaxLimit = control.GetValue(INPUT_SPRINT).HistoryHeldDown( SPRINT_AIM_DELAY ) ||
|
|
(control.GetValue(INPUT_SPRINT).HistoryPressed( SPRINT_AIM_DELAY ) && !bUsingKeyboardAndMouse) ||
|
|
( bWeaponWheelIsActive && CPedMotionData::GetIsSprinting(ped.GetMotionData()->GetCurrentMbrY()) );
|
|
|
|
// B*2083102: Don't force max sprint limits if weapon wheel is open and sprint button isn't down/pressed (when not using Standard FPS controls, ie toggle run).
|
|
if (bForceMaxLimit && bWeaponWheelIsActive && !control.CanUseToggleRun() && !bIsSprintButtonDown && !bIsSprintButtonPressed)
|
|
{
|
|
bForceMaxLimit = false;
|
|
}
|
|
|
|
if(bForceMaxLimit)
|
|
{
|
|
// Even though button bashing is enabled, allow holding sprint button to sprint also.
|
|
// TODO: should this be done for bicycles as well?
|
|
m_fSprintControlCounter = rSprintControlData.m_MaxLimit;
|
|
}
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
// Determine if we want to sprint based on the threshold for the sprint meter
|
|
float fSprintMult = 0.0f;
|
|
if (m_fSprintControlCounter > 0.0f)
|
|
{
|
|
fSprintMult = m_fSprintControlCounter / rSprintControlData.m_Threshhold;
|
|
}
|
|
return fSprintMult;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPlayerInfo::ControlButtonSprint(eSprintType nSprintType, bool bUseButtonBashing)
|
|
{
|
|
CPed& playerPed = *GetPlayerPed();
|
|
CControl* pControl = playerPed.GetControlFromPlayer();
|
|
|
|
// No control, no sprinting
|
|
if (!pControl)
|
|
return 0.0f;
|
|
|
|
const bool bOnBicycle = nSprintType == SPRINT_ON_BICYCLE;
|
|
|
|
if (bOnBicycle && playerPed.GetMyVehicle() && playerPed.GetPedResetFlag(CPED_RESET_FLAG_IsDoingDriveby))
|
|
{
|
|
const CVehicleDriveByInfo* pVehicleDriveByInfo = CVehicleMetadataMgr::GetVehicleDriveByInfoFromPed(&playerPed);
|
|
if (!pVehicleDriveByInfo || !pVehicleDriveByInfo->GetUseSpineAdditive())
|
|
{
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
// Called this function already this frame, just return previously computed value
|
|
if (m_fCachedSprintMultThisFrame > -1.0f)
|
|
{
|
|
return m_fCachedSprintMultThisFrame;
|
|
}
|
|
|
|
// Determine if we can sprint
|
|
const bool bCanSprint = ProcessCanSprint(playerPed);
|
|
|
|
static dev_float s_fClearToggleRunThreshold = 0.75f;
|
|
Vector3 vStickInput(pControl->GetPedWalkLeftRight().GetNorm(), -pControl->GetPedWalkUpDown().GetNorm(), 0.0f);
|
|
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
if (pControl->WasKeyboardMouseLastKnownSource() && pControl->GetPedWalkLeftRight().GetNorm() == 0.0f)
|
|
{
|
|
float fLeftNorm = Abs(pControl->GetValue(INPUT_MOVE_LEFT_ONLY).GetNorm());
|
|
float fRightNorm = Abs(pControl->GetValue(INPUT_MOVE_RIGHT_ONLY).GetNorm());
|
|
vStickInput.x = Max(fLeftNorm, fRightNorm);
|
|
}
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
|
|
if(vStickInput.Mag2() < s_fClearToggleRunThreshold)
|
|
{
|
|
pControl->ClearToggleRun();
|
|
}
|
|
|
|
// Determine if we want to sprint (return a value greater than one in this case)
|
|
float fSprintMult = ProcessSprintControl(*pControl, playerPed, nSprintType, bUseButtonBashing);
|
|
|
|
const sSprintControlData& rSprintControlData = ms_Tunables.m_SprintControlData[nSprintType];
|
|
|
|
bool bIsSprintButtonDown;
|
|
if (bOnBicycle)
|
|
{
|
|
bIsSprintButtonDown = pControl->GetVehiclePushbikePedal().IsDown();
|
|
}
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
else if(playerPed.IsUsingStealthMode() && pControl->WasKeyboardMouseLastKnownSource())
|
|
{
|
|
// On the keyboard and mouse we need to take into account the sprint toggle state.
|
|
bIsSprintButtonDown = pControl->GetPedSprintIsDown();
|
|
|
|
// On the keyboard, INPUT_SPRINT is held to sprint but pressed to toggle run/walk. If the button
|
|
// is down for less than CControl::KEYBOARD_START_SPRINT_HELD_TIME then reset fSprintMult otherwise it can kick us out of stealth mode.
|
|
// url:bugstar:1818710.
|
|
if(pControl->GetValue(INPUT_SPRINT).HistoryHeldDown(CControl::KEYBOARD_START_SPRINT_HELD_TIME) == false)
|
|
{
|
|
fSprintMult = 0.0f;
|
|
}
|
|
}
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
else
|
|
{
|
|
bIsSprintButtonDown = pControl->GetPedSprintIsDown();
|
|
}
|
|
|
|
const float fUseUpSprintEnergyThreshold = 1.0f;
|
|
|
|
// We want to sprint and are allowed to
|
|
if (fSprintMult > fUseUpSprintEnergyThreshold && bCanSprint)
|
|
{
|
|
const bool bShouldUseUpSprintEnergy = !bOnBicycle || !playerPed.GetPedResetFlag(CPED_RESET_FLAG_IsExitingVehicle);
|
|
|
|
// Reduce energy when on bicycle quicker if mashing
|
|
float fDepletionMultiplier = 1.0f;
|
|
if (bOnBicycle)
|
|
{
|
|
fDepletionMultiplier = ms_Tunables.m_BicycleDepletionMinMult;
|
|
|
|
if (fSprintMult >= ms_Tunables.m_BicycleMaxDepletionLimit)
|
|
{
|
|
fDepletionMultiplier = ms_Tunables.m_BicycleDepletionMaxMult;
|
|
}
|
|
else if (fSprintMult >= ms_Tunables.m_BicycleMidDepletionLimit && fSprintMult > ms_Tunables.m_BicycleMinDepletionLimit)
|
|
{
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, POW_EXP, 4.0f, 0.0f, 10.0f, 0.01f);
|
|
const float fRangedNorm = (fSprintMult - ms_Tunables.m_BicycleMidDepletionLimit) / (ms_Tunables.m_BicycleMaxDepletionLimit - ms_Tunables.m_BicycleMidDepletionLimit);
|
|
const float fMult = rage::Powf(fRangedNorm, POW_EXP);
|
|
fDepletionMultiplier = ms_Tunables.m_BicycleDepletionMidMult + fMult * (ms_Tunables.m_BicycleDepletionMaxMult - ms_Tunables.m_BicycleDepletionMidMult);
|
|
}
|
|
}
|
|
|
|
// Trying to sprint so use energy up
|
|
if (bShouldUseUpSprintEnergy && HandleSprintEnergy(true, fDepletionMultiplier * ComputeSprintDepletionRateForPed(*m_pPlayerPed, GetPlayerDataMaxSprintEnergy(), nSprintType)))
|
|
{
|
|
fSprintMult = 1.0f + rSprintControlData.m_ResultMult*rage::Max(0.0f, fSprintMult-1.0f);
|
|
}
|
|
// Ran out of energy so we need to replenish a bit before we can sprint again
|
|
else if (bShouldUseUpSprintEnergy)
|
|
{
|
|
m_bSprintReplenishing = true;
|
|
}
|
|
}
|
|
// Don't want to or unable to sprint but holding sprint button, keep the sprint meter just below the threshold if we're not replenishing
|
|
else if (bIsSprintButtonDown)
|
|
{
|
|
fSprintMult = 1.0f;
|
|
TUNE_GROUP_BOOL(SPRINT_TUNE, ENABLE_SPRINT_IF_ANY_ENERGY, true);
|
|
if ((ENABLE_SPRINT_IF_ANY_ENERGY && m_fSprintEnergy > 0.0f) || (m_fSprintEnergy > m_fMaxSprintEnergy * ms_Tunables.m_SprintReplenishFinishedPercentage))
|
|
{
|
|
m_fSprintControlCounter = rSprintControlData.m_Threshhold - 0.01f;
|
|
}
|
|
}
|
|
// If not pressing sprint and we're currently sprinting, stop sprinting
|
|
else if (fSprintMult > 1.0f)
|
|
{
|
|
fSprintMult = 1.0f;
|
|
}
|
|
|
|
// Cache the sprint multiplier in case we call this again this frame
|
|
m_fCachedSprintMultThisFrame = fSprintMult;
|
|
return m_fCachedSprintMultThisFrame;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPlayerInfo::GetButtonSprintResults(eSprintType nSprintType)
|
|
{
|
|
if(m_fSprintControlCounter > PLAYER_SPRINT_THRESHOLD)
|
|
return 1.0f + ms_Tunables.m_SprintControlData[nSprintType].m_ResultMult*rage::Max(0.0f, (m_fSprintControlCounter / PLAYER_SPRINT_THRESHOLD) - 1.0f);
|
|
else if(m_fSprintControlCounter > 0.0f)
|
|
return 1.0f;
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPlayerInfo::ControlButtonDuck()
|
|
{
|
|
CPed& playerPed = *GetPlayerPed();
|
|
const CControl* pControl = playerPed.GetControlFromPlayer();
|
|
|
|
if(pControl && pControl->GetPedDuck().IsPressed())
|
|
{
|
|
if( CGameConfig::Get().AllowStealthMovement())
|
|
{
|
|
if( playerPed.GetMotionData()->GetUsingStealth() )
|
|
{
|
|
playerPed.GetMotionData()->SetUsingStealth( false );
|
|
}
|
|
else if( playerPed.CanPedStealth() )
|
|
{
|
|
playerPed.GetMotionData()->SetUsingStealth( true );
|
|
}
|
|
}
|
|
else if(CGameConfig::Get().AllowCrouchedMovement())
|
|
{
|
|
// Allow the ped to uncrouch when in stealth..
|
|
if(playerPed.GetIsCrouching())
|
|
{
|
|
playerPed.SetIsCrouching(false);
|
|
}
|
|
// ..but not to crouch
|
|
else if (playerPed.CanPedCrouch() && !playerPed.GetMotionData()->GetUsingStealth())
|
|
{
|
|
playerPed.SetIsCrouching(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//mjc - potentially push down to CPed ?
|
|
bool CPlayerInfo::IsPlayerPressingHorn() const
|
|
{
|
|
const CPed* pPlayerPed = GetPlayerPed();
|
|
if(!pPlayerPed)
|
|
return false;
|
|
|
|
if(pPlayerPed->IsNetworkClone())
|
|
{
|
|
if(pPlayerPed->GetIsDrivingVehicle())
|
|
{
|
|
CNetObjVehicle* pNetObjVehicle = static_cast<CNetObjVehicle*>(pPlayerPed->GetMyVehicle()->GetNetworkObject());
|
|
return (pNetObjVehicle && pNetObjVehicle->IsHornOnFromNetwork());
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return pPlayerPed->GetIsDrivingVehicle() && CControlMgr::GetMainPlayerControl().GetVehicleHorn().IsDown();
|
|
}
|
|
}
|
|
|
|
bool CPlayerInfo::AffectedByWaterCannon()
|
|
{
|
|
if(m_fHitByWaterCannonImmuneTimer > 0.0f)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
void CPlayerInfo::RegisterHitByWaterCannon()
|
|
{
|
|
// Add on cool down rate to cancel out constant decrement in ProcessControl
|
|
m_fHitByWaterCannonTimer += fwTimer::GetTimeStep()*(1.0f + PLAYER_WATER_CANNON_COOLDOWN_RATE);
|
|
|
|
if(m_fHitByWaterCannonTimer > PLAYER_MAX_TIME_HIT_BY_WATER_CANNON)
|
|
{
|
|
m_fHitByWaterCannonImmuneTimer = PLAYER_WATER_CANNON_IMMUNE_TIME;
|
|
m_fHitByWaterCannonTimer = 0.0f;
|
|
}
|
|
}
|
|
|
|
|
|
//mjc - into CPed ?
|
|
bool CPlayerInfo::IsHidden() const
|
|
{
|
|
#define HIDDEN_THRESHOLD (0.05f)
|
|
return false;//(!GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && GetLightingTotal() <= HIDDEN_THRESHOLD);
|
|
}
|
|
|
|
float CPlayerInfo::GetFiringAttackValue()
|
|
{
|
|
CPed * pPlayer = GetPlayerPed();
|
|
|
|
if(pPlayer->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsArresting))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
|
|
// For network synced players.
|
|
if( !pControl )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
float fAttackValue = 0.0f;
|
|
|
|
bool bInVehicle = false;
|
|
|
|
// Held turrets use on foot aim / fire controls
|
|
if(pPlayer->GetIsInVehicle() && !pPlayer->IsExitingVehicle() && !pPlayer->GetMyVehicle()->IsTurretSeat(pPlayer->GetMyVehicle()->GetSeatManager()->GetPedsSeatIndex(pPlayer)))
|
|
{
|
|
taskAssert(pPlayer->GetMyVehicle()->GetLayoutInfo());
|
|
const bool bVehicleHasDriver = pPlayer->GetMyVehicle()->GetLayoutInfo()->GetHasDriver();
|
|
|
|
if (bVehicleHasDriver)
|
|
{
|
|
bInVehicle = true;
|
|
|
|
//Drive-bys have some really weird firing logic.
|
|
const bool bIsDoingDriveBy = pPlayer->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_AIM_GUN_VEHICLE_DRIVE_BY, true);
|
|
if(bIsDoingDriveBy)
|
|
{
|
|
bool bVehicleAim = pControl->IsVehicleAttackInputDown();
|
|
|
|
if(bVehicleAim)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (pPlayer->GetIsDrivingVehicle())
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( pPlayer->GetIsDrivingVehicle() && !pPlayer->GetMyVehicle()->InheritsFromTrailer())
|
|
{
|
|
const bool bSecondaryAttackButton = pPlayer->GetMyVehicle()->GetIsAircraft() || pPlayer->GetMyVehicle()->InheritsFromSubmarine() || (pPlayer->GetMyVehicle()->InheritsFromSubmarineCar() && pPlayer->GetMyVehicle()->IsInSubmarineMode());
|
|
|
|
//Disable attach button turning the search light on and off, this is now on the headlight button
|
|
bool bDisableAttackButton = false;
|
|
const CVehicleWeaponMgr* pWeaponMgr = pPlayer->GetMyVehicle()->GetVehicleWeaponMgr();
|
|
if(pWeaponMgr)
|
|
{
|
|
if(pWeaponMgr->GetNumVehicleWeapons() == 1)
|
|
{
|
|
CVehicleWeapon* pWeapon = pWeaponMgr->GetVehicleWeapon(0);
|
|
if(pWeapon)
|
|
{
|
|
if(pWeapon->GetType() == VGT_SEARCHLIGHT)
|
|
{
|
|
bDisableAttackButton = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bUseVehicleAttackForFiring = false;
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
if(pControl->WasKeyboardMouseLastKnownSource() && (pPlayer->GetMyVehicle()->GetModelIndex() == MI_TANK_RHINO || (MI_TANK_KHANJALI.IsValid() && pPlayer->GetMyVehicle()->GetModelIndex() == MI_TANK_KHANJALI)))
|
|
{
|
|
bUseVehicleAttackForFiring = true;
|
|
}
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
|
|
// Hack to use the cinematic camera input to fire torpedoes on the Kostka
|
|
bool bIsKeyboardMouse = false;
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
bIsKeyboardMouse = pControl->WasKeyboardMouseLastKnownSource();
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
bool bIsKostaka = MI_SUB_KOSATKA.IsValid() && pPlayer->GetMyVehicle()->GetModelIndex() == MI_SUB_KOSATKA;
|
|
bool bUseOtherInput = bIsKostaka && !bDisableAttackButton && !bIsKeyboardMouse;
|
|
ioValue::ReadOptions readOptions;
|
|
readOptions.SetFlags(ioValue::ReadOptions::F_READ_DISABLED, true);
|
|
bool bIsOrbisJapaneseBuild = false;
|
|
#if RSG_ORBIS
|
|
if(sysAppContent::IsJapaneseBuild())
|
|
{
|
|
bIsOrbisJapaneseBuild = true;
|
|
if(sysAppContent::IsJapaneseBuild() && !CPhoneMgr::IsDisplayed() && bUseOtherInput && pControl->GetCellphoneCancel().IsDown(ioValue::BUTTON_DOWN_THRESHOLD, readOptions))
|
|
{
|
|
fAttackValue = pControl->GetCellphoneCancel().GetNorm(readOptions);
|
|
}
|
|
}
|
|
#endif // RSG_ORBIS
|
|
|
|
if(!bIsOrbisJapaneseBuild && bUseOtherInput && pControl->GetVehicleCinematicCam().IsDown(ioValue::BUTTON_DOWN_THRESHOLD, readOptions))
|
|
{
|
|
fAttackValue = pControl->GetVehicleCinematicCam().GetNorm(readOptions);
|
|
}
|
|
|
|
//Using FlyAttack input for sub/subcar as we would need to patch the various commands meta files (ie settings.meta, standard.meta etc)
|
|
bool bVehicleAttack = bSecondaryAttackButton ? pControl->GetVehicleFlyAttack().IsDown() : (bUseVehicleAttackForFiring ? pControl->GetVehicleAttack().IsDown() : pControl->GetVehicleAim().IsDown());
|
|
if (bVehicleAttack && !bDisableAttackButton && !bUseOtherInput)
|
|
{
|
|
float fAttackNorm = bUseVehicleAttackForFiring ? pControl->GetVehicleAttack().GetNorm() : pControl->GetVehicleAim().GetNorm();
|
|
fAttackValue = MAX(fAttackNorm, pControl->GetVehicleFlyAttack().GetNorm());
|
|
}
|
|
}
|
|
else if(pPlayer->GetMyVehicle()->GetModelIndex() == MI_VAN_RIOT_2 && pPlayer->GetWeaponManager() && pPlayer->GetWeaponManager()->GetEquippedVehicleWeaponIndex() != -1 && pControl->GetVehicleAim().IsDown())
|
|
{
|
|
//B*4048108: Passengers using the water cannon of the RIOT2 should also allow L1 (INPUT_VEHICLE_AIM)
|
|
fAttackValue = pControl->GetVehicleAim().GetNorm();
|
|
}
|
|
else if(pControl->IsVehicleAttackInputDown())
|
|
{
|
|
fAttackValue = pControl->GetVehicleAttackInputNorm();
|
|
}
|
|
else
|
|
{
|
|
fAttackValue = pControl->GetVehiclePassengerAttack().GetNorm();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bInVehicle)
|
|
{
|
|
fAttackValue = MAX(pControl->GetPedAttack2().GetNorm(), pControl->GetPedAttack().GetNorm());
|
|
}
|
|
|
|
return fAttackValue;
|
|
}
|
|
|
|
float CPlayerInfo::GetFiringBoundaryValue()
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
CPed *pPed = GetPlayerPed();
|
|
if(pPed && pPed->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
if(m_bUseHighFiringAttackBoundary)
|
|
{
|
|
return ms_PlayerFPSFireBoundaryHigh;
|
|
}
|
|
else
|
|
{
|
|
return ms_PlayerFPSFireBoundaryLow;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return ms_PlayerSoftAimBoundary;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is not holding the aim button fully
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsFiring()
|
|
{
|
|
float fAttackValue = GetFiringAttackValue();
|
|
float fBoundaryValue = GetFiringBoundaryValue();
|
|
|
|
return fAttackValue >= fBoundaryValue;
|
|
}
|
|
|
|
|
|
bool CPlayerInfo::IsFiring_s()
|
|
{
|
|
CPed* pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return false;
|
|
}
|
|
return pPlayer->GetPlayerInfo()->IsFiring();
|
|
}
|
|
|
|
bool CPlayerInfo::IsDriverFiring()
|
|
{
|
|
const CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
|
|
if (pPlayer->GetIsDrivingVehicle())
|
|
{
|
|
const CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
return (pControl->GetVehicleAim().IsDown() && pControl->IsVehicleAttackInputDown());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::IsFlyingAircraft()
|
|
{
|
|
const CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
|
|
if (pPlayer->GetIsDrivingVehicle())
|
|
{
|
|
return pPlayer->GetMyVehicle()->GetIsAircraft();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::IsSimulatingSoftFiring(const CPed * pPlayer)
|
|
{
|
|
FatalAssert(pPlayer);
|
|
bool bSpecialAbilityForcingSoftAim = !NetworkInterface::IsGameInProgress() && pPlayer->GetSpecialAbility() && pPlayer->GetSpecialAbility()->GetType() == SAT_BULLET_TIME && pPlayer->GetSpecialAbility()->IsActive();
|
|
if(bSpecialAbilityForcingSoftAim)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::IsSoftFiring()
|
|
{
|
|
const CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
|
|
if(IsSimulatingSoftFiring(pPlayer))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
TUNE_GROUP_BOOL(SOFT_FIRE_TUNE, DONT_SOFT_FIRE_IN_COVER, true);
|
|
if(DONT_SOFT_FIRE_IN_COVER && pPlayer->GetIsInCover() && pPlayer->IsFirstPersonShooterModeEnabledForPlayer(false))
|
|
{
|
|
return false;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
const CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
if( !pControl ) { return false; }
|
|
|
|
const float fFireTestHighValue = ms_PlayerSoftAimBoundary - 0.001f;
|
|
const float fFireTestLowValue = ms_PlayerLowAimBoundry;
|
|
|
|
const float fAttackValue = MAX(pControl->GetPedAttack2().GetNorm(), pControl->GetPedAttack().GetNorm());
|
|
|
|
return (fAttackValue > fFireTestLowValue && fAttackValue <= fFireTestHighValue);
|
|
}
|
|
|
|
bool CPlayerInfo::IsJustFiring()
|
|
{
|
|
const CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
|
|
return pPlayer->GetPlayerInfo()->IsFiring() && !pPlayer->GetPedConfigFlag(CPED_CONFIG_FLAG_WasFiring);
|
|
}
|
|
|
|
bool CPlayerInfo::CanPlayerWarpIntoVehicle(const CPed& rPed, const CVehicle& rVeh, s32 iTargetSeat)
|
|
{
|
|
if (rPed.IsLocalPlayer())
|
|
{
|
|
#if __BANK
|
|
TUNE_GROUP_BOOL(PERSONAL_VEHICLE_DEBUG, FORCE_CAN_WARP_INTO_VEHICLE, false);
|
|
if (FORCE_CAN_WARP_INTO_VEHICLE)
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
CPed* pPedOccupyingSeat = iTargetSeat >= 0 ? rVeh.GetSeatManager()->GetPedInSeat(iTargetSeat) : nullptr;
|
|
const bool bTargetIsPlayer = pPedOccupyingSeat && pPedOccupyingSeat->IsPlayer();
|
|
if (bTargetIsPlayer)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(PERSONAL_VEHICLE_DEBUG, PRETEND_PERSONAL, false);
|
|
if (rVeh.InheritsFromBike())
|
|
{
|
|
return true;
|
|
}
|
|
else if (rVeh.PopTypeIsMission())
|
|
{
|
|
return true;
|
|
}
|
|
else if (CTaskVehicleFSM::VehicleContainsFriendlyPassenger(&rPed, &rVeh))
|
|
{
|
|
return true;
|
|
}
|
|
// Some of the decorator functions could do with some const-correctness
|
|
else if (const_cast<CVehicle&>(rVeh).IsPersonalVehicle() || PRETEND_PERSONAL)
|
|
{
|
|
// B*512607 - Allow warping into personal vehicles
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CPlayerInfo::GetCachedMeleeInputs( bool& bWasLightAttackPressed, bool& bWasAlternateAttackPressed )
|
|
{
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer )
|
|
return;
|
|
|
|
bWasLightAttackPressed = pPlayer->GetPlayerInfo()->WasLightAttackPressed();
|
|
bWasAlternateAttackPressed = pPlayer->GetPlayerInfo()->WasAlternateAttackPressed();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is not in a vehicle or isn't driving a vehicle
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsNotDrivingVehicle()
|
|
{
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( pPlayer->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && pPlayer->GetMyVehicle())
|
|
{
|
|
Assert(pPlayer->GetMyVehicle()->GetLayoutInfo());
|
|
const bool bVehicleHasDriver = pPlayer->GetMyVehicle()->GetLayoutInfo()->GetHasDriver();
|
|
|
|
if (bVehicleHasDriver && pPlayer->GetIsDrivingVehicle())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player can use assisted aim.
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::CanUseAssistedAiming()
|
|
{
|
|
if(!CPauseMenu::GetMenuPreference(PREF_CINEMATIC_SHOOTING))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(NetworkInterface::IsGameInProgress())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
const CPed* pPed = CGameWorld::FindLocalPlayer();
|
|
if(pPed->IsFirstPersonShooterModeEnabledForPlayer(false) || pPed->IsInFirstPersonVehicleCamera())
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
TUNE_GROUP_BOOL(PLAYER_TARGETING, bDisableCinematicAim, true)
|
|
if(bDisableCinematicAim)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is just aiming using the fire button and using cinematic mode
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsAssistedAiming()
|
|
{
|
|
const CPed* pPed = CGameWorld::FindLocalPlayer();
|
|
return CanUseAssistedAiming() && !pPed->GetPlayerInfo()->IsAiming(false) && (pPed->GetPlayerInfo()->IsSoftFiring() || pPed->GetPlayerInfo()->IsFiring());
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is just aiming using the fire button
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsRunAndGunning(bool bCanRunAndGunWhileAiming, bool bIgnoreSoftFiring)
|
|
{
|
|
const CPed* pPed = CGameWorld::FindLocalPlayer();
|
|
bool bAllowRunAndGunWhileAiming = bCanRunAndGunWhileAiming || !pPed->GetPlayerInfo()->IsAiming(false);
|
|
return !CanUseAssistedAiming() && bAllowRunAndGunWhileAiming && ((pPed->GetPlayerInfo()->IsSoftFiring()&&!bIgnoreSoftFiring) || pPed->GetPlayerInfo()->IsFiring());
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is aiming at all
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsAiming(const bool bConsiderAttackTriggerAiming)
|
|
{
|
|
return IsSoftAiming(bConsiderAttackTriggerAiming) || IsHardAiming() || IsForcedAiming() || IsSimulatingAiming();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is not holding the aim button fully
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsSoftAiming(const bool bConsiderAttackTriggerAiming)
|
|
{
|
|
const CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
|
|
const CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
|
|
if (pPlayer->GetMyVehicle())
|
|
{
|
|
Assert(pPlayer->GetMyVehicle()->GetLayoutInfo());
|
|
bool bVehicleHasDriver = pPlayer->GetMyVehicle()->GetLayoutInfo()->GetHasDriver();
|
|
|
|
if (bVehicleHasDriver && pPlayer->GetIsDrivingVehicle() && !pPlayer->IsExitingVehicle())
|
|
{
|
|
return false; //drivers cannot soft aim (for now)
|
|
}
|
|
}
|
|
|
|
float fAimTestHighValue = ms_PlayerSoftAimBoundary - 0.001f;
|
|
float fAimTestLowValue = ms_PlayerLowAimBoundry;
|
|
|
|
if( ms_ReverseAimBoundarys )
|
|
{
|
|
fAimTestLowValue = fAimTestHighValue;
|
|
fAimTestHighValue = 1.0f;
|
|
}
|
|
|
|
const float fTargetValue = pControl ? pControl->GetPedTargetNorm() : 0.f;
|
|
|
|
return (fTargetValue > fAimTestLowValue && fTargetValue <= fAimTestHighValue) || (bConsiderAttackTriggerAiming && IsSoftFiring());
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is holding the aim button fully
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsHardAiming()
|
|
{
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
|
|
CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
if( !pControl ) { return false; }
|
|
|
|
if (pPlayer->GetMyVehicle())
|
|
{
|
|
Assert(pPlayer->GetMyVehicle()->GetLayoutInfo());
|
|
bool bVehicleHasDriver = pPlayer->GetMyVehicle()->GetLayoutInfo()->GetHasDriver();
|
|
|
|
if(!pPlayer->IsExitingVehicle())
|
|
{
|
|
if (bVehicleHasDriver && pPlayer->GetIsDrivingVehicle() && !pPlayer->GetMyVehicle()->IsTurretSeat(pPlayer->GetMyVehicle()->GetSeatManager()->GetPedsSeatIndex(pPlayer)))
|
|
{
|
|
if(pControl->GetVehicleAim().IsDown() && !pPlayer->GetVehiclePedInside()->GetIsAircraft())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#if RSG_PC
|
|
//B*1823615: Enable driver aiming in PC builds and if we're using mouse/keyboard controls
|
|
if (pControl->WasKeyboardMouseLastKnownSource() && pControl->GetVehicleTargetDown())
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
else if(pPlayer->GetVehiclePedInside())
|
|
{
|
|
#if RSG_PC
|
|
// PREF_ALTERNATE_DRIVEBY shouldn't change mouse.
|
|
if (pControl->WasKeyboardMouseLastKnownSource() && pControl->GetVehicleAim().IsDown())
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
if(CPauseMenu::GetMenuPreference(PREF_ALTERNATE_DRIVEBY) && pControl->GetVehicleAim().IsDown())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
float fAimTestHighValue = 1.0f;
|
|
float fAimTestLowValue = ms_PlayerSoftAimBoundary;
|
|
if( ms_ReverseAimBoundarys )
|
|
{
|
|
fAimTestHighValue = fAimTestLowValue;
|
|
fAimTestLowValue = 0.004f;
|
|
}
|
|
|
|
bool bAimEnabled = pControl->GetVehicleTarget().IsEnabled();
|
|
|
|
return bAimEnabled
|
|
&& pControl->GetPedTargetNorm() >= fAimTestLowValue
|
|
&& pControl->GetPedTargetNorm() <= fAimTestHighValue;
|
|
}
|
|
|
|
bool CPlayerInfo::IsForcedAiming()
|
|
{
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
|
|
return pPlayer->GetPedConfigFlag( CPED_CONFIG_FLAG_ForcedAim ) || pPlayer->GetPedConfigFlag( CPED_CONFIG_FLAG_ForcedAimFromArrest );
|
|
}
|
|
|
|
bool CPlayerInfo::IsSimulatingAiming()
|
|
{
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
|
|
return pPlayer->GetPedConfigFlag( CPED_CONFIG_FLAG_SimulatingAiming );
|
|
}
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is holding the aim button fully
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsJustHardAiming()
|
|
{
|
|
if( !IsHardAiming() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// IF the player is hard aiming now, return true if they weren't last frame
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
if( !pControl ) { return false; }
|
|
|
|
float fAimTestHighValue = 1.0f;
|
|
float fAimTestLowValue = ms_PlayerSoftAimBoundary;
|
|
if( ms_ReverseAimBoundarys )
|
|
{
|
|
fAimTestHighValue = fAimTestLowValue;
|
|
fAimTestLowValue = 0.004f;
|
|
}
|
|
|
|
return pControl->GetPedTargetLastNorm() < fAimTestLowValue ||
|
|
pControl->GetPedTargetLastNorm() > fAimTestHighValue;
|
|
}
|
|
|
|
bool CPlayerInfo::IsReloading()
|
|
{
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
|
|
if(pPlayer->GetPedConfigFlag(CPED_CONFIG_FLAG_ForceReload))
|
|
{
|
|
// Should reload due to MAKE_PED_RELOAD.
|
|
return true;
|
|
}
|
|
|
|
#if USE_SIXAXIS_GESTURES
|
|
if(CControlMgr::GetPlayerPad() && CPadGestureMgr::GetMotionControlEnabled(CPadGestureMgr::MC_TYPE_RELOAD))
|
|
{
|
|
CPadGesture* gesture = CControlMgr::GetPlayerPad()->GetPadGesture();
|
|
if(gesture && gesture->GetHasReloaded())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
#endif // USE_SIXAXIS_GESTURES
|
|
|
|
return pControl && pControl->GetPedReload().IsPressed();
|
|
}
|
|
|
|
bool CPlayerInfo::IsCombatRolling()
|
|
{
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// HACK: disable combat roll when in first person camera.
|
|
TUNE_GROUP_BOOL(FIRST_PERSON_TUNE, COMBAT_ROLL_ENABLE, true);
|
|
if(pPlayer->IsFirstPersonShooterModeEnabledForPlayer(false) && !COMBAT_ROLL_ENABLE)
|
|
{
|
|
return false;
|
|
}
|
|
#endif // FPS_MODE_SUPPORTED
|
|
|
|
CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
|
|
// Get the weapon
|
|
weaponAssert(pPlayer->GetWeaponManager());
|
|
const CWeapon* pWeapon = pPlayer->GetWeaponManager()->GetEquippedWeapon();
|
|
#if FPS_MODE_SUPPORTED
|
|
if(pPlayer->IsFirstPersonShooterModeEnabledForPlayer(false) && (pPlayer->GetMotionData()->GetIsFPSIdle() ||
|
|
(pWeapon && pWeapon->GetWeaponInfo() && (pWeapon->GetWeaponInfo()->GetIsUnarmed() || pWeapon->GetWeaponInfo()->GetIsMelee()))))
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if(pControl && pControl->GetPedJump().IsDown() && (!pWeapon || !pWeapon->GetWeaponInfo()->GetDisableCombatRoll()) && pPlayer->GetPedIntelligence()->GetCanCombatRoll() && !pPlayer->GetIsInVehicle() && !pPlayer->GetIsParachuting())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static dev_u32 s_fHeldAndPressedMod = 2;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is trying to select the next target to the left
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsSelectingLeftTarget(bool bCheckAimButton)
|
|
{
|
|
// IF the player is hard aiming now, return true if they weren't last frame
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
if( !pControl ) { return false; }
|
|
|
|
bool bAssistedAiming = pPlayer->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON);
|
|
|
|
if( (!bCheckAimButton || IsHardAiming() || IsDriverFiring() || bAssistedAiming) &&
|
|
CTaskPlayerOnFoot::ms_bAnalogueLockonAimControl )
|
|
{
|
|
if( pControl->GetPedAimWeaponLeftRight().HistoryPressedAndHeld( (fwTimer::GetTimeStepInMilliseconds() * s_fHeldAndPressedMod) , NULL, -ms_PlayerSoftTargetSwitchBoundary) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns true if the player is trying to select the next target to the right
|
|
//-------------------------------------------------------------------------
|
|
bool CPlayerInfo::IsSelectingRightTarget(bool bCheckAimButton)
|
|
{
|
|
// IF the player is hard aiming now, return true if they weren't last frame
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if( !pPlayer ) { return false; }
|
|
CControl* pControl = pPlayer->GetControlFromPlayer();
|
|
if( !pControl ) { return false; }
|
|
|
|
bool bAssistedAiming = pPlayer->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_ASSISTED_AIMING_ON);
|
|
|
|
if( (!bCheckAimButton || IsHardAiming() || IsDriverFiring() || bAssistedAiming) &&
|
|
CTaskPlayerOnFoot::ms_bAnalogueLockonAimControl )
|
|
{
|
|
if(pControl->GetPedAimWeaponLeftRight().HistoryPressedAndHeld((fwTimer::GetTimeStepInMilliseconds() * s_fHeldAndPressedMod), NULL, ms_PlayerSoftTargetSwitchBoundary) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void CPlayerInfo::SetModelId(fwModelId modelId)
|
|
{
|
|
const CPedModelInfo* pModelInfo = static_cast<CPedModelInfo*>(CModelInfo::GetBaseModelInfo(modelId));
|
|
Assert(pModelInfo);
|
|
|
|
if (!pModelInfo->GetIsPlayerModel())
|
|
{
|
|
return;
|
|
}
|
|
|
|
CPed * pPed = GetPlayerPed();
|
|
|
|
//mjc - why is this not called in the CPed::SetModelId ?
|
|
pPed->SetInitialState();
|
|
|
|
// load in the initial data for the player
|
|
CPedVariationStream::RequestStreamPedFiles(pPed, &pPed->GetPedDrawHandler().GetVarData(), (STRFLAG_FORCE_LOAD | STRFLAG_PRIORITY_LOAD));
|
|
|
|
CPedVariationStream::ProcessStreamRequests();
|
|
}
|
|
|
|
void CPlayerInfo::SetupPlayerGroup()
|
|
{
|
|
const s32 playerGroup = CPedGroups::GetPlayersGroup(GetPlayerPed());
|
|
m_PlayerGroup = playerGroup;
|
|
CPedGroups::AddGroupAtIndex(POPTYPE_PERMANENT, playerGroup);
|
|
if (AssertVerify(CPedGroups::ms_groups[playerGroup].GetGroupMembership()))
|
|
{
|
|
CPedGroups::ms_groups[playerGroup].GetGroupMembership()->SetLeader(GetPlayerPed(), NetworkInterface::IsNetworkOpen());
|
|
}
|
|
CPedGroups::ms_groups[playerGroup].Process();
|
|
}
|
|
|
|
|
|
bool CPlayerInfo::CanPlayerStartMission()
|
|
{
|
|
//RAGE if (CGameLogic::GameState != CGameLogic::GAMESTATE_PLAYING || CGameLogic::IsCoopGameGoingOn()) return false; // Obbe: put in to fix the problem with ending a 2 player game on a mission trigger.
|
|
CPed * pPed = GetPlayerPed();
|
|
if (pPed->IsPedInControl() == false && !pPed->GetIsDrivingVehicle() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Name : KeepAreaAroundPlayerClear
|
|
// Purpose : When a player is in a cutscene, tries to keep the area around him clear of peds and cars
|
|
// Parameters :
|
|
// Returns :
|
|
void CPlayerInfo::KeepAreaAroundPlayerClear()
|
|
{
|
|
if (!m_bStopNearbyVehicles)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// check peds
|
|
int i;
|
|
CEntity *ppResults[8]; // whatever.. temp list
|
|
CVehicle *pVehicle;
|
|
CVehicle *pPlayersVehicle = NULL;
|
|
s32 Num;
|
|
Vector3 vecDistance, vecPosition;
|
|
|
|
|
|
if(GetPlayerPed()->GetIsInVehicle())
|
|
{
|
|
vecPosition = VEC3V_TO_VECTOR3(GetPlayerPed()->GetMyVehicle()->GetTransform().GetPosition());
|
|
pPlayersVehicle = GetPlayerPed()->GetMyVehicle();
|
|
}
|
|
else
|
|
vecPosition = VEC3V_TO_VECTOR3(GetPlayerPed()->GetTransform().GetPosition());
|
|
|
|
// check for threat peds in cars
|
|
// Should the first parameter below actually be vecPosition?
|
|
CGameWorld::FindObjectsInRange(VEC3V_TO_VECTOR3(GetPlayerPed()->GetTransform().GetPosition()), 15.0f, true, &Num, 6, ppResults, 0, 1, 0, 0, 0); // Just Cars we're interested in
|
|
// cars in range stored in ppResults
|
|
|
|
for(i=0; i<Num; i++) // find cars close by
|
|
{
|
|
pVehicle = (CVehicle *)ppResults[i];
|
|
|
|
if( pVehicle!=pPlayersVehicle &&
|
|
pVehicle->GetDriver() &&
|
|
!pVehicle->PopTypeIsMission() &&
|
|
!pVehicle->IsNetworkClone() &&
|
|
pVehicle->GetStatus() != STATUS_PLAYER &&
|
|
pVehicle->GetStatus() != STATUS_PLAYER_DISABLED &&
|
|
!pVehicle->InheritsFromTrailer() &&
|
|
!pVehicle->m_nVehicleFlags.bHasParentVehicle &&
|
|
!pVehicle->GetDriver()->IsDead())
|
|
{
|
|
const Vector3 vecVehiclePosition = VEC3V_TO_VECTOR3(pVehicle->GetVehiclePosition());
|
|
vecDistance = vecVehiclePosition - vecPosition;
|
|
|
|
// Depending on whether the car is facing away or towards the player we will reverse or go foward a bit
|
|
Vector3 vecForward(VEC3V_TO_VECTOR3(pVehicle->GetVehicleForwardDirection()));
|
|
if ( (vecForward.x * (vecPosition.x - vecVehiclePosition.x)) +
|
|
(vecForward.y * (vecPosition.y - vecVehiclePosition.y)) > 0.0f)
|
|
{
|
|
// cars are far enough away, stop them moving
|
|
if(vecDistance.Mag2() > 5.0f*5.0f)
|
|
{
|
|
// Tell cars to stop for 5 secs so that they will automatically start driving again after a while
|
|
aiTask *pTask = rage_new CTaskVehicleWait(NetworkInterface::GetSyncedTimeInMilliseconds() + 5000);
|
|
pVehicle->GetIntelligence()->AddTask(VEHICLE_TASK_TREE_PRIMARY, pTask, VEHICLE_TASK_PRIORITY_PRIMARY, false);
|
|
}
|
|
else
|
|
{
|
|
// reverse away
|
|
aiTask *pTask = rage_new CTaskVehicleReverse(NetworkInterface::GetSyncedTimeInMilliseconds() + 2000, CTaskVehicleReverse::Reverse_Opposite_Direction);
|
|
pVehicle->GetIntelligence()->AddTask(VEHICLE_TASK_TREE_PRIMARY, pTask, VEHICLE_TASK_PRIORITY_PRIMARY, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(vecDistance.Mag2() > 5.0f*5.0f)
|
|
{
|
|
//nothing, continue on our way
|
|
}
|
|
else
|
|
{
|
|
// go forward
|
|
aiTask *pTask = rage_new CTaskVehicleGoForward(NetworkInterface::GetSyncedTimeInMilliseconds() + 2000);
|
|
pVehicle->GetIntelligence()->AddTask(VEHICLE_TASK_TREE_PRIMARY, pTask, VEHICLE_TASK_PRIORITY_PRIMARY, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::SetPlayerCanBeDamaged(bool DmgeFlag)
|
|
{
|
|
if(m_bCanBeDamaged != DmgeFlag)
|
|
{
|
|
aiDebugf1("SetPlayerCanBeDamaged :: %s -> %s", m_bCanBeDamaged ? "True" : "False", DmgeFlag ? "True" : "False");
|
|
m_bCanBeDamaged = DmgeFlag;
|
|
}
|
|
|
|
// reset this to be sure we catch all cases, we'll apply the flag from SetPlayerControl separately
|
|
m_DamageAllowedFromSetPlayerControl = false;
|
|
}
|
|
|
|
f32 CPlayerInfo::GetPlayerMeleeDamageModifier(bool bUnarmedMelee) const
|
|
{
|
|
if (bUnarmedMelee)
|
|
{
|
|
return GetPlayerMeleeUnarmedDamageModifier();
|
|
}
|
|
else
|
|
{
|
|
return GetPlayerMeleeWeaponDamageModifier();
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::SetLastWeaponHashPickedUp(u32 uWeaponHash)
|
|
{
|
|
m_LastWeaponHashPickedUp = uWeaponHash;
|
|
m_TimeLastWeaponPickedUp = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPlayerInfo::DecayStealthNoise(float timeStep)
|
|
{
|
|
if(m_StealthNoise > ms_StealthNoiseMinValToCareAbout)
|
|
{
|
|
// To do linear decay, it would be appropriate to compute a decay factor like this
|
|
// const float mul = expf(-timeStep/ms_StealthNoiseExponentialDecayTimeConstant);
|
|
// but the exponential function is kind of slow and as long as the time step is small
|
|
// compared to the time constant, this is a decent enough approximation:
|
|
const float mul = Max(1.0f - timeStep/ms_StealthNoiseExponentialDecayTimeConstant, 0.0f);
|
|
|
|
m_StealthNoise *= mul;
|
|
if(m_StealthNoise <= ms_StealthNoiseMinValToCareAbout)
|
|
{
|
|
// No point in doing this once we get to a value small enough, more
|
|
// clear to let it snap to zero rather than letting it go infinitely small.
|
|
m_StealthNoise = 0.0f;
|
|
}
|
|
#if __BANK && DEBUG_DRAW
|
|
else if (CPedDebugVisualiser::GetDebugDisplay() == CPedDebugVisualiser::eStealth)
|
|
{
|
|
grcDebugDraw::Circle(m_pPlayerPed->GetTransform().GetPosition(), m_StealthNoise, Color_BlanchedAlmond, m_pPlayerPed->GetTransform().GetRight(), m_pPlayerPed->GetTransform().GetForward());
|
|
}
|
|
#endif // __BANK && DEBUG_DRAW
|
|
|
|
// Note: originally I was thinking about linear decay
|
|
// m_StealthNoise = Max(m_StealthNoise - timeStep*ms_StealthNoiseLinearDecayRate, 0.0f);
|
|
// but I'm thinking that perhaps that will give you too much of a time difference between
|
|
// some quiet sound and a really loud one.
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ReportStealthNoise(float noiseRange)
|
|
{
|
|
if (noiseRange > 0.0f)
|
|
{
|
|
m_StealthNoise = Max(m_StealthNoise, noiseRange);
|
|
}
|
|
}
|
|
|
|
|
|
void CPlayerInfo::IncrementAiAttemptingArrestOnPlayer()
|
|
{
|
|
if(aiVerifyf(m_uNumAiAttemptingArrestOnPlayer < 255, "Something went wrong, ref for arresting peds on this player is way too high!"))
|
|
{
|
|
m_uNumAiAttemptingArrestOnPlayer++;
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::DecrementAiAttemptingArrestOnPlayer()
|
|
{
|
|
if(m_uNumAiAttemptingArrestOnPlayer > 0)
|
|
{
|
|
m_uNumAiAttemptingArrestOnPlayer--;
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::SetSimulateGaitInput(float fMoveBlendRatio, float fTime /*= -1.0f*/, float fHeading /*= 0.0f*/, bool bRelativeHeading /*= false*/, bool bNoInputInterruption /*= false*/)
|
|
{
|
|
m_bSimulateGaitInput = true;
|
|
m_fSimulateGaitMoveBlendRatio = fMoveBlendRatio;
|
|
m_fSimulateGaitDuration = fTime;
|
|
m_fSimulateGaitTimerCount = 0.0f;
|
|
m_fSimulateGaitHeading = fHeading;
|
|
m_bSimulateGaitUseRelativeHeading = bRelativeHeading;
|
|
m_fSimulateGaitStartHeading = GetPlayerPed()->GetCurrentHeading();
|
|
m_bSimulateGaitNoInputInterruption = bNoInputInterruption;
|
|
|
|
Assertf(!(fTime < 0.0f && bNoInputInterruption), "The player will never break out of simulated gait with infinite timer and no input interruption.");
|
|
}
|
|
|
|
void CPlayerInfo::ProcessFireProofTimer()
|
|
{
|
|
if(m_sFireProofTimer > 0)
|
|
{
|
|
m_sFireProofTimer -= fwTimer::GetTimeStepInMilliseconds();
|
|
if(m_sFireProofTimer > 0)
|
|
{
|
|
bFireProof = true;
|
|
}
|
|
else
|
|
{
|
|
bFireProof = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CPlayerInfo::HaveNumEnemiesChargedWithinQueryPeriod( u32 queryPeriodMS, int queryCount ) const
|
|
{
|
|
// initialize count to zero
|
|
int numEnemiesCount = 0;
|
|
|
|
// check the current time
|
|
u32 currentTimeMS = fwTimer::GetTimeInMilliseconds();
|
|
|
|
// compute the comparison time for this query
|
|
u32 queryWindowStartTimeMS = 0;
|
|
if( queryPeriodMS < currentTimeMS )
|
|
{
|
|
queryWindowStartTimeMS = currentTimeMS - queryPeriodMS;
|
|
}
|
|
|
|
// traverse the history
|
|
for(int i=0; i < CHARGER_HISTORY_SIZE; ++i)
|
|
{
|
|
// if the recorded time is within the query window
|
|
if( m_ChargedByEnemyHistoryMS[i] > queryWindowStartTimeMS )
|
|
{
|
|
// add to the count of qualifying charge incidents
|
|
numEnemiesCount++;
|
|
|
|
// check if the query count has been reached
|
|
if( numEnemiesCount >= queryCount )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// fewer than query count occurrences in query window
|
|
return false;
|
|
}
|
|
|
|
bool CPlayerInfo::IsBestChargerCandidate( const CPed* pCandidatePed ) const
|
|
{
|
|
// Check if the acceptance period is not open
|
|
if( m_BestCandidateAcceptanceEndTimeMS == 0 )
|
|
{
|
|
// no best candidate yet
|
|
return false;
|
|
}
|
|
|
|
// Check if the given ped is the stored best candidate
|
|
Assert(pCandidatePed);
|
|
if( m_BestEnemyChargerCandidatePed.Get() == pCandidatePed )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// by default the given ped is not the best candidate
|
|
return false;
|
|
}
|
|
|
|
void CPlayerInfo::ReportEnemyStartedCharge()
|
|
{
|
|
// get the current time to record
|
|
u32 uTimeToAddMS = fwTimer::GetTimeInMilliseconds();
|
|
|
|
// find the oldest entry
|
|
Assert(CHARGER_HISTORY_SIZE > 1);
|
|
u32 uMinRecordTime = m_ChargedByEnemyHistoryMS[0];
|
|
int iMinIndex = 0;
|
|
for(int i=1; i < CHARGER_HISTORY_SIZE; ++i)
|
|
{
|
|
if( m_ChargedByEnemyHistoryMS[i] < uMinRecordTime )
|
|
{
|
|
uMinRecordTime = m_ChargedByEnemyHistoryMS[i];
|
|
iMinIndex = i;
|
|
}
|
|
}
|
|
|
|
// overwrite the oldest entry with this new time
|
|
m_ChargedByEnemyHistoryMS[iMinIndex] = uTimeToAddMS;
|
|
|
|
// clear the stored best candidate for charging
|
|
m_BestEnemyChargerCandidatePed = NULL;
|
|
|
|
// close the acceptance period as the winning ped has charged
|
|
m_BestCandidateAcceptanceEndTimeMS = 0;
|
|
|
|
aiDebugf3("CHARGING: CPlayerInfo::ReportEnemyStartedCharge() at time %d", uTimeToAddMS);
|
|
}
|
|
|
|
void CPlayerInfo::RegisterCandidateCharger( const CPed* pCandidatePed )
|
|
{
|
|
// If the player ped is null
|
|
if( m_pPlayerPed.Get() == NULL )
|
|
{
|
|
// no work to do
|
|
return;
|
|
}
|
|
|
|
// If the acceptance period is open
|
|
if( m_BestCandidateAcceptanceEndTimeMS > 0 )
|
|
{
|
|
// not accepting candidates at this time
|
|
return;
|
|
}
|
|
|
|
// If there is an active election for smoke throw
|
|
if( m_BestEnemySmokeThrowerCandidatePed.Get() != NULL )
|
|
{
|
|
// not accepting candidates at this time
|
|
return;
|
|
}
|
|
|
|
// If the registration end time is zero
|
|
if( m_CandidateRegistrationEndTimeMS == 0 )
|
|
{
|
|
// schedule an end time for the registration
|
|
const u32 uRegistrationPeriodMS = 2000;
|
|
m_CandidateRegistrationEndTimeMS = fwTimer::GetTimeInMilliseconds() + uRegistrationPeriodMS;
|
|
aiDebugf3("CHARGING: CPlayerInfo::RegisterCandidateCharger scheduled new registration end: %d",m_CandidateRegistrationEndTimeMS);
|
|
}
|
|
|
|
// Check if the candidate ped is dead
|
|
if( m_BestEnemyChargerCandidatePed.Get() && m_BestEnemyChargerCandidatePed.Get()->GetIsDeadOrDying() )
|
|
{
|
|
// clear the handle
|
|
m_BestEnemyChargerCandidatePed = NULL;
|
|
aiDebugf3("CHARGING: CPlayerInfo::RegisterCandidateCharger set candidate to NULL");
|
|
}
|
|
|
|
// If the candidate is null
|
|
if( m_BestEnemyChargerCandidatePed.Get() == NULL )
|
|
{
|
|
// set the candidate as the current best
|
|
m_BestEnemyChargerCandidatePed = pCandidatePed;
|
|
aiDebugf3("CHARGING: CPlayerInfo::RegisterCandidateCharger candidate was NULL, now [0x%p]", pCandidatePed);
|
|
}
|
|
else // candidate exists
|
|
{
|
|
// compute the distances between candidates and the player
|
|
const Vec3V playerPosition = m_pPlayerPed->GetTransform().GetPosition();
|
|
const ScalarV candidateDistSq = DistSquared(pCandidatePed->GetTransform().GetPosition(), playerPosition);
|
|
const ScalarV bestCandidateDistSq = DistSquared(m_BestEnemyChargerCandidatePed->GetTransform().GetPosition(), playerPosition);
|
|
|
|
// if this candidate is closer to the player
|
|
if( IsLessThanAll(candidateDistSq, bestCandidateDistSq) )
|
|
{
|
|
aiDebugf3("CHARGING: CPlayerInfo::RegisterCandidateCharger candidate was [0x%p], now [0x%p]", m_BestEnemyChargerCandidatePed.Get(), pCandidatePed);
|
|
|
|
// then this is the new best candidate
|
|
m_BestEnemyChargerCandidatePed = pCandidatePed;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if at least queryCount throws have occurred within the given query period.
|
|
bool CPlayerInfo::HaveNumEnemiesThrownSmokeWithinQueryPeriod(u32 queryPeriodMS, int queryCount) const
|
|
{
|
|
// initialize count to zero
|
|
int numEnemiesCount = 0;
|
|
|
|
// check the current time
|
|
u32 currentTimeMS = fwTimer::GetTimeInMilliseconds();
|
|
|
|
// compute the comparison time for this query
|
|
u32 queryWindowStartTimeMS = 0;
|
|
if( queryPeriodMS < currentTimeMS )
|
|
{
|
|
queryWindowStartTimeMS = currentTimeMS - queryPeriodMS;
|
|
}
|
|
|
|
// traverse the history
|
|
for(int i=0; i < SMOKE_THROWER_HISTORY_SIZE; ++i)
|
|
{
|
|
// if the recorded time is within the query window
|
|
if( m_SmokeThrownByEnemyHistoryMS[i] > queryWindowStartTimeMS )
|
|
{
|
|
// add to the count of qualifying incidents
|
|
numEnemiesCount++;
|
|
|
|
// check if the query count has been reached
|
|
if( numEnemiesCount >= queryCount )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// fewer than query count occurrences in query window
|
|
return false;
|
|
}
|
|
|
|
// Check if the given ped is the best thrower candidate in the most recent registration period.
|
|
bool CPlayerInfo::IsBestSmokeThrowerCandidate(const CPed* pCandidatePed) const
|
|
{
|
|
// Check if the acceptance period is not open
|
|
if( m_BestCandidateAcceptanceEndTimeMS == 0 )
|
|
{
|
|
// no best candidate yet
|
|
return false;
|
|
}
|
|
|
|
// Check if the given ped is the stored best candidate
|
|
Assert(pCandidatePed);
|
|
if( m_BestEnemySmokeThrowerCandidatePed.Get() == pCandidatePed )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// by default the given ped is not the best candidate
|
|
return false;
|
|
}
|
|
|
|
// The best thrower candidate has started his throw.
|
|
// Adds current time to history and closes the acceptance period.
|
|
void CPlayerInfo::ReportEnemyStartedSmokeThrow()
|
|
{
|
|
// get the current time to record
|
|
u32 uTimeToAddMS = fwTimer::GetTimeInMilliseconds();
|
|
|
|
// find the oldest entry
|
|
Assert(SMOKE_THROWER_HISTORY_SIZE > 1);
|
|
u32 uMinRecordTime = m_SmokeThrownByEnemyHistoryMS[0];
|
|
int iMinIndex = 0;
|
|
for(int i=1; i < SMOKE_THROWER_HISTORY_SIZE; ++i)
|
|
{
|
|
if( m_SmokeThrownByEnemyHistoryMS[i] < uMinRecordTime )
|
|
{
|
|
uMinRecordTime = m_SmokeThrownByEnemyHistoryMS[i];
|
|
iMinIndex = i;
|
|
}
|
|
}
|
|
|
|
// overwrite the oldest entry with this new time
|
|
m_SmokeThrownByEnemyHistoryMS[iMinIndex] = uTimeToAddMS;
|
|
|
|
// clear the stored best candidate
|
|
m_BestEnemySmokeThrowerCandidatePed = NULL;
|
|
|
|
// close the acceptance period as the winning ped has executed
|
|
m_BestCandidateAcceptanceEndTimeMS = 0;
|
|
|
|
aiDebugf3("SMOKE THROW: CPlayerInfo::ReportEnemyStartedSmokeThrow() at time %d", uTimeToAddMS);
|
|
}
|
|
|
|
// Consider the given ped as a candidate.
|
|
// Opens a new registration period if appropriate.
|
|
// Does nothing if the acceptance period is open.
|
|
void CPlayerInfo::RegisterCandidateSmokeThrower(const CPed* pCandidatePed)
|
|
{
|
|
// If the player ped is null
|
|
if( m_pPlayerPed.Get() == NULL )
|
|
{
|
|
// no work to do
|
|
return;
|
|
}
|
|
|
|
// If the acceptance period is open
|
|
if( m_BestCandidateAcceptanceEndTimeMS > 0 )
|
|
{
|
|
// not accepting candidates at this time
|
|
return;
|
|
}
|
|
|
|
// If there is an active election for charging
|
|
if( m_BestEnemyChargerCandidatePed.Get() != NULL )
|
|
{
|
|
// not accepting candidates at this time
|
|
return;
|
|
}
|
|
|
|
// If the registration end time is zero
|
|
if( m_CandidateRegistrationEndTimeMS == 0 )
|
|
{
|
|
// schedule an end time for the registration
|
|
const u32 uRegistrationPeriodMS = 2000;
|
|
m_CandidateRegistrationEndTimeMS = fwTimer::GetTimeInMilliseconds() + uRegistrationPeriodMS;
|
|
aiDebugf3("SMOKE THROW: CPlayerInfo::RegisterCandidateSmokeThrower scheduled new registration end: %d",m_CandidateRegistrationEndTimeMS);
|
|
}
|
|
|
|
// Check if the candidate ped is dead
|
|
if( m_BestEnemySmokeThrowerCandidatePed.Get() && m_BestEnemySmokeThrowerCandidatePed.Get()->GetIsDeadOrDying() )
|
|
{
|
|
// clear the handle
|
|
m_BestEnemySmokeThrowerCandidatePed = NULL;
|
|
aiDebugf3("SMOKE THROW: CPlayerInfo::RegisterCandidateSmokeThrower set candidate to NULL");
|
|
}
|
|
|
|
// If the candidate is null
|
|
if( m_BestEnemySmokeThrowerCandidatePed.Get() == NULL )
|
|
{
|
|
// set the candidate as the current best
|
|
m_BestEnemySmokeThrowerCandidatePed = pCandidatePed;
|
|
aiDebugf3("SMOKE THROW: CPlayerInfo::RegisterCandidateSmokeThrower candidate was NULL, now [0x%p]", pCandidatePed);
|
|
}
|
|
else // candidate exists
|
|
{
|
|
// compute the distances between candidates and the player
|
|
const Vec3V playerPosition = m_pPlayerPed->GetTransform().GetPosition();
|
|
const ScalarV candidateDistSq = DistSquared(pCandidatePed->GetTransform().GetPosition(), playerPosition);
|
|
const ScalarV bestCandidateDistSq = DistSquared(m_BestEnemySmokeThrowerCandidatePed->GetTransform().GetPosition(), playerPosition);
|
|
|
|
// if this candidate is closer to the player
|
|
if( IsLessThanAll(candidateDistSq, bestCandidateDistSq) )
|
|
{
|
|
aiDebugf3("SMOKE THROW: CPlayerInfo::RegisterCandidateSmokeThrower candidate was [0x%p], now [0x%p]", m_BestEnemySmokeThrowerCandidatePed.Get(), pCandidatePed);
|
|
|
|
// then this is the new best candidate
|
|
m_BestEnemySmokeThrowerCandidatePed = pCandidatePed;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::ShoutTargetPosition()
|
|
{
|
|
if(!m_pPlayerPed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(fwTimer::GetTimeInMilliseconds() - m_LastShoutTargetPositionTime < ms_Tunables.m_TimeBetweenShoutTargetPosition)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CEntity* pTargetEntity = m_targeting.GetTarget();
|
|
if(pTargetEntity && pTargetEntity->GetIsTypePed())
|
|
{
|
|
CPedGroup* pPedGroup = m_pPlayerPed->GetPedsGroup();
|
|
if(pPedGroup)
|
|
{
|
|
CEventShoutTargetPosition shoutTargetEvent(m_pPlayerPed, static_cast<CPed*>(pTargetEntity));
|
|
pPedGroup->GiveEventToAllMembers(shoutTargetEvent, m_pPlayerPed);
|
|
|
|
m_LastShoutTargetPositionTime = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 CPlayerInfo::GetTimeSinceLastAimedMS()
|
|
{
|
|
return fwTimer::GetTimeInMilliseconds() - m_uLastTimeStopAiming;
|
|
}
|
|
|
|
void CPlayerInfo::ClearPreviousVariationData()
|
|
{
|
|
m_PreviousVariationComponent = PV_COMP_INVALID;
|
|
m_PreviousVariationDrawableId = 0;
|
|
m_PreviousVariationDrawableAltId = 0;
|
|
m_PreviousVariationTexId = 0;
|
|
m_PreviousVariationPaletteId = 0;
|
|
}
|
|
|
|
void CPlayerInfo::GetPreviousVariationInfo(u32& rPreviousVariationDrawableId, u32& rPreviousVariationDrawableAltId, u32& rPreviousVariationTexId, u32& rPreviousVariationPaletteId)
|
|
{
|
|
rPreviousVariationDrawableId = m_PreviousVariationDrawableId;
|
|
rPreviousVariationDrawableAltId = m_PreviousVariationDrawableAltId;
|
|
rPreviousVariationTexId = m_PreviousVariationTexId;
|
|
rPreviousVariationPaletteId = m_PreviousVariationPaletteId;
|
|
}
|
|
|
|
void CPlayerInfo::SetPreviousVariationData(ePedVarComp ePreviousVariationComponent, u32 uPreviousVariationDrawableId, u32 uPreviousVariationDrawableAltId,
|
|
u32 uPreviousVariationTexId, u32 uPreviousVariationPaletteId)
|
|
{
|
|
m_PreviousVariationComponent = ePreviousVariationComponent;
|
|
m_PreviousVariationDrawableId = uPreviousVariationDrawableId;
|
|
m_PreviousVariationDrawableAltId = uPreviousVariationDrawableAltId;
|
|
m_PreviousVariationTexId = uPreviousVariationTexId;
|
|
m_PreviousVariationPaletteId = uPreviousVariationPaletteId;
|
|
}
|
|
|
|
void CPlayerInfo::SetPedParachuteVariationOverride(ePedVarComp slotId, u32 drawblId, u32 texId, u32 altDrawblId)
|
|
{
|
|
m_ParachuteVariationComponent = slotId;
|
|
m_ParachuteVariationDrawable = drawblId;
|
|
m_ParachuteVariationAltDrawable = altDrawblId;
|
|
m_ParachuteVariationTexId = texId;
|
|
}
|
|
|
|
void CPlayerInfo::ClearPedParachuteVariationOverride()
|
|
{
|
|
m_ParachuteVariationComponent = PV_COMP_INVALID;
|
|
m_ParachuteVariationDrawable = 0;
|
|
m_ParachuteVariationAltDrawable = 0;
|
|
m_ParachuteVariationTexId = 0;
|
|
}
|
|
|
|
#if __BANK
|
|
|
|
void CPlayerInfo::AddAimWidgets(bkBank & bank)
|
|
{
|
|
bank.AddSlider("Low aim boundary", &ms_PlayerLowAimBoundry, 0.0f, 1.0f, 0.05f);
|
|
bank.AddSlider("Soft/Hard aim boundary", &ms_PlayerSoftAimBoundary, 0.0f, 1.0f, 0.05f);
|
|
bank.AddSlider("Soft target switch boundary", &ms_PlayerSoftTargetSwitchBoundary, 0.0f, 1.0f, 0.05f);
|
|
bank.AddToggle("Reverse LT soft/hard aim", &ms_ReverseAimBoundarys);
|
|
#if FPS_MODE_SUPPORTED
|
|
bank.AddSlider("FPS fire boundary low", &ms_PlayerFPSFireBoundaryLow, 0.0f, 1.0f, 0.05f);
|
|
bank.AddSlider("FPS fire boundary high", &ms_PlayerFPSFireBoundaryHigh, 0.0f, 1.0f, 0.05f);
|
|
#endif
|
|
}
|
|
|
|
#endif // __BANK
|
|
|
|
#if __BANK
|
|
void CPlayerInfo::AddWeaponTuningWidgets(bkBank& bank)
|
|
{
|
|
bank.AddSlider("Player Weapon Damage Modifier", &m_fWeaponDamageModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Weapon Defense Modifier", &m_fWeaponDefenseModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Weapon Minigun Defense Modifier", &m_fWeaponMinigunDefenseModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Melee Weapon Damage Modifier", &m_fMeleeWeaponDamageModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Melee Unarmed Damage Modifier", &m_fMeleeUnarmedDamageModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Melee Weapon Defense Modifier", &m_fMeleeWeaponDefenseModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Melee Weapon Force Modifier", &m_fMeleeWeaponForceModifier, 1.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Vehicle Damage Modifier", &m_fVehicleDamageModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Vehicle Defense Modifier", &m_fVehicleDefenseModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Max Explosive Damage", &m_fMaxExplosiveDamage, -1.0f, 10000.0f, 0.01f);
|
|
bank.AddSlider("Player Explosive Damage Modifier", &m_fExplosiveDamageModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Player Weapon Takedown Defense Modifier", &m_fWeaponTakedownDefenseModifier, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Ai Weapon Damage Modifier", CPedDamageCalculator::GetAiWeaponDamageModifierPtr(), 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Ai Melee Weapon Damage Modifier", CPedDamageCalculator::GetAiMeleeWeaponDamageModifierPtr(), 0.0f, 10.0f, 0.01f);
|
|
}
|
|
#endif // __BANK
|
|
|
|
bool CPlayerInfo::IsPlayerStateValidForFPSManipulation() const
|
|
{
|
|
if(m_pPlayerPed &&
|
|
!m_pPlayerPed->GetMovePed().GetTaskNetwork() &&
|
|
m_pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_AMBIENT_CLIPS) &&
|
|
!m_pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_DisableIdleExtraHeadingChange) &&
|
|
!m_pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_PreserveAnimatedAngularVelocity) &&
|
|
m_pPlayerPed->IsLocalPlayer() && !AreControlsDisabled() &&
|
|
m_pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false, false, true))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SpecialAbilityType CPlayerInfo::GetMPSpecialAbility(ePlayerSpecialAbilitySlot eAbilitySlot /*= PSAS_PRIMARY*/) const
|
|
{
|
|
if (CPed::IsValidSpecialAbilitySlot(eAbilitySlot))
|
|
{
|
|
return m_eSpecialAbilitiesMP[eAbilitySlot];
|
|
}
|
|
else
|
|
{
|
|
return SAT_NONE;
|
|
}
|
|
}
|
|
|
|
void CPlayerInfo::SetMPSpecialAbility(SpecialAbilityType eAbility, ePlayerSpecialAbilitySlot eAbilitySlot /*= PSAS_PRIMARY*/)
|
|
{
|
|
if (!CPed::IsValidSpecialAbilitySlot(eAbilitySlot))
|
|
return;
|
|
|
|
const bool bHasChanged = m_eSpecialAbilitiesMP[eAbilitySlot] != eAbility;
|
|
m_eSpecialAbilitiesMP[eAbilitySlot] = eAbility;
|
|
|
|
if (m_pPlayerPed)
|
|
{
|
|
// if special ability has been changed, or has not been initialised on ped, or is different to what's been set on the ped, init special ability on ped
|
|
if (bHasChanged || !m_pPlayerPed->GetSpecialAbility(eAbilitySlot) || m_eSpecialAbilitiesMP[eAbilitySlot] != m_pPlayerPed->GetSpecialAbilityType(eAbilitySlot))
|
|
{
|
|
m_pPlayerPed->InitSpecialAbility(eAbilitySlot);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CPlayerInfo::CanPlayerPerformVehicleMelee() const
|
|
{
|
|
TUNE_GROUP_BOOL(VEHICLE_MELEE, bDisableInSP, true);
|
|
if (bDisableInSP && !NetworkInterface::IsGameInProgress())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const CVehicle* pVeh = m_pPlayerPed->GetVehiclePedInside();
|
|
if (!pVeh)
|
|
return false;
|
|
|
|
if (!pVeh->InheritsFromBike() && !pVeh->InheritsFromQuadBike() && !pVeh->InheritsFromAmphibiousQuadBike())
|
|
return false;
|
|
|
|
if (MI_BIKE_OPPRESSOR.IsValid() && MI_BIKE_OPPRESSOR == pVeh->GetModelIndex())
|
|
return false;
|
|
|
|
if (m_pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IsSwitchingHelmetVisor))
|
|
return false;
|
|
|
|
if (m_pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_DisableVehicleCombat))
|
|
return false;
|
|
|
|
if (m_pPlayerPed->GetNetworkObject())
|
|
{
|
|
CNetObjPlayer* pOwnerNetObjPlayer = static_cast<CNetObjPlayer*>(m_pPlayerPed->GetNetworkObject());
|
|
if (pOwnerNetObjPlayer->IsPassiveMode())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(VEHICLE_MELEE, bDisableStationaryKicks, true);
|
|
if (bDisableStationaryKicks && ((pVeh->GetVelocity().Mag() <= CTaskMountThrowProjectile::ms_fBikeStationarySpeedThreshold) || (pVeh->GetThrottle() < -0.01f)))
|
|
{
|
|
const CPedWeaponManager* pWeaponMgr = m_pPlayerPed->GetWeaponManager();
|
|
if (pWeaponMgr && pWeaponMgr->GetEquippedWeaponInfo() && (pWeaponMgr->GetEquippedWeaponInfo()->GetIsUnarmed() || pWeaponMgr->GetEquippedWeaponInfo()->GetIsThrownWeapon()))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (m_pPlayerPed->GetWeaponManager())
|
|
{
|
|
const CWeaponInfo* pWeaponInfo = m_pPlayerPed->GetWeaponManager()->GetEquippedWeaponInfo();
|
|
if (pWeaponInfo)
|
|
{
|
|
const CVehicleDriveByAnimInfo* pDriveByClipInfo = CVehicleMetadataMgr::GetDriveByAnimInfoForWeapon(m_pPlayerPed, pWeaponInfo->GetHash());
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
// FP is not supported for now for bike melee
|
|
//bool bFirstPerson = false;
|
|
//if (m_pPlayerPed->IsFirstPersonShooterModeEnabledForPlayer(false) || m_pPlayerPed->IsInFirstPersonVehicleCamera())
|
|
// bFirstPerson = true;
|
|
|
|
//if (bFirstPerson && pDriveByClipInfo && pDriveByClipInfo->GetFirstPersonVehicleMeleeClipSet() == CLIP_SET_ID_INVALID)
|
|
//{
|
|
// return false;
|
|
//}
|
|
//else
|
|
#endif //FPS_MODE_SUPPORTED
|
|
if (pDriveByClipInfo && pDriveByClipInfo->GetVehicleMeleeClipSet() == CLIP_SET_ID_INVALID)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
// Static member initialisation
|
|
CAnimSpeedUps::Tunables CAnimSpeedUps::ms_Tunables;
|
|
IMPLEMENT_PLAYER_INFO_TUNABLES(CAnimSpeedUps, 0xcd9f180b);
|
|
|
|
#if RSG_PC
|
|
void ReportPlayerResetMismatch(u32 expected, u32 tampered)
|
|
{
|
|
// Here, we want to
|
|
aiAssertf(false, "Unexpected mismatch in CPlayeResetFlags; please create a B* on Amir Soofi & CC Sean Casey");
|
|
LinkDataReport r(expected^tampered, LinkDataReporterCategories::LDRC_PLAYERRESET);
|
|
LinkDataReporterPlugin_Report(r);
|
|
return;
|
|
}
|
|
#endif |