diff --git a/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc b/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc new file mode 100644 index 0000000..fc486f0 --- /dev/null +++ b/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc @@ -0,0 +1,36 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Locust +//============================================================================= +// HRG Locust bullet impact +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Toxic_HRG_Locust extends KFDT_Bleeding + abstract + hidedropdown; + +// Damage dealt when zeds touch each other and spread the afflictions +var int SpreadOnTouchDamage; + +static function int GetSpreadOnTouchDamage() +{ + return default.SpreadOnTouchDamage; +} + +defaultproperties +{ + DoT_Type=DOT_Bleeding + DoT_Duration=3.0 + DoT_Interval=0.5 + DoT_DamageScale=0.5 + + BleedPower = 20 + PoisonPower = 25 + + ModifierPerkList(0)=class'KFPerk_Survivalist' + + WeaponDef=class'KFWeapDef_HRG_Locust' + + SpreadOnTouchDamage=30 +} \ No newline at end of file diff --git a/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Fall2022.uc b/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Fall2022.uc new file mode 100644 index 0000000..b4682d3 --- /dev/null +++ b/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Fall2022.uc @@ -0,0 +1,27 @@ +class KFGFXSpecialEventObjectivesContainer_Fall2022 extends KFGFxSpecialEventObjectivesContainer; + +function Initialize(KFGFxObject_Menu NewParentMenu) +{ + super.Initialize(NewParentMenu); +} + +DefaultProperties +{ + ObjectiveIconURLs[0] = "Halloween2022_UI.UI_Objectives_Halloween2022_CreaturesInDarkness" // Kill 15 Bosses on any map or mode + ObjectiveIconURLs[1] = "Spring_UI.UI_Objectives_Spring_Weekly" // Complete the Weekly on BarmwichTown + ObjectiveIconURLs[2] = "Halloween2022_UI.UI_Objetives_Halloween2022_ElementalMedallions" // Open the Weapon Room + ObjectiveIconURLs[3] = "Halloween2022_UI.UI_Objectives_Halloween2022_ZedOnFire" // Make 50 Zeds to pass through the bonfires of Barmwitch Town + ObjectiveIconURLs[4] = "Halloween2022_UI.UI_Objetives_Halloween2022_LongNightofWitches" // Complete wave 15 on Endless Hard or higher difficulty on Barmwitch Town + + //defaults + AllCompleteRewardIconURL="CHR_PlagueDoctorUniform_Item_TEX.potionbackpack.plaguedoctorbackpack_precious_large" + ChanceDropIconURLs[0]="CHR_CosmeticSet14_Item_TEX.Tickets.CyberPunk_ticket" + ChanceDropIconURLs[1]="CHR_CosmeticSet14_Item_TEX.Tickets.CyberPunk_ticket_golden" + IconURL="halloween2022_ui.KF2_Halloween_BloodandBonfires_SmallLogo" + + UsesProgressList[0] = true + UsesProgressList[1] = false + UsesProgressList[2] = false + UsesProgressList[3] = true + UsesProgressList[4] = false +} \ No newline at end of file diff --git a/KFGame/Classes/KFGFxWidget_VIP.uc b/KFGame/Classes/KFGFxWidget_VIP.uc new file mode 100644 index 0000000..ad7359a --- /dev/null +++ b/KFGame/Classes/KFGFxWidget_VIP.uc @@ -0,0 +1,49 @@ +//============================================================================= +// KFGFxWidget_VIP +//============================================================================= +// HUD Widget that displays VIP messages to the player +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= + +class KFGFxWidget_VIP extends GFxObject; + +function SetVIP() +{ + SetString("VIPSetLocalised", Class'KFCommon_LocalizedStrings'.default.VIPString); + SetString("VIPObjectiveSetLocalised", Class'KFCommon_LocalizedStrings'.default.VIPObjectiveBString); + SetString("VIPPlayerSet", Class'KFCommon_LocalizedStrings'.default.VIPObjectiveCString); + + SetBool("VIPPlayerNameSetVisibility", true); + SetBool("VIPHideHealthBar", true); +} + +function SetNOVIP(string VIPPlayerName, int VIPCurrentHealth, int VIPMaxHealth) +{ + SetString("VIPSetLocalised", Class'KFCommon_LocalizedStrings'.default.VIPString); + SetString("VIPObjectiveSetLocalised", Class'KFCommon_LocalizedStrings'.default.VIPObjectiveAString); + SetString("VIPPlayerSet", VIPPlayerName); + + SetBool("VIPPlayerNameSetVisibility", true); + + UpdateHealth(VIPCurrentHealth, VIPMaxHealth); +} + +function UpdateVIPVisibility(bool visible) +{ + if (visible) + { + SetBool("VIPSetVisibility", true); + } + else + { + SetBool("VIPSetVisibility", false); + } +} + +function UpdateHealth(int VIPCurrentHealth, int VIPMaxHealth) +{ + SetFloat("VIPHealthBarPercentage", VIPMaxHealth != 0 ? (float(VIPCurrentHealth) / VIPMaxHealth) : 0.0f); +} \ No newline at end of file diff --git a/KFGame/Classes/KFWeapDef_G36C.uc b/KFGame/Classes/KFWeapDef_G36C.uc new file mode 100644 index 0000000..976169f --- /dev/null +++ b/KFGame/Classes/KFWeapDef_G36C.uc @@ -0,0 +1,29 @@ +//============================================================================= +// KFWeaponDefintion +//============================================================================= +// A lightweight container for basic weapon properties that can be safely +// accessed without a weapon actor (UI, remote clients). +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_G36C extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_AssaultRifle_G36C" + + BuyPrice=1600 + AmmoPricePerMag=36 + ImagePath="wep_ui_g36c_tex.UI_WeaponSelect_G36C" + + EffectiveRange=70 + + UpgradePrice[0]=1500 + + UpgradeSellPrice[0]=1125 + + SharedUnlockId=SCU_G36C + +} diff --git a/KFGame/Classes/KFWeapDef_HRG_Dragonbreath.uc b/KFGame/Classes/KFWeapDef_HRG_Dragonbreath.uc new file mode 100644 index 0000000..62aa50e --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_Dragonbreath.uc @@ -0,0 +1,24 @@ +//============================================================================= +// KFWeapDef_Dragonbreath +// This is the Doomstick +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2017 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_Dragonbreath extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_Dragonbreath" + + BuyPrice=1400 + AmmoPricePerMag=25 + ImagePath="WEP_UI_HRG_MegaDragonsbreath_TEX.UI_WeaponSelect_HRG_MegaDragonsbreath" + + EffectiveRange=25 + + UpgradePrice[0]=1500 + + UpgradeSellPrice[0]=1125 +} diff --git a/KFGame/Classes/KFWeapDef_HRG_Locust.uc b/KFGame/Classes/KFWeapDef_HRG_Locust.uc new file mode 100644 index 0000000..ed63eb3 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_Locust.uc @@ -0,0 +1,28 @@ +//============================================================================= +// KFWeapDef_HRG_Locust +//============================================================================= +// A lightweight container for basic weapon properties that can be safely +// accessed without a weapon actor (UI, remote clients). +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_Locust extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_Locust" + + BuyPrice=900 + AmmoPricePerMag=40 + ImagePath="wep_ui_hrg_locust_tex.UI_WeaponSelect_HRG_Locust" + + EffectiveRange=100 + + UpgradePrice[0]=700 + UpgradePrice[1]=1500 + + UpgradeSellPrice[0]=525 + UpgradeSellPrice[1]=1650 +} \ No newline at end of file diff --git a/KFGame/Classes/KFWeapDef_Scythe.uc b/KFGame/Classes/KFWeapDef_Scythe.uc new file mode 100644 index 0000000..55c4588 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_Scythe.uc @@ -0,0 +1,26 @@ +//============================================================================= +// KFWeapDef_Scythe +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_Scythe extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_Edged_Scythe" + + BuyPrice=1500 + ImagePath="WEP_UI_Scythe_TEX.UI_WeaponSelect_Scythe" + + EffectiveRange=5 + + UpgradePrice[0]=1500 + + UpgradeSellPrice[0]=1125 + + SharedUnlockId=SCU_Scythe + +} diff --git a/KFGameContent/Classes/KFBarmwichBonfireVolume.uc b/KFGameContent/Classes/KFBarmwichBonfireVolume.uc new file mode 100644 index 0000000..36531f0 --- /dev/null +++ b/KFGameContent/Classes/KFBarmwichBonfireVolume.uc @@ -0,0 +1,77 @@ +//============================================================================= +// KFBarmwichBonfireVolume +//============================================================================= +// Barmwich volume used for bonfires. Triggers seasonal progression. +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFBarmwichBonfireVolume extends KFPhysicsDamageByPawnVolume + placeable; + +/** Objective index for the event this is tied to */ +var() int ObjectiveIndex; + +/** Index of the event this is tied to */ +var() int EventIndex; + +var array AffectedActors; + +var transient bool bIsDataValid; + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + bIsDataValid = IsObjectiveDataValid(); +} + +function CausePainTo(Actor Other) +{ + Super.CausePainTo(Other); + + if (bIsDataValid && KFPawn_Monster(Other) != none && KFPawn_Monster(Other).IsAliveAndWell()) + { + if (AffectedActors.Find(Other) == INDEX_NONE) + { + AffectedActors.AddItem(Other); + NotifyProgression(); + } + } +} + +function NotifyProgression() +{ + local KFPlayerController KFPC; + + if (!bIsDataValid) + { + return; + } + + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + KFPC.ClientOnTryCompleteObjective(ObjectiveIndex, EventIndex); + } +} + +function ClearAllActors() +{ + AffectedActors.Remove(0, AffectedActors.Length); +} + +function ClearActor(Actor Other) +{ + AffectedActors.RemoveItem(Other); +} + +simulated function bool IsObjectiveDataValid() +{ + return ObjectiveIndex >= 0 && ObjectiveIndex < 5 && EventIndex > SEI_None && EventIndex < SEI_MAX; +} + +DefaultProperties +{ + bIsDataValid = false; +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Ballistic_G36C.uc b/KFGameContent/Classes/KFDT_Ballistic_G36C.uc new file mode 100644 index 0000000..d03a350 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_G36C.uc @@ -0,0 +1,87 @@ +//============================================================================= +// KFDT_Ballistic_G36C +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= + +class KFDT_Ballistic_G36C extends KFDT_Ballistic_AssaultRifle + abstract + hidedropdown; + +/** G36C has still to play a metal effect if impacting metal. (and we demoted to impact on Flesh) */ +var ParticleSystem MetalImpactEffect; +var AkEvent MetalImpactSound; + +static function PlayImpactHitEffects( KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, optional Pawn HitInstigator ) +{ + local KFSkinTypeEffects SkinType, OriginalSkinType, FleshSkinType; + local int i; + + if ( P.CharacterArch != None && default.EffectGroup < FXG_Max ) + { + // Search if affected target has Flesh skin type + for (i = 0; i < P.CharacterArch.ImpactSkins.Length ; ++i) + { + if (P.CharacterArch.ImpactSkins[i].Name == 'Flesh' + || P.CharacterArch.ImpactSkins[i].Name == 'Tough_Flesh') + { + FleshSkinType = P.CharacterArch.ImpactSkins[i]; + break; + } + } + + SkinType = P.GetHitZoneSkinTypeEffects( HitZoneIndex ); + OriginalSkinType = SkinType; + + // If we don't hit flesh or shield, try to demote to Flesh + if (SkinType != none && SkinType.Name != 'Flesh' && SkinType.Name != 'Tough_Flesh' && SkinType.Name != 'ShieldEffects') + { + // We default to none as we don't want bullet to ricochet if any + SkinType = none; + + // Demote to flesh skin hit + if (FleshSkinType != none) + { + SkinType = FleshSkinType; + } + } + + // If we hit metal we have to make sure we play it's Metal impact effect (this effect doesn't contain bullet ricochet) (don't play sound though!) + if (OriginalSkinType != none && (OriginalSkinType.Name == 'Metal' || OriginalSkinType.Name == 'Machine')) + { + OriginalSkinType.PlayImpactParticleEffect(P, HitLocation, HitDirection, HitZoneIndex, default.EffectGroup, default.MetalImpactEffect); + OriginalSkinType.PlayTakeHitSound(P, HitLocation, HitInstigator, default.EffectGroup, default.MetalImpactSound); + } + + if (SkinType != none) + { + SkinType.PlayImpactParticleEffect(P, HitLocation, HitDirection, HitZoneIndex, default.EffectGroup); + SkinType.PlayTakeHitSound(P, HitLocation, HitInstigator, default.EffectGroup); + } + } +} + +defaultproperties +{ + KDamageImpulse=900 + KDeathUpKick=-300 + KDeathVel=100 + + DamageModifierAP=0.8f + ArmorDamageModifier=15.0f + + StumblePower=15 + GunHitPower=15 + + WeaponDef=class'KFWeapDef_G36C' + + //Perk + ModifierPerkList(0)=class'KFPerk_Swat' + + MetalImpactEffect=ParticleSystem'FX_Impacts_EMIT.FX_Wep_Impact_MetalArmor_E' + MetalImpactSound=AkEvent'WW_Skin_Impacts.Play_Slashing_Metal_3P' +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_Dragonbreath.uc new file mode 100644 index 0000000..1f7ce9a --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_Dragonbreath.uc @@ -0,0 +1,82 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Dragonbreath +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_HRG_Dragonbreath extends KFDT_Ballistic_Shotgun + abstract + hidedropdown; + +// Damage type to use for the burning damage over time +var class BurnDamageType; + +/** 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': + case 'chest': + case 'heart': + return true; + } + + return false; +} + +/** Play damage type specific impact effects when taking damage */ +static function PlayImpactHitEffects( KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, optional Pawn HitInstigator ) +{ + // Play burn effect when dead + if( P.bPlayedDeath && P.WorldInfo.TimeSeconds > P.TimeOfDeath ) + { + default.BurnDamageType.static.PlayImpactHitEffects(P, HitLocation, HitDirection, HitZoneIndex, HitInstigator); + return; + } + + super.PlayImpactHitEffects(P, HitLocation, HitDirection, HitZoneIndex, HitInstigator); +} + +/** 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 ) +{ + // Overriden to specific a different damage type to do the burn damage over + // time. We do this so we don't get shotgun pellet impact sounds/fx during + // the DOT burning. + if ( default.BurnDamageType.default.DoT_Type != DOT_None ) + { + Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, default.BurnDamageType); + } +} + +defaultproperties +{ + GoreDamageGroup=DGT_Shotgun + BloodSpread=0.4 + BloodScale=0.6 + + KDamageImpulse=3500 + KDeathUpKick=800 + KDeathVel=650 + GibImpulseScale=1.0 + + BurnPower=40 + StumblePower=40 + GunHitPower=50 + + WeaponDef=class'KFWeapDef_HRG_Dragonbreath' + + BurnDamageType=class'KFDT_Fire_HRG_DragonsBreathDoT' + + EffectGroup=FXG_IncendiaryRound + + ModifierPerkList(0)=class'KFPerk_Firebug' +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_Locust.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_Locust.uc new file mode 100644 index 0000000..53696e1 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_Locust.uc @@ -0,0 +1,18 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Locust +//============================================================================= +// HRG Locust bullet impact +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Ballistic_HRG_Locust extends KFDamageType + abstract + hidedropdown; + +defaultproperties +{ + ModifierPerkList(0)=class'KFPerk_Survivalist' + + WeaponDef=class'KFWeapDef_HRG_Locust' +} diff --git a/KFGameContent/Classes/KFDT_Bludgeon_G36C.uc b/KFGameContent/Classes/KFDT_Bludgeon_G36C.uc new file mode 100644 index 0000000..bf9c765 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_G36C.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_G36C +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_G36C extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_G36C' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Dragonbreath.uc new file mode 100644 index 0000000..39d9dda --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Dragonbreath.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_Dragonbreath +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2017 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_Dragonbreath extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_Dragonbreath' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_Locust.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Locust.uc new file mode 100644 index 0000000..7c38919 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Locust.uc @@ -0,0 +1,15 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_Locust +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Bludgeon_HRG_Locust extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_Locust' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Explosive_HRG_Locust.uc b/KFGameContent/Classes/KFDT_Explosive_HRG_Locust.uc new file mode 100644 index 0000000..249f276 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_HRG_Locust.uc @@ -0,0 +1,40 @@ +//============================================================================= +// KFDT_Explosive_HRG_Locust +//============================================================================= +// Explosive damage type for the HRG Locust +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Explosive_HRG_Locust extends KFDT_Explosive + abstract + hidedropdown; + +// Damage type to use for the damage over time effect +var class DoTDamageType; + +/** 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) +{ + if (Victim.Controller == InstigatedBy) + { + return; + } + + if (default.DoTDamageType.default.DoT_Type != DOT_None) + { + Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, default.DoTDamageType); + } +} + +defaultproperties +{ + //Perk + ModifierPerkList(0)=class'KFPerk_Survivalist' + WeaponDef=class'KFWeapDef_HRG_Locust' + + DoTDamageType=class'KFDT_Toxic_HRG_Locust' + + bCausesFracture=false + bCanPlayDeadHitEffects=false +} diff --git a/KFGameContent/Classes/KFDT_Fire_Ground_HRG_DragonBreath.uc b/KFGameContent/Classes/KFDT_Fire_Ground_HRG_DragonBreath.uc new file mode 100644 index 0000000..6d73673 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Fire_Ground_HRG_DragonBreath.uc @@ -0,0 +1,39 @@ +//============================================================================= +// KFDT_Fire_Ground_HRG_DragonBreath +//============================================================================= +// Damage caused by burning from being hit by a HRG dragon breath ground fire +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Fire_Ground_HRG_DragonBreath extends KFDT_Fire_Ground + abstract; + +static function int GetKillerDialogID() +{ + return 86;//KILL_Fire +} + +static function int GetDamagerDialogID() +{ + return 102;//DAMZ_Fire +} + +static function int GetDamageeDialogID() +{ + return 116;//DAMP_Fire +} + +defaultproperties +{ + WeaponDef=class'KFWeapDef_HRG_Dragonbreath' + + DoT_Type=DOT_Fire + DoT_Duration=2.7 + DoT_Interval=0.5 + DoT_DamageScale=0.7 + + BurnPower=10 +} + diff --git a/KFGameContent/Classes/KFDT_Fire_HRG_DragonsBreathDoT.uc b/KFGameContent/Classes/KFDT_Fire_HRG_DragonsBreathDoT.uc new file mode 100644 index 0000000..c9a9d80 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Fire_HRG_DragonsBreathDoT.uc @@ -0,0 +1,41 @@ +//============================================================================= +// KFDT_Fire_HRG_DragonsBreathDoT +//============================================================================= +// Damage caused by burning from being hit by an HRG dragon's breath round +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= + +class KFDT_Fire_HRG_DragonsBreathDoT extends KFDT_Fire + abstract + hidedropdown; + +static function int GetKillerDialogID() +{ + return 86;//KILL_Fire +} + +static function int GetDamagerDialogID() +{ + return 102;//DAMZ_Fire +} + +static function int GetDamageeDialogID() +{ + return 116;//DAMP_Fire +} + +defaultproperties +{ + WeaponDef=class'KFWeapDef_HRG_Dragonbreath' + + DoT_Type=DOT_Fire + DoT_Duration=2.7 //5.0 //1.0 + DoT_Interval=0.5 + DoT_DamageScale=0.7 //1.0 + + BurnPower=10 //1.0 //18.5 +} + diff --git a/KFGameContent/Classes/KFDT_Piercing_ScytheStab.uc b/KFGameContent/Classes/KFDT_Piercing_ScytheStab.uc new file mode 100644 index 0000000..bc546ea --- /dev/null +++ b/KFGameContent/Classes/KFDT_Piercing_ScytheStab.uc @@ -0,0 +1,23 @@ +//============================================================================= +// KFDT_Piercing_ScytheStab +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Piercing_ScytheStab extends KFDT_Piercing + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=200 + KDeathUpKick=250 + + StumblePower=50 + MeleeHitPower=100 + + WeaponDef=class'KFWeapDef_Scythe' + + ModifierPerkList(0)=class'KFPerk_Berserker' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Piercing_ScytheStabFolded.uc b/KFGameContent/Classes/KFDT_Piercing_ScytheStabFolded.uc new file mode 100644 index 0000000..60b1b40 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Piercing_ScytheStabFolded.uc @@ -0,0 +1,23 @@ +//============================================================================= +// KFDT_Piercing_ScytheStabFolded +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Piercing_ScytheStabFolded extends KFDT_Piercing + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=200 + KDeathUpKick=250 + + StumblePower=50 + MeleeHitPower=100 + + WeaponDef=class'KFWeapDef_Scythe' + + ModifierPerkList(0)=class'KFPerk_Berserker' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Slashing_ScytheLong.uc b/KFGameContent/Classes/KFDT_Slashing_ScytheLong.uc new file mode 100644 index 0000000..8cef9d6 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Slashing_ScytheLong.uc @@ -0,0 +1,140 @@ +//============================================================================= +// KFDT_Slashing_ScytheLong +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Slashing_ScytheLong 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 +{ + KDamageImpulse=10000 //1500 + KDeathUpKick=2000 //200 + KDeathVel=3750 //375 + + KnockdownPower=0 + StunPower=0 + StumblePower=100 + MeleeHitPower=100 + + WeaponDef=class'KFWeapDef_Scythe' + ModifierPerkList(0)=class'KFPerk_Berserker' +} diff --git a/KFGameContent/Classes/KFDT_Slashing_ScytheLongAlt.uc b/KFGameContent/Classes/KFDT_Slashing_ScytheLongAlt.uc new file mode 100644 index 0000000..2d6c7dd --- /dev/null +++ b/KFGameContent/Classes/KFDT_Slashing_ScytheLongAlt.uc @@ -0,0 +1,38 @@ +//============================================================================= +// KFDT_Slashing_ScytheLongAlt +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Slashing_ScytheLongAlt extends KFDT_Slashing_ZweihanderHeavy + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=3600 //1600 + KDeathUpKick=400 //200 + KDeathVel=750 //500 + + KnockdownPower=50 + StunPower=0 + StumblePower=200 + MeleeHitPower=100 + + // Obliteration + GoreDamageGroup = DGT_Explosive + RadialDamageImpulse = 8000.f // This controls how much impulse is applied to gibs when exploding + bUseHitLocationForGibImpulses = true // This will make the impulse origin where the victim was hit for directional gibs + bPointImpulseTowardsOrigin = true // This creates an impulse direction aligned along hitlocation and pawn location -- this will push all gibs in the same direction + ImpulseOriginScale = 100.f // Higher means more directional gibbing, lower means more outward (and upward) gibbing + ImpulseOriginLift = 150.f + MaxObliterationGibs = 12 // Maximum number of gibs that can be spawned by obliteration, 0=MAX + bCanGib = true + bCanObliterate = true + ObliterationHealthThreshold = 0 + ObliterationDamageThreshold = 100 + + WeaponDef=class'KFWeapDef_Scythe' + ModifierPerkList(0)=class'KFPerk_Berserker' +} diff --git a/KFGameContent/Classes/KFDT_Slashing_ScytheShort.uc b/KFGameContent/Classes/KFDT_Slashing_ScytheShort.uc new file mode 100644 index 0000000..945af04 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Slashing_ScytheShort.uc @@ -0,0 +1,140 @@ +//============================================================================= +// KFDT_Slashing_ScytheShort +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Slashing_ScytheShort 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 +{ + KDamageImpulse=10000 //1500 + KDeathUpKick=2000 //200 + KDeathVel=3750 //375 + + KnockdownPower=0 + StunPower=0 + StumblePower=50 + MeleeHitPower=100 + + WeaponDef=class'KFWeapDef_Scythe' + ModifierPerkList(0)=class'KFPerk_Berserker' +} diff --git a/KFGameContent/Classes/KFDT_Slashing_ScytheShortAlt.uc b/KFGameContent/Classes/KFDT_Slashing_ScytheShortAlt.uc new file mode 100644 index 0000000..c067e43 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Slashing_ScytheShortAlt.uc @@ -0,0 +1,38 @@ +//============================================================================= +// KFDT_Slashing_ScytheShortAlt +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Slashing_ScytheShortAlt extends KFDT_Slashing_ZweihanderHeavy + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=3600 //1600 + KDeathUpKick=400 //200 + KDeathVel=750 //500 + + KnockdownPower=0 + StunPower=0 + StumblePower=150 + MeleeHitPower=100 + + // Obliteration + GoreDamageGroup = DGT_Explosive + RadialDamageImpulse = 8000.f // This controls how much impulse is applied to gibs when exploding + bUseHitLocationForGibImpulses = true // This will make the impulse origin where the victim was hit for directional gibs + bPointImpulseTowardsOrigin = true // This creates an impulse direction aligned along hitlocation and pawn location -- this will push all gibs in the same direction + ImpulseOriginScale = 100.f // Higher means more directional gibbing, lower means more outward (and upward) gibbing + ImpulseOriginLift = 150.f + MaxObliterationGibs = 12 // Maximum number of gibs that can be spawned by obliteration, 0=MAX + bCanGib = true + bCanObliterate = true + ObliterationHealthThreshold = 0 + ObliterationDamageThreshold = 100 + + WeaponDef=class'KFWeapDef_Scythe' + ModifierPerkList(0)=class'KFPerk_Berserker' +} diff --git a/KFGameContent/Classes/KFExplosion_HRG_Dragonbreath_GroundFire.uc b/KFGameContent/Classes/KFExplosion_HRG_Dragonbreath_GroundFire.uc new file mode 100644 index 0000000..a577e7a --- /dev/null +++ b/KFGameContent/Classes/KFExplosion_HRG_Dragonbreath_GroundFire.uc @@ -0,0 +1,33 @@ +//============================================================================= +// KFExplosion_HRG_Dragonbreath_GroundFire +//============================================================================= +// Explosion actor class for ground fire +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFExplosion_HRG_Dragonbreath_GroundFire extends KFExplosionActorLingering; + + +simulated function SpawnExplosionParticleSystem(ParticleSystem Template) +{ + // If the template is none, grab the default + if( !ExplosionTemplate.bAllowPerMaterialFX && Template == none ) + { + Template = KFGameExplosion(ExplosionTemplate).ExplosionEffects.DefaultImpactEffect.ParticleTemplate; + } + + // Use custom pool + WorldInfo.GroundFireEmitterPool.SpawnEmitter(Template, Location, rotator(ExplosionTemplate.HitNormal), None); +} + +DefaultProperties +{ + //Interval=0.25f INTERVAL IS OVERRIDDEN BY ITS PROJECTILE + MaxTime=2.0 + + ExplosionLightPriority=LPP_Low + LoopStartEvent=AkEvent'ww_wep_hrg_megadragonbreath.Play_WEP_HRG_MegaDragonbreath_Flame_LP' + LoopStopEvent=AkEvent'ww_wep_hrg_megadragonbreath.Stop_WEP_HRG_MegaDragonbreath_End_Flame_LP' +} diff --git a/KFGameContent/Classes/KFExplosion_HRG_Locust.uc b/KFGameContent/Classes/KFExplosion_HRG_Locust.uc new file mode 100644 index 0000000..064c7fa --- /dev/null +++ b/KFGameContent/Classes/KFExplosion_HRG_Locust.uc @@ -0,0 +1,22 @@ +//============================================================================= +// KFExplosion_HRG_Locust +//============================================================================= +// Explosion actor class for HRG Locust +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFExplosion_HRG_Locust extends KFExplosionActorLingering; + +DefaultProperties +{ + Interval=0.5f + MaxTime=3.0 + + bOnlyDamagePawns=true + bDoFullDamage=false + + LoopStartEvent=AkEvent'WW_WEP_HRG_Locust.Play_WEP_HRG_Locust_Insects' + LoopStopEvent=AkEvent'WW_WEP_HRG_Locust.Stop_WEP_HRG_Locust_Insect' +} diff --git a/KFGameContent/Classes/KFProj_HRG_Dragonbreath_Splash.uc b/KFGameContent/Classes/KFProj_HRG_Dragonbreath_Splash.uc new file mode 100644 index 0000000..e8da41b --- /dev/null +++ b/KFGameContent/Classes/KFProj_HRG_Dragonbreath_Splash.uc @@ -0,0 +1,25 @@ +//============================================================================= +// KFProj_HRG_DragonBreath_Splash +//============================================================================= +// Projectile class for hrg dragonbreath gun splash. Handles a few overrides. +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_HRG_DragonBreath_Splash extends KFProj_FlareGunSplash; + +defaultproperties +{ + PostExplosionLifetime=2.5 + ExplosionActorClass=class'KFExplosion_HRG_Dragonbreath_GroundFire' + + Begin Object Name=ExploTemplate0 + Damage=8 + DamageRadius=150.0 + MyDamageType=class'KFDT_Fire_Ground_HRG_DragonBreath' + ExplosionEffects=KFImpactEffectInfo'WEP_Flamethrower_ARCH.GroundFire_Impacts' + End Object + + AssociatedPerkClass=none +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFProj_HRG_Locust.uc b/KFGameContent/Classes/KFProj_HRG_Locust.uc new file mode 100644 index 0000000..90cad39 --- /dev/null +++ b/KFGameContent/Classes/KFProj_HRG_Locust.uc @@ -0,0 +1,171 @@ +//============================================================================= +// KFProj_HRG_Locust +//============================================================================= +// HRG Locust projectile +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= +class KFProj_HRG_Locust extends KFProj_BallisticExplosive + hidedropdown; + +/** Our intended target actor */ +var private KFPawn LockedTarget; + +/** How much 'stickyness' when seeking toward our target. Determines how accurate rocket is */ +var const float SeekStrength; + +replication +{ + if( bNetInitial ) + LockedTarget; +} + +function SetLockedTarget( KFPawn NewTarget ) +{ + LockedTarget = NewTarget; +} + +simulated function bool AllowNuke() +{ + return false; +} + +simulated function bool AllowDemolitionistConcussive() +{ + return false; +} + +simulated function bool AllowDemolitionistExplosionChangeRadius() +{ + return false; +} + +simulated event Tick( float DeltaTime ) +{ + local vector TargetImpactPos, DirToTarget; + + super.Tick( DeltaTime ); + + // Skip the first frame, then start seeking + if( !bHasExploded + && LockedTarget != none + && Physics == PHYS_Projectile + && Velocity != vect(0,0,0) + && LockedTarget.IsAliveAndWell() + && `TimeSince(CreationTime) > 0.03f ) + { + // Grab our desired relative impact location from the weapon class + TargetImpactPos = class'KFWeap_HRG_Locust'.static.GetLockedTargetLoc( LockedTarget ); + + // Seek towards target + Speed = VSize( Velocity ); + DirToTarget = Normal( TargetImpactPos - Location ); + Velocity = Normal( Velocity + (DirToTarget * (SeekStrength * DeltaTime)) ) * Speed; + + // Aim rotation towards velocity every frame + SetRotation( rotator(Velocity) ); + } +} + +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 *= KFPawn(OwnerPawn).GetPerk().GetAoERadiusModifier(); + } + } + } +} + +defaultproperties +{ + Physics=PHYS_Projectile + Speed=4000 //6000 + MaxSpeed=4000 //6000 + TossZ=0 + GravityScale=1.0 + MomentumTransfer=0.0f + + Damage=10 + DamageRadius=0 + + SeekStrength=928000.0f // 128000.0f + + bWarnAIWhenFired=true + + ProjFlightTemplate=ParticleSystem'WEP_HRG_Locust_EMIT.FX_HRG_Locust_Projectile' + ProjFlightTemplateZedTime=ParticleSystem'WEP_HRG_Locust_EMIT.FX_HRG_Locust_Projectile_ZED_TIME' + ProjDisintegrateTemplate=ParticleSystem'WEP_HRG_Locust_EMIT.FX_Flying_Bugs_dispersion' + + AmbientSoundPlayEvent=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Projectile' + AmbientSoundStopEvent=AkEvent'WW_WEP_Seeker_6.Stop_WEP_Seeker_6_Projectile' + + ExplosionActorClass=class'KFExplosion_HRG_Locust' + + AltExploEffects=KFImpactEffectInfo'WEP_HRG_Locust_ARCH.FX_HRG_Locust_Explosion_Concussive_force' + + // Grenade explosion light + Begin Object Class=PointLightComponent Name=ExplosionPointLight + LightColor=(R=252,G=218,B=171,A=255) + Brightness=4.f + Radius=2000.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=60 + DamageRadius=200 + DamageFalloffExponent=0.5f + DamageDelay=0.f + + //Impulse applied to Zeds + MomentumTransferScale=1 + + // Damage Effects + MyDamageType=class'KFDT_Explosive_HRG_Locust' + KnockDownStrength=0 + FractureMeshRadius=0.0 + FracturePartVel=0.0 + ExplosionEffects=KFImpactEffectInfo'WEP_HRG_Locust_ARCH.FX_HRG_Locust_Explosion_Concussive_force' + ExplosionSound=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Explosion' + + // Dynamic Light + ExploLight=ExplosionPointLight + ExploLightStartFadeOutTime=0.0 + ExploLightFadeOutTime=0.2 + + // Camera Shake + CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' + CamShakeInnerRadius=0 + CamShakeOuterRadius=500 + CamShakeFalloff=3.f + bOrientCameraShakeTowardsEpicenter=true + + bIgnoreInstigator=true + End Object + ExplosionTemplate=ExploTemplate0 + + bCanDisintegrate=false +} diff --git a/KFGameContent/Classes/KFProj_Pellet_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFProj_Pellet_HRG_Dragonbreath.uc new file mode 100644 index 0000000..3fac59d --- /dev/null +++ b/KFGameContent/Classes/KFProj_Pellet_HRG_Dragonbreath.uc @@ -0,0 +1,85 @@ +//============================================================================= +// KFProj_Pellet_HRG_Dragonbreath +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Pellet_HRG_Dragonbreath extends KFProj_Bullet_Pellet + hidedropdown; + + +/** Last hit normal from Touch() or HitWall() */ +var vector LastHitNormal; + +var float GroundFireChance; + + +simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal) +{ + LastHitNormal = HitNormal; + Super.ProcessTouch(Other, HitLocation, HitNormal); +} + +/** +* Explode this Projectile +*/ +simulated function TriggerExplosion(Vector HitLocation, Vector HitNormal, Actor HitActor) +{ + LastHitNormal = HitNormal; + Super.TriggerExplosion(HitLocation, HitNormal, HitActor); +} + + +simulated protected function StopSimulating() +{ + local vector FlameSpawnVel; + + if (Role == ROLE_Authority && Physics == PHYS_Falling && FRand() < GroundFireChance) + { + //SpawnGroundFire(); + FlameSpawnVel = 0.25f * CalculateResidualFlameVelocity(LastHitNormal, Normal(Velocity), VSize(Velocity)); + SpawnResidualFlame(class'KFProj_HRG_DragonBreath_Splash', Location + (LastHitNormal * 10.f), FlameSpawnVel); + } + + super.StopSimulating(); +} + +defaultproperties +{ + GroundFireChance=1.f + Physics=PHYS_Falling + + MaxSpeed=7000.0 + Speed=7000.0 + TerminalVelocity=7000.0 + + bWarnAIWhenFired=true + + DamageRadius=0 + GravityScale=0.35 + TossZ=0 + + Begin Object Class=PointLightComponent Name=PointLight0 + LightColor=(R=252,G=218,B=171,A=255) + Brightness=0.5f + Radius=500.f + FalloffExponent=10.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=False + bCastPerObjectShadows=false + bEnabled=true + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE) + End Object + ProjFlightLight=PointLight0 + + ImpactEffects=KFImpactEffectInfo'WEP_DragonsBreath_ARCH.DragonsBreath_bullet_impact' + ProjFlightTemplate=ParticleSystem'WEP_DragonsBreath_EMIT.Tracer.FX_DragonsBreath_Tracer' + ProjFlightTemplateZedTime=ParticleSystem'WEP_DragonsBreath_EMIT.Tracer.FX_DragonsBreath_Tracer_ZEDTime' + + AmbientSoundPlayEvent=AkEvent'WW_WEP_SA_DragonsBreath.Play_SA_DragonsBreath_Projectile_Loop' + AmbientSoundStopEvent=AkEvent'WW_WEP_SA_DragonsBreath.Stop_SA_DragonsBreath_Projectile_Loop' +} + diff --git a/KFGameContent/Classes/KFSeasonalEventStats_Fall2022.uc b/KFGameContent/Classes/KFSeasonalEventStats_Fall2022.uc new file mode 100644 index 0000000..83021fc --- /dev/null +++ b/KFGameContent/Classes/KFSeasonalEventStats_Fall2022.uc @@ -0,0 +1,128 @@ +//============================================================================= +// KFSeasonalEventStats_Fall2022 +//============================================================================= +// Tracks event-specific challenges/accomplishments for Fall 2022 +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= +class KFSeasonalEventStats_Fall2022 extends KFSeasonalEventStats; + +var transient private const int BossKillsRequired, ZedsInBonfiresRequired, 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 BarmwichTown + bObjectiveIsValidForMap[2] = 0; // Open the Weapon Room + bObjectiveIsValidForMap[3] = 0; // Make 50 Zeds to pass through the bonfires of Barmwitch Town + bObjectiveIsValidForMap[4] = 0; // Complete wave 15 on Endless Hard or higher difficulty on Barmwitch Town + + if (CapsMapName == "KF-BARMWICHTOWN") + { + bObjectiveIsValidForMap[1] = 1; + bObjectiveIsValidForMap[2] = 1; + bObjectiveIsValidForMap[3] = 1; + bObjectiveIsValidForMap[4] = 1; + } + + SetSeasonalEventStatsMax(BossKillsRequired, 0, 0, ZedsInBonfiresRequired, EndlessWaveRequired); +} + +private event GrantEventItems() +{ + if (Outer.IsEventObjectiveComplete(0) && + Outer.IsEventObjectiveComplete(1) && + Outer.IsEventObjectiveComplete(2) && + Outer.IsEventObjectiveComplete(3) && + Outer.IsEventObjectiveComplete(4)) + { + GrantEventItem(9424); + } +} + +// 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 WeaponRoomIdx, BonfireIdx; + WeaponRoomIdx = 2; + BonfireIdx = 3; + + if(EventIndex == SEI_Fall) + { + if (ObjectiveIndex == WeaponRoomIdx) + { + if (bObjectiveIsValidForMap[ObjectiveIndex] != 0) + { + FinishedObjective(SEI_Fall, ObjectiveIndex); + } + } + else if (ObjectiveIndex == BonfireIdx) + { + if (bObjectiveIsValidForMap[ObjectiveIndex] != 0) + { + IncrementSeasonalEventStat(ObjectiveIndex, 1); + if (Outer.GetSeasonalEventStatValue(ObjectiveIndex) >= ZedsInBonfiresRequired) + { + FinishedObjective(SEI_Fall, ObjectiveIndex); + } + } + } + } +} + +defaultproperties +{ + BossKillsRequired=15 + EndlessWaveRequired=15 + ZedsInBonfiresRequired=50 +} diff --git a/KFGameContent/Classes/KFWeapAttach_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFWeapAttach_HRG_Dragonbreath.uc new file mode 100644 index 0000000..360f6d2 --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_HRG_Dragonbreath.uc @@ -0,0 +1,83 @@ +//============================================================================= +// KFWeapAttach_HRG_Dragonbreath +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeapAttach_HRG_Dragonbreath extends KFWeaponAttachment; + +`define HRGDRAGONBREATH_MIC_BARREL_INDEX 0 + +var transient float BarrelHeatPerProjectile; +var transient float MaxBarrelHeat; +var transient float BarrelCooldownRate; +var transient float CurrentBarrelHeat; +var transient float LastBarrelHeat; +var transient int NumPelletsDefault; +var transient int NumPelletsAlt; + +simulated event PreBeginPlay() +{ + Super.PreBeginPlay(); + + BarrelHeatPerProjectile = class'KFWeap_HRG_Dragonbreath'.default.BarrelHeatPerProjectile; + MaxBarrelHeat = class'KFWeap_HRG_Dragonbreath'.default.MaxBarrelHeat; + BarrelCooldownRate = class'KFWeap_HRG_Dragonbreath'.default.BarrelCooldownRate; + NumPelletsDefault = class'KFWeap_HRG_Dragonbreath'.default.NumPellets[0]; + NumPelletsAlt = class'KFWeap_HRG_Dragonbreath'.default.NumPellets[1]; +} + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + // Force start with "Glow_Intensity" of 0.0f + LastBarrelHeat = MaxBarrelHeat; + ChangeBarrelMaterial(); +} + +simulated function ChangeBarrelMaterial() +{ + if( CurrentBarrelHeat != LastBarrelHeat ) + { + if ( WeaponMIC == None && WeapMesh != None ) + { + WeaponMIC = WeapMesh.CreateAndSetMaterialInstanceConstant(`HRGDRAGONBREATH_MIC_BARREL_INDEX); + } + + WeaponMIC.SetScalarParameterValue('Barrel_intensity', CurrentBarrelHeat); + } +} + +simulated function Tick(float Delta) +{ + Super.Tick(Delta); + + CurrentBarrelHeat = fmax(CurrentBarrelHeat - BarrelCooldownRate * Delta, 0.0f); + ChangeBarrelMaterial(); +} + +/** Override to update emissive in weapon's barrel after firing */ +simulated function PlayWeaponFireAnim() +{ + local float BarrelHeatPerShot; + local KFPawn OwnerPawn; + + Super.PlayWeaponFireAnim(); + + OwnerPawn = KFPawn(Owner); + + BarrelHeatPerShot = BarrelHeatPerProjectile * (OwnerPawn.FiringMode == 0 ? NumPelletsDefault : NumPelletsAlt); + CurrentBarrelHeat = fmin(CurrentBarrelHeat + BarrelHeatPerShot, MaxBarrelHeat); +} + +defaultproperties +{ + CurrentBarrelHeat=0.0f + LastBarrelHeat=0.0f + NumPelletsDefault=0 + NumPelletsAlt=0 +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_Scythe.uc b/KFGameContent/Classes/KFWeapAttach_Scythe.uc new file mode 100644 index 0000000..6056a8a --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_Scythe.uc @@ -0,0 +1,144 @@ +//============================================================================= +// KFWeapAttach_Scythe +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeapAttach_Scythe extends KFWeaponAttachment; + +var const float UnfoldBlendingDuration; +var const float UnfoldedAnimRateModifier; +const FoldAnim = 'Clean_NoBlood'; + +var AnimTree CustomAnimTree; +var AnimNodeBlendPerBone FoldBlendNode; + +var transient bool bIsFolded; + +event PreBeginPlay() +{ + Super.PreBeginPlay(); + + // Override the animtree. Doing this here (before AttachTo) instead of in defaultprops + // avoids an undesired call to our owning Pawn's PostInitAnimTree + if ( CustomAnimTree != None ) + { + WeapMesh.SetAnimTreeTemplate(CustomAnimTree); + + WeapAnimNode = AnimNodeSequence(WeapMesh.FindAnimNode('WeaponSeq')); + FoldBlendNode = AnimNodeBlendPerBone(WeapMesh.FindAnimNode('FoldBlendNode')); + + // The special event might have arrived before the attachment is created, but it's updated in the owner, so copy the state here... + if (KFPawn(Owner) != none && FoldBlendNode != none) + { + bIsFolded = KFPawn(Owner).WeaponSpecialAction == 0; + FoldBlendNode.SetBlendTarget(bIsFolded ? 1.0f : 0.0f, 0.f); + } + } +} + +simulated function ChangeMode() +{ + bIsFolded = !bIsFolded; + + // FoldControl = SkelControlSingleBone( WeapMesh.FindSkelControl('FoldControl') ); + if( FoldBlendNode != none ) + { + FoldBlendNode.SetBlendTarget( bIsFolded ? 1.0f : 0.0f, 0.0f ); + } +} + +/** Called from the pawn when our first person weapon changes states */ +simulated function UpdateThirdPersonWeaponAction(EWeaponState NewWeaponState, KFPawn P, byte ThirdPersonAnimRateByte ) +{ + Super.UpdateThirdPersonWeaponAction(NewWeaponState, P, ThirdPersonAnimRate); + + if (NewWeaponState == WEP_Cleaning) + { + if (WeapAnimNode != none) + { + if (WeapAnimNode.AnimSeq == none) + { + WeapAnimNode.SetAnim(FoldAnim); + } + + WeapAnimNode.PlayAnim(); + } + } +} + +simulated function ANIMNOTIFY_ShellEject() +{ + ChangeMode(); +} + +/** + * Plays a split (upper and lower body) animation on the owning pawn + * Network: All but dedicated + * + * @param P Owning pawn to play animation on + * @param AnimName Anim to play + * @param bPlaySynchronizedWeaponAnim If true, try to play the same animation on the weapon mesh + */ +simulated function float PlayCharacterMeshAnim(KFPawn P, name AnimName, optional bool bPlaySynchedWeaponAnim, optional bool bLooping) +{ + local float AnimRate; + local float Duration; + local EAnimSlotStance Stance; + local string AnimStr; + + // skip weapon anims while in a special move + if( P.IsDoingSpecialMove() && !P.SpecialMoves[P.SpecialMove].bAllowThirdPersonWeaponAnims ) + { + return 0.f; + } + + Stance = (!P.bIsCrouched) ? EAS_UpperBody : EAS_CH_UpperBody; + + AnimRate = ThirdPersonAnimRate; + AnimStr = Caps(string(AnimName)); + + if (!bIsFolded && (InStr(AnimStr, "ATK") != INDEX_NONE || InStr(AnimName, "COMB") != INDEX_NONE)) + { + AnimRate *= UnfoldedAnimRateModifier; + } + + Duration = P.PlayBodyAnim(AnimName, Stance, AnimRate, DefaultBlendInTime, DefaultBlendOutTime, bLooping); + + if ( Duration > 0 && bPlaySynchedWeaponAnim ) + { + PlayWeaponMeshAnim(AnimName, P.BodyStanceNodes[Stance], bLooping); + } + + `log(GetFuncName()@"called on:"$P@"Anim:"$AnimName@"Duration:"$Duration, bDebug); + + return Duration; +} + +/** Special event added for weap attachments. Free for use */ +function OnSpecialEvent(int Arg) +{ + bIsFolded = Arg == 0; + + // FoldControl = SkelControlSingleBone( WeapMesh.FindSkelControl('FoldControl') ); + if( FoldBlendNode != none ) + { + FoldBlendNode.SetBlendTarget( bIsFolded ? 1.0f : 0.0f, 0.0f ); + } +} + +defaultproperties +{ + CustomAnimTree=AnimTree'WEP_Scythe_ARCH.3P_Scythe_Animtree' + bIsFolded=true; + UnfoldBlendingDuration=0.25f + UnfoldedAnimRateModifier=0.7f; + + // Weapon SkeletalMesh + Begin Object Name=SkeletalMeshComponent0 + bForceRefPose=0 + End Object +} diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc new file mode 100644 index 0000000..66a0139 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc @@ -0,0 +1,174 @@ +//============================================================================= +// KFWeap_AssaultRifle_G36C +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_AssaultRifle_G36C extends KFWeap_SMGBase; + +simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo) +{ + super.ZoomIn(bAnimateTransition, ZoomTimeToGo); + + if (LaserSight != none) + { + LaserSight.ChangeVisibility(false); + } +} + +simulated function ZoomOut( bool bAnimateTransition, float ZoomTimeToGo ) +{ + super.ZoomOut( bAnimateTransition, ZoomTimeToGo ); + + if (LaserSight != none) + { + LaserSight.ChangeVisibility(true); + } +} + +simulated function AttachLaserSight() +{ + if( WorldInfo.NetMode == NM_DedicatedServer ) + { + return; + } + + super.AttachLaserSight(); + + if (LaserSight != none) + { + LaserSight.bForceDotToMatch = true; + } +} + +defaultproperties +{ + bHasFireLastAnims=true + BonesToLockOnEmpty=(RW_Bolt, RW_Charging_Handle) + + // Shooting Animations + FireSightedAnims[0]=Shoot_Iron + FireSightedAnims[1]=Shoot_Iron2 + FireSightedAnims[2]=Shoot_Iron3 + + // FOV + MeshFOV=70 + MeshIronSightFOV=20 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=150 + DOF_FG_MaxNearBlurSize=3 + + // Zooming/Position + IronSightPosition=(X=40,Y=0.1,Z=-4.57) + PlayerViewOffset=(X=14,Y=11,Z=-5) + + // Content + PackageKey="G36C" + FirstPersonMeshName="WEP_1P_G36C_MESH.Wep_1stP_G36C_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_G36C_ANIM.Wep_1stP_G36C_Anim" + PickupMeshName="WEP_3P_G36C_MESH.Wep_G36C_Pickup" + AttachmentArchetypeName="WEP_G36C_ARCH.Wep_G36C_3P" + MuzzleFlashTemplateName="WEP_G36C_ARCH.Wep_G36C_MuzzleFlash" + + LaserSightTemplate=KFLaserSightAttachment'FX_LaserSight_ARCH.LaserSight_WithAttachment_1P' + + // Ammo + MagazineCapacity[0]=30 + SpareAmmoCapacity[0]=450 + InitialSpareMags[0]=3 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=50 + minRecoilPitch=40 + maxRecoilYaw=70 + minRecoilYaw=-70 + RecoilRate=0.085 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=100 + RecoilISMinYawLimit=65460 + RecoilISMaxPitchLimit=350 + RecoilISMinPitchLimit=65460 + IronSightMeshFOVCompensationScale=6.0 + + // Old Recoil Data + // maxRecoilPitch=80 + // minRecoilPitch=65 + // maxRecoilYaw=60 + // minRecoilYaw=-60 + // RecoilRate=0.063 + // RecoilMaxYawLimit=400 + // RecoilMinYawLimit=65135 + // RecoilMaxPitchLimit=800 + // RecoilMinPitchLimit=65035 + // RecoilISMaxYawLimit=150 + // RecoilISMinYawLimit=65385 + // RecoilISMaxPitchLimit=350 + // RecoilISMinPitchLimit=65435 + // IronSightMeshFOVCompensationScale=1.5 + + // Inventory + InventorySize=7 + GroupPriority=100 + WeaponSelectTexture=Texture2D'wep_ui_g36c_tex.UI_WeaponSelect_G36C' + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_AssaultRifle' + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_G36C' + PenetrationPower(DEFAULT_FIREMODE)=4.0 + FireInterval(DEFAULT_FIREMODE)=+0.08 // 750 RPM + Spread(DEFAULT_FIREMODE)=0.005 + InstantHitDamage(DEFAULT_FIREMODE)=45.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_InstantHit + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_AssaultRifle' + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_G36C' + PenetrationPower(ALTFIRE_FIREMODE)=4.0 + FireInterval(ALTFIRE_FIREMODE)=+0.08 // 750 RPM + InstantHitDamage(ALTFIRE_FIREMODE)=45.0 + Spread(ALTFIRE_FIREMODE)=0.005 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_G36C' + InstantHitDamage(BASH_FIREMODE)=26 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_3P_Shoot_LP', FirstPersonCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_1P_Shoot_LP') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_3P_Shoot_Single', FirstPersonCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_1P_Shoot_Single') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_AK12.Play_WEP_SA_AK12_Handling_DryFire' //@TODO: Replace me + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_AK12.Play_WEP_SA_AK12_Handling_DryFire' //@TODO: Replace me +// WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_G36C.Play_WEP_G36C_Dry_Fire' +// WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_G36C.Play_WEP_G36C_Dry_Fire' + + // Advanced (High RPM) Fire Effects + bLoopingFireAnim(DEFAULT_FIREMODE)=true + bLoopingFireSnd(DEFAULT_FIREMODE)=true + WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_3P_End_LP', FirstPersonCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_1P_End_LP') + SingleFireSoundIndex=ALTFIRE_FIREMODE + + // Attachments + bHasIronSights=true + bHasFlashlight=false + bHasLaserSight=true + + AssociatedPerkClasses(0)=class'KFPerk_Swat' + + // Weapon Upgrade stat boosts + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) +} diff --git a/KFGameContent/Classes/KFWeap_Edged_Scythe.uc b/KFGameContent/Classes/KFWeap_Edged_Scythe.uc new file mode 100644 index 0000000..3fe3677 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_Edged_Scythe.uc @@ -0,0 +1,437 @@ +//============================================================================= +// KFWeap_Edged_AbominationAxe +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_Edged_Scythe extends KFWeap_MeleeBase; + +var const array HitboxChainFolded; +var const array HitboxChainUnfolded; + +var const int MaxHitRangeFolded; +var const int MaxHitRangeUnfolded; + +var const int FoldedDamage; +var const int FoldedDamageAlt; + +var const int UnfoldedDamage; +var const int UnfoldedDamageAlt; + +var const class FoldedDT; +var const class FoldedDTAlt; + +var const class UnfoldedDT; +var const class UnfoldedDTAlt; + +var const float UnfoldBlendingDuration; + +var const float FoldedAttackAnimRate; + +var const vector PlayerViewOffsetUnfolded; + +var transient vector OriginalPlayerViewOffset; +var transient bool bIsFolded; + +var AnimNodeBlendPerBone FoldBlendNode; + +var const ParticleSystem BloodParticles; + +const BloodParticlesSocket = 'BlockEffect'; + +var ParticleSystem FoldedTrailParticleSystem; +var ParticleSystem UnfoldedTrailParticleSystem; + +simulated event PreBeginPlay() +{ + Super.PreBeginPlay(); + + OriginalPlayerViewOffset = PlayerViewOffset; +} + +event PostBeginPlay() +{ + Super.PostBeginPlay(); + + ChangeMode(true, false); + + SetFoldedBladeBlendTarget(1.0f, 0.0f); +} + +///////////////////////////////////////////////////////////////////////////// + +/** The SkeletalMeshComponents Animations are being instanced from AnimTreeTemplate +* before PostInitAnimTree. Be sure to never set the Mesh's animations directly through +* the package */ +simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp) +{ + Super.PostInitAnimTree(SkelComp); + + FoldBlendNode = AnimNodeBlendPerBone(SkelComp.FindAnimNode('FoldBlendNode')); + + SetFoldedBladeBlendTarget(1.0f, 0.0f); +} + +///////////////////////////////////////////////////////////////////////////// + +/** Returns true if weapon can potentially be reloaded */ +simulated function bool CanReload(optional byte FireModeNum) +{ + return true; +} + +simulated state WeaponUpkeep +{ + simulated function BeginState(name PreviousStateName) + { + local name AnimName; + local float Duration; + + AnimName = CleanNonBloodyAnim; + + Duration = MySkelMesh.GetAnimLength(AnimName); + if ( Duration > 0.f ) + { + if ( Instigator.IsFirstPerson() ) + { + PlayAnimation(AnimName); + SetTimer(Duration, FALSE, nameof(SwapComplete)); + } + } + else + { + `warn("Duration is zero!!!"@AnimName); + SetTimer(0.001, FALSE, nameof(SwapComplete)); + } + + NotifyBeginState(); + } + + simulated function BeginFire(byte FireModeNum) + { + + } + + simulated event EndState(Name NextStateName) + { + ClearTimer(nameof(SwapComplete)); + Super.EndState(NextStateName); + NotifyEndState(); + } + + simulated function SwapComplete() + { + if (Role == ROLE_Authority) + { + GotoState('Active'); + } + else + { + GotoState('Active'); + ServerSwapComplete(); + } + } +} + +server reliable function ServerSwapComplete() +{ + GotoState('Active'); +} + +simulated function ChangeMode(bool IsFolded, bool bApplyBlend = true) +{ + if (MeleeAttackHelper == none) + return; + + if (IsFolded) + { + MeleeAttackHelper.SetMeleeRange(MaxHitRangeFolded); + MeleeAttackHelper.SetHitBoxChain(HitboxChainFolded); + + InstantHitDamage[DEFAULT_FIREMODE] = FoldedDamage; + InstantHitDamage[HEAVY_ATK_FIREMODE] = FoldedDamageAlt; + + InstantHitDamageTypes[DEFAULT_FIREMODE] = FoldedDT; + InstantHitDamageTypes[HEAVY_ATK_FIREMODE] = FoldedDTAlt; + + PlayerViewOffset = OriginalPlayerViewOffset; + + InstantHitDamageTypes[BASH_FIREMODE]=class'KFDT_Piercing_ScytheStabFolded'; + InstantHitMomentum[BASH_FIREMODE]=100000.f; + InstantHitDamage[BASH_FIREMODE]=40; + } + else + { + MeleeAttackHelper.SetMeleeRange(MaxHitRangeUnfolded); + MeleeAttackHelper.SetHitBoxChain(HitboxChainUnfolded); + + InstantHitDamage[DEFAULT_FIREMODE] = UnfoldedDamage; + InstantHitDamage[HEAVY_ATK_FIREMODE] = UnfoldedDamageAlt; + + InstantHitDamageTypes[DEFAULT_FIREMODE] = UnfoldedDT; + InstantHitDamageTypes[HEAVY_ATK_FIREMODE] = UnfoldedDTAlt; + + PlayerViewOffset = PlayerViewOffsetUnfolded; + + InstantHitDamageTypes[BASH_FIREMODE]=class'KFDT_Piercing_ScytheStab'; + InstantHitMomentum[BASH_FIREMODE]=100000.f; + InstantHitDamage[BASH_FIREMODE]=60; + } + + NotifyServerMode(bIsFolded); + + if (bApplyBlend) + { + SetFoldedBladeBlendTarget(bIsFolded ? 1.0f : 0.0f, UnfoldBlendingDuration); + } +} + +simulated function SetFoldedBladeBlendTarget(float Value, float BlendTime) +{ + if ( FoldBlendNode != None ) + { + FoldBlendNode.SetBlendTarget(Value, BlendTime); + } +} + +simulated function StartFire(byte FireModeNum) +{ + if (StartFireDisabled && FireModeNum == BLOCK_FIREMODE) + { + StartFireDisabled = false; + return; + } + + if (FireModeNum == DEFAULT_FIREMODE || FireModeNum == HEAVY_ATK_FIREMODE) + { + DistortTrailParticle = bIsFolded ? FoldedTrailParticleSystem : UnfoldedTrailParticleSystem; + } + else if (FireModeNum == BASH_FIREMODE) + { + DistortTrailParticle = none; + } + + super.StartFire(FireModeNum); +} + +static simulated function float CalculateTraderWeaponStatDamage() +{ + // How is this calculated for this weapon? + return default.UnfoldedDamage; +} + +simulated function PlayAnimation(name Sequence, optional float fDesiredDuration, optional bool bLoop, optional float BlendInTime=0.1, optional float BlendOutTime=0.0) +{ + local string NewAnimName; + + if (Sequence == 'Idle' || + Sequence == 'Guncheck_V1' || + Sequence == 'Guncheck_V2' || + Sequence == 'Guncheck_V3') + { + NewAnimName = string(Sequence); + NewAnimName $= ((bIsFolded) ? "_Folded" : "_Unfolded"); + Super.PlayAnimation(name(NewAnimName), fDesiredDuration, bLoop, BlendInTime, BlendOutTime); + } + else + { + Super.PlayAnimation(Sequence, fDesiredDuration, bLoop, BlendInTime, BlendOutTime); + } +} + +simulated function ModifyMeleeAttackSpeed(out float InSpeed, optional int FireMode = DEFAULT_FIREMODE, optional int UpgradeIndex = INDEX_NONE, optional KFPerk CurrentPerk) +{ + Super.ModifyMeleeAttackSpeed(InSpeed, FireMode, UpgradeIndex, CurrentPerk); + + if (bIsFolded) + { + InSpeed *= FoldedAttackAnimRate; + } +} + +/** + * Overriden to apply fold/unfold logic + */ +simulated function ANIMNOTIFY_LockBolt() +{ + bIsFolded = !bIsFolded; + ChangeMode(bIsFolded); +} + +/** Unused */ +simulated function ANIMNOTIFY_UnLockBolt() +{ + +} + +/** Play blood VFX as we only use one anim. */ +simulated function ANIMNOTIFY_CleanBlood() +{ + if (!bIsBloody) + { + return; + } + + if (WorldInfo.NetMode != NM_DedicatedServer && BloodParticles != none) + { + WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment(BloodParticles, MySkelMesh, BloodParticlesSocket, true); + } + + Super.ANIMNOTIFY_CleanBlood(); +} + +simulated state WeaponEquipping +{ + /** Initialize the weapon as being active and ready to go. */ + simulated event BeginState(Name PreviousStateName) + { + Super.BeginState(PreviousStateName); + + if (WorldInfo.NetMode != NM_Client) + { + NotifyInitialState(bIsFolded); + } + } +} + +/** + Should replicate to 3P to show the shield effects + */ +simulated function NotifyInitialState(bool bFolded) +{ + local KFPawn KFP; + + if (WorldInfo.NetMode != NM_Client) + { + `Log("NotifyInitialState: " $bFolded); + + KFP = KFPawn(Instigator); + KFP.OnWeaponSpecialAction(bFolded ? 0 : 1); + } +} + +reliable server function NotifyServerMode(bool bFolded) +{ + bIsFolded = bFolded; +} + +defaultproperties +{ + // Zooming/Position + PlayerViewOffset=(X=10,Y=0,Z=-10) // (X=2,Y=0,Z=0) + PlayerViewOffsetUnfolded=(X=12,Y=0,Z=-7) + + // Content + PackageKey="Scythe" + FirstPersonMeshName="WEP_1P_Scythe_MESH.Wep_1stP_Scythe_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_Scythe_ANIM.Wep_1st_Scythe_Anim" + FirstPersonAnimTree="WEP_1P_Scythe_ANIM.1P_Scythe_Animtree" + PickupMeshName="wep_3p_scythe_mesh.Wep_3rdP_Scythe_Pickup" + AttachmentArchetypeName="WEP_Scythe_ARCH.Wep_Scythe_3P" + + // Short Range Mode Params + MaxHitRangeFolded=220 + FoldedDamage = 70 + FoldedDamageAlt = 120 + FoldedDT=class'KFDT_Slashing_ScytheShort' + FoldedDTAlt=class'KFDT_Slashing_ScytheShortAlt' + HitboxChainFolded = {( + (BoneOffset=(X=+3,Z=190)), + (BoneOffset=(X=-3,Z=170)), + (BoneOffset=(X=+3,Z=150)), + (BoneOffset=(X=-3,Z=130)), + (BoneOffset=(X=+3,Z=110)), + (BoneOffset=(X=-3,Z=90)), + (BoneOffset=(X=+3,Z=70)), + (BoneOffset=(X=-3,Z=50)), + (BoneOffset=(X=+3,Z=30)), + (BoneOffset=(Z=10)) + )} + + // Long Range Mode Params + MaxHitRangeUnfolded=300 + UnfoldedDamage=140 + UnfoldedDamageAlt=190 + UnfoldedDT=class'KFDT_Slashing_ScytheLong' + UnfoldedDTAlt=class'KFDT_Slashing_ScytheLongAlt' + HitboxChainUnfolded = {( + (BoneOffset=(X=-3,Z=290)), + (BoneOffset=(X=-3,Z=270)), + (BoneOffset=(X=-3,Z=250)), + (BoneOffset=(X=+3,Z=230)), + (BoneOffset=(X=-3,Z=210)), + (BoneOffset=(X=+3,Z=190)), + (BoneOffset=(X=-3,Z=170)), + (BoneOffset=(X=+3,Z=150)), + (BoneOffset=(X=-3,Z=130)), + (BoneOffset=(X=+3,Z=110)), + (BoneOffset=(X=-3,Z=90)), + (BoneOffset=(X=+3,Z=70)), + (BoneOffset=(X=-3,Z=50)), + (BoneOffset=(X=+3,Z=30)), + (BoneOffset=(Z=10)) + )} + + Begin Object Name=MeleeHelper_0 + WorldImpactEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Bladed_melee_impact' + MeleeImpactCamShakeScale=0.04f //0.5 + // modified combo sequences + ChainSequence_F=(DIR_ForwardRight, DIR_ForwardLeft, DIR_ForwardRight, DIR_ForwardLeft) + ChainSequence_B=(DIR_BackwardLeft, DIR_BackwardRight, DIR_BackwardLeft, DIR_ForwardRight, DIR_Left, DIR_Right, DIR_Left) + ChainSequence_L=(DIR_Right, DIR_Left) + ChainSequence_R=(DIR_Left, DIR_Right, DIR_ForwardLeft, DIR_ForwardRight, DIR_Left, DIR_Right) + End Object + + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Slashing_ScytheShort' + InstantHitMomentum(DEFAULT_FIREMODE)=30000.f + + InstantHitDamageTypes(HEAVY_ATK_FIREMODE)=class'KFDT_Slashing_ScytheShortAlt' + FiringStatesArray(HEAVY_ATK_FIREMODE)=MeleeHeavyAttacking + InstantHitMomentum(HEAVY_ATK_FIREMODE)=30000.f + + // Defined in ChangeMode function + //InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Piercing_ScytheStab' + //InstantHitMomentum(BASH_FIREMODE)=100000.f + //InstantHitDamage(BASH_FIREMODE)=50 + + // Inventory + GroupPriority=50 + InventorySize=7 + WeaponSelectTexture=Texture2D'WEP_UI_Scythe_TEX.UI_WeaponSelect_Scythe' + + + AssociatedPerkClasses(0)=class'KFPerk_Berserker' + + // Block Sounds + BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Katana' + ParrySound=AkEvent'WW_WEP_Bullet_Impacts.Play_Parry_Metal' + + ParryDamageMitigationPercent=0.4 + BlockDamageMitigation=0.5 + ParryStrength=5 + + bIsFolded=true + + BonesToLockOnEmpty.Empty() + + UnfoldBlendingDuration=0.05f + + FoldedAttackAnimRate=0.65f + + // IdleFidgetAnims=(Guncheck_v1, Guncheck_v2, Guncheck_v3) + + // Weapon Upgrade stat boosts + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + + DistortTrailParticle = none + WhiteTrailParticle = none + BlueTrailParticle = none + RedTrailParticle = none + + FoldedTrailParticleSystem=ParticleSystem'WEP_Scythe_EMIT.FX_Scythe_Custom_R_01' + UnfoldedTrailParticleSystem=ParticleSystem'WEP_Scythe_EMIT.FX_Scythe_Custom_Unfold_01'; + + BloodParticles = ParticleSystem'WEP_1P_KATANA_EMIT.FX_katana_blood_flick_01' +} diff --git a/KFGameContent/Classes/KFWeap_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFWeap_HRG_Dragonbreath.uc new file mode 100644 index 0000000..e1e7d2d --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_Dragonbreath.uc @@ -0,0 +1,286 @@ +//============================================================================= +// KFWeap_HRG_Dragonbreath +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeap_HRG_Dragonbreath extends KFWeap_ShotgunBase; + +/** How much to scale recoil when firing in double barrel fire. */ +var(Recoil) float QuadFireRecoilModifier; + +/** Shoot animation to play when shooting both barrels from the hip */ +var(Animations) const editconst name FireQuadAnim; + +/** How much momentum to apply when fired in double barrel */ +var(Recoil) float DoubleBarrelKickMomentum; + +/** How much to reduce shoot momentum when falling */ +var(Recoil) float FallingMomentumReduction; + +var(Spread) const float SpreadWidthDegrees; +var(Spread) const float SpreadWidthDegreesAlt; + +var transient float StartingPelletPosition; +var transient float StartingPelletPositionAlt; + +var const float BarrelHeatPerProjectile; +var const float MaxBarrelHeat; +var const float BarrelCooldownRate; +var transient float CurrentBarrelHeat; +var transient float LastBarrelHeat; + +simulated event PreBeginPlay() +{ + Super.PreBeginPlay(); + + Spread[DEFAULT_FIREMODE] = SpreadWidthDegrees * DegToRad / NumPellets[DEFAULT_FIREMODE]; + Spread[ALTFIRE_FIREMODE] = SpreadWidthDegreesAlt * DegToRad / NumPellets[ALTFIRE_FIREMODE]; + + StartingPelletPosition = -SpreadWidthDegrees * DegToRad / 2.0f; + StartingPelletPositionAlt = -SpreadWidthDegreesAlt * DegToRad / 2.0f; +} + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + // Force start with "Glow_Intensity" of 0.0f + LastBarrelHeat = MaxBarrelHeat; + ChangeBarrelMaterial(); +} + +simulated function AltFireMode() +{ + if ( !Instigator.IsLocallyControlled() ) + { + return; + } + + StartFire(ALTFIRE_FIREMODE); +} + +/** Returns number of projectiles to fire from SpawnProjectile */ +simulated function byte GetNumProjectilesToFire(byte FireModeNum) +{ + return NumPellets[CurrentFireMode]; +} + +/** Handle one-hand fire anims */ +simulated function name GetWeaponFireAnim(byte FireModeNum) +{ + if (bUsingSights) + { + return FireSightedAnims[FireModeNum]; + } + else + { + if (FireModeNum == ALTFIRE_FIREMODE) + { + return FireQuadAnim; + } + else + { + return FireAnim; + } + } +} + +simulated function KFProjectile SpawnAllProjectiles(class KFProjClass, vector RealStartLoc, vector AimDir) +{ + local int ProjectilesToFire, i; + local float InitialOffset; + + ProjectilesToFire = GetNumProjectilesToFire(CurrentFireMode); + if (CurrentFireMode == GRENADE_FIREMODE || ProjectilesToFire <= 1) + { + return SpawnProjectile(KFProjClass, RealStartLoc, AimDir); + } + + InitialOffset = CurrentFireMode == DEFAULT_FIREMODE ? StartingPelletPosition : StartingPelletPositionAlt; + + for (i = 0; i < ProjectilesToFire; i++) + { + SpawnProjectile(KFProjClass, RealStartLoc, CalculateSpread(InitialOffset, Spread[CurrentFireMode], i, CurrentFireMode == ALTFIRE_FIREMODE)); + CurrentBarrelHeat = fmin(CurrentBarrelHeat + BarrelHeatPerProjectile, MaxBarrelHeat); + } + + ChangeBarrelMaterial(); + return None; +} + +simulated function vector CalculateSpread(float InitialOffset, float CurrentSpread, byte PelletNum, bool bHorizontal) +{ + local Vector X, Y, Z, POVLoc; + local Quat R; + local rotator POVRot; + + if (Instigator != None && Instigator.Controller != none) + { + Instigator.Controller.GetPlayerViewPoint(POVLoc, POVRot); + } + + GetAxes(POVRot, X, Y, Z); + + R = QuatFromAxisAndAngle(bHorizontal ? Z : Y, InitialOffset + CurrentSpread * PelletNum); + return QuatRotateVector(R, vector(POVRot)); +} + +simulated function ChangeBarrelMaterial() +{ + local int i; + + if( CurrentBarrelHeat != LastBarrelHeat ) + { + for( i = 0; i < WeaponMICs.Length; ++i ) + { + if( WeaponMICs[i] != none ) + { + WeaponMICs[i].SetScalarParameterValue('Barrel_intensity', CurrentBarrelHeat); + LastBarrelHeat = CurrentBarrelHeat; + } + } + } +} + +simulated function Tick(float Delta) +{ + Super.Tick(Delta); + + CurrentBarrelHeat = fmax(CurrentBarrelHeat - BarrelCooldownRate * Delta, 0.0f); + ChangeBarrelMaterial(); +} + +defaultproperties +{ + // Inventory + InventorySize=7 + GroupPriority=110 + WeaponSelectTexture=Texture2D'WEP_UI_HRG_MegaDragonsbreath_TEX.UI_WeaponSelect_HRG_MegaDragonsbreath' + + // FOV + MeshFOV=60 + MeshIronSightFOV=52 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=65 + DOF_FG_MaxNearBlurSize=3 + + // Zooming/Position + PlayerViewOffset=(X=15.0,Y=8.0,Z=-4.5) + IronSightPosition=(X=3,Y=0,Z=0) + + // Content + PackageKey="HRG_MegaDragonsbreath" + FirstPersonMeshName="wep_1p_hrg_megadragonsbreath_mesh.Wep_1stP_HRG_MegaDragonsbreath_Rig" + FirstPersonAnimSetNames(0)="wep_1p_hrg_megadragonsbreath_anim.Wep_1stP_HRG_MegaDragonsbreath_Anim" + PickupMeshName="wep_3p_hrg_megadragonsbreath_mesh.Wep_3rdP_HRG_MegaDragonsbreath_Pickup" + AttachmentArchetypeName="wep_hrg_megadragonsbreath_arch.Wep_HRG_MegaDragonsbreath_3P" + MuzzleFlashTemplateName="wep_hrg_megadragonsbreath_arch.Wep_HRG_MegaDragonsbreath_MuzzleFlash" + + // Animations + FireAnim=Shoot_Single + FireQuadAnim=Shoot_Double + FireSightedAnims[0]=Shoot_Iron_Single + FireSightedAnims[1]=Shoot_Iron_Double + bHasFireLastAnims=false + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunSingle' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Pellet_HRG_Dragonbreath' + InstantHitDamage(DEFAULT_FIREMODE)=28.0 //36 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_Dragonbreath' + PenetrationPower(DEFAULT_FIREMODE)=4.0 + FireInterval(DEFAULT_FIREMODE)=0.25 // 240 RPM + FireOffset=(X=25,Y=3.5,Z=-4) + NumPellets(DEFAULT_FIREMODE)=8 + Spread(DEFAULT_FIREMODE)=0 + ForceReloadTimeOnEmpty=0.3 + + // ALT_FIREMODE + FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunAuto' + FiringStatesArray(ALTFIRE_FIREMODE)= WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Pellet_HRG_Dragonbreath' + InstantHitDamage(ALTFIRE_FIREMODE)=28.0 + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_Dragonbreath' + PenetrationPower(ALTFIRE_FIREMODE)=4.0 + FireInterval(ALTFIRE_FIREMODE)=0.25 // 240 RPM + NumPellets(ALTFIRE_FIREMODE)=8 + Spread(ALTFIRE_FIREMODE)=0 + AmmoCost(ALTFIRE_FIREMODE)=1 + DoubleBarrelKickMomentum=1000 + FallingMomentumReduction=0.5 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Dragonbreath' + InstantHitDamage(BASH_FIREMODE)=27 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_Fire_3P_Single', FirstPersonCue=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_Fire_1P_Single') //@TODO: Replace + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_Fire_3P_AltFire', FirstPersonCue=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_Fire_1P_AltFire') //@TODO: Replace + + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_DryFire' //@TODO: Replace + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_DryFire' //@TODO: Replace + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + // Ammo + MagazineCapacity[0]=4 + SpareAmmoCapacity[0]=60 //48 //72 + InitialSpareMags[0]=3 //8 + AmmoPickupScale[0]=2.0 //3.0 + bCanBeReloaded=true + bReloadFromMagazine=true + bNoMagazine=true + + // Recoil + maxRecoilPitch=1200 //900 + minRecoilPitch=775 + maxRecoilYaw=800 //500 + minRecoilYaw=-500 + RecoilRate=0.085 + RecoilBlendOutRatio=1.1 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=1500 + RecoilMinPitchLimit=64785 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=500 + RecoilISMinPitchLimit=65485 + RecoilViewRotationScale=0.8 + FallingRecoilModifier=1.5 + HippedRecoilModifier=1.1 //1.25 + QuadFireRecoilModifier=2.0 + + AssociatedPerkClasses(0)=class'KFPerk_Firebug' + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot' + + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.15f,IncrementWeight=1) + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + + SpreadWidthDegrees=7.0f + SpreadWidthDegreesAlt=15.0f + + StartingPelletPosition=0.0f + StartingPelletPositionAlt=0.0f + + MaxBarrelHeat=5.0f + BarrelHeatPerProjectile=0.089f + BarrelCooldownRate=0.45f + + CurrentBarrelHeat=0.0f + LastBarrelHeat=0.0f + + NumBloodMapMaterials=3 +} diff --git a/KFGameContent/Classes/KFWeap_HRG_Locust.uc b/KFGameContent/Classes/KFWeap_HRG_Locust.uc new file mode 100644 index 0000000..26391ae --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_Locust.uc @@ -0,0 +1,684 @@ +//============================================================================= +// KFWeap_HRG_Locust +//============================================================================= +// A mosquito rocket launcher +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= +class KFWeap_HRG_Locust extends KFWeap_GrenadeLauncher_Base; + +const MAX_LOCKED_TARGETS = 6; + +/** Constains all currently locked-on targets */ +var protected array LockedTargets; + +/** How much to scale recoil when firing in multi-rocket mode */ +var float BurstFireRecoilModifier; + +/** The last time a target was acquired */ +var protected float LastTargetLockTime; + +/** The last time a target validation check was performed */ +var protected float LastTargetValidationCheckTime; + +/** How much time after a lock on occurs before another is allowed */ +var const float TimeBetweenLockOns; + +/** How much time should pass between target validation checks */ +var const float TargetValidationCheckInterval; + +/** Minimum distance a target can be from the crosshair to be considered for lock on */ +var const float MinTargetDistFromCrosshairSQ; + +/** Dot product FOV that targets need to stay within to maintain a target lock */ +var const float MaxLockMaintainFOVDotThreshold; + +/** Sound Effects to play when Locking */ +var AkBaseSoundObject LockAcquiredSoundFirstPerson; +var AkBaseSoundObject LockLostSoundFirstPerson; + +/** Icon textures for lock on drawing */ +var const Texture2D LockedOnIcon; +var LinearColor LockedIconColor; + +/** Ironsights Audio */ +var AkComponent IronsightsComponent; +var AkEvent IronsightsZoomInSound; +var AkEvent IronsightsZoomOutSound; + +/** Reduction for the amount of damage dealt to the weapon owner (including damage by the explosion) */ +var() float SelfDamageReductionValue; + +/** +* Toggle between DEFAULT and ALTFIRE +*/ +simulated function AltFireMode() +{ + super.AltFireMode(); + + LockedTargets.Length = 0; +} + +/********************************************************************************************* + * @name Target Locking And Validation + **********************************************************************************************/ + +/** We need to update our locked targets every frame and make sure they're within view and not dead */ +simulated event Tick( float DeltaTime ) +{ + local Pawn RecentlyLocked, StaticLockedTargets[6]; + local bool bUpdateServerTargets; + local int i; + + super.Tick( DeltaTime ); + + if( bUsingSights && bUseAltFireMode + && Instigator != none + && Instigator.IsLocallyControlled() ) + { + if( `TimeSince(LastTargetLockTime) > TimeBetweenLockOns + && LockedTargets.Length < AmmoCount[GetAmmoType(0)] + && LockedTargets.Length < MAX_LOCKED_TARGETS) + { + bUpdateServerTargets = FindTargets( RecentlyLocked ); + } + + if( LockedTargets.Length > 0 ) + { + bUpdateServerTargets = bUpdateServerTargets || ValidateTargets( RecentlyLocked ); + } + + // If we are a client, synchronize our targets with the server + if( bUpdateServerTargets && Role < ROLE_Authority ) + { + for( i = 0; i < MAX_LOCKED_TARGETS; ++i ) + { + if( i < LockedTargets.Length ) + { + StaticLockedTargets[i] = LockedTargets[i]; + } + else + { + StaticLockedTargets[i] = none; + } + } + + ServerSyncLockedTargets( StaticLockedTargets ); + } + } +} + + +/** +* Given an potential target TA determine if we can lock on to it. By default only allow locking on +* to pawns. +*/ +simulated function bool CanLockOnTo(Actor TA) +{ + Local KFPawn PawnTarget; + + PawnTarget = KFPawn(TA); + + // Make sure the pawn is legit, isn't dead, and isn't already at full health + if ((TA == None) || !TA.bProjTarget || TA.bDeleteMe || (PawnTarget == None) || + (TA == Instigator) || (PawnTarget.Health <= 0) || + !HasAmmo(DEFAULT_FIREMODE)) + { + return false; + } + + // Make sure and only lock onto players on the same team + return !WorldInfo.GRI.OnSameTeam(Instigator, TA); +} + +/** Finds a new lock on target, adds it to the target array and returns TRUE if the array was updated */ +simulated function bool FindTargets( out Pawn RecentlyLocked ) +{ + local Pawn P, BestTargetLock; + local byte TeamNum; + local vector AimStart, AimDir, TargetLoc, Projection, DirToPawn, LinePoint; + local Actor HitActor; + local float PointDistSQ, Score, BestScore, TargetSizeSQ; + + TeamNum = Instigator.GetTeamNum(); + AimStart = GetSafeStartTraceLocation(); + AimDir = vector( GetAdjustedAim(AimStart) ); + BestScore = 0.f; + + //Don't add targets if we're already burst firing + if (IsInState('WeaponBurstFiring')) + { + return false; + } + + foreach WorldInfo.AllPawns( class'Pawn', P ) + { + if (!CanLockOnTo(P)) + { + continue; + } + // Want alive pawns and ones we already don't have locked + if( P != none && P.IsAliveAndWell() && P.GetTeamNum() != TeamNum && LockedTargets.Find(P) == INDEX_NONE ) + { + TargetLoc = GetLockedTargetLoc( P ); + Projection = TargetLoc - AimStart; + DirToPawn = Normal( Projection ); + + // Filter out pawns too far from center + if( AimDir dot DirToPawn < 0.5f ) + { + continue; + } + + // Check to make sure target isn't too far from center + PointDistToLine( TargetLoc, AimDir, AimStart, LinePoint ); + PointDistSQ = VSizeSQ( LinePoint - P.Location ); + + TargetSizeSQ = P.GetCollisionRadius() * 2.f; + TargetSizeSQ *= TargetSizeSQ; + + if( PointDistSQ > (TargetSizeSQ + MinTargetDistFromCrosshairSQ) ) + { + continue; + } + + // Make sure it's not obstructed + HitActor = class'KFAIController'.static.ActorBlockTest(self, TargetLoc, AimStart,, true, true); + if( HitActor != none && HitActor != P ) + { + continue; + } + + // Distance from target has much more impact on target selection score + Score = VSizeSQ( Projection ) + PointDistSQ; + if( BestScore == 0.f || Score < BestScore ) + { + BestTargetLock = P; + BestScore = Score; + } + } + } + + if( BestTargetLock != none ) + { + LastTargetLockTime = WorldInfo.TimeSeconds; + LockedTargets.AddItem( BestTargetLock ); + RecentlyLocked = BestTargetLock; + + // Plays sound/FX when locking on to a new target + PlayTargetLockOnEffects(); + + return true; + } + + RecentlyLocked = none; + + return false; +} + +/** Checks to ensure all of our current locked targets are valid */ +simulated function bool ValidateTargets( optional Pawn RecentlyLocked ) +{ + local int i; + local bool bShouldRemoveTarget, bAlteredTargets; + local vector AimStart, AimDir, TargetLoc; + local Actor HitActor; + + if( `TimeSince(LastTargetValidationCheckTime) < TargetValidationCheckInterval ) + { + return false; + } + + LastTargetValidationCheckTime = WorldInfo.TimeSeconds; + + AimStart = GetSafeStartTraceLocation(); + AimDir = vector( GetAdjustedAim(AimStart) ); + + bAlteredTargets = false; + for( i = 0; i < LockedTargets.Length; ++i ) + { + // For speed don't bother checking a target we just locked + if( RecentlyLocked != none && RecentlyLocked == LockedTargets[i] ) + { + continue; + } + + bShouldRemoveTarget = false; + + if( LockedTargets[i] == none + || !LockedTargets[i].IsAliveAndWell() ) + { + bShouldRemoveTarget = true; + } + else + { + TargetLoc = GetLockedTargetLoc( LockedTargets[i] ); + if( AimDir dot Normal(LockedTargets[i].Location - AimStart) >= MaxLockMaintainFOVDotThreshold ) + { + HitActor = class'KFAIController'.static.ActorBlockTest( self, TargetLoc, AimStart,, true, true ); + if( HitActor != none && HitActor != LockedTargets[i] ) + { + bShouldRemoveTarget = true; + } + } + else + { + bShouldRemoveTarget = true; + } + } + + // A target was invalidated, remove it from the list + if( bShouldRemoveTarget ) + { + LockedTargets.Remove( i, 1 ); + --i; + bAlteredTargets = true; + continue; + } + } + + // Plays sound/FX when losing a target lock, but only if we didn't play a lock on this frame + if( bAlteredTargets && RecentlyLocked == none ) + { + PlayTargetLostEffects(); + } + + return bAlteredTargets; +} + +/** Synchronizes our locked targets with the server */ +reliable server function ServerSyncLockedTargets( Pawn TargetPawns[MAX_LOCKED_TARGETS] ) +{ + local int i; + + LockedTargets.Length = 0; + for( i = 0; i < MAX_LOCKED_TARGETS; ++i ) + { + if (TargetPawns[i] != none) + { + LockedTargets.AddItem(TargetPawns[i]); + } + } +} + +/** Adjusts our destination target impact location */ +static simulated function vector GetLockedTargetLoc( Pawn P ) +{ + // Go for the chest, but just in case we don't have something with a chest bone we'll use collision and eyeheight settings + if( P.Mesh.SkeletalMesh != none && P.Mesh.bAnimTreeInitialised ) + { + if( P.Mesh.MatchRefBone('Spine2') != INDEX_NONE ) + { + return P.Mesh.GetBoneLocation( 'Spine2' ); + } + else if( P.Mesh.MatchRefBone('Spine1') != INDEX_NONE ) + { + return P.Mesh.GetBoneLocation( 'Spine1' ); + } + + return P.Mesh.GetPosition() + ((P.CylinderComponent.CollisionHeight + (P.BaseEyeHeight * 0.5f)) * vect(0,0,1)) ; + } + + // General chest area, fallback + return P.Location + ( vect(0,0,1) * P.BaseEyeHeight * 0.75f ); +} + +simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo) +{ + super.ZoomIn(bAnimateTransition, ZoomTimeToGo); + + if (IronsightsZoomInSound != none && Instigator != none && Instigator.IsLocallyControlled()) + { + IronsightsComponent.PlayEvent(IronsightsZoomInSound, false); + } +} + +/** Clear all locked targets when zooming out, both server and client */ +simulated function ZoomOut( bool bAnimateTransition, float ZoomTimeToGo ) +{ + super.ZoomOut( bAnimateTransition, ZoomTimeToGo ); + + if (IronsightsZoomOutSound != none && Instigator != none && Instigator.IsLocallyControlled()) + { + IronsightsComponent.PlayEvent(IronsightsZoomOutSound, false); + } + + // Play a target lost effect if we're clearing targets on the way out + if( Instigator.IsLocallyControlled() && LockedTargets.Length > 0 ) + { + PlayTargetLostEffects(); + } + LockedTargets.Length = 0; +} + +/** Play FX or sounds when locking on to a new target */ +simulated function PlayTargetLockOnEffects() +{ + if( Instigator != none && Instigator.IsHumanControlled() ) + { + PlaySoundBase( LockAcquiredSoundFirstPerson, true ); + } +} + +/** Play FX or sounds when losing a target lock */ +simulated function PlayTargetLostEffects() +{ + if( Instigator != none && Instigator.IsHumanControlled() ) + { + PlaySoundBase( LockLostSoundFirstPerson, true ); + } +} + +/********************************************************************************************* + * @name Projectile Spawning + **********************************************************************************************/ + +/** Spawn projectile is called once for each rocket fired. In burst mode it will cycle through targets until it runs out */ +simulated function KFProjectile SpawnProjectile( class KFProjClass, vector RealStartLoc, vector AimDir ) +{ + local KFProj_HRG_Locust LocustProj; + + if( CurrentFireMode == GRENADE_FIREMODE ) + { + return super.SpawnProjectile( KFProjClass, RealStartLoc, AimDir ); + } + + // We need to set our target if we are firing from a locked on position + if( bUsingSights + && CurrentFireMode == ALTFIRE_FIREMODE + && LockedTargets.Length > 0 ) + { + // We'll aim our rocket at a target here otherwise we will spawn a dumbfire rocket at the end of the function + if( LockedTargets.Length > 0 ) + { + // Spawn our projectile and set its target + LocustProj = KFProj_HRG_Locust( super.SpawnProjectile(KFProjClass, RealStartLoc, AimDir) ); + if( LocustProj != none ) + { + //Seek to new target, then remove from list. Always use first target in the list for new fire. + LocustProj.SetLockedTarget( KFPawn(LockedTargets[0]) ); + LockedTargets.Remove(0, 1); + + return LocustProj; + } + } + + return None; + } + + return super.SpawnProjectile( KFProjClass, RealStartLoc, AimDir ); +} + +/********************************************************************************************* + * @name Targeting HUD -- Partially adapted from KFWeap_Rifle_RailGun + **********************************************************************************************/ + +/** Handle drawing our custom lock on HUD */ +simulated function DrawHUD( HUD H, Canvas C ) +{ + local int i; + + if( !bUsingSights || LockedTargets.Length == 0 ) + { + return; + } + + // Draw target locked icons + C.EnableStencilTest( true ); + for( i = 0; i < LockedTargets.Length; ++i ) + { + if( LockedTargets[i] != none ) + { + DrawTargetingIcon( C, i ); + } + } + C.EnableStencilTest( false ); +} + +/** Draws a targeting icon for each one of our locked targets */ +simulated function DrawTargetingIcon( Canvas Canvas, int Index ) +{ + local vector WorldPos, ScreenPos; + local float IconSize, IconScale; + + // Project world pos to canvas + WorldPos = GetLockedTargetLoc( LockedTargets[Index] ); + ScreenPos = Canvas.Project( WorldPos );//WorldToCanvas(Canvas, WorldPos); + + // calculate scale based on resolution and distance + IconScale = fMin( float(Canvas.SizeX) / 1024.f, 1.f ); + // Scale down up to 40 meters away, with a clamp at 20% size + IconScale *= fClamp( 1.f - VSize(WorldPos - Instigator.Location) / 4000.f, 0.2f, 1.f ); + + // Apply size scale + IconSize = 200.f * IconScale; + ScreenPos.X -= IconSize / 2.f; + ScreenPos.Y -= IconSize / 2.f; + + // Off-screen check + if( ScreenPos.X < 0 || ScreenPos.X > Canvas.SizeX || ScreenPos.Y < 0 || ScreenPos.Y > Canvas.SizeY ) + { + return; + } + + Canvas.SetPos( ScreenPos.X, ScreenPos.Y ); + + // Draw the icon + Canvas.DrawTile( LockedOnIcon, IconSize, IconSize, 0, 0, LockedOnIcon.SizeX, LockedOnIcon.SizeY, LockedIconColor ); +} + +/********************************************************************************************* + * State WeaponSingleFiring + * Fire must be released between every shot. + *********************************************************************************************/ + +simulated state WeaponSingleFiring +{ + simulated function BeginState( Name PrevStateName ) + { + LockedTargets.Length = 0; + + super.BeginState( PrevStateName ); + } +} + +/********************************************************************************************* + * State WeaponBurstFiring + * Fires a burst of bullets. Fire must be released between every shot. + *********************************************************************************************/ + +simulated state WeaponBurstFiring +{ + simulated function int GetBurstAmount() + { + // Clamp our bursts to either the number of targets or how much ammo we have remaining + return Clamp( LockedTargets.Length, 1, AmmoCount[GetAmmoType(CurrentFireMode)] ); + } + + /** Overridden to apply scaled recoil when in multi-rocket mode */ + simulated function ModifyRecoil( out float CurrentRecoilModifier ) + { + super.ModifyRecoil( CurrentRecoilModifier ); + + CurrentRecoilModifier *= BurstFireRecoilModifier; + } + + simulated function bool ShouldRefire() + { + return LockedTargets.Length > 0; + } + + simulated function FireAmmunition() + { + super.FireAmmunition(); + if (Role < ROLE_Authority) + { + LockedTargets.Remove(0, 1); + } + } + + simulated event EndState( Name NextStateName ) + { + LockedTargets.Length = 0; + + super.EndState( NextStateName ); + } +} + +/** + Reduce the damage received and apply it to the shield + */ +function AdjustDamage(out int InDamage, class DamageType, Actor DamageCauser) +{ + super.AdjustDamage(InDamage, DamageType, DamageCauser); + + if (Instigator != none && DamageCauser.Instigator == Instigator) + { + InDamage *= SelfDamageReductionValue; + } +} + +defaultproperties +{ + ForceReloadTime=0.4f + + // Inventory + InventoryGroup=IG_Primary + GroupPriority=100 + InventorySize=6 + WeaponSelectTexture=Texture2D'wep_ui_hrg_locust_tex.UI_WeaponSelect_HRG_Locust' + + // FOV + MeshFOV=86 + MeshIronSightFOV=65 + PlayerIronSightFOV=70 + PlayerSprintFOV=95 + + // Depth of field + DOF_FG_FocalRadius=50 + DOF_FG_MaxNearBlurSize=2.5 + + // Zooming/Position + PlayerViewOffset=(X=20.0,Y=5,Z=-5) + FastZoomOutTime=0.2 + + // Content + PackageKey="HRG_Locust" + FirstPersonMeshName="wep_1p_hrg_locust_mesh.Wep_1stP_HRG_Locust_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_HRG_Locust_ANIM.Wep_1stP_HRG_Locust_Anim" + PickupMeshName="wep_3p_hrg_locust_mesh.Wep_3rdP_HRG_Locust_Pickup" + AttachmentArchetypeName="wep_hrg_locust_arch.Wep_HRG_Locust_3P" + MuzzleFlashTemplateName="wep_hrg_locust_arch.Wep_HRG_Locust_MuzzleFlash" + + // Target Locking + MinTargetDistFromCrosshairSQ=2500.0f // 0.5 meters + TimeBetweenLockOns=0.06f + TargetValidationCheckInterval=0.1f + MaxLockMaintainFOVDotThreshold=0.36f + + // LockOn Visuals + LockedOnIcon=Texture2D'Wep_Scope_TEX.Wep_1stP_Yellow_Red_Target' + LockedIconColor=(R=1.f, G=0.f, B=0.f, A=0.5f) + + // Lock On/Lost Sounds + LockAcquiredSoundFirstPerson=AkEvent'WW_WEP_SA_Railgun.Play_Railgun_Scope_Locked' + LockLostSoundFirstPerson=AkEvent'WW_WEP_SA_Railgun.Play_Railgun_Scope_Lost' + + // Zooming/Position + IronSightPosition=(X=0,Y=-0.065,Z=-0.31) + + // Ammo + MagazineCapacity[0]=6 + SpareAmmoCapacity[0]=84 + InitialSpareMags[0]=3 + AmmoPickupScale[0]=2.0 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=400 + minRecoilPitch=275 + maxRecoilYaw=200 + minRecoilYaw=-200 + RecoilRate=0.085 + RecoilBlendOutRatio=0.35 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=1500 + RecoilMinPitchLimit=64785 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=500 + RecoilISMinPitchLimit=65485 + RecoilViewRotationScale=0.8 + FallingRecoilModifier=1.5 + HippedRecoilModifier=1.25 + BurstFireRecoilModifier=0.15f // Reduce recoil between rockets when in burst mode + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'UI_FireModes_TEX.UI_FireModeSelect_Rocket' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_HRG_Locust' + FireInterval(DEFAULT_FIREMODE)=+0.35 + InstantHitDamage(DEFAULT_FIREMODE)=1 //140 // 120.0 //100.00 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_Locust' + Spread(DEFAULT_FIREMODE)=0.025 + FireOffset=(X=20,Y=4.0,Z=-3) + + // ALT_FIREMODE + FireModeIconPaths(ALTFIRE_FIREMODE)= Texture2D'UI_SecondaryAmmo_TEX.UI_FireModeSelect_AutoTarget' + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponBurstFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_HRG_Locust' + FireInterval(ALTFIRE_FIREMODE)=+0.3 //0.1 + InstantHitDamage(ALTFIRE_FIREMODE)=1 //140 // 120.0 //100.00 + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_Locust' + Spread(ALTFIRE_FIREMODE)=0.025 + AmmoCost(ALTFIRE_FIREMODE)=1 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Locust' + InstantHitDamage(BASH_FIREMODE)=29 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Fire_1P') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Fire_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_RPG7.Play_WEP_SA_RPG7_DryFire' + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_RPG7.Play_WEP_SA_RPG7_DryFire' + + // Animation + bHasFireLastAnims=true + IdleFidgetAnims=(Guncheck_v1, Guncheck_v2) + + //BonesToLockOnEmpty=(RW_Grenade1) + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + AssociatedPerkClasses(0)=class'KFPerk_Survivalist' + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot' + + // Audio + Begin Object Class=AkComponent name=IronsightsComponent0 + bForceOcclusionUpdateInterval=true + OcclusionUpdateInterval=0.f // never update occlusion for footsteps + bStopWhenOwnerDestroyed=true + End Object + IronsightsComponent=IronsightsComponent0 + Components.Add(IronsightsComponent0) + IronsightsZoomInSound=AkEvent'WW_WEP_Seeker_6.Play_Seeker_6_Iron_In' + IronsightsZoomOutSound=AkEvent'WW_WEP_Seeker_6.Play_Seeker_6_Iron_In_Out' + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.125f,IncrementWeight=1) + + 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))) + + SelfDamageReductionValue = 0f; +}