//============================================================================= // KFWeapAttach_CompoundBow //============================================================================= // A custom 3rd person weapon template for the CompoundBow //============================================================================= // Killing Floor 2 // Copyright (C) 2020 Tripwire Interactive LLC // Tulio Beloqui (Saber Interactive) //============================================================================= class KFWeapAttach_CompoundBow extends KFWeaponAttachment; /** The static mesh for arrow **/ var() StaticMeshComponent ArrowSMC; /** The partcile FX to display for the Cryo Projectile **/ var ParticleSystem CryoProjectileEffectOn; var ParticleSystem CryoProjectileEffectOff; var() ParticleSystemComponent CryoProjectilePSC; /** set to True if we started charging **/ var bool bCharging; const WeaponIdleAnim = 'Idle_master'; const WeaponIronIdleAnim = 'Idle_Iron_V1'; const WeaponFireLoopStartAnim = 'ShootLoop_Start'; const WeaponFireLoopAnim = 'ShootLoop'; const WeaponFireLoopIronAnim = 'ShootLoop_Sight'; const WeaponFireEndLoopAnim = 'ShootLoop_End'; const CH_WeapondleAnim = 'Idle_CH_V1'; const CH_WeaponFireStartAnim = 'ShootLoop_Start_CH'; const CH_WeaponFireLoopAnim = 'ShootLoop_CH'; const CH_WeaponFireLoopIronAnim = 'ShootLoop_Sight_CH'; const CH_WeaponFireEndLoopAnim = 'ShootLoop_End_CH'; const ArrowSocketName = 'RW_Weapon'; simulated function StartFire() { local KFPawn_Human P; local name WeaponLoopAnimName; P = KFPawn_Human(Owner); bCharging = true; WeaponLoopAnimName = GetWeaponFireLoopAnim (P); // ShootLoop animation is determined in PlayLoopAnim(), our "outro" animation is the actual ShootLoop_End: if (P.bIsCrouched) { StartLoopingAnim(P, WeaponLoopAnimName, CH_WeaponFireStartAnim, '', true); } else { StartLoopingAnim(P, WeaponLoopAnimName, WeaponFireLoopStartAnim, '', true); } } simulated function PlayWeaponFireAnim() { local float Duration; if (Instigator.bIsCrouched) { Duration = WeapMesh.GetAnimLength( CH_WeaponFireEndLoopAnim ); WeapMesh.PlayAnim( CH_WeaponFireEndLoopAnim, Duration / ThirdPersonAnimRate,, true ); } else { Duration = WeapMesh.GetAnimLength( WeaponFireEndLoopAnim ); WeapMesh.PlayAnim( WeaponFireEndLoopAnim, Duration / ThirdPersonAnimRate,, true ); } } simulated function PlayShoot(KFPawn P) { PlayPawnFireAnim (P, EAS_Additive); if (P.bIsCrouched) { P.PlayBodyAnim(CH_WeaponFireEndLoopAnim, EAS_CH_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); } else { P.PlayBodyAnim(WeaponFireEndLoopAnim, EAS_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); } } simulated function PlayReloadMagazineAnim (EWeaponState NewWeaponState, KFPawn P) { Super.PlayReloadMagazineAnim (NewWeaponState, P); SetTimer( 0.15f, false, nameof(ShowArrowOnReload) ); } simulated function ShowArrowOnReload() { // We show the arrow since we are reloading we know that we have one SetArrowVisbility (true); } simulated function UpdateThirdPersonWeaponAction( EWeaponState NewWeaponState, KFPawn P, byte ThirdPersonAnimRateByte ) { local KFPawn_Human PHuman; PHuman = KFPawn_Human (P); Super.UpdateThirdPersonWeaponAction( NewWeaponState, P, ThirdPersonAnimRateByte ); switch (NewWeaponState) { case WEP_Grenade: SetArrowVisbility (false); break; case WEP_Equipping: case WEP_Idle: if (P.IsLocallyControlled ()) { ToggleShowHideArrow (P); } else { /** just show the arrow if we cannot access AmmoCount from MyKFWeapon **/ SetArrowVisbility (true); } /** restore Cryo visibility based on the Pawn alt fire mode **/ if (PHuman != none) { if (PHuman.bUsingAltFireMode) { ToggleCryoFX (true); } } break; default: break; } } simulated function bool ThirdPersonFireEffects (vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte) { bCharging = false; SpawnTracer(GetMuzzleLocation(), HitLocation); // Effects below this point are culled based on visibility and distance if ( !ActorEffectIsRelevant(P, false, MaxFireEffectDistance) ) { return false; } DecodeThirdPersonAnimRate( ThirdPersonAnimRateByte ); if (P.FiringMode == 0 || P.FiringMode == 1) { SetArrowVisbility (false); } // Weapon shoot anims PlayWeaponFireAnim(); // Character shoot anims if ( !P.IsDoingSpecialMove()) { PlayShoot (P); if ( OnWeaponStateChanged != None ) { OnWeaponStateChanged(true); } } CauseMuzzleFlash(P.FiringMode); return true; } simulated function ToggleShowHideArrow (KFPawn P) { if (P.MyKFWeapon != none) { if (P.MyKFWeapon.AmmoCount[0] > 0) { SetArrowVisbility (true); } else { SetArrowVisbility (false); } } } simulated function SetArrowVisbility (bool bVisible) { // Keep the arrow from the weapon mesh unvisible WeapMesh.HideBoneByName (ArrowSocketName, PBO_None); if (bVisible) { ArrowSMC.SetHidden(false); CryoProjectilePSC.SetHidden(false); } else { ArrowSMC.SetHidden(true); CryoProjectilePSC.SetHidden(true); } } simulated function StartPawnCrouch () { if (KFPawn_Human (Owner) != none) { ResetAnimationState (KFPawn_Human (Owner)); } } simulated function EndPawnCrouch () { if (KFPawn_Human (Owner) != none) { ResetAnimationState (KFPawn_Human (Owner)); } } simulated function SetWeaponUsingIronSights (bool bUsingIronSights) { if (KFPawn_Human (Owner) != none) { ResetAnimationState (KFPawn_Human (Owner)); } } simulated function SetWeaponAltFireMode (bool bUsingAltFireMode) { ToggleCryoFX (bUsingAltFireMode); } simulated function ResetAnimationState (KFPawn_Human P) { PlayCharacterMeshAnim (P, GetCharacterAnimationName(P), true, true); } simulated function ToggleCryoFX(bool bEnable) { if (bEnable) { CryoProjectilePSC.DeactivateSystem(); CryoProjectilePSC.SetTemplate(CryoProjectileEffectOn); CryoProjectilePSC.ActivateSystem(); } else { CryoProjectilePSC.DeactivateSystem(); CryoProjectilePSC.SetTemplate(CryoProjectileEffectOff); CryoProjectilePSC.ActivateSystem(); } } simulated function name GetWeaponFireLoopAnim (KFPawn_Human P) { if (P.bIsCrouched && P.bUsingIronSights) { return CH_WeaponFireLoopIronAnim; } if (P.bIsCrouched) { return CH_WeaponFireLoopAnim; } if (P.bUsingIronSights) { return WeaponFireLoopIronAnim; } return WeaponFireLoopAnim; } simulated function name GetCharacterAnimationName (KFPawn_Human P) { if (bCharging) { return GetWeaponFireLoopAnim (P); } if (P.bUsingIronSights && P.bIsCrouched) { return CH_WeaponFireLoopIronAnim; } if (P.bUsingIronSights) { return WeaponIronIdleAnim; } if (P.bIsCrouched) { return CH_WeapondleAnim; } return WeaponIdleAnim; } simulated function AttachTo (KFPawn P) { local byte WeaponAnimSetIdx; if ( WeapMesh != None ) { // Attach Weapon mesh to player skel mesh WeapMesh.SetShadowParent(P.Mesh); P.Mesh.AttachComponent (WeapMesh, 'LW_Weapon'); // Keep the arrow from the weapon mesh unvisible WeapMesh.HideBoneByName (ArrowSocketName, PBO_None); } // Animation if ( CharacterAnimSet != None ) { WeaponAnimSetIdx = P.CharacterArch.GetWeaponAnimSetIdx(); P.Mesh.AnimSets[WeaponAnimSetIdx] = CharacterAnimSet; // update animations will reload all AnimSeqs with the new AnimSet P.Mesh.UpdateAnimations(); } // update aim offset nodes with new profile for this weapon P.SetAimOffsetNodesProfile(AimOffsetProfileName); //Do a first chance weapon skin switch (EX: changed weapon w/o ID changing by throwing a dualie) if (KFPawn_Human(P) != None && KFPawn_Human(P).WeaponSkinItemId > 0) { SetWeaponSkin(KFPawn_Human(P).WeaponSkinItemId); } if (ArrowSMC != none) { P.Mesh.AttachComponentToSocket(ArrowSMC, ArrowSocketName); } if (CryoProjectilePSC != none) { P.Mesh.AttachComponentToSocket(CryoProjectilePSC, ArrowSocketName); } if (P.MyKFWeapon != none && P.MyKFWeapon.bUseAltFireMode) { ToggleCryoFX (true); } } simulated function DetachFrom(KFPawn P) { if (CryoProjectilePSC != none) { CryoProjectilePSC.DeactivateSystem(); CryoProjectilePSC.DetachFromAny(); } if (ArrowSMC != none) { P.DetachComponent(ArrowSMC); ArrowSMC.DetachFromAny(); } Super.DetachFrom(P); } event SetWeaponSkin(int ItemId, optional bool bFinishedLoading = false) { local array SkinMICs; if ( WeapMesh != none && ArrowSMC != none && ItemId > 0 && WorldInfo.NetMode != NM_DedicatedServer && !bWaitingForWeaponSkinLoad) { if (!bFinishedLoading && StartLoadWeaponSkin(ItemId)) { return; } SkinMICs = class'KFWeaponSkinList'.static.GetWeaponSkin(ItemId, WST_ThirdPerson); if ( SkinMICs.Length > 0 ) { WeapMesh.SetMaterial(0, SkinMICs[0]); ArrowSMC.SetMaterial(0, SkinMICs[0]); } } } defaultproperties { Begin Object Class=StaticMeshComponent Name=ArrowStaticMeshComp0 StaticMesh=StaticMesh'WEP_3P_CompoundBow_MESH.Wep_3rdP_Compound_Bow_Arrow_Resource' End Object ArrowSMC=ArrowStaticMeshComp0 Begin Object Class=KFParticleSystemComponent Name=CryoArrowParticleComp0 bAutoActivate=FALSE TickGroup=TG_PostUpdateWork End Object CryoProjectilePSC=CryoArrowParticleComp0 CryoProjectileEffectOn=ParticleSystem'WEP_CompoundBow_EMIT.FX_Compound_Bow_Cryo_Idle_3P' CryoProjectileEffectOff=ParticleSystem'WEP_CompoundBow_EMIT.FX_Compound_Bow_Cryo_ToggleOff' }