//=============================================================================
// KFWeap_SMG_G18
//=============================================================================
// full auto pistol with riot shield
//=============================================================================
// Killing Floor 2
// Copyright (C) 2019 Tripwire Interactive LLC
//=============================================================================

class KFWeap_SMG_G18 extends KFWeap_SMGBase;

// default fx
var AkBaseSoundObject BlockSound;
var ParticleSystem BlockParticleSystem;
var name BlockEffectsSocketName;

var array<BlockEffectInfo> BlockTypes;
var float BlockDamageMitigation;

var const name IdleToIronSightAnim;
var const name IronSightToIdleAnim;

var float BlockAngle;
var transient float BlockAngleCos;

/** Explosion actor class to spawn */
var class<KFExplosionActor> ExplosionActorClass;
var() KFGameExplosion ExplosionTemplate;

simulated event PostBeginPlay()
{
	super.PostBeginPlay();
	BlockAngleCos = cos((BlockAngle / 2.f) * DegToRad);
}

/*********************************************************************************************
 * State Active
 * A Weapon this is being held by a pawn should be in the active state.  In this state,
 * a weapon should loop any number of idle animations, as well as check the PendingFire flags
 * to see if a shot has been fired.
 *********************************************************************************************/
simulated state Active
{
	simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo)
	{
		GotoState('ActiveIronSights');
	}
}

/*********************************************************************************************
 * State ActiveIronSights
 * Plays an animation when transitioning to or from iron sights, but this animation
 * should only be played in the "Active" state. This state allows us to encapsulate this
 * functionality.
 *********************************************************************************************/
simulated state ActiveIronSights extends Active
{
	simulated function BeginState(Name PreviousStateName)
	{
		local float ZoomTimeToGo;
		ZoomTimeToGo = MySkelMesh.GetAnimLength(IdleToIronSightAnim);

		Global.ZoomIn(true, ZoomTimeToGo);

		PlayAnimation(IdleToIronSightAnim, ZoomTime, false);
	}

	simulated function ZoomOut(bool bAnimateTransition, float ZoomTimeToGo)
	{
		ZoomTimeToGo = MySkelMesh.GetAnimLength(IronSightToIdleAnim);

		Global.ZoomOut(true, ZoomTimeToGo);

		PlayAnimation(IronSightToIdleAnim, ZoomTime, false);

		GotoState('Active');
	}
}

/*********************************************************************************************
 * State MeleeAttackBasic
 * This is a basic melee state that's used as a base for other more advanced states
 *********************************************************************************************/

simulated state MeleeAttackBasic
{
	simulated function NotifyMeleeCollision(Actor HitActor, optional vector HitLocation)
	{
		local KFPawn Victim;

		if (Role == ROLE_Authority)
		{
			if (HitActor.bWorldGeometry)
			{
				return;
			}

			Victim = KFPawn(HitActor);
			if (Victim == None ||
			   (Victim.GetTeamNum() == Instigator.GetTeamNum()) ||
			   (Victim.bPlayedDeath && `TimeSince(Victim.TimeOfDeath) > 0.f))
			{
				return;
			}

			// need to delay one frame, since this is called from AnimNotify
			SetTimer(0.001f, false, nameof(DoBashImpulse));
		}
	}
}

simulated function DoBashImpulse()
{
	local KFExplosionActor ExploActor;
	local vector SpawnLoc;
	local rotator SpawnRot;

	if (Instigator.Role < ROLE_Authority)
	{
		return;
	}

	SpawnLoc = Instigator.Location;
	SpawnRot = Instigator.Rotation;

	// nudge backwards to give a wider code near the player
	SpawnLoc += vector(SpawnRot);

	// explode using the given template
	ExploActor = Spawn(ExplosionActorClass, self,, SpawnLoc, SpawnRot,, true);
	if (ExploActor != None)
	{
		ExploActor.InstigatorController = Instigator.Controller;
		ExploActor.Instigator = Instigator;
		ExploActor.bReplicateInstigator = true;
		ExploActor.Explode(ExplosionTemplate, vector(SpawnRot));
	}
}

/**
 * State Reloading
 * State the weapon is in when it is being reloaded (current magazine replaced with a new one, related animations and effects played).
 */
simulated state Reloading
{
	/** Cancel reload when going into ironsights */
	simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo)
	{
		GotoState('ActiveIronSights');
		AbortReload();
	}
}

function AdjustDamage(out int InDamage, class<DamageType> DamageType, Actor DamageCauser)
{
	local KFPerk InstigatorPerk;
	local byte BlockTypeIndex;
	local float DmgCauserDot;

	if (!bUsingSights)
	{
		return;
	}

	// don't apply block effects for teammates
	if (Instigator.IsSameTeam(DamageCauser.Instigator))
	{
		return;
	}

	if (CanBlockDamageType(DamageType, BlockTypeIndex))
	{
		if (ClassIsChildOf(DamageCauser.class, class'Projectile'))
		{
			// Projectile might be beyond/behind player, resulting in bad dot
			// Projectile won't have a velocity to check against, either
			// Assume velocity is the vector between projectile and instigator
			DmgCauserDot = Normal(DamageCauser.Instigator.Location - DamageCauser.Location) dot vector(Instigator.Rotation);
		}
		else
		{
			DmgCauserDot = Normal(DamageCauser.Location - Instigator.Location) dot vector(Instigator.Rotation);
		}

		if (DmgCauserDot > BlockAngleCos)
		{
			InDamage *= GetUpgradedBlockDamageMitigation(CurrentWeaponUpgradeIndex);
			ClientPlayBlockEffects(BlockTypeIndex);

			InstigatorPerk = GetPerk();
			if (InstigatorPerk != none)
			{
				InstigatorPerk.SetSuccessfullBlock();
			}
		}
	}
}

/** If true, this damage type can be blocked by the MeleeBlocking state */
function bool CanBlockDamageType(class<DamageType> DamageType, optional out byte out_Idx)
{
	local int Idx;

	// Check if this damage should be ignored completely
	for (Idx = 0; Idx < BlockTypes.length; ++Idx)
	{
		if (ClassIsChildOf(DamageType, BlockTypes[Idx].DmgType))
		{
			out_Idx = Idx;
			return true;
		}
	}

	out_Idx = INDEX_NONE;
	return false;
}

/** Called on the server when successfully block/parry an attack */
unreliable client function ClientPlayBlockEffects(optional byte BlockTypeIndex=255)
{
	local AkBaseSoundObject Sound;
	local ParticleSystem PSTemplate;

	GetBlockEffects(BlockTypeIndex, Sound, PSTemplate);
	PlayLocalBlockEffects(Sound, PSTemplate);
}

/** Returns sound and particle system overrides using index into BlockTypes array */
simulated function GetBlockEffects(byte BlockIndex, out AKBaseSoundObject outSound, out ParticleSystem outParticleSys)
{
	outSound = BlockSound;
	outParticleSys = BlockParticleSystem;

	if (BlockIndex != 255)
	{
		if (BlockTypes[BlockIndex].BlockSound != None)
		{
			outSound = BlockTypes[BlockIndex].BlockSound;
		}
		if (BlockTypes[BlockIndex].BlockParticleSys != None)
		{
			outParticleSys = BlockTypes[BlockIndex].BlockParticleSys;
		}
	}
}

/** Called on the client when successfully block/parry an attack */
simulated function PlayLocalBlockEffects(AKBaseSoundObject Sound, ParticleSystem PSTemplate)
{
	local vector Loc;
	local rotator Rot;
	local ParticleSystemComponent PSC;

	if (Sound != None)
	{
		PlaySoundBase(Sound, true);
	}

	if (PSTemplate != None)
	{
		if (MySkelMesh.GetSocketWorldLocationAndRotation(BlockEffectsSocketName, Loc, Rot))
		{
			PSC = WorldInfo.MyEmitterPool.SpawnEmitter(PSTemplate, Loc,  Rot);
			PSC.SetDepthPriorityGroup(SDPG_Foreground);
		}
		else
		{
			`log(self@GetFuncName()@"missing BlockEffects Socket!");
		}
	}
}

/*********************************************************************************************
 * Upgrades
 ********************************************************************************************/

static simulated function float GetUpgradedBlockDamageMitigation(int UpgradeIndex)
{
	return GetUpgradedStatValue(default.BlockDamageMitigation, EWUS_BlockDmgMitigation, UpgradeIndex);
}

defaultproperties
{
	FireSightedAnims[0]=Shoot_Iron
	FireSightedAnims[1]=Shoot_Iron2
	FireSightedAnims[2]=Shoot_Iron3

	// Inventory
	InventorySize=8
	GroupPriority=100
	WeaponSelectTexture=Texture2D'WEP_UI_RiotShield_TEX.UI_WeaponSelect_RiotShield'

	// FOV
	MeshFOV=96
	MeshIronSightFOV=75
	PlayerIronSightFOV=64

	// Zooming/Position
	IronSightPosition=(X=0,Y=0,Z=0)
	PlayerViewOffset=(X=10.f,Y=0.f,Z=-7.0f)

	IdleToIronSightAnim=Iron_Shield_Up
	IronSightToIdleAnim=Iron_Shield_Down

	// Content
	PackageKey="RiotShield"
	FirstPersonMeshName="WEP_1P_RiotShield_MESH.Wep_1P_RiotShield_Rig"
	FirstPersonAnimSetNames(0)="WEP_1P_RiotShield_ANIM.Wep_1stP_RiotShield_Anim"
	PickupMeshName="WEP_3P_RiotShield_MESH.Wep_3P_RiotShield_Pickup"
	AttachmentArchetypeName="WEP_RiotShield_ARCH.Wep_G18_3P"
	MuzzleFlashTemplateName="WEP_RiotShield_ARCH.Wep_G18_MuzzleFlash"

	// Ammo
	MagazineCapacity[0]=33
	SpareAmmoCapacity[0]=462
	InitialSpareMags[0]=4
	bCanBeReloaded=true
	bReloadFromMagazine=true

	bHasFireLastAnims=true
	BonesToLockOnEmpty=(RW_Bolt, RW_Bullets1, RW_Bullets2, RW_Barrel)

	// Recoil
	maxRecoilPitch=100
	minRecoilPitch=75
	maxRecoilYaw=85
	minRecoilYaw=-85
	RecoilRate=0.045
	RecoilMaxYawLimit=500
	RecoilMinYawLimit=65035
	RecoilMaxPitchLimit=900
	RecoilMinPitchLimit=65035
	RecoilISMaxYawLimit=100
	RecoilISMinYawLimit=65435
	RecoilISMaxPitchLimit=375
	RecoilISMinPitchLimit=65460
	IronSightMeshFOVCompensationScale=1.65
	WalkingRecoilModifier=1.1
	JoggingRecoilModifier=1.2

	// Block FX
	BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Hammer'
	BlockParticleSystem=ParticleSystem'FX_Impacts_EMIT.FX_Block_melee_01'
	BlockEffectsSocketName=BlockEffect

	// Blocking
	BlockTypes.Add((DmgType=class'KFDT_Bludgeon', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_Slashing', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_Fire_HuskFireball', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_Fire_HuskFlamethrower'))
	BlockTypes.Add((DmgType=class'KFDT_BloatPuke'))
	BlockTypes.Add((DmgType=class'KFDT_EvilDAR_Rocket', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_EvilDAR_Laser', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_DAR_EMPBlast', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_Ballistic_PatMinigun', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_Explosive_PatMissile', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_Ballistic_HansAK12', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_EMP_MatriarchTeslaBlast', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
	BlockTypes.Add((DmgType=class'KFDT_EMP_MatriarchPlasmaCannon'))
	BlockTypes.Add((DmgType=class'KFDT_FleshpoundKing_ChestBeam'))
	BlockDamageMitigation=0.4

	// How many degrees in front of the player to block
	// 0 = don't block anything
	// 180 = block everything in front of player
	// 360 = block everything all around player
	BlockAngle=170.f

	// For procedural weapon hiding
	QuickWeaponDownRotation=(Pitch=-8192,Yaw=0,Roll=0)

	// DEFAULT_FIREMODE
	FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto'
	FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring
	WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit
	WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_G18'
	InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_G18'
	FireInterval(DEFAULT_FIREMODE)=+.05455 // 1100 RPM
	Spread(DEFAULT_FIREMODE)=0.06
	InstantHitDamage(DEFAULT_FIREMODE)=28
	FireOffset=(X=30,Y=6.5,Z=-4)

	// ALT_FIREMODE
	FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle'
	FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring
	WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_InstantHit
	WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_G18'
	InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_G18'
	FireInterval(ALTFIRE_FIREMODE)=+0.175 // 343 RPM
	InstantHitDamage(ALTFIRE_FIREMODE)=28
	Spread(ALTFIRE_FIREMODE)=0.015

	// BASH_FIREMODE
	InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_G18Shield'
	InstantHitMomentum(BASH_FIREMODE)=10000.f
	InstantHitDamage(BASH_FIREMODE)=35

	// Explosion settings.  Using archetype so that clients can serialize the content
	// without loading the 1st person weapon content (avoid 'Begin Object')!
	ExplosionActorClass=class'KFExplosionActorReplicated'
	ExplosionTemplate=KFGameExplosion'WEP_RiotShield_ARCH.Wep_G18_Shield_Impulse'

	//@todo: add akevents when we have them
	WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Auto_3P_Loop', FirstPersonCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Auto_Loop')
	WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Fire_3P_Single', FirstPersonCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Fire_1P_Single')
	WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_MedicSMG.Play_SA_MedicSMG_Handling_DryFire'
	WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_MedicDart.Play_WEP_SA_Medic_Dart_DryFire'

	// Advanced (High RPM) Fire Effects
	bLoopingFireAnim(DEFAULT_FIREMODE)=true
	bLoopingFireSnd(DEFAULT_FIREMODE)=true
	WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Fire_3P_EndLoop', FirstPersonCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Fire_1P_EndLoop')
	SingleFireSoundIndex=ALTFIRE_FIREMODE

	// Attachments
	bHasIronSights=true
	bHasFlashlight=true

	//Perks
	AssociatedPerkClasses(0)=class'KFPerk_Swat'

	//Weapon Upgrade
	WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.2f), (Stat=EWUS_Damage1, Scale=1.2f), (Stat=EWUS_Weight, Add=1)))
}
