diff --git a/Engine/Classes/Actor.uc b/Engine/Classes/Actor.uc index 71096ac..17d68a3 100644 --- a/Engine/Classes/Actor.uc +++ b/Engine/Classes/Actor.uc @@ -2609,7 +2609,8 @@ simulated function TakeRadiusDamage vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) { local float ColRadius, ColHeight; @@ -2636,9 +2637,13 @@ simulated function TakeRadiusDamage if (DamageScale > 0.f) { `if(`__TW_) - AdjustRadiusDamage(BaseDamage, DamageScale, HurtOrigin); + if (bAdjustRadiusDamage) + { + AdjustRadiusDamage(BaseDamage, DamageScale, HurtOrigin); + } `endif ScaledDamage = DamageScale * BaseDamage; + TakeDamage ( ScaledDamage, diff --git a/Engine/Classes/ApexDestructibleActor.uc b/Engine/Classes/ApexDestructibleActor.uc index b67d6f2..286b5ee 100644 --- a/Engine/Classes/ApexDestructibleActor.uc +++ b/Engine/Classes/ApexDestructibleActor.uc @@ -84,8 +84,10 @@ simulated native function TakeRadiusDamage vector HurtOrigin, /* The origin of the damage */ bool bFullDamage, /* Whether or not to apply full damage or attenuated damage */ Actor DamageCauser, /* The actor which caused the damage */ - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ); + function OnSetMaterial(SeqAct_SetMaterial Action) { StaticDestructibleComponent.SetMaterial( Action.MaterialIndex, Action.NewMaterial ); diff --git a/Engine/Classes/KActor.uc b/Engine/Classes/KActor.uc index 1c7a14b..0124a09 100644 --- a/Engine/Classes/KActor.uc +++ b/Engine/Classes/KActor.uc @@ -295,7 +295,8 @@ simulated function TakeRadiusDamage vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) { local int Idx; diff --git a/Engine/Classes/KActorFromStatic.uc b/Engine/Classes/KActorFromStatic.uc index 2b5e2be..5f096e9 100644 --- a/Engine/Classes/KActorFromStatic.uc +++ b/Engine/Classes/KActorFromStatic.uc @@ -165,7 +165,8 @@ simulated function TakeRadiusDamage vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) { local int Idx; diff --git a/Engine/Classes/KAsset.uc b/Engine/Classes/KAsset.uc index 7db73bd..7be01e8 100644 --- a/Engine/Classes/KAsset.uc +++ b/Engine/Classes/KAsset.uc @@ -136,7 +136,8 @@ simulated function TakeRadiusDamage vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) { if ( bDamageAppliesImpulse && damageType.default.RadialDamageImpulse > 0 && (Role == ROLE_Authority) ) diff --git a/Engine/Classes/SVehicle.uc b/Engine/Classes/SVehicle.uc index a510c3d..fa008a9 100644 --- a/Engine/Classes/SVehicle.uc +++ b/Engine/Classes/SVehicle.uc @@ -351,7 +351,8 @@ simulated function TakeRadiusDamage vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) { local vector HitLocation, Dir, NewDir; diff --git a/Engine/Classes/Vehicle.uc b/Engine/Classes/Vehicle.uc index 8b10674..175c1d0 100644 --- a/Engine/Classes/Vehicle.uc +++ b/Engine/Classes/Vehicle.uc @@ -220,12 +220,13 @@ simulated function TakeRadiusDamage vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) { if ( Role == ROLE_Authority ) { - Super.TakeRadiusDamage(InstigatedBy, BaseDamage, DamageRadius, DamageType, Momentum, HurtOrigin, bFullDamage, DamageCauser, DamageFalloffExponent); + Super.TakeRadiusDamage(InstigatedBy, BaseDamage, DamageRadius, DamageType, Momentum, HurtOrigin, bFullDamage, DamageCauser, DamageFalloffExponent, bAdjustRadiusDamage); if (Health > 0) { diff --git a/GameFramework/Classes/GameExplosion.uc b/GameFramework/Classes/GameExplosion.uc index ef3873a..5db8fb9 100644 --- a/GameFramework/Classes/GameExplosion.uc +++ b/GameFramework/Classes/GameExplosion.uc @@ -30,6 +30,11 @@ var transient Actor ActorToIgnoreForDamage; /** If set, ignore instigator when doing damage/effects. Can be set in addition to above */ var transient bool bIgnoreInstigator; +var bool bAlwaysFullDamage; + +// If we refine the search so we don't include Victim's that are outside the "height" of instigator +var bool bDoCylinderCheck; + `if(`__TW_) /** The actor class to ignore for damage from this explosion **/ var() class ActorClassToIgnoreForDamage; @@ -195,4 +200,7 @@ defaultproperties CamShakeFalloff=2.f bAutoControllerVibration=true + + bAlwaysFullDamage = false + bDoCylinderCheck = false } diff --git a/GameFramework/Classes/GameExplosionActor.uc b/GameFramework/Classes/GameExplosionActor.uc index 1ea4279..910d442 100644 --- a/GameFramework/Classes/GameExplosionActor.uc +++ b/GameFramework/Classes/GameExplosionActor.uc @@ -188,8 +188,8 @@ protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCau local Actor Victim, HitActor; local vector HitL, HitN, Dir, BBoxCenter;//, BBoxExtent; `endif - local bool bDamageBlocked, bDoFullDamage, bCauseFractureEffects, bCausePawnEffects, bCauseDamageEffects, bHurtSomeone; - local float ColRadius, ColHeight, CheckRadius, VictimDist; + local bool bDamageBlocked, bDoFullDamage, bCauseFractureEffects, bCausePawnEffects, bCauseDamageEffects, bHurtSomeone, bAdjustRadiusDamage; + local float ColRadius, ColHeight, CheckRadius, VictimDist, VictimColRadius, VictimColHeight; local array VictimsList; local Box BBox; local Controller ModInstigator; @@ -200,6 +200,8 @@ protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCau local KActorFromStatic NewKActor; local StaticMeshComponent HitStaticMesh; + bAdjustRadiusDamage = true; + // can pre-calculate this condition now bCauseFractureEffects = bCauseEffects && WorldInfo.NetMode != NM_DedicatedServer && ExplosionTemplate.bCausesFracture; bCauseEffects = bCauseEffects && WorldInfo.NetMode != NM_Client; @@ -256,6 +258,18 @@ protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCau VictimDist = VSize(Location - Victim.Location); } + if (ExplosionTemplate.bDoCylinderCheck) + { + Victim.GetBoundingCylinder(ColRadius, ColHeight); + Instigator.GetBoundingCylinder(VictimColRadius, VictimColHeight); + + // If the distance between them is more than the size of half the height of both cilinders, don't consider as target + if (Abs(Victim.Location.Z - Instigator.Location.Z) >= (ColHeight * 0.5f) + (VictimColHeight * 0.5f)) + { + continue; + } + } + // Do fracturing if( bCauseFractureEffects && (VictimPawn == None) ) { @@ -295,6 +309,12 @@ protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCau bDoFullDamage = FALSE; } + if (ExplosionTemplate.bAlwaysFullDamage) + { + bAdjustRadiusDamage = false; + bDoFullDamage = true; + } + if ( !bDamageBlocked ) { if ( bCauseDamageEffects ) @@ -309,9 +329,9 @@ protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCau } `if(`__TW_) - Victim.TakeRadiusDamage(ModInstigator, GetDamageFor(Victim), ExplosionTemplate.DamageRadius, ExplosionTemplate.MyDamageType, ExplosionTemplate.MomentumTransferScale, Location, bDoFullDamage, (Owner != None) ? Owner : self, ExplosionTemplate.DamageFalloffExponent); + Victim.TakeRadiusDamage(ModInstigator, GetDamageFor(Victim), ExplosionTemplate.DamageRadius, ExplosionTemplate.MyDamageType, ExplosionTemplate.MomentumTransferScale, Location, bDoFullDamage, (Owner != None) ? Owner : self, ExplosionTemplate.DamageFalloffExponent, bAdjustRadiusDamage); `else - Victim.TakeRadiusDamage(ModInstigator, ExplosionTemplate.Damage, ExplosionTemplate.DamageRadius, ExplosionTemplate.MyDamageType, ExplosionTemplate.MomentumTransferScale, Location, bDoFullDamage, (Owner != None) ? Owner : self, ExplosionTemplate.DamageFalloffExponent); + Victim.TakeRadiusDamage(ModInstigator, ExplosionTemplate.Damage, ExplosionTemplate.DamageRadius, ExplosionTemplate.MyDamageType, ExplosionTemplate.MomentumTransferScale, Location, bDoFullDamage, (Owner != None) ? Owner : self, ExplosionTemplate.DamageFalloffExponent, bAdjustRadiusDamage); `endif VictimsList[VictimsList.Length] = Victim; diff --git a/KFGame/Classes/KFAIController_ZedFleshpound.uc b/KFGame/Classes/KFAIController_ZedFleshpound.uc index 1faf425..797dc8c 100644 --- a/KFGame/Classes/KFAIController_ZedFleshpound.uc +++ b/KFGame/Classes/KFAIController_ZedFleshpound.uc @@ -160,10 +160,19 @@ function NotifyMeleeDamageDealt() function NotifyTakeHit( Controller InstigatedBy, vector HitLocation, int Damage, class damageType, vector Momentum ) { + local class KFDT; + super.NotifyTakeHit( InstigatedBy, HitLocation, Damage, damageType, Momentum ); if( RagePlugin != none && InstigatedBy != self ) { + KFDT = class(DamageType); + + if (KFDT != none && KFDT.default.bCanEnrage == false) + { + return; + } + RagePlugin.AccumulatedDOT += Damage; } } diff --git a/KFGame/Classes/KFAISpawnManager.uc b/KFGame/Classes/KFAISpawnManager.uc index e9c54b1..4429950 100644 --- a/KFGame/Classes/KFAISpawnManager.uc +++ b/KFGame/Classes/KFAISpawnManager.uc @@ -226,6 +226,8 @@ static function string ZedTypeToString(EAIType AiTypeToConvert) return "FleshpoundMini"; case AT_Bloat: return "Bloat"; + case AT_EliteCrawler: + return "CrawlerKing"; case AT_Siren: return "Siren"; case AT_Husk: diff --git a/KFGame/Classes/KFAfflictionManager.uc b/KFGame/Classes/KFAfflictionManager.uc index 689f138..92ba35e 100644 --- a/KFGame/Classes/KFAfflictionManager.uc +++ b/KFGame/Classes/KFAfflictionManager.uc @@ -264,19 +264,19 @@ protected function ProcessSpecialMoveAfflictions(KFPerk InstigatorPerk, vector H } if (StunPower > 0 && CanDoSpecialmove(SM_Stunned)) { - AccrueAffliction(AF_Stun, StunPower, BodyPart, InstigatorPerk); + AccrueAffliction(AF_Stun, StunPower, BodyPart, InstigatorPerk, DamageType); } if (StumblePower > 0 && CanDoSpecialmove(SM_Stumble)) { - AccrueAffliction(AF_Stumble, StumblePower, BodyPart, InstigatorPerk); + AccrueAffliction(AF_Stumble, StumblePower, BodyPart, InstigatorPerk, DamageType); } if (FreezePower > 0 && CanDoSpecialMove(SM_Frozen)) { - AccrueAffliction(AF_Freeze, FreezePower, BodyPart, InstigatorPerk); + AccrueAffliction(AF_Freeze, FreezePower, BodyPart, InstigatorPerk, DamageType); } if (SnarePower > 0) { - AccrueAffliction(AF_Snare, SnarePower, BodyPart, InstigatorPerk); + AccrueAffliction(AF_Snare, SnarePower, BodyPart, InstigatorPerk, DamageType); } } @@ -325,17 +325,17 @@ protected function ProcessHitReactionAfflictions(KFPerk InstigatorPerk, class 0) { - AccrueAffliction(AF_MeleeHit, MeleeHitPower * ReactionModifier, BodyPart, InstigatorPerk); + AccrueAffliction(AF_MeleeHit, MeleeHitPower * ReactionModifier, BodyPart, InstigatorPerk, DamageType); } // Force recovery time for the headless hit. GetTimerCount() is a dirty way to do this only on the frame of CauseHeadTrauma() if (HitZoneIdx == HZI_Head && IsHeadless() && GetTimerCount('BleedOutTimer', Outer) == 0.f) { - AccrueAffliction(AF_MeleeHit, 100.f, BodyPart, InstigatorPerk); + AccrueAffliction(AF_MeleeHit, 100.f, BodyPart, InstigatorPerk, DamageType); } // Check medium hit reaction if (GunHitPower > 0) { - AccrueAffliction(AF_GunHit, GunHitPower * ReactionModifier, BodyPart, InstigatorPerk); + AccrueAffliction(AF_GunHit, GunHitPower * ReactionModifier, BodyPart, InstigatorPerk, DamageType); } } } @@ -389,18 +389,18 @@ protected function ProcessEffectBasedAfflictions(KFPerk InstigatorPerk, class 0) { - AccrueAffliction(AF_FirePanic, BurnPower); + AccrueAffliction(AF_FirePanic, BurnPower, , InstigatorPerk, DamageType); } } else { if (EMPPower > 0) { - AccrueAffliction(AF_EMP, EMPPower); + AccrueAffliction(AF_EMP, EMPPower, , InstigatorPerk, DamageType); } else if (InstigatorPerk != none && InstigatorPerk.ShouldGetDaZeD(DamageType)) { - AccrueAffliction(AF_EMP, InstigatorPerk.GetDaZedEMPPower()); + AccrueAffliction(AF_EMP, InstigatorPerk.GetDaZedEMPPower(), , InstigatorPerk, DamageType); } if (BurnPower > 0) @@ -409,7 +409,7 @@ protected function ProcessEffectBasedAfflictions(KFPerk InstigatorPerk, class 0 || DamageType.static.AlwaysPoisons()) { - AccrueAffliction(AF_Poison, PoisonPower); + AccrueAffliction(AF_Poison, PoisonPower, , InstigatorPerk, DamageType); } if (MicrowavePower > 0) { @@ -417,15 +417,15 @@ protected function ProcessEffectBasedAfflictions(KFPerk InstigatorPerk, class 0) { - AccrueAffliction(AF_Bleed, BleedPower); + AccrueAffliction(AF_Bleed, BleedPower, , InstigatorPerk, DamageType); } if (BigHeadPower > 0) { - AccrueAffliction(AF_BigHead, BigHeadPower); + AccrueAffliction(AF_BigHead, BigHeadPower, , InstigatorPerk, DamageType); } if (ShrinkPower > 0) { - AccrueAffliction(AF_Shrink, ShrinkPower,,InstigatorPerk); + AccrueAffliction(AF_Shrink, ShrinkPower,,InstigatorPerk, DamageType); } } } @@ -440,6 +440,10 @@ protected function ProcessEffectBasedAfflictions(KFPerk InstigatorPerk, class DamageType = none) { + local bool bCanApplyRadial; + + bCanApplyRadial = true; + if ( InPower <= 0 || Type >= IncapSettings.Length ) { return; // immune @@ -450,8 +454,13 @@ function AccrueAffliction(EAfflictionType Type, float InPower, optional EHitZone return; // cannot create instance } + if (DamageType != none && DamageType.default.bCanApplyRadialCalculationtoAffliction == false) + { + bCanApplyRadial = false; + } + // for radius damage apply falloff using most recent HitFxInfo - if ( HitFxInfo.bRadialDamage && HitFxRadialInfo.RadiusDamageScale != 255 ) + if (bCanApplyRadial && HitFxInfo.bRadialDamage && HitFxRadialInfo.RadiusDamageScale != 255) { InPower *= ByteToFloat(HitFxRadialInfo.RadiusDamageScale); `log(Type@"Applied damage falloff modifier of"@ByteToFloat(HitFxRadialInfo.RadiusDamageScale), bDebugLog); diff --git a/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc b/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc index fc486f0..7f24ec9 100644 --- a/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc +++ b/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc @@ -25,12 +25,12 @@ defaultproperties DoT_Interval=0.5 DoT_DamageScale=0.5 - BleedPower = 20 - PoisonPower = 25 + BleedPower = 15 + PoisonPower = 20 ModifierPerkList(0)=class'KFPerk_Survivalist' WeaponDef=class'KFWeapDef_HRG_Locust' - SpreadOnTouchDamage=30 + SpreadOnTouchDamage=25 } \ No newline at end of file diff --git a/KFGame/Classes/KFDT_WeeklyContamination.uc b/KFGame/Classes/KFDT_WeeklyContamination.uc new file mode 100644 index 0000000..5cb8135 --- /dev/null +++ b/KFGame/Classes/KFDT_WeeklyContamination.uc @@ -0,0 +1,31 @@ +//============================================================================= +// KFDT_WeeklyContamination +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_WeeklyContamination extends KFDT_Bleeding + abstract + hidedropdown; + +var int Damage; + +static function int GetDamage() +{ + return default.Damage; +} + +defaultproperties +{ + // CameraLensEffectTemplate=class'KFCameraLensEmit_Puke' + + DoT_Type=DOT_Bleeding + DoT_Duration=3.0 + DoT_Interval=0.5 + DoT_DamageScale=0.5 + + BleedPower = 2 + PoisonPower = 0 + + Damage=7 +} \ No newline at end of file diff --git a/KFGame/Classes/KFDamageType.uc b/KFGame/Classes/KFDamageType.uc index ce94a48..4183c0e 100644 --- a/KFGame/Classes/KFDamageType.uc +++ b/KFGame/Classes/KFDamageType.uc @@ -239,6 +239,15 @@ var bool bIsTrapDamage; //When doing armour piercing damage (this is a % of total damage received, the rest is considered as base if defined) var float DamageModifierAP; +// If killing with this damage type can trigger zed time +var bool bCanZedTime; + +// If damaging with this can trigger rage +var bool bCanEnrage; + +// If applies radial impulse an it affect the affliction power as well? +var bool bCanApplyRadialCalculationtoAffliction; + /** * Take the primary HitDirection and modify it to add more spread. * Use the BloodSpread property to calculate the spread amount @@ -459,4 +468,9 @@ Defaultproperties DamageModifierAP=0.f bCanPlayDeadHitEffects=true -} + + bCanZedTime=true + bCanEnrage=true + + bCanApplyRadialCalculationtoAffliction=true +} \ No newline at end of file diff --git a/KFGame/Classes/KFDestructibleActor.uc b/KFGame/Classes/KFDestructibleActor.uc index 602d94f..3c87036 100644 --- a/KFGame/Classes/KFDestructibleActor.uc +++ b/KFGame/Classes/KFDestructibleActor.uc @@ -646,11 +646,12 @@ simulated function TakeRadiusDamage vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) { bIsRadiusDamage = true; - Super.TakeRadiusDamage(InstigatedBy, BaseDamage, DamageRadius, DamageType, Momentum, HurtOrigin, bFullDamage, DamageCauser, DamageFalloffExponent); + Super.TakeRadiusDamage(InstigatedBy, BaseDamage, DamageRadius, DamageType, Momentum, HurtOrigin, bFullDamage, DamageCauser, DamageFalloffExponent, bAdjustRadiusDamage); bIsRadiusDamage = false; } diff --git a/KFGame/Classes/KFFracturedMeshGlass.uc b/KFGame/Classes/KFFracturedMeshGlass.uc index 995e965..ebb56e7 100644 --- a/KFGame/Classes/KFFracturedMeshGlass.uc +++ b/KFGame/Classes/KFFracturedMeshGlass.uc @@ -147,10 +147,11 @@ simulated function TakeRadiusDamage vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) { - Super.TakeRadiusDamage(InstigatedBy, BaseDamage, DamageRadius, DamageType, Momentum, HurtOrigin, true, DamageCauser, DamageFalloffExponent); + Super.TakeRadiusDamage(InstigatedBy, BaseDamage, DamageRadius, DamageType, Momentum, HurtOrigin, true, DamageCauser, DamageFalloffExponent, bAdjustRadiusDamage); } defaultproperties diff --git a/KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Summer2023.uc b/KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Summer2023.uc new file mode 100644 index 0000000..03e9d31 --- /dev/null +++ b/KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Summer2023.uc @@ -0,0 +1,27 @@ +class KFGFXSpecialEventObjectivesContainer_Summer2023 extends KFGFxSpecialEventObjectivesContainer; + +function Initialize(KFGFxObject_Menu NewParentMenu) +{ + super.Initialize(NewParentMenu); +} + +DefaultProperties +{ + ObjectiveIconURLs[0] = "Summer2023_UI.UI_Objetives_Summer2023_TheBoomestofBuddies" // Kill 1500 Zeds with HRG Bombardier + ObjectiveIconURLs[1] = "Xmas2022_UI.Black_Weekly" // Complete the Weekly on Subduction + ObjectiveIconURLs[2] = "Summer2023_UI.UI_Objectives_Summer2023_ClassicWaterShockCombo" // Stun 2500 Zeds with EMP affliction + ObjectiveIconURLs[3] = "Summer2023_UI.UI_Objectives_Summer2023_DeepWaterareNotStill" // Complete 25 Stand your Ground objectives + ObjectiveIconURLs[4] = "Summer2023_UI.UI_Objectives_Summer2023_SomewhereBeyondtheZ" // Complete wave 15 on Endless Hard or higher difficulty on Subduction + + //defaults + AllCompleteRewardIconURL="CHR_CosmeticSet39_Item_TEX.sharkjaw.sharkjaw_precious" + ChanceDropIconURLs[0]="CHR_CosmeticSet_SS_01_ItemTex.Sideshow_Prize_Ticket" + ChanceDropIconURLs[1]="CHR_CosmeticSet_SS_01_ItemTex.SideshowGoldenTicket_Small" + IconURL="Summer2023_UI.KF2_Summer_2023_DeepBlueZ" + + UsesProgressList[0] = true + UsesProgressList[1] = false + UsesProgressList[2] = true + UsesProgressList[3] = true + UsesProgressList[4] = false +} \ No newline at end of file diff --git a/KFGame/Classes/KFGFxControlsContainer_Keybinding.uc b/KFGame/Classes/KFGFxControlsContainer_Keybinding.uc index b31cd89..ccf4450 100644 --- a/KFGame/Classes/KFGFxControlsContainer_Keybinding.uc +++ b/KFGame/Classes/KFGFxControlsContainer_Keybinding.uc @@ -20,6 +20,7 @@ var config array CombatBindList; var config array WeaponSelectBindList; var config array InteractionBindList; var config array VoiceCommBindList; +var config array OtherBindList; var KeyBind PendingKeyBind; var KeyBind OldKeyBind; @@ -37,7 +38,7 @@ var bool bModAlt; var bool bWaitForInput; var const string SectionName; -const MAX_SECTIONS = 5; +const MAX_SECTIONS = 6; var byte TotalBindSections; var localized array SectionHeaders; @@ -70,7 +71,8 @@ function Initialize( KFGFxObject_Menu NewParentMenu ) InitalizeCommandList(CombatBindList); InitalizeCommandList(WeaponSelectBindList); InitalizeCommandList(VoiceCommBindList); - + InitalizeCommandList(OtherBindList); + UpdateAllBindings(); } @@ -106,6 +108,8 @@ function UpdateAllBindings() UpdateBindList( CombatBindList, 2 ); UpdateBindList( WeaponSelectBindList, 3 ); UpdateBindList( VoiceCommBindList, 4 ); + UpdateBindList( OtherBindList, 5); + Manager.UpdateDynamicIgnoreKeys(); } @@ -156,6 +160,7 @@ function SetSectionBindings( int i, GFxObject bindData ) /** Wait for player input to modify the current bind command */ function ChangeBind( string ChangedCommand, byte SelectedSection ) { + `Log("Change BIND Command " $ChangedCommand); BindCommand = ChangedCommand; CurrentlySelectedSection = SelectedSection; bWaitForInput = true; @@ -265,6 +270,8 @@ function SetKeyBind(KeyBind NewKeyBind) } else { + `Log("BIND BindCommand " $BindCommand); + KFInput.BindKey( NewKeyBind, BindCommand, false ); UpdateAllBindings(); } diff --git a/KFGame/Classes/KFGFxHUD_ObjectiveConatiner.uc b/KFGame/Classes/KFGFxHUD_ObjectiveConatiner.uc index 099a80f..0ce5e92 100644 --- a/KFGame/Classes/KFGFxHUD_ObjectiveConatiner.uc +++ b/KFGame/Classes/KFGFxHUD_ObjectiveConatiner.uc @@ -25,16 +25,88 @@ function InitializeHUD() function LocalizeContainer() { local GFxObject TextObject; + local KFGameReplicationInfo KFGRI; + + KFGRI=KFGameReplicationInfo(KFPC.WorldInfo.GRI); TextObject = CreateObject("Object"); if (CurrentObjectiveInterface != none) { TextObject.SetString("failedString", Localize("Objectives", "FailedString", "KFGame")); + TextObject.SetString("objectiveTitle", Localize("Objectives", "ObjectiveTitle", "KFGame")); - TextObject.SetString("objectiveDesc", CurrentObjectiveInterface.GetLocalizedShortDescription()); + + if (KFGRI.IsContaminationMode()) + { + TextObject.SetBool("HideBonus", true); + + TextObject.SetString("objectiveDesc", Localize("Objectives", "ContaminationModeDescriptionShort", "KFGame")); + } + else + { + TextObject.SetString("objectiveDesc", CurrentObjectiveInterface.GetLocalizedShortDescription()); + } } + SetObject("localizedText", TextObject); +} + +function ContaminationModeSetPlayerIn(bool PlayerIsIn, bool Pulsate) +{ + local GFxObject TextObject; + + TextObject = CreateObject("Object"); + + //if (CurrentObjectiveInterface != none) + //{ + TextObject.SetString("failedString", Localize("Objectives", "FailedString", "KFGame")); + + TextObject.SetString("objectiveTitle", Localize("Objectives", "ObjectiveTitle", "KFGame")); + + TextObject.SetBool("HideBonus", true); + + if (PlayerIsIn) + { + TextObject.SetBool("ContaminationModeIn", true); + TextObject.SetString("objectiveDesc", Localize("Objectives", "ContaminationModeDescriptionShortPlayerIn", "KFGame")); + } + else + { + TextObject.SetBool("ContaminationModeOut", true); + + if (Pulsate) + { + TextObject.SetString("objectiveDesc", Localize("Objectives", "ContaminationModeDescriptionShortPlayerOut", "KFGame")); + } + else + { + TextObject.SetString("objectiveDesc", ""); + } + } + //} + + SetObject("localizedText", TextObject); +} + +function ContaminationModeTimer(int Timer) +{ + local GFxObject TextObject; + + TextObject = CreateObject("Object"); + + //if (CurrentObjectiveInterface != none) + //{ + TextObject.SetString("failedString", Localize("Objectives", "FailedString", "KFGame")); + + TextObject.SetString("objectiveTitle", Localize("Objectives", "ObjectiveTitle", "KFGame")); + + TextObject.SetBool("HideBonus", true); + + TextObject.SetString("objectiveDesc", Localize("Objectives", "ContaminationModeDescriptionShort", "KFGame")); + + TextObject.SetInt("ContaminationTimer", Timer); + //} SetObject("localizedText", TextObject); } @@ -42,6 +114,7 @@ function LocalizeContainer() simulated function SetActive(bool bActive) { SetVisible(bActive); + if (bActive) { CurrentObjectiveInterface = KFGameReplicationInfo(GetPC().WorldInfo.GRI).ObjectiveInterface; @@ -72,12 +145,18 @@ simulated function SetActive(bool bActive) function SetCompleted(bool bComplete) { local GFxObject DataObject; + local KFGameReplicationInfo KFGRI; - DataObject = CreateObject("Object"); + KFGRI=KFGameReplicationInfo(KFPC.WorldInfo.GRI); - DataObject.SetBool("bComplete", bComplete); - DataObject.SetString("completeString", bComplete ? Localize("Objectives", "SuccessString", "KFGame") : ""); - SetObject("completeStatus", DataObject); + if (KFGRI.IsContaminationMode() == false) + { + DataObject = CreateObject("Object"); + + DataObject.SetBool("bComplete", bComplete); + DataObject.SetString("completeString", bComplete ? Localize("Objectives", "SuccessString", "KFGame") : ""); + SetObject("completeStatus", DataObject); + } if (!bComplete) { @@ -119,6 +198,14 @@ function TickHud(float DeltaTime) local int bStatusWarning, bStatusNotification; local string StatusMessage; local GFxObject DataObject; + local KFGameReplicationInfo KFGRI; + + KFGRI=KFGameReplicationInfo(KFPC.WorldInfo.GRI); + + if (KFGRI.IsContaminationMode()) + { + return; + } if (CurrentObjectiveInterface != none) { @@ -144,6 +231,7 @@ function TickHud(float DeltaTime) { UpdateIcon(); } + SetMissionCritical(CurrentObjectiveInterface.GetIsMissionCritical()); } } @@ -179,6 +267,11 @@ function SetFailState(bool bFailed) } } +function ShowObjectiveUI() +{ + SetVisible(true); +} + function ClearObjectiveUI() { SetActive(false); @@ -187,6 +280,15 @@ function ClearObjectiveUI() //pass a value from 0-1 function SetCurrentProgress(float CurrentProgress) { + local KFGameReplicationInfo KFGRI; + + KFGRI=KFGameReplicationInfo(KFPC.WorldInfo.GRI); + + if (KFGRI.IsContaminationMode()) + { + return; + } + if (LastProgress != CurrentProgress) { CurrentProgress = FClamp(CurrentProgress, 0, 1); diff --git a/KFGame/Classes/KFGFxHUD_PlayerStatus.uc b/KFGame/Classes/KFGFxHUD_PlayerStatus.uc index bd1f2d1..ec8484d 100644 --- a/KFGame/Classes/KFGFxHUD_PlayerStatus.uc +++ b/KFGame/Classes/KFGFxHUD_PlayerStatus.uc @@ -242,6 +242,11 @@ function UpdateGlobalDamage() } } +function UpdateContaminationModeIconVisible(bool bisVisible) +{ + SetBool("ContaminationModeIconVisible", bisVisible); +} + DefaultProperties { LastUpdateTime = 0.f; diff --git a/KFGame/Classes/KFGFxMenu_Inventory.uc b/KFGame/Classes/KFGFxMenu_Inventory.uc index 7c574bf..286da3d 100644 --- a/KFGame/Classes/KFGFxMenu_Inventory.uc +++ b/KFGame/Classes/KFGFxMenu_Inventory.uc @@ -58,6 +58,10 @@ var localized string RarityFilterString; var localized string WeaponTypeFilterString; var localized string PerkFilterString; +var localized string SearchText; +var localized string ClearSearchText; + +var int SearchMaxChars; var GFxObject CraftingSubMenu; var GFxObject ItemListContainer; @@ -133,7 +137,7 @@ struct InventoryHelper var int ItemCount; var ItemType Type; var GFxObject GfxItemObject; - + var string FullName; // For ordering in weapon skins var int WeaponDef; var int Price; @@ -188,6 +192,8 @@ struct ByTypeItemsHelper var() array ItemsOnType; }; +var ByTypeItemsHelper ByTypeItems[7]; + var EInventoryWeaponType_Filter CurrentWeaponTypeFilter; var int CurrentPerkIndexFilter; var ItemRarity CurrentRarityFilter; @@ -198,6 +204,8 @@ var ExchangeRuleSets RuleToExchange; var private int CrcTable[256]; +var transient string SearchKeyword; + function InitializeMenu( KFGFxMoviePlayer_Manager InManager ) { super.InitializeMenu( InManager ); @@ -383,7 +391,6 @@ function InitInventory() local ItemProperties TempItemDetailsHolder; local GFxObject ItemArray, ItemObject; local bool bActiveItem; - local ByTypeItemsHelper ByTypeItems[7]; local InventoryHelper HelperItem; local array ExchangeRules; local class WeaponDef; @@ -391,6 +398,11 @@ function InitInventory() local GFxObject PendingItem; + for (i = 0; i < ArrayCount(ByTypeItems); ++i) + { + ByTypeItems[i].ItemsOnType.Length = 0; + } + ItemArray = CreateArray(); if(OnlineSub == none) @@ -424,7 +436,7 @@ function InitInventory() if (HelperIndex == INDEX_NONE) { HelperItem.Type = TempItemDetailsHolder.Type; - //HelperItem.FullName = TempItemDetailsHolder.Name; + HelperItem.FullName = TempItemDetailsHolder.Name; HelperItem.ItemDefinition = onlineSub.CurrentInventory[i].Definition; HelperItem.ItemCount = onlineSub.CurrentInventory[i].Quantity; @@ -823,17 +835,24 @@ function InitInventory() z = 0; - for (i = 0; i < ArrayCount(ByTypeItems); i++) + if (SearchKeyword != "") { - for (j = 0 ; j < ByTypeItems[i].ItemsOnType.Length; j++) - { - ItemArray.SetElementObject(z, ByTypeItems[i].ItemsOnType[j].GfxItemObject); - - ++z; - } + InventorySearch(SearchKeyword); } + else + { + for (i = 0; i < ArrayCount(ByTypeItems); i++) + { + for (j = 0 ; j < ByTypeItems[i].ItemsOnType.Length; j++) + { + ItemArray.SetElementObject(z, ByTypeItems[i].ItemsOnType[j].GfxItemObject); - SetObject("inventoryList", ItemArray); + ++z; + } + } + + SetObject("inventoryList", ItemArray); + } if(Manager.SelectIDOnOpen != INDEX_NONE ) { @@ -1071,6 +1090,11 @@ function LocalizeText() LocalizedObject.SetString("filterName_1", PerkFilterString); LocalizedObject.SetString("filterName_2", WeaponTypeFilterString); + LocalizedObject.SetString("searchTitle", SearchText); + LocalizedObject.SetString("searchText", SearchText$"..."); + + LocalizedObject.SetBool("bIsConsoleBuild", class'WorldInfo'.static.IsConsoleBuild()); + LocalizedObject.SetInt("searchMaxChars", SearchMaxChars); RarityList = CreateArray(); for (i = 0; i <= ITR_NONE; i++) @@ -1129,6 +1153,7 @@ function LocalizeText() LocalizedObject.SetObject("filterData_1", PerkList); LocalizedObject.SetObject("filterData_2", WeaponTypeList); + SetObject("localizedText", LocalizedObject); } @@ -1398,6 +1423,119 @@ function Callback_RequestInitialnventory() InitInventory(); } +function Callback_InventorySearch(string searchStr) +{ + InventorySearch(searchStr, true); +} + +function Callback_Log(string str) +{ + `Log("From AS: " $str); +} + +function Callback_OpenKeyboard() +{ + OnlineSub = Class'GameEngine'.static.GetOnlineSubsystem(); + OnlineSub.PlayerInterface.AddKeyboardInputDoneDelegate(KeyboardInputComplete); + OnlineSub.PlayerInterface.ShowKeyboardUI(0, "Search", "Search"); +} + +function KeyboardInputComplete(bool bWasSuccessful) +{ + local byte bWasCancelled; + local string UserInput; + + OnlineSub = Class'GameEngine'.static.GetOnlineSubsystem(); + UserInput = OnlineSub.PlayerInterface.GetKeyboardInputResults(bWasCancelled); + + Callback_InventorySearch(UserInput); + + OnlineSub.PlayerInterface.ClearKeyboardInputDoneDelegate(KeyboardInputComplete); +} + +function InventorySearch(string searchStr, bool bForceInitIfClear = false) +{ + local array ItemTypes; + local int i, j, k, ItemCounter; + local array ItemsOnType; + local GFxObject ItemArray; + local array SearchKeywords; + local bool Accepted; + + SearchKeyword = searchStr; + + if (searchStr == "") + { + if (bForceInitIfClear) + { + InitInventory(); + } + + return; + } + + SearchKeywords = SplitString( searchStr, " ", true); + + ItemCounter = 0; + + ItemArray = CreateArray(); + + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_WeaponSkins) + { + ItemTypes.AddItem(ITP_WeaponSkin); + } + + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_Cosmetics) + { + ItemTypes.AddItem(ITP_CharacterSkin); + } + + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_Consumables) + { + ItemTypes.AddItem(ITP_KeyCrate); + } + + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_CraftingMats) + { + ItemTypes.AddItem(ITP_CraftingComponent); + } + + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_Emotes) + { + ItemTypes.AddItem(ITP_Emote); + } + + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_SFX) + { + ItemTypes.AddItem(ITP_SFX); + } + + for (i = 0; i < ItemTypes.Length; ++i) + { + ItemsOnType = ByTypeItems[ItemTypes[i]].ItemsOnType; + for (j = 0; j < ItemsOnType.Length; ++j) + { + Accepted = true; + for (k = 0; k < SearchKeywords.Length; ++k) + { + if (InStr(Locs(ItemsOnType[j].FullName), Locs(SearchKeywords[k])) == -1) + { + Accepted = false; + break; + } + } + + if (Accepted) + { + ItemArray.SetElementObject(ItemCounter, ItemsOnType[j].GfxItemObject); + ++ItemCounter; + } + } + } + + SetObject("inventoryList", ItemArray); +} + function Callback_InventoryFilter( int FilterIndex ) { local EINventory_Filter NewFilter; @@ -1809,4 +1947,6 @@ defaultproperties KeylessCrateIDs(0)=5313 KillThatDangSoundEvent=AkEvent'WW_UI_Menu.Play_UI_Trader_Build_Stop_No_Sound' + + SearchMaxChars=128 } \ No newline at end of file diff --git a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc index 1665e1d..7dad4a6 100644 --- a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc +++ b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc @@ -1016,13 +1016,28 @@ simulated function DisplayNewObjective() ObjectiveObject=CreateObject("Object"); ObjectiveObject.SetString("titleString", class'KFLocalMessage_Priority'.default.ObjectiveStartMessage); - ObjectiveObject.SetString("nameString", ObjectiveInterface.GetLocalizedName()); - ObjectiveObject.SetString("descString", ObjectiveInterface.GetLocalizedDescription()); - ObjectiveObject.SetString("requireString", ObjectiveInterface.GetLocalizedRequirements()); - ObjectiveObject.SetString("rewardNum", string(ObjectiveInterface.GetMaxDoshReward())); - ObjectiveObject.SetString("xpBonus", string(ObjectiveInterface.GetMaxXPReward())); - ObjectiveObject.SetString("voshBonus", string(ObjectiveInterface.GetMaxVoshReward())); - ObjectiveObject.SetString("iconPath", "img://"$PathName(ObjectiveInterface.GetIcon())); + + if (KFGRI.IsContaminationMode()) + { + ObjectiveObject.SetString("nameString", Localize("Objectives", "ContaminationModeObjective", "KFGame")); + ObjectiveObject.SetString("descString", Localize("Objectives", "ContaminationModeDescription", "KFGame")); + + // TODO :: CHANGE ICON + ObjectiveObject.SetString("iconPath", "img://"$PathName(ObjectiveInterface.GetIcon())); + } + else + { + ObjectiveObject.SetString("nameString", ObjectiveInterface.GetLocalizedName()); + ObjectiveObject.SetString("descString", ObjectiveInterface.GetLocalizedDescription()); + + ObjectiveObject.SetString("requireString", ObjectiveInterface.GetLocalizedRequirements()); + ObjectiveObject.SetString("rewardNum", string(ObjectiveInterface.GetMaxDoshReward())); + ObjectiveObject.SetString("xpBonus", string(ObjectiveInterface.GetMaxXPReward())); + ObjectiveObject.SetString("voshBonus", string(ObjectiveInterface.GetMaxVoshReward())); + + ObjectiveObject.SetString("iconPath", "img://"$PathName(ObjectiveInterface.GetIcon())); + } + ObjectiveObject.SetBool("isBonus", false); KFGRI.PreviousObjectiveResult=INDEX_NONE; @@ -1190,6 +1205,50 @@ function UpdateVIP(ReplicatedVIPGameInfo VIPInfo, bool bIsVIP) } } +function UpdateContaminationMode(bool IsPlayerIn, bool Pulsate) +{ + if (KFPC.CanUseContaminationMode()) + { + if (WaveInfoWidget.ObjectiveContainer != none) + { + WaveInfoWidget.ObjectiveContainer.ContaminationModeSetPlayerIn(IsPlayerIn, Pulsate); + } + } +} + +function UpdateContaminationMode_Timer(int Timer) +{ + if (KFPC.CanUseContaminationMode()) + { + if (WaveInfoWidget.ObjectiveContainer != none) + { + WaveInfoWidget.ObjectiveContainer.ContaminationModeTimer(Timer); + } + } +} + +function ShowContaminationMode() +{ + if (KFPC.CanUseContaminationMode()) + { + if (WaveInfoWidget.ObjectiveContainer != none) + { + WaveInfoWidget.ObjectiveContainer.ShowObjectiveUI(); + } + } +} + +function HideContaminationMode() +{ + if (KFPC.CanUseContaminationMode()) + { + if (WaveInfoWidget.ObjectiveContainer != none) + { + WaveInfoWidget.ObjectiveContainer.ClearObjectiveUI(); + } + } +} + function ResetSyringe(float Ammo, float MaxAmmo) { if (PlayerStatusContainer != none) diff --git a/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc b/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc index 8dd85d7..2182d78 100644 --- a/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc +++ b/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc @@ -20,7 +20,9 @@ const MapNightmare = 'KF-Nightmare'; const MapPowerCore = 'KF-PowerCore_Holdout'; const MapDescent = 'KF-TheDescent'; const MapKrampus = 'KF-KrampusLair'; +const MapSantas = 'KF-SantasWorkshop'; const MapSteam = 'KF-SteamFortress'; +const MapElysium = 'KF-Elysium'; //============================================================== // Initialization @@ -56,6 +58,7 @@ function SetMapOptions() local bool IsBrokenTrader; local bool IsBossRush; local bool IsGunGame; + local bool IsContaminationMode; local bool bShouldSkipMaps; local name MapName; @@ -71,6 +74,7 @@ function SetMapOptions() IsBrokenTrader = KFGRI.CurrentWeeklyIndex == 11; IsBossRush = KFGRI.CurrentWeeklyIndex == 14; IsGunGame = KFGRI.CurrentWeeklyIndex == 16; + IsContaminationMode = KFGRI.CurrentWeeklyIndex == 19; bShouldSkipMaps = IsWeeklyMode && (IsBrokenTrader || IsBossRush || IsGunGame); @@ -80,6 +84,7 @@ function SetMapOptions() for (i = 0; i < ServerMapList.length; i++) { MapName = name(ServerMapList[i]); + if ( bShouldSkipMaps && ( MapName == MapBiolapse || MapName == MapNightmare || MapName == MapPowerCore || @@ -89,6 +94,20 @@ function SetMapOptions() continue; } + if (IsWeeklyMode && IsContaminationMode) + { + if (MapName == MapBiolapse || + MapName == MapNightmare || + MapName == MapPowerCore || + MapName == MapDescent || + MapName == MapKrampus || + MapName == MapElysium || + MapName == MapSantas) + { + continue; + } + } + /* Temporary removal of SteamFrotress for BossRush */ if (IsWeeklyMode && IsBossRush && MapName == MapSteam) { diff --git a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc index 17ce0c7..e819484 100644 --- a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc +++ b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc @@ -182,7 +182,7 @@ function FillWhatsNew() local SWhatsNew item; WhatsNewItems.Remove(0, WhatsNewItems.Length); // Latest Update - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_Event", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_Event", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); WhatsNewItems.AddItem(item); // Featured Ultimate Edition item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_UltimateEdition_Upgrade", "FeaturedItemBundle", "https://store.steampowered.com/app/1914560/KF2__Ultimate_Edition_Upgrade_DLC/"); @@ -193,32 +193,29 @@ function FillWhatsNew() // KF2 Armory Season Pass 2021 item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Spring_Armory_Season_Pass", "ArmorySeasonPass", "https://store.steampowered.com/app/1524820/Killing_Floor_2__Armory_Season_Pass"); WhatsNewItems.AddItem(item); -// Featured Weapon Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_WeaponsBundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9557"); - WhatsNewItems.AddItem(item); +// KF2 Armory Season Pass 2023 + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_CosmeticsSeasonPass", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9661"); + WhatsNewItems.AddItem(item); // Featured Weapon - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_ZEDMKIII", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9555"); - WhatsNewItems.AddItem(item); -// Featured Weapon - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_HVStormCannon","FeaturedItemBundle","https://store.steampowered.com/buyitem/232090/9556"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_S12Shockgun", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9655"); WhatsNewItems.AddItem(item); // Featured Outfit Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_TrainConductorOutfit", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9554"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_HorzineDiver_Uniforms", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9653"); WhatsNewItems.AddItem(item); // Featured Time Limited Item - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Christmas_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/5588"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_SS_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/4928"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_Tacticool_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9553"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_Predator_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9651"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_Retro_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9552"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_JunkyardMKII_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9646"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_Mediaval_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9503"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_JaegerMKIV_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9645"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_ChamaleonIII_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9502"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_Stingray_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9649"); WhatsNewItems.AddItem(item); // Misc Community Links item=SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_CommunityHub", "Jaegorhorn", "https://steamcommunity.com/app/232090"); diff --git a/KFGame/Classes/KFGFxStartGameContainer_Options.uc b/KFGame/Classes/KFGFxStartGameContainer_Options.uc index 46b1c9d..df70bff 100644 --- a/KFGame/Classes/KFGFxStartGameContainer_Options.uc +++ b/KFGame/Classes/KFGFxStartGameContainer_Options.uc @@ -304,6 +304,17 @@ function FilterWeeklyMaps(out array List) List.RemoveItem("KF-KrampusLair"); } + if (WeeklyIndex == 19) + { + List.RemoveItem("KF-Biolapse"); + List.RemoveItem("KF-Nightmare"); + List.RemoveItem("KF-PowerCore_Holdout"); + List.RemoveItem("KF-TheDescent"); + List.RemoveItem("KF-KrampusLair"); + List.RemoveItem("KF-SantasWorkshop"); + List.RemoveItem("KF-Elysium"); + } + /* Temporary removal of SteamFrotress for BossRush */ if (WeeklyIndex == 14) { diff --git a/KFGame/Classes/KFGFxStoreContainer_Main.uc b/KFGame/Classes/KFGFxStoreContainer_Main.uc index 1015f6d..2c0afa6 100644 --- a/KFGame/Classes/KFGFxStoreContainer_Main.uc +++ b/KFGame/Classes/KFGFxStoreContainer_Main.uc @@ -447,25 +447,21 @@ DefaultProperties XboxFilterExceptions[0]="Wasteland Bundle" // Wasteland Outfit Bundle - FeaturedItemIDs[0]=7944 //Whatsnew Gold Ticket - FeaturedItemIDs[1]=9557 - FeaturedItemIDs[2]=9556 - FeaturedItemIDs[3]=9555 - FeaturedItemIDs[4]=9554 - FeaturedItemIDs[5]=9502 - FeaturedItemIDs[6]=9503 - FeaturedItemIDs[7]=9553 - FeaturedItemIDs[8]=9552 + FeaturedItemIDs[0]=8178 //Whatsnew Gold Ticket + FeaturedItemIDs[1]=9655 + FeaturedItemIDs[2]=9646 + FeaturedItemIDs[3]=9649 + FeaturedItemIDs[4]=9645 + FeaturedItemIDs[5]=9653 + FeaturedItemIDs[6]=9651 - ConsoleFeaturedItemIDs[0]=7947 //Whatsnew Gold Ticket PSN - ConsoleFeaturedItemIDs[1]=9557 - ConsoleFeaturedItemIDs[2]=9556 - ConsoleFeaturedItemIDs[3]=9555 - ConsoleFeaturedItemIDs[4]=9554 - ConsoleFeaturedItemIDs[5]=9502 - ConsoleFeaturedItemIDs[6]=9503 - ConsoleFeaturedItemIDs[7]=9553 - ConsoleFeaturedItemIDs[8]=9552 + ConsoleFeaturedItemIDs[0]=8181 //Whatsnew Gold Ticket PSN + ConsoleFeaturedItemIDs[1]=9655 + ConsoleFeaturedItemIDs[2]=9646 + ConsoleFeaturedItemIDs[3]=9649 + ConsoleFeaturedItemIDs[4]=9645 + ConsoleFeaturedItemIDs[5]=9653 + ConsoleFeaturedItemIDs[6]=9651 MaxFeaturedItems=5 } \ No newline at end of file diff --git a/KFGame/Classes/KFGFxWidget_ButtonPrompt.uc b/KFGame/Classes/KFGFxWidget_ButtonPrompt.uc index 6b2be38..b1fe2da 100644 --- a/KFGame/Classes/KFGFxWidget_ButtonPrompt.uc +++ b/KFGame/Classes/KFGFxWidget_ButtonPrompt.uc @@ -27,6 +27,8 @@ function LocalizeWidget() LocalizedObject.SetString("cancel", CancelString); LocalizedObject.SetString("confirm", ConfirmString); + LocalizedObject.SetString("search", Localize("KFGFxMenu_Inventory", "SearchText", "KFGame")); + LocalizedObject.SetString("clearsearch", Localize("KFGFxMenu_Inventory", "ClearSearchText", "KFGame")); LocalizedObject.SetString("reset",Localize("KFGFxOptionsMenu_Graphics","DefaultString","KFGame")); LocalizedObject.SetString("party", Localize("KFGFxWidget_BaseParty", "SquadString", "KFGame")); LocalizedObject.SetString("config", Localize("KFGFxPerksContainer_SkillsSummary", "ConfigureString", "KFGame")); diff --git a/KFGame/Classes/KFGFxWidget_VoiceComms.uc b/KFGame/Classes/KFGFxWidget_VoiceComms.uc index 82fb0dc..3541801 100644 --- a/KFGame/Classes/KFGFxWidget_VoiceComms.uc +++ b/KFGame/Classes/KFGFxWidget_VoiceComms.uc @@ -48,6 +48,12 @@ function SetLocalizedText() } SetObject("textOptions", DataProvider); + + TempObj = CreateObject("Object"); + TempObj.SetString("text", class'KFLocalMessage_VoiceComms'.default.ToggleFriendlyHUDString); + SetObject("toggleFriendlyText", TempObj); + + SetBool("toggleFriendlyVisibility", GetPC().WorldInfo.NetMode != NM_StandAlone); } function SaveVoiceCommSelection( int CommsIndex ) diff --git a/KFGame/Classes/KFGameInfo.uc b/KFGame/Classes/KFGameInfo.uc index eb1d176..4819a2a 100644 --- a/KFGame/Classes/KFGameInfo.uc +++ b/KFGame/Classes/KFGameInfo.uc @@ -450,7 +450,9 @@ const MapNightmare = 'KF-Nightmare'; const MapPowerCore = 'KF-PowerCore_Holdout'; const MapDescent = 'KF-TheDescent'; const MapKrampus = 'KF-KrampusLair'; +const MapSantas = 'KF-SantasWorkshop'; const MapSteam = 'KF-SteamFortress'; +const MapElysium = 'KF-Elysium'; /************************************************************************************ * @name Native @@ -2795,6 +2797,12 @@ function CheckZedTimeOnKill(Controller Killer, Controller KilledPlayer, Pawn Kil return; } + // Skip for certain damage types we want to discard + if (KFDT != none && KFDT.default.bCanZedTime == false) + { + return; + } + // If already in zed time, check for zed time extensions if ( IsZedTimeActive() ) { @@ -3011,6 +3019,22 @@ function string GetNextMap() } } + if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 19 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[19]) // Contamination + { + MapName = name(GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]); + + if (MapName == MapBiolapse || + MapName == MapNightmare || + MapName == MapPowerCore || + MapName == MapDescent || + MapName == MapKrampus || + MapName == MapElysium || + MapName == MapSantas) + { + continue; + } + } + /* Temporary removal of SteamFrotress for BossRush */ if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 14 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[14] && MapName == MapSteam) diff --git a/KFGame/Classes/KFGameReplicationInfo.uc b/KFGame/Classes/KFGameReplicationInfo.uc index a8ee111..c614a9d 100644 --- a/KFGame/Classes/KFGameReplicationInfo.uc +++ b/KFGame/Classes/KFGameReplicationInfo.uc @@ -1970,6 +1970,7 @@ function ChooseNextObjective(int NextWaveNum) return; KFMI = KFMapInfo(WorldInfo.GetMapInfo()); + if (KFMI != none && NextWaveNum != WaveMax) { if (KFMI.bUsePresetObjectives && NextWaveNum <= GetPresetObjectiveLength(KFMI)) @@ -2047,12 +2048,13 @@ function int GetPresetObjectiveLength(KFMapInfo KFMI) } } -function bool ChooseNextRandomObjective(KFMapInfo KFMI, int NextWaveNum) +function bool ChooseNextRandomObjective(KFMapInfo KFMI, int NextWaveNum, optional bool bWaveCanDisable = true) { local int Idx; Idx = INDEX_NONE; //Start a random objective if we have any set - if (KFMI.RandomWaveObjectives.Length > 0 && KFMI.RandomObjectiveWavesToDisable.Find(NextWaveNum) == INDEX_NONE) + + if (KFMI.RandomWaveObjectives.Length > 0 && (bWaveCanDisable == false || KFMI.RandomObjectiveWavesToDisable.Find(NextWaveNum) == INDEX_NONE)) { //Attempt to reset if we've run out if (KFMI.CurrentAvailableRandomWaveObjectives.Length == 0) @@ -2084,7 +2086,10 @@ function int SetNextObjective(array PossibleObjectives if (PossibleObjectives[RandID].CanActivateObjectiveByWeekly()) { PctChanceToActivate = PossibleObjectives[RandID].GetActivationPctChance(); - if (bForceNextObjective || (PossibleObjectives[RandID].CanActivateObjective() && PreviousObjective != PossibleObjectives[RandID] && (PctChanceToActivate >= 1.f || DieRoll <= PctChanceToActivate))) + + if (bForceNextObjective || (PossibleObjectives[RandID].CanActivateObjective() + && PreviousObjective != PossibleObjectives[RandID] + && (PctChanceToActivate >= 1.f || DieRoll <= PctChanceToActivate))) { if (bActivateImmediately) { @@ -2096,6 +2101,7 @@ function int SetNextObjective(array PossibleObjectives NextObjectiveIsEndless = bUseEndlessSpawning; KFInterface_MapObjective(NextObjective).NotifyObjectiveSelected(); } + return RandID; } } @@ -2406,6 +2412,43 @@ simulated function bool IsRandomPerkMode() return bIsWeeklyMode && CurrentWeeklyIndex == 18; } +simulated function bool IsContaminationMode() +{ + return bIsWeeklyMode && CurrentWeeklyIndex == 19; +} + +simulated function int ContaminationModeZedsToFinish() +{ + local KFGameInfo KFGI; + + if (IsContaminationMode()) + { + KFGI = KFGameInfo(WorldInfo.Game); + if (KFGI != none && KFGI.OutbreakEvent != none) + { + return KFGI.OutbreakEvent.ActiveEvent.ContaminationModeZedsToFinish; + } + } + + return 0; +} + +simulated function int ContaminationModeExtraDosh() +{ + local KFGameInfo KFGI; + + if (IsContaminationMode()) + { + KFGI = KFGameInfo(WorldInfo.Game); + if (KFGI != none && KFGI.OutbreakEvent != none) + { + return KFGI.OutbreakEvent.ActiveEvent.ContaminationModeExtraDosh; + } + } + + return 0; +} + defaultproperties { TraderItemsPath="GP_Trader_ARCH.DefaultTraderItems" diff --git a/KFGame/Classes/KFGamepadLayoutManager.uc b/KFGame/Classes/KFGamepadLayoutManager.uc index 6d9b104..0120991 100644 --- a/KFGame/Classes/KFGamepadLayoutManager.uc +++ b/KFGame/Classes/KFGamepadLayoutManager.uc @@ -78,7 +78,7 @@ defaultproperties ButtonCommands[5]=(Name="XboxTypeS_RightX",Command="GBA_TurnLeft_Gamepad"), ButtonCommands[6]=(Name="XboxTypeS_RightY",Command="GBA_Look_Gamepad"), ButtonCommands[7]=(Name="XboxTypeS_RightThumbStick",Command="GBA_TertiaryFire"), - ButtonCommands[8]=(Name="XboxTypeS_LeftShoulder",Command="GBA_Grenade"), + ButtonCommands[8]=(Name="XboxTypeS_LeftShoulder",Command="GBA_Grenade | GBA_ToggleFriendlyUIFromHUD"), ButtonCommands[9]=(Name="XboxTypeS_RightShoulder",Command="GBA_AltFire_Gamepad"), ButtonCommands[10]=(Name="XboxTypeS_LeftTrigger",Command="GBA_IronsightsHold"), ButtonCommands[11]=(Name="XboxTypeS_RightTrigger",Command="GBA_Fire") @@ -94,7 +94,7 @@ defaultproperties ButtonCommands[5]=(Name="XboxTypeS_RightX",Command="GBA_TurnLeft_Gamepad"), ButtonCommands[6]=(Name="XboxTypeS_RightY",Command="GBA_Look_Gamepad"), ButtonCommands[7]=(Name="XboxTypeS_RightThumbStick",Command="GBA_Grenade"), - ButtonCommands[8]=(Name="XboxTypeS_LeftShoulder",Command="GBA_Jump"), + ButtonCommands[8]=(Name="XboxTypeS_LeftShoulder",Command="GBA_Jump | GBA_ToggleFriendlyUIFromHUD"), ButtonCommands[9]=(Name="XboxTypeS_RightShoulder",Command="GBA_AltFire_Gamepad"), ButtonCommands[10]=(Name="XboxTypeS_LeftTrigger",Command="GBA_IronsightsHold"), ButtonCommands[11]=(Name="XboxTypeS_RightTrigger",Command="GBA_Fire"), @@ -111,7 +111,7 @@ defaultproperties ButtonCommands[5]=(Name="XboxTypeS_RightX",Command="GBA_TurnLeft_Gamepad"), ButtonCommands[6]=(Name="XboxTypeS_RightY",Command="GBA_Look_Gamepad"), ButtonCommands[7]=(Name="XboxTypeS_RightThumbStick",Command="GBA_Grenade"), - ButtonCommands[8]=(Name="XboxTypeS_LeftShoulder",Command="GBA_WeaponSelect_Gamepad"), + ButtonCommands[8]=(Name="XboxTypeS_LeftShoulder",Command="GBA_WeaponSelect_Gamepad | GBA_ToggleFriendlyUIFromHUD"), ButtonCommands[9]=(Name="XboxTypeS_RightShoulder",Command="GBA_AltFire_Gamepad"), ButtonCommands[10]=(Name="XboxTypeS_LeftTrigger",Command="GBA_IronsightsHold"), ButtonCommands[11]=(Name="XboxTypeS_RightTrigger",Command="GBA_Fire"), @@ -127,7 +127,7 @@ defaultproperties ButtonCommands[5]=(Name="XboxTypeS_RightX",Command="GBA_StrafeLeft_Gamepad"), ButtonCommands[6]=(Name="XboxTypeS_RightY",Command="GBA_MoveForward_GamepadSouthpaw"), ButtonCommands[7]=(Name="XboxTypeS_RightThumbStick",Command="GBA_SprintAndCrouch"), - ButtonCommands[8]=(Name="XboxTypeS_LeftShoulder",Command="GBA_AltFire_Gamepad"), + ButtonCommands[8]=(Name="XboxTypeS_LeftShoulder",Command="GBA_AltFire_Gamepad | GBA_ToggleFriendlyUIFromHUD"), ButtonCommands[9]=(Name="XboxTypeS_RightShoulder",Command="GBA_Grenade"), ButtonCommands[10]=(Name="XboxTypeS_LeftTrigger",Command="GBA_Fire"), ButtonCommands[11]=(Name="XboxTypeS_RightTrigger",Command="GBA_IronsightsHold"), diff --git a/KFGame/Classes/KFGfxMenu_StartGame.uc b/KFGame/Classes/KFGfxMenu_StartGame.uc index 06c9892..c08d23c 100644 --- a/KFGame/Classes/KFGfxMenu_StartGame.uc +++ b/KFGame/Classes/KFGfxMenu_StartGame.uc @@ -206,7 +206,7 @@ static function class GetSpecialEventClass case SEI_Spring: return class'KFGFxSpecialEventObjectivesContainer_Spring2021'; case SEI_Summer: - return class'KFGFxSpecialEventObjectivesContainer_Summer2022'; + return class'KFGFxSpecialEventObjectivesContainer_Summer2023'; case SEI_Fall: return class'KFGFxSpecialEventObjectivesContainer_Fall2022'; case SEI_Winter: diff --git a/KFGame/Classes/KFHUDBase.uc b/KFGame/Classes/KFHUDBase.uc index 8e352f0..52f8bfa 100644 --- a/KFGame/Classes/KFHUDBase.uc +++ b/KFGame/Classes/KFHUDBase.uc @@ -56,6 +56,7 @@ var float PrestigeIconScale; ********************************************************************************************* */ var config float FriendlyHudScale; +var config bool bFriendlyHUDVisible; /********************************************************************************************* DrawGlowText() @@ -240,7 +241,7 @@ function DrawCrosshair() } // Skip if weapon is missing spread settings - if ( KFWP.Spread.Length == 0 && !bForceDrawCrosshair ) + if ( KFWP.Spread.Length == 0 && !bForceDrawCrosshair && !KFWP.bForceCrosshair) { return; } @@ -280,7 +281,7 @@ function DrawCrosshair() if( KFWP != none ) { - if( !bForceDrawCrosshair ) + if( !bForceDrawCrosshair && !KFWP.bForceCrosshair) { WeaponAccuracyAdjust = EvalInterpCurveFloat(CrosshairAccuracyScale, KFWP.GetUpgradedSpread()); // Scale spread based on weapon accuracy @@ -587,8 +588,6 @@ function DrawHUD() local rotator ViewRotation; local array VisibleHumanPlayers; local array HiddenHumanPlayers; - local float ThisDot; - local vector TargetLocation; local Actor LocActor; // Draw weapon HUD underneath everything else @@ -621,75 +620,128 @@ function DrawHUD() // Friendly player status if( PlayerOwner.GetTeamNum() == 0 ) { - if( KFPlayerOwner != none ) + if (KFPlayerOwner.bFriendlyUIEnabled) { - KFPlayerOwner.GetPlayerViewPoint( ViewLocation, ViewRotation ); - } - ViewVector = vector(ViewRotation); - - Canvas.EnableStencilTest(true); - foreach WorldInfo.AllPawns( class'KFPawn_Human', KFPH ) - { - if( KFPH.IsAliveAndWell() && KFPH != KFPlayerOwner.Pawn && KFPH.Mesh.SkeletalMesh != none && KFPH.Mesh.bAnimTreeInitialised ) + if( KFPlayerOwner != none ) { - PlayerPartyInfoLocation = KFPH.Mesh.GetPosition() + ( KFPH.CylinderComponent.CollisionHeight * vect(0,0,1) ); - if(`TimeSince(KFPH.Mesh.LastRenderTime) < 0.2f && Normal(PlayerPartyInfoLocation - ViewLocation) dot ViewVector > 0.f ) + KFPlayerOwner.GetPlayerViewPoint( ViewLocation, ViewRotation ); + } + ViewVector = vector(ViewRotation); + + Canvas.EnableStencilTest(true); + foreach WorldInfo.AllPawns( class'KFPawn_Human', KFPH ) + { + if( KFPH.IsAliveAndWell() && KFPH != KFPlayerOwner.Pawn && KFPH.Mesh.SkeletalMesh != none && KFPH.Mesh.bAnimTreeInitialised ) { - if( DrawFriendlyHumanPlayerInfo(KFPH) ) + PlayerPartyInfoLocation = KFPH.Mesh.GetPosition() + ( KFPH.CylinderComponent.CollisionHeight * vect(0,0,1) ); + if(`TimeSince(KFPH.Mesh.LastRenderTime) < 0.2f && Normal(PlayerPartyInfoLocation - ViewLocation) dot ViewVector > 0.f ) { - VisibleHumanPlayers.AddItem( KFPH.PlayerReplicationInfo ); + if( DrawFriendlyHumanPlayerInfo(KFPH) ) + { + VisibleHumanPlayers.AddItem( KFPH.PlayerReplicationInfo ); + } + else + { + HiddenHumanPlayers.Insert( 0, 1 ); + HiddenHumanPlayers[0].HumanPawn = KFPH; + HiddenHumanPlayers[0].HumanPRI = KFPH.PlayerReplicationInfo; + } } else { HiddenHumanPlayers.Insert( 0, 1 ); - HiddenHumanPlayers[0].HumanPawn = KFPH; - HiddenHumanPlayers[0].HumanPRI = KFPH.PlayerReplicationInfo; + HiddenHumanPlayers[0].HumanPawn = KFPH; + HiddenHumanPlayers[0].HumanPRI = KFPH.PlayerReplicationInfo; + } + } + } + + foreach WorldInfo.AllPawns(class'KFPawn_Scripted', KFPS) + { + if (KFPS.ShouldShowOnHUD()) + { + PlayerPartyInfoLocation = KFPS.Mesh.GetPosition() + (KFPS.CylinderComponent.CollisionHeight * vect(0,0,1)); + DrawScriptedPawnInfo(KFPS, Normal(PlayerPartyInfoLocation - ViewLocation) dot ViewVector, `TimeSince(KFPS.Mesh.LastRenderTime) < 0.2f); + } + } + + if( !KFGRI.bHidePawnIcons ) + { + // Draw hidden players + CheckAndDrawHiddenPlayerIcons( VisibleHumanPlayers, HiddenHumanPlayers ); + + // Draw last remaining zeds + CheckAndDrawRemainingZedIcons(); + + if (KFGRI.IsContaminationMode()) + { + // While on trader time we let previsualize the next objective icon, so players can get ready + if (KFGRI.bWaveIsActive == false) + { + if (KFGRI.WaveNum != (KFGRI.WaveMax - 1)) + { + CheckDrawContaminationModeObjectiveHUD(ViewLocation, ViewVector); + } + } + else if (KFGRI.ObjectiveInterface != none && KFGRI.ObjectiveInterface.IsActive()) + { + //Draw our current objective location + CheckDrawObjectiveHUD(ViewLocation, ViewVector, LocActor); } } else - { - HiddenHumanPlayers.Insert( 0, 1 ); - HiddenHumanPlayers[0].HumanPawn = KFPH; - HiddenHumanPlayers[0].HumanPRI = KFPH.PlayerReplicationInfo; - } - } - } - - foreach WorldInfo.AllPawns(class'KFPawn_Scripted', KFPS) - { - if (KFPS.ShouldShowOnHUD()) - { - PlayerPartyInfoLocation = KFPS.Mesh.GetPosition() + (KFPS.CylinderComponent.CollisionHeight * vect(0,0,1)); - DrawScriptedPawnInfo(KFPS, Normal(PlayerPartyInfoLocation - ViewLocation) dot ViewVector, `TimeSince(KFPS.Mesh.LastRenderTime) < 0.2f); - } - } - - if( !KFGRI.bHidePawnIcons ) - { - // Draw hidden players - CheckAndDrawHiddenPlayerIcons( VisibleHumanPlayers, HiddenHumanPlayers ); - - // Draw last remaining zeds - CheckAndDrawRemainingZedIcons(); - - //Draw our current objective location - if(KFGRI.CurrentObjective != none && KFGRI.ObjectiveInterface != none) - { - KFGRI.ObjectiveInterface.DrawHUD(self, Canvas); - - TargetLocation = KFGRI.ObjectiveInterface.GetIconLocation(); - ThisDot = Normal((TargetLocation + (class'KFPawn_Human'.default.CylinderComponent.CollisionHeight * vect(0, 0, 1))) - ViewLocation) dot ViewVector; - - if (ThisDot > 0 && - KFGRI.ObjectiveInterface.ShouldShowObjectiveHUD() && - (!KFGRI.ObjectiveInterFace.HasObjectiveDrawDistance() || VSizeSq(TargetLocation - LocActor.Location) < MaxDrawDistanceObjective)) { - DrawObjectiveHUD(); + //Draw our current objective location + CheckDrawObjectiveHUD(ViewLocation, ViewVector, LocActor); } } - } - Canvas.EnableStencilTest(false); + Canvas.EnableStencilTest(false); + } + + } +} + +function CheckDrawContaminationModeObjectiveHUD(vector ViewLocation, vector ViewVector) +{ + local float ThisDot; + local vector TargetLocation; + local KFInterface_MapObjective Objective; + + Objective = KFInterface_MapObjective(KFGRI.NextObjective); + + if (Objective != none) + { + TargetLocation = Objective.GetIconLocation(); + + ThisDot = Normal((TargetLocation + (class'KFPawn_Human'.default.CylinderComponent.CollisionHeight * vect(0, 0, 1))) - ViewLocation) dot ViewVector; + + if (ThisDot > 0) + { + DrawContaminationModeObjectiveHUD(); + } + } +} + +function CheckDrawObjectiveHUD(vector ViewLocation, vector ViewVector, Actor LocActor) +{ + local float ThisDot; + local vector TargetLocation; + + if (KFGRI.CurrentObjective != none && KFGRI.ObjectiveInterface != none) + { + KFGRI.ObjectiveInterface.DrawHUD(self, Canvas); + + TargetLocation = KFGRI.ObjectiveInterface.GetIconLocation(); + + ThisDot = Normal((TargetLocation + (class'KFPawn_Human'.default.CylinderComponent.CollisionHeight * vect(0, 0, 1))) - ViewLocation) dot ViewVector; + + if (ThisDot > 0 && + KFGRI.ObjectiveInterface.ShouldShowObjectiveHUD() && + (!KFGRI.ObjectiveInterFace.HasObjectiveDrawDistance() || VSizeSq(TargetLocation - LocActor.Location) < MaxDrawDistanceObjective)) + { + DrawObjectiveHUD(); + } } } @@ -955,6 +1007,37 @@ simulated function color GetHealthStateColor(const out KFPawn_Scripted KFPS) } } +simulated function bool DrawContaminationModeObjectiveHUD() +{ + local float IconCenteringLength; + local vector ScreenPos, TargetLocation; + local float ResModifier; + local KFInterface_MapObjective Objective; + + Objective = KFInterface_MapObjective(KFGRI.NextObjective); + + ResModifier = WorldInfo.static.GetResolutionBasedHUDScale() * FriendlyHudScale; + + TargetLocation = Objective.GetIconLocation(); + ScreenPos = Canvas.Project( TargetLocation ); + + // if not using the progress bar, center the remaining icon + IconCenteringLength = PlayerStatusIconSize * ResModifier * 0.5; + + if (Objective.GetIcon() != none) + { + Canvas.SetDrawColorStruct(PlayerBarShadowColor); + Canvas.SetPos((ScreenPos.X - IconCenteringLength) + 1, ScreenPos.Y + 1); + Canvas.DrawTile(Objective.GetIcon(), PlayerStatusIconSize * ResModifier, PlayerStatusIconSize * ResModifier, 0, 0, 256, 256); + + Canvas.SetDrawColorStruct(Objective.GetIconColor()); + Canvas.SetPos(ScreenPos.X - IconCenteringLength, ScreenPos.Y); + Canvas.DrawTile(Objective.GetIcon(), PlayerStatusIconSize * ResModifier, PlayerStatusIconSize * ResModifier, 0, 0, 256, 256); + } + + return true; +} + simulated function bool DrawObjectiveHUD() { local float Percentage; @@ -1008,6 +1091,7 @@ simulated function bool DrawObjectiveHUD() Canvas.SetPos(ScreenPos.X - (BarLength * 0.75) - IconCenteringLength, ScreenPos.Y - BarHeight * 2.0); Canvas.DrawTile(KFGRI.ObjectiveInterface.GetIcon(), PlayerStatusIconSize * ResModifier, PlayerStatusIconSize * ResModifier, 0, 0, 256, 256); } + return true; } diff --git a/KFGame/Classes/KFLocalMessage_VoiceComms.uc b/KFGame/Classes/KFLocalMessage_VoiceComms.uc index 76fc8ed..b9e867b 100644 --- a/KFGame/Classes/KFLocalMessage_VoiceComms.uc +++ b/KFGame/Classes/KFLocalMessage_VoiceComms.uc @@ -27,6 +27,8 @@ enum EVoiceCommsType }; var localized array VoiceCommsOptionStrings; +var localized string ToggleFriendlyHUDString; + var array VoiceCommsIcons; static function string GetString( diff --git a/KFGame/Classes/KFOnlineStatsReadDingo.uc b/KFGame/Classes/KFOnlineStatsReadDingo.uc index b36a67c..a9bcf20 100644 --- a/KFGame/Classes/KFOnlineStatsReadDingo.uc +++ b/KFGame/Classes/KFOnlineStatsReadDingo.uc @@ -73,6 +73,7 @@ defaultproperties ColumnIds.Add(STATID_ACHIEVE_RigCollectibles) ColumnIds.Add(STATID_ACHIEVE_BarmwichCollectibles) ColumnIds.Add(STATID_ACHIEVE_CrashCollectibles); + ColumnIds.Add(STATID_ACHIEVE_SubductionCollectibles); ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky5, Name="AchievementMrPerky5")) ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky10, Name = "AchievementMrPerky10")) @@ -134,5 +135,6 @@ defaultproperties ColumnMappings.Add((Id=STATID_ACHIEVE_RigCollectibles,Name="AchievementCollectRig")) ColumnMappings.Add((Id=STATID_ACHIEVE_BarmwichCollectibles,Name="AchievementCollectBarmwichTown")) ColumnMappings.Add((Id=STATID_ACHIEVE_CrashCollectibles,Name="AchievementCollectCrash")) + ColumnMappings.Add((Id=STATID_ACHIEVE_SubductionCollectibles,Name="AchievementCollectSubduction")) } diff --git a/KFGame/Classes/KFOnlineStatsWrite.uc b/KFGame/Classes/KFOnlineStatsWrite.uc index 96d658c..f7deb1f 100644 --- a/KFGame/Classes/KFOnlineStatsWrite.uc +++ b/KFGame/Classes/KFOnlineStatsWrite.uc @@ -453,6 +453,10 @@ const KFACHID_CrashHard = 298; const KFACHID_CrashHellOnEarth = 299; const KFACHID_CrashCollectibles = 300; +const KFACHID_SubductionHard = 301; +const KFACHID_SubductionHellOnEarth = 302; +const KFACHID_SubductionCollectibles = 303; + /* __TW_ANALYTICS_ */ var int PerRoundWeldXP; var int PerRoundHealXP; @@ -2095,7 +2099,7 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Shotgun_ElephantGun, KFDT_Ballistic_ElephantGun,KFDT_Bludgeon_ElephantGun),CompletionAmount=10000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_BlastBrawlers, KFDT_Ballistic_BlastBrawlersShotgun,KFDT_Bludgeon_BlastBrawlers,KFDT_Bludgeon_BlastBrawlersHeavy,KFDT_Bludgeon_BlastBrawlersBash),CompletionAmount=10000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_BallisticBouncer, KFDT_Bludgeon_HRG_BallisticBouncer_Shot,KFDT_Bludgeon_HRG_BallisticBouncer),CompletionAmount=10000)) - + //Medic Weapons DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Pistol_Medic, KFDT_Ballistic_Pistol_Medic,KFDT_Bludgeon_Pistol_Medic),CompletionAmount=5000)) //3000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_SMG_Medic, KFDT_Ballistic_SMG_Medic,KFDT_Bludgeon_SMG_Medic),CompletionAmount=7000)) //5000 @@ -2120,6 +2124,7 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_RocketLauncher_SealSqueal, KFDT_Bludgeon_SealSqueal, KFDT_Explosive_SealSqueal, KFDT_Ballistic_SealSquealImpact),CompletionAmount=7500)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_RocketLauncher_Seeker6, KFDT_Explosive_Seeker6, KFDT_Bludgeon_Seeker6, KFDT_Ballistic_Seeker6Impact),CompletionAmount=7500)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_GrenadeLauncher_M32, KFDT_Bludgeon_M32, KFDT_Explosive_M32, KFDT_Ballistic_M32Impact),CompletionAmount=10000)) + DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_Warthog, KFDT_Bludgeon_HRG_Warthog, KFDT_Explosive_HRG_Warthog, KFDT_Explosive_HRG_Warthog_HighExplosive, KFDT_Ballistic_HRG_Warthog),CompletionAmount=7000)) //Firebug Weapons DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Flame_CaulkBurn, KFDT_Bludgeon_CaulkBurn,KFDT_Fire_CaulkBurn,KFDT_Fire_Ground_CaulkNBurn),CompletionAmount=5000)) //3000 @@ -2325,6 +2330,10 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-CRASH),CompletionAmount=1)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-CRASH),CompletionAmount=2)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-CRASH),CompletionAmount=3)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-SUBDUCTION),CompletionAmount=1)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-SUBDUCTION),CompletionAmount=2)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-SUBDUCTION),CompletionAmount=3)) + //Versus Damage // Per design doc that I have right now, these are x class damage y players, not damage y amount diff --git a/KFGame/Classes/KFOnlineStatsWriteDingo.uc b/KFGame/Classes/KFOnlineStatsWriteDingo.uc index d5e12a1..bd03371 100644 --- a/KFGame/Classes/KFOnlineStatsWriteDingo.uc +++ b/KFGame/Classes/KFOnlineStatsWriteDingo.uc @@ -75,4 +75,5 @@ defaultproperties Properties.Add((PropertyId = STATID_ACHIEVE_RigCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_BarmwichCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_CrashCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) + Properties.Add((PropertyId = STATID_ACHIEVE_SubductionCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) } diff --git a/KFGame/Classes/KFOutbreakEvent.uc b/KFGame/Classes/KFOutbreakEvent.uc index f29ec57..1f13493 100644 --- a/KFGame/Classes/KFOutbreakEvent.uc +++ b/KFGame/Classes/KFOutbreakEvent.uc @@ -464,6 +464,12 @@ struct WeeklyOverrides /** Time between waves override. */ var() float TimeBetweenWaves; + /** Contamination mode Zeds to finish */ + var() int ContaminationModeZedsToFinish; + + /** Contamination mode Extra Dosh */ + var() int ContaminationModeExtraDosh; + structdefaultproperties { GameLength = GL_Short @@ -529,6 +535,8 @@ struct WeeklyOverrides TraderTimeModifier = 1.f; TimeBetweenWaves = -1.f; bForceShowSkipTrader = false; + ContaminationModeZedsToFinish = 0; + ContaminationModeExtraDosh = 0; } }; @@ -1101,8 +1109,23 @@ function class GetAISpawnOverrideInner(array { local SpawnReplacement Replacement; local float RandF; + local int IndexReplace; + + // Check if our current weekly event has any overrides available + + // We generate random number with the Elite Zed replacement we need + // If that replacement has a new EAIType we need to use it for the comparison + + IndexReplace = AIClassList[AIType].static.IndexOverrideReplaceSpawnWithElite(); + + if (IndexReplace >= 0) + { + if (AIClassList[AIType].default.EliteAIType.length > IndexReplace) + { + AIType = EAIType(AIClassList[AIType].default.EliteAIType[IndexReplace]); + } + } - //Check if our current weekly event has any overrides available foreach SpawnReplacementList(Replacement) { if (Replacement.SpawnEntry == AIType) diff --git a/KFGame/Classes/KFPawn.uc b/KFGame/Classes/KFPawn.uc index 55084f8..7518c18 100644 --- a/KFGame/Classes/KFPawn.uc +++ b/KFGame/Classes/KFPawn.uc @@ -2694,11 +2694,12 @@ simulated function TakeRadiusDamage vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) { bTakingRadiusDamage = true; - Super.TakeRadiusDamage(InstigatedBy, BaseDamage, DamageRadius, DamageType, Momentum, HurtOrigin, bFullDamage, DamageCauser, DamageFalloffExponent); + Super.TakeRadiusDamage(InstigatedBy, BaseDamage, DamageRadius, DamageType, Momentum, HurtOrigin, bFullDamage, DamageCauser, DamageFalloffExponent, bAdjustRadiusDamage); bTakingRadiusDamage = false; } @@ -5550,6 +5551,16 @@ simulated function StopLocustVFX() } } +simulated function bool CanInteractWithPawnGrapple() +{ + return true; +} + +simulated function bool CanInteractWithZoneVelocity() +{ + return true; +} + defaultproperties { InventoryManagerClass=class'KFInventoryManager' diff --git a/KFGame/Classes/KFPawn_Monster.uc b/KFGame/Classes/KFPawn_Monster.uc index 53f5b7c..7e11a0e 100644 --- a/KFGame/Classes/KFPawn_Monster.uc +++ b/KFGame/Classes/KFPawn_Monster.uc @@ -39,6 +39,7 @@ var private const KFCharacterInfo_Monster CharacterMonsterArch; var transient bool bArchLoaded; /** List of variants that this pawn can be spawned as */ +var const array EliteAIType; // can't use EAIType enumerator, use integer instead var const array > ElitePawnClass; /** Custom third person camera offsets */ @@ -718,15 +719,50 @@ simulated static function float GetXPValue(byte Difficulty) return default.XPValues[Difficulty]; } +// This is used to precalculate Elite Zed replacement at the Spawn Manager level decision, so Weeklies can infer a change via the SpawnReplacementList +static function int IndexOverrideReplaceSpawnWithElite() +{ + local WorldInfo WI; + local KFGameReplicationInfo KFGRI; + + WI = class'WorldInfo'.static.GetWorldInfo(); + KFGRI = KFGameReplicationInfo(WI.GRI); + + // We only use this feature in this weekly for now, we pregenerate the Random so we can replace the Zed with Elite version + // And use the Weekly SpawnReplacementList to replace correctly + + if (KFGRI.IsContaminationMode()) + { + if (default.ElitePawnClass.length > 0 + && default.DifficultySettings != none + && fRand() < default.DifficultySettings.static.GetSpecialSpawnChance(KFGRI)) + { + return Rand(default.ElitePawnClass.length); + } + } + + return -1; +} + /** Gets the actual classes used for spawning. Can be overridden to replace this monster with another */ static event class GetAIPawnClassToSpawn() { local WorldInfo WI; + local KFGameReplicationInfo KFGRI; WI = class'WorldInfo'.static.GetWorldInfo(); - if (default.ElitePawnClass.length > 0 && default.DifficultySettings != none && fRand() < default.DifficultySettings.static.GetSpecialSpawnChance(KFGameReplicationInfo(WI.GRI))) + KFGRI = KFGameReplicationInfo(WI.GRI); + + // We already generated the random for this mode when calling IndexOverrideReplaceSpawnWithElite, so no need to roll the dice again + + if (KFGRI.IsContaminationMode() == false) { - return default.ElitePawnClass[Rand(default.ElitePawnClass.length)]; + if (default.ElitePawnClass.length > 0 + && default.DifficultySettings != none + && fRand() < default.DifficultySettings.static.GetSpecialSpawnChance(KFGRI)) + { + return default.ElitePawnClass[Rand(default.ElitePawnClass.length)]; + } } return default.class; diff --git a/KFGame/Classes/KFPerk.uc b/KFGame/Classes/KFPerk.uc index 9590f87..98aa71f 100644 --- a/KFGame/Classes/KFPerk.uc +++ b/KFGame/Classes/KFPerk.uc @@ -1287,7 +1287,7 @@ simulated function bool IsCallOutActive(){ return false; } simulated function bool IsShootAndMoveActive(){ return false; } simulated function bool HasNightVision(){ return false; } simulated protected function bool IsRapidFireActive(){ return false; } -simulated function float GetZedTimeModifier( KFWeapon W ){ return 0.f; } +simulated event float GetZedTimeModifier( KFWeapon W ){ return 0.f; } simulated function float GetZedTimeModifierForWindUp(){ return 0.f; } simulated function ModifySpread( out float InSpread ); @@ -1349,8 +1349,6 @@ simulated function bool DoorShouldNuke(){ return false; } simulated function bool ShouldGetDaZeD( class DamageType ){ return false; } simulated function float GetDaZedEMPPower(){ return 0; } simulated function bool ShouldNeverDud(){ return false; } -simulated function SetLastHX25NukeTime( float NewTime ); -simulated function float GetLastHX25NukeTime() { return 0.f; } /** "Rack 'em Up" perk skill functions (Gunslinger, Sharpshooter) */ simulated function bool GetIsUberAmmoActive( KFWeapon KFW ){ return false; } diff --git a/KFGame/Classes/KFPerk_Demolitionist.uc b/KFGame/Classes/KFPerk_Demolitionist.uc index 7d31312..437b445 100644 --- a/KFGame/Classes/KFPerk_Demolitionist.uc +++ b/KFGame/Classes/KFPerk_Demolitionist.uc @@ -48,9 +48,6 @@ var private const float ProfessionalAoEModifier; var private bool bUsedSacrifice; var private const class LingeringNukeDamageType; -/** The last time an HX25 projectile spawned by our owner caused a nuke */ -var private transient float LastHX25NukeTime; - enum EDemoSkills { EDemoDamage, @@ -406,15 +403,6 @@ static function PrepareExplosive( Pawn ProjOwner, KFProjectile Proj, optional fl } } -simulated function SetLastHX25NukeTime( float NewTime ) -{ - LastHX25NukeTime = NewTime; -} -simulated function float GetLastHX25NukeTime() -{ - return LastHX25NukeTime; -} - simulated function float GetAoERadiusModifier() { local float RadiusModifier; @@ -722,7 +710,7 @@ simulated function float GetZedTimeModifier( KFWeapon W ) local name StateName; StateName = W.GetStateName(); - if( IsProfessionalActive() && IsWeaponOnPerk( W,, self.class ) ) + if( IsProfessionalActive() && IsWeaponOnPerk( W,, self.class )) { if( ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || W.HasAlwaysOnZedTimeResist() ) { @@ -1171,9 +1159,13 @@ DefaultProperties ZedTimeModifyingStates(9)="BlunderbussDeployAndDetonate" PassiveExtraAmmoIgnoredClassNames(0)="KFProj_DynamiteGrenade" + PassiveExtraAmmoIgnoredClassNames(1)="KFWeap_HRG_Warthog" + PassiveExtraAmmoIgnoredClassNames(2)="KFWeap_HRG_WarthogWeapon" ExtraAmmoIgnoredClassNames(0)="KFProj_DynamiteGrenade" ExtraAmmoIgnoredClassNames(1)="KFWeap_Thrown_C4" + ExtraAmmoIgnoredClassNames(2)="KFWeap_HRG_Warthog" + ExtraAmmoIgnoredClassNames(3)="KFWeap_HRG_WarthogWeapon" TacticalReloadAsReloadRateClassNames(0)="KFWeap_GrenadeLauncher_M32" diff --git a/KFGame/Classes/KFPerk_FieldMedic.uc b/KFGame/Classes/KFPerk_FieldMedic.uc index 85d2ef0..988648f 100644 --- a/KFGame/Classes/KFPerk_FieldMedic.uc +++ b/KFGame/Classes/KFPerk_FieldMedic.uc @@ -650,8 +650,8 @@ DefaultProperties ProgressStatID=STATID_Medic_Progress PerkBuildStatID=STATID_Medic_Build - SelfHealingSurgePct=0.06f //0.1f - MaxSurvivalistResistance=0.60f //0.70f //0.5f //0.8 + SelfHealingSurgePct=0.03f //0.1f + MaxSurvivalistResistance=0.30f //0.70f //0.5f //0.8 CombatantSpeedModifier=0.1f MaxHealingSpeedBoost=30 //15 //50 @@ -730,7 +730,7 @@ DefaultProperties HealerRecharge=(Name="Healer Recharge",Increment=0.08f,Rank=0,StartingValue=1.f,MaxValue=3.f) HealPotency=(Name="Healer Potency",Increment=0.02f,Rank=0,StartingValue=1.0f,MaxValue=1.5f) BloatBileResistance=(Name="Bloat Bile Resistance",Increment=0.02,Rank=0,StartingValue=0.f,MaxValue=0.5f) - MovementSpeed=(Name="Movement Speed",Increment=0.004f,Rank=0,StartingValue=0.f,MaxValue=0.1f) + MovementSpeed=(Name="Movement Speed",Increment=0.002f,Rank=0,StartingValue=0.f,MaxValue=0.1f) Armor=(Name="Armor",Increment=0.02f,Rank=0,StartingValue=1.f,MaxValue=1.5f) //0.03f, 0f, 1f, 1.75f PerkSkills(EMedicHealingSurge)=(Name="HealingSurge",IconPath="UI_PerkTalent_TEX.Medic.UI_Talents_Medic_HealingSurge", Increment=0.f,Rank=0,StartingValue=0.25,MaxValue=0.25) diff --git a/KFGame/Classes/KFPlayerController.uc b/KFGame/Classes/KFPlayerController.uc index cea47a6..0dda3d2 100644 --- a/KFGame/Classes/KFPlayerController.uc +++ b/KFGame/Classes/KFPlayerController.uc @@ -757,6 +757,7 @@ var transient bool bShotgunJumping; var bool bAllowSeasonalSkins; +var bool bFriendlyUIEnabled; cpptext { @@ -2855,6 +2856,12 @@ public function bool CanUseVIP() return false; } +public function bool CanUseContaminationMode() +{ + return KFGameReplicationInfo(WorldInfo.GRI) != none + && KFGameReplicationInfo(WorldInfo.GRI).IsContaminationMode(); +} + /********************************************************************************************* * @name Skill Tracking ********************************************************************************************* */ @@ -12169,6 +12176,12 @@ simulated function ClearShotgunJumpFlag() bShotgunJumping = false; } +exec function ToggleFriendlyUI() +{ + bFriendlyUIEnabled = !bFriendlyUIEnabled; + `Log("Toggle Friendly UI " $bFriendlyUIEnabled); +} + defaultproperties { EarnedDosh=0 @@ -12377,4 +12390,6 @@ defaultproperties StormCannonIDCounter = 0 bShotgunJumping = false bAllowSeasonalSkins = false + + bFriendlyUIEnabled = true } diff --git a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc index 5c30ed2..2269298 100644 --- a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc +++ b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc @@ -111,6 +111,12 @@ var transient VIPGameInfo VIPGameData; // RandomPerk weekly var byte InitialRandomPerk; +// Contamination Mode weekly +var float ContaminationModeGraceCurrentTimer; +var bool ContaminationModePlayerIsInside; +var int ContaminationModeLastTimePlayerOutPulsate; +var bool ContaminationModeLastPulsate; + cpptext { virtual UBOOL TestZedTimeVisibility(APawn* P, UNetConnection* Connection, UBOOL bLocalPlayerTest) override; @@ -208,6 +214,64 @@ simulated function UpdateVIPWidget(ReplicatedVIPGameInfo VIPInfo) } } +reliable client function UpdateContaminationModeWidget(bool IsPlayerIn) +{ + // Reset pulsate + if (IsPlayerIn != ContaminationModePlayerIsInside) + { + ContaminationModeLastPulsate = true; + ContaminationModeLastTimePlayerOutPulsate = WorldInfo.TimeSeconds; + } + + // Pulsate ping pongs for showing text when player is out or not + if (IsPlayerIn == false && WorldInfo.TimeSeconds - ContaminationModeLastTimePlayerOutPulsate > 1.f) + { + ContaminationModeLastTimePlayerOutPulsate = WorldInfo.TimeSeconds; + ContaminationModeLastPulsate = !ContaminationModeLastPulsate; + } + + ContaminationModePlayerIsInside = IsPlayerIn; + + if (MyGFxHUD != none) + { + MyGFxHUD.UpdateContaminationMode(IsPlayerIn, ContaminationModeLastPulsate); + + if (MyGFxHUD.PlayerStatusContainer != none) + { + MyGFxHUD.PlayerStatusContainer.UpdateContaminationModeIconVisible(IsPlayerIn == false); + } + } +} + +reliable client function UpdateContaminationModeWidget_Timer(int Timer) +{ + if (MyGFxHUD != none) + { + MyGFxHUD.UpdateContaminationMode_Timer(Timer); + } +} + +reliable client Function ShowContaminationMode() +{ + if (MyGFxHUD != none) + { + MyGFxHUD.ShowContaminationMode(); + } +} + +reliable client function HideContaminationMode() +{ + if (MyGFxHUD != none) + { + MyGFxHUD.HideContaminationMode(); + + if (MyGFxHUD.PlayerStatusContainer != none) + { + MyGFxHUD.PlayerStatusContainer.UpdateContaminationModeIconVisible(false); + } + } +} + reliable client function ResetSyringe() { local KFInventoryManager InventoryManager; @@ -709,4 +773,8 @@ defaultProperties VIPLowHealthSoundEvent=AkEvent'WW_GLO_Runtime.WeeklyVIPAlarm' VIPLowHealthLastTimePlayed = 0.f InitialRandomPerk=255 + ContaminationModeGraceCurrentTimer=0.f + ContaminationModePlayerIsInside=false + ContaminationModeLastTimePlayerOutPulsate=0 + ContaminationModeLastPulsate=false } \ No newline at end of file diff --git a/KFGame/Classes/KFPlayerInput.uc b/KFGame/Classes/KFPlayerInput.uc index 2b0b781..24990ac 100644 --- a/KFGame/Classes/KFPlayerInput.uc +++ b/KFGame/Classes/KFPlayerInput.uc @@ -2968,6 +2968,19 @@ exec function LogPerkInfo() } `endif +exec function ToggleFriendlyUI() +{ + Outer.ToggleFriendlyUI(); +} + +exec function ToggleFriendlyUIFromHUD() +{ + if( MyGFxHUD != none && MyGFxHUD.VoiceCommsWidget != none && MyGFxHUD.VoiceCommsWidget.bActive ) + { + Outer.ToggleFriendlyUI(); + } +} + defaultproperties { bEnableFOVScaling=true diff --git a/KFGame/Classes/KFProj_ExplosiveSubmunition_HX25.uc b/KFGame/Classes/KFProj_ExplosiveSubmunition_HX25.uc index 533886b..e4dc45c 100644 --- a/KFGame/Classes/KFProj_ExplosiveSubmunition_HX25.uc +++ b/KFGame/Classes/KFProj_ExplosiveSubmunition_HX25.uc @@ -14,6 +14,8 @@ class KFProj_ExplosiveSubmunition_HX25 extends KFProj_BallisticExplosive /** Cached reference to owner weapon */ var protected KFWeapon OwnerWeapon; +var bool bCanNuke; + /** Initialize the projectile */ function Init( vector Direction ) { @@ -37,25 +39,9 @@ function bool ShouldWarnAIWhenFired() */ simulated protected function PrepareExplosionTemplate() { - local KFPawn KFP; - local KFPerk CurrentPerk; - ExplosionTemplate.bIgnoreInstigator = true; super.PrepareExplosionTemplate(); - - if( ExplosionActorClass == class'KFPerk_Demolitionist'.static.GetNukeExplosionActorClass() ) - { - KFP = KFPawn( Instigator ); - if( KFP != none ) - { - CurrentPerk = KFP.GetPerk(); - if( CurrentPerk != none ) - { - CurrentPerk.SetLastHX25NukeTime( WorldInfo.TimeSeconds ); - } - } - } } simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallComp) @@ -73,23 +59,9 @@ simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallCom Super.HitWall(HitNormal, Wall, WallComp); } -/** Only allow this projectile to cause a nuke if there hasn't been another nuke very recently */ simulated function bool AllowNuke() { - local KFPawn KFP; - local KFPerk CurrentPerk; - - KFP = KFPawn( Instigator ); - if( KFP != none ) - { - CurrentPerk = KFP.GetPerk(); - if( CurrentPerk != none && `TimeSince(CurrentPerk.GetLastHX25NukeTime()) < 0.25f ) - { - return false; - } - } - - return super.AllowNuke(); + return bCanNuke; } defaultproperties @@ -162,5 +134,7 @@ defaultproperties //AmbientSoundStopEvent=AkEvent'WW_WEP_Bullet_FlyBys.Stop_WEP_Bullet_Flyby_Small' AlwaysRelevantDistanceSquared=2250000 // 15m + + bCanNuke = true } diff --git a/KFGame/Classes/KFSM_GrappleCombined.uc b/KFGame/Classes/KFSM_GrappleCombined.uc index 00194da..a31bcd8 100644 --- a/KFGame/Classes/KFSM_GrappleCombined.uc +++ b/KFGame/Classes/KFSM_GrappleCombined.uc @@ -214,6 +214,12 @@ function Pawn FindPlayerGrabTarget() function bool CanInteractWithPawn(KFPawn OtherPawn) { // Prevent interaction if potentiail victim is dead, not on our team, in Phys_Falling, or busy with another special move + + if (OtherPawn.CanInteractWithPawnGrapple() == false) + { + return false; + } + return( (OtherPawn.IsAliveAndWell() && !KFPOwner.IsSameTeam(OtherPawn) && OtherPawn.Physics != PHYS_Falling && !OtherPawn.IsDoingSpecialMove()) && Super.CanInteractWithPawn(OtherPawn) ); } diff --git a/KFGame/Classes/KFUnlockManager.uc b/KFGame/Classes/KFUnlockManager.uc index 934f6d8..a19f672 100644 --- a/KFGame/Classes/KFUnlockManager.uc +++ b/KFGame/Classes/KFUnlockManager.uc @@ -38,7 +38,8 @@ enum ESharedContentUnlock SCU_Scythe, SCU_G36C, SCU_HVStormCannon, - SCU_ZedMKIII + SCU_ZedMKIII, + SCU_Saiga12 }; @@ -393,4 +394,8 @@ defaultproperties Name=KFWeap_ZedMKIII, IconPath="wep_ui_zedmkiii_tex.UI_WeaponSelect_ZEDMKIII", ID=9575)} + SharedContentList(SCU_Saiga12)={( + Name=KFWeap_Shotgun_S12, + IconPath="WEP_UI_Saiga12_TEX.UI_WeaponSelect_Saiga12", + ID=9666)} } diff --git a/KFGame/Classes/KFWeapDef_HRG_Locust.uc b/KFGame/Classes/KFWeapDef_HRG_Locust.uc index 7d3b625..ecec7a1 100644 --- a/KFGame/Classes/KFWeapDef_HRG_Locust.uc +++ b/KFGame/Classes/KFWeapDef_HRG_Locust.uc @@ -14,7 +14,7 @@ DefaultProperties { WeaponClassPath="KFGameContent.KFWeap_HRG_Locust" - BuyPrice=900 + BuyPrice=1400 AmmoPricePerMag=40 ImagePath="wep_ui_hrg_locust_tex.UI_WeaponSelect_HRG_Locust" @@ -22,9 +22,7 @@ DefaultProperties EffectiveRange=100 - UpgradePrice[0]=700 - UpgradePrice[1]=1500 + UpgradePrice[0]=1500 - UpgradeSellPrice[0]=525 - UpgradeSellPrice[1]=1650 + UpgradeSellPrice[0]=1125 } \ No newline at end of file diff --git a/KFGame/Classes/KFWeapDef_HRG_Warthog.uc b/KFGame/Classes/KFWeapDef_HRG_Warthog.uc new file mode 100644 index 0000000..de35220 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_Warthog.uc @@ -0,0 +1,28 @@ +//============================================================================= +// KFWeapDef_HRGWarthog +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_Warthog extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_Warthog" + + BuyPrice=500 + AmmoPricePerMag=60 // 27 + ImagePath="WEP_UI_HRG_Warthog_TEX.UI_WeaponSelect_HRG_Warthog" + + IsPlayGoHidden=true; + + EffectiveRange=18 + + UpgradePrice[0]=700 + UpgradePrice[1]=1500 + + UpgradeSellPrice[0]=525 + UpgradeSellPrice[1]=1650 +} diff --git a/KFGame/Classes/KFWeapDef_HRG_WarthogWeapon.uc b/KFGame/Classes/KFWeapDef_HRG_WarthogWeapon.uc new file mode 100644 index 0000000..d4c9ff0 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_WarthogWeapon.uc @@ -0,0 +1,19 @@ +//============================================================================= +// KFWeapDef_HRG_WarthogWeapon +//============================================================================= +// Weapon attached to a drone +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_WarthogWeapon extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_WarthogWeapon" + + BuyPrice=0 + AmmoPricePerMag=0 + ImagePath="ui_weaponselect_tex.UI_WeaponSelect_AR15" +} diff --git a/KFGame/Classes/KFWeapDef_Minigun.uc b/KFGame/Classes/KFWeapDef_Minigun.uc index f41f676..c9b2b1e 100644 --- a/KFGame/Classes/KFWeapDef_Minigun.uc +++ b/KFGame/Classes/KFWeapDef_Minigun.uc @@ -14,7 +14,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_Minigun" BuyPrice=2000 //2500 - AmmoPricePerMag= 90//125 //175 //250 + AmmoPricePerMag= 120//125 //175 //250 ImagePath="WEP_UI_Minigun_TEX.UI_WeaponSelect_Minigun" IsPlayGoHidden=true; diff --git a/KFGame/Classes/KFWeapDef_Shotgun_S12.uc b/KFGame/Classes/KFWeapDef_Shotgun_S12.uc new file mode 100644 index 0000000..897ebf7 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_Shotgun_S12.uc @@ -0,0 +1,35 @@ +//============================================================================= +// 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) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_Shotgun_S12 extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_Shotgun_S12" + + BuyPrice=1500 + AmmoPricePerMag=40 //110 //82 + ImagePath="WEP_UI_Saiga12_TEX.UI_WeaponSelect_Saiga12" + + IsPlayGoHidden=true; + + SecondaryAmmoMagSize=1 + SecondaryAmmoMagPrice=30 //13 + + + EffectiveRange=30 + + UpgradePrice[0]=1500 + + UpgradeSellPrice[0]=1125 + + SharedUnlockId=SCU_Saiga12 + +} diff --git a/KFGame/Classes/KFWeapDef_ShrinkRayGun.uc b/KFGame/Classes/KFWeapDef_ShrinkRayGun.uc index 1a14067..f811f89 100644 --- a/KFGame/Classes/KFWeapDef_ShrinkRayGun.uc +++ b/KFGame/Classes/KFWeapDef_ShrinkRayGun.uc @@ -12,7 +12,7 @@ DefaultProperties { WeaponClassPath="KFGameContent.KFWeap_ShrinkRayGun" - BuyPrice=1200 + BuyPrice=900 AmmoPricePerMag=50 ImagePath="WEP_UI_ShrinkRay_Gun_TEX.UI_Weapon_Select_Shrink_Ray_Gun" diff --git a/KFGame/Classes/KFWeap_HealerBase.uc b/KFGame/Classes/KFWeap_HealerBase.uc index ee84f1c..0297720 100644 --- a/KFGame/Classes/KFWeap_HealerBase.uc +++ b/KFGame/Classes/KFWeap_HealerBase.uc @@ -117,8 +117,6 @@ function GivenTo(Pawn NewOwner, optional bool bDoNotActivate) KFInvManger = KFInventoryManager(InvManager); if( InvManager != none && KFInvManger != none ) { - `Log("GivenToGivenToGivenToGivenToGivenToGivenToGivenToGivenToGivenToGivenToGivenToGivenTo"); - KFInvManger.HealerWeapon = self; } } diff --git a/KFGame/Classes/KFWeap_ThrownBase.uc b/KFGame/Classes/KFWeap_ThrownBase.uc index 5c8d6a6..d92a840 100644 --- a/KFGame/Classes/KFWeap_ThrownBase.uc +++ b/KFGame/Classes/KFWeap_ThrownBase.uc @@ -354,6 +354,15 @@ static simulated function float CalculateTraderWeaponStatDamage() return BaseDamage + DoTDamage; } +simulated state WeaponPuttingDown +{ + simulated function BeginState( Name PreviousStateName ) + { + super.BeginState(PreviousStateName); + ClearTimer(nameOf(PerformArtificialReload) ); + } +} + defaultproperties { // Anim diff --git a/KFGame/Classes/KFWeapon.uc b/KFGame/Classes/KFWeapon.uc index a702e51..a25c6d8 100644 --- a/KFGame/Classes/KFWeapon.uc +++ b/KFGame/Classes/KFWeapon.uc @@ -985,6 +985,9 @@ var class TargetingCompClass; var KFTargetingWeaponComponent TargetingComp; var repnotify Actor TargetingCompRepActor; +// To force the crosshair for showing up even if there's no spread +var bool bForceCrosshair; + cpptext { // Actor @@ -1846,6 +1849,11 @@ simulated function bool DenyPerkResupply() return false; } + if (Class.Name == 'KFWeap_HRG_Warthog') + { + return false; + } + return true; } @@ -3931,6 +3939,11 @@ static simulated function class GetKFProjectileClassByFiringMode(i MyProjectileClass = GetKFProjectileClass(); + if (MyProjectileClass == none) + { + return none; + } + if( Role == ROLE_Authority || (MyProjectileClass.default.bUseClientSideHitDetection && MyProjectileClass.default.bNoReplicationToInstigator && Instigator != none && Instigator.IsLocallyControlled()) ) @@ -6087,7 +6100,8 @@ simulated state WeaponEquipping CurrentPerk = GetPerk(); if( CurrentPerk != none ) { - if( CurrentPerk.IsWeaponOnPerk( self,, CurrentPerk.class ) ) + // Ignore weapon on perk for survivalist + if( CurrentPerk.IsWeaponOnPerk( self,, CurrentPerk.class ) || CurrentPerk.GetPerkClass() == class'KFPerk_Survivalist'.static.GetPerkClass() ) { CurrentPerk.ModifyWeaponSwitchTime( ScaledRate ); } @@ -6116,7 +6130,8 @@ simulated function TimeWeaponEquipping() InstigatorPerk = GetPerk(); if( InstigatorPerk != none ) { - if( InstigatorPerk.IsWeaponOnPerk( self,, InstigatorPerk.class ) ) + // Ignore weapon on perk for survivalist + if( InstigatorPerk.IsWeaponOnPerk( self,, InstigatorPerk.class ) || InstigatorPerk.GetPerkClass() == class'KFPerk_Survivalist'.static.GetPerkClass() ) { InstigatorPerk.ModifyWeaponSwitchTime( ModifiedEquipTime ); } @@ -6196,7 +6211,8 @@ simulated state WeaponPuttingDown CurrentPerk = GetPerk(); if( CurrentPerk != none ) { - if( CurrentPerk.IsWeaponOnPerk( self,, CurrentPerk.class ) ) + // Ignore weapon on perk for survivalist + if( CurrentPerk.IsWeaponOnPerk( self,, CurrentPerk.class ) || CurrentPerk.GetPerkClass() == class'KFPerk_Survivalist'.static.GetPerkClass() ) { CurrentPerk.ModifyWeaponSwitchTime( ScaledRate ); } @@ -6375,7 +6391,8 @@ simulated function TimeWeaponPutDown() InstigatorPerk = GetPerk(); if( InstigatorPerk != none ) { - if( InstigatorPerk.IsWeaponOnPerk( self,, InstigatorPerk.class ) ) + // Ignore weapon on perk for survivalist + if( InstigatorPerk.IsWeaponOnPerk( self,, InstigatorPerk.class ) || InstigatorPerk.GetPerkClass() == class'KFPerk_Survivalist'.static.GetPerkClass() ) { InstigatorPerk.ModifyWeaponSwitchTime( ModifiedPutDownTime ); } @@ -8070,5 +8087,7 @@ defaultproperties bForceHandleImpacts=false UseFixedPhysicalFireLocation=false + + bForceCrosshair=false } diff --git a/KFGame/Classes/KFWeaponSkinList.uc b/KFGame/Classes/KFWeaponSkinList.uc index 8e027fa..ea410ae 100644 --- a/KFGame/Classes/KFWeaponSkinList.uc +++ b/KFGame/Classes/KFWeaponSkinList.uc @@ -4532,4 +4532,208 @@ defaultproperties //Retro Precious Railgun Skins.Add((Id=9539, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Railgun', MIC_1P=("WEP_SkinSet69_MAT.retro_railgun.RetroPrecious_Railgun_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_railgun.RetroPrecious_Railgun_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_railgun.RetroPrecious_Railgun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_railgun.RetroPrecious_Railgun_3P_Pickup_MIC")) + +//Jaeger Dynamic M14EBR + Skins.Add((Id=9582, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet72_MAT.dynamic_m14ebr.Dynamic_M14EBR_1P_Mint_MIC", "WEP_SkinSet72_MAT.dynamic_m14ebr.Dynamic_M14EBR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet72_MAT.dynamic_m14ebr.Dynamic_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet72_MAT.dynamic_m14ebr.Dynamic_M14EBR_3P_Pickup_MIC")) + +//Jaeger Dynamic Seeker Six + Skins.Add((Id=9583, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Seeker6', MIC_1P=("WEP_SkinSet72_MAT.dynamic_seekersix.Dynamic_SeekerSix_1P_Mint_MIC", "WEP_SkinSet72_MAT.dynamic_seekersix.Dynamic_SeekerSix_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet72_MAT.dynamic_seekersix.Dynamic_SeekerSix_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet72_MAT.dynamic_seekersix.Dynamic_SeekerSix_3P_Pickup_MIC")) + +//Jaeger Dynamic Railgun + Skins.Add((Id=9584, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Railgun', MIC_1P=("WEP_SkinSet72_MAT.dynamic_railgun.Dynamic_Railgun_1P_Mint_MIC", "WEP_SkinSet72_MAT.dynamic_railgun.Dynamic_Railgun_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet72_MAT.dynamic_railgun.Dynamic_Railgun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet72_MAT.dynamic_railgun.Dynamic_Railgun_3P_Pickup_MIC")) + +//Jaeger Dynamic MP5RAS + Skins.Add((Id=9585, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet72_MAT.dynamic_mp5ras.Dynamic_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet72_MAT.dynamic_mp5ras.Dynamic_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet72_MAT.dynamic_mp5ras.Dynamic_MP5RAS_3P_Pickup_MIC")) + +//Jaeger Dynamic RGB M14EBR + Skins.Add((Id=9586, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet72_MAT.dynamicrgb_m14ebr.DynamicRGB_M14EBR_1P_Mint_MIC", "WEP_SkinSet72_MAT.dynamicrgb_m14ebr.DynamicRGB_M14EBR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet72_MAT.dynamicrgb_m14ebr.DynamicRGB_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet72_MAT.dynamicrgb_m14ebr.DynamicRGB_M14EBR_3P_Pickup_MIC")) + +//Jaeger Dynamic RGB Seeker Six + Skins.Add((Id=9587, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Seeker6', MIC_1P=("WEP_SkinSet72_MAT.dynamicrgb_seekersix.DynamicRGB_SeekerSix_1P_Mint_MIC", "WEP_SkinSet72_MAT.dynamicrgb_seekersix.DynamicRGB_SeekerSix_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet72_MAT.dynamicrgb_seekersix.DynamicRGB_SeekerSix_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet72_MAT.dynamicrgb_seekersix.DynamicRGB_SeekerSix_3P_Pickup_MIC")) + +//Jaeger Dynamic RGB Railgun + Skins.Add((Id=9588, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Railgun', MIC_1P=("WEP_SkinSet72_MAT.dynamicrgb_railgun.DynamicRGB_Railgun_1P_Mint_MIC", "WEP_SkinSet72_MAT.dynamicrgb_railgun.DynamicRGB_Railgun_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet72_MAT.dynamicrgb_railgun.DynamicRGB_Railgun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet72_MAT.dynamicrgb_railgun.DynamicRGB_Railgun_3P_Pickup_MIC")) + +//Jaeger Dynamic RGB MP5RAS + Skins.Add((Id=9589, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet72_MAT.dynamicrgb_mp5ras.DynamicRGB_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet72_MAT.dynamicrgb_mp5ras.DynamicRGB_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet72_MAT.dynamicrgb_mp5ras.DynamicRGB_MP5RAS_3P_Pickup_MIC")) + +//Junkyard Mint RPG-7 + Skins.Add((Id=9590, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet73_MAT.junkyard_rpg7.Junkyard_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet73_MAT.junkyard_rpg7.Junkyard_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet73_MAT.junkyard_rpg7.Junkyard_RPG7_3P_Pickup_MIC")) + +//Junkyard Mint M99 + Skins.Add((Id=9591, Weapondef=class'KFWeapDef_M99', MIC_1P=("WEP_SkinSet73_MAT.junkyard_m99.Junkyard_M99_1P_Mint_MIC", "WEP_SkinSet73_MAT.junkyard_m99.Junkyard_M99_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet73_MAT.junkyard_m99.Junkyard_M99_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet73_MAT.junkyard_m99.Junkyard_M99_3P_Pickup_MIC")) + +//Junkyard Mint Heckler & Koch UMP + Skins.Add((Id=9592, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet73_MAT.junkyard_hk_ump.Junkyard_HK_UMP_1P_Mint_MIC", "WEP_SkinSet73_MAT.junkyard_hk_ump.Junkyard_HK_UMP_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet73_MAT.junkyard_hk_ump.Junkyard_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet73_MAT.junkyard_hk_ump.Junkyard_HK_UMP_3P_Pickup_MIC")) + +//Junkyard Mint FN FAL + Skins.Add((Id=9593, Weapondef=class'KFWeapDef_FNFAL', MIC_1P=("WEP_SkinSet73_MAT.junkyard_fnfal.Junkyard_FNFAL_1P_Mint_MIC", "WEP_SkinSet73_MAT.junkyard_fnfal.Junkyard_FNFAL_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet73_MAT.junkyard_fnfal.Junkyard_FNFAL_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet73_MAT.junkyard_fnfal.Junkyard_FNFAL_3P_Pickup_MIC")) + +//Junkyard Precious RPG-7 + Skins.Add((Id=9594, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet73_MAT.junkyardprecious_rpg7.JunkyardPrecious_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet73_MAT.junkyardprecious_rpg7.JunkyardPrecious_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet73_MAT.junkyardprecious_rpg7.JunkyardPrecious_RPG7_3P_Pickup_MIC")) + +//Junkyard Precious M99 + Skins.Add((Id=9595, Weapondef=class'KFWeapDef_M99', MIC_1P=("WEP_SkinSet73_MAT.junkyardprecious_m99.JunkyardPrecious_M99_1P_Mint_MIC", "WEP_SkinSet73_MAT.junkyardprecious_m99.JunkyardPrecious_M99_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet73_MAT.junkyardprecious_m99.JunkyardPrecious_M99_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet73_MAT.junkyardprecious_m99.JunkyardPrecious_M99_3P_Pickup_MIC")) + +//Junkyard Precious Heckler & Koch UMP + Skins.Add((Id=9596, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet73_MAT.junkyardprecious_hk_ump.JunkyardPrecious_HK_UMP_1P_Mint_MIC", "WEP_SkinSet73_MAT.junkyardprecious_hk_ump.JunkyardPrecious_HK_UMP_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet73_MAT.junkyardprecious_hk_ump.JunkyardPrecious_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet73_MAT.junkyardprecious_hk_ump.JunkyardPrecious_HK_UMP_3P_Pickup_MIC")) + +//Junkyard Precious FN FAL + Skins.Add((Id=9597, Weapondef=class'KFWeapDef_FNFAL', MIC_1P=("WEP_SkinSet73_MAT.junkyardprecious_fnfal.JunkyardPrecious_FNFAL_1P_Mint_MIC", "WEP_SkinSet73_MAT.junkyardprecious_fnfal.JunkyardPrecious_FNFAL_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet73_MAT.junkyardprecious_fnfal.JunkyardPrecious_FNFAL_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet73_MAT.junkyardprecious_fnfal.JunkyardPrecious_FNFAL_3P_Pickup_MIC")) + +//Predator Forest M16 M203 + Skins.Add((Id=9637, Weapondef=class'KFWeapDef_M16M203', MIC_1P=("WEP_SkinSet74_MAT.camo_m16m203.Camo_M16_1P_Mint_MIC", "WEP_SkinSet74_MAT.camo_m16m203.Camo_M203_1P_Mint_MIC"), MIC_3P="WEP_SkinSet74_MAT.camo_m16m203.Camo_M16M203_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet74_MAT.camo_m16m203.Camo_M16M203_3P_Pickup_MIC")) + +//Predator Forest M79 + Skins.Add((Id=9638, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet74_MAT.camo_m79.Camo_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet74_MAT.camo_m79.Camo_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet74_MAT.camo_m79.Camo_M79_3P_Pickup_MIC")) + +//Predator Forest Kriss SMG + Skins.Add((Id=9639, Weapondef=class'KFWeapDef_Kriss', MIC_1P=("WEP_SkinSet74_MAT.camo_kriss.Camo_Kriss_1P_Mint_MIC", "WEP_SkinSet74_MAT.camo_kriss.Camo_Kriss_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet74_MAT.camo_kriss.Camo_Kriss_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet74_MAT.camo_kriss.Camo_Kriss_3P_Pickup_MIC")) + +//Predator Forest 9MM + Skins.Add((Id=9640, Weapondef=class'KFWeapDef_9mm', MIC_1P=("WEP_SkinSet74_MAT.camo_9mm.Camo_9MM_1P_Mint_MIC"), MIC_3P="WEP_SkinSet74_MAT.camo_9mm.Camo_9MM_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet74_MAT.camo_9mm.Camo_9MM_3P_Pickup_MIC")) + +//Predator Desert M16 M203 + Skins.Add((Id=9641, Weapondef=class'KFWeapDef_M16M203', MIC_1P=("WEP_SkinSet74_MAT.camodesert_m16m203.CamoDesert_M16_1P_Mint_MIC", "WEP_SkinSet74_MAT.camodesert_m16m203.CamoDesert_M203_1P_Mint_MIC"), MIC_3P="WEP_SkinSet74_MAT.camodesert_m16m203.CamoDesert_M16M203_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet74_MAT.camo_m16m203.CamoDesert_M16M203_3P_Pickup_MIC")) + +//Predator Desert M79 + Skins.Add((Id=9642, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet74_MAT.camodesert_m79.CamoDesert_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet74_MAT.camodesert_m79.CamoDesert_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet74_MAT.camo_m79.CamoDesert_M79_3P_Pickup_MIC")) + +//Predator Desert Kriss SMG + Skins.Add((Id=9643, Weapondef=class'KFWeapDef_Kriss', MIC_1P=("WEP_SkinSet74_MAT.camodesert_kriss.CamoDesert_Kriss_1P_Mint_MIC", "WEP_SkinSet74_MAT.camodesert_kriss.CamoDesert_Kriss_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet74_MAT.camodesert_kriss.CamoDesert_Kriss_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet74_MAT.camo_kriss.CamoDesert_Kriss_3P_Pickup_MIC")) + +//Predator Desert 9MM + Skins.Add((Id=9644, Weapondef=class'KFWeapDef_9mm', MIC_1P=("WEP_SkinSet74_MAT.camodesert_9mm.CamoDesert_9MM_1P_Mint_MIC"), MIC_3P="WEP_SkinSet74_MAT.camodesert_9mm.CamoDesert_9MM_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet74_MAT.camo_9mm.CamoDesert_9MM_3P_Pickup_MIC")) + +//Stingray Infantry Heckler & Koch UMP + Skins.Add((Id=9601, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet75_MAT.diver_hk_ump.DiverVar1_HK_UMP_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_hk_ump.DiverVar1_HK_UMP_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar1_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar1_HK_UMP_3P_Pickup_MIC")) + +//Stingray Navy Heckler & Koch UMP + Skins.Add((Id=9602, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet75_MAT.diver_hk_ump.DiverVar2_HK_UMP_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_hk_ump.DiverVar2_HK_UMP_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar2_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar2_HK_UMP_3P_Pickup_MIC")) + +//Stingray Engineer Heckler & Koch UMP + Skins.Add((Id=9603, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet75_MAT.diver_hk_ump.DiverVar3_HK_UMP_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_hk_ump.DiverVar3_HK_UMP_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar3_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar3_HK_UMP_3P_Pickup_MIC")) + +//Stingray Artillery Heckler & Koch UMP + Skins.Add((Id=9604, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet75_MAT.diver_hk_ump.DiverVar4_HK_UMP_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_hk_ump.DiverVar4_HK_UMP_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar4_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar4_HK_UMP_3P_Pickup_MIC")) + +//Stingray Engineer Heckler & Koch UMP + Skins.Add((Id=9605, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet75_MAT.diver_hk_ump.DiverVar3_HK_UMP_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_hk_ump.DiverVar3_HK_UMP_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar3_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_hk_ump.DiverVar3_HK_UMP_3P_Pickup_MIC")) + +//Stingray Precious Heckler & Koch UMP + Skins.Add((Id=9606, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet75_MAT.diver_hk_ump.DiverPrecious_HK_UMP_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_hk_ump.DiverPrecious_HK_UMP_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_hk_ump.DiverPrecious_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_hk_ump.DiverPrecious_HK_UMP_3P_Pickup_MIC")) + +//Stingray Infantry Hemoclobber + Skins.Add((Id=9607, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet75_MAT.diver_medicbat.DiverVar1_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_medicbat.DiverVar1_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_medicbat.DiverVar1_MedicBat_3P_Pickup_MIC")) + +//Stingray Navy Hemoclobber + Skins.Add((Id=9608, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet75_MAT.diver_medicbat.DiverVar2_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_medicbat.DiverVar2_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_medicbat.DiverVar2_MedicBat_3P_Pickup_MIC")) + +//Stingray Engineer Hemoclobber + Skins.Add((Id=9609, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet75_MAT.diver_medicbat.DiverVar3_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_medicbat.DiverVar3_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_medicbat.DiverVar3_MedicBat_3P_Pickup_MIC")) + +//Stingray Artillery Hemoclobber + Skins.Add((Id=9610, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet75_MAT.diver_medicbat.DiverVar4_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_medicbat.DiverVar4_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_medicbat.DiverVar4_MedicBat_3P_Pickup_MIC")) + +//Stingray Logistic Hemoclobber + Skins.Add((Id=9611, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet75_MAT.diver_medicbat.DiverVar5_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_medicbat.DiverVar5_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_medicbat.DiverVar5_MedicBat_3P_Pickup_MIC")) + +//Stingray Precious Hemoclobber + Skins.Add((Id=9612, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet75_MAT.diver_medicbat.DiverPrecious_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_medicbat.DiverPrecious_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_medicbat.DiverPrecious_MedicBat_3P_Pickup_MIC")) + +//Stingray Infantry SCAR + Skins.Add((Id=9613, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet75_MAT.diver_scar.DiverVar1_SCAR_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_scar.DiverVar1_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_scar.DiverVar1_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_scar.DiverVar1_SCAR_3P_Pickup_MIC")) + +//Stingray Navy SCAR + Skins.Add((Id=9614, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet75_MAT.diver_scar.DiverVar2_SCAR_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_scar.DiverVar2_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_scar.DiverVar2_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_scar.DiverVar2_SCAR_3P_Pickup_MIC")) + +//Stingray Engineer SCAR + Skins.Add((Id=9615, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet75_MAT.diver_scar.DiverVar3_SCAR_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_scar.DiverVar3_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_scar.DiverVar3_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_scar.DiverVar3_SCAR_3P_Pickup_MIC")) + +//Stingray Artillery SCAR + Skins.Add((Id=9616, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet75_MAT.diver_scar.DiverVar4_SCAR_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_scar.DiverVar4_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_scar.DiverVar4_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_scar.DiverVar4_SCAR_3P_Pickup_MIC")) + +//Stingray Logistic SCAR + Skins.Add((Id=9617, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet75_MAT.diver_scar.DiverVar5_SCAR_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_scar.DiverVar5_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_scar.DiverVar5_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_scar.DiverVar5_SCAR_3P_Pickup_MIC")) + +//Stingray Precious SCAR + Skins.Add((Id=9618, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet75_MAT.diver_scar.DiverPrecious_SCAR_1P_Mint_MIC", "WEP_SkinSet75_MAT.diver_scar.DiverPrecious_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_scar.DiverPrecious_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_scar.DiverPrecious_SCAR_3P_Pickup_MIC")) + +//Stingray Infantry M4 + Skins.Add((Id=9619, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M4', MIC_1P=("WEP_SkinSet75_MAT.diver_m4.DiverVar1_M4_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_m4.DiverVar1_M4_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_m4.DiverVar1_M4_3P_Pickup_MIC")) + +//Stingray Navy M4 + Skins.Add((Id=9620, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M4', MIC_1P=("WEP_SkinSet75_MAT.diver_m4.DiverVar2_M4_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_m4.DiverVar2_M4_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_m4.DiverVar2_M4_3P_Pickup_MIC")) + +//Stingray Engineer M4 + Skins.Add((Id=9621, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M4', MIC_1P=("WEP_SkinSet75_MAT.diver_m4.DiverVar3_M4_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_m4.DiverVar3_M4_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_m4.DiverVar3_M4_3P_Pickup_MIC")) + +//Stingray Artillery M4 + Skins.Add((Id=9622, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M4', MIC_1P=("WEP_SkinSet75_MAT.diver_m4.DiverVar4_M4_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_m4.DiverVar4_M4_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_m4.DiverVar4_M4_3P_Pickup_MIC")) + +//Stingray Logistic M4 + Skins.Add((Id=9623, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M4', MIC_1P=("WEP_SkinSet75_MAT.diver_m4.DiverVar5_M4_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_m4.DiverVar5_M4_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_m4.DiverVar5_M4_3P_Pickup_MIC")) + +//Stingray Precious M4 + Skins.Add((Id=9624, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M4', MIC_1P=("WEP_SkinSet75_MAT.diver_m4.DiverPrecious_M4_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_m4.DiverPrecious_M4_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_m4.DiverPrecious_M4_3P_Pickup_MIC")) + +//Stingray Infantry Desert Eagle + Skins.Add((Id=9625, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet75_MAT.diver_deagle.DiverVar1_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_deagle.DiverVar1_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_deagle.DiverVar1_Deagle_3P_Pickup_MIC")) + +//Stingray Navy Desert Eagle + Skins.Add((Id=9626, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet75_MAT.diver_deagle.DiverVar2_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_deagle.DiverVar2_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_deagle.DiverVar2_Deagle_3P_Pickup_MIC")) + +//Stingray Engineer Desert Eagle + Skins.Add((Id=9627, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet75_MAT.diver_deagle.DiverVar3_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_deagle.DiverVar3_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_deagle.DiverVar3_Deagle_3P_Pickup_MIC")) + +//Stingray Artillery Desert Eagle + Skins.Add((Id=9628, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet75_MAT.diver_deagle.DiverVar4_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_deagle.DiverVar4_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_deagle.DiverVar4_Deagle_3P_Pickup_MIC")) + +//Stingray Logistic Desert Eagle + Skins.Add((Id=9629, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet75_MAT.diver_deagle.DiverVar5_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_deagle.DiverVar5_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_deagle.DiverVar5_Deagle_3P_Pickup_MIC")) + +//Stingray Precious Desert Eagle + Skins.Add((Id=9630, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet75_MAT.diver_deagle.DiverPrecious_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_deagle.DiverPrecious_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_deagle.DiverPrecious_Deagle_3P_Pickup_MIC")) + +//Stingray Infantry Pulverizer + Skins.Add((Id=9631, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet75_MAT.diver_pulverizer.DiverVar1_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar1_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar1_Pulverizer_3P_Pickup_MIC")) + +//Stingray Navy Pulverizer + Skins.Add((Id=9632, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet75_MAT.diver_pulverizer.DiverVar2_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar2_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar2_Pulverizer_3P_Pickup_MIC")) + +//Stingray Engineer Pulverizer + Skins.Add((Id=9633, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet75_MAT.diver_pulverizer.DiverVar3_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar3_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar3_Pulverizer_3P_Pickup_MIC")) + +//Stingray Artillery Pulverizer + Skins.Add((Id=9634, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet75_MAT.diver_pulverizer.DiverVar4_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar4_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar4_Pulverizer_3P_Pickup_MIC")) + +//Stingray Logistic Pulverizer + Skins.Add((Id=9635, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet75_MAT.diver_pulverizer.DiverVar5_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar5_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_pulverizer.DiverVar5_Pulverizer_3P_Pickup_MIC")) + +//Stingray Precious Pulverizer + Skins.Add((Id=9636, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet75_MAT.diver_pulverizer.DiverPrecious_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet75_MAT.diver_pulverizer.DiverPrecious_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet75_MAT.diver_pulverizer.DiverPrecious_Pulverizer_3P_Pickup_MIC")) + +//Contamination Zone Precious Healer + Skins.Add((Id=9659, Weapondef=class'KFWeapDef_Healer', MIC_1P=("WEP_SkinSet77_MAT.contamination_healer.Contamination_Healer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet77_MAT.contamination_healer.Contamination_Healer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet77_MAT.contamination_healer.Contamination_Healer_3P_Pickup_MIC")) + +//Contamination Zone Precious Welder + Skins.Add((Id=9658, Weapondef=class'KFWeapDef_Welder', MIC_1P=("WEP_SkinSet77_MAT.contamination_welder.Contamination_Welder_1P_Mint_MIC"), MIC_3P="WEP_SkinSet77_MAT.contamination_welder.Contamination_Welder_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet77_MAT.contamination_welder.Contamination_Welder_3P_Pickup_MIC")) + +//S12 Shockgun Standard + Skins.Add((Id=9666, Weapondef=class'KFWeapDef_Shotgun_S12', MIC_1P=("WEP_1P_Saiga12_MAT.Wep_1stP_Saiga12_MIC"), MIC_3P="WEP_3P_Saiga12_MAT.WEP_3P_Saiga12_MIC", MIC_Pickup="WEP_3P_Saiga12_MAT.3P_Pickup_Saiga12_MIC")) + +//S12 Shockgun Guppy + Skins.Add((Id=9667, Weapondef=class'KFWeapDef_Shotgun_S12', MIC_1P=("WEP_SkinSet76_MAT.Wep_1stP_Saiga12_Guppy_MIC"), MIC_3P="WEP_SkinSet76_MAT.WEP_3P_Saiga12_Guppy_MIC", MIC_Pickup="WEP_SkinSet76_MAT.Weo_3P_Pickup_Saiga12_Guppy_MIC")) + +//S12 Shockgun Lux + Skins.Add((Id=9668, Weapondef=class'KFWeapDef_Shotgun_S12', MIC_1P=("WEP_SkinSet76_MAT.Wep_1stP_Saiga12_Lux_MIC"), MIC_3P="WEP_SkinSet76_MAT.WEP_3P_Saiga12_Lux_MIC", MIC_Pickup="WEP_SkinSet76_MAT.Wep_3P_Pickup_Saiga12_Lux_MIC")) + +//S12 Shockgun Navy Camo + Skins.Add((Id=9669, Weapondef=class'KFWeapDef_Shotgun_S12', MIC_1P=("WEP_SkinSet76_MAT.Wep_1stP_Saiga12_Ocean_MIC"), MIC_3P="WEP_SkinSet76_MAT.WEP_3P_Saiga12_Ocean_MIC", MIC_Pickup="WEP_SkinSet76_MAT.Wep_3P_Pickup_Saiga12_Ocean_MIC")) + +//S12 Shockgun Showstopper + Skins.Add((Id=9670, Weapondef=class'KFWeapDef_Shotgun_S12', MIC_1P=("WEP_SkinSet76_MAT.Wep_1stP_Saiga12_Racing_MIC"), MIC_3P="WEP_SkinSet76_MAT.WEP_3P_Saiga12_Racing_MIC", MIC_Pickup="WEP_SkinSet76_MAT.Wep_3P_Pickup_Saiga12_Racing_MIC")) + +//S12 Shockgun Tiger + Skins.Add((Id=9671, Weapondef=class'KFWeapDef_Shotgun_S12', MIC_1P=("WEP_SkinSet76_MAT.Wep_1stP_Saiga12_Tiger_MIC"), MIC_3P="WEP_SkinSet76_MAT.WEP_3P_Saiga12_Tiger_MIC", MIC_Pickup="WEP_SkinSet76_MAT.Wep_3P_Pickup_Saiga12_Tiger_MIC")) } \ No newline at end of file diff --git a/KFGame/Classes/KFWeeklyOutbreakInformation.uc b/KFGame/Classes/KFWeeklyOutbreakInformation.uc index d99d69f..5a69979 100644 --- a/KFGame/Classes/KFWeeklyOutbreakInformation.uc +++ b/KFGame/Classes/KFWeeklyOutbreakInformation.uc @@ -27,7 +27,7 @@ var localized array ModifierDescriptions; cpptext { /** Num of Weekly events available */ - static const int NumWeeklyEvents = 19; + static const int NumWeeklyEvents = 20; } DefaultProperties { diff --git a/KFGame/KFOnlineStats.uci b/KFGame/KFOnlineStats.uci index 28d01b3..7b0808f 100644 --- a/KFGame/KFOnlineStats.uci +++ b/KFGame/KFOnlineStats.uci @@ -159,4 +159,5 @@ const STATID_ACHIEVE_CarillonHamletCollectibles = 4061; const STATID_ACHIEVE_RigCollectibles = 4062; const STATID_ACHIEVE_BarmwichCollectibles = 4063; const STATID_ACHIEVE_CrashCollectibles = 4064; +const STATID_ACHIEVE_SubductionCollectibles = 4065; /** `endif */ diff --git a/KFGameContent/Classes/KFDT_Ballistic_AutoTurret.uc b/KFGameContent/Classes/KFDT_Ballistic_AutoTurret.uc index 3e8a4fd..f9e03b8 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_AutoTurret.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_AutoTurret.uc @@ -24,4 +24,7 @@ defaultproperties //Perk ModifierPerkList(0)=class'KFPerk_Commando' + + bCanZedTime=false + bCanEnrage=false } diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_Warthog.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_Warthog.uc new file mode 100644 index 0000000..310cafc --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_Warthog.uc @@ -0,0 +1,30 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Warthog +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_HRG_Warthog extends KFDT_Ballistic_Shell + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=2000 + KDeathUpKick=750 + KDeathVel=350 + + KnockdownPower=125 + StumblePower=340 + GunHitPower=275 + + WeaponDef=class'KFWeapDef_HRG_Warthog' + + //Perk + ModifierPerkList(0)=class'KFPerk_Demolitionist' + + bCanZedTime=false +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_Shotgun_S12.uc b/KFGameContent/Classes/KFDT_Ballistic_Shotgun_S12.uc new file mode 100644 index 0000000..55b6794 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_Shotgun_S12.uc @@ -0,0 +1,53 @@ +//============================================================================= +// KFDT_Ballistic_Shotgun_S12 +//============================================================================= +// Damage type class for the S12 shotgun +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_Shotgun_S12 extends KFDT_Ballistic_Shotgun + abstract + hidedropdown; + +/** Allows the damage type to customize exactly which hit zones it can dismember */ +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ + if( super.CanDismemberHitZone( InHitZoneName ) ) + { + return true; + } + + switch ( InHitZoneName ) + { + case 'lupperarm': + case 'rupperarm': + case 'chest': + case 'heart': + return true; + } + + return false; +} + +defaultproperties +{ + BloodSpread=0.4 + BloodScale=0.6 + + + KDamageImpulse=900 + KDeathUpKick=-500 + KDeathVel=350 + //KDamageImpulse=350 + //KDeathUpKick=120 + //KDeathVel=10 + + StumblePower=5 + GunHitPower=0 + + ModifierPerkList(0)=class'KFPerk_Support' + + WeaponDef=class'KFWeapDef_Shotgun_S12' +} diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_Warthog.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Warthog.uc new file mode 100644 index 0000000..152041c --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Warthog.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_Warthog +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_Warthog extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_Warthog' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_Shotgun_S12.uc b/KFGameContent/Classes/KFDT_Bludgeon_Shotgun_S12.uc new file mode 100644 index 0000000..98ffd1d --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_Shotgun_S12.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_Shotgun_S12 +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_Shotgun_S12 extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_Shotgun_S12' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Explosive_AutoTurret.uc b/KFGameContent/Classes/KFDT_Explosive_AutoTurret.uc index c2923b8..5631a77 100644 --- a/KFGameContent/Classes/KFDT_Explosive_AutoTurret.uc +++ b/KFGameContent/Classes/KFDT_Explosive_AutoTurret.uc @@ -24,7 +24,9 @@ defaultproperties KnockdownPower = 100 StumblePower = 300 - WeaponDef=class'KFWeapDef_AutoTurret' ModifierPerkList(0)=class'KFPerk_Commando' + + bCanZedTime=false + bCanEnrage=false } diff --git a/KFGameContent/Classes/KFDT_Explosive_HRG_Warthog.uc b/KFGameContent/Classes/KFDT_Explosive_HRG_Warthog.uc new file mode 100644 index 0000000..b12fc1e --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_HRG_Warthog.uc @@ -0,0 +1,32 @@ +//============================================================================= +// KFDT_Explosive_HRG_Warthog +//============================================================================= +// Explosive damage type for HRG Warthog explosion +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Explosive_HRG_Warthog extends KFDT_Explosive + abstract + hidedropdown; + +defaultproperties +{ + bShouldSpawnPersistentBlood = true + + // physics impact + RadialDamageImpulse = 2000 + GibImpulseScale = 0.15 + KDeathUpKick = 1000 + KDeathVel = 300 + + KnockdownPower = 50 + StumblePower = 150 + + WeaponDef=class'KFWeapDef_HRG_Warthog' + ModifierPerkList(0)=class'KFPerk_Demolitionist' + + bCanZedTime=false + bCanEnrage=false +} diff --git a/KFGameContent/Classes/KFDT_Explosive_HRG_Warthog_HighExplosive.uc b/KFGameContent/Classes/KFDT_Explosive_HRG_Warthog_HighExplosive.uc new file mode 100644 index 0000000..f77c0e8 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_HRG_Warthog_HighExplosive.uc @@ -0,0 +1,32 @@ +//============================================================================= +// KFDT_Explosive_HRG_Warthog_HighExplosive +//============================================================================= +// Explosive damage type for HRG Warthog Projectile Explosion +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Explosive_HRG_Warthog_HighExplosive extends KFDT_Explosive + abstract + hidedropdown; + +defaultproperties +{ + bShouldSpawnPersistentBlood = true + + // physics impact + RadialDamageImpulse = 2000 + GibImpulseScale = 0.15 + KDeathUpKick = 1000 + KDeathVel = 300 + + KnockdownPower = 50 + StumblePower = 150 + + WeaponDef=class'KFWeapDef_HRG_Warthog' + ModifierPerkList(0)=class'KFPerk_Demolitionist' + + bCanZedTime=false + bCanEnrage=false +} diff --git a/KFGameContent/Classes/KFDT_Explosive_Shotgun_S12.uc b/KFGameContent/Classes/KFDT_Explosive_Shotgun_S12.uc new file mode 100644 index 0000000..3fd1732 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_Shotgun_S12.uc @@ -0,0 +1,39 @@ +//============================================================================= +// KFDT_Explosive_Shotgun_S12 +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Explosive_Shotgun_S12 extends KFDT_EMP + abstract + hidedropdown; + +defaultproperties +{ + bShouldSpawnPersistentBlood=true + + // physics impact + RadialDamageImpulse=3000 //5000 //20000 + GibImpulseScale=0.15 + KDeathUpKick=1000 + KDeathVel=300 + + // unreal physics momentum + bExtraMomentumZ=True + + bCanGib=true + + KnockdownPower=100 + StunPower=25 + StumblePower=200 + EMPPower=100 + + //Perk + ModifierPerkList(0)=class'KFPerk_Support' + + WeaponDef=class'KFWeapDef_Shotgun_S12' + + bCanApplyRadialCalculationtoAffliction=false +} diff --git a/KFGameContent/Classes/KFExplosion_HRG_Warthog.uc b/KFGameContent/Classes/KFExplosion_HRG_Warthog.uc new file mode 100644 index 0000000..c4ed2cd --- /dev/null +++ b/KFGameContent/Classes/KFExplosion_HRG_Warthog.uc @@ -0,0 +1,36 @@ +//============================================================================= +// KFExplosion_HRG_Warthog +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFExplosion_HRG_Warthog extends KFExplosionActor; + +var private int HealingValue; + +// Disable Knockdown for friendlies +protected function bool KnockdownPawn(BaseAiPawn Victim, float DistFromExplosion) +{ + if (Victim.GetTeamNum() != Instigator.GetTeamNum()) + { + return Super.KnockdownPawn(Victim, DistFromExplosion); + } + + return false; +} + +// Disable Stumble for friendlies +protected function bool StumblePawn(BaseAiPawn Victim, float DistFromExplosion) +{ + if (Victim.GetTeamNum() != Instigator.GetTeamNum()) + { + return Super.StumblePawn(Victim, DistFromExplosion); + } + + return false; +} + +DefaultProperties +{ +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFGameInfo_Survival.uc b/KFGameContent/Classes/KFGameInfo_Survival.uc index bba82f8..4a2e3c4 100644 --- a/KFGameContent/Classes/KFGameInfo_Survival.uc +++ b/KFGameContent/Classes/KFGameInfo_Survival.uc @@ -882,12 +882,33 @@ function StartWave() WaveNum++; MyKFGRI.WaveNum = WaveNum; - if (IsMapObjectiveEnabled()) + if (MyKFGRI.IsContaminationMode()) { - MyKFGRI.ClearPreviousObjective(); - if (MyKFGRI.StartNextObjective()) + if (WaveNum == 1) // Only on first wave.. { - WaveBuffer = ObjectiveSpawnDelay; + MyKFGRI.ChooseNextObjective(WaveNum); + } + + MyKFGRI.ClearPreviousObjective(); + + if (WaveNum != WaveMax) + { + if (MyKFGRI.StartNextObjective()) + { + WaveBuffer = ObjectiveSpawnDelay; + } + } + } + else + { + if (IsMapObjectiveEnabled()) + { + MyKFGRI.ClearPreviousObjective(); + + if (MyKFGRI.StartNextObjective()) + { + WaveBuffer = ObjectiveSpawnDelay; + } } } diff --git a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc index 8dc8719..ca95379 100644 --- a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc +++ b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc @@ -33,6 +33,32 @@ var array PerkRoulette_PlayersDelegateData; var array PerkRoulette_PlayersDelegateInventory; +struct ContaminationModeData +{ + var() float FirstWaveInitialTimer; + var() float WaveInitialTimer; + var() float WaveCurrentTimer; + var() float GraceTimer; // We store on each Player the GraceCurrentTimer + var() float DamageTimer; + var() float DamageCurrentTimer; + var() bool ObjectiveHidden; + var() bool CanUpdate; + + structdefaultproperties + { + FirstWaveInitialTimer = 45.f + WaveInitialTimer = 30.f + WaveCurrentTimer = 0.f + GraceTimer = 5.f + DamageTimer = 1.f + DamageCurrentTimer = 0.f + ObjectiveHidden = false + CanUpdate = false + } +}; + +var ContaminationModeData ContaminationMode; + //----------------------------------------------------------------------------- // Statics static event class SetGameType(string MapName, string Options, string Portal) @@ -538,6 +564,18 @@ function Tick(float DeltaTime) // This deals with players joining at any time (lobby, or in wave) ChooseRandomPerks(false); } + + if (MyKFGRI.IsContaminationMode()) + { + if (ContaminationMode.CanUpdate) + { + UpdateContaminationMode(DeltaTime); + } + else if (WaveNum < (WaveMax - 1)) + { + UpdateContaminationModeTrader(); + } + } } function TickZedTime( float DeltaTime ) @@ -576,6 +614,11 @@ function WaveEnded(EWaveEndCondition WinCondition) ChooseRandomPerks(true); } + if (MyKFGRI.IsContaminationMode()) + { + ContaminationMode.CanUpdate = false; + } + super.WaveEnded(WinCondition); if (OutbreakEvent.ActiveEvent.bPermanentZedTime && ZedTimeRemaining > ZedTimeBlendOutTime) @@ -614,6 +657,19 @@ function GrantExtraDoshOnWaveWon() KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo).AddDosh(ExtraDosh, true); } } + + if (MyKFGRI.IsContaminationMode()) + { + ExtraDosh = MyKFGRI.ContaminationModeExtraDosh(); + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + if (KFPC.IsInState('Spectating') == false + && KFPC.PlayerReplicationInfo.bOnlySpectator == false) + { + KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo).AddDosh(ExtraDosh, true); + } + } + } } function ClearZedTimePCTimers() @@ -687,6 +743,22 @@ function StartWave() { OverridePickupList(); } + + if (MyKFGRI.IsContaminationMode()) + { + if (WaveNum == 1) + { + ContaminationMode.WaveCurrentTimer = ContaminationMode.FirstWaveInitialTimer; + } + else + { + ContaminationMode.WaveCurrentTimer = ContaminationMode.WaveInitialTimer; + } + + ContaminationMode.DamageCurrentTimer = ContaminationMode.DamageTimer; + ContaminationMode.ObjectiveHidden = false; + ContaminationMode.CanUpdate = true; + } } function bool OverridePickupList() @@ -1187,6 +1259,14 @@ function NotifyKilled(Controller Killer, Controller Killed, Pawn KilledPawn, cla PerkRoulette_PlayersDelegateInventory.AddItem(KFPC_WS_Killed); } } + + if (MyKFGRI.IsContaminationMode()) + { + if (KFPC_WS_Killed != none) + { + KFPC_WS_Killed.HideContaminationMode(); + } + } } function GunGameLevelGrantWeapon(KFPlayerController_WeeklySurvival KFPC_WS, class ToGrantWeaponDefinition) @@ -1827,6 +1907,189 @@ function BroadcastCustomDelegate() } } +/* + * Weekly 19: Contamination Mode + */ + +function UpdatePlayersState(KFMapObjective_DoshHold Area + , out array ValidPlayers + , out array PlayersInsideArea + , out array PlayersOutsideArea) +{ + local KFPlayerController_WeeklySurvival KFPC_WS; + local int i; + + // Get available players + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + if (KFPC_WS.Pawn.IsAliveAndWell() == false + || KFPC_WS.PlayerReplicationInfo.bOnlySpectator + || KFPC_WS.IsInState('Spectating')) + { + continue; + } + + ValidPlayers.AddItem(KFPC_WS); + } + + // Update who's in and who's out + for (i = 0 ; i < ValidPlayers.Length ; ++i) + { + // If is inside.. + if (Area.TouchingHumans.Find(KFPawn_Human(ValidPlayers[i].Pawn)) != INDEX_NONE) + { + PlayersInsideArea.AddItem(ValidPlayers[i]); + } + else + { + PlayersOutsideArea.AddItem(ValidPlayers[i]); + } + } +} + + function UpdateContaminationModeTrader() + { + local KFMapObjective_DoshHold Area; + local array ValidPlayers, PlayersInsideArea, PlayersOutsideArea; + local int i; + + Area = KFMapObjective_DoshHold(MyKFGRI.NextObjective); + + if (Area != none) + { + UpdatePlayersState(Area, ValidPlayers, PlayersInsideArea, PlayersOutsideArea); + + for (i = 0 ; i < PlayersInsideArea.Length ; ++i) + { + PlayersInsideArea[i].ShowContaminationMode(); + + PlayersInsideArea[i].UpdateContaminationModeWidget(true); + } + + for (i = 0 ; i < PlayersOutsideArea.Length ; ++i) + { + PlayersOutsideArea[i].ShowContaminationMode(); + + PlayersOutsideArea[i].ContaminationModePlayerIsInside = false; + PlayersOutsideArea[i].UpdateContaminationModeWidget_Timer(ContaminationMode.WaveInitialTimer); + } + } + } + +function UpdateContaminationMode(float DeltaTime) +{ + local KFMapObjective_DoshHold Area; + local array ValidPlayers, PlayersInsideArea, PlayersOutsideArea; + local KFPlayerController_WeeklySurvival KFPC_WS; + local int i; + local bool CheckPlayersInArea, CanApplyDamage; + + CheckPlayersInArea = false; + + // Update wave timer.. + if (ContaminationMode.WaveCurrentTimer > 0.f) + { + ContaminationMode.WaveCurrentTimer -= DeltaTime; + } + + if (MyKFGRI.CurrentObjective != none) + { + foreach WorldInfo.AllActors(class'KFMapObjective_DoshHold', Area) + { + if (Area.IsActive()) + { + CheckPlayersInArea = true; + + UpdatePlayersState(Area, ValidPlayers, PlayersInsideArea, PlayersOutsideArea); + + break; + } + } + } + + // If there's a valid area an objective.. + if (CheckPlayersInArea && WaveNum != WaveMax) + { + // Trigger logic depending on state of game + + if (ContaminationMode.WaveCurrentTimer > 0.f) + { + // If we are still on safe time to reach area, we can only notify Player if you are inside or outside, no Grace Timer, and No Damage applied + + for (i = 0 ; i < PlayersInsideArea.Length ; ++i) + { + PlayersInsideArea[i].UpdateContaminationModeWidget(true); + } + + for (i = 0 ; i < PlayersOutsideArea.Length ; ++i) + { + PlayersOutsideArea[i].ContaminationModePlayerIsInside = false; + PlayersOutsideArea[i].UpdateContaminationModeWidget_Timer(ContaminationMode.WaveCurrentTimer); + } + } + else + { + // If Time finished, we must Damage Players that are outside (use Grace Timer) + + if (ContaminationMode.DamageCurrentTimer > 0.f) + { + ContaminationMode.DamageCurrentTimer -= DeltaTime; + } + + CanApplyDamage = ContaminationMode.DamageCurrentTimer <= 0.f; + + // Reset damage tick + if (CanApplyDamage) + { + ContaminationMode.DamageCurrentTimer = ContaminationMode.DamageTimer; + } + + for (i = 0 ; i < PlayersInsideArea.Length ; ++i) + { + PlayersInsideArea[i].ContaminationModeGraceCurrentTimer = ContaminationMode.GraceTimer; + + PlayersInsideArea[i].UpdateContaminationModeWidget(true); + } + + for (i = 0 ; i < PlayersOutsideArea.Length ; ++i) + { + if (PlayersOutsideArea[i].ContaminationModeGraceCurrentTimer > 0.f) + { + PlayersOutsideArea[i].ContaminationModeGraceCurrentTimer -= DeltaTime; + } + + PlayersOutsideArea[i].UpdateContaminationModeWidget(false); + + if (CanApplyDamage && PlayersOutsideArea[i].ContaminationModeGraceCurrentTimer <= 0.f) + { + PlayersOutsideArea[i].Pawn.TakeDamage(class'KFDT_WeeklyContamination'.static.GetDamage(), none, vect(0,0,0), vect(0,0,0), class'KFDT_WeeklyContamination'); + } + } + } + } + else + { + // Hide UIs if no more objective + + if (ContaminationMode.ObjectiveHidden == false) + { + ContaminationMode.ObjectiveHidden = true; + + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + if (KFPC_WS.Pawn.IsAliveAndWell() == false + || KFPC_WS.PlayerReplicationInfo.bOnlySpectator + || KFPC_WS.IsInState('Spectating')) + { + continue; + } + + KFPC_WS.HideContaminationMode(); + } + } + } +} + // defaultproperties diff --git a/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc b/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc index 6f649fc..9affe4a 100644 --- a/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc +++ b/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc @@ -62,6 +62,25 @@ simulated function NotifyWaveStart() super.NotifyWaveStart(); } +function ChooseNextObjective(int NextWaveNum) +{ + local KFMapInfo KFMI; + + if (IsContaminationMode() == false) + { + super.ChooseNextObjective(NextWaveNum); + } + + KFMI = KFMapInfo(WorldInfo.GetMapInfo()); + + if (KFMI != none && NextWaveNum != WaveMax) + { + bForceNextObjective = true; // this overrides the objective chance, so it just chooses randomnly between all them + + ChooseNextRandomObjective(KFMI, NextWaveNum, false); + } +} + DefaultProperties { bIsWeeklyMode=True diff --git a/KFGameContent/Classes/KFMGA_Target.uc b/KFGameContent/Classes/KFMGA_Target.uc index 17fb918..4af9294 100644 --- a/KFGameContent/Classes/KFMGA_Target.uc +++ b/KFGameContent/Classes/KFMGA_Target.uc @@ -95,7 +95,7 @@ simulated function AddToOwnerArray() } } -simulated function TakeRadiusDamage(Controller InstigatedBy, float BaseDamage, float DamageRadius, class DamageType, float Momentum, vector HurtOrigin, bool bFullDamage, Actor DamageCauser, optional float DamageFalloffExponent = 1.f) +simulated function TakeRadiusDamage(Controller InstigatedBy, float BaseDamage, float DamageRadius, class DamageType, float Momentum, vector HurtOrigin, bool bFullDamage, Actor DamageCauser, optional float DamageFalloffExponent = 1.f, optional bool bAdjustRadiusDamage=true) { if (!bIgnoreRadiusDamage || AcceptedDamageTypes.Find(DamageType) != INDEX_NONE) { diff --git a/KFGameContent/Classes/KFMapObjective_DoshHold.uc b/KFGameContent/Classes/KFMapObjective_DoshHold.uc index bfccb53..1ba20c6 100644 --- a/KFGameContent/Classes/KFMapObjective_DoshHold.uc +++ b/KFGameContent/Classes/KFMapObjective_DoshHold.uc @@ -94,6 +94,7 @@ var() float RemindPlayersTime; var transient float PrevWaveProgress; var transient bool bRemindPlayers; +var Texture2D ContaminationIcon; simulated event ReplicatedEvent(name VarName) { @@ -125,11 +126,95 @@ event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vecto } } +simulated function bool ShouldDrawIcon() +{ + local KFGameReplicationInfo KFGRI; + + if (WorldInfo != None && WorldInfo.Game != none && WorldInfo.Game.GameReplicationInfo != none) + { + KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo); + + if (KFGRI != none && KFGRI.IsContaminationMode()) + { + return KFGRI.AIRemaining > KFGRI.ContaminationModeZedsToFinish(); + } + } + + return Super.ShouldDrawIcon(); +} + +simulated function GrantReward(KFPlayerReplicationInfo KFPRI, KFPlayerController KFPC) +{ + local KFGameReplicationInfo KFGRI; + + Super.GrantReward(KFPRI, KFPC); + + KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo); + + if (KFGRI == none) + { + return; + } + + if (KFGRI.IsContaminationMode() == false) + { + if (KFPRI == none) + { + return; + } + + if (KFPRI.bOnlySpectator) + { + return; + } + + if (KFPC != none) + { + // Summer 2023 objective + KFPC.ClientOnTryCompleteObjective(3, SEI_Summer); + } + } +} + function NotifyZedKilled(Controller Killer, Pawn KilledPawn, bool bIsBoss) { local int i; local KFGameInfo KFGI; local KFGameReplicationInfo KFGRI; + local KFPlayerController KFPC; + local KFPlayerReplicationInfo KFPRI; + + KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo); + + if (KFGRI != none && KFGRI.IsContaminationMode()) + { + if (ROLE == Role_Authority) + { + if (bActive) + { + if (KFGRI.AIRemaining <= KFGRI.ContaminationModeZedsToFinish()) + { + DeactivateObjective(); + + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + if (KFPC != none) + { + KFPRI = KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo); + + if (KFPRI != none && KFPRI.bOnlySpectator == false) + { + // Summer 2023 objective + KFPC.ClientOnTryCompleteObjective(3, SEI_Summer); + } + } + } + } + } + } + + return; + } if (ROLE == Role_Authority) { @@ -144,7 +229,6 @@ function NotifyZedKilled(Controller Killer, Pawn KilledPawn, bool bIsBoss) { if (RewardPerZed == 0) { - KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo); RewardPerZed = GetMaxDoshReward() / (PctOfWaveZedsKilledForMaxReward * KFGRI.WaveTotalAICount); } CurrentRewardAmount = FMin(CurrentRewardAmount + RewardPerZed, float(GetMaxDoshReward())); @@ -237,6 +321,16 @@ function StartPenaltyCheck() function ActivationVO() { + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo); + + if (KFGRI != none && KFGRI.IsContaminationMode()) + { + PlaySoundBase(AkEvent'WW_VOX_NPC_Trader.Play_Trader_DEFA_Area', false, WorldInfo.NetMode == NM_DedicatedServer); + return; + } + if (ActivationSoundEventOverride != none) { PlaySoundBase(ActivationSoundEventOverride, false, WorldInfo.NetMode == NM_DedicatedServer); @@ -373,6 +467,15 @@ simulated function DeactivateObjective() function PlayDeactivationDialog() { + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo); + + if (KFGRI != none && KFGRI.IsContaminationMode()) + { + return; + } + if (CurrentRewardAmount <= 0) { if (FailureSoundEventOverride != none) @@ -499,6 +602,23 @@ simulated function bool ShouldShowObjectiveHUD() return !IsComplete(); } +simulated function Texture2D GetIcon() +{ + local KFGameReplicationInfo KFGRI; + + if (WorldInfo != None && WorldInfo.Game != none && WorldInfo.Game.GameReplicationInfo != none) + { + KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo); + + if (KFGRI != none && KFGRI.IsContaminationMode()) + { + return ContaminationIcon; + } + } + + return ObjectiveIcon; +} + defaultproperties { DescriptionLocKey="DescriptionDoshHold" @@ -561,7 +681,7 @@ defaultproperties ZedThresholds[5]=0 ObjectiveIcon=Texture2D'Objectives_UI.UI_Objectives_ObjectiveMode' - + ContaminationIcon=Texture2D'Objectives_UI.UI_Objectives_Xmas_DefendObj' RemindPlayersTime=30.f bUseEarlyTrail=false diff --git a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc index 7b31756..f5e6581 100644 --- a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc +++ b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc @@ -1235,6 +1235,21 @@ defaultproperties )} )} + // Contamination Mode + SetEvents[19]={( + EventDifficulty=2, + GameLength=GL_Normal, + ContaminationModeZedsToFinish=5, + ContaminationModeExtraDosh=200, + SpawnReplacementList={( + (SpawnEntry=AT_EliteCrawler,NewClass=(class'KFGameContent.KFPawn_ZedGorefast'),PercentChance=0.9), + (SpawnEntry=AT_Siren,NewClass=(class'KFGameContent.KFPawn_ZedDAR_Laser'),PercentChance=0.2), + (SpawnEntry=AT_Bloat,NewClass=(class'KFGameContent.KFPawn_ZedDAR_Rocket'),PercentChance=0.2) + + )} + + + )} //Test events from here down. These don't end up in the regular rotation. // The override ID starts from one higher than the last SetEvents entry above. // Ex: Big head = 7, Horde = 8 diff --git a/KFGameContent/Classes/KFPawn_AutoTurret.uc b/KFGameContent/Classes/KFPawn_AutoTurret.uc index d1734e4..1490577 100644 --- a/KFGameContent/Classes/KFPawn_AutoTurret.uc +++ b/KFGameContent/Classes/KFPawn_AutoTurret.uc @@ -105,6 +105,9 @@ const NoAmmoSocketName = 'malfunction'; const NoAmmoFXTemplate = ParticleSystem'WEP_AutoTurret_EMIT.FX_NoAmmo_Sparks'; var transient ParticleSystemComponent NoAmmoFX; +var transient vector DeployLastLocation; +var transient float LastMoveExpectedSize; + replication { if( bNetDirty ) @@ -374,7 +377,7 @@ simulated state Deploy SetTimer(AnimDuration, false, nameof(StartIdleAnim)); } - SetPhysics(PHYS_FLYING); + SetPhysics(PHYS_NONE); if (Role == ROLE_Authority) { @@ -390,22 +393,33 @@ simulated state Deploy super.Tick(DeltaTime); + // If we didn't move.. + if (VSize(Location - DeployLastLocation) < (LastMoveExpectedSize * 0.8f)) + { + SetTurretState(ETS_TargetSearch); + return; + } + LocationNext = Location; LocationNext.z += Velocity.z * DeltaTime; - // If there's little to no movement or we are going to collide - if (Velocity.z <= 0.01f || !FastTrace(LocationNext, Location, vect(25,25,25))) + // If we are going to collide stop + if (!FastTrace(LocationNext, Location, vect(25,25,25))) { SetTurretState(ETS_TargetSearch); + return; } - else + + DeployLastLocation = Location; + LastMoveExpectedSize = VSize(LocationNext - Location); + + SetLocation(LocationNext); + + // Check height to change state + CurrentHeight = Location.Z - GroundLocation.Z; + if (CurrentHeight >= DeployHeight) { - // Check height to change state - CurrentHeight = Location.Z - GroundLocation.Z; - if (CurrentHeight >= DeployHeight) - { - SetTurretState(ETS_TargetSearch); - } + SetTurretState(ETS_TargetSearch); } } @@ -425,6 +439,8 @@ simulated state Deploy SetTimer(0.25f, true, nameof(CheckEnemiesWithinExplosionRadius)); } } + + SetPhysics(PHYS_NONE); } } @@ -533,6 +549,8 @@ simulated state Combat local float NewAmmoPercentage; + local bool bIsSpotted; + if (Role == ROLE_Authority) { TurretWeapon.GetMuzzleLocAndRot(MuzzleLoc, MuzzleRot); @@ -565,11 +583,14 @@ simulated state Combat // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! HitActor = Trace(HitLocation, HitNormal, EnemyTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); - /** Search for new enemies if current is dead, cloaked or too far, or something between the drone and the target except a player */ + // Visible by local player or team + bIsSpotted = (EnemyTarget.bIsCloakingSpottedByLP || EnemyTarget.bIsCloakingSpottedByTeam); + + /** Search for new enemies if current is dead, cloaked or too far, or something between the drone that's world geometry */ if (!EnemyTarget.IsAliveAndWell() - || EnemyTarget.bIsCloaking + || (EnemyTarget.bIsCloaking && bIsSpotted == false) || VSizeSq(EnemyTarget.Location - Location) > EffectiveRadius * EffectiveRadius - || (HitActor != none && KFPawn_Monster(HitActor) == none && KFPawn_Human(HitActor) == none)) + || (HitActor != none && HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None)) { EnemyTarget = none; CheckForTargets(); @@ -590,13 +611,13 @@ simulated state Combat RotateBySpeed(DesiredRotationRot); - if (Role == ROLE_Authority) + if (Role == ROLE_Authority && ReachedRotation()) { HitActor = Trace(HitLocation, HitNormal, MuzzleLoc + vector(Rotation) * EffectiveRadius, MuzzleLoc, , , HitInfo, TRACEFLAG_Bullet); if (TurretWeapon != none) { - if (KFPawn_Monster(HitActor) != none) + if (HitActor != none && HitActor.bWorldGeometry == false) { TurretWeapon.Fire(); @@ -641,6 +662,7 @@ simulated state Detonate { ExploActor.InstigatorController = Instigator.Controller; ExploActor.Instigator = Instigator; + ExploActor.bIgnoreInstigator = true; ExploActor.Explode(ExplosionTemplate); } @@ -732,6 +754,8 @@ function CheckForTargets() local vector HitLocation, HitNormal; local Actor HitActor; + local bool bIsSpotted; + if (EnemyTarget != none) { CurrentDistance = VSizeSq(Location - EnemyTarget.Location); @@ -745,10 +769,19 @@ function CheckForTargets() foreach CollidingActors(class'KFPawn_Monster', CurrentTarget, EffectiveRadius, Location, true,, HitInfo) { + // Visible by local player or team + bIsSpotted = (CurrentTarget.bIsCloakingSpottedByLP || CurrentTarget.bIsCloakingSpottedByTeam); + + if (!CurrentTarget.IsAliveAndWell() + || (CurrentTarget.bIsCloaking && bIsSpotted == false)) + { + continue; + } + // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! HitActor = Trace(HitLocation, HitNormal, CurrentTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); - if (!CurrentTarget.IsAliveAndWell() || CurrentTarget.bIsCloaking || HitActor == none || KFPawn_Monster(HitActor) == none) + if (HitActor == none || (HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None)) { continue; } @@ -992,7 +1025,8 @@ simulated function TakeRadiusDamage( vector HurtOrigin, bool bFullDamage, Actor DamageCauser, - optional float DamageFalloffExponent=1.f + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true ) {} @@ -1001,6 +1035,16 @@ function bool CanAITargetThisPawn(Controller TargetingController) return false; } +simulated function bool CanInteractWithPawnGrapple() +{ + return false; +} + +simulated function bool CanInteractWithZoneVelocity() +{ + return false; +} + simulated function UpdateTurretMeshMaterialColor(float Value) { if (TurretWeaponAttachment == none) @@ -1104,6 +1148,9 @@ defaultproperties CamShakeOuterRadius=900 CamShakeFalloff=1.5f bOrientCameraShakeTowardsEpicenter=true + + bIgnoreInstigator=true + ActorClassToIgnoreForDamage = class'KFPawn_Human' End Object ExplosionTemplate=ExploTemplate0 @@ -1163,4 +1210,7 @@ defaultproperties bAlwaysRelevant=true AutoTurretFlashCount=0 + + DeployLastLocation=(X=-9999.f, Y=-9999.f, Z=-9999.f) + LastMoveExpectedSize= 0.f } diff --git a/KFGameContent/Classes/KFPawn_HRG_Warthog.uc b/KFGameContent/Classes/KFPawn_HRG_Warthog.uc new file mode 100644 index 0000000..ecb2d41 --- /dev/null +++ b/KFGameContent/Classes/KFPawn_HRG_Warthog.uc @@ -0,0 +1,1310 @@ +//============================================================================= +// KFPawn_HRG_Warthog +//============================================================================= +// Base pawn class for HRG Warthog +//============================================================================= +// 2022! +//============================================================================= +class KFPawn_HRG_Warthog extends KFPawn + notplaceable; + +`define AUTOTURRET_MIC_INDEX 0 + +enum ETurretState +{ + ETS_None, + ETS_Throw, + ETS_Deploy, + ETS_TargetSearch, + ETS_Combat, + ETS_Detonate, + ETS_Empty +}; + +var SkeletalMeshComponent TurretMesh; +var Controller InstigatorController; + +/** Speed to rise the drone in Z axis after thrown */ +var const float DeployZSpeed; +/** Height (Z axis) the drone should position for deployment */ +var const float DeployHeight; +/** Radius to detect enemies */ +var const float EffectiveRadius; +/** Radius to trigger explosion */ +var const float ExplosiveRadius; +/** Rotation Vel for aiming targets */ +var const rotator AimRotationVel; +/** Rotation Vel for aiming targets */ +var const rotator CombatRotationVel; +/** Time before refreshing targets */ +var const float TimeBeforeRefreshingTargets; +/** Time to idle after rotating to a random position (search) */ +var const float TimeIdlingWhenSearching; +/** Min Value to apply randomness to idling */ +var const float MinIdlingVariation; +/** Max Value to apply randomness to idling */ +var const float MaxIdlingVariation; +/** Pitch requested when out of ammo */ +var const int EmptyPitch; +/** Turret Explosion */ +var GameExplosion ExplosionTemplate; +/** Weapon the turret carries */ +var class WeaponDefinition; +/** WIf explodes when an enemy gets nearby */ +var const bool bCanDetonateOnProximityWithAmmo; + +var repnotify ETurretState CurrentState; +var repnotify rotator ReplicatedRotation; +var repnotify float CurrentAmmoPercentage; +var repnotify AKEvent TurretWeaponAmbientSound; +var repnotify int WeaponSkinID; +var repnotify int AutoTurretFlashCount; + +var transient rotator DeployRotation; + +/** Rotation */ +var transient rotator RotationStart; +var transient rotator TargetRotation; +var transient float RotationAlpha; +var transient float RotationTime; +var transient bool bRotating; + +var transient KFWeap_HRG_WarthogWeapon TurretWeapon; +var transient KFWeapAttach_HRG_WarthogWeap TurretWeaponAttachment; + +var transient KFWeap_HRG_Warthog OwnerWeapon; + +var bool deployUsingOffsetFromPlayerLocation; +var float deployUsingOffsetZ; + +var transient vector ThrowInstigatorLocation; +var transient vector GroundLocation; +var transient vector DeployedLocation; +var transient float RandomSearchLocation; +var transient KFPawn_Monster EnemyTarget; +var transient bool bHasSearchRandomLocation; +var transient byte TeamNum; +var transient bool bRotatingByTime; +var transient MaterialInstanceConstant TurretAttachMIC; + +/** Socket name to attach weapon */ +const WeaponSocketName = 'WeaponSocket'; +const WeaponAttachmentSocketName = 'WeaponAttachment'; + +const TransitionParamName = 'transition_full_to_empty'; +const EmptyParamName = 'Blinking_0_off___1_on'; + +const DroneFlyingStartAudio = AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Drone_IdleFly'; +const DroneFlyingStopAudio = AkEvent'WW_WEP_HRG_Warthog.Stop_WEP_HRG_Warthog_Drone_IdleFly'; + +var AkComponent FlyAkComponent; + +const DroneDryFire = AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Drone_DryFire'; + +const NoAmmoSocketName = 'malfunction'; +const NoAmmoFXTemplate = ParticleSystem'WEP_HRG_Warthog_EMIT.FX_HRG_Warthog_NoAmmo_Sparks'; +var transient ParticleSystemComponent NoAmmoFX; + +var transient int DetonateNumberOfProjectiles; +var transient int DetonateMinimumDistance; +var transient int DetonateMaximumDistance; +var transient float DetonateMinTime; +var transient float DetonateMaxTime; + +var transient vector DeployLastLocation; +var transient float LastMoveExpectedSize; + +replication +{ + if( bNetDirty ) + CurrentState, ReplicatedRotation, CurrentAmmoPercentage, TurretWeaponAmbientSound, EnemyTarget, WeaponSkinID, AutoTurretFlashCount; +} + +simulated event ReplicatedEvent(name VarName) +{ + if( VarName == nameof(CurrentState) ) + { + ChangeState(CurrentState); + } + else if (VarName == nameof(ReplicatedRotation)) + { + RotateBySpeed(ReplicatedRotation); + } + else if (VarName == nameof(CurrentAmmoPercentage)) + { + UpdateTurretMeshMaterialColor(CurrentAmmoPercentage); + } + else if (VarName == nameof(TurretWeaponAmbientSound)) + { + SetWeaponAmbientSound(TurretWeaponAmbientSound); + } + else if (VarName == nameof(WeaponSkinID)) + { + SetWeaponSkin(WeaponSkinID); + } + else if (VarName == nameof(AutoTurretFlashCount)) + { + FlashCountUpdated(Weapon, AutoTurretFlashCount, TRUE); + } + else if (VarName == nameof(FlashCount)) + { + // Intercept Flash Count: do nothing + } + else + { + super.ReplicatedEvent(VarName); + } +} + +simulated event PreBeginPlay() +{ + local class WeaponClass; + local rotator ZeroRotator; + + super.PreBeginPlay(); + + WeaponClass = class (DynamicLoadObject(WeaponDefinition.default.WeaponClassPath, class'Class')); + WeaponClassForAttachmentTemplate = WeaponClass; + + SetMeshVisibility(false); + + if (Role == ROLE_Authority) + { + Weapon = Spawn(WeaponClass, self); + TurretWeapon = KFWeap_HRG_WarthogWeapon(Weapon); + MyKFWeapon = TurretWeapon; + + if (Weapon != none) + { + Weapon.bReplicateInstigator=true; + Weapon.bReplicateMovement=true; + Weapon.Instigator = Instigator; + TurretWeapon.InstigatorDrone = self; + Weapon.SetCollision(false, false); + Weapon.SetBase(self,, TurretMesh, WeaponSocketName); + TurretMesh.AttachComponentToSocket(Weapon.Mesh, WeaponSocketName); + + Weapon.SetRelativeLocation(vect(0,0,0)); + Weapon.SetRelativeRotation(ZeroRotator); + Weapon.Mesh.SetOnlyOwnerSee(true); + MyKFWeapon.Mesh.SetHidden(true); + } + } + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + /** If this fails, the HRG Warthog is still loading from KFWeap_HRG_Warthog. */ + if (WeaponClass.default.AttachmentArchetype != none) + { + SetTurretWeaponAttachment(WeaponClass); + } + } +} + +simulated function SetTurretWeaponAttachment(class WeaponClass) +{ + local KFWeaponAttachment AttachmentTemplate; + local rotator ZeroRotator; + + if (WeaponAttachment != none) + return; + + // Create the new Attachment. + AttachmentTemplate = WeaponClass.default.AttachmentArchetype; + WeaponAttachment = Spawn(AttachmentTemplate.Class, self,,,, AttachmentTemplate); + + if (WeaponAttachment != None) + { + WeaponAttachment.SetCollision(false, false); + WeaponAttachment.Instigator = Instigator; + + TurretMesh.AttachComponentToSocket(WeaponAttachment.WeapMesh, WeaponAttachmentSocketName); + WeaponAttachment.SetRelativeLocation(vect(0,0,0)); + WeaponAttachment.SetRelativeRotation(ZeroRotator); + WeaponAttachment.WeapMesh.SetOnlyOwnerSee(false); + WeaponAttachment.WeapMesh.SetOwnerNoSee(false); + WeaponAttachment.ChangeVisibility(true); + + WeaponAttachment.AttachLaserSight(); + + TurretWeaponAttachment = KFWeapAttach_HRG_WarthogWeap(WeaponAttachment); + TurretWeaponAttachment.PlayCloseAnim(); + } +} + +simulated function UpdateInstigator(Pawn NewInstigator) +{ + local KFPawn_Human KFPH; + + Instigator = NewInstigator; + + TeamNum = Instigator.GetTeamNum(); + + if (Weapon != none) + { + Weapon.Instigator = NewInstigator; + } + + KFPH = KFPawn_Human(NewInstigator); + if (KFPH != none && KFPH.WeaponSkinItemId > 0) + { + SetWeaponSkin(KFPH.WeaponSkinItemId); + } +} + +simulated function SetWeaponSkin(int SkinID) +{ + if (Role == Role_Authority) + { + WeaponSkinID = SkinID; + bForceNetUpdate = true; + } + + if (WeaponAttachment != none) + { + WeaponAttachment.SetWeaponSkin(SkinID); + } +} + +simulated function UpdateWeaponUpgrade(int UpgradeIndex) +{ + if (Weapon != none) + { + TurretWeapon.SetWeaponUpgradeLevel(UpgradeIndex); + } +} + +/** + Object states + */ +function SetTurretState(ETurretState State) +{ + if (CurrentState == State) + return; + + ChangeState(State); + + CurrentState = State; + bForceNetUpdate = true; +} + +function UpdateReadyToUse(bool bReady) +{ + if (OwnerWeapon != none) + { + OwnerWeapon.SetReadyToUse(bReady); + } +} + +simulated function ChangeState(ETurretState State) +{ + switch(State) + { + case ETS_None: + break; + case ETS_Throw: + GotoState('Throw'); + break; + case ETS_Deploy: + GotoState('Deploy'); + break; + case ETS_TargetSearch: + GotoState('TargetSearch'); + break; + case ETS_Combat: + GotoState('Combat'); + break; + case ETS_Detonate: + GotoState('Detonate'); + break; + case ETS_Empty: + GotoState('Empty'); + break; + default: + break; + } +} + +auto simulated state Throw +{ + simulated function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + ThrowInstigatorLocation = Instigator.Location; + + if (Role == Role_Authority) + { + UpdateReadyToUse(false); + } + } + + simulated event Landed(vector HitNormal, actor FloorActor) + { + super.Landed(HitNormal, FloorActor); + + if (Role == ROLE_Authority) + { + GroundLocation = Location; + DeployRotation = Rotation; + + SetTurretState(ETS_Deploy); + } + } + + simulated event Tick(float DeltaTime) + { + if (deployUsingOffsetFromPlayerLocation) + { + // If the drone goes below the offset, prevent from falling more + if (Location.Z < (ThrowInstigatorLocation.z - deployUsingOffsetZ)) + { + GroundLocation = Location; + DeployRotation = Rotation; + + SetTurretState(ETS_Deploy); + } + } + + super.Tick(DeltaTime); + } +} + +simulated state Deploy +{ + simulated function BeginState(name PreviousStateName) + { + local float AnimDuration; + + super.BeginState(PreviousStateName); + + if (TurretWeaponAttachment != none) + { + AnimDuration = TurretWeaponAttachment.PlayDeployAnim(); + SetTimer(AnimDuration, false, nameof(StartIdleAnim)); + } + + SetPhysics(PHYS_NONE); + + if (Role == ROLE_Authority) + { + Velocity = vect(0,0,1) * DeployZSpeed; + UpdateReadyToUse(false); + } + } + + simulated event Tick(float DeltaTime) + { + local float CurrentHeight; + local vector LocationNext; + + super.Tick(DeltaTime); + + // If we didn't move.. + if (VSize(Location - DeployLastLocation) < (LastMoveExpectedSize * 0.8f)) + { + SetTurretState(ETS_TargetSearch); + return; + } + + LocationNext = Location; + LocationNext.z += Velocity.z * DeltaTime; + + // If we are going to collide stop + if (!FastTrace(LocationNext, Location, vect(25,25,25))) + { + SetTurretState(ETS_TargetSearch); + return; + } + + DeployLastLocation = Location; + LastMoveExpectedSize = VSize(LocationNext - Location); + + SetLocation(LocationNext); + + // Check height to change state + CurrentHeight = Location.Z - GroundLocation.Z; + if (CurrentHeight >= DeployHeight) + { + SetTurretState(ETS_TargetSearch); + } + } + + simulated function EndState(name NextStateName) + { + super.EndState(NextStateName); + DeployedLocation = Location; + + Velocity = vect(0,0,0); + + if (Role == ROLE_Authority) + { + UpdateReadyToUse(true); + + if (bCanDetonateOnProximityWithAmmo) + { + SetTimer(0.25f, true, nameof(CheckEnemiesWithinExplosionRadius)); + } + } + } +} + +simulated state TargetSearch +{ + simulated function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + bHasSearchRandomLocation = false; + + if (Role == ROLE_Authority) + { + GetRandomSearchLocation(); + // bHasSearchRandomLocation=true; + + SetTimer(TimeBeforeRefreshingTargets, true, nameof(CheckForTargets)); + } + } + + simulated function EndState(name EndStateName) + { + if (Role == ROLE_Authority) + { + ClearTimer(nameof(CheckForTargets)); + ClearTimer(nameof(GetRandomSearchLocation)); + } + + super.EndState(EndStateName); + } + + simulated event Tick( float DeltaTime ) + { + super.Tick(DeltaTime); + + if (Role == ROLE_Authority) + { + if (ReachedRotation() && !bHasSearchRandomLocation) + { + SetTimer( TimeIdlingWhenSearching + RandRange(MinIdlingVariation, MaxIdlingVariation), false, nameof(GetRandomSearchLocation)); + bHasSearchRandomLocation = true; + } + } + } + + function GetRandomSearchLocation() + { + local rotator NewRotation; + + NewRotation.Yaw += RandRange(0,65536); + bHasSearchRandomLocation = false; + + RequestRotation(NewRotation); + } +} + +simulated state Combat +{ + simulated function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + /*if (Role == ROLE_Authority) + { + SetTimer(TimeBeforeRefreshingTargets, true, nameof(CheckForTargets)); + }*/ + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + if (TurretWeaponAttachment != none) + { + TurretWeaponAttachment.UpdateLaserColor(true); + } + } + } + + simulated function EndState(name NextStateName) + { + ClearTimer(nameof(CheckForTargets)); + + if (Role == ROLE_Authority && TurretWeapon != none) + { + TurretWeapon.StopFire(0); + } + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + if (TurretWeaponAttachment != none) + { + TurretWeaponAttachment.UpdateLaserColor(false); + } + } + + super.EndState(NextStateName); + } + + simulated event Tick( float DeltaTime ) + { + local vector MuzzleLoc; + local rotator MuzzleRot; + local rotator DesiredRotationRot; + + local vector HitLocation, HitNormal; + local TraceHitInfo HitInfo; + local Actor HitActor; + + local float NewAmmoPercentage; + + local bool bIsSpotted; + + if (Role == ROLE_Authority) + { + TurretWeapon.GetMuzzleLocAndRot(MuzzleLoc, MuzzleRot); + + NewAmmoPercentage = float(TurretWeapon.AmmoCount[0]) / TurretWeapon.MagazineCapacity[0]; + + if (NewAmmoPercentage != CurrentAmmoPercentage) + { + CurrentAmmoPercentage = NewAmmoPercentage; + + if (WorldInfo.NetMode == NM_Standalone) + { + UpdateTurretMeshMaterialColor(CurrentAmmoPercentage); + } + else + { + bNetDirty = true; + } + } + } + else + { + WeaponAttachment.WeapMesh.GetSocketWorldLocationAndRotation('MuzzleFlash', MuzzleLoc, MuzzleRot); + } + + if (Role == ROLE_Authority) + { + if (EnemyTarget != none) + { + // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! + HitActor = Trace(HitLocation, HitNormal, EnemyTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); + + // Visible by local player or team + bIsSpotted = (EnemyTarget.bIsCloakingSpottedByLP || EnemyTarget.bIsCloakingSpottedByTeam); + + /** Search for new enemies if current is dead, cloaked or too far, or something between the drone that's world geometry */ + if (!EnemyTarget.IsAliveAndWell() + || (EnemyTarget.bIsCloaking && bIsSpotted == false) + || VSizeSq(EnemyTarget.Location - Location) > EffectiveRadius * EffectiveRadius + || (HitActor != none && HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None)) + { + EnemyTarget = none; + CheckForTargets(); + + if (EnemyTarget == none) + { + SetTurretState(ETS_TargetSearch); + return; + } + } + } + } + + if (EnemyTarget != none) + { + DesiredRotationRot = rotator(Normal(EnemyTarget.Mesh.GetBoneLocation('Spine1') - MuzzleLoc)); + DesiredRotationRot.Roll = 0; + + RotateBySpeed(DesiredRotationRot); + + if (Role == ROLE_Authority && ReachedRotation()) + { + HitActor = Trace(HitLocation, HitNormal, MuzzleLoc + vector(Rotation) * EffectiveRadius, MuzzleLoc, , , HitInfo, TRACEFLAG_Bullet); + + if (TurretWeapon != none) + { + if (HitActor != none && HitActor.bWorldGeometry == false) + { + TurretWeapon.CurrentTarget = EnemyTarget; + + TurretWeapon.Fire(); + + if (!TurretWeapon.HasAmmo(0)) + { + SetTurretState(ETS_Empty); + } + } + else + { + TurretWeapon.StopFire(0); + } + } + } + } + + super.Tick(DeltaTime); + } +} + +simulated state Detonate +{ + function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + StopIdleSound(); + + if (Role == ROLE_Authority) + { + TriggerExplosion(); + } + } + + function array GenerateFuseTimes() + { + local float i; + local array FuseTimes; + + i = 0; + while (i < (DetonateMaxTime - DetonateMinTime)) + { + FuseTimes.AddItem(DetonateMinTime + i); + i += 0.1f; + } + + return FuseTimes; + } + + function TriggerExplosion() + { + local KFExplosionActorReplicated ExploActor; + local int i, RandomNumber; + local vector StartTrace, AimDir, X, Y, Z; + local rotator StartAimRot; + local class MyProjectileClass; + local Quat R; + local float Angle, FuseTime; + local KFProj_HighExplosive_HRG_Warthog Projectile; + local array FuseTimes; + + // Shoot grenades around + + MyProjectileClass = class'KFProj_HighExplosive_HRG_Warthog'; + + if( Role == ROLE_Authority || (MyProjectileClass.default.bUseClientSideHitDetection + && MyProjectileClass.default.bNoReplicationToInstigator && Instigator != none + && Instigator.IsLocallyControlled()) ) + { + TurretWeapon.CurrentTarget = none; + + TurretWeapon.GetMuzzleLocAndRot(StartTrace, StartAimRot); + + StartAimRot.Pitch = 0.f; + StartAimRot.Roll = 0.f; + + GetAxes(StartAimRot, X, Y, Z); + + Angle = 360.f / DetonateNumberOfProjectiles; + + // Randomize a vector so the values are not repeated + FuseTimes = GenerateFuseTimes(); + + for (i = 0; i < DetonateNumberOfProjectiles; ++i) + { + // Generate different angle for each one + R = QuatFromAxisAndAngle(Z, Angle * DegToRad * i); + + AimDir = QuatRotateVector(R, vector(StartAimRot)); + AimDir.Z = 0.f; + + // Generate different distance from start location for each one + TurretWeapon.CurrentDistanceProjectile = DetonateMinimumDistance + Rand(DetonateMaximumDistance - DetonateMinimumDistance); + + Projectile = KFProj_HighExplosive_HRG_Warthog(TurretWeapon.SpawnProjectile(class'KFProj_HighExplosive_HRG_Warthog', StartTrace, AimDir)); + + // Setup as timed explosive and set the timer.. + + FuseTime = DetonateMinTime; + + if (FuseTimes.Length == 0) + { + FuseTimes = GenerateFuseTimes(); + } + + if (FuseTimes.Length != 0) + { + RandomNumber = Rand(FuseTimes.Length); + + FuseTime = FuseTimes[RandomNumber]; + + FuseTimes.Remove(RandomNumber, 1); + } + + Projectile.SetupDetonationTimer(FuseTime); + } + + TurretWeapon.CurrentDistanceProjectile = -1.f; + } + + // explode using the given template + ExploActor = Spawn(class'KFExplosionActorReplicated', TurretWeapon,, Location, Rotation,, true); + if (ExploActor != None) + { + ExploActor.InstigatorController = Instigator.Controller; + ExploActor.Instigator = Instigator; + ExploActor.bIgnoreInstigator = true; + + ExploActor.Explode(ExplosionTemplate); + } + + Destroy(); + } +} + +simulated state Empty +{ + simulated function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + // Attach No Ammo VFX + if (NoAmmoFX == none) + { + NoAmmoFX = WorldInfo.MyEmitterPool.SpawnEmitter(NoAmmoFXTemplate, Location); + } + + // Play dry sound 2 or 3 times + SetTimer(0.5f, false, 'PlayEmptySound1'); + SetTimer(1.f, false, 'PlayEmptySound2'); + SetTimer(1.5f, false, 'PlayEmptySound3'); + + // When sound finish play animation + SetTimer(2.f, false, 'PlayEmptyState'); + + UpdateTurretMeshMaterialColor(0.0f); + } + + if (Role == ROLE_Authority && !bCanDetonateOnProximityWithAmmo) + { + SetTimer(0.25f, true, nameof(CheckEnemiesWithinExplosionRadius)); + } + } + + simulated function EndState(name NextStateName) + { + super.EndState(NextStateName); + } +} + +// We can't use the same delegate, hence we create 3 functions.. + +simulated function PlayEmptySound1() +{ + PlaySoundBase(DroneDryFire); +} + +simulated function PlayEmptySound2() +{ + PlaySoundBase(DroneDryFire); +} + +simulated function PlayEmptySound3() +{ + PlaySoundBase(DroneDryFire); +} + +simulated function PlayEmptyState() +{ + if (TurretWeaponAttachment != none) + { + StopIdleSound(); + + TurretWeaponAttachment.PlayEmptyState(); + } +} + +simulated function StopIdleSound() +{ + FlyAkComponent.StopEvents(); +} + +function CheckForTargets() +{ + local KFPawn_Monster CurrentTarget; + local TraceHitInfo HitInfo; + + local float CurrentDistance; + local float Distance; + + local vector MuzzleLoc; + local rotator MuzzleRot; + + local vector HitLocation, HitNormal; + local Actor HitActor; + + local bool bIsSpotted; + + if (EnemyTarget != none) + { + CurrentDistance = VSizeSq(Location - EnemyTarget.Location); + } + else + { + CurrentDistance = 9999.f; + } + + TurretWeapon.GetMuzzleLocAndRot(MuzzleLoc, MuzzleRot); + + foreach CollidingActors(class'KFPawn_Monster', CurrentTarget, EffectiveRadius, Location, true,, HitInfo) + { + // Visible by local player or team + bIsSpotted = (CurrentTarget.bIsCloakingSpottedByLP || CurrentTarget.bIsCloakingSpottedByTeam); + + if (!CurrentTarget.IsAliveAndWell() + || (CurrentTarget.bIsCloaking && bIsSpotted == false)) + { + continue; + } + + // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! + HitActor = Trace(HitLocation, HitNormal, CurrentTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); + + if (HitActor == none || (HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None)) + { + continue; + } + + Distance = VSizeSq(Location - CurrentTarget.Location); + + if (EnemyTarget == none) + { + EnemyTarget = CurrentTarget; + CurrentDistance = Distance; + } + else if (CurrentDistance > Distance) + { + EnemyTarget = CurrentTarget; + CurrentDistance = Distance; + } + } + + if (EnemyTarget != none) + { + SetTurretState(ETS_Combat); + } +} + +//////////////////////////////////////////////////////////// + +simulated event Destroyed() +{ + local KFWeap_HRG_Warthog WeapOwner; + + StopIdleSound(); + + WeapOwner = KFWeap_HRG_Warthog(Owner); + + if (WeapOwner != none) + { + WeapOwner.RemoveDeployedTurret(,self); + } + + ClearTimer(nameof(CheckEnemiesWithinExplosionRadius)); + + if (NoAmmoFX != none) + { + NoAmmoFX.KillParticlesForced(); + WorldInfo.MyEmitterPool.OnParticleSystemFinished(NoAmmoFX); + NoAmmoFX = none; + } + + super.Destroyed(); +} + +simulated function CheckEnemiesWithinExplosionRadius() +{ + local KFPawn_Monster KFPM; + local Vector CheckExplosionLocation; + + CheckExplosionLocation = Location; + CheckExplosionLocation.z -= DeployHeight * 0.5f; + + if (CheckExplosionLocation.z < GroundLocation.z) + { + CheckExplosionLocation.z = GroundLocation.z; + } + + //DrawDebugSphere(CheckExplosionLocation, ExplosiveRadius, 10, 255, 255, 0 ); + + foreach CollidingActors(class'KFPawn_Monster', KFPM, ExplosiveRadius, CheckExplosionLocation, true,,) + { + if(KFPM != none && KFPM.IsAliveAndWell()) + { + SetTurretState(ETS_Detonate); + return; + } + } +} + +simulated function StartIdleAnim() +{ + if (TurretWeaponAttachment != none) + { + TurretWeaponAttachment.PlayIdleAnim(); + + FlyAkComponent.PlayEvent(DroneFlyingStartAudio, true, true); + } +} + +simulated event Tick(float DeltaTime) +{ + local rotator NewRotationRate; + + /** This gets reset somehow and causes issues with the rotation */ + RotationRate = NewRotationRate; + + if (bRotating) + { + UpdateRotation(DeltaTime); + } + + super.Tick(DeltaTime); +} + +simulated function UpdateRotation(float DeltaTime) +{ + local rotator RotationDiff; + local rotator RotationStep; + local rotator NewRotation; + local rotator RotationSpeed; + local int Sign; + + if (bRotating) + { + if (bRotatingByTime) /** Rotate in an amount of time */ + { + if(RotationAlpha < RotationTime) + { + RotationAlpha += DeltaTime; + + RotationStep = RLerp(RotationStart, TargetRotation, FMin(RotationAlpha / RotationTime, 1.0f), true); + RotationStep.Roll = 0.0f; + + RotationStep.Yaw = RotationStep.Yaw % 65536; + RotationStep.Pitch = RotationStep.Pitch % 65536; + + if (RotationStep.Yaw < 0) + RotationStep.Yaw += 65536; + + if (RotationStep.Pitch < 0) + RotationStep.Pitch += 65536; + + SetRotation(RotationStep); + } + else + { + bRotating = false; + } + } + else /** Rotate By Speed */ + { + RotationSpeed = CurrentState == ETS_Combat ? CombatRotationVel : AimRotationVel; + + RotationDiff = TargetRotation - Rotation; + + RotationDiff.Yaw = NormalizeRotAxis(RotationDiff.Yaw); + RotationDiff.Pitch = NormalizeRotAxis(RotationDiff.Pitch); + RotationDiff.Roll = NormalizeRotAxis(RotationDiff.Roll); + + Sign = RotationDiff.Yaw >= 0? 1 : -1; + RotationStep.Yaw = RotationSpeed.Yaw * DeltaTime * Sign; + if (Abs(RotationStep.Yaw) > Abs(RotationDiff.Yaw)) + { + RotationStep.Yaw = RotationDiff.Yaw; + } + + Sign = RotationDiff.Pitch >= 0? 1 : -1; + RotationStep.Pitch = RotationSpeed.Pitch * DeltaTime * Sign; + if (Abs(RotationStep.Pitch) > Abs(RotationDiff.Pitch)) + { + RotationStep.Pitch = RotationDiff.Pitch; + } + + RotationStep.Roll = 0.0f; + + NewRotation = Rotation + RotationStep; + NewRotation.Yaw = NewRotation.Yaw % 65536; + NewRotation.Pitch = NewRotation.Pitch % 65536; + + if (NewRotation.Yaw < 0) + NewRotation.Yaw += 65536; + + if (NewRotation.Pitch < 0) + NewRotation.Pitch += 65536; + + SetRotation(NewRotation); + + if (ReachedRotation()) + { + bRotating = false; + } + } + } +} + +simulated function RotateByTime(rotator NewRotation, float Time) +{ + if (NewRotation != Rotation) + { + RotationStart = Rotation; + TargetRotation = NewRotation; + RotationAlpha = 0.0; + RotationTime = Time; + bRotating = true; + bRotatingByTime = true; + } +} + +simulated function RotateBySpeed(rotator NewRotation) +{ + TargetRotation = NewRotation; + RotationAlpha = 0.0f; + RotationTime = 0.0f; + bRotating = true; + bRotatingByTime = false; +} + +simulated event byte ScriptGetTeamNum() +{ + return TeamNum; +} + +simulated function RequestRotation(rotator NewRotation) +{ + if (Role == ROLE_Authority) + { + /** Set rotation the same as its going to be replicated **/ + ReplicatedRotation.Yaw = ((NewRotation.Yaw >> 8) & 255) << 8; + ReplicatedRotation.Pitch = ((NewRotation.Pitch >> 8) & 255) << 8; + + bForceNetUpdate = true; + } + + RotateBySpeed(ReplicatedRotation); +} + +simulated function bool ReachedRotation(int DeltaError = 2000) +{ + local int YawDiff; + YawDiff = Abs((TargetRotation.Yaw & 65535) - (Rotation.Yaw & 65535)); + return (YawDiff < DeltaError) || (YawDiff > 65535 - DeltaError); +} + +event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser) +{ +} + +simulated function TakeRadiusDamage( + Controller InstigatedBy, + float BaseDamage, + float DamageRadius, + class DamageType, + float Momentum, + vector HurtOrigin, + bool bFullDamage, + Actor DamageCauser, + optional float DamageFalloffExponent=1.f, + optional bool bAdjustRadiusDamage=true +) +{} + +function bool CanAITargetThisPawn(Controller TargetingController) +{ + return false; +} + +simulated function bool CanInteractWithPawnGrapple() +{ + return false; +} + +simulated function bool CanInteractWithZoneVelocity() +{ + return false; +} + +simulated function UpdateTurretMeshMaterialColor(float Value) +{ + if (TurretWeaponAttachment == none) + { + return; + } + + if (TurretAttachMIC == none) + { + TurretAttachMIC = TurretWeaponAttachment.WeapMesh.CreateAndSetMaterialInstanceConstant(`AUTOTURRET_MIC_INDEX); + } + + if (TurretAttachMIC != none) + { + TurretAttachMIC.SetScalarParameterValue(TransitionParamName, 1.0f - Value); + TurretAttachMIC.SetScalarParameterValue(EmptyParamName, Value == 0.0f ? 1 : 0); + } +} + +/** No AutoAim */ +simulated function bool GetAutoTargetBones(out array WeakBones, out array NormalBones) +{ + return false; +} + +simulated function SetWeaponAmbientSound(AkEvent NewAmbientSound, optional AkEvent FirstPersonAmbientSound) +{ + super.SetWeaponAmbientSound(NewAmbientSound, FirstPersonAmbientSound); + + if (WorldInfo.NetMode == NM_DedicatedServer) + { + TurretWeaponAmbientSound = NewAmbientSound; + bNetDirty = true; + } +} + +/** + * This function's responsibility is to signal clients that non-instant hit shot + * has been fired. Call this on the server and local player. + * + * Network: Server and Local Player + */ +simulated function IncrementFlashCount(Weapon InWeapon, byte InFiringMode) +{ + Super.IncrementFlashCount(InWeapon, InFiringMode); + AutoTurretFlashCount = FlashCount; + // bNetDirty = true; + bForceNetUpdate = true; +} + +simulated function ClearFlashCount(Weapon InWeapon) +{ + Super.ClearFlashCount(InWeapon); + + AutoTurretFlashCount = FlashCount; + bForceNetUpdate=true; +} + +defaultproperties +{ + bCollideComplex=TRUE + Begin Object Name=CollisionCylinder + CollisionRadius=40 + CollisionHeight=30 + bIgnoreRadialImpulse=true + BlockNonZeroExtent=true + CollideActors=true + BlockActors=false + End Object + CylinderComponent=CollisionCylinder + + Begin Object Class=SkeletalMeshComponent Name=TurretMesh0 + SkeletalMesh=SkeletalMesh'wep_3p_hrg_warthog_mesh.drone_SM' + PhysicsAsset=PhysicsAsset'wep_3p_hrg_warthog_mesh.drone_SM_Physics' + BlockNonZeroExtent=false + CastShadow=false + bIgnoreRadialImpulse=true + + End Object + Components.Add(TurretMesh0) + TurretMesh=TurretMesh0 + Mesh=TurretMesh0 + + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=300 + DamageRadius=300 + DamageFalloffExponent=0.5f + DamageDelay=0.f + + // Damage Effects + MyDamageType=class'KFDT_Explosive_HRG_Warthog' + KnockDownStrength=0 + FractureMeshRadius=200.0 + FracturePartVel=500.0 + ExplosionEffects=KFImpactEffectInfo'WEP_HRG_Warthog_ARCH.HRG_Warthog_Explosion' + ExplosionSound=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Alt_Fire_3P' + + // Camera Shake + CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' + CamShakeInnerRadius=200 + CamShakeOuterRadius=900 + CamShakeFalloff=1.5f + bOrientCameraShakeTowardsEpicenter=true + + bIgnoreInstigator=true + ActorClassToIgnoreForDamage = class'KFPawn_Human' + End Object + ExplosionTemplate=ExploTemplate0 + + // sounds + Begin Object Class=AkComponent name=FlyAkComponent0 + BoneName=dummy + bStopWhenOwnerDestroyed=true + bForceOcclusionUpdateInterval=true + OcclusionUpdateInterval=0.2f + End Object + FlyAkComponent=FlyAkComponent0 + Components.Add(FlyAkComponent0) + + CurrentState=ETS_None + DeployZSpeed=800.0f + DeployHeight=200.0f + + deployUsingOffsetFromPlayerLocation=true + deployUsingOffsetZ = 100.f + + EffectiveRadius=1500.0f + ExplosiveRadius=100.0f + + WeaponDefinition=class'KFGame.KFWeapDef_HRG_WarthogWeapon' + + TimeBeforeRefreshingTargets=0.5f + TimeIdlingWhenSearching = 1.0f + MinIdlingVariation = -0.5f + MaxIdlingVariation = 1.0f + + // Rotation speed per second + AimRotationVel =(Pitch=16384,Yaw=32768,Roll=0) + CombatRotationVel =(Pitch=16384,Yaw=32768,Roll=0) + + RotationAlpha = 0.0f + RotationTime = 0.0f + bRotating = false + bHasSearchRandomLocation = false + + EmptyPitch=0 //-10000 + + bRunPhysicsWithNoController=true; + + bCanBeDamaged=false + TeamNum=0 + + bRotatingByTime=false + RemoteRole = ROLE_SimulatedProxy + + bCanDetonateOnProximityWithAmmo=false + bIgnoreForces=true + + bIsTurret=true + + NoAmmoFX=none + + bAlwaysRelevant=true + + AutoTurretFlashCount=0 + + DetonateNumberOfProjectiles=5 + DetonateMinimumDistance=200 // cm + DetonateMaximumDistance=700 // cm + DetonateMinTime=1f + DetonateMaxTime=1.4f + + DeployLastLocation=(X=-9999.f, Y=-9999.f, Z=-9999.f) + LastMoveExpectedSize= 0.f +} diff --git a/KFGameContent/Classes/KFPawn_ZedCrawler.uc b/KFGameContent/Classes/KFPawn_ZedCrawler.uc index 93506e0..2e2443b 100644 --- a/KFGameContent/Classes/KFPawn_ZedCrawler.uc +++ b/KFGameContent/Classes/KFPawn_ZedCrawler.uc @@ -391,6 +391,7 @@ defaultproperties // --------------------------------------------- // AI / Navigation + EliteAIType.Add(14); // AT_EliteCrawler ElitePawnClass.Add(class'KFPawn_ZedCrawlerKing') ControllerClass=class'KFAIController_ZedCrawler' bDebugCrawlerPhysics=false diff --git a/KFGameContent/Classes/KFPawn_ZedScrake.uc b/KFGameContent/Classes/KFPawn_ZedScrake.uc index bf50060..af395ee 100644 --- a/KFGameContent/Classes/KFPawn_ZedScrake.uc +++ b/KFGameContent/Classes/KFPawn_ZedScrake.uc @@ -124,10 +124,19 @@ simulated function CreateExhaustFx() event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser) { + local class KFDT; + super.TakeDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType, HitInfo, DamageCauser ); if( bCanRage && !bPlayedDeath && (GetHealthPercentage() < RageHealthThreshold || GetHeadHealthPercent() < RageHealthThreshold) ) { + KFDT = class(DamageType); + + if (KFDT != none && KFDT.default.bCanEnrage == false) + { + return; + } + SetEnraged( true ); } } diff --git a/KFGameContent/Classes/KFPhysicsVolume.uc b/KFGameContent/Classes/KFPhysicsVolume.uc index e1fb6f0..70e8958 100644 --- a/KFGameContent/Classes/KFPhysicsVolume.uc +++ b/KFGameContent/Classes/KFPhysicsVolume.uc @@ -35,6 +35,20 @@ function CausePainTo(Actor Other) } } +simulated event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal ) +{ + local KFPawn KFP; + + KFP = KFPawn(Other); + + if (KFP != none && KFP.CanInteractWithZoneVelocity() == false) + { + return; + } + + Super.Touch(Other, OtherComp, HitLocation, HitNormal); +} + simulated event UnTouch(Actor Other) { local int RecentHitIdx; diff --git a/KFGameContent/Classes/KFProj_Bolt_HRG_Crossboom.uc b/KFGameContent/Classes/KFProj_Bolt_HRG_Crossboom.uc index 84066ec..a53af4d 100644 --- a/KFGameContent/Classes/KFProj_Bolt_HRG_Crossboom.uc +++ b/KFGameContent/Classes/KFProj_Bolt_HRG_Crossboom.uc @@ -205,7 +205,7 @@ defaultproperties // explosion Begin Object Class=KFGameExplosion Name=ExploTemplate0 Damage=350 - DamageRadius=200 + DamageRadius=250 DamageFalloffExponent=1.f DamageDelay=0.f @@ -240,7 +240,7 @@ defaultproperties End Object StickHelper=StickHelper0 - SecondsBeforeDetonation=0.5f + SecondsBeforeDetonation=0.2f bIsProjActive=true bCanDisintegrate=true bAlwaysReplicateExplosion=true diff --git a/KFGameContent/Classes/KFProj_Bolt_HRG_CrossboomAlt.uc b/KFGameContent/Classes/KFProj_Bolt_HRG_CrossboomAlt.uc index 685c169..e5547c5 100644 --- a/KFGameContent/Classes/KFProj_Bolt_HRG_CrossboomAlt.uc +++ b/KFGameContent/Classes/KFProj_Bolt_HRG_CrossboomAlt.uc @@ -241,7 +241,7 @@ defaultproperties End Object StickHelper=StickHelper0 - SecondsBeforeDetonation=0.5f + SecondsBeforeDetonation=0.2f bIsProjActive=true bCanDisintegrate=true bAlwaysReplicateExplosion=true diff --git a/KFGameContent/Classes/KFProj_Bullet_Shotgun_S12_Alt.uc b/KFGameContent/Classes/KFProj_Bullet_Shotgun_S12_Alt.uc new file mode 100644 index 0000000..9bab005 --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_Shotgun_S12_Alt.uc @@ -0,0 +1,137 @@ +//============================================================================= +// KFProj_Bullet_Shotgun_S12_Alt +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_Shotgun_S12_Alt extends KFProj_BallisticExplosive + hidedropdown; + +var protected KFWeapon OwnerWeapon; + +/** Initialize the projectile */ +function Init( vector Direction ) +{ + super.Init( Direction ); + + OwnerWeapon = KFWeapon( Owner ); + if( OwnerWeapon != none ) + { + OwnerWeapon.LastPelletFireTime = WorldInfo.TimeSeconds; + } + + if (KFPawn(Instigator) != none) + { + // Explode right away + ForceExplode(); + } +} + +reliable client function ForceExplode() +{ + local vector Normal, MuzzleLocation; + + if (KFPawn(Instigator) != none) + { + Normal.X = 0.f; + Normal.Y = 0.f; + Normal.Z = 1.f; + + KFSkeletalMeshComponent(OwnerWeapon.Mesh).GetSocketWorldLocationAndRotation('MuzzleFlashAlt', MuzzleLocation); + + Explode(MuzzleLocation, Normal); + + Detonate(); + } +} + +simulated function bool CanDud() +{ + return false; +} + +/** Don't allow more than one pellet projectile to perform this check in a single frame */ +function bool ShouldWarnAIWhenFired() +{ + return super.ShouldWarnAIWhenFired() && OwnerWeapon != none && OwnerWeapon.LastPelletFireTime < WorldInfo.TimeSeconds; +} + +simulated protected function PrepareExplosionTemplate() +{ + super.PrepareExplosionTemplate(); + + /** Since bIgnoreInstigator is transient, its value must be defined here */ + ExplosionTemplate.bIgnoreInstigator = true; +} + +simulated function AdjustCanDisintigrate() {} + +/** Can be overridden in subclasses to exclude specific projectiles from nuking */ +simulated function bool AllowNuke() +{ + return false; +} + +defaultproperties +{ + ArmDistSquared=0.f + + MaxSpeed=0.0 + Speed=0.0 + + DamageRadius=0 + + // Grenade explosion light + Begin Object Class=PointLightComponent Name=ExplosionPointLight + LightColor=(R=252,G=218,B=171,A=255) + Brightness=0.5f + Radius=400.f + FalloffExponent=10.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=False + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE) + End Object + + // explosion + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=200 + DamageRadius=800 + DamageFalloffExponent=0.f + DamageDelay=0.f + MomentumTransferScale=10000 + bAlwaysFullDamage=true + bDoCylinderCheck=true + + // Damage Effects + MyDamageType=class'KFDT_Explosive_Shotgun_S12' + KnockDownStrength=150 + FractureMeshRadius=200.0 + FracturePartVel=500.0 + + ExplosionSound=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Alt_Fire_1P' + ExplosionEffects=KFImpactEffectInfo'WEP_Saiga12_ARCH.WEP_Saiga12_Impacts' + + // Dynamic Light + ExploLight=ExplosionPointLight + ExploLightStartFadeOutTime=0.0 + ExploLightFadeOutTime=0.3 + + bIgnoreInstigator=true + ActorClassToIgnoreForDamage = class'KFPawn_Human' + + // Camera Shake + CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' + CamShakeInnerRadius=0 + CamShakeOuterRadius=300 + CamShakeFalloff=1.5f + bOrientCameraShakeTowardsEpicenter=true + End Object + ExplosionTemplate=ExploTemplate0 +} + diff --git a/KFGameContent/Classes/KFProj_Explosive_HRG_Kaboomstick.uc b/KFGameContent/Classes/KFProj_Explosive_HRG_Kaboomstick.uc index dc8f954..c7d5748 100644 --- a/KFGameContent/Classes/KFProj_Explosive_HRG_Kaboomstick.uc +++ b/KFGameContent/Classes/KFProj_Explosive_HRG_Kaboomstick.uc @@ -14,6 +14,8 @@ class KFProj_Explosive_HRG_Kaboomstick extends KFProj_BallisticExplosive /** Cached reference to owner weapon */ var protected KFWeapon OwnerWeapon; +var bool bCanNuke; + /** Initialize the projectile */ function Init( vector Direction ) { @@ -37,25 +39,9 @@ function bool ShouldWarnAIWhenFired() */ simulated protected function PrepareExplosionTemplate() { - local KFPawn KFP; - local KFPerk CurrentPerk; - ExplosionTemplate.bIgnoreInstigator = true; super.PrepareExplosionTemplate(); - - if( ExplosionActorClass == class'KFPerk_Demolitionist'.static.GetNukeExplosionActorClass() ) - { - KFP = KFPawn( Instigator ); - if( KFP != none ) - { - CurrentPerk = KFP.GetPerk(); - if( CurrentPerk != none ) - { - CurrentPerk.SetLastHX25NukeTime( WorldInfo.TimeSeconds ); - } - } - } } simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallComp) @@ -76,20 +62,7 @@ simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallCom /** Only allow this projectile to cause a nuke if there hasn't been another nuke very recently */ simulated function bool AllowNuke() { - local KFPawn KFP; - local KFPerk CurrentPerk; - - KFP = KFPawn( Instigator ); - if( KFP != none ) - { - CurrentPerk = KFP.GetPerk(); - if( CurrentPerk != none && `TimeSince(CurrentPerk.GetLastHX25NukeTime()) < 0.25f ) - { - return false; - } - } - - return super.AllowNuke(); + return bCanNuke; } defaultproperties @@ -164,4 +137,6 @@ defaultproperties AmbientSoundPlayEvent=none AmbientSoundStopEvent=none + + bCanNuke = true } diff --git a/KFGameContent/Classes/KFProj_HRG_Locust.uc b/KFGameContent/Classes/KFProj_HRG_Locust.uc index 85f0a45..7906f04 100644 --- a/KFGameContent/Classes/KFProj_HRG_Locust.uc +++ b/KFGameContent/Classes/KFProj_HRG_Locust.uc @@ -137,7 +137,7 @@ defaultproperties // explosion Begin Object Class=KFGameExplosion Name=ExploTemplate0 - Damage=60 + Damage=45 DamageRadius=200 DamageFalloffExponent=0.5f DamageDelay=0.f diff --git a/KFGameContent/Classes/KFProj_HighExplosive_HRG_Warthog.uc b/KFGameContent/Classes/KFProj_HighExplosive_HRG_Warthog.uc new file mode 100644 index 0000000..bb89aaf --- /dev/null +++ b/KFGameContent/Classes/KFProj_HighExplosive_HRG_Warthog.uc @@ -0,0 +1,422 @@ +//============================================================================= +// KFProj_HighExplosive_HRG_Warthog +//============================================================================= +// High explosive grenade launcher grenade for the HRG Warthog +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFProj_HighExplosive_HRG_Warthog extends KFProj_BallisticExplosive + hidedropdown; + +var transient bool bHitWall; + +simulated event PreBeginPlay() +{ + local KFPawn InstigatorPawn; + local KFPawn_HRG_Warthog InstigatorPawnWarthog; + + InstigatorPawnWarthog = KFPawn_HRG_Warthog(Instigator); + if (InstigatorPawnWarthog != none) + { + InstigatorPawn = KFPawn(InstigatorPawnWarthog.OwnerWeapon.Instigator); + + if (InstigatorPawn != none) + { + Instigator = InstigatorPawn; + } + } + + Super.PreBeginPlay(); +} + +simulated function bool AllowNuke() +{ + return true; +} + +simulated function bool AllowDemolitionistConcussive() +{ + return true; +} + +simulated function bool AllowDemolitionistExplosionChangeRadius() +{ + return true; +} + +// Used by Demolitionist Nuke and Mad Bomber skills +simulated function bool CanDud() +{ + return false; +} + +simulated function SetupDetonationTimer(float FuseTime) +{ + if (FuseTime > 0) + { + bIsTimedExplosive = true; + SetTimer(FuseTime, false, 'ExplodeTimer'); + } +} + +function ExplodeTimer() +{ + local Actor HitActor; + local vector HitLocation, HitNormal; + + GetExplodeEffectLocation(HitLocation, HitNormal, HitActor); + + TriggerExplosion(HitLocation, HitNormal, HitActor); +} + +/** + * Trace down and get the location to spawn the explosion effects and decal + */ +simulated function GetExplodeEffectLocation(out vector HitLocation, out vector HitRotation, out Actor HitActor) +{ + local vector EffectStartTrace, EffectEndTrace; + local TraceHitInfo HitInfo; + + EffectStartTrace = Location + vect(0,0,1) * 4.f; + EffectEndTrace = EffectStartTrace - vect(0,0,1) * 32.f; + + // Find where to put the decal + HitActor = Trace(HitLocation, HitRotation, EffectEndTrace, EffectStartTrace, false,, HitInfo, TRACEFLAG_Bullet); + + // If the locations are zero (probably because this exploded in the air) set defaults + if( IsZero(HitLocation) ) + { + HitLocation = Location; + } + + if( IsZero(HitRotation) ) + { + HitRotation = vect(0,0,1); + } +} + +simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallComp) +{ + local Vector VNorm; + local rotator NewRotation; + local Vector Offset; + + bHitWall = true; + + if (bIsTimedExplosive) + { + // Reflect off Wall w/damping + VNorm = (Velocity dot HitNormal) * HitNormal; + + Velocity = -VNorm * DampenFactor + (Velocity - VNorm) * DampenFactorParallel; + + Speed = VSize(Velocity); + + if (WorldInfo.NetMode != NM_DedicatedServer && Pawn(Wall) == none) + { + // do the impact effects + `ImpactEffectManager.PlayImpactEffects(Location, Instigator, HitNormal, GrenadeBounceEffectInfo, true ); + } + + // if we hit a pawn or we are moving too slowly stop moving and lay down flat + if ( Speed < MinSpeedBeforeStop ) + { + ImpactedActor = Wall; + SetPhysics(PHYS_None); + + if( ProjEffects != none ) + { + ProjEffects.SetTranslation(LandedTranslationOffset); + } + + // Position the shell on the ground + RotationRate.Yaw = 0; + RotationRate.Pitch = 0; + RotationRate.Roll = 0; + NewRotation = Rotation; + NewRotation.Pitch = 0; + if(ResetRotationOnStop) + { + SetRotation(NewRotation); + } + Offset.Z = LandedTranslationOffset.X; + SetLocation(Location + Offset); + } + + return; + } + + if( WorldInfo.NetMode == NM_Standalone || + (WorldInfo.NetMode == NM_ListenServer && Instigator != none && Instigator.IsLocallyControlled()) ) + { + TriggerExplosion(Location, HitNormal, None); + Shutdown(); // cleanup/destroy projectile + return; + } + + if( Owner != none && KFWeapon( Owner ) != none && Instigator != none ) + { + if( Instigator.Role == ROLE_Authority) + { + KFWeap_HRG_WarthogWeapon(Owner).ForceExplosionReplicateKill(Location, self); + } + } + + StopSimulating(); // cleanup/destroy projectile +} + +simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal) +{ + // If we collided with a Siren shield, let the shield code handle touches + if( Other.IsA('KFTrigger_SirenProjectileShield') ) + { + return; + } + + if ( !bCollideWithTeammates && Pawn(Other) != None ) + { + // Don't hit teammates + if( Other.GetTeamNum() == GetTeamNum() ) + { + return; + } + } + + // Process impact hits + if (Other != Instigator && !Other.bStatic) + { + ProcessBulletTouch(Other, HitLocation, HitNormal); + } + + if (bIsTimedExplosive) + { + return; + } + + if( WorldInfo.NetMode == NM_Standalone || + (WorldInfo.NetMode == NM_ListenServer && Instigator != none && Instigator.IsLocallyControlled()) ) + { + // Call KFProjectile base code.. as we don't want to repeat the KFProj_BallisticExplosive base again + TriggerExplosion(HitLocation, HitNormal, Other); + Shutdown(); // cleanup/destroy projectile + return; + } + + if( Owner != none && KFWeapon( Owner ) != none && Instigator != none ) + { + // Special case, for some very complicate reason around replication, instigators and the Warthog pawn + // This code on the base class was never triggered on Clients: (IsLocallyControlled() always fails, also on the rightful owner of he projectile) + // if( Instigator.Role < ROLE_Authority && Instigator.IsLocallyControlled() ) + // Hence we call here a reliable client function on the Owner + // That will call exactly the same code that's inside that If on the base class of this file + + if( Instigator.Role == ROLE_Authority) + { + KFWeap_HRG_WarthogWeapon(Owner).ForceExplosionReplicate(Other, HitLocation, HitNormal, self); + } + } + + StopSimulating(); +} + +simulated event Tick(float DeltaTime) +{ + Super.Tick(DeltaTime); + + if (WorldInfo.NetMode != NM_DedicatedServer) + { + if (bHitWall == false) + { + SetRotation(rotator(Normal(Velocity))); + } + } +} + +/** This will cause the projectile to move to the Original Spawn location when first replicated. This solves the issue of the projectile spawning some distance away from the player when first replicated */ +simulated function SyncOriginalLocation() +{ + local vector MuzzleLocation, PredictedHitLocation, AimDir, EndTrace, MuzzleToPredictedHit; + + if ( WorldInfo.NetMode == NM_DedicatedServer ) + { + return; + } + + // For remote pawns, have the projectile look like its actually + // coming from the muzzle of the third person weapon + if( bSyncToThirdPersonMuzzleLocation && Instigator != none + && !Instigator.IsFirstPerson() && KFPawn(Owner) != none + && KFPawn(Owner).WeaponAttachment != none ) + { + MuzzleLocation = KFPawn(Owner).WeaponAttachment.GetMuzzleLocation(bFiredFromLeftHandWeapon ? 1 : 0); + + // Set the aim direction to the vector along the line where the + // projectile would hit based on velocity. This is the most accurate + if( !IsZero(Velocity) ) + { + AimDir = Normal(Velocity); + } + + // Set the aim direction to the vector along the line where the + // projectile would hit based on where it has moved away from + // the original location + if( IsZero(Velocity) ) + { + AimDir = Normal(Location-OriginalLocation); + } + + // Use the rotation if the location calcs give a zero direction + if( IsZero(AimDir) ) + { + AimDir = Normal(Vector(Rotation)); + } + + if( Location != MuzzleLocation ) + { + // if projectile is spawned at different location than the third + // person muzzle location, then simulate an instant trace where + // the projectile would hit + EndTrace = Location + AimDir * 16384; + PredictedHitLocation = GetPredictedHitLocation(Location, EndTrace); + + MuzzleToPredictedHit = Normal(PredictedHitLocation - MuzzleLocation); + + // only adjust AimDir if PredictedHitLocation is "forward" (i.e. don't let projectile fire back towards the shooter) + //@todo: still need to make this less wonky (can still shoot straight up sometimes when using long weapons, like the sawblade shooter) + if( MuzzleToPredictedHit dot vector(Rotation) > 0.f ) + { + // Then we realign projectile aim direction to match where the projectile would hit. + AimDir = MuzzleToPredictedHit; + } + } + + // Move the projectile to the MuzzleLocation + SetLocation(MuzzleLocation); + + // If the Velocity is zero (usually because the projectile impacted + // something on the server in its first tick before replicating) + // then turn its phyics and collion back on + if( IsZero(Velocity) ) + { + SetPhysics(default.Physics); + SetCollision( default.bCollideActors, default.bBlockActors ); + } + + // Adjust the velocity of the projectile so it will hit where + // it is supposed to + Velocity = Speed * Normal(AimDir); + } + // set location based on 'OriginalLocation' + else if ( Role < ROLE_Authority ) + { + // If the Velocity is zero (usually because the projectile impacted + // something on the server in its first tick before replicating) + // then turn its physics and collion back on and give it velocity + // again so the simulation will work properly on the client + if( IsZero(Velocity) ) + { + SetPhysics(default.Physics); + + // Set the aim direction to the vector along the line where the + // projectile would hit + AimDir = Normal(Location-OriginalLocation); + + // Use the rotation if the location calcs give a zero direction + if( IsZero(AimDir) ) + { + AimDir = Vector(Rotation); + } + + Velocity = Speed * AimDir; + SetCollision( default.bCollideActors, default.bBlockActors ); + } + + SetLocation(OriginalLocation); + } +} + + +defaultproperties +{ + bCollideWithTeammates = false + + Physics=PHYS_Falling + Speed=2000 + MaxSpeed=2000 + TerminalVelocity=2000 + TossZ=0 + GravityScale=1.0 + MomentumTransfer=50000.0 + ArmDistSquared=0 + LifeSpan=25.0f + + bIsTimedExplosive = false + + bWarnAIWhenFired=true + + ProjFlightTemplate=ParticleSystem'WEP_HRG_Warthog_EMIT.FX_HRG_Warthog_Projectile' + ProjFlightTemplateZedTime=ParticleSystem'WEP_HRG_Warthog_EMIT.FX_HRG_Warthog_Projectile_ZEDTIME' + ProjDudTemplate=ParticleSystem'WEP_HRG_Warthog_EMIT.FX_HRG_Warthog_Projectile_Dud' + GrenadeBounceEffectInfo=KFImpactEffectInfo'FX_Impacts_ARCH.DefaultGrenadeImpacts' + ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01' + AltExploEffects=KFImpactEffectInfo'WEP_HRG_Warthog_ARCH.HRG_Warthog_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 + + ExplosionActorClass=class'KFExplosion_HRG_Warthog' + + // explosion + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=35 + DamageRadius=200 + DamageFalloffExponent=1 + DamageDelay=0.f + + // Damage Effects + MyDamageType=class'KFDT_Explosive_HRG_Warthog_HighExplosive' + KnockDownStrength=0 + FractureMeshRadius=200.0 + FracturePartVel=500.0 + ExplosionEffects=KFImpactEffectInfo'WEP_HRG_WarthogWeapon_ARCH.HRG_WarthogWeapon_GrenadeExplosion' + ExplosionSound=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Explosion' + + // Dynamic Light + ExploLight=ExplosionPointLight + ExploLightStartFadeOutTime=0.0 + ExploLightFadeOutTime=0.2 + + // Camera Shake + CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' + CamShakeInnerRadius=200 + CamShakeOuterRadius=900 + CamShakeFalloff=1.5f + bOrientCameraShakeTowardsEpicenter=true + bIgnoreInstigator=true + ActorClassToIgnoreForDamage = class'KFPawn_Human' + End Object + ExplosionTemplate=ExploTemplate0 + + AmbientSoundPlayEvent=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Projectile_LP' + AmbientSoundStopEvent=AkEvent'WW_WEP_HRG_Warthog.Stop_WEP_HRG_Warthog_Projectile' + + // The higher the more bouncing + DampenFactor=0.4f + DampenFactorParallel=0.4f + + bHitWall = false +} + diff --git a/KFGameContent/Classes/KFProj_Rocket_HRG_MedicMissile.uc b/KFGameContent/Classes/KFProj_Rocket_HRG_MedicMissile.uc index 68720a8..04cfeee 100644 --- a/KFGameContent/Classes/KFProj_Rocket_HRG_MedicMissile.uc +++ b/KFGameContent/Classes/KFProj_Rocket_HRG_MedicMissile.uc @@ -83,7 +83,7 @@ defaultproperties AmbientSoundPlayEvent=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_Projectile_Loop' AmbientSoundStopEvent=AkEvent'WW_WEP_HRG_MedicMissile.Stop_WEP_HRG_MedicMissile_Projectile_Loop' - AltExploEffects=KFImpactEffectInfo'WEP_HRG_MedicMissile_ARCH.HRG_MedicMissile_Explosion_Concussive_Force' + AltExploEffects=KFImpactEffectInfo'WEP_HRG_MedicMissile_ARCH.HRG_MedicMissile_Explosion' ExplosionActorClass=class'KFExplosion_HRG_MedicMissile' diff --git a/KFGameContent/Classes/KFSeasonalEventStats_Summer2023.uc b/KFGameContent/Classes/KFSeasonalEventStats_Summer2023.uc new file mode 100644 index 0000000..4f9dfb7 --- /dev/null +++ b/KFGameContent/Classes/KFSeasonalEventStats_Summer2023.uc @@ -0,0 +1,170 @@ +//============================================================================= +// KFSeasonalEventStats_Summer2023 +//============================================================================= +// Tracks event-specific challenges/accomplishments for Summer 2023 +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFSeasonalEventStats_Summer2023 extends KFSeasonalEventStats; + +var transient private const int HRGBombardierZedsRequired, EMPRequired, StandYourGroundRequired, EndlessWaveRequired, SummerEventIndex; + +private event Initialize(string MapName) +{ + local string CapsMapName; + CapsMapName = Caps(MapName); + + bObjectiveIsValidForMap[0] = 1; // Kill 1500 Zeds with HRG Bombardier + bObjectiveIsValidForMap[1] = 0; // Complete the Weekly on Subduction + bObjectiveIsValidForMap[2] = 1; // Stun 2500 Zeds with EMP affliction + bObjectiveIsValidForMap[3] = 1; // Complete 25 Stand your Ground objectives + bObjectiveIsValidForMap[4] = 0; // Complete wave 15 on Endless Hard or higher difficulty on Subduction + + if (CapsMapName == "KF-SUBDUCTION") + { + bObjectiveIsValidForMap[1] = 1; + bObjectiveIsValidForMap[4] = 1; + } + + SetSeasonalEventStatsMax(HRGBombardierZedsRequired, 0, EMPRequired, StandYourGroundRequired, 0); +} + +private event GrantEventItems() +{ + if (Outer.IsEventObjectiveComplete(0) && + Outer.IsEventObjectiveComplete(1) && + Outer.IsEventObjectiveComplete(2) && + Outer.IsEventObjectiveComplete(3) && + Outer.IsEventObjectiveComplete(4)) + { + // TODO + GrantEventItem(9672); + } +} + +simulated event OnGameWon(class GameClass, int Difficulty, int GameLength, bool bCoOp) +{ + local int ObjIdx; + + // Complete the Weekly on Subduction + ObjIdx = 1; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (GameClass == class'KFGameInfo_WeeklySurvival') + { + FinishedObjective(SummerEventIndex, ObjIdx); + } + } +} + +simulated function OnTryCompleteObjective(int ObjectiveIndex, int EventIndex) +{ + local int HRGBombardierIdx, EMPIdx, StandYourGroundIdx, ObjectiveLimit; + local bool bValidIdx; + + HRGBombardierIdx = 0; + EMPIdx = 2; + StandYourGroundIdx = 3; + + bValidIdx = false; + + if (EventIndex == SummerEventIndex) + { + if (ObjectiveIndex == HRGBombardierIdx) + { + ObjectiveLimit = HRGBombardierZedsRequired; + bValidIdx = true; + } + else if (ObjectiveIndex == EMPIdx) + { + ObjectiveLimit = EMPRequired; + bValidIdx = true; + } + else if (ObjectiveIndex == StandYourGroundIdx) + { + ObjectiveLimit = StandYourGroundRequired; + bValidIdx = true; + } + + if (bValidIdx && bObjectiveIsValidForMap[ObjectiveIndex] != 0) + { + IncrementSeasonalEventStat(ObjectiveIndex, 1); + + if (Outer.GetSeasonalEventStatValue(ObjectiveIndex) >= ObjectiveLimit) + { + FinishedObjective(SummerEventIndex, ObjectiveIndex); + } + } + } +} + +simulated event OnWaveCompleted(class GameClass, int Difficulty, int WaveNum) +{ + local int ObjIdx; + + // Complete wave 15 on Endless Hard or higher difficulty on Subduction + ObjIdx = 4; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (WaveNum >= EndlessWaveRequired && GameClass == class'KFGameInfo_Endless' && Difficulty >= `DIFFICULTY_HARD) + { + FinishedObjective(SummerEventIndex, ObjIdx); + } + } +} + +simulated function OnAfflictionCaused(EAfflictionType Type) +{ + local int ObjIdx; + + // Stun 2500 Zeds with EMP affliction + ObjIdx = 2; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (Type == AF_EMP) + { + IncrementSeasonalEventStat(ObjIdx, 1); + + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= EMPRequired) + { + FinishedObjective(SummerEventIndex, ObjIdx); + } + } + } +} + +simulated function OnZedKilled(class MonsterClass, int Difficulty, class DT) +{ + local int ObjIdx; + + // Kill 1500 Zeds with HRG Bombardier + ObjIdx = 0; + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (DT == class'KFDT_Explosive_HRG_Warthog' || + DT == class'KFDT_Explosive_HRG_Warthog_HighExplosive' || + DT == class'KFDT_Ballistic_HRG_Warthog') + { + IncrementSeasonalEventStat(ObjIdx, 1); + + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= HRGBombardierZedsRequired) + { + FinishedObjective(SummerEventIndex, ObjIdx); + } + } + } +} + + +defaultproperties +{ + HRGBombardierZedsRequired=1500 + EMPRequired=2500 + StandYourGroundRequired=25 + EndlessWaveRequired=15 + SummerEventIndex=SEI_Summer +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_HRG_Warthog.uc b/KFGameContent/Classes/KFWeapAttach_HRG_Warthog.uc new file mode 100644 index 0000000..8cffa7d --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_HRG_Warthog.uc @@ -0,0 +1,113 @@ +//============================================================================= +// KFWeapAttach_HRG_Warthog +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeapAttach_HRG_Warthog extends KFWeapAttach_DualBase; + +`define AUTOTURRET_ATTACH_MIC_LED_INDEX 2 + +const ThrowAnim = 'Drone_Throw'; +const CrouchThrowAnim = 'Drone_Throw_CH'; + +const DetonateAnim = 'Shoot'; +const CrouchDetonateAnim = 'CH_Shoot'; + +const TransitionParamName = 'transition_full_to_empty'; +const EmptyParamName = 'Blinking_0_off___1_on'; + +/** Completely overridden to play anims for both C4 firemodes (throw and detonate), also doesn't need to play effects */ +simulated function bool ThirdPersonFireEffects( vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte ) +{ + local float Duration; + + // Effects below this point are culled based on visibility and distance + if ( !ActorEffectIsRelevant(P, false, MaxFireEffectDistance) ) + { + return false; + } + + DecodeThirdPersonAnimRate( ThirdPersonAnimRateByte ); + + // Weapon shoot anims + if (P.FiringMode == 0) + { + // anim simply hides and unhides bone + Duration = WeapMesh.GetAnimLength( ThrowAnim ); + WeapMesh.PlayAnim( ThrowAnim, Duration / ThirdPersonAnimRate,, true ); + + // use timer to make sure bone gets un-hidden (in case anim gets interrupted) + SetTimer( 0.75f, false, nameof(UnHide) ); + } + else if (P.FiringMode == 5) + { + Duration = WeapMesh.GetAnimLength( DetonateAnim ); + LeftWeapMesh.PlayAnim( DetonateAnim, Duration / ThirdPersonAnimRate,, true ); + } + + // Additive character shoot anims + if ( !P.IsDoingSpecialMove() ) + { + if( P.FiringMode == 0 ) + { + if ( P.bIsCrouched ) + { + P.PlayBodyAnim(CrouchThrowAnim, EAS_CH_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + else + { + P.PlayBodyAnim(ThrowAnim, EAS_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + } + else if( P.FiringMode == 5 ) + { + if ( P.bIsCrouched ) + { + P.PlayBodyAnim(CrouchDetonateAnim, EAS_CH_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + else + { + P.PlayBodyAnim(DetonateAnim, EAS_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + } + } + + // prevent using "aiming" KFAnim_BlendByTargetingMode since we don't have/need the aim anims for C4 + P.LastWeaponFireTime = -1.f; + + return true; +} + +/** Unhides the C4 unit in hand (basically the same as the notify, but don't use the notify) */ +simulated function UnHide() +{ + if( WeapMesh != none ) + { + WeapMesh.UnHideBoneByName( 'RW_Weapon' ); + } +} + +/** Special event added for weap attachments. Free for use */ +function OnSpecialEvent(int Arg) +{ + local float Value; + Value = (Arg - 1) / 100.0f; + + if (Value >= 0) + { + if ( WeaponMIC == None && LeftWeapMesh != None ) + { + WeaponMIC = LeftWeapMesh.CreateAndSetMaterialInstanceConstant(`AUTOTURRET_ATTACH_MIC_LED_INDEX); + } + + WeaponMIC.SetScalarParameterValue(TransitionParamName, 1.0f - Value); + WeaponMIC.SetScalarParameterValue(EmptyParamName, Value == 0 ? 1 : 0); + } +} + +defaultproperties +{ + bHasLaserSight=false +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_HRG_WarthogWeap.uc b/KFGameContent/Classes/KFWeapAttach_HRG_WarthogWeap.uc new file mode 100644 index 0000000..03abab4 --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_HRG_WarthogWeap.uc @@ -0,0 +1,271 @@ +//============================================================================= +// KFWeapAttach_HRG_WarthogWeapon +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeapAttach_HRG_WarthogWeap extends KFWeaponAttachment; + +const DeployAnimName = 'Drone_Deploy'; +const DroneFireAnim = 'Drone_Shoot'; +const DroneEmptyStartAnim = 'Drone_Start_Empty'; +const DroneEmptyAnim = 'Drone_Empty'; +const DroneIdleAnim = 'Drone_Idle'; +const DroneClosedAnim = 'Drone_IdleClose'; + +const LaserSightSocketName = 'LaserSightSocket'; +const LaserColorParamName = '0blue_1red'; + +var transient MaterialInstanceConstant LaserDotMIC; +var transient MaterialInstanceConstant LaserBeamMIC; + +simulated function float PlayDeployAnim() +{ + local float Duration; + + Duration = WeapMesh.GetAnimLength( DeployAnimName ); + WeapMesh.PlayAnim( DeployAnimName, Duration / ThirdPersonAnimRate, false, true ); + + return Duration; +} + +simulated function PlayEmptyState() +{ + local float Duration; + + ClearTimer(nameof(PlayIdleAnim)); + + Duration = WeapMesh.GetAnimLength( DroneEmptyStartAnim ); + WeapMesh.PlayAnim( DroneEmptyStartAnim, Duration / ThirdPersonAnimRate, true, false); + + SetTimer(Duration, false, 'PlayEmptyAnim'); +} + +simulated function PlayEmptyAnim() +{ + local float Duration; + + Duration = WeapMesh.GetAnimLength( DroneEmptyAnim ); + WeapMesh.PlayAnim( DroneEmptyAnim, Duration / ThirdPersonAnimRate, true, false); + + if (LaserSight != none) + { + LaserSight.ChangeVisibility(false); + } +} + +simulated function PlayIdleAnim() +{ + local float Duration; + + Duration = WeapMesh.GetAnimLength( DroneIdleAnim ); + WeapMesh.PlayAnim( DroneIdleAnim, Duration / ThirdPersonAnimRate, true, false ); +} + +simulated function PlayCloseAnim() +{ + local float Duration; + + Duration = WeapMesh.GetAnimLength( DroneClosedAnim ); + WeapMesh.PlayAnim( DroneClosedAnim, Duration / ThirdPersonAnimRate, true, false ); +} + +/** + * Spawn all of the effects that will be seen in behindview/remote clients. This + * function is called from the pawn, and should only be called when on a remote client or + * if the local client is in a 3rd person mode. + * @return TRUE if the effect culling check passes +*/ +simulated function bool ThirdPersonFireEffects( vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte ) +{ + // local EAnimSlotStance AnimType; + + SpawnTracer(GetMuzzleLocation(), HitLocation); + + // Effects below this point are culled based on visibility and distance + if ( !ActorIsRelevant(self, false, MaxFireEffectDistance) ) + { + return false; + } + + DecodeThirdPersonAnimRate( ThirdPersonAnimRateByte ); + + // Weapon shoot anims + if( !bWeapMeshIsPawnMesh ) + { + PlayWeaponFireAnim(); + } + +/* + if( P.IsDoingSpecialMove() && P.SpecialMoves[P.SpecialMove].bAllowFireAnims ) + { + AnimType = EAS_Additive; + } + else + { + AnimType = EAS_FullBody; + } +*/ + // AnimType = EAS_FullBody; + + // Character shoot anims +/* + if ( AnimType == EAS_Additive ) + { + PlayPawnFireAnim( P, AnimType ); + + // interrupt other weapon action anims (e.g. Reload) + if( !P.IsDoingSpecialMove() ) + { + P.StopBodyAnim(P.bIsCrouched ? EAS_CH_UpperBody : EAS_UpperBody, 0.1f); + } + + if ( OnWeaponStateChanged != None ) + { + OnWeaponStateChanged(true); + } + } +*/ + +// Always DEFAULT_FIREMODE + CauseMuzzleFlash(0); + + return true; +} + +simulated function bool ActorIsRelevant(Actor EffectInstigator, bool bForceDedicated, optional float VisibleCullDistance=5000.0, optional float HiddenCullDistance=350.0 ) +{ + local PlayerController P; + local float DistSq; + local vector CameraLoc; + local rotator CameraRot; + + if ( EffectInstigator == None ) + { + return FALSE; + } + + // No local player, so only spawn on dedicated server if bForceDedicated + if ( WorldInfo.NetMode == NM_DedicatedServer ) + { + return bForceDedicated; + } + + if ( bForceDedicated && (WorldInfo.NetMode == NM_ListenServer) && (WorldInfo.Game.NumPlayers > 1) ) + { + // Is acting as server, so spawn effect if bForceDedicated + return TRUE; + } + + // Determine how far to the nearest local viewer + DistSq = 10000000000.0; + ForEach LocalPlayerControllers(class'PlayerController', P) + { + if ( P.GetViewTarget() == self ) + { + return true; + } + P.GetPlayerViewPoint(CameraLoc, CameraRot); + DistSq = FMin(DistSq, VSizeSq(Location - CameraLoc)*Square(P.LODDistanceFactor)); + } + + if ( DistSq > VisibleCullDistance*VisibleCullDistance ) + { + // never spawn beyond cull distance + return FALSE; + } + else if ( DistSq < HiddenCullDistance*HiddenCullDistance ) + { + // If close enough, always spawn even if hidden + return TRUE; + } + +/* This doesn't seem to be updating the render time, so ignore it */ + return TRUE; +} + +/** Plays fire animation on weapon mesh */ +simulated function PlayWeaponFireAnim() +{ + local float Duration; + local bool bAnimPlayed; + + Duration = WeapMesh.GetAnimLength( DroneFireAnim ); + + bAnimPlayed = WeapMesh.PlayAnim( DroneFireAnim, Duration / ThirdPersonAnimRate, false, false ); + + if (bAnimPlayed) + { + ClearTimer(nameof(PlayIdleAnim)); + SetTimer(Duration, false, nameof(PlayIdleAnim)); + } +} + +/** + Laser + */ + +simulated function AttachLaserSight() +{ + if ( WeapMesh != none && LaserSight == None && LaserSightArchetype != None ) + { + LaserSight = new(self) Class'KFLaserSightAttachment' (LaserSightArchetype); + LaserSight.AttachLaserSight(WeapMesh, false, LaserSightSocketName); + } +} + +simulated function UpdateLaserColor(bool bInCombat) +{ + if (LaserSight != none) + { + if (LaserDotMIC == none) + { + LaserDotMIC = LaserSight.LaserDotMeshComp.CreateAndSetMaterialInstanceConstant(0); + } + + if (LaserBeamMIC == none) + { + LaserBeamMIC = LaserSight.LaserBeamMeshComp.CreateAndSetMaterialInstanceConstant(0); + } + } + + if (LaserDotMIC != none) + { + LaserDotMIC.SetScalarParameterValue(LaserColorParamName, bInCombat ? 1 : 0); + } + + if (LaserBeamMIC != none) + { + LaserBeamMIC.SetScalarParameterValue(LaserColorParamName, bInCombat ? 1 : 0); + } +} + +/** + * Assign weapon skin to 3rd person mesh + */ +simulated event SetWeaponSkin(int ItemId, optional bool bFinishedLoading = false) +{ + local array SkinMICs; + + if ( ItemId > 0 && WorldInfo.NetMode != NM_DedicatedServer && !bWaitingForWeaponSkinLoad) + { + if (!bFinishedLoading && StartLoadWeaponSkin(ItemId)) + { + return; + } + + SkinMICs = class'KFWeaponSkinList'.static.GetWeaponSkin(ItemId, WST_ThirdPerson); + + if ( SkinMICs.Length > 0 ) + { + WeapMesh.SetMaterial(0, SkinMICs[0]); + } + } +} + +defaultproperties +{ + //defaults + bHasLaserSight=true +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_Shotgun_S12.uc b/KFGameContent/Classes/KFWeapAttach_Shotgun_S12.uc new file mode 100644 index 0000000..8ca0492 --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_Shotgun_S12.uc @@ -0,0 +1,153 @@ +//============================================================================= +// KFWeapAttach_shotgun_s12 +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeapAttach_Shotgun_S12 extends KFWeaponAttachment; + +const SecondaryMuzzleSocket = 'MuzzleFlashAlt'; +const SecondaryReloadAnim = 'Reload_Secondary'; +const SecondaryReloadCHAnim = 'Reload_Secondary_CH'; +const SecondaryReloadEliteAnim = 'Reload_Secondary_Elite'; +const SecondaryReloadEliteCHAnim = 'Reload_Secondary_Elite_CH'; + +var transient ParticleSystemComponent ExplosionPSC; +var transient ParticleSystem ExplosionEffect; +var protected transient KFMuzzleFlash SecondaryMuzzleFlash; +var protected transient bool bWasSecondaryShot; + +simulated function bool ThirdPersonFireEffects( vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte ) +{ + local vector SocketLocation; + + bWasSecondaryShot = P.FiringMode == 1; + + if (Super.ThirdPersonFireEffects(HitLocation, P, ThirdPersonAnimRateByte)) + { + if (P.FiringMode == 1 && !P.IsLocallyControlled()) + { + if (ExplosionEffect != None) + { + WeapMesh.GetSocketWorldLocationAndRotation('MuzzleFlashAlt', SocketLocation); + + ExplosionPSC = WorldInfo.MyEmitterPool.SpawnEmitter(class'KFWeap_Shotgun_S12'.default.ExplosionEffect, SocketLocation, rotator(vect(0,0,1))); + ExplosionPSC.ActivateSystem(); + } + } + return true; + } + return false; +} + +/** @return the starting location for effects (e.g. tracers) */ +simulated function vector GetMuzzleLocation(optional byte MuzzleID) +{ + if (bWasSecondaryShot) + { + return GetAltMuzzleLocation(); + } + else + { + return Super.GetMuzzleLocation(MuzzleID); + } +} + +simulated function vector GetAltMuzzleLocation(optional byte MuzzleID) +{ + local vector SocketLocation; + + if (SecondaryMuzzleFlash == None) + { + AttachMuzzleFlash(); + } + + if( SecondaryMuzzleFlash != none ) + { + WeapMesh.GetSocketWorldLocationAndRotation(SecondaryMuzzleFlash.GetSocketName(), SocketLocation); + return SocketLocation; + } + + return Super.GetMuzzleLocation(MuzzleID); +} + +simulated event Tick(float DeltaTime) +{ + local vector SocketLocation; + + Super.Tick(DeltaTime); + + if (ExplosionPSC != none && ExplosionPSC.bIsActive) + { + WeapMesh.GetSocketWorldLocationAndRotation('MuzzleFlashAlt', SocketLocation); + ExplosionPSC.SetVectorParameter('WeaponEndpoint', SocketLocation); + } +} + +simulated function CauseMuzzleFlash(byte FiringMode) +{ + if ( FiringMode == 1 ) // AltFire + { + if (SecondaryMuzzleFlash == None) + { + AttachMuzzleFlash(); + } + + if (SecondaryMuzzleFlash != None ) + { + SecondaryMuzzleFlash.CauseMuzzleFlash(FiringMode); + if ( SecondaryMuzzleFlash.bAutoActivateShellEject ) + { + SecondaryMuzzleFlash.CauseShellEject(); + } + } + } + else + { + Super.CauseMuzzleFlash(FiringMode); + } +} + +simulated function AttachMuzzleFlash() +{ + Super.AttachMuzzleFlash(); + + if ( WeapMesh != none && SecondaryMuzzleFlash == None ) + { + SecondaryMuzzleFlash = new(self) Class'KFMuzzleFlash'(class'KFWeap_Shotgun_S12'.default.SecondaryMuzzleFlashTemplate); + SecondaryMuzzleFlash.AttachMuzzleFlash(WeapMesh, SecondaryMuzzleSocket,); + } +} + +simulated function PlayReloadMagazineAnim(EWeaponState NewWeaponState, KFPawn P) +{ + local name AnimName; + + switch (NewWeaponState) + { + + case WEP_ReloadSecondary: + AnimName = (P.bIsCrouched) ? SecondaryReloadCHAnim : SecondaryReloadAnim; + break; + case WEP_ReloadSecondary_Elite: + AnimName = (P.bIsCrouched) ? SecondaryReloadEliteCHAnim : SecondaryReloadEliteAnim; + break; + } + + if (AnimName != '') + { + PlayCharacterMeshAnim(P, AnimName, true); + } + else + { + Super.PlayReloadMagazineAnim(NewWeaponState, P); + } +} + +defaultproperties +{ + ExplosionEffect=ParticleSystem'WEP_1P_Saiga12_EMIT.FX_Saiga12_Explosion_3P' + bWasSecondaryShot=false +} diff --git a/KFGameContent/Classes/KFWeap_AutoTurret.uc b/KFGameContent/Classes/KFWeap_AutoTurret.uc index 7f62937..eb5e89a 100644 --- a/KFGameContent/Classes/KFWeap_AutoTurret.uc +++ b/KFGameContent/Classes/KFWeap_AutoTurret.uc @@ -192,7 +192,14 @@ simulated function Detonate(optional bool bKeepTurret = false) continue; } - KFPawn_AutoTurret(TurretsCopy[i]).SetTurretState(ETS_Detonate); + if (KFPawn_HRG_Warthog(TurretsCopy[i]) != none) + { + KFPawn_HRG_Warthog(TurretsCopy[i]).SetTurretState(ETS_Detonate); + } + else if (KFPawn_Autoturret(TurretsCopy[i]) != none) + { + KFPawn_Autoturret(TurretsCopy[i]).SetTurretState(ETS_Detonate); + } } KFPC.DeployedTurrets.Remove(bKeepTurret ? 1 : 0, KFPC.DeployedTurrets.Length); @@ -231,6 +238,7 @@ function RemoveDeployedTurret( optional int Index = INDEX_NONE, optional Actor T function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon ) { local int i; + local Actor WeaponPawn; super.SetOriginalValuesFromPickup( PickedUpWeapon ); @@ -256,13 +264,24 @@ function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon ) for( i = 0; i < NumDeployedTurrets; ++i ) { - // charge alerts (beep, light) need current instigator - KFPC.DeployedTurrets[i].Instigator = Instigator; - KFPC.DeployedTurrets[i].SetOwner(self); + WeaponPawn = KFPC.DeployedTurrets[i]; + if (WeaponPawn != none) + { + // charge alerts (beep, light) need current instigator + WeaponPawn.Instigator = Instigator; + WeaponPawn.SetOwner(self); - if( Instigator.Controller != none ) - { - KFPawn_AutoTurret(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller; + if (Instigator.Controller != none) + { + if (KFPawn_HRG_Warthog(KFPC.DeployedTurrets[i]) != none) + { + KFPawn_HRG_Warthog(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller; + } + else if (KFPawn_Autoturret(KFPC.DeployedTurrets[i]) != none) + { + KFPawn_Autoturret(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller; + } + } } } } @@ -343,6 +362,8 @@ simulated function bool HasAmmo( byte FireModeNum, optional int Amount ) simulated function BeginFire( byte FireModeNum ) { + local bool bCanDetonate; + // Clear any pending detonate if we pressed the main fire // That prevents strange holding right click behaviour and sound issues if (FireModeNum == DEFAULT_FIREMODE) @@ -359,11 +380,21 @@ simulated function BeginFire( byte FireModeNum ) if (NumDeployedTurrets > 0 && bTurretReadyToUse) { - PrepareAndDetonate(); + bCanDetonate = NumDeployedTurrets > 0; + + if (bCanDetonate) + { + PrepareAndDetonate(); + } } } else { + if (KFPC != none) + { + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + } + if (FireModeNum == DEFAULT_FIREMODE && NumDeployedTurrets >= MaxTurretsDeployed && HasAnyAmmo()) @@ -573,7 +604,15 @@ function CheckTurretAmmo() if (KFPC.DeployedTurrets.Length > 0) { - Weapon = KFWeapon(KFPawn_AutoTurret(KFPC.DeployedTurrets[0]).Weapon); + if (KFPawn_HRG_Warthog(KFPC.DeployedTurrets[0]) != none) + { + Weapon = KFWeapon(KFPawn_HRG_Warthog(KFPC.DeployedTurrets[0]).Weapon); + } + else if (KFPawn_Autoturret(KFPC.DeployedTurrets[0]) != none) + { + Weapon = KFWeapon(KFPawn_Autoturret(KFPC.DeployedTurrets[0]).Weapon); + } + if (Weapon != none) { Percentage = float(Weapon.AmmoCount[0]) / Weapon.MagazineCapacity[0]; @@ -797,4 +836,5 @@ defaultproperties NumBloodMapMaterials=3 bDetonateLocked=false + CurrentAmmoPercentage=-1.0f } diff --git a/KFGameContent/Classes/KFWeap_Blunt_ChainBat.uc b/KFGameContent/Classes/KFWeap_Blunt_ChainBat.uc index 81edab7..b495dbd 100644 --- a/KFGameContent/Classes/KFWeap_Blunt_ChainBat.uc +++ b/KFGameContent/Classes/KFWeap_Blunt_ChainBat.uc @@ -45,13 +45,13 @@ defaultproperties End Object InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Bludgeon_ChainBat' - InstantHitDamage(DEFAULT_FIREMODE)=68// 34 + InstantHitDamage(DEFAULT_FIREMODE)=75// 34 InstantHitDamageTypes(HEAVY_ATK_FIREMODE)=class'KFDT_Bludgeon_ChainBatHeavy' - InstantHitDamage(HEAVY_ATK_FIREMODE)=90 //68 + InstantHitDamage(HEAVY_ATK_FIREMODE)=100 //68 InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_ChainBatBash' - InstantHitDamage(BASH_FIREMODE)=68 + InstantHitDamage(BASH_FIREMODE)=75 // Inventory GroupPriority=50 diff --git a/KFGameContent/Classes/KFWeap_Blunt_Crovel.uc b/KFGameContent/Classes/KFWeap_Blunt_Crovel.uc index 772b437..4879b4e 100644 --- a/KFGameContent/Classes/KFWeap_Blunt_Crovel.uc +++ b/KFGameContent/Classes/KFWeap_Blunt_Crovel.uc @@ -40,7 +40,7 @@ defaultproperties End Object // Inventory - GroupPriority=25 + GroupPriority=47 InventorySize=4 WeaponSelectTexture=Texture2D'ui_weaponselect_tex.UI_WeaponSelect_Crovel' diff --git a/KFGameContent/Classes/KFWeap_GravityImploder.uc b/KFGameContent/Classes/KFWeap_GravityImploder.uc index 3e1d34b..5c815e2 100644 --- a/KFGameContent/Classes/KFWeap_GravityImploder.uc +++ b/KFGameContent/Classes/KFWeap_GravityImploder.uc @@ -22,6 +22,11 @@ var bool bLastFireWasAlt; var const bool bDebugDrawVortex; +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Explosive; +} + simulated function Activate() { super.Activate(); diff --git a/KFGameContent/Classes/KFWeap_GrenadeLauncher_HX25.uc b/KFGameContent/Classes/KFWeap_GrenadeLauncher_HX25.uc index d1a47e7..a5caf95 100644 --- a/KFGameContent/Classes/KFWeap_GrenadeLauncher_HX25.uc +++ b/KFGameContent/Classes/KFWeap_GrenadeLauncher_HX25.uc @@ -15,6 +15,8 @@ var protected const array PelletSpread; /** Last time a submunition projectile was fired from this weapon */ var float LastSubmunitionFireTime; +var transient bool AlreadyIssuedCanNuke; + /********************************************************************************************* Firing / Projectile ********************************************************************************************* */ @@ -79,6 +81,38 @@ static simulated event EFilterTypeUI GetAltTraderFilter() return FT_Pistol; } +simulated function KFProjectile SpawnAllProjectiles(class KFProjClass, vector RealStartLoc, vector AimDir) +{ + local KFProjectile Proj; + + AlreadyIssuedCanNuke = false; + + Proj = Super.SpawnAllProjectiles(KFProjClass, RealStartLoc, AimDir); + + AlreadyIssuedCanNuke = false; + + return Proj; +} + +simulated function KFProjectile SpawnProjectile( class KFProjClass, vector RealStartLoc, vector AimDir ) +{ + local KFProj_ExplosiveSubMunition_HX25 Proj; + + Proj = KFProj_ExplosiveSubMunition_HX25(Super.SpawnProjectile(KFProjClass, RealStartLoc, AimDir)); + + if (AlreadyIssuedCanNuke == false) + { + Proj.bCanNuke = true; + AlreadyIssuedCanNuke = true; + } + else + { + Proj.bCanNuke = false; + } + + return Proj; +} + defaultproperties { ForceReloadTime=0.3f @@ -200,4 +234,6 @@ defaultproperties WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.4f), (Stat=EWUS_Weight, Add=2))) WeaponUpgrades[3]=(Stats=((Stat=EWUS_Damage0, Scale=1.6f), (Stat=EWUS_Weight, Add=3))) WeaponUpgrades[4]=(Stats=((Stat=EWUS_Damage0, Scale=1.9f), (Stat=EWUS_Weight, Add=4))) + + AlreadyIssuedCanNuke = false } \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_HRG_BallisticBouncer.uc b/KFGameContent/Classes/KFWeap_HRG_BallisticBouncer.uc index 0e25831..e9185eb 100644 --- a/KFGameContent/Classes/KFWeap_HRG_BallisticBouncer.uc +++ b/KFGameContent/Classes/KFWeap_HRG_BallisticBouncer.uc @@ -55,6 +55,11 @@ Replication ChargeTime; } +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Projectile; +} + simulated event PostInitAnimTree( SkeletalMeshComponent SkelComp ) { local vector vec; diff --git a/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc b/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc index afa003a..3750670 100644 --- a/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc +++ b/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc @@ -343,7 +343,7 @@ defaultproperties InstantHitDamage(ALTFIRE_FIREMODE)=0.0 //override by AfflictionHandler.GetBigHeadAfflictionDamageModifier() InstantHitDamageTypes(ALTFIRE_FIREMODE)=Class'KFDT_Blast_HRG_CranialPopper' FireInterval(ALTFIRE_FIREMODE)=+0.25 - AmmoCost(ALTFIRE_FIREMODE)=100 + AmmoCost(ALTFIRE_FIREMODE)=25 // BASH_FIREMODE InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_CranialPopper' diff --git a/KFGameContent/Classes/KFWeap_HRG_Warthog.uc b/KFGameContent/Classes/KFWeap_HRG_Warthog.uc new file mode 100644 index 0000000..0b9954a --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_Warthog.uc @@ -0,0 +1,844 @@ +//============================================================================= +// KFWeap_HRG_Warthog +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HRG_Warthog extends KFWeap_ThrownBase; + +`define AUTOTURRET_MIC_LED_INDEX 2 + +const DETONATE_FIREMODE = 5; // NEW - IronSights Key + +var(Animations) const editconst name DetonateAnim; +var(Animations) const editconst name DetonateLastAnim; + +/** Sound to play upon successful detonation */ +var() AkEvent DetonateAkEvent; + +/** Strenght applied to forward dir to get the throwing velocity */ +var const float ThrowStrength; +/** Max num of turrets that can be deployed */ +var const byte MaxTurretsDeployed; +/** Offset to spawn the dron (screen coordinates) */ +var const vector TurretSpawnOffset; + +var transient byte NumDeployedTurrets; +var transient KFPlayerController KFPC; + +/** If the turret is in a state available for throw another to fix some animation issues. */ +var transient bool bTurretReadyToUse; + +var repnotify float CurrentAmmoPercentage; + +const TransitionParamName = 'transition_full_to_empty'; +const EmptyParamName = 'Blinking_0_off___1_on'; + +var transient bool bDetonateLocked; + +replication +{ + if( bNetDirty ) + NumDeployedTurrets, CurrentAmmoPercentage, bTurretReadyToUse; +} + +simulated event ReplicatedEvent(name VarName) +{ + if (VarName == nameof(CurrentAmmoPercentage)) + { + UpdateMaterialColor(CurrentAmmoPercentage); + } + else + { + Super.ReplicatedEvent(VarName); + } +} + +simulated event PreBeginPlay() +{ + local class WeaponClass; + + super.PreBeginPlay(); + + WeaponClass = class (DynamicLoadObject(class'KFPawn_HRG_Warthog'.default.WeaponDefinition.default.WeaponClassPath, class'Class')); + WeaponClass.static.TriggerAsyncContentLoad(WeaponClass); +} + +simulated event PostBeginPlay() +{ + super.PostBeginPlay(); + + if (Role == ROLE_Authority) + { + KFPC = KFPlayerController(Instigator.Controller); + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + } +} + +/** Route ironsight player input to detonate */ +simulated function SetIronSights(bool bNewIronSights) +{ + if ( !Instigator.IsLocallyControlled() ) + { + return; + } + + if ( bNewIronSights ) + { + StartFire(DETONATE_FIREMODE); + } + else + { + StopFire(DETONATE_FIREMODE); + } +} + +/** Overridded to add spawned turret to list of spawned turrets */ +simulated function Projectile ProjectileFire() +{ + local vector SpawnLocation, SpawnDirection; + local KFPawn_HRG_Warthog SpawnedActor; + + if (Role != ROLE_Authority) + { + return none; + } + + if (CurrentFireMode == DEFAULT_FIREMODE) + { + GetTurretSpawnLocationAndDir(SpawnLocation, SpawnDirection); + SpawnedActor = Spawn(class'KFPawn_HRG_Warthog', self,, SpawnLocation + (TurretSpawnOffset >> Rotation), Rotation,,true); + + if( SpawnedActor != none ) + { + SpawnedActor.OwnerWeapon = self; + SpawnedActor.SetPhysics(PHYS_Falling); + SpawnedActor.Velocity = SpawnDirection * ThrowStrength; + SpawnedActor.UpdateInstigator(Instigator); + SpawnedActor.UpdateWeaponUpgrade(CurrentWeaponUpgradeIndex); + SpawnedActor.SetTurretState(ETS_Throw); + + if (KFPC != none) + { + KFPC.DeployedTurrets.AddItem( SpawnedActor ); + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + } + + bTurretReadyToUse = false; + bForceNetUpdate = true; + } + + return none; + } + + return super.ProjectileFire(); +} + +simulated function GetTurretSpawnLocationAndDir(out vector SpawnLocation, out vector SpawnDirection) +{ + local vector StartTrace, EndTrace, RealStartLoc, AimDir; + local ImpactInfo TestImpact; + local vector DirA, DirB; + local Quat Q; + + // This is where we would start an instant trace. (what CalcWeaponFire uses) + StartTrace = GetSafeStartTraceLocation(); + AimDir = Vector(GetAdjustedAim( StartTrace )); + + // this is the location where the projectile is spawned. + RealStartLoc = GetPhysicalFireStartLoc(AimDir); + + if( StartTrace != RealStartLoc ) + { + // if projectile is spawned at different location of crosshair, + // then simulate an instant trace where crosshair is aiming at, Get hit info. + EndTrace = StartTrace + AimDir * GetTraceRange(); + TestImpact = CalcWeaponFire( StartTrace, EndTrace ); + // Store the original aim direction without correction + DirB = AimDir; + + // Then we realign projectile aim direction to match where the crosshair did hit. + AimDir = Normal(TestImpact.HitLocation - RealStartLoc); + + // Store the desired corrected aim direction + DirA = AimDir; + + // Clamp the maximum aim adjustment for the AimDir so you don't get wierd + // cases where the projectiles velocity is going WAY off of where you + // are aiming. This can happen if you are really close to what you are + // shooting - Ramm + if ( (DirA dot DirB) < MaxAimAdjust_Cos ) + { + Q = QuatFromAxisAndAngle(Normal(DirB cross DirA), MaxAimAdjust_Angle); + AimDir = QuatRotateVector(Q,DirB); + } + } + + SpawnDirection = AimDir; + SpawnLocation = RealStartLoc; +} + +/** Detonates the oldest turret */ +simulated function Detonate(optional bool bKeepTurret = false) +{ + local int i; + local array TurretsCopy; + + // auto switch weapon when out of ammo and after detonating the last deployed turret + if( Role == ROLE_Authority ) + { + TurretsCopy = KFPC.DeployedTurrets; + for (i = 0; i < TurretsCopy.Length; i++) + { + if (bKeepTurret && i == 0) + { + continue; + } + + if (KFPawn_HRG_Warthog(TurretsCopy[i]) != none) + { + KFPawn_HRG_Warthog(TurretsCopy[i]).SetTurretState(ETS_Detonate); + } + else if (KFPawn_Autoturret(TurretsCopy[i]) != none) + { + KFPawn_Autoturret(TurretsCopy[i]).SetTurretState(ETS_Detonate); + } + } + + KFPC.DeployedTurrets.Remove(bKeepTurret ? 1 : 0, KFPC.DeployedTurrets.Length); + + SetReadyToUse(true); + + if( !HasAnyAmmo() && NumDeployedTurrets == 0 ) + { + if( CanSwitchWeapons() ) + { + Instigator.Controller.ClientSwitchToBestWeapon(false); + } + } + } +} + +/** Removes a turret from the list using either an index or an actor and updates NumDeployedTurrets */ +function RemoveDeployedTurret( optional int Index = INDEX_NONE, optional Actor TurretActor ) +{ + if( Index == INDEX_NONE ) + { + if( TurretActor != none ) + { + Index = KFPC.DeployedTurrets.Find( TurretActor ); + } + } + + if( Index != INDEX_NONE ) + { + KFPC.DeployedTurrets.Remove( Index, 1 ); + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + bForceNetUpdate = true; + } +} + +function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon ) +{ + local int i; + local Actor WeaponPawn; + + super.SetOriginalValuesFromPickup( PickedUpWeapon ); + + if (PickedUpWeapon.KFPlayer != none && PickedUpWeapon.KFPlayer != KFPC) + { + for (i = 0; i < PickedUpWeapon.KFPlayer.DeployedTurrets.Length; i++) + { + KFPC.DeployedTurrets.AddItem(PickedUpWeapon.KFPlayer.DeployedTurrets[i]); + } + + PickedUpWeapon.KFPlayer.DeployedTurrets.Remove(0, PickedUpWeapon.KFPlayer.DeployedTurrets.Length); + } + + if (KFPC.DeployedTurrets.Length > 1) + { + Detonate(true); + } + + PickedUpWeapon.KFPlayer = none; + + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + bForceNetUpdate = true; + + for( i = 0; i < NumDeployedTurrets; ++i ) + { + WeaponPawn = KFPC.DeployedTurrets[i]; + if (WeaponPawn != none) + { + // charge alerts (beep, light) need current instigator + WeaponPawn.Instigator = Instigator; + WeaponPawn.SetOwner(self); + + if (Instigator.Controller != none) + { + if (KFPawn_HRG_Warthog(KFPC.DeployedTurrets[i]) != none) + { + KFPawn_HRG_Warthog(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller; + } + else if (KFPawn_Autoturret(KFPC.DeployedTurrets[i]) != none) + { + KFPawn_Autoturret(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller; + } + } + } + } +} + + +/** + * Drop this item out in to the world + */ +function DropFrom(vector StartLocation, vector StartVelocity) +{ + local DroppedPickup P; + + // Offset spawn closer to eye location + StartLocation.Z += Instigator.BaseEyeHeight / 2; + + // for some reason, Inventory::DropFrom removes weapon from inventory whether it was able to spawn the pickup or not. + // we only want the weapon removed from inventory if pickup was successfully spawned, so instead of calling the supers, + // do all the super functionality here. + + if( !CanThrow() ) + { + return; + } + + if( DroppedPickupClass == None || DroppedPickupMesh == None ) + { + Destroy(); + return; + } + + // the last bool param is to prevent collision from preventing spawns + P = Spawn(DroppedPickupClass,,, StartLocation,,,true); + if( P == None ) + { + // if we can't spawn the pickup (likely for collision reasons), + // just return without removing from inventory or destroying, which removes from inventory + PlayerController(Instigator.Controller).ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_FailedDropInventory ); + return; + } + + if( Instigator != None && Instigator.InvManager != None ) + { + Instigator.InvManager.RemoveFromInventory(Self); + + if( Instigator.IsAliveAndWell() && !Instigator.InvManager.bPendingDelete ) + { + `DialogManager.PlayDropWeaponDialog( KFPawn(Instigator) ); + } + } + + SetupDroppedPickup( P, StartVelocity ); + + KFDroppedPickup(P).PreviousOwner = KFPlayerController(Instigator.Controller); + + Instigator = None; + GotoState(''); + + AIController = None; +} + + /** + * Returns true if this weapon uses a secondary ammo pool + */ +static simulated event bool UsesAmmo() +{ + return true; +} + +simulated function bool HasAmmo( byte FireModeNum, optional int Amount ) +{ + if( FireModeNum == DETONATE_FIREMODE ) + { + return NumDeployedTurrets > 0; + } + + return super.HasAmmo( FireModeNum, Amount ); +} + +simulated function BeginFire( byte FireModeNum ) +{ + local bool bCanDetonate; + + // Clear any pending detonate if we pressed the main fire + // That prevents strange holding right click behaviour and sound issues + if (FireModeNum == DEFAULT_FIREMODE) + { + ClearPendingFire(DETONATE_FIREMODE); + } + + if (FireModeNum == DETONATE_FIREMODE ) + { + if (bDetonateLocked) + { + return; + } + + if (NumDeployedTurrets > 0 && bTurretReadyToUse) + { + bCanDetonate = NumDeployedTurrets > 0; + + if (bCanDetonate) + { + PrepareAndDetonate(); + } + } + } + else + { + if (KFPC != none) + { + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + } + + if (FireModeNum == DEFAULT_FIREMODE + && NumDeployedTurrets >= MaxTurretsDeployed + && HasAnyAmmo()) + { + if (!bTurretReadyToUse) + { + return; + } + + PrepareAndDetonate(); + } + + super.BeginFire( FireModeNum ); + } +} + +simulated function PrepareAndDetonate() +{ + local name DetonateAnimName; + local float AnimDuration; + local bool bInSprintState; + DetonateAnimName = ShouldPlayLastAnims() ? DetonateLastAnim : DetonateAnim; + AnimDuration = MySkelMesh.GetAnimLength( DetonateAnimName ); + bInSprintState = IsInState( 'WeaponSprinting' ); + + if( WorldInfo.NetMode != NM_DedicatedServer ) + { + if( NumDeployedTurrets > 0 ) + { + PlaySoundBase( DetonateAkEvent, true ); + } + + if( bInSprintState ) + { + AnimDuration *= 0.25f; + PlayAnimation( DetonateAnimName, AnimDuration ); + } + else + { + PlayAnimation( DetonateAnimName ); + } + } + + if( Role == ROLE_Authority ) + { + Detonate(); + } + + IncrementFlashCount(); + + if( bInSprintState ) + { + SetTimer( AnimDuration * 0.8f, false, nameof(PlaySprintStart) ); + } + else + { + SetTimer( AnimDuration * 0.5f, false, nameof(GotoActiveState) ); + } +} + +// do nothing, as we have no alt fire mode +simulated function AltFireMode(); + +/** Allow weapons with abnormal state transitions to always use zed time resist*/ +simulated function bool HasAlwaysOnZedTimeResist() +{ + return GetStateName() != FiringStatesArray[BASH_FIREMODE]; +} + +/********************************************************************************************* + * State Active + * A Weapon this is being held by a pawn should be in the active state. In this state, + * a weapon should loop any number of idle animations, as well as check the PendingFire flags + * to see if a shot has been fired. + *********************************************************************************************/ + +simulated state Active +{ + /** Overridden to prevent playing fidget if play has no more ammo */ + simulated function bool CanPlayIdleFidget(optional bool bOnReload) + { + if( !HasAmmo(0) ) + { + return false; + } + + return super.CanPlayIdleFidget( bOnReload ); + } +} + +/********************************************************************************************* + * State WeaponDetonating + * The weapon is in this state while detonating a charge +*********************************************************************************************/ + +simulated function GotoActiveState(); + +simulated state WeaponDetonating +{ + ignores AllowSprinting; + + simulated event BeginState( name PreviousStateName ) + { + PrepareAndDetonate(); + } + + simulated function GotoActiveState() + { + GotoState('Active'); + } +} + +/********************************************************************************************* + * State WeaponThrowing + * Handles throwing weapon (similar to state GrenadeFiring) + *********************************************************************************************/ + +simulated state WeaponThrowing +{ + /** Never refires. Must re-enter this state instead. */ + simulated function bool ShouldRefire() + { + return false; + } + + simulated function EndState(Name NextStateName) + { + local KFPerk InstigatorPerk; + + Super.EndState(NextStateName); + + //Targeted fix for Demolitionist w/ the C4. It should remain in zed time while waiting on + // the fake reload to be triggered. This will return 0 for other perks. + InstigatorPerk = GetPerk(); + if( InstigatorPerk != none ) + { + SetZedTimeResist( InstigatorPerk.GetZedTimeModifier(self) ); + } + } +} + +/********************************************************************************************* + * State WeaponEquipping + * The Weapon is in this state while transitioning from Inactive to Active state. + * Typically, the weapon will remain in this state while its selection animation is being played. + * While in this state, the weapon cannot be fired. +*********************************************************************************************/ + +simulated state WeaponEquipping +{ + simulated event BeginState( name PreviousStateName ) + { + super.BeginState( PreviousStateName ); + + // perform a "reload" if we refilled our ammo from empty while it was unequipped + if( !HasAmmo(THROW_FIREMODE) && HasSpareAmmo() ) + { + PerformArtificialReload(); + } + StopFire(DETONATE_FIREMODE); + } +} + +/********************************************************************************************* + * State WeaponPuttingDown + * Putting down weapon in favor of a new one. + * Weapon is transitioning to the Inactive state. + * + * Detonating while putting down causes C4 not to be put down, which causes problems, so let's + * just ignore SetIronSights, which causes detonation +*********************************************************************************************/ + +simulated state WeaponPuttingDown +{ + ignores SetIronSights; + + simulated event BeginState( name PreviousStateName ) + { + super.BeginState( PreviousStateName ); + StopFire(DETONATE_FIREMODE); + } +} + +/********************************************************************************************* + * @name Trader + *********************************************************************************************/ + +/** Returns trader filter index based on weapon type */ +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Explosive; +} + +function CheckTurretAmmo() +{ + local float Percentage; + local KFWeapon Weapon; + local KFPawn KFP; + + if (Role == Role_Authority) + { + if (KFPC == none) + { + return; + } + + if (KFPC.DeployedTurrets.Length > 0) + { + if (KFPawn_HRG_Warthog(KFPC.DeployedTurrets[0]) != none) + { + Weapon = KFWeapon(KFPawn_HRG_Warthog(KFPC.DeployedTurrets[0]).Weapon); + } + else if (KFPawn_Autoturret(KFPC.DeployedTurrets[0]) != none) + { + Weapon = KFWeapon(KFPawn_Autoturret(KFPC.DeployedTurrets[0]).Weapon); + } + + if (Weapon != none) + { + Percentage = float(Weapon.AmmoCount[0]) / Weapon.MagazineCapacity[0]; + if (Percentage != CurrentAmmoPercentage) + { + CurrentAmmoPercentage = Percentage; + bNetDirty = true; + + if (WorldInfo.NetMode == NM_Standalone) + { + UpdateMaterialColor(CurrentAmmoPercentage); + } + else + { + KFP = KFPawn(Instigator); + if (KFP != none) + { + KFP.OnWeaponSpecialAction( 1 + (CurrentAmmoPercentage * 100) ); + } + } + } + } + } + } +} + +function SetReadyToUse(bool bReady) +{ + if (bTurretReadyToUse != bReady) + { + bTurretReadyToUse = bReady; + bNetDirty = true; + } +} + +simulated event Tick(float DeltaTime) +{ + super.Tick(DeltaTime); + + if (Role == Role_Authority) + { + CheckTurretAmmo(); + } +} + +simulated function UpdateMaterialColor(float Percentage) +{ + if (NumDeployedTurrets == 0) + { + WeaponMICs[`AUTOTURRET_MIC_LED_INDEX].SetScalarParameterValue(EmptyParamName, 0); + } + else if (Percentage >= 0) + { + WeaponMICs[`AUTOTURRET_MIC_LED_INDEX].SetScalarParameterValue(TransitionParamName, 1.0f - Percentage); + WeaponMICs[`AUTOTURRET_MIC_LED_INDEX].SetScalarParameterValue(EmptyParamName, Percentage == 0 ? 1 : 0); + } +} + +simulated function SetWeaponUpgradeLevel(int WeaponUpgradeLevel) +{ + local Actor Turret; + local KFPawn_HRG_Warthog TurretPawn; + + super.SetWeaponUpgradeLevel(WeaponUpgradeLevel); + + if (KFPC != none) + { + foreach KFPC.DeployedTurrets(Turret) + { + TurretPawn = KFPawn_HRG_Warthog(Turret); + if (TurretPawn != none) + { + TurretPawn.UpdateWeaponUpgrade(WeaponUpgradeLevel); + } + } + } +} + +/** + * GRENADE FIRING + * There's a bug that alt fire interrupts the grenade anim at any moment, + * This avoids being able to altfire until the throw animation ends or the + * interrupt notify is reached. + */ + +simulated state GrenadeFiring +{ + simulated function EndState(Name NextStateName) + { + ClearDetonateLock(); + Super.EndState(NextStateName); + } +} + + +/** Play animation at the start of the GrenadeFiring state */ +simulated function PlayGrenadeThrow() +{ + local name WeaponFireAnimName; + local float InterruptTime; + + PlayFiringSound(CurrentFireMode); + + if( Instigator != none && Instigator.IsFirstPerson() ) + { + WeaponFireAnimName = GetGrenadeThrowAnim(); + + if ( WeaponFireAnimName != '' ) + { + InterruptTime = MySkelMesh.GetAnimInterruptTime(WeaponFireAnimName); + PlayAnimation(WeaponFireAnimName, MySkelMesh.GetAnimLength(WeaponFireAnimName),,FireTweenTime); + + bDetonateLocked = true; + SetTimer(InterruptTime, false, nameof(ClearDetonateLock)); + } + } +} + +simulated function ClearDetonateLock() +{ + bDetonateLocked = false; + ClearTimer(nameof(ClearDetonateLock)); +} + +/***/ + + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// Trader +// +/////////////////////////////////////////////////////////////////////////////////////////// + +/** Allows weapon to calculate its own stats for display in trader */ +static simulated event SetTraderWeaponStats( out array WeaponStats ) +{ + super.SetTraderWeaponStats(WeaponStats); + + WeaponStats.Length = 4; + + WeaponStats[0].StatType = TWS_Damage; + WeaponStats[0].StatValue = class'KFWeap_HRG_WarthogWeapon'.static.CalculateTraderWeaponStatDamage(); + + WeaponStats[1].StatType = TWS_Penetration; + WeaponStats[1].StatValue = class'KFWeap_HRG_WarthogWeapon'.default.PenetrationPower[DEFAULT_FIREMODE]; + + WeaponStats[2].StatType = TWS_Range; + // This is now set in native since EffectiveRange has been moved to KFWeaponDefinition + // WeaponStats[2].StatValue = CalculateTraderWeaponStatRange(); + + WeaponStats[3].StatType = TWS_RateOfFire; + WeaponStats[3].StatValue = class'KFWeap_HRG_WarthogWeapon'.static.CalculateTraderWeaponStatFireRate(); +} + +defaultproperties +{ + // start in detonate mode so that an attempt to detonate before any charges are thrown results in + // the proper third-person anim + CurrentFireMode=DETONATE_FIREMODE + + // Zooming/Position + PlayerViewOffset=(X=6.0,Y=2,Z=-4) + FireOffset=(X=0,Y=0) + TurretSpawnOffset=(X=0, Y=15, Z=-50) + + // Content + PackageKey="HRG_Warthog" + FirstPersonMeshName="Wep_1P_HRG_Warthog_MESH.Wep_1P_Warthog_Rig" + FirstPersonAnimSetNames(0)="Wep_1P_HRG_Warthog_ANIM.Wep_1P_HRG_Warthog_ANIM" + PickupMeshName="wep_3p_hrg_warthog_mesh.Wep_HRG_Warthog_Pickup" + AttachmentArchetypeName="WEP_HRG_Warthog_ARCH.Wep_HRG_Warthog_3P" + + // Anim + FireAnim=C4_Throw + FireLastAnim=C4_Throw_Last + + DetonateAnim=Detonate + DetonateLastAnim=Detonate_Last + + // Ammo + MagazineCapacity[0]=1 + SpareAmmoCapacity[0]=3 + InitialSpareMags[0]=1 + AmmoPickupScale[0]=1.0 + + // THROW_FIREMODE + FireInterval(THROW_FIREMODE)=0.25 + FireModeIconPaths(THROW_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Drone' + + // DETONATE_FIREMODE + FiringStatesArray(DETONATE_FIREMODE)=WeaponDetonating + WeaponFireTypes(DETONATE_FIREMODE)=EWFT_Custom + AmmoCost(DETONATE_FIREMODE)=0 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Warthog' + InstantHitDamage(BASH_FIREMODE)=26 + + // Inventory / Grouping + InventoryGroup=IG_Equipment + GroupPriority=11 + WeaponSelectTexture=Texture2D'WEP_UI_HRG_Warthog_TEX.UI_WeaponSelect_HRG_Warthog' + InventorySize=3 + + DetonateAkEvent=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Detonate_Trigger' + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.05f,IncrementWeight=1) + //WeaponUpgrades[2]=(IncrementDamage=1.1f,IncrementWeight=2) + //WeaponUpgrades[3]=(IncrementDamage=1.15f,IncrementWeight=3) + + AssociatedPerkClasses(0)=class'KFPerk_Demolitionist' + + MaxTurretsDeployed=1 + NumDeployedTurrets=0 + ThrowStrength=1350.0f + bTurretReadyToUse=true + + 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))) + NumBloodMapMaterials=3 + + bDetonateLocked=false + CurrentAmmoPercentage=-1.0f +} diff --git a/KFGameContent/Classes/KFWeap_HRG_WarthogWeapon.uc b/KFGameContent/Classes/KFWeap_HRG_WarthogWeapon.uc new file mode 100644 index 0000000..53a3f2b --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_WarthogWeapon.uc @@ -0,0 +1,492 @@ +//============================================================================= +// KFWeap_HRG_WarthogWeapon +//============================================================================= +// An HRG Warthog Grenade Launcher +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HRG_WarthogWeapon extends KFWeap_GrenadeLauncher_Base; + +var KFPawn_HRG_Warthog InstigatorDrone; + +// Used to calculate parabola when spawn projectile +var KFPawn_Monster CurrentTarget; + +// Used to force distance when throwing projectiles when the pawn dies +var float CurrentDistanceProjectile; + +var float DistanceParabolicLaunch; + +var transient float LastTimeFireSeconds; + +simulated event PreBeginPlay() +{ + super.PreBeginPlay(); + StartLoadWeaponContent(); +} + +simulated state WeaponFiring +{ + simulated function EndState(Name NextStateName) + { + local Pawn OriginalInstigator; + + // The Instigator for this weapon is the Player owning the weapon (for Perk, damage, etc,. calculations) + // But for Weapon Firing end state logic we need to point to the real Drone Pawn so we don't mess + // With whichever weapon the Player had equipped at that point + + OriginalInstigator = Instigator; + + Instigator = InstigatorDrone; + + super.EndState(NextStateName); + + Instigator = OriginalInstigator; + } +} + +simulated function FireAmmunition() +{ + CurrentFireMode = DEFAULT_FIREMODE; + super.FireAmmunition(); +} + +simulated function GetMuzzleLocAndRot(out vector MuzzleLoc, out rotator MuzzleRot) +{ + if (KFSkeletalMeshComponent(Mesh).GetSocketWorldLocationAndRotation('MuzzleFlash', MuzzleLoc, MuzzleRot) == false) + { + `Log("MuzzleFlash not found!"); + } +} + +simulated function Projectile ProjectileFire() +{ + local vector RealStartLoc, AimDir, TargetLocation; + local rotator AimRot; + local class MyProjectileClass; + + // tell remote clients that we fired, to trigger effects + + if ( ShouldIncrementFlashCountOnFire() ) + { + IncrementFlashCount(); + } + + MyProjectileClass = GetKFProjectileClass(); + + if( Role == ROLE_Authority || (MyProjectileClass.default.bUseClientSideHitDetection + && MyProjectileClass.default.bNoReplicationToInstigator && Instigator != none + && Instigator.IsLocallyControlled()) ) + { + GetMuzzleLocAndRot(RealStartLoc, AimRot); + + if (CurrentTarget != none) + { + TargetLocation = CurrentTarget.Location; + TargetLocation.Z += CurrentTarget.GetCollisionHeight() * 0.5f; // Add an offset on the location, so it matches correctly + + AimDir = Normal(TargetLocation - RealStartLoc); + } + else + { + AimDir = Vector(Owner.Rotation); + } + + return SpawnAllProjectiles(MyProjectileClass, RealStartLoc, AimDir); + } + + return None; +} + +simulated function KFProjectile SpawnProjectile( class KFProjClass, vector RealStartLoc, vector AimDir ) +{ + local KFProjectile SpawnedProjectile; + local int ProjDamage; + local Pawn OriginalInstigator; + local vector TargetLocation, Distance; + local float HorizontalDistance, TermA, TermB, TermC, InitialSpeed; + + /* + * Instigator issues here. The instigator of the weapon here is the PlayerController which needs to replicate the projectile. + * We spawn it with that instigator, then we change to be able to be able to apply perk effects. + */ + + // Spawn projectile + + OriginalInstigator = Instigator; + Instigator = InstigatorDrone; + + SpawnedProjectile = Spawn( KFProjClass, self,, RealStartLoc); + + if( SpawnedProjectile != none && !SpawnedProjectile.bDeleteMe ) + { + if (CurrentTarget != none) + { + //TargetLocation = CurrentTarget.Mesh.GetBoneLocation('Spine1'); + TargetLocation = CurrentTarget.Location; + TargetLocation.Z += CurrentTarget.GetCollisionHeight() * 0.5f; // Add an offset on the location, so it matches correctly + } + else if (CurrentDistanceProjectile > 0.f) + { + TargetLocation = RealStartLoc + AimDir * CurrentDistanceProjectile; + TargetLocation.Z -= InstigatorDrone.DeployHeight; // We target more or less the ground + } + + //CurrentTarget.DrawDebugSphere(TargetLocation, 100, 10, 0, 255, 0, true); + + Distance = TargetLocation - RealStartLoc; + Distance.Z = 0.f; + HorizontalDistance = VSize(Distance); // 2D + + // If bigger than minimal horizontal distance, and drone is higher than target.. + if (HorizontalDistance > DistanceParabolicLaunch + && RealStartLoc.Z > TargetLocation.Z) + { + // Parabolic launch calculation + // Tweak speed so it can fall on the TargetLocation, use parabolic launch, we assume an Angle = 0 + // We transform from 3D to 2D, assume horizontal start is 0, and horizontal distance is the modulus of the vector distance to target + + // Angle = 0 -> sin(0) -> 0 so no need to apply any initial Y velocity + + // ( ( Y - Y0 ) / ( 0.5 * gravity ) ) + TermA = (TargetLocation.Z - RealStartLoc.Z) / (-11.5f * 0.5f * 100.f); // gravity to cm/s + + // ( X - X0 )^2 / V^2 -> assume XO is 0 + TermB = HorizontalDistance * HorizontalDistance; + + TermC = TermB / TermA; + + InitialSpeed = Sqrt(TermC); + + AimDir = Normal(Distance); + AimDir.Z = 0.f; + } + else + { + // No parabollic, so we force Speed + if (RealStartLoc.Z < TargetLocation.Z) + { + InitialSpeed = 3000.f; + } + else + { + InitialSpeed = 1000.f; + } + } + + SpawnedProjectile.Speed = InitialSpeed; + SpawnedProjectile.MaxSpeed = 0; + SpawnedProjectile.TerminalVelocity = InitialSpeed * 2.f; + SpawnedProjectile.TossZ = 0.f; + + // Mirror damage and damage type from weapon. This is set on the server only and + // these properties are replicated via TakeHitInfo + if ( InstantHitDamage.Length > CurrentFireMode && InstantHitDamageTypes.Length > CurrentFireMode ) + { + ProjDamage = GetModifiedDamage(CurrentFireMode); + SpawnedProjectile.Damage = ProjDamage; + SpawnedProjectile.MyDamageType = InstantHitDamageTypes[CurrentFireMode]; + } + + SpawnedProjectile.UpgradeDamageMod = GetUpgradeDamageMod(); + + SpawnedProjectile.Init( AimDir ); + } + + Instigator = OriginalInstigator; + + return SpawnedProjectile; +} + +simulated function IncrementFlashCount() +{ + local KFPawn P; + P = KFPawn(Owner); + + if( P != None ) + { + P.IncrementFlashCount( Self, CurrentFireMode ); + } +} + +simulated function Fire() +{ + if (HasAmmo(DEFAULT_FIREMODE)) + { + //if (WorldInfo.TimeSeconds - LastTimeFireSeconds > GetFireInterval(DEFAULT_FIREMODE)) + //{ + LastTimeFireSeconds = WorldInfo.TimeSeconds; + + SendToFiringState(DEFAULT_FIREMODE); + //} + } +} + +simulated function StopFire(byte FireModeNum) +{ + super.StopFire(FireModeNum); + + GoToState('Inactive'); +} + +simulated function bool ShouldRefire() +{ + return false; +} + +simulated function ForceExplosionReplicateKill(Vector HitLocation, KFProj_HighExplosive_HRG_Warthog Proj) +{ + HandleClientProjectileExplosion(HitLocation, Proj); + + Proj.Shutdown(); // cleanup/destroy projectile +} + +simulated function ForceExplosionReplicate(Actor Other, Vector HitLocation, Vector HitNormal, KFProj_HighExplosive_HRG_Warthog Proj) +{ + HandleClientProjectileExplosion(HitLocation, Proj); + + if (Proj.ExplosionTemplate != None) + { + Proj.TriggerExplosion(HitLocation, HitNormal, Other); + } + + Proj.Shutdown(); // cleanup/destroy projectile +} + +/** + * Starts playing looping FireSnd only (used for switching sounds in Zedtime) + */ +simulated function StartLoopingFireSound(byte FireModeNum) +{ + if ( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !ShouldForceSingleFireSound() ) + { + bPlayingLoopingFireSnd = true; + KFPawn(Owner).SetWeaponAmbientSound(WeaponFireSnd[FireModeNum].DefaultCue, WeaponFireSnd[FireModeNum].FirstPersonCue); + } +} + +/** + * Stops playing looping FireSnd only (used for switching sounds in Zedtime) + */ +simulated function StopLoopingFireSound(byte FireModeNum) +{ + if ( bPlayingLoopingFireSnd ) + { + KFPawn(Owner).SetWeaponAmbientSound(None); + if ( FireModeNum < WeaponFireLoopEndSnd.Length ) + { + WeaponPlayFireSound(WeaponFireLoopEndSnd[FireModeNum].DefaultCue, WeaponFireLoopEndSnd[FireModeNum].FirstPersonCue); + } + + bPlayingLoopingFireSnd = false; + } +} + +simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation ) +{ + local name WeaponFireAnimName; + local KFPerk CurrentPerk; + local float TempTweenTime, AdjustedAnimLength; + local KFPawn KFPO; + + // If we have stopped the looping fire sound to play single fire sounds for zed time + // start the looping sound back up again when the time is back above zed time speed + if( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !bPlayingLoopingFireSnd ) + { + StartLoopingFireSound(FireModeNum); + } + + PlayFiringSound(CurrentFireMode); + KFPO = KFPawn(Owner); + + if( KFPO != none ) + { + // Tell our pawn about any changes in animation speed + UpdateWeaponAttachmentAnimRate( GetThirdPersonAnimRate() ); + + if( KFPO.IsLocallyControlled() ) + { + if( KFPO.IsFirstPerson() ) + { + if ( !bPlayingLoopingFireAnim ) + { + WeaponFireAnimName = GetWeaponFireAnim(FireModeNum); + + if ( WeaponFireAnimName != '' ) + { + AdjustedAnimLength = MySkelMesh.GetAnimLength(WeaponFireAnimName); + TempTweenTime = FireTweenTime; + + CurrentPerk = GetPerk(); + if( CurrentPerk != none ) + { + CurrentPerk.ModifyRateOfFire( AdjustedAnimLength, self ); + + // We need to unlock the slide if we fire from zero ammo while uber ammo is active + if( EmptyMagBlendNode != none + && BonesToLockOnEmpty.Length > 0 + && AmmoCount[GetAmmoType(FireModeNum)] == 0 + && CurrentPerk.GetIsUberAmmoActive(self) ) + { + EmptyMagBlendNode.SetBlendTarget( 0, 0 ); + TempTweenTime = 0.f; + } + } + + PlayAnimation(WeaponFireAnimName, AdjustedAnimLength,, TempTweenTime); + } + } + + // Start muzzle flash effect + CauseMuzzleFlash(FireModeNum); + } + + HandleRecoil(); + ShakeView(); + + if (AmmoCount[0] == 0 && ForceReloadTimeOnEmpty > 0) + { + SetTimer(ForceReloadTimeOnEmpty, false, nameof(ForceReload)); + } + } + } +} + +simulated function WeaponPlayFireSound(AkBaseSoundObject DefaultSound, AkBaseSoundObject FirstPersonSound) +{ + // ReplicateSound needs an "out" vector + local vector SoundLocation; + + if( Owner != None && !bSuppressSounds ) + { + SoundLocation = KFPawn(Owner).GetPawnViewLocation(); + + if ( DefaultSound != None ) + { + Owner.PlaySoundBase( DefaultSound, false, false, false, SoundLocation ); + } + } +} + +/** True if we want to override the looping fire sounds with fire sounds from another firemode */ +simulated function bool ShouldForceSingleFireSound() +{ + // If this weapon has a single-shot firemode, disable looping fire sounds during zedtime + if ( `IsInZedTime(Instigator) && SingleFireSoundIndex != 255 ) + { + return true; + } + + return false; +} + +simulated function bool HasAlwaysOnZedTimeResist() +{ + return true; +} + +defaultproperties +{ + // Inventory / Grouping + InventorySize=5 + GroupPriority=25 + WeaponSelectTexture=Texture2D'WEP_UI_AutoTurret_TEX.UI_WeaponSelect_AutoTurret' + + // FOV + MeshIronSightFOV=52 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=75 + DOF_FG_MaxNearBlurSize=3.5 + + // Zooming/Position + PlayerViewOffset=(X=9.0,Y=10,Z=-4) + + // Content + PackageKey="HRG_WarthogWeapon" + FirstPersonMeshName="Wep_1P_HRG_WarthogWeapon_MESH.Wep_1stP_HRG_WarthogWeapon_Rig" + FirstPersonAnimSetNames(0)="Wep_1P_HRG_WarthogWeapon_ANIM.Wep_1stP_HRG_WarthogWeapon_Anim" + PickupMeshName="WEP_3P_HRG_WarthogWeapon_MESH.Wep_HRG_Warthog_Pickup" + AttachmentArchetypeName="WEP_HRG_WarthogWeapon_ARCH.HRG_WarthogWeaponAttachment" + MuzzleFlashTemplateName="WEP_HRG_WarthogWeapon_ARCH.HRG_WarthogWeapon_MuzzleFlash" + + // Zooming/Position + IronSightPosition=(X=0,Y=0,Z=0) + + // Ammo + MagazineCapacity[0]=35 + SpareAmmoCapacity[0]=0 + InitialSpareMags[0]=0 + bCanBeReloaded=false + bReloadFromMagazine=false + + // Recoil + maxRecoilPitch=225 + minRecoilPitch=150 + maxRecoilYaw=150 + minRecoilYaw=-150 + RecoilRate=0.085 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=75 + RecoilISMinYawLimit=65460 + RecoilISMaxPitchLimit=195 + RecoilISMinPitchLimit=65460 + RecoilViewRotationScale=0.25 + IronSightMeshFOVCompensationScale=1.5 + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Grenade' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_HighExplosive_HRG_Warthog' + FireInterval(DEFAULT_FIREMODE)=+0.7 + InstantHitDamage(DEFAULT_FIREMODE)=10.0 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_Warthog' + Spread(DEFAULT_FIREMODE)=0.0 + FireOffset=(X=30,Y=4.5,Z=-4) + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None + + // BASH_FIREMODE + WeaponFireTypes(BASH_FIREMODE)=EWFT_None + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Fire_1P') + + //@todo: add akevent when we have it + WeaponDryFireSnd(DEFAULT_FIREMODE)=none + + // Attachments + bHasIronSights=false + bHasFlashlight=false + + AssociatedPerkClasses(0)=class'KFPerk_Demolitionist' + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot' + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.12f,IncrementWeight=1) + //WeaponUpgrades[2]=(IncrementDamage=1.3f,IncrementWeight=2) + //WeaponUpgrades[3]=(IncrementDamage=1.55f,IncrementWeight=3) + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.12f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) + WeaponUpgrades[3]=(Stats=((Stat=EWUS_Damage0, Scale=1.55f), (Stat=EWUS_Weight, Add=3))) + + InstigatorDrone=none + + CurrentTarget=none + CurrentDistanceProjectile=-1.f + + DistanceParabolicLaunch=150.f //cm + + LastTimeFireSeconds=0.f +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc b/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc index 35db31e..928438f 100644 --- a/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc +++ b/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc @@ -905,7 +905,7 @@ defaultproperties FiringStatesArray(DEFAULT_FIREMODE)=MineReconstructorCharge WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Mine_Reconstructor' - FireInterval(DEFAULT_FIREMODE)=+0.223 //+0.33 + FireInterval(DEFAULT_FIREMODE)=+0.2 //+0.33 InstantHitDamage(DEFAULT_FIREMODE)=120 PenetrationPower(DEFAULT_FIREMODE)=0.0; InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Toxic_MineReconstructorImpact' @@ -969,4 +969,6 @@ defaultproperties End Object //iCounterA = 0 bBlocked = false; + + bForceCrosshair=true } \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_Minigun.uc b/KFGameContent/Classes/KFWeap_Minigun.uc index 5920c7e..602f3d4 100644 --- a/KFGameContent/Classes/KFWeap_Minigun.uc +++ b/KFGameContent/Classes/KFWeap_Minigun.uc @@ -217,8 +217,8 @@ defaultproperties IronSightPosition=(X=3.0,Y=15,Z=15) // Ammo - MagazineCapacity[0]=90 //125 //175 //250 - SpareAmmoCapacity[0]=540 //500 //750 + MagazineCapacity[0]=120 //125 //175 //250 + SpareAmmoCapacity[0]=600 //500 //750 InitialSpareMags[0]=1 AmmoPickupScale[0]=1 //0.67 bCanBeReloaded=true diff --git a/KFGameContent/Classes/KFWeap_Pistol_Blunderbuss.uc b/KFGameContent/Classes/KFWeap_Pistol_Blunderbuss.uc index 15b891f..9cda462 100644 --- a/KFGameContent/Classes/KFWeap_Pistol_Blunderbuss.uc +++ b/KFGameContent/Classes/KFWeap_Pistol_Blunderbuss.uc @@ -53,6 +53,11 @@ replication } +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Explosive; +} + simulated function AltFireMode() { if ( !Instigator.IsLocallyControlled() ) diff --git a/KFGameContent/Classes/KFWeap_Shotgun_HRG_Kaboomstick.uc b/KFGameContent/Classes/KFWeap_Shotgun_HRG_Kaboomstick.uc index 7482b68..cc63f25 100644 --- a/KFGameContent/Classes/KFWeap_Shotgun_HRG_Kaboomstick.uc +++ b/KFGameContent/Classes/KFWeap_Shotgun_HRG_Kaboomstick.uc @@ -10,6 +10,40 @@ class KFWeap_Shotgun_HRG_Kaboomstick extends KFWeap_Shotgun_DoubleBarrel; +var transient bool AlreadyIssuedCanNuke; + +simulated function KFProjectile SpawnAllProjectiles(class KFProjClass, vector RealStartLoc, vector AimDir) +{ + local KFProjectile Proj; + + AlreadyIssuedCanNuke = false; + + Proj = Super.SpawnAllProjectiles(KFProjClass, RealStartLoc, AimDir); + + AlreadyIssuedCanNuke = false; + + return Proj; +} + +simulated function KFProjectile SpawnProjectile( class KFProjClass, vector RealStartLoc, vector AimDir ) +{ + local KFProj_Explosive_HRG_Kaboomstick Proj; + + Proj = KFProj_Explosive_HRG_Kaboomstick(Super.SpawnProjectile(KFProjClass, RealStartLoc, AimDir)); + + if (AlreadyIssuedCanNuke == false) + { + Proj.bCanNuke = true; + AlreadyIssuedCanNuke = true; + } + else + { + Proj.bCanNuke = false; + } + + return Proj; +} + defaultproperties { // Inventory @@ -126,4 +160,6 @@ defaultproperties WeaponUpgrades[0]=(Stats=((Stat=EWUS_Damage0, Scale=1.f, Add=0), (Stat=EWUS_Weight, Scale=1.f, Add=0))) 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.30f), (Stat=EWUS_Damage1, Scale=1.30f), (Stat=EWUS_Weight, Add=2))) + + AlreadyIssuedCanNuke = false } \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_Shotgun_S12.uc b/KFGameContent/Classes/KFWeap_Shotgun_S12.uc new file mode 100644 index 0000000..2dcf124 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_Shotgun_S12.uc @@ -0,0 +1,717 @@ +//============================================================================= +// KFWeap_Shotgun_S12 +//============================================================================= +// AA12 Auto Shotgun Weapon Class +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_Shotgun_S12 extends KFWeap_ShotgunBase; + +var (Positioning) vector SecondaryFireOffset; + +const SecondaryFireAnim = 'Shoot_Secondary'; +const SecondaryFireIronAnim = 'Shoot_Secondary_Iron'; + +const SecondaryReloadAnim = 'Reload_Secondary'; +const SecondaryReloadEliteAnim = 'Reload_Secondary_Elite'; + +var() KFMuzzleFlash SecondaryMuzzleFlashTemplate; + +// Used on the server to keep track of grenades +var int ServerTotalAltAmmo; + +var transient bool bCanceledAltAutoReload; + +var GameExplosion ExplosionTemplate; +var transient ParticleSystemComponent ExplosionPSC; +var ParticleSystem ExplosionEffect; + +var float ExplosionOriginalDamage; + +simulated event PostBeginPlay() +{ + super.PostBeginPlay(); + + ExplosionOriginalDamage = ExplosionTemplate.Damage; +} + +/** Instead of switch fire mode use as immediate alt fire */ +simulated function AltFireMode() +{ + if ( !Instigator.IsLocallyControlled() ) + { + return; + } + + if (bCanceledAltAutoReload) + { + bCanceledAltAutoReload = false; + TryToAltReload(); + return; + } + + StartFire(ALTFIRE_FIREMODE); +} + +simulated function BeginFire( Byte FireModeNum ) +{ + local bool bStoredAutoReload; + + // We are trying to reload the weapon but the primary ammo in already at full capacity + if ( FireModeNum == RELOAD_FIREMODE && !CanReload() ) + { + // Store the cuurent state of bCanceledAltAutoReload in case its not possible to do the reload + bStoredAutoReload = bCanceledAltAutoReload; + bCanceledAltAutoReload = false; + + if(CanAltAutoReload()) + { + TryToAltReload(); + return; + } + + bCanceledAltAutoReload = bStoredAutoReload; + } + + super.BeginFire( FireModeNum ); +} + +/** + * Initializes ammo counts, when weapon is spawned. + */ +function InitializeAmmo() +{ + Super.InitializeAmmo(); + + // Add Secondary ammo to our secondary spare ammo count both of these are important, in order to allow dropping the weapon to function properly. + SpareAmmoCount[1] = Min(SpareAmmoCount[1] + InitialSpareMags[1] * default.MagazineCapacity[1], GetMaxAmmoAmount(1) - AmmoCount[1]); + ServerTotalAltAmmo += SpareAmmoCount[1]; + + // Make sure the server doesn't get extra shots on listen servers. + if(Role == ROLE_Authority && !Instigator.IsLocallyControlled()) + { + ServerTotalAltAmmo += AmmoCount[1]; + } +} + +/** + * @see Weapon::ConsumeAmmo + */ +simulated function ConsumeAmmo( byte FireModeNum ) +{ + local byte AmmoType; + local bool bNoInfiniteAmmo; + local int OldAmmoCount; + + if(UsesSecondaryAmmo() && FireModeNum == ALTFIRE_FIREMODE && Role == ROLE_Authority && !Instigator.IsLocallyControlled()) + { + AmmoType = GetAmmoType(FireModeNum); + + OldAmmoCount = AmmoCount[AmmoType]; + Super.ConsumeAmmo(FireModeNum); + + bNoInfiniteAmmo = (OldAmmoCount - AmmoCount[AmmoType]) > 0 || AmmoCount[AmmoType] == 0; + if ( bNoInfiniteAmmo ) + { + ServerTotalAltAmmo--; + } + } + else + { + Super.ConsumeAmmo(FireModeNum); + } +} + +simulated function bool HasAnyAmmo() +{ + return HasSpareAmmo() || HasAmmo(DEFAULT_FIREMODE) || SpareAmmoCount[1] > 0 || HasAmmo(ALTFIRE_FIREMODE); +} + +/** Make sure user can't fire infinitely if they cheat to get infinite ammo locally. */ +simulated event bool HasAmmo( byte FireModeNum, optional int Amount=1 ) +{ + local byte AmmoType; + + AmmoType = GetAmmoType(FireModeNum); + + if(AmmoType == 1 && Role == ROLE_Authority && Instigator != none && UsesSecondaryAmmo() && !Instigator.IsLocallyControlled()) + { + if(ServerTotalAltAmmo <= 0) + { + return false; + } + } + + return Super.HasAmmo(FireModeNum, Amount ); +} + +/** + * Overridden so any grenades added will go to the spare ammo and no the clip. + */ +function int AddSecondaryAmmo(int Amount) +{ + local int OldAmmo; + + // If we can't accept spare ammo, then abort + if( !CanRefillSecondaryAmmo() ) + { + return 0; + } + + if(Role == ROLE_Authority && !Instigator.IsLocallyControlled()) + { + OldAmmo = ServerTotalAltAmmo; + + ServerTotalAltAmmo = Min(ServerTotalAltAmmo + Amount, GetMaxAmmoAmount(1)); + ClientGiveSecondaryAmmo(Amount); + return ServerTotalAltAmmo - OldAmmo; + } + else + { + OldAmmo = SpareAmmoCount[1]; + ClientGiveSecondaryAmmo(Amount); + return SpareAmmoCount[1] - OldAmmo; + } +} + +/** Give client specified amount of ammo (used player picks up ammo on the server) */ +reliable client function ClientGiveSecondaryAmmo(byte Amount) +{ + SpareAmmoCount[1] = Min(SpareAmmoCount[1] + Amount, GetMaxAmmoAmount(1) - AmmoCount[1]); + TryToAltReload(); +} + +function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon ) +{ + local KFWeap_Shotgun_S12 Weap; + + Super.SetOriginalValuesFromPickup(PickedUpWeapon); + + if(Role == ROLE_Authority && !Instigator.IsLocallyControlled()) + { + Weap = KFWeap_Shotgun_S12(PickedUpWeapon); + ServerTotalAltAmmo = Weap.ServerTotalAltAmmo; + SpareAmmoCount[1] = ServerTotalAltAmmo - AmmoCount[1]; + } + else + { + // If we're locally controlled, don't bother using ServerTotalAltAmmo. + SpareAmmoCount[1] = PickedUpWeapon.SpareAmmoCount[1]; + } +} + +/********************************************************************************************* + * State GrenadeFiring + * Handles firing grenade launcher. + *********************************************************************************************/ + +simulated state FiringSecondaryState extends WeaponSingleFiring +{ + // Overriden to not call FireAmmunition right at the start of the state + simulated event BeginState( Name PreviousStateName ) + { + Super.BeginState(PreviousStateName); + NotifyBeginState(); + } + + simulated function EndState(Name NextStateName) + { + Super.EndState(NextStateName); + NotifyEndState(); + } + + /** + * This function returns the world location for spawning the visual effects + * Overridden to use a special offset for throwing grenades + */ + simulated event vector GetMuzzleLoc() + { + local vector MuzzleLocation; + + // swap fireoffset temporarily + FireOffset = SecondaryFireOffset; + MuzzleLocation = Global.GetMuzzleLoc(); + FireOffset = default.FireOffset; + + return MuzzleLocation; + } + + /** Get whether we should play the reload anim as well or not */ + simulated function name GetWeaponFireAnim(byte FireModeNum) + { + return bUsingSights ? SecondaryFireIronAnim : SecondaryFireAnim; + } +} + +/** + * Don't allow secondary fire to make a primary fire shell particle come out of the gun. + */ +simulated function CauseMuzzleFlash(byte FireModeNum) +{ + local bool AutoShellEject; + + if(FireModeNum == ALTFIRE_FIREMODE) + { + if (MuzzleFlash == None) + { + AttachSecondaryMuzzleFlash(); + } + + AutoShellEject = MuzzleFlash.bAutoActivateShellEject; + + MuzzleFlash.bAutoActivateShellEject = false; + + Super.CauseMuzzleFlash(FireModeNum); + + MuzzleFlash.bAutoActivateShellEject = AutoShellEject; + } + else + { + Super.CauseMuzzleFlash(FireModeNum); + } +} + +simulated function AttachSecondaryMuzzleFlash() +{ + if ( MySkelMesh != none ) + { + if (MuzzleFlashTemplate != None) + { + MuzzleFlash = new(self) Class'KFMuzzleFlash'(SecondaryMuzzleFlashTemplate); + MuzzleFlash.AttachMuzzleFlash(MySkelMesh, 'MuzzleFlashAlt'); + } + } +} + +/********************************************************************************************* + * State Reloading + * This is the default Reloading State. It's performed on both the client and the server. + *********************************************************************************************/ + +/** Do not allow alternate fire to tell the weapon to reload. Alt reload occurs in a separate codepath */ +simulated function bool ShouldAutoReload(byte FireModeNum) +{ + if(FireModeNum == ALTFIRE_FIREMODE) + { + return false; + } + + return Super.ShouldAutoReload(FireModeNum); +} + +/** Called on local player when reload starts and replicated to server */ +simulated function SendToAltReload() +{ + ReloadAmountLeft = MagazineCapacity[1]; + GotoState('AltReloading'); + if ( Role < ROLE_Authority ) + { + ServerSendToAltReload(); + } +} + +/** Called from client when reload starts */ +reliable server function ServerSendToAltReload() +{ + ReloadAmountLeft = MagazineCapacity[1]; + GotoState('AltReloading'); +} + +/** + * State Reloading + * State the weapon is in when it is being reloaded (current magazine replaced with a new one, related animations and effects played). + */ +simulated state AltReloading extends Reloading +{ + ignores ForceReload, ShouldAutoReload, AllowSprinting; + + simulated function byte GetWeaponStateId() + { + local KFPerk Perk; + local bool bTacticalReload; + + Perk = GetPerk(); + bTacticalReload = (Perk != None && Perk.GetUsingTactialReload(self)); + + return (bTacticalReload ? WEP_ReloadSecondary_Elite : WEP_ReloadSecondary); + } + + simulated event BeginState(Name PreviousStateName) + { + super.BeginState(PreviousStateName); + bCanceledAltAutoReload = true; + } + + // Overridding super so we don't call functions we don't want to call. + simulated function EndState(Name NextStateName) + { + ClearZedTimeResist(); + ClearTimer(nameof(ReloadStatusTimer)); + ClearTimer(nameof(ReloadAmmoTimer)); + + CheckBoltLockPostReload(); + NotifyEndState(); + + `DialogManager.PlayAmmoDialog( KFPawn(Instigator), float(SpareAmmoCount[1]) / float(GetMaxAmmoAmount(1)) ); + } + + // Overridding super so when this reload is called directly after normal reload state there + // are not complications resulting from back to back reloads. + simulated event ReplicatedEvent(name VarName) + { + Global.ReplicatedEvent(Varname); + } + + /** Make sure we can inturrupt secondary reload with anything. */ + simulated function bool CanOverrideMagReload(byte FireModeNum) + { + return true; + } + + /** Returns animation to play based on reload type and status */ + simulated function name GetReloadAnimName( bool bTacticalReload ) + { + return (bTacticalReload) ? SecondaryReloadEliteAnim : SecondaryReloadAnim; + } + + simulated function PerformReload(optional byte FireModeNum) + { + Global.PerformReload(ALTFIRE_FIREMODE); + + if(Instigator.IsLocallyControlled() && Role < ROLE_Authority) + { + ServerSetAltAmmoCount(AmmoCount[1]); + } + + bCanceledAltAutoReload = false; + } + + simulated function EReloadStatus GetNextReloadStatus(optional byte FireModeNum) + { + return Global.GetNextReloadStatus(ALTFIRE_FIREMODE); + } +} + +reliable server function ServerSetAltAmmoCount(byte Amount) +{ + AmmoCount[1] = min(Amount, MagazineCapacity[1]); +} + +/** Allow reloads for primary weapon to be interupted by firing secondary weapon. */ +simulated function bool CanOverrideMagReload(byte FireModeNum) +{ + if(FireModeNum == ALTFIRE_FIREMODE) + { + return true; + } + + return Super.CanOverrideMagReload(FireModeNum); +} + +/********************************************************************************************* + * State Active + * Try to get weapon to automatically reload secondary fire types when it can. + *********************************************************************************************/ + +simulated state Active +{ + /** Initialize the weapon as being active and ready to go. */ + simulated event BeginState(Name PreviousStateName) + { + // do this last so the above code happens before any state changes + Super.BeginState(PreviousStateName); + + // If nothing happened, try to reload + TryToAltReload(); + } +} + +/** Network: Local Player */ +simulated function bool CanAltAutoReload() +{ + if ( !Instigator.IsLocallyControlled() ) + { + return false; + } + + if(!UsesSecondaryAmmo()) + { + return false; + } + + // If the weapon wants to fire its primary weapon, and it can fire, do not allow weapon to automatically alt reload + if(PendingFire(DEFAULT_FIREMODE) && HasAmmo(DEFAULT_FIREMODE)) + { + return false; + } + + if(!CanReload(ALTFIRE_FIREMODE)) + { + return false; + } + + if (bCanceledAltAutoReload) + { + return false; + } + + return true; +} + +simulated function TryToAltReload() +{ + if ((IsInState('Active') || IsInState('WeaponSprinting')) && CanAltAutoReload()) + { + SendToAltReload(); + } +} + +simulated function TriggerAltExplosion() +{ + local vector MuzzleLocation, HitLocation, HitNormal; + local KFExplosionActorReplicated ExploActor; + + // TriggerExplosion + if (Role == ROLE_Authority) + { + MuzzleLocation = GetMuzzleLoc(); + Trace( HitLocation, HitNormal, MuzzleLocation + vect(0, 0, -1) * 250000, MuzzleLocation); + // Move a bit from hit location + HitLocation = HitLocation + (vect(0,0,1) * 128.f); + + // Explode using the given template + ExploActor = Spawn(class'KFExplosionActorReplicated', self,, HitLocation, rotator(vect(0,0,1)),, true); + if (ExploActor != None) + { + ExploActor.InstigatorController = Instigator.Controller; + ExploActor.Instigator = Instigator; + ExploActor.bIgnoreInstigator = true; + ExplosionTemplate.Damage = ExplosionOriginalDamage * GetUpgradeDamageMod(ALTFIRE_FIREMODE); + + ExploActor.Explode(ExplosionTemplate); + } + } + + if (WorldInfo.NetMode != NM_DedicatedServer) + { + // Trigger VFX ? + + if (HitLocation == vect(0,0,0)) + { + MySkelMesh.GetSocketWorldLocationAndRotation('MuzzleFlashAlt', MuzzleLocation); + Trace( HitLocation, HitNormal, MuzzleLocation + vect(0, 0, -1) * 250000, MuzzleLocation); + // Move a bit from hit location + HitLocation = HitLocation + (vect(0,0,1) * 128.f); + } + + if (ExplosionEffect != None) + { + ExplosionPSC = WorldInfo.MyEmitterPool.SpawnEmitter(ExplosionEffect, HitLocation, rotator(vect(0,0,1))); + ExplosionPSC.ActivateSystem(); + } + } +} + +simulated function CustomFire() +{ + // Alt-fire blast only (server authoritative) + if ( CurrentFireMode != ALTFIRE_FIREMODE ) + { + Super.CustomFire(); + return; + } + + TriggerAltExplosion(); + IncrementFlashCount(); +} + +simulated event Tick(float DeltaTime) +{ + local vector SocketLocation; + Super.Tick(DeltaTime); + + // Client only + if (WorldInfo.NetMode != NM_DedicatedServer) + { + KFSkeletalMeshComponent(Mesh).GetSocketWorldLocationAndRotation('MuzzleFlashAlt', SocketLocation); + + if (ExplosionPSC != None && ExplosionPSC.bIsActive) + { + ExplosionPSC.SetVectorParameter('WeaponEndpoint', SocketLocation); + } + } + +} + +defaultproperties +{ + bCanRefillSecondaryAmmo = true; + + // Shooting Animations + FireSightedAnims[0]=Shoot_Iron + FireSightedAnims[1]=Shoot_Iron2 + FireSightedAnims[2]=Shoot_Iron3 + + // FOV + MeshFOV=86 + MeshIronSightFOV=35 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=85 + DOF_FG_MaxNearBlurSize=3.5 + + // Content + PackageKey="Saiga12" + FirstPersonMeshName="Wep_1P_Saiga12_MESH.Wep_1stP_Saiga12_Rig" + FirstPersonAnimSetNames(0)="Wep_1P_Saiga12_ANIM.Wep_1stP_Saiga12_Anim_New" + PickupMeshName="WEP_3P_Saiga12_MESH.Wep_Saiga12_Pickup" + AttachmentArchetypeName="WEP_Saiga12_ARCH.Wep_Saiga12_3P" + MuzzleFlashTemplateName="WEP_Saiga12_ARCH.Wep_Saiga12_MuzzleFlash" + SecondaryMuzzleFlashTemplate=KFMuzzleFlash'WEP_Saiga12_ARCH.Wep_Saiga12_MuzzleFlashAlt' + + // Zooming/Position + PlayerViewOffset=(X=0,Y=12.5,Z=-18)// + IronSightPosition=(X=12,Y=0,Z=-10.6) + + // Pickup + AmmoPickupScale[0]=2.0 + AmmoPickupScale[1]=1.0 + + // Ammo + MagazineCapacity[0]=10 + SpareAmmoCapacity[0]=130 + InitialSpareMags[0]=4 + + //grenades + MagazineCapacity[1]=1 + SpareAmmoCapacity[1]=5 + InitialSpareMags[1]=2 + + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=250 + minRecoilPitch=225 + maxRecoilYaw=125 + minRecoilYaw=-125 + RecoilRate=0.075 + RecoilBlendOutRatio=0.25 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=64785 + RecoilISMaxYawLimit=75 + RecoilISMinYawLimit=65460 + RecoilISMaxPitchLimit=375 + RecoilISMinPitchLimit=65460 + RecoilViewRotationScale=0.7 + FallingRecoilModifier=1.5 + HippedRecoilModifier=1.25 + + SecondaryAmmoTexture=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity' + bUseGrenadeAsSecondaryAmmo=true + + // Inventory / Grouping + InventorySize=8 + GroupPriority=100 + WeaponSelectTexture=Texture2D'WEP_UI_Saiga12_TEX.UI_WeaponSelect_Saiga12' + + AssociatedPerkClasses(0)=class'KFPerk_Support' + + // 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_Bullet_Pellet' + InstantHitDamage(DEFAULT_FIREMODE)=24.0 //25 //20 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_Shotgun_S12' + PenetrationPower(DEFAULT_FIREMODE)=2.0 + FireInterval(DEFAULT_FIREMODE)=0.2 // 300 RPM + Spread(DEFAULT_FIREMODE)=0.08 + FireOffset=(X=30,Y=5,Z=-4) + NumPellets(DEFAULT_FIREMODE)=7 + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=FiringSecondaryState + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Custom + AmmoCost(ALTFIRE_FIREMODE)=1 + FireInterval(ALTFIRE_FIREMODE)=+0.25 // 300 RPM + SecondaryFireOffset=(X=20.f,Y=4.5,Z=-7.f) + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_Shotgun_S12' + InstantHitDamage(BASH_FIREMODE)=30 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Fire_1P') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Alt_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Alt_Fire_1P') + + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Handling_DryFire' + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Handling_DryFire' + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil' + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + // Weapon Upgrade stat boosts + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + + // Grenade explosion light + Begin Object Class=PointLightComponent Name=ExplosionPointLight + LightColor=(R=252,G=218,B=171,A=255) + Brightness=0.5f + Radius=400.f + FalloffExponent=10.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=False + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE) + End Object + + // explosion + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=200 + DamageRadius=800 + DamageFalloffExponent=0.f + DamageDelay=0.f + MomentumTransferScale=10000 + bAlwaysFullDamage=true + bDoCylinderCheck=true + + // Damage Effects + MyDamageType=class'KFDT_Explosive_Shotgun_S12' + KnockDownStrength=150 + FractureMeshRadius=200.0 + FracturePartVel=500.0 + + ExplosionSound=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Alt_Fire_3P' + ExplosionEffects=KFImpactEffectInfo'WEP_Saiga12_ARCH.WEP_Saiga12_Impacts' + + // Dynamic Light + ExploLight=ExplosionPointLight + ExploLightStartFadeOutTime=0.0 + ExploLightFadeOutTime=0.3 + + bIgnoreInstigator=true + ActorClassToIgnoreForDamage = class'KFPawn_Human' + + // Camera Shake + CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' + CamShakeInnerRadius=0 + CamShakeOuterRadius=300 + CamShakeFalloff=1.5f + bOrientCameraShakeTowardsEpicenter=true + End Object + ExplosionTemplate=ExploTemplate0 + + ExplosionEffect=ParticleSystem'WEP_1P_Saiga12_EMIT.FX_Saiga12_Explosion' + +}