/*
 * Class:  Elite_FlakCannon
 * By: James Bishop A.K.A. *PingFre@K*
 * Copyright 1998-2009 Epic Games, Inc. All Rights Reserved.
*/

class Elite_FlakCannon extends Elite_Weapon
	dependson(MutEM_XX);

var float SpreadDist;
var UTSkeletalMeshComponent SkeletonFirstPersonMesh;

var int curTensOdometer;
var int curOnesOdometer;
var float OdometerMaxPerSecOnes;
var float OdometerMaxPerSecTens;
var name OnesPlaceSkelName;
var name TensPlaceSkelName;
var class<Projectile> CenterShardClass;

var() int FlakRecoil;

simulated function PostBeginPlay()
{
	super.PostBeginPlay();

   if ( class'MutEM_XX'.default.Config_FlakCannon.bDoubles )
   {
   BecomeDual();
   }

	if ( Role == ROLE_Authority )
		SetTimer(FireInterval[0] * 1, true, 'AmmoRegen');
		
	AIRating=class'MutEM_XX'.default.Config_FlakCannon.fAIRating;
	MaxDesireability=class'MutEM_XX'.default.Config_FlakCannon.fAIRating;
	CurrentRating=class'MutEM_XX'.default.Config_FlakCannon.fAIRating;
	
   FlakRecoil=class'MutEM_XX'.default.Config_FlakCannon.iFlakRecoil;
   
	FireInterval[0]=class'MutEM_XX'.default.Config_FlakCannon.fPriFireRate;
	FireInterval[1]=class'MutEM_XX'.default.Config_FlakCannon.fSecFireRate;
   
	AmmoCount=class'MutEM_XX'.default.Config_FlakCannon.iAmmoCount;
	LockerAmmoCount=class'MutEM_XX'.default.Config_FlakCannon.iAmmoCount;
	MaxAmmoCount=class'MutEM_XX'.default.Config_FlakCannon.iMaxAmmo;

	SpreadDist=class'MutEM_XX'.default.Config_FlakCannon.fSpread;
	
	ShotCost[0]=class'MutEM_XX'.default.Config_FlakCannon.iShotCost;
	ShotCost[1]=class'MutEM_XX'.default.Config_FlakCannon.iShotCost;
}

function AmmoRegen()
{
   if ( class'MutEM_XX'.default.Config_FlakCannon.bAmmoRegeneration )
   {
	   if ( Instigator != None && !IsFiring() && AmmoCount < class'MutEM_XX'.default.Config_FlakCannon.iMaxAmmo )
	   {
		   AddAmmo(1);
	   }
   }
}

/* I'm making the recoil being handled in this function */
simulated function ProcessRecoil()
{
   if (Instigator != None)
   {
	   Instigator.AddVelocity( Normal(vector(Rotation) * -1 ) * class'MutEM_XX'.default.Config_FlakCannon.iFlakRecoil / Instigator.Mass,
      Instigator.Location,
	   InstantHitDamageTypes[0] );
   }
}

/**
 * Detect that we are trying to pickup another link gun and switch in to dual mode.
 */
function bool DenyPickupQuery(class<Inventory> ItemClass, Actor Pickup)
{
	if (ItemClass==class && DualMode != EDM_Dual)
	{
      if ( class'MutEM_XX'.default.Config_FlakCannon.bDoubles )
      {
		   DualMode = EDM_DualEquipping;
		   BecomeDual();			// Handle any animations/effects locally.
		}
	}
	return super.DenyPickupQuery(ItemClass, Pickup);
}

simulated function Projectile ProjectileFire()
{
	local vector		RealStartLoc;
	local UTProj_FlakShell	SpawnedProjectile;

	// tell remote clients that we fired, to trigger effects
	IncrementFlashCount();

	if( Role == ROLE_Authority )
	{
		// this is the location where the projectile is spawned.
		RealStartLoc = GetPhysicalFireStartLoc();

		// Spawn projectile
		SpawnedProjectile = Spawn(class'EM_XX.Elite_Flak_Shell',,, RealStartLoc);
		if( SpawnedProjectile != None && !SpawnedProjectile.bDeleteMe )
		{
			SpawnedProjectile.Init( Vector(GetAdjustedAim( RealStartLoc )) );
		}

		// Return it up the line
		return SpawnedProjectile;
	}

	return None;
}

/**
 * GetAdjustedAim begins a chain of function class that allows the weapon, the pawn and the controller to make
 * on the fly adjustments to where this weapon is pointing.
 */
simulated function Rotator GetAdjustedAim( vector StartFireLoc )
{
	local rotator R;

	// Start the chain, see Pawn.GetAdjustedAimFor()
	if( Instigator != None )
	{
		R = Instigator.GetAdjustedAimFor( Self, StartFireLoc );

		if ( (PlayerController(Instigator.Controller) != None) && (CurrentFireMode == 1) )
		{
			R.Pitch = R.Pitch & 65535;
			if ( R.Pitch < 16384 )
			{
				R.Pitch += (16384 - R.Pitch)/32;
			}
			else if ( R.Pitch > 49152 )
			{
				R.Pitch += 512;
			}
		}
	}

	return R;
}

simulated event ReplicatedEvent(name VarName)
{
	if ( VarName == 'AmmoCount')
	{
		UpdateAmmoCount();
	}

	Super.ReplicatedEvent(VarName);
}

simulated state WeaponFiring
{
	simulated event ReplicatedEvent(name VarName)
	{
		if ( VarName == 'AmmoCount')
		{
			UpdateAmmoCount();
		}
		Super.ReplicatedEvent(VarName);
	}

	simulated function BecomeDual()
	{
		Global.BecomeDual();
		if ( DualMode == EDM_Dual )
		{
			bForceReturnToActive=true;
		}
	}

	/**
	 * We poll the PendingFire in RefireCheckTimer() to insure the switch to Burst Firing
	 * respects the current shot timings
	 */

	simulated function RefireCheckTimer()
	{
		// if switching to another weapon, abort firing and put down right away
		if( bWeaponPutDown )
		{
			PutDownWeapon();
			return;
		}

		if (CurrentFireMode == 1 )
		{
			GotoState('Active');
		}

		if (CurrentFireMode == 0 && bForceReturnToActive)
		{
			GotoState('Active');
			return;
		}
/*
		if ( PendingFire(1) )
		{
			SendToFiringState(1);
			return;
		}
*/
		Super.RefireCheckTimer();
	}

	simulated function EndState(name NextStateName)
	{
		if (CurrentFireMode == 0 )
		{
			ShotCount = 0;
		}
		super.EndState(NextStateName);
	}
}

function int AddAmmo( int Amount )
{
	local int rvalue;
	rvalue = super.AddAmmo(Amount);
	UpdateAmmoCount();
	return rvalue;
}

simulated function HideFlakCannonAmmo()
{
    local SkelControlSingleBone SkelControl;
    if (AmmoCount <= 1)
    {
	    //Hide the flak cannon ammo in the chamber
	    SkelControl = SkelControlSingleBone(SkeletonFirstPersonMesh.FindSkelControl('AmmoScale'));
	    if(SkelControl != none)
	    {
	    	SkelControl.SetSkelControlStrength(1.0f, 0.0f);
	    }
    }
}

simulated function UpdateAmmoCount()
{
	local SkelControlSingleBone SkelControl;
	local int AmmoCountOnes;
	local int AmmoCountTens;

	if(Instigator.IsHumanControlled() && Instigator.IsLocallyControlled())
	{
		AmmoCountOnes = AmmoCount%10;
		AmmoCountTens = (AmmoCount - AmmoCountOnes)/10;
		SkelControl = SkelControlSingleBone(SkeletonFirstPersonMesh.FindSkelControl('OnesDisplay'));
		if(SkelControl != none)
		{
			SkelControl.BoneRotation.Pitch = -AmmoCountOnes*6554;
		}
		SkelControl = SkelControlSingleBone(SkeletonFirstPersonMesh.FindSkelControl('TensDisplay'));
		if(SkelControl != none)
		{
			SkelControl.BoneRotation.Pitch = -AmmoCountTens*6554;
		}

	    if (AmmoCount == 1)
    	{
		    //Hide the flak cannon ammo in the chamber
		    //Delay the hiding a fraction of time so the user doesn't see it happen	(time from anim)
	    	SetTimer(0.40f, false, 'HideFlakCannonAmmo');
    	}
    	else if (AmmoCount > 1)
    	{
	    	//Show the flak cannon ammo in the chamber
	    	ClearTimer('HideFlakCannonAmmo');
		    SkelControl = SkelControlSingleBone(SkeletonFirstPersonMesh.FindSkelControl('AmmoScale'));
	    	if(SkelControl != none)
	    	{
	    		SkelControl.SetSkelControlStrength(0.0f, 0.0f);
    		}
	    }
    }
}

simulated function CustomFire()
{
	local int i,j;
   	local vector RealStartLoc, AimDir, YDir, ZDir;
	local Projectile Proj;
	local class<Projectile> ShardProjectileClass;
	local float Mag;

	IncrementFlashCount();

	if (Role == ROLE_Authority)
	{
		// this is the location where the projectile is spawned
		RealStartLoc = GetPhysicalFireStartLoc();
		// get fire aim direction
		GetAxes(GetAdjustedAim(RealStartLoc),AimDir, YDir, ZDir);

		// special center shard
		Proj = Spawn(CenterShardClass,,, RealStartLoc);
		if (Proj != None)
		{
			Proj.Init(AimDir);
		}

		// one shard in each of 9 zones (except center)
		ShardProjectileClass = GetProjectileClass();
		for ( i=-1; i<2; i++)
		{
			for ( j=-1; j<2; j++ )
			{
				if ( (i != 0) || (j != 0) )
				{
					Mag = (abs(i)+abs(j) > 1) ? 0.7 : 1.0;
					Proj = Spawn(ShardProjectileClass,,, RealStartLoc);
					if (Proj != None)
					{
						Proj.Init(AimDir + (0.3 + 0.7*FRand())*Mag*i*SpreadDist*YDir + (0.3 + 0.7*FRand())*Mag*j*SpreadDist*ZDir );
					}
				}
			}
	    }
	}
}

simulated event SetPosition(UTPawn Holder)
{
	local vector OldSmallWeaponsOffset;

	if (LeftMesh != None)
	{
		switch (GetHand())
		{
			case HAND_Left:
				LeftMesh.SetScale3D(default.Mesh.Scale3D);
				break;
			case HAND_Right:
				LeftMesh.SetScale3D(default.Mesh.Scale3D * vect(1,-1,1));
				break;
			default:
				break;
		}
	}

	OldSmallWeaponsOffset = SmallWeaponsOffset;
	if (DualMode != EDM_SingleWeapon)
	{
		SmallWeaponsOffset.Y = 0.0;
	}

	Super.SetPosition(Holder);

	SmallWeaponsOffset = OldSmallWeaponsOffset;
}

/**
 * Causes the muzzle flashlight to turn on and setup a time to
 * turn it back off again.
 */
simulated event CauseMuzzleFlash()
{
	local int Index;
	local UTPawn P;

	if ( WorldInfo.NetMode != NM_Client )
	{
		P = UTPawn(Instigator);
		if ( (P == None) || !P.bUpdateEyeHeight )
		{
			return;
		}
	}
	if ( !bMuzzleFlashAttached )
	{
		AttachMuzzleFlash();
	}
	CauseMuzzleFlashLight();
	
	ProcessRecoil();

	if (GetHand() != HAND_Hidden)
	{
		Index = UseLeftBarrel() ? 1 : 0;

		if (EliteMachine_MuzzleFlashPSC[Index] != none)
		{
			EliteMachine_MuzzleFlashPSC[Index].SetTemplate(MuzzleFlashPSCTemplate);
			EliteMachine_MuzzleFlashPSC[Index].SetVectorParameter('MFlashScale',Vect(0.5,0.5,0.5));
			EliteMachine_MuzzleFlashPSC[Index].ActivateSystem();
		}
		
		if (EliteMachine_MuzzleFlashPSC[Index] != none && Instigator.FiringMode == 1)
		{
			EliteMachine_MuzzleFlashPSC[Index].SetTemplate(MuzzleFlashAltPSCTemplate);
			EliteMachine_MuzzleFlashPSC[Index].SetVectorParameter('MFlashScale',Vect(0.5,0.5,0.5));
			EliteMachine_MuzzleFlashPSC[Index].ActivateSystem();
		}

		// Set when to turn it off.
		if (Index > 0 )
		{
			SetTimer(MuzzleFlashDuration,false,'MuzzleFlashTimerLeft');
		}
		else
		{
			SetTimer(MuzzleFlashDuration,false,'MuzzleFlashTimerRight');
		}
	}
}

// Start Fixing Secondary Continous Shot
simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation )
{
	local float Rate;
	// Play Weapon fire animation
	Rate = GetFireInterval(FireModeNum);

	// If we are in dual mode, we want to back it out to the normal speed
	if (DualMode == EDM_Dual)
	{
		Rate *= 2;
	}

		PlayWeaponAnimation( GetFireAnim(FireModeNum), Rate,, UseLeftBarrel() ? LeftMesh : None );
		PlayArmAnimation( GetFireAnim(FireModeNum), Rate, UseLeftBarrel(),, UseLeftBarrel() ? LeftMesh : None );
		bLastFiredLeft=UseLeftBarrel();

	// Start muzzle flash effect
	CauseMuzzleFlash();
	ShakeView();
	TrackShotCount();
}

//-----------------------------------------------------------------
// AI Interface

/* BestMode()
choose between regular or alt-fire
*/
function byte BestMode()
{
	local vector EnemyDir;
	local float EnemyDist;
	local UTBot B;

	B = UTBot(Instigator.Controller);
	if ( (B == None) || (B.Enemy == None) )
		return 0;

	EnemyDir = B.Enemy.Location - Instigator.Location;
	EnemyDist = VSize(EnemyDir);
	if ( EnemyDist > 750 )
	{
		if ( EnemyDir.Z < -0.5 * EnemyDist )
			return 1;
		return 0;
	}
	else if ( (B.Enemy.Weapon != None) && B.Enemy.Weapon.bMeleeWeapon )
		return 0;
	else if ( (EnemyDist < 400) || (EnemyDir.Z > 30) )
		return 0;
	else if ( FRand() < 0.65 )
		return 1;
	return 0;
}

function float GetAIRating()
{
	local UTBot B;
	local float EnemyDist;
	local vector EnemyDir;

	B = UTBot(Instigator.Controller);
	if ( B == None )
		return AIRating;

	if ( UTOnslaughtPowerNode(B.Focus) != None )
	{
		EnemyDist = VSize(B.FocalPoint - Instigator.Location);
		if (EnemyDist < 1250.0)
		{
			return 0.9;
		}
		return AIRating * 1500.0/EnemyDist;
	}

	if ( B.Enemy == None )
		return AIRating;

	EnemyDir = B.Enemy.Location - Instigator.Location;
	EnemyDist = VSize(EnemyDir);
	if ( EnemyDist > 750 )
	{
		if ( EnemyDist > 1700 )
		{
			if ( EnemyDist > 2500 )
				return 0.2;
			return (AIRating - 0.3);
		}
		if ( EnemyDir.Z < -0.5 * EnemyDist )
			return (AIRating - 0.3);
	}
	else if ( (B.Enemy.Weapon != None) && B.Enemy.Weapon.bMeleeWeapon )
		return (AIRating + 0.35);
	else if ( EnemyDist < 400 )
		return (AIRating + 0.2);
	return FMax(AIRating + 0.2 - (EnemyDist - 400) * 0.0008, 0.2);
}

function float SuggestAttackStyle()
{
	if ( (AIController(Instigator.Controller) != None)
		&& (AIController(Instigator.Controller).Skill < 3) )
		return 0.4;
    return 0.8;
}
/*
simulated state WeaponEquipping
{

//	 * We want to being this state by setting up the timing and then notifying the pawn
//	 * that the weapon has changed.


	simulated function BeginState(Name PreviousStateName)
	{
		super.BeginState(PreviousStateName);

		if (Instigator.IsLocallyControlled() && Instigator.IsHumanControlled())
		{
			UpdateAmmoCount();
		}
	}
}
*/
simulated state WeaponEquipping
{
	simulated function WeaponEquipped()
	{
		if( bWeaponPutDown )
		{
			// if switched to another weapon, put down right away
			PutDownWeapon();
			return;
		}
		else
		{
			GotoState('Active');
		}
	}
	simulated function EndState(Name NextStateName)
	{
		Super.EndState(NextStateName);
		bLoaded = true;
	}
}

function float SuggestDefenseStyle()
{
	return -0.4;
}

function float GetOptimalRangeFor(Actor Target)
{
	// short range so bots try to maximize shards that hit
	return 750.0;
}

simulated state Active
{
	simulated function int LagRot(int NewValue, int LastValue, float MaxDiff, int Index)
	{
		local int RotDiff;
		local float LeadMag, DeltaTime;

		if ( NewValue ClockWiseFrom LastValue )
		{
			if ( LastValue > NewValue )
			{
				LastValue -= 65536;
			}
		}
		else
		{
			if ( NewValue > LastValue )
			{
				NewValue -= 65536;
			}
		}

		DeltaTime = WorldInfo.TimeSeconds - LastRotUpdate;
		RotDiff = NewValue - LastValue;
		if ( DualMode != EDM_DualEquipping )
		{
			if ( (RotDiff == 0) || (OldRotDiff[Index] == 0) )
			{
				LeadMag = ShouldLagRot() ? OldLeadMag[Index] : 0.0;
				if ( (RotDiff == 0) && (OldRotDiff[Index] == 0) )
				{
					OldMaxDiff[Index] = 0;
				}
			}
			else if ( (RotDiff > 0) == (OldRotDiff[Index] > 0) )
			{
				if (ShouldLagRot())
				{
					MaxDiff = FMin(1, Abs(RotDiff)/(12000*DeltaTime)) * MaxDiff;
					if ( OldMaxDiff[Index] != 0 )
						MaxDiff = FMax(OldMaxDiff[Index], MaxDiff);

					OldMaxDiff[Index] = MaxDiff;
					LeadMag = (NewValue > LastValue) ? -1* MaxDiff : MaxDiff;
				}
				else
				{
					LeadMag = 0;
				}
				if ( DeltaTime < 1/RotChgSpeed )
				{
					LeadMag = (1.0 - RotChgSpeed*DeltaTime)*OldLeadMag[Index] + RotChgSpeed*DeltaTime*LeadMag;
				}
				else
				{
					LeadMag = 0;
				}
			}
			else
			{
				LeadMag = 0;
				OldMaxDiff[Index] = 0;
				if ( DeltaTime < 1/ReturnChgSpeed )
				{
					LeadMag = (1 - ReturnChgSpeed*DeltaTime)*OldLeadMag[Index] + ReturnChgSpeed*DeltaTime*LeadMag;
				}
			}
		}
		else
		{
			LeadMag = 0;
			OldMaxDiff[Index] = 0;
			if ( DeltaTime < 1/ReturnChgSpeed )
			{
				LeadMag = (1 - ReturnChgSpeed*DeltaTime)*OldLeadMag[Index] + ReturnChgSpeed*DeltaTime*LeadMag;
			}
		}
		OldLeadMag[Index] = LeadMag;
		OldRotDiff[Index] = RotDiff;

		return NewValue + LeadMag;
	}

	simulated function bool ShouldLagRot()
	{
		return (DualMode != EDM_Dual);
	}

	simulated event OnAnimEnd(optional AnimNodeSequence SeqNode, optional float PlayedTime, optional float ExcessTime)
	{
		Super.OnAnimEnd(SeqNode, PlayedTime, ExcessTime);

		if (WorldInfo.NetMode != NM_DedicatedServer && DualMode == EDM_Dual)
		{
			PlayWeaponAnimation(LeftIdleAnim, 0.0, true, LeftMesh);
		}
	}

	simulated event BeginState(name PreviousStateName)
	{
		bForceReturnToActive = false;
		Super.BeginState(PreviousStateName);
	}
}

defaultproperties
{
	WeaponColor=(R=255,G=255,B=128,A=255)
	FireInterval(0)=+1.1
	FireInterval(1)=+1.1
	MaxPitchLag=500
	MaxYawLag=500
	PlayerViewOffset=(X=-6.0,Y=-4.0,Z=0.5)
	SmallWeaponsOffset=(X=12.0,Y=6.0,Z=-6.0)

	Begin Object Name=FirstPersonMesh
		SkeletalMesh=SkeletalMesh'WP_FlakCannon.Mesh.SK_WP_FlakCannon_1P'
		PhysicsAsset=None
		FOV=70
		Materials(0)=Material'EM_XX_Content.Flak_Final'
		AnimTreeTemplate=AnimTree'WP_FlakCannon.Anims.AT_FlakCannon'
		AnimSets(0)=AnimSet'WP_FlakCannon.Anims.K_WP_FlakCannon_1P_Base'
	End Object
	AttachmentClass=class'EM_XX.Elite_Flak_Attach'
	SkeletonFirstPersonMesh = FirstPersonMesh;

	ArmsAnimSet=AnimSet'WP_FlakCannon.Anims.K_WP_FlakCannon_1P_Arms'

	Begin Object Name=PickupMesh
		SkeletalMesh=SkeletalMesh'EM_XX_Content.Mesh.FlakCannon_3P'
		Materials(0)=Material'EM_XX_Content.Flak_Final'
	End Object

	MuzzleFlashSocket=MuzzleFlashSocket
	MuzzleFlashPSCTemplate=WP_FlakCannon.Effects.P_WP_FlakCannon_Muzzle_Flash
	MuzzleFlashDuration=0.33
	MuzzleFlashLightClass=class'UTGame.UTRocketMuzzleFlashLight'

	WeaponFireSnd[0]=SoundCue'A_Weapon_FlakCannon.Weapons.A_FlakCannon_FireCue'
	WeaponFireSnd[1]=SoundCue'A_Weapon_FlakCannon.Weapons.A_FlakCannon_FireAltCue'

	WeaponFireAnim(0)=WeaponFire
	WeaponFireAnim(1)=WeaponFire

	FiringStatesArray(0)=WeaponFiring
	FiringStatesArray(1)=WeaponFiring

	WeaponFireTypes(0)=EWFT_Custom
	WeaponFireTypes(1)=EWFT_Projectile
	WeaponProjectiles(0)=class'EM_XX.Elite_Flak_Shard'
	WeaponProjectiles(1)=class'EM_XX.Elite_Flak_Shell'
	CenterShardClass=class'EM_XX.Elite_Flak_ShardMain'

	FireOffset=(X=8,Y=10,Z=-10)   //(X=0,Y=0,Z=0)  //

	MaxDesireability=0.75
	AIRating=+0.75
	CurrentRating=+0.75
	bInstantHit=false
	bSplashJump=false
	bRecommendSplashDamage=false
	bSniping=false
	ShouldFireOnRelease(0)=0
	ShouldFireOnRelease(1)=0
	
	DualMode=EDM_DualEquipping
	
	InventoryGroup=7
	GroupWeight=0.5

	PickupSound=SoundCue'A_Pickups.Weapons.Cue.A_Pickup_Weapons_Flak_Cue'
	WeaponPutDownSnd=SoundCue'A_Weapon_FlakCannon.Weapons.A_FlakCannon_LowerCue'
	WeaponEquipSnd=SoundCue'A_Weapon_FlakCannon.Weapons.A_FlakCannon_RaiseCue'

   AmmoCount=30
   LockerAmmoCount=30
   MaxAmmoCount=420

	PutDownTime=0.2
	SpreadDist=0.1

	IconX=394
	IconY=38
	IconWidth=60
	IconHeight=38

   bZoomedFireMode(0)=0
   bZoomedFireMode(1)=0

	EquipTime=0.2
	CrossHairCoordinates=(U=64,V=64,UL=64,VL=64)

	LockerRotation=(Pitch=0,Roll=-16384)
	IconCoordinates=(U=131,V=429,UL=132,VL=52)
   DualIconCoordinates=(U=131,V=429,UL=132,VL=52)

	OdometerMaxPerSecOnes=21845.0;
	OdometerMaxPerSecTens=10950.0;
	curTensOdometer=0;
	curOnesOdometer=0;
	OnesPlaceSkelName=OnesDisplay
	TensPlaceSkelName=TensDisplay

	QuickPickGroup=4
	QuickPickWeight=0.9
   bExportMenuData=True
   Priority=900.4
   ItemName="Elite Flak Cannon"
   PickupMessage="Elite Flak Cannon"
   Name="Elite Flak Cannon"
}
