//=============================================================================
// ZEDGunAltFire
//=============================================================================
// ZEDGun secondary fire class
//=============================================================================
// Killing Floor Source
// Copyright (C) 2012 Tripwire Interactive LLC
// - John "Ramm-Jaeger" Gibson
//=============================================================================

class ZEDGunAltFire extends KFFire;

// Zed Beam Firing Vars
var ZEDBeamEffect			Beam;
var class<ZEDBeamEffect>	BeamEffectClass;
var float	UpTime;
var float   ChargeUpTime;
var		bool bDoHit;
var		bool bStartFire;
var() Vector ProjSpawnOffset; // +x forward, +y right, +z up

// sound
var 	sound                  AmbientChargeUpSound;           // The charging up of the weapon
var 	float                  AmbientFireSoundRadius;         // The sound radius for the ambient fire sound
var		sound                  AmbientFireSound;               // How loud to play the looping ambient fire sound
var		byte                   AmbientFireVolume;              // The ambient fire sound

var		string                 AmbientChargeUpSoundRef;
var		string                 AmbientFireSoundRef;

var()   float                  MaxChargeTime;                   // The maximum amount of time for a full charged shot
var()   float                  MaxZedSphereChargeTime;          // The maximum amount of time to fully charge the zap sphere

var() class<Emitter> ChargeEmitterClass;
var() Emitter ChargeEmitter;

static function PreloadAssets(LevelInfo LevelInfo, optional KFFire Spawned)
{
	super.PreloadAssets(LevelInfo, Spawned);

	if ( default.AmbientChargeUpSoundRef != "" )
	{
		default.AmbientChargeUpSound = sound(DynamicLoadObject(default.AmbientChargeUpSoundRef, class'sound', true));
	}

	if ( default.AmbientFireSoundRef != "" )
	{
		default.AmbientFireSound = sound(DynamicLoadObject(default.AmbientFireSoundRef, class'sound', true));
	}

	if ( ZEDGunAltFire(Spawned) != none )
	{
		ZEDGunAltFire(Spawned).AmbientChargeUpSound = default.AmbientChargeUpSound;
		ZEDGunAltFire(Spawned).AmbientFireSound = default.AmbientFireSound;
	}
}

static function bool UnloadAssets()
{
	super.UnloadAssets();

	default.AmbientChargeUpSound = none;
	default.AmbientFireSound = none;

	return true;
}

function float MaxRange()
{
    return 2500;
}

function PlayPreFire()
{
	Weapon.PlayAnim('Charge', 1.0, 0.1);
}

simulated function ModeTick(float dt)
{
	local Vector StartTrace, EndTrace, X, Y, Z;
	local Vector HitLocation, HitNormal, EndEffect;
	local Actor Other;
	local Rotator Aim;
	local ZEDGun ZEDGun;
	local ZEDBeamEffect LB;
	local KFMonster KFM;
	local float ChargeScale;

    if ( !bIsFiring )
    {
        return;
    }

    ZEDGun = ZEDGun(Weapon);

    // Handle the beam firing
    if ( AllowFire() && ((UpTime > 0.0) || (Instigator.Role < ROLE_Authority)) )
    {
        UpTime -= dt;
        if( ChargeUpTime == 0 )
        {
            // Play the chargeup sound
            PlayAmbientSound(AmbientChargeUpSound);
            InitChargeEffect();
            SetTimer(0.15, true);
            PlayPreFire();
        }

        ChargeUpTime += dt;

		// the to-hit trace starts at the spawn offset to it looks like it is coming from the barrel
		ZEDGun.GetViewAxes(X, Y, Z);
		StartTrace = GetFireStart( X, Y, Z);

		StartTrace = StartTrace + X*ProjSpawnOffset.X;
	    StartTrace = StartTrace + Weapon.Hand * Y*ProjSpawnOffset.Y + Z*ProjSpawnOffset.Z;

        // Find the beam on the client
        if ( Instigator.Role < ROLE_Authority )
        {
			if ( Beam == None )
			{
				ForEach Weapon.DynamicActors(class'ZEDBeamEffect', LB )
					if ( !LB.bDeleteMe && (LB.Instigator != None) && (LB.Instigator == Instigator) )
					{
						Beam = LB;
						break;
					}
			}
		}

        // Consume the ammo on the server
        if ( Instigator.Role == ROLE_Authority )
		{
		    if ( bDoHit )
		    {
			    Weapon.ConsumeAmmo(0, AmmoPerFire);
			}
		}

        // client
        if ( bDoHit && Instigator.IsLocallyControlled() )
        {
            HandleRecoil(1.0);
        }

        // Set the end point of the beam
        Aim = AdjustAim(StartTrace, AimError);
        X = Vector(Aim);
        EndTrace = StartTrace + TraceRange * X;

        Other = Weapon.Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
        if ( Other != None && Other != Instigator )
			EndEffect = HitLocation;
		else
			EndEffect = EndTrace;

		if ( Beam != None )
			Beam.EndEffect = EndEffect;

		// Everything else here needs to happen on the server only
        if ( Instigator.Role < ROLE_Authority )
		{
			return;
		}

        if ( Other != None && Other != Instigator )
        {
            if( KFMonster(Other) != none && KFMonster(Other).Health > 0 )
            {
                KFMonster(Other).SetZapped(dt, Instigator);
            }
            else if( ExtendedZCollision(Other)!=None && Other.Base != none
                && KFMonster(Other.Base) != none && KFMonster(Other.Base).Health > 0 )
            {
                KFMonster(Other.Base).SetZapped(dt, Instigator);
            }

            // Make noise every time we "fire"
            if ( bDoHit )
            {
                Instigator.MakeNoise(1.0);
			}
		}

        if( ChargeUpTime < MaxZedSphereChargeTime )
        {
            ChargeScale = ChargeUpTime/MaxZedSphereChargeTime;
        }
        else
        {
            ChargeScale = 1.0;
        }

		if ( Beam != None )
			Beam.SphereCharge = ChargeScale;

        //Weapon.DrawDebugSphere( EndEffect, 250 * ChargeScale, 12, 0, 255, 0);

        if ( bDoHit && Other != None && Other != Instigator )
        {
    		foreach Weapon.VisibleCollidingActors( class 'KFMonster', KFM, 250 * ChargeScale, EndEffect )
    		{
                if( KFM != none && KFM != Other )
                {
                    if( KFM.Health > 0 )
                    {
                        KFM.SetZapped(FireRate * 0.75, Instigator);
                    }
                }
    		}
		}

		// beam effect is created and destroyed when firing starts and stops
		if ( (Beam == None) && bIsFiring )
		{
			Beam = Weapon.Spawn( BeamEffectClass, Instigator );
		}

		if ( Beam != None )
		{
			Beam.bHitSomething = (Other != None);
			Beam.EndEffect = EndEffect;
		}
    }
    else
        StopFiring();

    bStartFire = false;
    bDoHit = false;
}

// Overriden to share the same ammo as the primary fire
simulated function bool AllowFire()
{
	if(KFWeapon(Weapon).bIsReloading)
		return false;
	if(KFPawn(Instigator).SecondaryItem!=none)
		return false;
	if(KFPawn(Instigator).bThrowingNade)
		return false;

	if(KFWeapon(Weapon).MagAmmoRemaining < 1)
	{
    	if( Level.TimeSeconds - LastClickTime>FireRate )
    	{
    		LastClickTime = Level.TimeSeconds;
    	}

		if( AIController(Instigator.Controller)!=None )
			KFWeapon(Weapon).ReloadMeNow();
		return false;
	}

	return ( Weapon.AmmoAmount(0) >= AmmoPerFire);
}

// Do nothing
function ModeHoldFire(){}

// Handles toggling the weapon attachment's ambient sound on and off
function PlayAmbientSound(Sound aSound)
{
	local WeaponAttachment WA;

	WA = WeaponAttachment(Weapon.ThirdPersonActor);

    if ( Weapon == none || (WA == none))
        return;

	if(aSound == None)
	{
		WA.SoundVolume = WA.default.SoundVolume;
		WA.SoundRadius = WA.default.SoundRadius;
	}
	else
	{
		WA.SoundVolume = AmbientFireVolume;
		WA.SoundRadius = AmbientFireSoundRadius;
	}

    WA.AmbientSound = aSound;
}

// Handle the Zed gun charge sounds and effects
function Timer()
{
    local float ChargeScale;
	local ZEDGunAttachment WA;

	WA = ZEDGunAttachment(Weapon.ThirdPersonActor);

    if (ChargeUpTime > 0.0 && bIsFiring)
    {
        if( ChargeUpTime < MaxChargeTime )
        {
            PlayAmbientSound(AmbientChargeUpSound);
            ChargeScale = ChargeUpTime/MaxChargeTime;
            WA.ZedGunCharge = ChargeScale * 255;
            WA.UpdateZedGunCharge();
            if( ChargeEmitter != none )
            {
                ChargeEmitter.Emitters[0].SizeScale[1].RelativeSize = Lerp( ChargeScale, 4, 10 );
                ChargeEmitter.Emitters[1].StartVelocityRadialRange.Min = Lerp( ChargeScale, 50, 300 );
                ChargeEmitter.Emitters[1].StartVelocityRadialRange.Max = Lerp( ChargeScale, 50, 300 );
                ChargeEmitter.Emitters[1].SizeScale[0].RelativeSize = Lerp( ChargeScale, 2, 6 );
            }
        }
        else
        {
            PlayAmbientSound(AmbientFireSound);
            WA.ZedGunCharge = 255;
            WA.UpdateZedGunCharge();

            if( ChargeEmitter != none )
            {
                ChargeEmitter.Emitters[0].SizeScale[1].RelativeSize = 10;
                ChargeEmitter.Emitters[1].StartVelocityRadialRange.Min = 300;
                ChargeEmitter.Emitters[1].StartVelocityRadialRange.Max = 300;
                ChargeEmitter.Emitters[1].SizeScale[0].RelativeSize = 6;
            }
        }
    }
    else
    {
        PlayAmbientSound(none);
        DestroyChargeEffect();
        WA.ZedGunCharge = 0;
        WA.UpdateZedGunCharge();
        PlayFireEnd();
        Weapon.StopFire(ThisModeNum);

        SetTimer(0, false);
    }
}

// Handle setting new recoil
simulated function HandleRecoil(float Rec)
{
	local rotator NewRecoilRotation;
	local KFPlayerController KFPC;
	local KFPawn KFPwn;

    if( Instigator != none )
    {
		KFPC = KFPlayerController(Instigator.Controller);
		KFPwn = KFPawn(Instigator);
	}

    if( KFPC == none || KFPwn == none )
    	return;

	if( !KFPC.bFreeCamera )
	{
      	NewRecoilRotation.Pitch = RandRange( maxVerticalRecoilAngle * 0.5, maxVerticalRecoilAngle );
     	NewRecoilRotation.Yaw = RandRange( maxHorizontalRecoilAngle * 0.5, maxHorizontalRecoilAngle );

      	if( Rand( 2 ) == 1 )
         	NewRecoilRotation.Yaw *= -1;

	    NewRecoilRotation.Pitch += (Instigator.HealthMax / Instigator.Health * 5);
	    NewRecoilRotation.Yaw += (Instigator.HealthMax / Instigator.Health * 5);
	    NewRecoilRotation *= Rec;

	    KFPC.SetRecoil(NewRecoilRotation,RecoilRate * (default.FireRate/FireRate));
 	}
}

// Handle the beam having stopped firing
function StopFiring()
{
    if (Beam != None)
    {
        Beam.Destroy();
        Beam = None;
    }
    bStartFire = true;

    ChargeUpTime = 0;
}

event ModeDoFire()
{
    Load = 0; //don't use ammo here - it will be consumed in ModeTick() where it's sync'ed with damage dealing

	if (!AllowFire())
		return;

    if (!AllowFire())
        return;

    if (MaxHoldTime > 0.0)
        HoldTime = FMin(HoldTime, MaxHoldTime);

    // server
    if (Weapon.Role == ROLE_Authority)
    {
        DoFireEffect();
		HoldTime = 0;	// if bot decides to stop firing, HoldTime must be reset first
        if ( (Instigator == None) || (Instigator.Controller == None) )
			return;

        if ( AIController(Instigator.Controller) != None )
            AIController(Instigator.Controller).WeaponFireAgain(BotRefireRate, true);

        Instigator.DeactivateSpawnProtection();
    }

    // client
    if (Instigator.IsLocallyControlled())
    {
        ShakeView();
    }

    Weapon.IncrementFlashCount(ThisModeNum);

    // set the next firing time. must be careful here so client and server do not get out of sync
    if (bFireOnRelease)
    {
        if (bIsFiring)
            NextFireTime += MaxHoldTime + FireRate;
        else
            NextFireTime = Level.TimeSeconds + FireRate;
    }
    else
    {
        NextFireTime += FireRate;
        NextFireTime = FMax(NextFireTime, Level.TimeSeconds);
    }

    Load = AmmoPerFire;
    HoldTime = 0;

    if (Instigator.PendingWeapon != Weapon && Instigator.PendingWeapon != None)
    {
        bIsFiring = false;
        Weapon.PutDown();
    }
}

// Overridden to play different anims/sounds for the power down of this weapon
function PlayFireEnd()
{
    local float RandPitch;

	if ( Weapon.Mesh != None )
	{
		if ( FireCount > 0 )
		{
			if( KFWeap.bAimingRifle )
			{
                if ( Weapon.HasAnim(FireLoopAimedAnim) )
    			{
    				Weapon.PlayAnim(FireLoopAimedAnim, FireLoopAnimRate, 0.0);
    			}
    			else if( Weapon.HasAnim(FireAimedAnim) )
    			{
    				Weapon.PlayAnim(FireAimedAnim, FireAnimRate, TweenTime);
    			}
    			else
    			{
                    Weapon.PlayAnim(FireAnim, FireAnimRate, TweenTime);
    			}
			}
			else
			{
                if ( Weapon.HasAnim(FireLoopAnim) )
    			{
    				Weapon.PlayAnim(FireLoopAnim, FireLoopAnimRate, 0.0);
    			}
    			else
    			{
    				Weapon.PlayAnim(FireAnim, FireAnimRate, TweenTime);
    			}
			}
		}
		else
		{
            if( KFWeap.bAimingRifle )
			{
                if( Weapon.HasAnim(FireAimedAnim) )
    			{
                    Weapon.PlayAnim(FireAimedAnim, FireAnimRate, TweenTime);
    			}
    			else
    			{
                    Weapon.PlayAnim(FireAnim, FireAnimRate, TweenTime);
    			}
			}
			else
			{
                Weapon.PlayAnim(FireAnim, FireAnimRate, TweenTime);
			}
		}
	}


	if( Weapon.Instigator != none && Weapon.Instigator.IsLocallyControlled() &&
	   Weapon.Instigator.IsFirstPerson() && StereoFireSound != none )
	{
        if( bRandomPitchFireSound )
        {
            RandPitch = FRand() * RandomPitchAdjustAmt;

            if( FRand() < 0.5 )
            {
                RandPitch *= -1.0;
            }
        }

        Weapon.PlayOwnedSound(StereoFireSound,SLOT_Interact,TransientSoundVolume * 0.85,,TransientSoundRadius,(1.0 + RandPitch),false);
    }
    else
    {
        if( bRandomPitchFireSound )
        {
            RandPitch = FRand() * RandomPitchAdjustAmt;

            if( FRand() < 0.5 )
            {
                RandPitch *= -1.0;
            }
        }

        Weapon.PlayOwnedSound(FireSound,SLOT_Interact,TransientSoundVolume,,TransientSoundRadius,(1.0 + RandPitch),false);
    }
    ClientPlayForceFeedback(FireForce);  // jdf

    FireCount++;
}

// Don't do any fire effect, its all done in the mode tick and this fire mode doesn't do normal damage
function DoFireEffect()
{
    bDoHit = true;
    UpTime = FireRate+0.1;
}

// Don't do any fire effect, its all done in the mode tick and this fire mode doesn't do normal damage
simulated function DoClientOnlyEffectTrace(Vector Start, Rotator Dir){}

simulated function InitChargeEffect()
{
    // don't even spawn on server
    if ( (Level.NetMode == NM_DedicatedServer) || (AIController(Instigator.Controller) != None) )
		return;

    if ( (ChargeEmitterClass != None) && ((ChargeEmitter == None) || ChargeEmitter.bDeleteMe) )
    {
        ChargeEmitter = Weapon.Spawn(ChargeEmitterClass);
        if ( ChargeEmitter != None )
    		Weapon.AttachToBone(ChargeEmitter, 'tip');
    }
}

simulated function DestroyChargeEffect()
{
    if (ChargeEmitter != None)
        ChargeEmitter.Destroy();
}

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

    DestroyChargeEffect();

    if ( Level.NetMode != NM_Client )
    {
        if ( Beam != None )
            Beam.Destroy();
    }
}

defaultproperties
{
    bFireOnRelease=false
    FireAimedAnim=ChargeDown
    FireSoundRef="KF_ZEDGunSnd.KF_WEP_ZED_Secondary_SpinDown_M"
    StereoFireSoundRef="KF_ZEDGunSnd.KF_WEP_ZED_Secondary_SpinDown_S"
    NoAmmoSoundRef="KF_ZEDGunSnd.KF_WEP_ZED_Dryfire"
    DamageType=none
    FireAnim=ChargeDown
    TransientSoundVolume=2.0
    TransientSoundRadius=500.000000
    FireForce="AssaultRifleFire"
    FireRate=0.12
    RecoilRate=0.07
    maxVerticalRecoilAngle=100
    maxHorizontalRecoilAngle=150
    TweenTime=0.025
    bRecoilRightOnly=false
    bWaitForRelease=false
    TraceRange=2500

    AmmoClass=Class'KFMod.ZEDGunAmmo'
    AmmoPerFire=1
    BotRefireRate=0.15
    FlashEmitterClass=Class'ROEffects.ZEDGunChargeDown'
    aimerror=42.000000
    Spread=0.015
    SpreadStyle=SS_Random
    ProjSpawnOffset=(X=25,Y=18,Z=-14.5)
    BeamEffectClass=class'ZEDBeamEffect'

    ChargeEmitterClass=Class'ROEffects.ChargeUp1stZEDGun'
    AmbientFireSoundRef="KF_ZEDGunSnd.ZedGunChargeLoop"
    AmbientChargeUpSoundRef="KF_ZEDGunSnd.ZedGunChargeUp"
    AmbientFireSoundRadius=500
    AmbientFireVolume=255
    MaxChargeTime=1.0
    MaxZedSphereChargeTime=3.0

    //** View shake **//
    ShakeOffsetMag=(X=3.0,Y=1.0,Z=3.0)
    ShakeOffsetRate=(X=1000.0,Y=1000.0,Z=1000.0)
    ShakeOffsetTime=3.0
    ShakeRotMag=(X=25.0,Y=25.0,Z=100.0)
    ShakeRotRate=(X=12500.0,Y=12500.0,Z=12500.0)
    ShakeRotTime=5.0
    bRandomPitchFireSound=false
}
