//==================================================
//==================================================
// Whisky's Colt Python
// Balance job by PooSH
//==================================================
class Colt extends KFWeapon;

var float ReloadMulti;
var float BulletUnloadRate; // how long does it takes to unload empty bullets
var float BulletLoadRate;   // how long does it takes to load a single bullet
var transient bool bInterruptedReload;
var transient float NextBulletLoadTime;


// Implemented revolver-specific reloading
// Reload State 1 (uninterruptable): opening drum and unloading empty bullets. Lasts BulletUnloadRate seconds.
// Reload State 2: loading bullets. Each bullet load takes BulletLoadRate seconds.
// Reload State 3: closing drum.
simulated function WeaponTick(float dt)
{
    local float LastSeenSeconds;

    if( bHasAimingMode ) {
        if( bForceLeaveIronsights ) {
            if( bAimingRifle ) {
                ZoomOut(true);

                if( Role < ROLE_Authority)
                    ServerZoomOut(false);
            }
            bForceLeaveIronsights = false;
        }

        if( ForceZoomOutTime > 0 ) {
            if( bAimingRifle ) {
                if( Level.TimeSeconds - ForceZoomOutTime > 0 ) {
                    ForceZoomOutTime = 0;
                    ZoomOut(true);
                    if( Role < ROLE_Authority)
                        ServerZoomOut(false);
                }
            }
            else {
                ForceZoomOutTime = 0;
            }
        }
    }

    if ( Role < ROLE_Authority )
        return;

    if ( bIsReloading ) {
        if (AmmoAmount(0) <= MagAmmoRemaining )
            InterruptReload(); //force interrupt if loaded last bullet
        if( Level.TimeSeconds >= ReloadTimer ) {
            ActuallyFinishReloading();
        }
        else if ( !bInterruptedReload && Level.TimeSeconds >= NextBulletLoadTime ) {
            if ( MagAmmoRemaining >= MagCapacity || MagAmmoRemaining >= AmmoAmount(0) )
                NextBulletLoadTime += 1000; // don't load bullets anymore
            else {
                MagAmmoRemaining++;
                NextBulletLoadTime += BulletLoadRate;
            }
        }
    }
    else if( !Instigator.IsHumanControlled() ) { // bot
        LastSeenSeconds = Level.TimeSeconds - Instigator.Controller.LastSeenTime;
        if(MagAmmoRemaining == 0 || ((LastSeenSeconds >= 5 || LastSeenSeconds > MagAmmoRemaining) && MagAmmoRemaining < MagCapacity))
            ReloadMeNow();
    }

    // Turn it off on death  / battery expenditure
    if (FlashLight != none)
    {
        // Keep the 1Pweapon client beam up to date.
        AdjustLightGraphic();
        if (FlashLight.bHasLight)
        {
            if (Instigator.Health <= 0 || KFHumanPawn(Instigator).TorchBatteryLife <= 0 || Instigator.PendingWeapon != none )
            {
                //Log("Killing Light...you're out of batteries, or switched / dropped weapons");
                KFHumanPawn(Instigator).bTorchOn = false;
                ServerSpawnLight();
            }
        }
    }
}


simulated function bool AllowReload()
{
    if ( bIsReloading || MagAmmoRemaining >= AmmoAmount(0) )
        return false;

    UpdateMagCapacity(Instigator.PlayerReplicationInfo);
    if ( MagAmmoRemaining >= MagCapacity )
        return false;

    if( KFInvasionBot(Instigator.Controller) != none || KFFriendlyAI(Instigator.Controller) != none )
        return true;

    return !FireMode[0].IsFiring() && !FireMode[1].IsFiring()
        && Level.TimeSeconds > (FireMode[0].NextFireTime - 0.1);
}


// Since vanilla reloading replication is totally fucked, I moved base code into separate,
// replication-free function, which is executed on both server and client side
// -- PooSH
simulated function DoReload()
{
    if ( bHasAimingMode && bAimingRifle ) {
        FireMode[1].bIsFiring = False;
        ZoomOut(false);
        // ZoomOut() just a moment ago was executed on server side - why to force it again?  -- PooSH
        // if( Role < ROLE_Authority)
            // ServerZoomOut(false);
    }

    if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
        ReloadMulti = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.Static.GetReloadSpeedModifier(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), self);
    else
        ReloadMulti = 1.0;

    bIsReloading = true;
    bInterruptedReload = false;

    BulletUnloadRate = default.BulletUnloadRate / ReloadMulti;
    BulletLoadRate = default.BulletLoadRate / ReloadMulti;
    NextBulletLoadTime = Level.TimeSeconds + BulletUnloadRate + BulletLoadRate;
    // more bullets left = less time to reload
    ReloadRate = default.ReloadRate / ReloadMulti;
    ReloadRate -= MagAmmoRemaining * BulletLoadRate;
    ReloadTimer = Level.TimeSeconds + ReloadRate;

    Instigator.SetAnimAction(WeaponReloadAnim);
}

// This function is triggered by client, replicated to server and NOT EXECUTED on client,
// even if marked as simulated
exec function ReloadMeNow()
{
    local KFPlayerController PC;

    if ( !AllowReload() )
        return;

    DoReload();
    ClientReload();

    PC = KFPlayerController(Instigator.Controller);
    if ( PC != none && Level.Game.NumPlayers > 1 && KFGameType(Level.Game).bWaveInProgress
            && Level.TimeSeconds - PC.LastReloadMessageTime > PC.ReloadMessageDelay )
    {
        KFPlayerController(Instigator.Controller).Speech('AUTO', 2, "");
        KFPlayerController(Instigator.Controller).LastReloadMessageTime = Level.TimeSeconds;
    }
}

function ServerRequestAutoReload()
{
    ReloadMeNow();
}

// This function now is triggered by ReloadMeNow() and executed on local side only
simulated function ClientReload()
{
    DoReload();
    PlayAnim(ReloadAnim, ReloadAnimRate*ReloadMulti, 0.1);
}


function AddReloadedAmmo() {} // magazine is filled in WeaponTick now


simulated function bool StartFire(int Mode)
{
    if ( MagAmmoRemaining <= 0 )
        return false;
    if ( bIsReloading ) {
        InterruptReload();
        return false;
    }
    return super(Weapon).StartFire(Mode);
}

simulated function AltFire(float F)
{
    InterruptReload();
}

// another fucked up replication...
// By the looks if it, InterruptReload() is called only on client, which triggers ServerInterruptReload().
// Anyway, why server would want to interrupt reload by its own?..
simulated function bool InterruptReload()
{
    if( bIsReloading && !bInterruptedReload && (ReloadTimer - Level.TimeSeconds)*ReloadMulti > 0.7/ReloadMulti )
    {
        // that's very lame how to do stuff like that - don't repeat it at home ;)
        // in theory client should send server a request to interrupt the reload,
        // and the server send back accept to client.
        // But in such case we have a double chance of screwing the shit up, so let's just
        // do it lazy way.
        ServerInterruptReload();
        ClientInterruptReload();
        return true;
    }

    return false;
}

function ServerInterruptReload()
{
    if ( Role == ROLE_Authority ) {
        ReloadTimer = Level.TimeSeconds + 0.5/ReloadMulti;
        bInterruptedReload = true;
        SetAnimFrame(112, 0 , 1);  // go to closing drum stage
    }
}

simulated function ClientInterruptReload()
{
    if ( Role < ROLE_Authority ) {
        bInterruptedReload = true;
        SetAnimFrame(112, 0 , 1);  // go to closing drum stage
        ShowAllBullets();
    }
}

simulated exec function ToggleIronSights()
{
    if( bHasAimingMode ) {
        if( bAimingRifle )
            PerformZoom(false);
        else
            IronSightZoomIn();
    }
}

simulated exec function IronSightZoomIn()
{
    if( bHasAimingMode ) {
        if( Owner != none && Owner.Physics == PHYS_Falling
                && Owner.PhysicsVolume.Gravity.Z <= class'PhysicsVolume'.default.Gravity.Z )
            return;

        if( bIsReloading )
            InterruptReload(); // finish reloading while zooming in  -- PooSH
        PerformZoom(True);
    }
}

simulated function bool PutDown()
{
    local int Mode;

    // continue here, because there is nothing to stop us from interrupting the reload  -- PooSH
    if ( bIsReloading )
        InterruptReload();

    if( bAimingRifle )
        ZoomOut(False);

    // From Weapon.uc
    if (ClientState == WS_BringUp || ClientState == WS_ReadyToFire)
    {
        if ( (Instigator.PendingWeapon != None) && !Instigator.PendingWeapon.bForceSwitch )
        {
            for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
            {
                // if _RO_
                if( FireMode[Mode] == none )
                    continue;
                // End _RO_

                if ( FireMode[Mode].bFireOnRelease && FireMode[Mode].bIsFiring )
                    return false;
                if ( FireMode[Mode].NextFireTime > Level.TimeSeconds + FireMode[Mode].FireRate*(1.f - MinReloadPct))
                    DownDelay = FMax(DownDelay, FireMode[Mode].NextFireTime - Level.TimeSeconds - FireMode[Mode].FireRate*(1.f - MinReloadPct));
            }
        }

        if (Instigator.IsLocallyControlled())
        {
            for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
            {
                // if _RO_
                if( FireMode[Mode] == none )
                    continue;
                // End _RO_

                if ( FireMode[Mode].bIsFiring )
                    ClientStopFire(Mode);
            }

            if (  DownDelay <= 0  || KFPawn(Instigator).bIsQuickHealing > 0)
            {
                if ( ClientState == WS_BringUp || KFPawn(Instigator).bIsQuickHealing > 0 )
                    TweenAnim(SelectAnim,PutDownTime);
                else if ( HasAnim(PutDownAnim) )
                {
                    if( ClientGrenadeState == GN_TempDown || KFPawn(Instigator).bIsQuickHealing > 0)
                    {
                       PlayAnim(PutDownAnim, PutDownAnimRate * (PutDownTime/QuickPutDownTime), 0.0);
                    }
                    else
                    {
                       PlayAnim(PutDownAnim, PutDownAnimRate, 0.0);
                    }

                }
            }
        }
        ClientState = WS_PutDown;
        if ( Level.GRI.bFastWeaponSwitching )
            DownDelay = 0;
        if ( DownDelay > 0 )
        {
            SetTimer(DownDelay, false);
        }
        else
        {
            if( ClientGrenadeState == GN_TempDown )
            {
               SetTimer(QuickPutDownTime, false);
            }
            else
            {
               SetTimer(PutDownTime, false);
            }
        }
    }
    for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
    {
        // if _RO_
        if( FireMode[Mode] == none )
            continue;
        // End _RO_

        FireMode[Mode].bServerDelayStartFire = false;
        FireMode[Mode].bServerDelayStopFire = false;
    }
    Instigator.AmbientSound = None;
    OldWeapon = None;
    return true; // return false if preventing weapon switch
}


simulated function Notify_DropBullets()
{
    if ( !Instigator.IsLocallyControlled() )
        return ;
    // bullets are reloaded in this order: 2,1,5,4,6,3
    switch ( MagAmmoRemaining ) {
        case 5:
           SetBoneScale(1, 0.0, 'b1');
           SetBoneScale(2, 0.0, 'b2');
           SetBoneScale(3, 1.0, 'b3');
           SetBoneScale(4, 0.0, 'b4');
           SetBoneScale(5, 0.0, 'b5');
           SetBoneScale(6, 0.0, 'b6');
            break;
        case 4:
           SetBoneScale(1, 0.0, 'b1');
           SetBoneScale(2, 0.0, 'b2');
           SetBoneScale(3, 1.0, 'b3');
           SetBoneScale(4, 0.0, 'b4');
           SetBoneScale(5, 0.0, 'b5');
           SetBoneScale(6, 1.0, 'b6');
            break;
        case 3:
           SetBoneScale(1, 0.0, 'b1');
           SetBoneScale(2, 0.0, 'b2');
           SetBoneScale(3, 1.0, 'b3');
           SetBoneScale(4, 1.0, 'b4');
           SetBoneScale(5, 0.0, 'b5');
           SetBoneScale(6, 1.0, 'b6');
            break;
        case 2:
           SetBoneScale(1, 0.0, 'b1');
           SetBoneScale(2, 0.0, 'b2');
           SetBoneScale(3, 1.0, 'b3');
           SetBoneScale(4, 1.0, 'b4');
           SetBoneScale(5, 1.0, 'b5');
           SetBoneScale(6, 1.0, 'b6');
            break;
        case 1:
           SetBoneScale(1, 1.0, 'b1');
           SetBoneScale(2, 0.0, 'b2');
           SetBoneScale(3, 1.0, 'b3');
           SetBoneScale(4, 1.0, 'b4');
           SetBoneScale(5, 1.0, 'b5');
           SetBoneScale(6, 1.0, 'b6');
            break;
        default:
           SetBoneScale(1, 1.0, 'b1');
           SetBoneScale(2, 1.0, 'b2');
           SetBoneScale(3, 1.0, 'b3');
           SetBoneScale(4, 1.0, 'b4');
           SetBoneScale(5, 1.0, 'b5');
           SetBoneScale(6, 1.0, 'b6');
    }
}

simulated function Notify_InsertBullets()
{
    ShowAllBullets();

    switch ( MagAmmoRemaining ) {
        case 5: SetAnimFrame(82, 0 , 1); break;
        case 4: SetAnimFrame(71.5, 0 , 1); break;
        case 3: SetAnimFrame(61, 0 , 1); break;
        case 2: SetAnimFrame(50.5, 0 , 1); break;
        case 1: SetAnimFrame(38.5, 0 , 1); break;
    }
}

simulated function ShowAllBullets()
{
    if ( !Instigator.IsLocallyControlled() )
        return ;

   // restore bones
   // bullets are reloaded in this order: 2,1,5,4,6,3
   SetBoneScale(1, 1.0, 'b1');
   SetBoneScale(2, 1.0, 'b2');
   SetBoneScale(3, 1.0, 'b3');
   SetBoneScale(4, 1.0, 'b4');
   SetBoneScale(5, 1.0, 'b5');
   SetBoneScale(6, 1.0, 'b6');
}

defaultproperties
{
     BulletUnloadRate=1.000000
     BulletLoadRate=0.475000
     FirstPersonFlashlightOffset=(X=-20.000000,Y=-22.000000,Z=8.000000)
     MagCapacity=6
     ReloadRate=5.500000
     ReloadAnim="Reload"
     ReloadAnimRate=1.200000
     bHoldToReload=True
     WeaponReloadAnim="Reload_Revolver"
     Weight=5.000000
     bHasAimingMode=True
     IdleAimAnim="IronIdle"
     StandardDisplayFOV=70.000000
     bModeZeroCanDryFire=True
     SleeveNum=0
     TraderInfoTexture=Texture'ScrnWeaponPack_T.Colt.Trader_WColt'
     MeshRef="ScrnWeaponPack_A.colt_weapon"
     SkinRefs(1)="ScrnWeaponPack_T.Colt.ColtV2_T"
     HudImageRef="ScrnWeaponPack_T.Colt.WColt_Unselected"
     SelectedHudImageRef="ScrnWeaponPack_T.Colt.WColt"
     ZoomedDisplayFOV=65.000000
     FireModeClass(0)=Class'ScrnWeaponPack.ColtFire'
     FireModeClass(1)=Class'KFMod.NoFire'
     PutDownAnim="PutDown"
     SelectSound=Sound'KF_9MMSnd.9mm_Select'
     AIRating=0.250000
     CurrentRating=0.250000
     bShowChargingBar=True
     Description="The Colt Python is a double action handgun chambered for the .357 Magnum cartridge, built on Colt's large I-frame. Pythons have a reputation for accuracy, smooth trigger pull, and a tight cylinder lock-up."
     DisplayFOV=70.000000
     Priority=110
     InventoryGroup=2
     GroupOffset=1
     PickupClass=Class'ScrnWeaponPack.ColtPickup'
     PlayerViewOffset=(X=20.000000,Y=25.000000,Z=-10.000000)
     BobDamping=6.000000
     AttachmentClass=Class'ScrnWeaponPack.ColtAttachment'
     IconCoords=(X1=434,Y1=253,X2=506,Y2=292)
     ItemName="Colt Python SE"
}
