class MeleeProjectile extends BaseProjectile
{
	default m_forceRegisterInHudManager = true;
	protected var m_resourceLibraryComponent : ResourceLibraryComponent;
	protected var m_throwCooldownSE : TweakDBID;
	protected var m_collided : Bool;
	default m_collided = false;
	protected var m_wasPicked : Bool;
	default m_wasPicked = false;
	protected var m_isActive : Bool;
	default m_isActive = true;
	protected var m_hasHitWater : Bool;
	default m_hasHitWater = false;
	protected var m_waterHeight : Float;
	default m_waterHeight = 0.f;
	protected var m_deactivationDepth : Float;
	default m_deactivationDepth = -99999.f;
	protected var m_waterImpulseRadius : Float;
	default m_waterImpulseRadius = 0.f;
	protected var m_waterImpulseStrength : Float;
	default m_waterImpulseStrength = 0.f;
	protected var m_gravitySimulationMult : Float;
	protected var m_weapon : weak< GameObject >;
	private var m_throwingMeleeResourcePoolListener : ThrowingMeleeReloadListener;
	protected var m_projectileCollisionEvaluator : ThrowingMeleeCollisionEvaluator;
	protected var m_projectileStopped : Bool;
	protected var m_isCollidedWithEnemy : Bool;

	protected event OnRequestComponents( ri : EntityRequestComponentsInterface )
	{
		super.OnRequestComponents( ri );
		EntityRequestComponentsInterface.RequestComponent( ri, 'ResourceLibrary', 'ResourceLibraryComponent', true );
	}

	protected event OnTakeControl( ri : EntityResolveComponentsInterface )
	{
		super.OnTakeControl( ri );
		m_resourceLibraryComponent = ( ( ResourceLibraryComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'ResourceLibrary' ) ) );
	}

	protected event OnProjectileInitialize( eventData : gameprojectileSetUpEvent )
	{
		var statPoolsSystem : StatPoolsSystem;
		var statsSystem : StatsSystem;
		var weapon : WeaponObject;
		super.OnProjectileInitialize( eventData );
		m_projectileStopped = false;
		m_collided = false;
		m_wasPicked = false;
		m_isActive = true;
		m_hasHitWater = false;
		m_isCollidedWithEnemy = false;
		m_waterHeight = 0.0;
		m_deactivationDepth = GetProjectileTweakDBFloatParameter( "deactivationDepth" );
		m_waterImpulseRadius = GetProjectileTweakDBFloatParameter( "waterImpulseRadius" );
		m_waterImpulseStrength = GetProjectileTweakDBFloatParameter( "waterImpulseStrength" );
		m_projectileCollisionEvaluator = new ThrowingMeleeCollisionEvaluator;
		m_projectileComponent.SetCollisionEvaluator( m_projectileCollisionEvaluator );
		m_weapon = eventData.weapon;
		weapon = ( ( WeaponObject )( m_weapon ) );
		statsSystem = GameInstance.GetStatsSystem( GetGame() );
		if( ( ( weapon && RPGManager.GetWeaponEvolution( weapon.GetItemID() ) == gamedataWeaponEvolution.Throwable ) && eventData.owner ) && statsSystem.GetStatBoolValue( eventData.owner.GetEntityID(), gamedataStatType.HasKnifeSharpener ) )
		{
			StatusEffectHelper.ApplyStatusEffect( eventData.owner, T"BaseStatusEffect.KnifeSharpenerBuff" );
		}
		statPoolsSystem = GameInstance.GetStatPoolsSystem( GetGame() );
		statPoolsSystem.RequestSettingStatPoolMinValue( m_weapon.GetEntityID(), gamedataStatPoolType.ThrowRecovery, this );
		m_gravitySimulationMult = statsSystem.GetStatValue( m_weapon.GetEntityID(), gamedataStatType.MeleeProjectileGravitySimulationMultiplier );
		m_throwingMeleeResourcePoolListener = new ThrowingMeleeReloadListener;
		m_throwingMeleeResourcePoolListener.Bind( this );
		statPoolsSystem.RequestRegisteringListener( m_weapon.GetEntityID(), gamedataStatPoolType.ThrowRecovery, m_throwingMeleeResourcePoolListener );
		TagObject( this );
	}

	protected function SetMeshAppearance( appearance : CName, optional component : CName )
	{
		var evt : entAppearanceEvent;
		evt = new entAppearanceEvent;
		evt.appearanceName = appearance;
		evt.componentName = component;
		QueueEvent( evt );
	}

	protected event OnShoot( eventData : gameprojectileShootEvent )
	{
		ExecuteParabolicLaunch( eventData );
	}

	protected event OnShootTarget( eventData : gameprojectileShootTargetEvent )
	{
		var targetComponent : IPlacedComponent;
		var targetPuppet : ScriptedPuppet;
		GeneralLaunchSetup( eventData );
		m_initialLaunchVelocity = GetProjectileTweakDBFloatParameter( "startVelocity" );
		targetComponent = eventData.params.trackedTargetComponent;
		if( targetComponent )
		{
			targetPuppet = ( ( ScriptedPuppet )( targetComponent.GetEntity() ) );
			if( ( !( ScriptedPuppet.IsFriendlyTowardsPlayer( targetPuppet ) ) && !( targetPuppet.IsCrowd() ) ) && targetPuppet.IsActive() )
			{
				CurvedLaunchToTarget( eventData, ( ( GameObject )( targetComponent.GetEntity() ) ), targetComponent );
			}
			else
			{
				ExecuteParabolicLaunch( eventData );
			}
		}
		else
		{
			ExecuteParabolicLaunch( eventData );
		}
	}

	protected function ExecuteParabolicLaunch( eventData : gameprojectileShootEvent )
	{
		var startVelocity : Float;
		var gravitySimulation : Float;
		var energyLossFactorAfterCollision : Float;
		gravitySimulation = GetProjectileTweakDBFloatParameter( "gravitySimulation" ) * m_gravitySimulationMult;
		startVelocity = GetProjectileTweakDBFloatParameter( "startVelocity" );
		energyLossFactorAfterCollision = GetProjectileTweakDBFloatParameter( "energyLossFactorAfterCollision" );
		GeneralLaunchSetup( eventData );
		ParabolicLaunch( eventData, gravitySimulation, startVelocity, energyLossFactorAfterCollision );
	}

	protected event OnTick( eventData : gameprojectileTickEvent )
	{
		var currentPosition : Vector4;
		if( m_isActive && m_hasHitWater )
		{
			currentPosition = GetWorldPosition();
			if( ( currentPosition.Z - m_waterHeight ) <= m_deactivationDepth )
			{
				DeactivateAndSink();
			}
		}
	}

	protected event OnCollision( eventData : gameprojectileHitEvent )
	{
		var isObjectNPC : Bool;
		var hitInstance : gameprojectileHitInstance;
		var enableInteractionEvent : InteractionSetEnableEvent;
		var effect : FxResource;
		var effectTransform : WorldTransform;
		var hitEvent : gameprojectileHitEvent;
		var i : Int32;
		if( !( m_isActive ) )
		{
			return false;
		}
		super.OnCollision( eventData );
		for( i = 0; i < eventData.hitInstances.Size(); i += 1 )
		{
			hitInstance = eventData.hitInstances[ i ];
			if( hitInstance.isWaterSurfaceImpact )
			{
				m_hasHitWater = true;
				m_waterHeight = hitInstance.position.Z;
				effect = m_resourceLibraryComponent.GetResource( 'splash_effect' );
				if( FxResource.IsValid( effect ) )
				{
					WorldTransform.SetPosition( effectTransform, hitInstance.position );
					GameInstance.GetFxSystem( GetGame() ).SpawnEffect( effect, effectTransform );
				}
				RenderingSystem.AddWaterImpulse( hitInstance.position, m_waterImpulseRadius, m_waterImpulseStrength );
				continue;
			}
			enableInteractionEvent = new InteractionSetEnableEvent;
			enableInteractionEvent.enable = true;
			QueueEvent( enableInteractionEvent );
			if( !( m_hasHitWater ) || ( ( hitInstance.position.Z - m_waterHeight ) > m_deactivationDepth ) )
			{
				if( !( m_projectileStopped ) )
				{
					hitEvent = new gameprojectileHitEvent;
					hitEvent.hitInstances.PushBack( hitInstance );
					ProjectileHit( hitEvent );
				}
				if( m_projectileCollisionEvaluator.ProjectileStopAndStick() )
				{
					m_projectileStopped = true;
				}
				isObjectNPC = GetObject( hitInstance ).IsNPC();
				if( !( isObjectNPC || GetObject( hitInstance ).IsDrone() ) )
				{
					TriggerSingleStimuli( hitInstance, gamedataStimType.SoundDistraction );
				}
				else
				{
					m_isCollidedWithEnemy = true;
				}
			}
			break;
		}
	}

	protected event OnInteractionActivationEvent( evt : InteractionActivationEvent )
	{
		GameInstance.GetStatPoolsSystem( GetGame() ).RequestSettingStatPoolMaxValue( m_weapon.GetEntityID(), gamedataStatPoolType.ThrowRecovery, this );
		if( PlayerDevelopmentSystem.GetInstance( evt.activator ).IsNewPerkBought( evt.activator, gamedataNewPerkType.Cool_Right_Perk_3_1 ) == 1 )
		{
			if( m_isCollidedWithEnemy )
			{
				StatusEffectHelper.ApplyStatusEffect( evt.activator, T"BaseStatusEffect.IncreaseDamageOfNextMeleeAttackWithThrownWeaponSE" );
			}
		}
		TryToReleaseProjectile();
	}

	public function TryToReleaseProjectile()
	{
		if( !( m_wasPicked ) )
		{
			m_wasPicked = true;
			Release();
		}
	}

	protected event OnMaxLifetimeReached( evt : ProjectileDelayEvent )
	{
		TryToReleaseProjectile();
	}

	protected function DeactivateAndSink()
	{
		var parabolicTrajectoryParams : ParabolicTrajectoryParams;
		m_isActive = false;
		parabolicTrajectoryParams = ParabolicTrajectoryParams.GetAccelVelParabolicParams( Vector4( 0.0, 0.0, -0.2, 0.0 ), 0.2, 0.40000001 );
		m_projectileComponent.ClearTrajectories();
		m_projectileComponent.AddParabolic( parabolicTrajectoryParams );
	}

	public const override function GetDefaultHighlight() : FocusForcedHighlightData
	{
		var highlight : FocusForcedHighlightData;
		var outline : EFocusOutlineType;
		outline = GetCurrentOutline();
		highlight = new FocusForcedHighlightData;
		highlight.sourceID = GetEntityID();
		highlight.sourceName = GetClassName();
		highlight.priority = EPriority.High;
		highlight.outlineType = outline;
		highlight.patternType = VisionModePatternType.Default;
		highlight.highlightType = EFocusForcedHighlightType.IMPORTANT_INTERACTION;
		return highlight;
	}

	public const override function GetCurrentOutline() : EFocusOutlineType
	{
		return EFocusOutlineType.IMPORTANT_INTERACTION;
	}

}

class ThrowingMeleeCollisionEvaluator extends gameprojectileScriptCollisionEvaluator
{
	private var m_projectileStopAndStick : Bool;

	protected override function EvaluateCollision( defaultOnCollisionAction : gameprojectileOnCollisionAction, params : CollisionEvaluatorParams ) : gameprojectileOnCollisionAction
	{
		if( ( params.isWaterSurface || params.isPiercableSurface ) || params.projectilePenetration == 'Any' )
		{
			return gameprojectileOnCollisionAction.Pierce;
		}
		else if( params.isAutoBounceSurface )
		{
			return ( ( params.numBounces < 5 ) ? ( gameprojectileOnCollisionAction.Bounce ) : ( gameprojectileOnCollisionAction.Pierce ) );
		}
		else
		{
			m_projectileStopAndStick = true;
			return gameprojectileOnCollisionAction.StopAndStick;
		}
	}

	public function ProjectileStopAndStick() : Bool
	{
		return m_projectileStopAndStick;
	}

}

class ThrowingMeleeReloadListener extends ScriptStatPoolsListener
{
	private var m_melee : weak< MeleeProjectile >;

	public function Bind( melee : weak< MeleeProjectile > )
	{
		m_melee = melee;
	}

	protected event OnStatPoolMaxValueReached( value : Float )
	{
		m_melee.TryToReleaseProjectile();
	}

}

