class NiceDualies extends NiceWeapon;
var bool startedWithFullPistol;
var class<NiceSingle> SingleClass;
var name altFlashBoneName;
var name altTPAnim;
var Actor altThirdPersonActor;
var name altWeaponAttach;
// Track ammo in each gun separately
var int MagAmmoRemLeft, MagAmmoRemLeftClient;
var int MagAmmoRemRight, MagAmmoRemRightClient;
// Variables for managing dual-pistols reload
var const string    leftEjectStr,   rightEjectStr;      // Event names that trigger when magazines ejected
var const string    leftInsertStr,  rightInsertStr;     // Event names that trigger when magazines inserted
var float           leftEject,      rightEject;         // Frame at which magazines ejected
var float           leftInsert,     rightInsert;        // Frame at which magazines inserted
// This weapon is currently switching and soon won't exist
var bool    bSwitching;
replication{
    reliable if(Role < ROLE_Authority)
       ServerUpdateWeaponMag, ServerSetDualMagSize, ServerReduceDualMag, ServerSwitchToSingle, ServerSwitchToGivenSingle;
    reliable if(Role == ROLE_Authority)
      MagAmmoRemLeft, MagAmmoRemRight, ClientSetDualMagSize;
    reliable if(bNetOwner && bNetDirty && (Role == ROLE_Authority))
       altThirdPersonActor;
}
simulated function PostBeginPlay(){
    super.PostBeginPlay();
    SetupDualReloadEvents();
    reloadPreEndFrame = FMin(leftEject, rightEject);
    reloadEndFrame = FMax(leftInsert, rightInsert);
    DemoReplacement = SingleClass;
}
simulated function SetupDualReloadEvents(){
    local EventRecord record;
    relEvents.Length = 0;
    record.eventName = leftEjectStr;
    record.eventFrame = leftEject;
    relEvents[relEvents.Length] = record;
    record.eventName = rightEjectStr;
    record.eventFrame = rightEject;
    relEvents[relEvents.Length] = record;
    record.eventName = leftInsertStr;
    record.eventFrame = leftInsert;
    relEvents[relEvents.Length] = record;
    record.eventName = rightInsertStr;
    record.eventFrame = rightInsert;
    relEvents[relEvents.Length] = record;
}
exec simulated function ClientReloadMeNow(){
    startedWithFullPistol = false;
    if (    MagAmmoRemLeftClient >= GetSingleMagCapacity()
        ||  MagAmmoRemRightClient >= GetSingleMagCapacity()) {
        startedWithFullPistol = true;
    }
    super.ClientReloadMeNow();
}
simulated function float GetCurrentReloadMult(){
    local float normalMultiplier;
    normalMultiplier = super.GetCurrentReloadMult();
    if (startedWithFullPistol) {
        return normalMultiplier * 2;
    }
    return normalMultiplier;
}
// Don't use that one for dualies
simulated function AddReloadedAmmo(){}
// Use this one
simulated function ReloadEvent(string eventName){
    local int halfMag;
    local int totalAvailableAmmo;
    UpdateMagCapacity(Instigator.PlayerReplicationInfo);
    halfMag = GetSingleMagCapacity();
    totalAvailableAmmo = AmmoAmount(0);
    totalAvailableAmmo -= (MagAmmoRemLeftClient + MagAmmoRemRightClient);
    // Handle ejection
    if(eventName ~= leftEjectStr){
       MagAmmoRemLeftClient = 0;
       ServerSetDualMagSize(MagAmmoRemLeftClient, MagAmmoRemRightClient, Level.TimeSeconds);
       NiceDualies(Instigator.Weapon).GetMagazineAmmo();
       return;
    }
    else if(eventName ~= rightEjectStr){
       MagAmmoRemRightClient = 0;
       ServerSetDualMagSize(MagAmmoRemLeftClient, MagAmmoRemRightClient, Level.TimeSeconds);
       NiceDualies(Instigator.Weapon).GetMagazineAmmo();
       return;
    }
    // Handle reload
    if(totalAvailableAmmo < 0)
       return;
    if(eventName ~= leftInsertStr){
       MagAmmoRemLeftClient += totalAvailableAmmo;
       MagAmmoRemLeftClient = Min(MagAmmoRemLeftClient, halfMag);
    }
    else if(eventName ~= rightInsertStr){
       MagAmmoRemRightClient += totalAvailableAmmo;
       MagAmmoRemRightClient = Min(MagAmmoRemRightClient, halfMag);
    }
    NiceDualies(Instigator.Weapon).GetMagazineAmmo();
    ServerSetDualMagSize(MagAmmoRemLeftClient, MagAmmoRemRightClient, Level.TimeSeconds);
}
simulated function BringUp(optional Weapon PrevWeapon){
    super.BringUp(PrevWeapon);
    ApplyLaserState();
}
simulated function ApplyLaserState(){
    super.ApplyLaserState();
    if(NiceAttachment(altThirdPersonActor) != none)
       NiceAttachment(altThirdPersonActor).SetLaserType(LaserType);
}
simulated function ZoomIn(bool bAnimateTransition){
    super.ZoomIn(bAnimateTransition);
    if(bAnimateTransition){
       if(bZoomOutInterrupted)
           PlayAnim('GOTO_Iron',1.0,0.1);
       else
           PlayAnim('GOTO_Iron',1.0,0.1);
    }
}
simulated function ZoomOut(bool bAnimateTransition){
    local float AnimLength, AnimSpeed;
    super.ZoomOut(false);
    if(bAnimateTransition){
       AnimLength = GetAnimDuration('GOTO_Hip', 1.0);
       if(ZoomTime > 0 && AnimLength > 0)
           AnimSpeed = AnimLength/ZoomTime;
       else
           AnimSpeed = 1.0;
       PlayAnim('GOTO_Hip',AnimSpeed,0.1);
    }
}
function AttachToPawn(Pawn P){
    local name BoneName;
    Super.AttachToPawn(P);
    if(altThirdPersonActor == none){
       altThirdPersonActor = Spawn(AttachmentClass, Owner);
       InventoryAttachment(altThirdPersonActor).InitFor(self);
    }
    else
       altThirdPersonActor.NetUpdateTime = Level.TimeSeconds - 1;
    BoneName = P.GetOffhandBoneFor(self);
    if(BoneName == ''){
       altThirdPersonActor.SetLocation(P.Location);
       altThirdPersonActor.SetBase(P);
    }
    else
       P.AttachToBone(altThirdPersonActor, BoneName);
    if(altThirdPersonActor != none)
       NiceDualiesAttachment(altThirdPersonActor).bIsOffHand = true;
    if(altThirdPersonActor != none && ThirdPersonActor != none){
       NiceDualiesAttachment(altThirdPersonActor).brother = NiceDualiesAttachment(ThirdPersonActor);
       NiceDualiesAttachment(ThirdPersonActor).brother = NiceDualiesAttachment(altThirdPersonActor);
       altThirdPersonActor.LinkMesh(NiceDualiesAttachment(ThirdPersonActor).BrotherMesh);
    }
}
simulated function DetachFromPawn(Pawn P){
    super.DetachFromPawn(P);
    if(altThirdPersonActor != none){
       altThirdPersonActor.Destroy();
       altThirdPersonActor = none;
    }
}
simulated function Destroyed(){
    super.Destroyed();
    if(ThirdPersonActor != none)
       ThirdPersonActor.Destroy();
    if(altThirdPersonActor != none)
       altThirdPersonActor.Destroy();
}
simulated function vector GetEffectStart(){
    local Vector RightFlashLoc,LeftFlashLoc;
    RightFlashLoc = GetBoneCoords(default.FlashBoneName).Origin;
    LeftFlashLoc = GetBoneCoords(default.altFlashBoneName).Origin;
    if(Instigator.IsFirstPerson()){
       if(WeaponCentered())
           return CenteredEffectStart();
       if(bAimingRifle){
           if(KFFire(GetFireMode(0)).FireAimedAnim != 'FireLeft_Iron')
               return RightFlashLoc;
           else
               return LeftFlashLoc;
       }
       else{
           if(GetFireMode(0).FireAnim != 'FireLeft')
               return RightFlashLoc;
           else
               return LeftFlashLoc;
       }
    }
    else{
       return (Instigator.Location + Instigator.EyeHeight * Vect(0, 0, 0.5) + vector(Instigator.Rotation) * 40.0);
    }
}
function NicePlainData.Data GetNiceData(){
    local NicePlainData.Data transferData;
    transferData = super.GetNiceData();
    class'NicePlainData'.static.SetInt(transferData, "leftMag", MagAmmoRemLeft);
    class'NicePlainData'.static.SetInt(transferData, "rightMag", MagAmmoRemRight);
    return transferData;
}
function SetNiceData(NicePlainData.Data transferData, optional NiceHumanPawn newOwner){
    local int halfMag;
    super.SetNiceData(transferData, newOwner);
    if(newOwner != none)
       UpdateMagCapacity(newOwner.PlayerReplicationInfo);
    halfMag = GetSingleMagCapacity();
    MagAmmoRemLeft = class'NicePlainData'.static.GetInt(transferData, "leftMag", halfMag);
    MagAmmoRemRight = class'NicePlainData'.static.GetInt(transferData, "rightMag", halfMag);
    ClientSetDualMagSize(MagAmmoRemLeft, MagAmmoRemRight);
}
simulated function AltFire(float F){
    if(NicePlayerController(Instigator.Controller) != none)
       ClientForceInterruptReload(CANCEL_PASSIVESWITCH);
    if(!bIsReloading)
       ServerSwitchToSingle();
}
simulated function FireGivenGun(bool bFireLeft){
    local NiceDualiesFire niceFireMode;
    niceFireMode = NiceDualiesFire(FireMode[0]);
    if(niceFireMode != none){
       if(bFireLeft)
           niceFireMode.ModeDoFireLeft();
       else
           niceFireMode.ModeDoFireRight();
    }
}
function NiceSingle ServerSwitchToGivenSingle(bool bSwitchToLeft){
    local int m;
    local int origAmmo;
    local NiceHumanPawn nicePawn;
    local NiceSingle singlePistol;
    local NicePlainData.Data transferData;
    nicePawn = NiceHumanPawn(Instigator);
    if(nicePawn == none || SingleClass == none || nicePawn.Health <= 0)
       return none;
    nicePawn.CurrentWeight -= Weight;
    Weight = 0;
    bSwitching = true;
    origAmmo = AmmoAmount(0);
    for(m = 0; m < NUM_FIRE_MODES;m ++)
       if(FireMode[m].bIsFiring)
           StopFire(m);
    DetachFromPawn(nicePawn);
    singlePistol = nicePawn.Spawn(SingleClass);
    if(singlePistol != none){
       singlePistol.Weight = default.Weight;
       singlePistol.DemoReplacement = DemoReplacement;
       transferData = GetNiceData();
       singlePistol.GiveTo(nicePawn);
       singlePistol.SetNiceData(transferData, nicePawn);
       singlePistol.bIsDual = true;
       singlePistol.Weight = default.Weight;
       singlePistol.SellValue = SellValue;
       if(bSwitchToLeft){
           singlePistol.otherMagazine = MagAmmoRemRight;
           singlePistol.MagAmmoRemaining = MagAmmoRemLeft;
           singlePistol.Ammo[0].AmmoAmount = origAmmo - MagAmmoRemRight;
       }
       else{
           singlePistol.otherMagazine = MagAmmoRemLeft;
           singlePistol.MagAmmoRemaining = MagAmmoRemRight;
           singlePistol.Ammo[0].AmmoAmount = origAmmo - MagAmmoRemLeft;
       }
       singlePistol.ClientSetMagSize(singlePistol.MagAmmoRemaining, false);
       //nicePawn.ServerChangedWeapon(self, singlePistol);
       //nicePawn.ClientChangeWeapon(singlePistol);
    }
    Destroy();
    return singlePistol;
}
function NiceSingle ServerSwitchToSingle(){
    return ServerSwitchToGivenSingle(MagAmmoRemLeft > MagAmmoRemRight);
}
function DropFrom(vector StartLocation){
    local int m;
    local int magKeep, magGive;
    local NiceHumanPawn nicePawn;
    local KFWeaponPickup weapPickup;
    local NiceSingle singlePistol;
    local int AmmoThrown, OtherAmmo;
    nicePawn = NiceHumanPawn(Instigator);
    if(nicePawn == none || !bCanThrow || SingleClass == none)
       return;
    nicePawn.CurrentWeight -= Weight;
    Weight = 0;
    bSwitching = true;
    if(MagAmmoRemLeft > MagAmmoRemRight){
       magKeep = MagAmmoRemLeft;
       magGive = MagAmmoRemRight;
    }
    else{
       magKeep = MagAmmoRemRight;
       magGive = MagAmmoRemLeft;
    }
    OtherAmmo = AmmoAmount(0);
    ClientWeaponThrown();
    for(m = 0; m < NUM_FIRE_MODES;m ++)
       if(FireMode[m].bIsFiring)
           StopFire(m);
    DetachFromPawn(nicePawn);
    AmmoThrown = magGive;
    OtherAmmo = OtherAmmo - AmmoThrown;
    singlePistol = nicePawn.Spawn(SingleClass);
    if(singlePistol != none){
       singlePistol.DemoReplacement = none;
       singlePistol.GiveTo(nicePawn);
       singlePistol.Ammo[0].AmmoAmount = OtherAmmo;
       singlePistol.MagAmmoRemaining = magKeep;
       singlePistol.ClientSetMagSize(singlePistol.MagAmmoRemaining, false);
       MagAmmoRemaining = magGive;
       //nicePawn.ServerChangedWeapon(self, singlePistol);
       //nicePawn.ClientChangeWeapon(singlePistol);
    }
    weapPickup = KFWeaponPickup(nicePawn.Spawn(SingleClass.default.PickupClass,,, StartLocation));
    if(weapPickup != none){
       weapPickup.InitDroppedPickupFor(self);
       weapPickup.Weight = default.Weight;
       weapPickup.Velocity = Velocity;
       weapPickup.AmmoAmount[0] = AmmoThrown;
       weapPickup.SellValue = SellValue / 2;
       singlePistol.SellValue = weapPickup.SellValue;
       weapPickup.MagAmmoRemaining = magGive;
       if(nicePawn.Health > 0)
           weapPickup.bThrown = true;
    }
    Destroy();
    if(KFGameType(Level.Game) != none)
       KFGameType(Level.Game).WeaponDestroyed(class);
}
function bool HandlePickupQuery(pickup Item){
    if(Item.InventoryType == SingleClass){
       if(LastHasGunMsgTime < Level.TimeSeconds && PlayerController(Instigator.Controller) != none){
           LastHasGunMsgTime = Level.TimeSeconds + 0.5;
           PlayerController(Instigator.Controller).ReceiveLocalizedMessage(Class'KFMainMessages', 1);
       }
       return true;
    }
    return super.HandlePickupQuery(Item);
}
// Nice functions
simulated function int GetSingleMagCapacity(){
    return int(float(MagCapacity) * 0.5);
}
function UpdateWeaponMag(){
    ServerUpdateWeaponMag();
}
function ServerUpdateWeaponMag(){
    UpdateMagCapacity(Instigator.PlayerReplicationInfo);
    MagAmmoRemLeft = Min(MagAmmoRemLeft, GetSingleMagCapacity());
    MagAmmoRemRight = Min(MagAmmoRemRight, GetSingleMagCapacity());
    ClientSetDualMagSize(MagAmmoRemLeft, MagAmmoRemRight);
}
simulated function ClientUpdateWeaponMag(){
    UpdateMagCapacity(Instigator.PlayerReplicationInfo);
    MagAmmoRemLeftClient = Min(MagAmmoRemLeftClient, GetSingleMagCapacity());
    MagAmmoRemRightClient = Min(MagAmmoRemRightClient, GetSingleMagCapacity());
    ServerSetDualMagSize(MagAmmoRemLeftClient, MagAmmoRemRightClient, Level.TimeSeconds);
}
// Forces update for client's magazine ammo counter
// In case we are using client-side hit-detection, client itself manages remaining ammunition in magazine, but in some cases we want server to dictate current magazine amount
// This function sets client's mag size to a given value
simulated function ClientSetDualMagSize(int newLeftMag, int newRightMag){
    MagAmmoRemLeftClient = newLeftMag;
    MagAmmoRemRightClient = newRightMag;
    MagAmmoRemainingClient = MagAmmoRemLeftClient + MagAmmoRemRightClient;
}
// This function allows clients to change magazine size without altering total ammo amount
// It allows clients to provide time-stamps, so that older change won't override a newer one
function ServerSetDualMagSize(int newLeftMag, int newRightMag, float updateTime){
    MagAmmoRemLeft = newLeftMag;
    MagAmmoRemRight = newRightMag;
    magAmmoRemaining = MagAmmoRemLeft + MagAmmoRemRight;
    if(LastMagUpdateFromClient <= updateTime){
       LastMagUpdateFromClient = updateTime;
       if(magAmmoRemaining > 0)
           bServerFiredLastShot = false;
    }
}
// This function allows clients to change magazine size along with total ammo amount on the server (to update ammo counter in client-side mode)
// It allows clients to provide time-stamps, so that older change won't override a newer one
// Intended to be used for decreasing ammo count from shooting and cannot increase magazine size
function ServerReduceDualMag(int newLeftMag, int newRightMag, float updateTime, int Mode){
    local int delta;
    delta = MagAmmoRemLeft - newLeftMag;
    delta += MagAmmoRemRight - newRightMag;
    // Only update later changes that actually decrease magazine
    if(LastMagUpdateFromClient <= updateTime && delta > 0){
       LastMagUpdateFromClient = updateTime;
       MagAmmoRemLeft = newLeftMag;
       MagAmmoRemRight = newRightMag;
       ConsumeAmmo(Mode, delta);
       MagAmmoRemaining = MagAmmoRemLeft + MagAmmoRemRight;
    }
}
simulated function int GetMagazineAmmoLeft(){
    if(Role < ROLE_Authority)
       return MagAmmoRemLeftClient;
    else
       return MagAmmoRemLeft;
}
simulated function int GetMagazineAmmoRight(){
    if(Role < ROLE_Authority)
       return MagAmmoRemRightClient;
    else
       return MagAmmoRemRight;
}
simulated function bool AllowReload(){
    UpdateMagCapacity(Instigator.PlayerReplicationInfo);
    if(FireMode[0].IsFiring() ||
       bIsReloading || (GetMagazineAmmoLeft() >= GetSingleMagCapacity() && GetMagazineAmmoRight() >= GetSingleMagCapacity()) ||
       ClientState == WS_BringUp ||
       AmmoAmount(0) <= GetMagazineAmmo())
       return false;
    return true;
}
simulated function WeaponTick(float dt){
    if(Role == Role_AUTHORITY)
       MagAmmoRemaining = MagAmmoRemLeft + MagAmmoRemRight;
    else
       MagAmmoRemainingClient = MagAmmoRemLeftClient + MagAmmoRemRightClient;
    super.WeaponTick(dt);
}
// Some functions reloaded to force update of magazine size on client's side
function GiveAmmo(int m, WeaponPickup WP, bool bJustSpawned){
    super.GiveAmmo(m, WP, bJustSpawned);
    ClientSetDualMagSize(MagAmmoRemLeft, MagAmmoRemRight);
}

defaultproperties
{
     SingleClass=class'NiceSingle'
     altFlashBoneName="Tip_Left"
     altTPAnim="DualiesAttackLeft"
     altWeaponAttach="Bone_weapon2"
     leftEjectStr="LEFT_EJECT"
     rightEjectStr="RIGHT_EJECT"
     leftInsertStr="LEFT_INSERT"
     rightInsertStr="RIGHT_INSERT"
     leftEject=0.130000
     rightEject=0.102000
     leftInsert=0.444000
     rightInsert=0.787000
     reloadChargeEndFrame=-1.000000
     reloadMagStartFrame=-1.000000
     reloadChargeStartFrame=-1.000000
     MagazineBone=
     bHasChargePhase=False
     FirstPersonFlashlightOffset=(X=-15.000000,Z=5.000000)
     MagCapacity=30
     ReloadRate=3.500000
     ReloadAnim="Reload"
     ReloadAnimRate=1.000000
     FlashBoneName="Tip_Right"
     WeaponReloadAnim="Reload_Dual9mm"
     Weight=4.000000
     bDualWeapon=True
     bHasAimingMode=True
     IdleAimAnim="Idle_Iron"
     StandardDisplayFOV=70.000000
     TraderInfoTexture=Texture'KillingFloorHUD.Trader_Weapon_Images.Trader_Dual_9mm'
     ZoomInRotation=(Pitch=0,Roll=0)
     ZoomedDisplayFOV=65.000000
     FireModeClass(0)=class'NiceDualiesFire'
     FireModeClass(1)=Class'KFMod.NoFire'
     PutDownAnim="PutDown"
     AIRating=0.440000
     CurrentRating=0.440000
     bShowChargingBar=True
     Description="A pair of custom 9mm pistols. What they lack in stopping power, they compensate for with a quick refire."
     EffectOffset=(X=100.000000,Y=25.000000,Z=-10.000000)
     DisplayFOV=70.000000
     Priority=65
     InventoryGroup=2
     GroupOffset=2
     PickupClass=class'NiceDualiesPickup'
     PlayerViewOffset=(X=20.000000,Z=-7.000000)
     BobDamping=7.000000
     AttachmentClass=class'NiceDualiesAttachment'
     IconCoords=(X1=229,Y1=258,X2=296,Y2=307)
     ItemName="!!!Dual something"
     DrawScale=0.900000
     TransientSoundVolume=1.000000
}
