class GodModeStatListener extends ScriptStatsListener
{
	var m_healthbar : weak< healthbarWidgetGameController >;

	public export override function OnGodModeChanged( ownerID : EntityID, newType : gameGodModeType )
	{
		if( m_healthbar != NULL )
		{
			m_healthbar.UpdateGodModeVisibility();
		}
	}

}

class HealthbarMemoryStatListener extends ScriptStatsListener
{
	var m_healthbar : weak< healthbarWidgetGameController >;

	public override function OnStatChanged( ownerID : StatsObjectID, statType : gamedataStatType, diff : Float, total : Float )
	{
		if( m_healthbar != NULL )
		{
			m_healthbar.UpdateMemoryBarMaxStat( total );
		}
	}

}

import class healthbarWidgetGameController extends inkHUDGameController
{
	private var m_bbPlayerStats : weak< IBlackboard >;
	private var m_bbPlayerEventId : CallbackHandle;
	private var m_bbMuppetStats : weak< IBlackboard >;
	private var m_bbMuppetEventId : CallbackHandle;
	private var m_bbRightWeaponInfo : weak< IBlackboard >;
	private var m_bbRightWeaponEventId : CallbackHandle;
	private var m_bbLeftWeaponInfo : weak< IBlackboard >;
	private var m_bbLeftWeaponEventId : CallbackHandle;
	private var m_bbPSceneTierEventId : CallbackHandle;
	private var m_visionStateBlackboardId : CallbackHandle;
	private var m_combatModeBlackboardId : CallbackHandle;
	private var m_bbQuickhacksMemeoryEventId : CallbackHandle;
	private editable var m_healthBar : inkWidgetRef;
	private editable var m_overshieldBarRef : inkWidgetRef;
	private editable var m_expBar : inkWidgetRef;
	private editable var m_expBarSpacer : inkWidgetRef;
	private editable var m_levelUpArrow : inkWidgetRef;
	private editable var m_levelUpFrame : inkWidgetRef;
	private editable var m_barsLayoutPath : inkCompoundRef;
	private editable var m_buffsHolder : inkCompoundRef;
	private editable var m_invulnerableTextPath : inkTextRef;
	private editable var m_levelTextPath : inkTextRef;
	private editable var m_nextLevelTextPath : inkTextRef;
	private editable var m_healthTextPath : inkTextRef;
	private editable var m_maxHealthTextPath : inkTextRef;
	private editable var m_quickhacksContainer : inkCompoundRef;
	private editable var m_expText : inkTextRef;
	private editable var m_expTextLabel : inkTextRef;
	private editable var m_lostHealthAggregationBar : inkWidgetRef;
	private editable var m_levelUpRectangle : inkWidgetRef;
	private editable var m_damegePreview : inkWidgetRef;
	private editable var m_fullBar : inkWidgetRef;
	private var m_healthController : weak< NameplateBarLogicController >;
	private var m_armorController : weak< ProgressBarSimpleWidgetLogicController >;
	private var m_RootWidget : weak< inkWidget >;
	private var m_buffWidget : weak< inkWidget >;
	private var m_invulnerableText : weak< inkText >;
	private var m_animHideTemp : inkAnimDef;
	private var m_animShortFade : inkAnimDef;
	private var m_animLongFade : inkAnimDef;
	private var m_animHideHPProxy : inkAnimProxy;
	var delayAnimation : inkAnimDef;
	var animCreated : Bool;
	var aggregatingActive : Bool;
	var countingStartHealth : Int32;
	private var m_currentHealth : Int32;
	private var m_previousHealth : Int32;
	private var m_maximumHealth : Int32;
	private var m_quickhacksMemoryPercent : Float;
	private var m_currentArmor : Int32;
	default m_currentArmor = 0;
	private var m_maximumArmor : Int32;
	default m_maximumArmor = 0;
	private var m_quickhackBarArray : array< weak< inkWidget > >;
	private var m_spawnedMemoryCells : Int32;
	private var m_usedQuickhacks : Int32;
	private var m_buffsVisible : Bool;
	private var m_isUnarmedRightHand : Bool;
	default m_isUnarmedRightHand = true;
	private var m_isUnarmedLeftHand : Bool;
	default m_isUnarmedLeftHand = true;
	private var m_isControllingDevice : Bool;
	private var m_currentVisionPSM : gamePSMVision;
	private var m_combatModePSM : gamePSMCombat;
	private var m_sceneTier : GameplayTier;
	private var m_godModeStatListener : GodModeStatListener;
	private var m_memoryStatListener : HealthbarMemoryStatListener;
	private var m_playerStatsBlackboard : weak< IBlackboard >;
	private var characterCurrentXPListener : CallbackHandle;
	private var m_levelUpBlackboard : weak< IBlackboard >;
	private var playerLevelUpListener : CallbackHandle;
	private var m_currentLevel : Int32;
	private var m_playerObject : weak< GameObject >;
	private var m_playerDevelopmentSystem : PlayerDevelopmentSystem;
	private var m_gameInstance : GameInstance;
	private var m_foldingAnimProxy : inkAnimProxy;
	private var m_memoryFillCells : Float;
	private var m_memoryMaxCells : Int32;
	private var m_pendingRequests : Int32;
	private var m_spawnTokens : array< weak< inkAsyncSpawnRequest > >;
	private var m_overshieldListener : OvershieldListener;
	private var m_overshieldBarController : weak< NameplateBarLogicController >;
	private var m_useOevershield : Bool;
	private var m_currentOvershieldValue : Int32;
	default m_currentOvershieldValue = 0;
	private var m_currentOvershieldValuePercent : Float;
	default m_currentOvershieldValuePercent = 0.f;
	private var m_overclockListener : OverclockListener;
	private var m_isInOverclockedState : Bool;
	default m_isInOverclockedState = false;
	private var m_pulseBar : PulseAnimation;
	private var m_pulseText : PulseAnimation;
	private var m_pulse : PulseAnimation;
	private var m_healthMemoryJumpAnim : inkAnimProxy;
	private var m_healthMemoryFlashAnim : inkAnimProxy;

	public import function RequestHealthBarVisibilityUpdate();

	protected event OnInitialize()
	{
		var requestStatsEvent : RequestStatsBB;
		m_playerObject = ( ( GameObject )( GetOwnerEntity() ) );
		m_RootWidget = ( ( inkWidget )( GetRootWidget() ) );
		m_buffWidget = ( ( inkWidget )( SpawnFromExternal( inkCompoundRef.Get( m_buffsHolder ), R"base\gameplay\gui\widgets\healthbar\playerbuffbar.inkwidget", 'VertRoot' ) ) );
		m_currentHealth = CeilF( GameInstance.GetStatPoolsSystem( m_playerObject.GetGame() ).GetStatPoolValue( GetPlayer( m_playerObject.GetGame() ).GetEntityID(), gamedataStatPoolType.Health, false ) );
		m_playerDevelopmentSystem = ( ( PlayerDevelopmentSystem )( GameInstance.GetScriptableSystemsContainer( m_playerObject.GetGame() ).Get( 'PlayerDevelopmentSystem' ) ) );
		m_useOevershield = PlayerDevelopmentSystem.GetData( m_playerObject ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Body_Central_Milestone_3 );
		m_overshieldBarController = ( ( NameplateBarLogicController )( inkWidgetRef.GetController( m_overshieldBarRef ) ) );
		m_healthController = ( ( NameplateBarLogicController )( inkWidgetRef.GetController( m_healthBar ) ) );
		m_bbPlayerStats = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_PlayerBioMonitor );
		m_bbPlayerEventId = m_bbPlayerStats.RegisterListenerVariant( GetAllBlackboardDefs().UI_PlayerBioMonitor.PlayerStatsInfo, this, 'OnStatsChanged' );
		if( IsPlayingMultiplayer() )
		{
			m_bbMuppetStats = GetBlackboardSystem().Get( GetAllBlackboardDefs().UIGameData );
			m_bbMuppetEventId = m_bbMuppetStats.RegisterListenerVariant( GetAllBlackboardDefs().UIGameData.MuppetStats, this, 'OnMuppetUpdate' );
		}
		m_bbPlayerStats.SignalVariant( GetAllBlackboardDefs().UI_PlayerBioMonitor.PlayerStatsInfo );
		m_bbQuickhacksMemeoryEventId = m_bbPlayerStats.RegisterDelayedListenerFloat( GetAllBlackboardDefs().UI_PlayerBioMonitor.MemoryPercent, this, 'OnQuickhacksMemoryPercentUpdate' );
		m_bbRightWeaponInfo = GetBlackboardSystem().Get( GetAllBlackboardDefs().UIGameData );
		m_bbRightWeaponEventId = m_bbRightWeaponInfo.RegisterListenerVariant( GetAllBlackboardDefs().UIGameData.RightWeaponRecordID, this, 'OnRightWeaponSwap' );
		m_bbLeftWeaponInfo = GetBlackboardSystem().Get( GetAllBlackboardDefs().UIGameData );
		m_bbLeftWeaponEventId = m_bbLeftWeaponInfo.RegisterListenerVariant( GetAllBlackboardDefs().UIGameData.LeftWeaponRecordID, this, 'OnLeftWeaponSwap' );
		m_playerStatsBlackboard = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_PlayerStats );
		characterCurrentXPListener = m_playerStatsBlackboard.RegisterListenerInt( GetAllBlackboardDefs().UI_PlayerStats.CurrentXP, this, 'OnCharacterLevelCurrentXPUpdated' );
		m_playerStatsBlackboard.SignalInt( GetAllBlackboardDefs().UI_PlayerStats.CurrentXP );
		AnimateCharacterLevelUpdated( m_playerStatsBlackboard.GetInt( GetAllBlackboardDefs().UI_PlayerStats.Level ), true );
		m_levelUpBlackboard = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_PlayerStats );
		playerLevelUpListener = m_levelUpBlackboard.RegisterDelayedListenerInt( GetAllBlackboardDefs().UI_PlayerStats.Level, this, 'OnCharacterLevelUpdated' );
		m_levelUpBlackboard.SignalInt( GetAllBlackboardDefs().UI_PlayerStats.Level );
		CreateAnimations();
		OnUpdateHealthBarVisibility();
		SetupQuickhacksMemoryBar();
		requestStatsEvent = new RequestStatsBB;
		requestStatsEvent.Set( m_playerObject );
		m_playerDevelopmentSystem.QueueRequest( requestStatsEvent );
		m_gameInstance = GetPlayerControlledObject().GetGame();
		m_pulseBar = new PulseAnimation;
		m_pulseText = new PulseAnimation;
		m_pulse = new PulseAnimation;
		m_pulseBar.Configure( inkWidgetRef.Get( m_damegePreview ), 1.0, 0.80000001, 0.30000001 );
		m_pulseText.Configure( inkTextRef.Get( m_maxHealthTextPath ), 1.0, 0.1, 0.30000001 );
		m_pulse.Configure( inkWidgetRef.Get( m_healthBar ), 1.0, 0.5, 0.60000002 );
		inkWidgetRef.SetVisible( m_damegePreview, false );
	}

	protected event OnUninitialize()
	{
		if( m_bbPlayerStats )
		{
			m_bbPlayerStats.UnregisterListenerVariant( GetAllBlackboardDefs().UI_PlayerBioMonitor.PlayerStatsInfo, m_bbPlayerEventId );
			m_bbPlayerStats.UnregisterDelayedListener( GetAllBlackboardDefs().UI_PlayerBioMonitor.MemoryPercent, m_bbQuickhacksMemeoryEventId );
		}
		if( IsPlayingMultiplayer() && m_bbMuppetStats )
		{
			m_bbMuppetStats.UnregisterListenerVariant( GetAllBlackboardDefs().UIGameData.MuppetStats, m_bbMuppetEventId );
		}
		if( m_bbRightWeaponInfo )
		{
			m_bbRightWeaponInfo.UnregisterListenerVariant( GetAllBlackboardDefs().UIGameData.RightWeaponRecordID, m_bbRightWeaponEventId );
		}
		if( m_bbLeftWeaponInfo )
		{
			m_bbLeftWeaponInfo.UnregisterListenerVariant( GetAllBlackboardDefs().UIGameData.LeftWeaponRecordID, m_bbLeftWeaponEventId );
		}
		if( m_playerStatsBlackboard )
		{
			m_playerStatsBlackboard.UnregisterListenerInt( GetAllBlackboardDefs().UI_PlayerStats.CurrentXP, characterCurrentXPListener );
		}
	}

	protected event OnPlayerAttach( playerGameObject : GameObject )
	{
		var controlledPuppetRecordID : TweakDBID;
		var controlledPuppet : weak< gamePuppetBase >;
		var isOverclockActivated : Bool;
		RegisterPSMListeners( playerGameObject );
		m_playerObject = playerGameObject;
		if( m_foldingAnimProxy )
		{
			m_foldingAnimProxy.Stop();
		}
		m_foldingAnimProxy = PlayLibraryAnimation( 'unfold' );
		controlledPuppet = ( ( gamePuppetBase )( GetPlayer( m_gameInstance ) ) );
		if( controlledPuppet != NULL )
		{
			controlledPuppetRecordID = controlledPuppet.GetRecordID();
			if( controlledPuppetRecordID == T"Character.johnny_replacer" )
			{
				inkWidgetRef.SetVisible( m_levelUpRectangle, false );
			}
			else
			{
				inkWidgetRef.SetVisible( m_levelUpRectangle, true );
				m_overshieldListener = new OvershieldListener;
				m_overshieldListener.BindHelathBar( this );
				GameInstance.GetStatPoolsSystem( playerGameObject.GetGame() ).RequestRegisteringListener( playerGameObject.GetEntityID(), gamedataStatPoolType.Overshield, m_overshieldListener );
				EvaluateOvershieldBarVisibility();
				m_overclockListener = new OverclockListener;
				m_overclockListener.BindHelathBar( this );
				GameInstance.GetStatusEffectSystem( playerGameObject.GetGame() ).RegisterListener( playerGameObject.GetEntityID(), m_overclockListener );
				isOverclockActivated = QuickHackableHelper.IsOverclockedStateActive( playerGameObject );
				if( isOverclockActivated )
				{
					GetRootWidget().SetVisible( true );
					EvaluateHealthBarVisibility( true );
				}
			}
		}
		else
		{
			inkWidgetRef.SetVisible( m_levelUpRectangle, true );
		}
	}

	protected event OnPlayerDetach( playerGameObject : GameObject )
	{
		UnregisterPSMListeners( playerGameObject );
		if( m_foldingAnimProxy )
		{
			m_foldingAnimProxy.Stop();
		}
		if( m_overshieldListener )
		{
			GameInstance.GetStatPoolsSystem( playerGameObject.GetGame() ).RequestUnregisteringListener( playerGameObject.GetEntityID(), gamedataStatPoolType.Overshield, m_overshieldListener );
			m_overshieldListener = NULL;
		}
		if( m_overclockListener )
		{
			m_overclockListener = NULL;
		}
		m_foldingAnimProxy = PlayLibraryAnimation( 'fold' );
	}

	protected function RegisterPSMListeners( playerObject : GameObject )
	{
		var playerStateMachineBlackboard : IBlackboard;
		var playerSMDef : PlayerStateMachineDef;
		playerSMDef = GetAllBlackboardDefs().PlayerStateMachine;
		if( playerSMDef )
		{
			playerStateMachineBlackboard = GetPSMBlackboard( playerObject );
			if( playerStateMachineBlackboard )
			{
				m_visionStateBlackboardId = playerStateMachineBlackboard.RegisterListenerInt( playerSMDef.Vision, this, 'OnPSMVisionStateChanged' );
				m_bbPSceneTierEventId = playerStateMachineBlackboard.RegisterListenerInt( playerSMDef.SceneTier, this, 'OnSceneTierChange' );
				m_combatModeBlackboardId = playerStateMachineBlackboard.RegisterListenerInt( playerSMDef.Combat, this, 'OnCombatStateChanged' );
				m_isControllingDevice = playerStateMachineBlackboard.GetBool( playerSMDef.IsControllingDevice );
				if( m_isControllingDevice )
				{
					GetRootWidget().SetVisible( false );
				}
			}
			m_godModeStatListener = new GodModeStatListener;
			m_godModeStatListener.m_healthbar = this;
			GameInstance.GetStatsSystem( playerObject.GetGame() ).RegisterListener( playerObject.GetEntityID(), m_godModeStatListener );
			m_memoryStatListener = new HealthbarMemoryStatListener;
			m_memoryStatListener.m_healthbar = this;
			m_memoryStatListener.SetStatType( gamedataStatType.Memory );
			GameInstance.GetStatsSystem( playerObject.GetGame() ).RegisterListener( playerObject.GetEntityID(), m_memoryStatListener );
		}
	}

	protected function UnregisterPSMListeners( playerObject : GameObject )
	{
		var playerStateMachineBlackboard : IBlackboard;
		var playerSMDef : PlayerStateMachineDef;
		playerSMDef = GetAllBlackboardDefs().PlayerStateMachine;
		if( playerSMDef )
		{
			playerStateMachineBlackboard = GetPSMBlackboard( playerObject );
			if( playerStateMachineBlackboard )
			{
				playerStateMachineBlackboard.UnregisterDelayedListener( playerSMDef.Vision, m_visionStateBlackboardId );
				playerStateMachineBlackboard.UnregisterDelayedListener( playerSMDef.SceneTier, m_bbPSceneTierEventId );
				playerStateMachineBlackboard.UnregisterDelayedListener( playerSMDef.Combat, m_combatModeBlackboardId );
				GameInstance.GetStatsSystem( playerObject.GetGame() ).UnregisterListener( playerObject.GetEntityID(), m_godModeStatListener );
				m_godModeStatListener = NULL;
				GameInstance.GetStatsSystem( playerObject.GetGame() ).UnregisterListener( playerObject.GetEntityID(), m_memoryStatListener );
				m_memoryStatListener = NULL;
			}
		}
	}

	private function StartDamageFallDelay()
	{
		var delayProxy : inkAnimProxy;
		var delayInterpolator : inkAnimScale;
		var size, selfSize : Vector2;
		var width : Float;
		if( !( delayAnimation ) )
		{
			delayAnimation = new inkAnimDef;
			delayInterpolator = new inkAnimScale;
			delayInterpolator.SetStartDelay( 1.0 );
			delayInterpolator.SetDuration( 0.0 );
			delayInterpolator.SetDirection( inkanimInterpolationDirection.From );
			delayInterpolator.SetType( inkanimInterpolationType.Linear );
			delayInterpolator.SetMode( inkanimInterpolationMode.EasyIn );
			delayInterpolator.SetStartScale( Vector2( 1.0, 1.0 ) );
			delayAnimation.AddInterpolator( delayInterpolator );
		}
		if( !( aggregatingActive ) )
		{
			delayProxy = inkTextRef.PlayAnimation( m_expText, delayAnimation );
			delayProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnDamageAggregationFinished' );
			aggregatingActive = true;
			countingStartHealth = m_previousHealth;
			inkWidgetRef.SetScale( m_lostHealthAggregationBar, Vector2( 1.0, 1.0 ) );
		}
		size = m_healthController.GetFullSize();
		selfSize = inkWidgetRef.GetSize( m_lostHealthAggregationBar );
		width = ( ( ( Float )( ( countingStartHealth - m_currentHealth ) ) ) / ( ( Float )( m_maximumHealth ) ) ) * size.X;
		inkWidgetRef.SetSize( m_lostHealthAggregationBar, Vector2( width, selfSize.Y ) );
	}

	protected event OnDamageAggregationFinished( anim : inkAnimProxy )
	{
		aggregatingActive = false;
		PlayLibraryAnimation( 'hide_delay_bar' );
	}

	private function SetHealthProgress( value : Float )
	{
		value = ClampF( value, 0.01, 1.0 );
		m_healthController.SetNameplateBarProgress( value, m_previousHealth == m_currentHealth );
		UpdateCurrentHealthText();
		if( m_previousHealth > m_currentHealth )
		{
			StartDamageFallDelay();
		}
	}

	private function UpdateCurrentHealthText()
	{
		inkTextRef.SetState( m_healthTextPath, ( ( m_currentOvershieldValue > 0 ) ? ( 'Overshield' ) : ( 'Default' ) ) );
		inkTextRef.SetText( m_healthTextPath, IntToString( m_currentHealth + m_currentOvershieldValue ) );
	}

	protected event OnCharacterLevelUpdated( value : Int32 )
	{
		AnimateCharacterLevelUpdated( value );
	}

	private function AnimateCharacterLevelUpdated( value : Int32, optional skipAnimation : Bool )
	{
		var levelUpProxy : inkAnimProxy;
		if( m_currentLevel != value )
		{
			m_currentLevel = value;
			inkTextRef.SetText( m_nextLevelTextPath, IntToString( m_currentLevel ) );
			if( !( skipAnimation ) )
			{
				levelUpProxy = PlayLibraryAnimation( 'levelup_animation' );
				levelUpProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnLevelUpAnimationFinished' );
			}
			else
			{
				inkWidgetRef.SetOpacity( m_levelUpArrow, 0.0 );
				inkWidgetRef.SetOpacity( m_levelUpFrame, 0.0 );
				OnLevelUpAnimationFinished( levelUpProxy );
			}
			RequestHealthBarVisibilityUpdate();
		}
	}

	protected event OnLevelUpAnimationFinished( anim : inkAnimProxy )
	{
		inkTextRef.SetText( m_levelTextPath, IntToString( m_currentLevel ) );
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnCharacterLevelCurrentXPUpdated( value : Int32 )
	{
		var remainingXP : Int32;
		var expSum : Int32;
		var progressFloat : Float;
		remainingXP = m_playerDevelopmentSystem.GetRemainingExpForLevelUp( m_playerObject, gamedataProficiencyType.Level );
		expSum = remainingXP + value;
		progressFloat = ( ( Float )( value ) ) / ( ( Float )( expSum ) );
		inkTextRef.SetText( m_expText, IntToString( value ) );
		inkTextRef.SetText( m_expTextLabel, "LocKey#23263" );
		inkWidgetRef.SetSizeCoefficient( m_expBar, progressFloat );
		inkWidgetRef.SetSizeCoefficient( m_expBarSpacer, 1.0 - progressFloat );
	}

	private function AdjustRequest() {}

	private function SetupQuickhacksMemoryBar()
	{
		inkCompoundRef.RemoveAllChildren( m_quickhacksContainer );
		m_spawnedMemoryCells = 0;
		m_memoryMaxCells = FloorF( GameInstance.GetStatsSystem( ( ( PlayerPuppet )( GetOwnerEntity() ) ).GetGame() ).GetStatValue( GetPlayerControlledObject().GetEntityID(), gamedataStatType.Memory ) );
		m_memoryFillCells = ( ( Float )( m_memoryMaxCells ) );
		UpdateQuickhacksMemoryBarSize( m_memoryMaxCells );
	}

	private function UpdateQuickhacksMemoryBarSize( size : Int32 )
	{
		var i : Int32;
		var requestToken : weak< inkAsyncSpawnRequest >;
		if( size > m_spawnedMemoryCells )
		{
			for( i = m_spawnedMemoryCells + m_pendingRequests; i < size; i += 1 )
			{
				requestToken = AsyncSpawnFromLocal( inkCompoundRef.Get( m_quickhacksContainer ), 'quickhackBar', this, 'OnMemoryBarSpawned' );
				m_spawnTokens.PushBack( requestToken );
				m_pendingRequests += 1;
			}
		}
		else
		{
			UpdateMemoryBarData();
		}
	}

	protected event OnMuppetUpdate( value : Variant )
	{
		var incomingData : PlayerBioMonitor;
		incomingData = ( ( PlayerBioMonitor )value );
		m_previousHealth = m_currentHealth;
		m_maximumHealth = incomingData.maximumHealth;
		m_currentHealth = incomingData.currentHealth;
		m_currentHealth = Clamp( m_currentHealth, 0, m_maximumHealth );
		m_currentArmor = incomingData.currentArmor;
		m_maximumArmor = incomingData.maximumArmor;
		SetHealthProgress( ( ( Float )( m_currentHealth ) ) / ( ( Float )( m_maximumHealth ) ) );
		SetArmorProgress( ( ( Float )( m_currentArmor ) ) / ( ( Float )( m_maximumArmor ) ), false );
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnMemoryBarSpawned( widget : inkWidget, userData : IScriptable )
	{
		m_spawnedMemoryCells += 1;
		m_pendingRequests -= 1;
		m_quickhackBarArray.PushBack( widget );
		if( m_pendingRequests <= 0 )
		{
			m_spawnTokens.Clear();
			UpdateMemoryBarData();
		}
	}

	private function UpdateMemoryBarData()
	{
		var i : Int32;
		var fillCellsInt : Int32;
		var quickhackBar : weak< inkWidget >;
		var quickhackBarController : weak< QuickhackBarController >;
		fillCellsInt = FloorF( m_memoryFillCells );
		for( i = 0; i < m_quickhackBarArray.Size(); i += 1 )
		{
			if( i >= m_memoryMaxCells )
			{
				m_quickhackBarArray[ i ].SetVisible( false );
			}
			else
			{
				quickhackBar = m_quickhackBarArray[ i ];
				quickhackBarController = ( ( QuickhackBarController )( quickhackBar.GetController() ) );
				if( fillCellsInt < m_memoryMaxCells )
				{
					if( i < fillCellsInt )
					{
						quickhackBarController.SetStatus( 1.0 );
					}
					else if( i == fillCellsInt )
					{
						quickhackBarController.SetStatus( m_memoryFillCells - ( ( Float )( fillCellsInt ) ) );
					}
					else
					{
						quickhackBarController.SetStatus( 0.0 );
					}
				}
				else
				{
					quickhackBarController.SetStatus( 1.0 );
				}
				quickhackBar.SetVisible( true );
			}
		}
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnQuickhacksMemoryPercentUpdate( value : Float )
	{
		m_memoryMaxCells = FloorF( GameInstance.GetStatsSystem( ( ( PlayerPuppet )( GetOwnerEntity() ) ).GetGame() ).GetStatValue( GetPlayerControlledObject().GetEntityID(), gamedataStatType.Memory ) );
		m_memoryFillCells = ( ( ( Float )( m_memoryMaxCells ) ) * value ) * 0.01;
		m_usedQuickhacks = m_memoryMaxCells - FloorF( m_memoryFillCells );
		m_quickhacksMemoryPercent = value;
		UpdateQuickhacksMemoryBarSize( m_memoryMaxCells );
	}

	public function UpdateMemoryBarMaxStat( maxBars : Float )
	{
		var currentValue : Float;
		m_memoryMaxCells = FloorF( maxBars );
		currentValue = GameInstance.GetStatPoolsSystem( ( ( PlayerPuppet )( GetOwnerEntity() ) ).GetGame() ).GetStatPoolValue( GetPlayerControlledObject().GetEntityID(), gamedataStatPoolType.Memory );
		m_memoryFillCells = ( ( ( Float )( m_memoryMaxCells ) ) * currentValue ) * 0.01;
		m_usedQuickhacks = m_memoryMaxCells - FloorF( m_memoryFillCells );
		m_quickhacksMemoryPercent = currentValue;
		UpdateQuickhacksMemoryBarSize( m_memoryMaxCells );
	}

	private function IsCyberdeckEquipped() : Bool
	{
		var itemTags : array< CName >;
		var itemRecord : weak< Item_Record >;
		var systemReplacementID : ItemID;
		systemReplacementID = EquipmentSystem.GetData( GetPlayerControlledObject() ).GetActiveItem( gamedataEquipmentArea.SystemReplacementCW );
		itemRecord = RPGManager.GetItemRecord( systemReplacementID );
		if( itemRecord )
		{
			itemTags = itemRecord.Tags();
		}
		return itemTags.Contains( 'Cyberdeck' );
	}

	private function SetArmorProgress( normalizedValue : Float, silent : Bool ) {}

	protected event OnStatsChanged( value : Variant )
	{
		var incomingData : PlayerBioMonitor;
		incomingData = ( ( PlayerBioMonitor )value );
		m_previousHealth = m_currentHealth;
		m_maximumHealth = CeilF( GameInstance.GetStatsSystem( m_playerObject.GetGame() ).GetStatValue( GetPlayer( m_playerObject.GetGame() ).GetEntityID(), gamedataStatType.Health ) );
		m_currentHealth = CeilF( GameInstance.GetStatPoolsSystem( m_playerObject.GetGame() ).GetStatPoolValue( GetPlayer( m_playerObject.GetGame() ).GetEntityID(), gamedataStatPoolType.Health, false ) );
		m_currentHealth = Clamp( m_currentHealth, 0, m_maximumHealth );
		m_currentArmor = incomingData.currentArmor;
		m_maximumArmor = incomingData.maximumArmor;
		SetHealthProgress( ( ( Float )( m_currentHealth ) ) / ( ( Float )( m_maximumHealth ) ) );
		SetArmorProgress( ( ( Float )( m_currentArmor ) ) / ( ( Float )( m_maximumArmor ) ), false );
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnRightWeaponSwap( value : Variant )
	{
		m_isUnarmedRightHand = ( ( TweakDBID )value ) == TDBID.None();
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnLeftWeaponSwap( value : Variant )
	{
		m_isUnarmedLeftHand = ( ( TweakDBID )value ) == TDBID.None();
		RequestHealthBarVisibilityUpdate();
	}

	private const function IsUnarmed() : Bool
	{
		return m_isUnarmedRightHand && m_isUnarmedLeftHand;
	}

	public function UpdateGodModeVisibility()
	{
		inkTextRef.SetVisible( m_invulnerableTextPath, HelperHasGodMode() );
	}

	private function ShowOverclockedHealthbar()
	{
		if( m_isInOverclockedState )
		{
			GetRootWidget().SetState( 'Overclocked' );
			m_pulse.Start();
			inkWidgetRef.Get( m_fullBar ).SetEffectEnabled( inkEffectType.ScanlineWipe, 'ScanlineWipe_0', true );
			inkWidgetRef.SetOpacity( m_levelUpArrow, 0.0 );
			inkWidgetRef.SetOpacity( m_levelUpFrame, 0.0 );
		}
	}

	protected event OnOverclockDamagePreview( e : OverclockDamagePreview )
	{
		var healthCost : Float;
		var fullBarSize : Vector2;
		var animOptions : inkAnimOptions;
		if( e.IsHovering )
		{
			animOptions.loopInfinite = true;
			animOptions.loopType = inkanimLoopType.Cycle;
			m_healthMemoryJumpAnim = PlayLibraryAnimation( 'memorySelected', animOptions );
			m_pulse.Stop();
			m_pulseBar.Start();
			m_pulseText.Start();
			QuickHackableHelper.CanPayWithHealthInOverclockedState( m_playerObject, e.MemoryCost, healthCost );
			inkTextRef.SetText( m_maxHealthTextPath, "-" + IntToString( ( ( Int32 )( healthCost ) ) ) );
			inkTextRef.SetVisible( m_maxHealthTextPath, true );
			healthCost = healthCost / ( ( Float )( m_currentHealth ) );
			fullBarSize = inkWidgetRef.GetSize( m_fullBar );
			inkWidgetRef.SetSize( m_damegePreview, Vector2( healthCost * fullBarSize.X, fullBarSize.Y + 6.0 ) );
			inkWidgetRef.SetVisible( m_damegePreview, true );
		}
		else
		{
			m_healthMemoryJumpAnim.Stop();
			m_pulseBar.Stop();
			m_pulseText.Stop();
			m_pulse.Start();
			inkWidgetRef.SetVisible( m_damegePreview, false );
			inkTextRef.SetVisible( m_maxHealthTextPath, false );
		}
		if( e.JustHacked )
		{
			if( m_healthMemoryFlashAnim.IsPlaying() )
			{
				m_healthMemoryFlashAnim.GotoStartAndStop();
			}
			m_healthMemoryFlashAnim = PlayLibraryAnimation( 'overclock_use' );
		}
	}

	protected event OnUpdateHealthBarVisibility()
	{
		var isMaxHP, areQuickhacksUsed : Bool;
		isMaxHP = m_currentHealth == m_maximumHealth;
		areQuickhacksUsed = m_usedQuickhacks > 0;
		UpdateGodModeVisibility();
		inkCompoundRef.SetVisible( m_quickhacksContainer, IsCyberdeckEquipped() );
		if( !( m_isInOverclockedState ) )
		{
			inkWidgetRef.Get( m_fullBar ).SetEffectEnabled( inkEffectType.ScanlineWipe, 'ScanlineWipe_0', false );
			GetRootWidget().SetState( 'Default' );
			m_healthMemoryJumpAnim.Stop();
			m_pulseBar.Stop();
			m_pulseText.Stop();
			m_pulse.Stop();
			if( m_currentVisionPSM != gamePSMVision.Default || m_isControllingDevice )
			{
				HideRequest();
				return true;
			}
		}
		if( ( ( ( ( !( isMaxHP ) || areQuickhacksUsed ) || m_combatModePSM == gamePSMCombat.InCombat ) || ( m_quickhacksMemoryPercent < 100.0 ) ) || m_buffsVisible ) || m_isInOverclockedState )
		{
			ShowRequest();
		}
		else
		{
			HideRequest();
		}
	}

	private function HelperHasGodMode() : Bool
	{
		var godMode : GodModeSystem;
		var playerObject : GameObject;
		playerObject = ( ( GameObject )( GetOwnerEntity() ) );
		godMode = GameInstance.GetGodModeSystem( playerObject.GetGame() );
		return godMode.HasGodMode( playerObject.GetEntityID(), gameGodModeType.Invulnerable );
	}

	private function CreateAnimations()
	{
		var fadeInterp : inkAnimTransparency;
		var animStartDelay : Float;
		animStartDelay = 1.0;
		m_animShortFade = new inkAnimDef;
		fadeInterp = new inkAnimTransparency;
		fadeInterp.SetStartDelay( 2.0 );
		fadeInterp.SetStartTransparency( 1.0 );
		fadeInterp.SetEndTransparency( 0.0 );
		fadeInterp.SetDuration( 0.34999999 );
		m_animShortFade.AddInterpolator( fadeInterp );
		m_animLongFade = new inkAnimDef;
		fadeInterp = new inkAnimTransparency;
		fadeInterp.SetStartDelay( 10.0 );
		fadeInterp.SetStartTransparency( 1.0 );
		fadeInterp.SetEndTransparency( 0.0 );
		fadeInterp.SetDuration( 0.34999999 );
		m_animLongFade.AddInterpolator( fadeInterp );
		m_animHideTemp = new inkAnimDef;
		fadeInterp = new inkAnimTransparency;
		fadeInterp.SetStartDelay( animStartDelay + 0.25999999 );
		fadeInterp.SetStartTransparency( 1.0 );
		fadeInterp.SetEndTransparency( 0.0 );
		fadeInterp.SetDuration( 0.22 );
		m_animHideTemp.AddInterpolator( fadeInterp );
	}

	public function UpdateOvershieldValue( newValue : Float, percToPoints : Float )
	{
		m_currentOvershieldValue = RoundF( newValue * percToPoints );
		UpdateCurrentHealthText();
		m_currentOvershieldValuePercent = ( ( Float )( m_currentOvershieldValue ) ) / ( ( Float )( m_maximumHealth ) );
		m_overshieldBarController.SetNameplateBarProgress( m_currentOvershieldValuePercent, false );
	}

	public function EvaluateOvershieldBarVisibility()
	{
		m_useOevershield = PlayerDevelopmentSystem.GetData( m_playerObject ).IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Body_Central_Milestone_3 );
		if( !( m_useOevershield ) )
		{
			m_currentOvershieldValue = 0;
			m_currentOvershieldValuePercent = 0.0;
		}
		inkWidgetRef.SetVisible( m_overshieldBarRef, m_useOevershield && ( m_currentOvershieldValuePercent > 0.0 ) );
	}

	public function EvaluateHealthBarVisibility( isInOverclockedState : Bool )
	{
		var blackboard : IBlackboard;
		if( m_playerObject )
		{
			blackboard = GameInstance.GetBlackboardSystem( m_playerObject.GetGame() ).Get( GetAllBlackboardDefs().UI_QuickSlotsData );
			if( blackboard )
			{
				m_isInOverclockedState = isInOverclockedState;
				blackboard.SetBool( GetAllBlackboardDefs().UI_QuickSlotsData.quickhackPanelKeepContext, isInOverclockedState );
			}
		}
		ShowOverclockedHealthbar();
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnCombatStateChanged( value : Int32 )
	{
		m_combatModePSM = ( ( gamePSMCombat )( value ) );
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnPSMVisionStateChanged( value : Int32 )
	{
		m_currentVisionPSM = ( ( gamePSMVision )( value ) );
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnSceneTierChange( argTier : Int32 )
	{
		m_sceneTier = ( ( GameplayTier )( argTier ) );
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnBuffListVisibilityChanged( evt : BuffListVisibilityChangedEvent )
	{
		m_buffsVisible = evt.m_hasBuffs;
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnForceHide()
	{
		RequestHealthBarVisibilityUpdate();
	}

	protected event OnForceTierVisibility( tierVisibility : Bool )
	{
		RequestHealthBarVisibilityUpdate();
	}

}

class QuickhackBarController extends inkLogicController
{
	private editable var m_emptyMask : inkWidgetRef;
	private editable var m_empty : inkWidgetRef;
	private editable var m_full : inkWidgetRef;

	public function SetStatus( value : Float )
	{
		if( value <= 0.0 )
		{
			inkWidgetRef.SetVisible( m_full, false );
			inkWidgetRef.SetVisible( m_empty, true );
			inkWidgetRef.SetScale( m_emptyMask, Vector2( 1.0, 1.0 ) );
		}
		else if( value >= 1.0 )
		{
			inkWidgetRef.SetVisible( m_empty, false );
			inkWidgetRef.SetVisible( m_full, true );
		}
		else
		{
			inkWidgetRef.SetVisible( m_full, true );
			inkWidgetRef.SetVisible( m_empty, true );
			inkWidgetRef.SetScale( m_emptyMask, Vector2( 1.0, 1.0 - value ) );
		}
	}

}

class OvershieldListener extends ScriptStatPoolsListener
{
	private var m_healthBar : weak< healthbarWidgetGameController >;

	public function BindHelathBar( bar : weak< healthbarWidgetGameController > )
	{
		m_healthBar = bar;
	}

	public override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		if( newValue != oldValue )
		{
			m_healthBar.UpdateOvershieldValue( newValue, percToPoints );
		}
		m_healthBar.EvaluateOvershieldBarVisibility();
	}

}

class OverclockListener extends ScriptStatusEffectListener
{
	private var m_healthBar : weak< healthbarWidgetGameController >;

	public function BindHelathBar( bar : weak< healthbarWidgetGameController > )
	{
		m_healthBar = bar;
	}

	public virtual function OnStatusEffectApplied( statusEffect : weak< StatusEffect_Record > )
	{
		if( statusEffect.GetID() == QuickHackableHelper.GetOverclockedStateTweakDBID() )
		{
			m_healthBar.EvaluateHealthBarVisibility( true );
		}
	}

	public virtual function OnStatusEffectRemoved( statusEffect : weak< StatusEffect_Record > )
	{
		if( statusEffect.GetID() == QuickHackableHelper.GetOverclockedStateTweakDBID() )
		{
			m_healthBar.EvaluateHealthBarVisibility( false );
		}
	}

}

class OverclockDamagePreview extends Event
{
	var IsHovering : Bool;
	var JustHacked : Bool;
	var MemoryCost : Int32;
}

