From ea1d43f08f10076932d74327fd40174e1db0661e Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Mon, 28 Nov 2022 00:49:25 +0300 Subject: [PATCH] upload --- Engine/Classes/OnlineGameSettings.uc | 5 +- Engine/Classes/OnlineSubsystem.uc | 1 + KFGame/Classes/KFAfflictionAdvanced.uc | 6 +- KFGame/Classes/KFAfflictionBase.uc | 19 +- KFGame/Classes/KFAfflictionManager.uc | 4 +- KFGame/Classes/KFAffliction_BigHead.uc | 4 +- KFGame/Classes/KFAffliction_Bleed.uc | 4 +- KFGame/Classes/KFAffliction_EMP.uc | 8 +- KFGame/Classes/KFAffliction_EMPDisrupt.uc | 4 +- KFGame/Classes/KFAffliction_Fire.uc | 4 +- KFGame/Classes/KFAffliction_HeavyRecovery.uc | 4 +- KFGame/Classes/KFAffliction_Knockdown.uc | 4 +- KFGame/Classes/KFAffliction_MediumRecovery.uc | 4 +- KFGame/Classes/KFAffliction_Microwave.uc | 4 +- KFGame/Classes/KFAffliction_Poison.uc | 4 +- KFGame/Classes/KFAffliction_Shrink.uc | 4 +- KFGame/Classes/KFAffliction_Snare.uc | 4 +- KFGame/Classes/KFCheatManager.uc | 7 + KFGame/Classes/KFCommon_LocalizedStrings.uc | 15 + KFGame/Classes/KFDT_HVStormCannonSpread.uc | 26 + KFGame/Classes/KFDT_Toxic_HRG_MedicMissile.uc | 23 + KFGame/Classes/KFExplosionActor.uc | 8 +- ...pecialEventObjectivesContainer_Xmas2022.uc | 27 + KFGame/Classes/KFGFxMenu_Inventory.uc | 404 +++++++-- KFGame/Classes/KFGFxMenu_Perks.uc | 11 +- KFGame/Classes/KFGFxMoviePlayer_HUD.uc | 19 +- KFGame/Classes/KFGFxServerBrowser_Filters.uc | 34 +- .../KFGFxServerBrowser_ServerDetails.uc | 7 +- .../Classes/KFGFxServerBrowser_ServerList.uc | 10 + .../KFGFxSpecialeventObjectivesContainer.uc | 3 + .../KFGFxStartContainer_InGameOverview.uc | 29 +- .../KFGFxStartGameContainer_FindGame.uc | 20 +- .../KFGFxStartGameContainer_Options.uc | 38 +- KFGame/Classes/KFGFxStoreContainer_Main.uc | 32 +- .../KFGFxTraderContainer_PlayerInventory.uc | 4 +- KFGame/Classes/KFGFxWorld_WeaponRadar.uc | 79 ++ KFGame/Classes/KFGameEngine.uc | 2 +- KFGame/Classes/KFGameInfo.uc | 85 +- KFGame/Classes/KFGameReplicationInfo.uc | 14 +- KFGame/Classes/KFGfxMenu_StartGame.uc | 40 +- KFGame/Classes/KFInventoryManager.uc | 42 +- KFGame/Classes/KFLocalMessage_Priority.uc | 33 +- KFGame/Classes/KFOnlineStatsReadDingo.uc | 3 + KFGame/Classes/KFOnlineStatsWrite.uc | 32 +- KFGame/Classes/KFOnlineStatsWriteDingo.uc | 2 +- KFGame/Classes/KFPawn_Human.uc | 15 +- KFGame/Classes/KFPawn_Monster.uc | 2 +- KFGame/Classes/KFPerk.uc | 10 + KFGame/Classes/KFPerk_FieldMedic.uc | 2 +- KFGame/Classes/KFPerk_Support.uc | 4 +- KFGame/Classes/KFPerk_Swat.uc | 26 +- KFGame/Classes/KFPlayerController.uc | 186 ++++- .../KFPlayerController_WeeklySurvival.uc | 64 ++ KFGame/Classes/KFProfileSettings.uc | 2 + KFGame/Classes/KFProj_BallisticExplosive.uc | 9 +- KFGame/Classes/KFSeasonalEventStats.uc | 6 + KFGame/Classes/KFUnlockManager.uc | 12 +- KFGame/Classes/KFWeapDef_Doshinegun.uc | 2 +- .../Classes/KFWeapDef_HRG_BallisticBouncer.uc | 26 + KFGame/Classes/KFWeapDef_HRG_MedicMissile.uc | 24 + KFGame/Classes/KFWeapDef_HVStormCannon.uc | 25 + KFGame/Classes/KFWeapDef_ZedMKIII.uc | 22 + KFGame/Classes/KFWeap_ShotgunBase.uc | 36 + KFGame/Classes/KFWeapon.uc | 5 +- KFGame/Classes/KFWeaponSkinList.uc | 220 ++++- KFGame/Classes/KFWeeklyOutbreakInformation.uc | 2 +- KFGame/KFOnlineStats.uci | 1 + KFGame/KFProfileSettings.uci | 3 +- .../KFDT_Ballistic_HRG_MedicMissile.uc | 24 + .../Classes/KFDT_Ballistic_ZedMKIII_Rocket.uc | 25 + .../KFDT_Bludgeon_HRG_BallisticBouncer.uc | 16 + ...KFDT_Bludgeon_HRG_BallisticBouncer_Shot.uc | 26 + .../Classes/KFDT_Bludgeon_HRG_MedicMissile.uc | 16 + .../Classes/KFDT_Bludgeon_HVStormCannon.uc | 16 + .../Classes/KFDT_Bludgeon_ZedMKIII.uc | 16 + .../Classes/KFDT_EMP_HVStormCannon.uc | 54 ++ .../KFDT_Explosive_HRG_MedicMissile.uc | 32 + .../Classes/KFDT_Explosive_ZedMKIII.uc | 30 + .../Classes/KFDT_Microwave_ZedMKIII.uc | 69 ++ .../KFExplosion_HRG_BallisticBouncer.uc | 283 +++++++ .../Classes/KFExplosion_HRG_MedicMissile.uc | 73 ++ KFGameContent/Classes/KFGameInfo_Survival.uc | 2 + .../Classes/KFGameInfo_VersusSurvival.uc | 16 +- .../Classes/KFGameInfo_WeeklySurvival.uc | 187 +++++ .../Classes/KFOutbreakEvent_Weekly.uc | 15 + .../Classes/KFProj_Bullet_HVStormCannon.uc | 22 + .../Classes/KFProj_Bullet_ZedMKIII.uc | 22 + .../Classes/KFProj_DynamiteGrenade.uc | 4 +- .../Classes/KFProj_Grenade_HRGTeslauncher.uc | 4 +- .../Classes/KFProj_HRG_BallisticBouncer.uc | 556 +++++++++++++ .../Classes/KFProj_Rocket_HRG_MedicMissile.uc | 128 +++ .../Classes/KFProj_Rocket_ZedMKIII.uc | 123 +++ .../Classes/KFSeasonalEventStats_Xmas2022.uc | 183 +++++ .../Classes/KFShotgunJumpEndVolume.uc | 95 +++ .../Classes/KFShotgunJumpStartVolume.uc | 151 ++++ .../KFWeapAttach_HRG_BallisticBouncer.uc | 120 +++ .../Classes/KFWeap_AssaultRifle_Doshinegun.uc | 6 +- .../Classes/KFWeap_GravityImploder.uc | 4 +- .../Classes/KFWeap_HRG_BallisticBouncer.uc | 766 ++++++++++++++++++ .../Classes/KFWeap_HRG_MedicMissile.uc | 210 +++++ KFGameContent/Classes/KFWeap_HRG_SonicGun.uc | 69 +- KFGameContent/Classes/KFWeap_HVStormCannon.uc | 417 ++++++++++ .../Classes/KFWeap_Knife_Demolitionist.uc | 3 + .../Classes/KFWeap_Shotgun_DoubleBarrel.uc | 21 +- .../Classes/KFWeap_Shotgun_ElephantGun.uc | 39 +- KFGameContent/Classes/KFWeap_ZedMKIII.uc | 564 +++++++++++++ 106 files changed, 5944 insertions(+), 303 deletions(-) create mode 100644 KFGame/Classes/KFDT_HVStormCannonSpread.uc create mode 100644 KFGame/Classes/KFDT_Toxic_HRG_MedicMissile.uc create mode 100644 KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Xmas2022.uc create mode 100644 KFGame/Classes/KFGFxWorld_WeaponRadar.uc create mode 100644 KFGame/Classes/KFWeapDef_HRG_BallisticBouncer.uc create mode 100644 KFGame/Classes/KFWeapDef_HRG_MedicMissile.uc create mode 100644 KFGame/Classes/KFWeapDef_HVStormCannon.uc create mode 100644 KFGame/Classes/KFWeapDef_ZedMKIII.uc create mode 100644 KFGameContent/Classes/KFDT_Ballistic_HRG_MedicMissile.uc create mode 100644 KFGameContent/Classes/KFDT_Ballistic_ZedMKIII_Rocket.uc create mode 100644 KFGameContent/Classes/KFDT_Bludgeon_HRG_BallisticBouncer.uc create mode 100644 KFGameContent/Classes/KFDT_Bludgeon_HRG_BallisticBouncer_Shot.uc create mode 100644 KFGameContent/Classes/KFDT_Bludgeon_HRG_MedicMissile.uc create mode 100644 KFGameContent/Classes/KFDT_Bludgeon_HVStormCannon.uc create mode 100644 KFGameContent/Classes/KFDT_Bludgeon_ZedMKIII.uc create mode 100644 KFGameContent/Classes/KFDT_EMP_HVStormCannon.uc create mode 100644 KFGameContent/Classes/KFDT_Explosive_HRG_MedicMissile.uc create mode 100644 KFGameContent/Classes/KFDT_Explosive_ZedMKIII.uc create mode 100644 KFGameContent/Classes/KFDT_Microwave_ZedMKIII.uc create mode 100644 KFGameContent/Classes/KFExplosion_HRG_BallisticBouncer.uc create mode 100644 KFGameContent/Classes/KFExplosion_HRG_MedicMissile.uc create mode 100644 KFGameContent/Classes/KFProj_Bullet_HVStormCannon.uc create mode 100644 KFGameContent/Classes/KFProj_Bullet_ZedMKIII.uc create mode 100644 KFGameContent/Classes/KFProj_HRG_BallisticBouncer.uc create mode 100644 KFGameContent/Classes/KFProj_Rocket_HRG_MedicMissile.uc create mode 100644 KFGameContent/Classes/KFProj_Rocket_ZedMKIII.uc create mode 100644 KFGameContent/Classes/KFSeasonalEventStats_Xmas2022.uc create mode 100644 KFGameContent/Classes/KFShotgunJumpEndVolume.uc create mode 100644 KFGameContent/Classes/KFShotgunJumpStartVolume.uc create mode 100644 KFGameContent/Classes/KFWeapAttach_HRG_BallisticBouncer.uc create mode 100644 KFGameContent/Classes/KFWeap_HRG_BallisticBouncer.uc create mode 100644 KFGameContent/Classes/KFWeap_HRG_MedicMissile.uc create mode 100644 KFGameContent/Classes/KFWeap_HVStormCannon.uc create mode 100644 KFGameContent/Classes/KFWeap_ZedMKIII.uc diff --git a/Engine/Classes/OnlineGameSettings.uc b/Engine/Classes/OnlineGameSettings.uc index 4fa2d2f..89014ad 100644 --- a/Engine/Classes/OnlineGameSettings.uc +++ b/Engine/Classes/OnlineGameSettings.uc @@ -139,6 +139,8 @@ var const int SecondsDeallocatedBucketSize; var databinding string Region; //@HSL_END +var databinding bool bNoSeasonalSkins; + /** Represents a player in the game */ struct native PlayerResult { @@ -183,4 +185,5 @@ defaultproperties //@SABER_BEGIN "Exiling" servers which kicks/bans bServerExiled=false //@SABER_END -} \ No newline at end of file + bNoSeasonalSkins=false +} diff --git a/Engine/Classes/OnlineSubsystem.uc b/Engine/Classes/OnlineSubsystem.uc index 5444ea8..2443313 100644 --- a/Engine/Classes/OnlineSubsystem.uc +++ b/Engine/Classes/OnlineSubsystem.uc @@ -2314,6 +2314,7 @@ static function DumpGameSettings(const OnlineGameSettings GameSettings) `Log(" bAllowJoinViaPresence: "$GameSettings.bAllowJoinViaPresence); `Log(" bAllowJoinViaPresenceFriendsOnly: "$GameSettings.bAllowJoinViaPresenceFriendsOnly); `Log(" GameState: "$GameSettings.GameState); + `Log(" bNoSeasonalSkins: "$GameSettings.bNoSeasonalSkins); } /** diff --git a/KFGame/Classes/KFAfflictionAdvanced.uc b/KFGame/Classes/KFAfflictionAdvanced.uc index 80604d9..c64489f 100644 --- a/KFGame/Classes/KFAfflictionAdvanced.uc +++ b/KFGame/Classes/KFAfflictionAdvanced.uc @@ -15,7 +15,7 @@ var float Duration; var bool bIsActive; /** Default Effect Socket */ -var protected name EffectSocketName; +var name EffectSocketName; /** Copy incap settings we're going to need */ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) @@ -25,11 +25,11 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) } /** */ -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { if ( !bIsActive ) { - super.Activate(); + super.Activate(InstigatorPerk, DamageType); PawnOwner.SetTimer(Duration, false, nameof(DeActivate), self); bIsActive = true; } diff --git a/KFGame/Classes/KFAfflictionBase.uc b/KFGame/Classes/KFAfflictionBase.uc index ea8d47a..d20b7c3 100644 --- a/KFGame/Classes/KFAfflictionBase.uc +++ b/KFGame/Classes/KFAfflictionBase.uc @@ -42,12 +42,17 @@ var float LastDissipationTime; /** Enable debug logging */ var bool bDebug; +/** Cache the affliction type for passing to Seasonal objectives */ +var EAfflictionType AfflictionType; + /** */ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) { PawnOwner = P; MonsterOwner = KFPawn_Monster(P); + AfflictionType = Type; + Cooldown = P.IncapSettings[Type].Cooldown; if ( bNeedsTick && DissipationRate > 0 ) @@ -57,7 +62,7 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) } /** */ -function Accrue(float InPower, optional class DamageType = none) +function Accrue(float InPower, KFPerk InstigatorPerk, optional class DamageType = none) { // total immunity during cooldown if ( LastActivationTime > 0 && `TimeSinceEx(PawnOwner, LastActivationTime) < Cooldown ) @@ -80,14 +85,14 @@ function Accrue(float InPower, optional class DamageType = none) CurrentStrength = fClamp(CurrentStrength + InPower, InPower, INCAP_THRESHOLD); if ( CurrentStrength >= INCAP_THRESHOLD ) { - Activate(DamageType); + Activate(InstigatorPerk, DamageType); } `log(Class.Name@"Added="$InPower@"NewStrength="$CurrentStrength, bDebug); } /** */ -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { if ( SpecialMove != SM_None ) { @@ -100,6 +105,14 @@ function Activate(optional class DamageType = none) LastActivationTime = PawnOwner.WorldInfo.TimeSeconds; `log(Class.Name@"was activated", bDebug); + + if (InstigatorPerk != none) + { + if (InstigatorPerk.OwnerPC != none) + { + InstigatorPerk.OwnerPC.AddAfflictionCaused(AfflictionType); + } + } } /** For subclass special instructions */ diff --git a/KFGame/Classes/KFAfflictionManager.uc b/KFGame/Classes/KFAfflictionManager.uc index 5aecf8f..689f138 100644 --- a/KFGame/Classes/KFAfflictionManager.uc +++ b/KFGame/Classes/KFAfflictionManager.uc @@ -469,7 +469,7 @@ function AccrueAffliction(EAfflictionType Type, float InPower, optional EHitZone if ( InPower > 0 ) { - Afflictions[Type].Accrue(InPower, DamageType); + Afflictions[Type].Accrue(InPower, InstigatorPerk, DamageType); } } @@ -510,7 +510,7 @@ function AccrueAfflictionMicrowave(EAfflictionType Type, float InPower, bool bHa if ( InPower > 0 ) { KFAffliction_Microwave(Afflictions[Type]).bHasToSpawnFire = bHasToSpawnFire; - Afflictions[Type].Accrue(InPower); + Afflictions[Type].Accrue(InPower, InstigatorPerk); } } diff --git a/KFGame/Classes/KFAffliction_BigHead.uc b/KFGame/Classes/KFAffliction_BigHead.uc index d509277..bda60e6 100644 --- a/KFGame/Classes/KFAffliction_BigHead.uc +++ b/KFGame/Classes/KFAffliction_BigHead.uc @@ -44,11 +44,11 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) CurrentMaxStack = bIsBobbleHeadMode ? MaxStackBobbleHead : MaxStack; } -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { if (CurrentStack < CurrentMaxStack) { - Super.Activate(); + Super.Activate(InstigatorPerk, DamageType); if (!bIsBobbleHeadMode && !bIsShrunkenHeads) { diff --git a/KFGame/Classes/KFAffliction_Bleed.uc b/KFGame/Classes/KFAffliction_Bleed.uc index 5ac3fef..d75186a 100644 --- a/KFGame/Classes/KFAffliction_Bleed.uc +++ b/KFGame/Classes/KFAffliction_Bleed.uc @@ -115,9 +115,9 @@ function float GetAttackSpeedModifier() return 1.f; } -function Accrue(float InPower, optional class DamageType = none) +function Accrue(float InPower, KFPerk InstigatorPerk, optional class DamageType = none) { - super.Accrue(InPower); + super.Accrue(InPower, InstigatorPerk); if (PawnOwner != none) { PawnOwner.SetAfflictionSpeedModifier(); diff --git a/KFGame/Classes/KFAffliction_EMP.uc b/KFGame/Classes/KFAffliction_EMP.uc index 790d2f3..c0a3991 100644 --- a/KFGame/Classes/KFAffliction_EMP.uc +++ b/KFGame/Classes/KFAffliction_EMP.uc @@ -21,13 +21,13 @@ var protected ParticleSystemComponent EMPDisruptEffect; var protected name EMPDisruptSocketName; /** Sound to play when this pawn has been EMP'd */ -var protected AkEvent OnEMPSound; +var AkEvent OnEMPSound; /** Sound to play when this pawn stops being EMP'd */ -var protected AkEvent OnEMPEndSound; +var AkEvent OnEMPEndSound; -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { - Super.Activate(); + Super.Activate(InstigatorPerk, DamageType); SetEMPPanicked(true); } diff --git a/KFGame/Classes/KFAffliction_EMPDisrupt.uc b/KFGame/Classes/KFAffliction_EMPDisrupt.uc index 4b8901a..9088936 100644 --- a/KFGame/Classes/KFAffliction_EMPDisrupt.uc +++ b/KFGame/Classes/KFAffliction_EMPDisrupt.uc @@ -20,9 +20,9 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) DisruptCooldown = P.IncapSettings[Type].ChildAfflictionCooldown; } -function Accrue(float InPower, optional class DamageType = none) +function Accrue(float InPower, KFPerk InstigatorPerk, optional class DamageType = none) { - Super.Accrue(InPower); + Super.Accrue(InPower, InstigatorPerk, DamageType); if (!PawnOwner.IsTimerActive(nameof(Timer_DisruptCooldown), self)) { diff --git a/KFGame/Classes/KFAffliction_Fire.uc b/KFGame/Classes/KFAffliction_Fire.uc index 76499ee..4efd7e7 100644 --- a/KFGame/Classes/KFAffliction_Fire.uc +++ b/KFGame/Classes/KFAffliction_Fire.uc @@ -44,12 +44,12 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) } } -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { // fire can accrue after death, but cannot trigger panic if ( !PawnOwner.bPlayedDeath ) { - Super.Activate(); + Super.Activate(InstigatorPerk, DamageType); SetFirePanicked(true); } } diff --git a/KFGame/Classes/KFAffliction_HeavyRecovery.uc b/KFGame/Classes/KFAffliction_HeavyRecovery.uc index 84bdae7..c395411 100644 --- a/KFGame/Classes/KFAffliction_HeavyRecovery.uc +++ b/KFGame/Classes/KFAffliction_HeavyRecovery.uc @@ -9,7 +9,7 @@ class KFAffliction_HeavyRecovery extends KFAfflictionBase; /** */ -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { // Attempt to interrupt the special move if( PawnOwner.SpecialMove != SM_None ) @@ -23,7 +23,7 @@ function Activate(optional class DamageType = none) PawnOwner.MyKFAIC.DoPauseAI( PawnOwner.DamageRecoveryTimeHeavy, true ); } - Super.Activate(); + Super.Activate(InstigatorPerk, DamageType); } defaultproperties diff --git a/KFGame/Classes/KFAffliction_Knockdown.uc b/KFGame/Classes/KFAffliction_Knockdown.uc index 546c402..8b30176 100644 --- a/KFGame/Classes/KFAffliction_Knockdown.uc +++ b/KFGame/Classes/KFAffliction_Knockdown.uc @@ -9,14 +9,14 @@ class KFAffliction_Knockdown extends KFAfflictionBase; /** */ -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { ActivateKnockdown(DamageType, PawnOwner.HitFxInfo.HitLocation, PawnOwner.DecodeUnitVector( PawnOwner.HitFxInfo.EncodedHitDirection ), PawnOwner.HitFxInfo.HitBoneIndex); - Super.Activate(); + Super.Activate(InstigatorPerk, DamageType); } /** Apply a knockdown (on hit) to this character */ diff --git a/KFGame/Classes/KFAffliction_MediumRecovery.uc b/KFGame/Classes/KFAffliction_MediumRecovery.uc index b6fe5ea..f2e2f81 100644 --- a/KFGame/Classes/KFAffliction_MediumRecovery.uc +++ b/KFGame/Classes/KFAffliction_MediumRecovery.uc @@ -9,7 +9,7 @@ class KFAffliction_MediumRecovery extends KFAfflictionBase; /** */ -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { // Attempt to interrupt the special move if( PawnOwner.SpecialMove != SM_None ) @@ -23,7 +23,7 @@ function Activate(optional class DamageType = none) PawnOwner.MyKFAIC.DoPauseAI( PawnOwner.DamageRecoveryTimeHeavy, true ); } - Super.Activate(); + Super.Activate(InstigatorPerk, DamageType); } defaultproperties diff --git a/KFGame/Classes/KFAffliction_Microwave.uc b/KFGame/Classes/KFAffliction_Microwave.uc index aabe195..8058b00 100644 --- a/KFGame/Classes/KFAffliction_Microwave.uc +++ b/KFGame/Classes/KFAffliction_Microwave.uc @@ -30,9 +30,9 @@ var protected AkEvent OnSteamSound; /** Sound to play when this pawn stops being on fire */ var protected AkEvent OnSteamEndSound; -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { - Super.Activate(); + Super.Activate(InstigatorPerk, DamageType); SetMicrowavePanicked(true); } diff --git a/KFGame/Classes/KFAffliction_Poison.uc b/KFGame/Classes/KFAffliction_Poison.uc index 36c910b..11485cb 100644 --- a/KFGame/Classes/KFAffliction_Poison.uc +++ b/KFGame/Classes/KFAffliction_Poison.uc @@ -8,9 +8,9 @@ //============================================================================= class KFAffliction_Poison extends KFAfflictionAdvanced; -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { - Super.Activate(); + Super.Activate(InstigatorPerk, DamageType); SetPoisoned(true); } diff --git a/KFGame/Classes/KFAffliction_Shrink.uc b/KFGame/Classes/KFAffliction_Shrink.uc index 9c91f3e..d90f655 100644 --- a/KFGame/Classes/KFAffliction_Shrink.uc +++ b/KFGame/Classes/KFAffliction_Shrink.uc @@ -53,7 +53,7 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) } } -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { local float StackModifier; @@ -64,7 +64,7 @@ function Activate(optional class DamageType = none) if (CurrentEffect < MaxEffect) { - Super.Activate(); + Super.Activate(InstigatorPerk, DamageType); StackModifier = 1.0f; diff --git a/KFGame/Classes/KFAffliction_Snare.uc b/KFGame/Classes/KFAffliction_Snare.uc index 7abb163..b69e9b8 100644 --- a/KFGame/Classes/KFAffliction_Snare.uc +++ b/KFGame/Classes/KFAffliction_Snare.uc @@ -24,11 +24,11 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) } /** */ -function Activate(optional class DamageType = none) +function Activate(KFPerk InstigatorPerk, optional class DamageType = none) { if( !bIsActive ) { - super.Activate(); + super.Activate(InstigatorPerk, DamageType); PawnOwner.SetTimer(Duration, false, nameof(DeActivate), self); bIsActive = true; PawnOwner.SetAfflictionSpeedModifier(); diff --git a/KFGame/Classes/KFCheatManager.uc b/KFGame/Classes/KFCheatManager.uc index 277805c..c291120 100644 --- a/KFGame/Classes/KFCheatManager.uc +++ b/KFGame/Classes/KFCheatManager.uc @@ -6455,6 +6455,7 @@ simulated function ClearFakeDramaEvent() exec function DBJump() { local vector UsedKickMomentum; + local KFPlayerController KFPC; // Push the player back when they fire both barrels if (Pawn != none ) @@ -6473,6 +6474,12 @@ exec function DBJump() } Pawn.AddVelocity(UsedKickMomentum,Pawn.Location,none); + + KFPC = KFPlayerController(Pawn.Controller); + if (KFPC != none) + { + KFPC.SetShotgunJump(true); + } } } diff --git a/KFGame/Classes/KFCommon_LocalizedStrings.uc b/KFGame/Classes/KFCommon_LocalizedStrings.uc index 6c0b448..708a403 100644 --- a/KFGame/Classes/KFCommon_LocalizedStrings.uc +++ b/KFGame/Classes/KFCommon_LocalizedStrings.uc @@ -32,6 +32,7 @@ var localized array ServerTypeStrings; var localized array PermissionStrings; var localized array ConsolePermissionStrings; var localized array ModeStrings; +var localized array AllowSeasonalSkinsStrings; var localized string TeamSwappedString; var localized string NoPreferenceString; @@ -187,7 +188,21 @@ static function array GetPermissionStringsArray(bool bConsoleBuild) { return default.PermissionStrings; } +} +static function string GetAllowSeasonalSkinsString( float Index ) +{ + if( 0 < default.AllowSeasonalSkinsStrings.length && Index < default.AllowSeasonalSkinsStrings.length ) + { + return default.AllowSeasonalSkinsStrings[Index]; + } + + return default.NoPreferenceString; +} + +static function array GetAllowSeasonalSkinsStringsArray() +{ + return default.AllowSeasonalSkinsStrings; } static function array GetGameModeStringsArray() diff --git a/KFGame/Classes/KFDT_HVStormCannonSpread.uc b/KFGame/Classes/KFDT_HVStormCannonSpread.uc new file mode 100644 index 0000000..c62a12c --- /dev/null +++ b/KFGame/Classes/KFDT_HVStormCannonSpread.uc @@ -0,0 +1,26 @@ +//============================================================================= +// KFDT_HVStormCannonSpread +//============================================================================= +// Damage caused by HV Storm Cannon Spread +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2015 Tripwire Interactive LLC +//============================================================================= + +class KFDT_HVStormCannonSpread extends KFDamageType + abstract; + +defaultproperties +{ + bCausedByWorld=false + bArmorStops=false + + KnockdownPower=20 + StunPower=50 + StumblePower=200 + GunHitPower=150 + MeleeHitPower=100 + EMPPower=50 + + WeaponDef=class'KFWeapDef_HVStormCannon' +} \ No newline at end of file diff --git a/KFGame/Classes/KFDT_Toxic_HRG_MedicMissile.uc b/KFGame/Classes/KFDT_Toxic_HRG_MedicMissile.uc new file mode 100644 index 0000000..4eeba9a --- /dev/null +++ b/KFGame/Classes/KFDT_Toxic_HRG_MedicMissile.uc @@ -0,0 +1,23 @@ +//============================================================================= +// KFDT_Toxic_HRG_MedicMissile +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Toxic_HRG_MedicMissile extends KFDT_Toxic + abstract + hidedropdown; + +defaultproperties +{ + DoT_Type=DOT_Toxic + DoT_Duration=4.0 + DoT_Interval=1.0 + DoT_DamageScale=0.2 + + PoisonPower=100 + + ModifierPerkList(0)=class'KFPerk_FieldMedic' + + WeaponDef=class'KFWeapDef_HRG_MedicMissile' +} \ No newline at end of file diff --git a/KFGame/Classes/KFExplosionActor.uc b/KFGame/Classes/KFExplosionActor.uc index b4835cb..9a72633 100644 --- a/KFGame/Classes/KFExplosionActor.uc +++ b/KFGame/Classes/KFExplosionActor.uc @@ -308,6 +308,7 @@ simulated function DrawDebug() local Color C; local float Angle; local float ClotKillRadius, HalfFalloffRadius; + local float SafeDamage, SafeDamageFallOffExponent; FlushPersistentDebugLines(); @@ -324,13 +325,16 @@ simulated function DrawDebug() } else { + SafeDamage = ExplosionTemplate.Damage > 0.f ? ExplosionTemplate.Damage : 1.f; + SafeDamageFallOffExponent = ExplosionTemplate.DamageFalloffExponent > 0.f ? ExplosionTemplate.DamageFalloffExponent : 1.f; + DrawDebugSphere(Location, ExplosionTemplate.DamageRadius, 10, 255, 128, 16, TRUE); - ClotKillRadius = ExplosionTemplate.DamageRadius * (1.f - FClamp((100 / ExplosionTemplate.Damage) ** (1/ExplosionTemplate.DamageFallOffExponent), 0.f, 1.f)); + ClotKillRadius = ExplosionTemplate.DamageRadius * (1.f - FClamp((100 / SafeDamage) ** (1 / SafeDamageFallOffExponent), 0.f, 1.f)); DrawDebugSphere(Location, ClotKillRadius, 10, 255, 0, 0, TRUE); - HalfFalloffRadius = ExplosionTemplate.DamageRadius * (1.f - FClamp((0.5 ** (1.f/ExplosionTemplate.DamageFalloffExponent)), 0.f, 1.f)); + HalfFalloffRadius = ExplosionTemplate.DamageRadius * (1.f - FClamp((0.5 ** (1.f / SafeDamageFallOffExponent)), 0.f, 1.f)); DrawDebugSphere( Location, HalfFalloffRadius, 10, 255, 63, 0, true ); } diff --git a/KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Xmas2022.uc b/KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Xmas2022.uc new file mode 100644 index 0000000..2b55d9b --- /dev/null +++ b/KFGame/Classes/KFGFXSpecialEventObjectivesContainer_Xmas2022.uc @@ -0,0 +1,27 @@ +class KFGFXSpecialEventObjectivesContainer_Xmas2022 extends KFGFxSpecialEventObjectivesContainer; + +function Initialize(KFGFxObject_Menu NewParentMenu) +{ + super.Initialize(NewParentMenu); +} + +DefaultProperties +{ + ObjectiveIconURLs[0] = "Xmas2022_UI.UI_Objectives_Xmas_2022_Frozen_Hearts" // Freeze 500 Zeds using ice arsenal + ObjectiveIconURLs[1] = "Xmas2022_UI.Black_Weekly" // Complete the Weekly on Crash + ObjectiveIconURLs[2] = "Xmas2022_UI.UI_Objective_Xmas_2022_Shotgun_Jump" // Use 4 Boomstick Jumps in a same match on Crash + ObjectiveIconURLs[3] = "Xmas2022_UI.UI_Objective_Xmas_2022_Not_a_Snowball" // Hit 3 Zeds with a shot of HRG Ballistic Bouncer (15 times) + ObjectiveIconURLs[4] = "Xmas2022_UI.UI_Objective_Xmas_2022_Withstand_the_Tempest" // Complete wave 15 on Endless Hard or higher difficulty on Crash + + //defaults + AllCompleteRewardIconURL="CHR_TrainConductorUniform_Item_TEX.trainbackpack.trainconductorbackpack_precious" + ChanceDropIconURLs[0]="CHR_Cosmetic_XMAS_Item_TEX.Tickets.Krampus_Ticket" + ChanceDropIconURLs[1]="CHR_Cosmetic_XMAS_Item_TEX.Tickets.Krampus_Ticket_Golden" + IconURL="Xmas2022_UI.Polar_distress_small_logo" + + 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/KFGFxMenu_Inventory.uc b/KFGame/Classes/KFGFxMenu_Inventory.uc index 4595cdb..7c574bf 100644 --- a/KFGame/Classes/KFGFxMenu_Inventory.uc +++ b/KFGame/Classes/KFGFxMenu_Inventory.uc @@ -137,19 +137,51 @@ struct InventoryHelper // For ordering in weapon skins var int WeaponDef; var int Price; - //var string FullName; - var int SkinType; + var int SkinType; // also used in Cosmetics var ItemRarity Rarity; var int Quality; + // For ordering cosmetics + var string CosmeticType; + + // For ordering crafting + var int CraftingType; + var int CraftingRarity; + var int CraftingTicketType; + // For ordering items - var string KeyName; var bool IsKey; }; -var array SkinListWeaponsSearchCache; -var array SkinListOrderedCache; -var bool NeedToRegenerateSkinList; +struct WeaponSkinListCacheState +{ + var array SearchCache; + + var array OrderedCache; + + var bool NeedToRegenerate; + + var EInventoryWeaponType_Filter WeaponTypeFilter; + var int PerkIndexFilter; + var ItemRarity RarityFilter; + + structdefaultproperties + { + NeedToRegenerate = false + + WeaponTypeFilter = EInvWT_None + PerkIndexFilter = 0 + RarityFilter = ITR_NONE + } +}; + +var WeaponSkinListCacheState WeaponSkinListCache; + +var array CosmeticSkinListSearchCache; + +var array CraftingListSearchCache; + +var array ItemListSearchCache; struct ByTypeItemsHelper { @@ -291,7 +323,7 @@ final function int Crc(coerce string Text) return CrcValue; } -delegate int SortSkinList(InventoryHelper A, InventoryHelper B) +delegate int SortWeaponSkinList(InventoryHelper A, InventoryHelper B) { /** Format: Compare lower ? -1 : (Compare upper) : 1 : (Equal case, repeat formula with the next sort condition) */ return A.Price > B.Price ? -1 : (A.Price < B.Price ? 1 : ( @@ -305,6 +337,26 @@ delegate int SortSkinList(InventoryHelper A, InventoryHelper B) )); } +delegate int SortCosmeticsList(InventoryHelper A, InventoryHelper B) +{ + /** Format: Compare lower ? -1 : (Compare upper) : 1 : (Equal case, repeat formula with the next sort condition) */ + return A.CosmeticType > B.CosmeticType ? -1 : (A.CosmeticType < B.CosmeticType ? 1 : ( + A.SkinType < B.SkinType ? -1 : (A.SkinType > B.SkinType ? 1 : ( + A.Rarity > B.Rarity ? -1 : (A.Rarity < B.Rarity ? 1 : 1) + )) + )); +} + +delegate int SortCraftingList(InventoryHelper A, InventoryHelper B) +{ + /** Format: Compare lower ? -1 : (Compare upper) : 1 : (Equal case, repeat formula with the next sort condition) */ + return A.CraftingType > B.CraftingType ? -1 : (A.CraftingType < B.CraftingType ? 1 : ( + A.CraftingRarity > B.CraftingRarity ? -1 : (A.CraftingRarity < B.CraftingRarity ? 1 : ( + A.CraftingTicketType > B.CraftingTicketType ? -1 : (A.CraftingTicketType < B.CraftingTicketType ? 1 : 1) + )) + )); +} + delegate int SortItemList(InventoryHelper A, InventoryHelper B) { if (A.IsKey && B.IsKey) @@ -327,7 +379,7 @@ delegate int SortItemList(InventoryHelper A, InventoryHelper B) function InitInventory() { - local int i, j, z, ItemIndex, HelperIndex, WeaponItemID, SearchWeaponSkinIndex, SearchKeyKeywordIndex; + local int i, j, z, ItemIndex, HelperIndex, ItemID, SearchIndex; local ItemProperties TempItemDetailsHolder; local GFxObject ItemArray, ItemObject; local bool bActiveItem; @@ -335,7 +387,7 @@ function InitInventory() local InventoryHelper HelperItem; local array ExchangeRules; local class WeaponDef; - local string SkinType; + local string SkinType, CosmeticType, KeyType; local GFxObject PendingItem; @@ -378,40 +430,43 @@ function InitInventory() if (TempItemDetailsHolder.Type == ITP_WeaponSkin) { - // Copy required stuff HelperItem.Rarity = TempItemDetailsHolder.Rarity; HelperItem.Quality = TempItemDetailsHolder.Quality; if (bool(OnlineSub.CurrentInventory[i].NewlyAdded)) { - NeedToRegenerateSkinList = true; + WeaponSkinListCache.NeedToRegenerate = true; } - // Search on the cache, to speed up - WeaponItemID = SkinListWeaponsSearchCache.Find('ItemDefinition', HelperItem.ItemDefinition); + // Now find the name of the weapon skin - if (WeaponItemID != INDEX_NONE) + // Search on the cache, to speed up + ItemID = WeaponSkinListCache.SearchCache.Find('ItemDefinition', HelperItem.ItemDefinition); + + if (ItemID != INDEX_NONE) { - HelperItem.WeaponDef = SkinListWeaponsSearchCache[WeaponItemID].WeaponDef; - HelperItem.Price = SkinListWeaponsSearchCache[WeaponItemID].Price; - HelperItem.SkinType = SkinListWeaponsSearchCache[WeaponItemID].SkinType; + HelperItem.WeaponDef = WeaponSkinListCache.SearchCache[ItemID].WeaponDef; + HelperItem.Price = WeaponSkinListCache.SearchCache[ItemID].Price; + HelperItem.SkinType = WeaponSkinListCache.SearchCache[ItemID].SkinType; } else { + // Skin Type + // Get right part of the string without from the first "| " - SearchWeaponSkinIndex = InStr(TempItemDetailsHolder.Name, "|"); - SkinType = Right(TempItemDetailsHolder.Name, Len(TempItemDetailsHolder.Name) - SearchWeaponSkinIndex - 2); + SearchIndex = InStr(TempItemDetailsHolder.Name, "|"); + SkinType = Right(TempItemDetailsHolder.Name, Len(TempItemDetailsHolder.Name) - SearchIndex - 2); // Get the left part of the string without the next "| " - SearchWeaponSkinIndex = InStr(SkinType, "|"); + SearchIndex = InStr(SkinType, "|"); // Store as CRC, that speeds up comparisons later - HelperItem.SkinType = CrC(Left(SkinType, SearchWeaponSkinIndex)); + HelperItem.SkinType = CrC(Left(SkinType, SearchIndex)); - WeaponItemID = class'KFWeaponSkinList'.default.Skins.Find('Id', HelperItem.ItemDefinition); + ItemID = class'KFWeaponSkinList'.default.Skins.Find('Id', HelperItem.ItemDefinition); - if (WeaponItemID != INDEX_NONE) + if (ItemID != INDEX_NONE) { - WeaponDef = class'KFWeaponSkinList'.default.Skins[WeaponItemID].WeaponDef; + WeaponDef = class'KFWeaponSkinList'.default.Skins[ItemID].WeaponDef; // All Weapons start by KFGameContent.KFWeap_ Skip that prefix. // Store as CRC, that speeds up comparisons later @@ -424,12 +479,202 @@ function InitInventory() HelperItem.Price = 0; } - SkinListWeaponsSearchCache.AddItem(HelperItem); + WeaponSkinListCache.SearchCache.AddItem(HelperItem); } } - else + else if (TempItemDetailsHolder.Type == ITP_CharacterSkin) { - HelperItem.KeyName = TempItemDetailsHolder.KeyName; + // Search on the cache, to speed up + ItemID = CosmeticSkinListSearchCache.Find('ItemDefinition', HelperItem.ItemDefinition); + + if (ItemID != INDEX_NONE) + { + HelperItem.Rarity = CosmeticSkinListSearchCache[ItemID].Rarity; + HelperItem.CosmeticType = CosmeticSkinListSearchCache[ItemID].CosmeticType; + HelperItem.SkinType = CosmeticSkinListSearchCache[ItemID].SkinType; + } + else + { + HelperItem.Rarity = TempItemDetailsHolder.Rarity; + + // Cosmetic Type + + // Get left part of the string from the first "| " + SearchIndex = InStr(TempItemDetailsHolder.Name, "|"); + + // If we can't find the substring the equipment doesn't fit the pattern, we use the whole string as Cosmetic Type + if (SearchIndex < 0) + { + CosmeticType = TempItemDetailsHolder.Name; + HelperItem.CosmeticType = CosmeticType; + + HelperItem.SkinType = 0; + } + else + { + CosmeticType = Left(TempItemDetailsHolder.Name, SearchIndex); + HelperItem.CosmeticType = CosmeticType; + + // Skin Type + + // Get right part of the string without from the first "| " + SearchIndex = InStr(TempItemDetailsHolder.Name, "|"); + SkinType = Right(TempItemDetailsHolder.Name, Len(TempItemDetailsHolder.Name) - SearchIndex - 2); + + // Get the left part of the string without the next "| " + SearchIndex = InStr(SkinType, "|"); + SkinType = Left(SkinType, SearchIndex); + + // Store as CRC, that speeds up comparisons later + HelperItem.SkinType = CrC(SkinType); + } + + CosmeticSkinListSearchCache.AddItem(HelperItem); + } + } + else if (TempItemDetailsHolder.Type == ITP_CraftingComponent) + { + ItemId = CraftingListSearchCache.Find('ItemDefinition', HelperItem.ItemDefinition); + + if (ItemID != INDEX_NONE) + { + HelperItem.CraftingType = CraftingListSearchCache[ItemID].CraftingType; + HelperItem.CraftingRarity = CraftingListSearchCache[ItemID].CraftingRarity; + HelperItem.CraftingTicketType = CraftingListSearchCache[ItemID].CraftingTicketType; + } + else + { + HelperItem.CraftingType = 999; + HelperItem.CraftingRarity = 999; + HelperItem.CraftingTicketType = 0; + + // We don't have information to stick to.. so we have to search on the Name.. we use KeyName, as it contains the unmodified language Key + + // Type + + SearchIndex = InStr(TempItemDetailsHolder.KeyName, ":"); + + KeyType = Left(TempItemDetailsHolder.KeyName, SearchIndex); + + SearchIndex = InStr(KeyType, "CosmeticMaterial"); + if (SearchIndex != -1) + { + HelperItem.CraftingType = 0; + } + else + { + SearchIndex = InStr(KeyType, "WeaponSkinMaterial"); + if (SearchIndex != -1) + { + HelperItem.CraftingType = 1; + } + else + { + SearchIndex = InStr(KeyType, "VaultCraftingMaterial"); + if (SearchIndex != -1) + { + HelperItem.CraftingType = 2; + } + } + } + + // Rarity + + SearchIndex = InStr(KeyType, "Uncommon"); + if (SearchIndex != -1) + { + HelperItem.CraftingRarity = 1; + } + else + { + SearchIndex = InStr(KeyType, "Common"); + if (SearchIndex != -1) + { + HelperItem.CraftingRarity = 0; + } + else + { + SearchIndex = InStr(KeyType, "Rare"); + if (SearchIndex != -1) + { + HelperItem.CraftingRarity = 2; + } + else + { + SearchIndex = InStr(KeyType, "Exceptional"); + if (SearchIndex != -1) + { + HelperItem.CraftingRarity = 3; + } + } + } + } + + // Ticket Type + + SearchIndex = InStr(KeyType, "CyberPunk"); + if (SearchIndex != -1) + { + HelperItem.CraftingTicketType = 0; + } + else + { + SearchIndex = InStr(KeyType, "Sideshow"); + if (SearchIndex != -1) + { + HelperItem.CraftingTicketType = 1; + } + else + { + SearchIndex = InStr(KeyType, "Hllwn"); + if (SearchIndex != -1) + { + HelperItem.CraftingTicketType = 2; + } + else + { + SearchIndex = InStr(KeyType, "Christmas"); + if (SearchIndex != -1) + { + HelperItem.CraftingTicketType = 3; + } + } + } + } + + CraftingListSearchCache.AddItem(HelperItem); + } + } + else if (TempItemDetailsHolder.Type == ITP_KeyCrate) + { + ItemId = ItemListSearchCache.Find('ItemDefinition', HelperItem.ItemDefinition); + + if (ItemID != INDEX_NONE) + { + HelperItem.IsKey = ItemListSearchCache[ItemID].IsKey; + } + else + { + // We have to distinguish if the Item is a KEY or not, we use KeyName, as it contains the unmodified language Key + + // KeyName is something like : "NameItem:KeyCrate", we first remove the part from the : to the right + SearchIndex = InStr(TempItemDetailsHolder.KeyName, ":"); + KeyType = Left(TempItemDetailsHolder.KeyName, SearchIndex); + + // Then we search if the name of the Item contains "Key" + SearchIndex = InStr(KeyType, "Key"); + + if (SearchIndex != -1) + { + HelperItem.IsKey = true; + } + else + { + HelperItem.IsKey = false; + } + + ItemListSearchCache.AddItem(HelperItem); + } } ByTypeItems[TempItemDetailsHolder.Type].ItemsOnType.AddItem(HelperItem); @@ -481,30 +726,36 @@ function InitInventory() if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_WeaponSkins) { // If need to refresh... we regenerate the list, if not reuse our Cache - NeedToRegenerateSkinList = NeedToRegenerateSkinList || ByTypeItems[ITP_WeaponSkin].ItemsOnType.Length != SkinListOrderedCache.Length; - if (NeedToRegenerateSkinList) + if (WeaponSkinListCache.NeedToRegenerate + || WeaponSkinListCache.WeaponTypeFilter != CurrentWeaponTypeFilter + || WeaponSkinListCache.PerkIndexFilter != CurrentPerkIndexFilter + || WeaponSkinListCache.RarityFilter != CurrentRarityFilter + || ByTypeItems[ITP_WeaponSkin].ItemsOnType.Length != WeaponSkinListCache.OrderedCache.Length) { - NeedToRegenerateSkinList = false; + WeaponSkinListCache.NeedToRegenerate = false; + + WeaponSkinListCache.WeaponTypeFilter = CurrentWeaponTypeFilter; + WeaponSkinListCache.PerkIndexFilter = CurrentPerkIndexFilter; + WeaponSkinListCache.RarityFilter = CurrentRarityFilter; // We want to order by Price - Weapon Def - Rarity - Quality - // So we order inverse that way we keep the final list with the design intention - ByTypeItems[ITP_WeaponSkin].ItemsOnType.Sort(SortSkinList); + ByTypeItems[ITP_WeaponSkin].ItemsOnType.Sort(SortWeaponSkinList); - SkinListOrderedCache = ByTypeItems[ITP_WeaponSkin].ItemsOnType; + WeaponSkinListCache.OrderedCache = ByTypeItems[ITP_WeaponSkin].ItemsOnType; - /*`Log("----------");*/ + /*`Log("----------"); - /*for (i = 0 ; i < SkinListOrderedCache.Length; i++) + for (i = 0 ; i < ByTypeItems[ITP_WeaponSkin].ItemsOnType.Length; i++) { - `Log("ID : " $SkinListOrderedCache[i].ItemDefinition); - `Log("Weapon Def : " $SkinListOrderedCache[i].WeaponDef); - `Log("Price : " $SkinListOrderedCache[i].Price); - `Log("Full Name : " $SkinListOrderedCache[i].FullName); - `Log("Skin : " $SkinListOrderedCache[i].SkinType); - `Log("Rarity : " $SkinListOrderedCache[i].Rarity); - `Log("Quality : " $SkinListOrderedCache[i].Quality); + `Log("ID : " $ByTypeItems[ITP_WeaponSkin].ItemsOnType[i].ItemDefinition); + `Log("Weapon Def : " $ByTypeItems[ITP_WeaponSkin].ItemsOnType[i].WeaponDef); + `Log("Price : " $ByTypeItems[ITP_WeaponSkin].ItemsOnType[i].Price); + `Log("Full Name : " $ByTypeItems[ITP_WeaponSkin].ItemsOnType[i].FullName); + `Log("Skin : " $ByTypeItems[ITP_WeaponSkin].ItemsOnType[i].SkinType); + `Log("Rarity : " $ByTypeItems[ITP_WeaponSkin].ItemsOnType[i].Rarity); + `Log("Quality : " $ByTypeItems[ITP_WeaponSkin].ItemsOnType[i].Quality); `Log("----------"); } @@ -514,35 +765,58 @@ function InitInventory() { //`Log("USING SKIN LIST CACHE!!!"); - ByTypeItems[ITP_WeaponSkin].ItemsOnType = SkinListOrderedCache; + ByTypeItems[ITP_WeaponSkin].ItemsOnType = WeaponSkinListCache.OrderedCache; } } + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_Cosmetics) + { + ByTypeItems[ITP_CharacterSkin].ItemsOnType.Sort(SortCosmeticsList); + + /*`Log("----------"); + + for (i = 0 ; i < ByTypeItems[ITP_CharacterSkin].ItemsOnType.Length; i++) + { + `Log("Cosmetic Name : " $ByTypeItems[ITP_CharacterSkin].ItemsOnType[i].CosmeticType); + `Log("Skin : " $ByTypeItems[ITP_CharacterSkin].ItemsOnType[i].SkinType); + `Log("Rarity : " $ByTypeItems[ITP_CharacterSkin].ItemsOnType[i].Rarity); + `Log("----------"); + } + + `Log("----------");*/ + } + + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_CraftingMats) + { + ByTypeItems[ITP_CraftingComponent].ItemsOnType.Sort(SortCraftingList); + + /*`Log("----------"); + + for (i = 0 ; i < ByTypeItems[ITP_CraftingComponent].ItemsOnType.Length; i++) + { + `Log("Crafting Type : " $ByTypeItems[ITP_CraftingComponent].ItemsOnType[i].CraftingType); + `Log("Rarity : " $ByTypeItems[ITP_CraftingComponent].ItemsOnType[i].CraftingRarity); + `Log("Ticket Type : " $ByTypeItems[ITP_CraftingComponent].ItemsOnType[i].CraftingTicketType); + `Log("----------"); + } + + `Log("----------");*/ + } + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_Consumables) { // Consumables is the type for the "Items" category on the UI + ByTypeItems[ITP_KeyCrate].ItemsOnType.Sort(SortItemList); - // First we have to distinguish if the Item is a KEY or not - for (i = 0; i < ByTypeItems[ITP_KeyCrate].ItemsOnType.Length; i++) + /*`Log("----------"); + + for (i = 0 ; i < ByTypeItems[ITP_KeyCrate].ItemsOnType.Length; i++) { - // KeyName is something like : "NameItem:KeyCrate", we first remove the part from the : to the right - SearchKeyKeywordIndex = InStr(ByTypeItems[ITP_KeyCrate].ItemsOnType[i].KeyName, ":"); - ByTypeItems[ITP_KeyCrate].ItemsOnType[i].KeyName = Left(ByTypeItems[ITP_KeyCrate].ItemsOnType[i].KeyName, SearchKeyKeywordIndex); - - // Then we search if the name of the Item contains "Key" - SearchKeyKeywordIndex = InStr(ByTypeItems[ITP_KeyCrate].ItemsOnType[i].KeyName, "Key"); - - if (SearchKeyKeywordIndex != -1) - { - ByTypeItems[ITP_KeyCrate].ItemsOnType[i].IsKey = true; - } - else - { - ByTypeItems[ITP_KeyCrate].ItemsOnType[i].IsKey = false; - } + `Log("Is Key : " $ByTypeItems[ITP_KeyCrate].ItemsOnType[i].IsKey); + `Log("----------"); } - ByTypeItems[ITP_KeyCrate].ItemsOnType.Sort(SortItemList); + `Log("----------");*/ } //`Log("--------------------------------"); @@ -617,8 +891,7 @@ function FinishCraft() { SetVisible(true); - `Log("FinishCraft"); - NeedToRegenerateSkinList = true; + WeaponSkinListCache.NeedToRegenerate = true; } function SetMatineeColor(int ItemRarity) @@ -1198,7 +1471,8 @@ function Callback_Equip( int ItemDefinition ) } //refresh inventory - NeedToRegenerateSkinList = true; // need to regenerate as the equipped state changed + WeaponSkinListCache.NeedToRegenerate = true; // need to regenerate as the equipped state changed + InitInventory(); } @@ -1495,8 +1769,6 @@ function Callback_PreviewItem( int ItemDefinition ) defaultproperties { - NeedToRegenerateSkinList=false - CurrentWeaponTypeFilter = EInvWT_None; CurrentRarityFilter = ITR_NONE; diff --git a/KFGame/Classes/KFGFxMenu_Perks.uc b/KFGame/Classes/KFGFxMenu_Perks.uc index 1a09a11..48850f6 100644 --- a/KFGame/Classes/KFGFxMenu_Perks.uc +++ b/KFGame/Classes/KFGFxMenu_Perks.uc @@ -458,11 +458,16 @@ function Callback_ReadyClicked( bool bReady ) function Callback_PerkSelected(byte NewPerkIndex, bool bClickedIndex) { + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo( KFPC.WorldInfo.GRI ); + if (KFGRI != none && KFGRI.IsRandomPerkMode()) + { + return; + } + // bClickedIndex let's us know if the index was clicked and needs to be changed or if it was just selected and we should look at other perk info. PerkChanged(NewPerkIndex,bClickedIndex); - if(bClickedIndex) - { - } } function Callback_SkillSelected( byte TierIndex, byte SkillIndex ) diff --git a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc index 253a7eb..e92b39f 100644 --- a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc +++ b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc @@ -784,7 +784,7 @@ function DisplayMapCounterText(string MessageText, float DisplayTime) } } -function DisplayPriorityMessage(string InPrimaryMessageString, string InSecondaryMessageString, int LifeTime, optional EGameMessageType MessageType) +function DisplayPriorityMessage(string InPrimaryMessageString, string InSecondaryMessageString, int LifeTime, optional EGameMessageType MessageType, optional string SpecialIconPath) { local GFxObject PriorityMessageObject; @@ -797,6 +797,11 @@ function DisplayPriorityMessage(string InPrimaryMessageString, string InSecondar PriorityMessageObject.SetString("priorityTextSecondaryString", InSecondaryMessageString); PriorityMessageObject.SetInt("priorityTextDisplayTime", LifeTime); + if (SpecialIconPath != "") + { + PriorityMessageObject.SetString("specialIconPath", "img://"$SpecialIconPath); + } + PriorityMessageContainer.SetObject("priorityMessage", PriorityMessageObject); } } @@ -911,7 +916,11 @@ function int GetWaveTier() } else { - if (KFGRI.IsFinalWave()) + if (KFGRI.IsRandomPerkMode()) + { + return 5; + } + else if (KFGRI.IsFinalWave()) { return 3; } @@ -1393,6 +1402,11 @@ function Callback_ObjMessageFired() PlayObjectiveAudio(); } +function Callback_Log(string text) +{ + `Log("Received log from actionscript: " $text); +} + function Callback_PriorityMessageComplete() { local KFInterface_MapObjective ObjectiveInterface; @@ -1508,6 +1522,7 @@ function Callback_VoteKick(bool Vote) } } + DefaultProperties { SoundThemes.Add((ThemeName = "UI",Theme = UISoundTheme'SoundsShared_UI.SoundTheme_UI')) diff --git a/KFGame/Classes/KFGFxServerBrowser_Filters.uc b/KFGame/Classes/KFGFxServerBrowser_Filters.uc index 6e891b7..0a97c0f 100644 --- a/KFGame/Classes/KFGFxServerBrowser_Filters.uc +++ b/KFGame/Classes/KFGFxServerBrowser_Filters.uc @@ -13,13 +13,13 @@ class KFGFxServerBrowser_Filters extends KFGFxObject_Container config(UI); var KFGFxMenu_ServerBrowser ServerMenu; -var localized string NoPasswordString, NoMutatorsString, NotFullString, NotEmptyString, NoRankedStandardString, NoRankedCustomString, NoUnrankedString, DedicatedString, VACSecureString, InLobbyString, InProgressString, OnlyStockMapsString, OnlyCustomMapsString, LimitServerResultsString, NotServerExiledString; +var localized string NoPasswordString, NoMutatorsString, NotFullString, NotEmptyString, NoRankedStandardString, NoRankedCustomString, NoUnrankedString, DedicatedString, VACSecureString, InLobbyString, InProgressString, OnlyStockMapsString, OnlyCustomMapsString, LimitServerResultsString, NotServerExiledString, NoSeasonalSkinsString; var array FilterStrings; -var config Bool bNoPassword, bNoMutators, bNotFull, bNotEmpty, bUsesStats, bCustom, bDedicated, bVAC_Secure, bInLobby, bInProgress, bOnlyStockMaps, bOnlyCustomMaps, bLimitServerResults, bNoLocalAdmin; +var config Bool bNoPassword, bNoMutators, bNotFull, bNotEmpty, bUsesStats, bCustom, bDedicated, bVAC_Secure, bInLobby, bInProgress, bOnlyStockMaps, bOnlyCustomMaps, bLimitServerResults, bNoLocalAdmin, bNoSeasonalSkins; var config byte SavedGameModeIndex, SavedMapIndex, SavedDifficultyIndex, SavedLengthIndex, SavedPingIndex; -var Bool bNoPasswordPending, bNoMutatorsPending, bNotFullPending, bNotEmptyPending, bUsesStatsPending, bCustomPending, bDedicatedPending, bVAC_SecurePending, bInLobbyPending, bInProgressPending, bOnlyStockMapsPending, bOnlyCustomMapsPending, bLimitServerResultsPending, bNoLocalAdminPending; +var Bool bNoPasswordPending, bNoMutatorsPending, bNotFullPending, bNotEmptyPending, bUsesStatsPending, bCustomPending, bDedicatedPending, bVAC_SecurePending, bInLobbyPending, bInProgressPending, bOnlyStockMapsPending, bOnlyCustomMapsPending, bLimitServerResultsPending, bNoLocalAdminPending, bNoSeasonalSkinsPending; var byte SavedGameModeIndexPending, SavedMapIndexPending, SavedDifficultyIndexPending, SavedLengthIndexPending, SavedPingIndexPending; var transient string CachedMapName, CachedModeName; @@ -29,6 +29,8 @@ var transient array MapList; var int NumDifficultyStrings; +// if you change this also update ServerBrowserFilterContainer.as -> NUM_OF_FILTERS + enum EFilter_Key { NO_PASSWORD, @@ -46,6 +48,7 @@ enum EFilter_Key /*ONLY_STOCK_MAPS, //Not using for EA ONLY_CUSTOM_MAPS,*/ //Not using for EA NO_LOCAL_ADMIN, + NO_SEASONAL_SKINS, FILTERS_MAX, }; @@ -119,6 +122,7 @@ function InitFiltersArray() FilterStrings[NO_LOCAL_ADMIN] = NotServerExiledString; /*FilterStrings[ONLY_STOCK_MAPS] = OnlyStockMapsString; FilterStrings[ONLY_CUSTOM_MAPS] = OnlyCustomMapsString;*/ + FilterStrings[NO_SEASONAL_SKINS] = NoSeasonalSkinsString; } function LocalizeText() @@ -155,9 +159,22 @@ function LocalizeCheckBoxes() local byte i; local GFxObject FiltersArray; local GFxObject TempObject; + local bool bShowAllowSeasonalSkins; + + bShowAllowSeasonalSkins = true; + + if (ServerMenu.Manager.StartMenu.GetStartMenuState() == EMatchmaking + || class'KFGameEngine'.static.GetSeasonalEventID() == SEI_None) + { + bShowAllowSeasonalSkins = false; // Default if we don't have a season or it's find a match menu + } + + SetBool("SetAllowSkinsVisibility", bShowAllowSeasonalSkins); FiltersArray = CreateArray(); + // If you plan to disable a filter don't do it here, do it on ServerBrowserFilterContainer.as + for ( i = 0; i < FILTERS_MAX; i++ ) { TempObject = CreateObject( "Object" ); @@ -319,6 +336,7 @@ function ApplyFilters() bOnlyCustomMaps = bOnlyCustomMapsPending; bLimitServerResults = bLimitServerResultsPending; bNoLocalAdmin = bNoLocalAdminPending; + bNoSeasonalSkins = bNoSeasonalSkinsPending; SavedGameModeIndex = SavedGameModeIndexPending; SavedMapIndex = SavedMapIndexPending; @@ -345,6 +363,7 @@ function ClearPendingValues() bOnlyCustomMapsPending = bOnlyCustomMaps; bLimitServerResultsPending = bLimitServerResults; bNoLocalAdminPending = bNoLocalAdmin; + bNoSeasonalSkinsPending = bNoSeasonalSkins; SavedGameModeIndexPending = SavedGameModeIndex; SavedMapIndexPending = SavedMapIndex; SavedDifficultyIndexPending = SavedDifficultyIndex; @@ -369,6 +388,7 @@ function ResetFilters() bOnlyCustomMaps = false; bLimitServerResults = true; bNoLocalAdmin = true; + bNoSeasonalSkins = false; SavedGameModeIndex = 255; SavedMapIndex = 255; @@ -428,6 +448,9 @@ function SetBoolByEFilter_Key(EFilter_Key Filter, bool FilterValue) case NO_LOCAL_ADMIN: bNoLocalAdminPending = FilterValue; break; + case NO_SEASONAL_SKINS: + bNoSeasonalSkinsPending = FilterValue; + break; /*case ONLY_STOCK_MAPS: bOnlyStockMapsPending = FilterValue; break; @@ -479,6 +502,9 @@ function bool GetBoolByEFilter_Key(EFilter_Key Filter) case NO_LOCAL_ADMIN: return bNoLocalAdmin; + case NO_SEASONAL_SKINS: + return bNoSeasonalSkins; + /*case ONLY_STOCK_MAPS: return bOnlyStockMaps; @@ -491,4 +517,4 @@ cpptext { UBOOL CheckPingRange(INT index, INT ping) const; -} \ No newline at end of file +} diff --git a/KFGame/Classes/KFGFxServerBrowser_ServerDetails.uc b/KFGame/Classes/KFGFxServerBrowser_ServerDetails.uc index 603c87a..ae540d5 100644 --- a/KFGame/Classes/KFGFxServerBrowser_ServerDetails.uc +++ b/KFGame/Classes/KFGFxServerBrowser_ServerDetails.uc @@ -17,6 +17,8 @@ var localized string ServerInfoString; var localized string JoinGameString; var localized string SpectateGameString; var localized string UnfavoriteString; +var localized string SeasonalSkinsString; +var localized string NoSeasonalSkinsString; function Initialize( KFGFxObject_Menu NewParentMenu ) { @@ -42,6 +44,8 @@ function LocalizeText() LocalizedObject.SetString("mutators", MutatorsString); LocalizedObject.SetString("joinGame", JoinGameString); LocalizedObject.SetString("spectateGame", SpectateGameString); + LocalizedObject.SetString("seasonalString", SeasonalSkinsString); + LocalizedObject.SetString("noseasonalString", NoSeasonalSkinsString); SetObject("localizedText", LocalizedObject); } @@ -87,6 +91,7 @@ function SetDetails(KFOnlineGameSettings ServerResult) TempObj.SetBool("vacEnable", TempOnlingGamesSettings.bAntiCheatProtected); TempObj.SetBool("mutators", TempOnlingGamesSettings.bMutators); TempObj.SetBool("ranked", TempOnlingGamesSettings.bUsesStats); + TempObj.SetBool("seasonalSkins", TempOnlingGamesSettings.bNoSeasonalSkins == false); Ping = TempOnlingGamesSettings.PingInMs; TempObj.SetString("ping", (Ping < 0) ? ("-") : (String(Ping)) ); @@ -153,4 +158,4 @@ function string GetMapSource(string MapName) return "img://" $MapData.ScreenshotPathName; } } -} \ No newline at end of file +} diff --git a/KFGame/Classes/KFGFxServerBrowser_ServerList.uc b/KFGame/Classes/KFGFxServerBrowser_ServerList.uc index 78bb48d..b47a6e7 100644 --- a/KFGame/Classes/KFGFxServerBrowser_ServerList.uc +++ b/KFGame/Classes/KFGFxServerBrowser_ServerList.uc @@ -272,6 +272,7 @@ function BuildServerFilters(KFGFxServerBrowser_Filters Filters, OnlineGameSearch local string GametagSearch; local string MapName; local int Mode, Difficulty, Length; + local bool DisableSeasonalSkins; Search.ClearServerFilters(); @@ -280,6 +281,15 @@ function BuildServerFilters(KFGFxServerBrowser_Filters Filters, OnlineGameSearch Search.TestAddServerFilter( Filters.bNotEmpty, "hasplayers"); Search.TestAddBoolGametagFilter(GametagSearch, Filters.bNoLocalAdmin, 'bServerExiled', 0); + DisableSeasonalSkins = Filters.bNoSeasonalSkins; + + if (class'KFGameEngine'.static.GetSeasonalEventID() == SEI_None) + { + DisableSeasonalSkins = false; + } + + Search.TestAddBoolGametagFilter(GametagSearch, DisableSeasonalSkins, 'bNoSeasonalSkins', 1); + if( !class'WorldInfo'.static.IsConsoleBuild() ) { Search.TestAddServerFilter( Filters.bDedicated, "dedicated"); diff --git a/KFGame/Classes/KFGFxSpecialeventObjectivesContainer.uc b/KFGame/Classes/KFGFxSpecialeventObjectivesContainer.uc index 05d9698..162c5b6 100644 --- a/KFGame/Classes/KFGFxSpecialeventObjectivesContainer.uc +++ b/KFGame/Classes/KFGFxSpecialeventObjectivesContainer.uc @@ -117,6 +117,9 @@ static function GetObjectiveProgressValues(int ObjectiveID, out int CurrentValue local KFPlayerController LocalKFPC; local int TempCurrentValue, TempMaxValue; + TempCurrentValue = 0; + TempMaxValue = 0; + LocalKFPC = KFPlayerController(class'WorldInfo'.static.GetWorldInfo().GetALocalPlayerController()); LocalKFPC.GetSeasonalEventStatInfo(ObjectiveID, TempCurrentValue, TempMaxValue); diff --git a/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc b/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc index 031a4c2..5b37da5 100644 --- a/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc +++ b/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc @@ -13,7 +13,7 @@ dependson(KFUnlockManager); var KFGFxMenu_StartGame StartMenu; -var byte LastDifficultyIndex, LastLengthIndex, LastPrivacyIndex; +var byte LastDifficultyIndex, LastLengthIndex, LastPrivacyIndex, LastAllowSeasonalSkinsIndex; var localized string OverviewString; var localized string ChangeString; @@ -90,7 +90,6 @@ function LocalizeContainer() LocalizedObject.SetString("infoTitle", StartMenu.InfoTitle); LocalizedObject.SetString("permissionsTitle", StartMenu.PermissionsTitle); - WI = class'WorldInfo'.static.GetWorldInfo(); if( WI != none && WI.NetMode != NM_Standalone && !GetPC().WorldInfo.IsConsoleBuild() ) { @@ -115,6 +114,17 @@ function LocalizeContainer() LocalizedObject.SetObject("permissionOptions", DataProvider); + DataProvider = CreateArray(); + + for (i = 0; i < class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsStringsArray().length; i++) + { + TempObj = CreateObject("Object"); + TempObj.SetString("label", class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsString(i)); + DataProvider.SetElementObject(i, TempObj); + } + + LocalizedObject.SetObject("allowSeasonalSkinsOptions", DataProvider); + if( !class'WorldInfo'.static.IsMenuLevel() ) { LocalizedObject.SetString("authorName", AuthorString$GetPC().WorldInfo.Author); @@ -335,12 +345,17 @@ function UpdatePrivacy( string Privacy ) SetString("permissionsText", Privacy); } +function UpdateAllowSeasonalSkins(string AllowSeasonalStrings) +{ + SetString("allowSeasonalSkinsText", AllowSeasonalStrings); +} + function UpdateOverviewInGame() { local KFGameReplicationInfo KFGRI; local string GameDifficultyString; local Float CurrentGameDifficulty; - local int CurrentLengthIndex, CurrentPrivacyIndex; + local int CurrentLengthIndex, CurrentPrivacyIndex, CurrentAllowSeasonalSkinsIndex; local bool bCustomDifficulty; local bool bCustomLength; @@ -405,6 +420,13 @@ function UpdateOverviewInGame() UpdatePrivacy( class'KFCommon_LocalizedStrings'.static.GetPermissionString(CurrentPrivacyIndex) ); LastPrivacyIndex = CurrentPrivacyIndex; } + + CurrentAllowSeasonalSkinsIndex = StartMenu.OptionsComponent.GetAllowSeasonalSkinsIndex(); + if (LastAllowSeasonalSkinsIndex != CurrentAllowSeasonalSkinsIndex) + { + UpdateAllowSeasonalSkins( class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsString(CurrentAllowSeasonalSkinsIndex) ); + LastAllowSeasonalSkinsIndex = CurrentAllowSeasonalSkinsIndex; + } } } } @@ -425,6 +447,7 @@ DefaultProperties LastPrivacyIndex=255 LastLengthIndex=255 LastDifficultyIndex=255 + LastAllowSeasonalSkinsIndex=255 ObjectiveClassName=KFGameInfo_Objective } diff --git a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc index a9b76d4..17ce0c7 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_Halloween2022_BloodBonfire", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_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/"); @@ -194,31 +194,31 @@ function FillWhatsNew() 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_Blodd_Bornfires_Weapon_Bundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9471"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_WeaponsBundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9557"); WhatsNewItems.AddItem(item); // Featured Weapon - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Blood_Sickle_Weapon_bundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9469"); + 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_G36C_Weapon_Bundle","FeaturedItemBundle","https://store.steampowered.com/buyitem/232090/9467"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_HVStormCannon","FeaturedItemBundle","https://store.steampowered.com/buyitem/232090/9556"); WhatsNewItems.AddItem(item); // Featured Outfit Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_PlagueDoctor_Uniforms", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9465"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_TrainConductorOutfit", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9554"); WhatsNewItems.AddItem(item); // Featured Time Limited Item - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/5246"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Christmas_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/5588"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_Plague_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9457"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_Tacticool_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9553"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_Weapon_skins_XenoPack", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9459"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_Retro_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9552"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_Classic2_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9461"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_Mediaval_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9503"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_Chamaleon2_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9463"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas2022_ChamaleonIII_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9502"); 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 fbddd5e..6576786 100644 --- a/KFGame/Classes/KFGFxStartGameContainer_Options.uc +++ b/KFGame/Classes/KFGFxStartGameContainer_Options.uc @@ -74,6 +74,7 @@ var GFxObject LengthButton; var GFxObject DifficultyButton; var GfxObject MapButton; var GFxObject RegionButton; +var GFxObject AllowSeasonalSkinsButton; //============================================================== // Initialization @@ -94,6 +95,7 @@ function GetButtons() DifficultyButton = GetObject("difficultyButton"); MapButton = GetObject("mapButton"); RegionButton = GetObject("regionButton"); + AllowSeasonalSkinsButton = GetObject("allowSeasonalSkinsButton"); } function UpdateButtonsEnabled() @@ -241,6 +243,18 @@ function InitializeGameOptions() TextObject.SetString("length",StartMenu.LengthTitle); TextObject.SetString("privacy",StartMenu.PermissionsTitle); TextObject.SetString("inProgress", InProgressString); + + if (class'KFGameEngine'.static.GetSeasonalEventID() == SEI_None) + { + TextObject.SetBool("bShowAllowSeasonalSkins", false); + } + else + { + TextObject.SetBool("bShowAllowSeasonalSkins", true); + } + + TextObject.SetString("allowSeasonalSkins", StartMenu.AllowSeasonalSkinsTitle); + // Since the Mode list can include "ANY" we need to just accept that the selected index could be the length of the supported modes. Otherwise when "ANY" is selected we push the index to 1. // Also don't include the "ANY" option on Console since PlayGo doesn't support searching multiple game types. HSL_BB TextObject.SetObject("modeList", CreateList(SupportedGameModeStrings, Min(ParentMenu.Manager.GetModeIndex() , SupportedGameModeStrings.Length), false)); @@ -249,6 +263,7 @@ function InitializeGameOptions() TextObject.SetObject("mapList", CreateList(StartMenu.MapStringList, bIsSoloGame ? InitialMapIndex : InitialMapIndex+1, true, true)); TextObject.SetObject("difficultyList", CreateList(class'KFCommon_LocalizedStrings'.static.GetDifficultyStringsArray(), GetDifficultyIndex(), false)); TextObject.SetObject("privacyList", CreateList(class'KFCommon_LocalizedStrings'.static.GetPermissionStringsArray(class'WorldInfo'.static.IsConsoleBuild()), Profile.GetProfileInt(KFID_SavedPrivacyIndex), false)); + TextObject.SetObject("allowSeasonalSkinsList", CreateList(class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsStringsArray(), Profile.GetProfileInt(KFID_SavedAllowSeasonalSkinsIndex), false)); if (class'WorldInfo'.static.IsConsoleBuild()) { @@ -292,9 +307,7 @@ function FilterWeeklyMaps(out array List) if (WeeklyIndex == 14) { List.RemoveItem("KF-SteamFortress"); - } - /**/ - + } } function GFxObject CreateList( array TextArray, byte SelectedIndex, bool bAddNoPrefString, optional bool bIsMapList, optional byte MaxLength) @@ -465,6 +478,20 @@ function PrivacyChanged( int Index, optional bool bSetText ) } } +function AllowSeasonalSkinsChanged( int Index, optional bool bSetText ) +{ + if(Index != GetCachedProfile().GetProfileInt(KFID_SavedAllowSeasonalSkinsIndex)) + { + SaveConfig(); + if(bSetText) + { + AllowSeasonalSkinsButton.SetString("infoString", class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsStringsArray()[Index]); + } + + GetCachedProfile().SetProfileSettingValueInt(KFID_SavedAllowSeasonalSkinsIndex, Index); + } +} + function SetRegionIndex(int InRegionIndex, optional bool bSetText) { local array PlayfabRegionList; @@ -579,6 +606,11 @@ function int GetPrivacyIndex() return GetCachedProfile().GetProfileInt(KFID_SavedPrivacyIndex); } +function int GetAllowSeasonalSkinsIndex() +{ + return GetCachedProfile().GetProfileInt(KFID_SavedAllowSeasonalSkinsIndex); +} + function string GetMapName() { local string SavedMapString; diff --git a/KFGame/Classes/KFGFxStoreContainer_Main.uc b/KFGame/Classes/KFGFxStoreContainer_Main.uc index 19195b3..1015f6d 100644 --- a/KFGame/Classes/KFGFxStoreContainer_Main.uc +++ b/KFGame/Classes/KFGFxStoreContainer_Main.uc @@ -448,24 +448,24 @@ DefaultProperties XboxFilterExceptions[0]="Wasteland Bundle" // Wasteland Outfit Bundle FeaturedItemIDs[0]=7944 //Whatsnew Gold Ticket - FeaturedItemIDs[1]=9471 - FeaturedItemIDs[2]=9469 - FeaturedItemIDs[3]=9467 - FeaturedItemIDs[4]=9465 - FeaturedItemIDs[5]=9457 - FeaturedItemIDs[6]=9459 - FeaturedItemIDs[7]=9461 - FeaturedItemIDs[8]=9463 + FeaturedItemIDs[1]=9557 + FeaturedItemIDs[2]=9556 + FeaturedItemIDs[3]=9555 + FeaturedItemIDs[4]=9554 + FeaturedItemIDs[5]=9502 + FeaturedItemIDs[6]=9503 + FeaturedItemIDs[7]=9553 + FeaturedItemIDs[8]=9552 ConsoleFeaturedItemIDs[0]=7947 //Whatsnew Gold Ticket PSN - ConsoleFeaturedItemIDs[1]=9471 - ConsoleFeaturedItemIDs[2]=9469 - ConsoleFeaturedItemIDs[3]=9467 - ConsoleFeaturedItemIDs[4]=9465 - ConsoleFeaturedItemIDs[5]=9457 - ConsoleFeaturedItemIDs[6]=9459 - ConsoleFeaturedItemIDs[7]=9461 - ConsoleFeaturedItemIDs[8]=9463 + ConsoleFeaturedItemIDs[1]=9557 + ConsoleFeaturedItemIDs[2]=9556 + ConsoleFeaturedItemIDs[3]=9555 + ConsoleFeaturedItemIDs[4]=9554 + ConsoleFeaturedItemIDs[5]=9502 + ConsoleFeaturedItemIDs[6]=9503 + ConsoleFeaturedItemIDs[7]=9553 + ConsoleFeaturedItemIDs[8]=9552 MaxFeaturedItems=5 } \ No newline at end of file diff --git a/KFGame/Classes/KFGFxTraderContainer_PlayerInventory.uc b/KFGame/Classes/KFGFxTraderContainer_PlayerInventory.uc index 4be8076..0db9dad 100644 --- a/KFGame/Classes/KFGFxTraderContainer_PlayerInventory.uc +++ b/KFGame/Classes/KFGFxTraderContainer_PlayerInventory.uc @@ -66,9 +66,11 @@ function LocalizeContainer() //Updates the ablility to change the perk function UpdateLock() { + local bool bLocked; if (KFPC != none) { - SetBool("perkChangeLocked", !KFPC.CanUpdatePerkInfo()); + bLocked = !KFPC.CanUpdatePerkInfo() || KFGameReplicationInfo(KFPC.WorldInfo.GRI).IsRandomPerkMode(); + SetBool("perkChangeLocked", bLocked); } } diff --git a/KFGame/Classes/KFGFxWorld_WeaponRadar.uc b/KFGame/Classes/KFGFxWorld_WeaponRadar.uc new file mode 100644 index 0000000..b9d2541 --- /dev/null +++ b/KFGame/Classes/KFGFxWorld_WeaponRadar.uc @@ -0,0 +1,79 @@ +//============================================================================= +// KFGFxHUD_WeaponRadar +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFGFxWorld_WeaponRadar extends GFxMoviePlayer; + +struct RadarObject +{ + var vector Location; + var string IconPath; +}; + +var array ObjectsInRadar; + +var GFxObject RadarContainer; + +/** Ties the GFxClikWidget variables to the .swf components and handles events */ +event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget) +{ + switch(WidgetName) + { + case ('radarContainer'): + if ( RadarContainer == none ) + { + RadarContainer = Widget; + } + break; + } + + return true; +} + +simulated function AddRadarElements(array Elements) +{ + local int i; + local GFxObject DataObject; + local GFxObject DataProvider; + + if (Elements.Length == 0 || RadarContainer == none) + { + return; + } + + DataProvider = CreateArray(); + for (i = 0; i < Elements.Length; ++i) + { + DataObject = CreateObject( "Object" ); + DataObject.SetFloat("UIX", Elements[i].X); + DataObject.SetFloat("UIY", Elements[i].Y); + + DataProvider.SetElementObject(i, DataObject); + } + + RadarContainer.SetObject("RadarElements", DataProvider); +} + +simulated function Clear() +{ + if (RadarContainer != none) + { + RadarContainer.ActionScriptVoid("Clear"); + } +} + +simulated function PrintLog(string text) +{ + `Log("ActionScript Log: " $text); +} + +defaultproperties +{ + RenderTexture=TextureRenderTarget2D'WEP_1P_Optics_TEX.Wep_1stP_Optics_R2T' + MovieInfo=SwfMovie'UI_World.RadarWorld_SWF' + bAutoPlay=false +} diff --git a/KFGame/Classes/KFGameEngine.uc b/KFGame/Classes/KFGameEngine.uc index e3ae6a8..78e0661 100644 --- a/KFGame/Classes/KFGameEngine.uc +++ b/KFGame/Classes/KFGameEngine.uc @@ -143,7 +143,7 @@ cpptext { // static members static FString GetSeasonalEventPrefix(); - static FString GetSeasonalEventPackageName(INT EventId = -1, UMapInfo* MapInfo = NULL); + static FString GetSeasonalEventPackageName(INT EventId = -1, UMapInfo* MapInfo = NULL, bool bUnload = false); static FString GetSeasonalEventStatsClassPath(); // UEngine interface. diff --git a/KFGame/Classes/KFGameInfo.uc b/KFGame/Classes/KFGameInfo.uc index ea2c024..7511126 100644 --- a/KFGame/Classes/KFGameInfo.uc +++ b/KFGame/Classes/KFGameInfo.uc @@ -211,6 +211,8 @@ var protected const array< class > AITestBossClassList; //List var protected int BossIndex; //Index into boss array, only preload content for the boss we want - PC builds can handle DLO cost much better +var int AllowSeasonalSkinsIndex; + /** Class replacements for each zed type */ struct native SpawnReplacement { @@ -677,6 +679,7 @@ event InitGame( string Options, out string ErrorMessage ) GameLength = Clamp(GetIntOption(Options, "GameLength", GameLength), 0, SpawnManagerClasses.Length - 1); } + AllowSeasonalSkinsIndex = GetIntOption(Options, "AllowSeasonalSkins", AllowSeasonalSkinsIndex); if( OnlineSub != none && OnlineSub.GetLobbyInterface() != none ) { @@ -713,6 +716,36 @@ event InitGame( string Options, out string ErrorMessage ) CreateDifficultyInfo(Options); } +function UpdateGameSettings() +{ + local name SessionName; + local KFOnlineGameSettings KFGameSettings; + + super.UpdateGameSettings(); + + if (WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.NetMode == NM_ListenServer) + { + if (GameInterface != None) + { + SessionName = PlayerReplicationInfoClass.default.SessionName; + + if (PlayfabInter != none && PlayfabInter.GetGameSettings() != none) + { + KFGameSettings = KFOnlineGameSettings(PlayfabInter.GetGameSettings()); + } + else + { + KFGameSettings = KFOnlineGameSettings(GameInterface.GetGameSettings(SessionName)); + } + + if (KFGameSettings != none) + { + KFGameSettings.bNoSeasonalSkins = AllowSeasonalSkinsIndex == 1; + } + } + } +} + //for difficulty override function bool UsesModifiedLength() { @@ -1095,6 +1128,8 @@ function InitGRIVariables() MyKFGRI.ReceivedGameLength(); MyKFGRI.bVersusGame = bIsVersusGame; MyKFGRI.MaxHumanCount = MaxPlayers; + MyKFGRI.NotifyAllowSeasonalSkins(AllowSeasonalSkinsIndex); + SetBossIndex(); } @@ -2071,25 +2106,28 @@ function Killed(Controller Killer, Controller KilledPlayer, Pawn KilledPawn, cla { LastHitByDamageType = GetLastHitByDamageType( DT, MonsterPawn, Killer ); - //Chris: We have to do it earlier here because we need a damage type - KFPC.AddZedKill( MonsterPawn.class, GameDifficulty, LastHitByDamageType, true ); - - KFPCP = KFPC.GetPerk(); - if( KFPCP != none ) + if (LastHitByDamageType != none) { - if( KFPCP.CanEarnSmallRadiusKillXP( LastHitByDamageType ) ) - { - if(KFPCP.GetPerkClass() == class'KFPerk_Berserker'.static.GetPerkClass()) - { - CheckForSmallRadiusKill( MonsterPawn, KFPC, class'KFPerk_Berserker' ); - } - else if(KFPCP.GetPerkClass() == class'KFPerk_Survivalist'.static.GetPerkClass()) - { - CheckForSmallRadiusKill( MonsterPawn, KFPC, class'KFPerk_Survivalist' ); - } - } + //Chris: We have to do it earlier here because we need a damage type + KFPC.AddZedKill( MonsterPawn.class, GameDifficulty, LastHitByDamageType, true ); - KFPCP.AddVampireHealth( KFPC, LastHitByDamageType ); + KFPCP = KFPC.GetPerk(); + if( KFPCP != none ) + { + if( KFPCP.CanEarnSmallRadiusKillXP( LastHitByDamageType ) ) + { + if(KFPCP.GetPerkClass() == class'KFPerk_Berserker'.static.GetPerkClass()) + { + CheckForSmallRadiusKill( MonsterPawn, KFPC, class'KFPerk_Berserker' ); + } + else if(KFPCP.GetPerkClass() == class'KFPerk_Survivalist'.static.GetPerkClass()) + { + CheckForSmallRadiusKill( MonsterPawn, KFPC, class'KFPerk_Survivalist' ); + } + } + + KFPCP.AddVampireHealth( KFPC, LastHitByDamageType ); + } } } } @@ -2900,6 +2938,11 @@ simulated function ForceWWLMusicTrack() MyKFGRI.ForceNewMusicTrack( default.ForcedMusicTracks[EFM_WWL] ); } +function bool IsWeekly() +{ + return MyKFGRI.IsA('KFGameReplicationInfo_WeeklySurvival'); +} + /********************************************************************************************* * @name Map rotation *********************************************************************************************/ @@ -2933,7 +2976,7 @@ function string GetNextMap() /** This could be changed to read replicated values instead of engine ones. */ - if (MyKFGRI.IsA('KFGameReplicationInfo_WeeklySurvival')) + if (IsWeekly()) { if ((class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 11 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[11]) || // Scavenger (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 14 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[14]) || // Boss Rush @@ -3912,6 +3955,11 @@ function ClearActorFromBonfire(Actor Other); **********************************************/ simulated function ModifyDamageGiven(out int InDamage, optional Actor DamageCauser, optional KFPawn_Monster MyKFPM, optional KFPlayerController DamageInstigator, optional class DamageType, optional int HitZoneIdx ); +/*********************************************** + * @name Notify a player is initialized + **********************************************/ +simulated function NotifyPlayerStatsInitialized(KFPlayerController_WeeklySurvival KFPC){} + defaultproperties { /** Scoring */ @@ -3957,6 +4005,7 @@ defaultproperties bRestartLevel=false bWaitingToStartMatch=true bDelayedStart=true + AllowSeasonalSkinsIndex=0 ActionMusicDelay=5.0 ForcedMusicTracks(0)=KFMusicTrackInfo'WW_MMNU_Login.TrackInfo' // menu diff --git a/KFGame/Classes/KFGameReplicationInfo.uc b/KFGame/Classes/KFGameReplicationInfo.uc index 57291f7..77a9ad1 100644 --- a/KFGame/Classes/KFGameReplicationInfo.uc +++ b/KFGame/Classes/KFGameReplicationInfo.uc @@ -385,6 +385,7 @@ var repnotify int VIPRepCurrentHealth; var repnotify int VIPRepMaxHealth; var repnotify KFPlayerReplicationInfo VIPRepPlayer; +var bool bAllowSeasonalSkins; /************************************ * Steam heartbeat @@ -422,7 +423,7 @@ replication bIsUnrankedGame, GameSharedUnlocks, bHidePawnIcons, ConsoleGameSessionGuid, GameDifficulty, GameDifficultyModifier, BossIndex, bWaveStarted, NextObjective, bIsBrokenTrader, bIsWeeklyMode, CurrentWeeklyIndex, bIsEndlessPaused, bForceSkipTraderUI, VIPRepCurrentHealth, VIPRepMaxHealth, VIPRepPlayer; //@HSL - JRO - 3/21/2016 - PS4 Sessions if ( bNetInitial ) - GameLength, WaveMax, bCustom, bVersusGame, TraderItems, GameAmmoCostScale, bAllowGrenadePurchase, MaxPerkLevel, bTradersEnabled, bForceShowSkipTrader; + GameLength, WaveMax, bCustom, bVersusGame, TraderItems, GameAmmoCostScale, bAllowGrenadePurchase, MaxPerkLevel, bTradersEnabled, bForceShowSkipTrader, bAllowSeasonalSkins; if ( bNetInitial || bNetDirty ) PerksAvailableData; if ( bNetInitial && Role == ROLE_Authority ) @@ -2302,6 +2303,11 @@ simulated function NotifyWeeklyEventIndex(int EventIndex) bNetDirty = true; } +simulated function NotifyAllowSeasonalSkins(int AllowSeasonalSkinsIndex) +{ + bAllowSeasonalSkins = (AllowSeasonalSkinsIndex == 0); + bNetDirty = true; +} /** VIP weekly */ simulated function UpdateVIPMaxHealth(int NewMaxHealth) @@ -2392,6 +2398,11 @@ simulated function bool IsVIPMode() return bIsWeeklyMode && CurrentWeeklyIndex == 17; } +simulated function bool IsRandomPerkMode() +{ + return bIsWeeklyMode && CurrentWeeklyIndex == 18; +} + defaultproperties { TraderItemsPath="GP_Trader_ARCH.DefaultTraderItems" @@ -2413,6 +2424,7 @@ defaultproperties bIsBrokenTrader=false bIsWeeklyMode=false bForceShowSkipTrader=false + bAllowSeasonalSkins=true bForceSkipTraderUI=false GunGameWavesCurrent=1 bWaveGunGameIsFinal=false diff --git a/KFGame/Classes/KFGfxMenu_StartGame.uc b/KFGame/Classes/KFGfxMenu_StartGame.uc index ec61629..647b2cb 100644 --- a/KFGame/Classes/KFGfxMenu_StartGame.uc +++ b/KFGame/Classes/KFGfxMenu_StartGame.uc @@ -32,6 +32,7 @@ var transient int CurrentSearchIndex; var const string ModeKey, DifficultyKey, MapKey, WhitelistedKey, InProgressKey, PermissionsKey, ServerTypeKey; var const string GameLengthKey; +var const string AllowSeasonalSkinsKey; var KFGFxStartGameContainer_FindGame FindGameContainer; var KFGFxStartGameContainer_Options OptionsComponent; @@ -66,6 +67,7 @@ var localized string LengthTitle; var localized string MapTitle; var localized string MutatorTitle; var localized string PermissionsTitle; +var localized string AllowSeasonalSkinsTitle; var localized string ServerTypeString; var localized string WhiteListedTitle; var localized string InfoTitle; @@ -208,7 +210,7 @@ static function class GetSpecialEventClass case SEI_Fall: return class'KFGFxSpecialEventObjectivesContainer_Fall2022'; case SEI_Winter: - return class'KFGFXSpecialEventObjectivesContainer_Xmas2021'; + return class'KFGFXSpecialEventObjectivesContainer_Xmas2022'; } return class'KFGFxSpecialEventObjectivesContainer'; @@ -605,6 +607,7 @@ function SendLeaderOptions() SetLobbyData(MapKey, OptionsComponent.GetMapName()); SetLobbyData(ModeKey, String(Manager.GetModeIndex())); SetLobbyData(PermissionsKey, String(OptionsComponent.GetPrivacyIndex())); + SetLobbyData(AllowSeasonalSkinsKey, String(OptionsComponent.GetAllowSeasonalSkinsIndex())); } } @@ -634,6 +637,9 @@ function ReceiveLeaderOptions() OptionIndex = Int(OnlineLobby.GetLobbyData(0, PermissionsKey)); OverviewContainer.UpdatePrivacy(class'KFCommon_LocalizedStrings'.static.GetPermissionString(OptionIndex)); + + OptionIndex = Int(OnlineLobby.GetLobbyData(0, AllowSeasonalSkinsKey)); + OverviewContainer.UpdateAllowSeasonalSkins(class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsString(OptionIndex)); } function ApproveMatchMakingLeave() @@ -1081,6 +1087,11 @@ function Callback_Region(int RegionIndex) OptionsComponent.SetRegionIndex(RegionIndex); } +function Callback_AllowSeasonalSkins(int Index) +{ + OptionsComponent.AllowSeasonalSkinsChanged(Index); +} + function SetLobbyData( string KeyName, string ValueData ) { OnlineLobby.SetLobbyData( KeyName, ValueData ); @@ -1089,7 +1100,7 @@ function SetLobbyData( string KeyName, string ValueData ) function string MakeMapURL(KFGFxStartGameContainer_Options InOptionsComponent) { local string MapName; - local int LengthIndex, ModeIndex; + local int LengthIndex, ModeIndex, AllowSeasonalSkinsIndex; // this is ugly, but effectively makes sure that the player isn't solo with versus selected // or other error cases such as when the game isn't fully installed @@ -1130,9 +1141,17 @@ function string MakeMapURL(KFGFxStartGameContainer_Options InOptionsComponent) } } + AllowSeasonalSkinsIndex = InOptionsComponent.GetAllowSeasonalSkinsIndex(); + + if (GetStartMenuState() == EMatchmaking || class'KFGameEngine'.static.GetSeasonalEventID() == SEI_None) + { + AllowSeasonalSkinsIndex = 0; // Default if we don't have a season or it's find a match menu + } + return MapName$"?Game="$class'KFGameInfo'.static.GetGameModeClassFromNum( ModeIndex ) $"?Difficulty="$class'KFGameDifficultyInfo'.static.GetDifficultyValue( InOptionsComponent.GetDifficultyIndex() ) - $"?GameLength="$LengthIndex; + $"?GameLength="$LengthIndex + $"?AllowSeasonalSkins="$AllowSeasonalSkinsIndex; } native function bool GetSearchComplete(KFOnlineGameSearch GameSearch); @@ -1481,6 +1500,7 @@ function BuildServerFilters(OnlineGameInterface GameInterfaceSteam, KFGFxStartGa local int GameLength; local string GameTagFilters; local ActiveLobbyInfo LobbyInfo; + //local bool bAllowSeasonal; Search.ClearServerFilters(); @@ -1497,6 +1517,18 @@ function BuildServerFilters(OnlineGameInterface GameInterfaceSteam, KFGFxStartGa //TestAddBoolGametagFilter Explanation: if set to true the second param, it will add the property to the search filters. But this property may interest us to search it as "false" or "true" so that is the purpose of the fourth param. } + // We don't want to force a search with the Seasonal Skins filter, we find any available server + // then we do a takeover, that's when we change the server settings + + //bAllowSeasonal = OptionsComponent.GetAllowSeasonalSkinsIndex() == 0; + + //if (GetStartMenuState() == EMatchmaking || class'KFGameEngine'.static.GetSeasonalEventID() == SEI_None) + //{ + // bAllowSeasonal = true; // Default if we don't have a season or it's find a match menu + //} + + //Search.TestAddBoolGametagFilter(GameTagFilters, bAllowSeasonal == false, 'bNoSeasonalSkins', 1); + if (OptionsComponent.GetMakeNewServer() || bAttemptingServerCreate ) { Search.AddGametagFilter( GameTagFilters, 'bAvailableForTakeover', "1"); @@ -1915,6 +1947,8 @@ defaultproperties ServerTypeKey="ServerTypeKey" InProgressKey="InProgress" PermissionsKey="PermissionsKey" + AllowSeasonalSkinsKey="AllowSeasonalSkinsKey" + SearchDSName=KFGameSearch CurrentSearchIndex=0 diff --git a/KFGame/Classes/KFInventoryManager.uc b/KFGame/Classes/KFInventoryManager.uc index 596143e..e449ad3 100644 --- a/KFGame/Classes/KFInventoryManager.uc +++ b/KFGame/Classes/KFInventoryManager.uc @@ -56,6 +56,8 @@ var float LastCreatedWeaponTime; /** This stores any GFxMoviePlayers that are inventory may be using */ var array OpticsUIMovies; +var array RadarUIMovies; + var bool bAutoswitchWeapon; var const float StartedWithWeaponPriceModifier; // The selling price reduction for a weapon that we were given at the start of the game @@ -191,6 +193,29 @@ simulated function GFxMoviePlayer GetOpticsUIMovie(class OpticsC return OpticsUIMovies[OpticsIndex]; } +simulated function GFxMoviePlayer GetRadarUIMovie(class RadarClass) +{ + local byte RadarIndex; + + for (RadarIndex = 0; RadarIndex < RadarUIMovies.Length; RadarIndex++) + { + if (RadarUIMovies[RadarIndex].class == RadarClass) + { + return RadarUIMovies[RadarIndex]; + } + } + + // Create the screen's UI piece + RadarUIMovies.AddItem(new( self ) RadarClass); + + // Initialize the new movie + RadarIndex = RadarUIMovies.length - 1; + RadarUIMovies[RadarIndex].Init(); + RadarUIMovies[RadarIndex].Start(); + + return RadarUIMovies[RadarIndex]; +} + /** Create a GFxMoviePlayer for weapon optics if it doesn't exist, otherwise grab the existing GFxMoviePlayer */ simulated function RemoveOpticsUIMovie(class OpticsClass) { @@ -206,6 +231,21 @@ simulated function RemoveOpticsUIMovie(class OpticsClass } } +/** Create a GFxMoviePlayer for weapon optics if it doesn't exist, otherwise grab the existing GFxMoviePlayer */ +simulated function RemoveRadarUIMovie(class RadarClass) +{ + local byte RadarIndex; + + for (RadarIndex = 0; RadarIndex < RadarUIMovies.Length; RadarIndex++) + { + if (RadarUIMovies[RadarIndex].class == RadarClass) + { + RadarUIMovies[RadarIndex].Close(); + RadarUIMovies.Remove(RadarIndex, 1); + } + } +} + /** * InitFOV. Handle setting FOV values for inventory when the resolution changes * @@ -2782,5 +2822,5 @@ defaultproperties SwitchFireModeEvent=AkEvent'WW_UI_PlayerCharacter.Play_WEP_ModeSwitch' bLogInventory=false - DoshinegunSellModifier=0.167f + DoshinegunSellModifier=0.25f } diff --git a/KFGame/Classes/KFLocalMessage_Priority.uc b/KFGame/Classes/KFLocalMessage_Priority.uc index ecaa856..65ec35a 100644 --- a/KFGame/Classes/KFLocalMessage_Priority.uc +++ b/KFGame/Classes/KFLocalMessage_Priority.uc @@ -33,6 +33,7 @@ var localized string NextRoundBeginString; var localized string PlayerCanChangePerksString; var localized string ZedWaitingForNextRoundString; var localized string LastPlayerStandingString; +var localized string NewPerkAssignedMessage; struct SDelayedPriorityMessage { @@ -75,14 +76,19 @@ static function ClientReceive( optional Object OptionalObject ) { - local string MessageString,SecondaryMessageString; + local string MessageString,SecondaryMessageString, SpecialIconPath; local KFGFxMoviePlayer_HUD myGfxHUD; local KFGameReplicationInfo KFGRI; local TeamInfo TeamInfo; local byte TeamIndex; local KFPlayerController KFPC; + local class ReceivedPerkClass; + local bool bIsRandomPerkMode; + KFPC = KFPlayerController(class'WorldInfo'.static.GetWorldInfo().GetALocalPlayerController()); + bIsRandomPerkMode = KFGameReplicationInfo(class'WorldInfo'.static.GetWorldInfo().GRI).IsRandomPerkMode(); + TeamIndex = P.PlayerReplicationInfo.GetTeamNum(); if( OptionalObject != none ) { @@ -92,14 +98,25 @@ static function ClientReceive( TeamIndex = TeamInfo.TeamIndex; } } - + MessageString = static.GetMessageString( Switch, SecondaryMessageString, TeamIndex ); + + if (bIsRandomPerkMode) + { + ReceivedPerkClass = class(OptionalObject); + if (ReceivedPerkClass != none) + { + MessageString = ReceivedPerkClass.default.PerkName; + SpecialIconPath = ReceivedPerkClass.static.GetPerkIconPath(); + } + } + if ( MessageString != "" && KFGFxHudWrapper(p.myHUD) != none && ShouldShowPriortyMessage(P, Switch)) { myGfxHUD = KFGFxHudWrapper(p.myHUD).HudMovie; if ( myGfxHUD != None ) { - myGfxHUD.DisplayPriorityMessage(MessageString,SecondaryMessageString,static.GetMessageLifeTime(Switch), EGameMessageType(Switch)); + myGfxHUD.DisplayPriorityMessage(MessageString,SecondaryMessageString,static.GetMessageLifeTime(Switch), EGameMessageType(Switch), SpecialIconPath); } else if(KFPC.MyGFxManager != none) //store it off so we can queue it { @@ -271,7 +288,15 @@ static function string GetMessageString(int Switch, optional out String Secondar } else { - SecondaryString = KFGameReplicationInfo(class'WorldInfo'.static.GetWorldInfo().GRI).bTradersEnabled ? default.GetToTraderMessage : default.ScavengeMessage; + if ( KFGameReplicationInfo(class'WorldInfo'.static.GetWorldInfo().GRI).IsRandomPerkMode() ) + { + SecondaryString = default.NewPerkAssignedMessage; + } + else + { + SecondaryString = KFGameReplicationInfo(class'WorldInfo'.static.GetWorldInfo().GRI).bTradersEnabled ? default.GetToTraderMessage : default.ScavengeMessage; + } + return default.WaveEndMessage; } case GMT_MatchWon: diff --git a/KFGame/Classes/KFOnlineStatsReadDingo.uc b/KFGame/Classes/KFOnlineStatsReadDingo.uc index e98d807..b36a67c 100644 --- a/KFGame/Classes/KFOnlineStatsReadDingo.uc +++ b/KFGame/Classes/KFOnlineStatsReadDingo.uc @@ -72,6 +72,7 @@ defaultproperties ColumnIds.Add(STATID_ACHIEVE_CarillonHamletCollectibles) ColumnIds.Add(STATID_ACHIEVE_RigCollectibles) ColumnIds.Add(STATID_ACHIEVE_BarmwichCollectibles) + ColumnIds.Add(STATID_ACHIEVE_CrashCollectibles); ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky5, Name="AchievementMrPerky5")) ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky10, Name = "AchievementMrPerky10")) @@ -132,4 +133,6 @@ defaultproperties ColumnMappings.Add((Id=STATID_ACHIEVE_CarillonHamletCollectibles,Name="AchievementCollectCarillonHamlet")) ColumnMappings.Add((Id=STATID_ACHIEVE_RigCollectibles,Name="AchievementCollectRig")) ColumnMappings.Add((Id=STATID_ACHIEVE_BarmwichCollectibles,Name="AchievementCollectBarmwichTown")) + ColumnMappings.Add((Id=STATID_ACHIEVE_CrashCollectibles,Name="AchievementCollectCrash")) + } diff --git a/KFGame/Classes/KFOnlineStatsWrite.uc b/KFGame/Classes/KFOnlineStatsWrite.uc index 8a1259e..96d658c 100644 --- a/KFGame/Classes/KFOnlineStatsWrite.uc +++ b/KFGame/Classes/KFOnlineStatsWrite.uc @@ -449,6 +449,10 @@ const KFACHID_BarmwichHard = 295; const KFACHID_BarmwichHellOnEarth = 296; const KFACHID_BarmwichCollectibles = 297; +const KFACHID_CrashHard = 298; +const KFACHID_CrashHellOnEarth = 299; +const KFACHID_CrashCollectibles = 300; + /* __TW_ANALYTICS_ */ var int PerRoundWeldXP; var int PerRoundHealXP; @@ -1182,6 +1186,11 @@ private event AddToWeaponPurchased( class WeaponDef, int Pri SeasonalEventStats_OnWeaponPurchased(WeaponDef, Price); } +private event AddAfflictionCaused(EAfflictionType Type) +{ + SeasonalEventStats_OnAfflictionCaused(Type); +} + private native function AddToKillObjectives(class ZedClass); private native function AddToVersusKillObjectives(class KillerClass); @@ -1810,6 +1819,14 @@ native final function MapObjectiveCompleted(); * @name Seasonal Events ********************************************************************************************* */ +final simulated function SeasonalEventStats_OnStatsInitialized() +{ + if (SeasonalEventIsValid()) + { + SeasonalEvent.OnStatsInitialized(); + } +} + final native simulated function bool SeasonalEventIsValid(); final simulated function SeasonalEventStats_OnMapObjectiveDeactivated(Actor ObjectiveInterfaceActor) @@ -1896,6 +1913,14 @@ final simulated function SeasonalEventStats_OnWeaponPurchased(class DamageType) +{ + if (!ClassIsChildOf(DamageType, class'KFDT_Bludgeon')) + { + return false; + } + + if (IsDoshinegun(KFW)) + { + return DamageType.Name != 'KFDT_Bludgeon_Doshinegun_Shot'; + } + else if (IsHRGBallisticBouncer(KFW)) + { + return DamageType.Name != 'KFDT_Bludgeon_HRG_BallisticBouncer_Shot'; + } + + return true; +} + /** * @brief Modifies the max spare ammo * diff --git a/KFGame/Classes/KFPlayerController.uc b/KFGame/Classes/KFPlayerController.uc index 40c255c..f87cf19 100644 --- a/KFGame/Classes/KFPlayerController.uc +++ b/KFGame/Classes/KFPlayerController.uc @@ -728,6 +728,33 @@ var transient byte StoredLocalUserNum; **********************************************************************************************/ var transient array DeployedTurrets; +/********************************************************************************************* + * @name HV Storm Cannon + + Keep track of the electricity VFX Chain (Client Only) +********************************************************************************************* */ +struct native KFStormCannonBeamData +{ + var KFPawn_Monster SourcePawn; + var ParticleSystemComponent ParticleSystemPawn; + var ParticleSystemComponent ParticleSystemBeam; + var int ID; + + structdefaultproperties + { + SourcePawn = none + ParticleSystemPawn = none + ParticleSystemBeam = none + ID = 255 + } +}; + +var array StormCannonBeamData; + +var byte StormCannonIDCounter; + +var transient bool bShotgunJumping; + cpptext { virtual UBOOL Tick( FLOAT DeltaSeconds, ELevelTick TickType ); @@ -7045,8 +7072,15 @@ function SetUIScale(float fScale) function GetSeasonalEventStatInfo(int StatIdx, out int CurrentValue, out int MaxValue) { - CurrentValue = StatsWrite.GetSeasonalEventStatValue(StatIdx); - MaxValue = StatsWrite.GetSeasonalEventStatMaxValue(StatIdx); + if (StatsWrite != none) + { + CurrentValue = StatsWrite.GetSeasonalEventStatValue(StatIdx); + MaxValue = StatsWrite.GetSeasonalEventStatMaxValue(StatIdx); + } + else + { + `warn( "GetSeasonalEventStatInfo() No StatsWrite ??"); + } } simulated event CompletedDaily(int Index) @@ -7297,6 +7331,8 @@ simulated function OnStatsInitialized( bool bWasSuccessful ) { `RecordAARPerkXPGain(self, PerkList[i].PerkClass, 0, 0); } + + StatsWrite.SeasonalEventStats_OnStatsInitialized(); } @@ -7556,6 +7592,12 @@ function AddWeaponPurchased( class WeaponDef, int Price ) } native reliable client private function ClientAddWeaponPurchased( class WeaponDef, int Price ); +function AddAfflictionCaused(EAfflictionType Type) +{ + ClientAddAfflictionCaused(Type); +} +native reliable client private function ClientAddAfflictionCaused(EAfflictionType Type); + function AddZedAssist(class MonsterClass) { ClientAddZedAssist(MonsterClass); @@ -11944,6 +11986,137 @@ simulated function InitPerkLoadout() } } +/** + * HV Storm Cannon RPCs + */ + +client reliable function AddStormCannonVFX(KFPawn_Monster Target, int ID) +{ + local int i, DataIndex; + local KFStormCannonBeamData BeamData; + local ParticleSystemComponent PawnParticleSystem, BeamParticleSystem; + local KFPawn_Monster Origin; + + if (Target == none) + { + return; + } + + DataIndex = INDEX_NONE; + BeamParticleSystem = none; + + for (i = 0; i < StormCannonBeamData.Length; ++i) + { + if (StormCannonBeamData[i].ID == ID) + { + DataIndex = i; + break; + } + } + + if (DataIndex == INDEX_NONE) + { + BeamData.SourcePawn = Target; + BeamData.ID = ID; + + // Simulated EMP affliction Effects + Target.PlaySoundBase(class'KFAffliction_EMP'.default.OnEMPSound, true, true, true); + BeamData.ParticleSystemPawn = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment(ParticleSystem'FX_Gameplay_EMIT.FX_Char_Emp_clot' + , Target.Mesh + , class'KFAffliction_EMP'.default.EffectSocketName + , false); + + StormCannonBeamData.AddItem(BeamData); + } + else if (StormCannonBeamData[DataIndex].SourcePawn != none && StormCannonBeamData[DataIndex].SourcePawn != Target) + { + Origin = StormCannonBeamData[DataIndex].SourcePawn; + StormCannonBeamData[DataIndex].SourcePawn = Target; + + // Deactivate old VFX on SourcePawn + if (StormCannonBeamData[DataIndex].ParticleSystemPawn != none) + { + Origin.PlaySoundBase(class'KFAffliction_EMP'.default.OnEMPEndSound, true, true); + + PawnParticleSystem = StormCannonBeamData[DataIndex].ParticleSystemPawn; // Need to do this as UE doesn't allow passing an index into an array as out parameter + Origin.DetachEmitter(PawnParticleSystem); + } + + // Activate new VFX on Target Pawn + Target.PlaySoundBase(class'KFAffliction_EMP'.default.OnEMPSound, true, true, true); + StormCannonBeamData[DataIndex].ParticleSystemPawn = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment(ParticleSystem'FX_Gameplay_EMIT.FX_Char_Emp_clot' + , Target.Mesh + , class'KFAffliction_EMP'.default.EffectSocketName + , false); + + if (StormCannonBeamData[DataIndex].ParticleSystemBeam == none) + { + StormCannonBeamData[DataIndex].ParticleSystemBeam = WorldInfo.MyEmitterPool.SpawnEmitter(ParticleSystem'WEP_HVStormCannon_EMIT.FX_HVStormCannon_Beam' + , Origin.Mesh.GetBoneLocation('head')); + } + + // Reuse Particle System + BeamParticleSystem = StormCannonBeamData[DataIndex].ParticleSystemBeam; + } + + if (BeamParticleSystem == none || Origin == none) + { + return; + } + + //`Log("SpawnBeamStormCannon with ID: " $ID $" from : " $Origin $" To: " $self); + + BeamParticleSystem.SetBeamSourcePoint(0, Origin.Mesh.GetBoneLocation('head'), 0); + BeamParticleSystem.SetBeamTargetPoint(0, Target.Mesh.GetBoneLocation('head'), 0); + BeamParticleSystem.SetAbsolute(false, false, false); +} + +client reliable function RemoveStormCannonVFX(int ID) +{ + local int i; + local ParticleSystemComponent PawnParticleSystem; + + for (i = 0; i < StormCannonBeamData.Length; ++i) + { + if (StormCannonBeamData[i].ID == ID) + { + if (StormCannonBeamData[i].ParticleSystemPawn != none) + { + PawnParticleSystem = StormCannonBeamData[i].ParticleSystemPawn; // Need to do this as UE doesn't allow passing an index into an array as out parameter + StormCannonBeamData[i].SourcePawn.DetachEmitter(PawnParticleSystem); + + StormCannonBeamData[i].ParticleSystemPawn.SetActive(false); + StormCannonBeamData[i].ParticleSystemPawn = none; + } + + if (StormCannonBeamData[i].ParticleSystemBeam != none) + { + StormCannonBeamData[i].ParticleSystemBeam.DeactivateSystem(); + StormCannonBeamData[i].ParticleSystemBeam.KillParticlesForced(); + StormCannonBeamData[i].ParticleSystemBeam = none; + } + + StormCannonBeamData.Remove(i, 1); + return; + } + } +} + +simulated function SetShotgunJump(bool bJumping) +{ + bShotgunJumping = bJumping; +} + +simulated function ResetShotgunJump() +{ + SetTimer(0.1, false, nameof(ClearShotgunJumpFlag)); +} + +simulated function ClearShotgunJumpFlag() +{ + bShotgunJumping = false; +} + defaultproperties { EarnedDosh=0 @@ -12144,8 +12317,11 @@ defaultproperties BeginningRoundVaultAmount=INDEX_NONE - RotationSpeedLimit=-1.f; + RotationSpeedLimit=-1.f - RotationAdjustmentInterval = 0.1f; - CurrentRotationAdjustmentTime = 0.0f; + RotationAdjustmentInterval = 0.1f + CurrentRotationAdjustmentTime = 0.0f + + StormCannonIDCounter = 0 + bShotgunJumping=false } diff --git a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc index a3cc9ce..53d0e36 100644 --- a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc +++ b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc @@ -45,6 +45,8 @@ var protected const AKEvent VIPChosenSoundEvent; var protected const AKEvent VIPLowHealthSoundEvent; var protected float VIPLowHealthLastTimePlayed; +var protected const AKEvent RandomPerkChosenSoundEvent; + struct native GunGameInfo { var transient byte Level; @@ -106,6 +108,9 @@ struct native VIPGameInfo }; var transient VIPGameInfo VIPGameData; +// RandomPerk weekly +var array > LockedPerks; + cpptext { virtual UBOOL TestZedTimeVisibility(APawn* P, UNetConnection* Connection, UBOOL bLocalPlayerTest) override; @@ -601,6 +606,64 @@ function UpdateVIPDamage() } } +simulated function ForceNewPerk(class NewPerk) +{ + local int NewPerkIndex; + NewPerkIndex = Perklist.Find('PerkClass', NewPerk); + ServerSelectPerk(NewPerkIndex, Perklist[NewPerkIndex].PerkLevel, Perklist[NewPerkIndex].PrestigeLevel, true); + SavedPerkIndex = NewPerkIndex; + ForceNewSavedPerkIndex(NewPerkIndex); +} + +unreliable client function PlayRandomPerkChosenSound(optional float delay = 0.0f) +{ + if (delay > 0.0f) + { + SetTimer(delay, false, nameof(PlayRandomPerkChosenSound_Internal)); + } + else + { + PlayRandomPerkChosenSound_Internal(); + } +} + +simulated function PlayRandomPerkChosenSound_Internal() +{ + if (RandomPerkChosenSoundEvent != none) + { + PlaySoundBase(RandomPerkChosenSoundEvent); + } +} + +simulated function OnStatsInitialized( bool bWasSuccessful ) +{ + local KFGameReplicationInfo KFGRI; + + Super.OnStatsInitialized(bWasSuccessful); + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + if (KFGRI != none && KFGRI.IsRandomPerkMode()) + { + ServerOnStatsInitialized(); + } +} + +reliable server function ServerOnStatsInitialized() +{ + local KFGameInfo KFGI; + + KFGI = KFGameInfo(WorldInfo.Game); + if (KFGI != none) + { + KFGI.NotifyPlayerStatsInitialized(self); + } +} + +reliable client function ForceNewSavedPerkIndex(byte NewPerkIndex) +{ + SavedPerkIndex = NewPerkIndex; +} + // defaultProperties { @@ -615,6 +678,7 @@ defaultProperties GunGameLevelUpSoundEvent=AkEvent'WW_GLO_Runtime.WeeklyAALevelUp' GunGameLevelUpFinalWeaponSoundEvent=AkEvent'WW_GLO_Runtime.WeeklyAALevelFinal' VIPChosenSoundEvent=AkEvent'WW_UI_Menu.Play_AAR_TOPWEAPON_SLIDEIN_B' + RandomPerkChosenSoundEvent=AkEvent'WW_UI_Menu.Play_AAR_TOPWEAPON_SLIDEIN_B' VIPLowHealthSoundEvent=AkEvent'WW_GLO_Runtime.WeeklyVIPAlarm' VIPLowHealthLastTimePlayed = 0.f } diff --git a/KFGame/Classes/KFProfileSettings.uc b/KFGame/Classes/KFProfileSettings.uc index 5d06255..8c530b3 100644 --- a/KFGame/Classes/KFProfileSettings.uc +++ b/KFGame/Classes/KFProfileSettings.uc @@ -247,6 +247,7 @@ defaultproperties ProfileMappings.Add((Id=KFID_SavedDifficultyIndex, Name="SavedDifficultyIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_SavedLengthIndex, Name="SavedLengthIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_SavedPrivacyIndex, Name="SavedPrivacyIndex", MappingType=PVMT_RawValue)) + ProfileMappings.Add((Id=KFID_SavedAllowSeasonalSkinsIndex, Name="SavedAllowSeasonalSkinsIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_SavedServerTypeIndex, Name="SavedServerTypeIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_SavedInProgressIndex, Name="SavedInProgressIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_ControllerSoundEnabled, Name="Controller Sound Enabled", MappingType=PVMT_RawValue)) @@ -332,6 +333,7 @@ defaultproperties DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedDifficultyIndex,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedLengthIndex,Data=(Type=SDT_Int32,Value1=0)))) //default to any DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedPrivacyIndex,Data=(Type=SDT_Int32,Value1=0)))) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedAllowSeasonalSkinsIndex,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedServerTypeIndex,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedInProgressIndex,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_ControllerSoundEnabled,Data=(Type=SDT_Int32,Value1=0)))) diff --git a/KFGame/Classes/KFProj_BallisticExplosive.uc b/KFGame/Classes/KFProj_BallisticExplosive.uc index 7431234..2422b97 100644 --- a/KFGame/Classes/KFProj_BallisticExplosive.uc +++ b/KFGame/Classes/KFProj_BallisticExplosive.uc @@ -56,6 +56,8 @@ var() vector LandedTranslationOffset; //to keep track if we are hitting an actor multiple times. var array ImpactList; +var() bool bCanApplyDemolitionistPerks; + replication { if ( Role == ROLE_Authority && !bNetOwner ) @@ -388,7 +390,10 @@ simulated function bool TraceProjHitZones(Pawn P, vector EndTrace, vector StartT // for nukes && concussive force simulated protected function PrepareExplosionTemplate() { - class'KFPerk_Demolitionist'.static.PrepareExplosive( Instigator, self ); + if (bCanApplyDemolitionistPerks) + { + class'KFPerk_Demolitionist'.static.PrepareExplosive( Instigator, self ); + } super.PrepareExplosionTemplate(); } @@ -474,5 +479,7 @@ defaultproperties bCollideWithTeammates=false bDamageDestructiblesOnTouch=true + + bCanApplyDemolitionistPerks=true } diff --git a/KFGame/Classes/KFSeasonalEventStats.uc b/KFGame/Classes/KFSeasonalEventStats.uc index f592a16..96f6b8c 100644 --- a/KFGame/Classes/KFSeasonalEventStats.uc +++ b/KFGame/Classes/KFSeasonalEventStats.uc @@ -20,6 +20,7 @@ var protected int bObjectiveIsValidForMap[5]; // whether this seasonal stats object is the stats object that is being tracked right now final native function bool IsValid(); final protected native function IncrementSeasonalEventStat(int StatIdx, int Inc); +final protected native function ResetSeasonalEventStat(int StatIdx); final protected native function SetSeasonalEventStatsMax(int StatMax1, int StatMax2, int StatMax3, int StatMax4, int StatMax5); final protected native function GrantEventItem(int ItemId); @@ -31,6 +32,10 @@ private event Initialize(string MapName); static private event bool AllowEventBossOverrideForMap(string MapName); private event GrantEventItems(); +simulated event OnStatsInitialized() +{ +} + final protected simulated function FinishedObjective(int EventIndex, int ObjectiveIndex) { local KFPlayerController KFPC; @@ -75,3 +80,4 @@ simulated event OnTryCompleteObjective(int ObjectiveIndex, int EventIndex); simulated function OnHitTaken(); simulated function OnHitGiven(class DT); simulated function OnWeaponPurchased(class WeaponDef, int Price); +simulated function OnAfflictionCaused(EAfflictionType Type); \ No newline at end of file diff --git a/KFGame/Classes/KFUnlockManager.uc b/KFGame/Classes/KFUnlockManager.uc index b5b4324..934f6d8 100644 --- a/KFGame/Classes/KFUnlockManager.uc +++ b/KFGame/Classes/KFUnlockManager.uc @@ -36,7 +36,9 @@ enum ESharedContentUnlock SCU_AutoTurret, SCU_ShrinkRayGun, SCU_Scythe, - SCU_G36C + SCU_G36C, + SCU_HVStormCannon, + SCU_ZedMKIII }; @@ -383,4 +385,12 @@ defaultproperties Name=KFWeap_AssaultRifle_G36C, IconPath="WEP_UI_G36C_TEX.UI_WeaponSelect_G36C", ID=9484)} + SharedContentList(SCU_HVStormCannon)={( + Name=KFWeap_HVStormCannon, + IconPath="wep_ui_hvstormcannon_tex.UI_WeaponSelect_HVStormCannon", + ID=9569)} + SharedContentList(SCU_ZedMKIII)={( + Name=KFWeap_ZedMKIII, + IconPath="wep_ui_zedmkiii_tex.UI_WeaponSelect_ZEDMKIII", + ID=9575)} } diff --git a/KFGame/Classes/KFWeapDef_Doshinegun.uc b/KFGame/Classes/KFWeapDef_Doshinegun.uc index 6e33165..5ee1432 100644 --- a/KFGame/Classes/KFWeapDef_Doshinegun.uc +++ b/KFGame/Classes/KFWeapDef_Doshinegun.uc @@ -14,7 +14,7 @@ DefaultProperties { WeaponClassPath="KFGameContent.KFWeap_AssaultRifle_Doshinegun" - BuyPrice=600 //750 + BuyPrice=400 //600 //750 ImagePath="WEP_UI_Doshinegun_TEX.UI_Weapon_Select_Doshinegun" EffectiveRange=68 diff --git a/KFGame/Classes/KFWeapDef_HRG_BallisticBouncer.uc b/KFGame/Classes/KFWeapDef_HRG_BallisticBouncer.uc new file mode 100644 index 0000000..9a6a956 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_BallisticBouncer.uc @@ -0,0 +1,26 @@ +//============================================================================= +// KFWeapDef_HRG_BallisticBouncer +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_BallisticBouncer extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_BallisticBouncer" + + BuyPrice=900 + AmmoPricePerMag=40 + ImagePath="WEP_UI_HRG_BallisticBouncer_TEX.UI_WeaponSelect_HRG_BallisticBouncer" + + EffectiveRange=70 + + UpgradePrice[0]=700 + UpgradePrice[1]=1500 + + UpgradeSellPrice[0]=525 + UpgradeSellPrice[1]=1650 +} \ No newline at end of file diff --git a/KFGame/Classes/KFWeapDef_HRG_MedicMissile.uc b/KFGame/Classes/KFWeapDef_HRG_MedicMissile.uc new file mode 100644 index 0000000..6181bb3 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_MedicMissile.uc @@ -0,0 +1,24 @@ +//============================================================================= +// KFWeapDef_HRG_MedicMissile +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_MedicMissile extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_MedicMissile" + + BuyPrice=1600 + AmmoPricePerMag=25 + ImagePath="WEP_UI_HRG_MedicMissile_TEX.UI_WeaponSelect_HRG_MedicMissile" + + EffectiveRange=100 + + UpgradePrice[0]=1500 + + UpgradeSellPrice[0]=1125 +} \ No newline at end of file diff --git a/KFGame/Classes/KFWeapDef_HVStormCannon.uc b/KFGame/Classes/KFWeapDef_HVStormCannon.uc new file mode 100644 index 0000000..4b73694 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HVStormCannon.uc @@ -0,0 +1,25 @@ +//============================================================================= +// KFWeapDef_HVStormCannon +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HVStormCannon extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HVStormCannon" + + BuyPrice=1400 + AmmoPricePerMag=40 + ImagePath="wep_ui_hvstormcannon_tex.UI_WeaponSelect_HVStormCannon" + EffectiveRange=100 + + UpgradePrice[0]=1500 + + UpgradeSellPrice[0]=1125 + + SharedUnlockId=SCU_HVStormCannon +} \ No newline at end of file diff --git a/KFGame/Classes/KFWeapDef_ZedMKIII.uc b/KFGame/Classes/KFWeapDef_ZedMKIII.uc new file mode 100644 index 0000000..b807304 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_ZedMKIII.uc @@ -0,0 +1,22 @@ +//============================================================================= +// KFWeapDef_ZedMKIII +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_ZedMKIII extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_ZedMKIII" + + BuyPrice=2000 + AmmoPricePerMag=60 + ImagePath="wep_ui_zedmkiii_tex.UI_WeaponSelect_ZEDMKIII" + + EffectiveRange=100 + + SharedUnlockId=SCU_ZedMKIII +} \ No newline at end of file diff --git a/KFGame/Classes/KFWeap_ShotgunBase.uc b/KFGame/Classes/KFWeap_ShotgunBase.uc index 387ed9b..504f348 100644 --- a/KFGame/Classes/KFWeap_ShotgunBase.uc +++ b/KFGame/Classes/KFWeap_ShotgunBase.uc @@ -28,6 +28,42 @@ simulated function KFProjectile SpawnAllProjectiles(class KFProjCl return super.SpawnAllProjectiles(KFProjClass, RealStartLoc, AimDir); } +simulated function ApplyKickMomentum(float Momentum, float FallingMomentumReduction) +{ + local vector UsedKickMomentum; + + if (Instigator != none ) + { + UsedKickMomentum.X = -Momentum; + + if( Instigator.Physics == PHYS_Falling ) + { + UsedKickMomentum = UsedKickMomentum >> Instigator.GetViewRotation(); + UsedKickMomentum *= FallingMomentumReduction; + + NotifyShotgunJump(); + } + else + { + UsedKickMomentum = UsedKickMomentum >> Instigator.Rotation; + UsedKickMomentum.Z = 0; + } + + Instigator.AddVelocity(UsedKickMomentum,Instigator.Location,none); + } +} + +simulated function NotifyShotgunJump() +{ + local KFPlayerController KFPC; + + KFPC = KFPlayerController(Instigator.Controller); + if (KFPC != none) + { + KFPC.SetShotgunJump(true); + } +} + /********************************************************************************************* * @name Trader *********************************************************************************************/ diff --git a/KFGame/Classes/KFWeapon.uc b/KFGame/Classes/KFWeapon.uc index 38da260..7f4f1bf 100644 --- a/KFGame/Classes/KFWeapon.uc +++ b/KFGame/Classes/KFWeapon.uc @@ -4387,7 +4387,10 @@ event RecieveClientImpact(byte FiringMode, const out ImpactInfo Impact, optional event RecieveClientProjectileExplosion(vector ExplosionLocation, KFProjectile ExplodingProjectile) { // TODO: maybe validate the explosion normal on the client? - ExplodingProjectile.CallExplode( ExplosionLocation, vect(0,0,1) ); + if (ExplodingProjectile != none) + { + ExplodingProjectile.CallExplode( ExplosionLocation, vect(0,0,1) ); + } } /** diff --git a/KFGame/Classes/KFWeaponSkinList.uc b/KFGame/Classes/KFWeaponSkinList.uc index 80fd17f..550995c 100644 --- a/KFGame/Classes/KFWeaponSkinList.uc +++ b/KFGame/Classes/KFWeaponSkinList.uc @@ -3711,6 +3711,42 @@ defaultproperties //G36C Sahara Skins.Add((Id=9479, Weapondef=class'KFWeapDef_G36C', MIC_1P=("wep_skinset64_mat.Wep_1P_Sahara_G36C_MIC","wep_skinset64_mat.Wep_1P_Sahara_Scope_G36C_MIC"), MIC_3P="wep_skinset64_mat.Wep_3P_Sahara_G36C_MIC", MIC_Pickup="wep_skinset64_mat.Wep_3P_Sahara_G36C_Pickup_MIC")) +//HVStormCannon Standard + Skins.Add((Id=9569, Weapondef=class'KFWeapDef_HVStormCannon', MIC_1P=("WEP_1P_HVStormCannon_MAT.Wep_1P_HVStormCannon_Scope_MIC","WEP_1P_HVStormCannon_MAT.Wep_1stP_HVStormCannon_Lens_MIC","WEP_1P_HVStormCannon_MAT.Wep_1P_HVStormCannon_MIC"), MIC_3P="wep_3p_hvstormcannon_mat.WEP_3P_HVStormCannon_MIC", MIC_Pickup="wep_3p_hvstormcannon_mat.3P_Pickup_HVStormCannon_MIC")) + +//HVStormCannon Lost Planet + Skins.Add((Id=9570, Weapondef=class'KFWeapDef_HVStormCannon', MIC_1P=("wep_skinset70_mat.Wep_1P_StormCannon_LostPlanet_Scope_MIC","WEP_1P_HVStormCannon_MAT.Wep_1stP_HVStormCannon_Lens_MIC","wep_skinset70_mat.Wep_1P_StormCannon_LostPlanet_MIC"), MIC_3P="wep_skinset70_mat.Wep_3P_StormCannon_LostPlanet_MIC", MIC_Pickup="wep_skinset70_mat.Wep_3P_StormCannon_LostPlanet_Pickup_MIC")) + +//HVStormCannon Morph + Skins.Add((Id=9571, Weapondef=class'KFWeapDef_HVStormCannon', MIC_1P=("wep_skinset70_mat.Wep_1P_StormCannon_Xeno_Scope_MIC","WEP_1P_HVStormCannon_MAT.Wep_1stP_HVStormCannon_Lens_MIC","wep_skinset70_mat.Wep_1P_StormCannon_Xeno_MIC"), MIC_3P="wep_skinset70_mat.Wep_3P_StormCannon_Xeno_MIC", MIC_Pickup="wep_skinset70_mat.Wep_3P_StormCannon_Xeno_Pickup_MIC")) + +//HVStormCannon Rebel + Skins.Add((Id=9572, Weapondef=class'KFWeapDef_HVStormCannon', MIC_1P=("wep_skinset70_mat.Wep_1P_StormCannon_Rebel_Scope_MIC","WEP_1P_HVStormCannon_MAT.Wep_1stP_HVStormCannon_Lens_MIC","wep_skinset70_mat.Wep_1P_StormCannon_Rebel_MIC"), MIC_3P="wep_skinset70_mat.Wep_3P_StormCannon_Rebel_MIC", MIC_Pickup="wep_skinset70_mat.Wep_3P_StormCannon_Rebel_Pickup_MIC")) + +//HVStormCannon Space Man + Skins.Add((Id=9573, Weapondef=class'KFWeapDef_HVStormCannon', MIC_1P=("wep_skinset70_mat.Wep_1P_StormCannon_SpaceMan_Scope_MIC","WEP_1P_HVStormCannon_MAT.Wep_1stP_HVStormCannon_Lens_MIC","wep_skinset70_mat.Wep_1P_StormCannon_SpaceMan_MIC"), MIC_3P="wep_skinset70_mat.Wep_3P_StormCannon_SpaceMan_MIC", MIC_Pickup="wep_skinset70_mat.Wep_3P_StormCannon_SpaceMan_Pickup_MIC")) + +//HVStormCannon Tycho + Skins.Add((Id=9574, Weapondef=class'KFWeapDef_HVStormCannon', MIC_1P=("wep_skinset70_mat.Wep_1P_StormCannon_Tycho_Scope_MIC","WEP_1P_HVStormCannon_MAT.Wep_1stP_HVStormCannon_Lens_MIC","wep_skinset70_mat.Wep_1P_StormCannon_Tycho_MIC"), MIC_3P="wep_skinset70_mat.Wep_3P_StormCannon_Tycho_MIC", MIC_Pickup="wep_skinset70_mat.Wep_3P_StormCannon_Tycho_Pickup_MIC")) + +//ZEDMKIII Standard + Skins.Add((Id=9575, Weapondef=class'KFWeapDef_ZedMKIII', MIC_1P=("WEP_1P_ZEDMKIII_MAT.Wep_1stP_ZEDMKIII_MIC"), MIC_3P="Wep_3P_ZEDMKIII_MAT.Wep_3rdP_ZEDMKIII_MIC", MIC_Pickup="wep_3p_zedmkiii_mat.Wep_3rdP_ZEDMKIII_Pickup_MIC")) + +//ZEDMKIII Army + Skins.Add((Id=9576, Weapondef=class'KFWeapDef_ZedMKIII', MIC_1P=("WEP_SkinSet71_MAT.Wep_1P_ZEDMKIII_Army_MIC"), MIC_3P="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Army_MIC", MIC_Pickup="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Army_Pickup_MIC")) + +//ZEDMKIII Delux + Skins.Add((Id=9577, Weapondef=class'KFWeapDef_ZedMKIII', MIC_1P=("WEP_SkinSet71_MAT.Wep_1P_ZEDMKIII_Deluxe_MIC"), MIC_3P="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Deluxe_MIC", MIC_Pickup="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Deluxe_MIC")) + +//ZEDMKIII Ice Storm + Skins.Add((Id=9578, Weapondef=class'KFWeapDef_ZedMKIII', MIC_1P=("WEP_SkinSet71_MAT.Wep_1P_ZEDMKIII_Ice_MIC"), MIC_3P="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Ice_MIC", MIC_Pickup="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Ice_Pickup_MIC")) + +//ZEDMKIII Industrial + Skins.Add((Id=9579, Weapondef=class'KFWeapDef_ZedMKIII', MIC_1P=("WEP_SkinSet71_MAT.Wep_1P_ZEDMKIII_Industrial_MIC"), MIC_3P="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Industrial_MIC", MIC_Pickup="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Industrial_Pickup_MIC")) + +//ZEDMKIII Tiger + Skins.Add((Id=9580, Weapondef=class'KFWeapDef_ZedMKIII', MIC_1P=("WEP_SkinSet71_MAT.Wep_1P_ZEDMKIII_Tiger_MIC"), MIC_3P="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Tiger_MIC", MIC_Pickup="WEP_SkinSet71_MAT.Wep_3P_ZEDMKIII_Tiger_MIC")) + //BeyondHorizon AA12 Skins.Add((Id=8845, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet43_MAT.space_aa12.Space_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_aa12.Space_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_aa12.Space_AA12_3P_Pickup_MIC")) @@ -4009,7 +4045,7 @@ defaultproperties Skins.Add((Id=9351, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("wep_skinset58_mat.chameleon_deagle.Chameleon_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleon_deagle.Chameleon_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleon_deagle.Chameleon_Deagle_3P_Pickup_MIC")) //Chameleon Dynamic Katana - Skins.Add((Id=9352, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Katana', MIC_1P=("wep_skinset58_mat.chameleon_katana.Chameleon_Katana_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleon_katana.Chameleon_Katana_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleon_katana.Chameleon_Katana_3P_Pickup_MIC")) + Skins.Add((Id=9352, Weapondef=class'KFWeapDef_Katana', MIC_1P=("wep_skinset58_mat.chameleon_katana.Chameleon_Katana_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleon_katana.Chameleon_Katana_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleon_katana.Chameleon_Katana_3P_Pickup_MIC")) //Chameleon Dynamic Kriss SMG Skins.Add((Id=9353, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Kriss', MIC_1P=("wep_skinset58_mat.chameleon_kriss.Chameleon_Kriss_1P_Mint_MIC", "wep_skinset58_mat.chameleon_kriss.Chameleon_Kriss_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleon_kriss.Chameleon_Kriss_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleon_kriss.Chameleon_Kriss_3P_Pickup_MIC")) @@ -4021,7 +4057,7 @@ defaultproperties Skins.Add((Id=9355, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("wep_skinset58_mat.chameleonrgb_deagle.ChameleonRGB_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleonrgb_deagle.ChameleonRGB_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleonrgb_deagle.ChameleonRGB_Deagle_3P_Pickup_MIC")) //Chameleon Dynamic RGB Katana - Skins.Add((Id=9356, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Katana', MIC_1P=("wep_skinset58_mat.chameleonrgb_katana.ChameleonRGB_Katana_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleonrgb_katana.ChameleonRGB_Katana_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleonrgb_katana.ChameleonRGB_Katana_3P_Pickup_MIC")) + Skins.Add((Id=9356, Weapondef=class'KFWeapDef_Katana', MIC_1P=("wep_skinset58_mat.chameleonrgb_katana.ChameleonRGB_Katana_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleonrgb_katana.ChameleonRGB_Katana_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleonrgb_katana.ChameleonRGB_Katana_3P_Pickup_MIC")) //Chameleon Dynamic RGB Kriss SMG Skins.Add((Id=9357, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Kriss', MIC_1P=("wep_skinset58_mat.chameleonrgb_kriss.ChameleonRGB_Kriss_1P_Mint_MIC", "wep_skinset58_mat.chameleonrgb_kriss.ChameleonRGB_Kriss_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleonrgb_kriss.ChameleonRGB_Kriss_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleonrgb_kriss.ChameleonRGB_Kriss_3P_Pickup_MIC")) @@ -4316,4 +4352,184 @@ defaultproperties //Xeno Dynamic RGB Hemoclobber Skins.Add((Id=9432, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet63_MAT.xenorgb_medicbat.XenoRGB_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet63_MAT.xenorgb_medicbat.XenoRGB_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet63_MAT.xenorgb_medicbat.XenoRGB_MedicBat_3P_Pickup_MIC")) + +//Chameleon Dynamic Heckler & Koch UMP + Skins.Add((Id=9485, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("wep_skinset68_mat.chameleon_hk_ump.Chameleon_HK_UMP_1P_Mint_MIC", "wep_skinset68_mat.chameleon_hk_ump.Chameleon_HK_UMP_Sight_1P_Mint_MIC"), MIC_3P="wep_skinset68_mat.chameleon_hk_ump.Chameleon_HK_UMP_3P_Mint_MIC", MIC_Pickup="wep_skinset68_mat.chameleon_hk_ump.Chameleon_HK_UMP_3P_Pickup_MIC")) + +//Chameleon Dynamic Helios Rifle + Skins.Add((Id=9486, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("wep_skinset68_mat.chameleon_microwaveassault.Chameleon_MicrowaveAssault_1P_Mint_MIC"), MIC_3P="wep_skinset68_mat.chameleon_microwaveassault.Chameleon_MicrowaveAssault_3P_Mint_MIC", MIC_Pickup="wep_skinset68_mat.chameleon_microwaveassault.Chameleon_MicrowaveAssault_3P_Pickup_MIC")) + +//Chameleon Dynamic HMTech-501 Grenade Rifle + Skins.Add((Id=9487, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicRifleGrenadeLauncher', MIC_1P=("wep_skinset68_mat.chameleon_medicgrenadelauncher.Chameleon_MedicGrenadeLauncher_1P_Mint_MIC"), MIC_3P="wep_skinset68_mat.chameleon_medicgrenadelauncher.Chameleon_MedicGrenadeLauncher_3P_Mint_MIC", MIC_Pickup="wep_skinset68_mat.chameleon_medicgrenadelauncher.Chameleon_MedicGrenadeLauncher_3P_Pickup_MIC")) + +//Chameleon Dynamic M32 + Skins.Add((Id=9488, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M32', MIC_1P=("wep_skinset68_mat.chameleon_m32.Chameleon_M32_1P_Mint_MIC", "wep_skinset68_mat.chameleon_m32.Chameleon_M32_Sight_1P_Mint_MIC"), MIC_3P="wep_skinset68_mat.chameleon_m32.Chameleon_M32_3P_Mint_MIC", MIC_Pickup="wep_skinset68_mat.chameleon_m32.Chameleon_M32_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB Heckler & Koch UMP + Skins.Add((Id=9489, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("wep_skinset68_mat.chameleonrgb_hk_ump.ChameleonRGB_HK_UMP_1P_Mint_MIC", "wep_skinset68_mat.chameleonrgb_hk_ump.ChameleonRGB_HK_UMP_Sight_1P_Mint_MIC"), MIC_3P="wep_skinset68_mat.chameleonrgb_hk_ump.ChameleonRGB_HK_UMP_3P_Mint_MIC", MIC_Pickup="wep_skinset68_mat.chameleonrgb_hk_ump.ChameleonRGB_HK_UMP_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB Helios Rifle + Skins.Add((Id=9490, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("wep_skinset68_mat.chameleonrgb_microwaveassault.ChameleonRGB_MicrowaveAssault_1P_Mint_MIC"), MIC_3P="wep_skinset68_mat.chameleonrgb_microwaveassault.ChameleonRGB_MicrowaveAssault_3P_Mint_MIC", MIC_Pickup="wep_skinset68_mat.chameleonrgb_microwaveassault.ChameleonRGB_MicrowaveAssault_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB HMTech-501 Grenade Rifle + Skins.Add((Id=9491, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicRifleGrenadeLauncher', MIC_1P=("wep_skinset68_mat.chameleonrgb_medicgrenadelauncher.ChameleonRGB_MedicGrenadeLauncher_1P_Mint_MIC"), MIC_3P="wep_skinset68_mat.chameleonrgb_medicgrenadelauncher.ChameleonRGB_MedicGrenadeLauncher_3P_Mint_MIC", MIC_Pickup="wep_skinset68_mat.chameleonrgb_medicgrenadelauncher.ChameleonRGB_MedicGrenadeLauncher_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB M32 + Skins.Add((Id=9492, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M32', MIC_1P=("wep_skinset68_mat.chameleonrgb_m32.ChameleonRGB_M32_1P_Mint_MIC", "wep_skinset68_mat.chameleonrgb_m32.ChameleonRGB_M32_Sight_1P_Mint_MIC"), MIC_3P="wep_skinset68_mat.chameleonrgb_m32.ChameleonRGB_M32_3P_Mint_MIC", MIC_Pickup="wep_skinset68_mat.chameleonrgb_m32.ChameleonRGB_M32_3P_Pickup_MIC")) + +//Medieval Mint BattleAxe + Skins.Add((Id=9494, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AbominationAxe', MIC_1P=("WEP_SkinSet67_MAT.medieval_krampusaxe.Medieval_KrampusAxe_1P_Mint_MIC"), MIC_3P="WEP_SkinSet67_MAT.medieval_krampusaxe.Medieval_KrampusAxe_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet67_MAT.medieval_krampusaxe.Medieval_KrampusAxe_3P_Pickup_MIC")) + +//Medieval Mint Boomstick + Skins.Add((Id=9495, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_DoubleBarrel', MIC_1P=("WEP_SkinSet67_MAT.medieval_doublebarrel.Medieval_DoubleBarrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet67_MAT.medieval_doublebarrel.Medieval_DoubleBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet67_MAT.medieval_doublebarrel.Medieval_DoubleBarrel_3P_Pickup_MIC")) + +//Medieval Mint Pulverizer + Skins.Add((Id=9496, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet67_MAT.medieval_pulverizer.Medieval_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet67_MAT.medieval_pulverizer.Medieval_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet67_MAT.medieval_pulverizer.Medieval_Pulverizer_3P_Pickup_MIC")) + +//Medieval Mint RPG-7 + Skins.Add((Id=9497, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet67_MAT.medieval_rpg7.Medieval_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet67_MAT.medieval_rpg7.Medieval_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet67_MAT.medieval_rpg7.Medieval_RPG7_3P_Pickup_MIC")) + +//Medieval Precious BattleAxe + Skins.Add((Id=9498, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AbominationAxe', MIC_1P=("WEP_SkinSet67_MAT.medievalgold_krampusaxe.MedievalGold_KrampusAxe_1P_Mint_MIC"), MIC_3P="WEP_SkinSet67_MAT.medievalgold_krampusaxe.MedievalGold_KrampusAxe_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet67_MAT.medievalgold_krampusaxe.MedievalGold_KrampusAxe_3P_Pickup_MIC")) + +//Medieval Precious Boomstick + Skins.Add((Id=9499, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_DoubleBarrel', MIC_1P=("WEP_SkinSet67_MAT.medievalgold_DoubleBarrel.MedievalGold_DoubleBarrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet67_MAT.medievalgold_DoubleBarrel.MedievalGold_DoubleBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet67_MAT.medievalgold_DoubleBarrel.MedievalGold_DoubleBarrel_3P_Pickup_MIC")) + +//Medieval Precious Pulverizer + Skins.Add((Id=9500, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet67_MAT.medievalgold_pulverizer.MedievalGold_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet67_MAT.medievalgold_pulverizer.MedievalGold_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet67_MAT.medievalgold_pulverizer.MedievalGold_Pulverizer_3P_Pickup_MIC")) + +//Medieval Precious RPG-7 + Skins.Add((Id=9501, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet67_MAT.medievalgold_rpg7.MedievalGold_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet67_MAT.medievalgold_rpg7.MedievalGold_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet67_MAT.medievalgold_rpg7.MedievalGold_RPG7_3P_Pickup_MIC")) + +//Tacticool Dynamic AA12 + Skins.Add((Id=9544, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet66_MAT.tacticool_aa12.Tacticool_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet66_MAT.tacticool_aa12.Tacticool_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet66_MAT.tacticool_aa12.Tacticool_AA12_3P_Pickup_MIC")) + +//Tacticool Dynamic M99 + Skins.Add((Id=9545, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M99', MIC_1P=("WEP_SkinSet66_MAT.tacticool_m99.Tacticool_M99_1P_Mint_MIC", "WEP_SkinSet66_MAT.tacticool_m99.Tacticool_M99_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet66_MAT.tacticool_m99.Tacticool_M99_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet66_MAT.tacticool_m99.Tacticool_M99_3P_Pickup_MIC")) + +//Tacticool Dynamic MP5RAS + Skins.Add((Id=9546, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet66_MAT.tacticool_mp5ras.Tacticool_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet66_MAT.tacticool_mp5ras.Tacticool_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet66_MAT.tacticool_mp5ras.Tacticool_MP5RAS_3P_Pickup_MIC")) + +//Tacticool Dynamic SCAR + Skins.Add((Id=9547, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet66_MAT.tacticool_scar.Tacticool_SCAR_1P_Mint_MIC", "WEP_SkinSet66_MAT.tacticool_scar.Tacticool_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet66_MAT.tacticool_scar.Tacticool_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet66_MAT.tacticool_scar.Tacticool_SCAR_3P_Pickup_MIC")) + +//Tacticool Dynamic Precious AA12 + Skins.Add((Id=9548, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet66_MAT.tacticool_aa12.TacticoolPrecious_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet66_MAT.tacticool_aa12.TacticoolPrecious_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet66_MAT.tacticool_aa12.TacticoolPrecious_AA12_3P_Pickup_MIC")) + +//Tacticool Dynamic Precious M99 + Skins.Add((Id=9549, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M99', MIC_1P=("WEP_SkinSet66_MAT.tacticool_m99.TacticoolPrecious_M99_1P_Mint_MIC", "WEP_SkinSet66_MAT.tacticool_m99.TacticoolPrecious_M99_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet66_MAT.tacticool_m99.TacticoolPrecious_M99_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet66_MAT.tacticool_m99.TacticoolPrecious_M99_3P_Pickup_MIC")) + +//Tacticool Dynamic Precious MP5RAS + Skins.Add((Id=9550, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet66_MAT.tacticool_mp5ras.TacticoolPrecious_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet66_MAT.tacticool_mp5ras.TacticoolPrecious_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet66_MAT.tacticool_mp5ras.TacticoolPrecious_MP5RAS_3P_Pickup_MIC")) + +//Tacticool Dynamic Precious SCAR + Skins.Add((Id=9551, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet66_MAT.tacticool_scar.TacticoolPrecious_SCAR_1P_Mint_MIC", "WEP_SkinSet66_MAT.tacticool_scar.TacticoolPrecious_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet66_MAT.tacticool_scar.TacticoolPrecious_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet66_MAT.tacticool_scar.TacticoolPrecious_SCAR_3P_Pickup_MIC")) + +//Retro Arcade P90 + Skins.Add((Id=9504, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet69_MAT.retro_p90.RetroConsole_P90_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_p90.RetroConsole_P90_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_p90.RetroConsole_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_p90.RetroConsole_P90_3P_Pickup_MIC")) + +//Retro Light Noir P90 + Skins.Add((Id=9505, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet69_MAT.retro_p90.RetroLightNoir_P90_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_p90.RetroLightNoir_P90_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_p90.RetroLightNoir_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_p90.RetroLightNoir_P90_3P_Pickup_MIC")) + +//Retro Dark Noir P90 + Skins.Add((Id=9506, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet69_MAT.retro_p90.RetroDarkNoir_P90_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_p90.RetroDarkNoir_P90_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_p90.RetroDarkNoir_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_p90.RetroDarkNoir_P90_3P_Pickup_MIC")) + +//Retro Cyber P90 + Skins.Add((Id=9507, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet69_MAT.retro_p90.RetroCyber_P90_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_p90.RetroCyber_P90_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_p90.RetroCyber_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_p90.RetroCyber_P90_3P_Pickup_MIC")) + +//Retro Synth P90 + Skins.Add((Id=9508, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet69_MAT.retro_p90.RetroSynth_P90_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_p90.RetroSynth_P90_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_p90.RetroSynth_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_p90.RetroSynth_P90_3P_Pickup_MIC")) + +//Retro Precious P90 + Skins.Add((Id=9509, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet69_MAT.retro_p90.RetroPrecious_P90_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_p90.RetroPrecious_P90_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_p90.RetroPrecious_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_p90.RetroPrecious_P90_3P_Pickup_MIC")) + +//Retro Arcade Static Strikers + Skins.Add((Id=9510, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_PowerGloves', MIC_1P=("WEP_SkinSet69_MAT.retro_staticstrikers.RetroConsole_StaticStrikers_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_staticstrikers.RetroConsole_StaticStrikers_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_staticstrikers.RetroConsole_StaticStrikers_3P_Pickup_MIC")) + +//Retro Light Noir Static Strikers + Skins.Add((Id=9511, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_PowerGloves', MIC_1P=("WEP_SkinSet69_MAT.retro_staticstrikers.RetroLightNoir_StaticStrikers_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_staticstrikers.RetroLightNoir_StaticStrikers_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_staticstrikers.RetroLightNoir_StaticStrikers_3P_Pickup_MIC")) + +//Retro Dark Noir Static Strikers + Skins.Add((Id=9512, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_PowerGloves', MIC_1P=("WEP_SkinSet69_MAT.retro_staticstrikers.RetroDarkNoir_StaticStrikers_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_staticstrikers.RetroDarkNoir_StaticStrikers_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_staticstrikers.RetroDarkNoir_StaticStrikers_3P_Pickup_MIC")) + +//Retro Cyber Static Strikers + Skins.Add((Id=9513, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_PowerGloves', MIC_1P=("WEP_SkinSet69_MAT.retro_staticstrikers.RetroCyber_StaticStrikers_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_staticstrikers.RetroCyber_StaticStrikers_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_staticstrikers.RetroCyber_StaticStrikers_3P_Pickup_MIC")) + +//Retro Synth Static Strikers + Skins.Add((Id=9514, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_PowerGloves', MIC_1P=("WEP_SkinSet69_MAT.retro_staticstrikers.RetroSynth_StaticStrikers_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_staticstrikers.RetroSynth_StaticStrikers_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_staticstrikers.RetroSynth_StaticStrikers_3P_Pickup_MIC")) + +//Retro Precious Static Strikers + Skins.Add((Id=9515, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_PowerGloves', MIC_1P=("WEP_SkinSet69_MAT.retro_staticstrikers.RetroPrecious_StaticStrikers_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_staticstrikers.RetroPrecious_StaticStrikers_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_staticstrikers.RetroPrecious_StaticStrikers_3P_Pickup_MIC")) + +//Retro Arcade HZ12 + Skins.Add((Id=9516, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HZ12', MIC_1P=("WEP_SkinSet69_MAT.retro_hz12.RetroConsole_HZ12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_hz12.RetroConsole_HZ12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_hz12.RetroConsole_HZ12_3P_Pickup_MIC")) + +//Retro Light Noir HZ12 + Skins.Add((Id=9517, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HZ12', MIC_1P=("WEP_SkinSet69_MAT.retro_hz12.RetroLightNoir_HZ12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_hz12.RetroLightNoir_HZ12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_hz12.RetroLightNoir_HZ12_3P_Pickup_MIC")) + +//Retro Dark Noir HZ12 + Skins.Add((Id=9518, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HZ12', MIC_1P=("WEP_SkinSet69_MAT.retro_hz12.RetroDarkNoir_HZ12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_hz12.RetroDarkNoir_HZ12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_hz12.RetroDarkNoir_HZ12_3P_Pickup_MIC")) + +//Retro Cyber HZ12 + Skins.Add((Id=9519, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HZ12', MIC_1P=("WEP_SkinSet69_MAT.retro_hz12.RetroCyber_HZ12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_hz12.RetroCyber_HZ12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_hz12.RetroCyber_HZ12_3P_Pickup_MIC")) + +//Retro Synth HZ12 + Skins.Add((Id=9520, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HZ12', MIC_1P=("WEP_SkinSet69_MAT.retro_hz12.RetroSynth_HZ12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_hz12.RetroSynth_HZ12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_hz12.RetroSynth_HZ12_3P_Pickup_MIC")) + +//Retro Precious HZ12 + Skins.Add((Id=9521, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HZ12', MIC_1P=("WEP_SkinSet69_MAT.retro_hz12.RetroPrecious_HZ12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_hz12.RetroPrecious_HZ12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_hz12.RetroPrecious_HZ12_3P_Pickup_MIC")) + +//Retro Arcade Microwave Gun + Skins.Add((Id=9522, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet69_MAT.retro_microwavegun.RetroConsole_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwavegun.RetroConsole_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwavegun.RetroConsole_MicrowaveGun_3P_Pickup_MIC")) + +//Retro Light Noir Microwave Gun + Skins.Add((Id=9523, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet69_MAT.retro_microwavegun.RetroLightNoir_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwavegun.RetroLightNoir_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwavegun.RetroLightNoir_MicrowaveGun_3P_Pickup_MIC")) + +//Retro Dark Noir Microwave Gun + Skins.Add((Id=9524, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet69_MAT.retro_microwavegun.RetroDarkNoir_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwavegun.RetroDarkNoir_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwavegun.RetroDarkNoir_MicrowaveGun_3P_Pickup_MIC")) + +//Retro Cyber Microwave Gun + Skins.Add((Id=9525, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet69_MAT.retro_microwavegun.RetroCyber_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwavegun.RetroCyber_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwavegun.RetroCyber_MicrowaveGun_3P_Pickup_MIC")) + +//Retro Synth Microwave Gun + Skins.Add((Id=9526, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet69_MAT.retro_microwavegun.RetroSynth_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwavegun.RetroSynth_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwavegun.RetroSynth_MicrowaveGun_3P_Pickup_MIC")) + +//Retro Precious Microwave Gun + Skins.Add((Id=9527, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveGun', MIC_1P=("WEP_SkinSet69_MAT.retro_microwavegun.RetroPrecious_MicrowaveGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwavegun.RetroPrecious_MicrowaveGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwavegun.RetroPrecious_MicrowaveGun_3P_Pickup_MIC")) + +//Retro Arcade Helios Rifle + Skins.Add((Id=9528, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("WEP_SkinSet69_MAT.retro_microwaveassault.RetroConsole_MicrowaveAssault_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwaveassault.RetroConsole_MicrowaveAssault_3P_Mint_M", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwaveassault.RetroConsole_MicrowaveAssault_3P_Pickup_MIC")) + +//Retro Light Noir Helios Rifle + Skins.Add((Id=9529, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("WEP_SkinSet69_MAT.retro_microwaveassault.RetroLightNoir_MicrowaveAssault_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwaveassault.RetroLightNoir_MicrowaveAssault_3P_Mint_M", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwaveassault.RetroLightNoir_MicrowaveAssault_3P_Pickup_MIC")) + +//Retro Dark Noir Helios Rifle + Skins.Add((Id=9530, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("WEP_SkinSet69_MAT.retro_microwaveassault.RetroDarkNoir_MicrowaveAssault_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwaveassault.RetroDarkNoir_MicrowaveAssault_3P_Mint_M", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwaveassault.RetroDarkNoir_MicrowaveAssault_3P_Pickup_MIC")) + +//Retro Cyber Helios Rifle + Skins.Add((Id=9531, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("WEP_SkinSet69_MAT.retro_microwaveassault.RetroCyber_MicrowaveAssault_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwaveassault.RetroCyber_MicrowaveAssault_3P_Mint_M", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwaveassault.RetroCyber_MicrowaveAssault_3P_Pickup_MIC")) + +//Retro Synth Helios Rifle + Skins.Add((Id=9532, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("WEP_SkinSet69_MAT.retro_microwaveassault.RetroSynth_MicrowaveAssault_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwaveassault.RetroSynth_MicrowaveAssault_3P_Mint_M", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwaveassault.RetroSynth_MicrowaveAssault_3P_Pickup_MIC")) + +//Retro Precious Helios Rifle + Skins.Add((Id=9533, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("WEP_SkinSet69_MAT.retro_microwaveassault.RetroPrecious_MicrowaveAssault_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_microwaveassault.RetroPrecious_MicrowaveAssault_3P_Mint_M", MIC_Pickup="WEP_SkinSet69_MAT.retro_microwaveassault.RetroPrecious_MicrowaveAssault_3P_Pickup_MIC")) + +//Retro Arcade Railgun + Skins.Add((Id=9534, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Railgun', MIC_1P=("WEP_SkinSet69_MAT.retro_railgun.RetroConsole_Railgun_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_railgun.RetroConsole_Railgun_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_railgun.RetroConsole_Railgun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_railgun.RetroConsole_Railgun_3P_Pickup_MIC")) + +//Retro Light Noir Railgun + Skins.Add((Id=9535, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Railgun', MIC_1P=("WEP_SkinSet69_MAT.retro_railgun.RetroLightNoir_Railgun_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_railgun.RetroLightNoir_Railgun_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_railgun.RetroLightNoir_Railgun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_railgun.RetroLightNoir_Railgun_3P_Pickup_MIC")) + +//Retro Dark Noir Railgun + Skins.Add((Id=9536, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Railgun', MIC_1P=("WEP_SkinSet69_MAT.retro_railgun.RetroDarkNoir_Railgun_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_railgun.RetroDarkNoir_Railgun_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_railgun.RetroDarkNoir_Railgun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_railgun.RetroDarkNoir_Railgun_3P_Pickup_MIC")) + +//Retro Cyber Railgun + Skins.Add((Id=9537, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Railgun', MIC_1P=("WEP_SkinSet69_MAT.retro_railgun.RetroCyber_Railgun_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_railgun.RetroCyber_Railgun_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_railgun.RetroCyber_Railgun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_railgun.RetroCyber_Railgun_3P_Pickup_MIC")) + +//Retro Synth Railgun + Skins.Add((Id=9538, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Railgun', MIC_1P=("WEP_SkinSet69_MAT.retro_railgun.RetroSynth_Railgun_1P_Mint_MIC", "WEP_SkinSet69_MAT.retro_railgun.RetroSynth_Railgun_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet69_MAT.retro_railgun.RetroSynth_Railgun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet69_MAT.retro_railgun.RetroSynth_Railgun_3P_Pickup_MIC")) + +//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")) } \ No newline at end of file diff --git a/KFGame/Classes/KFWeeklyOutbreakInformation.uc b/KFGame/Classes/KFWeeklyOutbreakInformation.uc index 60185cd..d99d69f 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 = 18; + static const int NumWeeklyEvents = 19; } DefaultProperties { diff --git a/KFGame/KFOnlineStats.uci b/KFGame/KFOnlineStats.uci index 20d17fe..28d01b3 100644 --- a/KFGame/KFOnlineStats.uci +++ b/KFGame/KFOnlineStats.uci @@ -158,4 +158,5 @@ const STATID_ACHIEVE_NetherholdCollectibles = 4060; const STATID_ACHIEVE_CarillonHamletCollectibles = 4061; const STATID_ACHIEVE_RigCollectibles = 4062; const STATID_ACHIEVE_BarmwichCollectibles = 4063; +const STATID_ACHIEVE_CrashCollectibles = 4064; /** `endif */ diff --git a/KFGame/KFProfileSettings.uci b/KFGame/KFProfileSettings.uci index 6a9c423..05cfc0a 100644 --- a/KFGame/KFProfileSettings.uci +++ b/KFGame/KFProfileSettings.uci @@ -79,4 +79,5 @@ const KFID_SurvivalStartingGrenIdx=180; // Summer 2022 QoL: added option to choo const KFID_MouseLookUpScale=181; // Halloweeen 2022 QoL: added mouse options to menu const KFID_MouseLookRightScale=182; // Halloweeen 2022 QoL: added mouse options to menu const KFID_ViewSmoothingEnabled=183; // Halloweeen 2022 QoL: added mouse options to menu -const KFID_ViewAccelerationEnabled=184; // Halloweeen 2022 QoL: added mouse options to menu \ No newline at end of file +const KFID_ViewAccelerationEnabled=184; // Halloweeen 2022 QoL: added mouse options to menu +const KFID_SavedAllowSeasonalSkinsIndex=185; \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_MedicMissile.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_MedicMissile.uc new file mode 100644 index 0000000..579a054 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_MedicMissile.uc @@ -0,0 +1,24 @@ +//============================================================================= +// KFDT_Ballistic_HRG_MedicMissile +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_HRG_MedicMissile extends KFDT_Ballistic_Shell + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=3000 + KDeathUpKick=1000 + KDeathVel=500 + + StumblePower=10 + GunHitPower=45 + + ModifierPerkList(0)=class'KFPerk_FieldMedic' + + WeaponDef=class'KFWeapDef_HRG_MedicMissile' +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_ZedMKIII_Rocket.uc b/KFGameContent/Classes/KFDT_Ballistic_ZedMKIII_Rocket.uc new file mode 100644 index 0000000..478e26b --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_ZedMKIII_Rocket.uc @@ -0,0 +1,25 @@ +//============================================================================= +// KFDT_Ballistic_ZedMKIII_Rocket +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Ballistic_ZedMKIII_Rocket extends KFDT_Ballistic_Shell + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=3000 + KDeathUpKick=1000 + KDeathVel=500 + + KnockdownPower=50 + StumblePower=100 + GunHitPower=70 + + ModifierPerkList(0)=class'KFPerk_Demolitionist' + + WeaponDef=class'KFWeapDef_ZedMKIII' +} diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_BallisticBouncer.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_BallisticBouncer.uc new file mode 100644 index 0000000..77413fe --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_BallisticBouncer.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_BallisticBouncer +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_BallisticBouncer extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_BallisticBouncer' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_BallisticBouncer_Shot.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_BallisticBouncer_Shot.uc new file mode 100644 index 0000000..d56bcd3 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_BallisticBouncer_Shot.uc @@ -0,0 +1,26 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_BallisticBouncer_Shot +//============================================================================= +// Balls hit hard +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_BallisticBouncer_Shot extends KFDT_Bludgeon + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=900 + KDeathUpKick=-300 + KDeathVel=100 + + StumblePower=400 + GunHitPower=300 + MeleeHitPower=150 + KnockdownPower=100 + + WeaponDef=class'KFWeapDef_HRG_BallisticBouncer' +} diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_MedicMissile.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_MedicMissile.uc new file mode 100644 index 0000000..2a17567 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_MedicMissile.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_MedicMissile +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_MedicMissile extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_MedicMissile' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HVStormCannon.uc b/KFGameContent/Classes/KFDT_Bludgeon_HVStormCannon.uc new file mode 100644 index 0000000..83d3167 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HVStormCannon.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HVStormCannon +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HVStormCannon extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HVStormCannon' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_ZedMKIII.uc b/KFGameContent/Classes/KFDT_Bludgeon_ZedMKIII.uc new file mode 100644 index 0000000..e673dc0 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_ZedMKIII.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_ZedMKIII +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_ZedMKIII extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_ZedMKIII' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_EMP_HVStormCannon.uc b/KFGameContent/Classes/KFDT_EMP_HVStormCannon.uc new file mode 100644 index 0000000..ba7fd69 --- /dev/null +++ b/KFGameContent/Classes/KFDT_EMP_HVStormCannon.uc @@ -0,0 +1,54 @@ +//============================================================================= +// KFDT_EMP_HVStormCannon +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_EMP_HVStormCannon extends KFDT_EMP + abstract + hidedropdown; + +var ParticleSystem ForceImpactEffect; +var AkEvent ForceImpactSound; + +static function PlayImpactHitEffects( KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, optional Pawn HitInstigator ) +{ + local KFSkinTypeEffects SkinType; + + if ( P.CharacterArch != None && default.EffectGroup < FXG_Max ) + { + SkinType = P.GetHitZoneSkinTypeEffects( HitZoneIndex ); + + if (SkinType != none) + { + SkinType.PlayImpactParticleEffect(P, HitLocation, HitDirection, HitZoneIndex, default.EffectGroup, default.ForceImpactEffect); + SkinType.PlayTakeHitSound(P, HitLocation, HitInstigator, default.EffectGroup, default.ForceImpactSound); + } + } +} + +defaultproperties +{ + KDamageImpulse=2000 + KDeathUpKick=400 + KDeathVel=250 + + KnockdownPower=20 + StunPower=50 + StumblePower=200 + GunHitPower=150 + MeleeHitPower=100 + EMPPower=0 // Don't use the affliction here, we manage this on KFWeap_HVStormCannon to completely synchronize it with the logic of the weapon + + GoreDamageGroup=DGT_EMP + EffectGroup=FXG_Electricity + + ForceImpactEffect=ParticleSystem'WEP_HVStormCannon_EMIT.FX_HVStormCannon_Impact' + ForceImpactSound=AkEvent'WW_WEP_HVStormCannon.Play_WEP_HVStormCannon_Impact' + + WeaponDef=class'KFWeapDef_HVStormCannon' + + //Perk + ModifierPerkList(0)=class'KFPerk_Sharpshooter' +} diff --git a/KFGameContent/Classes/KFDT_Explosive_HRG_MedicMissile.uc b/KFGameContent/Classes/KFDT_Explosive_HRG_MedicMissile.uc new file mode 100644 index 0000000..4f32da6 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_HRG_MedicMissile.uc @@ -0,0 +1,32 @@ +//============================================================================= +// KFDT_Explosive_HRG_MedicMissile +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Explosive_HRG_MedicMissile extends KFDT_Explosive + abstract + hidedropdown; + +defaultproperties +{ + bShouldSpawnPersistentBlood=false + + RadialDamageImpulse=2500 + GibImpulseScale=0.15 + KDeathUpKick=1500 + KDeathVel=500 + + KnockdownPower=50 + StumblePower=200 + + bExtraMomentumZ=false + + bCanObliterate=false + + //Perk + ModifierPerkList(0)=class'KFPerk_FieldMedic' + + WeaponDef=class'KFWeapDef_HRG_MedicMissile' +} diff --git a/KFGameContent/Classes/KFDT_Explosive_ZedMKIII.uc b/KFGameContent/Classes/KFDT_Explosive_ZedMKIII.uc new file mode 100644 index 0000000..8287cd4 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_ZedMKIII.uc @@ -0,0 +1,30 @@ +//============================================================================= +// KFDT_Explosive_ZedMKIII +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Explosive_ZedMKIII extends KFDT_Explosive + abstract + hidedropdown; + +defaultproperties +{ + bShouldSpawnPersistentBlood=true + + // physics impact + RadialDamageImpulse=10000 + KDeathUpKick=2000 + KDeathVel=500 + + KnockdownPower=150 + StumblePower=350 + + //Perk + ModifierPerkList(0)=class'KFPerk_Demolitionist' + + ObliterationHealthThreshold=-500 + ObliterationDamageThreshold=500 + WeaponDef=class'KFWeapDef_ZedMKIII' +} diff --git a/KFGameContent/Classes/KFDT_Microwave_ZedMKIII.uc b/KFGameContent/Classes/KFDT_Microwave_ZedMKIII.uc new file mode 100644 index 0000000..7701682 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Microwave_ZedMKIII.uc @@ -0,0 +1,69 @@ +//============================================================================= +// KFDT_Microwave_ZedMKIII +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Microwave_ZedMKIII extends KFDT_Microwave + abstract + hidedropdown; + +var ParticleSystem ForceImpactEffect; +var AkEvent ForceImpactSound; + +/** Allows the damage type to customize exactly which hit zones it can dismember */ +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ + if( super.CanDismemberHitZone( InHitZoneName ) ) + { + return true; + } + + switch ( InHitZoneName ) + { + case 'lupperarm': + case 'rupperarm': + return true; + } + + return false; +} + +static function PlayImpactHitEffects( KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, optional Pawn HitInstigator ) +{ + local KFSkinTypeEffects SkinType; + + if ( P.CharacterArch != None && default.EffectGroup < FXG_Max ) + { + SkinType = P.GetHitZoneSkinTypeEffects( HitZoneIndex ); + + if (SkinType != none) + { + SkinType.PlayImpactParticleEffect(P, HitLocation, HitDirection, HitZoneIndex, default.EffectGroup, default.ForceImpactEffect); + SkinType.PlayTakeHitSound(P, HitLocation, HitInstigator, default.EffectGroup, default.ForceImpactSound); + } + } +} + +defaultproperties +{ + KDamageImpulse=550 + GibImpulseScale=0.85 + KDeathUpKick=-200 + KDeathVel=200 + + StumblePower=18 + StunPower=15 + GunHitPower=15 + + WeaponDef=class'KFWeapDef_ZedMKIII' + + //Perk + ModifierPerkList(0)=class'KFPerk_Demolitionist' + + EffectGroup=FXG_MicrowaveProj + + ForceImpactEffect=ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Bullet_Impact' + ForceImpactSound=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Impact' +} diff --git a/KFGameContent/Classes/KFExplosion_HRG_BallisticBouncer.uc b/KFGameContent/Classes/KFExplosion_HRG_BallisticBouncer.uc new file mode 100644 index 0000000..0891801 --- /dev/null +++ b/KFGameContent/Classes/KFExplosion_HRG_BallisticBouncer.uc @@ -0,0 +1,283 @@ +//============================================================================= +// KFExplosion_HRG_BallisticBouncer +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFExplosion_HRG_BallisticBouncer extends KFExplosionActorLingering; + +var() class HealingDamageType; +var() float HealingAmount; + +var AkEvent SmokeLoopStartEvent; +var AkEvent SmokeLoopStopEvent; + +var KFPerk CachedInstigatorPerk; + +var float fChargePercentage; + +var float fMinAmmoutHealing; +var float fMaxAmmoutHealing; + +var float fAltMinAmmoutHealing; +var float fAltMaxAmmoutHealing; + +var bool bHealsInstigator; +var bool bHealsDifferentAmmoutToInstigator; + +/* Only used if bOnlyDamagePawns is active to create fractures in walls */ +var bool bAllowFractureDamage; + +simulated function SpawnExplosionParticleSystem(ParticleSystem Template) +{ + local ParticleSystemComponent PSC; + local vector vec; + + // If the template is none, grab the default + if( !ExplosionTemplate.bAllowPerMaterialFX && Template == none ) + { + Template = KFGameExplosion(ExplosionTemplate).ExplosionEffects.DefaultImpactEffect.ParticleTemplate; + } + + // Use custom pool + PSC = WorldInfo.MyEmitterPool.SpawnEmitter(Template, Location, rotator(ExplosionTemplate.HitNormal), None); + //fChargePercentage + vec.X = fChargePercentage; + vec.Y = fChargePercentage; + vec.Z = fChargePercentage; + PSC.SetVectorParameter( name("BlobCharge"), vec); + PSC.SetFloatParameter( name("MineFxControlParam"), fChargePercentage); + + +} + +/* + * @param Direction For bDirectionalExplosion=true explosions, this is the forward direction of the blast. + * Overridden to add the ability to spawn fragments from the explosion + **/ +simulated function Explode(GameExplosion NewExplosionTemplate, optional vector Direction) +{ + local KFPawn KFP; + super.Explode(NewExplosionTemplate, Direction); + + if( Instigator != none ) + { + + KFP = KFPawn(Instigator); + if( KFP != none ) + { + CachedInstigatorPerk = KFP.GetPerk(); + } + } + + if (Role == Role_Authority) + { + //DelayedExplosionDamage(); + SetTimer(Interval, false, nameof(DelayedExplosionDamage), self); + } +} + +/** + * Deal damage or heal players + */ +protected simulated function AffectsPawn(Pawn Victim, float DamageScale) +{ + local KFPawn_Human HumanVictim; + local KFPawn_Monster MonsterVictim; + local KFProj_MedicGrenade OwnerProjectile; + local bool bCanRepairArmor; + local Box BBox; + local vector BBoxCenter; + local Actor HitActor; + local bool bDamageBlocked; + + + if( Victim != none && Victim.IsAliveAndWell() ) + { + MonsterVictim = KFPawn_Monster(Victim); + if( MonsterVictim != none ) + { + if( bWasFadedOut|| bDeleteMe || bPendingDelete ) + { + return; + } + + Victim.GetComponentsBoundingBox(BBox); + BBoxCenter = (BBox.Min + BBox.Max) * 0.5f; + HitActor = TraceExplosive(BBoxCenter, Location + vect(0, 0, 20)); + bDamageBlocked = (HitActor != None && HitActor != Victim); + if(bDamageBlocked && HitActor.IsA('KFDoorActor')) + { + bDamageBlocked = false; + } + if( !bDamageBlocked ) + { + Victim.TakeRadiusDamage(InstigatorController, ExplosionTemplate.Damage * DamageScale, ExplosionTemplate.DamageRadius, + ExplosionTemplate.MyDamageType, ExplosionTemplate.MomentumTransferScale, Location, bDoFullDamage, + (Owner != None) ? Owner : self, ExplosionTemplate.DamageFalloffExponent); + } + + } + else + { + + Victim.GetComponentsBoundingBox(BBox); + BBoxCenter = (BBox.Min + BBox.Max) * 0.5f; + HitActor = TraceExplosive(BBoxCenter, Location + vect(0, 0, 20)); + bDamageBlocked = (HitActor != None && HitActor != Victim); + if(bDamageBlocked && HitActor.IsA('KFDoorActor')) + { + bDamageBlocked = false; + } + if(!bDamageBlocked) + { + if(!bHealsInstigator && Victim == Instigator) + { + return; + } + HumanVictim = KFPawn_Human(Victim); + if( HumanVictim != none && HumanVictim.GetExposureTo(Location) > 0 ) + { + OwnerProjectile = KFProj_MedicGrenade(Owner); + if( OwnerProjectile != none ) + { + bCanRepairArmor = OwnerProjectile.HealedPawns.Find( HumanVictim ) == INDEX_NONE; + } + + if(bHealsDifferentAmmoutToInstigator && bHealsInstigator && Victim == Instigator) + { + HumanVictim.HealDamage( Lerp(fAltMinAmmoutHealing, fAltMaxAmmoutHealing, fChargePercentage), InstigatorController, HealingDamageType, bCanRepairArmor); + } + else + { + HumanVictim.HealDamage( Lerp(fMinAmmoutHealing, fMaxAmmoutHealing, fChargePercentage), InstigatorController, HealingDamageType, bCanRepairArmor); + } + + if( bCanRepairArmor ) + { + OwnerProjectile.HealedPawns.AddItem( HumanVictim ); + } + } + } + } + } +} + +protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCauseEffects) +{ + if( bWasFadedOut || bDeleteMe || bPendingDelete ) + { + return false; + } + + if( bOnlyDamagePawns && bAllowFractureDamage ) + { + ExplodeFractures(); + } + + if( bOnlyDamagePawns ) + { + return ExplodePawns(bCauseDamage); + } + + return super(KFExplosionActor).DoExplosionDamage(bCauseDamage, bCauseEffects); +} + +/** Check fractures only if */ +protected simulated function ExplodeFractures() +{ + local Actor Victim; + local bool bCauseFractureEffects; + local float CheckRadius, VictimDist; + local Box BBox; + local FracturedStaticMeshActor FracActor; + local byte WantPhysChunksAndParticles; + local TraceHitInfo HitInfo; + + if( bWasFadedOut || bDeleteMe || bPendingDelete ) + { + return; + } + + bCauseFractureEffects = WorldInfo.NetMode != NM_DedicatedServer && ExplosionTemplate.bCausesFracture; + + if( !bCauseFractureEffects ) + { + return; + } + + // determine radius to check + CheckRadius = GetEffectCheckRadius(true, bCauseFractureEffects, WorldInfo.NetMode != NM_Client); + if ( CheckRadius > 0.0 ) + { + foreach CollidingActors(class'Actor', Victim, CheckRadius, Location, ExplosionTemplate.bUseOverlapCheck,,HitInfo ) + { + if ( Victim != Self + && (!Victim.bWorldGeometry || Victim.bCanBeDamaged) + && (NavigationPoint(Victim) == None) + && Victim != ExplosionTemplate.ActorToIgnoreForDamage + && (!ExplosionTemplate.bIgnoreInstigator || Victim != Instigator) + && !ClassIsChildOf(Victim.Class, ExplosionTemplate.ActorClassToIgnoreForDamage) + && !IsBehindExplosion(Victim) ) + { + + // return if this is a pawn + if(GamePawn(Victim) != none) + { + return; + } + + // check if visible, unless physics object + // note: using bbox center instead of location, because that's what visiblecollidingactors does + Victim.GetComponentsBoundingBox(BBox); + + // adjust distance if using overlap check + VictimDist = ExplosionTemplate.bUseOverlapCheck ? BoxDistanceToPoint(Location, BBox) : VSize(Location - Victim.Location); + + FracActor = FracturedStaticMeshActor(Victim); + if ( (FracActor != None) + && (VictimDist < ExplosionTemplate.FractureMeshRadius) + && (FracActor.Physics == PHYS_None) + && FracActor.IsFracturedByDamageType(ExplosionTemplate.MyDamageType) + && FracActor.FractureEffectIsRelevant( false, Instigator, WantPhysChunksAndParticles) ) + { + // Let kismet know that we were hit by an explosion + FracActor.NotifyHitByExplosion(InstigatorController, ExplosionTemplate.Damage, ExplosionTemplate.MyDamageType); + + FracActor.BreakOffPartsInRadius(Location, ExplosionTemplate.FractureMeshRadius, ExplosionTemplate.FracturePartVel, WantPhysChunksAndParticles == 1 ? true : false); + } + } + } + } +} + +DefaultProperties +{ + HealingDamageType=class'KFDT_Healing' + HealingAmount=0; + + fMinAmmoutHealing=5; //4; + fMaxAmmoutHealing=50; //40 + + Interval=0 + MaxTime=0.0 + FadeOutTime=0.0 + + bExplodeMoreThanOnce=false + bDoFullDamage=false + + bOnlyDamagePawns=true + bAllowFractureDamage=true + + bSkipLineCheckForPawns=true + + LoopStartEvent=none + LoopStopEvent=none + +//EXPERIMENTAL FEATURES FOR DESIGN + bHealsInstigator = false; + bHealsDifferentAmmoutToInstigator = false; + fAltMinAmmoutHealing=1; + fAltMaxAmmoutHealing=5; +} diff --git a/KFGameContent/Classes/KFExplosion_HRG_MedicMissile.uc b/KFGameContent/Classes/KFExplosion_HRG_MedicMissile.uc new file mode 100644 index 0000000..7267cf0 --- /dev/null +++ b/KFGameContent/Classes/KFExplosion_HRG_MedicMissile.uc @@ -0,0 +1,73 @@ +//============================================================================= +// KFExplosion_HRG_MedicMissile +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFExplosion_HRG_MedicMissile extends KFExplosionActorLingering; + +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; +} + +protected simulated function AffectsPawn(Pawn Victim, float DamageScale) +{ + local KFPawn KFP; + + if( bWasFadedOut|| bDeleteMe || bPendingDelete ) + { + return; + } + + KFP = KFPawn(Victim); + + if (KFP == none) + { + return; + } + + if (KFP.GetTeamNum() == Instigator.GetTeamNum()) + { + KFP.HealDamage(HealingValue, Instigator.Controller, class'KFDT_Healing'); + } + else + { + super.AffectsPawn(VIctim, DamageScale); + + KFP.ApplyDamageOverTime(class'KFDT_Toxic_HRG_MedicMissile'.default.PoisonPower, Instigator.Controller, class'KFDT_Toxic_HRG_MedicMissile'); + } +} + +DefaultProperties +{ + Interval=0f + MaxTime=0f + + bOnlyDamagePawns=true + bDoFullDamage=false + + bExplodeMoreThanOnce=false + + HealingValue=50 +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFGameInfo_Survival.uc b/KFGameContent/Classes/KFGameInfo_Survival.uc index 20bff2d..bba82f8 100644 --- a/KFGameContent/Classes/KFGameInfo_Survival.uc +++ b/KFGameContent/Classes/KFGameInfo_Survival.uc @@ -386,6 +386,8 @@ function UpdateGameSettings() local int NumHumanPlayers, i; local KFGameEngine KFEngine; + super.UpdateGameSettings(); + if (WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.NetMode == NM_ListenServer) { //`REMOVEMESOON_ServerTakeoverLog("KFGameInfo_Survival.UpdateGameSettings 1 - GameInterface: "$GameInterface); diff --git a/KFGameContent/Classes/KFGameInfo_VersusSurvival.uc b/KFGameContent/Classes/KFGameInfo_VersusSurvival.uc index da0dac6..3322e63 100644 --- a/KFGameContent/Classes/KFGameInfo_VersusSurvival.uc +++ b/KFGameContent/Classes/KFGameInfo_VersusSurvival.uc @@ -550,14 +550,16 @@ function byte PickTeam(byte Current, Controller C, const out UniqueNetId PlayerI /** Return whether a team change is allowed. */ function bool ChangeTeam(Controller Other, int N, bool bNewTeam) { - if( Other.PlayerReplicationInfo == none - || Other.PlayerReplicationInfo.bBot - || (!Other.PlayerReplicationInfo.bOnlySpectator - && ArrayCount(Teams) > N - && Other.PlayerReplicationInfo.Team != Teams[N]) ) + if (ArrayCount(Teams) > N) { - SetTeam( Other, Teams[N] ); - return true; + if( Other.PlayerReplicationInfo == none + || Other.PlayerReplicationInfo.bBot + || ( !Other.PlayerReplicationInfo.bOnlySpectator + && Other.PlayerReplicationInfo.Team != Teams[N] ) ) + { + SetTeam( Other, Teams[N] ); + return true; + } } return false; diff --git a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc index aaccebd..b819983 100644 --- a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc +++ b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc @@ -445,6 +445,7 @@ event PostLogin( PlayerController NewPlayer ) { local KFPlayerController_WeeklySurvival KFPC_WS; local KFPawn_Customization KFCustomizePawn; + super.PostLogin(NewPlayer); KFPC_WS = KFPlayerController_WeeklySurvival(NewPlayer); @@ -544,6 +545,12 @@ function WaveEnded(EWaveEndCondition WinCondition) // This function is called multiple times in a row. Only apply it once. bWasFirstTime = bWaveStarted; + // Choose new perk before the end of wave message triggers in supper. + if (MyKFGRI.IsRandomPerkMode() && WinCondition == WEC_WaveWon) + { + ChooseRandomPerks(); + } + super.WaveEnded(WinCondition); if (OutbreakEvent.ActiveEvent.bPermanentZedTime && ZedTimeRemaining > ZedTimeBlendOutTime) @@ -1315,6 +1322,10 @@ function WaveStarted() } } +/** + * Weekly 17: VIP MODE + */ + function OnVIPDiesEndMatch() { local KFPlayerController KFPC; @@ -1500,6 +1511,182 @@ function OnOutbreakWaveWon() } } +/* + * Weekly 18: Random Perks + */ + +simulated function NotifyPlayerStatsInitialized(KFPlayerController_WeeklySurvival KFPC) +{ + if (KFPC != none && MyKFGRI.IsRandomPerkMode()) + { + ChooseInitialRandomPerk(KFPC); + } +} + +function ChooseInitialRandomPerk(KFPlayerController_WeeklySurvival KFPC_WS) +{ + local KFPlayerController_WeeklySurvival OtherKFPC; + local array > AvailablePerks; + local int i; + local byte NewPerkIndex; + local bool bPerkFound; + + `Log("CHOOSING INITIAL PERKS"); + + for (i = 0; i < KFPC_WS.PerkList.Length; ++i) + { + bPerkFound = false; + + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', OtherKFPC) + { + if (OtherKFPC == KFPC_WS) + { + continue; + } + + if (KFPC_WS.Perklist[i].PerkClass == OtherKFPC.CurrentPerk.Class) + { + bPerkFound = true; + break; + } + } + + if (!bPerkFound) + { + AvailablePerks.AddItem(KFPC_WS.PerkList[i].PerkClass); + } + } + + if (AvailablePerks.Length == 0) + { + for (i = 0; i < KFPC_WS.Perklist.Length; ++i) + { + AvailablePerks.AddItem(KFPC_WS.Perklist[i].PerkClass); + } + KFPC_WS.LockedPerks.Length = 0; + } + + NewPerkIndex = Rand(AvailablePerks.Length); + + KFPC_WS.LockedPerks.AddItem(AvailablePerks[NewPerkIndex]); + KFPC_WS.ForceNewPerk(AvailablePerks[NewPerkIndex]); +} + +function ChooseRandomPerks() +{ + local KFPlayerController_WeeklySurvival KFPC; + local array > AvailablePerks; + local array > PickedPerks; + local int i, j; + local byte NewPerkIndex; + local bool bPerkFound; + + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC) + { + AvailablePerks.Length = 0; + + for (i = 0; i < KFPC.Perklist.Length; ++i) + { + bPerkFound = false; + for (j = 0; j < PickedPerks.Length; ++j) + { + if (KFPC.Perklist[i].PerkClass == PickedPerks[j]) + { + bPerkFound = true; + break; + } + } + + if (!bPerkFound) + { + for (j = 0; j < KFPC.LockedPerks.Length; ++j) + { + if (KFPC.Perklist[i].PerkClass == KFPC.LockedPerks[j]) + { + bPerkFound = true; + break; + } + } + } + + if (!bPerkFound) + { + AvailablePerks.AddItem(KFPC.Perklist[i].PerkClass); + } + } + + if (AvailablePerks.Length == 0) + { + for (i = 0; i < KFPC.Perklist.Length; ++i) + { + bPerkFound = false; + for (j = 0; j < PickedPerks.Length; ++j) + { + if (KFPC.Perklist[i].PerkClass == PickedPerks[j]) + { + bPerkFound = true; + break; + } + } + + if (!bPerkFound && KFPC.Perklist[i].PerkClass != KFPC.CurrentPerk.Class) + { + AvailablePerks.AddItem(KFPC.Perklist[i].PerkClass); + } + } + + if (AvailablePerks.Length == 0) + { + for (i = 0; i < KFPC.Perklist.Length; ++i) + { + AvailablePerks.AddItem(KFPC.Perklist[i].PerkClass); + PickedPerks.Length = 0; + } + } + + KFPC.LockedPerks.Length = 0; + } + + NewPerkIndex = Rand(AvailablePerks.Length); + PickedPerks.AddItem(AvailablePerks[NewPerkIndex]); + KFPC.LockedPerks.AddItem(AvailablePerks[NewPerkIndex]); + + KFPC.ForceNewPerk(AvailablePerks[NewPerkIndex]); + + KFPC.PlayRandomPerkChosenSound(); + } +} + +// +// Overide BroadcastLocalizedMessage for the RandomPerk weekly (18) +// Perk is replicated while the message is sent through a RPC, so the message arrives before +// the perk is updated on clients. +// Override the BroadcastLocalizedMessage function to RPC each player the message with their own +// new perk class as the optional object so the client knows the information before it gets the perk updated. +// +event BroadcastLocalizedMessage( class InMessageClass, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject ) +{ + if (!MyKFGRI.IsRandomPerkMode() || Switch != GMT_WaveEnd) + { + Super.BroadcastLocalizedMessage(InMessageClass, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject); + } + else + { + BroadcastCustomWaveEndMessage(self, InMessageClass, Switch); + } +} + +function BroadcastCustomWaveEndMessage( actor Sender, class InMessageClass, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject ) +{ + local KFPlayerController KFPC; + + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + KFPC.ReceiveLocalizedMessage( InMessageClass, Switch, RelatedPRI_1, RelatedPRI_2, KFPC.GetPerk().Class); + } +} +// + defaultproperties { //Overrides diff --git a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc index 71122c5..7b31756 100644 --- a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc +++ b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc @@ -1220,6 +1220,21 @@ defaultproperties , class'KFGameContent.KFPawn_ZedHans'), )} + // Random Perks + SetEvents[18]={( + EventDifficulty=2, + GameLength=GL_Normal, + DoshOnKillGlobalModifier=1.3, + SpawnReplacementList={( + (SpawnEntry=AT_Clot,NewClass=(class'KFGameContent.KFPawn_ZedCrawler'),PercentChance=0.3), + (SpawnEntry=AT_AlphaClot,NewClass=(class'KFGameContent.KFPawn_ZedCrawler'),PercentChance=0.3), + (SpawnEntry=AT_SlasherClot,NewClass=(class'KFGameContent.KFPawn_ZedStalker'),PercentChance=0.3), + (SpawnEntry=AT_Bloat,NewClass=(class'KFGameContent.KFPawn_ZedSiren'),PercentChance=0.3), + (SpawnEntry=AT_Scrake,NewClass=(class'KFGameContent.KFPawn_ZedFleshpound'),PercentChance=0.3) + + )} + )} + //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/KFProj_Bullet_HVStormCannon.uc b/KFGameContent/Classes/KFProj_Bullet_HVStormCannon.uc new file mode 100644 index 0000000..98e2c7f --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_HVStormCannon.uc @@ -0,0 +1,22 @@ +//============================================================================= +// KFProj_Bullet_HVStormCannon +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_HVStormCannon extends KFProj_Bullet + hidedropdown; + +defaultproperties +{ + MaxSpeed=22500.0 + Speed=22500.0 + + DamageRadius=0 + + ProjFlightTemplate = ParticleSystem'WEP_HVStormCannon_EMIT.FX_HVStormCannon_Projectile' + ImpactEffects = KFImpactEffectInfo'WEP_HVStormCannon_ARCH.Wep_HVStormCannon_Impact' +} + diff --git a/KFGameContent/Classes/KFProj_Bullet_ZedMKIII.uc b/KFGameContent/Classes/KFProj_Bullet_ZedMKIII.uc new file mode 100644 index 0000000..f99426a --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_ZedMKIII.uc @@ -0,0 +1,22 @@ +//============================================================================= +// KFProj_Bullet_ZedMKIII +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_ZedMKIII extends KFProj_Bullet + hidedropdown; + +defaultproperties +{ + MaxSpeed=22500.0 + Speed=22500.0 + + DamageRadius=0 + + ProjFlightTemplate = ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Bullet_Projectile' + ImpactEffects = KFImpactEffectInfo'WEP_ZEDMKIII_ARCH.FX_ZEDMKIII_Bullet_Impact' +} + diff --git a/KFGameContent/Classes/KFProj_DynamiteGrenade.uc b/KFGameContent/Classes/KFProj_DynamiteGrenade.uc index 80edf33..bc5d6fd 100644 --- a/KFGameContent/Classes/KFProj_DynamiteGrenade.uc +++ b/KFGameContent/Classes/KFProj_DynamiteGrenade.uc @@ -69,7 +69,9 @@ simulated protected function PrepareExplosionTemplate() { class'KFPerk_Demolitionist'.static.PrepareExplosive( Instigator, self ); - super.PrepareExplosionTemplate(); + GetRadialDamageValues(ExplosionTemplate.Damage, ExplosionTemplate.DamageRadius, ExplosionTemplate.DamageFalloffExponent); + + // super.PrepareExplosionTemplate(); } simulated protected function SetExplosionActorClass() diff --git a/KFGameContent/Classes/KFProj_Grenade_HRGTeslauncher.uc b/KFGameContent/Classes/KFProj_Grenade_HRGTeslauncher.uc index c679a89..dc0c45d 100644 --- a/KFGameContent/Classes/KFProj_Grenade_HRGTeslauncher.uc +++ b/KFGameContent/Classes/KFProj_Grenade_HRGTeslauncher.uc @@ -133,7 +133,9 @@ simulated protected function PrepareExplosionTemplate() { class'KFPerk_Berserker'.static.PrepareExplosive( Instigator, self ); - super.PrepareExplosionTemplate(); + GetRadialDamageValues(ExplosionTemplate.Damage, ExplosionTemplate.DamageRadius, ExplosionTemplate.DamageFalloffExponent); + + //super.PrepareExplosionTemplate(); } diff --git a/KFGameContent/Classes/KFProj_HRG_BallisticBouncer.uc b/KFGameContent/Classes/KFProj_HRG_BallisticBouncer.uc new file mode 100644 index 0000000..251de2b --- /dev/null +++ b/KFGameContent/Classes/KFProj_HRG_BallisticBouncer.uc @@ -0,0 +1,556 @@ +//============================================================================= +// KFProj_HRG_BallisticBouncer +//============================================================================= +// Projectile class for HRG Ballistic Bouncer +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFProj_HRG_BallisticBouncer extends KFProjectile; + +/** Dampen amount for every bounce */ +var float DampenFactor; + +/** Dampen amount for parallel angle to velocity */ +var float DampenFactorParallel; + +/** Sound mine makes when it hits something and bounces off */ +var AkEvent BounceAkEvent; + +/** Sound mine makes when it hits something and comes to rest */ +var AkEvent ImpactAkEvent; + +/** Sound mine makes when it hits something and bounces off */ +var AkEvent BounceAkEventHeavy; + +/** Sound mine makes when it hits something and comes to rest */ +var AkEvent ImpactAkEventHeavy; + +/** Particle System spawned when it hits something */ +var ParticleSystem HitFXTemplate; +var transient ParticleSystemComponent HitPSC; + +/** Particle System for the fade out burst **/ +var ParticleSystem BurstFXTemplate; +var transient ParticleSystemComponent BurstPSC; + +/** Sound for the fade out burst **/ +var AkEvent BurstAkEvent; + +/** Decal settings */ +var MaterialInterface ImpactDecalMaterial; +var float ImpactDecalWidth; +var float ImpactDecalHeight; +var float ImpactDecalThickness; + +var int MaxBounces; +var int NumBounces; + +var float MaxInitialSpeedByCharge; +var float MinInitialSpeedByCharge; + +var float MaxCollisionRadius; +var float MinCollisionRadius; +var float MaxCollisionHeight; +var float MinCollisionHeight; + +var float MaxScalePerPercentage; +var float MinScalePerPercentage; + +var int MaxBouncesPerPercentage; +var int MinBouncesPerPercentage; + +var float MaxLifeSpanPerPercentage; +var float MinLifeSpanPerPercentage; + +var float InheritedScale; + +var repnotify float fChargePercentage; + +var float fCachedCylinderWidth, fCachedCylinderHeight; + +var float ArmDistSquared; + +var bool bFadingOut; +var float FadeOutTime; + +var transient byte RequiredImpactsForSeasonal; +var transient array ImpactVictims; + +replication +{ + if( bNetDirty ) + InheritedScale, fChargePercentage, MaxBounces; +} + +simulated event ReplicatedEvent( name VarName ) +{ + if( VarName == nameOf(fChargePercentage)) + { + fCachedCylinderWidth = Lerp(MinCollisionRadius, MaxCollisionRadius, fChargePercentage); + fCachedCylinderHeight = Lerp(MinCollisionHeight, MaxCollisionHeight, fChargePercentage); + // CylinderComponent(CollisionComponent).SetCylinderSize(fCachedCylinderWidth, fCachedCylinderHeight); + SetCollisionSize(fCachedCylinderWidth, fCachedCylinderHeight); + ApplyVFXParams(fChargePercentage); + } + else + { + super.ReplicatedEvent( VarName ); + } +} + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + RequiredImpactsForSeasonal = class'KFSeasonalEventStats_Xmas2022'.static.GetMaxBallisticBouncerImpacts(); +} + +simulated function SetInheritedScale(float Scale, float ChargePercentage) +{ + local float NewSpeed; + + InheritedScale = Scale; + + fChargePercentage = ChargePercentage; + fChargePercentage = FMax(0.1, fChargePercentage); + + if (WorldInfo.NetMode == NM_DedicatedServer) + { + SetCollisionSize(Lerp(MinCollisionRadius, MaxCollisionRadius, fChargePercentage), Lerp(MinCollisionHeight, MaxCollisionHeight, ChargePercentage)); + } + + NewSpeed = Lerp(MinInitialSpeedByCharge, MaxInitialSpeedByCharge, fChargePercentage); + Velocity = Normal(Velocity) * NewSpeed; + Speed = NewSpeed; + + MaxBounces = Lerp(MinBouncesPerPercentage, MaxBouncesPerPercentage, fChargePercentage); + + LifeSpan = Lerp(MinLifeSpanPerPercentage, MaxLifeSpanPerPercentage, fChargePercentage); + + if (ProjEffects != none) + { + ProjEffects.SetScale(Lerp(MinScalePerPercentage, MaxScalePerPercentage, fChargePercentage)); + } + + if (WorldInfo.NetMode != NM_DedicatedServer) + { + ApplyVFXParams(fChargePercentage); + } + + bNetDirty=true; +} + +function RestoreCylinder() +{ + CylinderComponent(CollisionComponent).SetCylinderSize(fCachedCylinderWidth, fCachedCylinderHeight); +} + +simulated function BounceNoCheckRepeatingTouch(vector HitNormal, Actor BouncedOff) +{ + local vector VNorm; + + // Reflect off BouncedOff w/damping + VNorm = (Velocity dot HitNormal) * HitNormal; + + if (NumBounces < MaxBounces) + { + Velocity = -VNorm + (Velocity - VNorm); + } + else + { + Velocity = -VNorm * DampenFactor + (Velocity - VNorm) * DampenFactorParallel; + } + + Speed = VSize(Velocity); + + // Play a sound + PlayImpactSound(); + + // Spawn impact particle system, server needs to send the message (it's the only one storing MaxBounces) + if (WorldInfo.NetMode != NM_DedicatedServer) + { + if (NumBounces < MaxBounces) + { + PlayImpactVFX(HitNormal); + } + + `ImpactEffectManager.PlayImpactEffects(Location, Instigator, VNorm, ImpactEffects); + } + + ++NumBounces; +} + +/** Adjusts movement/physics of projectile. + * Returns true if projectile actually bounced / was allowed to bounce */ +simulated function bool Bounce( vector HitNormal, Actor BouncedOff ) +{ + // Avoid crazy bouncing + if (CheckRepeatingTouch(BouncedOff)) + { + CylinderComponent(CollisionComponent).SetCylinderSize(1,1); + SetTimer(0.06f, false, nameof(RestoreCylinder)); + return false; + } + + BounceNoCheckRepeatingTouch(HitNormal, BouncedOff); + + return true; +} + +/** Plays a sound on impact */ +simulated function PlayImpactSound( optional bool bIsAtRest ) +{ + if( WorldInfo.NetMode != NM_DedicatedServer ) + { + if( bIsAtRest ) + { + if(fChargePercentage < 0.75f) + PostAkEvent( ImpactAkEvent, true, true, false ); + else + PostAkEvent( ImpactAkEventHeavy, true, true, false ); + } + else + { + if(fChargePercentage < 0.75f) + PostAkEvent( BounceAkEvent, true, true, false ); + else + PostAkEvent( BounceAkEventHeavy, true, true, false ); + } + } +} + +simulated function PlayImpactVFX(vector HitNormal) +{ + HitPSC = WorldInfo.MyEmitterPool.SpawnEmitter(HitFXTemplate, ProjEffects.GetPosition(), rotator(HitNormal)); + HitPSC.SetFloatParameter(name("Charge"), fChargePercentage); +} + +simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal) +{ + local float TraveledDistance; + + TraveledDistance = (`TimeSince(CreationTime) * Speed); + TraveledDistance *= TraveledDistance; + + // If we collided with a Siren shield, let the shield code handle touches + if( Other.IsA('KFTrigger_SirenProjectileShield') ) + { + return; + } + + if (Other != Instigator && Other.GetTeamNum() != GetTeamNum()) + { + // check/ignore repeat touch events + if (!CheckRepeatingTouch(Other)) + { + ProcessBulletTouch(Other, HitLocation, HitNormal); + } + } +} + +simulated function ProcessBulletTouch(Actor Other, Vector HitLocation, Vector HitNormal) +{ + local Pawn Victim; + local array HitZoneImpactList; + //local ImpactInfo ImpactInfoFallBack; + local vector StartTrace, EndTrace, Direction; //, DirectionFallBack; + local TraceHitInfo HitInfo; + local KFWeapon KFW; + + Victim = Pawn( Other ); + if ( Victim == none ) + { + if ( bDamageDestructiblesOnTouch && Other.bCanBeDamaged ) + { + HitInfo.HitComponent = LastTouchComponent; + HitInfo.Item = INDEX_None; // force TraceComponent on fractured meshes + Other.TakeDamage(Damage, InstigatorController, Location, MomentumTransfer * Normal(Velocity), MyDamageType, HitInfo, self); + } + + // Reduce the penetration power to zero if we hit something other than a pawn or foliage actor + if( InteractiveFoliageActor(Other) == None ) + { + PenetrationPower = 0; + + BounceNoCheckRepeatingTouch(HitNormal, Other); + + return; + } + } + else + { + if (bSpawnShrapnel) + { + //spawn straight forward through the zed + SpawnShrapnel(Other, HitLocation, HitNormal, rotator(Velocity), ShrapnelSpreadWidthZed, ShrapnelSpreadHeightZed); + } + + StartTrace = HitLocation; + Direction = Normal(Velocity); + EndTrace = StartTrace + Direction * (Victim.CylinderComponent.CollisionRadius * 6.0); + + TraceProjHitZones(Victim, EndTrace, StartTrace, HitZoneImpactList); + + //`Log("HitZoneImpactList: " $HitZoneImpactList.Length); + + if ( HitZoneImpactList.length > 0 ) + { + HitZoneImpactList[0].RayDir = Direction; + + if( Owner != none ) + { + KFW = KFWeapon( Owner ); + if( KFW != none ) + { + KFW.HandleProjectileImpact(WeaponFireMode, HitZoneImpactList[0], PenetrationPower); + } + } + + IncrementNumImpacts(Victim); + + BounceNoCheckRepeatingTouch(HitNormal, Other); + } + } +} + +simulated function IncrementNumImpacts(Pawn Victim) +{ + local int i; + local KFPlayerController KFPC; + + if (WorldInfo.NetMode == NM_Client) + { + return; + } + + KFPC = KFPlayerController(InstigatorController); + + if (KFPC == none) + { + return; + } + + for (i = 0; i < ImpactVictims.Length; ++i) + { + if (Victim == ImpactVictims[i]) + { + return; + } + } + + ImpactVictims.AddItem(Victim); + + UpdateImpactsSeasonalObjective(KFPC); +} + +function UpdateImpactsSeasonalObjective(KFPlayerController KFPC) +{ + local byte ObjectiveIndex; + + ObjectiveIndex = 3; + + if (ImpactVictims.Length >= RequiredImpactsForSeasonal) + { + // Check parent controller. + KFPC.ClientOnTryCompleteObjective(ObjectiveIndex, SEI_Winter); + } +} + +simulated event HitWall( vector HitNormal, Actor Wall, PrimitiveComponent WallComp ) +{ + // Don't collide with other projectiles + if( Wall.class == class ) + { + return; + } + + Bounce( HitNormal, Wall ); +} + +simulated function SpawnBurstFX() +{ + local vector vec; + + if( WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.MyEmitterPool == none || ProjEffects == none ) + { + return; + } + + BurstPSC = WorldInfo.MyEmitterPool.SpawnEmitter(BurstFXTemplate, ProjEffects.GetPosition(), rotator(vect(0,0,1)), self, , ,true); + + vec.X = fChargePercentage; + vec.Y = fChargePercentage; + vec.Z = fChargePercentage; + + BurstPSC.SetVectorParameter(name("BlobCharge"), vec); + BurstPSC.SetFloatParameter(name("MineFxControlParam"), fChargePercentage); + + PostAkEvent(BurstAkEvent, true, true, false); +} + +simulated function Tick(float Delta) +{ + if (NumBounces < MaxBounces || bFadingOut) + return; + + if (Speed < 20.0f) + { + bFadingOut = true; + StopSimulating(); + + SpawnBurstFX(); + + // Tell clients to tear off and fade out on their own + if( WorldInfo.NetMode != NM_Client ) + { + ClearTimer(nameof(Timer_Destroy)); + + SetTimer( 1.0f, false, nameOf(Timer_Destroy) ); + } + } +} + +simulated function Timer_Destroy() +{ + if (BurstPSC != none) + { + BurstPSC.DeactivateSystem(); + } + + Destroy(); +} + +simulated function ApplyVFXParams(float ChargePercent) +{ + if (ProjEffects != none) + { + ProjEffects.SetFloatParameter(name("InflateBlob"), ChargePercent); + } +} + +simulated function SyncOriginalLocation() +{ + local Actor HitActor; + local vector HitLocation, HitNormal; + local TraceHitInfo HitInfo; + + if (Role < ROLE_Authority && Instigator != none && Instigator.IsLocallyControlled()) + { + HitActor = Trace(HitLocation, HitNormal, OriginalLocation, Location,,, HitInfo, TRACEFLAG_Bullet); + if (HitActor != none) + { + ProcessBulletTouch(HitActor, HitLocation, HitNormal); + } + } + + Super.SyncOriginalLocation(); +} + + +defaultproperties +{ + TerminalVelocity=5000 + TossZ=150 + GravityScale=0.5 + MomentumTransfer=50000.0 + + LifeSpan=300 + PostExplosionLifetime=1 + Physics=PHYS_Falling + bBounce=true + + ProjFlightTemplate= ParticleSystem'WEP_HRG_BallisticBouncer_EMIT.FX_HRG_BallisticBouncer_Ball_Projectile' + BurstFXTemplate= ParticleSystem'WEP_HRG_BallisticBouncer_EMIT.FX_HRG_BallisticBouncer_Ball_Explode' + HitFXTemplate= ParticleSystem'WEP_HRG_BallisticBouncer_EMIT.FX_HRG_BallisticBouncer_Ball_Hit' + + bSuppressSounds=false + bAmbientSoundZedTimeOnly=false + bAutoStartAmbientSound=false + bStopAmbientSoundOnExplode=true + + ImpactAkEvent=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Impact' + BounceAkEvent=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Impact' + ImpactAkEventHeavy=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Impact_Heavy' + BounceAkEventHeavy=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Impact_Heavy' + + BurstAkEvent=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Explosion' + + Begin Object Class=AkComponent name=AmbientAkSoundComponent + bStopWhenOwnerDestroyed=true + bForceOcclusionUpdateInterval=true + OcclusionUpdateInterval=0.25f + End Object + AmbientComponent=AmbientAkSoundComponent + Components.Add(AmbientAkSoundComponent) + + //ImpactDecalMaterial=DecalMaterial'FX_Mat_Lib.FX_Puke_Mine_Splatter_DM' + ImpactDecalWidth=178.f + ImpactDecalHeight=178.f + ImpactDecalThickness=28.f + + Begin Object Name=CollisionCylinder + CollisionRadius=0.f + CollisionHeight=0.f + CollideActors=true + BlockNonZeroExtent=false + PhysMaterialOverride=PhysicalMaterial'WEP_HRG_BallisticBouncer_EMIT.BloatPukeMine_PM' + End Object + + bCollideComplex=TRUE // Ignore simple collision on StaticMeshes, and collide per poly + bUseClientSideHitDetection=true + bNoReplicationToInstigator=false + bUpdateSimulatedPosition=true + + bProjTarget=true + bCanBeDamaged=false + bNoEncroachCheck=true + bPushedByEncroachers=false + DampenFactor=0.175f + DampenFactorParallel=0.175f + + ExtraLineCollisionOffsets.Add((Y=-20)) + ExtraLineCollisionOffsets.Add((Y=20)) + ExtraLineCollisionOffsets.Add((Z=-20)) + ExtraLineCollisionOffsets.Add((Z=20)) + // Since we're still using an extent cylinder, we need a line at 0 + ExtraLineCollisionOffsets.Add(()) + GlassShatterType=FMGS_ShatterAll + InheritedScale=1 + + MaxInitialSpeedByCharge=5000 + MinInitialSpeedByCharge=1500 + + MaxCollisionRadius=20 + MinCollisionRadius=10 + MaxCollisionHeight=20 + MinCollisionHeight=10 + + MaxScalePerPercentage=1.5f + MinScalePerPercentage=0.5f + + MaxBouncesPerPercentage=5 + MinBouncesPerPercentage=1 + + MaxLifespanPerPercentage=500 + MinLifeSpanPerPercentage=300 + + bBlockedByInstigator=true + bNetTemporary=false + + bSyncToOriginalLocation=true + bSyncToThirdPersonMuzzleLocation=true + + bReplicateLocationOnExplosion=true + + TouchTimeThreshhold=0.05 + + MaxBounces=0 + NumBounces=0 + + ArmDistSquared=0 + + // Fade out properties + FadeOutTime=5.0f + + // ImpactEffects= KFImpactEffectInfo'WEP_DragonsBreath_ARCH.DragonsBreath_bullet_impact' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFProj_Rocket_HRG_MedicMissile.uc b/KFGameContent/Classes/KFProj_Rocket_HRG_MedicMissile.uc new file mode 100644 index 0000000..836a7d1 --- /dev/null +++ b/KFGameContent/Classes/KFProj_Rocket_HRG_MedicMissile.uc @@ -0,0 +1,128 @@ +//============================================================================= +// KFProj_Rocket_HRG_MedicMissile +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Rocket_HRG_MedicMissile extends KFProj_BallisticExplosive + hidedropdown; + +simulated function bool AllowNuke() +{ + return false; +} + +simulated function bool AllowDemolitionistConcussive() +{ + return false; +} + +simulated function bool AllowDemolitionistExplosionChangeRadius() +{ + return false; +} + +simulated protected function PrepareExplosionTemplate() +{ + local Weapon OwnerWeapon; + local Pawn OwnerPawn; + local KFPerk_Survivalist Perk; + + super(KFProjectile).PrepareExplosionTemplate(); + + OwnerWeapon = Weapon(Owner); + if (OwnerWeapon != none) + { + OwnerPawn = Pawn(OwnerWeapon.Owner); + if (OwnerPawn != none) + { + Perk = KFPerk_Survivalist(KFPawn(OwnerPawn).GetPerk()); + if (Perk != none) + { + ExplosionTemplate.DamageRadius *= KFPawn(OwnerPawn).GetPerk().GetAoERadiusModifier(); + } + } + } +} + +simulated function AdjustCanDisintigrate() +{ + // This weapon is not from Demolitionist, so always enable Siren disintegrate + bCanDisintegrate = true; +} + +defaultproperties +{ + Physics=PHYS_Projectile + Speed=5000 + MaxSpeed=5000 + TossZ=0 + GravityScale=1.0 + MomentumTransfer=50000.0 + ArmDistSquared=150000 // 4 meters + + bCollideWithTeammates=true + + bWarnAIWhenFired=true + + ProjFlightTemplate=ParticleSystem'WEP_HRG_MedicMissile_EMIT.FX_HRG_MedicMissile_Projectile' + ProjFlightTemplateZedTime=ParticleSystem'WEP_HRG_MedicMissile_EMIT.FX_HRG_MedicMissile_Projectile_ZED_TIME' + ProjDudTemplate=ParticleSystem'WEP_HRG_MedicMissile_EMIT.FX_HRG_MedicMissile_Projectile_Dud' + GrenadeBounceEffectInfo=KFImpactEffectInfo'WEP_HRG_MedicMissile_ARCH.HRG_MedicMissile_Projectile_Impacts' + + ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01' + bCanDisintegrate=true + + 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' + + ExplosionActorClass=class'KFExplosion_HRG_MedicMissile' + + // Grenade explosion light + Begin Object Class=PointLightComponent Name=ExplosionPointLight + LightColor=(R=60,G=200,B=255,A=255) + Brightness=2.f + Radius=1500.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=400 + DamageRadius=300 + DamageFalloffExponent=2 + DamageDelay=0.f + + // Damage Effects + MyDamageType=class'KFDT_Explosive_HRG_MedicMissile' + KnockDownStrength=0 + FractureMeshRadius=0.0 + FracturePartVel=0.0 + ExplosionEffects=KFImpactEffectInfo'WEP_HRG_MedicMissile_ARCH.HRG_MedicMissile_Explosion' + ExplosionSound=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_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 + End Object + ExplosionTemplate=ExploTemplate0 + + bCanApplyDemolitionistPerks=false +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFProj_Rocket_ZedMKIII.uc b/KFGameContent/Classes/KFProj_Rocket_ZedMKIII.uc new file mode 100644 index 0000000..49dc444 --- /dev/null +++ b/KFGameContent/Classes/KFProj_Rocket_ZedMKIII.uc @@ -0,0 +1,123 @@ +//============================================================================= +// KFProj_Rocket_ZedMKIII +//============================================================================= +// KFProj_Rocket_ZedMKIII rocket +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFProj_Rocket_ZedMKIII extends KFProj_BallisticExplosive + hidedropdown; + +/** Our intended target actor */ +var private KFPawn LockedTarget; + +/** How much 'stickyness' when seeking toward our target. Determines how accurate rocket is */ +var const float SeekStrength; + +replication +{ + if( bNetInitial ) + LockedTarget; +} + +function SetLockedTarget( KFPawn NewTarget ) +{ + LockedTarget = NewTarget; +} + +simulated event Tick( float DeltaTime ) +{ + local vector TargetImpactPos, DirToTarget; + + super.Tick( DeltaTime ); + + // Skip the first frame, then start seeking + if( !bHasExploded + && LockedTarget != none + && Physics == PHYS_Projectile + && Velocity != vect(0,0,0) + && LockedTarget.IsAliveAndWell() + && `TimeSince(CreationTime) > 0.03f ) + { + // Grab our desired relative impact location from the weapon class + TargetImpactPos = class'KFWeap_ZedMKIII'.static.GetLockedTargetLoc( LockedTarget ); + + // Seek towards target + Speed = VSize( Velocity ); + DirToTarget = Normal( TargetImpactPos - Location ); + Velocity = Normal( Velocity + (DirToTarget * (SeekStrength * DeltaTime)) ) * Speed; + + // Aim rotation towards velocity every frame + SetRotation( rotator(Velocity) ); + } +} + +defaultproperties +{ + Physics=PHYS_Projectile + Speed=4000 //6000 + MaxSpeed=4000 //6000 + TossZ=0 + GravityScale=1.0 + MomentumTransfer=50000.0f + ArmDistSquared=110000.0f //20000.0f // 110000.0f // 4 meters 150000.0 + + SeekStrength=928000.0f // 128000.0f + + bWarnAIWhenFired=true + + ProjFlightTemplate=ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Rocket' + ProjFlightTemplateZedTime=ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Rocket_ZED_TIME' + ProjDudTemplate=ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Rocket_Dud' + GrenadeBounceEffectInfo=KFImpactEffectInfo'WEP_RPG7_ARCH.RPG7_Projectile_Impacts' + ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01' + + AmbientSoundPlayEvent=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Rocket_LP' + AmbientSoundStopEvent=AkEvent'WW_WEP_ZEDMKIII.Stop_WEP_ZEDMKIII_Rocket_LP' + + AltExploEffects=KFImpactEffectInfo'WEP_ZEDMKIII_ARCH.FX_ZEDMKIII_Explosion_Concussive_force' + + // Grenade explosion light + Begin Object Class=PointLightComponent Name=ExplosionPointLight + LightColor=(R=252,G=218,B=171,A=255) + Brightness=4.f + Radius=2000.f + FalloffExponent=10.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=False + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE) + End Object + + // explosion + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=200 + DamageRadius=300 + DamageFalloffExponent=2 + DamageDelay=0.f + + // Damage Effects + MyDamageType=class'KFDT_Explosive_ZedMKIII' + KnockDownStrength=0 + FractureMeshRadius=200.0 + FracturePartVel=500.0 + ExplosionEffects=KFImpactEffectInfo'WEP_ZEDMKIII_ARCH.FX_ZEDMKIII_Explosion' + ExplosionSound=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Explosion' + + // Dynamic Light + ExploLight=ExplosionPointLight + ExploLightStartFadeOutTime=0.0 + ExploLightFadeOutTime=0.2 + + // Camera Shake + CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' + CamShakeInnerRadius=0 + CamShakeOuterRadius=500 + CamShakeFalloff=3.f + bOrientCameraShakeTowardsEpicenter=true + End Object + ExplosionTemplate=ExploTemplate0 +} diff --git a/KFGameContent/Classes/KFSeasonalEventStats_Xmas2022.uc b/KFGameContent/Classes/KFSeasonalEventStats_Xmas2022.uc new file mode 100644 index 0000000..b084786 --- /dev/null +++ b/KFGameContent/Classes/KFSeasonalEventStats_Xmas2022.uc @@ -0,0 +1,183 @@ +//============================================================================= +// KFSeasonalEventStats_Xmas2022 +//============================================================================= +// Tracks event-specific challenges/accomplishments for Xmas 2022 +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFSeasonalEventStats_Xmas2022 extends KFSeasonalEventStats; + +/** + Required impacts of the same projectile to count for the objective. I.e. 3 means + that a ballistic bouncer projectile needs to make 3 impacts to count for the objective +*/ +var private const byte HRGBBProjectilImpactsRequired; + +var transient private const int FrozenZedsRequired, ShotgunJumpsRequired, BallisticBouncerImpactsRequired, EndlessWaveRequired, XmasEventIndex; + +var transient int ShotgunJumpsIdx; + +private event Initialize(string MapName) +{ + local string CapsMapName; + CapsMapName = Caps(MapName); + + bObjectiveIsValidForMap[0] = 1; // Freeze 500 Zeds using ice arsenal + bObjectiveIsValidForMap[1] = 0; // Complete the Weekly on Crash + bObjectiveIsValidForMap[2] = 0; // Use 4 Boomstick Jumps in a same match on Crash + bObjectiveIsValidForMap[3] = 1; // Hit 3 Zeds with a shot of HRG Ballistic Bouncer (15 times) + bObjectiveIsValidForMap[4] = 0; // Complete wave 15 on Endless Hard or higher difficulty on Crash + + if (CapsMapName == "KF-CRASH") + { + bObjectiveIsValidForMap[1] = 1; + bObjectiveIsValidForMap[2] = 1; + bObjectiveIsValidForMap[4] = 1; + } + + SetSeasonalEventStatsMax(FrozenZedsRequired, 0, ShotgunJumpsRequired, BallisticBouncerImpactsRequired, 0); +} + +simulated event OnStatsInitialized() +{ + super.OnStatsInitialized(); + + CheckRestartObjective(ShotgunJumpsIdx, ShotgunJumpsRequired); +} + +private event GrantEventItems() +{ + if (Outer.IsEventObjectiveComplete(0) && + Outer.IsEventObjectiveComplete(1) && + Outer.IsEventObjectiveComplete(2) && + Outer.IsEventObjectiveComplete(3) && + Outer.IsEventObjectiveComplete(4)) + { + GrantEventItem(9568); + } +} + +simulated event OnGameWon(class GameClass, int Difficulty, int GameLength, bool bCoOp) +{ + local int ObjIdx; + ObjIdx = 1; + + // Crash weekly + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (GameClass == class'KFGameInfo_WeeklySurvival') + { + FinishedObjective(XmasEventIndex, ObjIdx); + } + } + + CheckRestartObjective(ShotgunJumpsIdx, ShotgunJumpsRequired); +} + +simulated event OnGameEnd(class GameClass) +{ + CheckRestartObjective(ShotgunJumpsIdx, ShotgunJumpsRequired); +} + +simulated function CheckRestartObjective(int ObjectiveIndex, int ObjectiveLimit) +{ + local int StatValue; + + StatValue = Outer.GetSeasonalEventStatValue(ObjectiveIndex); + + if (StatValue != 0 && StatValue < ObjectiveLimit) + { + ResetSeasonalEventStat(ObjectiveIndex); + } +} + +simulated function OnTryCompleteObjective(int ObjectiveIndex, int EventIndex) +{ + local int FrozenZedsIdx, BallisticBouncerImpactsIdx, ObjectiveLimit; + local bool bValidIdx; + + FrozenZedsIdx = 0; + BallisticBouncerImpactsIdx = 3; + bValidIdx = false; + + if(EventIndex == XmasEventIndex) + { + if (ObjectiveIndex == FrozenZedsIdx) + { + ObjectiveLimit = FrozenZedsRequired; + bValidIdx = true; + } + else if (ObjectiveIndex == ShotgunJumpsIdx) + { + ObjectiveLimit = ShotgunJumpsRequired; + bValidIdx = true; + } + else if (ObjectiveIndex == BallisticBouncerImpactsIdx) + { + ObjectiveLimit = BallisticBouncerImpactsRequired; + bValidIdx = true; + } + + if (bValidIdx && bObjectiveIsValidForMap[ObjectiveIndex] != 0) + { + IncrementSeasonalEventStat(ObjectiveIndex, 1); + if (Outer.GetSeasonalEventStatValue(ObjectiveIndex) >= ObjectiveLimit) + { + FinishedObjective(XmasEventIndex, ObjectiveIndex); + } + } + } +} + +simulated event OnWaveCompleted(class GameClass, int Difficulty, int WaveNum) +{ + local int ObjIdx; + + // Complete wave 15 on Endless Hard or higher difficulty on Crash + ObjIdx = 4; + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (WaveNum >= EndlessWaveRequired && GameClass == class'KFGameInfo_Endless' && Difficulty >= `DIFFICULTY_HARD) + { + FinishedObjective(XmasEventIndex, ObjIdx); + } + } +} + +simulated function OnAfflictionCaused(EAfflictionType Type) +{ + local int ObjIdx; + + ObjIdx = 0; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (Type == AF_Freeze) + { + IncrementSeasonalEventStat(ObjIdx, 1); + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= FrozenZedsRequired) + { + FinishedObjective(XmasEventIndex, ObjIdx); + } + } + } +} + +static function byte GetMaxBallisticBouncerImpacts() +{ + return default.HRGBBProjectilImpactsRequired; +} + +defaultproperties +{ + ShotgunJumpsIdx=2 + + FrozenZedsRequired=500 + ShotgunJumpsRequired=4 + BallisticBouncerImpactsRequired=30 + EndlessWaveRequired=15 + XmasEventIndex=SEI_Winter + + HRGBBProjectilImpactsRequired=2 +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFShotgunJumpEndVolume.uc b/KFGameContent/Classes/KFShotgunJumpEndVolume.uc new file mode 100644 index 0000000..b6d571a --- /dev/null +++ b/KFGameContent/Classes/KFShotgunJumpEndVolume.uc @@ -0,0 +1,95 @@ + + +//============================================================================= +// KFShotgunJumpEndVolume +//============================================================================= +// Barmwich volume used tracking the start of shotgun jumps. +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFShotgunJumpEndVolume extends PhysicsVolume + placeable; + +/** Objective index for the event this is tied to */ +var() int ObjectiveIndex; + +/** Index of the event this is tied to */ +var() int EventIndex; + +/** All volumes that track the begin of a shotgun jump in the area */ +var() array LinkedVolumes; + +var transient bool bIsDataValid; + +var array SucceededControllers; + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + bIsDataValid = IsObjectiveDataValid(); + + if (LinkedVolumes.Length == 0) + { + `warn("Volume [" $self $"] has no linked volumes (KFShotgunJumpStartVolume)."); + } +} + +simulated event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal ) +{ + local KFPawn_Human KFPH; + local KFPlayerController KFPC; + local KFShotgunJumpStartVolume StartVolume; + + KFPH = KFPawn_Human(Other); + if (KFPH == none) + { + return; + } + + KFPC = KFPlayerController(KFPH.Controller); + + if (KFPC == none || IsCompletedByController(KFPC)) + return; + + foreach LinkedVolumes(StartVolume) + { + if (StartVolume.IsPlayerTracked(KFPC)) + { + StartVolume.RemoveTrackedPlayer(KFPC); + if (KFPC.bShotgunJumping) + { + SucceededControllers.AddItem(KFPC); + KFPC.ClientOnTryCompleteObjective(ObjectiveIndex, EventIndex); + } + return; + } + } +} + +simulated function bool IsCompletedByController(KFPlayerController Controller) +{ + local KFPlayerController Current; + + foreach SucceededControllers(Current) + { + if (Controller == Current) + { + return true; + } + } + + return false; +} + +simulated function bool IsObjectiveDataValid() +{ + return ObjectiveIndex >= 0 && ObjectiveIndex < 5 && EventIndex > SEI_None && EventIndex < SEI_MAX; +} + +DefaultProperties +{ + bIsDataValid = false +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFShotgunJumpStartVolume.uc b/KFGameContent/Classes/KFShotgunJumpStartVolume.uc new file mode 100644 index 0000000..5e25d21 --- /dev/null +++ b/KFGameContent/Classes/KFShotgunJumpStartVolume.uc @@ -0,0 +1,151 @@ + + +//============================================================================= +// KFShotgunJumpStartVolume +//============================================================================= +// Barmwich volume used tracking the start of shotgun jumps. +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFShotgunJumpStartVolume extends PhysicsVolume + placeable; + +/** Max time we keep track of players */ +var() float TrackedPlayerLifetime; + +var Info ShotgunJumpVolumeTimer; + +struct KFTrackedController +{ + var KFPlayerController KFPC; + var float ExitTimestamp; + var bool bInVolume; + + structdefaultproperties + { + KFPC=none + ExitTimestamp=0 + bInVolume=false + } +}; + +var transient array TrackedControllers; + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + if ( Role < ROLE_Authority ) + return; + + ShotgunJumpVolumeTimer = Spawn(class'VolumeTimer', self); +} + +simulated event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal ) +{ + local KFPawn_Human KFPH; + local KFPlayerController KFPC; + local KFTrackedController NewTrackedController; + local int i; + + KFPH = KFPawn_Human(Other); + if (KFPH == none) + { + return; + } + + KFPC = KFPlayerController(KFPH.Controller); + if (KFPC == none) + return; + + for (i = 0; i < TrackedControllers.Length; ++i) + { + if (TrackedControllers[i].KFPC == KFPC) + { + TrackedControllers[i].bInVolume = true; + return; + } + } + + // Update current time + NewTrackedController.KFPC = KFPC; + NewTrackedController.bInVolume = true; + + TrackedControllers.AddItem(NewTrackedController); +} + +simulated event Untouch(Actor Other) +{ + local KFPawn_Human KFPH; + local int i; + local KFPlayerController KFPC; + + KFPH = KFPawn_Human(Other); + if (KFPH == none) + { + return; + } + + KFPC = KFPlayerController(KFPH.Controller); + if (KFPC == none) + return; + + for (i = 0; i < TrackedControllers.Length; ++i) + { + if (TrackedControllers[i].KFPC == KFPC) + { + TrackedControllers[i].ExitTimestamp = WorldInfo.TimeSeconds; + TrackedControllers[i].bInVolume = false; + + return; + } + } +} + +function TimerPop(VolumeTimer T) +{ + local int i; + + for (i = TrackedControllers.Length-1; i >= 0 ; --i) + { + if (!TrackedControllers[i].bInVolume && (`TimeSince(TrackedControllers[i].ExitTimestamp) >= TrackedPlayerLifetime) ) + { + TrackedControllers.Remove(i, 1); + } + } +} + +function RemoveTrackedPlayer(KFPlayerController KFPC) +{ + local int i; + + for (i = TrackedControllers.Length-1; i >= 0 ; --i) + { + if (TrackedControllers[i].KFPC == KFPC) + { + TrackedControllers.Remove(i, 1); + return; + } + } +} + +function bool IsPlayerTracked(KFPlayerController KFPC) +{ + local KFTrackedController TrackedController; + foreach TrackedControllers(TrackedController) + { + if (TrackedController.KFPC == KFPC) + { + return true; + } + } + + return false; +} + +DefaultProperties +{ + TrackedPlayerLifetime = 1.0f; +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_HRG_BallisticBouncer.uc b/KFGameContent/Classes/KFWeapAttach_HRG_BallisticBouncer.uc new file mode 100644 index 0000000..7ac7709 --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_HRG_BallisticBouncer.uc @@ -0,0 +1,120 @@ +//============================================================================= +// KFWeapAttach_HRG_BallisticBouncer +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeapAttach_HRG_BallisticBouncer extends KFWeaponAttachment; + +var() StaticMesh ChargeStaticMesh; +var transient StaticMeshComponent ChargeAttachment; +var transient MaterialInstanceConstant ChargeMIC; + +var float MinProjPlaceholderScale; +var float MaxProjPlaceHolderScale; + +var bool bIsCharging; +var bool bIsFullyCharged; + +var repnotify float StartFireTime; + +var int ChargeLevel; + +var float ChargeRTPC; + +simulated function StartFire() +{ + StartFireTime = WorldInfo.TimeSeconds; + bIsCharging = true; + + if (ChargeAttachment == none) + { + ChargeAttachment = new (self) class'StaticMeshComponent'; + ChargeAttachment.SetStaticMesh(ChargeStaticMesh); + + if (WeapMesh != none) + { + WeapMesh.AttachComponentToSocket(ChargeAttachment, 'MuzzleFlash'); + } + else + { + AttachComponent(ChargeAttachment); + } + + } + + if (ChargeAttachment != none) + { + ChargeAttachment.SetScale(MinProjPlaceholderScale); + ChargeAttachment.SetHidden(false); + } +} + +simulated event Tick(float DeltaTime) +{ + local float ChargeValue; + + if(bIsCharging && !bIsFullyCharged) + { + ChargeValue = FMin(class'KFWeap_HRG_BallisticBouncer'.default.MaxChargeTime, WorldInfo.TimeSeconds - StartFireTime) / class'KFWeap_HRG_BallisticBouncer'.default.MaxChargeTime; + + if (ChargeValue >= 1.0f) + { + bIsFullyCharged = true; + ChargeValue = 1.0f; + } + + if( ChargeAttachment != none) + { + ChargeAttachment.SetScale(MinProjPlaceholderScale + (MaxProjPlaceHolderScale - MinProjPlaceholderScale) * ChargeValue); + } + } + + Super.Tick(DeltaTime); +} + +simulated function FirstPersonFireEffects(Weapon W, vector HitLocation) +{ + super.FirstPersonFireEffects(W, HitLocation); + if (ChargeAttachment != none) + { + ChargeAttachment.SetHidden(true); + } +} + +simulated function bool ThirdPersonFireEffects(vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte) +{ + bIsCharging = false; + bIsFullyCharged = false; + ChargeRTPC=0; + + if (ChargeAttachment != none) + { + ChargeAttachment.SetHidden(true); + } + + return Super.ThirdPersonFireEffects(HitLocation, P, ThirdPersonAnimRateByte); +} + +simulated function CauseMuzzleFlash(byte FiringMode) +{ + if (MuzzleFlash == None && MuzzleFlashTemplate != None) + { + AttachMuzzleFlash(); + } + + Super.CauseMuzzleFlash(FiringMode); +} + +defaultproperties +{ + ChargeRTPC=0 + MuzzleFlashTemplate=KFMuzzleFlash'WEP_Mine_Reconstructor_Arch.Wep_Mine_Reconstructor_MuzzleFlash_3P' + + ChargeStaticMesh = StaticMesh'WEP_HRG_BallisticBouncer_EMIT.HRG_BallisticBouncer_ball_MESH' + MinProjPlaceholderScale = 2.0f; + MaxProjPlaceholderScale = 3.0f; +} diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_Doshinegun.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_Doshinegun.uc index f106d7d..ed8d853 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_Doshinegun.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_Doshinegun.uc @@ -196,7 +196,7 @@ defaultproperties InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Bludgeon_Doshinegun_Shot' FireInterval(DEFAULT_FIREMODE)=+0.2 Spread(DEFAULT_FIREMODE)=0.015 - InstantHitDamage(DEFAULT_FIREMODE)=60.0 //55.0 //60.0 + InstantHitDamage(DEFAULT_FIREMODE)=80 //60.0 //55.0 //60.0 FireOffset=(X=30,Y=4.5,Z=-5) // ALT_FIREMODE @@ -206,7 +206,7 @@ defaultproperties WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Dosh' InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Bludgeon_Doshinegun_Shot' FireInterval(ALTFIRE_FIREMODE)=+0.2 - InstantHitDamage(ALTFIRE_FIREMODE)=60.0 //55.0 //60.0 + InstantHitDamage(ALTFIRE_FIREMODE)=80 //60.0 //55.0 //60.0 Spread(ALTFIRE_FIREMODE)=0.015 // BASH_FIREMODE @@ -239,7 +239,7 @@ defaultproperties WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.47f), (Stat=EWUS_Damage1, Scale=1.47f), (Stat=EWUS_Weight, Add=2))) WeaponUpgrades[3]=(Stats=((Stat=EWUS_Damage0, Scale=1.70f), (Stat=EWUS_Damage1, Scale=1.70f), (Stat=EWUS_Weight, Add=3))) - DoshCost = 20; //25; + DoshCost = 10 //20; //25; bUsesSecondaryAmmoAltHUD=true bAllowClientAmmoTracking=false bIsBeingDropped=false diff --git a/KFGameContent/Classes/KFWeap_GravityImploder.uc b/KFGameContent/Classes/KFWeap_GravityImploder.uc index 83ef378..3e1d34b 100644 --- a/KFGameContent/Classes/KFWeap_GravityImploder.uc +++ b/KFGameContent/Classes/KFWeap_GravityImploder.uc @@ -142,7 +142,7 @@ defaultproperties WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Grenade_GravityImploder' InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_GravityImploderImpact' InstantHitDamage(DEFAULT_FIREMODE)=150 - FireInterval(DEFAULT_FIREMODE)=1.33 //45 RPM + FireInterval(DEFAULT_FIREMODE)=1 // 60 RPM //1.33 //45 RPM Spread(DEFAULT_FIREMODE)=0.02 //0 PenetrationPower(DEFAULT_FIREMODE)=0 FireOffset=(X=25,Y=3.0,Z=-2.5) @@ -152,7 +152,7 @@ defaultproperties FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Grenade_GravityImploderAlt' - FireInterval(ALTFIRE_FIREMODE)=1.33 //45 RPM + FireInterval(ALTFIRE_FIREMODE)=1 // 60 RPM //1.33 //45 RPM InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_GravityImploderImpactAlt' InstantHitDamage(ALTFIRE_FIREMODE)=200 Spread(ALTFIRE_FIREMODE)=0.02 //0.0085 diff --git a/KFGameContent/Classes/KFWeap_HRG_BallisticBouncer.uc b/KFGameContent/Classes/KFWeap_HRG_BallisticBouncer.uc new file mode 100644 index 0000000..3846e68 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_BallisticBouncer.uc @@ -0,0 +1,766 @@ +//============================================================================= +// KFWeap_HRG_BallisticBouncer +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeap_HRG_BallisticBouncer extends KFWeapon; + +//Props related to charging the weapon +var float MaxChargeTime; +var float ValueIncreaseTime; +var float DmgIncreasePerCharge; +var float AOEIncreasePerCharge; +var float IncapIncreasePerCharge; +var int AmmoIncreasePerCharge; + +var transient float ChargeTime; +var transient float ConsumeAmmoTime; +var transient float MaxChargeLevel; + +var ParticleSystem ChargedEffect; + +var transient ParticleSystemComponent FullyChargedPSC; +var transient bool bIsFullyCharged; + +var const WeaponFireSndInfo FullyChargedSound; + +var float FullChargedTimerInterval; +var float FXScalingFactorByCharge, ChargePercentage; +var float MinScale, MaxScale; + +var int MaxDamageByCharge; +var int MinDamageByCharge; + +const SecondaryFireAnim = 'Alt_Fire'; +const SecondaryFireIronAnim = 'Alt_Fire_Iron'; +const SecondaryFireAnimEmpty = 'Alt_Fire_Empty'; +const SecondaryFireIronAnimEmpty = 'Alt_Fire_Iron_Empty'; +var bool bHasToLaunchEmptyAnim; + +var SkelControlSingleBone Control; + +var bool bBlocked; + +var() StaticMesh ChargeStaticMesh; +var transient StaticMeshComponent ChargeAttachment; +var transient MaterialInstanceConstant ChargeMIC; + +var float MinProjPlaceholderScale; +var float MaxProjPlaceHolderScale; + +Replication +{ + if(Role == Role_Authority && bNetDirty) + ChargeTime; +} + +simulated event PostInitAnimTree( SkeletalMeshComponent SkelComp ) +{ + local vector vec; + local float fPercentage; + + super.PostInitAnimTree( SkelComp ); + + Control = SkelControlSingleBone( SkelComp.FindSkelControl('AmmoControl') ); + if( Control != none ) + { + Control.SetSkelControlActive( true ); + } + + //from 0 to -8 + // If AmmoCount is being replicated, don't allow the client to modify it here + fPercentage = FMin((float(AmmoCount[0])/(MagazineCapacity[0])), 1); + vec.X = Control.BoneTranslation.X; + vec.Y = Control.BoneTranslation.Y; + vec.Z = Lerp(-8, 0, fPercentage); + Control.BoneTranslation = vec; +} + +/** +* @see Weapon::ConsumeAmmo +*/ +simulated function ConsumeAmmo(byte FireModeNum) +{ + local vector vec; + local float fPercentage; + + //from 0 to -8 + // If AmmoCount is being replicated, don't allow the client to modify it here + if (Role == ROLE_Authority) + { + fPercentage = FMin(float(AmmoCount[0])/(MagazineCapacity[0]), 1); + super.ConsumeAmmo(FireModeNum); + + if(Control != none) + { + vec.X = Control.BoneTranslation.X; + vec.Y = Control.BoneTranslation.Y; + vec.Z = Lerp(-8, 0, fPercentage); + Control.BoneTranslation = vec; + } + + //Notify the client about the new percentage as the client is not tracking the ammo + ClientUpdateVisualAmmo(fPercentage); + } +} + +reliable client function ClientUpdateVisualAmmo(float BoneControlTranslation) +{ + local vector vec; + + if ( Role < ROLE_Authority && Control != none ) + { + vec.X = Control.BoneTranslation.X; + vec.Y = Control.BoneTranslation.Y; + vec.Z = Lerp(-8, 0, BoneControlTranslation); + Control.BoneTranslation = vec; + } +} + +simulated function StartFire(byte FiremodeNum) +{ + if (IsTimerActive('RefireCheckTimer') || bBlocked) + { + return; + } + if(Role != Role_Authority && FireModeNum == DEFAULT_FIREMODE && HasAmmo(DEFAULT_FIREMODE)) + { + bBlocked = true; + if(IsTimerActive(nameof(UnlockClientFire))) + { + ClearTimer(nameof(UnlockClientFire)); + } + } + + super.StartFire(FiremodeNum); + + if ( PendingFire(RELOAD_FIREMODE) && Role != Role_Authority) + { + bBlocked = false; + } + + +} + +simulated function RefireCheckTimer() +{ + Super.RefireCheckTimer(); + if(bBlocked && Role != Role_Authority) + { + SetTimer(0.25f , false, nameof(UnlockClientFire)); + } +} + +reliable client function UnlockClientFire() +{ + bBlocked = false; +} + +simulated function OnStartFire() +{ + local KFPawn PawnInst; + PawnInst = KFPawn(Instigator); + + if (PawnInst != none) + { + PawnInst.OnStartFire(); + } +} + + +simulated function FireAmmunition() +{ + // Let the accuracy tracking system know that we fired + HandleWeaponShotTaken(CurrentFireMode); + + // Handle the different fire types + switch (WeaponFireTypes[CurrentFireMode]) + { + case EWFT_InstantHit: + // Launch a projectile if we are in zed time, and this weapon has a projectile to launch for this mode + if (`IsInZedTime(self) && WeaponProjectiles[CurrentFireMode] != none ) + { + ProjectileFire(); + } + else + { + InstantFireClient(); + } + + break; + + case EWFT_Projectile: + ProjectileFire(); + break; + + case EWFT_Custom: + CustomFire(); + break; + } + + //// If we're firing without charging, still consume one ammo + if (GetChargeLevel() < 1) + { + ConsumeAmmo(CurrentFireMode); + } + + NotifyWeaponFired(CurrentFireMode); + + // Play fire effects now (don't wait for WeaponFired to replicate) + PlayFireEffects(CurrentFireMode, vect(0, 0, 0)); +} + +simulated function ANIMNOTIFY_FILLMAG() +{ + local vector vec; + + if (Control != none) + { + vec.X = Control.BoneTranslation.X; + vec.Y = Control.BoneTranslation.Y; + vec.Z = 0; + Control.BoneTranslation = vec; + } +} + +/** + * @see Weapon::HasAmmo + */ +simulated event bool HasAmmo( byte FireModeNum, optional int Amount ) +{ + local KFPerk InstigatorPerk; + // we can always do a melee attack + if( FireModeNum == BASH_FIREMODE ) + { + return TRUE; + } + else if ( FireModeNum == RELOAD_FIREMODE ) + { + return CanReload(); + } + else if ( FireModeNum == GRENADE_FIREMODE ) + { + if( KFInventoryManager(InvManager) != none ) + { + return KFInventoryManager(InvManager).HasGrenadeAmmo(Amount); + } + } + + InstigatorPerk = GetPerk(); + if( InstigatorPerk != none && InstigatorPerk.GetIsUberAmmoActive( self ) ) + { + return true; + } + + // If passed in ammo isn't set, use default ammo cost. + if( Amount == 0 ) + { + Amount = AmmoCost[FireModeNum]; + } + + return AmmoCount[GetAmmoType(FireModeNum)] >= Amount; +} + +simulated state MineReconstructorCharge extends WeaponFiring +{ + //For minimal code purposes, I'll directly call global.FireAmmunition after charging is released + simulated function FireAmmunition() + { + return; + } + + //Store start fire time so we don't have to timer this + simulated event BeginState(Name PreviousStateName) + { + local KFPerk InstigatorPerk; + + super.BeginState(PreviousStateName); + + InstigatorPerk = GetPerk(); + if( InstigatorPerk != none ) + { + SetZedTimeResist( InstigatorPerk.GetZedTimeModifier(self) ); + } + + ChargeTime = 0; + ConsumeAmmoTime = 0; + MaxChargeLevel = int(MaxChargeTime / ValueIncreaseTime); + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + if (ChargeAttachment == none) + { + ChargeAttachment = new (self) class'StaticMeshComponent'; + + // ChargeAttachment.SetActorCollision(false, false); + ChargeAttachment.SetStaticMesh(ChargeStaticMesh); + // ChargeAttachment.SetShadowParent(Mesh); + + ChargeMIC = ChargeAttachment.CreateAndSetMaterialInstanceConstant(0); + + MySkelMesh.AttachComponentToSocket(ChargeAttachment, 'MuzzleFlash'); + + ChargeAttachment.SetHidden(true); + } + else + { + ChargeAttachment.SetStaticMesh(ChargeStaticMesh); + } + } + + bIsFullyCharged = false; + + global.OnStartFire(); + + ChargeTime = 0; + FXScalingFactorByCharge = 0; + } + + simulated function bool ShouldRefire() + { + // ignore how much ammo is left (super/global counts ammo) + return StillFiring(CurrentFireMode); + } + + simulated event Tick(float DeltaTime) + { + local float ChargeRTPC; + local float InstantHitDamageValue; + local float NewScale; + + global.Tick(DeltaTime); + + if (ChargeAttachment != none && ChargeAttachment.StaticMesh == none) + { + ChargeAttachment.SetStaticMesh(ChargeStaticMesh); + } + + if(bIsFullyCharged) return; + + // Don't charge unless we're holding down the button + if (PendingFire(CurrentFireMode)) + { + ConsumeAmmoTime += DeltaTime; + } + + if (bIsFullyCharged) + { + if (ConsumeAmmoTime >= FullChargedTimerInterval) + { + ConsumeAmmo(DEFAULT_FIREMODE); + ConsumeAmmoTime -= FullChargedTimerInterval; + } + + return; + } + + // Don't charge unless we're holding down the button + if (PendingFire(CurrentFireMode)) + { + if(Role == Role_Authority && !bIsFullyCharged) + { + ChargeTime += DeltaTime; + bNetDirty = true; + } + + ChargePercentage = FMin(ChargeTime / MaxChargeTime, 1); + + if (ChargeAttachment != none && !bIsFullyCharged) + { + FXScalingFactorByCharge = FMin(Lerp(MinScale, MaxScale, ChargeTime / MaxChargeTime), MaxScale); + + if(ChargePercentage < 0.1f) + { + InstantHitDamageValue = Lerp(MinDamageByCharge, MaxDamageByCharge, 0.1f); + } + else + { + InstantHitDamageValue = Lerp(MinDamageByCharge, MaxDamageByCharge, ChargePercentage); + } + InstantHitDamage[DEFAULT_FIREMODE] = InstantHitDamageValue; + + NewScale = Lerp(MinProjPlaceholderScale, MaxProjPlaceholderScale, ChargePercentage); + + ChargeAttachment.SetHidden(false); + ChargeAttachment.SetScale( NewScale ); + + if (ChargeMIC != none) + { + // Change Color + ChargeMIC.SetScalarParameterValue('Charge', ChargePercentage); + } + } + } + + ChargeRTPC = FMin(ChargeTime / MaxChargeTime, 1.f); + KFPawn(Instigator).SetWeaponComponentRTPCValue("Weapon_Charge", ChargeRTPC); //For looping component + Instigator.SetRTPCValue('Weapon_Charge', ChargeRTPC); //For one-shot sounds + + if (ConsumeAmmoTime >= ValueIncreaseTime && !bIsFullyCharged) + { + ConsumeAmmo(DEFAULT_FIREMODE); + ConsumeAmmoTime -= ValueIncreaseTime; + } + + if (ChargeTime >= MaxChargeTime || !HasAmmo(DEFAULT_FIREMODE)) + { + bIsFullyCharged = true; + + if(( Instigator.Role != ROLE_Authority ) || WorldInfo.NetMode == NM_Standalone) + { + if (FullyChargedPSC == none) + { + FullyChargedPSC = new(self) class'ParticleSystemComponent'; + + if(MySkelMesh != none) + { + MySkelMesh.AttachComponentToSocket(FullyChargedPSC, 'MuzzleFlash'); + } + else + { + AttachComponent(FullyChargedPSC); + } + } + else + { + FullyChargedPSC.ActivateSystem(); + } + + FullyChargedPSC.SetTemplate(ChargedEffect); + + + KFPawn(Instigator).SetWeaponAmbientSound(FullyChargedSound.DefaultCue, FullyChargedSound.FirstPersonCue); + } + } + } + + //Now that we're done charging, directly call FireAmmunition. This will handle the actual projectile fire and scaling. + simulated event EndState(Name NextStateName) + { + if(Role == Role_Authority) + { + UnlockClientFire(); + } + + ClearZedTimeResist(); + ClearPendingFire(CurrentFireMode); + ClearTimer(nameof(RefireCheckTimer)); + + KFPawn(Instigator).bHasStartedFire = false; + KFPawn(Instigator).bNetDirty = true; + + if (ChargeAttachment != none) + { + ChargeAttachment.SetHidden(true); + ChargeAttachment.SetScale(MinProjPlaceholderScale); + } + + if (FullyChargedPSC != none) + { + FullyChargedPSC.DeactivateSystem(); + } + + KFPawn(Instigator).SetWeaponAmbientSound(none); + } + + simulated function HandleFinishedFiring() + { + global.FireAmmunition(); + + if (bPlayingLoopingFireAnim) + { + StopLoopingFireEffects(CurrentFireMode); + } + + SetTimer(0.1f, false, 'Timer_StopFireEffects'); + + NotifyWeaponFinishedFiring(CurrentFireMode); + + super.HandleFinishedFiring(); + //`Log("ChargePercentage"@ChargePercentage); + } + + simulated function PutDownWeapon() + { + global.FireAmmunition(); + + if (bPlayingLoopingFireAnim) + { + StopLoopingFireEffects(CurrentFireMode); + } + + SetTimer(0.1f, false, 'Timer_StopFireEffects'); + + if (ChargeAttachment != none) + { + ChargeAttachment.SetHidden(true); + ChargeAttachment.SetScale(MinProjPlaceholderScale); + } + + NotifyWeaponFinishedFiring(CurrentFireMode); + + if(Role == Role_Authority) + { + UnlockClientFire(); + } + + super.PutDownWeapon(); + } +} + +simulated function StopFireEffects(byte FireModeNum) +{ + Super.StopFireEffects(FireModeNum); + + if (ChargeAttachment != none) + { + ChargeAttachment.SetStaticMesh(none); // if we don't do this it doesn't work ?!, when starting fire it assigns again so no problem.. + + ChargeAttachment.SetHidden(true); + ChargeAttachment.SetScale(MinProjPlaceholderScale); + } +} + +// Placing the actual Weapon Firing end state here since we need it to happen at the end of the actual firing loop. +simulated function Timer_StopFireEffects() +{ + // Simulate weapon firing effects on the local client + if (WorldInfo.NetMode == NM_Client) + { + Instigator.WeaponStoppedFiring(self, false); + + if (ChargeAttachment != none) + { + ChargeAttachment.SetHidden(true); + } + + if (FullyChargedPSC != none) + { + FullyChargedPSC.DeactivateSystem(); + } + } + + ClearFlashCount(); + ClearFlashLocation(); +} + +simulated state Active +{ + simulated function BeginState(name PreviousStateName) + { + Super.BeginState(PreviousStateName); + if(Role == Role_Authority) + { + UnlockClientFire(); + } + } + +} + + +simulated function KFProjectile SpawnProjectile(class KFProjClass, vector RealStartLoc, vector AimDir) +{ + local KFProj_HRG_BallisticBouncer BouncingProj; + + BouncingProj = KFProj_HRG_BallisticBouncer(super.SpawnProjectile(KFProjClass, RealStartLoc, AimDir)); + //Calc and set scaling values + if (BouncingProj != none) + { + ChargePercentage = FMax(0.1, ChargePercentage); + FXScalingFactorByCharge = FMax(0.1, FXScalingFactorByCharge); + BouncingProj.SetInheritedScale(FXScalingFactorByCharge, ChargePercentage); + return BouncingProj; + } + return none; +} + +simulated function CauseMuzzleFlash(byte FireModeNum) +{ + local vector vec; + + if (MuzzleFlash == None) + { + AttachMuzzleFlash(); + } + + if (MuzzleFlash != none) + { + vec.X = ChargePercentage; + vec.Y = ChargePercentage; + vec.Z = ChargePercentage; + + MuzzleFlash.MuzzleFlash.PSC.SetVectorParameter(name("Charge"), vec); + MuzzleFlash.CauseMuzzleFlash(FireModeNum); + } + + super.CauseMuzzleFlash(FireModeNum); +} + +simulated function int GetChargeLevel() +{ + return Min(ChargeTime / ValueIncreaseTime, MaxChargeLevel); +} + +/**************************************************************** + + PAWN ADJUST DAMAGE + +****************************************************************/ + +// increase the instant hit damage based on the charge level +simulated function int GetModifiedDamage(byte FireModeNum, optional vector RayDir) +{ + local int ModifiedDamage; + ModifiedDamage = super.GetModifiedDamage(FireModeNum, RayDir); + return ModifiedDamage; +} + +state WeaponSingleFiring +{ + /** Get whether we should play the reload anim as well or not */ + simulated function name GetWeaponFireAnim(byte FireModeNum) + { + if(bUsingSights) + { + return (bHasToLaunchEmptyAnim == false) ? SecondaryFireIronAnim : SecondaryFireIronAnimEmpty; + } + else + { + return (bHasToLaunchEmptyAnim == false) ? SecondaryFireIronAnim : SecondaryFireIronAnimEmpty; + } + } +} + +defaultproperties +{ + //Gameplay Props + MaxChargeTime=0.6 + AmmoIncreasePerCharge=1 + ValueIncreaseTime=0.1 + + //FOR LERPING DAMANGE + MaxDamageByCharge=600 + MinDamageByCharge=60 + // FOV + Meshfov=80 + MeshIronSightFOV=65 //52 + PlayerIronSightFOV=50 //80 + + // Depth of field + DOF_FG_FocalRadius=150 + DOF_FG_MaxNearBlurSize=1 + + // Content + PackageKey="HRG_BallisticBouncer" + FirstPersonMeshName="wep_1p_hrg_ballisticbouncer_mesh.Wep_1stP_HRG_BallisticBouncer_Rig" + FirstPersonAnimSetNames(0)="wep_1p_hrg_ballisticbouncer_anim.Wep_1stP_BallisticBouncer_Anim" + PickupMeshName="wep_3p_hrg_ballisticbouncer_mesh.Wep_3rdP_HRG_BallisticBouncer_Pickup" + AttachmentArchetypeName="wep_hrg_ballisticbouncer_arch.Wep_HRG_BallisticBouncer_3P" + MuzzleFlashTemplateName="WEP_HRG_BallisticBouncer_ARCH.Wep_HRG_BallisticBouncer_MuzzleFlash" + + Begin Object Name=FirstPersonMesh + // new anim tree with skelcontrol to rotate cylinders + AnimTreeTemplate=AnimTree'WEP_HRG_BallisticBouncer_ARCH.WEP_1stP_Animtree_HRG_BallisticBouncer' + End Object + + // Zooming/Position + PlayerViewOffset=(X=0.0,Y=12,Z=-1) + IronSightPosition=(X=0,Y=0,Z=0) + + // Controls the rotation when Hans(the bastard) grabs you + QuickWeaponDownRotation=(Pitch=-19192,Yaw=-11500,Roll=16384) // (Pitch=-19192,Yaw=-11000,Roll=16384) + + // Ammo + MagazineCapacity[0]=18 + SpareAmmoCapacity[0]=162 + InitialSpareMags[0]=3 //2 + AmmoPickupScale[0]=1.5 //1 //0.75 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=180 + minRecoilPitch=140 + maxRecoilYaw=150 + minRecoilYaw=-150 + RecoilRate=0.085 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=75 + RecoilISMinYawLimit=65460 + RecoilISMaxPitchLimit=375 + RecoilISMinPitchLimit=65460 + RecoilViewRotationScale=0.25 + IronSightMeshFOVCompensationScale=1.5 + HippedRecoilModifier=1.5 + + // Inventory + InventorySize=5 + GroupPriority=80 //75 + WeaponSelectTexture=Texture2D'wep_ui_hrg_ballisticbouncer_tex.UI_WeaponSelect_HRG_BallisticBouncer' + + // DEFAULT_FIREMODE + //FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Grenade' //@TODO: Replace me + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunSingle' + FiringStatesArray(DEFAULT_FIREMODE)=MineReconstructorCharge + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_HRG_BallisticBouncer' + FireInterval(DEFAULT_FIREMODE)=+0.223 //+0.33 + InstantHitDamage(DEFAULT_FIREMODE)=100 + PenetrationPower(DEFAULT_FIREMODE)=0.0; + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Bludgeon_HRG_BallisticBouncer_Shot' + FireOffset=(X=39,Y=4.5,Z=-10) + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_BallisticBouncer' + InstantHitDamage(BASH_FIREMODE)=27 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_3P_Start', FirstPersonCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_1P_Start') + WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_3P_Shoot', FirstPersonCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_1P_Shoot') + FullyChargedSound=(DefaultCue = AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Charged_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Charged') + + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_DryFire' + + // Advanced (High RPM) Fire Effects + bLoopingFireAnim(DEFAULT_FIREMODE)=true + bLoopingFireSnd(DEFAULT_FIREMODE)=true + SingleFireSoundIndex=FIREMODE_NONE + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + AssociatedPerkClasses(0)= class'KFPerk_Support' + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Weak_Recoil' + + ChargedEffect=ParticleSystem'WEP_HRG_BallisticBouncer_EMIT.FX_Mine_HRG_BallisticBouncer_FullCharge' + + FullChargedTimerInterval=2.0f + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.1f,IncrementWeight=1) + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) + + FXScalingFactorByCharge = 0; + MinScale=0.5 + MaxScale=1.5 + + bBlocked = false; + + bAllowClientAmmoTracking = false; + + ChargeStaticMesh = StaticMesh'WEP_HRG_BallisticBouncer_EMIT.HRG_BallisticBouncer_ball_MESH' + MinProjPlaceholderScale = 2.0f; + MaxProjPlaceholderScale = 3.0f; +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_HRG_MedicMissile.uc b/KFGameContent/Classes/KFWeap_HRG_MedicMissile.uc new file mode 100644 index 0000000..578b004 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_MedicMissile.uc @@ -0,0 +1,210 @@ +//============================================================================= +// KFWeap_HRG_MedicMissile +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HRG_MedicMissile extends KFWeap_GrenadeLauncher_Base; + +/** Back blash explosion template. */ +var() GameExplosion ExplosionTemplate; + +/** Holds an offest for spawning back blast effects. */ +var() vector BackBlastOffset; + +/** Fires a projectile, but also does the back blast */ +simulated function CustomFire() +{ + local KFExplosionActorReplicated ExploActor; + local vector SpawnLoc; + local rotator SpawnRot; + + ProjectileFire(); + + if ( Instigator.Role < ROLE_Authority ) + { + return; + } + + GetBackBlastLocationAndRotation(SpawnLoc, SpawnRot); + + // explode using the given template + ExploActor = Spawn(class'KFExplosionActorReplicated', self,, SpawnLoc, SpawnRot,, true); + if (ExploActor != None) + { + ExploActor.InstigatorController = Instigator.Controller; + ExploActor.Instigator = Instigator; + + // So we get backblash decal from this explosion + ExploActor.bTraceForHitActorWhenDirectionalExplosion = true; + + ExploActor.Explode(ExplosionTemplate, vector(SpawnRot)); + } + + if ( bDebug ) + { + DrawDebugCone(SpawnLoc, vector(SpawnRot), ExplosionTemplate.DamageRadius, ExplosionTemplate.DirectionalExplosionAngleDeg * DegToRad, + ExplosionTemplate.DirectionalExplosionAngleDeg * DegToRad, 16, MakeColor(64,64,255,0), TRUE); + } +} + +/** + * This function returns the world location for spawning back blast and the direction to send the back blast in + */ +simulated function GetBackBlastLocationAndRotation(out vector BlastLocation, out rotator BlastRotation) +{ + local vector X, Y, Z; + local Rotator ViewRotation; + + if( Instigator != none ) + { + if( bUsingSights ) + { + ViewRotation = Instigator.GetViewRotation(); + + // Add in the free-aim rotation + if ( KFPlayerController(Instigator.Controller) != None ) + { + ViewRotation += KFPlayerController(Instigator.Controller).WeaponBufferRotation; + } + + GetAxes(ViewRotation, X, Y, Z); + + BlastRotation = Rotator(Vector(ViewRotation) * -1); + BlastLocation = Instigator.GetWeaponStartTraceLocation() + X * BackBlastOffset.X; + } + else + { + ViewRotation = Instigator.GetViewRotation(); + + // Add in the free-aim rotation + if ( KFPlayerController(Instigator.Controller) != None ) + { + ViewRotation += KFPlayerController(Instigator.Controller).WeaponBufferRotation; + } + + BlastRotation = Rotator(Vector(ViewRotation) * -1); + BlastLocation = Instigator.GetPawnViewLocation() + (BackBlastOffset >> ViewRotation); + } + } +} + +/** Locks the bolt bone in place to the open position (Called by animnotify) */ +simulated function ANIMNOTIFY_LockBolt() +{ + // Consider us empty after every shot so the rocket gets hidden + EmptyMagBlendNode.SetBlendTarget(1, 0); +} + +defaultproperties +{ + ForceReloadTime=0.4f + + // Inventory + InventoryGroup=IG_Primary + GroupPriority=100 + InventorySize=7 + WeaponSelectTexture=Texture2D'WEP_UI_HRG_MedicMissile_TEX.UI_WeaponSelect_HRG_MedicMissile' + + // FOV + MeshFOV=75 + MeshIronSightFOV=65 + PlayerIronSightFOV=70 + PlayerSprintFOV=95 + + // Depth of field + DOF_FG_FocalRadius=50 + DOF_FG_MaxNearBlurSize=2.5 + + // Zooming/Position + PlayerViewOffset=(X=10.0,Y=10,Z=-2) + FastZoomOutTime=0.2 + + // Content + PackageKey="HRG_MedicMissile" + FirstPersonMeshName="WEP_1P_HRG_MedicMissile_MESH.Wep_1stP_HRG_MedicMissile_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_HRG_MedicMissile_ANIM.Wep_1stP_HRG_MedicMissile_Anim" + PickupMeshName="WEP_3P_HRG_MedicMissile_MESH.Wep_HRG_MedicMissile_Pickup" + AttachmentArchetypeName="WEP_HRG_MedicMissile_ARCH.Wep_HRG_MedicMissile_3P" + MuzzleFlashTemplateName="WEP_HRG_MedicMissile_ARCH.Wep_HRG_MedicMissile_MuzzleFlash" + + // Zooming/Position + IronSightPosition=(X=0,Y=0,Z=0) + + // Ammo + MagazineCapacity[0]=1 + SpareAmmoCapacity[0]=22 + InitialSpareMags[0]=6 + AmmoPickupScale[0]=4.0 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=800 + minRecoilPitch=675 + maxRecoilYaw=400 + minRecoilYaw=-400 + RecoilRate=0.085 + RecoilBlendOutRatio=0.35 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=1500 + RecoilMinPitchLimit=64785 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=500 + RecoilISMinPitchLimit=65485 + RecoilViewRotationScale=0.8 + FallingRecoilModifier=1.5 + HippedRecoilModifier=1.25 + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'UI_FireModes_TEX.UI_FireModeSelect_Rocket' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFireAndReload + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Custom + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Rocket_HRG_MedicMissile' + FireInterval(DEFAULT_FIREMODE)=+0.25 + InstantHitDamage(DEFAULT_FIREMODE)=100.0 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_MedicMissile' + Spread(DEFAULT_FIREMODE)=0.025 + FireOffset=(X=20,Y=4.0,Z=-3) + BackBlastOffset=(X=-20,Y=4.0,Z=-3) + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None + + // Back blash explosion settings. Using archetype so that clients can serialize the content + // without loading the 1st person weapon content (avoid 'Begin Object')! + ExplosionTemplate=KFGameExplosion'WEP_HRG_MedicMissile_ARCH.Wep_HRG_MedicMissile_BackBlastExplosion' + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_MedicMissile' + InstantHitDamage(BASH_FIREMODE)=27 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_3P_Shoot', FirstPersonCue=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_1P_Shoot') + + //@todo: add akevent when we have it + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_DryFire' + + // Animation + bHasFireLastAnims=true + IdleFidgetAnims=(Guncheck_v1, Guncheck_v2) + + BonesToLockOnEmpty=(RW_Grenade1) + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + AssociatedPerkClasses(0)=class'KFPerk_FieldMedic' + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot' + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.1f,IncrementWeight=1) + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.1f), (Stat=EWUS_Weight, Add=1))) +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_HRG_SonicGun.uc b/KFGameContent/Classes/KFWeap_HRG_SonicGun.uc index e8525c6..3802717 100644 --- a/KFGameContent/Classes/KFWeap_HRG_SonicGun.uc +++ b/KFGameContent/Classes/KFWeap_HRG_SonicGun.uc @@ -76,6 +76,7 @@ var Array HolographicSightUseDefaultByChargeLevel; simulated function PostBeginPlay() { CurrentChargeLevel=0; + if( WeaponMICs.Length > `SONICGUN_MIC_SIGHT_INDEX ) { if (!HolographicSightUseDefaultByChargeLevel[0]) @@ -94,7 +95,6 @@ simulated function PostBeginPlay() simulated function AltFireMode() { // skip super - if (!Instigator.IsLocallyControlled()) { return; @@ -127,19 +127,6 @@ simulated function name GetReloadAnimName(bool bTacticalReload) } } -simulated function StartFire(byte FireModeNum) -{ - if (FireModeNum == ALTFIRE_FIREMODE) - { - if (!IsCanIncrementCharge()) - { - return; - } - } - - super.StartFire(FireModeNum); -} - /********************************************************************************************* * State WeaponSonicGunCharging * The weapon is in this state while detonating a charge @@ -184,7 +171,7 @@ simulated state WeaponSonicGunSingleFiring extends WeaponSingleFiring local vector UsedKickMomentum; // Push the player back when they fire a fully charged sonic blast - if (Instigator != none && CurrentChargeLevel == 1 ) //2 + if (Instigator != none && CurrentChargeLevel == MaxChargeLevel ) { UsedKickMomentum.X = -FullyChargedKickMomentum; @@ -208,21 +195,26 @@ simulated state WeaponSonicGunSingleFiring extends WeaponSingleFiring simulated function FireAmmunition() { - super.FireAmmunition(); - CurrentChargeLevel=0; + if (CurrentFireMode == ALTFIRE_FIREMODE) + { + // Instant shoot: always use max level + CurrentChargeLevel = MaxChargeLevel; + } if( WeaponMICs.Length > `SONICGUN_MIC_SIGHT_INDEX ) { - if (!HolographicSightUseDefaultByChargeLevel[0]) + if (!HolographicSightUseDefaultByChargeLevel[CurrentChargeLevel]) { - WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].SetVectorParameterValue('Vector_Center_Color_A', HolographicSightByChargeLevel[0]); - WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].SetVectorParameterValue('Vector_Scanline_Color_Mult', HolographicSightScanlineByChargeLevel[0]); + WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].SetVectorParameterValue('Vector_Center_Color_A', HolographicSightByChargeLevel[CurrentChargeLevel]); + WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].SetVectorParameterValue('Vector_Scanline_Color_Mult', HolographicSightScanlineByChargeLevel[CurrentChargeLevel]); } - else - { + else { WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].ClearParameterValues(); } } + + super.FireAmmunition(); + CurrentChargeLevel=0; } } @@ -319,7 +311,7 @@ simulated function float GetForceReloadDelay() simulated function KFProjectile SpawnProjectile( class KFProjClass, vector RealStartLoc, vector AimDir ) { local KFProjectile SpawnedProjectile; - local int ProjDamage; + local int ProjDamage; // Spawn projectile SpawnedProjectile = Spawn( KFProjClass, Self,, RealStartLoc,,,true); @@ -329,9 +321,9 @@ simulated function KFProjectile SpawnProjectile( class KFProjClass // these properties are replicated via TakeHitInfo if ( InstantHitDamage.Length > CurrentFireMode && InstantHitDamageTypes.Length > CurrentFireMode ) { - InstantHitDamage[DEFAULT_FIREMODE] = SonicBlastDamageByChargeLevel[CurrentChargeLevel]; - InstantHitMomentum[DEFAULT_FIREMODE]=SonicBlastMomentumByChargeLevel[CurrentChargeLevel]; - InstantHitDamageTypes[DEFAULT_FIREMODE]=SonicBlastDamageTypeByChargeLevel[CurrentChargeLevel]; + InstantHitDamage[CurrentFiremode] = SonicBlastDamageByChargeLevel[CurrentChargeLevel]; + InstantHitMomentum[CurrentFiremode]=SonicBlastMomentumByChargeLevel[CurrentChargeLevel]; + InstantHitDamageTypes[CurrentFiremode]=SonicBlastDamageTypeByChargeLevel[CurrentChargeLevel]; ProjDamage = GetModifiedDamage(CurrentFireMode); SpawnedProjectile.Damage = ProjDamage; @@ -341,7 +333,7 @@ simulated function KFProjectile SpawnProjectile( class KFProjClass // Set the penetration power for this projectile // because of clientside hit detection, we need two variables -- // one that replicates on init and one that updates but doesn't replicate - PenetrationPower[DEFAULT_FIREMODE]=SonicBlastPenetrationPowerByChargeLevel[CurrentChargeLevel]; + PenetrationPower[CurrentFireMode]=SonicBlastPenetrationPowerByChargeLevel[CurrentChargeLevel]; SpawnedProjectile.InitialPenetrationPower = GetInitialPenetrationPower(CurrentFireMode); SpawnedProjectile.PenetrationPower = SpawnedProjectile.InitialPenetrationPower; @@ -356,7 +348,7 @@ simulated function KFProjectile SpawnProjectile( class KFProjClass //Overriding to make KFProjectile Class for default fire mode to be dependant on charge level simulated function class GetKFProjectileClass() { - if (CurrentFireMode == DEFAULT_FIREMODE) + if (CurrentFireMode == DEFAULT_FIREMODE || CurrentFireMode == ALTFIRE_FIREMODE) { return SonicBlastProjectileClassByChargeLevel[CurrentChargeLevel]; } @@ -377,7 +369,7 @@ simulated function float GetUpgradedPenetration(optional int FireMode = DEFAULT_ //Overriding to change shot sounds for default fire mode to be dependant on charge level simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation ) { - WeaponFireSnd[DEFAULT_FIREMODE]=SonicBlastFireSoundByChargeLevel[CurrentChargeLevel]; + WeaponFireSnd[CurrentFireMode]=SonicBlastFireSoundByChargeLevel[CurrentChargeLevel]; super.PlayFireEffects(FireModeNum, HitLocation); } @@ -398,7 +390,7 @@ simulated function ProcessInstantHitEx(byte FiringMode, ImpactInfo Impact, optio IndexMomentumMultiplierByZed = MomentumMultiplierByZedArray.Find('ZedClassName', Impact.HitActor.Class.Name); if (IndexMomentumMultiplierByZed != INDEX_NONE) { - InstantHitMomentum[DEFAULT_FIREMODE] *= MomentumMultiplierByZedArray[IndexMomentumMultiplierByZed].MomentumMultiplier; + InstantHitMomentum[CurrentFireMode] *= MomentumMultiplierByZedArray[IndexMomentumMultiplierByZed].MomentumMultiplier; } } @@ -452,8 +444,8 @@ defaultproperties DOF_FG_MaxNearBlurSize=3.5 // Ammo - MagazineCapacity[0]=12 //8 - SpareAmmoCapacity[0]=96 //72 + MagazineCapacity[0]=10 //12 //8 + SpareAmmoCapacity[0]=90 //96 //72 InitialSpareMags[0]=1 bCanBeReloaded=true bReloadFromMagazine=true @@ -500,9 +492,16 @@ defaultproperties FallingMomentumReduction=0.5 // ALTFIRE_FIREMODE (remote detonate) - FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSonicGunCharging - WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Custom - AmmoCost(ALTFIRE_FIREMODE)=0 + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSonicGunSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_SonicBlastFullyCharged_HRG_SonicGun' + FireInterval(ALTFIRE_FIREMODE)=0.75 + InstantHitDamage(ALTFIRE_FIREMODE)=125 + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_SonicGun_SonicBlastFullyCharged' + InstantHitMomentum(ALTFIRE_FIREMODE)=200000 + Spread(ALTFIRE_FIREMODE)=0.005 + PenetrationPower(ALTFIRE_FIREMODE)=2.0 + AmmoCost(ALTFIRE_FIREMODE)=1 // BASH_FIREMODE InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_SonicGun' diff --git a/KFGameContent/Classes/KFWeap_HVStormCannon.uc b/KFGameContent/Classes/KFWeap_HVStormCannon.uc new file mode 100644 index 0000000..640f43b --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HVStormCannon.uc @@ -0,0 +1,417 @@ +//============================================================================= +// KFWeap_HVStormCannon +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HVStormCannon extends KFWeap_ScopedBase; + +var const float TrackingInstanceTimeDelaySeconds; +var const float TrackingRadius; +var const int TrackingDamageValue; +var const array TrackingDamage; + +var int WeaponID; + +struct HVStormCannon_ProjectileTrackingInstance +{ + var int NumberHits; + var float TimeNextJump; + var KFPawn_Monster LastTarget; + var array HitTargets; + var Controller Instigator; // Instigator that shot the bullet + var byte IDCounter; + + structdefaultproperties + { + NumberHits=0 + TimeNextJump=0 + LastTarget=none + Instigator=none + } +}; + +var array HVStormCannon_ProjectileTracking; + +event PostBeginPlay() +{ + local int Counter; + local KFPlayerController KFPC; + + Super.PostBeginPlay(); + + Counter = 0; + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + ++Counter; + if (KFPC == Instigator.Controller) + { + WeaponID = Counter * 1000; + break; + } + } +} + +simulated function ProcessInstantHitEx(byte FiringMode, ImpactInfo Impact, optional int NumHits, optional out float out_PenetrationVal, optional int ImpactNum ) +{ + local int HitZoneIdx; + local KFPawn_Monster Target; + local HVStormCannon_ProjectileTrackingInstance NewTrackingInstance; + local KFPlayerController KFPC; + + if (Role == ROLE_Authority) + { + Target = KFPawn_Monster(Impact.HitActor); + if (Target != none && !Target.bIsHeadless) + { + HitZoneIdx = Target.HitZones.Find('ZoneName', Impact.HitInfo.BoneName); + if (HitZoneIdx == HZI_Head && Target.IsAliveAndWell()) + { + NewTrackingInstance.TimeNextJump = WorldInfo.TimeSeconds + TrackingInstanceTimeDelaySeconds; + NewTrackingInstance.LastTarget = Target; + NewTrackingInstance.HitTargets.AddItem(Target); + NewTrackingInstance.Instigator = Instigator.Controller; + + KFPC = KFPlayerController(Instigator.Controller); + NewTrackingInstance.IDCounter = KFPC.StormCannonIDCounter; + + if (KFPC.StormCannonIDCounter < 255) + { + ++KFPC.StormCannonIDCounter; + } + else + { + KFPC.StormCannonIDCounter = 0; + } + + // We simulate EMP affliction on Server, we can't use the affliction itself because it's duration is super hard to control + // To completely sync with the logic of TrackingInstanceTimeDelaySeconds + + // Simulate start EMP affliction + Target.bEmpPanicked = true; + Target.OnStackingAfflictionChanged(AF_EMP); + + HVStormCannon_ProjectileTracking.AddItem(NewTrackingInstance); + + StartBeamVFX(Target, NewTrackingInstance.IDCounter); + } + } + } + super.ProcessInstantHitEx( FiringMode, Impact, NumHits, out_PenetrationVal, ImpactNum ); +} + +function KFPawn_Monster SearchClosestTarget(HVStormCannon_ProjectileTrackingInstance CurrentTrackingInstance) +{ + local KFPawn_Monster CurrentTarget, BestTarget; + local TraceHitInfo HitInfo; + + local float BestDistance; + local float Distance; + + local vector ReferenceLocation; + + local vector HitLocation, HitNormal; + local Actor HitActor; + + ReferenceLocation = CurrentTrackingInstance.LastTarget.Mesh.GetBoneLocation('head'); + + //CurrentTrackingInstance.LastTarget.DrawDebugSphere(ReferenceLocation, CurrentTrackingInstance.LastTarget.GetCollisionRadius() + TrackingRadius, 10, 0, 255, 0, true); + + foreach CollidingActors(class'KFPawn_Monster' + , CurrentTarget + , CurrentTrackingInstance.LastTarget.GetCollisionRadius() + TrackingRadius + , ReferenceLocation + , true,, HitInfo) + { + if (!CurrentTarget.IsAliveAndWell() || CurrentTarget.bIsCloaking) + { + continue; + } + + // Check if the target does have map geometry in between.. + HitActor = Trace(HitLocation, HitNormal, CurrentTarget.Mesh.GetBoneLocation('head'), ReferenceLocation,,,,TRACEFLAG_Bullet); + + if (HitActor != none && KFPawn_Monster(HitActor) == none) + { + continue; + } + + // Don't hit again targets.. + if (CurrentTrackingInstance.HitTargets.Find(CurrentTarget) != INDEX_NONE) + { + continue; + } + + // The closest.. + Distance = VSizeSq(ReferenceLocation - CurrentTarget.Mesh.GetBoneLocation('head')); + + if (BestTarget == none) + { + BestTarget = CurrentTarget; + BestDistance = Distance; + } + else if (BestDistance > Distance) + { + BestTarget = CurrentTarget; + BestDistance = Distance; + } + } + + return BestTarget; +} + +function UpdateTracking() +{ + local int i; + local KFPawn_Monster NextTarget; + local bool bClear; + local TraceHitInfo HitInfo; + + HitInfo.BoneName = 'head'; // we force headshot + + for (i = HVStormCannon_ProjectileTracking.Length - 1; i >= 0; i--) + { + bClear = false; + + if (WorldInfo.TimeSeconds >= HVStormCannon_ProjectileTracking[i].TimeNextJump) + { + // Simulate stop EMP affliction + HVStormCannon_ProjectileTracking[i].LastTarget.bEmpPanicked = false; + HVStormCannon_ProjectileTracking[i].LastTarget.OnStackingAfflictionChanged(AF_EMP); + + if (HVStormCannon_ProjectileTracking[i].NumberHits < TrackingDamage.length) + { + NextTarget = SearchClosestTarget(HVStormCannon_ProjectileTracking[i]); + if (NextTarget != none) + { + HVStormCannon_ProjectileTracking[i].TimeNextJump = WorldInfo.TimeSeconds + TrackingInstanceTimeDelaySeconds; + + // Simulate start EMP affliction + NextTarget.bEmpPanicked = true; + NextTarget.OnStackingAfflictionChanged(AF_EMP); + + StartBeamVFX(NextTarget, HVStormCannon_ProjectileTracking[i].IDCounter); + + HVStormCannon_ProjectileTracking[i].LastTarget = NextTarget; + HVStormCannon_ProjectileTracking[i].HitTargets.AddItem(NextTarget); + + NextTarget.TakeDamage(TrackingDamageValue * TrackingDamage[HVStormCannon_ProjectileTracking[i].NumberHits] + , HVStormCannon_ProjectileTracking[i].Instigator + , NextTarget.Mesh.GetBoneLocation('head') + , vect(0,0,0) + , class'KFDT_HVStormCannonSpread' + , HitInfo); + + ++HVStormCannon_ProjectileTracking[i].NumberHits; + } + else + { + // If there are no targets we finish early + bClear = true; + } + } + else + { + // Max hits reached + bClear = true; + } + } + + if (bClear) + { + StopBeamVFX(HVStormCannon_ProjectileTracking[i].IDCounter); + HVStormCannon_ProjectileTracking.Remove(i, 1); + } + } +} + +simulated event Tick(float DeltaTime) +{ + Super.Tick(DeltaTime); + + if (Role == ROLE_Authority) + { + UpdateTracking(); + } + + if (Role != ROLE_Authority || WorldInfo.NetMode == NM_Standalone) + { + UpdateAmmoCounter(); + } +} + +simulated function StartBeamVFX(KFPawn_Monster KFPM, byte ID) +{ + local KFPlayerController KFPC; + + if (Role == ROLE_Authority) + { + // Send RPC to all players + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + KFPC.AddStormCannonVFX(KFPM, GetID(ID)); + } + } +} + +simulated function StopBeamVFX(byte ID) +{ + local KFPlayerController KFPC; + + if (Role == ROLE_Authority) + { + // Send RPC to all players + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + KFPC.RemoveStormCannonVFX(GetID(ID)); + } + } +} + +function int GetID(byte ID) +{ + return WeaponID + ID; +} + +simulated function KFProjectile SpawnProjectile( class KFProjClass, vector RealStartLoc, vector AimDir ) +{ + local vector SocketLocation; + local rotator SocketRotation; + local vector SpawnLocation; + local vector SpawnDirection; + + if(bUsingSights && MySkelMesh.GetSocketWorldLocationAndRotation('MuzzleFlash_Scope', SocketLocation, SocketRotation, 0)) + { + SpawnLocation = SocketLocation; + SpawnDirection = vector(SocketRotation); + } + else + { + SpawnLocation = RealStartLoc; + SpawnDirection = AimDir; + } + + return super.SpawnProjectile(KFProjClass, SpawnLocation, SpawnDirection); +} + +simulated function UpdateAmmoCounter() +{ + local float PercentageAmmo; + + PercentageAmmo = float((AmmoCount[0] - 1)) / float(MagazineCapacity[0]); + + WeaponMICs[3].SetScalarParameterValue('opacity', PercentageAmmo); +} + +defaultproperties +{ + // FOV + MeshFOV=70 + MeshIronSightFOV=52 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=0 + DOF_FG_MaxNearBlurSize=2.5 + + // Content + PackageKey="HVStormCannon" + FirstPersonMeshName="WEP_1P_HVStormCannon_MESH.Wep_1stP_HVStormCannon_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_HVStormCannon_ANIM.Wep_1p_HVStormCannon_Anim" + PickupMeshName="WEP_3P_HVStormCannon_MESH.Wep_HRG_HVStormCannon_Pickup" + AttachmentArchetypeName = "WEP_HVStormCannon_ARCH.HVStormCannon_3P" + MuzzleFlashTemplateName="WEP_HVStormCannon_ARCH.Wep_HVStormCannon_MuzzleFlash" + + // Ammo + MagazineCapacity[0]=8 + SpareAmmoCapacity[0]=96 + InitialSpareMags[0]=2 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Zooming/Position + PlayerViewOffset=(X=12.0,Y=13,Z=-2) //x7 + IronSightPosition=(X=-5,Y=-0.01,Z=0.30) //X=-5,Y=-0.019,Z=0.22 + + // AI warning system + bWarnAIWhenAiming=true + AimWarningDelay=(X=0.4f, Y=0.8f) + AimWarningCooldown=0.0f + + // Recoil + maxRecoilPitch=300 + minRecoilPitch=200 + maxRecoilYaw=150 + minRecoilYaw=-150 + RecoilRate=0.08 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=1000 + RecoilMaxPitchLimit=1250 + RecoilMinPitchLimit=1500 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=1000 + RecoilISMaxPitchLimit=500 + RecoilISMinPitchLimit=1000 + RecoilViewRotationScale=0.6 + IronSightMeshFOVCompensationScale=1.5 + + // Inventory + InventorySize=8 + GroupPriority=125 + WeaponSelectTexture=Texture2D'wep_ui_hvstormcannon_tex.UI_WeaponSelect_HVStormCannon' + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_HVStormCannon' + InstantHitDamage(DEFAULT_FIREMODE)=150 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_EMP_HVStormCannon' + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None + + FireInterval(DEFAULT_FIREMODE)=0.4 // 150 RPM // 0.8 // 75 RPM + Spread(DEFAULT_FIREMODE)=0.005 + PenetrationPower(DEFAULT_FIREMODE)=0 + FireOffset=(X=30,Y=16,Z=-8) + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HVStormCannon' + InstantHitDamage(BASH_FIREMODE)=27 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HVStormCannon.Play_WEP_HVStormCannon_Shoot_3P', FirstPersonCue=AkEvent'WW_WEP_HVStormCannon.Play_WEP_HVStormCannon_Shoot_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_Winchester.Play_WEP_SA_Winchester_Handling_DryFire' + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + AssociatedPerkClasses(0)=class'KFPerk_Sharpshooter' + + TrackingInstanceTimeDelaySeconds=0.15 + TrackingRadius=200.0 // in cm, this is added to the Radius of the Zed that's spreading + TrackingDamageValue=150 + + TrackingDamage = (0.75f, 0.5f, 0.25f) + + WeaponID=0 + + // Scope Render + // 2D scene capture + Begin Object Name=SceneCapture2DComponent0 + //TextureTarget=TextureRenderTarget2D'Wep_Mat_Lib.WEP_ScopeLense_Target' + FieldOfView=12.5 //23.0 // "1.5X" = 35.0(our real world FOV determinant)/1.5 + End Object + + ScopedSensitivityMod=8.0 //16.0 + ScopeLenseMICTemplate=MaterialInstanceConstant'WEP_1P_HVStormCannon_MAT.Wep_1stP_HVStormCannon_Lens_MIC' + ScopeMICIndex = 1 + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + + NumBloodMapMaterials=4 +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_Knife_Demolitionist.uc b/KFGameContent/Classes/KFWeap_Knife_Demolitionist.uc index 8548589..4a2eb22 100644 --- a/KFGameContent/Classes/KFWeap_Knife_Demolitionist.uc +++ b/KFGameContent/Classes/KFWeap_Knife_Demolitionist.uc @@ -20,6 +20,9 @@ defaultproperties FirstPersonMeshName="WEP_1P_Demo_Knife_MESH.Wep_1stP_Demo_Knife_Rig" AttachmentArchetypeName="WEP_Demo_Knife_ARCH.Wep_Demo_Knife_3P" + Spread(DEFAULT_FIREMODE)=1.0 + Spread(ALTFIRE_FIREMODE)=1.0 + Begin Object Name=FirstPersonMesh AnimSets(0)=AnimSet'WEP_1P_CommandoKnife_ANIM.Wep_1stP_CommKnife_Anim' End Object diff --git a/KFGameContent/Classes/KFWeap_Shotgun_DoubleBarrel.uc b/KFGameContent/Classes/KFWeap_Shotgun_DoubleBarrel.uc index 683bce7..4466e14 100644 --- a/KFGameContent/Classes/KFWeap_Shotgun_DoubleBarrel.uc +++ b/KFGameContent/Classes/KFWeap_Shotgun_DoubleBarrel.uc @@ -74,7 +74,6 @@ simulated state WeaponDoubleBarrelFiring extends WeaponSingleFiring simulated function BeginState(name PreviousStateName) { - local vector UsedKickMomentum; local KFMapInfo KFMI; Super.BeginState(PreviousStateName); @@ -84,24 +83,8 @@ simulated state WeaponDoubleBarrelFiring extends WeaponSingleFiring return; } - // Push the player back when they fire both barrels - if (Instigator != none ) - { - UsedKickMomentum.X = -DoubleBarrelKickMomentum; - - if( Instigator.Physics == PHYS_Falling ) - { - UsedKickMomentum = UsedKickMomentum >> Instigator.GetViewRotation(); - UsedKickMomentum *= FallingMomentumReduction; - } - else - { - UsedKickMomentum = UsedKickMomentum >> Instigator.Rotation; - UsedKickMomentum.Z = 0; - } - - Instigator.AddVelocity(UsedKickMomentum,Instigator.Location,none); - } + // Push the player back when they fire both barrels + ApplyKickMomentum(DoubleBarrelKickMomentum, FallingMomentumReduction); } } diff --git a/KFGameContent/Classes/KFWeap_Shotgun_ElephantGun.uc b/KFGameContent/Classes/KFWeap_Shotgun_ElephantGun.uc index 99130bf..35c23f4 100644 --- a/KFGameContent/Classes/KFWeap_Shotgun_ElephantGun.uc +++ b/KFGameContent/Classes/KFWeap_Shotgun_ElephantGun.uc @@ -87,7 +87,6 @@ simulated state WeaponQuadBarrelFiring extends WeaponSingleFiring simulated function BeginState(name PreviousStateName) { - local vector UsedKickMomentum; local KFMapInfo KFMI; Super.BeginState(PreviousStateName); @@ -96,51 +95,17 @@ simulated state WeaponQuadBarrelFiring extends WeaponSingleFiring { return; } - // Push the player back when they fire both barrels - if (Instigator != none) - { - UsedKickMomentum.X = -DoubleBarrelKickMomentum; - - if (Instigator.Physics == PHYS_Falling) - { - UsedKickMomentum = UsedKickMomentum >> Instigator.GetViewRotation(); - UsedKickMomentum *= FallingMomentumReduction; - } - else - { - UsedKickMomentum = UsedKickMomentum >> Instigator.Rotation; - UsedKickMomentum.Z = 0; - } - - Instigator.AddVelocity(UsedKickMomentum,Instigator.Location,none); - } + ApplyKickMomentum(DoubleBarrelKickMomentum, FallingMomentumReduction); } } simulated function BeginState(name PreviousStateName) { - local vector UsedKickMomentum; Super.BeginState(PreviousStateName); // Push the player back when they fire both barrels - if (Instigator != none) - { - UsedKickMomentum.X = -DoubleBarrelKickMomentum; - - if (Instigator.Physics == PHYS_Falling) - { - UsedKickMomentum = UsedKickMomentum >> Instigator.GetViewRotation(); - UsedKickMomentum *= FallingMomentumReduction; - } - else - { - UsedKickMomentum = UsedKickMomentum >> Instigator.Rotation; - UsedKickMomentum.Z = 0; - } - - Instigator.AddVelocity(UsedKickMomentum,Instigator.Location,none); - } + ApplyKickMomentum(DoubleBarrelKickMomentum, FallingMomentumReduction); } defaultproperties diff --git a/KFGameContent/Classes/KFWeap_ZedMKIII.uc b/KFGameContent/Classes/KFWeap_ZedMKIII.uc new file mode 100644 index 0000000..7bcb31b --- /dev/null +++ b/KFGameContent/Classes/KFWeap_ZedMKIII.uc @@ -0,0 +1,564 @@ +//============================================================================= +// KFWeap_ZedMKIII +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_ZedMKIII extends KFWeap_RifleBase; + +/** Number of shots required in a row before firing a rocket */ +var const byte NumBulletsBeforeRocket; +/** Minimum distance a target can be from the crosshair to be considered for lock on */ +var const float MinTargetDistFromCrosshairSQ; +/** Dot product FOV that targets need to stay within to maintain a target lock */ +var const float MaxLockMaintainFOVDotThreshold; +/** Time interval for updating radar positions */ +var const float RadarUpdateEntitiesTime; +/** Distance the radar can track enemies */ +var const float MaxRadarDistance; +/** Speed at which the radar moves (rad/sec) */ +var const float RadarSpeed; +/** */ +var const byte RadarTargetSize; + +var transient array EnemiesInRadar; +var transient byte NumShotsFired; +var transient float PrevAngle; +var transient bool bRequiresRadarClear; + +var class RadarUIClass; +var KFGFxWorld_WeaponRadar RadarUI; + +var const float MaxTargetAngle; +var transient float CosTargetAngle; + +var transient bool LastShotIsRocket; +var WeaponFireSndInfo NormalFireSound; +var WeaponFireSndInfo RocketFireSound; + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + CosTargetAngle = Cos(MaxTargetAngle * DegToRad); +} + +simulated function AltFireMode() +{ + +} + +/** +* Given an potential target TA determine if we can lock on to it. By default only allow locking on +* to pawns. +*/ +simulated function bool CanLockOnTo(Actor TA) +{ + Local KFPawn PawnTarget; + + PawnTarget = KFPawn(TA); + + // Make sure the pawn is legit, isn't dead, and isn't already at full health + if ((TA == None) || !TA.bProjTarget || TA.bDeleteMe || (PawnTarget == None) || + (TA == Instigator) || (PawnTarget.Health <= 0) || + !HasAmmo(DEFAULT_FIREMODE)) + { + return false; + } + + // Make sure and only lock onto players on the same team + return !WorldInfo.GRI.OnSameTeam(Instigator, TA); +} + +/** Finds a new lock on target */ +simulated function bool FindTarget( out KFPawn RecentlyLocked ) +{ + local KFPawn P, BestTargetLock; + local byte TeamNum; + local vector AimStart, AimDir, TargetLoc, Projection, DirToPawn, LinePoint; + local Actor HitActor; + local float PointDistSQ, Score, BestScore, TargetSizeSQ; + + TeamNum = Instigator.GetTeamNum(); + AimStart = GetSafeStartTraceLocation(); + AimDir = vector( GetAdjustedAim(AimStart) ); + BestScore = 0.f; + + foreach WorldInfo.AllPawns( class'KFPawn', P ) + { + if (!CanLockOnTo(P)) + { + continue; + } + // Want alive pawns and ones we already don't have locked + if( P != none && P.IsAliveAndWell() && P.GetTeamNum() != TeamNum ) + { + TargetLoc = GetLockedTargetLoc( P ); + Projection = TargetLoc - AimStart; + DirToPawn = Normal( Projection ); + + // Filter out pawns too far from center + + if( AimDir dot DirToPawn < CosTargetAngle ) + { + continue; + } + + // Check to make sure target isn't too far from center + PointDistToLine( TargetLoc, AimDir, AimStart, LinePoint ); + PointDistSQ = VSizeSQ( LinePoint - P.Location ); + + TargetSizeSQ = P.GetCollisionRadius() * 2.f; + TargetSizeSQ *= TargetSizeSQ; + + // Make sure it's not obstructed + HitActor = class'KFAIController'.static.ActorBlockTest(self, TargetLoc, AimStart,, true, true); + if( HitActor != none && HitActor != P ) + { + continue; + } + + // Distance from target has much more impact on target selection score + Score = VSizeSQ( Projection ) + PointDistSQ; + if( BestScore == 0.f || Score < BestScore ) + { + BestTargetLock = P; + BestScore = Score; + } + } + } + + if( BestTargetLock != none ) + { + RecentlyLocked = BestTargetLock; + + // Plays sound/FX when locking on to a new target + // PlayTargetLockOnEffects(); + + return true; + } + + RecentlyLocked = none; + + return false; +} + +/** Adjusts our destination target impact location */ +static simulated function vector GetLockedTargetLoc( Pawn P ) +{ + // Go for the chest, but just in case we don't have something with a chest bone we'll use collision and eyeheight settings + if( P.Mesh.SkeletalMesh != none && P.Mesh.bAnimTreeInitialised ) + { + if( P.Mesh.MatchRefBone('Spine2') != INDEX_NONE ) + { + return P.Mesh.GetBoneLocation( 'Spine2' ); + } + else if( P.Mesh.MatchRefBone('Spine1') != INDEX_NONE ) + { + return P.Mesh.GetBoneLocation( 'Spine1' ); + } + + return P.Mesh.GetPosition() + ((P.CylinderComponent.CollisionHeight + (P.BaseEyeHeight * 0.5f)) * vect(0,0,1)) ; + } + + // General chest area, fallback + return P.Location + ( vect(0,0,1) * P.BaseEyeHeight * 0.75f ); +} + +simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation ) +{ + if (LastShotIsRocket) + { + super.PlayFireEffects(ALTFIRE_FIREMODE, HitLocation); + + WeaponPlayFireSound(WeaponFireSnd[ALTFIRE_FIREMODE].DefaultCue, WeaponFireSnd[ALTFIRE_FIREMODE].FirstPersonCue); + + return; + } + + super.PlayFireEffects(FireModeNum, HitLocation); +} + +simulated function Projectile ProjectileFire() +{ + if (CurrentFireMode == DEFAULT_FIREMODE || CurrentFireMode == ALTFIRE_FIREMODE) + { + if (NumShotsFired >= NumBulletsBeforeRocket) + { + WeaponFireSnd[ALTFIRE_FIREMODE]=RocketFireSound; + CurrentFireMode = ALTFIRE_FIREMODE; + NumShotsFired = 0; + LastShotIsRocket = true; + } + else + { + WeaponFireSnd[ALTFIRE_FIREMODE]=NormalFireSound; + CurrentFireMode = DEFAULT_FIREMODE; + ++NumShotsFired; + LastShotIsRocket = false; + } + } + + return super.ProjectileFire(); +} + +/** Spawn projectile is called once for each rocket fired. In burst mode it will cycle through targets until it runs out */ +simulated function KFProjectile SpawnProjectile( class KFProjClass, vector RealStartLoc, vector AimDir ) +{ + local KFProj_Rocket_ZedMKIII RocketProj; + local KFPawn TargetPawn; + + if ( CurrentFireMode == ALTFIRE_FIREMODE ) + { + FindTarget(TargetPawn); + + RocketProj = KFProj_Rocket_ZedMKIII( super.SpawnProjectile( class(WeaponProjectiles[CurrentFireMode]) , RealStartLoc, AimDir) ); + + if( RocketProj != none ) + { + // We'll aim our rocket at a target here otherwise we will spawn a dumbfire rocket at the end of the function + if ( TargetPawn != none) + { + //Seek to new target, then remove it + RocketProj.SetLockedTarget( TargetPawn ); + } + } + + // Resetting the firemode to default. + CurrentFireMode = DEFAULT_FIREMODE; + + return RocketProj; + } + + return super.SpawnProjectile( KFProjClass, RealStartLoc, AimDir ); +} + +/********************************************************************************************* + * state WeaponFiring + * This is the default Firing State. It's performed on both the client and the server. + *********************************************************************************************/ +simulated state WeaponFiring +{ + // Reset num shots fired when stop shooting + simulated event EndState( Name NextStateName) + { + super.EndState(NextStateName); + + NumShotsFired = 0; + } +} + +/********************************************************************************************* + * Radar implementation + *********************************************************************************************/ + +simulated state WeaponEquipping +{ + simulated function BeginState(Name PreviousStateName) + { + super.BeginState(PreviousStateName); + + PrevAngle = 0.0f; + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + StartRadar(); + } + } +} + +simulated state WeaponPuttingDown +{ + simulated function BeginState(Name PreviousStateName) + { + super.BeginState(PreviousStateName); + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + StopRadar(); + } + } +} + +simulated function StartRadar() +{ + SetTimer(RadarUpdateEntitiesTime, true, nameof(UpdateRadarEntities)); +} + +simulated function StopRadar() +{ + ClearTimer(nameof(UpdateRadarEntities)); + EnemiesInRadar.Length = 0; +} + +simulated function UpdateRadarEntities() +{ + local KFPawn_Monster KFPM; + local int RadarIndex; + local bool bIsAlive; + + bIsAlive = false; + + // Get nearby enemies + foreach CollidingActors(class'KFPawn_Monster', KFPM, MaxRadarDistance, Location, true) + { + RadarIndex = FindEnemyTrackedByRadar(KFPM); + bIsAlive = KFPM.IsAliveAndWell(); + + if (RadarIndex == INDEX_NONE) + { + if (bIsAlive) + { + EnemiesInRadar.AddItem(KFPM); + } + } + else if(!bIsAlive) + { + EnemiesInRadar.RemoveItem(KFPM); + } + } +} + +simulated function int FindEnemyTrackedByRadar(KFPawn_Monster KFPM) +{ + local int i; + + for (i = 0; i < EnemiesInRadar.Length; ++i) + { + if (KFPM == EnemiesInRadar[i] ) + { + return i; + } + } + + return INDEX_NONE; +} + +simulated function Tick(float Delta) +{ + local float DistanceSqrd; + local vector Distance, ScreenDirection, UILocation; + local rotator ViewRotation; + local int i; + local array RadarElements; + + super.Tick(Delta); + + if (RadarUI != none) + { + if (bRequiresRadarClear) + { + RadarUI.Clear(); + } + + if (EnemiesInRadar.Length == 0) + { + if (bRequiresRadarClear) + { + bRequiresRadarClear = false; + } + + return; + } + + ViewRotation = Rotation; + ViewRotation.Yaw *= -1; + ViewRotation.Pitch = 0; + ViewRotation.Roll = 0; + + RadarElements.Length = 0; + + for (i = EnemiesInRadar.Length - 1; i >= 0; --i) + { + if (!EnemiesInRadar[i].IsAliveAndWell()) + { + EnemiesInRadar.Remove(i, 1); + continue; + } + + Distance = EnemiesInRadar[i].Location - Location; + DistanceSqrd = VSizeSQ(Distance); + + if (DistanceSqrd > MaxRadarDistance * MaxRadarDistance) + { + EnemiesInRadar.Remove(i, 1); + continue; + } + + Distance.Z = 0; + ScreenDirection = Distance >> ViewRotation; + + UILocation.X = ScreenDirection.Y / MaxRadarDistance; + UILocation.Y = ScreenDirection.X / MaxRadarDistance; + + RadarElements.AddItem(UILocation); + } + + if (RadarElements.length > 0) + { + RadarUI.AddRadarElements(RadarElements); + bRequiresRadarClear = true; + } + } +} + +/** Radar UI */ +reliable client function ClientWeaponSet(bool bOptionalSet, optional bool bDoNotActivate) +{ + local KFInventoryManager KFIM; + + super.ClientWeaponSet(bOptionalSet, bDoNotActivate); + + if (RadarUI == none && RadarUIClass != none) + { + KFIM = KFInventoryManager(InvManager); + if (KFIM != none) + { + //Create the screen's UI piece + RadarUI = KFGFxWorld_WeaponRadar(KFIM.GetRadarUIMovie(RadarUIClass)); + } + } +} + +function ItemRemovedFromInvManager() +{ + local KFInventoryManager KFIM; + + Super.ItemRemovedFromInvManager(); + + if (RadarUI != none) + { + KFIM = KFInventoryManager(InvManager); + if (KFIM != none) + { + //Create the screen's UI piece + KFIM.RemoveRadarUIMovie(RadarUI.class); + + RadarUI.Close(); + RadarUI = none; + } + } +} + +/** */ + +defaultproperties +{ + // FOV + MeshFOV=70 + MeshIronSightFOV=30 //52 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=85 + DOF_FG_MaxNearBlurSize=2.5 + + // Zooming/Position + IronSightPosition=(X=14,Y=0.05,Z=-0.5) // x=-8,Y=0.01,Z=0.85 + PlayerViewOffset=(X=22,Y=12,Z=-2.5) + + // Content + PackageKey="ZedMKIII" + FirstPersonMeshName="WEP_1P_ZEDMKIII_MESH.Wep_1stP_ZEDMKIII_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_ZEDMKIII_ANIM.Wep_1stP_ZEDMKIII_Anim" + PickupMeshName="WEP_3P_ZEDMKIII_MESH.Wep_3rdP_ZEDMKIII_Pickup" + AttachmentArchetypeName = "WEP_ZEDMKIII_ARCH.Wep_ZEDMKIII_3P" + MuzzleFlashTemplateName="WEP_ZEDMKIII_ARCH.Wep_ZEDMKIII_MuzzleFlash" + + // Ammo + MagazineCapacity[0]=100 + SpareAmmoCapacity[0]=400 + InitialSpareMags[0]=1 + bCanBeReloaded=true + bReloadFromMagazine=true + + bCanRefillSecondaryAmmo=0 + + // Recoil + maxRecoilPitch=125 + minRecoilPitch=100 + maxRecoilYaw=120 + minRecoilYaw=-100 + RecoilRate=0.085 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=75 + RecoilISMinYawLimit=65460 + RecoilISMaxPitchLimit=375 + RecoilISMinPitchLimit=65460 + IronSightMeshFOVCompensationScale=4.0 + + // Inventory + InventorySize=9 + GroupPriority=125 + WeaponSelectTexture=Texture2D'wep_ui_zedmkiii_tex.UI_WeaponSelect_ZEDMKIII' + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_ZedMKIII' + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Microwave_ZedMKIII' + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Rocket_ZedMKIII' + InstantHitDamage(ALTFIRE_FIREMODE)=100 + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_ZedMKIII_Rocket' + Spread(ALTFIRE_FIREMODE)=0.025 + AmmoCost(ALTFIRE_FIREMODE)=1 + + FireInterval(DEFAULT_FIREMODE)=0.15 // 400 RPM //0.12 // 500 RPM //+0.1 // 600 RPM + Spread(DEFAULT_FIREMODE)=0.0085 + PenetrationPower(DEFAULT_FIREMODE)=4.0 + InstantHitDamage(DEFAULT_FIREMODE)=50.0 + FireOffset=(X=30,Y=8,Z=-15) + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_ZedMKIII' + InstantHitDamage(BASH_FIREMODE)=27 + + // Fire Effects + NormalFireSound=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Single_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Single_1P') + RocketFireSound=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Rocket_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Rocket_1P') + + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_LP_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_LP_1P') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Single_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Single_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Handling_DryFire' + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Handling_DryFire' + + // Advanced (High RPM) Fire Effects + bLoopingFireAnim(DEFAULT_FIREMODE)=true + bLoopingFireSnd(DEFAULT_FIREMODE)=true + WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_LP_End_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_LP_End_1P') + SingleFireSoundIndex=ALTFIRE_FIREMODE + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + AssociatedPerkClasses(0)=class'KFPerk_Demolitionist' + + NumBulletsBeforeRocket=6 + NumShotsFired=0 + MinTargetDistFromCrosshairSQ=2500.0f + MaxLockMaintainFOVDotThreshold=0.36f + + RadarUpdateEntitiesTime=0.1f + MaxRadarDistance=2000 + RadarSpeed=2.0f + RadarTargetSize=10.0f + + RadarUIClass=class'KFGFxWorld_WeaponRadar' + + NumBloodMapMaterials=2 + bRequiresRadarClear=false + + MaxTargetAngle=10 + + LastShotIsRocket=false +}