//=============================================================================
// KFWeap_HRG_BlastBrawlers
//=============================================================================
// Power gloves and shotgun, all in one.
//=============================================================================
// Killing Floor 2
// Copyright (C) 2021 Tripwire Interactive LLC
//=============================================================================
class KFWeap_HRG_BlastBrawlers extends KFWeap_Blunt_PowerGloves;

var name ReloadAnimation;
var float ReloadAnimRateModifier;
var float ReloadAnimRateModifierElite;

var protected transient bool bWaitingForSecondShot; 
var protected transient int  NumAttacks;

/** A muzzle flash instance for left weapon */
var KFMuzzleFlash LeftMuzzleFlash;

simulated event PreBeginPlay()
{
	super.PreBeginPlay();
}

simulated function Shoot()
{
	// LocalPlayer Only
	if ( !Instigator.IsLocallyControlled()  )
	{
		return;
	}
	
	if( Role < Role_Authority )
	{
		// if we're a client, synchronize server
		ServerShoot();
	}

	ProcessShoot();
}

/** 
    Each attack shoots twice, once with the right and left fists.
    Ammo is decremented after the second shot.
*/
reliable server function bool ServerShoot()
{
	return ProcessShoot();
}

simulated function bool ProcessShoot()
{
	// Shooting only happens when default firing
	if(CurrentFireMode != DEFAULT_FIREMODE)
		return false;

	CustomFire();

	if (!bWaitingForSecondShot)
	{
		// AmmoCount[DEFAULT_FIREMODE] = Max(AmmoCount[DEFAULT_FIREMODE] - 1, 0);
		DecrementAmmo();
	}

	bWaitingForSecondShot = !bWaitingForSecondShot;
	return true;
}

simulated function DecrementAmmo()
{
	AmmoCount[DEFAULT_FIREMODE] = Max(AmmoCount[DEFAULT_FIREMODE] - 1, 0);
}

simulated state Active
{
	/**
	 * Called from Weapon:Active.BeginState when HasAnyAmmo (which is overridden above) returns false.
	 */
	simulated function WeaponEmpty()
	{
		local int i;

		// Copied from Weapon:Active.BeginState where HasAnyAmmo returns true.
		// Basically, pretend the weapon isn't empty in this case.
		for (i=0; i<GetPendingFireLength(); i++)
		{
			if (PendingFire(i))
			{
				BeginFire(i);
				break;
			}
		}
	}
}

static simulated event bool UsesAmmo()
{
	return true;
}

simulated function CustomFire()
{
	local byte CachedFireMode;

	CachedFireMode = CurrentFireMode;
	CurrentFireMode = CUSTOM_FIREMODE;

	ProjectileFire();

	// Let the accuracy tracking system know that we fired
	HandleWeaponShotTaken(CurrentFireMode);

	NotifyWeaponFired(CurrentFireMode);

	// Play fire effects now (don't wait for WeaponFired to replicate)
	PlayFireEffects(CurrentFireMode, vect(0, 0, 0));

	CurrentFireMode = CachedFireMode;
}

/** Overriden for the Tight Choke perk */
simulated function KFProjectile SpawnAllProjectiles(class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir)
{
	local KFPerk InstigatorPerk;

	if (CurrentFireMode == CUSTOM_FIREMODE)
	{
		InstigatorPerk = GetPerk();
		if (InstigatorPerk != none)
		{
			Spread[CurrentFireMode] = default.Spread[CurrentFireMode] * InstigatorPerk.GetTightChokeModifier();
		}
	}

	return super.SpawnAllProjectiles(KFProjClass, RealStartLoc, AimDir);
}

/** Override for not playing animations (even if noanimation is set it interrupts the melee ones.) */
simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation )
{
	// If we have stopped the looping fire sound to play single fire sounds for zed time
	// start the looping sound back up again when the time is back above zed time speed
	if( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !bPlayingLoopingFireSnd )
    {
        StartLoopingFireSound(FireModeNum);
    }

	PlayFiringSound(CurrentFireMode);

	if( Instigator != none )
	{
		if( Instigator.IsLocallyControlled() )
		{
			if( Instigator.IsFirstPerson() )
			{
				// Start muzzle flash effect
				CauseMuzzleFlash(FireModeNum);
			}

			ShakeView();
		}
	}
}

/**
 * @see Weapon::StartFire
 */
simulated function StartFire(byte FireModeNum)
{
	// can't start fire because it's in an uninterruptible state
	if (StartFireDisabled)
	{
		return;
	}

	if (FireModeNum == DEFAULT_FIREMODE)
	{
		if(AmmoCount[DEFAULT_FIREMODE] > 0)
		{
			StartMeleeFire(FireModeNum, DIR_FORWARD, ATK_Normal);
		} 
		else
		{
			super.StartFire(RELOAD_FIREMODE);
			// If not cleared, it will loop the animation.
			ClearPendingFire(RELOAD_FIREMODE);
		}
		return;
	}

	super.StartFire(FireModeNum);
}

/** Avoiding reload anim to interrupt combo */
simulated state MeleeChainAttacking
{
	simulated function BeginState(Name PrevStateName)
	{
		local KFPerk InstigatorPerk;

		if( CurrentFireMode == DEFAULT_FIREMODE )
		{
			StartFireDisabled = true;
			bWaitingForSecondShot = false;
			NumAttacks = 0;
		}
		
		InstigatorPerk = GetPerk();
		if (InstigatorPerk != none)
		{
			SetZedTimeResist( InstigatorPerk.GetZedTimeModifier(self) );
		}

		super.BeginState( PrevStateName );
	}

	simulated function EndState(Name NextStateName)
	{
		super.EndState(NextStateName);
		if( CurrentFireMode == DEFAULT_FIREMODE )
		{
			StartFireDisabled = false;
		}

		ClearZedTimeResist();
	}

	simulated function bool ShouldContinueMelee(optional int ChainCount)
	{
		if ( CurrentFireMode == DEFAULT_FIREMODE )
		{
			return false;
		}	
		return super.ShouldContinueMelee(ChainCount);
	}
}

/**
 * Called on a client, this function Attaches the WeaponAttachment
 * to the Mesh.
 *
 * Overridden to attach LeftMuzzleFlash
 */
simulated function AttachMuzzleFlash()
{
	super.AttachMuzzleFlash();

	if ( MySkelMesh != none )
	{
		if (MuzzleFlashTemplate != None)
		{
			LeftMuzzleFlash = new(self) Class'KFMuzzleFlash'(MuzzleFlashTemplate);
			LeftMuzzleFlash.AttachMuzzleFlash(MySkelMesh, 'MuzzleFlash_L');
		}
	}
}

/**
 * Causes the muzzle flash to turn on and setup a time to
 * turn it back off again.
 *
 * Overridden to cause left weapon flash
 */
simulated function CauseMuzzleFlash(byte FireModeNum)
{
	if( MuzzleFlash == None || LeftMuzzleFlash == None )
	{
		AttachMuzzleFlash();
	}

	if( bWaitingForSecondShot )
	{
		if (MuzzleFlash != None )
		{
			// Not ejecting shells for this weapon.
			MuzzleFlash.CauseMuzzleFlash(FireModeNum);
		}
	}
	else
	{
		if( LeftMuzzleFlash != None )
		{
			// Not ejecting shells for this weapon.
			LeftMuzzleFlash.CauseMuzzleFlash(FireModeNum);
		}
	}
}

/**
 * Remove/Detach the muzzle flash components
 */
simulated function DetachMuzzleFlash()
{
	super.DetachMuzzleFlash();

	if (MySkelMesh != none && LeftMuzzleFlash != None)
	{
		LeftMuzzleFlash.DetachMuzzleFlash(MySkelMesh);
		LeftMuzzleFlash = None;
	}
}

/**
 * Adjust the FOV for the first person weapon and arms.
 */
simulated event SetFOV( float NewFOV )
{
	super.SetFOV( NewFOV );

	if( LeftMuzzleFlash != none )
	{
		LeftMuzzleFlash.SetFOV( NewFOV );
	}
}

simulated function StopFireEffects(byte FireModeNum)
{
	super.StopFireEffects( FireModeNum );

	if (LeftMuzzleFlash != None)
	{
        LeftMuzzleFlash.StopMuzzleFlash();
	}
}

/** Returns true if weapon can potentially be reloaded */
simulated function bool CanReload(optional byte FireModeNum)
{
	if ( FiringStatesArray[RELOAD_FIREMODE] == 'WeaponUpkeep' )
	{
		return true;
	}

	if ( FireModeNum == CUSTOM_FIREMODE)
	{
		FireModeNum = DEFAULT_FIREMODE;
	}

	return Super.CanReload(FireModeNum);
}

simulated function name GetReloadAnimName( bool bTacticalReload )
{
	return ReloadAnimation;
}

/** No diferent states */
simulated function EReloadStatus GetNextReloadStatus(optional byte FireModeNum)
{
	switch ( ReloadStatus )
	{
		case RS_None: //drop
		case RS_Reloading:
			if ( HasSpareAmmo(FiremodeNum) && ReloadAmountLeft > 0 )
			{
				return RS_Reloading;
			}
	}

	return RS_Complete;
}

/** Returns an anim rate scale for reloading */
simulated function float GetReloadRateScale()
{
	local float Modifier;

	Modifier = UseTacticalReload() ? ReloadAnimRateModifierElite : ReloadAnimRateModifier;

	return super.GetReloadRateScale() * Modifier;
}

simulated function bool HasAnyAmmo()
{
	return AmmoCount[0] > 0 || SpareAmmoCount[0] > 0;
}

simulated function int GetMeleeDamage(byte FireModeNum, optional vector RayDir)
{
	local int Damage;

	Damage = GetModifiedDamage(FireModeNum, RayDir);
	// decode damage scale (see GetDamageScaleByAngle) from the RayDir
	if ( !IsZero(RayDir) )
	{
		Damage = Round(float(Damage) * FMin(VSize(RayDir), 1.f));
	}

	return Damage;
}

defaultproperties
{
	// Content
	PackageKey="HRG_BlastBrawlers"
	FirstPersonMeshName="WEP_1P_HRG_BlastBrawlers_MESH.WEP_1stP_HRG_Blast_Brawlers_Rig"
	FirstPersonAnimSetNames(0)="WEP_1P_HRG_BlastBrawlers_ANIM.WEP_1P_HRG_BlastBrawlers_ANIM"
	PickupMeshName="WEP_3P_HRG_BlastBrawlers_MESH.Wep_HRG_Blast_Brawlers_Pickup"
	AttachmentArchetypeName="WEP_HRG_BlastBrawlers_ARCH.Wep_HRG_BlastBrawlers_3P"
	MuzzleFlashTemplateName="WEP_HRG_BlastBrawlers_ARCH.Wep_HRG_BlastBrawler_MuzzleFlash"

	Begin Object Class=KFMeleeHelperWeaponBlastBrawlers Name=MeleeHelper_0
		MaxHitRange=230 //150 //190
		// Override automatic hitbox creation (advanced)
		HitboxChain.Add((BoneOffset=(Y=+3,Z=150)))
		HitboxChain.Add((BoneOffset=(Y=-3,Z=130)))
		HitboxChain.Add((BoneOffset=(Y=+3,Z=110)))
		HitboxChain.Add((BoneOffset=(Y=-3,Z=90)))
		HitboxChain.Add((BoneOffset=(Y=+3,Z=70)))
		HitboxChain.Add((BoneOffset=(Y=-3,Z=50)))
		HitboxChain.Add((BoneOffset=(Y=+3,Z=30)))
		HitboxChain.Add((BoneOffset=(Z=10)))
		HitboxChain.Add((BoneOffset=(Z=-10)))
		WorldImpactEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Blunted_melee_impact'
		// modified combo sequences
		bAllowMeleeToFracture=false
		bUseDirectionalMelee=true
		bHasChainAttacks=false
		MeleeImpactCamShakeScale=0.035f //0.4
		ChainSequence_F=()
		ChainSequence_B=()
		ChainSequence_L=()
		ChainSequence_R=()
	End Object
	MeleeAttackHelper=MeleeHelper_0

	// FOV
	//MeshFOV=95

    // Shotgun Ammo
	MagazineCapacity[0]=4 //3
	SpareAmmoCapacity[0]=40 //36 //28
	InitialSpareMags[0]=2
	AmmoPickupScale[0]=1.5 //2.0

	bCanBeReloaded=true
	bReloadFromMagazine=true
	bNoMagazine=false
	
	// Zooming/Position
	PlayerViewOffset=(X=20,Y=0,Z=0)

    // Inventory
	GroupPriority=110
	InventorySize=9 //7
	WeaponSelectTexture=Texture2D'WEP_UI_HRG_BlastBrawlers_TEX.UI_WeaponSelect_HRG_BlastBrawlers'
	
	FireModeIconPaths(CUSTOM_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunSingle'
	FiringStatesArray(CUSTOM_FIREMODE)=WeaponSingleFiring
	WeaponFireTypes(CUSTOM_FIREMODE)=EWFT_Projectile
	WeaponProjectiles(CUSTOM_FIREMODE)=class'KFProj_Bullet_BlastBrawlers'
    FireInterval(CUSTOM_FIREMODE)=0.1f
	InstantHitDamageTypes(CUSTOM_FIREMODE)=class'KFDT_Ballistic_BlastBrawlersShotgun'
    InstantHitDamage(CUSTOM_FIREMODE)=39.0 //36.0 //30.0
	AmmoCost(CUSTOM_FIREMODE)=0
	NumPellets(CUSTOM_FIREMODE)=5
	Spread(CUSTOM_FIREMODE)=0.12 //0.1 //0.15
	WeaponFireSnd(CUSTOM_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_BlastBrawlers.Play_WEP_HRG_BlastBrawlers_Shoot_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_BlastBrawlers.Play_WEP_HRG_BlastBrawlers_Shoot_1P')
	InstantHitMomentum(CUSTOM_FIREMODE)=1.0
	PenetrationPower(CUSTOM_FIREMODE)=2.0
	PenetrationDamageReductionCurve(CUSTOM_FIREMODE)=(Points=((InVal=0.f,OutVal=0.f),(InVal=1.f, OutVal=1.f)))

	FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunSingle'
	InstantHitDamage(DEFAULT_FIREMODE)=50
	InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Bludgeon_BlastBrawlers'

	FireModeIconPaths(HEAVY_ATK_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BluntMelee'
	InstantHitDamage(HEAVY_ATK_FIREMODE)=200 //175
	InstantHitDamageTypes(HEAVY_ATK_FIREMODE)=class'KFDT_Bludgeon_BlastBrawlersHeavy'

	InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_BlastBrawlersBash'
	InstantHitDamage(BASH_FIREMODE)=100

	FiringStatesArray(RELOAD_FIREMODE)=Reloading

	AssociatedPerkClasses(0)=class'KFPerk_Support'

	// Block Sounds
	BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Crovel'
	ParrySound=AkEvent'WW_WEP_Bullet_Impacts.Play_Parry_Metal'

	ParryStrength=5
	ParryDamageMitigationPercent=0.40
	BlockDamageMitigation=0.50 //0.40

	bWaitingForSecondShot = false
	NumAttacks = 0

	bAllowClientAmmoTracking=false

	ReloadAnimation = "Atk_B"
	ReloadAnimRateModifier = 1.6f
	ReloadAnimRateModifierElite = 1.0f; //0.5f;

	WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.125f), (Stat=EWUS_Damage1, Scale=1.125f), (Stat=EWUS_Damage2, Scale=1.125f), (Stat=EWUS_Weight, Add=1)))
}
