class MemoryListener extends CustomValueStatPoolsListener
{
	var m_player : weak< PlayerPuppet >;

	public export override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		var uiBlackboard : IBlackboard;
		var diff : Float;
		var maxHealth : Float;
		uiBlackboard = GameInstance.GetBlackboardSystem( m_player.GetGame() ).Get( GetAllBlackboardDefs().UI_PlayerBioMonitor );
		uiBlackboard.SetFloat( GetAllBlackboardDefs().UI_PlayerBioMonitor.MemoryPercent, newValue );
		if( GameInstance.GetStatsSystem( m_player.GetGame() ).GetStatValue( m_player.GetEntityID(), gamedataStatType.AutomaticReplenishment ) != 0.0 )
		{
			if( FloorF( newValue ) == 0 )
			{
				GameInstance.GetStatPoolsSystem( m_player.GetGame() ).RequestSettingStatPoolValue( m_player.GetEntityID(), gamedataStatPoolType.Memory, 100.0, m_player );
			}
		}
		if( PlayerDevelopmentSystem.GetData( m_player ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Intelligence_Central_Perk_3_2 ) && QuickHackableHelper.IsOverclockedStateActive( m_player ) )
		{
			diff = newValue - oldValue;
			if( diff > 1.0 )
			{
				maxHealth = GameInstance.GetStatsSystem( m_player.GetGame() ).GetStatValue( m_player.GetEntityID(), gamedataStatType.Health );
				if( ( ( maxHealth * diff ) / 100.0 ) > 30.0 )
				{
					diff = ( 30.0 / maxHealth ) * 100.0;
				}
				GameInstance.GetStatPoolsSystem( m_player.GetGame() ).RequestChangingStatPoolValue( m_player.GetEntityID(), gamedataStatPoolType.Health, diff, m_player, true );
			}
		}
	}

}

class DelayedEhxautionSoundClue extends DelayCallback
{
	private var m_puppet : weak< GameObject >;
	private var m_audioEventName : CName;

	public static function Create( puppet : GameObject, audioEventName : CName ) : DelayedEhxautionSoundClue
	{
		var callback : DelayedEhxautionSoundClue;
		callback = new DelayedEhxautionSoundClue;
		callback.m_puppet = puppet;
		callback.m_audioEventName = audioEventName;
		return callback;
	}

	public override function Call()
	{
		GameObject.PlaySound( m_puppet, m_audioEventName );
	}

}

class StaminaListener extends CustomValueStatPoolsListener
{
	private var m_player : weak< PlayerPuppet >;
	private var m_psmAdded : Bool;
	private var m_staminaValue : Float;
	private var m_staminPerc : Float;
	private var m_sfxThreshold : Float;
	private var m_sfxDelay : Float;
	private var m_sfxName : CName;
	private var m_delayID : DelayID;

	public function Init( player : weak< PlayerPuppet > )
	{
		m_psmAdded = false;
		m_player = player;
		m_staminaValue = 100.0;
		m_staminPerc = 100.0;
		m_sfxThreshold = TweakDBInterface.GetFloat( T"player.staminaSFXEvent.threshold", 0.0 );
		m_sfxDelay = TweakDBInterface.GetFloat( T"player.staminaSFXEvent.delay", 0.0 );
		m_sfxName = TweakDBInterface.GetCName( T"player.staminaSFXEvent.sfxName", '' );
	}

	public export override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		var addEvent : PSMAddOnDemandStateMachine;
		var removeEvent : PSMRemoveOnDemandStateMachine;
		var stateMachineIdentifier : StateMachineIdentifier;
		var updateHandlingEvent : UpdateEquippedWeaponsHandlingEvent;
		var soundClue : DelayedEhxautionSoundClue;
		var newValueNormalised : Float;
		var oldValueNormalised : Float;
		newValueNormalised = newValue / 100.0;
		oldValueNormalised = oldValue / 100.0;
		m_staminPerc = newValue;
		m_staminaValue = newValue * percToPoints;
		updateHandlingEvent = new UpdateEquippedWeaponsHandlingEvent;
		updateHandlingEvent.currentStaminaValue = newValue / 100.0;
		m_player.QueueEvent( updateHandlingEvent );
		if( !( m_psmAdded ) && ( newValue < 100.0 ) )
		{
			addEvent = new PSMAddOnDemandStateMachine;
			addEvent.stateMachineName = 'Stamina';
			m_player.QueueEvent( addEvent );
			m_psmAdded = true;
		}
		else if( m_psmAdded && ( newValue >= 100.0 ) )
		{
			stateMachineIdentifier.definitionName = 'Stamina';
			removeEvent = new PSMRemoveOnDemandStateMachine;
			removeEvent.stateMachineIdentifier = stateMachineIdentifier;
			m_player.QueueEvent( removeEvent );
			m_psmAdded = false;
		}
		if( ( newValueNormalised < m_sfxThreshold ) && ( oldValueNormalised >= m_sfxThreshold ) )
		{
			soundClue = DelayedEhxautionSoundClue.Create( m_player, m_sfxName );
			m_delayID = GameInstance.GetDelaySystem( m_player.GetGame() ).DelayCallback( soundClue, m_sfxDelay, false );
		}
		else if( ( newValueNormalised >= m_sfxThreshold ) && ( oldValueNormalised < m_sfxThreshold ) )
		{
			if( m_delayID != GetInvalidDelayID() )
			{
				GameInstance.GetDelaySystem( m_player.GetGame() ).CancelCallback( m_delayID );
				m_delayID = GetInvalidDelayID();
			}
		}
	}

	protected event OnStatPoolMinValueReached( value : Float )
	{
		StatusEffectHelper.RemoveStatusEffect( m_player, T"BaseStatusEffect.OpticalCamoCoolPerkShort" );
	}

	public function GetStaminaValue() : Float
	{
		return m_staminaValue;
	}

	public function GetStaminaPerc() : Float
	{
		return m_staminPerc;
	}

}

class OxygenStatListener extends CustomValueStatPoolsListener
{
	var m_ownerPuppet : weak< PlayerPuppet >;
	var m_oxygenVfxBlackboard : worldEffectBlackboard;

	protected event OnStatPoolValueReached( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		TestOxygenLevel( oldValue, newValue, percToPoints );
	}

	protected event OnStatPoolMinValueReached( value : Float )
	{
		IsOutOfOxygen( true );
	}

	public override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		if( ( newValue > 0.0 ) && ( oldValue <= 0.0 ) )
		{
			IsOutOfOxygen( false );
		}
	}

	public function IsOutOfOxygen( b : Bool )
	{
		var statusEffectSystem : StatusEffectSystem;
		var statusEffectID : TweakDBID;
		statusEffectSystem = GameInstance.GetStatusEffectSystem( m_ownerPuppet.GetGame() );
		statusEffectID = T"BaseStatusEffect.OutOfOxygen";
		if( b == true && !( statusEffectSystem.HasStatusEffect( m_ownerPuppet.GetEntityID(), statusEffectID ) ) )
		{
			statusEffectSystem.ApplyStatusEffect( m_ownerPuppet.GetEntityID(), statusEffectID, m_ownerPuppet.GetRecordID(), m_ownerPuppet.GetEntityID() );
		}
		else if( b == false && statusEffectSystem.HasStatusEffect( m_ownerPuppet.GetEntityID(), statusEffectID ) )
		{
			statusEffectSystem.RemoveStatusEffect( m_ownerPuppet.GetEntityID(), statusEffectID );
		}
	}

	public function TestOxygenLevel( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		var critOxygenThreshold : Float;
		critOxygenThreshold = TDB.GetFloat( T"player.oxygenThresholds.critOxygenThreshold", 10.0 );
		if( ( oldValue > critOxygenThreshold ) && ( newValue <= critOxygenThreshold ) )
		{
			CriticalOxygenLevel( true );
		}
		else if( ( newValue >= critOxygenThreshold ) && ( oldValue <= critOxygenThreshold ) )
		{
			CriticalOxygenLevel( false );
		}
	}

	public function CriticalOxygenLevel( b : Bool )
	{
		if( b == true )
		{
			GameObject.PlaySound( m_ownerPuppet, 'oxygen_critical_start' );
			GameObjectEffectHelper.StartEffectEvent( m_ownerPuppet, 'fx_oxygen_critical', false, m_oxygenVfxBlackboard );
		}
		else
		{
			GameObject.PlaySound( m_ownerPuppet, 'oxygen_critical_stop' );
			GameObjectEffectHelper.BreakEffectLoopEvent( m_ownerPuppet, 'fx_oxygen_critical' );
		}
	}

}

class BaseChargesStatListener extends CustomValueStatPoolsListener
{
	var m_player : weak< PlayerPuppet >;
	var m_playedCueAlready : Bool;
	var m_currentCharges : Int32;
	var m_currentStatPoolValue : Int32;
	var m_rechargeSoundCue : CName;
	var m_statSystem : StatsSystem;
	var m_finalString : String;

	public virtual function Init( player : weak< PlayerPuppet > )
	{
		m_player = player;
		m_playedCueAlready = false;
		m_currentCharges = -1;
		m_statSystem = GameInstance.GetStatsSystem( m_player.GetGame() );
	}

	public virtual function MaxStatPoolValue() : Int32
	{
		return ( ( Int32 )( GameInstance.GetStatPoolsSystem( m_player.GetGame() ).GetStatPoolMaxPointValue( m_player.GetEntityID(), gamedataStatPoolType.HealingItemsCharges ) ) );
	}

	public override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		var charges : Int32;
		m_currentStatPoolValue = RoundMath( newValue * percToPoints );
		charges = m_currentStatPoolValue / GetRechargeDuration();
		if( m_currentCharges == -1 )
		{
			m_currentCharges = charges;
		}
		if( charges != m_currentCharges )
		{
			ConsumablesChargesHelper.HotkeyRefresh( m_player.GetGame() );
			if( charges > m_currentCharges )
			{
				Recharged();
			}
			m_currentCharges = charges;
		}
	}

	protected virtual function GetActiveItem( hotkey : EHotkey ) : Item_Record
	{
		var equipmentSystem : EquipmentSystem;
		var activeItem : ItemID;
		equipmentSystem = EquipmentSystem.GetInstance( m_player );
		activeItem = equipmentSystem.GetItemIDFromHotkey( m_player, hotkey );
		return TweakDBInterface.GetItemRecord( ItemID.GetTDBID( activeItem ) );
	}

	public virtual function GetCharges() : Int32
	{
		return m_currentCharges;
	}

	private virtual function Recharged() {}

	protected virtual function PlayRechagedSoundEvent()
	{
		var audioEvent : SoundPlayEvent;
		audioEvent = new SoundPlayEvent;
		audioEvent.soundName = m_rechargeSoundCue;
		m_player.QueueEvent( audioEvent );
	}

	public virtual function GetRechargeDuration() : Int32
	{
		return ( ( Int32 )( m_statSystem.GetStatValue( m_player.GetEntityID(), gamedataStatType.HealingItemsRechargeDuration ) ) );
	}

}

class AimAssistSettingsListener extends ConfigVarListener
{
	private var m_ctrl : weak< PlayerPuppet >;
	private var m_settings : UserSettings;
	private var m_settingsGroup : ConfigGroup;
	private var m_aimAssistLevel : EAimAssistLevel;
	private var m_aimAssistMeleeLevel : EAimAssistLevel;
	private var m_aimAssistDriverCombatEnabled : Bool;
	private var m_aimAssistSnapEnabled : Bool;
	private var m_difficultySettingsPath : CName;
	default m_difficultySettingsPath = '/accessibility/difficulty';
	var m_currentConfig : AimAssistSettingConfig;
	var m_settingsRecords : array< weak< AimAssistSettings_Record > >;

	public function Initialize( ctrl : weak< PlayerPuppet > )
	{
		var aimAssistVar : ConfigVarListInt;
		var aimAssistBoolVar : ConfigVarBool;
		m_ctrl = ctrl;
		m_settings = GameInstance.GetSettingsSystem( m_ctrl.GetGame() );
		m_settingsGroup = m_settings.GetGroup( m_difficultySettingsPath );
		aimAssistVar = ( ( ConfigVarListInt )( m_settingsGroup.GetVar( 'AimAssistanceMelee' ) ) );
		m_aimAssistMeleeLevel = ( ( EAimAssistLevel )( aimAssistVar.GetValue() ) );
		aimAssistBoolVar = ( ( ConfigVarBool )( m_settingsGroup.GetVar( 'AimAssistanceDriverCombatOnOff' ) ) );
		m_aimAssistDriverCombatEnabled = aimAssistBoolVar.GetValue();
		aimAssistVar = ( ( ConfigVarListInt )( m_settingsGroup.GetVar( 'AimAssistance' ) ) );
		m_aimAssistLevel = ( ( EAimAssistLevel )( aimAssistVar.GetValue() ) );
		aimAssistBoolVar = ( ( ConfigVarBool )( m_settingsGroup.GetVar( 'AimSnap' ) ) );
		m_aimAssistSnapEnabled = aimAssistBoolVar.GetValue();
		m_settingsRecords.Resize( ( ( Int32 )( AimAssistSettingConfig.Count ) ) );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.Default ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_Default" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.Scanning ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_Scanning" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.LeftHandCyberwareCharge ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_LeftHandCyberwareCharge" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.LeftHandCyberware ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_LeftHandCyberware" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.MeleeCombat ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_MeleeCombat" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.MeleeCombatIdle ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_MeleeCombatIdle" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.AimingLimbCyber ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_AimingLimbCyber" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.AimingLimbCyberZoomLevel1 ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_AimingLimbCyberZoomLevel1" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.AimingLimbCyberZoomLevel2 ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_AimingLimbCyberZoomLevel2" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.Aiming ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_Aiming" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.QuickMelee ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_QuickMelee" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.VehicleCombat ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_VehicleCombat" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.Sprinting ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_Sprinting" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.LimbCyber ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_LimbCyber" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.Vehicle ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_Vehicle" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.DriverCombat ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_DriverCombat" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.DriverCombatAiming ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_DriverCombatAiming" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.DriverCombatTPP ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_DriverCombatTPP" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.DriverCombatMissiles ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_DriverCombatMissiles" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.DriverCombatMissilesAiming ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_DriverCombatMissilesAiming" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.DriverCombatMeleeTPP ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_DriverCombatMeleeTPP" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.ZoomLevel1 ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_ZoomLevel1" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.ZoomLevel2 ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_ZoomLevel2" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.Exhausted ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_Exhausted" );
		m_settingsRecords[ ( ( Int32 )( AimAssistSettingConfig.Off ) ) ] = TDB.GetAimAssistSettingsRecord( T"AimAssist.Settings_AimAssistOff" );
		Register( m_difficultySettingsPath );
	}

	public export override function OnVarModified( groupPath : CName, varName : CName, varType : ConfigVarType, reason : ConfigChangeReason )
	{
		var aimAssistVar : ConfigVarListInt;
		var aimAssistBoolVar : ConfigVarBool;
		if( reason != ConfigChangeReason.Accepted )
		{
			return;
		}
		if( 'AimAssistanceMelee' == varName )
		{
			aimAssistVar = ( ( ConfigVarListInt )( m_settingsGroup.GetVar( 'AimAssistanceMelee' ) ) );
			m_aimAssistMeleeLevel = ( ( EAimAssistLevel )( aimAssistVar.GetValue() ) );
			m_ctrl.ApplyAimAssistSettings( AimAssistSettingConfig.Count );
		}
		else if( 'AimAssistanceDriverCombatOnOff' == varName )
		{
			aimAssistBoolVar = ( ( ConfigVarBool )( m_settingsGroup.GetVar( 'AimAssistanceDriverCombatOnOff' ) ) );
			m_aimAssistDriverCombatEnabled = aimAssistBoolVar.GetValue();
			m_ctrl.ApplyAimAssistSettings( AimAssistSettingConfig.Count );
		}
		else if( 'AimAssistance' == varName )
		{
			aimAssistVar = ( ( ConfigVarListInt )( m_settingsGroup.GetVar( 'AimAssistance' ) ) );
			m_aimAssistLevel = ( ( EAimAssistLevel )( aimAssistVar.GetValue() ) );
			m_ctrl.ApplyAimAssistSettings( AimAssistSettingConfig.Count );
		}
		else if( 'AimSnap' == varName )
		{
			aimAssistBoolVar = ( ( ConfigVarBool )( m_settingsGroup.GetVar( 'AimSnap' ) ) );
			m_aimAssistSnapEnabled = aimAssistBoolVar.GetValue();
		}
	}

	public const function GetAimAssistLevel() : EAimAssistLevel
	{
		return m_aimAssistLevel;
	}

	public const function GetAimAssistMeleeLevel() : EAimAssistLevel
	{
		return m_aimAssistMeleeLevel;
	}

	public const function GetAimAssistDriverCombatEnabled() : Bool
	{
		return m_aimAssistDriverCombatEnabled;
	}

	public const function GetAimSnapEnabled() : Bool
	{
		return m_aimAssistSnapEnabled;
	}

}

class AccessibilityControlsListener extends ConfigVarListener
{
	private var m_ctrl : weak< PlayerPuppet >;
	private var m_settings : UserSettings;
	private var m_settingsGroup : ConfigGroup;
	private var m_allowCycleToFistCyberware : Bool;
	private var m_accessibilityControlsPath : CName;
	default m_accessibilityControlsPath = '/accessibility/controls';

	public function Initialize( ctrl : weak< PlayerPuppet > )
	{
		var configBool : ConfigVarBool;
		m_ctrl = ctrl;
		m_settings = GameInstance.GetSettingsSystem( m_ctrl.GetGame() );
		m_settingsGroup = m_settings.GetGroup( m_accessibilityControlsPath );
		configBool = ( ( ConfigVarBool )( m_settingsGroup.GetVar( 'AllowCycleToFistCyberware' ) ) );
		m_allowCycleToFistCyberware = configBool.GetValue();
		Register( m_accessibilityControlsPath );
	}

	public export override function OnVarModified( groupPath : CName, varName : CName, varType : ConfigVarType, reason : ConfigChangeReason )
	{
		var configBool : ConfigVarBool;
		if( reason != ConfigChangeReason.Accepted )
		{
			return;
		}
		if( varName == 'AllowCycleToFistCyberware' )
		{
			configBool = ( ( ConfigVarBool )( m_settingsGroup.GetVar( varName ) ) );
			m_allowCycleToFistCyberware = configBool.GetValue();
		}
	}

	public const function GetAllowCycleToFistCyberware() : Bool
	{
		return m_allowCycleToFistCyberware;
	}

}

class RadioportSettingsListener extends ConfigVarListener
{
	private var m_player : weak< PlayerPuppet >;
	private var m_settings : UserSettings;
	private var m_settingsGroup : ConfigGroup;
	private var m_syncToCarRadio : Bool;
	private var m_cycleOnButtonPress : Bool;
	private var m_saveStation : Bool;
	private var m_syncToCarRadioName : CName;
	private var m_cycleOnButtonPressName : CName;
	private var m_saveStationName : CName;
	private var m_radioportSettingsPath : CName;

	public function Initialize( player : weak< PlayerPuppet > )
	{
		var configBool : ConfigVarBool;
		m_radioportSettingsPath = '/gameplay/radioport';
		m_syncToCarRadioName = 'radioport_sync_to_car';
		m_cycleOnButtonPressName = 'radioport_enable_cycling';
		m_saveStationName = 'radioport_save_station';
		m_player = player;
		m_settings = GameInstance.GetSettingsSystem( m_player.GetGame() );
		m_settingsGroup = m_settings.GetGroup( m_radioportSettingsPath );
		configBool = ( ( ConfigVarBool )( m_settingsGroup.GetVar( m_syncToCarRadioName ) ) );
		m_syncToCarRadio = configBool.GetValue();
		configBool = ( ( ConfigVarBool )( m_settingsGroup.GetVar( m_cycleOnButtonPressName ) ) );
		m_cycleOnButtonPress = configBool.GetValue();
		configBool = ( ( ConfigVarBool )( m_settingsGroup.GetVar( m_saveStationName ) ) );
		m_saveStation = configBool.GetValue();
		Register( m_radioportSettingsPath );
	}

	public export override function OnVarModified( groupPath : CName, varName : CName, varType : ConfigVarType, reason : ConfigChangeReason )
	{
		var configBool : ConfigVarBool;
		if( reason != ConfigChangeReason.Accepted )
		{
			return;
		}
		if( varName == m_syncToCarRadioName )
		{
			configBool = ( ( ConfigVarBool )( m_settingsGroup.GetVar( varName ) ) );
			m_syncToCarRadio = configBool.GetValue();
		}
		if( varName == m_cycleOnButtonPressName )
		{
			configBool = ( ( ConfigVarBool )( m_settingsGroup.GetVar( varName ) ) );
			m_cycleOnButtonPress = configBool.GetValue();
		}
		if( varName == m_saveStationName )
		{
			configBool = ( ( ConfigVarBool )( m_settingsGroup.GetVar( varName ) ) );
			m_saveStation = configBool.GetValue();
			m_player.PSSetPocketRadioStation( m_player.GetPocketRadio().GetStation() );
		}
	}

	public const function GetSyncToCarRadio() : Bool
	{
		return m_syncToCarRadio;
	}

	public const function GetCycleButtonPress() : Bool
	{
		return m_cycleOnButtonPress;
	}

	public const function GetSaveStation() : Bool
	{
		return m_saveStation;
	}

}

class PlayerPuppetAllStatListener extends ScriptStatsListener
{
	var player : weak< PlayerPuppet >;

	public export override function OnStatChanged( ownerID : StatsObjectID, statType : gamedataStatType, diff : Float, total : Float )
	{
		player.OnStatChanged( ownerID, statType, diff, total );
	}

}

class AutoRevealStatListener extends ScriptStatsListener
{
	var m_owner : weak< GameObject >;

	public override function OnStatChanged( ownerID : StatsObjectID, statType : gamedataStatType, diff : Float, total : Float )
	{
		var updateRequest : UpdateAutoRevealStatEvent;
		if( statType == gamedataStatType.AutoReveal && ( ( PlayerPuppet )( m_owner ) ) )
		{
			updateRequest = new UpdateAutoRevealStatEvent;
			updateRequest.hasAutoReveal = total > 0.0;
			m_owner.QueueEvent( updateRequest );
		}
	}

}

class VisibilityStatListener extends ScriptStatsListener
{
	var m_owner : weak< GameObject >;

	public export override function OnStatChanged( ownerID : StatsObjectID, statType : gamedataStatType, diff : Float, total : Float )
	{
		var updateRequest : UpdateVisibilityModifierEvent;
		if( statType == gamedataStatType.Visibility && ( ( PlayerPuppet )( m_owner ) ) )
		{
			updateRequest = new UpdateVisibilityModifierEvent;
			m_owner.QueueEvent( updateRequest );
		}
	}

}

class SecondHeartStatListener extends ScriptStatsListener
{
	var m_player : weak< PlayerPuppet >;

	public override function OnStatChanged( ownerID : StatsObjectID, statType : gamedataStatType, diff : Float, total : Float )
	{
		if( !( m_player ) )
		{
			return;
		}
		if( total > 0.0 )
		{
			GameInstance.GetGodModeSystem( m_player.GetGame() ).EnableOverride( m_player.GetEntityID(), gameGodModeType.Immortal, 'SecondHeart' );
		}
		else
		{
			GameInstance.GetGodModeSystem( m_player.GetGame() ).DisableOverride( m_player.GetEntityID(), 'SecondHeart' );
		}
	}

}

class PlayerPuppetAttachmentSlotsCallback extends AttachmentSlotsScriptCallback
{
	var m_player : weak< PlayerPuppet >;

	public export override function OnItemEquipped( slot : TweakDBID, item : ItemID )
	{
		m_player.OnItemEquipped( slot, item );
	}

	public export override function OnItemUnequipped( slot : TweakDBID, item : ItemID )
	{
		m_player.OnItemUnequipped( slot, item );
	}

}

class ArmorStatListener extends ScriptStatPoolsListener
{
	var m_ownerPuppet : weak< PlayerPuppet >;

	public override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		m_ownerPuppet.m_CPOMissionDataState.OnDamage( m_ownerPuppet, false );
	}

}

class HealthStatListener extends ScriptStatPoolsListener
{
	var m_ownerPuppet : weak< PlayerPuppet >;
	private var healthEvent : HealthUpdateEvent;

	public export override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		if( m_ownerPuppet.IsControlledByLocalPeer() )
		{
			m_ownerPuppet.m_CPOMissionDataState.OnDamage( m_ownerPuppet, true );
		}
		healthEvent = new HealthUpdateEvent;
		healthEvent.value = newValue;
		healthEvent.healthDifference = newValue - oldValue;
		m_ownerPuppet.QueueEvent( healthEvent );
	}

}

class HealingItemsChargeStatListener extends BaseChargesStatListener
{

	public override function Init( player : weak< PlayerPuppet > )
	{
		super.Init( player );
		m_rechargeSoundCue = 'ui_inhaler_injector_recharged';
	}

	private override function Recharged()
	{
		PlayRechagedSoundEvent();
	}

}

class GrenadesChargeStatListener extends BaseChargesStatListener
{

	public override function Init( player : weak< PlayerPuppet > )
	{
		super.Init( player );
		m_rechargeSoundCue = 'ui_grenade_recharged';
		m_finalString = ( GetLocalizedText( "LocKey#91660" ) + " " ) + GetLocalizedText( "LocKey#35671" );
	}

	public override function MaxStatPoolValue() : Int32
	{
		return ( ( Int32 )( GameInstance.GetStatPoolsSystem( m_player.GetGame() ).GetStatPoolMaxPointValue( m_player.GetEntityID(), gamedataStatPoolType.GrenadesCharges ) ) );
	}

	public override function GetRechargeDuration() : Int32
	{
		var grenadeID : ItemID;
		var grenadeRecord : Grenade_Record;
		grenadeID = EquipmentSystem.GetData( m_player ).GetActiveGadget();
		grenadeRecord = TweakDBInterface.GetGrenadeRecord( ItemID.GetTDBID( grenadeID ) );
		return GetRechargeDuration( grenadeRecord );
	}

	public function GetRechargeDuration( item : Grenade_Record ) : Int32
	{
		var rechargeDuration : Int32;
		rechargeDuration = ( ( Int32 )( m_statSystem.GetStatValue( m_player.GetEntityID(), gamedataStatType.GrenadesRechargeDuration ) ) );
		if( PlayerDevelopmentSystem.GetInstance( m_player ).IsNewPerkBought( m_player, gamedataNewPerkType.Tech_Left_Perk_3_3 ) == 1 )
		{
			if( ( item.TagsContains( 'FlashGrenade' ) || item.TagsContains( 'SmokeGrenade' ) ) || item.TagsContains( 'ReconGrenade' ) )
			{
				rechargeDuration /= 2;
			}
		}
		return rechargeDuration;
	}

	public function GetRechargeDurationClean() : Int32
	{
		return ( ( Int32 )( m_statSystem.GetStatValue( m_player.GetEntityID(), gamedataStatType.GrenadesRechargeDuration ) ) );
	}

	public override function GetCharges() : Int32
	{
		return m_currentStatPoolValue / GetRechargeDuration();
	}

	public function GetCharges( item : Grenade_Record ) : Int32
	{
		return m_currentStatPoolValue / GetRechargeDuration( item );
	}

	private override function Recharged()
	{
		var item : Item_Record;
		item = GetActiveItem( EHotkey.RB );
		if( item.ItemType().Type() == gamedataItemType.Gad_Grenade )
		{
			PlayRechagedSoundEvent();
		}
	}

}

class ProjectileLauncherChargeStatListener extends BaseChargesStatListener
{

	public override function Init( player : weak< PlayerPuppet > )
	{
		super.Init( player );
		m_rechargeSoundCue = 'ui_grenade_recharged';
		m_finalString = ( GetLocalizedText( "LocKey#52430" ) + " " ) + GetLocalizedText( "LocKey#3722" );
	}

	public override function GetRechargeDuration() : Int32
	{
		return ( ( Int32 )( m_statSystem.GetStatValue( m_player.GetEntityID(), gamedataStatType.ProjectileLauncherRechargeDuration ) ) );
	}

	private override function Recharged()
	{
		var item : Item_Record;
		item = GetActiveItem( EHotkey.RB );
		if( item.ItemType().Type() == gamedataItemType.Cyb_Launcher )
		{
			PlayRechagedSoundEvent();
		}
	}

}

class OpticalCamoChargeStatListener extends BaseChargesStatListener
{

	public override function Init( player : weak< PlayerPuppet > )
	{
		super.Init( player );
		m_rechargeSoundCue = 'ui_grenade_recharged';
		m_finalString = ( GetLocalizedText( "LocKey#52430" ) + " " ) + GetLocalizedText( "LocKey#3702" );
	}

	protected event OnStatPoolMaxValueReached( value : Float )
	{
		var item : Item_Record;
		item = GetActiveItem( EHotkey.RB );
		if( item.ItemType().Type() == gamedataItemType.Cyb_Ability && item.FriendlyName() == "OpticalCamo" )
		{
			PlayRechagedSoundEvent();
		}
	}

	protected event OnStatPoolMinValueReached( value : Float )
	{
		StatusEffectHelper.RemoveStatusEffect( m_player, T"BaseStatusEffect.OpticalCamoCoolPerkShort" );
	}

	public override function GetRechargeDuration() : Int32
	{
		return ( ( Int32 )( m_statSystem.GetStatValue( m_player.GetEntityID(), gamedataStatType.OpticalCamoRechargeDuration ) ) );
	}

	public override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float ) {}
}

class OverclockChargeListener extends BaseChargesStatListener
{

	public override function Init( player : weak< PlayerPuppet > )
	{
		super.Init( player );
		m_rechargeSoundCue = 'ui_iconic_cyberwear_recharged';
	}

	protected event OnStatPoolMaxValueReached( value : Float )
	{
		var item : Item_Record;
		item = GetActiveItem( EHotkey.LBRB );
		if( item.ItemType().Type() == gamedataItemType.Cyb_Ability )
		{
			PlayRechagedSoundEvent();
		}
	}

	protected event OnStatPoolMinValueReached( value : Float )
	{
		StatusEffectHelper.RemoveStatusEffect( m_player, QuickHackableHelper.GetOverclockedStateTweakDBID() );
	}

}

