enum AimAssistSettingConfig
{
	Default = 0,
	Scanning = 1,
	LeftHandCyberwareCharge = 2,
	LeftHandCyberware = 3,
	MeleeCombat = 4,
	MeleeCombatIdle = 5,
	AimingLimbCyber = 6,
	AimingLimbCyberZoomLevel1 = 7,
	AimingLimbCyberZoomLevel2 = 8,
	Aiming = 9,
	QuickMelee = 10,
	VehicleCombat = 11,
	Sprinting = 12,
	LimbCyber = 13,
	Vehicle = 14,
	DriverCombat = 15,
	DriverCombatAiming = 16,
	DriverCombatTPP = 17,
	DriverCombatMissiles = 18,
	DriverCombatMissilesAiming = 19,
	DriverCombatMeleeTPP = 20,
	ZoomLevel1 = 21,
	ZoomLevel2 = 22,
	Exhausted = 23,
	Off = 24,
	Count = 25,
}

function GetPlayer( gameInstance : GameInstance ) : PlayerPuppet
{
	if( GameInstance.GetPlayerSystem( gameInstance ) )
	{
		return ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( gameInstance ).GetLocalPlayerControlledGameObject() ) );
	}
	else
	{
		return NULL;
	}
}

function GetMainPlayer( gameInstance : GameInstance ) : PlayerPuppet
{
	if( GameInstance.GetPlayerSystem( gameInstance ) )
	{
		return ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( gameInstance ).GetLocalPlayerMainGameObject() ) );
	}
	else
	{
		return NULL;
	}
}

function GetPlayerObject( gameInstance : GameInstance ) : GameObject
{
	return ( ( GameObject )( GameInstance.GetPlayerSystem( gameInstance ).GetLocalPlayerControlledGameObject() ) );
}

function IsHostileTowardsPlayer( object : weak< GameObject > ) : Bool
{
	var player : weak< PlayerPuppet >;
	if( !( object ) )
	{
		return false;
	}
	player = GetPlayer( object.GetGame() );
	if( player && GameObject.GetAttitudeTowards( object, player ) == EAIAttitude.AIA_Hostile )
	{
		return true;
	}
	return false;
}

function IsFriendlyTowardsPlayer( object : weak< GameObject > ) : Bool
{
	var player : weak< PlayerPuppet >;
	if( !( object ) )
	{
		return false;
	}
	player = GetPlayer( object.GetGame() );
	if( player && GameObject.GetAttitudeTowards( object, player ) == EAIAttitude.AIA_Friendly )
	{
		return true;
	}
	return false;
}

struct KeyBindings
{
	persistent var DPAD_UP : TweakDBID;
	persistent var RB : TweakDBID;
}

class PlayerPuppetPS extends ScriptedPuppetPS
{
	private persistent var keybindigs : KeyBindings;
	private persistent var m_availablePrograms : array< MinigameProgramData >;
	private persistent var m_hasAutoReveal : Bool;
	private persistent var m_combatExitTimestamp : Float;
	private persistent var m_isInDriverCombat : Bool;
	private persistent var m_pocketRadioStation : Int32;
	private persistent var m_permanentHealthBonus : Float;
	private persistent var m_permanentStaminaBonus : Float;
	private persistent var m_permanentMemoryBonus : Float;
	private var m_minigameBB : weak< IBlackboard >;

	public const function GetCombatExitTimestamp() : Float
	{
		return m_combatExitTimestamp;
	}

	public function SetCombatExitTimestamp( timestamp : Float )
	{
		m_combatExitTimestamp = timestamp;
	}

	public const function HasAutoReveal() : Bool
	{
		return m_hasAutoReveal;
	}

	public function SetAutoReveal( value : Bool )
	{
		m_hasAutoReveal = value;
	}

	public const function IsInDriverCombat() : Bool
	{
		return m_isInDriverCombat;
	}

	public function SetIsInDriverCombat( value : Bool )
	{
		m_isInDriverCombat = value;
	}

	public const function GetPocketRadioStation() : Int32
	{
		return m_pocketRadioStation;
	}

	public function SetPocketRadioStation( value : Int32 )
	{
		m_pocketRadioStation = value;
	}

	public function GetPermanentHealthBonus() : Float
	{
		return m_permanentHealthBonus;
	}

	public function SetPermanentHealthBonus( value : Float )
	{
		m_permanentHealthBonus = value;
	}

	public function GetPermanentStaminaBonus() : Float
	{
		return m_permanentStaminaBonus;
	}

	public function SetPermanentStaminaBonus( value : Float )
	{
		m_permanentStaminaBonus = value;
	}

	public function GetPermanentMemoryBonus() : Float
	{
		return m_permanentMemoryBonus;
	}

	public function SetPermanentMemoryBonus( value : Float )
	{
		m_permanentMemoryBonus = value;
	}

	protected export function OnStoreMinigameProgram( evt : StoreMiniGameProgramEvent ) : EntityNotificationType
	{
		if( evt.add )
		{
			AddMinigameProgram( evt.program );
		}
		else
		{
			RemoveMinigameProgram( evt.program );
		}
		GetMinigameBlackboard().SetVariant( GetAllBlackboardDefs().HackingMinigame.PlayerPrograms, m_availablePrograms );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function AddMinigameProgram( program : MinigameProgramData )
	{
		var programTemp : MinigameProgramData;
		if( !( HasProgram( program.actionID, m_availablePrograms ) ) || ( program.actionID == T"MinigameAction.NetworkLowerICEMedium" ) )
		{
			if( ( ( program.actionID == T"MinigameAction.NetworkLowerICEMedium" ) || ( program.actionID == T"MinigameAction.NetworkLowerICEMajor" ) ) || ( program.actionID == T"MinigameAction.NetworkLowerICEMinorFirst" ) )
			{
				programTemp = DecideProgramToAdd( program.actionID );
				if( programTemp.programName != 'None' )
				{
					m_availablePrograms.Insert( 0, programTemp );
				}
			}
			else
			{
				UpgradePrograms( program.actionID );
				m_availablePrograms.PushBack( program );
			}
		}
	}

	protected function RemoveMinigameProgram( program : MinigameProgramData )
	{
		RemoveProgram( program.actionID );
	}

	protected function RemoveProgram( id : TweakDBID )
	{
		var i : Int32;
		for( i = m_availablePrograms.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_availablePrograms[ i ].actionID == id )
			{
				m_availablePrograms.Erase( i );
			}
		}
	}

	protected function HasProgram( id : TweakDBID ) : Bool
	{
		var i : Int32;
		for( i = m_availablePrograms.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_availablePrograms[ i ].actionID == id )
			{
				return true;
			}
		}
		return false;
	}

	protected function UpgradePrograms( id : TweakDBID )
	{
		if( id == T"MinigameAction.NetworkCameraFriendly" )
		{
			RemoveProgram( T"MinigameAction.NetworkCameraMalfunction" );
		}
		else if( id == T"MinigameAction.NetworkCameraShutdown" )
		{
			RemoveProgram( T"MinigameAction.NetworkCameraMalfunction" );
		}
		else if( id == T"MinigameAction.NetworkTurretFriendly" )
		{
			RemoveProgram( T"MinigameAction.NetworkTurretMalfunction" );
		}
		else if( id == T"MinigameAction.NetworkTurretShutdown" )
		{
			RemoveProgram( T"MinigameAction.NetworkTurretMalfunction" );
		}
	}

	protected function DecideProgramToAdd( id : TweakDBID ) : MinigameProgramData
	{
		var program : MinigameProgramData;
		if( id == T"MinigameAction.NetworkLowerICEMinorFirst" )
		{
			if( HasProgram( T"MinigameAction.NetworkLowerICEMedium" ) || HasProgram( T"MinigameAction.NetworkLowerICEMajor" ) )
			{
				program.programName = 'None';
				return program;
			}
			else
			{
				program.actionID = T"MinigameAction.NetworkLowerICEMinorFirst";
				program.programName = 'LocKey#34844';
				return program;
			}
		}
		if( id == T"MinigameAction.NetworkLowerICEMedium" )
		{
			if( HasProgram( T"MinigameAction.NetworkLowerICEMedium" ) )
			{
				program.actionID = T"MinigameAction.NetworkLowerICEMajor";
				program.programName = 'LocKey#34844';
				RemoveProgram( T"MinigameAction.NetworkLowerICEMedium" );
				return program;
			}
			else if( HasProgram( T"MinigameAction.NetworkLowerICEMajor" ) )
			{
				program.programName = 'None';
				return program;
			}
			else
			{
				program.actionID = T"MinigameAction.NetworkLowerICEMedium";
				program.programName = 'LocKey#34844';
				RemoveProgram( T"MinigameAction.NetworkLowerICEMinorFirst" );
				return program;
			}
		}
		if( id == T"MinigameAction.NetworkLowerICEMajor" )
		{
			if( !( HasProgram( T"MinigameAction.NetworkLowerICEMedium" ) ) )
			{
				program.actionID = T"MinigameAction.NetworkLowerICEMedium";
				program.programName = 'LocKey#34844';
				RemoveProgram( T"MinigameAction.NetworkLowerICEMinorFirst" );
				return program;
			}
			else
			{
				program.actionID = T"MinigameAction.NetworkLowerICEMajor";
				program.programName = 'LocKey#34844';
				RemoveProgram( T"MinigameAction.NetworkLowerICEMedium" );
				return program;
			}
		}
		return program;
	}

	private function GetMinigameBlackboard() : IBlackboard
	{
		if( m_minigameBB == NULL )
		{
			m_minigameBB = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().HackingMinigame );
		}
		return m_minigameBB;
	}

	public const function GetMinigamePrograms() : array< MinigameProgramData >
	{
		return m_availablePrograms;
	}

	protected function HasProgram( id : TweakDBID, const programs : ref< array< MinigameProgramData > > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < programs.Size(); i += 1 )
		{
			if( programs[ i ].actionID == id )
			{
				return true;
			}
		}
		return false;
	}

}

class CPOMissionDataState
{
	var m_CPOMissionDataDamagesPreset : CName;
	var m_compatibleDeviceName : CName;
	var m_ownerDecidesOnTransfer : Bool;
	var m_isChoiceToken : Bool;
	default m_isChoiceToken = false;
	var m_choiceTokenTimeout : Uint32;
	default m_choiceTokenTimeout = 0;
	var m_delayedGiveChoiceTokenEventId : DelayID;
	private var m_dataDamageTextLayerId : Uint32;

	public function OnDamage( puppet : PlayerPuppet, healthDamage : Bool )
	{
		var message : String;
		var soundEvent : SoundPlayEvent;
		var delaySystem : DelaySystem;
		var updateEvent : CPOMissionDataUpdateEvent;
		delaySystem = GameInstance.GetDelaySystem( puppet.GetGame() );
		if( puppet.HasCPOMissionData() && !( puppet.m_CPOMissionDataState.m_isChoiceToken ) )
		{
			message = "Corrupted data - internal damage!";
			m_dataDamageTextLayerId = GameInstance.GetDebugVisualizerSystem( puppet.GetGame() ).DrawText( Vector4( 500.0, 200.0, 0.0, 1.5 ), message, gameDebugViewETextAlignment.Center, Color( 255, 0, 0, 255 ), 1.0 );
			GameInstance.GetDebugVisualizerSystem( puppet.GetGame() ).SetScale( m_dataDamageTextLayerId, Vector4( 3.0, 3.0, 0.0, 0.0 ) );
			if( healthDamage )
			{
				soundEvent = new SoundPlayEvent;
				soundEvent.soundName = 'test_ad_emitter_2_1';
				puppet.QueueEvent( soundEvent );
			}
			GameObject.SetAudioParameter( puppet, 'g_player_health', 30.0 );
			GameObject.PlaySoundEvent( puppet, 'ST_Health_Status_Low_Set_State' );
			updateEvent = new CPOMissionDataUpdateEvent;
			delaySystem.DelayEvent( puppet, updateEvent, 1.0 );
		}
	}

	public function UpdateSounds( puppet : PlayerPuppet )
	{
		if( !( puppet.HasCPOMissionData() ) )
		{
			GameObject.SetAudioParameter( puppet, 'g_player_health', 1.0 );
			GameObject.PlaySoundEvent( puppet, 'ST_Health_Status_Hi_Set_State' );
		}
	}

}

enum LaserTargettingState
{
	Start = 0,
	Update = 1,
	End = 2,
}

struct InterestingFacts
{
	var m_zone : CName;
}

struct InterestingFactsListenersIds
{
	var m_zone : Uint32;
}

struct InterestingFactsListenersFunctions
{
	var m_zone : CName;
}

import enum gameCityAreaType
{
	Undefined,
	PublicZone,
	SafeZone,
	RestrictedZone,
	DangerousZone,
}

import class gameChaseSpawnComponent extends IComponent
{
}

class PlayerPuppet extends ScriptedPuppet
{
	private var m_quickSlotsManager : QuickSlotsManager;
	private var m_inspectionComponent : InspectionComponent;
	private var m_enviroDamageRcvComponent : EnvironmentDamageReceiverComponent;
	private var m_mountedVehicle : weak< VehicleObject >;
	private var m_vehicleKnockdownTimestamp : Float;
	var m_Phone : PlayerPhone;
	private var m_fppCameraComponent : FPPCameraComponent;
	private var m_primaryTargetingComponent : TargetingComponent;
	private var m_breachFinderComponent : BreachFinderComponent;
	private var m_chaseSpawnComponent : gameChaseSpawnComponent;
	private var m_isInFinisher : Bool;
	var DEBUG_Visualizer : DEBUG_VisualizerComponent;
	private var m_Debug_DamageInputRec : DEBUG_DamageInputReceiver;
	var m_highDamageThreshold : Float;
	var m_medDamageThreshold : Float;
	var m_lowDamageThreshold : Float;
	var m_meleeHighDamageThreshold : Float;
	var m_meleeMedDamageThreshold : Float;
	var m_meleeLowDamageThreshold : Float;
	var m_explosionHighDamageThreshold : Float;
	var m_explosionMedDamageThreshold : Float;
	var m_explosionLowDamageThreshold : Float;
	var m_effectTimeStamp : Float;
	var m_curInventoryWeight : Float;
	var m_healthVfxBlackboard : worldEffectBlackboard;
	var m_laserTargettingVfxBlackboard : worldEffectBlackboard;
	var m_itemLogBlackboard : weak< IBlackboard >;
	var m_interactionDataListener : CallbackHandle;
	var m_popupIsModalListener : CallbackHandle;
	var m_uiVendorContextListener : CallbackHandle;
	var m_uiRadialContextistener : CallbackHandle;
	var m_contactsActiveListener : CallbackHandle;
	var m_smsMessengerActiveListener : CallbackHandle;
	var m_currentVisibleTargetListener : CallbackHandle;
	var lastScanTarget : weak< GameObject >;
	var meleeSelectInputProcessed : Bool;
	private var m_waitingForDelayEvent : Bool;
	default m_waitingForDelayEvent = false;
	private var m_randomizedTime : Float;
	private var m_isResetting : Bool;
	private var m_delayEventID : DelayID;
	private var m_resetTickID : DelayID;
	private var m_katanaAnimProgression : Float;
	private var m_coverModifierActive : Bool;
	private var m_workspotDamageReductionActive : Bool;
	private var m_workspotVisibilityReductionActive : Bool;
	private var m_currentPlayerWorkspotTags : array< CName >;
	private var m_incapacitated : Bool;
	private var m_remoteMappinId : NewMappinID;
	var m_CPOMissionDataState : CPOMissionDataState;
	private var m_CPOMissionDataBbId : CallbackHandle;
	private var m_visibilityListener : VisibilityStatListener;
	private var m_secondHeartListener : SecondHeartStatListener;
	private var m_armorStatListener : ArmorStatListener;
	private var m_healthStatListener : HealthStatListener;
	private var m_oxygenStatListener : OxygenStatListener;
	private var m_aimAssistListener : AimAssistSettingsListener;
	private var m_autoRevealListener : AutoRevealStatListener;
	private var m_allStatsListener : PlayerPuppetAllStatListener;
	private var m_rightHandAttachmentSlotListener : AttachmentSlotsScriptListener;
	private var m_HealingItemsChargeStatListener : HealingItemsChargeStatListener;
	private var m_GrenadesChargeStatListener : GrenadesChargeStatListener;
	private var m_ProjectileLauncherChargeStatListener : ProjectileLauncherChargeStatListener;
	private var m_OpticalCamoChargeStatListener : OpticalCamoChargeStatListener;
	private var m_OverclockChargeListener : OverclockChargeListener;
	private var m_accessibilityControlsListener : AccessibilityControlsListener;
	private var isTalkingOnPhone : Bool;
	private var m_DataDamageUpdateID : DelayID;
	private var m_playerAttachedCallbackID : Uint32;
	private var m_playerDetachedCallbackID : Uint32;
	private var m_callbackHandles : array< CallbackHandle >;
	private var m_numberOfCombatants : Int32;
	private var m_equipmentMeshOverlayEffectName : CName;
	private var m_equipmentMeshOverlayEffectTag : CName;
	private var m_equipmentMeshOverlaySlots : array< TweakDBID >;
	private var m_coverVisibilityPerkBlocked : Bool;
	private var m_behindCover : Bool;
	private var m_inCombat : Bool;
	private var m_isBeingRevealed : Bool;
	private var m_hasBeenDetected : Bool;
	private var m_inCrouch : Bool;
	private var m_hasKiroshiOpticsFragment : Bool;
	private var m_doingQuickMelee : Bool;
	private var m_vehicleState : gamePSMVehicle;
	private var m_inMountedWeaponVehicle : Bool;
	private var m_inDriverCombatTPP : Bool;
	private var m_driverCombatWeaponType : gamedataItemType;
	private var m_isAiming : Bool;
	private var m_focusModeActive : Bool;
	private var m_customFastForwardPossible : Bool;
	private var m_equippedRightHandWeapon : weak< WeaponObject >;
	private var m_aimAssistUpdateQueued : Bool;
	private var m_locomotionState : Int32;
	private var m_leftHandCyberwareState : Int32;
	private var m_meleeWeaponState : Int32;
	private var m_weaponZoomLevel : Float;
	private var m_controllingDeviceID : EntityID;
	private var m_gunshotRange : Float;
	private var m_explosionRange : Float;
	private var m_isInBodySlam : Bool;
	private var m_combatGadgetState : Int32;
	private var m_sceneTier : GameplayTier;
	default m_sceneTier = GameplayTier.Undefined;
	private var m_nextBufferModifier : Int32;
	private var m_attackingNetrunnerID : EntityID;
	private var m_NPCDeathInstigator : weak< NPCPuppet >;
	private var m_bestTargettingWeapon : weak< WeaponObject >;
	private var m_bestTargettingDot : Float;
	private var m_targettingEnemies : Int32;
	private var m_isAimingAtFriendly : Bool;
	private var m_isAimingAtChild : Bool;
	private var m_distanceFromTargetSquared : Float;
	private var m_coverRecordID : TweakDBID;
	private var m_damageReductionRecordID : TweakDBID;
	private var m_visReductionRecordID : TweakDBID;
	private var m_lastDmgInflicted : EngineTime;
	private var m_critHealthRumblePlayed : Bool;
	default m_critHealthRumblePlayed = false;
	private var m_critHealthRumbleDurationID : DelayID;
	private var m_lastHealthUpdate : Float;
	private var m_staminaListener : StaminaListener;
	private var m_memoryListener : MemoryListener;
	var m_securityAreaTypeE3HACK : ESecurityAreaType;
	default m_securityAreaTypeE3HACK = ESecurityAreaType.DISABLED;
	private var m_overlappedSecurityZones : array< PersistentID >;
	private var m_interestingFacts : InterestingFacts;
	private var m_interestingFactsListenersIds : InterestingFactsListenersIds;
	private var m_interestingFactsListenersFunctions : InterestingFactsListenersFunctions;
	private var m_visionModeController : PlayerVisionModeController;
	private var m_combatController : PlayerCombatController;
	private var m_handlingModifiers : PlayerWeaponHandlingModifiers;
	private var m_cachedGameplayRestrictions : array< TweakDBID >;
	private var m_delayEndGracePeriodAfterSpawnEventID : DelayID;
	private var m_CWMaskInVehicleInputHeld : Bool;
	private var m_friendlyDevicesHostileToEnemiesLock : RWLock;
	private var m_friendlyDevicesHostileToEnemies : array< EntityID >;
	private var m_pocketRadio : PocketRadio;

	public const override function IsPlayer() : Bool
	{
		return true;
	}

	public const override function IsReplacer() : Bool
	{
		return GetRecord().GetID() != T"Character.Player_Puppet_Base";
	}

	public const override function IsVRReplacer() : Bool
	{
		return GetRecord().GetID() == T"Character.q000_vr_replacer";
	}

	public const override function IsJohnnyReplacer() : Bool
	{
		return GetRecord().GetID() == T"Character.johnny_replacer";
	}

	public constexpr export const function IsReplicable() : Bool
	{
		return true;
	}

	public constexpr const function GetReplicatedStateClass() : CName
	{
		return 'gamePlayerPuppetReplicatedState';
	}

	public const function IsCoverModifierAdded() : Bool
	{
		return m_coverModifierActive;
	}

	public const function IsWorkspotDamageReductionAdded() : Bool
	{
		return m_workspotDamageReductionActive;
	}

	public const function IsWorkspotVisibilityReductionActive() : Bool
	{
		return m_workspotVisibilityReductionActive;
	}

	public const function GetOverlappedSecurityZones() : array< PersistentID >
	{
		return m_overlappedSecurityZones;
	}

	public const function GetIsInWorkspotFinisher() : Bool
	{
		return m_isInFinisher;
	}

	protected const override function GetPS() : PlayerPuppetPS
	{
		return ( ( PlayerPuppetPS )( GetBasePS() ) );
	}

	public const function GetCombatExitTimestamp() : Float
	{
		return GetPS().GetCombatExitTimestamp();
	}

	public const function SetPermanentFoodBonus( type : gamedataStatType, value : Float )
	{
		if( type == gamedataStatType.HealthBonusBlackmarket )
		{
			GetPS().SetPermanentHealthBonus( value );
		}
		else if( type == gamedataStatType.StaminaRegenBonusBlackmarket )
		{
			GetPS().SetPermanentStaminaBonus( value );
		}
		else if( type == gamedataStatType.MemoryRegenBonusBlackmarket )
		{
			GetPS().SetPermanentMemoryBonus( value );
		}
	}

	public const function GetPermanentFoodBonus( type : gamedataStatType ) : Float
	{
		var resultValue : Float;
		if( type == gamedataStatType.HealthBonusBlackmarket )
		{
			resultValue = GetPS().GetPermanentHealthBonus();
		}
		else if( type == gamedataStatType.StaminaRegenBonusBlackmarket )
		{
			resultValue = GetPS().GetPermanentStaminaBonus();
		}
		else if( type == gamedataStatType.MemoryRegenBonusBlackmarket )
		{
			resultValue = GetPS().GetPermanentMemoryBonus();
		}
		return resultValue;
	}

	protected event OnRequestComponents( ri : EntityRequestComponentsInterface )
	{
		super.OnRequestComponents( ri );
		EntityRequestComponentsInterface.RequestComponent( ri, 'phone', 'PlayerPhone', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'inspect', 'InspectionComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'DEBUG_Visualizer', 'DEBUG_VisualizerComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'quickSlots', 'QuickSlotsManager', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'camera', 'gameFPPCameraComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'targeting_primary', 'gameTargetingComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'breachFinder', 'gameBreachFinderComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'ChaseSpawnComponent', 'gameChaseSpawnComponent', true );
		EntityRequestComponentsInterface.RequestComponent( ri, 'environmentDamageReceiver', 'EnvironmentDamageReceiverComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'vehicleVisualCustomization', 'vehicleVisualCustomizationComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'vehicleCameraManager', 'vehicleCameraManagerComponent', true );
		EntityRequestComponentsInterface.RequestComponent( ri, 'vehicleTPPCamera', 'vehicleTPPCameraComponent', true );
	}

	public function FindVehicleCameraManager() : VehicleCameraManager
	{
		var component : VehicleCameraManagerComponent;
		component = ( ( VehicleCameraManagerComponent )( FindComponentByName( 'vehicleCameraManager' ) ) );
		if( component )
		{
			return component.GetManagerHandle();
		}
		return NULL;
	}

	protected event OnTakeControl( ri : EntityResolveComponentsInterface )
	{
		super.OnTakeControl( ri );
		m_quickSlotsManager = ( ( QuickSlotsManager )( EntityResolveComponentsInterface.GetComponent( ri, 'quickSlots' ) ) );
		m_inspectionComponent = ( ( InspectionComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'inspect' ) ) );
		m_enviroDamageRcvComponent = ( ( EnvironmentDamageReceiverComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'environmentDamageReceiver' ) ) );
		DEBUG_Visualizer = ( ( DEBUG_VisualizerComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'DEBUG_Visualizer' ) ) );
		m_Phone = ( ( PlayerPhone )( EntityResolveComponentsInterface.GetComponent( ri, 'phone' ) ) );
		m_fppCameraComponent = ( ( FPPCameraComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'camera' ) ) );
		m_primaryTargetingComponent = ( ( TargetingComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'targeting_primary' ) ) );
		m_breachFinderComponent = ( ( BreachFinderComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'breachFinder' ) ) );
		m_breachFinderComponent.Init( this );
		m_chaseSpawnComponent = ( ( gameChaseSpawnComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'ChaseSpawnComponent' ) ) );
		m_visionModeController = new PlayerVisionModeController;
		m_combatController = new PlayerCombatController;
	}

	protected event OnReleaseControl()
	{
		m_visionModeController = NULL;
		m_combatController = NULL;
	}

	private function GracePeriodAfterSpawn()
	{
		var invisibilityDuration : Float;
		invisibilityDuration = TDB.GetFloat( T"player.stealth.durationOfGracePeriodAfterSpawn", -1.0 );
		if( invisibilityDuration > 0.0 )
		{
			SetInvisible( true );
			GameInstance.GetGodModeSystem( GetGame() ).AddGodMode( GetEntityID(), gameGodModeType.Invulnerable, 'GracePeriodAfterSpawn' );
			GameInstance.GetDelaySystem( GetGame() ).CancelDelay( m_delayEndGracePeriodAfterSpawnEventID );
			m_delayEndGracePeriodAfterSpawnEventID = GameInstance.GetDelaySystem( GetGame() ).DelayEvent( this, new EndGracePeriodAfterSpawn, invisibilityDuration );
		}
	}

	public function GetVehicleVisualCustomizationComponent() : vehicleVisualCustomizationComponent
	{
		var component : vehicleVisualCustomizationComponent;
		component = ( ( vehicleVisualCustomizationComponent )( FindComponentByName( 'vehicleVisualCustomization' ) ) );
		if( component )
		{
			return component;
		}
		return NULL;
	}

	private function UnlockAccessPointPrograms()
	{
		var program : MinigameProgramData;
		program.actionID = T"MinigameAction.NetworkDataMineLootAll";
		program.programName = StringToName( LocKeyToString( TweakDBInterface.GetObjectActionRecord( T"MinigameAction.NetworkDataMineLootAll" ).ObjectActionUI().Caption() ) );
		UpdateMinigamePrograms( program, true );
		program.actionID = T"MinigameAction.NetworkDataMineLootAllAdvanced";
		program.programName = StringToName( LocKeyToString( TweakDBInterface.GetObjectActionRecord( T"MinigameAction.NetworkDataMineLootAllAdvanced" ).ObjectActionUI().Caption() ) );
		UpdateMinigamePrograms( program, true );
		program.actionID = T"MinigameAction.NetworkDataMineLootAllMaster";
		program.programName = StringToName( LocKeyToString( TweakDBInterface.GetObjectActionRecord( T"MinigameAction.NetworkDataMineLootAllMaster" ).ObjectActionUI().Caption() ) );
		UpdateMinigamePrograms( program, true );
	}

	protected event OnMakePlayerVisibleAfterSpawn( evt : EndGracePeriodAfterSpawn )
	{
		SetInvisible( false );
		GameInstance.GetGodModeSystem( GetGame() ).RemoveGodMode( GetEntityID(), gameGodModeType.Invulnerable, 'GracePeriodAfterSpawn' );
	}

	public function IsAimingAtFriendly() : Bool
	{
		return m_isAimingAtFriendly;
	}

	public function IsAimingAtChild() : Bool
	{
		return m_isAimingAtChild;
	}

	public function DistanceFromTargetSquared() : Float
	{
		return m_distanceFromTargetSquared;
	}

	public function ReevaluateLookAtTarget()
	{
		var targetingSystem : TargetingSystem;
		var targetObject : GameObject;
		targetingSystem = GameInstance.GetTargetingSystem( GetGame() );
		targetObject = targetingSystem.GetLookAtObject( this, true, true );
		UpdateLookAtObject( targetObject );
	}

	private function UpdateLookAtObject( target : GameObject )
	{
		if( !( target ) )
		{
			m_isAimingAtFriendly = false;
			m_isAimingAtChild = false;
		}
		else
		{
			m_isAimingAtFriendly = PlayerPuppet.IsTargetFriendlyNPC( this, target );
			m_isAimingAtChild = PlayerPuppet.IsTargetChildNPC( this, target );
			m_distanceFromTargetSquared = Vector4.DistanceSquared( GetWorldPosition(), target.GetWorldPosition() );
		}
	}

	protected event OnLookAtObjectChangedEvent( evt : LookAtObjectChangedEvent )
	{
		UpdateLookAtObject( evt.lookatObject );
	}

	protected event OnWeaponEquipEvent( evt : WeaponEquipEvent )
	{
		AnimationControllerComponent.ApplyFeature( this, 'WeaponEquipType', evt.animFeature );
		AnimationControllerComponent.ApplyFeature( evt.item, 'WeaponEquipType', evt.animFeature );
	}

	protected event OnSetUpEquipmentOverlayEvent( evt : SetUpEquipmentOverlayEvent )
	{
		m_equipmentMeshOverlayEffectName = evt.meshOverlayEffectName;
		m_equipmentMeshOverlayEffectTag = evt.meshOverlayEffectTag;
		m_equipmentMeshOverlaySlots = evt.meshOverlaySlots;
	}

	protected event OnAppearanceChangeFinishEvent( evt : entAppearanceChangeFinishEvent )
	{
		var i : Int32;
		var ts : TransactionSystem;
		var item : weak< ItemObject >;
		var effect : EffectInstance;
		if( IsNameValid( m_equipmentMeshOverlayEffectName ) )
		{
			ts = GameInstance.GetTransactionSystem( GetGame() );
			for( i = 0; i < m_equipmentMeshOverlaySlots.Size(); i += 1 )
			{
				item = ts.GetItemInSlot( this, m_equipmentMeshOverlaySlots[ i ] );
				if( item )
				{
					effect = GameInstance.GetGameEffectSystem( GetGame() ).CreateEffectStatic( m_equipmentMeshOverlayEffectName, m_equipmentMeshOverlayEffectTag, this );
					if( effect )
					{
						EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.enable, true );
						EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.clearMaterialOverlayOnDetach, true );
						EffectData.SetEntity( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.entity, item );
						effect.Run();
					}
				}
			}
			effect = GameInstance.GetGameEffectSystem( GetGame() ).CreateEffectStatic( m_equipmentMeshOverlayEffectName, m_equipmentMeshOverlayEffectTag, this );
			if( effect )
			{
				EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.enable, true );
				EffectData.SetBool( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.clearMaterialOverlayOnDetach, true );
				EffectData.SetEntity( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.entity, this );
				effect.Run();
			}
		}
	}

	protected event OnSetSlowMoForOnePunchAttackEvent( evt : SetSlowMoForOnePunchAttackEvent )
	{
		SetSlowMo( TDB.GetFloat( T"Items.StrongArms.slowMoAmount" ), TDB.GetFloat( T"Items.StrongArms.slowDuration" ) );
	}

	protected function SetSlowMo( slowMoAmount : Float, slowMoDuration : Float )
	{
		GameInstance.GetTimeSystem( GetGame() ).SetTimeDilation( 'deflect', slowMoAmount, slowMoDuration );
	}

	protected event OnDodgeToAvoidCombatEvent( evt : DodgeToAvoidCombatEvent )
	{
		var npc : ScriptedPuppet;
		var justDodged : Bool;
		var playerIsVisible : Bool;
		var cooldownIsActive : Bool;
		var failureCooldownIsActive : Bool;
		npc = ( ( ScriptedPuppet )( GameInstance.FindEntityByID( GetGame(), evt.npcID ) ) );
		justDodged = StatusEffectHelper.HasStatusEffectWithTagConst( this, 'JustDodgedActiveBuffer' );
		playerIsVisible = npc.GetSenses().IsAgentVisible( this );
		cooldownIsActive = StatusEffectHelper.HasStatusEffectWithTagConst( this, 'DodgeOutOfSightCooldown' );
		failureCooldownIsActive = StatusEffectHelper.HasStatusEffectWithTagConst( this, 'SecondChancePerkCooldown' );
		if( ( ( justDodged && !( playerIsVisible ) ) && !( cooldownIsActive ) ) && !( failureCooldownIsActive ) )
		{
			StatusEffectHelper.ApplyStatusEffect( npc, T"BaseStatusEffect.MemoryWipeOnDodge" );
			GameObject.PlaySoundEvent( this, 'ui_gmpl_stealth_avoid_combat' );
			GameInstance.GetDelaySystem( GetGame() ).CancelDelay( evt.delayID );
			StatusEffectHelper.RemoveStatusEffect( this, T"BaseStatusEffect.SecondChancePerkTimeDilation" );
			StatusEffectHelper.ApplyStatusEffect( this, T"BaseStatusEffect.DodgeOutOfSightCooldown" );
			StatusEffectHelper.ApplyStatusEffect( this, T"BaseStatusEffect.SecondChancePerkCooldown" );
		}
	}

	private const function CanAvoidCombat() : Bool
	{
		return !( IsInCombat() ) || IsCombatStartBufferActive( TDB.GetFloat( T"AIGeneralSettings.avoidCombatBuffer" ) );
	}

	public const function CanAvoidCombatWithDodge() : Bool
	{
		return ( RPGManager.HasStatFlag( this, gamedataStatType.CanPlayerDodgeOnDetection ) && CanAvoidCombat() ) && m_inCrouch;
	}

	public const function CanAvoidCombatWithGag() : Bool
	{
		return RPGManager.HasStatFlag( this, gamedataStatType.CanPlayerGagOnDetection ) && CanAvoidCombat();
	}

	private const function IsCombatStartBufferActive( buffer : Float ) : Bool
	{
		var bboard : IBlackboard;
		var combatTimeStamp : Float;
		var currentTime : Float;
		var combatDuration : Float;
		bboard = GetPlayerPerkDataBlackboard();
		combatTimeStamp = bboard.GetFloat( GetAllBlackboardDefs().PlayerPerkData.CombatStateTime );
		currentTime = EngineTime.ToFloat( GameInstance.GetSimTime( GetGame() ) );
		combatDuration = currentTime - combatTimeStamp;
		if( combatDuration <= buffer )
		{
			return true;
		}
		return false;
	}

	protected event OnActivateOpticalCamoToExitCombatEvent( evt : ExitCombatOnOpticalCamoActivatedEvent )
	{
		var npcPuppet : weak< ScriptedPuppet >;
		if( evt.npc )
		{
			npcPuppet = ( ( ScriptedPuppet )( evt.npc ) );
			if( npcPuppet )
			{
				npcPuppet.GetTargetTrackerComponent().ActivateThreat( this );
			}
			if( IsInCombat() )
			{
				StatusEffectHelper.ApplyStatusEffect( evt.npc, T"BaseStatusEffect.MemoryWipeExitCombat" );
				GameObject.PlaySoundEvent( this, 'ui_gmpl_stealth_avoid_combat' );
			}
		}
	}

	protected event OnEnablePlayerVisibilityEvent( evt : EnablePlayerVisibilityEvent )
	{
		if( IsInvisible() )
		{
			SetInvisible( false );
		}
	}

	private function EvaluateApplyingReplacerGameplayRestrictions()
	{
		var mainObj : weak< PlayerPuppet >;
		var controlledObj : weak< PlayerPuppet >;
		var controlledObjRecordID : TweakDBID;
		mainObj = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject() ) );
		controlledObj = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerControlledGameObject() ) );
		controlledObjRecordID = controlledObj.GetRecordID();
		switch( controlledObjRecordID )
		{
			case T"Character.johnny_replacer":
				StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockAllHubMenu" );
			StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockFastTravel" );
			GameInstance.GetGodModeSystem( mainObj.GetGame() ).AddGodMode( mainObj.GetEntityID(), gameGodModeType.Invulnerable, 'JohnnyReplacerSequence' );
			break;
			case T"Character.q000_vr_replacer":
				StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockAllHubMenu" );
			StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockFastTravel" );
			break;
			case T"Character.mq304_assassin_replacer_male":
				StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockAllHubMenu" );
			StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockFastTravel" );
			break;
			case T"Character.mq304_assassin_replacer_female":
				StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockAllHubMenu" );
			StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockFastTravel" );
			break;
			case T"Character.Player_Puppet_Base":
				StatusEffectHelper.RemoveStatusEffect( mainObj, T"GameplayRestriction.BlockAllHubMenu" );
			StatusEffectHelper.RemoveStatusEffect( mainObj, T"GameplayRestriction.BlockFastTravel" );
			GameInstance.GetGodModeSystem( mainObj.GetGame() ).RemoveGodMode( mainObj.GetEntityID(), gameGodModeType.Invulnerable, 'JohnnyReplacerSequence' );
			GameInstance.GetInventoryManager( mainObj.GetGame() ).RemoveEquipmentStateFlag( gameEEquipmentManagerState.InfiniteAmmo );
			break;
			case T"Character.kurt_replacer":
				StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockAllHubMenu" );
			StatusEffectHelper.ApplyStatusEffect( mainObj, T"GameplayRestriction.BlockFastTravel" );
			break;
			default:
				StatusEffectHelper.RemoveStatusEffect( mainObj, T"GameplayRestriction.BlockAllHubMenu" );
			StatusEffectHelper.RemoveStatusEffect( mainObj, T"GameplayRestriction.BlockFastTravel" );
			break;
		}
	}

	private function ResolveCachedGameplayRestrictions()
	{
		var i : Int32;
		var psmBB : IBlackboard;
		psmBB = GetPlayerStateMachineBlackboard();
		for( i = 0; i < m_cachedGameplayRestrictions.Size(); i += 1 )
		{
			AddGameplayRestriction( psmBB, m_cachedGameplayRestrictions[ i ] );
		}
		if( psmBB )
		{
			m_cachedGameplayRestrictions.Clear();
		}
	}

	private function AddGameplayRestriction( psmBB : IBlackboard, actionRestrictionRecordID : TweakDBID )
	{
		var actionRestrictions : array< TweakDBID >;
		if( psmBB )
		{
			actionRestrictions = ( ( array< TweakDBID > )( psmBB.GetVariant( GetAllBlackboardDefs().PlayerStateMachine.ActionRestriction ) ) );
			actionRestrictions.PushBack( actionRestrictionRecordID );
			psmBB.SetVariant( GetAllBlackboardDefs().PlayerStateMachine.ActionRestriction, actionRestrictions );
		}
		else
		{
			CacheGameplayRestriction( actionRestrictionRecordID );
		}
	}

	private function RemoveGameplayRestriction( psmBB : IBlackboard, actionRestrictionRecordID : TweakDBID )
	{
		var actionRestrictions : array< TweakDBID >;
		if( psmBB )
		{
			actionRestrictions = ( ( array< TweakDBID > )( psmBB.GetVariant( GetAllBlackboardDefs().PlayerStateMachine.ActionRestriction ) ) );
			actionRestrictions.Remove( actionRestrictionRecordID );
			psmBB.SetVariant( GetAllBlackboardDefs().PlayerStateMachine.ActionRestriction, actionRestrictions );
		}
	}

	private function CacheGameplayRestriction( actionRestrictionRecordID : TweakDBID )
	{
		if( !( m_cachedGameplayRestrictions.Contains( actionRestrictionRecordID ) ) )
		{
			m_cachedGameplayRestrictions.PushBack( actionRestrictionRecordID );
		}
	}

	private export function PlayerAttachedCallback( playerPuppet : GameObject )
	{
		var blackboard : IBlackboard;
		var deviceTakeControlBlackboard : IBlackboard;
		var pmsBlackboard : IBlackboard;
		var allBlackboardDef : AllBlackboardDefinitions;
		var attachmentSlotCallback : PlayerPuppetAttachmentSlotsCallback;
		var playerDevelopmentData : PlayerDevelopmentData;
		if( playerPuppet == this )
		{
			m_callbackHandles.Clear();
			pmsBlackboard = GetPlayerStateMachineBlackboard();
			allBlackboardDef = GetAllBlackboardDefs();
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerFloat( allBlackboardDef.PlayerStateMachine.ZoomLevel, this, 'OnZoomLevelChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.Locomotion, this, 'OnLocomotionStateChanged', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.Combat, this, 'OnCombatStateChanged' ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerVariant( allBlackboardDef.PlayerStateMachine.SecurityZoneData, this, 'OnZoneChange' ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.LeftHandCyberware, this, 'OnLeftHandCyberwareStateChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.Vision, this, 'OnVisionStateChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.MeleeWeapon, this, 'OnMeleeWeaponStateChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.UpperBody, this, 'OnUpperBodyStateChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.Vehicle, this, 'OnVehicleStateChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.Weapon, this, 'OnWeaponStateChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerBool( allBlackboardDef.PlayerStateMachine.IsDriverCombatInTPP, this, 'OnDriverCombatCameraChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.DriverCombatWeaponType, this, 'OnDriverCombatWeaponTypeChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerBool( allBlackboardDef.PlayerStateMachine.IsInBodySlamState, this, 'OnBodySlamStateChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.CombatGadget, this, 'OnCombatGadgetStateChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.SceneTier, this, 'OnSceneTierChange', true ) );
			m_callbackHandles.PushBack( pmsBlackboard.RegisterListenerInt( allBlackboardDef.PlayerStateMachine.Takedown, this, 'OnPlayerTakedownStateChange', true ) );
			deviceTakeControlBlackboard = GameInstance.GetBlackboardSystem( GetGame() ).Get( allBlackboardDef.DeviceTakeControl );
			if( deviceTakeControlBlackboard )
			{
				m_callbackHandles.PushBack( deviceTakeControlBlackboard.RegisterListenerEntityID( allBlackboardDef.DeviceTakeControl.ActiveDevice, this, 'OnControllingDeviceChange', true ) );
			}
			blackboard = GameInstance.GetBlackboardSystem( GetGame() ).Get( allBlackboardDef.UI_Stealth );
			if( blackboard )
			{
				m_callbackHandles.PushBack( blackboard.RegisterListenerUint( allBlackboardDef.UI_Stealth.numberOfCombatants, this, 'OnNumberOfCombatantsChanged' ) );
			}
			m_allStatsListener = new PlayerPuppetAllStatListener;
			m_allStatsListener.player = this;
			GameInstance.GetStatsSystem( GetGame() ).RegisterListener( GetEntityID(), m_allStatsListener );
			m_hasKiroshiOpticsFragment = RPGManager.HasStatFlag( this, gamedataStatType.HasKiroshiOpticsFragment );
			attachmentSlotCallback = new PlayerPuppetAttachmentSlotsCallback;
			attachmentSlotCallback.m_player = this;
			attachmentSlotCallback.slotID = T"AttachmentSlots.WeaponRight";
			m_rightHandAttachmentSlotListener = GameInstance.GetTransactionSystem( GetGame() ).RegisterAttachmentSlotListener( this, attachmentSlotCallback );
			UpdateWeaponRightEquippedItemInfo();
			if( IsJohnnyReplacer() )
			{
				GameplaySettingsSystem.SetWasEverJohnny( this, true );
			}
			if( IsReplacer() )
			{
				playerDevelopmentData = PlayerDevelopmentSystem.GetData( this );
				playerDevelopmentData.OnRestored( GetGame() );
			}
			EvaluateApplyingReplacerGameplayRestrictions();
			RestoreMinigamePrograms();
			ResolveCachedGameplayRestrictions();
			RegisterInterestingFactsListeners();
			m_visionModeController.RegisterOwner( this );
			m_combatController.RegisterOwner( this );
			ChacheQuickHackListCleanup( playerPuppet );
			UpdateAimAssist();
		}
	}

	private export function PlayerDetachedCallback( playerPuppet : GameObject )
	{
		if( playerPuppet == this )
		{
			UnregisterInterestingFactsListeners();
			m_visionModeController.UnregisterOwner();
			m_callbackHandles.Clear();
			GameInstance.GetStatsSystem( GetGame() ).UnregisterListener( GetEntityID(), m_allStatsListener );
			GameInstance.GetTransactionSystem( GetGame() ).UnregisterAttachmentSlotListener( this, m_rightHandAttachmentSlotListener );
		}
	}

	protected event OnGameAttached()
	{
		var playerAttach : PlayerAttachRequest;
		var statPoolsSystem : StatPoolsSystem;
		var entityID : StatsObjectID;
		var evt : GameLoadedFactReset;
		var questSystem : QuestsSystem;
		playerAttach = new PlayerAttachRequest;
		playerAttach.owner = this;
		GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).QueueRequest( playerAttach );
		super.OnGameAttached();
		if( IsControlledByLocalPeer() || IsHost() )
		{
			RegisterInputListener( this, 'IconicCyberware' );
			RegisterInputListener( this, 'CyberwareMaskInVehicle' );
			RegisterInputListener( this, 'CallVehicle' );
			RegisterInputListener( this, 'ToggleWalk' );
			RegisterInputListener( this, 'SceneFastForward' );
			RegisterInputListener( this, 'PocketRadio' );
			m_Debug_DamageInputRec = new DEBUG_DamageInputReceiver;
			m_Debug_DamageInputRec.m_player = this;
			RegisterInputListener( m_Debug_DamageInputRec, 'Debug_KillAll' );
			RegisterInputListener( m_Debug_DamageInputRec, 'Debug_Kill' );
		}
		else if( IsClient() )
		{
			RegisterRemoteMappin();
			RefreshCPOVisionAppearance();
			RegisterCPOMissionDataCallback();
		}
		m_CPOMissionDataState = new CPOMissionDataState;
		isTalkingOnPhone = false;
		m_coverVisibilityPerkBlocked = false;
		m_behindCover = false;
		m_inCombat = false;
		SetIsBeingRevealed( false );
		m_hasBeenDetected = false;
		m_inCrouch = false;
		RegisterToFacts();
		EnableUIBlackboardListener( true );
		InitializeTweakDBRecords();
		DefineModifierGroups();
		RegisterStatListeners( this );
		UpdateVisibilityModifier();
		EnableInteraction( 'Revive', false );
		m_incapacitated = false;
		UpdatePlayerSettings();
		CalculateEncumbrance();
		AnimationControllerComponent.ApplyFeature( this, 'CameraGameplay', new AnimFeature_CameraGameplay );
		AnimationControllerComponent.ApplyFeature( this, 'CameraBodyOffset', new AnimFeature_CameraBodyOffset );
		m_playerAttachedCallbackID = GameInstance.GetPlayerSystem( GetGameInstance() ).RegisterPlayerPuppetAttachedCallback( this, 'PlayerAttachedCallback' );
		m_playerDetachedCallbackID = GameInstance.GetPlayerSystem( GetGameInstance() ).RegisterPlayerPuppetDetachedCallback( this, 'PlayerDetachedCallback' );
		UpdateSecondaryVisibilityOffset( false );
		EnableCombatVisibilityDistances( false );
		SetSenseObjectType( gamedataSenseObjectType.Player );
		GracePeriodAfterSpawn();
		StatusEffectHelper.RemoveStatusEffect( this, T"GameplayRestriction.FastForward" );
		StatusEffectHelper.RemoveStatusEffect( this, T"GameplayRestriction.FastForwardCrouchLock" );
		StatusEffectHelper.RemoveStatusEffect( this, T"BaseStatusEffect.DontShootAtMe" );
		entityID = GetEntityID();
		statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
		if( StatusEffectSystem.ObjectHasStatusEffect( this, T"AIQuickHackStatusEffect.BeingHacked" ) )
		{
			StatusEffectHelper.RemoveStatusEffect( this, T"AIQuickHackStatusEffect.BeingHacked" );
			if( statPoolsSystem.GetStatPoolValue( entityID, gamedataStatPoolType.QuickHackUpload ) > 0.0 )
			{
				statPoolsSystem.RequestRemovingStatPool( entityID, gamedataStatPoolType.QuickHackUpload );
			}
		}
		UnlockAccessPointPrograms();
		statPoolsSystem.RequestMarkingStatPoolActive( GetEntityID(), gamedataStatPoolType.Stamina );
		statPoolsSystem.RequestMarkingStatPoolActive( GetEntityID(), gamedataStatPoolType.HealingItemsCharges );
		statPoolsSystem.RequestMarkingStatPoolActive( GetEntityID(), gamedataStatPoolType.GrenadesCharges );
		statPoolsSystem.RequestMarkingStatPoolActive( GetEntityID(), gamedataStatPoolType.OpticalCamoCharges );
		statPoolsSystem.RequestMarkingStatPoolActive( GetEntityID(), gamedataStatPoolType.ProjectileLauncherCharges );
		statPoolsSystem.RequestMarkingStatPoolActive( GetEntityID(), gamedataStatPoolType.BerserkCharge );
		statPoolsSystem.RequestMarkingStatPoolActive( GetEntityID(), gamedataStatPoolType.SandevistanCharge );
		statPoolsSystem.RequestMarkingStatPoolActive( GetEntityID(), gamedataStatPoolType.CyberdeckOverclock );
		m_handlingModifiers = PlayerWeaponHandlingModifiers.Create( this );
		m_handlingModifiers.OnAttach();
		m_pocketRadio = new PocketRadio;
		m_pocketRadio.OnPlayerAttach( this );
		evt = new GameLoadedFactReset;
		GameInstance.GetDelaySystem( GetGame() ).DelayEvent( this, evt, 1.0 );
		questSystem = GameInstance.GetQuestsSystem( GetGame() );
		questSystem.SetFact( 'game_was_loaded', 1 );
	}

	protected event OnGameLoadedFactReset( evt : GameLoadedFactReset )
	{
		var questSystem : QuestsSystem;
		questSystem = GameInstance.GetQuestsSystem( GetGame() );
		questSystem.SetFact( 'game_was_loaded', 0 );
	}

	protected event OnDetach()
	{
		var playerDetach : PlayerDetachRequest;
		playerDetach = new PlayerDetachRequest;
		playerDetach.owner = this;
		playerDetach.ownerID = GetEntityID();
		GameInstance.GetScriptableSystemsContainer( GetGameInstance() ).QueueRequest( playerDetach );
		UnregisterStatListeners( this );
		EnableUIBlackboardListener( false );
		if( IsClient() )
		{
			UnregisterRemoteMappin();
			UnregisterCPOMissionDataCallback();
		}
		CPOMissionDataOnPlayerDetach();
		SetEntityNoticedPlayerBBValue( false );
		if( m_playerAttachedCallbackID )
		{
			GameInstance.GetPlayerSystem( GetGame() ).UnregisterPlayerPuppetAttachedCallback( m_playerAttachedCallbackID );
		}
		if( m_playerDetachedCallbackID )
		{
			GameInstance.GetPlayerSystem( GetGame() ).UnregisterPlayerPuppetDetachedCallback( m_playerDetachedCallbackID );
		}
		m_pocketRadio.OnPlayerDetach( this );
	}

	protected const override function ShouldRegisterToHUD() : Bool
	{
		return false;
	}

	private constexpr function GetCWMaskTweakDBID() : TweakDBID
	{
		return T"Items.MaskCW";
	}

	public function GetCWMaskID() : ItemID
	{
		return EquipmentSystem.GetData( this ).FindItemInEquipArea( ItemID.CreateQuery( GetCWMaskTweakDBID() ), gamedataEquipmentArea.EyesCW );
	}

	public function HasCWMask() : Bool
	{
		return ItemID.IsValid( GetCWMaskID() );
	}

	private function ExecuteCWMask()
	{
		var maskId : ItemID;
		var dpadAction : DPADActionPerformed;
		maskId = GetCWMaskID();
		if( !( ItemID.IsValid( maskId ) ) )
		{
			return;
		}
		dpadAction = new DPADActionPerformed;
		dpadAction.action = EHotkey.RB;
		dpadAction.state = EUIActionState.COMPLETED;
		dpadAction.successful = true;
		GameInstance.GetUISystem( GetGame() ).QueueEvent( dpadAction );
		ItemActionsHelper.UseItem( this, maskId );
	}

	protected event OnAction( action : ListenerAction, consumer : ListenerActionConsumer )
	{
		if( ListenerAction.GetName( action ) == 'ToggleWalk' && ListenerAction.IsButtonJustReleased( action ) )
		{
			ProcessToggleWalkInput();
			return true;
		}
		if( ListenerAction.GetName( action ) == 'IconicCyberware' )
		{
			if( HasCWMask() && m_vehicleState != gamePSMVehicle.Default )
			{
				if( ListenerAction.GetType( action ) == gameinputActionType.BUTTON_RELEASED && m_CWMaskInVehicleInputHeld )
				{
					m_CWMaskInVehicleInputHeld = false;
					ActivateIconicCyberware();
				}
				else if( ListenerAction.GetType( action ) == gameinputActionType.BUTTON_HOLD_COMPLETE )
				{
					m_CWMaskInVehicleInputHeld = false;
					ExecuteCWMask();
				}
				else if( ListenerAction.GetType( action ) == gameinputActionType.BUTTON_PRESSED )
				{
					m_CWMaskInVehicleInputHeld = true;
				}
			}
			else
			{
				if( ListenerAction.GetType( action ) == gameinputActionType.BUTTON_PRESSED )
				{
					ActivateIconicCyberware();
				}
			}
		}
		else if( ListenerAction.GetName( action ) == 'CallVehicle' )
		{
			if( !( GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_PlayerStats ).GetBool( GetAllBlackboardDefs().UI_PlayerStats.isReplacer ) ) && ListenerAction.GetType( action ) == gameinputActionType.BUTTON_RELEASED )
			{
				ProcessCallVehicleAction( ListenerAction.GetType( action ) );
			}
		}
		else if( ListenerAction.GetName( action ) == 'SceneFastForward' )
		{
			if( m_customFastForwardPossible )
			{
				if( ListenerAction.GetType( action ) == gameinputActionType.BUTTON_PRESSED )
				{
					GameInstance.GetTimeSystem( GetGame() ).SetTimeDilation( 'customFFTimeDilation', 10.0 );
					GameObjectEffectHelper.StartEffectEvent( this, 'transition_glitch_loop', false );
					GameInstance.GetAudioSystem( GetGame() ).Play( 'motion_light_fast_2d' );
				}
				else if( ListenerAction.GetType( action ) == gameinputActionType.BUTTON_RELEASED && !( ScenesFastForwardTransition.IsFastForwardByLine( this ) ) )
				{
					GameInstance.GetTimeSystem( GetGame() ).UnsetTimeDilation( 'customFFTimeDilation' );
					GameObjectEffectHelper.StopEffectEvent( this, 'transition_glitch_loop' );
				}
			}
		}
		else if( ListenerAction.GetName( action ) == 'PocketRadio' )
		{
			m_pocketRadio.HandleInputAction( action );
		}
	}

	private function KeybaordAndMouseControlsActive() : Bool
	{
		return this.PlayerLastUsedKBM();
	}

	public override function HasPrimaryOrSecondaryEquipment() : Bool
	{
		return true;
	}

	private final function ProcessToggleWalkInput()
	{
		var psmEvent : PSMPostponedParameterBool;
		psmEvent = new PSMPostponedParameterBool;
		psmEvent.id = 'ToggleWalkInputRegistered';
		psmEvent.aspect = gamestateMachineParameterAspect.Permanent;
		psmEvent.value = true;
		QueueEvent( psmEvent );
	}

	public function IconicCyberwareActivated()
	{
		if( PlayerDevelopmentSystem.GetData( this ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Tech_Master_Perk_2 ) )
		{
			if( !( StatusEffectSystem.ObjectHasStatusEffect( this, T"BaseStatusEffect.Tech_Master_Perk_2_Buff" ) ) )
			{
				StatusEffectHelper.ApplyStatusEffect( this, T"BaseStatusEffect.Tech_Master_Perk_2_Buff" );
			}
		}
	}

	private final function ActivateIconicCyberware()
	{
		var activeItem : ItemID;
		var audioEvent : SoundPlayEvent;
		var dpadAction : DPADActionPerformed;
		var activated : Bool;
		var isInFocusMode : Bool;
		activated = false;
		activeItem = EquipmentSystem.GetData( this ).GetActiveItem( gamedataEquipmentArea.SystemReplacementCW );
		if( !( ItemID.IsValid( activeItem ) ) )
		{
			return;
		}
		isInFocusMode = GetPlayerStateMachineBlackboard().GetInt( GetAllBlackboardDefs().PlayerStateMachine.Vision ) == ( ( Int32 )( gamePSMVision.Focus ) );
		if( isInFocusMode )
		{
			QuickHackableHelper.TryToCycleOverclockedState( this );
			return;
		}
		dpadAction = new DPADActionPerformed;
		dpadAction.action = EHotkey.LBRB;
		dpadAction.state = EUIActionState.COMPLETED;
		dpadAction.successful = true;
		if( GameInstance.GetStatsSystem( GetGame() ).GetStatBoolValue( GetEntityID(), gamedataStatType.HasBerserk ) )
		{
			activated = ItemActionsHelper.UseItem( this, activeItem );
			if( !( activated ) )
			{
				audioEvent = new SoundPlayEvent;
				audioEvent.soundName = 'ui_grenade_empty';
				QueueEvent( audioEvent );
			}
		}
		else if( GameInstance.GetStatsSystem( GetGame() ).GetStatBoolValue( GetEntityID(), gamedataStatType.HasSandevistan ) )
		{
			if( TimeDilationHelper.CanUseTimeDilation( this ) )
			{
				ItemActionsHelper.UseItem( this, activeItem );
				activated = false;
			}
		}
		else if( TweakDBInterface.GetCName( ItemID.GetTDBID( activeItem ) + T".cyberwareType", '' ) == 'Sandevistan' && IsPhoneCallActive() )
		{
			ShowSandevistanBlockedNotification();
			return;
		}
		else if( ( GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.HasCyberdeck ) > 0.0 ) && GameInstance.GetStatsSystem( GetGame() ).GetStatBoolValue( GetEntityID(), gamedataStatType.CanUseOverclock ) )
		{
			activated = QuickHackableHelper.TryToCycleOverclockedState( this );
		}
		else
		{
			dpadAction.successful = false;
		}
		if( activated )
		{
			IconicCyberwareActivated();
		}
		GameInstance.GetUISystem( GetGame() ).QueueEvent( dpadAction );
	}

	private function IsPhoneCallActive() : Bool
	{
		var blackboardSystem : BlackboardSystem;
		var lastPhoneCallInformation : PhoneCallInformation;
		var infoVariant : Variant;
		blackboardSystem = GameInstance.GetBlackboardSystem( GetGameInstance() );
		infoVariant = blackboardSystem.Get( GetAllBlackboardDefs().UI_ComDevice ).GetVariant( GetAllBlackboardDefs().UI_ComDevice.PhoneCallInformation );
		if( infoVariant.IsValid() )
		{
			lastPhoneCallInformation = ( ( PhoneCallInformation )infoVariant );
			return lastPhoneCallInformation.callPhase == questPhoneCallPhase.StartCall && ( lastPhoneCallInformation.callMode == questPhoneCallMode.Video || lastPhoneCallInformation.callMode == questPhoneCallMode.Audio );
		}
		return false;
	}

	private function ShowSandevistanBlockedNotification()
	{
		var notificationEvent : UIInGameNotificationEvent;
		notificationEvent = new UIInGameNotificationEvent;
		notificationEvent.m_notificationType = UIInGameNotificationType.SandevistanInCallRestriction;
		GameInstance.GetUISystem( GetGame() ).QueueEvent( notificationEvent );
	}

	private final function ProcessCallVehicleAction( type : gameinputActionType )
	{
		var dpadAction : DPADActionPerformed;
		if( ( !( VehicleComponent.IsMountedToVehicle( GetGame(), this ) ) && CheckRadialContextRequest() ) && !( VehicleSystem.IsSummoningVehiclesRestricted( GetGame() ) ) )
		{
			SendSummonVehicleQuickSlotsManagerRequest();
		}
		dpadAction = new DPADActionPerformed;
		dpadAction.action = EHotkey.DPAD_RIGHT;
		dpadAction.successful = false;
		GameInstance.GetUISystem( GetGame() ).QueueEvent( dpadAction );
	}

	protected event OnMountingEvent( evt : MountingEvent )
	{
		var bikeObject : BikeObject;
		var playerStateMachineBlackboard : weak< IBlackboard >;
		var allowsCombat : Bool;
		var mountedToVehicle : Bool;
		var missShotOverrideEvent : OverrideMissShotOffset;
		allowsCombat = false;
		mountedToVehicle = false;
		m_mountedVehicle = NULL;
		playerStateMachineBlackboard = GameInstance.GetBlackboardSystem( GetGame() ).GetLocalInstanced( GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		m_mountedVehicle = ( ( VehicleObject )( GameInstance.FindEntityByID( GetGame(), evt.request.lowLevelMountingInfo.parentId ) ) );
		if( m_mountedVehicle )
		{
			mountedToVehicle = true;
			allowsCombat = VehicleComponent.GetVehicleAllowsCombat( GetGame(), m_mountedVehicle );
			bikeObject = ( ( BikeObject )( m_mountedVehicle ) );
			if( !( bikeObject ) )
			{
				m_enviroDamageRcvComponent.Toggle( false );
			}
			if( m_mountedVehicle.GetRecord().IsArmoredVehicle() )
			{
				playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.InArmoredVehicle, true );
			}
		}
		playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedToCombatVehicle, allowsCombat );
		playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedToVehicle, mountedToVehicle );
		playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedPreventFirstEquip, true );
		if( evt.request.lowLevelMountingInfo.slotId.id == VehicleComponent.GetDriverSlotName() )
		{
			missShotOverrideEvent = new OverrideMissShotOffset;
			missShotOverrideEvent.overrideRecord = "vehicleMissShotOffset";
			QueueEvent( missShotOverrideEvent );
			if( mountedToVehicle )
			{
				m_pocketRadio.HandleVehicleMounted( m_mountedVehicle );
			}
		}
	}

	protected event OnUnmountingEvent( evt : UnmountingEvent )
	{
		var playerStateMachineBlackboard : weak< IBlackboard >;
		var missShotOverrideEvent : OverrideMissShotOffset;
		var mountChild : GameObject;
		mountChild = ( ( GameObject )( GameInstance.FindEntityByID( GetGame(), evt.request.lowLevelMountingInfo.childId ) ) );
		if( mountChild && mountChild.IsPlayer() )
		{
			playerStateMachineBlackboard = GetPlayerStateMachineBlackboard();
			playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedToCombatVehicle, false );
			playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedToVehicle, false );
			playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedToVehicleInDriverSeat, false );
			playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.InArmoredVehicle, false );
			playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.MountedPreventFirstEquip, false );
			if( evt.request.lowLevelMountingInfo.slotId.id == VehicleComponent.GetDriverSlotName() )
			{
				m_pocketRadio.HandleVehicleUnmounted( m_mountedVehicle );
			}
			m_mountedVehicle = NULL;
		}
		m_enviroDamageRcvComponent.Toggle( true );
		if( evt.request.lowLevelMountingInfo.slotId.id == VehicleComponent.GetDriverSlotName() )
		{
			missShotOverrideEvent = new OverrideMissShotOffset;
			QueueEvent( missShotOverrideEvent );
		}
	}

	public const function GetMountedVehicle() : VehicleObject
	{
		return m_mountedVehicle;
	}

	private const function IsCallingVehicleRestricted() : Bool
	{
		return PlayerGameplayRestrictions.IsHotkeyRestricted( GetGame(), EHotkey.DPAD_RIGHT );
	}

	public function IsInPoliceVehicle() : Bool
	{
		var vehicleObj : weak< VehicleObject >;
		var vehicleRecord : Vehicle_Record;
		VehicleComponent.GetVehicle( GetGame(), this, vehicleObj );
		if( !( vehicleObj ) )
		{
			return false;
		}
		if( !( VehicleComponent.GetVehicleRecord( vehicleObj, vehicleRecord ) ) )
		{
			return false;
		}
		return vehicleRecord.Affiliation().Type() == gamedataAffiliation.NCPD;
	}

	private final function GetUnlockedVehiclesSize() : Int32
	{
		var unlockedVehicles : array< PlayerVehicle >;
		GameInstance.GetVehicleSystem( GetGame() ).GetPlayerUnlockedVehicles( unlockedVehicles );
		return unlockedVehicles.Size();
	}

	private final function SendSummonVehicleQuickSlotsManagerRequest()
	{
		var evt : CallAction;
		evt = new CallAction;
		evt.calledAction = QuickSlotActionType.SummonVehicle;
		QueueEvent( evt );
	}

	private final function CheckVehicleSystemGarageState() : Bool
	{
		var blackboard : IBlackboard;
		var garageState : Uint32;
		blackboard = GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().VehicleSummonData );
		garageState = ( ( Uint32 )( blackboard.GetUint( GetAllBlackboardDefs().VehicleSummonData.GarageState ) ) );
		return ( ( vehicleGarageState )( garageState ) ) == vehicleGarageState.SummonAvailable;
	}

	private final function CheckRadialContextRequest() : Bool
	{
		return !( GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_QuickSlotsData ).GetBool( GetAllBlackboardDefs().UI_QuickSlotsData.UIRadialContextRequest ) );
	}

	private function OnActionMultiplayer( action : ListenerAction, consumer : ListenerActionConsumer )
	{
		var pingSystem : PingSystem;
		var isVisionModeActive : Bool;
		if( ListenerAction.GetName( action ) == 'MP_TriggerPing' )
		{
			isVisionModeActive = GetPlayerStateMachineBlackboard().GetInt( GetAllBlackboardDefs().PlayerStateMachine.Vision ) == ( ( Int32 )( gamePSMVision.Focus ) );
			if( !( isVisionModeActive ) )
			{
				if( ListenerAction.GetType( action ) == gameinputActionType.BUTTON_RELEASED )
				{
					pingSystem = GameInstance.GetPingSystem( GetGame() );
					if( pingSystem )
					{
						pingSystem.TriggerPing( this );
					}
				}
			}
		}
	}

	private function GetCPOQuickSlotID( action : ListenerAction ) : Int32
	{
		if( GameInstance.GetRuntimeInfo( GetGame() ).IsMultiplayer() || GameInstance.GetPlayerSystem( GetGame() ).IsCPOControlSchemeForced() )
		{
			if( ListenerAction.GetName( action ) == 'QuickSlot1' && ListenerAction.IsButtonJustReleased( action ) )
			{
				return 0;
			}
			if( ListenerAction.GetName( action ) == 'QuickSlot2' && ListenerAction.IsButtonJustReleased( action ) )
			{
				return 1;
			}
			if( ListenerAction.GetName( action ) == 'QuickSlot3' && ListenerAction.IsButtonJustReleased( action ) )
			{
				return 2;
			}
		}
		return -1;
	}

	private function UpdatePlayerSettings()
	{
		var meleeCameraShakeWeight : Float;
		var disableHeadBobbing : Bool;
		meleeCameraShakeWeight = TDB.GetFloat( T"player.camera.meleeCameraShakeWeight", 1.0 );
		disableHeadBobbing = TDB.GetBool( T"player.camera.disableHeadBobbing", false );
		AnimationControllerComponent.SetInputFloat( this, 'melee_camera_shake_weight', meleeCameraShakeWeight );
		AnimationControllerComponent.SetInputBool( this, 'disable_camera_bobbing', disableHeadBobbing );
	}

	public const function GetQuickSlotsManager() : QuickSlotsManager
	{
		return m_quickSlotsManager;
	}

	public const function GetInspectionComponent() : InspectionComponent
	{
		return m_inspectionComponent;
	}

	public const function GetFPPCameraComponent() : FPPCameraComponent
	{
		return m_fppCameraComponent;
	}

	public function GetBufferModifier() : Int32
	{
		return m_nextBufferModifier;
	}

	public function SetBufferModifier( i : Int32 )
	{
		m_nextBufferModifier = i;
	}

	public static function GetCriticalHealthThreshold() : Float
	{
		return TDB.GetFloat( T"player.hitVFX.critHealthThreshold" );
	}

	public static function GetLowHealthThreshold() : Float
	{
		return TDB.GetFloat( T"player.hitVFX.lowHealthThreshold" );
	}

	public function GetPlayerWeaponHandler() : PlayerWeaponHandlingModifiers
	{
		return m_handlingModifiers;
	}

	public function GetPocketRadio() : PocketRadio
	{
		return m_pocketRadio;
	}

	public static function IsTargetFriendlyNPC( player : PlayerPuppet, target : Entity ) : Bool
	{
		var targetAsPuppet : ScriptedPuppet;
		var targetAsWeakspot : WeakspotObject;
		var attitudeTowardsPlayer : EAIAttitude;
		targetAsWeakspot = ( ( WeakspotObject )( target ) );
		if( targetAsWeakspot )
		{
			target = targetAsWeakspot.GetOwner();
		}
		targetAsPuppet = ( ( ScriptedPuppet )( target ) );
		if( targetAsPuppet.GetRecordID() == T"Character.Silverhand" )
		{
			return false;
		}
		if( ( ( player && targetAsPuppet ) && ScriptedPuppet.IsAlive( targetAsPuppet ) ) && !( StatusEffectSystem.ObjectHasStatusEffect( targetAsPuppet, T"BaseStatusEffect.IgnoreWeaponSafe" ) ) )
		{
			attitudeTowardsPlayer = GameObject.GetAttitudeTowards( player, targetAsPuppet );
			if( attitudeTowardsPlayer == EAIAttitude.AIA_Friendly )
			{
				return true;
			}
		}
		return false;
	}

	public static function IsTargetChildNPC( player : PlayerPuppet, target : Entity ) : Bool
	{
		var targetAsPuppet : ScriptedPuppet;
		targetAsPuppet = ( ( ScriptedPuppet )( target ) );
		if( ( player && targetAsPuppet ) && ScriptedPuppet.IsAlive( targetAsPuppet ) )
		{
			if( targetAsPuppet.IsCharacterChildren() )
			{
				return true;
			}
		}
		return false;
	}

	public const function GetPlayerStateMachineBlackboard() : IBlackboard
	{
		var psmBlackboard : IBlackboard;
		psmBlackboard = GameInstance.GetBlackboardSystem( GetGame() ).GetLocalInstanced( this.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		return psmBlackboard;
	}

	public const function GetPlayerPerkDataBlackboard() : IBlackboard
	{
		return GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().PlayerPerkData );
	}

	public const function GetHackingDataBlackboard() : IBlackboard
	{
		return GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().HackingData );
	}

	public static function GetCurrentLocomotionState( player : weak< PlayerPuppet > ) : gamePSMLocomotionStates
	{
		var blackboard : IBlackboard;
		blackboard = player.GetPlayerStateMachineBlackboard();
		return ( ( gamePSMLocomotionStates )( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Locomotion ) ) );
	}

	public static function GetCurrentHighLevelState( player : weak< PlayerPuppet > ) : gamePSMHighLevel
	{
		var blackboard : IBlackboard;
		blackboard = player.GetPlayerStateMachineBlackboard();
		return ( ( gamePSMHighLevel )( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.HighLevel ) ) );
	}

	public static function GetCurrentCombatState( player : weak< PlayerPuppet > ) : gamePSMCombat
	{
		var blackboard : IBlackboard;
		blackboard = player.GetPlayerStateMachineBlackboard();
		return ( ( gamePSMCombat )( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Combat ) ) );
	}

	public static function GetCurrentVehicleState( player : weak< PlayerPuppet > ) : gamePSMVehicle
	{
		return player.m_vehicleState;
	}

	public static function GetQuickMeleeCooldown() : Float
	{
		return TDB.GetFloat( T"player.quickMelee.quickMeleeCooldown", 5.0 );
	}

	private function GetDamageThresholdParams()
	{
		m_highDamageThreshold = TDB.GetFloat( T"player.damageThresholds.highDamageThreshold", 0.40000001 );
		m_medDamageThreshold = TDB.GetFloat( T"player.damageThresholds.medDamageThreshold", 0.1 );
		m_lowDamageThreshold = TDB.GetFloat( T"player.damageThresholds.lowDamageThreshold", 0.1 );
		m_meleeHighDamageThreshold = TDB.GetFloat( T"player.damageThresholds.meleeHighDamageThreshold", 0.1 );
		m_meleeMedDamageThreshold = TDB.GetFloat( T"player.damageThresholds.meleeMedDamageThreshold", 0.2 );
		m_meleeLowDamageThreshold = TDB.GetFloat( T"player.damageThresholds.meleeLowDamageThreshold", 0.30000001 );
		m_explosionHighDamageThreshold = TDB.GetFloat( T"player.damageThresholds.explosionHighDamageThreshold", 0.0 );
		m_explosionMedDamageThreshold = TDB.GetFloat( T"player.damageThresholds.explosionMedDamageThreshold", 0.0 );
		m_explosionLowDamageThreshold = TDB.GetFloat( T"player.damageThresholds.explosionLowDamageThreshold", 0.0 );
	}

	private function EnableUIBlackboardListener( enable : Bool )
	{
		var blackboardSystem : BlackboardSystem;
		var uiBlackboard : IBlackboard;
		var quickSlotsBlackboard : IBlackboard;
		var phoneBlackboard : weak< IBlackboard >;
		var targetingBlackBoard : weak< IBlackboard >;
		blackboardSystem = GameInstance.GetBlackboardSystem( GetGameInstance() );
		uiBlackboard = blackboardSystem.Get( GetAllBlackboardDefs().UIGameData );
		quickSlotsBlackboard = blackboardSystem.Get( GetAllBlackboardDefs().UI_QuickSlotsData );
		phoneBlackboard = blackboardSystem.Get( GetAllBlackboardDefs().UI_ComDevice );
		targetingBlackBoard = blackboardSystem.Get( GetAllBlackboardDefs().UI_TargetingInfo );
		if( enable )
		{
			m_itemLogBlackboard = blackboardSystem.Get( GetAllBlackboardDefs().UI_ItemLog );
			m_interactionDataListener = uiBlackboard.RegisterListenerVariant( GetAllBlackboardDefs().UIGameData.InteractionData, this, 'OnInteractionStateChange' );
			m_popupIsModalListener = uiBlackboard.RegisterListenerBool( GetAllBlackboardDefs().UIGameData.Popup_IsModal, this, 'OnUIContextChange' );
			m_uiVendorContextListener = uiBlackboard.RegisterListenerBool( GetAllBlackboardDefs().UIGameData.UIVendorContextRequest, this, 'OnUIVendorContextChange' );
			m_uiRadialContextistener = quickSlotsBlackboard.RegisterListenerBool( GetAllBlackboardDefs().UI_QuickSlotsData.UIRadialContextRequest, this, 'OnUIRadialContextChange' );
			m_contactsActiveListener = phoneBlackboard.RegisterListenerBool( GetAllBlackboardDefs().UI_ComDevice.ContactsActive, this, 'OnUIContactListContextChanged' );
			m_smsMessengerActiveListener = phoneBlackboard.RegisterListenerBool( GetAllBlackboardDefs().UI_ComDevice.SmsMessengerActive, this, 'OnUISmsMessengerContextChanged' );
			m_currentVisibleTargetListener = targetingBlackBoard.RegisterListenerEntityID( GetAllBlackboardDefs().UI_TargetingInfo.CurrentVisibleTarget, this, 'OnCurrentVisibleTargetChanged' );
		}
		else
		{
			uiBlackboard.UnregisterListenerVariant( GetAllBlackboardDefs().UIGameData.InteractionData, m_interactionDataListener );
			uiBlackboard.UnregisterListenerBool( GetAllBlackboardDefs().UIGameData.Popup_IsModal, m_popupIsModalListener );
			uiBlackboard.UnregisterListenerBool( GetAllBlackboardDefs().UIGameData.UIVendorContextRequest, m_uiVendorContextListener );
			quickSlotsBlackboard.UnregisterListenerBool( GetAllBlackboardDefs().UI_QuickSlotsData.UIRadialContextRequest, m_uiRadialContextistener );
			phoneBlackboard.UnregisterListenerBool( GetAllBlackboardDefs().UI_ComDevice.ContactsActive, m_contactsActiveListener );
			phoneBlackboard.UnregisterListenerBool( GetAllBlackboardDefs().UI_ComDevice.SmsMessengerActive, m_smsMessengerActiveListener );
			targetingBlackBoard.UnregisterListenerEntityID( GetAllBlackboardDefs().UI_TargetingInfo.CurrentVisibleTarget, m_currentVisibleTargetListener );
			m_itemLogBlackboard = NULL;
			m_interactionDataListener = NULL;
			m_popupIsModalListener = NULL;
			m_uiVendorContextListener = NULL;
			m_uiRadialContextistener = NULL;
			m_contactsActiveListener = NULL;
			m_smsMessengerActiveListener = NULL;
			m_currentVisibleTargetListener = NULL;
		}
	}

	private function SetupInPlayerDevelopmentSystem()
	{
		var updatePDS : UpdatePlayerDevelopment;
		var setProgressionBuildReq : SetProgressionBuild;
		var build : gamedataBuildType;
		var cpoStartingBuildName : String;
		updatePDS = new UpdatePlayerDevelopment;
		updatePDS.Set( this );
		GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'PlayerDevelopmentSystem' ).QueueRequest( updatePDS );
		if( GameInstance.GetRuntimeInfo( GetGame() ).IsMultiplayer() )
		{
			cpoStartingBuildName = TweakDBInterface.GetCharacterRecord( GetRecordID() ).CpoCharacterBuild();
			build = ( ( gamedataBuildType )( ( ( Int32 )( EnumValueFromString( "gamedataBuildType", cpoStartingBuildName ) ) ) ) );
			setProgressionBuildReq = new SetProgressionBuild;
			setProgressionBuildReq.Set( this, build );
			GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'PlayerDevelopmentSystem' ).QueueRequest( setProgressionBuildReq );
		}
	}

	private function UpdateVisibilityModifier()
	{
		var visibilityValue : Float;
		var detectMultEvent : VisibleObjectDetectionMultEvent;
		var statsSystem : StatsSystem;
		statsSystem = GameInstance.GetStatsSystem( GetGame() );
		visibilityValue = statsSystem.GetStatValue( GetEntityID(), gamedataStatType.Visibility );
		if( visibilityValue >= 0.0 )
		{
			detectMultEvent = new VisibleObjectDetectionMultEvent;
			detectMultEvent.multiplier = visibilityValue;
			QueueEvent( detectMultEvent );
		}
	}

	public static function SendOnBeingNoticed( player : weak< PlayerPuppet >, objectThatNoticed : weak< GameObject > )
	{
		var evt : OnBeingNoticed;
		var revealEvt : RevealObjectEvent;
		if( !( player ) || !( objectThatNoticed ) )
		{
			return;
		}
		evt = new OnBeingNoticed;
		evt.objectThatNoticed = objectThatNoticed;
		player.QueueEvent( evt );
		if( GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.HasAutomaticTagging ) > 0.0 )
		{
			revealEvt = new RevealObjectEvent;
			revealEvt.reveal = true;
			revealEvt.reason.reason = 'AutomaticTagging';
			revealEvt.reason.sourceEntityId = player.GetEntityID();
			revealEvt.lifetime = 15.0;
			objectThatNoticed.QueueEvent( revealEvt );
		}
	}

	protected event OnBeingNoticed( evt : OnBeingNoticed )
	{
		if( !( IsMultiplayer() ) )
		{
			SetEntityNoticedPlayerBBValue( true );
			if( !( m_inCombat ) && ( ( EngineTime.ToFloat( GameInstance.GetTimeSystem( GetGame() ).GetSimTime() ) - GetPS().GetCombatExitTimestamp() ) > 45.0 ) )
			{
				ReactionManagerComponent.SendVOEventToSquad( this, 'detection_warning' );
			}
		}
	}

	private function SetEntityNoticedPlayerBBValue( b : Bool )
	{
		if( m_inCombat || IsReplacer() )
		{
			return;
		}
		if( b == true )
		{
			GetPlayerPerkDataBlackboard().SetUint( GetAllBlackboardDefs().PlayerPerkData.EntityNoticedPlayer, 1 );
			QueueEvent( new ClearBeingNoticedBB );
		}
		else
		{
			GetPlayerPerkDataBlackboard().SetUint( GetAllBlackboardDefs().PlayerPerkData.EntityNoticedPlayer, 0 );
		}
	}

	protected event OnClearBeingNoticedBB( evt : ClearBeingNoticedBB )
	{
		SetEntityNoticedPlayerBBValue( false );
	}

	public function IsBeingRevealed() : Bool
	{
		return m_isBeingRevealed;
	}

	public function SetIsBeingRevealed( isBeingRevealed : Bool )
	{
		m_isBeingRevealed = isBeingRevealed;
	}

	protected event OnBeingTargetByLaserSight( evt : BeingTargetByLaserSightUpdateEvent )
	{
		var forward : Vector4;
		var dot : Float;
		if( ( IsClient() && IsControlledByLocalPeer() ) || !( IsMultiplayer() ) )
		{
			if( evt.state == LaserTargettingState.End )
			{
				if( m_bestTargettingWeapon == evt.weapon )
				{
					m_bestTargettingDot = -1.0;
					m_bestTargettingWeapon = NULL;
				}
				m_targettingEnemies -= 1;
				if( m_targettingEnemies == 0 )
				{
					GameObjectEffectHelper.StopEffectEvent( this, 'laser_targetting' );
					m_laserTargettingVfxBlackboard = NULL;
				}
				return true;
			}
			if( evt.state == LaserTargettingState.Start )
			{
				m_targettingEnemies += 1;
			}
			if( !( m_laserTargettingVfxBlackboard ) )
			{
				m_laserTargettingVfxBlackboard = new worldEffectBlackboard;
				GameObjectEffectHelper.StartEffectEvent( this, 'laser_targetting', false, m_laserTargettingVfxBlackboard );
			}
			forward = Matrix.GetDirectionVector( GetFPPCameraComponent().GetLocalToWorld() );
			dot = -( Vector4.Dot( forward, evt.weapon.GetWorldForward() ) );
			if( m_bestTargettingWeapon != evt.weapon )
			{
				if( dot > m_bestTargettingDot )
				{
					m_bestTargettingWeapon = evt.weapon;
				}
				else
				{
					return true;
				}
			}
			m_laserTargettingVfxBlackboard.SetValue( 'laser_angle', dot );
			m_bestTargettingDot = dot;
		}
	}


	private var m_bossThatTargetsPlayer : EntityID;

	protected event OnBeingTarget( evt : OnBeingTarget )
	{
		var npcPuppet : NPCPuppet;
		var puppetTargetingPlayer : weak< ScriptedPuppet >;
		var evtToSend : OnBeingTarget;
		puppetTargetingPlayer = ( ( ScriptedPuppet )( evt.objectThatTargets ) );
		npcPuppet = ( ( NPCPuppet )( puppetTargetingPlayer ) );
		if( npcPuppet )
		{
			evtToSend = new OnBeingTarget;
			evtToSend.objectThatTargets = evt.objectThatTargets;
			evtToSend.noLongerTarget = evt.noLongerTarget;
			npcPuppet.QueueEvent( evtToSend );
		}
	}

	protected event OnInteractionStateChange( value : Variant )
	{
		var interactionData : bbUIInteractionData;
		var psmEvent : PSMPostponedParameterBool;
		interactionData = ( ( bbUIInteractionData )value );
		if( bbUIInteractionData.HasAnyInteraction( interactionData ) )
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnInteractionStateActive';
		}
		else
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnInteractionStateInactive';
		}
		psmEvent.value = true;
		QueueEvent( psmEvent );
	}

	protected event OnUpdateVisibilityModifierEvent( evt : UpdateVisibilityModifierEvent )
	{
		UpdateVisibilityModifier();
	}

	protected event OnUpdateAutoRevealStatEvent( evt : UpdateAutoRevealStatEvent )
	{
		GetPS().SetAutoReveal( evt.hasAutoReveal );
	}

	public const function HasAutoReveal() : Bool
	{
		return GetPS().HasAutoReveal();
	}

	protected event OnUIContextChange( value : Bool )
	{
		var psmEvent : PSMPostponedParameterBool;
		if( value )
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUIContextActive';
		}
		else
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUIContextInactive';
		}
		psmEvent.value = true;
		QueueEvent( psmEvent );
	}

	protected event OnUIRadialContextChange( value : Bool )
	{
		var psmEvent : PSMPostponedParameterBool;
		if( value )
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUIRadialContextActive';
		}
		else
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUIRadialContextInactive';
		}
		psmEvent.value = true;
		QueueEvent( psmEvent );
	}

	protected event OnCurrentVisibleTargetChanged( targetId : EntityID )
	{
		var bbSystem : BlackboardSystem;
		if( !( EntityID.IsDefined( targetId ) ) )
		{
			bbSystem = GameInstance.GetBlackboardSystem( GetGame() );
			bbSystem.Get( GetAllBlackboardDefs().UI_NameplateData ).SetInt( GetAllBlackboardDefs().UI_NameplateData.DamageProjection, 0, true );
			GameInstance.GetDamageSystem( GetGame() ).ClearPreviewTargetStruct();
		}
	}

	protected event OnUIContactListContextChanged( value : Bool )
	{
		var psmEvent : PSMPostponedParameterBool;
		if( value )
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUIContactListContextActive';
		}
		else
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUIContactListContextInactive';
		}
		psmEvent.value = true;
		QueueEvent( psmEvent );
	}

	protected event OnUISmsMessengerContextChanged( value : Bool )
	{
		var psmEvent : PSMPostponedParameterBool;
		if( value )
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUISmsMessengerContextActive';
		}
		else
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUISmsMessengerContextInactive';
		}
		psmEvent.value = true;
		QueueEvent( psmEvent );
	}

	protected event OnUIVendorContextChange( value : Bool )
	{
		var psmEvent : PSMPostponedParameterBool;
		if( value )
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUIVendorContextActive';
		}
		else
		{
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'OnUIVendorContextInactive';
		}
		psmEvent.value = true;
		QueueEvent( psmEvent );
	}

	protected event OnExperienceGained( evt : ExperiencePointsEvent )
	{
		var addExpRequest : AddExperience;
		addExpRequest = new AddExperience;
		addExpRequest.Set( this, evt.amount, evt.type, evt.isDebug );
		GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'PlayerDevelopmentSystem' ).QueueRequest( addExpRequest );
	}

	protected event OnLevelUp( evt : LevelUpdateEvent ) {}

	protected event OnRequestStats( evt : RequestStats )
	{
		var requestStatsEvent : RequestStatsBB;
		requestStatsEvent = new RequestStatsBB;
		requestStatsEvent.Set( this );
		GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'PlayerDevelopmentSystem' ).QueueRequest( requestStatsEvent );
	}

	protected event OnBuyAttribute( evt : RequestBuyAttribute )
	{
		var attType : gamedataStatType;
		var request : BuyAttribute;
		attType = evt.type;
		request = new BuyAttribute;
		request.Set( this, attType );
		GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'PlayerDevelopmentSystem' ).QueueRequest( request );
	}

	protected event OnItemAddedToSlot( evt : ItemAddedToSlot )
	{
		var newApp : CName;
		var itemTDBID : TweakDBID;
		var paperdollEquipData : SPaperdollEquipData;
		var equipmentData : EquipmentSystemPlayerData;
		var equipmentBB : IBlackboard;
		var equipSlot : SEquipSlot;
		var itemType : gamedataItemCategory;
		var itemRecord : Item_Record;
		var itemID : ItemID;
		var slotID : TweakDBID;
		itemID = evt.GetItemID();
		itemTDBID = ItemID.GetTDBID( itemID );
		itemRecord = TweakDBInterface.GetItemRecord( itemTDBID );
		slotID = evt.GetSlotID();
		if( itemRecord && itemRecord.ItemCategory() )
		{
			itemType = itemRecord.ItemCategory().Type();
		}
		if( itemType == gamedataItemCategory.Weapon )
		{
			newApp = TweakDBInterface.GetCName( itemTDBID + T".specific_player_appearance", '' );
			if( newApp != '' )
			{
				GameInstance.GetTransactionSystem( GetGame() ).ChangeItemAppearanceByName( this, itemID, newApp );
			}
			if( slotID == T"AttachmentSlots.WeaponRight" )
			{
				equipmentBB = GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_Equipment );
				if( equipmentBB )
				{
					paperdollEquipData.equipArea.areaType = TweakDBInterface.GetItemRecord( itemTDBID ).EquipArea().Type();
					paperdollEquipData.equipArea.activeIndex = 0;
					equipSlot.itemID = itemID;
					paperdollEquipData.equipArea.equipSlots.PushBack( equipSlot );
					paperdollEquipData.equipped = true;
					paperdollEquipData.placementSlot = EquipmentSystem.GetPlacementSlot( itemID );
					equipmentBB.SetVariant( GetAllBlackboardDefs().UI_Equipment.lastModifiedArea, paperdollEquipData );
				}
			}
		}
		else if( itemType == gamedataItemCategory.Clothing )
		{
			equipmentData = EquipmentSystem.GetData( GetPlayer( GetGame() ) );
			if( equipmentData && !( equipmentData.IsVisualSetActive() ) )
			{
				equipmentData.OnEquipProcessVisualTags( itemID );
			}
		}
		if( ( slotID == T"AttachmentSlots.WeaponRight" ) || ( slotID == T"AttachmentSlots.WeaponLeft" ) )
		{
			EquipmentSystemPlayerData.UpdateArmSlot( this, evt.GetItemID(), false );
		}
		super.OnItemAddedToSlot( evt );
	}

	protected event OnPartAddedToSlotEvent( evt : PartAddedToSlotEvent )
	{
		var i : Int32;
		var itemRecord : Item_Record;
		var packages : array< weak< GameplayLogicPackage_Record > >;
		itemRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( evt.partID ) );
		if( itemRecord )
		{
			itemRecord.OnAttach( packages );
		}
		for( i = 0; i < packages.Size(); i += 1 )
		{
			RPGManager.ApplyGLP( this, packages[ i ] );
		}
	}

	protected event OnClearItemAppearanceEvent( evt : ClearItemAppearanceEvent )
	{
		var equipmentData : EquipmentSystemPlayerData;
		equipmentData = EquipmentSystem.GetData( GetPlayer( GetGame() ) );
		equipmentData.OnClearItemAppearance( evt.itemID );
	}

	protected event OnResetItemAppearanceEvent( evt : ResetItemAppearanceEvent )
	{
		var equipmentData : EquipmentSystemPlayerData;
		equipmentData = EquipmentSystem.GetData( GetPlayer( GetGame() ) );
		equipmentData.OnResetItemAppearance( evt.itemID );
	}

	protected event OnUnderwearEquipFailsafeEvent( evt : UnderwearEquipFailsafeEvent )
	{
		var equipmentData : EquipmentSystemPlayerData;
		equipmentData = EquipmentSystem.GetData( GetPlayer( GetGame() ) );
		equipmentData.OnUnderwearEquipFailsafe( evt.bottom );
	}

	protected event OnItemRemovedFromSlot( evt : ItemRemovedFromSlot )
	{
		var itemTDBID : TweakDBID;
		var paperdollEquipData : SPaperdollEquipData;
		var equipmentBB : IBlackboard;
		var itemType : gamedataItemCategory;
		var itemRecord : Item_Record;
		var equipSlot : SEquipSlot;
		var itemID : ItemID;
		itemID = evt.GetItemID();
		itemTDBID = ItemID.GetTDBID( itemID );
		itemRecord = TweakDBInterface.GetItemRecord( itemTDBID );
		if( itemRecord && itemRecord.ItemCategory() )
		{
			itemType = itemRecord.ItemCategory().Type();
		}
		if( itemType == gamedataItemCategory.Weapon )
		{
			if( evt.GetSlotID() == T"AttachmentSlots.WeaponRight" )
			{
				equipmentBB = GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_Equipment );
				if( equipmentBB )
				{
					equipSlot.itemID = itemID;
					paperdollEquipData.equipArea.equipSlots.PushBack( equipSlot );
					paperdollEquipData.equipArea.activeIndex = 0;
					paperdollEquipData.equipArea.areaType = itemRecord.EquipArea().Type();
					paperdollEquipData.equipped = false;
					paperdollEquipData.placementSlot = EquipmentSystem.GetPlacementSlot( itemID );
					equipmentBB.SetVariant( GetAllBlackboardDefs().UI_Equipment.lastModifiedArea, paperdollEquipData );
				}
			}
		}
		if( ( evt.GetSlotID() == T"AttachmentSlots.WeaponRight" ) || ( evt.GetSlotID() == T"AttachmentSlots.WeaponLeft" ) )
		{
			EquipmentSystemPlayerData.UpdateArmSlot( this, evt.GetItemID(), true );
		}
		super.OnItemRemovedFromSlot( evt );
	}

	private static function RemoveItemGameplayPackage( objectToRemoveFrom : GameObject, itemID : ItemID )
	{
		var i : Int32;
		var itemRecord : Item_Record;
		var packages : array< weak< GameplayLogicPackage_Record > >;
		itemRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) );
		if( itemRecord )
		{
			itemRecord.OnAttach( packages );
		}
		for( i = 0; i < packages.Size(); i += 1 )
		{
			RPGManager.RemoveGLP( objectToRemoveFrom, packages[ i ] );
		}
	}

	protected event OnPartRemovedFromSlotEvent( evt : PartRemovedFromSlotEvent )
	{
		var transactionSystem : TransactionSystem;
		transactionSystem = GameInstance.GetTransactionSystem( this.GetGame() );
		if( !( transactionSystem.HasItemInAnySlot( this, evt.itemID ) ) )
		{
			RemoveItemGameplayPackage( this, evt.removedPartID );
		}
	}

	protected event OnItemChangedEvent( evt : ItemChangedEvent )
	{
		var eqSystem : weak< EquipmentSystem >;
		var assignHotkey : AssignHotkeyIfEmptySlot;
		var hotkeyRefresh : HotkeyRefreshRequest;
		var itemType : gamedataItemType;
		var itemData : gameItemData;
		var maxAmount : Float;
		itemType = gamedataItemType.Invalid;
		eqSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
		if( eqSystem )
		{
			itemData = evt.itemData;
			maxAmount = itemData.GetStatValueByType( gamedataStatType.Quantity );
			if( RPGManager.GetItemRecord( evt.itemID ).IsSingleInstance() )
			{
				UpdateInventoryWeight( RPGManager.GetItemWeight( itemData ) * ( ( Float )( evt.difference ) ) );
			}
			if( itemData )
			{
				itemType = itemData.GetItemType();
			}
			if( eqSystem.IsItemInHotkey( this, evt.itemID ) )
			{
				hotkeyRefresh = new HotkeyRefreshRequest;
				hotkeyRefresh.owner = this;
				eqSystem.QueueRequest( hotkeyRefresh );
			}
			else if( ( ( ( evt.currentQuantity > 0 ) && Hotkey.IsCompatible( EHotkey.DPAD_UP, itemType ) ) || Hotkey.IsCompatible( EHotkey.RB, itemType ) ) || ( Hotkey.IsCompatible( EHotkey.LBRB, EquipmentSystem.GetEquipAreaType( evt.itemID ) ) && eqSystem.IsEquippable( this, itemData ) ) )
			{
				if( itemType == gamedataItemType.Cyb_HealingAbility && !( RPGManager.IsItemEquipped( this, evt.itemID ) ) )
				{
					return true;
				}
				assignHotkey = AssignHotkeyIfEmptySlot.Construct( evt.itemID, this );
				eqSystem.QueueRequest( assignHotkey );
			}
			if( ( itemType == gamedataItemType.Gen_Junk || itemType == gamedataItemType.Gen_Jewellery ) && ( evt.currentQuantity > ( ( ( Int32 )( maxAmount ) ) - 1 ) ) )
			{
				ItemActionsHelper.DisassembleItem( this, evt.itemID, 1 );
			}
			if( ( itemType == gamedataItemType.Con_Edible || itemType == gamedataItemType.Con_LongLasting ) && ( evt.currentQuantity > ( ( ( Int32 )( maxAmount ) ) - 1 ) ) )
			{
				if( itemData.HasTag( 'Alcohol' ) || itemData.HasTag( 'LongLasting' ) )
				{
					ItemActionsHelper.ConsumeItem( this, evt.itemID, true );
				}
				else if( itemData.HasTag( 'Drink' ) )
				{
					ItemActionsHelper.DrinkItem( this, evt.itemID, true );
				}
				else if( itemData.HasTag( 'Food' ) )
				{
					ItemActionsHelper.EatItem( this, evt.itemID, true );
				}
			}
		}
	}

	protected event OnPartRemovedEvent( evt : PartRemovedEvent )
	{
		var transactionSystem : TransactionSystem;
		transactionSystem = GameInstance.GetTransactionSystem( this.GetGame() );
		if( transactionSystem.HasItemInAnySlot( this, evt.itemID ) )
		{
			RemoveItemGameplayPackage( this, evt.removedPartID );
		}
	}

	protected event OnPrepareForForcedVehicleCombat( evt : PrepareForForcedVehicleCombat )
	{
		var weaponID : ItemID;
		var equipmentSystem : EquipmentSystem;
		var equipRequest : GameplayEquipRequest;
		if( !( UpperBodyTransition.HasRangedWeaponEquipped( this ) ) )
		{
			weaponID = EquipmentSystem.GetData( this ).GetLastUsedOrFirstAvailableOneHandedRangedWeapon();
			if( !( ItemID.IsValid( weaponID ) ) )
			{
				equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
				equipRequest = new GameplayEquipRequest;
				equipRequest.owner = this;
				equipRequest.itemID = ItemID.CreateQuery( T"Items.Preset_V_Unity_Cutscene" );
				equipRequest.blockUpdateWeaponActiveSlots = false;
				equipRequest.forceEquipWeapon = false;
				equipmentSystem.QueueRequest( equipRequest );
			}
		}
	}

	protected event OnItemAddedToInventory( evt : ItemAddedEvent )
	{
		var itemLogDataData : ItemID;
		var entryString : String;
		var questSystem : QuestsSystem;
		var eqSystem : weak< EquipmentSystem >;
		var wardrobeSystem : weak< WardrobeSystem >;
		var transSystem : weak< TransactionSystem >;
		var drawItemRequest : DrawItemRequest;
		var itemData : weak< gameItemData >;
		var itemName : String;
		var gadgetItemUpdated : Bool;
		var itemRecord : Item_Record;
		var itemQuality : gamedataQuality;
		var itemCategory : gamedataItemCategory;
		var itemType : gamedataItemType;
		var weaponEvolution : gamedataWeaponEvolution;
		var tags : array< CName >;
		var notification_Message : String;
		var price : Int32;
		var player : GameObject;
		wardrobeSystem = GameInstance.GetWardrobeSystem( GetGame() );
		transSystem = GameInstance.GetTransactionSystem( GetGame() );
		itemQuality = gamedataQuality.Invalid;
		itemCategory = gamedataItemCategory.Invalid;
		itemType = gamedataItemType.Invalid;
		weaponEvolution = gamedataWeaponEvolution.Invalid;
		if( !( ItemID.IsValid( evt.itemID ) ) )
		{
			return false;
		}
		itemData = evt.itemData;
		questSystem = GameInstance.GetQuestsSystem( GetGame() );
		itemRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( evt.itemID ) );
		itemCategory = RPGManager.GetItemCategory( evt.itemID );
		itemType = RPGManager.GetItemType( evt.itemID );
		itemLogDataData = evt.itemID;
		if( itemData )
		{
			if( !( itemRecord.IsSingleInstance() ) )
			{
				UpdateInventoryWeight( RPGManager.GetItemWeight( itemData ), RPGManager.IsItemBroken( itemData ) );
			}
			TryScaleItemToPlayer( itemData );
			if( itemCategory == gamedataItemCategory.Weapon )
			{
				SetIconicWeaponsTier( itemData );
				LockItemPlusOnNonIconicWeapons( itemData );
				if( itemData.HasStatData( gamedataStatType.Airdropped ) )
				{
					player = GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject();
					StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.JustLootedAmazonIconicFromAirdrop" );
				}
				if( itemData.HasStatData( gamedataStatType.ItemPurchasedAtVendor ) && itemData.HasTag( 'Nue_Jackie' ) )
				{
					player = GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject();
					StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.JustPurchasedNueJackieAtBM" );
				}
			}
			if( itemCategory == gamedataItemCategory.Cyberware )
			{
				if( itemData.HasStatData( gamedataStatType.Airdropped ) )
				{
					player = GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject();
					StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.JustLootedIconicCWFromAirdrop" );
				}
				if( itemData.HasStatData( gamedataStatType.IconicCWFromTreasureChestLooted ) )
				{
					player = GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject();
					StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.JustLootedIconicCWFromTreasureChest" );
				}
			}
			itemQuality = RPGManager.GetItemDataQuality( itemData );
			if( !( ( ( ( ( ( ( itemData.HasTag( 'SkipActivityLog' ) || itemData.HasTag( 'SkipActivityLogOnLoot' ) ) || evt.flaggedAsSilent ) || itemData.HasTag( 'Currency' ) ) || ItemID.HasFlag( itemData.GetID(), gameEItemIDFlag.Preview ) ) || ( itemData.HasTag( 'Recipe' ) && !( itemData.HasTag( 'IconicRecipe' ) ) ) ) || RPGManager.IsItemBroken( itemData ) ) || GameInstance.GetWorkspotSystem( GetGame() ).IsActorInWorkspot( this ) ) )
			{
				itemName = UIItemsHelper.GetItemName( itemRecord, itemData );
				GameInstance.GetActivityLogSystem( GetGame() ).AddLog( ( GetLocalizedText( "UI-ScriptExports-Looted" ) + ": " ) + itemName );
			}
		}
		if( m_itemLogBlackboard )
		{
			m_itemLogBlackboard.SetVariant( GetAllBlackboardDefs().UI_ItemLog.ItemLogItem, itemLogDataData, true );
		}
		eqSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
		if( eqSystem )
		{
			if( ( itemCategory == gamedataItemCategory.Weapon && itemData ) && itemData.HasTag( 'TakeAndEquip' ) )
			{
				drawItemRequest = new DrawItemRequest;
				drawItemRequest.owner = this;
				drawItemRequest.itemID = evt.itemID;
				eqSystem.QueueRequest( drawItemRequest );
			}
		}
		if( ( wardrobeSystem && itemCategory == gamedataItemCategory.Clothing ) && !( wardrobeSystem.IsItemBlacklisted( evt.itemID ) ) )
		{
			wardrobeSystem.StoreUniqueItemIDAndMarkNew( GetGame(), evt.itemID );
		}
		if( itemType == gamedataItemType.Con_Skillbook )
		{
			GameInstance.GetTelemetrySystem( GetGame() ).LogSkillbookUsed( this, evt.itemID );
			ItemActionsHelper.LearnItem( this, evt.itemID, true );
			if( itemData.HasTag( 'LargeSkillbook' ) )
			{
				notification_Message = GetLocalizedText( "LocKey#46534" ) + "\n";
				if( itemData.HasTag( 'Body' ) )
				{
					notification_Message += GetLocalizedText( "LocKey#93274" );
				}
				else if( itemData.HasTag( 'Reflex' ) )
				{
					notification_Message += GetLocalizedText( "LocKey#93275" );
				}
				else if( itemData.HasTag( 'Intelligence' ) )
				{
					notification_Message += GetLocalizedText( "LocKey#93278" );
				}
				else if( itemData.HasTag( 'Cool' ) )
				{
					notification_Message += GetLocalizedText( "LocKey#93280" );
				}
				else if( itemData.HasTag( 'Tech' ) )
				{
					notification_Message += GetLocalizedText( "LocKey#51170" );
				}
				else if( itemData.HasTag( 'PerkSkillbook' ) )
				{
					notification_Message += GetLocalizedText( "LocKey#2694" );
				}
			}
			else if( itemData.HasTag( 'CWCapacity_Shard' ) )
			{
				if( GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.Humanity ) < TDB.GetFloat( T"BaseStats.Humanity.max" ) )
				{
					notification_Message = ( GetLocalizedText( "LocKey#46534" ) + "\n" ) + GetLocalizedText( LocKeyToString( TweakDBInterface.GetItemRecord( ItemID.GetTDBID( evt.itemID ) ).LocalizedDescription() ) );
				}
			}
			else
			{
				notification_Message = ( GetLocalizedText( "LocKey#46534" ) + "\n" ) + GetLocalizedText( LocKeyToString( TweakDBInterface.GetItemRecord( ItemID.GetTDBID( evt.itemID ) ).LocalizedDescription() ) );
			}
			SetWarningMessage( notification_Message, SimpleMessageType.Neutral );
			if( itemData.HasStatData( gamedataStatType.ItemPurchasedAtVendor ) && !( itemData.HasTag( 'LargeSkillbook' ) ) )
			{
				player = GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject();
				if( itemData.HasTag( 'SkillbookReward_Body' ) )
				{
					StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.JustPurchasedBodySkillBookAtShankVendor" );
				}
				else if( itemData.HasTag( 'SkillbookReward_Ref' ) )
				{
					StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.JustPurchasedRefSkillBookAtShankVendor" );
				}
				else if( itemData.HasTag( 'SkillbookReward_Int' ) )
				{
					StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.JustPurchasedIntSkillBookAtShankVendor" );
				}
				else if( itemData.HasTag( 'SkillbookReward_Tech' ) )
				{
					StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.JustPurchasedTechSkillBookAtShankVendor" );
				}
				else if( itemData.HasTag( 'SkillbookReward_Cool' ) )
				{
					StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.JustPurchasedCoolSkillBookAtShankVendor" );
				}
			}
		}
		else if( itemType == gamedataItemType.Con_Edible )
		{
			if( itemData.HasTag( 'PermanentFood' ) )
			{
				ItemActionsHelper.ConsumeItem( this, evt.itemID, true );
				if( itemData.HasTag( 'PermanentStaminaFood' ) )
				{
					( ( Int32 )( this.GetPermanentFoodBonus( gamedataStatType.StaminaRegenBonusBlackmarket ) ) );
					notification_Message = ( GetLocalizedText( "LocKey#93105" ) + "\n" ) + GetLocalizedText( "LocKey#93723" );
				}
				else if( itemData.HasTag( 'PermanentMemoryFood' ) )
				{
					( ( Int32 )( this.GetPermanentFoodBonus( gamedataStatType.MemoryRegenBonusBlackmarket ) ) );
					notification_Message = ( GetLocalizedText( "LocKey#93106" ) + "\n" ) + GetLocalizedText( "LocKey#93724" );
				}
				else if( itemData.HasTag( 'PermanentHealthFood' ) )
				{
					( ( Int32 )( this.GetPermanentFoodBonus( gamedataStatType.HealthBonusBlackmarket ) ) );
					notification_Message = ( GetLocalizedText( "LocKey#93104" ) + "\n" ) + GetLocalizedText( "LocKey#93725" );
				}
				SetWarningMessage( notification_Message, SimpleMessageType.Neutral );
			}
		}
		else if( itemType == gamedataItemType.Gen_Readable )
		{
			transSystem.RemoveItem( this, evt.itemID, 1 );
			entryString = ReadAction.GetJournalEntryFromAction( ItemActionsHelper.GetReadAction( evt.itemID ).GetID() );
			GameInstance.GetJournalManager( GetGame() ).ChangeEntryState( entryString, "gameJournalOnscreen", gameJournalEntryState.Active, JournalNotifyOption.Notify );
		}
		else if( itemType == gamedataItemType.Gen_Junk && ( GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.CanAutomaticallyDisassembleJunk ) > 0.0 ) )
		{
			ItemActionsHelper.DisassembleItem( this, evt.itemID, transSystem.GetItemQuantity( this, evt.itemID ) );
		}
		else if( ( itemType == gamedataItemType.Gad_Grenade || itemType == gamedataItemType.Con_Inhaler ) || itemType == gamedataItemType.Con_Injector )
		{
			tags = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemLogDataData ) ).Tags();
			gadgetItemUpdated = ConsumablesChargesHelper.LeaveTheBestQualityConsumable( GetGame(), ConsumablesChargesHelper.GetConsumableTag( tags ) );
			if( gadgetItemUpdated )
			{
				ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), ItemID.GetTDBID( evt.itemID ) );
				if( itemType == gamedataItemType.Gad_Grenade )
				{
					notification_Message = GetLocalizedText( "LocKey#95690" );
				}
				else
				{
					notification_Message = GetLocalizedText( "LocKey#95689" );
				}
				SetWarningMessage( notification_Message, SimpleMessageType.Neutral );
			}
		}
		else if( itemType == gamedataItemType.Con_Ammo )
		{
			GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_EquipmentData ).SetBool( GetAllBlackboardDefs().UI_EquipmentData.ammoLooted, true );
			GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_EquipmentData ).SignalBool( GetAllBlackboardDefs().UI_EquipmentData.ammoLooted );
		}
		else if( itemType == gamedataItemType.Gen_Keycard )
		{
			GotKeycardNotification();
		}
		else if( itemType == gamedataItemType.Gen_MoneyShard )
		{
			price = RPGManager.CalculateSellPrice( GetGame(), this, evt.itemID ) * evt.itemData.GetQuantity();
			transSystem.GiveMoney( this, price, 'money' );
			transSystem.RemoveItem( this, evt.itemID, transSystem.GetItemQuantity( this, evt.itemID ) );
		}
		if( RPGManager.IsItemBroken( evt.itemData ) && RPGManager.IsItemWeapon( evt.itemID ) )
		{
			ItemActionsHelper.DisassembleItem( this, evt.itemID, transSystem.GetItemQuantity( this, evt.itemID ) );
		}
		if( ( ( questSystem.GetFact( 'disable_tutorials' ) == 0 ) && ( questSystem.GetFact( 'q001_show_sts_tut' ) > 0 ) ) && !( RPGManager.IsItemBroken( evt.itemData ) ) )
		{
			weaponEvolution = RPGManager.GetWeaponEvolution( evt.itemID );
			if( weaponEvolution == gamedataWeaponEvolution.Smart && ( questSystem.GetFact( 'smart_weapon_tutorial' ) == 0 ) )
			{
				questSystem.SetFact( 'smart_weapon_tutorial', 1 );
			}
			else if( ( weaponEvolution == gamedataWeaponEvolution.Tech && RPGManager.IsTechPierceEnabled( GetGame(), this, evt.itemID ) ) && ( questSystem.GetFact( 'tech_weapon_tutorial' ) == 0 ) )
			{
				questSystem.SetFact( 'tech_weapon_tutorial', 1 );
			}
			else if( ( ( ( weaponEvolution == gamedataWeaponEvolution.Power && RPGManager.IsRicochetChanceEnabled( GetGame(), this, evt.itemID ) ) && ( questSystem.GetFact( 'power_weapon_tutorial' ) == 0 ) ) && ( evt.itemID != ItemID.CreateQuery( T"Items.Preset_V_Unity_Cutscene" ) ) ) && ( evt.itemID != ItemID.CreateQuery( T"Items.Preset_V_Unity" ) ) )
			{
				questSystem.SetFact( 'power_weapon_tutorial', 1 );
			}
			if( itemCategory == gamedataItemCategory.Gadget && ( questSystem.GetFact( 'grenade_inventory_tutorial' ) == 0 ) )
			{
				questSystem.SetFact( 'grenade_inventory_tutorial', 1 );
			}
			else if( itemCategory == gamedataItemCategory.Cyberware && ( questSystem.GetFact( 'cyberware_inventory_tutorial' ) == 0 ) )
			{
				questSystem.SetFact( 'cyberware_inventory_tutorial', 1 );
			}
			if( ( itemType == gamedataItemType.Con_Inhaler || itemType == gamedataItemType.Con_Injector ) && ( questSystem.GetFact( 'consumable_inventory_tutorial' ) == 0 ) )
			{
				questSystem.SetFact( 'consumable_inventory_tutorial', 1 );
			}
			if( ( RPGManager.IsItemIconic( evt.itemData ) && itemCategory == gamedataItemCategory.Weapon ) && ( questSystem.GetFact( 'iconic_item_tutorial' ) == 0 ) )
			{
				questSystem.SetFact( 'iconic_item_tutorial', 1 );
			}
		}
		if( questSystem.GetFact( 'initial_gadget_picked' ) == 0 )
		{
			if( RPGManager.GetItemCategory( evt.itemID ) == gamedataItemCategory.Gadget )
			{
				questSystem.SetFact( 'initial_gadget_picked', 1 );
			}
		}
		RPGManager.ProcessOnLootedPackages( this, evt.itemID );
		if( itemQuality == gamedataQuality.Legendary || itemQuality == gamedataQuality.Iconic )
		{
			GameInstance.GetAutoSaveSystem( GetGame() ).RequestCheckpoint();
		}
	}

	protected event OnRefreshItemPlayerScalingEvent( evt : RefreshItemPlayerScalingEvent )
	{
		var transactionSystem : TransactionSystem;
		var itemList : array< weak< gameItemData > >;
		var i : Int32;
		transactionSystem = GameInstance.GetTransactionSystem( GetGame() );
		transactionSystem.GetItemList( this, itemList );
		for( i = 0; i < itemList.Size(); i += 1 )
		{
			if( itemList[ i ].HasTag( 'RescalePL' ) )
			{
				TryScaleItemToPlayer( itemList[ i ] );
			}
		}
	}

	public function TryScaleItemToPlayer( itemData : gameItemData )
	{
		var scalingMod : gameStatModifierData;
		var scalingBlocker : gameStatModifierData;
		if( ( TweakDBInterface.GetBool( ItemID.GetTDBID( itemData.GetID() ) + T".scaleToPlayer", false ) && ( itemData.GetStatValueByType( gamedataStatType.ScalingBlocked ) < 1.0 ) ) && ( ( ( ( ( itemData.GetStatValueByType( gamedataStatType.PowerLevel ) <= 1.0 ) || itemData.HasTag( 'DLCStashItem' ) ) || itemData.HasTag( 'AutoScalingItem' ) ) || itemData.HasTag( 'StashScaling_Iconic' ) ) || itemData.HasTag( 'Left_Hand_Retrofix' ) ) )
		{
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.LootLevel, true );
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.PowerLevel, true );
			scalingMod = RPGManager.CreateStatModifier( gamedataStatType.LootLevel, gameStatModifierType.Additive, GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.PowerLevel ) );
			scalingBlocker = RPGManager.CreateStatModifier( gamedataStatType.ScalingBlocked, gameStatModifierType.Additive, 1.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingMod );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingBlocker );
		}
	}

	protected event OnRestoreCybWeaponQualitiesEvent( evt : RestoreCybWeaponQualitiesEvent )
	{
		var transactionSystem : TransactionSystem;
		var itemList : array< weak< gameItemData > >;
		var itemRecord : Item_Record;
		var quality : gamedataQuality;
		var itemData : gameItemData;
		var itemType : gamedataItemType;
		var qualityMod : gameStatModifierData;
		var tierPlusMod : gameStatModifierData;
		var qualityStat : Float;
		var tierPlusStat : Float;
		var baseQuality : Float;
		var qualityPlus : Float;
		var i : Int32;
		transactionSystem = GameInstance.GetTransactionSystem( GetGame() );
		transactionSystem.GetItemList( this, itemList );
		for( i = 0; i < itemList.Size(); i += 1 )
		{
			itemData = itemList[ i ];
			itemType = itemData.GetItemType();
			if( ( ( itemType != gamedataItemType.Cyb_Launcher && itemType != gamedataItemType.Cyb_MantisBlades ) && itemType != gamedataItemType.Cyb_StrongArms ) && itemType != gamedataItemType.Cyb_NanoWires )
			{
				continue;
			}
			itemRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemData.GetID() ) );
			quality = itemRecord.QualityHandle().Type();
			switch( quality )
			{
				case gamedataQuality.Common:
					baseQuality = 0.0;
				qualityPlus = 0.0;
				break;
				case gamedataQuality.CommonPlus:
					baseQuality = 0.0;
				qualityPlus = 1.0;
				break;
				case gamedataQuality.Uncommon:
					baseQuality = 1.0;
				qualityPlus = 0.0;
				break;
				case gamedataQuality.UncommonPlus:
					baseQuality = 1.0;
				qualityPlus = 1.0;
				break;
				case gamedataQuality.Rare:
					baseQuality = 2.0;
				qualityPlus = 0.0;
				break;
				case gamedataQuality.RarePlus:
					baseQuality = 2.0;
				qualityPlus = 1.0;
				break;
				case gamedataQuality.Epic:
					baseQuality = 3.0;
				qualityPlus = 0.0;
				break;
				case gamedataQuality.EpicPlus:
					baseQuality = 3.0;
				qualityPlus = 1.0;
				break;
				case gamedataQuality.Legendary:
					baseQuality = 4.0;
				qualityPlus = 0.0;
				break;
				case gamedataQuality.LegendaryPlus:
					baseQuality = 4.0;
				qualityPlus = 1.0;
				break;
				case gamedataQuality.LegendaryPlusPlus:
					baseQuality = 4.0;
				qualityPlus = 2.0;
				break;
				default:
					break;
			}
			qualityStat = itemData.GetStatValueByType( gamedataStatType.Quality );
			tierPlusStat = itemData.GetStatValueByType( gamedataStatType.IsItemPlus );
			if( ( ( baseQuality * 2.0 ) + qualityPlus ) != ( ( qualityStat * 2.0 ) + tierPlusStat ) )
			{
				GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.Quality, true );
				GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.IsItemPlus, true );
				qualityMod = RPGManager.CreateStatModifier( gamedataStatType.Quality, gameStatModifierType.Additive, baseQuality );
				tierPlusMod = RPGManager.CreateStatModifier( gamedataStatType.IsItemPlus, gameStatModifierType.Additive, qualityPlus );
				if( GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), qualityMod ) && GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), tierPlusMod ) )
				{
					qualityStat = itemData.GetStatValueByType( gamedataStatType.Quality );
					tierPlusStat = itemData.GetStatValueByType( gamedataStatType.IsItemPlus );
				}
			}
		}
	}

	protected event OnRescaleNonIconicWeaponsEvent( evt : RescaleNonIconicWeaponsEvent )
	{
		var transactionSystem : TransactionSystem;
		var itemList : array< weak< gameItemData > >;
		var i : Int32;
		transactionSystem = GameInstance.GetTransactionSystem( GetGame() );
		transactionSystem.GetItemList( this, itemList );
		for( i = 0; i < itemList.Size(); i += 1 )
		{
			if( !( RPGManager.IsItemIconic( itemList[ i ] ) ) && RPGManager.IsItemWeapon( itemList[ i ].GetID() ) )
			{
				RetroRescaleNonIconicWeapons( itemList[ i ] );
			}
		}
	}

	public function RetroRescaleNonIconicWeapons( itemData : gameItemData )
	{
		var lootLevelMod : gameStatModifierData;
		var scalingBlocker : gameStatModifierData;
		var randomizerMod : gameStatModifierData;
		var qualityMod : gameStatModifierData;
		var randomCurveMod : gameStatModifierData;
		var playerLevel : Float;
		var playerMaxQualtity : Float;
		var weaponQuality : Float;
		var itemType : gamedataItemType;
		var isScalingBlocked, lootLevel, isItemCrafted, isItemPurchased, isIconic : Float;
		itemType = itemData.GetItemType();
		if( ( ( itemType == gamedataItemType.Cyb_Launcher || itemType == gamedataItemType.Cyb_MantisBlades ) || itemType == gamedataItemType.Cyb_StrongArms ) || itemType == gamedataItemType.Cyb_NanoWires )
		{
			return;
		}
		playerLevel = GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.Level );
		playerMaxQualtity = GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.MaxQuality );
		weaponQuality = itemData.GetStatValueByType( gamedataStatType.Quality );
		isScalingBlocked = itemData.GetStatValueByType( gamedataStatType.ScalingBlocked );
		lootLevel = itemData.GetStatValueByType( gamedataStatType.LootLevel );
		isItemCrafted = itemData.GetStatValueByType( gamedataStatType.IsItemCrafted );
		isItemPurchased = itemData.GetStatValueByType( gamedataStatType.ItemPurchasedAtVendor );
		isIconic = itemData.GetStatValueByType( gamedataStatType.IsItemIconic );
		if( ( ( ( ( isScalingBlocked < 1.0 ) && ( ( lootLevel < 1.0 ) || ( lootLevel > playerLevel ) ) ) && ( isItemCrafted < 1.0 ) ) && ( isItemPurchased < 1.0 ) ) && ( isIconic < 1.0 ) )
		{
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.PowerLevel, true );
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.LootLevel, true );
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.RandomCurveInput, true );
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.Quality, true );
			lootLevelMod = RPGManager.CreateStatModifier( gamedataStatType.LootLevel, gameStatModifierType.Additive, playerLevel );
			scalingBlocker = RPGManager.CreateStatModifier( gamedataStatType.ScalingBlocked, gameStatModifierType.Additive, 1.0 );
			randomizerMod = RPGManager.CreateStatModifier( gamedataStatType.NPCWeaponDropRandomizer, gameStatModifierType.Additive, RandRangeF( -0.80000001, 0.2 ) );
			randomCurveMod = RPGManager.CreateStatModifier( gamedataStatType.RandomCurveInput, gameStatModifierType.Additive, RandRangeF( 0.01, 0.99000001 ) );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), lootLevelMod );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingBlocker );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), randomizerMod );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), randomCurveMod );
			lootLevelMod = RPGManager.CreateCombinedStatModifier( gamedataStatType.LootLevel, gameStatModifierType.AdditiveMultiplier, gamedataStatType.NPCWeaponDropRandomizer, gameCombinedStatOperation.Multiplication, 1.0, gameStatObjectsRelation.Self );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), lootLevelMod );
			randomCurveMod = RPGManager.CreateStatModifierUsingCurve( gamedataStatType.RandomCurveInput, gameStatModifierType.Additive, gamedataStatType.LootLevel, 'quality_curves', 'level_to_random_range_mult_new' );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), randomCurveMod );
			qualityMod = RPGManager.CreateStatModifierUsingCurve( gamedataStatType.Quality, gameStatModifierType.Additive, gamedataStatType.RandomCurveInput, 'random_distributions', 'quality_distribution_new' );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), qualityMod );
		}
		else if( ( ( ( isScalingBlocked < 1.0 ) && ( isItemCrafted >= 1.0 ) ) && ( isIconic < 1.0 ) ) && ( ( weaponQuality - playerMaxQualtity ) >= 2.0 ) )
		{
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.Quality, true );
			qualityMod = RPGManager.CreateStatModifier( gamedataStatType.Quality, gameStatModifierType.Additive, playerMaxQualtity );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), qualityMod );
			scalingBlocker = RPGManager.CreateStatModifier( gamedataStatType.ScalingBlocked, gameStatModifierType.Additive, 1.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingBlocker );
		}
	}

	protected event OnConsumablesChargesRework( evt : ConsumablesChargesReworkEvent )
	{
		var i : Int32;
		var chargedConsumables : array< weak< gameItemData > >;
		var transactionSystem : TransactionSystem;
		var playerLevel : Float;
		playerLevel = GameInstance.GetStatsSystem( GetGame() ).GetStatValue( this.GetEntityID(), gamedataStatType.Level );
		transactionSystem = GameInstance.GetTransactionSystem( GetGame() );
		transactionSystem.GetItemListByTag( this, 'ChargedConsumable', chargedConsumables );
		for( i = 0; i < chargedConsumables.Size(); i += 1 )
		{
			if( chargedConsumables[ i ].HasTag( 'Ozob' ) )
			{
				ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeOzobsNose" );
				continue;
			}
			transactionSystem.RemoveItem( this, chargedConsumables[ i ].GetID(), chargedConsumables[ i ].GetQuantity() );
		}
		if( playerLevel <= 9.0 )
		{
			transactionSystem.GiveItemByTDBID( this, T"Items.FirstAidWhiffV0", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.BonesMcCoy70V0", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFragRegular", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFlashRegular", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeEMPRegular", 1 );
		}
		else if( ( playerLevel > 9.0 ) && ( playerLevel <= 17.0 ) )
		{
			transactionSystem.GiveItemByTDBID( this, T"Items.FirstAidWhiffVUncommon", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.BonesMcCoy70VUncommon", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFragUncommon", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFlashUncommon", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeEMPUncommon", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeSmokeRegular", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeIncendiaryRegular", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeReconRegular", 1 );
		}
		else if( ( playerLevel > 17.0 ) && ( playerLevel <= 25.0 ) )
		{
			transactionSystem.GiveItemByTDBID( this, T"Items.FirstAidWhiffV1", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.BonesMcCoy70V1", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFragSticky", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFlashHoming", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeEMPRare", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeSmokeRare", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeBiohazardRegular", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeIncendiaryRare", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeReconRare", 1 );
		}
		else if( ( playerLevel > 25.0 ) && ( playerLevel <= 33.0 ) )
		{
			transactionSystem.GiveItemByTDBID( this, T"Items.FirstAidWhiffVEpic", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.BonesMcCoy70VEpic", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFragEpic", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFlashEpic", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeEMPSticky", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeSmokeEpic", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeBiohazardHoming", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeIncendiarySticky", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeReconSticky", 1 );
		}
		else if( playerLevel > 33.0 )
		{
			transactionSystem.GiveItemByTDBID( this, T"Items.FirstAidWhiffV2", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.BonesMcCoy70V2", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFragLegendary", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeFlashLegendary", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeEMPLegendary", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeSmokeLegendary", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeBiohazardLegendary", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeIncendiaryLegendary", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeReconLegendary", 1 );
			transactionSystem.GiveItemByTDBID( this, T"Items.GrenadeCuttingRegular", 1 );
		}
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeEMPRegular" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeFragRegular" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeFlashRegular" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.FirstAidWhiffV0" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.BonesMcCoy70V0" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeReconRegular" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeIncendiaryRegular" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.FirstAidWhiffV1" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.BonesMcCoy70V1" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.FirstAidWhiffV1" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.BonesMcCoy70V1" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.RecipeGrenadeFlashHoming" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeFragSticky" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeBiohazardRegular" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeIncendiaryRare" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeFlashHoming" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeEMPSticky" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeBiohazardHoming" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeIncendiarySticky" );
		ConsumablesChargesHelper.HideConsumableRecipe( GetGame(), T"Items.GrenadeReconSticky" );
	}

	protected event OnBlockAndCompensateScalingEvent( evt : BlockAndCompensateScalingEvent )
	{
		var transactionSystem : TransactionSystem;
		var itemList : array< weak< gameItemData > >;
		var i : Int32;
		transactionSystem = GameInstance.GetTransactionSystem( GetGame() );
		transactionSystem.GetItemList( this, itemList );
		for( i = 0; i < itemList.Size(); i += 1 )
		{
			if( itemList[ i ].HasTag( 'DLCStashItem' ) || itemList[ i ].HasTag( 'AutoScalingItem' ) )
			{
				BlockScaling( itemList[ i ] );
				CompensateExceedScaling( itemList[ i ] );
			}
		}
	}

	public function BlockScaling( itemData : gameItemData )
	{
		var scalingBlocker : gameStatModifierData;
		if( TweakDBInterface.GetBool( ItemID.GetTDBID( itemData.GetID() ) + T".scaleToPlayer", false ) )
		{
			scalingBlocker = RPGManager.CreateStatModifier( gamedataStatType.ScalingBlocked, gameStatModifierType.Additive, 1.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingBlocker );
		}
	}

	public function CompensateExceedScaling( itemData : gameItemData )
	{
		var scalingCompensateL : gameStatModifierData;
		var scalingCompensateIL : gameStatModifierData;
		var playerLevel : Float;
		var lvlDiff : Float;
		var upgradeCount : Float;
		playerLevel = GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.Level );
		lvlDiff = itemData.GetStatValueByType( gamedataStatType.Level ) - playerLevel;
		upgradeCount = itemData.GetStatValueByType( gamedataStatType.WasItemUpgraded );
		if( TweakDBInterface.GetBool( ItemID.GetTDBID( itemData.GetID() ) + T".scaleToPlayer", false ) && ( lvlDiff > 0.0 ) )
		{
			scalingCompensateL = RPGManager.CreateStatModifier( gamedataStatType.Level, gameStatModifierType.Additive, upgradeCount * -1.0 );
			scalingCompensateIL = RPGManager.CreateStatModifier( gamedataStatType.ItemLevel, gameStatModifierType.Additive, upgradeCount * -10.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingCompensateL );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingCompensateIL );
		}
	}

	public function SetIconicWeaponsTier( itemData : gameItemData )
	{
		var qualityToUpgradeMod : gameStatModifierData;
		var upgradeToQualityMod : gameStatModifierData;
		var plusToUpgradeMod : gameStatModifierData;
		var upgradeToPlusMod : gameStatModifierData;
		var iconicCheck : Bool;
		iconicCheck = itemData.HasTag( 'IconicWeapon' );
		if( iconicCheck && ( itemData.GetStatValueByType( gamedataStatType.WasItemUpgraded ) < 1.0 ) )
		{
			qualityToUpgradeMod = RPGManager.CreateStatModifier( gamedataStatType.WasItemUpgraded, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.Quality ) * 2.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), qualityToUpgradeMod );
			upgradeToQualityMod = RPGManager.CreateStatModifier( gamedataStatType.Quality, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.WasItemUpgraded ) * -0.5 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), upgradeToQualityMod );
			plusToUpgradeMod = RPGManager.CreateStatModifier( gamedataStatType.WasItemUpgraded, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.IsItemPlus ) );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), plusToUpgradeMod );
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.IsItemPlus, true );
			upgradeToPlusMod = RPGManager.CreateStatModifierUsingCurve( gamedataStatType.IsItemPlus, gameStatModifierType.Additive, gamedataStatType.WasItemUpgraded, 'quality_curves', 'iconic_upgrades_amount_to_plus' );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), upgradeToPlusMod );
		}
	}

	public function LockItemPlusOnNonIconicWeapons( itemData : gameItemData )
	{
		var plusMod : gameStatModifierData;
		var iconicCheck : Bool;
		iconicCheck = itemData.HasTag( 'IconicWeapon' );
		if( !( iconicCheck ) )
		{
			plusMod = RPGManager.CreateStatModifier( gamedataStatType.IsItemPlus, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.IsItemPlus ) );
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.IsItemPlus, true );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), plusMod );
		}
	}

	protected event OnIconicsReworkCompensateEvent( evt : IconicsReworkCompensateEvent )
	{
		var transactionSystem : TransactionSystem;
		var itemList : array< weak< gameItemData > >;
		var i : Int32;
		transactionSystem = GameInstance.GetTransactionSystem( GetGame() );
		transactionSystem.GetItemList( this, itemList );
		for( i = 0; i < itemList.Size(); i += 1 )
		{
			if( itemList[ i ].HasTag( 'IconicWeapon' ) )
			{
				RescaleOwnedIconicsToPlayerLevel( itemList[ i ] );
			}
		}
	}

	public function RescaleOwnedIconicsToPlayerLevel( itemData : gameItemData )
	{
		var lootLevelMod : gameStatModifierData;
		var itemLeveltier5LimiterMod : gameStatModifierData;
		var zeroUpgradeMod : gameStatModifierData;
		var qualityToUpgradeMod : gameStatModifierData;
		var plusToUpgradeMod : gameStatModifierData;
		var upgradeToQualityMod : gameStatModifierData;
		var upgradeToPlusMod : gameStatModifierData;
		var playerLevel : Float;
		playerLevel = GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.Level );
		lootLevelMod = RPGManager.CreateStatModifier( gamedataStatType.LootLevel, gameStatModifierType.Additive, playerLevel );
		zeroUpgradeMod = RPGManager.CreateStatModifier( gamedataStatType.WasItemUpgraded, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.WasItemUpgraded ) * -1.0 );
		GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.PowerLevel, true );
		GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.LootLevel, true );
		GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.ItemLevel, true );
		GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), zeroUpgradeMod );
		GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), lootLevelMod );
		itemLeveltier5LimiterMod = RPGManager.CreateStatModifierUsingCurve( gamedataStatType.ItemLevel, gameStatModifierType.Additive, gamedataStatType.LootLevel, 'quality_curves', 'iconic_weapon_level_tier5_limiter_retrofix' );
		GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), itemLeveltier5LimiterMod );
		lootLevelMod = RPGManager.CreateStatModifier( gamedataStatType.LootLevel, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.ItemLevel ) );
		GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), lootLevelMod );
		qualityToUpgradeMod = RPGManager.CreateStatModifier( gamedataStatType.WasItemUpgraded, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.Quality ) * 2.0 );
		GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), qualityToUpgradeMod );
		upgradeToQualityMod = RPGManager.CreateStatModifier( gamedataStatType.Quality, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.WasItemUpgraded ) * -0.5 );
		GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), upgradeToQualityMod );
		plusToUpgradeMod = RPGManager.CreateStatModifier( gamedataStatType.WasItemUpgraded, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.IsItemPlus ) );
		GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), plusToUpgradeMod );
		GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.IsItemPlus, true );
		upgradeToPlusMod = RPGManager.CreateStatModifierUsingCurve( gamedataStatType.IsItemPlus, gameStatModifierType.Additive, gamedataStatType.WasItemUpgraded, 'quality_curves', 'iconic_upgrades_amount_to_plus' );
		GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), upgradeToPlusMod );
	}

	protected event OnRasetsuToPlayerScalingEvent( evt : RasetsuToPlayerScalingEvent )
	{
		var transactionSystem : TransactionSystem;
		var itemList : array< weak< gameItemData > >;
		var i : Int32;
		transactionSystem = GameInstance.GetTransactionSystem( GetGame() );
		transactionSystem.GetItemList( this, itemList );
		for( i = 0; i < itemList.Size(); i += 1 )
		{
			if( itemList[ i ].HasTag( 'Rasetsu' ) )
			{
				ScaleRasetsuToProperTier( itemList[ i ] );
			}
		}
	}

	public function ScaleRasetsuToProperTier( itemData : gameItemData )
	{
		var scalingMod : gameStatModifierData;
		var scalingBlocker : gameStatModifierData;
		if( ( TweakDBInterface.GetBool( ItemID.GetTDBID( itemData.GetID() ) + T".scaleToPlayer", false ) && ( itemData.GetStatValueByType( gamedataStatType.ScalingBlocked ) < 1.0 ) ) && ( ( ( ( ( itemData.GetStatValueByType( gamedataStatType.PowerLevel ) <= 1.0 ) || itemData.HasTag( 'DLCStashItem' ) ) || itemData.HasTag( 'AutoScalingItem' ) ) || itemData.HasTag( 'StashScaling_Iconic' ) ) || itemData.HasTag( 'Left_Hand_Retrofix' ) ) )
		{
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.LootLevel, true );
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.PowerLevel, true );
			scalingMod = RPGManager.CreateStatModifierUsingCurve( gamedataStatType.LootLevel, gameStatModifierType.Additive, gamedataStatType.WasItemUpgraded, 'quality_curves', 'iconic_upgrades_amount_to_level' );
			scalingBlocker = RPGManager.CreateStatModifier( gamedataStatType.ScalingBlocked, gameStatModifierType.Additive, 1.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingMod );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingBlocker );
			scalingMod = RPGManager.CreateStatModifier( gamedataStatType.LootLevel, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.LootLevel ) );
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.LootLevel, true );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), scalingMod );
		}
	}

	protected event OnUnifyIconicsUpgradeCountWithEffectiveTierEvent( evt : UnifyIconicsUpgradeCountWithEffectiveTierEvent )
	{
		var transactionSystem : TransactionSystem;
		var itemList : array< weak< gameItemData > >;
		var i : Int32;
		transactionSystem = GameInstance.GetTransactionSystem( GetGame() );
		transactionSystem.GetItemList( this, itemList );
		for( i = 0; i < itemList.Size(); i += 1 )
		{
			if( itemList[ i ].HasTag( 'IconicWeapon' ) )
			{
				UnifyIconicWeaponsUpgradesCountWithEffectiveTier( itemList[ i ] );
			}
		}
	}

	public function UnifyIconicWeaponsUpgradesCountWithEffectiveTier( itemData : gameItemData )
	{
		var upgradeMod : gameStatModifierData;
		var plusMod : gameStatModifierData;
		var effectiveTier : Float;
		var upgradeCount : Float;
		effectiveTier = itemData.GetStatValueByType( gamedataStatType.EffectiveTier );
		upgradeCount = itemData.GetStatValueByType( gamedataStatType.WasItemUpgraded );
		if( upgradeCount < effectiveTier )
		{
			upgradeMod = RPGManager.CreateStatModifier( gamedataStatType.WasItemUpgraded, gameStatModifierType.Additive, itemData.GetStatValueByType( gamedataStatType.EffectiveTier ) );
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.WasItemUpgraded, true );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), upgradeMod );
		}
		GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.IsItemPlus, true );
		plusMod = RPGManager.CreateStatModifierUsingCurve( gamedataStatType.IsItemPlus, gameStatModifierType.Additive, gamedataStatType.WasItemUpgraded, 'quality_curves', 'iconic_upgrades_amount_to_plus' );
		GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( itemData.GetStatsObjectID(), plusMod );
	}

	private function GetItemCapacityRequirement( itemID : ItemID ) : Float
	{
		var itemModParams : ItemModParams;
		var itemData : gameItemData;
		var i : Int32;
		var reqs : array< SItemStackRequirementData >;
		itemModParams.quantity = 1;
		itemModParams.itemID = itemID;
		itemData = Inventory.CreateItemData( itemModParams, this );
		reqs = RPGManager.GetEquipRequirements( this, itemData );
		for( i = reqs.Size() - 1; i >= 0; i -= 1 )
		{
			if( reqs[ i ].statType == gamedataStatType.HumanityAvailable )
			{
				return reqs[ i ].requiredValue;
			}
		}
		return 0.0;
	}

	protected event OnRetrofixQuickhacksEvent( evt : RetrofixQuickhacksEvent )
	{
		var ts : TransactionSystem;
		var itemList : array< weak< gameItemData > >;
		var itemID : ItemID;
		var counterPartID : TweakDBID;
		var qhRecord, counterPartRecord : Item_Record;
		var i : Int32;
		ts = GameInstance.GetTransactionSystem( GetGame() );
		ts.GetItemListByTag( this, 'SoftwareShard', itemList );
		for( i = 0; i < itemList.Size(); i += 1 )
		{
			itemID = itemList[ i ].GetID();
			qhRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) );
			if( qhRecord.Deprecated() )
			{
				counterPartRecord = qhRecord.CounterpartHandle();
				if( counterPartRecord )
				{
					counterPartID = counterPartRecord.GetRecordID();
					if( !( TDBID.IsValid( counterPartID ) ) )
					{
						continue;
					}
					ts.GiveItemByTDBID( this, counterPartID, itemList[ i ].GetQuantity() );
				}
				ts.RemoveItem( this, itemID, itemList[ i ].GetQuantity() );
			}
		}
	}

	private function GetGlitchedEquippedCyberware( out items : array< ItemID > )
	{
		var ts : TransactionSystem;
		var equipmentAreas : array< gamedataEquipmentArea >;
		var equipmentSystemPlayerData : EquipmentSystemPlayerData;
		var currentItem : ItemID;
		var i, j : Int32;
		ts = GameInstance.GetTransactionSystem( GetGame() );
		equipmentSystemPlayerData = EquipmentSystem.GetData( this );
		equipmentAreas = equipmentSystemPlayerData.GetAllCyberwareEquipmentAreas();
		for( i = 0; i < equipmentAreas.Size(); i += 1 )
		{
			for( j = 0; j < equipmentSystemPlayerData.GetNumberOfSlots( equipmentAreas[ i ] ); j += 1 )
			{
				currentItem = GetEquippedItemIdInArea( equipmentAreas[ i ], j );
				if( ItemID.IsValid( currentItem ) && !( ts.HasItem( this, currentItem ) ) )
				{
					items.PushBack( currentItem );
				}
			}
		}
	}

	private function PrioritizeCyberwareOnRetrofix( itemID : ItemID, equipmentArea : gamedataEquipmentArea, out p1 : array< ItemID >, out p2 : array< ItemID >, out p3 : array< ItemID >, beforeVikVisit : Bool ) : Bool
	{
		if( beforeVikVisit && equipmentArea != gamedataEquipmentArea.SystemReplacementCW )
		{
			return false;
		}
		if( ( equipmentArea == gamedataEquipmentArea.SystemReplacementCW || equipmentArea == gamedataEquipmentArea.ArmsCW ) || equipmentArea == gamedataEquipmentArea.HandsCW )
		{
			p1.PushBack( itemID );
		}
		else if( equipmentArea == gamedataEquipmentArea.LegsCW )
		{
			p2.PushBack( itemID );
		}
		else
		{
			p3.PushBack( itemID );
		}
		return true;
	}

	private function RemoveCyberwareParts( item : ItemID ) : array< ItemID >
	{
		var ts : TransactionSystem;
		var i : Int32;
		var itemParts : array< SPartSlots >;
		var installedPartId : TweakDBID;
		var removedItems : array< ItemID >;
		var counterPartID : TweakDBID;
		var qhRecord, counterPartRecord : Item_Record;
		var convertedItem : ItemID;
		ts = GameInstance.GetTransactionSystem( GetGame() );
		itemParts = ItemModificationSystem.GetAllSlots( this, item );
		for( i = 0; i < itemParts.Size(); i += 1 )
		{
			if( itemParts[ i ].status == ESlotState.Taken )
			{
				installedPartId = ItemID.GetTDBID( itemParts[ i ].installedPart );
				if( installedPartId != T"Items.GenericItemRoot" )
				{
					qhRecord = TweakDBInterface.GetItemRecord( installedPartId );
					if( qhRecord.Deprecated() )
					{
						counterPartRecord = qhRecord.CounterpartHandle();
						counterPartID = counterPartRecord.GetRecordID();
						if( counterPartRecord && TDBID.IsValid( counterPartID ) )
						{
							convertedItem = ItemID.FromTDBID( counterPartID );
							ts.GiveItem( this, convertedItem, 1 );
							removedItems.PushBack( convertedItem );
						}
						ts.RemoveItem( this, ts.RemovePart( this, item, itemParts[ i ].slotID ), 1 );
					}
					else
					{
						removedItems.PushBack( ts.RemovePart( this, item, itemParts[ i ].slotID ) );
					}
				}
			}
		}
		return removedItems;
	}

	protected event OnRetrofixCyberwaresEvent( evt : RetrofixCyberwaresEvent )
	{
		var ts : TransactionSystem;
		var equipmentSystem : EquipmentSystem;
		var equipmentData : EquipmentSystemPlayerData;
		var statsSystem : StatsSystem;
		var itemList : array< weak< gameItemData > >;
		var equippedCW : array< ItemID >;
		var itemID : ItemID;
		var counterPartID : TweakDBID;
		var cwRecord, counterPartRecord : Item_Record;
		var i : Int32;
		var equipmentArea : gamedataEquipmentArea;
		var hasSmartLink : Bool;
		var maxAvailableQuality : gamedataQuality;
		var maxAvailableQualityInt : Int32;
		var tutorialCapacityPrediction : Float;
		var availableCapacity : Float;
		var firstPriorityCW, secondPriorityCW, thirdPriorityCW, freeArmorCW : array< ItemID >;
		var beforeVikVisit : Bool;
		var quickhackParts : array< ItemID >;
		var maxEquippedQuickhackQuality : Int32;
		ts = GameInstance.GetTransactionSystem( GetGame() );
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
		equipmentData = equipmentSystem.GetPlayerData( this );
		statsSystem = GameInstance.GetStatsSystem( GetGame() );
		hasSmartLink = ( ( Bool )( statsSystem.GetStatValue( GetEntityID(), gamedataStatType.HasSmartLink ) ) );
		maxAvailableQuality = RPGManager.ConvertPlayerLevelToCyberwareQuality( GameInstance.GetStatsSystem( GetGame() ).GetStatValue( this.GetEntityID(), gamedataStatType.Level ), false );
		maxAvailableQualityInt = RPGManager.ConvertQualityToCombinedValue( maxAvailableQuality );
		tutorialCapacityPrediction = RipperDocGameController.GetApproximateTutorialCapacity( this, maxAvailableQuality, hasSmartLink );
		availableCapacity = ( statsSystem.GetStatValue( GetEntityID(), gamedataStatType.HumanityAvailable ) - statsSystem.GetStatValue( GetEntityID(), gamedataStatType.HumanityOverallocated ) ) - tutorialCapacityPrediction;
		beforeVikVisit = GameInstance.GetQuestsSystem( GetGame() ).GetFact( 'q001_ripperdoc_done' ) < 1;
		ts.GetItemListByTag( this, 'Cyberware', itemList );
		GetGlitchedEquippedCyberware( equippedCW );
		for( i = 0; i < equippedCW.Size(); i += 1 )
		{
			cwRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( equippedCW[ i ] ) );
			counterPartRecord = cwRecord.CounterpartHandle();
			counterPartID = counterPartRecord.GetRecordID();
			counterPartRecord = GetAppropriateCWByQuality( maxAvailableQualityInt, counterPartID );
			if( !( TDBID.IsValid( counterPartRecord.GetRecordID() ) ) )
			{
				continue;
			}
			equipmentArea = counterPartRecord.EquipArea().Type();
			if( !( PrioritizeCyberwareOnRetrofix( ItemID.FromTDBID( counterPartRecord.GetRecordID() ), equipmentArea, firstPriorityCW, secondPriorityCW, thirdPriorityCW, beforeVikVisit ) ) )
			{
				ts.GiveItemByTDBID( this, counterPartRecord.GetRecordID(), 1 );
			}
		}
		for( i = 0; i < itemList.Size(); i += 1 )
		{
			itemID = itemList[ i ].GetID();
			cwRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) );
			if( cwRecord.Deprecated() )
			{
				if( equipmentData.IsEquipped( itemID ) )
				{
					RemoveCyberwareParts( itemID );
				}
				ts.RemoveItem( this, itemID, itemList[ i ].GetQuantity() );
				continue;
			}
			counterPartRecord = cwRecord.CounterpartHandle();
			if( counterPartRecord )
			{
				counterPartRecord = GetAppropriateCWByQuality( maxAvailableQualityInt, counterPartRecord.GetRecordID() );
				counterPartID = counterPartRecord.GetRecordID();
				if( !( TDBID.IsValid( counterPartID ) ) )
				{
					RemoveCyberwareParts( itemID );
					ts.RemoveItem( this, itemID, itemList[ i ].GetQuantity() );
					continue;
				}
				equipmentArea = counterPartRecord.EquipArea().Type();
				if( !( equipmentData.IsEquipped( itemID ) ) || !( PrioritizeCyberwareOnRetrofix( ItemID.FromTDBID( counterPartID ), equipmentArea, firstPriorityCW, secondPriorityCW, thirdPriorityCW, beforeVikVisit ) ) )
				{
					ts.GiveItemByTDBID( this, counterPartID, itemList[ i ].GetQuantity() );
				}
				if( equipmentData.IsEquipped( itemID ) && EquipmentSystem.IsItemCyberdeck( itemID ) )
				{
					quickhackParts = RemoveCyberwareParts( itemID );
					maxEquippedQuickhackQuality = counterPartRecord.Quality().Value();
				}
				else
				{
					RemoveCyberwareParts( itemID );
				}
				ts.RemoveItem( this, itemID, itemList[ i ].GetQuantity() );
			}
		}
		if( quickhackParts.Size() > 0 )
		{
			for( i = quickhackParts.Size() - 1; i >= 0; i -= 1 )
			{
				if( TweakDBInterface.GetItemRecord( ItemID.GetTDBID( quickhackParts[ i ] ) ).Quality().Value() > maxEquippedQuickhackQuality )
				{
					quickhackParts.Remove( quickhackParts[ i ] );
				}
			}
		}
		if( !( beforeVikVisit ) )
		{
			freeArmorCW.PushBack( ItemID.FromTDBID( GetAppropriateCWByQuality( maxAvailableQualityInt, T"Items.AdvancedBoringPlatingCommon" ).GetRecordID() ) );
			freeArmorCW.PushBack( ItemID.FromTDBID( GetAppropriateCWByQuality( maxAvailableQualityInt, T"Items.AdvancedBionicJointsCommon" ).GetRecordID() ) );
			freeArmorCW.PushBack( ItemID.FromTDBID( GetAppropriateCWByQuality( maxAvailableQualityInt, T"Items.AdvancedChargeSystemCommon" ).GetRecordID() ) );
		}
		availableCapacity -= EquipNewCyberware( firstPriorityCW, availableCapacity, true, quickhackParts );
		availableCapacity -= EquipNewCyberware( secondPriorityCW, availableCapacity, true );
		availableCapacity -= EquipNewCyberware( thirdPriorityCW, availableCapacity, true );
		availableCapacity -= EquipNewCyberware( freeArmorCW, availableCapacity, false );
	}

	private function GetAppropriateCWByQuality( quality : Int32, baseCWRecord : TweakDBID ) : Item_Record
	{
		var itemRecord : Item_Record;
		var currentQuality : Int32;
		itemRecord = TweakDBInterface.GetItemRecord( baseCWRecord );
		currentQuality = itemRecord.Quality().Value();
		if( !( TDBID.IsValid( baseCWRecord ) ) )
		{
			return itemRecord;
		}
		while( currentQuality < quality )
		{
			itemRecord = itemRecord.NextUpgradeItem();
			currentQuality = itemRecord.Quality().Value();
			if( !( itemRecord ) || !( TDBID.IsValid( itemRecord.GetRecordID() ) ) )
			{
				currentQuality = quality + 1;
				break;
			}
		}
		if( !( itemRecord ) || ( currentQuality > quality ) )
		{
			itemRecord = TweakDBInterface.GetItemRecord( baseCWRecord );
		}
		return itemRecord;
	}

	private function EquipNewCyberware( items : array< ItemID >, availableCapacity : Float, addToInventoryIfOverallocated : Bool, optional stashedQuickhacks : array< ItemID > ) : Float
	{
		var ts : TransactionSystem;
		var equipmentSystem : EquipmentSystem;
		var equipRequest : GameplayEquipRequest;
		var i : Int32;
		var itemCapacityRequirement : Float;
		var capacityTaken : Float;
		var tags : array< CName >;
		ts = GameInstance.GetTransactionSystem( GetGame() );
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
		for( i = 0; i < items.Size(); i += 1 )
		{
			itemCapacityRequirement = GetItemCapacityRequirement( items[ i ] );
			if( ( capacityTaken + itemCapacityRequirement ) > availableCapacity )
			{
				if( addToInventoryIfOverallocated )
				{
					ts.GiveItemByTDBID( this, ItemID.GetTDBID( items[ i ] ), 1 );
				}
				continue;
			}
			equipRequest = new GameplayEquipRequest;
			equipRequest.owner = this;
			equipRequest.addToInventory = true;
			equipRequest.blockUpdateWeaponActiveSlots = true;
			equipRequest.itemID = items[ i ];
			tags = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( items[ i ] ) ).Tags();
			if( tags.Contains( 'Cyberdeck' ) && ( stashedQuickhacks.Size() > 0 ) )
			{
				equipRequest.partsToAdd = stashedQuickhacks;
			}
			equipmentSystem.QueueRequest( equipRequest );
			capacityTaken += itemCapacityRequirement;
		}
		return capacityTaken;
	}

	protected event OnRetrofixOverallocatedCyberwareEvent( evt : RetrofixOverallocatedCyberwareEvent )
	{
		var ts : TransactionSystem;
		var equipmentSystem : EquipmentSystem;
		var equipmentData : EquipmentSystemPlayerData;
		var statsSystem : StatsSystem;
		var itemList : array< weak< gameItemData > >;
		var itemID : ItemID;
		var cwRecord : Item_Record;
		var i : Int32;
		var equipmentArea : gamedataEquipmentArea;
		var availableCapacity : Float;
		var firstPriorityCW, secondPriorityCW, thirdPriorityCW : array< ItemID >;
		ts = GameInstance.GetTransactionSystem( GetGame() );
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
		equipmentData = equipmentSystem.GetPlayerData( this );
		statsSystem = GameInstance.GetStatsSystem( GetGame() );
		availableCapacity = statsSystem.GetStatValue( GetEntityID(), gamedataStatType.HumanityAvailable ) - statsSystem.GetStatValue( GetEntityID(), gamedataStatType.HumanityOverallocated );
		if( availableCapacity < 0.0 )
		{
			ts.GetItemListByTag( this, 'Cyberware', itemList );
			for( i = 0; i < itemList.Size(); i += 1 )
			{
				itemID = itemList[ i ].GetID();
				if( !( equipmentData.IsEquipped( itemID ) ) )
				{
					continue;
				}
				cwRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) );
				equipmentArea = cwRecord.EquipArea().Type();
				if( ( ( ( equipmentArea == gamedataEquipmentArea.RightArm || equipmentArea == gamedataEquipmentArea.EyesCW ) || equipmentArea == gamedataEquipmentArea.SystemReplacementCW ) || equipmentArea == gamedataEquipmentArea.ArmsCW ) || equipmentArea == gamedataEquipmentArea.HandsCW )
				{
					continue;
				}
				else if( equipmentArea == gamedataEquipmentArea.LegsCW )
				{
					secondPriorityCW.PushBack( itemID );
				}
				else
				{
					thirdPriorityCW.PushBack( itemID );
				}
			}
			availableCapacity += UnequipOverallocatedCyberware( thirdPriorityCW, availableCapacity );
			if( availableCapacity < 0.0 )
			{
				availableCapacity += UnequipOverallocatedCyberware( secondPriorityCW, availableCapacity );
			}
			if( availableCapacity < 0.0 )
			{
				availableCapacity += UnequipOverallocatedCyberware( firstPriorityCW, availableCapacity );
			}
		}
	}

	private function UnequipOverallocatedCyberware( items : array< ItemID >, availableCapacity : Float ) : Float
	{
		var equipmentSystem : EquipmentSystem;
		var unequipRequest : UnequipItemsRequest;
		var i : Int32;
		var itemCapacityRequirement : Float;
		var capacityFreed : Float;
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
		unequipRequest = new UnequipItemsRequest;
		unequipRequest.owner = this;
		for( i = 0; i < items.Size(); i += 1 )
		{
			itemCapacityRequirement = GetItemCapacityRequirement( items[ i ] );
			unequipRequest.items.PushBack( items[ i ] );
			capacityFreed += itemCapacityRequirement;
			if( ( capacityFreed + availableCapacity ) >= 0.0 )
			{
				break;
			}
		}
		equipmentSystem.QueueRequest( unequipRequest );
		return capacityFreed;
	}

	public function UpdateInventoryWeight( weightChange : Float, optional isLootBroken : Bool )
	{
		if( weightChange == 0.0 )
		{
			return;
		}
		m_curInventoryWeight += weightChange;
		EvaluateEncumbrance( isLootBroken );
	}

	protected event OnItemBeingRemovedFromInventory( evt : ItemBeingRemovedEvent )
	{
		var dps : DropPointSystem;
		var dpsRequest : DropPointRequest;
		var unequipRequest : UnequipItemsRequest;
		var equipData : EquipmentSystemPlayerData;
		var itemName : String;
		var itemRecord : Item_Record;
		var equipmentSystem : EquipmentSystem;
		dpsRequest = new DropPointRequest;
		dpsRequest.CreateRequest( ItemID.GetTDBID( evt.itemID ), DropPointPackageStatus.COLLECTED );
		if( !( RPGManager.IsItemSingleInstance( evt.itemData ) ) )
		{
			UpdateInventoryWeight( -1.0 * RPGManager.GetItemWeight( evt.itemData ) );
		}
		if( evt.itemData )
		{
			if( ( ( !( evt.itemData.HasTag( 'SkipActivityLog' ) ) && !( evt.itemData.HasTag( 'SkipActivityLogOnRemove' ) ) ) && !( ItemID.HasFlag( evt.itemData.GetID(), gameEItemIDFlag.Preview ) ) ) && !( RPGManager.IsItemBroken( evt.itemData ) ) )
			{
				itemRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( evt.itemID ) );
				itemName = UIItemsHelper.GetItemName( itemRecord, evt.itemData );
				GameInstance.GetActivityLogSystem( GetGame() ).AddLog( ( GetLocalizedText( "LocKey#26163" ) + ": " ) + itemName );
			}
		}
		dps = ( ( DropPointSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'DropPointSystem' ) ) );
		if( dps )
		{
			dps.QueueRequest( dpsRequest );
		}
		SendCheckRemovedItemWithSlotActiveItemRequest( evt.itemID );
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
		if( equipmentSystem )
		{
			if( equipmentSystem.IsItemInHotkey( this, evt.itemID ) )
			{
			}
			equipData = equipmentSystem.GetPlayerData( this );
			if( equipData.IsEquipped( evt.itemID ) )
			{
				unequipRequest = new UnequipItemsRequest;
				unequipRequest.owner = this;
				unequipRequest.items.PushBack( evt.itemID );
				EquipmentSystem.GetInstance( this ).QueueRequest( unequipRequest );
			}
		}
	}

	protected event OnInventoryEmpty( evt : OnInventoryEmptyEvent )
	{
		m_curInventoryWeight = 0.0;
		EvaluateEncumbrance();
	}

	public function EvaluateEncumbrance( optional isLootBroken : Bool )
	{
		var ses : StatusEffectSystem;
		var carryCapacity : Float;
		var overweightEffectID : TweakDBID;
		var isApplyingRestricted : Bool;
		var hasEncumbranceEffect : Bool;
		if( m_curInventoryWeight < 0.0 )
		{
			m_curInventoryWeight = 0.0;
		}
		ses = GameInstance.GetStatusEffectSystem( GetGame() );
		overweightEffectID = T"BaseStatusEffect.Encumbered";
		hasEncumbranceEffect = ses.HasStatusEffect( GetEntityID(), overweightEffectID );
		isApplyingRestricted = StatusEffectSystem.ObjectHasStatusEffectWithTag( this, 'NoEncumbrance' );
		carryCapacity = GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.CarryCapacity );
		if( ( ( m_curInventoryWeight > carryCapacity ) && !( isApplyingRestricted ) ) && !( isLootBroken ) )
		{
			SetWarningMessage( GetLocalizedText( "UI-Notifications-Overburden" ) );
		}
		if( ( ( ( m_curInventoryWeight > carryCapacity ) && !( hasEncumbranceEffect ) ) && !( isApplyingRestricted ) ) && !( isLootBroken ) )
		{
			ses.ApplyStatusEffect( GetEntityID(), overweightEffectID );
		}
		else if( ( ( m_curInventoryWeight <= carryCapacity ) && hasEncumbranceEffect ) || ( hasEncumbranceEffect && isApplyingRestricted ) )
		{
			ses.RemoveStatusEffect( GetEntityID(), overweightEffectID );
		}
		GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_PlayerStats ).SetFloat( GetAllBlackboardDefs().UI_PlayerStats.currentInventoryWeight, m_curInventoryWeight, true );
	}

	private function CalculateEncumbrance()
	{
		var i : Int32;
		var items : array< weak< gameItemData > >;
		var TS : TransactionSystem;
		TS = GameInstance.GetTransactionSystem( GetGame() );
		TS.GetItemList( this, items );
		for( i = 0; i < items.Size(); i += 1 )
		{
			if( !( ItemID.HasFlag( items[ i ].GetID(), gameEItemIDFlag.Preview ) ) )
			{
				m_curInventoryWeight += RPGManager.GetItemStackWeight( this, items[ i ] );
			}
		}
	}

	protected event OnEvaluateEncumbranceEvent( evt : EvaluateEncumbranceEvent )
	{
		EvaluateEncumbrance();
	}

	private function SendCheckRemovedItemWithSlotActiveItemRequest( item : ItemID )
	{
		var request : CheckRemovedItemWithSlotActiveItem;
		request = new CheckRemovedItemWithSlotActiveItem;
		request.itemID = item;
		request.owner = this;
		GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ).QueueRequest( request );
	}

	protected event OnTakedownOrder( evt : OrderTakedownEvent )
	{
		var takedownCommand : AIFollowerTakedownCommand;
		var squadInterface : PlayerSquadInterface;
		takedownCommand = new AIFollowerTakedownCommand;
		takedownCommand.combatCommand = true;
		takedownCommand.target = evt.target;
		if( AISquadHelper.GetPlayerSquadInterface( this, squadInterface ) )
		{
			squadInterface.BroadcastCommand( takedownCommand );
		}
	}

	protected event OnSpiderbotOrderTargetEvent( evt : SpiderbotOrderDeviceEvent )
	{
		var deviceCommand : AIFollowerDeviceCommand;
		var squadInterface : PlayerSquadInterface;
		deviceCommand = new AIFollowerDeviceCommand;
		deviceCommand.target = evt.target;
		deviceCommand.overrideMovementTarget = evt.overrideMovementTarget;
		if( AISquadHelper.GetPlayerSquadInterface( this, squadInterface ) )
		{
			squadInterface.BroadcastCommand( deviceCommand );
		}
	}

	private override final function OnHitBlockedOrDeflected( hitEvent : gameHitEvent )
	{
		if( hitEvent.attackData.WasDeflectedAny() )
		{
			PushDeflectEvent( hitEvent.attackData.WasBulletDeflected() );
			GameObject.PlayVoiceOver( this, 'meleeDeflect', 'Scripts:OnHitBlockedOrDeflected' );
			GameInstance.GetTelemetrySystem( GetGame() ).LogHitDefense( telemetryHitDefenseType.Deflect );
		}
		else if( hitEvent.attackData.WasBlocked() )
		{
			PushDeflectEvent( hitEvent.attackData.WasBulletBlocked() );
			GameInstance.GetTelemetrySystem( GetGame() ).LogHitDefense( telemetryHitDefenseType.Block );
		}
	}

	private function PushDeflectEvent( isBulletDeflect : Bool )
	{
		if( isBulletDeflect )
		{
			if( !( m_waitingForDelayEvent ) )
			{
				GameInstance.GetDelaySystem( GetGame() ).DelayEvent( this, new KatanaMagFieldHitDelayEvent, 0.40000001 );
				m_waitingForDelayEvent = true;
				AnimationControllerComponent.PushEvent( this, 'MeleeBulletDeflect' );
			}
		}
		else
		{
			AnimationControllerComponent.PushEvent( this, 'MeleeBlock' );
		}
	}

	protected event OnKatanaMagFieldHitDelayEvent( evt : KatanaMagFieldHitDelayEvent )
	{
		m_waitingForDelayEvent = false;
	}

	private override final function OnHitAnimation( hitEvent : gameHitEvent )
	{
		var remoteControlledVehicle : weak< VehicleObject >;
		var playerVehicle : weak< VehicleObject >;
		var vehicleID : EntityID;
		var playerStateMachineBlackboard : IBlackboard;
		super.OnHitAnimation( hitEvent );
		GetDamageThresholdParams();
		PushHitDataToGraph( hitEvent );
		AddOnHitRumble( hitEvent );
		if( GetZoomBlackboardValues() )
		{
			SetZoomBlackboardValues( false );
		}
		TakeOverControlSystem.ReleaseControlOnHit( this );
		playerStateMachineBlackboard = GameInstance.GetBlackboardSystem( GetGame() ).GetLocalInstanced( GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		vehicleID = playerStateMachineBlackboard.GetEntityID( GetAllBlackboardDefs().PlayerStateMachine.EntityIDVehicleRemoteControlled );
		if( EntityID.IsDefined( vehicleID ) )
		{
			if( VehicleComponent.GetVehicleFromID( GetGame(), vehicleID, remoteControlledVehicle ) && !( VehicleComponent.GetVehicle( GetGame(), GetEntityID(), playerVehicle ) ) )
			{
				remoteControlledVehicle.SetVehicleRemoteControlled( false, false, true );
			}
		}
	}

	private function AddOnHitRumble( hitEvent : gameHitEvent )
	{
		var direction : Int32;
		var soundName : CName;
		var damageDealt : Float;
		var totalHealth : Float;
		var thresholdMed : Float;
		var thresholdHigh : Float;
		var rumbleIntensityPrefix : String;
		var rumbleDirectionSuffix : String;
		var rumbleDuration : String;
		rumbleIntensityPrefix = "light_";
		rumbleDirectionSuffix = "";
		rumbleDuration = "pulse";
		damageDealt = hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health );
		totalHealth = GameInstance.GetStatPoolsSystem( GetGame() ).GetStatPoolMaxPointValue( GetEntityID(), gamedataStatPoolType.Health );
		if( damageDealt < 1.0 )
		{
			return;
		}
		if( AttackData.IsDoT( hitEvent.attackData ) )
		{
			soundName = TDB.GetCName( T"rumble.local.medium_slow" );
			PlaySound( this, soundName );
			return;
		}
		damageDealt = damageDealt / totalHealth;
		if( AttackData.IsMelee( hitEvent.attackData.GetAttackType() ) )
		{
			thresholdMed = TDB.GetFloat( T"player.onHitRumble.meleeMedIntensityThreshold" );
			thresholdHigh = TDB.GetFloat( T"player.onHitRumble.meleeHighIntensityThreshold" );
			rumbleDuration = "slow";
		}
		else
		{
			thresholdMed = TDB.GetFloat( T"player.onHitRumble.medIntensityThreshold" );
			thresholdHigh = TDB.GetFloat( T"player.onHitRumble.highIntensityThreshold" );
		}
		if( damageDealt >= thresholdHigh )
		{
			rumbleIntensityPrefix = "heavy_";
		}
		else if( damageDealt >= thresholdMed )
		{
			rumbleIntensityPrefix = "medium_";
		}
		direction = GameObject.GetAttackAngleInInt( hitEvent );
		if( direction == 1 )
		{
			rumbleDirectionSuffix = "_left";
		}
		else if( direction == 3 )
		{
			rumbleDirectionSuffix = "_right";
		}
		soundName = TDB.GetCName( TDBID.Create( ( ( "rumble.local." + rumbleIntensityPrefix ) + rumbleDuration ) + rumbleDirectionSuffix ) );
		PlaySound( this, soundName );
	}

	private function PushHitDataToGraph( hitEvent : gameHitEvent )
	{
		var useMinMaxRangeValues : Bool;
		var minShakeStrength : Float;
		var damageDealt : Float;
		var shakeStrength : Float;
		var enableOnHitCameraShake : Bool;
		var totalHealth : Float;
		var attackSource : weak< GameObject >;
		var minShakeStrengthString : String;
		var maxShakeStrengthString : String;
		enableOnHitCameraShake = TDB.GetBool( T"player.cameraShake.enableOnHitCameraShake", true );
		useMinMaxRangeValues = TDB.GetBool( T"player.cameraShake.useMinMaxRangeValues", true );
		minShakeStrengthString = "player.cameraShake.";
		maxShakeStrengthString = "player.cameraShake.";
		damageDealt = hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health );
		totalHealth = GameInstance.GetStatPoolsSystem( GetGame() ).GetStatPoolValue( GetEntityID(), gamedataStatPoolType.Health );
		damageDealt = damageDealt / totalHealth;
		attackSource = hitEvent.attackData.GetSource();
		if( damageDealt <= 0.0 )
		{
			return;
		}
		if( enableOnHitCameraShake )
		{
			if( ( ( SecurityTurret )( attackSource ) ) )
			{
				minShakeStrengthString = minShakeStrengthString + "defaultMedMin";
				maxShakeStrengthString = maxShakeStrengthString + "defaultMedMax";
			}
			else if( AttackData.IsDoT( hitEvent.attackData ) && StatusEffectSystem.ObjectHasStatusEffect( hitEvent.target, T"BaseStatusEffect.OutOfOxygen" ) )
			{
				minShakeStrengthString = minShakeStrengthString + "outOfOxyMin";
				maxShakeStrengthString = maxShakeStrengthString + "outOfOxyMax";
			}
			else if( AttackData.IsExplosion( hitEvent.attackData.GetAttackType() ) )
			{
				if( damageDealt >= m_explosionHighDamageThreshold )
				{
					minShakeStrengthString = minShakeStrengthString + "explosionHighMin";
					maxShakeStrengthString = maxShakeStrengthString + "explosionHighMax";
				}
				else if( damageDealt >= m_explosionMedDamageThreshold )
				{
					minShakeStrengthString = minShakeStrengthString + "explosionMedMin";
					maxShakeStrengthString = maxShakeStrengthString + "explosionMedMax";
				}
				else if( damageDealt >= m_explosionLowDamageThreshold )
				{
					minShakeStrengthString = minShakeStrengthString + "explosionLowMin";
					maxShakeStrengthString = maxShakeStrengthString + "explosionLowMax";
				}
			}
			else if( AttackData.IsMelee( hitEvent.attackData.GetAttackType() ) )
			{
				if( damageDealt >= m_meleeHighDamageThreshold )
				{
					minShakeStrengthString = minShakeStrengthString + "meleeHighMin";
					maxShakeStrengthString = maxShakeStrengthString + "meleeHighMax";
				}
				else if( damageDealt >= m_meleeMedDamageThreshold )
				{
					minShakeStrengthString = minShakeStrengthString + "meleeMedMin";
					maxShakeStrengthString = maxShakeStrengthString + "meleeMedMax";
				}
				else if( damageDealt >= m_meleeLowDamageThreshold )
				{
					minShakeStrengthString = minShakeStrengthString + "meleeLowMin";
					maxShakeStrengthString = maxShakeStrengthString + "meleeLowMax";
				}
			}
			else if( damageDealt >= m_highDamageThreshold )
			{
				minShakeStrengthString = minShakeStrengthString + "defaultLowMin";
				maxShakeStrengthString = maxShakeStrengthString + "defaultLowMax";
			}
			else if( damageDealt >= m_medDamageThreshold )
			{
				minShakeStrengthString = minShakeStrengthString + "defaultMedMin";
				maxShakeStrengthString = maxShakeStrengthString + "defaultMedMax";
			}
			else if( damageDealt >= m_lowDamageThreshold )
			{
				minShakeStrengthString = minShakeStrengthString + "defaultHighMin";
				maxShakeStrengthString = maxShakeStrengthString + "defaultHighMax";
			}
			minShakeStrength = TDB.GetFloat( TDBID.Create( minShakeStrengthString ), 0.0 );
			shakeStrength = TDB.GetFloat( TDBID.Create( maxShakeStrengthString ), 0.0 );
		}
		else
		{
			shakeStrength = 0.0;
		}
		if( useMinMaxRangeValues )
		{
			shakeStrength = RandRangeF( minShakeStrength, shakeStrength );
		}
		SendCameraShakeDataToGraph( hitEvent, shakeStrength );
	}

	protected event OnCameraShakeEvent( evt : gameCameraShakeEvent )
	{
		SendCameraShakeDataToGraph( , evt.shakeStrength );
	}

	public function SendCameraShakeDataToGraph( optional hitEvent : gameHitEvent, shakeStrength : Float )
	{
		var animFeature : AnimFeature_PlayerHitReactionData;
		var attackType : gamedataAttackType;
		var attackRecord : Attack_Melee_Record;
		animFeature = new AnimFeature_PlayerHitReactionData;
		attackType = hitEvent.attackData.GetAttackType();
		animFeature.isMeleeHit = AttackData.IsMelee( attackType );
		animFeature.isLightMeleeHit = AttackData.IsLightMelee( attackType );
		animFeature.isStrongMeleeHit = AttackData.IsStrongMelee( attackType );
		animFeature.isQuickMeleeHit = AttackData.IsQuickMelee( attackType );
		animFeature.isExplosion = AttackData.IsExplosion( attackType );
		animFeature.isPressureWave = AttackData.IsPressureWave( attackType );
		if( animFeature.isMeleeHit )
		{
			attackRecord = ( ( Attack_Melee_Record )( hitEvent.attackData.GetAttackDefinition().GetRecord() ) );
			animFeature.meleeAttackDirection = ( ( Int32 )( attackRecord.AttackDirection().Direction().Type() ) );
		}
		animFeature.hitDirection = GameObject.GetAttackAngleInFloat( hitEvent );
		animFeature.hitStrength = shakeStrength;
		AnimationControllerComponent.ApplyFeature( this, 'HitReactionData', animFeature );
		AnimationControllerComponent.PushEvent( this, 'Hit' );
	}

	private override final function OnHitUI( hitEvent : gameHitEvent )
	{
		var i : Int32;
		var dmgType : gamedataDamageType;
		var effName : CName;
		var attackValues : array< Float >;
		attackValues = hitEvent.attackComputed.GetAttackValues();
		for( i = 0; i < attackValues.Size(); i += 1 )
		{
			if( attackValues[ i ] > 0.0 )
			{
				dmgType = ( ( gamedataDamageType )( i ) );
				break;
			}
		}
		switch( dmgType )
		{
			case gamedataDamageType.Thermal:
				effName = 'fire_damage_indicator';
			break;
			case gamedataDamageType.Electric:
				effName = 'emp_damage_indicator';
			break;
			default:
				break;
		}
		if( AttackData.IsDoT( hitEvent.attackData ) )
		{
			if( hitEvent.attackData.GetAttackDefinition().GetRecord().GetID() == T"Attacks.OutOfOxygenDamageOverTime" )
			{
				effName = 'status_pain';
			}
		}
		GameObjectEffectHelper.StartEffectEvent( this, effName );
	}

	private override final function OnHitSounds( hitEvent : gameHitEvent )
	{
		var soundEvent : SoundPlayEvent;
		var damageSwitch : SoundSwitchEvent;
		var damageTypeSwitch : SoundSwitchEvent;
		var soundParamAxisX : SoundParameterEvent;
		var soundParamAxisY : SoundParameterEvent;
		var hitDirection : Vector4;
		var target : GameObject;
		var forwardLocalToWorldAngle : Float;
		var damageValue : Float;
		var playerOutOfOxygen : Bool;
		var totalHealth : Float;
		var damagePercentage : Float;
		super.OnHitSounds( hitEvent );
		playerOutOfOxygen = hitEvent.attackData.GetAttackDefinition().GetRecord().GetID() == T"Attacks.OutOfOxygenDamageOverTime";
		if( playerOutOfOxygen )
		{
			return;
		}
		soundEvent = new SoundPlayEvent;
		damageSwitch = new SoundSwitchEvent;
		damageTypeSwitch = new SoundSwitchEvent;
		soundParamAxisX = new SoundParameterEvent;
		soundParamAxisY = new SoundParameterEvent;
		target = hitEvent.target;
		forwardLocalToWorldAngle = Vector4.Heading( target.GetWorldForward() );
		hitDirection = Vector4.RotByAngleXY( hitEvent.hitDirection, forwardLocalToWorldAngle );
		soundParamAxisX.parameterName = 'RTPC_Positioning_2D_LR_axis';
		soundParamAxisX.parameterValue = hitDirection.X * 100.0;
		soundParamAxisY.parameterName = 'RTPC_Positioning_2D_FB_axis';
		soundParamAxisY.parameterValue = hitDirection.Y * 100.0;
		target.QueueEvent( soundParamAxisX );
		target.QueueEvent( soundParamAxisY );
		damageSwitch.switchName = 'SW_Impact_Velocity';
		damageValue = hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health );
		damageTypeSwitch.switchName = 'w_feedback_player_damage_type';
		if( hitEvent.attackData.GetAttackType() == gamedataAttackType.PressureWave )
		{
			damageTypeSwitch.switchValue = 'falling';
		}
		else if( hitEvent.attackData.GetAttackType() == gamedataAttackType.Ranged )
		{
			damageTypeSwitch.switchValue = 'bullet';
		}
		else
		{
			damageTypeSwitch.switchValue = 'generic';
		}
		target.QueueEvent( damageTypeSwitch );
		if( damageValue <= 0.0 )
		{
			return;
		}
		else if( damageValue >= m_highDamageThreshold )
		{
			damageSwitch.switchValue = 'SW_Impact_Velocity_Hi';
		}
		else if( damageValue >= m_medDamageThreshold )
		{
			damageSwitch.switchValue = 'SW_Impact_Velocity_Med';
		}
		else if( damageValue >= m_lowDamageThreshold )
		{
			damageSwitch.switchValue = 'SW_Impact_Velocity_Low';
		}
		target.QueueEvent( damageSwitch );
		GameObject.PlayVoiceOver( this, 'onPlayerHit', 'Scripts:OnHitSounds' );
		if( !( hitEvent.attackData.GetWeapon().GetItemData().HasTag( WeaponObject.GetMeleeWeaponTag() ) ) )
		{
			totalHealth = GameInstance.GetStatPoolsSystem( GetGame() ).GetStatPoolMaxPointValue( GetEntityID(), gamedataStatPoolType.Health );
			damagePercentage = ( ClampF( damageValue, 0.0, totalHealth ) / totalHealth ) * 100.0;
			GameInstance.GetAudioSystem( GetGame() ).GlobalParameter( 'w_feedback_player_damage', damagePercentage );
			soundEvent.soundName = 'w_feedback_player_damage';
			target.QueueEvent( soundEvent );
		}
		if( ( IsClient() && IsControlledByLocalPeer() ) && ( GameInstance.GetStatPoolsSystem( GetGame() ).GetStatPoolValue( GetEntityID(), gamedataStatPoolType.CPO_Armor ) == 0.0 ) )
		{
			soundEvent.soundName = 'test_ad_emitter_2_1';
			target.QueueEvent( soundEvent );
		}
	}

	protected event OnDamageInflicted( evt : DamageInflictedEvent )
	{
		m_lastDmgInflicted = GameInstance.GetSimTime( GetGame() );
	}

	public function GetLastDamageInflictedTime() : EngineTime
	{
		return m_lastDmgInflicted;
	}

	protected event OnInteraction( choiceEvent : InteractionChoiceEvent )
	{
		var choice : String;
		var currentDataOwner : PlayerPuppet;
		var receivingData : PlayerPuppet;
		var clearDataEvent : CPOMissionDataTransferred;
		var transferDataEvent : CPOMissionDataTransferred;
		choice = choiceEvent.choice.choiceMetaData.tweakDBName;
		if( choice == "TakeCPOMissionDataFromPlayer" )
		{
			currentDataOwner = ( ( PlayerPuppet )( choiceEvent.hotspot ) );
			receivingData = ( ( PlayerPuppet )( choiceEvent.activator ) );
			clearDataEvent = new CPOMissionDataTransferred;
			clearDataEvent.dataDownloaded = false;
			clearDataEvent.isChoiceToken = currentDataOwner.m_CPOMissionDataState.m_isChoiceToken;
			currentDataOwner.QueueEvent( clearDataEvent );
			transferDataEvent = new CPOMissionDataTransferred;
			transferDataEvent.dataDownloaded = true;
			transferDataEvent.compatibleDeviceName = currentDataOwner.m_CPOMissionDataState.m_compatibleDeviceName;
			transferDataEvent.dataDamagesPresetName = currentDataOwner.m_CPOMissionDataState.m_CPOMissionDataDamagesPreset;
			transferDataEvent.ownerDecidesOnTransfer = currentDataOwner.m_CPOMissionDataState.m_ownerDecidesOnTransfer;
			transferDataEvent.isChoiceToken = currentDataOwner.m_CPOMissionDataState.m_isChoiceToken;
			transferDataEvent.choiceTokenTimeout = currentDataOwner.m_CPOMissionDataState.m_choiceTokenTimeout;
			receivingData.QueueEvent( transferDataEvent );
		}
		else if( choice == "GiveCPOMissionDataToPlayer" )
		{
			receivingData = ( ( PlayerPuppet )( choiceEvent.hotspot ) );
			currentDataOwner = ( ( PlayerPuppet )( choiceEvent.activator ) );
			clearDataEvent = new CPOMissionDataTransferred;
			clearDataEvent.dataDownloaded = false;
			clearDataEvent.isChoiceToken = currentDataOwner.m_CPOMissionDataState.m_isChoiceToken;
			currentDataOwner.QueueEvent( clearDataEvent );
			transferDataEvent = new CPOMissionDataTransferred;
			transferDataEvent.dataDownloaded = true;
			transferDataEvent.compatibleDeviceName = currentDataOwner.m_CPOMissionDataState.m_compatibleDeviceName;
			transferDataEvent.dataDamagesPresetName = currentDataOwner.m_CPOMissionDataState.m_CPOMissionDataDamagesPreset;
			transferDataEvent.ownerDecidesOnTransfer = currentDataOwner.m_CPOMissionDataState.m_ownerDecidesOnTransfer;
			transferDataEvent.isChoiceToken = currentDataOwner.m_CPOMissionDataState.m_isChoiceToken;
			transferDataEvent.choiceTokenTimeout = currentDataOwner.m_CPOMissionDataState.m_choiceTokenTimeout;
			receivingData.QueueEvent( transferDataEvent );
		}
		super.OnInteraction( choiceEvent );
	}

	protected event OnTogglePlayerFlashlightEvent( evt : TogglePlayerFlashlightEvent )
	{
		var comp : weak< IComponent >;
		comp = FindComponentByName( 'TEMP_flashlight' );
		if( comp )
		{
			comp.Toggle( evt.enable );
		}
	}

	protected event OnToggleNewPlayerFlashlightEvent( evt : ToggleNewPlayerFlashlightEvent )
	{
		var comp : weak< gameLightComponent >;
		comp = ( ( gameLightComponent )( FindComponentByName( 'Flashlight' ) ) );
		if( comp )
		{
			comp.ToggleLight( !( comp.IsOn() ) );
		}
	}

	private function InitializeTweakDBRecords()
	{
		m_coverRecordID = T"Character.Player_Cover_Modifier";
		m_damageReductionRecordID = T"Character.Player_Workspot_DamageReduction_Modifier";
		m_visReductionRecordID = T"Character.Player_Workspot_VisibilityReduction_Modifier";
	}

	protected function DefineModifierGroups()
	{
		var statsSystem : StatsSystem;
		statsSystem = GameInstance.GetStatsSystem( GetGame() );
		statsSystem.DefineModifierGroupFromRecord( TDBID.ToNumber( m_damageReductionRecordID ), m_damageReductionRecordID );
		statsSystem.DefineModifierGroupFromRecord( TDBID.ToNumber( m_visReductionRecordID ), m_visReductionRecordID );
		statsSystem.DefineModifierGroupFromRecord( TDBID.ToNumber( m_coverRecordID ), m_coverRecordID );
	}

	protected function RegisterStatListeners( self : PlayerPuppet )
	{
		var statsSystem : StatsSystem;
		var statPoolsSystem : StatPoolsSystem;
		var entityID : EntityID;
		statsSystem = GameInstance.GetStatsSystem( GetGame() );
		statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
		entityID = GetEntityID();
		m_visibilityListener = new VisibilityStatListener;
		m_visibilityListener.SetStatType( gamedataStatType.Visibility );
		m_visibilityListener.m_owner = ( ( GameObject )( self ) );
		statsSystem.RegisterListener( entityID, m_visibilityListener );
		m_secondHeartListener = new SecondHeartStatListener;
		m_secondHeartListener.SetStatType( gamedataStatType.HasSecondHeart );
		m_secondHeartListener.m_player = self;
		statsSystem.RegisterListener( entityID, m_secondHeartListener );
		m_healthStatListener = new HealthStatListener;
		m_healthStatListener.m_ownerPuppet = self;
		statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.Health, m_healthStatListener );
		m_oxygenStatListener = new OxygenStatListener;
		m_oxygenStatListener.SetValue( TDB.GetFloat( T"player.oxygenThresholds.critOxygenThreshold", 10.0 ) );
		m_oxygenStatListener.m_ownerPuppet = self;
		statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.Oxygen, m_oxygenStatListener );
		m_autoRevealListener = new AutoRevealStatListener;
		m_autoRevealListener.SetStatType( gamedataStatType.AutoReveal );
		m_autoRevealListener.m_owner = self;
		statsSystem.RegisterListener( entityID, m_autoRevealListener );
		m_aimAssistListener = new AimAssistSettingsListener;
		m_aimAssistListener.Initialize( self );
		m_accessibilityControlsListener = new AccessibilityControlsListener;
		m_accessibilityControlsListener.Initialize( self );
		if( IsControlledByLocalPeer() )
		{
			m_armorStatListener = new ArmorStatListener;
			m_armorStatListener.m_ownerPuppet = self;
			statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.CPO_Armor, m_armorStatListener );
		}
		m_memoryListener = new MemoryListener;
		m_memoryListener.m_player = this;
		statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.Memory, m_memoryListener );
		statPoolsSystem.RequestMarkingStatPoolActive( entityID, gamedataStatPoolType.Memory );
		m_staminaListener = new StaminaListener;
		m_staminaListener.Init( this );
		statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.Stamina, m_staminaListener );
		m_HealingItemsChargeStatListener = new HealingItemsChargeStatListener;
		m_HealingItemsChargeStatListener.Init( this );
		statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.HealingItemsCharges, m_HealingItemsChargeStatListener );
		m_GrenadesChargeStatListener = new GrenadesChargeStatListener;
		m_GrenadesChargeStatListener.Init( this );
		statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.GrenadesCharges, m_GrenadesChargeStatListener );
		m_ProjectileLauncherChargeStatListener = new ProjectileLauncherChargeStatListener;
		m_ProjectileLauncherChargeStatListener.Init( this );
		statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.ProjectileLauncherCharges, m_ProjectileLauncherChargeStatListener );
		m_OpticalCamoChargeStatListener = new OpticalCamoChargeStatListener;
		m_OpticalCamoChargeStatListener.Init( this );
		statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.OpticalCamoCharges, m_OpticalCamoChargeStatListener );
		m_OverclockChargeListener = new OverclockChargeListener;
		m_OverclockChargeListener.Init( this );
		statPoolsSystem.RequestRegisteringListener( entityID, gamedataStatPoolType.CyberdeckOverclock, m_OverclockChargeListener );
	}

	protected function UnregisterStatListeners( self : PlayerPuppet )
	{
		var statsSystem : StatsSystem;
		var statPoolsSystem : StatPoolsSystem;
		var entityID : EntityID;
		statsSystem = GameInstance.GetStatsSystem( GetGame() );
		statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
		entityID = GetEntityID();
		statsSystem.UnregisterListener( entityID, m_visibilityListener );
		m_visibilityListener = NULL;
		statsSystem.UnregisterListener( entityID, m_secondHeartListener );
		m_secondHeartListener = NULL;
		statsSystem.UnregisterListener( entityID, m_autoRevealListener );
		m_autoRevealListener = NULL;
		if( IsControlledByLocalPeer() )
		{
			statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.CPO_Armor, m_armorStatListener );
			m_armorStatListener = NULL;
		}
		m_aimAssistListener = NULL;
		m_accessibilityControlsListener = NULL;
		statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.Health, m_healthStatListener );
		m_healthStatListener = NULL;
		statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.Oxygen, m_oxygenStatListener );
		m_oxygenStatListener = NULL;
		statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.Memory, m_memoryListener );
		m_memoryListener = NULL;
		statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.Stamina, m_staminaListener );
		m_staminaListener = NULL;
		statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.HealingItemsCharges, m_HealingItemsChargeStatListener );
		m_HealingItemsChargeStatListener = NULL;
		statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.GrenadesCharges, m_GrenadesChargeStatListener );
		m_GrenadesChargeStatListener = NULL;
		statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.ProjectileLauncherCharges, m_ProjectileLauncherChargeStatListener );
		m_ProjectileLauncherChargeStatListener = NULL;
		statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.OpticalCamoCharges, m_OpticalCamoChargeStatListener );
		m_OpticalCamoChargeStatListener = NULL;
		statPoolsSystem.RequestUnregisteringListener( entityID, gamedataStatPoolType.CyberdeckOverclock, m_OverclockChargeListener );
		m_OverclockChargeListener = NULL;
	}

	protected event OnCleanUpTimeDilationEvent( evt : CleanUpTimeDilationEvent )
	{
		var timeSystem : TimeSystem;
		var reason : CName;
		if( evt.reason == 'focusMode' && ( GetPlayerStateMachineBlackboard().GetInt( GetAllBlackboardDefs().PlayerStateMachine.Vision ) == ( ( Int32 )( gamePSMVision.Focus ) ) ) )
		{
			return 0;
		}
		timeSystem = GameInstance.GetTimeSystem( this.GetGame() );
		timeSystem.UnsetTimeDilation( evt.reason, '' );
		timeSystem.SetTimeDilationOnLocalPlayerZero( reason, 1.0, 0.1, '', '' );
		timeSystem.SetIgnoreTimeDilationOnLocalPlayerZero( false );
		timeSystem.UnsetTimeDilationOnLocalPlayerZero( reason, '' );
	}

	protected event OnHealthUpdateEvent( evt : HealthUpdateEvent )
	{
		UpdateHealthStateSFX( evt.value );
		UpdateHealthStateVFX( evt.value );
		m_lastHealthUpdate = evt.value;
		GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_PlayerStats ).SetInt( GetAllBlackboardDefs().UI_PlayerStats.CurrentHealth, ( ( Int32 )( evt.value ) ), true );
	}

	private function UpdateHealthStateSFX( healthValue : Float )
	{
		var lowHealthThreshold : Float;
		var critHealthThreshold : Float;
		lowHealthThreshold = GetLowHealthThreshold();
		critHealthThreshold = GetCriticalHealthThreshold();
		GameInstance.GetAudioSystem( GetGame() ).GlobalParameter( 'g_player_health', healthValue );
		if( healthValue > lowHealthThreshold )
		{
			GameInstance.GetAudioSystem( GetGame() ).NotifyGameTone( 'InNormalHealth' );
		}
		else
		{
			GameInstance.GetAudioSystem( GetGame() ).NotifyGameTone( 'InLowHealth' );
			if( healthValue > TDB.GetFloat( T"player.hitVFX.critHealthRumbleEndThreshold", 30.0 ) )
			{
				m_critHealthRumblePlayed = false;
				StopCritHealthRumble();
			}
			if( ( healthValue <= critHealthThreshold ) && ( healthValue > 0.0 ) )
			{
				if( !( m_critHealthRumblePlayed ) )
				{
					m_critHealthRumblePlayed = true;
					PlayCritHealthRumble();
					GameInstance.GetDelaySystem( GetGame() ).CancelDelay( m_critHealthRumbleDurationID );
					m_critHealthRumbleDurationID = GameInstance.GetDelaySystem( GetGame() ).DelayEvent( this, new StopCritHealthRumble, TDB.GetFloat( T"player.hitVFX.critHealthRumbleMaxDuration", 8.0 ) );
				}
			}
		}
	}

	protected event OnStopCritHealthRumble( evt : StopCritHealthRumble )
	{
		StopCritHealthRumble();
	}

	private function PlayCritHealthRumble()
	{
		GameObject.PlaySound( this, TDB.GetCName( T"rumble.local.loop_light" ) );
	}

	private function StopCritHealthRumble()
	{
		GameObject.StopSound( this, TDB.GetCName( T"rumble.local.loop_light" ) );
	}

	private function UpdateHealthStateVFX( healthValue : Float )
	{
		var lowHealthThreshold : Float;
		var critHealthThreshold : Float;
		lowHealthThreshold = GetLowHealthThreshold();
		critHealthThreshold = GetCriticalHealthThreshold();
		if( ( IsClient() && IsControlledByLocalPeer() ) || !( IsMultiplayer() ) )
		{
			if( m_healthVfxBlackboard && ( ( healthValue >= lowHealthThreshold ) || StatusEffectSystem.ObjectHasStatusEffectWithTag( this, 'PreventLowHealthOverlay' ) ) )
			{
				GameObjectEffectHelper.BreakEffectLoopEvent( this, 'fx_health_low' );
				m_healthVfxBlackboard = NULL;
				GameInstance.GetTelemetrySystem( GetGame() ).LogPlayerReachedCriticalHealth( false );
				GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().PhotoMode ).SetUint( GetAllBlackboardDefs().PhotoMode.PlayerHealthState, 0 );
			}
			else if( ( healthValue <= critHealthThreshold ) && TDB.GetBool( T"player.hitVFX.useCriticalThreshold" ) )
			{
				if( !( m_healthVfxBlackboard ) )
				{
					if( !( StatusEffectSystem.ObjectHasStatusEffectWithTag( this, 'PreventLowHealthOverlay' ) ) )
					{
						m_healthVfxBlackboard = new worldEffectBlackboard;
						m_healthVfxBlackboard.SetValue( 'health_state', healthValue / critHealthThreshold );
						GameObjectEffectHelper.StartEffectEvent( this, 'fx_health_critical', false, m_healthVfxBlackboard );
						GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().PhotoMode ).SetUint( GetAllBlackboardDefs().PhotoMode.PlayerHealthState, 2 );
					}
				}
				else
				{
					m_healthVfxBlackboard.SetValue( 'health_state', healthValue / critHealthThreshold );
				}
			}
			else if( healthValue <= lowHealthThreshold )
			{
				if( !( m_healthVfxBlackboard ) )
				{
					if( !( StatusEffectSystem.ObjectHasStatusEffectWithTag( this, 'PreventLowHealthOverlay' ) ) )
					{
						m_healthVfxBlackboard = new worldEffectBlackboard;
						m_healthVfxBlackboard.SetValue( 'health_state', healthValue / lowHealthThreshold );
						if( StatusEffectSystem.ObjectHasStatusEffect( this, T"BaseStatusEffect.CerberusGrabAttackEffect01" ) )
						{
							GameObjectEffectHelper.StartEffectEvent( this, 'eyes_closing_instant', false, m_healthVfxBlackboard );
						}
						else if( StatusEffectSystem.ObjectHasStatusEffect( this, T"BaseStatusEffect.CerberusGrabAttackEffectBack" ) )
						{
							GameObjectEffectHelper.StopEffectEvent( this, 'status_bleeding' );
							GameObjectEffectHelper.StartEffectEvent( this, 'eyes_closed_loop', false, m_healthVfxBlackboard );
						}
						else
						{
							GameObjectEffectHelper.StartEffectEvent( this, 'fx_health_low', false, m_healthVfxBlackboard );
						}
						GameInstance.GetTelemetrySystem( GetGame() ).LogPlayerReachedCriticalHealth( true );
						ReactionManagerComponent.SendVOEventToSquad( this, 'player_fallback' );
						GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().PhotoMode ).SetUint( GetAllBlackboardDefs().PhotoMode.PlayerHealthState, 1 );
					}
				}
				else
				{
					m_healthVfxBlackboard.SetValue( 'health_state', healthValue / lowHealthThreshold );
				}
			}
		}
	}

	private function SetZoomBlackboardValues( newState : Bool )
	{
		var playerStateMachineBlackboard : IBlackboard;
		playerStateMachineBlackboard = GameInstance.GetBlackboardSystem( GetGame() ).GetLocalInstanced( GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.IsUIZoomDevice, newState );
		playerStateMachineBlackboard.FireCallbacks();
	}

	private function GetZoomBlackboardValues() : Bool
	{
		var playerStateMachineBlackboard : IBlackboard;
		playerStateMachineBlackboard = GameInstance.GetBlackboardSystem( GetGame() ).GetLocalInstanced( GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		return playerStateMachineBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsUIZoomDevice );
	}

	protected event OnRewardEvent( evt : RewardEvent )
	{
		RPGManager.GiveReward( GetGame(), evt.rewardName );
	}

	protected event OnManagePersonalLinkChangeEvent( evt : ManagePersonalLinkChangeEvent )
	{
		RPGManager.TogglePersonalLinkAppearance( this );
		RPGManager.ToggleHolsteredArmAppearance( this, evt.shouldEquip );
	}

	public const function IsBlackwallForceEquippedOnPlayer() : Bool
	{
		var itemTags : array< CName >;
		var itemRecord : weak< Item_Record >;
		var weaponItem : ItemID;
		weaponItem = EquipmentSystem.GetData( this ).GetActiveItem( gamedataEquipmentArea.Weapon );
		itemRecord = RPGManager.GetItemRecord( weaponItem );
		itemTags = itemRecord.Tags();
		return itemTags.Contains( 'BlackwallForce' );
	}

	public export const function GetPhoneCallFactName( contactName1 : CName, contactName2 : CName ) : String
	{
		var phoneSystem : PhoneSystem;
		phoneSystem = ( ( PhoneSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'PhoneSystem' ) ) );
		return phoneSystem.GetPhoneCallFactName( contactName1, contactName2 );
	}

	private function TriggerInspect( itemID : String, offset : Float, adsOffset : Float, timeToScan : Float )
	{
		var evt : InspectionTriggerEvent;
		var scanEvt : ScanEvent;
		GameInstance.GetTransactionSystem( GetGame() ).AddItemToSlot( this, T"AttachmentSlots.Inspect", ItemID.FromTDBID( TDBID.Create( itemID ) ) );
		scanEvt = new ScanEvent;
		scanEvt.isAvailable = true;
		scanEvt.clue = itemID;
		QueueEvent( scanEvt );
		evt = new InspectionTriggerEvent;
		evt.item = itemID;
		evt.offset = offset;
		evt.adsOffset = adsOffset;
		evt.timeToScan = timeToScan;
		QueueEvent( evt );
	}

	public function SetInvisible( isInvisible : Bool )
	{
		m_visibleObjectComponent.Toggle( !( isInvisible ) );
	}

	public function IsInvisible() : Bool
	{
		return !( m_visibleObjectComponent.IsEnabled() );
	}

	protected event OnHeavyFootstepEvent( evt : HeavyFootstepEvent )
	{
		PlayFootstepCameraShakeBasedOnProximity( evt );
	}

	private function PlayFootstepCameraShakeBasedOnProximity( evt : HeavyFootstepEvent )
	{
		var distanceToNPC : Float;
		var rumbleName : CName;
		var rumbleIntensityPrefix : String;
		var rumbleDuration : String;
		var footstepStylePrefix : String;
		var minFootstepDistanceThreshold : Float;
		var medFootstepDistanceThreshold : Float;
		var maxFootstepDistanceThreshold : Float;
		var shakeStrength : Float;
		var distanceBasedShake : Bool;
		rumbleIntensityPrefix = "light_";
		rumbleDuration = "pulse";
		footstepStylePrefix = "footstepWalk";
		minFootstepDistanceThreshold = TDB.GetFloat( T"player.onFootstepRumble.minFootstepDistanceThreshold" );
		medFootstepDistanceThreshold = TDB.GetFloat( T"player.onFootstepRumble.medFootstepDistanceThreshold" );
		maxFootstepDistanceThreshold = TDB.GetFloat( T"player.onFootstepRumble.maxFootstepDistanceThreshold" );
		switch( evt.audioEventName )
		{
			case 'nme_boss_smasher_lcm_walk':
				rumbleIntensityPrefix = "light_";
			footstepStylePrefix = "footstepWalk";
			distanceBasedShake = true;
			break;
			case 'nme_boss_smasher_lcm_sprint':
				rumbleIntensityPrefix = "medium_";
			footstepStylePrefix = "footstepSprint";
			distanceBasedShake = true;
			break;
			case 'enm_mech_minotaur_loco_fs_heavy':
				rumbleIntensityPrefix = "medium_";
			footstepStylePrefix = "footstepWalk";
			distanceBasedShake = true;
			break;
			case 'lcm_npc_exo_':
				rumbleIntensityPrefix = "medium_";
			footstepStylePrefix = "footstepWalk";
			distanceBasedShake = true;
			break;
			case 'q302_sc_02_chimera_melee_stomp':
			case 'q302_sc_02_chimera_melee_front_stomp':
			case 'q302_sc_02_chimera_attack_land':
				rumbleIntensityPrefix = "heavy_";
			footstepStylePrefix = "footstepWalk";
			distanceBasedShake = false;
			shakeStrength = TDB.GetFloat( TDBID.Create( ( "player.cameraShake." + footstepStylePrefix ) + "High" ) );
			break;
		}
		if( distanceBasedShake )
		{
			distanceToNPC = Vector4.Distance2D( evt.instigator.GetWorldPosition(), this.GetWorldPosition() );
			if( distanceToNPC > maxFootstepDistanceThreshold )
			{
				return;
			}
			if( distanceToNPC >= medFootstepDistanceThreshold )
			{
				shakeStrength = TDB.GetFloat( TDBID.Create( ( "player.cameraShake." + footstepStylePrefix ) + "Med" ) );
			}
			else if( distanceToNPC >= minFootstepDistanceThreshold )
			{
				shakeStrength = TDB.GetFloat( TDBID.Create( ( "player.cameraShake." + footstepStylePrefix ) + "High" ) );
			}
			else
			{
				shakeStrength = TDB.GetFloat( TDBID.Create( ( "player.cameraShake." + footstepStylePrefix ) + "Low" ) );
			}
		}
		rumbleName = TDB.GetCName( TDBID.Create( ( "rumble.local." + rumbleIntensityPrefix ) + rumbleDuration ) );
		PlaySound( this, rumbleName );
		SendCameraShakeDataToGraph( , shakeStrength );
	}

	public function UpdateVisibility()
	{
		var shouldCoverModiferBeActive : Bool;
		var shouldUseCombatVisibility : Bool;
		shouldUseCombatVisibility = false;
		shouldCoverModiferBeActive = ( m_behindCover && !( m_coverVisibilityPerkBlocked ) ) && !( m_inCombat );
		shouldUseCombatVisibility = m_coverVisibilityPerkBlocked || m_inCombat;
		EnableCombatVisibilityDistances( shouldUseCombatVisibility );
		if( shouldCoverModiferBeActive && !( m_coverModifierActive ) )
		{
			m_coverModifierActive = GameInstance.GetStatsSystem( GetGame() ).ApplyModifierGroup( GetEntityID(), TDBID.ToNumber( m_coverRecordID ) );
		}
		else if( !( shouldCoverModiferBeActive ) && m_coverModifierActive )
		{
			GameInstance.GetStatsSystem( GetGame() ).RemoveModifierGroup( GetEntityID(), TDBID.ToNumber( m_coverRecordID ) );
			m_coverModifierActive = false;
		}
	}

	private function UpdateSecondaryVisibilityOffset( isCrouching : Bool )
	{
		var objectOffsetEvent : VisibleObjectSecondaryPositionEvent;
		objectOffsetEvent = new VisibleObjectSecondaryPositionEvent;
		objectOffsetEvent.offset.X = 0.0;
		objectOffsetEvent.offset.Y = 0.0;
		objectOffsetEvent.offset.Z = ( ( isCrouching ) ? ( TweakDBInterface.GetFloat( T"player.stealth.crouchVisibilityZOffset", 0.60000002 ) ) : ( TweakDBInterface.GetFloat( T"player.stealth.chestVisibilityZOffset", 1.29999995 ) ) );
		QueueEvent( objectOffsetEvent );
	}

	private function EnableCombatVisibilityDistances( enable : Bool )
	{
		var objectDistanceEvent : VisibleObjectDistanceEvent;
		var objectSecondaryDistanceEvent : VisibleObjectetSecondaryDistanceEvent;
		var nearDistance : Float;
		var farDistance : Float;
		var extraEvalDistance : Float;
		objectDistanceEvent = new VisibleObjectDistanceEvent;
		objectSecondaryDistanceEvent = new VisibleObjectetSecondaryDistanceEvent;
		nearDistance = TweakDBInterface.GetFloat( T"player.stealth.nearVisibilityDistance", 200.0 );
		farDistance = TweakDBInterface.GetFloat( T"player.stealth.farVisibilityDistance", 5.0 );
		extraEvalDistance = TweakDBInterface.GetFloat( T"player.stealth.extraEvalVisibilityDistance", 3.0 );
		objectDistanceEvent.distance = ( ( enable ) ? ( farDistance ) : ( nearDistance ) );
		objectSecondaryDistanceEvent.distance = ( ( enable ) ? ( nearDistance ) : ( farDistance ) );
		objectSecondaryDistanceEvent.extraEvaluationDistance = ( ( enable ) ? ( extraEvalDistance ) : ( 0.0 ) );
		QueueEvent( objectDistanceEvent );
		QueueEvent( objectSecondaryDistanceEvent );
	}

	protected event OnZoomLevelChange( newLevel : Float )
	{
		UpdateAimAssist();
	}

	protected event OnLocomotionStateChanged( newState : Int32 )
	{
		var isCrouching : Bool;
		m_locomotionState = newState;
		isCrouching = newState == ( ( Int32 )( gamePSMLocomotionStates.Crouch ) );
		if( m_inCrouch != isCrouching )
		{
			UpdateSecondaryVisibilityOffset( isCrouching );
			m_inCrouch = isCrouching;
		}
		UpdateAimAssist();
	}

	protected event OnCombatStateChanged( newState : Int32 )
	{
		var psmEvent : PSMPostponedParameterBool;
		var inCombat : Bool;
		var bboard : IBlackboard;
		var combatTimeStamp : Float;
		inCombat = newState == ( ( Int32 )( gamePSMCombat.InCombat ) );
		if( inCombat != m_inCombat )
		{
			if( !( inCombat ) )
			{
				GetPS().SetCombatExitTimestamp( EngineTime.ToFloat( GameInstance.GetTimeSystem( GetGame() ).GetSimTime() ) );
			}
			m_inCombat = inCombat;
			UpdateVisibility();
			if( !( m_inCombat ) )
			{
				m_hasBeenDetected = false;
			}
			else
			{
				SetIsBeingRevealed( false );
				GetPlayerPerkDataBlackboard().SetUint( GetAllBlackboardDefs().PlayerPerkData.EntityNoticedPlayer, 0 );
				bboard = GetPlayerPerkDataBlackboard();
				combatTimeStamp = EngineTime.ToFloat( GameInstance.GetSimTime( GetGame() ) );
				bboard.SetFloat( GetAllBlackboardDefs().PlayerPerkData.CombatStateTime, combatTimeStamp );
			}
			psmEvent = new PSMPostponedParameterBool;
			psmEvent.id = 'ForceDisableToggleWalk';
			psmEvent.aspect = gamestateMachineParameterAspect.Permanent;
			psmEvent.value = true;
			QueueEvent( psmEvent );
			GameInstance.GetPlayerSystem( GetGame() ).PlayerEnteredCombat( m_inCombat );
		}
		if( inCombat )
		{
			( ( TargetTrackingExtension )( GetTargetTrackerComponent() ) ).RemoveHostileCamerasFromThreats();
			GetSensorObjectComponent().RemoveForcedSensesTracing( gamedataSenseObjectType.Camera, EAIAttitude.AIA_Hostile );
		}
		else
		{
			GetSensorObjectComponent().SetForcedSensesTracing( gamedataSenseObjectType.Camera, EAIAttitude.AIA_Hostile );
		}
	}

	public function OnStatChanged( ownerID : StatsObjectID, statType : gamedataStatType, diff : Float, total : Float )
	{
		if( statType == gamedataStatType.HasKiroshiOpticsFragment )
		{
			m_hasKiroshiOpticsFragment = total > 0.0;
			UpdateAimAssist();
		}
		else if( statType == gamedataStatType.ZoomLevel )
		{
			UpdateAimAssist();
		}
	}

	public function UpdateWeaponRightEquippedItemInfo()
	{
		m_equippedRightHandWeapon = ( ( WeaponObject )( GameInstance.GetTransactionSystem( GetGame() ).GetItemInSlot( this, T"AttachmentSlots.WeaponRight" ) ) );
	}

	public function OnItemEquipped( slot : TweakDBID, item : ItemID )
	{
		if( slot == T"AttachmentSlots.WeaponRight" )
		{
			UpdateWeaponRightEquippedItemInfo();
			UpdateAimAssist();
		}
	}

	public function OnItemUnequipped( slot : TweakDBID, item : ItemID )
	{
		if( slot == T"AttachmentSlots.WeaponRight" )
		{
			m_handlingModifiers.RemoveHandlingModifiersFromWeapon( m_equippedRightHandWeapon );
			m_equippedRightHandWeapon = NULL;
			UpdateAimAssist();
		}
	}

	protected event OnLeftHandCyberwareStateChange( newState : Int32 )
	{
		m_leftHandCyberwareState = newState;
		UpdateAimAssist();
	}

	protected event OnVisionStateChange( newState : Int32 )
	{
		var focus : Bool;
		focus = newState == ( ( Int32 )( gamePSMVision.Focus ) );
		if( focus != m_focusModeActive )
		{
			m_focusModeActive = focus;
			UpdateAimAssist();
		}
	}

	protected event OnMeleeWeaponStateChange( newState : Int32 )
	{
		m_meleeWeaponState = newState;
		UpdateAimAssist();
	}

	protected event OnBodySlamStateChange( newState : Bool )
	{
		m_isInBodySlam = newState;
		UpdateAimAssist();
	}

	protected event OnUpperBodyStateChange( newState : Int32 )
	{
		var isAiming : Bool;
		isAiming = newState == ( ( Int32 )( gamePSMUpperBodyStates.Aim ) );
		if( isAiming != m_isAiming )
		{
			m_isAiming = isAiming;
			UpdateAimAssist();
		}
		m_pocketRadio.HandleRestriction( PocketRadioRestrictions.UpperBodyState, newState == ( ( Int32 )( gamePSMUpperBodyStates.ForceEmptyHands ) ) );
	}

	protected event OnCombatGadgetStateChange( newState : Int32 )
	{
		m_combatGadgetState = newState;
		UpdateAimAssist();
	}

	protected event OnSceneTierChange( newState : Int32 )
	{
		m_sceneTier = ( ( GameplayTier )( newState ) );
		m_pocketRadio.HandleRestriction( PocketRadioRestrictions.SceneTier, ( ( Int32 )( m_sceneTier ) ) >= ( ( Int32 )( GameplayTier.Tier2_StagedGameplay ) ) );
	}

	protected event OnPlayerTakedownStateChange( takedownState : Int32 )
	{
		var takedownStateEnum : gamePSMTakedown;
		var workspotSystem : WorkspotGameSystem;
		var grappledEnemyID : EntityID;
		var grappledEnemy : GameObject;
		takedownStateEnum = ( ( gamePSMTakedown )( takedownState ) );
		if( takedownStateEnum != gamePSMTakedown.EnteringGrapple )
		{
			return true;
		}
		workspotSystem = GameInstance.GetWorkspotSystem( GetGame() );
		if( !( workspotSystem ) )
		{
			return true;
		}
		grappledEnemyID = GetPlayerStateMachineBlackboard().GetEntityID( GetAllBlackboardDefs().PlayerStateMachine.TakedownTargetID );
		grappledEnemy = ( ( GameObject )( GameInstance.FindEntityByID( GetGame(), grappledEnemyID ) ) );
		if( !( grappledEnemy ) )
		{
			return true;
		}
		if( workspotSystem.IsActorInWorkspot( grappledEnemy ) )
		{
			workspotSystem.StopNpcInWorkspot( grappledEnemy );
		}
	}

	public function OnEnterAimState()
	{
		if( !( m_isAiming ) )
		{
			m_isAiming = true;
			UpdateAimAssistImmediate();
		}
	}

	protected event OnVehicleStateChange( newState : Int32 )
	{
		var vehicle : weak< VehicleObject >;
		var recordID : TweakDBID;
		var vehicleRecord : Vehicle_Record;
		m_inMountedWeaponVehicle = false;
		if( m_vehicleState != ( ( gamePSMVehicle )( newState ) ) )
		{
			m_vehicleState = ( ( gamePSMVehicle )( newState ) );
			UpdateAimAssist();
			if( VehicleComponent.GetVehicle( GetGame(), GetEntityID(), vehicle ) )
			{
				recordID = vehicle.GetRecordID();
				vehicleRecord = TweakDBInterface.GetVehicleRecord( recordID );
				if( vehicleRecord.VehDataPackage() )
				{
					if( vehicleRecord.VehDataPackage().DriverCombat().Type() == gamedataDriverCombatType.Doors )
					{
						HandleDoorsForCombat( vehicle );
					}
					else if( vehicleRecord.VehDataPackage().DriverCombat().Type() == gamedataDriverCombatType.MountedWeapons )
					{
						m_inMountedWeaponVehicle = true;
					}
				}
			}
		}
	}

	private function HandleDoorsForCombat( vehicle : weak< VehicleObject > )
	{
		var ignoreAutoDoorCloseEvt : SetIgnoreAutoDoorCloseEvent;
		ignoreAutoDoorCloseEvt = new SetIgnoreAutoDoorCloseEvent;
		if( m_vehicleState == gamePSMVehicle.DriverCombat )
		{
			ignoreAutoDoorCloseEvt.set = true;
			vehicle.QueueEvent( ignoreAutoDoorCloseEvt );
			VehicleComponent.OpenDoor( vehicle, VehicleComponent.GetDriverSlotID() );
			VehicleComponent.OpenDoor( vehicle, VehicleComponent.GetFrontPassengerSlotID() );
		}
		else if( m_vehicleState != gamePSMVehicle.Transition && m_vehicleState != gamePSMVehicle.Passenger )
		{
			ignoreAutoDoorCloseEvt.set = false;
			vehicle.QueueEvent( ignoreAutoDoorCloseEvt );
			VehicleComponent.CloseDoor( vehicle, VehicleComponent.GetDriverSlotID() );
			VehicleComponent.CloseDoor( vehicle, VehicleComponent.GetFrontPassengerSlotID() );
		}
	}

	protected event OnDriverCombatCameraChange( inTPP : Bool )
	{
		if( m_inDriverCombatTPP != inTPP )
		{
			m_inDriverCombatTPP = inTPP;
			UpdateAimAssist();
		}
	}

	protected event OnDriverCombatWeaponTypeChange( newWeaponType : Int32 )
	{
		m_driverCombatWeaponType = ( ( gamedataItemType )( newWeaponType ) );
		UpdateAimAssist();
	}

	protected event OnWeaponStateChange( newState : Int32 )
	{
		var quickMelee : Bool;
		quickMelee = newState == ( ( Int32 )( gamePSMRangedWeaponStates.QuickMelee ) );
		if( quickMelee != m_doingQuickMelee )
		{
			m_doingQuickMelee = quickMelee;
			UpdateAimAssist();
		}
	}

	protected event OnControllingDeviceChange( controllingId : EntityID )
	{
		m_controllingDeviceID = controllingId;
		UpdateAimAssist();
	}

	protected event OnNumberOfCombatantsChanged( value : Uint32 )
	{
		if( ( ( m_numberOfCombatants == 0 ) && ( value > ( ( Uint32 )( 0 ) ) ) ) && !( m_hasBeenDetected ) )
		{
			m_hasBeenDetected = true;
		}
		m_numberOfCombatants = ( ( Int32 )( value ) );
		GameInstance.GetTelemetrySystem( GetGame() ).LogNumberOfCombatants( m_numberOfCombatants );
	}

	protected event OnPlayerCoverStatusChangedEvent( evt : PlayerCoverStatusChangedEvent )
	{
		if( m_behindCover != evt.fullyBehindCover )
		{
			m_behindCover = evt.fullyBehindCover;
			UpdateVisibility();
		}
	}

	protected event OnStatusEffectApplied( evt : ApplyStatusEffectEvent )
	{
		var psmEvent : PSMPostponedParameterScriptable;
		var restrictionRecord : GameplayRestrictionStatusEffect_Record;
		var bioMonitorBB, uiBB : IBlackboard;
		var cooldowns : array< SPlayerCooldown >;
		var newCooldown : SPlayerCooldown;
		var gameplayTags : array< CName >;
		var actionRestrictionRecord : ActionRestrictionGroup_Record;
		var weaponStatsObjectID : StatsObjectID;
		var weaponID : ItemID;
		var i, j : Int32;
		var modifier : gameStatModifierData;
		var statPoolsSystem : StatPoolsSystem;
		var hostileTargets : array< TrackedLocation >;
		var hostileTarget : weak< GameObject >;
		var hostileTargetPuppet : weak< ScriptedPuppet >;
		var vanishEvt : ExitCombatOnOpticalCamoActivatedEvent;
		var enableVisibilityEvt : EnablePlayerVisibilityEvent;
		var exitCombatDelay : Float;
		var enableVisiblityDelay : Float;
		var cwIconCooldown : Float;
		var permaMod : gameStatModifierData;
		if( evt.staticData.StatusEffectType().Type() == gamedataStatusEffectType.Defeated )
		{
			StatusEffectHelper.RemoveStatusEffect( this, evt.staticData.GetID() );
			return true;
		}
		if( evt.staticData.StatusEffectType().Type() == gamedataStatusEffectType.UncontrolledMovement )
		{
			StatusEffectHelper.RemoveStatusEffect( this, evt.staticData.GetID() );
			return true;
		}
		gameplayTags = evt.staticData.GameplayTags();
		if( gameplayTags.Contains( 'DoNotApplyOnPlayer' ) )
		{
			StatusEffectHelper.RemoveStatusEffect( this, evt.staticData.GetID() );
			return true;
		}
		psmEvent = new PSMPostponedParameterScriptable;
		psmEvent.id = 'StatusEffect';
		psmEvent.value = evt.staticData;
		QueueEvent( psmEvent );
		super.OnStatusEffectApplied( evt );
		if( evt.staticData.GetID() == PlayerCoverHelper.GetBlockCoverStatusEffectID() )
		{
			m_coverVisibilityPerkBlocked = true;
			UpdateVisibility();
		}
		if( gameplayTags.Contains( 'NoScanning' ) )
		{
			m_visionModeController.UpdateNoScanningRestriction();
		}
		if( gameplayTags.Contains( 'GameplayRestriction' ) )
		{
			PlayerGameplayRestrictions.OnGameplayRestrictionAdded( this, evt.staticData, gameplayTags );
			restrictionRecord = ( ( GameplayRestrictionStatusEffect_Record )( evt.staticData ) );
			if( restrictionRecord && evt.isNewApplication )
			{
				actionRestrictionRecord = restrictionRecord.ActionRestriction();
				if( actionRestrictionRecord )
				{
					AddGameplayRestriction( GetPlayerStateMachineBlackboard(), actionRestrictionRecord.GetID() );
				}
			}
		}
		if( gameplayTags.Contains( 'CameraAnimation' ) )
		{
			if( GameplaySettingsSystem.GetAdditiveCameraMovementsSetting( this ) <= 0.0 )
			{
				StatusEffectHelper.RemoveStatusEffectsWithTag( this, 'CameraAnimation' );
			}
			else if( gameplayTags.Contains( 'Breathing' ) || gameplayTags.Contains( 'JohnnySickness' ) )
			{
				ProcessBreathingEffectApplication( evt );
			}
			else
			{
				StatusEffectHelper.RemoveAllStatusEffectsWithTagBeside( this, 'CameraAnimation', evt.staticData.GetID() );
			}
		}
		if( gameplayTags.Contains( 'CyberspacePresence' ) )
		{
			DisableFootstepAudio( true );
			DisableCameraBobbing( true );
		}
		if( gameplayTags.Contains( 'JugglerPerkRemoveKnifeCooldowns' ) )
		{
			statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
			for( i = 0; i < 3; i += 1 )
			{
				weaponID = GetEquippedItemIdInArea( gamedataEquipmentArea.Weapon, i );
				if( RPGManager.GetWeaponEvolution( weaponID ) == gamedataWeaponEvolution.Throwable )
				{
					weaponStatsObjectID = ( ( StatsObjectID )( weaponID ) );
					statPoolsSystem.RequestSettingStatPoolMaxValue( weaponStatsObjectID, gamedataStatPoolType.ThrowRecovery, this );
				}
			}
			if( PlayerDevelopmentSystem.GetInstance( this ).IsNewPerkBought( this, gamedataNewPerkType.Cool_Right_Perk_3_4 ) == 1 )
			{
				StatusEffectHelper.ApplyStatusEffect( this, T"BaseStatusEffect.JugglerCriticalDamageBonusSE" );
			}
		}
		if( gameplayTags.Contains( 'IconicPunkKnifeRemoveCooldown' ) )
		{
			statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
			for( i = 0; i < 3; i += 1 )
			{
				weaponID = GetEquippedItemIdInArea( gamedataEquipmentArea.Weapon, i );
				if( GameInstance.GetTransactionSystem( GetGame() ).GetItemData( this, weaponID ).HasTag( 'BountyHunterIconicKnife' ) )
				{
					weaponStatsObjectID = ( ( StatsObjectID )( weaponID ) );
					statPoolsSystem.RequestSettingStatPoolMaxValue( weaponStatsObjectID, gamedataStatPoolType.ThrowRecovery, this );
				}
			}
		}
		if( gameplayTags.Contains( 'KurtKnifeRemoveCooldown' ) )
		{
			statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
			for( i = 0; i < 3; i += 1 )
			{
				weaponID = GetEquippedItemIdInArea( gamedataEquipmentArea.Weapon, i );
				if( GameInstance.GetTransactionSystem( GetGame() ).GetItemData( this, weaponID ).HasTag( 'KurtIconicKnife' ) )
				{
					weaponStatsObjectID = ( ( StatsObjectID )( weaponID ) );
					statPoolsSystem.RequestSettingStatPoolMaxValue( weaponStatsObjectID, gamedataStatPoolType.ThrowRecovery, this );
				}
			}
		}
		if( gameplayTags.Contains( 'ThrowMod1RemoveCooldown' ) )
		{
			statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
			for( i = 0; i < 3; i += 1 )
			{
				weaponID = GetEquippedItemIdInArea( gamedataEquipmentArea.Weapon, i );
				if( GameInstance.GetTransactionSystem( GetGame() ).GetItemData( this, weaponID ).GetStatValueByType( gamedataStatType.ThrowMod1_CanReturn ) > 0.0 )
				{
					weaponStatsObjectID = ( ( StatsObjectID )( weaponID ) );
					statPoolsSystem.RequestSettingStatPoolMaxValue( weaponStatsObjectID, gamedataStatPoolType.ThrowRecovery, this );
				}
			}
		}
		if( ( ( ( gameplayTags.Contains( 'ActiveCamo' ) && RPGManager.HasStatFlag( this, gamedataStatType.CanPlayerExitCombatWithOpticalCamo ) ) && !( RPGManager.HasStatFlag( this, gamedataStatType.BlockOpticalCamoRelicPerk ) ) ) && !( StatusEffectHelper.HasStatusEffectWithTagConst( this, 'OpticalCamoSlideCoolPerk' ) ) ) && !( StatusEffectHelper.HasStatusEffectWithTagConst( this, 'OpticalCamoGrapple' ) ) )
		{
			exitCombatDelay = TweakDBInterface.GetFloat( T"Items.AdvancedOpticalCamoCommon.exitCombatDelay", 1.5 );
			PromoteOpticalCamoEffectorToCompletelyBlocking();
			if( m_inCombat )
			{
				enableVisiblityDelay = GameInstance.GetStatsSystem( this.GetGame() ).GetStatValue( this.GetEntityID(), gamedataStatType.OpticalCamoDuration );
				SetInvisible( true );
				hostileTargets = this.GetTargetTrackerComponent().GetHostileThreats( false );
				for( j = 0; j < hostileTargets.Size(); j += 1 )
				{
					hostileTarget = ( ( weak< weak< GameObject > > )( hostileTargets[ j ].entity ) );
					hostileTargetPuppet = ( ( ScriptedPuppet )( hostileTarget ) );
					if( hostileTargetPuppet )
					{
						hostileTargetPuppet.GetTargetTrackerComponent().DeactivateThreat( this );
					}
					vanishEvt = new ExitCombatOnOpticalCamoActivatedEvent;
					vanishEvt.npc = hostileTarget;
					GameInstance.GetDelaySystem( this.GetGame() ).DelayEvent( this, vanishEvt, exitCombatDelay );
				}
				enableVisibilityEvt = new EnablePlayerVisibilityEvent;
				GameInstance.GetDelaySystem( this.GetGame() ).DelayEvent( this, enableVisibilityEvt, enableVisiblityDelay );
			}
		}
		if( ( gameplayTags.Contains( 'CamoActiveOnPlayer' ) || gameplayTags.Contains( 'OpticalCamoSlideCoolPerk' ) ) || gameplayTags.Contains( 'OpticalCamoGrapple' ) )
		{
			SetStatPoolEnabled( gamedataStatPoolType.OpticalCamoCharges, gameStatPoolModificationTypes.Decay, true );
			SetStatPoolEnabled( gamedataStatPoolType.OpticalCamoCharges, gameStatPoolModificationTypes.Regeneration, false );
		}
		if( gameplayTags.Contains( 'Overclock' ) )
		{
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( this.GetEntityID(), gamedataStatType.CyberdeckOverclockStatValue );
			cwIconCooldown = GameInstance.GetStatsSystem( GetGame() ).GetStatValue( this.GetEntityID(), gamedataStatType.CyberdeckOverclockDuration );
			modifier = ( ( gameConstantStatModifierData )( RPGManager.CreateStatModifier( gamedataStatType.CyberdeckOverclockStatValue, gameStatModifierType.Additive, cwIconCooldown ) ) );
			GameInstance.GetStatsSystem( GetGame() ).AddModifier( this.GetEntityID(), modifier );
			SetStatPoolEnabled( gamedataStatPoolType.CyberdeckOverclock, gameStatPoolModificationTypes.Regeneration, false );
			SetStatPoolEnabled( gamedataStatPoolType.CyberdeckOverclock, gameStatPoolModificationTypes.Decay, true );
		}
		if( gameplayTags.Contains( 'PostManiac' ) )
		{
			if( PlayerDevelopmentSystem.GetInstance( this ).IsNewPerkBought( this, gamedataNewPerkType.Tech_Inbetween_Left_3 ) >= 1 )
			{
				ConsumablesChargesHelper.RefreshAmountOfCharges( GetGame(), gamedataStatPoolType.ProjectileLauncherCharges, 1 );
			}
			ConsumablesChargesHelper.RefreshAmountOfCharges( GetGame(), gamedataStatPoolType.GrenadesCharges, 1 );
		}
		if( evt.staticData.StatusEffectType().Type() == gamedataStatusEffectType.PlayerCooldown )
		{
			bioMonitorBB = GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_PlayerBioMonitor );
			cooldowns = ( ( array< SPlayerCooldown > )( bioMonitorBB.GetVariant( GetAllBlackboardDefs().UI_PlayerBioMonitor.Cooldowns ) ) );
			newCooldown.effectID = evt.staticData.GetID();
			newCooldown.instigatorID = StatusEffectHelper.GetStatusEffectByID( this, evt.staticData.GetID() ).GetInstigatorStaticDataID();
			cooldowns.PushBack( newCooldown );
			bioMonitorBB.SetVariant( GetAllBlackboardDefs().UI_PlayerBioMonitor.Cooldowns, cooldowns );
		}
		ProcessTieredDrunkEffect( evt );
		ProcessTieredDruggedEffect( evt );
		if( gameplayTags.Contains( 'Exhausted' ) )
		{
			UpdateAimAssist();
		}
		if( gameplayTags.Contains( 'UsedHealingItemOrCyberwareStatusEffect' ) )
		{
			OnStatusEffectUsedHealingItemOrCyberwareApplied();
		}
		if( gameplayTags.Contains( 'Berserk' ) )
		{
			uiBB = GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UIGameData );
			uiBB.SetBool( GetAllBlackboardDefs().UIGameData.BerserkActive, true, true );
			GameInstance.GetUISystem( GetGame() ).PushGameContext( UIGameContext.Berserk );
		}
		if( gameplayTags.Contains( 'FocusedCoolPerkSE' ) )
		{
			if( RPGManager.HasStatFlag( this, gamedataStatType.FocusedGrenadeShootingPerk ) )
			{
				ToggleFocusedGrenadeShootingPerk( true );
			}
		}
		if( gameplayTags.Contains( 'CWshard_Tier2' ) )
		{
			permaMod = RPGManager.CreateStatModifier( gamedataStatType.Humanity, gameStatModifierType.Additive, 2.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( this.GetEntityID(), permaMod );
		}
		if( gameplayTags.Contains( 'CWshard_Tier3' ) )
		{
			permaMod = RPGManager.CreateStatModifier( gamedataStatType.Humanity, gameStatModifierType.Additive, 3.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( this.GetEntityID(), permaMod );
		}
		if( gameplayTags.Contains( 'CWshard_Tier4' ) )
		{
			permaMod = RPGManager.CreateStatModifier( gamedataStatType.Humanity, gameStatModifierType.Additive, 4.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( this.GetEntityID(), permaMod );
		}
		if( gameplayTags.Contains( 'CWshard_Tier5' ) )
		{
			permaMod = RPGManager.CreateStatModifier( gamedataStatType.Humanity, gameStatModifierType.Additive, 6.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( this.GetEntityID(), permaMod );
		}
		if( gameplayTags.Contains( 'CarryShard' ) )
		{
			permaMod = RPGManager.CreateStatModifier( gamedataStatType.CarryCapacity, gameStatModifierType.Additive, 2.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( this.GetEntityID(), permaMod );
		}
		if( gameplayTags.Contains( 'CWshard_debug_lvl20' ) )
		{
			permaMod = RPGManager.CreateStatModifier( gamedataStatType.Humanity, gameStatModifierType.Additive, 30.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( this.GetEntityID(), permaMod );
		}
		if( gameplayTags.Contains( 'CWshard_debug_lvl30' ) )
		{
			permaMod = RPGManager.CreateStatModifier( gamedataStatType.Humanity, gameStatModifierType.Additive, 54.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( this.GetEntityID(), permaMod );
		}
		if( gameplayTags.Contains( 'VanillaIconicOSLooted' ) || gameplayTags.Contains( 'AllVanillaIconicOSPurchased' ) )
		{
			permaMod = RPGManager.CreateStatModifier( gamedataStatType.IconicCWFromTreasureChestLooted, gameStatModifierType.Additive, 1.0 );
			GameInstance.GetStatsSystem( GetGame() ).AddSavedModifier( this.GetEntityID(), permaMod );
		}
		if( gameplayTags.Contains( 'CustomFastForward' ) )
		{
			SetCustomFastForwardEnabled( true );
		}
		if( gameplayTags.Contains( 'PreventLowHealthOverlay' ) )
		{
			UpdateHealthStateVFX( m_lastHealthUpdate );
		}
		if( gameplayTags.Contains( 'Body_Reward_Skillbook_Bought' ) )
		{
			AddFact( GetGame(), 'body_skillbook_bought_counter', 1 );
		}
		if( gameplayTags.Contains( 'Ref_Reward_Skillbook_Bought' ) )
		{
			AddFact( GetGame(), 'ref_skillbook_bought_counter', 1 );
		}
		if( gameplayTags.Contains( 'Int_Reward_Skillbook_Bought' ) )
		{
			AddFact( GetGame(), 'int_skillbook_bought_counter', 1 );
		}
		if( gameplayTags.Contains( 'Tech_Reward_Skillbook_Bought' ) )
		{
			AddFact( GetGame(), 'tech_skillbook_bought_counter', 1 );
		}
		if( gameplayTags.Contains( 'Cool_Reward_Skillbook_Bought' ) )
		{
			AddFact( GetGame(), 'cool_skillbook_bought_counter', 1 );
		}
		if( gameplayTags.Contains( 'ForcedQHUploadAwarenessBumps' ) )
		{
			GameInstance.GetDelaySystem( GetGame() ).DelayEventNextFrame( this, new RefreshQuickhackMenuEvent );
		}
		m_pocketRadio.OnStatusEffectApplied( evt, gameplayTags );
		m_combatController.OnStatusEffectApplied( evt, gameplayTags );
	}

	public function DisableScanningFromInput()
	{
		m_visionModeController.ForceInputOff();
	}

	private function SetCustomFastForwardEnabled( enabled : Bool )
	{
		if( enabled )
		{
			ScenesFastForwardTransition.DisplayFFButtonPrompt( this );
			GameInstance.GetUISystem( GetGame() ).SetHudEntryForcedVisibility( 'input_hint', worlduiEntryVisibility.ForceShow );
			m_customFastForwardPossible = true;
		}
		else
		{
			ScenesFastForwardTransition.HideFFButtonPrompt( this );
			GameInstance.GetUISystem( GetGame() ).SetHudEntryForcedVisibility( 'input_hint', worlduiEntryVisibility.TierVisibility );
			GameInstance.GetTimeSystem( GetGame() ).UnsetTimeDilation( 'customFFTimeDilation' );
			GameObjectEffectHelper.StopEffectEvent( this, 'transition_glitch_loop' );
			m_customFastForwardPossible = false;
		}
	}

	protected function SetStatPoolEnabled( statpool : gamedataStatPoolType, statpooltype : gameStatPoolModificationTypes, enabled : Bool )
	{
		var statPoolsSystem : StatPoolsSystem;
		var entityID : StatsObjectID;
		var mod : StatPoolModifier;
		statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
		entityID = GetEntityID();
		statPoolsSystem.GetModifier( entityID, statpool, statpooltype, mod );
		mod.enabled = enabled;
		statPoolsSystem.RequestSettingModifier( entityID, statpool, statpooltype, mod );
	}

	public function GetEquippedItemIdInArea( equipArea : gamedataEquipmentArea, optional slot : Int32 ) : ItemID
	{
		var equipmentSystem : EquipmentSystem;
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
		return equipmentSystem.GetItemInEquipSlot( this, equipArea, slot );
	}

	private function DisableFootstepAudio( b : Bool )
	{
		var audioEventName : CName;
		audioEventName = ( ( b ) ? ( 'disableFootsteps' ) : ( 'enableFootsteps' ) );
		GameObject.PlaySoundEvent( this, audioEventName );
	}

	private function DisableCameraBobbing( b : Bool )
	{
		AnimationControllerComponent.SetInputBool( this, 'disable_camera_bobbing', b );
	}

	public function OnAdditiveCameraMovementsSettingChanged()
	{
		if( GameplaySettingsSystem.GetAdditiveCameraMovementsSetting( this ) <= 0.0 )
		{
			StatusEffectHelper.RemoveStatusEffectsWithTag( this, 'CameraAnimation' );
		}
		else
		{
			PlayerPuppet.ReevaluateAllBreathingEffects( this );
		}
	}

	public static function ReevaluateAllBreathingEffects( player : weak< PlayerPuppet > )
	{
		if( !( PlayerPuppet.CanApplyBreathingEffect( player ) ) )
		{
			StatusEffectHelper.RemoveStatusEffectsWithTag( player, 'Breathing' );
		}
		else
		{
			if( ( ( StatusEffectSystem.ObjectHasStatusEffect( player, T"BaseStatusEffect.BreathingMedium" ) || StatusEffectSystem.ObjectHasStatusEffect( player, T"BaseStatusEffect.BreathingHeavy" ) ) || StatusEffectSystem.ObjectHasStatusEffect( player, T"BaseStatusEffect.BreathingSick" ) ) || IsJohnnySicknessBreathingEffectActive( player ) )
			{
				return;
			}
			if( GameplaySettingsSystem.GetAdditiveCameraMovementsSetting( player ) <= 0.5 )
			{
				StatusEffectHelper.RemoveStatusEffect( player, T"BaseStatusEffect.BreathingLow" );
			}
			else
			{
				StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.BreathingLow" );
			}
		}
	}

	public static function CanApplyBreathingEffect( player : weak< PlayerPuppet > ) : Bool
	{
		var blackboard : IBlackboard;
		if( !( player ) )
		{
			return false;
		}
		if( GameplaySettingsSystem.GetAdditiveCameraMovementsSetting( player ) <= 0.0 )
		{
			return false;
		}
		if( !( ScriptedPuppet.IsActive( player ) ) )
		{
			return false;
		}
		blackboard = player.GetPlayerStateMachineBlackboard();
		if( !( blackboard ) )
		{
			return false;
		}
		if( ( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Combat ) == ( ( Int32 )( gamePSMCombat.InCombat ) ) ) && !( IsJohnnySicknessBreathingEffectActive( player ) ) )
		{
			return false;
		}
		if( ( blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Swimming ) != ( ( Int32 )( gamePSMSwimming.Default ) ) ) && !( IsJohnnySicknessBreathingEffectActive( player ) ) )
		{
			return false;
		}
		if( VehicleComponent.IsMountedToVehicle( player.GetGame(), player ) )
		{
			return false;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectWithTag( player, 'CameraShake' ) )
		{
			return false;
		}
		return true;
	}

	public static function IsSwimming( player : weak< PlayerPuppet > ) : Bool
	{
		var blackboard : IBlackboard;
		if( !( player ) )
		{
			return false;
		}
		blackboard = player.GetPlayerStateMachineBlackboard();
		return blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Swimming ) != ( ( Int32 )( gamePSMSwimming.Default ) );
	}

	public static function GetSceneTier( player : weak< PlayerPuppet > ) : Int32
	{
		var psmBlackboard : IBlackboard;
		if( !( player ) )
		{
			return 0;
		}
		psmBlackboard = player.GetPlayerStateMachineBlackboard();
		if( psmBlackboard )
		{
			return psmBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.HighLevel );
		}
		return 0;
	}

	public static function IsJohnnySicknessBreathingEffectActive( player : weak< PlayerPuppet > ) : Bool
	{
		if( ( ( StatusEffectSystem.ObjectHasStatusEffect( player, T"BaseStatusEffect.JohnnySicknessLow" ) || StatusEffectSystem.ObjectHasStatusEffect( player, T"BaseStatusEffect.JohnnySicknessMedium" ) ) || StatusEffectSystem.ObjectHasStatusEffect( player, T"BaseStatusEffect.JohnnySicknessHeavy" ) ) || StatusEffectSystem.ObjectHasStatusEffect( player, T"BaseStatusEffect.JohnnySicknessMediumQuest" ) )
		{
			return true;
		}
		return false;
	}

	private function ProcessBreathingEffectApplication( evt : StatusEffectEvent )
	{
		var gameplayTags : array< CName >;
		gameplayTags = evt.staticData.GameplayTags();
		if( gameplayTags.Contains( 'Breathing' ) )
		{
			if( PlayerPuppet.CanApplyBreathingEffect( this ) )
			{
				switch( evt.staticData.GetID() )
				{
					case T"BaseStatusEffect.BreathingLow":
						if( GameplaySettingsSystem.GetAdditiveCameraMovementsSetting( this ) <= 0.5 )
						{
							StatusEffectHelper.RemoveStatusEffect( this, T"BaseStatusEffect.BreathingLow" );
						}
					StatusEffectHelper.RemoveAllStatusEffectsWithTagBeside( this, 'Breathing', T"BaseStatusEffect.BreathingLow" );
					break;
					default:
						StatusEffectHelper.RemoveAllStatusEffectsWithTagBeside( this, 'Breathing', evt.staticData.GetID() );
					break;
				}
			}
			else
			{
				StatusEffectHelper.RemoveStatusEffectsWithTag( this, 'Breathing' );
			}
		}
		else
		{
			if( PlayerPuppet.CanApplyBreathingEffect( this ) )
			{
				StatusEffectHelper.RemoveStatusEffectsWithTag( this, 'Breathing' );
				StatusEffectHelper.RemoveAllStatusEffectsWithTagBeside( this, 'JohnnySickness', evt.staticData.GetID() );
			}
		}
	}

	private function ProcessTieredDrunkEffect( evt : StatusEffectEvent )
	{
		var stackCount : Int32;
		var drunkID : TweakDBID;
		drunkID = T"BaseStatusEffect.Drunk";
		if( evt.staticData.GetID() == drunkID )
		{
			stackCount = ( ( Int32 )( StatusEffectHelper.GetStatusEffectByID( this, drunkID ).GetStackCount() ) );
			GameObjectEffectHelper.BreakEffectLoopEvent( this, 'status_drunk_level_1' );
			GameObjectEffectHelper.BreakEffectLoopEvent( this, 'status_drunk_level_2' );
			GameObjectEffectHelper.BreakEffectLoopEvent( this, 'status_drunk_level_3' );
			GameObject.SetAudioParameter( this, 'vfx_fullscreen_drunk_level', 0.0 );
			switch( stackCount )
			{
				case 1:
					GameObjectEffectHelper.StartEffectEvent( this, 'status_drunk_level_1' );
				GameObject.SetAudioParameter( this, 'vfx_fullscreen_drunk_level', 1.0 );
				break;
				case 2:
					GameObjectEffectHelper.StartEffectEvent( this, 'status_drunk_level_2' );
				GameObject.SetAudioParameter( this, 'vfx_fullscreen_drunk_level', 2.0 );
				break;
				case 3:
				case 4:
					GameObjectEffectHelper.StartEffectEvent( this, 'status_drunk_level_3' );
				GameObject.SetAudioParameter( this, 'vfx_fullscreen_drunk_level', 3.0 );
				break;
			}
		}
	}

	private function ProcessTieredDruggedEffect( evt : StatusEffectEvent )
	{
		var stackCount : Int32;
		var druggedID : TweakDBID;
		druggedID = T"BaseStatusEffect.Drugged";
		if( evt.staticData.GetID() == druggedID )
		{
			stackCount = ( ( Int32 )( StatusEffectHelper.GetStatusEffectByID( this, druggedID ).GetStackCount() ) );
			GameObjectEffectHelper.BreakEffectLoopEvent( this, 'status_drugged_low' );
			GameObjectEffectHelper.BreakEffectLoopEvent( this, 'status_drugged_medium' );
			GameObjectEffectHelper.BreakEffectLoopEvent( this, 'status_drugged_heavy' );
			GameObject.SetAudioParameter( this, 'vfx_fullscreen_drugged_level', 0.0 );
			switch( stackCount )
			{
				case 1:
					GameObjectEffectHelper.StartEffectEvent( this, 'status_drugged_low' );
				GameObject.SetAudioParameter( this, 'vfx_fullscreen_drugged_level', 1.0 );
				break;
				case 2:
					GameObjectEffectHelper.StartEffectEvent( this, 'status_drugged_medium' );
				GameObject.SetAudioParameter( this, 'vfx_fullscreen_drugged_level', 2.0 );
				break;
				case 3:
					GameObjectEffectHelper.StartEffectEvent( this, 'status_drugged_heavy' );
				GameObject.SetAudioParameter( this, 'vfx_fullscreen_drugged_level', 3.0 );
				break;
			}
		}
	}

	private function OnStatusEffectUsedHealingItemOrCyberwareApplied()
	{
		var entityID : StatsObjectID;
		var statPoolsSystem : StatPoolsSystem;
		var healthMax : Float;
		var overshieldPercentage : Float;
		var overshieldAmount : Float;
		GetPlayerPerkDataBlackboard().SetUint( GetAllBlackboardDefs().PlayerPerkData.UsedHealingItemOrCyberware, GetPlayerPerkDataBlackboard().GetUint( GetAllBlackboardDefs().PlayerPerkData.UsedHealingItemOrCyberware ) + 1 );
		if( PlayerDevelopmentSystem.GetInstance( this ).IsNewPerkBought( this, gamedataNewPerkType.Body_Central_Milestone_3 ) < 3 )
		{
			return;
		}
		entityID = GetEntityID();
		statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
		healthMax = statPoolsSystem.GetStatPoolMaxPointValue( entityID, gamedataStatPoolType.Health );
		overshieldAmount = statPoolsSystem.GetStatPoolValue( entityID, gamedataStatPoolType.Overshield, false );
		overshieldPercentage = TDB.GetFloat( T"NewPerks.Body_Central_Milestone_3.overshieldPercentage", 0.30000001 );
		overshieldAmount += ( healthMax * overshieldPercentage );
		statPoolsSystem.RequestSettingStatPoolValue( entityID, gamedataStatPoolType.Overshield, overshieldAmount, this, false );
	}

	protected event OnNewPerkSold( evt : NewPerkSoldEvent )
	{
		if( evt.perkType == gamedataNewPerkType.Body_Central_Milestone_3 && ( evt.perkLevelSold == 3 ) )
		{
			GameInstance.GetStatPoolsSystem( GetGame() ).RequestSettingStatPoolValue( GetEntityID(), gamedataStatPoolType.Overshield, 0.0, this );
		}
	}

	protected event OnStatusEffectRemoved( evt : RemoveStatusEffect )
	{
		var psmEvent : PSMPostponedParameterScriptable;
		var focusEventUI : FocusPerkTriggerd;
		var restrictionRecord : GameplayRestrictionStatusEffect_Record;
		var bioMonitorBB, uiBB : IBlackboard;
		var cooldowns : array< SPlayerCooldown >;
		var gameplayTags : array< CName >;
		var modifier : gameStatModifierData;
		var actionRestrictionRecord : ActionRestrictionGroup_Record;
		var emptyID : EntityID;
		var perkIsPurchased : Bool;
		var cwIconCooldown : Float;
		var i : Int32;
		psmEvent = new PSMPostponedParameterScriptable;
		psmEvent.id = 'StatusEffectRemoved';
		psmEvent.value = evt.staticData;
		QueueEvent( psmEvent );
		super.OnStatusEffectRemoved( evt );
		if( evt.staticData.GetID() == PlayerCoverHelper.GetBlockCoverStatusEffectID() )
		{
			m_coverVisibilityPerkBlocked = false;
			UpdateVisibility();
		}
		gameplayTags = evt.staticData.GameplayTags();
		if( gameplayTags.Contains( 'NPCQuickhack' ) && EntityID.IsDefined( m_attackingNetrunnerID ) )
		{
			m_attackingNetrunnerID = emptyID;
		}
		if( gameplayTags.Contains( 'NoScanning' ) )
		{
			m_visionModeController.UpdateNoScanningRestriction();
		}
		if( gameplayTags.Contains( 'GameplayRestriction' ) )
		{
			PlayerGameplayRestrictions.OnGameplayRestrictionRemoved( this, evt, gameplayTags );
			restrictionRecord = ( ( GameplayRestrictionStatusEffect_Record )( evt.staticData ) );
			if( restrictionRecord )
			{
				actionRestrictionRecord = restrictionRecord.ActionRestriction();
				if( actionRestrictionRecord )
				{
					RemoveGameplayRestriction( GetPlayerStateMachineBlackboard(), actionRestrictionRecord.GetID() );
				}
			}
		}
		if( ( gameplayTags.Contains( 'CameraAnimation' ) && ( evt.staticData.GetID() != T"BaseStatusEffect.BreathingLow" ) ) && !( StatusEffectSystem.ObjectHasStatusEffectWithTag( this, 'CameraShake' ) ) )
		{
			PlayerPuppet.ReevaluateAllBreathingEffects( this );
		}
		if( gameplayTags.Contains( 'CyberspacePresence' ) )
		{
			DisableFootstepAudio( false );
			DisableCameraBobbing( false );
		}
		if( gameplayTags.Contains( 'FocusedCoolPerkSE' ) )
		{
			if( GameInstance.GetTimeSystem( GetGame() ).IsTimeDilationActive( 'focusedStatePerkDilation' ) )
			{
				GameInstance.GetTimeSystem( GetGame() ).UnsetTimeDilation( 'focusedStatePerkDilation', 'MeleeHitEaseOut' );
			}
			GameObject.PlaySoundEvent( this, 'time_dilation_focused_exit' );
			GameObjectEffectHelper.BreakEffectLoopEvent( this, 'cool_perk_focused_state_fullscreen' );
			focusEventUI = new FocusPerkTriggerd;
			focusEventUI.isActive = false;
			QueueEvent( focusEventUI );
			perkIsPurchased = RPGManager.HasStatFlag( this, gamedataStatType.FocusedGrenadeShootingPerk );
			if( perkIsPurchased )
			{
				ToggleFocusedGrenadeShootingPerk( false );
			}
		}
		if( evt.staticData.StatusEffectType().Type() == gamedataStatusEffectType.PlayerCooldown )
		{
			bioMonitorBB = GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_PlayerBioMonitor );
			cooldowns = ( ( array< SPlayerCooldown > )( bioMonitorBB.GetVariant( GetAllBlackboardDefs().UI_PlayerBioMonitor.Cooldowns ) ) );
			for( i = 0; i < cooldowns.Size(); i += 1 )
			{
				if( cooldowns[ i ].effectID == evt.staticData.GetID() )
				{
					cooldowns.Erase( i );
					break;
				}
			}
			bioMonitorBB.SetVariant( GetAllBlackboardDefs().UI_PlayerBioMonitor.Cooldowns, cooldowns );
		}
		if( gameplayTags.Contains( 'Exhausted' ) )
		{
			UpdateAimAssist();
		}
		if( ( gameplayTags.Contains( 'CamoActiveOnPlayer' ) || gameplayTags.Contains( 'OpticalCamoSlideCoolPerk' ) ) || gameplayTags.Contains( 'OpticalCamoGrapple' ) )
		{
			SetStatPoolEnabled( gamedataStatPoolType.OpticalCamoCharges, gameStatPoolModificationTypes.Regeneration, true );
			SetStatPoolEnabled( gamedataStatPoolType.OpticalCamoCharges, gameStatPoolModificationTypes.Decay, false );
		}
		if( gameplayTags.Contains( 'Overclock' ) )
		{
			GameInstance.GetStatsSystem( GetGame() ).RemoveAllModifiers( this.GetEntityID(), gamedataStatType.CyberdeckOverclockStatValue );
			cwIconCooldown = GameInstance.GetStatsSystem( GetGame() ).GetStatValue( this.GetEntityID(), gamedataStatType.CyberdeckOverclockCooldown );
			modifier = ( ( gameConstantStatModifierData )( RPGManager.CreateStatModifier( gamedataStatType.CyberdeckOverclockStatValue, gameStatModifierType.Additive, cwIconCooldown ) ) );
			GameInstance.GetStatsSystem( GetGame() ).AddModifier( this.GetEntityID(), modifier );
			SetStatPoolEnabled( gamedataStatPoolType.CyberdeckOverclock, gameStatPoolModificationTypes.Regeneration, true );
			SetStatPoolEnabled( gamedataStatPoolType.CyberdeckOverclock, gameStatPoolModificationTypes.Decay, false );
		}
		ProcessTieredDrunkEffect( evt );
		ProcessTieredDruggedEffect( evt );
		if( gameplayTags.Contains( 'Berserk' ) )
		{
			uiBB = GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UIGameData );
			uiBB.SetBool( GetAllBlackboardDefs().UIGameData.BerserkActive, false, true );
			GameInstance.GetUISystem( GetGame() ).PopGameContext( UIGameContext.Berserk, true );
		}
		if( gameplayTags.Contains( 'CustomFastForward' ) )
		{
			SetCustomFastForwardEnabled( false );
		}
		if( gameplayTags.Contains( 'PreventLowHealthOverlay' ) )
		{
			UpdateHealthStateVFX( m_lastHealthUpdate );
		}
		m_pocketRadio.OnStatusEffectRemoved( evt, gameplayTags );
		m_combatController.OnStatusEffectRemoved( evt, gameplayTags );
	}

	protected event OnUpdateEquippedWeaponsHandling( evt : UpdateEquippedWeaponsHandlingEvent )
	{
		if( m_equippedRightHandWeapon )
		{
			m_handlingModifiers.UpdateEquippedWeaponsHandling( evt, m_equippedRightHandWeapon );
		}
	}

	public function RegisterFriendlyDeviceHostileToEnemies( deviceID : EntityID )
	{
		RWLock.Acquire( m_friendlyDevicesHostileToEnemiesLock );
		if( !( m_friendlyDevicesHostileToEnemies.Contains( deviceID ) ) )
		{
			m_friendlyDevicesHostileToEnemies.PushBack( deviceID );
		}
		RWLock.Release( m_friendlyDevicesHostileToEnemiesLock );
	}

	public function UnregisterFriendlyDeviceHostileToEnemies( deviceID : EntityID )
	{
		RWLock.Acquire( m_friendlyDevicesHostileToEnemiesLock );
		m_friendlyDevicesHostileToEnemies.Remove( deviceID );
		RWLock.Release( m_friendlyDevicesHostileToEnemiesLock );
	}

	protected event OnAttitudeChanged( evt : AttitudeChangedEvent )
	{
		var i : Int32;
		var device : SensorDevice;
		if( evt.attitude == EAIAttitude.AIA_Hostile )
		{
			RWLock.AcquireShared( m_friendlyDevicesHostileToEnemiesLock );
			for( i = 0; i < m_friendlyDevicesHostileToEnemies.Size(); i += 1 )
			{
				device = ( ( SensorDevice )( GameInstance.FindEntityByID( GetGame(), m_friendlyDevicesHostileToEnemies[ i ] ) ) );
				if( device )
				{
					device.ScheduleHostileUpdateTowardsPlayerHostiles();
				}
			}
			RWLock.ReleaseShared( m_friendlyDevicesHostileToEnemiesLock );
		}
	}

	protected event OnAdHocAnimationRequest( evt : AdHocAnimationEvent )
	{
		var blackboardSystem : BlackboardSystem;
		var blackboard : IBlackboard;
		blackboardSystem = GameInstance.GetBlackboardSystem( GetGame() );
		blackboard = blackboardSystem.Get( GetAllBlackboardDefs().AdHocAnimation );
		blackboard.SetBool( GetAllBlackboardDefs().AdHocAnimation.IsActive, true );
		blackboard.SetBool( GetAllBlackboardDefs().AdHocAnimation.UseBothHands, evt.useBothHands );
		blackboard.SetBool( GetAllBlackboardDefs().AdHocAnimation.UnequipWeapon, evt.unequipWeapon );
		blackboard.SetInt( GetAllBlackboardDefs().AdHocAnimation.AnimationIndex, evt.animationIndex );
		blackboard.SetFloat( GetAllBlackboardDefs().AdHocAnimation.AnimationDuration, evt.animationDuration );
	}

	protected event OnSceneForceWeaponAimEvent( evt : SceneForceWeaponAim )
	{
		var blackboard : IBlackboard;
		var eqManipulationRequest : EquipmentSystemWeaponManipulationRequest;
		var tier : Int32;
		blackboard = GetPlayerStateMachineBlackboard();
		tier = blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.HighLevel );
		if( ( tier > ( ( Int32 )( gamePSMHighLevel.SceneTier1 ) ) ) && ( tier <= ( ( Int32 )( gamePSMHighLevel.SceneTier5 ) ) ) )
		{
			blackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.SceneAimForced, true );
			blackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.SceneSafeForced, false );
			blackboard.SetFloat( GetAllBlackboardDefs().PlayerStateMachine.SceneWeaponLoweringSpeedOverride, 0.0 );
			SendSceneOverridesAnimFeature( blackboard );
			eqManipulationRequest = new EquipmentSystemWeaponManipulationRequest;
			eqManipulationRequest.requestType = EquipmentManipulationAction.RequestLastUsedOrFirstAvailableWeapon;
			eqManipulationRequest.owner = this;
			GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ).QueueRequest( eqManipulationRequest );
		}
	}

	protected event OnFelledEvent( evt : FelledEvent )
	{
		var blackboard : IBlackboard;
		blackboard = GetPlayerStateMachineBlackboard();
		blackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.Felled, evt.m_active );
	}

	protected event OnClearAnimFeatureCarryEvent( evt : ClearAnimFeatureCarryEvent )
	{
		var animFeatureCarry : AnimFeature_Carry;
		animFeatureCarry = new AnimFeature_Carry;
		animFeatureCarry.state = ( ( Int32 )( ECarryState.None ) );
		animFeatureCarry.pickupAnimation = 0;
		animFeatureCarry.fastMode = false;
		animFeatureCarry.useBothHands = false;
		animFeatureCarry.instant = false;
		animFeatureCarry.isCarryActive = false;
		animFeatureCarry.isFriendlyCarry = false;
		animFeatureCarry.wasThrown = false;
		AnimationControllerComponent.ApplyFeature( this, 'Carry', animFeatureCarry );
	}

	protected event OnSceneForceWeaponSafeEvent( evt : SceneForceWeaponSafe )
	{
		var blackboard : IBlackboard;
		var eqManipulationRequest : EquipmentSystemWeaponManipulationRequest;
		var tier : Int32;
		blackboard = GetPlayerStateMachineBlackboard();
		tier = blackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.HighLevel );
		if( ( tier > ( ( Int32 )( gamePSMHighLevel.SceneTier1 ) ) ) && ( tier <= ( ( Int32 )( gamePSMHighLevel.SceneTier5 ) ) ) )
		{
			blackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.SceneAimForced, false );
			blackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.SceneSafeForced, true );
			blackboard.SetFloat( GetAllBlackboardDefs().PlayerStateMachine.SceneWeaponLoweringSpeedOverride, evt.weaponLoweringSpeedOverride );
			SendSceneOverridesAnimFeature( blackboard );
			eqManipulationRequest = new EquipmentSystemWeaponManipulationRequest;
			eqManipulationRequest.requestType = EquipmentManipulationAction.RequestLastUsedOrFirstAvailableWeapon;
			eqManipulationRequest.owner = this;
			GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ).QueueRequest( eqManipulationRequest );
		}
	}

	protected event OnSceneFirstEquipState( evt : SceneFirstEquipState )
	{
		var blackboard : IBlackboard;
		blackboard = GetPlayerStateMachineBlackboard();
		blackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.ScenePreventFirstEquip, evt.prevented );
	}

	protected event OnEnableBraindanceActions( evt : EnableBraindanceActions )
	{
		var maskEvent : EnableFields;
		var bdEvent : BraindanceInputChangeEvent;
		var bdSystem : BraindanceSystem;
		maskEvent = new EnableFields;
		maskEvent.actionMask = evt.actionMask;
		bdSystem = ( ( BraindanceSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'BraindanceSystem' ) ) );
		bdSystem.QueueRequest( maskEvent );
		bdEvent = new BraindanceInputChangeEvent;
		bdEvent.bdSystem = bdSystem;
		GameInstance.GetUISystem( GetGame() ).QueueEvent( bdEvent );
	}

	protected event OnDisableBraindanceActions( evt : DisableBraindanceActions )
	{
		var maskEvent : DisableFields;
		var bdEvent : BraindanceInputChangeEvent;
		var bdSystem : BraindanceSystem;
		maskEvent = new DisableFields;
		maskEvent.actionMask = evt.actionMask;
		bdSystem = ( ( BraindanceSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'BraindanceSystem' ) ) );
		bdSystem.QueueRequest( maskEvent );
		bdEvent = new BraindanceInputChangeEvent;
		bdEvent.bdSystem = bdSystem;
		GameInstance.GetUISystem( GetGame() ).QueueEvent( bdEvent );
	}

	protected event OnForceBraindanceCameraToggle( evt : ForceBraindanceCameraToggle )
	{
		var request : SetBraindanceState;
		var bdSystem : BraindanceSystem;
		request = new SetBraindanceState;
		request.newState = evt.editorState;
		bdSystem = ( ( BraindanceSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'BraindanceSystem' ) ) );
		bdSystem.QueueRequest( request );
	}

	protected event OnPauseBraindance( evt : PauseBraindance )
	{
		var request : SendPauseBraindanceRequest;
		var bdSystem : BraindanceSystem;
		request = new SendPauseBraindanceRequest;
		bdSystem = ( ( BraindanceSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'BraindanceSystem' ) ) );
		bdSystem.QueueRequest( request );
	}

	protected event OnModifyOverlappedSecurityArease( evt : ModifyOverlappedSecurityAreas )
	{
		if( evt.isEntering )
		{
			AddOverrlappedSecurityZone( evt.zoneID );
		}
		else
		{
			RemoveOverrlappedSecurityZone( evt.zoneID );
		}
	}

	public function AddOverrlappedSecurityZone( zone : PersistentID )
	{
		if( !( m_overlappedSecurityZones.Contains( zone ) ) )
		{
			m_overlappedSecurityZones.PushBack( zone );
		}
	}

	public function RemoveOverrlappedSecurityZone( zone : PersistentID )
	{
		m_overlappedSecurityZones.Remove( zone );
	}

	protected function SendSceneOverridesAnimFeature( sceneOverridesBlackboard : IBlackboard )
	{
		var animFeature : AnimFeature_SceneGameplayOverrides;
		animFeature = new AnimFeature_SceneGameplayOverrides;
		animFeature.aimForced = sceneOverridesBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.SceneAimForced );
		animFeature.safeForced = sceneOverridesBlackboard.GetBool( GetAllBlackboardDefs().PlayerStateMachine.SceneSafeForced );
		if( sceneOverridesBlackboard.GetFloat( GetAllBlackboardDefs().PlayerStateMachine.SceneWeaponLoweringSpeedOverride ) > 0.0 )
		{
			animFeature.isAimOutTimeOverridden = true;
			animFeature.aimOutTimeOverride = sceneOverridesBlackboard.GetFloat( GetAllBlackboardDefs().PlayerStateMachine.SceneWeaponLoweringSpeedOverride );
		}
		else
		{
			animFeature.isAimOutTimeOverridden = false;
			animFeature.aimOutTimeOverride = 0.0;
		}
		AnimationControllerComponent.ApplyFeature( this, 'SceneGameplayOverrides', animFeature );
	}

	protected event OnWorkspotStartedEvent( evt : WorkspotStartedEvent )
	{
		m_currentPlayerWorkspotTags = evt.tags;
		if( evt.tags.Contains( 'wsPlayerDamageReduction' ) )
		{
			if( !( IsWorkspotDamageReductionAdded() ) )
			{
				m_workspotDamageReductionActive = GameInstance.GetStatsSystem( GetGame() ).ApplyModifierGroup( GetEntityID(), TDBID.ToNumber( m_damageReductionRecordID ) );
			}
		}
		if( evt.tags.Contains( 'wsPlayerVisibilityReduction' ) )
		{
			if( !( IsWorkspotVisibilityReductionActive() ) )
			{
				m_workspotVisibilityReductionActive = GameInstance.GetStatsSystem( GetGame() ).ApplyModifierGroup( GetEntityID(), TDBID.ToNumber( m_visReductionRecordID ) );
			}
		}
		if( evt.tags.Contains( 'FinisherWorkspot' ) )
		{
			m_isInFinisher = true;
			FinisherAttackEvents.SetCameraContext( this, 'WorkspotLocked' );
			FinisherAttackEvents.SetGameplayCameraParameters( this, "cameraFinishers" );
		}
	}

	protected event OnWorkspotFinishedEvent( evt : WorkspotFinishedEvent )
	{
		m_currentPlayerWorkspotTags.Clear();
		if( evt.tags.Contains( 'wsPlayerDamageReduction' ) )
		{
			if( IsWorkspotDamageReductionAdded() )
			{
				if( GameInstance.GetStatsSystem( GetGame() ).RemoveModifierGroup( GetEntityID(), TDBID.ToNumber( m_damageReductionRecordID ) ) )
				{
					m_workspotDamageReductionActive = false;
				}
			}
		}
		if( evt.tags.Contains( 'wsPlayerVisibilityReduction' ) )
		{
			if( IsWorkspotVisibilityReductionActive() )
			{
				if( GameInstance.GetStatsSystem( GetGame() ).RemoveModifierGroup( GetEntityID(), TDBID.ToNumber( m_visReductionRecordID ) ) )
				{
					m_workspotVisibilityReductionActive = false;
				}
			}
		}
		if( evt.tags.Contains( 'FinisherWorkspot' ) )
		{
			m_isInFinisher = false;
			FinisherAttackEvents.SetCameraContext( this, 'Default' );
			FinisherAttackEvents.SetGameplayCameraParameters( this, "cameraDefault" );
			FinisherEndEvents.ApplyFinisherBuffs( this, true );
		}
	}

	public const function GetPlayerCurrentWorkspotTags() : array< CName >
	{
		return m_currentPlayerWorkspotTags;
	}

	public const function PlayerContainsWorkspotTag( tag : CName ) : Bool
	{
		return m_currentPlayerWorkspotTags.Contains( tag );
	}

	public const function IsCooldownForActionActive( actionID : TweakDBID ) : Bool
	{
		return GetPS().IsActionReady( actionID );
	}

	private function RegisterToFacts()
	{
		GameInstance.GetQuestsSystem( GetGame() ).RegisterEntity( 'q001_took_vroom_jacket', GetEntityID() );
		GameInstance.GetQuestsSystem( GetGame() ).RegisterEntity( 'player_allow_outerwear_clothing', GetEntityID() );
	}

	protected event OnFactChangedEvent( evt : FactChangedEvent )
	{
		switch( evt.GetFactName() )
		{
			case 'q001_took_vroom_jacket':
			case 'player_allow_outerwear_clothing':
				if( GameInstance.GetQuestsSystem( GetGame() ).GetFact( evt.GetFactName() ) > 0 )
				{
					AllowOuterwearClothing();
				}
				else
				{
					DisallowOuterwearClothing();
				}
			;
			break;
			default:
				break;
		}
	}

	protected event OnSysDebuggerEvent( evt : SysDebuggerEvent )
	{
		var debugger : SecSystemDebugger;
		var req : RealTimeUpdateRequest;
		req = new RealTimeUpdateRequest;
		req.m_evt = evt;
		req.m_time = EngineTime.ToFloat( GameInstance.GetTimeSystem( GetGame() ).GetSimTime() );
		debugger = ( ( SecSystemDebugger )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'SecSystemDebugger' ) ) );
		debugger.QueueRequest( req );
	}

	private function AllowOuterwearClothing()
	{
		AnimationControllerComponent.SetInputBool( this, 'allow_outerwear_clothing', true );
	}

	private function DisallowOuterwearClothing()
	{
		AnimationControllerComponent.SetInputBool( this, 'allow_outerwear_clothing', false );
	}

	private function InitializeFocusModeTagging()
	{
		var request : RegisterInputListenerRequest;
		request = new RegisterInputListenerRequest;
		request.object = this;
		GetTaggingSystem().QueueRequest( request );
	}

	private function UnInitializeFocusModeTagging()
	{
		var request : UnRegisterInputListenerRequest;
		request = new UnRegisterInputListenerRequest;
		request.object = this;
		GetTaggingSystem().QueueRequest( request );
	}

	protected event OnRequestEquipHeavyWeapon( evt : RequestEquipHeavyWeapon )
	{
		var equipmentSystem : EquipmentSystem;
		var drawItem : DrawItemRequest;
		drawItem = new DrawItemRequest;
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
		drawItem.itemID = evt.itemID;
		drawItem.owner = this;
		equipmentSystem.QueueRequest( drawItem );
	}

	protected event OnFillAnimWrapperInfoBasedOnEquippedItem( evt : FillAnimWrapperInfoBasedOnEquippedItem )
	{
		if( ItemID.IsValid( evt.itemID ) )
		{
			if( evt.clearWrapperInfo )
			{
				AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( this, evt.itemName, 0.0 );
			}
			else
			{
				AnimationControllerComponent.SetAnimWrapperWeightOnOwnerAndItems( this, evt.itemName, 1.0 );
			}
		}
	}

	protected override function OnIncapacitated()
	{
		super.OnIncapacitated();
		if( IsDead() )
		{
			EnableInteraction( 'Revive', true );
		}
		m_incapacitated = true;
		RefreshCPOVisionAppearance();
		SetSenseObjectType( gamedataSenseObjectType.Deadbody );
	}

	private function RefreshCPOVisionAppearance()
	{
		var visionAppearance : VisionAppearance;
		if( IsControlledByAnotherClient() )
		{
			visionAppearance.showThroughWalls = true;
			if( IsIncapacitated() )
			{
				visionAppearance.fill = 1;
				if( HasCPOMissionData() )
				{
					visionAppearance.outline = 2;
				}
				else
				{
					visionAppearance.outline = 4;
				}
			}
			else if( HasCPOMissionData() )
			{
				visionAppearance.outline = 2;
			}
			else
			{
				visionAppearance.outline = 1;
			}
			GameInstance.GetVisionModeSystem( GetGame() ).ForceVisionAppearance( this, visionAppearance );
		}
	}

	protected override function OnResurrected()
	{
		if( IsMultiplayer() )
		{
			GameInstance.GetStatPoolsSystem( GetGame() ).RequestResetingModifier( GetEntityID(), gamedataStatPoolType.Health, gameStatPoolModificationTypes.Regeneration );
			Revive( 20.0 );
		}
		else
		{
			Revive( 100.0 );
		}
		super.OnResurrected();
		EnableInteraction( 'Revive', false );
		m_incapacitated = false;
		RefreshCPOVisionAppearance();
		CreateVendettaTimeDelayEvent();
		SetSenseObjectType( gamedataSenseObjectType.Player );
	}

	public const override function IsIncapacitated() : Bool
	{
		return m_incapacitated;
	}

	private function RegisterCPOMissionDataCallback()
	{
		m_CPOMissionDataBbId = GetBlackboard().RegisterListenerBool( GetAllBlackboardDefs().Puppet.HasCPOMissionData, this, 'OnCPOMissionDataChanged' );
	}

	private function UnregisterCPOMissionDataCallback()
	{
		if( m_CPOMissionDataBbId )
		{
			GetBlackboard().UnregisterListenerBool( GetAllBlackboardDefs().Puppet.HasCPOMissionData, m_CPOMissionDataBbId );
		}
	}

	protected event OnCPOMissionDataTransferred( evt : CPOMissionDataTransferred )
	{
		if( IsServer() )
		{
			OnCPOMissionDataTransferredServer( evt );
		}
		else
		{
			OnCPOMissionDataTransferredClient( evt );
		}
	}

	private function OnCPOMissionDataTransferredServer( evt : CPOMissionDataTransferred )
	{
		SetHasCPOMissionData( evt.dataDownloaded, evt.dataDamagesPresetName, evt.compatibleDeviceName, evt.ownerDecidesOnTransfer );
		if( evt.dataDownloaded )
		{
			m_CPOMissionDataState.m_choiceTokenTimeout = evt.choiceTokenTimeout;
			if( evt.isChoiceToken )
			{
				m_CPOMissionDataState.m_delayedGiveChoiceTokenEventId = MultiplayerGiveChoiceTokenEvent.CreateDelayedEvent( this, evt.compatibleDeviceName, ( ( Uint32 )( evt.choiceTokenTimeout ) ) );
			}
		}
		else
		{
			if( m_CPOMissionDataState.m_isChoiceToken && ( m_CPOMissionDataState.m_delayedGiveChoiceTokenEventId != DelayID() ) )
			{
				GameInstance.GetDelaySystem( GetGame() ).CancelDelay( m_CPOMissionDataState.m_delayedGiveChoiceTokenEventId );
			}
		}
		m_CPOMissionDataState.m_isChoiceToken = evt.isChoiceToken;
		QueueReplicatedEvent( evt );
	}

	private function OnCPOMissionDataTransferredClient( evt : CPOMissionDataTransferred )
	{
		m_CPOMissionDataState.m_isChoiceToken = evt.isChoiceToken;
		if( evt.isChoiceToken )
		{
			OnCPOMissionDataTransferredChoiceTokenClient( evt );
		}
	}


	private var m_choiceTokenTextLayerId : Uint32;
	default m_choiceTokenTextLayerId = 0;
	private var m_choiceTokenTextDrawn : Bool;
	default m_choiceTokenTextDrawn = false;

	private function OnCPOMissionDataTransferredChoiceTokenClient( evt : CPOMissionDataTransferred )
	{
		SetCPOMissionData( evt.dataDownloaded );
		if( m_choiceTokenTextDrawn )
		{
			GameInstance.GetDebugVisualizerSystem( GetGame() ).ClearLayer( m_choiceTokenTextLayerId );
			m_choiceTokenTextDrawn = false;
		}
		if( evt.dataDownloaded )
		{
			QueueEvent( new CPOChoiceTokenDrawTextEvent );
		}
	}

	protected event OnCPOChoiceTokenDrawTextEvent( evt : CPOChoiceTokenDrawTextEvent )
	{
		var choiceText : String;
		if( m_choiceTokenTextDrawn )
		{
			GameInstance.GetDebugVisualizerSystem( GetGame() ).ClearLayer( m_choiceTokenTextLayerId );
		}
		if( HasCPOMissionData() && m_CPOMissionDataState.m_isChoiceToken )
		{
			if( IsControlledByLocalPeer() )
			{
				choiceText = "Make a choice";
			}
			else
			{
				choiceText = "Other player is making choice";
			}
			m_choiceTokenTextLayerId = GameInstance.GetDebugVisualizerSystem( GetGame() ).DrawText( Vector4( 500.0, 300.0, 0.0, 1.5 ), choiceText, gameDebugViewETextAlignment.Center, Color( 245, 35, 32, 255 ), 2.0 );
			GameInstance.GetDebugVisualizerSystem( GetGame() ).SetScale( m_choiceTokenTextLayerId, Vector4( 3.0, 3.0, 0.0, 0.0 ) );
			m_choiceTokenTextDrawn = true;
			GameInstance.GetDelaySystem( GetGame() ).DelayEvent( this, evt, 3.0 );
		}
	}

	private function CPOMissionDataOnPlayerDetach()
	{
		var evt : MultiplayerGiveChoiceTokenEvent;
		if( ( IsServer() && HasCPOMissionData() ) && m_CPOMissionDataState.m_isChoiceToken )
		{
			if( m_CPOMissionDataState.m_delayedGiveChoiceTokenEventId != DelayID() )
			{
				GameInstance.GetDelaySystem( GetGame() ).CancelDelay( m_CPOMissionDataState.m_delayedGiveChoiceTokenEventId );
			}
			evt = MultiplayerGiveChoiceTokenEvent.CreateEvent( m_CPOMissionDataState.m_compatibleDeviceName, m_CPOMissionDataState.m_choiceTokenTimeout );
			evt.GiveChoiceToken( this );
		}
		if( m_choiceTokenTextDrawn )
		{
			GameInstance.GetDebugVisualizerSystem( GetGame() ).ClearLayer( m_choiceTokenTextLayerId );
			m_choiceTokenTextDrawn = false;
		}
	}

	protected event OnCPOMissionPlayerVotedEvent( evt : CPOMissionPlayerVotedEvent )
	{
		SetCPOMissionVoted( evt.compatibleDeviceName, true );
	}

	protected event OnPlayerDamageFromDataEvent( e : PlayerDamageFromDataEvent )
	{
		ProcessDamageEvents( true, m_CPOMissionDataState.m_CPOMissionDataDamagesPreset );
	}

	protected event OnCPOMissionDataUpdateEvent( e : CPOMissionDataUpdateEvent )
	{
		m_CPOMissionDataState.UpdateSounds( this );
	}

	public const function GetCompatibleCPOMissionDeviceName() : CName
	{
		return m_CPOMissionDataState.m_compatibleDeviceName;
	}

	protected event OnCPOMissionDataChanged( hasData : Bool )
	{
		RefreshCPOVisionAppearance();
	}

	public function SetHasCPOMissionData( setHasData : Bool, damagesPreset : CName, compatibleDeviceName : CName, ownerDecidesOnTransfer : Bool )
	{
		SetCPOMissionData( setHasData );
		m_CPOMissionDataState.m_CPOMissionDataDamagesPreset = damagesPreset;
		m_CPOMissionDataState.m_compatibleDeviceName = compatibleDeviceName;
		m_CPOMissionDataState.m_ownerDecidesOnTransfer = ownerDecidesOnTransfer;
		ProcessDamageEvents( setHasData, damagesPreset );
	}

	protected event OnCPOGiveChoiceTokenEvent( e : MultiplayerGiveChoiceTokenEvent )
	{
		e.GiveChoiceToken( this );
	}

	private function ProcessDamageEvents( addDamage : Bool, damagesPreset : CName )
	{
		var delaySystem : DelaySystem;
		var tickableEvent : PlayerDamageFromDataEvent;
		var currArmor : Float;
		var currHealth : Float;
		var armorDPS : Float;
		var healthDPS : Float;
		delaySystem = GameInstance.GetDelaySystem( GetGame() );
		delaySystem.CancelDelay( m_DataDamageUpdateID );
		if( addDamage )
		{
			armorDPS = TDB.GetFloat( TDBID.Create( ( "player." + NameToString( damagesPreset ) ) + ".armorDPS" ), 0.0 );
			healthDPS = TDB.GetFloat( TDBID.Create( ( "player." + NameToString( damagesPreset ) ) + ".healthDPS" ), 0.0 );
			currArmor = GameInstance.GetStatPoolsSystem( GetGame() ).GetStatPoolValue( GetEntityID(), gamedataStatPoolType.CPO_Armor );
			currHealth = GameInstance.GetStatPoolsSystem( GetGame() ).GetStatPoolValue( GetEntityID(), gamedataStatPoolType.Health );
			if( ( armorDPS > 0.0 ) && ( currArmor > 0.0 ) )
			{
				GameInstance.GetStatPoolsSystem( GetGame() ).RequestChangingStatPoolValue( GetEntityID(), gamedataStatPoolType.CPO_Armor, -( armorDPS ), NULL, false );
			}
			else if( ( healthDPS > 0.0 ) && ( currHealth > 0.0 ) )
			{
				if( armorDPS > 0.0 )
				{
					GameInstance.GetStatPoolsSystem( GetGame() ).RequestChangingStatPoolValue( GetEntityID(), gamedataStatPoolType.CPO_Armor, -( armorDPS ), NULL, false );
				}
				GameInstance.GetStatPoolsSystem( GetGame() ).RequestChangingStatPoolValue( GetEntityID(), gamedataStatPoolType.Health, -( healthDPS ), NULL, false );
			}
			tickableEvent = new PlayerDamageFromDataEvent;
			m_DataDamageUpdateID = delaySystem.DelayEvent( this, tickableEvent, 1.0 );
		}
	}

	protected event OnDeath( evt : gameDeathEvent )
	{
		ForceCloseRadialWheel();
		StatusEffectHelper.ApplyStatusEffect( this, T"GameplayRestriction.BlockAllHubMenu" );
		super.OnDeath( evt );
		GameInstance.GetTelemetrySystem( GetGame() ).LogPlayerDeathEvent( evt );
		GameInstance.GetAutoSaveSystem( GetGame() ).InvalidateAutoSaveRequests();
	}

	private function ForceCloseRadialWheel()
	{
		var closeEvt : ForceRadialWheelShutdown;
		closeEvt = new ForceRadialWheelShutdown;
		QueueEvent( closeEvt );
	}

	private function Revive( percAmount : Float )
	{
		var playerID : StatsObjectID;
		var statPoolsSystem : StatPoolsSystem;
		playerID = GetEntityID();
		statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
		if( ( percAmount >= 0.0 ) && ( percAmount <= 100.0 ) )
		{
			statPoolsSystem.RequestSettingStatPoolValue( playerID, gamedataStatPoolType.Health, percAmount, NULL, true );
		}
	}

	protected event OnTargetNeutraliziedEvent( evt : TargetNeutraliziedEvent )
	{
		var bb : IBlackboard;
		var processExpReq : ProcessQueuedCombatExperience;
		bb = GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_Crosshair );
		bb.SetVariant( GetAllBlackboardDefs().UI_Crosshair.EnemyNeutralized, evt.type );
		processExpReq = new ProcessQueuedCombatExperience;
		processExpReq.owner = GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerControlledGameObject();
		processExpReq.m_entity = evt.targetID;
		GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'PlayerDevelopmentSystem' ).QueueRequest( processExpReq );
		CheckVForVendettaAchievement( evt );
		GameInstance.GetRazerChromaEffectsSystem( GetGame() ).PlayAnimation( 'EnemyKill', false );
	}

	protected event OnRewindableSectionEvent( evt : scnRewindableSectionEvent )
	{
		var psmAdd : PSMAddOnDemandStateMachine;
		var psmRem : PSMRemoveOnDemandStateMachine;
		var inBD : SetIsInBraindance;
		var stateMachineIdentifierRem : StateMachineIdentifier;
		var bdSystem : BraindanceSystem;
		inBD = new SetIsInBraindance;
		bdSystem = ( ( BraindanceSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'BraindanceSystem' ) ) );
		if( evt.active )
		{
			psmAdd = new PSMAddOnDemandStateMachine;
			psmAdd.stateMachineName = 'BraindanceControls';
			QueueEvent( psmAdd );
			DisableCameraBobbing( true );
			inBD.newState = true;
			bdSystem.QueueRequest( inBD );
			GameInstance.GetAudioSystem( GetGame() ).SetBDCameraListenerOverride( true );
		}
		else
		{
			psmRem = new PSMRemoveOnDemandStateMachine;
			stateMachineIdentifierRem.definitionName = 'BraindanceControls';
			psmRem.stateMachineIdentifier = stateMachineIdentifierRem;
			QueueEvent( psmRem );
			DisableCameraBobbing( false );
			inBD.newState = false;
			bdSystem.QueueRequest( inBD );
			GameInstance.GetAudioSystem( GetGame() ).SetBDCameraListenerOverride( false );
		}
	}

	public const function IsInCombat() : Bool
	{
		return m_inCombat;
	}

	public const function IsNaked() : Bool
	{
		if( ( ItemID.IsValid( EquipmentSystem.GetData( GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject() ).GetActiveItem( gamedataEquipmentArea.Legs ) ) || ItemID.IsValid( EquipmentSystem.GetData( GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject() ).GetActiveItem( gamedataEquipmentArea.OuterChest ) ) ) || ItemID.IsValid( EquipmentSystem.GetData( GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject() ).GetActiveItem( gamedataEquipmentArea.InnerChest ) ) )
		{
			return false;
		}
		return true;
	}

	public const function IsMoving() : Bool
	{
		return IsMovingHorizontally() || IsMovingVertically();
	}

	public const function IsMovingHorizontally() : Bool
	{
		return GetPlayerStateMachineBlackboard().GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsMovingHorizontally );
	}

	public const function IsMovingVertically() : Bool
	{
		return GetPlayerStateMachineBlackboard().GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsMovingVertically );
	}

	protected event OnZoneChange( value : Variant )
	{
		var securityZoneData : SecurityAreaData;
		securityZoneData = ( ( SecurityAreaData )value );
		GameInstance.GetTelemetrySystem( GetGame() ).LogPlayerInDangerousArea( securityZoneData.securityAreaType == ESecurityAreaType.RESTRICTED || securityZoneData.securityAreaType == ESecurityAreaType.DANGEROUS );
	}

	private function SetWarningMessage( const message : ref< String >, optional msgType : SimpleMessageType )
	{
		var warningMsg : SimpleScreenMessage;
		warningMsg.isShown = true;
		warningMsg.duration = 5.0;
		warningMsg.message = message;
		if( msgType != SimpleMessageType.Undefined )
		{
			warningMsg.type = msgType;
		}
		GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_Notifications ).SetVariant( GetAllBlackboardDefs().UI_Notifications.WarningMessage, warningMsg, true );
	}

	private function StartProcessingVForVendettaAchievement( deathInstigator : GameObject )
	{
		if( ( ( GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.HasSecondHeart ) > 0.0 ) && ( ( NPCPuppet )( deathInstigator ) ) ) && ( m_NPCDeathInstigator == NULL ) )
		{
			m_NPCDeathInstigator = ( ( NPCPuppet )( deathInstigator ) );
		}
	}

	private function CreateVendettaTimeDelayEvent()
	{
		var vendettaTimeDelayEvent : FinishedVendettaTimeEvent;
		if( m_NPCDeathInstigator )
		{
			vendettaTimeDelayEvent = new FinishedVendettaTimeEvent;
			GameInstance.GetDelaySystem( GetGame() ).DelayEvent( this, vendettaTimeDelayEvent, 5.0 );
		}
	}

	protected event OnFinishedVendettaTimeEvent( evt : FinishedVendettaTimeEvent )
	{
		m_NPCDeathInstigator = NULL;
	}

	private const function CheckVForVendettaAchievement( evt : TargetNeutraliziedEvent )
	{
		var achievementRequest : AddAchievementRequest;
		var dataTrackingSystem : DataTrackingSystem;
		var achievement : gamedataAchievement;
		dataTrackingSystem = ( ( DataTrackingSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'DataTrackingSystem' ) ) );
		achievement = gamedataAchievement.VForVendetta;
		if( !( m_NPCDeathInstigator ) || ( m_NPCDeathInstigator.GetEntityID() != evt.targetID ) )
		{
			return;
		}
		achievementRequest = new AddAchievementRequest;
		achievementRequest.achievement = achievement;
		dataTrackingSystem.QueueRequest( achievementRequest );
	}

	protected event OnProcessVendettaAchievementEvent( evt : ProcessVendettaAchievementEvent )
	{
		StartProcessingVForVendettaAchievement( evt.deathInstigator );
	}

	public const override function GetNetworkLinkSlotName() : CName
	{
		return 'Chest';
	}

	public const override function IsNetworkLinkDynamic() : Bool
	{
		return true;
	}

	private function RegisterRemoteMappin()
	{
		var data : MappinData;
		data.mappinType = T"Mappins.CPO_RemotePlayerMappinDefinition";
		data.variant = gamedataMappinVariant.CPO_RemotePlayerVariant;
		data.active = true;
		m_remoteMappinId = GameInstance.GetMappinSystem( GetGame() ).RegisterRemotePlayerMappin( data, ( ( GameObject )( this ) ) );
	}

	private function UnregisterRemoteMappin()
	{
		if( m_remoteMappinId.value != ( ( Uint64 )( 0 ) ) )
		{
			GameInstance.GetMappinSystem( GetGame() ).UnregisterMappin( m_remoteMappinId );
			m_remoteMappinId.value = 0;
		}
	}

	protected event OnRegisterFastTravelPoints( evt : RegisterFastTravelPointsEvent )
	{
		var i : Int32;
		var request : RegisterFastTravelPointRequest;
		for( i = 0; i < evt.fastTravelNodes.Size(); i += 1 )
		{
			request = new RegisterFastTravelPointRequest;
			request.pointData = evt.fastTravelNodes[ i ];
			request.requesterID = GetEntityID();
			GetFastTravelSystem().QueueRequest( request );
		}
	}

	public const override function ShouldShowScanner() : Bool
	{
		if( IsMultiplayer() )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	protected event OnWoundedInstigated( evt : WoundedInstigated )
	{
		var value : Uint32;
		value = GetPlayerPerkDataBlackboard().GetUint( GetAllBlackboardDefs().PlayerPerkData.WoundedInstigated );
		GetPlayerPerkDataBlackboard().SetUint( GetAllBlackboardDefs().PlayerPerkData.WoundedInstigated, value + ( ( Uint32 )( 1 ) ) );
	}

	protected event OnDismembermentInstigated( evt : DismembermentInstigated )
	{
		var oldInfo : DismembermentInstigatedInfo;
		var info : DismembermentInstigatedInfo;
		oldInfo = ( ( DismembermentInstigatedInfo )( GetPlayerPerkDataBlackboard().GetVariant( GetAllBlackboardDefs().PlayerPerkData.DismembermentInstigated ) ) );
		info.value = oldInfo.value + 1;
		info.targetPosition = evt.targetPosition;
		info.attackPosition = evt.attackPosition;
		info.wasTargetAlreadyDead = evt.targetIsDead;
		info.wasTargetAlreadyDefeated = evt.targetIsDefeated;
		info.weaponRecord = evt.weaponRecord;
		info.bodyPart = evt.bodyPart;
		info.attackType = evt.attackType;
		info.attackSubtype = evt.attackSubtype;
		info.attackIsExplosion = evt.attackIsExplosion;
		info.target = evt.target;
		info.timeSinceDeath = evt.timeSinceDeath;
		info.timeSinceDefeat = evt.timeSinceDefeat;
		GetPlayerPerkDataBlackboard().SetVariant( GetAllBlackboardDefs().PlayerPerkData.DismembermentInstigated, info );
		if( ( ( PlayerDevelopmentSystem.GetInstance( this ).IsNewPerkBought( this, gamedataNewPerkType.Espionage_Central_Milestone_1 ) == 1 ) && evt.weaponRecord.ItemType().Type() == gamedataItemType.Cyb_MantisBlades ) && !( evt.targetIsDead ) )
		{
			StatusEffectHelper.ApplyStatusEffect( this, T"BaseStatusEffect.Espionage_Central_Milestone_1_Buff_MantisBlades" );
		}
	}

	private function ToggleFocusedGrenadeShootingPerk( activate : Bool )
	{
		var projectileSystem : ProjectileSystem;
		var projectileComponents : array< ProjectileComponent >;
		var aimCandidateMaxRadiusSquared : Float;
		var aimCandidateRadiusSquared : Float;
		var bestAimCandidate : WeaponGrenade;
		var bestAimCandidateRadiusSquared : Float;
		var cameraForward : Vector4;
		var playerToGrenade : Vector4;
		var projectionLength : Float;
		var projectionVector : Vector4;
		var projectileEntity : weak< Entity >;
		var asWeaponGrenade : WeaponGrenade;
		var asBaseGrenade : BaseGrenade;
		var i : Int32;
		projectileSystem = GameInstance.GetProjectileSystem( GetGame() );
		aimCandidateMaxRadiusSquared = 6.0;
		bestAimCandidateRadiusSquared = aimCandidateMaxRadiusSquared;
		projectileComponents = projectileSystem.GetRegisteredComponents();
		for( i = 0; i < projectileComponents.Size(); i += 1 )
		{
			projectileEntity = projectileComponents[ i ].GetEntity();
			if( !( projectileEntity ) )
			{
				continue;
			}
			asWeaponGrenade = ( ( WeaponGrenade )( projectileEntity ) );
			if( !( asWeaponGrenade ) )
			{
				continue;
			}
			if( !( activate ) )
			{
				asWeaponGrenade.DisableFocusedShootingHighlight();
				if( asWeaponGrenade.IsGrenadeTargetedWithFocusedShootingPerk() )
				{
					asWeaponGrenade.DeactivateFocusedShootingAim();
				}
				continue;
			}
			asWeaponGrenade.EnableFocusedShootingHighlight();
			playerToGrenade = projectileEntity.GetWorldPosition() - GetWorldPosition();
			cameraForward = Matrix.GetDirectionVector( GetFPPCameraComponent().GetLocalToWorld() );
			projectionLength = Vector4.Dot( cameraForward, playerToGrenade ) / Vector4.Length( cameraForward );
			if( projectionLength <= 0.0 )
			{
				continue;
			}
			projectionVector = cameraForward * projectionLength;
			aimCandidateRadiusSquared = Vector4.LengthSquared( playerToGrenade ) - Vector4.LengthSquared( projectionVector );
			if( ( aimCandidateRadiusSquared < bestAimCandidateRadiusSquared ) && ( aimCandidateRadiusSquared <= aimCandidateMaxRadiusSquared ) )
			{
				bestAimCandidateRadiusSquared = aimCandidateRadiusSquared;
				bestAimCandidate = asWeaponGrenade;
			}
		}
		if( !( activate ) )
		{
			return;
		}
		if( !( bestAimCandidate ) )
		{
			return;
		}
		bestAimCandidate.TriggerLookAtThisGrenade();
		asBaseGrenade = ( ( BaseGrenade )( asWeaponGrenade ) );
		if( asBaseGrenade )
		{
			asBaseGrenade.SetCanBeShot( true );
		}
	}

	public function GetPrimaryTargetingComponent() : TargetingComponent
	{
		return m_primaryTargetingComponent;
	}

	public function GetBreachFinderComponent() : BreachFinderComponent
	{
		return m_breachFinderComponent;
	}

	public static function SetLevel( inst : GameInstance, const stringType : ref< String >, const stringVal : ref< String >, levelGainReason : telemetryLevelGainReason )
	{
		var newLevel : Int32;
		var profType : gamedataProficiencyType;
		var request : SetProficiencyLevel;
		var inventory : array< weak< gameItemData > >;
		var i : Int32;
		var itemData : weak< gameItemData >;
		var statMod : gameStatModifierData;
		profType = ( ( gamedataProficiencyType )( ( ( Int32 )( EnumValueFromString( "gamedataProficiencyType", stringType ) ) ) ) );
		newLevel = StringToInt( stringVal );
		request = new SetProficiencyLevel;
		request.Set( GetPlayer( inst ), newLevel, profType, levelGainReason );
		GameInstance.GetScriptableSystemsContainer( inst ).Get( 'PlayerDevelopmentSystem' ).QueueRequest( request );
		if( profType == gamedataProficiencyType.Level )
		{
			GameInstance.GetTransactionSystem( inst ).GetItemList( GetPlayer( inst ), inventory );
			statMod = RPGManager.CreateStatModifier( gamedataStatType.PowerLevel, gameStatModifierType.Additive, StringToFloat( stringVal ) );
			for( i = 0; i < inventory.Size(); i += 1 )
			{
				itemData = inventory[ i ];
				if( RPGManager.GetItemRecord( itemData.GetID() ) )
				{
					GameInstance.GetStatsSystem( inst ).RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.PowerLevel, true );
					GameInstance.GetStatsSystem( inst ).AddSavedModifier( itemData.GetStatsObjectID(), statMod );
				}
			}
		}
	}

	public static function SetBuild( inst : GameInstance, const stringType : ref< String >, optional isDebug : Bool )
	{
		var buildType : gamedataBuildType;
		var buildTypeRequest : SetProgressionBuild;
		var buildRequest : questSetProgressionBuildRequest;
		var player : PlayerPuppet;
		var buildInt : Int32;
		buildInt = ( ( Int32 )( EnumValueFromString( "gamedataBuildType", stringType ) ) );
		player = ( ( PlayerPuppet )( GetPlayer( inst ) ) );
		if( buildInt >= 0 )
		{
			buildType = ( ( gamedataBuildType )( buildInt ) );
			buildTypeRequest = new SetProgressionBuild;
			buildTypeRequest.m_isDebug = isDebug;
			buildTypeRequest.Set( player, buildType );
			GameInstance.GetScriptableSystemsContainer( inst ).Get( 'PlayerDevelopmentSystem' ).QueueRequest( buildTypeRequest );
		}
		else
		{
			buildRequest = new questSetProgressionBuildRequest;
			buildRequest.buildID = TDBID.Create( stringType );
			buildRequest.owner = player;
			GameInstance.GetScriptableSystemsContainer( inst ).Get( 'PlayerDevelopmentSystem' ).QueueRequest( buildRequest );
		}
	}

	private function ApplyNPCLevelAndProgressionBuild( npc : weak< GameObject >, actionName : CName )
	{
		var statsSystem : StatsSystem;
		var NPCLevel : Int32;
		var presetBuildLevel : Int32;
		var buildName : String;
		var gameInstance : GameInstance;
		var buildSpacing : Int32;
		gameInstance = GetGame();
		if( npc )
		{
			statsSystem = GameInstance.GetStatsSystem( gameInstance );
			NPCLevel = ( ( Int32 )( statsSystem.GetStatValue( npc.GetEntityID(), gamedataStatType.PowerLevel ) ) );
			buildSpacing = FindBuildSpacing( "gamedataBuildType", "RangedCombat" );
			if( buildSpacing <= 0 )
			{
				return;
			}
			presetBuildLevel = NPCLevel - ( NPCLevel % buildSpacing );
			switch( actionName )
			{
				case 'ApplyNPCLevelToPlayerRanged':
					buildName = "RangedCombat";
				break;
				case 'ApplyNPCLevelToPlayerMelee':
					buildName = "MeleeCombat";
				break;
				case 'ApplyNPCLevelToPlayerNetrunner':
					buildName = "CombatNetrunner";
				break;
				default:
					break;
			}
			if( buildName == "" )
			{
				return;
			}
			buildName = buildName + presetBuildLevel;
			AddFact( gameInstance, 'full_rpg_progression_on' );
			SetBuild( gameInstance, buildName );
			SetLevel( gameInstance, "Level", IntToString( NPCLevel ), telemetryLevelGainReason.Ignore );
		}
	}

	protected event OnMeleeHitEvent( evt : MeleeHitEvent )
	{
		var slowMoEnabled : Bool;
		var slowMoDelay : Float;
		var slowMoEvent : MeleeHitSlowMoEvent;
		var validTarget : Bool;
		var targetAsPuppet : ScriptedPuppet;
		var isExhuasted : Bool;
		var bodySlamBump : Int32;
		var isBodySlamming : Bool;
		slowMoEnabled = TDB.GetBool( T"timeSystem.meleeHit.enabled", false );
		if( evt.isStrongAttack )
		{
			slowMoEnabled = TDB.GetBool( T"timeSystem.meleeHitStrong.enabled", false );
			slowMoDelay = TDB.GetFloat( T"timeSystem.meleeHitStrong.delay", 0.1 );
		}
		else
		{
			slowMoEnabled = TDB.GetBool( T"timeSystem.meleeHit.enabled", false );
			slowMoDelay = TDB.GetFloat( T"timeSystem.meleeHit.delay", 0.1 );
		}
		isBodySlamming = GetPlayerStateMachineBlackboard().GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsInBodySlamState );
		if( slowMoEnabled || isBodySlamming )
		{
			targetAsPuppet = ( ( ScriptedPuppet )( evt.target ) );
		}
		if( slowMoEnabled )
		{
			validTarget = ( targetAsPuppet && ScriptedPuppet.IsAlive( targetAsPuppet ) ) || ( ( WeakspotObject )( evt.target ) );
			isExhuasted = StatusEffectSystem.ObjectHasStatusEffect( this, PlayerStaminaHelpers.GetExhaustedStatusEffectID() );
			if( ( ( !( evt.hitBlocked ) && ( evt.instigator == this ) ) && !( isExhuasted ) ) && validTarget )
			{
				slowMoEvent = new MeleeHitSlowMoEvent;
				slowMoEvent.isStrongAttack = evt.isStrongAttack;
				GameInstance.GetDelaySystem( GetGame() ).DelayEvent( this, slowMoEvent, slowMoDelay );
			}
		}
		if( isBodySlamming && targetAsPuppet )
		{
			bodySlamBump = GetPlayerStateMachineBlackboard().GetInt( GetAllBlackboardDefs().PlayerStateMachine.BodySlamBump );
			bodySlamBump += 1;
			GetPlayerStateMachineBlackboard().SetInt( GetAllBlackboardDefs().PlayerStateMachine.BodySlamBump, bodySlamBump, true );
		}
	}

	protected event OnMeleeHitSloMo( evt : MeleeHitSlowMoEvent )
	{
		var easeOutCurve : CName;
		var easeInCurve : CName;
		var dilation : Float;
		var duration : Float;
		if( evt.isStrongAttack )
		{
			dilation = TDB.GetFloat( T"timeSystem.meleeHitStrong.timeDilation", 0.1 );
			duration = TDB.GetFloat( T"timeSystem.meleeHitStrong.duration", 0.1 );
			easeInCurve = TDB.GetCName( T"timeSystem.meleeHitStrong.easeInCurve", '' );
			easeOutCurve = TDB.GetCName( T"timeSystem.meleeHitStrong.easeOutCurve", '' );
		}
		else
		{
			dilation = TDB.GetFloat( T"timeSystem.meleeHit.timeDilation", 0.1 );
			duration = TDB.GetFloat( T"timeSystem.meleeHit.duration", 0.1 );
			easeInCurve = TDB.GetCName( T"timeSystem.meleeHit.easeInCurve", '' );
			easeOutCurve = TDB.GetCName( T"timeSystem.meleeHit.easeOutCurve", '' );
		}
		if( duration < 0.0 )
		{
			duration = 0.1;
		}
		GameInstance.GetTimeSystem( GetGame() ).SetTimeDilation( 'meleeHit', dilation, duration, easeInCurve, easeOutCurve );
	}

	private function FindBuildSpacing( const enumType : ref< String >, const buildNameStringPart : ref< String > ) : Int32
	{
		var i : Int32;
		var fullEnumString : String;
		var buildInt : Int32;
		for( i = 1; i <= 20; i += 1 )
		{
			fullEnumString = buildNameStringPart + i;
			buildInt = ( ( Int32 )( EnumValueFromString( enumType, fullEnumString ) ) );
			if( buildInt >= 0 )
			{
				return i;
			}
		}
		return -1;
	}

	private function GotKeycardNotification()
	{
		var notify : AuthorisationNotificationEvent;
		notify = new AuthorisationNotificationEvent;
		notify.type = gameuiAuthorisationNotificationType.GotKeycard;
		QueueEvent( notify );
	}

	protected event OnHackTargetEvent( evt : HackTargetEvent )
	{
		super.OnHackTargetEvent( evt );
		SetIsBeingRevealed( evt.settings.isRevealPositionAction );
		m_attackingNetrunnerID = evt.netrunnerID;
	}

	protected event OnCarHitPlayer( evt : OnCarHitPlayer )
	{
		var attackContext : AttackInitContext;
		var attack : IAttack;
		var hitEvent : gameHitEvent;
		var soundEvent : SoundPlayEvent;
		var broadcaster : StimBroadcasterComponent;
		var vehicleObject : VehicleObject;
		var hornEvt : VehicleHornProbsEvent;
		var seperationImpulseEvt : PSMImpulse;
		var currTime : Float;
		if( StatusEffectSystem.ObjectHasStatusEffect( this, T"BaseStatusEffect.VehicleKnockdown" ) || StatusEffectSystem.ObjectHasStatusEffect( this, T"BaseStatusEffect.BikeKnockdown" ) )
		{
			return false;
		}
		currTime = EngineTime.ToFloat( GameInstance.GetTimeSystem( GetGame() ).GetSimTime() );
		if( currTime < ( m_vehicleKnockdownTimestamp + 5.0 ) )
		{
			return false;
		}
		m_vehicleKnockdownTimestamp = currTime;
		hitEvent = new gameHitEvent;
		hitEvent.attackData = new AttackData;
		hitEvent.target = this;
		attackContext.record = TweakDBInterface.GetAttackRecord( T"Attacks.CarHitPlayer" );
		attackContext.instigator = this;
		attackContext.source = this;
		attack = IAttack.Create( attackContext );
		hitEvent.attackData.SetAttackDefinition( attack );
		hitEvent.attackData.AddFlag( hitFlag.FriendlyFire, 'vehicle_collision' );
		hitEvent.attackData.AddFlag( hitFlag.CanDamageSelf, 'vehicle_collision' );
		hitEvent.attackData.SetSource( this );
		hitEvent.attackData.SetInstigator( this );
		hitEvent.attackData.SetAttackDefinition( attack );
		hitEvent.hitDirection = evt.hitDirection;
		GameInstance.GetDamageSystem( GetGame() ).QueueHitEvent( hitEvent, this );
		if( evt.seperationImpulse != Vector4( 0.0, 0.0, 0.0, 0.0 ) )
		{
			seperationImpulseEvt = new PSMImpulse;
			seperationImpulseEvt.id = 'impulse';
			seperationImpulseEvt.impulse = evt.seperationImpulse;
			QueueEvent( seperationImpulseEvt );
		}
		soundEvent = new SoundPlayEvent;
		soundEvent.soundName = 'v_col_player_impact';
		hitEvent.target.QueueEvent( soundEvent );
		broadcaster = GetStimBroadcasterComponent();
		broadcaster.TriggerSingleBroadcast( this, gamedataStimType.CrowdIllegalAction, 4.0 );
		vehicleObject = ( ( VehicleObject )( GameInstance.FindEntityByID( GetGame(), evt.carId ) ) );
		if( vehicleObject.HasPassengers() && !( vehicleObject.IsVehicleRemoteControlled() ) )
		{
			hornEvt = new VehicleHornProbsEvent;
			hornEvt.honkMinTime = 1.0;
			hornEvt.honkMaxTime = 2.0;
			hornEvt.probability = 0.80000001;
			vehicleObject.QueueEvent( hornEvt );
		}
	}

	protected event OnDistrictChanged( evt : PlayerEnteredNewDistrictEvent )
	{
		m_gunshotRange = evt.gunshotRange;
		m_explosionRange = evt.explosionRange;
	}

	public const function GetGunshotRange() : Float
	{
		return m_gunshotRange;
	}

	public const function GetExplosionRange() : Float
	{
		return m_explosionRange;
	}

	public const function GetMinigamePrograms() : array< MinigameProgramData >
	{
		return GetPS().GetMinigamePrograms();
	}

	protected event OnUpdateMiniGameProgramsEvent( evt : UpdateMiniGameProgramsEvent )
	{
		UpdateMinigamePrograms( evt.program, evt.add );
	}

	private function UpdateMinigamePrograms( program : MinigameProgramData, add : Bool )
	{
		var evt : StoreMiniGameProgramEvent;
		evt = new StoreMiniGameProgramEvent;
		evt.program = program;
		evt.add = add;
		SendEventToDefaultPS( evt );
	}

	private function RestoreMinigamePrograms()
	{
		var programs : array< MinigameProgramData >;
		programs = GetPS().GetMinigamePrograms();
		GetMinigameBlackboard().SetVariant( GetAllBlackboardDefs().HackingMinigame.PlayerPrograms, programs );
	}

	private function GetMinigameBlackboard() : IBlackboard
	{
		return GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().HackingMinigame );
	}

	private function InitInterestingFacts()
	{
		m_interestingFacts.m_zone = 'CityAreaType';
		m_interestingFactsListenersFunctions.m_zone = 'OnZoneFactChanged';
	}

	private function UpdateAimAssist()
	{
		if( !( m_aimAssistUpdateQueued ) )
		{
			m_aimAssistUpdateQueued = true;
			GameInstance.GetDelaySystem( GetGame() ).QueueTask( this, NULL, 'UpdateAimAssistDelayedTask', gameScriptTaskExecutionStage.Any );
		}
	}

	protected export function UpdateAimAssistDelayedTask( data : ScriptTaskData )
	{
		UpdateAimAssistImmediate();
	}

	private function UpdateAimAssistImmediate()
	{
		var inSprint : Bool;
		var inMeleeAssistState : Bool;
		var inLefthandCW : Bool;
		var isExhuasted : Bool;
		m_aimAssistUpdateQueued = false;
		if( m_focusModeActive )
		{
			ApplyAimAssistSettings( AimAssistSettingConfig.Scanning );
			return;
		}
		if( EntityID.IsDefined( m_controllingDeviceID ) )
		{
			ApplyAimAssistSettings( AimAssistSettingConfig.Default );
			return;
		}
		if( m_leftHandCyberwareState == ( ( Int32 )( gamePSMLeftHandCyberware.Charge ) ) )
		{
			ApplyAimAssistSettings( AimAssistSettingConfig.LeftHandCyberwareCharge );
			return;
		}
		inLefthandCW = ( ( m_leftHandCyberwareState != ( ( Int32 )( gamePSMLeftHandCyberware.Default ) ) ) && ( m_leftHandCyberwareState != ( ( Int32 )( gamePSMLeftHandCyberware.Unequip ) ) ) ) && ( m_leftHandCyberwareState != ( ( Int32 )( gamePSMLeftHandCyberware.StartUnequip ) ) );
		if( inLefthandCW )
		{
			ApplyAimAssistSettings( AimAssistSettingConfig.LeftHandCyberware );
			return;
		}
		inSprint = m_locomotionState == ( ( Int32 )( gamePSMLocomotionStates.Sprint ) );
		if( m_vehicleState != gamePSMVehicle.Default )
		{
			if( m_vehicleState == gamePSMVehicle.Combat )
			{
				ApplyAimAssistSettings( AimAssistSettingConfig.VehicleCombat );
				return;
			}
			else if( m_vehicleState == gamePSMVehicle.DriverCombat )
			{
				if( m_equippedRightHandWeapon.IsMelee() )
				{
					if( m_meleeWeaponState == ( ( Int32 )( gamePSMMeleeWeapon.ThrowAttack ) ) )
					{
						ApplyAimAssistSettings( AimAssistSettingConfig.Vehicle );
						return;
					}
					if( m_inDriverCombatTPP )
					{
						ApplyAimAssistSettings( AimAssistSettingConfig.DriverCombatMeleeTPP );
						return;
					}
					else
					{
						ApplyAimAssistSettings( AimAssistSettingConfig.Vehicle );
						return;
					}
				}
				if( m_inMountedWeaponVehicle )
				{
					if( m_driverCombatWeaponType == gamedataItemType.Wea_VehicleMissileLauncher )
					{
						if( m_isAiming )
						{
							ApplyAimAssistSettings( AimAssistSettingConfig.DriverCombatMissilesAiming );
							return;
						}
						else
						{
							ApplyAimAssistSettings( AimAssistSettingConfig.DriverCombatMissiles );
							return;
						}
					}
					else
					{
						ApplyAimAssistSettings( AimAssistSettingConfig.Vehicle );
						return;
					}
				}
				if( m_isAiming )
				{
					ApplyAimAssistSettings( AimAssistSettingConfig.DriverCombatAiming );
					return;
				}
				if( m_inDriverCombatTPP )
				{
					ApplyAimAssistSettings( AimAssistSettingConfig.DriverCombatTPP );
					return;
				}
				ApplyAimAssistSettings( AimAssistSettingConfig.DriverCombat );
				return;
			}
			ApplyAimAssistSettings( AimAssistSettingConfig.Vehicle );
			return;
		}
		if( m_isInBodySlam || ( m_combatGadgetState != ( ( Int32 )( gamePSMCombatGadget.Default ) ) ) )
		{
			ApplyAimAssistSettings( AimAssistSettingConfig.Off );
			return;
		}
		if( ( ( ( !( inSprint ) && m_equippedRightHandWeapon ) && m_equippedRightHandWeapon.IsMelee() ) && ( !( m_isAiming ) || !( MeleeTransition.CanThrowWeaponObject( this, m_equippedRightHandWeapon ) ) ) ) && ( m_meleeWeaponState != ( ( Int32 )( gamePSMMeleeWeapon.ThrowAttack ) ) ) )
		{
			inMeleeAssistState = ( ( m_meleeWeaponState == ( ( Int32 )( gamePSMMeleeWeapon.Block ) ) ) || ( m_meleeWeaponState == ( ( Int32 )( gamePSMMeleeWeapon.Deflect ) ) ) ) || ( m_meleeWeaponState == ( ( Int32 )( gamePSMMeleeWeapon.DeflectAttack ) ) );
			ApplyAimAssistSettings( ( ( inMeleeAssistState ) ? ( AimAssistSettingConfig.MeleeCombat ) : ( AimAssistSettingConfig.MeleeCombatIdle ) ) );
			return;
		}
		if( m_doingQuickMelee )
		{
			ApplyAimAssistSettings( AimAssistSettingConfig.QuickMelee );
			return;
		}
		isExhuasted = StatusEffectSystem.ObjectHasStatusEffect( this, PlayerStaminaHelpers.GetExhaustedStatusEffectID() );
		if( isExhuasted )
		{
			ApplyAimAssistSettings( AimAssistSettingConfig.Exhausted );
			return;
		}
		if( m_isAiming )
		{
			m_weaponZoomLevel = GameInstance.GetStatsSystem( GetGame() ).GetStatValue( GetEntityID(), gamedataStatType.ZoomLevel );
			if( m_weaponZoomLevel <= 1.29999995 )
			{
				ApplyAimAssistSettings( ( ( m_hasKiroshiOpticsFragment ) ? ( AimAssistSettingConfig.AimingLimbCyber ) : ( AimAssistSettingConfig.Aiming ) ) );
			}
			else if( m_weaponZoomLevel <= 2.5 )
			{
				ApplyAimAssistSettings( ( ( m_hasKiroshiOpticsFragment ) ? ( AimAssistSettingConfig.AimingLimbCyberZoomLevel1 ) : ( AimAssistSettingConfig.ZoomLevel1 ) ) );
			}
			else
			{
				ApplyAimAssistSettings( ( ( m_hasKiroshiOpticsFragment ) ? ( AimAssistSettingConfig.AimingLimbCyberZoomLevel2 ) : ( AimAssistSettingConfig.ZoomLevel2 ) ) );
			}
			return;
		}
		if( inSprint )
		{
			ApplyAimAssistSettings( AimAssistSettingConfig.Sprinting );
			return;
		}
		ApplyAimAssistSettings( ( ( m_hasKiroshiOpticsFragment ) ? ( AimAssistSettingConfig.LimbCyber ) : ( AimAssistSettingConfig.Default ) ) );
	}

	public function GetAimAssistLevel() : EAimAssistLevel
	{
		return m_aimAssistListener.GetAimAssistLevel();
	}

	public function IsAimSnapEnabled() : Bool
	{
		return m_aimAssistListener.GetAimSnapEnabled();
	}

	[ profile = "" ]
	public function ApplyAimAssistSettings( config : AimAssistSettingConfig )
	{
		var settingsRecord : weak< AimAssistSettings_Record >;
		var configRecord : weak< AimAssistConfigPreset_Record >;
		var aimAssistLevel : EAimAssistLevel;
		aimAssistLevel = EAimAssistLevel.Standard;
		if( config == m_aimAssistListener.m_currentConfig )
		{
			return;
		}
		else if( config != AimAssistSettingConfig.Count )
		{
			m_aimAssistListener.m_currentConfig = config;
		}
		if( m_aimAssistListener.m_currentConfig != AimAssistSettingConfig.Count )
		{
			settingsRecord = m_aimAssistListener.m_settingsRecords[ ( ( Int32 )( m_aimAssistListener.m_currentConfig ) ) ];
			if( settingsRecord )
			{
				if( m_aimAssistListener.m_currentConfig == AimAssistSettingConfig.MeleeCombat || m_aimAssistListener.m_currentConfig == AimAssistSettingConfig.MeleeCombatIdle )
				{
					aimAssistLevel = m_aimAssistListener.GetAimAssistMeleeLevel();
				}
				else if( ( ( ( m_aimAssistListener.m_currentConfig == AimAssistSettingConfig.DriverCombat || m_aimAssistListener.m_currentConfig == AimAssistSettingConfig.DriverCombatTPP ) || m_aimAssistListener.m_currentConfig == AimAssistSettingConfig.DriverCombatMissiles ) || m_aimAssistListener.m_currentConfig == AimAssistSettingConfig.DriverCombatMissilesAiming ) || m_aimAssistListener.m_currentConfig == AimAssistSettingConfig.DriverCombatMeleeTPP )
				{
					if( m_aimAssistListener.GetAimAssistDriverCombatEnabled() )
					{
						aimAssistLevel = EAimAssistLevel.Light;
					}
					else
					{
						aimAssistLevel = EAimAssistLevel.Off;
					}
				}
				else
				{
					aimAssistLevel = m_aimAssistListener.GetAimAssistLevel();
				}
				if( aimAssistLevel == EAimAssistLevel.Off )
				{
					configRecord = settingsRecord.Off();
				}
				else if( aimAssistLevel == EAimAssistLevel.Light )
				{
					configRecord = settingsRecord.Light();
				}
				else if( aimAssistLevel == EAimAssistLevel.Heavy )
				{
					configRecord = settingsRecord.Heavy();
				}
				else
				{
					configRecord = settingsRecord.Standard();
				}
				GameInstance.GetTargetingSystem( GetGame() ).SetAimAssistConfig( this, configRecord.GetID() );
			}
		}
	}

	public const function ShouldAllowCycleToFistCyberware() : Bool
	{
		return m_accessibilityControlsListener.GetAllowCycleToFistCyberware();
	}

	private function RegisterInterestingFactsListeners()
	{
		InitInterestingFacts();
		m_interestingFactsListenersIds.m_zone = GameInstance.GetQuestsSystem( GetGame() ).RegisterListener( m_interestingFacts.m_zone, this, m_interestingFactsListenersFunctions.m_zone );
		InvalidateZone();
	}

	private function UnregisterInterestingFactsListeners()
	{
		GameInstance.GetQuestsSystem( GetGame() ).UnregisterListener( m_interestingFacts.m_zone, m_interestingFactsListenersIds.m_zone );
	}

	public function SetBlackboardIntVariable( id : BlackboardID_Int, value : Int32 )
	{
		var blackboardSystem : BlackboardSystem;
		var blackboard : IBlackboard;
		blackboardSystem = GameInstance.GetBlackboardSystem( GetGame() );
		blackboard = blackboardSystem.GetLocalInstanced( GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		if( blackboard )
		{
			blackboard.SetInt( id, value );
		}
	}

	private function InvalidateZone()
	{
		OnZoneFactChanged( GameInstance.GetQuestsSystem( GetGame() ).GetFact( m_interestingFacts.m_zone ) );
	}

	public function GetStaminaValueUnsafe() : Float
	{
		return m_staminaListener.GetStaminaValue();
	}

	public function GetStaminaPercUnsafe() : Float
	{
		return m_staminaListener.GetStaminaPerc();
	}

	public function IsExhausted() : Bool
	{
		return StatusEffectSystem.ObjectHasStatusEffect( this, PlayerStaminaHelpers.GetExhaustedStatusEffectID() );
	}

	public function GetHealingItemCharges() : Int32
	{
		return m_HealingItemsChargeStatListener.GetCharges();
	}

	public function GetHealingItemUseCost() : Int32
	{
		return m_HealingItemsChargeStatListener.GetRechargeDuration();
	}

	public function HealingItemChargesAreOnMax() : Bool
	{
		return ( GetHealingItemUseCost() * GetHealingItemCharges() ) == m_HealingItemsChargeStatListener.MaxStatPoolValue();
	}

	public function GetGrenadeCharges() : Int32
	{
		return m_GrenadesChargeStatListener.GetCharges();
	}

	public function GetGrenadeCharges( item : Grenade_Record ) : Int32
	{
		return m_GrenadesChargeStatListener.GetCharges( item );
	}

	public function GrenadeChargesAreOnMax() : Bool
	{
		return ( GetGrenadeThrowCostClean() * GetGrenadeCharges() ) == m_GrenadesChargeStatListener.MaxStatPoolValue();
	}

	public function GetGrenadeThrowCost() : Int32
	{
		return m_GrenadesChargeStatListener.GetRechargeDuration();
	}

	public function GetGrenadeThrowCostClean() : Int32
	{
		return m_GrenadesChargeStatListener.GetRechargeDurationClean();
	}

	public function GetProjectileLauncherCharges() : Int32
	{
		return m_ProjectileLauncherChargeStatListener.GetCharges();
	}

	public function GetProjectileLauncherShootCost() : Int32
	{
		return m_ProjectileLauncherChargeStatListener.GetRechargeDuration();
	}

	public export function OnZoneFactChanged( val : Int32 )
	{
		var zoneType : gameCityAreaType;
		zoneType = GetCurrentZoneType( val );
		switch( zoneType )
		{
			case gameCityAreaType.Undefined:
			{
				OnExitPublicZone();
				OnExitSafeZone();
				OnEnterUndefinedZone();
			}
			break;
			case gameCityAreaType.PublicZone:
			{
				OnExitSafeZone();
				OnEnterPublicZone();
			}
			break;
			case gameCityAreaType.SafeZone:
			{
				OnExitPublicZone();
				OnEnterSafeZone();
			}
			break;
			case gameCityAreaType.RestrictedZone:
			{
				OnExitPublicZone();
				OnExitSafeZone();
				OnEnterRestrictedZone();
			}
			break;
			case gameCityAreaType.DangerousZone:
			{
				OnExitPublicZone();
				OnExitSafeZone();
				OnEnterDangerousZone();
			}
			break;
		}
	}

	public function SetSecurityAreaTypeE3HACK( securityAreaType : ESecurityAreaType )
	{
		m_securityAreaTypeE3HACK = securityAreaType;
		InvalidateZone();
	}

	private function OnEnterUndefinedZone() {}

	private function OnEnterPublicZone()
	{
		var psmEvent : PSMPostponedParameterBool;
		psmEvent = new PSMPostponedParameterBool;
		psmEvent.id = 'InPublicZone';
		psmEvent.value = true;
		psmEvent.aspect = gamestateMachineParameterAspect.Permanent;
		QueueEvent( psmEvent );
		SetBlackboardIntVariable( GetAllBlackboardDefs().PlayerStateMachine.Zones, ( ( Int32 )( gamePSMZones.Public ) ) );
		GameInstance.GetAudioSystem( GetGame() ).NotifyGameTone( 'EnterPublic' );
	}

	private function OnExitPublicZone()
	{
		var psmEvent : PSMPostponedParameterBool;
		psmEvent = new PSMPostponedParameterBool;
		psmEvent.id = 'InPublicZone';
		psmEvent.value = false;
		psmEvent.aspect = gamestateMachineParameterAspect.Permanent;
		QueueEvent( psmEvent );
	}

	private function OnEnterSafeZone()
	{
		var psmEvent : PSMPostponedParameterBool;
		psmEvent = new PSMPostponedParameterBool;
		psmEvent.id = 'ForceEmptyHandsByZone';
		psmEvent.value = true;
		psmEvent.aspect = gamestateMachineParameterAspect.Permanent;
		QueueEvent( psmEvent );
		SetBlackboardIntVariable( GetAllBlackboardDefs().PlayerStateMachine.Zones, ( ( Int32 )( gamePSMZones.Safe ) ) );
		GameInstance.GetAudioSystem( GetGame() ).NotifyGameTone( 'EnterSafe' );
	}

	private function OnExitSafeZone()
	{
		var psmEvent : PSMPostponedParameterBool;
		psmEvent = new PSMPostponedParameterBool;
		psmEvent.id = 'ForceEmptyHandsByZone';
		psmEvent.value = false;
		psmEvent.aspect = gamestateMachineParameterAspect.Permanent;
		QueueEvent( psmEvent );
	}

	private function OnEnterRestrictedZone()
	{
		SetBlackboardIntVariable( GetAllBlackboardDefs().PlayerStateMachine.Zones, ( ( Int32 )( gamePSMZones.Restricted ) ) );
		GameInstance.GetAudioSystem( GetGame() ).NotifyGameTone( 'EnterRestricted' );
	}

	private function OnEnterDangerousZone()
	{
		GameInstance.GetAudioSystem( GetGame() ).NotifyGameTone( 'EnterDangerous' );
		SetBlackboardIntVariable( GetAllBlackboardDefs().PlayerStateMachine.Zones, ( ( Int32 )( gamePSMZones.Dangerous ) ) );
	}

	protected const function GetCurrentZoneType( factValue : Int32 ) : gameCityAreaType
	{
		var questZoneType : gameCityAreaType;
		questZoneType = ( ( gameCityAreaType )( factValue ) );
		if( ( questZoneType == gameCityAreaType.SafeZone || questZoneType == gameCityAreaType.RestrictedZone ) || questZoneType == gameCityAreaType.DangerousZone )
		{
			return questZoneType;
		}
		return GetCurrentSecurityZoneType( this );
	}

	protected const function GetCurrentSecurityZoneType( owner : GameObject ) : gameCityAreaType
	{
		switch( m_securityAreaTypeE3HACK )
		{
			case ESecurityAreaType.SAFE:
				return gameCityAreaType.PublicZone;
			case ESecurityAreaType.RESTRICTED:
				return gameCityAreaType.RestrictedZone;
			case ESecurityAreaType.DANGEROUS:
				return gameCityAreaType.DangerousZone;
			default:
				return gameCityAreaType.PublicZone;
		}
		return gameCityAreaType.PublicZone;
	}

	protected event OnInvalidateVisionModeController( evt : PlayerVisionModeControllerInvalidateEvent )
	{
		m_visionModeController.OnInvalidateActiveState( evt );
	}

	protected event OnInvalidateCombatController( evt : PlayerCombatControllerInvalidateEvent )
	{
		m_combatController.OnInvalidateActiveState( evt );
	}

	protected event OnStartedBeingTrackedAsHostile( evt : StartedBeingTrackedAsHostile )
	{
		m_combatController.OnStartedBeingTrackedAsHostile( evt );
	}

	protected event OnStoppedBeingTrackedAsHostile( evt : StoppedBeingTrackedAsHostile )
	{
		m_combatController.OnStoppedBeingTrackedAsHostile();
	}

	protected event OnCrouchDelayEvent( evt : CrouchDelayEvent )
	{
		m_combatController.OnCrouchDelayEvent( evt );
	}

	public const function GetCachedQuickHackList() : array< PlayerQuickhackData >
	{
		var QHList : array< PlayerQuickhackData >;
		QHList = ( ( array< PlayerQuickhackData > )( GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().PlayerQuickHackData ).GetVariant( GetAllBlackboardDefs().PlayerQuickHackData.CachedQuickHackList ) ) );
		return QHList;
	}

	public static function ChacheQuickHackList( self : weak< PlayerPuppet >, QHList : array< PlayerQuickhackData > )
	{
		if( !( self ) )
		{
			return;
		}
		GameInstance.GetBlackboardSystem( self.GetGame() ).Get( GetAllBlackboardDefs().PlayerQuickHackData ).SetVariant( GetAllBlackboardDefs().PlayerQuickHackData.CachedQuickHackList, QHList, true );
	}

	public static function ChacheQuickHackListCleanup( object : weak< GameObject > )
	{
		var QHList : array< PlayerQuickhackData >;
		if( !( object ) )
		{
			return;
		}
		GameInstance.GetBlackboardSystem( object.GetGame() ).Get( GetAllBlackboardDefs().PlayerQuickHackData ).SetVariant( GetAllBlackboardDefs().PlayerQuickHackData.CachedQuickHackList, QHList, true );
	}

	public static function GetPlayerQuickHackInCyberDeck( player : weak< PlayerPuppet > ) : array< ItemID >
	{
		var systemReplacementID : ItemID;
		var parts : array< SPartSlots >;
		var i : Int32;
		var hacksInDeck : array< ItemID >;
		var installedPartId : TweakDBID;
		systemReplacementID = EquipmentSystem.GetData( player ).GetActiveItem( gamedataEquipmentArea.SystemReplacementCW );
		if( EquipmentSystem.IsCyberdeckEquipped( player ) )
		{
			parts = ItemModificationSystem.GetAllSlots( player, systemReplacementID );
			for( i = 0; i < parts.Size(); i += 1 )
			{
				if( parts[ i ].status == ESlotState.Taken )
				{
					installedPartId = ItemID.GetTDBID( parts[ i ].installedPart );
					if( installedPartId != T"Items.GenericItemRoot" )
					{
						hacksInDeck.PushBack( parts[ i ].installedPart );
					}
				}
			}
		}
		return hacksInDeck;
	}

	public static function GetPlayerQuickHackInCyberDeckTweakDBID( player : weak< PlayerPuppet >, quality : gamedataQuality ) : array< TweakDBID >
	{
		var i : Int32;
		var hacksInDeck : array< ItemID >;
		var hacksInDeckTweakDBID : array< TweakDBID >;
		var tweakId : TweakDBID;
		hacksInDeck = GetPlayerQuickHackInCyberDeck( player );
		for( i = 0; i < hacksInDeck.Size(); i += 1 )
		{
			tweakId = ItemID.GetTDBID( hacksInDeck[ i ] );
			if( TweakDBInterface.GetItemRecord( tweakId ).Quality().Type() == quality )
			{
				hacksInDeckTweakDBID.PushBack( tweakId );
			}
		}
		return hacksInDeckTweakDBID;
	}

	public static function GetPlayerQuickHackInCyberDeckTweakDBID( player : weak< PlayerPuppet > ) : array< TweakDBID >
	{
		var i : Int32;
		var hacksInDeck : array< ItemID >;
		var hacksInDeckTweakDBID : array< TweakDBID >;
		var tweakId : TweakDBID;
		hacksInDeck = GetPlayerQuickHackInCyberDeck( player );
		for( i = 0; i < hacksInDeck.Size(); i += 1 )
		{
			tweakId = ItemID.GetTDBID( hacksInDeck[ i ] );
			hacksInDeckTweakDBID.PushBack( tweakId );
		}
		return hacksInDeckTweakDBID;
	}

	protected event OnDisableVisualOverride( evt : DisableVisualOverride )
	{
		var equipSys : EquipmentSystem;
		var request : QuestDisableWardrobeSetRequest;
		request = new QuestDisableWardrobeSetRequest;
		request.owner = this;
		request.blockReequipping = evt.blockReequipping;
		equipSys = GetEquipmentSystem();
		equipSys.QueueRequest( request );
	}

	protected event OnRestoreVisualOverride( evt : RestoreVisualOverride )
	{
		var equipSys : EquipmentSystem;
		var request : QuestRestoreWardrobeSetRequest;
		request = new QuestRestoreWardrobeSetRequest;
		request.owner = this;
		equipSys = GetEquipmentSystem();
		equipSys.QueueRequest( request );
	}

	protected event OnEnableVisualOverride( evt : EnableVisualOverride )
	{
		var equipSys : EquipmentSystem;
		var request : QuestEnableWardrobeSetRequest;
		request = new QuestEnableWardrobeSetRequest;
		request.owner = this;
		equipSys = GetEquipmentSystem();
		equipSys.QueueRequest( request );
	}

	private const function GetEquipmentSystem() : EquipmentSystem
	{
		return ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'EquipmentSystem' ) ) );
	}

	protected event OnHideVisualSlot( evt : HideVisualSlot )
	{
		var request : QuestHideSlotRequest;
		var equipSys : EquipmentSystem;
		request = new QuestHideSlotRequest;
		equipSys = GetEquipmentSystem();
		request.slot = GetAreaFromEnum( evt.slot );
		request.owner = this;
		equipSys.QueueRequest( request );
	}

	protected event OnRestoreVisualSlot( evt : RestoreVisualSlot )
	{
		var request : QuestRestoreSlotRequest;
		var equipSys : EquipmentSystem;
		request = new QuestRestoreSlotRequest;
		equipSys = GetEquipmentSystem();
		request.slot = GetAreaFromEnum( evt.slot );
		request.owner = this;
		equipSys.QueueRequest( request );
	}

	private const function GetAreaFromEnum( slot : TransmogSlots ) : gamedataEquipmentArea
	{
		switch( slot )
		{
			case TransmogSlots.Head:
				return gamedataEquipmentArea.Head;
			case TransmogSlots.Face:
				return gamedataEquipmentArea.Face;
			case TransmogSlots.InnerChest:
				return gamedataEquipmentArea.InnerChest;
			case TransmogSlots.OuterChest:
				return gamedataEquipmentArea.OuterChest;
			case TransmogSlots.Legs:
				return gamedataEquipmentArea.Legs;
			case TransmogSlots.Feet:
				return gamedataEquipmentArea.Feet;
			default:
				return gamedataEquipmentArea.Invalid;
		}
	}

	protected event OnRefreshQuickhackMenuEvent( evt : RefreshQuickhackMenuEvent )
	{
		var hudManager : HUDManager;
		hudManager = ( ( HUDManager )( GameInstance.GetScriptableSystemsContainer( GetGame() ).Get( 'HUDManager' ) ) );
		if( hudManager )
		{
			QuickhackModule.RequestRefreshQuickhackMenu( GetGame(), hudManager.GetCurrentTargetID() );
		}
	}

	protected event OnHalloweenEvent( evt : HalloweenEvent )
	{
		var broadcaster : StimBroadcasterComponent;
		if( m_sceneTier == GameplayTier.Tier1_FullGameplay || m_sceneTier == GameplayTier.Tier2_StagedGameplay )
		{
			broadcaster = GetStimBroadcasterComponent();
			if( broadcaster )
			{
				broadcaster.TriggerSingleBroadcast( this, gamedataStimType.UndeadCall );
			}
		}
	}

	protected event OnVehicleRadioEvent( evt : VehicleRadioEvent )
	{
		m_pocketRadio.HandleVehicleRadioEvent( evt );
	}

	protected event OnVehicleRadioStationChanged( evt : VehicleRadioStationChanged )
	{
		m_pocketRadio.HandleVehicleRadioStationChanged( evt );
	}

	protected event OnRadioToggleEvent( evt : RadioToggleEvent )
	{
		m_pocketRadio.HandleRadioToggleEvent( evt );
	}

	public function PSIsInDriverCombat() : Bool
	{
		return GetPS().IsInDriverCombat();
	}

	public function SetPSIsInDriverCombat( isInDriverCombat : Bool )
	{
		GetPS().SetIsInDriverCombat( isInDriverCombat );
	}

	public function PSGetPocketRadioStation() : Int32
	{
		return GetPS().GetPocketRadioStation();
	}

	public function PSSetPocketRadioStation( value : Int32 )
	{
		GetPS().SetPocketRadioStation( value );
	}

	public function OnExplosiveDeviceExploded()
	{
		if( ( GameInstance.GetStatsSystem( GetGame() ).GetStatValue( this.GetEntityID(), gamedataStatType.HasGrenadeHack ) == 1.0 ) && ( GameInstance.GetStatsSystem( GetGame() ).GetStatValue( this.GetEntityID(), gamedataStatType.ExplosionKillsRecudeUltimateHacksCost ) == 1.0 ) )
		{
			StatusEffectHelper.ApplyStatusEffect( this, T"BaseStatusEffect.ReduceUltimateSuicideWithGrenadeCostPlusPlus" );
			StatusEffectHelper.RemoveStatusEffect( this, T"BaseStatusEffect.ReduceUltimateSuicideWithGrenadeCost" );
		}
		else if( GameInstance.GetStatsSystem( GetGame() ).GetStatValue( this.GetEntityID(), gamedataStatType.HasGrenadeHack ) == 1.0 )
		{
			StatusEffectHelper.ApplyStatusEffect( this, T"BaseStatusEffect.ReduceUltimateSuicideWithGrenadeCost" );
			StatusEffectHelper.RemoveStatusEffect( this, T"BaseStatusEffect.ReduceUltimateSuicideWithGrenadeCostPlusPlus" );
		}
	}

	private function PromoteOpticalCamoEffectorToCompletelyBlocking()
	{
		var appliedEffectors : array< weak< Effector > >;
		var opticalCamoEffector : AttachOpticalCamoVisionBlockerEffector;
		var i : Int32;
		GameInstance.GetEffectorSystem( GetGame() ).GetEffectorsList( GetEntityID(), appliedEffectors );
		for( i = 0; i < appliedEffectors.Size(); i += 1 )
		{
			opticalCamoEffector = ( ( AttachOpticalCamoVisionBlockerEffector )( appliedEffectors[ i ] ) );
			if( !( opticalCamoEffector ) )
			{
				continue;
			}
			opticalCamoEffector.SetBlockingCompletely( true );
		}
	}

}

struct DismembermentInstigatedInfo
{
	var target : weak< GameObject >;
	var targetPosition : Vector4;
	var attackPosition : Vector4;
	var bodyPart : EHitReactionZone;
	var weaponRecord : WeaponItem_Record;
	var attackType : gamedataAttackType;
	var attackSubtype : gamedataAttackSubtype;
	var value : Uint32;
	var timeSinceDeath : Float;
	var timeSinceDefeat : Float;
	var wasTargetAlreadyDead : Bool;
	var wasTargetAlreadyDefeated : Bool;
	var attackIsExplosion : Bool;
}

class GameLoadedFactReset extends Event
{
}

