Advertisement
Guest User

Untitled

a guest
Jun 12th, 2018
523
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 210.15 KB | None | 0 0
  1. //=============================================================================
  2. // KFWeapon
  3. //=============================================================================
  4. // The base KF 2 weapon class
  5. //=============================================================================
  6. // Killing Floor 2
  7. // Copyright (C) 2015 Tripwire Interactive LLC
  8. // - John "Ramm-Jaeger" Gibson 9/27/2012
  9. //=============================================================================
  10. class KFWeapon extends Weapon
  11. abstract
  12. native
  13. nativereplication
  14. hidecategories(Advanced,Collision,Mobile,Movement,Object,Physics,Attachment,Debug)
  15. dependson(KFPlayerReplicationInfo, KFMeleeHelperWeapon);
  16.  
  17. `include(KFGame\KFGameAnalytics.uci);
  18.  
  19. /** mesh for overlay - Each weapon will need to add its own overlay mesh in its default props */
  20. var protected MeshComponent OverlayMesh;
  21.  
  22. /** Cached reference to KFSkeletalMeshComponent(Mesh) */
  23. var KFSkeletalMeshComponent MySkelMesh;
  24.  
  25. /************************************************************************************
  26. * @name Firing / Timing / States
  27. ***********************************************************************************/
  28. /** Fire mode 0 is the default weapon firing. */
  29. const DEFAULT_FIREMODE = 0;
  30. /** Fire mode 1 is the alternate weapon firing. */
  31. const ALTFIRE_FIREMODE = 1;
  32. /** arbitrary firemode for reloading */
  33. const RELOAD_FIREMODE = 2;
  34. /** Firing mode used when doing a melee attack */
  35. const BASH_FIREMODE = 3;
  36. /** Firing mode used when doing a melee attack */
  37. const GRENADE_FIREMODE = 4;
  38.  
  39. /** Invalid firemode */
  40. const FIREMODE_NONE = 255;
  41.  
  42. var array<Texture2D> FireModeIconPaths;
  43.  
  44. /** For weapons that have an automatic or burst fire mode, this stores the
  45. * single fire mode for use by the sound system when in zed time. If set
  46. * to 255 then the standard looping fire sound will still be played in
  47. * zed time */
  48. var byte SingleFireSoundIndex;
  49.  
  50. /** Alt-fire mode is selected, all default fires will be converted */
  51. var bool bUseAltFireMode;
  52. /** Flag to make sure we stop the correct FireMode when using alt-fire toggle */
  53. var transient bool bStopAltFireOnNextRelease;
  54.  
  55. /** Holds the min. amount of refire time that has to pass before you can switch */
  56. var float MinFiringPutDownPct;
  57.  
  58. /** How much penetration power does this fire mode have */
  59. var(Weapon) Array<float> PenetrationPower;
  60. /** How scale the penetration power this fire mode loses based on the percentage of penetration power left*/
  61. var(Weapon) Array<InterpCurveFloat> PenetrationDamageReductionCurve;
  62. /** How much penetration power is remaining for this shot */
  63. var transient float PenetrationPowerRemaining;
  64.  
  65. /** If > 0, reduce the effect of zed time dilation on this weapon */
  66. var const transient float ZedTimeResistance;
  67.  
  68. /** How long after we toss a grenade before a zed can grab us.
  69. * Prevents us from blowing ourselves up on grenades that we
  70. * were in the middle of tossing when we got grabbed */
  71. var(Weapon) float GrenadeTossWeakZedGrabCooldown;
  72.  
  73. /** Time after we are grabbed by a zed to prevent nade throwing so we don't blow ourselvs up */
  74. var(Weapon) float ZedGrabGrenadeTossCooldown;
  75.  
  76. /** Set when calling StartFire(DEFAULT_FIREMODE) from a gamepad press */
  77. var bool bGamepadFireEntry;
  78.  
  79. /** Number of shots to fire per burst. */
  80. var(Weapon) byte BurstAmount;
  81.  
  82. /************************************************************************************
  83. * @name Aim Assist
  84. ***********************************************************************************/
  85.  
  86. /** Target friction enabled? */
  87. var() bool bTargetFrictionEnabled;
  88.  
  89. /** Max distance allow for friction */
  90. var() float TargetFrictionDistanceMax;
  91.  
  92. /** Target adhesion enabled? */
  93. var() bool bTargetAdhesionEnabled;
  94.  
  95. /** Max distance to allow adhesion to still kick in */
  96. var() float TargetAdhesionDistanceMax;
  97.  
  98. /** Adhesion scalar curve based on distance */
  99. var() InterpCurveFloat TargetAdhesionOffsetScaleCurve;
  100. /** Adhesion scalar curve based on angle offset to center of zed cylinder */
  101. var() InterpCurveFloat TargetAdhesionDistanceScaleCurve;
  102.  
  103. /** Friction scalar curve based on distance */
  104. var() InterpCurveFloat TargetFrictionOffsetScaleCurve;
  105. /** Friction scalar curve based on angle offset to center of zed cylinder */
  106. var() InterpCurveFloat TargetFrictionDistanceScaleCurve;
  107.  
  108. /** Aim correction - headshot offset */
  109. var() const float AimCorrectionSize;
  110.  
  111. /************************************************************************************
  112. * @name Player controller vars
  113. ***********************************************************************************/
  114.  
  115. /** Current KFPlayerController controlling this weapon */
  116. var KFPlayerController KFPlayer;
  117.  
  118. /************************************************************************************
  119. * @name Weapon positioning: Iron sights, hipped, etc
  120. ***********************************************************************************/
  121.  
  122. /** The weapon is using the sights to aim*/
  123. var bool bUsingSights;
  124. /** This weapon has sights to aim*/
  125. var(IronSight) bool bHasIronSights;
  126.  
  127. /** The weapon wants to go to ironsights when the bring up happens (because someone was already in ironsights when they switched)*/
  128. var bool bIronSightOnBringUp;
  129.  
  130. /** Hide the weapon for screenshots, etc */
  131. var bool bForceHidden;
  132.  
  133. /** special offset when using hidden weapons, as we need to still place the weapon for e.g. attached beams */
  134. var vector HiddenWeaponsOffset;
  135.  
  136. /** The default FOV to use for this weapon when not in ironsights */
  137. var(Camera) float MeshFOV;
  138. /** The fov to use for this weapon when in ironsights */
  139. var(Camera) float MeshIronSightFOV;
  140. /** The fov to use for the player when in ironsights */
  141. var(Camera) float PlayerIronSightFOV;
  142. /** The fov to use while in the weapon sprinting state */
  143. var(Camera) float PlayerSprintFOV;
  144.  
  145. /** The position of the weapon when in standard ironsights position */
  146. var(Positioning) vector IronSightPosition;
  147. /** We are transitioning to zoomed in. When set to true native code will attempt to interpolate to the zoomed in ironsight position */
  148. var bool bZoomingIn;
  149. /** We are transitioning to zoomed out. When set to true native code will attempt to interpolate to the zoomed out ironsight position */
  150. var bool bZoomingOut;
  151. /** Time remaining on the active transition to/from iron sights */
  152. var float ZoomTime;
  153. /** How long the transition to iron sights should take */
  154. var(IronSight) float ZoomInTime;
  155. /** How long the transition from iron sights should take */
  156. var(IronSight) float ZoomOutTime;
  157. /** Amount to rotate to when zooming in to give the feeling of an animation playing */
  158. var(IronSight) rotator ZoomInRotation;
  159. /** Doing a "quick down" zoom */
  160. var(IronSight) bool bDoingQuickDownZoom;
  161. /** Amount to rotate to when doing a quick weapon put down */
  162. var(IronSight) rotator QuickWeaponDownRotation;
  163. /** How long the transition to quick weapon down should take */
  164. var(IronSight) float QuickWeaponDownTime;
  165. /** How long the transition from quick weapon down should take */
  166. var(IronSight) float QuickWeaponDownFinishTime;
  167. /** Don't use the ZoomInRotation for this zoom */
  168. var(IronSight) bool bSkipZoomInRotation;
  169. /** Set by the native code when zooming in/out. This is the interpolated rotation over time of the value set in ZoomInRotation */
  170. var rotator ZoomRotInterp;
  171. /** When we start zooming what player view location is the first person weapon in. Used by the native code to calculate where to start zooming from */
  172. var vector ZoomStartOffset;
  173. /** We were zooming in and it was interrupted. Used by the native code to handle smooth interpolations between zoomed positions */
  174. var bool bZoomInInterrupted;
  175. /** We were zooming out and it was interrupted. Used by the native code to handle smooth interpolations between zoomed positions */
  176. var bool bZoomOutInterrupted;
  177. /** How much time we have to finish a partial zoom out. Used by the native code to handle smooth interpolations between zoomed positions */
  178. var float ZoomPartialTime;
  179. /** When we start zooming what rotation offset is the weapon. Used by the native code to handle smooth interpolations between zoomed positions */
  180. var rotator ZoomRotStartOffset;
  181. /** When we start zooming what WeaponFOV did we start with */
  182. var float ZoomWeaponFOVStart;
  183. /** The last time we zoomed out. */
  184. var float LastZoomOutTime;
  185. /** How long to take to zoom out when we're doing a fast zoom out (i.e. when an action like reloading interupts ironsights) */
  186. var(IronSight) float FastZoomOutTime;
  187. /** The weapon is doing a fast zoom out without animating, and won't get ZoomStart/End Notifies */
  188. var bool bFastZoomOut;
  189. /** The Target player view location for the first person weapon for this zoom */
  190. var vector ZoomTargetOffset;
  191. /** The weapon has a scope and should use the scope offset for zoomed in mode */
  192. var bool bHasScopePosition;
  193. /** The position of the weapon when looking through the scope */
  194. var(Positioning) vector ScopePosition;
  195. /** Currently zoomed into the scope position */
  196. var bool bUsingScopePosition;
  197.  
  198. var(DepthOfField) bool DOF_bOverrideEnvironmentDOF;
  199. var(DepthOfField) float DOF_SharpRadius;
  200. var(DepthOfField) float DOF_FocalRadius;
  201. var(DepthOfField) float DOF_MinBlurSize;
  202. var(DepthOfField) float DOF_MaxNearBlurSize;
  203. var(DepthOfField) float DOF_MaxFarBlurSize;
  204. var(DepthOfField) float DOF_ExpFalloff;
  205. var(DepthOfField) float DOF_MaxFocalDistance;
  206.  
  207. var(DepthOfField) float DOF_BlendInSpeed;
  208. var(DepthOfField) float DOF_BlendOutSpeed;
  209.  
  210. var(DepthOfField) float DOF_FG_SharpRadius;
  211. var(DepthOfField) float DOF_FG_FocalRadius;
  212. var(DepthOfField) float DOF_FG_MinBlurSize;
  213. var(DepthOfField) float DOF_FG_MaxNearBlurSize;
  214. var(DepthOfField) float DOF_FG_ExpFalloff;
  215.  
  216. /** Holds an offest for spawning grenades. */
  217. var(Positioning) vector GrenadeFireOffset;
  218.  
  219. /** The weapon is using the sights to aim*/
  220. var(Positioning) bool bWeaponNeedsServerPosition;
  221.  
  222. /** If true, weapon position should follow camera anims played on the weapon AnimSeq */
  223. var(Positioning) bool bFollowAnimSeqCamera;
  224.  
  225. /** If TRUE, will use system to warn AI when a player has been aiming at them for too long */
  226. var(IronSight) protected const bool bWarnAIWhenAiming;
  227.  
  228. /** The maximum distance at which to warn AI, squared */
  229. var(IronSight) protected const float MaxAIWarningDistSQ;
  230.  
  231. /** The maximum distance from the danger point that AI should be warned */
  232. var(IronSight) protected const float MaxAIWarningDistFromPointSQ;
  233.  
  234. /** How long the weapon needs to be relatively settled before warning AI. X=MinDuration, Y=MaxDuration */
  235. var(IronSight) protected const vector2d AimWarningDelay;
  236.  
  237. /** How long to wait after a warning before checking if AI should be warned again */
  238. var(IronSight) protected const float AimWarningCooldown;
  239.  
  240. /** Last aim rotation between settled checks */
  241. var transient private rotator LastAimRotation;
  242.  
  243. /** Last time we warned AI */
  244. var transient private float LastAimWarningTime;
  245.  
  246. /** How much accumulated time the weapon has been settled for */
  247. var transient private float CurrentAimSettledTime;
  248.  
  249. /*********************************************************************************************
  250. * @name Inventory Grouping/etc.
  251. ********************************************************************************************* */
  252.  
  253. enum EInventoryGroup
  254. {
  255. IG_Primary,
  256. IG_Secondary,
  257. IG_Melee,
  258. IG_Equipment,
  259. IG_None
  260. };
  261.  
  262. /** Determines which group a weapon falls into in weapon select */
  263. var(Inventory) EInventoryGroup InventoryGroup;
  264.  
  265. /** Inventory (In blocks) cost */
  266. var(Inventory) byte InventorySize;
  267.  
  268. /** Used to place this weapon in the inventory */
  269. var(Inventory) float GroupPriority;
  270. ///** The UI image for this weapon */
  271. var(Inventory) Texture2D WeaponSelectTexture;
  272. /** The path that locates the image for this weapon */
  273. var(Inventory) Texture2D SecondaryAmmoTexture;
  274. /** The path that locates the image for this weapon */
  275. var(Inventory) bool bCanRefillSecondaryAmmo;
  276.  
  277. /** used when aborting a weapon switch (state WeaponAbortEquip) */
  278. var float EquipAbortTime;
  279.  
  280. /** True if we were given this weapon at spawn */
  281. var bool bGivenAtStart;
  282.  
  283. /** class of the dual variant of this weapon, if applicable */
  284. var class<KFWeap_DualBase> DualClass;
  285.  
  286. /*********************************************************************************************
  287. * @name Ammo / Reload
  288. ********************************************************************************************* */
  289.  
  290. /** If set execute PerformReload on client only and synchronize at end of state */
  291. `define USE_RELOAD_SYNC 1
  292.  
  293. const PRIMARY_AMMO = 0;
  294. const SECONDARY_AMMO = 1;
  295.  
  296. // ---------------------------------------------
  297. // Magazine - Primary and Secondary/alt
  298.  
  299. /** Ammo from current magazine */
  300. var byte AmmoCount[2];
  301. /** Size of the weapon magazine, i.e. how many rounds it can hold */
  302. var(Inventory) byte MagazineCapacity[2];
  303. /** How much ammo does it take to fire this firemode? */
  304. var(Inventory) protected Array<byte> AmmoCost;
  305. /** Is this a no magazine/clip weapon e.g. the hunting shotgun? */
  306. var(Inventory) bool bNoMagazine;
  307.  
  308. // ---------------------------------------------
  309. /// Spare ammo - Primary Only... for now
  310.  
  311. /** Spare ammo, contained in extra magazines (outside of what's currently in the weapon) */
  312. var repnotify int SpareAmmoCount[2];
  313. /** Maximum amount of amount that can be carried for this gun, not counting what is in the magazine. Total amount this weapon can carry is SpareAmmoCapacity + MagazineCapacity */
  314. var(Inventory) int SpareAmmoCapacity[2];
  315. /** Number of additional magazines to start with. Starting ammo total is (InitialSpareMags * MagazineCapacity) + MagazineCapacity */
  316. var int InitialSpareMags[2];
  317.  
  318. /** What percentage of a full single magazine capacity to give when resupplying this weapon from an ammo pickup */
  319. var(Inventory) float AmmoPickupScale[2];
  320.  
  321. enum EReloadStatus
  322. {
  323. RS_None,
  324. RS_OpeningBolt,
  325. RS_Reloading,
  326. RS_ClosingBolt,
  327. RS_Complete
  328. };
  329.  
  330. var EReloadStatus ReloadStatus;
  331. var(Inventory) bool bCanBeReloaded;
  332. var(Inventory) bool bReloadFromMagazine;
  333.  
  334. /** Number of rounds left to reload during the reload state */
  335. var byte ReloadAmountLeft;
  336. /** Number of rounds to reload during the reload state */
  337. var byte InitialReloadAmount;
  338.  
  339. /**
  340. * Initial Spare ammo when starting a reload. Used to keep track of how much ammo should be loaded into the gun
  341. * This is so, in case ammo changes when reloading, there are not desync problems.
  342. */
  343. var int InitialReloadSpareAmmo;
  344.  
  345. /** If set, this weapon can always reload */
  346. var const bool bInfiniteSpareAmmo;
  347.  
  348. /** If set, this weapon won't consume any ammo */
  349. var bool bInfiniteAmmo;
  350.  
  351. /** If FALSE, AmmoCount is server authoritative and should not be modified on the client */
  352. var const bool bAllowClientAmmoTracking;
  353.  
  354. /** Last time 'AbortReload' was called */
  355. var transient float LastReloadAbortTime;
  356.  
  357. /*********************************************************************************************
  358. * @name Fire Effects
  359. ******************************************************************************************* */
  360.  
  361. /** Camera anim to play when firing (for camera shakes) */
  362. var array<CameraAnim> FireCameraAnim;
  363.  
  364. var(Camera) float ShakeScaleSighted; // How much to scale the FireCameraAnim when in ironsights
  365. var(Camera) float ShakeScaleStandard; // How much to scale the FireCameraAnim by default (Hipped/Shouldered)
  366.  
  367. /** Controller rumble to play when firing. */
  368. var ForceFeedbackWaveform WeaponFireWaveForm;
  369.  
  370. var bool bPlayingLoopingFireSnd;
  371. var bool bPlayingLoopingFireAnim;
  372.  
  373. /*********************************************************************************************
  374. * @name Animations (const names reduce instanced data)
  375. ********************************************************************************************* */
  376.  
  377. /** Cached anim nodes */
  378. var KFAnimSeq_Tween WeaponAnimSeqNode;
  379. var AnimNodeAdditiveBlending IdleBobBlendNode;
  380. var AnimNodeBlendPerBone EmptyMagBlendNode;
  381.  
  382. // Loop settings per firemode
  383. var(Animations) array<bool> bLoopingFireAnim;
  384. var(Sounds) array<bool> bLoopingFireSnd;
  385.  
  386. /** How much tweening to use on fire animations */
  387. var(Animations) float FireTweenTime;
  388. /** Use the animation sequence length to get equip times */
  389. var(Animations) bool bUseAnimLenEquipTime;
  390.  
  391. /** If set, this weapon has unique shoot anims for the last round */
  392. var(Animations) bool bHasFireLastAnims;
  393.  
  394. // Standard
  395. /** Animations to play when the weapon is fired */
  396. var(Animations) const editconst name FireAnim;
  397. /** Animation to play when the weapon is fired and bLoopingFireAnim is true */
  398. var(Animations) const editconst name FireLoopAnim;
  399. /** Animation to play when the last shot if fired */
  400. var(Animations) const editconst name FireLastAnim;
  401. /** Animation to play when the weapon is Put Down */
  402. var(Animations) const editconst name PutDownAnim;
  403. /** Animation to play when the weapon is Equipped */
  404. var(Animations) const editconst name EquipAnim;
  405. /** Animation to play when the weapon is idle */
  406. var(Animations) const editconst array<name> IdleAnims;
  407.  
  408. // Special idle anims
  409. var(Animations) bool bUseAdditiveMoveAnim;
  410. var(Animations) const editconst array<name> IdleFidgetAnims;
  411. var transient float LastIdleFidgetAnimTime;
  412.  
  413. // Iron Sights
  414. /** Animation to play when the weapon is fired */
  415. var(Animations) const editconst array<name> FireSightedAnims;
  416. /** Animation to play when the weapon is fired and bLoopingFireAnim is true */
  417. var(Animations) const editconst name FireLoopSightedAnim;
  418. /** Animation to play when the last shot if fired */
  419. var(Animations) const editconst name FireLastSightedAnim;
  420. /** Animation to play when idling */
  421. var(Animations) editconst array<name> IdleSightedAnims;
  422.  
  423. // High RPM Firing
  424. /** Animation to play at the end of a looping fire anim */
  425. var(Animations) const editconst name FireLoopStartAnim;
  426. /** Animation to play at the end of a looping fire anim */
  427. var(Animations) const editconst name FireLoopStartSightedAnim;
  428. /** Animation to play at the end of a looping fire anim */
  429. var(Animations) const editconst name FireLoopEndAnim;
  430. /** Animation to play at the end of a looping fire anim */
  431. var(Animations) const editconst name FireLoopEndSightedAnim;
  432.  
  433. // Scoped Firing
  434. /** Animation to play when the weapon is fired */
  435. var(Animations) const editconst name FireScopedAnim;
  436. /** Animation to play when the weapon is fired and bLoopingFireAnim is true */
  437. var(Animations) const editconst name FireLoopScopedAnim;
  438. /** Animation to play when the last shot if fired */
  439. var(Animations) const editconst name FireLastScopedAnim;
  440.  
  441. /** Default melee attack animation names */
  442. var array<name> MeleeAttackAnims;
  443.  
  444. // Reloading (const to reduce instanced data)
  445. const ReloadEmptyMagAnim = 'Reload_Empty';
  446. const ReloadNonEmptyMagAnim = 'Reload_Half';
  447. const ReloadEmptyMagEliteAnim = 'Reload_Empty_Elite';
  448. const ReloadNonEmptyMagEliteAnim = 'Reload_Half_Elite';
  449.  
  450. const ReloadOpenAnim = 'Reload_Open';
  451. const ReloadSingleAnim = 'Reload_Insert';
  452. const ReloadOpenInsertAnim = 'Reload_Open_Shell';
  453. const ReloadCloseAnim = 'Reload_Close';
  454. const ReloadOpenEliteAnim = 'Reload_Open_Elite';
  455. const ReloadSingleEliteAnim = 'Reload_Insert_Elite';
  456. const ReloadOpenInsertEliteAnim = 'Reload_Open_Shell_Elite';
  457. const ReloadCloseEliteAnim = 'Reload_Close_Elite';
  458.  
  459. // Grenades
  460. /** Animation to play when a grenade is thrown */
  461. const GrenadeThrowAnim = 'Nade_Throw';
  462.  
  463. // Sprinting
  464. const SprintStartAnim = 'Sprint_In';
  465. const SprintLoopAnim = 'Sprint_Loop';
  466. const SprintEndAnim = 'Sprint_Out';
  467.  
  468. // Shoot animation to play when reload is interrupted when bReloadFromMagazine = false
  469. const FireOneHandAnim = 'Shoot_OneHand';
  470. const FireOneHandLastAnim = 'Shoot_OneHand_Last';
  471.  
  472. /** Camera anim played when sprinting */
  473. var(Camera) CameraAnim SprintCameraAnim;
  474. var transient CameraAnimInst SprintCameraAnimInst;
  475. var transient float SprintAnimRate;
  476.  
  477. // SkelControls
  478. var bool bEnableTiltSkelControl;
  479.  
  480. /** Set from unlock notify to enable test for an aborted reload */
  481. var transient bool bCheckBoltLockPostReload;
  482.  
  483. /** array of bones to lock when out of ammo */
  484. var array<name> BonesToLockOnEmpty;
  485.  
  486. /*********************************************************************************************
  487. * @name Sounds
  488. ********************************************************************************************* */
  489.  
  490. var(Sounds) bool bSuppressSounds;
  491.  
  492. struct native WeaponFireSndInfo
  493. {
  494. var() AkEvent DefaultCue;
  495. var() AkEvent FirstPersonCue;
  496. };
  497.  
  498. /** Sound to play when the weapon is fired */
  499. var(Sounds) array<WeaponFireSndInfo> WeaponFireSnd;
  500. /** sound to play when the weapon stops fired. Used for high ROF weapons that have a looping fire sound */
  501. var(Sounds) array<WeaponFireSndInfo> WeaponFireLoopEndSnd;
  502.  
  503. /** sound to play when the weapon is dry fired */
  504. var(Sounds) array<AkBaseSoundObject> WeaponDryFireSnd;
  505. /** If set to true, attempting to reload will switch to best available weapon */
  506. var() bool bPendingAutoSwitchOnDryFire;
  507.  
  508. /*********************************************************************************************
  509. * @name First person weapon view positioning and rendering
  510. ********************************************************************************************* */
  511.  
  512. const BloodParamName = 'Scalar_Blood_Contrast';
  513. const MinBloodParamValue = 0.20f;
  514.  
  515. /** How much to damp view bob */
  516. var(Motion) float BobDamping;
  517.  
  518. /** How much to damp jump and land bob */
  519. var(Motion) float JumpDamping;
  520.  
  521. /** Offset from view center */
  522. var(Positioning) vector PlayerViewOffset;
  523.  
  524. /** If true, will be un-hidden on the next setPosition call. */
  525. var bool bPendingShow;
  526.  
  527. /** Used to display effects such as blood splatter and muzzle glow */
  528. var array<MaterialInstanceConstant> WeaponMICs;
  529.  
  530. /** Number of materials in mesh used for blood maps */
  531. var int NumBloodMapMaterials;
  532.  
  533. /** Defines how bloody the weapon looks */
  534. var float BloodParamValue;
  535.  
  536. /** ItemID for currently/last equipped weapon skin. TODO: Cloud save */
  537. var const config int SkinItemId;
  538.  
  539. /*********************************************************************************************
  540. * @name Weapon Lag System
  541. ********************************************************************************************* */
  542.  
  543. /** The weapon lag value to use to offset the weapon in first person. Calculated by ProcessWeaponLag in the native code*/
  544. var vector WeaponLag;
  545.  
  546. // Basic Weapon Lag Variables
  547. /** Horizontal Offset, in units from the weapon's resting position */
  548. var float LagHorizontal;
  549. /** Vertical Offset, in units from the weapon's resting position */
  550. var float LagVertical;
  551. var float LagVelocityHorizontal;
  552. var float LagVelocityVertical;
  553.  
  554. // Weapon Lag calculations for when the pawn's accelleration changes, or the pawn turns. Affects spring LagTensionHorizontal and dampening.
  555. var(Motion) float LagTensionHorizontal;
  556. var(Motion) float LagVerticalTension;
  557.  
  558. // Weapon LagResistanceHorizontal/Vertical represents how much the gun does not want to move at all, affects dampening.
  559. var(Motion) float LagResistanceHorizontal;
  560. var(Motion) float LagResistanceVertical;
  561.  
  562. // Limitations and Conversions
  563. /** the maximum offset/lag that the weapon should lag when moving around */
  564. var(Motion) float LagLimit;
  565. /** A constant that converts yaw velocity to linear velocity to be used in calculating weapon-lag */
  566. var(Motion) float LagYawCoefficient;
  567.  
  568. // Weapon Lag State Properties
  569. // Dynamic Lag tension modifiers. Effectively, these Strength values
  570. // are multiplied into the tension based on the pawn's state.
  571. var(Motion) float LagStrengthIronSights;
  572. var(Motion) float LagStrengthCrouch;
  573. var(Motion) float LagStrengthWalk;
  574. var(Motion) float LagStrengthJog;
  575. var(Motion) float LagStrengthSprint;
  576.  
  577. // Strafe Lag - special lag for moving left and right
  578. /** The horizontal offset from center when the pawn Strafes left/right. */
  579. var float StrafeLag;
  580. var float StrafeLagVelocity;
  581. /** The maximum horizontal offset from center */
  582. var(Motion) float StrafeLagLimit;
  583. /** Rate at which momentum changes should affect Strafe lagging */
  584. var(Motion) float StrafeLagRate;
  585. /** Rate at which weapon should return to normal after straffing.*/
  586. var(Motion) float StrafeLagReturnRate;
  587.  
  588. /** How quickly the weapon is Yawing */
  589. var int AimYawSpeed;
  590. /** How quickly the weapon is Pitching */
  591. var int AimPitchSpeed;
  592.  
  593. /*********************************************************************************************
  594. * @name Attachments
  595. ********************************************************************************************* */
  596.  
  597. /** The class of the third person attachment to spawn */
  598. var(Attachments) KFWeaponAttachment AttachmentArchetype;
  599.  
  600. /** Object within weapon that manages melee attacks */
  601. var(Weapon) instanced KFMeleeHelperWeapon MeleeAttackHelper;
  602.  
  603. /** A muzzle flash instance */
  604. var KFMuzzleFlash MuzzleFlash;
  605. /** A reference to the muzzle flash template */
  606. var(Attachments) const KFMuzzleFlash MuzzleFlashTemplate;
  607.  
  608. /** How long ejected shells should stay in the foreground until changing to world depth */
  609. var(Attachments) const float EjectedShellForegroundDuration;
  610.  
  611. /** DEPRECATED */
  612. var deprecated bool bHasFlashlight;
  613.  
  614. /** A laser sight instance */
  615. var transient KFLaserSightAttachment LaserSight;
  616. /** A reference to the laser sight template */
  617. var(Attachments) const KFLaserSightAttachment LaserSightTemplate;
  618. /** Whether the weapon supports laser sights or not */
  619. var(Attachments) bool bHasLaserSight;
  620.  
  621. /*********************************************************************************************
  622. * @name Projectiles
  623. ********************************************************************************************* */
  624.  
  625. /** The max Dot Angle to allow for adjusting projectiles so they hit where the crosshair is aiming. This prevent wierd issues where you are firing on something really close, and the projectile spawning code makes the projectile's velocity (AimDir) go off in some really odd direction */
  626. const MaxAimAdjust_Angle = 0.1f; // Radians
  627. const MaxAimAdjust_Cos = 0.995f; // Cos(MaxAimAdjust)
  628.  
  629. /** Used only by shotgun classes. Needed here to function with both the Medic shotgun and regular shotguns, since they extend different base classes */
  630. var float LastPelletFireTime;
  631.  
  632. /*********************************************************************************************
  633. * @name Firing
  634. ********************************************************************************************* */
  635.  
  636. /** Amount to scale spread when moving and shooting. Set this to something greater than 1.0 if you want to have added spread while moving. KF1 did not have this */
  637. var(Weapon) float MovingSpreadMod;
  638. /** Amount to scale spread when using ironsights */
  639. var(Weapon) float IronSightsSpreadMod;
  640. /** Amount to scale spread when Crouched */
  641. var(Weapon) float CrouchSpreadMod;
  642.  
  643. /*********************************************************************************************
  644. * @name Recoil
  645. *
  646. * @note - recoil rotation is calculated randomly using the below values. The recoil pitch will be a random
  647. * amount between min and max Recoil Pitch. The recoil yaw will be a min and max value between min and max Recoil Yaw.
  648. * The weapon will recoil at for RecoilRate amount of seconds, and then when firing has ceased will blend back to zero at
  649. * the recoil rate multiplied by the RecoilBlendOutRatio. The weapon itself will move around independent of the camera
  650. * movement withing the RecoilMax limits. Suppression recoil is on top of standard recoil. It moves the player's
  651. * view around when bullets are whizzing by for example.
  652. ********************************************************************************************* */
  653.  
  654. /** max vertical units a weapon muzzle will climb from recoil */
  655. var(Recoil) int maxRecoilPitch;
  656. /** min vertical units a weapon muzzle will climb from recoil */
  657. var(Recoil) int minRecoilPitch;
  658. /** max horizontal units a weapon muzzle will move from recoil */
  659. var(Recoil) int maxRecoilYaw;
  660. /** min horizontal units a weapon muzzle will move from recoil */
  661. var(Recoil) int minRecoilYaw;
  662. /** Time in seconds each recoil should take to be applied. Must be less than the fire rate or the full recoil wont be applied */
  663. var(Recoil) float RecoilRate;
  664. /** What percentage of the RecoilSpeed it will take to blend recoil out */
  665. var(Recoil) float RecoilBlendOutRatio;
  666. /** What percentage of recoil to apply to the view rotation when firing */
  667. var(Recoil) float RecoilViewRotationScale;
  668. /** Scales how much effect the players rotation input (from mouse/gamepad) has on reducing the total recoil (the first person weapon model's recoil) */
  669. var(Recoil) float RecoilCompensationScale;
  670. /** At what percentage the weapon is recoiled torwards the Max or Min Pitch limit to start blending to full view rotation recoil */
  671. var(Recoil) float FullRecoilPitchPct;
  672. /** At what percentage the weapon is recoiled torwards the Max or Min Yaw limit to start blending to full view rotation recoil */
  673. var(Recoil) float FullRecoilYawPct;
  674.  
  675. // Normal Hipped Recoil
  676. /** Maximum yaw rotation of the weapon from recoil before the player's view moves */
  677. var(Recoil) int RecoilMaxYawLimit;
  678. /** Minimum yaw rotation of the weapon from recoil before the player's view moves */
  679. var(Recoil) int RecoilMinYawLimit;
  680. /** Maximum pitch rotation of the weapon from recoil before the player's view moves */
  681. var(Recoil) int RecoilMaxPitchLimit;
  682. /** Minimum pitch rotation of the weapon from recoil before the player's view moves */
  683. var(Recoil) int RecoilMinPitchLimit;
  684.  
  685. // Iron Sight Recoil
  686. /** Maximum yaw rotation of the weapon from recoil before the player's view moves when using Iron sights */
  687. var(Recoil) int RecoilISMaxYawLimit;
  688. /** Minimum yaw rotation of the weapon from recoil before the player's view moves when using Iron sights */
  689. var(Recoil) int RecoilISMinYawLimit;
  690. /** Maximum pitch rotation of the weapon from recoil before the player's view moves when using Iron sights */
  691. var(Recoil) int RecoilISMaxPitchLimit;
  692. /** Minimum pitch rotation of the weapon from recoil before the player's view moves when using Iron sights */
  693. var(Recoil) int RecoilISMinPitchLimit;
  694.  
  695. // Internal recoil vars used by the native code
  696. /** Stores recoil when a weapon is fired */
  697. var rotator RecoilRotator;
  698. /** Stores the total recoil for this group of weapon firing */
  699. var rotator TotalRecoilRotator;
  700. /** How much time left to process this recoil */
  701. var float RecoilTimeLeft;
  702. /** The amount of time it will take to process this recoil */
  703. var float RecoilSpeed;
  704. /** Stores the calculated yaw blend out rate */
  705. var int RecoilYawBlendOutRate;
  706. /** Stores the calculated pitch blend out rate */
  707. var int RecoilPitchBlendOutRate;
  708.  
  709. /** Used to track how close the recoil is to the edge of the buffer */
  710. var float RecoilPitchPercentage;
  711. /** Used to track how close the recoil is to the edge of the buffer */
  712. var float RecoilYawPercentage;
  713.  
  714. // Internal recoil vars used by the native code
  715. /** Stores recoil when suppression is recieved */
  716. var rotator SuppressRecoilRotator;
  717. /** Remaining time to process suppression recoil */
  718. var float SuppressRecoilTimeLeft;
  719.  
  720. /** The amount of time it will take to process this suppression recoil */
  721. var(Recoil) float SuppressRecoilSpeed;
  722. /** The percentage of the suppression recoil to apply to the player's view */
  723. var(Recoil) float SuppressRecoilViewRotationScale;
  724.  
  725. // Note on Recoil modifiers: Total recoil modifier will be the weapon posture modifier multiplied by the player stance modifier.
  726. // Default recoil is based on a standing player in ironsights position (not moving), or bipod deployed position for weapons
  727. // with bipods
  728.  
  729. /** Recoil modifier for when the player is holding the weapon in the hipped position */
  730. var(Recoil) float HippedRecoilModifier;
  731. /** Recoil modifier for when the player is jogging and shooting */
  732. var(Recoil) float JoggingRecoilModifier;
  733. /** Recoil modifier for when the player is walking and shooting */
  734. var(Recoil) float WalkingRecoilModifier;
  735. /** Recoil modifier for falling player stance */
  736. var(Recoil) float FallingRecoilModifier;
  737. /** Recoil modifier for crouched */
  738. var(Recoil) float StanceCrouchedRecoilModifier;
  739. /** Stores the last calculated recoil modifier */
  740. var float LastRecoilModifier;
  741.  
  742. /** Used to compensate for weapons that have an ironsight mesh FOV that is different
  743. * Than the player's world FOV. When these mismatch, it causes the recoil
  744. * rotation to mismatch as well. Use this value to get them back in sync.
  745. *
  746. */
  747. var(Recoil) float IronSightMeshFOVCompensationScale;
  748.  
  749. /*********************************************************************************************
  750. * @name Clientside hit detection
  751. ********************************************************************************************* */
  752.  
  753. /** Compressed ImpactInfo more suitable for server function replication */
  754. struct native ImpactRepInfo
  755. {
  756. var Actor HitActor;
  757. var vector HitLocation;
  758. var vector RayDir;
  759.  
  760. // BoneName --- Used by KFPawns for hit zone detection. Each zone name must be
  761. // added to MAX_NETWORKED_HARDCODED_NAME otherwise it will be replicated as a string.
  762. var name HitInfo_BoneName;
  763.  
  764. // HitComp --- Used for KActorFromStatic, but since the engine does not
  765. // replicate we only lose remote KActors playing on the listen server.
  766. };
  767.  
  768. /*********************************************************************************************
  769. * @name Perks
  770. ********************************************************************************************* */
  771. /** Is this perk backup weapon? */
  772. var bool bIsBackupWeapon;
  773. var(Weapon) protected array< Class<KFPerk> > AssociatedPerkClasses;
  774. /*********************************************************************************************
  775. * @name Debug
  776. ********************************************************************************************* */
  777.  
  778. // `log conditions. More efficient than using log tags, because the msg is not evaluated.
  779. var config bool bLogAnimation;
  780. var config bool bLogStates;
  781. /** If set, this weapon will pause with the PlayersOnly command */
  782. var bool bPauseWithPlayersOnly;
  783.  
  784. /** If set, this weapon will pause with the PlayersOnly command */
  785. var bool bDebugRecoilPosition;
  786.  
  787. /** temp ammo logging */
  788. var config bool bLogAmmo;
  789.  
  790. cpptext
  791. {
  792. // Actor
  793. INT* GetOptimizedRepList( BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map, UActorChannel* Channel );
  794. virtual void TickSpecial(FLOAT DeltaSeconds);
  795. virtual UBOOL PlayerControlled();
  796. virtual void PreBeginPlay();
  797.  
  798. // Called from TickSpecial
  799. void TickIronSightsZoom(FLOAT DeltaSeconds);
  800. void UpdateTimeDilation();
  801.  
  802. // Called from PC.ProcessViewRotation
  803. void ProcessRecoil(class APlayerController* PC, FLOAT DeltaTime, FRotator& DeltaRot);
  804. void ProcessWeaponLag(class AKFPawn* KFP, FLOAT DeltaTime, FRotator& DeltaRot);
  805. void ProcessSway(class APlayerController* PC, float DeltaTime);
  806.  
  807. /* Returns the size of the object/ resource for display to artists/ LDs in the Editor.*/
  808. int GetResourceSize();
  809. /* Manually grabs the important resources inside KFWeapon and returns their total size.*/
  810. int GetWeaponResourceSize( TArray<class UObject*>& InMemoryArray, UBOOL bUniqueItemsOnly );
  811. }
  812.  
  813. replication
  814. {
  815. if (bNetDirty && (bNetInitial || !bAllowClientAmmoTracking) )
  816. AmmoCount;
  817. if (bNetDirty)
  818. SpareAmmoCount, MagazineCapacity, SpareAmmoCapacity, bGivenAtStart;
  819. }
  820.  
  821. /*********************************************************************************************
  822. * @name Native helper functions
  823. ********************************************************************************************* */
  824.  
  825. /** Util that makes sure the overlay component is last in the AllComponents/Components array. */
  826. native function EnsureWeaponOverlayComponentLast();
  827.  
  828. /**
  829. * Handles view rotation and weapon rotation changes for the weapon. Called in script by
  830. * the KFPlayerController holding this weapon. Currently handles free-aim and recoil.
  831. *
  832. * @param PC PlayerController whose pawn is holding this weapon
  833. * @param DeltaTime Amount of time that has passed since the last update
  834. * @param DeltaRot The view rotational delta the PC is attempting to perform
  835. */
  836. native function WeaponProcessViewRotation(PlayerController PC, float DeltaTime, out Rotator DeltaRot);
  837.  
  838. /** Queries the pawn and returns our current Perk */
  839. native function KFPerk GetPerk() const;
  840.  
  841. /** 0: Normal zed time, 1: Ignore zed time */
  842. native function SetZedTimeResist(float ResistPct);
  843. /** Zero out current zed time resitances */
  844. native function ClearZedTimeResist();
  845. /** Allow weapons with abnormal state transitions to always use zed time resist*/
  846. simulated function bool HasAlwaysOnZedTimeResist(){return false;}
  847.  
  848. /*********************************************************************************************
  849. * @name Constructors, Destructors, and Loading
  850. ********************************************************************************************* */
  851.  
  852. /**
  853. * Called immediately before gameplay begins.
  854. */
  855. simulated event PreBeginPlay()
  856. {
  857. Super.PreBeginPlay();
  858.  
  859. MySkelMesh = KFSkeletalMeshComponent(Mesh);
  860. if ( MySkelMesh == None )
  861. {
  862. `warn("A Invalid KFSkeletalMeshComponent(Mesh) cast!!!");
  863. }
  864.  
  865. // Cache AnimNodeSeq (no AnimTree version)
  866. WeaponAnimSeqNode = KFAnimSeq_Tween( GetWeaponAnimNodeSeq() );
  867.  
  868. InitializeAmmo();
  869. InitializeEquipTime();
  870.  
  871. // Set up the recoil blend out parameters
  872. if( RecoilRate > 0 && RecoilBlendOutRatio > 0 )
  873. {
  874. RecoilYawBlendOutRate = maxRecoilYaw/RecoilRate * RecoilBlendOutRatio;
  875. RecoilPitchBlendOutRate = maxRecoilPitch/RecoilRate * RecoilBlendOutRatio;
  876. }
  877.  
  878. if( bHasLaserSight )
  879. {
  880. AttachLaserSight();
  881. }
  882. }
  883.  
  884. function SetShownInInventory(bool bValue);
  885.  
  886. /** Cache Anim Nodes from the tree
  887. * @note: skipped on server because AttachComponent/AttachWeaponTo is not called
  888. */
  889. simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
  890. {
  891. WeaponAnimSeqNode = KFAnimSeq_Tween(SkelComp.FindAnimNode('WeaponSeq'));
  892. // If we're not using a NodeName, try default anim node
  893. if ( WeaponAnimSeqNode == None )
  894. {
  895. WeaponAnimSeqNode = KFAnimSeq_Tween( GetWeaponAnimNodeSeq() );
  896. }
  897.  
  898. IdleBobBlendNode = AnimNodeAdditiveBlending(SkelComp.FindAnimNode('IdleBobAdditiveBlend'));
  899. ToggleAdditiveBobAnim(false, 0.f);
  900.  
  901. EmptyMagBlendNode = AnimNodeBlendPerBone(SkelComp.FindAnimNode('EmptyMagBlend'));
  902. if( EmptyMagBlendNode != none && BonesToLockOnEmpty.Length > 0 )
  903. {
  904. BuildEmptyMagNodeWeightList( EmptyMagBlendNode, BonesToLockOnEmpty );
  905. }
  906. }
  907.  
  908. /** Rebuilds weight list for "empty mag" bolt lock node after updating its target bones */
  909. native function BuildEmptyMagNodeWeightList( AnimNodeBlendPerBone EmptyNode, const out array<name> BonesToLock );
  910.  
  911. /** Setup the time it takes before the equip anim can be interrupted */
  912. simulated function InitializeEquipTime()
  913. {
  914. EquipTime = EquipTime>0 ? EquipTime : 0.01;
  915. PutDownTime = PutDownTime>0 ? PutDownTime : 0.01;
  916.  
  917. if ( bUseAnimLenEquipTime )
  918. {
  919. EquipTime = MySkelMesh.GetAnimInterruptTime(EquipAnim);
  920. PutDownTime = MySkelMesh.GetAnimLength(PutDownAnim);
  921. }
  922. }
  923.  
  924. /**
  925. * Initialize the FOV settings for this weapon, adjusting for aspect ratio
  926. * @param SizeX the X resolution of the screen
  927. * @param SizeY the Y resolution of the screen
  928. * @param DefaultPlayerFOV the default player FOV of the player holding this weapon
  929. * @param DefaultPlayerFOV the PlayerController of the player holding this weapon
  930. */
  931. simulated function InitFOV(float SizeX, float SizeY, float DefaultPlayerFOV)
  932. {
  933. local float DummyParam;
  934.  
  935. MeshFOV = class'KFPlayerController'.static.CalcFOVForAspectRatio(default.MeshFOV, SizeX, SizeY, DummyParam);
  936. MeshIronSightFOV = class'KFPlayerController'.static.CalcFOVForAspectRatio(default.MeshIronSightFOV, SizeX, SizeY, DummyParam);
  937. PlayerIronSightFOV = class'KFPlayerController'.static.CalcFOVForAspectRatio(default.PlayerIronSightFOV, SizeX, SizeY, DummyParam);
  938. PlayerSprintFOV = class'KFPlayerController'.static.CalcFOVForAspectRatio(default.PlayerSprintFOV, SizeX, SizeY, DummyParam);
  939.  
  940. if( DefaultPlayerFOV > PlayerSprintFOV )
  941. {
  942. PlayerSprintFOV = DefaultPlayerFOV;
  943. }
  944.  
  945. if( bUsingSights )
  946. {
  947. SetFOV(MeshIronSightFOV);
  948. }
  949. else
  950. {
  951. SetFOV(MeshFOV);
  952. }
  953.  
  954. `log("PlayerIronSightFOV = "$PlayerIronSightFOV$" default POV = "$default.PlayerIronSightFOV, bLogAnimation);
  955. `log("SprintFOV = "$PlayerSprintFOV$" default POV = "$default.PlayerSprintFOV, bLogAnimation);
  956. }
  957.  
  958. /*********************************************************************************************
  959. * @name Inventory & Weapon Switching
  960. *********************************************************************************************/
  961.  
  962. /** Weapon skins */
  963. native reliable client private function ClientSetFirstPersonSkin(int ItemId);
  964. native reliable server private event ServerUpdateWeaponSkin(int ItemId);
  965. native private function ClearSkinItemId();
  966.  
  967. /**
  968. * This Inventory Item has just been given to this Pawn
  969. * (server only)
  970. */
  971. function GivenTo( Pawn thisPawn, optional bool bDoNotActivate )
  972. {
  973. Super.GivenTo(thisPawn, bDoNotActivate);
  974.  
  975. // need to reset skin and wait for ServerUpdateWeaponSkin
  976. if ( !Instigator.IsLocallyControlled() )
  977. {
  978. ClearSkinItemId();
  979. }
  980.  
  981. KFInventoryManager(InvManager).AddCurrentCarryBlocks( InventorySize );
  982. KFPawn(Instigator).NotifyInventoryWeightChanged();
  983. }
  984.  
  985. /**
  986. * A notification call when this weapon is removed from the Inventory of a pawn
  987. * @see Inventory::ItemRemovedFromInvManager
  988. */
  989. function ItemRemovedFromInvManager()
  990. {
  991. Super.ItemRemovedFromInvManager();
  992.  
  993. KFInventoryManager(InvManager).AddCurrentCarryBlocks( -InventorySize );
  994.  
  995. KFPawn(Instigator).NotifyInventoryWeightChanged();
  996. }
  997.  
  998. reliable client function ClientWeaponSet(bool bOptionalSet, optional bool bDoNotActivate)
  999. {
  1000. local PlayerController PC;
  1001. local int i;
  1002.  
  1003. // This is the first time we have a valid Instigator (see PendingClientWeaponSet)
  1004. if ( Instigator != None && InvManager != None
  1005. && WorldInfo.NetMode != NM_DedicatedServer )
  1006. {
  1007. PC = PlayerController(Instigator.Controller);
  1008. if( PC != none && PC.myHUD != none )
  1009. {
  1010. InitFOV(PC.myHUD.SizeX, PC.myHUD.SizeY, PC.DefaultFOV);
  1011. }
  1012.  
  1013. // One-time skin initialization on local player
  1014. if ( SkinItemId > 0 )
  1015. {
  1016. ClientSetFirstPersonSkin(SkinItemId);
  1017. }
  1018.  
  1019. // Weapon MICs for blood maps
  1020. for( i = 0; i < NumBloodMapMaterials; ++i )
  1021. {
  1022. WeaponMICs.AddItem( Mesh.CreateAndSetMaterialInstanceConstant(i) );
  1023. }
  1024. }
  1025.  
  1026. Super.ClientWeaponSet(bOptionalSet, bDoNotActivate);
  1027. }
  1028.  
  1029. /**
  1030. * Attach Weapon Mesh, Weapon MuzzleFlash and Muzzle Flash Dynamic Light to a SkeletalMesh
  1031. *
  1032. * @param who is the pawn to attach to
  1033. */
  1034. simulated function AttachWeaponTo( SkeletalMeshComponent MeshCpnt, optional Name SocketName )
  1035. {
  1036. local KFPawn KFP;
  1037. local int i;
  1038.  
  1039. KFP = KFPawn(Instigator);
  1040. if( KFP != none && KFP.ArmsMesh != none )
  1041. {
  1042. KFP.ArmsMesh.SetParentAnimComponent(MySkelMesh);
  1043. KFP.ArmsMesh.SetFOV(MySkelMesh.FOV);
  1044. for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; i++)
  1045. {
  1046. if (KFP.FirstPersonAttachments[i] != none)
  1047. {
  1048. if (SkeletalMeshComponent(KFP.FirstPersonAttachments[i]) != none)
  1049. {
  1050. SkeletalMeshComponent(KFP.FirstPersonAttachments[i]).SetParentAnimComponent(MySkelMesh);
  1051. SkeletalMeshComponent(KFP.FirstPersonAttachments[i]).SetLODParent(MySkelMesh);
  1052. }
  1053.  
  1054. if (KFSkeletalMeshComponent(KFP.FirstPersonAttachments[i]) != none)
  1055. {
  1056. KFSkeletalMeshComponent(KFP.FirstPersonAttachments[i]).SetFOV(MySkelMesh.FOV);
  1057. }
  1058. }
  1059. }
  1060. }
  1061.  
  1062. // Attach 1st Person Muzzle Flashes, etc,
  1063. if ( Instigator.IsFirstPerson() )
  1064. {
  1065. // Toggle preshadows before attaching component (depending on graphics settings)
  1066. if( KFP.AllowFirstPersonPreshadows() )
  1067. {
  1068. Mesh.bAllowPerObjectShadows = true;
  1069. }
  1070. else
  1071. {
  1072. Mesh.bAllowPerObjectShadows = false;
  1073. }
  1074.  
  1075. AttachComponent(Mesh);
  1076. EnsureWeaponOverlayComponentLast();
  1077. SetHidden(True);
  1078. bPendingShow = TRUE;
  1079.  
  1080. // Set the weapon to use the Instigator pawn's lighting channel
  1081. if( KFP != none )
  1082. {
  1083. SetMeshLightingChannels(KFP.PawnLightingChannel);
  1084. if( KFP.ArmsMesh != none )
  1085. {
  1086. Mesh.SetShadowParent(KFP.ArmsMesh);
  1087. // Reattach our arms mesh at the same time as our weapon to avoid them ticking and popping on screen early
  1088. AttachComponent(KFP.ArmsMesh);
  1089.  
  1090. // Reattach the arms cosmetics to the weapon.
  1091. for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; i++)
  1092. {
  1093. if (KFP.FirstPersonAttachments[i] != none)
  1094. {
  1095. AttachComponent(KFP.FirstPersonAttachments[i]);
  1096. }
  1097. }
  1098. }
  1099. }
  1100. }
  1101. // Attach the weapon on the server too, otherwise the hipped free-aim stuff won't work (recoil) and attaching flamethrower stuff messes up
  1102. else if ( bWeaponNeedsServerPosition && (WorldInfo.NetMode == NM_DedicatedServer || (WorldInfo.NetMode == NM_ListenServer && !Instigator.IsLocallyControlled())) )
  1103. {
  1104. AttachComponent(Mesh);
  1105. EnsureWeaponOverlayComponentLast();
  1106. SetHidden(True);
  1107. bPendingShow = TRUE;
  1108. }
  1109. else
  1110. {
  1111. SetHidden(True);
  1112. if (KFP != None)
  1113. {
  1114. KFP.ArmsMesh.SetHidden(true);
  1115. for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; i++)
  1116. {
  1117. if (KFP.FirstPersonAttachments[i] != none)
  1118. {
  1119. KFP.FirstPersonAttachments[i].SetHidden(true);
  1120. }
  1121. }
  1122. }
  1123. }
  1124.  
  1125. // Assign/spawn the 3rd Person Attachment
  1126. if (KFP != None)
  1127. {
  1128. AttachThirdPersonWeapon(KFP);
  1129. }
  1130. }
  1131.  
  1132. /** Assign a new 3rd person weapon attachment */
  1133. function AttachThirdPersonWeapon(KFPawn P)
  1134. {
  1135. // Create weapon attachment (server only)
  1136. if ( Role == ROLE_Authority )
  1137. {
  1138. P.WeaponAttachmentTemplate = AttachmentArchetype;
  1139.  
  1140. // Assign replicated 3rd person skin (local & server)
  1141. if (P.IsHumanControlled())
  1142. {
  1143. ServerUpdateWeaponSkin(SkinItemId);
  1144. }
  1145.  
  1146. if ( WorldInfo.NetMode != NM_DedicatedServer )
  1147. {
  1148. P.WeaponAttachmentChanged();
  1149. }
  1150. }
  1151. }
  1152.  
  1153. /**
  1154. * Detach weapon from skeletal mesh
  1155. *
  1156. * @param SkeletalMeshComponent weapon is attached to.
  1157. */
  1158. simulated function DetachWeapon()
  1159. {
  1160. local KFPawn KFP;
  1161. local int i;
  1162.  
  1163. DetachComponent( Mesh );
  1164. if (OverlayMesh != None)
  1165. {
  1166. DetachComponent(OverlayMesh);
  1167. }
  1168.  
  1169. KFP = KFPawn(Instigator);
  1170. if (Role == ROLE_Authority && KFP != None)
  1171. {
  1172. if ( KFP.WeaponAttachmentTemplate == AttachmentArchetype )
  1173. {
  1174. KFP.WeaponAttachmentTemplate = None;
  1175. if ( WorldInfo.NetMode != NM_DedicatedServer )
  1176. {
  1177. KFP.WeaponAttachmentChanged();
  1178. }
  1179. }
  1180. }
  1181.  
  1182. if( KFP != none && KFP.ArmsMesh != none )
  1183. {
  1184. // Detach our arms mesh at the same time as our weapon to avoid them ticking and popping on screen early
  1185. DetachComponent(KFP.ArmsMesh);
  1186. for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; i++)
  1187. {
  1188. if (KFP.FirstPersonAttachments[i] != none)
  1189. {
  1190. DetachComponent(KFP.FirstPersonAttachments[i]);
  1191. }
  1192. }
  1193. }
  1194.  
  1195. SetBase(None);
  1196. SetHidden(True);
  1197. DetachMuzzleFlash();
  1198. }
  1199.  
  1200. /**
  1201. * Remove/Detach the muzzle flash components
  1202. */
  1203. simulated function DetachMuzzleFlash()
  1204. {
  1205. if (MySkelMesh != none && MuzzleFlash != None)
  1206. {
  1207. MuzzleFlash.DetachMuzzleFlash(MySkelMesh);
  1208. MuzzleFlash = None;
  1209. }
  1210. }
  1211.  
  1212. simulated function AttachLaserSight()
  1213. {
  1214. if( WorldInfo.NetMode == NM_DedicatedServer )
  1215. {
  1216. return;
  1217. }
  1218.  
  1219. if ( MySkelMesh != none && LaserSight == None && bHasLaserSight )
  1220. {
  1221. LaserSight = new(self) Class'KFLaserSightAttachment' (LaserSightTemplate);
  1222. LaserSight.AttachLaserSight(MySkelMesh, true);
  1223. }
  1224. }
  1225.  
  1226. /**
  1227. * Drop this item out in to the world
  1228. */
  1229. function DropFrom(vector StartLocation, vector StartVelocity)
  1230. {
  1231. local DroppedPickup P;
  1232.  
  1233. // Offset spawn closer to eye location
  1234. StartLocation.Z += Instigator.BaseEyeHeight / 2;
  1235.  
  1236. // for some reason, Inventory::DropFrom removes weapon from inventory whether it was able to spawn the pickup or not.
  1237. // we only want the weapon removed from inventory if pickup was successfully spawned, so instead of calling the supers,
  1238. // do all the super functionality here.
  1239.  
  1240. if( !CanThrow() )
  1241. {
  1242. return;
  1243. }
  1244.  
  1245. if( DroppedPickupClass == None || DroppedPickupMesh == None )
  1246. {
  1247. Destroy();
  1248. return;
  1249. }
  1250.  
  1251. // the last bool param is to prevent collision from preventing spawns
  1252. P = Spawn(DroppedPickupClass,,, StartLocation,,,true);
  1253. if( P == None )
  1254. {
  1255. // if we can't spawn the pickup (likely for collision reasons),
  1256. // just return without removing from inventory or destroying, which removes from inventory
  1257. PlayerController(Instigator.Controller).ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_FailedDropInventory );
  1258. return;
  1259. }
  1260.  
  1261. if( Instigator != None && Instigator.InvManager != None )
  1262. {
  1263. Instigator.InvManager.RemoveFromInventory(Self);
  1264.  
  1265. if( Instigator.IsAliveAndWell() && !Instigator.InvManager.bPendingDelete )
  1266. {
  1267. `DialogManager.PlayDropWeaponDialog( KFPawn(Instigator) );
  1268. }
  1269. }
  1270.  
  1271. SetupDroppedPickup( P, StartVelocity );
  1272.  
  1273. Instigator = None;
  1274. GotoState('');
  1275.  
  1276. AIController = None;
  1277. }
  1278.  
  1279. /** Sets up pickup. Allows subclasses to make adjustments (most notably dualbase) */
  1280. function SetupDroppedPickup( out DroppedPickup P, vector StartVelocity )
  1281. {
  1282. P.SetPhysics(PHYS_Falling);
  1283. P.Inventory = self;
  1284. P.InventoryClass = class;
  1285. P.Velocity = StartVelocity;
  1286. P.Instigator = Instigator;
  1287. P.SetPickupMesh(DroppedPickupMesh);
  1288. P.SetPickupParticles(DroppedPickupParticles);
  1289. }
  1290.  
  1291. /** Allows pickup to update weapon properties */
  1292. function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon )
  1293. {
  1294. local byte i;
  1295. local KFWeapon KFWInv;
  1296.  
  1297. for (i = 0; i < 2; i++)
  1298. {
  1299. AmmoCount[i] = PickedUpWeapon.AmmoCount[i];
  1300. }
  1301.  
  1302. SpareAmmoCount[0] = PickedUpWeapon.SpareAmmoCount[0];
  1303.  
  1304. // Duals are created when a dropped single is picked up. If this weapon is a single and we already have the dual version,
  1305. // that means we just created the dual from the single. So, since the dual we just created has default ammo, subtract
  1306. // whatever is missing from the single from the dual.
  1307. if( DualClass != none )
  1308. {
  1309. foreach KFInventoryManager(InvManager).InventoryActors( class'KFWeapon', KFWInv )
  1310. {
  1311. if( KFWInv.Class == DualClass )
  1312. {
  1313. KFWInv.AmmoCount[0] -= default.MagazineCapacity[0] - AmmoCount[0];
  1314. KFWInv.AmmoCount[1] -= default.MagazineCapacity[1] - AmmoCount[1];
  1315.  
  1316. KFWInv.SpareAmmoCount[0] -= (default.InitialSpareMags[0] * default.MagazineCapacity[0]) - SpareAmmoCount[0];
  1317. KFWInv.SpareAmmoCount[0] = Min( KFWInv.SpareAmmoCount[0], KFWInv.SpareAmmoCapacity[0] );
  1318.  
  1319. KFWInv.ClientForceAmmoUpdate(KFWInv.AmmoCount[0],KFWInv.SpareAmmoCount[0]);
  1320. KFWInv.ClientForceSecondaryAmmoUpdate(KFWInv.AmmoCount[1]);
  1321.  
  1322. KFWInv.bGivenAtStart = PickedUpWeapon.bGivenAtStart;
  1323.  
  1324. return;
  1325. }
  1326. }
  1327. }
  1328.  
  1329. // assign weapon skin from the pickup mesh
  1330. if ( PickedUpWeapon.SkinItemId > 0 )
  1331. {
  1332. ClientSetFirstPersonSkin(PickedUpWeapon.SkinItemId);
  1333. }
  1334.  
  1335. ClientForceAmmoUpdate( AmmoCount[0],SpareAmmoCount[0] );
  1336. ClientForceSecondaryAmmoUpdate( AmmoCount[1] );
  1337.  
  1338. bGivenAtStart = PickedUpWeapon.bGivenAtStart;
  1339. }
  1340.  
  1341. /**
  1342. * When you pickup a weapon, the inventory system has a chance to restrict the pickup.
  1343. */
  1344. function bool DenyPickupQuery(class<Inventory> ItemClass, Actor Pickup)
  1345. {
  1346. local bool bDenyPickUp;
  1347. local KFPlayerController KFPC;
  1348.  
  1349. if( ItemClass == class )
  1350. {
  1351. // Unless ammo is full, allow the pickup to handle giving ammo
  1352. // @note: projectile pickups can only refill primary ammo
  1353. if( CanRefillSecondaryAmmo() && !Pickup.IsA('Projectile') )
  1354. {
  1355. bDenyPickUp =((SpareAmmoCount[0] + MagazineCapacity[0]) >= GetMaxAmmoAmount(0) && AmmoCount[1] >= MagazineCapacity[1]);
  1356. }
  1357. else
  1358. {
  1359. bDenyPickUp = ((SpareAmmoCount[0] + MagazineCapacity[0]) >= GetMaxAmmoAmount(0));
  1360. }
  1361.  
  1362. if(bDenyPickUp)
  1363. {
  1364. KFPC = KFPlayerController(Instigator.Controller);
  1365. //show non critical message for deny pickup
  1366. if ( KFPC != None )
  1367. {
  1368. KFPC.ReceiveLocalizedMessage(class'KFLocalMessage_Game', (MagazineCapacity[0] == 0) ? GMT_AlreadyCarryingWeapon : GMT_AmmoIsFull);
  1369. }
  1370. }
  1371. }
  1372.  
  1373. return bDenyPickUp;
  1374. }
  1375.  
  1376. function NotifyPickedUp()
  1377. {
  1378. ClientNotifyPickedUp();
  1379. }
  1380.  
  1381. reliable client function ClientNotifyPickedUp()
  1382. {
  1383. local KFPlayerController KFPC;
  1384. local KFGFxMenu_Trader TraderMenu;
  1385.  
  1386. // tell the trader we picked up a new weapon so it can update its display
  1387. KFPC = KFPlayerController(Instigator.Controller);
  1388. if( KFPC != none )
  1389. {
  1390. if( KFPC.MyGFxManager != none )
  1391. {
  1392. TraderMenu = KFGFxMenu_Trader(KFPC.MyGFxManager.CurrentMenu);
  1393. if( TraderMenu != none )
  1394. {
  1395. TraderMenu.GiveExternalWeapon( self );
  1396. }
  1397. }
  1398. }
  1399. }
  1400.  
  1401. /** Treat as non-standard equipment item for */
  1402. simulated static function bool DenyPerkResupply()
  1403. {
  1404. return default.InventoryGroup >= IG_Equipment;
  1405. }
  1406.  
  1407. simulated static function bool IsMeleeWeapon()
  1408. {
  1409. return default.bMeleeWeapon;
  1410. }
  1411.  
  1412. /**
  1413. * Returns a weight reflecting the desire to use the
  1414. * given weapon, used for AI and player best weapon
  1415. * selection.
  1416. *
  1417. * @return weapon rating (range -1.f to 1.f)
  1418. *
  1419. * Overridden to use new GroupPriority
  1420. */
  1421. simulated function float GetWeaponRating()
  1422. {
  1423. if (!Instigator.IsHumanControlled() || !HasAnyAmmo())
  1424. {
  1425. return super.GetWeaponRating();
  1426. }
  1427.  
  1428. return GroupPriority;
  1429. }
  1430.  
  1431. /*********************************************************************************************
  1432. * @name Tick/Update
  1433. *********************************************************************************************/
  1434.  
  1435. simulated event Tick( float DeltaTime )
  1436. {
  1437. if( LaserSight != None )
  1438. {
  1439. LaserSight.Update(DeltaTime, self);
  1440. }
  1441. }
  1442.  
  1443. /*********************************************************************************************
  1444. * @name Iron Sights / Zoom
  1445. *********************************************************************************************/
  1446.  
  1447. /**
  1448. * Adjust the FOV for the first person weapon and arms.
  1449. */
  1450. simulated event SetFOV( float NewFOV )
  1451. {
  1452. local KFPawn KFP;
  1453. local int i;
  1454.  
  1455. if ( MySkelMesh != none)
  1456. {
  1457. MySkelMesh.SetFOV(NewFOV);
  1458. }
  1459.  
  1460. if( MuzzleFlash != none )
  1461. {
  1462. MuzzleFlash.SetFOV(NewFOV);
  1463. }
  1464.  
  1465. // if the first person arms are attached to this gun update those too
  1466. KFP = KFPawn(Instigator);
  1467. if( KFP != none && KFP.ArmsMesh.ParentAnimComponent == MySkelMesh )
  1468. {
  1469. if( KFP.ArmsMesh != none )
  1470. {
  1471. KFP.ArmsMesh.SetFOV(NewFOV);
  1472. for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; i++)
  1473. {
  1474. if (KFP.FirstPersonAttachments[i] != none)
  1475. {
  1476. if (KFSkeletalMeshComponent(KFP.FirstPersonAttachments[i]) != none)
  1477. {
  1478. KFSkeletalMeshComponent(KFP.FirstPersonAttachments[i]).SetFOV(NewFOV);
  1479. }
  1480. }
  1481. }
  1482. }
  1483. }
  1484.  
  1485. // Set the FOV of the Laser Sight
  1486. if( bHasLaserSight && LaserSight != none )
  1487. {
  1488. LaserSight.SetMeshFOV( NewFOV );
  1489. }
  1490. }
  1491.  
  1492. /**
  1493. * Toggle iron sights aiming
  1494. */
  1495. simulated function SetIronSights(bool bNewIronSights)
  1496. {
  1497. if ( bUsingSights == bNewIronSights )
  1498. {
  1499. return;
  1500. }
  1501.  
  1502. if ( !Instigator.IsLocallyControlled() )
  1503. {
  1504. return;
  1505. }
  1506.  
  1507. if( bUsingSights )
  1508. {
  1509. PerformZoom(false);
  1510. }
  1511. else if( AllowIronSights() )
  1512. {
  1513. PerformZoom(true);
  1514. }
  1515. }
  1516.  
  1517. /**
  1518. * Return true if the weapon can transition to iron sights. Overriden in
  1519. * states where we want to prevent ironsight transitions
  1520. */
  1521. simulated function bool AllowIronSights()
  1522. {
  1523. if( !bHasIronSights )
  1524. {
  1525. return false;
  1526. }
  1527.  
  1528. return true;
  1529. }
  1530.  
  1531. /**
  1532. * Enables or disables depth of field as part of the iron sights effect
  1533. */
  1534. simulated function EnableIronSightsDoF(bool bEnableDoF)
  1535. {
  1536. local KFPlayerController PC;
  1537.  
  1538. // Toggle depth of field
  1539. if(Instigator != none)
  1540. {
  1541. PC = KFPlayerController(Instigator.Controller);
  1542. if(PC != none)
  1543. {
  1544. PC.EnableIronSights(bEnableDoF);
  1545. PC.EnableDepthOfField(bEnableDoF);
  1546. }
  1547. }
  1548. }
  1549.  
  1550. /**
  1551. * Enables or disables player zoom as part of the iron sights effect
  1552. */
  1553. simulated function EnablePlayerZoom(bool bEnableZoom)
  1554. {
  1555. local KFPlayerController PC;
  1556.  
  1557. // Toggle depth of field
  1558. if(Instigator != none)
  1559. {
  1560. PC = KFPlayerController(Instigator.Controller);
  1561. if(PC != none)
  1562. {
  1563. if(bEnableZoom)
  1564. {
  1565. PC.StartAutoTargeting();
  1566. PC.HandleTransitionFOV(PlayerIronSightFOV, ZoomTime);
  1567. }
  1568. else
  1569. {
  1570. PC.HandleTransitionFOV(PC.DefaultFOV, ZoomTime);
  1571. }
  1572. }
  1573. }
  1574. }
  1575.  
  1576. /**
  1577. * Handles the logic of which zoom functions to call based on if
  1578. * we are a client or a server
  1579. *
  1580. * @param bZoomStatus which direction we are zooming
  1581. * @param bAnimateTransition whether or not to blend/animate the ironsight transition
  1582. */
  1583. simulated function PerformZoom(bool bZoomStatus, optional bool bAnimateTransition = true)
  1584. {
  1585. if( bZoomStatus )
  1586. {
  1587. if( Instigator.Physics == PHYS_Falling )
  1588. return;
  1589.  
  1590. ZoomIn(bAnimateTransition, default.ZoomInTime);
  1591.  
  1592. if( bUsingSights && Role < ROLE_Authority )
  1593. {
  1594. ServerZoomIn(bAnimateTransition);
  1595. }
  1596. }
  1597. else
  1598. {
  1599. ZoomOut(bAnimateTransition, default.ZoomOutTime);
  1600.  
  1601. if( !bUsingSights && Role < ROLE_Authority )
  1602. {
  1603. ServerZoomOut(bAnimateTransition);
  1604. }
  1605. }
  1606. }
  1607.  
  1608. /**
  1609. * Handles all the functionality for zooming in including
  1610. * setting the parameters for the weapon, pawn, and playercontroller
  1611. *
  1612. * @param bAnimateTransition whether or not to animate this zoom transition
  1613. * @param ZoomTimeToGo how much zoom time is left
  1614. */
  1615. simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo)
  1616. {
  1617. if( bAnimateTransition )
  1618. {
  1619. ZoomInTime=ZoomTimeToGo;
  1620.  
  1621. // If the zoom out was interrupted, set the parameters for the native code to interpolate the zoom from the proper position
  1622. if( bZoomingOut )
  1623. {
  1624. bZoomingOut=false;
  1625. // Flag so the native code knows the zoom was interupted
  1626. bZoomOutInterrupted=true;
  1627. // Set the zoom time relative to how far along we were when zooming out
  1628. ZoomTime=ZoomInTime - ZoomTime;
  1629. // Let the native code know where/when the zoom was interrupted
  1630. ZoomPartialTime=ZoomTime;
  1631. ZoomStartOffset=PlayerViewOffset;
  1632. ZoomRotStartOffset=ZoomRotInterp;
  1633. }
  1634. else
  1635. {
  1636. ZoomTime=ZoomInTime;
  1637. ZoomStartOffset=PlayerViewOffset;
  1638. }
  1639.  
  1640. // Choose which zoom target to use. If we have a weapon with special sights customize here.
  1641. if( bHasScopePosition && bUsingScopePosition )
  1642. {
  1643. ZoomTargetOffset=ScopePosition;
  1644. }
  1645. else
  1646. {
  1647. ZoomTargetOffset=IronSightPosition;
  1648. }
  1649.  
  1650. if( MySkelMesh != none )
  1651. {
  1652. ZoomWeaponFOVStart=MySkelMesh.FOV;
  1653. }
  1654. else
  1655. {
  1656. ZoomWeaponFOVStart=MeshFOV;
  1657. }
  1658.  
  1659. bZoomingIn=true;
  1660. }
  1661.  
  1662. if( Instigator != none && Instigator.IsLocallyControlled() )
  1663. {
  1664. EnablePlayerZoom(true);
  1665. EnableIronSightsDoF(true);
  1666.  
  1667. `DialogManager.PlayIronsightsDialog( KFPawn(Instigator) );
  1668. }
  1669.  
  1670. // Start timer to check if AI should be warned
  1671. if( bWarnAIWhenAiming && Instigator != none && WorldInfo.NetMode != NM_Client )
  1672. {
  1673. LastAimWarningTime = 0.f;
  1674. CurrentAimSettledTime = 0.f;
  1675. LastAimRotation = GetAdjustedAim( Instigator.GetWeaponStartTraceLocation() );
  1676. SetTimer( 0.1f, true, nameOf(Timer_CheckForAIWarning) );
  1677. }
  1678.  
  1679. bUsingSights = true;
  1680. }
  1681.  
  1682. /**
  1683. * Handles calling the zoom in function on the server
  1684. *
  1685. * @param bAnimateTransition whether or not to animate this zoom transition
  1686. */
  1687. reliable server private function ServerZoomIn(bool bAnimateTransition)
  1688. {
  1689. ZoomIn(bAnimateTransition, default.ZoomInTime);
  1690. }
  1691.  
  1692. /**
  1693. * Handles all the functionality for zooming out including
  1694. * setting the parameters for the weapon, pawn, and playercontroller
  1695. *
  1696. * @param bAnimateTransition whether or not to animate this zoom transition
  1697. * @param ZoomTimeToGo how much zoom time is left
  1698. */
  1699. simulated function ZoomOut(bool bAnimateTransition, float ZoomTimeToGo)
  1700. {
  1701. if( bAnimateTransition )
  1702. {
  1703. ZoomOutTime=ZoomTimeToGo;
  1704.  
  1705. // If the zoom in was interrupted, set the parameters for the native code to interpolate the zoom from the proper position
  1706. if( bZoomingIn )
  1707. {
  1708. bZoomingIn=false;
  1709. // Flag so the native code knows the zoom was interupted
  1710. bZoomInInterrupted=true;
  1711. // Set the zoom time relative to how far along we were when zooming in
  1712. ZoomTime=ZoomOutTime - ZoomTime;
  1713. // Let the native code know where/when the zoom was interrupted
  1714. ZoomPartialTime=ZoomTime;
  1715. ZoomStartOffset=PlayerViewOffset;
  1716. ZoomRotStartOffset=ZoomRotInterp;
  1717. }
  1718. else
  1719. {
  1720. ZoomTime=ZoomOutTime;
  1721. ZoomStartOffset=PlayerViewOffset;
  1722. }
  1723. bZoomingOut=true;
  1724. }
  1725. else
  1726. {
  1727. // do a fast zoomout with no notifies
  1728. bFastZoomOut = true;
  1729.  
  1730. // If the zoom in was interrupted, set the parameters for the native code to interpolate the zoom from the proper position
  1731. if( bZoomingIn )
  1732. {
  1733. bZoomingIn=false;
  1734. // Flag so the native code knows the zoom was interupted
  1735. bZoomInInterrupted=true;
  1736. // Set the zoom time relative to how far along we were when zooming in
  1737. ZoomTime=default.ZoomOutTime - ZoomTime;
  1738. // Let the native code know where/when the zoom was interrupted
  1739. ZoomPartialTime=ZoomTime;
  1740. ZoomStartOffset=PlayerViewOffset;
  1741. ZoomRotStartOffset=ZoomRotInterp;
  1742. }
  1743. else
  1744. {
  1745. ZoomTime=FastZoomOutTime;
  1746. ZoomStartOffset=PlayerViewOffset;
  1747. }
  1748. bZoomingOut=true;
  1749.  
  1750. LastZoomOutTime=WorldInfo.TimeSeconds+ZoomTime;
  1751. }
  1752.  
  1753. // Choose which zoom target to use. If we have a weapon with special sights customize here.
  1754. ZoomTargetOffset=default.PlayerViewOffset;
  1755.  
  1756. if( MySkelMesh != none )
  1757. {
  1758. ZoomWeaponFOVStart=MySkelMesh.FOV;
  1759. }
  1760. else
  1761. {
  1762. ZoomWeaponFOVStart=MeshIronSightFOV;
  1763. }
  1764.  
  1765. if( Instigator != none && Instigator.IsLocallyControlled() )
  1766. {
  1767. EnableIronSightsDoF(false);
  1768. EnablePlayerZoom(false);
  1769.  
  1770. `DialogManager.StopBreathingDialog( KFPawn(Instigator) );
  1771. }
  1772.  
  1773. // Stop timer to check if AI should be warned
  1774. if( bWarnAIWhenAiming && WorldInfo.NetMode != NM_Client )
  1775. {
  1776. ClearTimer( nameOf(Timer_CheckForAIWarning) );
  1777. }
  1778.  
  1779. bUsingSights = false;
  1780. }
  1781.  
  1782. /**
  1783. * Handles calling the zoom out function on the server
  1784. *
  1785. * @param bAnimateTransition whether or not to animate this zoom transition
  1786. */
  1787. reliable server private function ServerZoomOut(bool bAnimateTransition)
  1788. {
  1789. ZoomOut(bAnimateTransition, default.ZoomOutTime);
  1790. }
  1791.  
  1792. /**
  1793. * Called by the native code when the interpolation of the first person weapon to the zoomed position finishes
  1794. */
  1795. simulated event OnZoomInFinished()
  1796. {
  1797. // Manually trigger anim end event for idles (zooming is a procedural anim)
  1798. OnAnimEnd(none,0.f,0.f);
  1799. }
  1800.  
  1801. /**
  1802. * Called by the native code when the interpolation of the first person weapon from the zoomed position finishes
  1803. */
  1804. simulated event OnZoomOutFinished()
  1805. {
  1806. // Manually trigger anim end event for idles (zooming is a procedural anim)
  1807. OnAnimEnd(none,0.f,0.f);
  1808. }
  1809.  
  1810. /** Returns TRUE if this weapon is currently warning AI when aiming */
  1811. function bool IsWarningAI()
  1812. {
  1813. return bWarnAIWhenAiming && bUsingSights;
  1814. }
  1815.  
  1816. /** Runs on a loop when zoomed to determine if AI should be warned */
  1817. function Timer_CheckForAIWarning()
  1818. {
  1819. local vector Direction, DangerPoint;
  1820. local vector TraceStart, Projection;
  1821. local rotator NewAimRotation;
  1822. local Pawn P;
  1823. local KFPawn_Monster HitMonster;
  1824.  
  1825. TraceStart = Instigator.GetWeaponStartTraceLocation();
  1826. NewAimRotation = Instigator.GetBaseAimRotation();
  1827. if( RSize(Normalize(LastAimRotation - NewAimRotation)) < 400.f )
  1828. {
  1829. CurrentAimSettledTime += 0.1f;
  1830. }
  1831.  
  1832. // Warn AI if the weapon is off cooldown
  1833. if( CurrentAimSettledTime >= RandRange(AimWarningDelay.X, AimWarningDelay.Y)
  1834. && `TimeSince(LastAimWarningTime) > AimWarningCooldown )
  1835. {
  1836. // Grab our destination test location (trace offset + trace range)
  1837. Direction = vector( NewAimRotation );
  1838.  
  1839. // Trace along a line from Location to TestLocation, warning zeds on its path
  1840. foreach WorldInfo.AllPawns( class'Pawn', P )
  1841. {
  1842. if( P.GetTeamNum() != Instigator.GetTeamNum() && !P.IsHumanControlled() && P.IsAliveAndWell() )
  1843. {
  1844. // Determine if AI is within range as well as within our field of view
  1845. Projection = P.Location - TraceStart;
  1846. if( VSizeSQ(Projection) < MaxAIWarningDistSQ )
  1847. {
  1848. PointDistToLine( P.Location, Direction, TraceStart, DangerPoint );
  1849.  
  1850. if( VSizeSQ(DangerPoint - P.Location) < MaxAIWarningDistFromPointSQ )
  1851. {
  1852. // Tell the AI to evade away from the DangerPoint
  1853. HitMonster = KFPawn_Monster( P );
  1854. if( HitMonster != none && HitMonster.MyKFAIC != None )
  1855. {
  1856. HitMonster.MyKFAIC.ReceiveLocationalWarning( DangerPoint, TraceStart, self );
  1857. }
  1858. }
  1859. }
  1860. }
  1861. }
  1862.  
  1863. // Reset settled time and start cooldown
  1864. LastAimWarningTime = WorldInfo.TimeSeconds;
  1865. CurrentAimSettledTime = 0.f;
  1866. }
  1867.  
  1868. LastAimRotation = NewAimRotation;
  1869. }
  1870.  
  1871. /*********************************************************************************************
  1872. * @name Instigator / movement
  1873. ********************************************************************************************* */
  1874.  
  1875.  
  1876. /** If TRUE, Instigator/Owner moves at walking speed when this weapon is equipped */
  1877. simulated function bool ShouldOwnerWalk()
  1878. {
  1879. return bUsingSights;
  1880. }
  1881.  
  1882. /** If TRUE, the owner of this weapon is susceptible to grapple attacks */
  1883. function bool IsGrappleBlocked(Pawn InstigatedBy)
  1884. {
  1885. return FALSE;
  1886. }
  1887.  
  1888. simulated function SetWeaponSprint(bool bNewSprintStatus)
  1889. {
  1890. if ( bNewSprintStatus )
  1891. {
  1892. if ( bUsingSights )
  1893. {
  1894. SetIronSights( false );
  1895. }
  1896.  
  1897. if ( CurrentFireMode < GetPendingFireLength() && PendingFire(CurrentFireMode) )
  1898. {
  1899. StopFire(CurrentFireMode);
  1900. }
  1901.  
  1902. GotoWeaponSprinting();
  1903. }
  1904. }
  1905.  
  1906. simulated function StopPawnSprint(bool bClearPlayerInput)
  1907. {
  1908. local KFPawn KFP;
  1909. local PlayerController PC;
  1910. local KFPlayerInput Input;
  1911.  
  1912. KFP = KFPawn(Instigator);
  1913. if ( KFP != None )
  1914. {
  1915. KFP.SetSprinting(false);
  1916.  
  1917. if ( KFP.IsLocallyControlled() )
  1918. {
  1919. PC = PlayerController(Instigator.Controller);
  1920. if ( PC != none )
  1921. {
  1922. if( bClearPlayerInput )
  1923. {
  1924. PC.bRun = 0;
  1925. }
  1926.  
  1927. // always stop ExtendedSprint
  1928. Input = KFPlayerInput(PC.PlayerInput);
  1929. if ( Input != None && Input.bExtendedSprinting )
  1930. {
  1931. PC.bRun = 0;
  1932. Input.bExtendedSprinting = false;
  1933. }
  1934. }
  1935. }
  1936. }
  1937. }
  1938.  
  1939. simulated function bool AllowSprinting()
  1940. {
  1941. return true;
  1942. }
  1943.  
  1944. simulated function GotoWeaponSprinting();
  1945.  
  1946. /** Called by Pawn AdjustDamage */
  1947. function AdjustDamage(out int InDamage, class<DamageType> DamageType, Actor DamageCauser);
  1948.  
  1949. /** Called by Pawn PlayTakeHitEffects */
  1950. simulated function PlayTakeHitEffects(vector HitLocation, Actor DamageCauser);
  1951.  
  1952. /*********************************************************************************************
  1953. * @name Animation
  1954. ********************************************************************************************* */
  1955.  
  1956. /**
  1957. * Play the same animation on both the weapon and the arms
  1958. */
  1959. simulated function PlayAnimation(name Sequence, optional float fDesiredDuration, optional bool bLoop, optional float BlendInTime=0.1, optional float BlendOutTime=0.0)
  1960. {
  1961. if ( Sequence == '' || Instigator == None || WeaponAnimSeqNode == none )
  1962. {
  1963. return;
  1964. }
  1965.  
  1966. `log("PlayAnimation Sequence="@Sequence@"Length="@fDesiredDuration@"bLoop="@bLoop, bLogAnimation);
  1967.  
  1968. // Adjust tween node settings before setting the new animation
  1969. if ( Instigator.IsFirstPerson() )
  1970. {
  1971. // Do the blends in the same length of real world time so that things don't
  1972. // end up blending over really long periods of real time and looking wierd - Ramm
  1973. BlendInTime *= WorldInfo.TimeDilation * CustomTimeDilation;
  1974.  
  1975. WeaponAnimSeqNode.SetTweenTime(BlendInTime);
  1976. }
  1977.  
  1978. // Play animation on weapon and arms
  1979. PlayWeaponAnimation(Sequence, fDesiredDuration, bLoop);
  1980.  
  1981. // Go ahead and set up the OnAnimEnd callback in this global, and let the states handle it,
  1982. // otherwise the Active state cuts off any animation that was trying to finish.
  1983. if ( !bLoop )
  1984. {
  1985. if( fDesiredDuration > 0 )
  1986. {
  1987. SetTimer(fDesiredDuration - BlendOutTime,false,nameof(OnAnimEnd));
  1988. }
  1989. else
  1990. {
  1991. SetTimer((MySkelMesh.GetAnimLength(Sequence) * DefaultAnimSpeed) - BlendOutTime,false,nameof(OnAnimEnd));
  1992. }
  1993. }
  1994. else
  1995. {
  1996. ClearTimer(nameof(OnAnimEnd));
  1997. }
  1998. }
  1999.  
  2000. /** Overriden to use WeaponAnimSeqNode */
  2001. simulated function PlayWeaponAnimation(name Sequence, float fDesiredDuration, optional bool bLoop, optional SkeletalMeshComponent SkelMesh)
  2002. {
  2003. local float DesiredRate;
  2004.  
  2005. if ( Mesh != none && Instigator != none && WorldInfo.NetMode != NM_DedicatedServer )
  2006. {
  2007. if ( WeaponAnimSeqNode != None )
  2008. {
  2009. if (WeaponAnimSeqNode.AnimSeq == None || WeaponAnimSeqNode.AnimSeq.SequenceName != Sequence)
  2010. {
  2011. WeaponAnimSeqNode.SetAnim(Sequence);
  2012. }
  2013.  
  2014. if(fDesiredDuration > 0.0 && WeaponAnimSeqNode.AnimSeq.RateScale > 0.0)
  2015. {
  2016. DesiredRate = WeaponAnimSeqNode.AnimSeq.SequenceLength / (fDesiredDuration * WeaponAnimSeqNode.AnimSeq.RateScale);
  2017. WeaponAnimSeqNode.PlayAnim(bLoop, DesiredRate);
  2018. }
  2019. else
  2020. {
  2021. WeaponAnimSeqNode.PlayAnim(bLoop, DefaultAnimSpeed);
  2022. }
  2023. }
  2024. }
  2025. }
  2026.  
  2027. /** Overridden to look for cached WeaponAnimNodeSequence */
  2028. simulated function AnimNodeSequence GetWeaponAnimNodeSeq()
  2029. {
  2030. if ( WeaponAnimSeqNode != None )
  2031. {
  2032. return WeaponAnimSeqNode;
  2033. }
  2034.  
  2035. return Super.GetWeaponAnimNodeSeq();
  2036. }
  2037.  
  2038. /**
  2039. * returns true if this weapon is currently playing any animations
  2040. * TODO: Should probably add checking the hands in here as well, to make sure
  2041. * they don't get out of sync
  2042. */
  2043. simulated function bool WeaponIsAnimating()
  2044. {
  2045. // Check we have access to mesh and animations
  2046. if( MySkelMesh == none || GetWeaponAnimNodeSeq() == none )
  2047. {
  2048. return false;
  2049. }
  2050.  
  2051. return WeaponAnimSeqNode.bPlaying;
  2052. }
  2053.  
  2054. /** @see PlayerController:ClientPlayCameraAnim */
  2055. function CameraAnimInst PlayCameraAnim( CameraAnim AnimToPlay, optional float Scale=1.f, optional float Rate=1.f,
  2056. optional float BlendInTime, optional float BlendOutTime, optional bool bLoop,
  2057. optional bool bRandomStartTime, optional ECameraAnimPlaySpace Space=CAPS_CameraLocal, optional rotator CustomPlaySpace )
  2058. {
  2059. local PlayerController PC;
  2060. local CameraAnimInst AnimInst;
  2061.  
  2062. PC = PlayerController(Instigator.Controller);
  2063. if (PC.PlayerCamera != None)
  2064. {
  2065. AnimInst = PC.PlayerCamera.PlayCameraAnim(AnimToPlay, Rate, Scale, BlendInTime, BlendOutTime, bLoop, bRandomStartTime);
  2066. if (AnimInst != None && Space != CAPS_CameraLocal)
  2067. {
  2068. AnimInst.SetPlaySpace(Space, CustomPlaySpace);
  2069. }
  2070. }
  2071.  
  2072. return AnimInst;
  2073. }
  2074.  
  2075. /** Return true if this weapon should play the fire last animation for this shoot animation */
  2076. simulated function bool ShouldPlayFireLast(byte FireModeNum)
  2077. {
  2078. if ( bHasFireLastAnims )
  2079. {
  2080. if( (!bAllowClientAmmoTracking && Role < ROLE_Authority && AmmoCount[GetAmmoType(FireModeNum)] <= 1)
  2081. || ((bAllowClientAmmoTracking || Role == ROLE_Authority) && AmmoCount[GetAmmoType(FireModeNum)] == 0) )
  2082. {
  2083. return true;
  2084. }
  2085. }
  2086.  
  2087. return false;
  2088. }
  2089.  
  2090. /** Get name of the animation to play for PlayFireEffects */
  2091. simulated function name GetWeaponFireAnim(byte FireModeNum)
  2092. {
  2093. local bool bPlayFireLast;
  2094.  
  2095. bPlayFireLast = ShouldPlayFireLast(FireModeNum);
  2096.  
  2097. // scoped-sight anims
  2098. if( bUsingScopePosition )
  2099. {
  2100. if( bPlayFireLast && FireLastScopedAnim != '' )
  2101. {
  2102. return FireLastScopedAnim;
  2103. }
  2104. else
  2105. {
  2106. return FireScopedAnim;
  2107. }
  2108. }
  2109. // ironsights animations
  2110. else if ( bUsingSights )
  2111. {
  2112. if( bPlayFireLast && FireLastSightedAnim != '' )
  2113. {
  2114. return FireLastSightedAnim;
  2115. }
  2116. else
  2117. {
  2118. return FireSightedAnims[Rand(FireSightedAnims.Length)];
  2119. }
  2120. }
  2121. else
  2122. {
  2123. if( !bReloadFromMagazine && LastReloadAbortTime == WorldInfo.TimeSeconds )
  2124. {
  2125. return bPlayFireLast ? FireOneHandLastAnim : FireOneHandAnim;
  2126. }
  2127.  
  2128. return (bPlayFireLast && FireLastAnim != '') ? FireLastAnim : FireAnim;
  2129. }
  2130. }
  2131.  
  2132. /** Get name of the animation to play for PlayFireEffects */
  2133. simulated function name GetLoopingFireAnim(byte FireModeNum)
  2134. {
  2135. // scoped-sight anims
  2136. if( bUsingScopePosition )
  2137. {
  2138. return FireLoopScopedAnim;
  2139. }
  2140. // ironsights animations
  2141. else if ( bUsingSights )
  2142. {
  2143. return FireLoopSightedAnim;
  2144. }
  2145.  
  2146. return FireLoopAnim;
  2147. }
  2148.  
  2149. /** Get name of the animation to play for PlayFireEffects */
  2150. simulated function name GetLoopStartFireAnim(byte FireModeNum)
  2151. {
  2152. if ( bUsingSights )
  2153. {
  2154. return FireLoopStartSightedAnim;
  2155. }
  2156.  
  2157. return FireLoopStartAnim;
  2158. }
  2159.  
  2160. /** Get name of the animation to play for PlayFireEffects */
  2161. simulated function name GetLoopEndFireAnim(byte FireModeNum)
  2162. {
  2163. if ( bUsingSights )
  2164. {
  2165. return FireLoopEndSightedAnim;
  2166. }
  2167.  
  2168. return FireLoopEndAnim;
  2169. }
  2170.  
  2171. /** Get name of the animation to play for PlayFireEffects */
  2172. simulated function name GetMeleeAnimName(EPawnOctant AtkDir, EMeleeAttackType AtkType)
  2173. {
  2174. local int idx;
  2175.  
  2176. if( MeleeAttackAnims.Length > 0 )
  2177. {
  2178. // These are randomely selected, so anim lengths should match!
  2179. idx = Rand(MeleeAttackAnims.Length);
  2180. return MeleeAttackAnims[idx];
  2181. }
  2182. }
  2183.  
  2184. simulated function AnimNodeSequence GetArmAnimNodeSeq()
  2185. {
  2186. local KFPawn P;
  2187.  
  2188. P = KFPawn(Instigator);
  2189. if (P != None && P.ArmsMesh != None)
  2190. {
  2191. return AnimNodeSequence(P.ArmsMesh.Animations);
  2192. }
  2193.  
  2194. return None;
  2195. }
  2196.  
  2197. /** Locks the bolt bone in place to the open position (Called by animnotify) */
  2198. simulated function ANIMNOTIFY_LockBolt()
  2199. {
  2200. // only do this if we're out of ammo
  2201. if ( bAllowClientAmmoTracking )
  2202. {
  2203. if ( AmmoCount[0] == 0 )
  2204. {
  2205. EmptyMagBlendNode.SetBlendTarget(1, 0);
  2206. }
  2207. }
  2208. else
  2209. {
  2210. // 'approximately' out of ammo
  2211. if( (Role < ROLE_Authority && AmmoCount[0] <= 1)
  2212. || (Role == ROLE_Authority && AmmoCount[0] == 0) )
  2213. {
  2214. EmptyMagBlendNode.SetBlendTarget(1, 0);
  2215. }
  2216. }
  2217. }
  2218.  
  2219. /** Unlocks the bolt bone (Called by animnotify) */
  2220. simulated function ANIMNOTIFY_UnLockBolt()
  2221. {
  2222. bCheckBoltLockPostReload = true;
  2223. EmptyMagBlendNode.SetBlendTarget(0, 0);
  2224. }
  2225.  
  2226. /** Turn on/off additive blend node used for movement */
  2227. simulated function ToggleAdditiveBobAnim(bool bOn, optional float BlendTime=0.1f)
  2228. {
  2229. if ( IdleBobBlendNode != None )
  2230. {
  2231. IdleBobBlendNode.SetBlendTarget((bOn && bUseAdditiveMoveAnim) ? 1.f : 0.f, BlendTime);
  2232. }
  2233. }
  2234.  
  2235. /** Check AmmoCount and update anim tree nodes if needed */
  2236. simulated function UpdateOutOfAmmoEffects(float BlendTime)
  2237. {
  2238. if ( WorldInfo.NetMode == NM_DedicatedServer )
  2239. return;
  2240.  
  2241. if( EmptyMagBlendNode != None )
  2242. {
  2243. // For now just update the lock to fix edge case with newly picked up weapon until
  2244. // AnimNotify system is completely removed. Only supported for client-side ammo
  2245. if ( AmmoCount[0] == 0 )
  2246. {
  2247. EmptyMagBlendNode.SetBlendTarget(1, 0);
  2248. }
  2249. }
  2250. }
  2251.  
  2252. /*********************************************************************************************
  2253. * @name 1st person positioning
  2254. ********************************************************************************************* */
  2255.  
  2256. native final function bool GetAnimSeqCameraPosition(out vector OutPos, out rotator OutRot);
  2257.  
  2258. /**
  2259. * This function aligns the gun model in the world
  2260. */
  2261. simulated event SetPosition(KFPawn Holder)
  2262. {
  2263. local vector DrawOffset, ViewOffset, FinalLocation;
  2264. local rotator NewRotation, FinalRotation, SpecRotation;
  2265. local PlayerController PC;
  2266. local KFPlayerController KFPC;
  2267. local vector SpecViewLoc;
  2268. local Rotator DebugRotationOffset;
  2269. local rotator UsedBufferRotation;
  2270. local vector CamLoc;
  2271. local rotator CamRot;
  2272. local int i;
  2273. local KFPawn KFP;
  2274.  
  2275. if ( !Holder.IsFirstPerson() && !bWeaponNeedsServerPosition )
  2276. return;
  2277.  
  2278. // Hide the weapon if hidden
  2279. if ( bForceHidden )
  2280. {
  2281. Mesh.SetHidden(True);
  2282. Holder.ArmsMesh.SetHidden(true);
  2283. KFP = KFPawn(Instigator);
  2284. if(KFP != none)
  2285. {
  2286. for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; i++)
  2287. {
  2288. if (KFP.FirstPersonAttachments[i] != none)
  2289. {
  2290. KFP.FirstPersonAttachments[i].SetHidden(true);
  2291. }
  2292. }
  2293. }
  2294. NewRotation = Holder.GetViewRotation();
  2295. SetLocation(Instigator.GetPawnViewLocation() + (HiddenWeaponsOffset >> NewRotation));
  2296. SetRotation(NewRotation);
  2297. SetBase(Instigator);
  2298. return;
  2299. }
  2300.  
  2301. if(bPendingShow)
  2302. {
  2303. SetHidden(False);
  2304. bPendingShow = FALSE;
  2305. }
  2306.  
  2307. Mesh.SetHidden(False);
  2308.  
  2309. // Find the local player controller
  2310. PC = GetALocalPlayerController();
  2311.  
  2312. // Adjust for the current hand
  2313. ViewOffset = PlayerViewOffset;
  2314.  
  2315. // TODO: Ramm - look at this offset value and see if we like it
  2316. if (class'Engine'.static.IsRealDStereoEnabled())
  2317. {
  2318. ViewOffset.X -= 30;
  2319. }
  2320.  
  2321. // Calculate the draw offset for Recorded Demo Specator
  2322. if ( Holder.Controller == None && KFDemoRecSpectator(PC) != None)
  2323. {
  2324. PC.GetPlayerViewPoint(SpecViewLoc, SpecRotation);
  2325. DrawOffset = ViewOffset >> SpecRotation;
  2326. DrawOffset += Holder.WeaponBob(BobDamping, JumpDamping);
  2327. FinalLocation = SpecViewLoc + DrawOffset;
  2328. SetLocation(FinalLocation);
  2329. SetBase(Holder);
  2330.  
  2331. SetRotation(SpecRotation);
  2332. return;
  2333. }
  2334.  
  2335. // Calculate weapon rotation
  2336. NewRotation = (Holder.Controller == None) ? Holder.GetBaseAimRotation() : Holder.Controller.Rotation;
  2337.  
  2338. // Add in any rotation from the zoom in rotation interpolation
  2339. NewRotation += ZoomRotInterp;
  2340.  
  2341. // Rotation local space offsets
  2342. // Add in the free-aim rotation
  2343. KFPC = KFPlayerController( Holder.Controller );
  2344. if ( KFPC != None )
  2345. {
  2346. if( bDebugRecoilPosition )
  2347. {
  2348. DebugRotationOffset.Pitch = RecoilISMaxPitchLimit;
  2349. KFPC.WeaponBufferRotation = DebugRotationOffset;
  2350. }
  2351.  
  2352. // Scale the rendered Buffer rotation by the FOV compensation scale
  2353. if( KFPC.WeaponBufferRotation.Pitch < 32768 )
  2354. {
  2355. UsedBufferRotation.Pitch = KFPC.WeaponBufferRotation.Pitch/IronSightMeshFOVCompensationScale;
  2356. }
  2357. else
  2358. {
  2359. UsedBufferRotation.Pitch = 65535 - ((65535 - KFPC.WeaponBufferRotation.Pitch)/IronSightMeshFOVCompensationScale);
  2360. }
  2361.  
  2362. if( KFPC.WeaponBufferRotation.Yaw < 32768 )
  2363. {
  2364. UsedBufferRotation.Yaw = KFPC.WeaponBufferRotation.Yaw/IronSightMeshFOVCompensationScale;
  2365. }
  2366. else
  2367. {
  2368. UsedBufferRotation.Yaw = 65535 - ((65535 - KFPC.WeaponBufferRotation.Yaw) / IronSightMeshFOVCompensationScale);
  2369. }
  2370.  
  2371. NewRotation += UsedBufferRotation;
  2372. }
  2373.  
  2374. // Add in offset from camera animation
  2375. if ( bFollowAnimSeqCamera && GetAnimSeqCameraPosition(CamLoc, CamRot) )
  2376. {
  2377. ViewOffset += CamLoc;
  2378. NewRotation += CamRot;
  2379. }
  2380.  
  2381. FinalRotation = NewRotation;
  2382.  
  2383. // Calculate the draw offset
  2384. DrawOffset.Z += Holder.GetEyeHeight();
  2385. DrawOffset += Holder.WeaponBob(BobDamping, JumpDamping);
  2386. DrawOffset += (WeaponLag + ViewOffset) >> FinalRotation;
  2387.  
  2388. // Adjust it in the world
  2389. FinalLocation = Holder.Location + DrawOffset;
  2390.  
  2391. SetLocation(FinalLocation);
  2392. SetRotation(FinalRotation);
  2393. SetBase(Holder);
  2394. }
  2395.  
  2396. /**
  2397. * This function is called from the pawn when the visibility of the weapon changes
  2398. */
  2399. simulated function ChangeVisibility(bool bIsVisible)
  2400. {
  2401. local KFPawn KFP;
  2402. local SkeletalMeshComponent SkelMesh;
  2403. local PrimitiveComponent Primitive;
  2404. local int i;
  2405.  
  2406. if (Mesh != None)
  2407. {
  2408. if (bIsVisible && !Mesh.bAttached)
  2409. {
  2410. AttachComponent(Mesh);
  2411. EnsureWeaponOverlayComponentLast();
  2412. // if the mesh isn't attached, we skipped playing the idle anim so play it now
  2413. PlayIdleAnim();
  2414. }
  2415. SetHidden(!bIsVisible);
  2416. SkelMesh = SkeletalMeshComponent(Mesh);
  2417. if (SkelMesh != None)
  2418. {
  2419. foreach SkelMesh.AttachedComponents(class'PrimitiveComponent', Primitive)
  2420. {
  2421. Primitive.SetHidden(!bIsVisible);
  2422. }
  2423. }
  2424. }
  2425.  
  2426. KFP = KFPawn(Instigator);
  2427. if (KFP != None && KFP.ArmsMesh != None)
  2428. {
  2429. if( bIsVisible )
  2430. {
  2431. AttachComponent(KFP.ArmsMesh);
  2432. }
  2433. else
  2434. {
  2435. DetachComponent(KFP.ArmsMesh);
  2436. }
  2437. KFP.ArmsMesh.SetHidden(!bIsVisible);
  2438.  
  2439. for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; i++)
  2440. {
  2441. if (KFP.FirstPersonAttachments[i] != none)
  2442. {
  2443. if (bIsVisible)
  2444. {
  2445. AttachComponent(KFP.FirstPersonAttachments[i]);
  2446. }
  2447. else
  2448. {
  2449. DetachComponent(KFP.FirstPersonAttachments[i]);
  2450. }
  2451. KFP.FirstPersonAttachments[i].SetHidden(!bIsVisible);
  2452. }
  2453. }
  2454. }
  2455.  
  2456. if ( OverlayMesh != none )
  2457. {
  2458. OverlayMesh.SetHidden(!bIsVisible);
  2459. }
  2460. }
  2461.  
  2462. /*********************************************************************************************
  2463. * @name Effects
  2464. ********************************************************************************************* */
  2465.  
  2466. /**
  2467. * PlayFireEffects Is the root function that handles all of the effects associated with
  2468. * a weapon. This function creates the 1st person effects. It should only be called
  2469. * on a locally controlled player.
  2470. */
  2471. simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation )
  2472. {
  2473. local name WeaponFireAnimName;
  2474. local KFPerk CurrentPerk;
  2475. local float TempTweenTime, AdjustedAnimLength;
  2476.  
  2477. // If we have stopped the looping fire sound to play single fire sounds for zed time
  2478. // start the looping sound back up again when the time is back above zed time speed
  2479. if( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !bPlayingLoopingFireSnd )
  2480. {
  2481. StartLoopingFireSound(FireModeNum);
  2482. }
  2483.  
  2484. PlayFiringSound(CurrentFireMode);
  2485.  
  2486. if( Instigator != none )
  2487. {
  2488. // Tell our pawn about any changes in animation speed
  2489. UpdateWeaponAttachmentAnimRate( GetThirdPersonAnimRate() );
  2490.  
  2491. if( Instigator.IsLocallyControlled() )
  2492. {
  2493. if( Instigator.IsFirstPerson() )
  2494. {
  2495. if ( !bPlayingLoopingFireAnim )
  2496. {
  2497. WeaponFireAnimName = GetWeaponFireAnim(FireModeNum);
  2498.  
  2499. if ( WeaponFireAnimName != '' )
  2500. {
  2501. AdjustedAnimLength = MySkelMesh.GetAnimLength(WeaponFireAnimName);
  2502. TempTweenTime = FireTweenTime;
  2503.  
  2504. CurrentPerk = GetPerk();
  2505. if( CurrentPerk != none )
  2506. {
  2507. CurrentPerk.ModifyRateOfFire( AdjustedAnimLength, self );
  2508.  
  2509. // We need to unlock the slide if we fire from zero ammo while uber ammo is active
  2510. if( EmptyMagBlendNode != none
  2511. && BonesToLockOnEmpty.Length > 0
  2512. && AmmoCount[GetAmmoType(FireModeNum)] == 0
  2513. && CurrentPerk.GetIsUberAmmoActive(self) )
  2514. {
  2515. EmptyMagBlendNode.SetBlendTarget( 0, 0 );
  2516. TempTweenTime = 0.f;
  2517. }
  2518. }
  2519.  
  2520. PlayAnimation(WeaponFireAnimName, AdjustedAnimLength,, TempTweenTime);
  2521. }
  2522. }
  2523.  
  2524. // Start muzzle flash effect
  2525. CauseMuzzleFlash(FireModeNum);
  2526. }
  2527.  
  2528. HandleRecoil();
  2529. ShakeView();
  2530. }
  2531. }
  2532. }
  2533.  
  2534. simulated function StopFireEffects(byte FireModeNum)
  2535. {
  2536. if (MuzzleFlash != None)
  2537. {
  2538. MuzzleFlash.StopMuzzleFlash();
  2539. }
  2540. }
  2541.  
  2542. /**
  2543. * Starts playing looping FireAnim and FireSnd
  2544. */
  2545. simulated function StartLoopingFireEffects(byte FireModeNum, optional bool bForceAnim)
  2546. {
  2547. local name WeaponFireAnimName;
  2548.  
  2549. if ( bForceAnim || (FireModeNum < bLoopingFireAnim.Length && bLoopingFireAnim[FireModeNum]) )
  2550. {
  2551. // If available, try to play loop start anim
  2552. WeaponFireAnimName = GetLoopStartFireAnim(FireModeNum);
  2553. if ( WeaponFireAnimName != '' )
  2554. {
  2555. PlayAnimation(WeaponFireAnimName, MySkelMesh.GetAnimLength(WeaponFireAnimName), FALSE, FireTweenTime, 0.f);
  2556. }
  2557. // Otherwise go straight to the looping animation
  2558. else
  2559. {
  2560. WeaponFireAnimName = GetLoopingFireAnim(FireModeNum);
  2561. PlayAnimation(WeaponFireAnimName, MySkelMesh.GetAnimLength(WeaponFireAnimName), TRUE, FireTweenTime);
  2562. }
  2563.  
  2564. bPlayingLoopingFireAnim = true;
  2565. }
  2566.  
  2567. StartLoopingFireSound(FireModeNum);
  2568. }
  2569.  
  2570. /**
  2571. * Stops playing looping FireAnim and FireSnd
  2572. */
  2573. simulated function StopLoopingFireEffects(byte FireModeNum)
  2574. {
  2575. local name LoopEndFireAnim;
  2576.  
  2577. if ( bPlayingLoopingFireAnim )
  2578. {
  2579. LoopEndFireAnim = GetLoopEndFireAnim(FireModeNum);
  2580. if ( LoopEndFireAnim != '' )
  2581. {
  2582. PlayAnimation(LoopEndFireAnim, MySkelMesh.GetAnimLength(LoopEndFireAnim));
  2583. }
  2584. else
  2585. {
  2586. ClearTimer(nameof(OnAnimEnd)); // needed for loopstart anim to return to idle
  2587. }
  2588. bPlayingLoopingFireAnim = false;
  2589. }
  2590.  
  2591. StopLoopingFireSound( FireModeNum );
  2592. }
  2593.  
  2594. /** True if we want to override the looping fire sounds with fire sounds from another firemode */
  2595. simulated function bool ShouldForceSingleFireSound()
  2596. {
  2597. // If this weapon has a single-shot firemode, disable looping fire sounds during zedtime
  2598. if ( `IsInZedTime(self) && SingleFireSoundIndex != 255 )
  2599. {
  2600. return true;
  2601. }
  2602.  
  2603. return false;
  2604. }
  2605.  
  2606. /**
  2607. * Starts playing looping FireSnd only (used for switching sounds in Zedtime)
  2608. */
  2609. simulated function StartLoopingFireSound(byte FireModeNum)
  2610. {
  2611. if ( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !ShouldForceSingleFireSound() )
  2612. {
  2613. bPlayingLoopingFireSnd = true;
  2614. KFPawn(Instigator).SetWeaponAmbientSound(WeaponFireSnd[FireModeNum].DefaultCue, WeaponFireSnd[FireModeNum].FirstPersonCue);
  2615. }
  2616. }
  2617.  
  2618. /**
  2619. * Stops playing looping FireSnd only (used for switching sounds in Zedtime)
  2620. */
  2621. simulated function StopLoopingFireSound(byte FireModeNum)
  2622. {
  2623. if ( bPlayingLoopingFireSnd )
  2624. {
  2625. KFPawn(Instigator).SetWeaponAmbientSound(None);
  2626. if ( FireModeNum < WeaponFireLoopEndSnd.Length )
  2627. {
  2628. WeaponPlayFireSound(WeaponFireLoopEndSnd[FireModeNum].DefaultCue, WeaponFireLoopEndSnd[FireModeNum].FirstPersonCue);
  2629. }
  2630.  
  2631. bPlayingLoopingFireSnd = false;
  2632. }
  2633. }
  2634.  
  2635. /**
  2636. * Tells the weapon to play a firing sound (uses CurrentFireMode)
  2637. */
  2638. simulated function PlayFiringSound( byte FireModeNum )
  2639. {
  2640. local byte UsedFireModeNum;
  2641.  
  2642. MakeNoise(1.0,'PlayerFiring'); // AI
  2643.  
  2644. if ( !bPlayingLoopingFireSnd )
  2645. {
  2646. UsedFireModeNum = FireModeNum;
  2647.  
  2648. // Use the single fire sound if we're in zed time and want to play single fire sounds
  2649. if( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && ShouldForceSingleFireSound() )
  2650. {
  2651. UsedFireModeNum = SingleFireSoundIndex;
  2652. }
  2653.  
  2654. if ( UsedFireModeNum < WeaponFireSnd.Length )
  2655. {
  2656. WeaponPlayFireSound(WeaponFireSnd[UsedFireModeNum].DefaultCue, WeaponFireSnd[UsedFireModeNum].FirstPersonCue);
  2657. }
  2658. }
  2659. }
  2660.  
  2661. /**
  2662. * This function selects whether we should play a first person or third person sound
  2663. */
  2664. simulated function WeaponPlayFireSound(AkBaseSoundObject DefaultSound, AkBaseSoundObject FirstPersonSound)
  2665. {
  2666. // ReplicateSound needs an "out" vector
  2667. local vector SoundLocation;
  2668.  
  2669. if( Instigator != None && !bSuppressSounds )
  2670. {
  2671. // With our WWise integration, SoundLocation can't be same as Instigator location (or whatever
  2672. // object calls PlaySoundBase/ReplicateSound) otherwise it gets set to 0 in native in
  2673. // APlayerController::HearSound and attaches the sound to the instigator and the sound will
  2674. // follow the the actor around. For now sing the eye location to solve this issue.
  2675. SoundLocation = Instigator.GetPawnViewLocation();
  2676.  
  2677. if ( FirstPersonSound != None && Instigator.IsLocallyControlled() && Instigator.IsFirstPerson() )
  2678. {
  2679. Instigator.PlaySoundBase( FirstPersonSound, true, false, false );
  2680.  
  2681. // Play first person, but replicate third person
  2682. if ( WorldInfo.NetMode == NM_ListenServer )
  2683. {
  2684. KFPawn(Instigator).ReplicateSound(DefaultSound, false, false, false, SoundLocation);
  2685. }
  2686. }
  2687. else if ( DefaultSound != None )
  2688. {
  2689. Instigator.PlaySoundBase( DefaultSound, false, true, false, SoundLocation );
  2690. }
  2691. }
  2692. }
  2693.  
  2694. /**
  2695. * This function handles playing sounds for weapons. How it plays the sound depends on the following:
  2696. *
  2697. * If we are a listen server, then this sound is played and replicated as normal
  2698. * If we are a remote client, but locally controlled (ie: we are on the client) we play the sound and don't replicate it
  2699. * If we are a dedicated server, play the sound and replicate it to everyone BUT the owner (he will play it locally).
  2700. *
  2701. *
  2702. * @param Sound - The Source to play
  2703. */
  2704. simulated function WeaponPlaySound(AkBaseSoundObject Sound, optional float NoiseLoudness)
  2705. {
  2706. // if we are a listen server, just play the sound. It will play locally
  2707. // and be replicated to all other clients.
  2708. if( Sound != None && Instigator != None && !bSuppressSounds )
  2709. {
  2710. Instigator.PlaySoundBase(Sound, false, true);
  2711. }
  2712. }
  2713.  
  2714. /**
  2715. * Causes the muzzle flash to turn on and setup a time to
  2716. * turn it back off again.
  2717. */
  2718. simulated function CauseMuzzleFlash(byte FireModeNum)
  2719. {
  2720. if (MuzzleFlash == None)
  2721. {
  2722. AttachMuzzleFlash();
  2723. }
  2724.  
  2725. if (MuzzleFlash != None )
  2726. {
  2727. MuzzleFlash.CauseMuzzleFlash(FireModeNum);
  2728. if ( MuzzleFlash.bAutoActivateShellEject )
  2729. {
  2730. MuzzleFlash.CauseShellEject();
  2731. SetShellEjectsToForeground();
  2732. }
  2733. }
  2734. }
  2735.  
  2736. /** notify to spawn a shell eject from the muzzle flash component */
  2737. simulated function ANIMNOTIFY_ShellEject()
  2738. {
  2739. if (MuzzleFlash == None)
  2740. {
  2741. AttachMuzzleFlash();
  2742. }
  2743.  
  2744. if (MuzzleFlash != None )
  2745. {
  2746. MuzzleFlash.CauseShellEject();
  2747. SetShellEjectsToForeground();
  2748. }
  2749. }
  2750.  
  2751. /**
  2752. * Called on a client, this function Attaches the WeaponAttachment
  2753. * to the Mesh.
  2754. */
  2755. simulated function AttachMuzzleFlash()
  2756. {
  2757. if ( MySkelMesh != none )
  2758. {
  2759. if (MuzzleFlashTemplate != None)
  2760. {
  2761. MuzzleFlash = new(self) Class'KFMuzzleFlash'(MuzzleFlashTemplate);
  2762. MuzzleFlash.AttachMuzzleFlash(MySkelMesh);
  2763. }
  2764. }
  2765. }
  2766.  
  2767. /** Sets the shell ejector to the foreground depth */
  2768. simulated function SetShellEjectsToForeground()
  2769. {
  2770. // Put shells in the foreground at first
  2771. if( MuzzleFlash != none && MuzzleFlash.ShellEjectPSC != none && EjectedShellForegroundDuration > 0.f )
  2772. {
  2773. MuzzleFlash.ShellEjectPSC.SetDepthPriorityGroup( SDPG_Foreground );
  2774. MuzzleFlash.ShellEjectPSC.bDepthTestEnabled = true;
  2775.  
  2776. SetTimer( EjectedShellForegroundDuration, false, nameOf(Timer_RestoreShellEjectDepth) );
  2777. }
  2778. }
  2779.  
  2780. /** Sets the shell ejection PSC back to world depth */
  2781. simulated function Timer_RestoreShellEjectDepth()
  2782. {
  2783. if( MuzzleFlash != none && MuzzleFlash.ShellEjectPSC != none )
  2784. {
  2785. MuzzleFlash.ShellEjectPSC.SetDepthPriorityGroup( SDPG_World );
  2786. MuzzleFlash.ShellEjectPSC.bDepthTestEnabled = false;
  2787. }
  2788. }
  2789.  
  2790. /** plays view shake on the owning client only */
  2791. simulated function ShakeView()
  2792. {
  2793. local PlayerController PC;
  2794. local float ShakeViewScale;
  2795.  
  2796. PC = PlayerController(Instigator.Controller);
  2797. if( PC == None || LocalPlayer(PC.Player) == None )
  2798. {
  2799. return;
  2800. }
  2801.  
  2802. // Play camera shake/anim
  2803. if (CurrentFireMode < FireCameraAnim.length && FireCameraAnim[CurrentFireMode] != None)
  2804. {
  2805. ShakeViewScale = ShakeScaleStandard;
  2806. if( bUsingSights )
  2807. {
  2808. ShakeViewScale *= ShakeScaleSighted;
  2809. }
  2810.  
  2811. // Use a CameraAnim for now, but consider CameraShake with bRandomAnimSegment
  2812. PC.ClientPlayCameraAnim(FireCameraAnim[CurrentFireMode], ShakeViewScale, 1.f, 0.0f, 0.1f);
  2813. }
  2814.  
  2815. // Play controller vibration
  2816. PC.ClientPlayForceFeedbackWaveform( WeaponFireWaveForm );
  2817. }
  2818.  
  2819. /** Increment bloody material parameter */
  2820. simulated function AddBlood(float MinAmount, float MaxAmount)
  2821. {
  2822. local float NewBlood;
  2823. local int i;
  2824.  
  2825. if ( WorldInfo.NetMode != NM_DedicatedServer && WeaponMICs.Length > 0 )
  2826. {
  2827. NewBlood = RandRange(MinAmount, MaxAmount);
  2828. BloodParamValue = FMax(BloodParamValue + NewBlood, MinBloodParamValue);
  2829.  
  2830. for( i = 0; i < WeaponMICs.Length; ++i )
  2831. {
  2832. if( WeaponMICs[i] != none )
  2833. {
  2834. WeaponMICs[i].SetScalarParameterValue( BloodParamName, BloodParamValue );
  2835. }
  2836. }
  2837. }
  2838. }
  2839.  
  2840. /*********************************************************************************************
  2841. * @name Firing / Projectile
  2842. ********************************************************************************************* */
  2843.  
  2844. /**
  2845. * @see Weapon::StartFire
  2846. */
  2847. simulated function StartFire(byte FireModeNum)
  2848. {
  2849. // Attempt auto-reload
  2850. if( FireModeNum == DEFAULT_FIREMODE || FireModeNum == ALTFIRE_FIREMODE )
  2851. {
  2852. if ( ShouldAutoReload(FireModeNum) )
  2853. {
  2854. FireModeNum = RELOAD_FIREMODE;
  2855. }
  2856. }
  2857.  
  2858. // Convert to altfire if we have alt fire mode active
  2859. bStopAltFireOnNextRelease = false;
  2860. if ( FireModeNum == DEFAULT_FIREMODE && bUseAltFireMode && !bGamepadFireEntry )
  2861. {
  2862. FireModeNum = ALTFIRE_FIREMODE;
  2863. bStopAltFireOnNextRelease = true;
  2864. }
  2865.  
  2866. if ( FireModeNum == RELOAD_FIREMODE )
  2867. {
  2868. // Skip Super/ ServerStartFire and let server wait for ServerSendToReload to force state synchronization
  2869. BeginFire(FireModeNum);
  2870. return;
  2871. }
  2872.  
  2873. Super.StartFire(FireModeNum);
  2874. }
  2875.  
  2876. /**
  2877. * BeginFire is the point at which the server and client sync up their code path. It's job is to set
  2878. * the weapon in to the firing state.
  2879. * Network: LocalPlayer and Server
  2880. */
  2881. simulated function BeginFire( Byte FireModeNum )
  2882. {
  2883. local KFPerk_Gunslinger GunslingerPerk;
  2884. super.BeginFire( FireModeNum );
  2885.  
  2886. if( Role == Role_Authority )
  2887. {
  2888. GunslingerPerk = KFPerk_Gunslinger(GetPerk());
  2889. if( GunslingerPerk != none && !IsMeleeWeapon() && !GunslingerPerk.IsWeaponOnPerk( self,, GunslingerPerk.class ) )
  2890. {
  2891. GunslingerPerk.ResetHeadShotCombo();
  2892. }
  2893. }
  2894. }
  2895.  
  2896. /**
  2897. * @see Weapon::StopFire
  2898. */
  2899. simulated function StopFire(byte FireModeNum)
  2900. {
  2901. // Convert to altfire if we have alt fire mode active
  2902. if ( FireModeNum == DEFAULT_FIREMODE && bStopAltFireOnNextRelease )
  2903. {
  2904. bStopAltFireOnNextRelease = false;
  2905. FireModeNum = ALTFIRE_FIREMODE;
  2906. }
  2907.  
  2908. Super.StopFire(FireModeNum);
  2909. }
  2910.  
  2911. /**
  2912. * Toggle between DEFAULT and ALTFIRE
  2913. */
  2914. simulated function AltFireMode()
  2915. {
  2916. // LocalPlayer Only
  2917. if ( !Instigator.IsLocallyControlled() )
  2918. {
  2919. return;
  2920. }
  2921.  
  2922. // Needs a state name, and a valid fire type to switch to alt fire mode
  2923. if( FiringStatesArray[1] == '' || WeaponFireTypes[1] == EWFT_None )
  2924. {
  2925. return;
  2926. }
  2927.  
  2928. if( bUseAltFireMode )
  2929. {
  2930. // Clear fire now since StopFire will now be converted to default fire
  2931. StopFire(ALTFIRE_FIREMODE);
  2932. bUseAltFireMode = false;
  2933. }
  2934. else
  2935. {
  2936. // Clear fire now since StopFire will now be converted to alt fire
  2937. StopFire(DEFAULT_FIREMODE);
  2938. bUseAltFireMode = true;
  2939. }
  2940.  
  2941. Instigator.PlaySoundBase( KFInventoryManager(InvManager).SwitchFireModeEvent );
  2942. }
  2943.  
  2944. /** Player released switch fire mode button */
  2945. simulated function AltFireModeRelease()
  2946. {
  2947. // Weapons that call StartFire() in SwitchFireMode() need to also call StopFire()
  2948. // For most weapons this is unnecessary, but harmless
  2949. if ( PendingFire(ALTFIRE_FIREMODE) )
  2950. {
  2951. StopFire(ALTFIRE_FIREMODE);
  2952. }
  2953. }
  2954.  
  2955. /**
  2956. * Send weapon to proper firing state
  2957. * Also sets the CurrentFireMode.
  2958. * Network: LocalPlayer and Server
  2959. *
  2960. * @param FireModeNum Fire Mode.
  2961. */
  2962. simulated function SendToFiringState(byte FireModeNum)
  2963. {
  2964. local KFPawn_Human KFPH;
  2965.  
  2966. if( FireModeNum == GRENADE_FIREMODE && !GetPerk().GetGrenadeClass().default.bAllowTossDuringZedGrabRotation )
  2967. {
  2968. // Don't let us toss a grenade right after being grabbed and spun
  2969. // so we don't blow ourselves up.
  2970. if( `TimeSince(ZedGrabGrenadeTossCooldown) < 0 )
  2971. {
  2972. //`log("Can't toss a nade because just got grabbed!!! Cooldown: "$`TimeSince(ZedGrabGrenadeTossCooldown));
  2973. return;
  2974. }
  2975. }
  2976.  
  2977. // Custom reload handling for better ammo synchronization
  2978. if ( FireModeNum == RELOAD_FIREMODE && Instigator.IsLocallyControlled() )
  2979. {
  2980. InitializeReload();
  2981. }
  2982.  
  2983. //End emote before reload starts so 1P anims get triggered properly
  2984. KFPH = KFPawn_Human(Instigator);
  2985. if( KFPH != none )
  2986. {
  2987. KFPH.CheckAndEndActiveEMoteSpecialMove();
  2988. }
  2989.  
  2990. super.SendToFiringState(FireModeNum);
  2991. }
  2992.  
  2993. /**
  2994. * This function returns the world location for spawning the visual effects
  2995. * Overridden to only use the X portion of the offset when we are in ironsights
  2996. */
  2997. simulated event vector GetMuzzleLoc()
  2998. {
  2999. local vector X, Y, Z;
  3000. local Rotator ViewRotation;
  3001.  
  3002. if( Instigator != none )
  3003. {
  3004. if( bUsingSights )
  3005. {
  3006. ViewRotation = Instigator.GetViewRotation();
  3007.  
  3008. // Add in the free-aim rotation
  3009. if ( KFPlayerController(Instigator.Controller) != None )
  3010. {
  3011. ViewRotation += KFPlayerController(Instigator.Controller).WeaponBufferRotation;
  3012. }
  3013.  
  3014. GetAxes(ViewRotation, X, Y, Z);
  3015.  
  3016. return Instigator.GetWeaponStartTraceLocation() + X * FireOffset.X;
  3017. }
  3018. else
  3019. {
  3020. ViewRotation = Instigator.GetViewRotation();
  3021.  
  3022. // Add in the free-aim rotation
  3023. if ( KFPlayerController(Instigator.Controller) != None )
  3024. {
  3025. ViewRotation += KFPlayerController(Instigator.Controller).WeaponBufferRotation;
  3026. }
  3027.  
  3028. return Instigator.GetPawnViewLocation() + (FireOffset >> ViewRotation);
  3029. }
  3030.  
  3031. }
  3032.  
  3033. return Location;
  3034. }
  3035.  
  3036. /** returns ID of muzzle to spawn projectile at / play effects at (overridden in dual) */
  3037. simulated function byte GetCurrentMuzzleID()
  3038. {
  3039. return 0;
  3040. }
  3041.  
  3042. /** Store the KFPlayerController holding this weapon so it doesn't have to be casted over and over */
  3043. simulated function CacheKFPlayerController()
  3044. {
  3045. if( Instigator == None )
  3046. {
  3047. KFPlayer = None;
  3048. }
  3049. else
  3050. {
  3051. KFPlayer = KFPlayerController(Instigator.Controller);
  3052. }
  3053. }
  3054.  
  3055. /**
  3056. * FireAmmunition: Perform all logic associated with firing a shot
  3057. * - Fires ammunition (instant hit or spawn projectile)
  3058. * - Consumes ammunition
  3059. * - Plays any associated effects (fire sound and whatnot)
  3060. *
  3061. * Network: LocalPlayer and Server
  3062. */
  3063. simulated function FireAmmunition()
  3064. {
  3065. // Let the accuracy tracking system know that we fired
  3066. HandleWeaponShotTaken( CurrentFireMode );
  3067.  
  3068. // Handle the different fire types
  3069. switch( WeaponFireTypes[CurrentFireMode] )
  3070. {
  3071. case EWFT_InstantHit:
  3072. // Launch a projectile if we are in zed time, and this weapon has a projectile to launch for this mode
  3073. if( `IsInZedTime(self) && WeaponProjectiles[CurrentFireMode] != none )
  3074. {
  3075. ProjectileFire();
  3076. }
  3077. else
  3078. {
  3079. InstantFireClient();
  3080. }
  3081. break;
  3082.  
  3083. case EWFT_Projectile:
  3084. ProjectileFire();
  3085. break;
  3086.  
  3087. case EWFT_Custom:
  3088. CustomFire();
  3089. break;
  3090. }
  3091.  
  3092. // Decrement Ammo. Called after Fire() for weapons that can fire a variable ammo amount (e.g. nail shotgun)
  3093. ConsumeAmmo( CurrentFireMode );
  3094.  
  3095. NotifyWeaponFired( CurrentFireMode );
  3096.  
  3097. // Play fire effects now (don't wait for WeaponFired to replicate)
  3098. PlayFireEffects(CurrentFireMode, vect(0,0,0));
  3099. }
  3100.  
  3101.  
  3102. /** Notification that a weapon attack has has happened */
  3103. function HandleWeaponShotTaken( byte FireMode )
  3104. {
  3105. if( KFPlayer != None )
  3106. {
  3107. KFPlayer.AddShotsFired(1);
  3108. }
  3109. }
  3110.  
  3111. /**
  3112. * CalcWeaponFire: Simulate an instant hit shot.
  3113. * This doesn't deal any damage nor trigger any effect. It just simulates a shot and returns
  3114. * the hit information, to be post-processed later.
  3115. */
  3116. simulated function ImpactInfo CalcWeaponFire(vector StartTrace, vector EndTrace, optional out array<ImpactInfo> ImpactList, optional vector Extent)
  3117. {
  3118. local ImpactInfo CurrentImpact;
  3119. local int i;
  3120. local Actor HitActor;
  3121. local bool bFirst;
  3122.  
  3123. bFirst = (ImpactList.Length == 0);
  3124. CurrentImpact = Super.CalcWeaponFire(StartTrace, EndTrace, ImpactList, Extent);
  3125.  
  3126. // When recursion is finished process all impacts to find hit zones
  3127. if ( bFirst )
  3128. {
  3129. // For pawn hits calculate an improved hit zone and direction. The return, CurrentImpact, is
  3130. // unaffected which is fine since it's only used for it's HitLocation and not by ProcessInstantHit()
  3131. TraceImpactHitZones(StartTrace, EndTrace, ImpactList);
  3132.  
  3133. // Iterate though ImpactList, find water, return water Impact as 'realImpact'
  3134. // This is needed for impact effects on non-blocking water
  3135. for (i = 0; i < ImpactList.Length; i++)
  3136. {
  3137. HitActor = ImpactList[i].HitActor;
  3138. if ( HitActor != None && !HitActor.bBlockActors && HitActor.IsA('KFWaterMeshActor') )
  3139. {
  3140. return ImpactList[i];
  3141. }
  3142. }
  3143. }
  3144.  
  3145. return CurrentImpact;
  3146. }
  3147.  
  3148. /** Replaces ImpactInfo entries with hit zone information */
  3149. simulated function TraceImpactHitZones(vector StartTrace, vector EndTrace, out array<ImpactInfo> ImpactList)
  3150. {
  3151. local int i;
  3152. local array<ImpactInfo> HitZoneImpactList;
  3153.  
  3154. for (i = 0; i < ImpactList.Length; i++)
  3155. {
  3156. if ( ImpactList[i].HitActor != None
  3157. && ImpactList[i].HitActor.bCanBeDamaged
  3158. && ImpactList[i].HitActor.IsA('KFPawn') )
  3159. {
  3160. // Getting the SkelMeshComp here requires (BlockZeroExtent && CollideActors && !Cylinder.BlockZeroExtent)
  3161. TraceAllPhysicsAssetInteractions(SkeletalMeshComponent(ImpactList[i].HitInfo.HitComponent), EndTrace, StartTrace, HitZoneImpactList, vect(0,0,0), true);
  3162.  
  3163. // replace impact entry with the first hitzone impact, if the list is empty we
  3164. // missed all hit zones. Handle in KFPawn.AdjustDamage().
  3165. if ( HitZoneImpactList.Length > 0 )
  3166. {
  3167. HitZoneImpactList[0].RayDir = ImpactList[i].RayDir;
  3168. ImpactList[i] = HitZoneImpactList[0];
  3169. }
  3170. }
  3171. }
  3172. }
  3173.  
  3174. /** handles pawn penetration (why was this function static?) */
  3175. simulated function bool PassThroughDamage(Actor HitActor)
  3176. {
  3177. local KFPawn KFP;
  3178.  
  3179. if ( HitActor.bBlockActors )
  3180. {
  3181. KFP = KFPawn(HitActor);
  3182. if ( PenetrationPowerRemaining > 0 && KFP != none )
  3183. {
  3184. PenetrationPowerRemaining -= KFP.PenetrationResistance;
  3185. return true;
  3186. }
  3187. }
  3188.  
  3189. return (!HitActor.bBlockActors && (HitActor.IsA('Trigger') || HitActor.IsA('TriggerVolume')
  3190. || HitActor.IsA('InteractiveFoliageActor') || HitActor.IsA('KFWaterMeshActor')));
  3191. }
  3192.  
  3193. /**
  3194. * Adds any fire spread offset to the passed in rotator
  3195. * Overridden for KF Spread modifiers
  3196. * @param Aim the base aim direction
  3197. * @return the adjusted aim direction
  3198. */
  3199. simulated function rotator AddSpread(rotator BaseAim)
  3200. {
  3201. local vector X, Y, Z;
  3202. local float CurrentSpread, RandY, RandZ;
  3203.  
  3204. if ( CurrentFireMode >= Spread.Length )
  3205. {
  3206. return BaseAim;
  3207. }
  3208.  
  3209. CurrentSpread = Spread[CurrentFireMode];
  3210.  
  3211. if( bUsingSights )
  3212. {
  3213. CurrentSpread *= IronSightsSpreadMod;
  3214. }
  3215. else if( MovingSpreadMod > 1.0 && VSizeSq(Instigator.Velocity) > 0 )
  3216. {
  3217. CurrentSpread *= MovingSpreadMod;
  3218. }
  3219.  
  3220. if( Instigator.bIsCrouched )
  3221. {
  3222. CurrentSpread *= CrouchSpreadMod;
  3223. }
  3224.  
  3225. if (CurrentSpread == 0)
  3226. {
  3227. return BaseAim;
  3228. }
  3229. else
  3230. {
  3231. // There might be a perk active that reduces the spread
  3232. if ( GetPerk() != none )
  3233. {
  3234. GetPerk().ModifySpread(CurrentSpread);
  3235. }
  3236.  
  3237. // Add in any spread.
  3238. GetAxes(BaseAim, X, Y, Z);
  3239. RandY = FRand() - 0.5;
  3240. RandZ = Sqrt(0.5 - Square(RandY)) * (FRand() - 0.5);
  3241. return rotator(X + RandY * CurrentSpread * Y + RandZ * CurrentSpread * Z);
  3242. }
  3243. }
  3244.  
  3245. // Overridden if we want to add recoil modifiers
  3246. simulated function ModifyRecoil( out float CurrentRecoilModifier )
  3247. {
  3248. GetPerk().ModifyRecoil( CurrentRecoilModifier, self );
  3249. }
  3250.  
  3251. /**
  3252. * Called on the client when the weapon is fired calculate the recoil parameters
  3253. * Network: LocalPlayer
  3254. */
  3255. simulated event HandleRecoil()
  3256. {
  3257. local rotator NewRecoilRotation;
  3258. local float CurrentRecoilModifier;
  3259.  
  3260. local KFPawn KFP;
  3261.  
  3262. if ( Instigator == none || !Instigator.IsFirstPerson() )
  3263. {
  3264. return;
  3265. }
  3266.  
  3267. CurrentRecoilModifier = 1.0;
  3268.  
  3269. NewRecoilRotation.Pitch = RandRange( minRecoilPitch, maxRecoilPitch );
  3270. NewRecoilRotation.Yaw = RandRange( minRecoilYaw, maxRecoilYaw );
  3271.  
  3272. if ( Instigator.Physics == PHYS_Falling )
  3273. {
  3274. CurrentRecoilModifier *= FallingRecoilModifier;
  3275. }
  3276.  
  3277. // Handle weapon posture recoil modifiers
  3278. KFP = KFPawn(Instigator);
  3279. if ( KFP != none )
  3280. {
  3281. if( VSizeSq(Instigator.Velocity) > 50 )
  3282. {
  3283. if( Instigator.bIsWalking )
  3284. {
  3285. CurrentRecoilModifier *= WalkingRecoilModifier;
  3286. }
  3287. else
  3288. {
  3289. CurrentRecoilModifier *= JoggingRecoilModifier;
  3290. }
  3291. }
  3292. if ( Instigator.bIsCrouched )
  3293. {
  3294. CurrentRecoilModifier *= StanceCrouchedRecoilModifier;
  3295. }
  3296.  
  3297. if( !bUsingSights )
  3298. {
  3299. CurrentRecoilModifier *= HippedRecoilModifier;
  3300. }
  3301. }
  3302.  
  3303. // Add any additional recoil mods for children
  3304. ModifyRecoil( CurrentRecoilModifier );
  3305.  
  3306. NewRecoilRotation *= CurrentRecoilModifier;
  3307. LastRecoilModifier = CurrentRecoilModifier;
  3308.  
  3309. // Need to set this value per weapon
  3310. SetRecoil(NewRecoilRotation,RecoilRate);
  3311. }
  3312.  
  3313. /**
  3314. * Called on the client to set new recoil parameters to be processed
  3315. * Network: LocalPlayer
  3316. *
  3317. * @param NewRecoilRotation The new recoil rotation to add
  3318. * @param NewRecoilSpeed How long it should take to process this recoil
  3319. */
  3320. simulated function SetRecoil(rotator NewRecoilRotation, float NewRecoilSpeed)
  3321. {
  3322. local float UsedRecoilScale;
  3323.  
  3324. // If we haven't process all the recoil from the previous fire, scale down the
  3325. // recoil rotator by how much recoil is left before adding in new recoil
  3326. if( RecoilTimeLeft > 0 )
  3327. {
  3328. UsedRecoilScale = RecoilTimeLeft/RecoilSpeed;
  3329. }
  3330. else
  3331. {
  3332. UsedRecoilScale = 0.0;
  3333. }
  3334.  
  3335. RecoilRotator *= UsedRecoilScale;
  3336. RecoilRotator += NewRecoilRotation;
  3337.  
  3338. RecoilSpeed = NewRecoilSpeed;
  3339. RecoilTimeLeft = NewRecoilSpeed;
  3340. }
  3341.  
  3342. /**
  3343. * Returns the type of projectile to spawn. We use a function so subclasses can
  3344. * override it if needed (case in point, homing rockets).
  3345. * Overriden to be simulated so the client can get the projectile class
  3346. */
  3347. simulated function class<KFProjectile> GetKFProjectileClass()
  3348. {
  3349. if( CurrentFireMode == GRENADE_FIREMODE )
  3350. {
  3351. return GetPerk().GetGrenadeClass();
  3352. }
  3353. else
  3354. {
  3355. return (CurrentFireMode < WeaponProjectiles.length) ? class<KFProjectile>( WeaponProjectiles[CurrentFireMode] ) : None;
  3356. }
  3357. }
  3358.  
  3359. /**
  3360. * Fires a projectile.
  3361. * Spawns the projectile, but also increment the flash count for remote client effects.
  3362. * Network: Local Player and Server
  3363. * Overriden to add maximum aim adjustment
  3364. */
  3365. simulated function Projectile ProjectileFire()
  3366. {
  3367. local vector StartTrace, EndTrace, RealStartLoc, AimDir;
  3368. local ImpactInfo TestImpact;
  3369. local vector DirA, DirB;
  3370. local Quat Q;
  3371. local class<KFProjectile> MyProjectileClass;
  3372.  
  3373. // tell remote clients that we fired, to trigger effects
  3374. if ( ShouldIncrementFlashCountOnFire() )
  3375. {
  3376. IncrementFlashCount();
  3377. }
  3378.  
  3379. MyProjectileClass = GetKFProjectileClass();
  3380.  
  3381. if( Role == ROLE_Authority || (MyProjectileClass.default.bUseClientSideHitDetection
  3382. && MyProjectileClass.default.bNoReplicationToInstigator && Instigator != none
  3383. && Instigator.IsLocallyControlled()) )
  3384. {
  3385. // This is where we would start an instant trace. (what CalcWeaponFire uses)
  3386. StartTrace = GetSafeStartTraceLocation();
  3387. AimDir = Vector(GetAdjustedAim( StartTrace ));
  3388.  
  3389. // this is the location where the projectile is spawned.
  3390. RealStartLoc = GetPhysicalFireStartLoc(AimDir);
  3391.  
  3392. if( StartTrace != RealStartLoc )
  3393. {
  3394. // if projectile is spawned at different location of crosshair,
  3395. // then simulate an instant trace where crosshair is aiming at, Get hit info.
  3396. EndTrace = StartTrace + AimDir * GetTraceRange();
  3397. TestImpact = CalcWeaponFire( StartTrace, EndTrace );
  3398. // Store the original aim direction without correction
  3399. DirB = AimDir;
  3400.  
  3401. // Then we realign projectile aim direction to match where the crosshair did hit.
  3402. AimDir = Normal(TestImpact.HitLocation - RealStartLoc);
  3403.  
  3404. // Store the desired corrected aim direction
  3405. DirA = AimDir;
  3406.  
  3407. // Clamp the maximum aim adjustment for the AimDir so you don't get wierd
  3408. // cases where the projectiles velocity is going WAY off of where you
  3409. // are aiming. This can happen if you are really close to what you are
  3410. // shooting - Ramm
  3411. if ( (DirA dot DirB) < MaxAimAdjust_Cos )
  3412. {
  3413. Q = QuatFromAxisAndAngle(Normal(DirB cross DirA), MaxAimAdjust_Angle);
  3414. AimDir = QuatRotateVector(Q,DirB);
  3415. }
  3416. }
  3417.  
  3418. return SpawnProjectile(MyProjectileClass, RealStartLoc, AimDir);
  3419. }
  3420.  
  3421. return None;
  3422. }
  3423.  
  3424. /** Called from ProjectileFire() */
  3425. simulated function bool ShouldIncrementFlashCountOnFire()
  3426. {
  3427. return CurrentFireMode != GRENADE_FIREMODE;
  3428. }
  3429.  
  3430. simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir )
  3431. {
  3432. local KFProjectile SpawnedProjectile;
  3433. local int ProjDamage;
  3434.  
  3435. // Spawn projectile
  3436. SpawnedProjectile = Spawn( KFProjClass, Self,, RealStartLoc);
  3437. if( SpawnedProjectile != none && !SpawnedProjectile.bDeleteMe )
  3438. {
  3439. // Mirror damage and damage type from weapon. This is set on the server only and
  3440. // these properties are replicated via TakeHitInfo
  3441. if ( InstantHitDamage.Length > CurrentFireMode && InstantHitDamageTypes.Length > CurrentFireMode )
  3442. {
  3443. ProjDamage = InstantHitDamage[CurrentFireMode];
  3444. SpawnedProjectile.Damage = ProjDamage;
  3445. SpawnedProjectile.MyDamageType = InstantHitDamageTypes[CurrentFireMode];
  3446. }
  3447.  
  3448. // Set the penetration power for this projectile
  3449. if( SpawnedProjectile != none )
  3450. {
  3451. // because of clientside hit detection, we need two variables --
  3452. // one that replicates on init and one that updates but doesn't replicate
  3453. SpawnedProjectile.InitialPenetrationPower = GetInitialPenetrationPower(CurrentFireMode);
  3454. SpawnedProjectile.PenetrationPower = SpawnedProjectile.InitialPenetrationPower;
  3455. }
  3456.  
  3457. SpawnedProjectile.Init( AimDir );
  3458. }
  3459.  
  3460. // return it up the line
  3461. return SpawnedProjectile;
  3462. }
  3463.  
  3464. /**
  3465. * Get the starting penetration power for this FiringMode
  3466. * @param FiringMode: The FiringMode to get penetration power for
  3467. */
  3468. simulated event float GetInitialPenetrationPower(byte FiringMode)
  3469. {
  3470. local KFPerk InstigatorPerk;
  3471. local float UsedPenetrationPower;
  3472. local KFPlayerController KFPC;
  3473.  
  3474. // Set the initial penetration power for this shot
  3475. if ( PenetrationPower.Length > FiringMode && PenetrationPower[FiringMode] > 0 )
  3476. {
  3477. UsedPenetrationPower = default.PenetrationPower[FiringMode];
  3478. }
  3479.  
  3480. InstigatorPerk = GetPerk();
  3481.  
  3482. // Add in any extra penetration power from the perk
  3483. if ( InstigatorPerk != none && InstantHitDamageTypes.Length > FiringMode && Instigator != none)
  3484. {
  3485. KFPC = KFPlayerController(Instigator.Controller);
  3486. if(KFPC != none)
  3487. {
  3488. UsedPenetrationPower += InstigatorPerk.GetPenetrationModifier( KFPC.GetLevel(), class<KFDamageType>(InstantHitDamageTypes[FiringMode]) );
  3489. }
  3490. }
  3491.  
  3492. return UsedPenetrationPower;
  3493. }
  3494.  
  3495. /**
  3496. * See Pawn.ProcessInstantHit
  3497. * @param DamageReduction: Custom KF parameter to handle penetration damage reduction
  3498. */
  3499. simulated function ProcessInstantHitEx(byte FiringMode, ImpactInfo Impact, optional int NumHits, optional out float out_PenetrationVal, optional int ImpactNum)
  3500. {
  3501. local int TotalDamage;
  3502. local KActorFromStatic NewKActor;
  3503. local StaticMeshComponent HitStaticMesh;
  3504. local InterpCurveFloat PenetrationCurve;
  3505. local KFPawn KFP;
  3506. local float InitialPenetrationPower, OriginalPenetrationVal;
  3507. local KFPerk CurrentPerk;
  3508. local bool bNoPenetrationDmgReduction;
  3509.  
  3510.  
  3511. if (Impact.HitActor != None)
  3512. {
  3513. OriginalPenetrationVal = out_PenetrationVal;
  3514.  
  3515. // default damage model is just hits * base damage
  3516. NumHits = Max(NumHits, 1);
  3517. TotalDamage = InstantHitDamage[FiringMode] * NumHits;
  3518.  
  3519. if ( Impact.HitActor.bWorldGeometry )
  3520. {
  3521. HitStaticMesh = StaticMeshComponent(Impact.HitInfo.HitComponent);
  3522. if ( !WorldInfo.bDropDetail && WorldInfo.GetDetailMode() != DM_Low &&
  3523. (HitStaticMesh != None) && HitStaticMesh.CanBecomeDynamic() )
  3524. {
  3525. NewKActor = class'KActorFromStatic'.Static.MakeDynamic(HitStaticMesh);
  3526. if ( NewKActor != None )
  3527. {
  3528. Impact.HitActor = NewKActor;
  3529. }
  3530. }
  3531. }
  3532. // Handle PenetrationDamageReduction / DamageModifier if the weapon has penetration
  3533. else if ( Impact.HitActor.bCanBeDamaged && GetInitialPenetrationPower(FiringMode) > 0 )
  3534. {
  3535. if ( out_PenetrationVal <= 0 )
  3536. {
  3537. return;
  3538. }
  3539. else
  3540. {
  3541. CurrentPerk = GetPerk();
  3542. if( CurrentPerk != none )
  3543. {
  3544. bNoPenetrationDmgReduction = CurrentPerk.IgnoresPenetrationDmgReduction();
  3545. }
  3546.  
  3547. PenetrationCurve = PenetrationDamageReductionCurve[FiringMode];
  3548. if( !bNoPenetrationDmgReduction )
  3549. {
  3550. TotalDamage *= EvalInterpCurveFloat(PenetrationCurve, out_PenetrationVal/GetInitialPenetrationPower(FiringMode));
  3551. }
  3552.  
  3553. // Reduce penetration power for every KFPawn penetrated
  3554. KFP = KFPawn(Impact.HitActor);
  3555. if ( KFP != none )
  3556. {
  3557. out_PenetrationVal -= KFP.PenetrationResistance;
  3558. }
  3559. }
  3560. }
  3561.  
  3562. // The the skill tracking know that we got our initial impact for this shot
  3563. if( KFPlayer != none && KFPawn_Monster(Impact.HitActor) != none )
  3564. {
  3565. InitialPenetrationPower = GetInitialPenetrationPower(FiringMode);
  3566.  
  3567. if( InitialPenetrationPower <= 0 || OriginalPenetrationVal == InitialPenetrationPower )
  3568. {
  3569. KFPlayer.AddShotsHit(1);
  3570. }
  3571. }
  3572.  
  3573. Impact.HitActor.TakeDamage( TotalDamage, Instigator.Controller,
  3574. Impact.HitLocation, InstantHitMomentum[FiringMode] * Impact.RayDir,
  3575. InstantHitDamageTypes[FiringMode], Impact.HitInfo, self );
  3576. }
  3577. }
  3578.  
  3579. /**
  3580. * Handle a bullet this weapon has fired for client side hit detection impacting a Pawn
  3581. * see 'InstantFireClient'
  3582. */
  3583. simulated function HandleProjectileImpact(byte ProjectileFireMode, ImpactInfo Impact, optional float PenetrationValue)
  3584. {
  3585. // local player only for clientside hit detection
  3586. if ( Instigator != None && Instigator.IsLocallyControlled() )
  3587. {
  3588. if ( Instigator.Role < ROLE_Authority )
  3589. {
  3590. SendClientProjectileImpact(ProjectileFireMode, Impact, PenetrationValue);
  3591. }
  3592.  
  3593. ProcessInstantHitEx(ProjectileFireMode, Impact,, PenetrationValue, 0);
  3594. }
  3595. }
  3596.  
  3597. /** process local player fragment impact for clientside hit detection */
  3598. event RecieveClientFragmentImpact(const out ImpactInfo Impact, class<KFProjectile> Fragment)
  3599. {
  3600. ProcessGrenadeProjectileImpact(Impact, Fragment);
  3601. }
  3602.  
  3603. /**
  3604. * Handle a fragment for client side hit detection impacting a Pawn
  3605. * @param Impact: The hit to process
  3606. * @param Fragment: The type of fragment that impacted
  3607. */
  3608. simulated function HandleGrenadeProjectileImpact(ImpactInfo Impact, class<KFProjectile> Fragment)
  3609. {
  3610. // local player only for clientside hit detection
  3611. if ( Instigator != None && Instigator.IsLocallyControlled() )
  3612. {
  3613. if ( Instigator.Role < ROLE_Authority )
  3614. {
  3615. SendClientFragmentImpact(Impact,Fragment);
  3616. }
  3617.  
  3618. ProcessGrenadeProjectileImpact(Impact, Fragment);
  3619. }
  3620. }
  3621.  
  3622. /**
  3623. * Process the damage for a grenade fragment impact
  3624. * @param Impact: The hit to process
  3625. * @param Fragment: The type of fragment that impacted
  3626. */
  3627. simulated function ProcessGrenadeProjectileImpact(ImpactInfo Impact, class<KFProjectile> Fragment)
  3628. {
  3629. local int TotalDamage;
  3630. local KActorFromStatic NewKActor;
  3631. local StaticMeshComponent HitStaticMesh;
  3632.  
  3633. if (Impact.HitActor != None)
  3634. {
  3635. TotalDamage = Fragment.default.Damage;
  3636.  
  3637. if ( Impact.HitActor.bWorldGeometry )
  3638. {
  3639. HitStaticMesh = StaticMeshComponent(Impact.HitInfo.HitComponent);
  3640. if ( (HitStaticMesh != None) && HitStaticMesh.CanBecomeDynamic() )
  3641. {
  3642. NewKActor = class'KActorFromStatic'.Static.MakeDynamic(HitStaticMesh);
  3643. if ( NewKActor != None )
  3644. {
  3645. Impact.HitActor = NewKActor;
  3646. }
  3647. }
  3648. }
  3649.  
  3650. Impact.HitActor.TakeDamage( TotalDamage, Instigator.Controller,
  3651. Impact.HitLocation, Fragment.default.MomentumTransfer * Impact.RayDir,
  3652. Fragment.default.MyDamageType, Impact.HitInfo, Instigator );
  3653. }
  3654. }
  3655.  
  3656. /** Can be overridden on a per-weapon or per-state basis */
  3657. simulated function bool IsHeavyWeapon();
  3658.  
  3659. /*********************************************************************************************
  3660. * @name Clientside Hit Detection
  3661. *********************************************************************************************/
  3662.  
  3663. // Create RPC's of various sizes since we can't replicate dynamic arrays. Don't need to be too exact here
  3664. // as there is only a small overhead for calling another RPC (e.g. 2 + 8 = 10)
  3665. // see https://udn.unrealengine.com/questions/158710/efficient-array-replication.html
  3666. reliable server final private native event ServerRegisterImpact1(byte FiringMode, ImpactRepInfo NetImpact);
  3667. reliable server final private native event ServerRegisterImpact2(byte FiringMode, ImpactRepInfo NetImpacts[2]);
  3668. reliable server final private native event ServerRegisterImpact3(byte FiringMode, ImpactRepInfo NetImpacts[3]);
  3669. reliable server final private native event ServerRegisterImpact4(byte FiringMode, ImpactRepInfo NetImpacts[4]);
  3670. reliable server final private native event ServerRegisterImpact5(byte FiringMode, ImpactRepInfo NetImpacts[5]);
  3671. reliable server final private native event ServerRegisterImpact6(byte FiringMode, ImpactRepInfo NetImpacts[6]);
  3672. reliable server final private native event ServerRegisterImpact7(byte FiringMode, ImpactRepInfo NetImpacts[7]);
  3673. reliable server final private native event ServerRegisterImpact8(byte FiringMode, ImpactRepInfo NetImpacts[8]);
  3674. reliable server final private native event ServerRegisterProjectileImpact(byte FiringMode, ImpactRepInfo NetImpact, optional byte PenetrationByte);
  3675. // special ammo types
  3676. reliable server final private native event ServerRegisterProjectileExplosion(vector ExplosionLocation, KFProjectile ExplodingProjectile);
  3677. reliable server final private native event ServerRegisterFragmentImpact(ImpactRepInfo NetImpact, class<KFProjectile> FragmentClass);
  3678.  
  3679. // Compress our impact into a ImpactRepInfo for replication (returns number of impacts sent)
  3680. simulated protected native function int SendClientImpactList(byte FiringMode, const array<ImpactInfo> ImpactList);
  3681. // Same as SendClientImpactList, but recieves a single impact with a penetration value
  3682. simulated protected native function int SendClientProjectileImpact(byte FiringMode, const out ImpactInfo Impact, float PenetrationFloat);
  3683. // Replicate a projectile explosion to the server
  3684. simulated protected native function int SendClientProjectileExplosion(vector ExplosionLocation, KFProjectile ExplodingProjectile);
  3685. // Compress our fragment impact into a ImpactRepInfo for replication (returns number of impacts sent)
  3686. simulated protected native function int SendClientFragmentImpact(const out ImpactInfo Impact, class<KFProjectile> FragmentClass);
  3687. simulated protected native function vector GetSafeStartTraceLocation();
  3688.  
  3689. /**
  3690. * Allows subclass to perform extra bullet traces
  3691. * Network: Local Player
  3692. */
  3693. simulated function InstantFireClient_AddImpacts(vector StartTrace, rotator Aim, out array<ImpactInfo> ImpactList);
  3694.  
  3695. /**
  3696. * Performs an 'Instant Hit' shot.
  3697. * Network: Local Player and Server
  3698. */
  3699. simulated function InstantFireClient()
  3700. {
  3701. local vector StartTrace, EndTrace;
  3702. local rotator AimRot;
  3703. local Array<ImpactInfo> ImpactList;
  3704. local int Idx;
  3705. local ImpactInfo RealImpact;
  3706. local float CurPenetrationValue;
  3707.  
  3708. // see Controller AimHelpDot() / AimingHelp()
  3709. bInstantHit = true;
  3710.  
  3711. // define range to use for CalcWeaponFire()
  3712. StartTrace = GetSafeStartTraceLocation();
  3713. AimRot = GetAdjustedAim(StartTrace);
  3714. EndTrace = StartTrace + vector(AimRot) * GetTraceRange();
  3715.  
  3716. bInstantHit = false;
  3717.  
  3718. // Initialize penetration power
  3719. PenetrationPowerRemaining = GetInitialPenetrationPower(CurrentFireMode);
  3720. CurPenetrationValue = PenetrationPowerRemaining;
  3721.  
  3722. // Perform shot
  3723. RealImpact = CalcWeaponFire(StartTrace, EndTrace, ImpactList);
  3724.  
  3725. // Set flash location to trigger client side effects. Bypass Weapon.SetFlashLocation since
  3726. // that function is not marked as simulated and we want instant client feedback.
  3727. // ProjectileFire/IncrementFlashCount has the right idea:
  3728. // 1) Call IncrementFlashCount on Server & Local
  3729. // 2) Replicate FlashCount if ( !bNetOwner )
  3730. // 3) Call WeaponFired() once on local player
  3731. if( Instigator != None )
  3732. {
  3733. Instigator.SetFlashLocation( Self, CurrentFireMode, RealImpact.HitLocation );
  3734. }
  3735.  
  3736. // local player only for clientside hit detection
  3737. if ( Instigator != None && Instigator.IsLocallyControlled() )
  3738. {
  3739. // allow weapon to add extra bullet impacts (useful for shotguns)
  3740. InstantFireClient_AddImpacts(StartTrace, AimRot, ImpactList);
  3741.  
  3742. for (Idx = 0; Idx < ImpactList.Length; Idx++)
  3743. {
  3744. ProcessInstantHitEx(CurrentFireMode, ImpactList[Idx],, CurPenetrationValue, Idx);
  3745. }
  3746.  
  3747. if ( Instigator.Role < ROLE_Authority )
  3748. {
  3749. SendClientImpactList(CurrentFireMode, ImpactList);
  3750. }
  3751. }
  3752. }
  3753.  
  3754. /** process local player impact for clientside hit detection */
  3755. event RecieveClientImpact(byte FiringMode, const out ImpactInfo Impact, optional out float PenetrationValue, optional int ImpactNum)
  3756. {
  3757.  
  3758. if( FiringMode == BASH_FIREMODE )
  3759. {
  3760. MeleeAttackHelper.ProcessMeleeHit(FiringMode, Impact);
  3761. }
  3762. else
  3763. {
  3764. if( KFPawn_Monster(Impact.HitActor) != none && PenetrationValue < GetInitialPenetrationPower( FiringMode ) )
  3765. {
  3766. ImpactNum = 1;
  3767. }
  3768.  
  3769. ProcessInstantHitEx(FiringMode, Impact,, PenetrationValue, ImpactNum);
  3770. }
  3771. }
  3772.  
  3773. /** process local player explosion for clientside explosion handling */
  3774. event RecieveClientProjectileExplosion(vector ExplosionLocation, KFProjectile ExplodingProjectile)
  3775. {
  3776. // TODO: maybe validate the explosion normal on the client?
  3777. ExplodingProjectile.CallExplode( ExplosionLocation, vect(0,0,1) );
  3778. }
  3779.  
  3780. /**
  3781. * Replicate client side explosion to server
  3782. */
  3783. simulated function HandleClientProjectileExplosion(vector ExplosionLocation, KFProjectile ExplodingProjectile)
  3784. {
  3785. SendClientProjectileExplosion(ExplosionLocation, ExplodingProjectile);
  3786. }
  3787.  
  3788. /** process local player impact for clientside hit detection */
  3789. event RecieveClientImpactList(byte FiringMode, array<ImpactInfo> ImpactList)
  3790. {
  3791. // deprecated, all impacts go through RecieveClientImpact
  3792. }
  3793.  
  3794. /*********************************************************************************************
  3795. * @name Ammunition
  3796. *********************************************************************************************/
  3797.  
  3798. /**
  3799. * Returns which ammo pool a fire mode should pull from, primary or secondary.
  3800. @ param FiringMode the fire mode we want to check against
  3801. @ return the ammo pool we should pull from for the input fire mode
  3802. */
  3803. simulated function int GetAmmoType(byte FiringMode)
  3804. {
  3805. if( FiringMode == ALTFIRE_FIREMODE && UsesSecondaryAmmo() )
  3806. {
  3807. return 1;
  3808. }
  3809. else
  3810. {
  3811. return 0;
  3812. }
  3813. }
  3814.  
  3815. /**
  3816. * Returns true if this weapon uses a secondary ammo pool
  3817. */
  3818. static simulated event bool UsesAmmo()
  3819. {
  3820. return default.SpareAmmoCapacity[0] > 0;
  3821. }
  3822.  
  3823. /**
  3824. * Returns true if this weapon uses a secondary ammo pool
  3825. */
  3826. static simulated event bool UsesSecondaryAmmo()
  3827. {
  3828. return default.MagazineCapacity[1] > 0;
  3829. }
  3830.  
  3831. /**
  3832. * Returns true if this weapon uses a secondary ammo pool
  3833. */
  3834. static simulated event bool CanRefillSecondaryAmmo()
  3835. {
  3836. return UsesSecondaryAmmo() && default.bCanRefillSecondaryAmmo;
  3837. }
  3838.  
  3839. /**
  3840. * Initializes ammo counts, when weapon is spawned.
  3841. */
  3842. function InitializeAmmo()
  3843. {
  3844. local KFPerk CurrentPerk;
  3845.  
  3846. CurrentPerk = GetPerk();
  3847.  
  3848. // Let the perk change mag numbers etc if needed
  3849. if( CurrentPerk != none )
  3850. {
  3851. CurrentPerk.ModifyMagSizeAndNumber( self, MagazineCapacity[0] );
  3852. CurrentPerk.ModifyMagSizeAndNumber( self, MagazineCapacity[1],, true );
  3853.  
  3854. CurrentPerk.ModifyMaxSpareAmmoAmount( self, SpareAmmoCapacity[0] );
  3855. CurrentPerk.ModifyMaxSpareAmmoAmount( self, SpareAmmoCapacity[1],, true );
  3856. }
  3857.  
  3858. AmmoCount[0] = MagazineCapacity[0];
  3859. AmmoCount[1] = MagazineCapacity[1];
  3860.  
  3861. AddAmmo(InitialSpareMags[0] * default.MagazineCapacity[0]);
  3862.  
  3863. if( CurrentPerk != none )
  3864. {
  3865. CurrentPerk.ModifySpareAmmoAmount(self, SpareAmmoCount[0]);
  3866. CurrentPerk.ModifySpareAmmoAmount(self, SpareAmmoCount[1],, true);
  3867. }
  3868.  
  3869. // HACK: Finalize our spare ammo values
  3870. AddAmmo(0);
  3871.  
  3872. bForceNetUpdate = TRUE;
  3873. }
  3874.  
  3875. /**
  3876. * @brief Re-initializes ammo counts after perk and/or perk skills has/have changed.
  3877. */
  3878. function ReInitializeAmmoCounts(KFPerk CurrentPerk)
  3879. {
  3880. if( CurrentPerk != none )
  3881. {
  3882. MagazineCapacity[0] = default.MagazineCapacity[0];
  3883. MagazineCapacity[1] = default.MagazineCapacity[1];
  3884. SpareAmmoCapacity[0] = default.SpareAmmoCapacity[0];
  3885. SpareAmmoCapacity[1] = default.SpareAmmoCapacity[1];
  3886.  
  3887. CurrentPerk.ModifyMagSizeAndNumber( self, MagazineCapacity[0] );
  3888. CurrentPerk.ModifyMagSizeAndNumber( self, MagazineCapacity[1],, true );
  3889.  
  3890. CurrentPerk.ModifyMaxSpareAmmoAmount( self, SpareAmmoCapacity[0] );
  3891. CurrentPerk.ModifyMaxSpareAmmoAmount( self, SpareAmmoCapacity[1],, true );
  3892.  
  3893. // We don't want to modify current ammo, but spare ammo should respect perk and level
  3894. AddAmmo(0);
  3895. }
  3896. }
  3897.  
  3898. /**
  3899. * @see Weapon::ConsumeAmmo
  3900. */
  3901. simulated function ConsumeAmmo( byte FireModeNum )
  3902. {
  3903. local byte AmmoType;
  3904. local KFPerk InstigatorPerk;
  3905.  
  3906. `if(`notdefined(ShippingPC))
  3907. if( bInfiniteAmmo )
  3908. {
  3909. return;
  3910. }
  3911. `endif
  3912.  
  3913. AmmoType = GetAmmoType(FireModeNum);
  3914.  
  3915. InstigatorPerk = GetPerk();
  3916. if( InstigatorPerk != none && InstigatorPerk.GetIsUberAmmoActive( self ) )
  3917. {
  3918. return;
  3919. }
  3920.  
  3921. // If AmmoCount is being replicated, don't allow the client to modify it here
  3922. if ( Role == ROLE_Authority || bAllowClientAmmoTracking )
  3923. {
  3924. // Don't consume ammo if magazine size is 0 (infinite ammo with no reload)
  3925. if (MagazineCapacity[AmmoType] > 0 && AmmoCount[AmmoType] > 0)
  3926. {
  3927. // Ammo cost needs to be firemodenum because it is independent of ammo type.
  3928. AmmoCount[AmmoType] = Max(AmmoCount[AmmoType] - AmmoCost[FireModeNum], 0);
  3929. }
  3930. }
  3931. }
  3932.  
  3933. /**
  3934. * @see Weapon::AddAmmo
  3935. */
  3936. function int AddAmmo(int Amount)
  3937. {
  3938. local int OldSpareAmmo;
  3939.  
  3940. // If we can't accept spare ammo, then abort
  3941. if( GetMaxAmmoAmount(0) <= 0 )
  3942. {
  3943. return 0;
  3944. }
  3945.  
  3946. `log(self@"Add Amount =" @ Amount, bLogAmmo);
  3947. `log(self@"SpareAmmoCapacity[0] =" @ GetMaxAmmoAmount(0), bLogAmmo);
  3948. `log(self@"SpareAmmoCount[0] =" @ SpareAmmoCount[0], bLogAmmo);
  3949. `log(self@"AmmoCount[0] =" @ AmmoCount[0], bLogAmmo);
  3950.  
  3951. OldSpareAmmo = SpareAmmoCount[0];
  3952. SpareAmmoCount[0] = Min(SpareAmmoCount[0] + Amount, GetMaxAmmoAmount(0) - AmmoCount[0]);
  3953. bForceNetUpdate = TRUE;
  3954.  
  3955. `log(self@"SpareAmmoCount[0] AFTER =" @ SpareAmmoCount[0], bLogAmmo);
  3956. `log(self@"Added" @ SpareAmmoCount[0] - OldSpareAmmo @ "Ammo", bLogAmmo);
  3957.  
  3958. return SpareAmmoCount[0] - OldSpareAmmo;
  3959. }
  3960.  
  3961. /** Add primary and secondary ammo to weapon based on trader transactions. Amounts could be negative. */
  3962. function AddTransactionAmmo( int TransactionPrimaryAmmo, int TransactionSecondaryAmmo )
  3963. {
  3964. local int SpareAmmoToAdd, ExtraTransactionAmmo;
  3965.  
  3966. if( TransactionPrimaryAmmo >= 0 )
  3967. {
  3968. AddAmmo( TransactionPrimaryAmmo );
  3969. }
  3970. else
  3971. {
  3972. // only subtract as much spare ammo as the gun actually has
  3973. // (the passed-in values are from the trader/UI and include default spare and main ammo)
  3974. SpareAmmoToAdd = Max( -SpareAmmoCount[0], TransactionPrimaryAmmo );
  3975.  
  3976. // if there isn't enough spare ammo to subtract, keep track of the extra and subtract it from the main ammo
  3977. ExtraTransactionAmmo = TransactionPrimaryAmmo - SpareAmmoToAdd;
  3978.  
  3979. // subtract from spare ammo
  3980. AddAmmo( SpareAmmoToAdd );
  3981.  
  3982. // subtract any extra from ammo
  3983. AmmoCount[0] = Max( 0, AmmoCount[0] + ExtraTransactionAmmo );
  3984.  
  3985. ClientForceAmmoUpdate(AmmoCount[0], SpareAmmoCount[0]);
  3986. }
  3987.  
  3988. AddSecondaryAmmo( TransactionSecondaryAmmo );
  3989. }
  3990.  
  3991. /**
  3992. * Add secondary ammo to weapon
  3993. * @param Amount to add.
  3994. * @return Amount actually added. (In case magazine is already full and some ammo is left
  3995. *
  3996. */
  3997. function int AddSecondaryAmmo(int Amount)
  3998. {
  3999. local int OldAmmo;
  4000.  
  4001. // If we can't accept spare ammo, then abort
  4002. if( !CanRefillSecondaryAmmo() )
  4003. {
  4004. return 0;
  4005. }
  4006.  
  4007. OldAmmo = AmmoCount[1];
  4008. AmmoCount[1] = Min(AmmoCount[1] + Amount, MagazineCapacity[1]);
  4009. bForceNetUpdate = TRUE;
  4010.  
  4011. // unlike AddAmmo, we're updating AmmoCount directly which if ClientAmmoTracking is enabled
  4012. // will not be replicated. Needs for replication via RPC
  4013. if( bAllowClientAmmoTracking )
  4014. {
  4015. ClientForceSecondaryAmmoUpdate(AmmoCount[1]);
  4016. }
  4017.  
  4018. return AmmoCount[1] - OldAmmo;
  4019. }
  4020.  
  4021. /**
  4022. * Force client to match server ammo counts
  4023. *
  4024. * @param bAmmoSync - If performing a sync the lowest value is typically the most correct
  4025. */
  4026. reliable client function ClientForceAmmoUpdate(byte NewAmmoCount, int NewSpareAmmoCount, optional bool bAmmoSync)
  4027. {
  4028. if ( Role < ROLE_Authority )
  4029. {
  4030. if ( bAmmoSync )
  4031. {
  4032. // detect sync errors
  4033. `log(self@GetFuncName()@"ServerAmmo Primary:"$NewAmmoCount@"ClientAmmo Primary:"$AmmoCount[0]@"ServerSpareAmmo Primary:"$NewSpareAmmoCount@"ClientSpareAmmo Primary:"$SpareAmmoCount[0], bDebug);
  4034. if ( NewAmmoCount != AmmoCount[0] )
  4035. {
  4036. `log(self@GetFuncName()@"Primary Ammo mismatch Server:"$NewAmmoCount@"Client:"$AmmoCount[0]);
  4037. }
  4038. if ( NewSpareAmmoCount != SpareAmmoCount[0] )
  4039. {
  4040. `log(self@GetFuncName()@"Primary SpareAmmo mismatch Server:"$NewSpareAmmoCount@"Client:"$SpareAmmoCount[0]);
  4041. }
  4042.  
  4043. AmmoCount[0] = Min(NewAmmoCount, AmmoCount[0]);
  4044. SpareAmmoCount[0] = Min(NewSpareAmmoCount, SpareAmmoCount[0]);
  4045. }
  4046. else
  4047. {
  4048. AmmoCount[0] = NewAmmoCount;
  4049. SpareAmmoCount[0] = NewSpareAmmoCount;
  4050. }
  4051. NotifyHUDofWeapon(Pawn(Owner));
  4052. }
  4053. }
  4054.  
  4055. /** Force client to match server secondary ammo counts */
  4056. reliable client function ClientForceSecondaryAmmoUpdate(byte NewSecondaryAmmoCount)
  4057. {
  4058. if ( Role < ROLE_Authority )
  4059. {
  4060. // detect sync errors
  4061. `log(self@GetFuncName()@"ServerAmmo Secondary:"$NewSecondaryAmmoCount@"ClientAmmo Secondary:"$AmmoCount[1], bDebug);
  4062. if ( NewSecondaryAmmoCount != AmmoCount[1] )
  4063. {
  4064. `log(self@GetFuncName()@"Secondary Ammo mismatch Server:"$NewSecondaryAmmoCount@"Client:"$AmmoCount[1]);
  4065. }
  4066. AmmoCount[1] = NewSecondaryAmmoCount;
  4067. NotifyHUDofWeapon(Pawn(Owner));
  4068. }
  4069. }
  4070.  
  4071. /** Notify the client that their weapon has been picked up and ready to show */
  4072. unreliable client function NotifyHUDofWeapon( Pawn P )
  4073. {
  4074. local KFPlayerController KFPC;
  4075.  
  4076. if ( Role < ROLE_Authority )
  4077. {
  4078. KFPC = KFPlayerController(P.Owner);
  4079.  
  4080. if(KFPC != none && KFPC.MyGFxHUD != none)
  4081. {
  4082. KFPC.MyGFxHUD.NotifyHUDofWeapon();
  4083. }
  4084. }
  4085. }
  4086.  
  4087. /**
  4088. * @see Weapon::HasAmmo
  4089. */
  4090. simulated event bool HasAmmo( byte FireModeNum, optional int Amount )
  4091. {
  4092. local KFPerk InstigatorPerk;
  4093. // we can always do a melee attack
  4094. if( FireModeNum == BASH_FIREMODE )
  4095. {
  4096. return TRUE;
  4097. }
  4098. else if ( FireModeNum == RELOAD_FIREMODE )
  4099. {
  4100. return CanReload();
  4101. }
  4102. else if ( FireModeNum == GRENADE_FIREMODE )
  4103. {
  4104. if( KFInventoryManager(InvManager) != none )
  4105. {
  4106. return KFInventoryManager(InvManager).HasGrenadeAmmo(Amount);
  4107. }
  4108. }
  4109.  
  4110. InstigatorPerk = GetPerk();
  4111. if( InstigatorPerk != none && InstigatorPerk.GetIsUberAmmoActive( self ) )
  4112. {
  4113. return true;
  4114. }
  4115.  
  4116. // If passed in ammo isn't set, use default ammo cost.
  4117. if( Amount == 0 )
  4118. {
  4119. Amount = AmmoCost[FireModeNum];
  4120. }
  4121.  
  4122. return AmmoCount[GetAmmoType(FireModeNum)] >= Amount;
  4123. }
  4124.  
  4125. /**
  4126. * @see Weapon::HasAnyAmmo
  4127. */
  4128. simulated function bool HasAnyAmmo()
  4129. {
  4130. if ( HasSpareAmmo() || HasAmmo(DEFAULT_FIREMODE) )
  4131. {
  4132. return true;
  4133. }
  4134.  
  4135. if( UsesSecondaryAmmo() && (HasSpareAmmo(ALTFIRE_FIREMODE) || HasAmmo(ALTFIRE_FIREMODE) ))
  4136. {
  4137. return true;
  4138. }
  4139.  
  4140. return false;
  4141. }
  4142.  
  4143. /** Returns true if spare ammo is available (outside of current magazine) */
  4144. simulated event bool HasSpareAmmo(optional byte FireModeNum)
  4145. {
  4146. return (bInfiniteSpareAmmo || SpareAmmoCount[GetAmmoType(FireModeNum)] > 0);
  4147. }
  4148.  
  4149. simulated event int GetTotalAmmoAmount(byte FiringMode)
  4150. {
  4151. return AmmoCount[GetAmmoType(FiringMode)] + SpareAmmoCount[GetAmmoType(FiringMode)];
  4152. }
  4153.  
  4154. simulated event int GetMaxAmmoAmount(byte FiringMode)
  4155. {
  4156. return SpareAmmoCapacity[GetAmmoType(FiringMode)] + MagazineCapacity[GetAmmoType(FiringMode)];
  4157. }
  4158.  
  4159. simulated event int GetMissingSpareAmmoAmount(byte FiringMode)
  4160. {
  4161. return SpareAmmoCapacity[GetAmmoType(FiringMode)] - SpareAmmoCount[GetAmmoType(FiringMode)];
  4162. }
  4163.  
  4164. simulated function float GetAmmoPercentage(optional byte FiringMode)
  4165. {
  4166. if( SpareAmmoCapacity[GetAmmoType(FiringMode)] == 0 )
  4167. {
  4168. return -1.f;
  4169. }
  4170.  
  4171. return float(GetTotalAmmoAmount(FiringMode)) / float(GetMaxAmmoAmount(FiringMode));
  4172. }
  4173.  
  4174. /** Determines the ammo left in magazine for HUD display */
  4175. simulated function int GetSpareAmmoForHUD()
  4176. {
  4177. return SpareAmmoCount[0];
  4178. }
  4179.  
  4180. /** Determines the secondary ammo left for HUD display */
  4181. simulated function int GetSecondaryAmmoForHUD()
  4182. {
  4183. return AmmoCount[1] + SpareAmmoCount[1];
  4184. }
  4185.  
  4186. /**
  4187. * Called by Player to force current weapon to be reloaded
  4188. * NOTE: A reload will only begin once the weapons FireInterval has completed
  4189. * Network: LocalPlayer
  4190. */
  4191. simulated function bool ForceReload()
  4192. {
  4193. // Make sure it's called for a locally controlled pawn.
  4194. if (Instigator != None && Instigator.IsLocallyControlled())
  4195. {
  4196. // if we can reload,
  4197. if( CanReload() )
  4198. {
  4199. StartFire(RELOAD_FIREMODE);
  4200. return true;
  4201. }
  4202. }
  4203.  
  4204. return false;
  4205. }
  4206.  
  4207. /** Returns true if weapon can potentially be reloaded */
  4208. simulated function bool CanReload(optional byte FireModeNum)
  4209. {
  4210. local KFPawn P;
  4211. local bool bInstigatorCanReload;
  4212. local byte AmmoType;
  4213.  
  4214. P = KFPawn(Instigator);
  4215.  
  4216. AmmoType = GetAmmoType(FireModeNum);
  4217.  
  4218. // Cannot reload weapon when doing a special move
  4219. bInstigatorCanReload = (P != None && P.CanReloadWeapon());
  4220.  
  4221. return( bCanBeReloaded && // Weapon can be reloaded
  4222. bInstigatorCanReload && // Instigator can reload weapon
  4223. AmmoCount[AmmoType] < MagazineCapacity[AmmoType] && // we fired at least a shot
  4224. HasSpareAmmo(AmmoType) // and we have more ammo to fill current magazine
  4225. );
  4226. }
  4227.  
  4228. /** Returns true if weapon should be auto-reloaded */
  4229. simulated function bool ShouldAutoReload(byte FireModeNum)
  4230. {
  4231. local bool bHasAmmo;
  4232.  
  4233. bHasAmmo = HasAmmo(FireModeNum);
  4234.  
  4235. // do dry fire or auto switch
  4236. if( !bHasAmmo && Instigator != none && Instigator.IsLocallyControlled() )
  4237. {
  4238. if ( bPendingAutoSwitchOnDryFire )
  4239. {
  4240. if( CanSwitchWeapons() )
  4241. {
  4242. if ( !HasAnyAmmo() )
  4243. {
  4244. Instigator.Controller.ClientSwitchToBestWeapon(false);
  4245. }
  4246. }
  4247.  
  4248. bPendingAutoSwitchOnDryFire = false;
  4249. }
  4250. else
  4251. {
  4252. WeaponPlaySound(WeaponDryFireSnd[FireModeNum]);
  4253. if( Role < ROLE_Authority )
  4254. {
  4255. ServerPlayDryFireSound(FireModeNum);
  4256. }
  4257. bPendingAutoSwitchOnDryFire = !HasAnyAmmo();
  4258. }
  4259. }
  4260.  
  4261. return !bHasAmmo && CanReload();
  4262. }
  4263.  
  4264. simulated function bool ShouldAutoReloadGunslinger( byte FireModeNum )
  4265. {
  4266. local KFPerk InstigatorPerk;
  4267. local KFWeapon NewWeapon;
  4268. local KFInventoryManager KFIM;
  4269. local bool bHasAmmo;
  4270.  
  4271. bHasAmmo = HasAmmo( FireModeNum );
  4272.  
  4273. InstigatorPerk = GetPerk();
  4274. if( !bHasAmmo && CanSwitchWeapons() && InstigatorPerk != none &&
  4275. InstigatorPerk.ShouldInstantlySwitchWeapon( self ) )
  4276. {
  4277. KFIM = KFInventoryManager(InvManager);
  4278. if( KFIM != none )
  4279. {
  4280. NewWeapon = KFIM.GetBestPerkWeaponWithAmmo( InstigatorPerk.class, true );
  4281. if( NewWeapon != none )
  4282. {
  4283. KFIM.SetCurrentWeapon( NewWeapon );
  4284. return false;
  4285. }
  4286. }
  4287. }
  4288.  
  4289. // do dry fire or auto switch
  4290. if( !bHasAmmo && Instigator != none && Instigator.IsLocallyControlled() )
  4291. {
  4292. if( bPendingAutoSwitchOnDryFire )
  4293. {
  4294. if( CanSwitchWeapons() )
  4295. {
  4296. if( !HasAnyAmmo() )
  4297. {
  4298. Instigator.Controller.ClientSwitchToBestWeapon(false);
  4299. }
  4300. }
  4301.  
  4302. bPendingAutoSwitchOnDryFire = false;
  4303. }
  4304. else
  4305. {
  4306. WeaponPlaySound( WeaponDryFireSnd[FireModeNum] );
  4307.  
  4308. if( Role < ROLE_Authority )
  4309. {
  4310. ServerPlayDryFireSound(FireModeNum);
  4311. }
  4312. bPendingAutoSwitchOnDryFire = !HasAnyAmmo();
  4313. }
  4314. }
  4315.  
  4316. return !bHasAmmo && CanReload();
  4317. }
  4318.  
  4319. /** Play the dry fire sound on the server */
  4320. reliable server protected function ServerPlayDryFireSound(byte FireModeNum)
  4321. {
  4322. WeaponPlaySound(WeaponDryFireSnd[FireModeNum]);
  4323. }
  4324.  
  4325. /*********************************************************************************************
  4326. * @name Rendering Methods
  4327. ********************************************************************************************* */
  4328.  
  4329. /** Set the lighting channels on all the appropriate weapon meshes */
  4330. simulated function SetMeshLightingChannels(LightingChannelContainer NewLightingChannels)
  4331. {
  4332. Mesh.SetLightingChannels(NewLightingChannels);
  4333.  
  4334. if( LaserSight != none )
  4335. {
  4336. LaserSight.SetMeshLightingChannels(NewLightingChannels);
  4337. }
  4338. }
  4339.  
  4340. /*********************************************************************************************
  4341. * @name Debug info
  4342. *********************************************************************************************/
  4343.  
  4344. /**
  4345. * list important Weapon variables on canvas. HUD will call DisplayDebug() on the current ViewTarget when
  4346. * the ShowDebug exec is used
  4347. *
  4348. * @param HUD - HUD with canvas to draw on
  4349. * @input out_YL - Height of the current font
  4350. * @input out_YPos - Y position on Canvas. out_YPos += out_YL, gives position to draw text for next debug line.
  4351. */
  4352.  
  4353. simulated function GetWeaponDebug( out Array<String> DebugInfo )
  4354. {
  4355. super.GetWeaponDebug( DebugInfo );
  4356.  
  4357. DebugInfo[DebugInfo.Length] = "AmmoCount Primary:" $ AmmoCount[0] @ "MagazineSize Primary:" $ MagazineCapacity[0];
  4358. DebugInfo[DebugInfo.Length] = "SpareAmmoCount Primary:" $ SpareAmmoCount[0] @ "SpareAmmoCapacity Primary:" $ GetMaxAmmoAmount(0);
  4359. DebugInfo[DebugInfo.Length] = "AmmoCount Secondary:" $ AmmoCount[1] @ "MagazineSize Secondary:" $ MagazineCapacity[1];
  4360. if( KFInventoryManager(InvManager) != none )
  4361. {
  4362. DebugInfo[DebugInfo.Length] = "GrenadeCount:" $ KFInventoryManager(InvManager).GrenadeCount @ "MaxGrenades:" $ GetPerk().MaxGrenadeCount;
  4363. }
  4364. }
  4365.  
  4366. /**
  4367. * list important LFWeapon variables on canvas. HUD will call DisplayDebug() on the current ViewTarget when
  4368. * the ShowDebug exec is used
  4369. *
  4370. * @param HUD - HUD with canvas to draw on
  4371. * @input out_YL - Height of the current font
  4372. * @input out_YPos - Y position on Canvas. out_YPos += out_YL, gives position to draw text for next debug line.
  4373. */
  4374. simulated function DisplayDebug(HUD HUD, out float out_YL, out float out_YPos)
  4375. {
  4376. local string CurAnim, CurHandAnim;
  4377. // Recoil Debugging
  4378. local vector FADebugLineStart,FADebugLineEnd,FADebugLineUL,FADebugLineUR,FADebugLineLL,FADebugLineLR;
  4379. local rotator FADebugLineRot, FADebugViewRotation;
  4380. local int UsedMaxYawLimit, UsedMaxPitchLimit, UsedMinYawLimit, UsedMinPitchLimit;
  4381. local Rotator AimRotation, WeaponBufferRot;
  4382.  
  4383. super.DisplayDebug(HUD, out_YL, out_YPos);
  4384.  
  4385. if (HUD.ShouldDisplayDebug('Camera'))
  4386. {
  4387. HUD.Canvas.SetPos(4,out_YPos);
  4388. HUD.Canvas.DrawText("---------- KFWeapon: Camera ----------");
  4389. out_YPos += out_YL;
  4390.  
  4391. HUD.Canvas.DrawText("WEAPONCAMERA MeshFOV="$MeshFOV @ "MeshIronSightFOV="$MeshIronSightFOV @ "CameraIronSightFOV="$PlayerIronSightFOV @" CurrentMeshFOV = "$MySkelMesh.FOV);
  4392. out_YPos += out_YL;
  4393. HUD.Canvas.SetPos(4, out_YPos);
  4394. }
  4395.  
  4396. if (HUD.ShouldDisplayDebug('Zooming'))
  4397. {
  4398. HUD.Canvas.SetPos(4,out_YPos);
  4399. HUD.Canvas.DrawText("---------- KFWeapon: Zooming ----------");
  4400. out_YPos += out_YL;
  4401.  
  4402. HUD.Canvas.DrawText("ZOOMING ZoomRotInterp="$ZoomRotInterp @ "bDoingQuickDownZoom="$bDoingQuickDownZoom @ "QuickWeaponDownRotation="$QuickWeaponDownRotation @" ZoomInRotation = "$ZoomInRotation$" PlayerViewOffset= "$PlayerViewOffset);
  4403. out_YPos += out_YL;
  4404. HUD.Canvas.SetPos(4, out_YPos);
  4405. }
  4406.  
  4407. if (HUD.ShouldDisplayDebug('FireOffset'))
  4408. {
  4409. HUD.Canvas.SetPos(4,out_YPos);
  4410. HUD.Canvas.DrawText("---------- KFWeapon: FireOffset ----------");
  4411. out_YPos += out_YL;
  4412.  
  4413. HUD.Canvas.DrawText("PROJECTILE SPAWNING FireOffset="$FireOffset);
  4414. out_YPos += out_YL;
  4415. HUD.Canvas.SetPos(4, out_YPos);
  4416.  
  4417. if(Instigator.IsFirstPerson())
  4418. {
  4419. HUD.DrawDebugSphere( GetMuzzleLoc(), 3, 6, 255, 0, 0 );
  4420. }
  4421. else
  4422. {
  4423. if( CurrentFireMode == GRENADE_FIREMODE )
  4424. {
  4425. HUD.DrawDebugSphere( (Instigator.GetPawnViewLocation() + (GrenadeFireOffset >> Instigator.Rotation)), 3, 6, 0, 255, 0 );
  4426. }
  4427. else
  4428. {
  4429. HUD.DrawDebugSphere( (Instigator.GetPawnViewLocation() + (FireOffset >> Instigator.Rotation)), 3, 6, 255, 0, 0 );
  4430. }
  4431. }
  4432. }
  4433.  
  4434. if (HUD.ShouldDisplayDebug('animation'))
  4435. {
  4436. HUD.Canvas.SetPos(4,out_YPos);
  4437. HUD.Canvas.DrawText("---------- KFWeapon: animation ----------");
  4438. out_YPos += out_YL;
  4439.  
  4440. if ( GetWeaponAnimNodeSeq() != none )
  4441. {
  4442. CurAnim = string(GetWeaponAnimNodeSeq().AnimSeqName);
  4443.  
  4444. HUD.Canvas.DrawText("WEAPON WeaponAnim="$CurAnim$" Rate: "$GetWeaponAnimNodeSeq().Rate$" Position: "$GetWeaponAnimNodeSeq().CurrentTime);
  4445. out_YPos += out_YL;
  4446. HUD.Canvas.SetPos(4, out_YPos);
  4447. }
  4448. if ( GetArmAnimNodeSeq() != none )
  4449. {
  4450. CurHandAnim = string(GetArmAnimNodeSeq().AnimSeqName);
  4451.  
  4452. HUD.Canvas.DrawText("WEAPON HandAnim="$CurHandAnim$" Rate: "$GetArmAnimNodeSeq().Rate$" Position: "$GetArmAnimNodeSeq().CurrentTime);
  4453. out_YPos += out_YL;
  4454. HUD.Canvas.SetPos(4, out_YPos);
  4455. }
  4456. }
  4457.  
  4458. if (HUD.ShouldDisplayDebug('aim3d'))
  4459. {
  4460. HUD.Canvas.SetPos(4,out_YPos);
  4461. HUD.Canvas.DrawText("---------- KFWeapon: aim3d ----------");
  4462. out_YPos += out_YL;
  4463.  
  4464. if( KFPlayerController(Instigator.Controller) != none )
  4465. {
  4466. KFPlayerController(Instigator.Controller).GetPlayerViewPoint(FADebugLineStart, AimRotation);
  4467. }
  4468.  
  4469. if( bDebugRecoilPosition )
  4470. {
  4471. WeaponBufferRot.Pitch = RecoilISMaxPitchLimit;
  4472. WeaponBufferRot.Yaw += TotalRecoilRotator.Yaw;
  4473. WeaponBufferRot.Pitch += TotalRecoilRotator.Pitch;
  4474. }
  4475. else
  4476. {
  4477. WeaponBufferRot = KFPlayerController(Instigator.Controller).WeaponBufferRotation;
  4478. }
  4479.  
  4480.  
  4481. FADebugLineRot.Yaw = AimRotation.Yaw + WeaponBufferRot.Yaw;
  4482. FADebugLineRot.Pitch = AimRotation.Pitch + WeaponBufferRot.Pitch;
  4483.  
  4484. FADebugLineEnd = FADebugLineStart + 400 * vector(FADebugLineRot);
  4485. DrawDebugSphere(FADebugLineEnd, 5, 8, 0,255,0, false);
  4486. }
  4487.  
  4488. if (HUD.ShouldDisplayDebug('recoil3d'))
  4489. {
  4490. HUD.Canvas.SetPos(4,out_YPos);
  4491. HUD.Canvas.DrawText("---------- KFWeapon: recoil3d ----------");
  4492. out_YPos += out_YL;
  4493.  
  4494. if( KFPlayerController(Instigator.Controller) != none )
  4495. {
  4496. KFPlayerController(Instigator.Controller).GetPlayerViewPoint(FADebugLineStart, FADebugViewRotation);
  4497. }
  4498.  
  4499. // Draw where the weapon is currently recoiling to
  4500. FADebugLineRot.Yaw = FADebugViewRotation.Yaw + TotalRecoilRotator.Yaw;
  4501. FADebugLineRot.Pitch = FADebugViewRotation.Pitch + TotalRecoilRotator.Pitch;
  4502. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4503. DrawDebugBox(FADebugLineEnd, vect(0.1,0.1,0.1), 255,255,0, false);
  4504.  
  4505. // Draw where the free-aim buffer is currently at
  4506. FADebugLineRot.Yaw = FADebugViewRotation.Yaw;
  4507. FADebugLineRot.Pitch = FADebugViewRotation.Pitch;
  4508. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4509. DrawDebugBox(FADebugLineEnd, vect(0.1,0.1,0.1), 255,0,128, false);
  4510.  
  4511.  
  4512. // Draw where the recoil limits are
  4513. if( bUsingSights )
  4514. {
  4515. UsedMaxYawLimit = RecoilISMaxYawLimit;
  4516. UsedMinYawLimit = RecoilISMinYawLimit;
  4517. UsedMaxPitchLimit = RecoilISMaxPitchLimit;
  4518. UsedMinPitchLimit = RecoilISMinPitchLimit;
  4519. }
  4520. else
  4521. {
  4522. UsedMaxYawLimit = RecoilMaxYawLimit;
  4523. UsedMinYawLimit = RecoilMinYawLimit;
  4524. UsedMaxPitchLimit = RecoilMaxPitchLimit;
  4525. UsedMinPitchLimit = RecoilMinPitchLimit;
  4526. }
  4527.  
  4528. FADebugLineRot.Yaw = FADebugViewRotation.Yaw + UsedMinYawLimit;
  4529. FADebugLineRot.Pitch = FADebugViewRotation.Pitch + UsedMaxPitchLimit;
  4530. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4531. FADebugLineUL = FADebugLineEnd;
  4532.  
  4533. FADebugLineRot.Yaw = FADebugViewRotation.Yaw + UsedMaxYawLimit;
  4534. FADebugLineRot.Pitch = FADebugViewRotation.Pitch + UsedMaxPitchLimit;
  4535. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4536. FADebugLineUR = FADebugLineEnd;
  4537.  
  4538. FADebugLineRot.Yaw = FADebugViewRotation.Yaw + UsedMinYawLimit;
  4539. FADebugLineRot.Pitch = FADebugViewRotation.Pitch + UsedMinPitchLimit;
  4540. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4541. FADebugLineLL = FADebugLineEnd;
  4542.  
  4543. FADebugLineRot.Yaw = FADebugViewRotation.Yaw + UsedMaxYawLimit;
  4544. FADebugLineRot.Pitch = FADebugViewRotation.Pitch + UsedMinPitchLimit;
  4545. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4546. FADebugLineLR = FADebugLineEnd;
  4547.  
  4548. DrawDebugLine(FADebugLineUL,FADebugLineUR, 255,0,255,false);
  4549. DrawDebugLine(FADebugLineUR,FADebugLineLR, 255,0,255,false);
  4550. DrawDebugLine(FADebugLineLR,FADebugLineLL, 255,0,255,false);
  4551. DrawDebugLine(FADebugLineLL,FADebugLineUL, 255,0,255,false);
  4552.  
  4553. // Draw where the full recoil starts to blend in
  4554. FADebugLineRot.Yaw = FADebugViewRotation.Yaw + (UsedMinYawLimit + ((65535-UsedMinYawLimit) * (1.0 - FullRecoilYawPct)));
  4555. FADebugLineRot.Pitch = FADebugViewRotation.Pitch + UsedMaxPitchLimit * FullRecoilPitchPct;
  4556. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4557. FADebugLineUL = FADebugLineEnd;
  4558.  
  4559. FADebugLineRot.Yaw = FADebugViewRotation.Yaw + UsedMaxYawLimit * FullRecoilYawPct;
  4560. FADebugLineRot.Pitch = FADebugViewRotation.Pitch + UsedMaxPitchLimit * FullRecoilPitchPct;
  4561. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4562. FADebugLineUR = FADebugLineEnd;
  4563.  
  4564. FADebugLineRot.Yaw = FADebugViewRotation.Yaw + (UsedMinYawLimit + ((65535-UsedMinYawLimit) * (1.0 - FullRecoilYawPct)));
  4565. FADebugLineRot.Pitch = FADebugViewRotation.Pitch + (UsedMinPitchLimit + ((65535-UsedMinPitchLimit) * (1.0 - FullRecoilYawPct)));
  4566. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4567. FADebugLineLL = FADebugLineEnd;
  4568.  
  4569. FADebugLineRot.Yaw = FADebugViewRotation.Yaw + UsedMaxYawLimit * FullRecoilYawPct;
  4570. FADebugLineRot.Pitch = FADebugViewRotation.Pitch + (UsedMinPitchLimit + ((65535-UsedMinPitchLimit) * (1.0 - FullRecoilYawPct)));
  4571. FADebugLineEnd = FADebugLineStart + 20 * vector(FADebugLineRot);
  4572. FADebugLineLR = FADebugLineEnd;
  4573.  
  4574. DrawDebugLine(FADebugLineUL,FADebugLineUR, 128,255,255,false);
  4575. DrawDebugLine(FADebugLineUR,FADebugLineLR, 128,255,255,false);
  4576. DrawDebugLine(FADebugLineLR,FADebugLineLL, 128,255,255,false);
  4577. DrawDebugLine(FADebugLineLL,FADebugLineUL, 128,255,255,false);
  4578. }
  4579. }
  4580.  
  4581. /*********************************************************************************************
  4582. * @name Perks
  4583. ********************************************************************************************* */
  4584. simulated static event class<KFPerk> GetWeaponPerkClass( class<KFPerk> InstigatorPerkClass )
  4585. {
  4586. if( default.AssociatedPerkClasses.Length > 1 )
  4587. {
  4588. if (default.AssociatedPerkClasses.Find(InstigatorPerkClass) != INDEX_NONE)
  4589. {
  4590. return InstigatorPerkClass;
  4591. }
  4592. }
  4593.  
  4594. return default.AssociatedPerkClasses[0];
  4595. }
  4596.  
  4597. simulated static function bool AllowedForAllPerks()
  4598. {
  4599. return false;
  4600. }
  4601.  
  4602. static event array< Class<KFPerk> > GetAssociatedPerkClasses()
  4603. {
  4604. return default.AssociatedPerkClasses;
  4605. }
  4606.  
  4607. simulated static event class<KFPerk> GetWeaponPerkClassByIndex(int Index)
  4608. {
  4609. return default.AssociatedPerkClasses[Index];
  4610. }
  4611.  
  4612. simulated static function bool IsMultiPerkWeapn()
  4613. {
  4614. return default.AssociatedPerkClasses.Length > 1;
  4615. }
  4616.  
  4617. /*********************************************************************************************
  4618. * @name States
  4619. ********************************************************************************************* */
  4620.  
  4621. /** Gets a weapon state identifier for third person anims */
  4622. simulated function byte GetWeaponStateId()
  4623. {
  4624. return WEP_Idle;
  4625. }
  4626.  
  4627. /** Gets the current animation rate, scaled or not */
  4628. simulated function float GetThirdPersonAnimRate()
  4629. {
  4630. return 1.0f;
  4631. }
  4632.  
  4633. /** Sets the animation rate on the (human) owner for 3rd person anim syncing */
  4634. simulated function UpdateWeaponAttachmentAnimRate( float Rate )
  4635. {
  4636. local KFPawn_Human KFPH;
  4637.  
  4638. KFPH = KFPawn_Human( Instigator );
  4639. if( KFPH != none )
  4640. {
  4641. KFPH.SetWeaponAttachmentAnimRateByte( Rate );
  4642. }
  4643. }
  4644.  
  4645. /** Register new state with owning pawn */
  4646. simulated function NotifyBeginState()
  4647. {
  4648. UpdateWeaponAttachmentAnimRate( GetThirdPersonAnimRate() );
  4649. KFPawn(Instigator).WeaponStateChanged(GetWeaponStateId());
  4650. }
  4651.  
  4652. /** Register new state with owning pawn */
  4653. simulated function NotifyEndState()
  4654. {
  4655. UpdateWeaponAttachmentAnimRate( 1.0f );
  4656. KFPawn(Instigator).WeaponStateChanged(WEP_Idle);
  4657. }
  4658.  
  4659. /*********************************************************************************************
  4660. * state Inactive
  4661. * This state is the default state. It needs to make sure Zooming is reset when entering/leaving
  4662. *********************************************************************************************/
  4663.  
  4664. auto state Inactive
  4665. {
  4666. ignores ForceReload, ShouldAutoReload, AllowIronSights;
  4667.  
  4668. simulated function BeginState(name PreviousStateName)
  4669. {
  4670. // Reset ironsights
  4671. if ( bUsingSights )
  4672. {
  4673. // do this locally and replicate to avoid sync errors w/ bIronSightOnBringUp
  4674. if ( Instigator.IsLocallyControlled() )
  4675. {
  4676. SetIronSights(false);
  4677. }
  4678. }
  4679. else if( !bHasIronSights )
  4680. {
  4681. // If this weapon doesn't have ironsights, clear altfire. Fixes a bug where
  4682. // when switching to a weapon that does have ironsights it will immediately alt-fire
  4683. ClearPendingFire(ALTFIRE_FIREMODE);
  4684. }
  4685.  
  4686. // Clear pending reload - Fixes an issue where you can set a pending reload while
  4687. // switching weapons and it will carry over to the new weapon.
  4688. ClearPendingFire(RELOAD_FIREMODE);
  4689.  
  4690. // force an update when switching weapons so we can detect sync errors
  4691. if ( bAllowClientAmmoTracking && Role == ROLE_Authority && WorldInfo.TimeSeconds > CreationTime )
  4692. {
  4693. ClientForceAmmoUpdate(AmmoCount[0], SpareAmmoCount[0], true);
  4694. ClientForceSecondaryAmmoUpdate(AmmoCount[1]);
  4695. }
  4696.  
  4697. bPendingAutoSwitchOnDryFire = false;
  4698.  
  4699. Super.BeginState(PreviousStateName);
  4700. }
  4701.  
  4702. /** For weapons, this function starts the Equipping process. */
  4703. simulated function Activate()
  4704. {
  4705. Global.Activate();
  4706. UpdateOutOfAmmoEffects(0.0f);
  4707. }
  4708. }
  4709.  
  4710. /**
  4711. * Called from PerformReload when bAllowClientAmmoTracking==TRUE
  4712. * This was moved up in the file because it has to be defined before state active
  4713. */
  4714. reliable server function ServerSyncReload(int ClientSpareAmmoCount)
  4715. {
  4716. local int ClientReloadAmount;
  4717.  
  4718. // Checks the initial spare ammo values on both the server and client, so any ammo received won't throw off the reload.
  4719. if ( bAllowClientAmmoTracking && ClientSpareAmmoCount < InitialReloadSpareAmmo )
  4720. {
  4721. // clamp to spare ammo size
  4722. ClientReloadAmount = Min(InitialReloadSpareAmmo - ClientSpareAmmoCount, InitialReloadSpareAmmo);
  4723. // consume spare ammo
  4724. SpareAmmoCount[0] -= ClientReloadAmount;
  4725.  
  4726. // increment and clamp magazine ammo
  4727. AmmoCount[0] = Min(AmmoCount[0] + ClientReloadAmount, MagazineCapacity[0]);
  4728. }
  4729. }
  4730.  
  4731. /*********************************************************************************************
  4732. * State Active
  4733. * A Weapon this is being held by a pawn should be in the active state. In this state,
  4734. * a weapon should loop any number of idle animations, as well as check the PendingFire flags
  4735. * to see if a shot has been fired.
  4736. *********************************************************************************************/
  4737.  
  4738. simulated function Activate()
  4739. {
  4740. // Clear opposite firemode when coming from another weapon, this is required since
  4741. // StopFire() will only clear current firemode when the button is released.
  4742. if ( bUseAltFireMode )
  4743. {
  4744. ClearPendingFire(DEFAULT_FIREMODE);
  4745. }
  4746. else
  4747. {
  4748. ClearPendingFire(ALTFIRE_FIREMODE);
  4749. }
  4750.  
  4751. // The weapon is equipped, attach it to the mesh.
  4752. AttachWeaponTo( Instigator.Mesh );
  4753.  
  4754. super.Activate();
  4755. }
  4756.  
  4757. /** Global defs for active state */
  4758. simulated function PlayIdleAnim();
  4759. simulated function StartIdleFidgetTimer();
  4760. simulated function IdleFidgetTimer();
  4761. simulated function bool CanPlayIdleFidget(optional bool bOnReload);
  4762. simulated function bool PlayIdleFidgetAnim();
  4763. simulated function ANIMNOTIFY_EnableAdditiveBob();
  4764.  
  4765. simulated state Active
  4766. {
  4767. simulated event OnAnimEnd(AnimNodeSequence SeqNode, float PlayedTime, float ExcessTime)
  4768. {
  4769. if ( WorldInfo.NetMode != NM_DedicatedServer )
  4770. {
  4771. PlayIdleAnim();
  4772. }
  4773. }
  4774.  
  4775. simulated function PlayIdleAnim()
  4776. {
  4777. local int IdleIndex;
  4778.  
  4779. if ( Instigator.IsLocallyControlled() )
  4780. {
  4781. if( bUsingSights && IdleSightedAnims.Length > 0 )
  4782. {
  4783. IdleIndex = Rand(IdleSightedAnims.Length);
  4784. PlayAnimation(IdleSightedAnims[IdleIndex], 0.0, true, 0.1);
  4785. }
  4786. else if ( IdleAnims.Length > 0 )
  4787. {
  4788. IdleIndex = Rand(IdleAnims.Length);
  4789. PlayAnimation(IdleAnims[IdleIndex], 0.0, true, 0.2);
  4790. }
  4791.  
  4792. StartIdleFidgetTimer();
  4793. ToggleAdditiveBobAnim(!bUsingSights);
  4794. }
  4795. }
  4796.  
  4797. /** Change states */
  4798. simulated function GotoWeaponSprinting()
  4799. {
  4800. GotoState('WeaponSprinting');
  4801. }
  4802.  
  4803. /** Initialize the weapon as being active and ready to go. */
  4804. simulated event BeginState(Name PreviousStateName)
  4805. {
  4806. // Cache a reference to the KFPlayerController
  4807. if (Role == ROLE_Authority)
  4808. {
  4809. CacheKFPlayerController();
  4810. }
  4811.  
  4812. // In case this flag is still set
  4813. bIronSightOnBringUp = false;
  4814.  
  4815. // Initialize recoil blend out settings
  4816. if( RecoilRate > 0 && RecoilBlendOutRatio > 0 )
  4817. {
  4818. RecoilYawBlendOutRate = maxRecoilYaw/RecoilRate * RecoilBlendOutRatio;
  4819. RecoilPitchBlendOutRate = maxRecoilPitch/RecoilRate * RecoilBlendOutRatio;
  4820. }
  4821.  
  4822. // restore idle animation
  4823. if( !WeaponIsAnimating() || !IsTimerActive(nameof(OnAnimEnd)) )
  4824. OnAnimEnd(none, 0.f, 0.f);
  4825.  
  4826. // restore skel controls
  4827. bEnableTiltSkelControl = true;
  4828.  
  4829. if (InvManager != none && InvManager.LastAttemptedSwitchToWeapon != none)
  4830. {
  4831. if (InvManager.LastAttemptedSwitchToWeapon != self)
  4832. {
  4833. InvManager.LastAttemptedSwitchToWeapon.ClientWeaponSet(true);
  4834. }
  4835. InvManager.LastAttemptedSwitchToWeapon = none;
  4836. }
  4837.  
  4838. // test ironsights in the same way Super.BeginState() tests pending fire
  4839. CheckPendingIronsights();
  4840.  
  4841. // do this last so the above code happens before any state changes
  4842. Super.BeginState(PreviousStateName);
  4843. }
  4844.  
  4845. /** Event called when weapon leaves this state */
  4846. simulated event EndState(Name NextStateName)
  4847. {
  4848. ClearTimer(nameof(IdleFidgetTimer));
  4849. ToggleAdditiveBobAnim(false);
  4850.  
  4851. Super.EndState(NextStateName);
  4852. }
  4853.  
  4854. /** Animation */
  4855. simulated function StartIdleFidgetTimer()
  4856. {
  4857. if ( IdleFidgetAnims.Length > 0 )
  4858. {
  4859. SetTimer(RandRange(1.f, 10.f), false, nameof(IdleFidgetTimer));
  4860. }
  4861. }
  4862.  
  4863. /** Animation */
  4864. simulated function IdleFidgetTimer()
  4865. {
  4866. local bool bIsPlaying;
  4867.  
  4868. if ( CanPlayIdleFidget() )
  4869. {
  4870. bIsPlaying = PlayIdleFidgetAnim();
  4871. }
  4872.  
  4873. // Otherwise keep trying. When OnAnimEnd is called the timer will restart with a
  4874. // random interval, but this allows anims to keep trying if one doesn't play.
  4875. if ( !bIsPlaying )
  4876. {
  4877. SetTimer(20.f, false, nameof(IdleFidgetTimer));
  4878. }
  4879. }
  4880.  
  4881. /** Returns true if IdleFidget anim is allowed (i.e. not in combat). This has some
  4882. * hardcoded numbers, but it's easy to override in a subclass if needed
  4883. */
  4884. simulated function bool CanPlayIdleFidget(optional bool bOnReload)
  4885. {
  4886. local Pawn P;
  4887.  
  4888. if ( Instigator == none )
  4889. return false;
  4890. if ( bCanBeReloaded && !HasAmmo(DEFAULT_FIREMODE) )
  4891. return false; // only when Ammo > 0 since most anims check magazine
  4892. if ( bUsingSights )
  4893. return false;
  4894.  
  4895. if ( !bOnReload )
  4896. {
  4897. // played randomly via the StartIdleFidgetTimer/IdleFidgetTimer
  4898. if ( `TimeSince(LastIdleFidgetAnimTime) < 20.f )
  4899. return false;
  4900. if ( `TimeSince(Instigator.LastPainTime) < 10.f )
  4901. return false;
  4902. if ( `TimeSince(KFPawn(Instigator).LastWeaponFireTime) < 10.f )
  4903. return false;
  4904.  
  4905. // Is enemy visible on screen. Using class'Pawn' to avoid cast
  4906. foreach WorldInfo.AllPawns( class'Pawn', P )
  4907. {
  4908. if ( `TimeSince(P.LastRenderTime) < 1.f && P.GetTeamNum() != Instigator.GetTeamNum() )
  4909. {
  4910. return false;
  4911. }
  4912. }
  4913. }
  4914. else
  4915. {
  4916. // slightly modified rules when it's player activited
  4917. if ( `TimeSince(LastIdleFidgetAnimTime) < 1.f )
  4918. return false;
  4919. if ( !Instigator.IsLocallyControlled() )
  4920. return false;
  4921. }
  4922.  
  4923. return true;
  4924. }
  4925.  
  4926. /** Play a random animation from the IdleFidget list */
  4927. simulated function bool PlayIdleFidgetAnim()
  4928. {
  4929. local name AnimName;
  4930.  
  4931. AnimName = IdleFidgetAnims[Rand(IdleFidgetAnims.Length)];
  4932. if ( MySkelMesh.FindAnimSequence(AnimName) != None )
  4933. {
  4934. // disable the additive move bob temporarily
  4935. ToggleAdditiveBobAnim(false);
  4936.  
  4937. PlayAnimation(AnimName);
  4938. LastIdleFidgetAnimTime = WorldInfo.TimeSeconds;
  4939.  
  4940. return true;
  4941. }
  4942.  
  4943. return false;
  4944. }
  4945.  
  4946. /** Returns true if weapon can potentially be reloaded */
  4947. simulated function bool CanReload(optional byte FireModeNum)
  4948. {
  4949. if ( Global.CanReload(FireModeNum) )
  4950. {
  4951. return true;
  4952. }
  4953.  
  4954. // If this attempt to reload failed try to play a idle fidget instead
  4955. if ( PendingFire(RELOAD_FIREMODE) && CanPlayIdleFidget(true) )
  4956. {
  4957. PlayIdleFidgetAnim();
  4958. }
  4959. return false;
  4960. }
  4961.  
  4962. /** called during the settle portion of certain weapon actions */
  4963. simulated function ANIMNOTIFY_EnableAdditiveBob()
  4964. {
  4965. if ( !bUsingSights )
  4966. {
  4967. ToggleAdditiveBobAnim(true, 0.3f);
  4968. }
  4969. else
  4970. {
  4971. // Should never happen, but some notifies have been placed in IS anims?!
  4972. //`log(self@"called ANIMNOTIFY_EnableAdditiveBob while in ironsights");
  4973. }
  4974. }
  4975.  
  4976. /**
  4977. * Perform pending fire modes. This prevents a missed oppurtunity if the server was out
  4978. * of ammo during reload and has already called 'Active' BeginState.
  4979. */
  4980. reliable server function ServerSyncReload(int ClientSpareAmmoCount)
  4981. {
  4982. local int i;
  4983.  
  4984. Global.ServerSyncReload(ClientSpareAmmoCount);
  4985.  
  4986. for( i=0; i<GetPendingFireLength(); i++ )
  4987. {
  4988. if( PendingFire(i) )
  4989. {
  4990. BeginFire(i);
  4991. }
  4992. }
  4993. }
  4994.  
  4995. /**
  4996. * Called when the weapon runs out of ammo after firing
  4997. */
  4998. simulated function WeaponEmpty()
  4999. {
  5000. // Check firemodes that don't require ammo, since Weapon.Active.BeginState()
  5001. // will skip it's PendingFire check when !HasAnyAmmo().
  5002. if ( PendingFire(GRENADE_FIREMODE) && HasAmmo(GRENADE_FIREMODE) )
  5003. {
  5004. BeginFire(GRENADE_FIREMODE);
  5005. }
  5006. else if ( PendingFire(BASH_FIREMODE) && HasAmmo(BASH_FIREMODE) )
  5007. {
  5008. BeginFire(BASH_FIREMODE);
  5009. }
  5010. }
  5011. }
  5012.  
  5013. /** test ironsights in the same way Super.BeginState() tests pending fire */
  5014. simulated function CheckPendingIronsights()
  5015. {
  5016. local PlayerController PC;
  5017.  
  5018. if ( Instigator.IsLocallyControlled() )
  5019. {
  5020. PC = PlayerController(Instigator.Controller);
  5021. if ( PC != None && PC.PlayerInput != None )
  5022. {
  5023. if ( KFPlayerInput(PC.PlayerInput).bIronsightsHeld )
  5024. {
  5025. SetIronsights(true);
  5026. }
  5027. }
  5028. }
  5029. }
  5030.  
  5031. /*********************************************************************************************
  5032. * State WeaponEquipping
  5033. * The Weapon is in this state while transitioning from Inactive to Active state.
  5034. * Typically, the weapon will remain in this state while its selection animation is being played.
  5035. * While in this state, the weapon cannot be fired.
  5036. *********************************************************************************************/
  5037.  
  5038. simulated state WeaponEquipping
  5039. {
  5040. simulated function byte GetWeaponStateId()
  5041. {
  5042. return WEP_Equipping;
  5043. }
  5044.  
  5045. simulated function bool TryPutDown()
  5046. {
  5047. bWeaponPutDown = true;
  5048.  
  5049. if (!IsTimerActive('WeaponEquipped'))
  5050. {
  5051. PutDownWeapon();
  5052. }
  5053. else
  5054. {
  5055. // We want the abort to be the same amount of time as
  5056. // we have already spent equipping
  5057. EquipAbortTime = PutDownTime * GetTimerCount('WeaponEquipped') / GetTimerRate('WeaponEquipped');
  5058. GotoState('WeaponAbortEquip');
  5059. }
  5060.  
  5061. return true;
  5062. }
  5063.  
  5064. simulated function BeginState(Name PreviousStateName)
  5065. {
  5066. local KFPerk InstigatorPerk;
  5067. local float PerkZedTimeResist;
  5068.  
  5069. super.BeginState(PreviousStateName);
  5070.  
  5071. // see if we need to start in ironsights
  5072. if( bIronSightOnBringUp && bHasIronSights )
  5073. {
  5074. bIronSightOnBringUp = false;
  5075. SetIronSights(true);
  5076. }
  5077. else
  5078. {
  5079. // handle the bindable button hold version
  5080. CheckPendingIronsights();
  5081. }
  5082.  
  5083. InstigatorPerk = GetPerk();
  5084. if ( InstigatorPerk != none )
  5085. {
  5086. PerkZedTimeResist = InstigatorPerk.GetZedTimeModifier(self);
  5087. }
  5088.  
  5089. SetZedTimeResist(0.375f + PerkZedTimeResist);
  5090. NotifyBeginState();
  5091. }
  5092.  
  5093. simulated function EndState(Name NextStateName)
  5094. {
  5095. ClearTimer('WeaponEquipped');
  5096. ClearZedTimeResist();
  5097. NotifyEndState();
  5098. }
  5099.  
  5100. /** Gets the current animation rate, scaled or not */
  5101. simulated function float GetThirdPersonAnimRate()
  5102. {
  5103. local KFPerk CurrentPerk;
  5104. local float ScaledRate;
  5105.  
  5106. ScaledRate = 1.0f;
  5107. CurrentPerk = GetPerk();
  5108. if( CurrentPerk != none )
  5109. {
  5110. CurrentPerk.ModifyWeaponSwitchTime( ScaledRate );
  5111. }
  5112.  
  5113. return 1.f / ScaledRate;
  5114. }
  5115. }
  5116.  
  5117. /**
  5118. * Sets the timing for equipping a weapon.
  5119. * The WeaponEquipped event is trigged when expired
  5120. */
  5121. simulated function TimeWeaponEquipping()
  5122. {
  5123. local KFPerk InstigatorPerk;
  5124. local float ModifiedEquipTime;
  5125.  
  5126. ModifiedEquipTime = MySkelMesh.GetAnimLength( EquipAnim );
  5127.  
  5128. InstigatorPerk = GetPerk();
  5129. if( InstigatorPerk != none )
  5130. {
  5131. InstigatorPerk.ModifyWeaponSwitchTime( ModifiedEquipTime );
  5132. }
  5133.  
  5134. // Play the animation
  5135. PlayWeaponEquip( ModifiedEquipTime );
  5136.  
  5137. SetTimer( EquipTime>0 ? EquipTime : 0.01, false, nameof( WeaponEquipped ) );
  5138. }
  5139.  
  5140. simulated function WeaponEquipped();
  5141.  
  5142. /**
  5143. * Show the weapon begin equipped
  5144. */
  5145. simulated function PlayWeaponEquip( float ModifiedEquipTime )
  5146. {
  5147. // Play the animation for the weapon being equipped
  5148. PlayAnimation( GetEquipAnimName(), ModifiedEquipTime );
  5149. }
  5150.  
  5151. /** Get equip anim name (overridden as necessary) */
  5152. simulated function name GetEquipAnimName()
  5153. {
  5154. return EquipAnim;
  5155. }
  5156.  
  5157. /*********************************************************************************************
  5158. * State WeaponPuttingDown
  5159. * Putting down weapon in favor of a new one.
  5160. * Weapon is transitioning to the Inactive state.
  5161. *********************************************************************************************/
  5162.  
  5163. simulated state WeaponPuttingDown
  5164. {
  5165. simulated function byte GetWeaponStateId()
  5166. {
  5167. return WEP_PutAway;
  5168. }
  5169.  
  5170. simulated function BeginState( Name PreviousStateName )
  5171. {
  5172. local KFPerk InstigatorPerk;
  5173. local float PerkZedTimeResist;
  5174.  
  5175. TimeWeaponPutDown();
  5176. bWeaponPutDown = false;
  5177.  
  5178. InstigatorPerk = GetPerk();
  5179. if ( InstigatorPerk != none )
  5180. {
  5181. PerkZedTimeResist = InstigatorPerk.GetZedTimeModifier(self);
  5182. }
  5183.  
  5184. SetZedTimeResist(0.375f + PerkZedTimeResist);
  5185. NotifyBeginState();
  5186. }
  5187.  
  5188. simulated function EndState(Name NextStateName)
  5189. {
  5190. Super.EndState(NextStateName);
  5191.  
  5192. ClearZedTimeResist();
  5193. NotifyEndState();
  5194. }
  5195.  
  5196. simulated function Activate();
  5197.  
  5198. /** Gets the current animation rate, scaled or not */
  5199. simulated function float GetThirdPersonAnimRate()
  5200. {
  5201. local KFPerk CurrentPerk;
  5202. local float ScaledRate;
  5203.  
  5204. ScaledRate = 1.0f;
  5205. CurrentPerk = GetPerk();
  5206. if( CurrentPerk != none )
  5207. {
  5208. CurrentPerk.ModifyWeaponSwitchTime( ScaledRate );
  5209. }
  5210.  
  5211. return 1.f / ScaledRate;
  5212. }
  5213. }
  5214.  
  5215. // Control 'WeaponDownSimple' state
  5216. simulated function SetSimplePutDown(bool bPutDownWeapon)
  5217. {
  5218. if ( bPutDownWeapon )
  5219. {
  5220. GotoState('WeaponDownSimple');
  5221. }
  5222. }
  5223.  
  5224. simulated function bool CanSwitchWeapons()
  5225. {
  5226. return true;
  5227. }
  5228.  
  5229. /*********************************************************************************************
  5230. * State WeaponDownSimple
  5231. * Putting the weapon down off the screen, don't allow shooting or switching weapons
  5232. *********************************************************************************************/
  5233. simulated state WeaponDownSimple
  5234. {
  5235. simulated function bool CanTransitionToIronSights();
  5236. simulated function bool DenyClientWeaponSet() {return true;}
  5237. simulated function bool CanReload(optional byte FireModeNum);
  5238. simulated function SetIronSights(bool bNewIronSights){}
  5239. simulated event OnAnimEnd(AnimNodeSequence SeqNode, float PlayedTime, float ExcessTime){}
  5240.  
  5241. simulated function bool CanThrow()
  5242. {
  5243. return (Instigator == none || Instigator.Health <= 0);
  5244. }
  5245.  
  5246. simulated function bool CanSwitchWeapons();
  5247.  
  5248. exec function SimplePutDown()
  5249. {
  5250. SetSimplePutDown(false);
  5251. }
  5252.  
  5253. simulated function BeginState(name PreviousStateName)
  5254. {
  5255. Super.BeginState(PreviousStateName);
  5256.  
  5257. bDoingQuickDownZoom = true;
  5258.  
  5259. if ( Instigator.IsLocallyControlled() )
  5260. {
  5261. ZoomDown(true, QuickWeaponDownTime);
  5262. }
  5263.  
  5264. // Go to a looping idle animation to avoid screen clipping
  5265. if( Instigator.IsFirstPerson() )
  5266. {
  5267. PlayAnimation(IdleAnims[0], 0.0, true, 0.2);
  5268. }
  5269.  
  5270. StopFire(CurrentFireMode);
  5271. }
  5272.  
  5273. /**
  5274. * Zoom the weapon down, off the screen
  5275. *
  5276. * @param bAnimateTransition whether or not to animate this zoom transition
  5277. * @param ZoomTimeToGo how much zoom time is left
  5278. */
  5279. simulated function ZoomDown(bool bAnimateTransition, float ZoomTimeToGo)
  5280. {
  5281. if( bAnimateTransition )
  5282. {
  5283. ZoomInTime=ZoomTimeToGo;
  5284.  
  5285. // If the zoom out was interrupted, set the parameters for the native code to interpolate the zoom from the proper position
  5286. if( bZoomingOut )
  5287. {
  5288. bZoomingOut=false;
  5289. // Flag so the native code knows the zoom was interupted
  5290. bZoomOutInterrupted=true;
  5291. // Set the zoom time relative to how far along we were when zooming out
  5292. ZoomTime=ZoomInTime - ZoomTime;
  5293. // Let the native code know where/when the zoom was interrupted
  5294. ZoomPartialTime=ZoomTime;
  5295. ZoomStartOffset=PlayerViewOffset;
  5296. ZoomRotStartOffset=ZoomRotInterp;
  5297. }
  5298. else
  5299. {
  5300. ZoomTime=ZoomInTime;
  5301. ZoomStartOffset=PlayerViewOffset;
  5302. }
  5303.  
  5304. // Zoom down to hipped offset
  5305. ZoomTargetOffset=default.PlayerViewOffset;
  5306.  
  5307. if( MySkelMesh != none )
  5308. {
  5309. ZoomWeaponFOVStart=MySkelMesh.FOV;
  5310. }
  5311. else
  5312. {
  5313. ZoomWeaponFOVStart=MeshFOV;
  5314. }
  5315.  
  5316. if( WorldInfo.NetMode != NM_DedicatedServer && Instigator != none )
  5317. {
  5318. EnableIronSightsDoF(false);
  5319. EnablePlayerZoom(false);
  5320. }
  5321.  
  5322. bZoomingIn=true;
  5323. }
  5324. }
  5325.  
  5326. simulated function EndState(name NextStateName)
  5327. {
  5328. Super.EndState(NextStateName);
  5329.  
  5330. ClearTimer('EndWeaponDownSimpleTimer');
  5331.  
  5332. bDoingQuickDownZoom=false;
  5333. }
  5334.  
  5335. simulated function EndWeaponDownSimpleTimer()
  5336. {
  5337. // we're done, leave state and go back to active
  5338. GotoState('Active');
  5339. }
  5340.  
  5341. simulated function SetSimplePutDown(bool bPutDownWeapon)
  5342. {
  5343. if ( !bPutDownWeapon )
  5344. {
  5345. if ( WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.NetMode == NM_ListenServer && !Instigator.IsLocallyControlled() )
  5346. {
  5347. // Slightly faster on the server to prevent sync issues
  5348. EndWeaponDownSimpleTimer();
  5349. }
  5350. else
  5351. {
  5352. SetTimer(QuickWeaponDownFinishTime, false, 'EndWeaponDownSimpleTimer');
  5353. if ( Instigator.IsLocallyControlled() )
  5354. {
  5355. ZoomOut(true, QuickWeaponDownFinishTime);
  5356. }
  5357. }
  5358. }
  5359. else
  5360. {
  5361. ClearTimer('EndWeaponDownSimpleTimer');
  5362. if ( Instigator.IsLocallyControlled() )
  5363. {
  5364. ZoomDown(true, QuickWeaponDownTime);
  5365. }
  5366. StopFire(CurrentFireMode);
  5367. }
  5368. }
  5369. }
  5370.  
  5371. /**
  5372. * Sets the timing for putting a weapon down. The WeaponIsDown event is trigged when expired
  5373. */
  5374. simulated function TimeWeaponPutDown()
  5375. {
  5376. local KFPerk InstigatorPerk;
  5377. local float ModifiedPutDownTime;
  5378.  
  5379. ModifiedPutDownTime = PutDownTime;
  5380.  
  5381. InstigatorPerk = GetPerk();
  5382. if( InstigatorPerk != none )
  5383. {
  5384. InstigatorPerk.ModifyWeaponSwitchTime( ModifiedPutDownTime );
  5385. }
  5386.  
  5387. SetTimer( ModifiedPutDownTime > 0 ? ModifiedPutDownTime : 0.01, false, nameof(WeaponIsDown) );
  5388.  
  5389. if( Instigator.IsFirstPerson() )
  5390. {
  5391. PlayWeaponPutDown( ModifiedPutDownTime );
  5392. }
  5393. }
  5394.  
  5395. /**
  5396. * Show the weapon being put away
  5397. */
  5398. simulated function PlayWeaponPutDown( float ModifiedPutDownTime )
  5399. {
  5400. // Play the animation for the weapon being put down
  5401. PlayAnimation( GetWeaponPutDownAnimName(), ModifiedPutDownTime );
  5402. }
  5403.  
  5404. /** Get putdown anim name (overridden as necessary) */
  5405. simulated function name GetWeaponPutDownAnimName()
  5406. {
  5407. return PutDownAnim;
  5408. }
  5409.  
  5410. /**
  5411. * When attempting to put the weapon down, look to see if our MinFiringPutDownPct has been met. If so just put it down
  5412. */
  5413. simulated function bool TryPutDown()
  5414. {
  5415. local float MinTimerTarget;
  5416. local float TimerCount;
  5417.  
  5418. bWeaponPutDown = true;
  5419.  
  5420. if (!IsTimerActive('RefireCheckTimer'))
  5421. {
  5422. PutDownWeapon();
  5423. }
  5424. else
  5425. {
  5426. MinTimerTarget = GetTimerRate('RefireCheckTimer') * MinFiringPutDownPct;
  5427. TimerCount = GetTimerCount('RefireCheckTimer');
  5428.  
  5429. if (TimerCount >= MinTimerTarget)
  5430. {
  5431. PutDownWeapon();
  5432. }
  5433. else
  5434. {
  5435. // Shorten the wait time
  5436. SetTimer(MinTimerTarget - TimerCount, false, nameof( FiringPutDownWeapon ) );
  5437. }
  5438. }
  5439.  
  5440. return true;
  5441. }
  5442.  
  5443. simulated function FiringPutDownWeapon()
  5444. {
  5445. if (bWeaponPutDown)
  5446. {
  5447. PutDownWeapon();
  5448. }
  5449. }
  5450.  
  5451. /*********************************************************************************************
  5452. * State WeaponAbortEquip
  5453. * Special PuttingDown state used when WeaponEquipping is interrupted. Must come after
  5454. * WeaponPuttingDown definition or this willextend the super version.
  5455. *********************************************************************************************/
  5456.  
  5457. simulated state WeaponAbortEquip extends WeaponPuttingDown
  5458. {
  5459. simulated function TimeWeaponPutDown()
  5460. {
  5461. local AnimNodeSequence AnimNode;
  5462. local float Rate;
  5463.  
  5464. // Time the abort
  5465. SetTimer(FMax(EquipAbortTime, 0.01),, nameof(WeaponIsDown) );
  5466.  
  5467. if (WorldInfo.NetMode != NM_DedicatedServer)
  5468. {
  5469. // play anim
  5470. AnimNode = GetWeaponAnimNodeSeq();
  5471. if (AnimNode != None && AnimNode.AnimSeq != None)
  5472. {
  5473. AnimNode.SetAnim(PutDownAnim);
  5474. Rate = AnimNode.AnimSeq.SequenceLength / PutDownTime;
  5475. AnimNode.PlayAnim(false, Rate, AnimNode.AnimSeq.SequenceLength - EquipAbortTime * Rate);
  5476. }
  5477. }
  5478. }
  5479. }
  5480.  
  5481. /*********************************************************************************************
  5482. * state WeaponFiring
  5483. * This is the default Firing State. It's performed on both the client and the server.
  5484. *********************************************************************************************/
  5485. simulated state WeaponFiring
  5486. {
  5487. ignores AllowSprinting;
  5488.  
  5489. simulated function BeginState(Name PrevStateName)
  5490. {
  5491. local KFPerk InstigatorPerk;
  5492.  
  5493. InstigatorPerk = GetPerk();
  5494. if( InstigatorPerk != none )
  5495. {
  5496. SetZedTimeResist( InstigatorPerk.GetZedTimeModifier(self) );
  5497. }
  5498.  
  5499. if ( bLoopingFireAnim.Length > 0 || bLoopingFireSnd.Length > 0 )
  5500. {
  5501. StartLoopingFireEffects(CurrentFireMode);
  5502. }
  5503.  
  5504. super.BeginState(PrevStateName);
  5505. }
  5506.  
  5507. simulated event Tick(float DeltaTime)
  5508. {
  5509. Global.Tick(DeltaTime);
  5510.  
  5511. // Stop the looping fire sound if we're in zed time and want to play single fire sounds
  5512. if( bPlayingLoopingFireSnd && ShouldForceSingleFireSound() )
  5513. {
  5514. StopLoopingFireSound(CurrentFireMode);
  5515. }
  5516. }
  5517.  
  5518. /**
  5519. * Timer event, call is set up in Weapon::TimeWeaponFiring().
  5520. * The weapon is given a chance to evaluate if another shot should be fired.
  5521. * This event defines the weapon's rate of fire.
  5522. */
  5523. simulated function RefireCheckTimer()
  5524. {
  5525. local KFPerk InstigatorPerk;
  5526.  
  5527. InstigatorPerk = GetPerk();
  5528. if( InstigatorPerk != none )
  5529. {
  5530. SetZedTimeResist( InstigatorPerk.GetZedTimeModifier(self) );
  5531. }
  5532.  
  5533. super.RefireCheckTimer();
  5534. }
  5535.  
  5536. simulated function EndState(Name NextStateName)
  5537. {
  5538. super.EndState(NextStateName);
  5539.  
  5540. ClearZedTimeResist();
  5541.  
  5542. // Simulate weapon firing effects on the local client
  5543. if( WorldInfo.NetMode == NM_Client )
  5544. {
  5545. Instigator.WeaponStoppedFiring(self, false);
  5546. }
  5547.  
  5548. if ( bPlayingLoopingFireAnim || bPlayingLoopingFireAnim )
  5549. {
  5550. StopLoopingFireEffects(CurrentFireMode);
  5551. }
  5552. }
  5553.  
  5554. /** Override to continue any looping fire anims */
  5555. simulated event OnAnimEnd(AnimNodeSequence SeqNode, float PlayedTime, float ExcessTime)
  5556. {
  5557. local name WeaponFireAnimName;
  5558.  
  5559. if ( WorldInfo.NetMode != NM_DedicatedServer )
  5560. {
  5561. // If loop start or another event such as IronSights transition ended, restart looping fire anim.
  5562. if ( bPlayingLoopingFireAnim )
  5563. {
  5564. WeaponFireAnimName = GetLoopingFireAnim(CurrentFireMode);
  5565. if ( WeaponFireAnimName != '' )
  5566. {
  5567. PlayAnimation(WeaponFireAnimName, MySkelMesh.GetAnimLength(WeaponFireAnimName), true, 0.f);
  5568. }
  5569. }
  5570. }
  5571. }
  5572.  
  5573. /** don't allow for a pickup to switch the weapon */
  5574. simulated function bool DenyClientWeaponSet()
  5575. {
  5576. return true;
  5577. }
  5578.  
  5579. /** Gets the current animation rate, scaled or not */
  5580. simulated function float GetThirdPersonAnimRate()
  5581. {
  5582. local KFPerk CurrentPerk;
  5583. local float ScaledRate;
  5584.  
  5585. ScaledRate = 1.0f;
  5586. CurrentPerk = GetPerk();
  5587. if( CurrentPerk != none )
  5588. {
  5589. CurrentPerk.ModifyRateOfFire( ScaledRate, self );
  5590. }
  5591.  
  5592. return 1.f / ScaledRate;
  5593. }
  5594. }
  5595.  
  5596. /**
  5597. * Sets the timing for the firing state on server and local client.
  5598. * By default, a constant looping Rate Of Fire (ROF) is set up.
  5599. * When the delay has expired, the RefireCheckTimer event is triggered.
  5600. *
  5601. * Network: LocalPlayer and Server
  5602. *
  5603. * @param FireModeNum Fire Mode.
  5604. */
  5605. simulated function TimeWeaponFiring( byte FireModeNum )
  5606. {
  5607. local float AdjustedFireInterval;
  5608. local KFPerk CurrentPerk;
  5609.  
  5610. // if weapon is not firing, then start timer. Firing state is responsible to stopping the timer.
  5611. if( !IsTimerActive('RefireCheckTimer') )
  5612. {
  5613. // Let the perk adjust the rate of fire if needed;
  5614. AdjustedFireInterval = GetFireInterval(FireModeNum);
  5615.  
  5616. CurrentPerk = GetPerk();
  5617. if( CurrentPerk != none )
  5618. {
  5619. CurrentPerk.ModifyRateOfFire( AdjustedFireInterval, self );
  5620. }
  5621.  
  5622. SetTimer(AdjustedFireInterval, true, nameof(RefireCheckTimer) );
  5623. }
  5624. }
  5625.  
  5626. /*********************************************************************************************
  5627. * State WeaponSingleFiring
  5628. * Fire must be released between every shot.
  5629. *********************************************************************************************/
  5630.  
  5631. simulated state WeaponSingleFiring extends WeaponFiring
  5632. {
  5633. /** Handle ClearPendingFire */
  5634. simulated function FireAmmunition()
  5635. {
  5636. Super.FireAmmunition();
  5637.  
  5638. // ClearPendingFire flag each time so that player has to press fire button
  5639. // again. Called from BeginState and RefireCheckTimer
  5640. ClearPendingFire(CurrentFireMode);
  5641. }
  5642. }
  5643.  
  5644. /*********************************************************************************************
  5645. * State WeaponBurstFiring
  5646. * Fires a burst of bullets. Fire must be released between every shot.
  5647. *********************************************************************************************/
  5648.  
  5649. simulated function int GetBurstAmount() { return 1; }
  5650.  
  5651. simulated state WeaponBurstFiring extends WeaponFiring
  5652. {
  5653. simulated function BeginState(Name PrevStateName)
  5654. {
  5655. // Don't let us fire more shots than we have ammo for
  5656. BurstAmount = GetBurstAmount();
  5657.  
  5658. super.BeginState(PrevStateName);
  5659. }
  5660.  
  5661. simulated function int GetBurstAmount()
  5662. {
  5663. return Min( default.BurstAmount, AmmoCount[GetAmmoType(CurrentFireMode)] );
  5664. }
  5665.  
  5666. simulated function bool ShouldRefire()
  5667. {
  5668. // Stop firing when we hit the burst amount
  5669. if( 0 >= BurstAmount )
  5670. {
  5671. return false;
  5672. }
  5673. // if doesn't have ammo to keep on firing, then stop
  5674. else if( !HasAmmo( CurrentFireMode ) )
  5675. {
  5676. return false;
  5677. }
  5678. else
  5679. {
  5680. return true;
  5681. }
  5682. }
  5683.  
  5684. /**
  5685. * FireAmmunition: Perform all logic associated with firing a shot
  5686. * - Fires ammunition (instant hit or spawn projectile)
  5687. * - Consumes ammunition
  5688. * - Plays any associated effects (fire sound and whatnot)
  5689. * Overridden to decrement the BurstAmount
  5690. *
  5691. * Network: LocalPlayer and Server
  5692. */
  5693. simulated function FireAmmunition()
  5694. {
  5695. super.FireAmmunition();
  5696. BurstAmount--;
  5697. }
  5698.  
  5699. simulated event EndState( Name NextStateName )
  5700. {
  5701. Super.EndState(NextStateName);
  5702. EndFire(CurrentFireMode);
  5703. }
  5704. }
  5705.  
  5706. /*********************************************************************************************
  5707. * State GrenadeFiring
  5708. * Handles firing grenades.
  5709. *********************************************************************************************/
  5710.  
  5711. /** Synchronize the server if it's not quite finished with another state */
  5712. reliable server private function ServerGotoGrenadeFiring()
  5713. {
  5714. if( HasAmmo(GRENADE_FIREMODE) )
  5715. {
  5716. SendToFiringState(GRENADE_FIREMODE);
  5717. }
  5718. }
  5719.  
  5720. /** Set the cooldown time for weak zed grab for the pawn holding this weapon */
  5721. function SetWeakZedGrabCooldownOnPawn(float WeakZedGrabCooldown)
  5722. {
  5723. local KFPawn KFP;
  5724.  
  5725. if( Role == ROLE_Authority )
  5726. {
  5727. KFP = KFPawn(Instigator);
  5728.  
  5729. if( KFP != none )
  5730. {
  5731. KFP.SetWeakGrabCoolDown(WeakZedGrabCooldown);
  5732. }
  5733. }
  5734. }
  5735.  
  5736. simulated state GrenadeFiring extends WeaponSingleFiring
  5737. {
  5738. simulated function byte GetWeaponStateId()
  5739. {
  5740. return WEP_Grenade;
  5741. }
  5742.  
  5743. // Overriden to not call FireAmmunition right at the start of the state
  5744. simulated event BeginState( Name PreviousStateName )
  5745. {
  5746. `LogInv("PreviousStateName:" @ PreviousStateName);
  5747.  
  5748. // Force exit ironsights (affects IS toggle key bind)
  5749. if ( bUsingSights )
  5750. {
  5751. ZoomOut(false, default.ZoomOutTime);
  5752. }
  5753.  
  5754. // Don't let a weak zed grab us right after throwing a grenade
  5755. SetWeakZedGrabCooldownOnPawn(GrenadeTossWeakZedGrabCooldown);
  5756.  
  5757. PlayGrenadeThrow();
  5758. TimeWeaponFiring(CurrentFireMode);
  5759. ClearPendingFire(CurrentFireMode);
  5760.  
  5761. // Consume ammo right away instead of when grenade is spawned. This gives time
  5762. // for the client to get an updated grenade count before GotoState('Active')
  5763. if ( Role == ROLE_Authority && KFInventoryManager(InvManager) != none )
  5764. {
  5765. KFInventoryManager(InvManager).ConsumeGrenades();
  5766. }
  5767. else if ( Role < ROLE_Authority && Instigator.IsLocallyControlled() )
  5768. {
  5769. // force synchronize the server to spawn the projectile
  5770. ServerGotoGrenadeFiring();
  5771. }
  5772.  
  5773. NotifyBeginState();
  5774. }
  5775.  
  5776. simulated function EndState(Name NextStateName)
  5777. {
  5778. local byte FireModeSwap;
  5779.  
  5780. Super.EndState(NextStateName);
  5781. NotifyEndState();
  5782.  
  5783. // ProjectileFire needs GRENADE_FIREMODE. Could be something else if we're
  5784. // going directly (rare) to another firing state.
  5785. FireModeSwap = CurrentFireMode;
  5786. CurrentFireMode = GRENADE_FIREMODE;
  5787. // Spawn grenade, cannot be skipped once this state starts
  5788. ProjectileFire();
  5789. CurrentFireMode = FireModeSwap;
  5790. }
  5791.  
  5792. /**
  5793. * This function returns the world location for spawning the visual effects
  5794. * Overridden to use a special offset for throwing grenades
  5795. */
  5796. simulated event vector GetMuzzleLoc()
  5797. {
  5798. local Rotator ViewRotation;
  5799.  
  5800. if( Instigator != none )
  5801. {
  5802. ViewRotation = Instigator.GetViewRotation();
  5803.  
  5804. // Add in the free-aim rotation
  5805. if ( KFPlayerController(Instigator.Controller) != None )
  5806. {
  5807. ViewRotation += KFPlayerController(Instigator.Controller).WeaponBufferRotation;
  5808. }
  5809.  
  5810. return Instigator.GetPawnViewLocation() + (GrenadeFireOffset >> ViewRotation);
  5811. }
  5812.  
  5813. return Location;
  5814. }
  5815.  
  5816. /** Never refires. Must re-enter this state instead. */
  5817. simulated function bool ShouldRefire()
  5818. {
  5819. return false;
  5820. }
  5821. }
  5822.  
  5823. /** Play animation at the start of the GrenadeFiring state */
  5824. simulated function PlayGrenadeThrow()
  5825. {
  5826. local name WeaponFireAnimName;
  5827.  
  5828. PlayFiringSound(CurrentFireMode);
  5829.  
  5830. if( Instigator != none && Instigator.IsFirstPerson() )
  5831. {
  5832. WeaponFireAnimName = GetGrenadeThrowAnim();
  5833.  
  5834. if ( WeaponFireAnimName != '' )
  5835. {
  5836. PlayAnimation(WeaponFireAnimName, MySkelMesh.GetAnimLength(WeaponFireAnimName),,FireTweenTime);
  5837. }
  5838. }
  5839. }
  5840.  
  5841. /** Get nade throw anim name (overridden as necessary) */
  5842. simulated function name GetGrenadeThrowAnim()
  5843. {
  5844. return GrenadeThrowAnim;
  5845. }
  5846.  
  5847. /*********************************************************************************************
  5848. * State Reloading
  5849. * This is the default Reloading State. It's performed on both the client and the server.
  5850. *********************************************************************************************/
  5851.  
  5852. // Global declarations for reload state
  5853. simulated function ReloadStatusTimer();
  5854. simulated function ReloadAmmoTimer();
  5855. simulated function ReloadComplete();
  5856. simulated function AbortReload();
  5857.  
  5858. /** Called on local player when reload starts and replicated to server */
  5859. simulated function InitializeReload()
  5860. {
  5861. ReloadAmountLeft = Min(MagazineCapacity[0] - AmmoCount[0], SpareAmmoCount[0]);
  5862. InitialReloadAmount = ReloadAmountLeft;
  5863. InitialReloadSpareAmmo = SpareAmmoCount[0];
  5864.  
  5865. if ( Role < ROLE_Authority )
  5866. {
  5867. ServerSendToReload(ReloadAmountLeft);
  5868. }
  5869. }
  5870.  
  5871. /** Called from client when reload starts */
  5872. reliable server function ServerSendToReload(byte ClientReloadAmount)
  5873. {
  5874. ReloadAmountLeft = ClientReloadAmount;
  5875. InitialReloadAmount = ReloadAmountLeft;
  5876. InitialReloadSpareAmmo = SpareAmmoCount[0];
  5877. SendToFiringState(RELOAD_FIREMODE);
  5878. }
  5879.  
  5880. /**
  5881. * State Reloading
  5882. * State the weapon is in when it is being reloaded (current magazine replaced with a new one, related animations and effects played).
  5883. */
  5884. simulated state Reloading
  5885. {
  5886. ignores ForceReload, ShouldAutoReload, AllowSprinting;
  5887.  
  5888. simulated function byte GetWeaponStateId()
  5889. {
  5890. local KFPerk Perk;
  5891. local bool bTacticalReload;
  5892.  
  5893. Perk = GetPerk();
  5894. bTacticalReload = (Perk != None && Perk.GetUsingTactialReload(self));
  5895.  
  5896. if ( !bReloadFromMagazine )
  5897. {
  5898. if ( bTacticalReload )
  5899. {
  5900. return AmmoCount[0] > 0 ? WEP_ReloadSingle_Elite : WEP_ReloadSingleEmpty_Elite;
  5901. }
  5902. return AmmoCount[0] > 0 ? WEP_ReloadSingle : WEP_ReloadSingleEmpty;
  5903. }
  5904. else
  5905. {
  5906. if ( bTacticalReload )
  5907. {
  5908. return AmmoCount[0] > 0 ? WEP_Reload_Elite : WEP_ReloadEmpty_Elite;
  5909. }
  5910. return AmmoCount[0] > 0 ? WEP_Reload : WEP_ReloadEmpty;
  5911. }
  5912. }
  5913.  
  5914. /** When we have our spare ammo count replicated while reloading, it will force spare ammo to show
  5915. up incorrectly, as the server has not yet done it's reload in ServerSyncReload(). This fix forces
  5916. the spare ammo to be correct on the client */
  5917. simulated event ReplicatedEvent(name VarName)
  5918. {
  5919. local int ClientsideAmmoReloaded;
  5920.  
  5921. if (VarName == nameof(SpareAmmoCount))
  5922. {
  5923. // If the reload has not yet begun (spare ammo count will be the same on client and server) do not do any corrections.
  5924. ClientsideAmmoReloaded = (InitialReloadAmount - ReloadAmountLeft);
  5925.  
  5926. if(Role < Role_Authority && ClientsideAmmoReloaded > 0 && bAllowClientAmmoTracking )
  5927. {
  5928. // Re-apply the work already done to the spare ammo because of our reload (the server does not reload at the same time as the client)
  5929. SpareAmmoCount[0] -= Max(ClientsideAmmoReloaded, 0);
  5930. }
  5931. }
  5932. else
  5933. {
  5934. Global.ReplicatedEvent(VarName);
  5935. }
  5936. }
  5937.  
  5938. simulated function BeginState(name PreviousStateName)
  5939. {
  5940. local KFPerk InstigatorPerk;
  5941.  
  5942. InstigatorPerk = GetPerk();
  5943. if ( InstigatorPerk != none )
  5944. {
  5945. SetZedTimeResist(InstigatorPerk.GetZedTimeModifier(self));
  5946. }
  5947.  
  5948. // Leave ironsights
  5949. if ( bUsingSights )
  5950. {
  5951. ZoomOut(false, default.ZoomOutTime);
  5952. }
  5953.  
  5954. bEnableTiltSkelControl = false;
  5955. bPendingAutoSwitchOnDryFire = false;
  5956.  
  5957. ReloadStatus = RS_None;
  5958. TimeWeaponReloading();
  5959.  
  5960. NotifyBeginState();
  5961.  
  5962. `DialogManager.PlayReloadDialog( KFPawn(Instigator) );
  5963. }
  5964.  
  5965. simulated function EndState(Name NextStateName)
  5966. {
  5967. local int ActualReloadAmount;
  5968. ClearZedTimeResist();
  5969. ClearTimer(nameof(ReloadStatusTimer));
  5970. ClearTimer(nameof(ReloadAmmoTimer));
  5971. ClearPendingFire(RELOAD_FIREMODE);
  5972.  
  5973. `if(`USE_RELOAD_SYNC)
  5974. if ( bAllowClientAmmoTracking && Role < ROLE_Authority )
  5975. {
  5976. // Get how much total ammo was reloaded on the client side over the entire course of the reload.
  5977. ActualReloadAmount = InitialReloadAmount - ReloadAmountLeft;
  5978. // Sync spare ammo counts using initial spare ammo, and how much ammo has been reloaded since reload began.
  5979. ServerSyncReload(InitialReloadSpareAmmo - ActualReloadAmount);
  5980. }
  5981. `endif
  5982.  
  5983. CheckBoltLockPostReload();
  5984. NotifyEndState();
  5985.  
  5986. `DialogManager.PlayAmmoDialog( KFPawn(Instigator), float(SpareAmmoCount[0]) / float(GetMaxAmmoAmount(0)) );
  5987. }
  5988.  
  5989. simulated function BeginFire(byte FireModeNum)
  5990. {
  5991. Global.BeginFire(FireModeNum);
  5992.  
  5993. // handle reload interrupts
  5994. if ( FireModeNum != RELOAD_FIREMODE )
  5995. {
  5996. if ( !bReloadFromMagazine || CanOverrideMagReload(FireModeNum) )
  5997. {
  5998. // if able, immediately interupt/abort the reload state
  5999. if( PendingFire(FireModeNum) && HasAmmo(FireModeNum) )
  6000. {
  6001. AbortReload();
  6002. }
  6003. }
  6004. }
  6005. }
  6006.  
  6007. /** Cancel reload when going into ironsights */
  6008. simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo)
  6009. {
  6010. if ( !bReloadFromMagazine )
  6011. {
  6012. // Do zoom first in case Abort takes us to a state where sights are disallowed
  6013. Global.ZoomIn(bAnimateTransition, ZoomTimeToGo);
  6014.  
  6015. AbortReload();
  6016. }
  6017. else if( Role == ROLE_Authority && !Instigator.IsLocallyControlled() )
  6018. {
  6019. // In client-server situations, allow the client to tell the server when it's zooming in and out
  6020. Global.ZoomIn( bAnimateTransition, ZoomTimeToGo );
  6021. }
  6022. }
  6023.  
  6024. /** @see Weapon::TryPutDown() */
  6025. simulated function bool TryPutDown()
  6026. {
  6027. PutDownWeapon();
  6028. return TRUE;
  6029. }
  6030.  
  6031. /** Update Reload Status */
  6032. simulated function ReloadStatusTimer()
  6033. {
  6034. // If we haven't added ammo yet, do it now. Handles reloading when the
  6035. // weapon is missing notifies or the notify is very close to the end.
  6036. if ( ReloadStatus == RS_Reloading && IsTimerActive(nameof(ReloadAmmoTimer)) )
  6037. {
  6038. PerformReload();
  6039. ClearTimer(nameof(ReloadAmmoTimer));
  6040. }
  6041.  
  6042. TimeWeaponReloading();
  6043. }
  6044.  
  6045. /** Call Perform Reload */
  6046. simulated function ReloadAmmoTimer()
  6047. {
  6048. PerformReload();
  6049. }
  6050.  
  6051. /** Cleanup reload state */
  6052. simulated function ReloadComplete()
  6053. {
  6054. // we're done, leave state and go back to active
  6055. GotoState('Active');
  6056. }
  6057.  
  6058. /** Called when reload animation is interrupted */
  6059. simulated function AbortReload()
  6060. {
  6061. LastReloadAbortTime = WorldInfo.TimeSeconds;
  6062.  
  6063. // we're done, leave state and go back to active
  6064. GotoState('Active');
  6065. }
  6066.  
  6067. /** Gets the current animation rate, scaled or not */
  6068. simulated function float GetThirdPersonAnimRate()
  6069. {
  6070. return 1.f / GetReloadRateScale();
  6071. }
  6072. }
  6073.  
  6074. /** Called during reload state */
  6075. simulated function bool CanOverrideMagReload(byte FireModeNum)
  6076. {
  6077. if ( FireModeNum == BASH_FIREMODE || FireModeNum == GRENADE_FIREMODE )
  6078. {
  6079. return true;
  6080. }
  6081.  
  6082. return false;
  6083. }
  6084.  
  6085. /**
  6086. * Sets the timing for equipping a weapon.
  6087. * The WeaponEquipped event is trigged when expired
  6088. */
  6089. simulated function TimeWeaponReloading()
  6090. {
  6091. local name AnimName;
  6092. local float AnimLength, AnimRate;
  6093. local float AmmoTimer, StatusTimer;
  6094.  
  6095. ReloadStatus = GetNextReloadStatus();
  6096.  
  6097. // If we're finished exit reload
  6098. if ( ReloadStatus == RS_Complete || MySkelMesh == None )
  6099. {
  6100. ReloadComplete();
  6101. return;
  6102. }
  6103.  
  6104. // get desired animation and play-rate
  6105. AnimName = GetReloadAnimName( UseTacticalReload() );
  6106. AnimRate = GetReloadRateScale();
  6107. AnimLength = AnimRate * MySkelMesh.GetAnimLength(AnimName);
  6108.  
  6109. if ( AnimLength > 0.f )
  6110. {
  6111. MakeNoise(0.5f,'PlayerReload'); // AI
  6112.  
  6113. if ( Instigator.IsFirstPerson() )
  6114. {
  6115. PlayAnimation(AnimName, AnimLength);
  6116. }
  6117.  
  6118. // Start timer for when to give ammo (aka 'PerformReload')
  6119. if ( ReloadStatus == RS_Reloading )
  6120. {
  6121. AmmoTimer = AnimRate * MySkelMesh.GetReloadAmmoTime(AnimName);
  6122. SetTimer(AmmoTimer, FALSE, nameof(ReloadAmmoTimer));
  6123. }
  6124.  
  6125. // Start timer for when to continue (e.g. ReloadComplete, TimeWeaponReloading)
  6126. if ( bReloadFromMagazine || ReloadStatus == RS_ClosingBolt )
  6127. {
  6128. StatusTimer = AnimRate * MySkelMesh.GetAnimInterruptTime(AnimName);
  6129. }
  6130. else
  6131. {
  6132. StatusTimer = AnimLength;
  6133. }
  6134.  
  6135. SetTimer(StatusTimer, FALSE, nameof(ReloadStatusTimer));
  6136. }
  6137. else
  6138. {
  6139. `warn("Reload duration is zero! Anim="$AnimName@"Rate:"$AnimRate);
  6140. ReloadComplete();
  6141. }
  6142. }
  6143.  
  6144. /** Returns an anim rate scale for reloading */
  6145. simulated function float GetReloadRateScale()
  6146. {
  6147. local float Rate;
  6148. local KFPerk MyPerk;
  6149.  
  6150. Rate = 1.f;
  6151.  
  6152. MyPerk = GetPerk();
  6153. if( MyPerk != None )
  6154. {
  6155. Rate = MyPerk.GetReloadRateScale( self );
  6156. }
  6157.  
  6158. return Rate;
  6159. }
  6160.  
  6161. /** Access perk skill for tactical reload */
  6162. simulated function bool UseTacticalReload()
  6163. {
  6164. local KFPerk MyPerk;
  6165.  
  6166. MyPerk = GetPerk();
  6167. if ( MyPerk != None )
  6168. {
  6169. return MyPerk.GetUsingTactialReload(self);
  6170. }
  6171.  
  6172. return false;
  6173. }
  6174.  
  6175. /** Move to the next valid reload status */
  6176. simulated function EReloadStatus GetNextReloadStatus(optional byte FireModeNum)
  6177. {
  6178. // Magazine reloading
  6179. if ( bReloadFromMagazine )
  6180. {
  6181. if ( ReloadStatus < RS_Reloading )
  6182. {
  6183. return RS_Reloading;
  6184. }
  6185. return RS_Complete;
  6186. }
  6187.  
  6188. // Single shot reloading
  6189. switch ( ReloadStatus )
  6190. {
  6191. case RS_None:
  6192. return RS_OpeningBolt;
  6193. case RS_OpeningBolt:
  6194. return RS_Reloading;
  6195. case RS_Reloading:
  6196. if ( HasSpareAmmo(FiremodeNum) && ReloadAmountLeft > 0 )
  6197. {
  6198. return RS_Reloading;
  6199. }
  6200. return RS_ClosingBolt;
  6201. }
  6202.  
  6203. return RS_Complete;
  6204. }
  6205.  
  6206. /** Returns animation to play based on reload type and status */
  6207. simulated function name GetReloadAnimName( bool bTacticalReload )
  6208. {
  6209. if ( !bReloadFromMagazine )
  6210. {
  6211. switch ( ReloadStatus )
  6212. {
  6213. case RS_OpeningBolt:
  6214. if ( AmmoCount[0] == 0 )
  6215. {
  6216. // immediately skip reload status so that we start getting ammo
  6217. ReloadStatus = GetNextReloadStatus();
  6218. return bTacticalReload ? ReloadOpenInsertEliteAnim : ReloadOpenInsertAnim;
  6219. }
  6220. return (bTacticalReload) ? ReloadOpenEliteAnim : ReloadOpenAnim;
  6221. case RS_ClosingBolt:
  6222. return (bTacticalReload) ? ReloadCloseEliteAnim : ReloadCloseAnim;
  6223. }
  6224.  
  6225. return (bTacticalReload) ? ReloadSingleEliteAnim : ReloadSingleAnim;
  6226. }
  6227.  
  6228. // magazine relaod
  6229. if ( AmmoCount[0] > 0 )
  6230. {
  6231. return (bTacticalReload) ? ReloadNonEmptyMagEliteAnim : ReloadNonEmptyMagAnim;
  6232. }
  6233. else
  6234. {
  6235. return (bTacticalReload) ? ReloadEmptyMagEliteAnim : ReloadEmptyMagAnim;
  6236. }
  6237. }
  6238.  
  6239. /** Performs actual ammo reloading */
  6240. simulated function PerformReload(optional byte FireModeNum)
  6241. {
  6242. local int ReloadAmount;
  6243. local int AmmoType;
  6244.  
  6245. AmmoType = GetAmmoType(FireModeNum);
  6246.  
  6247. if ( bInfiniteSpareAmmo )
  6248. {
  6249. AmmoCount[AmmoType] = MagazineCapacity[AmmoType];
  6250. ReloadAmountLeft = 0;
  6251. return;
  6252. }
  6253.  
  6254. // Server only unless bAllowClientAmmoTracking
  6255. `if(`USE_RELOAD_SYNC)
  6256. if ( (Role == ROLE_Authority && !bAllowClientAmmoTracking) || (Instigator.IsLocallyControlled() && bAllowClientAmmoTracking) )
  6257. `else
  6258. if ( Role == ROLE_Authority || bAllowClientAmmoTracking )
  6259. `endif
  6260. {
  6261. if( GetMaxAmmoAmount(AmmoType) > 0 && SpareAmmoCount[AmmoType] > 0 )
  6262. {
  6263. if ( bReloadFromMagazine )
  6264. {
  6265. // clamp reload amount to spare ammo size
  6266. ReloadAmount = Min(ReloadAmountLeft, SpareAmmoCount[AmmoType]);
  6267. }
  6268. else
  6269. {
  6270. ReloadAmount = 1;
  6271. }
  6272.  
  6273. // increment and clamp magazine ammo
  6274. AmmoCount[AmmoType] = Min(AmmoCount[AmmoType] + ReloadAmount, MagazineCapacity[AmmoType]);
  6275.  
  6276. `if(`USE_RELOAD_SYNC)
  6277. // Update SpareAmmo, even if this is the client (for immediate feedback). This is safe as long as
  6278. // the server doesn't consume SpareAmmo first which is enforced by ServerSyncReload
  6279. SpareAmmoCount[AmmoType] -= ReloadAmount;
  6280. `else
  6281. if ( Role == ROLE_Authority )
  6282. {
  6283. // Only update SpareAmmo on the server and make the client wait, so if the
  6284. // server executes this code early the client won't consume ammo twice
  6285. SpareAmmoCount[AmmoType] -= ReloadAmount;
  6286. }
  6287. `endif
  6288. }
  6289. }
  6290.  
  6291. // decrement ReloadAmountLeft
  6292. if ( bReloadFromMagazine )
  6293. {
  6294. ReloadAmountLeft = 0;
  6295. }
  6296. else if ( ReloadAmountLeft > 0 )
  6297. {
  6298. ReloadAmountLeft--;
  6299. }
  6300. }
  6301.  
  6302. /**
  6303. * Called on reload end state if this weapon has unlocked the bolt
  6304. * during reload. If reload was interrupted before ammo was give
  6305. * the bolt should revert to previous state
  6306. */
  6307. simulated function CheckBoltLockPostReload()
  6308. {
  6309. if ( bCheckBoltLockPostReload )
  6310. {
  6311. if ( ReloadStatus < RS_Complete )
  6312. {
  6313. UpdateOutOfAmmoEffects(0.f);
  6314. }
  6315.  
  6316. bCheckBoltLockPostReload = false;
  6317. }
  6318. }
  6319.  
  6320. /*********************************************************************************************
  6321. * State WeaponSprinting
  6322. * This is the default Sprinting State. It's performed on both the client and the server.
  6323. *********************************************************************************************/
  6324.  
  6325. // Global declarations for sprinting state
  6326. simulated function SprintLoopTimer();
  6327. simulated function SprintRetryTimer();
  6328.  
  6329. /**
  6330. * State WeaponSprinting
  6331. * State the weapon is in when it is being reloaded (current magazine replaced with a new one, related animations and effects played).
  6332. */
  6333. simulated state WeaponSprinting
  6334. {
  6335. ignores ForceReload;
  6336.  
  6337. simulated function BeginState(name PreviousStateName)
  6338. {
  6339. PlaySprintStart();
  6340. }
  6341.  
  6342. simulated function EndState(Name NextStateName)
  6343. {
  6344. // skip the outro if sights interrupted because it interferes with the procedural anim
  6345. if ( !bUsingSights )
  6346. {
  6347. PlaySprintEnd();
  6348. }
  6349. }
  6350.  
  6351. simulated function SprintLoopTimer()
  6352. {
  6353. PlaySprintLoop();
  6354. }
  6355.  
  6356. simulated function SprintRetryTimer()
  6357. {
  6358. PlaySprintStart();
  6359. }
  6360.  
  6361. simulated function SetWeaponSprint(bool bNewSprintStatus)
  6362. {
  6363. if ( !bNewSprintStatus )
  6364. {
  6365. GotoState('Active');
  6366. }
  6367. }
  6368.  
  6369. simulated function SetIronSights(bool bNewIronSights)
  6370. {
  6371. Global.SetIronSights(bNewIronSights);
  6372.  
  6373. if ( bUsingSights )
  6374. {
  6375. StopPawnSprint(true);
  6376. }
  6377. }
  6378.  
  6379. /** Override BeginFire so that shooting/attacking will interrupt sprinting */
  6380. simulated function BeginFire(byte FireModeNum)
  6381. {
  6382. if( !bDeleteMe && Instigator != None )
  6383. {
  6384. Global.BeginFire(FireModeNum);
  6385.  
  6386. if( PendingFire(FireModeNum) && HasAmmo(FireModeNum) )
  6387. {
  6388. StopPawnSprint(false);
  6389. }
  6390. }
  6391. }
  6392. }
  6393.  
  6394. simulated function PlaySprintStart()
  6395. {
  6396. local KFPlayerController KFPC;
  6397. local name AnimName;
  6398. local float AnimDuration;
  6399.  
  6400. // Retry timer if we're in the middle of something that we don't want to interrupt
  6401. if ( bZoomingOut )
  6402. {
  6403. SetTimer(0.1f, false, nameof(SprintRetryTimer));
  6404. return;
  6405. }
  6406.  
  6407. AnimName = GetSprintStartAnimName();
  6408.  
  6409. if( Instigator.IsLocallyControlled() )
  6410. {
  6411. KFPC = KFPlayerController(Instigator.Controller);
  6412. if ( KFPC != None )
  6413. {
  6414. KFPC.HandleTransitionFOV(PlayerSprintFOV, 0.25);
  6415. }
  6416.  
  6417. PlayAnimation( AnimName );
  6418. }
  6419.  
  6420. // set timer to begin the looping state
  6421. AnimDuration = MySkelMesh.GetAnimLength( AnimName );
  6422. if ( AnimDuration > 0.f )
  6423. {
  6424. SetTimer(AnimDuration, false, nameof(SprintLoopTimer));
  6425. }
  6426. else
  6427. {
  6428. SprintLoopTimer();
  6429. }
  6430. }
  6431.  
  6432. /** Get sprint start anim name (overridden as necessary) */
  6433. simulated function name GetSprintStartAnimName()
  6434. {
  6435. return SprintStartAnim;
  6436. }
  6437.  
  6438. simulated function PlaySprintLoop()
  6439. {
  6440. local name AnimName;
  6441.  
  6442. if( Instigator.IsLocallyControlled() )
  6443. {
  6444. AnimName = GetSprintLoopAnimName();
  6445.  
  6446. SprintAnimRate = GetSprintAnimRate();
  6447.  
  6448. if (SprintCameraAnimInst != None)
  6449. {
  6450. SprintCameraAnimInst.Stop(TRUE);
  6451. }
  6452.  
  6453. if (SprintCameraAnim != None)
  6454. {
  6455. SprintCameraAnimInst = PlayCameraAnim(SprintCameraAnim, SprintAnimRate, SprintAnimRate, 0.3, 0.5, true, true);
  6456. }
  6457.  
  6458. PlayAnimation(AnimName, MySkelMesh.GetAnimLength(AnimName) / SprintAnimRate, true);
  6459. }
  6460. }
  6461.  
  6462. /** Get sprint loop anim name (overridden as necessary) */
  6463. simulated function name GetSprintLoopAnimName()
  6464. {
  6465. return SprintLoopAnim;
  6466. }
  6467.  
  6468. simulated function PlaySprintEnd()
  6469. {
  6470. local KFPlayerController KFPC;
  6471. local name AnimName;
  6472.  
  6473. if ( Instigator.IsLocallyControlled() )
  6474. {
  6475. AnimName = GetSprintEndAnimName();
  6476.  
  6477. KFPC = KFPlayerController(Instigator.Controller);
  6478. if ( KFPC != None && !bUsingSights )
  6479. {
  6480. KFPC.HandleTransitionFOV(KFPC.DefaultFOV, 0.25);
  6481. }
  6482.  
  6483. PlayAnimation(AnimName);
  6484. }
  6485. }
  6486.  
  6487. /** Get sprint end anim name (overridden as necessary) */
  6488. simulated function name GetSprintEndAnimName()
  6489. {
  6490. return SprintEndAnim;
  6491. }
  6492.  
  6493. simulated function float GetSprintAnimRate()
  6494. {
  6495. return 1.f;
  6496. }
  6497.  
  6498. /*********************************************************************************************
  6499. * State MeleeAttackBasic
  6500. * This is a basic melee state that's used as a base for other more advanced states
  6501. *********************************************************************************************/
  6502.  
  6503. /** Is this weapon currently in a melee attack firemode/state */
  6504. simulated function bool IsMeleeing();
  6505. /** Checks to see if we can perform another melee strike without changing states */
  6506. simulated function bool ShouldContinueMelee(optional int ChainCount);
  6507. /** Notification from the melee helper that a melee strike connected */
  6508. simulated function NotifyMeleeCollision(Actor HitActor, optional vector HitLocation);
  6509.  
  6510. /**
  6511. * state MeleeAttackBasic
  6512. * State the weapon is in when doing a melee attack
  6513. */
  6514. simulated state MeleeAttackBasic
  6515. {
  6516. ignores AllowSprinting, AllowIronSights;
  6517.  
  6518. simulated function bool IsMeleeing() { return TRUE; }
  6519.  
  6520. simulated function BeginState(Name PreviousStateName)
  6521. {
  6522. local KFPerk InstigatorPerk;
  6523.  
  6524. InstigatorPerk = GetPerk();
  6525. if( InstigatorPerk != none )
  6526. {
  6527. SetZedTimeResist( InstigatorPerk.GetZedTimeModifier(self) );
  6528. }
  6529.  
  6530. // Leave ironsights
  6531. if ( bUsingSights )
  6532. {
  6533. ZoomOut(false, default.ZoomOutTime);
  6534. }
  6535.  
  6536. TimeWeaponFiring(CurrentFireMode);
  6537. NotifyBeginState();
  6538. }
  6539.  
  6540. simulated function EndState(Name NextStateName)
  6541. {
  6542. ClearZedTimeResist();
  6543. ClearTimer( nameof(RefireCheckTimer) );
  6544. NotifyEndState();
  6545. }
  6546.  
  6547. /** Override BeginFire so that it will enter the firing state right away.
  6548. * @dweiss - Copied from state active in Weapon.uc. Melee weapons were skipping
  6549. * this code path on servers when quickly activating parry.
  6550. */
  6551. simulated function BeginFire(byte FireModeNum)
  6552. {
  6553. if (!bDeleteMe && Instigator != None)
  6554. {
  6555. Global.BeginFire(FireModeNum);
  6556.  
  6557. // in the active state, fire right away if we have the ammunition
  6558. if (PendingFire(FireModeNum) && HasAmmo(FireModeNum))
  6559. {
  6560. SendToFiringState(FireModeNum);
  6561. }
  6562. }
  6563. }
  6564.  
  6565. /** Allow the MeleeAttackHelper to do damage and setup timing */
  6566. simulated function TimeWeaponFiring(byte FireModeNum)
  6567. {
  6568. // For standard melee attacks, use the helper object
  6569. MeleeAttackHelper.BeginMeleeAttack();
  6570.  
  6571. // Unless this a loop/chain attack, clear pending fire.
  6572. // Do this at the beginning of an attack not the end, just like WeaponSingleFiring
  6573. if( PendingFire(CurrentFireMode) && !ShouldContinueMelee() )
  6574. {
  6575. ClearPendingFire(CurrentFireMode);
  6576. }
  6577. }
  6578.  
  6579. simulated function byte GetWeaponStateId()
  6580. {
  6581. return WEP_MeleeBasic;
  6582. }
  6583.  
  6584. /** Gets the current animation rate, scaled or not */
  6585. simulated function float GetThirdPersonAnimRate()
  6586. {
  6587. local KFPerk CurrentPerk;
  6588. local float ScaledRate;
  6589.  
  6590. ScaledRate = 1.0f;
  6591. CurrentPerk = GetPerk();
  6592. if ( CurrentPerk != none )
  6593. {
  6594. CurrentPerk.ModifyMeleeAttackSpeed( ScaledRate, self );
  6595. }
  6596.  
  6597. return 1.f / ScaledRate;
  6598. }
  6599. }
  6600.  
  6601. /** Called from the MeleeHelper class to allow for the weapon to override settings */
  6602. simulated function PlayMeleeAnimation(name AnimName, out float out_Rate, float BlendTime)
  6603. {
  6604. local KFPerk InstigatorPerk;
  6605. local float Duration;
  6606.  
  6607. InstigatorPerk = GetPerk();
  6608. if ( InstigatorPerk != none )
  6609. {
  6610. InstigatorPerk.ModifyMeleeAttackSpeed( out_Rate, self );
  6611. }
  6612.  
  6613. Duration = MySkelMesh.GetAnimLength(AnimName);
  6614. Duration *= out_Rate; // scale by desired rate
  6615. PlayAnimation(AnimName, Duration,, BlendTime);
  6616. }
  6617.  
  6618. /** returns the damage amount for this attack */
  6619. simulated function int GetMeleeDamage(byte FireModeNum, optional vector RayDir)
  6620. {
  6621. return InstantHitDamage[FireModeNum];
  6622. }
  6623.  
  6624. /*********************************************************************************************
  6625. * State WeaponSingleFireAndReload
  6626. * Fires a single shot and then calls ForceReload(). For server-side projectiles we also
  6627. * sync the firing state because of ammo/state sync issues.
  6628. *********************************************************************************************/
  6629.  
  6630. simulated function float GetForceReloadDelay();
  6631.  
  6632. /** Detect/fix single fire projectile weapon network synchronization errors */
  6633. reliable private server function ServerSyncWeaponFiring( byte FireModeNum )
  6634. {
  6635. local bool bNeedsToSync;
  6636.  
  6637. if( IsInState('Reloading') )
  6638. {
  6639. // Perform our reload if we haven't yet
  6640. if( ReloadStatus == RS_Reloading && IsTimerActive(nameOf(ReloadAmmoTimer)) )
  6641. {
  6642. PerformReload();
  6643. ClearTimer( nameOf(ReloadAmmoTimer) );
  6644. }
  6645.  
  6646. // Flag reload as complete
  6647. ReloadStatus = RS_Complete;
  6648.  
  6649. // May have not recieved ServerSyncReload yet! Do something!
  6650. if ( bAllowClientAmmoTracking && !HasAmmo(FireModeNum) )
  6651. {
  6652. ServerSyncReload(InitialReloadSpareAmmo - 1);
  6653. }
  6654.  
  6655. bNeedsToSync = true;
  6656. }
  6657. else if( IsInState('WeaponEquipping') || IsInState('MeleeAttackBasic') || IsInState('WeaponSprinting') )
  6658. {
  6659. bNeedsToSync = true;
  6660. }
  6661.  
  6662. if( bNeedsToSync )
  6663. {
  6664. // Move immediately to the firing state, as long as we have ammo
  6665. if( HasAmmo(FireModeNum) )
  6666. {
  6667. SendToFiringState( FireModeNum );
  6668. }
  6669. else
  6670. {
  6671. `warn("KFWeapon::ServerSyncWeaponFiring()."$GetStateName()$" - Failed to sync weapon ammo.");
  6672. }
  6673. }
  6674. }
  6675.  
  6676. simulated state WeaponSingleFireAndReload extends WeaponSingleFiring
  6677. {
  6678. // It is technically possible for ServerSyncWeaponFiring to call before the ServerSyncReload in the Reload state.
  6679. // If this happens, we don't want to allow ServerSyncReload to be called twice.
  6680. ignores ServerSyncReload;
  6681.  
  6682. simulated function FireAmmunition()
  6683. {
  6684. local float ReloadDelay;
  6685.  
  6686. Super.FireAmmunition();
  6687.  
  6688. if( Instigator.IsLocallyControlled() )
  6689. {
  6690. if( HasSpareAmmo() )
  6691. {
  6692. ReloadDelay = GetForceReloadDelay();
  6693. if ( ReloadDelay > 0.f )
  6694. {
  6695. SetTimer( ReloadDelay + GetFireInterval(CurrentFireMode), false, nameOf(ForceReload) );
  6696. }
  6697. else
  6698. {
  6699. // Reload after every shot, sets PendingFire for next 'active' state entry
  6700. StartFire( RELOAD_FIREMODE );
  6701. }
  6702. }
  6703.  
  6704. if( Instigator.Role < ROLE_Authority )
  6705. {
  6706. ServerSyncWeaponFiring( CurrentFireMode );
  6707. }
  6708. }
  6709. }
  6710. }
  6711.  
  6712. ///////////////////////////////////////////////////////////////////////////////////////////
  6713. //
  6714. // Trader
  6715. //
  6716. ///////////////////////////////////////////////////////////////////////////////////////////
  6717.  
  6718. /** Allows weapon to calculate its own stats for display in trader */
  6719. static simulated event SetTraderWeaponStats( out array<STraderItemWeaponStats> WeaponStats )
  6720. {
  6721. WeaponStats.Length = 4;
  6722.  
  6723. WeaponStats[0].StatType = TWS_Damage;
  6724. WeaponStats[0].StatValue = CalculateTraderWeaponStatDamage();
  6725.  
  6726. WeaponStats[1].StatType = TWS_Penetration;
  6727. WeaponStats[1].StatValue = default.PenetrationPower[DEFAULT_FIREMODE];
  6728.  
  6729. WeaponStats[2].StatType = TWS_Range;
  6730. // This is now set in native since EffectiveRange has been moved to KFWeaponDefinition
  6731. //WeaponStats[2].StatValue = CalculateTraderWeaponStatRange();
  6732.  
  6733. WeaponStats[3].StatType = TWS_RateOfFire;
  6734. WeaponStats[3].StatValue = CalculateTraderWeaponStatFireRate();
  6735. }
  6736.  
  6737. /** Allows weapon to calculate its own damage for display in trader */
  6738. static simulated function float CalculateTraderWeaponStatDamage()
  6739. {
  6740. local float CalculatedDamage;
  6741. local class<KFDamageType> DamageType;
  6742.  
  6743. CalculatedDamage = default.InstantHitDamage[DEFAULT_FIREMODE];
  6744.  
  6745. DamageType = class<KFDamageType>(default.InstantHitDamageTypes[DEFAULT_FIREMODE]);
  6746. if( DamageType != none && DamageType.default.DoT_Type != DOT_None )
  6747. {
  6748. CalculatedDamage += (DamageType.default.DoT_Duration / DamageType.default.DoT_Interval) * (CalculatedDamage * DamageType.default.DoT_DamageScale);
  6749. }
  6750.  
  6751. return CalculatedDamage;
  6752. }
  6753.  
  6754. /** Allows weapon to calculate its own fire rate for display in trader */
  6755. static simulated function float CalculateTraderWeaponStatFireRate()
  6756. {
  6757. return 60.f / default.FireInterval[DEFAULT_FIREMODE]; // attacks per minute
  6758. }
  6759.  
  6760. /** Returns trader filter index based on weapon type */
  6761. static simulated event EFilterTypeUI GetTraderFilter();
  6762.  
  6763. static simulated event EFilterTypeUI GetAltTraderFilter()
  6764. {
  6765. return FT_None;
  6766. }
  6767.  
  6768. defaultproperties
  6769. {
  6770. Begin Object class=KFAnimSeq_Tween Name=MeshSequenceA
  6771. bCauseActorAnimEnd=true
  6772. End Object
  6773.  
  6774. Begin Object Class=KFSkeletalMeshComponent Name=FirstPersonMesh
  6775. DepthPriorityGroup=SDPG_Foreground
  6776. bOnlyOwnerSee=true
  6777. CastShadow=true
  6778. bCastDynamicShadow=true
  6779. bAllowPerObjectShadows=true
  6780. bAllowBooleanPreshadows=false
  6781. bOverrideAttachmentOwnerVisibility=true
  6782. bPerBoneMotionBlur=false
  6783. bUpdateSkelWhenNotRendered=true // always update so we get notifies in third person
  6784. //Animations=MeshSequenceA
  6785. AnimTreeTemplate=AnimTree'CHR_1P_Arms_ARCH.WEP_1stP_Animtree_Master'
  6786. // Default to outdoor. If indoor, this will be set when TWIndoorLightingVolume::Touch() event is received at spawn.
  6787. LightingChannels=(Outdoor=TRUE,bInitialized=TRUE)
  6788. End Object
  6789. Mesh=FirstPersonMesh
  6790.  
  6791. /** Default pickup mesh */
  6792. Begin Object Class=StaticMeshComponent Name=StaticPickupComponent
  6793. StaticMesh=StaticMesh'EngineMeshes.Cube'
  6794. CastShadow=false
  6795. End Object
  6796. PickupFactoryMesh=StaticPickupComponent
  6797. DroppedPickupMesh=StaticPickupComponent
  6798. DroppedPickupClass=class'KFDroppedPickup'
  6799.  
  6800. // Ignore third person pawn rotation (handled by SetPosition)
  6801. // Now, calling P.SetRotation() after SetPosition is called will no longer cause problems
  6802. bIgnoreBaseRotation=true
  6803.  
  6804. // FOV
  6805. MeshFOV=86
  6806. MeshIronSightFOV=75
  6807. PlayerIronSightFOV=75
  6808. PlayerSprintFOV=95
  6809.  
  6810. // Depth of field
  6811. DOF_bOverrideEnvironmentDOF=true
  6812. DOF_SharpRadius=500.0
  6813. DOF_FocalRadius=1000.0
  6814. DOF_MinBlurSize=0.0
  6815. DOF_MaxNearBlurSize=2.0
  6816. DOF_MaxFarBlurSize=0.0
  6817. DOF_ExpFalloff=1.0
  6818. DOF_MaxFocalDistance=2000.0
  6819.  
  6820. DOF_BlendInSpeed=1.0
  6821. DOF_BlendOutSpeed=1.0
  6822.  
  6823. DOF_FG_FocalRadius=50
  6824. DOF_FG_SharpRadius=0
  6825. DOF_FG_MinBlurSize=0
  6826. DOF_FG_MaxNearBlurSize=3
  6827. DOF_FG_ExpFalloff=1
  6828.  
  6829. // Zooming/Position
  6830. PlayerViewOffset=(X=1.0,Y=9,Z=-3)
  6831. IronSightPosition=(X=0,Y=0,Z=0)
  6832. ZoomInTime=0.15
  6833. ZoomOutTime=0.15
  6834. FastZoomOutTime=0.1
  6835. ZoomInRotation=(Pitch=-910,Yaw=0,Roll=2910)
  6836. QuickWeaponDownRotation=(Pitch=-8192,Yaw=0,Roll=16384)
  6837. QuickWeaponDownTime=0.4
  6838. QuickWeaponDownFinishTime=0.2
  6839.  
  6840. // DEFAULT_FIREMODE
  6841. FireModeIconPaths[DEFAULT_FIREMODE]=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle'
  6842. FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring
  6843. WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit
  6844. FireInterval(DEFAULT_FIREMODE)=+1.0
  6845. InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic'
  6846. InstantHitMomentum(DEFAULT_FIREMODE)=1.0
  6847. PenetrationPower(DEFAULT_FIREMODE)=0.0
  6848. PenetrationDamageReductionCurve(DEFAULT_FIREMODE)=(Points=((InVal=0.f,OutVal=0.f),(InVal=1.f, OutVal=1.f)))
  6849. AmmoCost(DEFAULT_FIREMODE)=1
  6850.  
  6851. // ALTFIRE_FIREMODE
  6852. FireModeIconPaths[ALTFIRE_FIREMODE]=none
  6853. FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring
  6854. WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_InstantHit
  6855. FireInterval(ALTFIRE_FIREMODE)=+1.0
  6856. InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic'
  6857. InstantHitMomentum(ALTFIRE_FIREMODE)=1.0
  6858. PenetrationPower(ALTFIRE_FIREMODE)=0.0
  6859. PenetrationDamageReductionCurve(ALTFIRE_FIREMODE)=(Points=((InVal=0.f,OutVal=0.f),(InVal=1.f, OutVal=1.f)))
  6860. AmmoCost(ALTFIRE_FIREMODE)=1
  6861.  
  6862. // RELOAD
  6863. FiringStatesArray(RELOAD_FIREMODE)="Reloading"
  6864. WeaponFireTypes(RELOAD_FIREMODE)=EWFT_InstantHit
  6865.  
  6866. // Pickup
  6867. AmmoPickupScale[0]=1.0
  6868. AmmoPickupScale[1]=1.0
  6869.  
  6870. // BASH_FIREMODE
  6871. FiringStatesArray(BASH_FIREMODE)=MeleeAttackBasic
  6872. WeaponFireTypes(BASH_FIREMODE)=EWFT_Custom
  6873. InstantHitMomentum(BASH_FIREMODE)=30000.f
  6874. InstantHitDamage(BASH_FIREMODE)=15.0
  6875.  
  6876. // GRENADE_FIREMODE
  6877. FiringStatesArray(GRENADE_FIREMODE)=GrenadeFiring
  6878. WeaponFireTypes(GRENADE_FIREMODE)=EWFT_Projectile
  6879. FireInterval(GRENADE_FIREMODE)=0.6
  6880. GrenadeFireOffset=(X=25,Y=-15)
  6881. GrenadeTossWeakZedGrabCooldown=1.0
  6882.  
  6883. // spread variables
  6884. IronSightsSpreadMod=0.5
  6885. CrouchSpreadMod=0.75
  6886. MovingSpreadMod=1.0
  6887.  
  6888. // recoil variables
  6889. RecoilRate=0.09
  6890. RecoilBlendOutRatio=1.25
  6891. LastRecoilModifier=1.0
  6892. RecoilMaxYawLimit=2000
  6893. RecoilMinYawLimit=63535
  6894. RecoilMaxPitchLimit=1500
  6895. RecoilMinPitchLimit=64035
  6896. RecoilISMaxYawLimit=500
  6897. RecoilISMinYawLimit=65035
  6898. RecoilISMaxPitchLimit=350
  6899. RecoilISMinPitchLimit=65185
  6900. RecoilViewRotationScale=0.4
  6901. FullRecoilPitchPct=0.75
  6902. FullRecoilYawPct=0.75
  6903. RecoilCompensationScale=1.5
  6904. SuppressRecoilSpeed=0.08
  6905. SuppressRecoilViewRotationScale=0.5
  6906. WalkingRecoilModifier=1.25
  6907. JoggingRecoilModifier=1.5
  6908. FallingRecoilModifier=1.0
  6909. HippedRecoilModifier=1.75
  6910. StanceCrouchedRecoilModifier=0.75
  6911. IronSightMeshFOVCompensationScale=1.0
  6912.  
  6913. // Weaponlag
  6914. LagTensionHorizontal=0.8
  6915. LagVerticalTension=0.8
  6916. LagResistanceHorizontal=0.2
  6917. LagResistanceVertical=0.2
  6918. LagLimit=5.0
  6919. LagYawCoefficient=0.00005 // 0.00005 = Approx 3.25/65535.0 Represents 3.25 linear units per second == 360(65535) degrees per second.
  6920. LagStrengthIronSights=3.0
  6921. LagStrengthWalk=0.7
  6922. LagStrengthJog=0.6
  6923. LagStrengthSprint=0.5
  6924. LagStrengthCrouch=1.5
  6925. StrafeLagLimit=4.0
  6926. StrafeLagRate=0.025
  6927. StrafeLagReturnRate=10.0
  6928.  
  6929. // Melee
  6930. Begin Object Class=KFMeleeHelperWeapon Name=MeleeHelper_0
  6931. bUseMeleeHitTimer=false
  6932. MaxHitRange=175
  6933. End Object
  6934. MeleeAttackHelper=MeleeHelper_0
  6935.  
  6936. // Effects
  6937. WeaponFireSnd(DEFAULT_FIREMODE)=()
  6938. ShakeScaleStandard=1.0
  6939. ShakeScaleSighted=0.4
  6940. SingleFireSoundIndex=FIREMODE_NONE
  6941. NumBloodMapMaterials=1
  6942.  
  6943. BobDamping=0.85000
  6944. JumpDamping=1.0
  6945.  
  6946. LaserSightTemplate=KFLaserSightAttachment'FX_LaserSight_ARCH.Default_LaserSight_1P'
  6947.  
  6948. // Animation Blending
  6949. DefaultAnimSpeed=1.0
  6950. FireTweenTime=0.05
  6951. bUseAdditiveMoveAnim=true
  6952.  
  6953. //////////////////////////////////////
  6954. // Animation Names
  6955. /////////////////////////////////////
  6956. // Shooting Animations
  6957. FireAnim=Shoot
  6958. FireSightedAnims[0]=Shoot_Iron
  6959. FireLastAnim=Shoot_Last
  6960. FireLastSightedAnim=Shoot_Iron_Last
  6961. FireLoopAnim=ShootLoop
  6962. FireLoopSightedAnim=ShootLoop_Iron
  6963.  
  6964. // Equip/Idle
  6965. PutDownAnim=PutAway
  6966. EquipAnim=Equip
  6967. IdleAnims(0)=Idle
  6968. IdleSightedAnims(0)=Idle_Iron
  6969. IdleFidgetAnims=(Guncheck_v1, Guncheck_v2, Guncheck_v3)
  6970.  
  6971. // Advanced Looping (High RPM) Fire Effects
  6972. FireLoopStartAnim=ShootLoop_Start
  6973. FireLoopStartSightedAnim=ShootLoop_Iron_Start
  6974. FireLoopEndAnim=ShootLoop_End
  6975. FireLoopEndSightedAnim=ShootLoop_Iron_End
  6976.  
  6977. // melee
  6978. MeleeAttackAnims=(Bash)
  6979.  
  6980. // Equip/PutDown
  6981. EquipTime=+0.45
  6982. PutDownTime=+0.33
  6983. MinFiringPutDownPct=0.80
  6984. bUseAnimLenEquipTime=true
  6985.  
  6986. WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Default_Recoil'
  6987.  
  6988. BonesToLockOnEmpty=(RW_Bolt)
  6989.  
  6990. // inventory
  6991. InventoryGroup=IG_Primary
  6992. GroupPriority=0
  6993. bCanThrow=true
  6994. bDropOnDeath=true
  6995. WeaponSelectTexture=Texture2D'ui_weaponselect_tex.UI_WeaponSelect_AR15'
  6996. SecondaryAmmoTexture=Texture2D'UI_SecondaryAmmo_TEX.GasTank'
  6997. bCanRefillSecondaryAmmo=true
  6998. bNoMagazine=false
  6999. bAllowClientAmmoTracking=true
  7000.  
  7001. // Aim assist
  7002. bTargetFrictionEnabled=True
  7003. TargetFrictionDistanceMax=4000.0f
  7004. bTargetAdhesionEnabled=True
  7005. TargetAdhesionDistanceMax=2000.0f
  7006.  
  7007. // AI Warning
  7008. MaxAIWarningDistSQ=6250000
  7009. MaxAIWarningDistFromPointSQ=5625
  7010.  
  7011. TargetAdhesionDistanceScaleCurve={(Points=((InVal=0.100000,OutVal=1.000000),
  7012. (InVal=0.200000,OutVal=0.600000),
  7013. (InVal=0.300000,OutVal=0.400000),
  7014. (InVal=0.400000,OutVal=0.300000,InterpMode=CIM_CurveAuto),
  7015. (InVal=1.000000,OutVal=0.000000,InterpMode=CIM_CurveAuto)))}
  7016. TargetAdhesionOffsetScaleCurve={(Points=((InVal=0.000000,OutVal=1.000000),
  7017. (InVal=0.700000,OutVal=0.700000,InterpMode=CIM_CurveAuto),
  7018. (InVal=1.000000,OutVal=0.000000,InterpMode=CIM_CurveAuto)))}
  7019.  
  7020. TargetFrictionDistanceScaleCurve={(Points=((InVal=0.500000,OutVal=1.000000,InterpMode=CIM_CurveAuto),
  7021. (InVal=0.800000,OutVal=0.800000,InterpMode=CIM_CurveAuto),
  7022. (InVal=1.000000,OutVal=0.000000,InterpMode=CIM_CurveAuto)))}
  7023. TargetFrictionOffsetScaleCurve={(Points=((InVal=0.000000,OutVal=1.000000),
  7024. (InVal=0.900000,OutVal=0.900000,InterpMode=CIM_CurveAuto),
  7025. (InVal=1.000000,OutVal=0.000000,InterpMode=CIM_CurveAuto)))}
  7026. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement