diff --git a/Engine/Classes/OnlineGameSettings.uc b/Engine/Classes/OnlineGameSettings.uc index 89014ad..dc28d9d 100644 --- a/Engine/Classes/OnlineGameSettings.uc +++ b/Engine/Classes/OnlineGameSettings.uc @@ -140,6 +140,7 @@ var databinding string Region; //@HSL_END var databinding bool bNoSeasonalSkins; +var databinding int WeeklySelectorIndex; /** Represents a player in the game */ struct native PlayerResult @@ -186,4 +187,5 @@ defaultproperties bServerExiled=false //@SABER_END bNoSeasonalSkins=false + WeeklySelectorIndex=-1 } diff --git a/Engine/Classes/OnlineSubsystem.uc b/Engine/Classes/OnlineSubsystem.uc index 2443313..96511c9 100644 --- a/Engine/Classes/OnlineSubsystem.uc +++ b/Engine/Classes/OnlineSubsystem.uc @@ -2315,6 +2315,7 @@ static function DumpGameSettings(const OnlineGameSettings GameSettings) `Log(" bAllowJoinViaPresenceFriendsOnly: "$GameSettings.bAllowJoinViaPresenceFriendsOnly); `Log(" GameState: "$GameSettings.GameState); `Log(" bNoSeasonalSkins: "$GameSettings.bNoSeasonalSkins); + `Log(" WeeklySelectorIndex: "$GameSettings.WeeklySelectorIndex); } /** diff --git a/Engine/Classes/Pawn.uc b/Engine/Classes/Pawn.uc index 6c8ddda..b29e278 100644 --- a/Engine/Classes/Pawn.uc +++ b/Engine/Classes/Pawn.uc @@ -2543,10 +2543,11 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector if ( damagetype == None ) { - if ( InstigatedBy == None ) + /*if ( InstigatedBy == None ) `warn("No damagetype for damage with no instigator"); else `warn("No damagetype for damage by "$instigatedby.pawn$" with weapon "$InstigatedBy.Pawn.Weapon); + */ //scripttrace(); DamageType = class'DamageType'; } diff --git a/KFGame/Classes/AICommand_Flee.uc b/KFGame/Classes/AICommand_Flee.uc index 81f4bd2..77265cf 100644 --- a/KFGame/Classes/AICommand_Flee.uc +++ b/KFGame/Classes/AICommand_Flee.uc @@ -38,6 +38,8 @@ var bool bHaveGoal; /** A randomized distance outside of the target goal to stop at */ var float GoalOffset; +var bool bUseRandomDirection; + /********************************************************************************************* * Initialization ********************************************************************************************* */ @@ -47,7 +49,8 @@ static function bool FleeFrom( actor inFleeTarget, optional float inFleeDuration, optional float inFleeDistance=5000, - optional bool bShouldStopAtGoal=false ) + optional bool bShouldStopAtGoal=false, + optional bool bHasToUseRandomDirection=false ) { local AICommand_Flee Cmd; @@ -60,6 +63,7 @@ static function bool FleeFrom( Cmd.FleeDistance = inFleeDistance; Cmd.FleeDuration = inFleeDuration; Cmd.bStopAtGoal = bShouldStopAtGoal; + Cmd.bUseRandomDirection = bHasToUseRandomDirection; AI.PushCommand(Cmd); return true; } @@ -113,6 +117,84 @@ function Popped() EnableMeleeRangeEventProbing(); } +function vector GetRandomFleeVector() +{ + local vector VectorReference, VectorReferenceRotated, RandVector, HitLocation, HitNormal, Destination, DestinationRotated, AxxisRotation; + local Actor HitActor; + local int i, NumIterations; + local float Angle, LengthFlee; + local Quat R; + + // Start on the bearing of the FleeTarget, check for collision then explore to the sides rotating that vector + + VectorReference = Normal(Pawn.Location - FleeTarget.Location); + VectorReference.z = 0.f; + + RandVector = VectorReference; + + LengthFlee = 300.f; + //LengthFlee = FleeDistance; + + Destination = Pawn.Location + VectorReference * LengthFlee; + + HitActor = Trace(HitLocation, HitNormal, Destination, Pawn.Location, true,,, TRACEFLAG_Blocking); + if (HitActor != none && KFPawnBlockingVolume(HitActor) != none) + { + //HitActor.DrawDebugLine(Pawn.Location, Destination, 255, 0, 0, true ); + + Angle = 15.f * DegToRad; + + NumIterations = 180.f * DegToRad / Angle; + + AxxisRotation.x = 0.f; + AxxisRotation.y = 0.f; + AxxisRotation.z = 1.f; + + for (i = 1; i < NumIterations; ++i) + { + // Left + + R = QuatFromAxisAndAngle(AxxisRotation, i * Angle); + VectorReferenceRotated = Normal(QuatRotateVector(R, VectorReference)); + + DestinationRotated = Pawn.Location + VectorReferenceRotated * LengthFlee; + + HitActor = Trace(HitLocation, HitNormal, DestinationRotated, Pawn.Location, true,,, TRACEFLAG_Blocking); + if (HitActor != none && KFPawnBlockingVolume(HitActor) != none) + { + //Pawn.DrawDebugLine(Pawn.Location, DestinationRotated, 255, 255, 0, true ); + } + else + { + RandVector = DestinationRotated; + break; + } + + // Right + + R = QuatFromAxisAndAngle(AxxisRotation, - i * Angle); + VectorReferenceRotated = Normal(QuatRotateVector(R, VectorReference)); + + DestinationRotated = Pawn.Location + VectorReferenceRotated * LengthFlee; + + HitActor = Trace(HitLocation, HitNormal, DestinationRotated, Pawn.Location, true,,, TRACEFLAG_Blocking); + if (HitActor != none && KFPawnBlockingVolume(HitActor) != none) + { + //Pawn.DrawDebugLine(Pawn.Location, DestinationRotated, 0, 255, 255, true ); + } + else + { + RandVector = DestinationRotated; + break; + } + } + } + + //Pawn.DrawDebugLine(Pawn.Location, Pawn.Location + RandVector * LengthFlee, 0, 255, 0, true ); + + return Normal(RandVector); +} + /********************************************************************************************* * Hiding state ********************************************************************************************* */ @@ -129,6 +211,7 @@ state Fleeing function bool CheckRetreat() { local EPathSearchType OldSearchType; + local vector RandVector; if( RouteGoal != none && bHaveGoal ) { @@ -154,13 +237,27 @@ state Fleeing AIActionStatus = "Searching for navigable path from ["$FleeTarget$"]"; Pawn.PathSearchType = PST_Constraint; - class'Path_AlongLine'.static.AlongLine( Pawn, Normal(Pawn.Location - FleeTarget.Location) ); - class'Goal_AwayFromPosition'.static.FleeFrom( Pawn, FleeTarget.Location, FleeDistance ); + + if (bUseRandomDirection) + { + MyKFPawn.SetSprinting( true ); + + RandVector = GetRandomFleeVector(); + + class'Path_AlongLine'.static.AlongLine( Pawn, RandVector ); + class'Goal_Random'.static.FindRandom( Pawn ); + } + else + { + class'Path_AlongLine'.static.AlongLine( Pawn, Normal(Pawn.Location - FleeTarget.Location) ); + class'Goal_AwayFromPosition'.static.FleeFrom( Pawn, FleeTarget.Location, FleeDistance ); + } if( FindPathToward( Pawn ) != None ) { //Pawn.DrawDebugLine( RouteGoal.Location, Pawn.Location, 255, 80, 80, true ); //Pawn.DrawDebugSphere( RouteGoal.Location, 128, 4, 255, 80, 80, true ); + bHaveGoal = true; AIActionStatus = "Attempting to flee from ["$FleeTarget$"] at ["$RouteGoal$"]"; Focus = none; @@ -281,4 +378,5 @@ Begin: DefaultProperties { bAllowedToAttack=false + bUseRandomDirection=false } \ No newline at end of file diff --git a/KFGame/Classes/AICommand_TauntEnemy.uc b/KFGame/Classes/AICommand_TauntEnemy.uc index f36ed62..b701150 100644 --- a/KFGame/Classes/AICommand_TauntEnemy.uc +++ b/KFGame/Classes/AICommand_TauntEnemy.uc @@ -24,6 +24,14 @@ static function bool Taunt( KFAIController AI, optional Pawn inTauntTarget, opti if( AI != None ) { + if (AI.MyKFPawn != none) + { + if (AI.MyKFPawn.bIsBountyHuntObjective) + { + return false; + } + } + Cmd = new(AI) default.class; if( Cmd != None ) { diff --git a/KFGame/Classes/KFAIController.uc b/KFGame/Classes/KFAIController.uc index 12f5c28..1333267 100644 --- a/KFGame/Classes/KFAIController.uc +++ b/KFGame/Classes/KFAIController.uc @@ -2800,9 +2800,10 @@ function DoFleeFrom( actor FleeFrom, optional float FleeDuration, optional float FleeDistance, optional bool bShouldStopAtGoal=false, - optional bool bFromFear=false ) + optional bool bFromFear=false, + optional bool bUseRandomDirection=false ) { - class'AICommand_Flee'.static.FleeFrom( self, FleeFrom, FleeDuration, FleeDistance, bShouldStopAtGoal ); + class'AICommand_Flee'.static.FleeFrom( self, FleeFrom, FleeDuration, FleeDistance, bShouldStopAtGoal, bUseRandomDirection ); } /* @@ -5555,6 +5556,11 @@ function HardCoreCheckStuckTimer() function bool AmIAllowedToSuicideWhenStuck() { + if (MyKFPawn.bIsBountyHuntObjective) + { + return false; + } + return true; } diff --git a/KFGame/Classes/KFAIController_Hans.uc b/KFGame/Classes/KFAIController_Hans.uc index 100b4f2..a34f10a 100644 --- a/KFGame/Classes/KFAIController_Hans.uc +++ b/KFGame/Classes/KFAIController_Hans.uc @@ -1609,11 +1609,12 @@ function DoFleeFrom( actor FleeFrom, optional float FleeDuration, optional float FleeDistance, optional bool bShouldStopAtGoal=false, - optional bool bFromFear=false ) + optional bool bFromFear=false, + optional bool bUseRandomDirection=false ) { if( !bFromFear || !MyHansPawn.bInHuntAndHealMode ) { - super.DoFleeFrom( FleeFrom, FleeDuration, FleeDistance, bShouldStopAtGoal, bFromFear ); + super.DoFleeFrom( FleeFrom, FleeDuration, FleeDistance, bShouldStopAtGoal, bFromFear, bUseRandomDirection ); } } diff --git a/KFGame/Classes/KFAISpawnManager.uc b/KFGame/Classes/KFAISpawnManager.uc index 4429950..2277976 100644 --- a/KFGame/Classes/KFAISpawnManager.uc +++ b/KFGame/Classes/KFAISpawnManager.uc @@ -110,6 +110,7 @@ enum EAIType AT_EDAR_EMP, AT_EDAR_Laser, AT_EDAR_Rocket, + AT_HansClot }; /** IDs into the BossAIClassList array */ @@ -234,6 +235,8 @@ static function string ZedTypeToString(EAIType AiTypeToConvert) return "Husk"; case AT_BossRandom: return "Hans"; + case AT_HansClot: + return "HansClot"; } return "CLOT"; `endif @@ -479,7 +482,7 @@ function GetSpawnListFromSquad(byte SquadIdx, out array< KFAISpawnSquad > Squads else `endif //Always have the squad type be a boss if we're spawning one in case of override - if (OutbreakEvent.ActiveEvent.bBossRushMode) + if (OutbreakEvent != none && OutbreakEvent.ActiveEvent.bBossRushMode) { RandBossIndex = Rand(BossRushEnemies.length); TempSpawnList.AddItem( default.AIBossClassList[BossRushEnemies[RandBossIndex]]); diff --git a/KFGame/Classes/KFAffliction_BigHead.uc b/KFGame/Classes/KFAffliction_BigHead.uc index bda60e6..0a9ae9a 100644 --- a/KFGame/Classes/KFAffliction_BigHead.uc +++ b/KFGame/Classes/KFAffliction_BigHead.uc @@ -118,7 +118,7 @@ function float GetDamageModifier() defaultproperties { - DissipationRate=10 + DissipationRate=50 bNeedsTick=true MaxStack=3 @@ -126,7 +126,7 @@ defaultproperties EffectAppliedByStack=0.3f ApplyEffectVel= 1.0f // % per second - RemoveEffectVel= 1.0f // % per second + RemoveEffectVel= 0.9f // % per second CurrentStack=0 CurrentEffectApplied=0.0f diff --git a/KFGame/Classes/KFAffliction_Shrink.uc b/KFGame/Classes/KFAffliction_Shrink.uc index d90f655..58ee778 100644 --- a/KFGame/Classes/KFAffliction_Shrink.uc +++ b/KFGame/Classes/KFAffliction_Shrink.uc @@ -171,7 +171,7 @@ defaultproperties EffectAppliedByStack=1.0f ApplyEffectVel=100.0f // % per second - RemoveEffectVel=0.25f // % per second + RemoveEffectVel=0.5f // % per second CurrentEffect=0.0f CurrentEffectApplied=0.0f diff --git a/KFGame/Classes/KFAutoPurchaseHelper.uc b/KFGame/Classes/KFAutoPurchaseHelper.uc index e13cf98..21976a6 100644 --- a/KFGame/Classes/KFAutoPurchaseHelper.uc +++ b/KFGame/Classes/KFAutoPurchaseHelper.uc @@ -32,7 +32,7 @@ var int DoshBuffer; var int ArmorMagSize; // Percentage of armor bought individually -function Initialize(optional bool bInitOwnedItems = true) +function Initialize(optional bool bInitializeOwned = true) { if( Pawn != none && PlayerReplicationInfo != none ) { @@ -42,10 +42,11 @@ function Initialize(optional bool bInitOwnedItems = true) if( MyKFPRI != none && MyKFIM != none ) { // Grab the weapons in our inventory and add them to a stored array called OwnedItemList - if (bInitOwnedItems) + if (bInitializeOwned) { InitializeOwnedItemList(); } + TotalBlocks = MyKFIM.CurrentCarryBlocks; TotalDosh = MyKFPRI.Score; MaxBlocks = MyKFIM.MaxCarryBlocks; @@ -989,17 +990,36 @@ function Int GetAutoFillCost() @Name - General *******************/ +function InitializeOwnedGrenade() +{ + local bool AllowGrenades; + local KFPawn_Human KFP; + + AllowGrenades = KFGameReplicationInfo( WorldInfo.GRI ).bAllowGrenadePurchase; + + KFP = KFPawn_Human( Pawn ); + if( KFP != none ) + { + // init grenade purchase values + GrenadeItem.SpareAmmoCount = MyKFIM.GrenadeCount; + GrenadeItem.MaxSpareAmmo = AllowGrenades ? KFP.GetPerk().MaxGrenadeCount : 0; + GrenadeItem.AmmoPricePerMagazine = TraderItems.GrenadePrice; + GrenadeItem.DefaultItem.WeaponDef = CurrentPerk.GetGrenadeWeaponDef(); + + // @temp: fill in stuff that is normally serialized in the archetype + GrenadeItem.DefaultItem.AssociatedPerkClasses[0] = CurrentPerk.Class; + } +} + function InitializeOwnedItemList() { local Inventory Inv; local KFWeapon KFW; local KFPawn_Human KFP; - local bool AllowGrenades; OwnedItemList.length = 0; TraderItems = KFGameReplicationInfo( WorldInfo.GRI ).TraderItems; - AllowGrenades = KFGameReplicationInfo( WorldInfo.GRI ).bAllowGrenadePurchase; KFP = KFPawn_Human( Pawn ); if( KFP != none ) @@ -1010,14 +1030,7 @@ function InitializeOwnedItemList() ArmorItem.AmmoPricePerMagazine = TraderItems.ArmorPrice * CurrentPerk.GetArmorDiscountMod(); ArmorItem.DefaultItem.WeaponDef = TraderItems.ArmorDef; - // init grenade purchase values - GrenadeItem.SpareAmmoCount = MyKFIM.GrenadeCount; - GrenadeItem.MaxSpareAmmo = AllowGrenades ? KFP.GetPerk().MaxGrenadeCount : 0; - GrenadeItem.AmmoPricePerMagazine = TraderItems.GrenadePrice; - GrenadeItem.DefaultItem.WeaponDef = CurrentPerk.GetGrenadeWeaponDef(); - - // @temp: fill in stuff that is normally serialized in the archetype - GrenadeItem.DefaultItem.AssociatedPerkClasses[0] = CurrentPerk.Class; + InitializeOwnedGrenade(); for ( Inv = MyKFIM.InventoryChain; Inv != none; Inv = Inv.Inventory ) { @@ -1387,7 +1400,8 @@ function RemoveWeaponFromOwnedItemList( optional int OwnedListIdx = INDEX_NONE, // add a single to owned items when removing a dual //if( ItemInfo.DefaultItem.SingleClassName != '' ) - if( ItemInfo.DefaultItem.SingleClassName == 'KFWeap_Pistol_9mm' ) + if( ItemInfo.DefaultItem.SingleClassName == 'KFWeap_Pistol_9mm' + || ItemInfo.DefaultItem.SingleClassName == 'KFWeap_HRG_93R' ) { // When removing a dual, always add a single to the owned list so that it shows up in the player inventory UI. // If we don't own the single, then also buy it (add it to the transaction list). diff --git a/KFGame/Classes/KFCheatManager.uc b/KFGame/Classes/KFCheatManager.uc index c291120..a0178d1 100644 --- a/KFGame/Classes/KFCheatManager.uc +++ b/KFGame/Classes/KFCheatManager.uc @@ -5063,6 +5063,10 @@ function class LoadMonsterByName(string ZedName, optional bool b { SpawnClass = class(DynamicLoadObject("KFGameContent.KFPawn_ZedHusk_New"$VersusSuffix, class'Class')); } + else if (Left(ZedName, 5) ~= "HansC") + { + SpawnClass = class(DynamicLoadObject("KFGameContent.KFPawn_ZedHansClot" $ VersusSuffix, class'Class')); + } else if( Left(ZedName, 2) ~= "Ha" ) { SpawnClass = class(DynamicLoadObject("KFGameContent.KFPawn_ZedHans"$VersusSuffix, class'Class')); diff --git a/KFGame/Classes/KFCommon_LocalizedStrings.uc b/KFGame/Classes/KFCommon_LocalizedStrings.uc index 708a403..a015e10 100644 --- a/KFGame/Classes/KFCommon_LocalizedStrings.uc +++ b/KFGame/Classes/KFCommon_LocalizedStrings.uc @@ -33,6 +33,7 @@ var localized array PermissionStrings; var localized array ConsolePermissionStrings; var localized array ModeStrings; var localized array AllowSeasonalSkinsStrings; +var localized array WeeklySelectorStrings; var localized string TeamSwappedString; var localized string NoPreferenceString; @@ -200,11 +201,26 @@ static function string GetAllowSeasonalSkinsString( float Index ) return default.NoPreferenceString; } +static function string GetWeeklySelectorString( float Index ) +{ + if( 0 < default.WeeklySelectorStrings.length && Index < default.WeeklySelectorStrings.length ) + { + return default.WeeklySelectorStrings[Index]; + } + + return default.NoPreferenceString; +} + static function array GetAllowSeasonalSkinsStringsArray() { return default.AllowSeasonalSkinsStrings; } +static function array GetWeeklySelectorStringsArray() +{ + return default.WeeklySelectorStrings; +} + static function array GetGameModeStringsArray() { return default.ModeStrings; diff --git a/KFGame/Classes/KFDamageType.uc b/KFGame/Classes/KFDamageType.uc index 4183c0e..a748f91 100644 --- a/KFGame/Classes/KFDamageType.uc +++ b/KFGame/Classes/KFDamageType.uc @@ -363,6 +363,11 @@ static function int GetDamageeDialogID() return INDEX_NONE; } +/** Called when damage is dealt to apply additional instant damage */ +static function ModifyInstantDamage( KFPawn Victim, out int DamageTaken, optional Controller InstigatedBy ) +{ +} + /** 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 ) { diff --git a/KFGame/Classes/KFDroppedPickup.uc b/KFGame/Classes/KFDroppedPickup.uc index 5b6f152..0b11e53 100644 --- a/KFGame/Classes/KFDroppedPickup.uc +++ b/KFGame/Classes/KFDroppedPickup.uc @@ -389,6 +389,11 @@ function GiveTo(Pawn P) local Inventory NewInventory; local KFInventoryManager KFIM; local KFGameReplicationInfo KFGRI; + local class NewInventoryClass; + + NewInventoryClass = InventoryClass; + + // For HRG93R and 9mm pistols, if one of the other type is picked just give the one owned KFGRI = KFGameReplicationInfo(WorldInfo.GRI); if (KFGRI != none && KFGRI.bIsEndlessPaused) @@ -399,10 +404,33 @@ function GiveTo(Pawn P) KFIM = KFInventoryManager(P.InvManager); if (KFIM != None) { - KFWInvClass = class(InventoryClass); + if (KFIM.Is9mmInInventory()) + { + if (InventoryClass.name == 'KFWeap_HRG_93R') + { + NewInventoryClass = class(DynamicLoadObject(class'KfWeapDef_9mm'.default.WeaponClassPath, class'Class')); + } + else if (InventoryClass.name == 'KFWeap_HRG_93R_Dual') + { + NewInventoryClass = class(DynamicLoadObject(class'KfWeapDef_9mmDual'.default.WeaponClassPath, class'Class')); + } + } + else + { + if(InventoryClass.name == 'KFWeap_Pistol_9mm') + { + NewInventoryClass = class(DynamicLoadObject(class'KFWeapDef_HRG_93R'.default.WeaponClassPath, class'Class')); + } + else if (InventoryClass.name == 'KFWeap_Pistol_Dual9mm') + { + NewInventoryClass = class(DynamicLoadObject(class'KFWeapDef_HRG_93R_Dual'.default.WeaponClassPath, class'Class')); + } + } + + KFWInvClass = class(NewInventoryClass); foreach KFIM.InventoryActors(class'KFWeapon', KFW) { - if (KFW.Class == InventoryClass) + if (KFW.Class == NewInventoryClass) { // if this isn't a dual-wield class, then we can't carry another if (KFW.DualClass == none) @@ -426,7 +454,7 @@ function GiveTo(Pawn P) return; } - NewInventory = KFIM.CreateInventory(InventoryClass, true); + NewInventory = KFIM.CreateInventory(NewInventoryClass, true); if (NewInventory != none) { // Added extra check in case we want to pick up a non-weapon based pickup diff --git a/KFGame/Classes/KFGFxHUD_WaveInfo.uc b/KFGame/Classes/KFGFxHUD_WaveInfo.uc index f907252..e89229d 100644 --- a/KFGame/Classes/KFGFxHUD_WaveInfo.uc +++ b/KFGame/Classes/KFGFxHUD_WaveInfo.uc @@ -128,6 +128,7 @@ function UpdateWaveCount() function UpdateZEDCount() { local int CurrentZEDCount; + local KFPlayerController_WeeklySurvival KFPC_WS; if( KFGRI == none ) { @@ -149,6 +150,16 @@ function UpdateZEDCount() // # of ZEDs left to kill in this wave. CurrentZEDCount = KFGRI.AIRemaining; + + if (KFGRI.IsBountyHunt()) + { + KFPC_WS = KFPlayerController_WeeklySurvival(KFPC); + if (KFPC_WS != none) + { + CurrentZEDCount += KFPC_WS.BountyHuntCurrentExtraZeds; + } + } + if(LastZEDCount != CurrentZEDCount) { SetInt("remainingZEDs" ,CurrentZEDCount); diff --git a/KFGame/Classes/KFGFxMenu_Perks.uc b/KFGame/Classes/KFGFxMenu_Perks.uc index 8c6eac4..21c8eb9 100644 --- a/KFGame/Classes/KFGFxMenu_Perks.uc +++ b/KFGame/Classes/KFGFxMenu_Perks.uc @@ -23,6 +23,7 @@ var KFPlayerController KFPC; var const string LockIconPath; var byte LastPerkIndex; +var byte PreviewPerkIndex; VAR byte LastPerkLevel; var class PreviousPerk; var localized string TierUnlockedText; @@ -83,7 +84,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, SelectedSkillsHolder, false, false); + DetailsContainer.UpdateDetails(PerkClass, SelectedSkillsHolder, false, false, true); DetailsContainer.UpdatePassives(PerkClass); } break; @@ -228,6 +229,7 @@ event OnClose() if (bModifiedWeaponIndexes) { Manager.CachedProfile.SetProfileSettingValueInt( KFID_SurvivalStartingWeapIdx, KFPC.SurvivalPerkWeapIndex ); + Manager.CachedProfile.SetProfileSettingValueInt( KFID_SurvivalStartingSecondaryWeapIdx, KFPC.SurvivalPerkSecondaryWeapIndex ); Manager.CachedProfile.SetProfileSettingValueInt( KFID_SurvivalStartingGrenIdx, KFPC.SurvivalPerkGrenIndex ); } @@ -294,6 +296,8 @@ function PerkChanged( byte NewPerkIndex, bool bClickedIndex) } } + PreviewPerkIndex = NewPerkIndex; + UpdateContainers( KFPC.PerkList[NewPerkIndex].PerkClass, bClickedIndex ); } } @@ -340,7 +344,7 @@ function UpdateContainers( class PerkClass, optional bool bClickedIndex= if( DetailsContainer != none ) { - DetailsContainer.UpdateDetails( PerkClass, SelectedSkillsHolder, false, false ); + DetailsContainer.UpdateDetails( PerkClass, SelectedSkillsHolder, false, false, true ); DetailsContainer.UpdatePassives( PerkClass ); } @@ -480,10 +484,7 @@ function Callback_SkillSelected( byte TierIndex, byte SkillIndex ) UpdateSkillsUI(KFPC.PerkList[LastPerkIndex].PerkClass); SavePerkData(); - if ( KFPC.PerkList[LastPerkIndex].PerkClass.Name == 'KFPerk_Survivalist' ) - { - DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, false, false ); - } + DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, false, false, true ); } } @@ -496,50 +497,207 @@ function Callback_SkillSelectionOpened() } } +function bool CanChooseWeapons() +{ + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo(KFPC.WorldInfo.GRI); + + if (KFPC != none) + { + if (KFPC.Pawn.IsAliveAndWell() == false + || KFPC.PlayerReplicationInfo.bIsSpectator + || KFPC.PlayerReplicationInfo.bOnlySpectator) + { + return true; + } + } + + if (KFGRI != none) + { + if (KFGRI.WaveNum == 0) + { + if (KFGRI.bWaveStarted) + { + return false; + } + } + else + { + return false; + } + } + + return true; +} + function OnPrevWeaponPressed() { local byte NewIndex; + local bool bUpdateUI; + + if (CanChooseWeapons() == false) + { + return; + } + + bUpdateUI = true; + + if (GetPC().PlayerInput.bUsingGamepad) + { + if (PreviewPerkIndex != LastPerkIndex) + { + bUpdateUI = false; + } + } NewIndex = KFPC.CurrentPerk.OnPrevWeaponSelected(); KFPC.SurvivalPerkWeapIndex = NewIndex; - DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, true, false ); + DetailsContainer.UpdateDetails( KFPC.PerkList[LastPerkIndex].PerkClass, SelectedSkillsHolder, true, false, bUpdateUI ); bModifiedWeaponIndexes=true; } function OnNextWeaponPressed() { local byte NewIndex; + local bool bUpdateUI; + + if (CanChooseWeapons() == false) + { + return; + } + + bUpdateUI = true; + + if (GetPC().PlayerInput.bUsingGamepad) + { + if (PreviewPerkIndex != LastPerkIndex) + { + bUpdateUI = false; + } + } NewIndex = KFPC.CurrentPerk.OnNextWeaponSelected(); KFPC.SurvivalPerkWeapIndex = NewIndex; - DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, false, true ); + DetailsContainer.UpdateDetails( KFPC.PerkList[LastPerkIndex].PerkClass, SelectedSkillsHolder, false, true, bUpdateUI ); + bModifiedWeaponIndexes=true; +} + +function OnPrevSecondaryWeaponPressed() +{ + local byte NewIndex; + local bool bUpdateUI; + + if (CanChooseWeapons() == false) + { + return; + } + + bUpdateUI = true; + + if (GetPC().PlayerInput.bUsingGamepad) + { + if (PreviewPerkIndex != LastPerkIndex) + { + bUpdateUI = false; + } + } + + NewIndex = KFPC.CurrentPerk.OnPrevSecondaryWeaponSelected(); + KFPC.SurvivalPerkSecondaryWeapIndex = NewIndex; + + DetailsContainer.UpdateDetails( KFPC.PerkList[LastPerkIndex].PerkClass, SelectedSkillsHolder, true, false, bUpdateUI ); + bModifiedWeaponIndexes=true; +} + +function OnNextSecondaryWeaponPressed() +{ + local byte NewIndex; + local bool bUpdateUI; + + if (CanChooseWeapons() == false) + { + return; + } + + bUpdateUI = true; + + if (GetPC().PlayerInput.bUsingGamepad) + { + if (PreviewPerkIndex != LastPerkIndex) + { + bUpdateUI = false; + } + } + + NewIndex = KFPC.CurrentPerk.OnNextSecondaryWeaponSelected(); + KFPC.SurvivalPerkSecondaryWeapIndex = NewIndex; + + DetailsContainer.UpdateDetails( KFPC.PerkList[LastPerkIndex].PerkClass, SelectedSkillsHolder, false, true, bUpdateUI ); bModifiedWeaponIndexes=true; } function OnPrevGrenadePressed() { local byte NewIndex; + local bool bUpdateUI; + + bUpdateUI = true; + + if (GetPC().PlayerInput.bUsingGamepad) + { + if (PreviewPerkIndex != LastPerkIndex) + { + bUpdateUI = false; + } + } NewIndex = KFPC.CurrentPerk.OnPrevGrenadeSelected(); KFPC.SurvivalPerkGrenIndex = NewIndex; - - DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, true, false ); + + DetailsContainer.UpdateDetails( KFPC.PerkList[LastPerkIndex].PerkClass, SelectedSkillsHolder, true, false, bUpdateUI ); bModifiedWeaponIndexes=true; } function OnNextGrenadePressed() { local byte NewIndex; + local bool bUpdateUI; + + bUpdateUI = true; + + if (GetPC().PlayerInput.bUsingGamepad) + { + if (PreviewPerkIndex != LastPerkIndex) + { + bUpdateUI = false; + } + } NewIndex = KFPC.CurrentPerk.OnNextGrenadeSelected(); KFPC.SurvivalPerkGrenIndex = NewIndex; - DetailsContainer.UpdateDetails( KFPC.CurrentPerk.Class, SelectedSkillsHolder, false, true ); + DetailsContainer.UpdateDetails( KFPC.PerkList[LastPerkIndex].PerkClass, SelectedSkillsHolder, false, true, bUpdateUI ); bModifiedWeaponIndexes=true; } +function OnDpadPressed(int Right) +{ + if (GetPC().PlayerInput.bUsingGamepad) + { + if (Right > 0) + { + OnNextSecondaryWeaponPressed(); + } + else + { + OnPrevSecondaryWeaponPressed(); + } + } +} + event bool OnAxisModified( int ControllerId, name Key, float Delta, float DeltaTime, bool bGamepad ) { if (GetPC().PlayerInput.bUsingGamepad ) @@ -611,6 +769,7 @@ defaultproperties SubWidgetBindings.Add((WidgetName="DetailsContainer",WidgetClass= class'KFGFxPerksContainer_Details')) SubWidgetBindings.Add((WidgetName="SelectedPerkSummaryContainer",WidgetClass=class'KFGFxPerksContainer_SkillsSummary')) LastPerkIndex=255 + PreviewPerkIndex=255 LastPerkLevel=255 bAxisResetLeft=true diff --git a/KFGame/Classes/KFGFxMenu_PostGameReport.uc b/KFGame/Classes/KFGFxMenu_PostGameReport.uc index ed73c67..6450d53 100644 --- a/KFGame/Classes/KFGFxMenu_PostGameReport.uc +++ b/KFGame/Classes/KFGFxMenu_PostGameReport.uc @@ -79,6 +79,11 @@ function LocalizeText() { local GFxObject TextObject; local WorldInfo WI; + local bool bIsCustomWeekly; + local int IntendedWeeklyIndex, WeeklyIndex; + local KFPlayerController KFPC; + + KFPC = KFPlayerController(GetPC()); WI = class'WorldInfo'.static.GetWorldInfo(); @@ -90,7 +95,34 @@ function LocalizeText() TextObject.SetString("xp", XPString); TextObject.SetString("teamAwards", TeamAwardsString); - TextObject.SetString("dropTitle", ItemDropTitleString); + IntendedWeeklyIndex = class'KFGameEngine'.static.GetIntendedWeeklyEventIndexMod(); + WeeklyIndex = -1; + + if (KFPC.WorldInfo.NetMode == NM_Client) + { + if (KFGameReplicationInfo(WI.GRI) != none) + { + WeeklyIndex = KFGameReplicationInfo(WI.GRI).CurrentWeeklyIndex; + } + } + else + { + WeeklyIndex = class'KFGameEngine'.static.GetWeeklyEventIndexMod(); + } + + if (WeeklyIndex != -1) + { + bIsCustomWeekly = IntendedWeeklyIndex != WeeklyIndex; + } + + if (bIsCustomWeekly) + { + TextObject.SetString("dropTitle", ""); + } + else + { + TextObject.SetString("dropTitle", ItemDropTitleString); + } if(WI != none && WI.NetMode != NM_Standalone ) { diff --git a/KFGame/Classes/KFGFxMenu_ServerBrowser.uc b/KFGame/Classes/KFGFxMenu_ServerBrowser.uc index 95194da..612057f 100644 --- a/KFGame/Classes/KFGFxMenu_ServerBrowser.uc +++ b/KFGame/Classes/KFGFxMenu_ServerBrowser.uc @@ -31,6 +31,7 @@ var localized string BackString; var localized string ApplyString; var localized string ResetString; var localized string PingString; +var localized string WeeklyIndexString; var localized string LengthString; var localized string DifficultyString; var localized string MapString; @@ -263,6 +264,12 @@ function Callback_PingFilter(int FilterIndex) FiltersContainer.PingChanged(FilterIndex); } +function Callback_WeeklySelectorChanged(int FilterIndex) +{ + `log("WeeklySelector" @ FilterIndex, bLogServerBrowser); + FiltersContainer.WeeklySelectorChanged(FilterIndex); +} + function CallBack_SearchTabChanged(int TabIndex) { `log("SEARCH TAB CHANGED: " @TabIndex, bLogServerBrowser); diff --git a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc index 7dad4a6..c1bdbd8 100644 --- a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc +++ b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc @@ -74,6 +74,8 @@ var KFGFxWidget_MapCounterText MapCounterTextWidget; var KFGFxWidget_GunGame GunGameWidget; // Widget that displays vip mode texts var KFGFxWidget_VIP VIPWidget; +// Widget that displays bounty hunt mode texts +var KFGFxWidget_BountyHunt BountyHuntWidget; var KFPlayerController KFPC; @@ -112,6 +114,9 @@ var transient bool bLastGunGameVisibility; // VIP variables var transient bool bLastVIPVisibility; +// Bounty Hunt variables +var transient bool bLastBountyHuntVisibility; + /** On creation of the HUD */ function Init(optional LocalPlayer LocPlay) { @@ -372,6 +377,13 @@ event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget) SetWidgetPathBinding( Widget, WidgetPath ); } break; + case 'BountyHuntContainer': + if (BountyHuntWidget == none) + { + BountyHuntWidget = KFGFxWidget_BountyHunt(Widget); + SetWidgetPathBinding( Widget, WidgetPath ); + } + break; } return true; @@ -400,12 +412,15 @@ function UpdateWeaponSelect() /** Update all the unique HUD pieces */ function TickHud(float DeltaTime) { - local bool bGunGameVisibility, bVIPModeVisibility; + local KFGameReplicationInfo KFGRI; + local bool bGunGameVisibility, bVIPModeVisibility, bBountyHuntVisibility; if(KFPC == none || KFPC.WorldInfo.TimeSeconds - LastUpdateTime < UpdateInterval ) { return; } + + KFGRI=KFGameReplicationInfo(KFPC.WorldInfo.GRI); if (WaveInfoWidget != none) { @@ -498,6 +513,26 @@ function TickHud(float DeltaTime) bLastVIPVisibility = bVIPModeVisibility; } } + + if (BountyHuntWidget != none) + { + bBountyHuntVisibility = KFPC.CanUseBountyHunt(); + + if (KFGRI.bWaveIsActive == false) + { + bBountyHuntVisibility = false; + } + else if (KFGRI.WaveNum == KFGRI.WaveMax) + { + bBountyHuntVisibility = false; + } + + if (bBountyHuntVisibility != bLastBountyHuntVisibility) + { + BountyHuntWidget.UpdateBountyHuntVisibility(bBountyHuntVisibility); + bLastBountyHuntVisibility = bBountyHuntVisibility; + } + } } function UpdateObjectiveActive() @@ -1001,6 +1036,36 @@ simulated function DisplayObjectiveResults() } } +simulated function DisplayBountyHuntObjective(int Bounties) +{ + local KFGameReplicationInfo KFGRI; + local GFxObject ObjectiveObject; + local string BountyHuntString; + + KFGRI=KFGameReplicationInfo(KFPC.WorldInfo.GRI); + + ObjectiveObject=CreateObject("Object"); + + ObjectiveObject.SetString("titleString", Localize("Objectives", "BountyHuntObjective", "KFGame")); + + if (KFGRI.IsBountyHunt()) + { + ObjectiveObject.SetString("nameString", Localize("Objectives", "BountyHuntObjective", "KFGame")); + + BountyHuntString = Localize("Objectives", "BountyHuntObjectiveDescription", "KFGame")@Bounties@Localize("Objectives", "BountyHuntDescriptionAdd", "KFGame"); + + ObjectiveObject.SetString("descString", BountyHuntString); + + ObjectiveObject.SetString("iconPath", "img://"$PathName(Texture2D'WeeklyObjective_UI.BountyHunt')); + } + + ObjectiveObject.SetBool("isBonus", false); + + KFGRI.PreviousObjectiveResult=INDEX_NONE; + PriorityMessageContainer.SetObject("objectiveMessage", ObjectiveObject); + LastMessageType=GMT_Null; +} + simulated function DisplayNewObjective() { local KFGameReplicationInfo KFGRI; @@ -1022,7 +1087,6 @@ simulated function DisplayNewObjective() ObjectiveObject.SetString("nameString", Localize("Objectives", "ContaminationModeObjective", "KFGame")); ObjectiveObject.SetString("descString", Localize("Objectives", "ContaminationModeDescription", "KFGame")); - // TODO :: CHANGE ICON ObjectiveObject.SetString("iconPath", "img://"$PathName(ObjectiveInterface.GetIcon())); } else @@ -1249,6 +1313,14 @@ function HideContaminationMode() } } +function DisplayBountyHuntStatus(int Bounties, int Dosh, int DoshNoAssist) +{ + if (BountyHuntWidget != none) + { + BountyHuntWidget.SetData(Bounties, Dosh, DoshNoAssist); + } +} + function ResetSyringe(float Ammo, float MaxAmmo) { if (PlayerStatusContainer != none) @@ -1613,6 +1685,7 @@ DefaultProperties bLastGunGameVisibility=true bLastVIPVisibility=true + bLastBountyHuntVisibility=true WidgetBindings.Add((WidgetName="ObjectiveContainer",WidgetClass=class'KFGFxHUD_ObjectiveConatiner')) WidgetBindings.Add((WidgetName="SpectatorInfoWidget",WidgetClass=class'KFGFxHUD_SpectatorInfo')) @@ -1640,6 +1713,7 @@ DefaultProperties WidgetBindings.Add((WidgetName="counterMapTextWidget", WidgetClass=class'KFGFxWidget_MapCounterText')) WidgetBindings.ADD((WidgetName="GunGameContainer", WidgetClass=class'KFGFxWidget_GunGame')); WidgetBindings.ADD((WidgetName="VIPContainer", WidgetClass=class'KFGFxWidget_VIP')); + WidgetBindings.ADD((WidgetName="BountyHuntContainer", WidgetClass=class'KFGFxWidget_BountyHunt')); SpecialWaveIconPath(AT_Clot)="UI_Endless_TEX.ZEDs.UI_ZED_Endless_Cyst" SpecialWaveIconPath(AT_SlasherClot)="UI_Endless_TEX.ZEDs.UI_ZED_Endless_Slasher" @@ -1653,6 +1727,7 @@ DefaultProperties SpecialWaveIconPath(AT_Bloat)="UI_Endless_TEX.ZEDs.UI_ZED_Endless_Bloat" SpecialWaveIconPath(AT_Siren)="UI_Endless_TEX.ZEDs.UI_ZED_Endless_Siren" SpecialWaveIconPath(AT_Husk)="UI_Endless_TEX.ZEDs.UI_ZED_Endless_Husk" + SpecialWaveIconPath(AT_HansClot)="UI_Endless_TEX.ZEDs.UI_ZED_Endless_Cyst" SpecialWaveLocKey(AT_SlasherClot)="KFPawn_ZedClot_Slasher" SpecialWaveLocKey(AT_Clot)="KFPawn_ZedClot_Cyst" @@ -1665,5 +1740,6 @@ DefaultProperties SpecialWaveLocKey(AT_GoreFast)="KFPawn_ZedGorefast" SpecialWaveLocKey(AT_Bloat)="KFPawn_ZedBloat" SpecialWaveLocKey(AT_FleshPound)="KFPawn_ZedFleshpound" + SpecialWaveLocKey(AT_HansClot)="KFPawn_ZedClot_Cyst" } diff --git a/KFGame/Classes/KFGFxPerksContainer_Details.uc b/KFGame/Classes/KFGFxPerksContainer_Details.uc index cbbebce..820ba67 100644 --- a/KFGame/Classes/KFGFxPerksContainer_Details.uc +++ b/KFGame/Classes/KFGFxPerksContainer_Details.uc @@ -25,12 +25,47 @@ function LocalizeContainer() GetObject("basicLoadoutTextField").SetString("text", BasicLoadoutString); } -function UpdateDetailsInternal(class PerkClass, KFPlayerController KFPC, byte WeaponIdx, byte GrenadeIdx) +function bool CanChooseWeapons(KFPlayerController KFPC) +{ + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo(KFPC.WorldInfo.GRI); + + if (KFPC != none) + { + if (KFPC.Pawn.IsAliveAndWell() == false + || KFPC.PlayerReplicationInfo.bIsSpectator + || KFPC.PlayerReplicationInfo.bOnlySpectator) + { + return true; + } + } + + if (KFGRI != none) + { + if (KFGRI.WaveNum == 0) + { + if (KFGRI.bWaveStarted) + { + return false; + } + } + else + { + return false; + } + } + + return true; +} + +function UpdateDetailsInternal(class PerkClass, KFPlayerController KFPC, byte WeaponIdx, byte SecondaryWeaponIdx, byte GrenadeIdx) { local GFxObject DetailsProvider; local array WeaponNames; local array WeaponSources; local int i; + local bool CanIChooseWeapons; DetailsProvider = CreateObject( "Object" ); @@ -41,9 +76,9 @@ function UpdateDetailsInternal(class PerkClass, KFPlayerController KFPC, AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.static.GetPrimaryWeaponName(WeaponIdx), PerkClass.static.GetPrimaryWeaponImagePath(WeaponIdx)); } - if(PerkClass.default.SecondaryWeaponDef != none) + if(PerkClass.default.SecondaryWeaponPaths.Length > 0) { - AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.default.SecondaryWeaponDef.static.GetItemName(), PerkClass.default.SecondaryWeaponDef.static.GetImagePath()); + AddWeaponInfo(WeaponNames, WeaponSources, PerkClass.static.GetSecondaryWeaponName(SecondaryWeaponIdx), PerkClass.static.GetSecondaryWeaponImagePath(SecondaryWeaponIdx)); } if(PerkClass.default.KnifeWeaponDef != none) { @@ -61,18 +96,22 @@ function UpdateDetailsInternal(class PerkClass, KFPlayerController KFPC, } DetailsProvider.SetString( "EXPAction1", PerkClass.default.EXPAction1 ); - DetailsProvider.SetString( "EXPAction2", PerkClass.default.EXPAction2 ); + DetailsProvider.SetString( "EXPAction2", PerkClass.default.EXPAction2 ); + + CanIChooseWeapons = CanChooseWeapons(KFPC); + + DetailsProvider.SetBool("ShowPrimaryWeaponSelectors", CanIChooseWeapons && PerkClass.static.CanChoosePrimaryWeapon()); + DetailsProvider.SetBool("ShowSecondaryWeaponSelectors", CanIChooseWeapons && PerkClass.static.CanChooseSecondaryWeapon()); - 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) +function UpdateDetails(class PerkClass, byte SelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext, bool UpdateUI) { local KFPlayerController KFPC; - local byte WeaponIdx, GrenadeIdx; + local byte WeaponIdx, SecondaryWeaponIdx, GrenadeIdx; KFPC = KFPlayerController( GetPC() ); @@ -82,11 +121,15 @@ function UpdateDetails(class PerkClass, byte SelectedSkills[`MAX_PERK_SK } WeaponIdx = 0; + SecondaryWeaponIdx = 0; GrenadeIdx = 0; - UpdateAndGetCurrentWeaponIndexes(PerkClass, KFPC, WeaponIdx, GrenadeIdx, SelectedSkills, IsChoosingPrev, IsChoosingNext); + UpdateAndGetCurrentWeaponIndexes(PerkClass, KFPC, WeaponIdx, SecondaryWeaponIdx, GrenadeIdx, SelectedSkills, IsChoosingPrev, IsChoosingNext); - UpdateDetailsInternal(PerkClass, KFPC, WeaponIdx, GrenadeIdx); + if (UpdateUI) + { + UpdateDetailsInternal(PerkClass, KFPC, WeaponIdx, SecondaryWeaponIdx, GrenadeIdx); + } } function AddWeaponInfo(out array WeaponNames, out array WeaponSources, string WeaponName, string WeaponSource) @@ -123,12 +166,35 @@ function UpdatePassives(Class PerkClass) SetObject( "passivesData", PassivesProvider ); } -function UpdateAndGetCurrentWeaponIndexes(class PerkClass, KFPlayerController KFPC, out byte WeaponIdx, out byte GrenadeIdx +function UpdateAndGetCurrentWeaponIndexes(class PerkClass, KFPlayerController KFPC, out byte WeaponIdx, out byte SecondaryWeaponIdx, out byte GrenadeIdx , byte SelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext) { + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo(KFPC.WorldInfo.GRI); + + SecondaryWeaponIdx = PerkClass.static.GetSecondaryWeaponSelectedIndex(KFPC.SurvivalPerkSecondaryWeapIndex); + + if (KFPC.CurrentPerk.Class.Name == PerkClass.Name) + { + KFPC.CurrentPerk.StartingSecondaryWeaponClassIndex = SecondaryWeaponIdx; + } + if (PerkClass.Name == 'KFPerk_Survivalist') { - WeaponIdx = KFPC.CurrentPerk.SetWeaponSelectedIndex(KFPC.SurvivalPerkWeapIndex); - GrenadeIdx = KFPC.CurrentPerk.SetGrenadeSelectedIndexUsingSkills(KFPC.SurvivalPerkGrenIndex, SelectedSkills, IsChoosingPrev, IsChoosingNext); + WeaponIdx = PerkClass.static.GetWeaponSelectedIndex(KFPC.SurvivalPerkWeapIndex); + GrenadeIdx = PerkClass.static.GetGrenadeSelectedIndexUsingSkills(KFPC.SurvivalPerkGrenIndex, SelectedSkills, IsChoosingPrev, IsChoosingNext); + + if (KFPC.CurrentPerk.Class.Name == PerkClass.Name) + { + KFPerk_Survivalist(KFPC.CurrentPerk).StartingWeaponClassIndex = WeaponIdx; + KFPerk_Survivalist(KFPC.CurrentPerk).StartingGrenadeClassIndex = GrenadeIdx; + + // If we are in no gameplay time insta change + if (!KFGRI.bWaveIsActive) + { + KFPerk_Survivalist(KFPC.CurrentPerk).UpdateCurrentGrenade(); + } + } } } diff --git a/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc b/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc index 2182d78..22eeb1d 100644 --- a/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc +++ b/KFGame/Classes/KFGFxPostGameContainer_MapVote.uc @@ -55,11 +55,13 @@ function SetMapOptions() local array ServerMapList; local KFGameReplicationInfo KFGRI; local bool IsWeeklyMode; - local bool IsBrokenTrader; - local bool IsBossRush; - local bool IsGunGame; - local bool IsContaminationMode; local bool bShouldSkipMaps; + local bool bWeeklyNoSanta; + + local bool IsBoom, IsCraniumCracker, IsTinyTerror, IsBobbleZed, IsPoundemonium, IsUpUpAndDecay, IsZedTime, IsBeefcake; + local bool IsBloodThirst, IsColiseum, IsArachnophobia, IsScavenger, IsWW, IsAbandon, IsBossRush, IsShrunkenHeads; + local bool IsGunGame, /*IsVIP,*/ IsPerkRoulette, IsContaminationMode, IsBountyHunt; + local name MapName; KFGRI = KFGameReplicationInfo(GetPC().WorldInfo.GRI); @@ -70,13 +72,62 @@ function SetMapOptions() 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; - IsContaminationMode = KFGRI.CurrentWeeklyIndex == 19; - bShouldSkipMaps = IsWeeklyMode && (IsBrokenTrader || IsBossRush || IsGunGame); + IsWeeklyMode = KFGRI.bIsWeeklyMode; + + IsBoom = false; + IsCraniumCracker = false; + IsTinyTerror = false; + IsBobbleZed = false; + IsPoundemonium = false; + IsUpUpAndDecay = false; + IsZedTime = false; + IsBeefcake = false; + IsBloodThirst = false; + IsColiseum = false; + IsArachnophobia = false; + IsScavenger = false; + IsWW = false; + IsAbandon = false; + IsBossRush = false; + IsShrunkenHeads = false; + IsGunGame = false; + //IsVIP = false; + IsPerkRoulette = false; + IsContaminationMode = false; + IsBountyHunt = false; + + switch (KFGRI.CurrentWeeklyIndex) + { + case 0: IsBoom = true; break; + case 1: IsCraniumCracker = true; break; + case 2: IsTinyTerror = true; break; + case 3: IsBobbleZed = true; break; + case 4: IsPoundemonium = true; break; + case 5: IsUpUpAndDecay = true; break; + case 6: IsZedTime = true; break; + case 7: IsBeefcake = true; break; + case 8: IsBloodThirst = true; break; + case 9: IsColiseum = true; break; + case 10: IsArachnophobia = true; break; + case 11: IsScavenger = true; break; + case 12: IsWW = true; break; + case 13: IsAbandon = true; break; + case 14: IsBossRush = true; break; + case 15: IsShrunkenHeads = true; break; + case 16: IsGunGame = true; break; + //case 17: IsVIP = true; break; + case 18: IsPerkRoulette = true; break; + case 19: IsContaminationMode = true; break; + case 20: IsBountyHunt = true; break; + } + + bShouldSkipMaps = IsWeeklyMode && (IsScavenger || IsBossRush || IsGunGame); + + bWeeklyNoSanta = IsWeeklyMode && ( IsBoom || IsCraniumCracker || IsTinyTerror || IsBobbleZed + || IsPoundemonium || IsUpUpAndDecay || IsZedTime || IsBeefcake + || IsBloodThirst || IsColiseum || IsArachnophobia || IsScavenger + || IsWW || IsAbandon || IsShrunkenHeads || IsPerkRoulette); //gfx MapList = CreateArray(); @@ -85,6 +136,11 @@ function SetMapOptions() { MapName = name(ServerMapList[i]); + if (bWeeklyNoSanta && MapName == MapSantas) + { + continue; + } + if ( bShouldSkipMaps && ( MapName == MapBiolapse || MapName == MapNightmare || MapName == MapPowerCore || @@ -108,12 +164,24 @@ function SetMapOptions() } } - /* Temporary removal of SteamFrotress for BossRush */ + if (IsWeeklyMode && IsBountyHunt) + { + if (MapName == MapBiolapse || + MapName == MapNightmare || + MapName == MapPowerCore || + MapName == MapDescent || + MapName == MapKrampus || + MapName == MapElysium || + MapName == MapSteam) + { + continue; + } + } + if (IsWeeklyMode && IsBossRush && MapName == MapSteam) { continue; } - /* */ MapObject = CreateObject("Object"); MapObject.SetString("label", class'KFCommon_LocalizedStrings'.static.GetFriendlyMapName(ServerMapList[i]) ); diff --git a/KFGame/Classes/KFGFxServerBrowser_Filters.uc b/KFGame/Classes/KFGFxServerBrowser_Filters.uc index 136ec3b..b19ff92 100644 --- a/KFGame/Classes/KFGFxServerBrowser_Filters.uc +++ b/KFGame/Classes/KFGFxServerBrowser_Filters.uc @@ -17,10 +17,10 @@ var localized string NoPasswordString, NoMutatorsString, NotFullString, NotEmpty var array FilterStrings; var config Bool bNoPassword, bNoMutators, bNotFull, bNotEmpty, bUsesStats, bCustom, bDedicated, bVAC_Secure, bInLobby, bInProgress, bOnlyStockMaps, bOnlyCustomMaps, bLimitServerResults, bNoLocalAdmin, bNoSeasonalSkins; -var config byte SavedGameModeIndex, SavedMapIndex, SavedDifficultyIndex, SavedLengthIndex, SavedPingIndex; +var config byte SavedGameModeIndex, SavedMapIndex, SavedDifficultyIndex, SavedLengthIndex, SavedPingIndex, SavedWeeklySelectorIndex; var Bool bNoPasswordPending, bNoMutatorsPending, bNotFullPending, bNotEmptyPending, bUsesStatsPending, bCustomPending, bDedicatedPending, bVAC_SecurePending, bInLobbyPending, bInProgressPending, bOnlyStockMapsPending, bOnlyCustomMapsPending, bLimitServerResultsPending, bNoLocalAdminPending, bNoSeasonalSkinsPending; -var byte SavedGameModeIndexPending, SavedMapIndexPending, SavedDifficultyIndexPending, SavedLengthIndexPending, SavedPingIndexPending; +var byte SavedGameModeIndexPending, SavedMapIndexPending, SavedDifficultyIndexPending, SavedLengthIndexPending, SavedPingIndexPending, SavedWeeklySelectorIndexPending; var transient string CachedMapName, CachedModeName; var transient int CachedDifficulty, CachedLength; @@ -93,6 +93,12 @@ function AdjustSavedFiltersToMode() SavedLengthIndex = 255; } SavedLengthIndexPending = SavedLengthIndex; + + if (SavedGameModeIndex != 1) + { + SavedWeeklySelectorIndex = 0; + } + SavedWeeklySelectorIndexPending = SavedWeeklySelectorIndex; } exec native function string GetSelectedMap() const; @@ -105,6 +111,8 @@ exec native function int GetSelectedGameLength() const; native function int GetMaxPing() const; //@SABER_END +exec native function int GetWeeklySelectorIndex() const; + function InitFiltersArray() { FilterStrings[NO_PASSWORD] = NoPasswordString; @@ -141,6 +149,7 @@ function LocalizeText() LocalizedObject.SetString("difficulty", ServerMenu.DifficultyString); LocalizedObject.SetString("length", ServerMenu.LengthString); LocalizedObject.SetString("ping", ServerMenu.PingString); + LocalizedObject.SetString("weeklyIndex", ServerMenu.WeeklyIndexString); SetObject("localizedText", LocalizedObject); @@ -150,6 +159,7 @@ function LocalizeText() CreateList("difficultyScrollingList", class'KFCommon_LocalizedStrings'.static.GetDifficultyStringsArray(), SavedDifficultyIndex); CreateList("lengthScrollingList", class'KFCommon_LocalizedStrings'.static.GetLengthStringsArray(), SavedLengthIndex); CreateList("pingScrollingList", ServerMenu.PingOptionStrings, SavedPingIndex); + CreateList("weeklyIndexScrollingList", class'KFCommon_LocalizedStrings'.static.GetWeeklySelectorStringsArray(), SavedWeeklySelectorIndex, -1, true, SavedGameModeIndexPending == 1); LocalizeCheckBoxes(); } @@ -227,7 +237,10 @@ function SetModeMenus(string DifficultyListString, string LengthListString, int CreateList(LengthListString, class'KFCommon_LocalizedStrings'.static.GetLengthStringsArray(), NewLengthIndex, class'KFGameInfo'.default.GameModes[UseModeIndex].Lengths); } -function CreateList( string ListString, array TextArray, int SelectedIndex, optional int MaxListLength) +function CreateList( string ListString, array TextArray, int SelectedIndex + , optional int MaxListLength = -1 + , optional bool bAnyIsFirst = false + , optional bool bEnabled = true) { local int i; local GFxObject OptionList; @@ -252,33 +265,73 @@ function CreateList( string ListString, array TextArray, int SelectedInd { SelectedIndex = 255; } + + if (bAnyIsFirst) + { + //Add the any choice + ItemSlot = CreateObject( "Object" ); + ItemSlot.SetString("label", class'KFCommon_LocalizedStrings'.default.NoPreferenceString); + DataProvider.SetElementObject(0, ItemSlot); + } + for ( i = 0; i < ListLength; i++ ) { ItemSlot = CreateObject( "Object" ); TempString = TextArray[i]; ItemSlot.SetString("label", TempString ); - DataProvider.SetElementObject(i, ItemSlot); - } + DataProvider.SetElementObject(bAnyIsFirst ? i + 1 : i, ItemSlot); + } + + if (bAnyIsFirst == false) + { + //Add the any choice + ItemSlot = CreateObject( "Object" ); + ItemSlot.SetString("label", class'KFCommon_LocalizedStrings'.default.NoPreferenceString); + DataProvider.SetElementObject(i, ItemSlot); + } - //Add the any choice - ItemSlot = CreateObject( "Object" ); - ItemSlot.SetString("label", class'KFCommon_LocalizedStrings'.default.NoPreferenceString); - DataProvider.SetElementObject(i, ItemSlot); if(SelectedIndex != 255) { OptionList.SetInt("selectedIndex", SelectedIndex); } + OptionList.SetObject("dataProvider", DataProvider); - if(SelectedIndex < ListLength) + + if (bEnabled) { - ButtonLabel = TextArray[SelectedIndex]; + if (bAnyIsFirst) + { + ButtonLabel = class'KFCommon_LocalizedStrings'.default.NoPreferenceString; + } + else + { + if(SelectedIndex < ListLength) + { + ButtonLabel = TextArray[SelectedIndex]; + } + else + { + ButtonLabel = "-"; + } + } } else { ButtonLabel = "-"; } + OptionList.GetObject("associatedButton").SetString("label", ButtonLabel); + OptionList.ActionScriptVoid("invalidateData"); + + if (bEnabled ) + { + OptionList.GetObject("associatedButton").SetBool("enabled", true); + } + else + { + OptionList.GetObject("associatedButton").SetBool("enabled", false); + } } function ModeChanged(int index) @@ -321,6 +374,11 @@ function PingChanged(int index) SavedPingIndexPending = index; } +function WeeklySelectorChanged(int index) +{ + SavedWeeklySelectorIndexPending = index; +} + function ApplyFilters() { bNoPassword = bNoPasswordPending; @@ -344,7 +402,10 @@ function ApplyFilters() SavedDifficultyIndex = SavedDifficultyIndexPending; SavedLengthIndex = SavedLengthIndexPending; SavedPingIndex = SavedPingIndexPending; - //`log("ApplyFilters:"@bCustom); + SavedWeeklySelectorIndex = SavedWeeklySelectorIndexPending; + + //`Log("ApplyFilters"); + SaveConfig(); } @@ -370,7 +431,9 @@ function ClearPendingValues() SavedDifficultyIndexPending = SavedDifficultyIndex; SavedLengthIndexPending = SavedLengthIndex; SavedPingIndexPending = SavedPingIndex; - //`log("ClearPendingValues:"@bCustom); + SavedWeeklySelectorIndexPending = SavedWeeklySelectorIndex; + + //`Log("ClearPendingValues"); } function ResetFilters() @@ -396,6 +459,7 @@ function ResetFilters() SavedDifficultyIndex = 255; SavedLengthIndex = 255; SavedPingIndex = 255; + SavedWeeklySelectorIndex = 0; //`log("ResetFilters:"@bCustom); ClearPendingValues(); diff --git a/KFGame/Classes/KFGFxServerBrowser_ServerDetails.uc b/KFGame/Classes/KFGFxServerBrowser_ServerDetails.uc index 6b9a5e1..8872022 100644 --- a/KFGame/Classes/KFGFxServerBrowser_ServerDetails.uc +++ b/KFGame/Classes/KFGFxServerBrowser_ServerDetails.uc @@ -66,6 +66,7 @@ function SetDetails(KFOnlineGameSettings ServerResult) local int Ping, PlayerCount; local KFOnlineGameSettings TempOnlingGamesSettings; local bool bShowSeasonalSkins; + local KFWeeklyOutbreakInformation WeeklyInfo; if(ServerResult != none) { @@ -103,6 +104,22 @@ function SetDetails(KFOnlineGameSettings ServerResult) TempObj.SetString("ping", (Ping < 0) ? ("-") : (String(Ping)) ); TempObj.SetString("difficulty", Class'KFCommon_LocalizedStrings'.static.GetDifficultyString(TempOnlingGamesSettings.difficulty)); TempObj.SetString("mode", class'KFCommon_LocalizedStrings'.static.GetGameModeString(TempOnlingGamesSettings.Mode) ); + + // If weekly we add the weekly type + if (TempOnlingGamesSettings.Mode == 1) + { + if (TempOnlingGamesSettings.WeeklySelectorIndex > 0) + { + WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetWeeklyOutbreakInfoByIndex(TempOnlingGamesSettings.WeeklySelectorIndex - 1); + } + else + { + WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); + } + + TempObj.SetString("weeklyForced", WeeklyInfo.FriendlyName); + } + TempObj.SetString("map", TempOnlingGamesSettings.MapName); TempObj.SetString("mapImagePath", GetMapSource(TempOnlingGamesSettings.MapName)); diff --git a/KFGame/Classes/KFGFxServerBrowser_ServerList.uc b/KFGame/Classes/KFGFxServerBrowser_ServerList.uc index 745f444..b0a3620 100644 --- a/KFGame/Classes/KFGFxServerBrowser_ServerList.uc +++ b/KFGame/Classes/KFGFxServerBrowser_ServerList.uc @@ -271,7 +271,7 @@ function BuildServerFilters(KFGFxServerBrowser_Filters Filters, OnlineGameSearch { local string GametagSearch; local string MapName; - local int Mode, Difficulty, Length; + local int Mode, Difficulty, Length, WeeklySelectorIndex; local bool DisableSeasonalSkins; Search.ClearServerFilters(); @@ -344,7 +344,14 @@ function BuildServerFilters(KFGFxServerBrowser_Filters Filters, OnlineGameSearch //`log("adding custom filter"); Search.TestAddBoolGametagFilter(GametagSearch, Filters.bCustom, 'bCustom', 0); } - + + WeeklySelectorIndex = Filters.GetWeeklySelectorIndex(); + if (Mode == 1 && WeeklySelectorIndex != 0) // // Only when mode is Weekly, 0 means "ANY", 1 is "DEFAULT", then the weeklies + { + WeeklySelectorIndex = WeeklySelectorIndex - 1; // move back to index range + + Search.AddGametagFilter(GametagSearch, 'WeeklySelectorIndex', string(WeeklySelectorIndex)); + } if (Len(GametagSearch) > 0) { @@ -832,7 +839,7 @@ function string BuildJoinURL() function string BuildJoinFiltersRequestURL() { local string FiltersURL; - local int GameDifficulty; + local int GameDifficulty, WeeklySelectorIndex, IntendedWeeklyIndex; GameDifficulty = ServerMenu.FiltersContainer.GetSelectedDifficulty(); @@ -851,10 +858,32 @@ function string BuildJoinFiltersRequestURL() FiltersURL $= "?GameLength="$ServerMenu.FiltersContainer.SavedLengthIndex; } + WeeklySelectorIndex = ServerMenu.FiltersContainer.SavedWeeklySelectorIndex; + + if (ServerMenu.FiltersContainer.SavedGameModeIndex == 1 + && WeeklySelectorIndex != 0) // 0 means "ANY", 1 is "DEFAULT", then the weeklies + { + WeeklySelectorIndex = WeeklySelectorIndex - 1; // move back to index range + + // IF index matches default, set to 0 (default) + if (WeeklySelectorIndex >= 1) + { + IntendedWeeklyIndex = class'KFGameEngine'.static.GetIntendedWeeklyEventIndexMod(); + if (IntendedWeeklyIndex == (WeeklySelectorIndex - 1)) + { + WeeklySelectorIndex = 0; + } + } + + if (WeeklySelectorIndex >= 0) + { + FiltersURL $= "?WeeklySelectorIndex=" $WeeklySelectorIndex; + } + } + return FiltersURL; } - function OnRefreshServerDetails() { local KFOnlineGameSearch GameSearch; @@ -934,6 +963,7 @@ function UpdateListDataProvider() local KFOnlineGameSearch LatestGameSearch; local int Ping, PlayerCount; local KFOnlineGameSettings TempOnlineGamesSettings; + local KFWeeklyOutbreakInformation WeeklyInfo; LatestGameSearch = KFOnlineGameSearch(SearchDataStore.GetActiveGameSearch()); @@ -989,7 +1019,23 @@ function UpdateListDataProvider() TempObj.SetString("ping", (Ping < 0) ? ("-") : (String(Ping)) ); TempObj.SetString("difficulty", Class'KFCommon_LocalizedStrings'.static.GetDifficultyString(TempOnlineGamesSettings.difficulty)); - TempObj.SetString("mode", class'KFCommon_LocalizedStrings'.static.GetGameModeString(TempOnlineGamesSettings.Mode) ); + TempObj.SetString("mode", class'KFCommon_LocalizedStrings'.static.GetGameModeString(TempOnlineGamesSettings.Mode)); + + // If weekly we show the icon of the weekly type + if (IsWeeklyModeIndex(TempOnlineGamesSettings.Mode)) + { + if (TempOnlineGamesSettings.WeeklySelectorIndex > 0) + { + WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetWeeklyOutbreakInfoByIndex(TempOnlineGamesSettings.WeeklySelectorIndex - 1); + } + else + { + WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); + } + + TempObj.SetString("weeklyType", "img://"$WeeklyInfo.IconPath); + } + TempObj.SetString("map", TempOnlineGamesSettings.MapName); TempObj.SetBool("locked", TempOnlineGamesSettings.bRequiresPassword); TempObj.SetBool("serverExiled", TempOnlineGamesSettings.bServerExiled); @@ -1013,6 +1059,11 @@ function UpdateListDataProvider() } } +function bool IsWeeklyModeIndex(int ModeIndex) +{ + return ModeIndex == 1; +} + function bool IsEndlessModeIndex(int ModeIndex) { return ModeIndex == 3; diff --git a/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Fall2023.uc b/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Fall2023.uc new file mode 100644 index 0000000..270a660 --- /dev/null +++ b/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Fall2023.uc @@ -0,0 +1,27 @@ +class KFGFXSpecialEventObjectivesContainer_Fall2023 extends KFGFxSpecialEventObjectivesContainer; + +function Initialize(KFGFxObject_Menu NewParentMenu) +{ + super.Initialize(NewParentMenu); +} + +DefaultProperties +{ + ObjectiveIconURLs[0] = "Halloween2023_UI.UI_Objective_Halloween2023_ICouldDoThisAllDay" // Kill Hans Volter in 10 different maps + ObjectiveIconURLs[1] = "Spring_UI.UI_Objectives_Spring_Weekly" // Complete the Weekly on Castle Volter + ObjectiveIconURLs[2] = "Halloween2023_UI.UI_Objective_Halloween2023_LuckilyTheyreNotClones" // Find all Castle Volter’s Collectibles + ObjectiveIconURLs[3] = "Halloween2023_UI.UI_Objective_Halloween2023_ThisBelongToAMuseum" // Unlock all exhibits from Castle Volter’s trophy room ( TODO ) + ObjectiveIconURLs[4] = "Halloween2023_UI.UI_Objective_Halloween2023_AFineDayAtTheMountains" // Complete wave 15 on Endless Hard or higher difficulty on Castle Volter + + //defaults + AllCompleteRewardIconURL="WEP_SkinSet81_Item_TEX.mkb42_hans.VolterMKB42Precious_Mint" + ChanceDropIconURLs[0]="CHR_CosmeticSet14_Item_TEX.Tickets.CyberPunk_ticket" + ChanceDropIconURLs[1]="CHR_CosmeticSet14_Item_TEX.Tickets.CyberPunk_ticket_golden" + IconURL="Halloween2023_UI.UI_Halloween2023_EventLogo" + + UsesProgressList[0] = true + UsesProgressList[1] = false + UsesProgressList[2] = true + UsesProgressList[3] = false + UsesProgressList[4] = false +} \ No newline at end of file diff --git a/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc b/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc index 5b37da5..d55c4ca 100644 --- a/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc +++ b/KFGame/Classes/KFGFxStartContainer_InGameOverview.uc @@ -13,7 +13,7 @@ dependson(KFUnlockManager); var KFGFxMenu_StartGame StartMenu; -var byte LastDifficultyIndex, LastLengthIndex, LastPrivacyIndex, LastAllowSeasonalSkinsIndex; +var byte LastDifficultyIndex, LastLengthIndex, LastPrivacyIndex, LastAllowSeasonalSkinsIndex, LastWeeklySelectorIndex; var localized string OverviewString; var localized string ChangeString; @@ -125,6 +125,15 @@ function LocalizeContainer() LocalizedObject.SetObject("allowSeasonalSkinsOptions", DataProvider); + for (i = 0; i < class'KFCommon_LocalizedStrings'.static.GetWeeklySelectorStringsArray().length; i++) + { + TempObj = CreateObject("Object"); + TempObj.SetString("label", class'KFCommon_LocalizedStrings'.static.GetWeeklySelectorString(i)); + DataProvider.SetElementObject(i, TempObj); + } + + LocalizedObject.SetObject("weeklySelectorOptions", DataProvider); + if( !class'WorldInfo'.static.IsMenuLevel() ) { LocalizedObject.SetString("authorName", AuthorString$GetPC().WorldInfo.Author); @@ -350,12 +359,17 @@ function UpdateAllowSeasonalSkins(string AllowSeasonalStrings) SetString("allowSeasonalSkinsText", AllowSeasonalStrings); } +function UpdateWeeklySelector(string WeeklySelectorStrings) +{ + SetString("weeklySelectorText", WeeklySelectorStrings); +} + function UpdateOverviewInGame() { local KFGameReplicationInfo KFGRI; local string GameDifficultyString; local Float CurrentGameDifficulty; - local int CurrentLengthIndex, CurrentPrivacyIndex, CurrentAllowSeasonalSkinsIndex; + local int CurrentLengthIndex, CurrentPrivacyIndex, CurrentAllowSeasonalSkinsIndex, CurrentWeeklySelectorIndex; local bool bCustomDifficulty; local bool bCustomLength; @@ -427,6 +441,13 @@ function UpdateOverviewInGame() UpdateAllowSeasonalSkins( class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsString(CurrentAllowSeasonalSkinsIndex) ); LastAllowSeasonalSkinsIndex = CurrentAllowSeasonalSkinsIndex; } + + CurrentWeeklySelectorIndex = StartMenu.OptionsComponent.GetWeeklySelectorIndex(); + if (LastWeeklySelectorIndex != CurrentWeeklySelectorIndex) + { + UpdateWeeklySelector( class'KFCommon_LocalizedStrings'.static.GetWeeklySelectorString(CurrentWeeklySelectorIndex) ); + LastWeeklySelectorIndex = CurrentWeeklySelectorIndex; + } } } } @@ -448,6 +469,7 @@ DefaultProperties LastLengthIndex=255 LastDifficultyIndex=255 LastAllowSeasonalSkinsIndex=255 + LastWeeklySelectorIndex=255 ObjectiveClassName=KFGameInfo_Objective } diff --git a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc index 72cd62b..687d693 100644 --- a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc +++ b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc @@ -182,7 +182,7 @@ function FillWhatsNew() local SWhatsNew item; WhatsNewItems.Remove(0, WhatsNewItems.Length); // Latest Update - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_Event", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2023_Event", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); WhatsNewItems.AddItem(item); // Featured Ultimate Edition item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_UltimateEdition_Upgrade", "FeaturedItemBundle", "https://store.steampowered.com/app/1914560/KF2__Ultimate_Edition_Upgrade_DLC/"); @@ -197,25 +197,22 @@ function FillWhatsNew() item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_CosmeticsSeasonPass", "FeaturedItemBundle", "https://store.steampowered.com/app/2363410/Killing_Floor_2__Cosmetics_Season_Pass"); WhatsNewItems.AddItem(item); // Featured Weapon - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_S12Shockgun", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9655"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2023_MG3Shredder", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9749"); WhatsNewItems.AddItem(item); // Featured Outfit Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_HorzineDiver_Uniforms", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9653"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2023_LastStand_Uniforms", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9747"); WhatsNewItems.AddItem(item); // Featured Time Limited Item - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_SS_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/4928"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween_PremiumTicket", "FeaturedEventItem", "https://store.steampowered.com/buyitem/232090/5246"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_Predator_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9651"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2023_MedievalMKII_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9718"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_JunkyardMKII_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9646"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2023_HRGSpectreMKIII_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9720"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_JaegerMKIV_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9645"); - WhatsNewItems.AddItem(item); -// Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2023_Stingray_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9649"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2023_ChameleonMKIV_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9716"); 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 df70bff..65ddbfe 100644 --- a/KFGame/Classes/KFGFxStartGameContainer_Options.uc +++ b/KFGame/Classes/KFGFxStartGameContainer_Options.uc @@ -75,6 +75,7 @@ var GFxObject DifficultyButton; var GfxObject MapButton; var GFxObject RegionButton; var GFxObject AllowSeasonalSkinsButton; +var GFxObject WeeklySelectorButton; //============================================================== // Initialization @@ -96,25 +97,40 @@ function GetButtons() MapButton = GetObject("mapButton"); RegionButton = GetObject("regionButton"); AllowSeasonalSkinsButton = GetObject("allowSeasonalSkinsButton"); + WeeklySelectorButton = GetObject("weeklySelectorButton"); } function UpdateButtonsEnabled() { local int AdjustedGameModeIndex; + local bool ModeIsWeekly; if (bIsSoloGame) { AdjustedGameModeIndex = ParentMenu.Manager.GetModeIndex(false); + ModeIsWeekly = AdjustedGameModeIndex == EGameMode_Weekly; + LengthButton.SetBool("enabled", class'KFGameInfo'.default.GameModes[AdjustedGameModeIndex].Lengths > 0); DifficultyButton.SetBool("enabled", class'KFGameInfo'.default.GameModes[AdjustedGameModeIndex].DifficultyLevels > 0); } else { + ModeIsWeekly = ParentMenu.Manager.GetModeIndex() == EGameMode_Weekly; + LengthButton.SetBool("enabled", class'KFGameInfo'.default.GameModes[ParentMenu.Manager.GetModeIndex()].Lengths > 0); DifficultyButton.SetBool("enabled", class'KFGameInfo'.default.GameModes[ParentMenu.Manager.GetModeIndex()].DifficultyLevels > 0); } + + if (ModeIsWeekly) + { + WeeklySelectorButton.SetBool("enabled", true); + } + else + { + WeeklySelectorButton.SetBool("enabled", false); + } } function SetHelpText(string TextValue) @@ -131,6 +147,7 @@ function SetModeMenus(GFxObject TextObject, int ModeIndex, int LengthIndex) Lengths = class'KFGameInfo'.default.GameModes[ModeIndex].Lengths; NewDifficultyIndex = Clamp(NewDifficultyIndex, 0, DifficultyLevels); NewLengthIndex = Clamp(NewLengthIndex, 0, Lengths); + TextObject.SetObject("difficultyList", CreateList(class'KFCommon_LocalizedStrings'.static.GetDifficultyStringsArray(), GetDifficultyIndex(), false, false, byte(DifficultyLevels))); TextObject.SetObject("lengthList", CreateList(class'KFCommon_LocalizedStrings'.static.GetLengthStringsArray(), LengthIndex, bShowLengthNoPref, false, byte(Lengths))); } @@ -254,7 +271,8 @@ function InitializeGameOptions() TextObject.SetBool("bShowAllowSeasonalSkins", true); } - TextObject.SetString("allowSeasonalSkins", StartMenu.AllowSeasonalSkinsTitle); + TextObject.SetString("allowSeasonalSkins", StartMenu.AllowSeasonalSkinsTitle); + TextObject.SetString("weeklySelector", StartMenu.WeeklySelectorTitle); // Since the Mode list can include "ANY" we need to just accept that the selected index could be the length of the supported modes. Otherwise when "ANY" is selected we push the index to 1. // Also don't include the "ANY" option on Console since PlayGo doesn't support searching multiple game types. HSL_BB @@ -264,7 +282,8 @@ function InitializeGameOptions() TextObject.SetObject("mapList", CreateList(StartMenu.MapStringList, bIsSoloGame ? InitialMapIndex : InitialMapIndex+1, true, true)); TextObject.SetObject("difficultyList", CreateList(class'KFCommon_LocalizedStrings'.static.GetDifficultyStringsArray(), GetDifficultyIndex(), false)); TextObject.SetObject("privacyList", CreateList(class'KFCommon_LocalizedStrings'.static.GetPermissionStringsArray(class'WorldInfo'.static.IsConsoleBuild()), Profile.GetProfileInt(KFID_SavedPrivacyIndex), false)); - TextObject.SetObject("allowSeasonalSkinsList", CreateList(class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsStringsArray(), Profile.GetProfileInt(KFID_SavedAllowSeasonalSkinsIndex), false)); + TextObject.SetObject("allowSeasonalSkinsList", CreateList(class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsStringsArray(), Profile.GetProfileInt(KFID_SavedAllowSeasonalSkinsIndex), false)); + TextObject.SetObject("weeklySelectorList", CreateList(class'KFCommon_LocalizedStrings'.static.GetWeeklySelectorStringsArray(), Profile.GetProfileInt(KFID_SavedWeeklySelectorIndex), false, false)); if (class'WorldInfo'.static.IsConsoleBuild()) { @@ -287,39 +306,124 @@ function FilterWeeklyMaps(out array List) return; } - `Log("OPTIONS: Skipping Maps"); - - // Scavenger index = 11 - // BossRush index = 14 - // GunGame index = 16 WeeklyIndex = class'KFGameEngine'.static.GetWeeklyEventIndexMod(); - 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"); - List.RemoveItem("KF-TheDescent"); - List.RemoveItem("KF-KrampusLair"); + if (ParentMenu.Manager.GetWeeklySelectorIndex() != 0) + { + WeeklyIndex = ParentMenu.Manager.GetWeeklySelectorIndex() - 1; } - if (WeeklyIndex == 19) + switch (WeeklyIndex) { - List.RemoveItem("KF-Biolapse"); - List.RemoveItem("KF-Nightmare"); - List.RemoveItem("KF-PowerCore_Holdout"); - List.RemoveItem("KF-TheDescent"); - List.RemoveItem("KF-KrampusLair"); - List.RemoveItem("KF-SantasWorkshop"); - List.RemoveItem("KF-Elysium"); - } + case 0: // Boom + List.RemoveItem("KF-SantasWorkshop"); + break; - /* Temporary removal of SteamFrotress for BossRush */ - if (WeeklyIndex == 14) - { - List.RemoveItem("KF-SteamFortress"); - } + case 1: // Cranium Cracker + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 2: // Tiny Terror + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 3: // BobbleZed + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 4: // Poundemonium + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 5: // Up Up And Decay + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 6: // Zed Time + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 7: // Beefcake + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 8: // BloodThirst + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 9: // Coliseum + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 10: // Arachnophobia + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 11: // Scavenger + List.RemoveItem("KF-Biolapse"); + List.RemoveItem("KF-Nightmare"); + List.RemoveItem("KF-PowerCore_Holdout"); + List.RemoveItem("KF-TheDescent"); + List.RemoveItem("KF-KrampusLair"); + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 12: // WW + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 13: // Abandon + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 14: // Boss Rush + List.RemoveItem("KF-Biolapse"); + List.RemoveItem("KF-Nightmare"); + List.RemoveItem("KF-PowerCore_Holdout"); + List.RemoveItem("KF-TheDescent"); + List.RemoveItem("KF-KrampusLair"); + List.RemoveItem("KF-SteamFortress"); + break; + + case 15: // Shrunken Heads + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 16: // GunGame + List.RemoveItem("KF-Biolapse"); + List.RemoveItem("KF-Nightmare"); + List.RemoveItem("KF-PowerCore_Holdout"); + List.RemoveItem("KF-TheDescent"); + List.RemoveItem("KF-KrampusLair"); + break; + + case 17: // VIP + break; + + case 18: // Perk Roulette + List.RemoveItem("KF-SantasWorkshop"); + break; + + case 19: // Contamination Zone + List.RemoveItem("KF-Biolapse"); + List.RemoveItem("KF-Nightmare"); + List.RemoveItem("KF-PowerCore_Holdout"); + List.RemoveItem("KF-TheDescent"); + List.RemoveItem("KF-KrampusLair"); + List.RemoveItem("KF-SantasWorkshop"); + List.RemoveItem("KF-Elysium"); + break; + + case 20: // Bounty Hunt + List.RemoveItem("KF-Biolapse"); + List.RemoveItem("KF-Nightmare"); + List.RemoveItem("KF-PowerCore_Holdout"); + List.RemoveItem("KF-TheDescent"); + List.RemoveItem("KF-KrampusLair"); + List.RemoveItem("KF-Elysium"); + List.RemoveItem("KF-SteamFortress"); + break; + } } function GFxObject CreateList( array TextArray, byte SelectedIndex, bool bAddNoPrefString, optional bool bIsMapList, optional byte MaxLength) @@ -504,6 +608,22 @@ function AllowSeasonalSkinsChanged( int Index, optional bool bSetText ) } } +function WeeklySelectorChanged( int Index, optional bool bSetText ) +{ + if(Index != GetCachedProfile().GetProfileInt(KFID_SavedWeeklySelectorIndex)) + { + SaveConfig(); + if(bSetText) + { + WeeklySelectorButton.SetString("infoString", class'KFCommon_LocalizedStrings'.static.GetWeeklySelectorStringsArray()[Index]); + } + + GetCachedProfile().SetProfileSettingValueInt(KFID_SavedWeeklySelectorIndex, Index); + + InitializeGameOptions(); + } +} + function SetRegionIndex(int InRegionIndex, optional bool bSetText) { local array PlayfabRegionList; @@ -623,6 +743,11 @@ function int GetAllowSeasonalSkinsIndex() return GetCachedProfile().GetProfileInt(KFID_SavedAllowSeasonalSkinsIndex); } +function int GetWeeklySelectorIndex() +{ + return GetCachedProfile().GetProfileInt(KFID_SavedWeeklySelectorIndex); +} + function string GetMapName() { local string SavedMapString; diff --git a/KFGame/Classes/KFGFxStoreContainer_Main.uc b/KFGame/Classes/KFGFxStoreContainer_Main.uc index 2c0afa6..0b85129 100644 --- a/KFGame/Classes/KFGFxStoreContainer_Main.uc +++ b/KFGame/Classes/KFGFxStoreContainer_Main.uc @@ -447,21 +447,19 @@ DefaultProperties XboxFilterExceptions[0]="Wasteland Bundle" // Wasteland Outfit Bundle - FeaturedItemIDs[0]=8178 //Whatsnew Gold Ticket - FeaturedItemIDs[1]=9655 - FeaturedItemIDs[2]=9646 - FeaturedItemIDs[3]=9649 - FeaturedItemIDs[4]=9645 - FeaturedItemIDs[5]=9653 - FeaturedItemIDs[6]=9651 + FeaturedItemIDs[0]=7619 //Whatsnew Gold Ticket + FeaturedItemIDs[1]=9749 + FeaturedItemIDs[2]=9747 + FeaturedItemIDs[3]=9716 + FeaturedItemIDs[4]=9718 + FeaturedItemIDs[5]=9720 - ConsoleFeaturedItemIDs[0]=8181 //Whatsnew Gold Ticket PSN - ConsoleFeaturedItemIDs[1]=9655 - ConsoleFeaturedItemIDs[2]=9646 - ConsoleFeaturedItemIDs[3]=9649 - ConsoleFeaturedItemIDs[4]=9645 - ConsoleFeaturedItemIDs[5]=9653 - ConsoleFeaturedItemIDs[6]=9651 + ConsoleFeaturedItemIDs[0]=7783 //Whatsnew Gold Ticket PSN + ConsoleFeaturedItemIDs[1]=9749 + ConsoleFeaturedItemIDs[2]=9747 + ConsoleFeaturedItemIDs[3]=9716 + ConsoleFeaturedItemIDs[4]=9718 + ConsoleFeaturedItemIDs[5]=9720 MaxFeaturedItems=5 } \ No newline at end of file diff --git a/KFGame/Classes/KFGFxTraderContainer_Store.uc b/KFGame/Classes/KFGFxTraderContainer_Store.uc index 31af3a2..7a9722e 100644 --- a/KFGame/Classes/KFGFxTraderContainer_Store.uc +++ b/KFGame/Classes/KFGFxTraderContainer_Store.uc @@ -290,6 +290,8 @@ function SetItemInfo(out GFxObject ItemDataArray, STraderItem TraderItem, int Sl /** returns true if this item should not be displayed */ function bool IsItemFiltered(STraderItem Item, optional bool bDebug) { + local bool bUses9mm; + if(!class'GameEngine'.Static.IsGameFullyInstalled() && Item.WeaponDef.default.IsPlayGoHidden) { if (bDebug) @@ -340,5 +342,38 @@ function bool IsItemFiltered(STraderItem Item, optional bool bDebug) return true; } + bUses9mm = Has9mmGun(); + if (bUses9mm && (Item.ClassName == 'KFWeap_HRG_93r' || Item.ClassName == 'KFWeap_HRG_93r_Dual')) + { + if (bDebug) + { + `log("9mm owned, skip HRG_93"); + } + return true; + } + + if (!bUses9mm && (Item.ClassName == 'KFWeap_Pistol_9mm' || Item.ClassName == 'KFWeap_Pistol_Dual9mm')) + { + if (bDebug) + { + `log("HRG_93R owned, skip 9mm"); + } + return true; + } + return false; +} + +simulated function bool Has9mmGun() +{ + local SItemInformation Item; + + foreach KFPC.GetPurchaseHelper().OwnedItemList(Item) + { + if (Item.DefaultItem.ClassName == 'KFWeap_Pistol_9mm' || Item.DefaultItem.ClassName == 'KFWeap_Pistol_Dual9mm') + { + return true; + } + } + return false; } \ No newline at end of file diff --git a/KFGame/Classes/KFGFxWeeklyObjectivesContainer.uc b/KFGame/Classes/KFGFxWeeklyObjectivesContainer.uc index 1bc1094..7c9c5f9 100644 --- a/KFGame/Classes/KFGFxWeeklyObjectivesContainer.uc +++ b/KFGame/Classes/KFGFxWeeklyObjectivesContainer.uc @@ -11,61 +11,115 @@ class KFGFxWeeklyObjectivesContainer extends KFGFxObject_Container dependson(KFMission_LocalizedStrings); -var bool bInitialDataPopulated; +var int LastWeeklyPopulated; var bool bLastWeeklyComplete; var KFPlayerController KFPC; +var KFGFxMenu_StartGame StartGameMenu; + function Initialize( KFGFxObject_Menu NewParentMenu ) { super.Initialize( NewParentMenu ); + StartGameMenu = KFGFxMenu_StartGame(NewParentMenu); + KFPC = KFPlayerController(GetPC()); if(KFPC != none) { - LocalizeMenu(); - PopulateData(); + PopulateData(); } } function bool PopulateData() { + local int IntendedWeeklyIndex, WeeklyIndex, OverrideWeeklyIndex; local GFxObject DataObject; local KFWeeklyOutbreakInformation WeeklyInfo; - local bool bWeeklyComplete; - local int WeeklyIndex; + local byte CurrentMenuState; + local bool bWeeklyComplete, bIsCustomWeekly; + + IntendedWeeklyIndex = class'KFGameEngine'.static.GetIntendedWeeklyEventIndexMod(); + WeeklyIndex = -1; + OverrideWeeklyIndex = -1; + + // If the Start Game Menu is opened and in some of the next states,.. we can read a different weekly selection + if (StartGameMenu != none) + { + CurrentMenuState = StartGameMenu.GetStartMenuState(); + + Switch (EStartMenuState(CurrentMenuState)) + { + case ECreateGame: + case ESoloGame: + if (StartGameMenu.OptionsComponent.GetWeeklySelectorIndex() != 0) + { + OverrideWeeklyIndex = StartGameMenu.OptionsComponent.GetWeeklySelectorIndex() - 1; + } + + break; + } + } + + if (KFPC.WorldInfo.NetMode == NM_Client) + { + if (KFPC != none && KFGameReplicationInfo(KFPC.WorldInfo.GRI) != none) + { + WeeklyIndex = KFGameReplicationInfo(KFPC.WorldInfo.GRI).CurrentWeeklyIndex; + } + else + { + GetPC().SetTimer(0.5f, false, nameof(PopulateData)); + } + } + else + { + if (OverrideWeeklyIndex >= 0) + { + WeeklyIndex = OverrideWeeklyIndex; + } + else + { + WeeklyIndex = class'KFGameEngine'.static.GetWeeklyEventIndexMod(); + } + } + + if (WeeklyIndex != -1) + { + bIsCustomWeekly = IntendedWeeklyIndex != WeeklyIndex; + } bWeeklyComplete = KFPC.IsWeeklyEventComplete(); - WeeklyIndex = -1; - if(bWeeklyComplete != bLastWeeklyComplete || !bInitialDataPopulated) + if (bWeeklyComplete != bLastWeeklyComplete || LastWeeklyPopulated != WeeklyIndex) { - if (KFPC.WorldInfo.NetMode == NM_Client) + LastWeeklyPopulated = WeeklyIndex; + bLastWeeklyComplete = bWeeklyComplete; + + LocalizeMenu(bIsCustomWeekly); + + if (WeeklyIndex >= 0) { - 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)); - } + WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetWeeklyOutbreakInfoByIndex(WeeklyIndex); } else { WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); } - DataObject = CreateObject("Object"); - if(WeeklyInfo == none) + if (WeeklyInfo == none) { return false; } + + DataObject = CreateObject("Object"); + DataObject.SetString("label", WeeklyInfo.FriendlyName); + if(WeeklyInfo.ModifierDescriptions.length > 0) { DataObject.SetString("description", WeeklyInfo.DescriptionStrings[0]); } + DataObject.SetString("iconPath", "img://"$WeeklyInfo.IconPath); DataObject.SetBool("complete", bWeeklyComplete); @@ -81,10 +135,8 @@ function bool PopulateData() } PopulateModifiers(WeeklyInfo); - PopulateRewards(WeeklyInfo, WeeklyIndex); + PopulateRewards(WeeklyInfo, WeeklyIndex, bIsCustomWeekly); - bLastWeeklyComplete = bWeeklyComplete; - bInitialDataPopulated = true; return true; } @@ -119,7 +171,7 @@ function PopulateModifiers(KFWeeklyOutbreakInformation WeeklyInfo) SetObject("modifiers", DataProvider); //pass to SWF } -function PopulateRewards(KFWeeklyOutbreakInformation WeeklyInfo, int WeeklyIndex) +function PopulateRewards(KFWeeklyOutbreakInformation WeeklyInfo, int WeeklyIndex, bool bIsCustomWeekly) { local int i, ItemCount; local GFxObject DataProvider; //array containing the data objects @@ -144,13 +196,15 @@ function PopulateRewards(KFWeeklyOutbreakInformation WeeklyInfo, int WeeklyIndex } } - SetObject("rewards", DataProvider); //pass to SWF - UpdateDoshVaultRewardValue(); -} - -function UpdateDoshVaultRewardValue() -{ - SetInt("vaultDoshReward", class'KFOnlineStatsWrite'.static.GetWeeklyEventReward()); + if (bIsCustomWeekly == false) + { + SetObject("rewards", DataProvider); //pass to SWF + SetInt("vaultDoshReward", class'KFOnlineStatsWrite'.static.GetWeeklyEventReward()); + } + else + { + SetInt("setHideRewards", 1); + } } function GFxObject CreateRewardItem(KFWeeklyOutbreakInformation WeeklyInfo,int ItemID) @@ -185,25 +239,34 @@ function GFxObject CreateRewardItem(KFWeeklyOutbreakInformation WeeklyInfo,int I return DataObject; } -function LocalizeMenu() +function LocalizeMenu(bool bIsCustomWeekly) { local GFxObject TextObject; -// local KFWeeklyOutbreakInformation WeeklyInfo; -// WeeklyInfo = class'KFMission_LocalizedStrings'.static.GetCurrentWeeklyOutbreakInfo(); TextObject = CreateObject("Object"); + // Localize static text TextObject.SetString("currentModifier", class'KFMission_LocalizedStrings'.default.CurrentWeeklySettingsString); TextObject.SetString("reward", class'KFMission_LocalizedStrings'.default.RewardsString); - TextObject.SetString("granted", class'KFMission_LocalizedStrings'.default.GrantedWeeklyString); - TextObject.SetString("weekly", class'KFMission_LocalizedStrings'.default.WeeklyString); + TextObject.SetString("granted", class'KFMission_LocalizedStrings'.default.GrantedWeeklyString); + + if (bIsCustomWeekly) + { + TextObject.SetString("weekly", class'KFMission_LocalizedStrings'.default.WeeklyString $class'KFMission_LocalizedStrings'.default.WeeklyCustomString); + } + else + { + 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); } + +defaultproperties +{ + LastWeeklyPopulated = -1 + bLastWeeklyComplete = false +} \ No newline at end of file diff --git a/KFGame/Classes/KFGFxWidget_BountyHunt.uc b/KFGame/Classes/KFGFxWidget_BountyHunt.uc new file mode 100644 index 0000000..2b112e0 --- /dev/null +++ b/KFGame/Classes/KFGFxWidget_BountyHunt.uc @@ -0,0 +1,36 @@ + + +//============================================================================= +// KFGFxWidget_BountyHunt +//============================================================================= +// HUD Widget that displays bounty hunt messages to the player +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +// +//============================================================================= + +class KFGFxWidget_BountyHunt extends GFxObject; + +function SetData(int Bounties, int Dosh, int DoshNoAssist) +{ + local string BountyHuntString, BountyHuntStringDosh; + + BountyHuntString = Localize("Objectives", "BountyHuntDescriptionShort", "KFGame")@Bounties; + SetString("BountyHuntDescSetLocalised", BountyHuntString); + + BountyHuntStringDosh = ""$Dosh; + SetString("BountyHuntDoshSetLocalised", BountyHuntStringDosh); +} + +function UpdateBountyHuntVisibility(bool visible) +{ + if (visible) + { + SetBool("BountyHuntSetVisibility", true); + } + else + { + SetBool("BountyHuntSetVisibility", false); + } +} \ No newline at end of file diff --git a/KFGame/Classes/KFGameEngine.uc b/KFGame/Classes/KFGameEngine.uc index 78e0661..3b5c254 100644 --- a/KFGame/Classes/KFGameEngine.uc +++ b/KFGame/Classes/KFGameEngine.uc @@ -44,9 +44,15 @@ var bool bReadingPlayfabStoreData; var private{private} const int SeasonalEventId; var private const int LoadedSeasonalEventId; -/** Week index of the year - Used as index into weekly event */ +/** Week index of the year - Used as index into weekly event, this is the Intended one, the one the system time should apply */ +var private int IntendedWeeklyEventIndex; + +/** Week index of the year - Used as index into weekly event, this is the one used by the game, it starts as the system time one but can be overriden later */ var private int WeeklyEventIndex; +// If we forced via cmd line +var private bool IsForceWeeklyEvent; + /************************************************************************************ * @name Content @@ -469,8 +475,12 @@ function ClearOnlineDelegates() /** Static because these are both called on default object */ native static function int GetSeasonalEventID(); +native static function int GetIntendedWeeklyEventIndex(); +native static function int GetIntendedWeeklyEventIndexMod(); native static function int GetWeeklyEventIndex(); native static function int GetWeeklyEventIndexMod(); +native static function SetWeeklyEventIndex(int index); +native static function bool GetIsForceWeeklyEvent(); native static function bool IsSalesEventActive(); native static function bool IsSalesEventChecked(); /*********************************************************************************** @@ -649,7 +659,9 @@ DefaultProperties KFFontScale=0.65f SeasonalEventId=-1 LoadedSeasonalEventId=-1 + IntendedWeeklyEventIndex=-1 WeeklyEventIndex=-1 + IsForceWeeklyEvent=false LocalLoginStatus=LS_LoggedIn SafeFrameScale=1.0 diff --git a/KFGame/Classes/KFGameInfo.uc b/KFGame/Classes/KFGameInfo.uc index 4819a2a..ba6c3af 100644 --- a/KFGame/Classes/KFGameInfo.uc +++ b/KFGame/Classes/KFGameInfo.uc @@ -212,6 +212,7 @@ var protected const array< class > AITestBossClassList; //List var protected int BossIndex; //Index into boss array, only preload content for the boss we want - PC builds can handle DLO cost much better var int AllowSeasonalSkinsIndex; +var int WeeklySelectorIndex; /** Class replacements for each zed type */ struct native SpawnReplacement @@ -662,6 +663,29 @@ event InitGame( string Options, out string ErrorMessage ) { local string OptionRead; + OptionRead = ParseOption(Options, "WeeklySelectorIndex"); + if (OptionRead != "") + { + WeeklySelectorIndex = int(OptionRead); + } + else // If doesn't exist on the Options we default it.. + { + WeeklySelectorIndex = -1; + } + + if (WeeklySelectorIndex > 0) + { + // 0 is default, we move one index to the left so it matches first Weekly Mode + KFGameEngine(class'Engine'.static.GetEngine()).SetWeeklyEventIndex(WeeklySelectorIndex - 1); + } + else if (KFGameEngine(class'Engine'.static.GetEngine()).GetIsForceWeeklyEvent() == false) + { + // If we didn't force via cmd line and we ask for default, force the intended to make sure we are updated + KFGameEngine(class'Engine'.static.GetEngine()).SetWeeklyEventIndex(KFGameEngine(class'Engine'.static.GetEngine()).GetIntendedWeeklyEventIndex()); + } + + `Log("TEST - InitGame : " $WeeklySelectorIndex); + Super.InitGame( Options, ErrorMessage ); if (UsesModifiedDifficulty()) @@ -760,6 +784,7 @@ function UpdateGameSettings() if (KFGameSettings != none) { KFGameSettings.bNoSeasonalSkins = AllowSeasonalSkinsIndex == 1; + KFGameSettings.WeeklySelectorIndex = WeeklySelectorIndex; } } } @@ -917,7 +942,7 @@ event PreLogin(string Options, string Address, const UniqueNetId UniqueId, bool { local bool bSpectator; local bool bPerfTesting; - local string DesiredDifficulty, DesiredWaveLength, DesiredGameMode; + local string DesiredDifficulty, DesiredWaveLength, DesiredGameMode, DesiredWeeklySelectorIndex; // Check for an arbitrated match in progress and kick if needed if (WorldInfo.NetMode != NM_Standalone && bUsingArbitration && bHasArbitratedHandshakeBegun) @@ -965,8 +990,15 @@ event PreLogin(string Options, string Address, const UniqueNetId UniqueId, bool ErrorMessage = "Server No longer available. Mismatch DesiredGameMode."; return; } - } + DesiredWeeklySelectorIndex = ParseOption(Options, "WeeklySelectorIndex"); + if( DesiredWeeklySelectorIndex != "" && int(DesiredWeeklySelectorIndex) != WeeklySelectorIndex) + { + `log("Got bad weekly selector index"@DesiredWeeklySelectorIndex@"expected"@WeeklySelectorIndex); + ErrorMessage = "Server No longer available. Mismatch DesiredWeeklySelectorIndex."; + return; + } + } bPerfTesting = ( ParseOption( Options, "AutomatedPerfTesting" ) ~= "1" ); bSpectator = bPerfTesting || ( ParseOption( Options, "SpectatorOnly" ) ~= "1" ) || ( ParseOption( Options, "CauseEvent" ) ~= "FlyThrough" ); @@ -1148,6 +1180,9 @@ function InitGRIVariables() MyKFGRI.bVersusGame = bIsVersusGame; MyKFGRI.MaxHumanCount = MaxPlayers; MyKFGRI.NotifyAllowSeasonalSkins(AllowSeasonalSkinsIndex); + MyKFGRI.NotifyWeeklySelector(WeeklySelectorIndex); + + `Log("TEST - InitGRIVariables- NotifyWeeklySelector : " $WeeklySelectorIndex); SetBossIndex(); } @@ -1654,9 +1689,12 @@ function float GetTotalWaveCountScale() return 1.0f; } - if (OutbreakEvent != none && OutbreakEvent.ActiveEvent.WaveAICountScale.Length > 0) + if (OutbreakEvent != none) { - return GetLivingPlayerCount() > OutbreakEvent.ActiveEvent.WaveAICountScale.Length ? OutbreakEvent.ActiveEvent.WaveAICountScale[OutbreakEvent.ActiveEvent.WaveAICountScale.Length - 1] : OutbreakEvent.ActiveEvent.WaveAICountScale[GetLivingPlayerCount() - 1]; + if (OutbreakEvent.ActiveEvent.WaveAICountScale.Length > 0) + { + return GetLivingPlayerCount() > OutbreakEvent.ActiveEvent.WaveAICountScale.Length ? OutbreakEvent.ActiveEvent.WaveAICountScale[OutbreakEvent.ActiveEvent.WaveAICountScale.Length - 1] : OutbreakEvent.ActiveEvent.WaveAICountScale[GetLivingPlayerCount() - 1]; + } } return 1.0f; @@ -2231,7 +2269,7 @@ function class GetLastHitByDamageType(class DT, KFPawn_M } else { - `warn( "GetLastHitByDamageType() Received non-KFDamageType damagetype:"@DT); + //`warn( "GetLastHitByDamageType() Received non-KFDamageType damagetype:"@DT); } return RealDT; @@ -2577,6 +2615,7 @@ protected function DistributeMoneyAndXP(class MonsterClass, cons && DamageHistory[i].DamagerPRI != none ) { EarnedDosh = Round( DamageHistory[i].TotalDamage * ScoreDenominator ); + //`log("SCORING: Player" @ DamageHistory[i].DamagerPRI.PlayerName @ "received" @ EarnedDosh @ "dosh for killing a" @ MonsterClass, bLogScoring); DamagerKFPRI = KFPlayerReplicationInfo(DamageHistory[i].DamagerPRI); if( DamagerKFPRI != none ) @@ -2591,26 +2630,32 @@ protected function DistributeMoneyAndXP(class MonsterClass, cons DamageHistory[i].DamagePerks[0].static.ModifyAssistDosh( EarnedDosh ); } } - if (bIsBossKill && !bSplitBossDoshReward) - { - DamagerKFPRI.AddDosh(GetAdjustedAIDoshValue(MonsterClass), true); - } - else - { - DamagerKFPRI.AddDosh(EarnedDosh, true); - } + if (MyKFGRI.IsBountyHunt() == false) + { + if (bIsBossKill && !bSplitBossDoshReward) + { + DamagerKFPRI.AddDosh(GetAdjustedAIDoshValue(MonsterClass), true); + } + else + { + DamagerKFPRI.AddDosh(EarnedDosh, true); + } + } if( DamagerKFPRI.Team != none ) { //Dosh - if (bIsBossKill && !bSplitBossDoshReward) - { - KFTeamInfo_Human(DamagerKFPRI.Team).AddScore(GetAdjustedAIDoshValue(MonsterClass)); - } - else - { - KFTeamInfo_Human(DamagerKFPRI.Team).AddScore(EarnedDosh); + if (MyKFGRI.IsBountyHunt() == false) + { + if (bIsBossKill && !bSplitBossDoshReward) + { + KFTeamInfo_Human(DamagerKFPRI.Team).AddScore(GetAdjustedAIDoshValue(MonsterClass)); + } + else + { + KFTeamInfo_Human(DamagerKFPRI.Team).AddScore(EarnedDosh); + } } if( DamageHistory[i].DamagePerks.Length <= 0 ) @@ -3003,12 +3048,35 @@ function string GetNextMap() */ if (IsWeekly()) { + MapName = name(GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]); + + if ((class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 0 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[0]) || // Boom + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 1 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[1]) || // Cranium Cracker + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 2 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[2]) || // Tiny Terror + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 3 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[3]) || // Bobble Zed + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 4 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[4]) || // Poundemonium + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 5 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[5]) || // Up Up and Decay + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 6 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[6]) || // Zed Time + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 7 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[7]) || // Beefcake + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 8 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[8]) || // Bloodthirst + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 9 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[9]) || // Coliseum + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 10 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[10]) || // Arachnophobia + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 11 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[11]) || // Scavenger + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[12]) || // WW + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 13 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[13]) || // Abandon + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 15 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[15]) || // Shrunken Heads + (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 18 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[18])) // Perk Roulette + { + if (MapName == MapSantas) + { + continue; + } + } + 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 { - MapName = name(GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]); - if (MapName == MapBiolapse || MapName == MapNightmare || MapName == MapPowerCore || @@ -3021,8 +3089,6 @@ function string GetNextMap() if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 19 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[19]) // Contamination { - MapName = name(GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]); - if (MapName == MapBiolapse || MapName == MapNightmare || MapName == MapPowerCore || @@ -3035,13 +3101,27 @@ function string GetNextMap() } } - /* Temporary removal of SteamFrotress for BossRush */ - if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 14 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[14] && - MapName == MapSteam) + if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 20 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[20]) // Bounty Hunt { - continue; + if (MapName == MapBiolapse || + MapName == MapNightmare || + MapName == MapPowerCore || + MapName == MapDescent || + MapName == MapKrampus || + MapName == MapElysium || + MapName == MapSteam) + { + continue; + } + } + + if (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 14 || OutbreakEvent.ActiveEvent == OutbreakEvent.SetEvents[14]) + { + if (MapName == MapSteam) + { + continue; + } } - /* */ } if ( IsMapAllowedInCycle(GameMapCycles[ActiveMapCycle].Maps[MapCycleIndex]) ) @@ -4001,6 +4081,16 @@ simulated function ModifyDamageGiven(out int InDamage, optional Actor DamageCaus **********************************************/ simulated function NotifyPlayerStatsInitialized(KFPlayerController_WeeklySurvival KFPC){} +simulated function int WeeklyExtraNumberOfZeds() +{ + return 0; +} + +simulated function int WeeklyCurrentExtraNumberOfZeds() +{ + return 0; +} + defaultproperties { /** Scoring */ @@ -4047,6 +4137,7 @@ defaultproperties bWaitingToStartMatch=true bDelayedStart=true AllowSeasonalSkinsIndex=0 + WeeklySelectorIndex=-1 ActionMusicDelay=5.0 ForcedMusicTracks(0)=KFMusicTrackInfo'WW_MMNU_Login.TrackInfo' // menu diff --git a/KFGame/Classes/KFGameReplicationInfo.uc b/KFGame/Classes/KFGameReplicationInfo.uc index c614a9d..24b32c8 100644 --- a/KFGame/Classes/KFGameReplicationInfo.uc +++ b/KFGame/Classes/KFGameReplicationInfo.uc @@ -386,6 +386,7 @@ var repnotify int VIPRepMaxHealth; var repnotify KFPlayerReplicationInfo VIPRepPlayer; var bool bAllowSeasonalSkins; +var int WeeklySelectorIndex; /************************************ * Steam heartbeat @@ -421,11 +422,11 @@ replication 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, bForceSkipTraderUI, VIPRepCurrentHealth, VIPRepMaxHealth, VIPRepPlayer; //@HSL - JRO - 3/21/2016 - PS4 Sessions + bIsEndlessPaused, bForceSkipTraderUI, VIPRepCurrentHealth, VIPRepMaxHealth, VIPRepPlayer; //@HSL - JRO - 3/21/2016 - PS4 Sessions if ( bNetInitial ) GameLength, WaveMax, bCustom, bVersusGame, TraderItems, GameAmmoCostScale, bAllowGrenadePurchase, MaxPerkLevel, bTradersEnabled, bForceShowSkipTrader, bAllowSeasonalSkins; if ( bNetInitial || bNetDirty ) - PerksAvailableData; + CurrentWeeklyIndex, WeeklySelectorIndex, PerksAvailableData; if ( bNetInitial && Role == ROLE_Authority ) ServerAdInfo; @@ -1376,7 +1377,7 @@ simulated function DisplayDebug(HUD HUD, out float YL, out float YPos) TotalClots++; NumAlphas++; } - else if( KFPM.IsA('KFPawn_ZedClot_Cyst') ) + else if( KFPM.IsA('KFPawn_ZedClot_Cyst') || KFPM.IsA('KFPawn_ZedHansClot') ) { TotalClots++; NumUnders++; @@ -2312,10 +2313,16 @@ simulated function NotifyWeeklyEventIndex(int EventIndex) simulated function NotifyAllowSeasonalSkins(int AllowSeasonalSkinsIndex) { bAllowSeasonalSkins = (AllowSeasonalSkinsIndex == 0); - - `Log("NotifyAllowSeasonalSkins: AllowSeasonalSkins: "$bAllowSeasonalSkins); + bNetDirty = true; +} + +simulated function NotifyWeeklySelector(int WeeklySelectorIndex_) +{ + WeeklySelectorIndex = WeeklySelectorIndex_; bNetDirty = true; + + `Log("TEST - NotifyWeeklySelector : " $WeeklySelectorIndex); } /** VIP weekly */ @@ -2449,6 +2456,27 @@ simulated function int ContaminationModeExtraDosh() return 0; } +simulated function bool IsBountyHunt() +{ + return bIsWeeklyMode && CurrentWeeklyIndex == 20; +} + +simulated function int BountyHuntExtraDosh() +{ + local KFGameInfo KFGI; + + if (IsBountyHunt()) + { + KFGI = KFGameInfo(WorldInfo.Game); + if (KFGI != none && KFGI.OutbreakEvent != none) + { + return KFGI.OutbreakEvent.ActiveEvent.BountyHuntExtraDosh; + } + } + + return 0; +} + defaultproperties { TraderItemsPath="GP_Trader_ARCH.DefaultTraderItems" @@ -2471,6 +2499,7 @@ defaultproperties bIsWeeklyMode=false bForceShowSkipTrader=false bAllowSeasonalSkins=true + WeeklySelectorIndex=-1 bForceSkipTraderUI=false GunGameWavesCurrent=1 bWaveGunGameIsFinal=false diff --git a/KFGame/Classes/KFGfxMenu_StartGame.uc b/KFGame/Classes/KFGfxMenu_StartGame.uc index c08d23c..60514e1 100644 --- a/KFGame/Classes/KFGfxMenu_StartGame.uc +++ b/KFGame/Classes/KFGfxMenu_StartGame.uc @@ -33,6 +33,7 @@ var transient int CurrentSearchIndex; var const string ModeKey, DifficultyKey, MapKey, WhitelistedKey, InProgressKey, PermissionsKey, ServerTypeKey; var const string GameLengthKey; var const string AllowSeasonalSkinsKey; +var const string WeeklySelectorKey; var KFGFxStartGameContainer_FindGame FindGameContainer; var KFGFxStartGameContainer_Options OptionsComponent; @@ -68,6 +69,7 @@ var localized string MapTitle; var localized string MutatorTitle; var localized string PermissionsTitle; var localized string AllowSeasonalSkinsTitle; +var localized string WeeklySelectorTitle; var localized string ServerTypeString; var localized string WhiteListedTitle; var localized string InfoTitle; @@ -208,7 +210,7 @@ static function class GetSpecialEventClass case SEI_Summer: return class'KFGFxSpecialEventObjectivesContainer_Summer2023'; case SEI_Fall: - return class'KFGFxSpecialEventObjectivesContainer_Fall2022'; + return class'KFGFxSpecialEventObjectivesContainer_Fall2023'; case SEI_Winter: return class'KFGFXSpecialEventObjectivesContainer_Xmas2022'; } @@ -608,6 +610,7 @@ function SendLeaderOptions() SetLobbyData(ModeKey, String(Manager.GetModeIndex())); SetLobbyData(PermissionsKey, String(OptionsComponent.GetPrivacyIndex())); SetLobbyData(AllowSeasonalSkinsKey, String(OptionsComponent.GetAllowSeasonalSkinsIndex())); + SetLobbyData(WeeklySelectorKey, String(OptionsComponent.GetWeeklySelectorIndex())); } } @@ -640,6 +643,9 @@ function ReceiveLeaderOptions() OptionIndex = Int(OnlineLobby.GetLobbyData(0, AllowSeasonalSkinsKey)); OverviewContainer.UpdateAllowSeasonalSkins(class'KFCommon_LocalizedStrings'.static.GetAllowSeasonalSkinsString(OptionIndex)); + + OptionIndex = Int(OnlineLobby.GetLobbyData(0, WeeklySelectorKey)); + OverviewContainer.UpdateWeeklySelector(class'KFCommon_LocalizedStrings'.static.GetWeeklySelectorString(OptionIndex)); } function ApproveMatchMakingLeave() @@ -924,6 +930,7 @@ function Callback_CancelSearch() function Callback_OptionListOpened(string ListName, int OptionIndex) { local string MessageString; + local int IntendedWeeklyIndex; if (OptionsComponent.bIsSoloGame && ListName == "modeList") { @@ -939,6 +946,12 @@ function Callback_OptionListOpened(string ListName, int OptionIndex) { return; } + + if (ListName == "weeklySelectorList" && OptionIndex == 0) + { + IntendedWeeklyIndex = class'KFGameEngine'.static.GetIntendedWeeklyEventIndexMod(); + OptionIndex = IntendedWeeklyIndex + 1; + } MessageString = Localize("StartMenuHelperText", ListName$OptionIndex, "KFGame"); @@ -1092,6 +1105,11 @@ function Callback_AllowSeasonalSkins(int Index) OptionsComponent.AllowSeasonalSkinsChanged(Index); } +function Callback_WeeklySelector(int Index) +{ + OptionsComponent.WeeklySelectorChanged(Index); +} + function SetLobbyData( string KeyName, string ValueData ) { OnlineLobby.SetLobbyData( KeyName, ValueData ); @@ -1100,14 +1118,25 @@ function SetLobbyData( string KeyName, string ValueData ) function string MakeMapURL(KFGFxStartGameContainer_Options InOptionsComponent) { local string MapName; - local int LengthIndex, ModeIndex, AllowSeasonalSkins; + local int LengthIndex, ModeIndex, AllowSeasonalSkins, WeeklySelectorIndex, IntendedWeeklyIndex; + local string MapURL; + local byte CurrentMenuState; + + MapName = ""; // this is ugly, but effectively makes sure that the player isn't solo with versus selected // or other error cases such as when the game isn't fully installed ModeIndex = InOptionsComponent.GetNormalizedGameModeIndex(Manager.GetModeIndex(true)); LengthIndex = InOptionsComponent.GetLengthIndex(); - MapName = InOptionsComponent.GetMapName(); + CurrentMenuState = GetStartMenuState(); + + // In Find a Match we can't choose map, so don't use the options dropwdown + if (CurrentMenuState != EMatchmaking) + { + MapName = InOptionsComponent.GetMapName(); + } + if (MapName == "" || MapStringList.Find(MapName) == INDEX_NONE) { if (CurrentConnectMap != "" && MapStringList.Find(CurrentConnectMap) != INDEX_NONE) @@ -1148,19 +1177,58 @@ function string MakeMapURL(KFGFxStartGameContainer_Options InOptionsComponent) else { AllowSeasonalSkins = 0; - } + } - if (GetStartMenuState() == EMatchmaking + if (CurrentMenuState == EMatchmaking || class'KFGameEngine'.static.GetSeasonalEventID() == SEI_None || class'KFGameEngine'.static.GetSeasonalEventID() == SEI_Spring) { AllowSeasonalSkins = 1; // Default if we don't have a season or it's find a match menu } - return MapName$"?Game="$class'KFGameInfo'.static.GetGameModeClassFromNum( ModeIndex ) + MapURL = MapName$"?Game="$class'KFGameInfo'.static.GetGameModeClassFromNum( ModeIndex ) $"?Difficulty="$class'KFGameDifficultyInfo'.static.GetDifficultyValue( InOptionsComponent.GetDifficultyIndex() ) $"?GameLength="$LengthIndex $"?AllowSeasonalSkins="$AllowSeasonalSkins; + + if (ModeIndex == 1) // Only when mode is Weekly + { + WeeklySelectorIndex = -1; + + // Depending on StartMenu State we takeover with different weekly index selection + + Switch (CurrentMenuState) + { + case EMatchmaking: + // always default on "Find Game" + WeeklySelectorIndex = 0; + break; + + case ECreateGame: + case ESoloGame: + // use your selection on "Create Game" and "Play Solo" + WeeklySelectorIndex = InOptionsComponent.GetWeeklySelectorIndex(); + + // IF index matches default, set to 0 (default) + if (WeeklySelectorIndex > 0) + { + IntendedWeeklyIndex = class'KFGameEngine'.static.GetIntendedWeeklyEventIndexMod(); + if (IntendedWeeklyIndex == (WeeklySelectorIndex - 1)) + { + WeeklySelectorIndex = 0; + } + } + + break; + } + + if (WeeklySelectorIndex >= 0) + { + MapURL $= "?WeeklySelectorIndex="$WeeklySelectorIndex; + } + } + + return MapURL; } native function bool GetSearchComplete(KFOnlineGameSearch GameSearch); @@ -1510,6 +1578,8 @@ function BuildServerFilters(OnlineGameInterface GameInterfaceSteam, KFGFxStartGa local string GameTagFilters; local ActiveLobbyInfo LobbyInfo; //local bool bAllowSeasonal; + local byte CurrentMenuState; + local int WeeklySelectorIndex, IntendedWeeklyIndex; Search.ClearServerFilters(); @@ -1562,6 +1632,44 @@ function BuildServerFilters(OnlineGameInterface GameInterfaceSteam, KFGFxStartGa Search.AddGametagFilter( GameTagFilters, 'Mode', string(GameMode) ); } + if (GameMode == 1) // Only when mode is Weekly + { + WeeklySelectorIndex = -1; + + // Depending on StartMenu State we Search with different weekly index selection + CurrentMenuState = GetStartMenuState(); + + Switch (EStartMenuState(CurrentMenuState)) + { + /*case EMatchmaking: + // We take any weekly, so don't specify + WeeklySelectorIndex = 0; + break; + */ + case ECreateGame: + case ESoloGame: + // use your selection on "Create Game" and "Play Solo" + WeeklySelectorIndex = OptionsComponent.GetWeeklySelectorIndex(); + + // IF index matches default, set to 0 (default) + if (WeeklySelectorIndex > 0) + { + IntendedWeeklyIndex = class'KFGameEngine'.static.GetIntendedWeeklyEventIndexMod(); + if (IntendedWeeklyIndex == (WeeklySelectorIndex - 1)) + { + WeeklySelectorIndex = 0; + } + } + + break; + } + + if (WeeklySelectorIndex >= 0) + { + Search.AddGametagFilter(GameTagFilters, 'WeeklySelectorIndex', string(WeeklySelectorIndex)); + } + } + //For modes that don't use filtered difficulty, don't even attempt to send this (Ex: Weekly) if (ShouldUseDifficultyFilter(GameMode)) { @@ -1959,6 +2067,7 @@ defaultproperties InProgressKey="InProgress" PermissionsKey="PermissionsKey" AllowSeasonalSkinsKey="AllowSeasonalSkinsKey" + WeeklySelectorKey="WeeklySelectorKey" SearchDSName=KFGameSearch diff --git a/KFGame/Classes/KFGfxMenu_Trader.uc b/KFGame/Classes/KFGfxMenu_Trader.uc index 8bed295..bf492ac 100644 --- a/KFGame/Classes/KFGfxMenu_Trader.uc +++ b/KFGame/Classes/KFGfxMenu_Trader.uc @@ -780,6 +780,16 @@ function Callback_PerkChanged(int PerkIndex) { MyKFPC.SetHaveUpdatePerk(true); + if (KFPerk_Survivalist(MyKFPC.CurrentPerk) != none) + { + KFPerk_Survivalist(MyKFPC.CurrentPerk).StartingWeaponClassIndex = MyKFPC.SurvivalPerkWeapIndex; + KFPerk_Survivalist(MyKFPC.CurrentPerk).StartingGrenadeClassIndex = MyKFPC.SurvivalPerkGrenIndex; + + KFPerk_Survivalist(MyKFPC.CurrentPerk).UpdateCurrentGrenade(); + + MyKFPC.GetPurchaseHelper().InitializeOwnedGrenade(); + } + // re-initialize and refresh to reflect current carry weight (can change by perk) MyKFPC.GetPurchaseHelper().Initialize(false); RefreshItemComponents(); @@ -792,6 +802,7 @@ function Callback_PerkChanged(int PerkIndex) { PlayerInventoryContainer.UpdateLock(); } + UpdatePlayerInfo(); // Refresht he UI diff --git a/KFGame/Classes/KFGfxMoviePlayer_Manager.uc b/KFGame/Classes/KFGfxMoviePlayer_Manager.uc index eeb53a0..e05e686 100644 --- a/KFGame/Classes/KFGfxMoviePlayer_Manager.uc +++ b/KFGame/Classes/KFGfxMoviePlayer_Manager.uc @@ -1872,6 +1872,20 @@ event bool FilterButtonInput(int ControllerId, name ButtonName, EInputEvent Inpu CurrentMenu.Callback_ReadyClicked(true); } } + else if (ButtonName == 'XboxTypeS_DPad_Left') + { + if(CurrentMenu != none) + { + CurrentMenu.OnDpadPressed(-1); + } + } + else if (ButtonName == 'XboxTypeS_DPad_Right') + { + if(CurrentMenu != none) + { + CurrentMenu.OnDpadPressed(1); + } + } else if(ButtonName == 'XboxTypeS_RightThumbstick') { if(CurrentMenu != none) @@ -2278,6 +2292,11 @@ function int GetModeIndex(optional bool bAdjustedIndex = true) return SavedModeIndex; } +function int GetWeeklySelectorIndex() +{ + return CachedProfile.GetProfileInt(KFID_SavedWeeklySelectorIndex); +} + function OnLoginOnOtherPlatformDoneAndFriendsReady() { local KFGFxPopup_FriendsList FriendsList; diff --git a/KFGame/Classes/KFGfxObject_Menu.uc b/KFGame/Classes/KFGfxObject_Menu.uc index a638d86..a7d878a 100644 --- a/KFGame/Classes/KFGfxObject_Menu.uc +++ b/KFGame/Classes/KFGfxObject_Menu.uc @@ -36,6 +36,11 @@ function InitOnlineLobby() OnlineLobby = GetPC().OnlineSub.GetLobbyInterface(); } } + +function OnDpadPressed(int Right) +{ +} + function OnR3Pressed() { local KFPlayerController KFPC; @@ -516,6 +521,16 @@ function Callback_OnLoadoutNextWeaponPressed() Manager.PerksMenu.OnNextWeaponPressed(); } +function Callback_OnLoadoutPrevSecondaryWeaponPressed() +{ + Manager.PerksMenu.OnPrevSecondaryWeaponPressed(); +} + +function Callback_OnLoadoutNextSecondaryWeaponPressed() +{ + Manager.PerksMenu.OnNextSecondaryWeaponPressed(); +} + function Callback_OnLoadoutPrevGrenadePressed() { Manager.PerksMenu.OnPrevGrenadePressed(); diff --git a/KFGame/Classes/KFHUDBase.uc b/KFGame/Classes/KFHUDBase.uc index 52f8bfa..ab20665 100644 --- a/KFGame/Classes/KFHUDBase.uc +++ b/KFGame/Classes/KFHUDBase.uc @@ -673,6 +673,11 @@ function DrawHUD() // Draw last remaining zeds CheckAndDrawRemainingZedIcons(); + if (KFGRI.IsBountyHunt()) + { + CheckAndDrawBountyHudIcons(); + } + if (KFGRI.IsContaminationMode()) { // While on trader time we let previsualize the next objective icon, so players can get ready @@ -698,7 +703,19 @@ function DrawHUD() Canvas.EnableStencilTest(false); } - + else + { + if( !KFGRI.bHidePawnIcons ) + { + // Draw last remaining zeds + CheckAndDrawRemainingZedIcons(); + + if (KFGRI.IsBountyHunt()) + { + CheckAndDrawBountyHudIcons(); + } + } + } } } @@ -1382,6 +1399,7 @@ function CheckAndDrawRemainingZedIcons() local Pawn P; local vector ViewLocation, ViewDir, PawnLocation; local rotator ViewRotation; + local KFPawn_Monster Monster; if( KFGRI == none || KFPlayerOwner == none @@ -1402,20 +1420,109 @@ function CheckAndDrawRemainingZedIcons() if( P.Mesh.SkeletalMesh == none || !P.Mesh.bAnimTreeInitialised || P.GetTeamNum() == PlayerOwner.GetTeamNum() - || !P.IsAliveAndWell()) + || !P.IsAliveAndWell() ) //|| `TimeSince(P.Mesh.LastRenderTime) < 0.2f ) { continue; } + Monster = KFPawn_Monster(P); + if (Monster != none && Monster.bIsBountyHuntObjective) + { + continue; + } + PawnLocation = P.Mesh.GetPosition(); - DrawZedIcon( P, PawnLocation, Normal((PawnLocation + (P.CylinderComponent.CollisionHeight * vect(0, 0, 1))) - ViewLocation) dot ViewDir); + DrawZedIcon( P, PawnLocation + , Normal((PawnLocation + (P.CylinderComponent.CollisionHeight * vect(0, 0, 1))) - ViewLocation) dot ViewDir + , ZedIconColor, 1.f); } } +function CheckAndDrawBountyHudIcons() +{ + local KFPawn_Monster Monster; + local vector ViewLocation, ViewDir, PawnLocation; + local rotator ViewRotation; + local color ZedColor; + local float WaveProgress; + + if( KFGRI == none + || KFPlayerOwner == none + || KFPlayerOwner.PlayerCamera == none + || KFGRI.IsBossWave() + || KFGRI.IsEndlessWave()) + { + return; + } + + KFPlayerOwner.PlayerCamera.GetCameraViewPoint( ViewLocation, ViewRotation ); + ViewDir = vector( ViewRotation ); + + if (KFGRI.WaveTotalAICount > 0) + { + WaveProgress = float(KFGRI.AIRemaining) / float(KFGRI.WaveTotalAICount); + } + else + { + WaveProgress = 1.f; + } + + foreach WorldInfo.AllPawns( class'KFPawn_Monster', Monster ) + { + if (Monster.bIsBountyHuntObjective == false) + { + continue; + } + + if (Monster.IsAliveAndWell() == false) + { + continue; + } + + if (Monster.Mesh.SkeletalMesh != none + && Monster.Mesh.bAnimTreeInitialised) + { + PawnLocation = Monster.Mesh.GetPosition(); + } + else + { + PawnLocation = Monster.Location; + } + + ZedColor = ZedIconColor; + + if (Monster.bIsBountyHuntOnLastTier) + { + // Red (R = 255, G = 0, B = 0, A = 192) + ZedColor.R = 255; + ZedColor.G = 0; + ZedColor.B = 0; + } + else if (WaveProgress < 0.5f) + { + // Orange (R = 255, G = 128, B = 0, A = 192) + ZedColor.R = 255; + ZedColor.G = 128; + ZedColor.B = 0; + } + else + { + // Yellow (R = 255, G = 255, B = 0, A = 192) + ZedColor.R = 255; + ZedColor.G = 255; + ZedColor.B = 0; + } + + DrawZedIcon( Monster, PawnLocation + , Normal((PawnLocation + (Monster.CylinderComponent.CollisionHeight * vect(0, 0, 1))) - ViewLocation) dot ViewDir + , ZedColor, 1.5f); + } +} + /** Draws a zed icon */ -function DrawZedIcon( Pawn ZedPawn, vector PawnLocation, float NormalizedAngle ) +function DrawZedIcon( Pawn ZedPawn, vector PawnLocation, float NormalizedAngle, color ColorToUse, float SizeMultiplier ) { local vector ScreenPos, TargetLocation; local float IconSizeMult; @@ -1425,7 +1532,7 @@ function DrawZedIcon( Pawn ZedPawn, vector PawnLocation, float NormalizedAngle ) TargetLocation = PawnLocation + ( vect(0,0,2.5f) * ZedPawn.CylinderComponent.CollisionHeight ); ScreenPos = Canvas.Project( TargetLocation ); - IconSizeMult = PlayerStatusIconSize * ResModifier * 0.5f; + IconSizeMult = PlayerStatusIconSize * ResModifier * 0.5f * SizeMultiplier; ScreenPos.X -= IconSizeMult; ScreenPos.Y -= IconSizeMult; @@ -1448,9 +1555,8 @@ function DrawZedIcon( Pawn ZedPawn, vector PawnLocation, float NormalizedAngle ) ScreenPos = GetClampedScreenPosition(ScreenPos); } - // Draw boss icon - Canvas.SetDrawColorStruct( ZedIconColor ); + Canvas.SetDrawColorStruct( ColorToUse ); Canvas.SetPos( ScreenPos.X, ScreenPos.Y ); Canvas.DrawTile( GenericZedIconTexture, IconSizeMult, IconSizeMult, 0, 0, 128, 128 ); } diff --git a/KFGame/Classes/KFInventoryManager.uc b/KFGame/Classes/KFInventoryManager.uc index e449ad3..52f1cc7 100644 --- a/KFGame/Classes/KFInventoryManager.uc +++ b/KFGame/Classes/KFInventoryManager.uc @@ -543,6 +543,22 @@ simulated function Weapon GetBestWeapon( optional bool bForceADifferentWeapon, o { local KFWeapon W, BestWeapon, BackupGun; local float Rating, BestRating; + local PlayerController PC; + + PC = PlayerController(Instigator.Controller); + if (PC != none && KFPawn(PC.Pawn) != none) + { + BestWeapon = KFPawn(PC.Pawn).FindBestWeapon(true, allow9mm); + if (BestWeapon == none && allow9mm == false) + { + BestWeapon = KFPawn(PC.Pawn).FindBestWeapon(true, true); + } + + if (BestWeapon != none) + { + return BestWeapon; + } + } ForEach InventoryActors( class'KFWeapon', W ) { @@ -1369,7 +1385,7 @@ simulated function AttemptQuickHeal() /** Equip the welder immediately */ simulated function bool QuickWeld() { - local KFWeapon KFW; + local KFWeapon KFW, PreviousKFW; local KFInterface_Usable UsableTrigger; local KFDoorTrigger DoorTrigger; local KFRepairableActorTrigger RepairableTrigger; @@ -1382,8 +1398,8 @@ simulated function bool QuickWeld() } // make sure player is actually allowed to switch weapons - KFW = KFWeapon( Instigator.Weapon ); - if( KFW != none && !KFW.CanSwitchWeapons() ) + PreviousKFW = KFWeapon( Instigator.Weapon ); + if( PreviousKFW != none && !PreviousKFW.CanSwitchWeapons() ) { return false; } @@ -1413,6 +1429,7 @@ simulated function bool QuickWeld() { if( KFW.IsA('KFWeap_Welder') ) { + KFPC.MyGFxHUD.WeaponSelectWidget.UpdateWeaponGroupOnHUD(PreviousKFW.InventoryGroup); SetCurrentWeapon(KFW); ShowAllHUDGroups(); return true; @@ -2674,7 +2691,8 @@ simulated function int GetAdjustedSellPriceFor( // if OwnedItem is a dual, set sell price to that of a single (because we sell one single and keep one single) // Special case for 9mm - if( OwnedItem.SingleClassName == 'KFWeap_Pistol_9mm') + if( OwnedItem.SingleClassName == 'KFWeap_Pistol_9mm' + || OwnedItem.SingleClassName == 'KFWeap_HRG_93R' ) { // @todo: revisit // assume price of single is half the price of dual. might be better to use the actual buy price of the single, @@ -2712,7 +2730,7 @@ simulated function int GetDisplayedBlocksRequiredFor( const out STraderItem Shop // for now, only adjust blocks required for duals, except for dual 9mm since the single 9mm doesn't require any blocks // display half weight of dual if player owns single - if( !(ShopItem.SingleClassName == '' || ShopItem.SingleClassName == 'KFWeap_Pistol_9mm') && GetIsOwned(ShopItem.SingleClassName) ) + if( !(ShopItem.SingleClassName == '' || ShopItem.SingleClassName == 'KFWeap_Pistol_9mm' || ShopItem.SingleClassName == 'KFWeap_HRG_93R') && GetIsOwned(ShopItem.SingleClassName) ) { BlocksRequired /= 2; } @@ -2798,6 +2816,21 @@ simulated event DiscardInventory() } } +simulated function bool Is9mmInInventory() +{ + local Inventory Inv; + + for (Inv = InventoryChain; Inv != None; Inv = Inv.Inventory) + { + if (Inv.Class.name == 'KFWeap_Pistol_9mm' || Inv.Class.name == 'KFWeap_Pistol_Dual9mm') + { + return true; + } + } + + return false; +} + defaultproperties { PendingFire(0)=0 // DEFAULT_FIREMOD diff --git a/KFGame/Classes/KFMission_LocalizedStrings.uc b/KFGame/Classes/KFMission_LocalizedStrings.uc index f029cf5..b5f365c 100644 --- a/KFGame/Classes/KFMission_LocalizedStrings.uc +++ b/KFGame/Classes/KFMission_LocalizedStrings.uc @@ -13,6 +13,7 @@ class KFMission_LocalizedStrings extends Object var localized string GrantedWeeklyString; var localized string WeeklyString; +var localized string WeeklyCustomString; var localized string CurrentWeeklySettingsString; var localized string SpecialEventString; diff --git a/KFGame/Classes/KFOnlineStatsReadDingo.uc b/KFGame/Classes/KFOnlineStatsReadDingo.uc index a9bcf20..5320ab9 100644 --- a/KFGame/Classes/KFOnlineStatsReadDingo.uc +++ b/KFGame/Classes/KFOnlineStatsReadDingo.uc @@ -74,6 +74,7 @@ defaultproperties ColumnIds.Add(STATID_ACHIEVE_BarmwichCollectibles) ColumnIds.Add(STATID_ACHIEVE_CrashCollectibles); ColumnIds.Add(STATID_ACHIEVE_SubductionCollectibles); + ColumnIds.Add(STATID_ACHIEVE_VolterCastleCollectibles); ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky5, Name="AchievementMrPerky5")) ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky10, Name = "AchievementMrPerky10")) @@ -136,5 +137,6 @@ defaultproperties ColumnMappings.Add((Id=STATID_ACHIEVE_BarmwichCollectibles,Name="AchievementCollectBarmwichTown")) ColumnMappings.Add((Id=STATID_ACHIEVE_CrashCollectibles,Name="AchievementCollectCrash")) ColumnMappings.Add((Id=STATID_ACHIEVE_SubductionCollectibles,Name="AchievementCollectSubduction")) + ColumnMappings.Add((Id=STATID_ACHIEVE_VolterCastleCollectibles,Name="AchievementCollectVolterCastle")) } diff --git a/KFGame/Classes/KFOnlineStatsWrite.uc b/KFGame/Classes/KFOnlineStatsWrite.uc index f7deb1f..d217a03 100644 --- a/KFGame/Classes/KFOnlineStatsWrite.uc +++ b/KFGame/Classes/KFOnlineStatsWrite.uc @@ -457,6 +457,10 @@ const KFACHID_SubductionHard = 301; const KFACHID_SubductionHellOnEarth = 302; const KFACHID_SubductionCollectibles = 303; +const KFACHID_VolterCastleHard = 304; +const KFACHID_VolterCastleHellOnEarth = 305; +const KFACHID_VolterCastleCollectibles = 306; + /* __TW_ANALYTICS_ */ var int PerRoundWeldXP; var int PerRoundHealXP; @@ -1195,6 +1199,11 @@ private event AddAfflictionCaused(EAfflictionType Type) SeasonalEventStats_OnAfflictionCaused(Type); } +private event AddCollectibleFound(int Limit) +{ + SeasonalEventStats_OnCollectibleFound(Limit); +} + private native function AddToKillObjectives(class ZedClass); private native function AddToVersusKillObjectives(class KillerClass); @@ -1925,6 +1934,14 @@ final simulated function SeasonalEventStats_OnAfflictionCaused(EAfflictionType T } } +final simulated function SeasonalEventStats_OnCollectibleFound(int Limit) +{ + if (SeasonalEventIsValid()) + { + SeasonalEvent.OnCollectibleFound(Limit); + } +} + /********************************************************************************************* * @name Dailies and daily-specific tracking ********************************************************************************************* */ @@ -2069,6 +2086,7 @@ defaultproperties //Base Weapons DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,SecondaryType=DOST_KnifeDamage,ObjectiveClasses=(KFWeap_Edged_Knife,KFDT_Slashing_Knife,KFDT_Slashing_Knife_Berserker,KFDT_Slashing_Knife_Medic,KFDT_Slashing_Knife_SWAT,KFDT_Slashing_KnifeHeavy,KFDT_Slashing_KnifeHeavy_Berserker,KFDT_Slashing_KnifeHeavy_Medic,KFDT_Slashing_KnifeHeavy_SWAT,KFDT_Piercing_KnifeStab,KFDT_Piercing_KnifeStab_Berserker,KFDT_Piercing_KnifeStab_FieldMedic,KFDT_Piercing_KnifeStab_SWAT,KFDT_Slashing_Knife_Survivalist,KFDT_Piercing_KnifeStab_Survivalist,KFDT_Slashing_KnifeHeavy_Survivalist),CompletionAmount=2500)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Pistol_9mm, KFDT_Ballistic_9mm,KFDT_Bludgeon_9mm),CompletionAmount=4000)) //2500 + DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_93R, KFDT_Ballistic_HRG_93R, KFDT_Bludgeon_HRG_93R),CompletionAmount=4000)) //Swat Weapons DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_SMG_MP7, KFDT_Ballistic_MP7,KFDT_Bludgeon_MP7),CompletionAmount=5000)) //3000 @@ -2089,6 +2107,7 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_AssaultRifle_MKB42, KFDT_Ballistic_MKB42,KFDT_Bludgeon_MKB42),CompletionAmount=10000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_LMG_Stoner63A, KFDT_Ballistic_Stoner63A,KFDT_Bludgeon_Stoner63A),CompletionAmount=10000)) + //Support Weapons DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Shotgun_MB500, KFDT_Ballistic_MB500,KFDT_Bludgeon_MB500),CompletionAmount=5000)) //3000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Shotgun_DoubleBarrel, KFDT_Ballistic_DBShotgun,KFDT_Bludgeon_DBShotgun),CompletionAmount=7000)) //5000 @@ -2333,7 +2352,9 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-SUBDUCTION),CompletionAmount=1)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-SUBDUCTION),CompletionAmount=2)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-SUBDUCTION),CompletionAmount=3)) - + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-CASTLEVOLTER),CompletionAmount=1)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-CASTLEVOLTER),CompletionAmount=2)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-CASTLEVOLTER),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 bd03371..0dcce4a 100644 --- a/KFGame/Classes/KFOnlineStatsWriteDingo.uc +++ b/KFGame/Classes/KFOnlineStatsWriteDingo.uc @@ -76,4 +76,5 @@ defaultproperties Properties.Add((PropertyId = STATID_ACHIEVE_BarmwichCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_CrashCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_SubductionCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) + Properties.Add((PropertyId = STATID_ACHIEVE_VolterCastleCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) } diff --git a/KFGame/Classes/KFOutbreakEvent.uc b/KFGame/Classes/KFOutbreakEvent.uc index 1f13493..ad9cf1d 100644 --- a/KFGame/Classes/KFOutbreakEvent.uc +++ b/KFGame/Classes/KFOutbreakEvent.uc @@ -164,6 +164,51 @@ struct GunGamePerkData var() array GunGameRespawnLevels; }; +struct BountyHuntWavePerPlayerZedData +{ + var() int NumberOfPlayers; + var() int NumberOfZeds; +}; + +struct BountyHuntWaveData +{ + var() int Wave; + var() array BountyHuntWavePerPlayerZed; +}; + +struct BountyHuntSpecialZedPerWaveData +{ + var() int Wave; +}; + +struct BountyHuntZedProgressionData +{ + var() float RemainingZedRatio; + var() float HealthBuffRatio; + var() float DamageBuffRatio; +}; + +struct BountyHuntZedAndProgressionData +{ + var() class ZedType; + var() array BountyHuntSpecialZedPerWave; + var() array BountyHuntZedProgression; +}; + +struct BountyHuntDoshData +{ + var() int NumberOfPlayers; + var() int Dosh; + var() int DoshNoAssist; +}; + +struct BountyHuntGameData +{ + var() array BountyHuntDataWaves; + var() array BountyHuntZedAndProgression; + var() array BountyHuntDosh; +}; + /** Individual property overrides that drive other behavior to allow for * a large amount of variety in our weekly event mode. */ @@ -455,6 +500,8 @@ struct WeeklyOverrides var() bool bVIPGameMode; + var() bool bBountyHunt; + /** Ignores damage caused by headshots. */ var() bool bInvulnerableHeads; @@ -470,6 +517,26 @@ struct WeeklyOverrides /** Contamination mode Extra Dosh */ var() int ContaminationModeExtraDosh; + // Bounty Hunt + /** Information about each level in Gun Game Mode */ + var() BountyHuntGameData BountyHuntGame; + var() int BountyHuntExtraDosh; + var() bool BountyHuntNeedsToSeePlayerToTriggerFlee; + var() float BountyHuntTimeBetweenFlee; + var() float BountyHuntTimeBetweenAttack; + var() float BountyHuntTimeCanCancelAttack; + var() float BountyHuntDistancePlayerMinFirstFlee; + var() float BountyHuntDistancePlayerMinFlee; + var() float BountyHuntDistancePlayerMaxFlee; + var() float BountyHuntDistancePlayerAttack; + var() float BountyHuntSpecialZedBuffHealthRatio; + var() float BountyHuntSpecialZedBuffAfflictionResistance; + var() float BountyHuntMaxCoexistingZeds; + var() bool BountyHuntLastLevelStillUsesCoexistingZeds; + var() bool BountyHuntUseGradualSpawn; + + var() float HeadshotDamageMultiplier; + structdefaultproperties { GameLength = GL_Short @@ -531,12 +598,28 @@ struct WeeklyOverrides bDisableThrowWeapon = false; bGunGameMode = false; bVIPGameMode = false; + bBountyHunt = false; bInvulnerableHeads = false; TraderTimeModifier = 1.f; TimeBetweenWaves = -1.f; bForceShowSkipTrader = false; ContaminationModeZedsToFinish = 0; ContaminationModeExtraDosh = 0; + BountyHuntExtraDosh = 0; + BountyHuntNeedsToSeePlayerToTriggerFlee = false; + BountyHuntTimeBetweenFlee = 10.f; + BountyHuntTimeBetweenAttack = 12.f; + BountyHuntTimeCanCancelAttack = 5.f; + BountyHuntDistancePlayerMinFirstFlee = 100.f; + BountyHuntDistancePlayerMinFlee = 1000.f; + BountyHuntDistancePlayerMaxFlee = 2200.f; + BountyHuntDistancePlayerAttack = 400.f; + BountyHuntSpecialZedBuffHealthRatio=0.25f + BountyHuntSpecialZedBuffAfflictionResistance=0.5f + BountyHuntMaxCoexistingZeds = 6 + BountyHuntLastLevelStillUsesCoexistingZeds=false + BountyHuntUseGradualSpawn = false + HeadshotDamageMultiplier = 1.f } }; @@ -573,7 +656,7 @@ var WeeklyOverrides ActiveEvent; /** Stored values of World Info and GRI items incase we need to reset it. */ var CachedOutbreakInfo CachedItems; -function int SetActiveEvent(int ActiveEventIdx) +function int SetActiveEvent(int ActiveEventIdx, KFGameInfo GameInfo) { `if(`notdefined(ShippingPC)) local string LocalURL; @@ -583,7 +666,19 @@ function int SetActiveEvent(int ActiveEventIdx) //Runtime override by URL options for testing purposes LocalURL = WorldInfo.GetLocalURL(); LocalURL = Split(LocalURL, "?"); //remove map name - ActiveEventIdx = GetIntOption(LocalURL, "ActiveEventIdx", ActiveEventIdx); + + if (GameInfo.WeeklySelectorIndex == -1) // WeeklySelectorIndex overrides the ActiveEventIdx if any + { + ActiveEventIdx = GetIntOption(LocalURL, "ActiveEventIdx", ActiveEventIdx); + + // Set WeeklySelectorIndex to the value (ActiveEventIdx is not replicated), so all the flow of the game works the same + GameInfo.WeeklySelectorIndex = ActiveEventIdx + 1; + } + else if (GameInfo.WeeklySelectorIndex > 0) + { + // 0 is default, we move one index to the left so it matches first Weekly Mode + ActiveEventIdx = GameInfo.WeeklySelectorIndex - 1; + } //If our override is out of bounds, see if it's a valid test event if (ActiveEventIdx >= SetEvents.Length) @@ -599,12 +694,22 @@ function int SetActiveEvent(int ActiveEventIdx) ActiveEvent = SetEvents[ActiveEventIdx]; } `else + if (GameInfo.WeeklySelectorIndex > 0) + { + // 0 is default, we move one index to the left so it matches first Weekly Mode + ActiveEventIdx = GameInfo.WeeklySelectorIndex - 1; + } + if(ActiveEventIdx < SetEvents.length) { ActiveEvent = SetEvents[ActiveEventIdx]; } `endif + `Log("TEST - SetActiveEvent : " $ActiveEventIdx); + + KFGameEngine(class'Engine'.static.GetEngine()).SetWeeklyEventIndex(ActiveEventIdx); + return ActiveEventIdx; } @@ -744,7 +849,14 @@ function ModifyGroundSpeed(KFPawn PlayerPawn, out float GroundSpeed) function ReduceDamage(out int Damage, Pawn Injured, Controller InstigatedBy, class DamageType, TraceHitInfo HitInfo) { local int HitZoneIdx, WaveNum; - local KFPawn InstigatorPawn; + local KFPawn InjuredPawn, InstigatorPawn; + + InjuredPawn = KFPawn(Injured); + + if (InjuredPawn != none) + { + `log(self @ "ReduceDamage=" $ Damage, InjuredPawn.bLogTakeDamage); + } //Some events can be headshot only. Do this only if the incoming damage is against a monster-derived class // and it's one of our custom damage types. Keeps things like crush damage from being scaled to 0. @@ -765,6 +877,14 @@ function ReduceDamage(out int Damage, Pawn Injured, Controller InstigatedBy, cla Damage = 0; } } + else if (ActiveEvent.HeadshotDamageMultiplier != 1.f && KFPawn_Monster(Injured) != none && KFPawn_Monster(Injured).bIsBountyHuntObjective) + { + HitZoneIdx = KFPawn_Monster(Injured).HitZones.Find('ZoneName', HitInfo.BoneName); + if (HitZoneIdx == HZI_Head) + { + Damage *= ActiveEvent.HeadshotDamageMultiplier; + } + } if (InstigatedBy != none) { @@ -792,6 +912,11 @@ function ReduceDamage(out int Damage, Pawn Injured, Controller InstigatedBy, cla AdjustDamageReduction(Damage, Injured, InstigatedBy, InstigatorPawn, ActiveEvent.BossRushOverrideParams.PerWaves[WaveNum].ZedsToAdjust); } } + + if (InjuredPawn != none) + { + `log(self @ "ReduceDamage (after)=" $ Damage, InjuredPawn.bLogTakeDamage); + } } function AdjustDamageReduction(out int Damage, Pawn Injured, Controller InstigatedBy, KFPawn InstigatorPawn, array Adjustments) diff --git a/KFGame/Classes/KFPawn.uc b/KFGame/Classes/KFPawn.uc index 7518c18..902d465 100644 --- a/KFGame/Classes/KFPawn.uc +++ b/KFGame/Classes/KFPawn.uc @@ -2059,7 +2059,7 @@ final simulated function bool CanReloadWeapon() return TRUE; } -function KFWeapon FindBestWeapon() +function KFWeapon FindBestWeapon(optional bool bCheckHasAmmo = false, optional bool allow9mm = true) { local float BestPrimaryRating, BestSecondaryRating, BestMeleeRating; local KFWeapon BestWeapon, BestPrimary, BestSecondary, BestMelee, TempWeapon; @@ -2072,6 +2072,22 @@ function KFWeapon FindBestWeapon() continue; } + if (allow9mm == false) + { + if (TempWeapon.bIsBackupWeapon && !TempWeapon.IsMeleeWeapon()) + { + continue; + } + } + + if (bCheckHasAmmo) + { + if ( TempWeapon.HasAnyAmmo() == false ) + { + continue; + } + } + // Collect the best weapons from each inventory group if (TempWeapon.InventoryGroup == IG_Primary) { @@ -2100,7 +2116,8 @@ function KFWeapon FindBestWeapon() } // Get our best possible weapon from all 3 categories and throw it - BestWeapon = BestPrimary != none ? BestPrimary : (BestSecondary != none ? BestSecondary : BestMelee); + BestWeapon = BestPrimary != none ? BestPrimary : (BestMelee != none ? BestMelee : BestSecondary); + return BestWeapon; } /** @@ -2725,6 +2742,13 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector // NVCHANGE_BEGIN - RLS - Debugging Effects bAllowHeadshot = CanCountHeadshots(); OldHealth = Health; + + KFDT = class(DamageType); + if ( KFDT != None ) + { + KFDT.static.ModifyInstantDamage(self, Damage, InstigatedBy); + } + Super.TakeDamage(Damage, InstigatedBy, HitLocation, Momentum, DamageType, HitInfo, DamageCauser); // using the passed in damage type instead of the hitfxinfo since that doesn't get updated when zero damage is done @@ -2740,7 +2764,6 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector // damage. Requires valid DamageCauser, 'None' for DoT, to prevent recursion if ( Health < OldHealth && DamageCauser != None ) { - KFDT = class(DamageType); if ( KFDT != None ) { KFDT.static.ApplySecondaryDamage(self, Damage, InstigatedBy); diff --git a/KFGame/Classes/KFPawn_Monster.uc b/KFGame/Classes/KFPawn_Monster.uc index 7e11a0e..b68bac1 100644 --- a/KFGame/Classes/KFPawn_Monster.uc +++ b/KFGame/Classes/KFPawn_Monster.uc @@ -42,6 +42,8 @@ var transient bool bArchLoaded; var const array EliteAIType; // can't use EAIType enumerator, use integer instead var const array > ElitePawnClass; +var const array > MapReplacePawnClass; + /** Custom third person camera offsets */ var() ViewOffsetData ThirdPersonViewOffset; @@ -579,6 +581,11 @@ var ParticleSystem WeakPointParticleTemplate; var bool bCanBeKilledByShrinking; var float ShrinkEffectModifier; +// Bounty Hunt +var bool bIsBountyHuntObjective; +var bool bIsBountyHuntOnLastTier; +var bool bIsBountyHuntBlockLeaveMap; + /********************************************************************************************* * @name Delegates ********************************************************************************************* */ @@ -593,7 +600,8 @@ replication if (bNetDirty) bIsHeadless, bIsPoisoned, bPlayPanicked, bPlayShambling, MaxHeadChunkGoreWhileAlive, RepInflateMatParams, RepInflateMatParam, RepDamageInflateParam, RepBleedInflateMatParam, bDisableGoreMeshWhileAlive, - bDisableHeadless, InflateDeathGravity, InflationExplosionTimer, bUseDamageInflation, bUseExplosiveDeath, WeakPoints_TS; + bDisableHeadless, InflateDeathGravity, InflationExplosionTimer, bUseDamageInflation, bUseExplosiveDeath, WeakPoints_TS, + bIsBountyHuntObjective, bIsBountyHuntOnLastTier, bIsBountyHuntBlockLeaveMap; if ( bNetDirty && bCanCloak ) bIsCloakingSpottedByTeam; if ( bNetDirty && bCanRage ) @@ -699,7 +707,12 @@ simulated event ReplicatedEvent(name VarName) case nameof(WeakPoints_TS): SpawnWeakpointVFX(); break; + case nameof(bIsBountyHuntOnLastTier): + break; + case nameof(bIsBountyHuntBlockLeaveMap): + break; } + super.ReplicatedEvent( VarName ); } @@ -768,6 +781,16 @@ static event class GetAIPawnClassToSpawn() return default.class; } +static event class GetAIPawnClassToReplaceMapSpawn() +{ + if (default.MapReplacePawnClass.length > 0) + { + return default.MapReplacePawnClass[Rand(default.MapReplacePawnClass.length)]; + } + + return default.class; +} + /** Load content archetype when map loads */ native static final function PreloadContent(); /** Called if preload was not called before 1st spawn */ @@ -851,6 +874,11 @@ simulated event CheckShouldAlwaysBeRelevant() { bAlwaysRelevant = true; } + + if (bIsBountyHuntObjective) + { + bAlwaysRelevant = true; + } } /** Called immediately after gameplay begins */ @@ -1892,12 +1920,24 @@ simulated function float GetMinBlockFOV() /** Reduce affliction power when blocking */ simulated function AdjustAffliction( out float AfflictionPower ) { + local KFGameInfo KFGI; + if( Role == ROLE_Authority && bIsBlocking ) { AfflictionPower *= DifficultyBlockSettings.AfflictionModifier; } super.AdjustAffliction( AfflictionPower ); + + if (bIsBountyHuntObjective) // Only on Bounty Hunt Weekly + { + KFGI = KFGameInfo(WorldInfo.Game); + + if (KFGI != none && KFGI.OutbreakEvent != none) + { + AfflictionPower -= AfflictionPower * KFGI.OutbreakEvent.ActiveEvent.BountyHuntSpecialZedBuffAfflictionResistance; + } + } } /** Used by subclasses to determine if the boss icon can be rendered */ @@ -2338,8 +2378,12 @@ function AdjustDamage(out int InDamage, out vector Momentum, Controller Instigat // NVCHANGE_BEGIN - RLS - Debugging Effects // Do extra damage on blowing the head off - if( !bCheckingExtraHeadDamage && HitZoneIdx == HZI_Head && - HitZones[HZI_Head].GoreHealth > 0 && InDamage > HitZones[HZI_Head].GoreHealth ) + + if (bIsBountyHuntObjective == false + && !bCheckingExtraHeadDamage + && HitZoneIdx == HZI_Head + && HitZones[HZI_Head].GoreHealth > 0 + && InDamage > HitZones[HZI_Head].GoreHealth) { KFDT = class(DamageType); @@ -2349,7 +2393,7 @@ function AdjustDamage(out int InDamage, out vector Momentum, Controller Instigat InDamage *= KFDT.default.HeadDestructionDamageScale; } - ExtraHeadDamage = InDamage + HealthMax * 0.25; + ExtraHeadDamage = (InDamage + HealthMax * 0.25); bCheckingExtraHeadDamage = true; AdjustDamage( ExtraHeadDamage, Momentum, InstigatedBy, HitLocation, DamageType, HitInfo, DamageCauser ); @@ -2966,7 +3010,7 @@ simulated function UpdateVisualInflation(float InflationAmount) /** Called on server when pawn should has been crippled (e.g. Headless) */ function CauseHeadTrauma(float BleedOutTime=5.f) { - if ( !bIsHeadless && !bPlayedDeath && !bDisableHeadless ) + if ( !bIsHeadless && !bPlayedDeath && !bDisableHeadless && !bIsBountyHuntObjective ) { if( MyKFAIC != none && KFGameInfo(WorldInfo.Game) != none && MyKFAIC.TimeFirstSawPlayer >= 0 ) { @@ -4202,7 +4246,7 @@ function TakeHitZoneDamage(float Damage, class DamageType, int HitZo if ( HitZoneIdx == HZI_Head ) { // Based on head health, calculate number of head chunks we're allowed to remove - if( !bPlayedDeath && !bIsHeadless && !bTearOff ) + if( !bPlayedDeath && !bIsHeadless && !bTearOff && !bIsBountyHuntObjective ) { HeadHealthPercentage = GetHeadHealthPercent(); if( HeadHealthPercentage > 0.5 ) @@ -4958,6 +5002,40 @@ simulated function ServerSpawnWeakPointVFX(array WeakPoints) } } +simulated function SetBountyHuntObjective() +{ + bIsBountyHuntObjective = true; + bAlwaysRelevant = true; + + if (WorldInfo.NetMode == NM_DedicatedServer) + { + bNetDirty = true; + bForceNetUpdate = true; + } +} + +simulated function SetBountyHuntOnLastTier() +{ + bIsBountyHuntOnLastTier = true; + + if (WorldInfo.NetMode == NM_DedicatedServer) + { + bNetDirty = true; + bForceNetUpdate = true; + } +} + +simulated function SetBountyHuntBlockLeaveMap(bool bCanLeave) +{ + bIsBountyHuntBlockLeaveMap = bCanLeave; + + if (WorldInfo.NetMode == NM_DedicatedServer) + { + bNetDirty = true; + bForceNetUpdate = true; + } +} + /********************************************************************************************* * @name Achievements ********************************************************************************************* */ @@ -5165,4 +5243,8 @@ DefaultProperties bCanBeKilledByShrinking=true ShrinkEffectModifier=1.0f + + bIsBountyHuntObjective=false + bIsBountyHuntOnLastTier=false + bIsBountyHuntBlockLeaveMap=false } diff --git a/KFGame/Classes/KFPawn_ZedHansBase.uc b/KFGame/Classes/KFPawn_ZedHansBase.uc index 954ba5c..27c9a29 100644 --- a/KFGame/Classes/KFPawn_ZedHansBase.uc +++ b/KFGame/Classes/KFPawn_ZedHansBase.uc @@ -245,11 +245,12 @@ function AdjustDamage(out int InDamage, out vector Momentum, Controller Instigat /** Reduce affliction/incap strength when healing */ simulated function AdjustAffliction( out float AfflictionPower ) { - super.AdjustAffliction( AfflictionPower ); if( bInHuntAndHealMode ) { AfflictionPower *= IncapPowerScaleWhenHealing; } + + super.AdjustAffliction( AfflictionPower ); } /** Updates shield health and shield health percent */ diff --git a/KFGame/Classes/KFPerk.uc b/KFGame/Classes/KFPerk.uc index 98aa71f..2557691 100644 --- a/KFGame/Classes/KFPerk.uc +++ b/KFGame/Classes/KFPerk.uc @@ -149,9 +149,10 @@ var const bool bInitialized; * Inventory ********************************************************************************************* */ +var byte StartingSecondaryWeaponClassIndex; + /** The weapon class string used to load the weapon class for this perk */ var class PrimaryWeaponDef; -var class SecondaryWeaponDef; /** default starting knife */ var class KnifeWeaponDef; @@ -173,6 +174,8 @@ var array BackupWeaponDamageTypeNames; var array > AutoBuyLoadOutPath; +var const array > SecondaryWeaponPaths; + /********************************************************************************************* * Player Skill Trakcing ********************************************************************************************* */ @@ -502,7 +505,12 @@ static simulated public function bool IsSyringe( KFWeapon KFW ) */ static function bool IsDual9mm( KFWeapon KFW ) { - return KFW != none && KFW.Class.Name == 'KFWeap_Pistol_Dual9mm'; + return KFW != none && (KFW.Class.Name == 'KFWeap_Pistol_Dual9mm' || KFW.Class.Name == 'KFWeap_HRG_93R_Dual'); +} + +static function bool IsHRG93R(KFWeapon KFW) +{ + return KFW != none && (KFW.Class.Name == 'KFWeap_HRG_93R' || KFW.Class.Name == 'KFWeap_HRG_93R_Dual'); } /** @@ -1125,12 +1133,6 @@ simulated function string GetPrimaryWeaponClassPath() return PrimaryWeaponDef.default.WeaponClassPath; } -/* Returns the secondary weapon's class path for this perk */ -simulated function string GetSecondaryWeaponClassPath() -{ - return SecondaryWeaponDef.default.WeaponClassPath; -} - /* Returns the knife's class path for this perk */ simulated function string GetKnifeWeaponClassPath() { @@ -1314,6 +1316,7 @@ native function bool CanRepairDoors(); function bool RepairArmor( Pawn HealTarget ); function bool IsToxicDmgActive() { return false; } static function class GetToxicDmgTypeClass(){ return default.ToxicDmgTypeClass; } +static function float GetHealRechargeMod(); static function ModifyToxicDmg( out int ToxicDamage ); simulated function float GetSirenScreamStrength(){ return 1.f; } simulated function bool IsHealingSurgeActive(){ return false; } @@ -1633,6 +1636,11 @@ static simulated function bool CanChoosePrimaryWeapon() return false; } +static simulated function bool CanChooseSecondaryWeapon() +{ + return true; +} + static simulated function bool CanChooseGrenade() { return false; @@ -1642,9 +1650,10 @@ 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 static function byte GetWeaponSelectedIndex(byte idx); +simulated function SetWeaponSelectedIndex(byte idx); +simulated function SetGrenadeSelectedIndex(byte idx); +simulated static function byte GetGrenadeSelectedIndexUsingSkills(byte idx, byte InSelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext); simulated function byte GetGrenadeSelectedIndex() { return 255;} simulated function InitializeGrenades(); @@ -1666,7 +1675,69 @@ static simulated function string GetGrenadeWeaponName(byte Idx) static simulated function string GetGrenadeWeaponImagePath(byte Idx) { return default.GrenadeWeaponDef.static.GetImagePath(); -} +} + +simulated function string GetSecondaryWeaponClassPath() +{ + return SecondaryWeaponPaths[StartingSecondaryWeaponClassIndex].default.WeaponClassPath; +} + +simulated function byte OnPrevSecondaryWeaponSelected() +{ + if (StartingSecondaryWeaponClassIndex == 0) + { + StartingSecondaryWeaponClassIndex = SecondaryWeaponPaths.Length - 1; + } + else + { + --StartingSecondaryWeaponClassIndex; + } + + SetSecondaryWeaponSelectedIndex(StartingSecondaryWeaponClassIndex); + + return StartingSecondaryWeaponClassIndex; +} + +simulated function byte OnNextSecondaryWeaponSelected() +{ + StartingSecondaryWeaponClassIndex = (StartingSecondaryWeaponClassIndex + 1) % SecondaryWeaponPaths.Length; + + SetSecondaryWeaponSelectedIndex(StartingSecondaryWeaponClassIndex); + + return StartingSecondaryWeaponClassIndex; +} + +static simulated function string GetSecondaryWeaponName(byte Idx) +{ + return Idx >= default.SecondaryWeaponPaths.Length ? default.SecondaryWeaponPaths[0].static.GetItemName() : default.SecondaryWeaponPaths[Idx].static.GetItemName(); +} + +static simulated function string GetSecondaryWeaponImagePath(byte Idx) +{ + return Idx >= default.SecondaryWeaponPaths.Length ? default.SecondaryWeaponPaths[0].static.GetImagePath() : default.SecondaryWeaponPaths[Idx].static.GetImagePath(); +} + +simulated static function byte GetSecondaryWeaponSelectedIndex(byte idx) +{ + if (idx >= default.SecondaryWeaponPaths.Length) + { + return 0; + } + + return idx; +} + +simulated function SetSecondaryWeaponSelectedIndex(byte idx) +{ + StartingSecondaryWeaponClassIndex = GetSecondaryWeaponSelectedIndex(idx); + + ServerUpdateCurrentSecondaryWeapon(StartingSecondaryWeaponClassIndex); +} + +reliable server function ServerUpdateCurrentSecondaryWeapon(byte Value) +{ + StartingSecondaryWeaponClassIndex = Value; +} DefaultProperties { @@ -1692,6 +1763,7 @@ DefaultProperties BackupWeaponDamageTypeNames(0)="KFDT_Ballistic_9mm"; BackupWeaponDamageTypeNames(1)="KFDT_Slashing_Knife"; + StartingSecondaryWeaponClassIndex=0; // network RemoteRole=ROLE_SimulatedProxy @@ -1700,10 +1772,12 @@ DefaultProperties bOnlyRelevantToOwner=TRUE // weapon content - SecondaryWeaponDef=class'KFWeapDef_9mm' KnifeWeaponDef=class'KFWeapDef_Knife_Commando' GrenadeWeaponDef=class'KFWeapDef_Grenade_Berserker' + SecondaryWeaponPaths(0)=class'KFWeapDef_9mm' + SecondaryWeaponPaths(1)=class'KFWeapDef_HRG_93R' + InitialGrenadeCount=2 MaxGrenadeCount=5 diff --git a/KFGame/Classes/KFPerk_Berserker.uc b/KFGame/Classes/KFPerk_Berserker.uc index d8bbe22..8c47fa9 100644 --- a/KFGame/Classes/KFPerk_Berserker.uc +++ b/KFGame/Classes/KFPerk_Berserker.uc @@ -1020,6 +1020,7 @@ DefaultProperties SmallRadiusSizeSQ=40000 PrimaryWeaponDef=class'KFWeapDef_Crovel' + KnifeWeaponDef=class'KFweapDef_Knife_Berserker' GrenadeWeaponDef=class'KFWeapDef_Grenade_Berserker' diff --git a/KFGame/Classes/KFPerk_Commando.uc b/KFGame/Classes/KFPerk_Commando.uc index 1b77c41..d7ba97d 100644 --- a/KFGame/Classes/KFPerk_Commando.uc +++ b/KFGame/Classes/KFPerk_Commando.uc @@ -97,7 +97,8 @@ simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCau // have the perks so tied into using that, it's easier to just specifically fix commando here. if( KFW != none && !DamageCauser.IsA('KFProj_Grenade')) { - if( IsBackupActive() && (IsBackupWeapon( KFW ) || IsDual9mm( KFW )) ) + if( IsBackupActive() + && (IsBackupWeapon( KFW ) || IsDual9mm( KFW ) || IsHRG93R( KFW ) ) ) { `QALog( "Backup Damage" @ KFW @ GetPercentage( InDamage, InDamage * GetSkillValue( PerkSkills[ECommandoBackup] )), bLogPerk ); TempDamage += InDamage * GetSkillValue( PerkSkills[ECommandoBackup] ); @@ -692,6 +693,7 @@ DefaultProperties PerkBuildStatID=STATID_Cmdo_Build PrimaryWeaponDef=class'KFWeapDef_AR15' + KnifeWeaponDef=class'KFweapDef_Knife_Commando' GrenadeWeaponDef=class'KFWeapDef_Grenade_Commando' diff --git a/KFGame/Classes/KFPerk_Demolitionist.uc b/KFGame/Classes/KFPerk_Demolitionist.uc index a118197..4d81832 100644 --- a/KFGame/Classes/KFPerk_Demolitionist.uc +++ b/KFGame/Classes/KFPerk_Demolitionist.uc @@ -1042,6 +1042,7 @@ DefaultProperties InteractIcon=Texture2D'UI_World_TEX.Demolitionist_Supplier_HUD' PrimaryWeaponDef=class'KFWeapDef_HX25' + KnifeWeaponDef=class'KFWeapDef_Knife_Demo' GrenadeWeaponDef=class'KFWeapDef_Grenade_Demo' diff --git a/KFGame/Classes/KFPerk_FieldMedic.uc b/KFGame/Classes/KFPerk_FieldMedic.uc index 988648f..12197b8 100644 --- a/KFGame/Classes/KFPerk_FieldMedic.uc +++ b/KFGame/Classes/KFPerk_FieldMedic.uc @@ -62,11 +62,6 @@ enum EMedicPerkSkills EMedicZedative }; -/********************************************************************************************* -* @name Perk init and spawning -******************************************************************************************** */ - - /********************************************************************************************* * @name Passive skills functions ********************************************************************************************* */ @@ -329,6 +324,11 @@ simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCau InDamage = Round(TempDamage); } +static function float GetHealRechargeMod() +{ + return GetSkillValue( default.PerkSkills[EMedicAcidicCompound] ); +} + /** Takes the weapons primary damage and calculates the poisoning over time value */ /** * @brief Takes the weapons primary damage and calculates the bleeding over time value @@ -339,7 +339,7 @@ static function ModifyToxicDmg( out int ToxicDamage ) { local float TempDamage; - TempDamage = float(ToxicDamage) * GetSkillValue( default.PerkSkills[EMedicAcidicCompound] ); + TempDamage = float(ToxicDamage) * GetHealRechargeMod(); ToxicDamage = FCeil( TempDamage ); } @@ -644,13 +644,14 @@ DefaultProperties PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Medic' PrimaryWeaponDef=class'KFWeapDef_MedicPistol' + KnifeWeaponDef=class'KFWeapDef_Knife_Medic' GrenadeWeaponDef=class'KFWeapDef_Grenade_Medic' ProgressStatID=STATID_Medic_Progress PerkBuildStatID=STATID_Medic_Build - SelfHealingSurgePct=0.03f //0.1f + SelfHealingSurgePct=0.06f //0.1f MaxSurvivalistResistance=0.30f //0.70f //0.5f //0.8 CombatantSpeedModifier=0.1f @@ -738,7 +739,7 @@ DefaultProperties PerkSkills(EMedicHealingSpeedBoost)=(Name="HealingSpeedBoost",IconPath="ui_perktalent_tex.Medic.UI_Talents_Medic_AdrenalineShot", Increment=0.f,Rank=0,StartingValue=10.0f,MaxValue=10.0f) //3.0 PerkSkills(EMedicCombatant)=(Name="Combatant",IconPath="ui_perktalent_tex.Medic.UI_Talents_Medic_CombatantDoctor", Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f) PerkSkills(EMedicHealingDamageBoost)=(Name="HealingDamageBoost",IconPath="ui_perktalent_tex.Medic.UI_Talents_Medic_FocusInjection", Increment=0.f,Rank=0,StartingValue=5.0f,MaxValue=5.0f) //3.0 - PerkSkills(EMedicAcidicCompound)=(Name="AcidicCompound",IconPath="ui_perktalent_tex.Medic.UI_Talents_Medic_AcidicRounds", Increment=0.f,Rank=0,StartingValue=0.65f,MaxValue=0.65f) //0.75 //0.5 + PerkSkills(EMedicAcidicCompound)=(Name="AcidicCompound",IconPath="ui_perktalent_tex.Medic.UI_Talents_Medic_AcidicRounds", Increment=0.f,Rank=0,StartingValue=2f,MaxValue=2f) PerkSkills(EMedicHealingShield)=(Name="HealingShield",IconPath="ui_perktalent_tex.Medic.UI_Talents_Medic_CoagulantBooster", Increment=0.f,Rank=0,StartingValue=10.f,MaxValue=10.f) //25.0 PerkSkills(EMedicEnforcer)=(Name="Enforcer",IconPath="ui_perktalent_tex.Medic.UI_Talents_Medic_BattleSurgeon", Increment=0.f,Rank=0,StartingValue=0.2f,MaxValue=0.2f) PerkSkills(EMedicAirborneAgent)=(Name="AirborneAgent",IconPath="ui_perktalent_tex.Medic.UI_Talents_Medic_AirborneAgent", Increment=0.f,Rank=0,StartingValue=0.2f,MaxValue=0.2f) diff --git a/KFGame/Classes/KFPerk_Firebug.uc b/KFGame/Classes/KFPerk_Firebug.uc index 7cda5ca..806f08f 100644 --- a/KFGame/Classes/KFPerk_Firebug.uc +++ b/KFGame/Classes/KFPerk_Firebug.uc @@ -634,6 +634,7 @@ DefaultProperties PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Firebug' PrimaryWeaponDef=class'KFWeapDef_CaulkBurn' + KnifeWeaponDef=class'KFWeapDef_Knife_Firebug' GrenadeWeaponDef=class'KFWeapDef_Grenade_Firebug' diff --git a/KFGame/Classes/KFPerk_Gunslinger.uc b/KFGame/Classes/KFPerk_Gunslinger.uc index e35de57..1a8effe 100644 --- a/KFGame/Classes/KFPerk_Gunslinger.uc +++ b/KFGame/Classes/KFPerk_Gunslinger.uc @@ -60,9 +60,6 @@ var private const int MaxHeadShotComboCount; var private const float HeadShotCountdownIntervall; var private const float SteadySkillDamageModifier; -/********************************************************************************************* -* @name Perk init and spawning -******************************************************************************************** */ /** * @brief Weapons and perk skills can affect the jog/sprint speed * @@ -825,6 +822,7 @@ DefaultProperties PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Gunslinger' PrimaryWeaponDef=class'KFWeapDef_Remington1858Dual' + KnifeWeaponDef=class'KFWeapDef_Knife_Gunslinger' GrenadeWeaponDef=class'KFWeapDef_Grenade_Gunslinger' @@ -870,11 +868,14 @@ DefaultProperties AdditionalOnPerkWeaponNames(0)="KFWeap_Pistol_9mm" AdditionalOnPerkWeaponNames(1)="KFWeap_Pistol_Dual9mm" AdditionalOnPerkWeaponNames(2)="KFWeap_GrenadeLauncher_HX25" + AdditionalOnPerkWeaponNames(3)="KFWeap_HRG_93R" + AdditionalOnPerkWeaponNames(4)="KFWeap_HRG_93R_Dual" AdditionalOnPerkDTNames(0)="KFDT_Ballistic_9mm" AdditionalOnPerkDTNames(1)="KFDT_Ballistic_Pistol_Medic" AdditionalOnPerkDTNames(2)="KFDT_Ballistic_Winchester" AdditionalOnPerkDTNames(3)="KFDT_Ballistic_HX25Impact" AdditionalOnPerkDTNames(4)="KFDT_Ballistic_HX25SubmunitionImpact" + AdditionalOnPerkDTNames(5)="KFDT_Ballistic_HRG_93R" PerkSkills(EGunslingerShootnMove)=(Name="ShootnMove",IconPath="UI_PerkTalent_TEX.Gunslinger.UI_Talents_Gunslinger_Steady",Increment=0.f,Rank=0,StartingValue=2.f,MaxValue=2.f) PerkSkills(EGunslingerQuickSwitch)=(Name="QuickSwitch",IconPath="UI_PerkTalent_TEX.Gunslinger.UI_Talents_Gunslinger_QuickSwitch",Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f) diff --git a/KFGame/Classes/KFPerk_Sharpshooter.uc b/KFGame/Classes/KFPerk_Sharpshooter.uc index 753f7df..408bb9a 100644 --- a/KFGame/Classes/KFPerk_Sharpshooter.uc +++ b/KFGame/Classes/KFPerk_Sharpshooter.uc @@ -721,6 +721,7 @@ DefaultProperties PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Sharpshooter' PrimaryWeaponDef=class'KFWeapDef_Winchester1894' + KnifeWeaponDef=class'KFWeapDef_Knife_Sharpshooter' GrenadeWeaponDef=class'KFWeapDef_Grenade_Sharpshooter' @@ -741,10 +742,12 @@ DefaultProperties AdditionalOnPerkWeaponNames(1)="KFWeap_Pistol_Dual9mm" AdditionalOnPerkWeaponNames(2)="KFWeap_Revolver_Rem1858" AdditionalOnPerkWeaponNames(3)="KFWeap_Revolver_SW500" + AdditionalOnPerkWeaponNames(4)="KFWeap_HRG_93R" + AdditionalOnPerkWeaponNames(5)="KFWeap_HRG_93R_Dual" AdditionalOnPerkDTNames(0)="KFDT_Ballistic_9mm" AdditionalOnPerkDTNames(1)="KFDT_Ballistic_SW500" AdditionalOnPerkDTNames(2)="KFDT_Ballistic_Rem1858" - + AdditionalOnPerkDTNames(3)="KFDT_Ballistic_HRG_93R" HeadshotDamage=(Name="Headshot Damage",Increment=0.01f,Rank=0,StartingValue=0.0f,MaxValue=0.25f) Recoil=(Name="Recoil",Increment=0.01f,Rank=0,StartingValue=0.0f,MaxValue=0.25f) diff --git a/KFGame/Classes/KFPerk_Support.uc b/KFGame/Classes/KFPerk_Support.uc index 8803456..86ebd4e 100644 --- a/KFGame/Classes/KFPerk_Support.uc +++ b/KFGame/Classes/KFPerk_Support.uc @@ -40,6 +40,11 @@ var private const array AdditionalOnPerkDTNames; var private const int DoorRepairXP[4]; // Door repair XP per-difficulty + +// Resistance to damage taken when Fortitude skill is active +var private const float FortitudeDamageResistance; +var private const float APDamageModifier; + enum ESupportPerkSkills { ESupportHighCapMags, @@ -52,6 +57,7 @@ enum ESupportPerkSkills ESupportConcussionRounds, ESupportPerforate, ESupportBarrage, + }; /********************************************************************************************* @@ -177,12 +183,43 @@ simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCau TempDamage += InDamage * GetSkillValue( PerkSkills[ESupportSalvo] ); `QALog( GetFuncName() @ "+ Salvo Damage =" @ TempDamage, bLogPerk ); } + + if ( IsAPShotActive() ) + { + TempDamage += InDamage * APDamageModifier; + `QALog( GetFuncName() @ "+ Armor Piercing Damage =" @ TempDamage, bLogPerk ); + } } `QALog( "Total Damage Given" @ Damagetype @ KFW @ GetPercentage( InDamage, Round(TempDamage) ), bLogPerk ); InDamage = Round( TempDamage ); } +/** + * @brief Modifies the damage taken + * + * @param InDamage damage + * @param DamageType the damage type used (optional) + */ +function ModifyDamageTaken( out int InDamage, optional class DamageType, optional Controller InstigatedBy ) +{ + local float TempDamage; + + if( InDamage <= 0 ) + { + return; + } + + TempDamage = InDamage; + + if( IsFortitudeActive() ) + { + TempDamage -= TempDamage * FortitudeDamageResistance; + `QALog( "Fortitude Damage Resistance =" @ FortitudeDamageResistance, bLogPerk ); + } +} + + /** Welding Proficiency - faster welding/unwelding */ /** * @brief Modifies the welding speed @@ -563,14 +600,27 @@ simulated function float GetZedTimeModifier( KFWeapon W ) // Blast Brawlers use a different state for shooting (combining melee + firing). Needs a special case for this // FAMAS uses alt fire as common firing. Another special case added // HRG Ballistic Bouncer uses a special state of charging when shooting - if( IsWeaponOnPerk( W,, self.class ) && CouldBarrageActive() && - (ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || - (StateName == 'MeleeChainAttacking' && IsBlastBrawlers(W)) || - (IsFAMAS(W) && StateName == 'FiringSecondaryState')) || - (IsHRGBallisticBouncer(W) && StateName == 'MineReconstructorCharge')) + if (IsWeaponOnPerk( W,, self.class ) ) { - return BarrageFiringRate; - } + if( CouldBarrageActive() && + (ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || + (StateName == 'MeleeChainAttacking' && IsBlastBrawlers(W)) || + (IsFAMAS(W) && StateName == 'FiringSecondaryState')) || + (IsHRGBallisticBouncer(W) && StateName == 'MineReconstructorCharge')) + { + return BarrageFiringRate; + } + + if( IsPerforateActive() && ( IsWeaponOnPerk( W,, self.class ) ) ) + { + StateName = W.GetStateName(); + + if( StateName == 'Reloading' ) + { + return 1.f; + } + } + } return 0.f; } @@ -812,6 +862,7 @@ DefaultProperties InteractIcon=Texture2D'UI_World_TEX.Support_Supplier_HUD' PrimaryWeaponDef=class'KFWeapDef_MB500' + KnifeWeaponDef=class'KFWeapDef_Knife_Support' GrenadeWeaponDef=class'KFWeapDef_Grenade_Support' @@ -875,4 +926,7 @@ DefaultProperties AdditionalOnPerkDTNames(0)="KFDT_Ballistic_Shotgun_Medic" AdditionalOnPerkDTNames(1)="KFDT_Ballistic_DragonsBreath" AdditionalOnPerkDTNames(2)="KFDT_Ballistic_NailShotgun" + + FortitudeDamageResistance=0.1f + APDamageModifier=0.05f } diff --git a/KFGame/Classes/KFPerk_Survivalist.uc b/KFGame/Classes/KFPerk_Survivalist.uc index 60b1f10..82adb2c 100644 --- a/KFGame/Classes/KFPerk_Survivalist.uc +++ b/KFGame/Classes/KFPerk_Survivalist.uc @@ -893,27 +893,29 @@ simulated function string GetGrenadeClassPath() simulated function byte GetGrenadeSelectedIndex() { return CurrentGrenadeClassIndex; } -simulated function byte SetWeaponSelectedIndex(byte idx) +simulated static function byte GetWeaponSelectedIndex(byte idx) { if (idx >= default.PrimaryWeaponPaths.Length && idx < 255) { - StartingWeaponClassIndex = 0; + return 0; } - else if (idx == 255) + + if (idx == 255) { - StartingWeaponClassIndex = `WEAP_IDX_NONE; - } - else - { - StartingWeaponClassIndex = idx; + return `WEAP_IDX_NONE; } - ServerUpdateCurrentWeapon(StartingWeaponClassIndex); - - return StartingWeaponClassIndex; + return idx; } -simulated function byte SetGrenadeSelectedIndex(byte idx) +simulated function SetWeaponSelectedIndex(byte idx) +{ + StartingWeaponClassIndex = static.GetWeaponSelectedIndex(idx); + + ServerUpdateCurrentWeapon(StartingWeaponClassIndex); +} + +simulated function SetGrenadeSelectedIndex(byte idx) { local KFGameReplicationInfo KFGRI; @@ -953,70 +955,66 @@ simulated function byte SetGrenadeSelectedIndex(byte idx) { UpdateCurrentGrenade(); } - - return StartingGrenadeClassIndex; } -simulated function byte SetGrenadeSelectedIndexUsingSkills(byte idx, byte InSelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext) +simulated static function byte GetGrenadeSelectedIndexUsingSkills(byte idx, byte InSelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext) { - local KFGameReplicationInfo KFGRI; local bool AmmoVestActive, BigPocketsActive; - - KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + local byte SelectedGrenadeIndex; AmmoVestActive = false; BigPocketsActive = false; if (idx >= default.GrenadeWeaponPaths.Length && idx < 255) { - StartingGrenadeClassIndex = 0; + SelectedGrenadeIndex = 0; } else if (idx == 255) { - StartingGrenadeClassIndex = `WEAP_IDX_NONE; + SelectedGrenadeIndex = `WEAP_IDX_NONE; } else { - StartingGrenadeClassIndex = idx; + SelectedGrenadeIndex = idx; - if (StartingGrenadeClassIndex == MedicGrenadeIndex || StartingGrenadeClassIndex == FirebugGrenadeIndex) + if (SelectedGrenadeIndex == default.MedicGrenadeIndex || SelectedGrenadeIndex == default.FirebugGrenadeIndex) { AmmoVestActive = InSelectedSkills[2] == 1; BigPocketsActive = InSelectedSkills[2] == 2; if (IsChoosingPrev) { - if (StartingGrenadeClassIndex == FirebugGrenadeIndex) + if (SelectedGrenadeIndex == default.FirebugGrenadeIndex) { if (BigPocketsActive == false) { - --StartingGrenadeClassIndex; + --SelectedGrenadeIndex; } } - if (StartingGrenadeClassIndex == MedicGrenadeIndex) + if (SelectedGrenadeIndex == default.MedicGrenadeIndex) { if (AmmoVestActive == false) { - --StartingGrenadeClassIndex; + --SelectedGrenadeIndex; } } } else if (IsChoosingNext) { - if (StartingGrenadeClassIndex == MedicGrenadeIndex) + if (SelectedGrenadeIndex == default.MedicGrenadeIndex) { if (AmmoVestActive == false) { - ++StartingGrenadeClassIndex; + ++SelectedGrenadeIndex; } } - if (StartingGrenadeClassIndex == FirebugGrenadeIndex) + if (SelectedGrenadeIndex == default.FirebugGrenadeIndex) { if (BigPocketsActive == false) { - ++StartingGrenadeClassIndex; + ++SelectedGrenadeIndex; } } } @@ -1024,32 +1022,26 @@ simulated function byte SetGrenadeSelectedIndexUsingSkills(byte idx, byte InSele { if (AmmoVestActive) { - StartingGrenadeClassIndex = MedicGrenadeIndex; + SelectedGrenadeIndex = default.MedicGrenadeIndex; } else if (BigPocketsActive) { - StartingGrenadeClassIndex = FirebugGrenadeIndex; + SelectedGrenadeIndex = default.FirebugGrenadeIndex; } else { - StartingGrenadeClassIndex = 0; + SelectedGrenadeIndex = 0; } } - if (StartingGrenadeClassIndex > GrenadeWeaponPaths.Length - 1) + if (SelectedGrenadeIndex > default.GrenadeWeaponPaths.Length - 1) { - StartingGrenadeClassIndex = `WEAP_IDX_NONE; + SelectedGrenadeIndex = `WEAP_IDX_NONE; } } } - // If we are in no gameplay time insta change - if (!KFGRI.bWaveIsActive) - { - UpdateCurrentGrenade(); - } - - return StartingGrenadeClassIndex; + return SelectedGrenadeIndex; } simulated function InitializeGrenades() diff --git a/KFGame/Classes/KFPerk_Swat.uc b/KFGame/Classes/KFPerk_Swat.uc index 6c4b56a..978bb47 100644 --- a/KFGame/Classes/KFPerk_Swat.uc +++ b/KFGame/Classes/KFPerk_Swat.uc @@ -22,7 +22,6 @@ var private const PerkSkill WeaponSwitchSpeed; var private const float RapidAssaultFiringRate; // Faster firing rate in % NOTE:This is needed for combinations with the Skill: RapidAssault (Stumble Power and Rate) var private const float SnarePower; var private const float TacticalMovementBobDamp; -var private const class BackupSecondaryWeaponDef; var private const float TacticalMovementModifier; // QoL: Tactical movement - Added modifier to move and sprint speed. /** Percentage of how much armor should be damaged when the heavy armor skill is active */ @@ -60,6 +59,8 @@ enum ESWATPerkSkills ESWAT_RapidAssault }; +var private const array > SecondaryDualWeaponPaths; + replication { if (bNetDirty) @@ -109,12 +110,6 @@ simulated event float GetZedTimeSpeedScale() return IsSWATEnforcerActive() ? SWATEnforcerZedTimeSpeedScale : 1.f; } -/* Returns the secondary weapon's class path for this perk */ -simulated function string GetSecondaryWeaponClassPath() -{ - return IsBackupActive() ? BackupSecondaryWeaponDef.default.WeaponClassPath : SecondaryWeaponDef.default.WeaponClassPath; -} - /********************************************************************************************* * @name Passives ********************************************************************************************* */ @@ -279,7 +274,8 @@ simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCau if( KFW != none ) { // KFDT_Bludgeon_Doshinegun_Shot is a special case of Bludgeon damage that doesn't apply this mod. - if( IsBackupActive() && (IsBackupWeapon( KFW ) || IsDual9mm( KFW ) || ShouldAffectBackupToDamage(KFW, DamageType))) + if( IsBackupActive() + && (IsBackupWeapon( KFW ) || IsDual9mm( KFW ) || IsHRG93R( KFW ) || ShouldAffectBackupToDamage(KFW, DamageType))) { `QALog( "Backup Damage" @ KFW @ GetPercentage( InDamage, InDamage * GetSkillValue(PerkSkills[ESWAT_Backup])), bLogPerk ); TempDamage += InDamage * GetSkillValue( PerkSkills[ESWAT_Backup] ); @@ -700,12 +696,27 @@ simulated static function GetPassiveStrings( out array PassiveValues, ou Increments[4] = ""; } +simulated function string GetSecondaryWeaponClassPath() +{ + if (IsBackupActive()) + { + return SecondaryDualWeaponPaths[StartingSecondaryWeaponClassIndex].default.WeaponClassPath; + } + else + { + return SecondaryWeaponPaths[StartingSecondaryWeaponClassIndex].default.WeaponClassPath; + } +} + DefaultProperties { PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_SWAT' PrimaryWeaponDef=class'KFWeapDef_MP7' - BackupSecondaryWeaponDef=class'KFWeapDef_9mmDual' + + SecondaryDualWeaponPaths(0)=class'KFWeapDef_9mmDual' + SecondaryDualWeaponPaths(1)=class'KFWeapDef_HRG_93R_Dual' + KnifeWeaponDef=class'KFweapDef_Knife_SWAT' GrenadeWeaponDef=class'KFWeapDef_Grenade_SWAT' diff --git a/KFGame/Classes/KFPickupFactory_Item.uc b/KFGame/Classes/KFPickupFactory_Item.uc index 9a390c0..48a8a09 100644 --- a/KFGame/Classes/KFPickupFactory_Item.uc +++ b/KFGame/Classes/KFPickupFactory_Item.uc @@ -316,12 +316,40 @@ function GiveWeapon( Pawn P ) local KFInventoryManager KFIM; local Inventory Inv; + // Give us the weapon if we do not have it + InventoryClass = ItemPickups[ PickupIndex ].ItemClass; + // Check if we have the weapon KFIM = KFInventoryManager( P.InvManager ); + + // For HRG93R and 9mm pistols, if one of the other type is picked just give the one owned + if (KFIM.Is9mmInInventory()) + { + if (InventoryClass.name == 'KFWeap_HRG_93R') + { + InventoryClass = class(DynamicLoadObject(class'KfWeapDef_9mm'.default.WeaponClassPath, class'Class')); + } + else if (InventoryClass.name == 'KFWeap_HRG_93R_Dual') + { + InventoryClass = class(DynamicLoadObject(class'KfWeapDef_9mmDual'.default.WeaponClassPath, class'Class')); + } + } + else + { + if(InventoryClass.name == 'KFWeap_Pistol_9mm') + { + InventoryClass = class(DynamicLoadObject(class'KFWeapDef_HRG_93R'.default.WeaponClassPath, class'Class')); + } + else if (InventoryClass.name == 'KFWeap_Pistol_Dual9mm') + { + InventoryClass = class(DynamicLoadObject(class'KFWeapDef_HRG_93R_Dual'.default.WeaponClassPath, class'Class')); + } + } + foreach KFIM.InventoryActors( class'KFWeapon', KFW ) { - KFWeaponClass = class( ItemPickups[PickupIndex].ItemClass ); - if ( KFW.class == ItemPickups[ PickupIndex ].ItemClass ) + KFWeaponClass = class( InventoryClass ); + if ( KFW.class == InventoryClass ) { // if this isn't a dual-wield class, then we can't carry another if( KFW.DualClass == none ) @@ -338,12 +366,11 @@ function GiveWeapon( Pawn P ) } } - // Give us the weapon if we do not have it - InventoryClass = ItemPickups[ PickupIndex ].ItemClass; Inv = KFIM.CreateInventory(InventoryClass, true); if( Inv != none ) { + KFW = KFWeapon(Inv); if( KFW != none ) { diff --git a/KFGame/Classes/KFPlayerController.uc b/KFGame/Classes/KFPlayerController.uc index c2c5be6..fbea2b1 100644 --- a/KFGame/Classes/KFPlayerController.uc +++ b/KFGame/Classes/KFPlayerController.uc @@ -115,6 +115,8 @@ var private const bool bPerkStatsLoaded; var public byte SavedPerkIndex; /** Index of the weapon chosen for the survival perk */ var public byte SurvivalPerkWeapIndex; +/** Index of the secondary weapon chosen for the survival perk */ +var public byte SurvivalPerkSecondaryWeapIndex; /** Index of the grenade chosen for the survival perk */ var public byte SurvivalPerkGrenIndex; @@ -1673,6 +1675,7 @@ function OnReadProfileSettingsComplete(byte LocalUserNum,bool bWasSuccessful) bDisableAutoUpgrade = Profile.GetProfileBool(KFID_DisableAutoUpgrade); bHideRemotePlayerHeadshotEffects = Profile.GetProfileBool(KFID_HideRemoteHeadshotEffects); SurvivalPerkWeapIndex = byte(Profile.GetProfileInt(KFID_SurvivalStartingWeapIdx)); + SurvivalPerkSecondaryWeapIndex = byte(Profile.GetProfileInt(KFID_SurvivalStartingSecondaryWeapIdx)); SurvivalPerkGrenIndex = byte(Profile.GetProfileInt(KFID_SurvivalStartingGrenIdx)); KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo); @@ -2862,6 +2865,12 @@ public function bool CanUseContaminationMode() && KFGameReplicationInfo(WorldInfo.GRI).IsContaminationMode(); } +public function bool CanUseBountyHunt() +{ + return KFGameReplicationInfo(WorldInfo.GRI) != none + && KFGameReplicationInfo(WorldInfo.GRI).IsBountyHunt(); +} + /********************************************************************************************* * @name Skill Tracking ********************************************************************************************* */ @@ -7657,6 +7666,12 @@ function AddAfflictionCaused(EAfflictionType Type) } native reliable client private function ClientAddAfflictionCaused(EAfflictionType Type); +function AddCollectibleFound(int Limit) +{ + ClientAddCollectibleFound(Limit); +} +native reliable client private function ClientAddCollectibleFound(int Limit); + function AddZedAssist(class MonsterClass) { ClientAddZedAssist(MonsterClass); @@ -12038,6 +12053,8 @@ reliable client function ForceMonsterHeadExplode(KFPawn_Monster Victim) simulated function InitPerkLoadout() { + CurrentPerk.SetSecondaryWeaponSelectedIndex(SurvivalPerkSecondaryWeapIndex); + if (CurrentPerk.IsA('KFPerk_Survivalist')) { CurrentPerk.SetWeaponSelectedIndex(SurvivalPerkWeapIndex); diff --git a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc index 2269298..b08c393 100644 --- a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc +++ b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc @@ -117,6 +117,9 @@ var bool ContaminationModePlayerIsInside; var int ContaminationModeLastTimePlayerOutPulsate; var bool ContaminationModeLastPulsate; +// Bounty Hunt weekly +var int BountyHuntCurrentExtraZeds; + cpptext { virtual UBOOL TestZedTimeVisibility(APawn* P, UNetConnection* Connection, UBOOL bLocalPlayerTest) override; @@ -272,6 +275,27 @@ reliable client function HideContaminationMode() } } +reliable client function DisplayBountyHuntObjective(int Bounties) +{ + if (MyGFxHUD != none) + { + MyGFxHUD.DisplayBountyHuntObjective(Bounties); + } +} + +reliable client function DisplayBountyHuntStatus(int CurrentAliveBounties, int MaxBounties, int DeadBounties, int Dosh, int DoshNoAssist) +{ + if (MyGFxHUD != none) + { + //`Log("CurrentAliveBounties :" $CurrentAliveBounties); + //`Log("(MaxBounties - DeadBounties) :" $(MaxBounties - DeadBounties)); + + BountyHuntCurrentExtraZeds = (MaxBounties - DeadBounties) - CurrentAliveBounties; + + MyGFxHUD.DisplayBountyHuntStatus(MaxBounties - DeadBounties, Dosh, DoshNoAssist); + } +} + reliable client function ResetSyringe() { local KFInventoryManager InventoryManager; @@ -536,15 +560,21 @@ function AdjustDamage(out int InDamage, Controller InstigatedBy, class 0) + { + // Don't count current alive Bounty Zeds + WaveProgress = float(KFGRI.AIRemaining - KFGI.WeeklyCurrentExtraNumberOfZeds()) / float(KFGRI.WaveTotalAICount); + } + else + { + WaveProgress = 0.f; + } + + // Find first node of data for the Zed type we checking,. + for (BountyHuntZedIt = 0 ; BountyHuntZedIt < KFGI.OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression.Length; ++BountyHuntZedIt) + { + if (KFGI.OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].ZedType == KFPM.class) + { + break; + } + } + + // Update to the current level + for (BountyHuntIt = 0 ; BountyHuntIt < KFGI.OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression.Length; ++BountyHuntIt) + { + if (WaveProgress >= KFGI.OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression[BountyHuntIt].RemainingZedRatio) + { + break; + } + } + + if (BountyHuntIt == KFGI.OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression.Length) + { + BountyHuntIt = KFGI.OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression.Length - 1; + } + + //`Log("BOUNTY HUNT : Initial InDamage = " $InDamage); + + //`Log("WaveProgress : " $WaveProgress); + + InDamage += InDamage * KFGI.OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression[BountyHuntIt].DamageBuffRatio; + + //`Log("BOUNTY HUNT : Output Damage: " $KFGI.OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression[BountyHuntIt].DamageBuffRatio); + //`Log("BOUNTY HUNT : Final InDamage = " $InDamage); + } + } } function AdjustVIPDamage(out int InDamage, Controller InstigatedBy) diff --git a/KFGame/Classes/KFProfileSettings.uc b/KFGame/Classes/KFProfileSettings.uc index 500e513..989775f 100644 --- a/KFGame/Classes/KFProfileSettings.uc +++ b/KFGame/Classes/KFProfileSettings.uc @@ -20,6 +20,7 @@ struct native WeaponSkinPairs var transient array Characters; var transient array FavoriteWeapons; var transient array ActiveSkins; +var transient array KilledHansVolterInMaps; var transient bool Dirty; event FavoriteWeapon(name WeaponName) @@ -53,6 +54,29 @@ event UnFavoriteWeapon(name WeaponName) } } +function bool AddHansVolterKilledInMap(string MapName) +{ + if (KilledHansVolterInMaps.Find(MapName) == INDEX_NONE) + { + KilledHansVolterInMaps.AddItem(MapName); + + while (KilledHansVolterInMaps.Length > 5) + { + // There's a crash on the save system because the string this generates is huge + // We don't need to store more than 5 + // We are going to cap it then, we remove from the beginning + + KilledHansVolterInMaps.Remove(0, 1); + } + + Dirty = true; + + return true; + } + + return false; +} + event Save( byte ControllerId ) { if(Dirty) @@ -234,6 +258,7 @@ defaultproperties ProfileMappings.Add((Id=KFID_FriendlyHudScale, Name="Friendly HUD Scale", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_FavoriteWeapons, Name="Favorite Weapons", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_GearLoadouts, Name="Gear Loadouts", MappingType=PVMT_RawValue)) + ProfileMappings.Add((Id=KFID_KilledHansVolterInMaps, Name="Killed Hans Volter In Maps", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_SetGamma, Name="Set Gamma", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_RequiresPushToTalk, Name="RequiresPushToTalk", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_InvertController, Name="controllerInvertedValue", MappingType=PVMT_RawValue)) @@ -258,6 +283,7 @@ defaultproperties ProfileMappings.Add((Id=KFID_SavedLengthIndex, Name="SavedLengthIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_SavedPrivacyIndex, Name="SavedPrivacyIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_SavedAllowSeasonalSkinsIndex, Name="SavedAllowSeasonalSkinsIndex", MappingType=PVMT_RawValue)) + ProfileMappings.Add((Id=KFID_SavedWeeklySelectorIndex, Name="SavedWeeklySelectorIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_SavedServerTypeIndex, Name="SavedServerTypeIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_SavedInProgressIndex, Name="SavedInProgressIndex", MappingType=PVMT_RawValue)) ProfileMappings.Add((Id=KFID_ControllerSoundEnabled, Name="Controller Sound Enabled", MappingType=PVMT_RawValue)) @@ -320,6 +346,7 @@ defaultproperties DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_FriendlyHudScale,Data=(Type=SDT_Float,Value1=0x3f800000)))) // default of 1.0f DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_FavoriteWeapons,Data=(Type=SDT_String,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_GearLoadouts,Data=(Type=SDT_String,Value1=0)))) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_KilledHansVolterInMaps,Data=(Type=SDT_String,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SetGamma,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_RequiresPushToTalk,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_InvertController,Data=(Type=SDT_Int32,Value1=0)))) @@ -344,6 +371,7 @@ defaultproperties DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedLengthIndex,Data=(Type=SDT_Int32,Value1=0)))) //default to any DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedPrivacyIndex,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedAllowSeasonalSkinsIndex,Data=(Type=SDT_Int32,Value1=0)))) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedWeeklySelectorIndex,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedServerTypeIndex,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SavedInProgressIndex,Data=(Type=SDT_Int32,Value1=0)))) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_ControllerSoundEnabled,Data=(Type=SDT_Int32,Value1=0)))) @@ -411,4 +439,7 @@ defaultproperties ProfileMappings.Add((Id=KFID_ViewAccelerationEnabled, Name="View_Acceleration_Enabled", MappingType=PVMT_RawValue)) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_ViewAccelerationEnabled,Data=(Type=SDT_Int32,Value1=1)))) + + ProfileMappings.Add((Id=KFID_SurvivalStartingSecondaryWeapIdx, Name="Survival Starting Secondary Weapon Index", MappingType=PVMT_RawValue)) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SurvivalStartingSecondaryWeapIdx,Data=(Type=SDT_Int32,Value1=0)))) } diff --git a/KFGame/Classes/KFSM_GrappleCombined.uc b/KFGame/Classes/KFSM_GrappleCombined.uc index a31bcd8..7e22df0 100644 --- a/KFGame/Classes/KFSM_GrappleCombined.uc +++ b/KFGame/Classes/KFSM_GrappleCombined.uc @@ -392,7 +392,7 @@ function NotifyOwnerTakeHit(class DamageType, vector HitLoc, vecto KFPOwner.EndSpecialMove(); // force stumble when damaged from a grapple - if ( KFPOwner.CanDoSpecialMove(SM_Stumble) && DamageType.default.StumblePower > 0 ) + if ( KFPOwner.CanDoSpecialMove(SM_Stumble) && DamageType != none && DamageType.default.StumblePower > 0 ) { KFPOwner.DoSpecialMove(SM_Stumble,,, class'KFSM_Stumble'.static.PackBodyHitSMFlags(KFPOwner, HitDir)); } diff --git a/KFGame/Classes/KFSeasonalEventStats.uc b/KFGame/Classes/KFSeasonalEventStats.uc index 96f6b8c..3c97b3e 100644 --- a/KFGame/Classes/KFSeasonalEventStats.uc +++ b/KFGame/Classes/KFSeasonalEventStats.uc @@ -80,4 +80,5 @@ simulated event OnTryCompleteObjective(int ObjectiveIndex, int EventIndex); simulated function OnHitTaken(); simulated function OnHitGiven(class DT); simulated function OnWeaponPurchased(class WeaponDef, int Price); -simulated function OnAfflictionCaused(EAfflictionType Type); \ No newline at end of file +simulated function OnAfflictionCaused(EAfflictionType Type); +simulated function OnCollectibleFound(int Limit); \ No newline at end of file diff --git a/KFGame/Classes/KFSpawnVolume.uc b/KFGame/Classes/KFSpawnVolume.uc index 0d7f9d7..0185ac3 100644 --- a/KFGame/Classes/KFSpawnVolume.uc +++ b/KFGame/Classes/KFSpawnVolume.uc @@ -109,7 +109,7 @@ enum ESquadType EST_Large, EST_Medium, EST_Small, - EST_Crawler, + EST_Crawler }; /** If set, players cannot spawn here, only AI (Versus) */ var() bool bNoPlayers; @@ -154,6 +154,12 @@ var() float UnTouchCoolDownTime; /** When was the last time a player touched it. */ var protected transient float LastUnTouchTime; +/** If true, will be used to spawn forcing to use the override pawn list -> MapReplacePawnClass */ +var() bool bForceUseMapReplacePawn; + +/** If true, this volume will be disabled for Bounty Hunt Spawn */ +var() bool bDisableForBountyHuntSpawn; + /********************************************************************************************* Debug ********************************************************************************************* */ @@ -503,6 +509,9 @@ DefaultProperties MaxHeightDiffToPlayers=500.f // 5 meters LargestSquadType=EST_Large + bForceUseMapReplacePawn=false + bDisableForBountyHuntSpawn=false + // Debugging bDebugVisibilityChecks=false bMinimalDebugRatingChecks=true diff --git a/KFGame/Classes/KFUnlockManager.uc b/KFGame/Classes/KFUnlockManager.uc index a19f672..7fbde0a 100644 --- a/KFGame/Classes/KFUnlockManager.uc +++ b/KFGame/Classes/KFUnlockManager.uc @@ -39,7 +39,8 @@ enum ESharedContentUnlock SCU_G36C, SCU_HVStormCannon, SCU_ZedMKIII, - SCU_Saiga12 + SCU_Saiga12, + SCU_MG3 }; @@ -398,4 +399,8 @@ defaultproperties Name=KFWeap_Shotgun_S12, IconPath="WEP_UI_Saiga12_TEX.UI_WeaponSelect_Saiga12", ID=9666)} + SharedContentList(SCU_MG3)={( + Name=KFWeap_LMG_MG3, + IconPath="WEP_UI_MG3_TEX.UI_WeaponSelect_MG3", + ID=9755)} } diff --git a/KFGame/Classes/KFWeapDef_G18.uc b/KFGame/Classes/KFWeapDef_G18.uc index 3a4770e..16067ba 100644 --- a/KFGame/Classes/KFWeapDef_G18.uc +++ b/KFGame/Classes/KFWeapDef_G18.uc @@ -14,7 +14,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_SMG_G18" BuyPrice=1500 - AmmoPricePerMag=24 + AmmoPricePerMag=22 // 24 ImagePath="WEP_UI_RiotShield_TEX.UI_WeaponSelect_RiotShield" IsPlayGoHidden=true; diff --git a/KFGame/Classes/KFWeapDef_G36C.uc b/KFGame/Classes/KFWeapDef_G36C.uc index 02dcde0..3a7ca84 100644 --- a/KFGame/Classes/KFWeapDef_G36C.uc +++ b/KFGame/Classes/KFWeapDef_G36C.uc @@ -15,7 +15,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_AssaultRifle_G36C" BuyPrice=1600 - AmmoPricePerMag=36 + AmmoPricePerMag=32 // 36 ImagePath="wep_ui_g36c_tex.UI_WeaponSelect_G36C" IsPlayGoHidden=true; diff --git a/KFGame/Classes/KFWeapDef_HK_UMP.uc b/KFGame/Classes/KFWeapDef_HK_UMP.uc index 8354137..6faaf1a 100644 --- a/KFGame/Classes/KFWeapDef_HK_UMP.uc +++ b/KFGame/Classes/KFWeapDef_HK_UMP.uc @@ -12,7 +12,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_SMG_HK_UMP" BuyPrice=1200 - AmmoPricePerMag=36 //45 + AmmoPricePerMag=32 // 36 //45 ImagePath="WEP_UI_HK_UMP_TEX.UI_WeaponSelect_UMP" EffectiveRange=70 diff --git a/KFGame/Classes/KFWeapDef_HRG_93R.uc b/KFGame/Classes/KFWeapDef_HRG_93R.uc new file mode 100644 index 0000000..546508b --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_93R.uc @@ -0,0 +1,35 @@ +//============================================================================= +// KFWeaponDefintion +//============================================================================= +// A lightweight container for basic weapon properties that can be safely +// accessed without a weapon actor (UI, remote clients). +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_93R extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_93R" + + BuyPrice=0 + AmmoPricePerMag=10 //8 + ImagePath="wep_ui_hrg_93r_pistol_tex.UI_WeaponSelect_HRG_93R" + + EffectiveRange=50 + + UpgradePrice[0]=200 + UpgradePrice[1]=500 + UpgradePrice[2]=600 + UpgradePrice[3]=700 + UpgradePrice[4]=1500 + + // Cannot be sold + //UpgradeSellPrice[0]=150 + //UpgradeSellPrice[1]=337 + //UpgradeSellPrice[2]=412 + //UpgradeSellPrice[3]=450 + //UpgradeSellPrice[4]=750 +} diff --git a/KFGame/Classes/KFWeapDef_HRG_93R_Dual.uc b/KFGame/Classes/KFWeapDef_HRG_93R_Dual.uc new file mode 100644 index 0000000..559ff10 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_93R_Dual.uc @@ -0,0 +1,35 @@ +//============================================================================= +// KFWeaponDefintion +//============================================================================= +// A lightweight container for basic weapon properties that can be safely +// accessed without a weapon actor (UI, remote clients). +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_93R_Dual extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_93R_Dual" + + BuyPrice=300 + AmmoPricePerMag=20 //16 + ImagePath="wep_ui_dual_hrg_93r_pistol_tex.UI_WeaponSelect_Dual_HRG_93R" + + EffectiveRange=50 + + UpgradePrice[0]=200 + UpgradePrice[1]=500 + UpgradePrice[2]=600 + UpgradePrice[3]=700 + UpgradePrice[4]=1500 + + // Code has this value set to 0 to avoid exploit selling. I'm setting the values to 0 here as well just in case. + UpgradeSellPrice[0]=0 + UpgradeSellPrice[1]=0 + UpgradeSellPrice[2]=0 + UpgradeSellPrice[3]=0 + UpgradeSellPrice[4]=0 +} diff --git a/KFGame/Classes/KFWeapDef_HRG_BarrierRifle.uc b/KFGame/Classes/KFWeapDef_HRG_BarrierRifle.uc index ae0b951..38b557f 100644 --- a/KFGame/Classes/KFWeapDef_HRG_BarrierRifle.uc +++ b/KFGame/Classes/KFWeapDef_HRG_BarrierRifle.uc @@ -14,7 +14,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_HRG_BarrierRifle" BuyPrice=2000 - AmmoPricePerMag=55 + AmmoPricePerMag=50 // 55 ImagePath="wep_ui_hrg_barrierrifle_tex.UI_WeaponSelect_HRG_BarrierRifle" IsPlayGoHidden=true; diff --git a/KFGame/Classes/KFWeapDef_HRG_Stunner.uc b/KFGame/Classes/KFWeapDef_HRG_Stunner.uc index fd4be18..fbe1d0c 100644 --- a/KFGame/Classes/KFWeapDef_HRG_Stunner.uc +++ b/KFGame/Classes/KFWeapDef_HRG_Stunner.uc @@ -14,7 +14,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_HRG_Stunner" BuyPrice=1500 - AmmoPricePerMag=36 + AmmoPricePerMag=32 // 36 ImagePath="ui_weaponselect_tex.UI_WeaponSelect_AA12" IsPlayGoHidden=true; diff --git a/KFGame/Classes/KFWeapDef_Kriss.uc b/KFGame/Classes/KFWeapDef_Kriss.uc index 0e9b504..ba63003 100644 --- a/KFGame/Classes/KFWeapDef_Kriss.uc +++ b/KFGame/Classes/KFWeapDef_Kriss.uc @@ -15,7 +15,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_SMG_Kriss" BuyPrice=1500 - AmmoPricePerMag=35 //35 + AmmoPricePerMag=31 //35 ImagePath="WEP_UI_KRISS_TEX.UI_WeaponSelect_KRISS" EffectiveRange=70 diff --git a/KFGame/Classes/KFWeapDef_MG3.uc b/KFGame/Classes/KFWeapDef_MG3.uc new file mode 100644 index 0000000..dc8a963 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_MG3.uc @@ -0,0 +1,25 @@ +//============================================================================= +// KFWeapDef_MG3 +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_MG3 extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_LMG_MG3" + + BuyPrice=2000 + AmmoPricePerMag=70 + ImagePath="WEP_UI_MG3_TEX.UI_WeaponSelect_MG3" + + IsPlayGoHidden=true; + + EffectiveRange=68 + + SharedUnlockId=SCU_MG3 +} \ No newline at end of file diff --git a/KFGame/Classes/KFWeapDef_MP5RAS.uc b/KFGame/Classes/KFWeapDef_MP5RAS.uc index 738f010..b3969c9 100644 --- a/KFGame/Classes/KFWeapDef_MP5RAS.uc +++ b/KFGame/Classes/KFWeapDef_MP5RAS.uc @@ -15,7 +15,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_SMG_MP5RAS" BuyPrice=650 - AmmoPricePerMag=28 //22 //33 + AmmoPricePerMag=25 // 28 //22 //33 ImagePath="WEP_UI_MP5RAS_TEX.UI_WeaponSelect_MP5RAS" EffectiveRange=70 diff --git a/KFGame/Classes/KFWeapDef_MP7.uc b/KFGame/Classes/KFWeapDef_MP7.uc index 722db0e..f3db5b0 100644 --- a/KFGame/Classes/KFWeapDef_MP7.uc +++ b/KFGame/Classes/KFWeapDef_MP7.uc @@ -15,7 +15,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_SMG_MP7" BuyPrice=200 - AmmoPricePerMag=16 //26 + AmmoPricePerMag=14 // 16 //26 ImagePath="WEP_UI_MP7_TEX.UI_WeaponSelect_MP7" EffectiveRange=70 diff --git a/KFGame/Classes/KFWeapDef_Mac10.uc b/KFGame/Classes/KFWeapDef_Mac10.uc index 90453cf..667f59d 100644 --- a/KFGame/Classes/KFWeapDef_Mac10.uc +++ b/KFGame/Classes/KFWeapDef_Mac10.uc @@ -14,7 +14,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_SMG_Mac10" BuyPrice=900//1100 - AmmoPricePerMag=32 + AmmoPricePerMag=29 // 32 ImagePath="WEP_UI_MAC10_TEX.UI_WeaponSelect_Mac10" EffectiveRange=70 diff --git a/KFGame/Classes/KFWeapDef_MedicSMG.uc b/KFGame/Classes/KFWeapDef_MedicSMG.uc index 45c9438..cc8ba27 100644 --- a/KFGame/Classes/KFWeapDef_MedicSMG.uc +++ b/KFGame/Classes/KFWeapDef_MedicSMG.uc @@ -15,7 +15,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_SMG_Medic" BuyPrice=650 - AmmoPricePerMag=21 + AmmoPricePerMag=19 // 21 ImagePath="ui_weaponselect_tex.UI_WeaponSelect_MedicSMG" EffectiveRange=70 diff --git a/KFGame/Classes/KFWeapDef_P90.uc b/KFGame/Classes/KFWeapDef_P90.uc index c97ebc9..f5d5697 100644 --- a/KFGame/Classes/KFWeapDef_P90.uc +++ b/KFGame/Classes/KFWeapDef_P90.uc @@ -15,7 +15,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_SMG_P90" BuyPrice=1100 - AmmoPricePerMag=40 //50 + AmmoPricePerMag=36 // 40 //50 ImagePath="WEP_UI_P90_TEX.UI_WeaponSelect_FNP90" EffectiveRange=70 diff --git a/KFGame/Classes/KFWeapDef_Thompson.uc b/KFGame/Classes/KFWeapDef_Thompson.uc index 3cc1ca0..b092bb0 100644 --- a/KFGame/Classes/KFWeapDef_Thompson.uc +++ b/KFGame/Classes/KFWeapDef_Thompson.uc @@ -14,7 +14,7 @@ DefaultProperties WeaponClassPath="KFGameContent.KFWeap_AssaultRifle_Thompson" BuyPrice=650 //650 - AmmoPricePerMag=50 + AmmoPricePerMag=45 // 50 ImagePath="WEP_UI_TommyGun_TEX.UI_WeaponSelect_TommyGun" diff --git a/KFGame/Classes/KFWeap_MedicBase.uc b/KFGame/Classes/KFWeap_MedicBase.uc index 7ea2806..999ed66 100644 --- a/KFGame/Classes/KFWeap_MedicBase.uc +++ b/KFGame/Classes/KFWeap_MedicBase.uc @@ -408,7 +408,10 @@ function StartHealRecharge() if( Role == ROLE_Authority ) { InstigatorPerk = GetPerk(); - UsedHealRechargeTime = HealFullRechargeSeconds * static.GetUpgradeHealRechargeMod(CurrentWeaponUpgradeIndex); + + UsedHealRechargeTime = HealFullRechargeSeconds; + UsedHealRechargeTime *= static.GetUpgradeHealRechargeMod(CurrentWeaponUpgradeIndex); + UsedHealRechargeTime *= InstigatorPerk.GetHealRechargeMod(); InstigatorPerk.ModifyHealerRechargeTime( UsedHealRechargeTime ); // Set the healing recharge rate whenever we start charging diff --git a/KFGame/Classes/KFWeapon.uc b/KFGame/Classes/KFWeapon.uc index a25c6d8..04b034c 100644 --- a/KFGame/Classes/KFWeapon.uc +++ b/KFGame/Classes/KFWeapon.uc @@ -988,6 +988,8 @@ var repnotify Actor TargetingCompRepActor; // To force the crosshair for showing up even if there's no spread var bool bForceCrosshair; +const MinFireIntervalToTriggerSync = 0.23f; + cpptext { // Actor @@ -6563,6 +6565,11 @@ simulated state WeaponFiring { StopLoopingFireEffects(CurrentFireMode); } + + if (WorldInfo.NetMode == NM_Client && bAllowClientAmmoTracking && FireInterval[CurrentFireMode] <= MinFireIntervalToTriggerSync) + { + SyncCurrentAmmoCount(CurrentFireMode, AmmoCount[CurrentFireMode]); + } } /** Override to continue any looping fire anims */ @@ -6607,6 +6614,14 @@ simulated state WeaponFiring } } +unreliable server function SyncCurrentAmmoCount(byte FireMode, byte CurrentAmmoCount) +{ + if(AmmoCount[FireMode] != CurrentAmmoCount) + { + AmmoCount[FireMode] = CurrentAmmoCount; + } +} + /** * Sets the timing for the firing state on server and local client. * By default, a constant looping Rate Of Fire (ROF) is set up. diff --git a/KFGame/Classes/KFWeaponSkinList.uc b/KFGame/Classes/KFWeaponSkinList.uc index ea410ae..7a1ee1a 100644 --- a/KFGame/Classes/KFWeaponSkinList.uc +++ b/KFGame/Classes/KFWeaponSkinList.uc @@ -4736,4 +4736,154 @@ defaultproperties //S12 Shockgun Tiger Skins.Add((Id=9671, Weapondef=class'KFWeapDef_Shotgun_S12', MIC_1P=("WEP_SkinSet76_MAT.Wep_1stP_Saiga12_Tiger_MIC"), MIC_3P="WEP_SkinSet76_MAT.WEP_3P_Saiga12_Tiger_MIC", MIC_Pickup="WEP_SkinSet76_MAT.Wep_3P_Pickup_Saiga12_Tiger_MIC")) + +//MG3 Shredder Standard + Skins.Add((Id=9755, Weapondef=class'KFWeapDef_MG3', MIC_1P=("wep_1p_mg3_mat.Wep_1stP_MG3_MIC"), MIC_3P="wep_3p_mg3_mat.Wep_3rdP_MG3_MIC", MIC_Pickup="wep_3p_mg3_mat.3P_Pickup_MG3_MIC")) + +//MG3 Shredder Antique + Skins.Add((Id=9756, Weapondef=class'KFWeapDef_MG3', MIC_1P=("wep_skinset82_mat.MG3_Antique_1P_MIC"), MIC_3P="WEP_SkinSet82_MAT.WEP_3P_MG3_Antique_MIC", MIC_Pickup="WEP_SkinSet82_MAT.WEP_3P_MG3_Antique_Pickup_MIC")) + +//MG3 Shredder Arctic + Skins.Add((Id=9757, Weapondef=class'KFWeapDef_MG3', MIC_1P=("wep_skinset82_mat.MG3_Arctic_1P_MIC"), MIC_3P="WEP_SkinSet82_MAT.WEP_3P_MG3_Arctic_MIC", MIC_Pickup="WEP_SkinSet82_MAT.WEP_3P_MG3_Arctic_Pickup_MIC")) + +//MG3 Shredder Desert + Skins.Add((Id=9758, Weapondef=class'KFWeapDef_MG3', MIC_1P=("wep_skinset82_mat.MG3_Desert_1P_MIC"), MIC_3P="WEP_SkinSet82_MAT.WEP_3P_MG3_Desert_MIC", MIC_Pickup="WEP_SkinSet82_MAT.WEP_3P_MG3_Desert_Pickup_MIC")) + +//MG3 Shredder Filigree + Skins.Add((Id=9759, Weapondef=class'KFWeapDef_MG3', MIC_1P=("wep_skinset82_mat.MG3_Filigree_1P_MIC"), MIC_3P="WEP_SkinSet82_MAT.WEP_3P_MG3_Filigree_MIC", MIC_Pickup="WEP_SkinSet82_MAT.WEP_3P_MG3_Filigree_Pickup_MIC")) + +//MG3 Shredder Forest + Skins.Add((Id=9760, Weapondef=class'KFWeapDef_MG3', MIC_1P=("wep_skinset82_mat.MG3_Forest_1P_MIC"), MIC_3P="WEP_SkinSet82_MAT.WEP_3P_MG3_Forest_MIC", MIC_Pickup="WEP_SkinSet82_MAT.WEP_3P_MG3_Forest_Pickup_MIC")) + +//Chameleon Dynamic 500 Magnum Revolver + Skins.Add((Id=9674, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SW500', MIC_1P=("WEP_SkinSet79_MAT.chameleon_sw500.Chameleon_SW500_1P_Mint_MIC"), MIC_3P="WEP_SkinSet79_MAT.chameleon_sw500.Chameleon_SW500_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet79_MAT.chameleon_sw500.Chameleon_SW500_3P_Pickup_MIC")) + +//Chameleon Dynamic Doomstick + Skins.Add((Id=9675, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet79_MAT.chameleon_quadbarrel.Chameleon_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet79_MAT.chameleon_quadbarrel.Chameleon_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet79_MAT.chameleon_quadbarrel.Chameleon_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet79_MAT.chameleon_quadbarrel.Chameleon_QuadBarrel_3P_Pickup_MIC")) + +//Chameleon Dynamic Hemoclobber + Skins.Add((Id=9676, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet79_MAT.chameleon_medicbat.Chameleon_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet79_MAT.chameleon_medicbat.Chameleon_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet79_MAT.chameleon_medicbat.Chameleon_MedicBat_3P_Pickup_MIC")) + +//Chameleon Dynamic P90 + Skins.Add((Id=9677, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet79_MAT.chameleon_p90.Chameleon_P90_1P_Mint_MIC", "WEP_SkinSet79_MAT.chameleon_p90.Chameleon_P90_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet79_MAT.chameleon_p90.Chameleon_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet79_MAT.chameleon_p90.Chameleon_P90_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB 500 Magnum Revolver + Skins.Add((Id=9678, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SW500', MIC_1P=("WEP_SkinSet79_MAT.chameleonrgb_sw500.ChameleonRGB_SW500_1P_Mint_MIC"), MIC_3P="WEP_SkinSet79_MAT.chameleonrgb_sw500.ChameleonRGB_SW500_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet79_MAT.chameleonrgb_sw500.ChameleonRGB_SW500_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB Doomstick + Skins.Add((Id=9679, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet79_MAT.chameleonrgb_quadbarrel.ChameleonRGB_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet79_MAT.chameleonrgb_quadbarrel.ChameleonRGB_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet79_MAT.chameleonrgb_quadbarrel.ChameleonRGB_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet79_MAT.chameleonrgb_quadbarrel.ChameleonRGB_QuadBarrel_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB Hemoclobber + Skins.Add((Id=9680, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet79_MAT.chameleonrgb_medicbat.ChameleonRGB_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet79_MAT.chameleonrgb_medicbat.ChameleonRGB_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet79_MAT.chameleonrgb_medicbat.ChameleonRGB_MedicBat_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB P90 + Skins.Add((Id=9681, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_P90', MIC_1P=("WEP_SkinSet79_MAT.chameleonrgb_p90.ChameleonRGB_P90_1P_Mint_MIC", "WEP_SkinSet79_MAT.chameleonrgb_p90.ChameleonRGB_P90_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet79_MAT.chameleonrgb_p90.ChameleonRGB_P90_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet79_MAT.chameleonrgb_p90.ChameleonRGB_P90_3P_Pickup_MIC")) + +//Medieval Dynamic 500 Magnum Revolver + Skins.Add((Id=9711, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SW500', MIC_1P=("WEP_SkinSet80_MAT.medieval_sw500.Medieval_SW500_1P_Mint_MIC"), MIC_3P="WEP_SkinSet80_MAT.medieval_sw500.Medieval_SW500_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet80_MAT.medieval_sw500.Medieval_SW500_3P_Pickup_MIC")) + +//Medieval Dynamic Dragonsbreath + Skins.Add((Id=9708, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet80_MAT.medieval_dragonsbreath.Medieval_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet80_MAT.medieval_dragonsbreath.Medieval_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet80_MAT.medieval_dragonsbreath.Medieval_Dragonsbreath_3P_Pickup_MIC")) + +//Medieval Dynamic M79 + Skins.Add((Id=9709, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet80_MAT.medieval_m79.Medieval_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet80_MAT.medieval_m79.Medieval_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet80_MAT.medieval_m79.Medieval_M79_3P_Pickup_MIC")) + +//Medieval Dynamic Tommy Gun + Skins.Add((Id=9710, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Thompson', MIC_1P=("WEP_SkinSet80_MAT.medieval_tommygun.Medieval_TommyGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet80_MAT.medieval_tommygun.Medieval_TommyGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet80_MAT.medieval_tommygun.Medieval_TommyGun_3P_Pickup_MIC")) + +//Medieval Dynamic Precious 500 Magnum Revolver + Skins.Add((Id=9712, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SW500', MIC_1P=("WEP_SkinSet80_MAT.medievalgold_sw500.MedievalGold_SW500_1P_Mint_MIC"), MIC_3P="WEP_SkinSet80_MAT.medievalgold_sw500.MedievalGold_SW500_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet80_MAT.medievalgold_sw500.MedievalGold_SW500_3P_Pickup_MIC")) + +//Medieval Dynamic Precious Dragonsbreath + Skins.Add((Id=9713, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet80_MAT.medievalgold_dragonsbreath.MedievalGold_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet80_MAT.medievalgold_dragonsbreath.MedievalGold_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet80_MAT.medievalgold_dragonsbreath.MedievalGold_Dragonsbreath_3P_Pickup_MIC")) + +//Medieval Dynamic Precious M79 + Skins.Add((Id=9714, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet80_MAT.medievalgold_m79.MedievalGold_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet80_MAT.medievalgold_m79.MedievalGold_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet80_MAT.medievalgold_dragonsbreath.MedievalGold_M79_3P_Pickup_MIC")) + +//Medieval Dynamic Precious Tommy Gun + Skins.Add((Id=9715, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Thompson', MIC_1P=("WEP_SkinSet80_MAT.medievalgold_tommygun.MedievalGold_TommyGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet80_MAT.medievalgold_tommygun.MedievalGold_TommyGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet80_MAT.medievalgold_tommygun.MedievalGold_TommyGun_3P_Pickup_MIC")) + +//Spectre HRG 93R + Skins.Add((Id=9682, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_93R', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrg93r.Spectre_HRG93R_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrg93r.Spectre_HRG93R_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrg93r.Spectre_HRG93R_3P_Pickup_MIC")) + +//Spectre HRG Ballistic Bouncer + Skins.Add((Id=9683, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_BallisticBouncer', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgballisticbouncer.Spectre_HRGBallisticBouncer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgballisticbouncer.Spectre_HRGBallisticBouncer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgballisticbouncer.Spectre_HRGBallisticBouncer_3P_Pickup_MIC")) + +//Spectre HRG Bastion + Skins.Add((Id=9684, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_BarrierRifle', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgbarrierrifle.Spectre_HRGBarrierRifle_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectre_hrgbarrierrifle.Spectre_HRGBarrierRifle_Receiver_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectre_hrgbarrierrifle.Spectre_HRGBarrierRifle_Extra_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgbarrierrifle.Spectre_HRGBarrierRifle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgbarrierrifle.Spectre_HRGBarrierRifle_3P_Pickup_MIC")) + +//Spectre HRG Beluga Beat + Skins.Add((Id=9685, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_SonicGun', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgsonicgun.Spectre_HRGSonicGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgsonicgun.Spectre_HRGSonicGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgsonicgun.Spectre_HRGSonicGun_3P_Pickup_MIC")) + +//Spectre HRG Blast Brawlers + Skins.Add((Id=9686, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_BlastBrawlers', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgblastbrawlers.Spectre_HRGBlastBrawlers_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectre_hrgblastbrawlers.Spectre_HRGBlastBrawlers_Extra_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgblastbrawlers.Spectre_HRGBlastBrawlers_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgblastbrawlers.Spectre_HRGBlastBrawlers_3P_Pickup_MIC")) + +//Spectre HRG Crossboom + Skins.Add((Id=9687, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Crossboom', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgcrossboom.Spectre_HRGCrossboom_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgcrossboom.Spectre_HRGCrossboom_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgcrossboom.Spectre_HRGCrossboom_3P_Pickup_MIC")) + +//Spectre HRG Disrupter + Skins.Add((Id=9688, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Energy', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgenergy.Spectre_HRGEnergy_1P_Mint_MIC", "WEP_1P_HRG_Energy_MAT.Wep_Energy_Pistol_Optic_PM_MIC", "WEP_SkinSet78_MAT.spectre_hrgenergy.Spectre_HRGEnergy_Extra_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgenergy.Spectre_HRGEnergy_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgenergy.Spectre_HRGEnergy_3P_Pickup_MIC")) + +//Spectre HRG Dragonsblaze + Skins.Add((Id=9689, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Dragonbreath', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgmegadragonsbreath.Spectre_HRGMegaDragonsbreath_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectre_hrgmegadragonsbreath.Spectre_HRGMegaDragonsbreath_Barrel_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectre_hrgmegadragonsbreath.Spectre_HRGMegaDragonsbreath_Loader_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgmegadragonsbreath.Spectre_HRGMegaDragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgmegadragonsbreath.Spectre_HRGMegaDragonsbreath_3P_Pickup_MIC")) + +//Spectre HRG Head Hunter + Skins.Add((Id=9690, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_CranialPopper', MIC_1P=("WEP_1P_HRG_CranialPopper_MAT.Wep_Cranial_Optic_PM_MIC", "WEP_SkinSet78_MAT.spectre_hrgcranialpopper.Spectre_HRGCranialPopper_Optic_1P_Mint_MIC", "WEP_1P_RailGun_MAT.WEP_RailGunScopeLense_PM", "WEP_SkinSet78_MAT.spectre_hrgcranialpopper.Spectre_HRGCranialPopper_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgcranialpopper.Spectre_HRGCranialPopper_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgcranialpopper.Spectre_HRGCranialPopper_3P_Pickup_MIC")) + +//Spectre HRG Locust + Skins.Add((Id=9691, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Locust', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrglocust.Spectre_HRGLocust_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectre_hrglocust.Spectre_HRGLocust_Optic_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrglocust.Spectre_HRGLocust_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrglocust.Spectre_HRGLocust_3P_Pickup_MIC")) + +//Spectre HRG Medic Missile + Skins.Add((Id=9692, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_MedicMissile', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgmedicmissile.Spectre_HRGMedicMissile_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgmedicmissile.Spectre_HRGMedicMissile_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgmedicmissile.Spectre_HRGMedicMissile_3P_Pickup_MIC")) + +//Spectre HRG Stunner + Skins.Add((Id=9693, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Stunner', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgstunner.Spectre_HRGStunner_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgstunner.Spectre_HRGStunner_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgstunner.Spectre_HRGStunner_3P_Pickup_MIC")) + +//Spectre HRG Tommy Boom + Skins.Add((Id=9694, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Boomy', MIC_1P=("WEP_SkinSet78_MAT.spectre_hrgboomy.Spectre_HRGBoomy_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectre_hrgboomy.Spectre_HRGBoomy_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectre_hrgboomy.Spectre_HRGBoomy_3P_Pickup_MIC")) + +//Spectre Chroma HRG 93R + Skins.Add((Id=9695, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_93R', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrg93r.SpectreChroma_HRG93R_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrg93r.SpectreChroma_HRG93R_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrg93r.SpectreChroma_HRG93R_3P_Pickup_MIC")) + +//Spectre Chroma HRG Ballistic Bouncer + Skins.Add((Id=9696, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_BallisticBouncer', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgballisticbouncer.SpectreChroma_HRGBallisticBouncer_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgballisticbouncer.SpectreChroma_HRGBallisticBouncer_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgballisticbouncer.SpectreChroma_HRGBallisticBouncer_3P_Pickup_MIC")) + +//Spectre Chroma HRG Bastion + Skins.Add((Id=9697, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_BarrierRifle', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgbarrierrifle.SpectreChroma_HRGBarrierRifle_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectrechroma_hrgbarrierrifle.SpectreChroma_HRGBarrierRifle_Receiver_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectrechroma_hrgbarrierrifle.SpectreChroma_HRGBarrierRifle_Extra_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgbarrierrifle.SpectreChroma_HRGBarrierRifle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgbarrierrifle.SpectreChroma_HRGBarrierRifle_3P_Pickup_MIC")) + +//Spectre Chroma HRG Beluga Beat + Skins.Add((Id=9698, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_SonicGun', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgsonicgun.SpectreChroma_HRGSonicGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgsonicgun.SpectreChroma_HRGSonicGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgsonicgun.SpectreChroma_HRGSonicGun_3P_Pickup_MIC")) + +//Spectre Chroma HRG Blast Brawlers + Skins.Add((Id=9699, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_BlastBrawlers', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgblastbrawlers.SpectreChroma_HRGBlastBrawlers_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectrechroma_hrgblastbrawlers.SpectreChroma_HRGBlastBrawlers_Extra_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgblastbrawlers.SpectreChroma_HRGBlastBrawlers_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgblastbrawlers.SpectreChroma_HRGBlastBrawlers_3P_Pickup_MIC")) + +//Spectre Chroma HRG Crossboom + Skins.Add((Id=9700, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Crossboom', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgcrossboom.SpectreChroma_HRGCrossboom_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgcrossboom.SpectreChroma_HRGCrossboom_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgcrossboom.SpectreChroma_HRGCrossboom_3P_Pickup_MIC")) + +//Spectre Chroma HRG Disrupter + Skins.Add((Id=9701, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Energy', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgenergy.SpectreChroma_HRGEnergy_1P_Mint_MIC", "WEP_1P_HRG_Energy_MAT.Wep_Energy_Pistol_Optic_PM_MIC", "WEP_SkinSet78_MAT.spectrechroma_hrgenergy.SpectreChroma_HRGEnergy_Extra_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgenergy.SpectreChroma_HRGEnergy_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgenergy.SpectreChroma_HRGEnergy_3P_Pickup_MIC")) + +//Spectre Chroma HRG Dragonsblaze + Skins.Add((Id=9702, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Dragonbreath', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgmegadragonsbreath.SpectreChroma_HRGMegaDragonsbreath_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectrechroma_hrgmegadragonsbreath.SpectreChroma_HRGMegaDragonsbreath_Barrel_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectrechroma_hrgmegadragonsbreath.SpectreChroma_HRGMegaDragonsbreath_Loader_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgmegadragonsbreath.SpectreChroma_HRGMegaDragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgmegadragonsbreath.SpectreChroma_HRGMegaDragonsbreath_3P_Pickup_MIC")) + +//Spectre Chroma HRG Head Hunter + Skins.Add((Id=9703, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_CranialPopper', MIC_1P=("WEP_1P_HRG_CranialPopper_MAT.Wep_Cranial_Optic_PM_MIC", "WEP_SkinSet78_MAT.spectrechroma_hrgcranialpopper.SpectreChroma_HRGCranialPopper_Optic_1P_Mint_MIC", "WEP_1P_RailGun_MAT.WEP_RailGunScopeLense_PM", "WEP_SkinSet78_MAT.spectrechroma_hrgcranialpopper.SpectreChroma_HRGCranialPopper_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgcranialpopper.SpectreChroma_HRGCranialPopper_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgcranialpopper.SpectreChroma_HRGCranialPopper_3P_Pickup_MIC")) + +//Spectre Chroma HRG Locust + Skins.Add((Id=9704, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Locust', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrglocust.SpectreChroma_HRGLocust_1P_Mint_MIC", "WEP_SkinSet78_MAT.spectrechroma_hrglocust.SpectreChroma_HRGLocust_Optic_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrglocust.SpectreChroma_HRGLocust_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrglocust.SpectreChroma_HRGLocust_3P_Pickup_MIC")) + +//Spectre Chroma HRG Medic Missile + Skins.Add((Id=9705, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_MedicMissile', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgmedicmissile.SpectreChroma_HRGMedicMissile_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgmedicmissile.SpectreChroma_HRGMedicMissile_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgmedicmissile.SpectreChroma_HRGMedicMissile_3P_Pickup_MIC")) + +//Spectre Chroma HRG Stunner + Skins.Add((Id=9706, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Stunner', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgstunner.SpectreChroma_HRGStunner_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgstunner.SpectreChroma_HRGStunner_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgstunner.SpectreChroma_HRGStunner_3P_Pickup_MIC")) + +//Spectre Chroma HRG Tommy Boom + Skins.Add((Id=9707, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HRG_Boomy', MIC_1P=("WEP_SkinSet78_MAT.spectrechroma_hrgboomy.SpectreChroma_HRGBoomy_1P_Mint_MIC"), MIC_3P="WEP_SkinSet78_MAT.spectrechroma_hrgboomy.SpectreChroma_HRGBoomy_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet78_MAT.spectrechroma_hrgboomy.SpectreChroma_HRGBoomy_3P_Pickup_MIC")) + +//Bounty Hunt Deagle + Skins.Add((Id=9753, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet81_MAT.BountyHunt.BountyHunt_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet81_MAT.BountyHunt.BountyHunt_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet81_MAT.BountyHunt.BountyHunt_Deagle_3P_Pickup_MIC")) + +//Voltar MKB42 + Skins.Add((Id=9754, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MKB42', MIC_1P=("WEP_SkinSet81_MAT.hans_mkb42.Hans_MKB42_1P_Mint_MIC"), MIC_3P="WEP_SkinSet81_MAT.hans_mkb42.Hans_MKB42_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet81_MAT.hans_mkb42.Hans_MKB42_3P_Pickup_MIC")) } \ No newline at end of file diff --git a/KFGame/Classes/KFWeeklyOutbreakInformation.uc b/KFGame/Classes/KFWeeklyOutbreakInformation.uc index 5a69979..882a538 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 = 20; + static const int NumWeeklyEvents = 21; } DefaultProperties { diff --git a/KFGame/KFOnlineStats.uci b/KFGame/KFOnlineStats.uci index 7b0808f..a5a0648 100644 --- a/KFGame/KFOnlineStats.uci +++ b/KFGame/KFOnlineStats.uci @@ -160,4 +160,5 @@ const STATID_ACHIEVE_RigCollectibles = 4062; const STATID_ACHIEVE_BarmwichCollectibles = 4063; const STATID_ACHIEVE_CrashCollectibles = 4064; const STATID_ACHIEVE_SubductionCollectibles = 4065; +const STATID_ACHIEVE_VolterCastleCollectibles = 4066; /** `endif */ diff --git a/KFGame/KFProfileSettings.uci b/KFGame/KFProfileSettings.uci index 05cfc0a..9488383 100644 --- a/KFGame/KFProfileSettings.uci +++ b/KFGame/KFProfileSettings.uci @@ -80,4 +80,7 @@ const KFID_MouseLookUpScale=181; // Halloweeen 2022 QoL: added mouse options to const KFID_MouseLookRightScale=182; // Halloweeen 2022 QoL: added mouse options to menu const KFID_ViewSmoothingEnabled=183; // Halloweeen 2022 QoL: added mouse options to menu const KFID_ViewAccelerationEnabled=184; // Halloweeen 2022 QoL: added mouse options to menu -const KFID_SavedAllowSeasonalSkinsIndex=185; \ No newline at end of file +const KFID_SavedAllowSeasonalSkinsIndex=185; +const KFID_SurvivalStartingSecondaryWeapIdx=186; // Halloween 2023 : added HRG 93R Pistol to Survivalist secondary weapons +const KFID_KilledHansVolterInMaps=187; // Halloween 2023 : Seasonal Kill Hans on Different Maps +const KFID_SavedWeeklySelectorIndex=188; // Halloween 2023 QoL: Weekly selector \ No newline at end of file diff --git a/KFGameContent/Classes/KFAIController_ZedPatriarch.uc b/KFGameContent/Classes/KFAIController_ZedPatriarch.uc index 64ffea5..85c83b3 100644 --- a/KFGameContent/Classes/KFAIController_ZedPatriarch.uc +++ b/KFGameContent/Classes/KFAIController_ZedPatriarch.uc @@ -1698,11 +1698,12 @@ function DoFleeFrom( actor FleeFrom, optional float FleeDuration, optional float FleeDistance, optional bool bShouldStopAtGoal=false, - optional bool bFromFear=false ) + optional bool bFromFear=false, + optional bool bUseRandomDirection=false ) { if( !bFromFear || !MyPatPawn.bInFleeAndHealMode ) { - super.DoFleeFrom( FleeFrom, FleeDuration, FleeDistance, bShouldStopAtGoal, bFromFear ); + super.DoFleeFrom( FleeFrom, FleeDuration, FleeDistance, bShouldStopAtGoal, bFromFear, bUseRandomDirection ); } } diff --git a/KFGameContent/Classes/KFCollectibleActor.uc b/KFGameContent/Classes/KFCollectibleActor.uc index 25b2ed0..1737614 100644 --- a/KFGameContent/Classes/KFCollectibleActor.uc +++ b/KFGameContent/Classes/KFCollectibleActor.uc @@ -19,6 +19,7 @@ var private bool bFound; protected event TriggerDestroyedEvent( Controller EventInstigator ) { local KFMapInfo KFMI; + local KFPlayerController KFPC; super.TriggerDestroyedEvent( EventInstigator ); @@ -27,6 +28,12 @@ protected event TriggerDestroyedEvent( Controller EventInstigator ) { bFound = true; KFMI.OnCollectibleFound( self, EventInstigator ); + + KFPC = KFPlayerController(EventInstigator); + If (KFPC != none) + { + KFPC.AddCollectibleFound(KFMI.CollectiblesToFind); + } } // Used on network to tell clients who join late that this collectible is destroyed diff --git a/KFGameContent/Classes/KFDT_Ballistic_Assault_Medic.uc b/KFGameContent/Classes/KFDT_Ballistic_Assault_Medic.uc index c24a9b0..6729dc4 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_Assault_Medic.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_Assault_Medic.uc @@ -30,36 +30,6 @@ static simulated function bool CanDismemberHitZone( name InHitZoneName ) return false; } -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - -/** - * Allows medic perk to add poison damage - * @return: None if toxic skill is not available - */ -static function class GetMedicToxicDmgType( out int out_Damage, optional Controller InstigatedBy ) -{ - local KFPerk InstigatorPerk; - - InstigatorPerk = KFPlayerController(InstigatedBy).GetPerk(); - if( InstigatorPerk == none || (!InstigatorPerk.IsToxicDmgActive() && !InstigatorPerk.IsZedativeActive()) ) - { - return None; - } - - InstigatorPerk.ModifyToxicDmg( out_Damage ); - return InstigatorPerk.GetToxicDmgTypeClass(); -} - defaultproperties { KDamageImpulse=1000 diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRGIncisionHurt.uc b/KFGameContent/Classes/KFDT_Ballistic_HRGIncisionHurt.uc index ecf6f3b..a87bdcb 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_HRGIncisionHurt.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_HRGIncisionHurt.uc @@ -40,18 +40,6 @@ static simulated function bool CanDismemberHitZone( name InHitZoneName ) return false; } -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { GoreDamageGroup=DGT_Shotgun diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_93R.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_93R.uc new file mode 100644 index 0000000..12c8c2a --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_93R.uc @@ -0,0 +1,25 @@ +//============================================================================= +// KFDT_Ballistic_HRG_93R +//============================================================================= +// Base pistol damage type +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_HRG_93R extends KFDT_Ballistic_Handgun + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=900 + KDeathUpKick=-300 + KDeathVel=100 + + KnockdownPower=12 + StumblePower=0 + GunHitPower=10 + + WeaponDef=class'KFWeapDef_HRG_93R' +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_Hemogoblin.uc b/KFGameContent/Classes/KFDT_Ballistic_Hemogoblin.uc index 042a54e..e95975e 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_Hemogoblin.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_Hemogoblin.uc @@ -89,17 +89,6 @@ static function PlayImpactHitEffects(KFPawn P, vector HitLocation, vector HitDir /** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) { - local class ToxicDT; - local int ToxicDamageTaken; - - ToxicDamageTaken = DamageTaken; - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( ToxicDamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(ToxicDamageTaken, InstigatedBy, ToxicDT); - } - - // potential for two DoTs if DoT_Type is set if (default.BleedDamageType.default.DoT_Type != DOT_None) { Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, default.BleedDamageType); diff --git a/KFGameContent/Classes/KFDT_Ballistic_MG3.uc b/KFGameContent/Classes/KFDT_Ballistic_MG3.uc new file mode 100644 index 0000000..1b1c264 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_MG3.uc @@ -0,0 +1,26 @@ +//============================================================================= +// KFDT_Ballistic_MG3 +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFDT_Ballistic_MG3 extends KFDT_Ballistic_AssaultRifle + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=900 + KDeathUpKick=-300 + KDeathVel=100 + + StumblePower=10 + GunHitPower=50 + + WeaponDef=class'KFWeapDef_MG3' + + //Perk + ModifierPerkList(0)=class'KFPerk_Commando' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Ballistic_MG3_Alt.uc b/KFGameContent/Classes/KFDT_Ballistic_MG3_Alt.uc new file mode 100644 index 0000000..6ae30c3 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_MG3_Alt.uc @@ -0,0 +1,26 @@ +//============================================================================= +// KFDT_Ballistic_MG3_Alt +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFDT_Ballistic_MG3_Alt extends KFDT_Ballistic_AssaultRifle + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=900 + KDeathUpKick=-300 + KDeathVel=100 + + StumblePower=10 + GunHitPower=50 + + WeaponDef=class'KFWeapDef_MG3' + + //Perk + ModifierPerkList(0)=class'KFPerk_Commando' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Ballistic_MedicRifleGrenadeLauncher.uc b/KFGameContent/Classes/KFDT_Ballistic_MedicRifleGrenadeLauncher.uc index fcbb829..45c4951 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_MedicRifleGrenadeLauncher.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_MedicRifleGrenadeLauncher.uc @@ -11,18 +11,6 @@ class KFDT_Ballistic_MedicRifleGrenadeLauncher extends KFDT_Ballistic_AssaultRif abstract hidedropdown; -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { KDamageImpulse=900 @@ -32,7 +20,6 @@ defaultproperties StumblePower=10 GunHitPower=45 - WeaponDef=class'KFWeapDef_MedicRifleGrenadeLauncher' //Perk diff --git a/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanter.uc b/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanter.uc index c29fbda..50e2298 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanter.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanter.uc @@ -28,18 +28,6 @@ static simulated function bool CanDismemberHitZone( name InHitZoneName ) return false; } -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { KDamageImpulse=2000 diff --git a/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanterAlt.uc b/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanterAlt.uc index 3a64e54..f937e4f 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanterAlt.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_ParasiteImplanterAlt.uc @@ -10,18 +10,6 @@ class KFDT_Ballistic_ParasiteImplanterAlt extends KFDT_Toxic abstract hidedropdown; -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { KnockdownPower=30 diff --git a/KFGameContent/Classes/KFDT_Ballistic_Pistol_Medic.uc b/KFGameContent/Classes/KFDT_Ballistic_Pistol_Medic.uc index 5913ec0..b8bc0dd 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_Pistol_Medic.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_Pistol_Medic.uc @@ -12,19 +12,6 @@ class KFDT_Ballistic_Pistol_Medic extends KFDT_Ballistic_Handgun abstract hidedropdown; - -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { KDamageImpulse=900 diff --git a/KFGameContent/Classes/KFDT_Ballistic_SMG_Medic.uc b/KFGameContent/Classes/KFDT_Ballistic_SMG_Medic.uc index f7fdc5e..333bb3b 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_SMG_Medic.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_SMG_Medic.uc @@ -12,18 +12,6 @@ class KFDT_Ballistic_SMG_Medic extends KFDT_Ballistic_Submachinegun abstract hidedropdown; -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { KDamageImpulse=900 diff --git a/KFGameContent/Classes/KFDT_Ballistic_Shotgun_Medic.uc b/KFGameContent/Classes/KFDT_Ballistic_Shotgun_Medic.uc index 6f2d1db..33a8724 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_Shotgun_Medic.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_Shotgun_Medic.uc @@ -31,18 +31,6 @@ static simulated function bool CanDismemberHitZone( name InHitZoneName ) return false; } -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { BloodSpread=0.4 diff --git a/KFGameContent/Classes/KFDT_Bleeding_HRG_Vampire_BloodSuck.uc b/KFGameContent/Classes/KFDT_Bleeding_HRG_Vampire_BloodSuck.uc index 0169e86..a9f76ba 100644 --- a/KFGameContent/Classes/KFDT_Bleeding_HRG_Vampire_BloodSuck.uc +++ b/KFGameContent/Classes/KFDT_Bleeding_HRG_Vampire_BloodSuck.uc @@ -10,18 +10,6 @@ class KFDT_Bleeding_HRG_Vampire_BloodSuck extends KFDT_Bleeding abstract; -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { //physics diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_93R.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_93R.uc new file mode 100644 index 0000000..36ebad9 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_93R.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_93R +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_93R extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_93R' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_MG3.uc b/KFGameContent/Classes/KFDT_Bludgeon_MG3.uc new file mode 100644 index 0000000..c674de5 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_MG3.uc @@ -0,0 +1,15 @@ +//============================================================================= +// KFDT_Bludgeon_MG3 +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2016 Tripwire Interactive LLC +//============================================================================= +class KFDT_Bludgeon_MG3 extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_MG3' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Dart_Toxic.uc b/KFGameContent/Classes/KFDT_Dart_Toxic.uc index d794d24..f3de93e 100644 --- a/KFGameContent/Classes/KFDT_Dart_Toxic.uc +++ b/KFGameContent/Classes/KFDT_Dart_Toxic.uc @@ -12,7 +12,51 @@ class KFDT_Dart_Toxic extends KFDT_Toxic abstract hidedropdown; +/** Called when damage is dealt to apply additional instant damage */ +static function ModifyInstantDamage( KFPawn Victim, out int DamageTaken, optional Controller InstigatedBy ) +{ + class'KFDT_Dart_Toxic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); +} + +/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ +static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) +{ + local class ToxicDT; + + ToxicDT = class'KFDT_Dart_Toxic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); + if ( ToxicDT != None ) + { + Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); + } +} + +/** + * Allows medic perk to add poison damage +*/ + +static function class GetMedicToxicDmgType( out int out_Damage, optional Controller InstigatedBy ) +{ + local KFPerk InstigatorPerk; + + InstigatorPerk = KFPlayerController(InstigatedBy).GetPerk(); + if( InstigatorPerk == none || (!InstigatorPerk.IsToxicDmgActive() && !InstigatorPerk.IsZedativeActive()) ) + { + return None; + } + + InstigatorPerk.ModifyToxicDmg( out_Damage ); + + return InstigatorPerk.GetToxicDmgTypeClass(); +} + defaultproperties { WeaponDef=class'KFWeapDef_Healer' + + DoT_Type=DOT_None + DoT_Duration=0.0 + DoT_Interval=0.0 + DoT_DamageScale=0.0 + + PoisonPower=0.f } diff --git a/KFGameContent/Classes/KFDT_Piercing_HRG_Vampire_CrystalSpike.uc b/KFGameContent/Classes/KFDT_Piercing_HRG_Vampire_CrystalSpike.uc index 202b271..64139b5 100644 --- a/KFGameContent/Classes/KFDT_Piercing_HRG_Vampire_CrystalSpike.uc +++ b/KFGameContent/Classes/KFDT_Piercing_HRG_Vampire_CrystalSpike.uc @@ -11,18 +11,6 @@ class KFDT_Piercing_HRG_Vampire_CrystalSpike extends KFDT_Piercing abstract hidedropdown; -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { KDamageImpulse=1500 diff --git a/KFGameContent/Classes/KFDT_Piercing_KnifeStab_FieldMedic.uc b/KFGameContent/Classes/KFDT_Piercing_KnifeStab_FieldMedic.uc index 713c656..edb3912 100644 --- a/KFGameContent/Classes/KFDT_Piercing_KnifeStab_FieldMedic.uc +++ b/KFGameContent/Classes/KFDT_Piercing_KnifeStab_FieldMedic.uc @@ -11,18 +11,6 @@ class KFDT_Piercing_KnifeStab_FieldMedic extends KFDT_Piercing_KnifeStab abstract hidedropdown; -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { ModifierPerkList(0)=class'KFPerk_FieldMedic' diff --git a/KFGameContent/Classes/KFDT_Slashing_KnifeHeavy_Medic.uc b/KFGameContent/Classes/KFDT_Slashing_KnifeHeavy_Medic.uc index 2daf6ff..d41e43c 100644 --- a/KFGameContent/Classes/KFDT_Slashing_KnifeHeavy_Medic.uc +++ b/KFGameContent/Classes/KFDT_Slashing_KnifeHeavy_Medic.uc @@ -12,18 +12,6 @@ class KFDT_Slashing_KnifeHeavy_Medic extends KFDT_Slashing_KnifeHeavy abstract hidedropdown; -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { MeleeHitPower=113 diff --git a/KFGameContent/Classes/KFDT_Slashing_Knife_Medic.uc b/KFGameContent/Classes/KFDT_Slashing_Knife_Medic.uc index 03b4768..ee16115 100644 --- a/KFGameContent/Classes/KFDT_Slashing_Knife_Medic.uc +++ b/KFGameContent/Classes/KFDT_Slashing_Knife_Medic.uc @@ -12,18 +12,6 @@ class KFDT_Slashing_Knife_Medic extends KFDT_Slashing_Knife abstract hidedropdown; -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { GunHitPower=44 diff --git a/KFGameContent/Classes/KFDT_Toxic_MineReconstructorImpact.uc b/KFGameContent/Classes/KFDT_Toxic_MineReconstructorImpact.uc index 00002e4..0a1fe6a 100644 --- a/KFGameContent/Classes/KFDT_Toxic_MineReconstructorImpact.uc +++ b/KFGameContent/Classes/KFDT_Toxic_MineReconstructorImpact.uc @@ -12,26 +12,11 @@ class KFDT_Toxic_MineReconstructorImpact extends KFDT_Ballistic_Shell abstract hidedropdown; - - - static simulated function bool CanDismemberHitZone( name InHitZoneName ) { return false; } -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { //override dot as the impact has no dot diff --git a/KFGameContent/Classes/KFDT_Toxic_MineReconstructorImpactHeavy.uc b/KFGameContent/Classes/KFDT_Toxic_MineReconstructorImpactHeavy.uc index e6f36d2..95fa5ab 100644 --- a/KFGameContent/Classes/KFDT_Toxic_MineReconstructorImpactHeavy.uc +++ b/KFGameContent/Classes/KFDT_Toxic_MineReconstructorImpactHeavy.uc @@ -12,26 +12,11 @@ class KFDT_Toxic_MineReconstructorImpactHeavy extends KFDT_Ballistic_Shell abstract hidedropdown; - - - static simulated function bool CanDismemberHitZone( name InHitZoneName ) { return false; } -/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ -static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) -{ - local class ToxicDT; - - ToxicDT = class'KFDT_Ballistic_Assault_Medic'.static.GetMedicToxicDmgType( DamageTaken, InstigatedBy ); - if ( ToxicDT != None ) - { - Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, ToxicDT); - } -} - defaultproperties { //override dot as the impact has no dot @@ -39,7 +24,6 @@ defaultproperties EffectGroup=FXG_Toxic - MicrowavePower=50; PoisonPower=60; StumblePower=340; diff --git a/KFGameContent/Classes/KFGameInfo_Endless.uc b/KFGameContent/Classes/KFGameInfo_Endless.uc index 6f10f29..f800386 100644 --- a/KFGameContent/Classes/KFGameInfo_Endless.uc +++ b/KFGameContent/Classes/KFGameInfo_Endless.uc @@ -506,7 +506,7 @@ function ReduceDamage(out int Damage, Pawn Injured, Controller InstigatedBy, vec function StartOutbreakRound(int OutbreakIdx) { - OutbreakEvent.SetActiveEvent(OutbreakIdx); + OutbreakEvent.SetActiveEvent(OutbreakIdx, self); OutbreakEvent.UpdateGRI(); OutbreakEvent.SetWorldInfoOverrides(); diff --git a/KFGameContent/Classes/KFGameInfo_Survival.uc b/KFGameContent/Classes/KFGameInfo_Survival.uc index 4a2e3c4..0345fe4 100644 --- a/KFGameContent/Classes/KFGameInfo_Survival.uc +++ b/KFGameContent/Classes/KFGameInfo_Survival.uc @@ -50,6 +50,9 @@ var protected bool bGunGamePlayerOnLastGun; var transient array BonfireVolumes; +// Trader Time modifier for Castle Volter map in the last round +var float CastleVolterTraderModifier; + /** Whether this game mode should play music from the get-go (lobby) */ static function bool ShouldPlayMusicAtStart() { @@ -926,6 +929,7 @@ function StartWave() WaveStarted(); MyKFGRI.NotifyWaveStart(); + MyKFGRI.AIRemaining = SpawnManager.WaveTotalAI; MyKFGRI.WaveTotalAICount = SpawnManager.WaveTotalAI; @@ -1469,7 +1473,17 @@ function DoTraderTimeCleanup(); /** Handle functionality for opening trader */ function OpenTrader() { - MyKFGRI.OpenTrader(TimeBetweenWaves); + local int UpdatedTimeBetweenWaves; + + UpdatedTimeBetweenWaves = TimeBetweenWaves; + + // In castle volter the trader needs to have a special time + if (WorldInfo.GetMapName(true) == "KF-CastleVolter" && WaveNum == (WaveMax - 1) ) + { + UpdatedTimeBetweenWaves = UpdatedTimeBetweenWaves * CastleVolterTraderModifier; + } + + MyKFGRI.OpenTrader(UpdatedTimeBetweenWaves); NotifyTraderOpened(); } @@ -1924,7 +1938,8 @@ DefaultProperties MaxGameDifficulty=3 bWaveStarted=false bGunGamePlayerOnLastGun=false - + CastleVolterTraderModifier = 1.0f; + ObjectiveSpawnDelay=5 SpawnManagerClasses(0)=class'KFGame.KFAISpawnManager_Short' @@ -1962,6 +1977,7 @@ DefaultProperties AIClassList(AT_EDAR_EMP)=class'KFGameContent.KFPawn_ZedDAR_Emp' AIClassList(AT_EDAR_Laser)=class'KFGameContent.KFPawn_ZedDAR_Laser' AIClassList(AT_EDAR_Rocket)=class'KFGameContent.KFPawn_ZedDAR_Rocket' + AIClassList(AT_HansClot)=class'KFGameContent.KFPawn_ZedHansClot' NonSpawnAIClassList(0)=class'KFGameContent.KFPawn_ZedBloatKingSubspawn' diff --git a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc index ca95379..1db0471 100644 --- a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc +++ b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc @@ -59,6 +59,68 @@ struct ContaminationModeData var ContaminationModeData ContaminationMode; +struct BountyHuntSpecialZedData +{ + var() KFPawn_Monster SpecialZed; + var() vector SpecialZedLastLocation; + var() int MaxHealthReference; + var() int LastThresholdApplied; + var() bool FledFirstTime; + var() KFPlayerController FleeingFrom; + var() int LastAttackTime; + var() int LastAttackCanFinishSeconds; + var() int LastBlockingVolumeFleeTime; + var() float FleeMovementDelta; + var() int LastFleeMovementDeltaCheckTime; + var() int LastWakeupTime; + + structdefaultproperties + { + SpecialZed = none + MaxHealthReference = 0.f + LastThresholdApplied = -1 + FledFirstTime = false + FleeingFrom = none + LastAttackTime = 0 + LastAttackCanFinishSeconds = 0 + LastBlockingVolumeFleeTime = 0 + FleeMovementDelta = 0.f + LastFleeMovementDeltaCheckTime = 0 + LastWakeupTime = 0 + } +}; + +struct BountyHuntSpawnVolumeData +{ + var() KFSpawnVolume SpawnVolume; + var() float DistanceToPlayers; +}; + +struct BountyHuntData +{ + var() int MaxNumberOfSpecialZeds; + var() int SpawnedNumberOfSpecialZeds; + var() int NumDead; + var() int CurrentDosh; + var() int CurrentDoshNoAssist; + var() array SpecialZedsData; + var() bool NextSpawnIsBounty; + var() int NumberOfPlayers; + var() bool IsOnLastLevel; + + structdefaultproperties + { + MaxNumberOfSpecialZeds = 0 + SpawnedNumberOfSpecialZeds = 0 + NumDead = 0 + NextSpawnIsBounty = false + NumberOfPlayers = 0 + IsOnLastLevel = false + } +}; + +var BountyHuntData BountyHunt; + //----------------------------------------------------------------------------- // Statics static event class SetGameType(string MapName, string Options, string Portal) @@ -112,6 +174,7 @@ event PreBeginPlay() if (Role == Role_Authority && MyKFGRI != none) { MyKFGRI.NotifyWeeklyEventIndex(ActiveEventIdx); + if ( OutbreakEvent.ActiveEvent.bUnlimitedWeaponPickups) { MyKFGRI.NotifyBrokenTrader(); @@ -136,6 +199,8 @@ function CreateOutbreakEvent() // The beginning of time to reset the loop can be changed in UKFGameEngine::UpdateTimedGameEvents local KFGameEngine KGE; + local string LocalURL; + local int ReadWeeklySelectorIndex; super.CreateOutbreakEvent(); @@ -144,7 +209,26 @@ function CreateOutbreakEvent() { ActiveEventIdx = KGE.GetWeeklyEventIndex() % OutbreakEvent.SetEvents.Length; } - ActiveEventIdx = OutbreakEvent.SetActiveEvent(ActiveEventIdx); + + LocalURL = WorldInfo.GetLocalURL(); + ReadWeeklySelectorIndex = -1; + + LocalURL = Split(LocalURL, "?"); + + ReadWeeklySelectorIndex = GetIntOption(LocalURL, "WeeklySelectorIndex", ReadWeeklySelectorIndex); + + if (ReadWeeklySelectorIndex != -1) + { + WeeklySelectorIndex = ReadWeeklySelectorIndex; + } + + // This will apply WeeklySelectorIndex + ActiveEventIdx = OutbreakEvent.SetActiveEvent(ActiveEventIdx, self); + + if (Role == Role_Authority && MyKFGRI != none) + { + MyKFGRI.NotifyWeeklyEventIndex(ActiveEventIdx); + } } function bool UsesModifiedDifficulty() @@ -184,7 +268,7 @@ function SetPickupItemList() local KFPickupFactory_Item ItemFactory; local int Idx; - if (MyKFGRI.IsGunGameMode()) + if (MyKFGRI != none && MyKFGRI.IsGunGameMode()) { foreach AllActors(class'KFPickupFactory_Item', ItemFactory) { @@ -316,6 +400,16 @@ function float GetAdjustedAIDoshValue( class MonsterClass ) protected function ScoreMonsterKill( Controller Killer, Controller Monster, KFPawn_Monster MonsterPawn ) { + if (MyKFGRI.IsBountyHunt()) + { + if (MonsterPawn.bIsBountyHuntObjective) + { + BountyHuntScoreAfterKilling(MonsterPawn, Killer); + + return; + } + } + super.ScoreMonsterKill(Killer, Monster, MonsterPawn); if(OutbreakEvent.ActiveEvent.bHealAfterKill) @@ -457,6 +551,75 @@ function GunGameScoreAssistanceAfterKilling(KFPawn_Monster MonsterPawn , Control } } +function BountyHuntScoreAfterKilling(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; + + `Log("Killed Bounty : " $MonsterPawn); + + KFPC_WS = KFPlayerController_WeeklySurvival(Killer); + if (KFPC_WS != none) + { + Attackers.AddItem(KFPC_WS); + + `Log("Killed Bounty, Extra Dosh Given To (Killer) : " $Killer); + + KFPlayerReplicationInfo(Killer.PlayerReplicationInfo).AddDosh(BountyHunt.CurrentDosh, true); + } + + 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) + { + if (KFPC_WS.Pawn.Health > 0) + { + Attackers.AddItem(KFPC_WS); + + `Log("Killed Bounty, Extra Dosh Given To (Assistance) : " $KFPC_WS); + + KFPlayerReplicationInfo(KFPC_WS.PlayerReplicationInfo).AddDosh(BountyHunt.CurrentDosh, true); + } + } + } + } + } + } + + // Give also dosh to players that didn't do a thing + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + if (KFPC_WS.IsInState('Spectating') == false + && KFPC_WS.PlayerReplicationInfo.bOnlySpectator == false) + { + if (Attackers.Find(KFPC_WS) < 0) + { + Attackers.AddItem(KFPC_WS); + + `Log("Killed Bounty, Extra Dosh Given To (No Assistance) : " $KFPC_WS); + + KFPlayerReplicationInfo(KFPC_WS.PlayerReplicationInfo).AddDosh(BountyHunt.CurrentDoshNoAssist, true); + } + } + } +} + function StartMatch() { super.StartMatch(); @@ -576,6 +739,11 @@ function Tick(float DeltaTime) UpdateContaminationModeTrader(); } } + + if (MyKFGRI.IsBountyHunt()) + { + UpdateBountyHunt(DeltaTime); + } } function TickZedTime( float DeltaTime ) @@ -670,6 +838,20 @@ function GrantExtraDoshOnWaveWon() } } } + + + if (MyKFGRI.IsBountyHunt()) + { + ExtraDosh = MyKFGRI.BountyHuntExtraDosh(); + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + if (KFPC.IsInState('Spectating') == false + && KFPC.PlayerReplicationInfo.bOnlySpectator == false) + { + KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo).AddDosh(ExtraDosh, true); + } + } + } } function ClearZedTimePCTimers() @@ -701,6 +883,9 @@ function EndOfMatch(bool bVictory) function StartWave() { + local int BountyHuntIt, BountyHuntNumPlayerIt, i; + local KFPlayerController_WeeklySurvival KFPC_WS; + super.StartWave(); // Stop Global Damage for boss wave @@ -759,6 +944,107 @@ function StartWave() ContaminationMode.ObjectiveHidden = false; ContaminationMode.CanUpdate = true; } + + if (MyKFGRI.IsBountyHunt()) + { + BountyHunt.MaxNumberOfSpecialZeds = 0; + BountyHunt.SpawnedNumberOfSpecialZeds = 0; + BountyHunt.NumDead = 0; + BountyHunt.NumberOfPlayers = 0; + BountyHunt.SpecialZedsData.Remove(0, BountyHunt.SpecialZedsData.Length); + BountyHunt.IsOnLastLevel = false; + BountyHunt.NextSpawnIsBounty = false; + + if (WaveNum != WaveMax) + { + BountyHuntIt = 0; + + // Update to the current level + for (BountyHuntIt = 0; BountyHuntIt < OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDataWaves.Length; ++BountyHuntIt) + { + if (WaveNum == OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDataWaves[BountyHuntIt].Wave) + { + break; + } + } + + if (BountyHuntIt == OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDataWaves.Length) + { + BountyHuntIt = OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDataWaves.Length - 1; + } + + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + if (KFPC_WS.IsInState('Spectating') == false + && KFPC_WS.PlayerReplicationInfo.bOnlySpectator == false) + { + BountyHunt.NumberOfPlayers += 1; + } + } + + for (BountyHuntNumPlayerIt = 0; BountyHuntNumPlayerIt < OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDataWaves[BountyHuntIt].BountyHuntWavePerPlayerZed.Length; ++BountyHuntNumPlayerIt) + { + if (BountyHunt.NumberOfPlayers == OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDataWaves[BountyHuntIt].BountyHuntWavePerPlayerZed[BountyHuntNumPlayerIt].NumberOfPlayers) + { + break; + } + } + + if (BountyHuntNumPlayerIt == OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDataWaves[BountyHuntIt].BountyHuntWavePerPlayerZed.Length) + { + BountyHuntNumPlayerIt = OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDataWaves[BountyHuntIt].BountyHuntWavePerPlayerZed.Length - 1; + } + + BountyHunt.MaxNumberOfSpecialZeds = OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDataWaves[BountyHuntIt].BountyHuntWavePerPlayerZed[BountyHuntNumPlayerIt].NumberOfZeds; + + BountyHunt.SpawnedNumberOfSpecialZeds = 0; + + // Update to the current level + for (BountyHuntIt = 0; BountyHuntIt < OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDosh.Length; ++BountyHuntIt) + { + if (BountyHunt.NumberOfPlayers == OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDosh[BountyHuntIt].NumberOfPlayers) + { + break; + } + } + + if (BountyHuntIt == OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDosh.Length) + { + BountyHuntIt = OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDosh.Length - 1; + } + + BountyHunt.CurrentDosh = OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDosh[BountyHuntIt].Dosh; + BountyHunt.CurrentDoshNoAssist = OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntDosh[BountyHuntIt].DoshNoAssist; + + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + if (KFPC_WS.Pawn.IsAliveAndWell() == false + || KFPC_WS.PlayerReplicationInfo.bOnlySpectator + || KFPC_WS.IsInState('Spectating')) + { + continue; + } + + KFPC_WS.DisplayBountyHuntObjective(BountyHunt.MaxNumberOfSpecialZeds); + } + + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + KFPC_WS.DisplayBountyHuntStatus(0, BountyHunt.MaxNumberOfSpecialZeds, 0, BountyHunt.CurrentDosh, BountyHunt.CurrentDoshNoAssist); + } + + //`Log("BountyHunt.MaxNumberOfSpecialZeds : " $BountyHunt.MaxNumberOfSpecialZeds); + + // Spawn one Bounty Hunt per Player first., respecting BountyHuntMaxCoexistingZeds + for (i = 0; i < BountyHunt.NumberOfPlayers; ++i) + { + if (BountyHunt.SpecialZedsData.Length < OutbreakEvent.ActiveEvent.BountyHuntMaxCoexistingZeds) + { + SpawnBountyZed(); + } + } + } + } } function bool OverridePickupList() @@ -1214,12 +1500,27 @@ function NotifyKilled(Controller Killer, Controller Killed, Pawn KilledPawn, cla local KFPawn_Monster KFPM; local KFPlayerController_WeeklySurvival KFPC_WS_Killer, KFPC_WS_Killed; - super.NotifyKilled(Killer, Killed, KilledPawn, damageType); - KFPM = KFPawn_Monster(KilledPawn); KFPC_WS_Killer = KFPlayerController_WeeklySurvival(Killer); KFPC_WS_Killed = KFPlayerController_WeeklySurvival(Killed); + if (MyKFGRI.IsBountyHunt()) + { + // Special case when we kill last Zed on the wave and there are still Bounty to spawn.. + if (KFPM != none && KFPM.bIsBountyHuntObjective) + { + if (MyKFGRI.AIRemaining == 1) + { + if (BountyHunt.SpawnedNumberOfSpecialZeds < BountyHunt.MaxNumberOfSpecialZeds) + { + SpawnBountyZed(); + } + } + } + } + + super.NotifyKilled(Killer, Killed, KilledPawn, damageType); + if (OutbreakEvent.ActiveEvent.bGunGameMode) { // If pawn is monster increase gun game score for that monster @@ -1649,7 +1950,7 @@ simulated function RandomPerkWaveStarted() { if (KFPC_WS.InitialRandomPerk == 255) { - `Log("PLAYER - RandomPerkWaveStart : " $KFPC_WS); + //`Log("PLAYER - RandomPerkWaveStart : " $KFPC_WS); ChooseInitialRandomPerk(KFPC_WS); } @@ -2090,6 +2391,674 @@ function UpdateContaminationMode(float DeltaTime) } } +/* + * Weekly 20: Bounty Hunt + */ + +simulated function int WeeklyCurrentExtraNumberOfZeds() +{ + if (MyKFGRI.IsBountyHunt()) + { + return BountyHunt.SpecialZedsData.Length; + } + + return super.WeeklyCurrentExtraNumberOfZeds(); +} + +function SetMonsterDefaults( KFPawn_Monster Monster ) +{ + local BountyHuntSpecialZedData SpecialZedData; + local int i; + + super.SetMonsterDefaults(Monster); + + if (BountyHunt.NextSpawnIsBounty == false) + { + return; + } + + if (Role == Role_Authority && MyKFGRI != none) + { + if (MyKFGRI.IsBountyHunt()) + { + if (BountyHunt.SpawnedNumberOfSpecialZeds < BountyHunt.MaxNumberOfSpecialZeds) + { + for (i = 0; i < OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression.Length; ++i) + { + if (OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[i].ZedType == Monster.Class) + { + ++BountyHunt.SpawnedNumberOfSpecialZeds; + + Monster.SetBountyHuntObjective(); + + SpecialZedData.SpecialZed = Monster; + SpecialZedData.MaxHealthReference = Monster.HealthMax; + + BountyHunt.SpecialZedsData.AddItem(SpecialZedData); + + //`Log("Monster Converted to Bounty Target : " $Monster); + //`Log("SpawnedNumberOfSpecialZeds : " $BountyHunt.SpawnedNumberOfSpecialZeds); + //`Log("MaxNumberOfSpecialZeds : " $BountyHunt.MaxNumberOfSpecialZeds); + //`Log("NumDead : " $BountyHunt.NumDead); + + //`Log("Monster . HealthMax : " $Monster.HealthMax); + //`Log("Monster . Health : " $Monster.Health); + + Monster.HealthMax += Monster.HealthMax * OutbreakEvent.ActiveEvent.BountyHuntSpecialZedBuffHealthRatio; + Monster.Health += Monster.Health * OutbreakEvent.ActiveEvent.BountyHuntSpecialZedBuffHealthRatio; + + //`Log("Monster . HealthMax : " $Monster.HealthMax); + //`Log("Monster . Health : " $Monster.Health); + + //`Log("-------"); + + KFAIController(Monster.Controller).BeginCombatCommand(KFAIController(Monster.Controller).GetDefaultCommand(), "Restarting default command", true); + + // THIS WAKES HIM UP !!! + Monster.TakeDamage(0, none, vect(0,0,0), vect(0,0,0), none); + + break; + } + } + } + } + } +} + +delegate int SortSpawnVolumesDelegate(BountyHuntSpawnVolumeData A, BountyHuntSpawnVolumeData B) +{ + if (A.DistanceToPlayers == B.DistanceToPlayers) + { + return 0; + } + + if (A.DistanceToPlayers < B.DistanceToPlayers) + { + return -1; + } + + return 1; +} + +function SpawnBountyZed() +{ + local KFSpawnVolume SpawnVolume; + local array SpawnVolumeDataList; + local BountyHuntSpawnVolumeData SpawnVolumeData; + local int RangeRandom; + local class ZedToAdd; + local array< class > AvailableZeds; + local array< class > SpawnList; + local int i, j, RandNumber, VolumeIndex; + local KFAISpawnManager AISpawnManager; + local KFPlayerController KFPC; + local bool CheckForSpawnVolumeInSpecialMap; + + BountyHunt.NextSpawnIsBounty = true; + + for (i = 0; i < OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression.Length; ++i) + { + for (j = 0; j < OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[i].BountyHuntSpecialZedPerWave.Length; ++j) + { + if (OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[i].BountyHuntSpecialZedPerWave[j].Wave == WaveNum) + { + ZedToAdd = OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[i].ZedType; + + AvailableZeds.AddItem(ZedToAdd); + } + } + } + + if (AvailableZeds.Length > 0) + { + RandNumber = Rand(AvailableZeds.Length); + SpawnList.AddItem(AvailableZeds[RandNumber]); + + AISpawnManager = KFGameInfo(WorldInfo.Game).SpawnManager; + + // Choose SpawnVolume + + // Check for BOSS type only on some maps (where the points are far unreachable) + + CheckForSpawnVolumeInSpecialMap = WorldInfo.GetMapName() == "Airship" + || WorldInfo.GetMapName() == "CastleVolter" + || WorldInfo.GetMapName() == "Halloween 2023"; + + for ( VolumeIndex = 0; VolumeIndex < AISpawnManager.SpawnVolumes.Length; VolumeIndex++ ) + { + SpawnVolume = AISpawnManager.SpawnVolumes[VolumeIndex]; + + // Explicitly deactivated for the map + if (SpawnVolume.bDisableForBountyHuntSpawn) + { + continue; + } + + // Select which ones players can't be seen + if (SpawnVolume.IsVisible(false)) + { + continue; + } + + // Special check for maps + if (CheckForSpawnVolumeInSpecialMap) + { + if (SpawnVolume.LargestSquadType == EST_Boss) + { + continue; + } + } + + SpawnVolumeData.SpawnVolume = SpawnVolume; + SpawnVolumeData.DistanceToPlayers = 0.f; + + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + SpawnVolumeData.DistanceToPlayers += VSize(KFPC.Pawn.Location - SpawnVolume.Location); + } + + SpawnVolumeDataList.AddItem(SpawnVolumeData); + } + + // Order by distance + SpawnVolumeDataList.Sort(SortSpawnVolumesDelegate); + + SpawnVolume = none; + + // Select one that's more or less halfway all players + if (SpawnVolumeDataList.Length > 0) + { + if (SpawnVolumeDataList.Length == 1) + { + SpawnVolume = SpawnVolumeDataList[0].SpawnVolume; + } + else + { + // Make sure doesn't go over range... + + RangeRandom = SpawnVolumeDataList.Length * 0.5f; + + RandNumber = RangeRandom + Rand(RangeRandom); + + if (RandNumber < SpawnVolumeDataList.Length) + { + SpawnVolume = SpawnVolumeDataList[RandNumber].SpawnVolume; + } + else + { + SpawnVolume = SpawnVolumeDataList[SpawnVolumeDataList.Length - 1].SpawnVolume; + } + } + } + + // Fallback + if (SpawnVolume == none) + { + SpawnVolume = SpawnManager.GetBestSpawnVolume(SpawnList, , , True ); + } + + SpawnVolume.SpawnWave(SpawnList, false); + } + + BountyHunt.NextSpawnIsBounty = false; +} + +function UpdateBountyHunt(float DeltaTime) +{ + local KFPawn_Monster Monster; + local KFAIController MonsterAI; + local KFPlayerController KFPC, KFPC_ToFleeFrom; + local KFPlayerController_WeeklySurvival KFPC_WS; + local AICommand_SpecialMove AICSM; + local float WaveProgress, DistanceToPlayer; + local int i, BountyHuntZedIt, BountyHuntIt, NewMaxHealth, CurrentHealthIncrease, BaseHealthUpgrade; + local vector HitLocation, HitNormal, Destination; + local Actor HitActor; + local bool CanSpawnByLimit; + + if (MyKFGRI.bWaveIsActive && WaveNum != WaveMax) + { + if (MyKFGRI.WaveTotalAICount > 0) + { + // Don't count current alive Bounty Zeds + WaveProgress = float(MyKFGRI.AIRemaining - WeeklyCurrentExtraNumberOfZeds()) / float(MyKFGRI.WaveTotalAICount); + } + else + { + WaveProgress = 0.f; + } + + // Clean up dead ones + + for (i = BountyHunt.SpecialZedsData.Length - 1; i >= 0; --i) + { + Monster = BountyHunt.SpecialZedsData[i].SpecialZed; + + if (Monster.IsAliveAndWell() == false) + { + ++BountyHunt.NumDead; + BountyHunt.SpecialZedsData.Remove(i, 1); + } + } + + // Update behaviour + + for (i = 0 ; i < BountyHunt.SpecialZedsData.Length; ++i) + { + Monster = BountyHunt.SpecialZedsData[i].SpecialZed; + + // Find first node of data for the Zed type we checking,. + for (BountyHuntZedIt = 0 ; BountyHuntZedIt < OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression.Length; ++BountyHuntZedIt) + { + if (OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].ZedType == Monster.class) + { + break; + } + } + + // Update to the current level + for (BountyHuntIt = 0 ; BountyHuntIt < OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression.Length ; ++BountyHuntIt) + { + if (WaveProgress >= OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression[BountyHuntIt].RemainingZedRatio) + { + break; + } + } + + if (BountyHuntIt == OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression.Length) + { + BountyHuntIt = OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression.Length - 1; + } + + if (Monster.Controller != none) + { + MonsterAI = KFAIController(Monster.Controller); + + // Manger flee AI.. + + if (MonsterAI != none) + { + //`Log("STATE NAME : "$MonsterAI.GetStateName()); + + // Wak up call every 3 seconds, in case AI is not reacting to default behaviour... + if (WorldInfo.TimeSeconds > BountyHunt.SpecialZedsData[i].LastWakeupTime + 3.f) + { + BountyHunt.SpecialZedsData[i].LastWakeupTime = WorldInfo.TimeSeconds; + + // THIS WAKES HIM UP !!! + Monster.TakeDamage(0, none, vect(0,0,0), vect(0,0,0), none); + } + + // Don't assign orders on last threshold level.. + if (BountyHunt.SpecialZedsData[i].LastThresholdApplied == OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression.Length - 1) + { + // NOTHING..., the state change is managed below, it cancels orders and assigns default order + } + else + { + // IF we are not fleeing + if (MonsterAI.FindCommandOfClass(class'AICommand_Flee') == none) + { + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + // Check for distance and visibility.. + + DistanceToPlayer = VSize(KFPC.Pawn.Location - Monster.Location); + //`Log("DistanceToPlayer : " $DistanceToPlayer); + + // The first time we flee we need to be super close to player to flee from + // Then we deactivate crossing Blocking Volumes, so it's somehow guaranteed + // The Zed is inside the playable area, and can't leave anymore + if (BountyHunt.SpecialZedsData[i].FledFirstTime == false) + { + if (DistanceToPlayer < OutbreakEvent.ActiveEvent.BountyHuntDistancePlayerMinFirstFlee) + { + KFPC_ToFleeFrom = KFPC; + break; + } + } + else + { + if (DistanceToPlayer < OutbreakEvent.ActiveEvent.BountyHuntDistancePlayerMinFlee) + { + if (OutbreakEvent.ActiveEvent.BountyHuntNeedsToSeePlayerToTriggerFlee == false + || MonsterAI.CanSee(KFPC.Pawn)) + { + KFPC_ToFleeFrom = KFPC; + break; + } + } + } + } + + if (BountyHunt.SpecialZedsData[i].FledFirstTime == false) + { + Monster.bIsSprinting = true; + } + + // IF we have a valid player to Flee From and Timer from last attack can cancel is okay flee again + if (KFPC_ToFleeFrom != none + && WorldInfo.TimeSeconds > BountyHunt.SpecialZedsData[i].LastAttackTime + + BountyHunt.SpecialZedsData[i].LastAttackCanFinishSeconds) + { + Monster.SetBountyHuntBlockLeaveMap(false); + + BountyHunt.SpecialZedsData[i].FledFirstTime = true; + BountyHunt.SpecialZedsData[i].FleeingFrom = KFPC_ToFleeFrom; + BountyHunt.SpecialZedsData[i].FleeMovementDelta = 0.f; + BountyHunt.SpecialZedsData[i].LastFleeMovementDeltaCheckTime = WorldInfo.TimeSeconds; + + AICSM = MonsterAI.FindCommandOfClass( class'AICommand_SpecialMove' ); + if( AICSM != none ) + { + AICSM.ClearTimeout(); + } + + // Abort all commands + MonsterAI.AbortCommand(MonsterAI.CommandList); + + Monster.bIsSprinting = true; + + //`Log("FLEE!!! : "$Monster); + + MonsterAI.DoFleeFrom(KFPC_ToFleeFrom, OutbreakEvent.ActiveEvent.BountyHuntTimeBetweenFlee, 50000.f, false, false, true); + } + else if (BountyHunt.SpecialZedsData[i].FledFirstTime && MonsterAI.CommandList == none) + { + // IF not we reset combat if there is no commands + + BountyHunt.SpecialZedsData[i].FleeingFrom = none; + + BountyHunt.SpecialZedsData[i].LastAttackTime = WorldInfo.TimeSeconds; + BountyHunt.SpecialZedsData[i].LastAttackCanFinishSeconds = OutbreakEvent.ActiveEvent.BountyHuntTimeCanCancelAttack; + + //`Log("Restarting Combat on (NO ORDER) !!! : "$Monster); + //`Log("Restarting Combat on (NO ORDER) -> Issue command: : " $MonsterAI.GetDefaultCommand()); + + MonsterAI.BeginCombatCommand(MonsterAI.GetDefaultCommand(), "Restarting default command", true); + + // THIS WAKES HIM UP !!! + Monster.TakeDamage(0, none, vect(0,0,0), vect(0,0,0), none); + } + } + else + { + // IF we are fleeing + + Monster.bIsSprinting = true; + + if (BountyHunt.SpecialZedsData[i].FleeingFrom != none) + { + DistanceToPlayer = VSize(BountyHunt.SpecialZedsData[i].FleeingFrom.Pawn.Location - Monster.Location); + + //`Log("(FLEEING) DistanceToPlayer : " $DistanceToPlayer); + + // IF fleeing we cancel when distance to player is above max + if (DistanceToPlayer >= OutbreakEvent.ActiveEvent.BountyHuntDistancePlayerMaxFlee) + { + BountyHunt.SpecialZedsData[i].FleeingFrom = none; + + BountyHunt.SpecialZedsData[i].LastAttackTime = WorldInfo.TimeSeconds; + BountyHunt.SpecialZedsData[i].LastAttackCanFinishSeconds = OutbreakEvent.ActiveEvent.BountyHuntTimeCanCancelAttack; + + MonsterAI.AbortCommand(MonsterAI.FindCommandOfClass(class'AICommand_Flee')); + + // End flee as normal + MonsterAI.NotifyFleeFinished(true); + + //`Log("Restarting Combat on (DISTANCE) !!! : "$Monster); + + MonsterAI.BeginCombatCommand(MonsterAI.GetDefaultCommand(), "Restarting default command", true); + + // THIS WAKES HIM UP !!! + Monster.TakeDamage(0, none, vect(0,0,0), vect(0,0,0), none); + } + } + + // IF we still fleeing.. + if (BountyHunt.SpecialZedsData[i].FleeingFrom != none) + { + // We cancel when distance to any player is below allowed for Attack + if (WorldInfo.TimeSeconds > BountyHunt.SpecialZedsData[i].LastAttackTime + + BountyHunt.SpecialZedsData[i].LastAttackCanFinishSeconds) + { + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + DistanceToPlayer = VSize(KFPC.Pawn.Location - Monster.Location); + //`Log("DistanceToPlayer : " $DistanceToPlayer); + + if (DistanceToPlayer < OutbreakEvent.ActiveEvent.BountyHuntDistancePlayerAttack) + { + BountyHunt.SpecialZedsData[i].FleeingFrom = none; + + BountyHunt.SpecialZedsData[i].LastAttackTime = WorldInfo.TimeSeconds; + BountyHunt.SpecialZedsData[i].LastAttackCanFinishSeconds = OutbreakEvent.ActiveEvent.BountyHuntTimeCanCancelAttack; + + MonsterAI.AbortCommand(MonsterAI.FindCommandOfClass(class'AICommand_Flee')); + + // End flee as normal + MonsterAI.NotifyFleeFinished(true); + + //`Log("Restarting Combat on (DISTANCE CLOSE TO PLAYER) !!! : "$Monster); + + MonsterAI.BeginCombatCommand(MonsterAI.GetDefaultCommand(), "Restarting default command", true); + + // THIS WAKES HIM UP !!! + Monster.TakeDamage(0, none, vect(0,0,0), vect(0,0,0), none); + + break; + } + } + } + } + + // Check for accumulation of delta move to improve places where Bounty is stuck + BountyHunt.SpecialZedsData[i].FleeMovementDelta += VSize(Monster.Location - BountyHunt.SpecialZedsData[i].SpecialZedLastLocation); + //`Log("FleeMovementDelta : "$BountyHunt.SpecialZedsData[i].FleeMovementDelta); + + // Every second we check for movement + if (WorldInfo.TimeSeconds > BountyHunt.SpecialZedsData[i].LastFleeMovementDeltaCheckTime + 1.f) + { + // If we moved less than 1,5 meters ATTACK for 3 seconds ! + if (BountyHunt.SpecialZedsData[i].FleeMovementDelta < 150.f) + { + BountyHunt.SpecialZedsData[i].FleeingFrom = none; + + BountyHunt.SpecialZedsData[i].LastAttackTime = WorldInfo.TimeSeconds; + BountyHunt.SpecialZedsData[i].LastAttackCanFinishSeconds = 3.f; + + // Cancel FLEE if any.. + if (MonsterAI.FindCommandOfClass(class'AICommand_Flee') != none) + { + MonsterAI.AbortCommand(MonsterAI.FindCommandOfClass(class'AICommand_Flee')); + + // End flee as normal + MonsterAI.NotifyFleeFinished(true); + } + + //`Log("Restarting Combat on (BOUNTY ZED BLOCKED) !!! : "$Monster); + + MonsterAI.BeginCombatCommand(MonsterAI.GetDefaultCommand(), "Restarting default command", true); + + // THIS WAKES HIM UP !!! + Monster.TakeDamage(0, none, vect(0,0,0), vect(0,0,0), none); + + Monster.bIsSprinting = true; + } + + BountyHunt.SpecialZedsData[i].FleeMovementDelta = 0.f; + BountyHunt.SpecialZedsData[i].LastFleeMovementDeltaCheckTime = WorldInfo.TimeSeconds; + } + + // IF we still fleeing.. + if (BountyHunt.SpecialZedsData[i].FleeingFrom != none) + { + // Check for blocking volume in runtime, we do something similar on AICommand_Flee + // but we need more frequency + if (WorldInfo.TimeSeconds > BountyHunt.SpecialZedsData[i].LastBlockingVolumeFleeTime + 0.5f) + { + BountyHunt.SpecialZedsData[i].LastBlockingVolumeFleeTime = WorldInfo.TimeSeconds; + + Destination = Monster.Location + Normal(Monster.Velocity) * 300.f; + + HitActor = Trace(HitLocation, HitNormal, Destination, Monster.Location, true,,, TRACEFLAG_Blocking); + if (HitActor != none && KFPawnBlockingVolume(HitActor) != none) + { + //`Log("BLOCKING VOLUME FOUND!!! : " $HitActor); + + Monster.SetBountyHuntBlockLeaveMap(false); + + BountyHunt.SpecialZedsData[i].FleeMovementDelta = 0.f; + BountyHunt.SpecialZedsData[i].LastFleeMovementDeltaCheckTime = WorldInfo.TimeSeconds; + + MonsterAI.AbortCommand(MonsterAI.CommandList); + + //`Log("FLEE!!! : "$Monster); + + MonsterAI.DoFleeFrom(BountyHunt.SpecialZedsData[i].FleeingFrom, OutbreakEvent.ActiveEvent.BountyHuntTimeBetweenFlee, 50000.f, false, false, true); + + Monster.bIsSprinting = true; + } + } + } + } + } + + BountyHunt.SpecialZedsData[i].SpecialZedLastLocation = Monster.Location; + } + } + + // Apply On Change Level Buffs.. + + if (BountyHunt.SpecialZedsData[i].LastThresholdApplied != BountyHuntIt) + { + BountyHunt.SpecialZedsData[i].LastThresholdApplied = BountyHuntIt; + + //`Log("Monster : " $Monster); + //`Log("Level Change : " $BountyHuntIt); + //`Log("WaveProgress : " $WaveProgress); + + //`Log("Monster . HealthMax : " $Monster.HealthMax); + //`Log("Monster . MaxHealthReference : " $BountyHunt.SpecialZedsData[i].MaxHealthReference); + //`Log("Monster . Health : " $Monster.Health); + + NewMaxHealth = BountyHunt.SpecialZedsData[i].MaxHealthReference; + + BaseHealthUpgrade = BountyHunt.SpecialZedsData[i].MaxHealthReference * OutbreakEvent.ActiveEvent.BountyHuntSpecialZedBuffHealthRatio; + + NewMaxHealth += BaseHealthUpgrade; + NewMaxHealth += BountyHunt.SpecialZedsData[i].MaxHealthReference * OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression[BountyHuntIt].HealthBuffRatio; + + CurrentHealthIncrease = NewMaxHealth - Monster.HealthMax; + + Monster.HealthMax = NewMaxHealth; + Monster.Health += CurrentHealthIncrease; + + Monster.Health = Clamp(Monster.Health, 0, Monster.HealthMax); + + //`Log("Monster . HealthMax : " $Monster.HealthMax); + //`Log("Monster . Health : " $Monster.Health); + + //`Log("Last Level Check (1) : " $BountyHuntIt); + //`Log("Last Level Check (2) : " $OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression.Length - 1); + + if (BountyHuntIt == OutbreakEvent.ActiveEvent.BountyHuntGame.BountyHuntZedAndProgression[BountyHuntZedIt].BountyHuntZedProgression.Length - 1) + { + //`Log("SET Last Level!"); + + BountyHunt.IsOnLastLevel = true; + + if (Monster.bIsBountyHuntOnLastTier == false) + { + Monster.SetBountyHuntOnLastTier(); + } + + // Cancel FLEE if any.. + if (MonsterAI.FindCommandOfClass(class'AICommand_Flee') != none) + { + //`Log("STOP FLEE!!! : "$Monster); + + MonsterAI.AbortCommand(MonsterAI.FindCommandOfClass(class'AICommand_Flee')); + + // End flee as normal + MonsterAI.NotifyFleeFinished(true); + } + + //`Log("Restarting Combat on !!! : "$Monster); + + MonsterAI.BeginCombatCommand(MonsterAI.GetDefaultCommand(), "Restarting default command", true); + + // THIS WAKES HIM UP !!! + Monster.TakeDamage(0, BountyHunt.SpecialZedsData[i].FleeingFrom, vect(0,0,0), vect(0,0,0), none); + + Monster.bIsSprinting = true; + + BountyHunt.SpecialZedsData[i].FleeingFrom = none; + } + + //`Log("-------"); + } + } + + // Update UI.. + + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + KFPC_WS.DisplayBountyHuntStatus(BountyHunt.SpecialZedsData.Length, BountyHunt.MaxNumberOfSpecialZeds, BountyHunt.NumDead + , BountyHunt.CurrentDosh, BountyHunt.CurrentDoshNoAssist); + } + + // Update Bounty Zeds to spawn.. + + if (BountyHunt.SpawnedNumberOfSpecialZeds < BountyHunt.MaxNumberOfSpecialZeds) + { + // Hard limit of max coexisting.. + + CanSpawnByLimit = false; + + if (BountyHunt.SpecialZedsData.Length < OutbreakEvent.ActiveEvent.BountyHuntMaxCoexistingZeds) + { + CanSpawnByLimit = true; + } + else if (OutbreakEvent.ActiveEvent.BountyHuntLastLevelStillUsesCoexistingZeds == false) + { + CanSpawnByLimit = BountyHunt.IsOnLastLevel; + } + + if (CanSpawnByLimit) + { + if (OutbreakEvent.ActiveEvent.BountyHuntUseGradualSpawn) + { + if (BountyHunt.SpecialZedsData.Length < (1 * BountyHunt.NumberOfPlayers) && WaveProgress <= 0.75f) + { + SpawnBountyZed(); + } + + if (WaveNum >= 3 && WaveNum <= 5) + { + if (BountyHunt.SpecialZedsData.Length < (2 * BountyHunt.NumberOfPlayers) && WaveProgress <= 0.5f) + { + SpawnBountyZed(); + } + } + + if (WaveNum > 5) + { + if (BountyHunt.SpecialZedsData.Length < (3 * BountyHunt.NumberOfPlayers) && WaveProgress <= 0.25f) + { + SpawnBountyZed(); + } + } + } + else + { + SpawnBountyZed(); + } + } + } + } +} + // defaultproperties diff --git a/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc b/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc index 9affe4a..85ef86a 100644 --- a/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc +++ b/KFGameContent/Classes/KFGameReplicationInfo_WeeklySurvival.uc @@ -68,7 +68,9 @@ function ChooseNextObjective(int NextWaveNum) if (IsContaminationMode() == false) { - super.ChooseNextObjective(NextWaveNum); + super.ChooseNextObjective(NextWaveNum); + + return; } KFMI = KFMapInfo(WorldInfo.GetMapInfo()); diff --git a/KFGameContent/Classes/KFMapObjective_AreaDefense.uc b/KFGameContent/Classes/KFMapObjective_AreaDefense.uc index b69d947..4a4049d 100644 --- a/KFGameContent/Classes/KFMapObjective_AreaDefense.uc +++ b/KFGameContent/Classes/KFMapObjective_AreaDefense.uc @@ -200,6 +200,11 @@ simulated function bool CanActivateObjectiveByWeekly() { if (KFGameInfo(WorldInfo.Game).OutbreakEvent != none) { + if (KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bBossRushMode) + { + return false; + } + if (KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bGunGameMode) { return false; @@ -209,18 +214,33 @@ simulated function bool CanActivateObjectiveByWeekly() { return false; } + + if (KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bBountyHunt) + { + return false; + } } } else { if (KFGameReplicationInfo(WorldInfo.GRI) != none && KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode) { + if (KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 14) + { + return false; + } + if (KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 16) { return false; } if (KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 17) + { + return false; + } + + if (KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 20) { return false; } diff --git a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc index f5e6581..ff3ad76 100644 --- a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc +++ b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc @@ -1247,9 +1247,183 @@ defaultproperties (SpawnEntry=AT_Bloat,NewClass=(class'KFGameContent.KFPawn_ZedDAR_Rocket'),PercentChance=0.2) )} - - )} + + // Bounty Hunt + SetEvents[20]={( + EventDifficulty=2, + GameLength=GL_Normal, + OverrideAmmoPickupModifier=2.0f, + WaveAICountScale=(0.8f, 0.7f, 0.6f, 0.6f, 0.5f, 0.5f), // This is per player-count, if more players than slots uses last value + bBountyHunt=true, + BountyHuntExtraDosh=200, + + // Navigation parameters + BountyHuntNeedsToSeePlayerToTriggerFlee=false, //DEFAULT: false + BountyHuntTimeBetweenFlee=10.f, //DEFAULT: 10f // (seconds) Time the Flee AI Order is active + BountyHuntTimeBetweenAttack=12.f, //DEFAULT 12f // (seconds) Seconds between a new Attack Order can be issued using proximity to any player (BountyHuntDistancePlayerAttack) + BountyHuntTimeCanCancelAttack=1f, //DEFAULT 5.f // (seconds) Seconds after Attack is issued we can cancel it (to flee if the distance BountyHuntDistancePlayerMinFlee is okay) + BountyHuntDistancePlayerMinFirstFlee=2000.f, //DEFAULT 100f // (cm) Minimal Distance between Player and Zed when Zed decides to Flee first time (then we block crossing Blocking Volumes) + BountyHuntDistancePlayerMinFlee=2000.f, //DEFAULT 1000f // (cm) Minimal Distance between Player and Zed when Zed decides to Flee + BountyHuntDistancePlayerMaxFlee=20000.f, // DEFAULT 2200F // (cm) Distance between Player and Zed when Zed stops Fleeing (use to cancel a Flee) + BountyHuntDistancePlayerAttack=80.f, // DEFAULT 400f // (cm) Distance between Player and Zed when Zed decides to Attack + + // BZ stat parameters + BountyHuntSpecialZedBuffHealthRatio=0.1f, // EXTRA 10% health for all Bounty Zeds + BountyHuntSpecialZedBuffAfflictionResistance=5f, // EXTRA 500% affliction resistance for all Bounty Zeds + HeadshotDamageMultiplier=0.25f, + + BountyHuntMaxCoexistingZeds=3, + BountyHuntLastLevelStillUsesCoexistingZeds=false, + BountyHuntUseGradualSpawn=false, + BountyHuntGame= + {( + BountyHuntDataWaves= // Amount of Zeds is TOTAL, NOT NumberOfZeds per player + {( + (Wave=1 + , BountyHuntWavePerPlayerZed = + {( + (NumberOfPlayers=1, NumberOfZeds=1) + , (NumberOfPlayers=2, NumberOfZeds=2) + , (NumberOfPlayers=3, NumberOfZeds=3) + , (NumberOfPlayers=4, NumberOfZeds=4) + , (NumberOfPlayers=5, NumberOfZeds=5) + , (NumberOfPlayers=6, NumberOfZeds=5) + )}), + (Wave=2 + , BountyHuntWavePerPlayerZed = + {( + (NumberOfPlayers=1, NumberOfZeds=2) + , (NumberOfPlayers=2, NumberOfZeds=3) + , (NumberOfPlayers=3, NumberOfZeds=4) + , (NumberOfPlayers=4, NumberOfZeds=5) + , (NumberOfPlayers=5, NumberOfZeds=6) + , (NumberOfPlayers=6, NumberOfZeds=6) + )}), + (Wave=3 + , BountyHuntWavePerPlayerZed = + {( + (NumberOfPlayers=1, NumberOfZeds=3) + , (NumberOfPlayers=2, NumberOfZeds=4) + , (NumberOfPlayers=3, NumberOfZeds=5) + , (NumberOfPlayers=4, NumberOfZeds=6) + , (NumberOfPlayers=5, NumberOfZeds=7) + , (NumberOfPlayers=6, NumberOfZeds=7) + )}), + (Wave=4 + , BountyHuntWavePerPlayerZed = + {( + (NumberOfPlayers=1, NumberOfZeds=3) + , (NumberOfPlayers=2, NumberOfZeds=5) + , (NumberOfPlayers=3, NumberOfZeds=6) + , (NumberOfPlayers=4, NumberOfZeds=7) + , (NumberOfPlayers=5, NumberOfZeds=7) + , (NumberOfPlayers=6, NumberOfZeds=8) + )}), + (Wave=5 + , BountyHuntWavePerPlayerZed = + {( + (NumberOfPlayers=1, NumberOfZeds=4) + , (NumberOfPlayers=2, NumberOfZeds=6) + , (NumberOfPlayers=3, NumberOfZeds=7) + , (NumberOfPlayers=4, NumberOfZeds=8) + , (NumberOfPlayers=5, NumberOfZeds=8) + , (NumberOfPlayers=6, NumberOfZeds=8) + )}), + (Wave=6 + , BountyHuntWavePerPlayerZed = + {( + (NumberOfPlayers=1, NumberOfZeds=5) + , (NumberOfPlayers=2, NumberOfZeds=7) + , (NumberOfPlayers=3, NumberOfZeds=8) + , (NumberOfPlayers=4, NumberOfZeds=9) + , (NumberOfPlayers=5, NumberOfZeds=9) + , (NumberOfPlayers=6, NumberOfZeds=9) + )}), + (Wave=7 + , BountyHuntWavePerPlayerZed = + {( + (NumberOfPlayers=1, NumberOfZeds=6) + , (NumberOfPlayers=2, NumberOfZeds=8) + , (NumberOfPlayers=3, NumberOfZeds=9) + , (NumberOfPlayers=4, NumberOfZeds=10) + , (NumberOfPlayers=5, NumberOfZeds=12) + , (NumberOfPlayers=6, NumberOfZeds=12) + )}) + )}, + + // Zed Stats progression related to remaining AI zeds + BountyHuntZedAndProgression= + {( + ( ZedType=class'KFGameContent.KFPawn_ZedGorefastDualBlade', + BountyHuntSpecialZedPerWave = + {( + (Wave=2), (Wave=3), (Wave=4), (Wave=5) + )}, + BountyHuntZedProgression = + {( + (RemainingZedRatio=1.f, HealthBuffRatio=1f, DamageBuffRatio=0.f), // +100% H // +0% DMG + (RemainingZedRatio=0.75f, HealthBuffRatio=1.5f, DamageBuffRatio=0.1f), // +150% H // +10% DMG + (RemainingZedRatio=0.5f, HealthBuffRatio=2f, DamageBuffRatio=0.2f), // +200% H / +20% DMG + (RemainingZedRatio=0.3f, HealthBuffRatio=2.5f, DamageBuffRatio=0.3f), // +250% H // +30% DMG + (RemainingZedRatio=0.1f, HealthBuffRatio=3f, DamageBuffRatio=3.f) // +300%H // +300% DMG + )} + ), + ( ZedType=class'KFGameContent.KFPawn_ZedClot_AlphaKing', + BountyHuntSpecialZedPerWave = + {( + (Wave=1), (Wave=3) + )}, + BountyHuntZedProgression = + {( + (RemainingZedRatio=1.f, HealthBuffRatio=5f, DamageBuffRatio=0.f), // +500% H // +0% DMG + (RemainingZedRatio=0.75f, HealthBuffRatio=5.1f, DamageBuffRatio=0.1f), // +510% H // +10% DMG + (RemainingZedRatio=0.5f, HealthBuffRatio=5.2f, DamageBuffRatio=0.2f), // +520% H / +20% DMG + (RemainingZedRatio=0.3f, HealthBuffRatio=5.3f, DamageBuffRatio=0.5f), // +530% H // +50% DMG + (RemainingZedRatio=0.1f, HealthBuffRatio=5.5f, DamageBuffRatio=3.f) // +550% H // +300% DMG + )} + ), + ( ZedType=class'KFGameContent.KFPawn_ZedFleshpoundMini', + BountyHuntSpecialZedPerWave = + {( + (Wave=6), (Wave=7) + )}, + BountyHuntZedProgression = + {( + (RemainingZedRatio=1.f, HealthBuffRatio=0.f, DamageBuffRatio=0.f), // +0% H // +0% DMG + (RemainingZedRatio=0.75f, HealthBuffRatio=0.0f, DamageBuffRatio=0.01f), // +0% H // +1% DMG + (RemainingZedRatio=0.5f, HealthBuffRatio=0.0f, DamageBuffRatio=0.02f), // +0% H // +2% DMG + (RemainingZedRatio=0.3f, HealthBuffRatio=0.01f, DamageBuffRatio=0.03f), // +1% H // +3% DMG + (RemainingZedRatio=0.1f, HealthBuffRatio=0.05f, DamageBuffRatio=0.1f) // +5% H // +10% DMG + )} + ), + ( ZedType=class'KFGameContent.KFPawn_ZedScrake', + BountyHuntSpecialZedPerWave = + {( + (Wave=4), (Wave=5), (Wave=6), (Wave=7) + )}, + BountyHuntZedProgression = + {( + (RemainingZedRatio=1.f, HealthBuffRatio=0.01f, DamageBuffRatio=0.f), // +1% H // +0% DMG + (RemainingZedRatio=0.75f, HealthBuffRatio=0.02f, DamageBuffRatio=0.05f), // +2% H // +5% DMG + (RemainingZedRatio=0.5f, HealthBuffRatio=0.03f, DamageBuffRatio=0.05f), // +3% H // +5% DMG + (RemainingZedRatio=0.3f, HealthBuffRatio=0.04f, DamageBuffRatio=0.05f), // +4% H // +5% DMG + (RemainingZedRatio=0.1f, HealthBuffRatio=0.05f, DamageBuffRatio=0.25f) // +5% H // +25% DMG + )} + ) + )}, + BountyHuntDosh= + {( + (NumberOfPlayers=1, Dosh=300, DoshNoAssist=250), + (NumberOfPlayers=2, Dosh=250, DoshNoAssist=200), + (NumberOfPlayers=3, Dosh=200, DoshNoAssist=150), + (NumberOfPlayers=4, Dosh=150, DoshNoAssist=100), + (NumberOfPlayers=5, Dosh=130, DoshNoAssist=80), + (NumberOfPlayers=6, Dosh=130, DoshNoAssist=80) + )} + )} + )} + //Test events from here down. These don't end up in the regular rotation. // The override ID starts from one higher than the last SetEvents entry above. // Ex: Big head = 7, Horde = 8 diff --git a/KFGameContent/Classes/KFPawn_AutoTurret.uc b/KFGameContent/Classes/KFPawn_AutoTurret.uc index 1490577..7e027e8 100644 --- a/KFGameContent/Classes/KFPawn_AutoTurret.uc +++ b/KFGameContent/Classes/KFPawn_AutoTurret.uc @@ -497,6 +497,55 @@ simulated state TargetSearch } } +simulated function bool TargetValidWithGeometry(Actor Target, vector MuzzleLoc, vector ReferencePosition) +{ + local vector HitLocation, HitNormal; + local Actor HitActor; + local vector DistanceToTarget, DistanceToTrader; + local KFTraderTrigger TestTrader; + local float ModuloDistanceToTrader, DotValue; + local bool bTraderFound; + local int IteratorTrader; + + HitActor = Trace(HitLocation, HitNormal, ReferencePosition, MuzzleLoc,,,,TRACEFLAG_Bullet); + + if (HitActor == none || KFPawn_Monster(HitActor) == none) + { + return false; + } + + DistanceToTarget = Target.Location - MuzzleLoc; + + bTraderFound = false; + + for (IteratorTrader=0; IteratorTrader < KFGameInfo(WorldInfo.Game).TraderList.Length; ++ IteratorTrader) + { + TestTrader = KFGameInfo(WorldInfo.Game).TraderList[IteratorTrader]; + + DistanceToTrader = TestTrader.Location - MuzzleLoc; + + ModuloDistanceToTrader = VSize(DistanceToTrader); + + if (ModuloDistanceToTrader < VSize(DistanceToTarget)) + { + DotValue = Normal(DistanceToTrader) Dot Normal(DistanceToTarget); + + if (DotValue > 0.2f) + { + bTraderFound = true; + break; + } + } + } + + if (bTraderFound) + { + return false; + } + + return true; +} + simulated state Combat { simulated function BeginState(name PreviousStateName) @@ -543,10 +592,6 @@ simulated state Combat local rotator MuzzleRot; local rotator DesiredRotationRot; - local vector HitLocation, HitNormal; - local TraceHitInfo HitInfo; - local Actor HitActor; - local float NewAmmoPercentage; local bool bIsSpotted; @@ -580,17 +625,14 @@ simulated state Combat { if (EnemyTarget != none) { - // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! - HitActor = Trace(HitLocation, HitNormal, EnemyTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); - // Visible by local player or team bIsSpotted = (EnemyTarget.bIsCloakingSpottedByLP || EnemyTarget.bIsCloakingSpottedByTeam); /** Search for new enemies if current is dead, cloaked or too far, or something between the drone that's world geometry */ - if (!EnemyTarget.IsAliveAndWell() + if (EnemyTarget.IsAliveAndWell() == false || (EnemyTarget.bIsCloaking && bIsSpotted == false) || VSizeSq(EnemyTarget.Location - Location) > EffectiveRadius * EffectiveRadius - || (HitActor != none && HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None)) + || TargetValidWithGeometry(EnemyTarget, MuzzleLoc, EnemyTarget.Mesh.GetBoneLocation('Spine1'))) { EnemyTarget = none; CheckForTargets(); @@ -613,11 +655,9 @@ simulated state Combat if (Role == ROLE_Authority && ReachedRotation()) { - HitActor = Trace(HitLocation, HitNormal, MuzzleLoc + vector(Rotation) * EffectiveRadius, MuzzleLoc, , , HitInfo, TRACEFLAG_Bullet); - if (TurretWeapon != none) { - if (HitActor != none && HitActor.bWorldGeometry == false) + if (TargetValidWithGeometry(EnemyTarget, MuzzleLoc, EnemyTarget.Mesh.GetBoneLocation('Spine1'))) { TurretWeapon.Fire(); @@ -751,9 +791,6 @@ function CheckForTargets() local vector MuzzleLoc; local rotator MuzzleRot; - local vector HitLocation, HitNormal; - local Actor HitActor; - local bool bIsSpotted; if (EnemyTarget != none) @@ -778,10 +815,7 @@ function CheckForTargets() continue; } - // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! - HitActor = Trace(HitLocation, HitNormal, CurrentTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); - - if (HitActor == none || (HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None)) + if (TargetValidWithGeometry(CurrentTarget, MuzzleLoc, CurrentTarget.Mesh.GetBoneLocation('Spine1')) == false) { continue; } @@ -1045,6 +1079,11 @@ simulated function bool CanInteractWithZoneVelocity() return false; } +function bool CanBeGrabbed(KFPawn GrabbingPawn, optional bool bIgnoreFalling, optional bool bAllowSameTeamGrab) +{ + return false; +} + simulated function UpdateTurretMeshMaterialColor(float Value) { if (TurretWeaponAttachment == none) diff --git a/KFGameContent/Classes/KFPawn_HRG_Warthog.uc b/KFGameContent/Classes/KFPawn_HRG_Warthog.uc index a5757c9..4f563b0 100644 --- a/KFGameContent/Classes/KFPawn_HRG_Warthog.uc +++ b/KFGameContent/Classes/KFPawn_HRG_Warthog.uc @@ -501,6 +501,55 @@ simulated state TargetSearch } } +simulated function bool TargetValidWithGeometry(Actor Target, vector MuzzleLoc, vector ReferencePosition) +{ + local vector HitLocation, HitNormal; + local Actor HitActor; + local vector DistanceToTarget, DistanceToTrader; + local KFTraderTrigger TestTrader; + local float ModuloDistanceToTrader, DotValue; + local bool bTraderFound; + local int IteratorTrader; + + HitActor = Trace(HitLocation, HitNormal, ReferencePosition, MuzzleLoc,,,,TRACEFLAG_Bullet); + + if (HitActor == none || KFPawn_Monster(HitActor) == none) + { + return false; + } + + DistanceToTarget = Target.Location - MuzzleLoc; + + bTraderFound = false; + + for (IteratorTrader=0; IteratorTrader < KFGameInfo(WorldInfo.Game).TraderList.Length; ++ IteratorTrader) + { + TestTrader = KFGameInfo(WorldInfo.Game).TraderList[IteratorTrader]; + + DistanceToTrader = TestTrader.Location - MuzzleLoc; + + ModuloDistanceToTrader = VSize(DistanceToTrader); + + if (ModuloDistanceToTrader < VSize(DistanceToTarget)) + { + DotValue = Normal(DistanceToTrader) Dot Normal(DistanceToTarget); + + if (DotValue > 0.2f) + { + bTraderFound = true; + break; + } + } + } + + if (bTraderFound) + { + return false; + } + + return true; +} + simulated state Combat { simulated function BeginState(name PreviousStateName) @@ -547,10 +596,6 @@ simulated state Combat local rotator MuzzleRot; local rotator DesiredRotationRot; - local vector HitLocation, HitNormal; - local TraceHitInfo HitInfo; - local Actor HitActor; - local float NewAmmoPercentage; local bool bIsSpotted; @@ -584,17 +629,14 @@ simulated state Combat { if (EnemyTarget != none) { - // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! - HitActor = Trace(HitLocation, HitNormal, EnemyTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); - // Visible by local player or team bIsSpotted = (EnemyTarget.bIsCloakingSpottedByLP || EnemyTarget.bIsCloakingSpottedByTeam); /** Search for new enemies if current is dead, cloaked or too far, or something between the drone that's world geometry */ - if (!EnemyTarget.IsAliveAndWell() + if (EnemyTarget.IsAliveAndWell() == false || (EnemyTarget.bIsCloaking && bIsSpotted == false) || VSizeSq(EnemyTarget.Location - Location) > EffectiveRadius * EffectiveRadius - || (HitActor != none && HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None)) + || TargetValidWithGeometry(EnemyTarget, MuzzleLoc, EnemyTarget.Mesh.GetBoneLocation('Spine1'))) { EnemyTarget = none; CheckForTargets(); @@ -617,11 +659,9 @@ simulated state Combat if (Role == ROLE_Authority && ReachedRotation()) { - HitActor = Trace(HitLocation, HitNormal, MuzzleLoc + vector(Rotation) * EffectiveRadius, MuzzleLoc, , , HitInfo, TRACEFLAG_Bullet); - if (TurretWeapon != none) { - if (HitActor != none && HitActor.bWorldGeometry == false) + if (TargetValidWithGeometry(EnemyTarget, MuzzleLoc, EnemyTarget.Mesh.GetBoneLocation('Spine1'))) { TurretWeapon.CurrentTarget = EnemyTarget; @@ -632,7 +672,7 @@ simulated state Combat SetTurretState(ETS_Empty); } } - } + } } } @@ -848,7 +888,7 @@ simulated function StopIdleSound() function CheckForTargets() { local KFPawn_Monster CurrentTarget; - local TraceHitInfo HitInfo; + local TraceHitInfo HitInfo; local float CurrentDistance; local float Distance; @@ -856,9 +896,6 @@ function CheckForTargets() local vector MuzzleLoc; local rotator MuzzleRot; - local vector HitLocation, HitNormal; - local Actor HitActor; - local bool bIsSpotted; if (EnemyTarget != none) @@ -883,10 +920,7 @@ function CheckForTargets() continue; } - // Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone! - HitActor = Trace(HitLocation, HitNormal, CurrentTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet); - - if (HitActor == none || (HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None)) + if (TargetValidWithGeometry(CurrentTarget, MuzzleLoc, CurrentTarget.Mesh.GetBoneLocation('Spine1')) == false) { continue; } @@ -1150,6 +1184,11 @@ simulated function bool CanInteractWithZoneVelocity() return false; } +function bool CanBeGrabbed(KFPawn GrabbingPawn, optional bool bIgnoreFalling, optional bool bAllowSameTeamGrab) +{ + return false; +} + simulated function UpdateTurretMeshMaterialColor(float Value) { if (TurretWeaponAttachment == none) diff --git a/KFGameContent/Classes/KFPawn_ZedBloat.uc b/KFGameContent/Classes/KFPawn_ZedBloat.uc index 63ba38c..dcf3e0a 100644 --- a/KFGameContent/Classes/KFPawn_ZedBloat.uc +++ b/KFGameContent/Classes/KFPawn_ZedBloat.uc @@ -407,4 +407,8 @@ DefaultProperties MinSpawnSquadSizeType=EST_Large bIsBloatClass=true + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } diff --git a/KFGameContent/Classes/KFPawn_ZedBloatKing.uc b/KFGameContent/Classes/KFPawn_ZedBloatKing.uc index 9d541d4..6e7496f 100644 --- a/KFGameContent/Classes/KFPawn_ZedBloatKing.uc +++ b/KFGameContent/Classes/KFPawn_ZedBloatKing.uc @@ -790,4 +790,8 @@ defaultproperties bCanBePinned=false bCanBeKilledByShrinking=false + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here (here defaults to this same class) + MapReplacePawnClass[0]= class'KFPawn_ZedBloatKing' } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedClot.uc b/KFGameContent/Classes/KFPawn_ZedClot.uc index c8872ae..86b44ed 100644 --- a/KFGameContent/Classes/KFPawn_ZedClot.uc +++ b/KFGameContent/Classes/KFPawn_ZedClot.uc @@ -102,4 +102,8 @@ DefaultProperties // AI / Navigation //DebugRange_Melee_Material=Material'ENG_EditorResources_MAT.Debug_Radius_M' RotationRate=(Pitch=50000,Yaw=30000,Roll=50000) + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } diff --git a/KFGameContent/Classes/KFPawn_ZedCrawler.uc b/KFGameContent/Classes/KFPawn_ZedCrawler.uc index 2e2443b..39856c9 100644 --- a/KFGameContent/Classes/KFPawn_ZedCrawler.uc +++ b/KFGameContent/Classes/KFPawn_ZedCrawler.uc @@ -470,4 +470,8 @@ defaultproperties MinSpawnSquadSizeType=EST_Crawler ZEDCowboyHatAttachName=Hat_Attach + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } diff --git a/KFGameContent/Classes/KFPawn_ZedDAR.uc b/KFGameContent/Classes/KFPawn_ZedDAR.uc index 347ba1c..b60b5ad 100644 --- a/KFGameContent/Classes/KFPawn_ZedDAR.uc +++ b/KFGameContent/Classes/KFPawn_ZedDAR.uc @@ -436,4 +436,8 @@ defaultproperties StopSprintingSound=AkEvent'WW_ZED_Evil_DAR.Play_ZED_EvilDAR_SFX_Thruster_Stop' ZEDCowboyHatAttachName=Hat_Attach + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } diff --git a/KFGameContent/Classes/KFPawn_ZedFleshpound.uc b/KFGameContent/Classes/KFPawn_ZedFleshpound.uc index fe7d1fb..5c2699e 100644 --- a/KFGameContent/Classes/KFPawn_ZedFleshpound.uc +++ b/KFGameContent/Classes/KFPawn_ZedFleshpound.uc @@ -315,10 +315,22 @@ function class GetBumpAttackDamageType() */ simulated function AdjustAffliction(out float AfflictionPower) { + local KFGameInfo KFGI; + if ( bIsEnraged ) { AfflictionPower *= 0.25f; } + + if (bIsBountyHuntObjective) // Only on Bounty Hunt Weekly + { + KFGI = KFGameInfo(WorldInfo.Game); + + if (KFGI != none && KFGI.OutbreakEvent != none) + { + AfflictionPower -= AfflictionPower * KFGI.OutbreakEvent.ActiveEvent.BountyHuntSpecialZedBuffAfflictionResistance; + } + } } /** Track the fleshpound's speed and play the appropriate cues */ @@ -610,4 +622,8 @@ End Object OnDeathAchievementID=KFACHID_ItsOnlyAFleshWound ZEDCowboyHatAttachName=Hat_Attach + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc b/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc index 6fdd425..ea47ed0 100644 --- a/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc +++ b/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc @@ -985,4 +985,8 @@ DefaultProperties bCanBePinned=false bCanBeKilledByShrinking=false + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here (here defaults to this same class) + MapReplacePawnClass[0] = class'KFPawn_ZedFleshpoundKing' } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedGorefast.uc b/KFGameContent/Classes/KFPawn_ZedGorefast.uc index 02fa52e..55b8f15 100644 --- a/KFGameContent/Classes/KFPawn_ZedGorefast.uc +++ b/KFGameContent/Classes/KFPawn_ZedGorefast.uc @@ -131,4 +131,8 @@ DefaultProperties // --------------------------------------------- // Spawning MinSpawnSquadSizeType=EST_Medium + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } diff --git a/KFGameContent/Classes/KFPawn_ZedHans.uc b/KFGameContent/Classes/KFPawn_ZedHans.uc index 04c9c69..3f8fbc4 100644 --- a/KFGameContent/Classes/KFPawn_ZedHans.uc +++ b/KFGameContent/Classes/KFPawn_ZedHans.uc @@ -1671,4 +1671,8 @@ DefaultProperties MinSpawnSquadSizeType=EST_Boss OnDeathAchievementID=KFACHID_DieVolter + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here (here defaults to this same class) + MapReplacePawnClass.Add(class'KFPawn_ZedHans') } diff --git a/KFGameContent/Classes/KFPawn_ZedHansClot.uc b/KFGameContent/Classes/KFPawn_ZedHansClot.uc new file mode 100644 index 0000000..5d73886 --- /dev/null +++ b/KFGameContent/Classes/KFPawn_ZedHansClot.uc @@ -0,0 +1,96 @@ +//============================================================================= +// KFPawn_ZedHansClot +//============================================================================= +// KF Pawn for Hans Clot +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +// +//============================================================================= + +class KFPawn_ZedHansClot extends KFPawn_ZedClot; + +/** Returns (hardcoded) trader advice dialog ID */ +static function int GetTraderAdviceID() +{ + return 36;//TRAD_AdviceClotU +} + +defaultproperties +{ + LocalizationKey=KFPawn_ZedHansClot + // --------------------------------------------- + // Stats + XPValues(0)=8 + XPValues(1)=11 + XPValues(2)=11 + XPValues(3)=11 + + Begin Object Name=KFPawnSkeletalMeshComponent + bPerBoneMotionBlur=false + End Object + + // --------------------------------------------- + // Content + MonsterArchPath="ZED_ARCH.ZED_HansClot_Archetype" + PawnAnimInfo=KFPawnAnimInfo'ZED_Clot_Anim.UndevClot_AnimGroup' + DifficultySettings=class'KFDifficulty_ClotCyst' + + // --------------------------------------------- + // Gameplay + GrabAttackFrequency=0.8f + KnockedDownBySonicWaveOdds=0.35f + + // for reference: Vulnerability=(default, head, legs, arms, special) + IncapSettings(AF_Stun)= (Vulnerability=(2.0, 2.0, 1.0, 1.0, 1.0), Cooldown=3.0, Duration=3.0) + IncapSettings(AF_Knockdown)= (Vulnerability=(1.f), Cooldown=1.0) + IncapSettings(AF_Stumble)= (Vulnerability=(1.3f), Cooldown=0.2) + IncapSettings(AF_GunHit)= (Vulnerability=(2.f), Cooldown=0.2) + IncapSettings(AF_MeleeHit)= (Vulnerability=(2.0), Cooldown=0.0) + IncapSettings(AF_Poison)= (Vulnerability=(5), Cooldown=6.0, Duration=4.5) + IncapSettings(AF_Microwave)= (Vulnerability=(0.5), Cooldown=8.0, Duration=4.5) + IncapSettings(AF_FirePanic)= (Vulnerability=(1.5), Cooldown=7.0, Duration=5) + IncapSettings(AF_EMP)= (Vulnerability=(2.5), Cooldown=5.0, Duration=5.0) + 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 + + DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Submachinegun', DamageScale=(1.5))) //3.0 + DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_AssaultRifle', DamageScale=(1.5))) //2.5 + DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Shotgun', DamageScale=(1.0))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Handgun', DamageScale=(1.01))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Rifle', DamageScale=(1.0))) //0.76 + DamageTypeModifiers.Add((DamageType=class'KFDT_Slashing', DamageScale=(0.85))) //0.5 + DamageTypeModifiers.Add((DamageType=class'KFDT_Bludgeon', DamageScale=(0.9))) // 0.5 + DamageTypeModifiers.Add((DamageType=class'KFDT_Fire', DamageScale=(1.0))) //0.8 + DamageTypeModifiers.Add((DamageType=class'KFDT_Microwave', DamageScale=(0.25))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Explosive', DamageScale=(1.0))) //0.85 + DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing', DamageScale=(1.0))) + DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic', DamageScale=(1.0))) //0.9 + + + // special case + DamageTypeModifiers.Add((DamageType=class'KFDT_Slashing_Knife', DamageScale=(1.0)) + + // --------------------------------------------- + // AI / Navigation + ControllerClass=class'KFAIController_ZedClot_Cyst' + PeripheralVision=-0.5f //180 + DamageRecoveryTimeHeavy=0.85f + DamageRecoveryTimeMedium=1.0f + + RotationRate=(Pitch=50000,Yaw=20000,Roll=50000) + + KnockdownImpulseScale=1.0f + +`if(`notdefined(ShippingPC)) + DebugRadarTexture=Texture2D'UI_ZEDRadar_TEX.MapIcon_Underdeveloped'; +`endif +} + + diff --git a/KFGameContent/Classes/KFPawn_ZedHusk.uc b/KFGameContent/Classes/KFPawn_ZedHusk.uc index 2656dfc..d8edd36 100644 --- a/KFGameContent/Classes/KFPawn_ZedHusk.uc +++ b/KFGameContent/Classes/KFPawn_ZedHusk.uc @@ -662,4 +662,8 @@ DefaultProperties MinSpawnSquadSizeType=EST_Medium ZEDCowboyHatAttachName=Hat_Attach + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } diff --git a/KFGameContent/Classes/KFPawn_ZedMatriarch.uc b/KFGameContent/Classes/KFPawn_ZedMatriarch.uc index e37762e..80f052d 100644 --- a/KFGameContent/Classes/KFPawn_ZedMatriarch.uc +++ b/KFGameContent/Classes/KFPawn_ZedMatriarch.uc @@ -2033,4 +2033,8 @@ defaultproperties ZEDCowboyHatAttachName=Hat_Attach ShrinkEffectModifier = 0.15f + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here (here defaults to this same class) + MapReplacePawnClass.Add(class'KFPawn_ZedMatriarch') } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedPatriarch.uc b/KFGameContent/Classes/KFPawn_ZedPatriarch.uc index 887b7ed..9a95dc6 100644 --- a/KFGameContent/Classes/KFPawn_ZedPatriarch.uc +++ b/KFGameContent/Classes/KFPawn_ZedPatriarch.uc @@ -2340,4 +2340,8 @@ defaultproperties TickDialogInterval=0.5f OnDeathAchievementID=KFACHID_QuickOnTheTrigger + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here (here defaults to this same class) + MapReplacePawnClass.Add(class'KFPawn_ZedPatriarch') } \ No newline at end of file diff --git a/KFGameContent/Classes/KFPawn_ZedScrake.uc b/KFGameContent/Classes/KFPawn_ZedScrake.uc index af395ee..2c669e7 100644 --- a/KFGameContent/Classes/KFPawn_ZedScrake.uc +++ b/KFGameContent/Classes/KFPawn_ZedScrake.uc @@ -406,4 +406,8 @@ defaultproperties OnDeathAchievementID=KFACHID_HackAndSlash ZEDCowboyHatAttachName=Hat_Attach + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } diff --git a/KFGameContent/Classes/KFPawn_ZedSiren.uc b/KFGameContent/Classes/KFPawn_ZedSiren.uc index 5220702..a8a3a94 100644 --- a/KFGameContent/Classes/KFPawn_ZedSiren.uc +++ b/KFGameContent/Classes/KFPawn_ZedSiren.uc @@ -255,4 +255,8 @@ defaultproperties OnDeathAchievementID=KFACHID_DeadSilence ZEDCowboyHatAttachName=Hat_Attach + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } diff --git a/KFGameContent/Classes/KFPawn_ZedStalker.uc b/KFGameContent/Classes/KFPawn_ZedStalker.uc index f778fbe..99d3010 100644 --- a/KFGameContent/Classes/KFPawn_ZedStalker.uc +++ b/KFGameContent/Classes/KFPawn_ZedStalker.uc @@ -554,4 +554,8 @@ DefaultProperties `if(`notdefined(ShippingPC)) DebugRadarTexture=Texture2D'UI_ZEDRadar_TEX.MapIcon_Stalker'; `endif + + // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true + // If we need to reuse it more we'll have to connect map to zed here + MapReplacePawnClass.Add(class'KFPawn_ZedHansClot') } diff --git a/KFGameContent/Classes/KFProj_Bullet_MG3_Alt.uc b/KFGameContent/Classes/KFProj_Bullet_MG3_Alt.uc new file mode 100644 index 0000000..bc73152 --- /dev/null +++ b/KFGameContent/Classes/KFProj_Bullet_MG3_Alt.uc @@ -0,0 +1,23 @@ +//============================================================================= +// KFProj_Bullet_MG3_Alt +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Bullet_MG3_Alt extends KFProj_Bullet + hidedropdown; + +defaultproperties +{ + MaxSpeed=22500.0 + Speed=22500.0 + + DamageRadius=0 + + ProjFlightTemplate=ParticleSystem'WEP_MG3_EMIT.FX_MG3_Tracer_ZEDTime' + ImpactEffects=KFImpactEffectInfo'WEP_MG3_ARCH.MG3_ALT_impact' +} + diff --git a/KFGameContent/Classes/KFSM_BloatKing_Gorge.uc b/KFGameContent/Classes/KFSM_BloatKing_Gorge.uc index 7bb711b..3daa317 100644 --- a/KFGameContent/Classes/KFSM_BloatKing_Gorge.uc +++ b/KFGameContent/Classes/KFSM_BloatKing_Gorge.uc @@ -256,7 +256,10 @@ static function bool IsValidPullClass(KFPawn PullPawn) MonsterPawn = KFPawn_Monster(PullPawn); - if (PullPawn.class == class'KFPawn_ZedBloatKingSubspawn' || MonsterPawn != none && MonsterPawn.IsABoss()) + if (PullPawn.class == class'KFPawn_ZedBloatKingSubspawn' + || MonsterPawn != none && MonsterPawn.IsABoss() + || PullPawn.class == class'KFPawn_HRG_Warthog' + || PullPawn.class == class'KFPawn_AutoTurret') { return false; } diff --git a/KFGameContent/Classes/KFSeasonalEventStats_Fall2023.uc b/KFGameContent/Classes/KFSeasonalEventStats_Fall2023.uc new file mode 100644 index 0000000..e2a9019 --- /dev/null +++ b/KFGameContent/Classes/KFSeasonalEventStats_Fall2023.uc @@ -0,0 +1,181 @@ +//============================================================================= +// KFSeasonalEventStats_Fall2023 +//============================================================================= +// Tracks event-specific challenges/accomplishments for Fall 2023 +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= +class KFSeasonalEventStats_Fall2023 extends KFSeasonalEventStats; + +var transient private const int HansVolterKillsRequired, CollectiblesLimit, EndlessWaveRequired; +var transient string SavedMapName; + +private event Initialize(string MapName) +{ + local string CapsMapName; + + CapsMapName = Caps(MapName); + + SavedMapName = CapsMapName; + + bObjectiveIsValidForMap[0] = 1; // Kill Hans Volter in 5 different maps + bObjectiveIsValidForMap[1] = 0; // Complete the Weekly on Castle Volter + bObjectiveIsValidForMap[2] = 0; // Find ten Castle Volter's Collectibles + bObjectiveIsValidForMap[3] = 0; // Unlock the Castle Volter's trophy room + bObjectiveIsValidForMap[4] = 0; // Complete wave 15 on Endless Hard or higher difficulty on Castle Volter + + if (CapsMapName == "KF-CASTLEVOLTER") + { + bObjectiveIsValidForMap[1] = 1; + bObjectiveIsValidForMap[2] = 1; + bObjectiveIsValidForMap[3] = 1; + bObjectiveIsValidForMap[4] = 1; + } + + SetSeasonalEventStatsMax(HansVolterKillsRequired, 0, CollectiblesLimit, 0, EndlessWaveRequired); +} + +simulated event OnStatsInitialized() +{ + super.OnStatsInitialized(); + + CheckRestartObjective(2, CollectiblesLimit); +} + +private event GrantEventItems() +{ + if (Outer.IsEventObjectiveComplete(0) && + Outer.IsEventObjectiveComplete(1) && + Outer.IsEventObjectiveComplete(2) && + Outer.IsEventObjectiveComplete(3) && + Outer.IsEventObjectiveComplete(4)) + { + GrantEventItem(9754); + } +} + +// Complete the Weekly on Castle Volter +simulated event OnGameWon(class GameClass, int Difficulty, int GameLength, bool bCoOp) +{ + local int ObjIdx; + ObjIdx = 1; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (GameClass == class'KFGameInfo_WeeklySurvival') + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } + + CheckRestartObjective(2, CollectiblesLimit); +} + +simulated event OnGameEnd(class GameClass) +{ + CheckRestartObjective(2, CollectiblesLimit); +} + +// Complete wave 15 on Endless Hard or higher difficulty on Castle Volter +simulated event OnWaveCompleted(class GameClass, int Difficulty, int WaveNum) +{ + local int ObjIdx; + ObjIdx = 4; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (WaveNum >= EndlessWaveRequired && GameClass == class'KFGameInfo_Endless' && Difficulty >= `DIFFICULTY_HARD) + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } +} + +simulated function CheckRestartObjective(int ObjectiveIndex, int ObjectiveLimit) +{ + local int StatValue; + + StatValue = Outer.GetSeasonalEventStatValue(ObjectiveIndex); + + if (StatValue != 0 && StatValue < ObjectiveLimit) + { + ResetSeasonalEventStat(ObjectiveIndex); + } +} + +simulated function OnTryCompleteObjective(int ObjectiveIndex, int EventIndex) +{ + local int ObjIdx; + + ObjIdx=3; + if (bObjectiveIsValidForMap[ObjIdx] != 0 && EventIndex == SEI_Fall && ObjectiveIndex == ObjIdx) + { + FinishedObjective(EventIndex, ObjectiveIndex); + } +} + +// Kill Hans Volter in 5 different maps +simulated function OnZedKilled(class MonsterClass, int Difficulty, class DT) +{ + local int ObjIdx; + local KFProfileSettings Profile; + + ObjIdx = 0; + + if (Outer.IsEventObjectiveComplete(ObjIdx)) + { + return; + } + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (MonsterClass == class'KFPawn_ZedHansBase' + || MonsterClass == class'KFPawn_ZedHans') + { + if (Outer.GetSeasonalEventStatValue(ObjIdx) < HansVolterKillsRequired) // If we still didn't reach the limit.. + { + Profile = KFProfileSettings(Outer.MyKFPC.OnlineSub.PlayerInterface.GetProfileSettings(Outer.MyKFPC.StoredLocalUserNum)); + if(Profile != none) + { + if (Profile.AddHansVolterKilledInMap(SavedMapName)) + { + //`Log("AddHansVolterKilledInMap : " $SavedMapName); + + IncrementSeasonalEventStat(ObjIdx, 1); + + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= HansVolterKillsRequired) + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } + } + } + } + } +} + +// Find ten Castle Volter's Collectibles +simulated function OnCollectibleFound(int Limit) +{ + local int ObjIdx; + + ObjIdx = 2; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + IncrementSeasonalEventStat(ObjIdx, 1); + + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= CollectiblesLimit) + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } +} + +defaultproperties +{ + HansVolterKillsRequired=5 + CollectiblesLimit=10 + EndlessWaveRequired=15 +} diff --git a/KFGameContent/Classes/KFWeapAttach_LMG_MG3.uc b/KFGameContent/Classes/KFWeapAttach_LMG_MG3.uc new file mode 100644 index 0000000..fdd6a4b --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_LMG_MG3.uc @@ -0,0 +1,109 @@ +// KFWeapAttach_LMG_MG3 +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapAttach_LMG_MG3 extends KFWeaponAttachment; + +var transient byte CachedNumAltBullets; +var transient float CachedAltFireOffset; + +event PreBeginPlay() +{ + super.PreBeginPlay(); + + CachedNumAltBullets = class'KFWeap_LMG_MG3'.default.NumAltBullets; + CachedAltFireOffset = (class'KFWeap_LMG_MG3'.default.SpreadWidthDegrees * DegToRad) / (CachedNumAltBullets - 1); +} + +/** Plays fire animation on weapon mesh */ +simulated function PlayWeaponFireAnim() +{ + local float Duration; + local float NewAnimRate; + + // Increase Anim Rate so loop firing shows the ammo belt moving. + NewAnimRate = 2.0f; + + if ( Instigator.bIsWalking ) + { + Duration = WeapMesh.GetAnimLength( WeaponIronFireAnim ); + WeapMesh.PlayAnim( WeaponIronFireAnim, Duration / NewAnimRate,, true ); + } + else + { + Duration = WeapMesh.GetAnimLength( WeaponFireAnim ); + WeapMesh.PlayAnim( WeaponFireAnim, Duration / NewAnimRate,, true ); + } +} + +/** Spawn tracer effects for this weapon */ +simulated function SpawnTracer(vector EffectLocation, vector HitLocation) +{ + local vector OriginalDir; + local vector UpVector, RightVector; + local KFTracerInfo TracerInfo; + local Quat R; + local byte i; + + if ( Instigator == None || Instigator.FiringMode >= TracerInfos.Length ) + { + return; + } + + if (Instigator.FiringMode != 1 || Instigator.Controller != None) + { + Super.SpawnTracer(EffectLocation, HitLocation); + return; + } + + TracerInfo = TracerInfos[Instigator.FiringMode]; + if( ((`NotInZedTime(self) && TracerInfo.bDoTracerDuringNormalTime) + || (`IsInZedTime(self) && TracerInfo.bDoTracerDuringZedTime)) + && TracerInfo.TracerTemplate != none ) + { + // At least one matches the 1P + OriginalDir = HitLocation - EffectLocation; + SpawnTracerVFX(TracerInfo, OriginalDir, EffectLocation); + + RightVector = vector(Rotation) cross vect(0,0,1); + UpVector = RightVector cross vector(Rotation); + + for (i = 0; i < (CachedNumAltBullets - 1) / 2; ++i) + { + R = QuatFromAxisAndAngle(UpVector, CachedAltFireOffset * (i+1)); + SpawnTracerVFX(TracerInfo, QuatRotateVector(R, OriginalDir), EffectLocation); + + R = QuatFromAxisAndAngle(UpVector, -CachedAltFireOffset * (i+1)); + SpawnTracerVFX(TracerInfo, QuatRotateVector(R, OriginalDir), EffectLocation); + } + } +} + +simulated function SpawnTracerVFX(KFTracerInfo TracerInfo, vector Direction, vector EffectLocation) +{ + local ParticleSystemComponent E; + local float DistSQ; + local float TracerDuration; + + DistSQ = VSizeSq(Direction); + if ( DistSQ > TracerInfo.MinTracerEffectDistanceSquared ) + { + // Lifetime scales based on the distance from the impact point. Subtract a frame so it doesn't clip. + TracerDuration = fMin( (Sqrt(DistSQ) - 100.f) / TracerInfo.TracerVelocity, 1.f ); + if( TracerDuration > 0.f ) + { + E = WorldInfo.MyEmitterPool.SpawnEmitter( TracerInfo.TracerTemplate, EffectLocation, rotator(Direction) ); + E.SetVectorParameter( 'Tracer_Velocity', TracerInfo.VelocityVector ); + E.SetFloatParameter( 'Tracer_Lifetime', TracerDuration ); + } + } +} + +defaultproperties +{ + CachedNumAltBullets=0 + CachedAltFireOffset=0 +} diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc index f561c48..d187f9a 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_FAMAS.uc @@ -46,7 +46,7 @@ static simulated event EFilterTypeUI GetTraderFilter() static simulated event EFilterTypeUI GetAltTraderFilter() { - return FT_Explosive; + return FT_Shotgun; } /** Instead of switch fire mode use as immediate alt fire */ diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_FNFal.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_FNFal.uc index 63f22f6..4250e12 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_FNFal.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_FNFal.uc @@ -9,6 +9,19 @@ class KFWeap_AssaultRifle_FNFal extends KFWeap_ScopedBase; +simulated state WeaponSingleFiring +{ + simulated event EndState( Name NextStateName ) + { + Super.EndState(NextStateName); + + if (WorldInfo.NetMode == NM_Client && bAllowClientAmmoTracking && FireInterval[CurrentFireMode] <= MinFireIntervalToTriggerSync) + { + SyncCurrentAmmoCount(CurrentFireMode, AmmoCount[CurrentFireMode]); + } + } +} + defaultproperties { // Shooting Animations diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc index 66a0139..2c90a95 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc @@ -44,6 +44,11 @@ simulated function AttachLaserSight() } } +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Assault; +} + defaultproperties { bHasFireLastAnims=true diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_HRGIncendiaryRifle.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_HRGIncendiaryRifle.uc index 3d26d9e..a5f9645 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_HRGIncendiaryRifle.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_HRGIncendiaryRifle.uc @@ -10,7 +10,7 @@ class KFWeap_AssaultRifle_HRGIncendiaryRifle extends KFWeap_AssaultRifle_M16M203 static simulated event EFilterTypeUI GetTraderFilter() { - return FT_Rifle; + return FT_Assault; } static simulated event EFilterTypeUI GetAltTraderFilter() diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_LazerCutter.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_LazerCutter.uc index eece2a4..74f1448 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_LazerCutter.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_LazerCutter.uc @@ -971,8 +971,8 @@ defaultproperties { FlameSprayArchetype = SprayActor_Flame'WEP_Laser_Cutter_ARCH.WEP_Laser_Cutter_Flame' - ChargeTimePerLevel = 1.0f; - ChargeConsumeTime = 0.12f; //Consumes 25 on full charge + ChargeTimePerLevel = 0.7f; + ChargeConsumeTime = 0.082f; //Consumes 25 on full charge OverchargeConsumeTime = 0; //Setting to 0 or below deactivates ammo consumption while holding a charge // Shooting Animations @@ -1002,7 +1002,7 @@ defaultproperties // Ammo MagazineCapacity[0] = 50 - SpareAmmoCapacity[0] = 300 + SpareAmmoCapacity[0] = 450 InitialSpareMags[0] = 1 bCanBeReloaded = true bReloadFromMagazine = true @@ -1039,7 +1039,7 @@ defaultproperties InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_LazerCutter' FireInterval(DEFAULT_FIREMODE)=+0.0857 // 700 RPM Spread(DEFAULT_FIREMODE)=0.015 - InstantHitDamage(DEFAULT_FIREMODE)=50.0 + InstantHitDamage(DEFAULT_FIREMODE)=55.0 FireOffset=(X=30,Y=4.5,Z=-5) AmmoCost(DEFAULT_FIREMODE)=1 @@ -1116,11 +1116,11 @@ defaultproperties ChargingRotationSpeedLimit = 0.f; ChargingMovementSpeedModifier = 1.f; - FiringRotationSpeedLimit(0)=420.f; - FiringRotationSpeedLimit(1)=240.f; - FiringRotationSpeedLimit(2)=180.f; - FiringRotationSpeedLimit(3)=120.f; - FiringMovementSpeedModifier=0.75f; + FiringRotationSpeedLimit(0)=900.f; + FiringRotationSpeedLimit(1)=800.f; + FiringRotationSpeedLimit(2)=700.f; + FiringRotationSpeedLimit(3)=600.f; + FiringMovementSpeedModifier=1f; MaxRotationAdjustmentTime = 0.25f; RotationAdjustmentCurve = { (Points = ((InVal = 0.000000,OutVal = 120.0f), diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_Medic.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_Medic.uc index ed51180..aa56930 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_Medic.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_Medic.uc @@ -107,6 +107,7 @@ defaultproperties AmmoCost(ALTFIRE_FIREMODE)=30 WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_HealingDart_MedicBase' InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Dart_Toxic' + InstantHitDamage(ALTFIRE_FIREMODE)=5.0 // BASH_FIREMODE InstantHitDamage(BASH_FIREMODE)=27 diff --git a/KFGameContent/Classes/KFWeap_Beam_Microwave.uc b/KFGameContent/Classes/KFWeap_Beam_Microwave.uc index d67a5f2..cd8de3b 100644 --- a/KFGameContent/Classes/KFWeap_Beam_Microwave.uc +++ b/KFGameContent/Classes/KFWeap_Beam_Microwave.uc @@ -164,7 +164,7 @@ simulated function bool ShouldAutoReload(byte FireModeNum) static simulated event EFilterTypeUI GetTraderFilter() { - return FT_Electric; + return FT_Flame; } defaultproperties diff --git a/KFGameContent/Classes/KFWeap_HRG_93R.uc b/KFGameContent/Classes/KFWeap_HRG_93R.uc new file mode 100644 index 0000000..a91f58f --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_93R.uc @@ -0,0 +1,119 @@ +//============================================================================= +// KFWeap_HRG_93R +//============================================================================= +// An KFWeap_HRG_93R Pistol +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HRG_93R extends KFWeap_PistolBase; + +defaultproperties +{ + // FOV + MeshFOV=96 + MeshIronSightFOV=77 + PlayerIronSightFOV=77 + + // Depth of field + DOF_FG_FocalRadius=40 + DOF_FG_MaxNearBlurSize=3.5 + + // Zooming/Position + PlayerViewOffset=(X=12.0,Y=12,Z=-6) + + // Content + PackageKey="HRG_93R_Pistol" + FirstPersonMeshName="wep_1p_hrg_93r_pistol_mesh.WEP_1P_HRG_93R_Pistol_Rig" + FirstPersonAnimSetNames(0)="wep_1p_hrg_93r_pistol_anim.Wep_1stP_9MM_Anim" + PickupMeshName="wep_3p_hrg_93r_pistol_mesh.Wep_3rdP_HRG_93R_Pistol_Pickup" + AttachmentArchetypeName="wep_hrg_93r_pistol_arch.Wep_HRG_93R_Pistol_3P" + MuzzleFlashTemplateName="wep_hrg_93r_pistol_arch.Wep_HRG_93R_Pistol_MuzzleFlash" + + // Zooming/Position + IronSightPosition=(X=10,Y=0,Z=0) + + // Ammo + MagazineCapacity[0]=30 + SpareAmmoCapacity[0]=240 //225 + InitialSpareMags[0]=4 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=250 + minRecoilPitch=200 + maxRecoilYaw=100 + minRecoilYaw=-100 + RecoilRate=0.01 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=250 + RecoilISMinPitchLimit=65485 + + // DEFAULT_FIREMODE + FiringStatesArray(DEFAULT_FIREMODE)=WeaponBurstFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_Pistol9mm' + FireInterval(DEFAULT_FIREMODE)=+0.08 //0.175 + InstantHitDamage(DEFAULT_FIREMODE)=12.f //15 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_93R' + Spread(DEFAULT_FIREMODE)=0.015 + FireOffset=(X=20,Y=4.0,Z=-3) + BurstAmount=3 + + // ALT_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_93R' + InstantHitDamage(BASH_FIREMODE)=20 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_93R.Play_WEP_HRG_93R_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_93R.Play_WEP_HRG_93R_Fire_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_HRG_93R.Play_WEP_HRG_93R_Handling_DryFire' + + // Attachments + bHasIronSights=true + bHasFlashlight=true + + AssociatedPerkClasses(0)=none + + // Inventory + InventorySize=0 + GroupPriority=13 + bCanThrow=false + bDropOnDeath=false + WeaponSelectTexture=Texture2D'wep_ui_hrg_93r_pistol_tex.UI_WeaponSelect_HRG_93R' + bIsBackupWeapon=true + + DualClass=class'KFWeap_HRG_93R_Dual' + + // Custom animations + FireSightedAnims=(Shoot_Iron, Shoot_Iron2, Shoot_Iron3) + IdleFidgetAnims=(Guncheck_v1, Guncheck_v2, Guncheck_v3, Guncheck_v4) + + bHasFireLastAnims=true + + BonesToLockOnEmpty=(RW_Bolt, RW_Bullets1) + + // Weapon Upgrade stat boosts. Setting weight to 0 because single 9MM cannot be sold. + //WeaponUpgrades[1]=(IncrementDamage=1.2f,IncrementWeight=0) + //WeaponUpgrades[2]=(IncrementDamage=1.4f,IncrementWeight=0) //1 + //WeaponUpgrades[3]=(IncrementDamage=1.6f,IncrementWeight=0) //1 + //WeaponUpgrades[4]=(IncrementDamage=1.8f,IncrementWeight=0) //2 + //WeaponUpgrades[5]=(IncrementDamage=2.0f,IncrementWeight=0) //3 + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.2f))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.4f))) + WeaponUpgrades[3]=(Stats=((Stat=EWUS_Damage0, Scale=1.6f))) + WeaponUpgrades[4]=(Stats=((Stat=EWUS_Damage0, Scale=1.8f))) + WeaponUpgrades[5]=(Stats=((Stat=EWUS_Damage0, Scale=2.0f))) +} + diff --git a/KFGameContent/Classes/KFWeap_HRG_93R_Dual.uc b/KFGameContent/Classes/KFWeap_HRG_93R_Dual.uc new file mode 100644 index 0000000..6a1ace3 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_93R_Dual.uc @@ -0,0 +1,140 @@ +//============================================================================= +// A set of KFWeap_HRG_93R Pistols +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_HRG_93R_Dual extends KFWeap_DualBase; + +simulated state WeaponBurstFiring +{ + simulated function BeginState(Name PrevStateName) + { + bFireFromRightWeapon = !bFireFromRightWeapon; + + super.BeginState(PrevStateName); + } +} + +defaultproperties +{ + // Content + PackageKey="Dual_HRG_93R_Pistol" + FirstPersonMeshName="wep_1p_dual_hrg_93r_pistol_mesh.WEP_1P_HRG_93R_Dual_Pistol_Rig" + FirstPersonAnimSetNames(0)="wep_1p_dual_hrg_93r_pistol_anim.Wep_1stP_Dual_9MM_Anim" + PickupMeshName="wep_3p_dual_hrg_93r_pistol_mesh.Wep_Dual_HRG_93R_Pickup" + AttachmentArchetypeName="wep_dual_hrg_93r_pistol_arch.Wep_Dual_HRG_93R_Pistol_3P" + MuzzleFlashTemplateName="wep_dual_hrg_93r_pistol_arch.Wep_Dual_HRG_93R_Pistol_MuzzleFlash" + + FireOffset=(X=17,Y=4.0,Z=-2.25) + LeftFireOffset=(X=17,Y=-4,Z=-2.25) + + // Zooming/Position + IronSightPosition=(X=-3,Y=0,Z=0) + PlayerViewOffset=(X=5,Y=0,Z=-5) + QuickWeaponDownRotation=(Pitch=-8192,Yaw=0,Roll=0) + + bCanThrow=true + bDropOnDeath=true + + SingleClass=class'KFWeap_HRG_93R' + + // FOV + MeshFOV=96 + MeshIronSightFOV=77 + PlayerIronSightFOV=77 + + // Depth of field + DOF_FG_FocalRadius=40 + DOF_FG_MaxNearBlurSize=3.5 + + // Ammo + MagazineCapacity[0]=60 // twice as much as single + SpareAmmoCapacity[0]=240 + InitialSpareMags[0]=3 + AmmoPickupScale[0]=1.0 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=250 + minRecoilPitch=200 + maxRecoilYaw=100 + minRecoilYaw=-100 + RecoilRate=0.01 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=250 + RecoilISMinPitchLimit=65485 + + // DEFAULT_FIREMODE + FiringStatesArray(DEFAULT_FIREMODE)=WeaponBurstFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_Pistol9mm' + FireInterval(DEFAULT_FIREMODE)=+0.08 + InstantHitDamage(DEFAULT_FIREMODE)=12.0 //15 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_93R' + Spread(DEFAULT_FIREMODE)=0.015 + BurstAmount=3 + + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' + + // ALTFIRE_FIREMODE + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponBurstFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_Pistol9mm' + FireInterval(ALTFIRE_FIREMODE)=+0.08 // about twice as fast as single + InstantHitDamage(ALTFIRE_FIREMODE)=12.0 //15 + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_93R' + Spread(ALTFIRE_FIREMODE)=0.015 + + FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' + + // BASH_FIREMODE + InstantHitDamage(BASH_FIREMODE)=22 + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_93R' + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_93R.Play_WEP_HRG_93R_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_93R.Play_WEP_HRG_93R_Fire_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_HRG_93R.Play_WEP_HRG_93R_Handling_DryFire' + + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_93R.Play_WEP_HRG_93R_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_93R.Play_WEP_HRG_93R_Fire_1P') + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_HRG_93R.Play_WEP_HRG_93R_Handling_DryFire' + + // Attachments + bHasIronSights=true + bHasFlashlight=true + + AssociatedPerkClasses(0)=none + + // Inventory + InventoryGroup=IG_Secondary + InventorySize=2 + GroupPriority=20 + WeaponSelectTexture=Texture2D'wep_ui_dual_hrg_93r_pistol_tex.UI_WeaponSelect_Dual_HRG_93R' + bIsBackupWeapon=false + + BonesToLockOnEmpty=(RW_Bolt, RW_Bullets1) + BonesToLockOnEmpty_L=(LW_Bolt, LW_Bullets1) + + bHasFireLastAnims=true + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.2f,IncrementWeight=0) + //WeaponUpgrades[2]=(IncrementDamage=1.4f,IncrementWeight=0) //1 + //WeaponUpgrades[3]=(IncrementDamage=1.6f,IncrementWeight=0) //1 + //WeaponUpgrades[4]=(IncrementDamage=1.8f,IncrementWeight=0) //2 + //WeaponUpgrades[5]=(IncrementDamage=2.0f,IncrementWeight=0) //3 + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.2f), (Stat=EWUS_Damage1, Scale=1.2f))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.4f), (Stat=EWUS_Damage1, Scale=1.4f))) + WeaponUpgrades[3]=(Stats=((Stat=EWUS_Damage0, Scale=1.6f), (Stat=EWUS_Damage1, Scale=1.6f))) + WeaponUpgrades[4]=(Stats=((Stat=EWUS_Damage0, Scale=1.8f), (Stat=EWUS_Damage1, Scale=1.8f))) + WeaponUpgrades[5]=(Stats=((Stat=EWUS_Damage0, Scale=2.0f), (Stat=EWUS_Damage1, Scale=2.0f))) +} + diff --git a/KFGameContent/Classes/KFWeap_HRG_Healthrower.uc b/KFGameContent/Classes/KFWeap_HRG_Healthrower.uc index b55d603..74673a4 100644 --- a/KFGameContent/Classes/KFWeap_HRG_Healthrower.uc +++ b/KFGameContent/Classes/KFWeap_HRG_Healthrower.uc @@ -414,7 +414,10 @@ function StartHealRecharge() if (Role == ROLE_Authority) { InstigatorPerk = GetPerk(); - UsedHealRechargeTime = HealFullRechargeSeconds * static.GetUpgradeHealRechargeMod(CurrentWeaponUpgradeIndex); + + UsedHealRechargeTime = HealFullRechargeSeconds; + UsedHealRechargeTime *= static.GetUpgradeHealRechargeMod(CurrentWeaponUpgradeIndex); + UsedHealRechargeTime *= InstigatorPerk.GetHealRechargeMod(); InstigatorPerk.ModifyHealerRechargeTime(UsedHealRechargeTime); // Set the healing recharge rate whenever we start charging @@ -1071,7 +1074,7 @@ defaultproperties WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_HealingDart_MedicBase' FireInterval(ALTFIRE_FIREMODE)=+0.175 - InstantHitDamage(ALTFIRE_FIREMODE)=0 //Acidic compound skill can adjust that + InstantHitDamage(ALTFIRE_FIREMODE)=5 InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Dart_Toxic' Spread(ALTFIRE_FIREMODE)=0.015 AmmoCost(ALTFIRE_FIREMODE)=40 diff --git a/KFGameContent/Classes/KFWeap_HRG_SonicGun.uc b/KFGameContent/Classes/KFWeap_HRG_SonicGun.uc index 3802717..91e81ca 100644 --- a/KFGameContent/Classes/KFWeap_HRG_SonicGun.uc +++ b/KFGameContent/Classes/KFWeap_HRG_SonicGun.uc @@ -299,7 +299,7 @@ simulated function IncrementChargeAndPlayAnimation() /** Returns trader filter index based on weapon type */ static simulated event EFilterTypeUI GetTraderFilter() { - return FT_Explosive; + return FT_Projectile; } simulated function float GetForceReloadDelay() diff --git a/KFGameContent/Classes/KFWeap_HRG_Vampire.uc b/KFGameContent/Classes/KFWeap_HRG_Vampire.uc index 99af90b..7754d4a 100644 --- a/KFGameContent/Classes/KFWeap_HRG_Vampire.uc +++ b/KFGameContent/Classes/KFWeap_HRG_Vampire.uc @@ -316,7 +316,7 @@ simulated function bool ShouldAutoReload(byte FireModeNum) static simulated event EFilterTypeUI GetTraderFilter() { - return FT_Electric; + return FT_Projectile; } @@ -1372,4 +1372,6 @@ defaultproperties BloodSplashClass=KFProj_BloodSplash NumBloodStolenParticlesForPool = 15 + + bForceCrosshair=true } \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeap_HVStormCannon.uc b/KFGameContent/Classes/KFWeap_HVStormCannon.uc index e4810b3..1f9cad4 100644 --- a/KFGameContent/Classes/KFWeap_HVStormCannon.uc +++ b/KFGameContent/Classes/KFWeap_HVStormCannon.uc @@ -310,6 +310,12 @@ simulated function UpdateAmmoCounter() WeaponMICs[3].SetScalarParameterValue('opacity', PercentageAmmo); } +/** Returns trader filter index based on weapon type (copied from riflebase) */ +static simulated event EFilterTypeUI GetTraderFilter() +{ + return FT_Rifle; +} + defaultproperties { // FOV diff --git a/KFGameContent/Classes/KFWeap_Ice_FreezeThrower.uc b/KFGameContent/Classes/KFWeap_Ice_FreezeThrower.uc index 649e1e5..a4c7a67 100644 --- a/KFGameContent/Classes/KFWeap_Ice_FreezeThrower.uc +++ b/KFGameContent/Classes/KFWeap_Ice_FreezeThrower.uc @@ -148,7 +148,7 @@ simulated function SetPilotDynamicLightEnabled( bool bLightEnabled ); static simulated event EFilterTypeUI GetTraderFilter() { - return FT_Electric; + return FT_Flame; } defaultproperties diff --git a/KFGameContent/Classes/KFWeap_LMG_MG3.uc b/KFGameContent/Classes/KFWeap_LMG_MG3.uc new file mode 100644 index 0000000..0019576 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_LMG_MG3.uc @@ -0,0 +1,491 @@ +//============================================================================= +// KFWeap_LMG_MG3 +//============================================================================= +// The MG3 rifle +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2023 Tripwire Interactive LLC +//============================================================================= +class KFWeap_LMG_MG3 extends KFWeap_RifleBase; + +/** Alternate reload anims (when below the bullet belt threshold */ +const ReloadLowAmmoAnim = 'Reload_Empty_Half'; +const ReloadLowAmmoEliteAnim = 'Reload_Empty_Half_Elite'; + +const AltFireLoopAnim = 'ShootLoop_Secondary'; +const AltFireLoopStartAnim = 'ShootLoop_Start_Secondary'; +const AltFireLoopEndAnim = 'ShootLoop_End_Secondary'; + +/** Array of bone names corresponding to bullets in the ammo belt */ +var protected const string AmmoBeltBulletBonePrefix; + +/** Number of bullets in the ammo belt */ +var protected const int NumAmmoBeltBullets; + +/** How much ammo we had the last time we updated the ammo belt */ +var transient protected int LastAmmoCount; + +/** TRUE when set for the first time */ +var transient protected bool bAmmoBeltInitialized; + +/** Number of bullets fired (Alt firemode)) */ +var byte NumAltBullets; + +/** Degrees used in altfire */ +var float SpreadWidthDegrees; + +/** Max offset from shot in Y and X axes. */ +var vector2d SpreadMaxOffset; + +var transient float StartingAltBulletPosition; + +/** Just in case we spawned in with an ammo count that's lower than the threshold */ +simulated event ReplicatedEvent( name VarName ) +{ + super.ReplicatedEvent( VarName ); + + if( !bAmmoBeltInitialized ) + { + UpdateAmmoBeltBullets(); + } +} + +simulated event PreBeginPlay() +{ + Super.PreBeginPlay(); + + Spread[ALTFIRE_FIREMODE] = SpreadWidthDegrees * DegToRad / (NumAltBullets - 1); + + StartingAltBulletPosition = -SpreadWidthDegrees * DegToRad * 0.5f; +} + +/** Just in case we spawned in with an ammo count that's lower than the threshold */ +simulated event PostBeginPlay() +{ + super.PostBeginPlay(); + + if( !bAmmoBeltInitialized && Role == ROLE_Authority ) + { + UpdateAmmoBeltBullets(); + } +} + +/** Update ammo belt */ +simulated function ConsumeAmmo( byte FireModeNum ) +{ + super.ConsumeAmmo( FireModeNum ); + + UpdateAmmoBeltBullets(); +} + +/** Update ammo belt */ +simulated function PerformReload( optional byte FireModeNum ) +{ + super.PerformReload( FireModeNum ); + + UpdateAmmoBeltBullets(); +} + +/** Notify triggered by reload animations, when ammo belt is grabbed offscreen */ +simulated function ANIMNOTIFY_RestoreAmmoBelt() +{ + local int AmmoType, PendingAmmoCount; + + AmmoType = GetAmmoType(0); + + // This value will sync with the amount actually reloaded in PerformReload() + PendingAmmoCount = Min( AmmoCount[AmmoType] + SpareAmmoCount[AmmoType], MagazineCapacity[AmmoType] ); + if( PendingAmmoCount < NumAmmoBeltBullets ) + { + UpdateAmmoBeltBullets( PendingAmmoCount ); + } + else + { + UpdateAmmoBeltBullets( , true ); + } +} + +/** Hides/shows bones on the weapon to simulate reaching the end of the ammo belt */ +simulated function UpdateAmmoBeltBullets( optional int ForcedBulletCount=INDEX_NONE, optional bool bShowAll=false ) +{ + local Name BulletBoneName; + local int NumAmmo, i; + + if( WorldInfo.NetMode == NM_DedicatedServer ) + { + return; + } + + // Don't do any bone alterations until the bone array has been filled out + if( MySkelMesh.LocalAtoms.Length == 0 ) + { + // Check on the next frame to see if we can init our ammo belt + if( !IsTimerActive(nameOf(Timer_AttemptAmmoBeltUpdate)) ) + { + SetTimer( 0.01f, false, nameOf(Timer_AttemptAmmoBeltUpdate) ); + } + return; + } + + // So we don't do this in PostInitAnimTree() or again in Timer_AttemptAmmoBeltUpdate() if we don't have to + bAmmoBeltInitialized = true; + + // Don't do anything if ammo hasn't changed + NumAmmo = ForcedBulletCount != INDEX_NONE ? ForcedBulletCount : AmmoCount[GetAmmoType(0)]; + if( !bShowAll && (LastAmmoCount == NumAmmo || (LastAmmoCount >= NumAmmoBeltBullets && NumAmmo >= NumAmmoBeltBullets)) ) + { + return; + } + + // Hide or unhide depending on place in array + for( i = 0; i < NumAmmoBeltBullets; ++i ) + { + BulletBoneName = Name( AmmoBeltBulletBonePrefix $ (i+1) ); + + // Unhide all bullets if our ammo count is higher than the number of bullets + if( bShowAll || NumAmmo >= NumAmmoBeltBullets ) + { + MySkelMesh.UnHideBoneByName( BulletBoneName ); + continue; + } + + if( i > NumAmmo-1 ) + { + MySkelMesh.HideBoneByName( BulletBoneName, PBO_None ); + } + else + { + MySkelMesh.UnHideBoneByName( BulletBoneName ); + } + } + + LastAmmoCount = NumAmmo; +} + +simulated function Timer_AttemptAmmoBeltUpdate() +{ + if( !bAmmoBeltInitialized ) + { + UpdateAmmoBeltBullets(); + } +} + +/** Returns animation to play based on reload type and status */ +simulated function name GetReloadAnimName( bool bTacticalReload ) +{ + if( AmmoCount[0] > 0 && AmmoCount[0] < NumAmmoBeltBullets ) + { + // If we are below the threshold, play our low ammo reload + return bTacticalReload ? ReloadLowAmmoEliteAnim : ReloadLowAmmoAnim; + } + else + { + return super.GetReloadAnimName( bTacticalReload ); + } +} + +/** + * 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 ); + + // Just in case a reload was interrupted after ANIMNOTIFY_RestoreAmmoBelt() was called, + // set the ammo belt to the current amount of bullets in the magazine on equip + UpdateAmmoBeltBullets(); + } +} + + +simulated function KFProjectile SpawnAllProjectiles(class KFProjClass, vector RealStartLoc, vector AimDir) +{ + local int i; + local float InitialOffset; + + if (CurrentFireMode != ALTFIRE_FIREMODE) + { + return Super.SpawnAllProjectiles(KFProjClass, RealStartLoc, AimDir); + } + + InitialOffset = StartingAltBulletPosition; + + for (i = 0; i < NumAltBullets; i++) + { + SpawnProjectile(KFProjClass, RealStartLoc, CalculateSpread(InitialOffset, Spread[ALTFIRE_FIREMODE], i)); + } + + return None; +} + +simulated function vector CalculateSpread(float InitialOffset, float CurrentSpread, byte BulletNum) +{ + local Vector X, Y, Z, POVLoc; + local Quat R; + local rotator POVRot; + local float RandValue; + + if (Instigator != None && Instigator.Controller != none) + { + Instigator.Controller.GetPlayerViewPoint(POVLoc, POVRot); + } + + GetAxes(POVRot, X, Y, Z); + + // Calculate random offset on X axis + RandValue = FRand() * SpreadMaxOffset.X * DegToRad * (Rand(2) == 0 ? 1 : -1); + R = QuatFromAxisAndAngle(Z, InitialOffset + CurrentSpread * BulletNum + RandValue); + + // Calculate random offset on Y axisº + RandValue = FRand() * SpreadMaxOffset.Y * DegToRad * (Rand(2) == 0 ? 1 : -1); + R = QuatProduct(R, QuatFromAxisAndAngle(Y, RandValue)); + + return QuatRotateVector(R, vector(POVRot)); +} + +simulated function StartFire(byte FireModeNum) +{ + if (FireModeNum == DEFAULT_FIREMODE && bUseAltFireMode) + { + if (AmmoCount[FireModeNum] < AmmoCost[ALTFIRE_FIREMODE] && SpareAmmoCount[FireModeNum] > 0) + { + BeginFire(RELOAD_FIREMODE); + return; + } + } + + super.StartFire(FireModeNum); +} + +simulated function InstantFireClient() +{ + local vector StartTrace, EndTrace; + local rotator AimRot; + local Array ImpactList; + local int Idx; + local ImpactInfo RealImpact; + local float CurPenetrationValue; + + // see Controller AimHelpDot() / AimingHelp() + bInstantHit = true; + + // define range to use for CalcWeaponFire() + StartTrace = GetSafeStartTraceLocation(); + AimRot = GetAdjustedAim(StartTrace); + EndTrace = StartTrace + vector(AimRot) * GetTraceRange(); + + bInstantHit = false; + + // Initialize penetration power + PenetrationPowerRemaining = GetInitialPenetrationPower(CurrentFireMode); + CurPenetrationValue = PenetrationPowerRemaining; + + if (bUseAltFireMode) + { + EndTrace = StartTrace + CalculateSpread(StartingAltBulletPosition, Spread[ALTFIRE_FIREMODE], 0) * GetTraceRange(); + } + + // 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 + if( Instigator != None ) + { + Instigator.SetFlashLocation( Self, CurrentFireMode, RealImpact.HitLocation ); + } + + // local player only for clientside hit detection + if ( Instigator != None && Instigator.IsLocallyControlled() ) + { + // 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 InstantFireClient_AddImpacts(vector StartTrace, rotator Aim, out array ImpactList) +{ + local int i; + local ImpactInfo RealImpact; + + if (!bUseAltFireMode) + { + return; + } + + for (i = 1; i < NumAltBullets; ++i) + { + RealImpact = CalcWeaponFire(StartTrace, StartTrace + CalculateSpread(StartingAltBulletPosition, Spread[ALTFIRE_FIREMODE], i) * GetTraceRange(), ImpactList); + if( Instigator != None ) + { + Instigator.SetFlashLocation( Self, CurrentFireMode, RealImpact.HitLocation ); + } + } +} + +/** Get name of the animation to play for PlayFireEffects */ +simulated function name GetLoopingFireAnim(byte FireModeNum) +{ + if (FireModeNum == ALTFIRE_FIREMODE && !bUsingSights) + { + return AltFireLoopAnim; + } + + return super.GetLoopingFireAnim(FireModeNum); +} + +/** Get name of the animation to play for PlayFireEffects */ +simulated function name GetLoopStartFireAnim(byte FireModeNum) +{ + if (FireModeNum == ALTFIRE_FIREMODE && !bUsingSights) + { + return AltFireLoopStartAnim; + } + + return super.GetLoopStartFireAnim(FireModeNum); +} + + +/** Get name of the animation to play for PlayFireEffects */ +simulated function name GetLoopEndFireAnim(byte FireModeNum) +{ + if (FireModeNum == ALTFIRE_FIREMODE && !bUsingSights) + { + return AltFireLoopEndAnim; + } + + return super.GetLoopEndFireAnim(FireModeNum); +} + +defaultproperties +{ + // Shooting Animations + FireSightedAnims[0]=Shoot_Iron + FireSightedAnims[1]=Shoot_Iron2 + FireSightedAnims[2]=Shoot_Iron3 + + // FOV + MeshFOV=75 + MeshIronSightFOV=35 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=85 + DOF_FG_MaxNearBlurSize=2.5 + + // Content + PackageKey="MG3" + FirstPersonMeshName="WEP_1P_MG3_MESH.Wep_1stP_MG3_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_MG3_ANIM.Wep_1stP_MG3_Anim" + PickupMeshName="WEP_3P_MG3_MESH.Wep_MG3_Pickup" + AttachmentArchetypeName="WEP_MG3_ARCH.Wep_MG3_3P" + MuzzleFlashTemplateName="WEP_MG3_ARCH.Wep_MG3_MuzzleFlash" + + // Zooming/Position + PlayerViewOffset=(X=-5,Y=9,Z=-5) // (X=-8.5,Y=9,Z=-5) + IronSightPosition=(X=5,Y=0,Z=-5) // (X=8.5,Y=0,Z=-5) + + // Ammo + MagazineCapacity[0]=75 + SpareAmmoCapacity[0]=525 + InitialSpareMags[0]=1 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=120 + minRecoilPitch=70 + maxRecoilYaw=130 + minRecoilYaw=-130 + RecoilRate=0.08 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=75 + RecoilISMinYawLimit=65460 + RecoilISMaxPitchLimit=375 + RecoilISMinPitchLimit=65460 + RecoilViewRotationScale=0.25 + IronSightMeshFOVCompensationScale=2.3 + HippedRecoilModifier=1.5 + + // Inventory / Grouping + InventorySize=9 + GroupPriority=100 + WeaponSelectTexture=Texture2D'WEP_UI_MG3_TEX.UI_WeaponSelect_MG3' + AssociatedPerkClasses(0)=class'KFPerk_Commando' + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_AssaultRifle' + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_MG3' + FireInterval(DEFAULT_FIREMODE)=+0.066 // 900 RPM + Spread(DEFAULT_FIREMODE)=0.0085 + InstantHitDamage(DEFAULT_FIREMODE)=35.0 + FireOffset=(X=30,Y=4.5,Z=-5) + + // ALT_FIREMODE + FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletBurst' + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_MG3_Alt' + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_MG3_Alt' + FireInterval(ALTFIRE_FIREMODE)=+0.132 // 900 RPM it was +0.066 originally + Spread(ALTFIRE_FIREMODE)=0.001 + InstantHitDamage(ALTFIRE_FIREMODE)=15.0 // 30 + AmmoCost(ALTFIRE_FIREMODE)=3 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_MG3' + InstantHitDamage(BASH_FIREMODE)=26 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_MG3.Play_WEP_MG3_Fire_3P_Loop', FirstPersonCue=AkEvent'WW_WEP_MG3.Play_WEP_MG3_Fire_1P_Loop') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_MG3.Play_WEP_MG3_AltFire_3P_Single', FirstPersonCue=AkEvent'WW_WEP_MG3.Play_WEP_MG3_AltFire_1P_Single') + WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_MG3.Play_WEP_MG3_Fire_3P_EndLoop', FirstPersonCue=AkEvent'WW_WEP_MG3.Play_WEP_MG3_Fire_1P_EndLoop') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_L85A2.Play_WEP_SA_L85A2_Handling_DryFire' + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_L85A2.Play_WEP_SA_L85A2_Handling_DryFire' + EjectedShellForegroundDuration=0.8f + + // Advanced (High RPM) Fire Effects + bLoopingFireAnim(DEFAULT_FIREMODE)=true + bLoopingFireSnd(DEFAULT_FIREMODE)=true + bLoopingFireAnim(ALTFIRE_FIREMODE)=true + SingleFireSoundIndex=ALTFIRE_FIREMODE + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + // Ammo belt + AmmoBeltBulletBonePrefix="RW_Bullets" + NumAmmoBeltBullets=14 + LastAmmoCount=-1 + + NumAltBullets=5 + SpreadWidthDegrees=30 + StartingAltBulletPosition=0.0f + SpreadMaxOffset=(X=1.2f, Y=1.5f) +} diff --git a/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc b/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc index 928438f..e6784d5 100644 --- a/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc +++ b/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc @@ -753,7 +753,7 @@ simulated function int GetChargeLevel() static simulated event EFilterTypeUI GetTraderFilter() { - return FT_Flame; + return FT_Projectile; } static simulated function float CalculateTraderWeaponStatDamage() diff --git a/KFGameContent/Classes/KFWeap_Pistol_DualHRGWinterbite.uc b/KFGameContent/Classes/KFWeap_Pistol_DualHRGWinterbite.uc index 0c24473..bf349d7 100644 --- a/KFGameContent/Classes/KFWeap_Pistol_DualHRGWinterbite.uc +++ b/KFGameContent/Classes/KFWeap_Pistol_DualHRGWinterbite.uc @@ -9,11 +9,6 @@ class KFWeap_Pistol_DualHRGWinterbite extends KFWeap_DualBase; -static simulated event EFilterTypeUI GetAltTraderFilter() -{ - return FT_Flame; -} - defaultproperties { //Content diff --git a/KFGameContent/Classes/KFWeap_Pistol_HRGScorcher.uc b/KFGameContent/Classes/KFWeap_Pistol_HRGScorcher.uc index 76b0b83..4c9fae0 100644 --- a/KFGameContent/Classes/KFWeap_Pistol_HRGScorcher.uc +++ b/KFGameContent/Classes/KFWeap_Pistol_HRGScorcher.uc @@ -28,11 +28,16 @@ simulated function AltFireMode() StartFire(ALTFIRE_FIREMODE); } -static simulated event EFilterTypeUI GetAltTraderFilter() +static simulated event EFilterTypeUI GetTraderFilter() { return FT_Pistol; } +static simulated event EFilterTypeUI GetAltTraderFilter() +{ + return FT_Flame; +} + defaultproperties { ForceReloadTime=0.3f diff --git a/KFGameContent/Classes/KFWeap_Pistol_HRGWinterbite.uc b/KFGameContent/Classes/KFWeap_Pistol_HRGWinterbite.uc index c947c5b..b1646ab 100644 --- a/KFGameContent/Classes/KFWeap_Pistol_HRGWinterbite.uc +++ b/KFGameContent/Classes/KFWeap_Pistol_HRGWinterbite.uc @@ -9,11 +9,6 @@ class KFWeap_Pistol_HRGWinterbite extends KFWeap_PistolBase; -static simulated event EFilterTypeUI GetAltTraderFilter() -{ - return FT_Flame; -} - defaultproperties { // Content diff --git a/KFGameContent/Classes/KFWeap_Pistol_Medic.uc b/KFGameContent/Classes/KFWeap_Pistol_Medic.uc index 893d6bf..a2530f6 100644 --- a/KFGameContent/Classes/KFWeap_Pistol_Medic.uc +++ b/KFGameContent/Classes/KFWeap_Pistol_Medic.uc @@ -112,6 +112,7 @@ defaultproperties // ALTFIRE_FIREMODE WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_HealingDart_MedicBase' InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Dart_Toxic' + InstantHitDamage(ALTFIRE_FIREMODE)=5 // BASH_FIREMODE InstantHitDamage(BASH_FIREMODE)=21 diff --git a/KFGameContent/Classes/KFWeap_Rifle_FrostShotgunAxe.uc b/KFGameContent/Classes/KFWeap_Rifle_FrostShotgunAxe.uc index 42a80ac..db2dc1a 100644 --- a/KFGameContent/Classes/KFWeap_Rifle_FrostShotgunAxe.uc +++ b/KFGameContent/Classes/KFWeap_Rifle_FrostShotgunAxe.uc @@ -16,7 +16,12 @@ var int iNormalInstantHitDamage; /** Returns trader filter index based on weapon type */ static simulated event EFilterTypeUI GetTraderFilter() { - return FT_Rifle; + return FT_Shotgun; +} + +static simulated event EFilterTypeUI GetAltTraderFilter() +{ + return FT_Melee; } simulated event PreBeginPlay() diff --git a/KFGameContent/Classes/KFWeap_Rifle_Hemogoblin.uc b/KFGameContent/Classes/KFWeap_Rifle_Hemogoblin.uc index 475bde3..4bba2d5 100644 --- a/KFGameContent/Classes/KFWeap_Rifle_Hemogoblin.uc +++ b/KFGameContent/Classes/KFWeap_Rifle_Hemogoblin.uc @@ -130,6 +130,7 @@ defaultproperties AmmoCost(ALTFIRE_FIREMODE)=30 WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_HealingDart_MedicBase' InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Dart_Toxic' + InstantHitDamage(ALTFIRE_FIREMODE)=5 // BASH_FIREMODE InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_Hemogoblin' diff --git a/KFGameContent/Classes/KFWeap_SMG_Medic.uc b/KFGameContent/Classes/KFWeap_SMG_Medic.uc index f90a521..00e6fd9 100644 --- a/KFGameContent/Classes/KFWeap_SMG_Medic.uc +++ b/KFGameContent/Classes/KFWeap_SMG_Medic.uc @@ -97,7 +97,7 @@ defaultproperties AmmoCost(ALTFIRE_FIREMODE)=40 WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_HealingDart_MedicBase' InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Dart_Toxic' - + InstantHitDamage(ALTFIRE_FIREMODE)=5 // BASH_FIREMODE InstantHitDamage(BASH_FIREMODE)=23.0 diff --git a/KFGameContent/Classes/KFWeap_Shotgun_Medic.uc b/KFGameContent/Classes/KFWeap_Shotgun_Medic.uc index a3e7da1..8b3f8a3 100644 --- a/KFGameContent/Classes/KFWeap_Shotgun_Medic.uc +++ b/KFGameContent/Classes/KFWeap_Shotgun_Medic.uc @@ -114,6 +114,7 @@ defaultproperties AmmoCost(ALTFIRE_FIREMODE)=40 WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_HealingDart_MedicBase' InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Dart_Toxic' + InstantHitDamage(ALTFIRE_FIREMODE)=5 // BASH_FIREMODE - Waiting on animations InstantHitDamage(BASH_FIREMODE)=26.0 diff --git a/KFGameContent/Classes/KFWeap_Shotgun_S12.uc b/KFGameContent/Classes/KFWeap_Shotgun_S12.uc index 645b575..533abdc 100644 --- a/KFGameContent/Classes/KFWeap_Shotgun_S12.uc +++ b/KFGameContent/Classes/KFWeap_Shotgun_S12.uc @@ -476,7 +476,7 @@ simulated function TriggerAltExplosion() if (Role == ROLE_Authority) { MuzzleLocation = GetMuzzleLoc(); - Trace( HitLocation, HitNormal, MuzzleLocation + vect(0, 0, -1) * 250000, MuzzleLocation); + Trace( HitLocation, HitNormal, MuzzleLocation + vect(0, 0, -1) * 250000, MuzzleLocation,,,,TRACEFLAG_BULLET); // Move a bit from hit location HitLocation = HitLocation + (vect(0,0,1) * 128.f);