abstract class LeftHandCyberwareHelper
{

	public static function ProjectileLauncherHasCharge( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var charges : Int32;
		charges = ( ( PlayerPuppet )( scriptInterface.executionOwner ) ).GetProjectileLauncherCharges();
		return charges > 0;
	}

	public static function IsProjectileLauncherEquipped( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var weapon : weak< WeaponObject >;
		weapon = ( ( WeaponObject )( scriptInterface.GetTransactionSystem().GetItemInSlot( scriptInterface.executionOwner, T"AttachmentSlots.WeaponLeft" ) ) );
		if( weapon && WeaponObject.IsOfType( weapon.GetItemID(), gamedataItemType.Cyb_Launcher ) )
		{
			return true;
		}
		return false;
	}

}

abstract class LeftHandCyberwareTransition extends DefaultTransition
{
	var m_leftCWFeature : AnimFeature_LeftHandCyberware;
	var m_overchargeStatFlag : gameStatModifierData;

	protected function CreateAndSendAnimFeatureData( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_leftCWFeature = new AnimFeature_LeftHandCyberware;
		m_leftCWFeature.actionDuration = stateContext.GetFloatParameter( 'actionDuration', true );
		m_leftCWFeature.state = stateContext.GetIntParameter( 'state', true );
		m_leftCWFeature.isQuickAction = stateContext.GetBoolParameter( 'isQuickAction', true );
		m_leftCWFeature.isChargeAction = stateContext.GetBoolParameter( 'isChargeAction', true );
		m_leftCWFeature.isLoopAction = stateContext.GetBoolParameter( 'isLoopAction', true );
		m_leftCWFeature.isCatchAction = stateContext.GetBoolParameter( 'isCatchAction', true );
		m_leftCWFeature.isSafeAction = stateContext.GetBoolParameter( 'isSafeAction', true );
		m_leftCWFeature.chargeNormalized = 0.0;
		m_leftCWFeature.hasCWPerk = false;
		scriptInterface.SetAnimationParameterFeature( 'LeftHandCyberware', m_leftCWFeature, scriptInterface.executionOwner );
	}

	protected function UpdateAndSendChargeAnimData( chargeAmount : Float, hasCWPerk : Bool, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_leftCWFeature.chargeNormalized = chargeAmount;
		m_leftCWFeature.hasCWPerk = hasCWPerk;
		scriptInterface.SetAnimationParameterFeature( 'LeftHandCyberware', m_leftCWFeature, scriptInterface.executionOwner );
	}

	protected function SetLeftHandItemTypeAndState( scriptInterface : StateGameScriptInterface, type : Int32, state : Int32 )
	{
		var itemHandling : AnimFeature_EquipUnequipItem;
		itemHandling = new AnimFeature_EquipUnequipItem;
		itemHandling.itemType = type;
		itemHandling.itemState = state;
		scriptInterface.SetAnimationParameterFeature( 'leftHandItemHandling', itemHandling, scriptInterface.executionOwner );
	}

	protected function LockLeftHandAnimation( scriptInterface : StateGameScriptInterface, newState : Bool )
	{
		var animFeature : AnimFeature_LeftHandAnimation;
		animFeature = new AnimFeature_LeftHandAnimation;
		animFeature.lockLeftHandAnimation = newState;
		scriptInterface.SetAnimationParameterFeature( 'LeftHandAnimation', animFeature, scriptInterface.executionOwner );
	}

	protected function SetAnimEquipState( scriptInterface : StateGameScriptInterface, newState : Bool )
	{
		var animFeature : AnimFeature_LeftHandItem;
		animFeature = new AnimFeature_LeftHandItem;
		animFeature.itemInLeftHand = newState;
		scriptInterface.SetAnimationParameterFeature( 'LeftHandItem', animFeature, scriptInterface.executionOwner );
	}

	protected const function GetProjectileTemplateNameFromWeaponDefinition( weaponTweak : TweakDBID ) : CName
	{
		return TDB.GetCName( weaponTweak + T".projectileTemplateName", '' );
	}

	protected const function GetEquipDuration( const scriptInterface : StateGameScriptInterface ) : Float
	{
		return scriptInterface.GetStatsSystem().GetStatValue( GetLeftHandWeaponObject( scriptInterface ).GetEntityID(), gamedataStatType.EquipDuration );
	}

	protected const function GetUnequipDuration( const scriptInterface : StateGameScriptInterface ) : Float
	{
		return scriptInterface.GetStatsSystem().GetStatValue( GetLeftHandWeaponObject( scriptInterface ).GetEntityID(), gamedataStatType.UnequipDuration );
	}

	protected const function LeftHandCyberwareHasTag( const scriptInterface : StateGameScriptInterface, tag : CName ) : Bool
	{
		var leftHandObject : weak< WeaponObject >;
		leftHandObject = GetLeftHandWeaponObject( scriptInterface );
		if( leftHandObject )
		{
			if( scriptInterface.GetTransactionSystem().HasTag( scriptInterface.executionOwner, tag, leftHandObject.GetItemID() ) )
			{
				return true;
			}
		}
		return false;
	}

	protected const function QuickwheelHasTag( const scriptInterface : StateGameScriptInterface, tag : CName ) : Bool
	{
		var itemID : ItemID;
		itemID = EquipmentSystem.GetData( scriptInterface.executionOwner ).GetActiveItem( gamedataEquipmentArea.QuickWheel );
		return scriptInterface.GetTransactionSystem().HasTag( scriptInterface.owner, tag, itemID );
	}

	protected const function GetLeftHandWeaponObject( const scriptInterface : StateGameScriptInterface ) : weak< WeaponObject >
	{
		var leftHandWpnObject : weak< WeaponObject >;
		leftHandWpnObject = ( ( WeaponObject )( scriptInterface.GetTransactionSystem().GetItemInSlot( scriptInterface.executionOwner, T"AttachmentSlots.WeaponLeft" ) ) );
		return leftHandWpnObject;
	}

	protected const function WeaponIsCharged( const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var weaponObject : weak< WeaponObject >;
		weaponObject = GetLeftHandWeaponObject( scriptInterface );
		return weaponObject.IsCharged();
	}

	public function AttachAndPreviewProjectile( scriptInterface : StateGameScriptInterface, active : Bool )
	{
		var previewEvent : gameprojectileProjectilePreviewEvent;
		var installedProjectile : ItemID;
		var round : ItemObject;
		GetCurrentlyInstalledProjectile( scriptInterface, installedProjectile );
		if( !( round ) )
		{
			return;
		}
		previewEvent = new gameprojectileProjectilePreviewEvent;
		previewEvent.previewActive = active;
		round.QueueEvent( previewEvent );
	}

	public function DetachProjectile( scriptInterface : StateGameScriptInterface, optional angleOffset : Float )
	{
		var leftHandItemObj : ItemObject;
		var projectileTemplateName : CName;
		var installedProjectile : ItemID;
		leftHandItemObj = scriptInterface.GetTransactionSystem().GetItemInSlot( scriptInterface.executionOwner, RPGManager.GetAttachmentSlotID( "WeaponLeft" ) );
		if( !( leftHandItemObj ) )
		{
			return;
		}
		GetCurrentlyInstalledProjectile( scriptInterface, installedProjectile );
		projectileTemplateName = TDB.GetCName( ItemID.GetTDBID( installedProjectile ) + T".projectileTemplateName", '' );
		if( angleOffset != 0.0 )
		{
			ProjectileLaunchHelper.SpawnArmsLauncherProjectileWithRotation( scriptInterface.executionOwner, projectileTemplateName, leftHandItemObj, angleOffset );
		}
		else
		{
			ProjectileLaunchHelper.SpawnProjectileFromScreenCenter( scriptInterface.executionOwner, projectileTemplateName, '', leftHandItemObj );
		}
		GameInstance.GetTelemetrySystem( scriptInterface.executionOwner.GetGame() ).LogActiveCyberwareUsed( scriptInterface.executionOwner, leftHandItemObj.GetItemID() );
	}

	protected function DrainLeftHandWeaponCharge( scriptInterface : StateGameScriptInterface, chargeValue : Float )
	{
		GameInstance.GetStatPoolsSystem( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ).GetGame() ).RequestChangingStatPoolValue( scriptInterface.executionOwner.GetEntityID(), gamedataStatPoolType.ProjectileLauncherCharges, -( chargeValue ), NULL, false, false );
		ChangeStatPoolValue( scriptInterface, GetLeftHandWeaponObject( scriptInterface ).GetEntityID(), gamedataStatPoolType.WeaponCharge, -100.0, true );
	}

	protected function SetLeftHandWeaponCharged( scriptInterface : StateGameScriptInterface, charged : Bool )
	{
		GetLeftHandWeaponObject( scriptInterface ).SetCharged( charged );
	}

	protected function GetWeaponChargeCost( scriptInterface : StateGameScriptInterface ) : Float
	{
		var chargeCost : Float;
		chargeCost = ( ( Float )( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ).GetProjectileLauncherShootCost() ) );
		return chargeCost;
	}

	protected function TurnOnOvercharge( scriptInterface : StateGameScriptInterface )
	{
		var playerID : EntityID;
		var stats : StatsSystem;
		playerID = scriptInterface.executionOwner.GetEntityID();
		stats = scriptInterface.GetStatsSystem();
		if( !( m_overchargeStatFlag ) && ( stats.GetStatValue( playerID, gamedataStatType.CanOverchargeWeapon ) == 0.0 ) )
		{
			m_overchargeStatFlag = RPGManager.CreateStatModifier( gamedataStatType.CanOverchargeWeapon, gameStatModifierType.Additive, 1.0 );
			stats.AddModifier( playerID, m_overchargeStatFlag );
		}
	}

	protected function TurnOFFOvercharge( scriptInterface : StateGameScriptInterface )
	{
		var playerID : EntityID;
		var stats : StatsSystem;
		playerID = scriptInterface.executionOwner.GetEntityID();
		stats = scriptInterface.GetStatsSystem();
		if( m_overchargeStatFlag && ( stats.GetStatValue( playerID, gamedataStatType.CanOverchargeWeapon ) > 0.0 ) )
		{
			stats.RemoveModifier( playerID, m_overchargeStatFlag );
			m_overchargeStatFlag = NULL;
		}
	}

	protected function HasMeleewarePerkStatFlag( scriptInterface : StateGameScriptInterface ) : Bool
	{
		return RPGManager.HasStatFlag( scriptInterface.executionOwner, gamedataStatType.CanUseNewMeleewareAttackSpyTree );
	}

	public function GetCurrentlyInstalledProjectile( scriptInterface : StateGameScriptInterface, out installedProjectile : ItemID ) : Bool
	{
		var projectileLauncherRound : array< SPartSlots >;
		var partSlots : SPartSlots;
		var i : Int32;
		projectileLauncherRound = ItemModificationSystem.GetAllSlots( scriptInterface.executionOwner, GetLeftHandWeaponObject( scriptInterface ).GetItemID() );
		if( projectileLauncherRound.Size() == 0 )
		{
			return false;
		}
		for( i = 0; i < projectileLauncherRound.Size(); i += 1 )
		{
			partSlots = projectileLauncherRound[ i ];
			if( partSlots.status == ESlotState.Taken && ( partSlots.slotID == T"AttachmentSlots.ProjectileLauncherRound" ) )
			{
				installedProjectile = partSlots.installedPart;
			}
		}
		return false;
	}

	protected const function GetMaxChargeThreshold( const scriptInterface : StateGameScriptInterface ) : Float
	{
		var weapon : weak< WeaponObject >;
		weapon = GetLeftHandWeaponObject( scriptInterface );
		if( scriptInterface.HasStatFlag( gamedataStatType.CanOverchargeWeapon ) )
		{
			return WeaponObject.GetOverchargeThreshold( weapon );
		}
		if( scriptInterface.HasStatFlag( gamedataStatType.CanFullyChargeWeapon ) )
		{
			return WeaponObject.GetFullyChargedThreshold( weapon );
		}
		return WeaponObject.GetBaseMaxChargeThreshold( weapon );
	}

	protected function PlayEffect( effectName : CName, scriptInterface : StateGameScriptInterface, optional eventTag : CName )
	{
		var spawnEffectEvent : entSpawnEffectEvent;
		var weapon : weak< WeaponObject >;
		weapon = GetLeftHandWeaponObject( scriptInterface );
		if( weapon )
		{
			spawnEffectEvent = new entSpawnEffectEvent;
			spawnEffectEvent.effectName = effectName;
			spawnEffectEvent.effectInstanceName = eventTag;
			weapon.QueueEventToChildItems( spawnEffectEvent );
		}
	}

	protected function StopEffect( effectName : CName, scriptInterface : StateGameScriptInterface )
	{
		var killEffectEvent : entKillEffectEvent;
		var weapon : weak< WeaponObject >;
		weapon = GetLeftHandWeaponObject( scriptInterface );
		if( weapon )
		{
			killEffectEvent = new entKillEffectEvent;
			killEffectEvent.effectName = effectName;
			weapon.QueueEventToChildItems( killEffectEvent );
		}
	}

	protected function SetAnimFeatureState( stateContext : StateContext, value : Int32 )
	{
		stateContext.SetPermanentIntParameter( 'state', value, true );
	}

	protected function SetActionDuration( stateContext : StateContext, value : Float )
	{
		stateContext.SetPermanentFloatParameter( 'actionDuration', value, true );
	}

	protected function SetIsQuickAction( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isQuickAction', value, true );
	}

	protected function SetIsCharging( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isChargeAction', value, true );
	}

	protected function SetIsLooping( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isLoopAction', value, true );
	}

	protected function SetIsCatching( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isCatchAction', value, true );
	}

	protected function SetIsSafeAction( stateContext : StateContext, value : Bool )
	{
		stateContext.SetPermanentBoolParameter( 'isSafeAction', value, true );
	}

	protected function SetIsProjectileCaught( stateContext : StateContext, scriptInterface : StateGameScriptInterface, value : Bool )
	{
		SetBlackboardBoolVariable( scriptInterface, GetAllBlackboardDefs().LeftHandCyberware.ProjectileCaught, value );
	}

	protected function ResetAnimFeatureParameters( stateContext : StateContext )
	{
		stateContext.SetPermanentFloatParameter( 'actionDuration', -1.0, true );
		stateContext.SetPermanentIntParameter( 'state', 0, true );
		stateContext.SetPermanentBoolParameter( 'isQuickAction', false, true );
		stateContext.SetPermanentBoolParameter( 'isChargeAction', false, true );
		stateContext.SetPermanentBoolParameter( 'isLoopAction', false, true );
		stateContext.SetPermanentBoolParameter( 'isCatchAction', false, true );
		stateContext.SetPermanentBoolParameter( 'isSafeAction', false, true );
	}

	protected const function GetMaxActiveTime( const scriptInterface : StateGameScriptInterface ) : Float
	{
		var equipmentSystem : EquipmentSystem;
		equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( scriptInterface.owner.GetGame() ).Get( 'EquipmentSystem' ) ) );
		return CyberwareUtility.GetMaxActiveTimeFromTweak( ItemID.GetTDBID( equipmentSystem.GetPlayerData( scriptInterface.executionOwner ).GetActiveItem( gamedataEquipmentArea.QuickWheel ) ) );
	}

	protected const function ShouldInstantlyUnequipCyberware( const scriptInterface : StateGameScriptInterface, const stateContext : StateContext ) : Bool
	{
		return !( IsUsingCyberwareAllowed( stateContext, scriptInterface ) );
	}

	protected const function IsUsingCyberwareAllowed( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var vehicleState : CName;
		if( IsNoCombatActionsForced( scriptInterface ) )
		{
			return false;
		}
		if( IsUsingFirearmsForced( scriptInterface ) )
		{
			return false;
		}
		if( IsUsingFistsForced( scriptInterface ) )
		{
			return false;
		}
		if( IsUsingMeleeForced( scriptInterface ) )
		{
			return false;
		}
		if( IsInLocomotionState( stateContext, 'superheroFall' ) )
		{
			return false;
		}
		if( IsInLocomotionState( stateContext, 'vault' ) )
		{
			return false;
		}
		if( IsInLocomotionState( stateContext, 'climb' ) )
		{
			return false;
		}
		if( IsInLadderState( stateContext ) )
		{
			return false;
		}
		if( stateContext.IsStateMachineActive( 'Vehicle' ) )
		{
			vehicleState = stateContext.GetStateMachineCurrentState( 'Vehicle' );
			if( ( vehicleState != 'exit' && vehicleState != 'coolExiting' ) && vehicleState != 'slideExiting' )
			{
				return false;
			}
		}
		if( stateContext.IsStateMachineActive( 'CarriedObject' ) )
		{
			return false;
		}
		if( stateContext.IsStateMachineActive( 'LocomotionSwimming' ) )
		{
			return false;
		}
		if( stateContext.IsStateMachineActive( 'LocomotionTakedown' ) )
		{
			return false;
		}
		if( IsInLocomotionState( stateContext, 'vehicleKnockdown' ) )
		{
			return false;
		}
		if( IsInLocomotionState( stateContext, 'hardLand' ) || IsInLocomotionState( stateContext, 'veryHardLand' ) )
		{
			return false;
		}
		return true;
	}

	protected function AimSnap( scriptInterface : StateGameScriptInterface )
	{
		var targetingSystem : TargetingSystem;
		targetingSystem = scriptInterface.GetTargetingSystem();
		if( targetingSystem )
		{
			targetingSystem.OnAimStartBegin( scriptInterface.executionOwner );
			targetingSystem.OnAimStartEnd( scriptInterface.executionOwner );
			targetingSystem.AimSnap( scriptInterface.executionOwner );
		}
	}

	protected function EndAiming( scriptInterface : StateGameScriptInterface )
	{
		var targetingSystem : TargetingSystem;
		targetingSystem = scriptInterface.GetTargetingSystem();
		if( targetingSystem )
		{
			targetingSystem.OnAimStop( scriptInterface.executionOwner );
		}
	}

	protected const function HasProjectileLauncherWithStatFlag( scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( LeftHandCyberwareHasTag( scriptInterface, 'ProjectileLauncher' ) && RPGManager.HasStatFlag( scriptInterface.executionOwner, gamedataStatType.CanUseNewMeleewareAttackSpyTree ) )
		{
			return true;
		}
		return false;
	}

}

abstract class LeftHandCyberwareEventsTransition extends LeftHandCyberwareTransition
{

	public virtual function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		LockLeftHandAnimation( scriptInterface, true );
		CreateAndSendAnimFeatureData( stateContext, scriptInterface );
	}

	public virtual function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		CreateAndSendAnimFeatureData( stateContext, scriptInterface );
	}

	public virtual function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		CleanUpLeftHandCyberwareState( stateContext, scriptInterface );
		CreateAndSendAnimFeatureData( stateContext, scriptInterface );
	}

	protected virtual function CleanUpLeftHandCyberwareState( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		AttachAndPreviewProjectile( scriptInterface, false );
		SetLeftHandItemTypeAndState( scriptInterface, 0, 0 );
		LockLeftHandAnimation( scriptInterface, false );
		SetIsCharging( stateContext, false );
		stateContext.RemovePermanentBoolParameter( 'forceTempUnequipWeapon' );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.Default ) ) );
		SetAnimEquipState( scriptInterface, false );
		SendEquipmentSystemWeaponManipulationRequest( scriptInterface, EquipmentManipulationAction.UnequipLeftHandCyberware, gameEquipAnimationType.Instant );
		ResetAnimFeatureParameters( stateContext );
	}

}

class LeftHandCyberwareSafeDecisions extends LeftHandCyberwareTransition
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( IsSafeStateForced( stateContext, scriptInterface ) )
		{
			return true;
		}
		if( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ).IsAimingAtFriendly() )
		{
			return true;
		}
		if( IsInVisionModeActiveState( stateContext, scriptInterface ) && ( GetInStateTime() > 0.1 ) )
		{
			return true;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectOfType( scriptInterface.executionOwner, gamedataStatusEffectType.Stunned ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToLeftHandCyberwareUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( GetInStateTime() >= GetStaticFloatParameterDefault( "stateDuration", 2.0 ) )
		{
			return true;
		}
		return ShouldInstantlyUnequipCyberware( scriptInterface, stateContext );
	}

}

class LeftHandCyberwareSafeEvents extends LeftHandCyberwareEventsTransition
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		scriptInterface.PushAnimationEvent( 'LeftHandCyberwareReady' );
		SetAnimFeatureState( stateContext, 1 );
		SetIsSafeAction( stateContext, true );
		LockLeftHandAnimation( scriptInterface, true );
		SetActionDuration( stateContext, GetStaticFloatParameterDefault( "stateDuration", 2.0 ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.Safe ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetIsSafeAction( stateContext, false );
		super.OnExit( stateContext, scriptInterface );
	}

}

class LeftHandCyberwareEquipDecisions extends LeftHandCyberwareTransition
{

	protected const function ToLeftHandCyberwareCharge( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.IsActionHeld( 'UseCombatGadget' ) && LeftHandCyberwareHasTag( scriptInterface, 'ChargeAction' ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToLeftHandCyberwareUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return ShouldInstantlyUnequipCyberware( scriptInterface, stateContext );
	}

}

class LeftHandCyberwareEquipEvents extends LeftHandCyberwareEventsTransition
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var evt : LeftHandCyberwareEquippedEvent;
		var dpadAction : DPADActionPerformed;
		dpadAction = new DPADActionPerformed;
		dpadAction.ownerID = scriptInterface.executionOwnerEntityID;
		dpadAction.action = EHotkey.RB;
		dpadAction.state = EUIActionState.STARTED;
		dpadAction.stateInt = ( ( Int32 )( dpadAction.state ) );
		dpadAction.successful = true;
		scriptInterface.GetUISystem().QueueEvent( dpadAction );
		evt = new LeftHandCyberwareEquippedEvent;
		scriptInterface.owner.QueueEvent( evt );
		ResetAnimFeatureParameters( stateContext );
		SetLeftHandItemTypeAndState( scriptInterface, 2, 2 );
		ForceDisableVisionMode( stateContext );
		stateContext.SetTemporaryBoolParameter( 'InterruptAiming', true, true );
		if( IsRightHandInEquippedState( stateContext ) )
		{
			stateContext.SetPermanentBoolParameter( 'forceTempUnequipWeapon', true, true );
		}
		SetAnimFeatureState( stateContext, 1 );
		SetAnimEquipState( scriptInterface, true );
		LockLeftHandAnimation( scriptInterface, true );
		SetActionDuration( stateContext, GetEquipDuration( scriptInterface ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.Equipped ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		scriptInterface.PushAnimationEvent( 'LeftHandCyberwareReady' );
		super.OnExit( stateContext, scriptInterface );
	}

}

class LeftHandCyberwareChargeDecisions extends LeftHandCyberwareTransition
{

	protected const function ToLeftHandCyberwareChargeAction( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.IsActionJustReleased( 'UseCombatGadget' ) || scriptInterface.IsActionJustPressed( 'RangedAttack' ) )
		{
			return !( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ).IsAimingAtFriendly() ) && !( ( HasProjectileLauncherWithStatFlag( scriptInterface ) && WeaponIsCharged( scriptInterface ) ) );
		}
		return false;
	}

	protected constexpr function ToLeftHandCyberwareChargeRepeatAction( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.IsActionJustReleased( 'UseCombatGadget' ) || scriptInterface.IsActionJustPressed( 'RangedAttack' ) )
		{
			return !( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ).IsAimingAtFriendly() ) && ( HasProjectileLauncherWithStatFlag( scriptInterface ) && WeaponIsCharged( scriptInterface ) );
		}
		return false;
	}

	protected const function ToLeftHandCyberwareWaitForUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.IsActionJustReleased( 'UseCombatGadget' ) || ( scriptInterface.IsActionJustPressed( 'RangedAttack' ) && ( ( PlayerPuppet )( scriptInterface.executionOwner ) ).IsAimingAtFriendly() ) )
		{
			return true;
		}
		if( ( GetInStateTime() >= GetStaticFloatParameterDefault( "stateDuration", 1.0 ) ) && ( GetCancelChargeButtonInput( scriptInterface ) || scriptInterface.IsActionJustPressed( 'SwitchItem' ) ) )
		{
			return true;
		}
		if( scriptInterface.localBlackboard.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Vision ) == ( ( Int32 )( gamePSMVision.Focus ) ) )
		{
			return true;
		}
		if( StatusEffectSystem.ObjectHasStatusEffectOfType( scriptInterface.executionOwner, gamedataStatusEffectType.Knockdown ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToLeftHandCyberwareUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return ShouldInstantlyUnequipCyberware( scriptInterface, stateContext );
	}

}

class LeftHandCyberwareChargeEvents extends LeftHandCyberwareEventsTransition
{
	private var m_chargeModeAim : AnimFeature_AimPlayer;
	private var m_leftHandObject : weak< WeaponObject >;
	private var m_aimInTimeRemaining : Float;

	protected function UpdateChargeModeCameraAimAnimFeature( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( !( m_chargeModeAim ) )
		{
			m_chargeModeAim = new AnimFeature_AimPlayer;
		}
		m_chargeModeAim.SetAimState( animAimState.Aimed );
		m_chargeModeAim.SetZoomState( animAimState.Aimed );
		m_chargeModeAim.SetAimInTime( scriptInterface.GetStatsSystem().GetStatValue( m_leftHandObject.GetEntityID(), gamedataStatType.AimInTime ) );
		m_chargeModeAim.SetAimOutTime( scriptInterface.GetStatsSystem().GetStatValue( m_leftHandObject.GetEntityID(), gamedataStatType.AimOutTime ) );
		scriptInterface.SetAnimationParameterFeature( 'AnimFeature_AimPlayer', m_chargeModeAim );
		scriptInterface.SetAnimationParameterFeature( 'AnimFeature_AimPlayer', m_chargeModeAim, m_leftHandObject );
	}

	protected function ResetChargeModeCameraAimAnimFeature( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		m_chargeModeAim.SetAimState( animAimState.Unaimed );
		m_chargeModeAim.SetZoomState( animAimState.Unaimed );
		scriptInterface.SetAnimationParameterFeature( 'AnimFeature_AimPlayer', m_chargeModeAim );
		scriptInterface.SetAnimationParameterFeature( 'AnimFeature_AimPlayer', m_chargeModeAim, m_leftHandObject );
	}

	protected function GetChargeValuePerSec( scriptInterface : StateGameScriptInterface ) : Float
	{
		var weapon : WeaponObject;
		var chargeDuration : Float;
		var statsSystem : StatsSystem;
		statsSystem = scriptInterface.GetStatsSystem();
		if( !( statsSystem ) )
		{
			return -1.0;
		}
		weapon = GetLeftHandWeaponObject( scriptInterface );
		if( !( weapon ) )
		{
			return -1.0;
		}
		chargeDuration = statsSystem.GetStatValue( weapon.GetEntityID(), gamedataStatType.ChargeTime );
		if( chargeDuration <= 0.0 )
		{
			return -1.0;
		}
		return 100.0 / chargeDuration;
	}

	protected function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var maxCharge : Float;
		var currentCharge : Float;
		var chargeNormalized : Float;
		var hasCWPerk : Bool;
		hasCWPerk = HasMeleewarePerkStatFlag( scriptInterface );
		maxCharge = GetMaxChargeThreshold( scriptInterface );
		currentCharge = scriptInterface.GetStatPoolsSystem().GetStatPoolValue( GetLeftHandWeaponObject( scriptInterface ).GetEntityID(), gamedataStatPoolType.WeaponCharge );
		if( maxCharge > 0.0 )
		{
			chargeNormalized = ClampF( currentCharge / maxCharge, 0.0, 1.0 );
		}
		UpdateAndSendChargeAnimData( chargeNormalized, hasCWPerk, stateContext, scriptInterface );
		if( !( WeaponIsCharged( scriptInterface ) ) && ( chargeNormalized >= 1.0 ) )
		{
			PlayRumble( scriptInterface, "heavy_pulse" );
			PlayEffect( 'charged', scriptInterface );
			GetLeftHandWeaponObject( scriptInterface ).SetCharged( true );
			if( hasCWPerk )
			{
				GameObjectEffectHelper.StartEffectEvent( scriptInterface.executionOwner, 'RelicTree_Meleeware_PowerUp', true );
				GameObjectEffectHelper.StartEffectEvent( GetLeftHandWeaponObject( scriptInterface ), 'RelicTree_Meleeware_ChargeIndicator_Launcher', true );
				GameObject.PlaySoundEvent( GetLeftHandWeaponObject( scriptInterface ), 'w_cyb_launcher_spy_perk_charged' );
			}
		}
		if( m_aimInTimeRemaining > 0.0 )
		{
			m_aimInTimeRemaining -= timeDelta;
			SetBlackboardFloatVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.AimInTimeRemaining, m_aimInTimeRemaining );
		}
		UpdateChargeModeCameraAimAnimFeature( stateContext, scriptInterface );
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var chargeEvt : ChargeStartedEvent;
		var chargeValuePerSec : Float;
		var maxCharge : Float;
		chargeEvt = new ChargeStartedEvent;
		scriptInterface.owner.QueueEvent( chargeEvt );
		m_leftHandObject = ( ( WeaponObject )( scriptInterface.GetTransactionSystem().GetItemInSlot( scriptInterface.executionOwner, T"AttachmentSlots.WeaponLeft" ) ) );
		SetZoomStateAnimFeature( scriptInterface, true );
		SetActionDuration( stateContext, GetStaticFloatParameterDefault( "stateDuration", 1.0 ) );
		AimSnap( scriptInterface );
		m_aimInTimeRemaining = 0.5;
		SetBlackboardFloatVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.AimInTimeRemaining, m_aimInTimeRemaining );
		SetBlackboardFloatVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.AimInTime, m_aimInTimeRemaining );
		SetIsCharging( stateContext, true );
		if( LeftHandCyberwareHasTag( scriptInterface, 'ProjectileLauncher' ) )
		{
			if( HasMeleewarePerkStatFlag( scriptInterface ) )
			{
				TurnOnOvercharge( scriptInterface );
			}
			maxCharge = GetMaxChargeThreshold( scriptInterface );
			m_leftHandObject.SetMaxChargeThreshold( maxCharge );
			chargeValuePerSec = GetChargeValuePerSec( scriptInterface );
			StartPool( scriptInterface.GetStatPoolsSystem(), GetLeftHandWeaponObject( scriptInterface ).GetEntityID(), gamedataStatPoolType.WeaponCharge, 0.0, maxCharge, chargeValuePerSec );
			PlayEffect( 'charging', scriptInterface );
		}
		SetAnimFeatureState( stateContext, 2 );
		AttachAndPreviewProjectile( scriptInterface, true );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.Charge ) ) );
		stateContext.SetTemporaryBoolParameter( 'InterruptSprint', true, true );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var chargeEvt : ChargeEndedEvent;
		chargeEvt = new ChargeEndedEvent;
		scriptInterface.owner.QueueEvent( chargeEvt );
		StopPool( scriptInterface.GetStatPoolsSystem(), GetLeftHandWeaponObject( scriptInterface ).GetEntityID(), gamedataStatPoolType.WeaponCharge, false );
		StopEffect( 'charging', scriptInterface );
		StopEffect( 'charged', scriptInterface );
		GameObjectEffectHelper.BreakEffectLoopEvent( scriptInterface.executionOwner, 'RelicTree_Meleeware_PowerUp' );
		GameObjectEffectHelper.BreakEffectLoopEvent( scriptInterface.executionOwner, 'RelicTree_Meleeware_ChargeIndicator_Launcher' );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.Charge ) ) );
		ResetChargeModeCameraAimAnimFeature( stateContext, scriptInterface );
		SetZoomStateAnimFeature( scriptInterface, false );
		AttachAndPreviewProjectile( scriptInterface, false );
		EndAiming( scriptInterface );
		OnExitCommon( stateContext, scriptInterface );
		super.OnExit( stateContext, scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		ResetChargeModeCameraAimAnimFeature( stateContext, scriptInterface );
		SetZoomStateAnimFeature( scriptInterface, false );
		AttachAndPreviewProjectile( scriptInterface, false );
		EndAiming( scriptInterface );
		StopPool( scriptInterface.GetStatPoolsSystem(), GetLeftHandWeaponObject( scriptInterface ).GetEntityID(), gamedataStatPoolType.WeaponCharge, false );
		StopEffect( 'charging', scriptInterface );
		StopEffect( 'charged', scriptInterface );
		GameObjectEffectHelper.BreakEffectLoopEvent( scriptInterface.executionOwner, 'RelicTree_Meleeware_PowerUp' );
		GameObjectEffectHelper.BreakEffectLoopEvent( scriptInterface.executionOwner, 'RelicTree_Meleeware_ChargeIndicator_Launcher' );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.Charge ) ) );
		OnExitCommon( stateContext, scriptInterface );
		super.OnForcedExit( stateContext, scriptInterface );
	}

	public function OnExitCommon( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		TurnOFFOvercharge( scriptInterface );
	}

}

class LeftHandCyberwareLoopDecisions extends LeftHandCyberwareTransition
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ( scriptInterface.GetActionValue( 'UseCombatGadget' ) == 0.0 ) && LeftHandCyberwareHasTag( scriptInterface, 'LoopAction' ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToLeftHandCyberwareWaitForUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( scriptInterface.IsActionJustPressed( 'UseCombatGadget' ) )
		{
			return true;
		}
		if( stateContext.GetBoolParameter( 'InterruptLeftHandAction' ) )
		{
			return true;
		}
		if( ( GetMaxActiveTime( scriptInterface ) > 0.0 ) && ( GetInStateTime() > GetMaxActiveTime( scriptInterface ) ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToLeftHandCyberwareUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return ShouldInstantlyUnequipCyberware( scriptInterface, stateContext );
	}

}

class LeftHandCyberwareLoopEvents extends LeftHandCyberwareEventsTransition
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var evt : LoopStartedEvent;
		evt = new LoopStartedEvent;
		scriptInterface.owner.QueueEvent( evt );
		SetActionDuration( stateContext, GetStaticFloatParameterDefault( "stateDuration", 1.0 ) );
		SetAnimFeatureState( stateContext, 2 );
		SetIsLooping( stateContext, true );
		scriptInterface.PushAnimationEvent( 'LeftHandCyberwareLoopAction' );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.Loop ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var evt : LoopEndedEvent;
		evt = new LoopEndedEvent;
		scriptInterface.owner.QueueEvent( evt );
		SetIsLooping( stateContext, false );
		scriptInterface.PushAnimationEvent( 'LeftHandCyberwareLoopActionEnd' );
		super.OnExit( stateContext, scriptInterface );
	}

}

abstract class LeftHandCyberwareActionAbstractDecisions extends LeftHandCyberwareTransition
{

	protected const virtual function ToLeftHandCyberwareWaitForUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( stateContext.GetBoolParameter( 'InterruptLeftHandAction' ) )
		{
			return true;
		}
		if( GetInStateTime() >= GetStaticFloatParameterDefault( "stateDuration", 1.0 ) )
		{
			return true;
		}
		return false;
	}

}

abstract class LeftHandCyberwareActionAbstractEvents extends LeftHandCyberwareEventsTransition
{
	var m_projectileReleased : Bool;
	default m_projectileReleased = false;

	protected virtual function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		if( ( GetInStateTime() >= GetStaticFloatParameterDefault( "projectileDetachDelay", 0.0 ) ) && !( m_projectileReleased ) )
		{
			DetachProjectile( scriptInterface );
			m_projectileReleased = true;
		}
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var dpadAction : DPADActionPerformed;
		dpadAction = new DPADActionPerformed;
		dpadAction.ownerID = scriptInterface.executionOwnerEntityID;
		dpadAction.action = EHotkey.RB;
		dpadAction.state = EUIActionState.COMPLETED;
		dpadAction.stateInt = ( ( Int32 )( dpadAction.state ) );
		dpadAction.successful = true;
		scriptInterface.GetUISystem().QueueEvent( dpadAction );
		m_projectileReleased = false;
		SetActionDuration( stateContext, GetStaticFloatParameterDefault( "stateDuration", 1.0 ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

	protected function ConsumeStamina( scriptInterface : StateGameScriptInterface )
	{
		var weapon : weak< WeaponObject >;
		var attackRecord : weak< Attack_Record >;
		var staminaCostMods : array< weak< StatModifier_Record > >;
		var staminaCost : Float;
		staminaCost = 0.0;
		weapon = GetLeftHandWeaponObject( scriptInterface );
		attackRecord = weapon.GetCurrentAttack().GetRecord();
		attackRecord.StaminaCost( staminaCostMods );
		staminaCost = RPGManager.CalculateStatModifiers( staminaCostMods, scriptInterface.GetGame(), scriptInterface.owner, scriptInterface.ownerEntityID );
		if( ( staminaCost > 0.0 ) && !( StatusEffectSystem.ObjectHasStatusEffectWithTag( scriptInterface.executionOwner, 'FocusedCoolPerkSE' ) ) )
		{
			PlayerStaminaHelpers.ModifyStamina( ( ( PlayerPuppet )( scriptInterface.executionOwner ) ), -( staminaCost ) );
		}
	}

}

class LeftHandCyberwareQuickActionDecisions extends LeftHandCyberwareActionAbstractDecisions
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( ( scriptInterface.GetActionValue( 'UseCombatGadget' ) == 0.0 ) && LeftHandCyberwareHasTag( scriptInterface, 'QuickAction' ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToLeftHandCyberwareUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( stateContext.GetBoolParameter( 'InterruptLeftHandAction' ) )
		{
			return true;
		}
		if( GetInStateTime() >= GetEquipDuration( scriptInterface ) )
		{
			return true;
		}
		return ShouldInstantlyUnequipCyberware( scriptInterface, stateContext );
	}

}

class LeftHandCyberwareQuickActionEvents extends LeftHandCyberwareActionAbstractEvents
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var evt : QuickActionEvent;
		evt = new QuickActionEvent;
		SetActionDuration( stateContext, GetEquipDuration( scriptInterface ) );
		AttachAndPreviewProjectile( scriptInterface, true );
		AimSnap( scriptInterface );
		SetIsQuickAction( stateContext, true );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.QuickAction ) ) );
		ConsumeStamina( scriptInterface );
		scriptInterface.owner.QueueEvent( evt );
		PlayRumble( scriptInterface, "light_fast" );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		DrainLeftHandWeaponCharge( scriptInterface, GetWeaponChargeCost( scriptInterface ) );
		EndAiming( scriptInterface );
		AttachAndPreviewProjectile( scriptInterface, false );
		SetIsQuickAction( stateContext, false );
		super.OnExit( stateContext, scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		DrainLeftHandWeaponCharge( scriptInterface, GetWeaponChargeCost( scriptInterface ) );
		EndAiming( scriptInterface );
		super.OnForcedExit( stateContext, scriptInterface );
	}

}

class LeftHandCyberwareChargeActionDecisions extends LeftHandCyberwareActionAbstractDecisions
{

	protected const function ToLeftHandCyberwareUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( stateContext.GetBoolParameter( 'InterruptLeftHandAction' ) )
		{
			return true;
		}
		if( GetInStateTime() >= GetStaticFloatParameterDefault( "stateDuration", 0.30000001 ) )
		{
			return true;
		}
		return ShouldInstantlyUnequipCyberware( scriptInterface, stateContext );
	}

}

class LeftHandCyberwareChargeActionEvents extends LeftHandCyberwareActionAbstractEvents
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.ChargeAction ) ) );
		ConsumeStamina( scriptInterface );
		scriptInterface.PushAnimationEvent( 'LeftHandCyberwareChargeAction' );
		PlayRumble( scriptInterface, "heavy_pulse" );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		DrainLeftHandWeaponCharge( scriptInterface, GetWeaponChargeCost( scriptInterface ) );
		super.OnExit( stateContext, scriptInterface );
	}

}

class LeftHandCyberwareChargeRepeatActionDecisions extends LeftHandCyberwareActionAbstractDecisions
{

	protected const override function ToLeftHandCyberwareWaitForUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var projectilesReleased : StateResultInt;
		var m_maxProjectiles : Int32;
		projectilesReleased = stateContext.GetPermanentIntParameter( 'projectilesReleased' );
		m_maxProjectiles = GetStaticIntParameterDefault( "maxProjectiles", 3 );
		if( ShouldInstantlyUnequipCyberware( scriptInterface, stateContext ) )
		{
			return true;
		}
		if( stateContext.GetBoolParameter( 'InterruptLeftHandAction' ) )
		{
			return true;
		}
		return projectilesReleased.valid && ( projectilesReleased.value >= m_maxProjectiles );
	}

	protected const function ToLeftHandCyberwareChargeRepeatAction( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		var projectilesReleased : StateResultInt;
		var m_maxProjectiles : Int32;
		projectilesReleased = stateContext.GetPermanentIntParameter( 'projectilesReleased' );
		m_maxProjectiles = GetStaticIntParameterDefault( "maxProjectiles", 3 );
		if( GetInStateTime() >= GetStaticFloatParameterDefault( "stateDuration", 0.30000001 ) )
		{
			return projectilesReleased.valid && ( projectilesReleased.value < m_maxProjectiles );
		}
		return false;
	}

}

class LeftHandCyberwareChargeRepeatActionEvents extends LeftHandCyberwareActionAbstractEvents
{
	var m_maxSpread : Float;
	var m_maxProjectiles : Int32;

	protected override function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var projectilesReleased : StateResultInt;
		var delay : Float;
		var spread : Float;
		projectilesReleased = stateContext.GetPermanentIntParameter( 'projectilesReleased' );
		delay = GetStaticFloatParameterDefault( "projectileDetachDelay", 0.0 );
		if( ( ( projectilesReleased.valid && ( GetInStateTime() >= delay ) ) && !( m_projectileReleased ) ) && ( projectilesReleased.value < m_maxProjectiles ) )
		{
			spread = m_maxSpread - ( ( ( ( ( Float )( projectilesReleased.value ) ) * m_maxSpread ) / ( ( Float )( ( m_maxProjectiles - 1 ) ) ) ) * 2.0 );
			DetachProjectile( scriptInterface, spread );
			m_projectileReleased = true;
			stateContext.SetPermanentIntParameter( 'projectilesReleased', projectilesReleased.value + 1, true );
		}
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var projectilesReleased : StateResultInt;
		var evt : QuickActionEvent;
		projectilesReleased = stateContext.GetPermanentIntParameter( 'projectilesReleased' );
		evt = new QuickActionEvent;
		m_maxSpread = ( GetStaticFloatParameterDefault( "maxSpread", 45.0 ) * Pi() ) / 360.0;
		m_maxProjectiles = GetStaticIntParameterDefault( "maxProjectiles", 5 );
		SetActionDuration( stateContext, GetEquipDuration( scriptInterface ) );
		AttachAndPreviewProjectile( scriptInterface, true );
		AimSnap( scriptInterface );
		SetIsQuickAction( stateContext, true );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.ChargeAction ) ) );
		scriptInterface.owner.QueueEvent( evt );
		PlayRumble( scriptInterface, "light_fast" );
		GameObjectEffectHelper.StartEffectEvent( GetLeftHandWeaponObject( scriptInterface ), 'RelicTree_Meleeware_Attack_Launcher', true );
		if( !( projectilesReleased.valid ) )
		{
			stateContext.SetPermanentIntParameter( 'projectilesReleased', 0, true );
		}
		scriptInterface.PushAnimationEvent( 'LeftHandCyberwareChargeRepeatAction' );
		m_projectileReleased = false;
		if( !( projectilesReleased.value ) )
		{
			DrainLeftHandWeaponCharge( scriptInterface, GetWeaponChargeCost( scriptInterface ) );
		}
		super.OnEnter( stateContext, scriptInterface );
	}

	public function OnEnterFromLeftHandCyberwareCharge( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		StopEffect( 'charging', scriptInterface );
		StopEffect( 'charged', scriptInterface );
		OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var projectilesReleased : StateResultInt;
		projectilesReleased = stateContext.GetPermanentIntParameter( 'projectilesReleased' );
		EndAiming( scriptInterface );
		AttachAndPreviewProjectile( scriptInterface, false );
		SetIsQuickAction( stateContext, false );
		if( projectilesReleased.value && ( projectilesReleased.value >= m_maxProjectiles ) )
		{
			stateContext.SetPermanentIntParameter( 'projectilesReleased', 0, true );
			SetLeftHandWeaponCharged( scriptInterface, false );
		}
		GameObjectEffectHelper.BreakEffectLoopEvent( GetLeftHandWeaponObject( scriptInterface ), 'RelicTree_Meleeware_ChargeIndicator_Launcher' );
		super.OnExit( stateContext, scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		EndAiming( scriptInterface );
		GameObjectEffectHelper.BreakEffectLoopEvent( GetLeftHandWeaponObject( scriptInterface ), 'RelicTree_Meleeware_ChargeIndicator_Launcher' );
		SetLeftHandWeaponCharged( scriptInterface, false );
		super.OnForcedExit( stateContext, scriptInterface );
	}

}

class LeftHandCyberwareCatchActionDecisions extends LeftHandCyberwareActionAbstractDecisions
{
}

class LeftHandCyberwareCatchActionEvents extends LeftHandCyberwareActionAbstractEvents
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.CatchAction ) ) );
		scriptInterface.PushAnimationEvent( 'LeftHandCyberwareCatchAction' );
		super.OnEnter( stateContext, scriptInterface );
	}

}

class LeftHandCyberwareCatchDecisions extends LeftHandCyberwareTransition
{

	protected const function EnterCondition( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( stateContext.GetBoolParameter( 'CatchMonodisc', true ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToLeftHandCyberwareWaitForUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( stateContext.GetBoolParameter( 'InterruptLeftHandAction' ) )
		{
			return true;
		}
		if( GetInStateTime() >= GetStaticFloatParameterDefault( "stateDuration", 1.20000005 ) )
		{
			return true;
		}
		return false;
	}

	protected const function ToLeftHandCyberwareCatchAction( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		return stateContext.GetConditionBool( 'LeftHandCyberwareCatchButtonPressed' ) && !( stateContext.GetConditionBool( 'LeftHandCyberwareCatchWindowMissed' ) );
	}

}

class LeftHandCyberwareCatchEvents extends LeftHandCyberwareEventsTransition
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		stateContext.SetConditionBoolParameter( 'LeftHandCyberwareCatchButtonPressed', false, true );
		stateContext.SetConditionBoolParameter( 'LeftHandCyberwareCatchWindowMissed', false, true );
		SetIsCatching( stateContext, true );
		SetAnimFeatureState( stateContext, 3 );
		LockLeftHandAnimation( scriptInterface, true );
		scriptInterface.PushAnimationEvent( 'LeftHandCyberwareReady' );
		stateContext.SetPermanentBoolParameter( 'CatchMonodisc', false, true );
		stateContext.SetTemporaryBoolParameter( 'DisableWeaponUI', true, true );
		SetIsProjectileCaught( stateContext, scriptInterface, true );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.Catch ) ) );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetIsCatching( stateContext, false );
		SetIsProjectileCaught( stateContext, scriptInterface, false );
		super.OnExit( stateContext, scriptInterface );
	}

	protected function OnUpdate( timeDelta : Float, stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var throwWindowStart : Float;
		var throwWindowEnd : Float;
		var inStateTime : Float;
		inStateTime = GetInStateTime();
		throwWindowStart = GetStaticFloatParameterDefault( "throwWindowStart", 0.34999999 );
		throwWindowEnd = GetStaticFloatParameterDefault( "throwWindowEnd", -1.0 );
		if( scriptInterface.IsActionJustPressed( 'UseCombatGadget' ) )
		{
			if( !( stateContext.GetConditionBool( 'LeftHandCyberwareCatchButtonPressed' ) ) || !( GetStaticBoolParameterDefault( "preventButtonSpamming", false ) ) )
			{
				stateContext.SetConditionBoolParameter( 'LeftHandCyberwareCatchButtonPressed', true, true );
			}
		}
		if( ( throwWindowStart >= 0.0 ) && ( inStateTime < throwWindowStart ) )
		{
			if( stateContext.GetConditionBool( 'LeftHandCyberwareCatchButtonPressed' ) )
			{
				stateContext.SetConditionBoolParameter( 'LeftHandCyberwareCatchWindowMissed', true, true );
			}
		}
		if( ( throwWindowEnd >= 0.0 ) && ( inStateTime > throwWindowEnd ) )
		{
			if( stateContext.GetConditionBool( 'LeftHandCyberwareCatchButtonPressed' ) )
			{
				stateContext.SetConditionBoolParameter( 'LeftHandCyberwareCatchWindowMissed', true, true );
			}
		}
	}

}

class LeftHandCyberwareWaitForUnequipDecisions extends LeftHandCyberwareTransition
{

	protected const function ToLeftHandCyberwareUnequip( const stateContext : StateContext, const scriptInterface : StateGameScriptInterface ) : Bool
	{
		if( GetInStateTime() >= GetUnequipDuration( scriptInterface ) )
		{
			return true;
		}
		return ShouldInstantlyUnequipCyberware( scriptInterface, stateContext );
	}

}

class LeftHandCyberwareWaitForUnequipEvents extends LeftHandCyberwareEventsTransition
{

	public function OnEnterFromLeftHandCyberwareCharge( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var dpadAction : DPADActionPerformed;
		dpadAction = new DPADActionPerformed;
		dpadAction.ownerID = scriptInterface.executionOwnerEntityID;
		dpadAction.action = EHotkey.RB;
		dpadAction.state = EUIActionState.ABORTED;
		dpadAction.stateInt = ( ( Int32 )( dpadAction.state ) );
		dpadAction.successful = false;
		scriptInterface.GetUISystem().QueueEvent( dpadAction );
		OnEnter( stateContext, scriptInterface );
	}

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		SetActionDuration( stateContext, GetUnequipDuration( scriptInterface ) );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.StartUnequip ) ) );
		scriptInterface.PushAnimationEvent( 'LeftHandCyberwareWaitForUnequip' );
		super.OnEnter( stateContext, scriptInterface );
	}

	public override function OnExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnExit( stateContext, scriptInterface );
	}

	public override function OnForcedExit( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		super.OnForcedExit( stateContext, scriptInterface );
	}

}

class LeftHandCyberwareUnequipEvents extends LeftHandCyberwareEventsTransition
{

	public override function OnEnter( stateContext : StateContext, scriptInterface : StateGameScriptInterface )
	{
		var evt : LeftHandCyberwareUnequippedEvent;
		evt = new LeftHandCyberwareUnequippedEvent;
		scriptInterface.owner.QueueEvent( evt );
		CleanUpLeftHandCyberwareState( stateContext, scriptInterface );
		SetBlackboardIntVariable( scriptInterface, GetAllBlackboardDefs().PlayerStateMachine.LeftHandCyberware, ( ( Int32 )( gamePSMLeftHandCyberware.Unequip ) ) );
	}

}

class LeftHandCyberwareEquippedEvent extends Event
{
}

class LeftHandCyberwareUnequippedEvent extends Event
{
}

class ChargeStartedEvent extends Event
{
}

class ChargeEndedEvent extends Event
{
}

class LoopStartedEvent extends Event
{
}

class LoopEndedEvent extends Event
{
}

class QuickActionEvent extends Event
{
}

