struct NpcNameplateVisualData
{
	var npcNextToCrosshair : NPCNextToTheCrosshair;
}

class NameplateVisibleEvent extends Event
{
	var isNameplateVisible : Bool;
	var entityID : EntityID;
}

import class NpcNameplateGameController extends inkProjectedHUDGameController
{
	private import var projection : inkWidgetRef;
	private editable var m_displayName : inkWidgetRef;
	private editable var m_mappinSlot : inkWidgetRef;
	private editable var m_chattersSlot : inkWidgetRef;
	private var m_rootWidget : weak< inkCompoundWidget >;
	private var m_visualController : weak< NameplateVisualsLogicController >;
	private var m_cachedMappinControllers : array< weak< BaseMappinBaseController > >;
	private var m_visualControllerNeedsMappinsUpdate : Bool;
	default m_visualControllerNeedsMappinsUpdate = false;
	private var m_nameplateProjection : inkScreenProjection;
	private var m_nameplateProjectionCloseDistance : inkScreenProjection;
	private var m_nameplateProjectionDevice : inkScreenProjection;
	private var m_nameplateProjectionDeviceCloseDistance : inkScreenProjection;
	private var m_bufferedGameObject : weak< GameObject >;
	private var m_bufferedPuppetHideNameTag : Bool;
	private var m_bufferedCharacterNamePlateRecord : UINameplate_Record;
	private var m_isScanning : Bool;
	private var m_isNewNPC : Bool;
	private var m_attitude : EAIAttitude;
	var m_UI_NameplateDataDef : UI_NameplateDataDef;
	private var m_zoom : Float;
	default m_zoom = 1;
	private var m_currentHealth : Int32;
	default m_currentHealth = 100;
	private var m_maximumHealth : Int32;
	default m_maximumHealth = 100;
	private var c_DisplayRange : Float;
	private var c_MaxDisplayRange : Float;
	private var c_MaxDisplayRangeNotAggressive : Float;
	private var c_DisplayRangeNotAggressive : Float;
	private var m_bbNameplateData : CallbackHandle;
	private var m_bbHighLevelStateID : CallbackHandle;
	private var m_bbNPCNamesEnabledID : CallbackHandle;
	private var m_VisionStateBlackboardId : CallbackHandle;
	private var m_ZoomStateBlackboardId : CallbackHandle;
	private var m_playerZonesBlackboardID : CallbackHandle;
	private var m_playerCombatBlackboardID : CallbackHandle;
	private var m_playerAimStatusBlackboardID : CallbackHandle;
	private var m_damagePreviewBlackboardID : CallbackHandle;
	private var m_uiBlackboardTargetNPC : weak< IBlackboard >;
	private var m_uiBlackboardInteractions : weak< IBlackboard >;
	private var m_interfaceOptionsBlackboard : weak< IBlackboard >;
	private var m_uiBlackboardNameplateBlackboard : weak< IBlackboard >;
	private var m_nextDistanceCheckTime : Float;

	private import function GetNameplateVisible() : Bool;
	private import function SetNameplateVisible( visible : Bool );
	private import function SlotWidget( widgetToSlot : weak< inkWidget >, newParentWidget : weak< inkWidget >, optional index : Int32 );
	private import function UnslotWidget( widgetToUnslot : weak< inkWidget > );
	private import function IsWidgetSlotted( widget : weak< inkWidget > ) : Bool;
	private import function SetSlottedWidgets( widgetsToSlot : array< inkWidget >, newParentWidger : weak< inkWidget > );
	private import function ClearSlottedWidgets();

	protected event OnInitialize()
	{
		var nameplateProjectionData : inkScreenProjectionData;
		var playerPuppet : PlayerPuppet;
		c_DisplayRange = SNameplateRangesData.GetDisplayRange();
		c_MaxDisplayRange = SNameplateRangesData.GetMaxDisplayRange();
		c_DisplayRangeNotAggressive = SNameplateRangesData.GetDisplayRangeNotAggressive();
		c_MaxDisplayRangeNotAggressive = SNameplateRangesData.GetMaxDisplayRangeNotAggressive();
		m_uiBlackboardTargetNPC = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_NPCNextToTheCrosshair );
		m_bbNameplateData = m_uiBlackboardTargetNPC.RegisterDelayedListenerVariant( GetAllBlackboardDefs().UI_NPCNextToTheCrosshair.NameplateData, this, 'OnNameplateDataChanged' );
		m_uiBlackboardInteractions = GetBlackboardSystem().Get( GetAllBlackboardDefs().UIInteractions );
		m_rootWidget = ( ( inkCompoundWidget )( GetRootWidget() ) );
		m_visualController = ( ( NameplateVisualsLogicController )( inkWidgetRef.GetController( projection ) ) );
		m_interfaceOptionsBlackboard = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_InterfaceOptions );
		m_UI_NameplateDataDef = GetAllBlackboardDefs().UI_NameplateData;
		m_uiBlackboardNameplateBlackboard = GetBlackboardSystem().Get( m_UI_NameplateDataDef );
		m_bbNPCNamesEnabledID = m_interfaceOptionsBlackboard.RegisterDelayedListenerBool( GetAllBlackboardDefs().UI_InterfaceOptions.NPCNamesEnabled, this, 'OnNPCNamesEnabledChanged' );
		m_visualController.UpdateNPCNamesEnabled( m_interfaceOptionsBlackboard.GetBool( GetAllBlackboardDefs().UI_InterfaceOptions.NPCNamesEnabled ), true );
		nameplateProjectionData.fixedWorldOffset = Vector4( 0.0, 0.0, 1.17999995, 0.0 );
		nameplateProjectionData.slotComponentName = 'UI_Slots';
		nameplateProjectionData.slotName = 'UI_Interaction';
		nameplateProjectionData.adjustWithDistance = true;
		nameplateProjectionData.extraOffsetAtMaxDistance = Vector3( 0.0, 0.0, 0.2 );
		m_nameplateProjection = RegisterScreenProjection( nameplateProjectionData );
		nameplateProjectionData.adjustWithDistance = false;
		nameplateProjectionData.fixedWorldOffset.Z = 0.22;
		m_nameplateProjectionCloseDistance = RegisterScreenProjection( nameplateProjectionData );
		nameplateProjectionData.fixedWorldOffset.Z = 0.60000002;
		nameplateProjectionData.slotName = 'Nameplate';
		m_nameplateProjectionDevice = RegisterScreenProjection( nameplateProjectionData );
		nameplateProjectionData.fixedWorldOffset.Z = 0.0;
		m_nameplateProjectionDeviceCloseDistance = RegisterScreenProjection( nameplateProjectionData );
		inkWidgetRef.SetVAlign( projection, inkEVerticalAlign.Top );
		playerPuppet = ( ( PlayerPuppet )( GetPlayerControlledObject() ) );
		if( playerPuppet )
		{
			if( GameInstance.GetRuntimeInfo( playerPuppet.GetGame() ).IsMultiplayer() )
			{
				m_rootWidget.ChangeTranslation( Vector2( 0.0, -14.0 ) );
			}
		}
		SetMainVisible( false );
		m_uiBlackboardNameplateBlackboard.SetBool( GetAllBlackboardDefs().UI_NameplateData.IsVisible, false, false );
		m_damagePreviewBlackboardID = m_uiBlackboardNameplateBlackboard.RegisterDelayedListenerInt( GetAllBlackboardDefs().UI_NameplateData.DamageProjection, this, 'OnDamagePreview' );
		UpdateHealthbarColor( false );
		EnableSleeping( true );
		EnableUpdates( false );
	}

	protected function RegisterPSMListeners( playerPuppet : GameObject )
	{
		var uiBlackboardPSM : IBlackboard;
		uiBlackboardPSM = GetPSMBlackboard( playerPuppet );
		if( uiBlackboardPSM )
		{
			m_VisionStateBlackboardId = uiBlackboardPSM.RegisterDelayedListenerInt( GetAllBlackboardDefs().PlayerStateMachine.Vision, this, 'OnIsEnabledChange' );
			m_ZoomStateBlackboardId = uiBlackboardPSM.RegisterDelayedListenerFloat( GetAllBlackboardDefs().PlayerStateMachine.ZoomLevel, this, 'OnZoomChanged' );
			m_playerZonesBlackboardID = uiBlackboardPSM.RegisterDelayedListenerInt( GetAllBlackboardDefs().PlayerStateMachine.Zones, this, 'OnZoneChange' );
			m_playerCombatBlackboardID = uiBlackboardPSM.RegisterListenerInt( GetAllBlackboardDefs().PlayerStateMachine.UpperBody, this, 'OnAimStatusChange' );
			m_playerAimStatusBlackboardID = uiBlackboardPSM.RegisterListenerInt( GetAllBlackboardDefs().PlayerStateMachine.Combat, this, 'OnPlayerCombatChange' );
			m_visualController.UpdatePlayerZone( ( ( gamePSMZones )( uiBlackboardPSM.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Zones ) ) ), true );
			m_visualController.UpdatePlayerCombat( ( ( gamePSMCombat )( uiBlackboardPSM.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Combat ) ) ), true );
			m_visualController.UpdatePlayerAimStatus( ( ( gamePSMUpperBodyStates )( uiBlackboardPSM.GetInt( GetAllBlackboardDefs().PlayerStateMachine.UpperBody ) ) ), true );
		}
	}

	protected function EnableUpdates( enable : Bool )
	{
		m_nameplateProjection.SetEnabled( enable );
		m_nameplateProjectionCloseDistance.SetEnabled( enable );
		m_nameplateProjectionDevice.SetEnabled( enable );
		m_nameplateProjectionDeviceCloseDistance.SetEnabled( enable );
		if( enable )
		{
			WakeUp();
		}
	}

	protected function UnregisterPSMListeners( playerPuppet : GameObject )
	{
		var uiBlackboardPSM : IBlackboard;
		uiBlackboardPSM = GetPSMBlackboard( playerPuppet );
		if( uiBlackboardPSM )
		{
			uiBlackboardPSM.UnregisterDelayedListener( GetAllBlackboardDefs().PlayerStateMachine.Vision, m_VisionStateBlackboardId );
			uiBlackboardPSM.UnregisterDelayedListener( GetAllBlackboardDefs().PlayerStateMachine.ZoomLevel, m_ZoomStateBlackboardId );
			uiBlackboardPSM.UnregisterDelayedListener( GetAllBlackboardDefs().PlayerStateMachine.Zones, m_playerZonesBlackboardID );
			uiBlackboardPSM.UnregisterDelayedListener( GetAllBlackboardDefs().PlayerStateMachine.Combat, m_playerCombatBlackboardID );
			uiBlackboardPSM.UnregisterDelayedListener( GetAllBlackboardDefs().PlayerStateMachine.UpperBody, m_playerAimStatusBlackboardID );
		}
	}

	protected event OnUninitialize()
	{
		if( m_uiBlackboardTargetNPC )
		{
			if( m_bbNameplateData )
			{
				m_uiBlackboardTargetNPC.UnregisterDelayedListener( GetAllBlackboardDefs().UI_NPCNextToTheCrosshair.NameplateData, m_bbNameplateData );
			}
			if( m_bbNPCNamesEnabledID )
			{
				m_interfaceOptionsBlackboard.UnregisterDelayedListener( GetAllBlackboardDefs().UI_InterfaceOptions.NPCNamesEnabled, m_bbNPCNamesEnabledID );
			}
		}
		if( m_uiBlackboardNameplateBlackboard )
		{
			if( m_damagePreviewBlackboardID )
			{
				m_uiBlackboardNameplateBlackboard.UnregisterDelayedListener( GetAllBlackboardDefs().UI_NameplateData.DamageProjection, m_damagePreviewBlackboardID );
			}
		}
	}

	protected event OnPlayerAttach( playerGameObject : GameObject )
	{
		RegisterPSMListeners( playerGameObject );
	}

	protected event OnPlayerDetach( playerGameObject : GameObject )
	{
		UnregisterPSMListeners( playerGameObject );
	}

	protected event OnScreenProjectionUpdate( projections : gameuiScreenProjectionsData )
	{
		var isNameplateVisible : Bool;
		var showDisplayName : Bool;
		var targetID : EntityID;
		var nameplateDisplayType : gamedataUINameplateDisplayType;
		var entityJustSet : Bool;
		var mountInfo : MountingInfo;
		var data : DialogChoiceHubs;
		var owner : weak< GameObject >;
		var invalidID : EntityID;
		var time : Float;
		var bufferedPuppet : gamePuppet;
		isNameplateVisible = false;
		showDisplayName = false;
		entityJustSet = false;
		if( m_bufferedGameObject )
		{
			time = EngineTime.ToFloat( GameInstance.GetEngineTime( m_bufferedGameObject.GetGame() ) );
			if( m_nextDistanceCheckTime < time )
			{
				m_nextDistanceCheckTime = time + 0.25;
				if( HelperCheckDistance( m_bufferedGameObject ) )
				{
					entityJustSet = m_nameplateProjection.GetEntity() == NULL;
					SetNameplateProjectionEntity( m_bufferedGameObject );
				}
				else
				{
					SetMainVisible( false );
					SetNameplateProjectionEntity( NULL );
				}
			}
		}
		if( ( m_nameplateProjection.GetEntity() != NULL ) && !( entityJustSet ) )
		{
			if( m_bufferedGameObject && m_bufferedGameObject.IsDevice() )
			{
				ApplyProjectionMarginOnWidget( inkWidgetRef.Get( projection ), inkMargin( projections.data[ 2 ].currentPosition.X, ComputeTopMargin( projections.data[ 3 ].currentPosition.Y, projections.data[ 2 ].currentPosition.Y ), 0.0, 0.0 ) );
			}
			else
			{
				ApplyProjectionMarginOnWidget( inkWidgetRef.Get( projection ), inkMargin( projections.data[ 0 ].currentPosition.X, ComputeTopMargin( projections.data[ 1 ].currentPosition.Y, projections.data[ 0 ].currentPosition.Y ), 0.0, 0.0 ) );
			}
			if( m_bufferedGameObject && m_bufferedGameObject.IsAttached() )
			{
				targetID = GetHUDManager().GetCurrentTargetID();
				if( ( targetID != EntityID() ) && ( targetID != m_bufferedGameObject.GetEntityID() ) )
				{
					isNameplateVisible = true;
				}
				else
				{
					isNameplateVisible = !( m_bufferedPuppetHideNameTag );
				}
				mountInfo = GameInstance.GetMountingFacility( m_bufferedGameObject.GetGame() ).GetMountingInfoSingleWithIds( m_bufferedGameObject.GetEntityID() );
				if( EntityID.IsDefined( mountInfo.parentId ) )
				{
					isNameplateVisible = false;
				}
				if( isNameplateVisible )
				{
					if( ( m_bufferedCharacterNamePlateRecord != NULL ) && m_bufferedCharacterNamePlateRecord.Enabled() )
					{
						nameplateDisplayType = m_bufferedCharacterNamePlateRecord.Type().Type();
						switch( nameplateDisplayType )
						{
							case gamedataUINameplateDisplayType.Always:
								showDisplayName = true;
							break;
							case gamedataUINameplateDisplayType.AfterScan:
								showDisplayName = m_bufferedGameObject.IsScanned();
							break;
							default:
								showDisplayName = false;
							break;
						}
					}
				}
			}
			owner = ( ( GameObject )( GetOwnerEntity() ) );
			data = ( ( DialogChoiceHubs )( m_uiBlackboardInteractions.GetVariant( GetAllBlackboardDefs().UIInteractions.DialogChoiceHubs ) ) );
			if( ( data.choiceHubs.Size() > 0 ) || GameInstance.GetSceneSystem( owner.GetGame() ).GetScriptInterface().IsRewindableSectionActive() )
			{
				isNameplateVisible = false;
			}
			if( isNameplateVisible )
			{
				isNameplateVisible = m_visualController.IsAnyElementVisible();
			}
			if( bufferedPuppet = ( ( gamePuppet )( m_bufferedGameObject ) ) )
			{
				if( bufferedPuppet.GetBlackboard().GetBool( GetAllBlackboardDefs().Puppet.HideNameplate ) )
				{
					isNameplateVisible = false;
				}
			}
			SetMainVisible( isNameplateVisible );
			inkWidgetRef.SetVisible( m_displayName, showDisplayName );
		}
		if( projections.data[ 0 ] && projections.data[ 0 ].GetEntity() )
		{
			m_uiBlackboardNameplateBlackboard.SetVariant( m_UI_NameplateDataDef.EntityID, projections.data[ 0 ].GetEntity().GetEntityID(), false );
		}
		else
		{
			m_uiBlackboardNameplateBlackboard.SetVariant( m_UI_NameplateDataDef.EntityID, invalidID, false );
		}
		m_uiBlackboardNameplateBlackboard.SetBool( m_UI_NameplateDataDef.IsVisible, GetNameplateVisible(), false );
	}

	protected event OnZoomChanged( value : Float )
	{
		m_zoom = MaxF( 1.0, ( ( Float )( value ) ) );
	}

	protected event OnNameplateDataChanged( value : Variant )
	{
		var playerPuppet : PlayerPuppet;
		var puppetNPC : weak< NPCPuppet >;
		var requestStatsEvent : RequestStats;
		var incomingData : NPCNextToTheCrosshair;
		var nameplateBlackboard : UI_NameplateDataDef;
		var charRecord : Character_Record;
		var invalidID : EntityID;
		incomingData = ( ( NPCNextToTheCrosshair )value );
		m_attitude = incomingData.attitude;
		if( m_bufferedGameObject != incomingData.npc )
		{
			m_isNewNPC = true;
			m_bufferedGameObject = incomingData.npc;
			if( m_bufferedGameObject )
			{
				EnableUpdates( true );
			}
			m_nextDistanceCheckTime = -1.0;
			puppetNPC = ( ( NPCPuppet )( m_bufferedGameObject ) );
			if( puppetNPC != NULL )
			{
				charRecord = TweakDBInterface.GetCharacterRecord( puppetNPC.GetRecordID() );
				if( charRecord )
				{
					m_bufferedCharacterNamePlateRecord = charRecord.UiNameplate();
				}
				else
				{
					m_bufferedCharacterNamePlateRecord = NULL;
				}
				m_bufferedPuppetHideNameTag = puppetNPC.GetBoolFromCharacterTweak( "hide_nametag" );
			}
			else
			{
				m_bufferedCharacterNamePlateRecord = NULL;
				m_bufferedPuppetHideNameTag = false;
			}
			SetNameplateProjectionEntity( m_bufferedGameObject );
		}
		if( incomingData.npc == NULL )
		{
			m_visualController.SetVisualData( NULL, incomingData, m_isNewNPC );
			EnableUpdates( false );
			SetMainVisible( false );
			SetNameplateProjectionEntity( NULL );
			m_uiBlackboardNameplateBlackboard.SetVariant( m_UI_NameplateDataDef.EntityID, invalidID, false );
			m_uiBlackboardNameplateBlackboard.SetBool( m_UI_NameplateDataDef.IsVisible, false, false );
		}
		else
		{
			if( m_visualControllerNeedsMappinsUpdate )
			{
				UpdateVisualControllerState( m_cachedMappinControllers );
			}
			m_visualController.SetVisualData( m_bufferedGameObject, incomingData, m_isNewNPC );
			GetBlackboardSystem().Get( nameplateBlackboard ).SetFloat( m_UI_NameplateDataDef.HeightOffset, 0.0, true );
		}
		m_visualControllerNeedsMappinsUpdate = false;
		playerPuppet = ( ( PlayerPuppet )( GetPlayerControlledObject() ) );
		requestStatsEvent = new RequestStats;
		playerPuppet.QueueEvent( requestStatsEvent );
	}

	protected event OnIsEnabledChange( val : Int32 )
	{
		if( val == ( ( Int32 )( gamePSMVision.Default ) ) )
		{
			m_isScanning = false;
		}
		else if( val == ( ( Int32 )( gamePSMVision.Focus ) ) )
		{
			m_isScanning = true;
		}
	}

	protected event OnZoneChange( value : Int32 )
	{
		m_visualController.UpdatePlayerZone( ( ( gamePSMZones )( value ) ) );
	}

	protected event OnAimStatusChange( value : Int32 )
	{
		m_visualController.UpdatePlayerAimStatus( ( ( gamePSMUpperBodyStates )( value ) ) );
	}

	protected event OnPlayerCombatChange( value : Int32 )
	{
		m_visualController.UpdatePlayerCombat( ( ( gamePSMCombat )( value ) ) );
	}

	protected event OnDamagePreview( value : Int32 )
	{
		m_visualController.PreviewDamage( value );
	}

	protected event OnNPCNamesEnabledChanged( value : Bool )
	{
		m_visualController.UpdateNPCNamesEnabled( value );
	}

	public function UpdateHealthbarColor( isHostile : Bool )
	{
		m_visualController.UpdateHealthbarColor( isHostile );
	}

	protected event OnMappinsUpdated( mappinControllers : array< weak< BaseMappinBaseController > > )
	{
		m_cachedMappinControllers = mappinControllers;
		m_visualControllerNeedsMappinsUpdate = true;
	}

	protected export function ResolveSlotAttachment()
	{
		UpdateSlotAttachment( m_cachedMappinControllers );
	}

	private function UpdateVisualControllerState( const mappinControllers : ref< array< weak< BaseMappinBaseController > > > )
	{
		var profile : weak< MappinUIRuntimeProfile_Record >;
		var controller : weak< BaseMappinBaseController >;
		var mappin : weak< IMappin >;
		var i, count : Int32;
		m_visualController.SetQuestTarget( false );
		m_visualController.SetForceHide( false );
		count = mappinControllers.Size();
		for( i = 0; i < count; i += 1 )
		{
			controller = mappinControllers[ i ];
			mappin = controller.GetMappin();
			profile = controller.GetProfile();
			if( mappin )
			{
				if( mappin.IsQuestImportant() )
				{
					m_visualController.SetQuestTarget( true );
				}
			}
			if( profile )
			{
				if( !( profile.KeepNameplate() ) )
				{
					m_visualController.SetForceHide( true );
				}
			}
		}
	}

	private function UpdateSlotAttachment( const mappinControllers : ref< array< weak< BaseMappinBaseController > > > )
	{
		var i, count : Int32;
		var attachmentWidget : weak< inkWidget >;
		var widgetsToSlot : array< inkWidget >;
		if( GetNameplateVisible() )
		{
			count = mappinControllers.Size();
			for( i = 0; i < count; i += 1 )
			{
				attachmentWidget = mappinControllers[ i ].GetWidgetForNameplateSlot();
				if( attachmentWidget )
				{
					widgetsToSlot.PushBack( attachmentWidget );
				}
			}
			SetSlottedWidgets( widgetsToSlot, inkWidgetRef.Get( m_mappinSlot ) );
		}
		else
		{
			ClearSlottedWidgets();
		}
	}

	private function SetMainVisible( visible : Bool )
	{
		var wasVisible : Bool;
		wasVisible = GetNameplateVisible();
		if( visible != wasVisible || m_isNewNPC )
		{
			SetNameplateVisible( visible );
			SetNameplateOwnerID( visible );
		}
		m_isNewNPC = false;
	}

	private function SetNameplateOwnerID( visible : Bool )
	{
		var id : EntityID;
		if( m_uiBlackboardInteractions )
		{
			if( visible && m_bufferedGameObject )
			{
				id = m_bufferedGameObject.GetEntityID();
			}
			m_uiBlackboardInteractions.SetEntityID( GetAllBlackboardDefs().UIInteractions.NameplateOwnerID, id );
		}
	}

	private function GetHUDManager() : HUDManager
	{
		return ( ( HUDManager )( GameInstance.GetScriptableSystemsContainer( GetPlayerControlledObject().GetGame() ).Get( 'HUDManager' ) ) );
	}

	private function GetDistanceToEntity( entity : Entity ) : Float
	{
		var distToEntity : Float;
		var playerPuppet : PlayerPuppet;
		var puppet : weak< GameObject >;
		distToEntity = -1.0;
		playerPuppet = ( ( PlayerPuppet )( GetPlayerControlledObject() ) );
		puppet = ( ( GameObject )( entity ) );
		if( puppet )
		{
			distToEntity = Vector4.Distance( playerPuppet.GetWorldPosition(), puppet.GetWorldPosition() );
		}
		return distToEntity;
	}

	private function HelperCheckDistance( entity : Entity ) : Bool
	{
		var puppet : weak< ScriptedPuppet >;
		var gameObject : weak< GameObject >;
		var distToEntity, max_dist : Float;
		var displayRange : Float;
		var displayMaxRange : Float;
		if( entity == NULL )
		{
			return false;
		}
		gameObject = ( ( GameObject )( entity ) );
		puppet = ( ( ScriptedPuppet )( entity ) );
		if( puppet && ( m_attitude == EAIAttitude.AIA_Hostile || ( puppet.IsAggressive() && m_attitude != EAIAttitude.AIA_Friendly ) ) )
		{
			displayRange = c_DisplayRange;
			displayMaxRange = c_MaxDisplayRange;
		}
		else if( ( gameObject && gameObject.IsTurret() ) && m_attitude != EAIAttitude.AIA_Friendly )
		{
			displayRange = c_DisplayRange;
			displayMaxRange = c_MaxDisplayRange;
		}
		else
		{
			displayRange = c_DisplayRangeNotAggressive;
			displayMaxRange = c_MaxDisplayRangeNotAggressive;
		}
		distToEntity = MinF( GetDistanceToEntity( entity ), displayMaxRange * m_zoom );
		max_dist = displayRange * m_zoom;
		if( distToEntity < max_dist )
		{
			return true;
		}
		return false;
	}

	private function SetNameplateProjectionEntity( entity : Entity )
	{
		m_nameplateProjection.SetEntity( entity );
		m_nameplateProjectionDevice.SetEntity( entity );
		m_nameplateProjectionCloseDistance.SetEntity( entity );
		m_nameplateProjectionDeviceCloseDistance.SetEntity( entity );
	}

	private function ComputeTopMargin( marginClosest : Float, marginFurthest : Float ) : Float
	{
		var distance, lerpCoef, result : Float;
		distance = GetDistanceToEntity( m_nameplateProjection.GetEntity() );
		if( distance >= 50.0 )
		{
			return marginFurthest;
		}
		lerpCoef = MinF( distance, 50.0 ) / 50.0;
		result = LerpF( lerpCoef, marginClosest, marginFurthest );
		return result;
	}

}

struct SNameplateRangesData
{
	private var c_DisplayRange : Float;
	default c_DisplayRange = 35;
	private var c_MaxDisplayRange : Float;
	default c_MaxDisplayRange = 50;
	private var c_MaxDisplayRangeNotAggressive : Float;
	default c_MaxDisplayRangeNotAggressive = 10;
	private var c_DisplayRangeNotAggressive : Float;
	default c_DisplayRangeNotAggressive = 3;

	public static function GetDisplayRange() : Float
	{
		var self : SNameplateRangesData;
		return self.c_DisplayRange;
	}

	public static function GetMaxDisplayRange() : Float
	{
		var self : SNameplateRangesData;
		return self.c_MaxDisplayRange;
	}

	public static function GetMaxDisplayRangeNotAggressive() : Float
	{
		var self : SNameplateRangesData;
		return self.c_MaxDisplayRangeNotAggressive;
	}

	public static function GetDisplayRangeNotAggressive() : Float
	{
		var self : SNameplateRangesData;
		return self.c_DisplayRangeNotAggressive;
	}

}

