diff --git a/Engine/Classes/Controller.uc b/Engine/Classes/Controller.uc index cfbd6ee..e45a844 100644 --- a/Engine/Classes/Controller.uc +++ b/Engine/Classes/Controller.uc @@ -1052,12 +1052,12 @@ function ReceiveProjectileWarning(Projectile Proj); * ===================================================== */ -exec function SwitchToBestWeapon(optional bool bForceNewWeapon) +exec function SwitchToBestWeapon(optional bool bForceNewWeapon, optional bool check_9mm_logic = false) { if ( Pawn == None || Pawn.InvManager == None ) return; - Pawn.InvManager.SwitchToBestWeapon(bForceNewWeapon); + Pawn.InvManager.SwitchToBestWeapon(bForceNewWeapon, check_9mm_logic); } /* epic =============================================== @@ -1070,7 +1070,11 @@ exec function SwitchToBestWeapon(optional bool bForceNewWeapon) */ reliable client function ClientSwitchToBestWeapon(optional bool bForceNewWeapon) { - SwitchToBestWeapon(bForceNewWeapon); + local bool check_9mm_logic; + + check_9mm_logic = true; + + SwitchToBestWeapon(bForceNewWeapon, check_9mm_logic); } /* epic =============================================== diff --git a/Engine/Classes/InventoryManager.uc b/Engine/Classes/InventoryManager.uc index bd0841a..21b16c1 100644 --- a/Engine/Classes/InventoryManager.uc +++ b/Engine/Classes/InventoryManager.uc @@ -386,7 +386,7 @@ simulated function float GetWeaponRatingFor( Weapon W ) /** * returns the best weapon for this Pawn in loadout */ -simulated function Weapon GetBestWeapon( optional bool bForceADifferentWeapon ) +simulated function Weapon GetBestWeapon( optional bool bForceADifferentWeapon, optional bool allow9mm ) { local Weapon W, BestWeapon; local float Rating, BestRating; @@ -419,7 +419,7 @@ simulated function Weapon GetBestWeapon( optional bool bForceADifferentWeapon ) * Switch to best weapon available in loadout * Network: LocalPlayer */ -simulated function SwitchToBestWeapon( optional bool bForceADifferentWeapon ) +simulated function SwitchToBestWeapon( optional bool bForceADifferentWeapon, optional bool check_9mm_logic = false ) { local Weapon BestWeapon; diff --git a/Engine/Classes/SkeletalMeshComponent.uc b/Engine/Classes/SkeletalMeshComponent.uc index a3f3971..3d43e95 100644 --- a/Engine/Classes/SkeletalMeshComponent.uc +++ b/Engine/Classes/SkeletalMeshComponent.uc @@ -1385,11 +1385,13 @@ simulated final native function ShowMaterialSection(int MaterialID, bool bShow, * @param StartTime (optional) What time to start the animation at * @param bPlayBackwards (optional) Play this animation backwards */ -function PlayAnim(name AnimName, optional float Duration, optional bool bLoop, optional bool bRestartIfAlreadyPlaying = true, optional float StartTime=0.0f, optional bool bPlayBackwards=false) +function bool PlayAnim(name AnimName, optional float Duration, optional bool bLoop, optional bool bRestartIfAlreadyPlaying = true, optional float StartTime=0.0f, optional bool bPlayBackwards=false) { local AnimNodeSequence AnimNode; local float DesiredRate; + local bool bIsAnimPlayed; + bIsAnimPlayed = false; AnimNode = AnimNodeSequence(Animations); if (AnimNode == None && Animations.IsA('AnimTree')) { @@ -1408,6 +1410,8 @@ function PlayAnim(name AnimName, optional float Duration, optional bool bLoop, o if (bRestartIfAlreadyPlaying || !AnimNode.bPlaying) { AnimNode.PlayAnim(bLoop, DesiredRate, StartTime); + + bIsAnimPlayed = true; } else { @@ -1423,9 +1427,13 @@ function PlayAnim(name AnimName, optional float Duration, optional bool bLoop, o DesiredRate = (Duration > 0.0) ? (AnimNode.AnimSeq.SequenceLength / (Duration * AnimNode.AnimSeq.RateScale)) : 1.0; DesiredRate = (bPlayBackwards) ? -DesiredRate : DesiredRate; AnimNode.PlayAnim(bLoop, DesiredRate, StartTime); + + bIsAnimPlayed = true; } } } + + return bIsAnimPlayed; } /** simple generic case animation stopper diff --git a/KFGame/Classes/EphemeralMatchStats.uc b/KFGame/Classes/EphemeralMatchStats.uc index a61a835..7009e3a 100644 --- a/KFGame/Classes/EphemeralMatchStats.uc +++ b/KFGame/Classes/EphemeralMatchStats.uc @@ -42,7 +42,8 @@ struct AARAward var array TeamAwardList; enum ETeamAwards -{ +{ + ETA_WeaponMaster, ETA_ZedStomper, ETA_MedicineMaster, ETA_ZedSlayer, @@ -1103,6 +1104,9 @@ static function GetTeamAward(ETeamAwards AwardIndex, out AARAward TempAwardObjec case ETA_ZedStomper: Give_ZedStomper(TempAwardObject, KFPCArray); break; + case ETA_WeaponMaster: + GiveWeaponMaster(TempAwardObject, KFPCArray); + break; } } @@ -1416,6 +1420,27 @@ static function Give_ZedStomper(out AARAward outAward, const out Array KFPCArray) +{ + local int i; + local KFPlayerController_WeeklySurvival KFPC_WS; + + for(i = 0; i < KFPCArray.Length; i++) + { + KFPC_WS = KFPlayerController_WeeklySurvival(KFPCArray[i]); + if (KFPC_WS != none) + { + if (KFPC_WS.GunGameData.GiveWeaponMaster) + { + outAward.PRI = KFPCArray[i].PlayerReplicationInfo; + outAward.DisplayValue = 1; + `log(KFPCArray[i].PlayerReplicationInfo.PlayerName @"Weapon Master", class'EphemeralMatchStats'.default.bShowMatchStatsLogging); + return; + } + } + } +} + function ReceiveAwardInfo(byte AwardID, PlayerReplicationInfo PRI, int Value) { TeamAwardList[AwardID].PRI = PRI; @@ -1435,6 +1460,7 @@ DefaultProperties TeamAwardList(ETA_HeadPopper)=(TitleIdentifier="HeadPopper",ValueIdentifier="HeadPopperValue",IconPath="UI_Award_Team.UI_Award_Team-Headshots") TeamAwardList(ETA_Dominator)=(TitleIdentifier="Dominator",ValueIdentifier="DominatorValue",IconPath="UI_Award_Team.UI_Award_Team-BossKO") TeamAwardList(ETA_ZedStomper)=(TitleIdentifier="ZedStomper",ValueIdentifier="ZedStomperValue",IconPath="UI_Award_Team.UI_Award_Team-ZedStomper") + TeamAwardList(ETA_WeaponMaster)=(TitleIdentifier="WeaponMaster",ValueIdentifier="WeaponMasterValue",IconPath="UI_Award_Team.UI_Award_Team_GunMode") //zed awards TeamAwardList(ETA_Carnage)=(TitleIdentifier="Carnage",ValueIdentifier="CarnageValue",IconPath="ui_award_zeds.UI_Award_ZED_RawDmg") TeamAwardList(ETA_Closer)=(TitleIdentifier="Closer",ValueIdentifier="CloserValue",IconPath="ui_award_zeds.UI_Award_ZED_Kills") diff --git a/KFGame/Classes/KFAfflictionAdvanced.uc b/KFGame/Classes/KFAfflictionAdvanced.uc index 0357703..80604d9 100644 --- a/KFGame/Classes/KFAfflictionAdvanced.uc +++ b/KFGame/Classes/KFAfflictionAdvanced.uc @@ -25,7 +25,7 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) } /** */ -function Activate() +function Activate(optional class DamageType = none) { if ( !bIsActive ) { diff --git a/KFGame/Classes/KFAfflictionBase.uc b/KFGame/Classes/KFAfflictionBase.uc index a6ef173..ea8d47a 100644 --- a/KFGame/Classes/KFAfflictionBase.uc +++ b/KFGame/Classes/KFAfflictionBase.uc @@ -57,7 +57,7 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) } /** */ -function Accrue(float InPower) +function Accrue(float InPower, optional class DamageType = none) { // total immunity during cooldown if ( LastActivationTime > 0 && `TimeSinceEx(PawnOwner, LastActivationTime) < Cooldown ) @@ -80,14 +80,14 @@ function Accrue(float InPower) CurrentStrength = fClamp(CurrentStrength + InPower, InPower, INCAP_THRESHOLD); if ( CurrentStrength >= INCAP_THRESHOLD ) { - Activate(); + Activate(DamageType); } `log(Class.Name@"Added="$InPower@"NewStrength="$CurrentStrength, bDebug); } /** */ -function Activate() +function Activate(optional class DamageType = none) { if ( SpecialMove != SM_None ) { @@ -159,6 +159,11 @@ function float GetAttackSpeedModifier() return 1.f; } +function float GetDamageTakenModifier() +{ + return 0.f; +} + defaultproperties { //bDebug=true diff --git a/KFGame/Classes/KFAfflictionManager.uc b/KFGame/Classes/KFAfflictionManager.uc index b1b559c..5aecf8f 100644 --- a/KFGame/Classes/KFAfflictionManager.uc +++ b/KFGame/Classes/KFAfflictionManager.uc @@ -83,6 +83,8 @@ enum EAfflictionType AF_Freeze, AF_Microwave, AF_Bleed, + AF_BigHead, + AF_Shrink, AF_Custom1, AF_Custom2, @@ -258,7 +260,7 @@ protected function ProcessSpecialMoveAfflictions(KFPerk InstigatorPerk, vector H // increment affliction power if (KnockdownPower > 0 && CanDoSpecialmove(SM_Knockdown)) { - AccrueAffliction(AF_Knockdown, KnockdownPower, BodyPart, InstigatorPerk); + AccrueAffliction(AF_Knockdown, KnockdownPower, BodyPart, InstigatorPerk, DamageType); } if (StunPower > 0 && CanDoSpecialmove(SM_Stunned)) { @@ -342,26 +344,32 @@ protected function ProcessHitReactionAfflictions(KFPerk InstigatorPerk, class DamageType, Actor DamageCauser) { local KFWeapon DamageWeapon; - local float BurnPower, EMPPower, PoisonPower, MicrowavePower, BleedPower; + local float BurnPower, EMPPower, PoisonPower, MicrowavePower, BleedPower, BigHeadPower, ShrinkPower; local KFInterface_DamageCauser KFDmgCauser; // Get upgraded affliction power DamageWeapon = class'KFPerk'.static.GetWeaponFromDamageCauser(DamageCauser); if (DamageWeapon != none) { - BurnPower = DamageWeapon.GetUpgradedAfflictionPower(AF_FirePanic, DamageType.default.BurnPower); - EMPPower = DamageWeapon.GetUpgradedAfflictionPower(AF_EMP, DamageType.default.EMPPower); - PoisonPower = DamageWeapon.GetUpgradedAfflictionPower(AF_Poison, DamageType.default.PoisonPower); + BurnPower = DamageWeapon.GetUpgradedAfflictionPower(AF_FirePanic, DamageType.default.BurnPower); + EMPPower = DamageWeapon.GetUpgradedAfflictionPower(AF_EMP, DamageType.default.EMPPower); + PoisonPower = DamageWeapon.GetUpgradedAfflictionPower(AF_Poison, DamageType.default.PoisonPower); MicrowavePower = DamageWeapon.GetUpgradedAfflictionPower(AF_Microwave, DamageType.default.MicrowavePower); - BleedPower = DamageWeapon.GetUpgradedAfflictionPower(AF_Bleed, DamageType.default.BleedPower); + BleedPower = DamageWeapon.GetUpgradedAfflictionPower(AF_Bleed, DamageType.default.BleedPower); + ShrinkPower = DamageWeapon.GetUpgradedAfflictionPower(AF_Shrink, DamageType.default.ShrinkPower); + + // No modifiers applied to Big Heads + BigHeadPower = DamageType.default.BigHeadPower; } else { - BurnPower = DamageType.default.BurnPower; - EMPPower = DamageType.default.EMPPower; - PoisonPower = DamageType.default.PoisonPower; + BurnPower = DamageType.default.BurnPower; + EMPPower = DamageType.default.EMPPower; + PoisonPower = DamageType.default.PoisonPower; MicrowavePower = DamageType.default.MicrowavePower; - BleedPower = DamageType.default.BleedPower; + BleedPower = DamageType.default.BleedPower; + BigHeadPower = DamageType.default.BigHeadPower; + ShrinkPower = DamageType.default.ShrinkPower; } KFDmgCauser = KFInterface_DamageCauser(DamageCauser); @@ -397,7 +405,7 @@ protected function ProcessEffectBasedAfflictions(KFPerk InstigatorPerk, class 0) { - AccrueAffliction(AF_FirePanic, BurnPower); + AccrueAffliction(AF_FirePanic, BurnPower, , InstigatorPerk, DamageType); } if (PoisonPower > 0 || DamageType.static.AlwaysPoisons()) { @@ -411,6 +419,14 @@ protected function ProcessEffectBasedAfflictions(KFPerk InstigatorPerk, class 0) + { + AccrueAffliction(AF_BigHead, BigHeadPower); + } + if (ShrinkPower > 0) + { + AccrueAffliction(AF_Shrink, ShrinkPower,,InstigatorPerk); + } } } @@ -422,7 +438,7 @@ protected function ProcessEffectBasedAfflictions(KFPerk InstigatorPerk, class DamageType = none) { if ( InPower <= 0 || Type >= IncapSettings.Length ) { @@ -453,7 +469,7 @@ function AccrueAffliction(EAfflictionType Type, float InPower, optional EHitZone if ( InPower > 0 ) { - Afflictions[Type].Accrue(InPower); + Afflictions[Type].Accrue(InPower, DamageType); } } @@ -626,6 +642,11 @@ function float GetAfflictionDamageModifier() { if (Afflictions[i] != none) { + if (i == AF_BigHead) + { + continue; + } + DamageModifier += Afflictions[i].GetDamageModifier(); } } @@ -633,6 +654,23 @@ function float GetAfflictionDamageModifier() return DamageModifier; } +function float GetAfflictionDamageTakenModifier() +{ + local float DamageModifier; + local int i; + + DamageModifier = 1.f; + for (i = 0; i < Afflictions.Length; ++i) + { + if (Afflictions[i] != none) + { + DamageModifier += Afflictions[i].GetDamageTakenModifier(); + } + } + + return DamageModifier; +} + /** Accessor to get known affliction speed modifier - Multiplicative from all mods */ function float GetAfflictionSpeedModifier() { @@ -668,6 +706,25 @@ function float GetAfflictionAttackSpeedModifier() return SpeedModifier; } + +function float GetBigHeadAfflictionDamageModifier() +{ + local float DamageModifier; + + DamageModifier = 0.f; + + if (Afflictions.Length > AF_BigHead) + { + if (Afflictions[AF_BigHead] != none) + { + DamageModifier = Afflictions[AF_BigHead].GetDamageModifier(); + } + } + + return DamageModifier; +} + + /** Turns off all affliction sounds / effects */ simulated function Shutdown() { @@ -745,4 +802,6 @@ defaultproperties AfflictionClasses(AF_Knockdown)=class'KFAffliction_Knockdown' AfflictionClasses(AF_Snare)=class'KFAffliction_Snare' AfflictionClasses(AF_Bleed)=class'KFAffliction_Bleed' + AfflictionClasses(AF_BigHead)=class'KFAffliction_BigHead' + AfflictionClasses(AF_Shrink)=class'KFAffliction_Shrink' } diff --git a/KFGame/Classes/KFAffliction_BigHead.uc b/KFGame/Classes/KFAffliction_BigHead.uc new file mode 100644 index 0000000..d509277 --- /dev/null +++ b/KFGame/Classes/KFAffliction_BigHead.uc @@ -0,0 +1,136 @@ +//============================================================================= +// KFAffliction_BigHead +//============================================================================= +// Affliction for cranial popper +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFAffliction_BigHead extends KFAfflictionAdvanced; + +var() float EffectAppliedByStack; +var() float ApplyEffectVel; +var() float RemoveEffectVel; +var() array StackDamageExplosion; +var() byte MaxStack; +var() byte MaxStackBobbleHead; + +var transient byte CurrentStack; +var transient float CurrentEffectApplied; +var transient bool bIsBobbleHeadMode; +var transient bool bIsShrunkenHeads; +var transient byte CurrentMaxStack; + +function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) +{ + local KFGameReplicationInfo KFGRI; + local int EndlessWeeklyWaveIdx; + + Super.Init(P, Type, InstigatorPerk); + + KFGRI = KFGameReplicationInfo(P.WorldInfo.GRI); + + if (KFGRI != none) + { + bIsBobbleHeadMode = (KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 3) || (KFGRI.IsWeeklyWave(EndlessWeeklyWaveIdx) && EndlessWeeklyWaveIdx == 3); + bIsShrunkenHeads = KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 15; + + if (bIsBobbleHeadMode) + { + MaxStack = MaxStackBobbleHead; + } + } + + CurrentMaxStack = bIsBobbleHeadMode ? MaxStackBobbleHead : MaxStack; +} + +function Activate(optional class DamageType = none) +{ + if (CurrentStack < CurrentMaxStack) + { + Super.Activate(); + + if (!bIsBobbleHeadMode && !bIsShrunkenHeads) + { + ++CurrentStack; + } + } +} + +event Tick(float DeltaTime) +{ + local float Target; + + if ( PawnOwner.bPlayedDeath ) + { + CurrentStrength= 0.0f; + super.Deactivate(); + return; + } + + if (bIsBobbleHeadMode || bIsShrunkenHeads) + { + return; + } + + Target = CurrentStack * EffectAppliedByStack; + + if (CurrentEffectApplied != Target) + { + if (CurrentEffectApplied < Target) + { + CurrentEffectApplied = FMin(CurrentEffectApplied + DeltaTime * ApplyEffectVel, Target); + } + else + { + CurrentEffectApplied = FMax(CurrentEffectApplied - DeltaTime * ApplyEffectVel, 0.0f); + } + + PawnOwner.IntendedHeadScale = 1 + CurrentEffectApplied; + PawnOwner.SetHeadScale(1 + CurrentEffectApplied, PawnOwner.CurrentHeadScale); + PawnOwner.bNetDirty = true; + } + + if (CurrentStrength <= 0.0f) + { + CurrentStack = 0; + CurrentStrength = 0.1f; + + if (CurrentEffectApplied <= 0.0f) + { + CurrentStrength = 0.0f; + super.Deactivate(); + } + } +} + +/** Accessor to get known affliction Damage modifier */ +function float GetDamageModifier() +{ + local float damage; + + damage = bIsBobbleHeadMode ? StackDamageExplosion[MaxStackBobbleHead] : StackDamageExplosion[CurrentStack]; + + CurrentStack = 0; + + return damage; +} + +defaultproperties +{ + DissipationRate=10 + bNeedsTick=true + + MaxStack=3 + MaxStackBobbleHead=1 + EffectAppliedByStack=0.3f + + ApplyEffectVel= 1.0f // % per second + RemoveEffectVel= 1.0f // % per second + + CurrentStack=0 + CurrentEffectApplied=0.0f + + StackDamageExplosion={(0.0f, 150.0f, 300.0f, 500.0f, 0, 0)}; + +} diff --git a/KFGame/Classes/KFAffliction_Bleed.uc b/KFGame/Classes/KFAffliction_Bleed.uc index e865088..5ac3fef 100644 --- a/KFGame/Classes/KFAffliction_Bleed.uc +++ b/KFGame/Classes/KFAffliction_Bleed.uc @@ -115,7 +115,7 @@ function float GetAttackSpeedModifier() return 1.f; } -function Accrue(float InPower) +function Accrue(float InPower, optional class DamageType = none) { super.Accrue(InPower); if (PawnOwner != none) diff --git a/KFGame/Classes/KFAffliction_EMP.uc b/KFGame/Classes/KFAffliction_EMP.uc index c87bd06..790d2f3 100644 --- a/KFGame/Classes/KFAffliction_EMP.uc +++ b/KFGame/Classes/KFAffliction_EMP.uc @@ -25,7 +25,7 @@ var protected AkEvent OnEMPSound; /** Sound to play when this pawn stops being EMP'd */ var protected AkEvent OnEMPEndSound; -function Activate() +function Activate(optional class DamageType = none) { Super.Activate(); SetEMPPanicked(true); diff --git a/KFGame/Classes/KFAffliction_EMPDisrupt.uc b/KFGame/Classes/KFAffliction_EMPDisrupt.uc index 9d7de2a..4b8901a 100644 --- a/KFGame/Classes/KFAffliction_EMPDisrupt.uc +++ b/KFGame/Classes/KFAffliction_EMPDisrupt.uc @@ -20,7 +20,7 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) DisruptCooldown = P.IncapSettings[Type].ChildAfflictionCooldown; } -function Accrue(float InPower) +function Accrue(float InPower, optional class DamageType = none) { Super.Accrue(InPower); diff --git a/KFGame/Classes/KFAffliction_Fire.uc b/KFGame/Classes/KFAffliction_Fire.uc index 9a4fa1c..76499ee 100644 --- a/KFGame/Classes/KFAffliction_Fire.uc +++ b/KFGame/Classes/KFAffliction_Fire.uc @@ -28,6 +28,8 @@ var protected AkEvent OnFireSound; /** Sound to play when this pawn stops being on fire */ var protected AkEvent OnFireEndSound; +var transient KFPlayerController Instigator; + function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) { Super.Init(P, Type, InstigatorPerk); @@ -35,9 +37,14 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) // copy over some settings from the affliction handler that we'll need FireFullyCharredDuration = P.AfflictionHandler.FireFullyCharredDuration; FireCharPercentThreshhold = P.AfflictionHandler.FireCharPercentThreshhold; + + if (InstigatorPerk != none) + { + Instigator = InstigatorPerk.OwnerPC; + } } -function Activate() +function Activate(optional class DamageType = none) { // fire can accrue after death, but cannot trigger panic if ( !PawnOwner.bPlayedDeath ) diff --git a/KFGame/Classes/KFAffliction_HeavyRecovery.uc b/KFGame/Classes/KFAffliction_HeavyRecovery.uc index 1f08d47..84bdae7 100644 --- a/KFGame/Classes/KFAffliction_HeavyRecovery.uc +++ b/KFGame/Classes/KFAffliction_HeavyRecovery.uc @@ -9,7 +9,7 @@ class KFAffliction_HeavyRecovery extends KFAfflictionBase; /** */ -function Activate() +function Activate(optional class DamageType = none) { // Attempt to interrupt the special move if( PawnOwner.SpecialMove != SM_None ) diff --git a/KFGame/Classes/KFAffliction_Knockdown.uc b/KFGame/Classes/KFAffliction_Knockdown.uc index 68fa2b9..546c402 100644 --- a/KFGame/Classes/KFAffliction_Knockdown.uc +++ b/KFGame/Classes/KFAffliction_Knockdown.uc @@ -9,9 +9,9 @@ class KFAffliction_Knockdown extends KFAfflictionBase; /** */ -function Activate() +function Activate(optional class DamageType = none) { - ActivateKnockdown(PawnOwner.HitFxInfo.DamageType, + ActivateKnockdown(DamageType, PawnOwner.HitFxInfo.HitLocation, PawnOwner.DecodeUnitVector( PawnOwner.HitFxInfo.EncodedHitDirection ), PawnOwner.HitFxInfo.HitBoneIndex); diff --git a/KFGame/Classes/KFAffliction_MediumRecovery.uc b/KFGame/Classes/KFAffliction_MediumRecovery.uc index dff51c8..b6fe5ea 100644 --- a/KFGame/Classes/KFAffliction_MediumRecovery.uc +++ b/KFGame/Classes/KFAffliction_MediumRecovery.uc @@ -9,7 +9,7 @@ class KFAffliction_MediumRecovery extends KFAfflictionBase; /** */ -function Activate() +function Activate(optional class DamageType = none) { // Attempt to interrupt the special move if( PawnOwner.SpecialMove != SM_None ) diff --git a/KFGame/Classes/KFAffliction_Microwave.uc b/KFGame/Classes/KFAffliction_Microwave.uc index 53b706b..aabe195 100644 --- a/KFGame/Classes/KFAffliction_Microwave.uc +++ b/KFGame/Classes/KFAffliction_Microwave.uc @@ -30,7 +30,7 @@ var protected AkEvent OnSteamSound; /** Sound to play when this pawn stops being on fire */ var protected AkEvent OnSteamEndSound; -function Activate() +function Activate(optional class DamageType = none) { Super.Activate(); SetMicrowavePanicked(true); diff --git a/KFGame/Classes/KFAffliction_Poison.uc b/KFGame/Classes/KFAffliction_Poison.uc index 7f7e2ad..36c910b 100644 --- a/KFGame/Classes/KFAffliction_Poison.uc +++ b/KFGame/Classes/KFAffliction_Poison.uc @@ -8,7 +8,7 @@ //============================================================================= class KFAffliction_Poison extends KFAfflictionAdvanced; -function Activate() +function Activate(optional class DamageType = none) { Super.Activate(); SetPoisoned(true); diff --git a/KFGame/Classes/KFAffliction_Shrink.uc b/KFGame/Classes/KFAffliction_Shrink.uc new file mode 100644 index 0000000..9c91f3e --- /dev/null +++ b/KFGame/Classes/KFAffliction_Shrink.uc @@ -0,0 +1,182 @@ +//============================================================================= +// KFAffliction_Shrink +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFAffliction_Shrink extends KFAfflictionAdvanced; + +var() float ScaleOnMaxEffect; +var() float EffectAppliedByStack; +var() float MaxEffectDamageValue; +var() float ApplyEffectVel; +var() float RemoveEffectVel; +var() float StackDamageModifier; +var() byte MaxEffect; + +var transient float CurrentEffect; +var transient float CurrentEffectApplied; +var transient KFPlayerController Instigator; +var transient bool bIsTinyTerrorMode; + +var transient bool AlreadyCached; +var transient float CachedBodyScaleChangePerSecond; +var transient float CachedBodyScale; + +function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) +{ + local KFGameReplicationInfo KFGRI; + + Super.Init(P, Type, InstigatorPerk); + + if (InstigatorPerk != none) + { + Instigator = InstigatorPerk.OwnerPC; + } + + if (AlreadyCached == false) + { + // (see KFPawn_ZedMatriarch.uc) + // There are some systems directly calling Init again without letting the Affliction Manager know + // That is a hack, but we can't improve it at this stage, so catch that here only once + AlreadyCached = true; + + CachedBodyScaleChangePerSecond = P.BodyScaleChangePerSecond; + CachedBodyScale = P.Mesh.Scale; + } + + KFGRI = KFGameReplicationInfo(P.WorldInfo.GRI); + if (KFGRI != none && KFGRI.bIsWeeklyMode) + { + bIsTinyTerrorMode = KFGRI.CurrentWeeklyIndex == 2; + } +} + +function Activate(optional class DamageType = none) +{ + local float StackModifier; + + if (bIsTinyTerrorMode) + { + return; + } + + if (CurrentEffect < MaxEffect) + { + Super.Activate(); + + StackModifier = 1.0f; + + if (KFPawn_Monster(PawnOwner) != none) + { + StackModifier = KFPawn_Monster(PawnOwner).ShrinkEffectModifier; + } + + // Allow body to shrink visually faster + if (StackModifier >= 1.0f) + { + PawnOwner.BodyScaleChangePerSecond = CachedBodyScaleChangePerSecond * StackModifier; + PawnOwner.bNetDirty = true; + } + + CurrentEffect += EffectAppliedByStack * StackModifier; + } + + if (CurrentEffectApplied >= MaxEffect) + { + CurrentEffectApplied = MaxEffect; + + KillOwner(); + } +} + +event Tick(float DeltaTime) +{ + if ( PawnOwner.bPlayedDeath ) + { + CurrentStrength= 0.0f; + super.Deactivate(); + return; + } + + if (CurrentEffectApplied != CurrentEffect) + { + if (CurrentEffectApplied < CurrentEffect) + { + CurrentEffectApplied = FMin(CurrentEffectApplied + DeltaTime * ApplyEffectVel * KFPawn_Monster(PawnOwner).ShrinkEffectModifier, CurrentEffect); + } + else + { + CurrentEffectApplied = CurrentEffectApplied - DeltaTime * RemoveEffectVel; + } + + CurrentEffectApplied = FMax(CurrentEffectApplied, 0.f); + CurrentEffectApplied = FMin(CurrentEffectApplied, MaxEffect); + + // It goes from the Cached Body Scale to CachedBodyScale - 0.5 at 100% affliction + PawnOwner.IntendedBodyScale = CachedBodyScale - ((CurrentEffectApplied/MaxEffect) * (1.0f - ScaleOnMaxEffect)); + } + + if (CurrentStrength <= 0.0f) + { + CurrentEffect = 0; + CurrentStrength = 0.1f; + + if (CurrentEffectApplied <= 0.0f) + { + CurrentStrength = 0.0f; + PawnOwner.BodyScaleChangePerSecond = CachedBodyScaleChangePerSecond; + PawnOwner.bNetDirty = true; + super.Deactivate(); + } + } +} + +function float GetDamageTakenModifier() +{ + return MaxEffectDamageValue * (CurrentEffect/MaxEffect); +} + +function KillOwner() +{ + local KFPawn_Monster KFPM; + + KFPM = KFPawn_Monster(PawnOwner); + + if (KFPM == none) + { + return; + } + + KFPM.BodyScaleChangePerSecond = CachedBodyScaleChangePerSecond; + KFPM.bNetDirty = true; + + if (!KFPM.bCanBeKilledByShrinking) + { + return; + } + + KFPM.bUseExplosiveDeath = true; + KFPM.TakeDamage(9999, Instigator, KFPM.Location, vect(0,0,0), class'KFDT_ShrinkDeath',, Instigator.Pawn.Weapon); +} + +defaultproperties +{ + DissipationRate=10 //20 + bNeedsTick=true + + MaxEffect=10.f; + ScaleOnMaxEffect=0.5f + EffectAppliedByStack=1.0f + + ApplyEffectVel=100.0f // % per second + RemoveEffectVel=0.25f // % per second + + CurrentEffect=0.0f + CurrentEffectApplied=0.0f + + MaxEffectDamageValue=1.0f + + AlreadyCached = false +} \ No newline at end of file diff --git a/KFGame/Classes/KFAffliction_Snare.uc b/KFGame/Classes/KFAffliction_Snare.uc index 386cfde..7abb163 100644 --- a/KFGame/Classes/KFAffliction_Snare.uc +++ b/KFGame/Classes/KFAffliction_Snare.uc @@ -24,7 +24,7 @@ function Init(KFPawn P, EAfflictionType Type, KFPerk InstigatorPerk) } /** */ -function Activate() +function Activate(optional class DamageType = none) { if( !bIsActive ) { diff --git a/KFGame/Classes/KFAutoPurchaseHelper.uc b/KFGame/Classes/KFAutoPurchaseHelper.uc index cdc47a9..cb152c7 100644 --- a/KFGame/Classes/KFAutoPurchaseHelper.uc +++ b/KFGame/Classes/KFAutoPurchaseHelper.uc @@ -386,7 +386,6 @@ function PurchaseWeapon(STraderItem ShopItem) Price = GetAdjustedBuyPriceFor(ShopItem); // XMAS 2021 Seasonal Objective KFPC = Outer; - `Log("ADDING WEAPON PURCHASED"); KFPC.AddWeaponPurchased(ShopItem.WeaponDef, Price); // diff --git a/KFGame/Classes/KFCharacterInfo_Human.uc b/KFGame/Classes/KFCharacterInfo_Human.uc index ac33ee1..948ddfd 100644 --- a/KFGame/Classes/KFCharacterInfo_Human.uc +++ b/KFGame/Classes/KFCharacterInfo_Human.uc @@ -871,7 +871,7 @@ private function SetAttachmentMeshAndSkin( CharAttachmentSocketName, KFP.ArmsMesh, KFP, true); KFGRI = KFGameReplicationInfo(KFP.WorldInfo.GRI); - if (AttachmentSlotIndex == 2 && KFP != none && KFGRI.bIsWeeklyMode && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + if (AttachmentSlotIndex == 2 && KFP != none && KFGRI.bIsWeeklyMode && (KFGRI.CurrentWeeklyIndex == 12)) { SetWeeklyCowboyAttachmentSkinMaterial( AttachmentSlotIndex, @@ -917,7 +917,7 @@ private function SetAttachmentMeshAndSkin( CharAttachmentSocketName, KFP.Mesh, KFP, false); KFGRI = KFGameReplicationInfo(KFP.WorldInfo.GRI); - if (AttachmentSlotIndex == 2 && KFP != none && KFGRI.bIsWeeklyMode && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + if (AttachmentSlotIndex == 2 && KFP != none && KFGRI.bIsWeeklyMode && (KFGRI.CurrentWeeklyIndex == 12)) { SetWeeklyCowboyAttachmentSkinMaterial( AttachmentSlotIndex, diff --git a/KFGame/Classes/KFCharacterInfo_Monster.uc b/KFGame/Classes/KFCharacterInfo_Monster.uc index 267bedc..1fe247f 100644 --- a/KFGame/Classes/KFCharacterInfo_Monster.uc +++ b/KFGame/Classes/KFCharacterInfo_Monster.uc @@ -273,7 +273,7 @@ simulated function SetCharacterMeshFromArch( KFPawn KFP, optional KFPlayerReplic } } - if (KFP != none && KFGRI.bIsWeeklyMode && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + if (KFP != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 12) { NewAttachment.StaticAttachment = StaticMesh(DynamicLoadObject(ZEDCowboyHatMeshPath, class'StaticMesh')); NewAttachment.AttachSocketName = KFPawn_Monster(KFP).ZEDCowboyHatAttachName; diff --git a/KFGame/Classes/KFCommon_LocalizedStrings.uc b/KFGame/Classes/KFCommon_LocalizedStrings.uc index 5f2432b..8b38c9d 100644 --- a/KFGame/Classes/KFCommon_LocalizedStrings.uc +++ b/KFGame/Classes/KFCommon_LocalizedStrings.uc @@ -18,6 +18,9 @@ var localized string FailedToReachInventoryServerString; var localized array DifficultyStrings; var localized array LengthStrings; +var localized string SpecialLengthString; +var localized string WeaponLevelString; +var localized string GunPointsString; var localized array ServerTypeStrings; var localized array PermissionStrings; var localized array ConsolePermissionStrings; diff --git a/KFGame/Classes/KFDT_ShrinkDeath.uc b/KFGame/Classes/KFDT_ShrinkDeath.uc new file mode 100644 index 0000000..7185f39 --- /dev/null +++ b/KFGame/Classes/KFDT_ShrinkDeath.uc @@ -0,0 +1,27 @@ +//============================================================================= +// KFDT_ShrinkDeath +//============================================================================= +// damage applied for killind zeds by shrinking them +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_ShrinkDeath extends KFDamageType; + +defaultproperties +{ + RadialDamageImpulse = 1000.f // This controls how much impulse is applied to gibs when exploding + bUseHitLocationForGibImpulses = true // This will make the impulse origin where the victim was hit for directional gibs + bPointImpulseTowardsOrigin = true // This creates an impulse direction aligned along hitlocation and pawn location -- this will push all gibs in the same direction + ImpulseOriginScale = 100.f // Higher means more directional gibbing, lower means more outward (and upward) gibbing + ImpulseOriginLift = 150.f + MaxObliterationGibs = 12 // Maximum number of gibs that can be spawned by obliteration, 0=MAX + bCanGib = true + bCanObliterate = true + ObliterationHealthThreshold = 0 + ObliterationDamageThreshold = 1 + bIgnoreAggroOnDamage=true + + WeaponDef = class'KFWeapDef_ShrinkRayGun' +} diff --git a/KFGame/Classes/KFDamageType.uc b/KFGame/Classes/KFDamageType.uc index 4dfc081..87e8d12 100644 --- a/KFGame/Classes/KFDamageType.uc +++ b/KFGame/Classes/KFDamageType.uc @@ -91,7 +91,8 @@ var float MicrowavePower; var float FreezePower; var float SnarePower; var float BleedPower; - +var float BigHeadPower; +var float ShrinkPower; /********************************************************************************************* Impact Effects ********************************************************************************************* */ diff --git a/KFGame/Classes/KFGFxHUD_PlayerBackpack.uc b/KFGame/Classes/KFGFxHUD_PlayerBackpack.uc index 927e07c..ea689b9 100644 --- a/KFGame/Classes/KFGFxHUD_PlayerBackpack.uc +++ b/KFGame/Classes/KFGFxHUD_PlayerBackpack.uc @@ -36,6 +36,8 @@ var bool bUsesSecondaryAmmo; var bool bUsesGrenadesAsSecondaryAmmo; var bool bUsesSecondaryAmmoAltHUD; +var transient bool bLastDoshVisibility; + var class LastPerkClass; var KFWeapon LastWeapon; @@ -45,6 +47,8 @@ var ASColorTransform DefaultColor; var ASColorTransform RedColor; var name OldState; +var transient byte LastGrenadeIndex; + function InitializeHUD() { MyKFPC = KFPlayerController(GetPC()); @@ -87,6 +91,20 @@ function UpdateDosh() { local int CurrentDosh; local int DeltaDosh; + local bool bCanUseDosh; + + bCanUseDosh = MyKFPC.CanUseDosh(); + + if (bCanUseDosh != bLastDoshVisibility) + { + UpdateDoshVisibility(bCanUseDosh); + bLastDoshVisibility = bCanUseDosh; + } + + if (!bCanUseDosh) + { + return; + } if (MyKFPC.PlayerReplicationInfo != none) { @@ -99,6 +117,18 @@ function UpdateDosh() SetInt("backpackDosh" ,DeltaDosh); LastDosh = CurrentDosh; } + } +} + +function UpdateDoshVisibility(bool visible) +{ + if (visible) + { + SetBool("DoshSetVisibility", true); + } + else + { + SetBool("DoshSetVisibility", false); } } @@ -125,7 +155,13 @@ function UpdateGrenades() SetString("backpackGrenadeType", "img://" $ MyKFPC.CurrentPerk.GetGrenadeImagePath()); LastPerkClass = MyKFPC.CurrentPerk.Class; LastSavedBuild = MyKFPC.CurrentPerk.GetSavedBuild(); - } + LastGrenadeIndex = MyKFPC.CurrentPerk.GetGrenadeSelectedIndex(); + } + else if (MyKFPC.CurrentPerk.GetGrenadeSelectedIndex() != LastGrenadeIndex) + { + SetString("backpackGrenadeType", "img://" $ MyKFPC.CurrentPerk.GetGrenadeImagePath()); + LastGrenadeIndex = MyKFPC.CurrentPerk.GetGrenadeSelectedIndex(); + } } // Update the grenades count value if(CurrentGrenades != LastGrenades) @@ -328,4 +364,6 @@ DefaultProperties { LastMaxWeight=-1 LastWeight=-1 + bLastDoshVisibility=true + LastGrenadeIndex=0 } diff --git a/KFGame/Classes/KFGFxHUD_ScoreboardMapInfoContainer.uc b/KFGame/Classes/KFGFxHUD_ScoreboardMapInfoContainer.uc index c00c1de..a80aa05 100644 --- a/KFGame/Classes/KFGFxHUD_ScoreboardMapInfoContainer.uc +++ b/KFGame/Classes/KFGFxHUD_ScoreboardMapInfoContainer.uc @@ -89,7 +89,19 @@ function UpdateWaveCount() } CurrentWaveNum = KFGRI.WaveNum; - if(KFGRI.IsBossWave()) + + if (KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 16) + { + if (KFGRI.bWaveGunGameIsFinal) + { + SetString("waveNumber", FinalString); + } + else + { + SetString("waveNumber", "" $ KFGRI.GunGameWavesCurrent); + } + } + else if(KFGRI.IsBossWave()) { SetString("waveNumber", class'KFGFxHUD_WaveInfo'.default.BossWaveString); } diff --git a/KFGame/Classes/KFGFxHUD_WaveInfo.uc b/KFGame/Classes/KFGFxHUD_WaveInfo.uc index 32804d2..f907252 100644 --- a/KFGame/Classes/KFGFxHUD_WaveInfo.uc +++ b/KFGame/Classes/KFGFxHUD_WaveInfo.uc @@ -71,7 +71,7 @@ function TickHud(float DeltaTime) function UpdateWaveCount() { - local int CurrentWaveMax,CurrentWave; + local int CurrentWaveMax, CurrentWave; if( KFGRI == none ) { @@ -83,21 +83,46 @@ function UpdateWaveCount() return; } - // Max # of waves. - CurrentWaveMax = KFGRI.GetFinalWaveNum(); - if(LastWaveMax != CurrentWaveMax) - { - SetInt("maxWaves" , KFGRI.default.bEndlessMode ? INDEX_NONE : CurrentWaveMax); - LastWaveMax = CurrentWaveMax; - } + if (KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 16) + { + CurrentWave = KFGRI.GunGameWavesCurrent; + CurrentWaveMax = KFGRI.GetFinalWaveNum(); - // Current wave we're on. - CurrentWave = KFGRI.WaveNum; - if(CurrentWave != LastWave) - { - SetInt("currentWave" , CurrentWave); + if (KFGRI.bWaveGunGameIsFinal) + { + CurrentWave = CurrentWaveMax + 1; + } + else + { + CurrentWaveMax = -1; + } + + Setint("maxGunGameWave" , CurrentWaveMax); + Setint("currentGunGameWave" , CurrentWave); + + LastWaveMax = CurrentWaveMax; LastWave = CurrentWave; } + else + { + CurrentWave = KFGRI.WaveNum; + CurrentWaveMax = KFGRI.GetFinalWaveNum(); + + // Max # of waves. + if (LastWaveMax != CurrentWaveMax) + { + SetInt("maxWaves" , KFGRI.default.bEndlessMode ? INDEX_NONE : CurrentWaveMax); + LastWaveMax = CurrentWaveMax; + } + + // Current wave we're on. + if (CurrentWave != LastWave) + { + SetInt("currentWave" , CurrentWave); + + LastWave = CurrentWave; + } + } } function UpdateZEDCount() diff --git a/KFGame/Classes/KFGFxMenu_Gear.uc b/KFGame/Classes/KFGFxMenu_Gear.uc index d14ebfa..e644ddc 100644 --- a/KFGame/Classes/KFGFxMenu_Gear.uc +++ b/KFGame/Classes/KFGFxMenu_Gear.uc @@ -306,6 +306,7 @@ function UpdateAttachmentsList(array Attachments) local string AttachmentName; local PlayerController PC; local bool bIsWildWest; + local KFGameReplicationInfo KFGRI; bIsWildWest = false; ItemIndex = 0; @@ -322,7 +323,8 @@ function UpdateAttachmentsList(array Attachments) ItemIndex++; PC = GetPC(); - bIsWildWest = (PC != none && Pc.WorldInfo.GRI.IsA('KFGameReplicationInfo_WeeklySurvival') && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)); + KFGRI = PC != none ? KFGameReplicationInfo(Pc.WorldInfo.GRI) : none; + bIsWildWest = (KFGRI != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 12); for (i = 0; i < Attachments.length; i++) { @@ -773,9 +775,14 @@ function ForceWeeklyCowboyHat() { local PlayerController PC; local int CowboyHatIndex; - PC = GetPC(); + local bool bIsWildWest; + local KFGameReplicationInfo KFGRI; - if (PC != none && Pc.WorldInfo.GRI.IsA('KFGameReplicationInfo_WeeklySurvival') && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + PC = GetPC(); + KFGRI = PC != none ? KFGameReplicationInfo(Pc.WorldInfo.GRI) : none; + bIsWildWest = (KFGRI != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 12); + + if (bIsWildWest) { CowboyHatIndex = FindCowboyHatAttachmentIndex(CurrentCharInfo); if (CowboyHatIndex >= 0) diff --git a/KFGame/Classes/KFGFxMenu_Inventory.uc b/KFGame/Classes/KFGFxMenu_Inventory.uc index 382728b..b99867f 100644 --- a/KFGame/Classes/KFGFxMenu_Inventory.uc +++ b/KFGame/Classes/KFGFxMenu_Inventory.uc @@ -131,9 +131,22 @@ struct InventoryHelper var int ItemDefinition; var int ItemIndex; var int ItemCount; + var ItemType Type; var GFxObject GfxItemObject; + + // For ordering in weapon skins + var int WeaponDef; + var int Price; + //var string FullName; + var int SkinType; + var ItemRarity Rarity; + var int Quality; }; +var array SkinListWeaponsSearchCache; +var array SkinListOrderedCache; +var bool NeedToRegenerateSkinList; + var EInventoryWeaponType_Filter CurrentWeaponTypeFilter; var int CurrentPerkIndexFilter; var ItemRarity CurrentRarityFilter; @@ -142,9 +155,14 @@ var EINventory_Filter CurrentInventoryFilter; var ExchangeRuleSets RuleToExchange; +var private int CrcTable[256]; + function InitializeMenu( KFGFxMoviePlayer_Manager InManager ) { super.InitializeMenu( InManager ); + + CrcInit(); + KFPC = KFPlayerController(GetPC()); CurrentPerkIndexFilter = KFPC.PerkList.length; //default value @@ -229,15 +247,95 @@ function OnClose() } } +final function CrcInit() { + + const CrcPolynomial = 0xedb88320; + + local int CrcValue; + local int IndexBit; + local int IndexEntry; + + for (IndexEntry = 0; IndexEntry < 256; IndexEntry++) { + CrcValue = IndexEntry; + + for (IndexBit = 8; IndexBit > 0; IndexBit--) + if ((CrcValue & 1) != 0) + CrcValue = (CrcValue >>> 1) ^ CrcPolynomial; + else + CrcValue = CrcValue >>> 1; + + CrcTable[IndexEntry] = CrcValue; + } + } + +final function int Crc(coerce string Text) +{ + local int CrcValue; + local int IndexChar; + local int StrLen; + + CrcValue = 0xffffffff; + StrLen = Len(Text); + for (IndexChar = 0; IndexChar < StrLen; IndexChar++) + CrcValue = (CrcValue >>> 8) ^ CrcTable[(Asc(Mid(Text, IndexChar, 1)) ^ CrcValue) & 0xff]; + + return CrcValue; +} + +delegate int SortByWeaponTypeDefinition(InventoryHelper A, InventoryHelper B) +{ + return A.WeaponDef < B.WeaponDef ? -1 : +1; +} + +delegate int SortByPrice(InventoryHelper A, InventoryHelper B) +{ + return A.Price > B.Price ? -1 : +1; +} + +delegate int SortByRarity(InventoryHelper A, InventoryHelper B) +{ + return A.Rarity > B.Rarity ? -1 : +1; +} + +delegate int SortBySkinType(InventoryHelper A, InventoryHelper B) +{ + return A.SkinType > B.SkinType ? -1 : +1; +} + +delegate int SortByQuality(InventoryHelper A, InventoryHelper B) +{ + return A.Quality < B.Quality ? -1 : +1; +} + +delegate int SortByAll(InventoryHelper A, InventoryHelper B) +{ + //local int WeapDefValue, SkinTypeValue; + + /** 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 : ( + //STRCompare(A.WeaponDef, B.WeaponDef, WeapDefValue) < 0 ? -1 : ( WeapDefValue != 0 ? 1 : ( + A.WeaponDef < B.WeaponDef ? -1 : (A.WeaponDef > B.WeaponDef ? 1 : ( + A.Rarity > B.Rarity ? -1 : (A.Rarity < B.Rarity ? 1 : ( + //STRCompare(A.SkinType, B.SkinType, SkinTypeValue) > 0 ? -1 : ( SkinTypeValue != 0 ? 1 : ( + A.SkinType > B.SkinType ? -1 : (A.SkinType < B.SkinType ? 1 : ( + A.Quality < B.Quality ? -1 : 1 + )) + )) + )) + )); +} + function InitInventory() { - local int i, ItemIndex, HelperIndex; + local int i, ItemIndex, HelperIndex, WeaponItemID, SearchWeaponSkinIndex; local ItemProperties TempItemDetailsHolder; local GFxObject ItemArray, ItemObject; local bool bActiveItem; - local array ActiveItems; + local array ActiveItems, ValidSkinItems, FailedSkinItems; local InventoryHelper HelperItem; local array ExchangeRules; + local class WeaponDef; + local string SkinType; local GFxObject PendingItem; @@ -249,6 +347,7 @@ function InitInventory() SetObject("inventoryList", ItemArray); return; } + for (i = 0; i < OnlineSub.CurrentInventory.length; i++) { //look item up to get info on it. @@ -264,10 +363,62 @@ function InitInventory() ItemObject = CreateObject("Object"); HelperIndex = ActiveItems.Find('ItemDefinition', onlineSub.CurrentInventory[i].Definition); - if(HelperIndex == INDEX_NONE) + if (HelperIndex == INDEX_NONE) { - HelperItem.ItemDefinition = onlineSub.CurrentInventory[i].Definition; - HelperItem.ItemCount = onlineSub.CurrentInventory[i].Quantity; + HelperItem.ItemDefinition = onlineSub.CurrentInventory[i].Definition; + HelperItem.ItemCount = onlineSub.CurrentInventory[i].Quantity; + + if (TempItemDetailsHolder.Type == ITP_WeaponSkin) + { + // Copy required stuff + //HelperItem.FullName = TempItemDetailsHolder.Name; + HelperItem.Rarity = TempItemDetailsHolder.Rarity; + HelperItem.Quality = TempItemDetailsHolder.Quality; + + if (bool(OnlineSub.CurrentInventory[i].NewlyAdded)) + { + NeedToRegenerateSkinList = true; + } + + // Search on the cache, to speed up + WeaponItemID = SkinListWeaponsSearchCache.Find('ItemDefinition', HelperItem.ItemDefinition); + + if (WeaponItemID != INDEX_NONE) + { + HelperItem.WeaponDef = SkinListWeaponsSearchCache[WeaponItemID].WeaponDef; + HelperItem.Price = SkinListWeaponsSearchCache[WeaponItemID].Price; + HelperItem.SkinType = SkinListWeaponsSearchCache[WeaponItemID].SkinType; + } + else + { + // Get right part of the string without from the first "| " + SearchWeaponSkinIndex = InStr(TempItemDetailsHolder.Name, "|"); + SkinType = Right(TempItemDetailsHolder.Name, Len(TempItemDetailsHolder.Name) - SearchWeaponSkinIndex - 2); + + // Get the left part of the string without the next "| " + SearchWeaponSkinIndex = InStr(SkinType, "|"); + HelperItem.SkinType = CrC(Left(SkinType, SearchWeaponSkinIndex)); + + WeaponItemID = class'KFWeaponSkinList'.default.Skins.Find('Id', HelperItem.ItemDefinition); + + if (WeaponItemID != INDEX_NONE) + { + WeaponDef = class'KFWeaponSkinList'.default.Skins[WeaponItemID].WeaponDef; + + // All Weapons start by KFGameContent.KFWeap_ Skip that prefix. + HelperItem.WeaponDef = CrC(Mid(WeaponDef.default.WeaponClassPath, 21)); + HelperItem.Price = WeaponDef.default.BuyPrice; + } + else + { + HelperItem.WeaponDef = -1; + HelperItem.Price = 0; + } + + SkinListWeaponsSearchCache.AddItem(HelperItem); + } + } + ActiveItems.AddItem(HelperItem); HelperIndex = ActiveItems.length - 1; } @@ -314,9 +465,87 @@ function InitInventory() OnlineSub.ClearNewlyAdded(); - for (i = 0; i < ActiveItems.length; i++) + if (CurrentInventoryFilter == EInv_WeaponSkins) { - ItemArray.SetElementObject(i, ActiveItems[i].GfxItemObject); + // If need to refresh... we regenerate the list, if not reuse our Cache + NeedToRegenerateSkinList = NeedToRegenerateSkinList || ActiveItems.Length != SkinListOrderedCache.Length; + if (NeedToRegenerateSkinList) + { + NeedToRegenerateSkinList = false; + + /*`Log("START ORDERING!!!"); + `Log("----------");*/ + + for (i = 0 ; i < ActiveItems.Length; i++) + { + // If doesn't have weapon definition, don't consider, only add as FailedItem to be added at the end + if (ActiveItems[i].WeaponDef != -1) + { + ValidSkinItems.AddItem(ActiveItems[i]); + } + else + { + FailedSkinItems.AddItem(ActiveItems[i]); + } + } + + // Now we have all valid weapons + + // 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 + + ValidSkinItems.Sort(SortByAll); + + SkinListOrderedCache = ValidSkinItems; + + /*for (i = 0 ; i < SkinListOrderedCache.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("----------"); + }*/ + + // FailedItems are weapons that don't have a Weapon Definition, this might be a case with old unsupported content? + for (i = 0; i < FailedSkinItems.Length; i++) + { + /*if (FailedSkinItems[i].FullName != "") + { + `Log("FailedSkinItems ID : " $FailedSkinItems[i].ItemDefinition); + `Log("FailedSkinItems Price : " $FailedSkinItems[i].Price); + `Log("FailedSkinItems Full Name : " $FailedSkinItems[i].FullName); + `Log("FailedSkinItems Skin : " $FailedSkinItems[i].SkinType); + `Log("FailedSkinItems Rarity : " $FailedSkinItems[i].Rarity); + `Log("FailedFailedSkinItemsItems Quality : " $FailedSkinItems[i].Quality); + `Log("----------"); + }*/ + + SkinListOrderedCache.AddItem(FailedSkinItems[i]); + } + + /*`Log("FINISH ORDERING!!!"); + `Log("----------");*/ + } + else + { + //`Log("USING SKIN LIST CACHE!!!"); + } + + for (i = 0; i < SkinListOrderedCache.length; i++) + { + ItemArray.SetElementObject(i, SkinListOrderedCache[i].GfxItemObject); + } + } + else + { + for (i = 0; i < ActiveItems.length; i++) + { + ItemArray.SetElementObject(i, ActiveItems[i].GfxItemObject); + } } SetObject("inventoryList", ItemArray); @@ -376,6 +605,9 @@ function OnItemExhangeTimeOut() function FinishCraft() { SetVisible(true); + + `Log("FinishCraft"); + NeedToRegenerateSkinList = true; } function SetMatineeColor(int ItemRarity) @@ -494,10 +726,12 @@ function bool IsItemActive(int ItemDefinition) if(WeaponDef != none) { - return class'KFWeaponSkinList'.Static.IsSkinEquip(WeaponDef, ItemDefinition); + if (class'KFWeaponSkinList'.Static.IsSkinEquip(WeaponDef, ItemDefinition)) + { + return true; + } } - return false; } @@ -953,6 +1187,7 @@ function Callback_Equip( int ItemDefinition ) } //refresh inventory + NeedToRegenerateSkinList = true; // need to regenerate as the equipped state changed InitInventory(); } @@ -1249,6 +1484,8 @@ 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 7177791..1a09a11 100644 --- a/KFGame/Classes/KFGFxMenu_Perks.uc +++ b/KFGame/Classes/KFGFxMenu_Perks.uc @@ -39,11 +39,17 @@ var KFPlayerReplicationInfo MyKFPRI; var bool bModifiedSkills; var bool bModifiedPerk; var bool bChangesMadeDuringLobby; +var bool bModifiedWeaponIndexes; var name PerkLevelupSound; var byte SelectedSkillsHolder[`MAX_PERK_SKILLS]; +var const private float StickInputThreshold; +var const private float StickResetThreshold; + +var transient bool bAxisResetLeft, bAxisResetRight; + function InitializeMenu(KFGFxMoviePlayer_Manager InManager) { super.InitializeMenu(InManager); @@ -77,7 +83,7 @@ event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget) { DetailsContainer = KFGFxPerksContainer_Details(Widget);//some reason this is coming out to none! DetailsContainer.Initialize( self ); - DetailsContainer.UpdateDetails(PerkClass); + DetailsContainer.UpdateDetails(PerkClass, SelectedSkillsHolder, false, false); DetailsContainer.UpdatePassives(PerkClass); } break; @@ -114,6 +120,8 @@ function OnOpen() { local KFGameReplicationInfo KFGRI; + GetGameViewportClient().HandleInputAxis = OnAxisModified; + LastPerkIndex = KFPC.SavedPerkIndex; MyKFPRI = KFPlayerReplicationInfo( GetPC().PlayerReplicationInfo ); @@ -196,9 +204,11 @@ event OnClose() { local bool bShouldUpdatePerk; + GetGameViewportClient().HandleInputAxis = none; + if( KFPC != none ) { - if( bModifiedPerk || bModifiedSkills ) + if( bModifiedPerk || bModifiedSkills || bModifiedWeaponIndexes) { bShouldUpdatePerk = bModifiedPerk && LastPerkIndex != KFPC.SavedPerkIndex; @@ -215,8 +225,15 @@ event OnClose() Manager.CachedProfile.SetProfileSettingValueInt( KFID_SavedPerkIndex, LastPerkIndex ); } + if (bModifiedWeaponIndexes) + { + Manager.CachedProfile.SetProfileSettingValueInt( KFID_SurvivalStartingWeapIdx, KFPC.SurvivalPerkWeapIndex ); + Manager.CachedProfile.SetProfileSettingValueInt( KFID_SurvivalStartingGrenIdx, KFPC.SurvivalPerkGrenIndex ); + } + bModifiedPerk = false; bModifiedSkills = false; + bModifiedWeaponIndexes = false; } } @@ -323,7 +340,7 @@ function UpdateContainers( class PerkClass, optional bool bClickedIndex= if( DetailsContainer != none ) { - DetailsContainer.UpdateDetails( PerkClass ); + DetailsContainer.UpdateDetails( PerkClass, SelectedSkillsHolder, false, false ); DetailsContainer.UpdatePassives( PerkClass ); } @@ -457,6 +474,11 @@ function Callback_SkillSelected( byte TierIndex, byte SkillIndex ) SelectedSkillsHolder[TierIndex] = SkillIndex; UpdateSkillsUI(KFPC.PerkList[LastPerkIndex].PerkClass); SavePerkData(); + + if ( KFPC.PerkList[LastPerkIndex].PerkClass.Name == 'KFPerk_Survivalist' ) + { + DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, false, false ); + } } } @@ -469,6 +491,110 @@ function Callback_SkillSelectionOpened() } } +function OnPrevWeaponPressed() +{ + local byte NewIndex; + + NewIndex = KFPC.CurrentPerk.OnPrevWeaponSelected(); + KFPC.SurvivalPerkWeapIndex = NewIndex; + + DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, true, false ); + bModifiedWeaponIndexes=true; +} + +function OnNextWeaponPressed() +{ + local byte NewIndex; + + NewIndex = KFPC.CurrentPerk.OnNextWeaponSelected(); + KFPC.SurvivalPerkWeapIndex = NewIndex; + + DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, false, true ); + bModifiedWeaponIndexes=true; +} + +function OnPrevGrenadePressed() +{ + local byte NewIndex; + + NewIndex = KFPC.CurrentPerk.OnPrevGrenadeSelected(); + KFPC.SurvivalPerkGrenIndex = NewIndex; + + DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, true, false ); + bModifiedWeaponIndexes=true; +} + +function OnNextGrenadePressed() +{ + local byte NewIndex; + + NewIndex = KFPC.CurrentPerk.OnNextGrenadeSelected(); + KFPC.SurvivalPerkGrenIndex = NewIndex; + + DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, false, true ); + bModifiedWeaponIndexes=true; +} + +event bool OnAxisModified( int ControllerId, name Key, float Delta, float DeltaTime, bool bGamepad ) +{ + if (GetPC().PlayerInput.bUsingGamepad ) + { + if (DetailsContainer != none && bGamepad) + { + OnGamepadAxisModified(ControllerId, Key, Delta, DeltaTime, bGamepad); + } + } + + return false; +} + +function OnGamepadAxisModified( int ControllerId, name Key, float Delta, float DeltaTime, bool bGamepad ) +{ + local float AbsDelta; + + AbsDelta = Abs(Delta); + + if ( KFPC.CurrentPerk.static.CanChoosePrimaryWeapon() && Key == 'XboxTypeS_LeftX' ) + { + if (bAxisResetLeft && AbsDelta > StickInputThreshold) + { + if (Delta < 0) + { + OnPrevWeaponPressed(); + } + else + { + OnNextWeaponPressed(); + } + bAxisResetLeft = false; + } + else if (!bAxisResetLeft && AbsDelta < StickResetThreshold) + { + bAxisResetLeft = true; + } + } + else if (KFPC.CurrentPerk.static.CanChooseGrenade() && Key == 'XboxTypeS_RightX') + { + if (bAxisResetRight && AbsDelta > StickInputThreshold) + { + if (Delta < 0) + { + OnPrevGrenadePressed(); + } + else + { + OnNextGrenadePressed(); + } + bAxisResetRight = false; + } + else if (!bAxisResetRight && AbsDelta < StickResetThreshold) + { + bAxisResetRight = true; + } + } +} + + defaultproperties { PerkLevelupSound=LevelUp_Popup @@ -481,4 +607,9 @@ defaultproperties SubWidgetBindings.Add((WidgetName="SelectedPerkSummaryContainer",WidgetClass=class'KFGFxPerksContainer_SkillsSummary')) LastPerkIndex=255 LastPerkLevel=255 + + bAxisResetLeft=true + bAxisResetRight=false + StickInputThreshold=0.5 + StickResetThreshold=0.5 } diff --git a/KFGame/Classes/KFGFxMenu_PostGameReport.uc b/KFGame/Classes/KFGFxMenu_PostGameReport.uc index 8e8755c..ed73c67 100644 --- a/KFGame/Classes/KFGFxMenu_PostGameReport.uc +++ b/KFGame/Classes/KFGFxMenu_PostGameReport.uc @@ -216,7 +216,11 @@ function SetSumarryInfo() } else { - if (KFGRI.default.bEndlessMode) + if (KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 16) + { + TextObject.SetString("waveTime", WaveString @ KFGRI.GunGameWavesCurrent @"-" @FormatTime(KFGRI.ElapsedTime)); + } + else if (KFGRI.default.bEndlessMode) { TextObject.SetString("waveTime", WaveString @ KFGRI.WaveNum @"-" @FormatTime(KFGRI.ElapsedTime)); } diff --git a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc index 911e5f1..cb22f53 100644 --- a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc +++ b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc @@ -70,6 +70,9 @@ var KFGFxWidget_BossHealthBar bossHealthBar; var KFGFxWidget_MapText MapTextWidget; // Widget that displays map texts var KFGFxWidget_MapCounterText MapCounterTextWidget; +// Widget that displays gun mode texts +var KFGFxWidget_GunGame GunGameWidget; + var KFPlayerController KFPC; @@ -102,6 +105,9 @@ var Protected float LastUpdateTime; // The name of the player currently being voted on to kick var string PendingKickPlayerName; +// Gun game variables +var transient bool bLastGunGameVisibility; + /** On creation of the HUD */ function Init(optional LocalPlayer LocPlay) { @@ -348,6 +354,13 @@ event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget) GoompaCounterWidget=KFGFxWidget_GoompaCounter(Widget); } break; + case 'GunGameContainer': + if (GunGameWidget == none) + { + GunGameWidget=KFGFxWidget_GunGame(Widget); + SetWidgetPathBinding( Widget, WidgetPath ); + } + break; } return true; @@ -376,6 +389,8 @@ function UpdateWeaponSelect() /** Update all the unique HUD pieces */ function TickHud(float DeltaTime) { + local bool bGunGameVisibility; + if(KFPC == none || KFPC.WorldInfo.TimeSeconds - LastUpdateTime < UpdateInterval ) { return; @@ -447,6 +462,22 @@ function TickHud(float DeltaTime) { GfxScoreBoardPlayer.TickHud(DeltaTime); } + + if (GunGameWidget != none) + { + bGunGameVisibility = KFPC.CanUseGunGame(); + + if (bGunGameVisibility) + { + bGunGameVisibility = KFPC.Pawn.Health > 0; + } + + if (bGunGameVisibility != bLastGunGameVisibility) + { + GunGameWidget.UpdateGunGameVisibility(bGunGameVisibility); + bLastGunGameVisibility = bGunGameVisibility; + } + } } function UpdateObjectiveActive() @@ -781,7 +812,18 @@ function DisplayExpandedWaveInfo() } else { - if (KFGRI.IsBossWave()) + if (KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 16) + { + if (KFGRI.bWaveGunGameIsFinal) + { + PriorityMessageObject.SetString("waveNum", class'KFGFxHUD_WaveInfo'.default.FinalWaveString); + } + else + { + PriorityMessageObject.SetString("waveNum", KFGRI.GunGameWavesCurrent $"/?"); + } + } + else if (KFGRI.IsBossWave()) { PriorityMessageObject.SetString("waveNum", class'KFGFxHUD_WaveInfo'.default.BossWaveString); } @@ -1091,6 +1133,14 @@ function UpdateGoompaCounterWidget(int value, int max) } } +function UpdateGunGameWidget(int score, int max_score, int level, int max_level) +{ + if (GunGameWidget != none) + { + GunGameWidget.SetData(score, max_score, level, max_level); + } +} + //============================================================== // Input //============================================================== @@ -1441,6 +1491,8 @@ DefaultProperties bAutoPlay=true bIsSpectating=false + bLastGunGameVisibility=true + WidgetBindings.Add((WidgetName="ObjectiveContainer",WidgetClass=class'KFGFxHUD_ObjectiveConatiner')) WidgetBindings.Add((WidgetName="SpectatorInfoWidget",WidgetClass=class'KFGFxHUD_SpectatorInfo')) WidgetBindings.Add((WidgetName="PlayerStatWidgetMC",WidgetClass=class'KFGFxHUD_PlayerStatus')) @@ -1465,6 +1517,7 @@ DefaultProperties WidgetBindings.Add((WidgetName="bossHealthBar", WidgetClass=class'KFGFxWidget_BossHealthBar')) WidgetBindings.Add((WidgetName="mapTextWidget", WidgetClass=class'KFGFxWidget_MapText')) WidgetBindings.Add((WidgetName="counterMapTextWidget", WidgetClass=class'KFGFxWidget_MapCounterText')) + WidgetBindings.ADD((WidgetName="GunGameContainer", WidgetClass=class'KFGFxWidget_GunGame')); SpecialWaveIconPath(AT_Clot)="UI_Endless_TEX.ZEDs.UI_ZED_Endless_Cyst" diff --git a/KFGame/Classes/KFGFxPerksContainer_Details.uc b/KFGame/Classes/KFGFxPerksContainer_Details.uc index 3c5760f..cbbebce 100644 --- a/KFGame/Classes/KFGFxPerksContainer_Details.uc +++ b/KFGame/Classes/KFGFxPerksContainer_Details.uc @@ -25,53 +25,68 @@ function LocalizeContainer() GetObject("basicLoadoutTextField").SetString("text", BasicLoadoutString); } -function UpdateDetails( class PerkClass) +function UpdateDetailsInternal(class PerkClass, KFPlayerController KFPC, byte WeaponIdx, byte GrenadeIdx) { local GFxObject DetailsProvider; - local KFPlayerController KFPC; local array WeaponNames; local array WeaponSources; local int i; DetailsProvider = CreateObject( "Object" ); + DetailsProvider.SetString( "ExperienceMessage", ExperienceString @ KFPC.GetPerkXP(PerkClass) ); + + if(PerkClass.default.PrimaryWeaponDef != none) + { + AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.static.GetPrimaryWeaponName(WeaponIdx), PerkClass.static.GetPrimaryWeaponImagePath(WeaponIdx)); + } + + if(PerkClass.default.SecondaryWeaponDef != none) + { + AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.default.SecondaryWeaponDef.static.GetItemName(), PerkClass.default.SecondaryWeaponDef.static.GetImagePath()); + } + if(PerkClass.default.KnifeWeaponDef != none) + { + AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.default.KnifeWeaponDef.static.GetItemName(), PerkClass.default.KnifeWeaponDef.static.GetImagePath()); + } + if(PerkClass.default.GrenadeWeaponDef != none) + { + AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.static.GetGrenadeWeaponName(GrenadeIdx), PerkClass.static.GetGrenadeWeaponImagePath(GrenadeIdx)); + } + + for (i = 0; i < WeaponNames.length; i++) + { + DetailsProvider.SetString( "WeaponName" $ i, WeaponNames[i] ); + DetailsProvider.SetString( "WeaponImage" $ i, "img://"$WeaponSources[i] ); + } + + DetailsProvider.SetString( "EXPAction1", PerkClass.default.EXPAction1 ); + DetailsProvider.SetString( "EXPAction2", PerkClass.default.EXPAction2 ); + + DetailsProvider.SetBool("ShowPrimaryWeaponSelectors", PerkClass.static.CanChoosePrimaryWeapon()); + DetailsProvider.SetBool("ShowGrenadeSelectors", PerkClass.static.CanChooseGrenade()); + + SetObject( "detailsData", DetailsProvider ); +} + +function UpdateDetails(class PerkClass, byte SelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext) +{ + local KFPlayerController KFPC; + local byte WeaponIdx, GrenadeIdx; + KFPC = KFPlayerController( GetPC() ); - if ( KFPC != none) + if ( KFPC == none) { - if(KFPC != none) - { - DetailsProvider.SetString( "ExperienceMessage", ExperienceString @ KFPC.GetPerkXP(PerkClass) ); - } - - if(PerkClass.default.PrimaryWeaponDef != none) - { - AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.default.PrimaryWeaponDef.static.GetItemName(), PerkClass.default.PrimaryWeaponDef.static.GetImagePath()); - } - if(PerkClass.default.SecondaryWeaponDef != none) - { - AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.default.SecondaryWeaponDef.static.GetItemName(), PerkClass.default.SecondaryWeaponDef.static.GetImagePath()); - } - if(PerkClass.default.KnifeWeaponDef != none) - { - AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.default.KnifeWeaponDef.static.GetItemName(), PerkClass.default.KnifeWeaponDef.static.GetImagePath()); - } - if(PerkClass.default.GrenadeWeaponDef != none) - { - AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.default.GrenadeWeaponDef.static.GetItemName(), PerkClass.default.GrenadeWeaponDef.static.GetImagePath()); - } - - for (i = 0; i < WeaponNames.length; i++) - { - DetailsProvider.SetString( "WeaponName" $ i, WeaponNames[i] ); - DetailsProvider.SetString( "WeaponImage" $ i, "img://"$WeaponSources[i] ); - } - - DetailsProvider.SetString( "EXPAction1", PerkClass.default.EXPAction1 ); - DetailsProvider.SetString( "EXPAction2", PerkClass.default.EXPAction2 ); - - SetObject( "detailsData", DetailsProvider ); + return; } + + WeaponIdx = 0; + GrenadeIdx = 0; + + UpdateAndGetCurrentWeaponIndexes(PerkClass, KFPC, WeaponIdx, GrenadeIdx, SelectedSkills, IsChoosingPrev, IsChoosingNext); + + UpdateDetailsInternal(PerkClass, KFPC, WeaponIdx, GrenadeIdx); } function AddWeaponInfo(out array WeaponNames, out array WeaponSources, string WeaponName, string WeaponSource) @@ -108,4 +123,12 @@ function UpdatePassives(Class PerkClass) SetObject( "passivesData", PassivesProvider ); } - +function UpdateAndGetCurrentWeaponIndexes(class PerkClass, KFPlayerController KFPC, out byte WeaponIdx, out byte GrenadeIdx + , byte SelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext) +{ + if (PerkClass.Name == 'KFPerk_Survivalist') + { + WeaponIdx = KFPC.CurrentPerk.SetWeaponSelectedIndex(KFPC.SurvivalPerkWeapIndex); + GrenadeIdx = KFPC.CurrentPerk.SetGrenadeSelectedIndexUsingSkills(KFPC.SurvivalPerkGrenIndex, SelectedSkills, IsChoosingPrev, IsChoosingNext); + } +} diff --git a/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc b/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc index ff63b49..8dd85d7 100644 --- a/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc +++ b/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc @@ -14,6 +14,14 @@ var localized string YourVoteString; var localized string TopVotesString; var string MapVoteString; //localized in parent and parent passes it to this class +// Maps to skip +const MapBiolapse = 'KF-Biolapse'; +const MapNightmare = 'KF-Nightmare'; +const MapPowerCore = 'KF-PowerCore_Holdout'; +const MapDescent = 'KF-TheDescent'; +const MapKrampus = 'KF-KrampusLair'; +const MapSteam = 'KF-SteamFortress'; + //============================================================== // Initialization //============================================================== @@ -47,36 +55,46 @@ function SetMapOptions() local bool IsWeeklyMode; local bool IsBrokenTrader; local bool IsBossRush; + local bool IsGunGame; + local bool bShouldSkipMaps; + local name MapName; KFGRI = KFGameReplicationInfo(GetPC().WorldInfo.GRI); + bShouldSkipMaps = false; Counter = 0; + if(KFGRI != none && KFGRI.VoteCollector != none) { ServerMapList = KFGRI.VoteCollector.MapList; IsWeeklyMode = KFGRI.bIsWeeklyMode; IsBrokenTrader = KFGRI.CurrentWeeklyIndex == 11; IsBossRush = KFGRI.CurrentWeeklyIndex == 14; + IsGunGame = KFGRI.CurrentWeeklyIndex == 16; + + bShouldSkipMaps = IsWeeklyMode && (IsBrokenTrader || IsBossRush || IsGunGame); + //gfx MapList = CreateArray(); for (i = 0; i < ServerMapList.length; i++) { - if ( IsWeeklyMode && (IsBrokenTrader || IsBossRush) && ( ServerMapList[i] == "KF-Biolapse" || - ServerMapList[i] == "KF-Nightmare" || - ServerMapList[i] == "KF-PowerCore_Holdout" || - ServerMapList[i] == "KF-TheDescent" || - ServerMapList[i] == "KF-KrampusLair")) + MapName = name(ServerMapList[i]); + if ( bShouldSkipMaps && ( MapName == MapBiolapse || + MapName == MapNightmare || + MapName == MapPowerCore || + MapName == MapDescent || + MapName == MapKrampus)) { continue; } /* Temporary removal of SteamFrotress for BossRush */ - if (IsWeeklyMode && IsBossRush && ServerMapList[i] == "KF-SteamFortress") + if (IsWeeklyMode && IsBossRush && MapName == MapSteam) { continue; } - /**/ + /* */ MapObject = CreateObject("Object"); MapObject.SetString("label", class'KFCommon_LocalizedStrings'.static.GetFriendlyMapName(ServerMapList[i]) ); diff --git a/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Summer2022.uc b/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Summer2022.uc new file mode 100644 index 0000000..6ef7161 --- /dev/null +++ b/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Summer2022.uc @@ -0,0 +1,27 @@ +class KFGFxSpecialEventObjectivesContainer_Summer2022 extends KFGFxSpecialEventObjectivesContainer; + +function Initialize(KFGFxObject_Menu NewParentMenu) +{ + super.Initialize(NewParentMenu); +} + +DefaultProperties +{ + ObjectiveIconURLs[0] = "Summer2022_UI.UI_Objectives_Summer2022_Not_the_best_tourists" // Kill 1500 Zeds on any map or mode + ObjectiveIconURLs[1] = "Summer2022_UI.UI_Objectives_Summer2022_Splashdown" // Complete the Weekly on Rig + ObjectiveIconURLs[2] = "Summer2022_UI.UI_Objectives_Summer2022_Top_Surfer" // Complete 100 waves on Rig + ObjectiveIconURLs[3] = "Summer2022_UI.UI_Objectives_Summer2022_Overboard" // Throw 50 Zeds to the sea on Rig + ObjectiveIconURLs[4] = "Summer2022_UI.UI_Objectives_Summer2022_Wide_wide_sea" // Complete wave 15 on Endless Hard or higher difficulty on Rig + + //defaults + AllCompleteRewardIconURL="CHR_DeepSeaUniform_Item_TEX.trident.deepseatrident_precious" + ChanceDropIconURLs[0]="CHR_CosmeticSet_SS_01_ItemTex.Sideshow_Prize_Ticket" + ChanceDropIconURLs[1]="CHR_CosmeticSet_SS_01_ItemTex.SideshowGoldenTicket_Small" + IconURL="Summer2022_UI.KF2_Summer_2022_Tidal_Terror" + + 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/KFGFxStartContainer_InGameOverview.uc b/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc index b9507ff..031a4c2 100644 --- a/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc +++ b/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc @@ -348,11 +348,9 @@ function UpdateOverviewInGame() bCustomLength = false; //not using these now bCustomDifficulty = false; - KFGRI = KFGameReplicationInfo(GetPC().WorldInfo.GRI); if(KFGRI != none) { - if (KFGRI != none && KFGRI.GameClass != none && !KFGRI.GameClass.Static.GetShouldShowLength()) { HideLengthInfo(); @@ -372,19 +370,30 @@ function UpdateOverviewInGame() } CurrentLengthIndex = KFGRI.GameLength; - if(LastLengthIndex != CurrentLengthIndex) - { - // don't show the length category in objective mode - if (KFGRI.GameClass.Name == ObjectiveClassName) - { - UpdateLength(""); - } - else - { - UpdateLength(bCustomLength ? Class'KFCommon_LocalizedStrings'.default.CustomString : class'KFCommon_LocalizedStrings'.static.GetLengthString(CurrentLengthIndex)); - } + + if (KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 16) + { + UpdateLength(Class'KFCommon_LocalizedStrings'.default.SpecialLengthString); + LastLengthIndex = CurrentLengthIndex; - } + } + else + { + if (LastLengthIndex != CurrentLengthIndex) + { + // don't show the length category in objective mode + if (KFGRI.GameClass.Name == ObjectiveClassName) + { + UpdateLength(""); + } + else + { + UpdateLength(bCustomLength ? Class'KFCommon_LocalizedStrings'.default.CustomString : class'KFCommon_LocalizedStrings'.static.GetLengthString(CurrentLengthIndex)); + } + + LastLengthIndex = CurrentLengthIndex; + } + } UpdateServerType( class'KFCommon_LocalizedStrings'.static.GetServerTypeString(int(KFGRI.bCustom)) ); diff --git a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc index 8b67d53..823c2d8 100644 --- a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc +++ b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc @@ -182,31 +182,40 @@ function FillWhatsNew() local SWhatsNew item; WhatsNewItems.Remove(0, WhatsNewItems.Length); // Latest Update - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Christmas_ChopTilYouDrop", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_TidalTerror", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); WhatsNewItems.AddItem(item); -// KF2 Armory Season Pass 2021 - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Spring_Armory_Season_Pass", "ArmorySeasonPass", "https://store.steampowered.com/app/1524820/Killing_Floor_2__Armory_Season_Pass"); +// KF2 Armory Season Pass 2022 + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_ArmorySeasonPassII", "ArmorySeasonPass", "https://store.steampowered.com/app/1914490/KF2__Season_Pass_2022"); WhatsNewItems.AddItem(item); // Featured Time Limited Item - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Christmas_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/5588"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_SS_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/4928"); WhatsNewItems.AddItem(item); // Featured Full Gear - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas_Holiday_Shopper", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9262"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_UltimateEdition", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9283"); + WhatsNewItems.AddItem(item); +// Featured Weapon Skin Bundle + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_NeonMKVIII_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9362"); + WhatsNewItems.AddItem(item); +// Featured Weapon Skin Bundle + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_Classic_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9363"); + WhatsNewItems.AddItem(item); +// Featured Weapon Skin Bundle + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_DeepSea_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9364"); + WhatsNewItems.AddItem(item); +// Featured Weapon Skin Bundle + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_Chameleon_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9365"); WhatsNewItems.AddItem(item); // Featured Outfit Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Christmas_Shopping_Uniforms", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9263"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_DeepSea_Explorer_Uniforms", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9366"); WhatsNewItems.AddItem(item); -// Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas_Christmas", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9264"); +// Featured Weapon + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_ReductoRay", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9367"); WhatsNewItems.AddItem(item); -// Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas_Alchemist_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9265"); - WhatsNewItems.AddItem(item); -// Featured Cosmetic Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Xmas_Alchemist","FeaturedItemBundle","https://store.steampowered.com/buyitem/232090/9266"); +// Featured Weapon + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_Sentinel","FeaturedItemBundle","https://store.steampowered.com/buyitem/232090/9368"); WhatsNewItems.AddItem(item); // Featured Weapon Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_XMAS_Doshinegun", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9267"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_Weaponsbundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9369"); 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 601ba03..fbddd5e 100644 --- a/KFGame/Classes/KFGFxStartGameContainer_Options.uc +++ b/KFGame/Classes/KFGFxStartGameContainer_Options.uc @@ -271,11 +271,16 @@ function FilterWeeklyMaps(out array List) return; } + `Log("OPTIONS: Skipping Maps"); + // Scavenger index = 11 - // BossRush index = 14 + // BossRush index = 14 + // GunGame index = 16 WeeklyIndex = class'KFGameEngine'.static.GetWeeklyEventIndexMod(); - if (WeeklyIndex == 11 || WeeklyIndex == 14) + if (WeeklyIndex == 11 || WeeklyIndex == 14 || WeeklyIndex == 16) { + `Log("OPTIONS: Inside, removing maps"); + List.RemoveItem("KF-Biolapse"); List.RemoveItem("KF-Nightmare"); List.RemoveItem("KF-PowerCore_Holdout"); diff --git a/KFGame/Classes/KFGFxStoreContainer_Main.uc b/KFGame/Classes/KFGFxStoreContainer_Main.uc index 0e09998..a008368 100644 --- a/KFGame/Classes/KFGFxStoreContainer_Main.uc +++ b/KFGame/Classes/KFGFxStoreContainer_Main.uc @@ -447,21 +447,25 @@ DefaultProperties XboxFilterExceptions[0]="Wasteland Bundle" // Wasteland Outfit Bundle - FeaturedItemIDs[0]=7944 //Whatsnew Gold Ticket - FeaturedItemIDs[1]=9262 - FeaturedItemIDs[2]=9263 - FeaturedItemIDs[3]=9264 - FeaturedItemIDs[4]=9265 - FeaturedItemIDs[5]=9266 - FeaturedItemIDs[6]=9267 + FeaturedItemIDs[0]=8178 //Whatsnew Gold Ticket + FeaturedItemIDs[1]=9369 + FeaturedItemIDs[2]=9367 + FeaturedItemIDs[3]=9368 + FeaturedItemIDs[4]=9366 + FeaturedItemIDs[5]=9364 + FeaturedItemIDs[6]=9362 + FeaturedItemIDs[7]=9363 + FeaturedItemIDs[8]=9365 - ConsoleFeaturedItemIDs[0]=7947 //Whatsnew Gold Ticket PSN - ConsoleFeaturedItemIDs[1]=9262 - ConsoleFeaturedItemIDs[2]=9263 - ConsoleFeaturedItemIDs[3]=9264 - ConsoleFeaturedItemIDs[4]=9265 - ConsoleFeaturedItemIDs[5]=9266 - ConsoleFeaturedItemIDs[6]=9267 + ConsoleFeaturedItemIDs[0]=8181 //Whatsnew Gold Ticket PSN + ConsoleFeaturedItemIDs[1]=9369 + ConsoleFeaturedItemIDs[2]=9367 + ConsoleFeaturedItemIDs[3]=9368 + ConsoleFeaturedItemIDs[4]=9366 + ConsoleFeaturedItemIDs[5]=9364 + ConsoleFeaturedItemIDs[6]=9362 + ConsoleFeaturedItemIDs[7]=9363 + ConsoleFeaturedItemIDs[8]=9365 MaxFeaturedItems=5 } \ No newline at end of file diff --git a/KFGame/Classes/KFGFxTraderContainer_GameInfo.uc b/KFGame/Classes/KFGFxTraderContainer_GameInfo.uc index 8ec1472..47fa7e0 100644 --- a/KFGame/Classes/KFGFxTraderContainer_GameInfo.uc +++ b/KFGame/Classes/KFGFxTraderContainer_GameInfo.uc @@ -34,8 +34,11 @@ function UpdateGameInfo() } else { - // Show the upcoming wave number for every wave except the boss wave - if (KFGRI.default.bEndlessMode) + if (KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 16) + { + FinalWaveString = WaveString @ (KFGRI.GunGameWavesCurrent) $"/?"; + } + else if (KFGRI.default.bEndlessMode) // Show the upcoming wave number for every wave except the boss wave { // Show wave number, since there is no max FinalWaveString = WaveString @ (KFGRI.WaveNum); diff --git a/KFGame/Classes/KFGFxWeeklyObjectivesContainer.uc b/KFGame/Classes/KFGFxWeeklyObjectivesContainer.uc index ef01746..779b6ca 100644 --- a/KFGame/Classes/KFGFxWeeklyObjectivesContainer.uc +++ b/KFGame/Classes/KFGFxWeeklyObjectivesContainer.uc @@ -31,14 +31,30 @@ function bool PopulateData() { local GFxObject DataObject; local KFWeeklyOutbreakInformation WeeklyInfo; - local bool bWeeklyComplete; + local int WeeklyIndex; bWeeklyComplete = KFPC.IsWeeklyEventComplete(); + WeeklyIndex = -1; if(bWeeklyComplete != bLastWeeklyComplete || !bInitialDataPopulated) { - WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); + if (KFPC.WorldInfo.NetMode == NM_Client) + { + if (KFPC != none && KFGameReplicationInfo(KFPC.WorldInfo.GRI) != none) + { + WeeklyIndex = KFGameReplicationInfo(KFPC.WorldInfo.GRI).CurrentWeeklyIndex; + WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetWeeklyOutbreakInfoByIndex(WeeklyIndex); + } + else + { + GetPC().SetTimer(0.5f, false, nameof(PopulateData)); + } + } + else + { + WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); + } DataObject = CreateObject("Object"); if(WeeklyInfo == none) @@ -46,8 +62,9 @@ function bool PopulateData() return false; } DataObject.SetString("label", WeeklyInfo.FriendlyName); - if(WeeklyInfo != none && WeeklyInfo.ModifierDescriptions.length > 0) + if(WeeklyInfo.ModifierDescriptions.length > 0) { + `Log("SETTING DESCRIPTION: " $WeeklyInfo.DescriptionStrings[0]); DataObject.SetString("description", WeeklyInfo.DescriptionStrings[0]); } DataObject.SetString("iconPath", "img://"$WeeklyInfo.IconPath); @@ -58,8 +75,14 @@ function bool PopulateData() DataObject.SetString("textValue", ""); SetObject("weeklyObjectiveData", DataObject); - PopulateModifiers(); - PopulateRewards(); + + if (WeeklyInfo.ModifierDescriptions.Length > 0) + { + SetString("weeklyDescription", WeeklyInfo.ModifierDescriptions[0]); + } + + PopulateModifiers(WeeklyInfo); + PopulateRewards(WeeklyInfo, WeeklyIndex); bLastWeeklyComplete = bWeeklyComplete; bInitialDataPopulated = true; @@ -69,24 +92,26 @@ function bool PopulateData() return false; } -function PopulateModifiers() +function PopulateModifiers(KFWeeklyOutbreakInformation WeeklyInfo) { local int i; local GFxObject DataObject; local GFxObject DataProvider; //array containing the data objects - local KFWeeklyOutbreakInformation WeeklyInfo; + + if (WeeklyInfo == none || (GetPC().WorldInfo.NetMode == NM_Client && KFPC.WorldInfo.GRI == none)) + { + return; + } DataProvider = CreateArray(); - - WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); + for (i = 0; i < WeeklyInfo.ModifierDescriptions.length; i++) { DataObject = CreateObject("Object"); DataObject.SetString("label", ""); //no lable at the moment - if(WeeklyInfo != none && WeeklyInfo.ModifierDescriptions.length > 0) - { - DataObject.SetString("description", WeeklyInfo.ModifierDescriptions[i]); - } + + DataObject.SetString("description", WeeklyInfo.ModifierDescriptions[i]); + //DataObject.SetString("iconPath", "img://"$WeeklyInfo.ModifierIconPaths[i]); DataProvider.SetElementObject(i, DataObject); //add it to the array @@ -95,18 +120,21 @@ function PopulateModifiers() SetObject("modifiers", DataProvider); //pass to SWF } -function PopulateRewards() +function PopulateRewards(KFWeeklyOutbreakInformation WeeklyInfo, int WeeklyIndex) { local int i, ItemCount; local GFxObject DataProvider; //array containing the data objects - local KFWeeklyOutbreakInformation WeeklyInfo; local GFxObject GfxRewardItem; + if (WeeklyInfo == none) + { + return; + } + ItemCount = 0; DataProvider = CreateArray(); - - WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); - WeeklyInfo.RewardIDs = class'KFOnlineStatsWrite'.static.GetWeeklyOutbreakRewards(); + + WeeklyInfo.RewardIDs = class'KFOnlineStatsWrite'.static.GetWeeklyOutbreakRewards(WeeklyIndex); for (i = 0; i < WeeklyInfo.RewardIDs.length; i++) { GfxRewardItem = CreateRewardItem(WeeklyInfo, WeeklyInfo.RewardIDs[i]); @@ -161,9 +189,9 @@ function GFxObject CreateRewardItem(KFWeeklyOutbreakInformation WeeklyInfo,int I function LocalizeMenu() { local GFxObject TextObject; - local KFWeeklyOutbreakInformation WeeklyInfo; +// local KFWeeklyOutbreakInformation WeeklyInfo; - WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); +// WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); TextObject = CreateObject("Object"); // Localize static text TextObject.SetString("currentModifier", class'KFMission_LocalizedStrings'.default.CurrentWeeklySettingsString); @@ -172,11 +200,11 @@ function LocalizeMenu() TextObject.SetString("weekly", class'KFMission_LocalizedStrings'.default.WeeklyString); TextObject.SetString("overview", class'KFMission_LocalizedStrings'.default.WeeklyOverview); TextObject.SetString("vaultDosh", class'KFMission_LocalizedStrings'.default.VaultDoshString); - +/* if(WeeklyInfo != none && WeeklyInfo.ModifierDescriptions.length > 0) { TextObject.SetString("description", WeeklyInfo.ModifierDescriptions[0]); } - +*/ SetObject("localizedText", TextObject); -} \ No newline at end of file +} diff --git a/KFGame/Classes/KFGFxWidget_GunGame.uc b/KFGame/Classes/KFGFxWidget_GunGame.uc new file mode 100644 index 0000000..178dadc --- /dev/null +++ b/KFGame/Classes/KFGFxWidget_GunGame.uc @@ -0,0 +1,36 @@ + + +//============================================================================= +// KFGFxWidget_GunGame +//============================================================================= +// HUD Widget that displays gun game messages to the player +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= + +class KFGFxWidget_GunGame extends GFxObject; + +function SetData( int score, int max_score, int level, int max_level ) +{ + SetString("WeaponLevelSetLocalised", Class'KFCommon_LocalizedStrings'.default.WeaponLevelString); + SetString("GunPointsSetLocalised", Class'KFCommon_LocalizedStrings'.default.GunPointsString); + + SetInt("GunGameSetLevel", level); + SetInt("GunGameSetMaxLevel", max_level); + SetInt("GunGameSetScore", score); + SetInt("GunGameSetMaxScore", max_score); +} + +function UpdateGunGameVisibility(bool visible) +{ + if (visible) + { + SetBool("GunGameSetVisibility", true); + } + else + { + SetBool("GunGameSetVisibility", false); + } +} \ No newline at end of file diff --git a/KFGame/Classes/KFGFxWidget_PartyInGame.uc b/KFGame/Classes/KFGFxWidget_PartyInGame.uc index 365d35f..6e7c7e8 100644 --- a/KFGame/Classes/KFGFxWidget_PartyInGame.uc +++ b/KFGame/Classes/KFGFxWidget_PartyInGame.uc @@ -84,12 +84,15 @@ function UpdateReadyButtonVisibility() //sanity check because this is happening MyKFPRI = KFPlayerReplicationInfo(GetPC().PlayerReplicationInfo); } + if(bReadyButtonVisible) { KFGRI = KFGameReplicationInfo( GetPC().WorldInfo.GRI ); if ( KFGRI != none ) { - if (KFGRI.bMatchHasBegun && (MyKFPRI != none && MyKFPRI.bHasSpawnedIn && KFGRI.bTraderIsOpen) && !KFGRI.bMatchIsOver && MyKFPRI.GetTeamNum() != 255 ) + if (KFGRI.bMatchHasBegun + && (MyKFPRI != none && MyKFPRI.bHasSpawnedIn && (KFGRI.bTraderIsOpen || KFGRI.bForceSkipTraderUI)) + && !KFGRI.bMatchIsOver && MyKFPRI.GetTeamNum() != 255) { bShowingSkipTrader = !MyKFPRI.bVotedToSkipTraderTime; if (bShowingSkipTrader && !ReadyButton.GetBool("visible")) diff --git a/KFGame/Classes/KFGameInfo.uc b/KFGame/Classes/KFGameInfo.uc index ec60a29..42128de 100644 --- a/KFGame/Classes/KFGameInfo.uc +++ b/KFGame/Classes/KFGameInfo.uc @@ -442,6 +442,14 @@ var int SpawnedMonsterProperties[EMonsterProperties]; */ var transient array< byte > BossRushEnemies; +// Maps to skip +const MapBiolapse = 'KF-Biolapse'; +const MapNightmare = 'KF-Nightmare'; +const MapPowerCore = 'KF-PowerCore_Holdout'; +const MapDescent = 'KF-TheDescent'; +const MapKrampus = 'KF-KrampusLair'; +const MapSteam = 'KF-SteamFortress'; + /************************************************************************************ * @name Native ***********************************************************************************/ @@ -1193,14 +1201,7 @@ function ResetPickups( array PickupList, int NumPickups ) PossiblePickups = PickupList; - if (OutbreakEvent != none && OutbreakEvent.ActiveEvent.bUnlimitedWeaponPickups && KFPickupFactory_Item(PickupList[0]) != none) - { - NumIterations = Min(NumPickups, PickupList.Length - 1); - } - else - { - NumIterations = Min(NumPickups, PickupList.Length); - } + NumIterations = Min(NumPickups, PickupList.Length - 1); for ( i = 0; i < NumIterations; i++ ) { @@ -1267,8 +1268,15 @@ function ActivateNextPickup( KFPickupFactory NextFactory, int RespawnDelay ) { if( NextFactory != none ) { - NextFactory.bToBeActivated = true; - NextFactory.SetTimer( RespawnDelay, , nameof(NextFactory.Reset) ); + if (NextFactory.CanUsePickup()) + { + NextFactory.bToBeActivated = true; + NextFactory.SetTimer( RespawnDelay, , nameof(NextFactory.Reset) ); + } + else + { + NextFactory.SetPickupHidden(); + } } } @@ -1421,6 +1429,14 @@ function RestartPlayer(Controller NewPlayer) } } +function ResetGunGame(KFPlayerController_WeeklySurvival KFPC_WS) +{ +} + +function RestartGunGamePlayerWeapon(KFPlayerController_WeeklySurvival KFPC_WS, byte WaveToUse) +{ +} + /** Returns a low rating if other players are nearby (spawn will fail) */ function float RatePlayerStart(PlayerStart P, byte Team, Controller Player) { @@ -2895,62 +2911,69 @@ function string GetNextMap() { local array MapList; local int i; + local name MapName; - if ( bUseMapList && GameMapCycles.Length > 0 ) + if ( bUseMapList && GameMapCycles.Length > 0 ) + { + if ( MapCycleIndex == INDEX_NONE ) { + MapList = GameMapCycles[ActiveMapCycle].Maps; + MapCycleIndex = GetCurrentMapCycleIndex(MapList); if ( MapCycleIndex == INDEX_NONE ) { - MapList = GameMapCycles[ActiveMapCycle].Maps; - MapCycleIndex = GetCurrentMapCycleIndex(MapList); - if ( MapCycleIndex == INDEX_NONE ) - { - // Assume current map is actually zero - MapCycleIndex = 0; - } + // Assume current map is actually zero + MapCycleIndex = 0; } + } - for (i = 0; i < GameMapCycles[ActiveMapCycle].Maps.length; ++i) + for (i = 0; i < GameMapCycles[ActiveMapCycle].Maps.length; ++i) + { + MapCycleIndex = MapCycleIndex + 1 < GameMapCycles[ActiveMapCycle].Maps.length ? (MapCycleIndex + 1) : 0; + + /** + This could be changed to read replicated values instead of engine ones. + */ + if (MyKFGRI.IsA('KFGameReplicationInfo_WeeklySurvival')) { - MapCycleIndex = MapCycleIndex + 1 < GameMapCycles[ActiveMapCycle].Maps.length ? (MapCycleIndex + 1) : 0; - - if (MyKFGRI.IsA('KFGameReplicationInfo_WeeklySurvival')) + if ((class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 11 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[11]) || // Scavenger + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 14 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[14]) || // Boss Rush + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 16 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[16])) // Gun Game { - if ((class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 11 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[11]) || // Scavenger - (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 14 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[14])) // Boss Rush - { - if (GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-Biolapse" || - GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-Nightmare" || - GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-PowerCore_Holdout" || - GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-TheDescent" || - GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-KrampusLair") - { - continue; - } - } + MapName = name(GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]); - /* Temporary removal of SteamFrotress for BossRush */ - if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 14 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[14] && - GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex] == "KF-SteamFortress") + if (MapName == MapBiolapse || + MapName == MapNightmare || + MapName == MapPowerCore || + MapName == MapDescent || + MapName == MapKrampus) { continue; } - /**/ } - if ( IsMapAllowedInCycle(GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]) ) + /* Temporary removal of SteamFrotress for BossRush */ + if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 14 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[14] && + MapName == MapSteam) { - SaveConfig(); - return GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]; + continue; } + /* */ } - return string(WorldInfo.GetPackageName()); - } - else - { - return string(WorldInfo.GetPackageName()); + if ( IsMapAllowedInCycle(GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]) ) + { + SaveConfig(); + return GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]; + } } + return string(WorldInfo.GetPackageName()); + } + else + { + return string(WorldInfo.GetPackageName()); + } + return ""; } @@ -3781,14 +3804,22 @@ function UpdateCurrentMapVoteTime(byte NewTime, optional bool bStartTime); function float GetTraderTime() { local float MapOverride; + local float TraderTimeModifier; + + TraderTimeModifier = 1.f; + + if (OutbreakEvent != none && OutbreakEvent.ActiveEvent.TraderTimeModifier != 1.f) + { + TraderTimeModifier = OutbreakEvent.ActiveEvent.TraderTimeModifier; + } MapOverride = DifficultyInfo.GetTraderTimeByMap(WorldInfo.GetMapName(true)); if (MapOverride > 0.f) { - return MapOverride; + return MapOverride * TraderTimeModifier; } - return DifficultyInfo.GetTraderTimeByDifficulty(); + return DifficultyInfo.GetTraderTimeByDifficulty() * TraderTimeModifier; } diff --git a/KFGame/Classes/KFGameReplicationInfo.uc b/KFGame/Classes/KFGameReplicationInfo.uc index f5001f6..77e8c53 100644 --- a/KFGame/Classes/KFGameReplicationInfo.uc +++ b/KFGame/Classes/KFGameReplicationInfo.uc @@ -60,6 +60,7 @@ var repnotify bool bTraderIsOpen; var repnotify bool bWaveIsActive; var repnotify bool bWaveStarted; var bool bIsEndlessPaused; +var bool bForceSkipTraderUI; /** Replicates at beginning and end of waves to change track / track type */ var repnotify byte MusicTrackRepCount; @@ -107,6 +108,8 @@ var repnotify PerkAvailableData PerksAvailableData; var byte WaveMax; // The "end" wave var repnotify byte WaveNum; // The wave we are currently in var bool bWaveIsEndless; +var repnotify byte GunGameWavesCurrent; +var repnotify bool bWaveGunGameIsFinal; var int AIRemaining; var int WaveTotalAICount; var bool bEndlessMode; @@ -358,6 +361,9 @@ var transient bool bIsBrokenTrader; ************************************/ var int CurrentWeeklyIndex; +/** If true, force show skip time between waves ready button */ +var bool bForceShowSkipTrader; + /************************************ * Steam heartbeat ************************************/ @@ -389,11 +395,12 @@ cpptext replication { if ( bNetDirty ) - TraderVolume, TraderVolumeCheckType, bTraderIsOpen, NextTrader, WaveNum, bWaveIsEndless, AIRemaining, WaveTotalAICount, bWaveIsActive, MaxHumanCount, bGlobalDamage, + TraderVolume, TraderVolumeCheckType, bTraderIsOpen, NextTrader, WaveNum, bWaveIsEndless, GunGameWavesCurrent, bWaveGunGameIsFinal, AIRemaining, WaveTotalAICount, bWaveIsActive, MaxHumanCount, bGlobalDamage, CurrentObjective, PreviousObjective, PreviousObjectiveResult, PreviousObjectiveXPResult, PreviousObjectiveVoshResult, MusicIntensity, ReplicatedMusicTrackInfo, MusicTrackRepCount, - bIsUnrankedGame, GameSharedUnlocks, bHidePawnIcons, ConsoleGameSessionGuid, GameDifficulty, GameDifficultyModifier, BossIndex, bWaveStarted, NextObjective, bIsBrokenTrader, bIsWeeklyMode, CurrentWeeklyIndex, bIsEndlessPaused; //@HSL - JRO - 3/21/2016 - PS4 Sessions + bIsUnrankedGame, GameSharedUnlocks, bHidePawnIcons, ConsoleGameSessionGuid, GameDifficulty, GameDifficultyModifier, BossIndex, bWaveStarted, NextObjective, bIsBrokenTrader, bIsWeeklyMode, + CurrentWeeklyIndex, bIsEndlessPaused, bForceSkipTraderUI; //@HSL - JRO - 3/21/2016 - PS4 Sessions if ( bNetInitial ) - GameLength, WaveMax, bCustom, bVersusGame, TraderItems, GameAmmoCostScale, bAllowGrenadePurchase, MaxPerkLevel, bTradersEnabled; + GameLength, WaveMax, bCustom, bVersusGame, TraderItems, GameAmmoCostScale, bAllowGrenadePurchase, MaxPerkLevel, bTradersEnabled, bForceShowSkipTrader; if ( bNetInitial || bNetDirty ) PerksAvailableData; if ( bNetInitial && Role == ROLE_Authority ) @@ -543,6 +550,14 @@ simulated event ReplicatedEvent(name VarName) { UpdatePerksAvailable(); } + else if (VarName == 'GunGameWavesCurrent') + { + UpdateHUDWaveCount(); + } + else if (VarName == 'bWaveGunGameIsFinal') + { + UpdateHUDWaveCount(); + } else { super.ReplicatedEvent(VarName); @@ -797,7 +812,7 @@ simulated function EndGame() bMatchHasBegun = false; bMatchIsOver = true; - class'KFGameEngine'.static.RefreshOnlineGameData(false); + class'KFGameEngine'.static.RefreshOnlineGameData(true); } /* Welcome screen shenanigans */ @@ -1111,6 +1126,7 @@ function SetWaveActive(bool bWaveActive, optional byte NewMusicIntensity) // set up music intensity for this wave MusicIntensity = NewMusicIntensity; bTraderIsOpen = !bWaveActive && bMatchHasBegun && bTradersEnabled; + bForceSkipTraderUI = !bWaveActive && bMatchHasBegun && bForceShowSkipTrader; bWaveIsActive = bWaveActive; bForceNetUpdate = true; @@ -2029,21 +2045,25 @@ function int SetNextObjective(array PossibleObjectives while (PossibleObjectives.Length > 0) { RandID = Rand(PossibleObjectives.Length); - PctChanceToActivate = PossibleObjectives[RandID].GetActivationPctChance(); - if (bForceNextObjective || (PossibleObjectives[RandID].CanActivateObjective() && PreviousObjective != PossibleObjectives[RandID] && (PctChanceToActivate >= 1.f || DieRoll <= PctChanceToActivate))) - { - if (bActivateImmediately) + + if (PossibleObjectives[RandID].CanActivateObjectiveByWeekly()) + { + PctChanceToActivate = PossibleObjectives[RandID].GetActivationPctChance(); + if (bForceNextObjective || (PossibleObjectives[RandID].CanActivateObjective() && PreviousObjective != PossibleObjectives[RandID] && (PctChanceToActivate >= 1.f || DieRoll <= PctChanceToActivate))) { - ActivateObjective(PossibleObjectives[RandID], bUseEndlessSpawning); + if (bActivateImmediately) + { + ActivateObjective(PossibleObjectives[RandID], bUseEndlessSpawning); + } + else + { + NextObjective = Actor(PossibleObjectives[RandID]); + NextObjectiveIsEndless = bUseEndlessSpawning; + KFInterface_MapObjective(NextObjective).NotifyObjectiveSelected(); + } + return RandID; } - else - { - NextObjective = Actor(PossibleObjectives[RandID]); - NextObjectiveIsEndless = bUseEndlessSpawning; - KFInterface_MapObjective(NextObjective).NotifyObjectiveSelected(); - } - return RandID; - } + } PossibleObjectives.Remove(RandID, 1); } @@ -2268,4 +2288,8 @@ defaultproperties PreviousObjectiveXPResult=-1 bIsBrokenTrader=false bIsWeeklyMode=false + bForceShowSkipTrader=false + bForceSkipTraderUI=false + GunGameWavesCurrent=1 + bWaveGunGameIsFinal=false } diff --git a/KFGame/Classes/KFGfxMenu_StartGame.uc b/KFGame/Classes/KFGfxMenu_StartGame.uc index af5d867..bda8db5 100644 --- a/KFGame/Classes/KFGfxMenu_StartGame.uc +++ b/KFGame/Classes/KFGfxMenu_StartGame.uc @@ -204,7 +204,7 @@ static function class GetSpecialEventClass case SEI_Spring: return class'KFGFxSpecialEventObjectivesContainer_Spring2021'; case SEI_Summer: - return class'KFGFxSpecialEventObjectivesContainer_Summer2021'; + return class'KFGFxSpecialEventObjectivesContainer_Summer2022'; case SEI_Fall: return class'KFGFxSpecialEventObjectivesContainer_Fall2021'; case SEI_Winter: diff --git a/KFGame/Classes/KFGfxMoviePlayer_Manager.uc b/KFGame/Classes/KFGfxMoviePlayer_Manager.uc index 7625bca..eeb53a0 100644 --- a/KFGame/Classes/KFGfxMoviePlayer_Manager.uc +++ b/KFGame/Classes/KFGfxMoviePlayer_Manager.uc @@ -1866,7 +1866,7 @@ event bool FilterButtonInput(int ControllerId, name ButtonName, EInputEvent Inpu PartyWidget.ReadyButton.SetBool("selected", KFPRI.bReadyToPlay); } - else if(KFPRI.bHasSpawnedIn && !KFGRI.bMatchIsOver && KFGRI.bMatchHasBegun && KFGRI.bTraderIsOpen && CurrentMenu != TraderMenu && bMenusOpen) + else if(KFPRI.bHasSpawnedIn && !KFGRI.bMatchIsOver && KFGRI.bMatchHasBegun && (KFGRI.bTraderIsOpen || KFGRI.bForceSkipTraderUI) && CurrentMenu != TraderMenu && bMenusOpen) { bForceCloseMenuNextTime = true; CurrentMenu.Callback_ReadyClicked(true); diff --git a/KFGame/Classes/KFGfxObject_Menu.uc b/KFGame/Classes/KFGfxObject_Menu.uc index b963b68..a638d86 100644 --- a/KFGame/Classes/KFGfxObject_Menu.uc +++ b/KFGame/Classes/KFGfxObject_Menu.uc @@ -234,7 +234,7 @@ function Callback_ReadyClicked( bool bReady ) if (KFGRI.bMatchHasBegun) { //player has spawned, skip trader time - if (KFGRI.bTraderIsOpen && KFPRI.bHasSpawnedIn) + if ((KFGRI.bTraderIsOpen || KFGRI.bForceSkipTraderUI) && KFPRI.bHasSpawnedIn) { if (KFPC.MyGFxManager.bMenusOpen && KFPC.MyGFxManager.CurrentMenu != KFPC.MyGFxManager.TraderMenu) { @@ -506,6 +506,26 @@ function Callback_ChatFocusOut() Manager.UpdateDynamicIgnoreKeys(); } +function Callback_OnLoadoutPrevWeaponPressed() +{ + Manager.PerksMenu.OnPrevWeaponPressed(); +} + +function Callback_OnLoadoutNextWeaponPressed() +{ + Manager.PerksMenu.OnNextWeaponPressed(); +} + +function Callback_OnLoadoutPrevGrenadePressed() +{ + Manager.PerksMenu.OnPrevGrenadePressed(); +} + +function Callback_OnLoadoutNextGrenadePressed() +{ + Manager.PerksMenu.OnNextGrenadePressed(); +} + /** RETURN TO THESE FUNCTIONS */ /************************************************************************ diff --git a/KFGame/Classes/KFImpactEffectManager.uc b/KFGame/Classes/KFImpactEffectManager.uc index ed1304f..e81a9e9 100644 --- a/KFGame/Classes/KFImpactEffectManager.uc +++ b/KFGame/Classes/KFImpactEffectManager.uc @@ -92,7 +92,7 @@ simulated function PlayImpactEffects(const vector HitLocation, const Pawn Effect } } - bIsWeaponHandlingEffects = KFPawn(EffectInstigator).MyKFWeapon.bForceHandleImpacts; + bIsWeaponHandlingEffects = KFPawn(EffectInstigator).MyKFWeapon != none ? KFPawn(EffectInstigator).MyKFWeapon.bForceHandleImpacts : false; // Trace using the Instigator as the TraceOwner so that melee weapons don't collide with Instigator HitActor = EffectInstigator.Trace(NewHitLoc, HitNormal, (HitLocation - (HitNormal * 32)), HitLocation + (HitNormal * 32), !bWorldImpactsOnly,, HitInfo, TRACEFLAG_Bullet); diff --git a/KFGame/Classes/KFInterface_MapObjective.uc b/KFGame/Classes/KFInterface_MapObjective.uc index af37d2f..afd23f5 100644 --- a/KFGame/Classes/KFInterface_MapObjective.uc +++ b/KFGame/Classes/KFInterface_MapObjective.uc @@ -11,6 +11,7 @@ interface KFInterface_MapObjective; // Status +simulated function bool CanActivateObjectiveByWeekly(); simulated function ActivateObjective(); simulated function DeactivateObjective(); simulated function GrantReward(KFPlayerReplicationInfo KFPRI, KFPlayerController KFPC); diff --git a/KFGame/Classes/KFInventoryManager.uc b/KFGame/Classes/KFInventoryManager.uc index e0a6c60..e1fd5d0 100644 --- a/KFGame/Classes/KFInventoryManager.uc +++ b/KFGame/Classes/KFInventoryManager.uc @@ -495,6 +495,130 @@ function bool ClassNameIsInInventory(name ItemClassName, out Inventory out_Inven return false; } +/** + * returns the best weapon for this Pawn in loadout + */ +simulated function Weapon GetBestWeapon( optional bool bForceADifferentWeapon, optional bool allow9mm ) +{ + local KFWeapon W, BestWeapon; + local float Rating, BestRating; + + ForEach InventoryActors( class'KFWeapon', W ) + { + if( w.HasAnyAmmo() ) + { + if( bForceADifferentWeapon && + W == Instigator.Weapon ) + { + continue; + } + + Rating = W.GetWeaponRating(); + if( BestWeapon == None || + Rating > BestRating ) + { + if (allow9mm == false) + { + if (W.bIsBackupWeapon && !W.IsMeleeWeapon()) + { + continue; + } + } + + BestWeapon = W; + BestRating = Rating; + } + } + } + + if (BestWeapon == none && allow9mm == false) + { + ForEach InventoryActors( class'KFWeapon', W ) + { + if( w.HasAnyAmmo() ) + { + if( bForceADifferentWeapon && + W == Instigator.Weapon ) + { + continue; + } + + Rating = W.GetWeaponRating(); + if( BestWeapon == None || + Rating > BestRating ) + { + BestWeapon = W; + BestRating = Rating; + } + } + } + } + + return BestWeapon; +} + +/** + * Switch to best weapon available in loadout + * Network: LocalPlayer + */ +simulated function SwitchToBestWeapon( optional bool bForceADifferentWeapon, optional bool check_9mm_logic = false ) +{ + local Weapon BestWeapon; + local PlayerController PC; + local KFPlayerInput KFPI; + local bool bCanSwapTo9mm; + + if (check_9mm_logic) + { + // Default behaviour is you can't swap to 9mm + bCanSwapTo9mm = false; + + PC = PlayerController(Instigator.Controller); + if ( PC != None ) + { + KFPI = KFPlayerInput(PC.PlayerInput); + if (KFPI != None) + { + bCanSwapTo9mm = KFPI.bAllowSwapTo9mm; + } + } + } + else + { + bCanSwapTo9mm = true; + } + + `LogInv("bForceADifferentWeapon:" @ bForceADifferentWeapon); + + // if we don't already have a pending weapon, + if( bForceADifferentWeapon || + PendingWeapon == None || + (AIController(Instigator.Controller) != None) ) + { + // figure out the new weapon to bring up + BestWeapon = GetBestWeapon( bForceADifferentWeapon, bCanSwapTo9mm ); + + if( BestWeapon == None ) + { + return; + } + + // if it matches our current weapon then don't bother switching + if( BestWeapon == Instigator.Weapon ) + { + BestWeapon = None; + PendingWeapon = None; + Instigator.Weapon.Activate(); + } + } + + // stop any current weapon fire + Instigator.Controller.StopFiring(); + + // and activate the new pending weapon + SetCurrentWeapon(BestWeapon); +} + /** * Switches to Previous weapon * Network: Client @@ -943,7 +1067,7 @@ reliable client function SetCurrentWeapon(Weapon DesiredWeapon) local KFWeapon DesiredKFW; local KFWeapon PendingKFW; - CurrentKFW = KFWeapon(Instigator.Weapon); + CurrentKFW = Instigator != none ? KFWeapon(Instigator.Weapon) : none; if ( CurrentKFW != none ) { // Set the flag to switch to ironsights when the weapon is brought up @@ -963,6 +1087,7 @@ reliable client function SetCurrentWeapon(Weapon DesiredWeapon) // Only change your weapon if it is different or we weant to equip the weapon we are currently putting down DesiredKFW = KFWeapon(DesiredWeapon); if( DesiredKFW != none && + Instigator != none && (DesiredKFW != Instigator.Weapon || Instigator.Weapon.IsInState('WeaponPuttingDown')) ) { if ( DesiredKFW.bHasIronSights ) diff --git a/KFGame/Classes/KFMapObjective_ActorBase.uc b/KFGame/Classes/KFMapObjective_ActorBase.uc index 91a55a1..819aa51 100644 --- a/KFGame/Classes/KFMapObjective_ActorBase.uc +++ b/KFGame/Classes/KFMapObjective_ActorBase.uc @@ -63,6 +63,11 @@ simulated function PlayActivationSoundEvent() } } +simulated function bool CanActivateObjectiveByWeekly() +{ + return true; +} + // Status simulated function ActivateObjective() { diff --git a/KFGame/Classes/KFMapObjective_VolumeBase.uc b/KFGame/Classes/KFMapObjective_VolumeBase.uc index d8a6b75..e33761c 100644 --- a/KFGame/Classes/KFMapObjective_VolumeBase.uc +++ b/KFGame/Classes/KFMapObjective_VolumeBase.uc @@ -158,6 +158,11 @@ simulated function ActivateBoundarySplines() } } +simulated function bool CanActivateObjectiveByWeekly() +{ + return true; +} + // Status simulated function ActivateObjective() { diff --git a/KFGame/Classes/KFOnlineStatsReadDingo.uc b/KFGame/Classes/KFOnlineStatsReadDingo.uc index e7538b4..5b29a48 100644 --- a/KFGame/Classes/KFOnlineStatsReadDingo.uc +++ b/KFGame/Classes/KFOnlineStatsReadDingo.uc @@ -70,6 +70,7 @@ defaultproperties ColumnIds.Add(STATID_ACHIEVE_MoonbaseCollectibles) ColumnIds.Add(STATID_ACHIEVE_NetherholdCollectibles) ColumnIds.Add(STATID_ACHIEVE_CarillonHamletCollectibles) + ColumnIds.Add(STATID_ACHIEVE_RigCollectibles) ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky5, Name="AchievementMrPerky5")) ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky10, Name = "AchievementMrPerky10")) @@ -124,8 +125,9 @@ defaultproperties ColumnMappings.Add((Id=STATID_ACHIEVE_DesolationCollectibles,Name="AchievementCollectDesolation")) ColumnMappings.Add((Id=STATID_ACHIEVE_HellmarkStationCollectibles,Name="AchievementCollectHellmarkStation")) ColumnMappings.Add((Id=STATID_ACHIEVE_ElysiumEndlessWaveFifteen,Name="AchievementEndlessElysium")) - ColumnMappings.Add((Id=STATID_ACHIEVE_Dystopia2029Collectibles,NAme="AchievementCollectDystopia2029")) - ColumnMappings.Add((Id=STATID_ACHIEVE_MoonbaseCollectibles,NAme="AchievementCollectMoonbase")) - ColumnMappings.Add((Id=STATID_ACHIEVE_NetherholdCollectibles,NAme="AchievementCollectNetherhold")) - ColumnMappings.Add((Id=STATID_ACHIEVE_CarillonHamletCollectibles,NAme="AchievementCollectCarillonHamlet")) + ColumnMappings.Add((Id=STATID_ACHIEVE_Dystopia2029Collectibles,Name="AchievementCollectDystopia2029")) + ColumnMappings.Add((Id=STATID_ACHIEVE_MoonbaseCollectibles,Name="AchievementCollectMoonbase")) + ColumnMappings.Add((Id=STATID_ACHIEVE_NetherholdCollectibles,Name="AchievementCollectNetherhold")) + ColumnMappings.Add((Id=STATID_ACHIEVE_CarillonHamletCollectibles,Name="AchievementCollectCarillonHamlet")) + ColumnMappings.Add((Id=STATID_ACHIEVE_RigCollectibles,Name="AchievementCollectRig")) } diff --git a/KFGame/Classes/KFOnlineStatsWrite.uc b/KFGame/Classes/KFOnlineStatsWrite.uc index 4948473..637e6b4 100644 --- a/KFGame/Classes/KFOnlineStatsWrite.uc +++ b/KFGame/Classes/KFOnlineStatsWrite.uc @@ -441,6 +441,10 @@ const KFACHID_CarillonHamletHard = 289; const KFACHID_CarillonHamletHellOnEarth = 290; const KFACHID_CarillonHamletCollectibles = 291; +const KFACHID_RigHard = 292; +const KFACHID_RigHellOnEarth = 293; +const KFACHID_RigCollectibles = 294; + /* __TW_ANALYTICS_ */ var int PerRoundWeldXP; var int PerRoundHealXP; @@ -2078,6 +2082,7 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Thrown_C4, KFDT_Explosive_C4,KFDT_Bludgeon_C4),CompletionAmount=2500)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_GrenadeLauncher_M79, KFDT_Ballistic_M79Impact,KFDT_Explosive_M79,KFDT_Bludgeon_M79),CompletionAmount=7000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_Boomy, KFDT_Ballistic_HRG_Boomy,KFDT_Explosive_HRG_Boomy,KFDT_Bludgeon_HRG_Boomy),CompletionAmount=7000)) + DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_Crossboom, KFDT_Piercing_HRG_Crossboom,KFDT_Explosive_HRG_Crossboom,KFDT_Explosive_HRG_CrossboomAlt,KFDT_Bludgeon_HRG_Crossboom),CompletionAmount=7000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Shotgun_HRG_Kaboomstick, KFDT_Ballistic_HRG_Kaboomstick,KFDT_Explosive_HRG_Kaboomstick,KFDT_Bludgeon_HRG_Kaboomstick),CompletionAmount=9000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_RocketLauncher_RPG7, KFDT_Ballistic_RPG7Impact,KFDT_Explosive_RPG7,KFDT_Explosive_RPG7BackBlast,KFDT_Bludgeon_RPG7),CompletionAmount=7500)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_AssaultRifle_M16M203, KFDT_Ballistic_M16M203,KFDT_Bludgeon_M16M203,KFDT_Ballistic_M203Impact,KFDT_Explosive_M16M203),CompletionAmount=9000)) //7000 @@ -2124,6 +2129,7 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Bow_Crossbow, KFDT_Bludgeon_Crossbow,KFDT_Piercing_Crossbow),CompletionAmount=7000)) //5000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Rifle_M14EBR, KFDT_Bludgeon_M14EBR,KFDT_Ballistic_M14EBR),CompletionAmount=9000)) //7000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_SonicGun, KFDT_Bludgeon_HRG_SonicGun, KFDT_Ballistic_HRG_SonicGun_SonicBlastUncharged, KFDT_Ballistic_HRG_SonicGun_SonicBlastHalfCharged, KFDT_Ballistic_HRG_SonicGun_SonicBlastFullyCharged),CompletionAmount=7000)) + DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_CranialPopper, KFDT_Bludgeon_HRG_CranialPopper,KFDT_Piercing_HRG_CranialPopper,KFDT_Blast_HRG_CranialPopper),CompletionAmount=7000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Rifle_RailGun, KFDT_Bludgeon_RailGun,KFDT_Ballistic_RailGun),CompletionAmount=5000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Rifle_CenterfireMB464, KFDT_Bludgeon_CenterfireMB464,KFDT_Ballistic_CenterfireMB464),CompletionAmount=7000)) //5000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Rifle_M99, KFDT_Bludgeon_M99,KFDT_Ballistic_M99),CompletionAmount=5000)) @@ -2277,6 +2283,9 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-CARILLONHAMLET),CompletionAmount=1)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-CARILLONHAMLET),CompletionAmount=2)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-CARILLONHAMLET),CompletionAmount=3)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-RIG),CompletionAmount=1)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-RIG),CompletionAmount=2)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-RIG),CompletionAmount=3)) //Versus Damage // Per design doc that I have right now, these are x class damage y players, not damage y amount diff --git a/KFGame/Classes/KFOnlineStatsWriteDingo.uc b/KFGame/Classes/KFOnlineStatsWriteDingo.uc index 8c82196..65ce0d3 100644 --- a/KFGame/Classes/KFOnlineStatsWriteDingo.uc +++ b/KFGame/Classes/KFOnlineStatsWriteDingo.uc @@ -72,4 +72,5 @@ defaultproperties Properties.Add((PropertyId = STATID_ACHIEVE_MoonbaseCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_NetherholdCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_CarillonHamletCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) + Properties.Add((PropertyId = STATID_ACHIEVE_RigCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) } diff --git a/KFGame/Classes/KFOutbreakEvent.uc b/KFGame/Classes/KFOutbreakEvent.uc index 9a5934c..a879439 100644 --- a/KFGame/Classes/KFOutbreakEvent.uc +++ b/KFGame/Classes/KFOutbreakEvent.uc @@ -99,6 +99,10 @@ struct StatAdjustments /** WeakPoints to show special VFX*/ var() array WeakPoints; + /** Score given when killed on GunGame weekly */ + var() byte GunGameKilledScore; + var() byte GunGameAssistanceScore; + structdefaultproperties { HealthScale = 1.f; @@ -118,6 +122,8 @@ struct StatAdjustments DoshGiven=INDEX_NONE InitialGroundSpeedModifierScale = 1.0 + GunGameKilledScore = 0; + GunGameAssistanceScore = 0; } }; @@ -140,6 +146,24 @@ struct BossRushOverrides var() array PerWaves; }; +struct GunGameLevel +{ + var() int RequiredScore; + var() array< class > GrantedWeapons; +}; + +struct GunGameRespawnLevel +{ + var() int Wave; + var() int Level; +}; + +struct GunGamePerkData +{ + var() array GunGameLevels; + var() array GunGameRespawnLevels; +}; + /** Individual property overrides that drive other behavior to allow for * a large amount of variety in our weekly event mode. */ @@ -210,6 +234,9 @@ struct WeeklyOverrides /** Whether or not to skip opening of the trader */ var() bool bDisableTraders; + /** Whether or not to force show skip trader button */ + var() bool bForceShowSkipTrader; + /** When to reset pickups */ var() PickupResetTime PickupResetTime; @@ -394,6 +421,12 @@ struct WeeklyOverrides /** Global modifier of dosh received by players when a zed is killed. Default value is 1.0 */ var() float DoshOnKillGlobalModifier; + /** Disable Add Dosh */ + var() bool bDisableAddDosh; + + /** Disable Throw Weapon */ + var() bool bDisableThrowWeapon; + /** Delay After a wave starts for applying global damage. */ var() float DamageDelayAfterWaveStarted; @@ -411,9 +444,24 @@ struct WeeklyOverrides var() BossRushOverrides BossRushOverrideParams; + /** */ + var() bool bGunGameMode; + + /** Information about each level in Gun Game Mode */ + var() GunGamePerkData GunGamePerksData; + /** Ignores damage caused by headshots. */ var() bool bInvulnerableHeads; + /** Trade time override. */ + var() float TraderTimeModifier; + + /** Time between waves override. */ + var() float TimeBetweenWaves; + + /** Wether or not we only can spawn Armor on the Item pickups */ + var() bool bOnlyArmorItemPickup; + structdefaultproperties { GameLength = GL_Short @@ -471,7 +519,14 @@ struct WeeklyOverrides DroppedItemLifespan=-1.0f bForceWWLMusic = false; bBossRushMode = false; + bDisableAddDosh = false; + bDisableThrowWeapon = false; + bGunGameMode = false; bInvulnerableHeads = false; + TraderTimeModifier = 1.f; + TimeBetweenWaves = -1.f; + bOnlyArmorItemPickup=false; + bForceShowSkipTrader = false; } }; @@ -485,12 +540,14 @@ struct CachedOutbreakInfo var float CachedWorldGravityZ; var float CachedGlobalGravityZ; var PerkAvailableData PerksAvailableData; + var bool bForceShowSkipTrader; structdefaultproperties { - bTradersEnabled=true, + bTradersEnabled=true bAllowGrenadePurchase=true GameAmmoCostScale=1.0 + bForceShowSkipTrader=false } }; @@ -506,7 +563,7 @@ var WeeklyOverrides ActiveEvent; /** Stored values of World Info and GRI items incase we need to reset it. */ var CachedOutbreakInfo CachedItems; -function SetActiveEvent(int ActiveEventIdx) +function int SetActiveEvent(int ActiveEventIdx) { `if(`notdefined(ShippingPC)) local string LocalURL; @@ -537,6 +594,8 @@ function SetActiveEvent(int ActiveEventIdx) ActiveEvent = SetEvents[ActiveEventIdx]; } `endif + + return ActiveEventIdx; } function ClearActiveEvent() @@ -562,6 +621,7 @@ function ClearActiveEvent() KFGameReplicationInfo(GameReplicationInfo).bAllowGrenadePurchase = CachedItems.bAllowGrenadePurchase; KFGameReplicationInfo(GameReplicationInfo).bTradersEnabled = CachedItems.bTradersEnabled; KFGameReplicationInfo(GameReplicationInfo).MaxPerkLevel = CachedItems.MaxPerkLevel; + KFGameReplicationInfo(GameReplicationInfo).bForceShowSkipTrader = CachedItems.bForceShowSkipTrader; } ActiveEvent = EmptyEvent; @@ -588,6 +648,7 @@ function CacheGRI() CachedItems.bAllowGrenadePurchase = KFGameReplicationInfo(GameReplicationInfo).bAllowGrenadePurchase; CachedItems.bTradersEnabled = KFGameReplicationInfo(GameReplicationInfo).bTradersEnabled; CachedItems.MaxPerkLevel = KFGameReplicationInfo(GameReplicationInfo).MaxPerkLevel; + CachedItems.bForceShowSkipTrader = KFGameReplicationInfo(GameReplicationInfo).bForceShowSkipTrader; } } @@ -641,6 +702,7 @@ function UpdateGRI() KFGRI.bAllowGrenadePurchase = !ActiveEvent.bDisableGrenades; KFGRI.bTradersEnabled = !ActiveEvent.bDisableTraders; KFGRI.MaxPerkLevel = ActiveEvent.MaxPerkLevel; + KFGRI.bForceShowSkipTrader = ActiveEvent.bForceShowSkipTrader; } } @@ -893,6 +955,9 @@ function AdjustDefaults(out KFPawn_Monster P, array Adjustment P.HealByAssistance = ToAdjust.HealByAssistance; P.InitialGroundSpeedModifier *= ToAdjust.InitialGroundSpeedModifierScale; + P.GunGameKilledScore = ToAdjust.GunGameKilledScore; + P.GunGameAssistanceScore = ToAdjust.GunGameAssistanceScore; + if (ToAdjust.bStartEnraged) { //If we aren't using the AI controller's spawn enrage, go into the pawn diff --git a/KFGame/Classes/KFPawn.uc b/KFGame/Classes/KFPawn.uc index d602f05..aead556 100644 --- a/KFGame/Classes/KFPawn.uc +++ b/KFGame/Classes/KFPawn.uc @@ -880,6 +880,12 @@ var repnotify byte WeaponSpecialAction; ********************************************************************************************* */ var transient byte LastHitZoneIndex; +/** + AutoTurret + */ +var const bool bIsTurret; + + replication { // Replicated to ALL @@ -887,7 +893,7 @@ replication AmbientSound, WeaponClassForAttachmentTemplate, bIsSprinting, InjuredHitZones, KnockdownImpulse, ReplicatedSpecialMove, bEmpDisrupted, bEmpPanicked, bFirePanicked, RepFireBurnedAmount, bUnaffectedByZedTime, bMovesFastInZedTime, IntendedBodyScale, - IntendedHeadScale, AttackSpeedModifier, bHasStartedFire, PowerUpAmbientSound; + IntendedHeadScale, AttackSpeedModifier, bHasStartedFire, PowerUpAmbientSound, BodyScaleChangePerSecond; if ( bNetDirty && WorldInfo.TimeSeconds < LastTakeHitTimeout ) HitFxInfo, HitFxRadialInfo, HitFxInstigator, HitFxAddedRelativeLocs, HitFxAddedHitCount; if ( Physics == PHYS_RigidBody && !bTearOff ) @@ -2696,6 +2702,7 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector HitFxInfo.HitBoneIndex = HZI_HEAD; } `endif + // NVCHANGE_BEGIN - RLS - Debugging Effects bAllowHeadshot = CanCountHeadshots(); OldHealth = Health; @@ -2778,6 +2785,12 @@ function AdjustDamage(out int InDamage, out vector Momentum, Controller Instigat } InstigatorMonster = InstigatedBy == none ? none : KFPawn_Monster(InstigatedBy.Pawn); + + if (InDamage > 0 && InstigatedBy != none && InstigatedBy.Pawn.IsA('KFPawn_Human')) + { + InDamage = InDamage * AfflictionHandler.GetAfflictionDamageTakenModifier(); + } + if( InDamage > 0 && InstigatorMonster != None ) { // Increase AI damage by AI Damage modifiers @@ -5393,6 +5406,8 @@ simulated function StopExtraVFX(Name FXLabel) } } +simulated function SetTurretWeaponAttachment(class WeaponClass) {} + defaultproperties { InventoryManagerClass=class'KFInventoryManager' @@ -5648,11 +5663,15 @@ defaultproperties // Visuals IntendedBodyScale=1.0 CurrentBodyScale=1.0 - BodyScaleChangePerSecond=0.5 + BodyScaleChangePerSecond=0.5f IntendedHeadScale=1.0 CurrentHeadScale=1.0 bAllowDeathSM=true bCanBePinned=false LastHitZoneIndex=0 + + // --------------------------------------------- + // AutoTurret + bIsTurret=false } diff --git a/KFGame/Classes/KFPawn_Human.uc b/KFGame/Classes/KFPawn_Human.uc index 06aa3a6..7c6bf68 100644 --- a/KFGame/Classes/KFPawn_Human.uc +++ b/KFGame/Classes/KFPawn_Human.uc @@ -471,6 +471,14 @@ function AddDefaultInventory() DefaultInventory.AddItem(class(DynamicLoadObject("KFGameContent.KFInventory_Money", class'Class'))); Super.AddDefaultInventory(); + + if (GameInfo.OutbreakEvent != none && GameInfo.OutbreakEvent.ActiveEvent.bGunGameMode) + { + if (KFPlayerController_WeeklySurvival(Controller) != none) + { + KFPlayerController_WeeklySurvival(Controller).UpdateInitialHeldWeapon(); + } + } } /** When switching weapon modify GroundSpeed by encumbrance level */ @@ -491,6 +499,12 @@ simulated function bool CanThrowWeapon() { local KFPlayerController KFPC; + if (KFGameInfo(WorldInfo.Game).OutbreakEvent != none + && KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bDisableThrowWeapon) + { + return false; + } + KFPC = KFPlayerController(Controller); if (KFPC != none && KFPC.MyGFxManager != none && KFPC.MyGFxManager.TraderMenu != none && KFPC.MyGFxManager.CurrentMenu == KFPC.MyGFxManager.TraderMenu) { @@ -2128,11 +2142,11 @@ client reliable function ClientOverrideHumanDefaults() { return; } - + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); - if (KFGRI != none && KFGRI.CurrentWeeklyIndex == 12) - { + if (KFGRI != none && KFGRI.CurrentWeeklyIndex == 12) + { KFPRI = KFPlayerReplicationInfo(KFPC_WS.PlayerReplicationInfo); if (KFPRI != none) { @@ -2144,7 +2158,7 @@ client reliable function ClientOverrideHumanDefaults() KFPRI.SetWeeklyCharacterAttachment(CowboyHatIndex, 0); } } - } + } } defaultproperties diff --git a/KFGame/Classes/KFPawn_Monster.uc b/KFGame/Classes/KFPawn_Monster.uc index 62f9b02..267f775 100644 --- a/KFGame/Classes/KFPawn_Monster.uc +++ b/KFGame/Classes/KFPawn_Monster.uc @@ -95,6 +95,9 @@ var int HealByAssistance; /** WWL Hat attach name*/ var name ZEDCowboyHatAttachName; +/** GunGameMode: score given when killed */ +var byte GunGameKilledScore; +var byte GunGameAssistanceScore; /** * Information on resistant or vulnerable damage types @@ -568,6 +571,13 @@ var transient array WeakPointVFXComponents; var repnotify WeakPoint WeakPoints_TS[`TINY_SKULL_MAX_WEAKPOINTS]; var ParticleSystem WeakPointParticleTemplate; +/********************************************************************************************* + * @name ShrinkRayGun +********************************************************************************************* */ + +var bool bCanBeKilledByShrinking; +var float ShrinkEffectModifier; + /********************************************************************************************* * @name Delegates ********************************************************************************************* */ @@ -2050,6 +2060,7 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector local KFAIController KFAIC; local KFPawn_Monster KFPM; local float NapalmCheckDist; + local float InfernoRadius; AIMonster = KFAIController_Monster(InstigatedBy); KFDT = class(DamageType); @@ -2098,9 +2109,9 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector && DamageCauser != none && KFDT != none && KFDT.default.DoT_Type == DOT_Fire - && KFDT != class'KFDT_Fire_Napalm' - && WorldInfo.RealTimeSeconds - LastNapalmInfectCheckTime > 0.25f ) + && KFDT != class'KFDT_Fire_Napalm') { + if( KFPC != none && KFPC.GetPerk() != none && KFPC.GetPerk().CanSpreadNapalm() @@ -2130,6 +2141,29 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector } } + if( Damage > 0 + && InstigatedBy != none + && KFDT != none + && KFDT.default.DoT_Type == DOT_Fire + && KFDT != class'KFDT_Fire_Napalm' + && KFPC != none + && KFPC.GetPerk() != none + && KFPC.GetPerk().CanSpreadInferno() + && class'KFPerk'.static.IsDamageTypeOnThisPerk(KFDT, class'KFPerk_Firebug') ) + { + + InfernoRadius = CylinderComponent.CollisionRadius + class'KFPerk_Firebug'.static.GetInfernoRadius(); + + //PawnOwner.DrawDebugSphere(PawnOwner.Location, InfernoRadius, 20, 255, 255, 0, true); + + foreach CollidingActors(class'KFPawn_Monster', KFPM, InfernoRadius, Location, true,,) + { + KFPM.ApplyDamageOverTime(Damage, + KFPC, + KFDT); + } + } + KFPRI = KFPlayerReplicationInfo( PlayerReplicationInfo ); if( KFPRI != none ) { @@ -4907,7 +4941,8 @@ DefaultProperties IncapSettings(AF_Microwave)=(Cooldown=5.0, Duration=5.0,) IncapSettings(AF_Freeze)=(Cooldown=5.0) IncapSettings(AF_Snare)=(Cooldown=5.0, Duration=5.0,) - + IncapSettings(AF_BigHead)=(Cooldown=0.0, Duration=10.0) + IncapSettings(AF_Shrink)=(Cooldown=0.0, Duration=10.0) // --------------------------------------------- // Movement / Physics bCanCrouch=false @@ -5052,4 +5087,10 @@ DefaultProperties ZEDCowboyHatAttachName=HEAD_Attach WeakPointParticleTemplate=ParticleSystem'FX_Gameplay_EMIT.FX_Weak_Indicator' + + GunGameKilledScore=0 + GunGameAssistanceScore=0 + + bCanBeKilledByShrinking=true + ShrinkEffectModifier=1.0f } diff --git a/KFGame/Classes/KFPawn_MonsterBoss.uc b/KFGame/Classes/KFPawn_MonsterBoss.uc index b1729b8..ba57ddf 100644 --- a/KFGame/Classes/KFPawn_MonsterBoss.uc +++ b/KFGame/Classes/KFPawn_MonsterBoss.uc @@ -445,4 +445,6 @@ defaultproperties bCanBePinned=false VortexAttracionModifier=0.3f + + bCanBeKilledByShrinking=false } \ No newline at end of file diff --git a/KFGame/Classes/KFPerk.uc b/KFGame/Classes/KFPerk.uc index 9f4e0ec..cdc6caa 100644 --- a/KFGame/Classes/KFPerk.uc +++ b/KFGame/Classes/KFPerk.uc @@ -461,6 +461,39 @@ static function bool IsBackupWeapon( KFWeapon KFW ) return KFW != none && KFW.default.bIsBackupWeapon; } +/** + * @brief Return if a weapon is a knife + * + * @param KFW Weapon to check + * @return true if knife weapon + */ +static simulated public function bool IsKnife( KFWeapon KFW ) +{ + return KFW != none && KFW.default.bIsBackupWeapon && KFW.IsMeleeWeapon(); +} + +/** + * @brief Return if a weapon is a welder + * + * @param KFW Weapon to check + * @return true if knife weapon + */ +static simulated public function bool IsWelder( KFWeapon KFW ) +{ + return KFW != none && KFW.Class.Name == 'KFWeap_Welder'; +} + +/** + * @brief Return if a weapon is the syringe + * + * @param KFW Weapon to check + * @return true if syringe weapon + */ +static simulated public function bool IsSyringe( KFWeapon KFW ) +{ + return KFW != none && KFW.Class.Name == 'KFWeap_Healer_Syringe'; +} + /** * @brief Return if a weapon is Dual 9mm * @@ -505,6 +538,29 @@ static function bool IsDoshinegun( KFWeapon KFW ) return KFW != none && KFW.Class.Name == 'KFWeap_AssaultRifle_Doshinegun'; } +/** + * @brief Return if a weapon is Crossboom (special case for high rounds perk) + * + * @param KFW Weapon to check + * @return true if backup weapon + */ +static function bool IsHRGCrossboom( KFWeapon KFW ) +{ + return KFW != none && KFW.Class.Name == 'KFWeap_HRG_Crossboom'; +} + +/** + * @brief Return if a weapon is AutoTurret (should ignore ammo upgrades) + * + * @param KFW Weapon to check + * @return true if backup weapon + */ +static function bool IsAutoTurret( KFWeapon KFW ) +{ + return KFW != none && KFW.Class.Name == 'KFWeap_AutoTurret'; +} + + /********************************************************************************************* * @name Build / Level Management - Apply and save the updated build and level ********************************************************************************************* */ @@ -789,7 +845,7 @@ simulated final function int GetSavedBuild() simulated event PreBeginPlay() { // Set the grenade class for this perk - GrenadeClass = class(DynamicLoadObject(GrenadeWeaponDef.default.WeaponClassPath, class'Class')); + GrenadeClass = class(DynamicLoadObject(GetGrenadeClassPath(), class'Class')); PerkIcon = Texture2D(DynamicLoadObject(GetPerkIconPath(), class'Texture2D')); MyKFGRI = KFGameReplicationInfo(WorldInfo.GRI); @@ -1020,6 +1076,12 @@ simulated function string GetKnifeWeaponClassPath() return KnifeWeaponDef.default.WeaponClassPath; } +/* Returns the primary weapon's class path for this perk */ +simulated function string GetGrenadeClassPath() +{ + return GrenadeWeaponDef.default.WeaponClassPath; +} + simulated function bool PerkNeedsTick(){ return false; } /** @@ -1208,6 +1270,7 @@ simulated function bool IsFlarotovActive(){ return false; } function float GetDoTScalerAdditions(class KFDT); function bool GetFireStumble( optional KFPawn KFP, optional class DamageType ){ return false; } function bool CanSpreadNapalm(){ return false; } +function bool CanSpreadInferno(){ return false; } function bool CouldBeZedShrapnel( class KFDT ){ return false; } simulated function bool ShouldShrapnel(){ return false; } simulated function float GetSplashDamageModifier(){ return 1.f; } @@ -1293,6 +1356,11 @@ function OnWaveEnded(); function OnWaveStart(); +/** + * Notifications for Wave start / end but on client + */ +simulated function OnClientWaveEnded(); + simulated function bool GetUsingTactialReload( KFWeapon KFW ) { return false; @@ -1493,6 +1561,46 @@ simulated function FormatPerkSkills() simulated function PlayerDied(){} +static simulated function bool CanChoosePrimaryWeapon() +{ + return false; +} + +static simulated function bool CanChooseGrenade() +{ + return false; +} + +simulated function byte OnPrevWeaponSelected() { return 255; } +simulated function byte OnNextWeaponSelected() { return 255; } +simulated function byte OnPrevGrenadeSelected() { return 255; } +simulated function byte OnNextGrenadeSelected() { return 255; } +simulated function byte SetWeaponSelectedIndex(byte idx); +simulated function byte SetGrenadeSelectedIndex(byte idx); +simulated function byte SetGrenadeSelectedIndexUsingSkills(byte idx, byte InSelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext); +simulated function byte GetGrenadeSelectedIndex() { return 255;} +simulated function InitializeGrenades(); + +static simulated function string GetPrimaryWeaponName(byte Idx) +{ + return default.PrimaryWeaponDef.static.GetItemName(); +} + +static simulated function string GetPrimaryWeaponImagePath(byte Idx) +{ + return default.PrimaryWeaponDef.static.GetImagePath(); +} + +static simulated function string GetGrenadeWeaponName(byte Idx) +{ + return default.GrenadeWeaponDef.static.GetItemName(); +} + +static simulated function string GetGrenadeWeaponImagePath(byte Idx) +{ + return default.GrenadeWeaponDef.static.GetImagePath(); +} + DefaultProperties { bTickIsDisabled=TRUE diff --git a/KFGame/Classes/KFPerk_Commando.uc b/KFGame/Classes/KFPerk_Commando.uc index 7717f38..ebbac28 100644 --- a/KFGame/Classes/KFPerk_Commando.uc +++ b/KFGame/Classes/KFPerk_Commando.uc @@ -251,7 +251,8 @@ simulated function ModifyMagSizeAndNumber( KFWeapon KFW, out int MagazineCapacit TempCapacity = MagazineCapacity; // FAMAS needs its secondary ammo affected - if( (!bSecondary || IsFAMAS(KFW)) && IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && (KFW == none || !KFW.bNoMagazine) ) + // Autoturret cannot modify its magazine capacity + if( (!bSecondary || IsFAMAS(KFW)) && !IsAutoTurret(KFW) && IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && (KFW == none || !KFW.bNoMagazine) ) { if( IsLargeMagActive() ) { @@ -260,9 +261,10 @@ simulated function ModifyMagSizeAndNumber( KFWeapon KFW, out int MagazineCapacit if( IsEatLeadActive() ) { - TempCapacity += MagazineCapacity * GetSkillValue( PerkSkills[ECommandoEatLead] ); + TempCapacity += MagazineCapacity * GetSkillValue( PerkSkills[ECommandoEatLead] ); } } + MagazineCapacity = Round(TempCapacity); } diff --git a/KFGame/Classes/KFPerk_Demolitionist.uc b/KFGame/Classes/KFPerk_Demolitionist.uc index 54af445..6a69548 100644 --- a/KFGame/Classes/KFPerk_Demolitionist.uc +++ b/KFGame/Classes/KFPerk_Demolitionist.uc @@ -126,6 +126,7 @@ simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCau { local KFWeapon KFW; local float TempDamage; + local bool bIsCrossboom; if( DamageType != none && IsDamageIgnoredDT( DamageType ) ) { @@ -140,7 +141,9 @@ simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCau KFW = GetWeaponFromDamageCauser( DamageCauser ); } - if( (KFW != none && IsWeaponOnPerk( KFW,, self.class )) || (DamageType != none && IsDamageTypeOnPerk( DamageType )) ) + bIsCrossboom = IsHRGCrossboom(KFW); + + if( (KFW != none && (IsWeaponOnPerk( KFW,, self.class )) || bIsCrossboom) || (DamageType != none && IsDamageTypeOnPerk( DamageType )) ) { `QALog( "Base Damage Given" @ DamageType @ KFW @ InDamage, bLogPerk ); //Passive @@ -153,9 +156,9 @@ simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCau `QALog( "Bombadier Given" @ DamageType @ KFW @ InDamage * GetSkillValue( PerkSkills[EDemoDamage] ), bLogPerk ); } - if( IsDirectHitActive() && DamageType != none && IsDamageTypeOnPerk( DamageType ) ) + if( IsDirectHitActive() && ((DamageType != none && IsDamageTypeOnPerk( DamageType )) || bIsCrossboom )) { - if( class(DamageType) != none || class(DamageType)!=none) + if( class(DamageType) != none || class(DamageType)!=none || (bIsCrossboom && class(DamageType) != none)) { TempDamage += InDamage * GetSkillValue( PerkSkills[EDemoDirectHit] ); `QALog( "High Impact Damage Given" @ DamageType @ KFW @ InDamage * GetSkillValue( PerkSkills[EDemoDirectHit] ), bLogPerk ); diff --git a/KFGame/Classes/KFPerk_Firebug.uc b/KFGame/Classes/KFPerk_Firebug.uc index 3164a71..51268e4 100644 --- a/KFGame/Classes/KFPerk_Firebug.uc +++ b/KFGame/Classes/KFPerk_Firebug.uc @@ -32,7 +32,8 @@ var private const class SnareCausingDmgTypeClass; var private const int NapalmDamage; /** Multiplier on CylinderComponent.CollisionRadius to check for infecting other zeds */ var private const float NapalmCheckCollisionScale; - +// Radius in cm to check for, this is added to CylinderComponent.CollisionRadius +var private const float InfernoRadius; enum EFirebugSkills { @@ -294,11 +295,21 @@ function bool CanSpreadNapalm() return IsNapalmActive(); } +function bool CanSpreadInferno() +{ + return IsInfernoActive(); +} + static final function float GetNapalmCheckCollisionScale() { return default.NapalmCheckCollisionScale; } +static final function float GetInfernoRadius() +{ + return default.InfernoRadius; +} + /** * @brief Checks if a zed could potentially explode later * @@ -634,6 +645,7 @@ DefaultProperties NapalmDamage=7 //50 NapalmCheckCollisionScale=2.0f //6.0 + InfernoRadius=800.0f ShrapnelChance=0.3f //0.2 diff --git a/KFGame/Classes/KFPerk_Gunslinger.uc b/KFGame/Classes/KFPerk_Gunslinger.uc index 05c509b..e35de57 100644 --- a/KFGame/Classes/KFPerk_Gunslinger.uc +++ b/KFGame/Classes/KFPerk_Gunslinger.uc @@ -806,7 +806,7 @@ simulated function bool ShouldDisableZedTimeSkillsForWildWest() { if (WorldInfo.NetMode == NM_Client) { - return MyKFGRI.bIsWeeklyMode && class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12; + return MyKFGRI.bIsWeeklyMode && MyKFGRI.CurrentWeeklyIndex == 12; } else { diff --git a/KFGame/Classes/KFPerk_Sharpshooter.uc b/KFGame/Classes/KFPerk_Sharpshooter.uc index 2465778..753f7df 100644 --- a/KFGame/Classes/KFPerk_Sharpshooter.uc +++ b/KFGame/Classes/KFPerk_Sharpshooter.uc @@ -696,7 +696,7 @@ function bool ShouldDisableZedTimeSkillsForWildWest() { if (WorldInfo.NetMode == NM_Client) { - return MyKFGRI.bIsWeeklyMode && class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12; + return MyKFGRI.bIsWeeklyMode && MyKFGRI.CurrentWeeklyIndex == 12; } else { @@ -708,7 +708,7 @@ simulated function bool IsFanfareActiveForWildWest() { if (WorldInfo.NetMode == NM_Client) { - return MyKFGRI.bIsWeeklyMode && class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12; + return MyKFGRI.bIsWeeklyMode && MyKFGRI.CurrentWeeklyIndex == 12; } else { diff --git a/KFGame/Classes/KFPerk_Support.uc b/KFGame/Classes/KFPerk_Support.uc index 1918771..2656b85 100644 --- a/KFGame/Classes/KFPerk_Support.uc +++ b/KFGame/Classes/KFPerk_Support.uc @@ -423,7 +423,7 @@ simulated function Interact( KFPawn_Human KFPH ) { foreach KFPH.InvManager.InventoryActors( class'KFWeapon', KFW ) { - if( KFW.static.DenyPerkResupply() ) + if( KFW.DenyPerkResupply() ) { continue; } diff --git a/KFGame/Classes/KFPerk_Survivalist.uc b/KFGame/Classes/KFPerk_Survivalist.uc index cf6c766..60b1f10 100644 --- a/KFGame/Classes/KFPerk_Survivalist.uc +++ b/KFGame/Classes/KFPerk_Survivalist.uc @@ -47,14 +47,24 @@ var class HealingGrenadeWeaponDef; var class MolotovGrenadeWeaponDef; var private const array > PrimaryWeaponPaths; +var private const array > GrenadeWeaponPaths; var private const array KnifeWeaponPaths; -var int StartingWeaponClassIndex; +var byte StartingWeaponClassIndex; +var byte StartingGrenadeClassIndex; // This is the cached value for the tentative Grenade selection (only applied when gameplay time) +var byte CurrentGrenadeClassIndex; // This is the gameplay value we use for Grenade Index, it can only be changed while TraderTime / Wave Start / Wave Ended var private const array TacticalReloadAsReloadRateClassNames; /** When MakeThingsGoBoom skill is selected the survivalist gets additional explosive resistance */ var private const float MakeThingsGoBoomExplosiveResistance; +var private const byte MedicGrenadeIndex; +var private const byte FirebugGrenadeIndex; + +var private transient bool bIsGrenadeDirty; + +`define WEAP_IDX_NONE 255 + /********************************************************************************************* * @name Perk init and spawning ******************************************************************************************** */ @@ -105,7 +115,11 @@ function bool ShouldGetAllTheXP() /* Returns the primary weapon's class path for this perk */ simulated function string GetPrimaryWeaponClassPath() { - StartingWeaponClassIndex = Rand(PrimaryWeaponPaths.length); + if (StartingWeaponClassIndex == `WEAP_IDX_NONE) + { + StartingWeaponClassIndex = Rand(PrimaryWeaponPaths.length); + } + AutoBuyLoadOutPath.InsertItem(0,PrimaryWeaponPaths[StartingWeaponClassIndex]); return PrimaryWeaponPaths[StartingWeaponClassIndex].default.WeaponClassPath; } @@ -556,16 +570,7 @@ simulated function float GetSnarePowerModifier( optional class Damag /* Returns the grenade class for this perk */ simulated function class< KFProj_Grenade > GetGrenadeClass() { - if( IsAmmoVestActive() ) - { - return class(DynamicLoadObject(HealingGrenadeWeaponDef.default.WeaponClassPath, class'Class')); - } - else if( IsBigPocketsActive() ) - { - return class(DynamicLoadObject(MolotovGrenadeWeaponDef.default.WeaponClassPath, class'Class')); - } - - return GrenadeClass; + return class(DynamicLoadObject(GrenadeWeaponPaths[CurrentGrenadeClassIndex].default.WeaponClassPath, class'Class')); } /** @@ -744,41 +749,379 @@ simulated static function GetPassiveStrings( out array PassiveValues, ou simulated function string GetGrenadeImagePath() { - if( IsAmmoVestActive() ) - { - return default.HealingGrenadeWeaponDef.Static.GetImagePath(); - } - else if( IsBigPocketsActive() ) - { - return default.MolotovGrenadeWeaponDef.Static.GetImagePath(); - } - - return default.GrenadeWeaponDef.Static.GetImagePath(); + return CurrentGrenadeClassIndex == `WEAP_IDX_NONE ? default.GrenadeWeaponDef.static.GetImagePath() : default.GrenadeWeaponPaths[CurrentGrenadeClassIndex].static.GetImagePath(); } - simulated function class GetGrenadeWeaponDef() { - if( IsAmmoVestActive() ) - { - return default.HealingGrenadeWeaponDef; - } - else if( IsBigPocketsActive() ) - { - return default.MolotovGrenadeWeaponDef; + return GrenadeWeaponPaths[CurrentGrenadeClassIndex]; +} + +static simulated function bool CanChoosePrimaryWeapon() +{ + return true; +} + +static simulated function bool CanChooseGrenade() +{ + return true; +} + +simulated function byte OnPrevWeaponSelected() +{ + if (StartingWeaponClassIndex == `WEAP_IDX_NONE) + { + StartingWeaponClassIndex = PrimaryWeaponPaths.Length - 1; + } + else if (StartingWeaponClassIndex == 0) + { + StartingWeaponClassIndex = `WEAP_IDX_NONE; + } + else + { + --StartingWeaponClassIndex; } - return default.GrenadeWeaponDef; + return StartingWeaponClassIndex; +} + +simulated function byte OnNextWeaponSelected() +{ + if (StartingWeaponClassIndex == `WEAP_IDX_NONE) + { + StartingWeaponClassIndex = 0; + } + else if (StartingWeaponClassIndex == PrimaryWeaponPaths.Length - 1) + { + StartingWeaponClassIndex = `WEAP_IDX_NONE; + } + else + { + ++StartingWeaponClassIndex; + } + + return StartingWeaponClassIndex; +} + +simulated function byte OnPrevGrenadeSelected() +{ + if (StartingGrenadeClassIndex == `WEAP_IDX_NONE) + { + StartingGrenadeClassIndex = GrenadeWeaponPaths.Length - 1; + } + else + { + --StartingGrenadeClassIndex; + + if (StartingGrenadeClassIndex == FirebugGrenadeIndex && !IsBigPocketsActive()) + { + --StartingGrenadeClassIndex; + } + + if (StartingGrenadeClassIndex == MedicGrenadeIndex && !IsAmmoVestActive()) + { + --StartingGrenadeClassIndex; + } + + if (StartingGrenadeClassIndex > GrenadeWeaponPaths.Length - 1) + { + StartingGrenadeClassIndex = `WEAP_IDX_NONE; + } + } + + bIsGrenadeDirty=true; + + return StartingGrenadeClassIndex; +} + +simulated function byte OnNextGrenadeSelected() +{ + if (StartingGrenadeClassIndex == `WEAP_IDX_NONE) + { + StartingGrenadeClassIndex = 0; + } + else + { + ++StartingGrenadeClassIndex; + + if (StartingGrenadeClassIndex == MedicGrenadeIndex && !IsAmmoVestActive()) + { + ++StartingGrenadeClassIndex; + } + + if (StartingGrenadeClassIndex == FirebugGrenadeIndex && !IsBigPocketsActive()) + { + ++StartingGrenadeClassIndex; + } + + if (StartingGrenadeClassIndex > GrenadeWeaponPaths.Length - 1) + { + StartingGrenadeClassIndex = `WEAP_IDX_NONE; + } + } + + bIsGrenadeDirty=true; + + return StartingGrenadeClassIndex; +} + +static simulated function string GetPrimaryWeaponName(byte Idx) +{ + return Idx == `WEAP_IDX_NONE ? default.PrimaryWeaponDef.static.GetItemName() : default.PrimaryWeaponPaths[Idx].static.GetItemName(); +} + +static simulated function string GetPrimaryWeaponImagePath(byte Idx) +{ + return Idx == `WEAP_IDX_NONE ? default.PrimaryWeaponDef.static.GetImagePath() : default.PrimaryWeaponPaths[Idx].static.GetImagePath(); +} + +static simulated function string GetGrenadeWeaponName(byte Idx) +{ + return Idx == `WEAP_IDX_NONE ? default.GrenadeWeaponDef.static.GetItemName() : default.GrenadeWeaponPaths[Idx].static.GetItemName(); +} + +static simulated function string GetGrenadeWeaponImagePath(byte Idx) +{ + return Idx == `WEAP_IDX_NONE ? default.GrenadeWeaponDef.static.GetImagePath() : default.GrenadeWeaponPaths[Idx].static.GetImagePath(); +} + +/* Returns the primary weapon's class path for this perk */ +simulated function string GetGrenadeClassPath() +{ + return GrenadeWeaponPaths[StartingGrenadeClassIndex].default.WeaponClassPath; +} + +simulated function byte GetGrenadeSelectedIndex() { return CurrentGrenadeClassIndex; } + +simulated function byte SetWeaponSelectedIndex(byte idx) +{ + if (idx >= default.PrimaryWeaponPaths.Length && idx < 255) + { + StartingWeaponClassIndex = 0; + } + else if (idx == 255) + { + StartingWeaponClassIndex = `WEAP_IDX_NONE; + } + else + { + StartingWeaponClassIndex = idx; + } + + ServerUpdateCurrentWeapon(StartingWeaponClassIndex); + + return StartingWeaponClassIndex; +} + +simulated function byte SetGrenadeSelectedIndex(byte idx) +{ + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + if (idx >= default.GrenadeWeaponPaths.Length && idx < 255) + { + StartingGrenadeClassIndex = 0; + } + else if (idx == 255) + { + StartingGrenadeClassIndex = `WEAP_IDX_NONE; + } + else + { + StartingGrenadeClassIndex = idx; + + if (StartingGrenadeClassIndex == MedicGrenadeIndex || StartingGrenadeClassIndex == FirebugGrenadeIndex) + { + if (IsAmmoVestActive()) + { + StartingGrenadeClassIndex = MedicGrenadeIndex; + } + else if (IsBigPocketsActive()) + { + StartingGrenadeClassIndex = FirebugGrenadeIndex; + } + else + { + StartingGrenadeClassIndex = 0; + } + } + } + + // If we are in no gameplay time insta change + if (!KFGRI.bWaveIsActive) + { + UpdateCurrentGrenade(); + } + + return StartingGrenadeClassIndex; +} + +simulated function byte SetGrenadeSelectedIndexUsingSkills(byte idx, byte InSelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext) +{ + local KFGameReplicationInfo KFGRI; + local bool AmmoVestActive, BigPocketsActive; + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + AmmoVestActive = false; + BigPocketsActive = false; + + if (idx >= default.GrenadeWeaponPaths.Length && idx < 255) + { + StartingGrenadeClassIndex = 0; + } + else if (idx == 255) + { + StartingGrenadeClassIndex = `WEAP_IDX_NONE; + } + else + { + StartingGrenadeClassIndex = idx; + + if (StartingGrenadeClassIndex == MedicGrenadeIndex || StartingGrenadeClassIndex == FirebugGrenadeIndex) + { + AmmoVestActive = InSelectedSkills[2] == 1; + BigPocketsActive = InSelectedSkills[2] == 2; + + if (IsChoosingPrev) + { + if (StartingGrenadeClassIndex == FirebugGrenadeIndex) + { + if (BigPocketsActive == false) + { + --StartingGrenadeClassIndex; + } + } + + if (StartingGrenadeClassIndex == MedicGrenadeIndex) + { + if (AmmoVestActive == false) + { + --StartingGrenadeClassIndex; + } + } + } + else if (IsChoosingNext) + { + if (StartingGrenadeClassIndex == MedicGrenadeIndex) + { + if (AmmoVestActive == false) + { + ++StartingGrenadeClassIndex; + } + } + + if (StartingGrenadeClassIndex == FirebugGrenadeIndex) + { + if (BigPocketsActive == false) + { + ++StartingGrenadeClassIndex; + } + } + } + else + { + if (AmmoVestActive) + { + StartingGrenadeClassIndex = MedicGrenadeIndex; + } + else if (BigPocketsActive) + { + StartingGrenadeClassIndex = FirebugGrenadeIndex; + } + else + { + StartingGrenadeClassIndex = 0; + } + } + + if (StartingGrenadeClassIndex > GrenadeWeaponPaths.Length - 1) + { + StartingGrenadeClassIndex = `WEAP_IDX_NONE; + } + } + } + + // If we are in no gameplay time insta change + if (!KFGRI.bWaveIsActive) + { + UpdateCurrentGrenade(); + } + + return StartingGrenadeClassIndex; +} + +simulated function InitializeGrenades() +{ + local byte MaxValue; + + if (StartingGrenadeClassIndex == `WEAP_IDX_NONE && ( bIsGrenadeDirty || CurrentGrenadeClassIndex == `WEAP_IDX_NONE)) + { + MaxValue = (!IsAmmoVestActive() && !IsBigPocketsActive()) ? GrenadeWeaponPaths.length - 1 : GrenadeWeaponPaths.length; + CurrentGrenadeClassIndex = Rand(MaxValue); + + if ( (!IsAmmoVestActive() && CurrentGrenadeClassIndex == MedicGrenadeIndex) || + (!IsBigPocketsActive() && CurrentGrenadeClassIndex == FirebugGrenadeIndex) || + (CurrentLevel < 15 && (CurrentGrenadeClassIndex == MedicGrenadeIndex || CurrentGrenadeClassIndex == FirebugGrenadeIndex))) + { + CurrentGrenadeClassIndex = GrenadeWeaponPaths.length - 1; + } + + bIsGrenadeDirty = false; + + if (Controller(Owner).IsLocalController()) + { + ServerUpdateCurrentGrenade(CurrentGrenadeClassIndex); + } + } +} + +simulated function OnClientWaveEnded() +{ + super.OnWaveEnded(); + UpdateCurrentGrenade(); +} + +simulated function UpdateCurrentGrenade() +{ + if (StartingGrenadeClassIndex == `WEAP_IDX_NONE) + { + InitializeGrenades(); + } + else if (CurrentGrenadeClassIndex != StartingGrenadeClassIndex || bIsGrenadeDirty) + { + CurrentGrenadeClassIndex = StartingGrenadeClassIndex; + + if (Controller(Owner).IsLocalController()) + { + ServerUpdateCurrentGrenade(CurrentGrenadeClassIndex); + } + } +} + +reliable server function ServerUpdateCurrentWeapon(byte Value) +{ + StartingWeaponClassIndex = Value; +} + +reliable server function ServerUpdateCurrentGrenade(byte CurrentIndex) +{ + CurrentGrenadeClassIndex = CurrentIndex; } DefaultProperties { - StartingWeaponClassIndex=Index_None + StartingWeaponClassIndex=0 + StartingGrenadeClassIndex=0 + CurrentGrenadeClassIndex=0 + bIsGrenadeDirty=true + PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Survivalist' PrimaryWeaponDef=class'KFWeapDef_Random' KnifeWeaponDef=class'KFweapDef_Knife_Survivalist' - GrenadeWeaponDef=class'KFWeapDef_Grenade_Commando' + GrenadeWeaponDef=class'KFWeapDef_RandomGrenade' HealingGrenadeWeaponDef=class'KFWeapDef_Grenade_Medic' MolotovGrenadeWeaponDef=class'KFWeapDef_Grenade_Firebug' @@ -856,17 +1199,27 @@ DefaultProperties ZedTimeModifyingStates(11)="WeaponSonicGunSingleFiring" ZedTimeModifyingStates(12)="WeaponSonicGunCharging" - PrimaryWeaponPaths(0)=class'KFWeapDef_AR15' - PrimaryWeaponPaths(1)=class'KFWeapDef_MB500' - PrimaryWeaponPaths(2)=class'KFWeapDef_Crovel' - PrimaryWeaponPaths(3)=class'KFWeapDef_HX25' - PrimaryWeaponPaths(4)=class'KFWeapDef_MedicPistol' + PrimaryWeaponPaths(0)=class'KFWeapDef_Crovel' + PrimaryWeaponPaths(1)=class'KFWeapDef_AR15' + PrimaryWeaponPaths(2)=class'KFWeapDef_MB500' + PrimaryWeaponPaths(3)=class'KFWeapDef_MedicPistol' + PrimaryWeaponPaths(4)=class'KFWeapDef_HX25' PrimaryWeaponPaths(5)=class'KFWeapDef_CaulkBurn' PrimaryWeaponPaths(6)=class'KFWeapDef_Remington1858Dual' PrimaryWeaponPaths(7)=class'KFWeapDef_Winchester1894' PrimaryWeaponPaths(8)=class'KFWeapDef_MP7' AutoBuyLoadOutPath=(class'KFWeapDef_DragonsBreath', class'KFWeapDef_FreezeThrower', class'KFWeapDef_MedicRifle', class'KFWeapDef_LazerCutter') + GrenadeWeaponPaths(0)=class'KFWeapDef_Grenade_Commando' + GrenadeWeaponPaths(1)=class'KFWeapDef_Grenade_Support' + GrenadeWeaponPaths(2)=class'KFWeapDef_Grenade_Medic' + GrenadeWeaponPaths(3)=class'KFWeapDef_Grenade_Firebug' + GrenadeWeaponPaths(4)=class'KFWeapDef_Grenade_Gunslinger' + GrenadeWeaponPaths(5)=class'KFWeapDef_Grenade_SWAT' + + MedicGrenadeIndex = 2; + FirebugGrenadeIndex = 3; + // Prestige Rewards PrestigeRewardItemIconPaths[0]="WEP_SkinSet_Prestige01_Item_TEX.knives.SurvivalistKnife_PrestigePrecious_Mint_large" PrestigeRewardItemIconPaths[1]="WEP_SkinSet_Prestige02_Item_TEX.tier01.FreezeThrower_PrestigePrecious_Mint_large" diff --git a/KFGame/Classes/KFPickupFactory.uc b/KFGame/Classes/KFPickupFactory.uc index 3d1750a..2453e69 100644 --- a/KFGame/Classes/KFPickupFactory.uc +++ b/KFGame/Classes/KFPickupFactory.uc @@ -37,17 +37,29 @@ var() bool bEnabledAtStart; /** Whether this pickup node has been modified by kismet (enabled or disabled) */ var transient bool bKismetEnabled; +function bool CanUsePickup() +{ + return true; +} + /** Pick a weapon from 'ItemPickups' and enable it in the world */ function Reset() { - if( bKismetDriven ) + if (CanUsePickup()) { - SetInitialState(); + if( bKismetDriven ) + { + SetInitialState(); + } + else + { + bToBeActivated = false; + GotoState('Pickup'); + } } else { - bToBeActivated = false; - GotoState('Pickup'); + SetPickupHidden(); } } diff --git a/KFGame/Classes/KFPickupFactory_Item.uc b/KFGame/Classes/KFPickupFactory_Item.uc index 972a1a8..46e788f 100644 --- a/KFGame/Classes/KFPickupFactory_Item.uc +++ b/KFGame/Classes/KFPickupFactory_Item.uc @@ -49,11 +49,42 @@ replication PickupIndex; } +function bool CanUsePickup() +{ + local KFGameInfo KFGI; + local int i; + local bool has_armour; + + KFGI = KFGameInfo( WorldInfo.Game ); + + if (KFGI != none && KFGI.OutbreakEvent != none) + { + if (KFGI.OutbreakEvent.ActiveEvent.bOnlyArmorItemPickup) + { + for (i = 0 ; i < ItemPickups.Length ; i++) + { + if (ItemPickups[i].ItemClass.Name == ArmorClassName) + { + has_armour = true; + break; + } + } + } + + if (has_armour == false) + { + return false; + } + } + + return super.CanUsePickup(); +} + simulated event PreBeginPlay() { local KFGameInfo KFGI; -// For Scavenger weekly, we need to treat the factory items as non kismet items. + // For Scavenger weekly, we need to treat the factory items as non kismet items. KFGI = KFGameInfo( WorldInfo.Game ); if (KFGI != none && KFGI.OutbreakEvent != none && KFGI.OutbreakEvent.ActiveEvent.bUnlimitedWeaponPickups) { @@ -62,7 +93,6 @@ simulated event PreBeginPlay() bKismetDriven=false; } } -//////////////////////////////////////// super.PreBeginPlay(); } @@ -152,10 +182,26 @@ function int ChooseWeaponPickup() local int i, DesiredItemIdx; local float Weight, TotalWeight, RandomWeight; local array IndexList; + local KFGameInfo KFGI; + + KFGI = KFGameInfo(WorldInfo.Game); + + DesiredItemIdx = 255; // Add up the total weight for all valid attacks for(i = 0; i < ItemPickups.Length; i++) { + if (KFGI != none && KFGI.OutbreakEvent != none) + { + if (KFGI.OutbreakEvent.ActiveEvent.bOnlyArmorItemPickup) + { + if (ItemPickups[i].ItemClass.Name != 'KFInventory_Armor') + { + continue; + } + } + } + if ( ItemPickups[i].Priority > 0.f ) { TotalWeight += ItemPickups[i].Priority; @@ -194,11 +240,26 @@ simulated native function GetPickupMesh(class ItemClass); /** Use the pickups static mesh for this factory */ simulated function SetPickupMesh() { + local KFGameInfo KFGI; + if (PickupIndex >= ItemPickups.Length) { return; } + KFGI = KFGameInfo(WorldInfo.Game); + + if (KFGI != none && KFGI.OutbreakEvent != none) + { + if (KFGI.OutbreakEvent.ActiveEvent.bOnlyArmorItemPickup) + { + if (ItemPickups[PickupIndex].ItemClass.Name != ArmorClassName) + { + return; + } + } + } + if (ItemPickups[PickupIndex].ItemClass.Name == ArmorClassName) { FinalizePickupMesh(StaticMeshComponent(ItemPickups[PickupIndex].ItemClass.default.PickupFactoryMesh).StaticMesh); @@ -307,12 +368,24 @@ function ActivateNewPickup(Pawn P) { local KFGameInfo KFGI; + KFGI = KFGameInfo( WorldInfo.Game ); + + if (KFGI != none && KFGI.OutbreakEvent != none) + { + if (KFGI.OutbreakEvent.ActiveEvent.bOnlyArmorItemPickup) + { + if (ItemPickups[PickupIndex].ItemClass.Name != ArmorClassName) + { + return; + } + } + } + if( bKismetDriven ) { return; } - KFGI = KFGameInfo( WorldInfo.Game ); if ( KFGI != none ) { KFGI.EnableNewPickup( KFGI.ItemPickups, KFGI.DifficultyInfo.GetWeaponPickupInterval(KFGI.GetLivingPlayerCount()), self ); diff --git a/KFGame/Classes/KFPlayerController.uc b/KFGame/Classes/KFPlayerController.uc index 5e29926..e303260 100644 --- a/KFGame/Classes/KFPlayerController.uc +++ b/KFGame/Classes/KFPlayerController.uc @@ -113,6 +113,10 @@ var private const bool bPerkStatsLoaded; /** Id of previously selected perk */ var public byte SavedPerkIndex; +/** Index of the weapon chosen for the survival perk */ +var public byte SurvivalPerkWeapIndex; +/** Index of the grenade chosen for the survival perk */ +var public byte SurvivalPerkGrenIndex; /** Player zed spawn params (Versus) */ var transient sPlayerZedSpawnInfo PlayerZedSpawnInfo; @@ -136,6 +140,10 @@ var protected float UnmodifiedFOV; var transient protected int BenefactorDosh; var private const int BenefactorDoshReq; +var array KilledZeds; +var array KilledZedsLastZPosition; +var KFSeaTrigger SeaTrigger; + /********************************************************************************************* * @name UDK Variables ********************************************************************************************* */ @@ -712,6 +720,14 @@ var transient bool bNoGoActive; var transient byte StoredLocalUserNum; +/********************************************************************************************* + * @name KFWeap_Autoturret + + With the trader you can buy and sell the weapon without exploding the turrets. Add them to + the controller to keep track of them rather than the weapon. +**********************************************************************************************/ +var transient array DeployedTurrets; + cpptext { virtual UBOOL Tick( FLOAT DeltaSeconds, ELevelTick TickType ); @@ -811,6 +827,8 @@ native private function ShowPreClientTravelMovie(string URLString); simulated event PostBeginPlay() { + local KFSeaTrigger actor_search; + super.PostBeginPlay(); PostAkEvent( ResetFiltersEvent ); @@ -831,6 +849,18 @@ simulated event PostBeginPlay() OnlineSub.AddOnReadOnlineAvatarCompleteDelegate(OnAvatarReceived); OnlineSub.AddOnReadOnlineAvatarByNameCompleteDelegate(OnAvatarURLPS4Received); } + + foreach AllActors(class'KFSeaTrigger', actor_search) + { + SeaTrigger = actor_search; + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + SetTimer(1.f, true, nameOf(ZedKillsSeaDetection)); + } + + break; + } } function UpdateVOIP() @@ -952,7 +982,8 @@ simulated event name GetSeasonalStateName() local int EventId; local KFMapInfo KFMI; local bool bIsWWLWeekly; // WWL Weekly should not allow seasonal overrides - + local KFGameReplicationInfo KFGRI; + EventId = class'KFGameEngine'.static.GetSeasonalEventID(); KFMI = KFMapInfo(WorldInfo.GetMapInfo()); if (KFMI != none) @@ -960,7 +991,8 @@ simulated event name GetSeasonalStateName() KFMI.ModifySeasonalEventId(EventId); } - bIsWWLWeekly = class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12 && KFGameReplicationInfo(WorldInfo.GRI) != none && KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode; + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + bIsWWLWeekly = KFGRI != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 12; if (bIsWWLWeekly) return 'No_Event'; @@ -1566,13 +1598,15 @@ function OnReadProfileSettingsComplete(byte LocalUserNum,bool bWasSuccessful) if(Profile != none) { - SavedPerkIndex = byte(Profile.GetProfileInt(KFID_SavedPerkIndex)); - bSkipNonCriticalForceLookAt = Profile.GetProfileBool(KFID_AutoTurnOff); - bShowKillTicker = Profile.GetProfileBool(KFID_ShowKillTicker); - bNoEarRingingSound = Profile.GetProfileBool(KFID_ReduceHightPitchSounds); - bHideBossHealthBar = Profile.GetProfileBool(KFID_HideBossHealthBar); - bDisableAutoUpgrade = Profile.GetProfileBool(KFID_DisableAutoUpgrade); + SavedPerkIndex = byte(Profile.GetProfileInt(KFID_SavedPerkIndex)); + bSkipNonCriticalForceLookAt = Profile.GetProfileBool(KFID_AutoTurnOff); + bShowKillTicker = Profile.GetProfileBool(KFID_ShowKillTicker); + bNoEarRingingSound = Profile.GetProfileBool(KFID_ReduceHightPitchSounds); + bHideBossHealthBar = Profile.GetProfileBool(KFID_HideBossHealthBar); + bDisableAutoUpgrade = Profile.GetProfileBool(KFID_DisableAutoUpgrade); bHideRemotePlayerHeadshotEffects = Profile.GetProfileBool(KFID_HideRemoteHeadshotEffects); + SurvivalPerkWeapIndex = byte(Profile.GetProfileInt(KFID_SurvivalStartingWeapIdx)); + SurvivalPerkGrenIndex = byte(Profile.GetProfileInt(KFID_SurvivalStartingGrenIdx)); KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo); if(KFPRI != none) @@ -2581,6 +2615,30 @@ function NotifyPlayTogetherFailed(optional string LocKey = "UnableToPlayTogether * @name Dosh Vault ********************************************************************************************* */ +public function bool CanUseDosh() +{ + /** If this is run in Server or Standalone, GameInfo exists so can access to the OutbreakEvent */ + if (Role == Role_Authority) + { + return KFGameInfo(WorldInfo.Game).OutbreakEvent == none + || !KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bDisableAddDosh; + } + /** But in client, GameInfo doesn't exist, so needs to be checked in a different way. */ + else + { + /** + In client there's a kfgame replication info that contains if the mode is a weekly, and the index. + This way would also work in server, but will need to be in code rather than using the weekly variables. + */ + /** Another option is to use instead a variable replicated just with that value */ + return KFGameReplicationInfo(WorldInfo.GRI) == none + || !KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode + || KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex != 16; + } + + return true; +} + function int GetPreStigeValueDoshRewardValue() { if (StatsWrite != none) @@ -2672,6 +2730,34 @@ function CheckHasViewedDoshVault() } } +/********************************************************************************************* + * @name Gun Game +********************************************************************************************* */ + +public function bool CanUseGunGame() +{ + /** If this is run in Server or Standalone, GameInfo exists so can access to the OutbreakEvent */ + if (Role == Role_Authority) + { + return KFGameInfo(WorldInfo.Game).OutbreakEvent != none + && KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bGunGameMode; + } + /** But in client, GameInfo doesn't exist, so needs to be checked in a different way. */ + else + { + /** + In client there's a kfgame replication info that contains if the mode is a weekly, and the index. + This way would also work in server, but will need to be in code rather than using the weekly variables. + */ + /** Another option is to use instead a variable replicated just with that value */ + return KFGameReplicationInfo(WorldInfo.GRI) != none + && KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode + && KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 16; + } + + return false; +} + /********************************************************************************************* * @name Skill Tracking ********************************************************************************************* */ @@ -3089,6 +3175,8 @@ function RecievedNewPerkClass() { MyGfxManager.TraderMenu.UpdatePlayerInfo(); } + + InitPerkLoadout(); } /********************************************************************************************* @@ -3976,7 +4064,7 @@ simulated final function Pawn GetPickedAimAtTarget(out float bestAim, out float return PickAimAtTarget(bestAim, bestDist, FireDir, projStart, MaxRange, bTargetTeammates); } -exec function SwitchToBestWeapon(optional bool bForceNewWeapon) +exec function SwitchToBestWeapon(optional bool bForceNewWeapon, optional bool check_9mm_logic = false) { // Don't let players use an exec to bring up a weapon if the weapon prevents it if( Pawn != none && Pawn.Weapon != none && KFWeapon(Pawn.Weapon) != none ) @@ -3987,7 +4075,7 @@ exec function SwitchToBestWeapon(optional bool bForceNewWeapon) } } - super.SwitchToBestWeapon(bForceNewWeapon); + super.SwitchToBestWeapon(bForceNewWeapon, check_9mm_logic); } /** @@ -4148,6 +4236,7 @@ simulated event OnWeaponAsyncContentLoaded(class WeaponClass) local KFPawn_Human KFPH; local KFDroppedPickup KFDP; + local KFPawn KFP; // Attempt to set the weapon attachment for any player than might need theirs set. This is a backup // for when content isn't quite ready when WeaponClassForAttachmentTemplate is replicated. @@ -4169,6 +4258,14 @@ simulated event OnWeaponAsyncContentLoaded(class WeaponClass) KFDP.SetPickupMesh(WeaponClass.default.DroppedPickupMesh); } } + + foreach WorldInfo.AllPawns(class'KFPawn', KFP) + { + if (KFP.bIsTurret && WeaponClass == KFP.WeaponClassForAttachmentTemplate) + { + KFP.SetTurretWeaponAttachment(WeaponClass); + } + } } /********************************************************************************************* @@ -5285,7 +5382,9 @@ function bool ShouldDisplayGameplayPostProcessFX() /* ZED time effect is active */ CurrentZEDTimeEffectIntensity > 0.f || /* sepia effect */ - (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12) || + (KFGameReplicationInfo(WorldInfo.GRI) != none && + KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode && + KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 12) || /* Night vision active */ bNightVisionActive || SirenScreamEffectTimeRemaining > 0.f || @@ -7154,6 +7253,8 @@ simulated function OnStatsInitialized( bool bWasSuccessful ) LoadAllPerkLevels(); ClientInitializePerks(); + InitPerkLoadout(); + // Update the GFX menu if we need to if( MyGFxManager != none && MyGFxManager.PerksMenu != none ) { @@ -9186,6 +9287,13 @@ event Destroyed() StingerAkComponent.StopEvents(); } + // Destroy deployed turrets + // Destroyed event on Turret calls the KFPlayer to modify the same list, that's why we use a while loop... + while (DeployedTurrets.Length > 0) + { + DeployedTurrets[0].Destroy(); + } + SetRTPCValue( 'Health', 100, true ); PostAkEvent( LowHealthStopEvent ); bPlayingLowHealthSFX = false; @@ -9215,6 +9323,7 @@ event Destroyed() ClearMixerDelegates(); ClearDiscord(); } + ClientMatchEnded(); Super.Destroyed(); @@ -9340,6 +9449,43 @@ state Dead } } +simulated function ZedKillsSeaDetection() +{ + local int i; + local KFPawn_Monster Monster; + local float LastMonsterZPosition; + + for (i = KilledZeds.Length - 1; i >= 0; i--) + { + Monster = KilledZeds[i]; + + // If respawned... + if (Monster == None || Monster.Health > 0) + { + KilledZeds.Remove(i, 1); + KilledZedsLastZPosition.Remove(i, 1); + continue; + } + + LastMonsterZPosition = KilledZedsLastZPosition[i]; + + KilledZedsLastZPosition[i] = Monster.Location.Z; + + // If we are falling... + if (LastMonsterZPosition > Monster.Location.Z) + { + // If we passed the point of the sea trigger detection + if (Monster.Location.Z < SeaTrigger.Location.Z) + { + ClientOnTriggerUsed(class'KFSeaTrigger'); + + KilledZeds.Remove(i, 1); + KilledZedsLastZPosition.Remove(i, 1); + } + } + } +} + /** * Should be to instigate a transition to spectating, automatically handles notifying server or notifying the owning client as necessary. */ @@ -9916,6 +10062,15 @@ unreliable client event ClientHearDialog( Actor DialogSpeaker, AkEvent DialogEve } } +reliable client function NotifyKilledForTracking(KFPawn_Monster KilledMonster) +{ + if (KilledMonster != None && KilledZeds.Find(KilledMonster) == INDEX_NONE) + { + KilledZeds.AddItem(KilledMonster); + KilledZedsLastZPosition.AddItem(KilledMonster.Location.Z); + } +} + function NotifyKilled( Controller Killer, Controller Killed, pawn KilledPawn, class damageType ) { local KFPawn_Monster MonsterPawn; @@ -9937,6 +10092,12 @@ function NotifyKilled( Controller Killer, Controller Killed, pawn KilledPawn, cl MatchStats.ZedsKilledLastWave++; CheckForZedOnDeathAchievements( MonsterPawn ); + + // If we need to track killed zeds, make the function to be called on client... + if (SeaTrigger != none) + { + NotifyKilledForTracking(MonsterPawn); + } } // Own death. Like PawnDied(), but with more input parameters else if ( self == Killed ) @@ -10065,7 +10226,7 @@ exec function RequestSkipTrader() { if (KFGRI.bMatchHasBegun) { - if (KFGRI.bTraderIsOpen && KFPRI.bHasSpawnedIn) + if ((KFGRI.bTraderIsOpen || KFGRI.bForceSkipTraderUI) && KFPRI.bHasSpawnedIn) { KFPRI.RequestSkiptTrader(KFPRI); if (MyGFxManager != none) @@ -11728,6 +11889,24 @@ event OnServerTakeoverResponseRecieved() } } +reliable client function ForceMonsterHeadExplode(KFPawn_Monster Victim) +{ + if (Victim != none) + { + Victim.bIsHeadless=true; + Victim.PlayHeadAsplode(); + } +} + +simulated function InitPerkLoadout() +{ + if (CurrentPerk.IsA('KFPerk_Survivalist')) + { + CurrentPerk.SetWeaponSelectedIndex(SurvivalPerkWeapIndex); + CurrentPerk.SetGrenadeSelectedIndex(SurvivalPerkGrenIndex); + } +} + defaultproperties { EarnedDosh=0 diff --git a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc index c71df50..cb7fa5a 100644 --- a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc +++ b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc @@ -38,8 +38,26 @@ var protected const AkEvent RhythmMethodSoundReset; var protected const AkEvent RhythmMethodSoundHit; var protected const AkEvent RhythmMethodSoundTop; var protected const AkEvent AracnoStompSoundEvent; +var protected const AKEvent GunGameLevelUpSoundEvent; +var protected const AKEvent GunGameLevelUpFinalWeaponSoundEvent; +struct native GunGameInfo +{ + var transient byte Level; + var transient int Score; + var array GunGamePreselectedWeapons; + var byte WaveToUseForRestart; + var bool GiveWeaponMaster; +structdefaultproperties +{ + Level=0; + Score=0; + WaveToUseForRestart=0; + GiveWeaponMaster=false; +} +}; +var transient GunGameInfo GunGameData; cpptext { @@ -49,7 +67,7 @@ cpptext replication { if (bNetDirty) - bUsingPermanentZedTime, ZedTimeRadius, ZedTimeBossRadius, ZedTimeHeight, GoompaStreak; + bUsingPermanentZedTime, ZedTimeRadius, ZedTimeBossRadius, ZedTimeHeight, GoompaStreak, GunGameData; } simulated event PostBeginPlay() @@ -114,6 +132,22 @@ function RecheckZedTime() EnterZedTime(); } +reliable client function UpdateWaveCount() +{ + if (MyGFxHUD != none) + { + MyGFxHUD.UpdateWaveCount(); + } +} + +reliable client function UpdateGunGameWidget(int score, int max_score, int level, int max_level) +{ + if (MyGFxHUD != none) + { + MyGFxHUD.UpdateGunGameWidget(score, max_score, level, max_level); + } +} + /** Arachnophobia Goompa Stomp Streak functions */ @@ -195,13 +229,35 @@ reliable client function GoompaStompMessage( byte StompNum) } } +reliable client function PlayGunGameMessage(bool isLastLevel) +{ + if (isLastLevel) + { + if (GunGameLevelUpFinalWeaponSoundEvent != none) + { + PlaySoundBase(GunGameLevelUpFinalWeaponSoundEvent); + } + } + else + { + if (GunGameLevelUpSoundEvent != none) + { + PlaySoundBase(GunGameLevelUpSoundEvent); + } + } +} + /** Resets all gameplay FX to initial state. Append to this list if additional effects are added. */ function ResetGameplayPostProcessFX() { + local KFGameReplicationInfo KFGRI; + super.ResetGameplayPostProcessFX(); - if( GameplayPostProcessEffectMIC != none && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + if((KFGRI != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 12) && GameplayPostProcessEffectMIC != none) { GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectZedTimeSepiaParamName, 1.f); } @@ -228,6 +284,55 @@ simulated function ResetBossCamera() super(PlayerController).ResetCameraMode(); } +function RestartGunGame() +{ + local KFGameInfo KFGI; + local KFGameReplicationInfo KFGRI; + + KFGI = KFGameInfo(WorldInfo.Game); + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + if (KFGI != none && KFGRI != none) + { + KFGI.RestartGunGamePlayerWeapon(self, GunGameData.WaveToUseForRestart); + } +} + +function UpdateInitialHeldWeapon() +{ + //local KFWeapon KFW; + local KFPawn_Human KFPH; + local KFGameInfo KFGI; + local KFGameReplicationInfo KFGRI; + + KFPH = KFPawn_Human(Pawn); + + if (KFPH == none || KFPH.InvManager == none) + { + return; + } + + /*foreach KFPH.InvManager.InventoryActors( class'KFWeapon', KFW ) + { + /** Seems its in order, so knife goes first. Equip it */ + + KFPH.InvManager.SetCurrentWeapon(KFW); + break; + }*/ + + KFGI = KFGameInfo(WorldInfo.Game); + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + if (KFGI != none && KFGRI != none) + { + KFGI.ResetGunGame(self); + + GunGameData.WaveToUseForRestart = KFGRI.WaveNum; + + SetTimer(1.0, false, 'RestartGunGame'); + } +} + // defaultProperties { @@ -239,4 +344,6 @@ defaultProperties RhythmMethodSoundHit =AkEvent'WW_UI_PlayerCharacter.Play_R_Method_Hit' RhythmMethodSoundTop =AkEvent'WW_UI_PlayerCharacter.Play_R_Method_Top' AracnoStompSoundEvent =AkEvent'WW_GLO_Runtime.WeeklyArcno' + GunGameLevelUpSoundEvent=AkEvent'WW_GLO_Runtime.WeeklyAALevelUp' + GunGameLevelUpFinalWeaponSoundEvent=AkEvent'WW_GLO_Runtime.WeeklyAALevelFinal' } diff --git a/KFGame/Classes/KFPlayerInput.uc b/KFGame/Classes/KFPlayerInput.uc index 34ec632..85de67d 100644 --- a/KFGame/Classes/KFPlayerInput.uc +++ b/KFGame/Classes/KFPlayerInput.uc @@ -2062,8 +2062,10 @@ function vector GetBestAutoTargetLocation(Pawn CheckTarget, out name outBoneName if( KFP != none ) { // Get the location from the pawn we're targeting if we can - KFP.GetAutoTargetBones(WeakBones, NormalBones); - + if (!KFP.GetAutoTargetBones(WeakBones, NormalBones)) + { + return vect(0,0,0); + } // cone setup GetPlayerViewPoint( CamLoc, CamRot ); CamRot += WeaponBufferRotation; diff --git a/KFGame/Classes/KFPlayerReplicationInfo.uc b/KFGame/Classes/KFPlayerReplicationInfo.uc index b4b95e4..13472cc 100644 --- a/KFGame/Classes/KFPlayerReplicationInfo.uc +++ b/KFGame/Classes/KFPlayerReplicationInfo.uc @@ -1351,6 +1351,15 @@ reliable server private function ServerSetPlayerReady( bool bReady ) /** Called on server to +/- dosh. Do not modify score directly */ function AddDosh( int DoshAmount, optional bool bEarned ) { + local KFPlayerController KFPC; + + /** Server code: controllers exist */ + KFPC = KFPlayerController(Owner); + if (KFPC != none && !KFPC.CanUseDosh()) + { + return; + } + //If the game has turned off dosh earning for this PRI, early out. if (!bAllowDoshEarning && bEarned) { @@ -1465,6 +1474,10 @@ simulated function NotifyWaveEnded() { bVotedToSkipTraderTime = false; + if ( (WorldInfo.NetMode == NM_Standalone || WorldInfo.NetMode == NM_Client) && KFPlayerController(Owner) != none && KFPlayerController(Owner).GetPerk() != none) + { + KFPlayerController(Owner).GetPerk().OnClientWaveEnded(); + } /*local KFGameReplicationInfo KFGRI; if( Role == ROLE_Authority ) @@ -1475,6 +1488,11 @@ simulated function NotifyWaveEnded() KFGRI.VoteCollector.ResetSkipTraderBeforeWaveStarts(); } }*/ + + if ( (WorldInfo.NetMode == NM_Standalone || WorldInfo.NetMode == NM_Client) && KFPlayerController(Owner) != none && KFPlayerController(Owner).GetPerk() != none) + { + KFPlayerController(Owner).GetPerk().OnClientWaveEnded(); + } } //reset the icons here diff --git a/KFGame/Classes/KFProfileSettings.uc b/KFGame/Classes/KFProfileSettings.uc index e845920..73e8428 100644 --- a/KFGame/Classes/KFProfileSettings.uc +++ b/KFGame/Classes/KFProfileSettings.uc @@ -380,4 +380,10 @@ defaultproperties // Added 16/07/2021 - QoL: Quick Swap button allowing 9mm as an option. ProfileMappings.Add((Id=KFID_AllowSwapTo9mm, Name="AllowSwitchTo9mm", MappingType=PVMT_RawValue)) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_AllowSwapTo9mm,Data=(Type=SDT_Int32,Value1=0)))) + + // Added 07/02/2022 - QoL: Allow survivalits to chose starting weapons. + ProfileMappings.Add((Id=KFID_SurvivalStartingWeapIdx, Name="Survival Starting Weapon Index", MappingType=PVMT_RawValue)) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SurvivalStartingWeapIdx,Data=(Type=SDT_Int32,Value1=0)))) + ProfileMappings.Add((Id=KFID_SurvivalStartingGrenIdx, Name="Survival Starting Grenade Index", MappingType=PVMT_RawValue)) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SurvivalStartingGrenIdx,Data=(Type=SDT_Int32,Value1=0)))) } diff --git a/KFGame/Classes/KFSM_Frozen.uc b/KFGame/Classes/KFSM_Frozen.uc index 0052419..4da9369 100644 --- a/KFGame/Classes/KFSM_Frozen.uc +++ b/KFGame/Classes/KFSM_Frozen.uc @@ -182,6 +182,8 @@ function SetFrozenParameter(float FreezeAmount) local MaterialInstanceConstant MIC; local int i; local bool bIsWWLMode; + local KFGameReplicationInfo KFGRI; + if ( PawnOwner.WorldInfo.NetMode != NM_DedicatedServer ) { FreezeMatParamValue = FreezeAmount; @@ -201,7 +203,8 @@ function SetFrozenParameter(float FreezeAmount) if (KFPawn_Monster(KFPOwner) != none) { - bIsWWLMode = class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12 && KFGameReplicationInfo(PawnOwner.WorldInfo.GRI) != none && KFGameReplicationInfo(PawnOwner.WorldInfo.GRI).bIsWeeklyMode; + KFGRI = KFGameReplicationInfo(PawnOwner.WorldInfo.GRI); + bIsWWLMode = KFGRI != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 12; for (i = 0; i < KFPawn_Monster(KFPOwner).StaticAttachList.length; i++) { diff --git a/KFGame/Classes/KFSeaTrigger.uc b/KFGame/Classes/KFSeaTrigger.uc new file mode 100644 index 0000000..5d7e003 --- /dev/null +++ b/KFGame/Classes/KFSeaTrigger.uc @@ -0,0 +1,41 @@ +//============================================================================= +// KFSeaTrigger +//============================================================================= +// Simple trigger used for sea to bypass kismet +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= +class KFSeaTrigger extends Trigger_PawnsOnly + placeable + native; + +// This class is totally dummy, it is used to place the trigger on the Scene +// Then on KFPlayerController we detect this exists and do the logic for Zeds falling to the sea + +cpptext +{ +#if WITH_EDITOR + virtual void CheckForErrors(); // Skip 'Trigger is not referenced' warning +#endif +} + +event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal) +{ +} + +event UnTouch(Actor Other) +{ +} + +DefaultProperties +{ + Begin Object NAME=CollisionCylinder + CollisionRadius=+00200.000000 + CollisionHeight=+00100.000000 + End Object + + bProjTarget=false + bStatic=true +} \ No newline at end of file diff --git a/KFGame/Classes/KFUnlockManager.uc b/KFGame/Classes/KFUnlockManager.uc index e595fe7..1c3cd59 100644 --- a/KFGame/Classes/KFUnlockManager.uc +++ b/KFGame/Classes/KFUnlockManager.uc @@ -32,7 +32,9 @@ enum ESharedContentUnlock SCU_Thermite, SCU_BladedPistol, SCU_ParasiteImplanter, - SCU_Doshinegun + SCU_Doshinegun, + SCU_AutoTurret, + SCU_ShrinkRayGun }; @@ -212,6 +214,7 @@ static private event bool CheckCustomizationOwnership(KFPlayerReplicationInfo PR local SkinVariant Skin; local AttachmentVariants Attachment; local int i; + local KFGameReplicationInfo KFGRI; CharArch = PRI.CharacterArchetypes[PRI.RepCustomizationInfo.CharacterIndex]; @@ -246,10 +249,12 @@ static private event bool CheckCustomizationOwnership(KFPlayerReplicationInfo PR return FALSE; } + + KFGRI = KFGameReplicationInfo(PRI.WorldInfo.GRI); // accessory for( i=0; i < `MAX_COSMETIC_ATTACHMENTS; i++ ) { - if (i == 2 && PRI.WorldInfo.GRI.IsA('KFGameReplicationInfo_WeeklySurvival') && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) + if (i == 2 && KFGRI != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 12) { continue; } @@ -360,4 +365,12 @@ defaultproperties Name=KFWeap_AssaultRifle_Doshinegun, IconPath="WEP_UI_Doshinegun_TEX.UI_Weapon_Select_Doshinegun", ID=9275)} + SharedContentList(SCU_AutoTurret)={( + Name=KFWeap_AutoTurret, + IconPath="WEP_UI_AutoTurret_TEX.UI_WeaponSelect_AutoTurret", + ID=9284)} + SharedContentList(SCU_ShrinkRayGun)={( + Name=KFWeap_ShrinkRayGun, + IconPath="WEP_UI_ShrinkRay_Gun_TEX.UI_Weapon_Select_Shrink_Ray_Gun", + ID=9290)} } diff --git a/KFGame/Classes/KFVoteCollector.uc b/KFGame/Classes/KFVoteCollector.uc index 78de2a2..405e68a 100644 --- a/KFGame/Classes/KFVoteCollector.uc +++ b/KFGame/Classes/KFVoteCollector.uc @@ -434,7 +434,7 @@ function ServerStartVoteSkipTrader(PlayerReplicationInfo PRI) } // Trader is not open, we are not allowed to initiate a skip trader vote - if(!KFGRI.bTraderIsOpen) + if(!KFGRI.bTraderIsOpen && !KFGRI.bForceShowSkipTrader) { KFPC.ReceiveLocalizedMessage(class'KFLocalMessage', LMT_SkipTraderIsNotOpen); return; diff --git a/KFGame/Classes/KFWeapAttach_SprayBase.uc b/KFGame/Classes/KFWeapAttach_SprayBase.uc index c17e117..5679458 100644 --- a/KFGame/Classes/KFWeapAttach_SprayBase.uc +++ b/KFGame/Classes/KFWeapAttach_SprayBase.uc @@ -31,6 +31,8 @@ var AkEvent PilotLightPlayEvent; /** Pilot light sound stop event */ var AkEvent PilotLightStopEvent; +var protected bool bInvertPilot; + /** Effect for the pilot light. */ var protected KFParticleSystemComponent PSC_PilotLight; /** Socket to attach the pilot light to. */ @@ -263,9 +265,17 @@ simulated function StopPilotSound() simulated function SetPilotDynamicLightEnabled( bool bLightEnabled ) { local int Idx; + local bool doEnable; + + doEnable = bLightEnabled; + + if (bInvertPilot) + { + doEnable = bLightEnabled == false; + } // Don't turn these on if we're not in third person - if( bLightEnabled && (Instigator != none && Instigator.IsFirstPerson()) ) + if (doEnable && (Instigator != none && Instigator.IsFirstPerson())) { return; } @@ -273,7 +283,7 @@ simulated function SetPilotDynamicLightEnabled( bool bLightEnabled ) // turn off lights for (Idx=0; Idx 0 ) + if (ActiveFlameSpray != none) + { + FlameHeat = ActiveFlameSpray.MaterialHeatRange.X; + } + else if (FlameSprayArchetype != none) + { + FlameHeat = FlameSprayArchetype.default.MaterialHeatRange.X; + } + else + { + FlameHeat = 0; + } + + if( BarrelHeat != FlameHeat ) { // Cool the barrel down when not shooting - BarrelHeat -= DeltaTime * 0.5; - if( BarrelHeat < 0 ) + BarrelHeat -= DeltaTime * CooldownBarrelModifier; + if( BarrelHeat < FlameHeat ) { - BarrelHeat = 0; + BarrelHeat = FlameHeat; } } } @@ -340,9 +358,17 @@ simulated protected function TurnOffPilot() simulated function SetPilotDynamicLightEnabled( bool bLightEnabled ) { local int Idx; + local bool doEnable; + + doEnable = bLightEnabled; + + if (bInvertPilot) + { + doEnable = bLightEnabled == false; + } // Don't turn these on if we're not the local playercontroller - if( bLightEnabled && (Instigator == none || !Instigator.IsLocallyControlled() || !Instigator.IsFirstPerson()) ) + if (doEnable && (Instigator == none || !Instigator.IsLocallyControlled() || !Instigator.IsFirstPerson())) { return; } @@ -350,7 +376,7 @@ simulated function SetPilotDynamicLightEnabled( bool bLightEnabled ) // turn off lights for (Idx=0; Idx WeaponClass); -native private function StartLoadWeaponContent(); +native protected function StartLoadWeaponContent(); native private function LoadWeaponContent(); native private function CacheWeaponContent(); native private function UnloadWeaponContent(); @@ -1618,6 +1618,21 @@ simulated function AttachLaserSight() } } +function GunGameRemove() +{ + if (Instigator != none && Instigator.InvManager != none) + { + Instigator.InvManager.RemoveFromInventory(Self); + } + + Instigator = None; + GotoState(''); + + AIController = None; + + Destroy(); +} + /** * Drop this item out in to the world */ @@ -1815,9 +1830,24 @@ reliable client function ClientNotifyPickedUp() } /** Treat as non-standard equipment item for */ -simulated static function bool DenyPerkResupply() +simulated function bool DenyPerkResupply() { - return default.InventoryGroup >= IG_Equipment; + if (default.InventoryGroup >= IG_Equipment) + { + if (Class.Name == 'KFWeap_Thrown_C4') + { + return false; + } + + if (Class.Name == 'KFWeap_AutoTurret') + { + return false; + } + + return true; + } + + return false; } simulated static function bool IsMeleeWeapon() @@ -6488,7 +6518,7 @@ simulated state WeaponFiring Instigator.WeaponStoppedFiring(self, false); } - if ( bPlayingLoopingFireAnim || bPlayingLoopingFireAnim ) + if ( bPlayingLoopingFireAnim || bPlayingLoopingFireSnd ) { StopLoopingFireEffects(CurrentFireMode); } diff --git a/KFGame/Classes/KFWeaponSkinList.uc b/KFGame/Classes/KFWeaponSkinList.uc index 25aaf5a..068e8e1 100644 --- a/KFGame/Classes/KFWeaponSkinList.uc +++ b/KFGame/Classes/KFWeaponSkinList.uc @@ -2748,6 +2748,9 @@ defaultproperties //Dragon & Koi Pulverizer Skins.Add((Id=7519, Weapondef=class'KFWeapDef_Pulverizer', MIC_1P=("WEP_SkinSet_DragonKoi_03_MAT.dragonkoi_pulverizer.DragonKoi_Pulverizer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet_DragonKoi_03_MAT.dragonkoi_pulverizer.DragonKoi_Pulverizer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet_DragonKoi_03_MAT.dragonkoi_pulverizer.DragonKoi_Pulverizer_3P_Pickup_MIC")) +//Standard Ion Sword + Skins.Add((Id=7715, Weapondef=class'KFWeapDef_IonThruster', MIC_1P=("WEP_1P_Ion_Sword_MAT.Wep_1stP_Ion_Sword_MIC"), MIC_3P="WEP_3P_Ion_Sword_MAT.Wep_3rdP_Ion_Sword_MIC", MIC_Pickup="WEP_3P_Ion_Sword_MAT.Wep_3rdP_Ion_Sword_Pickup_MIC")) + //Dosh Royale Ion Sword Skins.Add((Id=7716, Weapondef=class'KFWeapDef_IonThruster', MIC_1P=("WEP_1P_Ion_Sword_MAT.Wep_1st_Ion_Sword_DoshRoyale_MIC"), MIC_3P="WEP_3P_Ion_Sword_MAT.Wep_3rdP_Ion_Sword_DoshRoyale_MIC", MIC_Pickup="WEP_3P_Ion_Sword_MAT.Wep_3rdP_Ion_Sword_DoshRoyale_Pickup_MIC")) @@ -2763,6 +2766,9 @@ defaultproperties //Shatter Ion Sword Skins.Add((Id=7720, Weapondef=class'KFWeapDef_IonThruster', MIC_1P=("WEP_1P_Ion_Sword_MAT.Wep_1st_Ion_Sword_Shatter_MIC"), MIC_3P="WEP_3P_Ion_Sword_MAT.Wep_3rdP_Ion_Sword_Shatter_MIC", MIC_Pickup="WEP_3P_Ion_Sword_MAT.Wep_3rdP_Ion_Sword_Shatter_Pickup_MIC")) +//Standard Chiappa Rhino + Skins.Add((Id=7704, Weapondef=class'KFWeapDef_ChiappaRhino', MIC_1P=("wep_1p_chiapparhino_mat.WEP_1P_ChiappaRhino_MIC"), MIC_3P="WEP_3P_ChiappoRhino_MAT.WEP_3P_ChiappaRhino_MIC", MIC_Pickup="WEP_3P_ChiappoRhino_MAT.3P_Pickup_ChiappaRhino_MIC")) + //Dosh Royale Chiappa Rhino Skins.Add((Id=7705, Weapondef=class'KFWeapDef_ChiappaRhino', MIC_1P=("wep_1p_chiapparhino_mat.WEP_1P_ChiappaRhinos_DoshRoyale_MIC"), MIC_3P="WEP_3P_ChiappoRhino_MAT.WEP_3P_ChiappaRhino_DoshRoyale_MIC", MIC_Pickup="WEP_3P_ChiappoRhino_MAT.3P_Pickup_ChiappaRhino_DoshRoyale_MIC")) @@ -2798,6 +2804,26 @@ defaultproperties Skins.Add((Id=7796, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("WEP_SkinSet27_MAT.jaeger_microwaveassault.Jaeger_MicrowaveAssault_1P_FieldTested_MIC"), MIC_3P="WEP_SkinSet27_MAT.jaeger_microwaveassault.Jaeger_MicrowaveAssault_3P_FieldTested_MIC", MIC_Pickup="WEP_SkinSet27_MAT.jaeger_microwaveassault.Jaeger_MicrowaveAssault_3P_Pickup_MIC")) Skins.Add((Id=7797, Weapondef=class'KFWeapDef_MicrowaveRifle', MIC_1P=("WEP_SkinSet27_MAT.jaeger_microwaveassault.Jaeger_MicrowaveAssault_1P_Mint_MIC"), MIC_3P="WEP_SkinSet27_MAT.jaeger_microwaveassault.Jaeger_MicrowaveAssault_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet27_MAT.jaeger_microwaveassault.Jaeger_MicrowaveAssault_3P_Pickup_MIC")) +//Jaeger Desert Eagle + //BattleScarred + Skins.Add((Id=7798, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_1P_BattleScarred_MIC"), MIC_3P="WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_3P_BattleScarred_MIC", MIC_Pickup="WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_3P_Pickup_MIC")) + + //Field Tested + Skins.Add((Id=7799, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_1P_FieldTested_MIC"), MIC_3P="WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_3P_FieldTested_MIC", MIC_Pickup="WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_3P_Pickup_MIC")) + + //Mint + Skins.Add((Id=7800, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_3P_Pickup_MIC")) + +//Jaeger P90 + //BattleScarred + Skins.Add((Id=7804, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet27_MAT.jaeger_p90.jaeger_P90_1P_BattleScarred_MIC"), MIC_3P="WEP_SkinSet27_MAT.jaeger_p90.Jaeger_P90_3P_BattleScarred_MIC", MIC_Pickup="WEP_SkinSet27_MAT.jaeger_p90.Jaeger_P90_3P_Pickup_MIC")) + + //Field Tested + Skins.Add((Id=7805, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet27_MAT.jaeger_p90.jaeger_P90_1P_FieldTested_MIC"), MIC_3P="WEP_SkinSet27_MAT.jaeger_p90.Jaeger_P90_3P_FieldTested_MIC", MIC_Pickup="WEP_SkinSet27_MAT.jaeger_p90.Jaeger_P90_3P_Pickup_MIC")) + + //Mint + Skins.Add((Id=7806, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet27_MAT.jaeger_p90.jaeger_P90_1P_Mint_MIC"), MIC_3P="WEP_SkinSet27_MAT.jaeger_p90.Jaeger_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet27_MAT.jaeger_p90.Jaeger_P90_3P_Pickup_MIC")) + //Jaeger Desert Eagle BattleScarred Skins.Add((Id=8031, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_1P_BattleScarred_MIC"), MIC_3P="WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_3P_BattleScarred_MIC", MIC_Pickup="WEP_SkinSet27_MAT.jaeger_deagle.Jaeger_Deagle_3P_Pickup_MIC")) @@ -3613,6 +3639,42 @@ defaultproperties //Doshinegun Koi Dream Skins.Add((Id=9276, Weapondef=class'KFWeapDef_Doshinegun', MIC_1P=("wep_skinset53_mat.Wep_Doshinegun_koidream_body_PM", "wep_skinset53_mat.Wep_Doshinegun_koidream_mag_PM"), MIC_3P="WEP_SkinSet53_MAT.Wep_Doshinegun_koidream_3P_PM", MIC_Pickup="WEP_SkinSet53_MAT.Wep_Doshinegun_koidream_3P_Pickup_MIC")); +//Sentinel Standard + Skins.Add((Id=9284, Weapondef=class'KFWeapDef_AutoTurret', MIC_1P=("wep_1p_autoturret_mat.Wep_1P_AutoTurret_MIC","Wep_1P_AutoTurret_MAT.Wep_1P_Remote_MIC"), MIC_3P="WEP_3P_AutoTurret_MAT.WEP_3P_AutoTurret_MIC", MIC_Pickup="wep_3p_autoturret_mat.3P_Pickup_AutoTurret_MIC")) + +//Sentinel 1942 + Skins.Add((Id=9285, Weapondef=class'KFWeapDef_AutoTurret', MIC_1P=("WEP_SkinSet54_MAT.Wep_1P_1942_AutoTurret_MIC","WEP_SkinSet54_MAT.Wep_1P_1942_Remote_MIC"), MIC_3P="wep_skinset54_mat.Wep_3P_1942_AutoTurret_MIC", MIC_Pickup="wep_skinset54_mat.Wep_3P_1942_AutoTurrent_Pickup_MIC")) + +//Sentinel Black Bird + Skins.Add((Id=9286, Weapondef=class'KFWeapDef_AutoTurret', MIC_1P=("WEP_SkinSet54_MAT.Wep_1P_BlackBird_AutoTurret_MIC","WEP_SkinSet54_MAT.Wep_1P_BlackBird_Remote_MIC"), MIC_3P="wep_skinset54_mat.Wep_3P_BlackBird_AutoTurret_MIC", MIC_Pickup="wep_skinset54_mat.Wep_3P_BlackBird_AutoTurrent_Pickup_MIC")) + +//Sentinel Hades + Skins.Add((Id=9287, Weapondef=class'KFWeapDef_AutoTurret', MIC_1P=("WEP_SkinSet54_MAT.Wep_1P_Hades_AutoTurret_MIC","WEP_SkinSet54_MAT.Wep_1P_Hades_Remote_MIC"), MIC_3P="wep_skinset54_mat.Wep_3P_Hades_AutoTurret_MIC", MIC_Pickup="wep_skinset54_mat.Wep_3P_Hades_AutoTurrent_Pickup_MIC")) + +//Sentinel Ice Storm + Skins.Add((Id=9288, Weapondef=class'KFWeapDef_AutoTurret', MIC_1P=("WEP_SkinSet54_MAT.Wep_1P_IceStorm_AutoTurret_MIC","WEP_SkinSet54_MAT.Wep_1P_IceStorm_Remote_MIC"), MIC_3P="wep_skinset54_mat.Wep_3P_IceStorm_AutoTurret_MIC", MIC_Pickup="wep_skinset54_mat.Wep_3P_IceStorm_AutoTurrent_Pickup_MIC")) + +//Sentinel Shock + Skins.Add((Id=9289, Weapondef=class'KFWeapDef_AutoTurret', MIC_1P=("WEP_SkinSet54_MAT.Wep_1P_Shock_AutoTurret_MIC","WEP_SkinSet54_MAT.Wep_1P_Shock_Remote_MIC"), MIC_3P="wep_skinset54_mat.Wep_3P_Shock_AutoTurret_MIC", MIC_Pickup="wep_skinset54_mat.Wep_3P_Shock_AutoTurrent_Pickup_MIC")) + +//Reducto Ray Standard + Skins.Add((Id=9290, Weapondef=class'KFWeapDef_ShrinkRayGun', MIC_1P=("wep_1p_shrinkray_gun_mat.Wep_1P_ShrinkRay_Gun_MIC","WEP_1P_ShrinkRay_Gun_MAT.WEP_ShrinkRay_Glass_MIC"), MIC_3P="wep_3p_shrinkray_gun_mat.WEP_3P_ShrinkRay_Gun_MIC", MIC_Pickup="wep_3p_shrinkray_gun_mat.3P_Pickup_ShrinkRay_Gun_MIC")) + +//Reducto Ray Atomic Love + Skins.Add((Id=9291, Weapondef=class'KFWeapDef_ShrinkRayGun', MIC_1P=("wep_skinset55_mat.Wep_1P_AtomicLove_ShrinkRay_Gun_MIC","WEP_1P_ShrinkRay_Gun_MAT.WEP_ShrinkRay_Glass_MIC"), MIC_3P="wep_skinset55_mat.Wep_3P_AtomicLove_ShrinkRay_Gun_MIC", MIC_Pickup="wep_skinset55_mat.Wep_3P_AtomicLove_ShrinkRay_Pickup_MIC")) + +//Reducto Ray Destructor + Skins.Add((Id=9292, Weapondef=class'KFWeapDef_ShrinkRayGun', MIC_1P=("wep_skinset55_mat.Wep_1P_Destructor_ShrinkRay_Gun_MIC","WEP_1P_ShrinkRay_Gun_MAT.WEP_ShrinkRay_Glass_MIC"), MIC_3P="wep_skinset55_mat.Wep_3P_Destructor_ShrinkRay_Gun_MIC", MIC_Pickup="wep_skinset55_mat.Wep_3P_Destructor_ShrinkRay_Pickup_MIC")) + +//Reducto Ray Heavy Metal + Skins.Add((Id=9293, Weapondef=class'KFWeapDef_ShrinkRayGun', MIC_1P=("wep_skinset55_mat.Wep_1P_HeavyMetal_ShrinkRay_Gun_MIC","WEP_1P_ShrinkRay_Gun_MAT.WEP_ShrinkRay_Glass_MIC"), MIC_3P="wep_skinset55_mat.Wep_3P_HeavyMetal_ShrinkRay_Gun_MIC", MIC_Pickup="wep_skinset55_mat.Wep_3P_HeavyMetal_ShrinkRay_Pickup_MIC")) + +//Reducto Ray HellsFury + Skins.Add((Id=9294, Weapondef=class'KFWeapDef_ShrinkRayGun', MIC_1P=("wep_skinset55_mat.Wep_1P_HellsFury_ShrinkRay_Gun_MIC","WEP_1P_ShrinkRay_Gun_MAT.WEP_ShrinkRay_Glass_MIC"), MIC_3P="wep_skinset55_mat.Wep_3P_HellsFury_ShrinkRay_Gun_MIC", MIC_Pickup="wep_skinset55_mat.Wep_3P_HellsFury_ShrinkRay_Pickup_MIC")) + +//Reducto Ray Lucky Strike + Skins.Add((Id=9295, Weapondef=class'KFWeapDef_ShrinkRayGun', MIC_1P=("wep_skinset55_mat.Wep_1P_LuckyStrike_ShrinkRay_Gun_MIC","WEP_1P_ShrinkRay_Gun_MAT.WEP_ShrinkRay_Glass_MIC"), MIC_3P="wep_skinset55_mat.Wep_3P_LuckyStrike_ShrinkRay_Gun_MIC", MIC_Pickup="wep_skinset55_mat.Wep_3P_LuckyStrike_ShrinkRay_Pickup_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")) @@ -3858,4 +3920,183 @@ defaultproperties //XMas Sour Spitfire Skins.Add((Id=9217, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Flaregun', MIC_1P=("WEP_SkinSet52_MAT.sour_flaregun.Sour_FlareGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet52_MAT.sour_flaregun.Sour_FlareGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet52_MAT.sour_flaregun.Sour_FlareGun_3P_Pickup_MIC")) + +//Neon Boomstick + Skins.Add((Id=9335, Weapondef=class'KFWeapDef_DoubleBarrel', MIC_1P=("wep_skinset56_mat.neon_doublebarrel.Neon_DoubleBarrel_1P_Mint_MIC"), MIC_3P="wep_skinset56_mat.neon_doublebarrel.Neon_DoubleBarrel_3P_Mint_MIC", MIC_Pickup="wep_skinset56_mat.neon_doublebarrel.Neon_DoubleBarrel_3P_Pickup_MIC")) + +//Neon HZ12 + Skins.Add((Id=9336, Weapondef=class'KFWeapDef_HZ12', MIC_1P=("wep_skinset56_mat.neon_hz12.Neon_HZ12_1P_Mint_MIC"), MIC_3P="wep_skinset56_mat.neon_hz12.Neon_HZ12_3P_Mint_MIC", MIC_Pickup="wep_skinset56_mat.neon_hz12.Neon_HZ12_3P_Pickup_MIC")) + +//Neon HRG Beluga Beat + Skins.Add((Id=9337, Weapondef=class'KFWeapDef_HRG_SonicGun', MIC_1P=("wep_skinset56_mat.neon_hrgsonicgun.Neon_HRGSonicGun_1P_Mint_MIC"), MIC_3P="wep_skinset56_mat.neon_hrgsonicgun.Neon_HRGSonicGun_3P_Mint_MIC", MIC_Pickup="wep_skinset56_mat.neon_hrgsonicgun.Neon_HRGSonicGun_3P_Pickup_MIC")) + +//Neon Zweihander + Skins.Add((Id=9338, Weapondef=class'KFWeapDef_Zweihander', MIC_1P=("wep_skinset56_mat.neon_zweihander.Neon_Zweihander_1P_Mint_MIC"), MIC_3P="wep_skinset56_mat.neon_zweihander.Neon_Zweihander_3P_Mint_MIC", MIC_Pickup="wep_skinset56_mat.neon_zweihander.Neon_Zweihander_3P_Pickup_MIC")) + +//Neon RGB Boomstick + Skins.Add((Id=9339, Weapondef=class'KFWeapDef_DoubleBarrel', MIC_1P=("wep_skinset56_mat.neonrgb_doublebarrel.NeonRGB_DoubleBarrel_1P_Mint_MIC"), MIC_3P="wep_skinset56_mat.neonrgb_doublebarrel.NeonRGB_DoubleBarrel_3P_Mint_MIC", MIC_Pickup="wep_skinset56_mat.neonrgb_doublebarrel.NeonRGB_DoubleBarrel_3P_Pickup_MIC")) + +//Neon RGB HZ12 + Skins.Add((Id=9340, Weapondef=class'KFWeapDef_HZ12', MIC_1P=("wep_skinset56_mat.neonrgb_hz12.NeonRGB_HZ12_1P_Mint_MIC"), MIC_3P="wep_skinset56_mat.neonrgb_hz12.NeonRGB_HZ12_3P_Mint_MIC", MIC_Pickup="wep_skinset56_mat.neonrgb_hz12.NeonRGB_HZ12_3P_Pickup_MIC")) + +//Neon RGB HRG Beluga Beat + Skins.Add((Id=9341, Weapondef=class'KFWeapDef_HRG_SonicGun', MIC_1P=("wep_skinset56_mat.neonrgb_hrgsonicgun.NeonRGB_HRGSonicGun_1P_Mint_MIC"), MIC_3P="wep_skinset56_mat.neonrgb_hrgsonicgun.NeonRGB_HRGSonicGun_3P_Mint_MIC", MIC_Pickup="wep_skinset56_mat.neonrgb_hrgsonicgun.NeonRGB_HRGSonicGun_3P_Pickup_MIC")) + +//Neon RGB Zweihander + Skins.Add((Id=9342, Weapondef=class'KFWeapDef_Zweihander', MIC_1P=("wep_skinset56_mat.neonrgb_zweihander.NeonRGB_Zweihander_1P_Mint_MIC"), MIC_3P="wep_skinset56_mat.neonrgb_zweihander.NeonRGB_Zweihander_3P_Mint_MIC", MIC_Pickup="wep_skinset56_mat.neonrgb_zweihander.NeonRGB_Zweihander_3P_Pickup_MIC")) + +//Classic Mint 9MM + Skins.Add((Id=9343, Weapondef=class'KFWeapDef_9mm', MIC_1P=("wep_skinset57_mat.classic_9mm.Classic_9MM_1P_Mint_MIC"), MIC_3P="wep_skinset57_mat.classic_9mm.Classic_9MM_3P_Mint_MIC", MIC_Pickup="wep_skinset57_mat.classic_9mm.Classic_9MM_3P_Pickup_MIC")) + +//Classic Mint AA12 + Skins.Add((Id=9344, Weapondef=class'KFWeapDef_AA12', MIC_1P=("wep_skinset57_mat.classic_aa12.Classic_AA12_1P_Mint_MIC"), MIC_3P="wep_skinset57_mat.classic_aa12.Classic_AA12_3P_Mint_MIC", MIC_Pickup="wep_skinset57_mat.classic_aa12.Classic_AA12_3P_Pickup_MIC")) + +//Classic Mint Boomstick + Skins.Add((Id=9345, Weapondef=class'KFWeapDef_DoubleBarrel', MIC_1P=("wep_skinset57_mat.classic_doublebarrel.Classic_DoubleBarrel_1P_Mint_MIC"), MIC_3P="wep_skinset57_mat.classic_doublebarrel.Classic_DoubleBarrel_3P_Mint_MIC", MIC_Pickup="wep_skinset57_mat.classic_doublebarrel.Classic_DoubleBarrel_3P_Pickup_MIC")) + +//Classic Mint M1911 + Skins.Add((Id=9346, Weapondef=class'KFWeapDef_Colt1911', MIC_1P=("wep_skinset57_mat.classic_m1911.Classic_M1911_1P_Mint_MIC"), MIC_3P="wep_skinset57_mat.classic_m1911.Classic_M1911_3P_Mint_MIC", MIC_Pickup="wep_skinset57_mat.classic_m1911.Classic_M1911_3P_Pickup_MIC")) + +//Classic Precious 9MM + Skins.Add((Id=9347, Weapondef=class'KFWeapDef_9mm', MIC_1P=("wep_skinset57_mat.standard_9mm.Standard_9MM_1P_Mint_MIC"), MIC_3P="wep_skinset57_mat.standard_9mm.Standard_9MM_3P_Mint_MIC", MIC_Pickup="wep_skinset57_mat.standard_9mm.Standard_9MM_3P_Pickup_MIC")) + +//Classic Precious AA12 + Skins.Add((Id=9348, Weapondef=class'KFWeapDef_AA12', MIC_1P=("wep_skinset57_mat.standard_aa12.Standard_AA12_1P_Mint_MIC"), MIC_3P="wep_skinset57_mat.standard_aa12.Standard_AA12_3P_Mint_MIC", MIC_Pickup="wep_skinset57_mat.standard_aa12.Standard_AA12_3P_Pickup_MIC")) + +//Classic Precious Boomstick + Skins.Add((Id=9349, Weapondef=class'KFWeapDef_DoubleBarrel', MIC_1P=("wep_skinset57_mat.standard_doublebarrel.Standard_DoubleBarrel_1P_Mint_MIC"), MIC_3P="wep_skinset57_mat.standard_doublebarrel.Standard_DoubleBarrel_3P_Mint_MIC", MIC_Pickup="wep_skinset57_mat.standard_doublebarrel.Standard_DoubleBarrel_3P_Pickup_MIC")) + +//Classic Precious M1911 + Skins.Add((Id=9350, Weapondef=class'KFWeapDef_Colt1911', MIC_1P=("wep_skinset57_mat.standard_m1911.Standard_M1911_1P_Mint_MIC"), MIC_3P="wep_skinset57_mat.standard_m1911.Standard_M1911_3P_Mint_MIC", MIC_Pickup="wep_skinset57_mat.standard_m1911.Standard_M1911_3P_Pickup_MIC")) + +//Chameleon Dynamic Desert Eagle + 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")) + +//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")) + +//Chameleon Dynamic RPG-7 + Skins.Add((Id=9354, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("wep_skinset58_mat.chameleon_rpg7.Chameleon_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleon_rpg7.Chameleon_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleon_rpg7.Chameleon_RPG7_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB Desert Eagle + 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")) + +//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")) + +//Chameleon Dynamic RGB RPG-7 + Skins.Add((Id=9358, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("wep_skinset58_mat.chameleonrgb_rpg7.ChameleonRGB_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleonrgb_rpg7.ChameleonRGB_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleonrgb_rpg7.ChameleonRGB_RPG7_3P_Pickup_MIC")) +//Deep Sea Antique AA12 + Skins.Add((Id=9298, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet59_MAT.deepsea_aa12.DeepSea_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_aa12.DeepSea_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_aa12.DeepSea_AA12_3P_Pickup_MIC")) + +//Deep Sea Aqua AA12 + Skins.Add((Id=9299, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaAqua_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaAqua_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaAqua_AA12_3P_Pickup_MIC")) + +//Deep Sea Coral AA12 + Skins.Add((Id=9300, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaCoral_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaCoral_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaCoral_AA12_3P_Pickup_MIC")) + +//Deep Sea Pearl AA12 + Skins.Add((Id=9301, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaPearl_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaPearl_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaPearl_AA12_3P_Pickup_MIC")) + +//Deep Sea Black Seal AA12 + Skins.Add((Id=9302, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaBlackSeal_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaBlackSeal_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaBlackSeal_AA12_3P_Pickup_MIC")) + +//Deep Sea Precious AA12 + Skins.Add((Id=9303, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaPrecious_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaPrecious_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_aa12.DeepSeaPrecious_AA12_3P_Pickup_MIC")) + +//Deep Sea Antique FN FAL + Skins.Add((Id=9304, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_FNFAL', MIC_1P=("WEP_SkinSet59_MAT.deepsea_fnfal.DeepSea_FNFAL_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_fnfal.DeepSea_FNFAL_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSea_FNFAL_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSea_FNFAL_3P_Pickup_MIC")) + +//Deep Sea Aqua FN FAL + Skins.Add((Id=9305, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_FNFAL', MIC_1P=("WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaAqua_FNFAL_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaAqua_FNFAL_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaAqua_FNFAL_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaAqua_FNFAL_3P_Pickup_MIC")) + +//Deep Sea Coral FN FAL + Skins.Add((Id=9306, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_FNFAL', MIC_1P=("WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaCoral_FNFAL_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaCoral_FNFAL_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaCoral_FNFAL_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaCoral_FNFAL_3P_Pickup_MIC")) + +//Deep Sea Pearl FN FAL + Skins.Add((Id=9307, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_FNFAL', MIC_1P=("WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaPearl_FNFAL_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaPearl_FNFAL_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaPearl_FNFAL_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaPearl_FNFAL_3P_Pickup_MIC")) + +//Deep Sea Black Seal FN FAL + Skins.Add((Id=9308, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_FNFAL', MIC_1P=("WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaBlackSeal_FNFAL_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaBlackSeal_FNFAL_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaBlackSeal_FNFAL_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaBlackSeal_FNFAL_3P_Pickup_MIC")) + +//Deep Sea Precious FN FAL + Skins.Add((Id=9309, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_FNFAL', MIC_1P=("WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaPrecious_FNFAL_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaPrecious_FNFAL_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaPrecious_FNFAL_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_fnfal.DeepSeaPrecious_FNFAL_3P_Pickup_MIC")) + +//Deep Sea Antique M14EBR + Skins.Add((Id=9310, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSea_M14EBR_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSea_M14EBR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSea_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSea_M14EBR_3P_Pickup_MIC")) + +//Deep Sea Aqua M14EBR + Skins.Add((Id=9311, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaAqua_M14EBR_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaAqua_M14EBR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaAqua_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaAqua_M14EBR_3P_Pickup_MIC")) + +//Deep Sea Coral M14EBR + Skins.Add((Id=9312, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaCoral_M14EBR_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaCoral_M14EBR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaCoral_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaCoral_M14EBR_3P_Pickup_MIC")) + +//Deep Sea Pearl M14EBR + Skins.Add((Id=9313, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaPearl_M14EBR_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaPearl_M14EBR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaPearl_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaPearl_M14EBR_3P_Pickup_MIC")) + +//Deep Sea Black Seal M14EBR + Skins.Add((Id=9314, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaBlackSeal_M14EBR_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaBlackSeal_M14EBR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaBlackSeal_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaBlackSeal_M14EBR_3P_Pickup_MIC")) + +//Deep Sea Precious M14EBR + Skins.Add((Id=9315, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaPrecious_M14EBR_1P_Mint_MIC", "WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaPrecious_M14EBR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaPrecious_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_m14ebr.DeepSeaPrecious_M14EBR_3P_Pickup_MIC")) + +//Deep Sea Antique MAC 10 + Skins.Add((Id=9316, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mac10.DeepSea_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mac10.DeepSea_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mac10.DeepSea_MAC10_3P_Pickup_MIC")) + +//Deep Sea Aqua MAC 10 + Skins.Add((Id=9317, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaAqua_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaAqua_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaAqua_MAC10_3P_Pickup_MIC")) + +//Deep Sea Coral MAC 10 + Skins.Add((Id=9318, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaCoral_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaCoral_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaCoral_MAC10_3P_Pickup_MIC")) + +//Deep Sea Pearl MAC 10 + Skins.Add((Id=9319, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaPearl_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaPearl_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaPearl_MAC10_3P_Pickup_MIC")) + +//Deep Sea Black Seal MAC 10 + Skins.Add((Id=9320, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaBlackSeal_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaBlackSeal_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaBlackSeal_MAC10_3P_Pickup_MIC")) + +//Deep Sea Precious MAC 10 + Skins.Add((Id=9321, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Mac10', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaPrecious_MAC10_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaPrecious_MAC10_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mac10.DeepSeaPrecious_MAC10_3P_Pickup_MIC")) + +//Deep Sea Antique MP5RAS + Skins.Add((Id=9322, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSea_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSea_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSea_MP5RAS_3P_Pickup_MIC")) + +//Deep Sea Aqua MP5RAS + Skins.Add((Id=9323, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaAqua_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaAqua_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaAqua_MP5RAS_3P_Pickup_MIC")) + +//Deep Sea Coral MP5RAS + Skins.Add((Id=9324, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaCoral_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaCoral_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaCoral_MP5RAS_3P_Pickup_MIC")) + +//Deep Sea Pearl MP5RAS + Skins.Add((Id=9325, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaPearl_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaPearl_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaPearl_MP5RAS_3P_Pickup_MIC")) + +//Deep Sea Black Seal MP5RAS + Skins.Add((Id=9326, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaBlackSeal_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaBlackSeal_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaBlackSeal_MP5RAS_3P_Pickup_MIC")) + +//Deep Sea Precious MP5RAS + Skins.Add((Id=9327, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MP5RAS', MIC_1P=("WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaPrecious_MP5RAS_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaPrecious_MP5RAS_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_mp5ras.DeepSeaPrecious_MP5RAS_3P_Pickup_MIC")) + +//Deep Sea Antique RPG-7 + Skins.Add((Id=9328, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet59_MAT.deepsea_rpg7.DeepSea_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSea_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSea_RPG7_3P_Pickup_MIC")) + +//Deep Sea Aqua RPG-7 + Skins.Add((Id=9329, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaAqua_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaAqua_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaAqua_RPG7_3P_Pickup_MIC")) + +//Deep Sea Coral RPG-7 + Skins.Add((Id=9330, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaCoral_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaCoral_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaCoral_RPG7_3P_Pickup_MIC")) + +//Deep Sea Pearl RPG-7 + Skins.Add((Id=9331, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaPearl_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaPearl_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaPearl_RPG7_3P_Pickup_MIC")) + +//Deep Sea Black Seal RPG-7 + Skins.Add((Id=9332, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaBlackSeal_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaBlackSeal_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaBlackSeal_RPG7_3P_Pickup_MIC")) + +//Deep Sea Precious RPG-7 + Skins.Add((Id=9333, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaPrecious_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaPrecious_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaPrecious_RPG7_3P_Pickup_MIC")) } \ No newline at end of file diff --git a/KFGame/Classes/KFWeeklyOutbreakInformation.uc b/KFGame/Classes/KFWeeklyOutbreakInformation.uc index 0f0e359..a8427bc 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 = 16; + static const int NumWeeklyEvents = 17; } DefaultProperties { diff --git a/KFGame/KFOnlineStats.uci b/KFGame/KFOnlineStats.uci index de06714..fdf2ff3 100644 --- a/KFGame/KFOnlineStats.uci +++ b/KFGame/KFOnlineStats.uci @@ -156,4 +156,5 @@ const STATID_ACHIEVE_Dystopia2029Collectibles = 4058; const STATID_ACHIEVE_MoonbaseCollectibles = 4059; const STATID_ACHIEVE_NetherholdCollectibles = 4060; const STATID_ACHIEVE_CarillonHamletCollectibles = 4061; +const STATID_ACHIEVE_RigCollectibles = 4062; /** `endif */ diff --git a/KFGame/KFProfileSettings.uci b/KFGame/KFProfileSettings.uci index 66773f9..f7410ff 100644 --- a/KFGame/KFProfileSettings.uci +++ b/KFGame/KFProfileSettings.uci @@ -74,3 +74,5 @@ const KFID_GamepadDeadzoneScale = 175; const KFID_GamepadAccelerationJumpScale = 176; const KFID_HasTabbedToStore = 177; const KFID_AllowSwapTo9mm = 178; // Halloween 2021 QoL: added option to quick switch weapons to 9mm +const KFID_SurvivalStartingWeapIdx=179; // Summer 2022 QoL: added option to choose starting weapon for survival perk +const KFID_SurvivalStartingGrenIdx=180; // Summer 2022 QoL: added option to choose starting grenade for survival perk diff --git a/KFGameContent/Classes/KFDT_Ballistic_AutoTurret.uc b/KFGameContent/Classes/KFDT_Ballistic_AutoTurret.uc new file mode 100644 index 0000000..3e8a4fd --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_AutoTurret.uc @@ -0,0 +1,27 @@ +//============================================================================= +// KFDT_Ballistic_AutoTurret +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_AutoTurret extends KFDT_Ballistic_AssaultRifle + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=900 + KDeathUpKick=-300 + KDeathVel=100 + + StumblePower=5 + GunHitPower=0 + + WeaponDef=class'KFWeapDef_AutoTurret' + + //Perk + ModifierPerkList(0)=class'KFPerk_Commando' +} diff --git a/KFGameContent/Classes/KFDT_Blast_HRG_CranialPopper.uc b/KFGameContent/Classes/KFDT_Blast_HRG_CranialPopper.uc new file mode 100644 index 0000000..a829bcc --- /dev/null +++ b/KFGameContent/Classes/KFDT_Blast_HRG_CranialPopper.uc @@ -0,0 +1,67 @@ +//============================================================================= +// KFDT_Blast_HRG_CranialPopper +//============================================================================= +// Damage caused by the cranial popper alternate fire +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Blast_HRG_CranialPopper extends KFDT_Ballistic_Rifle + abstract + hidedropdown; + + +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ + switch ( InHitZoneName ) + { + case 'head': + return false; //true to dismember + } + + return false; +} + +/** +* Allows the damage type to customize exactly which hit zones it can dismember while the zed is alive +*/ +//static simulated function bool CanDismemberHitZoneWhileAlive(name InHitZoneName) +//{ +// switch ( InHitZoneName ) +// { +// case 'head': +// return true; +// } +// +// return false; +//} + + +DefaultProperties +{ + // This weapon uses radial impulses + RadialDamageImpulse=1500 + KDamageImpulse=0 + KDeathUpKick=500.0 + KDeathVel=300 + + // unreal physics momentum + bExtraMomentumZ=True + + KnockdownPower=0 + StunPower=250 + StumblePower=0 + GunHitPower=0 + MeleeHitPower=0 + EMPPower=0 + + //bCanObliterate=true + //ObliterationHealthThreshold=-75 + //ObliterationDamageThreshold=100 + bCanGib=true + GoreDamageGroup=DGT_Obliteration + + WeaponDef=class'KFWeapDef_HRG_CranialPopper' + ModifierPerkList(0)=class'KFPerk_Sharpshooter' +} diff --git a/KFGameContent/Classes/KFDT_Bludgeon_AutoTurret.uc b/KFGameContent/Classes/KFDT_Bludgeon_AutoTurret.uc new file mode 100644 index 0000000..611426c --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_AutoTurret.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_AutoTurret +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_AutoTurret extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_AutoTurret' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_CranialPopper.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_CranialPopper.uc new file mode 100644 index 0000000..47e1b7a --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_CranialPopper.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_CranialPopper +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_CranialPopper extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_CranialPopper' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_Crossboom.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Crossboom.uc new file mode 100644 index 0000000..3d0eff2 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Crossboom.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_Crossboom +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_Crossboom extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_Crossboom' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_ShrinkRay.uc b/KFGameContent/Classes/KFDT_Bludgeon_ShrinkRay.uc new file mode 100644 index 0000000..057a913 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_ShrinkRay.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_Flamethrower +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2015 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_ShrinkRay extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_ShrinkRayGun' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Explosive_AutoTurret.uc b/KFGameContent/Classes/KFDT_Explosive_AutoTurret.uc new file mode 100644 index 0000000..c2923b8 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_AutoTurret.uc @@ -0,0 +1,30 @@ +//============================================================================= +// KFDT_Explosive_AutoTurret +//============================================================================= +// Explosive damage type for AutoTurret explosion +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Explosive_AutoTurret extends KFDT_Explosive + abstract + hidedropdown; + +defaultproperties +{ + bShouldSpawnPersistentBlood = true + + // physics impact + RadialDamageImpulse = 2000 + GibImpulseScale = 0.15 + KDeathUpKick = 1000 + KDeathVel = 300 + + KnockdownPower = 100 + StumblePower = 300 + + + WeaponDef=class'KFWeapDef_AutoTurret' + ModifierPerkList(0)=class'KFPerk_Commando' +} diff --git a/KFGameContent/Classes/KFDT_Explosive_HRG_Crossboom.uc b/KFGameContent/Classes/KFDT_Explosive_HRG_Crossboom.uc new file mode 100644 index 0000000..4348dbe --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_HRG_Crossboom.uc @@ -0,0 +1,31 @@ +//============================================================================= +// KFDT_Explosive_HRG_Crossboom +//============================================================================= +// Explosive damage type for the HRG Crossboom +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Explosive_HRG_Crossboom extends KFDT_Explosive + abstract + hidedropdown; + +defaultproperties +{ + bShouldSpawnPersistentBlood = true + + // physics impact + RadialDamageImpulse = 2000 + GibImpulseScale = 0.15 + KDeathUpKick = 1000 + KDeathVel = 300 + + KnockdownPower = 150 + StumblePower = 400 + + //Perk + ModifierPerkList(0) = class'KFPerk_Demolitionist' + + WeaponDef = class'KFWeapDef_HRG_Crossboom' +} diff --git a/KFGameContent/Classes/KFDT_Explosive_HRG_CrossboomAlt.uc b/KFGameContent/Classes/KFDT_Explosive_HRG_CrossboomAlt.uc new file mode 100644 index 0000000..0a6ff1b --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_HRG_CrossboomAlt.uc @@ -0,0 +1,31 @@ +//============================================================================= +// KFDT_Explosive_HRG_CrossboomAlt +//============================================================================= +// Explosive damage type for the HRG Crossboom +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Explosive_HRG_CrossboomAlt extends KFDT_Explosive + abstract + hidedropdown; + +defaultproperties +{ + bShouldSpawnPersistentBlood = true + + // physics impact + RadialDamageImpulse = 2000 + GibImpulseScale = 0.15 + KDeathUpKick = 1000 + KDeathVel = 300 + + KnockdownPower = 100 + StumblePower = 200 + + //Perk + ModifierPerkList(0) = class'KFPerk_Demolitionist' + + WeaponDef = class'KFWeapDef_HRG_Crossboom' +} diff --git a/KFGameContent/Classes/KFDT_Piercing_HRG_CranialPopper.uc b/KFGameContent/Classes/KFDT_Piercing_HRG_CranialPopper.uc new file mode 100644 index 0000000..64066d1 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Piercing_HRG_CranialPopper.uc @@ -0,0 +1,116 @@ +//============================================================================= +// KFDT_Piercing_HRG_CranialPopper +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Piercing_HRG_CranialPopper extends KFDT_Piercing + abstract + hidedropdown; + +//Visual class to attach to the victim when impact occurs +var class TubeAttachClass; + +var class PoisonDamageType; + +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ +// if( super.CanDismemberHitZone( InHitZoneName ) ) +// { +// return true; +// } + + switch ( InHitZoneName ) + { + case 'head': + return true; + } + + return false; +} + +/** +* Allows the damage type to customize exactly which hit zones it can dismember while the zed is alive +*/ +static simulated function bool CanDismemberHitZoneWhileAlive(name InHitZoneName) +{ + switch ( InHitZoneName ) + { + case 'head': + return true; + } + + return false; +} + +static function PlayImpactHitEffects(KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, optional Pawn HitInstigator) +{ + local Actor TubeAttachment; + local Vector StickLocation; + local Rotator StickRotation; + local name BoneName; + local WorldInfo WI; + local KFPawn RetracePawn; + local Vector RetraceLocation; + local Vector RetraceNormal; + local TraceHitInfo HitInfo; + + WI = class'WorldInfo'.static.GetWorldInfo(); + if (P != none && HitZoneIndex > 0 && HitZoneIndex < P.HitZones.Length && WI != none && WI.NetMode != NM_DedicatedServer) + { + //Don't play additional FX here if we aren't attaching a new tube, let its built in blood spray handle things + //super.PlayImpactHitEffects(P, HitLocation, HitDirection, HitZoneIndex, HitInstigator); + + //Retrace to get valid hit normal + foreach WI.TraceActors(class'KFPawn', RetracePawn, RetraceLocation, RetraceNormal, HitLocation + HitDirection * 50, HitLocation - HitDirection * 50, vect(0, 0, 0), HitInfo, 1) //TRACEFLAG_Bullet + { + if (P == RetracePawn) + { + HitLocation = RetraceLocation; + HitDirection = -RetraceNormal; + break; + } + } + + TubeAttachment = P.Spawn(default.TubeAttachClass, P, , HitLocation, Rotator(HitDirection)); + if (TubeAttachment != none) + { + BoneName = P.HitZones[HitZoneIndex].BoneName; + P.Mesh.TransformToBoneSpace(BoneName, TubeAttachment.Location, TubeAttachment.Rotation, StickLocation, StickRotation); + TubeAttachment.SetBase(P, , P.Mesh, BoneName); + TubeAttachment.SetRelativeLocation(StickLocation); + TubeAttachment.SetRelativeRotation(StickRotation); + } + } +} + +/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ +static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) +{ + // potential for two DoTs if DoT_Type is set + if (default.PoisonDamageType.default.DoT_Type != DOT_None) + { + Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, default.PoisonDamageType); + } +} + +defaultproperties +{ + KDamageImpulse=3000 + KDeathUpKick=800 + KDeathVel=500 + + StumblePower=30 + GunHitPower=0 + + BigHeadPower=100 + + PoisonDamageType=class'KFDT_Poison_HRG_CranialPopper' + + WeaponDef=class'KFWeapDef_HRG_CranialPopper' + ModifierPerkList(0)=class'KFPerk_Sharpshooter' + + TubeAttachClass=class'KFWeapActor_CranialPopper_Tube' +} diff --git a/KFGameContent/Classes/KFDT_Piercing_HRG_Crossboom.uc b/KFGameContent/Classes/KFDT_Piercing_HRG_Crossboom.uc new file mode 100644 index 0000000..cf7ea9a --- /dev/null +++ b/KFGameContent/Classes/KFDT_Piercing_HRG_Crossboom.uc @@ -0,0 +1,35 @@ +//============================================================================= +// KFDT_Piercing_HRG_Crossboom +//============================================================================= +// Damage type for HRG_Crossboom +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Piercing_HRG_Crossboom extends KFDT_Piercing + abstract + hidedropdown; + +static simulated function bool CanDismemberHitZone(name InHitZoneName) +{ + return false; +} + +static simulated function bool CanDismemberHitZoneWhileAlive(name InHitZoneName) +{ + return false; +} + +defaultproperties +{ + KDamageImpulse=0 + KDeathUpKick=0 + KDeathVel=0 + + StumblePower=250 + GunHitPower=100 + + ModifierPerkList(0)=class'KFPerk_Demolitionist' + WeaponDef=class'KFWeapDef_HRG_Crossboom' +} diff --git a/KFGameContent/Classes/KFDT_Poison_HRG_CranialPopper.uc b/KFGameContent/Classes/KFDT_Poison_HRG_CranialPopper.uc new file mode 100644 index 0000000..f6f0237 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Poison_HRG_CranialPopper.uc @@ -0,0 +1,29 @@ +//============================================================================= +// KFDT_Poison_HRG_CranialPopper +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Poison_HRG_CranialPopper extends KFDT_Toxic + abstract + hidedropdown; + +defaultproperties +{ + //physics + KDamageImpulse=0 + KDeathUpKick=0 + KDeathVel=0 + + //Damage Over Time Components + DoT_Type=DOT_None //DOT_Toxic + DoT_Duration=5.0 + DoT_Interval=1.0 + DoT_DamageScale=0.1 + bStackDoT=true + + WeaponDef=class'KFWeapDef_HRG_CranialPopper' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Shrink_ShrinkRayGun.uc b/KFGameContent/Classes/KFDT_Shrink_ShrinkRayGun.uc new file mode 100644 index 0000000..e84104e --- /dev/null +++ b/KFGameContent/Classes/KFDT_Shrink_ShrinkRayGun.uc @@ -0,0 +1,29 @@ +//============================================================================= +// KFDT_Shrink_ShrinkRayGun +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Shrink_ShrinkRayGun extends KFDT_EMP + abstract; + +defaultproperties +{ + ShrinkPower=100 // Apply always + MeleeHitPower=20 + EMPPower=0 + KDeathVel=300 + + DoT_Type=DOT_Fire + DoT_Duration=INDEX_NONE //5.0//1.0 + DoT_Interval=INDEX_NONE//0.5 + DoT_DamageScale=0.0//0.1 + bIgnoreSelfInflictedScale=false + + bCanObliterate=false + bCanGib=false + + WeaponDef=class'KFWeapDef_ShrinkRayGun' + ModifierPerkList(0)=class'KFPerk_Survivalist' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFGameInfo_Survival.uc b/KFGameContent/Classes/KFGameInfo_Survival.uc index 4f5947a..92f09cd 100644 --- a/KFGameContent/Classes/KFGameInfo_Survival.uc +++ b/KFGameContent/Classes/KFGameInfo_Survival.uc @@ -41,9 +41,13 @@ var byte WaveMax; // The "end" wave var int WaveNum; // The wave we are currently in var bool bHumanDeathsLastWave; //Track this separate from player count in case someone dies and leaves var int ObjectiveSpawnDelay; // How long should the first wave be delayed if there is an active objective. + // The boss waves spams the WaveEnd functions, adding this to prevent it (was affecting seasonal events). var protected transient bool bWaveStarted; +// When this is true next wave will be last +var protected bool bGunGamePlayerOnLastGun; + /** Whether this game mode should play music from the get-go (lobby) */ static function bool ShouldPlayMusicAtStart() { @@ -69,6 +73,8 @@ event PostBeginPlay() super.PostBeginPlay(); TimeBetweenWaves = GetTraderTime(); + + bGunGamePlayerOnLastGun = false; } /** Set up the spawning */ @@ -254,6 +260,16 @@ function RestartPlayer(Controller NewPlayer) } } +function ResetGunGame(KFPlayerController_WeeklySurvival KFPC_WS) +{ + super.ResetGunGame(KFPC_WS); +} + +function RestartGunGamePlayerWeapon(KFPlayerController_WeeklySurvival KFPC_WS, byte WaveToUse) +{ + super.RestartGunGamePlayerWeapon(KFPC_WS, WaveToUse); +} + function Killed(Controller Killer, Controller KilledPlayer, Pawn KilledPawn, class damageType) { local Sequence GameSeq; @@ -1186,7 +1202,31 @@ function WaveEnded(EWaveEndCondition WinCondition) } } - if( WaveNum < WaveMax ) + if (OutbreakEvent != none && OutbreakEvent.ActiveEvent.bGunGameMode) + { + MyKFGRI.GunGameWavesCurrent += 1; + + // If we unlocked last weapon we only finish if we completed the boss wave + // If we didn't unlock to last weapon and we just finished last wave (before BOSS), repeat + if (bGunGamePlayerOnLastGun) + { + MyKFGRI.bWaveGunGameIsFinal = true; + + if (WaveNum < WaveMax) + { + WaveNum = WaveMax - 1; + } + } + else if (WaveNum >= WaveMax - 1) + { + // Repeat wave before BOSS till forever + WaveNum = WaveMax - 2; + } + + MyKFGRI.bNetDirty = true; + } + + if (WaveNum < WaveMax) { GotoState( 'TraderOpen', 'Begin' ); } @@ -1841,6 +1881,7 @@ DefaultProperties bCanPerkAlwaysChange=false MaxGameDifficulty=3 bWaveStarted=false + bGunGamePlayerOnLastGun=false ObjectiveSpawnDelay=5 diff --git a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc index e03515f..2639284 100644 --- a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc +++ b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc @@ -76,6 +76,16 @@ event PreBeginPlay() } } +event PostBeginPlay() +{ + super.PostBeginPlay(); + + if (OutbreakEvent.ActiveEvent.TimeBetweenWaves >= 0.f) + { + TimeBetweenWaves = OutbreakEvent.ActiveEvent.TimeBetweenWaves; + } +} + function CreateOutbreakEvent() { //The KFGameEngine at startup will store the week index of our current time @@ -91,7 +101,7 @@ function CreateOutbreakEvent() { ActiveEventIdx = KGE.GetWeeklyEventIndex() % OutbreakEvent.SetEvents.Length; } - OutbreakEvent.SetActiveEvent(ActiveEventIdx); + ActiveEventIdx = OutbreakEvent.SetActiveEvent(ActiveEventIdx); } function bool UsesModifiedDifficulty() @@ -130,6 +140,22 @@ function SetPickupItemList() local STraderItem TraderItem; local KFPickupFactory_Item ItemFactory; local int Idx; + + if (OutbreakEvent.ActiveEvent.bOnlyArmorItemPickup) + { + foreach AllActors(class'KFPickupFactory_Item', ItemFactory) + { + for (Idx = ItemFactory.ItemPickups.Length - 1; Idx >= 0; --Idx) + { + if (ItemFactory.ItemPickups[Idx].ItemClass.Name != 'KFInventory_Armor') + { + ItemFactory.ItemPickups.Remove(Idx, 1); + } + } + } + + return; + } //If we have an override weapon list, it's not enough to block trader and default inventory. // Iterate through the item pickups in the map to trim their lists as well. @@ -266,6 +292,11 @@ protected function ScoreMonsterKill( Controller Killer, Controller Monster, KFPa } } } + + if (OutbreakEvent.ActiveEvent.bGunGameMode) + { + GunGameScoreAssistanceAfterKilling(MonsterPawn, Killer); + } } @@ -344,6 +375,44 @@ function HealAfterKilling(KFPawn_Monster MonsterPawn , Controller Killer, option } } +function GunGameScoreAssistanceAfterKilling(KFPawn_Monster MonsterPawn , Controller Killer) +{ + local int i; + local KFPlayerController_WeeklySurvival KFPC_WS; + local array DamageHistory; + local KFPlayerReplicationInfo DamagerKFPRI; + local array Attackers; + + DamageHistory = MonsterPawn.DamageHistory; + + for (i = 0; i < DamageHistory.Length; i++) + { + if (DamageHistory[i].DamagerController != none + && DamageHistory[i].DamagerController.bIsPlayer + && DamageHistory[i].DamagerPRI.GetTeamNum() == 0 + && DamageHistory[i].DamagerPRI != none) + { + DamagerKFPRI = KFPlayerReplicationInfo(DamageHistory[i].DamagerPRI); + if (DamagerKFPRI != none) + { + KFPC_WS = KFPlayerController_WeeklySurvival(DamagerKFPRI.Owner); + if (KFPC_WS != none && KFPC_WS != Killer) + { + if (Attackers.Find(KFPC_WS) < 0) + { + Attackers.AddItem(KFPC_WS); + + if (KFPC_WS.Pawn.Health > 0) + { + KFPC_WS.GunGameData.Score += MonsterPawn.GunGameAssistanceScore; + UpdateGunGameLevel(KFPC_WS); + } + } + } + } + } + } +} function StartMatch() { @@ -374,27 +443,29 @@ function CreateDifficultyInfo(string Options) event PostLogin( PlayerController NewPlayer ) { - local KFPlayerController_WeeklySurvival KFPC; + local KFPlayerController_WeeklySurvival KFPC_WS; local KFPawn_Customization KFCustomizePawn; super.PostLogin(NewPlayer); - KFPC = KFPlayerController_WeeklySurvival(NewPlayer); - if (KFPC != none) + KFPC_WS = KFPlayerController_WeeklySurvival(NewPlayer); + if (KFPC_WS != none) { - KFPC.bUsingPermanentZedTime = OutbreakEvent.ActiveEvent.bPermanentZedTime; - KFPC.ZedTimeRadius = OutbreakEvent.ActiveEvent.ZedTimeRadius * OutbreakEvent.ActiveEvent.ZedTimeRadius; - KFPC.ZedTimeBossRadius = OutbreakEvent.ActiveEvent.ZedTimeBossRadius * OutbreakEvent.ActiveEvent.ZedTimeBossRadius; - KFPC.ZedTimeHeight = OutbreakEvent.ActiveEvent.ZedTimeHeight; - KFPC.ZedRecheckTime = OutbreakEvent.ActiveEvent.PermanentZedResetTime; + KFPC_WS.bUsingPermanentZedTime = OutbreakEvent.ActiveEvent.bPermanentZedTime; + KFPC_WS.ZedTimeRadius = OutbreakEvent.ActiveEvent.ZedTimeRadius * OutbreakEvent.ActiveEvent.ZedTimeRadius; + KFPC_WS.ZedTimeBossRadius = OutbreakEvent.ActiveEvent.ZedTimeBossRadius * OutbreakEvent.ActiveEvent.ZedTimeBossRadius; + KFPC_WS.ZedTimeHeight = OutbreakEvent.ActiveEvent.ZedTimeHeight; + KFPC_WS.ZedRecheckTime = OutbreakEvent.ActiveEvent.PermanentZedResetTime; //Handle any visual-related things for customization pawn so the pregame lobby has the fun things - KFCustomizePawn = KFPawn_Customization(KFPC.Pawn); + KFCustomizePawn = KFPawn_Customization(KFPC_WS.Pawn); if (KFCustomizePawn != none) { KFCustomizePawn.IntendedHeadScale = OutbreakEvent.ActiveEvent.PlayerSpawnHeadScale; KFCustomizePawn.SetHeadScale(KFCustomizePawn.IntendedHeadScale, KFCustomizePawn.CurrentHeadScale); } } + + LoadGunGameWeapons(NewPlayer); } function SetBossIndex() @@ -830,7 +901,7 @@ function bool AllowPrimaryWeapon(string ClassPath) return true; } } - return true; + return false; } return true; } @@ -884,6 +955,46 @@ function bool IsPerkAllowed(class PerkClass) return false; } +function LoadGunGameWeapons(Controller NewPlayer) +{ + local int i, RandomNumber; + local KFPlayerController_WeeklySurvival KFPC_WS; + local class InventoryClass; + local Inventory Inv; + local KFWeapon Weapon; + + // Deactivated preload in console version + + if (OutbreakEvent.ActiveEvent.bGunGameMode && WorldInfo.IsConsoleBuild() == false) + { + KFPC_WS = KFPlayerController_WeeklySurvival(NewPlayer); + + if (KFPC_WS == none) + { + return; + } + + for (i=0; i < OutbreakEvent.ActiveEvent.GunGamePerksData.GunGameLevels.Length; i++) + { + RandomNumber = Rand(OutbreakEvent.ActiveEvent.GunGamePerksData.GunGameLevels[i].GrantedWeapons.Length); + + KFPC_WS.GunGameData.GunGamePreselectedWeapons.AddItem(RandomNumber); + + InventoryClass = class (DynamicLoadObject(OutbreakEvent.ActiveEvent.GunGamePerksData.GunGameLevels[i].GrantedWeapons[RandomNumber].default.WeaponClassPath, class'Class')); + Inv = KFPC_WS.Pawn.InvManager.CreateInventory(InventoryClass, true); + + if (Inv != none) + { + Weapon = KFWeapon(Inv); + if (Weapon != none) + { + Weapon.GunGameRemove(); + } + } + } + } +} + function RestartPlayer(Controller NewPlayer) { local KFPawn_Human KFPH; @@ -891,9 +1002,41 @@ function RestartPlayer(Controller NewPlayer) super.RestartPlayer(NewPlayer); KFPH = KFPawn_Human(NewPlayer.Pawn); + OutbreakEvent.AdjustRestartedPlayer(KFPH); } +function RestartGunGamePlayerWeapon(KFPlayerController_WeeklySurvival KFPC_WS, byte WaveToUse) +{ + local byte i; + local int CurrentGunGameWaveLevel; + + super.RestartGunGamePlayerWeapon(KFPC_WS, WaveToUse); + + ResetGunGame(KFPC_WS); + + CurrentGunGameWaveLevel = -1; + + // Find wave level, the data needs to be ordered + + for (i = 0; i < OutbreakEvent.ActiveEvent.GunGamePerksData.GunGameRespawnLevels.Length; i++) + { + if (WaveToUse >= OutbreakEvent.ActiveEvent.GunGamePerksData.GunGameRespawnLevels[i].Wave) + { + CurrentGunGameWaveLevel = OutbreakEvent.ActiveEvent.GunGamePerksData.GunGameRespawnLevels[i].Level - 1; + } + } + + // If any level we force gun game update + + if (CurrentGunGameWaveLevel >= 0) + { + KFPC_WS.GunGameData.Score = OutbreakEvent.ActiveEvent.GunGamePerksData.GunGameLevels[CurrentGunGameWaveLevel].RequiredScore; + + UpdateGunGameLevel(KFPC_WS); + } +} + function DoDeathExplosion(Pawn DeadPawn, KFGameExplosion ExplosionTemplate, class ExplosionIgnoreClass) { local KFExplosionActorReplicated ExploActor; @@ -950,6 +1093,191 @@ simulated function ModifyDamageGiven(out int InDamage, optional Actor DamageCaus } } +/* + * Gun Game + */ + +function ResetGunGame(KFPlayerController_WeeklySurvival KFPC_WS) +{ + KFPC_WS.GunGameData.Score = 0; + KFPC_WS.GunGameData.Level = 0; + + KFPC_WS.UpdateGunGameWidget(0, OutbreakEvent.ActiveEvent.GunGamePerksData.GunGameLevels[0].RequiredScore, 0, OutbreakEvent.ActiveEvent.GunGamePerksData.GunGameLevels.Length); +} + +function NotifyKilled(Controller Killer, Controller Killed, Pawn KilledPawn, class damageType ) +{ + local KFPawn_Monster KFPM; + local KFPlayerController_WeeklySurvival KFPC_WS; + + super.NotifyKilled(Killer, Killed, KilledPawn, damageType); + + if (!OutbreakEvent.ActiveEvent.bGunGameMode) + { + return; + } + + // If pawn is monster increase gun game score for that monster + + KFPM = KFPawn_Monster(KilledPawn); + KFPC_WS = KFPlayerController_WeeklySurvival(Killer); + + if (KFPM != none && KFPC_WS != none) + { + if (KFPC_WS.Pawn.Health > 0) + { + KFPC_WS.GunGameData.Score += KFPM.GunGameKilledScore; + UpdateGunGameLevel(KFPC_WS); + } + } + else + { + // If pawn is human reset game score (we can just check Killed exists as Controller) + + KFPC_WS = KFPlayerController_WeeklySurvival(Killed); + + if (KFPC_WS != none) + { + ResetGunGame(KFPC_WS); + } + } +} + +function GunGameLevelGrantWeapon(KFPlayerController_WeeklySurvival KFPC_WS, class ToGrantWeaponDefinition) +{ + local class InventoryClass; + local Inventory Inv; + local KFWeapon KFW; + + InventoryClass = class (DynamicLoadObject(ToGrantWeaponDefinition.default.WeaponClassPath, class'Class')); + Inv = KFPC_WS.Pawn.InvManager.CreateInventory(InventoryClass, true); + + if (Inv != none) + { + KFW = KFWeapon(Inv); + if (KFW != none) + { + KFW.bDropOnDeath = false; + KFW.bGivenAtStart = true; + KFW = KFInventoryManager(KFPC_WS.Pawn.InvManager).CombineWeaponsOnPickup( KFW ); + KFW.NotifyPickedUp(); + + // Refill ammo + KFW.AmmoCount[0] = KFW.MagazineCapacity[0]; + KFW.AddAmmo(KFW.GetMaxAmmoAmount(0)); + KFW.AmmoCount[1] = KFW.MagazineCapacity[1]; + KFW.AddSecondaryAmmo(KFW.GetMaxAmmoAmount(1)); + + KFPC_WS.Pawn.InvManager.SetCurrentWeapon(KFW); + } + } +} + +function UpdateGunGameLevel(KFPlayerController_WeeklySurvival KFPC_WS) +{ + local byte CurrentLevel, InitialLevel, RandomNumber; + local class ToGrantWeaponDefinition; + local GunGamePerkData PerkData; + local KFWeapon CurrentWeapon; + local bool found_base_weapon; + + if (!OutbreakEvent.ActiveEvent.bGunGameMode) + return; + + PerkData = OutbreakEvent.ActiveEvent.GunGamePerksData; + + InitialLevel = KFPC_WS.GunGameData.Level; + CurrentLevel = KFPC_WS.GunGameData.Level; + + // Update to the current level + while (CurrentLevel < PerkData.GunGameLevels.Length && KFPC_WS.GunGameData.Score >= PerkData.GunGameLevels[CurrentLevel].RequiredScore) + { + ++CurrentLevel; + } + + // Update HUD + + if (CurrentLevel > PerkData.GunGameLevels.Length - 1) + { + KFPC_WS.UpdateGunGameWidget(KFPC_WS.GunGameData.Score, -1, PerkData.GunGameLevels.Length, PerkData.GunGameLevels.Length); + } + else + { + KFPC_WS.UpdateGunGameWidget(KFPC_WS.GunGameData.Score, PerkData.GunGameLevels[CurrentLevel].RequiredScore, CurrentLevel, PerkData.GunGameLevels.Length); + } + + if (InitialLevel != CurrentLevel) + { + // If this player reached last level.. + if (CurrentLevel > PerkData.GunGameLevels.Length - 1) + { + if (bGunGamePlayerOnLastGun == false) + { + KFPC_WS.GunGameData.GiveWeaponMaster = true; + } + + bGunGamePlayerOnLastGun = true; + + KFPC_WS.PlayGunGameMessage(true); + } + else + { + KFPC_WS.PlayGunGameMessage(false); + } + + KFPC_WS.GunGameData.Level = CurrentLevel; + + found_base_weapon = false; + + // Remove Previous Granted Items + foreach KFPC_WS.Pawn.InvManager.InventoryActors ( class'KFWeapon', CurrentWeapon ) + { + // (not if it's knife/9mm/syringe) + if (!class'KFPerk'.static.IsKnife(CurrentWeapon) + && !class'KFPerk_SWAT'.static.Is9mm(CurrentWeapon) + && !class'KFPerk'.static.IsSyringe(CurrentWeapon) + && !class'KFPerk'.static.IsWelder(CurrentWeapon)) + { + // To prevent audio/vfx lock, while firing when removing the equipped weapon we do a proper gun remove + // This new function manages it's state internally + CurrentWeapon.GunGameRemove(); + } + + if (class'KFPerk_SWAT'.static.Is9mm(CurrentWeapon)) + { + found_base_weapon = true; + } + } + + // We need to grant 9MM is we don't have it and we jumped levels + + if (CurrentLevel > 1 && found_base_weapon == false) + { + ToGrantWeaponDefinition = PerkData.GunGameLevels[0].GrantedWeapons[0]; + + GunGameLevelGrantWeapon(KFPC_WS, ToGrantWeaponDefinition); + } + + // Grant Weapon + + // Generate random weapon to grant from the list + + // Deactivated preload in console version + if (WorldInfo.IsConsoleBuild()) + { + RandomNumber = Rand(PerkData.GunGameLevels[CurrentLevel-1].GrantedWeapons.Length); + } + else + { + RandomNumber = KFPC_WS.GunGameData.GunGamePreselectedWeapons[CurrentLevel-1]; + } + + ToGrantWeaponDefinition = PerkData.GunGameLevels[CurrentLevel-1].GrantedWeapons[RandomNumber]; + + GunGameLevelGrantWeapon(KFPC_WS, ToGrantWeaponDefinition); + } +} + defaultproperties { //Overrides diff --git a/KFGameContent/Classes/KFInventory_Money.uc b/KFGameContent/Classes/KFInventory_Money.uc index 000d056..478d20b 100644 --- a/KFGameContent/Classes/KFInventory_Money.uc +++ b/KFGameContent/Classes/KFInventory_Money.uc @@ -25,9 +25,12 @@ function DropFrom(vector StartLocation, vector StartVelocity) local KFDroppedPickup_Cash KFDP; local PlayerReplicationInfo PRI; local int Amount; + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); // if cannot spawn a pickup, then destroy and quit - if( DroppedPickupClass == None || DroppedPickupMesh == None ) + if( DroppedPickupClass == None || DroppedPickupMesh == None || (KFGRI != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 16)) { return; } diff --git a/KFGameContent/Classes/KFMapObjective_AreaDefense.uc b/KFGameContent/Classes/KFMapObjective_AreaDefense.uc index 275c983..bdecf21 100644 --- a/KFGameContent/Classes/KFMapObjective_AreaDefense.uc +++ b/KFGameContent/Classes/KFMapObjective_AreaDefense.uc @@ -194,6 +194,29 @@ simulated function bool IsActive() return bActive; } +simulated function bool CanActivateObjectiveByWeekly() +{ + if (Role == Role_Authority) + { + if (KFGameInfo(WorldInfo.Game).OutbreakEvent != none + && KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bGunGameMode) + { + return false; + } + } + else + { + if (KFGameReplicationInfo(WorldInfo.GRI) != none + && KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode + && KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 16) + { + return false; + } + } + + return true; +} + function bool CanActivateObjective() { return !IsCurrentGameModeBlacklisted(); diff --git a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc index cff12c2..bd00b0d 100644 --- a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc +++ b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc @@ -908,6 +908,288 @@ defaultproperties )} )} + // Gun Run + SetEvents[16]={( + EventDifficulty=2, + GameLength=GL_Normal, + bGunGameMode=true, + PerksAvailableList=(class'KFPerk_Survivalist'), + bDisableTraders=true, + bForceShowSkipTrader=true, + SpawnWeaponList=KFGFxObject_TraderItems'GP_Trader_ARCH.GunGameWeeklySpawnList', + bSpawnWeaponListAffectsSecondaryWeapons=true, + OverrideItemPickupModifier= 0.5f, //1.0f, //2.0f, // 0.f, + OverrideAmmoPickupModifier= 1.0f, //2.0f, //3.0f, // 0.01f, + bOnlyArmorItemPickup=true, + TraderTimeModifier=1.0f, // 0.1f, + TimeBetweenWaves=30.f, + bDisableAddDosh=true, + bDisableThrowWeapon=true, + ZedsToAdjust={( + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_Cyst',GunGameKilledScore=10, GunGameAssistanceScore=2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_Alpha',GunGameKilledScore=10, GunGameAssistanceScore=2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_AlphaKing',GunGameKilledScore=30, GunGameAssistanceScore=5), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedClot_Slasher',GunGameKilledScore=10, GunGameAssistanceScore=2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedSiren',GunGameKilledScore=30, GunGameAssistanceScore=5), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedStalker',GunGameKilledScore=10, GunGameAssistanceScore=2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedCrawler',GunGameKilledScore=10, GunGameAssistanceScore=2), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedCrawlerKing',GunGameKilledScore=20, GunGameAssistanceScore=4), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedGorefast',GunGameKilledScore=20, GunGameAssistanceScore=4), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedGorefastDualBlade',GunGameKilledScore=30, GunGameAssistanceScore=4), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedBloat',GunGameKilledScore=30, GunGameAssistanceScore=5), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedHusk',GunGameKilledScore=30, GunGameAssistanceScore=5), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedDAR_EMP',GunGameKilledScore=20, GunGameAssistanceScore=4), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedDAR_Laser',GunGameKilledScore=20, GunGameAssistanceScore=4), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedDAR_Rocket',GunGameKilledScore=20, GunGameAssistanceScore=4), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedScrake',GunGameKilledScore=50, GunGameAssistanceScore=10), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedFleshpound',GunGameKilledScore=50, GunGameAssistanceScore=10), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedFleshpoundMini',GunGameKilledScore=40, GunGameAssistanceScore=8), + (ClassToAdjust=class'KFGameContent.KFPawn_ZedBloatKingSubspawn',GunGameKilledScore=0, GunGameAssistanceScore=0) + )}, + // + GunGamePerksData={(GunGameRespawnLevels={( + (Wave=1, Level=3), (Wave=2, Level=7), + (Wave=3, Level=10), (Wave=4, Level=12), + (Wave=5, Level=14), (Wave=6, Level=16), + (Wave=7, Level=18), (Wave=8, Level=19), + (Wave=9, Level=20) // We have to take in account that players spawn during trade time, and the wave changes after that + )}, + GunGameLevels={( + ( RequiredScore=50 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_9mm' + ) + } + ), + ( RequiredScore=100 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_9mmDual' + , class'KFGame.KFWeapDef_MedicPistol' + , class'KFGame.KFWeapDef_Crovel' + ) + } + ), + ( RequiredScore=200 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_HX25' + , class'KFGame.KFWeapDef_FlareGun' + , class'KFGame.KFWeapDef_HRGWinterbite' + ) + } + ), + ( RequiredScore=300 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_AR15' + , class'KFGame.KFWeapDef_CaulkBurn' + , class'KFGame.KFWeapDef_MP7' + ) + } + ), + ( RequiredScore=400 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_Remington1858Dual' + , class'KFGame.KFWeapDef_Winchester1894' + , class'KFGame.KFWeapDef_MB500' + ) + } + ), + ( RequiredScore=500 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_FireAxe' + , class'KFGame.KFWeapDef_MedicSMG' + , class'KFGame.KFWeapDef_SW500_HRG' + ) + } + ), + ( RequiredScore=600 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_M79' + , class'KFGame.KFWeapDef_Crossbow' + , class'KFGame.KFWeapDef_Colt1911Dual' + ) + } + ), + ( RequiredScore=750 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_MP5RAS' + , class'KFGame.KFWeapDef_Thompson' + , class'KFGame.KFWeapDef_HRG_Boomy' + , class'KFGame.KFWeapDef_Bullpup' + ) + } + ), + ( RequiredScore=900 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_FlareGunDual' + , class'KFGame.KFWeapDef_HRGWinterbiteDual' + , class'KFGame.KFWeapDef_Katana' + , class'KFGame.KFWeapDef_CenterfireMB464' + ) + } + ), + ( RequiredScore=1050 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_Nailgun' + , class'KFGame.KFWeapDef_DoubleBarrel' + , class'KFGame.KFWeapDef_HZ12' + , class'KFGame.KFWeapDef_DragonsBreath' + ) + } + ), + ( RequiredScore=1200 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_Healthrower_HRG' + , class'KFGame.KFWeapDef_Mac10' + , class'KFGame.KFWeapDef_HRG_Crossboom' + , class'KFGame.KFWeapDef_HRGScorcher' + ) + } + ), + ( RequiredScore=1350 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_SealSqueal' + , class'KFGame.KFWeapDef_HRG_CranialPopper' + , class'KFGame.KFWeapDef_HRG_SonicGun' + , class'KFGame.KFWeapDef_Hemogoblin' + ) + } + ), + ( RequiredScore=1500 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_AK12' + , class'KFGame.KFWeapDef_MKB42' + , class'KFGame.KFWeapDef_FlameThrower' + , class'KFGame.KFWeapDef_FreezeThrower' + ) + } + ), + ( RequiredScore=1650 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_Pulverizer' + , class'KFGame.KFWeapDef_MedicBat' + , class'KFGame.KFWeapDef_Nailgun_HRG' + , class'KFGame.KFWeapDef_P90' + ) + } + ), + ( RequiredScore=1800 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_DeagleDual' + , class'KFGame.KFWeapDef_HRG_Energy' + , class'KFGame.KFWeapDef_AF2011Dual' + , class'KFGame.KFWeapDef_HRG_Vampire' + ) + } + ), + ( RequiredScore=2000 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_HRG_Kaboomstick' + , class'KFGame.KFWeapDef_MedicShotgun' + , class'KFGame.KFWeapDef_M4' + , class'KFGame.KFWeapDef_SW500Dual_HRG' + ) + } + ), + ( RequiredScore=2200 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_HK_UMP' + , class'KFGame.KFWeapDef_HRGIncendiaryRifle' + , class'KFGame.KFWeapDef_M16M203' + , class'KFGame.KFWeapDef_M14EBR' + ) + } + ), + ( RequiredScore=2400 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_PowerGloves' + , class'KFGame.KFWeapDef_HRG_BlastBrawlers' + , class'KFGame.KFWeapDef_HRGTeslauncher' + , class'KFGame.KFWeapDef_MaceAndShield' + ) + } + ), + ( RequiredScore=2600 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_MedicRifle' + , class'KFGame.KFWeapDef_HRG_EMP_ArcGenerator' + , class'KFGame.KFWeapDef_HRG_Stunner' + , class'KFGame.KFWeapDef_SCAR' + ) + } + ), + ( RequiredScore=2800 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_HuskCannon' + , class'KFGame.KFWeapDef_AA12' + , class'KFGame.KFWeapDef_FNFal' + , class'KFGame.KFWeapDef_HRGIncision' + ) + } + ), + ( RequiredScore=3000 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_Stoner63A' + , class'KFGame.KFWeapDef_ElephantGun' + , class'KFGame.KFWeapDef_Seeker6' + , class'KFGame.KFWeapDef_Eviscerator' + ) + } + ), + ( RequiredScore=3300 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_AbominationAxe' + , class'KFGame.KFWeapDef_RailGun' + , class'KFGame.KFWeapDef_MicrowaveGun' + , class'KFGame.KFWeapDef_SW500Dual' + ) + } + ), + ( RequiredScore=3600 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_MedicRifleGrenadeLauncher' + , class'KFGame.KFWeapDef_Kriss' + , class'KFGame.KFWeapDef_RPG7' + , class'KFGame.KFWeapDef_M99' + ) + } + ), + ( RequiredScore=4000 + , GrantedWeapons= { + ( + class'KFGame.KFWeapDef_M32' + , class'KFGame.KFWeapDef_LazerCutter' + , class'KFGame.KFWeapDef_MicrowaveRifle' + , class'KFGame.KFWeapDef_HRG_BarrierRifle' + ) + } + ) + )})} + + )} + //Test events from here down. These don't end up in the regular rotation. // The override ID starts from one higher than the last SetEvents entry above. // Ex: Big head = 7, Horde = 8 diff --git a/KFGameContent/Classes/KFPawn_AutoTurret.uc b/KFGameContent/Classes/KFPawn_AutoTurret.uc new file mode 100644 index 0000000..400ed0e --- /dev/null +++ b/KFGameContent/Classes/KFPawn_AutoTurret.uc @@ -0,0 +1,1139 @@ +//============================================================================= +// KFPawn_AutoTurret +//============================================================================= +// Base pawn class for autoturret +//============================================================================= +// 2022! +//============================================================================= +class KFPawn_AutoTurret extends KFPawn + notplaceable; + +`define AUTOTURRET_MIC_INDEX 0 + +enum ETurretState +{ + ETS_None, + ETS_Throw, + ETS_Deploy, + ETS_TargetSearch, + ETS_Combat, + ETS_Detonate, + ETS_Empty +}; + +var SkeletalMeshComponent TurretMesh; +var Controller InstigatorController; + +/** Speed to rise the drone in Z axis after thrown */ +var const float DeployZSpeed; +/** Height (Z axis) the drone should position for deployment */ +var const float DeployHeight; +/** Radius to detect enemies */ +var const float EffectiveRadius; +/** Radius to trigger explosion */ +var const float ExplosiveRadius; +/** Rotation Vel for aiming targets */ +var const rotator AimRotationVel; +/** Rotation Vel for aiming targets */ +var const rotator CombatRotationVel; +/** Time before refreshing targets */ +var const float TimeBeforeRefreshingTargets; +/** Time to idle after rotating to a random position (search) */ +var const float TimeIdlingWhenSearching; +/** Min Value to apply randomness to idling */ +var const float MinIdlingVariation; +/** Max Value to apply randomness to idling */ +var const float MaxIdlingVariation; +/** Pitch requested when out of ammo */ +var const int EmptyPitch; +/** Turret Explosion */ +var GameExplosion ExplosionTemplate; +/** Weapon the turret carries */ +var class WeaponDefinition; +/** WIf explodes when an enemy gets nearby */ +var const bool bCanDetonateOnProximityWithAmmo; + +var repnotify ETurretState CurrentState; +var repnotify rotator ReplicatedRotation; +var repnotify float CurrentAmmoPercentage; +var repnotify AKEvent TurretWeaponAmbientSound; +var repnotify int WeaponSkinID; + +var transient rotator DeployRotation; + +/** Rotation */ +var transient rotator RotationStart; +var transient rotator TargetRotation; +var transient float RotationAlpha; +var transient float RotationTime; +var transient bool bRotating; + +var transient KFWeap_AutoTurretWeapon TurretWeapon; +var transient KFWeapAttach_AutoTurretWeap TurretWeaponAttachment; + +var transient KFWeap_Autoturret OwnerWeapon; + +var bool deployUsingOffsetFromPlayerLocation; +var float deployUsingOffsetZ; + +var transient vector ThrowInstigatorLocation; +var transient vector GroundLocation; +var transient vector DeployedLocation; +var transient float RandomSearchLocation; +var transient KFPawn_Monster EnemyTarget; +var transient bool bHasSearchRandomLocation; +var transient byte TeamNum; +var transient bool bRotatingByTime; +var transient MaterialInstanceConstant TurretAttachMIC; + +/** Socket name to attach weapon */ +const WeaponSocketName = 'WeaponSocket'; +const WeaponAttachmentSocketName = 'WeaponAttachment'; + +const TransitionParamName = 'transition_full_to_empty'; +const EmptyParamName = 'Blinking_0_off___1_on'; + +const DroneFlyingStartAudio = AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_IdleFly'; +const DroneFlyingStopAudio = AkEvent'WW_WEP_Autoturret.Stop_WEP_AutoTurret_IdleFly'; + +var AkComponent FlyAkComponent; + +const DroneDryFire = AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Dron_Dry_Fire'; + +const NoAmmoSocketName = 'malfunction'; +const NoAmmoFXTemplate = ParticleSystem'WEP_AutoTurret_EMIT.FX_NoAmmo_Sparks'; +var transient ParticleSystemComponent NoAmmoFX; + +replication +{ + if( bNetDirty ) + CurrentState, ReplicatedRotation, CurrentAmmoPercentage, TurretWeaponAmbientSound, EnemyTarget, WeaponSkinID; +} + +simulated event ReplicatedEvent(name VarName) +{ + if( VarName == nameof(CurrentState) ) + { + ChangeState(CurrentState); + } + else if (VarName == nameof(ReplicatedRotation)) + { + RotateBySpeed(ReplicatedRotation); + } + else if (VarName == nameof(CurrentAmmoPercentage)) + { + UpdateTurretMeshMaterialColor(CurrentAmmoPercentage); + } + else if (VarName == nameof(TurretWeaponAmbientSound)) + { + SetWeaponAmbientSound(TurretWeaponAmbientSound); + } + else if (VarName == nameof(WeaponSkinID)) + { + SetWeaponSkin(WeaponSkinID); + } + else + { + super.ReplicatedEvent(VarName); + } +} + +simulated event PreBeginPlay() +{ + local class WeaponClass; + local rotator ZeroRotator; + + super.PreBeginPlay(); + + WeaponClass = class (DynamicLoadObject(WeaponDefinition.default.WeaponClassPath, class'Class')); + WeaponClassForAttachmentTemplate = WeaponClass; + + SetMeshVisibility(false); + + if (Role == ROLE_Authority) + { + Weapon = Spawn(WeaponClass, self); + TurretWeapon = KFWeap_AutoTurretWeapon(Weapon); + MyKFWeapon = TurretWeapon; + + if (Weapon != none) + { + Weapon.Instigator = Instigator; + TurretWeapon.InstigatorDrone = self; + Weapon.SetCollision(false, false); + Weapon.SetBase(self,, TurretMesh, WeaponSocketName); + TurretMesh.AttachComponentToSocket(Weapon.Mesh, WeaponSocketName); + + Weapon.SetRelativeLocation(vect(0,0,0)); + Weapon.SetRelativeRotation(ZeroRotator); + Weapon.Mesh.SetOnlyOwnerSee(true); + MyKFWeapon.Mesh.SetHidden(true); + } + } + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + /** If this fails, the AutoTurret is still loading from KFWeap_Autoturret. */ + if (WeaponClass.default.AttachmentArchetype != none) + { + SetTurretWeaponAttachment(WeaponClass); + } + } +} + +simulated function SetTurretWeaponAttachment(class WeaponClass) +{ + local KFWeaponAttachment AttachmentTemplate; + local rotator ZeroRotator; + + if (WeaponAttachment != none) + return; + + // Create the new Attachment. + AttachmentTemplate = WeaponClass.default.AttachmentArchetype; + WeaponAttachment = Spawn(AttachmentTemplate.Class, self,,,, AttachmentTemplate); + + if (WeaponAttachment != None) + { + WeaponAttachment.SetCollision(false, false); + WeaponAttachment.Instigator = Instigator; + + TurretMesh.AttachComponentToSocket(WeaponAttachment.WeapMesh, WeaponAttachmentSocketName); + WeaponAttachment.SetRelativeLocation(vect(0,0,0)); + WeaponAttachment.SetRelativeRotation(ZeroRotator); + WeaponAttachment.WeapMesh.SetOnlyOwnerSee(false); + WeaponAttachment.WeapMesh.SetOwnerNoSee(false); + WeaponAttachment.ChangeVisibility(true); + + WeaponAttachment.AttachLaserSight(); + + TurretWeaponAttachment = KFWeapAttach_AutoTurretWeap(WeaponAttachment); + TurretWeaponAttachment.PlayCloseAnim(); + } +} + +simulated function UpdateInstigator(Pawn NewInstigator) +{ + local KFPawn_Human KFPH; + + Instigator = NewInstigator; + + TeamNum = Instigator.GetTeamNum(); + + if (Weapon != none) + { + Weapon.Instigator = NewInstigator; + } + + KFPH = KFPawn_Human(NewInstigator); + if (KFPH != none && KFPH.WeaponSkinItemId > 0) + { + SetWeaponSkin(KFPH.WeaponSkinItemId); + } +} + +simulated function SetWeaponSkin(int SkinID) +{ + if (Role == Role_Authority) + { + WeaponSkinID = SkinID; + bForceNetUpdate = true; + } + + if (WeaponAttachment != none) + { + WeaponAttachment.SetWeaponSkin(SkinID); + } +} + +simulated function UpdateWeaponUpgrade(int UpgradeIndex) +{ + if (Weapon != none) + { + TurretWeapon.SetWeaponUpgradeLevel(UpgradeIndex); + } +} + +/** + Object states + */ +function SetTurretState(ETurretState State) +{ + if (CurrentState == State) + return; + + ChangeState(State); + + CurrentState = State; + bForceNetUpdate = true; +} + +function UpdateReadyToUse(bool bReady) +{ + if (OwnerWeapon != none) + { + OwnerWeapon.SetReadyToUse(bReady); + } +} + +simulated function ChangeState(ETurretState State) +{ + switch(State) + { + case ETS_None: + break; + case ETS_Throw: + GotoState('Throw'); + break; + case ETS_Deploy: + GotoState('Deploy'); + break; + case ETS_TargetSearch: + GotoState('TargetSearch'); + break; + case ETS_Combat: + GotoState('Combat'); + break; + case ETS_Detonate: + GotoState('Detonate'); + break; + case ETS_Empty: + GotoState('Empty'); + break; + default: + break; + } +} + +auto simulated state Throw +{ + simulated function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + ThrowInstigatorLocation = Instigator.Location; + + if (Role == Role_Authority) + { + UpdateReadyToUse(false); + } + } + + simulated function EndState(name NextStateName) + { + super.EndState(NextStateName); + + if (Role == Role_Authority) + { + UpdateReadyToUse(true); + } + } + + simulated event Landed(vector HitNormal, actor FloorActor) + { + super.Landed(HitNormal, FloorActor); + + if (Role == ROLE_Authority) + { + GroundLocation = Location; + DeployRotation = Rotation; + + SetTurretState(ETS_Deploy); + } + } + + simulated event Tick(float DeltaTime) + { + if (deployUsingOffsetFromPlayerLocation) + { + // If the drone goes below the offset, prevent from falling more + if (Location.Z < (ThrowInstigatorLocation.z - deployUsingOffsetZ)) + { + GroundLocation = Location; + DeployRotation = Rotation; + + SetTurretState(ETS_Deploy); + } + } + + super.Tick(DeltaTime); + } +} + +simulated state Deploy +{ + simulated function BeginState(name PreviousStateName) + { + local float AnimDuration; + + super.BeginState(PreviousStateName); + + if (TurretWeaponAttachment != none) + { + AnimDuration = TurretWeaponAttachment.PlayDeployAnim(); + SetTimer(AnimDuration, false, nameof(StartIdleAnim)); + } + + SetPhysics(PHYS_FLYING); + + if (Role == ROLE_Authority) + { + Velocity = vect(0,0,1) * DeployZSpeed; + UpdateReadyToUse(false); + } + } + + simulated event Tick(float DeltaTime) + { + local float CurrentHeight; + local vector LocationNext; + + super.Tick(DeltaTime); + + LocationNext = Location; + LocationNext.z += Velocity.z * DeltaTime; + + // If there's little to no movement or we are going to collide + if (Velocity.z <= 0.01f || !FastTrace(LocationNext, Location, vect(25,25,25))) + { + SetTurretState(ETS_TargetSearch); + } + else + { + // Check height to change state + CurrentHeight = Location.Z - GroundLocation.Z; + if (CurrentHeight >= DeployHeight) + { + SetTurretState(ETS_TargetSearch); + } + } + } + + simulated function EndState(name NextStateName) + { + super.EndState(NextStateName); + DeployedLocation = Location; + + Velocity = vect(0,0,0); + + if (Role == ROLE_Authority) + { + UpdateReadyToUse(true); + + if (bCanDetonateOnProximityWithAmmo) + { + SetTimer(0.25f, true, nameof(CheckEnemiesWithinExplosionRadius)); + } + } + } +} + +simulated state TargetSearch +{ + simulated function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + bHasSearchRandomLocation = false; + + if (Role == ROLE_Authority) + { + GetRandomSearchLocation(); + // bHasSearchRandomLocation=true; + + SetTimer(TimeBeforeRefreshingTargets, true, nameof(CheckForTargets)); + } + } + + simulated function EndState(name EndStateName) + { + if (Role == ROLE_Authority) + { + ClearTimer(nameof(CheckForTargets)); + ClearTimer(nameof(GetRandomSearchLocation)); + } + + super.EndState(EndStateName); + } + + simulated event Tick( float DeltaTime ) + { + super.Tick(DeltaTime); + + if (Role == ROLE_Authority) + { + if (ReachedRotation() && !bHasSearchRandomLocation) + { + SetTimer( TimeIdlingWhenSearching + RandRange(MinIdlingVariation, MaxIdlingVariation), false, nameof(GetRandomSearchLocation)); + bHasSearchRandomLocation = true; + } + } + } + + function GetRandomSearchLocation() + { + local rotator NewRotation; + + NewRotation.Yaw += RandRange(0,65536); + bHasSearchRandomLocation = false; + + RequestRotation(NewRotation); + } +} + +simulated state Combat +{ + simulated function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + /*if (Role == ROLE_Authority) + { + SetTimer(TimeBeforeRefreshingTargets, true, nameof(CheckForTargets)); + }*/ + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + if (TurretWeaponAttachment != none) + { + TurretWeaponAttachment.UpdateLaserColor(true); + } + } + } + + simulated function EndState(name NextStateName) + { + ClearTimer(nameof(CheckForTargets)); + + if (Role == ROLE_Authority && TurretWeapon != none) + { + TurretWeapon.StopFire(0); + } + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + if (TurretWeaponAttachment != none) + { + TurretWeaponAttachment.UpdateLaserColor(false); + } + } + + super.EndState(NextStateName); + } + + simulated event Tick( float DeltaTime ) + { + local vector MuzzleLoc; + local rotator MuzzleRot; + local rotator DesiredRotationRot; + + local vector HitLocation, HitNormal; + local TraceHitInfo HitInfo; + local Actor HitActor; + + local float NewAmmoPercentage; + + if (Role == ROLE_Authority) + { + TurretWeapon.GetMuzzleLocAndRot(MuzzleLoc, MuzzleRot); + + NewAmmoPercentage = float(TurretWeapon.AmmoCount[0]) / TurretWeapon.MagazineCapacity[0]; + + if (NewAmmoPercentage != CurrentAmmoPercentage) + { + CurrentAmmoPercentage = NewAmmoPercentage; + + if (WorldInfo.NetMode == NM_Standalone) + { + UpdateTurretMeshMaterialColor(CurrentAmmoPercentage); + } + else + { + bNetDirty = true; + } + } + } + else + { + WeaponAttachment.WeapMesh.GetSocketWorldLocationAndRotation('MuzzleFlash', MuzzleLoc, MuzzleRot); + } + + if (Role == ROLE_Authority) + { + if (EnemyTarget != none) + { + // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! + HitActor = Trace(HitLocation, HitNormal, EnemyTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); + + /** Search for new enemies if current is dead, cloaked or too far, or something between the drone and the target except a player */ + if (!EnemyTarget.IsAliveAndWell() + || EnemyTarget.bIsCloaking + || VSizeSq(EnemyTarget.Location - Location) > EffectiveRadius * EffectiveRadius + || (HitActor != none && KFPawn_Monster(HitActor) == none && KFPawn_Human(HitActor) == none)) + { + EnemyTarget = none; + CheckForTargets(); + + if (EnemyTarget == none) + { + SetTurretState(ETS_TargetSearch); + return; + } + } + } + } + + if (EnemyTarget != none) + { + DesiredRotationRot = rotator(Normal(EnemyTarget.Mesh.GetBoneLocation('Spine1') - MuzzleLoc)); + DesiredRotationRot.Roll = 0; + + RotateBySpeed(DesiredRotationRot); + + if (Role == ROLE_Authority) + { + HitActor = Trace(HitLocation, HitNormal, MuzzleLoc + vector(Rotation) * EffectiveRadius, MuzzleLoc, , , HitInfo, TRACEFLAG_Bullet); + + if (TurretWeapon != none) + { + if (KFPawn_Monster(HitActor) != none) + { + TurretWeapon.Fire(); + + if (!TurretWeapon.HasAmmo(0)) + { + SetTurretState(ETS_Empty); + } + } + else + { + TurretWeapon.StopFire(0); + } + } + } + } + + super.Tick(DeltaTime); + } +} + +simulated state Detonate +{ + function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + StopIdleSound(); + + if (Role == ROLE_Authority) + { + TriggerExplosion(); + } + } + + function TriggerExplosion() + { + local KFExplosionActorReplicated ExploActor; + + // explode using the given template + ExploActor = Spawn(class'KFExplosionActorReplicated', TurretWeapon,, Location, Rotation,, true); + if (ExploActor != None) + { + ExploActor.InstigatorController = Instigator.Controller; + ExploActor.Instigator = Instigator; + + ExploActor.Explode(ExplosionTemplate); + } + + Destroy(); + } +} + +simulated state Empty +{ + simulated function BeginState(name PreviousStateName) + { + super.BeginState(PreviousStateName); + + if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) + { + // Attach No Ammo VFX + if (NoAmmoFX == none) + { + NoAmmoFX = WorldInfo.MyEmitterPool.SpawnEmitter(NoAmmoFXTemplate, Location); + } + + // Play dry sound 2 or 3 times + SetTimer(0.5f, false, 'PlayEmptySound1'); + SetTimer(1.f, false, 'PlayEmptySound2'); + SetTimer(1.5f, false, 'PlayEmptySound3'); + + // When sound finish play animation + SetTimer(2.f, false, 'PlayEmptyState'); + + UpdateTurretMeshMaterialColor(0.0f); + } + + if (Role == ROLE_Authority && !bCanDetonateOnProximityWithAmmo) + { + SetTimer(0.25f, true, nameof(CheckEnemiesWithinExplosionRadius)); + } + } + + simulated function EndState(name NextStateName) + { + super.EndState(NextStateName); + } +} + +// We can't use the same delegate, hence we create 3 functions.. + +simulated function PlayEmptySound1() +{ + PlaySoundBase(DroneDryFire); +} + +simulated function PlayEmptySound2() +{ + PlaySoundBase(DroneDryFire); +} + +simulated function PlayEmptySound3() +{ + PlaySoundBase(DroneDryFire); +} + +simulated function PlayEmptyState() +{ + if (TurretWeaponAttachment != none) + { + StopIdleSound(); + + TurretWeaponAttachment.PlayEmptyState(); + } +} + +simulated function StopIdleSound() +{ + FlyAkComponent.StopEvents(); +} + +function CheckForTargets() +{ + local KFPawn_Monster CurrentTarget; + local TraceHitInfo HitInfo; + + local float CurrentDistance; + local float Distance; + + local vector MuzzleLoc; + local rotator MuzzleRot; + + local vector HitLocation, HitNormal; + local Actor HitActor; + + if (EnemyTarget != none) + { + CurrentDistance = VSizeSq(Location - EnemyTarget.Location); + } + else + { + CurrentDistance = 9999.f; + } + + TurretWeapon.GetMuzzleLocAndRot(MuzzleLoc, MuzzleRot); + + foreach CollidingActors(class'KFPawn_Monster', CurrentTarget, EffectiveRadius, Location, true,, HitInfo) + { + // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! + HitActor = Trace(HitLocation, HitNormal, CurrentTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); + + if (!CurrentTarget.IsAliveAndWell() || CurrentTarget.bIsCloaking || HitActor == none || KFPawn_Monster(HitActor) == none) + { + continue; + } + + Distance = VSizeSq(Location - CurrentTarget.Location); + + if (EnemyTarget == none) + { + EnemyTarget = CurrentTarget; + CurrentDistance = Distance; + } + else if (CurrentDistance > Distance) + { + EnemyTarget = CurrentTarget; + CurrentDistance = Distance; + } + } + + if (EnemyTarget != none) + { + SetTurretState(ETS_Combat); + } +} + +//////////////////////////////////////////////////////////// + +simulated event Destroyed() +{ + local KFWeap_AutoTurret WeapOwner; + + StopIdleSound(); + + WeapOwner = KFWeap_AutoTurret(Owner); + + if (WeapOwner != none) + { + WeapOwner.RemoveDeployedTurret(,self); + } + + ClearTimer(nameof(CheckEnemiesWithinExplosionRadius)); + + if (NoAmmoFX != none) + { + NoAmmoFX.KillParticlesForced(); + WorldInfo.MyEmitterPool.OnParticleSystemFinished(NoAmmoFX); + NoAmmoFX = none; + } + + super.Destroyed(); +} + +simulated function CheckEnemiesWithinExplosionRadius() +{ + local KFPawn_Monster KFPM; + local Vector CheckExplosionLocation; + + CheckExplosionLocation = Location; + CheckExplosionLocation.z -= DeployHeight * 0.5f; + + if (CheckExplosionLocation.z < GroundLocation.z) + { + CheckExplosionLocation.z = GroundLocation.z; + } + + //DrawDebugSphere(CheckExplosionLocation, ExplosiveRadius, 10, 255, 255, 0 ); + + foreach CollidingActors(class'KFPawn_Monster', KFPM, ExplosiveRadius, CheckExplosionLocation, true,,) + { + if(KFPM != none && KFPM.IsAliveAndWell()) + { + SetTurretState(ETS_Detonate); + return; + } + } +} + +simulated function StartIdleAnim() +{ + if (TurretWeaponAttachment != none) + { + TurretWeaponAttachment.PlayIdleAnim(); + + FlyAkComponent.PlayEvent(DroneFlyingStartAudio, true, true); + } +} + +simulated event Tick(float DeltaTime) +{ + local rotator NewRotationRate; + + /** This gets reset somehow and causes issues with the rotation */ + RotationRate = NewRotationRate; + + if (bRotating) + { + UpdateRotation(DeltaTime); + } + + super.Tick(DeltaTime); +} + +simulated function UpdateRotation(float DeltaTime) +{ + local rotator RotationDiff; + local rotator RotationStep; + local rotator NewRotation; + local rotator RotationSpeed; + local int Sign; + + if (bRotating) + { + if (bRotatingByTime) /** Rotate in an amount of time */ + { + if(RotationAlpha < RotationTime) + { + RotationAlpha += DeltaTime; + + RotationStep = RLerp(RotationStart, TargetRotation, FMin(RotationAlpha / RotationTime, 1.0f), true); + RotationStep.Roll = 0.0f; + + RotationStep.Yaw = RotationStep.Yaw % 65536; + RotationStep.Pitch = RotationStep.Pitch % 65536; + + if (RotationStep.Yaw < 0) + RotationStep.Yaw += 65536; + + if (RotationStep.Pitch < 0) + RotationStep.Pitch += 65536; + + SetRotation(RotationStep); + } + else + { + bRotating = false; + } + } + else /** Rotate By Speed */ + { + RotationSpeed = CurrentState == ETS_Combat ? CombatRotationVel : AimRotationVel; + + RotationDiff = TargetRotation - Rotation; + + RotationDiff.Yaw = NormalizeRotAxis(RotationDiff.Yaw); + RotationDiff.Pitch = NormalizeRotAxis(RotationDiff.Pitch); + RotationDiff.Roll = NormalizeRotAxis(RotationDiff.Roll); + + Sign = RotationDiff.Yaw >= 0? 1 : -1; + RotationStep.Yaw = RotationSpeed.Yaw * DeltaTime * Sign; + if (Abs(RotationStep.Yaw) > Abs(RotationDiff.Yaw)) + { + RotationStep.Yaw = RotationDiff.Yaw; + } + + Sign = RotationDiff.Pitch >= 0? 1 : -1; + RotationStep.Pitch = RotationSpeed.Pitch * DeltaTime * Sign; + if (Abs(RotationStep.Pitch) > Abs(RotationDiff.Pitch)) + { + RotationStep.Pitch = RotationDiff.Pitch; + } + + RotationStep.Roll = 0.0f; + + NewRotation = Rotation + RotationStep; + NewRotation.Yaw = NewRotation.Yaw % 65536; + NewRotation.Pitch = NewRotation.Pitch % 65536; + + if (NewRotation.Yaw < 0) + NewRotation.Yaw += 65536; + + if (NewRotation.Pitch < 0) + NewRotation.Pitch += 65536; + + SetRotation(NewRotation); + + if (ReachedRotation()) + { + bRotating = false; + } + } + } +} + +simulated function RotateByTime(rotator NewRotation, float Time) +{ + if (NewRotation != Rotation) + { + RotationStart = Rotation; + TargetRotation = NewRotation; + RotationAlpha = 0.0; + RotationTime = Time; + bRotating = true; + bRotatingByTime = true; + } +} + +simulated function RotateBySpeed(rotator NewRotation) +{ + TargetRotation = NewRotation; + RotationAlpha = 0.0f; + RotationTime = 0.0f; + bRotating = true; + bRotatingByTime = false; +} + +simulated event byte ScriptGetTeamNum() +{ + return TeamNum; +} + +simulated function RequestRotation(rotator NewRotation) +{ + if (Role == ROLE_Authority) + { + /** Set rotation the same as its going to be replicated **/ + ReplicatedRotation.Yaw = ((NewRotation.Yaw >> 8) & 255) << 8; + ReplicatedRotation.Pitch = ((NewRotation.Pitch >> 8) & 255) << 8; + + bForceNetUpdate = true; + } + + RotateBySpeed(ReplicatedRotation); +} + +simulated function bool ReachedRotation(int DeltaError = 2000) +{ + local int YawDiff; + YawDiff = Abs((TargetRotation.Yaw & 65535) - (Rotation.Yaw & 65535)); + return (YawDiff < DeltaError) || (YawDiff > 65535 - DeltaError); +} + +event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser) +{ +} + +simulated function TakeRadiusDamage( + Controller InstigatedBy, + float BaseDamage, + float DamageRadius, + class DamageType, + float Momentum, + vector HurtOrigin, + bool bFullDamage, + Actor DamageCauser, + optional float DamageFalloffExponent=1.f +) +{} + +function bool CanAITargetThisPawn(Controller TargetingController) +{ + return false; +} + +simulated function UpdateTurretMeshMaterialColor(float Value) +{ + if (TurretWeaponAttachment == none) + { + return; + } + + if (TurretAttachMIC == none) + { + TurretAttachMIC = TurretWeaponAttachment.WeapMesh.CreateAndSetMaterialInstanceConstant(`AUTOTURRET_MIC_INDEX); + } + + if (TurretAttachMIC != none) + { + TurretAttachMIC.SetScalarParameterValue(TransitionParamName, 1.0f - Value); + TurretAttachMIC.SetScalarParameterValue(EmptyParamName, Value == 0.0f ? 1 : 0); + } +} + +/** No AutoAim */ +simulated function bool GetAutoTargetBones(out array WeakBones, out array NormalBones) +{ + return false; +} + +simulated function SetWeaponAmbientSound(AkEvent NewAmbientSound, optional AkEvent FirstPersonAmbientSound) +{ + super.SetWeaponAmbientSound(NewAmbientSound, FirstPersonAmbientSound); + + if (WorldInfo.NetMode == NM_DedicatedServer) + { + TurretWeaponAmbientSound = NewAmbientSound; + bNetDirty = true; + } +} + +defaultproperties +{ + bCollideComplex=TRUE + Begin Object Name=CollisionCylinder + CollisionRadius=40 + CollisionHeight=30 + bIgnoreRadialImpulse=true + BlockNonZeroExtent=true + CollideActors=true + BlockActors=false + End Object + CylinderComponent=CollisionCylinder + + Begin Object Class=SkeletalMeshComponent Name=TurretMesh0 + SkeletalMesh=SkeletalMesh'WEP_3P_AutoTurret_MESH.drone_SM' + PhysicsAsset=PhysicsAsset'WEP_3P_AutoTurret_MESH.drone_SM_Physics' + BlockNonZeroExtent=false + CastShadow=false + bIgnoreRadialImpulse=true + + End Object + Components.Add(TurretMesh0) + TurretMesh=TurretMesh0 + Mesh=TurretMesh0 + + Begin Object Class=KFGameExplosion Name=ExploTemplate0 // @todo: change me + Damage=200 + DamageRadius=300 + DamageFalloffExponent=0.5f + DamageDelay=0.f + + // Damage Effects + MyDamageType=class'KFDT_Explosive_AutoTurret' + KnockDownStrength=0 + FractureMeshRadius=200.0 + FracturePartVel=500.0 + ExplosionEffects=KFImpactEffectInfo'wep_autoturret_arch.AutoTurret_Explosion' // Original : KFImpactEffectInfo'WEP_Seal_Squeal_ARCH.SealSquealHarpoon_Explosion' + ExplosionSound=AkEvent'ww_wep_autoturret.Play_WEP_AutoTurret_Alt_Fire_3P' + + // 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 + + // sounds + Begin Object Class=AkComponent name=FlyAkComponent0 + BoneName=dummy + bStopWhenOwnerDestroyed=true + bForceOcclusionUpdateInterval=true + OcclusionUpdateInterval=0.2f + End Object + FlyAkComponent=FlyAkComponent0 + Components.Add(FlyAkComponent0) + + CurrentState=ETS_None + DeployZSpeed=800.0f + DeployHeight=200.0f + + deployUsingOffsetFromPlayerLocation=true + deployUsingOffsetZ = 100.f + + EffectiveRadius=1500.0f + ExplosiveRadius=100.0f + + WeaponDefinition=class'KFGame.KFWeapDef_AutoTurretWeapon' + + TimeBeforeRefreshingTargets=0.5f + TimeIdlingWhenSearching = 1.0f + MinIdlingVariation = -0.5f + MaxIdlingVariation = 1.0f + + // Rotation speed per second + AimRotationVel =(Pitch=16384,Yaw=32768,Roll=0) + CombatRotationVel =(Pitch=16384,Yaw=32768,Roll=0) + + RotationAlpha = 0.0f + RotationTime = 0.0f + bRotating = false + bHasSearchRandomLocation = false + + EmptyPitch=0 //-10000 + + bRunPhysicsWithNoController=true; + + bCanBeDamaged=false + TeamNum=0 + + bRotatingByTime=false + RemoteRole = ROLE_SimulatedProxy + + bCanDetonateOnProximityWithAmmo=false + bIgnoreForces=true + + bIsTurret=true + + NoAmmoFX=none +} diff --git a/KFGameContent/Classes/KFPawn_ZedBloat.uc b/KFGameContent/Classes/KFPawn_ZedBloat.uc index a5556ea..63ba38c 100644 --- a/KFGameContent/Classes/KFPawn_ZedBloat.uc +++ b/KFGameContent/Classes/KFPawn_ZedBloat.uc @@ -314,6 +314,9 @@ DefaultProperties IncapSettings(AF_Freeze)= (Vulnerability=(1.0), Cooldown=3.0, Duration=2.0) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0), Cooldown=5.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(1.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.25f Begin Object Name=Afflictions_0 FireFullyCharredDuration=3.5 diff --git a/KFGameContent/Classes/KFPawn_ZedBloatKing.uc b/KFGameContent/Classes/KFPawn_ZedBloatKing.uc index 719264c..9d541d4 100644 --- a/KFGameContent/Classes/KFPawn_ZedBloatKing.uc +++ b/KFGameContent/Classes/KFPawn_ZedBloatKing.uc @@ -737,6 +737,9 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(0.3), Cooldown=15.0, Duration=1.0) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0, 2.0), Cooldown=10.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.15), Cooldown=10.0) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.15f // Resistant damage types DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Submachinegun', DamageScale=(0.5))) @@ -765,6 +768,7 @@ defaultproperties DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGeneratorSphereImpact', DamageScale=(2))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_DefaultFiremodeZapDamage', DamageScale=(1.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_AltFiremodeZapDamage', DamageScale=(1.5))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Shrink_ShrinkRayGun', DamageScale=(3.0))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGScorcherLightingImpact', DamageScale=(0.4))) DamageTypeModifiers.Add((DamageType=class'KFDT_Fire_HRGScorcherDoT', DamageScale=(0.4))) @@ -784,4 +788,6 @@ defaultproperties PenetrationResistance=10.0 bCanBePinned=false + + bCanBeKilledByShrinking=false } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedClot_Alpha.uc b/KFGameContent/Classes/KFPawn_ZedClot_Alpha.uc index 2536aec..1555dcb 100644 --- a/KFGameContent/Classes/KFPawn_ZedClot_Alpha.uc +++ b/KFGameContent/Classes/KFPawn_ZedClot_Alpha.uc @@ -55,6 +55,9 @@ DefaultProperties IncapSettings(AF_Freeze)= (Vulnerability=(2.5), Cooldown=1.5, Duration=4.0) IncapSettings(AF_Snare)= (Vulnerability=(10.0, 10.0, 10.0, 10.0), Cooldown=5.5, Duration=4.0) IncapSettings(AF_Bleed)= (Vulnerability=(2.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier=2.0f //2.0f // Double speed for shrinking ParryResistance=0 diff --git a/KFGameContent/Classes/KFPawn_ZedClot_AlphaKing.uc b/KFGameContent/Classes/KFPawn_ZedClot_AlphaKing.uc index 8bee365..a72ab4d 100644 --- a/KFGameContent/Classes/KFPawn_ZedClot_AlphaKing.uc +++ b/KFGameContent/Classes/KFPawn_ZedClot_AlphaKing.uc @@ -196,6 +196,9 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(2.5), Cooldown=1.5, Duration=4.0) IncapSettings(AF_Snare)= (Vulnerability=(10.0, 10.0, 10.0, 10.0), Cooldown=5.5, Duration=4.0) IncapSettings(AF_Bleed)= (Vulnerability=(2.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.4f ParryResistance=0 diff --git a/KFGameContent/Classes/KFPawn_ZedClot_Cyst.uc b/KFGameContent/Classes/KFPawn_ZedClot_Cyst.uc index 74012c1..08bb49f 100644 --- a/KFGameContent/Classes/KFPawn_ZedClot_Cyst.uc +++ b/KFGameContent/Classes/KFPawn_ZedClot_Cyst.uc @@ -54,6 +54,9 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(2.5), Cooldown=1.5, Duration=5.0) IncapSettings(AF_Snare)= (Vulnerability=(10.0, 10.0, 10.0, 10.0), Cooldown=5.5, Duration=4.0) IncapSettings(AF_Bleed)= (Vulnerability=(2.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 2.0f ParryResistance=0 diff --git a/KFGameContent/Classes/KFPawn_ZedClot_Slasher.uc b/KFGameContent/Classes/KFPawn_ZedClot_Slasher.uc index dc1893b..2179469 100644 --- a/KFGameContent/Classes/KFPawn_ZedClot_Slasher.uc +++ b/KFGameContent/Classes/KFPawn_ZedClot_Slasher.uc @@ -66,6 +66,9 @@ DefaultProperties IncapSettings(AF_Freeze)= (Vulnerability=(2.5), Cooldown=1.5, Duration=4.0) IncapSettings(AF_Snare)= (Vulnerability=(10.0, 10.0, 10.0, 10.0), Cooldown=5.5, Duration=4.0) IncapSettings(AF_Bleed)= (Vulnerability=(2.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier =2.0f //3.0f // Triple shrinking speed ParryResistance=0 diff --git a/KFGameContent/Classes/KFPawn_ZedCrawler.uc b/KFGameContent/Classes/KFPawn_ZedCrawler.uc index d2101ef..93506e0 100644 --- a/KFGameContent/Classes/KFPawn_ZedCrawler.uc +++ b/KFGameContent/Classes/KFPawn_ZedCrawler.uc @@ -423,6 +423,9 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(2.5), Cooldown=1.5, Duration=4.5) IncapSettings(AF_Snare)= (Vulnerability=(10.0, 10.0, 10.0, 10.0), Cooldown=5.5, Duration=4.0) IncapSettings(AF_Bleed)= (Vulnerability=(2.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 2.0f ParryResistance=1 diff --git a/KFGameContent/Classes/KFPawn_ZedDAR.uc b/KFGameContent/Classes/KFPawn_ZedDAR.uc index c392d51..347ba1c 100644 --- a/KFGameContent/Classes/KFPawn_ZedDAR.uc +++ b/KFGameContent/Classes/KFPawn_ZedDAR.uc @@ -395,6 +395,9 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(0.9), Cooldown=1.0, Duration=4.2) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 1.0, 2.0, 1.0, 1.0), Cooldown=5.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.01)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.7f ParryResistance=1 diff --git a/KFGameContent/Classes/KFPawn_ZedFleshpound.uc b/KFGameContent/Classes/KFPawn_ZedFleshpound.uc index fd1fe3d..5c73cea 100644 --- a/KFGameContent/Classes/KFPawn_ZedFleshpound.uc +++ b/KFGameContent/Classes/KFPawn_ZedFleshpound.uc @@ -537,6 +537,9 @@ End Object IncapSettings(AF_Freeze)= (Vulnerability=(0.95), Cooldown=10.5, Duration=1.0) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0, 2.0), Cooldown=8.5, Duration=5.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.75)) //0.25 + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.2f Begin Object Name=Afflictions_0 FireFullyCharredDuration=5 diff --git a/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc b/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc index c08f199..1909671 100644 --- a/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc +++ b/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc @@ -673,6 +673,7 @@ DefaultProperties DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGeneratorSphereImpact', DamageScale=(2))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_DefaultFiremodeZapDamage', DamageScale=(1.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_AltFiremodeZapDamage', DamageScale=(1.5))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Shrink_ShrinkRayGun', DamageScale=(3.0))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGScorcherLightingImpact', DamageScale=(0.6))) DamageTypeModifiers.Add((DamageType=class'KFDT_Fire_HRGScorcherDoT', DamageScale=(0.4))) @@ -792,6 +793,9 @@ DefaultProperties IncapSettings(AF_Freeze)= (Vulnerability=(0.37), Cooldown=10.0, Duration=1.0) IncapSettings(AF_Snare)= (Vulnerability=(0.75, 1.5, 0.75, 0.75, 1.5), Cooldown=10.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.20), Cooldown=10.0) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.15f Begin Object Name=Afflictions_0 FireFullyCharredDuration=5 @@ -979,4 +983,6 @@ DefaultProperties BeamHitAC=BeamHitAC0 bCanBePinned=false + + bCanBeKilledByShrinking=false } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedFleshpoundMini.uc b/KFGameContent/Classes/KFPawn_ZedFleshpoundMini.uc index 1bb9c82..15cf140 100644 --- a/KFGameContent/Classes/KFPawn_ZedFleshpoundMini.uc +++ b/KFGameContent/Classes/KFPawn_ZedFleshpoundMini.uc @@ -72,6 +72,9 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(0.95), Cooldown=10.5, Duration=1.0) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0, 2.0), Cooldown=8.5, Duration=5.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.75)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.3f ZEDCowboyHatAttachName=HEAD_Attach } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedGorefast.uc b/KFGameContent/Classes/KFPawn_ZedGorefast.uc index 6975adf..02fa52e 100644 --- a/KFGameContent/Classes/KFPawn_ZedGorefast.uc +++ b/KFGameContent/Classes/KFPawn_ZedGorefast.uc @@ -97,6 +97,9 @@ DefaultProperties IncapSettings(AF_Freeze)= (Vulnerability=(2.0), Cooldown=1.5, Duration=4.0) IncapSettings(AF_Snare)= (Vulnerability=(10.0, 10.0, 10.0, 10.0), Cooldown=5.5, Duration=4.0) IncapSettings(AF_Bleed)= (Vulnerability=(2.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.6f ParryResistance=2 diff --git a/KFGameContent/Classes/KFPawn_ZedGorefastDualBlade.uc b/KFGameContent/Classes/KFPawn_ZedGorefastDualBlade.uc index 0f8c88e..36d0838 100644 --- a/KFGameContent/Classes/KFPawn_ZedGorefastDualBlade.uc +++ b/KFGameContent/Classes/KFPawn_ZedGorefastDualBlade.uc @@ -79,6 +79,9 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(2.0), Cooldown=1.5, Duration=4.0) IncapSettings(AF_Snare)= (Vulnerability=(10.0, 10.0, 10.0, 10.0), Cooldown=5.5, Duration=4.0) IncapSettings(AF_Bleed)= (Vulnerability=(2.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.4f ParryResistance=2 diff --git a/KFGameContent/Classes/KFPawn_ZedHans.uc b/KFGameContent/Classes/KFPawn_ZedHans.uc index 50d1d76..04c9c69 100644 --- a/KFGameContent/Classes/KFPawn_ZedHans.uc +++ b/KFGameContent/Classes/KFPawn_ZedHans.uc @@ -1181,6 +1181,9 @@ DefaultProperties IncapSettings(AF_Freeze)= (Vulnerability=(0.5), Cooldown=10.0, Duration=1.0) //0.95 IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0, 2.0), Cooldown=10.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.20), Cooldown=10.0) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.15f ParryResistance=4 @@ -1228,6 +1231,7 @@ DefaultProperties DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGeneratorSphereImpact', DamageScale=(2))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_DefaultFiremodeZapDamage', DamageScale=(1.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_AltFiremodeZapDamage', DamageScale=(1.5))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Shrink_ShrinkRayGun', DamageScale=(3.0))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGScorcherLightingImpact', DamageScale=(0.6))) DamageTypeModifiers.Add((DamageType=class'KFDT_Fire_HRGScorcherDoT', DamageScale=(0.9))) diff --git a/KFGameContent/Classes/KFPawn_ZedHusk.uc b/KFGameContent/Classes/KFPawn_ZedHusk.uc index 67f3a24..2656dfc 100644 --- a/KFGameContent/Classes/KFPawn_ZedHusk.uc +++ b/KFGameContent/Classes/KFPawn_ZedHusk.uc @@ -577,6 +577,9 @@ DefaultProperties IncapSettings(AF_Freeze)= (Vulnerability=(1.0), Cooldown=1.5, Duration=1.0) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0, 2.0), Cooldown=5.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(1.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.7f Begin Object Name=Afflictions_0 FireFullyCharredDuration=5 diff --git a/KFGameContent/Classes/KFPawn_ZedMatriarch.uc b/KFGameContent/Classes/KFPawn_ZedMatriarch.uc index 70209b9..e37762e 100644 --- a/KFGameContent/Classes/KFPawn_ZedMatriarch.uc +++ b/KFGameContent/Classes/KFPawn_ZedMatriarch.uc @@ -1525,6 +1525,8 @@ defaultproperties /*AF_Freeze*/ (Vulnerability=(0.0), Cooldown=10.0, Duration=1.0), /*AF_Microwave*/(Vulnerability=(0.08), Cooldown=10.0, Duration=3.0), /*AF_Bleed*/ (Vulnerability=(0.15), Cooldown=10.0), + /*AF_Bighead*/ (Vulnerability=(0.0)), + /*AF_Shrink*/ (Vulnerability=(1.0)) )} )} BattlePhases(1)={( @@ -1554,6 +1556,8 @@ defaultproperties /*AF_Freeze*/ (Vulnerability=(0.65), Cooldown=10.0, Duration=1.0), /*AF_Microwave*/(Vulnerability=(0.08), Cooldown=10.0, Duration=3.0), /*AF_Bleed*/ (Vulnerability=(0.15), Cooldown=10.0), + /*AF_Bighead*/ (Vulnerability=(0.0)), + /*AF_Shrink*/ (Vulnerability=(1.0)) )}, DamageFX={( FXInfo={( @@ -1595,6 +1599,8 @@ defaultproperties /*AF_Freeze*/ (Vulnerability=(0.65), Cooldown=10.0, Duration=1.0), /*AF_Microwave*/(Vulnerability=(0.08), Cooldown=10.0, Duration=3.0), /*AF_Bleed*/ (Vulnerability=(0.15), Cooldown=10.0), + /*AF_Bighead*/ (Vulnerability=(0.0)), + /*AF_Shrink*/ (Vulnerability=(1.0)) )}, DamageFX={( FXInfo={( @@ -1648,6 +1654,8 @@ defaultproperties /*AF_Freeze*/ (Vulnerability=(0.65), Cooldown=10.0, Duration=1.0), /*AF_Microwave*/(Vulnerability=(0.08), Cooldown=10.0, Duration=3.0), /*AF_Bleed*/ (Vulnerability=(0.15), Cooldown=10.0), + /*AF_Bighead*/ (Vulnerability=(0.0)), + /*AF_Shrink*/ (Vulnerability=(1.0)) )}, DamageFX={( FXInfo={( @@ -1960,6 +1968,7 @@ defaultproperties DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGeneratorSphereImpact', DamageScale=(2))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_DefaultFiremodeZapDamage', DamageScale=(1.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_AltFiremodeZapDamage', DamageScale=(1.5))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Shrink_ShrinkRayGun', DamageScale=(3.0))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGScorcherLightingImpact', DamageScale=(0.4))) DamageTypeModifiers.Add((DamageType=class'KFDT_Fire_HRGScorcherDoT', DamageScale=(0.4))) @@ -1972,6 +1981,7 @@ defaultproperties DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRG_Vampire_BloodBallHeavyImpact', DamageScale=(0.4))) DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing_HRG_Vampire_CrystalSpike', DamageScale=(0.3))) DamageTypeModifiers.Add((DamageType=class'KFDT_Bleeding_HRG_Vampire_BloodSuck', DamageScale=(0.7))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing_HRG_CranialPopper', DamageScale=(2.0))) // --------------------------------------------- // Armor @@ -2021,4 +2031,6 @@ defaultproperties GunTargetBoneName=Spine2 ZEDCowboyHatAttachName=Hat_Attach + + ShrinkEffectModifier = 0.15f } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedPatriarch.uc b/KFGameContent/Classes/KFPawn_ZedPatriarch.uc index d7713ca..887b7ed 100644 --- a/KFGameContent/Classes/KFPawn_ZedPatriarch.uc +++ b/KFGameContent/Classes/KFPawn_ZedPatriarch.uc @@ -2117,6 +2117,9 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(0.5), Cooldown=10.0, Duration=1.0) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0, 2.0), Cooldown=10.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.15), Cooldown=10.0) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.15f Begin Object Class=Name=Afflictions_0 FireFullyCharredDuration=50.f @@ -2186,6 +2189,7 @@ defaultproperties DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGeneratorSphereImpact', DamageScale=(2))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_DefaultFiremodeZapDamage', DamageScale=(1.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_AltFiremodeZapDamage', DamageScale=(1.5))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Shrink_ShrinkRayGun', DamageScale=(3.0))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGScorcherLightingImpact', DamageScale=(0.4))) DamageTypeModifiers.Add((DamageType=class'KFDT_Fire_HRGScorcherDoT', DamageScale=(0.4))) diff --git a/KFGameContent/Classes/KFPawn_ZedScrake.uc b/KFGameContent/Classes/KFPawn_ZedScrake.uc index 824c1d0..b7e2547 100644 --- a/KFGameContent/Classes/KFPawn_ZedScrake.uc +++ b/KFGameContent/Classes/KFPawn_ZedScrake.uc @@ -284,7 +284,10 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(0.98), Cooldown=6.0, Duration=1.0) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0), Cooldown=5.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.75)) //0.5 - + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) // Normal Vulnerability + + ShrinkEffectModifier = 0.2f + Begin Object Name=Afflictions_0 FireFullyCharredDuration=5 AfflictionClasses(AF_EMP)=class'KFAffliction_EMPDisrupt' diff --git a/KFGameContent/Classes/KFPawn_ZedSiren.uc b/KFGameContent/Classes/KFPawn_ZedSiren.uc index 097e36c..5220702 100644 --- a/KFGameContent/Classes/KFPawn_ZedSiren.uc +++ b/KFGameContent/Classes/KFPawn_ZedSiren.uc @@ -155,6 +155,9 @@ defaultproperties IncapSettings(AF_Freeze)= (Vulnerability=(2.0), Cooldown=1.5, Duration=4.2) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0, 2.0), Cooldown=5.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(1.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 0.7f Begin Object Name=Afflictions_0 AfflictionClasses(AF_EMP)=class'KFAffliction_EMPDisrupt' diff --git a/KFGameContent/Classes/KFPawn_ZedStalker.uc b/KFGameContent/Classes/KFPawn_ZedStalker.uc index 33fed3c..f778fbe 100644 --- a/KFGameContent/Classes/KFPawn_ZedStalker.uc +++ b/KFGameContent/Classes/KFPawn_ZedStalker.uc @@ -465,6 +465,9 @@ DefaultProperties IncapSettings(AF_Freeze)= (Vulnerability=(2.5), Cooldown=1.5, Duration=4.0) IncapSettings(AF_Snare)= (Vulnerability=(10.0, 10.0, 10.0, 10.0), Cooldown=5.5, Duration=4.0) IncapSettings(AF_Bleed)= (Vulnerability=(2.0)) + IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) + + ShrinkEffectModifier = 2.0f ParryResistance=1 diff --git a/KFGameContent/Classes/KFProj_Bolt_HRG_Crossboom.uc b/KFGameContent/Classes/KFProj_Bolt_HRG_Crossboom.uc new file mode 100644 index 0000000..84066ec --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bolt_HRG_Crossboom.uc @@ -0,0 +1,247 @@ +//============================================================================= +// KFProj_Bolt_HRG_Crossboom +//============================================================================= +// Bullet class for the HRG Crossboom +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bolt_HRG_Crossboom extends KFProjectile; + +var() float SecondsBeforeDetonation; +var() bool bIsProjActive; +/** + * Set the initial velocity and cook time + */ +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + AdjustCanDisintigrate(); +} + +/** + * Explode after a certain amount of time + */ +function Timer_Detonate() +{ + Detonate(); +} + +/** Called when the owning instigator controller has left a game */ +simulated function OnInstigatorControllerLeft() +{ + if( WorldInfo.NetMode != NM_Client ) + { + SetTimer( 1.f + Rand(5) + fRand(), false, nameOf(Destroy) ); + } +} + +/** + * Trace down and get the location to spawn the explosion effects and decal + */ +simulated function GetExplodeEffectLocation(out vector HitLocation, out vector HitRotation, out Actor HitActor) +{ + local vector EffectStartTrace, EffectEndTrace; + local TraceHitInfo HitInfo; + + EffectStartTrace = Location + vect(0,0,1) * 4.f; + EffectEndTrace = EffectStartTrace - vect(0,0,1) * 32.f; + + // Find where to put the decal + HitActor = Trace(HitLocation, HitRotation, EffectEndTrace, EffectStartTrace, false,, HitInfo, TRACEFLAG_Bullet); + + // If the locations are zero (probably because this exploded in the air) set defaults + if (IsZero(HitLocation)) + { + HitLocation = Location; + } + + if (IsZero(HitRotation)) + { + HitRotation = vect(0,0,1); + } +} + +/** Used to check current status of StuckTo actor (to figure out if we should fall) */ +simulated event Tick(float DeltaTime) +{ + super.Tick(DeltaTime); + + if (bIsProjActive) + { + StickHelper.Tick(DeltaTime); + } + + if (!IsZero(Velocity)) + { + SetRelativeRotation(rotator(Velocity)); + } +} + +/** Causes charge to explode */ +function Detonate() +{ + local vector ExplosionNormal; + + ExplosionNormal = vect(0,0,1) >> Rotation; + Explode(Location, ExplosionNormal); +} + +simulated function Explode(vector HitLocation, vector HitNormal) +{ + if (bIsProjActive) + { + StickHelper.UnPin(); + super.Explode(HitLocation, HitNormal); + } +} + +// for nukes && concussive force +simulated protected function PrepareExplosionTemplate() +{ + class'KFPerk_Demolitionist'.static.PrepareExplosive(Instigator, self); + + super.PrepareExplosionTemplate(); +} + +simulated protected function SetExplosionActorClass() +{ + local KFPlayerReplicationInfo InstigatorPRI; + + if( WorldInfo.TimeDilation < 1.f && Instigator != none ) + { + InstigatorPRI = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo); + if( InstigatorPRI != none ) + { + if( InstigatorPRI.bNukeActive && class'KFPerk_Demolitionist'.static.ProjectileShouldNuke( self ) ) + { + ExplosionActorClass = class'KFPerk_Demolitionist'.static.GetNukeExplosionActorClass(); + } + } + } + + super.SetExplosionActorClass(); +} + +simulated function SyncOriginalLocation() +{ + local Actor HitActor; + local vector HitLocation, HitNormal; + local TraceHitInfo HitInfo; + + if (Role < ROLE_Authority && Instigator != none && Instigator.IsLocallyControlled()) + { + HitActor = Trace(HitLocation, HitNormal, OriginalLocation, Location,,, HitInfo, TRACEFLAG_Bullet); + if (HitActor != none) + { + StickHelper.TryStick(HitNormal, HitLocation, HitActor); + } + } +} + +simulated function NotifyStick() +{ + SetTimer(SecondsBeforeDetonation, false, nameof(Timer_Detonate)); +} + +simulated function NotifyBounce() +{ + ClearTimer(nameof(Timer_Detonate), self); + ClearTimer(nameOf(Destroy)); + bIsProjActive=false; +} + +unreliable server function OnCollectibleHit(Actor Collectible) +{ + Collectible.TakeDamage(Damage, InstigatorController, Location, MomentumTransfer * Normal(Velocity), MyDamageType,, Owner); +} + +defaultproperties +{ + bWarnAIWhenFired=true + + MaxSpeed=15000.0 + Speed=15000.0 + + DamageRadius=0 + + ProjFlightTemplate=ParticleSystem'WEP_HRG_Crossboom_EMIT.FX_Crossboom_Projectile' + ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01' + + LifeSpan=20 + + bBlockedByInstigator=false + bCollideActors=true + bCollideComplex=true + bNoEncroachCheck=true + bNoReplicationToInstigator=false + bUseClientSideHitDetection=true + bUpdateSimulatedPosition=false + bRotationFollowsVelocity=false + bNetTemporary=false + bSyncToOriginalLocation=true + + ImpactEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Crossbow_impact' + + AmbientSoundPlayEvent=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_Bolt_Fly_By' + AmbientSoundStopEvent=AkEvent'WW_WEP_HRG_Crossboom.Stop_WEP_HRG_Crossboom_Bolt_Fly_By' + + // 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=350 + DamageRadius=200 + DamageFalloffExponent=1.f + DamageDelay=0.f + + // Damage Effects + MyDamageType=class'KFDT_Explosive_HRG_Crossboom' + KnockDownStrength=0 + FractureMeshRadius=200.0 + FracturePartVel=500.0 + ExplosionEffects=KFImpactEffectInfo'WEP_HRG_Crossboom_ARCH.Wep_HRG_Crossboom_Explosion' + ExplosionSound=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_Impact_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 + + ExplosionActorClass=class'KFExplosionActor' + + //PinBoneIdx=INDEX_None + bCanStick=true + bCanPin=false + Begin Object Class=KFProjectileStickHelper_HRG_Crossboom Name=StickHelper0 + End Object + StickHelper=StickHelper0 + + SecondsBeforeDetonation=0.5f + bIsProjActive=true + bCanDisintegrate=true + bAlwaysReplicateExplosion=true +} diff --git a/KFGameContent/Classes/KFProj_Bolt_HRG_CrossboomAlt.uc b/KFGameContent/Classes/KFProj_Bolt_HRG_CrossboomAlt.uc new file mode 100644 index 0000000..685c169 --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bolt_HRG_CrossboomAlt.uc @@ -0,0 +1,248 @@ +//============================================================================= +// KFProj_Bolt_HRG_Crossboom +//============================================================================= +// Bullet class for the HRG Crossboom +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bolt_HRG_CrossboomAlt extends KFProjectile; + +var() float SecondsBeforeDetonation; +var() bool bIsProjActive; +/** + * Set the initial velocity and cook time + */ +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + AdjustCanDisintigrate(); +} + +/** + * Explode after a certain amount of time + */ +function Timer_Detonate() +{ + Detonate(); +} + +/** Called when the owning instigator controller has left a game */ +simulated function OnInstigatorControllerLeft() +{ + if( WorldInfo.NetMode != NM_Client ) + { + SetTimer( 1.f + Rand(5) + fRand(), false, nameOf(Destroy) ); + } +} + +/** + * Trace down and get the location to spawn the explosion effects and decal + */ +simulated function GetExplodeEffectLocation(out vector HitLocation, out vector HitRotation, out Actor HitActor) +{ + local vector EffectStartTrace, EffectEndTrace; + local TraceHitInfo HitInfo; + + EffectStartTrace = Location + vect(0,0,1) * 4.f; + EffectEndTrace = EffectStartTrace - vect(0,0,1) * 32.f; + + // Find where to put the decal + HitActor = Trace(HitLocation, HitRotation, EffectEndTrace, EffectStartTrace, false,, HitInfo, TRACEFLAG_Bullet); + + // If the locations are zero (probably because this exploded in the air) set defaults + if (IsZero(HitLocation)) + { + HitLocation = Location; + } + + if (IsZero(HitRotation)) + { + HitRotation = vect(0,0,1); + } +} + +/** Used to check current status of StuckTo actor (to figure out if we should fall) */ +simulated event Tick(float DeltaTime) +{ + super.Tick(DeltaTime); + + if (bIsProjActive) + { + StickHelper.Tick(DeltaTime); + } + + if (!IsZero(Velocity)) + { + SetRelativeRotation(rotator(Velocity)); + } +} + +/** Causes charge to explode */ +function Detonate() +{ + local vector ExplosionNormal; + + ExplosionNormal = vect(0,0,1) >> Rotation; + Explode(Location, ExplosionNormal); +} + +simulated function Explode(vector HitLocation, vector HitNormal) +{ + if (bIsProjActive) + { + StickHelper.UnPin(); + super.Explode(HitLocation, HitNormal); + } +} + +// for nukes && concussive force +simulated protected function PrepareExplosionTemplate() +{ + class'KFPerk_Demolitionist'.static.PrepareExplosive(Instigator, self); + + super.PrepareExplosionTemplate(); +} + +simulated protected function SetExplosionActorClass() +{ + local KFPlayerReplicationInfo InstigatorPRI; + + if( WorldInfo.TimeDilation < 1.f && Instigator != none ) + { + InstigatorPRI = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo); + if( InstigatorPRI != none ) + { + if( InstigatorPRI.bNukeActive && class'KFPerk_Demolitionist'.static.ProjectileShouldNuke( self ) ) + { + ExplosionActorClass = class'KFPerk_Demolitionist'.static.GetNukeExplosionActorClass(); + } + } + } + + super.SetExplosionActorClass(); +} + +simulated function SyncOriginalLocation() +{ + local Actor HitActor; + local vector HitLocation, HitNormal; + local TraceHitInfo HitInfo; + + if (Role < ROLE_Authority && Instigator != none && Instigator.IsLocallyControlled()) + { + HitActor = Trace(HitLocation, HitNormal, OriginalLocation, Location,,, HitInfo, TRACEFLAG_Bullet); + if (HitActor != none) + { + StickHelper.TryStick(HitNormal, HitLocation, HitActor); + } + } +} + +simulated function NotifyStick() +{ + SetTimer(SecondsBeforeDetonation, false, nameof(Timer_Detonate)); +} + +simulated function NotifyBounce() +{ + ClearTimer(nameof(Timer_Detonate), self); + ClearTimer(nameOf(Destroy)); + bIsProjActive=false; +} + +unreliable server function OnCollectibleHit(Actor Collectible) +{ + Collectible.TakeDamage(Damage, InstigatorController, Location, MomentumTransfer * Normal(Velocity), MyDamageType,, Owner); +} + +defaultproperties +{ + bWarnAIWhenFired=true + + MaxSpeed=15000.0 + Speed=15000.0 + + DamageRadius=0 + + ProjFlightTemplate=ParticleSystem'WEP_HRG_Crossboom_EMIT.FX_ALTFIRE_Crossboom_Projectile' + ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01' + + LifeSpan=20 + + bBlockedByInstigator=false + bCollideActors=true + bCollideComplex=true + bNoEncroachCheck=true + bNoReplicationToInstigator=false + bUseClientSideHitDetection=true + bUpdateSimulatedPosition=false + bRotationFollowsVelocity=false + bNetTemporary=false + bSyncToOriginalLocation=true + + ImpactEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Crossbow_impact' + + AmbientSoundPlayEvent=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_Bolt_Fly_By' + AmbientSoundStopEvent=AkEvent'WW_WEP_HRG_Crossboom.Stop_WEP_HRG_Crossboom_Bolt_Fly_By' + + // 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=100 + DamageRadius=800 + DamageFalloffExponent=0.5f + DamageDelay=0.f + + // Damage Effects + MyDamageType=class'KFDT_Explosive_HRG_CrossboomAlt' + KnockDownStrength=0 + FractureMeshRadius=200.0 + FracturePartVel=500.0 + ExplosionEffects=KFImpactEffectInfo'WEP_HRG_Crossboom_ARCH.Wep_ALTFIRE_HRG_Crossboom_Explosion' + ExplosionSound=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_Impact_Explosion_Alt_Fire' + + // 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 + + ExplosionActorClass=class'KFExplosionActor' + + //PinBoneIdx=INDEX_None + bCanStick=true + bCanPin=false + Begin Object Class=KFProjectileStickHelper_HRG_Crossboom Name=StickHelper0 + bAltFire=true + End Object + StickHelper=StickHelper0 + + SecondsBeforeDetonation=0.5f + bIsProjActive=true + bCanDisintegrate=true + bAlwaysReplicateExplosion=true +} diff --git a/KFGameContent/Classes/KFProj_Bullet_HRG_CranialPopper.uc b/KFGameContent/Classes/KFProj_Bullet_HRG_CranialPopper.uc new file mode 100644 index 0000000..93ddc94 --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_HRG_CranialPopper.uc @@ -0,0 +1,23 @@ +//============================================================================= +// KFProj_Bullet_HRG_CranialPopper +//============================================================================= +// Bullet class for the HRG CranialPopper +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_HRG_CranialPopper extends KFProj_Bullet + hidedropdown; + +defaultproperties +{ + MaxSpeed=30000.0 + Speed=30000.0 + + DamageRadius=0 + + ProjFlightTemplate=ParticleSystem'WEP_HRG_CranialPopper_EMIT.FX_CranialPopper_Projectile' + ImpactEffects=KFImpactEffectInfo'WEP_HRG_CranialPopper_ARCH.Bleeder_bullet_impact' +} + diff --git a/KFGameContent/Classes/KFProj_Grenade_HRG_CranialPopper.uc b/KFGameContent/Classes/KFProj_Grenade_HRG_CranialPopper.uc new file mode 100644 index 0000000..fff0862 --- /dev/null +++ b/KFGameContent/Classes/KFProj_Grenade_HRG_CranialPopper.uc @@ -0,0 +1,221 @@ +//============================================================================= +// KFProj_Grenade_CranialPopper +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2020 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Grenade_HRG_CranialPopper extends KFProj_BallisticExplosive + hidedropdown; + +/* Ensure it detonates */ +var float WaveDuration; +var float WaveRadius; +var float WaveRadiusMax; +var float WaveTime; +var protected transient bool bWaveActive; +var array VictimsList; +var vector WaveImpactMomentum; + +var GameExplosion VFXExplosionTemplate; + +state WaveState +{ + simulated event BeginState(Name PrevStateName) + { + super.BeginState(PrevStateName); + + bWaveActive = true; + + if (Role == ROLE_Authority) + { + SetTimer(WaveDuration, false, 'Timer_EndWave'); + } + } + + simulated event Tick(float DeltaTime) + { + local KFPawn_Monster Victim; + local TraceHitInfo HitInfo; + local float Radius; + local float DamageHead; + + if(bWaveActive) + { + WaveTime += DeltaTime; + Radius = Lerp(WaveRadius, WaveRadiusMax, WaveTime / WaveDuration); + // DrawDebugSphere(Location, Radius, 100, 255, 255, 0, false); + foreach CollidingActors(class'KFPawn_Monster', Victim, Radius, Location, true,, HitInfo) + { + if (Victim != none && Victim.CollisionComponent != none && VictimsList.Find(Victim) == INDEX_NONE) + { + VictimsList[VictimsList.Length] = Victim; + DamageHead = Victim.AfflictionHandler.GetBigHeadAfflictionDamageModifier(); + if(DamageHead > 0) + { + //`Log("Take: "$Victim); + //HitInfo.BoneName = 'head'; + Victim.TakeDamage(DamageHead * UpgradeDamageMod, InstigatorController, Victim.Location, Normal(Victim.Location - Instigator.Location), MyDamageType, HitInfo, (Owner != None) ? Owner : self); + //Monster.PlayDismemberment(0, MyDamageType, WaveImpactMomentum); + + if (Victim.Health <= 0) + { + MakeHeadExplode(Victim); + } + } + } + } + } + } +} + +/** + * Set the initial velocity and cook time + */ +function Init(vector Direction) +{ + Super.Init(Direction); + + if ( Instigator.Role < ROLE_Authority ) + { + return; + } + + GotoState('WaveState'); + TriggerVFXExplosion(); +} + +simulated function TriggerVFXExplosion() +{ + local KFExplosionActorReplicated ExploActor; + local float ModifiedDamage, OriginalDamage, OriginalDamageRadius; + + if (VFXExplosionTemplate != none) + { + // explode using the given template + ExploActor = Spawn(class'KFExplosionActorReplicated', self,, Location, Rotation,, true); + if (ExploActor != None) + { + ExploActor.InstigatorController = Instigator.Controller; + ExploActor.Instigator = Instigator; + + // enable muzzle location sync + ExploActor.bReplicateInstigator = true; + ExploActor.bSyncParticlesToMuzzle = true; + + ModifiedDamage = 0; + + OriginalDamage = VFXExplosionTemplate.Damage; + OriginalDamageRadius = VFXExplosionTemplate.DamageRadius; + + VFXExplosionTemplate.Damage *= ModifiedDamage; + VFXExplosionTemplate.DamageRadius *= ModifiedDamage; + + ExploActor.Explode(VFXExplosionTemplate); + + VFXExplosionTemplate.Damage = OriginalDamage; + VFXExplosionTemplate.DamageRadius = OriginalDamageRadius; + } + } +} + +simulated function Explode(vector HitLocation, vector HitNormal) +{ +} + +simulated function Timer_EndWave() +{ + bWaveActive = false; + DeferredDestroy(0.15); +} + +simulated function MakeHeadExplode(KFPawn_Monster Victim) +{ + local KFPlayerController KFPC; + + if (Victim == none) return; + + // Notify all Clients to explode the monster head. + foreach WorldInfo.AllControllers( class'KFPlayerController', KFPC ) + { + if (KFPC != none) + { + KFPC.ForceMonsterHeadExplode(Victim); + } + } +} + +defaultproperties +{ + TouchTimeThreshhold = 60.0f + Physics=PHYS_Falling + Speed=0 + MaxSpeed=0 + TerminalVelocity=0 + GravityScale=0.0 + LifeSpan=0.f + bWarnAIWhenFired=true + + ProjFlightTemplate=none + ProjFlightTemplateZedTime=none + ProjDisintegrateTemplate=none + + bIsTimedExplosive=false; + + WaveImpactMomentum=(X=0,Y=0,Z=0) + + WaveTime=0.0f + WaveDuration=2.0f + WaveRadius=10 + WaveRadiusMax=15000 // 750 + bWaveActive=false + + // Grenade explosion light + Begin Object Class=PointLightComponent Name=ExplosionPointLight + LightColor=(R=252,G=218,B=171,A=255) + Brightness=0.5f + Radius=400.f + FalloffExponent=10.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=False + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE) + End Object + + // explosion + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=0 + DamageRadius=2.0f + DamageFalloffExponent=1.f + DamageDelay=0.f + MomentumTransferScale=0 + + // Damage Effects + MyDamageType=Class'KFDT_Blast_HRG_CranialPopper' + KnockDownStrength=0 + FractureMeshRadius=0.0 + FracturePartVel=0.0 + + ExplosionSound=AkEvent'WW_WEP_HRG_CranialPopper.Play_WEP_HRG_CranialPopper_AltFire_3P' + //ExplosionEffects=KFImpactEffectInfo'WEP_HRG_Boomy_ARCH.WEB_HRG_Boomy_Impacts' + ParticleEmitterTemplate=ParticleSystem'WEP_HRG_CranialPopper_EMIT.PS_Expansion_Wave' + + // Dynamic Light + ExploLight=ExplosionPointLight + ExploLightStartFadeOutTime=0.0 + ExploLightFadeOutTime=0.3 + + bIgnoreInstigator=true + + // Camera Shake + CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' + CamShakeInnerRadius=0 + CamShakeOuterRadius=300 + CamShakeFalloff=1.5f + bOrientCameraShakeTowardsEpicenter=true + End Object + + VFXExplosionTemplate=ExploTemplate0 +} diff --git a/KFGameContent/Classes/KFProj_Rocket_SealSqueal.uc b/KFGameContent/Classes/KFProj_Rocket_SealSqueal.uc index 430f2a1..9959d17 100644 --- a/KFGameContent/Classes/KFProj_Rocket_SealSqueal.uc +++ b/KFGameContent/Classes/KFProj_Rocket_SealSqueal.uc @@ -163,6 +163,25 @@ simulated protected function PrepareExplosionTemplate() super.PrepareExplosionTemplate(); } +simulated protected function SetExplosionActorClass() +{ + local KFPlayerReplicationInfo InstigatorPRI; + + if( WorldInfo.TimeDilation < 1.f && Instigator != none ) + { + InstigatorPRI = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo); + if( InstigatorPRI != none ) + { + if( InstigatorPRI.bNukeActive && class'KFPerk_Demolitionist'.static.ProjectileShouldNuke( self ) ) + { + ExplosionActorClass = class'KFPerk_Demolitionist'.static.GetNukeExplosionActorClass(); + } + } + } + + super.SetExplosionActorClass(); +} + simulated function SyncOriginalLocation() { // IMPORTANT NOTE: We aren't actually syncing to the original location (or calling the super). diff --git a/KFGameContent/Classes/KFProj_ShrinkRay.uc b/KFGameContent/Classes/KFProj_ShrinkRay.uc new file mode 100644 index 0000000..3908483 --- /dev/null +++ b/KFGameContent/Classes/KFProj_ShrinkRay.uc @@ -0,0 +1,62 @@ +//============================================================================= +// KFProj_ShrinkRay +//============================================================================= +// Projectile class for ground fire from Shrink ray, etc +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2015 Tripwire Interactive LLC +// - John "Ramm-Jaeger" Gibson +//============================================================================= + +class KFProj_ShrinkRay extends KFProj_GroundFire; + +defaultproperties +{ + bWarnAIWhenFired=true + + // pointlight at far end of spray + Begin Object Name=FlamePointLight + LightColor=(R=13,G=193,B=42,A=255) + Brightness=0.25f + Radius=500.f + FalloffExponent=10.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=TRUE + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE) + End Object + + // explosion + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=0 + DamageRadius=0.0 + DamageFalloffExponent=1.f + DamageDelay=0.f + MyDamageType=none + // Don't burn the guy with the flamethrower + bIgnoreInstigator=true + + // Dynamic Light + ExploLight=FlamePointLight + ExploLightStartFadeOutTime=0.3 + ExploLightFadeOutTime=0.1 + ExploLightFlickerIntensity=1.f + ExploLightFlickerInterpSpeed=50.f + + // Damage Effects + KnockDownStrength=0 + KnockDownRadius=0 + FractureMeshRadius=0 + FracturePartVel=0 + ExplosionEffects=KFImpactEffectInfo'WEP_ShrinkRay_Gun_ARCH.Beam_Impacts' + ExplosionSound=none + MomentumTransferScale=1 + bAllowPerMaterialFX=false + + // Camera Shake + CamShake=none + End Object + ExplosionTemplate=ExploTemplate0 +} diff --git a/KFGameContent/Classes/KFProjectileStickHelper_HRG_Crossboom.uc b/KFGameContent/Classes/KFProjectileStickHelper_HRG_Crossboom.uc new file mode 100644 index 0000000..4dc9267 --- /dev/null +++ b/KFGameContent/Classes/KFProjectileStickHelper_HRG_Crossboom.uc @@ -0,0 +1,296 @@ +//============================================================================= +// KFProjectileStickHelper_HRG_Crossboom +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProjectileStickHelper_HRG_Crossboom extends KFProjectileStickHelper; + +var() float MinDistanceFromPlayer; +var() float DampingFactor; +var() bool bAltFire; + +simulated function TryStick(vector HitNormal, optional vector HitLocation, optional Actor HitActor) +{ + local TraceHitInfo HitInfo; +// local KFProj_Bolt_HRG_Crossboom ProjOwner; +// ProjOwner = KFProj_Bolt_HRG_Crossboom(Outer); + + if (Instigator == None || !Instigator.IsLocallyControlled() || (Physics == PHYS_None && StuckToActor != none)) + { + return; + } + + if (HitActor != none && (HitActor == StuckToActor || HitActor == PinPawn)) + { + return; + } + + GetImpactInfo(Velocity, HitLocation, HitNormal, HitInfo); + + if (HitInfo.HitComponent != none && GetImpactResult(HitActor, HitInfo.HitComponent)) + { + + Stick(HitActor, HitLocation, HitNormal, HitInfo); + +// Bounce if distance is not met? +/* + if (VSizeSq(HitLocation - Instigator.Location) >= MinDistanceFromPlayer * MinDistanceFromPlayer) + { + Stick(HitActor, HitLocation, HitNormal, HitInfo); + } + else + { + // Bounce + ProjOwner.Velocity = DampingFactor * (ProjOwner.Velocity - 2.0*HitNormal*(ProjOwner.Velocity dot HitNormal)); + ProjOwner.GravityScale = 1.0f; + + if( WorldInfo.NetMode != NM_DedicatedServer ) + { + // do the impact effects + `ImpactEffectManager.PlayImpactEffects(HitLocation, Instigator, HitNormal, ProjOwner.ImpactEffects, true ); + } + + ProjOwner.NotifyBounce(); + } +*/ + + if( WorldInfo.NetMode != NM_DedicatedServer ) + { + if (bAltFire) + { + `ImpactEffectManager.PlayImpactEffects(HitLocation, Instigator,, KFProj_Bolt_HRG_CrossboomAlt(Outer).ImpactEffects); + } + else + { + `ImpactEffectManager.PlayImpactEffects(HitLocation, Instigator,, KFProj_Bolt_HRG_Crossboom(Outer).ImpactEffects); + } + } + } +} + +/** Stops movement of projectile and calculates orientation to surface */ +simulated function Stick(Actor HitActor, vector HitLocation, vector HitNormal, const out TraceHitInfo HitInfo) +{ + local int BoneIdx; + + local KFPawn_Monster HitMonster; + local array HitZoneImpactList; + local vector StartTrace, EndTrace, Direction, ClosestBoneLocation; + local name BoneName; + + BoneName = HitInfo.BoneName; + + HitMonster = KFPawn_Monster(HitActor); + if (HitMonster != none) + { + // get injury hit zone + StartTrace = HitLocation; + Direction = Normal(Velocity); + EndTrace = StartTrace + Direction * (HitMonster.CylinderComponent.CollisionRadius * 6.0); + TraceProjHitZones(HitMonster, EndTrace, StartTrace, HitZoneImpactList); + + if (BoneName == '') + { + // get the best bone to attach to + ClosestBoneLocation = HitMonster.Mesh.GetClosestCollidingBoneLocation(HitLocation, true, false); + BoneName = HitMonster.Mesh.FindClosestBone(ClosestBoneLocation, ClosestBoneLocation); + } + + // do impact damage + if (KFWeapon(Owner) != none) + { + HitZoneImpactList[0].RayDir = Normal(EndTrace - StartTrace); // add a raydir here since TraceProjHitZones doesn't fill this out (so certain afflictions apply) + KFWeapon(Owner).HandleProjectileImpact(WeaponFireMode, HitZoneImpactList[0], PenetrationPower); + } + } + else if ( HitActor.bCanBeDamaged ) // Break collectibles and damage destructibles + { + if (Role != ROLE_Authority) + { + if (KFProj_Bolt_HRG_Crossboom(Outer) != none) + { + KFProj_Bolt_HRG_Crossboom(Outer).OnCollectibleHit(HitActor); + } + else if (KFProj_Bolt_HRG_CrossboomAlt(Outer) != none) + { + KFProj_Bolt_HRG_CrossboomAlt(Outer).OnCollectibleHit(HitActor); + } + } + else + { + HitActor.TakeDamage(Damage, InstigatorController, Location, MomentumTransfer * Normal(Velocity), MyDamageType, HitInfo, Owner); + } + } + + if (!IsZero(HitLocation)) + { + SetLocation(HitLocation); + } + + SetStickOrientation(HitNormal); + + BoneIdx = INDEX_NONE; + + if (BoneName != '') + { + BoneIdx = GetBoneIndexFromActor(HitActor, BoneName); + } + + StickToActor(HitActor, HitInfo.HitComponent, BoneIdx, true); + + if (Role < ROLE_Authority) + { + Outer.ServerStick(HitActor, BoneIdx, StuckToLocation, StuckToRotation); + } + + if (WorldInfo.NetMode != NM_DedicatedServer && StickAkEvent != none) + { + PlaySoundBase(StickAkEvent); + } +} + +/** Changes the base of the charge to the stick actor and sets its relative loc/rot */ +simulated function StickToActor(Actor StickTo, PrimitiveComponent HitComp, int BoneIdx, optional bool bCalculateRelativeLocRot) +{ + local SkeletalMeshComponent SkelMeshComp; + local Name BoneName; + + local vector RelStuckToLocation; + local rotator RelStuckToRotation; + + local KFPawn StickToPawn; + + StickToPawn = KFPawn(StickTo); + + if (bCanPin && (StickToPawn == none || StickToPawn.bCanBePinned)) + { + // if StickTo pawn is dead, pin it and keep flying + if (Role == ROLE_Authority) + { + if (StickToPawn != none && !StickToPawn.IsAliveAndWell()) + { + if (PinPawn == none) + { + Pin(StickTo, BoneIdx); + } + + return; + } + } + + if (WorldInfo.NetMode != NM_DedicatedServer && PinPawn != none) + { + if (StickToPawn == none) + { + // Pin pinned pawn to StickTo actor + //PinPawn.Mesh.RetardRBLinearVelocity(vector(Rotation), 0.75); + PinPawn.Mesh.SetRBPosition(Location, PinBoneName); + + PinConstraint = Spawn(class'RB_ConstraintActorSpawnable',,,Location); + PinConstraint.InitConstraint(PinPawn, none, PinBoneName, ''); + } + + PinPawn = none; + } + } + + SetPhysics(PHYS_None); + + PrevStuckToActor = StuckToActor; + StuckToActor = StickTo; + StuckToBoneIdx = BoneIdx; + + // if we found a skel mesh, set our base to it and set relative loc/rot + if (BoneIdx != INDEX_NONE) + { + SkelMeshComp = SkeletalMeshComponent(HitComp); + + BoneName = SkelMeshComp.GetBoneName(BoneIdx); + + if (bCalculateRelativeLocRot) + { + StuckToLocation = Location; + StuckToRotation = Rotation; + } + + SkelMeshComp.TransformToBoneSpace(BoneName, StuckToLocation, StuckToRotation, RelStuckToLocation, RelStuckToRotation); + + SetBase(StickTo,, SkelMeshComp, BoneName); + SetRelativeLocation(RelStuckToLocation); + SetRelativeRotation(RelStuckToRotation); + + } + // otherwise, just set our base + else + { + if (bCalculateRelativeLocRot) + { + // set replicated loc/rot + StuckToLocation = Location; + StuckToRotation = Rotation; + } + else + { + // set loc/rot to replicated loc/rot + SetLocation(StuckToLocation); + SetRotation(StuckToRotation); + } + + SetBase(StickTo); + } + + if (bAltFire) + { + KFProj_Bolt_HRG_CrossboomAlt(Outer).NotifyStick(); + } + else + { + KFProj_Bolt_HRG_Crossboom(Outer).NotifyStick(); + } +} + +simulated function UnStick() +{ + PrevStuckToActor = StuckToActor; + + StuckToActor = none; + StuckToBoneIdx = INDEX_NONE; + + StuckToLocation = vect(0,0,0); + StuckToRotation = rot(0,0,0); + if (!PrevStuckToActor.bTearOff) + { + SetBase(none); + SetPhysics(default.Physics); + } +} + +/** Returns appropriate interaction with HitActor (stick or ignore, for now. add bounce later?) */ +simulated function bool GetImpactResult(Actor HitActor, PrimitiveComponent HitComp) +{ + local StaticMeshComponent StaticMeshComp; + + StaticMeshComp = StaticMeshComponent(HitComp); + if (StaticMeshComp != none) + { + return true; + } + + if (super.GetImpactResult(HitActor, HitComp)) + { + return true; + } + + // Check if it is water + return (InterpActor(HitActor) != none && InterpActor(HitActor).bDestroyProjectilesOnEncroach); +} + +defaultproperties +{ + MinDistanceFromPlayer=1500.0f + DampingFactor=0.02 + bAltFire=false +} diff --git a/KFGameContent/Classes/KFSeasonalEventStats_Summer2022.uc b/KFGameContent/Classes/KFSeasonalEventStats_Summer2022.uc new file mode 100644 index 0000000..33a1428 --- /dev/null +++ b/KFGameContent/Classes/KFSeasonalEventStats_Summer2022.uc @@ -0,0 +1,152 @@ +//============================================================================= +// KFSeasonalEventStats_Summer2022 +//============================================================================= +// Tracks event-specific challenges/accomplishments for Summer 2022 +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= +class KFSeasonalEventStats_Summer2022 extends KFSeasonalEventStats; + +var transient private const int ZedsKillRequired; +var transient private const int WavesRequired; +var transient private const int ZedsThrowSeaRequired; +var transient private const int EndlessWaveRequired; + +var transient int LastWaveFinished; + +private event Initialize(string MapName) +{ + local string CapsMapName; + CapsMapName = Caps(MapName); + + bObjectiveIsValidForMap[0] = 1; // Kill 1500 Zeds on any map or mode + bObjectiveIsValidForMap[1] = 0; // Complete the Weekly on Rig + bObjectiveIsValidForMap[2] = 0; // Complete 100 waves on Rig + bObjectiveIsValidForMap[3] = 0; // Throw 50 Zeds to the sea on Rig + bObjectiveIsValidForMap[4] = 0; // Complete wave 15 on Endless Hard or higher difficulty on Rig + + if (CapsMapName == "KF-RIG") + { + bObjectiveIsValidForMap[1] = 1; + bObjectiveIsValidForMap[2] = 1; + bObjectiveIsValidForMap[3] = 1; + bObjectiveIsValidForMap[4] = 1; + } + + SetSeasonalEventStatsMax(ZedsKillRequired, 0, WavesRequired, ZedsThrowSeaRequired, EndlessWaveRequired); +} + +private event GrantEventItems() +{ + if (Outer.IsEventObjectiveComplete(0) && + Outer.IsEventObjectiveComplete(1) && + Outer.IsEventObjectiveComplete(2) && + Outer.IsEventObjectiveComplete(3) && + Outer.IsEventObjectiveComplete(4)) + { + GrantEventItem(9334); + } +} + +simulated event OnGameWon(class GameClass, int Difficulty, int GameLength, bool bCoOp) +{ + // Rig weekly + if (bObjectiveIsValidForMap[1] != 0) + { + if (GameClass == class'KFGameInfo_WeeklySurvival') + { + FinishedObjective(SEI_Summer, 1); + } + } +} + +simulated function OnZedKilled(class MonsterClass, int Difficulty, class DT) +{ + local int ObjIdx; + + // Kill 1500 Zeds on any map or mode + ObjIdx = 0; + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + IncrementSeasonalEventStat(ObjIdx, 1); + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= ZedsKillRequired) + { + FinishedObjective(SEI_SUMMER, ObjIdx); + } + } +} + +simulated function OnTriggerUsed(class TriggerClass) +{ + local int ObjIdx; + + // Throw 50 Zeds to the sea on Rig + ObjIdx = 3; + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (TriggerClass == class'KFSeaTrigger') + { + IncrementSeasonalEventStat(ObjIdx, 1); + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= ZedsThrowSeaRequired) + { + FinishedObjective(SEI_SUMMER, ObjIdx); + } + } + } +} + +simulated event OnWaveCompleted(class GameClass, int Difficulty, int WaveNum) +{ + local int ObjIdx; + local bool canIncreaseWave; + + canIncreaseWave = false; + + // Complete 100 waves on Rig + ObjIdx = 2; + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (GameClass == class'KFGameInfo_Endless') + { + if (LastWaveFinished != WaveNum) + { + canIncreaseWave = true; + LastWaveFinished = WaveNum; + } + } + else + { + canIncreaseWave = true; + } + + if (canIncreaseWave) + { + IncrementSeasonalEventStat(ObjIdx, 1); + } + + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= WavesRequired) + { + FinishedObjective(SEI_SUMMER, ObjIdx); + } + } + + // Complete wave 15 on Endless Hard or higher difficulty on Rig + ObjIdx = 4; + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (WaveNum >= EndlessWaveRequired && GameClass == class'KFGameInfo_Endless' && Difficulty >= `DIFFICULTY_HARD) + { + FinishedObjective(SEI_Summer, ObjIdx); + } + } +} + +defaultproperties +{ + ZedsKillRequired=1500 + WavesRequired=100 + ZedsThrowSeaRequired=50 + EndlessWaveRequired=15 + LastWaveFinished=-1 +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapActor_CranialPopper_Tube.uc b/KFGameContent/Classes/KFWeapActor_CranialPopper_Tube.uc new file mode 100644 index 0000000..9d46790 --- /dev/null +++ b/KFGameContent/Classes/KFWeapActor_CranialPopper_Tube.uc @@ -0,0 +1,72 @@ +//============================================================================= +// KFWeapActor_CranialPopper_Tube +//============================================================================= +// Attach class to handle visual burrowing of a tube into the hit zed +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2017 Tripwire Interactive LLC +//============================================================================= + +class KFWeapActor_CranialPopper_Tube extends Actor; + +//Visual tube mesh that embeds into the enemy +var() StaticMeshComponent TubeMesh; + +//Lifetime until destroy is called +var float Lifetime; + +//Size in the X direction - @TODO - get this at runtime +var float TubeLength; + +//Destroy Delay +var float DestroyDelay; +var bool bStopTick; + +event PostBeginPlay() +{ + super.PostBeginPlay(); + + Lifetime = class'KFDT_Bleeding_Hemogoblin'.default.DoT_Duration; + SetTimer(Lifetime, false, nameof(TearDown)); +} + +function TearDown() +{ + bStopTick = true; + + //Because Mark wants his sound things to fade + SetTimer(DestroyDelay, false, nameof(ActualDestroy)); +} + + +function ActualDestroy() +{ + Destroy(); +} + +event Tick(float DeltaTime) +{ + local Vector NewRelativeLocation; + super.Tick(DeltaTime); + + if (!bStopTick) + { + //Push the actor into the body based on relative rotation + NewRelativeLocation = RelativeLocation + (Vector(RelativeRotation) * (TubeLength / Lifetime) * DeltaTime); + SetRelativeLocation(NewRelativeLocation); + } +} + +defaultproperties +{ + Begin Object Class=StaticMeshComponent Name=Mesh0 + StaticMesh=StaticMesh'FX_Projectile_MESH.FX_Projectile_Leech' + End Object + Components.Add(Mesh0) + TubeMesh=Mesh0 + + TubeLength=10 + + bStopTick=false + DestroyDelay=0.5 +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_AutoTurret.uc b/KFGameContent/Classes/KFWeapAttach_AutoTurret.uc new file mode 100644 index 0000000..fdc4488 --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_AutoTurret.uc @@ -0,0 +1,113 @@ +//============================================================================= +// KFWeapAttach_AutoTurret +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapAttach_AutoTurret extends KFWeapAttach_DualBase; + +`define AUTOTURRET_ATTACH_MIC_LED_INDEX 2 + +const ThrowAnim = 'Drone_Throw'; +const CrouchThrowAnim = 'Drone_Throw_CH'; + +const DetonateAnim = 'Shoot'; +const CrouchDetonateAnim = 'CH_Shoot'; + +const TransitionParamName = 'transition_full_to_empty'; +const EmptyParamName = 'Blinking_0_off___1_on'; + +/** Completely overridden to play anims for both C4 firemodes (throw and detonate), also doesn't need to play effects */ +simulated function bool ThirdPersonFireEffects( vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte ) +{ + local float Duration; + + // Effects below this point are culled based on visibility and distance + if ( !ActorEffectIsRelevant(P, false, MaxFireEffectDistance) ) + { + return false; + } + + DecodeThirdPersonAnimRate( ThirdPersonAnimRateByte ); + + // Weapon shoot anims + if (P.FiringMode == 0) + { + // anim simply hides and unhides bone + Duration = WeapMesh.GetAnimLength( ThrowAnim ); + WeapMesh.PlayAnim( ThrowAnim, Duration / ThirdPersonAnimRate,, true ); + + // use timer to make sure bone gets un-hidden (in case anim gets interrupted) + SetTimer( 0.75f, false, nameof(UnHide) ); + } + else if (P.FiringMode == 5) + { + Duration = WeapMesh.GetAnimLength( DetonateAnim ); + LeftWeapMesh.PlayAnim( DetonateAnim, Duration / ThirdPersonAnimRate,, true ); + } + + // Additive character shoot anims + if ( !P.IsDoingSpecialMove() ) + { + if( P.FiringMode == 0 ) + { + if ( P.bIsCrouched ) + { + P.PlayBodyAnim(CrouchThrowAnim, EAS_CH_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + else + { + P.PlayBodyAnim(ThrowAnim, EAS_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + } + else if( P.FiringMode == 5 ) + { + if ( P.bIsCrouched ) + { + P.PlayBodyAnim(CrouchDetonateAnim, EAS_CH_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + else + { + P.PlayBodyAnim(DetonateAnim, EAS_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime); + } + } + } + + // prevent using "aiming" KFAnim_BlendByTargetingMode since we don't have/need the aim anims for C4 + P.LastWeaponFireTime = -1.f; + + return true; +} + +/** Unhides the C4 unit in hand (basically the same as the notify, but don't use the notify) */ +simulated function UnHide() +{ + if( WeapMesh != none ) + { + WeapMesh.UnHideBoneByName( 'RW_Weapon' ); + } +} + +/** Special event added for weap attachments. Free for use */ +function OnSpecialEvent(int Arg) +{ + local float Value; + Value = (Arg - 1) / 100.0f; + + if (Value >= 0) + { + if ( WeaponMIC == None && LeftWeapMesh != None ) + { + WeaponMIC = LeftWeapMesh.CreateAndSetMaterialInstanceConstant(`AUTOTURRET_ATTACH_MIC_LED_INDEX); + } + + WeaponMIC.SetScalarParameterValue(TransitionParamName, 1.0f - Value); + WeaponMIC.SetScalarParameterValue(EmptyParamName, Value == 0 ? 1 : 0); + } +} + +defaultproperties +{ + bHasLaserSight=false +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_AutoTurretWeap.uc b/KFGameContent/Classes/KFWeapAttach_AutoTurretWeap.uc new file mode 100644 index 0000000..40770d2 --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_AutoTurretWeap.uc @@ -0,0 +1,270 @@ +//============================================================================= +// KFWeapAttach_AutoTurretWeapon +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapAttach_AutoTurretWeap extends KFWeaponAttachment; + +const DeployAnimName = 'Drone_Deploy'; +const DroneFireAnim = 'Drone_Shoot'; +const DroneEmptyStartAnim = 'Drone_Start_Empty'; +const DroneEmptyAnim = 'Drone_Empty'; +const DroneIdleAnim = 'Drone_Idle'; +const DroneClosedAnim = 'Drone_IdleClose'; + +const LaserSightSocketName = 'LaserSightSocket'; +const LaserColorParamName = '0blue_1red'; + +var transient MaterialInstanceConstant LaserDotMIC; +var transient MaterialInstanceConstant LaserBeamMIC; + +simulated function float PlayDeployAnim() +{ + local float Duration; + + Duration = WeapMesh.GetAnimLength( DeployAnimName ); + WeapMesh.PlayAnim( DeployAnimName, Duration / ThirdPersonAnimRate, false, true ); + + return Duration; +} + +simulated function PlayEmptyState() +{ + local float Duration; + + ClearTimer(nameof(PlayIdleAnim)); + + Duration = WeapMesh.GetAnimLength( DroneEmptyStartAnim ); + WeapMesh.PlayAnim( DroneEmptyStartAnim, Duration / ThirdPersonAnimRate, true, false); + + SetTimer(Duration, false, 'PlayEmptyAnim'); +} + +simulated function PlayEmptyAnim() +{ + local float Duration; + + Duration = WeapMesh.GetAnimLength( DroneEmptyAnim ); + WeapMesh.PlayAnim( DroneEmptyAnim, Duration / ThirdPersonAnimRate, true, false); + + if (LaserSight != none) + { + LaserSight.ChangeVisibility(false); + } +} + +simulated function PlayIdleAnim() +{ + local float Duration; + + Duration = WeapMesh.GetAnimLength( DroneIdleAnim ); + WeapMesh.PlayAnim( DroneIdleAnim, Duration / ThirdPersonAnimRate, true, false ); +} + +simulated function PlayCloseAnim() +{ + local float Duration; + + Duration = WeapMesh.GetAnimLength( DroneClosedAnim ); + WeapMesh.PlayAnim( DroneClosedAnim, Duration / ThirdPersonAnimRate, true, false ); +} + +/** + * Spawn all of the effects that will be seen in behindview/remote clients. This + * function is called from the pawn, and should only be called when on a remote client or + * if the local client is in a 3rd person mode. + * @return TRUE if the effect culling check passes +*/ +simulated function bool ThirdPersonFireEffects( vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte ) +{ + // local EAnimSlotStance AnimType; + + SpawnTracer(GetMuzzleLocation(), HitLocation); + + // Effects below this point are culled based on visibility and distance + if ( !ActorIsRelevant(self, false, MaxFireEffectDistance) ) + { + return false; + } + + DecodeThirdPersonAnimRate( ThirdPersonAnimRateByte ); + + // Weapon shoot anims + if( !bWeapMeshIsPawnMesh ) + { + PlayWeaponFireAnim(); + } + +/* + if( P.IsDoingSpecialMove() && P.SpecialMoves[P.SpecialMove].bAllowFireAnims ) + { + AnimType = EAS_Additive; + } + else + { + AnimType = EAS_FullBody; + } +*/ + // AnimType = EAS_FullBody; + + // Character shoot anims +/* + if ( AnimType == EAS_Additive ) + { + PlayPawnFireAnim( P, AnimType ); + + // interrupt other weapon action anims (e.g. Reload) + if( !P.IsDoingSpecialMove() ) + { + P.StopBodyAnim(P.bIsCrouched ? EAS_CH_UpperBody : EAS_UpperBody, 0.1f); + } + + if ( OnWeaponStateChanged != None ) + { + OnWeaponStateChanged(true); + } + } +*/ + +// Always DEFAULT_FIREMODE + CauseMuzzleFlash(0); + + return true; +} + +simulated function bool ActorIsRelevant(Actor EffectInstigator, bool bForceDedicated, optional float VisibleCullDistance=5000.0, optional float HiddenCullDistance=350.0 ) +{ + local PlayerController P; + local float DistSq; + local vector CameraLoc; + local rotator CameraRot; + + if ( EffectInstigator == None ) + { + return FALSE; + } + + // No local player, so only spawn on dedicated server if bForceDedicated + if ( WorldInfo.NetMode == NM_DedicatedServer ) + { + return bForceDedicated; + } + + if ( bForceDedicated && (WorldInfo.NetMode == NM_ListenServer) && (WorldInfo.Game.NumPlayers > 1) ) + { + // Is acting as server, so spawn effect if bForceDedicated + return TRUE; + } + + // Determine how far to the nearest local viewer + DistSq = 10000000000.0; + ForEach LocalPlayerControllers(class'PlayerController', P) + { + if ( P.GetViewTarget() == self ) + { + return true; + } + P.GetPlayerViewPoint(CameraLoc, CameraRot); + DistSq = FMin(DistSq, VSizeSq(Location - CameraLoc)*Square(P.LODDistanceFactor)); + } + + if ( DistSq > VisibleCullDistance*VisibleCullDistance ) + { + // never spawn beyond cull distance + return FALSE; + } + else if ( DistSq < HiddenCullDistance*HiddenCullDistance ) + { + // If close enough, always spawn even if hidden + return TRUE; + } + +/* This doesn't seem to be updating the render time, so ignore it */ + return TRUE; +} + +/** Plays fire animation on weapon mesh */ +simulated function PlayWeaponFireAnim() +{ + local float Duration; + local bool bAnimPlayed; + + Duration = WeapMesh.GetAnimLength( DroneFireAnim ); + + bAnimPlayed = WeapMesh.PlayAnim( DroneFireAnim, Duration / ThirdPersonAnimRate, false, false ); + + if (bAnimPlayed) + { + ClearTimer(nameof(PlayIdleAnim)); + SetTimer(Duration, false, nameof(PlayIdleAnim)); + } +} + +/** + Laser + */ + +simulated function AttachLaserSight() +{ + if ( WeapMesh != none && LaserSight == None && LaserSightArchetype != None ) + { + LaserSight = new(self) Class'KFLaserSightAttachment' (LaserSightArchetype); + LaserSight.AttachLaserSight(WeapMesh, false, LaserSightSocketName); + } +} + +simulated function UpdateLaserColor(bool bInCombat) +{ + if (LaserSight != none) + { + if (LaserDotMIC == none) + { + LaserDotMIC = LaserSight.LaserDotMeshComp.CreateAndSetMaterialInstanceConstant(0); + } + + if (LaserBeamMIC == none) + { + LaserBeamMIC = LaserSight.LaserBeamMeshComp.CreateAndSetMaterialInstanceConstant(0); + } + } + + if (LaserDotMIC != none) + { + LaserDotMIC.SetScalarParameterValue(LaserColorParamName, bInCombat ? 1 : 0); + } + + if (LaserBeamMIC != none) + { + LaserBeamMIC.SetScalarParameterValue(LaserColorParamName, bInCombat ? 1 : 0); + } +} + +/** + * Assign weapon skin to 3rd person mesh + */ +simulated event SetWeaponSkin(int ItemId, optional bool bFinishedLoading = false) +{ + local array SkinMICs; + + if ( ItemId > 0 && WorldInfo.NetMode != NM_DedicatedServer && !bWaitingForWeaponSkinLoad) + { + if (!bFinishedLoading && StartLoadWeaponSkin(ItemId)) + { + return; + } + + SkinMICs = class'KFWeaponSkinList'.static.GetWeaponSkin(ItemId, WST_ThirdPerson); + if ( SkinMICs.Length > 0 ) + { + WeapMesh.SetMaterial(0, SkinMICs[0]); + } + } +} + +defaultproperties +{ + //defaults + bHasLaserSight=true +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_ShrinkRayGun.uc b/KFGameContent/Classes/KFWeapAttach_ShrinkRayGun.uc new file mode 100644 index 0000000..fcef72f --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_ShrinkRayGun.uc @@ -0,0 +1,56 @@ +//============================================================================= +// KFWeapAttach_ShrinkRayGun +//============================================================================= +// Custom weapon attachment for the third person shrink ray gun +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapAttach_ShrinkRayGun extends KFWeapAttach_SprayBase; + +simulated protected function TurnOffPilot() +{ + // Undo the invert of the pilot, so it does really deactivate + + bInvertPilot = false; + + Super.TurnOffPilot(); + + bInvertPilot = true; +} + +defaultproperties +{ + // Muzzle Flash point light + // want this light to illuminate characters only + Begin Object Class=PointLightComponent Name=PilotPointLight0 + LightColor=(R=0,G=200,B=255,A=255) + Brightness=10.f + FalloffExponent=4.f + Radius=128.f + CastShadows=TRUE + CastStaticShadows=FALSE + CastDynamicShadows=TRUE + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=FALSE) + End Object + + Begin Object Class=PointLightComponent Name=PilotPointLight1 + LightColor=(R=250,G=150,B=85,A=255) + Brightness=3.f + FalloffExponent=8.f + Radius=32.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=TRUE + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=FALSE) + End Object + + PilotLights(0)=(Light=PilotPointLight0,FlickerIntensity=25f,FlickerInterpSpeed=50f,LightAttachBone=MuzzleFlash) + PilotLights(1)=(Light=PilotPointLight1,FlickerIntensity=4.f,FlickerInterpSpeed=50f,LightAttachBone=MuzzleFlash) + + bInvertPilot=true +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc index 4422399..f561c48 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc @@ -676,9 +676,9 @@ defaultproperties InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_FAMAS_Shotgun' InstantHitDamage(ALTFIRE_FIREMODE)=30.0 //25.0 PenetrationPower(ALTFIRE_FIREMODE)=2.0 - FireInterval(ALTFIRE_FIREMODE)=+1.2 //50 RPM + FireInterval(ALTFIRE_FIREMODE)=+0.77 //78 RPM //+1.2 //50 RPM NumPellets(ALTFIRE_FIREMODE)=7 //6 - Spread(ALTFIRE_FIREMODE)=0.10 //0.12 + Spread(ALTFIRE_FIREMODE)=0.08 //0.10 //0.12 SecondaryAmmoTexture=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunSingle' // BASH_FIREMODE diff --git a/KFGameContent/Classes/KFWeap_AutoTurret.uc b/KFGameContent/Classes/KFWeap_AutoTurret.uc new file mode 100644 index 0000000..60f63a0 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_AutoTurret.uc @@ -0,0 +1,665 @@ +//============================================================================= +// KFWeap_AutoTurret +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_AutoTurret extends KFWeap_ThrownBase; + +`define AUTOTURRET_MIC_LED_INDEX 2 + +const DETONATE_FIREMODE = 5; // NEW - IronSights Key + +var(Animations) const editconst name DetonateAnim; +var(Animations) const editconst name DetonateLastAnim; + +/** Sound to play upon successful detonation */ +var() AkEvent DetonateAkEvent; +/** Sound to play upon attempted but unsuccessful detonation */ +var() AkEvent DryFireAkEvent; + +/** Strenght applied to forward dir to get the throwing velocity */ +var const float ThrowStrength; +/** Max num of turrets that can be deployed */ +var const byte MaxTurretsDeployed; +/** Offset to spawn the dron (screen coordinates) */ +var const vector TurretSpawnOffset; + +var transient byte NumDeployedTurrets; +var transient KFPlayerController KFPC; + +/** If the turret is in a state available for throw another to fix some animation issues. */ +var transient bool bTurretReadyToUse; + +var repnotify float CurrentAmmoPercentage; + +const TransitionParamName = 'transition_full_to_empty'; +const EmptyParamName = 'Blinking_0_off___1_on'; + +replication +{ + if( bNetDirty ) + NumDeployedTurrets, CurrentAmmoPercentage, bTurretReadyToUse; +} + +simulated event ReplicatedEvent(name VarName) +{ + if (VarName == nameof(CurrentAmmoPercentage)) + { + UpdateMaterialColor(CurrentAmmoPercentage); + } + else + { + Super.ReplicatedEvent(VarName); + } +} + +simulated event PreBeginPlay() +{ + local class WeaponClass; + + super.PreBeginPlay(); + + WeaponClass = class (DynamicLoadObject(class'KFPawn_AutoTurret'.default.WeaponDefinition.default.WeaponClassPath, class'Class')); + WeaponClass.static.TriggerAsyncContentLoad(WeaponClass); +} + +simulated event PostBeginPlay() +{ + super.PostBeginPlay(); + + if (Role == ROLE_Authority) + { + KFPC = KFPlayerController(Instigator.Controller); + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + } +} + +/** Route ironsight player input to detonate */ +simulated function SetIronSights(bool bNewIronSights) +{ + if ( !Instigator.IsLocallyControlled() ) + { + return; + } + + if ( bNewIronSights ) + { + StartFire(DETONATE_FIREMODE); + } + else + { + StopFire(DETONATE_FIREMODE); + } +} + +/** Overridded to add spawned turret to list of spawned turrets */ +simulated function Projectile ProjectileFire() +{ + local vector SpawnLocation, SpawnDirection; + local KFPawn_AutoTurret SpawnedActor; + + if (Role == ROLE_Authority && CurrentFireMode == DEFAULT_FIREMODE) + { + GetTurretSpawnLocationAndDir(SpawnLocation, SpawnDirection); + SpawnedActor = Spawn(class'KFPawn_AutoTurret', self,, SpawnLocation + (TurretSpawnOffset >> Rotation), Rotation,,true); + + if( SpawnedActor != none ) + { + SpawnedActor.OwnerWeapon = self; + SpawnedActor.SetPhysics(PHYS_Falling); + SpawnedActor.Velocity = SpawnDirection * ThrowStrength; + SpawnedActor.UpdateInstigator(Instigator); + SpawnedActor.UpdateWeaponUpgrade(CurrentWeaponUpgradeIndex); + SpawnedActor.SetTurretState(ETS_Throw); + + KFPC.DeployedTurrets.AddItem( SpawnedActor ); + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + bForceNetUpdate = true; + } + + return none; + } + else + { + return super.ProjectileFire(); + } + + return none; +} + +simulated function GetTurretSpawnLocationAndDir(out vector SpawnLocation, out vector SpawnDirection) +{ + local vector StartTrace, EndTrace, RealStartLoc, AimDir; + local ImpactInfo TestImpact; + local vector DirA, DirB; + local Quat Q; + + // This is where we would start an instant trace. (what CalcWeaponFire uses) + StartTrace = GetSafeStartTraceLocation(); + AimDir = Vector(GetAdjustedAim( StartTrace )); + + // this is the location where the projectile is spawned. + RealStartLoc = GetPhysicalFireStartLoc(AimDir); + + if( StartTrace != RealStartLoc ) + { + // if projectile is spawned at different location of crosshair, + // then simulate an instant trace where crosshair is aiming at, Get hit info. + EndTrace = StartTrace + AimDir * GetTraceRange(); + TestImpact = CalcWeaponFire( StartTrace, EndTrace ); + // Store the original aim direction without correction + DirB = AimDir; + + // Then we realign projectile aim direction to match where the crosshair did hit. + AimDir = Normal(TestImpact.HitLocation - RealStartLoc); + + // Store the desired corrected aim direction + DirA = AimDir; + + // Clamp the maximum aim adjustment for the AimDir so you don't get wierd + // cases where the projectiles velocity is going WAY off of where you + // are aiming. This can happen if you are really close to what you are + // shooting - Ramm + if ( (DirA dot DirB) < MaxAimAdjust_Cos ) + { + Q = QuatFromAxisAndAngle(Normal(DirB cross DirA), MaxAimAdjust_Angle); + AimDir = QuatRotateVector(Q,DirB); + } + } + + SpawnDirection = AimDir; + SpawnLocation = RealStartLoc; +} + +/** Detonates the oldest turret */ +simulated function Detonate() +{ + // auto switch weapon when out of ammo and after detonating the last deployed turret + if( Role == ROLE_Authority ) + { + if( KFPC.DeployedTurrets.Length > 0 ) + { + KFPawn_AutoTurret(KFPC.DeployedTurrets[0]).SetTurretState(ETS_Detonate); + } + + SetReadyToUse(true); + + if( !HasAnyAmmo() && NumDeployedTurrets == 0 ) + { + if( CanSwitchWeapons() ) + { + Instigator.Controller.ClientSwitchToBestWeapon(false); + } + } + } +} + +/** Removes a turret from the list using either an index or an actor and updates NumDeployedTurrets */ +function RemoveDeployedTurret( optional int Index = INDEX_NONE, optional Actor TurretActor ) +{ + if( Index == INDEX_NONE ) + { + if( TurretActor != none ) + { + Index = KFPC.DeployedTurrets.Find( TurretActor ); + } + } + + if( Index != INDEX_NONE ) + { + KFPC.DeployedTurrets.Remove( Index, 1 ); + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + bForceNetUpdate = true; + } +} + +function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon ) +{ + local int i; + + super.SetOriginalValuesFromPickup( PickedUpWeapon ); + + if (PickedUpWeapon.Instigator.Controller != none && PickedUpWeapon.Instigator.Controller != KFPC) + { + KFPC.DeployedTurrets = KFPlayerController(PickedUpWeapon.Instigator.Controller).DeployedTurrets; + } + + NumDeployedTurrets = KFPC.DeployedTurrets.Length; + bForceNetUpdate = true; + + for( i = 0; i < NumDeployedTurrets; ++i ) + { + // charge alerts (beep, light) need current instigator + KFPC.DeployedTurrets[i].Instigator = Instigator; + KFPC.DeployedTurrets[i].SetOwner(self); + + if( Instigator.Controller != none ) + { + KFPawn_AutoTurret(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller; + } + } +} + + /** + * Returns true if this weapon uses a secondary ammo pool + */ +static simulated event bool UsesAmmo() +{ + return true; +} + +simulated function bool HasAmmo( byte FireModeNum, optional int Amount ) +{ + if( FireModeNum == DETONATE_FIREMODE ) + { + return NumDeployedTurrets > 0; + } + + return super.HasAmmo( FireModeNum, Amount ); +} + +simulated function BeginFire( byte FireModeNum ) +{ + // Clear any pending detonate if we pressed the main fire + // That prevents strange holding right click behaviour and sound issues + if (FireModeNum == DEFAULT_FIREMODE) + { + ClearPendingFire(DETONATE_FIREMODE); + } + + if (FireModeNum == DETONATE_FIREMODE && NumDeployedTurrets > 0) + { + if (bTurretReadyToUse) + { + PrepareAndDetonate(); + } + } + else + { + if (FireModeNum == DEFAULT_FIREMODE + && NumDeployedTurrets >= MaxTurretsDeployed + && HasAmmo(THROW_FIREMODE)) + { + if (!bTurretReadyToUse) + { + return; + } + + PrepareAndDetonate(); + } + else if (HasAmmo(THROW_FIREMODE) == false) + { + PlaySoundBase( DryFireAkEvent, true ); + } + + super.BeginFire( FireModeNum ); + } +} + +simulated function PrepareAndDetonate() +{ + local name DetonateAnimName; + local float AnimDuration; + local bool bInSprintState; + + DetonateAnimName = ShouldPlayLastAnims() ? DetonateLastAnim : DetonateAnim; + AnimDuration = MySkelMesh.GetAnimLength( DetonateAnimName ); + bInSprintState = IsInState( 'WeaponSprinting' ); + + if( WorldInfo.NetMode != NM_DedicatedServer ) + { + if( NumDeployedTurrets > 0 ) + { + PlaySoundBase( DetonateAkEvent, true ); + } + else + { + PlaySoundBase( DryFireAkEvent, true ); + } + + if( bInSprintState ) + { + AnimDuration *= 0.25f; + PlayAnimation( DetonateAnimName, AnimDuration ); + } + else + { + PlayAnimation( DetonateAnimName ); + } + } + + if( Role == ROLE_Authority ) + { + Detonate(); + } + + IncrementFlashCount(); + + if( bInSprintState ) + { + SetTimer( AnimDuration * 0.8f, false, nameof(PlaySprintStart) ); + } + else + { + SetTimer( AnimDuration * 0.5f, false, nameof(GotoActiveState) ); + } +} + +// do nothing, as we have no alt fire mode +simulated function AltFireMode(); + +/** Allow weapons with abnormal state transitions to always use zed time resist*/ +simulated function bool HasAlwaysOnZedTimeResist() +{ + return true; +} + +/********************************************************************************************* + * State Active + * A Weapon this is being held by a pawn should be in the active state. In this state, + * a weapon should loop any number of idle animations, as well as check the PendingFire flags + * to see if a shot has been fired. + *********************************************************************************************/ + +simulated state Active +{ + /** Overridden to prevent playing fidget if play has no more ammo */ + simulated function bool CanPlayIdleFidget(optional bool bOnReload) + { + if( !HasAmmo(0) ) + { + return false; + } + + return super.CanPlayIdleFidget( bOnReload ); + } +} + +/********************************************************************************************* + * State WeaponDetonating + * The weapon is in this state while detonating a charge +*********************************************************************************************/ + +simulated function GotoActiveState(); + +simulated state WeaponDetonating +{ + ignores AllowSprinting; + + simulated event BeginState( name PreviousStateName ) + { + PrepareAndDetonate(); + } + + simulated function GotoActiveState() + { + GotoState('Active'); + } +} + +/********************************************************************************************* + * State WeaponThrowing + * Handles throwing weapon (similar to state GrenadeFiring) + *********************************************************************************************/ + +simulated state WeaponThrowing +{ + /** Never refires. Must re-enter this state instead. */ + simulated function bool ShouldRefire() + { + return false; + } + + simulated function EndState(Name NextStateName) + { + local KFPerk InstigatorPerk; + + Super.EndState(NextStateName); + + //Targeted fix for Demolitionist w/ the C4. It should remain in zed time while waiting on + // the fake reload to be triggered. This will return 0 for other perks. + InstigatorPerk = GetPerk(); + if( InstigatorPerk != none ) + { + SetZedTimeResist( InstigatorPerk.GetZedTimeModifier(self) ); + } + } +} + +/********************************************************************************************* + * State WeaponEquipping + * The Weapon is in this state while transitioning from Inactive to Active state. + * Typically, the weapon will remain in this state while its selection animation is being played. + * While in this state, the weapon cannot be fired. +*********************************************************************************************/ + +simulated state WeaponEquipping +{ + simulated event BeginState( name PreviousStateName ) + { + super.BeginState( PreviousStateName ); + + // perform a "reload" if we refilled our ammo from empty while it was unequipped + if( !HasAmmo(THROW_FIREMODE) && HasSpareAmmo() ) + { + PerformArtificialReload(); + } + StopFire(DETONATE_FIREMODE); + } +} + +/********************************************************************************************* + * State WeaponPuttingDown + * Putting down weapon in favor of a new one. + * Weapon is transitioning to the Inactive state. + * + * Detonating while putting down causes C4 not to be put down, which causes problems, so let's + * just ignore SetIronSights, which causes detonation +*********************************************************************************************/ + +simulated state WeaponPuttingDown +{ + ignores SetIronSights; + + simulated event BeginState( name PreviousStateName ) + { + super.BeginState( PreviousStateName ); + StopFire(DETONATE_FIREMODE); + } +} + +/********************************************************************************************* + * @name Trader + *********************************************************************************************/ + +/** Returns trader filter index based on weapon type */ +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Explosive; +} + +function CheckTurretAmmo() +{ + local float Percentage; + local KFWeapon Weapon; + local KFPawn KFP; + + if (Role == Role_Authority) + { + if (KFPC.DeployedTurrets.Length > 0) + { + Weapon = KFWeapon(KFPawn_AutoTurret(KFPC.DeployedTurrets[0]).Weapon); + if (Weapon != none) + { + Percentage = float(Weapon.AmmoCount[0]) / Weapon.MagazineCapacity[0]; + if (Percentage != CurrentAmmoPercentage) + { + CurrentAmmoPercentage = Percentage; + bNetDirty = true; + + if (WorldInfo.NetMode == NM_Standalone) + { + UpdateMaterialColor(CurrentAmmoPercentage); + } + else + { + KFP = KFPawn(Instigator); + KFP.OnWeaponSpecialAction( 1 + (CurrentAmmoPercentage * 100) ); + } + } + } + } + } +} + +function SetReadyToUse(bool bReady) +{ + if (bTurretReadyToUse != bReady) + { + bTurretReadyToUse = bReady; + bNetDirty = true; + } +} + +simulated event Tick(float DeltaTime) +{ + super.Tick(DeltaTime); + + if (Role == Role_Authority) + { + CheckTurretAmmo(); + } +} + +simulated function UpdateMaterialColor(float Percentage) +{ + if (NumDeployedTurrets == 0) + { + WeaponMICs[`AUTOTURRET_MIC_LED_INDEX].SetScalarParameterValue(EmptyParamName, 0); + } + else if (Percentage >= 0) + { + WeaponMICs[`AUTOTURRET_MIC_LED_INDEX].SetScalarParameterValue(TransitionParamName, 1.0f - Percentage); + WeaponMICs[`AUTOTURRET_MIC_LED_INDEX].SetScalarParameterValue(EmptyParamName, Percentage == 0 ? 1 : 0); + } +} + +simulated function SetWeaponUpgradeLevel(int WeaponUpgradeLevel) +{ + local Actor Turret; + local KFPawn_AutoTurret TurretPawn; + + super.SetWeaponUpgradeLevel(WeaponUpgradeLevel); + + if (KFPC != none) + { + foreach KFPC.DeployedTurrets(Turret) + { + TurretPawn = KFPawn_AutoTurret(Turret); + if (TurretPawn != none) + { + TurretPawn.UpdateWeaponUpgrade(WeaponUpgradeLevel); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// Trader +// +/////////////////////////////////////////////////////////////////////////////////////////// + +/** Allows weapon to calculate its own stats for display in trader */ +static simulated event SetTraderWeaponStats( out array WeaponStats ) +{ + super.SetTraderWeaponStats(WeaponStats); + + WeaponStats.Length = 4; + + WeaponStats[0].StatType = TWS_Damage; + WeaponStats[0].StatValue = class'KFWeap_AutoTurretWeapon'.static.CalculateTraderWeaponStatDamage(); + + WeaponStats[1].StatType = TWS_Penetration; + WeaponStats[1].StatValue = class'KFWeap_AutoTurretWeapon'.default.PenetrationPower[DEFAULT_FIREMODE]; + + WeaponStats[2].StatType = TWS_Range; + // This is now set in native since EffectiveRange has been moved to KFWeaponDefinition + // WeaponStats[2].StatValue = CalculateTraderWeaponStatRange(); + + WeaponStats[3].StatType = TWS_RateOfFire; + WeaponStats[3].StatValue = class'KFWeap_AutoTurretWeapon'.static.CalculateTraderWeaponStatFireRate(); +} + +defaultproperties +{ + // start in detonate mode so that an attempt to detonate before any charges are thrown results in + // the proper third-person anim + CurrentFireMode=DETONATE_FIREMODE + + // Zooming/Position + PlayerViewOffset=(X=6.0,Y=2,Z=-4) + FireOffset=(X=0,Y=0) + TurretSpawnOffset=(X=0, Y=15, Z=-50) + + // Content + PackageKey="AutoTurret" + FirstPersonMeshName="Wep_1P_AutoTurret_MESH.Wep_1stP_AutoTurret_Rig" + FirstPersonAnimSetNames(0)="Wep_1P_AutoTurret_ANIM.Wep_1P_AutoTurret_ANIM" + PickupMeshName="wep_3p_autoturret_mesh.Wep_AutoTurret_Pickup" + AttachmentArchetypeName="WEP_AutoTurret_ARCH.Wep_AutoTurret_3P" + + // Anim + FireAnim=C4_Throw + FireLastAnim=C4_Throw_Last + + DetonateAnim=Detonate + DetonateLastAnim=Detonate_Last + + // Ammo + MagazineCapacity[0]=1 + SpareAmmoCapacity[0]=3 + InitialSpareMags[0]=1 + AmmoPickupScale[0]=1.0 + + // THROW_FIREMODE + FireInterval(THROW_FIREMODE)=0.25 + FireModeIconPaths(THROW_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Drone' + + // DETONATE_FIREMODE + FiringStatesArray(DETONATE_FIREMODE)=WeaponDetonating + WeaponFireTypes(DETONATE_FIREMODE)=EWFT_Custom + AmmoCost(DETONATE_FIREMODE)=0 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_AutoTurret' + InstantHitDamage(BASH_FIREMODE)=26 + + // Inventory / Grouping + InventoryGroup=IG_Equipment + GroupPriority=11 + WeaponSelectTexture=Texture2D'WEP_UI_AutoTurret_TEX.UI_WeaponSelect_AutoTurret' + InventorySize=3 + + DetonateAkEvent=AkEvent'ww_wep_autoturret.Play_WEP_AutoTurret_Detonate_Trigger' + DryFireAkEvent=AkEvent'ww_wep_autoturret.Play_WEP_AutoTurret_Dry_Fire' + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.05f,IncrementWeight=1) + //WeaponUpgrades[2]=(IncrementDamage=1.1f,IncrementWeight=2) + //WeaponUpgrades[3]=(IncrementDamage=1.15f,IncrementWeight=3) + + AssociatedPerkClasses(0)=class'KFPerk_Commando' + + MaxTurretsDeployed=1 + NumDeployedTurrets=0 + ThrowStrength=1350.0f + bTurretReadyToUse=true + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Damage1, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) + NumBloodMapMaterials=3 +} diff --git a/KFGameContent/Classes/KFWeap_AutoTurretWeapon.uc b/KFGameContent/Classes/KFWeap_AutoTurretWeapon.uc new file mode 100644 index 0000000..39fadd2 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_AutoTurretWeapon.uc @@ -0,0 +1,388 @@ +//============================================================================= +// KFWeap_AutoTurretWeapon +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_AutoTurretWeapon extends KFWeap_SMGBase; + +var KFPawn_AutoTurret InstigatorDrone; + +var transient bool bFiring; + +simulated event PreBeginPlay() +{ + super.PreBeginPlay(); + StartLoadWeaponContent(); +} + +simulated state WeaponFiring +{ + simulated function EndState(Name NextStateName) + { + local Pawn OriginalInstigator; + + // The Instigator for this weapon is the Player owning the weapon (for Perk, damage, etc,. calculations) + // But for Weapon Firing end state logic we need to point to the real Drone Pawn so we don't mess + // With whichever weapon the Player had equipped at that point + + OriginalInstigator = Instigator; + + Instigator = InstigatorDrone; + + super.EndState(NextStateName); + + Instigator = OriginalInstigator; + } +} + +simulated function FireAmmunition() +{ + CurrentFireMode = DEFAULT_FIREMODE; + super.FireAmmunition(); +} + +simulated function GetMuzzleLocAndRot(out vector MuzzleLoc, out rotator MuzzleRot) +{ + if (KFSkeletalMeshComponent(Mesh).GetSocketWorldLocationAndRotation('MuzzleFlash', MuzzleLoc, MuzzleRot) == false) + { + `Log("MuzzleFlash not found!"); + } + + // To World Coordinates. (Rotation should be 0 so no taken into account) + // MuzzleLoc = Location + QuatRotateVector(QuatFromRotator(Rotation), MuzzleLoc); +} + +simulated function InstantFireClient() +{ + local vector StartTrace; + local vector EndTrace; + local rotator AimRot; + local Array ImpactList; + local int Idx; + local ImpactInfo RealImpact; + local float CurPenetrationValue; + local KFPawn KFPOwner; + + // see Controller AimHelpDot() / AimingHelp() + bInstantHit = true; + + // define range to use for CalcWeaponFire() + GetMuzzleLocAndRot(StartTrace, AimRot ); + EndTrace = StartTrace + vector(Owner.Rotation) * GetTraceRange(); + + bInstantHit = false; + + // Initialize penetration power + PenetrationPowerRemaining = GetInitialPenetrationPower(CurrentFireMode); + CurPenetrationValue = PenetrationPowerRemaining; + + // Perform shot + RealImpact = CalcWeaponFire(StartTrace, EndTrace, ImpactList); + + // Set flash location to trigger client side effects. Bypass Weapon.SetFlashLocation since + // that function is not marked as simulated and we want instant client feedback. + // ProjectileFire/IncrementFlashCount has the right idea: + // 1) Call IncrementFlashCount on Server & Local + // 2) Replicate FlashCount if ( !bNetOwner ) + // 3) Call WeaponFired() once on local player + KFPOwner = KFPawn(Owner); + if( KFPOwner != None ) + { + KFPOwner.SetFlashLocation( Self, CurrentFireMode, RealImpact.HitLocation ); + } + + // local player only for clientside hit detection + // allow weapon to add extra bullet impacts (useful for shotguns) + InstantFireClient_AddImpacts(StartTrace, AimRot, ImpactList); + + for (Idx = 0; Idx < ImpactList.Length; Idx++) + { + ProcessInstantHitEx(CurrentFireMode, ImpactList[Idx],, CurPenetrationValue, Idx); + } + + if ( Instigator.Role < ROLE_Authority ) + { + SendClientImpactList(CurrentFireMode, ImpactList); + } +} + + +simulated function Projectile ProjectileFire() +{ + local vector StartTrace, RealStartLoc, AimDir; + local rotator AimRot; + local class MyProjectileClass; + + // tell remote clients that we fired, to trigger effects + + if ( ShouldIncrementFlashCountOnFire() ) + { + IncrementFlashCount(); + } + + MyProjectileClass = GetKFProjectileClass(); + + if( Role == ROLE_Authority || (MyProjectileClass.default.bUseClientSideHitDetection + && MyProjectileClass.default.bNoReplicationToInstigator && Instigator != none + && Instigator.IsLocallyControlled()) ) + { + // This is where we would start an instant trace. (what CalcWeaponFire uses) + // MySkelMesh.GetSocketWorldLocationAndRotation( 'MuzzleFlash', StartTrace, AimRot ); + GetMuzzleLocAndRot(StartTrace, AimRot ); + + AimDir = Vector(Owner.Rotation); + + // this is the location where the projectile is spawned. + RealStartLoc = StartTrace; + + return SpawnAllProjectiles(MyProjectileClass, RealStartLoc, AimDir); + } + + return None; +} + +simulated function IncrementFlashCount() +{ + local KFPawn P; + P = KFPawn(Owner); + + if( P != None ) + { + P.IncrementFlashCount( Self, CurrentFireMode ); + } +} + +simulated function Fire() +{ + if (bFiring) + { + return; + } + + if (HasAmmo(DEFAULT_FIREMODE)) + { + SendToFiringState(DEFAULT_FIREMODE); + } + + bFiring = true; +} + + +simulated function StopFire(byte FireModeNum) +{ + if (!bFiring) + { + return; + } + + bFiring = false; + super.StopFire(FireModeNum); + GoToState('Inactive'); +} + +simulated function bool ShouldRefire() +{ + // if doesn't have ammo to keep on firing, then stop + if( !HasAmmo( CurrentFireMode ) ) + { + return false; + } + + return bFiring; +} + +/** + * Starts playing looping FireSnd only (used for switching sounds in Zedtime) + */ +simulated function StartLoopingFireSound(byte FireModeNum) +{ + if ( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !ShouldForceSingleFireSound() ) + { + bPlayingLoopingFireSnd = true; + KFPawn(Owner).SetWeaponAmbientSound(WeaponFireSnd[FireModeNum].DefaultCue, WeaponFireSnd[FireModeNum].FirstPersonCue); + } +} + +/** + * Stops playing looping FireSnd only (used for switching sounds in Zedtime) + */ +simulated function StopLoopingFireSound(byte FireModeNum) +{ + if ( bPlayingLoopingFireSnd ) + { + KFPawn(Owner).SetWeaponAmbientSound(None); + if ( FireModeNum < WeaponFireLoopEndSnd.Length ) + { + WeaponPlayFireSound(WeaponFireLoopEndSnd[FireModeNum].DefaultCue, WeaponFireLoopEndSnd[FireModeNum].FirstPersonCue); + } + + bPlayingLoopingFireSnd = false; + } +} + +simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation ) +{ + local name WeaponFireAnimName; + local KFPerk CurrentPerk; + local float TempTweenTime, AdjustedAnimLength; + local KFPawn KFPO; + + // If we have stopped the looping fire sound to play single fire sounds for zed time + // start the looping sound back up again when the time is back above zed time speed + if( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !bPlayingLoopingFireSnd ) + { + StartLoopingFireSound(FireModeNum); + } + + PlayFiringSound(CurrentFireMode); + KFPO = KFPawn(Owner); + + if( KFPO != none ) + { + // Tell our pawn about any changes in animation speed + UpdateWeaponAttachmentAnimRate( GetThirdPersonAnimRate() ); + + if( KFPO.IsLocallyControlled() ) + { + if( KFPO.IsFirstPerson() ) + { + if ( !bPlayingLoopingFireAnim ) + { + WeaponFireAnimName = GetWeaponFireAnim(FireModeNum); + + if ( WeaponFireAnimName != '' ) + { + AdjustedAnimLength = MySkelMesh.GetAnimLength(WeaponFireAnimName); + TempTweenTime = FireTweenTime; + + CurrentPerk = GetPerk(); + if( CurrentPerk != none ) + { + CurrentPerk.ModifyRateOfFire( AdjustedAnimLength, self ); + + // We need to unlock the slide if we fire from zero ammo while uber ammo is active + if( EmptyMagBlendNode != none + && BonesToLockOnEmpty.Length > 0 + && AmmoCount[GetAmmoType(FireModeNum)] == 0 + && CurrentPerk.GetIsUberAmmoActive(self) ) + { + EmptyMagBlendNode.SetBlendTarget( 0, 0 ); + TempTweenTime = 0.f; + } + } + + PlayAnimation(WeaponFireAnimName, AdjustedAnimLength,, TempTweenTime); + } + } + + // Start muzzle flash effect + CauseMuzzleFlash(FireModeNum); + } + + HandleRecoil(); + ShakeView(); + + if (AmmoCount[0] == 0 && ForceReloadTimeOnEmpty > 0) + { + SetTimer(ForceReloadTimeOnEmpty, false, nameof(ForceReload)); + } + } + } +} + +defaultproperties +{ + // Shooting Animations + FireSightedAnims[0]=Shoot_Iron + FireSightedAnims[1]=Shoot_Iron2 + FireSightedAnims[2]=Shoot_Iron3 + + // FOV + MeshIronSightFOV=52 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=75 + DOF_FG_MaxNearBlurSize=3.5 + + // Zooming/Position + PlayerViewOffset=(X=9.0,Y=10,Z=-4) + + // Content + PackageKey="AutoTurretWeapon" + FirstPersonMeshName="wep_1p_autoturretWeapon_mesh.Wep_1stP_AutoTurretWeapon_Rig" + FirstPersonAnimSetNames(0)="wep_1p_autoturretWeapon_anim.Wep_1stP_AutoTurretWeapon_Anim" + PickupMeshName="wep_3p_autoturretWeapon_mesh.Wep_AutoTurretWeapon_Pickup" + AttachmentArchetypeName="WEP_AutoTurretWeapon_ARCH.AutoTurretWeaponAttachment" + MuzzleFlashTemplateName="wep_autoturretWeapon_arch.Wep_AutoTurretWeapon_MuzzleFlash" + + // Zooming/Position + IronSightPosition=(X=7,Y=0,Z=0) + + // Ammo + MagazineCapacity[0]=150 + SpareAmmoCapacity[0]=0 + InitialSpareMags[0]=0 + bCanBeReloaded=false + bReloadFromMagazine=false + + // Recoil + maxRecoilPitch=225 + minRecoilPitch=150 + maxRecoilYaw=150 + minRecoilYaw=-150 + RecoilRate=0.085 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=75 + RecoilISMinYawLimit=65460 + RecoilISMaxPitchLimit=195 + RecoilISMinPitchLimit=65460 + RecoilViewRotationScale=0.25 + IronSightMeshFOVCompensationScale=1.5 + + // Inventory / Grouping + InventorySize=5 + GroupPriority=25 + WeaponSelectTexture=Texture2D'WEP_UI_AutoTurret_TEX.UI_WeaponSelect_AutoTurret' + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletBurst' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_Pistol9mm' + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_AutoTurret' + FireInterval(DEFAULT_FIREMODE)=+0.12 // 500 rpm + InstantHitDamage(DEFAULT_FIREMODE)=8 + Spread(DEFAULT_FIREMODE)=0.01 + FireOffset=(X=30,Y=4.5,Z=-4) + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None + + // BASH_FIREMODE + WeaponFireTypes(BASH_FIREMODE)=EWFT_None + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_LP_3P', FirstPersonCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_LP_1P') + WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_EndLP_3P', FirstPersonCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_EndLP_1P') + + bLoopingFireSnd(DEFAULT_FIREMODE)=true + bLoopingFireAnim(DEFAULT_FIREMODE)=true + + AssociatedPerkClasses(0)=class'KFPerk_Commando' + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Damage1, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) + + InstigatorDrone=none + + bFiring=false +} + diff --git a/KFGameContent/Classes/KFWeap_Bow_Crossbow.uc b/KFGameContent/Classes/KFWeap_Bow_Crossbow.uc index 918b16b..26c8d98 100644 --- a/KFGameContent/Classes/KFWeap_Bow_Crossbow.uc +++ b/KFGameContent/Classes/KFWeap_Bow_Crossbow.uc @@ -52,7 +52,7 @@ defaultproperties MeshIronSightFOV=52 PlayerIronSightFOV=70 PlayerViewOffset=(X=1,Y=8,Z=-5) - IronSightPosition=(X=-13,Y=0,Z=0) + IronSightPosition=(X=-13,Y=0,Z=-0.015) // AI warning system bWarnAIWhenAiming=true diff --git a/KFGameContent/Classes/KFWeap_GrenadeLauncher_M79.uc b/KFGameContent/Classes/KFWeap_GrenadeLauncher_M79.uc index faa01e6..9ea06b2 100644 --- a/KFGameContent/Classes/KFWeap_GrenadeLauncher_M79.uc +++ b/KFGameContent/Classes/KFWeap_GrenadeLauncher_M79.uc @@ -37,7 +37,7 @@ defaultproperties MuzzleFlashTemplateName="WEP_M79_ARCH.Wep_M79_MuzzleFlash" // Zooming/Position - IronSightPosition=(X=0,Y=0,Z=0) + IronSightPosition=(X=0,Y=0.05,Z=0.2) // Ammo MagazineCapacity[0]=1 diff --git a/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc b/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc new file mode 100644 index 0000000..3da45fd --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc @@ -0,0 +1,378 @@ +//============================================================================= +// KFWeap_HRG_CranialPopper +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HRG_CranialPopper extends KFWeap_ScopedBase; + +/** How many alt ammo to recharge per second */ +var float AltAmmoFullRechargeSeconds; +/** How many alt ammo to recharge per second. */ +var transient float AltAmmoRechargePerSecond; + +var transient float AltAmmoIncrement; +var repnotify byte AltAmmo; + +var class OpticsUIClass; +var KFGFxWorld_MedicOptics OpticsUI; + +/** The last updated value for our ammo - Used to know when to update our optics ammo */ +var byte StoredPrimaryAmmo; +var byte StoredSecondaryAmmo; + +replication +{ + if (bNetDirty && Role == ROLE_Authority) + AltAmmo; +} + +simulated event ReplicatedEvent(name VarName) +{ + if (VarName == nameof(AltAmmo)) + { + AmmoCount[ALTFIRE_FIREMODE] = AltAmmo; + } + else + { + Super.ReplicatedEvent(VarName); + } +} + +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Rifle; +} + +simulated event PreBeginPlay() +{ + super.PreBeginPlay(); + StartAltAmmoRecharge(); +} + +function StartAltAmmoRecharge() +{ + local float UsedAltAmmoRechargeTime; + + // begin ammo recharge on server + if( Role == ROLE_Authority ) + { + UsedAltAmmoRechargeTime = AltAmmoFullRechargeSeconds; + AltAmmoRechargePerSecond = MagazineCapacity[ALTFIRE_FIREMODE] / UsedAltAmmoRechargeTime; + AltAmmoIncrement = 0; + } +} + +function RechargeAltAmmo(float DeltaTime) +{ + if ( Role == ROLE_Authority ) + { + AltAmmoIncrement += AltAmmoRechargePerSecond * DeltaTime; + + if( AltAmmoIncrement >= 1.0 && AmmoCount[ALTFIRE_FIREMODE] < MagazineCapacity[ALTFIRE_FIREMODE] ) + { + AmmoCount[ALTFIRE_FIREMODE]++; + AltAmmoIncrement -= 1.0; + AltAmmo = AmmoCount[ALTFIRE_FIREMODE]; + } + } +} + +/** Overridden to call StartHealRecharge on server */ +function GivenTo( Pawn thisPawn, optional bool bDoNotActivate ) +{ + super.GivenTo( thisPawn, bDoNotActivate ); + + if( Role == ROLE_Authority && !thisPawn.IsLocallyControlled() ) + { + StartAltAmmoRecharge(); + } +} + +simulated event Tick( FLOAT DeltaTime ) +{ + if( AmmoCount[ALTFIRE_FIREMODE] < MagazineCapacity[ALTFIRE_FIREMODE] ) + { + RechargeAltAmmo(DeltaTime); + } + + if (Instigator != none && Instigator.weapon == self) + { + UpdateOpticsUI(); + } + + Super.Tick(DeltaTime); +} + +simulated function bool CanOverrideMagReload(byte FireModeNum) +{ + return FireModeNum == ALTFIRE_FIREMODE || Super.CanOverrideMagReload(FireModeNum); +} + +/** Instead of switch fire mode use as immediate alt fire */ +simulated function AltFireMode() +{ + if ( !Instigator.IsLocallyControlled() ) + { + return; + } + + StartFire(ALTFIRE_FIREMODE); +} + +/** Alt ammo doesn't count as ammo for purposes of inventory management (e.g. switching) */ +simulated function bool HasAnyAmmo() +{ + return HasSpareAmmo() || HasAmmo(DEFAULT_FIREMODE); +} + +simulated function bool ShouldAutoReload(byte FireModeNum) +{ + if (FireModeNum == ALTFIRE_FIREMODE) + return false; + + return super.ShouldAutoReload(FireModeNum); +} + +/** + Optic UI + */ + +/** Get our optics movie from the inventory once our InvManager is created */ +reliable client function ClientWeaponSet(bool bOptionalSet, optional bool bDoNotActivate) +{ + local KFInventoryManager KFIM; + + super.ClientWeaponSet(bOptionalSet, bDoNotActivate); + + if (OpticsUI == none && OpticsUIClass != none) + { + KFIM = KFInventoryManager(InvManager); + if (KFIM != none) + { + //Create the screen's UI piece + OpticsUI = KFGFxWorld_MedicOptics(KFIM.GetOpticsUIMovie(OpticsUIClass)); + } + } +} + +/** + * Update our displayed ammo count if it's changed + */ +simulated function UpdateOpticsUI(optional bool bForceUpdate) +{ + if (OpticsUI != none && OpticsUI.OpticsContainer != none) + { + if (AmmoCount[DEFAULT_FIREMODE] != StoredPrimaryAmmo || bForceUpdate) + { + StoredPrimaryAmmo = AmmoCount[DEFAULT_FIREMODE]; + OpticsUI.SetPrimaryAmmo(StoredPrimaryAmmo); + } + + if (AmmoCount[ALTFIRE_FIREMODE] != StoredSecondaryAmmo || bForceUpdate) + { + StoredSecondaryAmmo = AmmoCount[ALTFIRE_FIREMODE]; + OpticsUI.SetHealerCharge(StoredSecondaryAmmo); + } + + if(OpticsUI.MinPercentPerShot != AmmoCost[ALTFIRE_FIREMODE]) + { + OpticsUI.SetShotPercentCost( AmmoCost[ALTFIRE_FIREMODE] ); + } + } +} + +function ItemRemovedFromInvManager() +{ + local KFInventoryManager KFIM; + local KFWeap_MedicBase KFW; + + Super.ItemRemovedFromInvManager(); + + if (OpticsUI != none) + { + KFIM = KFInventoryManager(InvManager); + if (KFIM != none) + { + // @todo future implementation will have optics in base weapon class + foreach KFIM.InventoryActors(class'KFWeap_MedicBase', KFW) + { + if( KFW.OpticsUI.Class == OpticsUI.class) + { + // A different weapon is still using this optics class + return; + } + } + + //Create the screen's UI piece + KFIM.RemoveOpticsUIMovie(OpticsUI.class); + + OpticsUI.Close(); + OpticsUI = none; + } + } +} + +/** Unpause our optics movie and reinitialize our ammo when we equip the weapon */ +simulated function AttachWeaponTo(SkeletalMeshComponent MeshCpnt, optional Name SocketName) +{ + super.AttachWeaponTo(MeshCpnt, SocketName); + + if (OpticsUI != none) + { + OpticsUI.SetPause(false); + OpticsUI.ClearLockOn(); + UpdateOpticsUI(true); + OpticsUI.SetShotPercentCost( AmmoCost[ALTFIRE_FIREMODE]); + } +} + +/** Pause the optics movie once we unequip the weapon so it's not playing in the background */ +simulated function DetachWeapon() +{ + local Pawn OwnerPawn; + super.DetachWeapon(); + + OwnerPawn = Pawn(Owner); + if( OwnerPawn != none && OwnerPawn.Weapon == self ) + { + if (OpticsUI != none) + { + OpticsUI.SetPause(); + } + } +} + +defaultproperties +{ + AltAmmoFullRechargeSeconds=5 + + // Inventory / Grouping + InventorySize=7 + GroupPriority=75 + WeaponSelectTexture=Texture2D'wep_ui_hrg_cranialpopper_tex.UI_WeaponSelect_HRG_CranialPopper' + SecondaryAmmoTexture=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity' + AssociatedPerkClasses(0)=class'KFPerk_Sharpshooter' + + // FOV + MeshFOV=60 + MeshIronSightFOV=27 + PlayerIronSightFOV=70 + + // Depth of field + DOF_BlendInSpeed=3.0 + DOF_FG_FocalRadius=0//70 + DOF_FG_MaxNearBlurSize=3.5 + + // Content + PackageKey="HRG_CranialPopper" + FirstPersonMeshName="WEP_1P_HRG_CranialPopper_MESH.Wep_1stP_HRG_CranialPopper_Rig" + FirstPersonAnimSetNames(0)="wep_1p_hrg_cranialpopper_anim.Wep_1stP_HRG_CranialPopper_Anim" + PickupMeshName="WEP_3P_HRG_CranialPopper_MESH.Wep_3rdP_HRG_CranialPopper_Pickup" + AttachmentArchetypeName="WEP_HRG_CranialPopper_ARCH.Wep_HRG_CrannialPopper_3P" + MuzzleFlashTemplateName="WEP_HRG_CranialPopper_ARCH.Wep_HRG_CranialPopper_MuzzleFlash" + + LaserSightTemplate=KFLaserSightAttachment'FX_LaserSight_ARCH.LaserSight_WithAttachment_1P' + + // Ammo + MagazineCapacity[0]=7 + SpareAmmoCapacity[0]=112 //98 + InitialSpareMags[0]=5 + AmmoPickupScale[0]=2.0 + bCanBeReloaded=true + bReloadFromMagazine=true + + MagazineCapacity[1]=100 + AltAmmo=100 + bCanRefillSecondaryAmmo=false + + // Zooming/Position + PlayerViewOffset=(X=15.0,Y=11.5,Z=-4) //(X=20.0,Y=11.0,Z=-2) //(X=15.0,Y=11.5,Z=-4) + IronSightPosition=(X=-7.0,Y=0.07,Z=0.05) //(X=30.0,Y=0,Z=0) + + // AI warning system + bWarnAIWhenAiming=true + AimWarningDelay=(X=0.4f, Y=0.8f) + AimWarningCooldown=0.0f + + // Recoil + maxRecoilPitch=225 + minRecoilPitch=200 + maxRecoilYaw=200 + minRecoilYaw=-200 + RecoilRate=0.08 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=150 + RecoilISMinYawLimit=65385 + RecoilISMaxPitchLimit=375 + RecoilISMinPitchLimit=65460 + RecoilViewRotationScale=0.6 + + // Scope Render + // 2D scene capture + Begin Object Name=SceneCapture2DComponent0 + //TextureTarget=TextureRenderTarget2D'WEP_1P_HRG_CranialPopper_MAT.WEP_1P_Cranial_zoomed_Scope_MAT' + 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_HRG_CranialPopper_MAT.WEP_1P_Cranial_zoomed_Scope_MAT' + ScopeMICIndex=2 + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_HRG_CranialPopper' + InstantHitDamage(DEFAULT_FIREMODE)=50.0 //100.0 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Piercing_HRG_CranialPopper' + FireInterval(DEFAULT_FIREMODE)=0.25 + PenetrationPower(DEFAULT_FIREMODE)=0.0 //2.0 + Spread(DEFAULT_FIREMODE)=0.006 + FireOffset=(X=30,Y=3.0,Z=-2.5) + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Grenade_HRG_CranialPopper' + InstantHitDamage(ALTFIRE_FIREMODE)=0.0 //override by AfflictionHandler.GetBigHeadAfflictionDamageModifier() + InstantHitDamageTypes(ALTFIRE_FIREMODE)=Class'KFDT_Blast_HRG_CranialPopper' + FireInterval(ALTFIRE_FIREMODE)=+0.25 + AmmoCost(ALTFIRE_FIREMODE)=100 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_CranialPopper' + InstantHitDamage(BASH_FIREMODE)=26 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_CranialPopper.Play_WEP_HRG_CranialPopper_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_CranialPopper.Play_WEP_HRG_CranialPopper_Fire_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_EBR.Play_WEP_SA_EBR_Handling_DryFire' //@TODO: Replace + + // Custom animations + FireSightedAnims=(Shoot_Iron, Shoot_Iron2, Shoot_Iron3) + + // Attachments + bHasIronSights=true + bHasFlashlight=false + bHasLaserSight=false + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil' + + //WeaponUpgrades[1]=(IncrementDamage=1.4f,IncrementWeight=1, IncrementHealFullRecharge=.8) + //WeaponUpgrades[2]=(IncrementDamage=1.8f,IncrementWeight=2, IncrementHealFullRecharge=.6) + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Weight, Add=1), (Stat=EWUS_HealFullRecharge, Scale=0.9f))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Weight, Add=2), (Stat=EWUS_HealFullRecharge, Scale=0.8f))) + + // From original KFWeap_RifleBase base class + AimCorrectionSize=40.f + + NumBloodMapMaterials=3 + + OpticsUIClass=class'KFGFxWorld_MedicOptics' +} diff --git a/KFGameContent/Classes/KFWeap_HRG_Crossboom.uc b/KFGameContent/Classes/KFWeap_HRG_Crossboom.uc new file mode 100644 index 0000000..62d7844 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_Crossboom.uc @@ -0,0 +1,191 @@ +//============================================================================= +// KFWeap_HRG_Crossboom +//============================================================================= +// A crossbow but with extra explosions +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HRG_Crossboom extends KFWeap_ScopedBase; + +/** Reduction for the amount of damage dealt to the weapon owner (including damage by the explosion) */ +var() float SelfDamageReductionValue; + +/** Return true if this weapon should play the fire last animation for this shoot animation */ +simulated function bool ShouldPlayFireLast(byte FireModeNum) +{ + if( SpareAmmoCount[GetAmmoType(FireModeNum)] == 0 ) + { + return true; + } + + return false; +} + +/** Returns animation to play based on reload type and status */ +simulated function name GetReloadAnimName( bool bTacticalReload ) +{ + if ( AmmoCount[0] > 0 ) + { + // Disable half-reloads for now. This can happen if server gets out + // of sync, but choosing the wrong animation will just make it worse! + `warn("Grenade launcher reloading with non-empty mag"); + } + + return bTacticalReload ? ReloadEmptyMagEliteAnim : ReloadEmptyMagAnim; +} + +/** Returns trader filter index based on weapon type (copied from riflebase) */ +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Projectile; +} + +/** Instead of switch fire mode use as immediate alt fire */ +simulated function AltFireMode() +{ + // StartFire - StopFire called from KFPlayerInput + StartFire(ALTFIRE_FIREMODE); +} + +/** + Reduce the damage received and apply it to the shield + */ +function AdjustDamage(out int InDamage, class DamageType, Actor DamageCauser) +{ + super.AdjustDamage(InDamage, DamageType, DamageCauser); + + if (Instigator != none && DamageCauser.Instigator == Instigator) + { + InDamage *= SelfDamageReductionValue; + } +} + +defaultproperties +{ + // Inventory + InventorySize=7 + GroupPriority=70 + WeaponSelectTexture=Texture2D'WEP_UI_HRG_Crossboom_TEX.UI_WeaponSelect_Crossboom' + AssociatedPerkClasses(0)=class'KFPerk_Demolitionist' + + // FOV / Position + MeshFOV=70 + MeshIronSightFOV=52 + PlayerIronSightFOV=70 + PlayerViewOffset=(X=1,Y=8,Z=-5) + IronSightPosition=(X=-13,Y=0,Z=-0.061) + + // AI warning system + bWarnAIWhenAiming=true + MaxAIWarningDistSQ=4000000 + AimWarningDelay=(X=0.4f, Y=0.8f) + AimWarningCooldown=0.0f + +// SCOPE + // 2D scene capture + Begin Object Name=SceneCapture2DComponent0 + TextureTarget=TextureRenderTarget2D'Wep_Mat_Lib.WEP_ScopeLense_Target' + FieldOfView=18.5 // "2.0X" = 37(our real world FOV determinant)/2.0 + End Object + + ScopedSensitivityMod=12.0 + + ScopeLenseMICTemplate=MaterialInstanceConstant'WEP_1P_HRG_Crossboom_MAT.WEP_1P_HRG_Crossboom_Scope_MAT' + ScopeMICIndex = 1 + +// + + // Depth of field + DOF_BlendInSpeed=3.0 + DOF_FG_FocalRadius=0.0 + DOF_FG_MaxNearBlurSize=3.5 + + // Content + PackageKey="HRG_Crossboom" + FirstPersonMeshName="WEP_1P_HRG_Crossboom_MESH.Wep_1stP_HRG_Crossboom_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_HRG_Crossboom_ANIM.Wep_1stP_HRG_Crossboom_Anim" + PickupMeshName="WEP_3P_HRG_Crossboom_MESH.Wep_HRG_Crossboom_Pickup" + AttachmentArchetypeName="WEP_HRG_Crossboom_ARCH.Wep_Crossboom_3P" + MuzzleFlashTemplateName="WEP_HRG_Crossboom_ARCH.Wep_Crossboom_MuzzleFlash" + + // Ammo + MagazineCapacity[0]=1 + SpareAmmoCapacity[0]=38 + InitialSpareMags[0]=11 + AmmoPickupScale[0]=4.0 // 4 arrows + bCanBeReloaded=true + bReloadFromMagazine=true // reloading from mag is one step, while NOT reloading from mag is multi-step (open bolt, load ammo, close bolt) and not applicable for bow + + // Recoil + maxRecoilPitch=200 + minRecoilPitch=150 + maxRecoilYaw=100 + minRecoilYaw=-100 + RecoilRate=0.06 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=64785 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=375 + RecoilISMinPitchLimit=65460 + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletArrow' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFireAndReload + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bolt_HRG_Crossboom' + InstantHitDamage(DEFAULT_FIREMODE)=10.0 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Piercing_HRG_Crossboom' + PenetrationPower(DEFAULT_FIREMODE)=0.0 + FireInterval(DEFAULT_FIREMODE)=0.3 // For this weapon, this is not the fire rate, but the time when the auto reload anim kicks in + Spread(DEFAULT_FIREMODE)=0.007 //0.007 + FireOffset=(X=25,Y=3.0,Z=-4.0) + + // ALT_FIREMODE + FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletArrow' + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFireAndReload + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bolt_HRG_CrossboomAlt' + InstantHitDamage(ALTFIRE_FIREMODE)=10.0 + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Piercing_HRG_Crossboom' + PenetrationPower(ALTFIRE_FIREMODE)=0.0 + FireInterval(ALTFIRE_FIREMODE)=0.3 // For this weapon, this is not the fire rate, but the time when the auto reload anim kicks in + Spread(ALTFIRE_FIREMODE)=0.007 //0.007 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_Fire_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_DryFire' + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_Fire_1P') + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_HRG_Crossboom.Play_WEP_HRG_Crossboom_DryFire' + + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Crossboom' + InstantHitDamage(BASH_FIREMODE)=26 + + // Custom animations + FireSightedAnims=(Shoot_Iron, Shoot_Iron2, Shoot_Iron3) + BonesToLockOnEmpty=(RW_Cable_Parent) + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + // Just like the launchers, this weapon has mag size of 1 and force reload which + // causes significant ammo sync issues. This fix is far from perfect, but it helps. + bAllowClientAmmoTracking=true + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.2f,IncrementWeight=1) + //WeaponUpgrades[2]=(IncrementDamage=1.4f,IncrementWeight=2) + //WeaponUpgrades[3]=(IncrementDamage=1.6f,IncrementWeight=3) + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Damage1, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) + + SelfDamageReductionValue = 0.25f; +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_HuskCannon.uc b/KFGameContent/Classes/KFWeap_HuskCannon.uc index 741dba0..6d34a71 100644 --- a/KFGameContent/Classes/KFWeap_HuskCannon.uc +++ b/KFGameContent/Classes/KFWeap_HuskCannon.uc @@ -438,7 +438,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=20.0,Y=12,Z=-1) - IronSightPosition=(X=0,Y=0,Z=0) + IronSightPosition=(X=0,Y=0,Z=-0.18) // Ammo MagazineCapacity[0]=30 diff --git a/KFGameContent/Classes/KFWeap_Minigun.uc b/KFGameContent/Classes/KFWeap_Minigun.uc index 5e1cbfc..5920c7e 100644 --- a/KFGameContent/Classes/KFWeap_Minigun.uc +++ b/KFGameContent/Classes/KFWeap_Minigun.uc @@ -303,11 +303,11 @@ defaultproperties // movement and rotation speed values when winding up WindUpViewRotationSpeed=2000 // base rotation speed is 2000 - WindUpPawnMovementSpeed=0.8f //0.7f // base modifier is 1 + WindUpPawnMovementSpeed=0.9f //0.8f //0.7f // base modifier is 1 // movement and rotation speed values when firing - FiringViewRotationSpeed=488 //320 //270 - FiringPawnMovementSpeed=0.3f //0.2f //0.08f + FiringViewRotationSpeed=1000 //488 //320 //270 + FiringPawnMovementSpeed=1.8f //0.3f //0.2f //0.08f } diff --git a/KFGameContent/Classes/KFWeap_Pistol_9mm.uc b/KFGameContent/Classes/KFWeap_Pistol_9mm.uc index 8348e55..8bd48d5 100644 --- a/KFGameContent/Classes/KFWeap_Pistol_9mm.uc +++ b/KFGameContent/Classes/KFWeap_Pistol_9mm.uc @@ -87,7 +87,7 @@ defaultproperties // Inventory InventorySize=0 - GroupPriority=10 + GroupPriority=13 bCanThrow=false bDropOnDeath=false WeaponSelectTexture=Texture2D'ui_weaponselect_tex.UI_WeaponSelect_9mm' diff --git a/KFGameContent/Classes/KFWeap_RandomGrenade.uc b/KFGameContent/Classes/KFWeap_RandomGrenade.uc new file mode 100644 index 0000000..e5cf1c5 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_RandomGrenade.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFWeap_RandomGrenade +//============================================================================= +// A random grenade +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_RandomGrenade extends KFWeapon + abstract; + +defaultproperties +{ +} + diff --git a/KFGameContent/Classes/KFWeap_Rifle_ParasiteImplanter.uc b/KFGameContent/Classes/KFWeap_Rifle_ParasiteImplanter.uc index 3f1966a..bd4c853 100644 --- a/KFGameContent/Classes/KFWeap_Rifle_ParasiteImplanter.uc +++ b/KFGameContent/Classes/KFWeap_Rifle_ParasiteImplanter.uc @@ -291,6 +291,12 @@ simulated function bool ShouldAutoReload(byte FireModeNum) return super.ShouldAutoReload(FireModeNum); } +/** Called during reload state */ +simulated function bool CanOverrideMagReload(byte FireModeNum) +{ + return super.CanOverrideMagReload(FireModeNum) || FireModeNum == ALTFIRE_FIREMODE; +} + defaultproperties { SeedFullRechargeSeconds=14 //10 diff --git a/KFGameContent/Classes/KFWeap_RocketLauncher_SealSqueal.uc b/KFGameContent/Classes/KFWeap_RocketLauncher_SealSqueal.uc index 52957e9..5fb6efc 100644 --- a/KFGameContent/Classes/KFWeap_RocketLauncher_SealSqueal.uc +++ b/KFGameContent/Classes/KFWeap_RocketLauncher_SealSqueal.uc @@ -269,7 +269,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=11.0,Y=8,Z=-2) - IronSightPosition=(X=10,Y=0,Z=0) + IronSightPosition=(X=10,Y=-0.09,Z=-0.2) // AI warning system bWarnAIWhenAiming=true diff --git a/KFGameContent/Classes/KFWeap_RocketLauncher_Seeker6.uc b/KFGameContent/Classes/KFWeap_RocketLauncher_Seeker6.uc index 9940068..7e0d212 100644 --- a/KFGameContent/Classes/KFWeap_RocketLauncher_Seeker6.uc +++ b/KFGameContent/Classes/KFWeap_RocketLauncher_Seeker6.uc @@ -567,7 +567,7 @@ defaultproperties LockLostSoundFirstPerson=AkEvent'WW_WEP_SA_Railgun.Play_Railgun_Scope_Lost' // Zooming/Position - IronSightPosition=(X=0,Y=0,Z=0) + IronSightPosition=(X=0,Y=-0.065,Z=-0.31) // Ammo MagazineCapacity[0]=6 diff --git a/KFGameContent/Classes/KFWeap_Shotgun_HRG_Kaboomstick.uc b/KFGameContent/Classes/KFWeap_Shotgun_HRG_Kaboomstick.uc index fa13b06..7482b68 100644 --- a/KFGameContent/Classes/KFWeap_Shotgun_HRG_Kaboomstick.uc +++ b/KFGameContent/Classes/KFWeap_Shotgun_HRG_Kaboomstick.uc @@ -28,7 +28,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=4.0,Y=7.0,Z=-5.0) - IronSightPosition=(X=7,Y=0,Z=0) + IronSightPosition=(X=7,Y=0.22,Z=-0.6) // Content PackageKey="HRG_Kaboomstick" diff --git a/KFGameContent/Classes/KFWeap_Shotgun_M4.uc b/KFGameContent/Classes/KFWeap_Shotgun_M4.uc index 43649c1..998279f 100644 --- a/KFGameContent/Classes/KFWeap_Shotgun_M4.uc +++ b/KFGameContent/Classes/KFWeap_Shotgun_M4.uc @@ -28,7 +28,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=15.0,Y=8.5,Z=-3.5) - IronSightPosition=(X=5,Y=0,Z=0) + IronSightPosition=(X=5,Y=-0.025,Z=-0.03) // Content PackageKey="M4Shotgun" diff --git a/KFGameContent/Classes/KFWeap_Shotgun_MB500.uc b/KFGameContent/Classes/KFWeap_Shotgun_MB500.uc index f3c0e53..e65fab6 100644 --- a/KFGameContent/Classes/KFWeap_Shotgun_MB500.uc +++ b/KFGameContent/Classes/KFWeap_Shotgun_MB500.uc @@ -25,7 +25,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=8.0,Y=8.0,Z=-3.5) - IronSightPosition=(X=9.5,Y=0,Z=0) + IronSightPosition=(X=9.5,Y=-0.29,Z=0) // Content PackageKey="MB500" diff --git a/KFGameContent/Classes/KFWeap_ShrinkRayGun.uc b/KFGameContent/Classes/KFWeap_ShrinkRayGun.uc new file mode 100644 index 0000000..24f98c0 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_ShrinkRayGun.uc @@ -0,0 +1,187 @@ +//============================================================================= +// KFWeap_ShrinkRayGun +//============================================================================= +// Matheson would like this. +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_ShrinkRayGun extends KFWeap_FlameBase; + +simulated event PostBeginPlay() +{ + super.PostBeginPlay(); + BarrelHeat = FlameSprayArchetype.default.MaterialHeatRange.X; + ChangeMaterial(); + LastBarrelHeat = BarrelHeat; +} + +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Electric; +} + +/** Disable normal bullet spread */ +simulated function rotator AddSpread( rotator BaseAim ) +{ + return BaseAim; // do nothing +} + +simulated protected function TurnOffPilot() +{ + // Undo the invert of the pilot, so it does really deactivate + + bInvertPilot = false; + + Super.TurnOffPilot(); + + bInvertPilot = true; +} + +defaultproperties +{ + FlameSprayArchetype=SprayActor_Flame'WEP_ShrinkRay_Gun_ARCH.WEP_ShrinkRay_Gun_SprayFire' + + // Shooting Animations + bHasFireLastAnims=true + FireSightedAnims[0]=Shoot + FireSightedAnims[1]=Shoot_Heavy_Iron + + // FOV + MeshIronSightFOV=52 + PlayerIronSightFOV=77 + + // Zooming/Position + PlayerViewOffset=(X=0.0,Y=14,Z=-1) //(X=-2,Y=12,Z=0) + IronSightPosition=(X=-5.0,Y=-0.05,Z=1.0) //(X=-0.5,Y=-0.05,Z=1.35) //(X=5.0,Y=9,Z=-3) + + // Depth of field + DOF_FG_FocalRadius=150 + DOF_FG_MaxNearBlurSize=1 + + // Content + PackageKey="ShrinkRay_Gun" + FirstPersonMeshName="WEP_1P_ShrinkRay_Gun_MESH.Wep_1stP_ShrinkRay_Gun_Rig" + FirstPersonAnimSetNames(0)="WEP_1p_ShrinkRay_Gun_ANIM.WEP_1stp_shrinkray_gun_anim" + PickupMeshName="WEP_3P_ShrinkRay_Gun_MESH.Wep_ShrinkRay_Gun_Pickup" + AttachmentArchetypeName="WEP_ShrinkRay_Gun_ARCH.Microwave_Gun_3P" + MuzzleFlashTemplateName="WEP_ShrinkRay_Gun_ARCH.Wep_Microwave_Gun_MuzzleFlash" + + // Ammo + MagazineCapacity[0]=100 + SpareAmmoCapacity[0]=600 + InitialSpareMags[0]=1 + AmmoPickupScale[0]=1.0 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=50 + minRecoilPitch=50 + maxRecoilYaw=115 + minRecoilYaw=-115 + 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=100 + WeaponSelectTexture=Texture2D'WEP_UI_ShrinkRay_Gun_TEX.UI_Weapon_Select_Shrink_Ray_Gun' + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity' + FiringStatesArray(DEFAULT_FIREMODE)=SprayingFire + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Custom + FireInterval(DEFAULT_FIREMODE)=+0.08 // 750 RPM + MinAmmoConsumed=3 + FireOffset=(X=30,Y=4.5,Z=-5) + + Spread(DEFAULT_FIREMODE)=0.12f + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_ShrinkRay' + InstantHitDamage(BASH_FIREMODE)=26 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'ww_wep_shrink_ray_gun.Play_WEP_ShrinkRay_Gun_Shoot_Loop_3P', FirstPersonCue=AkEvent'ww_wep_shrink_ray_gun.Play_WEP_ShrinkRay_Gun_Shoot_Loop_1P') + WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Shrink_Ray_Gun.Play_WEP_ShrinkRay_Gun_Shoot_Loop_End_3P', FirstPersonCue=AkEvent'WW_WEP_Shrink_Ray_Gun.Play_WEP_ShrinkRay_Gun_Shoot_Loop_End_1P') + + // Muzzle Flash point light + // want this light to illuminate characters only + Begin Object Class=PointLightComponent Name=PilotPointLight0 + LightColor=(R=0,G=200,B=255,A=255) + Brightness=35.f + FalloffExponent=20.f + Radius=95.f + CastShadows=TRUE + CastStaticShadows=FALSE + CastDynamicShadows=TRUE + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=FALSE) + End Object + + Begin Object Class=PointLightComponent Name=PilotPointLight1 + LightColor=(R=250,G=150,B=85,A=255) + Brightness=3.f + FalloffExponent=8.f + Radius=32.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=TRUE + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=FALSE) + End Object + + PilotLights(0)=(Light=PilotPointLight0,FlickerIntensity=25.f,FlickerInterpSpeed=50f,LightAttachBone=MuzzleFlash) + PilotLights(1)=(Light=PilotPointLight1,FlickerIntensity=15.f,FlickerInterpSpeed=50.f,LightAttachBone=MuzzleFlash) + + bInvertPilot=true + + //@todo: add akevents when we have them + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'ww_wep_shrink_ray_gun.Play_WEP_ShrinkRay_Gun_Dry_Fire' + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'ww_wep_shrink_ray_gun.Play_WEP_ShrinkRay_Gun_Dry_Fire' + //PilotLightPlayEvent=AkEvent'WW_WEP_SA_Microwave_Gun.Play_SA_MicrowaveGun_PilotLight_Loop' + //PilotLightStopEvent=AkEvent'WW_WEP_SA_Microwave_Gun.Stop_SA_MicrowaveGun_PilotLight_Loop' + + // 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_Survivalist' + + BonesToLockOnEmpty=(RW_Handle1, RW_BatteryCylinder1, RW_BatteryCylinder2, RW_LeftArmSpinner, RW_RightArmSpinner, RW_LockEngager2, RW_LockEngager1) + + // AI Warning + bWarnAIWhenFiring=true + MaxAIWarningDistSQ=2250000 + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.15f,IncrementWeight=1) + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.35f), (Stat=EWUS_Damage1, Scale=1.35f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.7f), (Stat=EWUS_Damage1, Scale=1.7f), (Stat=EWUS_Weight, Add=2))) + + CooldownBarrelModifier = 2.5f; +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_Thrown_C4.uc b/KFGameContent/Classes/KFWeap_Thrown_C4.uc index 518baf9..d028c37 100644 --- a/KFGameContent/Classes/KFWeap_Thrown_C4.uc +++ b/KFGameContent/Classes/KFWeap_Thrown_C4.uc @@ -474,7 +474,7 @@ defaultproperties // Inventory / Grouping InventoryGroup=IG_Equipment - GroupPriority=25 + GroupPriority=11 WeaponSelectTexture=Texture2D'WEP_UI_C4_TEX.UI_WeaponSelect_C4' InventorySize=3 diff --git a/KFGameContent/Classes/KFZedArmorInfo_BloatKing.uc b/KFGameContent/Classes/KFZedArmorInfo_BloatKing.uc index 2f0bdfb..b15c522 100644 --- a/KFGameContent/Classes/KFZedArmorInfo_BloatKing.uc +++ b/KFGameContent/Classes/KFZedArmorInfo_BloatKing.uc @@ -88,4 +88,5 @@ defaultproperties //Special cases ArmorDamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_HRGHealthrower', DamageScale=(1.2))) ArmorDamageTypeModifiers.Add((DamageType=class'KFDT_Bleeding_Hemogoblin', DamageScale=(1.0))) + ArmorDamageTypeModifiers.Add((DamageType=class'KFDT_Shrink_ShrinkRayGun', DamageScale=(5.0))) } diff --git a/KFGameContent/Classes/KFZedArmorInfo_Matriarch.uc b/KFGameContent/Classes/KFZedArmorInfo_Matriarch.uc index 6582bfd..a562789 100644 --- a/KFGameContent/Classes/KFZedArmorInfo_Matriarch.uc +++ b/KFGameContent/Classes/KFZedArmorInfo_Matriarch.uc @@ -71,4 +71,7 @@ defaultproperties //special case ArmorDamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_MicrowaveRifle', DamageScale=(0.7))) ArmorDamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_HRGHealthrower', DamageScale=(0.5))) + ArmorDamageTypeModifiers.Add((DamageType=class'KFDT_Shrink_ShrinkRayGun', DamageScale=(5.0))) + ArmorDamageTypeModifiers.Add((DamageType=class'KFDT_Blast_HRG_CranialPopper', DamageScale=(0.0))) + ArmorDamageTypeModifiers.Add((DamageType=class'KFDT_Piercing_HRG_CranialPopper',DamageScale=(2.0))) }