diff --git a/Engine/Classes/OnlineSubsystem.uc b/Engine/Classes/OnlineSubsystem.uc index 643d6c7..5444ea8 100644 --- a/Engine/Classes/OnlineSubsystem.uc +++ b/Engine/Classes/OnlineSubsystem.uc @@ -1498,6 +1498,7 @@ struct native ItemProperties /** Key ID used to open this item (used for playfab locked containers) */ var string RequiredKeyId; var string Name; + var string KeyName; var ItemType Type; var ItemRarity Rarity; var string ShortDescription; diff --git a/Engine/Classes/Pawn.uc b/Engine/Classes/Pawn.uc index e5dceca..6c8ddda 100644 --- a/Engine/Classes/Pawn.uc +++ b/Engine/Classes/Pawn.uc @@ -2596,17 +2596,10 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Killer = SetKillInstigator(InstigatedBy, DamageType); TearOffMomentum = momentum; Died(Killer, damageType, HitLocation); - - // using the passed in damage type instead of the hitfxinfo since that doesn't get updated when zero damage is done - HandleAfflictionsOnHit(InstigatedBy, Normal(Momentum), DamageType, DamageCauser); } else { HandleMomentum( momentum, HitLocation, DamageType, HitInfo ); - - // using the passed in damage type instead of the hitfxinfo since that doesn't get updated when zero damage is done - HandleAfflictionsOnHit(InstigatedBy, Normal(Momentum), DamageType, DamageCauser); - NotifyTakeHit(InstigatedBy, HitLocation, ActualDamage, DamageType, Momentum, DamageCauser); if (DrivenVehicle != None) { @@ -2626,8 +2619,6 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector `endif } -function HandleAfflictionsOnHit(Controller DamageInstigator, vector HitDir, class DamageType, Actor DamageCauser); - /* * Queries the PRI and returns our current team index. */ diff --git a/KFGame/Classes/KFAIController.uc b/KFGame/Classes/KFAIController.uc index 03b1d48..12f5c28 100644 --- a/KFGame/Classes/KFAIController.uc +++ b/KFGame/Classes/KFAIController.uc @@ -508,6 +508,15 @@ var const int ZedBumpEffectThreshold; /** The chance of obliterating a zed on an enraged bump */ var const float ZedBumpObliterationEffectChance; +// Only enabled while we didn't receive damage and we use Aggro for choosing Enemy +var bool CanForceEnemy; +var Pawn ForcedEnemy; +var Pawn LastForcedEnemy; +var float ForcedEnemyLastTime; +var float DamageRatioToChangeForcedEnemy; +var float TimeCanRestartForcedEnemy; +var float TimeCannotChangeFromForcedEnemy; + /********************************************************************************************* Evasion / Blocking ********************************************************************************************* */ @@ -1282,6 +1291,33 @@ native function StopAllLatentMoveExecution(); /** Am I being targeted by a player (optionally returns first found) */ native function bool IsTargetedByPlayer( optional out KFPawn outThreateningPlayer ); +function Pawn FindForcedEnemy() +{ + local KFGameInfo KFGI; + local KFPlayerController_WeeklySurvival KFPC_WS; + local class MyMonster; + + KFGI = KFGameInfo(WorldInfo.Game); + if(KFGI != none && KFGI.OutbreakEvent != none && KFGI.OutbreakEvent.ActiveEvent.bVIPGameMode) + { + MyMonster = class(Pawn.Class); + + // If this monster is included on the vip targetting, force VIP as enemy + if (KFGI.OutbreakEvent.ActiveEvent.VIPTargetting.Find(MyMonster) != INDEX_NONE) + { + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + if (KFPC_WS.VIPGameData.IsVIP && KFPC_WS.Pawn.IsAliveAndWell() && KFPC_WS.Pawn.CanAITargetThisPawn(self)) + { + return KFPC_WS.Pawn; + } + } + } + } + + return none; +} + /** * Originally from KF1, KFMonsterController.uc, added check to take # of Zeds targeting * the threat into account. @@ -1300,45 +1336,50 @@ event bool FindNewEnemy() } BestEnemy = none; - foreach WorldInfo.AllPawns( class'Pawn', PotentialEnemy ) - { - if( !PotentialEnemy.IsAliveAndWell() || Pawn.IsSameTeam( PotentialEnemy ) || - !PotentialEnemy.CanAITargetThisPawn(self) ) - { - continue; - } - NewDist = VSizeSq( PotentialEnemy.Location - Pawn.Location ); - if( BestEnemy == none || BestDist > NewDist ) + if (BestEnemy == none) + { + foreach WorldInfo.AllPawns( class'Pawn', PotentialEnemy ) { - // New best enemies do not care about the number of zeds around us yet - BestEnemyZedCount = INDEX_None; - bUpdateBestEnemy = true; - } - else - { - // Only update NumZedsTargetingBestEnemy if it's a new best enemy and the best enemy is further - if(BestEnemyZedCount == INDEX_None) + if( !PotentialEnemy.IsAliveAndWell() || Pawn.IsSameTeam( PotentialEnemy ) || + !PotentialEnemy.CanAITargetThisPawn(self) ) { - // Cache BestEnemyZedCount so we don't need to calculate it again - BestEnemyZedCount = NumberOfZedsTargetingPawn( BestEnemy ); + continue; } - PotentialEnemyZedCount = NumberOfZedsTargetingPawn( PotentialEnemy ); - if( PotentialEnemyZedCount < BestEnemyZedCount ) + NewDist = VSizeSq( PotentialEnemy.Location - Pawn.Location ); + if( BestEnemy == none || BestDist > NewDist ) { - BestEnemyZedCount = PotentialEnemyZedCount; + // New best enemies do not care about the number of zeds around us yet + BestEnemyZedCount = INDEX_None; bUpdateBestEnemy = true; } - } + else + { + // Only update NumZedsTargetingBestEnemy if it's a new best enemy and the best enemy is further + if(BestEnemyZedCount == INDEX_None) + { + // Cache BestEnemyZedCount so we don't need to calculate it again + BestEnemyZedCount = NumberOfZedsTargetingPawn( BestEnemy ); + } - if( bUpdateBestEnemy ) - { - BestEnemy = PotentialEnemy; - BestDist = NewDist; - bUpdateBestEnemy = false; + PotentialEnemyZedCount = NumberOfZedsTargetingPawn( PotentialEnemy ); + if( PotentialEnemyZedCount < BestEnemyZedCount ) + { + BestEnemyZedCount = PotentialEnemyZedCount; + bUpdateBestEnemy = true; + } + } + + if( bUpdateBestEnemy ) + { + BestEnemy = PotentialEnemy; + BestDist = NewDist; + bUpdateBestEnemy = false; + } } } + if( Enemy != none && BestEnemy != none && BestEnemy == Enemy ) { return false; @@ -1536,10 +1577,37 @@ event bool SetEnemy( Pawn NewEnemy ) function ChangeEnemy( Pawn NewEnemy, optional bool bCanTaunt = true ) { - local Pawn OldEnemy; + local Pawn OldEnemy, NewForcedEnemy; local KFGameInfo KFGI; + if (CanForceEnemy) + { + NewForcedEnemy = FindForcedEnemy(); + } + else if (NewEnemy == LastForcedEnemy) + { + return; // Don't allow to change to the ForcedEnemy while we can't (we reenable that again from outside) + } + + if (NewForcedEnemy != none) + { + ForcedEnemy = NewForcedEnemy; + + if (Enemy != ForcedEnemy) + { + LastForcedEnemy = ForcedEnemy; + + ForcedEnemyLastTime = WorldInfo.TimeSeconds; + } + + NewEnemy = NewForcedEnemy; + } + else + { + ForcedEnemy = none; + } + // gameinfo hook that calls mutator hook KFGI = KFGameInfo( WorldInfo.Game ); if( KFGI != none ) @@ -7626,6 +7694,13 @@ DefaultProperties LastFrustrationCheckTime=0.f LowIntensityAttackCooldown=2.0 //bUseOldAttackDecisions=true + CanForceEnemy=true + ForcedEnemy=none + LastForcedEnemy=none + ForcedEnemyLastTime=0.f + DamageRatioToChangeForcedEnemy=0.5f + TimeCanRestartForcedEnemy=10.f + TimeCannotChangeFromForcedEnemy=10.f // --------------------------------------------- // AI / Navigation diff --git a/KFGame/Classes/KFAIController_ZedFleshpound.uc b/KFGame/Classes/KFAIController_ZedFleshpound.uc index be317d9..1faf425 100644 --- a/KFGame/Classes/KFAIController_ZedFleshpound.uc +++ b/KFGame/Classes/KFAIController_ZedFleshpound.uc @@ -96,30 +96,35 @@ event bool FindNewEnemy() local Controller C; local Pawn PotentialEnemy; - foreach WorldInfo.AllControllers( class'Controller', C ) + BestEnemy = none; + + if (BestEnemy == none) { - if( C.Pawn == none || !C.Pawn.IsAliveAndWell() || Pawn.IsSameTeam( C.Pawn ) || - !C.Pawn.CanAITargetThisPawn(self) ) + foreach WorldInfo.AllControllers( class'Controller', C ) { - continue; - } + if( C.Pawn == none || !C.Pawn.IsAliveAndWell() || Pawn.IsSameTeam( C.Pawn ) || + !C.Pawn.CanAITargetThisPawn(self) ) + { + continue; + } - PotentialEnemy = C.Pawn; - NewDist = VSizeSq( PotentialEnemy.Location - Pawn.Location ); + PotentialEnemy = C.Pawn; + NewDist = VSizeSq( PotentialEnemy.Location - Pawn.Location ); - if( BestEnemy == none ) - { - BestEnemy = PotentialEnemy; - BestDist = NewDist; - } - - else if( BestEnemy != none ) - { - if( (BestDist > NewDist) || (NumberOfZedsTargetingPawn( PotentialEnemy ) < NumberOfZedsTargetingPawn( BestEnemy )) ) + if( BestEnemy == none ) { BestEnemy = PotentialEnemy; BestDist = NewDist; } + + else if( BestEnemy != none ) + { + if( (BestDist > NewDist) || (NumberOfZedsTargetingPawn( PotentialEnemy ) < NumberOfZedsTargetingPawn( BestEnemy )) ) + { + BestEnemy = PotentialEnemy; + BestDist = NewDist; + } + } } } diff --git a/KFGame/Classes/KFAISpawnManager.uc b/KFGame/Classes/KFAISpawnManager.uc index 18f63aa..e9c54b1 100644 --- a/KFGame/Classes/KFAISpawnManager.uc +++ b/KFGame/Classes/KFAISpawnManager.uc @@ -143,6 +143,8 @@ var const int ForcedBossNum; var bool bTemporarilyEndless; +var int VIP_CurrentSpawnCounter; +var int VIP_MaxSpawnCounter; /************************************************************************************ * Debugging @@ -154,6 +156,51 @@ var config bool bLogRateVolume; /** Builds a sorted list of spawn volumes based on distance to a specific player */ native function bool SortSpawnVolumes(Controller C, bool bTeleporting, float MinDistSquared); +delegate int SortVIPSpawnVolumesDelegate(KFSpawnVolume A, KFSpawnVolume B) +{ + if (A.CurrentRating == B.CurrentRating) + { + return 0; + } + + if (A.CurrentRating < B.CurrentRating) + { + return 1; + } + + return -1; +} + +function SortVIPSpawnVolumes() +{ + local KFGameReplicationInfo KFGRI; + local int VolumeIndex; + + if (VIP_CurrentSpawnCounter < VIP_MaxSpawnCounter) + { + ++VIP_CurrentSpawnCounter; + return; + } + + VIP_CurrentSpawnCounter = 0; + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + if (KFGRI != none && KFGRI.VIPRepPlayer != none) + { + // Recalculate rating based on the distance to VIP player + for (VolumeIndex = 0; VolumeIndex < SpawnVolumes.Length; VolumeIndex++) + { + if (SpawnVolumes[VolumeIndex].CurrentRating > 0) + { + SpawnVolumes[VolumeIndex].CurrentRating = VSizeSq( SpawnVolumes[VolumeIndex].Location - KFGRI.VIPRepPlayer.Location ); + } + } + } + + // Sort vector based on closest + SpawnVolumes.Sort(SortVIPSpawnVolumesDelegate); +} + static function string ZedTypeToString(EAIType AiTypeToConvert) { `if(`notdefined(ShippingPC)) @@ -226,8 +273,11 @@ function RegisterSpawnVolumes() function SetupNextWave(byte NextWaveIndex, int TimeToNextWaveBuffer = 0) { local KFGameReplicationInfo KFGRI; + local bool bIsBossRush; - if (OutbreakEvent.ActiveEvent.bBossRushMode) + bIsBossRush = OutbreakEvent != none && OutbreakEvent.ActiveEvent.bBossRushMode; + + if (bIsBossRush) { NextWaveIndex = MyKFGRI.WaveMax - 1; } @@ -255,7 +305,7 @@ function SetupNextWave(byte NextWaveIndex, int TimeToNextWaveBuffer = 0) // Initialize our recycle number NumSpecialSquadRecycles = 0; - if (MyKFGRI.IsBossWave() || OutbreakEvent.ActiveEvent.bBossRushMode) + if (MyKFGRI.IsBossWave() || bIsBossRush) { WaveTotalAI = 1; } @@ -1208,6 +1258,7 @@ function KFSpawnVolume GetBestSpawnVolume( optional array< class { local int VolumeIndex, ControllerIndex; local Controller RateController; + local KFGameReplicationInfo KFGRI; if( OverrideController != none ) { @@ -1250,6 +1301,13 @@ function KFSpawnVolume GetBestSpawnVolume( optional array< class // pre-sort the list to reduce the number of line checks performed by IsValidForSpawn SortSpawnVolumes(RateController, bTeleporting, MinDistSquared); + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + if (KFGRI != none && KFGRI.IsVIPMode()) + { + SortVIPSpawnVolumes(); + } + for ( VolumeIndex = 0; VolumeIndex < SpawnVolumes.Length; VolumeIndex++ ) { if ( SpawnVolumes[VolumeIndex].IsValidForSpawn(DesiredSquadType, OtherController) @@ -1396,5 +1454,8 @@ defaultproperties MaxBossMinionScaleByPlayers(5)=2.0 // 6 players bForceRequiredSquad=false + + VIP_CurrentSpawnCounter = 0 + VIP_MaxSpawnCounter = 5 } diff --git a/KFGame/Classes/KFCommon_LocalizedStrings.uc b/KFGame/Classes/KFCommon_LocalizedStrings.uc index 8b38c9d..6c0b448 100644 --- a/KFGame/Classes/KFCommon_LocalizedStrings.uc +++ b/KFGame/Classes/KFCommon_LocalizedStrings.uc @@ -19,8 +19,15 @@ var localized string FailedToReachInventoryServerString; var localized array DifficultyStrings; var localized array LengthStrings; var localized string SpecialLengthString; + var localized string WeaponLevelString; var localized string GunPointsString; + +var localized string VIPString; +var localized string VIPObjectiveAString; +var localized string VIPObjectiveBString; +var localized string VIPObjectiveCString; + var localized array ServerTypeStrings; var localized array PermissionStrings; var localized array ConsolePermissionStrings; diff --git a/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc b/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc new file mode 100644 index 0000000..b81de2b --- /dev/null +++ b/KFGame/Classes/KFDT_Toxic_HRG_Locust.uc @@ -0,0 +1,36 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Locust +//============================================================================= +// HRG Locust bullet impact +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Toxic_HRG_Locust extends KFDT_Bleeding + abstract + hidedropdown; + +// Damage dealt when zeds touch each other and spread the afflictions +var int SpreadOnTouchDamage; + +static function int GetSpreadOnTouchDamage() +{ + return default.SpreadOnTouchDamage; +} + +defaultproperties +{ + DoT_Type=DOT_Bleeding + DoT_Duration=3.0 + DoT_Interval=0.5 + DoT_DamageScale=1.0 + + BleedPower = 20 + PoisonPower = 25 + + ModifierPerkList(0)=class'KFPerk_Survivalist' + + WeaponDef=class'KFWeapDef_HRG_Locust' + + SpreadOnTouchDamage=40 +} \ No newline at end of file diff --git a/KFGame/Classes/KFDamageType.uc b/KFGame/Classes/KFDamageType.uc index 87e8d12..ce94a48 100644 --- a/KFGame/Classes/KFDamageType.uc +++ b/KFGame/Classes/KFDamageType.uc @@ -40,6 +40,9 @@ var bool bConsideredIndirectOrAoE; /** If true, this damagetype will destroy a door if closed and unwelded */ var bool bAllowAIDoorDestruction; +/** If true can PlayDeadHitEffects the Zeds when killing them */ +var bool bCanPlayDeadHitEffects; + /********************************************************************************************* Damage over time ********************************************************************************************* */ @@ -233,6 +236,9 @@ var AKEvent OverrideImpactSound; //For use on in world trap damage types var bool bIsTrapDamage; +//When doing armour piercing damage (this is a % of total damage received, the rest is considered as base if defined) +var float DamageModifierAP; + /** * Take the primary HitDirection and modify it to add more spread. * Use the BloodSpread property to calculate the spread amount @@ -421,6 +427,11 @@ static function bool AlwaysPoisons() /** Do anything related to killing a pawn */ static function ApplyKillResults(KFPawn KilledPawn); +static function bool CanPlayDeadHitEffects() +{ + return default.bCanPlayDeadHitEffects; +} + Defaultproperties { bNoPain=false @@ -444,4 +455,8 @@ Defaultproperties bCanObliterate=false; bHasToSpawnMicrowaveFire=true + + DamageModifierAP=0.f + + bCanPlayDeadHitEffects=true } diff --git a/KFGame/Classes/KFExplosionActorLingering.uc b/KFGame/Classes/KFExplosionActorLingering.uc index 5629cc3..e54e22a 100644 --- a/KFGame/Classes/KFExplosionActorLingering.uc +++ b/KFGame/Classes/KFExplosionActorLingering.uc @@ -121,14 +121,14 @@ protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCau if(bOnlyDamagePawns) { - return ExplodePawns(); + return ExplodePawns(bCauseDamage); } return super.DoExplosionDamage(bCauseDamage, bCauseEffects); } /** Stripped down and optimized version of DoExplosionDamage that only checks for pawns */ -protected simulated function bool ExplodePawns() +protected simulated function bool ExplodePawns(bool bCauseDamage) { local Pawn Victim; local float CheckRadius; @@ -171,6 +171,11 @@ protected simulated function bool ExplodePawns() DamageScale = (DamageScalePerStack < 1.f) ? CalcStackingDamageScale(KFPawn(Victim), Interval) : 1.f; if ( DamageScale > 0.f ) { + if (bCauseDamage == false) + { + DamageScale = 0.f; // We still want effects + } + AffectsPawn(Victim, DamageScale); bHitPawn = true; } diff --git a/KFGame/Classes/KFExplosion_Nuke.uc b/KFGame/Classes/KFExplosion_Nuke.uc index 089aa8d..5db63d5 100644 --- a/KFGame/Classes/KFExplosion_Nuke.uc +++ b/KFGame/Classes/KFExplosion_Nuke.uc @@ -38,7 +38,7 @@ simulated function Explode(GameExplosion NewExplosionTemplate, optional vector D ExplosionTemplate.MyDamageType = class'KFPerk_Demolitionist'.static.GetLingeringDamageType(); } -protected simulated function bool ExplodePawns() +protected simulated function bool ExplodePawns(bool bCauseDamage) { local Pawn Victim; local float CheckRadius; diff --git a/KFGame/Classes/KFExplosion_ZedativeCloud.uc b/KFGame/Classes/KFExplosion_ZedativeCloud.uc index 780d053..527fe58 100644 --- a/KFGame/Classes/KFExplosion_ZedativeCloud.uc +++ b/KFGame/Classes/KFExplosion_ZedativeCloud.uc @@ -26,6 +26,8 @@ var class ZedativeDamageType; var class ZedativeHealingType; var int ZedativeEffectRadius; +var array AffectedHumans; + replication { if(bNetInitial) @@ -79,16 +81,16 @@ protected simulated function AffectsPawn(Pawn Victim, float DamageScale) local Actor HitActor; local bool bDamageBlocked; + if( bWasFadedOut|| bDeleteMe || bPendingDelete ) + { + return; + } + if( Victim != none && Victim.IsAliveAndWell() ) { MonsterVictim = KFPawn_Monster(Victim); if( MonsterVictim != none ) - { - if( bWasFadedOut|| bDeleteMe || bPendingDelete ) - { - return; - } - + { Victim.GetComponentsBoundingBox(BBox); BBoxCenter = (BBox.Min + BBox.Max) * 0.5f; HitActor = TraceExplosive(BBoxCenter, Location + vect(0, 0, 20)); @@ -109,7 +111,12 @@ protected simulated function AffectsPawn(Pawn Victim, float DamageScale) HumanVictim = KFPawn_Human(Victim); if( HumanVictim != none && HumanVictim.GetExposureTo(Location) > 0 ) { - HumanVictim.HealDamage(ZedativeHealth, InstigatorController, ZedativeHealingType, false); + if (AffectedHumans.Find(HumanVictim) == INDEX_NONE) + { + AffectedHumans.AddItem(HumanVictim); + + HumanVictim.HealDamage(ZedativeHealth, InstigatorController, ZedativeHealingType, false); + } } } } diff --git a/KFGame/Classes/KFGFxControlsContainer_Input.uc b/KFGame/Classes/KFGFxControlsContainer_Input.uc index 45275ee..3da1dce 100644 --- a/KFGame/Classes/KFGFxControlsContainer_Input.uc +++ b/KFGame/Classes/KFGFxControlsContainer_Input.uc @@ -31,6 +31,10 @@ var localized string AimAssistLockOnString; var localized string AimAssistRotationString; var localized string AimAssistSlowDownString; var localized string ForceFeedbackString; +var localized string MouseLookUpScaleString; +var localized string MouseLookRightScaleString; +var localized string ViewSmoothingString; +var localized string ViewAccelerationString; var KFGFxOptionsMenu_Controls ControlsMenu; @@ -61,6 +65,14 @@ function LocalizeText() LocalizedObject.SetString("controllerDeadzoneLabel" , ControllerDeadzoneString); LocalizedObject.SetString("controllerAccelerationJumpLabel" , ControllerAccelerationJumpString); + if(!class'WorldInfo'.static.IsConsoleBuild() ) + { + LocalizedObject.SetString("lookUpScaleLabel", MouseLookUpScaleString); + LocalizedObject.SetString("lookRightScaleLabel", MouseLookRightScaleString); + LocalizedObject.SetString("viewSmoothingLabel", ViewSmoothingString); + LocalizedObject.SetString("viewAccelerationLabel", ViewAccelerationString); + } + // Localization alternative for Xbox if( class'WorldInfo'.static.IsConsoleBuild(CONSOLE_Durango) ) { @@ -103,9 +115,20 @@ function InitializeOptions() ValuesObject.SetBool("invertedValue" , KFPI.bInvertMouse); ValuesObject.SetBool("mouseSmoothingLabel" , KFPI.bEnableMouseSmoothing); - + + ValuesObject.SetFloat("lookUpScaleValue" , -KFPI.MouseLookUpScale); + ValuesObject.SetFloat("lookUpScaleMin" , ControlsMenu.MinMouseLookUpScale); + ValuesObject.SetFloat("lookUpScaleMax" , ControlsMenu.MaxMouseLookUpScale); + + ValuesObject.SetFloat("lookRightScaleValue" , KFPI.MouseLookRightScale); + ValuesObject.SetFloat("lookRightScaleMin" , ControlsMenu.MinMouseLookRightScale); + ValuesObject.SetFloat("lookRightScaleMax" , ControlsMenu.MaxMouseLookRightScale); + + ValuesObject.SetBool("viewSmoothingValue" , KFPI.bViewSmoothingEnabled); + ValuesObject.SetBool("viewAccelerationValue" , KFPI.bViewAccelerationEnabled); } + ValuesObject.SetBool("forceFeedbackValue" , KFPI.bForceFeedbackEnabled); ValuesObject.SetFloat("controllerSensitivityValue" , 100 * KFPI.GamepadSensitivityScale); @@ -152,6 +175,23 @@ function ResetInputOptions() KFPI.bEnableMouseSmoothing = ControlsMenu.Manager.CachedProfile.GetDefaultBool(KFID_EnableMouseSmoothing); ControlsMenu.Manager.CachedProfile.SetProfileSettingValueBool(KFID_EnableMouseSmoothing, KFPI.bEnableMouseSmoothing); + + KFPI.MouseLookUpScale = ControlsMenu.Manager.CachedProfile.GetDefaultFloat(KFID_MouseLookUpScale); + ControlsMenu.Manager.CachedProfile.SetProfileSettingValueFloat(KFID_MouseLookUpScale, KFPI.MouseLookUpScale); + KFPI.LookUpScale = KFPI.MouseLookUpScale; + class'PlayerInput'.default.LookUpScale = KFPI.MouseLookUpScale; + + KFPI.MouseLookRightScale = ControlsMenu.Manager.CachedProfile.GetDefaultFloat(KFID_MouseLookRightScale); + ControlsMenu.Manager.CachedProfile.SetProfileSettingValueFloat(KFID_MouseLookRightScale, KFPI.MouseLookRightScale); + KFPI.LookRightScale = KFPI.MouseLookRightScale; + class'PlayerInput'.default.LookRightScale = KFPI.MouseLookRightScale; + class'PlayerInput'.static.StaticSaveConfig(); + + KFPI.bViewSmoothingEnabled = ControlsMenu.Manager.CachedProfile.GetDefaultBool(KFID_ViewSmoothingEnabled); + ControlsMenu.Manager.CachedProfile.SetProfileSettingValueBool(KFID_ViewSmoothingEnabled, KFPI.bViewSmoothingEnabled); + + KFPI.bViewAccelerationEnabled = ControlsMenu.Manager.CachedProfile.GetDefaultBool(KFID_ViewAccelerationEnabled); + ControlsMenu.Manager.CachedProfile.SetProfileSettingValueBool(KFID_ViewAccelerationEnabled, KFPI.bViewAccelerationEnabled); } //durango diff --git a/KFGame/Classes/KFGFxHUD_PlayerStatus.uc b/KFGame/Classes/KFGFxHUD_PlayerStatus.uc index 9e510a0..d8a30ec 100644 --- a/KFGame/Classes/KFGFxHUD_PlayerStatus.uc +++ b/KFGame/Classes/KFGFxHUD_PlayerStatus.uc @@ -30,6 +30,8 @@ var int LastEXPValue; var localized string EXPString; +var float LastUpdateTime; + function InitializeHUD() { MyPC = KFPlayerController(GetPC()); @@ -48,9 +50,11 @@ function TickHud(float DeltaTime) UpdateArmor(); UpdateHealer(); UpdateGlobalDamage(); + + LastUpdateTime = MyPC.WorldInfo.TimeSeconds; } -function ShowActiveIndicators( array IconPathStrings ) +function ShowActiveIndicators( array ActiveSkills ) { local byte i; local GFxObject DataProvider; @@ -58,11 +62,14 @@ function ShowActiveIndicators( array IconPathStrings ) DataProvider = CreateArray(); - for (i = 0; i < IconPathStrings.length; i++) + for (i = 0; i < ActiveSkills.length; i++) { //Corresponding AS3 class reads string off of the object to load in icon TempObj = CreateObject( "Object" ); - TempObj.SetString( "iconPath", "img://"$IconPathStrings[i] ); + TempObj.SetString( "iconPath", "img://" $ActiveSkills[i].IconPath ); + TempObj.SetInt( "Multiplier", ActiveSkills[i].Multiplier ); + TempObj.SetFloat( "MaxDuration", ActiveSkills[i].MaxDuration ); + TempObj.SetFloat( "Duration", ActiveSkills[i].Duration ); DataProvider.SetElementObject( i, TempObj ); } @@ -229,5 +236,5 @@ function UpdateGlobalDamage() DefaultProperties { - + LastUpdateTime = 0.f; } diff --git a/KFGame/Classes/KFGFxMenu_Inventory.uc b/KFGame/Classes/KFGFxMenu_Inventory.uc index b99867f..4595cdb 100644 --- a/KFGame/Classes/KFGFxMenu_Inventory.uc +++ b/KFGame/Classes/KFGFxMenu_Inventory.uc @@ -141,12 +141,21 @@ struct InventoryHelper var int SkinType; var ItemRarity Rarity; var int Quality; + + // For ordering items + var string KeyName; + var bool IsKey; }; var array SkinListWeaponsSearchCache; var array SkinListOrderedCache; var bool NeedToRegenerateSkinList; +struct ByTypeItemsHelper +{ + var() array ItemsOnType; +}; + var EInventoryWeaponType_Filter CurrentWeaponTypeFilter; var int CurrentPerkIndexFilter; var ItemRarity CurrentRarityFilter; @@ -282,41 +291,12 @@ final function int Crc(coerce string Text) return CrcValue; } -delegate int SortByWeaponTypeDefinition(InventoryHelper A, InventoryHelper B) +delegate int SortSkinList(InventoryHelper A, InventoryHelper B) { - return A.WeaponDef < B.WeaponDef ? -1 : +1; -} - -delegate int SortByPrice(InventoryHelper A, InventoryHelper B) -{ - return A.Price > B.Price ? -1 : +1; -} - -delegate int SortByRarity(InventoryHelper A, InventoryHelper B) -{ - return A.Rarity > B.Rarity ? -1 : +1; -} - -delegate int SortBySkinType(InventoryHelper A, InventoryHelper B) -{ - return A.SkinType > B.SkinType ? -1 : +1; -} - -delegate int SortByQuality(InventoryHelper A, InventoryHelper B) -{ - return A.Quality < B.Quality ? -1 : +1; -} - -delegate int SortByAll(InventoryHelper A, InventoryHelper B) -{ - //local int WeapDefValue, SkinTypeValue; - /** Format: Compare lower ? -1 : (Compare upper) : 1 : (Equal case, repeat formula with the next sort condition) */ return A.Price > B.Price ? -1 : (A.Price < B.Price ? 1 : ( - //STRCompare(A.WeaponDef, B.WeaponDef, WeapDefValue) < 0 ? -1 : ( WeapDefValue != 0 ? 1 : ( A.WeaponDef < B.WeaponDef ? -1 : (A.WeaponDef > B.WeaponDef ? 1 : ( A.Rarity > B.Rarity ? -1 : (A.Rarity < B.Rarity ? 1 : ( - //STRCompare(A.SkinType, B.SkinType, SkinTypeValue) > 0 ? -1 : ( SkinTypeValue != 0 ? 1 : ( A.SkinType > B.SkinType ? -1 : (A.SkinType < B.SkinType ? 1 : ( A.Quality < B.Quality ? -1 : 1 )) @@ -325,13 +305,33 @@ delegate int SortByAll(InventoryHelper A, InventoryHelper B) )); } +delegate int SortItemList(InventoryHelper A, InventoryHelper B) +{ + if (A.IsKey && B.IsKey) + { + return 0; + } + + if (A.IsKey == false && B.IsKey == false) + { + return 0; + } + + if (A.IsKey) + { + return 1; + } + + return -1; +} + function InitInventory() { - local int i, ItemIndex, HelperIndex, WeaponItemID, SearchWeaponSkinIndex; + local int i, j, z, ItemIndex, HelperIndex, WeaponItemID, SearchWeaponSkinIndex, SearchKeyKeywordIndex; local ItemProperties TempItemDetailsHolder; local GFxObject ItemArray, ItemObject; local bool bActiveItem; - local array ActiveItems, ValidSkinItems, FailedSkinItems; + local ByTypeItemsHelper ByTypeItems[7]; local InventoryHelper HelperItem; local array ExchangeRules; local class WeaponDef; @@ -348,6 +348,10 @@ function InitInventory() return; } + // While reading from the profile we also order by type, then we might want to order again some stuff that's inside the same item type later + + //`Log("NEW MENU OPEN: " $CurrentInventoryFilter); + for (i = 0; i < OnlineSub.CurrentInventory.length; i++) { //look item up to get info on it. @@ -358,22 +362,25 @@ function InitInventory() { TempItemDetailsHolder = OnlineSub.ItemPropertiesList[ItemIndex]; - if (((CurrentInventoryFilter == EInv_All || Int(CurrentInventoryFilter) == Int(TempItemDetailsHolder.Type)) && DoesMatchFilter(TempItemDetailsHolder)) || bool(OnlineSub.CurrentInventory[i].NewlyAdded)) + if (((CurrentInventoryFilter == EInv_All || Int(CurrentInventoryFilter) == Int(TempItemDetailsHolder.Type)) + && DoesMatchFilter(TempItemDetailsHolder)) + || bool(OnlineSub.CurrentInventory[i].NewlyAdded)) { - ItemObject = CreateObject("Object"); - HelperIndex = ActiveItems.Find('ItemDefinition', onlineSub.CurrentInventory[i].Definition); + ItemObject = CreateObject("Object"); + HelperIndex = ByTypeItems[TempItemDetailsHolder.Type].ItemsOnType.Find('ItemDefinition', onlineSub.CurrentInventory[i].Definition); if (HelperIndex == INDEX_NONE) { + HelperItem.Type = TempItemDetailsHolder.Type; + //HelperItem.FullName = TempItemDetailsHolder.Name; HelperItem.ItemDefinition = onlineSub.CurrentInventory[i].Definition; HelperItem.ItemCount = onlineSub.CurrentInventory[i].Quantity; if (TempItemDetailsHolder.Type == ITP_WeaponSkin) { // Copy required stuff - //HelperItem.FullName = TempItemDetailsHolder.Name; - HelperItem.Rarity = TempItemDetailsHolder.Rarity; - HelperItem.Quality = TempItemDetailsHolder.Quality; + HelperItem.Rarity = TempItemDetailsHolder.Rarity; + HelperItem.Quality = TempItemDetailsHolder.Quality; if (bool(OnlineSub.CurrentInventory[i].NewlyAdded)) { @@ -397,6 +404,7 @@ function InitInventory() // Get the left part of the string without the next "| " SearchWeaponSkinIndex = InStr(SkinType, "|"); + // Store as CRC, that speeds up comparisons later HelperItem.SkinType = CrC(Left(SkinType, SearchWeaponSkinIndex)); WeaponItemID = class'KFWeaponSkinList'.default.Skins.Find('Id', HelperItem.ItemDefinition); @@ -406,6 +414,7 @@ function InitInventory() WeaponDef = class'KFWeaponSkinList'.default.Skins[WeaponItemID].WeaponDef; // All Weapons start by KFGameContent.KFWeap_ Skip that prefix. + // Store as CRC, that speeds up comparisons later HelperItem.WeaponDef = CrC(Mid(WeaponDef.default.WeaponClassPath, 21)); HelperItem.Price = WeaponDef.default.BuyPrice; } @@ -418,18 +427,22 @@ function InitInventory() SkinListWeaponsSearchCache.AddItem(HelperItem); } } + else + { + HelperItem.KeyName = TempItemDetailsHolder.KeyName; + } - ActiveItems.AddItem(HelperItem); - HelperIndex = ActiveItems.length - 1; + ByTypeItems[TempItemDetailsHolder.Type].ItemsOnType.AddItem(HelperItem); + HelperIndex = ByTypeItems[TempItemDetailsHolder.Type].ItemsOnType.Length - 1; } else { - ActiveItems[HelperIndex].ItemCount += onlineSub.CurrentInventory[i].Quantity; + ByTypeItems[TempItemDetailsHolder.Type].ItemsOnType[HelperIndex].ItemCount += onlineSub.CurrentInventory[i].Quantity; } OnlineSub.IsExchangeable(onlineSub.CurrentInventory[i].Definition, ExchangeRules); - ItemObject.SetInt("count", ActiveItems[HelperIndex].ItemCount); + ItemObject.SetInt("count", ByTypeItems[TempItemDetailsHolder.Type].ItemsOnType[HelperIndex].ItemCount); ItemObject.SetString("label", TempItemDetailsHolder.Name); ItemObject.SetString("price", TempItemDetailsHolder.price); ItemObject.Setstring("typeRarity", TempItemDetailsHolder.ShortDescription); @@ -445,7 +458,7 @@ function InitInventory() ItemObject.SetInt("definition", TempItemDetailsHolder.Definition); ItemObject.SetBool("newlyAdded", bool(OnlineSub.CurrentInventory[i].NewlyAdded) ); - ActiveItems[HelperIndex].GfxItemObject = ItemObject; + ByTypeItems[TempItemDetailsHolder.Type].ItemsOnType[HelperIndex].GfxItemObject = ItemObject; if(onlineSub.CurrentInventory[i].Definition == Manager.SelectIDOnOpen) { @@ -465,86 +478,84 @@ function InitInventory() OnlineSub.ClearNewlyAdded(); - if (CurrentInventoryFilter == EInv_WeaponSkins) + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_WeaponSkins) { // If need to refresh... we regenerate the list, if not reuse our Cache - NeedToRegenerateSkinList = NeedToRegenerateSkinList || ActiveItems.Length != SkinListOrderedCache.Length; + NeedToRegenerateSkinList = NeedToRegenerateSkinList || ByTypeItems[ITP_WeaponSkin].ItemsOnType.Length != SkinListOrderedCache.Length; + if (NeedToRegenerateSkinList) { NeedToRegenerateSkinList = false; - /*`Log("START ORDERING!!!"); - `Log("----------");*/ - - for (i = 0 ; i < ActiveItems.Length; i++) - { - // If doesn't have weapon definition, don't consider, only add as FailedItem to be added at the end - if (ActiveItems[i].WeaponDef != -1) - { - ValidSkinItems.AddItem(ActiveItems[i]); - } - else - { - FailedSkinItems.AddItem(ActiveItems[i]); - } - } - - // Now we have all valid weapons - // We want to order by Price - Weapon Def - Rarity - Quality // So we order inverse that way we keep the final list with the design intention - ValidSkinItems.Sort(SortByAll); + ByTypeItems[ITP_WeaponSkin].ItemsOnType.Sort(SortSkinList); - SkinListOrderedCache = ValidSkinItems; + SkinListOrderedCache = ByTypeItems[ITP_WeaponSkin].ItemsOnType; + + /*`Log("----------");*/ /*for (i = 0 ; i < SkinListOrderedCache.Length; i++) { `Log("ID : " $SkinListOrderedCache[i].ItemDefinition); `Log("Weapon Def : " $SkinListOrderedCache[i].WeaponDef); `Log("Price : " $SkinListOrderedCache[i].Price); - //`Log("Full Name : " $SkinListOrderedCache[i].FullName); + `Log("Full Name : " $SkinListOrderedCache[i].FullName); `Log("Skin : " $SkinListOrderedCache[i].SkinType); `Log("Rarity : " $SkinListOrderedCache[i].Rarity); `Log("Quality : " $SkinListOrderedCache[i].Quality); `Log("----------"); - }*/ - - // FailedItems are weapons that don't have a Weapon Definition, this might be a case with old unsupported content? - for (i = 0; i < FailedSkinItems.Length; i++) - { - /*if (FailedSkinItems[i].FullName != "") - { - `Log("FailedSkinItems ID : " $FailedSkinItems[i].ItemDefinition); - `Log("FailedSkinItems Price : " $FailedSkinItems[i].Price); - `Log("FailedSkinItems Full Name : " $FailedSkinItems[i].FullName); - `Log("FailedSkinItems Skin : " $FailedSkinItems[i].SkinType); - `Log("FailedSkinItems Rarity : " $FailedSkinItems[i].Rarity); - `Log("FailedFailedSkinItemsItems Quality : " $FailedSkinItems[i].Quality); - `Log("----------"); - }*/ - - SkinListOrderedCache.AddItem(FailedSkinItems[i]); } - /*`Log("FINISH ORDERING!!!"); `Log("----------");*/ } else { //`Log("USING SKIN LIST CACHE!!!"); + + ByTypeItems[ITP_WeaponSkin].ItemsOnType = SkinListOrderedCache; + } + } + + if (CurrentInventoryFilter == EInv_All || CurrentInventoryFilter == EInv_Consumables) + { + // Consumables is the type for the "Items" category on the UI + + // First we have to distinguish if the Item is a KEY or not + for (i = 0; i < ByTypeItems[ITP_KeyCrate].ItemsOnType.Length; i++) + { + // KeyName is something like : "NameItem:KeyCrate", we first remove the part from the : to the right + SearchKeyKeywordIndex = InStr(ByTypeItems[ITP_KeyCrate].ItemsOnType[i].KeyName, ":"); + ByTypeItems[ITP_KeyCrate].ItemsOnType[i].KeyName = Left(ByTypeItems[ITP_KeyCrate].ItemsOnType[i].KeyName, SearchKeyKeywordIndex); + + // Then we search if the name of the Item contains "Key" + SearchKeyKeywordIndex = InStr(ByTypeItems[ITP_KeyCrate].ItemsOnType[i].KeyName, "Key"); + + if (SearchKeyKeywordIndex != -1) + { + ByTypeItems[ITP_KeyCrate].ItemsOnType[i].IsKey = true; + } + else + { + ByTypeItems[ITP_KeyCrate].ItemsOnType[i].IsKey = false; + } } - for (i = 0; i < SkinListOrderedCache.length; i++) - { - ItemArray.SetElementObject(i, SkinListOrderedCache[i].GfxItemObject); - } + ByTypeItems[ITP_KeyCrate].ItemsOnType.Sort(SortItemList); } - else + + //`Log("--------------------------------"); + + z = 0; + + for (i = 0; i < ArrayCount(ByTypeItems); i++) { - for (i = 0; i < ActiveItems.length; i++) + for (j = 0 ; j < ByTypeItems[i].ItemsOnType.Length; j++) { - ItemArray.SetElementObject(i, ActiveItems[i].GfxItemObject); + ItemArray.SetElementObject(z, ByTypeItems[i].ItemsOnType[j].GfxItemObject); + + ++z; } } diff --git a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc index cb22f53..253a7eb 100644 --- a/KFGame/Classes/KFGFxMoviePlayer_HUD.uc +++ b/KFGame/Classes/KFGFxMoviePlayer_HUD.uc @@ -72,7 +72,8 @@ var KFGFxWidget_MapText MapTextWidget; var KFGFxWidget_MapCounterText MapCounterTextWidget; // Widget that displays gun mode texts var KFGFxWidget_GunGame GunGameWidget; - +// Widget that displays vip mode texts +var KFGFxWidget_VIP VIPWidget; var KFPlayerController KFPC; @@ -108,6 +109,9 @@ var string PendingKickPlayerName; // Gun game variables var transient bool bLastGunGameVisibility; +// VIP variables +var transient bool bLastVIPVisibility; + /** On creation of the HUD */ function Init(optional LocalPlayer LocPlay) { @@ -361,6 +365,13 @@ event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget) SetWidgetPathBinding( Widget, WidgetPath ); } break; + case 'VIPContainer': + if (VIPWidget == none) + { + VIPWidget=KFGFxWidget_VIP(Widget); + SetWidgetPathBinding( Widget, WidgetPath ); + } + break; } return true; @@ -389,7 +400,7 @@ function UpdateWeaponSelect() /** Update all the unique HUD pieces */ function TickHud(float DeltaTime) { - local bool bGunGameVisibility; + local bool bGunGameVisibility, bVIPModeVisibility; if(KFPC == none || KFPC.WorldInfo.TimeSeconds - LastUpdateTime < UpdateInterval ) { @@ -456,8 +467,6 @@ function TickHud(float DeltaTime) TraderCompassWidget.TickHUD( DeltaTime); } - - if(GfxScoreBoardPlayer != none) { GfxScoreBoardPlayer.TickHud(DeltaTime); @@ -478,6 +487,17 @@ function TickHud(float DeltaTime) bLastGunGameVisibility = bGunGameVisibility; } } + + if (VIPWidget != none) + { + bVIPModeVisibility = KFPC.CanUseVIP(); + + if (bVIPModeVisibility != bLastVIPVisibility) + { + VIPWidget.UpdateVIPVisibility(bVIPModeVisibility); + bLastVIPVisibility = bVIPModeVisibility; + } + } } function UpdateObjectiveActive() @@ -1141,6 +1161,26 @@ function UpdateGunGameWidget(int score, int max_score, int level, int max_level) } } +function UpdateVIP(ReplicatedVIPGameInfo VIPInfo, bool bIsVIP) +{ + local KFGameReplicationInfo KFGRI; + KFGRI=KFGameReplicationInfo(KFPC.WorldInfo.GRI); + + if (VipWidget == none || KFGRI == none || !KFGRI.IsVIPMode()) + { + return; + } + + if (bIsVIP) + { + VIPWidget.SetVIP(); + } + else if (VipInfo.VIPPlayer != none) + { + VIPWidget.SetNOVIP(VIPInfo.VIPPlayer.PlayerName, VIPInfo.CurrentHealth, VIPInfo.MaxHealth); + } +} + //============================================================== // Input //============================================================== @@ -1303,8 +1343,6 @@ function UpdatePauseGameVoteCount(byte YesVotes, byte NoVotes) { if(KickVoteWidget != none) { - `Log("UPDATING PAUSE GAME VOTE COUNT - YES: "$YesVotes); - `Log("UPDATING PAUSE GAME VOTE COUNT - NO: "$NoVotes); KickVoteWidget.UpdateVoteCount(YesVotes, NoVotes); } } @@ -1492,6 +1530,7 @@ DefaultProperties bIsSpectating=false bLastGunGameVisibility=true + bLastVIPVisibility=true WidgetBindings.Add((WidgetName="ObjectiveContainer",WidgetClass=class'KFGFxHUD_ObjectiveConatiner')) WidgetBindings.Add((WidgetName="SpectatorInfoWidget",WidgetClass=class'KFGFxHUD_SpectatorInfo')) @@ -1518,7 +1557,7 @@ DefaultProperties WidgetBindings.Add((WidgetName="mapTextWidget", WidgetClass=class'KFGFxWidget_MapText')) WidgetBindings.Add((WidgetName="counterMapTextWidget", WidgetClass=class'KFGFxWidget_MapCounterText')) WidgetBindings.ADD((WidgetName="GunGameContainer", WidgetClass=class'KFGFxWidget_GunGame')); - + WidgetBindings.ADD((WidgetName="VIPContainer", WidgetClass=class'KFGFxWidget_VIP')); SpecialWaveIconPath(AT_Clot)="UI_Endless_TEX.ZEDs.UI_ZED_Endless_Cyst" SpecialWaveIconPath(AT_SlasherClot)="UI_Endless_TEX.ZEDs.UI_ZED_Endless_Slasher" diff --git a/KFGame/Classes/KFGFxOptionsMenu_Controls.uc b/KFGame/Classes/KFGFxOptionsMenu_Controls.uc index dbafca6..b9067e3 100644 --- a/KFGame/Classes/KFGFxOptionsMenu_Controls.uc +++ b/KFGame/Classes/KFGFxOptionsMenu_Controls.uc @@ -28,6 +28,10 @@ var const float MinMouseLookSensitivity; var const float MaxMouseLookSensitivity; var const float MinMouseLookZoomSensitivity; var const float MaxMouseLookZoomSensitivity; +var const float MinMouseLookUpScale; +var const float MaxMouseLookUpScale; +var const float MinMouseLookRightScale; +var const float MaxMouseLookRightScale; var localized array TabStrings; var localized string HeaderText; @@ -330,6 +334,55 @@ function CallBack_ResetInputOptions() } } +function Callback_MouseLookUpScale(float NewValue) +{ + local KFPlayerInput KFPI; + + NewValue = -NewValue; + + KFPI = KFPlayerInput(GetPC().PlayerInput); + KFPI.MouseLookUpScale = NewValue; + KFPI.LookUpScale = NewValue; + class'PlayerInput'.default.LookUpScale = KFPI.MouseLookUpScale; + class'PlayerInput'.static.StaticSaveConfig(); + + Manager.CachedProfile.SetProfileSettingValueFloat(KFID_MouseLookUpScale, NewValue); +} + +function Callback_MouseLookRightScale(float NewValue) +{ + local KFPlayerInput KFPI; + + KFPI = KFPlayerInput(GetPC().PlayerInput); + KFPI.MouseLookRightScale = NewValue; + KFPI.LookRightScale = NewValue; + class'PlayerInput'.default.LookRightScale = KFPI.MouseLookRightScale; + class'PlayerInput'.static.StaticSaveConfig(); + + Manager.CachedProfile.SetProfileSettingValueFloat(KFID_MouseLookRightScale, NewValue); + +} + +function Callback_ViewSmoothingChanged(bool bActive) +{ + local KFPlayerInput KFPI; + + KFPI = KFPlayerInput(GetPC().PlayerInput); + KFPI.bViewSmoothingEnabled = bActive; + + Manager.CachedProfile.SetProfileSettingValueInt(KFID_ViewSmoothingEnabled, bActive ? 1 : 0); +} + +function Callback_ViewAccelerationChanged(bool bActive) +{ + local KFPlayerInput KFPI; + + KFPI = KFPlayerInput(GetPC().PlayerInput); + KFPI.bViewAccelerationEnabled = bActive; + + Manager.CachedProfile.SetProfileSettingValueInt(KFID_ViewAccelerationEnabled, bActive ? 1 : 0); +} + defaultproperties { MinControllerLookSensitivity=.4 @@ -345,6 +398,11 @@ defaultproperties MinMouseLookZoomSensitivity=.2 MaxMouseLookZoomSensitivity=1 + MinMouseLookUpScale=20 + MaxMouseLookUpScale=500 + MinMouseLookRightScale=20 + MaxMouseLookRightScale=500 + SubWidgetBindings.Add((WidgetName="keybindingsContainer",WidgetClass=class'KFGFxControlsContainer_Keybinding')) SubWidgetBindings.Add((WidgetName="inputContainer",WidgetClass=class'KFGFxControlsContainer_Input')) SubWidgetBindings.Add((WidgetName="controllerPresetsContainer",WidgetClass=class'KFGFxControlsContainer_ControllerPresets')) diff --git a/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Fall2022.uc b/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Fall2022.uc new file mode 100644 index 0000000..b4682d3 --- /dev/null +++ b/KFGame/Classes/KFGFxSpecialEventObjectivesContainer_Fall2022.uc @@ -0,0 +1,27 @@ +class KFGFXSpecialEventObjectivesContainer_Fall2022 extends KFGFxSpecialEventObjectivesContainer; + +function Initialize(KFGFxObject_Menu NewParentMenu) +{ + super.Initialize(NewParentMenu); +} + +DefaultProperties +{ + ObjectiveIconURLs[0] = "Halloween2022_UI.UI_Objectives_Halloween2022_CreaturesInDarkness" // Kill 15 Bosses on any map or mode + ObjectiveIconURLs[1] = "Spring_UI.UI_Objectives_Spring_Weekly" // Complete the Weekly on BarmwichTown + ObjectiveIconURLs[2] = "Halloween2022_UI.UI_Objetives_Halloween2022_ElementalMedallions" // Open the Weapon Room + ObjectiveIconURLs[3] = "Halloween2022_UI.UI_Objectives_Halloween2022_ZedOnFire" // Make 50 Zeds to pass through the bonfires of Barmwitch Town + ObjectiveIconURLs[4] = "Halloween2022_UI.UI_Objetives_Halloween2022_LongNightofWitches" // Complete wave 15 on Endless Hard or higher difficulty on Barmwitch Town + + //defaults + AllCompleteRewardIconURL="CHR_PlagueDoctorUniform_Item_TEX.potionbackpack.plaguedoctorbackpack_precious_large" + ChanceDropIconURLs[0]="CHR_CosmeticSet14_Item_TEX.Tickets.CyberPunk_ticket" + ChanceDropIconURLs[1]="CHR_CosmeticSet14_Item_TEX.Tickets.CyberPunk_ticket_golden" + IconURL="halloween2022_ui.KF2_Halloween_BloodandBonfires_SmallLogo" + + UsesProgressList[0] = true + UsesProgressList[1] = false + UsesProgressList[2] = false + UsesProgressList[3] = true + UsesProgressList[4] = false +} \ No newline at end of file diff --git a/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc b/KFGame/Classes/KFGFxStartGameContainer_FindGame.uc index 0812078..a9b76d4 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_Summer2022_TidalTerror", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_BloodBonfire", "LatestUpdate", "http://www.tripwireinteractive.com/redirect/KF2LatestUpdate/"); WhatsNewItems.AddItem(item); // Featured Ultimate Edition item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_UltimateEdition_Upgrade", "FeaturedItemBundle", "https://store.steampowered.com/app/1914560/KF2__Ultimate_Edition_Upgrade_DLC/"); @@ -194,31 +194,31 @@ function FillWhatsNew() item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Spring_Armory_Season_Pass", "ArmorySeasonPass", "https://store.steampowered.com/app/1524820/Killing_Floor_2__Armory_Season_Pass"); WhatsNewItems.AddItem(item); // Featured Weapon Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_Weaponsbundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9369"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Blodd_Bornfires_Weapon_Bundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9471"); WhatsNewItems.AddItem(item); // Featured Weapon - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_ReductoRay", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9367"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Blood_Sickle_Weapon_bundle", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9469"); WhatsNewItems.AddItem(item); // Featured Weapon - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_Sentinel","FeaturedItemBundle","https://store.steampowered.com/buyitem/232090/9368"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_G36C_Weapon_Bundle","FeaturedItemBundle","https://store.steampowered.com/buyitem/232090/9467"); WhatsNewItems.AddItem(item); // Featured Outfit Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_DeepSea_Explorer_Uniforms", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9366"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_PlagueDoctor_Uniforms", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9465"); 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_Summer2022_DeepSea_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9364"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_Plague_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9457"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_NeonMKVIII_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9362"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_Weapon_skins_XenoPack", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9459"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_Classic_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9363"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_Classic2_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9461"); WhatsNewItems.AddItem(item); // Featured Weapon Skin Bundle - item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Summer2022_Chameleon_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9365"); + item = SetWhatsNewItem("img://UI_WhatsNew.UI_WhatsNew_Halloween2022_Chamaleon2_Weapon_Skin", "FeaturedItemBundle", "https://store.steampowered.com/buyitem/232090/9463"); 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/KFGFxStoreContainer_Main.uc b/KFGame/Classes/KFGFxStoreContainer_Main.uc index f8c1b8c..19195b3 100644 --- a/KFGame/Classes/KFGFxStoreContainer_Main.uc +++ b/KFGame/Classes/KFGFxStoreContainer_Main.uc @@ -447,25 +447,25 @@ DefaultProperties XboxFilterExceptions[0]="Wasteland Bundle" // Wasteland Outfit Bundle - FeaturedItemIDs[0]=8178 //Whatsnew Gold Ticket - FeaturedItemIDs[1]=9369 - FeaturedItemIDs[2]=9367 - FeaturedItemIDs[3]=9368 - FeaturedItemIDs[4]=9366 - FeaturedItemIDs[5]=9364 - FeaturedItemIDs[6]=9362 - FeaturedItemIDs[7]=9363 - FeaturedItemIDs[8]=9365 + FeaturedItemIDs[0]=7944 //Whatsnew Gold Ticket + FeaturedItemIDs[1]=9471 + FeaturedItemIDs[2]=9469 + FeaturedItemIDs[3]=9467 + FeaturedItemIDs[4]=9465 + FeaturedItemIDs[5]=9457 + FeaturedItemIDs[6]=9459 + FeaturedItemIDs[7]=9461 + FeaturedItemIDs[8]=9463 - ConsoleFeaturedItemIDs[0]=8181 //Whatsnew Gold Ticket PSN - ConsoleFeaturedItemIDs[1]=9369 - ConsoleFeaturedItemIDs[2]=9367 - ConsoleFeaturedItemIDs[3]=9368 - ConsoleFeaturedItemIDs[4]=9366 - ConsoleFeaturedItemIDs[5]=9364 - ConsoleFeaturedItemIDs[6]=9362 - ConsoleFeaturedItemIDs[7]=9363 - ConsoleFeaturedItemIDs[8]=9365 + ConsoleFeaturedItemIDs[0]=7947 //Whatsnew Gold Ticket PSN + ConsoleFeaturedItemIDs[1]=9471 + ConsoleFeaturedItemIDs[2]=9469 + ConsoleFeaturedItemIDs[3]=9467 + ConsoleFeaturedItemIDs[4]=9465 + ConsoleFeaturedItemIDs[5]=9457 + ConsoleFeaturedItemIDs[6]=9459 + ConsoleFeaturedItemIDs[7]=9461 + ConsoleFeaturedItemIDs[8]=9463 MaxFeaturedItems=5 } \ No newline at end of file diff --git a/KFGame/Classes/KFGFxTraderContainer_PlayerInfo.uc b/KFGame/Classes/KFGFxTraderContainer_PlayerInfo.uc index bc6d04b..f4cd9c8 100644 --- a/KFGame/Classes/KFGFxTraderContainer_PlayerInfo.uc +++ b/KFGame/Classes/KFGFxTraderContainer_PlayerInfo.uc @@ -70,18 +70,25 @@ function SetPerkList() local GFxObject PerkObject; local GFxObject DataProvider; local KFPlayerController KFPC; - local byte i; + local byte i, counter; local int PerkPercent; + local KFGameReplicationInfo KFGRI; KFPC = KFPlayerController(GetPC()); if (KFPC != none) { DataProvider = CreateArray(); + KFGRI = KFGameReplicationInfo(KFPC.WorldInfo.GRI); + + counter = 0; for (i = 0; i < KFPC.PerkList.Length; i++) - { - - + { + if (KFGRI != none && !KFGRI.IsPerkAllowed(KFPC.PerkList[i].PerkClass)) + { + continue; + } + PerkObject = CreateObject( "Object" ); PerkObject.SetString("name", KFPC.PerkList[i].PerkClass.default.PerkName); PerkObject.SetString("perkIconSource", "img://"$KFPC.PerkList[i].PerkClass.static.GetPerkIconPath()); @@ -90,8 +97,12 @@ function SetPerkList() PerkPercent = KFPC.GetPerkLevelProgressPercentage(KFPC.PerkList[i].PerkClass); PerkObject.SetInt("perkXP", PerkPercent); + + PerkObject.SetInt("perkIndex", i); - DataProvider.SetElementObject(i, PerkObject); + DataProvider.SetElementObject(counter, PerkObject); + + ++counter; } SetObject("perkList", DataProvider); diff --git a/KFGame/Classes/KFGFxWidget_VIP.uc b/KFGame/Classes/KFGFxWidget_VIP.uc new file mode 100644 index 0000000..ad7359a --- /dev/null +++ b/KFGame/Classes/KFGFxWidget_VIP.uc @@ -0,0 +1,49 @@ +//============================================================================= +// KFGFxWidget_VIP +//============================================================================= +// HUD Widget that displays VIP messages to the player +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= + +class KFGFxWidget_VIP extends GFxObject; + +function SetVIP() +{ + SetString("VIPSetLocalised", Class'KFCommon_LocalizedStrings'.default.VIPString); + SetString("VIPObjectiveSetLocalised", Class'KFCommon_LocalizedStrings'.default.VIPObjectiveBString); + SetString("VIPPlayerSet", Class'KFCommon_LocalizedStrings'.default.VIPObjectiveCString); + + SetBool("VIPPlayerNameSetVisibility", true); + SetBool("VIPHideHealthBar", true); +} + +function SetNOVIP(string VIPPlayerName, int VIPCurrentHealth, int VIPMaxHealth) +{ + SetString("VIPSetLocalised", Class'KFCommon_LocalizedStrings'.default.VIPString); + SetString("VIPObjectiveSetLocalised", Class'KFCommon_LocalizedStrings'.default.VIPObjectiveAString); + SetString("VIPPlayerSet", VIPPlayerName); + + SetBool("VIPPlayerNameSetVisibility", true); + + UpdateHealth(VIPCurrentHealth, VIPMaxHealth); +} + +function UpdateVIPVisibility(bool visible) +{ + if (visible) + { + SetBool("VIPSetVisibility", true); + } + else + { + SetBool("VIPSetVisibility", false); + } +} + +function UpdateHealth(int VIPCurrentHealth, int VIPMaxHealth) +{ + SetFloat("VIPHealthBarPercentage", VIPMaxHealth != 0 ? (float(VIPCurrentHealth) / VIPMaxHealth) : 0.0f); +} \ No newline at end of file diff --git a/KFGame/Classes/KFGameInfo.uc b/KFGame/Classes/KFGameInfo.uc index 42128de..ea2c024 100644 --- a/KFGame/Classes/KFGameInfo.uc +++ b/KFGame/Classes/KFGameInfo.uc @@ -3902,6 +3902,11 @@ simulated function AddWeaponsFromSpawnList(KFPawn P); simulated function OverrideHumanDefaults(KFPawn_Human P); +/*********************************************** + * @name Helper for halloween 2022 barmwich bonfire seasonal objective + **********************************************/ +function ClearActorFromBonfire(Actor Other); + /*********************************************** * @name Damage Modifier for Event **********************************************/ diff --git a/KFGame/Classes/KFGameInfo_Entry.uc b/KFGame/Classes/KFGameInfo_Entry.uc index b151a8e..53ba891 100644 --- a/KFGame/Classes/KFGameInfo_Entry.uc +++ b/KFGame/Classes/KFGameInfo_Entry.uc @@ -484,11 +484,11 @@ auto State PendingMatch { local int SystemTimeMinutes; - // Update every 30 minutes using system clock for suspend mode. + // Update every minute using system clock for suspend mode. // Originally tried using GameEnding(), but the TitleData response // doesn't come back in time for the new map. SystemTimeMinutes = GetSystemTimeMinutes(); - if ( (SystemTimeMinutes - LastSystemTimeMinutes) >= 30 ) + if ( (SystemTimeMinutes - LastSystemTimeMinutes) >= 1 ) { class'KFGameEngine'.static.RefreshOnlineGameData(); LastSystemTimeMinutes = SystemTimeMinutes; diff --git a/KFGame/Classes/KFGameReplicationInfo.uc b/KFGame/Classes/KFGameReplicationInfo.uc index 6cfcea7..57291f7 100644 --- a/KFGame/Classes/KFGameReplicationInfo.uc +++ b/KFGame/Classes/KFGameReplicationInfo.uc @@ -364,6 +364,28 @@ var int CurrentWeeklyIndex; /** If true, force show skip time between waves ready button */ var bool bForceShowSkipTrader; +/** Struct with replicated information about the VIP mode */ +struct native ReplicatedVIPGameInfo +{ + var int CurrentHealth; + var int MaxHealth; + var KFPlayerReplicationInfo VIPPlayer; + + structdefaultproperties + { + VIPPlayer = none + CurrentHealth = 0 + MaxHealth = 0 + } +}; +var transient ReplicatedVIPGameInfo VIPModeData; + +/** Structs are sent as a pack through the network. Split it in variables for optimization. */ +var repnotify int VIPRepCurrentHealth; +var repnotify int VIPRepMaxHealth; +var repnotify KFPlayerReplicationInfo VIPRepPlayer; + + /************************************ * Steam heartbeat ************************************/ @@ -398,7 +420,7 @@ 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; //@HSL - JRO - 3/21/2016 - PS4 Sessions + CurrentWeeklyIndex, bIsEndlessPaused, bForceSkipTraderUI, VIPRepCurrentHealth, VIPRepMaxHealth, VIPRepPlayer; //@HSL - JRO - 3/21/2016 - PS4 Sessions if ( bNetInitial ) GameLength, WaveMax, bCustom, bVersusGame, TraderItems, GameAmmoCostScale, bAllowGrenadePurchase, MaxPerkLevel, bTradersEnabled, bForceShowSkipTrader; if ( bNetInitial || bNetDirty ) @@ -550,14 +572,26 @@ simulated event ReplicatedEvent(name VarName) { UpdatePerksAvailable(); } - else if (VarName == 'GunGameWavesCurrent') + else if (VarName == nameof(GunGameWavesCurrent)) { UpdateHUDWaveCount(); } - else if (VarName == 'bWaveGunGameIsFinal') + else if (VarName == nameof(bWaveGunGameIsFinal)) { UpdateHUDWaveCount(); } + else if (VarName == nameof(VIPRepCurrentHealth)) + { + UpdateVIPCurrentHealth(VIPRepCurrentHealth); + } + else if (VarName == nameof(VIPRepMaxHealth)) + { + UpdateVIPMaxHealth(VIPRepMaxHealth); + } + else if (VarName == nameof(VIPRepPlayer)) + { + UpdateVIPPlayer(VIPRepPlayer); + } else { super.ReplicatedEvent(VarName); @@ -2269,11 +2303,95 @@ simulated function NotifyWeeklyEventIndex(int EventIndex) } +/** VIP weekly */ +simulated function UpdateVIPMaxHealth(int NewMaxHealth) +{ + if (NewMaxHealth != VIPModeData.MaxHealth) + { + VIPModeData.MaxHealth = NewMaxHealth; + + if (Role == ROLE_Authority) + { + VIPRepMaxHealth = NewMaxHealth; + bNetDirty = true; + } + + if (WorldInfo.NetMode != NM_DedicatedServer) + { + UpdateVIPUI(); + } + } +} + +simulated function UpdateVIPCurrentHealth(int NewCurrentHealth) +{ + if (NewCurrentHealth != VIPModeData.CurrentHealth) + { + VIPModeData.CurrentHealth = NewCurrentHealth; + + if (Role == ROLE_Authority) + { + VIPRepCurrentHealth = NewCurrentHealth; + bNetDirty = true; + } + + if (WorldInfo.NetMode != NM_DedicatedServer) + { + UpdateVIPUI(); + } + } +} + +simulated function UpdateVIPPlayer(KFPlayerReplicationInfo NewVIPPlayer) +{ + if (NewVIPPlayer == none) + { + return; + } + + if (NewVIPPlayer != VIPModeData.VIPPlayer) + { + VIPModeData.VIPPlayer = NewVIPPlayer; + + if (Role == ROLE_Authority) + { + VIPRepPlayer = NewVIPPlayer; + bNetDirty = true; + } + + if (WorldInfo.NetMode != NM_DedicatedServer) + { + UpdateVIPUI(); + } + } +} + +simulated function UpdateVIPUI() +{ + local KFPlayerController_WeeklySurvival KFPC_WS; + + if( WorldInfo.NetMode == NM_DedicatedServer ) + { + return; + } + + KFPC_WS = KFPlayerController_WeeklySurvival(GetALocalPlayerController()); + if (KFPC_WS != none) + { + KFPC_WS.UpdateVIPWidget(VIPModeData); + } +} + simulated function bool IsGunGameMode() { return bIsWeeklyMode && CurrentWeeklyIndex == 16; } +simulated function bool IsVIPMode() +{ + return bIsWeeklyMode && CurrentWeeklyIndex == 17; +} + defaultproperties { TraderItemsPath="GP_Trader_ARCH.DefaultTraderItems" @@ -2298,4 +2416,7 @@ defaultproperties bForceSkipTraderUI=false GunGameWavesCurrent=1 bWaveGunGameIsFinal=false + VIPRepCurrentHealth=0 + VIPRepMaxHealth=0 + VIPRepPlayer=none } diff --git a/KFGame/Classes/KFGfxMenu_StartGame.uc b/KFGame/Classes/KFGfxMenu_StartGame.uc index bda8db5..ec61629 100644 --- a/KFGame/Classes/KFGfxMenu_StartGame.uc +++ b/KFGame/Classes/KFGfxMenu_StartGame.uc @@ -206,7 +206,7 @@ static function class GetSpecialEventClass case SEI_Summer: return class'KFGFxSpecialEventObjectivesContainer_Summer2022'; case SEI_Fall: - return class'KFGFxSpecialEventObjectivesContainer_Fall2021'; + return class'KFGFxSpecialEventObjectivesContainer_Fall2022'; case SEI_Winter: return class'KFGFXSpecialEventObjectivesContainer_Xmas2021'; } diff --git a/KFGame/Classes/KFHUDBase.uc b/KFGame/Classes/KFHUDBase.uc index 6daa52e..8e352f0 100644 --- a/KFGame/Classes/KFHUDBase.uc +++ b/KFGame/Classes/KFHUDBase.uc @@ -126,6 +126,10 @@ var const Texture2D GenericHumanIconTexture; /** Texture used for the generic zed icon */ var const Texture2D GenericZedIconTexture; +/** Texture used for the VIP representation over a player */ +var const Texture2D VIPIconTexture; +var const float OriginalVIPIconSize; + /** * Draw a glowing string */ @@ -772,7 +776,7 @@ simulated function bool DrawFriendlyHumanPlayerInfo( KFPawn_Human KFPH ) local float ResModifier; local float PerkIconPosX, PerkIconPosY, SupplyIconPosX, SupplyIconPosY, PerkIconSize; local color CurrentArmorColor, CurrentHealthColor; - + local float VIPIconSize, VIPIconPosX, VIPIconPosY; ResModifier = WorldInfo.static.GetResolutionBasedHUDScale() * FriendlyHudScale; @@ -812,14 +816,11 @@ simulated function bool DrawFriendlyHumanPlayerInfo( KFPawn_Human KFPH ) CurrentArmorColor = ClassicPlayerInfo ? ClassicArmorColor : ArmorColor; DrawKFBar(Percentage, BarLength, BarHeight, ScreenPos.X - (BarLength * 0.5f), ScreenPos.Y + BarHeight + (36 * FontScale * ResModifier), CurrentArmorColor); - - //Draw health bar PercentageHealth = FMin(float(KFPH.Health) / float(KFPH.HealthMax), 100); CurrentHealthColor = ClassicPlayerInfo ? ClassicHealthColor : HealthColor; DrawKFBar(PercentageHealth, BarLength, BarHeight, ScreenPos.X - (BarLength * 0.5f), ScreenPos.Y + BarHeight * 2 + (36 * FontScale * ResModifier), CurrentHealthColor); - //Draw health being regenerated bar PercentageHealthToRegen = FMin(float(KFPH.HealthToRegen) / float(KFPH.HealthMax), 100); PercentageHealthMissing = FMin((float(KFPH.HealthMax) - float(KFPH.Health)) / float(KFPH.HealthMax), 100); @@ -827,6 +828,19 @@ simulated function bool DrawFriendlyHumanPlayerInfo( KFPawn_Human KFPH ) CurrentHealthColor = HealthBeingRegeneratedColor; DrawKFBar(1, PercentageHealthToRegen * BarLength, BarHeight, ScreenPos.X + BarLength * (PercentageHealth - 0.5f), ScreenPos.Y + BarHeight * 2 + (36 * FontScale * ResModifier), HealthBeingRegeneratedColor); + if (KFGRI != none + && KFGRI.IsVIPMode() + && KFPH.PlayerReplicationInfo != none + && KFGRI.VIPModeData.VIPPlayer != none + && KFPH.PlayerReplicationInfo == KFGRI.VIPModeData.VIPPlayer) + { + // Draw VIP Icon + VIPIconSize = OriginalVIPIconSize * ResModifier; + VIPIconPosX = ScreenPos.X - VIPIconSize * 0.5f; + VIPIconPosY = ScreenPos.Y - 2.5f * BarHeight + (36 * FontScale * ResModifier) - 20.f * ResModifier - VIPIconSize * 0.5f - 1.f; + DrawVIPIcon(VIPIconSize , VIPIconPosX, VIPIconPosY); + } + if( KFPRI.CurrentPerkClass == none ) { return false; @@ -862,6 +876,12 @@ simulated function bool DrawFriendlyHumanPlayerInfo( KFPawn_Human KFPH ) return true; } +simulated function DrawVIPIcon(float VIPIconSize, float VIPIconPosX, float VIPIconPosY) +{ + Canvas.SetPos(VIPIconPosX, VIPIconPosY); + Canvas.DrawTile(VIPIconTexture, VIPIconSize, VIPIconSize, 0, 0, 256, 256); +} + simulated function bool DrawScriptedPawnInfo(KFPawn_Scripted KFPS, float NormalizedAngle, bool bRendered) { local float Percentage; @@ -1088,7 +1108,7 @@ simulated function CheckAndDrawHiddenPlayerIcons( array V } } - DrawHiddenHumanPlayerIcon( PRI, PawnLocation, Normal((PawnLocation + (class'KFPawn_Human'.default.CylinderComponent.CollisionHeight * vect(0, 0, 2))) - ViewLocation) dot ViewVector); + DrawHiddenHumanPlayerIcon(PRI, PawnLocation, ViewLocation, ViewVector); } } @@ -1099,13 +1119,15 @@ simulated function CheckAndDrawHiddenPlayerIcons( array V * @param IconWorldLocation The "player's" location in the world * @Note:This is the one we want to clamp */ -function DrawHiddenHumanPlayerIcon( PlayerReplicationInfo PRI, vector IconWorldLocation, float NormalizedAngle) +function DrawHiddenHumanPlayerIcon(PlayerReplicationInfo PRI, vector IconWorldLocation, vector ViewLocation, vector ViewVector) { - local vector ScreenPos; + local vector ReferencePosition, UpVector, ScreenPos, VIPIconPos, ViewLeftVector; local float IconSizeMult; local KFPlayerReplicationInfo KFPRI; local Texture2D PlayerIcon; local float ResModifier; + local float VIPIconSize; + local float NormalizedAngle, NormalizedAngleWithLeftView; ResModifier = WorldInfo.static.GetResolutionBasedHUDScale() * FriendlyHudScale; @@ -1115,7 +1137,91 @@ function DrawHiddenHumanPlayerIcon( PlayerReplicationInfo PRI, vector IconWorldL return; } - ScreenPos = Canvas.Project(IconWorldLocation + class'KFPawn_Human'.default.CylinderComponent.CollisionHeight * vect(0, 0, 2)); + ReferencePosition = IconWorldLocation + (class'KFPawn_Human'.default.CylinderComponent.CollisionHeight * vect(0, 0, 2)); + + NormalizedAngle = Normal(ReferencePosition - ViewLocation) dot ViewVector; + + ScreenPos = Canvas.Project(ReferencePosition); + + if (KFGRI != none + && KFGRI.IsVIPMode() + && KFGRI.VIPModeData.VIPPlayer != none + && PRI != none + && PRI == KFGRI.VIPModeData.VIPPlayer) + { + VIPIconSize = OriginalVIPIconSize * ResModifier; + + VIPIconPos = ScreenPos; + VIPIconPos.X -= VIPIconSize * 0.5f; + + // If the player is on front of you + if (NormalizedAngle > 0) + { + // Adjust on X + if (ScreenPos.X < 0 || ScreenPos.X > (Canvas.ClipX - VIPIconSize)) + { + if (ScreenPos.X < 0) + { + VIPIconPos.X = 0; + } + else + { + VIPIconPos.X = Canvas.ClipX - VIPIconSize; + } + } + + // Adjust on Y + if (ScreenPos.Y < 0 || ScreenPos.Y > (Canvas.ClipY - VIPIconSize)) + { + if (ScreenPos.Y < 0) + { + VIPIconPos.Y = 0; + } + else + { + VIPIconPos.Y = Canvas.ClipY - VIPIconSize; + } + } + } + // If the player is behind you + else + { + // New to know if Player is on your left or on your right side.. + UpVector.Z = 1; + ViewLeftVector = ViewVector cross UpVector; + NormalizedAngleWithLeftView = Normal(ReferencePosition - ViewLocation) dot ViewLeftVector; + + // The X position clamps between minimum and maximum, we don't interpolate in the middle as it makes more difficult for the player to understand + // Where the VIP is + + // Adjust on X + if (NormalizedAngleWithLeftView > 0) + { + VIPIconPos.X = 0; + } + else + { + VipIconPos.X = Canvas.ClipX - VIPIconSize; + } + + // Adjust on Y + if (ScreenPos.Y < 0 || ScreenPos.Y > (Canvas.ClipY - VIPIconSize)) + { + if (ScreenPos.Y < 0) + { + VIPIconPos.Y = 0; + } + else + { + VIPIconPos.Y = Canvas.ClipY - VIPIconSize; + } + } + } + + Canvas.SetDrawColorStruct(PlayerBarIconColor); + DrawVIPIcon(VIPIconSize, VIPIconPos.X, VIPIconPos.Y); + } + // Fudge by icon size IconSizeMult = (PlayerStatusIconSize * 0.8) * ResModifier; ScreenPos.X -= IconSizeMult; @@ -1135,6 +1241,7 @@ function DrawHiddenHumanPlayerIcon( PlayerReplicationInfo PRI, vector IconWorldL { CurrentAlphaDelta = 5; } + ScreenPos.X = Canvas.ClipX - ScreenPos.x; ScreenPos = GetClampedScreenPosition(ScreenPos); CurrentVoiceCommsHighlightAlpha += CurrentAlphaDelta; @@ -1142,7 +1249,6 @@ function DrawHiddenHumanPlayerIcon( PlayerReplicationInfo PRI, vector IconWorldL Canvas.SetDrawColor(255, 255, 255, CurrentVoiceCommsHighlightAlpha); Canvas.SetPos(ScreenPos.X - (IconSizeMult * VoiceCommsIconHighlightScale / 2), ScreenPos.Y - (IconSizeMult * VoiceCommsIconHighlightScale / 2)); Canvas.DrawTile(IconHighLightTexture, IconSizeMult + (IconSizeMult * VoiceCommsIconHighlightScale), IconSizeMult + (IconSizeMult * VoiceCommsIconHighlightScale), 0, 0, 128, 128); - } else { @@ -1371,6 +1477,9 @@ defaultproperties GenericHumanIconTexture = Texture2D'UI_PerkIcons_TEX.UI_Horzine_H_Logo' GenericZedIconTexture = Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_ZED' + + VIPIConTexture = Texture2D'UI_PerkIcons_TEX.UI_Overscreen_vip_icon_' + OriginalVIPIconSize = 64; IconHighLightTexture = Texture2D'UI_World_TEX.VoicCommsCircleHighlight' VoiceCommsIconHighlightScale = 0.5f diff --git a/KFGame/Classes/KFInventoryManager.uc b/KFGame/Classes/KFInventoryManager.uc index 69947d4..596143e 100644 --- a/KFGame/Classes/KFInventoryManager.uc +++ b/KFGame/Classes/KFInventoryManager.uc @@ -21,6 +21,7 @@ var transient KFWeap_HealerBase HealerWeapon; /** Localized message for quick heal */ var localized string FullHealthMsg; +var localized string CannotHealthMsg; /** The number of grenades the character is carrying */ var byte GrenadeCount; @@ -1270,6 +1271,20 @@ simulated function AttemptQuickHeal() { local KFWeap_HealerBase W; local KFPlayerController KFPC; + local KFPlayerController_WeeklySurvival KFPCWS; + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + KFPCWS = KFPlayerController_WeeklySurvival(Instigator.Owner); + + // VIP cannot heal + if (KFGRI != none + && KFGRI.VIPRepPlayer != none + && KFGRI.VIPRepPlayer == KFPlayerReplicationInfo(KFPCWS.PlayerReplicationInfo)) + { + KFPCWS.MyGFxHUD.ShowNonCriticalMessage(CannotHealthMsg); + return; + } // Do not heal if we have full health if ( Instigator.Health >= Instigator.HealthMax ) diff --git a/KFGame/Classes/KFLaserSightAttachment.uc b/KFGame/Classes/KFLaserSightAttachment.uc index cbc50fb..3af9ab5 100644 --- a/KFGame/Classes/KFLaserSightAttachment.uc +++ b/KFGame/Classes/KFLaserSightAttachment.uc @@ -57,6 +57,8 @@ var() float AnimBlendRate; var transient float LaserSightAimStrength; var transient float DesiredAimStrength; +var transient bool IsVisible; + /** Create/Attach lasersight components */ function AttachLaserSight(SkeletalMeshComponent OwnerMesh, bool bFirstPerson, optional name SocketNameOverride) { @@ -135,6 +137,8 @@ simulated function SetMeshLightingChannels(LightingChannelContainer NewLightingC simulated event ChangeVisibility(bool bVisible) { + IsVisible = bVisible; + LaserDotMeshComp.SetHidden(!bVisible); LaserSightMeshComp.SetHidden(!bVisible); LaserBeamMeshComp.SetHidden(!bVisible); @@ -159,6 +163,11 @@ simulated function Update(float DeltaTime, KFWeapon OwningWeapon) local Quat Q; local TraceHitInfo HitInfo; + if (IsVisible == false) + { + return; + } + if( OwningWeapon != None && OwningWeapon.Instigator != None && OwningWeapon.Instigator.Weapon == OwningWeapon && @@ -409,4 +418,6 @@ defaultproperties LaserDotLerpEndDistance=6000.f LaserDotMaxScale=10.f LaserDotDepthBias=0.95f + + IsVisible=true } diff --git a/KFGame/Classes/KFLocalMessage_Priority.uc b/KFGame/Classes/KFLocalMessage_Priority.uc index 3f1c474..ecaa856 100644 --- a/KFGame/Classes/KFLocalMessage_Priority.uc +++ b/KFGame/Classes/KFLocalMessage_Priority.uc @@ -17,6 +17,7 @@ var localized string ScavengeMessage; var localized string YouLostMessage; var localized string YouWonMessage; var localized string SquadWipedOutMessage; +var localized string SquadWipedOutVIPMessage; var localized string SquadSurvivedMessage; var localized string ObjectiveStartMessage; var localized string ObjectiveWonMessage; @@ -231,6 +232,8 @@ static function ClientReceive( static function string GetMessageString(int Switch, optional out String SecondaryString, optional byte TeamIndex) { + local KFGameReplicationInfo KFGRI; + SecondaryString = ""; switch ( Switch ) @@ -281,7 +284,16 @@ static function string GetMessageString(int Switch, optional out String Secondar case GMT_MatchLost: if(class'WorldInfo'.static.GetWorldInfo().NetMode != NM_Standalone) { - SecondaryString = default.SquadWipedOutMessage; + KFGRI = KFGameReplicationInfo(class'WorldInfo'.static.GetWorldInfo().GRI); + + if (KFGRI != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 17) + { + SecondaryString = default.SquadWipedOutVIPMessage; + } + else + { + SecondaryString = default.SquadWipedOutMessage; + } } return default.YouLostMessage; diff --git a/KFGame/Classes/KFOnlineStatsReadDingo.uc b/KFGame/Classes/KFOnlineStatsReadDingo.uc index 5b29a48..e98d807 100644 --- a/KFGame/Classes/KFOnlineStatsReadDingo.uc +++ b/KFGame/Classes/KFOnlineStatsReadDingo.uc @@ -71,6 +71,7 @@ defaultproperties ColumnIds.Add(STATID_ACHIEVE_NetherholdCollectibles) ColumnIds.Add(STATID_ACHIEVE_CarillonHamletCollectibles) ColumnIds.Add(STATID_ACHIEVE_RigCollectibles) + ColumnIds.Add(STATID_ACHIEVE_BarmwichCollectibles) ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky5, Name="AchievementMrPerky5")) ColumnMappings.Add((Id=STATID_ACHIEVE_MrPerky10, Name = "AchievementMrPerky10")) @@ -130,4 +131,5 @@ defaultproperties ColumnMappings.Add((Id=STATID_ACHIEVE_NetherholdCollectibles,Name="AchievementCollectNetherhold")) ColumnMappings.Add((Id=STATID_ACHIEVE_CarillonHamletCollectibles,Name="AchievementCollectCarillonHamlet")) ColumnMappings.Add((Id=STATID_ACHIEVE_RigCollectibles,Name="AchievementCollectRig")) + ColumnMappings.Add((Id=STATID_ACHIEVE_BarmwichCollectibles,Name="AchievementCollectBarmwichTown")) } diff --git a/KFGame/Classes/KFOnlineStatsWrite.uc b/KFGame/Classes/KFOnlineStatsWrite.uc index 637e6b4..8a1259e 100644 --- a/KFGame/Classes/KFOnlineStatsWrite.uc +++ b/KFGame/Classes/KFOnlineStatsWrite.uc @@ -445,6 +445,10 @@ const KFACHID_RigHard = 292; const KFACHID_RigHellOnEarth = 293; const KFACHID_RigCollectibles = 294; +const KFACHID_BarmwichHard = 295; +const KFACHID_BarmwichHellOnEarth = 296; +const KFACHID_BarmwichCollectibles = 297; + /* __TW_ANALYTICS_ */ var int PerRoundWeldXP; var int PerRoundHealXP; @@ -2093,15 +2097,16 @@ defaultproperties //Firebug Weapons DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Flame_CaulkBurn, KFDT_Bludgeon_CaulkBurn,KFDT_Fire_CaulkBurn,KFDT_Fire_Ground_CaulkNBurn),CompletionAmount=5000)) //3000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Pistol_Flare, KFDT_Bludgeon_FlareGun,KFDT_Fire_FlareGun,KFDT_Fire_FlareGun_Dual,KFDT_Fire_FlareGunDoT),CompletionAmount=7000)) //5000 - DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Shotgun_DragonsBreath, KFDT_Ballistic_DragonsBreath,KFDT_Bludgeon_DragonsBreath,KFDT_Fire_DragonsBreathDoT),CompletionAmount=7000)) //5000 + DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Shotgun_DragonsBreath, KFDT_Ballistic_DragonsBreath,KFDT_Bludgeon_DragonsBreath,KFDT_Fire_DragonsBreathDoT, KFDT_Fire_Ground_DragonsBreath),CompletionAmount=7000)) //5000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_SMG_Mac10, KFDT_Bludgeon_Mac10,KFDT_Fire_Mac10,KFDT_Fire_Mac10DoT),CompletionAmount=9000)) //7000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Flame_Flamethrower, KFDT_Bludgeon_Flamethrower,KFDT_Fire_FlameThrower,KFDT_Fire_Ground_FlameThrower),CompletionAmount=9000)) //7000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_AssaultRifle_HRGIncendiaryRifle, KFDT_Bludgeon_HRGIncendiaryRifle,KFDT_Ballistic_HRGIncendiaryRifle,KFDT_Explosive_HRGIncendiaryRifle,KFDT_Ballistic_HRGIncendiaryRifleGrenadeImpact,KFDT_Fire_HRGIncendiaryRifleBulletDoT,KFDT_Fire_HRGIncendiaryRifleGrenadeDoT,KFDT_Fire_Ground_HRGIncendiaryRifle),CompletionAmount=9000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Pistol_HRGScorcher, KFDT_Bludgeon_HRGScorcher,KFDT_Ballistic_HRGScorcherLightingImpact,KFDT_Fire_HRGScorcherDoT,KFDT_Ballistic_HRGScorcherBrokenImpact,KFDT_Fire_Ground_HRGScorcher),CompletionAmount=9000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Beam_Microwave, KFDT_Bludgeon_MicrowaveGun,KFDT_Fire_Ground_MicrowaveGun,KFDT_Microwave,KFDT_Microwave_Beam,KFDT_Microwave_Blast),CompletionAmount=10000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HuskCannon, KFDT_Bludgeon_HuskCannon, KFDT_Explosive_HuskCannon, KFDT_HuskCannonDot, KFDT_Explosive_HuskCannonImpact),CompletionAmount=10000)) + DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_Dragonbreath, KFDT_Ballistic_HRG_Dragonbreath, KFDT_Bludgeon_HRG_Dragonbreath, KFDT_Fire_HRG_DragonsBreathDoT, KFDT_Fire_HRG_DragonsBreathDoT, KFDT_Fire_Ground_HRG_DragonBreath),CompletionAmount=10000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_AssaultRifle_Microwave, KFDT_Ballistic_MicrowaveRifle, KFDT_Fire_MicrowaveRifleDoT, KFDT_Bludgeon_MicrowaveRifle),CompletionAmount=10000)) - + //Berserker Weapons DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Blunt_Crovel, KFDT_Bludgeon_Crovel,KFDT_Bludgeon_CrovelBash,KFDT_Slashing_Crovel),CompletionAmount=5000)) //3000 DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Shotgun_Nailgun, KFDT_Ballistic_NailShotgun,KFDT_Bludgeon_NailShotgun),CompletionAmount=7000)) //5000 @@ -2136,6 +2141,7 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_AssaultRifle_FNFal, KFDT_Ballistic_FNFal,KFDT_Bludgeon_FNFal),CompletionAmount=10000)) //Survivalist Weapons + DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_Locust, KFDT_Bludgeon_HRG_Locust, KFDT_Toxic_HRG_Locust, KFDT_Ballistic_HRG_Locust),CompletionAmount=7000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_Ice_FreezeThrower, KFDT_Bludgeon_Freezethrower, KFDT_Freeze_FreezeThrower, KFDT_Freeze_FreezeThrower_IceShards, KFDT_Freeze_Ground_FreezeThrower),CompletionAmount=7000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_HRG_EMP_ArcGenerator, KFDT_Bludgeon_ArcGenerator, KFDT_EMP_ArcGenerator_Beam, KFDT_EMP_ArcGeneratorSphereImpact, KFDT_EMP_ArcGenerator_DefaultFiremodeZapDamage, KFDT_EMP_ArcGenerator_AltFiremodeZapDamage),CompletionAmount=9000)) DailyEvents.Add((ObjectiveType=DOT_WeaponDamage,ObjectiveClasses=(KFWeap_AssaultRifle_LazerCutter, KFDT_Ballistic_LazerCutter, KFDT_LazerCutter_Beam, KFDT_Bludgeon_LazerCutter),CompletionAmount=10000)) @@ -2286,6 +2292,9 @@ defaultproperties DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-RIG),CompletionAmount=1)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-RIG),CompletionAmount=2)) DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-RIG),CompletionAmount=3)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-BARMWICHTOWN),CompletionAmount=1)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-BARMWICHTOWN),CompletionAmount=2)) + DailyEvents.Add((ObjectiveType=DOT_Maps,SecondaryType=DOST_MapCompletion,ObjectiveClasses=(KF-BARMWICHTOWN),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 65ce0d3..61857f6 100644 --- a/KFGame/Classes/KFOnlineStatsWriteDingo.uc +++ b/KFGame/Classes/KFOnlineStatsWriteDingo.uc @@ -73,4 +73,6 @@ defaultproperties Properties.Add((PropertyId = STATID_ACHIEVE_NetherholdCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_CarillonHamletCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) Properties.Add((PropertyId = STATID_ACHIEVE_RigCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) + Properties.Add((PropertyId = STATID_ACHIEVE_BarmwichCollectibles, Data = (Type = SDT_Int32, Value1 = 0))) + } diff --git a/KFGame/Classes/KFOutbreakEvent.uc b/KFGame/Classes/KFOutbreakEvent.uc index a879439..f29ec57 100644 --- a/KFGame/Classes/KFOutbreakEvent.uc +++ b/KFGame/Classes/KFOutbreakEvent.uc @@ -448,7 +448,12 @@ struct WeeklyOverrides var() bool bGunGameMode; /** Information about each level in Gun Game Mode */ - var() GunGamePerkData GunGamePerksData; + var() GunGamePerkData GunGamePerksData; + + /** VIP targetting */ + var() const array< class > VIPTargetting; + + var() bool bVIPGameMode; /** Ignores damage caused by headshots. */ var() bool bInvulnerableHeads; @@ -459,9 +464,6 @@ struct WeeklyOverrides /** Time between waves override. */ var() float TimeBetweenWaves; - /** Wether or not we only can spawn Armor on the Item pickups */ - var() bool bOnlyArmorItemPickup; - structdefaultproperties { GameLength = GL_Short @@ -522,10 +524,10 @@ struct WeeklyOverrides bDisableAddDosh = false; bDisableThrowWeapon = false; bGunGameMode = false; + bVIPGameMode = false; bInvulnerableHeads = false; TraderTimeModifier = 1.f; TimeBetweenWaves = -1.f; - bOnlyArmorItemPickup=false; bForceShowSkipTrader = false; } }; diff --git a/KFGame/Classes/KFPawn.uc b/KFGame/Classes/KFPawn.uc index aead556..55084f8 100644 --- a/KFGame/Classes/KFPawn.uc +++ b/KFGame/Classes/KFPawn.uc @@ -178,6 +178,10 @@ struct native DamageInfo var array > DamageTypes; }; +// VFX that need to linger when a specific DamageType starts (those should loop), we stop them when the DamageType ends affecting +var() ParticleSystem Toxic_HRG_Locust_LoopingParticleEffect; +var transient ParticleSystemComponent Toxic_HRG_Locust_LoopingPSC; + /** List of PRIs who damaged the specimen */ var array DamageHistory; @@ -885,6 +889,7 @@ var transient byte LastHitZoneIndex; */ var const bool bIsTurret; +var repnotify bool bEnableSwarmVFX; replication { @@ -893,7 +898,8 @@ replication AmbientSound, WeaponClassForAttachmentTemplate, bIsSprinting, InjuredHitZones, KnockdownImpulse, ReplicatedSpecialMove, bEmpDisrupted, bEmpPanicked, bFirePanicked, RepFireBurnedAmount, bUnaffectedByZedTime, bMovesFastInZedTime, IntendedBodyScale, - IntendedHeadScale, AttackSpeedModifier, bHasStartedFire, PowerUpAmbientSound, BodyScaleChangePerSecond; + IntendedHeadScale, AttackSpeedModifier, bHasStartedFire, PowerUpAmbientSound, BodyScaleChangePerSecond, + bEnableSwarmVFX; if ( bNetDirty && WorldInfo.TimeSeconds < LastTakeHitTimeout ) HitFxInfo, HitFxRadialInfo, HitFxInstigator, HitFxAddedRelativeLocs, HitFxAddedHitCount; if ( Physics == PHYS_RigidBody && !bTearOff ) @@ -1171,6 +1177,18 @@ simulated event ReplicatedEvent(name VarName) break; case nameof(WeaponSpecialAction): OnWeaponSpecialAction(WeaponSpecialAction); + break; + + case nameof(bEnableSwarmVFX): + if (bEnableSwarmVFX) + { + StartLocustVFX(); + } + else + { + StopLocustVFX(); + } + break; } Super.ReplicatedEvent(VarName); @@ -2709,7 +2727,7 @@ event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector 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 - // HandleAfflictionsOnHit(InstigatedBy, Normal(Momentum), class(DamageType), DamageCauser); + HandleAfflictionsOnHit(InstigatedBy, Normal(Momentum), class(DamageType), DamageCauser); ActualDamage = OldHealth - Health; if( ActualDamage > 0 ) @@ -2811,6 +2829,15 @@ function AdjustDamage(out int InDamage, out vector Momentum, Controller Instigat InDamage *= VolumeDamageScale; + if (KFPlayerController(Controller) != none) + { + KFPlayerController(Controller).AdjustDamage(InDamage, InstigatedBy, DamageType, DamageCauser, self); + } + else if (KFPlayerController(InstigatedBy) != none) + { + KFPlayerController(InstigatedBy).AdjustDamage(InDamage, InstigatedBy, DamageType, DamageCauser, self); + } + // Check non lethal damage KFDT = class(DamageType); if ( InDamage >= Health && KFDT != none && KFDT.default.bNonLethalDamage ) @@ -2915,6 +2942,30 @@ function AddTakenDamage( Controller DamagerController, int Damage, Actor DamageC } } +function TimerRestartForceEnemy() +{ + local KFAIController KFAIC; + local Pawn ForcedEnemy; + + if (Controller != none) + { + KFAIC = KFAIController( Controller ); + if (KFAIC != none) + { + // Forces the ForcedEnemy again + + KFAIC.CanForceEnemy = true; + + ForcedEnemy = KFAIC.FindForcedEnemy(); + + if (ForcedEnemy != none) + { + KFAIC.ChangeEnemy(ForcedEnemy); + } + } + } +} + function UpdateDamageHistory( Controller DamagerController, int Damage, Actor DamageCauser, class DamageType ) { local DamageInfo Info; @@ -2943,19 +2994,40 @@ function UpdateDamageHistory( Controller DamagerController, int Damage, Actor Da DamageHistory[KFAIC.CurrentEnemysHistoryIndex].Damage = 0; } - if( KFAIC.IsAggroEnemySwitchAllowed() - && DamagerController.Pawn != KFAIC.Enemy - && Info.Damage >= DamageThreshold - && Info.Damage > DamageHistory[KFAIC.CurrentEnemysHistoryIndex].Damage ) + // If we have a forced Enemy break it only if we fulfill the minimum health value + if (KFAIC.ForcedEnemy != none) { - BlockerPawn = KFAIC.GetPawnBlockingPathTo( DamagerController.Pawn, true ); - if( BlockerPawn == none ) + if (GetHealthPercentage() < KFAIC.DamageRatioToChangeForcedEnemy) { - bChangedEnemies = KFAIC.SetEnemy(DamagerController.Pawn); + // Only if X seconds passed since last time we choose the ForcedEnemy + if ((WorldInfo.TimeSeconds - KFAIC.ForcedEnemyLastTime) > KFAIC.TimeCannotChangeFromForcedEnemy) + { + KFAIC.CanForceEnemy = false; // The timer we will reenable this to allow selection again + + // If we have forced enemy, reactivate ForcedEnemy after X seconds, so it can default to the ForcedEnemy again + ClearTimer('TimerRestartForceEnemy'); + SetTimer(KFAIC.TimeCanRestartForcedEnemy, false, 'TimerRestartForceEnemy'); + + KFAIC.ChangeEnemy(DamagerController.Pawn); + } } - else + } + else + { + if (KFAIC.IsAggroEnemySwitchAllowed() + && DamagerController.Pawn != KFAIC.Enemy + && Info.Damage >= DamageThreshold + && Info.Damage > DamageHistory[KFAIC.CurrentEnemysHistoryIndex].Damage) { - bChangedEnemies = KFAIC.SetEnemy( BlockerPawn ); + BlockerPawn = KFAIC.GetPawnBlockingPathTo( DamagerController.Pawn, true ); + if( BlockerPawn == none ) + { + bChangedEnemies = KFAIC.SetEnemy(DamagerController.Pawn); + } + else + { + bChangedEnemies = KFAIC.SetEnemy( BlockerPawn ); + } } } } @@ -3403,6 +3475,8 @@ simulated function bool IsHeadless(); /** Clean up function to terminate any effects on death */ simulated function TerminateEffectsOnDeath() { + local int i; + // Destroy our weapon attachment if( WeaponAttachment != None && !WeaponAttachment.bPendingDelete ) { @@ -3420,11 +3494,18 @@ simulated function TerminateEffectsOnDeath() AfflictionHandler.Shutdown(); + StopLocustVFX(); + // send a special stop event to the audio system if ( SoundGroupArch.OnDeathStopEvent != None ) { PostAkEvent( SoundGroupArch.OnDeathStopEvent ); } + + for (i = 0 ; i < DamageOverTimeArray.Length; i++) + { + OnEndDamageType(DamageOverTimeArray[i].DamageType); + } } /********************************************************************************************* @@ -4043,12 +4124,12 @@ simulated function KFSkinTypeEffects GetHitZoneSkinTypeEffects( int HitZoneIdx ) */ simulated function AdjustAffliction(out float AfflictionPower); -function HandleAfflictionsOnHit(Controller DamageInstigator, vector HitDir, class DamageType, Actor DamageCauser) +function HandleAfflictionsOnHit(Controller DamageInstigator, vector HitDir, class DamageType, Actor DamageCauser) { //Handle afflictions if (AfflictionHandler != None) { - AfflictionHandler.NotifyTakeHit(DamageInstigator, HitDir, class(DamageType), DamageCauser); + AfflictionHandler.NotifyTakeHit(DamageInstigator, HitDir, DamageType, DamageCauser); } } @@ -4084,6 +4165,8 @@ function ApplyDamageOverTime(int Damage, Controller InstigatedBy, class DamageType) +{ + switch (DamageType.Name) + { + case 'KFDT_Toxic_HRG_Locust': + StartLocustVFX(); + break; + } +} + +simulated function OnEndDamageType(class DamageType) +{ + switch (DamageType.Name) + { + case 'KFDT_Toxic_HRG_Locust': + StopLocustVFX(); + break; + } +} + /********************************************************************************************* * @name Animation ********************************************************************************************* */ @@ -5408,6 +5513,43 @@ simulated function StopExtraVFX(Name FXLabel) simulated function SetTurretWeaponAttachment(class WeaponClass) {} +simulated function StartLocustVFX() +{ + if ( WorldInfo.NetMode == NM_DedicatedServer ) + { + bEnableSwarmVFX=true; + bNetDirty = true; + return; + } + + if (Toxic_HRG_Locust_LoopingParticleEffect != none) + { + if (Toxic_HRG_Locust_LoopingPSC == none) + { + Toxic_HRG_Locust_LoopingPSC = WorldInfo.MyEmitterPool.SpawnEmitter(Toxic_HRG_Locust_LoopingParticleEffect, Location, Rotation, self); + } + else + { + Toxic_HRG_Locust_LoopingPSC.SetStopSpawning(-1, false); + } + } +} + +simulated function StopLocustVFX() +{ + if ( WorldInfo.NetMode == NM_DedicatedServer ) + { + bEnableSwarmVFX=false; + bForceNetUpdate = true; + return; + } + + if (Toxic_HRG_Locust_LoopingPSC != none) + { + Toxic_HRG_Locust_LoopingPSC.SetStopSpawning(-1, true); + } +} + defaultproperties { InventoryManagerClass=class'KFInventoryManager' @@ -5674,4 +5816,8 @@ defaultproperties // --------------------------------------------- // AutoTurret bIsTurret=false + + Toxic_HRG_Locust_LoopingParticleEffect=ParticleSystem'WEP_HRG_Locust_EMIT.FX_Flying_Bugs_attacking' + Toxic_HRG_Locust_LoopingPSC=none + bEnableSwarmVFX=false } diff --git a/KFGame/Classes/KFPawn_Human.uc b/KFGame/Classes/KFPawn_Human.uc index 7c6bf68..bd724a8 100644 --- a/KFGame/Classes/KFPawn_Human.uc +++ b/KFGame/Classes/KFPawn_Human.uc @@ -157,7 +157,16 @@ var float MinHealthPctToTriggerSurrounded; /********************************************************************************************* * @name Perk @ToDo: Move stuff to PRI and combine in a byte/INT ********************************************************************************************* */ -var array ActiveSkillIconPaths; + +struct native ActiveSkill +{ + var string IconPath; + var int Multiplier; + var float MaxDuration; + var float Duration; +}; + +var private array ActiveSkills; var repnotify private byte HealingSpeedBoost; var repnotify private byte HealingDamageBoost; @@ -632,6 +641,7 @@ function float GetHealthMod() simulated function WeaponStateChanged(byte NewState, optional bool bViaReplication) { CurrentWeaponState = NewState; + bForceNetUpdate=true; // skip if this pawn was recently spawned, so we don't play out-of-date anims when pawns become relevant if( `TimeSince(CreationTime) < 1.f ) @@ -711,6 +721,18 @@ event bool HealDamage(int Amount, Controller Healer, class DamageTyp local KFPlayerController KFPC; local KFPowerUp KFPowerUp; local KFGameInfo GameInfo; + local KFPlayerController_WeeklySurvival KFPCWS; + + KFPCWS = KFPlayerController_WeeklySurvival(Healer); + + if (Controller != none && KFPCWS != none && KFPCWS.VIPGameData.IsVIP) + { + // VIP can't heal itself + if (Controller == KFPCWS) + { + return false; + } + } KFPC = KFPlayerController(Controller); if ( KFPC != none ) @@ -896,6 +918,11 @@ function GiveHealthOverTime() KFPRI.PlayerHealth = Health; KFPRI.PlayerHealthPercent = FloatToByte( float(Health) / float(HealthMax) ); } + + if (KFPlayerController_WeeklySurvival(Controller) != none) + { + KFPlayerController_WeeklySurvival(Controller).UpdateVIPDamage(); + } } else { @@ -1272,6 +1299,15 @@ function AdjustDamage(out int InDamage, out vector Momentum, Controller Instigat } } + if (KFPlayerController_WeeklySurvival(Controller) != none) + { + KFPlayerController_WeeklySurvival(Controller).AdjustVIPDamage(InDamage, InstigatedBy); + } + else if (KFPlayerController_WeeklySurvival(InstigatedBy) != none) + { + KFPlayerController_WeeklySurvival(InstigatedBy).AdjustVIPDamage(InDamage, InstigatedBy); + } + if( bHasSacrificeSkill && Health >= 5 && Health - InDamage < 5 ) { Health = InDamage + 5; @@ -1302,8 +1338,14 @@ function AdjustDamage(out int InDamage, out vector Momentum, Controller Instigat } } `endif + + if (KFPlayerController_WeeklySurvival(Controller) != none) + { + KFPlayerController_WeeklySurvival(Controller).UpdateVIPDamage(); + } } + event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser) { local int ActualDamageTaken, OldHealth, OldArmor; @@ -1451,12 +1493,17 @@ function StartAirBorneAgentEvent() simulated function UpdateHealingSpeedBoost() { HealingSpeedBoost = Min( HealingSpeedBoost + class'KFPerk_FieldMedic'.static.GetHealingSpeedBoost(), class'KFPerk_FieldMedic'.static.GetMaxHealingSpeedBoost() ); + SetTimer( class'KFPerk_FieldMedic'.static.GetHealingSpeedBoostDuration(),, nameOf(ResetHealingSpeedBoost) ); - if ( WorldInfo.NetMode == NM_STANDALONE) + if (WorldInfo.NetMode == NM_STANDALONE) { NotifyHealingSpeedBoostBuff(HealingSpeedBoost); } + else + { + ClientNotifyHealingSpeedBoostBuff(HealingSpeedBoost); + } } simulated function ResetHealingSpeedBoost() @@ -1482,12 +1529,17 @@ simulated function float GetHealingDamageBoostModifier() simulated function UpdateHealingDamageBoost() { HealingDamageBoost = Min( HealingDamageBoost + class'KFPerk_FieldMedic'.static.GetHealingDamageBoost(), class'KFPerk_FieldMedic'.static.GetMaxHealingDamageBoost() ); + SetTimer( class'KFPerk_FieldMedic'.static.GetHealingDamageBoostDuration(),, nameOf(ResetHealingDamageBoost) ); - if ( WorldInfo.NetMode == NM_STANDALONE) + if (WorldInfo.NetMode == NM_STANDALONE) { NotifyHealingDamageBoostBuff(HealingDamageBoost); } + else + { + ClientNotifyHealingDamageBoostBuff(HealingDamageBoost); + } } simulated function ResetHealingDamageBoost() @@ -1513,12 +1565,17 @@ simulated function float GetHealingShieldModifier() simulated function UpdateHealingShield() { HealingShield = Min( HealingShield + class'KFPerk_FieldMedic'.static.GetHealingShield(), class'KFPerk_FieldMedic'.static.GetMaxHealingShield() ); + SetTimer( class'KFPerk_FieldMedic'.static.GetHealingShieldDuration(),, nameOf(ResetHealingShield) ); - if ( WorldInfo.NetMode == NM_STANDALONE) + if (WorldInfo.NetMode == NM_STANDALONE) { NotifyHealingShieldBoostBuff(HealingShield); } + else + { + ClientNotifyHealingShieldBoostBuff(HealingShield); + } } simulated function ResetHealingShield() @@ -1574,11 +1631,6 @@ function float GetPerkDoTScaler( optional Controller InstigatedBy, optional clas return DoTScaler; } -function array GetUpdatedSkillIndicators() -{ - return ActiveSkillIconPaths; -} - /********************************************************************************************* * @name Dialog ********************************************************************************************* */ @@ -2060,10 +2112,30 @@ simulated function NotifyHealingSpeedBoostBuff(byte Speed) if( IsLocallyControlled() ) { - UpdateActiveSkillsPath(class'KFPerk_FieldMedic'.default.PerkSkills[EMedicHealingSpeedBoost].IconPath, Speed > 0.0f); + UpdateHealingSpeedBoostBuff(Speed); } } +reliable client function ClientNotifyHealingSpeedBoostBuff(byte Speed) +{ + UpdateHealingSpeedBoostBuff(Speed); +} + +simulated function UpdateHealingSpeedBoostBuff(byte Speed) +{ + local float TotalTimes, Multiplier; + + TotalTimes = class'KFPerk_FieldMedic'.static.GetMaxHealingSpeedBoost() / float(class'KFPerk_FieldMedic'.static.GetHealingSpeedBoost()); + + Multiplier = Speed / float(class'KFPerk_FieldMedic'.static.GetMaxHealingSpeedBoost()); + Multiplier *= TotalTimes; + + UpdateActiveSkillsPath(class'KFPerk_FieldMedic'.default.PerkSkills[EMedicHealingSpeedBoost].IconPath + , Multiplier + , Speed > 0 + , class'KFPerk_FieldMedic'.static.GetHealingSpeedBoostDuration()); +} + simulated function NotifyHealingDamageBoostBuff(byte Damage) { if( Role == ROLE_Authority ) @@ -2074,10 +2146,30 @@ simulated function NotifyHealingDamageBoostBuff(byte Damage) if( IsLocallyControlled() ) { - UpdateActiveSkillsPath(class'KFPerk_FieldMedic'.default.PerkSkills[EMedicHealingDamageBoost].IconPath, Damage > 0.0f); + UpdateHealingDamageBoostBuff(Damage); } } +reliable client function ClientNotifyHealingDamageBoostBuff(byte Damage) +{ + UpdateHealingDamageBoostBuff(Damage); +} + +simulated function UpdateHealingDamageBoostBuff(byte Damage) +{ + local float TotalTimes, Multiplier; + + TotalTimes = class'KFPerk_FieldMedic'.static.GetMaxHealingDamageBoost() / float(class'KFPerk_FieldMedic'.static.GetHealingDamageBoost()); + + Multiplier = Damage / float(class'KFPerk_FieldMedic'.static.GetMaxHealingDamageBoost()); + Multiplier *= TotalTimes; + + UpdateActiveSkillsPath(class'KFPerk_FieldMedic'.default.PerkSkills[EMedicHealingDamageBoost].IconPath + , Multiplier + , Damage > 0 + , class'KFPerk_FieldMedic'.static.GetHealingDamageBoostDuration()); +} + simulated function NotifyHealingShieldBoostBuff(byte Shield) { if( Role == ROLE_Authority ) @@ -2088,28 +2180,70 @@ simulated function NotifyHealingShieldBoostBuff(byte Shield) if( IsLocallyControlled() ) { - UpdateActiveSkillsPath(class'KFPerk_FieldMedic'.default.PerkSkills[EMedicHealingShield].IconPath, Shield > 0.0f); + UpdateHealingShieldBoostBuff(Shield); } } -function UpdateActiveSkillsPath(string IconPath, bool Active) +reliable client function ClientNotifyHealingShieldBoostBuff(byte Shield) +{ + UpdateHealingShieldBoostBuff(Shield); +} + +simulated function UpdateHealingShieldBoostBuff(byte Shield) +{ + local float TotalTimes, Multiplier; + + TotalTimes = class'KFPerk_FieldMedic'.static.GetMaxHealingShield() / float(class'KFPerk_FieldMedic'.static.GetHealingShield()); + + Multiplier = Shield / float(class'KFPerk_FieldMedic'.static.GetMaxHealingShield()); + Multiplier *= TotalTimes; + + UpdateActiveSkillsPath(class'KFPerk_FieldMedic'.default.PerkSkills[EMedicHealingShield].IconPath + , Multiplier + , Shield > 0 + , class'KFPerk_FieldMedic'.static.GetHealingShieldDuration()); +} + +function UpdateActiveSkillsPath(string IconPath, int Multiplier, bool Active, float MaxDuration) { local KFPlayerController KFPC; + local int i, IndexSearch; + local ActiveSkill active_Skill; - if(Active) + if (Active) { - if (ActiveSkillIconPaths.Find(IconPath) == INDEX_NONE) + IndexSearch = ActiveSkills.Find('IconPath', IconPath); + + if (IndexSearch == INDEX_NONE) { - ActiveSkillIconPaths.AddItem(IconPath); + active_Skill.IconPath = IconPath; + active_Skill.Multiplier = Multiplier; + active_Skill.MaxDuration = MaxDuration; + active_Skill.Duration = MaxDuration; + + ActiveSkills.AddItem(active_Skill); + } + else + { + ActiveSkills[IndexSearch].Multiplier = Multiplier; + ActiveSkills[IndexSearch].MaxDuration = MaxDuration; + ActiveSkills[IndexSearch].Duration = MaxDuration; } } else { - ActiveSkillIconPaths.RemoveItem(IconPath); + for (i=0; i < ActiveSkills.Length; ++i) + { + if (ActiveSkills[i].IconPath == IconPath) + { + ActiveSkills.Remove(i, 1); + break; + } + } } KFPC = KFPlayerController(Controller); - KFPC.MyGFxHUD.PlayerStatusContainer.ShowActiveIndicators(ActiveSkillIconPaths); + KFPC.MyGFxHUD.PlayerStatusContainer.ShowActiveIndicators(ActiveSkills); } event Landed(vector HitNormal, actor FloorActor) diff --git a/KFGame/Classes/KFPawn_Monster.uc b/KFGame/Classes/KFPawn_Monster.uc index 267f775..fc17e50 100644 --- a/KFGame/Classes/KFPawn_Monster.uc +++ b/KFGame/Classes/KFPawn_Monster.uc @@ -1170,11 +1170,18 @@ simulated event Bump( Actor Other, PrimitiveComponent OtherComp, Vector HitNorma function HandleMonsterBump( KFPawn_Monster Other, Vector HitNormal ) { local KFPlayerController KFPC; + local int LocustDoTIndex; + local int IgnoredIndex; if( !Other.IsNapalmInfected() && CanNapalmInfect(KFPC) ) { InfectWithNapalm( Other, KFPC ); } + + if (IsLocustInfected(LocustDoTIndex) && !Other.IsLocustInfected(IgnoredIndex)) + { + InfectWithLocust(Other, KFPlayerController(DamageOverTimeArray[LocustDoTIndex].InstigatedBy)); + } } /** Override to handle special berserker functionality */ @@ -2648,12 +2655,19 @@ simulated function int GetRallyBoostResistance( int NewDamage ) function bool Died(Controller Killer, class DamageType, vector HitLocation) { + local KFGameInfo KFGI; local KFPlayerController KFPC; local KFPerk InstigatorPerk; local int i; if ( super.Died(Killer, damageType, HitLocation) ) { + KFGI = KFGameInfo(WorldInfo.Game); + if (KFGI != none) + { + KFGI.ClearActorFromBonfire(self); + } + if( Killer != none && Killer.Pawn != none && KFPawn_Human(Killer.Pawn) != none ) { `DialogManager.PlayKilledZedDialog( KFPawn_Human(Killer.Pawn), self, DamageType, IsDoingSpecialMove(SM_Knockdown) || IsDoingSpecialMove(SM_RecoverFromRagdoll) ); @@ -2761,6 +2775,12 @@ simulated function bool IsNapalmInfected() return DamageOverTimeArray.Find('DamageType', class'KFDT_Fire_Napalm') != INDEX_NONE; } +simulated function bool IsLocustInfected(out int OutDoTIndex) +{ + OutDoTIndex = DamageOverTimeArray.Find('DamageType', class'KFDT_Toxic_HRG_Locust'); + return OutDoTIndex != INDEX_NONE; +} + function bool CanNapalmInfect( out KFPlayerController NapalmInstigator ) { local int DoTIndex; @@ -2802,6 +2822,19 @@ function InfectWithNapalm( KFPawn_Monster KFPM, KFPlayerController KFPC ) } } +function InfectWithLocust( KFPawn_Monster KFPM, KFPlayerController KFPC ) +{ + if( KFPC != none ) + { + KFPM.TakeDamage( class'KFDT_Toxic_HRG_Locust'.static.GetSpreadOnTouchDamage(), + KFPC, + vect(0,0,0), + vect(0,0,0), + class'KFDT_Toxic_HRG_Locust',, + KFPC ); + } +} + /** * @brief Spawns a radioactive cloud that hurts other Zeds * @@ -3287,7 +3320,10 @@ simulated function PlayTakeHitEffects( vector HitDirection, vector HitLocation, if ( bPlayedDeath ) { - PlayDeadHitEffects(HitLocation, HitDirection, HitZoneIndex, HitZoneName, HitBoneName, DmgType, bUseHitImpulse); + if (DmgType.static.CanPlayDeadHitEffects()) + { + PlayDeadHitEffects(HitLocation, HitDirection, HitZoneIndex, HitZoneName, HitBoneName, DmgType, bUseHitImpulse); + } } else { diff --git a/KFGame/Classes/KFPerk.uc b/KFGame/Classes/KFPerk.uc index cdc6caa..94ededc 100644 --- a/KFGame/Classes/KFPerk.uc +++ b/KFGame/Classes/KFPerk.uc @@ -889,7 +889,7 @@ event NotifyPerkModified() PostLevelUp(); } -private simulated final function PerkSetOwnerHealthAndArmor( optional bool bModifyHealth ) +simulated final function PerkSetOwnerHealthAndArmor( optional bool bModifyHealth ) { // don't allow clients to set health, since health/healthmax/playerhealth/playerhealthpercent is replicated if( Role != ROLE_Authority ) @@ -906,7 +906,15 @@ private simulated final function PerkSetOwnerHealthAndArmor( optional bool bModi } OwnerPawn.HealthMax = OwnerPawn.default.Health; + ModifyHealth( OwnerPawn.HealthMax ); + + if (ModifyHealthMaxWeekly(OwnerPawn.HealthMax)) + { + // Change current health directly, Pawn.HealDamage does a lot of other stuff that can block the healing + OwnerPawn.Health = OwnerPawn.HealthMax; + } + OwnerPawn.Health = Min( OwnerPawn.Health, OwnerPawn.HealthMax ); if( OwnerPC == none ) @@ -927,6 +935,49 @@ private simulated final function PerkSetOwnerHealthAndArmor( optional bool bModi } } +function bool ModifyHealthMaxWeekly(out int InHealth) +{ + local KFGameReplicationInfo KFGRI; + local KFPlayerController_WeeklySurvival KFPC_WS; + local bool bNeedToFullyHeal; + + KFGRI = KFGameReplicationInfo(Owner.WorldInfo.GRI); + + bNeedToFullyHeal = false; + + //`Log("PerkSetOwnerHealthAndArmor: Max Health Before Weekly " $OwnerPawn.HealthMax); + + if (KFGRI.IsVIPMode()) + { + KFPC_WS = KFPlayerController_WeeklySurvival(Owner); + + if (KFPC_WS != none && OwnerPawn != none) + { + if (KFPC_WS.VIPGameData.isVIP) + { + // We don't need to check if already applied as this function resets the HealthMax to the value the Perk says + + InHealth += KFPC_WS.VIPGameData.ExtraHealth; + + // Heal if we are on trader time + if (KFGRI != none && KFGRI.bWaveIsActive == false) + { + bNeedToFullyHeal = true; + } + } + else + { + // We don't need to check if already applied as this function resets the HealthMax to the value the Perk says + // So no need to further reduce + } + } + } + + //`Log("PerkSetOwnerHealthAndArmor: Max Health " $OwnerPawn.HealthMax); + + return bNeedToFullyHeal; +} + /** (Server) Modify Instigator settings based on selected perk */ function ApplySkillsToPawn() { @@ -1419,6 +1470,7 @@ function TickRegen( float DeltaTime ) local int OldHealth; local KFPlayerReplicationInfo KFPRI; local KFPlayerController KFPC; + local KFPlayerController_WeeklySurvival KFPCWS; local KFPowerUp PowerUp; local bool bCannotBeHealed; local KFGameInfo GameInfo; @@ -1438,6 +1490,13 @@ function TickRegen( float DeltaTime ) GameInfo = KFGameInfo(WorldInfo.Game); bCannotBeHealed = bCannotBeHealed || (GameInfo.OutbreakEvent != none && GameInfo.OutbreakEvent.ActiveEvent.bCannotBeHealed); + // VIP cannot heal + KFPCWS = KFPlayerController_WeeklySurvival(OwnerPawn.Controller); + if (KFPCWS != none && KFPCWS.VIPGameData.IsVIP) + { + bCannotBeHealed = true; + } + // If the Pawn cannot be healed return... if( bCannotBeHealed ) { diff --git a/KFGame/Classes/KFPerk_Berserker.uc b/KFGame/Classes/KFPerk_Berserker.uc index f236255..d8bbe22 100644 --- a/KFGame/Classes/KFPerk_Berserker.uc +++ b/KFGame/Classes/KFPerk_Berserker.uc @@ -552,10 +552,12 @@ function NotifyZedTimeStarted() local KFGameInfo GameInfo; local bool bScaredAI; local bool bCannotBeHealed; + local KFGameReplicationInfo KFGRI; if( IsRageActive() && OwnerPawn != none ) { KFPC = KFPlayerController(OwnerPawn.Controller); + if( KFPC != none ) { PowerUp = KFPC.GetPowerUp(); @@ -566,6 +568,15 @@ function NotifyZedTimeStarted() { bCannotBeHealed = bCannotBeHealed ||(GameInfo.OutbreakEvent != none && GameInfo.OutbreakEvent.ActiveEvent.bCannotBeHealed); } + + // VIP cannot heal + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + if (KFGRI != none + && KFGRI.VIPRepPlayer != none + && KFGRI.VIPRepPlayer == KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo)) + { + bCannotBeHealed = true; + } } if( bCannotBeHealed == false ) diff --git a/KFGame/Classes/KFPerk_Commando.uc b/KFGame/Classes/KFPerk_Commando.uc index 3221592..1b77c41 100644 --- a/KFGame/Classes/KFPerk_Commando.uc +++ b/KFGame/Classes/KFPerk_Commando.uc @@ -504,7 +504,7 @@ final private function bool IsHealthIncreaseActive() * * @return true/false */ -final private function bool IsEatLeadActive() +simulated final private function bool IsEatLeadActive() { return PerkSkills[ECommandoEatLead].bActive && IsPerkLevelAllowed(ECommandoEatLead); } diff --git a/KFGame/Classes/KFPerk_Demolitionist.uc b/KFGame/Classes/KFPerk_Demolitionist.uc index 6a69548..7d31312 100644 --- a/KFGame/Classes/KFPerk_Demolitionist.uc +++ b/KFGame/Classes/KFPerk_Demolitionist.uc @@ -380,7 +380,7 @@ static function PrepareExplosive( Pawn ProjOwner, KFProjectile Proj, optional fl Proj.ExplosionTemplate.DamageRadius = Proj.default.ExplosionTemplate.DamageRadius * class'KFPerk_Demolitionist'.static.GetNukeRadiusModifier() * AuxRadiusMod; Proj.ExplosionTemplate.DamageFalloffExponent = Proj.default.ExplosionTemplate.DamageFalloffExponent; } - else if( InstigatorPRI.bConcussiveActive && Proj.AltExploEffects != none ) + else if( InstigatorPRI.bConcussiveActive && Proj.AltExploEffects != none && class'KFPerk_Demolitionist'.static.ProjectileShouldConcussive(Proj) ) { Proj.ExplosionTemplate.ExplosionEffects = Proj.AltExploEffects; Proj.ExplosionTemplate.ExplosionSound = class'KFPerk_Demolitionist'.static.GetConcussiveExplosionSound(); @@ -394,8 +394,13 @@ static function PrepareExplosive( Pawn ProjOwner, KFProjectile Proj, optional fl KFPC = KFPlayerController( ProjOwner.Controller ); if( KFPC != none ) { - InstigatorPerk = KFPC.GetPerk(); - Proj.ExplosionTemplate.DamageRadius *= InstigatorPerk.GetAoERadiusModifier() * AuxRadiusMod; + Proj.ExplosionTemplate.DamageRadius *= AuxRadiusMod; + + if (class'KFPerk_Demolitionist'.static.ProjectileShouldExplosionChangeRadius(Proj)) + { + InstigatorPerk = KFPC.GetPerk(); + Proj.ExplosionTemplate.DamageRadius *= InstigatorPerk.GetAoERadiusModifier(); + } } } } @@ -686,6 +691,16 @@ simulated static function bool ProjectileShouldNuke( KFProjectile Proj ) return Proj.AllowNuke(); } +simulated static function bool ProjectileShouldConcussive( KFProjectile Proj ) +{ + return Proj.AllowDemolitionistConcussive(); +} + +simulated static function bool ProjectileShouldExplosionChangeRadius( KFProjectile Proj ) +{ + return Proj.AllowDemolitionistExplosionChangeRadius(); +} + simulated function bool DoorShouldNuke() { return IsNukeActive() && WorldInfo.TimeDilation < 1.f; diff --git a/KFGame/Classes/KFPerk_FieldMedic.uc b/KFGame/Classes/KFPerk_FieldMedic.uc index f82ab66..76add66 100644 --- a/KFGame/Classes/KFPerk_FieldMedic.uc +++ b/KFGame/Classes/KFPerk_FieldMedic.uc @@ -213,11 +213,15 @@ simulated function ModifyMagSizeAndNumber( KFWeapon KFW, out int MagazineCapacit TempCapacity = MagazineCapacity; - if( IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && (KFW == none || !KFW.bNoMagazine) && !bSecondary ) + // Fix this function on the trader because KFW is None and the check cannot look for bNoMagazine = true + if(WeaponClassname != 'KFWeap_Rifle_HRGIncision') { - if( IsCombatantActive() ) + if( IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && (KFW == none || !KFW.bNoMagazine) && !bSecondary ) { - TempCapacity += MagazineCapacity * GetSkillValue( PerkSkills[EMedicCombatant] ); + if( IsCombatantActive() ) + { + TempCapacity += MagazineCapacity * GetSkillValue( PerkSkills[EMedicCombatant] ); + } } } diff --git a/KFGame/Classes/KFPerk_Firebug.uc b/KFGame/Classes/KFPerk_Firebug.uc index 51268e4..7cda5ca 100644 --- a/KFGame/Classes/KFPerk_Firebug.uc +++ b/KFGame/Classes/KFPerk_Firebug.uc @@ -273,13 +273,12 @@ simulated function ModifyMagSizeAndNumber( KFWeapon KFW, out int MagazineCapacit TempCapacity = MagazineCapacity; // Fix this function on the trader because KFW is None and the check cannot look for bNoMagazine = true - if(WeaponClassname == 'KFWeap_Pistol_HRGScorcher') + if(WeaponClassname != 'KFWeap_Pistol_HRGScorcher' && WeaponClassname != 'KFWeap_HRG_Dragonbreath') { - TempCapacity = TempCapacity; - } - else if( IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && IsHighCapFuelTankActive() && (KFW == none || !KFW.bNoMagazine) ) - { - TempCapacity += MagazineCapacity * GetSkillValue( PerkSkills[EFirebugHighCapFuelTank] ); + if( IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && IsHighCapFuelTankActive() && (KFW == none || !KFW.bNoMagazine) ) + { + TempCapacity += MagazineCapacity * GetSkillValue( PerkSkills[EFirebugHighCapFuelTank] ); + } } MagazineCapacity = Round(TempCapacity); diff --git a/KFGame/Classes/KFPlayerController.uc b/KFGame/Classes/KFPlayerController.uc index 442eadc..0fa0d95 100644 --- a/KFGame/Classes/KFPlayerController.uc +++ b/KFGame/Classes/KFPlayerController.uc @@ -1631,6 +1631,13 @@ function OnReadProfileSettingsComplete(byte LocalUserNum,bool bWasSuccessful) KFInput.SetGamepadLayout(Profile.GetProfileInt(KFID_CurrentLayoutIndex)); KFInput.bToggleToRun = Profile.GetProfileBool(KFID_ToggleToRun); KFInput.bAllowSwapTo9mm = Profile.GetProfileBool(KFID_AllowSwapTo9mm); + + // Console?? PC?? + KFInput.MouseLookUpScale = Profile.GetProfileFloat(KFID_MouseLookUpScale); + KFInput.MouseLookRightScale = Profile.GetProfileFloat(KFID_MouseLookRightScale); + KFInput.bViewSmoothingEnabled = Profile.GetProfileBool(KFID_ViewSmoothingEnabled); + KFInput.bViewAccelerationEnabled = Profile.GetProfileBool(KFID_ViewAccelerationEnabled); + KFInput.ReInitializeControlsUI(); } @@ -2758,6 +2765,30 @@ public function bool CanUseGunGame() return false; } +public function bool CanUseVIP() +{ + /** If this is run in Server or Standalone, GameInfo exists so can access to the OutbreakEvent */ + if (Role == Role_Authority) + { + return KFGameInfo(WorldInfo.Game).OutbreakEvent != none + && KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bVIPGameMode; + } + /** But in client, GameInfo doesn't exist, so needs to be checked in a different way. */ + else + { + /** + In client there's a kfgame replication info that contains if the mode is a weekly, and the index. + This way would also work in server, but will need to be in code rather than using the weekly variables. + */ + /** Another option is to use instead a variable replicated just with that value */ + return KFGameReplicationInfo(WorldInfo.GRI) != none + && KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode + && KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 17; + } + + return false; +} + /********************************************************************************************* * @name Skill Tracking ********************************************************************************************* */ @@ -10516,6 +10547,10 @@ protected function MotivatePlayerToMove() SetTimer( class'KFVersusNoGoVolume'.static.GetNoGoHurtInterval(), true, nameOf(MotivatePlayerToMove) ); } +function AdjustDamage(out int InDamage, Controller InstigatedBy, class DamageType, Actor DamageCauser, Actor DamageReceiver) +{ +} + exec function GCF() { if ( MyGFxManager != None ) diff --git a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc index cb7fa5a..a3cc9ce 100644 --- a/KFGame/Classes/KFPlayerController_WeeklySurvival.uc +++ b/KFGame/Classes/KFPlayerController_WeeklySurvival.uc @@ -41,6 +41,10 @@ var protected const AkEvent AracnoStompSoundEvent; var protected const AKEvent GunGameLevelUpSoundEvent; var protected const AKEvent GunGameLevelUpFinalWeaponSoundEvent; +var protected const AKEvent VIPChosenSoundEvent; +var protected const AKEvent VIPLowHealthSoundEvent; +var protected float VIPLowHealthLastTimePlayed; + struct native GunGameInfo { var transient byte Level; @@ -59,6 +63,49 @@ structdefaultproperties }; var transient GunGameInfo GunGameData; +struct native VIPGameInfo +{ + var bool IsVIP; + var bool WasVIP; + var bool PendingHealthReset; + + var int ExtraHealth; + + var int DamageHealthLimit; + var int DamageHealthTop; + var int DamageHealthBottom; + + var float DamageLimitModifier; + + var float OutputDamageTopModifier; + var float InputDamageTopModifier; + + var float OutputDamageBottomModifier; + var float InputDamageBottomModifier; + + structdefaultproperties + { + IsVIP = false + WasVIP = false + PendingHealthReset = false + + ExtraHealth = 100 + + DamageHealthLimit = 100 + DamageHealthTop = 50 + DamageHealthBottom = 25 + + DamageLimitModifier = 1.0 + + OutputDamageTopModifier = 1.5 + InputDamageTopModifier = 0.75 + + OutputDamageBottomModifier = 1.75 + InputDamageBottomModifier = 0.5 + } +}; +var transient VIPGameInfo VIPGameData; + cpptext { virtual UBOOL TestZedTimeVisibility(APawn* P, UNetConnection* Connection, UBOOL bLocalPlayerTest) override; @@ -148,6 +195,31 @@ reliable client function UpdateGunGameWidget(int score, int max_score, int level } } +simulated function UpdateVIPWidget(ReplicatedVIPGameInfo VIPInfo) +{ + if (MyGFxHUD != none) + { + MyGFxHUD.UpdateVIP(VIPInfo, VIPInfo.VIPPlayer == PlayerReplicationInfo); + } +} + +function bool CanUseHealObject() +{ + local KFGameReplicationInfo KFGRI; + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + // VIP cannot heal + if (KFGRI != none + && KFGRI.VIPRepPlayer != none + && KFGRI.VIPRepPlayer == KFPlayerReplicationInfo(PlayerReplicationInfo)) + { + return false; + } + + return super.CanUseHealObject(); +} + /** Arachnophobia Goompa Stomp Streak functions */ @@ -247,6 +319,34 @@ reliable client function PlayGunGameMessage(bool isLastLevel) } } +reliable client function PlayVIPSound_ChosenInternal() +{ + if (VIPChosenSoundEvent != none) + { + PlaySoundBase(VIPChosenSoundEvent); + } +} + +reliable client function PlayVIPGameChosenSound(float delay) +{ + // Put a timer because the sound happens at the same time as end wave and it's difficult to distinguish + SetTimer(delay, false, nameof(PlayVIPSound_ChosenInternal)); +} + +reliable client function PlayVIPGameLowHealthSound() +{ + if (VIPLowHealthSoundEvent != none) + { + if (WorldInfo.TimeSeconds - VIPLowHealthLastTimePlayed > 8.f) + { + VIPLowHealthLastTimePlayed = WorldInfo.TimeSeconds; + + PlaySoundBase(VIPLowHealthSoundEvent); + } + } +} + + /** Resets all gameplay FX to initial state. Append to this list if additional effects are added. */ function ResetGameplayPostProcessFX() @@ -333,6 +433,174 @@ function UpdateInitialHeldWeapon() } } +function AdjustDamage(out int InDamage, Controller InstigatedBy, class DamageType, Actor DamageCauser, Actor DamageReceiver) +{ + local KFGameInfo KFGI; + local float Multiplier, ModifierRange, HealthTop, HealthRange; + local KFGameReplicationInfo KFGRI; + + super.AdjustDamage(InDamage, InstigatedBy, DamageType, DamageCauser, DamageReceiver); + + KFGI = KFGameInfo(WorldInfo.Game); + + if (Pawn != None && KFGI != none && KFGI.OutbreakEvent != none && KFGI.OutbreakEvent.ActiveEvent.bVIPGameMode) + { + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + // If I am the VIP doing the damage, and I am NOT doing damage to myself + if (KFGRI != none + && KFGRI.VIPRepPlayer != none + && KFGRI.VIPRepPlayer == KFPlayerReplicationInfo(PlayerReplicationInfo) + && InstigatedBy == self + && DamageReceiver != self.Pawn) + { + Multiplier = 1.0; + + //`Log("Current health for VIP OUTPUT DAMAGE: " $Pawn.Health); + + if (Pawn.Health < VIPGameData.DamageHealthLimit) + { + if (Pawn.Health <= VIPGameData.DamageHealthBottom) + { + Multiplier = VIPGameData.OutputDamageBottomModifier; + } + else + { + if (Pawn.Health > VIPGameData.DamageHealthTop) + { + Multiplier = VIPGameData.DamageLimitModifier; + + // From 1.0 to 1.5 on the range of 100 - 50 + ModifierRange = VIPGameData.OutputDamageTopModifier - VIPGameData.DamageLimitModifier; + + HealthTop = VIPGameData.DamageHealthLimit; + HealthRange = Abs(HealthTop - VIPGameData.DamageHealthTop); + } + else + { + // From 1.5 to 1.75 on the range of 50 - 25 + Multiplier = VIPGameData.OutputDamageTopModifier; + + ModifierRange = VIPGameData.OutputDamageBottomModifier - VIPGameData.OutputDamageTopModifier; + + HealthTop = VIPGameData.DamageHealthTop; + HealthRange = Abs(HealthTop - VIPGameData.DamageHealthBottom); + } + + Multiplier += ModifierRange * ((HealthTop - Pawn.Health) / HealthRange); + } + } + else + { + Multiplier = VIPGameData.DamageLimitModifier; + } + + //`Log("Multiplier for VIP OUTPUT DAMAGE: Output: " $Multiplier); + + InDamage = int(float(InDamage) * Multiplier); + } + } +} + +function AdjustVIPDamage(out int InDamage, Controller InstigatedBy) +{ + local KFGameInfo KFGI; + local float Multiplier, ModifierRange, HealthTop, HealthRange; + local KFGameReplicationInfo KFGRI; + + KFGI = KFGameInfo(WorldInfo.Game); + if (Pawn != None && KFGI != none && KFGI.OutbreakEvent != none && KFGI.OutbreakEvent.ActiveEvent.bVIPGameMode) + { + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + // If I am the VIP + // We do it on a different step as don't want to scale InDamage to VIP Armour when receiving damage + if (KFGRI != none + && KFGRI.VIPRepPlayer != none + && KFGRI.VIPRepPlayer == KFPlayerReplicationInfo(PlayerReplicationInfo)) + { + Multiplier = 1.0; + + //`Log("Current health for VIP INPUT DAMAGE: " $Pawn.Health); + + if (Pawn.Health < VIPGameData.DamageHealthLimit) + { + if (Pawn.Health <= VIPGameData.DamageHealthBottom) + { + Multiplier = VIPGameData.InputDamageBottomModifier; + } + else + { + if (Pawn.Health > VIPGameData.DamageHealthTop) + { + Multiplier = VIPGameData.DamageLimitModifier; + + // From 1.0 to 0.5 on the range of 100 - 50 + ModifierRange = VIPGameData.InputDamageTopModifier - VIPGameData.DamageLimitModifier; + + HealthTop = VIPGameData.DamageHealthLimit; + HealthRange = Abs(HealthTop - VIPGameData.DamageHealthTop); + } + else + { + // From 0.5 to 0.25 on the range of 50 - 25 + Multiplier = VIPGameData.InputDamageTopModifier; + + ModifierRange = VIPGameData.InputDamageBottomModifier - VIPGameData.InputDamageTopModifier; + + HealthTop = VIPGameData.DamageHealthTop; + HealthRange = Abs(HealthTop - VIPGameData.DamageHealthBottom); + } + + Multiplier += ModifierRange * ((HealthTop - Pawn.Health) / HealthRange); + } + } + else + { + Multiplier = VIPGameData.DamageLimitModifier; + } + + //`Log("Multiplier for VIP INPUT DAMAGE: Output: " $Multiplier); + + InDamage = int(float(InDamage) * Multiplier); + } + } +} + +function NotifyTakeHit(Controller InstigatedBy, vector HitLocation, int Damage, class damageType, vector Momentum) +{ + local KFPlayerController_WeeklySurvival KFPC_WS; + + Super.NotifyTakeHit(InstigatedBy,HitLocation,Damage,damageType,Momentum); + + if (VIPGameData.IsVIP) + { + // Only sound once we pass down 50, sound again if recovered health and go down again + if (Pawn.Health < 50 && Pawn.Health + Damage >= 50) + { + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + KFPC_WS.PlayVIPGameLowHealthSound(); + } + } + } +} + +function UpdateVIPDamage() +{ + local KFGameReplicationInfo KFGRI; + + if (VIPGameData.IsVIP) + { + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + if (KFGRI != none) + { + KFGRI.UpdateVIPCurrentHealth(Pawn.Health); + } + } +} + // defaultProperties { @@ -346,4 +614,7 @@ defaultProperties AracnoStompSoundEvent =AkEvent'WW_GLO_Runtime.WeeklyArcno' GunGameLevelUpSoundEvent=AkEvent'WW_GLO_Runtime.WeeklyAALevelUp' GunGameLevelUpFinalWeaponSoundEvent=AkEvent'WW_GLO_Runtime.WeeklyAALevelFinal' + VIPChosenSoundEvent=AkEvent'WW_UI_Menu.Play_AAR_TOPWEAPON_SLIDEIN_B' + VIPLowHealthSoundEvent=AkEvent'WW_GLO_Runtime.WeeklyVIPAlarm' + VIPLowHealthLastTimePlayed = 0.f } diff --git a/KFGame/Classes/KFPlayerInput.uc b/KFGame/Classes/KFPlayerInput.uc index 85de67d..4d3046f 100644 --- a/KFGame/Classes/KFPlayerInput.uc +++ b/KFGame/Classes/KFPlayerInput.uc @@ -241,6 +241,12 @@ var bool bVersusInput; /** Cached value of bUsingGamepad so we can handle button releases across devices */ var bool bUsingVersusGamepadScheme; +/********************************************************************************************* + * @name QoL: Mouse input options +********************************************************************************************* */ +var float MouseLookUpScale; +var float MouseLookRightScale; + cpptext { /** Searches the bind and skips the mainCommand */ @@ -405,6 +411,7 @@ function EDoubleClickDir CheckForDoubleClickMove(float DeltaTime) event PlayerInput( float DeltaTime ) { local float FOVScale, TimeScale; + local float MouseYScale, MouseXScale; local vector RawJoyVector; // Save Raw values @@ -437,8 +444,12 @@ event PlayerInput( float DeltaTime ) aBaseY *= TimeScale * MoveForwardSpeed; aStrafe *= TimeScale * MoveStrafeSpeed; aUp *= TimeScale * MoveStrafeSpeed; - aTurn *= TimeScale * LookRightScale; - aLookUp *= TimeScale * LookUpScale; + + if (class'WorldInfo'.static.IsConsoleBuild() || bUsingGamepad) + { + aTurn *= TimeScale * LookRightScale; + aLookUp *= TimeScale * LookUpScale; + } PostProcessInput( DeltaTime ); @@ -462,14 +473,16 @@ event PlayerInput( float DeltaTime ) aLookUp *= FOVScale; aTurn *= FOVScale; + MouseXScale = (TimeScale * -MouseLookUpScale / 100.0f); // Turning and strafing share the same axis. if( bStrafe > 0 ) - aStrafe += aBaseX + aMouseX; + aStrafe += aBaseX + aMouseX * ( MouseXScale > 0.0f ? MouseXScale : 1.0f); else - aTurn += aBaseX + aMouseX; - + aTurn += aBaseX + aMouseX * ( MouseXScale > 0.0f ? MouseXScale : 1.0f); + // Look up/down. - aLookup += aMouseY; + MouseYScale = (TimeScale * -MouseLookUpScale / 100.0f); + aLookup += aMouseY * ( MouseYScale > 0.0f ? MouseYScale : 1.0f); if ( (!bUsingGamepad && bInvertMouse) || (bInvertController && bUsingGamepad) ) { aLookup *= -1.f; diff --git a/KFGame/Classes/KFPlayerReplicationInfo.uc b/KFGame/Classes/KFPlayerReplicationInfo.uc index 13472cc..f612933 100644 --- a/KFGame/Classes/KFPlayerReplicationInfo.uc +++ b/KFGame/Classes/KFPlayerReplicationInfo.uc @@ -113,7 +113,7 @@ var private byte ActivePerkLevel; var private byte ActivePerkPrestigeLevel; /** Kill assists. Need an integer here because it is very easy to exceed 255 assists. */ var int Assists; -var byte PlayerHealth; //represented as a percentage +var int PlayerHealth; var byte PlayerHealthPercent; //represented as a percentage /** The firebug range skill increases the range of fire weapons we need to tell other clients if it is on */ var bool bExtraFireRange; diff --git a/KFGame/Classes/KFProfileSettings.uc b/KFGame/Classes/KFProfileSettings.uc index 73e8428..5d06255 100644 --- a/KFGame/Classes/KFProfileSettings.uc +++ b/KFGame/Classes/KFProfileSettings.uc @@ -386,4 +386,17 @@ defaultproperties DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SurvivalStartingWeapIdx,Data=(Type=SDT_Int32,Value1=0)))) ProfileMappings.Add((Id=KFID_SurvivalStartingGrenIdx, Name="Survival Starting Grenade Index", MappingType=PVMT_RawValue)) DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_SurvivalStartingGrenIdx,Data=(Type=SDT_Int32,Value1=0)))) + + // Added 07/06/2022 - QoL: Add mouse options to menu + ProfileMappings.Add((Id=KFID_MouseLookUpScale, Name="Mouse_Look_Up_Scale", MappingType=PVMT_RawValue)) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_MouseLookUpScale,Data=(Type=SDT_Float,Value1=0xc2c80000)))) // -100 + + ProfileMappings.Add((Id=KFID_MouseLookRightScale, Name="Mouse_Look_Right_Scale", MappingType=PVMT_RawValue)) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_MouseLookRightScale,Data=(Type=SDT_Float,Value1=0x42c80000)))) //100 + + ProfileMappings.Add((Id=KFID_ViewSmoothingEnabled, Name="View_Smoothing_Enabled", MappingType=PVMT_RawValue)) + DefaultSettings.Add((Owner=OPPO_Game,ProfileSetting=(PropertyId=KFID_ViewSmoothingEnabled,Data=(Type=SDT_Int32,Value1=0)))) + + 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)))) } diff --git a/KFGame/Classes/KFProj_Grenade.uc b/KFGame/Classes/KFProj_Grenade.uc index 44e4d72..2e74822 100644 --- a/KFGame/Classes/KFProj_Grenade.uc +++ b/KFGame/Classes/KFProj_Grenade.uc @@ -254,6 +254,9 @@ simulated event GrenadeIsAtRest() /** Overriding so that the grenade doesn't take on the */ simulated protected function PrepareExplosionTemplate() { + local Weapon OwnerWeapon; + local KFPawn_Human OwnerPawn; + if (bUpgradable) { super.PrepareExplosionTemplate(); @@ -261,6 +264,16 @@ simulated protected function PrepareExplosionTemplate() else { GetRadialDamageValues(ExplosionTemplate.Damage, ExplosionTemplate.DamageRadius, ExplosionTemplate.DamageFalloffExponent); + + OwnerWeapon = Weapon(Owner); + if (OwnerWeapon != none) + { + OwnerPawn = KFPawn_Human(OwnerWeapon.Owner); + if (OwnerPawn != none) + { + ExplosionTemplate.DamageRadius *= OwnerPawn.GetPerk().GetAoERadiusModifier(); + } + } } } diff --git a/KFGame/Classes/KFProjectile.uc b/KFGame/Classes/KFProjectile.uc index 378add2..80e42b1 100644 --- a/KFGame/Classes/KFProjectile.uc +++ b/KFGame/Classes/KFProjectile.uc @@ -1117,6 +1117,18 @@ simulated function bool AllowNuke() return true; } +/** Can be overridden in subclasses to exclude specific projectiles from using changing explosion radius */ +simulated function bool AllowDemolitionistExplosionChangeRadius() +{ + return true; +} + +/** Can be overridden in subclasses to exclude specific projectiles from using concussive */ +simulated function bool AllowDemolitionistConcussive() +{ + return true; +} + /** * Give the projectiles a chance to situationally customize/tweak any explosion parameters. * We will also copy in any data we exposed here for .ini file access. diff --git a/KFGame/Classes/KFSkinTypeEffects.uc b/KFGame/Classes/KFSkinTypeEffects.uc index 8bbbf8c..8b4aedd 100644 --- a/KFGame/Classes/KFSkinTypeEffects.uc +++ b/KFGame/Classes/KFSkinTypeEffects.uc @@ -85,13 +85,20 @@ cpptext virtual void PostLoad(); } -function PlayImpactParticleEffect( - KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, EEffectDamageGroup EffectGroup) +function PlayImpactParticleEffect(KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, EEffectDamageGroup EffectGroup, optional ParticleSystem ForceParticleTemplate) { local ParticleSystem ParticleTemplate; local name HitBoneName; - ParticleTemplate = GetImpactParticleEffect(EffectGroup); + if (ForceParticleTemplate != none) + { + ParticleTemplate = ForceParticleTemplate; + } + else + { + ParticleTemplate = GetImpactParticleEffect(EffectGroup); + } + if (ParticleTemplate == None) { return; @@ -212,7 +219,7 @@ function ParticleSystem GetImpactParticleEffect(EEffectDamageGroup EffectGroup) } /** Play an impact sound on taking damage */ -function PlayTakeHitSound(KFPawn P, vector HitLocation, Pawn DamageCauser, EEffectDamageGroup EffectGroup) +function PlayTakeHitSound(KFPawn P, vector HitLocation, Pawn DamageCauser, EEffectDamageGroup EffectGroup, optional AKEvent ForceImpactSound) { local AKEvent ImpactSound; local float ArmorPct; @@ -225,7 +232,14 @@ function PlayTakeHitSound(KFPawn P, vector HitLocation, Pawn DamageCauser, EEffe return; } - ImpactSound = GetImpactSound(EffectGroup, DamageCauser, P); + if (ForceImpactSound != none) + { + ImpactSound = ForceImpactSound; + } + else + { + ImpactSound = GetImpactSound(EffectGroup, DamageCauser, P); + } if (ShouldSetArmorValue(P, ArmorPct)) { diff --git a/KFGame/Classes/KFSpawnVolume.uc b/KFGame/Classes/KFSpawnVolume.uc index e0ca618..0d7f9d7 100644 --- a/KFGame/Classes/KFSpawnVolume.uc +++ b/KFGame/Classes/KFSpawnVolume.uc @@ -129,7 +129,7 @@ var() float MaxDistanceToPlayer; var() bool bOutOfSight; /** Result of last time this volume was rated & sorted */ -var const transient float CurrentRating; +var transient float CurrentRating; /** Cached visibility for performance */ var const transient bool bCachedVisibility; diff --git a/KFGame/Classes/KFUnlockManager.uc b/KFGame/Classes/KFUnlockManager.uc index 1c3cd59..b5b4324 100644 --- a/KFGame/Classes/KFUnlockManager.uc +++ b/KFGame/Classes/KFUnlockManager.uc @@ -34,7 +34,9 @@ enum ESharedContentUnlock SCU_ParasiteImplanter, SCU_Doshinegun, SCU_AutoTurret, - SCU_ShrinkRayGun + SCU_ShrinkRayGun, + SCU_Scythe, + SCU_G36C }; @@ -373,4 +375,12 @@ defaultproperties Name=KFWeap_ShrinkRayGun, IconPath="WEP_UI_ShrinkRay_Gun_TEX.UI_Weapon_Select_Shrink_Ray_Gun", ID=9290)} + SharedContentList(SCU_Scythe)={( + Name=KFWeap_Edged_Scythe, + IconPath="WEP_UI_Scythe_TEX.UI_WeaponSelect_Scythe", + ID=9478)} + SharedContentList(SCU_G36C)={( + Name=KFWeap_AssaultRifle_G36C, + IconPath="WEP_UI_G36C_TEX.UI_WeaponSelect_G36C", + ID=9484)} } diff --git a/KFGame/Classes/KFWeapDef_G36C.uc b/KFGame/Classes/KFWeapDef_G36C.uc new file mode 100644 index 0000000..976169f --- /dev/null +++ b/KFGame/Classes/KFWeapDef_G36C.uc @@ -0,0 +1,29 @@ +//============================================================================= +// KFWeaponDefintion +//============================================================================= +// A lightweight container for basic weapon properties that can be safely +// accessed without a weapon actor (UI, remote clients). +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_G36C extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_AssaultRifle_G36C" + + BuyPrice=1600 + AmmoPricePerMag=36 + ImagePath="wep_ui_g36c_tex.UI_WeaponSelect_G36C" + + EffectiveRange=70 + + UpgradePrice[0]=1500 + + UpgradeSellPrice[0]=1125 + + SharedUnlockId=SCU_G36C + +} diff --git a/KFGame/Classes/KFWeapDef_HRG_Dragonbreath.uc b/KFGame/Classes/KFWeapDef_HRG_Dragonbreath.uc new file mode 100644 index 0000000..48608fa --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_Dragonbreath.uc @@ -0,0 +1,24 @@ +//============================================================================= +// KFWeapDef_Dragonbreath +// This is the Doomstick +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2017 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_Dragonbreath extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_Dragonbreath" + + BuyPrice=1400 + AmmoPricePerMag=25 + ImagePath="WEP_UI_Quad_Barrel_TEX.UI_WeaponSelect_QuadBarrel" + + EffectiveRange=25 + + UpgradePrice[0]=1500 + + UpgradeSellPrice[0]=1125 +} diff --git a/KFGame/Classes/KFWeapDef_HRG_Locust.uc b/KFGame/Classes/KFWeapDef_HRG_Locust.uc new file mode 100644 index 0000000..ed63eb3 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_HRG_Locust.uc @@ -0,0 +1,28 @@ +//============================================================================= +// KFWeapDef_HRG_Locust +//============================================================================= +// A lightweight container for basic weapon properties that can be safely +// accessed without a weapon actor (UI, remote clients). +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_HRG_Locust extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_HRG_Locust" + + BuyPrice=900 + AmmoPricePerMag=40 + ImagePath="wep_ui_hrg_locust_tex.UI_WeaponSelect_HRG_Locust" + + EffectiveRange=100 + + UpgradePrice[0]=700 + UpgradePrice[1]=1500 + + UpgradeSellPrice[0]=525 + UpgradeSellPrice[1]=1650 +} \ No newline at end of file diff --git a/KFGame/Classes/KFWeapDef_Scythe.uc b/KFGame/Classes/KFWeapDef_Scythe.uc new file mode 100644 index 0000000..55c4588 --- /dev/null +++ b/KFGame/Classes/KFWeapDef_Scythe.uc @@ -0,0 +1,26 @@ +//============================================================================= +// KFWeapDef_Scythe +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeapDef_Scythe extends KFWeaponDefinition + abstract; + +DefaultProperties +{ + WeaponClassPath="KFGameContent.KFWeap_Edged_Scythe" + + BuyPrice=1500 + ImagePath="WEP_UI_Scythe_TEX.UI_WeaponSelect_Scythe" + + EffectiveRange=5 + + UpgradePrice[0]=1500 + + UpgradeSellPrice[0]=1125 + + SharedUnlockId=SCU_Scythe + +} diff --git a/KFGame/Classes/KFWeap_HealerBase.uc b/KFGame/Classes/KFWeap_HealerBase.uc index 39030f9..0297720 100644 --- a/KFGame/Classes/KFWeap_HealerBase.uc +++ b/KFGame/Classes/KFWeap_HealerBase.uc @@ -228,11 +228,21 @@ simulated function bool CanReload(optional byte FireModeNum); /** Instead of switch fire mode use as immediate alt fire */ simulated function AltFireMode() { + local KFPlayerController_WeeklySurvival Instigator_KFPC_WS; + if ( !Instigator.IsLocallyControlled() ) { return; } + Instigator_KFPC_WS = KFPlayerController_WeeklySurvival(Instigator.Controller); + + if (Instigator_KFPC_WS != none && Instigator_KFPC_WS.VIPGameData.IsVIP) + { + // VIP can't heal himself + return; + } + // StartFire - StopFire called from KFPlayerInput StartFire(ALTFIRE_FIREMODE); } diff --git a/KFGame/Classes/KFWeapon.uc b/KFGame/Classes/KFWeapon.uc index e403c73..38da260 100644 --- a/KFGame/Classes/KFWeapon.uc +++ b/KFGame/Classes/KFWeapon.uc @@ -1618,7 +1618,7 @@ simulated function AttachLaserSight() } } -function GunGameRemove() +function RemoveGun() { if (Instigator != none && Instigator.InvManager != none) { diff --git a/KFGame/Classes/KFWeaponSkinList.uc b/KFGame/Classes/KFWeaponSkinList.uc index 068e8e1..80fd17f 100644 --- a/KFGame/Classes/KFWeaponSkinList.uc +++ b/KFGame/Classes/KFWeaponSkinList.uc @@ -3675,6 +3675,42 @@ defaultproperties //Reducto Ray Lucky Strike Skins.Add((Id=9295, Weapondef=class'KFWeapDef_ShrinkRayGun', MIC_1P=("wep_skinset55_mat.Wep_1P_LuckyStrike_ShrinkRay_Gun_MIC","WEP_1P_ShrinkRay_Gun_MAT.WEP_ShrinkRay_Glass_MIC"), MIC_3P="wep_skinset55_mat.Wep_3P_LuckyStrike_ShrinkRay_Gun_MIC", MIC_Pickup="wep_skinset55_mat.Wep_3P_LuckyStrike_ShrinkRay_Pickup_MIC")) +//Blood Sickle Standard + Skins.Add((Id=9478, Weapondef=class'KFWeapDef_Scythe', MIC_1P=("wep_1p_scythe_mat.Wep_1stP_Scythe_MIC"), MIC_3P="WEP_3P_Scythe_MAT.WEP_3P_Scythe_MIC", MIC_Pickup="wep_3p_scythe_mat.WEP_3P_ScythePickup_MIC")) + +//Blood Sickle Bloodbath + Skins.Add((Id=9473, Weapondef=class'KFWeapDef_Scythe', MIC_1P=("WEP_SkinSet65_MAT.Wep_1P_Bloodbath_Scythe_MIC"), MIC_3P="WEP_SkinSet65_MAT.Wep_3P_Bloodbath_Scythe_MIC", MIC_Pickup="WEP_SkinSet65_MAT.Wep_3P_Bloodbath_Scythe_Pickup_MIC")) + +//Blood Sickle Butchery + Skins.Add((Id=9474, Weapondef=class'KFWeapDef_Scythe', MIC_1P=("WEP_SkinSet65_MAT.Wep_1P_Butchery_Scythe_MIC"), MIC_3P="WEP_SkinSet65_MAT.Wep_3P_Butchery_Scythe_MIC", MIC_Pickup="WEP_SkinSet65_MAT.Wep_3P_Butchery_Scythe_Pickup_MIC")) + +//Blood Sickle Carousel + Skins.Add((Id=9475, Weapondef=class'KFWeapDef_Scythe', MIC_1P=("WEP_SkinSet65_MAT.Wep_1P_Carousel_Scythe_MIC"), MIC_3P="WEP_SkinSet65_MAT.Wep_3P_Carousel_Scythe_MIC", MIC_Pickup="WEP_SkinSet65_MAT.Wep_3P_Caurel_Scythe_Pickup_MIC")) + +//Blood Sickle Hunter + Skins.Add((Id=9477, Weapondef=class'KFWeapDef_Scythe', MIC_1P=("WEP_SkinSet65_MAT.Wep_1P_Hunter_Scythe_MIC"), MIC_3P="WEP_SkinSet65_MAT.Wep_3P_Hunter_Scythe_MIC", MIC_Pickup="WEP_SkinSet65_MAT.Wep_3P_Hunter_Scythe_Pickup_MIC")) + +//Blood Sickle Reaper + Skins.Add((Id=9476, Weapondef=class'KFWeapDef_Scythe', MIC_1P=("WEP_SkinSet65_MAT.Wep_1P_Reaper_Scythe_MIC"), MIC_3P="WEP_SkinSet65_MAT.Wep_3P_Reaper_Scythe_MIC", MIC_Pickup="WEP_SkinSet65_MAT.Wep_3P_Reaper_Scythe_Pickup_MIC")) + +//G36C Standard + Skins.Add((Id=9484, Weapondef=class'KFWeapDef_G36C', MIC_1P=("wep_1p_g36c_mat.Wep_1stP_G36C_MIC","wep_1p_g36c_mat.Wep_1stP_G36C_Scope_MIC"), MIC_3P="wep_3p_g36c_mat.Wep_3rdP_G36C_MIC", MIC_Pickup="wep_3p_g36c_mat.3P_Pickup_G36C_MIC")) + +//G36C Aftermath + Skins.Add((Id=9481, Weapondef=class'KFWeapDef_G36C', MIC_1P=("wep_skinset64_mat.Wep_1P_Aftermatch_G36C_MIC","wep_skinset64_mat.Wep_1P_Aftermatch_Scope_G36C_MIC"), MIC_3P="wep_skinset64_mat.Wep_3P_Aftermatch_G36C_MIC", MIC_Pickup="wep_skinset64_mat.Wep_3P_Aftermatch_G36C_Pickup_MIC")) + +//G36C Dazzle + Skins.Add((Id=9483, Weapondef=class'KFWeapDef_G36C', MIC_1P=("wep_skinset64_mat.Wep_1P_Dazzle_G36C_MIC","wep_skinset64_mat.Wep_1P_Dazzle_Scope_G36C_MIC"), MIC_3P="wep_skinset64_mat.Wep_3P_Dazzle_G36C_MIC", MIC_Pickup="wep_skinset64_mat.Wep_3P_Dazzle_G36C_Pickup_MIC")) + +//G36C Icepack + Skins.Add((Id=9480, Weapondef=class'KFWeapDef_G36C', MIC_1P=("wep_skinset64_mat.Wep_1P_Icepack_G36C_MIC","wep_skinset64_mat.Wep_1P_Icepack_Scope_G36C_MIC"), MIC_3P="wep_skinset64_mat.Wep_3P_Icepack_G36C_MIC", MIC_Pickup="wep_skinset64_mat.Wep_3P_Icepack_G36C_Pickup_MIC")) + +//G36C Jungle + Skins.Add((Id=9482, Weapondef=class'KFWeapDef_G36C', MIC_1P=("wep_skinset64_mat.Wep_1P_Jungle_G36C_MIC","wep_skinset64_mat.Wep_1P_Jungle_Scope_G36C_MIC"), MIC_3P="wep_skinset64_mat.Wep_3P_Jungle_G36C_MIC", MIC_Pickup="wep_skinset64_mat.Wep_3P_Jungle_G36C_Pickup_MIC")) + +//G36C Sahara + Skins.Add((Id=9479, Weapondef=class'KFWeapDef_G36C', MIC_1P=("wep_skinset64_mat.Wep_1P_Sahara_G36C_MIC","wep_skinset64_mat.Wep_1P_Sahara_Scope_G36C_MIC"), MIC_3P="wep_skinset64_mat.Wep_3P_Sahara_G36C_MIC", MIC_Pickup="wep_skinset64_mat.Wep_3P_Sahara_G36C_Pickup_MIC")) + //BeyondHorizon AA12 Skins.Add((Id=8845, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet43_MAT.space_aa12.Space_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet43_MAT.space_aa12.Space_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet43_MAT.space_aa12.Space_AA12_3P_Pickup_MIC")) @@ -3992,6 +4028,7 @@ defaultproperties //Chameleon Dynamic RGB RPG-7 Skins.Add((Id=9358, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("wep_skinset58_mat.chameleonrgb_rpg7.ChameleonRGB_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet58_MAT.chameleonrgb_rpg7.ChameleonRGB_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet58_MAT.chameleonrgb_rpg7.ChameleonRGB_RPG7_3P_Pickup_MIC")) + //Deep Sea Antique AA12 Skins.Add((Id=9298, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet59_MAT.deepsea_aa12.DeepSea_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_aa12.DeepSea_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_aa12.DeepSea_AA12_3P_Pickup_MIC")) @@ -4099,4 +4136,184 @@ defaultproperties //Deep Sea Precious RPG-7 Skins.Add((Id=9333, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_RPG7', MIC_1P=("WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaPrecious_RPG7_1P_Mint_MIC"), MIC_3P="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaPrecious_RPG7_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet59_MAT.deepsea_rpg7.DeepSeaPrecious_RPG7_3P_Pickup_MIC")) + +//Plague Doctor Mint Tommy Gun + Skins.Add((Id=9388, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Thompson', MIC_1P=("WEP_SkinSet62_MAT.plague_tommygun.Plague_TommyGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_tommygun.Plague_TommyGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_tommygun.Plague_TommyGun_3P_Pickup_MIC")) + +//Plague Doctor Sterling Tommy Gun + Skins.Add((Id=9389, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Thompson', MIC_1P=("WEP_SkinSet62_MAT.plague_tommygun.PlagueSterling_TommyGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_tommygun.PlagueSterling_TommyGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_tommygun.PlagueSterling_TommyGun_3P_Pickup_MIC")) + +//Plague Doctor Obsidian Tommy Gun + Skins.Add((Id=9390, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Thompson', MIC_1P=("WEP_SkinSet62_MAT.plague_tommygun.PlagueObsidian_TommyGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_tommygun.PlagueObsidian_TommyGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_tommygun.PlagueObsidian_TommyGun_3P_Pickup_MIC")) + +//Plague Doctor Volcanic Tommy Gun + Skins.Add((Id=9391, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Thompson', MIC_1P=("WEP_SkinSet62_MAT.plague_tommygun.PlagueVolcanic_TommyGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_tommygun.PlagueVolcanic_TommyGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_tommygun.PlagueVolcanic_TommyGun_3P_Pickup_MIC")) + +//Plague Doctor Emerald Tommy Gun + Skins.Add((Id=9392, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Thompson', MIC_1P=("WEP_SkinSet62_MAT.plague_tommygun.PlagueEmerald_TommyGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_tommygun.PlagueEmerald_TommyGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_tommygun.PlagueEmerald_TommyGun_3P_Pickup_MIC")) + +//Plague Doctor Precious Tommy Gun + Skins.Add((Id=9393, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Thompson', MIC_1P=("WEP_SkinSet62_MAT.plague_tommygun.PlaguePrecious_TommyGun_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_tommygun.PlaguePrecious_TommyGun_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_tommygun.PlaguePrecious_TommyGun_3P_Pickup_MIC")) + +//Plague Doctor Mint M79 + Skins.Add((Id=9394, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet62_MAT.plague_m79.Plague_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_m79.Plague_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_m79.Plague_M79_3P_Pickup_MIC")) + +//Plague Doctor Sterling M79 + Skins.Add((Id=9395, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet62_MAT.plague_m79.PlagueSterling_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_m79.PlagueSterling_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_m79.PlagueSterling_M79_3P_Pickup_MIC")) + +//Plague Doctor Obsidian M79 + Skins.Add((Id=9396, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet62_MAT.plague_m79.PlagueObsidian_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_m79.PlagueObsidian_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_m79.PlagueObsidian_M79_3P_Pickup_MIC")) + +//Plague Doctor Volcanic M79 + Skins.Add((Id=9397, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet62_MAT.plague_m79.PlagueVolcanic_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_m79.PlagueVolcanic_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_m79.PlagueVolcanic_M79_3P_Pickup_MIC")) + +//Plague Doctor Emerald M79 + Skins.Add((Id=9398, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet62_MAT.plague_m79.PlagueEmerald_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_m79.PlagueEmerald_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_m79.PlagueEmerald_M79_3P_Pickup_MIC")) + +//Plague Doctor Precious M79 + Skins.Add((Id=9399, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M79', MIC_1P=("WEP_SkinSet62_MAT.plague_m79.PlaguePrecious_M79_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_m79.PlaguePrecious_M79_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_m79.PlaguePrecious_M79_3P_Pickup_MIC")) + +//Plague Doctor Mint Desert Eagle + Skins.Add((Id=9400, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet62_MAT.plague_deagle.Plague_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_deagle.Plague_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_deagle.Plague_Deagle_3P_Pickup_MIC")) + +//Plague Doctor Sterling Desert Eagle + Skins.Add((Id=9401, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet62_MAT.plague_deagle.PlagueSterling_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_deagle.PlagueSterling_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_deagle.PlagueSterling_Deagle_3P_Pickup_MIC")) + +//Plague Doctor Obsidian Desert Eagle + Skins.Add((Id=9402, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet62_MAT.plague_deagle.PlagueObsidian_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_deagle.PlagueObsidian_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_deagle.PlagueObsidian_Deagle_3P_Pickup_MIC")) + +//Plague Doctor Volcanic Desert Eagle + Skins.Add((Id=9403, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet62_MAT.plague_deagle.PlagueVolcanic_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_deagle.PlagueVolcanic_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_deagle.PlagueVolcanic_Deagle_3P_Pickup_MIC")) + +//Plague Doctor Emerald Desert Eagle + Skins.Add((Id=9404, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet62_MAT.plague_deagle.PlagueEmerald_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_deagle.PlagueEmerald_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_deagle.PlagueEmerald_Deagle_3P_Pickup_MIC")) + +//Plague Doctor Precious Desert Eagle + Skins.Add((Id=9405, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet62_MAT.plague_deagle.PlaguePrecious_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_deagle.PlaguePrecious_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_deagle.PlaguePrecious_Deagle_3P_Pickup_MIC")) + +//Plague Doctor Mint Crossbow + Skins.Add((Id=9406, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Crossbow', MIC_1P=("WEP_SkinSet62_MAT.plague_crossbow.Plague_Crossbow_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_crossbow.Plague_Crossbow_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_crossbow.Plague_Crossbow_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_crossbow.Plague_Crossbow_3P_Pickup_MIC")) + +//Plague Doctor Sterling Crossbow + Skins.Add((Id=9407, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Crossbow', MIC_1P=("WEP_SkinSet62_MAT.plague_crossbow.PlagueSterling_Crossbow_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_crossbow.PlagueSterling_Crossbow_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_crossbow.PlagueSterling_Crossbow_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_crossbow.PlagueSterling_Crossbow_3P_Pickup_MIC")) + +//Plague Doctor Obsidian Crossbow + Skins.Add((Id=9408, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Crossbow', MIC_1P=("WEP_SkinSet62_MAT.plague_crossbow.PlagueObsidian_Crossbow_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_crossbow.PlagueObsidian_Crossbow_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_crossbow.PlagueObsidian_Crossbow_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_crossbow.PlagueObsidian_Crossbow_3P_Pickup_MIC")) + +//Plague Doctor Volcanic Crossbow + Skins.Add((Id=9409, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Crossbow', MIC_1P=("WEP_SkinSet62_MAT.plague_crossbow.PlagueVolcanic_Crossbow_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_crossbow.PlagueVolcanic_Crossbow_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_crossbow.PlagueVolcanic_Crossbow_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_crossbow.PlagueVolcanic_Crossbow_3P_Pickup_MIC")) + +//Plague Doctor Emerald Crossbow + Skins.Add((Id=9410, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Crossbow', MIC_1P=("WEP_SkinSet62_MAT.plague_crossbow.PlagueEmerald_Crossbow_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_crossbow.PlagueEmerald_Crossbow_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_crossbow.PlagueEmerald_Crossbow_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_crossbow.PlagueEmerald_Crossbow_3P_Pickup_MIC")) + +//Plague Doctor Precious Crossbow + Skins.Add((Id=9411, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Crossbow', MIC_1P=("WEP_SkinSet62_MAT.plague_crossbow.PlaguePrecious_Crossbow_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_crossbow.PlaguePrecious_Crossbow_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_crossbow.PlaguePrecious_Crossbow_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_crossbow.PlaguePrecious_Crossbow_3P_Pickup_MIC")) + +//Plague Doctor Mint Doomstick + Skins.Add((Id=9412, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet62_MAT.plague_quadbarrel.Plague_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_quadbarrel.Plague_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_quadbarrel.Plague_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_quadbarrel.Plague_QuadBarrel_3P_Pickup_MIC")) + +//Plague Doctor Sterling Doomstick + Skins.Add((Id=9413, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet62_MAT.plague_quadbarrel.PlagueSterling_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_quadbarrel.PlagueSterling_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_quadbarrel.PlagueSterling_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_quadbarrel.PlagueSterling_QuadBarrel_3P_Pickup_MIC")) + +//Plague Doctor Obsidian Doomstick + Skins.Add((Id=9414, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet62_MAT.plague_quadbarrel.PlagueObsidian_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_quadbarrel.PlagueObsidian_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_quadbarrel.PlagueObsidian_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_quadbarrel.PlagueObsidian_QuadBarrel_3P_Pickup_MIC")) + +//Plague Doctor Volcanic Doomstick + Skins.Add((Id=9415, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet62_MAT.plague_quadbarrel.PlagueVolcanic_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_quadbarrel.PlagueVolcanic_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_quadbarrel.PlagueVolcanic_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_quadbarrel.PlagueVolcanic_QuadBarrel_3P_Pickup_MIC")) + +//Plague Doctor Emerald Doomstick + Skins.Add((Id=9416, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet62_MAT.plague_quadbarrel.PlagueEmerald_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_quadbarrel.PlagueEmerald_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_quadbarrel.PlagueEmerald_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_quadbarrel.PlagueEmerald_QuadBarrel_3P_Pickup_MIC")) + +//Plague Doctor Precious Doomstick + Skins.Add((Id=9417, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_ElephantGun', MIC_1P=("WEP_SkinSet62_MAT.plague_quadbarrel.PlaguePrecious_QuadBarrel_Main_1P_Mint_MIC", "WEP_SkinSet62_MAT.plague_quadbarrel.PlaguePrecious_QuadBarrel_Barrel_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_quadbarrel.PlaguePrecious_QuadBarrel_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_quadbarrel.PlaguePrecious_QuadBarrel_3P_Pickup_MIC")) + +//Plague Doctor Mint Dragonsbreath + Skins.Add((Id=9418, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet62_MAT.plague_dragonsbreath.Plague_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_dragonsbreath.Plague_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_dragonsbreath.Plague_Dragonsbreath_3P_Pickup_MIC")) + +//Plague Doctor Sterling Dragonsbreath + Skins.Add((Id=9419, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueSterling_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueSterling_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueSterling_Dragonsbreath_3P_Pickup_MIC")) + +//Plague Doctor Obsidian Dragonsbreath + Skins.Add((Id=9420, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueObsidian_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueObsidian_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueObsidian_Dragonsbreath_3P_Pickup_MIC")) + +//Plague Doctor Volcanic Dragonsbreath + Skins.Add((Id=9421, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueVolcanic_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueVolcanic_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueVolcanic_Dragonsbreath_3P_Pickup_MIC")) + +//Plague Doctor Emerald Dragonsbreath + Skins.Add((Id=9422, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueEmerald_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueEmerald_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_dragonsbreath.PlagueEmerald_Dragonsbreath_3P_Pickup_MIC")) + +//Plague Doctor Precious Dragonsbreath + Skins.Add((Id=9423, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_Dragonsbreath', MIC_1P=("WEP_SkinSet62_MAT.plague_dragonsbreath.PlaguePrecious_Dragonsbreath_1P_Mint_MIC"), MIC_3P="WEP_SkinSet62_MAT.plague_dragonsbreath.PlaguePrecious_Dragonsbreath_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet62_MAT.plague_dragonsbreath.PlaguePrecious_Dragonsbreath_3P_Pickup_MIC")) + +//Chameleon Dynamic AA12 + Skins.Add((Id=9441, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet60_MAT.chameleon_aa12.Chameleon_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet60_MAT.chameleon_aa12.Chameleon_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet60_MAT.chameleon_aa12.Chameleon_AA12_3P_Pickup_MIC")) + +//Chameleon Dynamic Flamethrower + Skins.Add((Id=9442, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_FlameThrower', MIC_1P=("WEP_SkinSet60_MAT.chameleon_flamethrower.Chameleon_Flamethrower_1P_Mint_MIC"), MIC_3P="WEP_SkinSet60_MAT.chameleon_flamethrower.Chameleon_Flamethrower_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet60_MAT.chameleon_flamethrower.Chameleon_Flamethrower_3P_Pickup_MIC")) + +//Chameleon Dynamic M99 + Skins.Add((Id=9443, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M99', MIC_1P=("WEP_SkinSet60_MAT.chameleon_m99.Chameleon_M99_1P_Mint_MIC", "WEP_SkinSet60_MAT.chameleon_m99.Chameleon_M99_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet60_MAT.chameleon_m99.Chameleon_M99_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet60_MAT.chameleon_m99.Chameleon_M99_3P_Pickup_MIC")) + +//Chameleon Dynamic SCAR + Skins.Add((Id=9444, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet60_MAT.chameleon_scar.Chameleon_SCAR_1P_Mint_MIC", "WEP_SkinSet60_MAT.chameleon_scar.Chameleon_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet60_MAT.chameleon_scar.Chameleon_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet60_MAT.chameleon_scar.Chameleon_SCAR_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB AA12 + Skins.Add((Id=9445, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet60_MAT.chameleonrgb_aa12.ChameleonRGB_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet60_MAT.chameleonrgb_aa12.ChameleonRGB_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet60_MAT.chameleonrgb_aa12.ChameleonRGB_AA12_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB Flamethrower + Skins.Add((Id=9446, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_FlameThrower', MIC_1P=("WEP_SkinSet60_MAT.chameleonrgb_flamethrower.ChameleonRGB_Flamethrower_1P_Mint_MIC"), MIC_3P="WEP_SkinSet60_MAT.chameleonrgb_flamethrower.ChameleonRGB_Flamethrower_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet60_MAT.chameleonrgb_flamethrower.ChameleonRGB_Flamethrower_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB M99 + Skins.Add((Id=9447, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M99', MIC_1P=("WEP_SkinSet60_MAT.chameleonrgb_m99.ChameleonRGB_M99_1P_Mint_MIC", "WEP_SkinSet60_MAT.chameleonrgb_m99.ChameleonRGB_M99_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet60_MAT.chameleonrgb_m99.ChameleonRGB_M99_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet60_MAT.chameleonrgb_m99.ChameleonRGB_M99_3P_Pickup_MIC")) + +//Chameleon Dynamic RGB SCAR + Skins.Add((Id=9448, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_SCAR', MIC_1P=("WEP_SkinSet60_MAT.chameleonrgb_scar.ChameleonRGB_SCAR_1P_Mint_MIC", "WEP_SkinSet60_MAT.chameleonrgb_scar.ChameleonRGB_SCAR_Scope_1P_Mint_MIC"), MIC_3P="WEP_SkinSet60_MAT.chameleonrgb_scar.ChameleonRGB_SCAR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet60_MAT.chameleonrgb_scar.ChameleonRGB_SCAR_3P_Pickup_MIC")) + +//Classic Mint AK12 + Skins.Add((Id=9433, Weapondef=class'KFWeapDef_Ak12', MIC_1P=("WEP_SkinSet61_MAT.classic_ak12.Classic_AK12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet61_MAT.classic_ak12.Classic_AK12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet61_MAT.classic_ak12.Classic_AK12_3P_Pickup_MIC")) + +//Classic Mint Desert Eagle + Skins.Add((Id=9434, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet61_MAT.classic_deagle.Classic_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet61_MAT.classic_deagle.Classic_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet61_MAT.classic_deagle.Classic_Deagle_3P_Pickup_MIC")) + +//Classic Mint M14EBR + Skins.Add((Id=9435, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet61_MAT.classic_m14ebr.Classic_M14EBR_1P_Mint_MIC"), MIC_3P="WEP_SkinSet61_MAT.classic_m14ebr.Classic_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet61_MAT.classic_m14ebr.Classic_M14EBR_3P_Pickup_MIC")) + +//Classic Mint Kriss + Skins.Add((Id=9436, Weapondef=class'KFWeapDef_Kriss', MIC_1P=("WEP_SkinSet61_MAT.classic_kriss.Classic_Kriss_1P_Mint_MIC"), MIC_3P="WEP_SkinSet61_MAT.classic_kriss.Classic_Kriss_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet61_MAT.classic_kriss.Classic_Kriss_3P_Pickup_MIC")) + +//Classic Precious AK12 + Skins.Add((Id=9437, Weapondef=class'KFWeapDef_Ak12', MIC_1P=("WEP_SkinSet61_MAT.standard_ak12.Standard_AK12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet61_MAT.standard_ak12.Standard_AK12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet61_MAT.standard_ak12.Standard_AK12_3P_Pickup_MIC")) + +//Classic Precious Desert Eagle + Skins.Add((Id=9438, Weapondef=class'KFWeapDef_Deagle', MIC_1P=("WEP_SkinSet61_MAT.standard_deagle.Standard_Deagle_1P_Mint_MIC"), MIC_3P="WEP_SkinSet61_MAT.standard_deagle.Standard_Deagle_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet61_MAT.standard_deagle.Standard_Deagle_3P_Pickup_MIC")) + +//Classic Precious M14EBR + Skins.Add((Id=9439, Weapondef=class'KFWeapDef_M14EBR', MIC_1P=("WEP_SkinSet61_MAT.standard_m14ebr.Standard_M14EBR_1P_Mint_MIC"), MIC_3P="WEP_SkinSet61_MAT.standard_m14ebr.Standard_M14EBR_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet61_MAT.standard_m14ebr.Standard_M14EBR_3P_Pickup_MIC")) + +//Classic Precious Kriss + Skins.Add((Id=9440, Weapondef=class'KFWeapDef_Kriss', MIC_1P=("WEP_SkinSet61_MAT.standard_kriss.Standard_Kriss_1P_Mint_MIC"), MIC_3P="WEP_SkinSet61_MAT.standard_kriss.Standard_Kriss_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet61_MAT.standard_kriss.Standard_Kriss_3P_Pickup_MIC")) + +//Xeno Dynamic M4 + Skins.Add((Id=9425, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M4', MIC_1P=("wep_skinset63_mat.xeno_m4.Xeno_M4_1P_Mint_MIC"), MIC_3P="WEP_SkinSet63_MAT.xeno_m4.Xeno_M4_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet63_MAT.xeno_m4.Xeno_M4_3P_Pickup_MIC")) + +//Xeno Dynamic Heckler & Koch UMP + Skins.Add((Id=9426, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet63_MAT.xeno_hk_ump.Xeno_HK_UMP_1P_Mint_MIC", "WEP_SkinSet63_MAT.xeno_hk_ump.Xeno_HK_UMP_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet63_MAT.xeno_hk_ump.Xeno_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet63_MAT.xeno_hk_ump.Xeno_HK_UMP_3P_Pickup_MIC")) + +//Xeno Dynamic Centerfire + Skins.Add((Id=9427, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_CenterfireMB464', MIC_1P=("WEP_SkinSet63_MAT.xeno_centerfire.Xeno_Centerfire_1P_Mint_MIC"), MIC_3P="WEP_SkinSet63_MAT.xeno_centerfire.Xeno_Centerfire_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet63_MAT.xeno_centerfire.Xeno_Centerfire_3P_Pickup_MIC")) + +//Xeno Dynamic Hemoclobber + Skins.Add((Id=9428, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet63_MAT.xeno_medicbat.Xeno_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet63_MAT.xeno_medicbat.Xeno_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet63_MAT.xeno_medicbat.Xeno_MedicBat_3P_Pickup_MIC")) + +//Xeno Dynamic RGB M4 + Skins.Add((Id=9429, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_M4', MIC_1P=("WEP_SkinSet63_MAT.xenorgb_m4.XenoRGB_M4_1P_Mint_MIC"), MIC_3P="WEP_SkinSet63_MAT.xenorgb_m4.XenoRGB_M4_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet63_MAT.xenorgb_m4.XenoRGB_M4_3P_Pickup_MIC")) + +//Xeno Dynamic RGB Heckler & Koch UMP + Skins.Add((Id=9430, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_HK_UMP', MIC_1P=("WEP_SkinSet63_MAT.xenorgb_hk_ump.XenoRGB_HK_UMP_1P_Mint_MIC", "WEP_SkinSet63_MAT.xenorgb_hk_ump.XenoRGB_HK_UMP_Sight_1P_Mint_MIC"), MIC_3P="WEP_SkinSet63_MAT.xenorgb_hk_ump.XenoRGB_HK_UMP_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet63_MAT.xenorgb_hk_ump.XenoRGB_HK_UMP_3P_Pickup_MIC")) + +//Xeno Dynamic RGB Centerfire + Skins.Add((Id=9431, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_CenterfireMB464', MIC_1P=("WEP_SkinSet63_MAT.xenorgb_centerfire.XenoRGB_Centerfire_1P_Mint_MIC"), MIC_3P="WEP_SkinSet63_MAT.xenorgb_centerfire.XenoRGB_Centerfire_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet63_MAT.xenorgb_centerfire.XenoRGB_Centerfire_3P_Pickup_MIC")) + +//Xeno Dynamic RGB Hemoclobber + Skins.Add((Id=9432, bNeedsCodeUpdates = true, Weapondef=class'KFWeapDef_MedicBat', MIC_1P=("WEP_SkinSet63_MAT.xenorgb_medicbat.XenoRGB_MedicBat_1P_Mint_MIC"), MIC_3P="WEP_SkinSet63_MAT.xenorgb_medicbat.XenoRGB_MedicBat_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet63_MAT.xenorgb_medicbat.XenoRGB_MedicBat_3P_Pickup_MIC")) } \ No newline at end of file diff --git a/KFGame/Classes/KFWeeklyOutbreakInformation.uc b/KFGame/Classes/KFWeeklyOutbreakInformation.uc index a8427bc..60185cd 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 = 17; + static const int NumWeeklyEvents = 18; } DefaultProperties { diff --git a/KFGame/Classes/KFZedArmorInfo.uc b/KFGame/Classes/KFZedArmorInfo.uc index f74a182..fbb2040 100644 --- a/KFGame/Classes/KFZedArmorInfo.uc +++ b/KFGame/Classes/KFZedArmorInfo.uc @@ -242,6 +242,9 @@ function AdjustBoneDamage(out int InDamage, name BoneName, Vector DamagerSource, { local int ArmorZoneIdx, ModDmgMax, ModDmgRem, ObliterateDamage; local float Modifier; + local class mykfDamageType; + + mykfDamageType = class(DamageType); // modify damage done and apply to armor Modifier = GetArmorDamageTypeModifier(DamageType); @@ -259,7 +262,22 @@ function AdjustBoneDamage(out int InDamage, name BoneName, Vector DamagerSource, } else { + // If we have armour penetration damage, we calculate the Damage % that is base (and applied to armor) + // And we calculate the AP one, that's applied as minimum always + if (mykfDamageType != none && mykfDamageType.default.DamageModifierAP > 0.f) + { + ModDmgRem = (1.0 - mykfDamageType.default.DamageModifierAP) * ModDmgRem; + } + TakeArmorZoneDamage(ArmorZoneIdx, ModDmgRem, ModDmgRem); + + // Apply to Zed: % AP (unmodified InDamage) + what's left of base after armor applied (unmodified ModDmgRem) + if (mykfDamageType != none && mykfDamageType.default.DamageModifierAP > 0.f) + { + InDamage = InDamage * mykfDamageType.default.DamageModifierAP; + InDamage += float(ModDmgRem) / Modifier; + return; + } } } diff --git a/KFGame/KFOnlineStats.uci b/KFGame/KFOnlineStats.uci index fdf2ff3..20d17fe 100644 --- a/KFGame/KFOnlineStats.uci +++ b/KFGame/KFOnlineStats.uci @@ -157,4 +157,5 @@ const STATID_ACHIEVE_MoonbaseCollectibles = 4059; const STATID_ACHIEVE_NetherholdCollectibles = 4060; const STATID_ACHIEVE_CarillonHamletCollectibles = 4061; const STATID_ACHIEVE_RigCollectibles = 4062; +const STATID_ACHIEVE_BarmwichCollectibles = 4063; /** `endif */ diff --git a/KFGame/KFProfileSettings.uci b/KFGame/KFProfileSettings.uci index f7410ff..6a9c423 100644 --- a/KFGame/KFProfileSettings.uci +++ b/KFGame/KFProfileSettings.uci @@ -76,3 +76,7 @@ const KFID_HasTabbedToStore = 177; const KFID_AllowSwapTo9mm = 178; // Halloween 2021 QoL: added option to quick switch weapons to 9mm const KFID_SurvivalStartingWeapIdx=179; // Summer 2022 QoL: added option to choose starting weapon for survival perk const KFID_SurvivalStartingGrenIdx=180; // Summer 2022 QoL: added option to choose starting grenade for survival perk +const KFID_MouseLookUpScale=181; // Halloweeen 2022 QoL: added mouse options to menu +const KFID_MouseLookRightScale=182; // Halloweeen 2022 QoL: added mouse options to menu +const KFID_ViewSmoothingEnabled=183; // Halloweeen 2022 QoL: added mouse options to menu +const KFID_ViewAccelerationEnabled=184; // Halloweeen 2022 QoL: added mouse options to menu \ No newline at end of file diff --git a/KFGameContent/Classes/KFBarmwichBonfireVolume.uc b/KFGameContent/Classes/KFBarmwichBonfireVolume.uc new file mode 100644 index 0000000..36531f0 --- /dev/null +++ b/KFGameContent/Classes/KFBarmwichBonfireVolume.uc @@ -0,0 +1,77 @@ +//============================================================================= +// KFBarmwichBonfireVolume +//============================================================================= +// Barmwich volume used for bonfires. Triggers seasonal progression. +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFBarmwichBonfireVolume extends KFPhysicsDamageByPawnVolume + placeable; + +/** Objective index for the event this is tied to */ +var() int ObjectiveIndex; + +/** Index of the event this is tied to */ +var() int EventIndex; + +var array AffectedActors; + +var transient bool bIsDataValid; + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + bIsDataValid = IsObjectiveDataValid(); +} + +function CausePainTo(Actor Other) +{ + Super.CausePainTo(Other); + + if (bIsDataValid && KFPawn_Monster(Other) != none && KFPawn_Monster(Other).IsAliveAndWell()) + { + if (AffectedActors.Find(Other) == INDEX_NONE) + { + AffectedActors.AddItem(Other); + NotifyProgression(); + } + } +} + +function NotifyProgression() +{ + local KFPlayerController KFPC; + + if (!bIsDataValid) + { + return; + } + + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + KFPC.ClientOnTryCompleteObjective(ObjectiveIndex, EventIndex); + } +} + +function ClearAllActors() +{ + AffectedActors.Remove(0, AffectedActors.Length); +} + +function ClearActor(Actor Other) +{ + AffectedActors.RemoveItem(Other); +} + +simulated function bool IsObjectiveDataValid() +{ + return ObjectiveIndex >= 0 && ObjectiveIndex < 5 && EventIndex > SEI_None && EventIndex < SEI_MAX; +} + +DefaultProperties +{ + bIsDataValid = false; +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Ballistic_BlastBrawlersShotgun.uc b/KFGameContent/Classes/KFDT_Ballistic_BlastBrawlersShotgun.uc index db71db0..bd15df2 100644 --- a/KFGameContent/Classes/KFDT_Ballistic_BlastBrawlersShotgun.uc +++ b/KFGameContent/Classes/KFDT_Ballistic_BlastBrawlersShotgun.uc @@ -43,7 +43,7 @@ defaultproperties //KDeathUpKick=120 //KDeathVel=10 - StumblePower=12 + StumblePower=48 //12 GunHitPower=12 OverrideImpactEffect=ParticleSystem'WEP_HRG_BlastBrawlers_EMIT.FX_BlastBrawlers_Impact' diff --git a/KFGameContent/Classes/KFDT_Ballistic_G36C.uc b/KFGameContent/Classes/KFDT_Ballistic_G36C.uc new file mode 100644 index 0000000..d03a350 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_G36C.uc @@ -0,0 +1,87 @@ +//============================================================================= +// KFDT_Ballistic_G36C +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= + +class KFDT_Ballistic_G36C extends KFDT_Ballistic_AssaultRifle + abstract + hidedropdown; + +/** G36C has still to play a metal effect if impacting metal. (and we demoted to impact on Flesh) */ +var ParticleSystem MetalImpactEffect; +var AkEvent MetalImpactSound; + +static function PlayImpactHitEffects( KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, optional Pawn HitInstigator ) +{ + local KFSkinTypeEffects SkinType, OriginalSkinType, FleshSkinType; + local int i; + + if ( P.CharacterArch != None && default.EffectGroup < FXG_Max ) + { + // Search if affected target has Flesh skin type + for (i = 0; i < P.CharacterArch.ImpactSkins.Length ; ++i) + { + if (P.CharacterArch.ImpactSkins[i].Name == 'Flesh' + || P.CharacterArch.ImpactSkins[i].Name == 'Tough_Flesh') + { + FleshSkinType = P.CharacterArch.ImpactSkins[i]; + break; + } + } + + SkinType = P.GetHitZoneSkinTypeEffects( HitZoneIndex ); + OriginalSkinType = SkinType; + + // If we don't hit flesh or shield, try to demote to Flesh + if (SkinType != none && SkinType.Name != 'Flesh' && SkinType.Name != 'Tough_Flesh' && SkinType.Name != 'ShieldEffects') + { + // We default to none as we don't want bullet to ricochet if any + SkinType = none; + + // Demote to flesh skin hit + if (FleshSkinType != none) + { + SkinType = FleshSkinType; + } + } + + // If we hit metal we have to make sure we play it's Metal impact effect (this effect doesn't contain bullet ricochet) (don't play sound though!) + if (OriginalSkinType != none && (OriginalSkinType.Name == 'Metal' || OriginalSkinType.Name == 'Machine')) + { + OriginalSkinType.PlayImpactParticleEffect(P, HitLocation, HitDirection, HitZoneIndex, default.EffectGroup, default.MetalImpactEffect); + OriginalSkinType.PlayTakeHitSound(P, HitLocation, HitInstigator, default.EffectGroup, default.MetalImpactSound); + } + + if (SkinType != none) + { + SkinType.PlayImpactParticleEffect(P, HitLocation, HitDirection, HitZoneIndex, default.EffectGroup); + SkinType.PlayTakeHitSound(P, HitLocation, HitInstigator, default.EffectGroup); + } + } +} + +defaultproperties +{ + KDamageImpulse=900 + KDeathUpKick=-300 + KDeathVel=100 + + DamageModifierAP=0.8f + ArmorDamageModifier=15.0f + + StumblePower=15 + GunHitPower=15 + + WeaponDef=class'KFWeapDef_G36C' + + //Perk + ModifierPerkList(0)=class'KFPerk_Swat' + + MetalImpactEffect=ParticleSystem'FX_Impacts_EMIT.FX_Wep_Impact_MetalArmor_E' + MetalImpactSound=AkEvent'WW_Skin_Impacts.Play_Slashing_Metal_3P' +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_Dragonbreath.uc new file mode 100644 index 0000000..1f7ce9a --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_Dragonbreath.uc @@ -0,0 +1,82 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Dragonbreath +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Ballistic_HRG_Dragonbreath extends KFDT_Ballistic_Shotgun + abstract + hidedropdown; + +// Damage type to use for the burning damage over time +var class BurnDamageType; + +/** Allows the damage type to customize exactly which hit zones it can dismember */ +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ + if( super.CanDismemberHitZone( InHitZoneName ) ) + { + return true; + } + + switch ( InHitZoneName ) + { + case 'lupperarm': + case 'rupperarm': + case 'chest': + case 'heart': + return true; + } + + return false; +} + +/** Play damage type specific impact effects when taking damage */ +static function PlayImpactHitEffects( KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, optional Pawn HitInstigator ) +{ + // Play burn effect when dead + if( P.bPlayedDeath && P.WorldInfo.TimeSeconds > P.TimeOfDeath ) + { + default.BurnDamageType.static.PlayImpactHitEffects(P, HitLocation, HitDirection, HitZoneIndex, HitInstigator); + return; + } + + super.PlayImpactHitEffects(P, HitLocation, HitDirection, HitZoneIndex, HitInstigator); +} + +/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ +static function ApplySecondaryDamage( KFPawn Victim, int DamageTaken, optional Controller InstigatedBy ) +{ + // Overriden to specific a different damage type to do the burn damage over + // time. We do this so we don't get shotgun pellet impact sounds/fx during + // the DOT burning. + if ( default.BurnDamageType.default.DoT_Type != DOT_None ) + { + Victim.ApplyDamageOverTime(DamageTaken, InstigatedBy, default.BurnDamageType); + } +} + +defaultproperties +{ + GoreDamageGroup=DGT_Shotgun + BloodSpread=0.4 + BloodScale=0.6 + + KDamageImpulse=3500 + KDeathUpKick=800 + KDeathVel=650 + GibImpulseScale=1.0 + + BurnPower=40 + StumblePower=40 + GunHitPower=50 + + WeaponDef=class'KFWeapDef_HRG_Dragonbreath' + + BurnDamageType=class'KFDT_Fire_HRG_DragonsBreathDoT' + + EffectGroup=FXG_IncendiaryRound + + ModifierPerkList(0)=class'KFPerk_Firebug' +} diff --git a/KFGameContent/Classes/KFDT_Ballistic_HRG_Locust.uc b/KFGameContent/Classes/KFDT_Ballistic_HRG_Locust.uc new file mode 100644 index 0000000..53696e1 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Ballistic_HRG_Locust.uc @@ -0,0 +1,18 @@ +//============================================================================= +// KFDT_Ballistic_HRG_Locust +//============================================================================= +// HRG Locust bullet impact +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Ballistic_HRG_Locust extends KFDamageType + abstract + hidedropdown; + +defaultproperties +{ + ModifierPerkList(0)=class'KFPerk_Survivalist' + + WeaponDef=class'KFWeapDef_HRG_Locust' +} diff --git a/KFGameContent/Classes/KFDT_Bludgeon_G36C.uc b/KFGameContent/Classes/KFDT_Bludgeon_G36C.uc new file mode 100644 index 0000000..bf9c765 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_G36C.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_G36C +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_G36C extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_G36C' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Dragonbreath.uc new file mode 100644 index 0000000..39d9dda --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Dragonbreath.uc @@ -0,0 +1,16 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_Dragonbreath +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2017 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Bludgeon_HRG_Dragonbreath extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_Dragonbreath' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Bludgeon_HRG_Locust.uc b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Locust.uc new file mode 100644 index 0000000..7c38919 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Bludgeon_HRG_Locust.uc @@ -0,0 +1,15 @@ +//============================================================================= +// KFDT_Bludgeon_HRG_Locust +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Bludgeon_HRG_Locust extends KFDT_Bludgeon_RifleButt + abstract + hidedropdown; + +DefaultProperties +{ + //defaults + WeaponDef=class'KFWeapDef_HRG_Locust' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Explosive_HRG_Locust.uc b/KFGameContent/Classes/KFDT_Explosive_HRG_Locust.uc new file mode 100644 index 0000000..30e7609 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Explosive_HRG_Locust.uc @@ -0,0 +1,40 @@ +//============================================================================= +// KFDT_Explosive_HRG_Locust +//============================================================================= +// Explosive damage type for the HRG Locust +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFDT_Explosive_HRG_Locust extends KFDT_Explosive + abstract + hidedropdown; + +// Damage type to use for the damage over time effect +var class DoTDamageType; + +/** Called when damage is dealt to apply additional damage type (e.g. Damage Over Time) */ +static function ApplySecondaryDamage(KFPawn Victim, int DamageTaken, optional Controller InstigatedBy) +{ + if (Victim.Controller == InstigatedBy) + { + return; + } + + if (default.DoTDamageType.default.DoT_Type != DOT_None) + { + Victim.ApplyDamageOverTime(class'KFDT_Toxic_HRG_Locust'.default.SpreadOnTouchDamage, InstigatedBy, default.DoTDamageType); + } +} + +defaultproperties +{ + //Perk + ModifierPerkList(0)=class'KFPerk_Survivalist' + WeaponDef=class'KFWeapDef_HRG_Locust' + + DoTDamageType=class'KFDT_Toxic_HRG_Locust' + + bCausesFracture=false + bCanPlayDeadHitEffects=false +} diff --git a/KFGameContent/Classes/KFDT_Fire_Ground_HRG_DragonBreath.uc b/KFGameContent/Classes/KFDT_Fire_Ground_HRG_DragonBreath.uc new file mode 100644 index 0000000..6d73673 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Fire_Ground_HRG_DragonBreath.uc @@ -0,0 +1,39 @@ +//============================================================================= +// KFDT_Fire_Ground_HRG_DragonBreath +//============================================================================= +// Damage caused by burning from being hit by a HRG dragon breath ground fire +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Fire_Ground_HRG_DragonBreath extends KFDT_Fire_Ground + abstract; + +static function int GetKillerDialogID() +{ + return 86;//KILL_Fire +} + +static function int GetDamagerDialogID() +{ + return 102;//DAMZ_Fire +} + +static function int GetDamageeDialogID() +{ + return 116;//DAMP_Fire +} + +defaultproperties +{ + WeaponDef=class'KFWeapDef_HRG_Dragonbreath' + + DoT_Type=DOT_Fire + DoT_Duration=2.7 + DoT_Interval=0.5 + DoT_DamageScale=0.7 + + BurnPower=10 +} + diff --git a/KFGameContent/Classes/KFDT_Fire_HRG_DragonsBreathDoT.uc b/KFGameContent/Classes/KFDT_Fire_HRG_DragonsBreathDoT.uc new file mode 100644 index 0000000..c9a9d80 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Fire_HRG_DragonsBreathDoT.uc @@ -0,0 +1,41 @@ +//============================================================================= +// KFDT_Fire_HRG_DragonsBreathDoT +//============================================================================= +// Damage caused by burning from being hit by an HRG dragon's breath round +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= + +class KFDT_Fire_HRG_DragonsBreathDoT extends KFDT_Fire + abstract + hidedropdown; + +static function int GetKillerDialogID() +{ + return 86;//KILL_Fire +} + +static function int GetDamagerDialogID() +{ + return 102;//DAMZ_Fire +} + +static function int GetDamageeDialogID() +{ + return 116;//DAMP_Fire +} + +defaultproperties +{ + WeaponDef=class'KFWeapDef_HRG_Dragonbreath' + + DoT_Type=DOT_Fire + DoT_Duration=2.7 //5.0 //1.0 + DoT_Interval=0.5 + DoT_DamageScale=0.7 //1.0 + + BurnPower=10 //1.0 //18.5 +} + diff --git a/KFGameContent/Classes/KFDT_Piercing_ScytheStab.uc b/KFGameContent/Classes/KFDT_Piercing_ScytheStab.uc new file mode 100644 index 0000000..bc546ea --- /dev/null +++ b/KFGameContent/Classes/KFDT_Piercing_ScytheStab.uc @@ -0,0 +1,23 @@ +//============================================================================= +// KFDT_Piercing_ScytheStab +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Piercing_ScytheStab extends KFDT_Piercing + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=200 + KDeathUpKick=250 + + StumblePower=50 + MeleeHitPower=100 + + WeaponDef=class'KFWeapDef_Scythe' + + ModifierPerkList(0)=class'KFPerk_Berserker' +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFDT_Slashing_ScytheLong.uc b/KFGameContent/Classes/KFDT_Slashing_ScytheLong.uc new file mode 100644 index 0000000..8cef9d6 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Slashing_ScytheLong.uc @@ -0,0 +1,140 @@ +//============================================================================= +// KFDT_Slashing_ScytheLong +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Slashing_ScytheLong extends KFDT_Slashing + abstract + hidedropdown; + +/** Allows the damage type to customize exactly which hit zones it can dismember */ +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ + return true; +} + +/** Allows the damage type to map a hit zone to a different bone for dismemberment purposes */ +static simulated function GetBoneToDismember(KFPawn_Monster InPawn, vector HitDirection, name InHitZoneName, out name OutBoneName) +{ + local EPawnOctant SlashDir; + local KFCharacterInfo_Monster MonsterInfo; + + MonsterInfo = InPawn.GetCharacterMonsterInfo(); + if ( MonsterInfo == none ) + { + return; + } + + SlashDir = GetLastSlashDirection(InPawn, HitDirection); + + if( SlashDir == DIR_Forward || SlashDir == DIR_Backward ) + { + if( InHitZoneName == 'chest' || InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Randomly pick the left or right shoulder bone and split the guy in half vertically + OutBoneName = Rand(2) == 0 + ? MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName + : MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + } + else if( SlashDir == DIR_Left || SlashDir == DIR_Right ) + { + if( InHitZoneName == 'chest' || InHitZoneName == 'abdomen' || InHitZoneName == 'stomach' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowHorizontalSplit ) + { + // Split the guy in half horizontally + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.SpineBoneName; + } + } + } + else if( SlashDir == DIR_ForwardLeft || SlashDir == DIR_BackwardRight ) + { + if( InHitZoneName == 'chest' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + else if( InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Use a random chance to decide whether to dismember the head or the shoulder constraints + if( Rand(2) == 0 ) + { + // ... and choose one of the shoulder constraints at random + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + } + } + else if( SlashDir == DIR_ForwardRight || SlashDir == DIR_BackwardLeft ) + { + if( InHitZoneName == 'chest' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName; + } + } + else if( InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Use a random chance to decide whether to dismember the head or the shoulder constraints + if( Rand(2) == 0 ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName; + } + } + } + } +} + +/** Allows the damage type to modify the impulse when a specified hit zone is dismembered */ +static simulated function ModifyDismembermentHitImpulse(KFPawn_Monster InPawn, name InHitZoneName, vector HitDirection, + out vector OutImpulseDir, out vector OutParentImpulseDir, + out float OutImpulseScale, out float OutParentImpulseScale) +{ + local EPawnOctant SlashDir; + + SlashDir = GetLastSlashDirection(InPawn, HitDirection); + + // Apply upward impulse on decapitation from a clean horizontal slash + if( InHitZoneName == 'head' && + ( SlashDir == DIR_Left || SlashDir == DIR_Right ) ) + { + OutImpulseDir += 10*vect(0,0,1); + OutImpulseDir = Normal(OutImpulseDir); + OutParentImpulseScale = 0.f; + } + // Do not apply any impulse on split in half from a vertical slash + else if( (InHitZoneName == 'head' || InHitZoneName == 'chest' ) && + ( SlashDir == DIR_Forward || SlashDir == DIR_Backward) ) + { + OutImpulseScale = 0.f; + OutParentImpulseScale = 0.f; + } +} + +defaultproperties +{ + KDamageImpulse=10000 //1500 + KDeathUpKick=2000 //200 + KDeathVel=3750 //375 + + KnockdownPower=0 + StunPower=0 + StumblePower=100 + MeleeHitPower=100 + + WeaponDef=class'KFWeapDef_Scythe' + ModifierPerkList(0)=class'KFPerk_Berserker' +} diff --git a/KFGameContent/Classes/KFDT_Slashing_ScytheLongAlt.uc b/KFGameContent/Classes/KFDT_Slashing_ScytheLongAlt.uc new file mode 100644 index 0000000..2d6c7dd --- /dev/null +++ b/KFGameContent/Classes/KFDT_Slashing_ScytheLongAlt.uc @@ -0,0 +1,38 @@ +//============================================================================= +// KFDT_Slashing_ScytheLongAlt +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Slashing_ScytheLongAlt extends KFDT_Slashing_ZweihanderHeavy + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=3600 //1600 + KDeathUpKick=400 //200 + KDeathVel=750 //500 + + KnockdownPower=50 + StunPower=0 + StumblePower=200 + MeleeHitPower=100 + + // Obliteration + GoreDamageGroup = DGT_Explosive + RadialDamageImpulse = 8000.f // This controls how much impulse is applied to gibs when exploding + bUseHitLocationForGibImpulses = true // This will make the impulse origin where the victim was hit for directional gibs + bPointImpulseTowardsOrigin = true // This creates an impulse direction aligned along hitlocation and pawn location -- this will push all gibs in the same direction + ImpulseOriginScale = 100.f // Higher means more directional gibbing, lower means more outward (and upward) gibbing + ImpulseOriginLift = 150.f + MaxObliterationGibs = 12 // Maximum number of gibs that can be spawned by obliteration, 0=MAX + bCanGib = true + bCanObliterate = true + ObliterationHealthThreshold = 0 + ObliterationDamageThreshold = 100 + + WeaponDef=class'KFWeapDef_Scythe' + ModifierPerkList(0)=class'KFPerk_Berserker' +} diff --git a/KFGameContent/Classes/KFDT_Slashing_ScytheShort.uc b/KFGameContent/Classes/KFDT_Slashing_ScytheShort.uc new file mode 100644 index 0000000..945af04 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Slashing_ScytheShort.uc @@ -0,0 +1,140 @@ +//============================================================================= +// KFDT_Slashing_ScytheShort +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Slashing_ScytheShort extends KFDT_Slashing + abstract + hidedropdown; + +/** Allows the damage type to customize exactly which hit zones it can dismember */ +static simulated function bool CanDismemberHitZone( name InHitZoneName ) +{ + return true; +} + +/** Allows the damage type to map a hit zone to a different bone for dismemberment purposes */ +static simulated function GetBoneToDismember(KFPawn_Monster InPawn, vector HitDirection, name InHitZoneName, out name OutBoneName) +{ + local EPawnOctant SlashDir; + local KFCharacterInfo_Monster MonsterInfo; + + MonsterInfo = InPawn.GetCharacterMonsterInfo(); + if ( MonsterInfo == none ) + { + return; + } + + SlashDir = GetLastSlashDirection(InPawn, HitDirection); + + if( SlashDir == DIR_Forward || SlashDir == DIR_Backward ) + { + if( InHitZoneName == 'chest' || InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Randomly pick the left or right shoulder bone and split the guy in half vertically + OutBoneName = Rand(2) == 0 + ? MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName + : MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + } + else if( SlashDir == DIR_Left || SlashDir == DIR_Right ) + { + if( InHitZoneName == 'chest' || InHitZoneName == 'abdomen' || InHitZoneName == 'stomach' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowHorizontalSplit ) + { + // Split the guy in half horizontally + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.SpineBoneName; + } + } + } + else if( SlashDir == DIR_ForwardLeft || SlashDir == DIR_BackwardRight ) + { + if( InHitZoneName == 'chest' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + else if( InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Use a random chance to decide whether to dismember the head or the shoulder constraints + if( Rand(2) == 0 ) + { + // ... and choose one of the shoulder constraints at random + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.RightShoulderBoneName; + } + } + } + } + else if( SlashDir == DIR_ForwardRight || SlashDir == DIR_BackwardLeft ) + { + if( InHitZoneName == 'chest' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName; + } + } + else if( InHitZoneName == 'head' ) + { + if( MonsterInfo.SpecialMeleeDismemberment.bAllowVerticalSplit ) + { + // Use a random chance to decide whether to dismember the head or the shoulder constraints + if( Rand(2) == 0 ) + { + OutBoneName = MonsterInfo.SpecialMeleeDismemberment.LeftShoulderBoneName; + } + } + } + } +} + +/** Allows the damage type to modify the impulse when a specified hit zone is dismembered */ +static simulated function ModifyDismembermentHitImpulse(KFPawn_Monster InPawn, name InHitZoneName, vector HitDirection, + out vector OutImpulseDir, out vector OutParentImpulseDir, + out float OutImpulseScale, out float OutParentImpulseScale) +{ + local EPawnOctant SlashDir; + + SlashDir = GetLastSlashDirection(InPawn, HitDirection); + + // Apply upward impulse on decapitation from a clean horizontal slash + if( InHitZoneName == 'head' && + ( SlashDir == DIR_Left || SlashDir == DIR_Right ) ) + { + OutImpulseDir += 10*vect(0,0,1); + OutImpulseDir = Normal(OutImpulseDir); + OutParentImpulseScale = 0.f; + } + // Do not apply any impulse on split in half from a vertical slash + else if( (InHitZoneName == 'head' || InHitZoneName == 'chest' ) && + ( SlashDir == DIR_Forward || SlashDir == DIR_Backward) ) + { + OutImpulseScale = 0.f; + OutParentImpulseScale = 0.f; + } +} + +defaultproperties +{ + KDamageImpulse=10000 //1500 + KDeathUpKick=2000 //200 + KDeathVel=3750 //375 + + KnockdownPower=0 + StunPower=0 + StumblePower=50 + MeleeHitPower=100 + + WeaponDef=class'KFWeapDef_Scythe' + ModifierPerkList(0)=class'KFPerk_Berserker' +} diff --git a/KFGameContent/Classes/KFDT_Slashing_ScytheShortAlt.uc b/KFGameContent/Classes/KFDT_Slashing_ScytheShortAlt.uc new file mode 100644 index 0000000..c067e43 --- /dev/null +++ b/KFGameContent/Classes/KFDT_Slashing_ScytheShortAlt.uc @@ -0,0 +1,38 @@ +//============================================================================= +// KFDT_Slashing_ScytheShortAlt +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFDT_Slashing_ScytheShortAlt extends KFDT_Slashing_ZweihanderHeavy + abstract + hidedropdown; + +defaultproperties +{ + KDamageImpulse=3600 //1600 + KDeathUpKick=400 //200 + KDeathVel=750 //500 + + KnockdownPower=0 + StunPower=0 + StumblePower=150 + MeleeHitPower=100 + + // Obliteration + GoreDamageGroup = DGT_Explosive + RadialDamageImpulse = 8000.f // This controls how much impulse is applied to gibs when exploding + bUseHitLocationForGibImpulses = true // This will make the impulse origin where the victim was hit for directional gibs + bPointImpulseTowardsOrigin = true // This creates an impulse direction aligned along hitlocation and pawn location -- this will push all gibs in the same direction + ImpulseOriginScale = 100.f // Higher means more directional gibbing, lower means more outward (and upward) gibbing + ImpulseOriginLift = 150.f + MaxObliterationGibs = 12 // Maximum number of gibs that can be spawned by obliteration, 0=MAX + bCanGib = true + bCanObliterate = true + ObliterationHealthThreshold = 0 + ObliterationDamageThreshold = 100 + + WeaponDef=class'KFWeapDef_Scythe' + ModifierPerkList(0)=class'KFPerk_Berserker' +} diff --git a/KFGameContent/Classes/KFExplosion_HRG_Dragonbreath_GroundFire.uc b/KFGameContent/Classes/KFExplosion_HRG_Dragonbreath_GroundFire.uc new file mode 100644 index 0000000..a577e7a --- /dev/null +++ b/KFGameContent/Classes/KFExplosion_HRG_Dragonbreath_GroundFire.uc @@ -0,0 +1,33 @@ +//============================================================================= +// KFExplosion_HRG_Dragonbreath_GroundFire +//============================================================================= +// Explosion actor class for ground fire +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFExplosion_HRG_Dragonbreath_GroundFire extends KFExplosionActorLingering; + + +simulated function SpawnExplosionParticleSystem(ParticleSystem Template) +{ + // If the template is none, grab the default + if( !ExplosionTemplate.bAllowPerMaterialFX && Template == none ) + { + Template = KFGameExplosion(ExplosionTemplate).ExplosionEffects.DefaultImpactEffect.ParticleTemplate; + } + + // Use custom pool + WorldInfo.GroundFireEmitterPool.SpawnEmitter(Template, Location, rotator(ExplosionTemplate.HitNormal), None); +} + +DefaultProperties +{ + //Interval=0.25f INTERVAL IS OVERRIDDEN BY ITS PROJECTILE + MaxTime=2.0 + + ExplosionLightPriority=LPP_Low + LoopStartEvent=AkEvent'ww_wep_hrg_megadragonbreath.Play_WEP_HRG_MegaDragonbreath_Flame_LP' + LoopStopEvent=AkEvent'ww_wep_hrg_megadragonbreath.Stop_WEP_HRG_MegaDragonbreath_End_Flame_LP' +} diff --git a/KFGameContent/Classes/KFExplosion_HRG_Locust.uc b/KFGameContent/Classes/KFExplosion_HRG_Locust.uc new file mode 100644 index 0000000..f6d6d45 --- /dev/null +++ b/KFGameContent/Classes/KFExplosion_HRG_Locust.uc @@ -0,0 +1,43 @@ +//============================================================================= +// KFExplosion_HRG_Locust +//============================================================================= +// Explosion actor class for HRG Locust +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFExplosion_HRG_Locust extends KFExplosionActorLingering; + +var private bool bFirstExplosion; + +protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCauseEffects) +{ + local bool bReturnValue; + + // Only does explosion damage one time + if (bCauseDamage && bFirstExplosion == false) + { + bCauseDamage = false; + } + + bReturnValue = super.DoExplosionDamage(bCauseDamage, bCauseEffects); + + bFirstExplosion = false; + + return bReturnValue; +} + +DefaultProperties +{ + Interval=0.5f + MaxTime=3.0 + + bOnlyDamagePawns=true + bDoFullDamage=false + + bFirstExplosion=true + + LoopStartEvent=AkEvent'WW_WEP_HRG_Locust.Play_WEP_HRG_Locust_Insects' + LoopStopEvent=AkEvent'WW_WEP_HRG_Locust.Stop_WEP_HRG_Locust_Insect' +} diff --git a/KFGameContent/Classes/KFExplosion_MedicGrenade.uc b/KFGameContent/Classes/KFExplosion_MedicGrenade.uc index 5a2ffbf..7434f7f 100644 --- a/KFGameContent/Classes/KFExplosion_MedicGrenade.uc +++ b/KFGameContent/Classes/KFExplosion_MedicGrenade.uc @@ -87,6 +87,7 @@ protected simulated function AffectsPawn(Pawn Victim, float DamageScale) if( HumanVictim != none && HumanVictim.GetExposureTo(Location) > 0 ) { OwnerProjectile = KFProj_MedicGrenade(Owner); + if( OwnerProjectile != none ) { bCanRepairArmor = OwnerProjectile.HealedPawns.Find( HumanVictim ) == INDEX_NONE; diff --git a/KFGameContent/Classes/KFExplosion_MineReconstructor.uc b/KFGameContent/Classes/KFExplosion_MineReconstructor.uc index 7a6f138..4fe71a8 100644 --- a/KFGameContent/Classes/KFExplosion_MineReconstructor.uc +++ b/KFGameContent/Classes/KFExplosion_MineReconstructor.uc @@ -180,7 +180,7 @@ protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCau if( bOnlyDamagePawns ) { - return ExplodePawns(); + return ExplodePawns(bCauseDamage); } return super(KFExplosionActor).DoExplosionDamage(bCauseDamage, bCauseEffects); diff --git a/KFGameContent/Classes/KFGFxHUD_PlayerStatusVersus.uc b/KFGameContent/Classes/KFGFxHUD_PlayerStatusVersus.uc index 2571c54..3ee5a9e 100644 --- a/KFGameContent/Classes/KFGFxHUD_PlayerStatusVersus.uc +++ b/KFGameContent/Classes/KFGFxHUD_PlayerStatusVersus.uc @@ -62,7 +62,7 @@ function UpdateHealer(Optional bool bForce) } -function ShowActiveIndicators( array IconPathStrings ) +function ShowActiveIndicators( array ActiveSkills ) { } diff --git a/KFGameContent/Classes/KFGameInfo_Survival.uc b/KFGameContent/Classes/KFGameInfo_Survival.uc index 92f09cd..20bff2d 100644 --- a/KFGameContent/Classes/KFGameInfo_Survival.uc +++ b/KFGameContent/Classes/KFGameInfo_Survival.uc @@ -48,6 +48,8 @@ var protected transient bool bWaveStarted; // When this is true next wave will be last var protected bool bGunGamePlayerOnLastGun; +var transient array BonfireVolumes; + /** Whether this game mode should play music from the get-go (lobby) */ static function bool ShouldPlayMusicAtStart() { @@ -75,6 +77,8 @@ event PostBeginPlay() TimeBetweenWaves = GetTraderTime(); bGunGamePlayerOnLastGun = false; + + UpdateBonfires(); } /** Set up the spawning */ @@ -1144,6 +1148,8 @@ function WaveEnded(EWaveEndCondition WinCondition) return; } + ClearAllActorsFromBonfire(); + if (WorldInfo.NetMode == NM_DedicatedServer) { scripttrace(); @@ -1202,28 +1208,9 @@ function WaveEnded(EWaveEndCondition WinCondition) } } - if (OutbreakEvent != none && OutbreakEvent.ActiveEvent.bGunGameMode) + if (OutbreakEvent != none) { - MyKFGRI.GunGameWavesCurrent += 1; - - // If we unlocked last weapon we only finish if we completed the boss wave - // If we didn't unlock to last weapon and we just finished last wave (before BOSS), repeat - if (bGunGamePlayerOnLastGun) - { - MyKFGRI.bWaveGunGameIsFinal = true; - - if (WaveNum < WaveMax) - { - WaveNum = WaveMax - 1; - } - } - else if (WaveNum >= WaveMax - 1) - { - // Repeat wave before BOSS till forever - WaveNum = WaveMax - 2; - } - - MyKFGRI.bNetDirty = true; + OnOutbreakWaveWon(); } if (WaveNum < WaveMax) @@ -1873,6 +1860,38 @@ function DebugKillZeds() } } +function OnOutbreakWaveWon() {} + +function UpdateBonfires() +{ + local KFBarmwichBonfireVolume BonfireVolume; + + foreach AllActors(class'KFBarmwichBonfireVolume', BonfireVolume) + { + BonfireVolumes.AddItem(BonfireVolume); + } +} + +function ClearAllActorsFromBonfire() +{ + local KFBarmwichBonfireVolume BonfireVolume; + + foreach BonfireVolumes(BonfireVolume) + { + BonfireVolume.ClearAllActors(); + } +} + +function ClearActorFromBonfire(Actor Other) +{ + local KFBarmwichBonfireVolume BonfireVolume; + + foreach BonfireVolumes(BonfireVolume) + { + BonfireVolume.ClearActor(Other); + } +} + DefaultProperties { TimeBetweenWaves=60 //This is going to be a difficulty setting later diff --git a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc index 655c65a..aaccebd 100644 --- a/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc +++ b/KFGameContent/Classes/KFGameInfo_WeeklySurvival.uc @@ -607,7 +607,7 @@ function EndOfMatch(bool bVictory) KFPC.CompletedWeeklySurvival(); } } - + super.EndOfMatch(bVictory); } @@ -988,7 +988,7 @@ function LoadGunGameWeapons(Controller NewPlayer) Weapon = KFWeapon(Inv); if (Weapon != none) { - Weapon.GunGameRemove(); + Weapon.RemoveGun(); } } } @@ -1108,37 +1108,43 @@ function ResetGunGame(KFPlayerController_WeeklySurvival KFPC_WS) function NotifyKilled(Controller Killer, Controller Killed, Pawn KilledPawn, class damageType ) { local KFPawn_Monster KFPM; - local KFPlayerController_WeeklySurvival KFPC_WS; + local KFPlayerController_WeeklySurvival KFPC_WS_Killer, KFPC_WS_Killed; super.NotifyKilled(Killer, Killed, KilledPawn, damageType); - - if (!OutbreakEvent.ActiveEvent.bGunGameMode) - { - return; - } - - // If pawn is monster increase gun game score for that monster - KFPM = KFPawn_Monster(KilledPawn); - KFPC_WS = KFPlayerController_WeeklySurvival(Killer); + KFPM = KFPawn_Monster(KilledPawn); + KFPC_WS_Killer = KFPlayerController_WeeklySurvival(Killer); + KFPC_WS_Killed = KFPlayerController_WeeklySurvival(Killed); - if (KFPM != none && KFPC_WS != none) - { - if (KFPC_WS.Pawn.Health > 0) + if (OutbreakEvent.ActiveEvent.bGunGameMode) + { + // If pawn is monster increase gun game score for that monster + + if (KFPM != none && KFPC_WS_Killer != none) { - KFPC_WS.GunGameData.Score += KFPM.GunGameKilledScore; - UpdateGunGameLevel(KFPC_WS); + if (KFPC_WS_Killer.Pawn.Health > 0) + { + KFPC_WS_Killer.GunGameData.Score += KFPM.GunGameKilledScore; + UpdateGunGameLevel(KFPC_WS_Killer); + } + } + else + { + // If pawn is human reset game score (we can just check Killed exists as Controller + if (KFPC_WS_Killed != none) + { + ResetGunGame(KFPC_WS_Killed); + } } } - else + + if (OutbreakEvent.ActiveEvent.bVIPGameMode) { - // If pawn is human reset game score (we can just check Killed exists as Controller) - - KFPC_WS = KFPlayerController_WeeklySurvival(Killed); - - if (KFPC_WS != none) + if (KFPC_WS_Killed != none && KFPC_WS_Killed.VIPGameData.isVIP) { - ResetGunGame(KFPC_WS); + // UnregisterPlayer is done on the same frame but this function comes first.. + // we queue a petition to end the game if no vip is found + SetTimer(1.5f, false, 'OnVIPDiesEndMatch'); } } } @@ -1240,7 +1246,7 @@ function UpdateGunGameLevel(KFPlayerController_WeeklySurvival KFPC_WS) { // To prevent audio/vfx lock, while firing when removing the equipped weapon we do a proper gun remove // This new function manages it's state internally - CurrentWeapon.GunGameRemove(); + CurrentWeapon.RemoveGun(); } if (class'KFPerk_SWAT'.static.Is9mm(CurrentWeapon)) @@ -1278,6 +1284,222 @@ function UpdateGunGameLevel(KFPlayerController_WeeklySurvival KFPC_WS) } } +/////////////////////////////////////////////////////////////////////////////////// + +function UnregisterPlayer(PlayerController PC) +{ + local KFPlayerController_WeeklySurvival KFPC_WS; + + super.UnregisterPlayer(PC); + + KFPC_WS = KFPlayerController_WeeklySurvival(PC); + if (OutbreakEvent.ActiveEvent.bVIPGameMode) + { + if (KFPC_WS != none && KFPC_WS.VIPGameData.IsVIP) + { + ChooseVIP(false, KFPC_WS); + } + } +} + +function WaveStarted() +{ + Super.WaveStarted(); + + if (OutbreakEvent.ActiveEvent.bVIPGameMode) + { + if (WaveNum <= 1) + { + ChooseVIP(true); + } + } +} + +function OnVIPDiesEndMatch() +{ + local KFPlayerController KFPC; + + foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) + { + KFPC.SetCameraMode('ThirdPerson'); + } + + WaveEnded(WEC_TeamWipedOut); +} + +function ChooseVIP_SetupVIP() +{ + local KFPlayerController_WeeklySurvival KFPC_WS, NewVIP; + local KFGameReplicationInfo KFGRI; + + NewVIP = none; + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + if (KFPC_WS != none) + { + if (KFPC_WS.VIPGameData.IsVIP) + { + NewVIP = KFPC_WS; + break; + } + } + } + + if (NewVIP != none) + { + //`Log("Setup new VIP: " $NewVIP); + + if (NewVIP.Pawn != none) + { + //`Log("Finished setup new VIP: " $NewVIP); + + NewVIP.GetPerk().PerkSetOwnerHealthAndArmor(false); + + if (NewVIP.VIPGameData.PendingHealthReset) + { + NewVIP.VIPGameData.PendingHealthReset = false; + + // Change current health directly, Pawn.HealDamage does a lot of other stuff that can block the healing + NewVIP.Pawn.Health = NewVIP.Pawn.HealthMax; + } + + // Replicate new data to clients + KFGRI.UpdateVIPPlayer(KFPlayerReplicationInfo(NewVIP.PlayerReplicationInfo)); + KFGRI.UpdateVIPMaxHealth(NewVIP.Pawn.HealthMax); + KFGRI.UpdateVIPCurrentHealth(NewVIP.Pawn.Health); + + NewVIP.PlayVIPGameChosenSound(3.5f); + + ClearTimer('ChooseVIP_SetupVIP'); + } + } +} + +function ChooseVIP(bool ForceAddHealth, optional KFPlayerController_WeeklySurvival PlayerJustLeft = none) +{ + local int RandomNumber; + local KFPlayerController_WeeklySurvival KFPC_WS, CurrentVIP, NewVIP; + local array PotentialVIP; + local KFGameReplicationInfo KFGRI; + + //`Log("ChooseVIP!!!!!"); + + ClearTimer('ChooseVIP_SetupVIP'); + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + if (KFPC_WS != none) + { + if (KFPC_WS.VIPGameData.IsVIP == false && KFPC_WS.VIPGameData.WasVIP == false) + { + PotentialVIP.AddItem(KFPC_WS); + } + + if (KFPC_WS.VIPGameData.IsVIP) + { + CurrentVIP = KFPC_WS; + } + } + } + + if (CurrentVIP != none) + { + //`Log("Remove old VIP: " $CurrentVIP); + + CurrentVIP.VIPGameData.IsVIP = false; + + CurrentVIP.GetPerk().PerkSetOwnerHealthAndArmor(false); + } + + // If there's no potential VIP we restart + if (PotentialVIP.Length == 0) + { + foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS) + { + if (KFPC_WS != none) + { + KFPC_WS.VIPGameData.WasVIP = false; + + if (PlayerJustLeft == none + || PlayerJustLeft != KFPC_WS) + { + PotentialVIP.AddItem(KFPC_WS); + } + } + } + } + + if (PotentialVIP.Length > 0) + { + RandomNumber = Rand(PotentialVIP.Length); + + NewVIP = PotentialVIP[RandomNumber]; + + NewVIP.VIPGameData.IsVIP = true; + NewVIP.VIPGameData.WasVIP = true; + } + + if (NewVIP != none) + { + if (ForceAddHealth || (KFGRI != none && KFGRI.bWaveIsActive == false)) + { + NewVIP.VIPGameData.PendingHealthReset = true; + } + + // If there's no Pawn we have to wait on a Timer function + if (NewVIP.Pawn != none) + { + ChooseVIP_SetupVIP(); + } + else + { + SetTimer(0.25f, true, 'ChooseVIP_SetupVIP'); + } + + ClearTimer('OnVIPDiesEndMatch'); + } +} + +function OnOutbreakWaveWon() +{ + Super.OnOutbreakWaveWon(); + + // GunGame Mode + if (OutbreakEvent.ActiveEvent.bGunGameMode) + { + MyKFGRI.GunGameWavesCurrent += 1; + + // If we unlocked last weapon we only finish if we completed the boss wave + // If we didn't unlock to last weapon and we just finished last wave (before BOSS), repeat + if (bGunGamePlayerOnLastGun) + { + MyKFGRI.bWaveGunGameIsFinal = true; + + if (WaveNum < WaveMax) + { + WaveNum = WaveMax - 1; + } + } + else if (WaveNum >= WaveMax - 1) + { + // Repeat wave before BOSS till forever + WaveNum = WaveMax - 2; + } + + MyKFGRI.bNetDirty = true; + } + + // VIP Mode + if (OutbreakEvent.ActiveEvent.bVIPGameMode) + { + ChooseVIP(true); + } +} + defaultproperties { //Overrides diff --git a/KFGameContent/Classes/KFMapObjective_AreaDefense.uc b/KFGameContent/Classes/KFMapObjective_AreaDefense.uc index bdecf21..b69d947 100644 --- a/KFGameContent/Classes/KFMapObjective_AreaDefense.uc +++ b/KFGameContent/Classes/KFMapObjective_AreaDefense.uc @@ -198,19 +198,32 @@ simulated function bool CanActivateObjectiveByWeekly() { if (Role == Role_Authority) { - if (KFGameInfo(WorldInfo.Game).OutbreakEvent != none - && KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bGunGameMode) + if (KFGameInfo(WorldInfo.Game).OutbreakEvent != none) { - return false; + if (KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bGunGameMode) + { + return false; + } + + if (KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bVIPGameMode) + { + return false; + } } } else { - if (KFGameReplicationInfo(WorldInfo.GRI) != none - && KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode - && KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 16) + if (KFGameReplicationInfo(WorldInfo.GRI) != none && KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode) { - return false; + if (KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 16) + { + return false; + } + + if (KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 17) + { + return false; + } } } diff --git a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc index 7fc8ab6..71122c5 100644 --- a/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc +++ b/KFGameContent/Classes/KFOutbreakEvent_Weekly.uc @@ -920,7 +920,6 @@ defaultproperties bSpawnWeaponListAffectsSecondaryWeapons=true, OverrideItemPickupModifier= 0.5f, //1.0f, //2.0f, // 0.f, OverrideAmmoPickupModifier= 1.0f, //2.0f, //3.0f, // 0.01f, - bOnlyArmorItemPickup=true, TraderTimeModifier=1.0f, // 0.1f, TimeBetweenWaves=30.f, bDisableAddDosh=true, @@ -1190,6 +1189,37 @@ defaultproperties )} + // VIP + SetEvents[17]={( + EventDifficulty=2, + GameLength=GL_Normal, + bVIPGameMode=true, + VIPTargetting=(class'KFGameContent.KFPawn_ZedScrake' + , class'KFGameContent.KFPawn_ZedFleshpound' + , class'KFGameContent.KFPawn_ZedFleshpoundMini' + , class'KFGameContent.KFPawn_ZedGorefast' + , class'KFGameContent.KFPawn_ZedGorefastDualBlade' + , class'KFGameContent.KFPawn_ZedClot_Cyst' + , class'KFGameContent.KFPawn_ZedClot_Slasher' + , class'KFGameContent.KFPawn_ZedClot_Alpha' + , class'KFGameContent.KFPawn_ZedClot_AlphaKing' + , class'KFGameContent.KFPawn_ZedBloat' + , class'KFGameContent.KFPawn_ZedHusk' + , class'KFGameContent.KFPawn_ZedSiren' + , class'KFGameContent.KFPawn_ZedCrawler' + , class'KFGameContent.KFPawn_ZedCrawlerKing' + , class'KFGameContent.KFPawn_ZedStalker' + , class'KFGameContent.KFPawn_ZedDAR_EMP' + , class'KFGameContent.KFPawn_ZedDAR_Laser' + , class'KFGameContent.KFPawn_ZedDAR_Rocket' + , class'KFGameContent.KFPawn_ZedMatriarch' + , class'KFGameContent.KFPawn_ZedPatriarch' + , class'KFGameContent.KFPawn_ZedBloatKing' + , class'KFGameContent.KFPawn_ZedBloatKingSubspawn' + , class'KFGameContent.KFPawn_ZedFleshpoundKing' + , class'KFGameContent.KFPawn_ZedHans'), + )} + //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_ZedFleshpoundKing.uc b/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc index 1909671..6fdd425 100644 --- a/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc +++ b/KFGameContent/Classes/KFPawn_ZedFleshpoundKing.uc @@ -440,7 +440,7 @@ function AdjustDamage(out int InDamage, out vector Momentum, Controller Instigat } } -function HandleAfflictionsOnHit(Controller DamageInstigator, vector HitDir, class DamageType, Actor DamageCauser) +function HandleAfflictionsOnHit(Controller DamageInstigator, vector HitDir, class DamageType, Actor DamageCauser) { if (ShieldHealthPctByte == 0) { diff --git a/KFGameContent/Classes/KFProj_Grenade_HRG_CranialPopper.uc b/KFGameContent/Classes/KFProj_Grenade_HRG_CranialPopper.uc index fff0862..e30c6d2 100644 --- a/KFGameContent/Classes/KFProj_Grenade_HRG_CranialPopper.uc +++ b/KFGameContent/Classes/KFProj_Grenade_HRG_CranialPopper.uc @@ -39,6 +39,7 @@ state WaveState local TraceHitInfo HitInfo; local float Radius; local float DamageHead; + local name HitBoneName; if(bWaveActive) { @@ -54,9 +55,17 @@ state WaveState if(DamageHead > 0) { //`Log("Take: "$Victim); - //HitInfo.BoneName = 'head'; + + HitBoneName = Victim.HeadBoneName; + + if(HitBoneName != `NAME_NONE) + { + HitInfo.BoneName = HitBoneName; + } + + //`Log("HitInfo.BoneName: "$HitInfo.BoneName); + Victim.TakeDamage(DamageHead * UpgradeDamageMod, InstigatorController, Victim.Location, Normal(Victim.Location - Instigator.Location), MyDamageType, HitInfo, (Owner != None) ? Owner : self); - //Monster.PlayDismemberment(0, MyDamageType, WaveImpactMomentum); if (Victim.Health <= 0) { diff --git a/KFGameContent/Classes/KFProj_HRG_Dragonbreath_Splash.uc b/KFGameContent/Classes/KFProj_HRG_Dragonbreath_Splash.uc new file mode 100644 index 0000000..e8da41b --- /dev/null +++ b/KFGameContent/Classes/KFProj_HRG_Dragonbreath_Splash.uc @@ -0,0 +1,25 @@ +//============================================================================= +// KFProj_HRG_DragonBreath_Splash +//============================================================================= +// Projectile class for hrg dragonbreath gun splash. Handles a few overrides. +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_HRG_DragonBreath_Splash extends KFProj_FlareGunSplash; + +defaultproperties +{ + PostExplosionLifetime=2.5 + ExplosionActorClass=class'KFExplosion_HRG_Dragonbreath_GroundFire' + + Begin Object Name=ExploTemplate0 + Damage=8 + DamageRadius=150.0 + MyDamageType=class'KFDT_Fire_Ground_HRG_DragonBreath' + ExplosionEffects=KFImpactEffectInfo'WEP_Flamethrower_ARCH.GroundFire_Impacts' + End Object + + AssociatedPerkClass=none +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFProj_HRG_Locust.uc b/KFGameContent/Classes/KFProj_HRG_Locust.uc new file mode 100644 index 0000000..eacf8e1 --- /dev/null +++ b/KFGameContent/Classes/KFProj_HRG_Locust.uc @@ -0,0 +1,171 @@ +//============================================================================= +// KFProj_HRG_Locust +//============================================================================= +// HRG Locust projectile +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= +class KFProj_HRG_Locust extends KFProj_BallisticExplosive + hidedropdown; + +/** Our intended target actor */ +var private KFPawn LockedTarget; + +/** How much 'stickyness' when seeking toward our target. Determines how accurate rocket is */ +var const float SeekStrength; + +replication +{ + if( bNetInitial ) + LockedTarget; +} + +function SetLockedTarget( KFPawn NewTarget ) +{ + LockedTarget = NewTarget; +} + +simulated function bool AllowNuke() +{ + return false; +} + +simulated function bool AllowDemolitionistConcussive() +{ + return false; +} + +simulated function bool AllowDemolitionistExplosionChangeRadius() +{ + return false; +} + +simulated event Tick( float DeltaTime ) +{ + local vector TargetImpactPos, DirToTarget; + + super.Tick( DeltaTime ); + + // Skip the first frame, then start seeking + if( !bHasExploded + && LockedTarget != none + && Physics == PHYS_Projectile + && Velocity != vect(0,0,0) + && LockedTarget.IsAliveAndWell() + && `TimeSince(CreationTime) > 0.03f ) + { + // Grab our desired relative impact location from the weapon class + TargetImpactPos = class'KFWeap_HRG_Locust'.static.GetLockedTargetLoc( LockedTarget ); + + // Seek towards target + Speed = VSize( Velocity ); + DirToTarget = Normal( TargetImpactPos - Location ); + Velocity = Normal( Velocity + (DirToTarget * (SeekStrength * DeltaTime)) ) * Speed; + + // Aim rotation towards velocity every frame + SetRotation( rotator(Velocity) ); + } +} + +simulated protected function PrepareExplosionTemplate() +{ + local Weapon OwnerWeapon; + local Pawn OwnerPawn; + local KFPerk_Survivalist Perk; + + super(KFProjectile).PrepareExplosionTemplate(); + + OwnerWeapon = Weapon(Owner); + if (OwnerWeapon != none) + { + OwnerPawn = Pawn(OwnerWeapon.Owner); + if (OwnerPawn != none) + { + Perk = KFPerk_Survivalist(KFPawn(OwnerPawn).GetPerk()); + if (Perk != none) + { + ExplosionTemplate.DamageRadius *= KFPawn(OwnerPawn).GetPerk().GetAoERadiusModifier(); + } + } + } +} + +defaultproperties +{ + Physics=PHYS_Projectile + Speed=4000 //6000 + MaxSpeed=4000 //6000 + TossZ=0 + GravityScale=1.0 + MomentumTransfer=0.0f + + Damage=10 + DamageRadius=0 + + SeekStrength=928000.0f // 128000.0f + + bWarnAIWhenFired=true + + ProjFlightTemplate=ParticleSystem'WEP_HRG_Locust_EMIT.FX_HRG_Locust_Projectile' + ProjFlightTemplateZedTime=ParticleSystem'WEP_HRG_Locust_EMIT.FX_HRG_Locust_Projectile_ZED_TIME' + ProjDisintegrateTemplate=ParticleSystem'WEP_HRG_Locust_EMIT.FX_Flying_Bugs_dispersion' + + AmbientSoundPlayEvent=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Projectile' + AmbientSoundStopEvent=AkEvent'WW_WEP_Seeker_6.Stop_WEP_Seeker_6_Projectile' + + ExplosionActorClass=class'KFExplosion_HRG_Locust' + + AltExploEffects=KFImpactEffectInfo'WEP_HRG_Locust_ARCH.FX_HRG_Locust_Explosion_Concussive_force' + + // Grenade explosion light + Begin Object Class=PointLightComponent Name=ExplosionPointLight + LightColor=(R=252,G=218,B=171,A=255) + Brightness=4.f + Radius=2000.f + FalloffExponent=10.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=False + bCastPerObjectShadows=false + bEnabled=FALSE + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE) + End Object + + // explosion + Begin Object Class=KFGameExplosion Name=ExploTemplate0 + Damage=80 + DamageRadius=300 + DamageFalloffExponent=0.5f + DamageDelay=0.f + + //Impulse applied to Zeds + MomentumTransferScale=1 + + // Damage Effects + MyDamageType=class'KFDT_Explosive_HRG_Locust' + KnockDownStrength=0 + FractureMeshRadius=0.0 + FracturePartVel=0.0 + ExplosionEffects=KFImpactEffectInfo'WEP_HRG_Locust_ARCH.FX_HRG_Locust_Explosion_Concussive_force' + ExplosionSound=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Explosion' + + // Dynamic Light + ExploLight=ExplosionPointLight + ExploLightStartFadeOutTime=0.0 + ExploLightFadeOutTime=0.2 + + // Camera Shake + CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' + CamShakeInnerRadius=0 + CamShakeOuterRadius=500 + CamShakeFalloff=3.f + bOrientCameraShakeTowardsEpicenter=true + + bIgnoreInstigator=true + End Object + ExplosionTemplate=ExploTemplate0 + + bCanDisintegrate=false +} diff --git a/KFGameContent/Classes/KFProj_Mine_Reconstructor.uc b/KFGameContent/Classes/KFProj_Mine_Reconstructor.uc index 91929a1..2571787 100644 --- a/KFGameContent/Classes/KFProj_Mine_Reconstructor.uc +++ b/KFGameContent/Classes/KFProj_Mine_Reconstructor.uc @@ -905,8 +905,8 @@ defaultproperties MaxDamageRadiusPerPercentage=340 MinDamageRadiusPerPercentage=160 - MaxDamagePerPercentage=350 //300 - MinDamagePerPercentage=35 //30 + MaxDamagePerPercentage=400 //350 //300 + MinDamagePerPercentage=40 //35 //30 MaxCollisionRadius=20 MinCollisionRadius=10 diff --git a/KFGameContent/Classes/KFProj_Pellet_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFProj_Pellet_HRG_Dragonbreath.uc new file mode 100644 index 0000000..3fac59d --- /dev/null +++ b/KFGameContent/Classes/KFProj_Pellet_HRG_Dragonbreath.uc @@ -0,0 +1,85 @@ +//============================================================================= +// KFProj_Pellet_HRG_Dragonbreath +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFProj_Pellet_HRG_Dragonbreath extends KFProj_Bullet_Pellet + hidedropdown; + + +/** Last hit normal from Touch() or HitWall() */ +var vector LastHitNormal; + +var float GroundFireChance; + + +simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal) +{ + LastHitNormal = HitNormal; + Super.ProcessTouch(Other, HitLocation, HitNormal); +} + +/** +* Explode this Projectile +*/ +simulated function TriggerExplosion(Vector HitLocation, Vector HitNormal, Actor HitActor) +{ + LastHitNormal = HitNormal; + Super.TriggerExplosion(HitLocation, HitNormal, HitActor); +} + + +simulated protected function StopSimulating() +{ + local vector FlameSpawnVel; + + if (Role == ROLE_Authority && Physics == PHYS_Falling && FRand() < GroundFireChance) + { + //SpawnGroundFire(); + FlameSpawnVel = 0.25f * CalculateResidualFlameVelocity(LastHitNormal, Normal(Velocity), VSize(Velocity)); + SpawnResidualFlame(class'KFProj_HRG_DragonBreath_Splash', Location + (LastHitNormal * 10.f), FlameSpawnVel); + } + + super.StopSimulating(); +} + +defaultproperties +{ + GroundFireChance=1.f + Physics=PHYS_Falling + + MaxSpeed=7000.0 + Speed=7000.0 + TerminalVelocity=7000.0 + + bWarnAIWhenFired=true + + DamageRadius=0 + GravityScale=0.35 + TossZ=0 + + Begin Object Class=PointLightComponent Name=PointLight0 + LightColor=(R=252,G=218,B=171,A=255) + Brightness=0.5f + Radius=500.f + FalloffExponent=10.f + CastShadows=False + CastStaticShadows=FALSE + CastDynamicShadows=False + bCastPerObjectShadows=false + bEnabled=true + LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE) + End Object + ProjFlightLight=PointLight0 + + ImpactEffects=KFImpactEffectInfo'WEP_DragonsBreath_ARCH.DragonsBreath_bullet_impact' + ProjFlightTemplate=ParticleSystem'WEP_DragonsBreath_EMIT.Tracer.FX_DragonsBreath_Tracer' + ProjFlightTemplateZedTime=ParticleSystem'WEP_DragonsBreath_EMIT.Tracer.FX_DragonsBreath_Tracer_ZEDTime' + + AmbientSoundPlayEvent=AkEvent'WW_WEP_SA_DragonsBreath.Play_SA_DragonsBreath_Projectile_Loop' + AmbientSoundStopEvent=AkEvent'WW_WEP_SA_DragonsBreath.Stop_SA_DragonsBreath_Projectile_Loop' +} + diff --git a/KFGameContent/Classes/KFSeasonalEventStats_Fall2022.uc b/KFGameContent/Classes/KFSeasonalEventStats_Fall2022.uc new file mode 100644 index 0000000..83021fc --- /dev/null +++ b/KFGameContent/Classes/KFSeasonalEventStats_Fall2022.uc @@ -0,0 +1,128 @@ +//============================================================================= +// KFSeasonalEventStats_Fall2022 +//============================================================================= +// Tracks event-specific challenges/accomplishments for Fall 2022 +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2021 Tripwire Interactive LLC +//============================================================================= +class KFSeasonalEventStats_Fall2022 extends KFSeasonalEventStats; + +var transient private const int BossKillsRequired, ZedsInBonfiresRequired, EndlessWaveRequired; + +private event Initialize(string MapName) +{ + local string CapsMapName; + CapsMapName = Caps(MapName); + + bObjectiveIsValidForMap[0] = 1; // Kill 15 Bosses on any map or mode + bObjectiveIsValidForMap[1] = 0; // Complete the Weekly on BarmwichTown + bObjectiveIsValidForMap[2] = 0; // Open the Weapon Room + bObjectiveIsValidForMap[3] = 0; // Make 50 Zeds to pass through the bonfires of Barmwitch Town + bObjectiveIsValidForMap[4] = 0; // Complete wave 15 on Endless Hard or higher difficulty on Barmwitch Town + + if (CapsMapName == "KF-BARMWICHTOWN") + { + bObjectiveIsValidForMap[1] = 1; + bObjectiveIsValidForMap[2] = 1; + bObjectiveIsValidForMap[3] = 1; + bObjectiveIsValidForMap[4] = 1; + } + + SetSeasonalEventStatsMax(BossKillsRequired, 0, 0, ZedsInBonfiresRequired, EndlessWaveRequired); +} + +private event GrantEventItems() +{ + if (Outer.IsEventObjectiveComplete(0) && + Outer.IsEventObjectiveComplete(1) && + Outer.IsEventObjectiveComplete(2) && + Outer.IsEventObjectiveComplete(3) && + Outer.IsEventObjectiveComplete(4)) + { + GrantEventItem(9424); + } +} + +// Kill 15 Bosses on any map or mode +simulated function OnBossDied() +{ + local int ObjIdx; + ObjIdx = 0; + + // Boss kills in any map + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + IncrementSeasonalEventStat(ObjIdx, 1); + if (Outer.GetSeasonalEventStatValue(ObjIdx) >= BossKillsRequired) + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } +} + +// Complete the Weekly on Netherhold +simulated event OnGameWon(class GameClass, int Difficulty, int GameLength, bool bCoOp) +{ + local int ObjIdx; + ObjIdx = 1; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (GameClass == class'KFGameInfo_WeeklySurvival') + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } +} + +// Complete wave 15 on Endless Hard or higher difficulty on Netherhold +simulated event OnWaveCompleted(class GameClass, int Difficulty, int WaveNum) +{ + local int ObjIdx; + ObjIdx = 4; + + if (bObjectiveIsValidForMap[ObjIdx] != 0) + { + if (WaveNum >= EndlessWaveRequired && GameClass == class'KFGameInfo_Endless' && Difficulty >= `DIFFICULTY_HARD) + { + FinishedObjective(SEI_Fall, ObjIdx); + } + } +} + +simulated function OnTryCompleteObjective(int ObjectiveIndex, int EventIndex) +{ + local int WeaponRoomIdx, BonfireIdx; + WeaponRoomIdx = 2; + BonfireIdx = 3; + + if(EventIndex == SEI_Fall) + { + if (ObjectiveIndex == WeaponRoomIdx) + { + if (bObjectiveIsValidForMap[ObjectiveIndex] != 0) + { + FinishedObjective(SEI_Fall, ObjectiveIndex); + } + } + else if (ObjectiveIndex == BonfireIdx) + { + if (bObjectiveIsValidForMap[ObjectiveIndex] != 0) + { + IncrementSeasonalEventStat(ObjectiveIndex, 1); + if (Outer.GetSeasonalEventStatValue(ObjectiveIndex) >= ZedsInBonfiresRequired) + { + FinishedObjective(SEI_Fall, ObjectiveIndex); + } + } + } + } +} + +defaultproperties +{ + BossKillsRequired=15 + EndlessWaveRequired=15 + ZedsInBonfiresRequired=50 +} diff --git a/KFGameContent/Classes/KFWeapAttach_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFWeapAttach_HRG_Dragonbreath.uc new file mode 100644 index 0000000..360f6d2 --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_HRG_Dragonbreath.uc @@ -0,0 +1,83 @@ +//============================================================================= +// KFWeapAttach_HRG_Dragonbreath +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeapAttach_HRG_Dragonbreath extends KFWeaponAttachment; + +`define HRGDRAGONBREATH_MIC_BARREL_INDEX 0 + +var transient float BarrelHeatPerProjectile; +var transient float MaxBarrelHeat; +var transient float BarrelCooldownRate; +var transient float CurrentBarrelHeat; +var transient float LastBarrelHeat; +var transient int NumPelletsDefault; +var transient int NumPelletsAlt; + +simulated event PreBeginPlay() +{ + Super.PreBeginPlay(); + + BarrelHeatPerProjectile = class'KFWeap_HRG_Dragonbreath'.default.BarrelHeatPerProjectile; + MaxBarrelHeat = class'KFWeap_HRG_Dragonbreath'.default.MaxBarrelHeat; + BarrelCooldownRate = class'KFWeap_HRG_Dragonbreath'.default.BarrelCooldownRate; + NumPelletsDefault = class'KFWeap_HRG_Dragonbreath'.default.NumPellets[0]; + NumPelletsAlt = class'KFWeap_HRG_Dragonbreath'.default.NumPellets[1]; +} + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + // Force start with "Glow_Intensity" of 0.0f + LastBarrelHeat = MaxBarrelHeat; + ChangeBarrelMaterial(); +} + +simulated function ChangeBarrelMaterial() +{ + if( CurrentBarrelHeat != LastBarrelHeat ) + { + if ( WeaponMIC == None && WeapMesh != None ) + { + WeaponMIC = WeapMesh.CreateAndSetMaterialInstanceConstant(`HRGDRAGONBREATH_MIC_BARREL_INDEX); + } + + WeaponMIC.SetScalarParameterValue('Barrel_intensity', CurrentBarrelHeat); + } +} + +simulated function Tick(float Delta) +{ + Super.Tick(Delta); + + CurrentBarrelHeat = fmax(CurrentBarrelHeat - BarrelCooldownRate * Delta, 0.0f); + ChangeBarrelMaterial(); +} + +/** Override to update emissive in weapon's barrel after firing */ +simulated function PlayWeaponFireAnim() +{ + local float BarrelHeatPerShot; + local KFPawn OwnerPawn; + + Super.PlayWeaponFireAnim(); + + OwnerPawn = KFPawn(Owner); + + BarrelHeatPerShot = BarrelHeatPerProjectile * (OwnerPawn.FiringMode == 0 ? NumPelletsDefault : NumPelletsAlt); + CurrentBarrelHeat = fmin(CurrentBarrelHeat + BarrelHeatPerShot, MaxBarrelHeat); +} + +defaultproperties +{ + CurrentBarrelHeat=0.0f + LastBarrelHeat=0.0f + NumPelletsDefault=0 + NumPelletsAlt=0 +} \ No newline at end of file diff --git a/KFGameContent/Classes/KFWeapAttach_Scythe.uc b/KFGameContent/Classes/KFWeapAttach_Scythe.uc new file mode 100644 index 0000000..6056a8a --- /dev/null +++ b/KFGameContent/Classes/KFWeapAttach_Scythe.uc @@ -0,0 +1,144 @@ +//============================================================================= +// KFWeapAttach_Scythe +//============================================================================= +// +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeapAttach_Scythe extends KFWeaponAttachment; + +var const float UnfoldBlendingDuration; +var const float UnfoldedAnimRateModifier; +const FoldAnim = 'Clean_NoBlood'; + +var AnimTree CustomAnimTree; +var AnimNodeBlendPerBone FoldBlendNode; + +var transient bool bIsFolded; + +event PreBeginPlay() +{ + Super.PreBeginPlay(); + + // Override the animtree. Doing this here (before AttachTo) instead of in defaultprops + // avoids an undesired call to our owning Pawn's PostInitAnimTree + if ( CustomAnimTree != None ) + { + WeapMesh.SetAnimTreeTemplate(CustomAnimTree); + + WeapAnimNode = AnimNodeSequence(WeapMesh.FindAnimNode('WeaponSeq')); + FoldBlendNode = AnimNodeBlendPerBone(WeapMesh.FindAnimNode('FoldBlendNode')); + + // The special event might have arrived before the attachment is created, but it's updated in the owner, so copy the state here... + if (KFPawn(Owner) != none && FoldBlendNode != none) + { + bIsFolded = KFPawn(Owner).WeaponSpecialAction == 0; + FoldBlendNode.SetBlendTarget(bIsFolded ? 1.0f : 0.0f, 0.f); + } + } +} + +simulated function ChangeMode() +{ + bIsFolded = !bIsFolded; + + // FoldControl = SkelControlSingleBone( WeapMesh.FindSkelControl('FoldControl') ); + if( FoldBlendNode != none ) + { + FoldBlendNode.SetBlendTarget( bIsFolded ? 1.0f : 0.0f, 0.0f ); + } +} + +/** Called from the pawn when our first person weapon changes states */ +simulated function UpdateThirdPersonWeaponAction(EWeaponState NewWeaponState, KFPawn P, byte ThirdPersonAnimRateByte ) +{ + Super.UpdateThirdPersonWeaponAction(NewWeaponState, P, ThirdPersonAnimRate); + + if (NewWeaponState == WEP_Cleaning) + { + if (WeapAnimNode != none) + { + if (WeapAnimNode.AnimSeq == none) + { + WeapAnimNode.SetAnim(FoldAnim); + } + + WeapAnimNode.PlayAnim(); + } + } +} + +simulated function ANIMNOTIFY_ShellEject() +{ + ChangeMode(); +} + +/** + * Plays a split (upper and lower body) animation on the owning pawn + * Network: All but dedicated + * + * @param P Owning pawn to play animation on + * @param AnimName Anim to play + * @param bPlaySynchronizedWeaponAnim If true, try to play the same animation on the weapon mesh + */ +simulated function float PlayCharacterMeshAnim(KFPawn P, name AnimName, optional bool bPlaySynchedWeaponAnim, optional bool bLooping) +{ + local float AnimRate; + local float Duration; + local EAnimSlotStance Stance; + local string AnimStr; + + // skip weapon anims while in a special move + if( P.IsDoingSpecialMove() && !P.SpecialMoves[P.SpecialMove].bAllowThirdPersonWeaponAnims ) + { + return 0.f; + } + + Stance = (!P.bIsCrouched) ? EAS_UpperBody : EAS_CH_UpperBody; + + AnimRate = ThirdPersonAnimRate; + AnimStr = Caps(string(AnimName)); + + if (!bIsFolded && (InStr(AnimStr, "ATK") != INDEX_NONE || InStr(AnimName, "COMB") != INDEX_NONE)) + { + AnimRate *= UnfoldedAnimRateModifier; + } + + Duration = P.PlayBodyAnim(AnimName, Stance, AnimRate, DefaultBlendInTime, DefaultBlendOutTime, bLooping); + + if ( Duration > 0 && bPlaySynchedWeaponAnim ) + { + PlayWeaponMeshAnim(AnimName, P.BodyStanceNodes[Stance], bLooping); + } + + `log(GetFuncName()@"called on:"$P@"Anim:"$AnimName@"Duration:"$Duration, bDebug); + + return Duration; +} + +/** Special event added for weap attachments. Free for use */ +function OnSpecialEvent(int Arg) +{ + bIsFolded = Arg == 0; + + // FoldControl = SkelControlSingleBone( WeapMesh.FindSkelControl('FoldControl') ); + if( FoldBlendNode != none ) + { + FoldBlendNode.SetBlendTarget( bIsFolded ? 1.0f : 0.0f, 0.0f ); + } +} + +defaultproperties +{ + CustomAnimTree=AnimTree'WEP_Scythe_ARCH.3P_Scythe_Animtree' + bIsFolded=true; + UnfoldBlendingDuration=0.25f + UnfoldedAnimRateModifier=0.7f; + + // Weapon SkeletalMesh + Begin Object Name=SkeletalMeshComponent0 + bForceRefPose=0 + End Object +} diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc new file mode 100644 index 0000000..2dc3df4 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_G36C.uc @@ -0,0 +1,159 @@ +//============================================================================= +// KFWeap_AssaultRifle_G36C +//============================================================================= +// Class Description +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_AssaultRifle_G36C extends KFWeap_SMGBase; + +simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo) +{ + super.ZoomIn(bAnimateTransition, ZoomTimeToGo); + + if (LaserSight != none) + { + LaserSight.ChangeVisibility(false); + } +} + +simulated function ZoomOut( bool bAnimateTransition, float ZoomTimeToGo ) +{ + super.ZoomOut( bAnimateTransition, ZoomTimeToGo ); + + if (LaserSight != none) + { + LaserSight.ChangeVisibility(true); + } +} + +defaultproperties +{ + bHasFireLastAnims=true + BonesToLockOnEmpty=(RW_Bolt, RW_Charging_Handle) + + // Shooting Animations + FireSightedAnims[0]=Shoot_Iron + FireSightedAnims[1]=Shoot_Iron2 + FireSightedAnims[2]=Shoot_Iron3 + + // FOV + MeshFOV=70 + MeshIronSightFOV=20 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=150 + DOF_FG_MaxNearBlurSize=3 + + // Zooming/Position + IronSightPosition=(X=40,Y=0.1,Z=-4.57) + PlayerViewOffset=(X=14,Y=11,Z=-5) + + // Content + PackageKey="G36C" + FirstPersonMeshName="WEP_1P_G36C_MESH.Wep_1stP_G36C_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_G36C_ANIM.Wep_1stP_G36C_Anim" + PickupMeshName="WEP_3P_G36C_MESH.Wep_G36C_Pickup" + AttachmentArchetypeName="WEP_G36C_ARCH.Wep_G36C_3P" + MuzzleFlashTemplateName="WEP_G36C_ARCH.Wep_G36C_MuzzleFlash" + + LaserSightTemplate=KFLaserSightAttachment'FX_LaserSight_ARCH.LaserSight_WithAttachment_1P' + + // Ammo + MagazineCapacity[0]=30 + SpareAmmoCapacity[0]=450 + InitialSpareMags[0]=3 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=90 + minRecoilPitch=80 + maxRecoilYaw=80 + minRecoilYaw=-80 + RecoilRate=0.085 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=900 + RecoilMinPitchLimit=65035 + RecoilISMaxYawLimit=100 + RecoilISMinYawLimit=65460 + RecoilISMaxPitchLimit=350 + RecoilISMinPitchLimit=65460 + IronSightMeshFOVCompensationScale=4.0 + + // Old Recoil Data + // maxRecoilPitch=80 + // minRecoilPitch=65 + // maxRecoilYaw=60 + // minRecoilYaw=-60 + // RecoilRate=0.063 + // RecoilMaxYawLimit=400 + // RecoilMinYawLimit=65135 + // RecoilMaxPitchLimit=800 + // RecoilMinPitchLimit=65035 + // RecoilISMaxYawLimit=150 + // RecoilISMinYawLimit=65385 + // RecoilISMaxPitchLimit=350 + // RecoilISMinPitchLimit=65435 + // IronSightMeshFOVCompensationScale=1.5 + + // Inventory + InventorySize=7 + GroupPriority=100 + WeaponSelectTexture=Texture2D'wep_ui_g36c_tex.UI_WeaponSelect_G36C' + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_AssaultRifle' + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_G36C' + PenetrationPower(DEFAULT_FIREMODE)=4.0 + FireInterval(DEFAULT_FIREMODE)=+0.08 // 750 RPM + Spread(DEFAULT_FIREMODE)=0.005 + InstantHitDamage(DEFAULT_FIREMODE)=45.0 + FireOffset=(X=30,Y=4.5,Z=-5) + + // ALT_FIREMODE + FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_InstantHit + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_AssaultRifle' + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_G36C' + PenetrationPower(ALTFIRE_FIREMODE)=4.0 + FireInterval(ALTFIRE_FIREMODE)=+0.08 // 750 RPM + InstantHitDamage(ALTFIRE_FIREMODE)=45.0 + Spread(ALTFIRE_FIREMODE)=0.005 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_G36C' + InstantHitDamage(BASH_FIREMODE)=26 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_3P_Shoot_LP', FirstPersonCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_1P_Shoot_LP') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_3P_Shoot_Single', FirstPersonCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_1P_Shoot_Single') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_AK12.Play_WEP_SA_AK12_Handling_DryFire' //@TODO: Replace me + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_AK12.Play_WEP_SA_AK12_Handling_DryFire' //@TODO: Replace me +// WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_G36C.Play_WEP_G36C_Dry_Fire' +// WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_G36C.Play_WEP_G36C_Dry_Fire' + + // Advanced (High RPM) Fire Effects + bLoopingFireAnim(DEFAULT_FIREMODE)=true + bLoopingFireSnd(DEFAULT_FIREMODE)=true + WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_3P_End_LP', FirstPersonCue=AkEvent'WW_WEP_G36C.Play_WEP_G36C_1P_End_LP') + SingleFireSoundIndex=ALTFIRE_FIREMODE + + // Attachments + bHasIronSights=true + bHasFlashlight=false + bHasLaserSight=true + + AssociatedPerkClasses(0)=class'KFPerk_Swat' + + // Weapon Upgrade stat boosts + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) +} diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_LazerCutter.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_LazerCutter.uc index e178831..eece2a4 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_LazerCutter.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_LazerCutter.uc @@ -998,7 +998,7 @@ defaultproperties // Zooming/Position PlayerViewOffset = (X = 3.0, Y = 10, Z = -1) - IronSightPosition = (X = 0, Y = 0, Z = 0) + IronSightPosition = (X = 0, Y = 0.037, Z = 0.11) // Ammo MagazineCapacity[0] = 50 diff --git a/KFGameContent/Classes/KFWeap_AssaultRifle_Microwave.uc b/KFGameContent/Classes/KFWeap_AssaultRifle_Microwave.uc index 5693bfc..0d13cba 100644 --- a/KFGameContent/Classes/KFWeap_AssaultRifle_Microwave.uc +++ b/KFGameContent/Classes/KFWeap_AssaultRifle_Microwave.uc @@ -73,7 +73,7 @@ defaultproperties DOF_FG_MaxNearBlurSize=2.5 // Zooming/Position - IronSightPosition=(X=10,Y=0,Z=0) //x20 + IronSightPosition=(X=10,Y=-0.015,Z=0.09) //x20 PlayerViewOffset=(X=30.0,Y=10,Z=-2.5) //x18 y9 z0 // Content diff --git a/KFGameContent/Classes/KFWeap_AutoTurret.uc b/KFGameContent/Classes/KFWeap_AutoTurret.uc index 76019c6..7f62937 100644 --- a/KFGameContent/Classes/KFWeap_AutoTurret.uc +++ b/KFGameContent/Classes/KFWeap_AutoTurret.uc @@ -176,7 +176,7 @@ simulated function GetTurretSpawnLocationAndDir(out vector SpawnLocation, out ve } /** Detonates the oldest turret */ -simulated function Detonate() +simulated function Detonate(optional bool bKeepTurret = false) { local int i; local array TurretsCopy; @@ -187,10 +187,15 @@ simulated function Detonate() TurretsCopy = KFPC.DeployedTurrets; for (i = 0; i < TurretsCopy.Length; i++) { + if (bKeepTurret && i == 0) + { + continue; + } + KFPawn_AutoTurret(TurretsCopy[i]).SetTurretState(ETS_Detonate); } - KFPC.DeployedTurrets.Remove(0, KFPC.DeployedTurrets.Length); + KFPC.DeployedTurrets.Remove(bKeepTurret ? 1 : 0, KFPC.DeployedTurrets.Length); SetReadyToUse(true); @@ -231,7 +236,17 @@ function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon ) if (PickedUpWeapon.KFPlayer != none && PickedUpWeapon.KFPlayer != KFPC) { - KFPC.DeployedTurrets = PickedUpWeapon.KFPlayer.DeployedTurrets; + for (i = 0; i < PickedUpWeapon.KFPlayer.DeployedTurrets.Length; i++) + { + KFPC.DeployedTurrets.AddItem(PickedUpWeapon.KFPlayer.DeployedTurrets[i]); + } + + PickedUpWeapon.KFPlayer.DeployedTurrets.Remove(0, PickedUpWeapon.KFPlayer.DeployedTurrets.Length); + } + + if (KFPC.DeployedTurrets.Length > 1) + { + Detonate(true); } PickedUpWeapon.KFPlayer = none; diff --git a/KFGameContent/Classes/KFWeap_Beam_Microwave.uc b/KFGameContent/Classes/KFWeap_Beam_Microwave.uc index fb6cd61..d67a5f2 100644 --- a/KFGameContent/Classes/KFWeap_Beam_Microwave.uc +++ b/KFGameContent/Classes/KFWeap_Beam_Microwave.uc @@ -184,7 +184,7 @@ defaultproperties PlayerIronSightFOV=80 // Zooming/Position - IronSightPosition=(X=3,Y=0,Z=0) + IronSightPosition=(X=3,Y=-0.032,Z=-0.03) PlayerViewOffset=(X=5.0,Y=9,Z=-3) // Depth of field diff --git a/KFGameContent/Classes/KFWeap_Blunt_MedicBat.uc b/KFGameContent/Classes/KFWeap_Blunt_MedicBat.uc index 4f48058..b00c40e 100644 --- a/KFGameContent/Classes/KFWeap_Blunt_MedicBat.uc +++ b/KFGameContent/Classes/KFWeap_Blunt_MedicBat.uc @@ -396,8 +396,13 @@ simulated protected function PrepareExplosion() KFPC = KFPlayerController(Instigator.Controller); if (KFPC != none) { + `Log("RADIUS BEFORE: " $ExplosionTemplate.DamageRadius); + InstigatorPerk = KFPC.GetPerk(); ExplosionTemplate.DamageRadius *= InstigatorPerk.GetAoERadiusModifier(); + + `Log("RADIUS BEFORE: " $ExplosionTemplate.DamageRadius); + } } @@ -448,6 +453,9 @@ simulated function SpawnExplosionFromTemplate(KFGameExplosion Template) ExploActor.Explode(Template, vector(SpawnRot)); } + + // Reset damage radius + ExplosionTemplate.DamageRadius = StartingDamageRadius; } simulated function CustomFire() diff --git a/KFGameContent/Classes/KFWeap_Blunt_Pulverizer.uc b/KFGameContent/Classes/KFWeap_Blunt_Pulverizer.uc index 17d3229..5ad9da5 100644 --- a/KFGameContent/Classes/KFWeap_Blunt_Pulverizer.uc +++ b/KFGameContent/Classes/KFWeap_Blunt_Pulverizer.uc @@ -138,6 +138,8 @@ simulated function CustomFire() // tell remote clients that we fired, to trigger effects in third person IncrementFlashCount(); + ExplosionTemplate.DamageRadius = StartingDamageRadius; + if ( bDebug ) { DrawDebugCone(SpawnLoc, vector(SpawnRot), ExplosionTemplate.DamageRadius, ExplosionTemplate.DirectionalExplosionAngleDeg * DegToRad, diff --git a/KFGameContent/Classes/KFWeap_Edged_Scythe.uc b/KFGameContent/Classes/KFWeap_Edged_Scythe.uc new file mode 100644 index 0000000..07adae4 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_Edged_Scythe.uc @@ -0,0 +1,428 @@ +//============================================================================= +// KFWeap_Edged_AbominationAxe +//============================================================================= +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= + +class KFWeap_Edged_Scythe extends KFWeap_MeleeBase; + +var const array HitboxChainFolded; +var const array HitboxChainUnfolded; + +var const int MaxHitRangeFolded; +var const int MaxHitRangeUnfolded; + +var const int FoldedDamage; +var const int FoldedDamageAlt; + +var const int UnfoldedDamage; +var const int UnfoldedDamageAlt; + +var const class FoldedDT; +var const class FoldedDTAlt; + +var const class UnfoldedDT; +var const class UnfoldedDTAlt; + +var const float UnfoldBlendingDuration; + +var const float FoldedAttackAnimRate; + +var const vector PlayerViewOffsetUnfolded; + +var transient vector OriginalPlayerViewOffset; +var transient bool bIsFolded; + +var AnimNodeBlendPerBone FoldBlendNode; + +var const ParticleSystem BloodParticles; + +const BloodParticlesSocket = 'BlockEffect'; + +var ParticleSystem FoldedTrailParticleSystem; +var ParticleSystem UnfoldedTrailParticleSystem; + +simulated event PreBeginPlay() +{ + Super.PreBeginPlay(); + + OriginalPlayerViewOffset = PlayerViewOffset; +} + +event PostBeginPlay() +{ + Super.PostBeginPlay(); + + ChangeMode(true, false); + + SetFoldedBladeBlendTarget(1.0f, 0.0f); +} + +///////////////////////////////////////////////////////////////////////////// + +/** The SkeletalMeshComponents Animations are being instanced from AnimTreeTemplate +* before PostInitAnimTree. Be sure to never set the Mesh's animations directly through +* the package */ +simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp) +{ + Super.PostInitAnimTree(SkelComp); + + FoldBlendNode = AnimNodeBlendPerBone(SkelComp.FindAnimNode('FoldBlendNode')); + + SetFoldedBladeBlendTarget(1.0f, 0.0f); +} + +///////////////////////////////////////////////////////////////////////////// + +/** Returns true if weapon can potentially be reloaded */ +simulated function bool CanReload(optional byte FireModeNum) +{ + return true; +} + +simulated state WeaponUpkeep +{ + simulated function BeginState(name PreviousStateName) + { + local name AnimName; + local float Duration; + + AnimName = CleanNonBloodyAnim; + + Duration = MySkelMesh.GetAnimLength(AnimName); + if ( Duration > 0.f ) + { + if ( Instigator.IsFirstPerson() ) + { + PlayAnimation(AnimName); + SetTimer(Duration, FALSE, nameof(SwapComplete)); + } + } + else + { + `warn("Duration is zero!!!"@AnimName); + SetTimer(0.001, FALSE, nameof(SwapComplete)); + } + + NotifyBeginState(); + } + + simulated function BeginFire(byte FireModeNum) + { + + } + + simulated event EndState(Name NextStateName) + { + ClearTimer(nameof(SwapComplete)); + Super.EndState(NextStateName); + NotifyEndState(); + } + + simulated function SwapComplete() + { + if (Role == ROLE_Authority) + { + GotoState('Active'); + } + else + { + GotoState('Active'); + ServerSwapComplete(); + } + } +} + +server reliable function ServerSwapComplete() +{ + GotoState('Active'); +} + +simulated function ChangeMode(bool IsFolded, bool bApplyBlend = true) +{ + if (MeleeAttackHelper == none) + return; + + if (IsFolded) + { + MeleeAttackHelper.SetMeleeRange(MaxHitRangeFolded); + MeleeAttackHelper.SetHitBoxChain(HitboxChainFolded); + + InstantHitDamage[DEFAULT_FIREMODE] = FoldedDamage; + InstantHitDamage[HEAVY_ATK_FIREMODE] = FoldedDamageAlt; + + InstantHitDamageTypes[DEFAULT_FIREMODE] = FoldedDT; + InstantHitDamageTypes[HEAVY_ATK_FIREMODE] = FoldedDTAlt; + + PlayerViewOffset = OriginalPlayerViewOffset; + } + else + { + MeleeAttackHelper.SetMeleeRange(MaxHitRangeUnfolded); + MeleeAttackHelper.SetHitBoxChain(HitboxChainUnfolded); + + InstantHitDamage[DEFAULT_FIREMODE] = UnfoldedDamage; + InstantHitDamage[HEAVY_ATK_FIREMODE] = UnfoldedDamageAlt; + + InstantHitDamageTypes[DEFAULT_FIREMODE] = UnfoldedDT; + InstantHitDamageTypes[HEAVY_ATK_FIREMODE] = UnfoldedDTAlt; + + PlayerViewOffset = PlayerViewOffsetUnfolded; + } + + NotifyServerMode(bIsFolded); + + if (bApplyBlend) + { + SetFoldedBladeBlendTarget(bIsFolded ? 1.0f : 0.0f, UnfoldBlendingDuration); + } +} + +simulated function SetFoldedBladeBlendTarget(float Value, float BlendTime) +{ + if ( FoldBlendNode != None ) + { + FoldBlendNode.SetBlendTarget(Value, BlendTime); + } +} + +simulated function StartFire(byte FireModeNum) +{ + if (StartFireDisabled && FireModeNum == BLOCK_FIREMODE) + { + StartFireDisabled = false; + return; + } + + if (FireModeNum == DEFAULT_FIREMODE || FireModeNum == HEAVY_ATK_FIREMODE) + { + DistortTrailParticle = bIsFolded ? FoldedTrailParticleSystem : UnfoldedTrailParticleSystem; + } + else if (FireModeNum == BASH_FIREMODE) + { + DistortTrailParticle = none; + } + + super.StartFire(FireModeNum); +} + +static simulated function float CalculateTraderWeaponStatDamage() +{ + // How is this calculated for this weapon? + return default.UnfoldedDamage; +} + +simulated function PlayAnimation(name Sequence, optional float fDesiredDuration, optional bool bLoop, optional float BlendInTime=0.1, optional float BlendOutTime=0.0) +{ + local string NewAnimName; + + if (Sequence == 'Idle' || + Sequence == 'Guncheck_V1' || + Sequence == 'Guncheck_V2' || + Sequence == 'Guncheck_V3') + { + NewAnimName = string(Sequence); + NewAnimName $= ((bIsFolded) ? "_Folded" : "_Unfolded"); + Super.PlayAnimation(name(NewAnimName), fDesiredDuration, bLoop, BlendInTime, BlendOutTime); + } + else + { + Super.PlayAnimation(Sequence, fDesiredDuration, bLoop, BlendInTime, BlendOutTime); + } +} + +simulated function ModifyMeleeAttackSpeed(out float InSpeed, optional int FireMode = DEFAULT_FIREMODE, optional int UpgradeIndex = INDEX_NONE, optional KFPerk CurrentPerk) +{ + Super.ModifyMeleeAttackSpeed(InSpeed, FireMode, UpgradeIndex, CurrentPerk); + + if (bIsFolded) + { + InSpeed *= FoldedAttackAnimRate; + } +} + +/** + * Overriden to apply fold/unfold logic + */ +simulated function ANIMNOTIFY_LockBolt() +{ + bIsFolded = !bIsFolded; + ChangeMode(bIsFolded); +} + +/** Unused */ +simulated function ANIMNOTIFY_UnLockBolt() +{ + +} + +/** Play blood VFX as we only use one anim. */ +simulated function ANIMNOTIFY_CleanBlood() +{ + if (!bIsBloody) + { + return; + } + + if (WorldInfo.NetMode != NM_DedicatedServer && BloodParticles != none) + { + WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment(BloodParticles, MySkelMesh, BloodParticlesSocket, true); + } + + Super.ANIMNOTIFY_CleanBlood(); +} + +simulated state WeaponEquipping +{ + /** Initialize the weapon as being active and ready to go. */ + simulated event BeginState(Name PreviousStateName) + { + Super.BeginState(PreviousStateName); + + if (WorldInfo.NetMode != NM_Client) + { + NotifyInitialState(bIsFolded); + } + } +} + +/** + Should replicate to 3P to show the shield effects + */ +simulated function NotifyInitialState(bool bFolded) +{ + local KFPawn KFP; + + if (WorldInfo.NetMode != NM_Client) + { + `Log("NotifyInitialState: " $bFolded); + + KFP = KFPawn(Instigator); + KFP.OnWeaponSpecialAction(bFolded ? 0 : 1); + } +} + +reliable server function NotifyServerMode(bool bFolded) +{ + bIsFolded = bFolded; +} + +defaultproperties +{ + // Zooming/Position + PlayerViewOffset=(X=10,Y=0,Z=-10) // (X=2,Y=0,Z=0) + PlayerViewOffsetUnfolded=(X=12,Y=0,Z=-7) + + // Content + PackageKey="Scythe" + FirstPersonMeshName="WEP_1P_Scythe_MESH.Wep_1stP_Scythe_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_Scythe_ANIM.Wep_1st_Scythe_Anim" + FirstPersonAnimTree="WEP_1P_Scythe_ANIM.1P_Scythe_Animtree" + PickupMeshName="wep_3p_scythe_mesh.Wep_3rdP_Scythe_Pickup" + AttachmentArchetypeName="WEP_Scythe_ARCH.Wep_Scythe_3P" + + // Short Range Mode Params + MaxHitRangeFolded=220 + FoldedDamage = 60 + FoldedDamageAlt = 87 + FoldedDT=class'KFDT_Slashing_ScytheShort' + FoldedDTAlt=class'KFDT_Slashing_ScytheShortAlt' + HitboxChainFolded = {( + (BoneOffset=(X=+3,Z=190)), + (BoneOffset=(X=-3,Z=170)), + (BoneOffset=(X=+3,Z=150)), + (BoneOffset=(X=-3,Z=130)), + (BoneOffset=(X=+3,Z=110)), + (BoneOffset=(X=-3,Z=90)), + (BoneOffset=(X=+3,Z=70)), + (BoneOffset=(X=-3,Z=50)), + (BoneOffset=(X=+3,Z=30)), + (BoneOffset=(Z=10)) + )} + + // Long Range Mode Params + MaxHitRangeUnfolded=300 + UnfoldedDamage=90 + UnfoldedDamageAlt=150 + UnfoldedDT=class'KFDT_Slashing_ScytheLong' + UnfoldedDTAlt=class'KFDT_Slashing_ScytheLongAlt' + HitboxChainUnfolded = {( + (BoneOffset=(X=-3,Z=290)), + (BoneOffset=(X=-3,Z=270)), + (BoneOffset=(X=-3,Z=250)), + (BoneOffset=(X=+3,Z=230)), + (BoneOffset=(X=-3,Z=210)), + (BoneOffset=(X=+3,Z=190)), + (BoneOffset=(X=-3,Z=170)), + (BoneOffset=(X=+3,Z=150)), + (BoneOffset=(X=-3,Z=130)), + (BoneOffset=(X=+3,Z=110)), + (BoneOffset=(X=-3,Z=90)), + (BoneOffset=(X=+3,Z=70)), + (BoneOffset=(X=-3,Z=50)), + (BoneOffset=(X=+3,Z=30)), + (BoneOffset=(Z=10)) + )} + + Begin Object Name=MeleeHelper_0 + WorldImpactEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Bladed_melee_impact' + MeleeImpactCamShakeScale=0.04f //0.5 + // modified combo sequences + ChainSequence_F=(DIR_ForwardRight, DIR_ForwardLeft, DIR_ForwardRight, DIR_ForwardLeft) + ChainSequence_B=(DIR_BackwardLeft, DIR_BackwardRight, DIR_BackwardLeft, DIR_ForwardRight, DIR_Left, DIR_Right, DIR_Left) + ChainSequence_L=(DIR_Right, DIR_Left) + ChainSequence_R=(DIR_Left, DIR_Right, DIR_ForwardLeft, DIR_ForwardRight, DIR_Left, DIR_Right) + End Object + + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Slashing_ScytheShort' + InstantHitMomentum(DEFAULT_FIREMODE)=30000.f + + InstantHitDamageTypes(HEAVY_ATK_FIREMODE)=class'KFDT_Slashing_ScytheShortAlt' + FiringStatesArray(HEAVY_ATK_FIREMODE)=MeleeHeavyAttacking + InstantHitMomentum(HEAVY_ATK_FIREMODE)=30000.f + + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Piercing_ScytheStab' + InstantHitMomentum(BASH_FIREMODE)=100000.f + InstantHitDamage(BASH_FIREMODE)=50 + + // Inventory + GroupPriority=50 + InventorySize=7 + WeaponSelectTexture=Texture2D'WEP_UI_Scythe_TEX.UI_WeaponSelect_Scythe' + + + AssociatedPerkClasses(0)=class'KFPerk_Berserker' + + // Block Sounds + BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Katana' + ParrySound=AkEvent'WW_WEP_Bullet_Impacts.Play_Parry_Metal' + + ParryDamageMitigationPercent=0.4 + BlockDamageMitigation=0.5 + ParryStrength=5 + + bIsFolded=true + + BonesToLockOnEmpty.Empty() + + UnfoldBlendingDuration=0.05f + + FoldedAttackAnimRate=0.65f + + // IdleFidgetAnims=(Guncheck_v1, Guncheck_v2, Guncheck_v3) + + // Weapon Upgrade stat boosts + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + + DistortTrailParticle = none + WhiteTrailParticle = none + BlueTrailParticle = none + RedTrailParticle = none + + FoldedTrailParticleSystem=ParticleSystem'WEP_Scythe_EMIT.FX_Scythe_Custom_R_01' + UnfoldedTrailParticleSystem=ParticleSystem'WEP_Scythe_EMIT.FX_Scythe_Custom_Unfold_01'; + + BloodParticles = ParticleSystem'WEP_1P_KATANA_EMIT.FX_katana_blood_flick_01' +} diff --git a/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc b/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc index b16e1e6..afa003a 100644 --- a/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc +++ b/KFGameContent/Classes/KFWeap_HRG_CranialPopper.uc @@ -366,8 +366,8 @@ defaultproperties //WeaponUpgrades[1]=(IncrementDamage=1.4f,IncrementWeight=1, IncrementHealFullRecharge=.8) //WeaponUpgrades[2]=(IncrementDamage=1.8f,IncrementWeight=2, IncrementHealFullRecharge=.6) - WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Weight, Add=1), (Stat=EWUS_HealFullRecharge, Scale=0.9f))) - WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Weight, Add=2), (Stat=EWUS_HealFullRecharge, Scale=0.8f))) + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) // From original KFWeap_RifleBase base class AimCorrectionSize=40.f diff --git a/KFGameContent/Classes/KFWeap_HRG_Dragonbreath.uc b/KFGameContent/Classes/KFWeap_HRG_Dragonbreath.uc new file mode 100644 index 0000000..5b51225 --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_Dragonbreath.uc @@ -0,0 +1,286 @@ +//============================================================================= +// KFWeap_HRG_Dragonbreath +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +//============================================================================= +class KFWeap_HRG_Dragonbreath extends KFWeap_ShotgunBase; + +/** How much to scale recoil when firing in double barrel fire. */ +var(Recoil) float QuadFireRecoilModifier; + +/** Shoot animation to play when shooting both barrels from the hip */ +var(Animations) const editconst name FireQuadAnim; + +/** How much momentum to apply when fired in double barrel */ +var(Recoil) float DoubleBarrelKickMomentum; + +/** How much to reduce shoot momentum when falling */ +var(Recoil) float FallingMomentumReduction; + +var(Spread) const float SpreadWidthDegrees; +var(Spread) const float SpreadWidthDegreesAlt; + +var transient float StartingPelletPosition; +var transient float StartingPelletPositionAlt; + +var const float BarrelHeatPerProjectile; +var const float MaxBarrelHeat; +var const float BarrelCooldownRate; +var transient float CurrentBarrelHeat; +var transient float LastBarrelHeat; + +simulated event PreBeginPlay() +{ + Super.PreBeginPlay(); + + Spread[DEFAULT_FIREMODE] = SpreadWidthDegrees * DegToRad / NumPellets[DEFAULT_FIREMODE]; + Spread[ALTFIRE_FIREMODE] = SpreadWidthDegreesAlt * DegToRad / NumPellets[ALTFIRE_FIREMODE]; + + StartingPelletPosition = -SpreadWidthDegrees * DegToRad / 2.0f; + StartingPelletPositionAlt = -SpreadWidthDegreesAlt * DegToRad / 2.0f; +} + +simulated event PostBeginPlay() +{ + Super.PostBeginPlay(); + + // Force start with "Glow_Intensity" of 0.0f + LastBarrelHeat = MaxBarrelHeat; + ChangeBarrelMaterial(); +} + +simulated function AltFireMode() +{ + if ( !Instigator.IsLocallyControlled() ) + { + return; + } + + StartFire(ALTFIRE_FIREMODE); +} + +/** Returns number of projectiles to fire from SpawnProjectile */ +simulated function byte GetNumProjectilesToFire(byte FireModeNum) +{ + return NumPellets[CurrentFireMode]; +} + +/** Handle one-hand fire anims */ +simulated function name GetWeaponFireAnim(byte FireModeNum) +{ + if (bUsingSights) + { + return FireSightedAnims[FireModeNum]; + } + else + { + if (FireModeNum == ALTFIRE_FIREMODE) + { + return FireQuadAnim; + } + else + { + return FireAnim; + } + } +} + +simulated function KFProjectile SpawnAllProjectiles(class KFProjClass, vector RealStartLoc, vector AimDir) +{ + local int ProjectilesToFire, i; + local float InitialOffset; + + ProjectilesToFire = GetNumProjectilesToFire(CurrentFireMode); + if (CurrentFireMode == GRENADE_FIREMODE || ProjectilesToFire <= 1) + { + return SpawnProjectile(KFProjClass, RealStartLoc, AimDir); + } + + InitialOffset = CurrentFireMode == DEFAULT_FIREMODE ? StartingPelletPosition : StartingPelletPositionAlt; + + for (i = 0; i < ProjectilesToFire; i++) + { + SpawnProjectile(KFProjClass, RealStartLoc, CalculateSpread(InitialOffset, Spread[CurrentFireMode], i, CurrentFireMode == ALTFIRE_FIREMODE)); + CurrentBarrelHeat = fmin(CurrentBarrelHeat + BarrelHeatPerProjectile, MaxBarrelHeat); + } + + ChangeBarrelMaterial(); + return None; +} + +simulated function vector CalculateSpread(float InitialOffset, float CurrentSpread, byte PelletNum, bool bHorizontal) +{ + local Vector X, Y, Z, POVLoc; + local Quat R; + local rotator POVRot; + + if (Instigator != None && Instigator.Controller != none) + { + Instigator.Controller.GetPlayerViewPoint(POVLoc, POVRot); + } + + GetAxes(POVRot, X, Y, Z); + + R = QuatFromAxisAndAngle(bHorizontal ? Z : Y, InitialOffset + CurrentSpread * PelletNum); + return QuatRotateVector(R, vector(POVRot)); +} + +simulated function ChangeBarrelMaterial() +{ + local int i; + + if( CurrentBarrelHeat != LastBarrelHeat ) + { + for( i = 0; i < WeaponMICs.Length; ++i ) + { + if( WeaponMICs[i] != none ) + { + WeaponMICs[i].SetScalarParameterValue('Barrel_intensity', CurrentBarrelHeat); + LastBarrelHeat = CurrentBarrelHeat; + } + } + } +} + +simulated function Tick(float Delta) +{ + Super.Tick(Delta); + + CurrentBarrelHeat = fmax(CurrentBarrelHeat - BarrelCooldownRate * Delta, 0.0f); + ChangeBarrelMaterial(); +} + +defaultproperties +{ + // Inventory + InventorySize=7 + GroupPriority=110 + WeaponSelectTexture=Texture2D'WEP_UI_Quad_Barrel_TEX.UI_WeaponSelect_QuadBarrel' + + // FOV + MeshFOV=60 + MeshIronSightFOV=52 + PlayerIronSightFOV=70 + + // Depth of field + DOF_FG_FocalRadius=65 + DOF_FG_MaxNearBlurSize=3 + + // Zooming/Position + PlayerViewOffset=(X=15.0,Y=8.0,Z=-4.5) + IronSightPosition=(X=3,Y=0,Z=0) + + // Content + PackageKey="HRG_MegaDragonsbreath" + FirstPersonMeshName="wep_1p_hrg_megadragonsbreath_mesh.Wep_1stP_HRG_MegaDragonsbreath_Rig" + FirstPersonAnimSetNames(0)="wep_1p_hrg_megadragonsbreath_anim.Wep_1stP_HRG_MegaDragonsbreath_Anim" + PickupMeshName="wep_3p_hrg_megadragonsbreath_mesh.Wep_3rdP_HRG_MegaDragonsbreath_Pickup" + AttachmentArchetypeName="wep_hrg_megadragonsbreath_arch.Wep_HRG_MegaDragonsbreath_3P" + MuzzleFlashTemplateName="wep_hrg_megadragonsbreath_arch.Wep_HRG_MegaDragonsbreath_MuzzleFlash" + + // Animations + FireAnim=Shoot_Single + FireQuadAnim=Shoot_Double + FireSightedAnims[0]=Shoot_Iron_Single + FireSightedAnims[1]=Shoot_Iron_Double + bHasFireLastAnims=false + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunSingle' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Pellet_HRG_Dragonbreath' + InstantHitDamage(DEFAULT_FIREMODE)=28.0 //36 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_Dragonbreath' + PenetrationPower(DEFAULT_FIREMODE)=4.0 + FireInterval(DEFAULT_FIREMODE)=0.25 // 240 RPM + FireOffset=(X=25,Y=3.5,Z=-4) + NumPellets(DEFAULT_FIREMODE)=8 + Spread(DEFAULT_FIREMODE)=0 + ForceReloadTimeOnEmpty=0.3 + + // ALT_FIREMODE + FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunAuto' + FiringStatesArray(ALTFIRE_FIREMODE)= WeaponSingleFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Pellet_HRG_Dragonbreath' + InstantHitDamage(ALTFIRE_FIREMODE)=28.0 + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_Dragonbreath' + PenetrationPower(ALTFIRE_FIREMODE)=4.0 + FireInterval(ALTFIRE_FIREMODE)=0.25 // 240 RPM + NumPellets(ALTFIRE_FIREMODE)=8 + Spread(ALTFIRE_FIREMODE)=0 + AmmoCost(ALTFIRE_FIREMODE)=1 + DoubleBarrelKickMomentum=1000 + FallingMomentumReduction=0.5 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Dragonbreath' + InstantHitDamage(BASH_FIREMODE)=27 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_Fire_3P_Single', FirstPersonCue=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_Fire_1P_Single') //@TODO: Replace + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_Fire_3P_AltFire', FirstPersonCue=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_Fire_1P_AltFire') //@TODO: Replace + + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_DryFire' //@TODO: Replace + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_Quad_Shotgun.Play_Quad_Shotgun_DryFire' //@TODO: Replace + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + // Ammo + MagazineCapacity[0]=4 + SpareAmmoCapacity[0]=48 //72 + InitialSpareMags[0]=3 //8 + AmmoPickupScale[0]=2.0 //3.0 + bCanBeReloaded=true + bReloadFromMagazine=true + bNoMagazine=true + + // Recoil + maxRecoilPitch=1200 //900 + minRecoilPitch=775 + maxRecoilYaw=800 //500 + minRecoilYaw=-500 + RecoilRate=0.085 + RecoilBlendOutRatio=1.1 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=1500 + RecoilMinPitchLimit=64785 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=500 + RecoilISMinPitchLimit=65485 + RecoilViewRotationScale=0.8 + FallingRecoilModifier=1.5 + HippedRecoilModifier=1.1 //1.25 + QuadFireRecoilModifier=2.0 + + AssociatedPerkClasses(0)=class'KFPerk_Firebug' + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot' + + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.15f,IncrementWeight=1) + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + + SpreadWidthDegrees=7.0f + SpreadWidthDegreesAlt=15.0f + + StartingPelletPosition=0.0f + StartingPelletPositionAlt=0.0f + + MaxBarrelHeat=5.0f + BarrelHeatPerProjectile=0.089f + BarrelCooldownRate=0.45f + + CurrentBarrelHeat=0.0f + LastBarrelHeat=0.0f + + NumBloodMapMaterials=3 +} diff --git a/KFGameContent/Classes/KFWeap_HRG_EMP_ArcGenerator.uc b/KFGameContent/Classes/KFWeap_HRG_EMP_ArcGenerator.uc index b00358c..8735661 100644 --- a/KFGameContent/Classes/KFWeap_HRG_EMP_ArcGenerator.uc +++ b/KFGameContent/Classes/KFWeap_HRG_EMP_ArcGenerator.uc @@ -746,7 +746,7 @@ defaultproperties PlayerIronSightFOV=80 // Zooming/Position - IronSightPosition=(X=3,Y=0,Z=0) + IronSightPosition=(X=3,Y=-0.032,Z=-0.03) PlayerViewOffset=(X=5.0,Y=9,Z=-3) // Depth of field diff --git a/KFGameContent/Classes/KFWeap_HRG_Locust.uc b/KFGameContent/Classes/KFWeap_HRG_Locust.uc new file mode 100644 index 0000000..26391ae --- /dev/null +++ b/KFGameContent/Classes/KFWeap_HRG_Locust.uc @@ -0,0 +1,684 @@ +//============================================================================= +// KFWeap_HRG_Locust +//============================================================================= +// A mosquito rocket launcher +//============================================================================= +// Killing Floor 2 +// Copyright (C) 2022 Tripwire Interactive LLC +// +//============================================================================= +class KFWeap_HRG_Locust extends KFWeap_GrenadeLauncher_Base; + +const MAX_LOCKED_TARGETS = 6; + +/** Constains all currently locked-on targets */ +var protected array LockedTargets; + +/** How much to scale recoil when firing in multi-rocket mode */ +var float BurstFireRecoilModifier; + +/** The last time a target was acquired */ +var protected float LastTargetLockTime; + +/** The last time a target validation check was performed */ +var protected float LastTargetValidationCheckTime; + +/** How much time after a lock on occurs before another is allowed */ +var const float TimeBetweenLockOns; + +/** How much time should pass between target validation checks */ +var const float TargetValidationCheckInterval; + +/** Minimum distance a target can be from the crosshair to be considered for lock on */ +var const float MinTargetDistFromCrosshairSQ; + +/** Dot product FOV that targets need to stay within to maintain a target lock */ +var const float MaxLockMaintainFOVDotThreshold; + +/** Sound Effects to play when Locking */ +var AkBaseSoundObject LockAcquiredSoundFirstPerson; +var AkBaseSoundObject LockLostSoundFirstPerson; + +/** Icon textures for lock on drawing */ +var const Texture2D LockedOnIcon; +var LinearColor LockedIconColor; + +/** Ironsights Audio */ +var AkComponent IronsightsComponent; +var AkEvent IronsightsZoomInSound; +var AkEvent IronsightsZoomOutSound; + +/** Reduction for the amount of damage dealt to the weapon owner (including damage by the explosion) */ +var() float SelfDamageReductionValue; + +/** +* Toggle between DEFAULT and ALTFIRE +*/ +simulated function AltFireMode() +{ + super.AltFireMode(); + + LockedTargets.Length = 0; +} + +/********************************************************************************************* + * @name Target Locking And Validation + **********************************************************************************************/ + +/** We need to update our locked targets every frame and make sure they're within view and not dead */ +simulated event Tick( float DeltaTime ) +{ + local Pawn RecentlyLocked, StaticLockedTargets[6]; + local bool bUpdateServerTargets; + local int i; + + super.Tick( DeltaTime ); + + if( bUsingSights && bUseAltFireMode + && Instigator != none + && Instigator.IsLocallyControlled() ) + { + if( `TimeSince(LastTargetLockTime) > TimeBetweenLockOns + && LockedTargets.Length < AmmoCount[GetAmmoType(0)] + && LockedTargets.Length < MAX_LOCKED_TARGETS) + { + bUpdateServerTargets = FindTargets( RecentlyLocked ); + } + + if( LockedTargets.Length > 0 ) + { + bUpdateServerTargets = bUpdateServerTargets || ValidateTargets( RecentlyLocked ); + } + + // If we are a client, synchronize our targets with the server + if( bUpdateServerTargets && Role < ROLE_Authority ) + { + for( i = 0; i < MAX_LOCKED_TARGETS; ++i ) + { + if( i < LockedTargets.Length ) + { + StaticLockedTargets[i] = LockedTargets[i]; + } + else + { + StaticLockedTargets[i] = none; + } + } + + ServerSyncLockedTargets( StaticLockedTargets ); + } + } +} + + +/** +* Given an potential target TA determine if we can lock on to it. By default only allow locking on +* to pawns. +*/ +simulated function bool CanLockOnTo(Actor TA) +{ + Local KFPawn PawnTarget; + + PawnTarget = KFPawn(TA); + + // Make sure the pawn is legit, isn't dead, and isn't already at full health + if ((TA == None) || !TA.bProjTarget || TA.bDeleteMe || (PawnTarget == None) || + (TA == Instigator) || (PawnTarget.Health <= 0) || + !HasAmmo(DEFAULT_FIREMODE)) + { + return false; + } + + // Make sure and only lock onto players on the same team + return !WorldInfo.GRI.OnSameTeam(Instigator, TA); +} + +/** Finds a new lock on target, adds it to the target array and returns TRUE if the array was updated */ +simulated function bool FindTargets( out Pawn RecentlyLocked ) +{ + local Pawn P, BestTargetLock; + local byte TeamNum; + local vector AimStart, AimDir, TargetLoc, Projection, DirToPawn, LinePoint; + local Actor HitActor; + local float PointDistSQ, Score, BestScore, TargetSizeSQ; + + TeamNum = Instigator.GetTeamNum(); + AimStart = GetSafeStartTraceLocation(); + AimDir = vector( GetAdjustedAim(AimStart) ); + BestScore = 0.f; + + //Don't add targets if we're already burst firing + if (IsInState('WeaponBurstFiring')) + { + return false; + } + + foreach WorldInfo.AllPawns( class'Pawn', P ) + { + if (!CanLockOnTo(P)) + { + continue; + } + // Want alive pawns and ones we already don't have locked + if( P != none && P.IsAliveAndWell() && P.GetTeamNum() != TeamNum && LockedTargets.Find(P) == INDEX_NONE ) + { + TargetLoc = GetLockedTargetLoc( P ); + Projection = TargetLoc - AimStart; + DirToPawn = Normal( Projection ); + + // Filter out pawns too far from center + if( AimDir dot DirToPawn < 0.5f ) + { + continue; + } + + // Check to make sure target isn't too far from center + PointDistToLine( TargetLoc, AimDir, AimStart, LinePoint ); + PointDistSQ = VSizeSQ( LinePoint - P.Location ); + + TargetSizeSQ = P.GetCollisionRadius() * 2.f; + TargetSizeSQ *= TargetSizeSQ; + + if( PointDistSQ > (TargetSizeSQ + MinTargetDistFromCrosshairSQ) ) + { + continue; + } + + // Make sure it's not obstructed + HitActor = class'KFAIController'.static.ActorBlockTest(self, TargetLoc, AimStart,, true, true); + if( HitActor != none && HitActor != P ) + { + continue; + } + + // Distance from target has much more impact on target selection score + Score = VSizeSQ( Projection ) + PointDistSQ; + if( BestScore == 0.f || Score < BestScore ) + { + BestTargetLock = P; + BestScore = Score; + } + } + } + + if( BestTargetLock != none ) + { + LastTargetLockTime = WorldInfo.TimeSeconds; + LockedTargets.AddItem( BestTargetLock ); + RecentlyLocked = BestTargetLock; + + // Plays sound/FX when locking on to a new target + PlayTargetLockOnEffects(); + + return true; + } + + RecentlyLocked = none; + + return false; +} + +/** Checks to ensure all of our current locked targets are valid */ +simulated function bool ValidateTargets( optional Pawn RecentlyLocked ) +{ + local int i; + local bool bShouldRemoveTarget, bAlteredTargets; + local vector AimStart, AimDir, TargetLoc; + local Actor HitActor; + + if( `TimeSince(LastTargetValidationCheckTime) < TargetValidationCheckInterval ) + { + return false; + } + + LastTargetValidationCheckTime = WorldInfo.TimeSeconds; + + AimStart = GetSafeStartTraceLocation(); + AimDir = vector( GetAdjustedAim(AimStart) ); + + bAlteredTargets = false; + for( i = 0; i < LockedTargets.Length; ++i ) + { + // For speed don't bother checking a target we just locked + if( RecentlyLocked != none && RecentlyLocked == LockedTargets[i] ) + { + continue; + } + + bShouldRemoveTarget = false; + + if( LockedTargets[i] == none + || !LockedTargets[i].IsAliveAndWell() ) + { + bShouldRemoveTarget = true; + } + else + { + TargetLoc = GetLockedTargetLoc( LockedTargets[i] ); + if( AimDir dot Normal(LockedTargets[i].Location - AimStart) >= MaxLockMaintainFOVDotThreshold ) + { + HitActor = class'KFAIController'.static.ActorBlockTest( self, TargetLoc, AimStart,, true, true ); + if( HitActor != none && HitActor != LockedTargets[i] ) + { + bShouldRemoveTarget = true; + } + } + else + { + bShouldRemoveTarget = true; + } + } + + // A target was invalidated, remove it from the list + if( bShouldRemoveTarget ) + { + LockedTargets.Remove( i, 1 ); + --i; + bAlteredTargets = true; + continue; + } + } + + // Plays sound/FX when losing a target lock, but only if we didn't play a lock on this frame + if( bAlteredTargets && RecentlyLocked == none ) + { + PlayTargetLostEffects(); + } + + return bAlteredTargets; +} + +/** Synchronizes our locked targets with the server */ +reliable server function ServerSyncLockedTargets( Pawn TargetPawns[MAX_LOCKED_TARGETS] ) +{ + local int i; + + LockedTargets.Length = 0; + for( i = 0; i < MAX_LOCKED_TARGETS; ++i ) + { + if (TargetPawns[i] != none) + { + LockedTargets.AddItem(TargetPawns[i]); + } + } +} + +/** Adjusts our destination target impact location */ +static simulated function vector GetLockedTargetLoc( Pawn P ) +{ + // Go for the chest, but just in case we don't have something with a chest bone we'll use collision and eyeheight settings + if( P.Mesh.SkeletalMesh != none && P.Mesh.bAnimTreeInitialised ) + { + if( P.Mesh.MatchRefBone('Spine2') != INDEX_NONE ) + { + return P.Mesh.GetBoneLocation( 'Spine2' ); + } + else if( P.Mesh.MatchRefBone('Spine1') != INDEX_NONE ) + { + return P.Mesh.GetBoneLocation( 'Spine1' ); + } + + return P.Mesh.GetPosition() + ((P.CylinderComponent.CollisionHeight + (P.BaseEyeHeight * 0.5f)) * vect(0,0,1)) ; + } + + // General chest area, fallback + return P.Location + ( vect(0,0,1) * P.BaseEyeHeight * 0.75f ); +} + +simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo) +{ + super.ZoomIn(bAnimateTransition, ZoomTimeToGo); + + if (IronsightsZoomInSound != none && Instigator != none && Instigator.IsLocallyControlled()) + { + IronsightsComponent.PlayEvent(IronsightsZoomInSound, false); + } +} + +/** Clear all locked targets when zooming out, both server and client */ +simulated function ZoomOut( bool bAnimateTransition, float ZoomTimeToGo ) +{ + super.ZoomOut( bAnimateTransition, ZoomTimeToGo ); + + if (IronsightsZoomOutSound != none && Instigator != none && Instigator.IsLocallyControlled()) + { + IronsightsComponent.PlayEvent(IronsightsZoomOutSound, false); + } + + // Play a target lost effect if we're clearing targets on the way out + if( Instigator.IsLocallyControlled() && LockedTargets.Length > 0 ) + { + PlayTargetLostEffects(); + } + LockedTargets.Length = 0; +} + +/** Play FX or sounds when locking on to a new target */ +simulated function PlayTargetLockOnEffects() +{ + if( Instigator != none && Instigator.IsHumanControlled() ) + { + PlaySoundBase( LockAcquiredSoundFirstPerson, true ); + } +} + +/** Play FX or sounds when losing a target lock */ +simulated function PlayTargetLostEffects() +{ + if( Instigator != none && Instigator.IsHumanControlled() ) + { + PlaySoundBase( LockLostSoundFirstPerson, true ); + } +} + +/********************************************************************************************* + * @name Projectile Spawning + **********************************************************************************************/ + +/** Spawn projectile is called once for each rocket fired. In burst mode it will cycle through targets until it runs out */ +simulated function KFProjectile SpawnProjectile( class KFProjClass, vector RealStartLoc, vector AimDir ) +{ + local KFProj_HRG_Locust LocustProj; + + if( CurrentFireMode == GRENADE_FIREMODE ) + { + return super.SpawnProjectile( KFProjClass, RealStartLoc, AimDir ); + } + + // We need to set our target if we are firing from a locked on position + if( bUsingSights + && CurrentFireMode == ALTFIRE_FIREMODE + && LockedTargets.Length > 0 ) + { + // We'll aim our rocket at a target here otherwise we will spawn a dumbfire rocket at the end of the function + if( LockedTargets.Length > 0 ) + { + // Spawn our projectile and set its target + LocustProj = KFProj_HRG_Locust( super.SpawnProjectile(KFProjClass, RealStartLoc, AimDir) ); + if( LocustProj != none ) + { + //Seek to new target, then remove from list. Always use first target in the list for new fire. + LocustProj.SetLockedTarget( KFPawn(LockedTargets[0]) ); + LockedTargets.Remove(0, 1); + + return LocustProj; + } + } + + return None; + } + + return super.SpawnProjectile( KFProjClass, RealStartLoc, AimDir ); +} + +/********************************************************************************************* + * @name Targeting HUD -- Partially adapted from KFWeap_Rifle_RailGun + **********************************************************************************************/ + +/** Handle drawing our custom lock on HUD */ +simulated function DrawHUD( HUD H, Canvas C ) +{ + local int i; + + if( !bUsingSights || LockedTargets.Length == 0 ) + { + return; + } + + // Draw target locked icons + C.EnableStencilTest( true ); + for( i = 0; i < LockedTargets.Length; ++i ) + { + if( LockedTargets[i] != none ) + { + DrawTargetingIcon( C, i ); + } + } + C.EnableStencilTest( false ); +} + +/** Draws a targeting icon for each one of our locked targets */ +simulated function DrawTargetingIcon( Canvas Canvas, int Index ) +{ + local vector WorldPos, ScreenPos; + local float IconSize, IconScale; + + // Project world pos to canvas + WorldPos = GetLockedTargetLoc( LockedTargets[Index] ); + ScreenPos = Canvas.Project( WorldPos );//WorldToCanvas(Canvas, WorldPos); + + // calculate scale based on resolution and distance + IconScale = fMin( float(Canvas.SizeX) / 1024.f, 1.f ); + // Scale down up to 40 meters away, with a clamp at 20% size + IconScale *= fClamp( 1.f - VSize(WorldPos - Instigator.Location) / 4000.f, 0.2f, 1.f ); + + // Apply size scale + IconSize = 200.f * IconScale; + ScreenPos.X -= IconSize / 2.f; + ScreenPos.Y -= IconSize / 2.f; + + // Off-screen check + if( ScreenPos.X < 0 || ScreenPos.X > Canvas.SizeX || ScreenPos.Y < 0 || ScreenPos.Y > Canvas.SizeY ) + { + return; + } + + Canvas.SetPos( ScreenPos.X, ScreenPos.Y ); + + // Draw the icon + Canvas.DrawTile( LockedOnIcon, IconSize, IconSize, 0, 0, LockedOnIcon.SizeX, LockedOnIcon.SizeY, LockedIconColor ); +} + +/********************************************************************************************* + * State WeaponSingleFiring + * Fire must be released between every shot. + *********************************************************************************************/ + +simulated state WeaponSingleFiring +{ + simulated function BeginState( Name PrevStateName ) + { + LockedTargets.Length = 0; + + super.BeginState( PrevStateName ); + } +} + +/********************************************************************************************* + * State WeaponBurstFiring + * Fires a burst of bullets. Fire must be released between every shot. + *********************************************************************************************/ + +simulated state WeaponBurstFiring +{ + simulated function int GetBurstAmount() + { + // Clamp our bursts to either the number of targets or how much ammo we have remaining + return Clamp( LockedTargets.Length, 1, AmmoCount[GetAmmoType(CurrentFireMode)] ); + } + + /** Overridden to apply scaled recoil when in multi-rocket mode */ + simulated function ModifyRecoil( out float CurrentRecoilModifier ) + { + super.ModifyRecoil( CurrentRecoilModifier ); + + CurrentRecoilModifier *= BurstFireRecoilModifier; + } + + simulated function bool ShouldRefire() + { + return LockedTargets.Length > 0; + } + + simulated function FireAmmunition() + { + super.FireAmmunition(); + if (Role < ROLE_Authority) + { + LockedTargets.Remove(0, 1); + } + } + + simulated event EndState( Name NextStateName ) + { + LockedTargets.Length = 0; + + super.EndState( NextStateName ); + } +} + +/** + Reduce the damage received and apply it to the shield + */ +function AdjustDamage(out int InDamage, class DamageType, Actor DamageCauser) +{ + super.AdjustDamage(InDamage, DamageType, DamageCauser); + + if (Instigator != none && DamageCauser.Instigator == Instigator) + { + InDamage *= SelfDamageReductionValue; + } +} + +defaultproperties +{ + ForceReloadTime=0.4f + + // Inventory + InventoryGroup=IG_Primary + GroupPriority=100 + InventorySize=6 + WeaponSelectTexture=Texture2D'wep_ui_hrg_locust_tex.UI_WeaponSelect_HRG_Locust' + + // FOV + MeshFOV=86 + MeshIronSightFOV=65 + PlayerIronSightFOV=70 + PlayerSprintFOV=95 + + // Depth of field + DOF_FG_FocalRadius=50 + DOF_FG_MaxNearBlurSize=2.5 + + // Zooming/Position + PlayerViewOffset=(X=20.0,Y=5,Z=-5) + FastZoomOutTime=0.2 + + // Content + PackageKey="HRG_Locust" + FirstPersonMeshName="wep_1p_hrg_locust_mesh.Wep_1stP_HRG_Locust_Rig" + FirstPersonAnimSetNames(0)="WEP_1P_HRG_Locust_ANIM.Wep_1stP_HRG_Locust_Anim" + PickupMeshName="wep_3p_hrg_locust_mesh.Wep_3rdP_HRG_Locust_Pickup" + AttachmentArchetypeName="wep_hrg_locust_arch.Wep_HRG_Locust_3P" + MuzzleFlashTemplateName="wep_hrg_locust_arch.Wep_HRG_Locust_MuzzleFlash" + + // Target Locking + MinTargetDistFromCrosshairSQ=2500.0f // 0.5 meters + TimeBetweenLockOns=0.06f + TargetValidationCheckInterval=0.1f + MaxLockMaintainFOVDotThreshold=0.36f + + // LockOn Visuals + LockedOnIcon=Texture2D'Wep_Scope_TEX.Wep_1stP_Yellow_Red_Target' + LockedIconColor=(R=1.f, G=0.f, B=0.f, A=0.5f) + + // Lock On/Lost Sounds + LockAcquiredSoundFirstPerson=AkEvent'WW_WEP_SA_Railgun.Play_Railgun_Scope_Locked' + LockLostSoundFirstPerson=AkEvent'WW_WEP_SA_Railgun.Play_Railgun_Scope_Lost' + + // Zooming/Position + IronSightPosition=(X=0,Y=-0.065,Z=-0.31) + + // Ammo + MagazineCapacity[0]=6 + SpareAmmoCapacity[0]=84 + InitialSpareMags[0]=3 + AmmoPickupScale[0]=2.0 + bCanBeReloaded=true + bReloadFromMagazine=true + + // Recoil + maxRecoilPitch=400 + minRecoilPitch=275 + maxRecoilYaw=200 + minRecoilYaw=-200 + RecoilRate=0.085 + RecoilBlendOutRatio=0.35 + RecoilMaxYawLimit=500 + RecoilMinYawLimit=65035 + RecoilMaxPitchLimit=1500 + RecoilMinPitchLimit=64785 + RecoilISMaxYawLimit=50 + RecoilISMinYawLimit=65485 + RecoilISMaxPitchLimit=500 + RecoilISMinPitchLimit=65485 + RecoilViewRotationScale=0.8 + FallingRecoilModifier=1.5 + HippedRecoilModifier=1.25 + BurstFireRecoilModifier=0.15f // Reduce recoil between rockets when in burst mode + + // DEFAULT_FIREMODE + FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'UI_FireModes_TEX.UI_FireModeSelect_Rocket' + FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring + WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile + WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_HRG_Locust' + FireInterval(DEFAULT_FIREMODE)=+0.35 + InstantHitDamage(DEFAULT_FIREMODE)=1 //140 // 120.0 //100.00 + InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_Locust' + Spread(DEFAULT_FIREMODE)=0.025 + FireOffset=(X=20,Y=4.0,Z=-3) + + // ALT_FIREMODE + FireModeIconPaths(ALTFIRE_FIREMODE)= Texture2D'UI_SecondaryAmmo_TEX.UI_FireModeSelect_AutoTarget' + FiringStatesArray(ALTFIRE_FIREMODE)=WeaponBurstFiring + WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile + WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_HRG_Locust' + FireInterval(ALTFIRE_FIREMODE)=+0.3 //0.1 + InstantHitDamage(ALTFIRE_FIREMODE)=1 //140 // 120.0 //100.00 + InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_Locust' + Spread(ALTFIRE_FIREMODE)=0.025 + AmmoCost(ALTFIRE_FIREMODE)=1 + + // BASH_FIREMODE + InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Locust' + InstantHitDamage(BASH_FIREMODE)=29 + + // Fire Effects + WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Fire_1P') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_Seeker_6.Play_WEP_Seeker_6_Fire_1P') + WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_RPG7.Play_WEP_SA_RPG7_DryFire' + WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_RPG7.Play_WEP_SA_RPG7_DryFire' + + // Animation + bHasFireLastAnims=true + IdleFidgetAnims=(Guncheck_v1, Guncheck_v2) + + //BonesToLockOnEmpty=(RW_Grenade1) + + // Attachments + bHasIronSights=true + bHasFlashlight=false + + AssociatedPerkClasses(0)=class'KFPerk_Survivalist' + + WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot' + + // Audio + Begin Object Class=AkComponent name=IronsightsComponent0 + bForceOcclusionUpdateInterval=true + OcclusionUpdateInterval=0.f // never update occlusion for footsteps + bStopWhenOwnerDestroyed=true + End Object + IronsightsComponent=IronsightsComponent0 + Components.Add(IronsightsComponent0) + IronsightsZoomInSound=AkEvent'WW_WEP_Seeker_6.Play_Seeker_6_Iron_In' + IronsightsZoomOutSound=AkEvent'WW_WEP_Seeker_6.Play_Seeker_6_Iron_In_Out' + + // Weapon Upgrade stat boosts + //WeaponUpgrades[1]=(IncrementDamage=1.125f,IncrementWeight=1) + + WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) + WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Damage1, Scale=1.3f), (Stat=EWUS_Weight, Add=2))) + + SelfDamageReductionValue = 0f; +} diff --git a/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc b/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc index d5e202a..35db31e 100644 --- a/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc +++ b/KFGameContent/Classes/KFWeap_Mine_Reconstructor.uc @@ -842,8 +842,8 @@ defaultproperties ValueIncreaseTime=0.2 //FOR LERPING DAMANGE - MaxDamageByCharge=300 //250 //200 //120 - MinDamageByCharge=30 //25 //30 + MaxDamageByCharge=350 //300 //250 //200 //120 + MinDamageByCharge=35 //30 //25 //30 // FOV Meshfov=80 MeshIronSightFOV=65 //52 @@ -895,7 +895,7 @@ defaultproperties HippedRecoilModifier=1.5 // Inventory - InventorySize=8 + InventorySize=7 //8 GroupPriority=80 //75 WeaponSelectTexture=Texture2D'WEP_UI_Mine_Reconstructor_TEX.UI_WeaponSelect_HMTechMineReconstructor' //@TODO: Replace me diff --git a/KFGameContent/Classes/KFWeap_Pistol_Blunderbuss.uc b/KFGameContent/Classes/KFWeap_Pistol_Blunderbuss.uc index 4eca1cb..15b891f 100644 --- a/KFGameContent/Classes/KFWeap_Pistol_Blunderbuss.uc +++ b/KFGameContent/Classes/KFWeap_Pistol_Blunderbuss.uc @@ -492,7 +492,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=-15,Y=12,Z=-6) - IronSightPosition=(X=-3,Y=0,Z=0) + IronSightPosition=(X=-3,Y=0.145,Z=0) // Content PackageKey="Blunderbuss" diff --git a/KFGameContent/Classes/KFWeap_Pistol_G18C.uc b/KFGameContent/Classes/KFWeap_Pistol_G18C.uc index b74eeb0..4190163 100644 --- a/KFGameContent/Classes/KFWeap_Pistol_G18C.uc +++ b/KFGameContent/Classes/KFWeap_Pistol_G18C.uc @@ -121,7 +121,7 @@ defaultproperties // Zooming/Position [FFERRANDO NEEDS TO BE UPDATED TO G18] PlayerViewOffset=(X=-15,Y=12,Z=-6) - IronSightPosition=(X=0,Y=0,Z=0) //(X=-3,Y=-0.38,Z=-0.2) + IronSightPosition=(X=0,Y=-0.12,Z=-0.1) //(X=-3,Y=-0.38,Z=-0.2) // Content [FFERRANDO NEEDS TO BE UPDATED TO G18] PackageKey="G18C" diff --git a/KFGameContent/Classes/KFWeap_Rifle_Hemogoblin.uc b/KFGameContent/Classes/KFWeap_Rifle_Hemogoblin.uc index 250276d..475bde3 100644 --- a/KFGameContent/Classes/KFWeap_Rifle_Hemogoblin.uc +++ b/KFGameContent/Classes/KFWeap_Rifle_Hemogoblin.uc @@ -91,7 +91,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=20.0,Y=11.0,Z=-2) //(X=15.0,Y=11.5,Z=-4) - IronSightPosition=(X=30.0,Y=0,Z=0) + IronSightPosition=(X=30.0,Y=-0.035,Z=-0.07) // AI warning system bWarnAIWhenAiming=true diff --git a/KFGameContent/Classes/KFWeap_Rifle_M14EBR.uc b/KFGameContent/Classes/KFWeap_Rifle_M14EBR.uc index a16fb9a..e302c01 100644 --- a/KFGameContent/Classes/KFWeap_Rifle_M14EBR.uc +++ b/KFGameContent/Classes/KFWeap_Rifle_M14EBR.uc @@ -57,7 +57,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=15.0,Y=11.5,Z=-4) - IronSightPosition=(X=0.0,Y=0,Z=0) + IronSightPosition=(X=0.0,Y=-0.016,Z=-0.016) // AI warning system bWarnAIWhenAiming=true diff --git a/KFGameContent/Classes/KFWeap_Rifle_RailGun.uc b/KFGameContent/Classes/KFWeap_Rifle_RailGun.uc index 85c8d57..32a2cfe 100644 --- a/KFGameContent/Classes/KFWeap_Rifle_RailGun.uc +++ b/KFGameContent/Classes/KFWeap_Rifle_RailGun.uc @@ -509,7 +509,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=3.0,Y=7,Z=-2) - IronSightPosition=(X=-0.25,Y=0,Z=0) // any further back along X and the scope clips through the camera during firing + IronSightPosition=(X=-0.25,Y=0.005,Z=-0.005) // any further back along X and the scope clips through the camera during firing // AI warning system bWarnAIWhenAiming=true diff --git a/KFGameContent/Classes/KFWeap_RocketLauncher_Seeker6.uc b/KFGameContent/Classes/KFWeap_RocketLauncher_Seeker6.uc index 7e0d212..6491766 100644 --- a/KFGameContent/Classes/KFWeap_RocketLauncher_Seeker6.uc +++ b/KFGameContent/Classes/KFWeap_RocketLauncher_Seeker6.uc @@ -137,7 +137,7 @@ simulated function bool FindTargets( out Pawn RecentlyLocked ) local byte TeamNum; local vector AimStart, AimDir, TargetLoc, Projection, DirToPawn, LinePoint; local Actor HitActor; - local float PointDistSQ, Score, BestScore; + local float PointDistSQ, Score, BestScore, TargetSizeSQ; TeamNum = Instigator.GetTeamNum(); AimStart = GetSafeStartTraceLocation(); @@ -172,7 +172,11 @@ simulated function bool FindTargets( out Pawn RecentlyLocked ) // Check to make sure target isn't too far from center PointDistToLine( TargetLoc, AimDir, AimStart, LinePoint ); PointDistSQ = VSizeSQ( LinePoint - P.Location ); - if( PointDistSQ > MinTargetDistFromCrosshairSQ ) + + TargetSizeSQ = P.GetCollisionRadius() * 2.f; + TargetSizeSQ *= TargetSizeSQ; + + if( PointDistSQ > (TargetSizeSQ + MinTargetDistFromCrosshairSQ) ) { continue; } diff --git a/KFGameContent/Classes/KFWeap_SMG_Kriss.uc b/KFGameContent/Classes/KFWeap_SMG_Kriss.uc index 57bd7f7..84cfe44 100644 --- a/KFGameContent/Classes/KFWeap_SMG_Kriss.uc +++ b/KFGameContent/Classes/KFWeap_SMG_Kriss.uc @@ -23,7 +23,7 @@ defaultproperties PlayerIronSightFOV=75 // Zooming/Position - IronSightPosition=(X=15.f,Y=0.f,Z=0.0f) + IronSightPosition=(X=15.f,Y=0.03f,Z=0.1f) PlayerViewOffset=(X=20.f,Y=9.5f,Z=-3.0f) // Content diff --git a/KFGameContent/Classes/KFWeap_SMG_Medic.uc b/KFGameContent/Classes/KFWeap_SMG_Medic.uc index 0bf3660..f90a521 100644 --- a/KFGameContent/Classes/KFWeap_SMG_Medic.uc +++ b/KFGameContent/Classes/KFWeap_SMG_Medic.uc @@ -38,7 +38,7 @@ defaultproperties PlayerIronSightFOV=70 // Zooming/Position - IronSightPosition=(X=8,Y=0,Z=0) + IronSightPosition=(X=8,Y=0.01,Z=-0.04) PlayerViewOffset=(X=22,Y=10,Z=-4.5) // Content diff --git a/KFGameContent/Classes/KFWeap_Shotgun_Medic.uc b/KFGameContent/Classes/KFWeap_Shotgun_Medic.uc index d2d21ad..a3e7da1 100644 --- a/KFGameContent/Classes/KFWeap_Shotgun_Medic.uc +++ b/KFGameContent/Classes/KFWeap_Shotgun_Medic.uc @@ -72,7 +72,7 @@ defaultproperties // Zooming/Position PlayerViewOffset=(X=14.0,Y=6.5,Z=-3.5) - IronSightPosition=(X=12.0,Y=0,Z=0) + IronSightPosition=(X=12.0,Y=-0.03,Z=-0.07) // Content PackageKey="Medic_Shotgun"