//================================================== //================================================== // 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" }