From 9ed2045549502697fd720b7a55d1cd4074bf0018 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Fri, 3 Sep 2021 00:46:08 +0300 Subject: [PATCH] upload --- KFGame/Classes/KFAutoPurchaseHelper.uc | 77 +- KFGame/Classes/KFCharacterInfo_Human.uc | 97 ++- KFGame/Classes/KFCharacterInfo_Monster.uc | 39 + ...pecialEventObjectivesContainer_Fall2021.uc | 27 + KFGame/Classes/KFGFxMenu_Gear.uc | 44 +- KFGame/Classes/KFGFxMenu_Perks.uc | 21 + KFGame/Classes/KFGFxMenu_Store.uc | 11 + .../Classes/KFGFxOptionsMenu_GameSettings.uc | 27 + .../KFGFxStartGameContainer_FindGame.uc | 16 +- KFGame/Classes/KFGFxStoreContainer_Main.uc | 33 +- .../KFGFxTraderContainer_PlayerInventory.uc | 13 +- KFGame/Classes/KFGameInfo.uc | 26 +- KFGame/Classes/KFGameReplicationInfo.uc | 24 +- KFGame/Classes/KFGfxMenu_StartGame.uc | 2 +- KFGame/Classes/KFGfxMenu_Trader.uc | 9 + KFGame/Classes/KFImpactEffectManager.uc | 7 +- KFGame/Classes/KFInventoryManager.uc | 19 +- KFGame/Classes/KFOnlineStatsReadDingo.uc | 2 + KFGame/Classes/KFOnlineStatsWrite.uc | 9 + KFGame/Classes/KFOnlineStatsWriteDingo.uc | 1 + KFGame/Classes/KFOutbreakEvent.uc | 13 + KFGame/Classes/KFPawn_Human.uc | 32 +- KFGame/Classes/KFPawn_Monster.uc | 50 ++ KFGame/Classes/KFPerk_Berserker.uc | 2 +- KFGame/Classes/KFPerk_Commando.uc | 7 +- KFGame/Classes/KFPerk_Gunslinger.uc | 26 +- KFGame/Classes/KFPerk_Sharpshooter.uc | 63 +- KFGame/Classes/KFPerk_Support.uc | 5 +- KFGame/Classes/KFPerk_Survivalist.uc | 5 +- KFGame/Classes/KFPerk_Swat.uc | 69 +- KFGame/Classes/KFPlayerController.uc | 60 +- .../KFPlayerController_WeeklySurvival.uc | 11 + KFGame/Classes/KFPlayerInput.uc | 5 + KFGame/Classes/KFPlayerReplicationInfo.uc | 1 + KFGame/Classes/KFProfileSettings.uc | 5 +- KFGame/Classes/KFProj_RicochetStickBullet.uc | 11 +- KFGame/Classes/KFSM_Frozen.uc | 6 +- KFGame/Classes/KFSkinTypeEffects.uc | 6 +- KFGame/Classes/KFUnlockManager.uc | 5 + KFGame/Classes/KFWeapDef_BladedPistol.uc | 25 + KFGame/Classes/KFWeapDef_DualBladed.uc | 26 + KFGame/Classes/KFWeapDef_HRG_Boomy.uc | 30 + KFGame/Classes/KFWeapDef_HRG_Energy.uc | 25 + KFGame/Classes/KFWeapDef_MedicBat.uc | 2 +- KFGame/Classes/KFWeapDef_ParasiteImplanter.uc | 25 + KFGame/Classes/KFWeapon.uc | 6 +- KFGame/Classes/KFWeaponAttachment.uc | 4 + KFGame/Classes/KFWeaponSkinList.uc | 159 +++- KFGame/Classes/KFWeeklyOutbreakInformation.uc | 2 +- KFGame/KFOnlineStats.uci | 1 + KFGame/KFProfileSettings.uci | 1 + .../Classes/KFDT_Ballistic_HRG_Boomy.uc | 27 + .../KFDT_Ballistic_HRG_Energy_Primary.uc | 29 + .../KFDT_Ballistic_HRG_Energy_Secondary.uc | 28 + .../KFDT_Ballistic_ParasiteImplanter.uc | 59 ++ .../KFDT_Ballistic_ParasiteImplanterAlt.uc | 36 + .../Classes/KFDT_Bludgeon_BladedPistol.uc | 136 ++++ .../Classes/KFDT_Bludgeon_HRG_Boomy.uc | 15 + .../Classes/KFDT_Bludgeon_HRG_Energy.uc | 16 + .../KFDT_Bludgeon_ParasiteImplanter.uc | 16 + .../Classes/KFDT_Explosive_HRG_Boomy.uc | 30 + .../Classes/KFDT_Healing_ParasiteSeed.uc | 16 + .../Classes/KFDT_Slashing_BladedPistol.uc | 190 +++++ .../KFDT_Toxic_ParasiteSeedExplosion.uc | 29 + .../Classes/KFExplosion_ParasiteSeed.uc | 97 +++ .../Classes/KFExplosion_ParasiteSeedHuman.uc | 26 + .../Classes/KFGameInfo_WeeklySurvival.uc | 25 +- .../KFGameReplicationInfo_WeeklySurvival.uc | 1 + .../Classes/KFOutbreakEvent_Weekly.uc | 112 +++ KFGameContent/Classes/KFPawn_ZedClot_Alpha.uc | 2 + .../Classes/KFPawn_ZedClot_Slasher.uc | 2 + KFGameContent/Classes/KFPawn_ZedCrawler.uc | 2 + KFGameContent/Classes/KFPawn_ZedDAR.uc | 2 + KFGameContent/Classes/KFPawn_ZedFleshpound.uc | 2 + .../Classes/KFPawn_ZedFleshpoundMini.uc | 2 + KFGameContent/Classes/KFPawn_ZedHusk.uc | 2 + KFGameContent/Classes/KFPawn_ZedMatriarch.uc | 2 + KFGameContent/Classes/KFPawn_ZedScrake.uc | 2 + KFGameContent/Classes/KFPawn_ZedSiren.uc | 2 + .../Classes/KFProj_Blade_BladedPistol.uc | 79 ++ .../Classes/KFProj_Bullet_HRG_Boomy.uc | 79 ++ .../Classes/KFProj_Bullet_HRG_Energy.uc | 23 + .../KFProj_Bullet_HRG_Energy_Secondary.uc | 21 + .../KFProj_Bullet_ParasiteImplanter.uc | 22 + .../KFProj_Bullet_ParasiteImplanterAlt.uc | 382 ++++++++++ .../Classes/KFProj_Mine_Reconstructor.uc | 4 +- .../Classes/KFProj_Rocket_ThermiteBore.uc | 23 + ...ProjectileStickHelper_ParasiteImplanter.uc | 205 ++++++ .../Classes/KFSeasonalEventStats_Fall2021.uc | 117 +++ KFGameContent/Classes/KFWeapAttach_FAMAS.uc | 62 +- .../Classes/KFWeapAttach_HRG_Energy.uc | 106 +++ .../Classes/KFWeapAttach_ParasiteImplanter.uc | 108 +++ .../Classes/KFWeap_AssaultRifle_FAMAS.uc | 9 +- .../Classes/KFWeap_Blunt_MedicBat.uc | 10 +- .../Classes/KFWeap_HRG_BlastBrawlers.uc | 16 +- KFGameContent/Classes/KFWeap_HRG_Boomy.uc | 192 +++++ KFGameContent/Classes/KFWeap_HRG_Energy.uc | 510 +++++++++++++ .../Classes/KFWeap_Mine_Reconstructor.uc | 12 +- KFGameContent/Classes/KFWeap_Pistol_Bladed.uc | 228 ++++++ .../Classes/KFWeap_Pistol_DualBladed.uc | 679 ++++++++++++++++++ .../Classes/KFWeap_Rifle_ParasiteImplanter.uc | 428 +++++++++++ 101 files changed, 5216 insertions(+), 199 deletions(-) create mode 100644 KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Fall2021.uc create mode 100644 KFGame/Classes/KFWeapDef_BladedPistol.uc create mode 100644 KFGame/Classes/KFWeapDef_DualBladed.uc create mode 100644 KFGame/Classes/KFWeapDef_HRG_Boomy.uc create mode 100644 KFGame/Classes/KFWeapDef_HRG_Energy.uc create mode 100644 KFGame/Classes/KFWeapDef_ParasiteImplanter.uc create mode 100644 KFGameContent/Classes/KFDT_Ballistic_HRG_Boomy.uc create mode 100644 KFGameContent/Classes/KFDT_Ballistic_HRG_Energy_Primary.uc create mode 100644 KFGameContent/Classes/KFDT_Ballistic_HRG_Energy_Secondary.uc create mode 100644 KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanter.uc create mode 100644 KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanterAlt.uc create mode 100644 KFGameContent/Classes/KFDT_Bludgeon_BladedPistol.uc create mode 100644 KFGameContent/Classes/KFDT_Bludgeon_HRG_Boomy.uc create mode 100644 KFGameContent/Classes/KFDT_Bludgeon_HRG_Energy.uc create mode 100644 KFGameContent/Classes/KFDT_Bludgeon_ParasiteImplanter.uc create mode 100644 KFGameContent/Classes/KFDT_Explosive_HRG_Boomy.uc create mode 100644 KFGameContent/Classes/KFDT_Healing_ParasiteSeed.uc create mode 100644 KFGameContent/Classes/KFDT_Slashing_BladedPistol.uc create mode 100644 KFGameContent/Classes/KFDT_Toxic_ParasiteSeedExplosion.uc create mode 100644 KFGameContent/Classes/KFExplosion_ParasiteSeed.uc create mode 100644 KFGameContent/Classes/KFExplosion_ParasiteSeedHuman.uc create mode 100644 KFGameContent/Classes/KFProj_Blade_BladedPistol.uc create mode 100644 KFGameContent/Classes/KFProj_Bullet_HRG_Boomy.uc create mode 100644 KFGameContent/Classes/KFProj_Bullet_HRG_Energy.uc create mode 100644 KFGameContent/Classes/KFProj_Bullet_HRG_Energy_Secondary.uc create mode 100644 KFGameContent/Classes/KFProj_Bullet_ParasiteImplanter.uc create mode 100644 KFGameContent/Classes/KFProj_Bullet_ParasiteImplanterAlt.uc create mode 100644 KFGameContent/Classes/KFProjectileStickHelper_ParasiteImplanter.uc create mode 100644 KFGameContent/Classes/KFSeasonalEventStats_Fall2021.uc create mode 100644 KFGameContent/Classes/KFWeapAttach_HRG_Energy.uc create mode 100644 KFGameContent/Classes/KFWeapAttach_ParasiteImplanter.uc create mode 100644 KFGameContent/Classes/KFWeap_HRG_Boomy.uc create mode 100644 KFGameContent/Classes/KFWeap_HRG_Energy.uc create mode 100644 KFGameContent/Classes/KFWeap_Pistol_Bladed.uc create mode 100644 KFGameContent/Classes/KFWeap_Pistol_DualBladed.uc create mode 100644 KFGameContent/Classes/KFWeap_Rifle_ParasiteImplanter.uc diff --git a/KFGame/Classes/KFAutoPurchaseHelper.uc b/KFGame/Classes/KFAutoPurchaseHelper.uc index 4ec3e88..27634df 100644 --- a/KFGame/Classes/KFAutoPurchaseHelper.uc +++ b/KFGame/Classes/KFAutoPurchaseHelper.uc @@ -30,6 +30,8 @@ var int PrevArmor; var int CostPerAutofillCycle; var int DoshBuffer; +var int ArmorMagSize; // Percentage of armor bought individually + function Initialize(optional bool bInitOwnedItems = true) { if( Pawn != none && PlayerReplicationInfo != none ) @@ -431,13 +433,18 @@ function bool UpgradeWeapon(int OwnedItemIndex) *******************/ function int GetFillArmorCost() { - local float ArmorPercentage, FillCost, ArmorPricePerPercent; + // local float ArmorPercentage, FillCost, ArmorPricePerPercent; - ArmorPercentage = (float(ArmorItem.MaxSpareAmmo - ArmorItem.SpareAmmoCount) / float(ArmorItem.MaxSpareAmmo)) * 100.f; - ArmorPricePerPercent = ArmorItem.AmmoPricePerMagazine; - FillCost = FCeil( ArmorPercentage * ArmorPricePerPercent ); + // ArmorPercentage = (float() / float(ArmorItem.MaxSpareAmmo)) * 100.f; + // ArmorPricePerPercent = ArmorItem.AmmoPricePerMagazine; + // FillCost = FCeil( (ArmorItem.SpareAmmoCount - ArmorItem.MaxSpareAmmo) * ArmorItem.AmmoPricePerMagazine ); - return FillCost; + return FCeil( (ArmorItem.MaxSpareAmmo - ArmorItem.SpareAmmoCount) * ArmorItem.AmmoPricePerMagazine ); +} + +function int GetChunkArmorCost() +{ + return FCeil ( FMin(ArmorItem.MaxSpareAmmo - ArmorItem.SpareAmmoCount, ArmorMagSize) * ArmorItem.AmmoPricePerMagazine ); } function int FillArmor( ) @@ -470,29 +477,72 @@ function int FillArmor( ) return FillCost; } +function int BuyArmorMag( ) +{ + local float ArmorPricePerPercent; + local float ChunkCost; + local float ArmorAvailable; + local float PercentArmorBought; + local float ArmorAmmountBought; + + ArmorAvailable = (1 - float(ArmorItem.SpareAmmoCount) / ArmorItem.MaxSpareAmmo) * 100; + ArmorAmmountBought = ArmorMagSize; + PercentArmorBought = ArmorMagSize * 100.0f / ArmorItem.MaxSpareAmmo; + + if (ArmorAvailable < PercentArmorBought) + return FillArmor(); + + ChunkCost = ArmorAmmountBought * ArmorItem.AmmoPricePerMagazine; + // Buy as much armor as we possibly can + if (ChunkCost > TotalDosh) + { + ArmorPricePerPercent = ArmorItem.AmmoPricePerMagazine; + + // Because we are using ints this will round down and we can get how much we actually spent + PercentArmorBought = TotalDosh / ArmorPricePerPercent; + ChunkCost = ArmorPricePerPercent * PercentArmorBought; + ArmorAmmountBought = ArmorItem.MaxSpareAmmo * PercentArmorBought / 100; + } + + PercentArmorBought = (PercentArmorBought > 0.f && PercentArmorBought < 1.f) ? 1.f : PercentArmorBought; + ArmorItem.SpareAmmoCount = FMin(ArmorItem.SpareAmmoCount + ArmorAmmountBought, ArmorItem.MaxSpareAmmo); + + BoughtAmmo(PercentArmorBought, ChunkCost, EIT_Armor); + return ChunkCost; +} + + function bool AttemptBuyArmorChunk( out int InAutoFillDosh ) { - local float ArmorPricePerPercent, ChunkCost; - local int PercentArmorBought; + local float PercentArmorBought; + local int ArmorPricePerPercent; + local int ChunkCost; + local int ActualArmorPointsAvailable; + + ActualArmorPointsAvailable = ArmorItem.MaxSpareAmmo - ArmorItem.SpareAmmoCount; ArmorPricePerPercent = ArmorItem.AmmoPricePerMagazine; - PercentArmorBought = 0; + PercentArmorBought = 0; + if( ArmorItem.SpareAmmoCount < ArmorItem.MaxSpareAmmo ) { // Because we are using int's this will round down and we can get how much we actually spent - PercentArmorBought = CostPerAutofillCycle / ArmorPricePerPercent; - ChunkCost = ArmorPricePerPercent * PercentArmorBought; + PercentArmorBought = FMin(CostPerAutofillCycle / ArmorPricePerPercent, float(ActualArmorPointsAvailable)); + ChunkCost = ArmorPricePerPercent * PercentArmorBought; + if( InAutoFillDosh < ChunkCost ) { - PercentArmorBought = InAutoFillDosh / ArmorPricePerPercent; - ChunkCost = ArmorPricePerPercent * PercentArmorBought; + PercentArmorBought = FMin(InAutoFillDosh / ArmorPricePerPercent, float(ActualArmorPointsAvailable)); + ChunkCost = ArmorPricePerPercent * PercentArmorBought; } InAutoFillDosh -= ChunkCost; - ArmorItem.SpareAmmoCount = FMin(ArmorItem.SpareAmmoCount + PercentArmorBought, ArmorItem.MaxSpareAmmo); + PercentArmorBought = (PercentArmorBought > 0.f && PercentArmorBought < 1.f) ? 1.f : PercentArmorBought; + ArmorItem.SpareAmmoCount = FMin(ArmorItem.SpareAmmoCount + (PercentArmorBought / 100.f * ArmorItem.MaxSpareAmmo), ArmorItem.MaxSpareAmmo); BoughtAmmo(PercentArmorBought, ChunkCost, EIT_Armor); } + return (PercentArmorBought > 0); } @@ -1429,4 +1479,5 @@ DefaultProperties //defaults CostPerAutofillCycle=10 DoshBuffer=150 + ArmorMagSize=25.0f; } \ No newline at end of file diff --git a/KFGame/Classes/KFCharacterInfo_Human.uc b/KFGame/Classes/KFCharacterInfo_Human.uc index a36b00b..ac33ee1 100644 --- a/KFGame/Classes/KFCharacterInfo_Human.uc +++ b/KFGame/Classes/KFCharacterInfo_Human.uc @@ -192,6 +192,11 @@ var(FaveWeapons) editoronly class FavoriteWeaponClassDefs[NU /************************************************************************/ var Animset EmoteAnimset; +/************************************************************************/ +/* Wild West London Weekly */ +/************************************************************************/ +var transient LinearColor WWLHatMonoChromeValue; +var transient LinearColor WWLHatColorValue; /************************************************************************/ /* Native Functions */ @@ -771,6 +776,44 @@ protected simulated function SetAttachmentSkinMaterial( } } +protected simulated function SetWeeklyCowboyAttachmentSkinMaterial( + int PawnAttachmentIndex, + const out AttachmentVariants CurrentVariant, + byte NewSkinIndex, + KFPawn KFP, + optional bool bIsFirstPerson) +{ + local MaterialInstanceConstant MIC; + + if (KFP.WorldInfo.NetMode != NM_DedicatedServer) + { + if (bIsFirstPerson) + { + if (KFP.FirstPersonAttachments[PawnAttachmentIndex] != none) + { + KFP.FirstPersonAttachments[PawnAttachmentIndex].SetMaterial( + CurrentVariant.AttachmentItem.SkinMaterialID, + CurrentVariant.AttachmentItem.SkinVariations[0].Skin1p); + + MIC = MaterialInstanceConstant(KFP.FirstPersonAttachments[PawnAttachmentIndex].GetMaterial(0)); + } + } + else + { + KFP.ThirdPersonAttachments[PawnAttachmentIndex].SetMaterial( + CurrentVariant.AttachmentItem.SkinMaterialID, + CurrentVariant.AttachmentItem.SkinVariations[0].Skin); + MIC = MaterialInstanceConstant(KFP.ThirdPersonAttachments[PawnAttachmentIndex].GetMaterial(0)); + } + + if (MIC != none) + { + MIC.SetVectorParameterValue('color_monochrome', WWLHatMonoChromeValue); + MIC.SetVectorParameterValue('Black_White_switcher', WWLHatColorValue); + } + } +} + /** Called on owning client to change a cosmetic attachment or on other clients via replication */ private function SetAttachmentMeshAndSkin( int CurrentAttachmentMeshIndex, @@ -781,6 +824,8 @@ private function SetAttachmentMeshAndSkin( local string CharAttachmentMeshName; local name CharAttachmentSocketName; local int AttachmentSlotIndex; + local KFGameReplicationInfo KFGRI; + if (KFP.WorldInfo.NetMode == NM_DedicatedServer) { return; @@ -825,12 +870,25 @@ private function SetAttachmentMeshAndSkin( SetAttachmentMesh(CurrentAttachmentMeshIndex, AttachmentSlotIndex, CharAttachmentMeshName, CharAttachmentSocketName, KFP.ArmsMesh, KFP, true); - SetAttachmentSkinMaterial( - AttachmentSlotIndex, - CosmeticVariants[CurrentAttachmentMeshIndex], - CurrentAttachmentSkinIndex, - KFP, - true); + KFGRI = KFGameReplicationInfo(KFP.WorldInfo.GRI); + if (AttachmentSlotIndex == 2 && KFP != none && KFGRI.bIsWeeklyMode && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + { + SetWeeklyCowboyAttachmentSkinMaterial( + AttachmentSlotIndex, + CosmeticVariants[CurrentAttachmentMeshIndex], + CurrentAttachmentSkinIndex, + KFP, + true); + } + else + { + SetAttachmentSkinMaterial( + AttachmentSlotIndex, + CosmeticVariants[CurrentAttachmentMeshIndex], + CurrentAttachmentSkinIndex, + KFP, + true); + } } else { @@ -858,12 +916,25 @@ private function SetAttachmentMeshAndSkin( SetAttachmentMesh(CurrentAttachmentMeshIndex, AttachmentSlotIndex, CharAttachmentMeshName, CharAttachmentSocketName, KFP.Mesh, KFP, false); - SetAttachmentSkinMaterial( - AttachmentSlotIndex, - CosmeticVariants[CurrentAttachmentMeshIndex], - CurrentAttachmentSkinIndex, - KFP, - false); + KFGRI = KFGameReplicationInfo(KFP.WorldInfo.GRI); + if (AttachmentSlotIndex == 2 && KFP != none && KFGRI.bIsWeeklyMode && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + { + SetWeeklyCowboyAttachmentSkinMaterial( + AttachmentSlotIndex, + CosmeticVariants[CurrentAttachmentMeshIndex], + CurrentAttachmentSkinIndex, + KFP, + false); + } + else + { + SetAttachmentSkinMaterial( + AttachmentSlotIndex, + CosmeticVariants[CurrentAttachmentMeshIndex], + CurrentAttachmentSkinIndex, + KFP, + false); + } } } @@ -1165,4 +1236,6 @@ defaultproperties { SoundGroupArch=KFPawnSoundGroup'FX_Pawn_Sounds_ARCH.HumanPawnSounds' EmoteAnimset=AnimSet'ECON_emote.ECON_Emotes' + WWLHatMonoChromeValue=(R=1.0f,G=0.0f,B=0.0f) + WWLHatColorValue=(R=0.0f,G=0.0f,B=0.0f) } \ No newline at end of file diff --git a/KFGame/Classes/KFCharacterInfo_Monster.uc b/KFGame/Classes/KFCharacterInfo_Monster.uc index 36793f1..f1a7229 100644 --- a/KFGame/Classes/KFCharacterInfo_Monster.uc +++ b/KFGame/Classes/KFCharacterInfo_Monster.uc @@ -131,6 +131,14 @@ var(Gore) float ExplosionGibScale ; +/************************************************************************/ +/* Wild West London Weekly */ +/************************************************************************/ +var string ZEDCowboyHatMeshPath; + +var transient LinearColor WWLHatMonoChromeValue; +var transient LinearColor WWLHatColorValue; + /************************************************************************/ /* Script Functions */ /************************************************************************/ @@ -145,6 +153,9 @@ simulated function SetCharacterMeshFromArch( KFPawn KFP, optional KFPlayerReplic local LinearColor AppliedColor; local array ExtraMICs; local MaterialInstanceConstant ExtraMIC; + local StaticAttachments NewAttachment; + local KFGameReplicationInfo KFGRI; + local MaterialInstanceConstant NewMIC; super.SetCharacterMeshFromArch( KFP, KFPRI ); @@ -244,6 +255,29 @@ simulated function SetCharacterMeshFromArch( KFPawn KFP, optional KFPlayerReplic } } } + + KFGRI = KFGameReplicationInfo(KFP.WorldInfo.GRI); + if (KFP != none && KFGRI.bIsWeeklyMode && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + { + NewAttachment.StaticAttachment = StaticMesh(DynamicLoadObject(ZEDCowboyHatMeshPath, class'StaticMesh')); + NewAttachment.AttachSocketName = KFPawn_Monster(KFP).ZEDCowboyHatAttachName; + + StaticAttachment = new (KFP) class'StaticMeshComponent'; + if (StaticAttachment != none) + { + KFPawn_Monster(KFP).StaticAttachList.AddItem(StaticAttachment); + StaticAttachment.SetActorCollision(false, false); + StaticAttachment.SetStaticMesh(NewAttachment.StaticAttachment); + StaticAttachment.SetShadowParent(KFP.Mesh); + StaticAttachment.SetLightingChannels(KFP.PawnLightingChannel); + NewMIC = StaticAttachment.CreateAndSetMaterialInstanceConstant(0); + NewMIC.SetVectorParameterValue('color_monochrome', WWLHatMonoChromeValue); + NewMIC.SetVectorParameterValue('Black_White_switcher', WWLHatColorValue); + ExtraMICs.AddItem(NewMIC); + KFP.AttachComponent(StaticAttachment); + KFP.Mesh.AttachComponentToSocket(StaticAttachment, NewAttachment.AttachSocketName); + } + } } // Initialize MICs @@ -286,4 +320,9 @@ defaultproperties //GoreMeshLOD=-1 ExplosionGibScale=1.f ExplosionImpulseScale=1.f + + WWLHatMonoChromeValue=(R=1.0f,G=0.0f,B=0.0f) + WWLHatColorValue=(R=1.0f,G=0.0f,B=0.0f) + + ZEDCowboyHatMeshPath = "CHR_CosmeticSet01_MESH.cowboyhat.CHR_CowboyHat_Alberts_Cosmetic" } diff --git a/KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Fall2021.uc b/KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Fall2021.uc new file mode 100644 index 0000000..92a69db --- /dev/null +++ b/KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Fall2021.uc @@ -0,0 +1,27 @@ +class KFGFXSpecialEventObjectivesContainer_Fall2021 extends KFGFxSpecialEventObjectivesContainer; + +function Initialize(KFGFxObject_Menu NewParentMenu) +{ + super.Initialize(NewParentMenu); +} + +DefaultProperties +{ + ObjectiveIconURLs[0] = "Xmas_UI.UI_Objectives_Xmas_BossKill02" // Kill 15 Bosses on any map or mode + ObjectiveIconURLs[1] = "Spring_UI.UI_Objectives_Spring_Weekly" // Complete the Weekly on Netherhold + ObjectiveIconURLs[2] = "halloween2021_ui.UI_Objective_Halloween_2021_Im_hearing_heartbeats" // Find the nether heart + ObjectiveIconURLs[3] = "halloween2021_ui.UI_Objective_Halloween_2021_Purple_Miasma" // Unlock the chapel and the dining hall doors + ObjectiveIconURLs[4] = "halloween2021_ui.UI_Objective_Halloween_2021_Eternal_Punishment" // Complete wave 15 on Endless Hard or higher difficulty on Netherhold + + //defaults + AllCompleteRewardIconURL="CHR_CosmeticSet_Halloween_05_Item_TEX.muertos_companion.diadelosmuertoscompanion_precious" + ChanceDropIconURLs[0]="CHR_CosmeticSet14_Item_TEX.Tickets.CyberPunk_ticket" + ChanceDropIconURLs[1]="CHR_CosmeticSet14_Item_TEX.Tickets.CyberPunk_ticket_golden" + IconURL="Halloween2021_UI.KF2_Halloween_DayOfTheZED_SmallLogo" + + UsesProgressList[0] = true + UsesProgressList[1] = false + UsesProgressList[2] = false + UsesProgressList[3] = false + UsesProgressList[4] = false +} \ No newline at end of file diff --git a/KFGame/Classes/KFGFxMenu_Gear.uc b/KFGame/Classes/KFGFxMenu_Gear.uc index 9a82aa6..d14ebfa 100644 --- a/KFGame/Classes/KFGFxMenu_Gear.uc +++ b/KFGame/Classes/KFGFxMenu_Gear.uc @@ -63,6 +63,11 @@ var string ClearImagePath; var array EmoteList; +cpptext +{ + static INT GetCowboyHatIndex(const UKFCharacterInfo_Human* CharInfo); +} + function InitializeMenu( KFGFxMoviePlayer_Manager InManager ) { super.InitializeMenu(InManager); @@ -78,6 +83,8 @@ function InitializeMenu( KFGFxMoviePlayer_Manager InManager ) UpdateCharacterList(); UpdateGear(); TraderItems = KFGameReplicationInfo( KFPlayerController(GetPC()).WorldInfo.GRI ).TraderItems; + + ForceWeeklyCowboyHat(); } function OnOpen() @@ -236,6 +243,8 @@ function UpdateGear() UpdateEmoteList(); SetCurrentCharacterButtons(); + + ForceWeeklyCowboyHat(); } else { @@ -295,6 +304,10 @@ function UpdateAttachmentsList(array Attachments) local Pawn MyPawn; local SkinVariant FirstSkin; local string AttachmentName; + local PlayerController PC; + local bool bIsWildWest; + + bIsWildWest = false; ItemIndex = 0; DataProvider = CreateArray(); MyPawn = GetPC().Pawn; @@ -308,11 +321,19 @@ function UpdateAttachmentsList(array Attachments) DataProvider.SetElementObject(ItemIndex, SlotObject); ItemIndex++; + PC = GetPC(); + bIsWildWest = (PC != none && Pc.WorldInfo.GRI.IsA('KFGameReplicationInfo_WeeklySurvival') && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)); + for (i = 0; i < Attachments.length; i++) { Variant = Attachments[i]; - if ( CurrentCharInfo.IsAttachmentAvailable(Variant, MyPawn) ) + if ( CurrentCharInfo.IsAttachmentAvailable(Variant, MyPawn) ) { + if (bIsWildWest && Variant.SocketName == 'HAT_Attach') + { + continue; + } + SlotObject = CreateObject( "Object" ); SlotObject.SetInt("ItemIndex", i); FirstSkin = UpdateCosmeticVariants( AttachmentKey, AttachmentSkinKey, Variant.AttachmentItem, i, SlotObject ); @@ -698,6 +719,7 @@ private function Callback_AttachmentNumbered(int MeshIndex, int SkinIndex, int S local Pawn P; local KFPawn KFP; local array RemovedAttachments; + P = GetPC().Pawn; if( P != none ) { @@ -747,9 +769,29 @@ function RelayFromCheatManager(Pawn P, ECustomizationOption CustomizationOption, Manager.CachedProfile.SetCharacterGear(MyKFPRI.RepCustomizationInfo); } +function ForceWeeklyCowboyHat() +{ + local PlayerController PC; + local int CowboyHatIndex; + PC = GetPC(); + + if (PC != none && Pc.WorldInfo.GRI.IsA('KFGameReplicationInfo_WeeklySurvival') && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + { + CowboyHatIndex = FindCowboyHatAttachmentIndex(CurrentCharInfo); + if (CowboyHatIndex >= 0) + { + Callback_AttachmentNumbered(CowboyHatIndex,0,2); + } + + SetBool("ThirdAttachmentBlocked", true); + } +} + /** Update our character parts when the UI is being used */ native private function SelectCharacter(Pawn P, byte CharacterIndex); native private function SelectCustomizationOption(Pawn P, ECustomizationOption CustomizationOption, int MeshIndex, int SkinIndex, optional int AttachmentIndex); +// native static function INT GetCowboyHatIndex(KFCharacterInfo_Human CharInfo); +native static function int FindCowboyHatAttachmentIndex(KFCharacterInfo_Human CharInfo); defaultproperties { diff --git a/KFGame/Classes/KFGFxMenu_Perks.uc b/KFGame/Classes/KFGFxMenu_Perks.uc index eba6ff5..7177791 100644 --- a/KFGame/Classes/KFGFxMenu_Perks.uc +++ b/KFGame/Classes/KFGFxMenu_Perks.uc @@ -112,6 +112,8 @@ event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget) function OnOpen() { + local KFGameReplicationInfo KFGRI; + LastPerkIndex = KFPC.SavedPerkIndex; MyKFPRI = KFPlayerReplicationInfo( GetPC().PlayerReplicationInfo ); @@ -120,6 +122,25 @@ function OnOpen() `log("NO KFPC!!"); KFPC = KFPlayerController(GetPC()); } + + // Current Perk not allowed, search for the first one + KFGRI = KFGameReplicationInfo( KFPC.WorldInfo.GRI ); + if( KFGRI != none && !KFGRI.IsPerkAllowed(KFPC.PerkList[LastPerkIndex].PerkClass) ) + { + if (KFGRI.PerksAvailableData.bBerserkerAvailable) LastPerkIndex=0; + else if (KFGRI.PerksAvailableData.bCommandoAvailable) LastPerkIndex=1; + else if (KFGRI.PerksAvailableData.bSupportAvailable) LastPerkIndex=2; + else if (KFGRI.PerksAvailableData.bFieldMedicAvailable) LastPerkIndex=3; + else if (KFGRI.PerksAvailableData.bDemolitionistAvailable) LastPerkIndex=4; + else if (KFGRI.PerksAvailableData.bFirebugAvailable) LastPerkIndex=5; + else if (KFGRI.PerksAvailableData.bGunslingerAvailable) LastPerkIndex=6; + else if (KFGRI.PerksAvailableData.bSharpshooterAvailable) LastPerkIndex=7; + else if (KFGRI.PerksAvailableData.bSwatAvailable) LastPerkIndex=8; + else if (KFGRI.PerksAvailableData.bSurvivalistAvailable) LastPerkIndex=9; + else LastPerkIndex=0; + KFPC.SavedPerkIndex = LastPerkIndex; + } + UpdateSkillsHolder(KFPC.PerkList[KFPC.SavedPerkIndex].PerkClass); UpdateContainers(KFPC.PerkList[KFPC.SavedPerkIndex].PerkClass); diff --git a/KFGame/Classes/KFGFxMenu_Store.uc b/KFGame/Classes/KFGFxMenu_Store.uc index 78780ea..43ed397 100644 --- a/KFGame/Classes/KFGFxMenu_Store.uc +++ b/KFGame/Classes/KFGFxMenu_Store.uc @@ -271,6 +271,17 @@ function GFxObject CreateStoreItem(ItemProperties DesiredStoreItem) DataObject.SetString("imageURLLarge", "img://"$DesiredStoreItem.IconURLLarge); DataObject.SetInt("SKU", DesiredStoreItem.Definition); + if( DesiredStoreItem.ItemOnSale && DesiredStoreItem.BasePrice != DesiredStoreItem.Price) + { + DataObject.SetString("itemOnSale", DesiredStoreItem.ItemOnSale ? "1" : "0"); + DataObject.SetString("itemPriceBase", DesiredStoreItem.BasePrice); + + if(DesiredStoreItem.DiscountRate != "" && DesiredStoreItem.DiscountRate != "0") + { + DataObject.SetString("discountRate", DesiredStoreItem.DiscountRate); + } + } + return DataObject; } diff --git a/KFGame/Classes/KFGFxOptionsMenu_GameSettings.uc b/KFGame/Classes/KFGFxOptionsMenu_GameSettings.uc index 89f084f..fface4e 100644 --- a/KFGame/Classes/KFGFxOptionsMenu_GameSettings.uc +++ b/KFGame/Classes/KFGFxOptionsMenu_GameSettings.uc @@ -42,6 +42,8 @@ var localized string HideRemodeHeadshotEffectsString; var localized string ToggleToRunString; var localized string ClassicPlayerInfoString; +var localized string AllowSwap9mmString; + var float FOVMinValue, FOVMaxValue, FOVCurrentValue; var float FriendlyHudScaleMinValue, FriendlyHudScaleMaxValue; @@ -99,6 +101,8 @@ function LocalizeText() //LocalizedObject.SetString("reduceHighPitchNoise", ReduceHighPitchNoiseString); //LocalizedObject.SetString("antiMotionSickness", AntiMotionSicknessString); + LocalizedObject.SetString("allowSwap9mmLabel", AllowSwap9mmString); + SetObject("localizedText", LocalizedObject); } @@ -143,6 +147,8 @@ function InitValues() DataObject.SetBool("enableToggleToRun", Manager.CachedProfile.GetProfileBool(KFID_ToggletoRun)); DataObject.SetBool("enableClassicPlayerInfo", Manager.CachedProfile.GetProfileBool(KFID_ClassicPlayerInfo)); + DataObject.SetBool("allowSwapTo9mm", Manager.CachedProfile.GetProfileBool(KFID_AllowSwapTo9mm)); + if(class'WorldInfo'.static.IsConsoleBuild(CONSOLE_Durango)) { DataObject.SetBool("bDingo", true); @@ -542,6 +548,25 @@ function Callback_ClassicPlayerInfoChanged(bool bActive) } } +function Callback_AllowSwapTo9mm(bool bActive) +{ + local OnlineProfileSettings Settings; + local KFPlayerInput KFPI; + + Settings = class'GameEngine'.static.GetOnlineSubsystem().PlayerInterface.GetProfileSettings(GetLP().ControllerId); + KFPI = KFPlayerInput(GetPC().PlayerInput); + + if (Settings != none) + { + Settings.SetProfileSettingValueInt(KFID_AllowSwapTo9mm, bActive ? 1 : 0); + } + + if (KFPI != none) + { + KFPI.bAllowSwapTo9mm = bActive; + } +} + function ResetGameOptions() { //local KFPlayerController KFPC; @@ -599,6 +624,8 @@ function ResetGameOptions() Callback_ClassicPlayerInfoChanged(Manager.CachedProfile.GetDefaultInt(KFID_ClassicPlayerInfo) != 0); + Callback_AllowSwapTo9mm(Manager.CachedProfile.GetDefaultInt(KFID_AllowSwapTo9mm) != 0); + InitValues(); } diff --git a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc index 77f16f6..d8f2f57 100644 --- a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc +++ b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc @@ -182,31 +182,31 @@ function FillWhatsNew() local SWhatsNew item; WhatsNewItems.Remove(0, WhatsNewItems.Length); // Latest Update - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer_InterstellarInsanity_Event", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween_DayOfTheZED_Event", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); WhatsNewItems.AddItem(item); // KF2 Armory Season Pass 2021 item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Spring_Armory_Season_Pass", "ArmorySeasonPass", "https://store.steampowered.com/app/1524820/Killing_Floor_2__Armory_Season_Pass"); WhatsNewItems.AddItem(item); // Featured Time Limited Item - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_SS_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/4928"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/9119"); WhatsNewItems.AddItem(item); // Featured Cosmetic Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer_Astronaut", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/8953"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween_DoZ", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9120"); WhatsNewItems.AddItem(item); // Featured Cosmetic Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer_Foundry","FeaturedItemBundle","https://store.steampowered.com/buyitem/232090/8956"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween_Vietnam","FeaturedItemBundle","https://store.steampowered.com/buyitem/232090/9122"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer_BeyondHorizon", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/8955"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween_Hellmark", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9121"); WhatsNewItems.AddItem(item); // Featured Weapon Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer_FamasMasterkey", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/8957"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween_PiranhaPistol", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9123"); WhatsNewItems.AddItem(item); // Featured Weapon Bundle - item=SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer_ThermiteBore", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/8958"); + item=SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween_CorrupterCarbine", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9124"); WhatsNewItems.AddItem(item); // Featured Weapon Bundle - item=SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer_Weaponsbundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/8959"); + item=SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_halloween_Weaponsbundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9125"); WhatsNewItems.AddItem(item); // Misc Community Links item=SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_CommunityHub", "Jaegorhorn", "https://steamcommunity.com/app/232090"); diff --git a/KFGame/Classes/KFGFxStoreContainer_Main.uc b/KFGame/Classes/KFGFxStoreContainer_Main.uc index 24a38ca..62173e9 100644 --- a/KFGame/Classes/KFGFxStoreContainer_Main.uc +++ b/KFGame/Classes/KFGFxStoreContainer_Main.uc @@ -341,11 +341,12 @@ function GFxObject CreateStoreItem(ItemProperties StoreItem) DataObject.SetString("description", StoreItem.Description); //`log("Price"@StoreItem.Price@"- BasePrice"@StoreItem.BasePrice@"- DiscountRate"@StoreItem.DiscountRate@"- ItemOnSale"@StoreItem.ItemOnSale); - if( StoreItem.ItemOnSale ) + if( StoreItem.ItemOnSale && StoreItem.BasePrice != StoreItem.Price) { DataObject.SetString("itemOnSale", StoreItem.ItemOnSale ? "1" : "0"); DataObject.SetString("itemPriceBase", StoreItem.BasePrice); - if(StoreItem.DiscountRate != "") + + if(StoreItem.DiscountRate != "" && StoreItem.DiscountRate != "0") { DataObject.SetString("discountRate", StoreItem.DiscountRate); } @@ -446,21 +447,21 @@ DefaultProperties XboxFilterExceptions[0]="Wasteland Bundle" // Wasteland Outfit Bundle - FeaturedItemIDs[0]=8178 - FeaturedItemIDs[1]=8953 - FeaturedItemIDs[2]=8959 - FeaturedItemIDs[3]=8956 - FeaturedItemIDs[4]=8955 - FeaturedItemIDs[5]=8957 - FeaturedItemIDs[6]=8958 + FeaturedItemIDs[0]=7619 //Whatsnew Gold Ticket + FeaturedItemIDs[1]=9119 + FeaturedItemIDs[2]=9120 + FeaturedItemIDs[3]=9121 + FeaturedItemIDs[4]=9122 + FeaturedItemIDs[5]=9123 + FeaturedItemIDs[6]=9124 - ConsoleFeaturedItemIDs[0]=8181 - ConsoleFeaturedItemIDs[1]=8953 - ConsoleFeaturedItemIDs[2]=8959 - ConsoleFeaturedItemIDs[3]=8956 - ConsoleFeaturedItemIDs[4]=8955 - ConsoleFeaturedItemIDs[5]=8957 - ConsoleFeaturedItemIDs[6]=8958 + ConsoleFeaturedItemIDs[0]=7783 //Whatsnew Gold Ticket PSN + ConsoleFeaturedItemIDs[1]=9119 + ConsoleFeaturedItemIDs[2]=9120 + ConsoleFeaturedItemIDs[3]=9121 + ConsoleFeaturedItemIDs[4]=9122 + ConsoleFeaturedItemIDs[5]=9123 + ConsoleFeaturedItemIDs[6]=9124 MaxFeaturedItems=5 } \ No newline at end of file diff --git a/KFGame/Classes/KFGFxTraderContainer_PlayerInventory.uc b/KFGame/Classes/KFGFxTraderContainer_PlayerInventory.uc index 3866011..d521672 100644 --- a/KFGame/Classes/KFGFxTraderContainer_PlayerInventory.uc +++ b/KFGame/Classes/KFGFxTraderContainer_PlayerInventory.uc @@ -54,6 +54,8 @@ function LocalizeContainer() LocalizedObject.SetString("changePerk", ChangePerkString); LocalizedObject.SetString("grenadeLabel", BuyGrenadeString); + LocalizedObject.SetString("armorBuyLabel", class'KFAutoPurchaseHelper'.default.ArmorMagSize$"X"); + //Prompt strings LocalizedObject.SetString("sellPrompt", Localize("KFGFxTraderContainer_ItemDetails", "SellString", "KFGame")); LocalizedObject.SetString("perkPrompt", Localize("KFGFxTraderContainer_PlayerInventory", "PerkPrompt", "KFGameConsole")); @@ -155,19 +157,26 @@ function RefreshPlayerInventory() function SetArmorInfo(out SItemInformation ArmorInfo, out int AutoFillCost) { local GFxObject SlotObject; - local int FillCost; + local int FillCost, ChunkCost; + local int ButtonState; FillCost = KFPC.GetPurchaseHelper().GetFillArmorCost(); + ChunkCost = KFPC.GetPurchaseHelper().GetChunkArmorCost(); SlotObject = CreateObject( "Object" ); + + SlotObject.SetInt("magCost", ChunkCost); SlotObject.SetInt("cost", FillCost); SlotObject.SetString("itemName", ArmorString); SlotObject.SetString("itemSource", "img://"$ArmorInfo.DefaultItem.WeaponDef.static.GetImagePath()); SlotObject.SetString("itemAmmo", ArmorInfo.SpareAmmoCount$"/"$ArmorInfo.MaxSpareAmmo); - SlotObject.Setint("buttonState", GetButtonState( ArmorInfo.AmmoPricePerMagazine, ArmorInfo.SpareAmmoCount, ArmorInfo.MaxSpareAmmo )); SlotObject.SetBool("lowAmmo", (ArmorInfo.MaxSpareAmmo > 0) ? (float(ArmorInfo.SpareAmmoCount) / float(ArmorInfo.MaxSpareAmmo)) <= LowAmmoPercentThreshold : false); + ButtonState = GetButtonState( ArmorInfo.AmmoPricePerMagazine, ArmorInfo.SpareAmmoCount, ArmorInfo.MaxSpareAmmo ); + SlotObject.Setint("buttonState", ButtonState ); + SlotObject.Setint("magButtonState", ButtonState ); + SetObject("armorInfo", SlotObject); AutoFillCost += FillCost; } diff --git a/KFGame/Classes/KFGameInfo.uc b/KFGame/Classes/KFGameInfo.uc index 17da05d..416610e 100644 --- a/KFGame/Classes/KFGameInfo.uc +++ b/KFGame/Classes/KFGameInfo.uc @@ -319,7 +319,8 @@ enum EForcedMusicType EFM_Boss2, EFM_Boss3, EFM_Boss4, - EFM_Boss5 + EFM_Boss5, + EFM_WWL }; /** Tracks that are not selected randomly but rather "forced" by the server (or standalone client) at specific times */ @@ -436,11 +437,6 @@ enum EMonsterProperties var int SpawnedMonsterProperties[EMonsterProperties]; -/************************************************************************************ -* @name Force to sort maps by name -***********************************************************************************/ -var bool bForceMapSorting; - /************************************************************************************ * @name Native ***********************************************************************************/ @@ -950,6 +946,10 @@ event PostLogin( PlayerController NewPlayer ) KFPC = KFPlayerController(NewPlayer); if( KFPC != None ) { + KFPC.UpdatePerkOnInit(); + + KFPC.InitGameplayPostProcessFX(); + if( KFPC.PlayerReplicationInfo.bOnlySpectator ) { // if we're initially spectating, initialize front-end but skip lobby menu @@ -2867,6 +2867,11 @@ simulated function ForceAbominationMusicTrack() MyKFGRI.ForceNewMusicTrack( default.ForcedMusicTracks[EFM_Boss5] ); } +simulated function ForceWWLMusicTrack() +{ + MyKFGRI.ForceNewMusicTrack( default.ForcedMusicTracks[EFM_WWL] ); +} + /********************************************************************************************* * @name Map rotation *********************************************************************************************/ @@ -2896,10 +2901,10 @@ function string GetNextMap() { MapCycleIndex = MapCycleIndex + 1 < GameMapCycles[ActiveMapCycle].Maps.length ? (MapCycleIndex + 1) : 0; - if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 11) + if ((class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 11 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[11]) + && MyKFGRI.IsA('KFGameReplicationInfo_WeeklySurvival')) { - if (MyKFGRI.IsA('KFGameReplicationInfo_WeeklySurvival') && - GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-Biolapse" || + if (GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-Biolapse" || GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-Nightmare" || GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-PowerCore_Holdout" || GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-TheDescent" || @@ -3871,6 +3876,7 @@ defaultproperties ForcedMusicTracks(4)=KFMusicTrackInfo'WW_MACT_Default.TI_Boss_Matriarch' // matriarch ForcedMusicTracks(5)=KFMusicTrackInfo'WW_MACT_Default.TI_RG_KingFP' // king fp ForcedMusicTracks(6)=KFMusicTrackInfo'WW_MACT_Default.TI_RG_Abomination' // abomination + ForcedMusicTracks(7)=KFMusicTrackInfo'WW_MACT_Default.TI_WWL' // WWL Weekly ReservationTimeout=32 bLogReservations=true @@ -3895,6 +3901,4 @@ defaultproperties DebugForcedOutbreakIdx=INDEX_NONE DebugForceSpecialWaveZedType=INDEX_NONE - - bForceMapSorting=True } diff --git a/KFGame/Classes/KFGameReplicationInfo.uc b/KFGame/Classes/KFGameReplicationInfo.uc index 264068b..f972c83 100644 --- a/KFGame/Classes/KFGameReplicationInfo.uc +++ b/KFGame/Classes/KFGameReplicationInfo.uc @@ -106,7 +106,7 @@ var int AIRemaining; var int WaveTotalAICount; var bool bEndlessMode; var bool bObjectiveMode; - +var bool bIsWeeklyMode; //@HSL_BEGIN - JRO - 3/21/2016 - PS4 Sessions /************************************ * Console Sessions @@ -381,7 +381,7 @@ replication if ( bNetDirty ) TraderVolume, TraderVolumeCheckType, bTraderIsOpen, NextTrader, WaveNum, bWaveIsEndless, AIRemaining, WaveTotalAICount, bWaveIsActive, MaxHumanCount, bGlobalDamage, CurrentObjective, PreviousObjective, PreviousObjectiveResult, PreviousObjectiveXPResult, PreviousObjectiveVoshResult, MusicIntensity, ReplicatedMusicTrackInfo, MusicTrackRepCount, - bIsUnrankedGame, GameSharedUnlocks, bHidePawnIcons, ConsoleGameSessionGuid, GameDifficulty, GameDifficultyModifier, BossIndex, bWaveStarted, NextObjective, bIsBrokenTrader; //@HSL - JRO - 3/21/2016 - PS4 Sessions + bIsUnrankedGame, GameSharedUnlocks, bHidePawnIcons, ConsoleGameSessionGuid, GameDifficulty, GameDifficultyModifier, BossIndex, bWaveStarted, NextObjective, bIsBrokenTrader, bIsWeeklyMode; //@HSL - JRO - 3/21/2016 - PS4 Sessions if ( bNetInitial ) GameLength, WaveMax, bCustom, bVersusGame, TraderItems, GameAmmoCostScale, bAllowGrenadePurchase, MaxPerkLevel, bTradersEnabled; if ( bNetInitial || bNetDirty ) @@ -1091,6 +1091,7 @@ simulated function int GetNextMapTimeRemaining() /** Called from the GameInfo when the trader pod should be activated */ function SetWaveActive(bool bWaveActive, optional byte NewMusicIntensity) { + // set up music intensity for this wave MusicIntensity = NewMusicIntensity; bTraderIsOpen = !bWaveActive && bMatchHasBegun && bTradersEnabled; @@ -1106,6 +1107,21 @@ function SetWaveActive(bool bWaveActive, optional byte NewMusicIntensity) } } +simulated function bool CanOverrideWeeklyMusic() +{ + local KFGameInfo KFGI; + + if (WorldInfo.NetMode == NM_Client) + { + return !bIsWeeklyMode || class'KFGameEngine'.static.GetWeeklyEventIndexMod() != 12; + } + else + { + KFGI = KFGameInfo(WorldInfo.Game); + return (KFGI == none || KFGI.OutbreakEvent == none || !KFGI.OutbreakEvent.ActiveEvent.bForceWWLMusic); + } +} + simulated function bool IsFinalWave() { return (WaveNum == WaveMax - 1); @@ -1717,6 +1733,9 @@ simulated function PlayNewMusicTrack( optional bool bGameStateChanged, optional return; } + if ( !CanOverrideWeeklyMusic() ) + return; + // @todo: consider using music intensity (255?) for ambient music to simplify this logic bPlayActionTrack = (!bForceAmbient && KFGameClass.static.ShouldPlayActionMusicTrack(self)); @@ -2202,4 +2221,5 @@ defaultproperties PreviousObjectiveVoshResult=-1 PreviousObjectiveXPResult=-1 bIsBrokenTrader=false + bIsWeeklyMode=false } diff --git a/KFGame/Classes/KFGfxMenu_StartGame.uc b/KFGame/Classes/KFGfxMenu_StartGame.uc index d4d09af..1a52db4 100644 --- a/KFGame/Classes/KFGfxMenu_StartGame.uc +++ b/KFGame/Classes/KFGfxMenu_StartGame.uc @@ -206,7 +206,7 @@ static function class GetSpecialEventClass case SEI_Summer: return class'KFGFxSpecialEventObjectivesContainer_Summer2021'; case SEI_Fall: - return class'KFGFxSpecialEventObjectivesContainer_Fall2020'; + return class'KFGFxSpecialEventObjectivesContainer_Fall2021'; case SEI_Winter: return class'KFGFXSpecialEventObjectivesContainer_Xmas2020'; } diff --git a/KFGame/Classes/KFGfxMenu_Trader.uc b/KFGame/Classes/KFGfxMenu_Trader.uc index 07581f8..a74354a 100644 --- a/KFGame/Classes/KFGfxMenu_Trader.uc +++ b/KFGame/Classes/KFGfxMenu_Trader.uc @@ -702,6 +702,15 @@ function Callback_FillGrenades() } } +function Callback_BuyArmor() +{ + if (PlayerInventoryContainer != none) + { + MyKFPC.GetPurchaseHelper().BuyArmorMag(); + RefreshItemComponents(); + } +} + function Callback_FillArmor() { if (PlayerInventoryContainer != none) diff --git a/KFGame/Classes/KFImpactEffectManager.uc b/KFGame/Classes/KFImpactEffectManager.uc index 53cd755..ed1304f 100644 --- a/KFGame/Classes/KFImpactEffectManager.uc +++ b/KFGame/Classes/KFImpactEffectManager.uc @@ -65,6 +65,7 @@ simulated function PlayImpactEffects(const vector HitLocation, const Pawn Effect local KFImpactEffectInfo ImpactEffectInfo; local KFFracturedMeshActor FracturedMeshActor; local int i; + local bool bIsWeaponHandlingEffects; // allow optional parameter to override impact effects ImpactEffectInfo = (CustomImpactEffects != None) ? CustomImpactEffects : DefaultImpactEffects; @@ -91,9 +92,11 @@ simulated function PlayImpactEffects(const vector HitLocation, const Pawn Effect } } + bIsWeaponHandlingEffects = KFPawn(EffectInstigator).MyKFWeapon.bForceHandleImpacts; + // Trace using the Instigator as the TraceOwner so that melee weapons don't collide with Instigator HitActor = EffectInstigator.Trace(NewHitLoc, HitNormal, (HitLocation - (HitNormal * 32)), HitLocation + (HitNormal * 32), !bWorldImpactsOnly,, HitInfo, TRACEFLAG_Bullet); - if( HitActor != none && HitActor.bCanBeDamaged && HitActor.IsA('Pawn') ) + if( HitActor != none && HitActor.bCanBeDamaged && HitActor.IsA('Pawn') && !bIsWeaponHandlingEffects ) { return; // pawns impacts are handled by the pawn (see PlayTakeHitEffects) } @@ -130,7 +133,7 @@ simulated function PlayImpactEffects(const vector HitLocation, const Pawn Effect // Pawns handle their own hit effects if ( HitActor != None && - (Pawn(HitActor) == None || Vehicle(HitActor) != None) && + ((Pawn(HitActor) == None || bIsWeaponHandlingEffects ) || Vehicle(HitActor) != None) && AllowImpactEffects(HitActor, HitLocation, HitNormal) ) { if (ImpactEffect.ParticleTemplate != None) diff --git a/KFGame/Classes/KFInventoryManager.uc b/KFGame/Classes/KFInventoryManager.uc index 2757303..fabc046 100644 --- a/KFGame/Classes/KFInventoryManager.uc +++ b/KFGame/Classes/KFInventoryManager.uc @@ -803,8 +803,25 @@ simulated function InternalNextWeapon(bool bGamepad) simulated function bool ShouldSkipCycleWeapon(Weapon CandidateWeapon, bool bGamepad) { local KFWeapon KFW; + local PlayerController PC; + local KFPlayerInput KFPI; + local bool bCanSwapTo9mm; + // Default behaviour is you can't swap to 9mm + bCanSwapTo9mm = false; + + PC = PlayerController(Instigator.Controller); + if ( PC != None ) + { + KFPI = KFPlayerInput(PC.PlayerInput); + if (KFPI != None) + { + bCanSwapTo9mm = KFPI.bAllowSwapTo9mm; + } + } + KFW = KFWeapon(CandidateWeapon); + if(KFW != none) { if(KFW.InventoryGroup == IG_None) @@ -819,7 +836,7 @@ simulated function bool ShouldSkipCycleWeapon(Weapon CandidateWeapon, bool bGame return true; } - if (KFW.bIsBackupWeapon) + if (KFW.bIsBackupWeapon && (!bCanSwapTo9mm || KFW.IsMeleeWeapon())) { return true; } diff --git a/KFGame/Classes/KFOnlineStatsReadDingo.uc b/KFGame/Classes/KFOnlineStatsReadDingo.uc index 714b608..8dc1e07 100644 --- a/KFGame/Classes/KFOnlineStatsReadDingo.uc +++ b/KFGame/Classes/KFOnlineStatsReadDingo.uc @@ -68,6 +68,7 @@ defaultproperties ColumnIds.Add(STATID_ACHIEVE_ElysiumEndlessWaveFifteen) ColumnIds.Add(STATID_ACHIEVE_Dystopia2029Collectibles) ColumnIds.Add(STATID_ACHIEVE_MoonbaseCollectibles) + ColumnIds.Add(STATID_ACHIEVE_NetherholdCollectibles) ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky5, Name="AchievementMrPerky5")) ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky10, Name = "AchievementMrPerky10")) @@ -124,4 +125,5 @@ defaultproperties ColumnMappings.Add((Id=STATID_ACHIEVE_ElysiumEndlessWaveFifteen,Name="AchievementEndlessElysium")) ColumnMappings.Add((Id=STATID_ACHIEVE_Dystopia2029Collectibles,NAme="AchievementCollectDystopia2029")) ColumnMappings.Add((Id=STATID_ACHIEVE_MoonbaseCollectibles,NAme="AchievementCollectMoonbase")) + ColumnMappings.Add((Id=STATID_ACHIEVE_NetherholdCollectibles,NAme="AchievementCollectNetherhold")) } diff --git a/KFGame/Classes/KFOnlineStatsWrite.uc b/KFGame/Classes/KFOnlineStatsWrite.uc index a4b77a5..8817006 100644 --- a/KFGame/Classes/KFOnlineStatsWrite.uc +++ b/KFGame/Classes/KFOnlineStatsWrite.uc @@ -433,6 +433,10 @@ const KFACHID_MoonbaseHard = 283; const KFACHID_MoonbaseHellOnEarth = 284; const KFACHID_MoonbaseCollectibles = 285; +const KFACHID_NetherholdHard = 286; +const KFACHID_NetherholdHellOnEarth = 287; +const KFACHID_NetherholdCollectibles = 288; + /* __TW_ANALYTICS_ */ var int PerRoundWeldXP; var int PerRoundHealXP; @@ -2055,6 +2059,7 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_GrenadeLauncher_HX25, KFDT_ExplosiveSubmunition_HX25,KFDT_Ballistic_HX25Impact,KFDT_Ballistic_HX25SubmunitionImpact,KFDT_Bludgeon_HX25),CompletionAmount=5000)) //3000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Thrown_C4, KFDT_Explosive_C4,KFDT_Bludgeon_C4),CompletionAmount=2500)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_GrenadeLauncher_M79, KFDT_Ballistic_M79Impact,KFDT_Explosive_M79,KFDT_Bludgeon_M79),CompletionAmount=7000)) + DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_Boomy, KFDT_Ballistic_HRG_Boomy,KFDT_Explosive_HRG_Boomy,KFDT_Bludgeon_HRG_Boomy),CompletionAmount=7000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Shotgun_HRG_Kaboomstick, KFDT_Ballistic_HRG_Kaboomstick,KFDT_Explosive_HRG_Kaboomstick,KFDT_Bludgeon_HRG_Kaboomstick),CompletionAmount=9000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_RocketLauncher_RPG7, KFDT_Ballistic_RPG7Impact,KFDT_Explosive_RPG7,KFDT_Explosive_RPG7BackBlast,KFDT_Bludgeon_RPG7),CompletionAmount=7500)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_AssaultRifle_M16M203, KFDT_Ballistic_M16M203,KFDT_Bludgeon_M16M203,KFDT_Ballistic_M203Impact,KFDT_Explosive_M16M203),CompletionAmount=9000)) //7000 @@ -2094,6 +2099,7 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Pistol_Deagle, KFDT_Bludgeon_Deagle,KFDT_Ballistic_Deagle),CompletionAmount=10000)) // 7000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Revolver_SW500, KFDT_Bludgeon_SW500,KFDT_Ballistic_SW500,KFDT_Ballistic_SW500_Dual),CompletionAmount=10000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Pistol_AF2011, KFDT_Bludgeon_AF2011,KFDT_Ballistic_AF2011),CompletionAmount=10000)) + DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_Energy, KFDT_Ballistic_HRG_Energy_Primary,KFDT_Ballistic_HRG_Energy_Secondary, KFDT_Bludgeon_HRG_Energy),CompletionAmount=10000)) //Sharpshooter Weapons DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Rifle_Winchester1894, KFDT_Bludgeon_Winchester,KFDT_Ballistic_Winchester),CompletionAmount=5000)) //2000 @@ -2247,6 +2253,9 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-MOONBASE),CompletionAmount=1)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-MOONBASE),CompletionAmount=2)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-MOONBASE),CompletionAmount=3)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-NETHERHOLD),CompletionAmount=1)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-NETHERHOLD),CompletionAmount=2)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-NETHERHOLD),CompletionAmount=3)) //Versus Damage // Per design doc that I have right now, these are x class damage y players, not damage y amount diff --git a/KFGame/Classes/KFOnlineStatsWriteDingo.uc b/KFGame/Classes/KFOnlineStatsWriteDingo.uc index 14afe34..30b0bea 100644 --- a/KFGame/Classes/KFOnlineStatsWriteDingo.uc +++ b/KFGame/Classes/KFOnlineStatsWriteDingo.uc @@ -70,4 +70,5 @@ defaultproperties Properties.Add((PropertyId = STATID_ACHIEVE_ElysiumEndlessWaveFifteen,Data = (Type = SDT_Int32,Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_Dystopia2029Collectibles, Data = (Type = SDT_Int32,Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_MoonbaseCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) + Properties.Add((PropertyId = STATID_ACHIEVE_NetherholdCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) } diff --git a/KFGame/Classes/KFOutbreakEvent.uc b/KFGame/Classes/KFOutbreakEvent.uc index c935e84..5c94b95 100644 --- a/KFGame/Classes/KFOutbreakEvent.uc +++ b/KFGame/Classes/KFOutbreakEvent.uc @@ -165,6 +165,10 @@ struct WeeklyOverrides * 1) Disables Berserker lvl25 skills 2) Enables lvl25 battery ram skill of the swat */ var() bool bColliseumSkillConditionsActive; + /** Activates the special conditions for the Wild West Weekly Mode + * 1) Disables Berserker lvl25 skills 2) Enables lvl25 battery ram skill of the swat + */ + var () bool bWildWestSkillConditionsActive; /** If this array is not empty, replaces AIClassList entries with a new spawn class */ var() array SpawnReplacementList; @@ -323,6 +327,9 @@ struct WeeklyOverrides /** Heal after kill */ var() bool bHealAfterKill; + /** Only heal with headshots */ + var() bool bHealWithHeadshot; + /** Cannot be Healed*/ var() bool bCannotBeHealed; @@ -371,6 +378,9 @@ struct WeeklyOverrides /** If another outbreak mode shares the same events, this will link the two to quicker UI lookup */ var() int WeeklyOutbreakId; + /** If WWL music should be forced */ + var() bool bForceWWLMusic; + structdefaultproperties { GameLength = GL_Short @@ -403,6 +413,7 @@ struct WeeklyOverrides MaxPerkLevel = 4 bAllowSpawnReplacementDuringBossWave = true bHealAfterKill = false + bHealWithHeadshot = false bCannotBeHealed = false bGlobalDamageAffectsShield = true bApplyGlobalDamageBossWave = true @@ -419,11 +430,13 @@ struct WeeklyOverrides bAddSpawnListToLoadout = false bSpawnWeaponListAffectsSecondaryWeapons = false bColliseumSkillConditionsActive = false + bWildWestSkillConditionsActive = false bModifyZedTimeOnANearZedKill = false ZedTimeOnANearZedKill = 0.05 DoshOnKillGlobalModifier = 1.0f JumpZ = -1.f DroppedItemLifespan=-1.0f + bForceWWLMusic = false; } }; diff --git a/KFGame/Classes/KFPawn_Human.uc b/KFGame/Classes/KFPawn_Human.uc index d255d66..5661b32 100644 --- a/KFGame/Classes/KFPawn_Human.uc +++ b/KFGame/Classes/KFPawn_Human.uc @@ -313,7 +313,7 @@ function PossessedBy(Controller C, bool bVehicleTransition) } KFGameInfo(WorldInfo.Game).OverrideHumanDefaults( self ); - + SetTimer(0.5f, false, nameof(ClientOverrideHumanDefaults), self); } simulated function NotifyTeamChanged() @@ -2114,6 +2114,36 @@ event Landed(vector HitNormal, actor FloorActor) } } +// Used for override aesthetics +client reliable function ClientOverrideHumanDefaults() +{ + local KFPlayerController_WeeklySurvival KFPC_WS; + local KFPlayerReplicationInfo KFPRI; + local KFCharacterInfo_Human KFCIH; + local int CowboyHatIndex; + + KFPC_WS = KFPlayerController_WeeklySurvival(Controller); + if (KFPC_WS == none) + { + return; + } + + if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12) + { + KFPRI = KFPlayerReplicationInfo(KFPC_WS.PlayerReplicationInfo); + if (KFPRI != none) + { + KFCIH = KFPRI.CharacterArchetypes[KFPRI.RepCustomizationInfo.CharacterIndex]; + CowboyHatIndex = class'KFGFxMenu_Gear'.static.FindCowboyHatAttachmentIndex(KFCIH); + if (CowboyHatIndex >= 0) + { + KFCIH.DetachConflictingAttachments(CowboyHatIndex, self, KFPRI); + KFPRI.SetWeeklyCharacterAttachment(CowboyHatIndex, 0); + } + } + } +} + defaultproperties { Begin Object Class=KFFlashlightAttachment name=Flashlight_0 diff --git a/KFGame/Classes/KFPawn_Monster.uc b/KFGame/Classes/KFPawn_Monster.uc index d03e54b..8bc0658 100644 --- a/KFGame/Classes/KFPawn_Monster.uc +++ b/KFGame/Classes/KFPawn_Monster.uc @@ -92,6 +92,10 @@ var() array WeakSpotSocketNames; var int HealByKill; var int HealByAssistance; +/** WWL Hat attach name*/ +var name ZEDCowboyHatAttachName; + + /** * Information on resistant or vulnerable damage types * @todo: This is all static data so we should consider moving to the archetype @@ -540,6 +544,14 @@ var byte PreviousArmorZoneStatus; //Hit FX overrides for hitting armor var const int OverrideArmorFXIndex; +/********************************************************************************************* + * @name Parasite Weapon +********************************************************************************************* */ +var array ParasiteSeeds; + +// Max num of seeds in this character +var byte MaxNumSeeds; + /********************************************************************************************* * @name Delegates ********************************************************************************************* */ @@ -2573,6 +2585,7 @@ function bool Died(Controller Killer, class DamageType, vector HitLo { local KFPlayerController KFPC; local KFPerk InstigatorPerk; + local int i; if ( super.Died(Killer, damageType, HitLocation) ) { @@ -2613,6 +2626,16 @@ function bool Died(Controller Killer, class DamageType, vector HitLo } } + if (ParasiteSeeds.Length > 0) + { + for (i = 0; i < ParasiteSeeds.Length; ++i) + { + ParasiteSeeds[i].Explode(Location - (vect(0,0,1) * GetCollisionHeight()), vect(0,0,1) >> ParasiteSeeds[i].Rotation); + } + + ParasiteSeeds.Remove(0, ParasiteSeeds.Length); + } + OnZedDied(Killer); return true; @@ -4732,6 +4755,30 @@ function ZedExplodeArmor(int ArmorZoneIdx, name ArmorZoneName) } } +/********************************************************************************************* +* @name Armor +********************************************************************************************* */ +server reliable function AddParasiteSeed(KFProjectile Proj) +{ + local int i; + + if (Role < ROLE_AUTHORITY) + return; + + if (ParasiteSeeds.Length >= MaxNumSeeds) + { + for (i = 0; i < ParasiteSeeds.Length - (MaxNumSeeds-1); ++i) + { + ParasiteSeeds[i].Detonate(); + } + + ParasiteSeeds.Remove(0, ParasiteSeeds.Length - (MaxNumSeeds-1)); + } + + ParasiteSeeds.AddItem(Proj); +} + + /********************************************************************************************* * @name Achievements ********************************************************************************************* */ @@ -4927,4 +4974,7 @@ DefaultProperties bSprintOverride=false VortexAttracionModifier=1.0f + MaxNumSeeds=1 + + ZEDCowboyHatAttachName=HEAD_Attach } diff --git a/KFGame/Classes/KFPerk_Berserker.uc b/KFGame/Classes/KFPerk_Berserker.uc index 12ce9af..f236255 100644 --- a/KFGame/Classes/KFPerk_Berserker.uc +++ b/KFGame/Classes/KFPerk_Berserker.uc @@ -159,7 +159,7 @@ simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCau } `QALog( GetFuncName() @ "Base damage:" @ InDamage , bLogPerk); - if( (MyKFWeapon != none && IsWeaponOnPerk( MyKFWeapon,, self.class )) || IsDamageTypeOnPerk( DamageType ) ) + if( ((MyKFWeapon != none && IsWeaponOnPerk( MyKFWeapon,, self.class )) || IsDamageTypeOnPerk( DamageType )) && !IsBlastBrawlers(MyKFWeapon) ) { TempDamage += InDamage * GetPassiveValue( BerserkerDamage, CurrentLevel ); if( IsSpeedActive() ) diff --git a/KFGame/Classes/KFPerk_Commando.uc b/KFGame/Classes/KFPerk_Commando.uc index d4e0dbe..f14633f 100644 --- a/KFGame/Classes/KFPerk_Commando.uc +++ b/KFGame/Classes/KFPerk_Commando.uc @@ -303,6 +303,8 @@ simulated function float GetZedTimeModifier( KFWeapon W ) local name StateName; StateName = W.GetStateName(); + `Log("STATE NAME: " $StateName); + if( IsProfessionalActive() && (IsWeaponOnPerk( W,, self.class ) || IsBackupWeapon( W ) || IsDual9mm( W )) ) { if( StateName == 'Reloading' || @@ -315,8 +317,9 @@ simulated function float GetZedTimeModifier( KFWeapon W ) return 0.3f; } } - - if( CouldRapidFireActive() && (Is9mm(W) || IsDual9mm( W ) || IsWeaponOnPerk( W,, self.class )) && ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE ) + // FAMAS uses alt fire as common firing. Needs a special case + if( CouldRapidFireActive() && (Is9mm(W) || IsDual9mm( W ) || IsWeaponOnPerk( W,, self.class )) && + (ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || (IsFAMAS(W) && StateName == 'FiringSecondaryState')) ) { return RapidFireFiringRate; } diff --git a/KFGame/Classes/KFPerk_Gunslinger.uc b/KFGame/Classes/KFPerk_Gunslinger.uc index 672d239..6724349 100644 --- a/KFGame/Classes/KFPerk_Gunslinger.uc +++ b/KFGame/Classes/KFPerk_Gunslinger.uc @@ -348,7 +348,7 @@ simulated function float GetZedTimeModifier( KFWeapon W ) */ simulated function bool GetIsUberAmmoActive( KFWeapon KFW ) { - return IsWeaponOnPerk( KFW,, self.class ) && IsUberAmmoActive() && WorldInfo.TimeDilation < 1.f; + return IsWeaponOnPerk( KFW,, self.class ) && IsUberAmmoActive() && WorldInfo.TimeDilation < 1.f && !ShouldDisableZedTimeSkillsForWildWest(); } /** @@ -664,7 +664,7 @@ simulated function bool IsFanfareActive() */ simulated function bool GetFanfareActive() { - return IsFanfareActive(); + return IsFanfareActive() || IsFanfareActiveForWildWest(); } /** @@ -798,6 +798,28 @@ simulated function LogPerkSkills() } } +/********************************************************************************************* +* @name Special Weekly Modes +********************************************************************************************* */ + +simulated function bool ShouldDisableZedTimeSkillsForWildWest() +{ + if (WorldInfo.NetMode == NM_Client) + { + return MyKFGRI.bIsWeeklyMode && class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12; + } + else + { + return MyKFGI != none && MyKFGI.OutbreakEvent != none && MyKFGI.OutbreakEvent.ActiveEvent.bWildWestSkillConditionsActive; + } + +} + +function bool IsFanfareActiveForWildWest() +{ + return MyKFGI != none && MyKFGI.OutbreakEvent != none && MyKFGI.OutbreakEvent.ActiveEvent.bWildWestSkillConditionsActive; +} + DefaultProperties { PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Gunslinger' diff --git a/KFGame/Classes/KFPerk_Sharpshooter.uc b/KFGame/Classes/KFPerk_Sharpshooter.uc index f705298..e03b15f 100644 --- a/KFGame/Classes/KFPerk_Sharpshooter.uc +++ b/KFGame/Classes/KFPerk_Sharpshooter.uc @@ -52,6 +52,8 @@ var float SkillZedTimeChance; var private transient bool bWasHeadshot; +var private float FanFareModifier; + /********************************************************************************************* * @name Stats/XP ********************************************************************************************* */ @@ -446,7 +448,7 @@ simulated function bool IsAmmoPouchActive() */ simulated function bool IsZTKnockdownActive() { - return PerkSkills[ESharpshooterZTKnockdown].bActive && IsPerkLevelAllowed(ESharpshooterZTKnockdown); + return PerkSkills[ESharpshooterZTKnockdown].bActive && IsPerkLevelAllowed(ESharpshooterZTKnockdown) && !ShouldDisableZedTimeSkillsForWildWest(); } /** @@ -466,7 +468,7 @@ simulated function bool GetZTKnockdownActive() */ simulated function bool IsZTStunActive() { - return PerkSkills[ESharpshooterZTStun].bActive && IsPerkLevelAllowed(ESharpshooterZTStun); + return PerkSkills[ESharpshooterZTStun].bActive && IsPerkLevelAllowed(ESharpshooterZTStun) && !ShouldDisableZedTimeSkillsForWildWest(); } /** @@ -658,6 +660,62 @@ static simulated function bool IsWeaponOnPerk( KFWeapon W, optional array < clas return super.IsWeaponOnPerk( W, WeaponPerkClass, InstigatorPerkClass, WeaponClassName ); } +/********************************************************************************************* +* @name Special Weekly Modes +********************************************************************************************* */ + +/** + * @brief Skills can modify the zed time time delation (Forced during wild west weekly) + * + * @param W used weapon + * @return time dilation modifier + */ +simulated function float GetZedTimeModifier( KFWeapon W ) +{ + local name StateName; + + if( IsFanfareActiveForWildWest() && IsWeaponOnPerk( W,, self.class ) ) + { + StateName = W.GetStateName(); + if( ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE ) + { + return FanFareModifier; + } + + if( StateName == 'Reloading' ) + { + return 1.f; + } + } + + return 0.f; +} + + +function bool ShouldDisableZedTimeSkillsForWildWest() +{ + if (WorldInfo.NetMode == NM_Client) + { + return MyKFGRI.bIsWeeklyMode && class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12; + } + else + { + return MyKFGI != none && MyKFGI.OutbreakEvent != none && MyKFGI.OutbreakEvent.ActiveEvent.bWildWestSkillConditionsActive; + } +} + +simulated function bool IsFanfareActiveForWildWest() +{ + if (WorldInfo.NetMode == NM_Client) + { + return MyKFGRI.bIsWeeklyMode && class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12; + } + else + { + return MyKFGI != none && MyKFGI.OutbreakEvent != none && MyKFGI.OutbreakEvent.ActiveEvent.bWildWestSkillConditionsActive; + } +} + DefaultProperties { PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Sharpshooter' @@ -732,4 +790,5 @@ DefaultProperties AssistDoshModifier=1.1f bWasHeadshot = false; + FanFareModifier = 1.0f; } diff --git a/KFGame/Classes/KFPerk_Support.uc b/KFGame/Classes/KFPerk_Support.uc index 4e18291..1918771 100644 --- a/KFGame/Classes/KFPerk_Support.uc +++ b/KFGame/Classes/KFPerk_Support.uc @@ -561,8 +561,11 @@ simulated function float GetZedTimeModifier( KFWeapon W ) StateName = W.GetStateName(); // Blast Brawlers use a different state for shooting (combining melee + firing). Needs a special case for this + // FAMAS uses alt fire as common firing. Another special case added if( IsWeaponOnPerk( W,, self.class ) && CouldBarrageActive() && - (ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || (StateName == 'MeleeChainAttacking' && IsBlastBrawlers(W)))) + (ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || + (StateName == 'MeleeChainAttacking' && IsBlastBrawlers(W)) || + (IsFAMAS(W) && StateName == 'FiringSecondaryState'))) { return BarrageFiringRate; } diff --git a/KFGame/Classes/KFPerk_Survivalist.uc b/KFGame/Classes/KFPerk_Survivalist.uc index 27adb60..969ebef 100644 --- a/KFGame/Classes/KFPerk_Survivalist.uc +++ b/KFGame/Classes/KFPerk_Survivalist.uc @@ -468,7 +468,10 @@ simulated function float GetZedTimeModifier( KFWeapon W ) `Warn(StateName); // Blast Brawlers use a different state for shooting (combining melee + firing). Needs a special case for this - if( ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || (StateName == 'MeleeChainAttacking' && IsBlastBrawlers(W)) ) + // FAMAS uses alt fire as common firing. Another special case added + if( ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || + (StateName == 'MeleeChainAttacking' && IsBlastBrawlers(W)) || + (StateName == 'FiringSecondaryState' && IsFAMAS(W))) { return GetSkillValue( PerkSkills[ESurvivalist_MadMan] ); } diff --git a/KFGame/Classes/KFPerk_Swat.uc b/KFGame/Classes/KFPerk_Swat.uc index 3a7030e..cd1456d 100644 --- a/KFGame/Classes/KFPerk_Swat.uc +++ b/KFGame/Classes/KFPerk_Swat.uc @@ -23,6 +23,7 @@ var private const float RapidAssaultFiringRate; // Faster firing rate var private const float SnarePower; var private const float TacticalMovementBobDamp; var private const class BackupSecondaryWeaponDef; +var private const float TacticalMovementModifier; // QoL: Tactical movement - Added modifier to move and sprint speed. /** Percentage of how much armor should be damaged when the heavy armor skill is active */ var private const float HeavyArmorAbsorptionPct; @@ -277,7 +278,7 @@ simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCau if( KFW != none ) { - if( IsBackupActive() && (IsBackupWeapon( KFW ) || IsDual9mm( KFW )) ) + if( IsBackupActive() && (IsBackupWeapon( KFW ) || IsDual9mm( KFW ) || ClassIsChildOf(DamageType, class'KFDT_Bludgeon')) ) { `QALog( "Backup Damage" @ KFW @ GetPercentage( InDamage, InDamage * GetSkillValue(PerkSkills[ESWAT_Backup])), bLogPerk ); TempDamage += InDamage * GetSkillValue( PerkSkills[ESWAT_Backup] ); @@ -467,6 +468,68 @@ simulated function int GetArmorDamageAmount( int AbsorbedAmt ) return AbsorbedAmt; } +/** + * @brief Weapons and perk skills can affect the jog/sprint speed + * + * @param Speed jog speed + */ +simulated function ModifySpeed( out float Speed ) +{ + local KFWeapon MyKFWeapon; + local KFInventoryManager KFIM; + + if( !IsTacticalMovementActive() ) + { + return; + } + + MyKFWeapon = GetOwnerWeapon(); + if( MyKFWeapon == none && CheckOwnerPawn() ) + { + KFIM = KFInventoryManager(OwnerPawn.InvManager); + if( KFIM != none && KFIM.PendingWeapon != none ) + { + MyKFWeapon = KFWeapon(KFIM.PendingWeapon); + } + } + + if (MyKFWeapon != none && (Is9mm(MyKFWeapon) || IsDual9mm( MyKFWeapon ) || IsWeaponOnPerk(MyKFWeapon,, self.class))) + { + Speed += Speed * TacticalMovementModifier; + } +} + +/** + * @brief Weapons and perk skills can affect the jog/sprint speed + * + * @param Speed sprint speed + */ +simulated function ModifySprintSpeed( out float Speed ) +{ + local KFWeapon MyKFWeapon; + local KFInventoryManager KFIM; + + if( !IsTacticalMovementActive() ) + { + return; + } + + MyKFWeapon = GetOwnerWeapon(); + if( MyKFWeapon == none && CheckOwnerPawn() ) + { + KFIM = KFInventoryManager(OwnerPawn.InvManager); + if( KFIM != none && KFIM.PendingWeapon != none ) + { + MyKFWeapon = KFWeapon(KFIM.PendingWeapon); + } + } + + if (MyKFWeapon != none && (Is9mm(MyKFWeapon) || IsDual9mm( MyKFWeapon ) || IsWeaponOnPerk(MyKFWeapon,, self.class))) + { + Speed += Speed * TacticalMovementModifier; + } +} + /********************************************************************************************* * @name Getters etc ********************************************************************************************* */ @@ -649,7 +712,7 @@ DefaultProperties PerkSkills(ESWAT_HeavyArmor)=(Name="HeavyArmor",IconPath="UI_PerkTalent_TEX.SWAT.UI_Talents_SWAT_HeavyArmor", Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f) //0.1 PerkSkills(ESWAT_TacticalMovement)=(Name="TacticalMovement",IconPath="UI_PerkTalent_TEX.SWAT.UI_Talents_SWAT_TacticalMovement", Increment=0.f,Rank=0,StartingValue=2.5f,MaxValue=2.5f) - PerkSkills(ESWAT_Backup)=(Name="Backup",IconPath="UI_PerkTalent_TEX.SWAT.UI_Talents_SWAT_Backup", Increment=0.f,Rank=0,StartingValue=0.85f,MaxValue=0.85f) //1.1 + PerkSkills(ESWAT_Backup)=(Name="Backup",IconPath="UI_PerkTalent_TEX.SWAT.UI_Talents_SWAT_Backup", Increment=0.f,Rank=0,StartingValue=1.0f,MaxValue=1.0f) //0.85f PerkSkills(ESWAT_TacticalReload)=(Name="TacticalReload",IconPath="UI_PerkTalent_TEX.SWAT.UI_Talents_SWAT_TacticalReload", Increment=0.f,Rank=0,StartingValue=2.0,MaxValue=2.0) PerkSkills(ESWAT_SpecialAmmunition)=(Name="SpecialAmmunition",IconPath="UI_PerkTalent_TEX.SWAT.UI_Talents_SWAT_SpecialAmmunition", Increment=0.f,Rank=0,StartingValue=2.0f,MaxValue=2.0f) PerkSkills(ESWAT_AmmoVest)=(Name="AmmoVest",IconPath="UI_PerkTalent_TEX.SWAT.UI_Talents_SWAT_AmmoVest", Increment=0.f,Rank=0,StartingValue=0.3f,MaxValue=0.3f) @@ -675,4 +738,6 @@ DefaultProperties SWATEnforcerZedTimeSpeedScale=1.25f BumpCooldown = 0.1f + + TacticalMovementModifier = 0.2; } \ No newline at end of file diff --git a/KFGame/Classes/KFPlayerController.uc b/KFGame/Classes/KFPlayerController.uc index 6b6237c..c630097 100644 --- a/KFGame/Classes/KFPlayerController.uc +++ b/KFGame/Classes/KFPlayerController.uc @@ -287,6 +287,8 @@ var name EffectPainParamName; var name EffectLowHealthParamName; /** Name of the MIC parameter used to display zed time */ var name EffectZedTimeParamName; +/** Name of the MIC parameter used to display zed time sepia*/ +var name EffectZedTimeSepiaParamName; /** Name of the MIC parameter used to display night vision time */ var name EffectNightVisionParamName; /** Name of the MIC parameter used to display Siren's scream attack effect */ @@ -1546,7 +1548,6 @@ function OnReadProfileSettingsComplete(byte LocalUserNum,bool bWasSuccessful) local KFProfileSettings Profile; local KFPlayerInput KFInput; local KFGameInfo KFGI; - local KFGameReplicationInfo KFGRI; local KFGameEngine KFEngine; local KFPlayerReplicationInfo KFPRI; local string MatchmakingRegion; @@ -1590,7 +1591,7 @@ function OnReadProfileSettingsComplete(byte LocalUserNum,bool bWasSuccessful) KFInput.GamepadAccelerationJumpScale = Profile.GetProfileFloat(KFID_GamepadAccelerationJumpScale); KFInput.SetGamepadLayout(Profile.GetProfileInt(KFID_CurrentLayoutIndex)); KFInput.bToggleToRun = Profile.GetProfileBool(KFID_ToggleToRun); - + KFInput.bAllowSwapTo9mm = Profile.GetProfileBool(KFID_AllowSwapTo9mm); KFInput.ReInitializeControlsUI(); } @@ -1686,37 +1687,21 @@ function OnReadProfileSettingsComplete(byte LocalUserNum,bool bWasSuccessful) OnlineSub.GetLobbyInterface().LobbyInvite(LobbyId, Zero, true); } - // If the perk is not allowed for this game mode, search for one that is available starting from the index 0 - KFGRI = KFGameReplicationInfo(WorldInfo.GRI); - - if( KFGRI != none && !KFGRI.IsPerkAllowed(PerkList[SavedPerkIndex].PerkClass) ) - { - SavedPerkIndex = 0; - for(SavedPerkIndex=0 ; SavedPerkIndex 0.f || /* ZED time effect is active */ CurrentZEDTimeEffectIntensity > 0.f || + /* sepia effect */ + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12) || /* Night vision active */ bNightVisionActive || SirenScreamEffectTimeRemaining > 0.f || @@ -11726,6 +11723,7 @@ defaultproperties PainEffectDuration=0.5f EffectLowHealthParamName=Effect_LowHealth EffectZedTimeParamName=Effect_ZEDTIME + EffectZedTimeSepiaParamName=Effect_ZEDSEPIA EffectNightVisionParamName=Effect_NightVision EffectSirenScreamParamName=Effect_Siren SonicScreamEffectDuration=6.f diff --git a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc index 4ca6b41..e19d8a8 100644 --- a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc +++ b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc @@ -195,6 +195,17 @@ reliable client function GoompaStompMessage( byte StompNum) } } +/** Resets all gameplay FX to initial state. + Append to this list if additional effects are added. */ +function ResetGameplayPostProcessFX() +{ + super.ResetGameplayPostProcessFX(); + + if( GameplayPostProcessEffectMIC != none && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + { + GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectZedTimeSepiaParamName, 1.f); + } +} // defaultProperties diff --git a/KFGame/Classes/KFPlayerInput.uc b/KFGame/Classes/KFPlayerInput.uc index cdf7887..36b37ed 100644 --- a/KFGame/Classes/KFPlayerInput.uc +++ b/KFGame/Classes/KFPlayerInput.uc @@ -227,6 +227,11 @@ var const float DoubleTapDelay; ********************************************************************************************* */ var bool bToggleToRun; +/********************************************************************************************* + * @name Run/Sprint +********************************************************************************************* */ +var bool bAllowSwapTo9mm; + /********************************************************************************************* * @name Game class ********************************************************************************************* */ diff --git a/KFGame/Classes/KFPlayerReplicationInfo.uc b/KFGame/Classes/KFPlayerReplicationInfo.uc index 42d81f6..04aeb16 100644 --- a/KFGame/Classes/KFPlayerReplicationInfo.uc +++ b/KFGame/Classes/KFPlayerReplicationInfo.uc @@ -198,6 +198,7 @@ cpptext native function bool StartLoadCosmeticContent(KFCharacterInfo_Human CharArch, INT CosmeticType, INT CosmeticIdx); native function StartLoadHeadshotFxContent(); +native function SetWeeklyCharacterAttachment(INT AttachmentIndex, int AttachmentSkin); replication { diff --git a/KFGame/Classes/KFProfileSettings.uc b/KFGame/Classes/KFProfileSettings.uc index 8750056..e845920 100644 --- a/KFGame/Classes/KFProfileSettings.uc +++ b/KFGame/Classes/KFProfileSettings.uc @@ -254,7 +254,6 @@ defaultproperties ProfileMappings.Add((Id=KFID_GamepadDeadzoneScale, Name="Gamepad Deadzone", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_GamepadAccelerationJumpScale, Name="Gamepad Acceleration Jump", MappingType=PVMT_RawValue)) - //Added 7/11/2016 ProfileMappings.Add((Id=KFID_UseAltAimOnDuals, Name="Use alt Dual Aim", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_HideBossHealthBar, Name="Hide Boss Health Bar", MappingType=PVMT_RawValue)) @@ -377,4 +376,8 @@ defaultproperties //Saber Added 10/15/2020 - Has Enter to Store Tab during sales ProfileMappings.Add((Id = KFID_HasTabbedToStore, Name = "Has Tabbed To Store", MappingType = PVMT_RawValue)) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_HasTabbedToStore,Data=(Type=SDT_Int32,Value1=0)))) + + // Added 16/07/2021 - QoL: Quick Swap button allowing 9mm as an option. + ProfileMappings.Add((Id=KFID_AllowSwapTo9mm, Name="AllowSwitchTo9mm", MappingType=PVMT_RawValue)) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_AllowSwapTo9mm,Data=(Type=SDT_Int32,Value1=0)))) } diff --git a/KFGame/Classes/KFProj_RicochetStickBullet.uc b/KFGame/Classes/KFProj_RicochetStickBullet.uc index 74446dd..fdfb9fa 100644 --- a/KFGame/Classes/KFProj_RicochetStickBullet.uc +++ b/KFGame/Classes/KFProj_RicochetStickBullet.uc @@ -90,7 +90,8 @@ event PreBeginPlay() WeaponClass = Instigator.Weapon.Class; // These better be the same or someone messed up - if ( WeaponClass.Name != WeaponClassName ) + if ( WeaponClass.Name != WeaponClassName && + (class(WeaponClass) == none || class(WeaponClass).default.SingleClass.Name != WeaponClassName)) { `warn("Projectile pickup mismatch class:"$WeaponClass@"name:"$WeaponClassName); } @@ -222,11 +223,17 @@ state Pickup function GiveTo( Pawn P ) { local KFWeapon W; + local class KFWeaponClass; + local class DualWeaponClass; + + KFWeaponClass = class(WeaponClass); + DualWeaponClass = class(WeaponClass); foreach P.InvManager.InventoryActors( class'KFWeapon', W ) { // Give the player 1 shot back - if( W.Class == WeaponClass ) + if( W.Class == WeaponClass || (DualWeaponClass != none ? DualWeaponClass.default.SingleClass == W.Class + : KFWeaponClass.default.DualClass == W.Class)) { W.AddAmmo(1); diff --git a/KFGame/Classes/KFSM_Frozen.uc b/KFGame/Classes/KFSM_Frozen.uc index f8e939e..0052419 100644 --- a/KFGame/Classes/KFSM_Frozen.uc +++ b/KFGame/Classes/KFSM_Frozen.uc @@ -181,7 +181,7 @@ function SetFrozenParameter(float FreezeAmount) { local MaterialInstanceConstant MIC; local int i; - + local bool bIsWWLMode; if ( PawnOwner.WorldInfo.NetMode != NM_DedicatedServer ) { FreezeMatParamValue = FreezeAmount; @@ -201,9 +201,11 @@ function SetFrozenParameter(float FreezeAmount) if (KFPawn_Monster(KFPOwner) != none) { + bIsWWLMode = class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12 && KFGameReplicationInfo(PawnOwner.WorldInfo.GRI) != none && KFGameReplicationInfo(PawnOwner.WorldInfo.GRI).bIsWeeklyMode; + for (i = 0; i < KFPawn_Monster(KFPOwner).StaticAttachList.length; i++) { - if (KFPawn_Monster(KFPOwner).StaticAttachList[i] != none) + if (KFPawn_Monster(KFPOwner).StaticAttachList[i] != none && (!bIsWWLMode || KFPawn_Monster(KFPOwner).StaticAttachList[i].StaticMesh.Name != 'CHR_CowboyHat_Alberts_Cosmetic' )) { ApplyFreeze(KFPawn_Monster(KFPOwner).StaticAttachList[i]); } diff --git a/KFGame/Classes/KFSkinTypeEffects.uc b/KFGame/Classes/KFSkinTypeEffects.uc index 6e1d4aa..8bbbf8c 100644 --- a/KFGame/Classes/KFSkinTypeEffects.uc +++ b/KFGame/Classes/KFSkinTypeEffects.uc @@ -38,9 +38,11 @@ enum EEffectDamageGroup FXG_Bludgeon_Chains, FXG_MicrowaveProj, FXG_Electricity, - FXG_Slashing_Ion + FXG_Slashing_Ion, + FXG_Energy_Yellow, + FXG_Energy_Magenta }; -const FXG_MAX = 20; //!! Update me when the enum gets modified !! +const FXG_MAX = 22; //!! Update me when the enum gets modified !! struct native SkinEffectInfo { diff --git a/KFGame/Classes/KFUnlockManager.uc b/KFGame/Classes/KFUnlockManager.uc index 7b06a8e..fd68b9d 100644 --- a/KFGame/Classes/KFUnlockManager.uc +++ b/KFGame/Classes/KFUnlockManager.uc @@ -246,6 +246,11 @@ static private event bool CheckCustomizationOwnership(KFPlayerReplicationInfo PR // accessory for( i=0; i < `MAX_COSMETIC_ATTACHMENTS; i++ ) { + if (i == 2 && PRI.WorldInfo.GRI.IsA('KFGameReplicationInfo_WeeklySurvival') && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + { + continue; + } + if (PRI.RepCustomizationInfo.AttachmentSkinIndices[i] == INDEX_NONE) { continue; diff --git a/KFGame/Classes/KFWeapDef_BladedPistol.uc b/KFGame/Classes/KFWeapDef_BladedPistol.uc new file mode 100644 index 0000000..ecddd40 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_BladedPistol.uc @@ -0,0 +1,25 @@ +//============================================================================= +// KFWeapDef_BladedPistol +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_BladedPistol extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_Pistol_Bladed" + + BuyPrice=600 + AmmoPricePerMag=32 + ImagePath="WEP_UI_BladedPistol_TEX.UI_WeaponSelect_BladedPistol" + + EffectiveRange=25 + + UpgradePrice[0]=700 + UpgradePrice[1]=1500 + UpgradeSellPrice[0]=550 + UpgradeSellPrice[1]=1650 +} diff --git a/KFGame/Classes/KFWeapDef_DualBladed.uc b/KFGame/Classes/KFWeapDef_DualBladed.uc new file mode 100644 index 0000000..1546e6e --- /dev/null +++ b/KFGame/Classes/KFWeapDef_DualBladed.uc @@ -0,0 +1,26 @@ +//============================================================================= +// KFWeapDef_DualBladed +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2015 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_DualBladed extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_Pistol_DualBladed" + + BuyPrice=1200 + AmmoPricePerMag=64 + ImagePath="WEP_UI_Dual_BladedPistol_TEX.UI_WeaponSelect_Dual_BladedPistol" + + EffectiveRange=50 + + UpgradePrice[0]=700 + UpgradePrice[1]=1500 + + UpgradeSellPrice[0]=525 + UpgradeSellPrice[1]=1650 +} diff --git a/KFGame/Classes/KFWeapDef_HRG_Boomy.uc b/KFGame/Classes/KFWeapDef_HRG_Boomy.uc new file mode 100644 index 0000000..5e1402a --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_Boomy.uc @@ -0,0 +1,30 @@ +//============================================================================= +// KFWeapDef_HRG_Boomy +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_Boomy extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_Boomy" + + BuyPrice=650 + AmmoPricePerMag=30 //50 + ImagePath="WEP_UI_HRG_Boomy_TEX.UI_WeaponSelect_HRG_Boomy" + + EffectiveRange=50 + + UpgradePrice[0]=600 + UpgradePrice[1]=700 + UpgradePrice[2]=1500 + + UpgradeSellPrice[0]=450 + UpgradeSellPrice[1]=975 + UpgradeSellPrice[2]=2100 +} + diff --git a/KFGame/Classes/KFWeapDef_HRG_Energy.uc b/KFGame/Classes/KFWeapDef_HRG_Energy.uc new file mode 100644 index 0000000..62581aa --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_Energy.uc @@ -0,0 +1,25 @@ +//============================================================================= +// KFWeapDef_HRG_Energy +//============================================================================= +// A lightweight container for basic weapon properties that can be safely +// accessed without a weapon actor (UI, remote clients). +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_Energy extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_Energy" + + BuyPrice=1500 //1100 + AmmoPricePerMag=65 + ImagePath="WEP_UI_HRG_Energy_TEX.UI_WeaponSelect_HRG_Energy" + + EffectiveRange=50 + + UpgradePrice[0]=1500 + UpgradeSellPrice[0]=1125 +} diff --git a/KFGame/Classes/KFWeapDef_MedicBat.uc b/KFGame/Classes/KFWeapDef_MedicBat.uc index f116fbe..d24703c 100644 --- a/KFGame/Classes/KFWeapDef_MedicBat.uc +++ b/KFGame/Classes/KFWeapDef_MedicBat.uc @@ -15,7 +15,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_Blunt_MedicBat" BuyPrice=1200 - AmmoPricePerMag=85 + AmmoPricePerMag=75 //85 ImagePath="WEP_UI_Medic_Bat_TEX.UI_WeaponSelect_MedicBat" EffectiveRange=3 diff --git a/KFGame/Classes/KFWeapDef_ParasiteImplanter.uc b/KFGame/Classes/KFWeapDef_ParasiteImplanter.uc new file mode 100644 index 0000000..5bac8a5 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_ParasiteImplanter.uc @@ -0,0 +1,25 @@ +//============================================================================= +// KFWeapDef_ParasiteImplanter +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_ParasiteImplanter extends KFWeaponDefinition + abstract; + +defaultproperties +{ + WeaponClassPath="KFGameContent.KFWeap_Rifle_ParasiteImplanter" + ImagePath="wep_ui_parasiteimplanter_tex.UI_WeaponSelect_ParasiteImplanter" + + BuyPrice=1500 + AmmoPricePerMag=42 + EffectiveRange=90 + + UpgradePrice[0]=1500 + UpgradeSellPrice[0]=1125 + + // SharedUnlockId=SCU_ParasiteImplanter +} diff --git a/KFGame/Classes/KFWeapon.uc b/KFGame/Classes/KFWeapon.uc index 18c8612..d549903 100644 --- a/KFGame/Classes/KFWeapon.uc +++ b/KFGame/Classes/KFWeapon.uc @@ -545,6 +545,9 @@ var transient bool bCheckBoltLockPostReload; /** array of bones to lock when out of ammo */ var array BonesToLockOnEmpty; +/** Handle impact effects on impact manager */ +var bool bForceHandleImpacts; + /********************************************************************************************* * @name Sounds ********************************************************************************************* */ @@ -8000,6 +8003,7 @@ defaultproperties bKeepIronSightsOnJump=false - bUsesSecondaryAmmoAltHUD = false + bUsesSecondaryAmmoAltHUD=false + bForceHandleImpacts=false } diff --git a/KFGame/Classes/KFWeaponAttachment.uc b/KFGame/Classes/KFWeaponAttachment.uc index 081293b..0487e3c 100644 --- a/KFGame/Classes/KFWeaponAttachment.uc +++ b/KFGame/Classes/KFWeaponAttachment.uc @@ -105,6 +105,8 @@ enum EWeaponState WEP_ReloadSingleEmpty_Elite, WEP_ReloadSecondary, WEP_ReloadSecondary_Elite, + WEP_ReloadSecondaryEmpty, + WEP_ReloadSecondaryEmpty_Elite, WEP_ReloadDualsOneEmpty, WEP_ReloadDualsOneEmpty_Elite, WEP_MeleeBasic, @@ -799,6 +801,8 @@ simulated function UpdateThirdPersonWeaponAction(EWeaponState NewWeaponState, KF case WEP_ReloadSecondary_Elite: case WEP_ReloadDualsOneEmpty: case WEP_ReloadDualsOneEmpty_Elite: + case WEP_ReloadSecondaryEmpty: + case WEP_ReloadSecondaryEmpty_Elite: bIsReloading = true; PlayReloadMagazineAnim(NewWeaponState, P); break; diff --git a/KFGame/Classes/KFWeaponSkinList.uc b/KFGame/Classes/KFWeaponSkinList.uc index e1e102a..f1030f6 100644 --- a/KFGame/Classes/KFWeaponSkinList.uc +++ b/KFGame/Classes/KFWeaponSkinList.uc @@ -3559,66 +3559,165 @@ defaultproperties //Thermite Thunder Jaws Skins.Add((Id=8945, Weapondef=class'KFWeapDef_ThermiteBore', MIC_1P=("WEP_SkinSet46_MAT.Wep_1stP_Thermite_ThunderJaws_MIC"), MIC_3P="WEP_SkinSet46_MAT.Wep_3rdP_Thermite_ThunderJaws_MIC", MIC_Pickup="WEP_SkinSet46_MAT.Wep_3rdP_Thermite_Pickup_ThunderJaws_MIC")); -//BeyondHorizon AA12 - Skins.Add((Id=8845, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet43_MAT.space_aa12.Space_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_aa12.Space_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_aa12.Space_AA12_3P_Pickup_MIC")) +//Corrupter Carbine Standard + Skins.Add((Id=9132, Weapondef=class'KFWeapDef_ParasiteImplanter', MIC_1P=("WEP_1P_ParasiteImplanter_MAT.Wep_1stP_ParasiteImplanter_PM", "WEP_1P_ParasiteImplanter_MAT.Wep_1stP_ParasiteImplanter_scope_PM"), MIC_3P="WEP_3P_ParasiteImplanter_MAT.Wep_3P_ParasiteImplanter_PM", MIC_Pickup="WEP_3P_ParasiteImplanter_MAT.WEP_3rdP_ParasiteImplanter_MIC")); -//BeyondHorizon AK12 - Skins.Add((Id=8846, Weapondef=class'KFWeapDef_Ak12', MIC_1P=("WEP_SkinSet43_MAT.space_ak12.Space_AK12_1P_Mint_MIC", "WEP_SkinSet43_MAT.space_ak12.Space_AK12_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_ak12.Space_AK12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_ak12.Space_AK12_3P_Pickup_MIC")) +//Corrupter Carbine Radioactive + Skins.Add((Id=9133, Weapondef=class'KFWeapDef_ParasiteImplanter', MIC_1P=("wep_skinset49_mat.Wep_ParasiteBody_Radioactive_PM", "wep_skinset49_mat.Wep_ParasiteScope_Radioactive_PM"), MIC_3P="wep_skinset49_mat.Wep_3rd_Parasite_Radioactive_PM", MIC_Pickup="wep_skinset49_mat.Wep_3rd_Parasite_Pickup_Radioactive_PM")); -//BeyondHorizon Desert Eagle - Skins.Add((Id=8847, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet43_MAT.space_deagle.Space_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_deagle.Space_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_deagle.Space_Deagle_3P_Pickup_MIC")) +//Corrupter Carbine Icy + Skins.Add((Id=9134, Weapondef=class'KFWeapDef_ParasiteImplanter', MIC_1P=("wep_skinset49_mat.Wep_ParasiteBody_Icy_PM", "wep_skinset49_mat.Wep_ParasiteScope_Icy_PM"), MIC_3P="wep_skinset49_mat.Wep_3rd_Parasite_Icy_PM", MIC_Pickup="wep_skinset49_mat.Wep_3rd_Parasite_Pickup_Icy_PM")); -//BeyondHorizon Doomstick - Skins.Add((Id=8848, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet43_MAT.space_quadbarrel.Space_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet43_MAT.space_quadbarrel.Space_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_quadbarrel.Space_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_quadbarrel.Space_QuadBarrel_3P_Pickup_MIC")) +//Corrupter Carbine Danger + Skins.Add((Id=9135, Weapondef=class'KFWeapDef_ParasiteImplanter', MIC_1P=("wep_skinset49_mat.Wep_ParasiteBody_Deathranger_PM", "wep_skinset49_mat.Wep_ParasiteScope_Deathranger_PM"), MIC_3P="wep_skinset49_mat.Wep_3rd_Parasite_Deathranger_PM", MIC_Pickup="wep_skinset49_mat.Wep_3rd_Parasite_Pickup_Deathranger_PM")); -//BeyondHorizon Hemoclobber - Skins.Add((Id=8849, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet43_MAT.space_medicbat.Space_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_medicbat.Space_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_medicbat.Space_MedicBat_3P_Pickup_MIC")) +//Corrupter Carbine Lush + Skins.Add((Id=9136, Weapondef=class'KFWeapDef_ParasiteImplanter', MIC_1P=("wep_skinset49_mat.Wep_ParasiteBody_Lush_PM", "wep_skinset49_mat.Wep_ParasiteScope_Lush_PM"), MIC_3P="wep_skinset49_mat.Wep_3rd_Parasite_Lush_PM", MIC_Pickup="wep_skinset49_mat.Wep_3rd_Parasite_Pickup_Lush_PM")); -//BeyondHorizon HMTech-501 Grenade Rifle - Skins.Add((Id=8850, Weapondef=class'KFWeapDef_MedicRifleGrenadeLauncher', MIC_1P=("WEP_SkinSet43_MAT.space_medicgrenadelauncher.Space_MedicGrenadeLauncher_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_medicgrenadelauncher.Space_MedicGrenadeLauncher_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_medicgrenadelauncher.Space_MedicGrenadeLauncher_3P_Pickup_MIC")) +//Corrupter Carbine Lightning + Skins.Add((Id=9137, Weapondef=class'KFWeapDef_ParasiteImplanter', MIC_1P=("wep_skinset49_mat.Wep_ParasiteBody_Bluelightning_PM", "wep_skinset49_mat.Wep_ParasiteScope_Bluelightning_PM"), MIC_3P="wep_skinset49_mat.Wep_3rd_Parasite_Bluelightning_PM", MIC_Pickup="wep_skinset49_mat.Wep_3rd_Parasite_Pickup_Bluelightning_PM")); -//BeyondHorizon M32 - Skins.Add((Id=8851, Weapondef=class'KFWeapDef_M32', MIC_1P=("WEP_SkinSet43_MAT.space_m32.Space_M32_1P_Mint_MIC", "WEP_SkinSet43_MAT.space_m32.Space_M32_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_m32.Space_M32_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_m32.Space_M32_3P_Pickup_MIC")) +//Piranha Standard + Skins.Add((Id=9126, Weapondef=class'KFWeapDef_BladedPistol', MIC_1P=("wep_1p_bladedpistol_mat.Wep_1stP_BladedPistol_MIC"), MIC_3P="wep_3p_bladedpistol_mat.WEP_3P_BladedPistol_MIC", MIC_Pickup="wep_3p_bladedpistol_mat.3P_Pickup_BladedPistol_MIC")); -//BeyondHorizon MAC 10 - Skins.Add((Id=8852, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet43_MAT.space_mac10.Space_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_mac10.Space_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_mac10.Space_MAC10_3P_Pickup_MIC")) +//Piranha Reptilian + Skins.Add((Id=9127, Weapondef=class'KFWeapDef_BladedPistol', MIC_1P=("wep_skinset50_mat.Wep_Bladed_Reptilian_PM"), MIC_3P="wep_skinset50_mat.3rd_Wep_Bladed_Reptilian_PM", MIC_Pickup="wep_skinset50_mat.3rd_Wep_Bladed_Pickup_Reptilian_PM")); -//BeyondHorizon Microwave Gun - Skins.Add((Id=8853, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet43_MAT.space_microwavegun.Space_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_microwavegun.Space_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_microwavegun.Space_MicrowaveGun_3P_Pickup_MIC")) +//Piranha Phoenix + Skins.Add((Id=9128, Weapondef=class'KFWeapDef_BladedPistol', MIC_1P=("wep_skinset50_mat.Wep_Bladed_Phoenix_PM"), MIC_3P="wep_skinset50_mat.3rd_Wep_Bladed_Phoenix_PM", MIC_Pickup="wep_skinset50_mat.3rd_Wep_Bladed_Pickup_Phoenix_PM")); -//BeyondHorizon P90 - Skins.Add((Id=8854, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet43_MAT.space_p90.Space_P90_1P_Mint_MIC", "WEP_SkinSet43_MAT.space_p90.Space_P90_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_p90.Space_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_p90.Space_P90_3P_Pickup_MIC")) +//Piranha Copper + Skins.Add((Id=9129, Weapondef=class'KFWeapDef_BladedPistol', MIC_1P=("wep_skinset50_mat.Wep_Bladed_Angrycopper_PM"), MIC_3P="wep_skinset50_mat.3rd_Wep_Bladed_Angrycopper_PM", MIC_Pickup="wep_skinset50_mat.3rd_Wep_Bladed_Pickup_Angrycopper_PM")); + +//Piranha Blade + Skins.Add((Id=9130, Weapondef=class'KFWeapDef_BladedPistol', MIC_1P=("wep_skinset50_mat.Wep_Bladed_Killerblade_PM"), MIC_3P="wep_skinset50_mat.3rd_Wep_Bladed_Killerblade_PM", MIC_Pickup="wep_skinset50_mat.3rd_Wep_Bladed_Pickup_Killerblade_PM")); + +//Piranha Rusty + Skins.Add((Id=9131, Weapondef=class'KFWeapDef_BladedPistol', MIC_1P=("wep_skinset50_mat.Wep_Bladed_Rustynightmare_PM"), MIC_3P="wep_skinset50_mat.3rd_Wep_Bladed_Rustynightmare_PM", MIC_Pickup="wep_skinset50_mat.3rd_Wep_Bladed_Pickup_Rustynightmare_PM")); //BeyondHorizon AA12 - Skins.Add((Id=8855, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_aa12.SpaceElite_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_aa12.SpaceElite_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_aa12.SpaceElite_AA12_3P_Pickup_MIC")) + Skins.Add((Id=8845, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet43_MAT.space_aa12.Space_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_aa12.Space_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_aa12.Space_AA12_3P_Pickup_MIC")) //BeyondHorizon AK12 - Skins.Add((Id=8856, Weapondef=class'KFWeapDef_Ak12', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_ak12.SpaceElite_AK12_1P_Mint_MIC", "WEP_SkinSet43_MAT.spaceelite_ak12.SpaceElite_AK12_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_ak12.SpaceElite_AK12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_ak12.SpaceElite_AK12_3P_Pickup_MIC")) + Skins.Add((Id=8846, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Ak12', MIC_1P=("WEP_SkinSet43_MAT.space_ak12.Space_AK12_1P_Mint_MIC", "WEP_SkinSet43_MAT.space_ak12.Space_AK12_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_ak12.Space_AK12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_ak12.Space_AK12_3P_Pickup_MIC")) //BeyondHorizon Desert Eagle - Skins.Add((Id=8857, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_deagle.SpaceElite_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_deagle.SpaceElite_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_deagle.SpaceElite_Deagle_3P_Pickup_MIC")) + Skins.Add((Id=8847, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet43_MAT.space_deagle.Space_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_deagle.Space_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_deagle.Space_Deagle_3P_Pickup_MIC")) //BeyondHorizon Doomstick - Skins.Add((Id=8858, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_quadbarrel.SpaceElite_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet43_MAT.spaceelite_quadbarrel.SpaceElite_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_quadbarrel.SpaceElite_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_quadbarrel.SpaceElite_QuadBarrel_3P_Pickup_MIC")) + Skins.Add((Id=8848, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet43_MAT.space_quadbarrel.Space_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet43_MAT.space_quadbarrel.Space_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_quadbarrel.Space_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_quadbarrel.Space_QuadBarrel_3P_Pickup_MIC")) //BeyondHorizon Hemoclobber - Skins.Add((Id=8859, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_medicbat.SpaceElite_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_medicbat.SpaceElite_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_medicbat.SpaceElite_MedicBat_3P_Pickup_MIC")) + Skins.Add((Id=8849, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet43_MAT.space_medicbat.Space_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_medicbat.Space_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_medicbat.Space_MedicBat_3P_Pickup_MIC")) //BeyondHorizon HMTech-501 Grenade Rifle - Skins.Add((Id=8860, Weapondef=class'KFWeapDef_MedicRifleGrenadeLauncher', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_medicgrenadelauncher.SpaceElite_MedicGrenadeLauncher_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_medicgrenadelauncher.SpaceElite_MedicGrenadeLauncher_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_medicgrenadelauncher.SpaceElite_MedicGrenadeLauncher_3P_Pickup_MIC")) + Skins.Add((Id=8850, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicRifleGrenadeLauncher', MIC_1P=("WEP_SkinSet43_MAT.space_medicgrenadelauncher.Space_MedicGrenadeLauncher_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_medicgrenadelauncher.Space_MedicGrenadeLauncher_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_medicgrenadelauncher.Space_MedicGrenadeLauncher_3P_Pickup_MIC")) //BeyondHorizon M32 - Skins.Add((Id=8861, Weapondef=class'KFWeapDef_M32', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_m32.SpaceElite_M32_1P_Mint_MIC", "WEP_SkinSet43_MAT.spaceelite_m32.SpaceElite_M32_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_m32.SpaceElite_M32_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_m32.SpaceElite_M32_3P_Pickup_MIC")) + Skins.Add((Id=8851, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M32', MIC_1P=("WEP_SkinSet43_MAT.space_m32.Space_M32_1P_Mint_MIC", "WEP_SkinSet43_MAT.space_m32.Space_M32_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_m32.Space_M32_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_m32.Space_M32_3P_Pickup_MIC")) //BeyondHorizon MAC 10 - Skins.Add((Id=8862, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_mac10.SpaceElite_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_mac10.SpaceElite_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_mac10.SpaceElite_MAC10_3P_Pickup_MIC")) + Skins.Add((Id=8852, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet43_MAT.space_mac10.Space_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_mac10.Space_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_mac10.Space_MAC10_3P_Pickup_MIC")) //BeyondHorizon Microwave Gun - Skins.Add((Id=8863, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_microwavegun.SpaceElite_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_microwavegun.SpaceElite_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_microwavegun.SpaceElite_MicrowaveGun_3P_Pickup_MIC")) + Skins.Add((Id=8853, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet43_MAT.space_microwavegun.Space_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_microwavegun.Space_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_microwavegun.Space_MicrowaveGun_3P_Pickup_MIC")) //BeyondHorizon P90 - Skins.Add((Id=8864, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_p90.SpaceElite_P90_1P_Mint_MIC", "WEP_SkinSet43_MAT.spaceelite_p90.SpaceElite_P90_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_p90.SpaceElite_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_p90.SpaceElite_P90_3P_Pickup_MIC")) + Skins.Add((Id=8854, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet43_MAT.space_p90.Space_P90_1P_Mint_MIC", "WEP_SkinSet43_MAT.space_p90.Space_P90_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_p90.Space_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_p90.Space_P90_3P_Pickup_MIC")) + +//BeyondHorizon AA12 + Skins.Add((Id=8855, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_aa12.SpaceElite_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_aa12.SpaceElite_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_aa12.SpaceElite_AA12_3P_Pickup_MIC")) + +//BeyondHorizon AK12 + Skins.Add((Id=8856, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Ak12', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_ak12.SpaceElite_AK12_1P_Mint_MIC", "WEP_SkinSet43_MAT.spaceelite_ak12.SpaceElite_AK12_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_ak12.SpaceElite_AK12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_ak12.SpaceElite_AK12_3P_Pickup_MIC")) + +//BeyondHorizon Desert Eagle + Skins.Add((Id=8857, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_deagle.SpaceElite_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_deagle.SpaceElite_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_deagle.SpaceElite_Deagle_3P_Pickup_MIC")) + +//BeyondHorizon Doomstick + Skins.Add((Id=8858, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_quadbarrel.SpaceElite_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet43_MAT.spaceelite_quadbarrel.SpaceElite_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_quadbarrel.SpaceElite_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_quadbarrel.SpaceElite_QuadBarrel_3P_Pickup_MIC")) + +//BeyondHorizon Hemoclobber + Skins.Add((Id=8859, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_medicbat.SpaceElite_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_medicbat.SpaceElite_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_medicbat.SpaceElite_MedicBat_3P_Pickup_MIC")) + +//BeyondHorizon HMTech-501 Grenade Rifle + Skins.Add((Id=8860, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicRifleGrenadeLauncher', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_medicgrenadelauncher.SpaceElite_MedicGrenadeLauncher_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_medicgrenadelauncher.SpaceElite_MedicGrenadeLauncher_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_medicgrenadelauncher.SpaceElite_MedicGrenadeLauncher_3P_Pickup_MIC")) + +//BeyondHorizon M32 + Skins.Add((Id=8861, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M32', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_m32.SpaceElite_M32_1P_Mint_MIC", "WEP_SkinSet43_MAT.spaceelite_m32.SpaceElite_M32_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_m32.SpaceElite_M32_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_m32.SpaceElite_M32_3P_Pickup_MIC")) + +//BeyondHorizon MAC 10 + Skins.Add((Id=8862, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_mac10.SpaceElite_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_mac10.SpaceElite_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_mac10.SpaceElite_MAC10_3P_Pickup_MIC")) + +//BeyondHorizon Microwave Gun + Skins.Add((Id=8863, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_microwavegun.SpaceElite_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_microwavegun.SpaceElite_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_microwavegun.SpaceElite_MicrowaveGun_3P_Pickup_MIC")) + +//BeyondHorizon P90 + Skins.Add((Id=8864, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet43_MAT.spaceelite_p90.SpaceElite_P90_1P_Mint_MIC", "WEP_SkinSet43_MAT.spaceelite_p90.SpaceElite_P90_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.spaceelite_p90.SpaceElite_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.spaceelite_p90.SpaceElite_P90_3P_Pickup_MIC")) //Scavenger AK12 Skins.Add((Id=8921, Weapondef=class'KFWeapDef_Ak12', MIC_1P=("wep_skinset44_mat.scavenger_ak12.Scavenger_AK12_1P_Mint_MIC", "wep_skinset44_mat.scavenger_ak12.Scavenger_AK12_Scope_1P_Mint_MIC"), MIC_3P="wep_skinset44_mat.scavenger_ak12.Scavenger_AK12_3P_Mint_MIC", MIC_Pickup="wep_skinset44_mat.scavenger_ak12.Scavenger_AK12_3P_Pickup_MIC")) + +//Infernal Elite Kriss + Skins.Add((Id=9081, Weapondef=class'KFWeapDef_Kriss', MIC_1P=("wep_skinset47_mat.elite_kriss.Elite_Kriss_1P_Mint_MIC", "wep_skinset47_mat.elite_kriss.Elite_Kriss_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet47_MAT.elite_kriss.Elite_Kriss_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet47_MAT.elite_kriss.Elite_Kriss_3P_Pickup_MIC")) + +//Hellmark Magma Katana + Skins.Add((Id=8991, Weapondef=class'KFWeapDef_Katana', MIC_1P=("WEP_SkinSet48_MAT.hellblade_katana.Hellblade_Katana_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_katana.Hellblade_Katana_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_katana.Hellblade_Katana_3P_Pickup_MIC")) + +//Hellmark Magma BattleAxe + Skins.Add((Id=8992, Weapondef=class'KFWeapDef_AbominationAxe', MIC_1P=("WEP_SkinSet48_MAT.hellblade_krampusaxe.Hellblade_KrampusAxe_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_krampusaxe.Hellblade_KrampusAxe_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_krampusaxe.Hellblade_KrampusAxe_3P_Pickup_MIC")) + +//Hellmark Magma Fire Axe + Skins.Add((Id=8993, Weapondef=class'KFWeapDef_FireAxe', MIC_1P=("WEP_SkinSet48_MAT.hellblade_fireaxe.Hellblade_FireAxe_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_fireaxe.Hellblade_FireAxe_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_fireaxe.Hellblade_FireAxe_3P_Pickup_MIC")) + +//Hellmark Magma Bone Crusher + Skins.Add((Id=8994, Weapondef=class'KFWeapDef_MaceAndShield', MIC_1P=("WEP_SkinSet48_MAT.hellblade_maceshield.Hellblade_Mace_1P_Mint_MIC", "WEP_SkinSet48_MAT.hellblade_maceshield.Hellblade_Shield_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_maceshield.Hellblade_MaceAndShield_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_maceshield.Hellblade_MaceAndShield_3P_Pickup_MIC")) + +//Hellmark Magma Pulverizer + Skins.Add((Id=8995, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet48_MAT.hellblade_pulverizer.Hellblade_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_pulverizer.Hellblade_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_pulverizer.Hellblade_Pulverizer_3P_Pickup_MIC")) + +//Hellmark Magma Dragonsbreath + Skins.Add((Id=8996, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet48_MAT.hellblade_dragonsbreath.Hellblade_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_dragonsbreath.Hellblade_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_dragonsbreath.Hellblade_Dragonsbreath_3P_Pickup_MIC")) + +//Hellmark Magma 500 Magnum Revolver + Skins.Add((Id=8997, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SW500', MIC_1P=("WEP_SkinSet48_MAT.hellblade_sw500.Hellblade_SW500_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_sw500.Hellblade_SW500_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_sw500.Hellblade_SW500_3P_Pickup_MIC")) + +//Hellmark Magma RPG-7 + Skins.Add((Id=8998, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet48_MAT.hellblade_rpg7.Hellblade_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_rpg7.Hellblade_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_rpg7.Hellblade_RPG7_3P_Pickup_MIC")) + +//Hellmark Magma Doomstick + Skins.Add((Id=8999, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet48_MAT.hellblade_quadbarrel.Hellblade_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet48_MAT.hellblade_quadbarrel.Hellblade_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_quadbarrel.Hellblade_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_quadbarrel.Hellblade_QuadBarrel_3P_Pickup_MIC")) + +//Hellmark Magma Hemoclobber + Skins.Add((Id=9000, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet48_MAT.hellblade_medicbat.Hellblade_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.hellblade_medicbat.Hellblade_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.hellblade_medicbat.Hellblade_MedicBat_3P_Pickup_MIC")) + +//Hellmark Thunder Katana + Skins.Add((Id=9001, Weapondef=class'KFWeapDef_Katana', MIC_1P=("WEP_SkinSet48_MAT.thunder_katana.Thunder_Katana_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_katana.Thunder_Katana_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_katana.Thunder_Katana_3P_Pickup_MIC")) + +//Hellmark Thunder BattleAxe + Skins.Add((Id=9002, Weapondef=class'KFWeapDef_AbominationAxe', MIC_1P=("WEP_SkinSet48_MAT.thunder_krampusaxe.Thunder_KrampusAxe_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_krampusaxe.Thunder_KrampusAxe_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_krampusaxe.Thunder_KrampusAxe_3P_Pickup_MIC")) + +//Hellmark Thunder Fire Axe + Skins.Add((Id=9003, Weapondef=class'KFWeapDef_FireAxe', MIC_1P=("WEP_SkinSet48_MAT.thunder_fireaxe.Thunder_FireAxe_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_fireaxe.Thunder_FireAxe_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_fireaxe.Thunder_FireAxe_3P_Pickup_MIC")) + +//Hellmark Thunder Bone Crusher + Skins.Add((Id=9004, Weapondef=class'KFWeapDef_MaceAndShield', MIC_1P=("WEP_SkinSet48_MAT.thunder_maceshield.Thunder_Mace_1P_Mint_MIC", "WEP_SkinSet48_MAT.thunder_maceshield.Thunder_Shield_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_maceshield.Thunder_MaceAndShield_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_maceshield.Thunder_MaceAndShield_3P_Pickup_MIC")) + +//Hellmark Thunder Pulverizer + Skins.Add((Id=9005, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet48_MAT.thunder_pulverizer.Thunder_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_pulverizer.Thunder_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_pulverizer.Thunder_Pulverizer_3P_Pickup_MIC")) + +//Hellmark Thunder Dragonsbreath + Skins.Add((Id=9006, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet48_MAT.thunder_dragonsbreath.Thunder_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_dragonsbreath.Thunder_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_dragonsbreath.Thunder_Dragonsbreath_3P_Pickup_MIC")) + +//Hellmark Thunder 500 Magnum Revolver + Skins.Add((Id=9007, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SW500', MIC_1P=("WEP_SkinSet48_MAT.thunder_sw500.Thunder_SW500_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_sw500.Thunder_SW500_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_sw500.Thunder_SW500_3P_Pickup_MIC")) + +//Hellmark Thunder RPG-7 + Skins.Add((Id=9008, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet48_MAT.thunder_rpg7.Thunder_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_rpg7.Thunder_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_rpg7.Thunder_RPG7_3P_Pickup_MIC")) + +//Hellmark Thunder Doomstick + Skins.Add((Id=9009, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet48_MAT.thunder_quadbarrel.Thunder_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet48_MAT.thunder_quadbarrel.Thunder_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_quadbarrel.Thunder_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_quadbarrel.Thunder_QuadBarrel_3P_Pickup_MIC")) + +//Hellmark Thunder Hemoclobber + Skins.Add((Id=9010, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet48_MAT.thunder_medicbat.Thunder_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet48_MAT.thunder_medicbat.Thunder_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet48_MAT.thunder_medicbat.Thunder_MedicBat_3P_Pickup_MIC")) } \ No newline at end of file diff --git a/KFGame/Classes/KFWeeklyOutbreakInformation.uc b/KFGame/Classes/KFWeeklyOutbreakInformation.uc index 217e39c..eca2e1d 100644 --- a/KFGame/Classes/KFWeeklyOutbreakInformation.uc +++ b/KFGame/Classes/KFWeeklyOutbreakInformation.uc @@ -27,7 +27,7 @@ var localized array ModifierDescriptions; cpptext { /** Num of Weekly events available */ - static const int NumWeeklyEvents = 12; + static const int NumWeeklyEvents = 14; } DefaultProperties { diff --git a/KFGame/KFOnlineStats.uci b/KFGame/KFOnlineStats.uci index 1bad7c4..0a67293 100644 --- a/KFGame/KFOnlineStats.uci +++ b/KFGame/KFOnlineStats.uci @@ -154,4 +154,5 @@ const STATID_ACHIEVE_HellmarkStationCollectibles = 4056; const STATID_ACHIEVE_ElysiumEndlessWaveFifteen = 4057; const STATID_ACHIEVE_Dystopia2029Collectibles = 4058; const STATID_ACHIEVE_MoonbaseCollectibles = 4059; +const STATID_ACHIEVE_NetherholdCollectibles = 4060; /** `endif */ diff --git a/KFGame/KFProfileSettings.uci b/KFGame/KFProfileSettings.uci index 437b516..66773f9 100644 --- a/KFGame/KFProfileSettings.uci +++ b/KFGame/KFProfileSettings.uci @@ -73,3 +73,4 @@ const KFID_VOIPMicVolumeMultiplier = 174; const KFID_GamepadDeadzoneScale = 175; const KFID_GamepadAccelerationJumpScale = 176; const KFID_HasTabbedToStore = 177; +const KFID_AllowSwapTo9mm = 178; // Halloween 2021 QoL: added option to quick switch weapons to 9mm diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_Boomy.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_Boomy.uc new file mode 100644 index 0000000..a53fc56 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_Boomy.uc @@ -0,0 +1,27 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Boomy +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_HRG_Boomy extends KFDT_Ballistic_Submachinegun + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=900 + KDeathUpKick=-300 + KDeathVel=100 + + StumblePower=35 + GunHitPower=25 + + WeaponDef=class'KFWeapDef_HRG_Boomy' + + //Perk + ModifierPerkList(0)=class'KFPerk_Demolitionist' +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_Energy_Primary.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_Energy_Primary.uc new file mode 100644 index 0000000..658eb55 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_Energy_Primary.uc @@ -0,0 +1,29 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Energy_Primary +//============================================================================= +// HRG Energy Gun Damage Type +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_HRG_Energy_Primary extends KFDT_Ballistic_Handgun + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=900 + KDeathUpKick=-300 + KDeathVel=100 + + StumblePower=15 + GunHitPower=175 + + WeaponDef=class'KFWeapDef_HRG_Energy' + + //Perk + ModifierPerkList(0)=class'KFPerk_Gunslinger' + + EffectGroup=FXG_Energy_Yellow +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_Energy_Secondary.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_Energy_Secondary.uc new file mode 100644 index 0000000..35e6e83 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_Energy_Secondary.uc @@ -0,0 +1,28 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Energy_Secondary +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_HRG_Energy_Secondary extends KFDT_Ballistic_Handgun + abstract + hidedropdown; + +defaultproperties +{ + + StumblePower=100 + KnockdownPower=20 + GunHitPower=250 + EMPPower=60 + + WeaponDef=class'KFWeapDef_HRG_Energy' + + //Perk + ModifierPerkList(2)=class'KFPerk_Gunslinger' + + EffectGroup=FXG_Energy_Magenta +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanter.uc b/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanter.uc new file mode 100644 index 0000000..c29fbda --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanter.uc @@ -0,0 +1,59 @@ +//============================================================================= +// KFDT_Ballistic_ParasiteImplanter +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_ParasiteImplanter extends KFDT_Ballistic_Rifle + abstract + hidedropdown; + +/** Allows the damage type to customize exactly which hit zones it can dismember */ +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ + if( super.CanDismemberHitZone( InHitZoneName ) ) + { + return true; + } + + switch ( InHitZoneName ) + { + case 'lupperarm': + case 'rupperarm': + return true; + } + + return false; +} + +/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ +static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) +{ + local class ToxicDT; + + ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); + if ( ToxicDT != None ) + { + Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); + } +} + +defaultproperties +{ + KDamageImpulse=2000 + KDeathUpKick=400 + KDeathVel=250 + + KnockdownPower=25 + //StunPower=20 //25 + StumblePower=80 + GunHitPower=160 + //MeleeHitPower=0 + + WeaponDef=class'KFWeapDef_ParasiteImplanter' + + ModifierPerkList(0)=class'KFPerk_FieldMedic' + ModifierPerkList(1)=class'KFPerk_Sharpshooter' +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanterAlt.uc b/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanterAlt.uc new file mode 100644 index 0000000..3a64e54 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanterAlt.uc @@ -0,0 +1,36 @@ +//============================================================================= +// KFDT_Ballistic_ParasiteImplanterAlt +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_ParasiteImplanterAlt extends KFDT_Toxic + abstract + hidedropdown; + +/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ +static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) +{ + local class ToxicDT; + + ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); + if ( ToxicDT != None ) + { + Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); + } +} + +defaultproperties +{ + KnockdownPower=30 + StumblePower=200 + GunHitPower=100 + PoisonPower=0 //80 + + WeaponDef=class'KFWeapDef_ParasiteImplanter' + + ModifierPerkList(0)=class'KFPerk_FieldMedic' + ModifierPerkList(1)=class'KFPerk_Sharpshooter' +} diff --git a/KFGameContent/Classes/KFDT_Bludgeon_BladedPistol.uc b/KFGameContent/Classes/KFDT_Bludgeon_BladedPistol.uc new file mode 100644 index 0000000..c5dbd90 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_BladedPistol.uc @@ -0,0 +1,136 @@ +//============================================================================= +// KFDT_Bludgeon_BladedPistol +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_BladedPistol extends KFDT_Slashing + abstract + hidedropdown; + +/** Allows the damage type to customize exactly which hit zones it can dismember */ +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ + return true; +} + +/** Allows the damage type to map a hit zone to a different bone for dismemberment purposes */ +static simulated function GetBoneToDismember(KFPawn_Monster InPawn, vector HitDirection, name InHitZoneName, out name OutBoneName) +{ + local EPawnOctant SlashDir; + local KFCharacterInfo_Monster MonsterInfo; + + MonsterInfo = InPawn.GetCharacterMonsterInfo(); + if ( MonsterInfo == none ) + { + return; + } + + SlashDir = GetLastSlashDirection(InPawn, HitDirection); + + if( SlashDir == DIR_Forward || SlashDir == DIR_Backward ) + { + if( InHitZoneName == 'chest' || InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Randomly pick the left or right shoulder bone and split the guy in half vertically + OutBoneName = Rand(2) == 0 + ? MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName + : MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + } + else if( SlashDir == DIR_Left || SlashDir == DIR_Right ) + { + if( InHitZoneName == 'chest' || InHitZoneName == 'abdomen' || InHitZoneName == 'stomach' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowHorizontalSplit ) + { + // Split the guy in half horizontally + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.SpineBoneName; + } + } + } + else if( SlashDir == DIR_ForwardLeft || SlashDir == DIR_BackwardRight ) + { + if( InHitZoneName == 'chest' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + else if( InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Use a random chance to decide whether to dismember the head or the shoulder constraints + if( Rand(2) == 0 ) + { + // ... and choose one of the shoulder constraints at random + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + } + } + else if( SlashDir == DIR_ForwardRight || SlashDir == DIR_BackwardLeft ) + { + if( InHitZoneName == 'chest' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName; + } + } + else if( InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Use a random chance to decide whether to dismember the head or the shoulder constraints + if( Rand(2) == 0 ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName; + } + } + } + } +} + +/** Allows the damage type to modify the impulse when a specified hit zone is dismembered */ +static simulated function ModifyDismembermentHitImpulse(KFPawn_Monster InPawn, name InHitZoneName, vector HitDirection, + out vector OutImpulseDir, out vector OutParentImpulseDir, + out float OutImpulseScale, out float OutParentImpulseScale) +{ + local EPawnOctant SlashDir; + + SlashDir = GetLastSlashDirection(InPawn, HitDirection); + + // Apply upward impulse on decapitation from a clean horizontal slash + if( InHitZoneName == 'head' && + ( SlashDir == DIR_Left || SlashDir == DIR_Right ) ) + { + OutImpulseDir += 10*vect(0,0,1); + OutImpulseDir = Normal(OutImpulseDir); + OutParentImpulseScale = 0.f; + } + // Do not apply any impulse on split in half from a vertical slash + else if( (InHitZoneName == 'head' || InHitZoneName == 'chest' ) && + ( SlashDir == DIR_Forward || SlashDir == DIR_Backward) ) + { + OutImpulseScale = 0.f; + OutParentImpulseScale = 0.f; + } +} + +DefaultProperties +{ + StumblePower=200 + MeleeHitPower=100 + + WeaponDef=class'KFWeapDef_BladedPistol' + + ModifierPerkList(0)=class'KFPerk_Berserker' + ModifierPerkList(1)=class'KFPerk_Gunslinger' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_Boomy.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Boomy.uc new file mode 100644 index 0000000..9425e9a --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Boomy.uc @@ -0,0 +1,15 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_Boomy +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_Boomy extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + WeaponDef=class'KFWeapDef_HRG_Boomy' +} diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_Energy.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Energy.uc new file mode 100644 index 0000000..ed09c9a --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Energy.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_Energy +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_Energy extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_Energy' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_ParasiteImplanter.uc b/KFGameContent/Classes/KFDT_Bludgeon_ParasiteImplanter.uc new file mode 100644 index 0000000..2155c97 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_ParasiteImplanter.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_ParasiteImplanter +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_ParasiteImplanter extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_ParasiteImplanter' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Explosive_HRG_Boomy.uc b/KFGameContent/Classes/KFDT_Explosive_HRG_Boomy.uc new file mode 100644 index 0000000..b1bb5f4 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_HRG_Boomy.uc @@ -0,0 +1,30 @@ +//============================================================================= +// KFDT_Explosive_GravityImploderWave +//============================================================================= +// Explosive damage type for the Gravity Imploder shockwave +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2020 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Explosive_HRG_Boomy extends KFDT_Explosive + abstract + hidedropdown; + +defaultproperties +{ + bShouldSpawnPersistentBlood=true + + // physics impact + RadialDamageImpulse=3000 //5000 //20000 + GibImpulseScale=0.15 + KDeathUpKick=1000 + KDeathVel=300 + + KnockdownPower=7 + StumblePower=20 + + //Perk + ModifierPerkList(0)=class'KFPerk_Demolitionist' + WeaponDef=class'KFWeapDef_HRG_Boomy' +} diff --git a/KFGameContent/Classes/KFDT_Healing_ParasiteSeed.uc b/KFGameContent/Classes/KFDT_Healing_ParasiteSeed.uc new file mode 100644 index 0000000..fc2ff2c --- /dev/null +++ b/KFGameContent/Classes/KFDT_Healing_ParasiteSeed.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Healing_ParasiteSeed +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Healing_ParasiteSeed extends KFDT_Healing + abstract + hidedropdown; + +defaultproperties +{ + bNoPain=true +} diff --git a/KFGameContent/Classes/KFDT_Slashing_BladedPistol.uc b/KFGameContent/Classes/KFDT_Slashing_BladedPistol.uc new file mode 100644 index 0000000..7341278 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Slashing_BladedPistol.uc @@ -0,0 +1,190 @@ +//============================================================================= +// KFDT_Slashing_EvisceratorProj +//============================================================================= +// Damage type for projectiles fired from the Eviscerator +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2015 Tripwire Interactive LLC +// John "Ramm-Jaeger" Gibson +//============================================================================= + +class KFDT_Slashing_BladedPistol extends KFDT_Slashing + abstract + hidedropdown; + +`define COSINE_0_DEGREES 1 +`define COSINE_20_DEGREES 0.94 +`define COSINE_70_DEGREES 0.34 +`define COSINE_90_DEGREES 0 + +/** Allows the damage type to customize exactly which hit zones it can dismember */ +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ + return true; +} + +/** @return The slast type from the HitFxInfo. This returns a 8-way slash info. */ +static simulated function EPawnOctant GetLastSlashDirection(KFPawn_Monster InPawn, vector HitDirection) +{ + local vector SlashPlaneNormal; + local rotator InstigatorRotation; + local vector InstigatorFaceDir, InstigatorRightDir; + local float UpDotSlash, RightDotSlash, UpThresholdValue; + + if( InPawn != none ) + { + HitDirection = Normal(HitDirection); + InstigatorRotation = InPawn.GetBaseAimRotation(); + InstigatorFaceDir = GetRotatorAxis(InstigatorRotation, 0); + InstigatorRightDir = GetRotatorAxis(InstigatorRotation, 1); + + // Cross the face direction of the instigator with the hit direction to find + // the normal to the "slashing" plane + SlashPlaneNormal = Normal(InstigatorFaceDir Cross HitDirection); + + // Dot the slash plane normal with the world up direction to figure out + // the quadrant for the normal of the slash plane + UpDotSlash = vect(0,0,1) Dot SlashPlaneNormal; + UpThresholdValue = Abs(UpDotSlash); + + // Dot the slash plane normal with the instigator right to figure out + // the direction of the slash + RightDotSlash = InstigatorRightDir Dot SlashPlaneNormal; + + // Threshhold against the preset values to find out the type of slash. The sign (+ or -) of the + // dot products are used to further classify the direction of the slash + if( UpThresholdValue <= `COSINE_0_DEGREES && UpThresholdValue >= `COSINE_20_DEGREES ) + { + if( UpDotSlash > 0 ) + return DIR_Right; + else + return DIR_Left; + } + else if( UpThresholdValue <= `COSINE_20_DEGREES && UpThresholdValue >= `COSINE_70_DEGREES ) + { + if( UpDotSlash < 0 && RightDotSlash > 0 ) + return DIR_ForwardLeft; + else if( UpDotSlash > 0 && RightDotSlash > 0 ) + return DIR_ForwardRight; + else if( UpDotSlash > 0 && RightDotSlash < 0 ) + return DIR_BackwardRight; + else if( UpDotSlash < 0 && RightDotSlash < 0 ) + return DIR_BackwardLeft; + } + else if( UpThresholdValue <= `COSINE_70_DEGREES && UpThresholdValue >= `COSINE_90_DEGREES ) + { + if( RightDotSlash > 0 ) + return DIR_Forward; + else + return DIR_Backward; + } + } + + return DIR_None; +} + +/** Allows the damage type to map a hit zone to a different bone for dismemberment purposes */ +static simulated function GetBoneToDismember(KFPawn_Monster InPawn, vector HitDirection, name InHitZoneName, out name OutBoneName) +{ + local EPawnOctant SlashDir; + local KFCharacterInfo_Monster MonsterInfo; + + MonsterInfo = InPawn.GetCharacterMonsterInfo(); + if ( MonsterInfo == none ) + { + return; + } + + SlashDir = GetLastSlashDirection(InPawn, HitDirection); + + if( SlashDir == DIR_Forward || SlashDir == DIR_Backward ) + { + if( InHitZoneName == 'chest' || InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Randomly pick the left or right shoulder bone and split the guy in half vertically + OutBoneName = Rand(2) == 0 + ? MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName + : MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + } + else if( SlashDir == DIR_Left || SlashDir == DIR_Right ) + { + if( InHitZoneName == 'chest' || InHitZoneName == 'abdomen' || InHitZoneName == 'stomach') + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowHorizontalSplit ) + { + // Split the guy in half horizontally + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.SpineBoneName; + } + } + } + else if( SlashDir == DIR_ForwardLeft || SlashDir == DIR_BackwardRight ) + { + if( InHitZoneName == 'chest' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + else if( InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Use a random chance to decide whether to dismember the head or the shoulder constraints + if( Rand(2) == 0 ) + { + // ... and choose one of the shoulder constraints at random + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + } + } + else if( SlashDir == DIR_ForwardRight || SlashDir == DIR_BackwardLeft ) + { + if( InHitZoneName == 'chest' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName; + } + } + else if( InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Use a random chance to decide whether to dismember the head or the shoulder constraints + if( Rand(2) == 0 ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName; + } + } + } + } +} + +defaultproperties +{ + EffectGroup=FXG_Sawblade + KDamageImpulse=1000 + KDeathUpKick=800 + KDeathVel=135 + + // Instigator damage can happen on owning client when projectile hits before Instigator is replicated and + // (Other != Instigator) == FALSE. This problem is specific to the eviscerator projectile since it's a + // replicated CSHD weapon, but it might make sense to have bNoInstigatorDamage=true on more weapons. + bNoInstigatorDamage=true + + //StunPower=0 + StumblePower=100 + GunHitPower=100 + //MeleeHitPower=100 + + WeaponDef=class'KFWeapDef_BladedPistol' + + ModifierPerkList(0)=class'KFPerk_Berserker' + ModifierPerkList(1)=class'KFPerk_Gunslinger' +} diff --git a/KFGameContent/Classes/KFDT_Toxic_ParasiteSeedExplosion.uc b/KFGameContent/Classes/KFDT_Toxic_ParasiteSeedExplosion.uc new file mode 100644 index 0000000..0ed422b --- /dev/null +++ b/KFGameContent/Classes/KFDT_Toxic_ParasiteSeedExplosion.uc @@ -0,0 +1,29 @@ +//============================================================================= +// KFDT_Toxic_ParasiteSeedExplosion +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Toxic_ParasiteSeedExplosion extends KFDT_Toxic_MedicGrenade + abstract + hidedropdown; + +defaultproperties +{ + DoT_Type=DOT_Toxic + DoT_Duration=6.5 //3.0 + DoT_Interval=0.5 + DoT_DamageScale=0.1 + + KnockdownPower=30 + StumblePower=200 + GunHitPower=100 + PoisonPower=80.f + + ModifierPerkList(0)=class'KFPerk_FieldMedic' + ModifierPerkList(1)=class'KFPerk_SharpShooter' + + WeaponDef=class'KFWeapDef_ParasiteImplanter' +} diff --git a/KFGameContent/Classes/KFExplosion_ParasiteSeed.uc b/KFGameContent/Classes/KFExplosion_ParasiteSeed.uc new file mode 100644 index 0000000..094f85a --- /dev/null +++ b/KFGameContent/Classes/KFExplosion_ParasiteSeed.uc @@ -0,0 +1,97 @@ +//============================================================================= +// KFExplosion_ParasiteSeed +//============================================================================= +// Used by projectiles and kismet to spawn an explosion +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFExplosion_ParasiteSeed extends KFExplosion_MedicGrenade; + +var transient bool FirstExplosion; + +/** + * Deal damage or heal players + */ +protected simulated function AffectsPawn(Pawn Victim, float DamageScale) +{ + local KFPawn_Human HumanVictim; + local KFPawn_Monster MonsterVictim; + local KFProj_MedicGrenade OwnerProjectile; + local bool bCanRepairArmor; + local Box BBox; + local vector BBoxCenter; + local Actor HitActor; + local bool bDamageBlocked; + + if( Victim != none && Victim.IsAliveAndWell() ) + { + MonsterVictim = KFPawn_Monster(Victim); + if( MonsterVictim != none ) + { + if(!FirstExplosion || bWasFadedOut|| bDeleteMe || bPendingDelete ) + { + return; + } + + Victim.GetComponentsBoundingBox(BBox); + BBoxCenter = (BBox.Min + BBox.Max) * 0.5f; + HitActor = TraceExplosive(BBoxCenter, Location + vect(0, 0, 20)); + bDamageBlocked = (HitActor != None && HitActor != Victim); + if(bDamageBlocked && HitActor.IsA('KFDoorActor')) + { + bDamageBlocked = false; + } + if( !bDamageBlocked ) + { + Victim.TakeRadiusDamage(InstigatorController, ExplosionTemplate.Damage * DamageScale, ExplosionTemplate.DamageRadius, + ExplosionTemplate.MyDamageType, ExplosionTemplate.MomentumTransferScale, Location, bDoFullDamage, + (Owner != None) ? Owner : self, ExplosionTemplate.DamageFalloffExponent); + } + } + else + { + HumanVictim = KFPawn_Human(Victim); + if( HumanVictim != none && HumanVictim.GetExposureTo(Location) > 0 ) + { + OwnerProjectile = KFProj_MedicGrenade(Owner); + if( OwnerProjectile != none ) + { + bCanRepairArmor = OwnerProjectile.HealedPawns.Find( HumanVictim ) == INDEX_NONE; + } + + HumanVictim.HealDamage(HealingAmount, InstigatorController, HealingDamageType, bCanRepairArmor); + + if( bCanRepairArmor ) + { + OwnerProjectile.HealedPawns.AddItem( HumanVictim ); + } + } + } + } +} + +simulated function DelayedExplosionDamage() +{ + FirstExplosion=false; + super.DelayedExplosionDamage(); +} + +DefaultProperties +{ + FirstExplosion = true; + + HealingDamageType=class'KFDT_Healing_ParasiteSeed' + HealingAmount=5 //10 + Interval=0.5 //1 + MaxTime=5.5 //6.5 //8 + + bExplodeMoreThanOnce=false + bDoFullDamage=false //true + bSkipLineCheckForPawns=true + + LoopingParticleEffect=ParticleSystem'WEP_3P_Medic_Grenade_EMIT.FX_Medic_Grenade_Explosion' + LoopStartEvent=AkEvent'WW_WEP_Medic_GrenadeLauncher.Play_WEP_Medic_GrenadeLauncher_Grenade_Smoke_Loop' + LoopStopEvent=AkEvent'WW_WEP_Medic_GrenadeLauncher.Stop_WEP_Medic_GrenadeLauncher_Grenade_Smoke_Loop' +} diff --git a/KFGameContent/Classes/KFExplosion_ParasiteSeedHuman.uc b/KFGameContent/Classes/KFExplosion_ParasiteSeedHuman.uc new file mode 100644 index 0000000..52ad356 --- /dev/null +++ b/KFGameContent/Classes/KFExplosion_ParasiteSeedHuman.uc @@ -0,0 +1,26 @@ +//============================================================================= +// KFExplosion_ParasiteSeedHuman +//============================================================================= +// Explosion created when hitting a human +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFExplosion_ParasiteSeedHuman extends KFExplosion_MedicGrenade; + +DefaultProperties +{ + HealingDamageType=class'KFDT_Healing_ParasiteSeed' + HealingAmount=30 //10 + Interval=1.0 //1 + MaxTime=0.5 //6.5 //8 + + bExplodeMoreThanOnce=false + bDoFullDamage=false //true + bSkipLineCheckForPawns=true + + LoopingParticleEffect=ParticleSystem'WEP_3P_Medic_Grenade_EMIT.FX_Medic_Grenade_Explosion' + LoopStartEvent=AkEvent'WW_WEP_Medic_GrenadeLauncher.Play_WEP_Medic_GrenadeLauncher_Grenade_Smoke_Loop' + LoopStopEvent=AkEvent'WW_WEP_Medic_GrenadeLauncher.Stop_WEP_Medic_GrenadeLauncher_Grenade_Smoke_Loop' +} diff --git a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc index 15c4e78..25995ae 100644 --- a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc +++ b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc @@ -235,16 +235,26 @@ protected function ScoreMonsterKill( Controller Killer, Controller Monster, KFPa if(OutbreakEvent.ActiveEvent.bHealAfterKill) { - if( MonsterPawn != none && MonsterPawn.DamageHistory.Length > 0 ) - { - HealAfterKilling( MonsterPawn, Killer ); + if( MonsterPawn != none && MonsterPawn.DamageHistory.Length > 0 ) + { + if(OutbreakEvent.ActiveEvent.bHealWithHeadshot) + { + if (MonsterPawn.LastHitZoneIndex == HZI_HEAD) + { + HealAfterKilling( MonsterPawn, Killer, false ); + } + } + else + { + HealAfterKilling( MonsterPawn, Killer ); + } } } } /** Heal players after a Zed was killed, based in more heal to the player that was the killer and less heal to the players that damaged the Zed */ -function HealAfterKilling(KFPawn_Monster MonsterPawn , Controller Killer) +function HealAfterKilling(KFPawn_Monster MonsterPawn , Controller Killer, optional bool bGivePowerUp = true) { local int i; local int j; @@ -302,7 +312,7 @@ function HealAfterKilling(KFPawn_Monster MonsterPawn , Controller Killer) { PawnHuman.HealDamageForce(MonsterPawn.HealByKill, KFPC, class'KFDT_Healing', false, false ); - if( KFPawn_ZedFleshpound(MonsterPawn) != none || KFPawn_ZedScrake(MonsterPawn) != none ) + if( bGivePowerUp && ( KFPawn_ZedFleshpound(MonsterPawn) != none || KFPawn_ZedScrake(MonsterPawn) != none )) { KFPC.ReceivePowerUp(class'KFPowerUp_HellishRage_NoCostHeal'); } @@ -322,6 +332,11 @@ function HealAfterKilling(KFPawn_Monster MonsterPawn , Controller Killer) function StartMatch() { super.StartMatch(); + + if (OutbreakEvent.ActiveEvent.bForceWWLMusic) + { + ForceWWLMusicTrack(); + } } function CreateDifficultyInfo(string Options) diff --git a/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc b/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc index 6447aba..6f649fc 100644 --- a/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc +++ b/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc @@ -64,6 +64,7 @@ simulated function NotifyWaveStart() DefaultProperties { + bIsWeeklyMode=True BrokenTraderItemPickups={( (WeaponClasses={( class'KFGame.KFWeapDef_9mmDual', diff --git a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc index b206553..532f1ea 100644 --- a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc +++ b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc @@ -518,6 +518,118 @@ defaultproperties )} + //Wild West + SetEvents[12]={( + EventDifficulty=2, + GameLength=GL_Normal, + PerksAvailableList=(class'KFPerk_Gunslinger', class'KFPerk_Sharpshooter'), + TraderWeaponList=KFGFxObject_TraderItems'GP_Trader_ARCH.WildWestWeeklyTraderList', + bWildWestSkillConditionsActive=true, + //bModifyZedTimeOnANearZedKill=true, + DoshOnKillGlobalModifier=1.4, + PickupResetTime=PRS_Wave, + OverrideItemPickupModifier=0, + OverrideAmmoPickupModifier=1, + WaveAmmoPickupModifiers={( + 0.125, 0.175, 0.35, 0.525, 0.7, 0.875, 0.75, 0.99, 0.99 + )}, + SpawnRateMultiplier=0.75, + WaveAICountScale=(0.75, 0.7, 0.7, 0.65, 0.65, 0.6), + bHealAfterKill = true, + bHealWithHeadshot = true, + bForceWWLMusic = true, + ZedsToAdjust={( + (ClassToAdjust=class'KFGameContent.KFPawn_ZedCrawler', HealByKill=1, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedCrawlerKing', HealByKill=5, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_Cyst', HealByKill=1, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_Alpha', HealByKill=1, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_AlphaKing', HealByKill=10, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_Slasher', HealByKill=1, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedSiren', HealByKill=10, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedStalker', HealByKill=2, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedGorefast', HealByKill=5, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedGorefastDualBlade', HealByKill=5, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedBloat', HealByKill=10, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedHusk', HealByKill=10, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedDAR_EMP', HealByKill=5, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedDAR_Laser', HealByKill=5, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedDAR_Rocket', HealByKill=5, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedScrake', HealByKill=20, HealByAssistance=0), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedFleshpound', HealByKill=20, HealByAssistance=0, HealthScale=0.8, DamageDealtScale=0.7), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedFleshpoundMini', HealByKill=15, HealByAssistance=0, HealthScale=0.8, DamageDealtScale=0.7), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedBloatKingSubspawn', HealByKill=2, HealByAssistance=0), + + (ClassToAdjust=class'KFGameContent.KFPawn_ZedMatriarch',HealthScale=0.8,DamageDealtScale=0.7), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedPatriarch',HealthScale=0.8,DamageDealtScale=0.7), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedHans',HealthScale=0.8,DamageDealtScale=0.7), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedFleshpoundKing',HealthScale=0.8,DamageDealtScale=0.7), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedBloatKing',HealthScale=0.8,DamageDealtScale=0.7) + )}, + SpawnReplacementList={( + (SpawnEntry=AT_FleshPound,NewClass=(class'KFGameContent.KFPawn_ZedScrake'),PercentChance=0.5), + (SpawnEntry=AT_FleshpoundMini,NewClass=(class'KFGameContent.KFPawn_ZedGorefastDualBlade'),PercentChance=0.5) + )} + + )} + + //Infernal Eternal + SetEvents[13]={( + EventDifficulty=3, + GameLength=GL_Normal, + SpawnRateMultiplier=3, + WaveAICountScale=(1.3, 1.3, 1.3, 1.3, 1.3, 1.3), + OverrideAmmoPickupModifier=1, // 1.2 + WaveAmmoPickupModifiers={( + 0.125, 0.15, 0.3, 0.45, 0.6, 0.75, 0.9, 0.99, 0.99 + )}, + bUseOverrideAmmoRespawnTime=true, + OverrideAmmoRespawnTime={( + PlayersMod[0]=25.000000, + PlayersMod[1]=12.000000, + PlayersMod[2]=8.000000, + PlayersMod[3]=5.000000, + PlayersMod[4]=4.000000, + PlayersMod[5]=3.000000, + ModCap=1.000000 + )}, + + ZedsToAdjust={( + (ClassToAdjust=class'KFGameContent.KFPawn_ZedMatriarch',HealthScale=2.0,DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedPatriarch',HealthScale=2.0,DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedHans',HealthScale=2.0,DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedFleshpoundKing',HealthScale=2.0,DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedBloatKing',HealthScale=2.0,DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_Cyst',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_Alpha',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_AlphaKing',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_Slasher',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedSiren',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedStalker',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedCrawler',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedCrawlerKing',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedGorefast',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedGorefastDualBlade',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedBloat',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedHusk',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedDAR_EMP',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedDAR_Laser',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedDAR_Rocket',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedScrake',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedFleshpound',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedFleshpoundMini',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedBloatKingSubspawn',bStartEnraged=true, DamageDealtScale=1.0, InitialGroundSpeedModifierScale=1.2) + )}, + SpawnReplacementList={( + (SpawnEntry=AT_Clot,NewClass=(class'KFGameContent.KFPawn_ZedClot_Alpha'),PercentChance=0.15), + (SpawnEntry=AT_AlphaClot,NewClass=(class'KFGameContent.KFPawn_ZedClot_AlphaKing'),PercentChance=0.15), + (SpawnEntry=AT_GoreFast,NewClass=(class'KFGameContent.KFPawn_ZedGorefastDualBlade'),PercentChance=0.15), + (SpawnEntry=AT_Crawler,NewClass=(class'KFGameContent.KFPawn_ZedCrawlerKing'),PercentChance=0.15), + (SpawnEntry=AT_Bloat,NewClass=(class'KFGameContent.KFPawn_ZedScrake'),PercentChance=0.05), + (SpawnEntry=AT_FleshpoundMini,NewClass=(class'KFGameContent.KFPawn_ZedFleshpound'),PercentChance=0.05) + )} + )} + //Test events from here down. These don't end up in the regular rotation. // The override ID starts from one higher than the last SetEvents entry above. // Ex: Big head = 7, Horde = 8 diff --git a/KFGameContent/Classes/KFPawn_ZedClot_Alpha.uc b/KFGameContent/Classes/KFPawn_ZedClot_Alpha.uc index 2d1968e..2536aec 100644 --- a/KFGameContent/Classes/KFPawn_ZedClot_Alpha.uc +++ b/KFGameContent/Classes/KFPawn_ZedClot_Alpha.uc @@ -98,4 +98,6 @@ DefaultProperties `if(`notdefined(ShippingPC)) DebugRadarTexture=Texture2D'UI_ZEDRadar_TEX.MapIcon_Clot'; `endif + + ZEDCowboyHatAttachName=Hat_Attach } diff --git a/KFGameContent/Classes/KFPawn_ZedClot_Slasher.uc b/KFGameContent/Classes/KFPawn_ZedClot_Slasher.uc index 3c13b2b..dc1893b 100644 --- a/KFGameContent/Classes/KFPawn_ZedClot_Slasher.uc +++ b/KFGameContent/Classes/KFPawn_ZedClot_Slasher.uc @@ -88,4 +88,6 @@ DefaultProperties `if(`notdefined(ShippingPC)) DebugRadarTexture=Texture2D'UI_ZEDRadar_TEX.MapIcon_Slasher'; `endif + + ZEDCowboyHatAttachName=Hat_Attach } diff --git a/KFGameContent/Classes/KFPawn_ZedCrawler.uc b/KFGameContent/Classes/KFPawn_ZedCrawler.uc index 138b520..d2101ef 100644 --- a/KFGameContent/Classes/KFPawn_ZedCrawler.uc +++ b/KFGameContent/Classes/KFPawn_ZedCrawler.uc @@ -464,4 +464,6 @@ defaultproperties // --------------------------------------------- // Spawning MinSpawnSquadSizeType=EST_Crawler + + ZEDCowboyHatAttachName=Hat_Attach } diff --git a/KFGameContent/Classes/KFPawn_ZedDAR.uc b/KFGameContent/Classes/KFPawn_ZedDAR.uc index 0b66556..c392d51 100644 --- a/KFGameContent/Classes/KFPawn_ZedDAR.uc +++ b/KFGameContent/Classes/KFPawn_ZedDAR.uc @@ -431,4 +431,6 @@ defaultproperties StartSprintingSound=AkEvent'WW_ZED_Evil_DAR.Play_ZED_EvilDAR_SFX_Thruster_Start' SprintLoopingSound=AkEvent'WW_ZED_Evil_DAR.Play_ZED_EvilDAR_SFX_Thruster_LP' StopSprintingSound=AkEvent'WW_ZED_Evil_DAR.Play_ZED_EvilDAR_SFX_Thruster_Stop' + + ZEDCowboyHatAttachName=Hat_Attach } diff --git a/KFGameContent/Classes/KFPawn_ZedFleshpound.uc b/KFGameContent/Classes/KFPawn_ZedFleshpound.uc index bb4dad5..fd1fe3d 100644 --- a/KFGameContent/Classes/KFPawn_ZedFleshpound.uc +++ b/KFGameContent/Classes/KFPawn_ZedFleshpound.uc @@ -605,4 +605,6 @@ End Object MinSpawnSquadSizeType=EST_Large OnDeathAchievementID=KFACHID_ItsOnlyAFleshWound + + ZEDCowboyHatAttachName=Hat_Attach } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedFleshpoundMini.uc b/KFGameContent/Classes/KFPawn_ZedFleshpoundMini.uc index 0bde540..1bb9c82 100644 --- a/KFGameContent/Classes/KFPawn_ZedFleshpoundMini.uc +++ b/KFGameContent/Classes/KFPawn_ZedFleshpoundMini.uc @@ -72,4 +72,6 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(0.95), Cooldown=10.5, Duration=1.0) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0, 2.0), Cooldown=8.5, Duration=5.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.75)) + + ZEDCowboyHatAttachName=HEAD_Attach } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedHusk.uc b/KFGameContent/Classes/KFPawn_ZedHusk.uc index 52cdf1f..67f3a24 100644 --- a/KFGameContent/Classes/KFPawn_ZedHusk.uc +++ b/KFGameContent/Classes/KFPawn_ZedHusk.uc @@ -657,4 +657,6 @@ DefaultProperties // --------------------------------------------- // Spawning MinSpawnSquadSizeType=EST_Medium + + ZEDCowboyHatAttachName=Hat_Attach } diff --git a/KFGameContent/Classes/KFPawn_ZedMatriarch.uc b/KFGameContent/Classes/KFPawn_ZedMatriarch.uc index ecb454d..99e36ab 100644 --- a/KFGameContent/Classes/KFPawn_ZedMatriarch.uc +++ b/KFGameContent/Classes/KFPawn_ZedMatriarch.uc @@ -2012,4 +2012,6 @@ defaultproperties // Gun tracking bUseServerSideGunTracking=true GunTargetBoneName=Spine2 + + ZEDCowboyHatAttachName=Hat_Attach } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedScrake.uc b/KFGameContent/Classes/KFPawn_ZedScrake.uc index c3b9a58..824c1d0 100644 --- a/KFGameContent/Classes/KFPawn_ZedScrake.uc +++ b/KFGameContent/Classes/KFPawn_ZedScrake.uc @@ -391,4 +391,6 @@ defaultproperties MinSpawnSquadSizeType=EST_Large OnDeathAchievementID=KFACHID_HackAndSlash + + ZEDCowboyHatAttachName=Hat_Attach } diff --git a/KFGameContent/Classes/KFPawn_ZedSiren.uc b/KFGameContent/Classes/KFPawn_ZedSiren.uc index 05814f7..097e36c 100644 --- a/KFGameContent/Classes/KFPawn_ZedSiren.uc +++ b/KFGameContent/Classes/KFPawn_ZedSiren.uc @@ -250,4 +250,6 @@ defaultproperties `endif OnDeathAchievementID=KFACHID_DeadSilence + + ZEDCowboyHatAttachName=Hat_Attach } diff --git a/KFGameContent/Classes/KFProj_Blade_BladedPistol.uc b/KFGameContent/Classes/KFProj_Blade_BladedPistol.uc new file mode 100644 index 0000000..159921d --- /dev/null +++ b/KFGameContent/Classes/KFProj_Blade_BladedPistol.uc @@ -0,0 +1,79 @@ +//============================================================================= +// KFProj_Blade_BladedPistol +//============================================================================= +// Blade class for the BladedPistol +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Blade_BladedPistol extends KFProj_RicochetStickBullet + hidedropdown; + +// Make sure that last location always exists. +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + LastLocation = Location; +} + +simulated function bool ShouldProcessBulletTouch() +{ + return BouncesLeft > 0 && GravityScale == default.GravityScale; +} + +defaultproperties +{ + MaxSpeed=4000.0 //2500.0 + Speed=4000.0 //2500.0 + GravityScale=0.8 //0.75 + + DamageRadius=0 + + bWarnAIWhenFired=true + + BouncesLeft=2 + DampingFactor=0.8 //0.95 + RicochetEffects=KFImpactEffectInfo'WEP_BladedPistol_ARCH.BladedImpacts' + LifeSpan=8 + LifeSpanAfterStick=180 + + Begin Object Name=CollisionCylinder + CollisionRadius=6 + CollisionHeight=2 + End Object + + // Additional zero-extent line traces + //ExtraLineCollisionOffsets.Add((Y=-8)) + //ExtraLineCollisionOffsets.Add((Y=8)) + ExtraLineCollisionOffsets.Add((Y=-16)) + ExtraLineCollisionOffsets.Add((Y=16)) + ExtraLineCollisionOffsets.Add((Z=-6)) + ExtraLineCollisionOffsets.Add((Z=6)) + // Since we're still using an extent cylinder, we need a line at 0 + ExtraLineCollisionOffsets.Add(()) + + bAmbientSoundZedTimeOnly=false + bNoReplicationToInstigator=false + bUseClientSideHitDetection=true + bUpdateSimulatedPosition=true + bRotationFollowsVelocity=false + bNetTemporary=False + + + ProjFlightTemplate=ParticleSystem'WEP_BladedPistol_EMIT.FX_bladed_projectile_01' + ProjFlightTemplateZedTime=ParticleSystem'WEP_BladedPistol_EMIT.FX_bladed_projectile_01' + + ImpactEffects=KFImpactEffectInfo'WEP_BladedPistol_ARCH.BladedEmbedFX' + + AmbientSoundPlayEvent=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Projectile_Loop' + AmbientSoundStopEvent=AkEvent'WW_WEP_BladedPistol.Stop_WEP_BladedPistol_Projectile_Loop' + + PickupRadius=250 //200 + WeaponClassName=KFWeap_Pistol_Bladed + ProjPickupTemplate=ParticleSystem'WEP_BladedPistol_EMIT.FX_bladed_pickup_01' + AmmoPickupSound=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Projectile_Pickup' + + TouchTimeThreshhold=0.15 +} diff --git a/KFGameContent/Classes/KFProj_Bullet_HRG_Boomy.uc b/KFGameContent/Classes/KFProj_Bullet_HRG_Boomy.uc new file mode 100644 index 0000000..d38754e --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_HRG_Boomy.uc @@ -0,0 +1,79 @@ +//============================================================================= +// KFProj_Bullet_HRG_Boomy +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_HRG_Boomy extends KFProj_BallisticExplosive + hidedropdown; + +simulated protected function PrepareExplosionTemplate() +{ + super.PrepareExplosionTemplate(); + + /** Since bIgnoreInstigator is transient, its value must be defined here */ + ExplosionTemplate.bIgnoreInstigator = true; +} + +defaultproperties +{ + MaxSpeed=22500.0 + Speed=22500.0 + + DamageRadius=0 + + ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01' + // ProjFlightTemplate=ParticleSystem'WEP_HRG_Boomy_EMIT.FX_Boomy_Tracer_ZEDTime' + ProjFlightTemplateZedTime=ParticleSystem'WEP_HRG_Boomy_EMIT.FX_Boomy_Tracer_ZEDTime' + + // Grenade explosion light + Begin Object Class=PointLightComponent Name=ExplosionPointLight + LightColor=(R=252,G=218,B=171,A=255) + Brightness=0.5f + Radius=400.f + FalloffExponent=10.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=False + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE) + End Object + + // explosion + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=35 //30 + DamageRadius=150 //120 + DamageFalloffExponent=1.f + DamageDelay=0.f + MomentumTransferScale=10000 + + // Damage Effects + MyDamageType=class'KFDT_Explosive_HRG_Boomy' + KnockDownStrength=150 + FractureMeshRadius=200.0 + FracturePartVel=500.0 + + ExplosionSound=AkEvent'ww_wep_hrg_boomy.Play_WEP_HRG_Boomy_ProjExplosion' + ExplosionEffects=KFImpactEffectInfo'WEP_HRG_Boomy_ARCH.WEB_HRG_Boomy_Impacts' + + // Dynamic Light + ExploLight=ExplosionPointLight + ExploLightStartFadeOutTime=0.0 + ExploLightFadeOutTime=0.3 + + bIgnoreInstigator=true + + // Camera Shake + CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' + CamShakeInnerRadius=0 + CamShakeOuterRadius=300 + CamShakeFalloff=1.5f + bOrientCameraShakeTowardsEpicenter=true + End Object + ExplosionTemplate=ExploTemplate0 +} + diff --git a/KFGameContent/Classes/KFProj_Bullet_HRG_Energy.uc b/KFGameContent/Classes/KFProj_Bullet_HRG_Energy.uc new file mode 100644 index 0000000..86a4ff3 --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_HRG_Energy.uc @@ -0,0 +1,23 @@ +//============================================================================= +// KFProj_Bullet_HRG_Energy +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_HRG_Energy extends KFProj_Bullet + hidedropdown; + +defaultproperties +{ + MaxSpeed=15000.0 + Speed=15000.0 + + DamageRadius=0 + + ProjFlightTemplateZedTime=ParticleSystem'WEP_HRG_Energy_EMIT.FX_Energy_Tracer_Instant' + ImpactEffects = KFImpactEffectInfo'WEP_HRG_Energy_ARCH.Wep_HRG_Energy_Impact' + +} + diff --git a/KFGameContent/Classes/KFProj_Bullet_HRG_Energy_Secondary.uc b/KFGameContent/Classes/KFProj_Bullet_HRG_Energy_Secondary.uc new file mode 100644 index 0000000..25b22d7 --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_HRG_Energy_Secondary.uc @@ -0,0 +1,21 @@ +//============================================================================= +// KFProj_Bullet_HRG_Energy_Secondary +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_HRG_Energy_Secondary extends KFProj_Bullet; + +defaultproperties +{ + MaxSpeed=7500.0 + Speed=7500.0 + + DamageRadius=0 + + ProjFlightTemplateZedTime=ParticleSystem'WEP_HRG_Energy_EMIT.FX_Energy_Alt_Tracer_Instant' + ImpactEffects = KFImpactEffectInfo'WEP_HRG_Energy_ARCH.Wep_HRG_Energy_Impact_Alt' + +} diff --git a/KFGameContent/Classes/KFProj_Bullet_ParasiteImplanter.uc b/KFGameContent/Classes/KFProj_Bullet_ParasiteImplanter.uc new file mode 100644 index 0000000..ed0cdac --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_ParasiteImplanter.uc @@ -0,0 +1,22 @@ +//============================================================================= +// KFProj_Bullet_ParasiteImplanter +//============================================================================= +// Bullet class for the Parasite Implanter +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_ParasiteImplanter extends KFProj_Bullet + hidedropdown; + +defaultproperties +{ + MaxSpeed=24000.0 + Speed=24000.0 + + DamageRadius=0 + + ProjFlightTemplate=ParticleSystem'WEP_1P_L85A2_EMIT.FX_L85A2_Tracer_ZEDTime' +} + diff --git a/KFGameContent/Classes/KFProj_Bullet_ParasiteImplanterAlt.uc b/KFGameContent/Classes/KFProj_Bullet_ParasiteImplanterAlt.uc new file mode 100644 index 0000000..8a7e5db --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_ParasiteImplanterAlt.uc @@ -0,0 +1,382 @@ +//============================================================================= +// KFProj_Bullet_ParasiteImplanterAlt +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_ParasiteImplanterAlt extends KFProjectile; + +var float FuseTime; + +/** This is the effect indicator that is played for the current user **/ +var(Projectile) ParticleSystem ProjIndicatorTemplate; +var ParticleSystemComponent ProjIndicatorEffects; + +var bool IndicatorActive; +var transient bool bShouldEnableIndicator; + +var Class HumanExplosionActorClass; +var KFGameExplosion HumanExplosionTemplate; + +var private AKEvent SeedTimerOutEvent; + +simulated function TryActivateIndicator() +{ + if(!IndicatorActive && Instigator != None) + { + IndicatorActive = true; + + if(WorldInfo.NetMode == NM_Standalone || Instigator.Role == Role_AutonomousProxy || + (Instigator.Role == ROLE_Authority && WorldInfo.NetMode == NM_ListenServer && Instigator.IsLocallyControlled() )) + { + if( ProjIndicatorTemplate != None ) + { + ProjIndicatorEffects = WorldInfo.MyEmitterPool.SpawnEmitterCustomLifetime(ProjIndicatorTemplate); + } + + if(ProjIndicatorEffects != None) + { + ProjIndicatorEffects.SetAbsolute(false, false, false); + ProjIndicatorEffects.SetLODLevel(WorldInfo.bDropDetail ? 1 : 0); + ProjIndicatorEffects.bUpdateComponentInTick = true; + AttachComponent(ProjIndicatorEffects); + } + } + } +} + +/** + * Set the initial velocity and cook time + */ +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + if (Role == ROLE_Authority) + { + SetTimer(FuseTime, false, 'Timer_Detonate'); + } + + AdjustCanDisintigrate(); +} + +/** + * Explode after a certain amount of time + */ +function Timer_Detonate() +{ + Detonate(); +} + +/** Called when the owning instigator controller has left a game */ +simulated function OnInstigatorControllerLeft() +{ + if( WorldInfo.NetMode != NM_Client ) + { + SetTimer( 1.f + Rand(5) + fRand(), false, nameOf(Timer_Detonate) ); + } +} + +/** + * Trace down and get the location to spawn the explosion effects and decal + */ +simulated function GetExplodeEffectLocation(out vector HitLocation, out vector HitRotation, out Actor HitActor) +{ + local vector EffectStartTrace, EffectEndTrace; + local TraceHitInfo HitInfo; + + EffectStartTrace = Location + vect(0,0,1) * 4.f; + EffectEndTrace = EffectStartTrace - vect(0,0,1) * 32.f; + + // Find where to put the decal + HitActor = Trace(HitLocation, HitRotation, EffectEndTrace, EffectStartTrace, false,, HitInfo, TRACEFLAG_Bullet); + + // If the locations are zero (probably because this exploded in the air) set defaults + if (IsZero(HitLocation)) + { + HitLocation = Location; + } + + if (IsZero(HitRotation)) + { + HitRotation = vect(0,0,1); + } +} + +/** Used to check current status of StuckTo actor (to figure out if we should fall) */ +simulated event Tick(float DeltaTime) +{ + super.Tick(DeltaTime); + + StickHelper.Tick(DeltaTime); + + if (!IsZero(Velocity)) + { + SetRelativeRotation(rotator(Velocity)); + } + + if (bShouldEnableIndicator) + { + TryActivateIndicator(); + } +} + +simulated function Explode(vector HitLocation, vector HitNormal) +{ + StickHelper.UnPin(); + super.Explode(HitLocation, HitNormal); +} + +simulated function SyncOriginalLocation() +{ + local Actor HitActor; + local vector HitLocation, HitNormal; + local TraceHitInfo HitInfo; + + if (Role < ROLE_Authority && Instigator != none && Instigator.IsLocallyControlled()) + { + HitActor = Trace(HitLocation, HitNormal, OriginalLocation, Location,,, HitInfo, TRACEFLAG_Bullet); + if (HitActor != none) + { + StickHelper.TryStick(HitNormal, HitLocation, HitActor); + } + } +} + +simulated protected function StopSimulating() +{ + super.StopSimulating(); + + if (ProjIndicatorEffects!=None) + { + ProjIndicatorEffects.DeactivateSystem(); + } +} + +/** Causes charge to explode */ +function Detonate() +{ + local KFPawn_Monster KFPM; + //@Todo: Some VFX? + if (StuckToActor != none) + { + KFPM = KFPawn_Monster(StuckToActor); + if (KFPM != none) + { + KFPM.ParasiteSeeds.RemoveItem(self); + } + } + + StickHelper.UnPin(); + ShutDown(); +} + +simulated function OnActorSticked(Actor TargetActor) +{ + local KFPawn_Monster KFPM; + + KFPM = KFPawn_Monster(TargetActor); + if (KFPM != none) + { + if (Role == ROLE_Authority) + { + KFPM.AddParasiteSeed(self); + PlaySoundBase(SeedTimerOutEvent, false, false); + } + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_StandAlone) + { + bShouldEnableIndicator = true; + } + } +} + +simulated function ExplodeOnHuman(Vector HitLocation, Vector HitNormal, Actor HitActor) +{ + local vector NudgedHitLocation, ExplosionDirection; + + if (HumanExplosionTemplate != None) + { + StopSimulating(); + + // using a hit location slightly away from the impact point is nice for certain things + NudgedHitLocation = HitLocation + (HitNormal * 32.f); + + ExplosionActor = Spawn(HumanExplosionActorClass, self,, NudgedHitLocation, rotator(HitNormal)); + if (ExplosionActor != None) + { + ExplosionActor.Instigator = Instigator; + ExplosionActor.InstigatorController = InstigatorController; + + PrepareExplosionTemplate(); + + // If the locations are zero (probably because this exploded in the air) set defaults + if( IsZero(HitLocation) ) + { + HitLocation = Location; + } + + if( IsZero(HitNormal) ) + { + HitNormal = vect(0,0,1); + } + + // these are needed for the decal tracing later in GameExplosionActor.Explode() + HumanExplosionTemplate.HitLocation = HitLocation;// NudgedHitLocation + HumanExplosionTemplate.HitNormal = HitNormal; + + // If desired, attach to mover if we hit one + if(bAttachExplosionToHitMover && InterpActor(HitActor) != None) + { + ExplosionActor.Attachee = HitActor; + HumanExplosionTemplate.bAttachExplosionEmitterToAttachee = TRUE; + ExplosionActor.SetBase(HitActor); + } + + // directional? + if (HumanExplosionTemplate.bDirectionalExplosion) + { + ExplosionDirection = GetExplosionDirection(HitNormal); + //DrawDebugLine(ExplosionActor.Location, ExplosionActor.Location+ExplosionDirection*64, 255, 255, 0, TRUE); + } + + // @todo: make this function responsible for setting explosion instance parameters, and take instance parameters + // out of GearExplosion (e.g. Attachee) + PrepareExplosionActor(ExplosionActor); + + ExplosionActor.Explode(HumanExplosionTemplate, ExplosionDirection); // go bewm + } + + // done with it + if (!bPendingDelete && !bDeleteMe) + { + // defer destruction so any replication of explosion stuff can happen if necessary + DeferredDestroy(PostExplosionLifetime); + } + } +} + + +simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal) +{ + if (KFPawn_Human(Other) != none && Other != Instigator) + { + ExplodeOnHuman(HitLocation, HitNormal, Other); + } + else + { + super.ProcessTouch(Other, HitLocation, HitNormal); + } +} + +defaultproperties +{ + ProjFlightTemplate=ParticleSystem'WEP_ParasiteImplanter_EMIT.FX_Seed_Projectile' + ProjIndicatorTemplate=ParticleSystem'WEP_ParasiteImplanter_EMIT.FX_Seed_Projectile_Indicator' + + bWarnAIWhenFired=true + + MaxSpeed=15000.0 //10000.0 //4000.0 + Speed=15000.0 //10000.0 //4000.0 + TerminalVelocity=15000.0 //10000.0 //4000.0 + + Physics=PHYS_Falling + + TossZ=0 //150 + GravityScale=0 //0.01 //0.7 + + GlassShatterType=FMGS_ShatterAll + + bCollideComplex=true + + bIgnoreFoliageTouch=true + + bBlockedByInstigator=false + bAlwaysReplicateExplosion=true + + FuseTime=6.0 //4.0 + + bNetTemporary=false + NetPriority=5 + NetUpdateFrequency=200 + + bNoReplicationToInstigator=false + bUseClientSideHitDetection=true + bUpdateSimulatedPosition=true + bSyncToOriginalLocation=true + bSyncToThirdPersonMuzzleLocation=true + + PinBoneIdx=INDEX_None + + bCanBeDamaged=true + bCanDisintegrate=true + ExplosionActorClass=class'KFExplosion_ParasiteSeed' + HumanExplosionActorClass=class'KFExplosion_ParasiteSeedHuman' + + // explosion + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=400 //250 + DamageRadius=450 + DamageFalloffExponent=0.5f //1.f + DamageDelay=0.f + MyDamageType=class'KFDT_Toxic_ParasiteSeedExplosion' + + // Damage Effects + KnockDownStrength=0 + KnockDownRadius=0 + FractureMeshRadius=0 + FracturePartVel=0 + // ExplosionEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Explosions.MedicGrenade_Explosion' + ExplosionSound=AkEvent'WW_WEP_Medic_GrenadeLauncher.Play_WEP_Medic_GrenadeLauncher_Grenade_Explosion' + MomentumTransferScale=0 + + // Camera Shake + CamShake=none + CamShakeInnerRadius=0 + CamShakeOuterRadius=0 + CamShakeFalloff=1.5f + bOrientCameraShakeTowardsEpicenter=true + End Object + ExplosionTemplate=ExploTemplate0 + + Begin Object Class=KFGameExplosion Name=ExploTemplate1 + Damage=1 //250 + DamageRadius=100 //450 + DamageFalloffExponent=0.5f //1.f + DamageDelay=0.f + + // Damage Effects + KnockDownStrength=0 + KnockDownRadius=0 + FractureMeshRadius=0 + FracturePartVel=0 + // ExplosionEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Explosions.MedicGrenade_Explosion' + ExplosionSound=AkEvent'WW_WEP_Medic_GrenadeLauncher.Play_WEP_Medic_GrenadeLauncher_Grenade_Explosion' + MomentumTransferScale=0 + + // Camera Shake + CamShake=none + CamShakeInnerRadius=0 + CamShakeOuterRadius=0 + CamShakeFalloff=1.5f + bOrientCameraShakeTowardsEpicenter=true + End Object + HumanExplosionTemplate=ExploTemplate1 + + bCanStick=true + bCanPin=true + + Begin Object Class=KFProjectileStickHelper_ParasiteImplanter Name=StickHelper0 + End Object + StickHelper=StickHelper0 + + ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01' + + bShouldEnableIndicator=false; + + ImpactEffects = KFImpactEffectInfo'WEP_ParasiteImplanter_ARCH.Wep_Parasite_Impact_Alt' + + SeedTimerOutEvent = AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_Seed_Timer_Out'; +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFProj_Mine_Reconstructor.uc b/KFGameContent/Classes/KFProj_Mine_Reconstructor.uc index 439978e..91929a1 100644 --- a/KFGameContent/Classes/KFProj_Mine_Reconstructor.uc +++ b/KFGameContent/Classes/KFProj_Mine_Reconstructor.uc @@ -905,8 +905,8 @@ defaultproperties MaxDamageRadiusPerPercentage=340 MinDamageRadiusPerPercentage=160 - MaxDamagePerPercentage=300 //270//180 - MinDamagePerPercentage=30//90 + MaxDamagePerPercentage=350 //300 + MinDamagePerPercentage=35 //30 MaxCollisionRadius=20 MinCollisionRadius=10 diff --git a/KFGameContent/Classes/KFProj_Rocket_ThermiteBore.uc b/KFGameContent/Classes/KFProj_Rocket_ThermiteBore.uc index b3c6a30..cd86383 100644 --- a/KFGameContent/Classes/KFProj_Rocket_ThermiteBore.uc +++ b/KFGameContent/Classes/KFProj_Rocket_ThermiteBore.uc @@ -249,6 +249,29 @@ function vector CalculateResidualFlameVelocity( vector HitNormal, vector HitVelD return SpawnDir * ResidualFlameForceMultiplier; } +simulated protected function PrepareExplosionTemplate() +{ + local Weapon OwnerWeapon; + local Pawn OwnerPawn; + local KFPerk_Survivalist Perk; + + super(KFProjectile).PrepareExplosionTemplate(); + OwnerWeapon = Weapon(Owner); + if (OwnerWeapon != none) + { + OwnerPawn = Pawn(OwnerWeapon.Owner); + if (OwnerPawn != none) + { + Perk = KFPerk_Survivalist(KFPawn(OwnerPawn).GetPerk()); + if (Perk != none) + { + ExplosionTemplate.DamageRadius *= Perk.GetAoERadiusModifier(); + } + } + } +} + + defaultproperties { ProjFlightTemplate=ParticleSystem'WEP_Thermite_EMIT.FX_Harpoon_Projectile' diff --git a/KFGameContent/Classes/KFProjectileStickHelper_ParasiteImplanter.uc b/KFGameContent/Classes/KFProjectileStickHelper_ParasiteImplanter.uc new file mode 100644 index 0000000..33cb0f9 --- /dev/null +++ b/KFGameContent/Classes/KFProjectileStickHelper_ParasiteImplanter.uc @@ -0,0 +1,205 @@ +//============================================================================= +// KFProjectileStickHelper_ParasiteImplanter +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFProjectileStickHelper_ParasiteImplanter extends KFProjectileStickHelper; + +simulated function TryStick(vector HitNormal, optional vector HitLocation, optional Actor HitActor) +{ + if( WorldInfo.NetMode != NM_DedicatedServer ) + { + `ImpactEffectManager.PlayImpactEffects(HitLocation, Instigator,, KFProj_Bullet_ParasiteImplanterAlt(Outer).ImpactEffects); + } + + super.TryStick(HitNormal, HitLocation, HitActor); +} + +/** Stops movement of projectile and calculates orientation to surface */ +simulated function Stick(Actor HitActor, vector HitLocation, vector HitNormal, const out TraceHitInfo HitInfo) +{ + local int BoneIdx; + + local KFPawn_Monster HitMonster; + local array HitZoneImpactList; + local vector StartTrace, EndTrace, Direction, ClosestBoneLocation; + local name BoneName; + + BoneName = HitInfo.BoneName; + + HitMonster = KFPawn_Monster(HitActor); + if (HitMonster != none) + { + // get injury hit zone + StartTrace = HitLocation; + Direction = Normal(Velocity); + EndTrace = StartTrace + Direction * (HitMonster.CylinderComponent.CollisionRadius * 6.0); + TraceProjHitZones(HitMonster, EndTrace, StartTrace, HitZoneImpactList); + + if (BoneName == '') + { + // get the best bone to attach to + ClosestBoneLocation = HitMonster.Mesh.GetClosestCollidingBoneLocation(HitLocation, true, false); + BoneName = HitMonster.Mesh.FindClosestBone(ClosestBoneLocation, ClosestBoneLocation); + } + + // Deleted damage to monster code, we don't want to do damage on sticking + // Bringed damage back + if (KFWeapon(Owner) != none) + { + HitZoneImpactList[0].RayDir = Normal(EndTrace - StartTrace); // add a raydir here since TraceProjHitZones doesn't fill this out (so certain afflictions apply) + KFWeapon(Owner).HandleProjectileImpact(WeaponFireMode, HitZoneImpactList[0], PenetrationPower); + } + } + else if (KFPawn_Human(HitActor) != none) + { + // When it hits a human, use explosion. + KFProj_Bullet_ParasiteImplanterAlt(Outer).ExplodeOnHuman(HitLocation, HitNormal, HitActor); + return; + } + + if (!IsZero(HitLocation)) + { + + SetLocation(HitLocation); + } + + SetStickOrientation(HitNormal); + + BoneIdx = INDEX_NONE; + + if (BoneName != '') + { + BoneIdx = GetBoneIndexFromActor(HitActor, BoneName); + } + + StickToActor(HitActor, HitInfo.HitComponent, BoneIdx, true); + + if (Role < ROLE_Authority) + { + Outer.ServerStick(HitActor, BoneIdx, StuckToLocation, StuckToRotation); + } + + if (WorldInfo.NetMode != NM_DedicatedServer && StickAkEvent != none) + { + PlaySoundBase(StickAkEvent); + } +} + +/** Changes the base of the charge to the stick actor and sets its relative loc/rot */ +simulated function StickToActor(Actor StickTo, PrimitiveComponent HitComp, int BoneIdx, optional bool bCalculateRelativeLocRot) +{ + local SkeletalMeshComponent SkelMeshComp; + local Name BoneName; + + local vector RelStuckToLocation; + local rotator RelStuckToRotation; + + local KFPawn StickToPawn; + local KFProj_Bullet_ParasiteImplanterAlt ParasiteProj; + + StickToPawn = KFPawn(StickTo); + + if (bCanPin && (StickToPawn == none || StickToPawn.bCanBePinned)) + { + // if StickTo pawn is dead, pin it and keep flying + if (Role == ROLE_Authority) + { + if (StickToPawn != none && !StickToPawn.IsAliveAndWell()) + { + if (PinPawn == none) + { + Pin(StickTo, BoneIdx); + } + + return; + } + } + + if (WorldInfo.NetMode != NM_DedicatedServer && PinPawn != none) + { + if (StickToPawn == none) + { + // Pin pinned pawn to StickTo actor + //PinPawn.Mesh.RetardRBLinearVelocity(vector(Rotation), 0.75); + PinPawn.Mesh.SetRBPosition(Location, PinBoneName); + + PinConstraint = Spawn(class'RB_ConstraintActorSpawnable',,,Location); + PinConstraint.InitConstraint(PinPawn, none, PinBoneName, ''); + } + + PinPawn = none; + } + } + else if (StickToPawn != none && !StickToPawn.IsAliveAndWell()) + { + return; + } + + SetPhysics(PHYS_None); + + PrevStuckToActor = StuckToActor; + StuckToActor = StickTo; + StuckToBoneIdx = BoneIdx; + + // if we found a skel mesh, set our base to it and set relative loc/rot + if (BoneIdx != INDEX_NONE) + { + SkelMeshComp = SkeletalMeshComponent(HitComp); + + BoneName = SkelMeshComp.GetBoneName(BoneIdx); + + if (bCalculateRelativeLocRot) + { + StuckToLocation = Location; + StuckToRotation = Rotation; + } + + SkelMeshComp.TransformToBoneSpace(BoneName, StuckToLocation, StuckToRotation, RelStuckToLocation, RelStuckToRotation); + + SetBase(StickTo,, SkelMeshComp, BoneName); + SetRelativeLocation(RelStuckToLocation); + SetRelativeRotation(RelStuckToRotation); + + } + // otherwise, just set our base + else + { + if (bCalculateRelativeLocRot) + { + // set replicated loc/rot + StuckToLocation = Location; + StuckToRotation = Rotation; + } + else + { + // set loc/rot to replicated loc/rot + SetLocation(StuckToLocation); + SetRotation(StuckToRotation); + } + + SetBase(StickTo); + } + + ParasiteProj = KFProj_Bullet_ParasiteImplanterAlt(Outer); + ParasiteProj.OnActorSticked(StickTo); +} + +/** Resets physics/collision vars to defaults */ +simulated function UnStick() +{ +// local KFPawn_Monster KFPM; + + GravityScale=0.5f; +/* + KFPM = KFPawn_Monster(StuckToActor); + if (KFPM != none && KFPM.Health > 0) + { + KFPM.ParasiteSeeds.Remove(0, KFPM.ParasiteSeeds.Length - (KFPM.MaxNumSeeds-1)); + } +*/ + super.UnStick(); +} diff --git a/KFGameContent/Classes/KFSeasonalEventStats_Fall2021.uc b/KFGameContent/Classes/KFSeasonalEventStats_Fall2021.uc new file mode 100644 index 0000000..09d7397 --- /dev/null +++ b/KFGameContent/Classes/KFSeasonalEventStats_Fall2021.uc @@ -0,0 +1,117 @@ +//============================================================================= +// KFSeasonalEventStats_Fall2021 +//============================================================================= +// Tracks event-specific challenges/accomplishments for Fall 2021 +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= +class KFSeasonalEventStats_Fall2021 extends KFSeasonalEventStats; + +var transient private const int BossKillsRequired, EndlessWaveRequired; + +private event Initialize(string MapName) +{ + local string CapsMapName; + CapsMapName = Caps(MapName); + + bObjectiveIsValidForMap[0] = 1; // Kill 15 Bosses on any map or mode + bObjectiveIsValidForMap[1] = 0; // Complete the Weekly on Netherhold + bObjectiveIsValidForMap[2] = 0; // Find the nether heart + bObjectiveIsValidForMap[3] = 0; // Unlock the chapel and the dining hall doors + bObjectiveIsValidForMap[4] = 0; // Complete wave 15 on Endless Hard or higher difficulty on Netherhold + + if (CapsMapName == "KF-NETHERHOLD") + { + bObjectiveIsValidForMap[1] = 1; + bObjectiveIsValidForMap[2] = 1; + bObjectiveIsValidForMap[3] = 1; + bObjectiveIsValidForMap[4] = 1; + } + + SetSeasonalEventStatsMax(BossKillsRequired, 0, 0, 0, 0); +} + +private event GrantEventItems() +{ + if (Outer.IsEventObjectiveComplete(0) && + Outer.IsEventObjectiveComplete(1) && + Outer.IsEventObjectiveComplete(2) && + Outer.IsEventObjectiveComplete(3) && + Outer.IsEventObjectiveComplete(4)) + { + // TODO: Uncomment me and set the proper item ID + GrantEventItem(8990); + } +} + +// Kill 15 Bosses on any map or mode +simulated function OnBossDied() +{ + local int ObjIdx; + ObjIdx = 0; + + // Boss kills in any map + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + IncrementSeasonalEventStat(ObjIdx, 1); + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= BossKillsRequired) + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } +} + +// Complete the Weekly on Netherhold +simulated event OnGameWon(class GameClass, int Difficulty, int GameLength, bool bCoOp) +{ + local int ObjIdx; + ObjIdx = 1; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (GameClass == class'KFGameInfo_WeeklySurvival') + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } +} + +// Complete wave 15 on Endless Hard or higher difficulty on Netherhold +simulated event OnWaveCompleted(class GameClass, int Difficulty, int WaveNum) +{ + local int ObjIdx; + ObjIdx = 4; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (WaveNum >= EndlessWaveRequired && GameClass == class'KFGameInfo_Endless' && Difficulty >= `DIFFICULTY_HARD) + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } +} + +simulated function OnTryCompleteObjective(int ObjectiveIndex, int EventIndex) +{ + local int NetherHeartIdx, ChapelIdx; + NetherHeartIdx = 2; + ChapelIdx = 3; + + if(EventIndex == SEI_Fall) + { + if (ObjectiveIndex == NetherHeartIdx || ObjectiveIndex == ChapelIdx) + { + if (bObjectiveIsValidForMap[ObjectiveIndex] != 0) + { + FinishedObjective(SEI_Fall, ObjectiveIndex); + } + } + } +} + +defaultproperties +{ + BossKillsRequired=15 + EndlessWaveRequired=15 +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_FAMAS.uc b/KFGameContent/Classes/KFWeapAttach_FAMAS.uc index 46e5fd0..6932b2c 100644 --- a/KFGameContent/Classes/KFWeapAttach_FAMAS.uc +++ b/KFGameContent/Classes/KFWeapAttach_FAMAS.uc @@ -9,18 +9,21 @@ class KFWeapAttach_FAMAS extends KFWeaponAttachment; -const SecondaryFireAnim = 'Shoot_Secondary'; -const SecondaryFireIronAnim = 'Shoot_Secondary_Iron'; -const SecondaryFireAnimLast = 'Shoot_Secondary_Last'; -const SecondaryFireIronAnimLast = 'Shoot_Secondary_Iron_Last'; -const SecondaryFireBodyAnim = 'ADD_Shoot_Secondary'; -const SecondaryFireBodyAnimCH = 'ADD_Shoot_Secondary_CH'; -const SecondaryFireBodyAnimIron = 'ADD_Shoot_Secondary_Iron'; -const SecondaryReloadAnimEmpty = 'Reload_Secondary_Empty'; -const SecondaryReloadAnimHalf = 'Reload_Secondary_Half'; -const SecondaryReloadAnimEliteEmpty = 'Reload_Secondary_Elite_Empty'; -const SecondaryReloadAnimEliteHalf = 'Reload_Secondary_Elite_Half'; -const ShotgunMuzzleSocket = 'ShotgunMuzzleFlash'; +const SecondaryFireAnim = 'Shoot_Secondary'; +const SecondaryFireAnimCrouch = 'Shoot_Secondary_CH'; +const SecondaryFireIronAnim = 'Shoot_Secondary_Iron'; +const SecondaryFireBodyAnim = 'ADD_Shoot_Secondary'; +const SecondaryFireBodyAnimCH = 'ADD_Shoot_Secondary_CH'; +const SecondaryFireBodyAnimIron = 'ADD_Shoot_Secondary_Iron'; +const SecondaryReloadAnimEmpty = 'Reload_Secondary_Empty'; +const SecondaryReloadAnimEmptyCrouch = 'Reload_Secondary_Empty_CH'; +const SecondaryReloadAnimHalf = 'Reload_Secondary_Half'; +const SecondaryReloadAnimHalfCrouch = 'Reload_Secondary_Half_CH'; +const SecondaryReloadAnimEliteEmpty = 'Reload_Secondary_Elite_Empty'; +const SecondaryReloadAnimEliteEmptyCrouch = 'Reload_Secondary_Elite_Empty_CH'; +const SecondaryReloadAnimEliteHalf = 'Reload_Secondary_Elite_Half'; +const SecondaryReloadAnimEliteHalfCrouch = 'Reload_Secondary_Elite_Half_CH'; +const ShotgunMuzzleSocket = 'ShotgunMuzzleFlash'; var protected transient KFMuzzleFlash ShotgunMuzzleFlash; @@ -30,18 +33,25 @@ simulated function PlayReloadMagazineAnim(EWeaponState NewWeaponState, KFPawn P) { local name AnimName; - if(NewWeaponState == WEP_ReloadSecondary || NewWeaponState == WEP_ReloadSecondary_Elite) + switch (NewWeaponState) { - switch (NewWeaponState) - { - case WEP_ReloadSecondary: - AnimName = (P.MyKFWeapon.AmmoCount[1] == 0) ? SecondaryReloadAnimEmpty : SecondaryReloadAnimHalf; - break; - case WEP_ReloadSecondary_Elite: - AnimName = (P.MyKFWeapon.AmmoCount[1] == 0) ? SecondaryReloadAnimEliteEmpty : SecondaryReloadAnimEliteHalf; - break; - } + case WEP_ReloadSecondary: + AnimName = (P.bIsCrouched) ? SecondaryReloadAnimHalfCrouch : SecondaryReloadAnimHalf; + break; + case WEP_ReloadSecondaryEmpty: + AnimName = (P.bIsCrouched) ? SecondaryReloadAnimEmptyCrouch : SecondaryReloadAnimEmpty; + break; + case WEP_ReloadSecondary_Elite: + AnimName = (P.bIsCrouched) ? SecondaryReloadAnimEliteHalfCrouch : SecondaryReloadAnimEliteHalf; + break; + case WEP_ReloadSecondaryEmpty_Elite: + AnimName = (P.bIsCrouched) ? SecondaryReloadAnimEliteEmptyCrouch : SecondaryReloadAnimEliteEmpty; + break; + } + + if (AnimName != '') + { PlayCharacterMeshAnim(P, AnimName, true); } else @@ -103,21 +113,19 @@ simulated function PlayFireAnim(KFPawn P) } else if (OwnerPawn.FiringMode == 1) // ALT FIRE MODE (Shotgun) { - // Anim = (P.MyKFWeapon.AmmoCount[1] == 0) ? SecondaryFireIronAnimLast : SecondaryFireIronAnim; Anim = SecondaryFireIronAnim; } } else // Normal anims { - if (Pawn(Owner).FiringMode == 0) // DEFAULT FIRE MODE (Rifle) + if (OwnerPawn.FiringMode == 0) // DEFAULT FIRE MODE (Rifle) { Anim = WeaponFireAnim; } - else if (Pawn(Owner).FiringMode == 1) // ALT FIRE MODE (Shotgun) + else if (OwnerPawn.FiringMode == 1) // ALT FIRE MODE (Shotgun) { - // Anim = (P.MyKFWeapon.AmmoCount[1] == 0) ? SecondaryFireAnimLast : SecondaryFireAnim; - Anim = SecondaryFireAnim; + Anim = OwnerPawn.bIsCrouched ? SecondaryFireAnimCrouch : SecondaryFireAnim; } } diff --git a/KFGameContent/Classes/KFWeapAttach_HRG_Energy.uc b/KFGameContent/Classes/KFWeapAttach_HRG_Energy.uc new file mode 100644 index 0000000..4be69d7 --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_HRG_Energy.uc @@ -0,0 +1,106 @@ +//============================================================================= +// KFWeapAttach_HRG_Energy +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFWeapAttach_HRG_Energy extends KFWeaponAttachment; + +`define HRG_ENERGY_MIC_LED_INDEX_1 0 +`define HRG_ENERGY_MIC_SCREEN_INDEX 1 +`define HRG_ENERGY_MIC_LED_INDEX_2 2 + +var MaterialInstanceConstant WeaponMIC_2; +var MaterialInstanceConstant WeaponMICScreen; + +simulated function SetWeaponAltFireMode (bool bUsingAltFireMode) +{ + super.SetWeaponAltFireMode(bUsingAltFireMode); + UpdateMaterial(bUsingAltFireMode ? 1 : 0); +} + +simulated function UpdateMaterial(byte FireMode) +{ + local LinearColor MatColor; + + if (WeapMesh == none) + { + return; + } + + MatColor = FireMode == 0 ? class'KFWeap_HRG_Energy'.default.DefaultFireMaterialColor : class'KFWeap_HRG_Energy'.default.AltFireMaterialColor; + + if ( WeaponMIC == none ) + { + WeaponMIC = WeapMesh.CreateAndSetMaterialInstanceConstant(`HRG_ENERGY_MIC_LED_INDEX_1); + } + + if ( WeaponMIC_2 == none ) + { + WeaponMIC_2 = WeapMesh.CreateAndSetMaterialInstanceConstant(`HRG_ENERGY_MIC_LED_INDEX_2); + } + + if (WeaponMICScreen == none) + { + WeaponMICScreen = WeapMesh.CreateAndSetMaterialInstanceConstant(`HRG_ENERGY_MIC_SCREEN_INDEX); + } + + WeaponMIC.SetVectorParameterValue('Vector_GlowColor', MatColor); + WeaponMIC_2.SetVectorParameterValue('Vector_GlowColor', MatColor); + WeaponMICScreen.SetVectorParameterValue('Color_override', MatColor); +} + +/** Plays fire animation on weapon mesh */ +simulated function PlayWeaponFireAnim() +{ + local float Duration, AnimRateModifier; + local name Animation; + local KFPawn_Human KFPH; + + KFPH = KFPawn_Human(Instigator); + + AnimRateModifier = (KFPH != none && KFPH.bUsingAltFireMode) ? class'KFWeap_HRG_Energy'.default.SecondaryFireAnimRateModifier : 1.0f; + + if ( Instigator.bIsWalking ) + { + Duration = WeapMesh.GetAnimLength( WeaponIronFireAnim ); + Animation = WeaponIronFireAnim; + } + else + { + Duration = WeapMesh.GetAnimLength( WeaponFireAnim ); + Animation = WeaponFireAnim; + WeapMesh.PlayAnim( WeaponFireAnim, Duration / ThirdPersonAnimRate,, true ); + } + + WeapMesh.PlayAnim( Animation, Duration / ThirdPersonAnimRate * AnimRateModifier,, true ); +} + + +/** Play a 3rd person reload animation */ +simulated function PlayReloadMagazineAnim(EWeaponState NewWeaponState, KFPawn P) +{ + local name AnimName; + + switch (NewWeaponState) + { + case WEP_Reload: + case WEP_ReloadEmpty: + AnimName = (!P.bIsCrouched) ? ReloadHalfAnim : CH_ReloadHalfAnim; + break; + case WEP_Reload_Elite: + case WEP_ReloadEmpty_Elite: + AnimName = (!P.bIsCrouched) ? ReloadHalfEliteAnim : CH_ReloadHalfEliteAnim; + break; + } + + PlayCharacterMeshAnim(P, AnimName, true); +} + +defaultproperties +{ + +} diff --git a/KFGameContent/Classes/KFWeapAttach_ParasiteImplanter.uc b/KFGameContent/Classes/KFWeapAttach_ParasiteImplanter.uc new file mode 100644 index 0000000..82502b4 --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_ParasiteImplanter.uc @@ -0,0 +1,108 @@ +//============================================================================= +// KFWeapAttach_FAMAS +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFWeapAttach_ParasiteImplanter extends KFWeaponAttachment; + +`define PARASITEIMPLANTER_MIC_INDEX 0 + +const SecondaryFireAnim = 'Shoot_Secondary'; +const SecondaryFireAnimCrouch = 'Shoot_Secondary_CH'; +const SecondaryFireIronAnim = 'Shoot_Secondary_Iron'; +const SecondaryFireBodyAnim = 'ADD_Shoot_Secondary'; +const SecondaryFireBodyAnimCH = 'ADD_Shoot_Secondary_CH'; +const SecondaryFireBodyAnimIron = 'ADD_Shoot_Secondary_Iron'; + +/** Material colors applied to different fire modes */ +var LinearColor NoAmmoMaterialColor; +var LinearColor AmmoReadyMaterialColor; + +/** Plays fire animation on weapon mesh */ +simulated function PlayWeaponFireAnim() +{ + local float Duration; + local name Anim; + + if ( Instigator.bIsWalking ) + { + if (Instigator.FiringMode == 0) // DEFAULT FIRE MODE + { + Anim = WeaponIronFireAnim; + } + else if (Instigator.FiringMode == 1) + { + Anim = SecondaryFireIronAnim; + } + } + else + { + if (Instigator.FiringMode == 0) // ALT FIRE MODE + { + Anim = WeaponFireAnim; + } + else if (Instigator.FiringMode == 1) + { + Anim = Instigator.bIsCrouched ? SecondaryFireAnimCrouch : SecondaryFireAnim; + } + } + + Duration = WeapMesh.GetAnimLength( Anim ); + WeapMesh.PlayAnim( Anim, Duration / ThirdPersonAnimRate,, true ); +} + +/** Plays fire animation on pawn */ +simulated function PlayPawnFireAnim( KFPawn P, EAnimSlotStance AnimType ) +{ + if (P.FiringMode == 0) + { + super.PlayPawnFireAnim(P, AnimType); + } + else if (P.FiringMode == 1) + { + if ( P.bIsCrouched ) + { + P.PlayBodyAnim(SecondaryFireBodyAnimCH, AnimType, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + else if ( P.bIsWalking ) + { + P.PlayBodyAnim(SecondaryFireBodyAnimIron, AnimType, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + else + { + P.PlayBodyAnim(SecondaryFireBodyAnim, AnimType, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + } +} + +/** Special event added for weap attachments. Free for use */ +function OnSpecialEvent(int Arg) +{ + if (Arg <= 2) + { + UpdateMaterial(Arg == 2); + } +} + +simulated function UpdateMaterial(bool HasEnoughAmmo) +{ + local LinearColor Value; + + Value = HasEnoughAmmo ? AmmoReadyMaterialColor : NoAmmoMaterialColor; + if ( WeaponMIC == None && WeapMesh != None ) + { + WeaponMIC = WeapMesh.CreateAndSetMaterialInstanceConstant(`PARASITEIMPLANTER_MIC_INDEX); + } + + WeaponMIC.SetVectorParameterValue('Vector_GlowColor', Value); +} + +defaultproperties +{ + NoAmmoMaterialColor=(R=0.0f,G=0.0f,B=0.0f) + AmmoReadyMaterialColor=(R=0.08f,G=1.0f,B=0.08f) +} diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc index a78ad3b..4422399 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc @@ -387,7 +387,14 @@ simulated state AltReloading extends Reloading Perk = GetPerk(); bTacticalReload = (Perk != None && Perk.GetUsingTactialReload(self)); - return (bTacticalReload ? WEP_ReloadSecondary_Elite : WEP_ReloadSecondary); + if (AmmoCount[ALTFIRE_FIREMODE] == 0) + { + return (bTacticalReload ? WEP_ReloadSecondaryEmpty_Elite : WEP_ReloadSecondaryEmpty); + } + else + { + return (bTacticalReload ? WEP_ReloadSecondary_Elite : WEP_ReloadSecondary); + } } simulated event BeginState(Name PreviousStateName) diff --git a/KFGameContent/Classes/KFWeap_Blunt_MedicBat.uc b/KFGameContent/Classes/KFWeap_Blunt_MedicBat.uc index e09daa2..03ac3e6 100644 --- a/KFGameContent/Classes/KFWeap_Blunt_MedicBat.uc +++ b/KFGameContent/Classes/KFWeap_Blunt_MedicBat.uc @@ -661,8 +661,8 @@ defaultproperties HealFullRechargeSeconds=12 // Heavy Attack Explosion Ammo - MagazineCapacity[0]=3 - SpareAmmoCapacity[0]=12 + MagazineCapacity[0]=2 //3 + SpareAmmoCapacity[0]=10 //12 InitialSpareMags[0]=1 bCanBeReloaded=true bReloadFromMagazine=true @@ -687,7 +687,7 @@ defaultproperties // Heavy Attack FireModeIconPaths(HEAVY_ATK_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BluntMelee' - InstantHitDamage(HEAVY_ATK_FIREMODE)=130 + InstantHitDamage(HEAVY_ATK_FIREMODE)=140 //130 InstantHitDamageTypes(HEAVY_ATK_FIREMODE) = class'KFDT_Bludgeon_MedicBatHeavy' // Heavy Attack Explosion @@ -729,12 +729,12 @@ defaultproperties // Explosion ExplosionActorClass = class'KFExplosionActorReplicated' Begin Object Class=KFGameExplosion Name=HeavyAttackHealingExplosion - Damage=200 + Damage=225 //200 DamageRadius=500 DamageFalloffExponent=0.f DamageDelay=0.f MyDamageType=class'KFDT_Toxic_MedicBatGas' - HealingAmount=30 + HealingAmount=20 //30 // Damage Effects KnockDownStrength=0 diff --git a/KFGameContent/Classes/KFWeap_HRG_BlastBrawlers.uc b/KFGameContent/Classes/KFWeap_HRG_BlastBrawlers.uc index fd301f7..4ce330a 100644 --- a/KFGameContent/Classes/KFWeap_HRG_BlastBrawlers.uc +++ b/KFGameContent/Classes/KFWeap_HRG_BlastBrawlers.uc @@ -373,7 +373,21 @@ simulated function float GetReloadRateScale() simulated function bool HasAnyAmmo() { - return AmmoCount[0] != 0 && SpareAmmoCount[0] != 0; + return AmmoCount[0] > 0 || SpareAmmoCount[0] > 0; +} + +simulated function int GetMeleeDamage(byte FireModeNum, optional vector RayDir) +{ + local int Damage; + + Damage = GetModifiedDamage(FireModeNum, RayDir); + // decode damage scale (see GetDamageScaleByAngle) from the RayDir + if ( !IsZero(RayDir) ) + { + Damage = Round(float(Damage) * FMin(VSize(RayDir), 1.f)); + } + + return Damage; } defaultproperties diff --git a/KFGameContent/Classes/KFWeap_HRG_Boomy.uc b/KFGameContent/Classes/KFWeap_HRG_Boomy.uc new file mode 100644 index 0000000..2792ffc --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_Boomy.uc @@ -0,0 +1,192 @@ +//============================================================================= +// KFWeap_HRG_Boomy +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HRG_Boomy extends KFWeap_SMGBase; + +/** + * Overriden to use instant hit vfx. + * Basically, calculate the hit location so vfx can play + */ + simulated function Projectile ProjectileFire() +{ + local vector StartTrace, EndTrace, RealStartLoc, AimDir; + local ImpactInfo TestImpact; + local vector DirA, DirB; + local Quat Q; + local class MyProjectileClass; + + MyProjectileClass = GetKFProjectileClass(); + + // This is where we would start an instant trace. (what CalcWeaponFire uses) + StartTrace = GetSafeStartTraceLocation(); + AimDir = Vector(GetAdjustedAim( StartTrace )); + + // this is the location where the projectile is spawned. + RealStartLoc = GetPhysicalFireStartLoc(AimDir); + + // if projectile is spawned at different location of crosshair, + // then simulate an instant trace where crosshair is aiming at, Get hit info. + EndTrace = StartTrace + AimDir * GetTraceRange(); + TestImpact = CalcWeaponFire( StartTrace, EndTrace ); + + // Set flash location to trigger client side effects. Bypass Weapon.SetFlashLocation since + // that function is not marked as simulated and we want instant client feedback. + // ProjectileFire/IncrementFlashCount has the right idea: + // 1) Call IncrementFlashCount on Server & Local + // 2) Replicate FlashCount if ( !bNetOwner ) + // 3) Call WeaponFired() once on local player + if( Instigator != None ) + { + Instigator.SetFlashLocation( Self, CurrentFireMode, TestImpact.HitLocation ); + } + + if( Role == ROLE_Authority || (MyProjectileClass.default.bUseClientSideHitDetection + && MyProjectileClass.default.bNoReplicationToInstigator && Instigator != none + && Instigator.IsLocallyControlled()) ) + { + + if( StartTrace != RealStartLoc ) + { + // Store the original aim direction without correction + DirB = AimDir; + + // Then we realign projectile aim direction to match where the crosshair did hit. + AimDir = Normal(TestImpact.HitLocation - RealStartLoc); + + // Store the desired corrected aim direction + DirA = AimDir; + + // Clamp the maximum aim adjustment for the AimDir so you don't get wierd + // cases where the projectiles velocity is going WAY off of where you + // are aiming. This can happen if you are really close to what you are + // shooting - Ramm + if ( (DirA dot DirB) < MaxAimAdjust_Cos ) + { + Q = QuatFromAxisAndAngle(Normal(DirB cross DirA), MaxAimAdjust_Angle); + AimDir = QuatRotateVector(Q,DirB); + } + } + + return SpawnAllProjectiles(MyProjectileClass, RealStartLoc, AimDir); + } + + return None; +} + + +defaultproperties +{ + bHasFireLastAnims=true + BonesToLockOnEmpty=(RW_Charging_Handle) + + // Shooting Animations + FireSightedAnims[0]=Shoot_Iron + FireSightedAnims[1]=Shoot_Iron2 + FireSightedAnims[2]=Shoot_Iron3 + + // FOV + MeshFOV=75 + MeshIronSightFOV=52 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=85 + DOF_FG_MaxNearBlurSize=2.5 + + // Content + PackageKey="HRG_Boomy" + FirstPersonMeshName="WEP_1P_HRG_Boomy_MESH.Wep_1stP_HRG_Boomy_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_HRG_Boomy_ANIM.WEP_1stP_HRG_Boomy_Anim" + PickupMeshName="WEP_3P_HRG_Boomy_MESH.Wep_3rdP_HRG_Boomy_Pickup" + AttachmentArchetypeName="WEP_HRG_Boomy_ARCH.Wep_HRG_Boomy_3P" + MuzzleFlashTemplateName="WEP_HRG_Boomy_ARCH.Wep_HRG_Boomy_MuzzleFlash" + + + // Zooming/Position + PlayerViewOffset=(X=5.0,Y=9,Z=-5) + IronSightPosition=(X=5,Y=0,Z=0) + + // Ammo + MagazineCapacity[0]=24 + SpareAmmoCapacity[0]=192 + InitialSpareMags[0]=1 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=325 //325 //130 //165 + minRecoilPitch=300 //275 //115 //130 + maxRecoilYaw=140 //150 //115 //130 + minRecoilYaw=-140 //-150 //-115 //130 + RecoilRate=0.085 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=75 + RecoilISMinYawLimit=65460 + RecoilISMaxPitchLimit=375 + RecoilISMinPitchLimit=65460 + RecoilViewRotationScale=0.25 + IronSightMeshFOVCompensationScale=1.5 + HippedRecoilModifier=1.5 + + // Inventory / Grouping + InventorySize=7 + GroupPriority=50 + WeaponSelectTexture=Texture2D'WEP_UI_HRG_Boomy_TEX.UI_WeaponSelect_HRG_Boomy' + + AssociatedPerkClasses(0)=class'KFPerk_Demolitionist' + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_HRG_Boomy' + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_Boomy' + FireInterval(DEFAULT_FIREMODE)=+0.1667 // 360 RPM + Spread(DEFAULT_FIREMODE)=0.01 //0.025 + InstantHitDamage(DEFAULT_FIREMODE)=25.0 + FireOffset=(X=30,Y=4.5,Z=-5) + + // ALT_FIREMODE + FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_HRG_Boomy' + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_Boomy' + FireInterval(ALTFIRE_FIREMODE)=+0.1667 // 360 RPM + InstantHitDamage(ALTFIRE_FIREMODE)=25.0 + Spread(ALTFIRE_FIREMODE)=0.01 //0.025 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Boomy' + InstantHitDamage(BASH_FIREMODE)=26 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'ww_wep_hrg_boomy.Play_WEP_HRG_Boomy_3P_ShootLoop', FirstPersonCue=AkEvent'ww_wep_hrg_boomy.Play_WEP_HRG_Boomy_1P_ShootLoop') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'ww_wep_hrg_boomy.Play_WEP_HRG_Boomy_3P_Shoot', FirstPersonCue=AkEvent'ww_wep_hrg_boomy.Play_WEP_HRG_Boomy_1P_Shoot') + WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'ww_wep_hrg_boomy.Play_WEP_HRG_Boomy_3P_ShootLoopEnd', FirstPersonCue=AkEvent'ww_wep_hrg_boomy.Play_WEP_HRG_Boomy_1P_ShootLoopEnd') + + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_L85A2.Play_WEP_SA_L85A2_Handling_DryFire' + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_L85A2.Play_WEP_SA_L85A2_Handling_DryFire' + + // Advanced (High RPM) Fire Effects + bLoopingFireAnim(DEFAULT_FIREMODE)=true + bLoopingFireSnd(DEFAULT_FIREMODE)=true + SingleFireSoundIndex=ALTFIRE_FIREMODE + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Damage1, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) + WeaponUpgrades[3]=(Stats=((Stat=EWUS_Damage0, Scale=1.45f), (Stat=EWUS_Damage1, Scale=1.45f), (Stat=EWUS_Weight, Add=3))) +} diff --git a/KFGameContent/Classes/KFWeap_HRG_Energy.uc b/KFGameContent/Classes/KFWeap_HRG_Energy.uc new file mode 100644 index 0000000..9aa49a1 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_Energy.uc @@ -0,0 +1,510 @@ +//============================================================================= +// KFWeap_HRG_Energy +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HRG_Energy extends KFWeap_PistolBase; + +`define HRG_ENERGY_MIC_LED_INDEX_1 0 +`define HRG_ENERGY_MIC_SCREEN_INDEX 1 +`define HRG_ENERGY_MIC_LED_INDEX_2 2 + +/********************************************************************************************* + @name Optics UI +********************************************************************************************* */ +var class OpticsUIClass; +var KFGFxWorld_MedicOptics OpticsUI; + +/** The last updated value for our ammo - Used to know when to update our optics ammo */ +var byte StoredPrimaryAmmo; +var byte StoredSecondaryAmmo; + +var transient float AltFireMaxShots; + +/** Modifier applied to the alt fire animation */ +var float SecondaryFireAnimRateModifier; + +/** Material colors applied to different fire modes */ +var LinearColor DefaultFireMaterialColor; +var LinearColor AltFireMaterialColor; + +/** How much recoil the altfire should do */ +var protected const float AltFireRecoilScale; + +simulated event PreBeginPlay() +{ + super.PreBeginPlay(); + AltFireMaxShots = MagazineCapacity[DEFAULT_FIREMODE] / AmmoCost[ALTFIRE_FIREMODE]; +} + +simulated function Activate() +{ + super.Activate(); + UpdateMaterial(bUseAltFireMode ? ALTFIRE_FIREMODE : DEFAULT_FIREMODE); +} + +simulated function UpdateMaterial(byte FireMode) +{ + local LinearColor MatColor; + MatColor = FireMode == DEFAULT_FIREMODE ? DefaultFireMaterialColor : AltFireMaterialColor; + + if( WeaponMICs.Length > `HRG_ENERGY_MIC_LED_INDEX_1 ) + { + WeaponMICs[`HRG_ENERGY_MIC_LED_INDEX_1].SetVectorParameterValue('Vector_GlowColor', MatColor); + } + + if (WeaponMICs.Length > `HRG_ENERGY_MIC_SCREEN_INDEX) + { + WeaponMICs[`HRG_ENERGY_MIC_SCREEN_INDEX].SetVectorParameterValue('Color_override', MatColor); + } + + if( WeaponMICs.Length > `HRG_ENERGY_MIC_LED_INDEX_2 ) + { + WeaponMICs[`HRG_ENERGY_MIC_LED_INDEX_2].SetVectorParameterValue('Vector_GlowColor', MatColor); + } +} + +/** + * Check target locking - server-side only + * HealAmmo Regen client and server + */ +simulated event Tick( FLOAT DeltaTime ) +{ + if (Instigator != none && Instigator.weapon == self) + { + UpdateOpticsUI(); + } + + Super.Tick(DeltaTime); +} + +/********************************************************************************************* + @name Optics UI +********************************************************************************************* */ + +/** Get our optics movie from the inventory once our InvManager is created */ +reliable client function ClientWeaponSet(bool bOptionalSet, optional bool bDoNotActivate) +{ + local KFInventoryManager KFIM; + + super.ClientWeaponSet(bOptionalSet, bDoNotActivate); + + if (OpticsUI == none) + { + KFIM = KFInventoryManager(InvManager); + if (KFIM != none) + { + //Create the screen's UI piece + OpticsUI = KFGFxWorld_MedicOptics(KFIM.GetOpticsUIMovie(OpticsUIClass)); + } + } +} + +function ItemRemovedFromInvManager() +{ + local KFInventoryManager KFIM; + local KFWeap_MedicBase KFW; + + Super.ItemRemovedFromInvManager(); + + if (OpticsUI != none) + { + KFIM = KFInventoryManager(InvManager); + if (KFIM != none) + { + // @todo future implementation will have optics in base weapon class + foreach KFIM.InventoryActors(class'KFWeap_MedicBase', KFW) + { + // This is not a MedicBase, no need to check against itself + if(KFW.OpticsUI.Class == OpticsUI.class) + { + // A different weapon is still using this optics class + return; + } + } + + //Create the screen's UI piece + KFIM.RemoveOpticsUIMovie(OpticsUI.class); + + OpticsUI.Close(); + OpticsUI = none; + } + } +} + +/** Unpause our optics movie and reinitialize our ammo when we equip the weapon */ +simulated function AttachWeaponTo(SkeletalMeshComponent MeshCpnt, optional Name SocketName) +{ + super.AttachWeaponTo(MeshCpnt, SocketName); + + if (OpticsUI != none) + { + OpticsUI.SetPause(false); + OpticsUI.ClearLockOn(); + UpdateOpticsUI(true); + OpticsUI.SetShotPercentCost( AmmoCost[ALTFIRE_FIREMODE]); + } +} + +/** Pause the optics movie once we unequip the weapon so it's not playing in the background */ +simulated function DetachWeapon() +{ + local Pawn OwnerPawn; + super.DetachWeapon(); + + OwnerPawn = Pawn(Owner); + if( OwnerPawn != none && OwnerPawn.Weapon == self ) + { + if (OpticsUI != none) + { + OpticsUI.SetPause(); + } + } +} + +/** + * Update our displayed ammo count if it's changed + */ +simulated function UpdateOpticsUI(optional bool bForceUpdate) +{ + if (OpticsUI != none && OpticsUI.OpticsContainer != none) + { + if (AmmoCount[DEFAULT_FIREMODE] != StoredPrimaryAmmo || bForceUpdate) + { + StoredPrimaryAmmo = AmmoCount[DEFAULT_FIREMODE]; + OpticsUI.SetPrimaryAmmo(StoredPrimaryAmmo); + + if(AmmoCount[DEFAULT_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE]) + { + OpticsUI.SetHealerCharge(0); + } + else + { + OpticsUI.SetHealerCharge((AmmoCount[DEFAULT_FIREMODE] / AmmoCost[ALTFIRE_FIREMODE]) / AltFireMaxShots * 100); + } + } + + if(OpticsUI.MinPercentPerShot != AmmoCost[ALTFIRE_FIREMODE]) + { + OpticsUI.SetShotPercentCost( AmmoCost[ALTFIRE_FIREMODE] ); + } + } +} + +/** Healing charge doesn't count as ammo for purposes of inventory management (e.g. switching) */ +simulated function bool HasAnyAmmo() +{ + return HasSpareAmmo() || HasAmmo(DEFAULT_FIREMODE) || HasSpareAmmo(ALTFIRE_FIREMODE) || HasAmmo(ALTFIRE_FIREMODE); +} + +simulated function StartFire(byte FireModeNum) +{ + if (FireModeNum == DEFAULT_FIREMODE && bUseAltFireMode) + { + if (AmmoCount[FireModeNum] < AmmoCost[ALTFIRE_FIREMODE] && SpareAmmoCount[FireModeNum] > 0) + { + BeginFire(RELOAD_FIREMODE); + return; + } + } + + super.StartFire(FireModeNum); +} + +simulated function AltFireMode() +{ + if ( !Instigator.IsLocallyControlled() ) + { + return; + } + + if ( !bUseAltFireMode && SpareAmmoCount[0] + AmmoCount[0] < AmmoCost[1] ) + { + return; + } + + super.AltFireMode(); + UpdateMaterial(bUseAltFireMode ? ALTFIRE_FIREMODE : DEFAULT_FIREMODE); + + NotifyAltFireUsage(); +} + +/** Overriden to use WeaponAnimSeqNode */ +simulated function PlayWeaponAnimation(name Sequence, float fDesiredDuration, optional bool bLoop, optional SkeletalMeshComponent SkelMesh) +{ + local float DesiredRate; + + if ( Mesh != none && Instigator != none && WorldInfo.NetMode != NM_DedicatedServer ) + { + if ( WeaponAnimSeqNode != None ) + { + if (WeaponAnimSeqNode.AnimSeq == None || WeaponAnimSeqNode.AnimSeq.SequenceName != Sequence) + { + WeaponAnimSeqNode.SetAnim(Sequence); + } + + if(fDesiredDuration > 0.0 && WeaponAnimSeqNode.AnimSeq.RateScale > 0.0) + { + DesiredRate = WeaponAnimSeqNode.AnimSeq.SequenceLength / (fDesiredDuration * WeaponAnimSeqNode.AnimSeq.RateScale); + WeaponAnimSeqNode.PlayAnim(bLoop, DesiredRate); + } + else + { + WeaponAnimSeqNode.PlayAnim(bLoop, DefaultAnimSpeed); + } + } + } +} + +/** + * PlayFireEffects Is the root function that handles all of the effects associated with + * a weapon. This function creates the 1st person effects. It should only be called + * on a locally controlled player. + */ +simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation ) +{ + local name WeaponFireAnimName; + local KFPerk CurrentPerk; + local float TempTweenTime, AdjustedAnimLength; + + // If we have stopped the looping fire sound to play single fire sounds for zed time + // start the looping sound back up again when the time is back above zed time speed + if( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !bPlayingLoopingFireSnd ) + { + StartLoopingFireSound(FireModeNum); + } + + PlayFiringSound(CurrentFireMode); + + if( Instigator != none ) + { + // Tell our pawn about any changes in animation speed + UpdateWeaponAttachmentAnimRate( GetThirdPersonAnimRate() ); + + if( Instigator.IsLocallyControlled() ) + { + if( Instigator.IsFirstPerson() ) + { + if ( !bPlayingLoopingFireAnim ) + { + WeaponFireAnimName = GetWeaponFireAnim(FireModeNum); + + if ( WeaponFireAnimName != '' ) + { + AdjustedAnimLength = MySkelMesh.GetAnimLength(WeaponFireAnimName); + TempTweenTime = FireTweenTime; + + if (FireModeNum == ALTFIRE_FIREMODE) + { + AdjustedAnimLength *= SecondaryFireAnimRateModifier; + } + + CurrentPerk = GetPerk(); + if( CurrentPerk != none ) + { + CurrentPerk.ModifyRateOfFire( AdjustedAnimLength, self ); + + // We need to unlock the slide if we fire from zero ammo while uber ammo is active + if( EmptyMagBlendNode != none + && BonesToLockOnEmpty.Length > 0 + && AmmoCount[GetAmmoType(FireModeNum)] == 0 + && CurrentPerk.GetIsUberAmmoActive(self) ) + { + EmptyMagBlendNode.SetBlendTarget( 0, 0 ); + TempTweenTime = 0.f; + } + } + + PlayAnimation(WeaponFireAnimName, AdjustedAnimLength,, TempTweenTime); + } + } + + // Start muzzle flash effect + CauseMuzzleFlash(FireModeNum); + } + + HandleRecoil(); + ShakeView(); + + if (AmmoCount[0] == 0 && ForceReloadTimeOnEmpty > 0) + { + SetTimer(ForceReloadTimeOnEmpty, false, nameof(ForceReload)); + } + } + } +} + +simulated function ModifyRecoil( out float CurrentRecoilModifier ) +{ + if( CurrentFireMode == ALTFIRE_FIREMODE ) + { + CurrentRecoilModifier *= AltFireRecoilScale; + } + + super.ModifyRecoil( CurrentRecoilModifier ); +} + +simulated function NotifyAltFireUsage() +{ + local KFPawn_Human KFPH; + + // Notify 3P to change material. + KFPH = KFPawn_Human(Instigator); + if (KFPH != none) + { + KFPH.SetUsingAltFireMode(bUseAltFireMode, true); + KFPH.bNetDirty = true; + } +} + +simulated state WeaponEquipping +{ + simulated function BeginState(Name PreviousStateName) + { + super.BeginState(PreviousStateName); + // NotifyAltFireUsage(); + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + SetTimer(1.0f, false, nameof(NotifyAltFireUsage)); + } + } +} + +/** Returns animation to play based on reload type and status */ +simulated function name GetReloadAnimName( bool bTacticalReload ) +{ + return bTacticalReload ? ReloadNonEmptyMagEliteAnim : ReloadNonEmptyMagAnim; +} + +simulated function ConsumeAmmo( byte FireModeNum ) +{ + super.ConsumeAmmo(FireModeNum); + + if( bUseAltFireMode && SpareAmmoCount[0] + AmmoCount[0] < AmmoCost[1] ) + { + bUseAltFireMode = false; + UpdateMaterial(DEFAULT_FIREMODE); + NotifyAltFireUsage(); + } +} + +defaultproperties +{ + // Inventory + InventoryGroup=IG_Secondary + InventorySize=4 + GroupPriority=125 + bCanThrow=true + bDropOnDeath=true + WeaponSelectTexture=Texture2D'WEP_UI_HRG_Energy_TEX.UI_WeaponSelect_HRG_Energy' + SecondaryAmmoTexture=Texture2D'UI_SecondaryAmmo_TEX.MedicDarts' + AssociatedPerkClasses(0)=class'KFPerk_Gunslinger' + + // Shooting Animations + FireSightedAnims[0]=Shoot_Iron + FireSightedAnims[1]=Shoot_Iron2 + FireSightedAnims[2]=Shoot_Iron3 + + // FOV + MeshFOV=86 + MeshIronSightFOV=77 + PlayerIronSightFOV=77 + + // Depth of field + DOF_FG_FocalRadius=40 + DOF_FG_MaxNearBlurSize=3.5 + + // Zooming/Position + PlayerViewOffset=(X=29.0,Y=13,Z=-4) + + //Content + PackageKey="HRG_Energy" + FirstPersonMeshName="WEP_1P_HRG_Energy_MESH.Wep_1stP_HRG_Energy_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_HRG_Energy_ANIM.WEP_1P_HRG_Energy_ANIM" + PickupMeshName="wep_3p_HRG_Energy_mesh.Wep_HRG_Energy_Pickup" + AttachmentArchetypeName="WEP_HRG_Energy_ARCH.Wep_HRG_Energy_3P" + MuzzleFlashTemplateName="WEP_HRG_Energy_ARCH.Wep_HRG_Energy_MuzzleFlash" + + OpticsUIClass=class'KFGFxWorld_MedicOptics' + + // Zooming/Position + IronSightPosition=(X=15,Y=0,Z=0) + + // Ammo + MagazineCapacity[0]=15 + SpareAmmoCapacity[0]=135 + InitialSpareMags[0]=2 + bCanBeReloaded=true + bReloadFromMagazine=true + + MagazineCapacity[1]=0 + bCanRefillSecondaryAmmo=false + + // Recoil + maxRecoilPitch=475 //250 + minRecoilPitch=425 //200 + maxRecoilYaw=130 //100 + minRecoilYaw=-130 //-100 + RecoilRate=0.07 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=250 + RecoilISMinPitchLimit=65485 + AltFireRecoilScale=2.0f + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_HRG_Energy' + FireInterval(DEFAULT_FIREMODE)=+0.175 //342 RPM + PenetrationPower(DEFAULT_FIREMODE)=1.0 + InstantHitDamage(DEFAULT_FIREMODE)=90.0 //125.0 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_Energy_Primary' + Spread(DEFAULT_FIREMODE)=0.015 + FireOffset=(X=20,Y=4.0,Z=-3) + + // ALTFIRE_FIREMODE + FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity' + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_InstantHit + FireInterval(ALTFIRE_FIREMODE)=+0.705 //85 RPM + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_HRG_Energy_Secondary' + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_Energy_Secondary' + InstantHitMomentum(ALTFIRE_FIREMODE)=1.0 + PenetrationPower(ALTFIRE_FIREMODE)=3.0 + InstantHitDamage(ALTFIRE_FIREMODE)=300.0 //475.0 + PenetrationDamageReductionCurve(ALTFIRE_FIREMODE)=(Points=((InVal=0.f,OutVal=0.f),(InVal=1.f, OutVal=1.f))) + AmmoCost(ALTFIRE_FIREMODE)=3 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Energy' + InstantHitDamage(BASH_FIREMODE)=26 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'ww_wep_hrg_energy.Play_WEP_HRG_Energy_3P_Shoot', FirstPersonCue=AkEvent'ww_wep_hrg_energy.Play_WEP_HRG_Energy_1P_Shoot') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'ww_wep_hrg_energy.Play_WEP_HRG_Energy_3P_ShootAlt', FirstPersonCue=AkEvent'ww_wep_hrg_energy.Play_WEP_HRG_Energy_1P_ShootAlt') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_MedicPistol.Play_SA_MedicPistol_Handling_DryFire' + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_MedicDart.Play_WEP_SA_Medic_Dart_DryFire' + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + + SecondaryFireAnimRateModifier = 2.0f; + + DefaultFireMaterialColor=(R=0.90f,G=0.26f,B=0.0f) + AltFireMaterialColor=(R=0.7f,G=0.04f,B=0.9f) + + NumBloodMapMaterials=3 +// bForceHandleImpacts=true; +} diff --git a/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc b/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc index 64766e9..d5e202a 100644 --- a/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc +++ b/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc @@ -842,8 +842,8 @@ defaultproperties ValueIncreaseTime=0.2 //FOR LERPING DAMANGE - MaxDamageByCharge=250 //200 //120 - MinDamageByCharge=25 //30 + MaxDamageByCharge=300 //250 //200 //120 + MinDamageByCharge=30 //25 //30 // FOV Meshfov=80 MeshIronSightFOV=65 //52 @@ -870,8 +870,8 @@ defaultproperties // Ammo MagazineCapacity[0]=12 //24 - SpareAmmoCapacity[0]=108 //96 //120 - InitialSpareMags[0]=2 //1 + SpareAmmoCapacity[0]=132 //108 + InitialSpareMags[0]=3 //2 AmmoPickupScale[0]=1.5 //1 //0.75 bCanBeReloaded=true bReloadFromMagazine=true @@ -905,7 +905,7 @@ defaultproperties FiringStatesArray(DEFAULT_FIREMODE)=MineReconstructorCharge WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Mine_Reconstructor' - FireInterval(DEFAULT_FIREMODE)=+0.33 //180 RPMs + FireInterval(DEFAULT_FIREMODE)=+0.223 //+0.33 InstantHitDamage(DEFAULT_FIREMODE)=120 PenetrationPower(DEFAULT_FIREMODE)=0.0; InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Toxic_MineReconstructorImpact' @@ -914,7 +914,7 @@ defaultproperties // ALT_FIREMODE FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Custom - FireInterval(ALTFIRE_FIREMODE)=+0.25 + FireInterval(ALTFIRE_FIREMODE)=+0.15 //+0.25 AmmoCost(ALTFIRE_FIREMODE)=0 // BASH_FIREMODE diff --git a/KFGameContent/Classes/KFWeap_Pistol_Bladed.uc b/KFGameContent/Classes/KFWeap_Pistol_Bladed.uc new file mode 100644 index 0000000..f89d08e --- /dev/null +++ b/KFGameContent/Classes/KFWeap_Pistol_Bladed.uc @@ -0,0 +1,228 @@ +//============================================================================= +// KFWeap_Pistol_Bladed +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_Pistol_Bladed extends KFWeap_MeleeBase; + +/** Returns trader filter index based on weapon type */ +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Pistol; +} + +/** Override melee SetIronSights (which sends to heavy attack) so that this weapon ironsights normally*/ +simulated function SetIronSights(bool bNewIronSights) +{ + super(KFWeapon).SetIronSights(bNewIronSights); +} + +/** Override melee ShouldOwnerWalk which doesn't account for walking when in ironsights */ +simulated function bool ShouldOwnerWalk() +{ + return super(KFWeapon).ShouldOwnerWalk(); +} + + +/** Override to drop the player out of ironsights first */ +simulated function AltFireMode() +{ + if (!Instigator.IsLocallyControlled()) + { + return; + } + + // break out of ironsights when starting to block + if (bUsingSights) + { + SetIronSights(false); + } + + StartFire(BLOCK_FIREMODE); +} + +simulated state MeleeBlocking +{ + simulated function bool AllowIronSights() { return false; } +} + +/** Called during reload state */ +simulated function bool CanOverrideMagReload(byte FireModeNum) +{ + if (FireModeNum == BLOCK_FIREMODE) + { + return true; + } + + return super.CanOverrideMagReload(FireModeNum); +} + +simulated function StartFire(byte FireModeNum) +{ + if( FireModeNum == DEFAULT_FIREMODE ) + { + if ( ShouldAutoReload(FireModeNum) ) + { + BeginFire(RELOAD_FIREMODE); + return; + } + } + + super.StartFire(FireModeNum); +} + +/********************************************************************************************* + * @name Firing / Projectile +********************************************************************************************* */ +/** + * See Pawn.ProcessInstantHit + * @param DamageReduction: Custom KF parameter to handle penetration damage reduction + */ +simulated function ProcessInstantHitEx(byte FiringMode, ImpactInfo Impact, optional int NumHits, optional out float out_PenetrationVal, optional int ImpactNum ) +{ + local KFPerk InstigatorPerk; + + InstigatorPerk = GetPerk(); + if( InstigatorPerk != none ) + { + InstigatorPerk.UpdatePerkHeadShots( Impact, InstantHitDamageTypes[FiringMode], ImpactNum ); + } + + super.ProcessInstantHitEx( FiringMode, Impact, NumHits, out_PenetrationVal, ImpactNum ); +} + +defaultproperties +{ + // MeleeBase + bMeleeWeapon=false + // FOV + MeshFOV=96 + MeshIronSightFOV=77 + PlayerIronSightFOV=77 + + // Depth of field + DOF_FG_FocalRadius=40 + DOF_FG_MaxNearBlurSize=3.5 + + // Zooming/Position + PlayerViewOffset=(X=-15,Y=12,Z=-6) + IronSightPosition=(X=0,Y=0,Z=1.0) //(X=-3,Y=-0.38,Z=-0.2) + + // Content + PackageKey="BladedPistol" + FirstPersonMeshName="WEP_1P_BladedPistol_MESH.WEP_1stP_BladedPistol_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_BladedPistol_ANIM.WEP_1stP_BladedPistol_Anim" + PickupMeshName="WEP_3P_BladedPistol_MESH.Wep_BladedPistol_Pickup" + AttachmentArchetypeName="WEP_BladedPistol_ARCH.Wep_BladedPistol_3P" + MuzzleFlashTemplateName="WEP_BladedPistol_ARCH.Wep_BladedPistol_MuzzleFlash" + + // Ammo + MagazineCapacity[0]=6 + SpareAmmoCapacity[0]=72 //96 + InitialSpareMags[0]=2 + AmmoPickupScale[0]=1.0 //2.0 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=330 //400 //250 + minRecoilPitch=300 //350 //200 + maxRecoilYaw=90 //120 //100 + minRecoilYaw=-90 //-120 //-100 + RecoilRate=0.07 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=250 + RecoilISMinPitchLimit=65485 + + // DEFAULT_FIREMODE + FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Blade_BladedPistol' + PenetrationPower(DEFAULT_FIREMODE)=3.0 + FireInterval(DEFAULT_FIREMODE)=+0.25 //+0.3 + InstantHitDamage(DEFAULT_FIREMODE)=115.0 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Slashing_BladedPistol' + Spread(DEFAULT_FIREMODE)=0.005 //0.015 + AmmoCost(DEFAULT_FIREMODE)=1 + + FireOffset=(X=30,Y=5,Z=-4) + + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Sawblade' + + // ALT_FIREMODE + // FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + // WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_BladedPistol' + InstantHitDamage(BASH_FIREMODE)=75 //26 + FiringStatesArray(BASH_FIREMODE)=MeleeAttackBasic + WeaponFireTypes(BASH_FIREMODE)=EWFT_Custom + InstantHitMomentum(BASH_FIREMODE)=10000.f + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Fire_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Handling_DryFire' + + // RELOAD_FIREMODE + FiringStatesArray(RELOAD_FIREMODE)="Reloading" + WeaponFireTypes(RELOAD_FIREMODE)=EWFT_InstantHit + + // Attachments + bHasIronSights=true + bHasFlashlight=true + + AssociatedPerkClasses(0)=class'KFPerk_Gunslinger' + AssociatedPerkClasses(1)=class'KFPerk_Berserker' + + + // Inventory + InventoryGroup=IG_Secondary + InventorySize=3 + GroupPriority=25 + bCanThrow=true + bDropOnDeath=true + WeaponSelectTexture=Texture2D'WEP_UI_BladedPistol_TEX.UI_WeaponSelect_BladedPistol' + bIsBackupWeapon=false + + DualClass=class'KFWeap_Pistol_DualBladed' + + // Custom animations + FireSightedAnims=(Shoot_Iron) + IdleFidgetAnims=(Guncheck_v1, Guncheck_v2, Guncheck_v3) + + bHasFireLastAnims=true + + BonesToLockOnEmpty=(RW_FrontPivot) + + // default MIC param names + BlockEffectsSocketName=BlockEffect + // Defensive + BlockDamageMitigation=0.60f + ParryDamageMitigationPercent=0.5 + ParryStrength=4 + BlockHitAnimCooldownTime=0.5f + BlockTypes.Add((DmgType=class'KFDT_Bludgeon')) + BlockTypes.Add((DmgType=class'KFDT_Slashing')) + + // Block Sounds + BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Crovel' + ParrySound=AkEvent'WW_WEP_Bullet_Impacts.Play_Parry_Metal' + + BlockParticleSystem=ParticleSystem'FX_Impacts_EMIT.FX_Block_melee_01' + ParryParticleSystem=ParticleSystem'FX_Impacts_EMIT.FX_Parry_melee_01' + MeleeBlockHitAnims=(Block_Hit_V1, Block_Hit_V2, Block_Hit_V3); + + //Upgrades + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Damage2, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Damage1, Scale=1.3f), (Stat=EWUS_Damage2, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) +} + diff --git a/KFGameContent/Classes/KFWeap_Pistol_DualBladed.uc b/KFGameContent/Classes/KFWeap_Pistol_DualBladed.uc new file mode 100644 index 0000000..24b2a27 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_Pistol_DualBladed.uc @@ -0,0 +1,679 @@ +//============================================================================= +// KFWeap_Pistol_DualBladed +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_Pistol_DualBladed extends KFWeap_DualBase; + +/* + * + * BROUGHT FROM MELEE WEAPON TO SUPPORT PARRYING + * + */ + +/** These override the base firemodes of the same ID (for readability) */ +const BLOCK_FIREMODE = 1; // ALTFIRE_FIREMODE + +/********************************************************************************************* + * @name Defensive Abilities + *********************************************************************************************/ + +struct native BlockEffectInfo +{ + var class DmgType; + + /** If != None, overrides the class default FX */ + var AkEvent BlockSound; + var AkEvent ParrySound; + var ParticleSystem BlockParticleSys; + var ParticleSystem ParryParticleSys; +}; + +var array BlockTypes; + +/** Damage while blocking will be mitigated by this percentage */ +var() float BlockDamageMitigation; + +/** Parry damage will be mitigated by this percentage */ +var() float ParryDamageMitigationPercent; +/** Hit reaction strength to bypass pawn's ParryStumbleResist */ +var() byte ParryStrength; + +/** If true, owning pawn moves at a slower (iron sight) walking speed */ +var bool bMoveAtWalkingSpeed; + +/** Time between block hit reaction anims */ +var() protected float BlockHitAnimCooldownTime; + +/** The last time we played a block hit reaction anim */ +var transient protected float LastBlockHitAnimTime; + +/** Animations played on successful block */ +var array MeleeBlockHitAnims; + +/********************************************************************************************* + * @name Effects + ********************************************************************************************* */ +/** Block / Parry */ +var AkBaseSoundObject BlockSound; +var AKBaseSoundObject ParrySound; +var ParticleSystem BlockParticleSystem; +var ParticleSystem ParryParticleSystem; +var name BlockEffectsSocketName; + +/** Defensive stance animation names */ +const MeleeBlockStartAnim = 'Brace_in'; +const MeleeBlockLoopAnim = 'Brace_loop'; +const MeleeBlockEndAnim = 'Brace_out'; + +var array BonesToLockOnEmpty_Override; + +simulated function NotifyAttackParried(); +simulated function NotifyAttackBlocked(); + +// Global declarations for blocking state +simulated function BlockLoopTimer(); +simulated function ParryCheckTimer(); + +/** Called on the server when successfully block/parry an attack */ +unreliable client function ClientPlayBlockEffects(optional byte BlockTypeIndex=255) +{ + local AkBaseSoundObject Sound; + local ParticleSystem PSTemplate; + + GetBlockEffects(BlockTypeIndex, Sound, PSTemplate); + PlayLocalBlockEffects(Sound, PSTemplate); +} + +/** Called on the server when successfully block/parry an attack */ +reliable client function ClientPlayParryEffects(optional byte BlockTypeIndex=255) +{ + local AkBaseSoundObject Sound; + local ParticleSystem PSTemplate; + local KFPerk InstigatorPerk; + + InstigatorPerk = GetPerk(); + if( InstigatorPerk != none ) + { + InstigatorPerk.SetSuccessfullParry(); + } + + GetParryEffects(BlockTypeIndex, Sound, PSTemplate); + PlayLocalBlockEffects(Sound, PSTemplate); +} + +simulated state MeleeBlocking +{ + ignores ForceReload, ShouldAutoReload; + + simulated function bool AllowIronSights() { return false; } + + simulated function byte GetWeaponStateId() + { + return WEP_MeleeBlock; + } + + simulated function BeginState(name PreviousStateName) + { + local float ParryDuration; + + ParryDuration = PlayBlockStart(); + + // Set the duration of the window to parry incoming attacks + if ( ParryDuration > 0.f ) + { + SetTimer( ParryDuration, false, nameof(ParryCheckTimer) ); + } + + NotifyBeginState(); + } + + simulated function EndState(Name NextStateName) + { + if ( Instigator.IsLocallyControlled() ) + { + PlayAnimation(MeleeBlockEndAnim); + } + + //SetSlowMovement(false); + NotifyEndState(); + } + + /** Return to active state if we're done blocking */ + simulated function EndFire(byte FireModeNum) + { + Global.EndFire(FireModeNum); + + // Wait until parry is finished, then check PendingFire to stop blocking + if ( !StillFiring(CurrentFireMode) && !IsTimerActive(nameof(ParryCheckTimer)) ) + { + GotoState('BlockingCooldown'); + } + } + + /** After the parry window is finished, check PendingFire to see if we're still blocking */ + simulated function ParryCheckTimer() + { + // Check PendingFire to stop blocking + if ( !StillFiring(CurrentFireMode) ) + { + GotoState('BlockingCooldown'); + } + } + + /** Grab/Grapple attacks can be parried */ + function bool IsGrappleBlocked(Pawn InstigatedBy) + { + local float FacingDot; + local vector Dir2d; + + // zero Z to give us a 2d dot product + Dir2d = Normal2d(InstigatedBy.Location - Instigator.Location); + FacingDot = vector(Instigator.Rotation) dot (Dir2d); + + // Cos(85) + if ( FacingDot > 0.087f ) + { + if ( IsTimerActive(nameof(ParryCheckTimer)) ) + { + KFPawn(InstigatedBy).NotifyAttackParried(Instigator, 255); + ClientPlayParryEffects(); + NotifyAttackParried(); + } + else + { + ClientPlayBlockEffects(); + NotifyAttackBlocked(); + } + + return TRUE; + } + return FALSE; + } + + /** While holding a melee weapon reduce some incoming damage */ + function AdjustDamage(out int InDamage, class DamageType, Actor DamageCauser) + { + local float FacingDot; + local vector Dir2d; + local KFPerk InstigatorPerk; + local byte BlockTypeIndex; + + // don't apply block/parry effects for teammates + if (Instigator.IsSameTeam(DamageCauser.Instigator)) + { + return; + } + + // zero Z to give us a 2d dot product + Dir2d = Normal2d(DamageCauser.Location - Instigator.Location); + FacingDot = vector(Instigator.Rotation) dot (Dir2d); + + // Cos(85) + if ( FacingDot > 0.087f && CanBlockDamageType(DamageType, BlockTypeIndex) ) + { + InstigatorPerk = GetPerk(); + + if ( IsTimerActive(nameof(ParryCheckTimer)) ) + { + InDamage *= GetUpgradedParryDamageMitigation(CurrentWeaponUpgradeIndex); + // Notify attacking pawn for effects / animations + if ( KFPawn(DamageCauser) != None ) + { + KFPawn(DamageCauser).NotifyAttackParried(Instigator, ParryStrength); + } + + // @NOTE: This is now always true per discussion with AndrewL on KFII-29686. Since we always + // do the damage mitigation, we should always play the effect regardless of whether the + // zed was stumbled or knocked down. -MattF + ClientPlayParryEffects(BlockTypeIndex); + + NotifyAttackParried(); + + if( InstigatorPerk != none ) + { + InstigatorPerk.SetSuccessfullParry(); + } + } + else + { + InDamage *= GetUpgradedBlockDamageMitigation(CurrentWeaponUpgradeIndex); + ClientPlayBlockEffects(BlockTypeIndex); + + NotifyAttackBlocked(); + + if( InstigatorPerk != none ) + { + InstigatorPerk.SetSuccessfullBlock(); + } + } + } + } + + simulated function BlockLoopTimer() + { + if( Instigator.IsLocallyControlled() ) + { + PlayAnimation(MeleeBlockLoopAnim, , true); + } + } + + /** State override for Block_Hit animations */ + unreliable client function ClientPlayBlockEffects(optional byte BlockTypeIndex=255) + { + local int AnimIdx; + local float Duration; + local KFPerk InstigatorPerk; + + Global.ClientPlayBlockEffects(BlockTypeIndex); + + InstigatorPerk = GetPerk(); + if( InstigatorPerk != none ) + { + InstigatorPerk.SetSuccessfullBlock(); + } + + if( MeleeBlockHitAnims.Length > 0 && `TimeSince(LastBlockHitAnimTime) > BlockHitAnimCooldownTime && !IsTimerActive(nameof(ParryCheckTimer)) ) + { + AnimIdx = Rand(MeleeBlockHitAnims.Length); + Duration = MySkelMesh.GetAnimLength(MeleeBlockHitAnims[AnimIdx]); + + if ( Duration > 0 ) + { + LastBlockHitAnimTime = WorldInfo.TimeSeconds; + PlayAnimation(MeleeBlockHitAnims[AnimIdx]); + SetTimer(Duration, false, nameof(BlockLoopTimer)); + } + } + } +} + +simulated function float PlayBlockStart() +{ + local float AnimDuration; + + if( Instigator.IsLocallyControlled() ) + { + PlayAnimation(MeleeBlockStartAnim); + } + + // set when to start playing the looping anim + AnimDuration = MySkelMesh.GetAnimLength(MeleeBlockStartAnim); + if ( AnimDuration > 0.f ) + { + SetTimer(AnimDuration, false, nameof(BlockLoopTimer)); + } + else + { + BlockLoopTimer(); + } + + // set the parry duration to the same as the block start anim + return AnimDuration; +} + +/** Called on the client when successfully block/parry an attack */ +simulated function PlayLocalBlockEffects(AKBaseSoundObject Sound, ParticleSystem PSTemplate) +{ + local vector Loc; + local rotator Rot; + local ParticleSystemComponent PSC; + + if ( Sound != None ) + { + PlaySoundBase(Sound, true); + } + + if ( PSTemplate != None ) + { + if ( MySkelMesh.GetSocketWorldLocationAndRotation(BlockEffectsSocketName, Loc, Rot) ) + { + PSC = WorldInfo.MyEmitterPool.SpawnEmitter(PSTemplate, Loc, Rot); + PSC.SetDepthPriorityGroup(SDPG_Foreground); + } + else + { + `log(self@GetFuncName()@"missing BlockEffects Socket!"); + } + } +} + +/** If true, this damage type can be blocked by the MeleeBlocking state */ +function bool CanBlockDamageType(class DamageType, optional out byte out_Idx) +{ + local int Idx; + + // Check if this damage should be ignored completely + for (Idx = 0; Idx < BlockTypes.length; ++Idx) + { + if ( ClassIsChildOf(DamageType, BlockTypes[Idx].DmgType) ) + { + out_Idx = Idx; + return true; + } + } + + out_Idx = INDEX_NONE; + return false; +} + +/** Returns sound and particle system overrides using index into BlockTypes array */ +simulated function GetBlockEffects(byte BlockIndex, out AKBaseSoundObject outSound, out ParticleSystem outParticleSys) +{ + outSound = BlockSound; + outParticleSys = BlockParticleSystem; + + if ( BlockIndex != 255 ) + { + if ( BlockTypes[BlockIndex].BlockSound != None ) + { + outSound = BlockTypes[BlockIndex].BlockSound; + } + if ( BlockTypes[BlockIndex].BlockParticleSys != None ) + { + outParticleSys = BlockTypes[BlockIndex].BlockParticleSys; + } + } +} + +/** Returns sound and particle system overrides using index into BlockTypes array */ +simulated function GetParryEffects(byte BlockIndex, out AKBaseSoundObject outSound, out ParticleSystem outParticleSys) +{ + outSound = ParrySound; + outParticleSys = ParryParticleSystem; + + if ( BlockIndex != 255 ) + { + if ( BlockTypes[BlockIndex].ParrySound != None ) + { + outSound = BlockTypes[BlockIndex].ParrySound; + } + if ( BlockTypes[BlockIndex].ParryParticleSys != None ) + { + outParticleSys = BlockTypes[BlockIndex].ParryParticleSys; + } + } +} + +/********************************************************************************************* + * State BlockingCooldown + * A short cooldown state to prevent spamming block while still allowing pendingfire to be set + *********************************************************************************************/ + +// Global declarations for this state +simulated function BlockCooldownTimer(); + +simulated state BlockingCooldown extends Active +{ + ignores AllowSprinting; + + /** Set cooldown duration */ + simulated function BeginState( Name PreviousStateName ) + { + SetTimer(0.5, false, nameof(BlockCooldownTimer)); + Super.BeginState(PreviousStateName); + } + + // prevent going to block/parry state + simulated function bool HasAmmo( byte FireModeNum, optional int Amount ) + { + if ( FireModeNum == BLOCK_FIREMODE ) + { + return false; + } + + return Global.HasAmmo(FireModeNum, Amount); + } + + // prevent HasAmmo (above) from causing an auto reload + simulated function bool ShouldAutoReload(byte FireModeNum) + { + if ( FireModeNum == BLOCK_FIREMODE ) + { + return false; + } + + return Global.ShouldAutoReload(FireModeNum); + } + + simulated function BlockCooldownTimer() + { + GotoState('Active'); + } +} + +/* + * + * END of parrying code. + * + */ + + +/** + * Toggle between DEFAULT and ALTFIRE + */ +simulated function AltFireMode() +{ + if (!Instigator.IsLocallyControlled()) + { + return; + } + + // break out of ironsights when starting to block + if (bUsingSights) + { + SetIronSights(false); + } + + Super(KFWeapon).StartFire(BLOCK_FIREMODE); +} + +/** Called during reload state */ +simulated function bool CanOverrideMagReload(byte FireModeNum) +{ + if (FireModeNum == BLOCK_FIREMODE) + { + return true; + } + + return super.CanOverrideMagReload(FireModeNum); +} + +/********************************************************************************************* + * Upgrades + ********************************************************************************************/ + +static simulated function float GetUpgradedBlockDamageMitigation(int UpgradeIndex) +{ + return GetUpgradedStatValue(default.BlockDamageMitigation, EWUS_BlockDmgMitigation, UpgradeIndex); +} + +static simulated function float GetUpgradedParryDamageMitigation(int UpgradeIndex) +{ + return GetUpgradedStatValue(default.ParryDamageMitigationPercent, EWUS_ParryDmgMitigation, UpgradeIndex); +} + +simulated function int GetModifiedDamage(byte FireModeNum, optional vector RayDir) +{ + if (FireModeNum == BASH_FIREMODE) + { + return GetUpgradedStatValue(InstantHitDamage[FireModeNum], EWUS_Damage2, CurrentWeaponUpgradeIndex); + } + + return super.GetModifiedDamage(FireModeNum, RayDir); +} + +/** Check AmmoCount and update anim tree nodes if needed */ +simulated function UpdateOutOfAmmoEffects(float BlendTime) +{ + if ( WorldInfo.NetMode == NM_DedicatedServer ) + return; + + if( EmptyMagBlendNode != None ) + { + // Differentiate Left/Right + if ( bAllowClientAmmoTracking && AmmoCount[0] <= 1 ) + { + EmptyMagBlendNode.SetBlendTarget(1, 0); + if ( AmmoCount[0] == 0 ) + { + + EmptyMagBlendNode = AnimNodeBlendPerBone(SkeletalMeshComponent(Mesh).FindAnimNode('EmptyMagBlend')); + BuildEmptyMagNodeWeightList( EmptyMagBlendNode, BonesToLockOnEmpty_Override ); + + EmptyMagBlendNode.SetBlendTarget(1,0); + + `Log("blending left"); + EmptyMagBlendNode_L.SetBlendTarget(1,0); + } + } + } +} + +/** Unlocks the bolt bone (Called by animnotify) */ +simulated function ANIMNOTIFY_UnLockBolt() +{ + super.ANIMNOTIFY_UnLockBolt(); + EmptyMagBlendNode_L.SetBlendTarget(0, 0); + + EmptyMagBlendNode = AnimNodeBlendPerBone(SkeletalMeshComponent(Mesh).FindAnimNode('EmptyMagBlend')); + BuildEmptyMagNodeWeightList( EmptyMagBlendNode, BonesToLockOnEmpty); +} + +defaultproperties +{ + // Content + PackageKey="Dual_BladedPistol" + FirstPersonMeshName="WEP_1P_Dual_BladedPistol_MESH.WEP_1stP_DualBladedPistol_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_Dual_BladedPistol_ANIM.Wep_1stP_Dual_BladedPistol_ANIM" + PickupMeshName="WEP_3P_Dual_BladedPistol_MESH.Wep_Dual_BladedPistol_Pickup" + AttachmentArchetypeName="WEP_Dual_BladedPistol_ARCH.Wep_Dual_BladedPistol_3P" + MuzzleFlashTemplateName="WEP_Dual_BladedPistol_ARCH.Wep_Dual_BladedPistol_MuzzleFlash" + + Begin Object Name=FirstPersonMesh + AnimTreeTemplate=AnimTree'CHR_1P_Arms_ARCH.WEP_1stP_Dual_Animtree_Master' + End Object + + FireOffset=(X=30,Y=7,Z=-5) + LeftFireOffset=(X=30,Y=-7,Z=-5) + + // Zooming/Position + IronSightPosition=(X=-3,Y=0,Z=0) + PlayerViewOffset=(X=-15,Y=0,Z=0) + + QuickWeaponDownRotation=(Pitch=-8192,Yaw=0,Roll=0) + + SingleClass=class'KFWeap_Pistol_Bladed' + + // FOV + MeshFOV=96 + MeshIronSightFOV=77 + PlayerIronSightFOV=77 + + // Depth of field + DOF_FG_FocalRadius=40 + DOF_FG_MaxNearBlurSize=3.5 + + // Ammo + MagazineCapacity[0]=12 + SpareAmmoCapacity[0]=72 //96 + InitialSpareMags[0]=1 + AmmoPickupScale[0]=0.5 //1.0 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=330 //400 //250 + minRecoilPitch=300 //350 //200 + maxRecoilYaw=120 //100 + minRecoilYaw=-120 //-100 + RecoilRate=0.07 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=250 + RecoilISMinPitchLimit=65485 + + // DEFAULT_FIREMODE + FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Blade_BladedPistol' + PenetrationPower(DEFAULT_FIREMODE)=3.0 + FireInterval(DEFAULT_FIREMODE)=+0.19 //+0.231 + InstantHitDamage(DEFAULT_FIREMODE)=115.0 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Slashing_BladedPistol' + Spread(DEFAULT_FIREMODE)=0.005 //0.015 + + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Sawblade' + + // MELEE_BLOCK_FIREMODE + FiringStatesArray(BLOCK_FIREMODE)=MeleeBlocking + WeaponFireTypes(BLOCK_FIREMODE)=EWFT_Custom + FireInterval(BLOCK_FIREMODE)=1.f + AmmoCost(BLOCK_FIREMODE)=0 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_BladedPistol' + InstantHitDamage(BASH_FIREMODE)=75 //26 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Fire_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Handling_DryFire' + + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Fire_1P') + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_BladedPistol.Play_WEP_BladedPistol_Handling_DryFire' + + // Attachments + bHasIronSights=true + bHasFlashlight=true + + AssociatedPerkClasses(0)=class'KFPerk_Gunslinger' + AssociatedPerkClasses(1)=class'KFPerk_Berserker' + + + // Inventory + InventoryGroup= IG_Primary + InventorySize=6 + GroupPriority=45 + bCanThrow=true + bDropOnDeath=true + WeaponSelectTexture=Texture2D'WEP_UI_Dual_BladedPistol_TEX.UI_WeaponSelect_Dual_BladedPistol' + bIsBackupWeapon=false + + BonesToLockOnEmpty=(RW_FrontPivot) + BonesToLockOnEmpty_L=(LW_FrontPivot) + BonesToLockOnEmpty_Override=(RW_FrontPivot, LW_FrontPivot) + + bHasFireLastAnims=true + + // default MIC param names + BlockEffectsSocketName=BlockEffect + // Defensive + BlockDamageMitigation=0.60f + ParryDamageMitigationPercent=0.5 + ParryStrength=4 + BlockHitAnimCooldownTime=0.5f + BlockTypes.Add((DmgType=class'KFDT_Bludgeon')) + BlockTypes.Add((DmgType=class'KFDT_Slashing')) + + // Block Sounds + BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Crovel' + ParrySound=AkEvent'WW_WEP_Bullet_Impacts.Play_Parry_Metal' + + BlockParticleSystem=ParticleSystem'FX_Impacts_EMIT.FX_Block_melee_01' + ParryParticleSystem=ParticleSystem'FX_Impacts_EMIT.FX_Parry_melee_01' + MeleeBlockHitAnims=(Block_Hit_V1, Block_Hit_V2, Block_Hit_V3); + + // Upgrades + UpgradeFireModes(BLOCK_FIREMODE) = 0 + //Upgrades + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Damage2, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Damage1, Scale=1.3f), (Stat=EWUS_Damage2, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) +} + diff --git a/KFGameContent/Classes/KFWeap_Rifle_ParasiteImplanter.uc b/KFGameContent/Classes/KFWeap_Rifle_ParasiteImplanter.uc new file mode 100644 index 0000000..2ba54ea --- /dev/null +++ b/KFGameContent/Classes/KFWeap_Rifle_ParasiteImplanter.uc @@ -0,0 +1,428 @@ +//============================================================================= +// KFWeap_Rifle_ParasiteImplanter +//============================================================================= +// Weapon code for parasite implanter. +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_Rifle_ParasiteImplanter extends KFWeap_ScopedBase; + +`define PARASITE_MIC_LED_INDEX 0 + +var float LastFireInterval; + +/** How many seed ammo to recharge per second */ +var float SeedFullRechargeSeconds; +/** How many ammo to recharge per second. */ +var transient float SeedRechargePerSecond; + +var transient float SeedIncrement; +var repnotify byte SeedAmmo; + +const SecondaryFireAnim = 'Shoot_Secondary'; +const SecondaryFireIronAnim = 'Shoot_Secondary_Iron'; + +/** Material colors applied to different fire modes */ +var LinearColor NoAmmoMaterialColor; +var LinearColor AmmoReadyMaterialColor; + +/** How much recoil the altfire should do */ +var protected const float AltFireRecoilScale; + +replication +{ + if (bNetDirty && Role == ROLE_Authority) + SeedAmmo; +} + +simulated event ReplicatedEvent(name VarName) +{ + if (VarName == nameof(SeedAmmo)) + { + if (AmmoCount[ALTFIRE_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE] && SeedAmmo >= AmmoCost[ALTFIRE_FIREMODE]) + { + UpdateMaterial(true); + } + else if (AmmoCount[ALTFIRE_FIREMODE] >= AmmoCost[ALTFIRE_FIREMODE] && SeedAmmo < AmmoCost[ALTFIRE_FIREMODE]) + { + UpdateMaterial(false); + } + AmmoCount[ALTFIRE_FIREMODE] = SeedAmmo; + } + else + { + Super.ReplicatedEvent(VarName); + } +} + +/********************************************************************************************* + * @name Trader + *********************************************************************************************/ + +/** Returns trader filter index based on weapon type */ +static simulated event EFilterTypeUI GetTraderFilter() +{ + if( default.FiringStatesArray[DEFAULT_FIREMODE] == 'WeaponFiring' || default.FiringStatesArray[DEFAULT_FIREMODE] == 'WeaponBurstFiring' ) + { + return FT_Assault; + } + else // if( FiringStatesArray[DEFAULT_FIREMODE] == 'WeaponSingleFiring') + { + return FT_Rifle; + } +} + +simulated event PreBeginPlay() +{ + super.PreBeginPlay(); + StartSeedRecharge(); +} + +function StartSeedRecharge() +{ + // local KFPerk InstigatorPerk; + local float UsedSeedRechargeTime; + + // begin ammo recharge on server + if( Role == ROLE_Authority ) + { + UsedSeedRechargeTime = SeedFullRechargeSeconds; + SeedRechargePerSecond = MagazineCapacity[ALTFIRE_FIREMODE] / UsedSeedRechargeTime; + SeedIncrement = 0; + } +} + +function RechargeSeed(float DeltaTime) +{ + if ( Role == ROLE_Authority ) + { + SeedIncrement += SeedRechargePerSecond * DeltaTime; + + if( SeedIncrement >= 1.0 && AmmoCount[ALTFIRE_FIREMODE] < MagazineCapacity[ALTFIRE_FIREMODE] ) + { + AmmoCount[ALTFIRE_FIREMODE]++; + SeedIncrement -= 1.0; + SeedAmmo = AmmoCount[ALTFIRE_FIREMODE]; + } + } +} + +/** Overridden to call StartHealRecharge on server */ +function GivenTo( Pawn thisPawn, optional bool bDoNotActivate ) +{ + super.GivenTo( thisPawn, bDoNotActivate ); + + if( Role == ROLE_Authority && !thisPawn.IsLocallyControlled() ) + { + StartSeedRecharge(); + } +} +/********************************************************************************************* + @name Actor +********************************************************************************************* */ +simulated event Tick( FLOAT DeltaTime ) +{ + local bool bWasLowAmmo; + bWasLowAmmo = AmmoCount[ALTFIRE_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE]; + + if( AmmoCount[ALTFIRE_FIREMODE] < MagazineCapacity[ALTFIRE_FIREMODE] ) + { + RechargeSeed(DeltaTime); + } + + if (WorldInfo.NetMode != NM_DedicatedServer) + { + if (bWasLowAmmo && AmmoCount[ALTFIRE_FIREMODE] >= AmmoCost[ALTFIRE_FIREMODE]) + { + UpdateMaterial(true); + } + } + else + { + if (bWasLowAmmo && AmmoCount[ALTFIRE_FIREMODE] >= AmmoCost[ALTFIRE_FIREMODE]) + { + NotifyAltAmmoReady(true); + } + } + + Super.Tick(DeltaTime); +} + +/********************************************************************************************* + * Trader + ********************************************************************************************/ + +/** Allows weapon to set its own trader stats (can set number of stats, names and values of stats) */ +static simulated event SetTraderWeaponStats( out array WeaponStats ) +{ + super.SetTraderWeaponStats( WeaponStats ); + + WeaponStats.Length = WeaponStats.Length + 1; + WeaponStats[WeaponStats.Length-1].StatType = TWS_RechargeTime; + WeaponStats[WeaponStats.Length-1].StatValue = default.SeedFullRechargeSeconds; +} + +/** Seeds doesn't count as ammo for purposes of inventory management (e.g. switching) */ +simulated function bool HasAnyAmmo() +{ + return HasSpareAmmo() || HasAmmo(DEFAULT_FIREMODE); +} + +simulated function ConsumeAmmo( byte FireModeNum ) +{ + local bool bWasHighAmmo; + + if( FireModeNum != ALTFIRE_FIREMODE ) + { + Super.ConsumeAmmo(FireModeNum); + return; + } + +`if(`notdefined(ShippingPC)) + if( bInfiniteAmmo ) + { + return; + } +`endif + + bWasHighAmmo = AmmoCount[ALTFIRE_FIREMODE] >= AmmoCost[ALTFIRE_FIREMODE]; + + // If AmmoCount is being replicated, don't allow the client to modify it here + if (Role == ROLE_Authority) + { + // Don't consume ammo if magazine size is 0 (infinite ammo with no reload) + if (MagazineCapacity[1] > 0 && AmmoCount[1] > 0) + { + // Reduce ammo amount by heal ammo cost + AmmoCount[1] = Max(AmmoCount[1] - AmmoCost[1], 0); + } + } + + if (WorldInfo.NetMode != NM_DedicatedServer) + { + if (bWasHighAmmo && AmmoCount[ALTFIRE_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE]) + { + UpdateMaterial(false); + } + } + else if (bWasHighAmmo && AmmoCount[ALTFIRE_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE]) + { + NotifyAltAmmoReady(false); + } +} + +/** Instead of switch fire mode use as immediate alt fire */ +simulated function AltFireMode() +{ + if ( !Instigator.IsLocallyControlled() ) + { + return; + } + + StartFire(ALTFIRE_FIREMODE); +} + +simulated function float GetFireInterval(byte FireModeNum) +{ + if (FireModeNum == DEFAULT_FIREMODE && AmmoCount[FireModeNum] == 0) + { + return LastFireInterval; + } + + return super.GetFireInterval(FireModeNum); +} + +simulated function name GetWeaponFireAnim(byte FireModeNum) +{ + if (FireModeNum == ALTFIRE_FIREMODE) + { + return bUsingSights ? SecondaryFireIronAnim : SecondaryFireAnim; + } + + return super.GetWeaponFireAnim(FireModeNum); +} + +simulated function UpdateMaterial(bool HasEnoughAmmo) +{ + local LinearColor MatColor; + + if (WorldInfo.NetMode != NM_DedicatedServer) + { + MatColor = HasEnoughAmmo ? AmmoReadyMaterialColor : NoAmmoMaterialColor; + + if( WeaponMICs.Length >= `PARASITE_MIC_LED_INDEX ) + { + WeaponMICs[`PARASITE_MIC_LED_INDEX].SetVectorParameterValue('Vector_GlowColor', MatColor); + } + } +} + +/** + Should replicate to 3P to show the alt fire ready + */ +simulated function NotifyAltAmmoReady(bool bActive) +{ + local KFPawn KFP; + + if (WorldInfo.NetMode != NM_Client) + { + KFP = KFPawn(Instigator); + KFP.OnWeaponSpecialAction(bActive ? 2 : 1); + } +} + +simulated function ModifyRecoil( out float CurrentRecoilModifier ) +{ + if( CurrentFireMode == ALTFIRE_FIREMODE ) + { + CurrentRecoilModifier *= AltFireRecoilScale; + } + + super.ModifyRecoil( CurrentRecoilModifier ); +} + +simulated function bool ShouldAutoReload(byte FireModeNum) +{ + if (FireModeNum == ALTFIRE_FIREMODE) + return false; + + return super.ShouldAutoReload(FireModeNum); +} + +defaultproperties +{ + SeedFullRechargeSeconds=14 //10 + + // Inventory / Grouping + InventorySize=7 + GroupPriority=100 + WeaponSelectTexture=Texture2D'wep_ui_parasiteimplanter_tex.UI_WeaponSelect_ParasiteImplanter' + AssociatedPerkClasses(0)=class'KFPerk_FieldMedic' + AssociatedPerkClasses(1)=class'KFPerk_SharpShooter' + + // FOV + MeshFOV=70 //65 + MeshIronSightFOV=27 //25 //45 + PlayerIronSightFOV=70 //65 + + // Depth of field + DOF_BlendInSpeed=3.0 + DOF_FG_FocalRadius=0 //50 + DOF_FG_MaxNearBlurSize=3.5 + + // Content + PackageKey="ParasiteImplanter" + FirstPersonMeshName="WEP_1P_ParasiteImplanter_MESH.Wep_1stP_ParasiteImplanter_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_ParasiteImplanter_ANIM.Wep_1stP_ParasiteImplanter_Anim" + PickupMeshName="WEP_3P_ParasiteImplanter_MESH.Wep_3rdP_ParasiteImplanter_Pickup" + AttachmentArchetypeName="WEP_ParasiteImplanter_ARCH.Wep_ParasiteImplanter_3P" + MuzzleFlashTemplateName="WEP_ParasiteImplanter_ARCH.Wep_ParasiteImplanter_MuzzleFlash" + + // Ammo + MagazineCapacity[0]=6 + SpareAmmoCapacity[0]=78 + InitialSpareMags[0]=3 + bCanBeReloaded=true + bReloadFromMagazine=true + + MagazineCapacity[1]=100 + SeedAmmo=100 + bCanRefillSecondaryAmmo=false + + // Zooming/Position + PlayerViewOffset=(X=3.0,Y=8,Z=-1.8) + //IronSightPosition=(X=0,Y=-0.07,Z=1.03) + IronSightPosition=(X=0,Y=-0.115,Z=1.0425) + + // AI warning system + bWarnAIWhenAiming=true + AimWarningDelay=(X=0.4f, Y=0.8f) + AimWarningCooldown=0.0f + + // Recoil + maxRecoilPitch=575 //700 + minRecoilPitch=425 //600 + maxRecoilYaw=135 + minRecoilYaw=-135 + RecoilRate=0.08 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=1250 + RecoilMinPitchLimit=64785 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=500 + RecoilISMinPitchLimit=65485 + RecoilViewRotationScale=0.6 + IronSightMeshFOVCompensationScale=1.5 + AltFireRecoilScale=0.2f + + // Scope Render + // 2D scene capture + Begin Object Name=SceneCapture2DComponent0 + //TextureTarget=TextureRenderTarget2D'Wep_Mat_Lib.WEP_ScopeLense_Target' + FieldOfView=12.5 //23.0 // "1.5X" = 35.0(our real world FOV determinant)/1.5 + End Object + + ScopedSensitivityMod=8.0 //16.0 + ScopeLenseMICTemplate=MaterialInstanceConstant'WEP_1P_ParasiteImplanter_MAT.Wep_1stP_Parasite_Lens_MIC' + ScopeMICIndex = 2 + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_ParasiteImplanter' + InstantHitDamage(DEFAULT_FIREMODE)=275 //250 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_ParasiteImplanter' + FireInterval(DEFAULT_FIREMODE)=+1.0 //60 RPM + Spread(DEFAULT_FIREMODE)=0.005 + PenetrationPower(DEFAULT_FIREMODE)=3 + FireOffset=(X=25,Y=3.0,Z=-2.5) + LastFireInterval=0.3 + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + FireInterval(ALTFIRE_FIREMODE)=+0.5 //+1.0 //+0.175 + InstantHitDamage(ALTFIRE_FIREMODE)=1.0 + Spread(ALTFIRE_FIREMODE)=0.005 + AmmoCost(ALTFIRE_FIREMODE)=50 + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_ParasiteImplanterAlt' + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_ParasiteImplanterAlt' + SecondaryAmmoTexture=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity' + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_ParasiteImplanter' + InstantHitDamage(BASH_FIREMODE)=26 + + // Custom animations + FireSightedAnims=(Shoot_Iron) + BonesToLockOnEmpty=(RW_Hammer) + bHasFireLastAnims=true + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_3P_Rifle', FirstPersonCue=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_1P_Rifle') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_Dry_Fire_Rifle' + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_3P_Altfire', FirstPersonCue=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_1P_Altfire') + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_Dry_Fire_Altfire' + EjectedShellForegroundDuration=1.5f + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Medium_Recoil' + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + + // From original KFWeap_RifleBase base class + AimCorrectionSize=40.f + + NoAmmoMaterialColor=(R=0.0f,G=0.0f,B=0.0f) + AmmoReadyMaterialColor=(R=0.08f,G=1.0f,B=0.08f) + + NumBloodMapMaterials=2 +}