class RipperDocItemBoughtCallback extends InventoryScriptCallback
{
	private var eventTarget : weak< RipperDocGameController >;

	public function Bind( eventTargetArg : RipperDocGameController )
	{
		eventTarget = eventTargetArg;
	}

	public override function OnItemAdded( itemIDArg : ItemID, itemData : weak< gameItemData >, flaggedAsSilent : Bool )
	{
		eventTarget.OnItemBought( itemIDArg, itemData );
	}

}

struct RipperdocCategory
{
	var equipArea : gamedataEquipmentArea;
	var align : inkEHorizontalAlign;
	var parent : inkCompoundRef;
}

class RipperDocGameController extends gameuiMenuGameController
{
	private editable var m_TooltipsManagerRef : inkWidgetRef;
	private editable var m_buttonHintsManagerRef : inkWidgetRef;
	private editable var m_animationControllerContainer : inkWidgetRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_armsAnchor : inkCompoundRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_legsAnchor : inkCompoundRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_handsAnchor : inkCompoundRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_systemAnchor : inkCompoundRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_nervousAnchor : inkCompoundRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_skeletonAnchor : inkCompoundRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_ocularCortexAnchor : inkCompoundRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_integumentaryAnchor : inkCompoundRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_frontalCortexAnchor : inkCompoundRef;
	[ category = "CW SLOT ANCHORS" ]
	private editable var m_cardiovascularAnchor : inkCompoundRef;
	private editable var m_minigridTargetAnchor : inkCompoundRef;
	private var m_minigridTargetAnchorMargin : inkMargin;
	private editable var m_minigridSelectorLeftAnchor : inkCompoundRef;
	private editable var m_minigridSelectorRightAnchor : inkCompoundRef;
	private var m_minigridSelectorLeftAnchorMargin : inkMargin;
	private var m_minigridSelectorRightAnchorMargin : inkMargin;
	private editable var m_tooltipLeftAnchor : inkWidgetRef;
	private editable var m_tooltipRightAnchor : inkWidgetRef;
	[ category = "Upgrade" ]
	private editable var m_upgradeResourcesAnchor : inkCompoundRef;
	[ category = "Upgrade" ]
	private editable var m_upgradeCWInputName : CName;
	default m_upgradeCWInputName = 'upgrade_cyberware';
	[ tooltip = "Width after which low tier components will be collapsed - hide comp. counter." ][ category = "Upgrade" ]
	private editable var m_upgradeResourcesContainerMaxWidth : Float;
	default m_upgradeResourcesContainerMaxWidth = 860.f;
	private editable var m_allocationPointContainerDefault : inkCompoundRef;
	private editable var m_inventoryViewAnchor : inkCompoundRef;
	private editable var m_selectorAnchor : inkCompoundRef;
	private editable var m_inventoryWarnning : inkWidgetRef;
	[ category = "Mask" ]
	private editable var m_maleEyeAndMaskBinkAnimation : inkVideoRef;
	[ category = "Mask" ]
	private editable var m_femaleEyeAndMaskBinkAnimation : inkVideoRef;
	[ category = "Mask" ]
	private const var c_maleOcular : ResRef;
	default c_maleOcular = R"base\movies\misc\paperdoll\man\man_ocular.bk2";
	[ category = "Mask" ]
	private const var c_femaleOcular : ResRef;
	default c_femaleOcular = R"base\movies\misc\paperdoll\woman\woman_ocular.bk2";
	[ category = "Mask" ]
	private const var c_maleMask : ResRef;
	default c_maleMask = R"base\movies\misc\paperdoll\man\man_mask.bk2";
	[ category = "Mask" ]
	private const var c_femaleMask : ResRef;
	default c_femaleMask = R"base\movies\misc\paperdoll\woman\woman_mask.bk2";
	[ category = "Minigrid Animations" ]
	private editable var m_minigridSetPositionAnimationSpeed : Float;
	default m_minigridSetPositionAnimationSpeed = 0.3f;
	[ category = "Minigrid Animations" ]
	private editable var m_minigridSetPositionAnimInterpolationType : inkanimInterpolationType;
	default m_minigridSetPositionAnimInterpolationType = inkanimInterpolationType.Quadratic;
	[ category = "Minigrid Animations" ]
	private editable var m_minigridSetPositionAnimInterpolationMode : inkanimInterpolationMode;
	default m_minigridSetPositionAnimInterpolationMode = inkanimInterpolationMode.EasyInOut;
	[ category = "Minigrid Animations" ]
	private editable var m_minigridIntroAnimationSpeed : Float;
	default m_minigridIntroAnimationSpeed = 0.1f;
	[ category = "Minigrid Animations" ]
	private editable var m_minigridIntroAnimInterpolationType : inkanimInterpolationType;
	default m_minigridIntroAnimInterpolationType = inkanimInterpolationType.Quadratic;
	[ category = "Minigrid Animations" ]
	private editable var m_minigridIntroAnimInterpolationMode : inkanimInterpolationMode;
	default m_minigridIntroAnimInterpolationMode = inkanimInterpolationMode.EasyOut;
	private editable var m_capacityTutorialAnchor : inkWidgetRef;
	private editable var m_armorTutorialAnchor : inkWidgetRef;
	private editable var m_slotsTutorialAnchor : inkWidgetRef;
	private var m_vikTutorial : Bool;
	private var m_isTutorial : Bool;
	private var m_ep1StandaloneTutorial : Bool;
	private var m_mq048TutorialFact : Bool;
	private var m_isReturningPlayer : Bool;
	private var m_tutorialEyesCW : TweakDBID;
	private var m_tutorialHandsCW : TweakDBID;
	private var m_tutorialArmorCW : TweakDBID;
	private var m_tutorialZeroCapacityModifier : gameStatModifierData;
	[ category = "Perk Tooltips" ]
	editable var m_perkBarCapacity : gamedataNewPerkType;
	default m_perkBarCapacity = gamedataNewPerkType.Tech_Master_Perk_3;
	[ category = "Perk Tooltips" ]
	editable var m_perkBarArmor : gamedataNewPerkType;
	default m_perkBarArmor = gamedataNewPerkType.Tech_Central_Milestone_3;
	[ category = "Perk Tooltips" ]
	editable var m_perkSlotSkeleton : gamedataNewPerkType;
	default m_perkSlotSkeleton = gamedataNewPerkType.Tech_Central_Milestone_3;
	[ category = "Perk Tooltips" ]
	editable var m_perkSlotHands : gamedataNewPerkType;
	default m_perkSlotHands = gamedataNewPerkType.Tech_Central_Perk_3_2;
	private var m_ripperdocHoverState : RipperdocHoverState;
	private var m_screen : CyberwareScreenType;
	private var m_filterMode : RipperdocModes;
	private var m_player : weak< PlayerPuppet >;
	private var m_audioSystem : weak< AudioSystem >;
	private var m_uiSystem : weak< UISystem >;
	private var m_questSystem : weak< QuestsSystem >;
	private var m_playerID : EntityID;
	private var m_InventoryManager : InventoryDataManagerV2;
	private var m_uiScriptableSystem : weak< UIScriptableSystem >;
	private var m_uiInventorySystem : weak< UIInventoryScriptableSystem >;
	private var m_menuEventDispatcher : weak< inkMenuEventDispatcher >;
	private var m_ripperdocTokenManager : RipperdocTokenManager;
	private var m_categories : array< RipperdocCategory >;
	private var m_TooltipsManager : weak< gameuiTooltipsManager >;
	private var m_defaultTooltipsMargin : inkMargin;
	private var m_defaultTooltipGap : Float;
	default m_defaultTooltipGap = 10.f;
	private var m_VendorBlackboard : weak< IBlackboard >;
	private var m_equipmentBlackboard : weak< IBlackboard >;
	private var m_equipmentBlackboardCallback : CallbackHandle;
	private var m_tokenBlackboard : weak< IBlackboard >;
	private var m_tokenBlackboardCallback : CallbackHandle;
	private var m_inventoryView : weak< RipperdocInventoryController >;
	private var m_selector : weak< RipperdocSelectorController >;
	private var m_dollHoverArea : gamedataEquipmentArea;
	private var m_dollSelected : Bool;
	private var m_hoverArea : gamedataEquipmentArea;
	private var m_filterArea : gamedataEquipmentArea;
	private var m_lastAreaVisited : gamedataEquipmentArea;
	private var m_filteringByArea : Bool;
	private var m_isInEquipPopup : Bool;
	private var m_isInventoryOpen : Bool;
	private var m_allFilters : array< gamedataEquipmentArea >;
	private var m_cachedAvailableItemsCounters : array< Int32 >;
	private var m_cachedVendorItemsCounters : array< Int32 >;
	private var m_cachedPlayerItemsCounters : array< Int32 >;
	private var m_cachedPlayerItems : array< weak< UIInventoryItem > >[ 10 ];
	private var m_cachedVendorItems : array< weak< WrappedUIInventoryItem > >[ 10 ];
	private var m_vendorItems : inkHashMap;
	private var m_vendorWrappedItems : inkHashMap;
	private var m_soldItemsCache : SoldItemsCache;
	private var m_craftingMaterialsListItems : array< weak< CrafringMaterialItemController > >;
	private var m_upgradeHoldFinished : Bool;
	private var m_commonCraftingMaterials : array< CachedCraftingMaterial >;
	private var m_equipmentMinigrids : array< weak< CyberwareInventoryMiniGrid > >;
	private var m_minigridsMap : gamedataEquipmentArea[ 10 ];
	private var m_isActivePanel : Bool;
	private var m_hasEquipEventTriggered : Bool;
	private var m_hasUnequipEventTriggered : Bool;
	private var m_statsSystem : weak< StatsSystem >;
	private var m_statsDataSystem : weak< StatsDataSystem >;
	private var m_statusEffectSystem : weak< StatusEffectSystem >;
	private var m_inventorySystem : weak< InventoryManager >;
	private var m_isPurchased : Bool;
	private var m_isPurchasing : Bool;
	private var m_isPurchaseEquip : Bool;
	private var m_isUpgrading : Bool;
	private var m_previewMinigrid : weak< CyberwareInventoryMiniGrid >;
	private var m_equippedSlotIndex : Int32;
	default m_equippedSlotIndex = -1;
	private var m_isMusculoskeletalUpgrade3Unlocked : Bool;
	private var m_handleItemEquippedNextFrameRequested : Bool;
	private var m_handleItemEquippedOnItemAdded : TweakDBID;
	private var m_inventoryListener : InventoryScriptListener;
	private var m_tokenPopup : inkGameNotificationToken;
	private var m_playerItemDisplayContext : ItemDisplayContextData;
	private var m_vendorItemDisplayContext : ItemDisplayContextData;
	private var m_inventoryRefreshRequested : Bool;
	private var m_invalidateMinigridsRequested : Bool;
	private var m_upgradeData : RipperdocTokenPopupData;
	private var m_vendorUserData : VendorUserData;
	private var m_VendorDataManager : VendorDataManager;
	private var m_buttonHintsController : weak< ButtonHints >;
	private var m_soldItemsFetched : Bool;
	private var m_animationController : weak< RipperdocScreenAnimationController >;
	private var m_isHoveringOverUpgradableSlot : Bool;
	private var m_upgradeQuality : gamedataQuality;
	private var m_upgradeCostData : CyberwareUpgradeCostData;
	private var m_upgradeItem : Item_Record;
	private var m_hoveredItem : weak< UIInventoryItem >;
	private var m_hoveredItemDisplay : weak< InventoryItemDisplayController >;
	private var m_pulse : PulseAnimation;
	private var m_anim : inkAnimProxy;
	private var m_developmentDataManager : PlayerDevelopmentDataManager;
	private var m_capacityHoverEvent : RipperdocMeterCapacityHoverEvent;
	private var m_capacityApplyEvent : RipperdocMeterCapacityApplyEvent;
	private var m_armorHoverEvent : RipperdocMeterArmorHoverEvent;
	private var m_armorApplyEvent : RipperdocMeterArmorApplyEvent;
	private var m_maxCapacityPossible : Float;
	private var m_capacityBarintroAnimProxy : inkAnimProxy;
	private var m_armorBarintroAnimProxy : inkAnimProxy;
	private var m_armorAttunemendDescription : String;
	default m_armorAttunemendDescription = "LocKey#91185";
	private var m_armorAttunemendDescription2 : String;
	default m_armorAttunemendDescription2 = "LocKey#91756";
	private var m_armorMultBonusDescription : String;
	default m_armorMultBonusDescription = "LocKey#89019";
	private var m_isArmorBarReady : Bool;
	private var m_isCapacityBarReady : Bool;
	private var m_capacityPerk1Bought : Bool;
	private var m_capacityPerk2Bought : Bool;
	private var m_armorPerk1Bought : Bool;
	private var m_armorCWEquipedNum : Int32;
	private var m_cameFromInventoryMenu : Bool;
	private var m_screenDisplayContext : ScreenDisplayContext;

	protected event OnSetUserData( userData : IScriptable )
	{
		var menuData : PreviousMenuData;
		menuData = ( ( PreviousMenuData )( userData ) );
		if( menuData )
		{
			m_cameFromInventoryMenu = true;
		}
		m_vendorUserData = ( ( VendorUserData )( userData ) );
	}

	private function CheckTokenAvailability() : Bool
	{
		if( m_ripperdocTokenManager.IfPlayerHasTokens() )
		{
			return true;
		}
		return false;
	}

	protected event OnInitialize()
	{
		var vendorPanelData : VendorPanelData;
		var vendorData : VendorData;
		var i : Int32;
		var names : array< String >;
		var requiredCapacity, currentAllocatedCapacity, freedCapacity : Float;
		var tutorialItemQuality : gamedataQuality;
		inkWidgetRef.SetVisible( m_inventoryWarnning, false );
		m_inventoryView = ( ( RipperdocInventoryController )( inkCompoundRef.GetController( m_inventoryViewAnchor ) ) );
		m_selector = ( ( RipperdocSelectorController )( inkCompoundRef.GetController( m_selectorAnchor ) ) );
		m_player = ( ( PlayerPuppet )( GetPlayerControlledObject() ) );
		m_playerID = m_player.GetEntityID();
		m_audioSystem = GameInstance.GetAudioSystem( m_player.GetGame() );
		m_uiSystem = GameInstance.GetUISystem( m_player.GetGame() );
		m_questSystem = GameInstance.GetQuestsSystem( m_player.GetGame() );
		m_filterMode = RipperdocModes.Default;
		if( m_vendorUserData )
		{
			vendorPanelData = m_vendorUserData.vendorData;
			vendorData = vendorPanelData.data;
			m_screen = CyberwareScreenType.Ripperdoc;
			m_VendorDataManager = new VendorDataManager;
			m_VendorDataManager.Initialize( GetPlayerControlledObject(), vendorData.entityID );
		}
		else
		{
			m_screen = CyberwareScreenType.Inventory;
		}
		m_statsSystem = GameInstance.GetStatsSystem( m_player.GetGame() );
		m_statsDataSystem = GameInstance.GetStatsDataSystem( m_player.GetGame() );
		m_statusEffectSystem = GameInstance.GetStatusEffectSystem( m_player.GetGame() );
		m_inventorySystem = GameInstance.GetInventoryManager( m_player.GetGame() );
		RegisterInventoryListener( GetPlayerControlledObject() );
		PopulateCategories();
		SpawnMinigrids();
		Init();
		GameInstance.GetTelemetrySystem( GetPlayerControlledObject().GetGame() ).LogVendorMenuState( m_VendorDataManager.GetVendorID(), true );
		m_filterArea = gamedataEquipmentArea.Invalid;
		m_hoverArea = gamedataEquipmentArea.Invalid;
		m_dollHoverArea = gamedataEquipmentArea.Invalid;
		m_ripperdocHoverState = RipperdocHoverState.None;
		m_defaultTooltipsMargin = inkMargin( 60.0, 60.0, 0.0, 0.0 );
		super.OnInitialize();
		m_hasEquipEventTriggered = true;
		m_hasUnequipEventTriggered = true;
		for( i = 0; i < m_categories.Size(); i += 1 )
		{
			m_allFilters.PushBack( m_categories[ i ].equipArea );
			names.PushBack( GetAreaHeader( m_allFilters[ i ] ) );
			m_cachedAvailableItemsCounters.PushBack( 0 );
			m_cachedVendorItemsCounters.PushBack( 0 );
			m_cachedPlayerItemsCounters.PushBack( 0 );
		}
		m_selector.Configure( names );
		m_minigridTargetAnchorMargin = inkCompoundRef.GetMargin( m_minigridTargetAnchor );
		m_minigridSelectorLeftAnchorMargin = inkCompoundRef.GetMargin( m_minigridSelectorLeftAnchor );
		m_minigridSelectorRightAnchorMargin = inkCompoundRef.GetMargin( m_minigridSelectorRightAnchor );
		m_developmentDataManager = new PlayerDevelopmentDataManager;
		m_developmentDataManager.Initialize( ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( GetPlayerControlledObject().GetGame() ).GetLocalPlayerMainGameObject() ) ), this );
		DisplayInventory( false );
		m_maxCapacityPossible = GetMaxCapacityPossible();
		SpawnPerks();
		if( m_screen != CyberwareScreenType.Inventory )
		{
			inkCompoundRef.SetVisible( m_upgradeResourcesAnchor, true );
			PopulateCraftingMaterials();
		}
		else
		{
			inkCompoundRef.SetVisible( m_upgradeResourcesAnchor, false );
		}
		RegisterToGlobalInputCallback( 'OnPostOnRelease', this, 'OnReleaseInput' );
		InitFacePaperdoll();
		m_isReturningPlayer = m_questSystem.GetFact( 'ripperdoc_screen_glitched' ) && m_screen == CyberwareScreenType.Inventory;
		m_vikTutorial = ( ( m_VendorDataManager.GetVendorID() == T"Vendors.wat_lch_ripperdoc_01" ) && ( m_questSystem.GetFact( 'q001_ripperdoc_done' ) == 0 ) ) && m_screen == CyberwareScreenType.Ripperdoc;
		m_isTutorial = m_vikTutorial;
		m_mq048TutorialFact = ( ( m_questSystem.GetFact( 'mq048_active' ) > 0 ) && ( m_questSystem.GetFact( 'mq048_done' ) == 0 ) ) && m_screen == CyberwareScreenType.Ripperdoc;
		m_isTutorial = ( m_isTutorial || m_mq048TutorialFact ) && ( m_questSystem.GetFact( 'ep1_ripperdoc_tutorial_seen' ) == 0 );
		m_ep1StandaloneTutorial = ( ( m_questSystem.GetFact( 'ep1_standalone' ) > 0 ) && ( m_questSystem.GetFact( 'ep1_ripperdoc_tutorial_seen' ) == 0 ) ) && m_screen == CyberwareScreenType.Ripperdoc;
		if( !( m_isTutorial ) && m_ep1StandaloneTutorial )
		{
			m_isTutorial = true;
			if( m_questSystem.GetFact( 'ep1_ripperdoc_tutorial_started' ) < 1 )
			{
				m_questSystem.SetFact( 'tutorial_ripperdoc_eyes_passed', 0 );
				m_questSystem.SetFact( 'tutorial_ripperdoc_hands_passed', 0 );
				m_questSystem.SetFact( 'tutorial_ripperdoc_armor_passed', 0 );
				m_questSystem.SetFact( 'ep1_ripperdoc_tutorial_started', 1 );
			}
		}
		if( m_isTutorial )
		{
			m_statusEffectSystem.ApplyStatusEffect( m_player.GetEntityID(), T"BaseStatusEffect.CyberwareTutorialAdjustments" );
			tutorialItemQuality = RPGManager.ConvertPlayerLevelToCyberwareQuality( GameInstance.GetStatsSystem( m_player.GetGame() ).GetStatValue( m_playerID, gamedataStatType.Level ), false );
			requiredCapacity = m_statsSystem.GetStatValue( m_playerID, gamedataStatType.HumanityOverallocated ) - m_statsSystem.GetStatValue( m_playerID, gamedataStatType.HumanityAvailable );
			if( m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' ) < 1 )
			{
				m_tutorialEyesCW = GetAppropriateEyesTutorialCyberware( tutorialItemQuality );
				freedCapacity += UnequipAllFromGrid( gamedataEquipmentArea.EyesCW );
			}
			if( m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' ) < 1 )
			{
				m_tutorialHandsCW = GetAppropriateHandsTutorialCyberware( tutorialItemQuality );
				freedCapacity += UnequipAllFromGrid( gamedataEquipmentArea.HandsCW );
			}
			if( m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' ) < 1 )
			{
				m_tutorialArmorCW = GetAppropriateArmorTutorialCyberware( tutorialItemQuality );
				freedCapacity += UnequipAllFromGrid( gamedataEquipmentArea.IntegumentarySystemCW );
			}
			requiredCapacity -= freedCapacity;
			if( m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' ) < 1 )
			{
				requiredCapacity += GetTutorialItemCapacityRequirement( m_tutorialEyesCW, m_player );
			}
			if( m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' ) < 1 )
			{
				requiredCapacity += GetTutorialItemCapacityRequirement( m_tutorialHandsCW, m_player );
			}
			if( m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' ) < 1 )
			{
				requiredCapacity += GetTutorialItemCapacityRequirement( m_tutorialArmorCW, m_player );
			}
			AddTutorialItemsToStock( gamedataEquipmentArea.Invalid );
			if( requiredCapacity > 0.0 )
			{
				freedCapacity += requiredCapacity;
				freedCapacity -= FreeUpTheCapacityForTutorial( requiredCapacity );
			}
			AsyncSpawnFromLocal( inkWidgetRef.Get( m_capacityTutorialAnchor ), 'tutorial_popup_capacity', this );
			AsyncSpawnFromLocal( inkWidgetRef.Get( m_armorTutorialAnchor ), 'tutorial_popup_armor', this );
			AsyncSpawnFromLocal( inkWidgetRef.Get( m_slotsTutorialAnchor ), 'tutorial_slots', this );
			currentAllocatedCapacity = m_statsSystem.GetStatValue( m_playerID, gamedataStatType.HumanityAllocated ) - freedCapacity;
			m_tutorialZeroCapacityModifier = RPGManager.CreateStatModifier( gamedataStatType.HumanityAllocated, gameStatModifierType.Additive, -( currentAllocatedCapacity ) );
			m_statsSystem.AddModifier( m_player.GetEntityID(), m_tutorialZeroCapacityModifier );
		}
		m_selector.SetIsInTutorial( m_isTutorial );
		inkCompoundRef.SetVisible( m_selectorAnchor, !( m_isTutorial ) );
		ShowMainScreenTutorials();
		m_audioSystem.Play( 'ui_gui_cyberware_tab_open' );
		if( MarketSystem.IsAttached( m_VendorDataManager.GetVendorInstance() ) )
		{
			OnUIVendorAttachedEvent( NULL );
		}
		if( m_screen == CyberwareScreenType.Inventory )
		{
			PreparePlayerItems();
		}
		SetButtonHints( true, true );
	}

	protected event OnUIVendorAttachedEvent( evt : UIVendorAttachedEvent )
	{
		PreparePlayerItems();
		PrepareVendorItems();
	}

	public static function GetAppropriateEyesTutorialCyberware( itemQuality : gamedataQuality ) : TweakDBID
	{
		switch( itemQuality )
		{
			case gamedataQuality.Common:
				return T"Items.AdvancedKiroshiOptics_q001_1";
			case gamedataQuality.CommonPlus:
				return T"Items.AdvancedKiroshiOptics_Tutorial_CommonPlus";
			case gamedataQuality.Uncommon:
				return T"Items.AdvancedKiroshiOptics_Tutorial_Uncommon";
			case gamedataQuality.UncommonPlus:
				return T"Items.AdvancedKiroshiOptics_Tutorial_UncommonPlus";
			case gamedataQuality.Rare:
				return T"Items.AdvancedKiroshiOptics_Tutorial_Rare";
			case gamedataQuality.RarePlus:
				return T"Items.AdvancedKiroshiOptics_Tutorial_RarePlus";
			case gamedataQuality.Epic:
				return T"Items.AdvancedKiroshiOptics_Tutorial_Epic";
			case gamedataQuality.EpicPlus:
				return T"Items.AdvancedKiroshiOptics_Tutorial_EpicPlus";
			case gamedataQuality.Legendary:
				return T"Items.AdvancedKiroshiOptics_Tutorial_Legendary";
			case gamedataQuality.LegendaryPlus:
				return T"Items.AdvancedKiroshiOptics_Tutorial_LegendaryPlus";
			case gamedataQuality.LegendaryPlusPlus:
				return T"Items.AdvancedKiroshiOptics_Tutorial_LegendaryPlusPlus";
			default:
				return T"Items.AdvancedKiroshiOptics_q001_1";
		}
	}

	private function GetAppropriateHandsTutorialCyberware( itemQuality : gamedataQuality ) : TweakDBID
	{
		var isSmartLink : Bool;
		isSmartLink = m_InventoryManager.HasPlayerSmartGunLink();
		return GetAppropriateHandsTutorialCyberware( itemQuality, isSmartLink );
	}

	public static function GetAppropriateHandsTutorialCyberware( itemQuality : gamedataQuality, isSmartLink : Bool ) : TweakDBID
	{
		switch( itemQuality )
		{
			case gamedataQuality.Common:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_Common" ) : ( T"Items.AdvancedPowerGrip_q001_1" ) );
			case gamedataQuality.CommonPlus:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_CommonPlus" ) : ( T"Items.AdvancedPowerGrip_Tutorial_CommonPlus" ) );
			case gamedataQuality.Uncommon:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_Uncommon" ) : ( T"Items.AdvancedPowerGrip_Tutorial_Uncommon" ) );
			case gamedataQuality.UncommonPlus:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_UncommonPlus" ) : ( T"Items.AdvancedPowerGrip_Tutorial_UncommonPlus" ) );
			case gamedataQuality.Rare:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_Rare" ) : ( T"Items.AdvancedPowerGrip_Tutorial_Rare" ) );
			case gamedataQuality.RarePlus:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_RarePlus" ) : ( T"Items.AdvancedPowerGrip_Tutorial_RarePlus" ) );
			case gamedataQuality.Epic:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_Epic" ) : ( T"Items.AdvancedPowerGrip_Tutorial_Epic" ) );
			case gamedataQuality.EpicPlus:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_EpicPlus" ) : ( T"Items.AdvancedPowerGrip_Tutorial_EpicPlus" ) );
			case gamedataQuality.Legendary:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_Legendary" ) : ( T"Items.AdvancedPowerGrip_Tutorial_Legendary" ) );
			case gamedataQuality.LegendaryPlus:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_LegendaryPlus" ) : ( T"Items.AdvancedPowerGrip_Tutorial_LegendaryPlus" ) );
			case gamedataQuality.LegendaryPlusPlus:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_LegendaryPlusPlus" ) : ( T"Items.AdvancedPowerGrip_Tutorial_LegendaryPlusPlus" ) );
			default:
				return ( ( isSmartLink ) ? ( T"Items.AdvancedSmartLink_Tutorial_Common" ) : ( T"Items.AdvancedPowerGrip_q001_1" ) );
		}
	}

	private static function GetAppropriateArmorTutorialCyberware( itemQuality : gamedataQuality ) : TweakDBID
	{
		switch( itemQuality )
		{
			case gamedataQuality.Common:
				return T"Items.AdvancedBoringPlating_Q001";
			case gamedataQuality.CommonPlus:
				return T"Items.AdvancedBoringPlating_Tutorial_CommonPlus";
			case gamedataQuality.Uncommon:
				return T"Items.AdvancedBoringPlating_Tutorial_Uncommon";
			case gamedataQuality.UncommonPlus:
				return T"Items.AdvancedBoringPlating_Tutorial_UncommonPlus";
			case gamedataQuality.Rare:
				return T"Items.AdvancedBoringPlating_Tutorial_Rare";
			case gamedataQuality.RarePlus:
				return T"Items.AdvancedBoringPlating_Tutorial_RarePlus";
			case gamedataQuality.Epic:
				return T"Items.AdvancedBoringPlating_Tutorial_Epic";
			case gamedataQuality.EpicPlus:
				return T"Items.AdvancedBoringPlating_Tutorial_EpicPlus";
			case gamedataQuality.Legendary:
				return T"Items.AdvancedBoringPlating_Tutorial_Legendary";
			case gamedataQuality.LegendaryPlus:
				return T"Items.AdvancedBoringPlating_Tutorial_LegendaryPlus";
			case gamedataQuality.LegendaryPlusPlus:
				return T"Items.AdvancedBoringPlating_Tutorial_LegendaryPlusPlus";
			default:
				return T"Items.AdvancedBoringPlating_Q001";
		}
	}

	private static function GetTutorialItemCapacityRequirement( tweakDBID : TweakDBID, player : GameObject ) : Float
	{
		var itemModParams : ItemModParams;
		var itemData : gameItemData;
		itemModParams.quantity = 1;
		itemModParams.itemID = ItemID.FromTDBID( tweakDBID );
		itemData = Inventory.CreateItemData( itemModParams, player );
		return GetItemAttribute( itemData, gamedataStatType.HumanityAvailable, player );
	}

	public static function GetApproximateTutorialCapacity( player : GameObject, quality : gamedataQuality, hasSmartLink : Bool ) : Float
	{
		var tutorialCapacityPrediction : Float;
		tutorialCapacityPrediction = GetTutorialItemCapacityRequirement( GetAppropriateEyesTutorialCyberware( quality ), player );
		tutorialCapacityPrediction += GetTutorialItemCapacityRequirement( GetAppropriateHandsTutorialCyberware( quality, hasSmartLink ), player );
		tutorialCapacityPrediction += GetTutorialItemCapacityRequirement( GetAppropriateArmorTutorialCyberware( quality ), player );
		return tutorialCapacityPrediction;
	}

	private function UnequipAllFromGrid( equipmentArea : gamedataEquipmentArea, optional requiredCapacity : Float ) : Float
	{
		var equipmentSystem : EquipmentSystem;
		var i : Int32;
		var slotCount : Int32;
		var unequipRequest : UnequipRequest;
		var releasedCapacity : Float;
		var itemID : ItemID;
		releasedCapacity = 0.0;
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( m_player.GetGame() ).Get( 'EquipmentSystem' ) ) );
		slotCount = m_InventoryManager.GetNumberOfSlots( equipmentArea );
		for( i = 0; i < slotCount; i += 1 )
		{
			itemID = equipmentSystem.GetItemInEquipSlot( m_player, equipmentArea, i );
			if( ItemID.IsValid( itemID ) )
			{
				if( ItemID.GetTDBID( itemID ) == T"Items.MaskCW" )
				{
					continue;
				}
				releasedCapacity += GetTutorialItemCapacityRequirement( ItemID.GetTDBID( itemID ), m_player );
				unequipRequest = new UnequipRequest;
				unequipRequest.owner = m_player;
				unequipRequest.areaType = equipmentArea;
				unequipRequest.slotIndex = i;
				equipmentSystem.QueueRequest( unequipRequest );
				if( ( requiredCapacity > 0.0 ) && ( releasedCapacity >= requiredCapacity ) )
				{
					return releasedCapacity;
				}
			}
		}
		return releasedCapacity;
	}

	private function FreeUpTheCapacityForTutorial( capacity : Float ) : Float
	{
		var i : Int32;
		var remainingCapacity : Float;
		remainingCapacity = capacity;
		for( i = 0; i < m_categories.Size(); i += 1 )
		{
			remainingCapacity -= UnequipAllFromGrid( m_categories[ i ].equipArea, remainingCapacity );
			if( remainingCapacity <= 0.0 )
			{
				return remainingCapacity;
			}
		}
		return remainingCapacity;
	}

	private function InitFacePaperdoll()
	{
		if( ShouldMaskPaperdollBeVisible() )
		{
			inkVideoRef.SetVideoPath( m_femaleEyeAndMaskBinkAnimation, c_femaleMask );
			inkVideoRef.SetVideoPath( m_maleEyeAndMaskBinkAnimation, c_maleMask );
			m_animationController.SetMask( true );
		}
	}

	private function ShouldMaskPaperdollBeVisible() : Bool
	{
		var items : array< ItemID >;
		var i : Int32;
		items = EquipmentSystem.GetItemsInArea( GetPlayerControlledObject(), gamedataEquipmentArea.EyesCW );
		for( i = 0; i < items.Size(); i += 1 )
		{
			if( ItemID.GetTDBID( items[ i ] ) == T"Items.MaskCW" )
			{
				return true;
			}
		}
		return false;
	}

	public function InventoryModeWarnning( show : Bool )
	{
		var animOptions : inkAnimOptions;
		if( show )
		{
			if( m_anim.IsPlaying() )
			{
				m_anim.GotoStartAndStop();
			}
			inkWidgetRef.SetVisible( m_inventoryWarnning, true );
			m_anim = PlayLibraryAnimation( 'warnning' );
			m_pulse = new PulseAnimation;
			m_pulse.Configure( inkWidgetRef.Get( m_inventoryWarnning ), 1.0, 0.69999999, 0.69999999 );
			m_pulse.Start( false );
		}
		else
		{
			animOptions.playReversed = true;
			animOptions.customTimeDilation = 1.20000005;
			animOptions.applyCustomTimeDilation = true;
			m_anim = PlayLibraryAnimation( 'warnning', animOptions );
			m_anim.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnWarnningHidden' );
		}
	}

	protected event OnWarnningHidden( anim : inkAnimProxy )
	{
		m_pulse.Stop();
		inkWidgetRef.SetVisible( m_inventoryWarnning, false );
	}

	private function GetAreaHeader( area : gamedataEquipmentArea ) : String
	{
		var record : EquipmentArea_Record;
		var label : String;
		record = TweakDBInterface.GetEquipmentAreaRecord( TDBID.Create( "EquipmentArea." + EnumValueToString( "gamedataEquipmentArea", ( ( Int32 )( area ) ) ) ) );
		label = record.LocalizedName();
		if( label == "" )
		{
			label = EnumValueToString( "gamedataEquipmentArea", ( ( Int32 )( area ) ) );
		}
		return label;
	}

	private function StartCWUpgrade()
	{
		var equippedItemData : weak< UIInventoryItem >;
		equippedItemData = m_hoveredItem;
		m_isInEquipPopup = true;
		m_upgradeData = RPGManager.GetCyberwareUpgradeData( m_player, equippedItemData, m_upgradeItem, m_upgradeCostData, m_inventorySystem.GetCyberwareUpgradeSeed(), m_uiInventorySystem, RPGManager.ShouldShowCWChoice( m_player, equippedItemData ) );
		if( m_upgradeData.option1InventoryItem )
		{
			m_upgradeData.option1InventoryItem.GetRequirementsManager( m_player ).SetIsEquippable( CheckIfCanEquip( m_upgradeData.option1InventoryItem.GetItemData(), m_upgradeData.option1InventoryItem.GetEquipmentArea() ) );
		}
		if( m_upgradeData.option2InventoryItem )
		{
			m_upgradeData.option2InventoryItem.GetRequirementsManager( m_player ).SetIsEquippable( CheckIfCanEquip( m_upgradeData.option2InventoryItem.GetItemData(), m_upgradeData.option2InventoryItem.GetEquipmentArea() ) );
		}
		if( m_upgradeData.option3InventoryItem )
		{
			m_upgradeData.option3InventoryItem.GetRequirementsManager( m_player ).SetIsEquippable( CheckIfCanEquip( m_upgradeData.option3InventoryItem.GetItemData(), m_upgradeData.option3InventoryItem.GetEquipmentArea() ) );
		}
		m_upgradeData.cyberwareUpgradeData = GetCyberwareUpgradeData( equippedItemData, true );
		m_upgradeData.displayContext = m_playerItemDisplayContext.Copy();
		m_upgradeData.displayContext.AddTag( 'CyberwareUpgrade' );
		m_tokenPopup = ShowGameNotification( m_upgradeData );
		m_tokenPopup.RegisterListener( this, 'OnBuyShardPopupClosed' );
		m_buttonHintsController.Hide();
	}

	private function HighlightUpgradeResources( item : weak< UIInventoryItem >, isVendorItem : Bool )
	{
		var canAffordUpgrade : Bool;
		canAffordUpgrade = RPGManager.CanUpgradeCyberware( m_player, item.GetID(), item.IsEquipped(), gamedataQuality.Invalid, m_upgradeQuality, m_upgradeItem, m_upgradeCostData, true );
		if( ( m_screen == CyberwareScreenType.Ripperdoc && !( isVendorItem ) ) && ( item != NULL ) )
		{
			m_hoveredItem = item;
			if( canAffordUpgrade )
			{
				m_isHoveringOverUpgradableSlot = true;
				m_buttonHintsController.AddButtonHint( m_upgradeCWInputName, GetLocalizedText( "LocKey#42735" ) );
			}
		}
		else
		{
			m_isHoveringOverUpgradableSlot = false;
		}
	}

	private function UnhighlightUpgradeResources()
	{
		m_isHoveringOverUpgradableSlot = false;
	}

	private function PopulateCraftingMaterials()
	{
		var i, limit : Int32;
		var materialsTweaks : array< TweakDBID >;
		materialsTweaks = GetCommonCraftingMaterials();
		for( i = 0, limit = materialsTweaks.Size(); i < limit; i += 1 )
		{
			m_commonCraftingMaterials.PushBack( CachedCraftingMaterial.Make( materialsTweaks[ i ] ) );
		}
		for( i = 0, limit = m_commonCraftingMaterials.Size(); i < limit; i += 1 )
		{
			m_commonCraftingMaterials[ i ].UpdateQuantity( m_player );
			CreateCraftingMaterialItem( m_commonCraftingMaterials[ i ], m_upgradeResourcesAnchor );
		}
	}

	public static function GetCommonCraftingMaterials() : array< TweakDBID >
	{
		var result : array< TweakDBID >;
		result.PushBack( T"Items.CommonMaterial1" );
		result.PushBack( T"Items.UncommonMaterial1" );
		result.PushBack( T"Items.RareMaterial1" );
		result.PushBack( T"Items.EpicMaterial1" );
		result.PushBack( T"Items.LegendaryMaterial1" );
		return result;
	}

	private function CreateCraftingMaterialItem( craftingMaterial : CachedCraftingMaterial, gridList : inkCompoundRef )
	{
		var callbackData : BackpackCraftingMaterialItemCallbackData;
		callbackData = new BackpackCraftingMaterialItemCallbackData;
		callbackData.craftingMaterial = craftingMaterial;
		AsyncSpawnFromLocal( inkCompoundRef.Get( gridList ), 'craftingMaterialItem', this, 'OnCraftingMaterialItemSpawned', callbackData );
	}

	protected event OnCraftingMaterialItemSpawned( widget : inkWidget, callbackData : BackpackCraftingMaterialItemCallbackData )
	{
		var controller : CrafringMaterialItemController;
		controller = ( ( CrafringMaterialItemController )( widget.GetController() ) );
		m_craftingMaterialsListItems.PushBack( controller );
		controller.Setup( callbackData.craftingMaterial );
		( ( controller.GetQuantity() > 1 ) ? ( controller.GetRootWidget().SetOpacity( 1.0 ) ) : ( controller.GetRootWidget().SetOpacity( 0.0 ) ) );
		controller.RegisterToCallback( 'OnHoverOver', this, 'OnCraftingMaterialHoverOver' );
		controller.RegisterToCallback( 'OnHoverOut', this, 'OnCraftingMaterialHoverOut' );
	}

	protected event OnCraftingMaterialHoverOver( evt : inkPointerEvent )
	{
		var widget : weak< inkWidget >;
		var tooltipData : MessageTooltipData;
		var controller : CrafringMaterialItemController;
		var position : Vector2;
		widget = evt.GetCurrentTarget();
		controller = ( ( CrafringMaterialItemController )( widget.GetController() ) );
		position = WidgetUtils.LocalToGlobal( widget );
		position.X -= 16.0;
		position.Y += 50.0;
		tooltipData = new MessageTooltipData;
		tooltipData.Title = IntToString( controller.GetQuantity() );
		tooltipData.Description = controller.GetMateialDisplayName();
		m_TooltipsManager.ShowTooltipAtWidget( 0, controller.GetTooltipAnchorWidget(), tooltipData, gameuiETooltipPlacement.RightTop );
	}

	protected event OnCraftingMaterialHoverOut( evt : inkPointerEvent )
	{
		m_TooltipsManager.HideTooltips();
	}

	private function UpdateCraftingMaterial( materialTweakDBID : TweakDBID, optional skipAnim : Bool )
	{
		var i, limit : Int32;
		for( i = 0, limit = m_commonCraftingMaterials.Size(); i < limit; i += 1 )
		{
			if( m_commonCraftingMaterials[ i ].m_itemID == materialTweakDBID )
			{
				if( m_upgradeCostData.materialCount == m_commonCraftingMaterials[ i ].m_quantity )
				{
					m_craftingMaterialsListItems[ i ].SetQuantity( 0 );
					m_craftingMaterialsListItems[ i ].PlayAnimation( true );
				}
				else
				{
					m_commonCraftingMaterials[ i ].UpdateQuantity( m_player );
					m_craftingMaterialsListItems[ i ].SetQuantity( m_commonCraftingMaterials[ i ].m_quantity );
					m_craftingMaterialsListItems[ i ].PlayAnimation();
				}
				break;
			}
		}
		if( m_craftingMaterialsListItems.Size() >= 5 )
		{
			CheckCraftingMaterialContainerOverflow();
		}
	}

	private function CheckCraftingMaterialContainerOverflow()
	{
		var currentWidth : Float;
		currentWidth = inkCompoundRef.Get( m_upgradeResourcesAnchor ).GetDesiredWidth();
		if( currentWidth >= 860.0 )
		{
			m_craftingMaterialsListItems[ 0 ].Collapse( true );
			m_craftingMaterialsListItems[ 1 ].Collapse( true );
			m_craftingMaterialsListItems[ 2 ].Collapse( true );
			m_craftingMaterialsListItems[ 3 ].Collapse( true );
		}
	}

	protected event OnReleaseInput( e : inkPointerEvent )
	{
		if( ( ( m_isHoveringOverUpgradableSlot && e.IsAction( m_upgradeCWInputName ) ) && !( e.IsHandled() ) ) && !( e.IsConsumed() ) )
		{
			e.Handle();
			e.Consume();
			if( m_upgradeHoldFinished )
			{
				m_upgradeHoldFinished = false;
			}
			else
			{
				StartCWUpgrade();
			}
		}
		if( ( ( m_ripperdocHoverState != RipperdocHoverState.None && e.IsAction( 'upgrade_perk' ) ) && !( e.IsHandled() ) ) && !( e.IsConsumed() ) )
		{
			e.Consume();
			if( !( m_isTutorial ) )
			{
				OpenPerkTree();
			}
			else
			{
				ShowActionBlockedRightNowNotification();
			}
		}
	}

	private function OpenPerkTree()
	{
		var userData : PerkUserData;
		var evt : OpenMenuRequest;
		userData = new PerkUserData;
		userData.statType = gamedataStatType.TechnicalAbility;
		userData.cyberwareScreenType = m_screen;
		userData.perkType = GetRequiredPerk( m_ripperdocHoverState );
		if( m_screen == CyberwareScreenType.Inventory )
		{
			evt = new OpenMenuRequest;
			evt.m_menuName = 'new_perks';
			evt.m_isMainMenu = true;
			evt.m_jumpBack = true;
			evt.m_eventData.userData = userData;
			evt.m_eventData.m_overrideDefaultUserData = true;
			QueueBroadcastEvent( evt );
		}
		else
		{
			m_menuEventDispatcher.SpawnEvent( 'OnSwitchToCharacter', userData );
		}
	}

	private function ShowActionBlockedRightNowNotification()
	{
		var notificationEvent : UIInGameNotificationEvent;
		notificationEvent = new UIInGameNotificationEvent;
		m_uiSystem.QueueEvent( new UIInGameNotificationRemoveEvent );
		notificationEvent.m_notificationType = UIInGameNotificationType.CombatRestriction;
		m_uiSystem.QueueEvent( notificationEvent );
		m_audioSystem.Play( 'ui_menu_attributes_fail' );
	}

	private function IsEquipmentAreaRequiringPerk( equipmentArea : gamedataEquipmentArea ) : Bool
	{
		return equipmentArea == gamedataEquipmentArea.HandsCW || equipmentArea == gamedataEquipmentArea.MusculoskeletalSystemCW;
	}

	private function GetRequiredPerk( hoverArea : RipperdocHoverState ) : gamedataNewPerkType
	{
		switch( hoverArea )
		{
			case RipperdocHoverState.BarCapacity:
				return m_perkBarCapacity;
			break;
			case RipperdocHoverState.BarArmor:
				return m_perkBarArmor;
			break;
			case RipperdocHoverState.SlotSkeleton:
				return m_perkSlotSkeleton;
			break;
			case RipperdocHoverState.SlotHands:
				return m_perkSlotHands;
			break;
		}
		return gamedataNewPerkType.Tech_Central_Milestone_3;
	}

	private function SpawnPerks()
	{
		var level : Int32;
		var perkEvent : EdgrunnerPerkEvent;
		level = PlayerDevelopmentSystem.GetInstance( m_player ).IsNewPerkBought( m_player, gamedataNewPerkType.Tech_Master_Perk_3 );
		m_capacityPerk1Bought = level > 0;
		perkEvent = new EdgrunnerPerkEvent;
		perkEvent.isPurchased = level > 0;
		QueueEvent( perkEvent );
		level = PlayerDevelopmentSystem.GetInstance( m_player ).IsNewPerkBought( m_player, gamedataNewPerkType.Tech_Central_Perk_2_2 );
		m_capacityPerk2Bought = level > 0;
		level = PlayerDevelopmentSystem.GetInstance( m_player ).IsNewPerkBought( m_player, gamedataNewPerkType.Tech_Central_Milestone_3 );
		m_armorPerk1Bought = level > 1;
	}

	private function InitializeEquipmentMinigrids()
	{
		var i : Int32;
		var miniGrid : CyberwareInventoryMiniGrid;
		var equipmentMinigridsSize : Int32;
		var options : inkAnimOptions;
		equipmentMinigridsSize = m_equipmentMinigrids.Size();
		for( i = 0; i < equipmentMinigridsSize; i += 1 )
		{
			miniGrid = m_equipmentMinigrids[ i ];
			miniGrid.PlayIntroAnimation( m_minigridIntroAnimationSpeed, m_minigridIntroAnimInterpolationMode, m_minigridIntroAnimInterpolationType );
			if( i == ( equipmentMinigridsSize - 1 ) )
			{
				options.executionDelay = 0.1;
				m_capacityBarintroAnimProxy = PlayLibraryAnimation( 'meter_intro_PRE_LEFT', options );
				m_capacityBarintroAnimProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnIntroAnimationFinished_CAPACTIY_METER' );
				m_armorBarintroAnimProxy = PlayLibraryAnimation( 'meter_intro_PRE_RIGHT', options );
				m_armorBarintroAnimProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnIntroAnimationFinished_ARMOR_METER' );
			}
		}
	}

	protected event OnSetScreenDisplayContext( userData : IScriptable )
	{
		var displayContext : ScreenDisplayContextData;
		displayContext = ( ( ScreenDisplayContextData )( userData ) );
		if( displayContext )
		{
			m_screenDisplayContext = displayContext.Context;
		}
	}

	protected event OnBeforeLeaveScenario( userData : IScriptable )
	{
		if( m_screenDisplayContext == ScreenDisplayContext.Vendor )
		{
			MenuUIUtils.RequestAutoSave( m_player, 1.0 );
		}
	}

	protected event OnCloseMenu( userData : IScriptable )
	{
		if( m_inventoryView )
		{
			m_inventoryView.ReleaseVirtualGrid();
		}
	}

	protected event OnUninitialize()
	{
		if( m_uiInventorySystem )
		{
			m_uiInventorySystem.FlushFullscreenCache();
		}
		if( m_inventoryView )
		{
			m_inventoryView.ReleaseVirtualGrid();
		}
		UnregisterInventoryListener( GetPlayerControlledObject() );
		m_InventoryManager.UnInitialize();
		UnregisterBlackboard();
		m_menuEventDispatcher.UnregisterFromEvent( 'OnBack', this, 'OnBack' );
		m_menuEventDispatcher.UnregisterFromEvent( 'OnCloseMenu', this, 'OnCloseMenu' );
		m_menuEventDispatcher.UnregisterFromEvent( 'OnBeforeLeaveScenario', this, 'OnBeforeLeaveScenario' );
		m_menuEventDispatcher.UnregisterFromEvent( 'OnSetScreenDisplayContext', this, 'OnSetScreenDisplayContext' );
		GameInstance.GetTelemetrySystem( GetPlayerControlledObject().GetGame() ).LogVendorMenuState( m_VendorDataManager.GetVendorID(), false );
		m_equipmentMinigrids.Clear();
		UnregisterFromGlobalInputCallback( 'OnPostOnRelease', this, 'OnReleaseInput' );
		super.OnUninitialize();
	}

	private function Init()
	{
		m_TooltipsManager = ( ( gameuiTooltipsManager )( inkWidgetRef.GetControllerByType( m_TooltipsManagerRef, 'gameuiTooltipsManager' ) ) );
		m_TooltipsManager.Setup( ETooltipsStyle.Menus );
		m_buttonHintsController = ( ( ButtonHints )( SpawnFromExternal( inkWidgetRef.Get( m_buttonHintsManagerRef ), R"base\gameplay\gui\common\buttonhints.inkwidget", 'Root' ).GetController() ) );
		m_InventoryManager = new InventoryDataManagerV2;
		m_InventoryManager.Initialize( m_player );
		m_playerItemDisplayContext = ItemDisplayContextData.Make( m_player, ItemDisplayContext.VendorPlayer );
		m_vendorItemDisplayContext = ItemDisplayContextData.Make( m_player, ItemDisplayContext.Vendor );
		m_playerItemDisplayContext.AddTag( 'AllowProgramLink' );
		m_vendorItemDisplayContext.AddTag( 'AllowProgramLink' );
		if( m_screen == CyberwareScreenType.Ripperdoc )
		{
			m_playerItemDisplayContext.AddTag( 'Ripperdoc' );
			m_vendorItemDisplayContext.AddTag( 'Ripperdoc' );
		}
		m_isMusculoskeletalUpgrade3Unlocked = PlayerDevelopmentSystem.GetInstance( m_player ).IsNewPerkBought( m_player, gamedataNewPerkType.Tech_Central_Milestone_3 ) >= 3;
		m_uiScriptableSystem = UIScriptableSystem.GetInstance( m_player.GetGame() );
		m_uiInventorySystem = UIInventoryScriptableSystem.GetInstance( m_player.GetGame() );
		m_inventoryView.Configure( m_uiScriptableSystem );
		RegisterBlackboard( GetPlayerControlledObject() );
		m_animationController = ( ( RipperdocScreenAnimationController )( inkWidgetRef.GetControllerByType( m_animationControllerContainer, 'RipperdocScreenAnimationController' ) ) );
		m_animationController.SetGender( m_player.GetResolvedGenderName() == 'Female' );
		m_ripperdocTokenManager = new RipperdocTokenManager;
		m_ripperdocTokenManager.Initialize( m_player );
		PlayLibraryAnimation( 'Paperdoll_default_tab_intro' );
	}

	protected function RegisterBlackboard( player : GameObject )
	{
		m_equipmentBlackboard = GameInstance.GetBlackboardSystem( player.GetGame() ).Get( GetAllBlackboardDefs().UI_Equipment );
		if( m_equipmentBlackboard )
		{
			m_equipmentBlackboardCallback = m_equipmentBlackboard.RegisterDelayedListenerVariant( GetAllBlackboardDefs().UI_Equipment.itemEquipped, this, 'OnItemEquipped' );
		}
		m_tokenBlackboard = GameInstance.GetBlackboardSystem( player.GetGame() ).Get( GetAllBlackboardDefs().TokenUpgradedCyberwareBlackboard );
		if( m_tokenBlackboard )
		{
			m_tokenBlackboardCallback = m_tokenBlackboard.RegisterDelayedListenerVariant( GetAllBlackboardDefs().TokenUpgradedCyberwareBlackboard.CyberwareTypes, this, 'OnItemUpgrade' );
		}
		m_VendorBlackboard = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_Vendor );
	}

	protected function UnregisterBlackboard()
	{
		if( m_equipmentBlackboard )
		{
			m_equipmentBlackboard.UnregisterListenerVariant( GetAllBlackboardDefs().UI_Equipment.itemEquipped, m_equipmentBlackboardCallback );
		}
		m_VendorBlackboard = NULL;
	}

	private function RegisterInventoryListener( player : GameObject )
	{
		var itemBoughtCallback : RipperDocItemBoughtCallback;
		itemBoughtCallback = new RipperDocItemBoughtCallback;
		itemBoughtCallback.itemID = ItemID.None();
		itemBoughtCallback.Bind( this );
		m_inventoryListener = GameInstance.GetTransactionSystem( player.GetGame() ).RegisterInventoryListener( player, itemBoughtCallback );
	}

	private function UnregisterInventoryListener( player : GameObject )
	{
		if( m_inventoryListener )
		{
			GameInstance.GetTransactionSystem( player.GetGame() ).UnregisterInventoryListener( player, m_inventoryListener );
			m_inventoryListener = NULL;
		}
	}

	private function PopulateCategories()
	{
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.FrontalCortexCW, inkEHorizontalAlign.Right, m_frontalCortexAnchor ) );
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.SystemReplacementCW, inkEHorizontalAlign.Left, m_systemAnchor ) );
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.ArmsCW, inkEHorizontalAlign.Right, m_armsAnchor ) );
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.EyesCW, inkEHorizontalAlign.Left, m_ocularCortexAnchor ) );
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.MusculoskeletalSystemCW, inkEHorizontalAlign.Right, m_skeletonAnchor ) );
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.HandsCW, inkEHorizontalAlign.Left, m_handsAnchor ) );
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.NervousSystemCW, inkEHorizontalAlign.Right, m_nervousAnchor ) );
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.CardiovascularSystemCW, inkEHorizontalAlign.Left, m_cardiovascularAnchor ) );
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.IntegumentarySystemCW, inkEHorizontalAlign.Right, m_integumentaryAnchor ) );
		m_categories.PushBack( RipperdocCategory( gamedataEquipmentArea.LegsCW, inkEHorizontalAlign.Left, m_legsAnchor ) );
	}

	private function PreparePlayerItems()
	{
		var i, limit, targetIndex : Int32;
		var playerItems : inkHashMap;
		var values : array< weak< IScriptable > >;
		var uiInventoryItem : weak< UIInventoryItem >;
		var equipmentArea : gamedataEquipmentArea;
		playerItems = m_uiInventorySystem.GetPlayerItemsMap();
		playerItems.GetValues( values );
		m_uiInventorySystem.GetInventoryItemsManager().FlushEquippedItems();
		for( i = 0, limit = m_cachedPlayerItems.Size(); i < limit; i += 1 )
		{
			m_cachedPlayerItems[ i ].Clear();
		}
		for( i = 0, limit = values.Size(); i < limit; i += 1 )
		{
			uiInventoryItem = ( ( UIInventoryItem )( values[ i ] ) );
			equipmentArea = uiInventoryItem.GetEquipmentArea();
			if( equipmentArea == gamedataEquipmentArea.Invalid )
			{
				continue;
			}
			targetIndex = EquipmentAreaToIndex( equipmentArea );
			if( targetIndex < 0 )
			{
				continue;
			}
			m_cachedPlayerItems[ targetIndex ].PushBack( uiInventoryItem );
		}
	}

	private function PrepareVendorItems()
	{
		var i, limit, targetIndex : Int32;
		var vendorObject : weak< GameObject >;
		var vendorItems : array< VendorGameItemData >;
		var uiInventoryItem : UIInventoryItem;
		var wrappedItem : WrappedUIInventoryItem;
		var equipmentArea : gamedataEquipmentArea;
		for( i = 0, limit = m_cachedVendorItems.Size(); i < limit; i += 1 )
		{
			m_cachedVendorItems[ i ].Clear();
		}
		vendorObject = m_VendorDataManager.GetVendorInstance();
		vendorItems = m_VendorDataManager.GetRipperDocItems();
		m_vendorItems = new inkHashMap;
		m_vendorWrappedItems = new inkHashMap;
		for( i = 0, limit = vendorItems.Size(); i < limit; i += 1 )
		{
			uiInventoryItem = UIInventoryItem.Make( vendorObject, vendorItems[ i ].gameItemData, m_uiInventorySystem.GetInventoryItemsManager() );
			wrappedItem = WrappedUIInventoryItem.Make( uiInventoryItem, VendorItemAdditionalData.Make( vendorItems[ i ].itemStack ) );
			m_vendorItems.Insert( uiInventoryItem.Hash, uiInventoryItem );
			m_vendorWrappedItems.Insert( uiInventoryItem.Hash, wrappedItem );
			equipmentArea = uiInventoryItem.GetEquipmentArea();
			if( equipmentArea == gamedataEquipmentArea.Invalid )
			{
				continue;
			}
			targetIndex = EquipmentAreaToIndex( equipmentArea );
			if( targetIndex < 0 )
			{
				continue;
			}
			m_cachedVendorItems[ targetIndex ].PushBack( wrappedItem );
		}
	}

	private function UpdateSoldItems()
	{
		if( m_soldItemsFetched )
		{
			return;
		}
		m_soldItemsCache = m_VendorDataManager.GetVendorSoldItems();
	}

	private function SpawnMinigrids()
	{
		var i : Int32;
		m_armorCWEquipedNum = 0;
		for( i = 0; i < m_categories.Size(); i = i + 1 )
		{
			SpawnMinigrid( m_categories[ i ] );
		}
	}

	private function SpawnMinigrid( category : RipperdocCategory )
	{
		var gridUserData : GridUserData;
		var widgetName : CName;
		inkCompoundRef.RemoveAllChildren( category.parent );
		gridUserData = new GridUserData;
		gridUserData.equipArea = category.equipArea;
		gridUserData.align = category.align;
		gridUserData.parent = category.parent;
		widgetName = ( ( category.align == inkEHorizontalAlign.Right ) ? ( 'CWInventoryMiniGridLeft' ) : ( 'CWInventoryMiniGridRight' ) );
		AsyncSpawnFromLocal( inkCompoundRef.Get( category.parent ), widgetName, this, 'OnMinigridSpawned', gridUserData );
	}

	protected event OnMinigridSpawned( widget : inkWidget, userData : IScriptable )
	{
		var i, limit, count : Int32;
		var minigridController : CyberwareInventoryMiniGrid;
		var gridUserData : GridUserData;
		var containsNewCyberware : Bool;
		var items : array< weak< UIInventoryItem > >;
		gridUserData = ( ( GridUserData )( userData ) );
		widget.SetHAlign( gridUserData.align );
		minigridController = ( ( CyberwareInventoryMiniGrid )( widget.GetController() ) );
		items = m_uiInventorySystem.GetPlayerAreaItems( gridUserData.equipArea );
		for( i = 0, limit = items.Size(); i < limit; i += 1 )
		{
			items[ i ].GetRequirementsManager( m_player ).SetIsEquippable( CheckIfCanEquip( items[ i ].GetItemData(), items[ i ].GetItemData() ) );
			if( items[ i ].GetPrimaryStat().Value > 0.0 )
			{
				m_armorCWEquipedNum += 1;
			}
		}
		minigridController.SetTargetMargin( m_minigridTargetAnchorMargin, gridUserData.parent );
		UpdateAllItemCounters( gridUserData.equipArea );
		minigridController.SetupData( gridUserData.equipArea, items, this, ( ( m_filterMode == RipperdocModes.Item ) ? ( 'OnEquipmentSlotClick' ) : ( 'OnPreviewCyberwareClick' ) ), m_screen, items[ i ].GetModsManager().GetAllSlotsSize() > 0, m_playerItemDisplayContext, m_InventoryManager, m_player );
		containsNewCyberware = DoesEquipAreaContainNewItems( gridUserData.equipArea );
		minigridController.RefreshisNewPreview( containsNewCyberware );
		m_equipmentMinigrids.PushBack( minigridController );
		count = m_equipmentMinigrids.Size();
		if( count <= 10 )
		{
			m_minigridsMap[ count - 1 ] = gridUserData.equipArea;
		}
		if( count == 10 )
		{
			InitializeEquipmentMinigrids();
			EnableFocusTutorialModeHandsAndEye();
		}
	}

	private function DoesEquipAreaContainNewItems( area : gamedataEquipmentArea ) : Bool
	{
		var i, limit : Int32;
		var newItems : array< TweakDBID >;
		var vendorItems : array< weak< IScriptable > >;
		var inventoryItem : weak< UIInventoryItem >;
		newItems = MarketSystem.GetNewItems( m_VendorDataManager.GetVendorInstance() );
		m_vendorItems.GetValues( vendorItems );
		for( i = 0, limit = vendorItems.Size(); i < limit; i += 1 )
		{
			inventoryItem = ( ( weak< weak< UIInventoryItem > > )( vendorItems[ i ] ) );
			if( newItems.Contains( inventoryItem.GetTweakDBID() ) )
			{
				if( inventoryItem.GetEquipmentArea() == area )
				{
					return true;
				}
			}
		}
		return false;
	}

	private function EquipCyberware( itemData : weak< gameItemData > ) : Bool
	{
		var equipRequest : EquipRequest;
		var notification : UIMenuNotificationEvent;
		var additionalInfo : VendorRequirementsNotMetNotificationData;
		var slotIndex : Int32;
		var equipped : weak< UIInventoryItem >;
		var grid : CyberwareInventoryMiniGrid;
		var equipmentArea : gamedataEquipmentArea;
		var equipAnimationUpdateData : EquipAnimationUpdateData;
		if( !( CheckIfCanEquip( itemData, m_hoverArea ) ) )
		{
			notification = new UIMenuNotificationEvent;
			notification.m_notificationType = UIMenuNotificationType.VendorRequirementsNotMet;
			additionalInfo = new VendorRequirementsNotMetNotificationData;
			additionalInfo.m_data = RPGManager.GetFirstUnmetEquipRequirement( m_player, RPGManager.GetEquipRequirements( m_player, itemData ) );
			notification.m_additionalInfo = additionalInfo;
			m_uiSystem.QueueEvent( notification );
			m_hoverArea = gamedataEquipmentArea.Invalid;
			m_isPurchaseEquip = false;
			m_isInEquipPopup = false;
			m_audioSystem.Play( 'ui_menu_attributes_fail' );
			return false;
		}
		grid = GetMinigrid( m_hoverArea );
		if( grid == NULL )
		{
			m_isPurchaseEquip = false;
			m_isInEquipPopup = false;
			return false;
		}
		slotIndex = grid.GetSlotToEquipe( itemData.GetID() );
		m_equippedSlotIndex = slotIndex;
		equipped = grid.GetSlotData( slotIndex );
		if( equipped != NULL )
		{
			if( !( UnequipCyberware( equipped.GetItemData(), true ) ) )
			{
				m_isPurchaseEquip = false;
				m_isInEquipPopup = false;
				return false;
			}
		}
		equipRequest = new EquipRequest;
		equipRequest.owner = m_player;
		equipRequest.itemID = itemData.GetID();
		equipRequest.slotIndex = slotIndex;
		equipAnimationUpdateData = new EquipAnimationUpdateData;
		equipAnimationUpdateData.equipArea = m_hoverArea;
		equipAnimationUpdateData.isEquip = true;
		m_menuEventDispatcher.SpawnEvent( 'OnEquipAnimationDataUpdate', equipAnimationUpdateData );
		PlayCyberwareSound( RPGManager.GetItemType( itemData.GetID() ), true, RPGManager.GetItemQuality( itemData ) );
		PlayRumble( RumbleStrength.Heavy, RumbleType.Pulse, RumblePosition.Left );
		if( itemData.GetStatValueByType( gamedataStatType.Armor ) > 0.0 )
		{
			m_armorCWEquipedNum += 1;
			m_audioSystem.Play( 'ui_gui_cyberware_armor_guage_up_lp_01' );
			PlayRumble( RumbleStrength.Heavy, RumbleType.Pulse, RumblePosition.Right );
		}
		m_hoverArea = gamedataEquipmentArea.Invalid;
		m_hasEquipEventTriggered = false;
		GameInstance.GetScriptableSystemsContainer( m_player.GetGame() ).Get( 'EquipmentSystem' ).QueueRequest( equipRequest );
		if( m_isPurchased )
		{
			m_isPurchased = false;
			m_audioSystem.Play( 'ui_gui_cyberware_capacity_guage_up_lp_01' );
		}
		if( m_isTutorial )
		{
			equipmentArea = grid.GetEquipmentArea();
			switch( equipmentArea )
			{
				case gamedataEquipmentArea.EyesCW:
					m_questSystem.SetFact( 'tutorial_ripperdoc_eyes_passed', 1 );
				break;
				case gamedataEquipmentArea.HandsCW:
					m_questSystem.SetFact( 'tutorial_ripperdoc_hands_passed', 1 );
				break;
				case gamedataEquipmentArea.IntegumentarySystemCW:
					m_questSystem.SetFact( 'tutorial_ripperdoc_armor_passed', 1 );
				break;
			}
			if( ( ( ( m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' ) > 0 ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' ) > 0 ) ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' ) > 0 ) ) && m_isTutorial )
			{
				m_ep1StandaloneTutorial = false;
				m_mq048TutorialFact = false;
				m_questSystem.SetFact( 'ep1_ripperdoc_tutorial_seen', 1 );
				m_statusEffectSystem.RemoveStatusEffect( m_player.GetEntityID(), T"BaseStatusEffect.CyberwareTutorialAdjustments" );
				DisplayInventory( true );
			}
		}
		return true;
	}

	protected event OnUIEquipmentReplacedEvent( evt : UIEquipmentReplacedEvent )
	{
		UpdateArmorBar( false );
	}

	private function PlayCyberwareSound( itemType : gamedataItemType, OnEquip : Bool, itemQuality : gamedataQuality )
	{
		PlaySound( GetItemType( itemType ), ( ( OnEquip ) ? ( 'OnEquip' ) : ( 'OnUnequip' ) ) );
		if( itemQuality == gamedataQuality.Legendary || itemQuality == gamedataQuality.Iconic )
		{
			m_audioSystem.Play( 'ui_loot_rarity_legendary' );
		}
		else if( itemQuality == gamedataQuality.Epic )
		{
			m_audioSystem.Play( 'ui_loot_rarity_epic' );
		}
	}

	private static function GetItemType( itemType : gamedataItemType ) : CName
	{
		switch( itemType )
		{
			case gamedataItemType.Cyb_Ability:
				return 'Cyb_Ability';
			break;
			case gamedataItemType.Cyb_Launcher:
				return 'Cyb_Launcher';
			break;
			case gamedataItemType.Cyb_MantisBlades:
				return 'Cyb_MantisBlades';
			break;
			case gamedataItemType.Cyb_NanoWires:
				return 'Cyb_NanoWires';
			break;
			case gamedataItemType.Cyb_StrongArms:
				return 'Cyb_StrongArms';
			break;
		}
		return 'Cyb_Ability';
	}

	private function CheckIfCanEquip( itemData : weak< gameItemData >, equipped : weak< gameItemData > ) : Bool
	{
		var hoveredReqs, replacedReqs : array< SItemStackRequirementData >;
		var hoveredReq, replacedReq : SItemStackRequirementData;
		var playerLevel : Float;
		var found : Bool;
		var i, j : Int32;
		replacedReqs = RPGManager.GetEquipRequirements( m_player, equipped );
		hoveredReqs = RPGManager.GetEquipRequirements( m_player, itemData );
		for( i = 0; i < hoveredReqs.Size(); i += 1 )
		{
			found = false;
			hoveredReq = hoveredReqs[ i ];
			playerLevel = m_statsSystem.GetStatValue( m_playerID, hoveredReq.statType );
			for( j = 0; j < replacedReqs.Size(); j += 1 )
			{
				replacedReq = replacedReqs[ j ];
				if( hoveredReq.statType == replacedReq.statType )
				{
					if( ( hoveredReq.requiredValue - replacedReq.requiredValue ) > playerLevel )
					{
						return false;
					}
					found = true;
					break;
				}
			}
			if( !( found ) && ( hoveredReq.requiredValue > playerLevel ) )
			{
				return false;
			}
		}
		return true;
	}

	private function CheckIfCanEquip( itemData : weak< gameItemData >, itemArea : gamedataEquipmentArea ) : Bool
	{
		var equipped : weak< UIInventoryItem >;
		var found : Bool;
		var grid : weak< CyberwareInventoryMiniGrid >;
		grid = GetMinigrid( itemArea );
		if( grid != NULL )
		{
			equipped = grid.GetEquippedData( itemData.GetID() );
			if( equipped != NULL )
			{
				found = true;
			}
			else
			{
				equipped = grid.GetSelectedSlotData();
				if( equipped != NULL )
				{
					found = true;
				}
			}
		}
		if( !( found ) )
		{
			return EquipmentSystem.GetInstance( m_player ).GetPlayerData( m_player ).IsEquippable( itemData );
		}
		return CheckIfCanEquip( itemData, equipped.GetItemData() );
	}

	private function UnequipCyberware( itemData : weak< gameItemData >, optional skipRefresh : Bool ) : Bool
	{
		var unequipRequest : UnequipRequest;
		var notification : UIMenuNotificationEvent;
		var statValue : Float;
		var humanityAvailable : Float;
		var blocked : Bool;
		var equipAnimationUpdateData : EquipAnimationUpdateData;
		blocked = false;
		if( m_tokenPopup != NULL )
		{
			return false;
		}
		if( itemData.HasTag( 'CapacityBooster' ) )
		{
			statValue = GameInstance.GetStatsSystem( m_player.GetGame() ).GetStatValue( m_player.GetEntityID(), gamedataStatType.CapacityBoosterHumanity );
			humanityAvailable = GameInstance.GetStatsSystem( m_player.GetGame() ).GetStatValue( m_player.GetEntityID(), gamedataStatType.HumanityAvailable );
			if( humanityAvailable < statValue )
			{
				blocked = true;
			}
		}
		if( blocked )
		{
			notification = new UIMenuNotificationEvent;
			notification.m_notificationType = UIMenuNotificationType.InventoryActionBlocked;
			m_uiSystem.QueueEvent( notification );
			m_audioSystem.Play( 'ui_menu_attributes_fail' );
			return false;
		}
		unequipRequest = new UnequipRequest;
		unequipRequest.owner = m_player;
		unequipRequest.areaType = m_hoverArea;
		unequipRequest.slotIndex = GetMinigrid( m_hoverArea ).GetSlotToEquipe( itemData.GetID() );
		equipAnimationUpdateData = new EquipAnimationUpdateData;
		equipAnimationUpdateData.equipArea = m_hoverArea;
		equipAnimationUpdateData.isEquip = false;
		m_menuEventDispatcher.SpawnEvent( 'OnEquipAnimationDataUpdate', equipAnimationUpdateData );
		PlayCyberwareSound( RPGManager.GetItemType( itemData.GetID() ), false, RPGManager.GetItemQuality( itemData ) );
		PlayRumble( RumbleStrength.Light, RumbleType.Pulse, RumblePosition.Left );
		m_audioSystem.Play( 'ui_gui_cyberware_capacity_guage_down_lp_01' );
		if( itemData.GetStatValueByType( gamedataStatType.Armor ) > 0.0 )
		{
			m_armorCWEquipedNum -= 1;
			m_audioSystem.Play( 'ui_gui_cyberware_armor_guage_down_lp_01' );
			PlayRumble( RumbleStrength.Light, RumbleType.Pulse, RumblePosition.Right );
		}
		m_hoverArea = gamedataEquipmentArea.Invalid;
		if( !( skipRefresh ) )
		{
			m_hasUnequipEventTriggered = false;
		}
		GameInstance.GetScriptableSystemsContainer( m_player.GetGame() ).Get( 'EquipmentSystem' ).QueueRequest( unequipRequest );
		m_audioSystem.Play( 'ui_gui_cyberware_capacity_guage_down_lp_01' );
		m_TooltipsManager.HideTooltips();
		UnhighlightUpgradeResources();
		return true;
	}

	protected event OnItemEquipped( value : Variant )
	{
		var itemID : ItemID;
		itemID = ( ( ItemID )value );
		HandleItemEquipped( itemID );
	}

	private function HandleItemEquippedNextFrame( itemID : ItemID )
	{
		var evt : HandleItemEquippedNextFrameEvent;
		if( !( m_handleItemEquippedNextFrameRequested ) )
		{
			evt = new HandleItemEquippedNextFrameEvent;
			evt.itemID = itemID;
			QueueEvent( evt );
			m_handleItemEquippedNextFrameRequested = true;
		}
	}

	protected event OnHandleItemEquippedNextFrameEvent( evt : HandleItemEquippedNextFrameEvent )
	{
		m_handleItemEquippedNextFrameRequested = false;
		HandleItemEquipped( evt.itemID );
	}

	private function RequestHandleEquippedOnItemAdded( tweak : TweakDBID )
	{
		if( TDBID.IsValid( tweak ) )
		{
			m_handleItemEquippedOnItemAdded = tweak;
		}
	}

	private function HandleItemEquipped( itemID : ItemID )
	{
		var grid : CyberwareInventoryMiniGrid;
		var isEquipped : Bool;
		var sideUpgrade : Item_Record;
		isEquipped = ItemID.IsValid( itemID );
		if( ( !( m_hasEquipEventTriggered ) && isEquipped ) || ( !( m_hasUnequipEventTriggered ) && !( isEquipped ) ) )
		{
			if( ( m_filterArea == gamedataEquipmentArea.MusculoskeletalSystemCW && m_isMusculoskeletalUpgrade3Unlocked ) && isEquipped )
			{
				if( TDBID.IsValid( m_handleItemEquippedOnItemAdded ) )
				{
					if( m_uiInventorySystem.GetPlayerItem( itemID ) == NULL )
					{
						if( RPGManager.GetItemRecord( itemID ).GetID() == m_handleItemEquippedOnItemAdded )
						{
							return;
						}
					}
				}
				if( RPGManager.CyberwareHasSideUpgrade( itemID, sideUpgrade ) )
				{
					RequestHandleEquippedOnItemAdded( sideUpgrade.GetID() );
					return;
				}
			}
			m_isInEquipPopup = false;
			if( m_equippedSlotIndex >= 0 )
			{
				grid = GetMinigrid( m_filterArea );
				grid.PlayEquipAnimation( m_equippedSlotIndex );
				m_equippedSlotIndex = -1;
			}
			PreparePlayerItems();
			UpdateCapacityBar( m_isPurchaseEquip );
			UpdateArmorBar( m_isPurchaseEquip );
			UpdateMinigrids();
			RefreshInventoryNextFrame();
			HideInventoryTutorial();
			PlayLibraryAnimation( 'filter_change' );
			if( isEquipped && !( m_hasEquipEventTriggered ) )
			{
				m_hasEquipEventTriggered = true;
			}
			if( !( isEquipped ) && !( m_hasUnequipEventTriggered ) )
			{
				m_hasUnequipEventTriggered = true;
			}
			m_isPurchaseEquip = false;
		}
	}

	private function InvalidateMinigridsNextFrame()
	{
		if( !( m_invalidateMinigridsRequested ) )
		{
			QueueEvent( new RipperdocInvalidateMinigridsNextFrame );
			m_invalidateMinigridsRequested = true;
		}
	}

	protected event OnInvalidateMinigridsEvent( evt : RipperdocInvalidateMinigridsNextFrame )
	{
		UpdateMinigrids();
		m_invalidateMinigridsRequested = false;
	}

	protected event OnUIInventoryItemAdded( evt : UIInventoryItemAdded )
	{
		if( TDBID.IsValid( m_handleItemEquippedOnItemAdded ) )
		{
			if( RPGManager.GetItemRecord( evt.itemID ).GetID() == m_handleItemEquippedOnItemAdded )
			{
				HandleItemEquippedNextFrame( evt.itemID );
				m_handleItemEquippedOnItemAdded = TDBID.None();
			}
		}
		PreparePlayerItems();
		RefreshInventoryNextFrame();
		if( m_isUpgrading )
		{
			InvalidateMinigridsNextFrame();
		}
	}

	protected event OnUIInventoryItemRemoved( evt : UIInventoryItemRemoved )
	{
		PreparePlayerItems();
		RefreshInventoryNextFrame();
	}

	private function GetMaxCapacityPossible() : Float
	{
		var max : Float;
		max = m_statsDataSystem.GetMaxValueFromCurve( 'cyberware_curves', 'power_level_to_humanity' );
		max += TDB.GetFloat( T"NewPerks.Tech_Central_Perk_2_2.amountOfCapacityGivenForRipperdocUI" );
		max += TDB.GetFloat( T"NewPerks.Tech_Master_Perk_3.amountOfCapacityGivenForRipperdocUI" );
		max += TDB.GetFloat( T"Items.CWCapacityPermaRewardBase.amountOfCapacityGivenForRipperdocUI" );
		max += TDB.GetFloat( T"Proficiencies.TechnicalAbilitySkill.amountOfCapacityGivenForRipperdocUI" );
		max += TDB.GetFloat( T"Items.CapacityBoosterBase.amountOfCapacityGivenForRipperdocUI" );
		max += 40.0;
		return max;
	}

	private function UpdateCapacityBar( isPurchase : Bool )
	{
		m_capacityApplyEvent = new RipperdocMeterCapacityApplyEvent;
		m_capacityApplyEvent.IsPurchase = isPurchase;
		m_capacityApplyEvent.CurrentCapacity = ( ( Int32 )( m_statsSystem.GetStatValue( m_playerID, gamedataStatType.HumanityAllocated ) ) );
		m_capacityApplyEvent.OverchargeCapacity = ( ( Int32 )( m_statsSystem.GetStatValue( m_playerID, gamedataStatType.HumanityOverallocationPossible ) ) );
		m_capacityApplyEvent.MaxCapacity = ( ( Int32 )( m_statsSystem.GetStatValue( m_playerID, gamedataStatType.HumanityTotalMaxValue ) ) ) - m_capacityApplyEvent.OverchargeCapacity;
		m_capacityApplyEvent.MaxCapacityPossible = m_maxCapacityPossible;
		QueueEvent( m_capacityApplyEvent );
	}

	private function UpdateArmorBar( isPurchase : Bool )
	{
		m_armorApplyEvent = new RipperdocMeterArmorApplyEvent;
		m_armorApplyEvent.IsPurchase = isPurchase;
		m_armorApplyEvent.ArmorData = new RipperdocArmorData;
		m_armorApplyEvent.ArmorData.CurrentArmor = m_statsSystem.GetStatValue( m_playerID, gamedataStatType.Armor );
		m_armorApplyEvent.ArmorData.CurrentMaxArmor = m_statsDataSystem.GetValueFromCurve( 'cyberware_curves', m_statsSystem.GetStatValue( m_playerID, gamedataStatType.Level ), 'max_armor_for_level' );
		m_armorApplyEvent.ArmorData.MaxArmorPossible = m_statsDataSystem.GetMaxValueFromCurve( 'cyberware_curves', 'max_armor_for_level' );
		m_armorApplyEvent.ArmorData.MaxDamageReduction = 1.0 - ( 1.0 / ( 1.0 + ( ( m_armorApplyEvent.ArmorData.CurrentMaxArmor * m_statsSystem.GetStatValue( m_playerID, gamedataStatType.ArmorEffectivenessMultiplier ) ) * m_statsDataSystem.GetArmorEffectivenessValue( true ) ) ) );
		QueueEvent( m_armorApplyEvent );
	}

	protected event OnIntroAnimationFinished_CAPACTIY_METER( proxy : inkAnimProxy )
	{
		UpdateCapacityBar( false );
		m_capacityBarintroAnimProxy.UnregisterFromCallback( inkanimEventType.OnFinish, this, 'OnIntroAnimationFinished_CAPACTIY_METER' );
		PlayLibraryAnimation( 'upgrade_resourses_intro' );
	}

	protected event OnIntroAnimationFinished_ARMOR_METER( proxy : inkAnimProxy )
	{
		UpdateArmorBar( false );
		m_armorBarintroAnimProxy.UnregisterFromCallback( inkanimEventType.OnFinish, this, 'OnIntroAnimationFinished_ARMOR_METER' );
	}

	protected event OnArmorBarFinalizedEvent( e : ArmorBarFinalizedEvent )
	{
		var hoverWidget : weak< inkWidget >;
		m_isArmorBarReady = true;
		hoverWidget = m_uiSystem.GetInteractableWidgetUnderCursor();
		if( hoverWidget && hoverWidget.GetName() == 'armorHoverArea' )
		{
			QueueEvent( new RipperdocMeterArmorBarHoverEvent );
		}
	}

	protected event OnCapacityBarFinalizedEvent( e : CapacityBarFinalizedEvent )
	{
		var hoverWidget : weak< inkWidget >;
		m_isCapacityBarReady = true;
		hoverWidget = m_uiSystem.GetInteractableWidgetUnderCursor();
		if( hoverWidget && hoverWidget.GetName() == 'capacityHoverArea' )
		{
			QueueEvent( new RipperdocMeterCapacityBarHoverEvent );
		}
		if( m_screen != CyberwareScreenType.Inventory )
		{
			CheckCraftingMaterialContainerOverflow();
		}
	}

	protected event OnPreviewCyberwareClick( evt : inkPointerEvent )
	{
		var itemController : weak< InventoryItemDisplayController >;
		var oldArea : gamedataEquipmentArea;
		var openModsScreenEvent : CyberwareTabModsRequest;
		if( evt.IsAction( 'click' ) )
		{
			itemController = GetCyberwareSlotControllerFromTarget( evt );
			m_audioSystem.Play( 'ui_menu_onpress' );
			if( itemController.IsLocked() )
			{
				return false;
			}
			oldArea = m_filterArea;
			m_filterArea = itemController.GetEquipmentArea();
			m_filteringByArea = true;
			DollHover( m_filterArea );
			DollSelect( true );
			if( oldArea != m_filterArea )
			{
				m_audioSystem.Play( 'ui_gui_cyberware_paperdoll_zoom_in_01' );
				DisplayInventory( true );
				ShowInventoryTutorial();
				m_lastAreaVisited = m_filterArea;
			}
			AnimateMinigrids();
			SetMinigridSelection( itemController );
		}
		else if( evt.IsAction( 'install_quickhack' ) )
		{
			if( m_hoverArea == gamedataEquipmentArea.ArmsCW || m_hoverArea == gamedataEquipmentArea.SystemReplacementCW )
			{
				itemController = GetCyberwareSlotControllerFromTarget( evt );
				m_audioSystem.Play( 'ui_menu_onpress' );
				if( itemController.IsLocked() )
				{
					return false;
				}
				if( itemController.GetUIInventoryItem().GetModsManager().GetAttachmentsSize() > 0 )
				{
					openModsScreenEvent = new CyberwareTabModsRequest;
					openModsScreenEvent.open = true;
					openModsScreenEvent.wrapper = new CyberwareDisplayWrapper;
					openModsScreenEvent.wrapper.displayData = itemController.GetItemDisplayData();
					m_uiSystem.QueueEvent( openModsScreenEvent );
				}
			}
		}
	}

	protected event OnEquipmentSlotClick( evt : inkPointerEvent )
	{
		var itemController : weak< InventoryItemDisplayController >;
		itemController = GetCyberwareSlotControllerFromTarget( evt );
		if( evt.IsAction( 'select' ) )
		{
			m_audioSystem.Play( 'ui_menu_onpress' );
			m_filterArea = itemController.GetUIInventoryItem().GetEquipmentArea();
			m_filteringByArea = true;
			DollHover( m_filterArea );
			DollSelect( true );
			DisplayInventory( true );
		}
	}

	private function GetCyberwareSlotControllerFromTarget( evt : inkPointerEvent ) : InventoryItemDisplayController
	{
		var widget : inkWidget;
		var controller : weak< InventoryItemDisplayController >;
		widget = evt.GetCurrentTarget();
		controller = ( ( InventoryItemDisplayController )( widget.GetController() ) );
		return controller;
	}

	protected event OnSlotClick( evt : ItemDisplayClickEvent )
	{
		var item : weak< UIInventoryItem >;
		var uiMenuNotification : UIMenuNotificationEvent;
		var additionalInfo : VendorRequirementsNotMetNotificationData;
		var type : VendorConfirmationPopupType;
		var isEquipped, isRequirementMet : Bool;
		var requirementsManager : weak< UIInventoryItemRequirementsManager >;
		var vendorAdditionalData : VendorItemAdditionalData;
		var isSellable : Bool;
		item = evt.uiInventoryItem;
		isEquipped = item.IsEquipped();
		isSellable = item.IsSellable();
		if( evt.actionName.IsAction( 'click' ) && m_screen == CyberwareScreenType.Inventory )
		{
			InventoryModeWarnning( m_filterMode == RipperdocModes.Item );
			return false;
		}
		if( ( !( m_isActivePanel ) || ( item == NULL ) ) || m_hoverArea == gamedataEquipmentArea.Invalid )
		{
			return false;
		}
		if( !( m_isCapacityBarReady ) || !( m_isArmorBarReady ) )
		{
			return false;
		}
		if( evt.actionName.IsAction( 'click' ) )
		{
			m_audioSystem.Play( 'ui_menu_onpress' );
			if( evt.displayContextData.IsVendorItem() )
			{
				requirementsManager = item.GetRequirementsManager( m_player );
				isRequirementMet = true;
				vendorAdditionalData = ( ( VendorItemAdditionalData )( evt.additionalData ) );
				if( vendorAdditionalData )
				{
					isRequirementMet = vendorAdditionalData.IsAvailable;
				}
				if( !( isRequirementMet ) )
				{
					uiMenuNotification = new UIMenuNotificationEvent;
					uiMenuNotification.m_notificationType = UIMenuNotificationType.VendorRequirementsNotMet;
					additionalInfo = new VendorRequirementsNotMetNotificationData;
					additionalInfo.m_data = vendorAdditionalData.Requirement;
					uiMenuNotification.m_additionalInfo = additionalInfo;
					m_uiSystem.QueueEvent( uiMenuNotification );
				}
				else if( evt.isBuybackStack )
				{
					m_VendorDataManager.BuybackItemFromVendor( item.GetItemData(), 1 );
				}
				else if( m_VendorDataManager.GetBuyingPrice( item.GetID() ) > m_VendorDataManager.GetLocalPlayerCurrencyAmount() )
				{
					uiMenuNotification = new UIMenuNotificationEvent;
					uiMenuNotification.m_notificationType = UIMenuNotificationType.VNotEnoughMoney;
					m_uiSystem.QueueEvent( uiMenuNotification );
				}
				else if( item.GetItemData().HasTag( 'MustBeWearableToPurchase' ) && !( requirementsManager.IsEquippable() ) )
				{
					uiMenuNotification = new UIMenuNotificationEvent;
					uiMenuNotification.m_notificationType = UIMenuNotificationType.VendorRequirementsNotMet;
					additionalInfo = new VendorRequirementsNotMetNotificationData;
					additionalInfo.m_data = requirementsManager.GetFirstUnmetEquipRequirement();
					uiMenuNotification.m_additionalInfo = additionalInfo;
					m_uiSystem.QueueEvent( uiMenuNotification );
				}
				else
				{
					m_isInEquipPopup = true;
					type = ( ( EquipmentSystem.GetInstance( m_player ).GetPlayerData( m_player ).IsEquippable( item.GetItemData() ) ) ? ( VendorConfirmationPopupType.BuyAndEquipCyberware ) : ( VendorConfirmationPopupType.BuyNotEquipableCyberware ) );
					OpenConfirmationPopup( item, m_VendorDataManager.GetBuyingPrice( item.GetID() ), type, 'OnBuyConfirmationPopupClosed' );
				}
			}
			else if( !( isEquipped ) && m_hasEquipEventTriggered )
			{
				m_isInEquipPopup = true;
				EquipCyberware( item.GetItemData() );
			}
		}
		else if( ( ( isSellable && evt.actionName.IsAction( 'disassemble_item' ) ) && !( isEquipped ) ) && evt.displayContextData.GetDisplayContext() != ItemDisplayContext.Vendor )
		{
			OpenConfirmationPopup( item, m_VendorDataManager.GetSellingPrice( item.GetID() ), VendorConfirmationPopupType.SellCyberware, 'OnSellConfirmationPopupClosed' );
		}
		else if( evt.actionName.IsAction( 'unequip_item' ) && isEquipped )
		{
			if( item.GetEquipmentArea() == gamedataEquipmentArea.EyesCW )
			{
				uiMenuNotification = new UIMenuNotificationEvent;
				uiMenuNotification.m_notificationType = UIMenuNotificationType.FaceUnequipBlocked;
				m_uiSystem.QueueEvent( uiMenuNotification );
			}
			else if( m_isTutorial || m_vikTutorial )
			{
				uiMenuNotification = new UIMenuNotificationEvent;
				uiMenuNotification.m_notificationType = UIMenuNotificationType.TutorialUnequipBlocked;
				m_uiSystem.QueueEvent( uiMenuNotification );
			}
			else
			{
				m_isInEquipPopup = true;
				UnequipCyberware( item.GetItemData() );
			}
		}
	}

	protected event OnSlotHover( evt : ItemDisplayHoverOverEvent )
	{
		var item : weak< UIInventoryItem >;
		var isVendorItem : Bool;
		var isSlotLocked : Bool;
		item = evt.uiInventoryItem;
		isVendorItem = evt.displayContextData.GetDisplayContext() == ItemDisplayContext.Vendor;
		m_hoverArea = evt.display.GetEquipmentArea();
		if( m_filterMode == RipperdocModes.Default )
		{
			DollHover( m_hoverArea );
		}
		m_TooltipsManager.HideTooltips();
		if( m_screen != CyberwareScreenType.Inventory )
		{
			SetButtonHintsHover( item, isVendorItem );
		}
		if( ( item == NULL ) && !( evt.display.IsLocked() ) )
		{
			ShowCategoryTooltip( m_dollHoverArea );
			return false;
		}
		m_capacityHoverEvent = GetCapacityHoverEventData( item );
		m_capacityHoverEvent.IsHover = true;
		QueueEvent( m_capacityHoverEvent );
		m_armorHoverEvent = GetArmorHoverEventData( item );
		m_armorHoverEvent.IsHover = true;
		m_armorHoverEvent.isCyberwareEquipped = item.IsEquipped();
		QueueEvent( m_armorHoverEvent );
		if( item == NULL )
		{
			if( IsEquipmentAreaRequiringPerk( m_hoverArea ) )
			{
				isSlotLocked = ( m_uiInventorySystem.GetInventoryItemsManager().GetNumberOfSlots( m_hoverArea ) - evt.display.GetSlotIndex() ) > 0;
			}
		}
		if( isSlotLocked )
		{
			ShowCWPerkTooltip( evt.widget );
		}
		else
		{
			ShowCWTooltip( item, GetMinigrid( item.GetEquipmentArea() ).GetEquippedData( item.GetID() ), evt.widget, isVendorItem, evt.isBuybackStack );
		}
		PreviewMinigridSelection( item );
		HighlightUpgradeResources( item, isVendorItem );
		if( isVendorItem )
		{
			MarketSystem.ItemInspected( m_VendorDataManager.GetVendorInstance(), ItemID.GetTDBID( item.ID ) );
		}
		else
		{
			RequestItemInspected( item.GetID() );
		}
		m_hoveredItemDisplay = evt.display;
	}

	private function RequestItemInspected( itemID : ItemID )
	{
		var request : UIScriptableSystemInventoryInspectItem;
		request = new UIScriptableSystemInventoryInspectItem;
		request.itemID = itemID;
		m_uiScriptableSystem.QueueRequest( request );
	}

	protected event OnSlotUnhover( evt : ItemDisplayHoverOutEvent )
	{
		var i : Int32;
		m_TooltipsManager.HideTooltips();
		m_ripperdocHoverState = RipperdocHoverState.None;
		SetButtonHintsUnhover();
		if( !( m_isInEquipPopup ) )
		{
			if( m_filterMode == RipperdocModes.Default )
			{
				DollHover( gamedataEquipmentArea.Invalid );
				for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
				{
					m_equipmentMinigrids[ i ].OpacityShow();
				}
			}
			m_hoverArea = gamedataEquipmentArea.Invalid;
			m_capacityHoverEvent.IsHover = false;
			QueueEvent( m_capacityHoverEvent );
			m_armorHoverEvent.IsHover = false;
			QueueEvent( m_armorHoverEvent );
		}
		AnimateMinigrids();
		PreviewMinigridSelection();
		UnhighlightUpgradeResources();
	}

	protected event OnBarHover( evt : BarHoverOverEvent )
	{
		var i : Int32;
		var tooltipLibararyName : CName;
		var grid : weak< CyberwareInventoryMiniGrid >;
		var gridArea : gamedataEquipmentArea;
		var tooltipData : RipperdocBarTooltipTooltipData;
		tooltipData = new RipperdocBarTooltipTooltipData;
		tooltipData.barType = evt.data.barType;
		tooltipData.totalValue = evt.data.totalValue;
		tooltipData.maxValue = evt.data.maxValue;
		tooltipData.maxDamageReduction = evt.data.maxDamageReduction;
		if( evt.data.barType == BarType.CurrentCapacity && m_isCapacityBarReady )
		{
			tooltipData.capacityPerk1Bought = m_capacityPerk1Bought;
			tooltipData.capacityPerk2Bought = m_capacityPerk2Bought;
			m_InventoryManager.GetPlayerCyberwareCapacitStats( tooltipData.statsData );
			tooltipData.health = m_InventoryManager.GetPlayerHealth();
			m_ripperdocHoverState = RipperdocHoverState.BarCapacity;
			tooltipLibararyName = 'RipperdocBarTooltip';
		}
		if( evt.data.barType == BarType.Armor && m_isArmorBarReady )
		{
			tooltipData.armorPerk1Bought = m_armorPerk1Bought;
			tooltipData.statValue = m_armorCWEquipedNum;
			m_InventoryManager.GetPlayerArmorStats( tooltipData.statsData );
			m_ripperdocHoverState = RipperdocHoverState.BarArmor;
			tooltipLibararyName = 'RipperdocBarTooltip';
		}
		if( evt.data.barType == BarType.Edgerunner && m_isCapacityBarReady )
		{
			tooltipData.capacityPerk1Bought = m_capacityPerk1Bought;
			tooltipData.capacityPerk2Bought = m_capacityPerk2Bought;
			m_InventoryManager.GetPlayerCyberwareCapacitStats( tooltipData.statsData );
			tooltipData.health = m_InventoryManager.GetPlayerHealth();
			m_ripperdocHoverState = RipperdocHoverState.BarCapacity;
			tooltipLibararyName = 'RipperdocBarTooltipEdgerunner';
		}
		m_TooltipsManager.AttachToCursor();
		m_TooltipsManager.ShowTooltip( tooltipLibararyName, tooltipData, m_defaultTooltipsMargin );
		if( evt.data.barType == BarType.Armor )
		{
			for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
			{
				grid = m_equipmentMinigrids[ i ];
				gridArea = grid.GetEquipmentArea();
				if( m_filterMode == RipperdocModes.Default )
				{
					if( ( gridArea == gamedataEquipmentArea.IntegumentarySystemCW || gridArea == gamedataEquipmentArea.MusculoskeletalSystemCW ) || gridArea == gamedataEquipmentArea.LegsCW )
					{
						grid.OpacityShow();
					}
					else
					{
						grid.OpacityHide( true );
					}
				}
			}
		}
	}

	protected event OnBarUnhover( evt : BarHoverOutEvent )
	{
		m_TooltipsManager.HideTooltips();
		m_ripperdocHoverState = RipperdocHoverState.None;
		AnimateMinigrids();
	}

	private function GetCapacityHoverEventData( item : weak< UIInventoryItem > ) : RipperdocMeterCapacityHoverEvent
	{
		var result : RipperdocMeterCapacityHoverEvent;
		var equippedItem : weak< UIInventoryItem >;
		result = new RipperdocMeterCapacityHoverEvent;
		result.CapacityChange = ( ( Int32 )( GetItemAttribute( item, gamedataStatType.HumanityAvailable ) ) );
		result.isCyberwareEquipped = item.IsEquipped();
		if( !( item.IsEquipped() ) )
		{
			equippedItem = GetMinigrid( item.GetEquipmentArea() ).GetEquippedData( item.GetID() );
			if( equippedItem )
			{
				result.CapacityChange -= ( ( Int32 )( GetItemAttribute( equippedItem, gamedataStatType.HumanityAvailable ) ) );
			}
		}
		else
		{
			result.CapacityChange *= -1;
		}
		return result;
	}

	private function GetArmorHoverEventData( item : weak< UIInventoryItem > ) : RipperdocMeterArmorHoverEvent
	{
		var result : RipperdocMeterArmorHoverEvent;
		var equippedItem : weak< UIInventoryItem >;
		var attunemendBonus : Float;
		var multiplierBonus : Float;
		GetItemArmorBonuses( item, attunemendBonus, multiplierBonus );
		result = new RipperdocMeterArmorHoverEvent;
		result.ArmorChange = GetItemArmor( item ) + attunemendBonus;
		result.ArmorMultiplier = multiplierBonus;
		result.CurrentArmorMultiplier = m_statsSystem.GetStatValue( m_playerID, gamedataStatType.ArmorMultBonus );
		if( !( item.IsEquipped() ) )
		{
			equippedItem = GetMinigrid( item.GetEquipmentArea() ).GetEquippedData( item.GetID() );
			if( equippedItem )
			{
				GetItemArmorBonuses( equippedItem, attunemendBonus, multiplierBonus );
				result.EquippedArmorChange = GetItemArmor( equippedItem ) + attunemendBonus;
				result.EquippedArmorMultiplier = multiplierBonus;
			}
		}
		else
		{
			result.ArmorChange *= -1.0;
			result.ArmorMultiplier *= -1.0;
		}
		return result;
	}

	private function GetTooltipData( item : weak< UIInventoryItem >, equippedItem : weak< UIInventoryItem >, isVendorItem : Bool, isBuybackStack : Bool ) : InventoryTooltipData
	{
		var itemTooltipData : InventoryTooltipData;
		if( item.IsEquipped() )
		{
			itemTooltipData = m_InventoryManager.GetTooltipDataForInventoryItem( item, true, isVendorItem );
			itemTooltipData.cyberwareUpgradeData = GetCyberwareUpgradeData( item, false );
		}
		else
		{
			if( equippedItem )
			{
			}
			else
			{
				itemTooltipData = m_InventoryManager.GetTooltipDataForInventoryItem( item, false, isVendorItem );
			}
			if( isBuybackStack )
			{
				itemTooltipData.buyPrice = ( ( Float )( RPGManager.CalculateSellPrice( m_VendorDataManager.GetVendorInstance().GetGame(), m_VendorDataManager.GetVendorInstance(), item.GetID() ) ) );
			}
		}
		itemTooltipData.SetManager( m_InventoryManager.GetUIInventorySystem().GetInventoryItemsManager() );
		return itemTooltipData;
	}

	private function GetCyberwareUpgradeData( item : weak< UIInventoryItem >, optional isUpgradeScreen : Bool ) : InventoryTooltiData_CyberwareUpgradeData
	{
		var upgradeComponentsItemData : weak< gameItemData >;
		var cyberwareUpgradeData : InventoryTooltiData_CyberwareUpgradeData;
		cyberwareUpgradeData = new InventoryTooltiData_CyberwareUpgradeData;
		cyberwareUpgradeData.isUpgradable = RPGManager.CanUpgradeCyberware( m_player, item.GetID(), item.IsEquipped(), gamedataQuality.Invalid, m_upgradeQuality, m_upgradeItem, m_upgradeCostData, true );
		cyberwareUpgradeData.isRipperdoc = m_screen != CyberwareScreenType.Inventory;
		cyberwareUpgradeData.isUpgradeScreen = isUpgradeScreen;
		cyberwareUpgradeData.upgradeCost = m_upgradeCostData;
		cyberwareUpgradeData.upgradeQuality = m_upgradeQuality;
		upgradeComponentsItemData = RPGManager.GetItemData( m_player.GetGame(), m_player, ItemID.FromTDBID( m_upgradeCostData.materialRecordID ) );
		cyberwareUpgradeData.playerComponents = upgradeComponentsItemData.GetQuantity();
		return cyberwareUpgradeData;
	}

	private function HideOpposideSideCategoreis( isLeftSide : Bool )
	{
		var i : Int32;
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			if( isLeftSide != m_equipmentMinigrids[ i ].IsLeftSide() )
			{
				m_equipmentMinigrids[ i ].OpacityHide( true );
			}
			else
			{
				m_equipmentMinigrids[ i ].OpacityShow();
			}
		}
	}

	private function ShowCWTooltip( item : weak< UIInventoryItem >, equippedItem : weak< UIInventoryItem >, widget : weak< inkWidget >, isVendorItem : Bool, isBuyBack : Bool, optional iconErrorInfo : DEBUG_IconErrorInfo )
	{
		var anchor : weak< inkWidget >;
		var isLeftSide : Bool;
		var placement : gameuiETooltipPlacement;
		var tooltipData : UIInventoryItemTooltipWrapper;
		anchor = widget;
		isLeftSide = m_equipmentMinigrids[ m_minigridsMap.FindFirst( m_hoverArea ) ].IsLeftSide();
		placement = gameuiETooltipPlacement.RightTop;
		if( m_filterMode == RipperdocModes.Default )
		{
			if( isLeftSide )
			{
				anchor = inkWidgetRef.Get( m_tooltipRightAnchor );
				placement = gameuiETooltipPlacement.RightCenter;
			}
			else
			{
				anchor = inkWidgetRef.Get( m_tooltipLeftAnchor );
				placement = gameuiETooltipPlacement.LeftCenter;
			}
			HideOpposideSideCategoreis( isLeftSide );
		}
		tooltipData = UIInventoryItemTooltipWrapper.Make( item, ( ( isVendorItem ) ? ( m_vendorItemDisplayContext ) : ( m_playerItemDisplayContext ) ) );
		if( item.GetItemData().HasTag( 'Cyberdeck' ) )
		{
			m_TooltipsManager.ShowTooltipAtWidget( 'cyberdeckTooltip', anchor, tooltipData, placement, false, inkMargin( m_defaultTooltipGap, 0.0, 0.0, 0.0 ) );
		}
		else if( item.GetItemType() == gamedataItemType.Prt_Program )
		{
			m_TooltipsManager.ShowTooltipAtWidget( 'programTooltip', anchor, tooltipData, placement, false, inkMargin( m_defaultTooltipGap, 0.0, 0.0, 0.0 ) );
		}
		else
		{
			if( isBuyBack )
			{
				tooltipData.m_overridePrice = RoundF( tooltipData.m_data.GetSellPrice() );
			}
			m_TooltipsManager.ShowTooltipAtWidget( 'itemTooltip', anchor, tooltipData, placement, false, inkMargin( m_defaultTooltipGap, 0.0, 0.0, 0.0 ) );
		}
	}

	private function ShowCWPerkTooltip( widget : weak< inkWidget > )
	{
		var data : RipperdocPerkTooltipData;
		data = new RipperdocPerkTooltipData;
		if( m_dollHoverArea == gamedataEquipmentArea.HandsCW )
		{
			m_ripperdocHoverState = RipperdocHoverState.SlotHands;
			data.ripperdocHoverState = m_ripperdocHoverState;
			m_TooltipsManager.ShowTooltipAtWidget( 'RipperdocPerkTooltip', widget, data, gameuiETooltipPlacement.RightTop, false, inkMargin( m_defaultTooltipGap, 0.0, 0.0, 0.0 ) );
		}
		if( m_dollHoverArea == gamedataEquipmentArea.MusculoskeletalSystemCW )
		{
			m_ripperdocHoverState = RipperdocHoverState.SlotSkeleton;
			data.ripperdocHoverState = m_ripperdocHoverState;
			m_TooltipsManager.ShowTooltipAtWidget( 'RipperdocPerkTooltip', widget, data, gameuiETooltipPlacement.RightTop, false, inkMargin( m_defaultTooltipGap, 0.0, 0.0, 0.0 ) );
		}
	}

	protected event OnCategoryHoverOverEvent( evt : CategoryHoverOverEvent )
	{
		ShowCategoryTooltip( evt.equipArea );
	}

	protected event OnCategoryHoverOutEvent( evt : CategoryHoverOutEvent )
	{
		m_TooltipsManager.HideTooltips();
	}

	private function ShowCategoryTooltip( equipArea : gamedataEquipmentArea )
	{
		var i : Int32;
		var anchorWidget : weak< inkWidget >;
		var placement : gameuiETooltipPlacement;
		var margin : inkMargin;
		var data : RipperdocCategoryTooltipData;
		data = new RipperdocCategoryTooltipData;
		data.category = equipArea;
		data.availableItems = GetCachedAvailableItemCounters( equipArea );
		data.ownedItems = data.availableItems - GetCachedVendorItemCounters( equipArea );
		data.screenType = m_screen;
		i = m_minigridsMap.FindFirst( equipArea );
		if( equipArea != gamedataEquipmentArea.MusculoskeletalSystemCW )
		{
			if( m_equipmentMinigrids[ i ].IsLeftSide() && m_filterMode == RipperdocModes.Default )
			{
				anchorWidget = m_equipmentMinigrids[ i ].GetLastSlot();
				placement = gameuiETooltipPlacement.LeftTop;
				margin = inkMargin( 0.0, 0.0, 0.0, 0.0 );
			}
			else if( m_equipmentMinigrids[ i ].IsLeftSide() && m_filterMode == RipperdocModes.Item )
			{
				anchorWidget = m_equipmentMinigrids[ i ].GetFirstSlot();
				placement = gameuiETooltipPlacement.RightTop;
				margin = inkMargin( m_defaultTooltipGap, 0.0, 0.0, 0.0 );
			}
			else
			{
				anchorWidget = m_equipmentMinigrids[ i ].GetLastSlot();
				placement = gameuiETooltipPlacement.RightTop;
				margin = inkMargin( m_defaultTooltipGap, 0.0, 0.0, 0.0 );
			}
		}
		else
		{
			anchorWidget = m_equipmentMinigrids[ i ].GetFirstSlot();
			placement = gameuiETooltipPlacement.RightTop;
			margin = inkMargin( m_defaultTooltipGap, 0.0, 0.0, 0.0 );
		}
		if( anchorWidget )
		{
			m_TooltipsManager.ShowTooltipAtWidget( 'RipperdocCategoryTooltip', anchorWidget, data, placement, false, margin );
		}
		else
		{
			m_TooltipsManager.AttachToCursor();
			m_TooltipsManager.ShowTooltip( 'RipperdocCategoryTooltip', data, m_defaultTooltipsMargin );
		}
	}

	private function SetButtonHints( toDefault : Bool, optional isClose : Bool )
	{
		var backLocKey : String;
		backLocKey = ( ( isClose ) ? ( "LocKey#903" ) : ( "LocKey#15324" ) );
		if( toDefault )
		{
			m_buttonHintsController.AddButtonHint( 'back', backLocKey );
		}
		else
		{
			m_buttonHintsController.RemoveButtonHint( 'back' );
			if( ( ( !( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetPlayerControlledObject(), 'LockInHubMenu' ) ) && !( m_ep1StandaloneTutorial ) ) && !( m_mq048TutorialFact ) ) || m_screen == CyberwareScreenType.Inventory )
			{
				m_buttonHintsController.AddButtonHint( 'back', GetLocalizedText( "LocKey#903" ) );
			}
		}
	}

	private function SetButtonHintsHover( item : weak< UIInventoryItem >, isVendorItem : Bool )
	{
		var isEquipped, isEmpty, isSellable : Bool;
		isEmpty = item == NULL;
		isEquipped = item.IsEquipped();
		isSellable = item.IsSellable();
		switch( m_screen )
		{
			case CyberwareScreenType.Ripperdoc:
				if( isEquipped && item.GetEquipmentArea() != gamedataEquipmentArea.EyesCW )
				{
					m_buttonHintsController.AddButtonHint( 'unequip_item', GetLocalizedText( "UI-UserActions-Unequip" ) );
				}
			if( isEmpty || isEquipped )
			{
				m_buttonHintsController.AddButtonHint( 'select', "LocKey#34928" );
			}
			else if( isVendorItem )
			{
				m_buttonHintsController.AddButtonHint( 'select', "LocKey#17847" );
			}
			else
			{
				if( isSellable )
				{
					m_buttonHintsController.AddButtonHint( 'disassemble_item', "LocKey#17848" );
				}
				m_buttonHintsController.AddButtonHint( 'select', "LocKey#246" );
			}
			if( isEquipped && CheckTokenAvailability() )
			{
				m_buttonHintsController.AddButtonHint( 'upgrade_perk', ( ( m_ripperdocTokenManager.IsItemUpgraded( item.GetID() ) ) ? ( "LocKey#79251" ) : ( "LocKey#79250" ) ) );
			}
			SetCursorContext( 'Hover' );
			break;
			case CyberwareScreenType.Inventory:
				if( m_filterMode == RipperdocModes.Default )
				{
					m_buttonHintsController.AddButtonHint( 'select', "LocKey#273" );
				}
			SetCursorContext( 'Default' );
			break;
		}
	}

	private function SetButtonHintsUnhover()
	{
		m_buttonHintsController.RemoveButtonHint( 'select' );
		m_buttonHintsController.RemoveButtonHint( 'upgrade_perk' );
		m_buttonHintsController.RemoveButtonHint( 'disassemble_item' );
		m_buttonHintsController.RemoveButtonHint( 'unequip_item' );
		m_buttonHintsController.RemoveButtonHint( m_upgradeCWInputName );
	}

	private function RefreshInventoryNextFrame()
	{
		if( !( m_inventoryRefreshRequested ) )
		{
			m_inventoryRefreshRequested = true;
			QueueEvent( new RipperdocRefreshInventoryEvent );
		}
	}

	protected event OnRefreshInventoryEvent( evt : RipperdocRefreshInventoryEvent )
	{
		m_inventoryRefreshRequested = false;
		DisplayInventory( m_isInventoryOpen );
	}

	private function DisplayInventory( visible : Bool )
	{
		var i, limit, targetIndex : Int32;
		var playerItems : array< weak< UIInventoryItem > >;
		var vendorItems : array< weak< WrappedUIInventoryItem > >;
		var filteredPlayerItems, filteredVendorItems : array< RipperdocWrappedUIInventoryItem >;
		var ripperdocItem : RipperdocWrappedUIInventoryItem;
		var soldItem : SoldItem;
		var playerCurrencyAmount : Int32;
		var requirementsManager : weak< UIInventoryItemRequirementsManager >;
		UpdateSoldItems();
		m_isInventoryOpen = visible;
		m_filterMode = ( ( visible ) ? ( RipperdocModes.Item ) : ( RipperdocModes.Default ) );
		if( !( visible ) )
		{
			m_filterArea = gamedataEquipmentArea.Invalid;
			m_inventoryView.Hide();
		}
		else
		{
			HideMainScreenTutorials();
			vendorItems = GetVendorItems( m_filterArea );
			if( !( m_isTutorial ) || ( ( ( !( m_vikTutorial ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' ) > 0 ) ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' ) > 0 ) ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' ) > 0 ) ) )
			{
				targetIndex = EquipmentAreaToIndex( m_filterArea );
				playerItems = m_cachedPlayerItems[ targetIndex ];
			}
			else
			{
				if( ( vendorItems.Size() == 0 ) && ( ( ( ( m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' ) < 1 ) && m_filterArea == gamedataEquipmentArea.EyesCW ) || ( ( m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' ) < 1 ) && m_filterArea == gamedataEquipmentArea.HandsCW ) ) || ( ( m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' ) < 1 ) && m_filterArea == gamedataEquipmentArea.IntegumentarySystemCW ) ) )
				{
					AddTutorialItemsToStock( m_filterArea, true );
				}
			}
			for( i = 0, limit = playerItems.Size(); i < limit; i += 1 )
			{
				if( FilterItem( playerItems[ i ] ) )
				{
					filteredPlayerItems.PushBack( RipperdocWrappedUIInventoryItem.Make( playerItems[ i ], m_playerItemDisplayContext ) );
					requirementsManager = playerItems[ i ].GetRequirementsManager( m_player );
					requirementsManager.SetIsEquippable( CheckIfCanEquip( playerItems[ i ].GetItemData(), m_filterArea ) );
				}
			}
			playerCurrencyAmount = m_VendorDataManager.GetLocalPlayerCurrencyAmount();
			for( i = 0, limit = vendorItems.Size(); i < limit; i += 1 )
			{
				if( FilterItem( vendorItems[ i ].Item ) )
				{
					ripperdocItem = RipperdocWrappedUIInventoryItem.Make( vendorItems[ i ].Item, m_vendorItemDisplayContext, vendorItems[ i ].AdditionalData );
					soldItem = m_soldItemsCache.GetItem( vendorItems[ i ].Item.GetID() );
					if( soldItem )
					{
						ripperdocItem.IsBuybackStack = true;
					}
					else
					{
						ripperdocItem.IsEnoughMoney = playerCurrencyAmount >= ( ( Int32 )( vendorItems[ i ].Item.GetBuyPrice() ) );
					}
					if( MarketSystem.IsNewItem( m_VendorDataManager.GetVendorInstance(), vendorItems[ i ].Item.GetTweakDBID() ) )
					{
						ripperdocItem.IsNew = true;
					}
					requirementsManager = vendorItems[ i ].Item.GetRequirementsManager( m_player );
					requirementsManager.SetIsEquippable( CheckIfCanEquip( vendorItems[ i ].Item.GetItemData(), m_filterArea ) );
					filteredVendorItems.PushBack( ripperdocItem );
				}
			}
			m_inventoryView.ShowArea( filteredPlayerItems, filteredVendorItems, m_filterArea );
		}
	}

	private function AddTutorialItemsToStock( area : gamedataEquipmentArea, optional force : Bool )
	{
		var vendor : Vendor;
		var transactionSystem : TransactionSystem;
		var tweakDBID : TweakDBID;
		var itemStack : SItemStack;
		vendor = MarketSystem.GetInstance( m_VendorDataManager.GetVendorInstance().GetGame() ).GetVendor( m_VendorDataManager.GetVendorInstance() );
		transactionSystem = GameInstance.GetTransactionSystem( m_player.GetGame() );
		if( ( m_questSystem.GetFact( 'tutorial_ripperdoc_items_added' ) > 0 ) && !( force ) )
		{
			return;
		}
		itemStack.quantity = 1;
		tweakDBID = m_tutorialEyesCW;
		if( ( ( area == gamedataEquipmentArea.Invalid || area == gamedataEquipmentArea.EyesCW ) && !( transactionSystem.HasItem( vendor.GetVendorObject(), ItemID.FromTDBID( tweakDBID ) ) ) ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' ) < 1 ) )
		{
			itemStack.itemID = ItemID.FromTDBID( tweakDBID );
			vendor.AddItemsToStock( itemStack );
			transactionSystem.GiveItem( vendor.GetVendorObject(), itemStack.itemID, itemStack.quantity );
		}
		tweakDBID = m_tutorialHandsCW;
		if( ( ( area == gamedataEquipmentArea.Invalid || area == gamedataEquipmentArea.HandsCW ) && !( transactionSystem.HasItem( vendor.GetVendorObject(), ItemID.FromTDBID( tweakDBID ) ) ) ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' ) < 1 ) )
		{
			itemStack.itemID = ItemID.FromTDBID( tweakDBID );
			vendor.AddItemsToStock( itemStack );
			transactionSystem.GiveItem( vendor.GetVendorObject(), itemStack.itemID, itemStack.quantity );
		}
		tweakDBID = m_tutorialArmorCW;
		if( ( ( area == gamedataEquipmentArea.Invalid || area == gamedataEquipmentArea.IntegumentarySystemCW ) && !( transactionSystem.HasItem( vendor.GetVendorObject(), ItemID.FromTDBID( tweakDBID ) ) ) ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' ) < 1 ) )
		{
			itemStack.itemID = ItemID.FromTDBID( tweakDBID );
			vendor.AddItemsToStock( itemStack );
			transactionSystem.GiveItem( vendor.GetVendorObject(), itemStack.itemID, itemStack.quantity );
		}
		m_questSystem.SetFact( 'tutorial_ripperdoc_items_added', 1 );
	}

	private function GetVendorItems( area : gamedataEquipmentArea ) : array< weak< WrappedUIInventoryItem > >
	{
		var i, limit, targetIndex : Int32;
		var result : array< weak< WrappedUIInventoryItem > >;
		var tweakDBID : TweakDBID;
		var tutorialSpecialMode : Bool;
		var data : array< weak< WrappedUIInventoryItem > >;
		var vendorItems : array< weak< IScriptable > >;
		tutorialSpecialMode = ( m_isTutorial && ( ( area == gamedataEquipmentArea.EyesCW || area == gamedataEquipmentArea.HandsCW ) || area == gamedataEquipmentArea.IntegumentarySystemCW ) ) && ( ( ( m_vikTutorial || ( m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' ) < 1 ) ) || ( m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' ) < 1 ) ) || ( m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' ) < 1 ) );
		if( tutorialSpecialMode )
		{
			switch( area )
			{
				case gamedataEquipmentArea.EyesCW:
					tweakDBID = m_tutorialEyesCW;
				break;
				case gamedataEquipmentArea.HandsCW:
					tweakDBID = m_tutorialHandsCW;
				break;
				case gamedataEquipmentArea.IntegumentarySystemCW:
					tweakDBID = m_tutorialArmorCW;
				break;
			}
		}
		if( area == gamedataEquipmentArea.Invalid )
		{
			m_vendorWrappedItems.GetValues( vendorItems );
			for( i = 0, limit = vendorItems.Size(); i < limit; i += 1 )
			{
				data.PushBack( ( ( weak< weak< WrappedUIInventoryItem > > )( vendorItems[ i ] ) ) );
			}
		}
		else
		{
			targetIndex = EquipmentAreaToIndex( m_filterArea );
			data = m_cachedVendorItems[ targetIndex ];
		}
		for( i = 0, limit = data.Size(); i < limit; i += 1 )
		{
			if( tutorialSpecialMode && ( tweakDBID != ItemID.GetTDBID( data[ i ].Item.GetID() ) ) )
			{
				continue;
			}
			result.PushBack( data[ i ] );
		}
		return result;
	}

	private function FilterItem( item : weak< UIInventoryItem > ) : Bool
	{
		if( item.IsEquipped() )
		{
			return false;
		}
		if( item.IsRecipe() )
		{
			return false;
		}
		if( EquipmentSystem.GetInstance( m_player ).GetPlayerData( m_player ).IsSideUpgradeEquipped( item.GetID() ) )
		{
			return false;
		}
		return m_filterArea == gamedataEquipmentArea.Invalid || m_filterArea == item.GetEquipmentArea();
	}

	private function GetItemWrapper( cachedInvyItem : InventoryItemData, isVendor : Bool, playerCurrencyAmount : Int32 ) : RipperdocInventoryItemData
	{
		var itemWrapper : RipperdocInventoryItemData;
		InventoryItemData.SetEquipRequirements( cachedInvyItem, RPGManager.GetEquipRequirements( m_player, InventoryItemData.GetGameItemData( cachedInvyItem ) ) );
		InventoryItemData.SetIsEquippable( cachedInvyItem, CheckIfCanEquip( InventoryItemData.GetGameItemData( cachedInvyItem ), InventoryItemData.GetEquipmentArea( cachedInvyItem ) ) );
		InventoryItemData.SetIsVendorItem( cachedInvyItem, isVendor );
		if( isVendor )
		{
			InventoryItemData.SetIsNew( cachedInvyItem, MarketSystem.IsNewItem( m_VendorDataManager.GetVendorInstance(), ItemID.GetTDBID( cachedInvyItem.ID ) ) );
		}
		m_InventoryManager.GetOrCreateInventoryItemSortData( cachedInvyItem, m_uiScriptableSystem );
		itemWrapper = new RipperdocInventoryItemData;
		itemWrapper.InventoryItem = cachedInvyItem;
		itemWrapper.IsVendor = isVendor;
		itemWrapper.IsUpgraded = m_ripperdocTokenManager.IsItemUpgraded( InventoryItemData.GetID( cachedInvyItem ) );
		itemWrapper.IsEnoughMoney = ( ( isVendor ) ? ( playerCurrencyAmount >= ( ( Int32 )( InventoryItemData.GetBuyPrice( cachedInvyItem ) ) ) ) : ( true ) );
		return itemWrapper;
	}

	private static function GetItemAttribute( const itemData : weak< gameItemData >, const attribute : gamedataStatType, player : GameObject ) : Float
	{
		var i : Int32;
		var reqs : array< SItemStackRequirementData >;
		reqs = RPGManager.GetEquipRequirements( player, itemData );
		for( i = reqs.Size() - 1; i >= 0; i -= 1 )
		{
			if( reqs[ i ].statType == attribute )
			{
				return reqs[ i ].requiredValue;
			}
		}
		return 0.0;
	}

	private function GetItemAttribute( const itemData : weak< gameItemData >, const attribute : gamedataStatType ) : Float
	{
		var i : Int32;
		var reqs : array< SItemStackRequirementData >;
		reqs = RPGManager.GetEquipRequirements( m_player, itemData );
		for( i = reqs.Size() - 1; i >= 0; i -= 1 )
		{
			if( reqs[ i ].statType == attribute )
			{
				return reqs[ i ].requiredValue;
			}
		}
		return 0.0;
	}

	private function GetItemAttribute( item : weak< UIInventoryItem >, const attribute : gamedataStatType ) : Float
	{
		return GetItemAttribute( item.GetItemData(), attribute );
	}

	private function GetItemAttributes( item : weak< UIInventoryItem > ) : array< SItemStackRequirementData >
	{
		var i : Int32;
		var reqs : array< SItemStackRequirementData >;
		reqs = RPGManager.GetEquipRequirements( m_player, item.GetItemData() );
		for( i = reqs.Size() - 1; i >= 0; i -= 1 )
		{
			if( reqs[ i ].requiredValue <= 0.0 )
			{
				reqs.PopBack();
			}
		}
		return reqs;
	}

	private function GetItemArmor( item : weak< UIInventoryItem > ) : Float
	{
		var stat : weak< UIInventoryItemStat >;
		stat = item.GetPrimaryStat();
		if( stat.Type == gamedataStatType.Armor && ( stat.Value > 0.0 ) )
		{
			return stat.Value;
		}
		return 0.0;
	}

	private function GetItemArmorBonuses( item : weak< UIInventoryItem >, out attunemend : Float, out multiplier : Float )
	{
		var i, limit : Int32;
		var modDataPackage : UIInventoryItemModDataPackage;
		var modsManager : weak< UIInventoryItemModsManager >;
		attunemend = 0.0;
		multiplier = 0.0;
		modsManager = item.GetModsManager();
		for( i = 0, limit = modsManager.GetModsSize(); i < limit; i += 1 )
		{
			modDataPackage = ( ( UIInventoryItemModDataPackage )( modsManager.GetMod( i ) ) );
			if( modDataPackage.Description == m_armorAttunemendDescription || modDataPackage.Description == m_armorAttunemendDescription2 )
			{
				attunemend = modDataPackage.DataPackage.floatValues[ 0 ];
			}
			if( modDataPackage.Description == m_armorMultBonusDescription )
			{
				multiplier = modDataPackage.DataPackage.floatValues[ 0 ];
			}
		}
		if( attunemend > 0.0 )
		{
			attunemend *= m_statsSystem.GetStatValue( m_playerID, gamedataStatType.TechnicalAbility );
		}
		if( multiplier > 0.0 )
		{
			multiplier /= 100.0;
		}
	}

	protected event OnSelectorChange( evt : RipperdocSelectorChangeEvent )
	{
		var prev, next : weak< CyberwareInventoryMiniGrid >;
		if( m_isInventoryOpen )
		{
			PlayRumble( RumbleStrength.SuperLight, RumbleType.Pulse, ( ( evt.SlidingRight ) ? ( RumblePosition.Right ) : ( RumblePosition.Left ) ) );
			m_audioSystem.Play( 'ui_gui_tab_change' );
			prev = GetMinigrid( m_filterArea );
			m_filterArea = m_allFilters[ evt.Index ];
			next = GetMinigrid( m_filterArea );
			DisplayInventory( true );
			prev.SetInteractive( false );
			next.SetInteractive( true );
			next.SetLabelImmediate( false );
			m_selector.Show( evt.Index );
			m_animationController.StartSlide( evt.SlidingRight, m_filterArea );
			DollHover( m_filterArea );
			DollSelect( true );
			prev.OpacityHide();
			next.OpacityShow();
			next.SetPositionImmediate( ( ( evt.SlidingRight ) ? ( m_minigridSelectorRightAnchorMargin ) : ( m_minigridSelectorLeftAnchorMargin ) ) );
			next.SetPosition( m_minigridTargetAnchorMargin, 0.5 );
		}
	}

	private function ResetMinigridPositions()
	{
		var i : Int32;
		var grid : weak< CyberwareInventoryMiniGrid >;
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			grid = m_equipmentMinigrids[ i ];
			grid.ResetPosition( m_selector.GetIndicatorIndex() != i, m_minigridSetPositionAnimationSpeed );
			grid.SetInteractive( true );
			grid.UnhighlightAllSlots();
		}
	}

	private function SetMinigridPosition( target : weak< CyberwareInventoryMiniGrid > )
	{
		var i, targetIndex : Int32;
		var grid : weak< CyberwareInventoryMiniGrid >;
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			grid = m_equipmentMinigrids[ i ];
			if( grid != target )
			{
				grid.ResetPosition( true );
				grid.SetInteractive( false );
			}
			else
			{
				targetIndex = i;
			}
		}
		m_selector.Show( targetIndex );
		target.SetPosition_Animation( m_minigridTargetAnchorMargin, m_minigridSetPositionAnimationSpeed, , , m_minigridSetPositionAnimInterpolationMode, m_minigridSetPositionAnimInterpolationType );
		target.SetInteractive( true );
	}

	private function AnimateMinigrids()
	{
		var i : Int32;
		var show : Bool;
		var grid : weak< CyberwareInventoryMiniGrid >;
		var gridArea : gamedataEquipmentArea;
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			grid = m_equipmentMinigrids[ i ];
			gridArea = grid.GetEquipmentArea();
			show = ( ( m_filterArea == gamedataEquipmentArea.Invalid && m_hoverArea == gamedataEquipmentArea.Invalid ) || gridArea == m_filterArea ) || gridArea == m_hoverArea;
			if( show )
			{
				grid.OpacityShow();
			}
			else
			{
				grid.OpacityHide();
			}
		}
	}

	private function UpdateMinigrids()
	{
		var i, j, limit : Int32;
		var selectedArea : gamedataEquipmentArea;
		var items : array< weak< UIInventoryItem > >;
		m_uiInventorySystem.GetInventoryItemsManager().FlushEquippedItems();
		for( j = 0; j < m_allFilters.Size(); j = j + 1 )
		{
			selectedArea = m_allFilters[ j ];
			items = m_uiInventorySystem.GetPlayerAreaItems( selectedArea );
			for( i = 0, limit = items.Size(); i < limit; i += 1 )
			{
				items[ i ].GetRequirementsManager( m_player ).SetIsEquippable( CheckIfCanEquip( items[ i ].GetItemData(), items[ i ].GetItemData() ) );
			}
			UpdateAllItemCounters( selectedArea );
			for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
			{
				if( m_equipmentMinigrids[ i ].GetEquipmentArea() == selectedArea )
				{
					m_equipmentMinigrids[ i ].UpdateData( selectedArea, items, m_screen );
					break;
				}
			}
		}
	}

	private function GetMinigrid( area : gamedataEquipmentArea ) : weak< CyberwareInventoryMiniGrid >
	{
		var i : Int32;
		var grid : weak< CyberwareInventoryMiniGrid >;
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			grid = m_equipmentMinigrids[ i ];
			if( grid.GetEquipmentArea() == area )
			{
				return grid;
			}
		}
		return NULL;
	}

	private function PreviewMinigridSelection( optional item : weak< UIInventoryItem > )
	{
		var i, slotIndex : Int32;
		var grid : weak< CyberwareInventoryMiniGrid >;
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			m_equipmentMinigrids[ i ].UnhighlightAllSlots();
		}
		grid = GetMinigrid( item.GetEquipmentArea() );
		if( grid != NULL )
		{
			grid.UnhighlightSelectedSlot();
			slotIndex = grid.GetSlotToEquipe( item.GetID() );
			grid.HighlightSlot( ( ( slotIndex == -1 ) ? ( 0 ) : ( slotIndex ) ), m_filterMode == RipperdocModes.Item );
		}
		else if( m_previewMinigrid != NULL )
		{
			m_previewMinigrid.HighlightSelectedSlot();
		}
	}

	private function SetMinigridSelection( slot : weak< InventoryItemDisplayController > )
	{
		var area : gamedataEquipmentArea;
		var grid : weak< CyberwareInventoryMiniGrid >;
		var index : Int32;
		area = slot.GetEquipmentArea();
		grid = GetMinigrid( area );
		m_previewMinigrid = grid;
		if( grid != NULL )
		{
			index = grid.GetSlotIndex( slot );
			if( ( index == grid.GetSelectedSlotIndex() ) && m_filterMode == RipperdocModes.Item )
			{
			}
			else
			{
				ClearMinigridSelection();
				grid.SelectSlot( index );
				SetMinigridPosition( grid );
				RefreshInventoryNextFrame();
			}
		}
	}

	private function ClearMinigridSelection()
	{
		var i : Int32;
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			m_equipmentMinigrids[ i ].UnselectSlot();
		}
	}

	protected event OnSetMenuEventDispatcher( menuEventDispatcher : weak< inkMenuEventDispatcher > )
	{
		super.OnSetMenuEventDispatcher( menuEventDispatcher );
		m_menuEventDispatcher = menuEventDispatcher;
		m_menuEventDispatcher.RegisterToEvent( 'OnBack', this, 'OnBack' );
		m_menuEventDispatcher.RegisterToEvent( 'OnCloseMenu', this, 'OnCloseMenu' );
		m_menuEventDispatcher.RegisterToEvent( 'OnBeforeLeaveScenario', this, 'OnBeforeLeaveScenario' );
		m_menuEventDispatcher.RegisterToEvent( 'OnSetScreenDisplayContext', this, 'OnSetScreenDisplayContext' );
	}

	protected event OnBack( userData : IScriptable )
	{
		var evt : OpenMenuRequest;
		m_audioSystem.Play( 'ui_gui_cyberware_tab_close' );
		switch( m_filterMode )
		{
			case RipperdocModes.Default:
				if( m_screen == CyberwareScreenType.Inventory && m_cameFromInventoryMenu )
				{
					evt = new OpenMenuRequest;
					evt.m_menuName = 'inventory_screen';
					evt.m_isMainMenu = true;
					evt.m_jumpBack = true;
					QueueBroadcastEvent( evt );
				}
				else if( m_screen == CyberwareScreenType.Inventory )
				{
					super.OnBack( userData );
				}
				else if( !( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetPlayerControlledObject(), 'LockInHubMenu' ) ) && !( m_isTutorial ) )
				{
					m_menuEventDispatcher.SpawnEvent( 'OnVendorClose' );
					if( m_vikTutorial )
					{
						UnequipAllFromGrid( gamedataEquipmentArea.EyesCW );
						UnequipAllFromGrid( gamedataEquipmentArea.HandsCW );
						m_vikTutorial = false;
					}
				}
			break;
			case RipperdocModes.Item:
				if( m_isPurchasing )
				{
					break;
				}
			m_audioSystem.Play( 'ui_gui_cyberware_paperdoll_zoom_out_01' );
			GetMinigrid( m_lastAreaVisited ).RefreshisNewPreview( MarketSystem.DoesEquipAreaContainNewItems( m_VendorDataManager.GetVendorInstance(), m_lastAreaVisited, true ) );
			m_lastAreaVisited = gamedataEquipmentArea.Invalid;
			m_hoverArea = gamedataEquipmentArea.Invalid;
			DisplayInventory( false );
			m_animationController.SetOutside();
			DollHover( gamedataEquipmentArea.Invalid );
			ClearMinigridSelection();
			ResetMinigridPositions();
			AnimateMinigrids();
			ShowMainScreenTutorials();
			HideInventoryTutorial();
			HideCapacityTutorial();
			HideArmorTutorial();
			InventoryModeWarnning( false );
			if( ( ( m_isTutorial && ( m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' ) > 0 ) ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' ) > 0 ) ) && ( m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' ) > 0 ) )
			{
				m_statusEffectSystem.RemoveStatusEffect( m_player.GetEntityID(), T"BaseStatusEffect.CyberwareTutorialAdjustments" );
				if( m_tutorialZeroCapacityModifier )
				{
					m_statsSystem.RemoveModifier( m_player.GetEntityID(), m_tutorialZeroCapacityModifier );
					m_tutorialZeroCapacityModifier = NULL;
				}
				m_isTutorial = false;
				m_menuEventDispatcher.SpawnEvent( 'OnTutorialComplete' );
			}
			break;
		}
	}

	private function DollHover( area : gamedataEquipmentArea )
	{
		if( m_dollHoverArea == area )
		{
			return;
		}
		if( m_dollSelected )
		{
			DollSelect( false );
		}
		if( m_dollHoverArea != gamedataEquipmentArea.Invalid )
		{
			m_animationController.StopHover();
		}
		if( area != gamedataEquipmentArea.Invalid )
		{
			m_animationController.StartHover( area );
		}
		m_dollHoverArea = area;
	}

	private function DollSelect( select : Bool )
	{
		if( m_dollHoverArea == gamedataEquipmentArea.Invalid || m_dollSelected == select )
		{
			return;
		}
		if( select )
		{
			m_animationController.StartSelect();
		}
		else
		{
			m_animationController.StopSelect();
		}
		m_dollSelected = select;
	}

	private function UpdateAllItemCounters( equipArea : gamedataEquipmentArea )
	{
		UpdateCachedPlayerItemCounters( equipArea, GetAreaPlayerItemCount( equipArea ) );
		UpdateCachedVendorItemCounters( equipArea, GetAreaVendorItemCount( equipArea ) );
		UpdateCachedAvailableItemCounters( equipArea, GetAmountOfAvailableItems( equipArea ) );
	}

	private function EquipmentAreaToIndex( equipArea : gamedataEquipmentArea ) : Int32
	{
		switch( equipArea )
		{
			case gamedataEquipmentArea.FrontalCortexCW:
				return 0;
			case gamedataEquipmentArea.SystemReplacementCW:
				return 1;
			case gamedataEquipmentArea.ArmsCW:
				return 2;
			case gamedataEquipmentArea.EyesCW:
				return 3;
			case gamedataEquipmentArea.MusculoskeletalSystemCW:
				return 4;
			case gamedataEquipmentArea.HandsCW:
				return 5;
			case gamedataEquipmentArea.NervousSystemCW:
				return 6;
			case gamedataEquipmentArea.CardiovascularSystemCW:
				return 7;
			case gamedataEquipmentArea.IntegumentarySystemCW:
				return 8;
			case gamedataEquipmentArea.LegsCW:
				return 9;
		}
		return -1;
	}

	private function IndexToEquipmentArea( index : Int32 ) : gamedataEquipmentArea
	{
		switch( index )
		{
			case 0:
				return gamedataEquipmentArea.FrontalCortexCW;
			case 1:
				return gamedataEquipmentArea.SystemReplacementCW;
			case 2:
				return gamedataEquipmentArea.ArmsCW;
			case 3:
				return gamedataEquipmentArea.EyesCW;
			case 4:
				return gamedataEquipmentArea.MusculoskeletalSystemCW;
			case 5:
				return gamedataEquipmentArea.HandsCW;
			case 6:
				return gamedataEquipmentArea.NervousSystemCW;
			case 7:
				return gamedataEquipmentArea.CardiovascularSystemCW;
			case 8:
				return gamedataEquipmentArea.IntegumentarySystemCW;
			case 9:
				return gamedataEquipmentArea.LegsCW;
		}
		return gamedataEquipmentArea.Invalid;
	}

	private function UpdateCachedAvailableItemCounters( equipArea : gamedataEquipmentArea, newCount : Int32 )
	{
		var index : Int32;
		index = EquipmentAreaToIndex( equipArea );
		if( index >= 0 )
		{
			m_cachedAvailableItemsCounters[ index ] = newCount;
		}
	}

	private function GetCachedAvailableItemCounters( equipArea : gamedataEquipmentArea ) : Int32
	{
		var index : Int32;
		index = EquipmentAreaToIndex( equipArea );
		if( index < 0 )
		{
			return 0;
		}
		return m_cachedAvailableItemsCounters[ index ];
	}

	private function UpdateCachedVendorItemCounters( equipArea : gamedataEquipmentArea, newCount : Int32 )
	{
		var index : Int32;
		index = EquipmentAreaToIndex( equipArea );
		if( index >= 0 )
		{
			m_cachedVendorItemsCounters[ index ] = newCount;
		}
	}

	private function GetCachedVendorItemCounters( equipArea : gamedataEquipmentArea ) : Int32
	{
		var index : Int32;
		index = EquipmentAreaToIndex( equipArea );
		if( index < 0 )
		{
			return 0;
		}
		return m_cachedVendorItemsCounters[ index ];
	}

	private function UpdateCachedPlayerItemCounters( equipArea : gamedataEquipmentArea, newCount : Int32 )
	{
		var index : Int32;
		index = EquipmentAreaToIndex( equipArea );
		if( index >= 0 )
		{
			m_cachedPlayerItemsCounters[ index ] = newCount;
		}
	}

	private function GetCachedPlayerItemCounters( equipArea : gamedataEquipmentArea ) : Int32
	{
		var index : Int32;
		index = EquipmentAreaToIndex( equipArea );
		if( index < 0 )
		{
			return 0;
		}
		return m_cachedPlayerItemsCounters[ index ];
	}

	private function GetAmountOfAvailableItems( equipArea : gamedataEquipmentArea ) : Int32
	{
		return GetCachedVendorItemCounters( equipArea ) + GetCachedPlayerItemCounters( equipArea );
	}

	private function GetAreaVendorItemCount( equipmentArea : gamedataEquipmentArea ) : Int32
	{
		var targetIndex : Int32;
		targetIndex = EquipmentAreaToIndex( equipmentArea );
		if( targetIndex < 0 )
		{
			return 0;
		}
		return m_cachedVendorItems[ targetIndex ].Size();
	}

	private function GetAreaPlayerItemCount( equipmentArea : gamedataEquipmentArea ) : Int32
	{
		var i, limit, counter, targetIndex : Int32;
		var items : array< weak< UIInventoryItem > >;
		targetIndex = EquipmentAreaToIndex( equipmentArea );
		if( targetIndex < 0 )
		{
			return 0;
		}
		items = m_cachedPlayerItems[ targetIndex ];
		for( i = 0, limit = items.Size(); i < limit; i += 1 )
		{
			if( items[ i ].IsEquipped() )
			{
				counter += 1;
			}
		}
		return counter;
	}

	private function OpenConfirmationPopup( item : weak< UIInventoryItem >, price : Int32, type : VendorConfirmationPopupType, listener : CName )
	{
		var data : VendorConfirmationPopupData;
		m_audioSystem.Play( 'ui_hacking_access_granted' );
		data = new VendorConfirmationPopupData;
		data.notificationName = 'base\gameplay\gui\widgets\notifications\vendor_confirmation.inkwidget';
		data.isBlocking = true;
		data.useCursor = true;
		data.queueName = 'modal_popup';
		data.inventoryItem = item;
		data.quantity = item.GetQuantity();
		data.type = type;
		data.price = price;
		if( type == VendorConfirmationPopupType.BuyAndEquipCyberware || type == VendorConfirmationPopupType.BuyNotEquipableCyberware )
		{
			m_isPurchasing = true;
		}
		m_tokenPopup = ShowGameNotification( data );
		m_tokenPopup.RegisterListener( this, listener );
		m_buttonHintsController.Hide();
	}

	protected event OnBuyConfirmationPopupClosed( data : inkGameNotificationData )
	{
		var resultData : VendorConfirmationPopupCloseData;
		m_tokenPopup = NULL;
		resultData = ( ( VendorConfirmationPopupCloseData )( data ) );
		m_isInEquipPopup = false;
		if( resultData.confirm )
		{
			m_VendorDataManager.BuyItemFromVendor( resultData.inventoryItem.GetItemData(), resultData.inventoryItem.GetQuantity() );
		}
		else
		{
			m_hoverArea = gamedataEquipmentArea.Invalid;
			AnimateMinigrids();
			m_isPurchasing = false;
		}
		m_buttonHintsController.Show();
		m_audioSystem.Play( 'ui_menu_onpress' );
	}

	protected event OnSellConfirmationPopupClosed( data : inkGameNotificationData )
	{
		var resultData : VendorConfirmationPopupCloseData;
		m_tokenPopup = NULL;
		resultData = ( ( VendorConfirmationPopupCloseData )( data ) );
		if( resultData.confirm )
		{
			m_VendorDataManager.SellItemToVendor( resultData.inventoryItem.GetItemData(), resultData.inventoryItem.GetQuantity() );
		}
		m_buttonHintsController.Show();
		m_audioSystem.Play( 'ui_menu_onpress' );
	}

	protected event OnBuyShardPopupClosed( data : inkGameNotificationData )
	{
		var resultData : RipperdocTokenPopupCloseData;
		var equippedItemData : weak< UIInventoryItem >;
		resultData = ( ( RipperdocTokenPopupCloseData )( data ) );
		equippedItemData = m_hoveredItem;
		m_tokenPopup = NULL;
		m_upgradeData = NULL;
		if( resultData.confirm )
		{
			m_isUpgrading = true;
			m_uiInventorySystem.GetInventoryItemsManager().FlushEquippedItems();
		}
		RPGManager.HandleBuyShardPopupClosed( m_player, equippedItemData.GetID(), resultData );
		m_buttonHintsController.Show();
		m_isInEquipPopup = false;
		m_hoverArea = gamedataEquipmentArea.Invalid;
		AnimateMinigrids();
		m_isPurchasing = false;
		m_audioSystem.Play( 'ui_menu_onpress' );
		if( resultData.confirm )
		{
			m_isHoveringOverUpgradableSlot = false;
			m_buttonHintsController.RemoveButtonHint( m_upgradeCWInputName );
			m_hoveredItemDisplay.PlayUpgradeFeedback();
			PlayRumble( RumbleStrength.SuperLight, RumbleType.Pulse, RumblePosition.Right );
			UnhighlightUpgradeResources();
			UpdateCraftingMaterial( m_upgradeCostData.materialRecordID );
			UpdateCapacityBar( false );
			UpdateArmorBar( false );
		}
	}

	protected event OnVendorHubMenuChanged( evt : VendorHubMenuChanged )
	{
		m_isActivePanel = evt.item == HubVendorMenuItems.Cyberware;
	}

	public function OnItemBought( itemID : ItemID, itemData : weak< gameItemData > )
	{
		if( !( m_isPurchasing ) )
		{
			return;
		}
		m_isPurchased = true;
		m_isPurchasing = false;
		m_isPurchaseEquip = true;
		RemoveCachedVendorItem( itemID );
		m_InventoryManager.MarkToRebuild();
		if( !( EquipCyberware( itemData ) ) )
		{
			m_isPurchased = false;
			m_isPurchaseEquip = false;
		}
		HideInventoryTutorial();
	}

	private function RemoveCachedVendorItem( itemID : ItemID )
	{
		var i, limit : Int32;
		var item : weak< WrappedUIInventoryItem >;
		var hash : Uint64;
		hash = ItemID.GetCombinedHash( itemID );
		item = ( ( weak< weak< WrappedUIInventoryItem > > )( m_vendorWrappedItems.Get( hash ) ) );
		if( item != NULL )
		{
			for( i = 0, limit = m_cachedVendorItems.Size(); i < limit; i += 1 )
			{
				m_cachedVendorItems[ i ].Remove( item );
			}
			m_vendorItems.Remove( hash );
			m_vendorWrappedItems.Remove( hash );
		}
	}

	protected event OnUIVendorItemSoldEvent( evt : UIVendorItemsSoldEvent )
	{
		PrepareVendorItems();
		PreparePlayerItems();
		RefreshInventoryNextFrame();
	}

	protected event OnUIVendorItemBoughtEvent( evt : UIVendorItemsBoughtEvent )
	{
		var i, limit : Int32;
		for( i = 0, limit = evt.itemsID.Size(); i < limit; i += 1 )
		{
			RemoveCachedVendorItem( evt.itemsID[ i ] );
		}
	}

	private function ShowMainScreenTutorials()
	{
		var handsBracket : TutorialBracketData;
		var eyesBracket : TutorialBracketData;
		var armorBracket : TutorialBracketData;
		var handsFact, eyesFact, armorFact : Int32;
		if( !( m_isTutorial ) )
		{
			return;
		}
		inkWidgetRef.SetVisible( m_slotsTutorialAnchor, true );
		handsBracket.bracketID = 'id_hand_ripper_panel';
		handsBracket.bracketType = gameTutorialBracketType.WidgetArea;
		eyesBracket.bracketID = 'id_eyes_ripper_panel';
		eyesBracket.bracketType = gameTutorialBracketType.WidgetArea;
		armorBracket.bracketID = 'id_armor_ripper_panel';
		armorBracket.bracketType = gameTutorialBracketType.WidgetArea;
		handsFact = m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' );
		eyesFact = m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' );
		armorFact = m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' );
		if( handsFact == 0 )
		{
			m_uiSystem.ShowTutorialBracket( handsBracket );
		}
		if( eyesFact == 0 )
		{
			m_uiSystem.ShowTutorialBracket( eyesBracket );
		}
		if( ( ( armorFact == 0 ) && ( handsFact == 1 ) ) && ( eyesFact == 1 ) )
		{
			EnableFocusTutorialModeArmor();
			m_uiSystem.ShowTutorialBracket( armorBracket );
		}
		if( ( ( armorFact == 1 ) && ( handsFact == 1 ) ) && ( eyesFact == 1 ) )
		{
			DisableFocusTutorialMode();
			inkCompoundRef.SetVisible( m_selectorAnchor, true );
			m_selector.SetIsInTutorial( false );
		}
	}

	private function HideMainScreenTutorials()
	{
		if( !( m_isTutorial ) )
		{
			return;
		}
		m_uiSystem.HideTutorialBracket( 'id_eyes_ripper_panel' );
		m_uiSystem.HideTutorialBracket( 'id_hand_ripper_panel' );
		m_uiSystem.HideTutorialBracket( 'id_armor_ripper_panel' );
		inkWidgetRef.SetVisible( m_slotsTutorialAnchor, false );
	}

	private function ShowInventoryTutorial()
	{
		var inventoryBracket : TutorialBracketData;
		var handsFact, eyesFact, armorFact : Int32;
		if( !( m_isTutorial ) )
		{
			return;
		}
		inventoryBracket.bracketID = 'item_area_extended';
		inventoryBracket.bracketType = gameTutorialBracketType.WidgetArea;
		handsFact = m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' );
		eyesFact = m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' );
		armorFact = m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' );
		switch( m_filterArea )
		{
			case gamedataEquipmentArea.EyesCW:
				( ( eyesFact == 0 ) ? ( m_uiSystem.ShowTutorialBracket( inventoryBracket ) ) : ( HideInventoryTutorial() ) );
			break;
			case gamedataEquipmentArea.HandsCW:
				( ( handsFact == 0 ) ? ( m_uiSystem.ShowTutorialBracket( inventoryBracket ) ) : ( HideInventoryTutorial() ) );
			break;
			case gamedataEquipmentArea.IntegumentarySystemCW:
				( ( armorFact == 0 ) ? ( m_uiSystem.ShowTutorialBracket( inventoryBracket ) ) : ( HideInventoryTutorial() ) );
			break;
		}
	}

	private function HideInventoryTutorial()
	{
		if( !( m_isTutorial ) )
		{
			return;
		}
		m_uiSystem.HideTutorialBracket( 'item_area_extended' );
	}

	protected event OnCapacityHoverTutorial( evt : RipperdocMeterCapacityHoverEvent )
	{
		var capacityBracket : TutorialBracketData;
		var handsFact, eyesFact : Int32;
		if( !( m_isTutorial ) )
		{
			return false;
		}
		capacityBracket.bracketID = 'id_capacity_ripper_panel';
		capacityBracket.bracketType = gameTutorialBracketType.WidgetArea;
		handsFact = m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' );
		eyesFact = m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' );
		if( ( handsFact == 1 ) || ( eyesFact == 1 ) )
		{
			return false;
		}
		if( evt.IsHover )
		{
			m_uiSystem.ShowTutorialBracket( capacityBracket );
			inkWidgetRef.SetVisible( m_capacityTutorialAnchor, true );
		}
		else
		{
			HideCapacityTutorial();
		}
	}

	private function HideCapacityTutorial()
	{
		if( !( m_isTutorial ) )
		{
			return;
		}
		m_uiSystem.HideTutorialBracket( 'id_capacity_ripper_panel' );
		inkWidgetRef.SetVisible( m_capacityTutorialAnchor, false );
	}

	protected event OnArmorHoverTutorial( evt : RipperdocMeterArmorHoverEvent )
	{
		var armorBracket : TutorialBracketData;
		var armorFact, handsFact, eyesFact : Int32;
		if( !( m_isTutorial ) )
		{
			return false;
		}
		armorBracket.bracketID = 'id_armorbar_ripper_panel';
		armorBracket.bracketType = gameTutorialBracketType.WidgetArea;
		armorFact = m_questSystem.GetFact( 'tutorial_ripperdoc_armor_passed' );
		handsFact = m_questSystem.GetFact( 'tutorial_ripperdoc_hands_passed' );
		eyesFact = m_questSystem.GetFact( 'tutorial_ripperdoc_eyes_passed' );
		if( ( ( armorFact == 1 ) || ( handsFact == 0 ) ) || ( eyesFact == 0 ) )
		{
			return false;
		}
		if( evt.IsHover && ( evt.ArmorChange >= 1.0 ) )
		{
			m_uiSystem.ShowTutorialBracket( armorBracket );
			inkWidgetRef.SetVisible( m_armorTutorialAnchor, true );
		}
		else
		{
			HideArmorTutorial();
		}
	}

	private function HideArmorTutorial()
	{
		if( !( m_isTutorial ) )
		{
			return;
		}
		m_uiSystem.HideTutorialBracket( 'id_armorbar_ripper_panel' );
		inkWidgetRef.SetVisible( m_armorTutorialAnchor, false );
	}

	private function EnableFocusTutorialModeHandsAndEye()
	{
		var i : Int32;
		var grid : weak< CyberwareInventoryMiniGrid >;
		var gridArea : gamedataEquipmentArea;
		if( !( m_isTutorial ) )
		{
			return;
		}
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			grid = m_equipmentMinigrids[ i ];
			gridArea = grid.GetEquipmentArea();
			if( gridArea == gamedataEquipmentArea.EyesCW || gridArea == gamedataEquipmentArea.HandsCW )
			{
				grid.OpacityShow();
			}
			else
			{
				grid.OpacityFullHide();
			}
		}
	}

	private function EnableFocusTutorialModeArmor()
	{
		var i : Int32;
		var grid : weak< CyberwareInventoryMiniGrid >;
		var gridArea : gamedataEquipmentArea;
		if( !( m_isTutorial ) )
		{
			return;
		}
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			grid = m_equipmentMinigrids[ i ];
			gridArea = grid.GetEquipmentArea();
			if( ( gridArea == gamedataEquipmentArea.EyesCW || gridArea == gamedataEquipmentArea.HandsCW ) || gridArea == gamedataEquipmentArea.IntegumentarySystemCW )
			{
				grid.OpacityShow();
				grid.OpacityFullShow();
			}
			else
			{
				grid.OpacityFullHide();
			}
		}
	}

	private function DisableFocusTutorialMode()
	{
		var i : Int32;
		var grid : weak< CyberwareInventoryMiniGrid >;
		if( !( m_isTutorial ) )
		{
			return;
		}
		for( i = 0; i < m_equipmentMinigrids.Size(); i += 1 )
		{
			grid = m_equipmentMinigrids[ i ];
			grid.OpacityFullShow();
		}
		inkWidgetRef.SetVisible( m_slotsTutorialAnchor, false );
	}

}

enum RipperdocModes
{
	Default = 0,
	Item = 1,
}

enum CyberwareScreenType
{
	Invalid = 0,
	Ripperdoc = 1,
	Inventory = 2,
}

class CyberwareTemplateClassifier extends inkVirtualItemTemplateClassifier
{

	public virtual function ClassifyItem( data : Variant ) : Uint32
	{
		return 0;
	}

}

class CyberwareDisplayWrapper extends IScriptable
{
	var displayData : InventoryItemDisplayData;
}

class CyberwareTabModsRequest extends Event
{
	var open : Bool;
	var wrapper : CyberwareDisplayWrapper;
}

class RipperdocCyberwareEquipAnimationCategory
{
	var m_factName : CName;
	var m_equipAreas : array< gamedataEquipmentArea >;
	var m_weight : Float;
	var m_equipCount : Int32;

	public function SetData( factName : CName, equipAreas : ref< array< gamedataEquipmentArea > >, weight : Float )
	{
		m_factName = factName;
		m_equipAreas = equipAreas;
		m_weight = weight;
		equipAreas.Clear();
	}

}

enum RipperdocHoverState
{
	None = 0,
	BarCapacity = 1,
	BarArmor = 2,
	SlotSkeleton = 3,
	SlotHands = 4,
}

class ArmorBarFinalizedEvent extends Event
{
}

class CapacityBarFinalizedEvent extends Event
{
}

class RipperdocRefreshInventoryEvent extends Event
{
}

class RipperdocRefreshMinigridsEvent extends Event
{
}

class RipperdocInvalidateMinigridsNextFrame extends Event
{
}

class HandleItemEquippedNextFrameEvent extends Event
{
	var itemID : ItemID;
}

class WrappedUIInventoryItem
{
	var Item : weak< UIInventoryItem >;
	var AdditionalData : IScriptable;

	public static function Make( item : weak< UIInventoryItem >, additionalData : IScriptable ) : WrappedUIInventoryItem
	{
		var instance : WrappedUIInventoryItem;
		instance = new WrappedUIInventoryItem;
		instance.Item = item;
		instance.AdditionalData = additionalData;
		return instance;
	}

}

class VendorItemAdditionalData
{
	var Requirement : SItemStackRequirementData;
	var IsAvailable : Bool;

	public static function Make( itemStack : ref< SItemStack > ) : VendorItemAdditionalData
	{
		var instance : VendorItemAdditionalData;
		instance = new VendorItemAdditionalData;
		instance.Requirement = itemStack.requirement;
		instance.IsAvailable = itemStack.isAvailable;
		return instance;
	}

}

