//=============================================================================
// KFWeapAttach_CompoundBow
//=============================================================================
// A custom 3rd person weapon template for the CompoundBow
//=============================================================================
// Killing Floor 2
// Copyright (C) 2020 Tripwire Interactive LLC
// Tulio Beloqui (Saber Interactive)
//=============================================================================
class KFWeapAttach_CompoundBow extends KFWeaponAttachment;

/** The static mesh for arrow **/
var() StaticMeshComponent ArrowSMC;

/** The partcile FX to display for the Cryo Projectile **/
var ParticleSystem CryoProjectileEffectOn;
var ParticleSystem CryoProjectileEffectOff;
var() ParticleSystemComponent CryoProjectilePSC;

/** set to True if we started charging **/
var bool bCharging;

const WeaponIdleAnim = 'Idle_master';
const WeaponIronIdleAnim = 'Idle_Iron_V1';
const WeaponFireLoopStartAnim = 'ShootLoop_Start';
const WeaponFireLoopAnim = 'ShootLoop';
const WeaponFireLoopIronAnim = 'ShootLoop_Sight';
const WeaponFireEndLoopAnim = 'ShootLoop_End';

const CH_WeapondleAnim = 'Idle_CH_V1';
const CH_WeaponFireStartAnim = 'ShootLoop_Start_CH';
const CH_WeaponFireLoopAnim = 'ShootLoop_CH';
const CH_WeaponFireLoopIronAnim = 'ShootLoop_Sight_CH';
const CH_WeaponFireEndLoopAnim = 'ShootLoop_End_CH';

const ArrowSocketName = 'RW_Weapon';

simulated function StartFire()
{
	local KFPawn_Human P;
	local name WeaponLoopAnimName;
	
	P = KFPawn_Human(Owner);

	bCharging = true;
	WeaponLoopAnimName = GetWeaponFireLoopAnim (P);

	// ShootLoop animation is determined in PlayLoopAnim(), our "outro" animation is the actual ShootLoop_End:
	if (P.bIsCrouched)
	{
		StartLoopingAnim(P, WeaponLoopAnimName, CH_WeaponFireStartAnim, '', true);
	}
	else
	{
		StartLoopingAnim(P, WeaponLoopAnimName, WeaponFireLoopStartAnim, '', true);
	}
}

simulated function PlayWeaponFireAnim()
{
	local float Duration;

	if (Instigator.bIsCrouched)
	{
		Duration = WeapMesh.GetAnimLength( CH_WeaponFireEndLoopAnim );
		WeapMesh.PlayAnim( CH_WeaponFireEndLoopAnim, Duration / ThirdPersonAnimRate,, true );
	}
	else
	{
		Duration = WeapMesh.GetAnimLength( WeaponFireEndLoopAnim );
		WeapMesh.PlayAnim( WeaponFireEndLoopAnim, Duration / ThirdPersonAnimRate,, true );
	}
}

simulated function PlayShoot(KFPawn P)
{
	PlayPawnFireAnim (P, EAS_Additive);

	if (P.bIsCrouched)
	{
		P.PlayBodyAnim(CH_WeaponFireEndLoopAnim, EAS_CH_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime);
	}
	else
	{
		P.PlayBodyAnim(WeaponFireEndLoopAnim, EAS_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime);
	}
}

simulated function PlayReloadMagazineAnim (EWeaponState NewWeaponState, KFPawn P)
{
	Super.PlayReloadMagazineAnim (NewWeaponState, P);
	SetTimer( 0.15f, false, nameof(ShowArrowOnReload) );
}

simulated function ShowArrowOnReload()
{
	// We show the arrow since we are reloading we know that we have one
	SetArrowVisbility (true);
}

simulated function UpdateThirdPersonWeaponAction( EWeaponState NewWeaponState, KFPawn P, byte ThirdPersonAnimRateByte )
{
	local KFPawn_Human PHuman;

	PHuman = KFPawn_Human (P);

	Super.UpdateThirdPersonWeaponAction( NewWeaponState, P, ThirdPersonAnimRateByte );

	switch (NewWeaponState)
	{
	case WEP_Grenade:
		SetArrowVisbility (false);
		break;

	case WEP_Equipping:
	case WEP_Idle:

		if (P.IsLocallyControlled ())
		{
			ToggleShowHideArrow (P);
		}
		else
		{
			/** just show the arrow if we cannot access AmmoCount from MyKFWeapon **/
			SetArrowVisbility (true);
		}
		
		/** restore Cryo visibility based on the Pawn alt fire mode **/
		if (PHuman != none)
		{
			if (PHuman.bUsingAltFireMode)
			{
				ToggleCryoFX (true);
			}
		}

		break;
	default:
		break;
	}
}

simulated function bool ThirdPersonFireEffects (vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte)
{
	bCharging = false;

    SpawnTracer(GetMuzzleLocation(), HitLocation);

	// Effects below this point are culled based on visibility and distance
	if ( !ActorEffectIsRelevant(P, false, MaxFireEffectDistance) )
	{
		return false;
	}

	DecodeThirdPersonAnimRate( ThirdPersonAnimRateByte );

	if (P.FiringMode == 0 || P.FiringMode == 1)
	{
		SetArrowVisbility (false);
	}

	// Weapon shoot anims
	PlayWeaponFireAnim();

	// Character shoot anims
	if ( !P.IsDoingSpecialMove())
	{
		PlayShoot (P);
		
		if ( OnWeaponStateChanged != None )
		{
			OnWeaponStateChanged(true);
		}
	}

	CauseMuzzleFlash(P.FiringMode);
	return true;
}

simulated function ToggleShowHideArrow (KFPawn P)
{
	if (P.MyKFWeapon != none)
	{
		if (P.MyKFWeapon.AmmoCount[0] > 0)
		{
			SetArrowVisbility (true);
		}
		else
		{
			SetArrowVisbility (false);
		}
	}
}

simulated function SetArrowVisbility (bool bVisible)
{
	// Keep the arrow from the weapon mesh unvisible
	WeapMesh.HideBoneByName (ArrowSocketName, PBO_None);

	if (bVisible)
	{
		ArrowSMC.SetHidden(false);
		CryoProjectilePSC.SetHidden(false);
	}
	else
	{
		ArrowSMC.SetHidden(true);
		CryoProjectilePSC.SetHidden(true);
	}
}

simulated function StartPawnCrouch ()
{
	if (KFPawn_Human (Owner) != none)
	{
		ResetAnimationState (KFPawn_Human (Owner));
	}
}

simulated function EndPawnCrouch ()
{
	if (KFPawn_Human (Owner) != none)
	{
		ResetAnimationState (KFPawn_Human (Owner));
	}
}

simulated function SetWeaponUsingIronSights (bool bUsingIronSights)
{
	if (KFPawn_Human (Owner) != none)
	{
		ResetAnimationState (KFPawn_Human (Owner));
	}
}

simulated function SetWeaponAltFireMode (bool bUsingAltFireMode)
{
	ToggleCryoFX (bUsingAltFireMode);
}

simulated function ResetAnimationState (KFPawn_Human P)
{
	PlayCharacterMeshAnim (P, GetCharacterAnimationName(P), true, true);
}

simulated function ToggleCryoFX(bool bEnable)
{
	if (bEnable)
	{
		CryoProjectilePSC.DeactivateSystem();
		CryoProjectilePSC.SetTemplate(CryoProjectileEffectOn);
		CryoProjectilePSC.ActivateSystem();
	}
	else
	{
		CryoProjectilePSC.DeactivateSystem();
		CryoProjectilePSC.SetTemplate(CryoProjectileEffectOff);
		CryoProjectilePSC.ActivateSystem();
	}
}

simulated function name GetWeaponFireLoopAnim (KFPawn_Human P)
{
	if (P.bIsCrouched && P.bUsingIronSights)
	{
		return CH_WeaponFireLoopIronAnim;
	}
	if (P.bIsCrouched)
	{
		return CH_WeaponFireLoopAnim;
	}
	if (P.bUsingIronSights)
	{
		return WeaponFireLoopIronAnim;
	}

	return WeaponFireLoopAnim;
}

simulated function name GetCharacterAnimationName (KFPawn_Human P)
{
	if (bCharging)
	{
		return GetWeaponFireLoopAnim (P);
	}
	if (P.bUsingIronSights && P.bIsCrouched)
	{
		return CH_WeaponFireLoopIronAnim;
	}
	if (P.bUsingIronSights)
	{
		return WeaponIronIdleAnim;
	}
	if (P.bIsCrouched)
	{
		return CH_WeapondleAnim;
	}
	
	return WeaponIdleAnim;
}

simulated function AttachTo (KFPawn P)
{
	local byte WeaponAnimSetIdx;

    if ( WeapMesh != None )
	{
		// Attach Weapon mesh to player skel mesh
		WeapMesh.SetShadowParent(P.Mesh);
		P.Mesh.AttachComponent (WeapMesh, 'LW_Weapon');
		
		// Keep the arrow from the weapon mesh unvisible
		WeapMesh.HideBoneByName (ArrowSocketName, PBO_None);
	}

	// Animation
	if ( CharacterAnimSet != None )
	{
		WeaponAnimSetIdx = P.CharacterArch.GetWeaponAnimSetIdx();
		P.Mesh.AnimSets[WeaponAnimSetIdx] = CharacterAnimSet;
		// update animations will reload all AnimSeqs with the new AnimSet
		P.Mesh.UpdateAnimations();
	}

	// update aim offset nodes with new profile for this weapon
	P.SetAimOffsetNodesProfile(AimOffsetProfileName);

    //Do a first chance weapon skin switch (EX: changed weapon w/o ID changing by throwing a dualie)
    if (KFPawn_Human(P) != None && KFPawn_Human(P).WeaponSkinItemId > 0)
    {
        SetWeaponSkin(KFPawn_Human(P).WeaponSkinItemId);
    }

	if (ArrowSMC != none)
	{
		P.Mesh.AttachComponentToSocket(ArrowSMC, ArrowSocketName);
	}

	if (CryoProjectilePSC != none)
	{
		P.Mesh.AttachComponentToSocket(CryoProjectilePSC, ArrowSocketName);
	}

	if (P.MyKFWeapon != none && P.MyKFWeapon.bUseAltFireMode)
	{
		ToggleCryoFX (true);
	}
}

simulated function DetachFrom(KFPawn P)
{
	if (CryoProjectilePSC != none)
	{
		CryoProjectilePSC.DeactivateSystem();
		CryoProjectilePSC.DetachFromAny();
	}

	if (ArrowSMC != none)
	{
		P.DetachComponent(ArrowSMC);
		ArrowSMC.DetachFromAny();
	}

    Super.DetachFrom(P);
}

event SetWeaponSkin(int ItemId, optional bool bFinishedLoading = false)
{
	local array<MaterialInterface> SkinMICs;

	if ( WeapMesh != none && ArrowSMC != none && ItemId > 0 && WorldInfo.NetMode != NM_DedicatedServer && !bWaitingForWeaponSkinLoad)
	{
		if (!bFinishedLoading && StartLoadWeaponSkin(ItemId))
		{
			return;
		}

		SkinMICs = class'KFWeaponSkinList'.static.GetWeaponSkin(ItemId, WST_ThirdPerson);
		if ( SkinMICs.Length > 0 )
		{
			WeapMesh.SetMaterial(0, SkinMICs[0]);
			ArrowSMC.SetMaterial(0, SkinMICs[0]);
		}
	}
}

defaultproperties
{
	Begin Object Class=StaticMeshComponent Name=ArrowStaticMeshComp0
		StaticMesh=StaticMesh'WEP_3P_CompoundBow_MESH.Wep_3rdP_Compound_Bow_Arrow_Resource'
	End Object
	ArrowSMC=ArrowStaticMeshComp0

	Begin Object Class=KFParticleSystemComponent Name=CryoArrowParticleComp0
		bAutoActivate=FALSE
		TickGroup=TG_PostUpdateWork
	End Object
	CryoProjectilePSC=CryoArrowParticleComp0

	CryoProjectileEffectOn=ParticleSystem'WEP_CompoundBow_EMIT.FX_Compound_Bow_Cryo_Idle_3P'
	CryoProjectileEffectOff=ParticleSystem'WEP_CompoundBow_EMIT.FX_Compound_Bow_Cryo_ToggleOff'
}
