import class gameuiPersonalLinkSwitcherEvent extends Event
{
	import var isAdvanced : Bool;
}

import class RPGManager extends IScriptable
{
	public import static function GetItemData( gi : GameInstance, owner : GameObject, itemID : ItemID ) : weak< gameItemData >;
	public import static function GetInnerItemDataQuality( itemData : InnerItemData ) : gamedataQuality;
	public import static function GetItemDataQuality( itemData : weak< gameItemData > ) : gamedataQuality;
	public import static function GetFloatItemQuality( qualityStat : Float ) : gamedataQuality;
	public import static function IsInnerItemDataIconic( itemData : InnerItemData ) : Bool;
	public import static function IsItemDataIconic( itemData : weak< gameItemData > ) : Bool;
	public import static function IsItemBroken( itemData : gameItemData ) : Bool;
	public import static function ApplyAbilityArray( owner : weak< GameObject >, abilities : array< weak< GameplayAbility_Record > > );
	public import static function ShouldFlipNegativeValue( record : weak< Stat_Record > ) : Bool;
	public import static function ShouldSlotBeAvailable( owner : weak< GameObject >, item : ItemID, attachmentSlotRecord : weak< AttachmentSlot_Record > ) : Bool;
	public import static function IsSlotAvailable( itemData : weak< gameItemData >, attachmentSlotRecord : weak< AttachmentSlot_Record > ) : Bool;
	public import static function CalculateStatModifiers( modifiers : array< weak< StatModifier_Record > >, context : GameInstance, root : weak< GameObject >, targetID : StatsObjectID, optional instigator : StatsObjectID, optional itemStatsID : StatsObjectID ) : Float;
	public import static function CalculateAdditiveModifiers( modifiers : array< weak< StatModifier_Record > >, context : GameInstance, root : weak< GameObject >, targetID : StatsObjectID, optional instigator : StatsObjectID, optional itemStatsID : StatsObjectID ) : Float;
	public import static function CalculateMultiplierModifiers( modifiers : array< weak< StatModifier_Record > >, context : GameInstance, root : weak< GameObject >, targetID : StatsObjectID, optional instigator : StatsObjectID, optional itemStatsID : StatsObjectID ) : Float;
	public import static function CalculateAdditiveMultiplierModifiers( modifiers : array< weak< StatModifier_Record > >, context : GameInstance, root : weak< GameObject >, targetID : StatsObjectID, optional instigator : StatsObjectID, optional itemStatsID : StatsObjectID ) : Float;
	public import static function CalculateStatModifier( modifier : weak< StatModifier_Record >, context : GameInstance, root : weak< GameObject >, targetID : StatsObjectID, optional instigator : StatsObjectID, optional itemStatsID : StatsObjectID ) : Float;
	public import static function CalculateConstantModifier( modifier : weak< ConstantStatModifier_Record > ) : Float;
	public import static function CalculateRandomModifier( modifier : weak< RandomStatModifier_Record > ) : Float;
	public import static function CalculateCurveModifier( modifier : weak< CurveStatModifier_Record >, context : GameInstance, root : weak< GameObject >, targetID : StatsObjectID, optional instigator : StatsObjectID, optional itemStatsID : StatsObjectID ) : Float;
	public import static function CalculateCombinedModifier( modifier : weak< CombinedStatModifier_Record >, context : GameInstance, root : weak< GameObject >, targetID : StatsObjectID, optional instigator : StatsObjectID, optional itemStatsID : StatsObjectID ) : Float;
	public import static function GetRefObjectID( refObjectName : CName, context : GameInstance, root : weak< GameObject >, targetID : StatsObjectID, optional instigator : StatsObjectID, optional itemStatsID : StatsObjectID ) : StatsObjectID;
	public import static function CalculateBuyPrice( context : GameInstance, vendor : weak< GameObject >, itemID : ItemID, multiplier : Float ) : Int32;
	public import static function CalculateSellPrice( context : GameInstance, vendor : weak< GameObject >, itemID : ItemID ) : Int32;
	public import static function CalculateSellPriceItemData( context : GameInstance, vendor : weak< GameObject >, itemData : gameItemData ) : Int32;
	public import static function CalculatePowerDifferential( target : weak< GameObject > ) : gameEPowerDifferential;

	public static function CalculateStatModifiers( addValue : Float, multValue : Float, addMultValue : Float, const modifiers : ref< array< weak< StatModifier_Record > > >, context : GameInstance, root : weak< GameObject >, targetID : StatsObjectID, optional instigator : StatsObjectID, optional itemStatsID : StatsObjectID ) : Float
	{
		var additiveMods : array< weak< StatModifier_Record > >;
		var multiplierMods : array< weak< StatModifier_Record > >;
		var addMultMods : array< weak< StatModifier_Record > >;
		var i : Int32;
		var modType : CName;
		for( i = 0; i < modifiers.Size(); i += 1 )
		{
			modType = modifiers[ i ].ModifierType();
			switch( modType )
			{
				case 'Additive':
					additiveMods.PushBack( modifiers[ i ] );
				break;
				case 'Multiplier':
					multiplierMods.PushBack( modifiers[ i ] );
				break;
				case 'AdditiveMultiplier':
					addMultMods.PushBack( modifiers[ i ] );
				break;
				default:
					break;
			}
		}
		addValue += CalculateAdditiveModifiers( additiveMods, context, root, targetID, instigator, itemStatsID );
		multValue *= CalculateMultiplierModifiers( multiplierMods, context, root, targetID, instigator, itemStatsID );
		addMultValue += CalculateAdditiveMultiplierModifiers( addMultMods, context, root, targetID, instigator, itemStatsID );
		return ( addValue * multValue ) * addMultValue;
	}

	public static function InjectStatModifier( gi : GameInstance, obj : GameObject, modifier : gameStatModifierData )
	{
		GameInstance.GetStatsSystem( gi ).AddModifier( obj.GetEntityID(), modifier );
	}

	public static function InjectStatModifierToItem( gi : GameInstance, itemData : gameItemData, modifier : gameStatModifierData )
	{
		GameInstance.GetStatsSystem( gi ).AddModifier( itemData.GetStatsObjectID(), modifier );
	}

	public static function IsDamageStat( stat : gamedataStatType ) : Bool
	{
		return ( ( ( ( stat == gamedataStatType.PhysicalDamage || stat == gamedataStatType.ThermalDamage ) || stat == gamedataStatType.ChemicalDamage ) || stat == gamedataStatType.ElectricDamage ) || stat == gamedataStatType.DamagePerHit ) || stat == gamedataStatType.EffectiveDamagePerHit;
	}

	public static function GetStatValueFromObject( gi : GameInstance, object : weak< GameObject >, stat : gamedataStatType ) : Float
	{
		return GameInstance.GetStatsSystem( gi ).GetStatValue( object.GetEntityID(), stat );
	}

	public static function CheckPrereqs( const prereqs : ref< array< weak< IPrereq_Record > > >, target : weak< GameObject >, optional referenceStatsID : StatsObjectID ) : Bool
	{
		var i : Int32;
		var retVal : Bool;
		retVal = true;
		for( i = 0; i < prereqs.Size(); i += 1 )
		{
			retVal = retVal && CheckPrereq( prereqs[ i ], target );
			if( !( retVal ) )
			{
				break;
			}
		}
		return retVal;
	}

	public static function CheckPrereq( prereqRecord : weak< IPrereq_Record >, target : weak< GameObject >, optional referenceStatsID : StatsObjectID ) : Bool
	{
		var prereq : IPrereq;
		var statPrereq : StatPrereq;
		prereq = IPrereq.CreatePrereq( prereqRecord.GetID() );
		statPrereq = ( ( StatPrereq )( prereq ) );
		if( statPrereq && StatsObjectID.IsDefined( referenceStatsID ) )
		{
			if( !( statPrereq.IsFulfilled( target.GetGame(), target, referenceStatsID ) ) )
			{
				return false;
			}
		}
		else
		{
			if( !( prereq.IsFulfilled( target.GetGame(), target ) ) )
			{
				return false;
			}
		}
		return true;
	}

	public static function CheckPerkPrereqs( itemData : gameItemData, owner : GameObject, perkRequiredName : ref< String > ) : Bool
	{
		var prereqs : array< weak< IPrereq_Record > >;
		var perkPrereq : PerkPrereq_Record;
		var newPerkPrereq : PlayerIsNewPerkBoughtPrereq_Record;
		var perkType : gamedataPerkType;
		var newPerkType : gamedataNewPerkType;
		var perkRecord : Perk_Record;
		var newPerkRecord : NewPerk_Record;
		var checkedPerkName : String;
		var i : Int32;
		GetItemRecord( itemData.GetID() ).EquipPrereqs( prereqs );
		for( i = 0; i < prereqs.Size(); i += 1 )
		{
			perkPrereq = ( ( PerkPrereq_Record )( prereqs[ i ] ) );
			if( perkPrereq )
			{
				checkedPerkName = perkPrereq.Perk();
				perkRecord = TDB.GetPerkRecord( TDBID.Create( "Perks." + checkedPerkName ) );
				if( perkRecord )
				{
					perkType = perkRecord.Type();
					if( !( PlayerDevelopmentSystem.GetData( owner ).HasPerk( perkType ) ) )
					{
						perkRequiredName = perkRecord.Loc_name_key();
						return true;
					}
				}
			}
			else
			{
				newPerkPrereq = ( ( PlayerIsNewPerkBoughtPrereq_Record )( prereqs[ i ] ) );
				if( newPerkPrereq )
				{
					checkedPerkName = newPerkPrereq.PerkType();
					newPerkRecord = TDB.GetNewPerkRecord( TDBID.Create( "NewPerks." + checkedPerkName ) );
					if( newPerkRecord )
					{
						newPerkType = newPerkRecord.Type();
						if( PlayerDevelopmentSystem.GetData( owner ).IsNewPerkBought( newPerkType ) < newPerkPrereq.Level() )
						{
							perkRequiredName = newPerkRecord.Loc_name_key();
							return true;
						}
					}
				}
			}
		}
		return false;
	}

	public static function GetEquipRequirements( owner : GameObject, itemData : gameItemData ) : array< SItemStackRequirementData >
	{
		var i : Int32;
		var prereqs : array< weak< IPrereq_Record > >;
		var statPrereqs : array< StatPrereq_Record >;
		var data : SItemStackRequirementData;
		var datas : array< SItemStackRequirementData >;
		var itemRecord : weak< Item_Record >;
		itemRecord = GetItemRecord( itemData.GetID() );
		itemRecord.EquipPrereqs( prereqs );
		if( itemRecord.UsesVariants() )
		{
			itemRecord.GetVariantsItem( itemData.GetVariant() ).VariantPrereqs( prereqs );
		}
		statPrereqs = GetNestedPrereqs( prereqs );
		for( i = 0; i < statPrereqs.Size(); i += 1 )
		{
			data.statType = ( ( gamedataStatType )( ( ( Int32 )( EnumValueFromName( 'gamedataStatType', statPrereqs[ i ].StatType() ) ) ) ) );
			if( statPrereqs[ i ].GetStatModifiersCount() > 0 )
			{
				data.requiredValue = StatsSystemHelper.GetStatPrereqModifiersValue( owner.GetGame(), itemData.GetStatsObjectID(), statPrereqs[ i ].GetID() );
			}
			else
			{
				data.requiredValue = statPrereqs[ i ].ValueToCheck();
			}
			datas.PushBack( data );
		}
		return datas;
	}

	private static function GetNestedPrereqs( const prereqs : ref< array< weak< IPrereq_Record > > > ) : array< StatPrereq_Record >
	{
		var i, j : Int32;
		var statPrereq : StatPrereq_Record;
		var multiPrereq : MultiPrereq_Record;
		var statPrereqs, temps : array< StatPrereq_Record >;
		var nestedPrereqs : array< weak< IPrereq_Record > >;
		for( i = 0; i < prereqs.Size(); i += 1 )
		{
			multiPrereq = ( ( MultiPrereq_Record )( prereqs[ i ] ) );
			if( multiPrereq )
			{
				nestedPrereqs.Clear();
				multiPrereq.NestedPrereqs( nestedPrereqs );
				temps = GetNestedPrereqs( nestedPrereqs );
				for( j = 0; j < temps.Size(); j += 1 )
				{
					statPrereqs.PushBack( temps[ j ] );
				}
			}
			else
			{
				statPrereq = ( ( StatPrereq_Record )( prereqs[ i ] ) );
				if( statPrereq )
				{
					statPrereqs.PushBack( statPrereq );
				}
			}
		}
		return statPrereqs;
	}

	public static function GetFirstUnmetEquipRequirement( owner : GameObject, const equipRequirements : ref< array< SItemStackRequirementData > > ) : SItemStackRequirementData
	{
		var i : Int32;
		var statsSystem : StatsSystem;
		var emptyRequirement : SItemStackRequirementData;
		statsSystem = GameInstance.GetStatsSystem( owner.GetGame() );
		for( i = 0; i < equipRequirements.Size(); i += 1 )
		{
			if( statsSystem.GetStatValue( owner.GetEntityID(), equipRequirements[ i ].statType ) < equipRequirements[ i ].requiredValue )
			{
				return equipRequirements[ i ];
			}
		}
		emptyRequirement.statType = gamedataStatType.Invalid;
		return emptyRequirement;
	}

	public static function GetRarityMultiplier( puppet : weak< NPCPuppet >, curveName : CName ) : Float
	{
		var rarity : gamedataNPCRarity;
		var statsDataSystem : StatsDataSystem;
		var multiplier : Float;
		var powerLevel : Float;
		multiplier = 1.0;
		rarity = puppet.GetNPCRarity();
		statsDataSystem = GameInstance.GetStatsDataSystem( puppet.GetGame() );
		powerLevel = GameInstance.GetStatsSystem( puppet.GetGame() ).GetStatValue( puppet.GetEntityID(), gamedataStatType.PowerLevel );
		switch( rarity )
		{
			case gamedataNPCRarity.Trash:
				multiplier = statsDataSystem.GetValueFromCurve( 'puppet_preset_trash_mods', powerLevel, curveName );
			break;
			case gamedataNPCRarity.Weak:
				multiplier = statsDataSystem.GetValueFromCurve( 'puppet_preset_weak_mods', powerLevel, curveName );
			break;
			case gamedataNPCRarity.Rare:
				multiplier = statsDataSystem.GetValueFromCurve( 'puppet_preset_rare_mods', powerLevel, curveName );
			break;
			case gamedataNPCRarity.Elite:
				multiplier = statsDataSystem.GetValueFromCurve( 'puppet_preset_elite_mods', powerLevel, curveName );
			break;
			case gamedataNPCRarity.Officer:
				multiplier = statsDataSystem.GetValueFromCurve( 'puppet_preset_officer_mods', powerLevel, curveName );
			break;
			case gamedataNPCRarity.Normal:
				multiplier = statsDataSystem.GetValueFromCurve( 'puppet_preset_normal_mods', powerLevel, curveName );
			break;
			case gamedataNPCRarity.MaxTac:
			case gamedataNPCRarity.Boss:
				multiplier = statsDataSystem.GetValueFromCurve( 'puppet_preset_boss_mods', powerLevel, curveName );
			break;
			default:
				multiplier = 1.0;
			break;
		}
		return multiplier;
	}

	public static function ResistancesList() : array< gamedataStatType >
	{
		var resistances : array< gamedataStatType >;
		resistances.PushBack( gamedataStatType.PhysicalResistance );
		resistances.PushBack( gamedataStatType.ChemicalResistance );
		resistances.PushBack( gamedataStatType.ThermalResistance );
		resistances.PushBack( gamedataStatType.ElectricResistance );
		return resistances;
	}

	public static function ApplyAbility( owner : weak< GameObject >, ability : weak< GameplayAbility_Record > )
	{
		var GLP : GameplayLogicPackageSystem;
		GLP = GameInstance.GetGameplayLogicPackageSystem( owner.GetGame() );
		GLP.RemovePackage( owner, ability.AbilityPackage().GetID() );
		GLP.ApplyPackage( owner, owner, ability.AbilityPackage().GetID() );
	}

	public static function RemoveAbility( owner : weak< GameObject >, ability : weak< GameplayAbility_Record > )
	{
		var GLP : GameplayLogicPackageSystem;
		GLP = GameInstance.GetGameplayLogicPackageSystem( owner.GetGame() );
		GLP.RemovePackage( owner, ability.AbilityPackage().GetID() );
	}

	public static function ApplyAbilityGroup( owner : weak< GameObject >, group : weak< GameplayAbilityGroup_Record > )
	{
		var abilities : array< weak< GameplayAbility_Record > >;
		if( group.GetAbilitiesCount() > 0 )
		{
			group.Abilities( abilities );
			RPGManager.ApplyAbilityArray( owner, abilities );
		}
	}

	public static function RemoveAbilityGroup( owner : weak< GameObject >, group : weak< GameplayAbilityGroup_Record > )
	{
		var i : Int32;
		var abilities : array< weak< GameplayAbility_Record > >;
		group.Abilities( abilities );
		for( i = 0; i < abilities.Size(); i += 1 )
		{
			RPGManager.RemoveAbility( owner, abilities[ i ] );
		}
	}

	public static function ApplyGLP( owner : weak< GameObject >, package : weak< GameplayLogicPackage_Record > )
	{
		var appliedPackages : array< TweakDBID >;
		var glpSys : GameplayLogicPackageSystem;
		glpSys = GameInstance.GetGameplayLogicPackageSystem( owner.GetGame() );
		glpSys.GetAppliedPackages( owner, appliedPackages );
		if( !( appliedPackages.Contains( package.GetID() ) ) || package.Stackable() )
		{
			glpSys.ApplyPackage( owner, owner, package.GetID() );
		}
	}

	public static function RemoveGLP( owner : weak< GameObject >, package : weak< GameplayLogicPackage_Record > )
	{
		var appliedPackages : array< TweakDBID >;
		var glpSys : GameplayLogicPackageSystem;
		glpSys = GameInstance.GetGameplayLogicPackageSystem( owner.GetGame() );
		glpSys.GetAppliedPackages( owner, appliedPackages );
		if( appliedPackages.Contains( package.GetID() ) )
		{
			glpSys.RemovePackage( owner, package.GetID() );
		}
	}

	public static function ApplyGLPArray( owner : weak< GameObject >, const arr : ref< array< weak< GameplayLogicPackage_Record > > >, optional ignoreAppliedPackages : Bool, optional withAnimationWrapperOverrides : Int32 )
	{
		var i : Int32;
		var hasAnimWrappers : Bool;
		var appliedPackages : array< TweakDBID >;
		var glpSys : GameplayLogicPackageSystem;
		glpSys = GameInstance.GetGameplayLogicPackageSystem( owner.GetGame() );
		if( ignoreAppliedPackages )
		{
			for( i = 0; i < arr.Size(); i += 1 )
			{
				hasAnimWrappers = arr[ i ].GetAnimationWrapperOverridesCount() > 0;
				if( ( ( withAnimationWrapperOverrides == 0 ) || ( ( withAnimationWrapperOverrides > 0 ) && hasAnimWrappers ) ) || ( ( withAnimationWrapperOverrides < 0 ) && !( hasAnimWrappers ) ) )
				{
					glpSys.ApplyPackage( owner, owner, arr[ i ].GetID() );
				}
			}
		}
		else
		{
			glpSys.GetAppliedPackages( owner, appliedPackages );
			for( i = 0; i < arr.Size(); i += 1 )
			{
				if( arr[ i ].Stackable() || !( appliedPackages.Contains( arr[ i ].GetID() ) ) )
				{
					glpSys.ApplyPackage( owner, owner, arr[ i ].GetID() );
				}
			}
		}
	}

	public static function ApplyEffectorsArray( owner : weak< GameObject >, const arr : ref< array< weak< Effector_Record > > > )
	{
		var i : Int32;
		var ES : EffectorSystem;
		ES = GameInstance.GetEffectorSystem( owner.GetGame() );
		for( i = 0; i < arr.Size(); i += 1 )
		{
			ES.ApplyEffector( owner.GetEntityID(), owner, arr[ i ].GetID() );
		}
	}

	public static function RemoveEffectorsArray( owner : weak< GameObject >, const arr : ref< array< weak< Effector_Record > > > )
	{
		var i : Int32;
		var ES : EffectorSystem;
		ES = GameInstance.GetEffectorSystem( owner.GetGame() );
		for( i = 0; i < arr.Size(); i += 1 )
		{
			ES.RemoveEffector( owner.GetEntityID(), arr[ i ].GetID() );
		}
	}

	public static function ApplyStatModifierGroups( owner : weak< GameObject >, const arr : ref< array< weak< StatModifierGroup_Record > > > )
	{
		var i : Int32;
		var modGroupID : Uint64;
		var SS : StatsSystem;
		SS = GameInstance.GetStatsSystem( owner.GetGame() );
		for( i = 0; i < arr.Size(); i += 1 )
		{
			modGroupID = TDBID.ToNumber( arr[ i ].GetID() );
			SS.DefineModifierGroupFromRecord( modGroupID, arr[ i ].GetID() );
			SS.ApplyModifierGroup( owner.GetEntityID(), modGroupID );
		}
	}

	public static function RemoveStatModifierGroups( owner : weak< GameObject >, const arr : ref< array< weak< StatModifierGroup_Record > > > )
	{
		var i : Int32;
		var modGroupID : Uint64;
		var SS : StatsSystem;
		SS = GameInstance.GetStatsSystem( owner.GetGame() );
		for( i = 0; i < arr.Size(); i += 1 )
		{
			modGroupID = TDBID.ToNumber( arr[ i ].GetID() );
			SS.RemoveModifierGroup( owner.GetEntityID(), modGroupID );
		}
	}

	public export static function GetLevelPercentage( object : GameObject ) : Int32
	{
		var exp : Int32;
		exp = PlayerDevelopmentSystem.GetData( object ).GetExperiencePercentage();
		return exp;
	}

	public static function GetItemQualityFromRecord( itemRecord : Item_Record ) : gamedataQuality
	{
		var quality : gamedataQuality;
		quality = itemRecord.Quality().Type();
		if( quality != gamedataQuality.Random )
		{
			return quality;
		}
		return gamedataQuality.Invalid;
	}

	public static function GetBumpedQuality( quality : gamedataQuality ) : gamedataQuality
	{
		switch( quality )
		{
			case gamedataQuality.Common:
				return gamedataQuality.Uncommon;
			case gamedataQuality.Uncommon:
				return gamedataQuality.Rare;
			case gamedataQuality.Rare:
				return gamedataQuality.Epic;
			case gamedataQuality.Epic:
				return gamedataQuality.Legendary;
			default:
				return quality;
		}
	}

	public static function GetItemQuality( itemData : InnerItemData ) : gamedataQuality
	{
		return GetItemQuality( InnerItemData.GetStatValueByType( itemData, gamedataStatType.Quality ) );
	}

	public static function ItemQualityNameToValue( q : CName ) : Float
	{
		var val : Float;
		switch( q )
		{
			case 'Common':
				val = 0.0;
			break;
			case 'Uncommon':
				val = 1.0;
			break;
			case 'Rare':
				val = 2.0;
			break;
			case 'Epic':
				val = 3.0;
			break;
			case 'Legendary':
				val = 4.0;
			break;
			default:
				val = 0.0;
			break;
		}
		return val;
	}

	public static function ItemQualityEnumToValue( q : gamedataQuality ) : Float
	{
		var val : Float;
		switch( q )
		{
			case gamedataQuality.Common:
				val = 0.0;
			break;
			case gamedataQuality.Uncommon:
				val = 1.0;
			break;
			case gamedataQuality.Rare:
				val = 2.0;
			break;
			case gamedataQuality.Epic:
				val = 3.0;
			break;
			case gamedataQuality.Legendary:
				val = 4.0;
			break;
			default:
				val = 0.0;
			break;
		}
		return val;
	}

	public static function SetQualityBasedOnLevel( object : weak< GameObject > ) : CName
	{
		var playerLevel : Float;
		var scalingValue : Float;
		var quality : CName;
		playerLevel = GameInstance.GetStatsSystem( object.GetGame() ).GetStatValue( object.GetEntityID(), gamedataStatType.Level );
		scalingValue = GameInstance.GetStatsDataSystem( object.GetGame() ).GetValueFromCurve( 'quality_curves', playerLevel, 'crafted_iconic_quality_to_level' );
		switch( scalingValue )
		{
			case 0.0:
				quality = 'Common';
			break;
			case 1.0:
				quality = 'Uncommon';
			break;
			case 2.0:
				quality = 'Rare';
			break;
			case 3.0:
				quality = 'Epic';
			break;
			case 4.0:
				quality = 'Legendary';
			break;
			default:
				quality = 'Common';
			break;
		}
		return quality;
	}

	public static function GetNextItemQuality( itemData : weak< gameItemData > ) : gamedataQuality
	{
		if( itemData )
		{
			return GetItemQuality( itemData.GetStatValueByType( gamedataStatType.Quality ) + 1.0 );
		}
		return gamedataQuality.Invalid;
	}

	public static function GetPlayerNextLevelBasedOnRandomQuality( quality : gamedataQuality ) : Int32
	{
		var playerLevel : Int32;
		switch( quality )
		{
			case gamedataQuality.Uncommon:
				playerLevel = 9;
			break;
			case gamedataQuality.Rare:
				playerLevel = 17;
			break;
			case gamedataQuality.Epic:
				playerLevel = 25;
			break;
			case gamedataQuality.Legendary:
				playerLevel = 33;
			break;
			default:
				playerLevel = -1;
			break;
		}
		return playerLevel;
	}

	public static function GetItemQuality( itemData : weak< gameItemData > ) : gamedataQuality
	{
		if( itemData )
		{
			return GetItemQuality( itemData.GetStatValueByType( gamedataStatType.Quality ) );
		}
		return gamedataQuality.Invalid;
	}

	public static function GetItemTierForUpgrades( itemData : weak< gameItemData > ) : gamedataQuality
	{
		if( itemData )
		{
			return GetItemTierForUpgrades( itemData.GetStatValueByType( gamedataStatType.WasItemUpgraded ) );
		}
		return gamedataQuality.Invalid;
	}

	public static function IsItemIconic( itemData : weak< gameItemData > ) : Bool
	{
		return itemData.GetStatValueByType( gamedataStatType.IsItemIconic ) > 0.0;
	}

	public static function IsItemIconic( itemData : InnerItemData ) : Bool
	{
		return InnerItemData.GetStatValueByType( itemData, gamedataStatType.IsItemIconic ) > 0.0;
	}

	public static function IsItemMaxLevel( itemData : weak< gameItemData > ) : Bool
	{
		var tempStat : Float;
		tempStat = itemData.GetStatValueByType( gamedataStatType.ItemLevel );
		return tempStat >= 500.0;
	}

	public static function IsItemMaxTier( itemData : weak< gameItemData > ) : Bool
	{
		var tempStat : Float;
		tempStat = itemData.GetStatValueByType( gamedataStatType.EffectiveTier );
		if( IsEP1() )
		{
			return tempStat > 9.98999977;
		}
		else
		{
			return tempStat > 8.98999977;
		}
	}

	public static function GetItemPlus( itemData : gameItemData ) : Float
	{
		if( itemData )
		{
			return itemData.GetStatValueByType( gamedataStatType.IsItemPlus );
		}
		return 0.0;
	}

	public static function IsItemWeapon( itemID : ItemID ) : Bool
	{
		return RPGManager.GetItemCategory( itemID ) == gamedataItemCategory.Weapon;
	}

	public static function IsItemClothing( itemID : ItemID ) : Bool
	{
		return RPGManager.GetItemCategory( itemID ) == gamedataItemCategory.Clothing;
	}

	public static function IsItemCyberware( itemID : ItemID ) : Bool
	{
		return RPGManager.GetItemCategory( itemID ) == gamedataItemCategory.Cyberware;
	}

	public static function IsItemGadget( itemID : ItemID ) : Bool
	{
		return RPGManager.GetItemCategory( itemID ) == gamedataItemCategory.Gadget;
	}

	public static function IsItemProgram( itemID : ItemID ) : Bool
	{
		return RPGManager.GetItemType( itemID ) == gamedataItemType.Prt_Program;
	}

	public static function IsItemMisc( itemID : ItemID ) : Bool
	{
		return RPGManager.GetItemType( itemID ) == gamedataItemType.Gen_Misc;
	}

	public static function IsItemTypeCyberwareWeapon( type : gamedataItemType ) : Bool
	{
		switch( type )
		{
			case gamedataItemType.Cyb_MantisBlades:
			case gamedataItemType.Cyb_NanoWires:
			case gamedataItemType.Cyb_StrongArms:
				return true;
			default:
				return false;
		}
	}

	public static function IsItemAdaptiveStemCells( itemID : TweakDBID ) : Bool
	{
		return TweakDBInterface.GetCName( itemID + T".cyberwareType", '' ) == 'AdaptiveStemCells';
	}

	public static function GetItemQuality( qualityStat : Float ) : gamedataQuality
	{
		var qualityInt : Int32;
		qualityInt = RoundF( qualityStat );
		switch( qualityInt )
		{
			case 0:
				return gamedataQuality.Common;
			case 1:
				return gamedataQuality.Uncommon;
			case 2:
				return gamedataQuality.Rare;
			case 3:
				return gamedataQuality.Epic;
			case 4:
				return gamedataQuality.Legendary;
			default:
				return gamedataQuality.Common;
		}
	}

	public static function GetItemTierForUpgrades( tierStat : Float ) : gamedataQuality
	{
		var tierInt : Int32;
		tierInt = RoundF( tierStat );
		switch( tierInt )
		{
			case 0:
				return gamedataQuality.Common;
			case 1:
				return gamedataQuality.CommonPlus;
			case 2:
				return gamedataQuality.Uncommon;
			case 3:
				return gamedataQuality.UncommonPlus;
			case 4:
				return gamedataQuality.Rare;
			case 5:
				return gamedataQuality.RarePlus;
			case 6:
				return gamedataQuality.Epic;
			case 7:
				return gamedataQuality.EpicPlus;
			case 8:
				return gamedataQuality.Legendary;
			case 9:
				return gamedataQuality.LegendaryPlus;
			case 10:
				return gamedataQuality.LegendaryPlusPlus;
			default:
				return gamedataQuality.Common;
		}
	}

	public static function GetCraftingMaterialRecord( quality : gamedataQuality, optional isQuickhack : Bool ) : Item_Record
	{
		var record : Item_Record;
		switch( quality )
		{
			case gamedataQuality.Common:
				record = TweakDBInterface.GetItemRecord( T"Items.CommonMaterial1" );
			break;
			case gamedataQuality.Uncommon:
				if( isQuickhack )
				{
					record = TweakDBInterface.GetItemRecord( T"Items.QuickHackUncommonMaterial1" );
				}
				else
				{
					record = TweakDBInterface.GetItemRecord( T"Items.UncommonMaterial1" );
				}
			break;
			case gamedataQuality.Rare:
				if( isQuickhack )
				{
					record = TweakDBInterface.GetItemRecord( T"Items.QuickHackRareMaterial1" );
				}
				else
				{
					record = TweakDBInterface.GetItemRecord( T"Items.RareMaterial1" );
				}
			break;
			case gamedataQuality.Epic:
				if( isQuickhack )
				{
					record = TweakDBInterface.GetItemRecord( T"Items.QuickHackEpicMaterial1" );
				}
				else
				{
					record = TweakDBInterface.GetItemRecord( T"Items.EpicMaterial1" );
				}
			break;
			case gamedataQuality.Legendary:
				if( isQuickhack )
				{
					record = TweakDBInterface.GetItemRecord( T"Items.QuickHackLegendaryMaterial1" );
				}
				else
				{
					record = TweakDBInterface.GetItemRecord( T"Items.LegendaryMaterial1" );
				}
			break;
			default:
				return record;
		}
		return record;
	}

	public static function GetAvailableSlotsForQuality( itemData : weak< gameItemData >, quality : gamedataQuality ) : Float
	{
		switch( quality )
		{
			case gamedataQuality.Common:
				return 0.0;
			break;
			case gamedataQuality.Uncommon:
				return 0.0;
			break;
			case gamedataQuality.Rare:
				return 1.0;
			break;
			case gamedataQuality.Epic:
				return 2.0;
			break;
			case gamedataQuality.Legendary:
				return 3.0;
			break;
			default:
				return 0.0;
		}
		return -1.0;
	}

	public static function GetListOfRandomStatsFromEvolutionType( evolution : gamedataWeaponEvolution ) : array< weak< Stat_Record > >
	{
		var record : weak< UIStatsMap_Record >;
		var statMap : array< weak< Stat_Record > >;
		var tempStr : String;
		if( evolution == gamedataWeaponEvolution.Invalid )
		{
			record = TweakDBInterface.GetUIStatsMapRecord( T"UIMaps.WeaponGeneral" );
		}
		else
		{
			tempStr = "UIMaps.";
			tempStr += EnumValueToString( "gamedataWeaponEvolution", ( ( Int32 )( evolution ) ) );
			record = TweakDBInterface.GetUIStatsMapRecord( TDBID.Create( tempStr ) );
		}
		record.PrimaryStats( statMap );
		return statMap;
	}

	public static function GetDominatingDamageType( gi : GameInstance, itemData : weak< gameItemData > ) : gamedataDamageType
	{
		var i : Int32;
		var dmgIndex : Int32;
		var highestValue : Float;
		var tempStat : Float;
		var statsSystem : StatsSystem;
		highestValue = 0.0;
		statsSystem = GameInstance.GetStatsSystem( gi );
		for( i = 0; i < ( ( Int32 )( gamedataDamageType.Count ) ); i += 1 )
		{
			tempStat = statsSystem.GetStatValueFromDamageType( itemData.GetStatsObjectID(), ( ( gamedataDamageType )( i ) ) );
			if( tempStat > highestValue )
			{
				highestValue = tempStat;
				dmgIndex = i;
			}
		}
		return ( ( gamedataDamageType )( dmgIndex ) );
	}

	public static function SetDroppedWeaponQuality( npc : weak< ScriptedPuppet >, itemData : weak< gameItemData > )
	{
		var SS : StatsSystem;
		var mod : gameStatModifierData;
		var quality : Float;
		if( !( npc ) )
		{
			return;
		}
		SS = GameInstance.GetStatsSystem( npc.GetGame() );
		if( RandF() < 0.89999998 )
		{
			quality = 0.0;
		}
		else
		{
			quality = 1.0;
		}
		SS.RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.Quality, true );
		mod = CreateStatModifier( gamedataStatType.Quality, gameStatModifierType.Additive, quality );
		SS.AddSavedModifier( itemData.GetStatsObjectID(), mod );
		ScriptedPuppet.EvaluateLootQualityByTask( npc );
	}

	public static function ForceItemTier( obj : weak< GameObject >, itemData : weak< gameItemData >, forcedQuality : CName )
	{
		var SS : StatsSystem;
		var value : Float;
		var helperMod, qualityMod, plusMod : gameStatModifierData;
		if( !( obj ) )
		{
			return;
		}
		SS = GameInstance.GetStatsSystem( obj.GetGame() );
		value = RPGManager.GetItemTierFromName( forcedQuality );
		if( ( ( itemData.GetStatValueByType( gamedataStatType.Quality ) * 2.0 ) + itemData.GetStatValueByType( gamedataStatType.IsItemPlus ) ) == value )
		{
			return;
		}
		helperMod = CreateStatModifier( gamedataStatType.ForceQualityHelper, gameStatModifierType.Additive, value );
		SS.AddSavedModifier( itemData.GetStatsObjectID(), helperMod );
		qualityMod = CreateStatModifierUsingCurve( gamedataStatType.Quality, gameStatModifierType.Additive, gamedataStatType.ForceQualityHelper, 'quality_curves', 'iconic_upgrades_amount_to_quality' );
		plusMod = CreateStatModifierUsingCurve( gamedataStatType.IsItemPlus, gameStatModifierType.Additive, gamedataStatType.ForceQualityHelper, 'quality_curves', 'iconic_upgrades_amount_to_plus' );
		SS.RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.Quality, true );
		SS.RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.RandomCurveInput, true );
		SS.RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.IsItemPlus, true );
		SS.RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.QualityToMaxQualityRatio, true );
		SS.RemoveAllModifiers( itemData.GetStatsObjectID(), gamedataStatType.CommonTierFailsafe, true );
		SS.AddSavedModifier( itemData.GetStatsObjectID(), qualityMod );
		SS.AddSavedModifier( itemData.GetStatsObjectID(), plusMod );
	}

	public static function ProcessOnLootedPackages( owner : weak< GameObject >, itemID : ItemID )
	{
		var i : Int32;
		var glp : GameplayLogicPackageSystem;
		var packages : array< weak< GameplayLogicPackage_Record > >;
		RPGManager.GetItemRecord( itemID ).OnLooted( packages );
		glp = GameInstance.GetGameplayLogicPackageSystem( owner.GetGame() );
		for( i = 0; i < packages.Size(); i += 1 )
		{
			glp.ApplyPackage( owner, owner, packages[ i ].GetID() );
		}
	}

	public constexpr static function GetItemTierFromName( tierName : CName ) : Float
	{
		switch( tierName )
		{
			case 'Common':
				return 0.0;
			case 'CommonPlus':
				return 1.0;
			case 'Uncommon':
				return 2.0;
			case 'UncommonPlus':
				return 3.0;
			case 'Rare':
				return 4.0;
			case 'RarePlus':
				return 5.0;
			case 'Epic':
				return 6.0;
			case 'EpicPlus':
				return 7.0;
			case 'Legendary':
				return 8.0;
			case 'LegendaryPlus':
				return 9.0;
			case 'LegendaryPlusPlus':
				return 10.0;
			default:
				return 0.0;
		}
	}

	public static function GetCombinedItemQualityValue( game : GameInstance, itemID : ItemID ) : Int32
	{
		var statsSystem : StatsSystem;
		var statsID : StatsObjectID;
		statsSystem = GameInstance.GetStatsSystem( game );
		statsID = ( ( StatsObjectID )( itemID ) );
		if( statsSystem && StatsObjectID.IsDefined( statsID ) )
		{
			return ( ( Int32 )( ( statsSystem.GetStatValue( statsID, gamedataStatType.Quality ) * 2.0 ) + statsSystem.GetStatValue( statsID, gamedataStatType.IsItemPlus ) ) );
		}
		return 0;
	}

	public constexpr static function ConvertQualityToNonPlusQuality( quality : gamedataQuality ) : gamedataQuality
	{
		switch( quality )
		{
			case gamedataQuality.Common:
			case gamedataQuality.CommonPlus:
				return gamedataQuality.Common;
			case gamedataQuality.Uncommon:
			case gamedataQuality.UncommonPlus:
				return gamedataQuality.Uncommon;
			case gamedataQuality.Rare:
			case gamedataQuality.RarePlus:
				return gamedataQuality.Rare;
			case gamedataQuality.Epic:
			case gamedataQuality.EpicPlus:
				return gamedataQuality.Epic;
			case gamedataQuality.Legendary:
			case gamedataQuality.LegendaryPlus:
			case gamedataQuality.LegendaryPlusPlus:
				return gamedataQuality.Legendary;
			default:
				return gamedataQuality.Invalid;
		}
	}

	public constexpr static function ConvertQualityToItemPlusValue( quality : gamedataQuality ) : Int32
	{
		switch( quality )
		{
			case gamedataQuality.LegendaryPlusPlus:
				return 2;
			case gamedataQuality.CommonPlus:
			case gamedataQuality.UncommonPlus:
			case gamedataQuality.RarePlus:
			case gamedataQuality.EpicPlus:
			case gamedataQuality.LegendaryPlus:
				return 1;
			default:
				return 0;
		}
	}

	public constexpr static function ConvertCombinedValueToQuality( combinedValue : Int32 ) : gamedataQuality
	{
		switch( combinedValue )
		{
			case 0:
				return gamedataQuality.Common;
			case 1:
				return gamedataQuality.CommonPlus;
			case 2:
				return gamedataQuality.Uncommon;
			case 3:
				return gamedataQuality.UncommonPlus;
			case 4:
				return gamedataQuality.Rare;
			case 5:
				return gamedataQuality.RarePlus;
			case 6:
				return gamedataQuality.Epic;
			case 7:
				return gamedataQuality.EpicPlus;
			case 8:
				return gamedataQuality.Legendary;
			case 9:
				return gamedataQuality.LegendaryPlus;
			case 10:
				return gamedataQuality.LegendaryPlusPlus;
			default:
				return gamedataQuality.Invalid;
		}
	}

	public constexpr static function ConvertQualityToCombinedValue( quality : gamedataQuality ) : Int32
	{
		switch( quality )
		{
			case gamedataQuality.Common:
				return 0;
			case gamedataQuality.CommonPlus:
				return 1;
			case gamedataQuality.Uncommon:
				return 2;
			case gamedataQuality.UncommonPlus:
				return 3;
			case gamedataQuality.Rare:
				return 4;
			case gamedataQuality.RarePlus:
				return 5;
			case gamedataQuality.Epic:
				return 6;
			case gamedataQuality.EpicPlus:
				return 7;
			case gamedataQuality.Legendary:
				return 8;
			case gamedataQuality.LegendaryPlus:
				return 9;
			case gamedataQuality.LegendaryPlusPlus:
				return 10;
			default:
				return 0;
		}
	}

	public constexpr static function ConvertPlayerLevelToCyberwareQuality( playerLevel : Float, includeAboveLegendary : Bool ) : gamedataQuality
	{
		if( includeAboveLegendary && ( playerLevel > 50.0 ) )
		{
			return gamedataQuality.LegendaryPlusPlus;
		}
		if( includeAboveLegendary && ( playerLevel > 46.0 ) )
		{
			return gamedataQuality.LegendaryPlus;
		}
		if( playerLevel > 39.0 )
		{
			return gamedataQuality.Legendary;
		}
		if( playerLevel > 36.0 )
		{
			return gamedataQuality.EpicPlus;
		}
		if( playerLevel > 29.0 )
		{
			return gamedataQuality.Epic;
		}
		if( playerLevel > 26.0 )
		{
			return gamedataQuality.RarePlus;
		}
		if( playerLevel > 19.0 )
		{
			return gamedataQuality.Rare;
		}
		if( playerLevel > 16.0 )
		{
			return gamedataQuality.UncommonPlus;
		}
		if( playerLevel > 9.0 )
		{
			return gamedataQuality.Uncommon;
		}
		if( playerLevel > 6.0 )
		{
			return gamedataQuality.CommonPlus;
		}
		return gamedataQuality.Common;
	}

	public static function HasItem( obj : weak< GameObject >, id : TweakDBID ) : Bool
	{
		var itemID : ItemID;
		itemID = ItemID.CreateQuery( id );
		return GameInstance.GetTransactionSystem( obj.GetGame() ).HasItem( obj, itemID );
	}

	public static function HasItem( obj : weak< GameObject >, id : ItemID ) : Bool
	{
		return GameInstance.GetTransactionSystem( obj.GetGame() ).HasItem( obj, id );
	}

	public static function GetItemType( itemID : ItemID ) : gamedataItemType
	{
		if( ItemID.IsValid( itemID ) )
		{
			return TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) ).ItemType().Type();
		}
		return gamedataItemType.Invalid;
	}

	public static function IsClothingMod( itemID : ItemID ) : Bool
	{
		if( ItemID.IsValid( itemID ) )
		{
			return IsClothingMod( RPGManager.GetItemType( itemID ) );
		}
		else
		{
			return false;
		}
	}

	public static function IsClothingMod( type : gamedataItemType ) : Bool
	{
		switch( type )
		{
			case gamedataItemType.Prt_FabricEnhancer:
				return true;
			case gamedataItemType.Prt_HeadFabricEnhancer:
				return true;
			case gamedataItemType.Prt_FaceFabricEnhancer:
				return true;
			case gamedataItemType.Prt_TorsoFabricEnhancer:
				return true;
			case gamedataItemType.Prt_OuterTorsoFabricEnhancer:
				return true;
			case gamedataItemType.Prt_PantsFabricEnhancer:
				return true;
			case gamedataItemType.Prt_BootsFabricEnhancer:
				return true;
			default:
				return false;
		}
	}

	public static function IsWeaponMod( itemID : ItemID ) : Bool
	{
		if( ItemID.IsValid( itemID ) )
		{
			return IsWeaponMod( RPGManager.GetItemType( itemID ) );
		}
		else
		{
			return false;
		}
	}

	public static function IsWeaponMod( type : gamedataItemType ) : Bool
	{
		switch( type )
		{
			case gamedataItemType.Prt_Mod:
				return true;
			case gamedataItemType.Prt_RangedMod:
				return true;
			case gamedataItemType.Prt_PowerMod:
				return true;
			case gamedataItemType.Prt_TechMod:
				return true;
			case gamedataItemType.Prt_SmartMod:
				return true;
			case gamedataItemType.Prt_AR_SMG_LMGMod:
				return true;
			case gamedataItemType.Prt_HandgunMod:
				return true;
			case gamedataItemType.Prt_Precision_Sniper_RifleMod:
				return true;
			case gamedataItemType.Prt_ShotgunMod:
				return true;
			case gamedataItemType.Prt_MeleeMod:
				return true;
			case gamedataItemType.Prt_BladeMod:
				return true;
			case gamedataItemType.Prt_BluntMod:
				return true;
			case gamedataItemType.Prt_ThrowableMod:
				return true;
			default:
				return false;
		}
	}

	public static function IsScopeAttachment( itemID : ItemID ) : Bool
	{
		if( ItemID.IsValid( itemID ) )
		{
			return IsScopeAttachment( RPGManager.GetItemType( itemID ) );
		}
		else
		{
			return false;
		}
	}

	public static function IsScopeAttachment( type : gamedataItemType ) : Bool
	{
		switch( type )
		{
			case gamedataItemType.Prt_Scope:
				return true;
			case gamedataItemType.Prt_ShortScope:
				return true;
			case gamedataItemType.Prt_LongScope:
				return true;
			case gamedataItemType.Prt_TechSniperScope:
				return true;
			case gamedataItemType.Prt_PowerSniperScope:
				return true;
			default:
				return false;
		}
	}

	public static function IsMuzzleAttachment( type : gamedataItemType ) : Bool
	{
		switch( type )
		{
			case gamedataItemType.Prt_Muzzle:
				return true;
			case gamedataItemType.Prt_RifleMuzzle:
				return true;
			case gamedataItemType.Prt_HandgunMuzzle:
				return true;
			default:
				return false;
		}
	}

	public static function ReturnRetrievableWeaponMods( itemData : weak< gameItemData >, owner : weak< GameObject > )
	{
		var restoredAttachments : array< ItemAttachments >;
		var transactionSystem : TransactionSystem;
		var removedID : ItemID;
		var i : Int32;
		if( !( itemData ) || !( owner ) )
		{
			return;
		}
		restoredAttachments = GetRetrievableAttachments( itemData );
		transactionSystem = GameInstance.GetTransactionSystem( owner.GetGame() );
		for( i = 0; i < restoredAttachments.Size(); i += 1 )
		{
			removedID = transactionSystem.RemovePart( owner, itemData.GetID(), restoredAttachments[ i ].attachmentSlotID );
			if( ItemID.IsValid( removedID ) )
			{
				transactionSystem.GiveItem( owner, restoredAttachments[ i ].itemID, 1 );
			}
		}
	}

	public static function GetRetrievableAttachments( itemData : weak< gameItemData > ) : array< ItemAttachments >
	{
		var restoredAttachments : array< ItemAttachments >;
		var slotsToCheck : array< TweakDBID >;
		var tempArr : array< TweakDBID >;
		var innerPartID : ItemID;
		var partTags : array< CName >;
		var innerPart : InnerItemData;
		var i : Int32;
		if( !( itemData ) )
		{
			return restoredAttachments;
		}
		if( RPGManager.GetItemCategory( itemData.GetID() ) == gamedataItemCategory.Weapon )
		{
			slotsToCheck = RPGManager.GetAttachmentSlotIDs();
			tempArr = RPGManager.GetModsSlotIDs( itemData.GetItemType() );
			for( i = 0; i < tempArr.Size(); i += 1 )
			{
				slotsToCheck.PushBack( tempArr[ i ] );
			}
			for( i = 0; i < slotsToCheck.Size(); i += 1 )
			{
				itemData.GetItemPart( innerPart, slotsToCheck[ i ] );
				innerPartID = InnerItemData.GetItemID( innerPart );
				partTags = InnerItemData.GetStaticData( innerPart ).Tags();
				if( ItemID.IsValid( innerPartID ) && partTags.Contains( 'Retrievable' ) )
				{
					restoredAttachments.PushBack( ItemAttachments.Create( innerPartID, slotsToCheck[ i ] ) );
				}
			}
		}
		return restoredAttachments;
	}

	public static function GetItemCategory( itemID : ItemID ) : gamedataItemCategory
	{
		var itemRecord : Item_Record;
		var itemCategory : weak< ItemCategory_Record >;
		if( ItemID.IsValid( itemID ) )
		{
			itemRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) );
			if( itemRecord )
			{
				itemCategory = itemRecord.ItemCategory();
				if( itemCategory )
				{
					return itemCategory.Type();
				}
			}
		}
		return gamedataItemCategory.Invalid;
	}

	public static function GetWeaponEvolution( itemID : ItemID ) : gamedataWeaponEvolution
	{
		var itemRecord : WeaponItem_Record;
		var weaponEvolution : weak< WeaponEvolution_Record >;
		if( ItemID.IsValid( itemID ) )
		{
			itemRecord = TweakDBInterface.GetWeaponItemRecord( ItemID.GetTDBID( itemID ) );
			if( itemRecord )
			{
				weaponEvolution = itemRecord.Evolution();
				if( weaponEvolution )
				{
					return weaponEvolution.Type();
				}
			}
		}
		return gamedataWeaponEvolution.Invalid;
	}

	public static function GetItemWeight( itemData : gameItemData ) : Float
	{
		if( itemData )
		{
			return itemData.GetStatValueByType( gamedataStatType.Weight );
		}
		return 0.0;
	}

	public static function GetItemStackWeight( owner : weak< GameObject >, itemData : weak< gameItemData > ) : Float
	{
		var quantity : Float;
		var weight : Float;
		quantity = ( ( Float )( GameInstance.GetTransactionSystem( owner.GetGame() ).GetItemQuantity( owner, itemData.GetID() ) ) );
		weight = GetItemWeight( itemData );
		return quantity * weight;
	}

	public static function IsItemSingleInstance( itemData : weak< gameItemData > ) : Bool
	{
		return GetItemRecord( itemData.GetID() ).IsSingleInstance();
	}

	public static function GetItemFromInventory( object : GameObject, item : TweakDBID ) : ItemID
	{
		var i : Int32;
		var items : array< weak< gameItemData > >;
		var TS : TransactionSystem;
		TS = GameInstance.GetTransactionSystem( object.GetGame() );
		TS.GetItemList( object, items );
		for( i = i; i < items.Size(); i += 1 )
		{
			if( ItemID.GetTDBID( items[ i ].GetID() ) == item )
			{
				return items[ i ].GetID();
			}
		}
		return ItemID.None();
	}

	public static function GetAttachmentSlotIDs() : array< TweakDBID >
	{
		var arr : array< TweakDBID >;
		arr.PushBack( T"AttachmentSlots.Scope" );
		arr.PushBack( T"AttachmentSlots.ScopeRail" );
		arr.PushBack( T"AttachmentSlots.PowerModule" );
		return arr;
	}

	public static function GetModsSlotIDs( type : gamedataItemType ) : array< TweakDBID >
	{
		var arr : array< TweakDBID >;
		switch( type )
		{
			case gamedataItemType.Clo_Head:
				arr.PushBack( T"AttachmentSlots.HeadFabricEnhancer1" );
			arr.PushBack( T"AttachmentSlots.HeadFabricEnhancer2" );
			arr.PushBack( T"AttachmentSlots.HeadFabricEnhancer3" );
			arr.PushBack( T"AttachmentSlots.HeadFabricEnhancer4" );
			break;
			case gamedataItemType.Clo_Feet:
				arr.PushBack( T"AttachmentSlots.FootFabricEnhancer1" );
			arr.PushBack( T"AttachmentSlots.FootFabricEnhancer2" );
			arr.PushBack( T"AttachmentSlots.FootFabricEnhancer3" );
			arr.PushBack( T"AttachmentSlots.FootFabricEnhancer4" );
			break;
			case gamedataItemType.Clo_Face:
				arr.PushBack( T"AttachmentSlots.FaceFabricEnhancer1" );
			arr.PushBack( T"AttachmentSlots.FaceFabricEnhancer2" );
			arr.PushBack( T"AttachmentSlots.FaceFabricEnhancer3" );
			arr.PushBack( T"AttachmentSlots.FaceFabricEnhancer4" );
			break;
			case gamedataItemType.Clo_InnerChest:
				arr.PushBack( T"AttachmentSlots.InnerChestFabricEnhancer1" );
			arr.PushBack( T"AttachmentSlots.InnerChestFabricEnhancer2" );
			arr.PushBack( T"AttachmentSlots.InnerChestFabricEnhancer3" );
			break;
			case gamedataItemType.Clo_Legs:
				arr.PushBack( T"AttachmentSlots.LegsFabricEnhancer1" );
			arr.PushBack( T"AttachmentSlots.LegsFabricEnhancer2" );
			arr.PushBack( T"AttachmentSlots.LegsFabricEnhancer3" );
			arr.PushBack( T"AttachmentSlots.LegsFabricEnhancer4" );
			break;
			case gamedataItemType.Clo_OuterChest:
				arr.PushBack( T"AttachmentSlots.OuterChestFabricEnhancer1" );
			arr.PushBack( T"AttachmentSlots.OuterChestFabricEnhancer2" );
			arr.PushBack( T"AttachmentSlots.OuterChestFabricEnhancer3" );
			break;
			case gamedataItemType.Wea_AssaultRifle:
			case gamedataItemType.Wea_Rifle:
			case gamedataItemType.Wea_SubmachineGun:
			case gamedataItemType.Wea_LightMachineGun:
				arr.PushBack( T"AttachmentSlots.Power_AR_SMG_LMG_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Power_AR_SMG_LMG_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Power_AR_SMG_LMG_WeaponMod1_Collectible" );
			arr.PushBack( T"AttachmentSlots.Power_AR_SMG_LMG_WeaponMod2_Collectible" );
			arr.PushBack( T"AttachmentSlots.Tech_AR_SMG_LMG_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Tech_AR_SMG_LMG_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Smart_AR_SMG_LMG_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Smart_AR_SMG_LMG_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod1" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod2" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod3" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod4" );
			return arr;
			case gamedataItemType.Wea_Handgun:
			case gamedataItemType.Wea_Revolver:
				arr.PushBack( T"AttachmentSlots.Power_Handgun_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Power_Handgun_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Power_Handgun_WeaponMod1_Collectible" );
			arr.PushBack( T"AttachmentSlots.Power_Handgun_WeaponMod2_Collectible" );
			arr.PushBack( T"AttachmentSlots.Tech_Handgun_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Tech_Handgun_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Smart_Handgun_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Smart_Handgun_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Smart_Handgun_WeaponMod1_Collectible" );
			arr.PushBack( T"AttachmentSlots.Smart_Handgun_WeaponMod2_Collectible" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod1" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod2" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod3" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod4" );
			return arr;
			case gamedataItemType.Wea_PrecisionRifle:
			case gamedataItemType.Wea_SniperRifle:
				arr.PushBack( T"AttachmentSlots.Power_Precision_Sniper_Rifle_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Power_Precision_Sniper_Rifle_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Tech_Precision_Sniper_Rifle_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Tech_Precision_Sniper_Rifle_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Tech_Precision_Sniper_Rifle_WeaponMod1_Collectible" );
			arr.PushBack( T"AttachmentSlots.Tech_Precision_Sniper_Rifle_WeaponMod2_Collectible" );
			arr.PushBack( T"AttachmentSlots.Smart_Precision_Sniper_Rifle_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Smart_Precision_Sniper_Rifle_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Smart_Precision_Sniper_Rifle_WeaponMod1_Collectible" );
			arr.PushBack( T"AttachmentSlots.Smart_Precision_Sniper_Rifle_WeaponMod2_Collectible" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod1" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod2" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod3" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod4" );
			return arr;
			case gamedataItemType.Wea_Shotgun:
			case gamedataItemType.Wea_ShotgunDual:
				arr.PushBack( T"AttachmentSlots.Power_Shotgun_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Power_Shotgun_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Power_Shotgun_WeaponMod1_Collectible" );
			arr.PushBack( T"AttachmentSlots.Power_Shotgun_WeaponMod2_Collectible" );
			arr.PushBack( T"AttachmentSlots.Tech_Shotgun_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Tech_Shotgun_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Smart_Shotgun_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Smart_Shotgun_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod1" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod2" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod3" );
			arr.PushBack( T"AttachmentSlots.GenericWeaponMod4" );
			return arr;
			case gamedataItemType.Wea_Chainsword:
			case gamedataItemType.Wea_Katana:
			case gamedataItemType.Wea_Sword:
			case gamedataItemType.Wea_LongBlade:
			case gamedataItemType.Wea_Machete:
			case gamedataItemType.Wea_ShortBlade:
				arr.PushBack( T"AttachmentSlots.Blade_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Blade_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Blade_WeaponMod1_Collectible" );
			arr.PushBack( T"AttachmentSlots.Blade_WeaponMod2_Collectible" );
			arr.PushBack( T"AttachmentSlots.MeleeWeaponMod1" );
			arr.PushBack( T"AttachmentSlots.MeleeWeaponMod2" );
			arr.PushBack( T"AttachmentSlots.MeleeWeaponMod3" );
			return arr;
			case gamedataItemType.Wea_Axe:
			case gamedataItemType.Wea_Knife:
				arr.PushBack( T"AttachmentSlots.Throwable_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Throwable_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Throwable_WeaponMod1_Collectible" );
			arr.PushBack( T"AttachmentSlots.Throwable_WeaponMod2_Collectible" );
			arr.PushBack( T"AttachmentSlots.MeleeWeaponMod1" );
			arr.PushBack( T"AttachmentSlots.MeleeWeaponMod2" );
			arr.PushBack( T"AttachmentSlots.MeleeWeaponMod3" );
			return arr;
			case gamedataItemType.Wea_Hammer:
			case gamedataItemType.Wea_OneHandedClub:
			case gamedataItemType.Wea_TwoHandedClub:
				arr.PushBack( T"AttachmentSlots.Blunt_WeaponMod1" );
			arr.PushBack( T"AttachmentSlots.Blunt_WeaponMod2" );
			arr.PushBack( T"AttachmentSlots.Blunt_WeaponMod1_Collectible" );
			arr.PushBack( T"AttachmentSlots.Blunt_WeaponMod2_Collectible" );
			arr.PushBack( T"AttachmentSlots.MeleeWeaponMod1" );
			arr.PushBack( T"AttachmentSlots.MeleeWeaponMod2" );
			arr.PushBack( T"AttachmentSlots.MeleeWeaponMod3" );
			return arr;
		}
		return arr;
	}

	public static function IsInventoryEmpty( object : weak< GameObject > ) : Bool
	{
		var items : array< weak< gameItemData > >;
		GameInstance.GetTransactionSystem( object.GetGame() ).GetItemList( object, items );
		return items.Size() <= 0;
	}

	public static function ProcessReadAction( choice : InteractionChoiceEvent )
	{
		var lootActionWrapper : LootChoiceActionWrapper;
		lootActionWrapper = LootChoiceActionWrapper.Unwrap( choice );
		if( LootChoiceActionWrapper.IsValid( lootActionWrapper ) )
		{
			if( lootActionWrapper.action == 'Read' )
			{
				ItemActionsHelper.ReadItem( choice.activator, lootActionWrapper.itemId );
			}
		}
	}

	public static function ToggleLootHighlight( obj : weak< GameObject >, enable : Bool )
	{
		var recordID : TweakDBID;
		recordID = T"Effectors.LootHighlightEffector";
		if( enable )
		{
			GameInstance.GetEffectorSystem( obj.GetGame() ).ApplyEffector( obj.GetEntityID(), obj, recordID );
		}
		else
		{
			GameInstance.GetEffectorSystem( obj.GetGame() ).RemoveEffector( obj.GetEntityID(), recordID );
		}
	}

	public static function CreateStatModifier( statType : gamedataStatType, modType : gameStatModifierType, value : Float ) : gameStatModifierData
	{
		var newMod : gameConstantStatModifierData;
		newMod = new gameConstantStatModifierData;
		newMod.statType = statType;
		newMod.modifierType = modType;
		newMod.value = value;
		return newMod;
	}

	public static function CreateStatModifierUsingCurve( statType : gamedataStatType, modType : gameStatModifierType, refStat : gamedataStatType, curveName : CName, columnName : CName ) : gameStatModifierData
	{
		var newMod : gameCurveStatModifierData;
		newMod = new gameCurveStatModifierData;
		newMod.statType = statType;
		newMod.curveStat = refStat;
		newMod.modifierType = modType;
		newMod.curveName = curveName;
		newMod.columnName = columnName;
		return newMod;
	}

	public static function CreateCombinedStatModifier( statType : gamedataStatType, modType : gameStatModifierType, refStat : gamedataStatType, opSymbol : gameCombinedStatOperation, value : Float, refObject : gameStatObjectsRelation ) : gameStatModifierData
	{
		var newMod : gameCombinedStatModifierData;
		newMod = new gameCombinedStatModifierData;
		newMod.statType = statType;
		newMod.modifierType = modType;
		newMod.value = value;
		newMod.refStatType = refStat;
		newMod.operation = opSymbol;
		newMod.refObject = refObject;
		return newMod;
	}

	public static function CreateCurveModifier( statRecord : CurveStatModifier_Record ) : gameStatModifierData
	{
		var newMod : gameCurveStatModifierData;
		newMod = new gameCurveStatModifierData;
		newMod.statType = statRecord.StatType().StatType();
		newMod.modifierType = ( ( gameStatModifierType )( ( ( Int32 )( EnumValueFromName( 'gameStatModifierType', statRecord.ModifierType() ) ) ) ) );
		newMod.curveName = StringToName( statRecord.Id() );
		newMod.columnName = StringToName( statRecord.Column() );
		newMod.curveStat = statRecord.RefStat().StatType();
		return newMod;
	}

	public static function StatRecordToModifier( statRecord : StatModifier_Record ) : gameStatModifierData
	{
		var statType : gamedataStatType;
		var modType : gameStatModifierType;
		var combinedOp : gameCombinedStatOperation;
		var relation : gameStatObjectsRelation;
		var value : Float;
		var curveMod : CurveStatModifier_Record;
		var constMod : ConstantStatModifier_Record;
		var combinedMod : CombinedStatModifier_Record;
		constMod = ( ( ConstantStatModifier_Record )( statRecord ) );
		curveMod = ( ( CurveStatModifier_Record )( statRecord ) );
		combinedMod = ( ( CombinedStatModifier_Record )( statRecord ) );
		if( constMod )
		{
			statType = constMod.StatType().StatType();
			modType = ( ( gameStatModifierType )( ( ( Int32 )( EnumValueFromName( 'gameStatModifierType', constMod.ModifierType() ) ) ) ) );
			value = constMod.Value();
			return CreateStatModifier( statType, modType, value );
		}
		else if( curveMod )
		{
			return CreateCurveModifier( curveMod );
		}
		else if( combinedMod )
		{
			statType = combinedMod.StatType().StatType();
			modType = ( ( gameStatModifierType )( ( ( Int32 )( EnumValueFromName( 'gameStatModifierType', combinedMod.ModifierType() ) ) ) ) );
			combinedOp = ( ( gameCombinedStatOperation )( ( ( Int32 )( EnumValueFromName( 'gameCombinedStatOperation', combinedMod.OpSymbol() ) ) ) ) );
			relation = ( ( gameStatObjectsRelation )( ( ( Int32 )( EnumValueFromName( 'gameStatObjectsRelation', combinedMod.RefObject() ) ) ) ) );
			value = constMod.Value();
			return CreateCombinedStatModifier( statType, modType, combinedMod.RefStatHandle().StatType(), combinedOp, value, relation );
		}
		else
		{
			return CreateStatModifier( gamedataStatType.Quantity, gameStatModifierType.Additive, 0.0 );
		}
	}

	public static function GetPowerLevelFromContentAssignment( gi : GameInstance, contentAssignmentID : TweakDBID ) : Float
	{
		var constantModRecord : weak< ConstantStatModifier_Record >;
		var curveModRecord : weak< CurveStatModifier_Record >;
		var contentAssignment : weak< ContentAssignment_Record >;
		contentAssignment = TweakDBInterface.GetContentAssignmentRecord( contentAssignmentID );
		if( contentAssignment )
		{
			constantModRecord = ( ( ConstantStatModifier_Record )( contentAssignment.PowerLevelMod() ) );
			curveModRecord = ( ( CurveStatModifier_Record )( contentAssignment.PowerLevelMod() ) );
			if( constantModRecord )
			{
				return constantModRecord.Value();
			}
			else if( curveModRecord )
			{
				return GameInstance.GetStatsDataSystem( gi ).GetMinValueFromCurve( StringToName( curveModRecord.Id() ), StringToName( curveModRecord.Column() ) );
			}
			else
			{
				return ( ( Float )( GameInstance.GetLevelAssignmentSystem( gi ).GetLevelAssignment( contentAssignment.GetID() ) ) );
			}
		}
		return 0.0;
	}

	public static function CheckDifficultyToStatValue( gi : GameInstance, skill : gamedataStatType, difficulty : EGameplayChallengeLevel, id : EntityID ) : Int32
	{
		var checkPowerLevel : Float;
		var entity : weak< Entity >;
		var device : weak< Device >;
		var vehicle : weak< VehicleObject >;
		entity = GameInstance.FindEntityByID( gi, id );
		device = ( ( Device )( entity ) );
		vehicle = ( ( VehicleObject )( entity ) );
		if( device )
		{
			checkPowerLevel = RPGManager.GetPowerLevelFromContentAssignment( gi, device.GetContentScale() );
		}
		if( vehicle )
		{
			checkPowerLevel = RPGManager.GetStatValueFromObject( gi, vehicle, gamedataStatType.PowerLevel );
		}
		return RPGManager.GetCheckValue( gi, checkPowerLevel, difficulty );
	}

	public static function GetCheckValue( gi : GameInstance, powerLevel : Float, difficulty : EGameplayChallengeLevel ) : Int32
	{
		var curveName : CName;
		switch( difficulty )
		{
			case EGameplayChallengeLevel.NONE:
				curveName = 'none_difficulty';
			break;
			case EGameplayChallengeLevel.EASY:
				curveName = 'easy_difficulty';
			break;
			case EGameplayChallengeLevel.MEDIUM:
				curveName = 'medium_difficulty';
			break;
			case EGameplayChallengeLevel.HARD:
				curveName = 'hard_difficulty';
			break;
			case EGameplayChallengeLevel.IMPOSSIBLE:
				curveName = 'impossible_difficulty';
			break;
		}
		return RoundMath( GameInstance.GetStatsDataSystem( gi ).GetValueFromCurve( 'attribute_checks', powerLevel, curveName ) );
	}

	public static function CheckDifficultyToPerkLevel( perk : gamedataPerkType, difficulty : EGameplayChallengeLevel, id : EntityID ) : Int32
	{
		return ( ( Int32 )( difficulty ) );
	}

	public static function GetBuildScore( player : GameObject, buildToCheck : PlayerBuild_Record ) : Int32
	{
		var attribute : gamedataStatType;
		var attributeBonus : gamedataStatType;
		var buildType : gamedataPlayerBuild;
		var statsSystem : StatsSystem;
		var attributeBaseValue : Int32;
		var attributeBonusValue : Int32;
		var maxValue : Int32;
		attributeBonus = gamedataStatType.Invalid;
		attributeBaseValue = 0;
		attributeBonusValue = 0;
		maxValue = 20;
		buildType = buildToCheck.Type();
		statsSystem = GameInstance.GetStatsSystem( player.GetGame() );
		switch( buildType )
		{
			case gamedataPlayerBuild.Netrunner:
				attribute = gamedataStatType.Intelligence;
			attributeBonus = gamedataStatType.IntelligenceSkillcheckBonus;
			break;
			case gamedataPlayerBuild.Solo:
				attribute = gamedataStatType.Strength;
			attributeBonus = gamedataStatType.StrengthSkillcheckBonus;
			break;
			case gamedataPlayerBuild.Techie:
				attribute = gamedataStatType.TechnicalAbility;
			attributeBonus = gamedataStatType.TechnicalAbilitySkillcheckBonus;
			break;
			case gamedataPlayerBuild.Reflexes:
				attribute = gamedataStatType.Reflexes;
			break;
			case gamedataPlayerBuild.Cool:
				attribute = gamedataStatType.Cool;
			break;
		}
		attributeBaseValue = ( ( Int32 )( statsSystem.GetStatValue( player.GetEntityID(), attribute ) ) );
		if( attributeBonus != gamedataStatType.Invalid )
		{
			attributeBonusValue = ( ( Int32 )( statsSystem.GetStatValue( player.GetEntityID(), attributeBonus ) ) );
		}
		return Min( attributeBaseValue + attributeBonusValue, maxValue );
	}

	public static function GetBluelineBuildCheckValue( player : GameObject, contentAssignment : ContentAssignment_Record, difficulty : EGameplayChallengeLevel ) : Int32
	{
		var checkPowerLevel : Float;
		checkPowerLevel = RPGManager.GetPowerLevelFromContentAssignment( player.GetGame(), contentAssignment.GetID() );
		return RPGManager.GetCheckValue( player.GetGame(), checkPowerLevel, difficulty );
	}

	public static function GetBluelinePaymentValue( player : GameObject, contentAssignment : ContentAssignment_Record, difficulty : EGameplayChallengeLevel ) : Int32
	{
		var paymentPowerLevel : Float;
		var scaledPaymentValue : Float;
		var playerMoney : Int32;
		var base : Float;
		var quotient : Float;
		var digitCount : Int32;
		var overrideValue : Int32;
		var upToAmountCheck : Bool;
		paymentPowerLevel = RPGManager.GetPowerLevelFromContentAssignment( player.GetGame(), contentAssignment.GetID() );
		scaledPaymentValue = GameInstance.GetStatsDataSystem( player.GetGame() ).GetValueFromCurve( 'price_curves', paymentPowerLevel, 'power_level_to_payment_check' );
		switch( difficulty )
		{
			case EGameplayChallengeLevel.NONE:
				scaledPaymentValue = 1.0;
			break;
			case EGameplayChallengeLevel.EASY:
				scaledPaymentValue *= 0.25;
			break;
			case EGameplayChallengeLevel.HARD:
				scaledPaymentValue *= 2.0;
			break;
			case EGameplayChallengeLevel.IMPOSSIBLE:
				scaledPaymentValue *= 10.0;
			break;
			default:
				break;
		}
		quotient = scaledPaymentValue;
		while( ( quotient > 1.0 ) && ( digitCount < 10 ) )
		{
			digitCount += 1;
			base = PowF( 10.0, ( ( Float )( digitCount ) ) );
			quotient = scaledPaymentValue / base;
		}
		base = PowF( 10.0, ( ( Float )( CeilF( ( ( Float )( digitCount ) ) / 2.0 ) ) ) );
		scaledPaymentValue /= base;
		scaledPaymentValue = ( ( Float )( RoundMath( scaledPaymentValue ) ) );
		scaledPaymentValue *= base;
		overrideValue = TweakDBInterface.GetInt( contentAssignment.GetID() + T".overrideValue", 0 );
		if( overrideValue > 0 )
		{
			scaledPaymentValue = ( ( Float )( overrideValue ) );
		}
		upToAmountCheck = TweakDBInterface.GetBool( contentAssignment.GetID() + T".upToCheck", false );
		if( upToAmountCheck )
		{
			playerMoney = GameInstance.GetTransactionSystem( player.GetGame() ).GetItemQuantity( player, MarketSystem.Money() );
			if( playerMoney < ( ( Int32 )( scaledPaymentValue ) ) )
			{
				scaledPaymentValue = ( ( Float )( playerMoney ) );
			}
		}
		return RoundF( scaledPaymentValue );
	}

	public static function GetStatRecord( type : gamedataStatType ) : Stat_Record
	{
		return TweakDBInterface.GetStatRecord( TDBID.Create( "BaseStats." + EnumValueToString( "gamedataStatType", ( ( Int32 )( type ) ) ) ) );
	}

	public static function GetProficiencyRecord( type : gamedataProficiencyType ) : Proficiency_Record
	{
		return TweakDBInterface.GetProficiencyRecord( TDBID.Create( "Proficiencies." + EnumValueToString( "gamedataProficiencyType", ( ( Int32 )( type ) ) ) ) );
	}

	public static function GetTraitRecord( type : gamedataTraitType ) : Trait_Record
	{
		return TweakDBInterface.GetTraitRecord( TDBID.Create( "Traits." + EnumValueToString( "gamedataTraitType", ( ( Int32 )( type ) ) ) ) );
	}

	public static function GetAttributeDataRecord( type : gamedataAttributeDataType ) : AttributeData_Record
	{
		return TweakDBInterface.GetAttributeDataRecord( TDBID.Create( "NewPerks." + EnumValueToString( "gamedataAttributeDataType", ( ( Int32 )( type ) ) ) ) );
	}

	public static function GetNewPerkRecord( type : gamedataNewPerkType ) : NewPerk_Record
	{
		return TweakDBInterface.GetNewPerkRecord( TDBID.Create( "NewPerks." + EnumValueToString( "gamedataNewPerkType", ( ( Int32 )( type ) ) ) ) );
	}

	public static function GetResistanceTypeFromDamageType( damageType : gamedataDamageType ) : gamedataStatType
	{
		switch( damageType )
		{
			case gamedataDamageType.Physical:
				return gamedataStatType.PhysicalResistance;
			case gamedataDamageType.Thermal:
				return gamedataStatType.ThermalResistance;
			case gamedataDamageType.Chemical:
				return gamedataStatType.ChemicalResistance;
			case gamedataDamageType.Electric:
				return gamedataStatType.ElectricResistance;
			default:
				return gamedataStatType.Invalid;
		}
	}

	public static function CalculatePowerDifferential( level : Int32 ) : gameEPowerDifferential
	{
		if( level <= ( ( Int32 )( gameEPowerDifferential.IMPOSSIBLE ) ) )
		{
			return gameEPowerDifferential.IMPOSSIBLE;
		}
		else if( ( level > ( ( Int32 )( gameEPowerDifferential.IMPOSSIBLE ) ) ) && ( level <= ( ( Int32 )( gameEPowerDifferential.HARD ) ) ) )
		{
			return gameEPowerDifferential.HARD;
		}
		else if( ( level > ( ( Int32 )( gameEPowerDifferential.HARD ) ) ) && ( level <= ( ( Int32 )( gameEPowerDifferential.NORMAL ) ) ) )
		{
			return gameEPowerDifferential.NORMAL;
		}
		else if( ( level > ( ( Int32 )( gameEPowerDifferential.NORMAL ) ) ) && ( level <= ( ( Int32 )( gameEPowerDifferential.EASY ) ) ) )
		{
			return gameEPowerDifferential.EASY;
		}
		else
		{
			return gameEPowerDifferential.TRASH;
		}
	}

	public static function CalculateThreatValue( obj : GameObject ) : Float
	{
		var threatVal : Float;
		var statsSystem : StatsSystem;
		var player : StatsObjectID;
		var npc : StatsObjectID;
		var npcPowerLevel : Float;
		var playerPowerLevel : Float;
		var normPowerLevelDiff : Float;
		var maxPowerLevel : Float;
		var minPowerLevel : Float;
		statsSystem = GameInstance.GetStatsSystem( obj.GetGame() );
		player = GetPlayer( obj.GetGame() ).GetEntityID();
		npc = obj.GetEntityID();
		threatVal = 0.0;
		maxPowerLevel = 60.0;
		minPowerLevel = 0.0;
		npcPowerLevel = statsSystem.GetStatValue( npc, gamedataStatType.PowerLevel );
		playerPowerLevel = statsSystem.GetStatValue( player, gamedataStatType.PowerLevel );
		normPowerLevelDiff = MathHelper.NormalizeF( npcPowerLevel - playerPowerLevel, maxPowerLevel - minPowerLevel, minPowerLevel - maxPowerLevel );
		threatVal = normPowerLevelDiff;
		return threatVal;
	}

	public static function GetScannerResistanceDetails( obj : GameObject, statType : gamedataStatType, optional player : GameObject ) : ScannerStatDetails
	{
		var currentResist : Float;
		var statsSystem : StatsSystem;
		var scanStatDetails : ScannerStatDetails;
		var powerLevelDiff : Float;
		var executorLevel : Float;
		var targetLevel : Float;
		var extraCost : Float;
		statsSystem = GameInstance.GetStatsSystem( obj.GetGame() );
		currentResist = statsSystem.GetStatValue( obj.GetEntityID(), statType );
		if( statType == gamedataStatType.HackingResistance && player )
		{
			scanStatDetails.baseValue = currentResist;
			executorLevel = statsSystem.GetStatValue( player.GetEntityID(), gamedataStatType.PowerLevel );
			targetLevel = statsSystem.GetStatValue( obj.GetEntityID(), gamedataStatType.PowerLevel );
			powerLevelDiff = ( ( Float )( RoundMath( executorLevel ) - RoundF( targetLevel ) ) );
			extraCost = GameInstance.GetStatsDataSystem( player.GetGame() ).GetValueFromCurve( 'puppet_dynamic_scaling', powerLevelDiff, 'pl_diff_to_memory_cost_modifier' );
			currentResist += extraCost;
		}
		scanStatDetails.statType = statType;
		scanStatDetails.value = currentResist;
		return scanStatDetails;
	}

	public static function GetCharacterWeakspotCount( puppet : gamePuppet ) : Int32
	{
		var weakspots : array< weak< Weakspot_Record > >;
		TweakDBInterface.GetCharacterRecord( puppet.GetRecordID() ).Weakspots( weakspots );
		return weakspots.Size();
	}

	public static function GetStatValues( obj : GameObject, const stats : ref< array< gamedataStatType > > ) : array< gameStatTotalValue >
	{
		var statsSystem : StatsSystem;
		var statInfos : array< gameStatTotalValue >;
		var statInfo : gameStatTotalValue;
		var i : Int32;
		statsSystem = GameInstance.GetStatsSystem( obj.GetGame() );
		for( i = 0; i < stats.Size(); i += 1 )
		{
			statInfo.value = statsSystem.GetStatValue( obj.GetEntityID(), stats[ i ] );
			statInfo.statType = stats[ i ];
			statInfos.PushBack( statInfo );
		}
		return statInfos;
	}

	public static function GetMinStats( obj : GameObject, const stats : ref< array< gamedataStatType > > ) : array< gameStatTotalValue >
	{
		var i : Int32;
		var statInfos : array< gameStatTotalValue >;
		var minStats : array< gameStatTotalValue >;
		var minValue : Float;
		statInfos = GetStatValues( obj, stats );
		minValue = MathHelper.PositiveInfinity();
		for( i = 0; i < statInfos.Size(); i += 1 )
		{
			if( statInfos[ i ].value < minValue )
			{
				minValue = statInfos[ i ].value;
				minStats.Clear();
				minStats.PushBack( statInfos[ i ] );
			}
			else if( statInfos[ i ].value == minValue )
			{
				minStats.PushBack( statInfos[ i ] );
			}
		}
		return minStats;
	}

	public static function GetMaxStats( obj : GameObject, const stats : ref< array< gamedataStatType > > ) : array< gameStatTotalValue >
	{
		var i : Int32;
		var statInfos : array< gameStatTotalValue >;
		var maxStats : array< gameStatTotalValue >;
		var maxValue : Float;
		statInfos = GetStatValues( obj, stats );
		maxValue = MathHelper.NegativeInfinity();
		for( i = 0; i < statInfos.Size(); i += 1 )
		{
			if( statInfos[ i ].value > maxValue )
			{
				maxValue = statInfos[ i ].value;
				maxStats.Clear();
				maxStats.PushBack( statInfos[ i ] );
			}
			else if( statInfos[ i ].value == maxValue )
			{
				maxStats.PushBack( statInfos[ i ] );
			}
		}
		return maxStats;
	}

	public static function GetLowestResistances( obj : GameObject ) : array< gameStatTotalValue >
	{
		return GetMinStats( obj, ResistancesList() );
	}

	public static function GetHighestResistances( obj : GameObject ) : array< gameStatTotalValue >
	{
		return GetMaxStats( obj, ResistancesList() );
	}

	public static function CanPlayerCraftFromInventory( obj : weak< GameObject > ) : Bool
	{
		var val : Float;
		val = GameInstance.GetStatsSystem( obj.GetGame() ).GetStatValue( obj.GetEntityID(), gamedataStatType.CanCraftFromInventory );
		return val > 0.0;
	}

	public static function CanPlayerUpgradeFromInventory( obj : weak< GameObject > ) : Bool
	{
		var val : Float;
		val = GameInstance.GetStatsSystem( obj.GetGame() ).GetStatValue( obj.GetEntityID(), gamedataStatType.CanUpgradeFromInventory );
		return val > 0.0;
	}

	public static function AwardExperienceFromDamage( hitEvent : gameHitEvent, damagePercentage : Float )
	{
		var attackData : AttackData;
		var queueExpRequests : array< QueueCombatExperience >;
		var queueExpRequest : QueueCombatExperience;
		var inst : GameInstance;
		var expAwarded : Bool;
		var playerDevSystem : PlayerDevelopmentSystem;
		var i : Int32;
		var targetPuppet : weak< ScriptedPuppet >;
		var targetPowerLevel : Float;
		var curveSetName : CName;
		var weaponRecord : weak< Item_Record >;
		var playerXPmultiplier : Float;
		var temp : Float;
		var additionalProf : gamedataProficiencyType;
		var player : weak< PlayerPuppet >;
		var attackRecord : weak< Attack_Record >;
		attackData = hitEvent.attackData;
		curveSetName = 'activity_to_proficiency_xp';
		additionalProf = gamedataProficiencyType.Invalid;
		inst = hitEvent.target.GetGame();
		playerDevSystem = ( ( PlayerDevelopmentSystem )( GameInstance.GetScriptableSystemsContainer( inst ).Get( 'PlayerDevelopmentSystem' ) ) );
		expAwarded = true;
		targetPuppet = ( ( ScriptedPuppet )( hitEvent.target ) );
		if( ( ( ( !( targetPuppet ) || !( targetPuppet.IsActive() ) ) || !( targetPuppet.AwardsExperience() ) ) || !( attackData.GetInstigator().IsPlayer() ) ) || hitEvent.target.IsPlayer() )
		{
			return;
		}
		targetPowerLevel = GameInstance.GetStatsSystem( inst ).GetStatValue( targetPuppet.GetEntityID(), gamedataStatType.PowerLevel );
		playerXPmultiplier = GameInstance.GetStatsSystem( inst ).GetStatValue( GetPlayer( inst ).GetEntityID(), gamedataStatType.XPbonusMultiplier );
		queueExpRequest = new QueueCombatExperience;
		weaponRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( attackData.GetWeapon().GetItemID() ) );
		switch( weaponRecord.ItemType().Type() )
		{
			case gamedataItemType.Wea_TwoHandedClub:
			case gamedataItemType.Wea_Hammer:
			case gamedataItemType.Wea_OneHandedClub:
			case gamedataItemType.Wea_Melee:
			case gamedataItemType.Wea_Fists:
			case gamedataItemType.Cyb_StrongArms:
				queueExpRequest.m_experienceType = gamedataProficiencyType.StrengthSkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' );
			break;
			case gamedataItemType.Wea_Shotgun:
			case gamedataItemType.Wea_ShotgunDual:
			case gamedataItemType.Wea_HeavyMachineGun:
			case gamedataItemType.Wea_LightMachineGun:
				temp = ( ( weaponRecord.TagsContains( 'PowerWeapon' ) ) ? ( 1.0 ) : ( 0.5 ) );
			queueExpRequest.m_experienceType = gamedataProficiencyType.StrengthSkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' ) * temp;
			break;
			case gamedataItemType.Wea_Rifle:
			case gamedataItemType.Wea_AssaultRifle:
			case gamedataItemType.Wea_SubmachineGun:
				temp = ( ( weaponRecord.TagsContains( 'PowerWeapon' ) ) ? ( 1.0 ) : ( 0.5 ) );
			queueExpRequest.m_experienceType = gamedataProficiencyType.ReflexesSkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' ) * temp;
			break;
			case gamedataItemType.Wea_Katana:
			case gamedataItemType.Wea_Sword:
			case gamedataItemType.Wea_LongBlade:
			case gamedataItemType.Wea_ShortBlade:
			case gamedataItemType.Wea_Chainsword:
			case gamedataItemType.Wea_Machete:
			case gamedataItemType.Cyb_MantisBlades:
				queueExpRequest.m_experienceType = gamedataProficiencyType.ReflexesSkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' );
			break;
			case gamedataItemType.Cyb_NanoWires:
				queueExpRequest.m_experienceType = gamedataProficiencyType.IntelligenceSkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' );
			break;
			case gamedataItemType.Wea_Handgun:
			case gamedataItemType.Wea_Revolver:
			case gamedataItemType.Wea_SniperRifle:
			case gamedataItemType.Wea_PrecisionRifle:
				temp = ( ( weaponRecord.TagsContains( 'PowerWeapon' ) ) ? ( 1.0 ) : ( 0.5 ) );
			queueExpRequest.m_experienceType = gamedataProficiencyType.CoolSkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' ) * temp;
			break;
			case gamedataItemType.Wea_Knife:
			case gamedataItemType.Wea_Axe:
				queueExpRequest.m_experienceType = gamedataProficiencyType.CoolSkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' );
			break;
			default:
				expAwarded = false;
			break;
		}
		if( ( attackData.HasFlag( hitFlag.WeakspotHit ) || attackData.HasFlag( hitFlag.Headshot ) ) || attackData.HasFlag( hitFlag.FinisherTriggered ) )
		{
			queueExpRequest.m_amount *= 1.10000002;
		}
		if( attackData.HasFlag( hitFlag.PerfectlyCharged ) )
		{
			queueExpRequest.m_amount *= 1.10000002;
		}
		if( attackData.HasFlag( hitFlag.BodyPerksMeleeAttack ) )
		{
			queueExpRequest.m_amount *= 1.10000002;
		}
		if( expAwarded )
		{
			if( StatusEffectSystem.ObjectHasStatusEffectWithTag( attackData.GetInstigator(), 'TrainingGuns' ) && attackData.GetWeapon().IsRanged() )
			{
				queueExpRequest.m_amount *= 2.0;
			}
			if( StatusEffectSystem.ObjectHasStatusEffectWithTag( attackData.GetInstigator(), 'TrainingMelee' ) && attackData.GetWeapon().IsMelee() )
			{
				queueExpRequest.m_amount *= 2.0;
			}
			queueExpRequests.PushBack( queueExpRequest );
			if( weaponRecord.TagsContains( 'SmartWeapon' ) )
			{
				additionalProf = gamedataProficiencyType.IntelligenceSkill;
			}
			else if( weaponRecord.TagsContains( 'TechWeapon' ) )
			{
				additionalProf = gamedataProficiencyType.TechnicalAbilitySkill;
			}
			if( additionalProf != gamedataProficiencyType.Invalid )
			{
				temp = queueExpRequest.m_amount;
				queueExpRequest = new QueueCombatExperience;
				queueExpRequest.m_experienceType = additionalProf;
				queueExpRequest.m_amount = temp;
				queueExpRequests.PushBack( queueExpRequest );
			}
		}
		if( attackData.GetAttackType() == gamedataAttackType.Hack )
		{
			queueExpRequest = new QueueCombatExperience;
			queueExpRequest.m_experienceType = gamedataProficiencyType.IntelligenceSkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' );
			queueExpRequests.PushBack( queueExpRequest );
		}
		attackRecord = ( ( Attack_GameEffect_Record )( attackData.GetAttackDefinition().GetRecord() ) );
		if( ( ( ( BaseGrenade )( attackData.GetSource() ) ) || ( ( ProjectileLauncherRound )( attackData.GetSource() ) ) ) || attackRecord.HitFlagsContains( "Grenade" ) )
		{
			queueExpRequest = new QueueCombatExperience;
			queueExpRequest.m_experienceType = gamedataProficiencyType.TechnicalAbilitySkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' );
			queueExpRequests.PushBack( queueExpRequest );
		}
		if( hitEvent.attackData.HasFlag( hitFlag.StealthHit ) )
		{
			queueExpRequest = new QueueCombatExperience;
			queueExpRequest.m_experienceType = gamedataProficiencyType.CoolSkill;
			queueExpRequest.m_amount = GameInstance.GetStatsDataSystem( inst ).GetValueFromCurve( curveSetName, targetPowerLevel, 'damage_to_skill_xp' ) * 0.69999999;
			queueExpRequests.PushBack( queueExpRequest );
		}
		player = GetPlayer( inst );
		for( i = 0; i < queueExpRequests.Size(); i += 1 )
		{
			queueExpRequest = queueExpRequests[ i ];
			queueExpRequest.owner = player;
			queueExpRequest.m_amount *= RPGManager.GetRarityMultiplier( ( ( NPCPuppet )( hitEvent.target ) ), 'power_level_to_dmg_xp_mult' );
			queueExpRequest.m_amount *= damagePercentage;
			queueExpRequest.m_amount *= playerXPmultiplier;
			queueExpRequest.m_entity = targetPuppet.GetEntityID();
			playerDevSystem.QueueRequest( queueExpRequest );
		}
		if( ( ( ( ( ( attackData.HasFlag( hitFlag.WasKillingBlow ) || attackData.HasFlag( hitFlag.FinisherTriggered ) ) && StatusEffectSystem.ObjectHasStatusEffect( attackData.GetInstigator(), T"BaseStatusEffect.Intelligence_Central_Milestone_3_Overclock_Buff" ) ) || StatusEffectSystem.ObjectHasStatusEffect( attackData.GetInstigator(), T"BaseStatusEffect.Tech_Master_Perk_3_Buff" ) ) || StatusEffectSystem.ObjectHasStatusEffectOfType( attackData.GetInstigator(), gamedataStatusEffectType.Berserk ) ) || StatusEffectSystem.ObjectHasStatusEffectOfType( attackData.GetInstigator(), gamedataStatusEffectType.Sandevistan ) ) || StatusEffectSystem.ObjectHasStatusEffectWithTag( attackData.GetInstigator(), 'CamoActiveOnPlayer' ) )
		{
			queueExpRequest = new QueueCombatExperience;
			queueExpRequest.owner = player;
			queueExpRequest.m_experienceType = gamedataProficiencyType.TechnicalAbilitySkill;
			queueExpRequest.m_amount = 20.0 * playerXPmultiplier;
			queueExpRequest.m_entity = targetPuppet.GetEntityID();
			playerDevSystem.QueueRequest( queueExpRequest );
		}
	}

	public static function AwardExperienceFromDeflect( hitEvent : gameHitEvent )
	{
		var queueExpRequest : QueueCombatExperience;
		var blockingItem : weak< WeaponObject >;
		var playerXPmultiplier : Float;
		var gameInstance : GameInstance;
		var playerDevSystem : PlayerDevelopmentSystem;
		var statPoolsSystem : StatPoolsSystem;
		var player : weak< PlayerPuppet >;
		var damage : Float;
		var health, healthPercent : Float;
		var parryEffect : weak< StatusEffect >;
		player = ( ( PlayerPuppet )( hitEvent.target ) );
		if( !( player ) )
		{
			return;
		}
		gameInstance = player.GetGame();
		blockingItem = ( ( WeaponObject )( GameInstance.GetTransactionSystem( gameInstance ).GetItemInSlot( player, T"AttachmentSlots.WeaponRight" ) ) );
		if( !( blockingItem ) )
		{
			return;
		}
		statPoolsSystem = GameInstance.GetStatPoolsSystem( gameInstance );
		damage = hitEvent.attackComputed.GetTotalAttackValue( gamedataStatPoolType.Health );
		healthPercent = statPoolsSystem.GetStatPoolValue( player.GetEntityID(), gamedataStatPoolType.Health );
		health = statPoolsSystem.ToPoints( player.GetEntityID(), gamedataStatPoolType.Health, healthPercent );
		damage = ( damage * healthPercent ) / health;
		parryEffect = StatusEffectHelper.GetStatusEffectByID( player, T"BaseStatusEffect.ParryExpHarvested" );
		if( ( damage <= 0.0 ) || ( parryEffect && ( parryEffect.GetMaxStacks() <= parryEffect.GetStackCount() ) ) )
		{
			return;
		}
		playerDevSystem = ( ( PlayerDevelopmentSystem )( GameInstance.GetScriptableSystemsContainer( gameInstance ).Get( 'PlayerDevelopmentSystem' ) ) );
		playerXPmultiplier = GameInstance.GetStatsSystem( gameInstance ).GetStatValue( player.GetEntityID(), gamedataStatType.XPbonusMultiplier );
		queueExpRequest = new QueueCombatExperience;
		queueExpRequest.owner = player;
		queueExpRequest.m_entity = hitEvent.attackData.GetInstigator().GetEntityID();
		queueExpRequest.m_amount = ( damage * playerXPmultiplier ) * 0.75;
		if( blockingItem.IsBlunt() )
		{
			queueExpRequest.m_experienceType = gamedataProficiencyType.StrengthSkill;
			playerDevSystem.QueueRequest( queueExpRequest );
		}
		else if( blockingItem.IsBlade() )
		{
			queueExpRequest.m_experienceType = gamedataProficiencyType.ReflexesSkill;
			playerDevSystem.QueueRequest( queueExpRequest );
		}
		StatusEffectHelper.ApplyStatusEffect( hitEvent.attackData.GetInstigator(), T"BaseStatusEffect.ParryExpHarvested", player.GetEntityID() );
	}

	public static function AwardExperienceFromLocomotion( player : weak< PlayerPuppet >, amount : Float )
	{
		var queueExpRequest : AddExperience;
		var playerDevSystem : PlayerDevelopmentSystem;
		var statsSystem : StatsSystem;
		var cachedExp : Int32;
		var statMod : gameConstantStatModifierData;
		var statusEffect : StatusEffect;
		var gameInstance : GameInstance;
		var playerXPmultiplier : Float;
		var stat : gamedataStatType;
		stat = gamedataStatType.LocomotionExperienceReward;
		if( !( player ) || ( amount <= 0.0 ) )
		{
			return;
		}
		statusEffect = StatusEffectHelper.GetStatusEffectByID( player, T"BaseStatusEffect.LocomotionExpHarvested" );
		if( statusEffect && ( statusEffect.GetMaxStacks() <= statusEffect.GetStackCount() ) )
		{
			return;
		}
		gameInstance = player.GetGame();
		playerDevSystem = ( ( PlayerDevelopmentSystem )( GameInstance.GetScriptableSystemsContainer( gameInstance ).Get( 'PlayerDevelopmentSystem' ) ) );
		statsSystem = GameInstance.GetStatsSystem( gameInstance );
		playerXPmultiplier = GameInstance.GetStatsSystem( gameInstance ).GetStatValue( player.GetEntityID(), gamedataStatType.XPbonusMultiplier );
		statMod = ( ( gameConstantStatModifierData )( RPGManager.CreateStatModifier( stat, gameStatModifierType.Additive, amount * playerXPmultiplier ) ) );
		statsSystem.AddModifier( player.GetEntityID(), statMod );
		cachedExp = ( ( Int32 )( statsSystem.GetStatValue( player.GetEntityID(), stat ) ) );
		if( cachedExp >= 100 )
		{
			queueExpRequest = new AddExperience;
			queueExpRequest.Set( player, cachedExp, gamedataProficiencyType.ReflexesSkill, false );
			playerDevSystem.QueueRequest( queueExpRequest );
			statMod = ( ( gameConstantStatModifierData )( RPGManager.CreateStatModifier( stat, gameStatModifierType.Additive, ( ( Float )( cachedExp ) ) * -1.0 ) ) );
			statsSystem.AddModifier( player.GetEntityID(), statMod );
		}
		StatusEffectHelper.ApplyStatusEffect( player, T"BaseStatusEffect.LocomotionExpHarvested", player.GetEntityID() );
	}

	public static function AwardExperienceFromResourceSpent( player : weak< PlayerPuppet >, value : Float, type : gamedataStatPoolType, optional hitEvent : gameHitEvent )
	{
		var queueCombatExpRequest : QueueCombatExperience;
		var playerXPmultiplier : Float;
		var gameInstance : GameInstance;
		var playerDevSystem : PlayerDevelopmentSystem;
		if( !( player ) )
		{
			return;
		}
		gameInstance = player.GetGame();
		playerDevSystem = ( ( PlayerDevelopmentSystem )( GameInstance.GetScriptableSystemsContainer( gameInstance ).Get( 'PlayerDevelopmentSystem' ) ) );
		playerXPmultiplier = GameInstance.GetStatsSystem( gameInstance ).GetStatValue( player.GetEntityID(), gamedataStatType.XPbonusMultiplier );
		if( ( ( type == gamedataStatPoolType.Overshield && hitEvent ) && !( hitEvent.attackData.GetInstigator().IsPlayer() ) ) && hitEvent.target.IsPlayer() )
		{
			queueCombatExpRequest = new QueueCombatExperience;
			queueCombatExpRequest.owner = player;
			queueCombatExpRequest.m_experienceType = gamedataProficiencyType.StrengthSkill;
			queueCombatExpRequest.m_amount = ( value * playerXPmultiplier ) * 0.25;
			queueCombatExpRequest.m_entity = hitEvent.attackData.GetInstigator().GetEntityID();
			playerDevSystem.QueueRequest( queueCombatExpRequest );
		}
	}

	public static function AwardExperienceFromQuickhack( player : weak< PlayerPuppet >, cost : Float, target : EntityID, category : gamedataHackCategory )
	{
		var playerXPmultiplier : Float;
		var playerDevSystem : PlayerDevelopmentSystem;
		var queueExpRequest : AddExperience;
		var gameInstance : GameInstance;
		var multiplier : Float;
		var targetPowerLevel : Float;
		var statusEffect : weak< StatusEffect >;
		var targetObj : weak< GameObject >;
		multiplier = 1.0;
		if( ( !( player ) || !( EntityID.IsDefined( target ) ) ) || category == gamedataHackCategory.NotAHack )
		{
			return;
		}
		targetObj = ( ( GameObject )( GameInstance.FindEntityByID( gameInstance, target ) ) );
		statusEffect = StatusEffectHelper.GetStatusEffectByID( targetObj, T"BaseStatusEffect.QuickhackExpHarvested" );
		if( statusEffect && ( statusEffect.GetMaxStacks() <= statusEffect.GetStackCount() ) )
		{
			return;
		}
		gameInstance = player.GetGame();
		playerDevSystem = ( ( PlayerDevelopmentSystem )( GameInstance.GetScriptableSystemsContainer( gameInstance ).Get( 'PlayerDevelopmentSystem' ) ) );
		playerXPmultiplier = GameInstance.GetStatsSystem( gameInstance ).GetStatValue( player.GetEntityID(), gamedataStatType.XPbonusMultiplier );
		targetPowerLevel = GameInstance.GetStatsSystem( gameInstance ).GetStatValue( target, gamedataStatType.PowerLevel );
		if( GameInstance.GetStatsSystem( gameInstance ).GetStatValue( target, gamedataStatType.WasQuickHacked ) < 1.0 )
		{
			multiplier = 20.0;
		}
		if( category == gamedataHackCategory.DeviceHack )
		{
			multiplier *= 0.30000001;
		}
		queueExpRequest = new AddExperience;
		queueExpRequest.owner = player;
		queueExpRequest.m_experienceType = gamedataProficiencyType.IntelligenceSkill;
		queueExpRequest.m_amount = ( ( Int32 )( ( ( ( ( cost * 0.69999999 ) + ( targetPowerLevel * 0.2 ) ) * playerXPmultiplier ) * multiplier ) * 0.5 ) );
		playerDevSystem.QueueRequest( queueExpRequest );
		StatusEffectHelper.ApplyStatusEffect( targetObj, T"BaseStatusEffect.QuickhackExpHarvested", player.GetEntityID() );
	}

	public static function AwardExperienceInstantly( player : weak< PlayerPuppet >, amount : Int32, type : gamedataProficiencyType )
	{
		var queueExpRequest : AddExperience;
		var playerDevSystem : PlayerDevelopmentSystem;
		queueExpRequest = new AddExperience;
		if( !( player ) || ( amount <= 0 ) )
		{
			return;
		}
		playerDevSystem = ( ( PlayerDevelopmentSystem )( GameInstance.GetScriptableSystemsContainer( player.GetGame() ).Get( 'PlayerDevelopmentSystem' ) ) );
		queueExpRequest = new AddExperience;
		queueExpRequest.Set( player, amount, type, false );
		playerDevSystem.QueueRequest( queueExpRequest );
	}

	public static function CalculateMinorActivityReward( gi : GameInstance, experienceValue : Float ) : Float
	{
		var maPrologueMultiplier : Float;
		var maDynamicMultiplier : Float;
		var maCount : Int32;
		var factVal : Int32;
		var experienceReward : Float;
		var maxExperience : Int32;
		var maAmount : Int32;
		var baseReward : Int32;
		maDynamicMultiplier = 1.0;
		maPrologueMultiplier = TDB.GetFloat( T"Constants.RPGManager.maPrologueMultiplier" );
		maxExperience = TDB.GetInt( T"Constants.RPGManager.maDesiredExperience" );
		maAmount = TDB.GetInt( T"Constants.RPGManager.maAmount" );
		baseReward = TDB.GetInt( T"Constants.RPGManager.maBaseReward" );
		if( GetFact( gi, 'q005_done' ) <= 0 )
		{
			experienceReward = experienceValue * maPrologueMultiplier;
			factVal = GetFact( gi, 'prologue_ma_done' );
			SetFactValue( gi, 'prologue_ma_done', factVal + 1 );
			SetFactValue( gi, 'maExperienceSum', GetFact( gi, 'maExperienceSum' ) + ( ( Int32 )( experienceReward ) ) );
		}
		else
		{
			maCount = GetFact( gi, 'prologue_ma_done' );
			if( ( maAmount - maCount ) > 0 )
			{
				maDynamicMultiplier = ( ( ( ( Float )( maxExperience ) ) - ( ( Float )( GetFact( gi, 'maExperienceSum' ) ) ) ) / ( ( ( Float )( maAmount ) ) - ( ( Float )( maCount ) ) ) ) / ( ( Float )( baseReward ) );
			}
			experienceReward = experienceValue * maDynamicMultiplier;
		}
		return experienceReward;
	}

	public static function CalculateStreetStoryReward( gi : GameInstance, experienceValue : Float ) : Float
	{
		var stsPrologueMultiplier : Float;
		var stsDynamicMultiplier : Float;
		var stsCount : Int32;
		var factVal : Int32;
		var experienceReward : Float;
		var maxExperience : Int32;
		var stsAmount : Int32;
		var baseReward : Int32;
		stsDynamicMultiplier = 1.0;
		stsPrologueMultiplier = TDB.GetFloat( T"Constants.RPGManager.stsPrologueMultiplier" );
		maxExperience = TDB.GetInt( T"Constants.RPGManager.stsDesiredExperience" );
		stsAmount = TDB.GetInt( T"Constants.RPGManager.stsAmount" );
		baseReward = TDB.GetInt( T"Constants.RPGManager.stsBaseReward" );
		if( GetFact( gi, 'q005_done' ) <= 0 )
		{
			experienceReward = experienceValue * stsPrologueMultiplier;
			factVal = GetFact( gi, 'prologue_sts_done' );
			SetFactValue( gi, 'prologue_sts_done', factVal + 1 );
			SetFactValue( gi, 'stsExperienceSum', GetFact( gi, 'stsExperienceSum' ) + ( ( Int32 )( experienceReward ) ) );
		}
		else
		{
			stsCount = GetFact( gi, 'prologue_sts_done' );
			if( ( stsAmount - stsCount ) > 0 )
			{
				stsDynamicMultiplier = ( ( ( ( Float )( maxExperience ) ) - ( ( Float )( GetFact( gi, 'stsExperienceSum' ) ) ) ) / ( ( ( Float )( stsAmount ) ) - ( ( Float )( stsCount ) ) ) ) / ( ( Float )( baseReward ) );
			}
			experienceReward = experienceValue * stsDynamicMultiplier;
		}
		return experienceReward;
	}

	public static function CalculateEP1Reward( gi : GameInstance, experienceValue : Float, playerLevel : Float ) : Float
	{
		var experienceReward : Float;
		var statsDataSystem : StatsDataSystem;
		var levelMult : Float;
		statsDataSystem = GameInstance.GetStatsDataSystem( gi );
		levelMult = statsDataSystem.GetValueFromCurve( 'player_level_to_xp_multiplier', playerLevel, 'player_level_to_xp_mult' );
		experienceReward = experienceValue * levelMult;
		return experienceReward;
	}

	public static function GiveReward( gi : GameInstance, rewardID : TweakDBID, optional target : StatsObjectID, optional moneyMultiplier : Float )
	{
		GiveReward( gi, rewardID, 1, target, moneyMultiplier );
	}

	public static function GiveReward( gi : GameInstance, rewardID : TweakDBID, amount : Int32, optional target : StatsObjectID, optional moneyMultiplier : Float )
	{
		var i : Int32;
		var quantity : Int32;
		var rewardName : String;
		var rewardRecord : RewardBase_Record;
		var itemArr : array< weak< InventoryItem_Record > >;
		var expArr : array< weak< XPPoints_Record > >;
		var currencyArr : array< weak< CurrencyReward_Record > >;
		var quantityMods : array< weak< StatModifier_Record > >;
		var recipesArr : array< weak< Item_Record > >;
		var achievementsArr : array< weak< Achievement_Record > >;
		var photoModeItmsArr : array< weak< PhotoModeItem_Record > >;
		var expEvt : ExperiencePointsEvent;
		var transSys : TransactionSystem;
		var visualizer : DebugVisualizerSystem;
		var statsDataSystem : StatsDataSystem;
		var expType : gamedataProficiencyType;
		var player : PlayerPuppet;
		var addRecipeRequest : AddRecipeRequest;
		var craftingSystem : CraftingSystem;
		var NCPDJobDone : NCPDJobDoneEvent;
		var itemID : ItemID;
		var powerDiff : gameEPowerDifferential;
		var contentAssignment : TweakDBID;
		var experienceValue : Float;
		var playerLevel : Float;
		var playerXPmultiplier : Float;
		var streetCredXPmultiplier : Float;
		var contentLevel : Int32;
		var levelDiff : Int32;
		var currencyItemID : ItemID;
		var moneyQuantity : Int32;
		var calculateXPOldSaves : Bool;
		var courierMoneyMultiplier : Float;
		var moneyCalc : Float;
		var factVal : Int32;
		var courierMissionsDone : Float;
		craftingSystem = ( ( CraftingSystem )( GameInstance.GetScriptableSystemsContainer( gi ).Get( 'CraftingSystem' ) ) );
		transSys = GameInstance.GetTransactionSystem( gi );
		visualizer = GameInstance.GetDebugVisualizerSystem( gi );
		statsDataSystem = GameInstance.GetStatsDataSystem( gi );
		rewardRecord = TweakDBInterface.GetRewardBaseRecord( rewardID );
		if( !( rewardRecord ) )
		{
			return;
		}
		player = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( gi ).GetLocalPlayerControlledGameObject() ) );
		NCPDJobDone = new NCPDJobDoneEvent;
		rewardName = rewardRecord.Name();
		if( rewardName != "" )
		{
			visualizer.DrawText( Vector4( 5.0, 350.0, 0.0, 0.0 ), rewardName, gameDebugViewETextAlignment.Left, Color( 255, 128, 0, 255 ), 1.5 );
		}
		else
		{
			rewardName = "GiveReward(): No reward name found";
			visualizer.DrawText( Vector4( 5.0, 350.0, 0.0, 0.0 ), rewardName, gameDebugViewETextAlignment.Left, Color( 255, 128, 0, 255 ), 1.5 );
		}
		rewardRecord.Items( itemArr );
		for( i = 0; i < itemArr.Size(); i += 1 )
		{
			quantity = itemArr[ i ].Quantity() * amount;
			itemID = ItemID.FromTDBID( itemArr[ i ].Item().GetID() );
			transSys.GiveItem( player, ItemID.FromTDBID( itemArr[ i ].Item().GetID() ), quantity );
			GameInstance.GetTelemetrySystem( player.GetGame() ).LogItemReward( player, itemID );
		}
		rewardRecord.Recipes( recipesArr );
		for( i = 0; i < recipesArr.Size(); i += 1 )
		{
			addRecipeRequest = new AddRecipeRequest;
			addRecipeRequest.owner = player;
			addRecipeRequest.amount = 1;
			addRecipeRequest.recipe = recipesArr[ i ].GetID();
			craftingSystem.QueueRequest( addRecipeRequest );
			GameInstance.GetTelemetrySystem( player.GetGame() ).LogItemReward( player, ItemID.FromTDBID( recipesArr[ i ].GetID() ) );
		}
		rewardRecord.Experience( expArr );
		playerXPmultiplier = GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.XPbonusMultiplier );
		streetCredXPmultiplier = GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.StreetCredXPBonusMultiplier ) / 100.0;
		for( i = 0; i < expArr.Size(); i += 1 )
		{
			expEvt = new ExperiencePointsEvent;
			quantityMods.Clear();
			expArr[ i ].QuantityModifiers( quantityMods );
			experienceValue = RPGManager.CalculateStatModifiers( quantityMods, player.GetGame(), player, target );
			expType = expArr[ i ].Type().Type();
			expEvt.type = expType;
			expEvt.isDebug = false;
			if( expType != gamedataProficiencyType.Level && expType != gamedataProficiencyType.StreetCred )
			{
				experienceValue *= playerXPmultiplier;
			}
			else if( expType == gamedataProficiencyType.StreetCred )
			{
				experienceValue *= ( 1.0 + streetCredXPmultiplier );
			}
			contentAssignment = TweakDBInterface.GetForeignKey( rewardID + T".contentAssignment", T"" );
			if( TDBID.IsValid( contentAssignment ) )
			{
				playerLevel = GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.PowerLevel );
				contentLevel = GameInstance.GetLevelAssignmentSystem( player.GetGame() ).GetLevelAssignment( contentAssignment );
				levelDiff = RoundMath( playerLevel - ( ( Float )( contentLevel ) ) );
				powerDiff = CalculatePowerDifferential( levelDiff );
				switch( powerDiff )
				{
					case gameEPowerDifferential.TRASH:
						experienceValue *= 0.80000001;
					break;
					case gameEPowerDifferential.EASY:
						experienceValue *= 0.89999998;
					break;
					case gameEPowerDifferential.HARD:
						experienceValue *= 1.10000002;
					break;
					case gameEPowerDifferential.IMPOSSIBLE:
						experienceValue *= 1.20000005;
					break;
					default:
						break;
				}
			}
			expEvt.amount = ( ( Int32 )( experienceValue ) ) * amount;
			rewardName = StrLower( rewardName );
			if( expType == gamedataProficiencyType.Level )
			{
				calculateXPOldSaves = GetFact( gi, 'CalculateXPOldSaves' );
				if( StrBeginsWith( rewardName, "ma_" ) && !( calculateXPOldSaves ) )
				{
					expEvt.amount = ( ( Int32 )( CalculateMinorActivityReward( gi, experienceValue ) ) );
				}
				else if( ( StrBeginsWith( rewardName, "sts_" ) && !( StrBeginsWith( rewardName, "sts_ep1" ) ) ) && !( calculateXPOldSaves ) )
				{
					expEvt.amount = ( ( Int32 )( CalculateStreetStoryReward( gi, experienceValue ) ) );
				}
				else if( ( ( StrBeginsWith( rewardName, "q3" ) || StrBeginsWith( rewardName, "mq3" ) ) || StrBeginsWith( rewardName, "sts_ep1" ) ) || StrBeginsWith( rewardName, "sa_ep1" ) )
				{
					playerLevel = GameInstance.GetStatsSystem( player.GetGame() ).GetStatValue( player.GetEntityID(), gamedataStatType.Level );
					expEvt.amount = ( ( Int32 )( CalculateEP1Reward( gi, experienceValue, playerLevel ) ) );
				}
			}
			GameInstance.GetTelemetrySystem( gi ).LogXPReward( expArr[ i ].GetID(), expEvt.amount, expEvt.type );
			player.QueueEvent( expEvt );
			if( expType == gamedataProficiencyType.Level )
			{
				NCPDJobDone.levelXPAwarded = expEvt.amount;
			}
			else if( expType == gamedataProficiencyType.StreetCred )
			{
				NCPDJobDone.streetCredXPAwarded = expEvt.amount;
			}
		}
		moneyQuantity = 0;
		rewardRecord.CurrencyPackage( currencyArr );
		for( i = 0; i < currencyArr.Size(); i += 1 )
		{
			quantityMods.Clear();
			currencyArr[ i ].QuantityModifiers( quantityMods );
			quantity = ( ( Int32 )( RPGManager.CalculateStatModifiers( quantityMods, player.GetGame(), player, target ) ) );
			if( quantity > 0 )
			{
				quantity = ( ( moneyMultiplier > 0.0 ) ? ( ( ( Int32 )( ( ( Float )( quantity ) ) * moneyMultiplier ) ) ) : ( quantity ) );
				quantity *= amount;
				currencyItemID = ItemID.FromTDBID( currencyArr[ i ].Currency().GetID() );
				if( StrBeginsWith( rewardName, "sa_ep1_courier_tier" ) )
				{
					factVal = GetFact( gi, 'courier_missions_done' );
					courierMissionsDone = ( ( Float )( factVal ) );
					courierMoneyMultiplier = statsDataSystem.GetValueFromCurve( 'activities_done_to_money_multiplier', courierMissionsDone, 'courier_done_to_money_mult' );
					moneyCalc = ( ( courierMoneyMultiplier > 0.0 ) ? ( ( ( Float )( quantity ) ) * courierMoneyMultiplier ) : ( ( ( Float )quantity ) ) );
					quantity = ( ( Int32 )( moneyCalc ) );
					SetFactValue( gi, 'courier_missions_done', factVal + 1 );
				}
				transSys.GiveItem( player, currencyItemID, quantity );
				if( currencyItemID == MarketSystem.Money() )
				{
					moneyQuantity += quantity;
				}
			}
		}
		rewardRecord.Achievement( achievementsArr );
		for( i = 0; i < achievementsArr.Size(); i += 1 )
		{
			SendAddAchievementRequest( gi, achievementsArr[ i ].Type(), achievementsArr[ i ] );
		}
		rewardRecord.PhotoModeItem( photoModeItmsArr );
		for( i = 0; i < photoModeItmsArr.Size(); i += 1 )
		{
			SendPhotoModeItemUnlockRequest( gi, photoModeItmsArr[ i ] );
		}
		if( StrBeginsWith( rewardName, "ma_" ) )
		{
			GameInstance.GetUISystem( gi ).QueueEvent( NCPDJobDone );
		}
		GameInstance.GetTelemetrySystem( gi ).LogRewardGiven( StringToName( rewardName ), rewardID, moneyQuantity );
	}

	public static function AwardXP( gi : GameInstance, amount : Float, type : gamedataProficiencyType )
	{
		var xpEvent : ExperiencePointsEvent;
		var statsSystem : StatsSystem;
		var playerXPmultiplier : Float;
		var awardXP : Float;
		statsSystem = GameInstance.GetStatsSystem( gi );
		playerXPmultiplier = statsSystem.GetStatValue( GetPlayer( gi ).GetEntityID(), gamedataStatType.XPbonusMultiplier );
		awardXP = amount * playerXPmultiplier;
		xpEvent = new ExperiencePointsEvent;
		xpEvent.amount = ( ( Int32 )( awardXP ) );
		xpEvent.type = type;
		GetPlayer( gi ).QueueEvent( xpEvent );
	}

	private static function SendAddAchievementRequest( gi : GameInstance, achievement : gamedataAchievement, achievementRecord : weak< Achievement_Record > )
	{
		var request : AddAchievementRequest;
		request = new AddAchievementRequest;
		request.achievement = achievement;
		GameInstance.GetScriptableSystemsContainer( gi ).Get( 'DataTrackingSystem' ).QueueRequest( request );
	}

	private static function SendPhotoModeItemUnlockRequest( gi : GameInstance, photoModeItm : weak< PhotoModeItem_Record > )
	{
		var tweakID : TweakDBID;
		tweakID = photoModeItm.GetID();
		GameInstance.GetPhotoModeSystem( gi ).UnlockPhotoModeItem( tweakID );
	}

	public static function GiveScavengeReward( gi : GameInstance, rewardID : TweakDBID, scavengeTargetEntityID : EntityID )
	{
		GiveReward( gi, rewardID );
	}

	public static function PrepareGameEffectAttack( gi : GameInstance, instigator : GameObject, source : GameObject, attackName : TweakDBID, optional position : Vector4, optional hitFlags : array< SHitFlag >, optional target : GameObject, optional tickRateOverride : Float ) : Attack_GameEffect
	{
		var attack : Attack_GameEffect;
		var attackRecord : Attack_GameEffect_Record;
		var attackContext : AttackInitContext;
		var attackEffect : EffectInstance;
		var statMods : array< gameStatModifierData >;
		var sharedData : EffectData;
		var effectDef : EffectSharedDataDef;
		attackRecord = ( ( Attack_GameEffect_Record )( TweakDBInterface.GetAttackRecord( attackName ) ) );
		if( attackRecord )
		{
			attackContext.record = attackRecord;
			attackContext.instigator = instigator;
			attackContext.source = source;
			attack = ( ( Attack_GameEffect )( IAttack.Create( attackContext ) ) );
			attackEffect = attack.PrepareAttack( instigator );
			attack.GetStatModList( statMods );
			if( position == Vector4.EmptyVector() )
			{
				position = source.GetWorldPosition();
			}
			sharedData = attackEffect.GetSharedData();
			effectDef = GetAllBlackboardDefs().EffectSharedData;
			EffectData.SetFloat( sharedData, effectDef.radius, attackRecord.Range() );
			EffectData.SetVector( sharedData, effectDef.position, position );
			EffectData.SetVariant( sharedData, effectDef.attack, ( ( IAttack )( attack ) ) );
			EffectData.SetVariant( sharedData, effectDef.attackStatModList, statMods );
			EffectData.SetVariant( sharedData, effectDef.flags, hitFlags );
			EffectData.SetEntity( sharedData, effectDef.entity, target );
			if( tickRateOverride > 0.0 )
			{
				EffectData.SetFloat( sharedData, effectDef.tickRateOverride, tickRateOverride );
			}
			EffectData.SetName( sharedData, effectDef.slotName, 'Head' );
			return attack;
		}
		return attack;
	}

	public static function ExtractItemsOfEquipArea( type : gamedataEquipmentArea, const input : ref< array< weak< gameItemData > > >, output : ref< array< weak< gameItemData > > > ) : Bool
	{
		var i : Int32;
		var itemsFound : Bool;
		for( i = 0; i < input.Size(); i += 1 )
		{
			if( TweakDBInterface.GetItemRecord( ItemID.GetTDBID( input[ i ].GetID() ) ).EquipArea().Type() == type )
			{
				output.PushBack( input[ i ] );
				itemsFound = true;
			}
		}
		return itemsFound;
	}

	public static function GetAmmoCount( owner : GameObject, itemID : ItemID ) : String
	{
		var itemRecord : Item_Record;
		var weaponRecord : WeaponItem_Record;
		var category : gamedataItemCategory;
		var ammoQuery : ItemID;
		var ammoCount : Int32;
		var transSystem : TransactionSystem;
		transSystem = GameInstance.GetTransactionSystem( owner.GetGame() );
		itemRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) );
		category = itemRecord.ItemCategory().Type();
		if( category == gamedataItemCategory.Gadget || category == gamedataItemCategory.Consumable )
		{
			ammoQuery = ItemID.CreateQuery( ItemID.GetTDBID( itemID ) );
			ammoCount = transSystem.GetItemQuantity( owner, ammoQuery );
			return ( ( String )( ammoCount ) );
		}
		else if( category == gamedataItemCategory.Weapon )
		{
			weaponRecord = ( ( WeaponItem_Record )( itemRecord ) );
			ammoQuery = ItemID.CreateQuery( weaponRecord.Ammo().GetID() );
			ammoCount = transSystem.GetItemQuantity( owner, ammoQuery );
		}
		if( ammoCount > 0 )
		{
			return ( ( String )( ammoCount ) );
		}
		else
		{
			return "";
		}
	}

	public static function GetAmmoCountValue( owner : GameObject, itemID : ItemID ) : Int32
	{
		var itemRecord : Item_Record;
		var weaponRecord : WeaponItem_Record;
		var category : gamedataItemCategory;
		var ammoQuery : ItemID;
		var ammoCount : Int32;
		var transSystem : TransactionSystem;
		transSystem = GameInstance.GetTransactionSystem( owner.GetGame() );
		itemRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) );
		if( itemRecord )
		{
			category = itemRecord.ItemCategory().Type();
		}
		if( category == gamedataItemCategory.Gadget || category == gamedataItemCategory.Consumable )
		{
			ammoQuery = ItemID.CreateQuery( ItemID.GetTDBID( itemID ) );
			ammoCount = transSystem.GetItemQuantity( owner, ammoQuery );
			return ammoCount;
		}
		else if( category == gamedataItemCategory.Weapon && itemRecord )
		{
			weaponRecord = ( ( WeaponItem_Record )( itemRecord ) );
			ammoQuery = ItemID.CreateQuery( weaponRecord.Ammo().GetID() );
			ammoCount = transSystem.GetItemQuantity( owner, ammoQuery );
		}
		return ammoCount;
	}

	public static function GetWeaponAmmoTDBID( weaponID : ItemID ) : TweakDBID
	{
		return TweakDBInterface.GetWeaponItemRecord( ItemID.GetTDBID( weaponID ) ).Ammo().GetID();
	}

	public static function GetItemRecord( itemID : ItemID ) : Item_Record
	{
		return TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) );
	}

	public static function GetAttachmentSlotID( const slot : ref< String > ) : TweakDBID
	{
		return TDBID.Create( "AttachmentSlots." + slot );
	}

	public static function ForceEquipItemOnPlayer( puppet : GameObject, itemTDBID : TweakDBID, addToInv : Bool )
	{
		var itemID : ItemID;
		var equipRequest : EquipRequest;
		if( ( puppet == NULL ) || !( ( ( PlayerPuppet )( puppet ) ) ) )
		{
			return;
		}
		itemID = ItemID.FromTDBID( itemTDBID );
		equipRequest = new EquipRequest;
		equipRequest.itemID = itemID;
		equipRequest.owner = puppet;
		equipRequest.addToInventory = addToInv;
		GameInstance.GetScriptableSystemsContainer( puppet.GetGame() ).Get( 'EquipmentSystem' ).QueueRequest( equipRequest );
	}

	public static function GetItemActions( itemID : ItemID ) : array< weak< ObjectAction_Record > >
	{
		var actions : array< weak< ObjectAction_Record > >;
		TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) ).ObjectActions( actions );
		return actions;
	}

	public static function IsTechPierceEnabled( gi : GameInstance, owner : GameObject, itemID : ItemID ) : Bool
	{
		return GameInstance.GetTransactionSystem( gi ).GetItemData( owner, itemID ).GetStatValueByType( gamedataStatType.TechPierceEnabled ) > 0.0;
	}

	public static function IsRicochetChanceEnabled( gi : GameInstance, owner : GameObject, itemID : ItemID ) : Bool
	{
		var itemData : gameItemData;
		itemData = GameInstance.GetTransactionSystem( gi ).GetItemData( owner, itemID );
		if( itemData )
		{
			return itemData.GetStatValueByType( gamedataStatType.RicochetChance ) > 0.0;
		}
		return false;
	}

	public static function HasSmartLinkRequirement( itemData : gameItemData ) : Bool
	{
		if( itemData )
		{
			return itemData.GetStatValueByType( gamedataStatType.ItemRequiresSmartLink ) > 0.0;
		}
		return false;
	}

	public static function CanPartBeUnequipped( data : InventoryItemData, slotId : TweakDBID ) : Bool
	{
		var itemID : ItemID;
		var type : gamedataItemType;
		itemID = data.ID;
		type = RPGManager.GetItemType( itemID );
		if( IsWeaponMod( type ) || IsClothingMod( type ) )
		{
			return data.IsIconic;
		}
		else if( type == gamedataItemType.Prt_Fragment )
		{
			return !( IsNonModifableSlot( slotId ) );
		}
		else if( ( ( ( ( type == gamedataItemType.Prt_ShortScope || type == gamedataItemType.Prt_LongScope ) || type == gamedataItemType.Prt_Muzzle ) || type == gamedataItemType.Prt_HandgunMuzzle ) || type == gamedataItemType.Prt_RifleMuzzle ) || type == gamedataItemType.Prt_Program )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	private static function IsNonModifableSlot( slot : TweakDBID ) : Bool
	{
		return ( ( ( slot == T"AttachmentSlots.StrongArmsKnuckles" ) || ( slot == T"AttachmentSlots.MantisBladesEdge" ) ) || ( slot == T"AttachmentSlots.NanoWiresCable" ) ) || ( slot == T"AttachmentSlots.ProjectileLauncherRound" );
	}

	public static function CanItemBeDropped( puppet : GameObject, itemData : gameItemData ) : Bool
	{
		var blackboard : IBlackboard;
		var consumableBeingUsed : ItemID;
		blackboard = GameInstance.GetBlackboardSystem( puppet.GetGame() ).Get( GetAllBlackboardDefs().UI_QuickSlotsData );
		consumableBeingUsed = ( ( ItemID )( blackboard.GetVariant( GetAllBlackboardDefs().UI_QuickSlotsData.consumableBeingUsed ) ) );
		if( itemData )
		{
			if( ItemID.IsValid( consumableBeingUsed ) && ( TweakDBInterface.GetConsumableItemRecord( ItemID.GetTDBID( itemData.GetID() ) ) == TweakDBInterface.GetConsumableItemRecord( ItemID.GetTDBID( consumableBeingUsed ) ) ) )
			{
				return false;
			}
			return ( ( !( itemData.HasTag( 'IconicWeapon' ) ) && !( itemData.HasTag( 'Quest' ) ) ) && !( itemData.HasTag( 'UnequipBlocked' ) ) ) && ItemActionsHelper.GetDropAction( itemData.GetID() );
		}
		return false;
	}

	public static function CanItemBeDisassembled( gameInstance : GameInstance, itemID : ItemID ) : Bool
	{
		return CraftingSystem.GetInstance( gameInstance ).CanItemBeDisassembled( GetPlayer( gameInstance ), itemID );
	}

	public static function HasDownloadFundsAction( itemID : ItemID ) : Bool
	{
		var i : Int32;
		var actions : array< weak< ObjectAction_Record > >;
		actions = RPGManager.GetItemActions( itemID );
		for( i = 0; i < actions.Size(); i += 1 )
		{
			if( actions[ i ].ActionName() == 'DownloadFunds' )
			{
				return true;
			}
		}
		return false;
	}

	public static function CanDownloadFunds( gi : GameInstance, itemID : ItemID ) : Bool
	{
		var i : Int32;
		var fact : CName;
		var actions : array< weak< ObjectAction_Record > >;
		actions = RPGManager.GetItemActions( itemID );
		for( i = 0; i < actions.Size(); i += 1 )
		{
			if( actions[ i ].ActionName() == 'DownloadFunds' )
			{
				fact = TweakDBInterface.GetCName( actions[ i ].GetID() + T".factToCheck", '' );
				if( IsNameValid( fact ) && ( GetFact( gi, fact ) <= 0 ) )
				{
					return true;
				}
			}
		}
		return false;
	}

	public static function CanItemBeDisassembled( gameInstance : GameInstance, itemData : weak< gameItemData > ) : Bool
	{
		var CS : CraftingSystem;
		var blackboard : IBlackboard;
		var consumableBeingUsed : ItemID;
		blackboard = GameInstance.GetBlackboardSystem( gameInstance ).Get( GetAllBlackboardDefs().UI_QuickSlotsData );
		consumableBeingUsed = ( ( ItemID )( blackboard.GetVariant( GetAllBlackboardDefs().UI_QuickSlotsData.consumableBeingUsed ) ) );
		if( itemData )
		{
			if( ItemID.IsValid( consumableBeingUsed ) && ( TweakDBInterface.GetConsumableItemRecord( ItemID.GetTDBID( itemData.GetID() ) ) == TweakDBInterface.GetConsumableItemRecord( ItemID.GetTDBID( consumableBeingUsed ) ) ) )
			{
				return false;
			}
			CS = CraftingSystem.GetInstance( gameInstance );
			return CS.CanItemBeDisassembled( itemData );
		}
		return false;
	}

	public static function IsItemEquipped( owner : weak< GameObject >, itemID : ItemID ) : Bool
	{
		var ES : EquipmentSystem;
		var result : Bool;
		ES = EquipmentSystem.GetInstance( owner );
		result = ES.IsEquipped( owner, itemID );
		return result;
	}

	public static function IsItemCrafted( itemData : weak< gameItemData > ) : Bool
	{
		var value : Float;
		value = itemData.GetStatValueByType( gamedataStatType.IsItemCrafted );
		return value > 0.0;
	}

	public static function IsItemCrafted( gi : GameInstance, itemId : ItemID ) : Bool
	{
		var value : Float;
		var statsID : StatsObjectID;
		statsID = ( ( StatsObjectID )( itemId ) );
		value = GameInstance.GetStatsSystem( gi ).GetStatValue( statsID, gamedataStatType.IsItemCrafted );
		return value > 0.0;
	}

	public static function ConsumeItem( obj : weak< GameObject >, evt : InteractionChoiceEvent ) : Bool
	{
		var itemQuantity : Int32;
		var lootActionWrapper : LootChoiceActionWrapper;
		var request : EquipmentSystemWeaponManipulationRequest;
		var eqs : EquipmentSystem;
		var blackboard : IBlackboard;
		var gameInstance : GameInstance;
		var transactionSystem : TransactionSystem;
		var itemType : gamedataItemType;
		lootActionWrapper = LootChoiceActionWrapper.Unwrap( evt );
		gameInstance = obj.GetGame();
		transactionSystem = GameInstance.GetTransactionSystem( gameInstance );
		if( lootActionWrapper.action == 'Consume' )
		{
			itemQuantity = GameInstance.GetTransactionSystem( gameInstance ).GetItemQuantity( obj, lootActionWrapper.itemId );
			transactionSystem.TransferItem( obj, evt.activator, lootActionWrapper.itemId, itemQuantity );
			itemType = GetItemType( lootActionWrapper.itemId );
			if( itemType == gamedataItemType.Con_Inhaler || itemType == gamedataItemType.Con_Injector )
			{
				blackboard = GameInstance.GetBlackboardSystem( gameInstance ).Get( GetAllBlackboardDefs().UI_QuickSlotsData );
				blackboard.SetVariant( GetAllBlackboardDefs().UI_QuickSlotsData.containerConsumable, lootActionWrapper.itemId );
				eqs = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( gameInstance ).Get( 'EquipmentSystem' ) ) );
				request = new EquipmentSystemWeaponManipulationRequest;
				request.owner = evt.activator;
				request.requestType = EquipmentManipulationAction.RequestConsumable;
				eqs.QueueRequest( request );
			}
			else
			{
				ItemActionsHelper.ConsumeItem( evt.activator, lootActionWrapper.itemId, true );
				return true;
			}
		}
		else if( lootActionWrapper.action == 'Eat' )
		{
			itemQuantity = GameInstance.GetTransactionSystem( gameInstance ).GetItemQuantity( obj, lootActionWrapper.itemId );
			transactionSystem.TransferItem( obj, evt.activator, lootActionWrapper.itemId, itemQuantity );
			ItemActionsHelper.EatItem( evt.activator, lootActionWrapper.itemId, true );
			return true;
		}
		else if( lootActionWrapper.action == 'Drink' )
		{
			itemQuantity = GameInstance.GetTransactionSystem( gameInstance ).GetItemQuantity( obj, lootActionWrapper.itemId );
			transactionSystem.TransferItem( obj, evt.activator, lootActionWrapper.itemId, itemQuantity );
			ItemActionsHelper.DrinkItem( evt.activator, lootActionWrapper.itemId, true );
			return true;
		}
		return false;
	}

	public static function IsWeaponMelee( type : gamedataItemType ) : Bool
	{
		return ( ( ( ( ( ( ( ( ( ( type == gamedataItemType.Wea_Fists || type == gamedataItemType.Wea_Knife ) || type == gamedataItemType.Wea_Katana ) || type == gamedataItemType.Wea_Sword ) || type == gamedataItemType.Wea_OneHandedClub ) || type == gamedataItemType.Wea_LongBlade ) || type == gamedataItemType.Wea_ShortBlade ) || type == gamedataItemType.Wea_Melee ) || type == gamedataItemType.Wea_Axe ) || type == gamedataItemType.Wea_Chainsword ) || type == gamedataItemType.Wea_Machete ) || type == gamedataItemType.Wea_Hammer;
	}

	public static function BreakItem( gi : GameInstance, owner : GameObject, itemID : ItemID ) : Bool
	{
		var chance : Float;
		var rand : Float;
		var itemData : weak< gameItemData >;
		itemData = GameInstance.GetTransactionSystem( gi ).GetItemData( owner, itemID );
		if( !( itemData ) )
		{
			return false;
		}
		if( RPGManager.GetItemDataQuality( itemData ) == gamedataQuality.Common )
		{
			chance = TDB.GetFloat( T"GlobalStats.ChanceForItemToBeBroken.value", 1.0 );
			rand = RandF();
			if( rand < chance )
			{
				return true;
			}
		}
		return false;
	}

	public static function DropManyItems( gameInstance : GameInstance, obj : weak< GameObject >, const items : ref< array< ItemModParams > > )
	{
		var i : Int32;
		var dropList : array< DropInstruction >;
		var LM : LootManager;
		for( i = 0; i < items.Size(); i += 1 )
		{
			dropList.PushBack( DropInstruction.Create( items[ i ].itemID, items[ i ].quantity ) );
			RPGManager.ReturnRetrievableWeaponMods( GetItemData( gameInstance, obj, items[ i ].itemID ), obj );
		}
		LM = GameInstance.GetLootManager( gameInstance );
		LM.SpawnItemDropOfManyItems( obj, dropList, 'playerDropBag', obj.GetWorldPosition() );
	}

	public static function GetRandomizedHealingConsumable( puppet : weak< ScriptedPuppet > ) : TweakDBID
	{
		var rand : Int32;
		var list : array< TweakDBID >;
		list.PushBack( T"Items.FirstAidWhiffV0" );
		list.PushBack( T"Items.BonesMcCoy70V0" );
		rand = RandRange( 0, list.Size() + 1 );
		return list[ rand ];
	}

	public static function GetRandomizedGadget( puppet : weak< ScriptedPuppet > ) : TweakDBID
	{
		var rand : Int32;
		var list : array< TweakDBID >;
		rand = RandRange( 0, list.Size() + 1 );
		return list[ rand ];
	}

	public static function ForceUnequipItemFromPlayer( puppet : GameObject, slotTDBID : TweakDBID, removeItem : Bool )
	{
		var itemID : ItemID;
		var transactionSys : TransactionSystem;
		if( ( puppet == NULL ) && !( ( ( PlayerPuppet )( puppet ) ) ) )
		{
			return;
		}
		if( !( TDBID.IsValid( slotTDBID ) ) )
		{
			return;
		}
		transactionSys = GameInstance.GetTransactionSystem( puppet.GetGame() );
		itemID = transactionSys.GetItemInSlot( puppet, slotTDBID ).GetItemID();
		transactionSys.RemoveItemFromSlot( puppet, slotTDBID, true );
		if( removeItem )
		{
			transactionSys.RemoveItem( puppet, itemID, 1 );
		}
	}

	public static function ToggleHolsteredArmAppearance( puppet : GameObject, setHoleInArm : Bool )
	{
		var itemObj : ItemObject;
		var player : PlayerPuppet;
		var switchEvent : gameuiPersonalLinkSwitcherEvent;
		player = ( ( PlayerPuppet )( puppet ) );
		if( !( player ) )
		{
			return;
		}
		switchEvent = new gameuiPersonalLinkSwitcherEvent;
		switchEvent.isAdvanced = setHoleInArm;
		itemObj = GameInstance.GetTransactionSystem( player.GetGame() ).GetItemInSlot( player, RPGManager.GetAttachmentSlotID( "RightArm" ) );
		if( itemObj )
		{
			itemObj.QueueEvent( switchEvent );
		}
	}

	public static function TogglePersonalLinkAppearance( puppet : GameObject )
	{
		var itemObj : ItemObject;
		var player : PlayerPuppet;
		var meshEvent : entAppearanceEvent;
		player = ( ( PlayerPuppet )( puppet ) );
		if( !( player ) )
		{
			return;
		}
		itemObj = GameInstance.GetTransactionSystem( player.GetGame() ).GetItemInSlot( player, RPGManager.GetAttachmentSlotID( "PersonalLink" ) );
		if( ItemID.GetTDBID( EquipmentSystem.GetData( player ).GetActiveItem( gamedataEquipmentArea.RightArm ) ) == T"Items.HolsteredStrongArms" )
		{
			meshEvent = new entAppearanceEvent;
			meshEvent.appearanceName = 'only_plug';
			itemObj.QueueEvent( meshEvent );
			return;
		}
	}

	public static function HasStatFlag( owner : weak< GameObject >, flag : gamedataStatType ) : Bool
	{
		if( !( owner ) || !( owner.IsAttached() ) )
		{
			return false;
		}
		return GameInstance.GetStatsSystem( owner.GetGame() ).GetStatBoolValue( owner.GetEntityID(), flag );
	}

	public static function DoesPlayerHaveQuickHack( player : weak< PlayerPuppet >, quickHackTweak : TweakDBID ) : Bool
	{
		var actionIDs : array< TweakDBID >;
		var i : Int32;
		actionIDs = GetPlayerQuickHackList( player );
		for( i = 0; i < actionIDs.Size(); i += 1 )
		{
			if( actionIDs[ i ] == quickHackTweak )
			{
				return true;
			}
		}
		return false;
	}

	public static function GetPlayerQuickHackList( player : weak< PlayerPuppet > ) : array< TweakDBID >
	{
		var i : Int32;
		var actionIDs : array< TweakDBID >;
		var playerActions : array< PlayerQuickhackData >;
		playerActions = GetPlayerQuickHackListWithQuality( player );
		for( i = 0; i < playerActions.Size(); i += 1 )
		{
			actionIDs.PushBack( playerActions[ i ].actionRecord.GetID() );
		}
		return actionIDs;
	}

	public static function GetPlayerQuickHackListWithQuality( player : weak< PlayerPuppet > ) : array< PlayerQuickhackData >
	{
		var systemReplacementID : ItemID;
		var parts : array< SPartSlots >;
		var i, i1 : Int32;
		var itemRecord : weak< Item_Record >;
		var itemID : ItemID;
		var tweakItemID : TweakDBID;
		var actions : array< weak< ObjectAction_Record > >;
		var quickhackDataArray : array< PlayerQuickhackData >;
		var quickhackData : PlayerQuickhackData;
		var quickhackDataEmpty : PlayerQuickhackData;
		quickhackDataArray = player.GetCachedQuickHackList();
		if( quickhackDataArray.Size() > 0 )
		{
			return quickhackDataArray;
		}
		systemReplacementID = EquipmentSystem.GetData( player ).GetActiveItem( gamedataEquipmentArea.SystemReplacementCW );
		itemRecord = RPGManager.GetItemRecord( systemReplacementID );
		tweakItemID = itemRecord.GetID();
		if( EquipmentSystem.IsCyberdeckEquipped( player ) )
		{
			itemRecord.ObjectActions( actions );
			for( i = 0; i < actions.Size(); i += 1 )
			{
				if( ( actions[ i ].ObjectActionType().Type() == gamedataObjectActionType.DeviceQuickHack || actions[ i ].ObjectActionType().Type() == gamedataObjectActionType.PuppetQuickHack ) || actions[ i ].ObjectActionType().Type() == gamedataObjectActionType.VehicleQuickHack )
				{
					quickhackData = quickhackDataEmpty;
					quickhackData.actionRecord = actions[ i ];
					quickhackData.quality = itemRecord.Quality().Value();
					quickhackDataArray.PushBack( quickhackData );
				}
			}
			parts = ItemModificationSystem.GetAllSlots( player, systemReplacementID );
			for( i = 0; i < parts.Size(); i += 1 )
			{
				actions.Clear();
				itemRecord = RPGManager.GetItemRecord( parts[ i ].installedPart );
				tweakItemID = itemRecord.GetID();
				itemID = ItemID.FromTDBID( tweakItemID );
				if( itemRecord )
				{
					itemRecord.ObjectActions( actions );
					for( i1 = 0; i1 < actions.Size(); i1 += 1 )
					{
						if( ( actions[ i1 ].ObjectActionType().Type() == gamedataObjectActionType.DeviceQuickHack || actions[ i1 ].ObjectActionType().Type() == gamedataObjectActionType.PuppetQuickHack ) || actions[ i ].ObjectActionType().Type() == gamedataObjectActionType.VehicleQuickHack )
						{
							quickhackData = quickhackDataEmpty;
							quickhackData.actionRecord = actions[ i1 ];
							quickhackData.quality = itemRecord.Quality().Value();
							quickhackData.itemID = itemID;
							quickhackDataArray.PushBack( quickhackData );
						}
					}
				}
			}
		}
		actions.Clear();
		itemRecord = RPGManager.GetItemRecord( EquipmentSystem.GetData( player ).GetActiveItem( gamedataEquipmentArea.Splinter ) );
		if( itemRecord )
		{
			itemRecord.ObjectActions( actions );
			for( i = 0; i < actions.Size(); i += 1 )
			{
				if( ( actions[ i ].ObjectActionType().Type() == gamedataObjectActionType.DeviceQuickHack || actions[ i ].ObjectActionType().Type() == gamedataObjectActionType.PuppetQuickHack ) || actions[ i ].ObjectActionType().Type() == gamedataObjectActionType.VehicleQuickHack )
				{
					quickhackData = quickhackDataEmpty;
					quickhackData.actionRecord = actions[ i ];
					quickhackDataArray.PushBack( quickhackData );
				}
			}
		}
		RemoveDuplicatedHacks( quickhackDataArray );
		PlayerPuppet.ChacheQuickHackList( player, quickhackDataArray );
		return quickhackDataArray;
	}

	public static function GetMonoWireQuickhackRecord( weaponObject : WeaponObject ) : Item_Record
	{
		var quickhackSlotInnerData : InnerItemData;
		var selectedQuickhackItemID : ItemID;
		if( !( WeaponObject.IsOfType( weaponObject.GetItemID(), gamedataItemType.Cyb_NanoWires ) ) )
		{
			return NULL;
		}
		weaponObject.GetItemData().GetItemPart( quickhackSlotInnerData, T"AttachmentSlots.NanoWiresQuickhackSlot" );
		selectedQuickhackItemID = InnerItemData.GetItemID( quickhackSlotInnerData );
		return GetItemRecord( selectedQuickhackItemID );
	}

	public static function GetMonoWireQuickHackData( playerOwner : PlayerPuppet, targetScriptedPuppet : ScriptedPuppet, weaponObject : WeaponObject ) : QuickhackData
	{
		var monowireQuickHackData : QuickhackData;
		var quickhackRecordActions : array< weak< ObjectAction_Record > >;
		var selectedQuickhackActionRecord : ObjectAction_Record;
		var quickhackRecord : Item_Record;
		var context : GetActionsContext;
		var targetPuppetActions : array< PuppetAction >;
		var targetActionRecords : array< weak< ObjectAction_Record > >;
		var i : Int32;
		var j : Int32;
		var hasSameActionName : Bool;
		var isQuickhackOfDeviceOrPuppetType : Bool;
		var newActionHasBiggerPriority : Bool;
		quickhackRecord = GetMonoWireQuickhackRecord( weaponObject );
		if( !( targetScriptedPuppet ) || !( quickhackRecord ) )
		{
			return NULL;
		}
		context = targetScriptedPuppet.GetPuppetPS().GenerateContext( gamedeviceRequestType.Remote, Device.GetInteractionClearance(), playerOwner, targetScriptedPuppet.GetEntityID() );
		targetScriptedPuppet.GetRecord().ObjectActions( targetActionRecords );
		targetScriptedPuppet.GetPuppetPS().GetAllChoices( targetActionRecords, context, targetPuppetActions );
		if( targetPuppetActions.Size() == 0 )
		{
			return NULL;
		}
		quickhackRecord.ObjectActions( quickhackRecordActions );
		selectedQuickhackActionRecord = NULL;
		for( i = 0; i < targetPuppetActions.Size(); i += 1 )
		{
			for( j = quickhackRecordActions.Size() - 1; j >= 0; j -= 1 )
			{
				hasSameActionName = quickhackRecordActions[ j ].ActionName() == targetPuppetActions[ i ].GetObjectActionRecord().ActionName();
				isQuickhackOfDeviceOrPuppetType = quickhackRecordActions[ j ].ObjectActionType().Type() == gamedataObjectActionType.DeviceQuickHack || quickhackRecordActions[ j ].ObjectActionType().Type() == gamedataObjectActionType.PuppetQuickHack;
				if( hasSameActionName && isQuickhackOfDeviceOrPuppetType )
				{
					if( !( selectedQuickhackActionRecord ) )
					{
						selectedQuickhackActionRecord = quickhackRecordActions[ j ];
						continue;
					}
					newActionHasBiggerPriority = quickhackRecordActions[ j ].Priority() > selectedQuickhackActionRecord.Priority();
					if( newActionHasBiggerPriority )
					{
						selectedQuickhackActionRecord = quickhackRecordActions[ j ];
					}
				}
			}
			if( selectedQuickhackActionRecord )
			{
				break;
			}
		}
		if( !( selectedQuickhackActionRecord ) )
		{
			return NULL;
		}
		monowireQuickHackData = new QuickhackData;
		monowireQuickHackData.m_actionOwner = targetScriptedPuppet.GetEntityID();
		monowireQuickHackData.m_actionOwnerName = StringToName( targetScriptedPuppet.GetTweakDBFullDisplayName( true ) );
		monowireQuickHackData.m_action = targetPuppetActions[ i ];
		monowireQuickHackData.m_uploadTime = targetPuppetActions[ i ].GetActivationTime();
		monowireQuickHackData.m_duration = targetScriptedPuppet.GetQuickHackDurationFromLongestEffect( selectedQuickhackActionRecord, ( ( GameObject )( targetScriptedPuppet ) ), targetScriptedPuppet.GetEntityID(), playerOwner.GetEntityID() );
		monowireQuickHackData.m_action.SetObjectActionID( selectedQuickhackActionRecord.GetID() );
		monowireQuickHackData.m_cost = 0;
		monowireQuickHackData.m_title = LocKeyToString( selectedQuickhackActionRecord.ObjectActionUI().Caption() );
		return monowireQuickHackData;
	}

	public static function CreateSimpleQuickhackData( playerOwner : PlayerPuppet, targetScriptedPuppet : ScriptedPuppet, quickhackActionRecord : ObjectAction_Record ) : QuickhackData
	{
		var monowireQuickHackData : QuickhackData;
		var context : GetActionsContext;
		var targetPuppetActions : array< PuppetAction >;
		var targetActionRecords : array< weak< ObjectAction_Record > >;
		var i : Int32;
		var quickHackActionFound : Bool;
		quickHackActionFound = false;
		context = targetScriptedPuppet.GetPuppetPS().GenerateContext( gamedeviceRequestType.Remote, Device.GetInteractionClearance(), playerOwner, targetScriptedPuppet.GetEntityID() );
		targetScriptedPuppet.GetRecord().ObjectActions( targetActionRecords );
		targetScriptedPuppet.GetPuppetPS().GetAllChoices( targetActionRecords, context, targetPuppetActions );
		for( i = 0; i < targetPuppetActions.Size(); i += 1 )
		{
			if( quickhackActionRecord.ActionName() == targetPuppetActions[ i ].GetObjectActionRecord().ActionName() )
			{
				quickHackActionFound = true;
				break;
			}
		}
		if( !( quickHackActionFound ) )
		{
			return NULL;
		}
		monowireQuickHackData = new QuickhackData;
		monowireQuickHackData.m_actionOwner = targetScriptedPuppet.GetEntityID();
		monowireQuickHackData.m_actionOwnerName = StringToName( targetScriptedPuppet.GetTweakDBFullDisplayName( true ) );
		monowireQuickHackData.m_action = targetPuppetActions[ i ];
		monowireQuickHackData.m_uploadTime = targetPuppetActions[ i ].GetActivationTime();
		monowireQuickHackData.m_duration = targetScriptedPuppet.GetQuickHackDurationFromLongestEffect( quickhackActionRecord, ( ( GameObject )( targetScriptedPuppet ) ), targetScriptedPuppet.GetEntityID(), playerOwner.GetEntityID() );
		monowireQuickHackData.m_action.SetObjectActionID( quickhackActionRecord.GetID() );
		monowireQuickHackData.m_cost = 0;
		return monowireQuickHackData;
	}

	public static function IncrementQuickHackBlackboard( gameInstance : GameInstance, actionID : TweakDBID )
	{
		var playerPuppet : PlayerPuppet;
		var playerBlackboard : IBlackboard;
		var uploadingQuickHackIDs : array< TweakDBID >;
		playerPuppet = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( gameInstance ).GetLocalPlayerControlledGameObject() ) );
		if( playerPuppet )
		{
			playerBlackboard = GameInstance.GetBlackboardSystem( gameInstance ).GetLocalInstanced( playerPuppet.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
			uploadingQuickHackIDs = ( ( array< TweakDBID > )( playerBlackboard.GetVariant( GetAllBlackboardDefs().PlayerStateMachine.UploadingQuickHackIDs ) ) );
			uploadingQuickHackIDs.PushBack( actionID );
			playerBlackboard.SetVariant( GetAllBlackboardDefs().PlayerStateMachine.UploadingQuickHackIDs, uploadingQuickHackIDs );
		}
	}

	public static function DecrementQuickHackBlackboard( gameInstance : GameInstance, actionID : TweakDBID )
	{
		var playerPuppet : PlayerPuppet;
		var playerBlackboard : IBlackboard;
		var uploadingQuickHackIDs : array< TweakDBID >;
		playerPuppet = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( gameInstance ).GetLocalPlayerControlledGameObject() ) );
		if( playerPuppet )
		{
			playerBlackboard = GameInstance.GetBlackboardSystem( gameInstance ).GetLocalInstanced( playerPuppet.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
			uploadingQuickHackIDs = ( ( array< TweakDBID > )( playerBlackboard.GetVariant( GetAllBlackboardDefs().PlayerStateMachine.UploadingQuickHackIDs ) ) );
			uploadingQuickHackIDs.Remove( actionID );
			playerBlackboard.SetVariant( GetAllBlackboardDefs().PlayerStateMachine.UploadingQuickHackIDs, uploadingQuickHackIDs );
		}
	}

	private static function RemoveDuplicatedHacks( commands : ref< array< PlayerQuickhackData > > )
	{
		var i, i1 : Int32;
		var indexesToRemove : array< Int32 >;
		for( i = commands.Size() - 1; i >= 0; i -= 1 )
		{
			for( i1 = 0; i1 < i; i1 += 1 )
			{
				if( commands[ i ].actionRecord.ActionName() == commands[ i1 ].actionRecord.ActionName() && commands[ i ].actionRecord.ObjectActionType().Type() == commands[ i1 ].actionRecord.ObjectActionType().Type() )
				{
					if( commands[ i ].actionRecord.Priority() >= commands[ i1 ].actionRecord.Priority() )
					{
						if( !( indexesToRemove.Contains( i1 ) ) )
						{
							indexesToRemove.PushBack( i1 );
						}
					}
					else
					{
						if( !( indexesToRemove.Contains( i1 ) ) )
						{
							indexesToRemove.PushBack( i1 );
						}
					}
				}
			}
		}
		indexesToRemove = ArraySortReverse( indexesToRemove );
		for( i = 0; i < indexesToRemove.Size(); i += 1 )
		{
			commands.Erase( indexesToRemove[ i ] );
		}
	}

	private static function RemoveDuplicatedHacks( deck : ref< array< weak< ObjectAction_Record > > >, splinter : ref< array< weak< ObjectAction_Record > > > )
	{
		var i, i1 : Int32;
		for( i = deck.Size() - 1; i >= 0; i -= 1 )
		{
			for( i1 = splinter.Size() - 1; i1 >= 0; i1 -= 1 )
			{
				if( deck[ i ].ActionName() == splinter[ i1 ].ActionName() )
				{
					if( deck[ i ].Priority() >= splinter[ i1 ].Priority() )
					{
						splinter.Erase( i1 );
					}
					else
					{
						deck.Erase( i );
					}
				}
			}
		}
	}

	public static function GetPlayerCurrentHealthPercent( gi : GameInstance ) : Float
	{
		var player : weak< PlayerPuppet >;
		player = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( gi ).GetLocalPlayerControlledGameObject() ) );
		if( player )
		{
			return GameInstance.GetStatPoolsSystem( gi ).GetStatPoolValue( player.GetEntityID(), gamedataStatPoolType.Health, true );
		}
		return -1.0;
	}

	public static function GetVendorWareRequirement( game : GameInstance, record : weak< VendorWare_Record >, itemStatsID : StatsObjectID ) : SItemStackRequirementData
	{
		var statPrereq : StatPrereq_Record;
		var data : SItemStackRequirementData;
		statPrereq = ( ( StatPrereq_Record )( record.AvailabilityPrereq() ) );
		if( statPrereq )
		{
			data.statType = ( ( gamedataStatType )( ( ( Int32 )( EnumValueFromName( 'gamedataStatType', statPrereq.StatType() ) ) ) ) );
			if( StatsObjectID.IsDefined( itemStatsID ) && ( statPrereq.GetStatModifiersCount() > 0 ) )
			{
				data.requiredValue = StatsSystemHelper.GetStatPrereqModifiersValue( game, itemStatsID, statPrereq.GetID() );
			}
			else
			{
				data.requiredValue = statPrereq.ValueToCheck();
			}
		}
		else
		{
			data.statType = gamedataStatType.Invalid;
			data.requiredValue = -1.0;
		}
		return data;
	}

	public static function CyberwareHasUpgrade( game : GameInstance, itemID : ItemID, currentQuality : gamedataQuality, desiredQuality : gamedataQuality, out upgradeItemRecord : Item_Record ) : Bool
	{
		var potentialUpgradeRecord : Item_Record;
		var confirmedUpgradeRecord : Item_Record;
		var qualityUpgrade : Int32;
		qualityUpgrade = ConvertQualityToCombinedValue( desiredQuality ) - ConvertQualityToCombinedValue( currentQuality );
		potentialUpgradeRecord = RPGManager.GetItemRecord( itemID ).NextUpgradeItem();
		while( qualityUpgrade >= 1 )
		{
			if( TDBID.IsValid( potentialUpgradeRecord.GetID() ) )
			{
				confirmedUpgradeRecord = potentialUpgradeRecord;
				potentialUpgradeRecord = confirmedUpgradeRecord.NextUpgradeItem();
			}
			else
			{
				break;
			}
			qualityUpgrade -= 1;
		}
		if( TDBID.IsValid( confirmedUpgradeRecord.GetID() ) && ( qualityUpgrade <= 0 ) )
		{
			upgradeItemRecord = confirmedUpgradeRecord;
			return true;
		}
		return false;
	}

	public static function CyberwareHasSideUpgrade( itemID : ItemID, out sideUpgradeItemRecord : Item_Record ) : Bool
	{
		var sideUpgradeRecord : Item_Record;
		sideUpgradeRecord = RPGManager.GetItemRecord( itemID ).SideUpgradeItem();
		if( TDBID.IsValid( sideUpgradeRecord.GetID() ) )
		{
			sideUpgradeItemRecord = sideUpgradeRecord;
			return true;
		}
		return false;
	}

	public static function CanUpgradeCyberware( owner : GameObject, itemID : ItemID, isEquipped : Bool, selectedQuality : gamedataQuality, out upgradeItemQuality : gamedataQuality, out upgradeItemRecord : Item_Record, out costData : CyberwareUpgradeCostData, optional updateOutputOnFailure : Bool ) : Bool
	{
		var game : GameInstance;
		var itemCombinedQualityValue, checkQualityValue : Int32;
		var potentialUpgradeQuality, currentItemQuality : gamedataQuality;
		var potentialUpgradeItemRecord : Item_Record;
		var potentialCostData : CyberwareUpgradeCostData;
		game = owner.GetGame();
		if( !( isEquipped ) )
		{
			return false;
		}
		itemCombinedQualityValue = GetCombinedItemQualityValue( game, itemID );
		currentItemQuality = ConvertCombinedValueToQuality( itemCombinedQualityValue );
		if( selectedQuality != gamedataQuality.Invalid )
		{
			checkQualityValue = ConvertQualityToCombinedValue( selectedQuality );
		}
		else
		{
			checkQualityValue = itemCombinedQualityValue + 1;
		}
		if( checkQualityValue <= ConvertQualityToCombinedValue( ( ( IsEP1() ) ? ( gamedataQuality.LegendaryPlusPlus ) : ( gamedataQuality.LegendaryPlus ) ) ) )
		{
			potentialUpgradeQuality = ConvertCombinedValueToQuality( checkQualityValue );
			if( CyberwareHasUpgrade( game, itemID, currentItemQuality, potentialUpgradeQuality, potentialUpgradeItemRecord ) )
			{
				if( CanAffordAndPerformCyberwareUpgrade( owner, currentItemQuality, potentialUpgradeItemRecord, potentialCostData ) )
				{
					upgradeItemRecord = potentialUpgradeItemRecord;
					upgradeItemQuality = potentialUpgradeQuality;
					costData = potentialCostData;
					return true;
				}
			}
		}
		if( updateOutputOnFailure )
		{
			upgradeItemRecord = potentialUpgradeItemRecord;
			upgradeItemQuality = potentialUpgradeQuality;
			costData = potentialCostData;
		}
		return false;
	}

	public static function CanAffordAndPerformCyberwareUpgrade( owner : GameObject, currentQuality : gamedataQuality, upgradeItemRecord : Item_Record, out costData : CyberwareUpgradeCostData ) : Bool
	{
		var currentItemPlusValue, upgradeItemPlusValue : Int32;
		var currentNonPlusQuality, upgradeNonPlusQuality, newQuality : gamedataQuality;
		var upgradeIsValid : Bool;
		newQuality = upgradeItemRecord.QualityHandle().Type();
		upgradeItemPlusValue = ConvertQualityToItemPlusValue( newQuality );
		upgradeNonPlusQuality = ConvertQualityToNonPlusQuality( newQuality );
		if( upgradeItemPlusValue > 0 )
		{
			currentItemPlusValue = ConvertQualityToItemPlusValue( currentQuality );
			currentNonPlusQuality = ConvertQualityToNonPlusQuality( currentQuality );
			if( currentNonPlusQuality == upgradeNonPlusQuality && ( ( upgradeItemPlusValue - currentItemPlusValue ) == 1 ) )
			{
				upgradeIsValid = true;
			}
		}
		else
		{
			upgradeIsValid = true;
		}
		if( upgradeIsValid )
		{
			costData.materialRecordID = GetCraftingMaterialRecord( upgradeNonPlusQuality ).GetID();
			costData.materialCount = GetCyberwareUpgradeCost( upgradeItemRecord );
			if( GameInstance.GetTransactionSystem( owner.GetGame() ).GetItemQuantityWithDuplicates( owner, ItemID.CreateQuery( costData.materialRecordID ) ) >= costData.materialCount )
			{
				return true;
			}
		}
		return false;
	}

	public static function GetCyberwareUpgradeCost( itemRecord : Item_Record ) : Int32
	{
		var baseCost : Float;
		var quality : gamedataQuality;
		var coreCW : Bool;
		quality = itemRecord.QualityHandle().Type();
		coreCW = itemRecord.IsCoreCW();
		switch( quality )
		{
			case gamedataQuality.CommonPlus:
				if( coreCW )
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.commonPlus_CoreCW" );
				}
				else
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.commonPlus" );
				}
			break;
			case gamedataQuality.Uncommon:
				baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.uncommon" );
			break;
			case gamedataQuality.UncommonPlus:
				if( coreCW )
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.uncommonPlus_CoreCW" );
				}
				else
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.uncommonPlus" );
				}
			break;
			case gamedataQuality.Rare:
				baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.rare" );
			break;
			case gamedataQuality.RarePlus:
				if( coreCW )
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.rarePlus_CoreCW" );
				}
				else
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.rarePlus" );
				}
			break;
			case gamedataQuality.Epic:
				baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.epic" );
			break;
			case gamedataQuality.EpicPlus:
				if( coreCW )
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.epicPlus_CoreCW" );
				}
				else
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.epicPlus" );
				}
			break;
			case gamedataQuality.Legendary:
				baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.legendary" );
			break;
			case gamedataQuality.LegendaryPlus:
				if( coreCW )
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.legendaryPlus_CoreCW" );
				}
				else
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.legendaryPlus" );
				}
			break;
			case gamedataQuality.LegendaryPlusPlus:
				if( coreCW )
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.legendaryPlusPlus_CoreCW" );
				}
				else
				{
					baseCost = TDB.GetFloat( T"Items.CyberwareBaseUpgradeCosts.legendaryPlusPlus" );
				}
			break;
			default:
				return 0;
		}
		return ( ( Int32 )( itemRecord.UpgradeCostMult() * baseCost ) );
	}

	public static function GetCyberwareUpgradeShardCount( owner : GameObject, quality : gamedataQuality ) : Int32
	{
		return GameInstance.GetTransactionSystem( owner.GetGame() ).GetItemQuantityWithDuplicates( owner, ItemID.CreateQuery( GetCyberwareUpgradeShardID( quality ) ) );
	}

	public static function GetCyberwareUpgradeShardItemData( owner : GameObject, quality : gamedataQuality ) : weak< gameItemData >
	{
		return GameInstance.GetTransactionSystem( owner.GetGame() ).GetItemDataByTDBID( owner, GetCyberwareUpgradeShardID( quality ) );
	}

	public static function GetCyberwareUpgradeShardID( quality : gamedataQuality ) : TweakDBID
	{
		var requiredShardID : TweakDBID;
		switch( quality )
		{
			case gamedataQuality.Uncommon:
				requiredShardID = T"Items.CyberwareUpgradeShardUncommon";
			break;
			case gamedataQuality.Rare:
				requiredShardID = T"Items.CyberwareUpgradeShardRare";
			break;
			case gamedataQuality.Epic:
				requiredShardID = T"Items.CyberwareUpgradeShardEpic";
			break;
			case gamedataQuality.Legendary:
				requiredShardID = T"Items.CyberwareUpgradeShardLegendary";
			break;
			default:
				requiredShardID = T"Items.CyberwareUpgradeShard";
			break;
		}
		return requiredShardID;
	}

	public static function ShouldShowCWChoice( owner : GameObject, itemData : weak< UIInventoryItem > ) : Bool
	{
		var playerDevelopmentData : PlayerDevelopmentData;
		playerDevelopmentData = PlayerDevelopmentSystem.GetData( owner );
		return playerDevelopmentData.IsNewPerkBoughtAnyLevel( gamedataNewPerkType.Tech_Inbetween_Right_2 ) && itemData.GetEquipmentArea() != gamedataEquipmentArea.SystemReplacementCW;
	}

	private static function GetCyberwareUpgradeItemDataWithOffset( owner : GameObject, upgradeItemRecord : Item_Record, statsShardTDBID : TweakDBID, seed : Uint32, offset : Int32 ) : gameItemData
	{
		var statsShardItemID : ItemID;
		var optionId : ItemID;
		var newItemModParams : ItemModParams;
		optionId = ItemID.FromTDBID( upgradeItemRecord.GetID() );
		newItemModParams.itemID = optionId;
		statsShardItemID = ItemID.CreateFromSeedWithOffset( statsShardTDBID, seed, offset );
		newItemModParams.customPartsToInstall.PushBack( statsShardItemID );
		return Inventory.CreateItemData( newItemModParams, owner );
	}

	public static function GetCyberwareUpgradeData( owner : GameObject, itemData : weak< UIInventoryItem >, upgradeItemRecord : Item_Record, costData : CyberwareUpgradeCostData, seed : Uint32, inventorySystem : weak< UIInventoryScriptableSystem >, useMultichoice : Bool ) : RipperdocTokenPopupData
	{
		var data : RipperdocTokenPopupData;
		var newItemModParams : ItemModParams;
		var statsShardTDBID, statsShardSlotTDBID : TweakDBID;
		var existingStatsShard : InnerItemData;
		var statsShardItemID : ItemID;
		var upgradeQualityValue : Int32;
		var sideItemModParams : ItemModParams;
		var useSideItem : Bool;
		data = new RipperdocTokenPopupData;
		data.notificationName = 'base\gameplay\gui\widgets\notifications\ripperdoc_token_popup.inkwidget';
		data.isBlocking = true;
		data.useCursor = true;
		data.costData = costData;
		data.queueName = 'modal_popup';
		upgradeQualityValue = upgradeItemRecord.QualityHandle().Value();
		if( upgradeQualityValue >= 8 )
		{
			statsShardTDBID = T"Items.CyberwareStatsShardLegendary";
		}
		else if( upgradeQualityValue >= 6 )
		{
			statsShardTDBID = T"Items.CyberwareStatsShardEpic";
		}
		else if( upgradeQualityValue >= 4 )
		{
			statsShardTDBID = T"Items.CyberwareStatsShardRare";
		}
		else if( upgradeQualityValue >= 2 )
		{
			statsShardTDBID = T"Items.CyberwareStatsShardUncommon";
		}
		else
		{
			statsShardTDBID = T"Items.CyberwareStatsShardCommon";
		}
		statsShardSlotTDBID = T"AttachmentSlots.StatsShardSlot";
		newItemModParams.itemID = ItemID.FromTDBID( upgradeItemRecord.GetID() );
		newItemModParams.quantity = 1;
		if( itemData.GetItemData().HasPartInSlot( statsShardSlotTDBID ) )
		{
			itemData.GetItemData().GetItemPart( existingStatsShard, statsShardSlotTDBID );
			statsShardItemID = ItemID.DuplicateRandomSeedWithOffset( InnerItemData.GetItemID( existingStatsShard ), InnerItemData.GetStaticData( existingStatsShard ).GetRecordID(), 0 );
			newItemModParams.customPartsToInstall.PushBack( statsShardItemID );
		}
		useSideItem = PlayerDevelopmentSystem.GetData( GetPlayer( owner.GetGame() ) ).IsNewPerkBought( gamedataNewPerkType.Tech_Central_Milestone_3 ) >= 3;
		useSideItem = useSideItem && upgradeItemRecord.EquipAreaHandle().Type() == gamedataEquipmentArea.MusculoskeletalSystemCW;
		if( useSideItem )
		{
			sideItemModParams.itemID = ItemID.FromTDBID( upgradeItemRecord.SideUpgradeItemHandle().GetID() );
			sideItemModParams.customPartsToInstall.PushBack( statsShardItemID );
			sideItemModParams.quantity = 1;
			data.option1SideItemData = Inventory.CreateItemData( sideItemModParams, owner );
			data.option2SideItemData = GetCyberwareUpgradeItemDataWithOffset( owner, upgradeItemRecord.SideUpgradeItemHandle(), statsShardTDBID, seed, 1 );
			data.option3SideItemData = GetCyberwareUpgradeItemDataWithOffset( owner, upgradeItemRecord.SideUpgradeItemHandle(), statsShardTDBID, seed, 2 );
		}
		data.option1GameItemData = Inventory.CreateItemData( newItemModParams, owner );
		data.option1InventoryItem = UIInventoryItem.Make( owner, ( ( useSideItem ) ? ( data.option1SideItemData ) : ( data.option1GameItemData ) ), inventorySystem.GetInventoryItemsManager() );
		if( useMultichoice )
		{
			data.option2GameItemData = GetCyberwareUpgradeItemDataWithOffset( owner, upgradeItemRecord, statsShardTDBID, seed, 1 );
			data.option2InventoryItem = UIInventoryItem.Make( owner, ( ( useSideItem ) ? ( data.option2SideItemData ) : ( data.option2GameItemData ) ), inventorySystem.GetInventoryItemsManager() );
			if( upgradeQualityValue >= 4 )
			{
				data.option3GameItemData = GetCyberwareUpgradeItemDataWithOffset( owner, upgradeItemRecord, statsShardTDBID, seed, 2 );
				data.option3InventoryItem = UIInventoryItem.Make( owner, ( ( useSideItem ) ? ( data.option3SideItemData ) : ( data.option3GameItemData ) ), inventorySystem.GetInventoryItemsManager() );
			}
		}
		data.baseItemData = itemData;
		return data;
	}

	public static function CreateReplaceEquipmentRequest( owner : GameObject, equippedItemID : ItemID, upgradeItemID : ItemID, optional partItemID : ItemID )
	{
		var replaceEquipmentRequest : ReplaceEquipmentRequest;
		var equipmentSystem : EquipmentSystem;
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( owner.GetGame() ).Get( 'EquipmentSystem' ) ) );
		replaceEquipmentRequest = new ReplaceEquipmentRequest;
		replaceEquipmentRequest.owner = owner;
		replaceEquipmentRequest.slotIndex = equipmentSystem.GetItemSlotIndex( owner, equippedItemID );
		replaceEquipmentRequest.itemID = upgradeItemID;
		replaceEquipmentRequest.addToInventory = true;
		replaceEquipmentRequest.rerollIdOnAddToInventory = false;
		replaceEquipmentRequest.removeOldItem = true;
		replaceEquipmentRequest.customPartToGenerateID = partItemID;
		replaceEquipmentRequest.transferInstalledParts = true;
		equipmentSystem.QueueRequest( replaceEquipmentRequest );
	}

	public static function HandleBuyShardPopupClosed( owner : GameObject, equippedItemId : ItemID, resultData : RipperdocTokenPopupCloseData )
	{
		var statsShardItemID : ItemID;
		var statsShardData : InnerItemData;
		var statsShardSlotTDBID : TweakDBID;
		if( resultData.confirm )
		{
			statsShardSlotTDBID = T"AttachmentSlots.StatsShardSlot";
			if( resultData.chosenOptionData.HasPartInSlot( statsShardSlotTDBID ) )
			{
				resultData.chosenOptionData.GetItemPart( statsShardData, statsShardSlotTDBID );
				statsShardItemID = InnerItemData.GetItemID( statsShardData );
			}
			CreateReplaceEquipmentRequest( owner, equippedItemId, resultData.chosenOptionData.GetID(), statsShardItemID );
			GameInstance.GetTransactionSystem( owner.GetGame() ).RemoveItemByTDBID( owner, resultData.costData.materialRecordID, resultData.costData.materialCount );
			GameInstance.GetInventoryManager( owner.GetGame() ).IncrementCyberwareUpgradeSeed( 3 );
		}
	}

	public static function IsItemEffectivelyIdentical( itemID1 : ItemID, itemID2 : ItemID ) : Bool
	{
		if( ( ItemID.GetTDBID( itemID1 ) == ItemID.GetTDBID( itemID2 ) ) && ( ItemID.GetRngSeed( itemID1 ) == ItemID.GetRngSeed( itemID2 ) ) )
		{
			return true;
		}
		return false;
	}

	public static function HealPuppetAfterQuickhack( gi : GameInstance, executor : GameObject )
	{
		var statSystem : StatsSystem;
		statSystem = GameInstance.GetStatsSystem( gi );
		if( statSystem.GetStatBoolValue( ( ( ScriptedPuppet )( executor ) ).GetEntityID(), gamedataStatType.CanQuickhackHealPuppet ) )
		{
			GameInstance.GetStatusEffectSystem( gi ).ApplyStatusEffect( ( ( ScriptedPuppet )( executor ) ).GetEntityID(), T"BaseStatusEffect.QuickhackConsumableHealing" );
		}
	}

	public static function ForceEquipStrongArms( player : PlayerPuppet, instantStatChange : Bool ) : Bool
	{
		var TS : TransactionSystem;
		var armsCW : ItemID;
		var record : WeaponItem_Record;
		var itemData : gameItemData;
		if( !( player ) )
		{
			return false;
		}
		TS = GameInstance.GetTransactionSystem( player.GetGame() );
		armsCW = EquipmentSystem.GetData( player ).GetActiveItem( gamedataEquipmentArea.ArmsCW );
		if( !( TS ) || !( ItemID.IsValid( armsCW ) ) )
		{
			return false;
		}
		record = TweakDBInterface.GetWeaponItemRecord( ItemID.GetTDBID( armsCW ) );
		if( record && record.ItemType().Type() == gamedataItemType.Cyb_StrongArms )
		{
			if( TS.IsSlotEmpty( player, T"AttachmentSlots.WeaponRight" ) && !( TS.IsSlotEmpty( player, T"AttachmentSlots.RightArm" ) ) )
			{
				if( instantStatChange )
				{
					EquipmentSystemPlayerData.UpdateArmSlot( player, armsCW, false );
					itemData = RPGManager.GetItemData( player.GetGame(), player, armsCW );
					itemData.AddStatsOnEquip( player );
				}
				TS.AddItemToSlot( player, T"AttachmentSlots.RightArm", armsCW );
				return true;
			}
		}
		return false;
	}

	public static function ForceUnequipStrongArms( player : PlayerPuppet ) : Bool
	{
		var TS : TransactionSystem;
		var armsCW : ItemID;
		var record : WeaponItem_Record;
		if( !( player ) )
		{
			return false;
		}
		TS = GameInstance.GetTransactionSystem( player.GetGame() );
		armsCW = EquipmentSystem.GetData( player ).GetActiveItem( gamedataEquipmentArea.ArmsCW );
		if( !( TS ) || !( ItemID.IsValid( armsCW ) ) )
		{
			return false;
		}
		record = TweakDBInterface.GetWeaponItemRecord( ItemID.GetTDBID( armsCW ) );
		if( record && record.ItemType().Type() == gamedataItemType.Cyb_StrongArms )
		{
			EquipmentSystemPlayerData.UpdateArmSlot( player, armsCW, true );
			return true;
		}
		return false;
	}

	public static function ForceEquipPersonalLink( player : PlayerPuppet ) : Bool
	{
		var TS : TransactionSystem;
		var itemID : ItemID;
		var playerStateMachineBlackboard : IBlackboard;
		var playerPuppet : weak< PlayerPuppet >;
		if( !( player ) )
		{
			return false;
		}
		TS = GameInstance.GetTransactionSystem( player.GetGame() );
		itemID = ItemID.CreateQuery( T"Items.personal_link" );
		if( !( TS ) || !( ItemID.IsValid( itemID ) ) )
		{
			return false;
		}
		if( !( TS.HasItem( player, itemID ) ) )
		{
			TS.GiveItem( player, itemID, 1 );
		}
		TS.RemoveItemFromSlot( player, T"AttachmentSlots.PersonalLink" );
		TS.AddItemToSlot( player, T"AttachmentSlots.PersonalLink", itemID );
		playerPuppet = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( player.GetGame() ).GetLocalPlayerControlledGameObject() ) );
		playerStateMachineBlackboard = GameInstance.GetBlackboardSystem( player.GetGame() ).GetLocalInstanced( playerPuppet.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.IsInteractingViaPersonalLink, true );
		return true;
	}

	public static function ForceUnequipPersonalLink( player : PlayerPuppet ) : Bool
	{
		var TS : TransactionSystem;
		var playerStateMachineBlackboard : IBlackboard;
		var playerPuppet : weak< PlayerPuppet >;
		if( !( player ) )
		{
			return false;
		}
		TS = GameInstance.GetTransactionSystem( player.GetGame() );
		if( !( TS ) )
		{
			return false;
		}
		TS.RemoveItemFromSlot( player, T"AttachmentSlots.PersonalLink" );
		playerPuppet = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( player.GetGame() ).GetLocalPlayerControlledGameObject() ) );
		playerStateMachineBlackboard = GameInstance.GetBlackboardSystem( player.GetGame() ).GetLocalInstanced( playerPuppet.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		playerStateMachineBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.IsInteractingViaPersonalLink, false );
		return true;
	}

}

abstract class MathHelper
{

	public constexpr static function PositiveInfinity() : Float
	{
		return 100000000.0;
	}

	public constexpr static function NegativeInfinity() : Float
	{
		return -100000000.0;
	}

	public constexpr static function EulerNumber() : Float
	{
		return 2.71828175;
	}

	public static function IsFloatInRange( value : Float, min : Float, max : Float, optional leftClosed : Bool, optional rightClosed : Bool ) : Bool
	{
		if( leftClosed && rightClosed )
		{
			return ( value >= min ) && ( value <= max );
		}
		if( leftClosed && !( rightClosed ) )
		{
			return ( value >= min ) && ( value < max );
		}
		if( !( leftClosed ) && rightClosed )
		{
			return ( value > min ) && ( value <= max );
		}
		return ( value > min ) && ( value < max );
	}

	public static function NormalizeF( value : Float, min : Float, max : Float ) : Float
	{
		var numerator : Float;
		var denominator : Float;
		numerator = value - min;
		denominator = max - min;
		return numerator / denominator;
	}

	public static function RandFromNormalDist( optional mean : Float, optional stdDev : Float ) : Float
	{
		var output : Float;
		var uniform1, uniform2 : Float;
		var randStandardNormal : Float;
		if( stdDev == 0.0 )
		{
			stdDev = 1.0;
		}
		uniform1 = RandF();
		uniform2 = RandF();
		randStandardNormal = SqrtF( -2.0 * LogF( uniform1 ) ) * CosF( ( 2.0 * Pi() ) * uniform2 );
		output = ( randStandardNormal * stdDev ) + mean;
		return output;
	}

}

class StatusEffectTriggerListener extends CustomValueStatPoolsListener
{
	var m_owner : weak< GameObject >;
	var m_statusEffect : TweakDBID;
	var m_statPoolType : gamedataStatPoolType;
	var m_instigator : weak< GameObject >;

	protected event OnStatPoolMinValueReached( value : Float )
	{
		var puppet : ScriptedPuppet;
		var gameInstance : GameInstance;
		puppet = ( ( ScriptedPuppet )( m_owner ) );
		if( puppet )
		{
			gameInstance = puppet.GetGame();
			GameInstance.GetStatusEffectSystem( gameInstance ).ApplyStatusEffect( puppet.GetEntityID(), m_statusEffect, GameObject.GetTDBID( m_instigator ), m_instigator.GetEntityID() );
			GameInstance.GetStatPoolsSystem( gameInstance ).RequestRemovingStatPool( puppet.GetEntityID(), m_statPoolType );
			GameObject.RemoveStatusEffectTriggerListener( puppet, this );
		}
	}

}

class PhoneCallUploadDurationListener extends CustomValueStatPoolsListener
{
	var m_gameInstance : GameInstance;
	var m_requesterPuppet : weak< ScriptedPuppet >;
	var m_requesterID : EntityID;
	var m_duration : Float;
	var m_statPoolType : gamedataStatPoolType;
	default m_statPoolType = gamedataStatPoolType.PhoneCallDuration;

	protected event OnStatPoolAdded()
	{
		SendUploadStartedEvent();
		SetRegenBehavior();
	}

	protected virtual function SetRegenBehavior()
	{
		var activationTime : Float;
		var regenMod : StatPoolModifier;
		activationTime = m_duration;
		regenMod.enabled = true;
		regenMod.valuePerSec = 100.0 / activationTime;
		regenMod.rangeEnd = 100.0;
		GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestSettingModifier( m_requesterID, m_statPoolType, gameStatPoolModificationTypes.Regeneration, regenMod );
	}

	protected event OnStatPoolRemoved()
	{
		SendUploadFinishedEvent();
		UnregisterListener();
	}

	protected event OnStatPoolMaxValueReached( value : Float )
	{
		if( m_statPoolType == gamedataStatPoolType.CallReinforcementProgress )
		{
			m_requesterPuppet.GetPuppetStateBlackboard().SetBool( GetAllBlackboardDefs().PuppetState.HasCalledReinforcements, true );
		}
		SendUploadFinishedEvent();
		UnregisterListener();
	}

	private function UnregisterListener()
	{
		GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestUnregisteringListener( m_requesterID, m_statPoolType, this );
	}

	private function SendUploadStartedEvent()
	{
		var evt : UploadProgramProgressEvent;
		evt = new UploadProgramProgressEvent;
		evt.state = EUploadProgramState.STARTED;
		evt.duration = m_duration;
		evt.progressBarType = EProgressBarType.UPLOAD;
		evt.progressBarContext = EProgressBarContext.PhoneCall;
		evt.iconRecord = TweakDBInterface.GetChoiceCaptionIconPartRecord( T"ChoiceCaptionParts.PhoneCall" );
		evt.statPoolType = m_statPoolType;
		GameInstance.GetPersistencySystem( m_gameInstance ).QueueEntityEvent( m_requesterID, evt );
	}

	protected function SendUploadFinishedEvent()
	{
		var evt : UploadProgramProgressEvent;
		evt = new UploadProgramProgressEvent;
		evt.state = EUploadProgramState.COMPLETED;
		evt.progressBarContext = EProgressBarContext.PhoneCall;
		evt.statPoolType = m_statPoolType;
		GameInstance.GetPersistencySystem( m_gameInstance ).QueueEntityEvent( m_requesterID, evt );
	}

}

class ActionUploadListener extends CustomValueStatPoolsListener
{
	var m_action : ScriptableDeviceAction;
	var m_gameInstance : GameInstance;
}

class QuickHackDurationListener extends ActionUploadListener
{

	protected event OnStatPoolAdded()
	{
		SendUploadStartedEvent( m_action );
		SetRegenBehavior();
	}

	protected virtual function SetRegenBehavior()
	{
		var activationTime : Float;
		var regenMod : StatPoolModifier;
		activationTime = m_action.GetDurationValue();
		regenMod.enabled = true;
		regenMod.valuePerSec = 100.0 / activationTime;
		regenMod.rangeEnd = 100.0;
		GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestSettingModifier( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackDuration, gameStatPoolModificationTypes.Regeneration, regenMod );
	}

	protected event OnStatPoolMaxValueReached( value : Float )
	{
		m_action.CompleteAction( m_gameInstance );
		GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestRemovingStatPool( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackDuration );
		GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestUnregisteringListener( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackDuration, this );
		SendUploadFinishedEvent();
	}

	protected virtual function SendUploadStartedEvent( action : ScriptableDeviceAction )
	{
		var evt : UploadProgramProgressEvent;
		var uploadDuration : Float;
		evt = new UploadProgramProgressEvent;
		uploadDuration = m_action.GetDurationValue();
		evt.state = EUploadProgramState.STARTED;
		evt.duration = uploadDuration;
		evt.progressBarType = EProgressBarType.DURATION;
		evt.action = action;
		evt.iconRecord = action.GetInteractionIcon();
		evt.statPoolType = gamedataStatPoolType.QuickHackDuration;
		GameInstance.GetPersistencySystem( m_gameInstance ).QueueEntityEvent( m_action.GetRequesterID(), evt );
	}

	protected function SendUploadFinishedEvent()
	{
		var evt : UploadProgramProgressEvent;
		evt = new UploadProgramProgressEvent;
		evt.state = EUploadProgramState.COMPLETED;
		evt.action = m_action;
		evt.statPoolType = gamedataStatPoolType.QuickHackDuration;
		GameInstance.GetPersistencySystem( m_gameInstance ).QueueEntityEvent( m_action.GetRequesterID(), evt );
	}

}

class QuickHackUploadListener extends ActionUploadListener
{

	public virtual function Initialize() {}

	protected event OnStatPoolAdded()
	{
		var psmBB : IBlackboard;
		var enableQHSoundActivation : Bool;
		enableQHSoundActivation = true;
		if( m_action.IsQuickHack() )
		{
			SendUploadStartedEvent( m_action );
			psmBB = GameInstance.GetBlackboardSystem( m_gameInstance ).GetLocalInstanced( m_action.GetExecutor().GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
			if( psmBB )
			{
				enableQHSoundActivation = psmBB.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Vision ) == ( ( Int32 )( gamePSMVision.Focus ) );
			}
			if( enableQHSoundActivation )
			{
				PlayQuickHackSound( 'ui_focus_mode_scanning_qh' );
			}
		}
		SetRegenBehavior();
	}

	protected virtual function SetRegenBehavior()
	{
		var activationTime : Float;
		var regenMod : StatPoolModifier;
		activationTime = m_action.GetActivationTime();
		regenMod.enabled = true;
		regenMod.valuePerSec = 100.0 / activationTime;
		regenMod.rangeEnd = 100.0;
		GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestSettingModifier( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackUpload, gameStatPoolModificationTypes.Regeneration, regenMod );
	}

	protected event OnStatPoolMaxValueReached( value : Float )
	{
		m_action.CompleteAction( m_gameInstance );
		GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestRemovingStatPool( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackUpload );
		GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestUnregisteringListener( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackUpload, this );
		if( m_action.IsQuickHack() )
		{
			SendUploadFinishedEvent();
		}
	}

	protected virtual function SendUploadStartedEvent( action : ScriptableDeviceAction )
	{
		var evt : UploadProgramProgressEvent;
		var uploadDuration : Float;
		evt = new UploadProgramProgressEvent;
		uploadDuration = m_action.GetActivationTime();
		evt.state = EUploadProgramState.STARTED;
		evt.duration = uploadDuration;
		evt.progressBarType = EProgressBarType.UPLOAD;
		evt.action = action;
		evt.iconRecord = action.GetInteractionIcon();
		evt.statPoolType = gamedataStatPoolType.QuickHackUpload;
		GameInstance.GetPersistencySystem( m_gameInstance ).QueueEntityEvent( m_action.GetRequesterID(), evt );
		QuickhackModule.RequestRefreshQuickhackMenu( m_gameInstance, m_action.GetRequesterID() );
	}

	protected function SendUploadFinishedEvent()
	{
		var evt : UploadProgramProgressEvent;
		evt = new UploadProgramProgressEvent;
		evt.state = EUploadProgramState.COMPLETED;
		evt.action = m_action;
		if( m_action.m_isActionQueueingUsed )
		{
			evt.deviceActionQueue = m_action.m_deviceActionQueue;
		}
		evt.statPoolType = gamedataStatPoolType.QuickHackUpload;
		GameInstance.GetPersistencySystem( m_gameInstance ).QueueEntityEvent( m_action.GetRequesterID(), evt );
		PlayQuickHackSound( 'ui_focus_mode_scanning_qh_done' );
		if( m_action.GetExecutor().IsPlayer() )
		{
			RPGManager.DecrementQuickHackBlackboard( m_gameInstance, m_action.GetObjectActionID() );
		}
	}

	protected function PlayQuickHackSound( eventName : CName )
	{
		var flag : audioAudioEventFlags;
		flag = audioAudioEventFlags.Unique;
		GameObject.PlaySoundEventWithParams( m_action.GetExecutor(), eventName, flag );
	}

	protected function RemoveLink( owner : weak< ScriptedPuppet > )
	{
		var evt : RemoveLinkEvent;
		if( !( owner ) )
		{
			return;
		}
		evt = new RemoveLinkEvent;
		owner.QueueEvent( evt );
	}

	protected function RemoveLinkedStatusEffects( owner : weak< ScriptedPuppet >, optional ssAction : Bool )
	{
		var evt : RemoveLinkedStatusEffectsEvent;
		if( !( owner ) )
		{
			return;
		}
		evt = new RemoveLinkedStatusEffectsEvent;
		evt.ssAction = ssAction;
		owner.QueueEvent( evt );
	}

}

class AccessBreachListener extends QuickHackUploadListener
{
}

class UploadFromNPCToNPCListener extends QuickHackUploadListener
{
	var m_npcPuppet : weak< ScriptedPuppet >;

	public override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		var uploadEvent : UploadProgramProgressEvent;
		if( !( NPCPuppet.IsActive( m_npcPuppet ) ) )
		{
			RemoveLinkedStatusEffects( m_npcPuppet );
			GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestRemovingStatPool( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackUpload );
			GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestUnregisteringListener( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackUpload, this );
			uploadEvent = new UploadProgramProgressEvent;
			uploadEvent.state = EUploadProgramState.COMPLETED;
			GameInstance.GetPersistencySystem( m_gameInstance ).QueueEntityEvent( m_action.GetRequesterID(), uploadEvent );
		}
	}

}

class UploadFromNPCToPlayerListener extends QuickHackUploadListener
{
	var m_playerPuppet : weak< ScriptedPuppet >;
	var m_npcPuppet : weak< ScriptedPuppet >;
	var m_npcSquad : array< EntityID >;
	var m_HUDData : HUDProgressBarData;
	var m_hudBlackboard : weak< IBlackboard >;
	private var m_startUploadTimeStamp : Float;
	private var m_ssAction : Bool;
	private var m_preventionHackLoopAction : Bool;
	private var m_shouldStopRevealOnPreventionDeescalation : Bool;
	private var m_squadScriptInterface : SquadScriptInterface;
	private var m_useInterruptionPrereqs : Bool;

	public override function Initialize()
	{
		var isPositionReveal : Bool;
		var isPreventionPositionReveal : Bool;
		var tbid : TweakDBID;
		tbid = m_action.GetObjectActionID();
		isPositionReveal = tbid == T"AIQuickHack.HackRevealPosition";
		isPreventionPositionReveal = m_action.GetObjectActionID() == T"AIQuickHack.PreventionHackRevealPosition";
		m_useInterruptionPrereqs = m_action.GetObjectActionRecord().GetInterruptionPrereqsCount() > 0;
		m_hudBlackboard = GameInstance.GetBlackboardSystem( m_gameInstance ).Get( GetAllBlackboardDefs().UI_HUDProgressBar );
		if( isPositionReveal || isPreventionPositionReveal )
		{
			m_ssAction = true;
			m_shouldStopRevealOnPreventionDeescalation = isPreventionPositionReveal;
			AISquadHelper.GetSquadmatesID( m_npcPuppet, m_npcSquad );
			AISquadHelper.GetSquadMemberInterface( m_npcPuppet, m_squadScriptInterface );
		}
		else if( ( ( m_action.GetObjectActionID() == T"AIQuickHack.PreventionSystemHackerLoop" ) || ( m_action.GetObjectActionID() == T"AIQuickHack.PreventionSystemHackerLoop2" ) ) || ( m_action.GetObjectActionID() == T"AIQuickHack.PreventionSystemHackerLoop3" ) )
		{
			m_preventionHackLoopAction = true;
		}
	}

	protected event OnStatPoolAdded()
	{
		SendUploadStartedEvent( m_action );
		SaveLocksManager.RequestSaveLockAdd( m_playerPuppet.GetGame(), 'PlayerBeingHacked' );
	}

	protected override function SendUploadStartedEvent( action : ScriptableDeviceAction )
	{
		var isQuickhackPanelOpen : Bool;
		m_HUDData.active = true;
		m_HUDData.header = LocKeyToString( action.GetObjectActionRecord().ObjectActionUI().Caption() );
		if( m_preventionHackLoopAction )
		{
			m_HUDData.bottomText = "LocKey#22169";
			m_HUDData.failedText = "LocKey#92701";
			m_HUDData.completedText = "LocKey#92701";
			m_HUDData.type = SimpleMessageType.Vehicle;
		}
		m_startUploadTimeStamp = EngineTime.ToFloat( GameInstance.GetSimTime( m_npcPuppet.GetGame() ) );
		m_hudBlackboard.SetBool( GetAllBlackboardDefs().UI_HUDProgressBar.Active, m_HUDData.active );
		m_hudBlackboard.SetString( GetAllBlackboardDefs().UI_HUDProgressBar.Header, m_HUDData.header );
		m_hudBlackboard.SetString( GetAllBlackboardDefs().UI_HUDProgressBar.BottomText, m_HUDData.bottomText, true );
		m_hudBlackboard.SetString( GetAllBlackboardDefs().UI_HUDProgressBar.FailedText, m_HUDData.failedText, true );
		m_hudBlackboard.SetString( GetAllBlackboardDefs().UI_HUDProgressBar.CompletedText, m_HUDData.completedText, true );
		m_hudBlackboard.SetVariant( GetAllBlackboardDefs().UI_HUDProgressBar.MessageType, m_HUDData.type, true );
		isQuickhackPanelOpen = GameInstance.GetBlackboardSystem( m_gameInstance ).Get( GetAllBlackboardDefs().UI_QuickSlotsData ).GetBool( GetAllBlackboardDefs().UI_QuickSlotsData.quickhackPanelOpen );
		if( isQuickhackPanelOpen )
		{
			GameInstance.GetDelaySystem( m_gameInstance ).DelayEventNextFrame( m_playerPuppet, new RefreshQuickhackMenuEvent );
		}
	}

	public function ForceClose()
	{
		OnStatPoolMaxValueReached( 100.0 );
	}

	private function TryStartCombat( player : weak< ScriptedPuppet >, npc : weak< ScriptedPuppet >, targetTracker : weak< TargetTrackingExtension > )
	{
		if( AIActionHelper.TryChangingAttitudeToHostile( npc, player ) )
		{
			targetTracker.AddThreat( player, true, player.GetWorldPosition(), 1.0, -1.0, false );
			m_npcPuppet.TriggerSecuritySystemNotification( player.GetWorldPosition(), player, ESecurityNotificationType.COMBAT );
		}
	}

	protected event OnStatPoolMaxValueReached( value : Float )
	{
		var actionEffects : array< weak< ObjectActionEffect_Record > >;
		var targetTracker : weak< TargetTrackingExtension >;
		var i : Int32;
		if( m_preventionHackLoopAction && PreventionSystemHackerLoop.KeepProgressBarAliveAfterCompletion( m_gameInstance ) )
		{
			return false;
		}
		PreventionSystemHackerLoop.UpdateOtherProgressBarReference( m_gameInstance, NULL );
		super.OnStatPoolMaxValueReached( value );
		RemoveLink( m_npcPuppet );
		ScriptedPuppet.SendActionSignal( m_npcPuppet, 'HackingCompleted', 1.0 );
		m_HUDData.active = false;
		m_hudBlackboard.SetBool( GetAllBlackboardDefs().UI_HUDProgressBar.Active, m_HUDData.active );
		m_action.GetObjectActionRecord().CompletionEffects( actionEffects );
		m_npcPuppet.AddLinkedStatusEffect( m_npcPuppet.GetEntityID(), m_playerPuppet.GetEntityID(), actionEffects );
		m_playerPuppet.AddLinkedStatusEffect( m_npcPuppet.GetEntityID(), m_playerPuppet.GetEntityID(), actionEffects );
		SaveLocksManager.RequestSaveLockRemove( m_playerPuppet.GetGame(), 'PlayerBeingHacked' );
		StatusEffectHelper.RemoveStatusEffect( m_playerPuppet, T"AIQuickHackStatusEffect.BeingHacked" );
		if( m_ssAction )
		{
			m_playerPuppet.RemoveLinkedStatusEffects( true );
			if( !( TargetTrackingExtension.Get( ( ( ScriptedPuppet )( GameInstance.FindEntityByID( m_playerPuppet.GetGame(), m_npcSquad[ 0 ] ) ) ), targetTracker ) ) )
			{
				if( !( TargetTrackingExtension.Get( m_npcPuppet, targetTracker ) ) )
				{
					return false;
				}
			}
			TryStartCombat( m_playerPuppet, m_npcPuppet, targetTracker );
			for( i = 0; i < m_npcSquad.Size(); i += 1 )
			{
				TryStartCombat( m_playerPuppet, ( ( ScriptedPuppet )( GameInstance.FindEntityByID( m_playerPuppet.GetGame(), m_npcSquad[ i ] ) ) ), targetTracker );
			}
		}
	}

	private function IsSquadAlive() : Bool
	{
		var squadmates : array< weak< Entity > >;
		var i : Int32;
		var squadmate : weak< ScriptedPuppet >;
		if( !( m_squadScriptInterface ) )
		{
			return false;
		}
		squadmates = m_squadScriptInterface.ListMembersWeak();
		for( i = 0; i < squadmates.Size(); i += 1 )
		{
			squadmate = ( ( ScriptedPuppet )( squadmates[ i ] ) );
			if( ScriptedPuppet.IsActive( squadmate ) )
			{
				return true;
			}
		}
		return false;
	}

	private function StopHackBasedOnTier( currentTier : Int32 ) : Bool
	{
		var allowedInTier2 : Bool;
		var allowedInTier3 : Bool;
		var allowedInTier4 : Bool;
		var allowedInTier5 : Bool;
		allowedInTier2 = StatusEffectSystem.ObjectHasStatusEffectWithTag( ( ( GameObject )( m_playerPuppet ) ), 'AllowTracingInTier2' );
		allowedInTier3 = StatusEffectSystem.ObjectHasStatusEffectWithTag( ( ( GameObject )( m_playerPuppet ) ), 'AllowTracingInTier3' );
		allowedInTier4 = StatusEffectSystem.ObjectHasStatusEffectWithTag( ( ( GameObject )( m_playerPuppet ) ), 'AllowTracingInTier4' );
		allowedInTier5 = StatusEffectSystem.ObjectHasStatusEffectWithTag( ( ( GameObject )( m_playerPuppet ) ), 'AllowTracingInTier5' );
		if( currentTier == ( ( Int32 )( gamePSMHighLevel.SceneTier2 ) ) )
		{
			return !( allowedInTier2 );
		}
		else if( currentTier == ( ( Int32 )( gamePSMHighLevel.SceneTier3 ) ) )
		{
			return !( allowedInTier3 );
		}
		else if( currentTier == ( ( Int32 )( gamePSMHighLevel.SceneTier4 ) ) )
		{
			return !( allowedInTier4 );
		}
		else if( currentTier == ( ( Int32 )( gamePSMHighLevel.SceneTier5 ) ) )
		{
			return !( allowedInTier5 );
		}
		else if( currentTier == ( ( Int32 )( gamePSMHighLevel.Swimming ) ) )
		{
			return true;
		}
		return false;
	}

	public override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		var evt : UploadProgramProgressEvent;
		var quickhackShieldValue : Float;
		var immune : Bool;
		var stopHack : Bool;
		var playerDetected : Bool;
		var playerIsInCombat : Bool;
		var hackingMinigameBB : IBlackboard;
		var psmBB : IBlackboard;
		var statSystem : StatsSystem;
		var preventionSystem : PreventionSystem;
		var maxRevealDistance : Float;
		var sceneTier : Int32;
		playerDetected = false;
		playerIsInCombat = ( ( PlayerPuppet )( m_playerPuppet ) ).IsInCombat();
		statSystem = GameInstance.GetStatsSystem( m_gameInstance );
		preventionSystem = ( ( PreventionSystem )( GameInstance.GetScriptableSystemsContainer( m_gameInstance ).Get( 'PreventionSystem' ) ) );
		quickhackShieldValue = GameInstance.GetStatsSystem( m_playerPuppet.GetGame() ).GetStatValue( m_playerPuppet.GetEntityID(), gamedataStatType.QuickhackShield );
		if( m_preventionHackLoopAction )
		{
			if( PreventionSystemHackerLoop.ShouldForceUpdateProgressBar( m_gameInstance ) )
			{
				newValue += PreventionSystemHackerLoop.GetProgressBarForcedValue( m_gameInstance );
				if( newValue > 99.98999786 )
				{
					newValue = 100.0;
				}
			}
			PreventionSystemHackerLoop.UpdateOtherProgressBarReference( m_gameInstance, NULL );
			PreventionSystemHackerLoop.UpdateHackLoopProgressBarValue( m_gameInstance, newValue, this );
		}
		else
		{
			PreventionSystemHackerLoop.UpdateOtherProgressBarReference( m_gameInstance, this );
		}
		m_HUDData.progress = newValue / 100.0;
		m_hudBlackboard.SetFloat( GetAllBlackboardDefs().UI_HUDProgressBar.Progress, m_HUDData.progress );
		if( ( ( !( m_ssAction ) && ( newValue > 15.0 ) ) && ( quickhackShieldValue > 0.0 ) ) && !( StatusEffectSystem.ObjectHasStatusEffect( m_playerPuppet, T"BaseStatusEffect.AntiVirusCooldown" ) ) )
		{
			StatusEffectHelper.ApplyStatusEffect( ( ( GameObject )( m_playerPuppet ) ), T"BaseStatusEffect.AntiVirusCooldown" );
			immune = true;
		}
		if( m_useInterruptionPrereqs )
		{
			stopHack = immune || m_action.CanInterrupt( m_npcPuppet );
		}
		else
		{
			if( m_ssAction )
			{
				hackingMinigameBB = GameInstance.GetBlackboardSystem( m_playerPuppet.GetGame() ).Get( GetAllBlackboardDefs().HackingMinigame );
				psmBB = ( ( PlayerPuppet )( m_playerPuppet ) ).GetPlayerStateMachineBlackboard();
				maxRevealDistance = statSystem.GetStatValue( m_playerPuppet.GetEntityID(), gamedataStatType.RevealPositionMaxDistance );
				sceneTier = psmBB.GetInt( GetAllBlackboardDefs().PlayerStateMachine.HighLevel );
				if( ( ( ( ( ( !( ScriptedPuppet.IsActive( m_playerPuppet ) ) || playerIsInCombat ) || ( Vector4.Distance( m_playerPuppet.GetWorldPosition(), hackingMinigameBB.GetVector4( GetAllBlackboardDefs().HackingMinigame.LastPlayerHackPosition ) ) > maxRevealDistance ) ) || StopHackBasedOnTier( sceneTier ) ) || ( !( ScriptedPuppet.IsActive( m_npcPuppet ) ) && !( IsSquadAlive() ) ) ) || ( m_shouldStopRevealOnPreventionDeescalation && !( preventionSystem.IsChasingPlayer() ) ) ) || GameInstance.GetStatusEffectSystem( m_playerPuppet.GetGame() ).HasStatusEffect( m_playerPuppet.GetEntityID(), T"BaseStatusEffect.RevealInterrupted" ) )
				{
					stopHack = true;
					playerDetected = playerIsInCombat;
				}
			}
			else if( ( ( ( immune || !( ScriptedPuppet.IsActive( m_npcPuppet ) ) ) || !( ScriptedPuppet.IsActive( m_playerPuppet ) ) ) || ( ( m_action.GetObjectActionID() != T"AIQuickHack.HackReveal" ) && ( ( StatusEffectSystem.ObjectHasStatusEffect( m_npcPuppet, T"AIQuickHackStatusEffect.HackingInterrupted" ) || StatusEffectSystem.ObjectHasStatusEffect( m_npcPuppet, T"AIQuickHackStatusEffect.HackRevealInterrupted" ) ) || StatusEffectSystem.ObjectHasStatusEffectWithTag( m_npcPuppet, 'CyberwareMalfunction' ) ) ) ) || ( ( m_action.GetObjectActionID() == T"AIQuickHack.HackReveal" ) && playerIsInCombat ) )
			{
				stopHack = true;
			}
		}
		if( stopHack )
		{
			PreventionSystemHackerLoop.UpdateOtherProgressBarReference( m_gameInstance, NULL );
			if( m_preventionHackLoopAction && ( newValue > 99.98999786 ) )
			{
				return;
			}
			StatusEffectHelper.RemoveStatusEffect( m_playerPuppet, T"BaseStatusEffect.RevealInterrupted" );
			StatusEffectHelper.RemoveStatusEffect( m_playerPuppet, T"BaseStatusEffect.ForcedQHUploadAwarenessBumps" );
			( ( PlayerPuppet )( m_playerPuppet ) ).SetIsBeingRevealed( false );
			immune = false;
			RemoveLink( m_npcPuppet );
			RemoveLinkedStatusEffects( m_npcPuppet, m_ssAction );
			SaveLocksManager.RequestSaveLockRemove( m_playerPuppet.GetGame(), 'PlayerBeingHacked' );
			m_HUDData.failedText = ( ( playerDetected || ( ( m_action.GetObjectActionID() == T"AIQuickHack.HackReveal" ) && playerIsInCombat ) ) ? ( "LocKey#92985" ) : ( "LocKey#15353" ) );
			m_HUDData.active = false;
			m_hudBlackboard.SetString( GetAllBlackboardDefs().UI_HUDProgressBar.FailedText, m_HUDData.failedText );
			m_hudBlackboard.SetBool( GetAllBlackboardDefs().UI_HUDProgressBar.Active, m_HUDData.active );
			GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestRemovingStatPool( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackUpload );
			GameInstance.GetStatPoolsSystem( m_gameInstance ).RequestUnregisteringListener( m_action.GetRequesterID(), gamedataStatPoolType.QuickHackUpload, this );
			evt = new UploadProgramProgressEvent;
			evt.state = EUploadProgramState.COMPLETED;
			GameInstance.GetPersistencySystem( m_gameInstance ).QueueEntityEvent( m_action.GetRequesterID(), evt );
		}
	}

}

struct PlayerQuickhackData
{
	var itemID : ItemID;
	var actionRecord : weak< ObjectAction_Record >;
	var quality : Int32;
}

import enum gameEPowerDifferential
{
	IMPOSSIBLE,
	HARD,
	NORMAL,
	EASY,
	TRASH,
}

operator>( q1 : gamedataQuality, q2 : gamedataQuality ) : Bool
{
	var result : Bool;
	if( ( ( q1 == gamedataQuality.Invalid || q1 == gamedataQuality.Count ) || q2 == gamedataQuality.Invalid ) || q2 == gamedataQuality.Count )
	{
		return false;
	}
	switch( q1 )
	{
		case gamedataQuality.Common:
			return false;
		case gamedataQuality.Uncommon:
			result = ( ( q2 == gamedataQuality.Common ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Rare:
			result = ( ( q2 == gamedataQuality.Common || q2 == gamedataQuality.Uncommon ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Epic:
			result = ( ( ( q2 == gamedataQuality.Common || q2 == gamedataQuality.Uncommon ) || q2 == gamedataQuality.Rare ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Legendary:
			result = ( ( ( ( q2 == gamedataQuality.Common || q2 == gamedataQuality.Uncommon ) || q2 == gamedataQuality.Rare ) || q2 == gamedataQuality.Epic ) ? ( true ) : ( false ) );
		break;
		default:
			return false;
	}
	return result;
}

operator>=( q1 : gamedataQuality, q2 : gamedataQuality ) : Bool
{
	var result : Bool;
	if( ( ( q1 == gamedataQuality.Invalid || q1 == gamedataQuality.Count ) || q2 == gamedataQuality.Invalid ) || q2 == gamedataQuality.Count )
	{
		return false;
	}
	switch( q1 )
	{
		case gamedataQuality.Common:
			result = ( ( q2 == gamedataQuality.Common ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Uncommon:
			result = ( ( q2 == gamedataQuality.Common || q2 == gamedataQuality.Uncommon ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Rare:
			result = ( ( ( q2 == gamedataQuality.Common || q2 == gamedataQuality.Uncommon ) || q2 == gamedataQuality.Rare ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Epic:
			result = ( ( ( ( q2 == gamedataQuality.Common || q2 == gamedataQuality.Uncommon ) || q2 == gamedataQuality.Rare ) || q2 == gamedataQuality.Epic ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Legendary:
			result = ( ( ( ( ( q2 == gamedataQuality.Common || q2 == gamedataQuality.Uncommon ) || q2 == gamedataQuality.Rare ) || q2 == gamedataQuality.Epic ) || q2 == gamedataQuality.Legendary ) ? ( true ) : ( false ) );
		break;
		default:
			return false;
	}
	return result;
}

operator<( q1 : gamedataQuality, q2 : gamedataQuality ) : Bool
{
	var result : Bool;
	if( ( ( q1 == gamedataQuality.Invalid || q1 == gamedataQuality.Count ) || q2 == gamedataQuality.Invalid ) || q2 == gamedataQuality.Count )
	{
		return false;
	}
	switch( q2 )
	{
		case gamedataQuality.Common:
			return false;
		case gamedataQuality.Uncommon:
			result = ( ( q1 == gamedataQuality.Common ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Rare:
			result = ( ( q1 == gamedataQuality.Common || q1 == gamedataQuality.Uncommon ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Epic:
			result = ( ( ( q1 == gamedataQuality.Common || q1 == gamedataQuality.Uncommon ) || q1 == gamedataQuality.Rare ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Legendary:
			result = ( ( ( ( q1 == gamedataQuality.Common || q1 == gamedataQuality.Uncommon ) || q1 == gamedataQuality.Rare ) || q1 == gamedataQuality.Epic ) ? ( true ) : ( false ) );
		break;
		default:
			return false;
	}
	return result;
}

operator<=( q1 : gamedataQuality, q2 : gamedataQuality ) : Bool
{
	var result : Bool;
	if( ( ( q1 == gamedataQuality.Invalid || q1 == gamedataQuality.Count ) || q2 == gamedataQuality.Invalid ) || q2 == gamedataQuality.Count )
	{
		return false;
	}
	switch( q2 )
	{
		case gamedataQuality.Common:
			result = ( ( q1 == gamedataQuality.Common ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Uncommon:
			result = ( ( q1 == gamedataQuality.Common || q1 == gamedataQuality.Uncommon ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Rare:
			result = ( ( ( q1 == gamedataQuality.Common || q1 == gamedataQuality.Uncommon ) || q1 == gamedataQuality.Rare ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Epic:
			result = ( ( ( ( q1 == gamedataQuality.Common || q1 == gamedataQuality.Uncommon ) || q1 == gamedataQuality.Rare ) || q1 == gamedataQuality.Epic ) ? ( true ) : ( false ) );
		break;
		case gamedataQuality.Legendary:
			result = ( ( ( ( ( q1 == gamedataQuality.Common || q1 == gamedataQuality.Uncommon ) || q1 == gamedataQuality.Rare ) || q1 == gamedataQuality.Epic ) || q1 == gamedataQuality.Legendary ) ? ( true ) : ( false ) );
		break;
		default:
			return false;
	}
	return result;
}

