1
0
This commit is contained in:
2022-11-28 00:49:25 +03:00
parent 2893490576
commit ea1d43f08f
106 changed files with 5944 additions and 303 deletions

View File

@ -0,0 +1,24 @@
//=============================================================================
// KFDT_Ballistic_HRG_MedicMissile
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Ballistic_HRG_MedicMissile extends KFDT_Ballistic_Shell
abstract
hidedropdown;
defaultproperties
{
KDamageImpulse=3000
KDeathUpKick=1000
KDeathVel=500
StumblePower=10
GunHitPower=45
ModifierPerkList(0)=class'KFPerk_FieldMedic'
WeaponDef=class'KFWeapDef_HRG_MedicMissile'
}

View File

@ -0,0 +1,25 @@
//=============================================================================
// KFDT_Ballistic_ZedMKIII_Rocket
//=============================================================================
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Ballistic_ZedMKIII_Rocket extends KFDT_Ballistic_Shell
abstract
hidedropdown;
defaultproperties
{
KDamageImpulse=3000
KDeathUpKick=1000
KDeathVel=500
KnockdownPower=50
StumblePower=100
GunHitPower=70
ModifierPerkList(0)=class'KFPerk_Demolitionist'
WeaponDef=class'KFWeapDef_ZedMKIII'
}

View File

@ -0,0 +1,16 @@
//=============================================================================
// KFDT_Bludgeon_HRG_BallisticBouncer
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Bludgeon_HRG_BallisticBouncer extends KFDT_Bludgeon_RifleButt
abstract
hidedropdown;
DefaultProperties
{
//defaults
WeaponDef=class'KFWeapDef_HRG_BallisticBouncer'
}

View File

@ -0,0 +1,26 @@
//=============================================================================
// KFDT_Bludgeon_HRG_BallisticBouncer_Shot
//=============================================================================
// Balls hit hard
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Bludgeon_HRG_BallisticBouncer_Shot extends KFDT_Bludgeon
abstract
hidedropdown;
defaultproperties
{
KDamageImpulse=900
KDeathUpKick=-300
KDeathVel=100
StumblePower=400
GunHitPower=300
MeleeHitPower=150
KnockdownPower=100
WeaponDef=class'KFWeapDef_HRG_BallisticBouncer'
}

View File

@ -0,0 +1,16 @@
//=============================================================================
// KFDT_Bludgeon_HRG_MedicMissile
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Bludgeon_HRG_MedicMissile extends KFDT_Bludgeon_RifleButt
abstract
hidedropdown;
DefaultProperties
{
//defaults
WeaponDef=class'KFWeapDef_HRG_MedicMissile'
}

View File

@ -0,0 +1,16 @@
//=============================================================================
// KFDT_Bludgeon_HVStormCannon
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Bludgeon_HVStormCannon extends KFDT_Bludgeon_RifleButt
abstract
hidedropdown;
DefaultProperties
{
//defaults
WeaponDef=class'KFWeapDef_HVStormCannon'
}

View File

@ -0,0 +1,16 @@
//=============================================================================
// KFDT_Bludgeon_ZedMKIII
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Bludgeon_ZedMKIII extends KFDT_Bludgeon_RifleButt
abstract
hidedropdown;
DefaultProperties
{
//defaults
WeaponDef=class'KFWeapDef_ZedMKIII'
}

View File

@ -0,0 +1,54 @@
//=============================================================================
// KFDT_EMP_HVStormCannon
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_EMP_HVStormCannon extends KFDT_EMP
abstract
hidedropdown;
var ParticleSystem ForceImpactEffect;
var AkEvent ForceImpactSound;
static function PlayImpactHitEffects( KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, optional Pawn HitInstigator )
{
local KFSkinTypeEffects SkinType;
if ( P.CharacterArch != None && default.EffectGroup < FXG_Max )
{
SkinType = P.GetHitZoneSkinTypeEffects( HitZoneIndex );
if (SkinType != none)
{
SkinType.PlayImpactParticleEffect(P, HitLocation, HitDirection, HitZoneIndex, default.EffectGroup, default.ForceImpactEffect);
SkinType.PlayTakeHitSound(P, HitLocation, HitInstigator, default.EffectGroup, default.ForceImpactSound);
}
}
}
defaultproperties
{
KDamageImpulse=2000
KDeathUpKick=400
KDeathVel=250
KnockdownPower=20
StunPower=50
StumblePower=200
GunHitPower=150
MeleeHitPower=100
EMPPower=0 // Don't use the affliction here, we manage this on KFWeap_HVStormCannon to completely synchronize it with the logic of the weapon
GoreDamageGroup=DGT_EMP
EffectGroup=FXG_Electricity
ForceImpactEffect=ParticleSystem'WEP_HVStormCannon_EMIT.FX_HVStormCannon_Impact'
ForceImpactSound=AkEvent'WW_WEP_HVStormCannon.Play_WEP_HVStormCannon_Impact'
WeaponDef=class'KFWeapDef_HVStormCannon'
//Perk
ModifierPerkList(0)=class'KFPerk_Sharpshooter'
}

View File

@ -0,0 +1,32 @@
//=============================================================================
// KFDT_Explosive_HRG_MedicMissile
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Explosive_HRG_MedicMissile extends KFDT_Explosive
abstract
hidedropdown;
defaultproperties
{
bShouldSpawnPersistentBlood=false
RadialDamageImpulse=2500
GibImpulseScale=0.15
KDeathUpKick=1500
KDeathVel=500
KnockdownPower=50
StumblePower=200
bExtraMomentumZ=false
bCanObliterate=false
//Perk
ModifierPerkList(0)=class'KFPerk_FieldMedic'
WeaponDef=class'KFWeapDef_HRG_MedicMissile'
}

View File

@ -0,0 +1,30 @@
//=============================================================================
// KFDT_Explosive_ZedMKIII
//=============================================================================
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Explosive_ZedMKIII extends KFDT_Explosive
abstract
hidedropdown;
defaultproperties
{
bShouldSpawnPersistentBlood=true
// physics impact
RadialDamageImpulse=10000
KDeathUpKick=2000
KDeathVel=500
KnockdownPower=150
StumblePower=350
//Perk
ModifierPerkList(0)=class'KFPerk_Demolitionist'
ObliterationHealthThreshold=-500
ObliterationDamageThreshold=500
WeaponDef=class'KFWeapDef_ZedMKIII'
}

View File

@ -0,0 +1,69 @@
//=============================================================================
// KFDT_Microwave_ZedMKIII
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFDT_Microwave_ZedMKIII extends KFDT_Microwave
abstract
hidedropdown;
var ParticleSystem ForceImpactEffect;
var AkEvent ForceImpactSound;
/** Allows the damage type to customize exactly which hit zones it can dismember */
static simulated function bool CanDismemberHitZone( name InHitZoneName )
{
if( super.CanDismemberHitZone( InHitZoneName ) )
{
return true;
}
switch ( InHitZoneName )
{
case 'lupperarm':
case 'rupperarm':
return true;
}
return false;
}
static function PlayImpactHitEffects( KFPawn P, vector HitLocation, vector HitDirection, byte HitZoneIndex, optional Pawn HitInstigator )
{
local KFSkinTypeEffects SkinType;
if ( P.CharacterArch != None && default.EffectGroup < FXG_Max )
{
SkinType = P.GetHitZoneSkinTypeEffects( HitZoneIndex );
if (SkinType != none)
{
SkinType.PlayImpactParticleEffect(P, HitLocation, HitDirection, HitZoneIndex, default.EffectGroup, default.ForceImpactEffect);
SkinType.PlayTakeHitSound(P, HitLocation, HitInstigator, default.EffectGroup, default.ForceImpactSound);
}
}
}
defaultproperties
{
KDamageImpulse=550
GibImpulseScale=0.85
KDeathUpKick=-200
KDeathVel=200
StumblePower=18
StunPower=15
GunHitPower=15
WeaponDef=class'KFWeapDef_ZedMKIII'
//Perk
ModifierPerkList(0)=class'KFPerk_Demolitionist'
EffectGroup=FXG_MicrowaveProj
ForceImpactEffect=ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Bullet_Impact'
ForceImpactSound=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Impact'
}

View File

@ -0,0 +1,283 @@
//=============================================================================
// KFExplosion_HRG_BallisticBouncer
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFExplosion_HRG_BallisticBouncer extends KFExplosionActorLingering;
var() class<KFDamageType> HealingDamageType;
var() float HealingAmount;
var AkEvent SmokeLoopStartEvent;
var AkEvent SmokeLoopStopEvent;
var KFPerk CachedInstigatorPerk;
var float fChargePercentage;
var float fMinAmmoutHealing;
var float fMaxAmmoutHealing;
var float fAltMinAmmoutHealing;
var float fAltMaxAmmoutHealing;
var bool bHealsInstigator;
var bool bHealsDifferentAmmoutToInstigator;
/* Only used if bOnlyDamagePawns is active to create fractures in walls */
var bool bAllowFractureDamage;
simulated function SpawnExplosionParticleSystem(ParticleSystem Template)
{
local ParticleSystemComponent PSC;
local vector vec;
// If the template is none, grab the default
if( !ExplosionTemplate.bAllowPerMaterialFX && Template == none )
{
Template = KFGameExplosion(ExplosionTemplate).ExplosionEffects.DefaultImpactEffect.ParticleTemplate;
}
// Use custom pool
PSC = WorldInfo.MyEmitterPool.SpawnEmitter(Template, Location, rotator(ExplosionTemplate.HitNormal), None);
//fChargePercentage
vec.X = fChargePercentage;
vec.Y = fChargePercentage;
vec.Z = fChargePercentage;
PSC.SetVectorParameter( name("BlobCharge"), vec);
PSC.SetFloatParameter( name("MineFxControlParam"), fChargePercentage);
}
/*
* @param Direction For bDirectionalExplosion=true explosions, this is the forward direction of the blast.
* Overridden to add the ability to spawn fragments from the explosion
**/
simulated function Explode(GameExplosion NewExplosionTemplate, optional vector Direction)
{
local KFPawn KFP;
super.Explode(NewExplosionTemplate, Direction);
if( Instigator != none )
{
KFP = KFPawn(Instigator);
if( KFP != none )
{
CachedInstigatorPerk = KFP.GetPerk();
}
}
if (Role == Role_Authority)
{
//DelayedExplosionDamage();
SetTimer(Interval, false, nameof(DelayedExplosionDamage), self);
}
}
/**
* Deal damage or heal players
*/
protected simulated function AffectsPawn(Pawn Victim, float DamageScale)
{
local KFPawn_Human HumanVictim;
local KFPawn_Monster MonsterVictim;
local KFProj_MedicGrenade OwnerProjectile;
local bool bCanRepairArmor;
local Box BBox;
local vector BBoxCenter;
local Actor HitActor;
local bool bDamageBlocked;
if( Victim != none && Victim.IsAliveAndWell() )
{
MonsterVictim = KFPawn_Monster(Victim);
if( MonsterVictim != none )
{
if( bWasFadedOut|| bDeleteMe || bPendingDelete )
{
return;
}
Victim.GetComponentsBoundingBox(BBox);
BBoxCenter = (BBox.Min + BBox.Max) * 0.5f;
HitActor = TraceExplosive(BBoxCenter, Location + vect(0, 0, 20));
bDamageBlocked = (HitActor != None && HitActor != Victim);
if(bDamageBlocked && HitActor.IsA('KFDoorActor'))
{
bDamageBlocked = false;
}
if( !bDamageBlocked )
{
Victim.TakeRadiusDamage(InstigatorController, ExplosionTemplate.Damage * DamageScale, ExplosionTemplate.DamageRadius,
ExplosionTemplate.MyDamageType, ExplosionTemplate.MomentumTransferScale, Location, bDoFullDamage,
(Owner != None) ? Owner : self, ExplosionTemplate.DamageFalloffExponent);
}
}
else
{
Victim.GetComponentsBoundingBox(BBox);
BBoxCenter = (BBox.Min + BBox.Max) * 0.5f;
HitActor = TraceExplosive(BBoxCenter, Location + vect(0, 0, 20));
bDamageBlocked = (HitActor != None && HitActor != Victim);
if(bDamageBlocked && HitActor.IsA('KFDoorActor'))
{
bDamageBlocked = false;
}
if(!bDamageBlocked)
{
if(!bHealsInstigator && Victim == Instigator)
{
return;
}
HumanVictim = KFPawn_Human(Victim);
if( HumanVictim != none && HumanVictim.GetExposureTo(Location) > 0 )
{
OwnerProjectile = KFProj_MedicGrenade(Owner);
if( OwnerProjectile != none )
{
bCanRepairArmor = OwnerProjectile.HealedPawns.Find( HumanVictim ) == INDEX_NONE;
}
if(bHealsDifferentAmmoutToInstigator && bHealsInstigator && Victim == Instigator)
{
HumanVictim.HealDamage( Lerp(fAltMinAmmoutHealing, fAltMaxAmmoutHealing, fChargePercentage), InstigatorController, HealingDamageType, bCanRepairArmor);
}
else
{
HumanVictim.HealDamage( Lerp(fMinAmmoutHealing, fMaxAmmoutHealing, fChargePercentage), InstigatorController, HealingDamageType, bCanRepairArmor);
}
if( bCanRepairArmor )
{
OwnerProjectile.HealedPawns.AddItem( HumanVictim );
}
}
}
}
}
}
protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCauseEffects)
{
if( bWasFadedOut || bDeleteMe || bPendingDelete )
{
return false;
}
if( bOnlyDamagePawns && bAllowFractureDamage )
{
ExplodeFractures();
}
if( bOnlyDamagePawns )
{
return ExplodePawns(bCauseDamage);
}
return super(KFExplosionActor).DoExplosionDamage(bCauseDamage, bCauseEffects);
}
/** Check fractures only if */
protected simulated function ExplodeFractures()
{
local Actor Victim;
local bool bCauseFractureEffects;
local float CheckRadius, VictimDist;
local Box BBox;
local FracturedStaticMeshActor FracActor;
local byte WantPhysChunksAndParticles;
local TraceHitInfo HitInfo;
if( bWasFadedOut || bDeleteMe || bPendingDelete )
{
return;
}
bCauseFractureEffects = WorldInfo.NetMode != NM_DedicatedServer && ExplosionTemplate.bCausesFracture;
if( !bCauseFractureEffects )
{
return;
}
// determine radius to check
CheckRadius = GetEffectCheckRadius(true, bCauseFractureEffects, WorldInfo.NetMode != NM_Client);
if ( CheckRadius > 0.0 )
{
foreach CollidingActors(class'Actor', Victim, CheckRadius, Location, ExplosionTemplate.bUseOverlapCheck,,HitInfo )
{
if ( Victim != Self
&& (!Victim.bWorldGeometry || Victim.bCanBeDamaged)
&& (NavigationPoint(Victim) == None)
&& Victim != ExplosionTemplate.ActorToIgnoreForDamage
&& (!ExplosionTemplate.bIgnoreInstigator || Victim != Instigator)
&& !ClassIsChildOf(Victim.Class, ExplosionTemplate.ActorClassToIgnoreForDamage)
&& !IsBehindExplosion(Victim) )
{
// return if this is a pawn
if(GamePawn(Victim) != none)
{
return;
}
// check if visible, unless physics object
// note: using bbox center instead of location, because that's what visiblecollidingactors does
Victim.GetComponentsBoundingBox(BBox);
// adjust distance if using overlap check
VictimDist = ExplosionTemplate.bUseOverlapCheck ? BoxDistanceToPoint(Location, BBox) : VSize(Location - Victim.Location);
FracActor = FracturedStaticMeshActor(Victim);
if ( (FracActor != None)
&& (VictimDist < ExplosionTemplate.FractureMeshRadius)
&& (FracActor.Physics == PHYS_None)
&& FracActor.IsFracturedByDamageType(ExplosionTemplate.MyDamageType)
&& FracActor.FractureEffectIsRelevant( false, Instigator, WantPhysChunksAndParticles) )
{
// Let kismet know that we were hit by an explosion
FracActor.NotifyHitByExplosion(InstigatorController, ExplosionTemplate.Damage, ExplosionTemplate.MyDamageType);
FracActor.BreakOffPartsInRadius(Location, ExplosionTemplate.FractureMeshRadius, ExplosionTemplate.FracturePartVel, WantPhysChunksAndParticles == 1 ? true : false);
}
}
}
}
}
DefaultProperties
{
HealingDamageType=class'KFDT_Healing'
HealingAmount=0;
fMinAmmoutHealing=5; //4;
fMaxAmmoutHealing=50; //40
Interval=0
MaxTime=0.0
FadeOutTime=0.0
bExplodeMoreThanOnce=false
bDoFullDamage=false
bOnlyDamagePawns=true
bAllowFractureDamage=true
bSkipLineCheckForPawns=true
LoopStartEvent=none
LoopStopEvent=none
//EXPERIMENTAL FEATURES FOR DESIGN
bHealsInstigator = false;
bHealsDifferentAmmoutToInstigator = false;
fAltMinAmmoutHealing=1;
fAltMaxAmmoutHealing=5;
}

View File

@ -0,0 +1,73 @@
//=============================================================================
// KFExplosion_HRG_MedicMissile
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFExplosion_HRG_MedicMissile extends KFExplosionActorLingering;
var private int HealingValue;
// Disable Knockdown for friendlies
protected function bool KnockdownPawn(BaseAiPawn Victim, float DistFromExplosion)
{
if (Victim.GetTeamNum() != Instigator.GetTeamNum())
{
return Super.KnockdownPawn(Victim, DistFromExplosion);
}
return false;
}
// Disable Stumble for friendlies
protected function bool StumblePawn(BaseAiPawn Victim, float DistFromExplosion)
{
if (Victim.GetTeamNum() != Instigator.GetTeamNum())
{
return Super.StumblePawn(Victim, DistFromExplosion);
}
return false;
}
protected simulated function AffectsPawn(Pawn Victim, float DamageScale)
{
local KFPawn KFP;
if( bWasFadedOut|| bDeleteMe || bPendingDelete )
{
return;
}
KFP = KFPawn(Victim);
if (KFP == none)
{
return;
}
if (KFP.GetTeamNum() == Instigator.GetTeamNum())
{
KFP.HealDamage(HealingValue, Instigator.Controller, class'KFDT_Healing');
}
else
{
super.AffectsPawn(VIctim, DamageScale);
KFP.ApplyDamageOverTime(class'KFDT_Toxic_HRG_MedicMissile'.default.PoisonPower, Instigator.Controller, class'KFDT_Toxic_HRG_MedicMissile');
}
}
DefaultProperties
{
Interval=0f
MaxTime=0f
bOnlyDamagePawns=true
bDoFullDamage=false
bExplodeMoreThanOnce=false
HealingValue=50
}

View File

@ -386,6 +386,8 @@ function UpdateGameSettings()
local int NumHumanPlayers, i;
local KFGameEngine KFEngine;
super.UpdateGameSettings();
if (WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.NetMode == NM_ListenServer)
{
//`REMOVEMESOON_ServerTakeoverLog("KFGameInfo_Survival.UpdateGameSettings 1 - GameInterface: "$GameInterface);

View File

@ -550,14 +550,16 @@ function byte PickTeam(byte Current, Controller C, const out UniqueNetId PlayerI
/** Return whether a team change is allowed. */
function bool ChangeTeam(Controller Other, int N, bool bNewTeam)
{
if( Other.PlayerReplicationInfo == none
|| Other.PlayerReplicationInfo.bBot
|| (!Other.PlayerReplicationInfo.bOnlySpectator
&& ArrayCount(Teams) > N
&& Other.PlayerReplicationInfo.Team != Teams[N]) )
if (ArrayCount(Teams) > N)
{
SetTeam( Other, Teams[N] );
return true;
if( Other.PlayerReplicationInfo == none
|| Other.PlayerReplicationInfo.bBot
|| ( !Other.PlayerReplicationInfo.bOnlySpectator
&& Other.PlayerReplicationInfo.Team != Teams[N] ) )
{
SetTeam( Other, Teams[N] );
return true;
}
}
return false;

View File

@ -445,6 +445,7 @@ event PostLogin( PlayerController NewPlayer )
{
local KFPlayerController_WeeklySurvival KFPC_WS;
local KFPawn_Customization KFCustomizePawn;
super.PostLogin(NewPlayer);
KFPC_WS = KFPlayerController_WeeklySurvival(NewPlayer);
@ -544,6 +545,12 @@ function WaveEnded(EWaveEndCondition WinCondition)
// This function is called multiple times in a row. Only apply it once.
bWasFirstTime = bWaveStarted;
// Choose new perk before the end of wave message triggers in supper.
if (MyKFGRI.IsRandomPerkMode() && WinCondition == WEC_WaveWon)
{
ChooseRandomPerks();
}
super.WaveEnded(WinCondition);
if (OutbreakEvent.ActiveEvent.bPermanentZedTime && ZedTimeRemaining > ZedTimeBlendOutTime)
@ -1315,6 +1322,10 @@ function WaveStarted()
}
}
/**
* Weekly 17: VIP MODE
*/
function OnVIPDiesEndMatch()
{
local KFPlayerController KFPC;
@ -1500,6 +1511,182 @@ function OnOutbreakWaveWon()
}
}
/*
* Weekly 18: Random Perks
*/
simulated function NotifyPlayerStatsInitialized(KFPlayerController_WeeklySurvival KFPC)
{
if (KFPC != none && MyKFGRI.IsRandomPerkMode())
{
ChooseInitialRandomPerk(KFPC);
}
}
function ChooseInitialRandomPerk(KFPlayerController_WeeklySurvival KFPC_WS)
{
local KFPlayerController_WeeklySurvival OtherKFPC;
local array<class<KFPerk> > AvailablePerks;
local int i;
local byte NewPerkIndex;
local bool bPerkFound;
`Log("CHOOSING INITIAL PERKS");
for (i = 0; i < KFPC_WS.PerkList.Length; ++i)
{
bPerkFound = false;
foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', OtherKFPC)
{
if (OtherKFPC == KFPC_WS)
{
continue;
}
if (KFPC_WS.Perklist[i].PerkClass == OtherKFPC.CurrentPerk.Class)
{
bPerkFound = true;
break;
}
}
if (!bPerkFound)
{
AvailablePerks.AddItem(KFPC_WS.PerkList[i].PerkClass);
}
}
if (AvailablePerks.Length == 0)
{
for (i = 0; i < KFPC_WS.Perklist.Length; ++i)
{
AvailablePerks.AddItem(KFPC_WS.Perklist[i].PerkClass);
}
KFPC_WS.LockedPerks.Length = 0;
}
NewPerkIndex = Rand(AvailablePerks.Length);
KFPC_WS.LockedPerks.AddItem(AvailablePerks[NewPerkIndex]);
KFPC_WS.ForceNewPerk(AvailablePerks[NewPerkIndex]);
}
function ChooseRandomPerks()
{
local KFPlayerController_WeeklySurvival KFPC;
local array<class<KFPerk> > AvailablePerks;
local array<class<KFPerk> > PickedPerks;
local int i, j;
local byte NewPerkIndex;
local bool bPerkFound;
foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC)
{
AvailablePerks.Length = 0;
for (i = 0; i < KFPC.Perklist.Length; ++i)
{
bPerkFound = false;
for (j = 0; j < PickedPerks.Length; ++j)
{
if (KFPC.Perklist[i].PerkClass == PickedPerks[j])
{
bPerkFound = true;
break;
}
}
if (!bPerkFound)
{
for (j = 0; j < KFPC.LockedPerks.Length; ++j)
{
if (KFPC.Perklist[i].PerkClass == KFPC.LockedPerks[j])
{
bPerkFound = true;
break;
}
}
}
if (!bPerkFound)
{
AvailablePerks.AddItem(KFPC.Perklist[i].PerkClass);
}
}
if (AvailablePerks.Length == 0)
{
for (i = 0; i < KFPC.Perklist.Length; ++i)
{
bPerkFound = false;
for (j = 0; j < PickedPerks.Length; ++j)
{
if (KFPC.Perklist[i].PerkClass == PickedPerks[j])
{
bPerkFound = true;
break;
}
}
if (!bPerkFound && KFPC.Perklist[i].PerkClass != KFPC.CurrentPerk.Class)
{
AvailablePerks.AddItem(KFPC.Perklist[i].PerkClass);
}
}
if (AvailablePerks.Length == 0)
{
for (i = 0; i < KFPC.Perklist.Length; ++i)
{
AvailablePerks.AddItem(KFPC.Perklist[i].PerkClass);
PickedPerks.Length = 0;
}
}
KFPC.LockedPerks.Length = 0;
}
NewPerkIndex = Rand(AvailablePerks.Length);
PickedPerks.AddItem(AvailablePerks[NewPerkIndex]);
KFPC.LockedPerks.AddItem(AvailablePerks[NewPerkIndex]);
KFPC.ForceNewPerk(AvailablePerks[NewPerkIndex]);
KFPC.PlayRandomPerkChosenSound();
}
}
//
// Overide BroadcastLocalizedMessage for the RandomPerk weekly (18)
// Perk is replicated while the message is sent through a RPC, so the message arrives before
// the perk is updated on clients.
// Override the BroadcastLocalizedMessage function to RPC each player the message with their own
// new perk class as the optional object so the client knows the information before it gets the perk updated.
//
event BroadcastLocalizedMessage( class<LocalMessage> InMessageClass, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject )
{
if (!MyKFGRI.IsRandomPerkMode() || Switch != GMT_WaveEnd)
{
Super.BroadcastLocalizedMessage(InMessageClass, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject);
}
else
{
BroadcastCustomWaveEndMessage(self, InMessageClass, Switch);
}
}
function BroadcastCustomWaveEndMessage( actor Sender, class<LocalMessage> InMessageClass, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject )
{
local KFPlayerController KFPC;
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
{
KFPC.ReceiveLocalizedMessage( InMessageClass, Switch, RelatedPRI_1, RelatedPRI_2, KFPC.GetPerk().Class);
}
}
//
defaultproperties
{
//Overrides

View File

@ -1220,6 +1220,21 @@ defaultproperties
, class'KFGameContent.KFPawn_ZedHans'),
)}
// Random Perks
SetEvents[18]={(
EventDifficulty=2,
GameLength=GL_Normal,
DoshOnKillGlobalModifier=1.3,
SpawnReplacementList={(
(SpawnEntry=AT_Clot,NewClass=(class'KFGameContent.KFPawn_ZedCrawler'),PercentChance=0.3),
(SpawnEntry=AT_AlphaClot,NewClass=(class'KFGameContent.KFPawn_ZedCrawler'),PercentChance=0.3),
(SpawnEntry=AT_SlasherClot,NewClass=(class'KFGameContent.KFPawn_ZedStalker'),PercentChance=0.3),
(SpawnEntry=AT_Bloat,NewClass=(class'KFGameContent.KFPawn_ZedSiren'),PercentChance=0.3),
(SpawnEntry=AT_Scrake,NewClass=(class'KFGameContent.KFPawn_ZedFleshpound'),PercentChance=0.3)
)}
)}
//Test events from here down. These don't end up in the regular rotation.
// The override ID starts from one higher than the last SetEvents entry above.
// Ex: Big head = 7, Horde = 8

View File

@ -0,0 +1,22 @@
//=============================================================================
// KFProj_Bullet_HVStormCannon
//=============================================================================
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFProj_Bullet_HVStormCannon extends KFProj_Bullet
hidedropdown;
defaultproperties
{
MaxSpeed=22500.0
Speed=22500.0
DamageRadius=0
ProjFlightTemplate = ParticleSystem'WEP_HVStormCannon_EMIT.FX_HVStormCannon_Projectile'
ImpactEffects = KFImpactEffectInfo'WEP_HVStormCannon_ARCH.Wep_HVStormCannon_Impact'
}

View File

@ -0,0 +1,22 @@
//=============================================================================
// KFProj_Bullet_ZedMKIII
//=============================================================================
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFProj_Bullet_ZedMKIII extends KFProj_Bullet
hidedropdown;
defaultproperties
{
MaxSpeed=22500.0
Speed=22500.0
DamageRadius=0
ProjFlightTemplate = ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Bullet_Projectile'
ImpactEffects = KFImpactEffectInfo'WEP_ZEDMKIII_ARCH.FX_ZEDMKIII_Bullet_Impact'
}

View File

@ -69,7 +69,9 @@ simulated protected function PrepareExplosionTemplate()
{
class'KFPerk_Demolitionist'.static.PrepareExplosive( Instigator, self );
super.PrepareExplosionTemplate();
GetRadialDamageValues(ExplosionTemplate.Damage, ExplosionTemplate.DamageRadius, ExplosionTemplate.DamageFalloffExponent);
// super.PrepareExplosionTemplate();
}
simulated protected function SetExplosionActorClass()

View File

@ -133,7 +133,9 @@ simulated protected function PrepareExplosionTemplate()
{
class'KFPerk_Berserker'.static.PrepareExplosive( Instigator, self );
super.PrepareExplosionTemplate();
GetRadialDamageValues(ExplosionTemplate.Damage, ExplosionTemplate.DamageRadius, ExplosionTemplate.DamageFalloffExponent);
//super.PrepareExplosionTemplate();
}

View File

@ -0,0 +1,556 @@
//=============================================================================
// KFProj_HRG_BallisticBouncer
//=============================================================================
// Projectile class for HRG Ballistic Bouncer
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFProj_HRG_BallisticBouncer extends KFProjectile;
/** Dampen amount for every bounce */
var float DampenFactor;
/** Dampen amount for parallel angle to velocity */
var float DampenFactorParallel;
/** Sound mine makes when it hits something and bounces off */
var AkEvent BounceAkEvent;
/** Sound mine makes when it hits something and comes to rest */
var AkEvent ImpactAkEvent;
/** Sound mine makes when it hits something and bounces off */
var AkEvent BounceAkEventHeavy;
/** Sound mine makes when it hits something and comes to rest */
var AkEvent ImpactAkEventHeavy;
/** Particle System spawned when it hits something */
var ParticleSystem HitFXTemplate;
var transient ParticleSystemComponent HitPSC;
/** Particle System for the fade out burst **/
var ParticleSystem BurstFXTemplate;
var transient ParticleSystemComponent BurstPSC;
/** Sound for the fade out burst **/
var AkEvent BurstAkEvent;
/** Decal settings */
var MaterialInterface ImpactDecalMaterial;
var float ImpactDecalWidth;
var float ImpactDecalHeight;
var float ImpactDecalThickness;
var int MaxBounces;
var int NumBounces;
var float MaxInitialSpeedByCharge;
var float MinInitialSpeedByCharge;
var float MaxCollisionRadius;
var float MinCollisionRadius;
var float MaxCollisionHeight;
var float MinCollisionHeight;
var float MaxScalePerPercentage;
var float MinScalePerPercentage;
var int MaxBouncesPerPercentage;
var int MinBouncesPerPercentage;
var float MaxLifeSpanPerPercentage;
var float MinLifeSpanPerPercentage;
var float InheritedScale;
var repnotify float fChargePercentage;
var float fCachedCylinderWidth, fCachedCylinderHeight;
var float ArmDistSquared;
var bool bFadingOut;
var float FadeOutTime;
var transient byte RequiredImpactsForSeasonal;
var transient array<Pawn> ImpactVictims;
replication
{
if( bNetDirty )
InheritedScale, fChargePercentage, MaxBounces;
}
simulated event ReplicatedEvent( name VarName )
{
if( VarName == nameOf(fChargePercentage))
{
fCachedCylinderWidth = Lerp(MinCollisionRadius, MaxCollisionRadius, fChargePercentage);
fCachedCylinderHeight = Lerp(MinCollisionHeight, MaxCollisionHeight, fChargePercentage);
// CylinderComponent(CollisionComponent).SetCylinderSize(fCachedCylinderWidth, fCachedCylinderHeight);
SetCollisionSize(fCachedCylinderWidth, fCachedCylinderHeight);
ApplyVFXParams(fChargePercentage);
}
else
{
super.ReplicatedEvent( VarName );
}
}
simulated event PostBeginPlay()
{
Super.PostBeginPlay();
RequiredImpactsForSeasonal = class'KFSeasonalEventStats_Xmas2022'.static.GetMaxBallisticBouncerImpacts();
}
simulated function SetInheritedScale(float Scale, float ChargePercentage)
{
local float NewSpeed;
InheritedScale = Scale;
fChargePercentage = ChargePercentage;
fChargePercentage = FMax(0.1, fChargePercentage);
if (WorldInfo.NetMode == NM_DedicatedServer)
{
SetCollisionSize(Lerp(MinCollisionRadius, MaxCollisionRadius, fChargePercentage), Lerp(MinCollisionHeight, MaxCollisionHeight, ChargePercentage));
}
NewSpeed = Lerp(MinInitialSpeedByCharge, MaxInitialSpeedByCharge, fChargePercentage);
Velocity = Normal(Velocity) * NewSpeed;
Speed = NewSpeed;
MaxBounces = Lerp(MinBouncesPerPercentage, MaxBouncesPerPercentage, fChargePercentage);
LifeSpan = Lerp(MinLifeSpanPerPercentage, MaxLifeSpanPerPercentage, fChargePercentage);
if (ProjEffects != none)
{
ProjEffects.SetScale(Lerp(MinScalePerPercentage, MaxScalePerPercentage, fChargePercentage));
}
if (WorldInfo.NetMode != NM_DedicatedServer)
{
ApplyVFXParams(fChargePercentage);
}
bNetDirty=true;
}
function RestoreCylinder()
{
CylinderComponent(CollisionComponent).SetCylinderSize(fCachedCylinderWidth, fCachedCylinderHeight);
}
simulated function BounceNoCheckRepeatingTouch(vector HitNormal, Actor BouncedOff)
{
local vector VNorm;
// Reflect off BouncedOff w/damping
VNorm = (Velocity dot HitNormal) * HitNormal;
if (NumBounces < MaxBounces)
{
Velocity = -VNorm + (Velocity - VNorm);
}
else
{
Velocity = -VNorm * DampenFactor + (Velocity - VNorm) * DampenFactorParallel;
}
Speed = VSize(Velocity);
// Play a sound
PlayImpactSound();
// Spawn impact particle system, server needs to send the message (it's the only one storing MaxBounces)
if (WorldInfo.NetMode != NM_DedicatedServer)
{
if (NumBounces < MaxBounces)
{
PlayImpactVFX(HitNormal);
}
`ImpactEffectManager.PlayImpactEffects(Location, Instigator, VNorm, ImpactEffects);
}
++NumBounces;
}
/** Adjusts movement/physics of projectile.
* Returns true if projectile actually bounced / was allowed to bounce */
simulated function bool Bounce( vector HitNormal, Actor BouncedOff )
{
// Avoid crazy bouncing
if (CheckRepeatingTouch(BouncedOff))
{
CylinderComponent(CollisionComponent).SetCylinderSize(1,1);
SetTimer(0.06f, false, nameof(RestoreCylinder));
return false;
}
BounceNoCheckRepeatingTouch(HitNormal, BouncedOff);
return true;
}
/** Plays a sound on impact */
simulated function PlayImpactSound( optional bool bIsAtRest )
{
if( WorldInfo.NetMode != NM_DedicatedServer )
{
if( bIsAtRest )
{
if(fChargePercentage < 0.75f)
PostAkEvent( ImpactAkEvent, true, true, false );
else
PostAkEvent( ImpactAkEventHeavy, true, true, false );
}
else
{
if(fChargePercentage < 0.75f)
PostAkEvent( BounceAkEvent, true, true, false );
else
PostAkEvent( BounceAkEventHeavy, true, true, false );
}
}
}
simulated function PlayImpactVFX(vector HitNormal)
{
HitPSC = WorldInfo.MyEmitterPool.SpawnEmitter(HitFXTemplate, ProjEffects.GetPosition(), rotator(HitNormal));
HitPSC.SetFloatParameter(name("Charge"), fChargePercentage);
}
simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal)
{
local float TraveledDistance;
TraveledDistance = (`TimeSince(CreationTime) * Speed);
TraveledDistance *= TraveledDistance;
// If we collided with a Siren shield, let the shield code handle touches
if( Other.IsA('KFTrigger_SirenProjectileShield') )
{
return;
}
if (Other != Instigator && Other.GetTeamNum() != GetTeamNum())
{
// check/ignore repeat touch events
if (!CheckRepeatingTouch(Other))
{
ProcessBulletTouch(Other, HitLocation, HitNormal);
}
}
}
simulated function ProcessBulletTouch(Actor Other, Vector HitLocation, Vector HitNormal)
{
local Pawn Victim;
local array<ImpactInfo> HitZoneImpactList;
//local ImpactInfo ImpactInfoFallBack;
local vector StartTrace, EndTrace, Direction; //, DirectionFallBack;
local TraceHitInfo HitInfo;
local KFWeapon KFW;
Victim = Pawn( Other );
if ( Victim == none )
{
if ( bDamageDestructiblesOnTouch && Other.bCanBeDamaged )
{
HitInfo.HitComponent = LastTouchComponent;
HitInfo.Item = INDEX_None; // force TraceComponent on fractured meshes
Other.TakeDamage(Damage, InstigatorController, Location, MomentumTransfer * Normal(Velocity), MyDamageType, HitInfo, self);
}
// Reduce the penetration power to zero if we hit something other than a pawn or foliage actor
if( InteractiveFoliageActor(Other) == None )
{
PenetrationPower = 0;
BounceNoCheckRepeatingTouch(HitNormal, Other);
return;
}
}
else
{
if (bSpawnShrapnel)
{
//spawn straight forward through the zed
SpawnShrapnel(Other, HitLocation, HitNormal, rotator(Velocity), ShrapnelSpreadWidthZed, ShrapnelSpreadHeightZed);
}
StartTrace = HitLocation;
Direction = Normal(Velocity);
EndTrace = StartTrace + Direction * (Victim.CylinderComponent.CollisionRadius * 6.0);
TraceProjHitZones(Victim, EndTrace, StartTrace, HitZoneImpactList);
//`Log("HitZoneImpactList: " $HitZoneImpactList.Length);
if ( HitZoneImpactList.length > 0 )
{
HitZoneImpactList[0].RayDir = Direction;
if( Owner != none )
{
KFW = KFWeapon( Owner );
if( KFW != none )
{
KFW.HandleProjectileImpact(WeaponFireMode, HitZoneImpactList[0], PenetrationPower);
}
}
IncrementNumImpacts(Victim);
BounceNoCheckRepeatingTouch(HitNormal, Other);
}
}
}
simulated function IncrementNumImpacts(Pawn Victim)
{
local int i;
local KFPlayerController KFPC;
if (WorldInfo.NetMode == NM_Client)
{
return;
}
KFPC = KFPlayerController(InstigatorController);
if (KFPC == none)
{
return;
}
for (i = 0; i < ImpactVictims.Length; ++i)
{
if (Victim == ImpactVictims[i])
{
return;
}
}
ImpactVictims.AddItem(Victim);
UpdateImpactsSeasonalObjective(KFPC);
}
function UpdateImpactsSeasonalObjective(KFPlayerController KFPC)
{
local byte ObjectiveIndex;
ObjectiveIndex = 3;
if (ImpactVictims.Length >= RequiredImpactsForSeasonal)
{
// Check parent controller.
KFPC.ClientOnTryCompleteObjective(ObjectiveIndex, SEI_Winter);
}
}
simulated event HitWall( vector HitNormal, Actor Wall, PrimitiveComponent WallComp )
{
// Don't collide with other projectiles
if( Wall.class == class )
{
return;
}
Bounce( HitNormal, Wall );
}
simulated function SpawnBurstFX()
{
local vector vec;
if( WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.MyEmitterPool == none || ProjEffects == none )
{
return;
}
BurstPSC = WorldInfo.MyEmitterPool.SpawnEmitter(BurstFXTemplate, ProjEffects.GetPosition(), rotator(vect(0,0,1)), self, , ,true);
vec.X = fChargePercentage;
vec.Y = fChargePercentage;
vec.Z = fChargePercentage;
BurstPSC.SetVectorParameter(name("BlobCharge"), vec);
BurstPSC.SetFloatParameter(name("MineFxControlParam"), fChargePercentage);
PostAkEvent(BurstAkEvent, true, true, false);
}
simulated function Tick(float Delta)
{
if (NumBounces < MaxBounces || bFadingOut)
return;
if (Speed < 20.0f)
{
bFadingOut = true;
StopSimulating();
SpawnBurstFX();
// Tell clients to tear off and fade out on their own
if( WorldInfo.NetMode != NM_Client )
{
ClearTimer(nameof(Timer_Destroy));
SetTimer( 1.0f, false, nameOf(Timer_Destroy) );
}
}
}
simulated function Timer_Destroy()
{
if (BurstPSC != none)
{
BurstPSC.DeactivateSystem();
}
Destroy();
}
simulated function ApplyVFXParams(float ChargePercent)
{
if (ProjEffects != none)
{
ProjEffects.SetFloatParameter(name("InflateBlob"), ChargePercent);
}
}
simulated function SyncOriginalLocation()
{
local Actor HitActor;
local vector HitLocation, HitNormal;
local TraceHitInfo HitInfo;
if (Role < ROLE_Authority && Instigator != none && Instigator.IsLocallyControlled())
{
HitActor = Trace(HitLocation, HitNormal, OriginalLocation, Location,,, HitInfo, TRACEFLAG_Bullet);
if (HitActor != none)
{
ProcessBulletTouch(HitActor, HitLocation, HitNormal);
}
}
Super.SyncOriginalLocation();
}
defaultproperties
{
TerminalVelocity=5000
TossZ=150
GravityScale=0.5
MomentumTransfer=50000.0
LifeSpan=300
PostExplosionLifetime=1
Physics=PHYS_Falling
bBounce=true
ProjFlightTemplate= ParticleSystem'WEP_HRG_BallisticBouncer_EMIT.FX_HRG_BallisticBouncer_Ball_Projectile'
BurstFXTemplate= ParticleSystem'WEP_HRG_BallisticBouncer_EMIT.FX_HRG_BallisticBouncer_Ball_Explode'
HitFXTemplate= ParticleSystem'WEP_HRG_BallisticBouncer_EMIT.FX_HRG_BallisticBouncer_Ball_Hit'
bSuppressSounds=false
bAmbientSoundZedTimeOnly=false
bAutoStartAmbientSound=false
bStopAmbientSoundOnExplode=true
ImpactAkEvent=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Impact'
BounceAkEvent=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Impact'
ImpactAkEventHeavy=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Impact_Heavy'
BounceAkEventHeavy=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Impact_Heavy'
BurstAkEvent=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Ball_Explosion'
Begin Object Class=AkComponent name=AmbientAkSoundComponent
bStopWhenOwnerDestroyed=true
bForceOcclusionUpdateInterval=true
OcclusionUpdateInterval=0.25f
End Object
AmbientComponent=AmbientAkSoundComponent
Components.Add(AmbientAkSoundComponent)
//ImpactDecalMaterial=DecalMaterial'FX_Mat_Lib.FX_Puke_Mine_Splatter_DM'
ImpactDecalWidth=178.f
ImpactDecalHeight=178.f
ImpactDecalThickness=28.f
Begin Object Name=CollisionCylinder
CollisionRadius=0.f
CollisionHeight=0.f
CollideActors=true
BlockNonZeroExtent=false
PhysMaterialOverride=PhysicalMaterial'WEP_HRG_BallisticBouncer_EMIT.BloatPukeMine_PM'
End Object
bCollideComplex=TRUE // Ignore simple collision on StaticMeshes, and collide per poly
bUseClientSideHitDetection=true
bNoReplicationToInstigator=false
bUpdateSimulatedPosition=true
bProjTarget=true
bCanBeDamaged=false
bNoEncroachCheck=true
bPushedByEncroachers=false
DampenFactor=0.175f
DampenFactorParallel=0.175f
ExtraLineCollisionOffsets.Add((Y=-20))
ExtraLineCollisionOffsets.Add((Y=20))
ExtraLineCollisionOffsets.Add((Z=-20))
ExtraLineCollisionOffsets.Add((Z=20))
// Since we're still using an extent cylinder, we need a line at 0
ExtraLineCollisionOffsets.Add(())
GlassShatterType=FMGS_ShatterAll
InheritedScale=1
MaxInitialSpeedByCharge=5000
MinInitialSpeedByCharge=1500
MaxCollisionRadius=20
MinCollisionRadius=10
MaxCollisionHeight=20
MinCollisionHeight=10
MaxScalePerPercentage=1.5f
MinScalePerPercentage=0.5f
MaxBouncesPerPercentage=5
MinBouncesPerPercentage=1
MaxLifespanPerPercentage=500
MinLifeSpanPerPercentage=300
bBlockedByInstigator=true
bNetTemporary=false
bSyncToOriginalLocation=true
bSyncToThirdPersonMuzzleLocation=true
bReplicateLocationOnExplosion=true
TouchTimeThreshhold=0.05
MaxBounces=0
NumBounces=0
ArmDistSquared=0
// Fade out properties
FadeOutTime=5.0f
// ImpactEffects= KFImpactEffectInfo'WEP_DragonsBreath_ARCH.DragonsBreath_bullet_impact'
}

View File

@ -0,0 +1,128 @@
//=============================================================================
// KFProj_Rocket_HRG_MedicMissile
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFProj_Rocket_HRG_MedicMissile extends KFProj_BallisticExplosive
hidedropdown;
simulated function bool AllowNuke()
{
return false;
}
simulated function bool AllowDemolitionistConcussive()
{
return false;
}
simulated function bool AllowDemolitionistExplosionChangeRadius()
{
return false;
}
simulated protected function PrepareExplosionTemplate()
{
local Weapon OwnerWeapon;
local Pawn OwnerPawn;
local KFPerk_Survivalist Perk;
super(KFProjectile).PrepareExplosionTemplate();
OwnerWeapon = Weapon(Owner);
if (OwnerWeapon != none)
{
OwnerPawn = Pawn(OwnerWeapon.Owner);
if (OwnerPawn != none)
{
Perk = KFPerk_Survivalist(KFPawn(OwnerPawn).GetPerk());
if (Perk != none)
{
ExplosionTemplate.DamageRadius *= KFPawn(OwnerPawn).GetPerk().GetAoERadiusModifier();
}
}
}
}
simulated function AdjustCanDisintigrate()
{
// This weapon is not from Demolitionist, so always enable Siren disintegrate
bCanDisintegrate = true;
}
defaultproperties
{
Physics=PHYS_Projectile
Speed=5000
MaxSpeed=5000
TossZ=0
GravityScale=1.0
MomentumTransfer=50000.0
ArmDistSquared=150000 // 4 meters
bCollideWithTeammates=true
bWarnAIWhenFired=true
ProjFlightTemplate=ParticleSystem'WEP_HRG_MedicMissile_EMIT.FX_HRG_MedicMissile_Projectile'
ProjFlightTemplateZedTime=ParticleSystem'WEP_HRG_MedicMissile_EMIT.FX_HRG_MedicMissile_Projectile_ZED_TIME'
ProjDudTemplate=ParticleSystem'WEP_HRG_MedicMissile_EMIT.FX_HRG_MedicMissile_Projectile_Dud'
GrenadeBounceEffectInfo=KFImpactEffectInfo'WEP_HRG_MedicMissile_ARCH.HRG_MedicMissile_Projectile_Impacts'
ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01'
bCanDisintegrate=true
AmbientSoundPlayEvent=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_Projectile_Loop'
AmbientSoundStopEvent=AkEvent'WW_WEP_HRG_MedicMissile.Stop_WEP_HRG_MedicMissile_Projectile_Loop'
AltExploEffects=KFImpactEffectInfo'WEP_HRG_MedicMissile_ARCH.HRG_MedicMissile_Explosion_Concussive_Force'
ExplosionActorClass=class'KFExplosion_HRG_MedicMissile'
// Grenade explosion light
Begin Object Class=PointLightComponent Name=ExplosionPointLight
LightColor=(R=60,G=200,B=255,A=255)
Brightness=2.f
Radius=1500.f
FalloffExponent=10.f
CastShadows=False
CastStaticShadows=FALSE
CastDynamicShadows=False
bCastPerObjectShadows=false
bEnabled=FALSE
LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
End Object
// explosion
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=400
DamageRadius=300
DamageFalloffExponent=2
DamageDelay=0.f
// Damage Effects
MyDamageType=class'KFDT_Explosive_HRG_MedicMissile'
KnockDownStrength=0
FractureMeshRadius=0.0
FracturePartVel=0.0
ExplosionEffects=KFImpactEffectInfo'WEP_HRG_MedicMissile_ARCH.HRG_MedicMissile_Explosion'
ExplosionSound=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_Explosion'
// Dynamic Light
ExploLight=ExplosionPointLight
ExploLightStartFadeOutTime=0.0
ExploLightFadeOutTime=0.2
// Camera Shake
CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble'
CamShakeInnerRadius=200
CamShakeOuterRadius=900
CamShakeFalloff=1.5f
bOrientCameraShakeTowardsEpicenter=true
End Object
ExplosionTemplate=ExploTemplate0
bCanApplyDemolitionistPerks=false
}

View File

@ -0,0 +1,123 @@
//=============================================================================
// KFProj_Rocket_ZedMKIII
//=============================================================================
// KFProj_Rocket_ZedMKIII rocket
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFProj_Rocket_ZedMKIII extends KFProj_BallisticExplosive
hidedropdown;
/** Our intended target actor */
var private KFPawn LockedTarget;
/** How much 'stickyness' when seeking toward our target. Determines how accurate rocket is */
var const float SeekStrength;
replication
{
if( bNetInitial )
LockedTarget;
}
function SetLockedTarget( KFPawn NewTarget )
{
LockedTarget = NewTarget;
}
simulated event Tick( float DeltaTime )
{
local vector TargetImpactPos, DirToTarget;
super.Tick( DeltaTime );
// Skip the first frame, then start seeking
if( !bHasExploded
&& LockedTarget != none
&& Physics == PHYS_Projectile
&& Velocity != vect(0,0,0)
&& LockedTarget.IsAliveAndWell()
&& `TimeSince(CreationTime) > 0.03f )
{
// Grab our desired relative impact location from the weapon class
TargetImpactPos = class'KFWeap_ZedMKIII'.static.GetLockedTargetLoc( LockedTarget );
// Seek towards target
Speed = VSize( Velocity );
DirToTarget = Normal( TargetImpactPos - Location );
Velocity = Normal( Velocity + (DirToTarget * (SeekStrength * DeltaTime)) ) * Speed;
// Aim rotation towards velocity every frame
SetRotation( rotator(Velocity) );
}
}
defaultproperties
{
Physics=PHYS_Projectile
Speed=4000 //6000
MaxSpeed=4000 //6000
TossZ=0
GravityScale=1.0
MomentumTransfer=50000.0f
ArmDistSquared=110000.0f //20000.0f // 110000.0f // 4 meters 150000.0
SeekStrength=928000.0f // 128000.0f
bWarnAIWhenFired=true
ProjFlightTemplate=ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Rocket'
ProjFlightTemplateZedTime=ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Rocket_ZED_TIME'
ProjDudTemplate=ParticleSystem'WEP_ZEDMKIII_EMIT.FX_ZEDMKIII_Rocket_Dud'
GrenadeBounceEffectInfo=KFImpactEffectInfo'WEP_RPG7_ARCH.RPG7_Projectile_Impacts'
ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01'
AmbientSoundPlayEvent=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Rocket_LP'
AmbientSoundStopEvent=AkEvent'WW_WEP_ZEDMKIII.Stop_WEP_ZEDMKIII_Rocket_LP'
AltExploEffects=KFImpactEffectInfo'WEP_ZEDMKIII_ARCH.FX_ZEDMKIII_Explosion_Concussive_force'
// Grenade explosion light
Begin Object Class=PointLightComponent Name=ExplosionPointLight
LightColor=(R=252,G=218,B=171,A=255)
Brightness=4.f
Radius=2000.f
FalloffExponent=10.f
CastShadows=False
CastStaticShadows=FALSE
CastDynamicShadows=False
bCastPerObjectShadows=false
bEnabled=FALSE
LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
End Object
// explosion
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=200
DamageRadius=300
DamageFalloffExponent=2
DamageDelay=0.f
// Damage Effects
MyDamageType=class'KFDT_Explosive_ZedMKIII'
KnockDownStrength=0
FractureMeshRadius=200.0
FracturePartVel=500.0
ExplosionEffects=KFImpactEffectInfo'WEP_ZEDMKIII_ARCH.FX_ZEDMKIII_Explosion'
ExplosionSound=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Explosion'
// Dynamic Light
ExploLight=ExplosionPointLight
ExploLightStartFadeOutTime=0.0
ExploLightFadeOutTime=0.2
// Camera Shake
CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble'
CamShakeInnerRadius=0
CamShakeOuterRadius=500
CamShakeFalloff=3.f
bOrientCameraShakeTowardsEpicenter=true
End Object
ExplosionTemplate=ExploTemplate0
}

View File

@ -0,0 +1,183 @@
//=============================================================================
// KFSeasonalEventStats_Xmas2022
//=============================================================================
// Tracks event-specific challenges/accomplishments for Xmas 2022
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFSeasonalEventStats_Xmas2022 extends KFSeasonalEventStats;
/**
Required impacts of the same projectile to count for the objective. I.e. 3 means
that a ballistic bouncer projectile needs to make 3 impacts to count for the objective
*/
var private const byte HRGBBProjectilImpactsRequired;
var transient private const int FrozenZedsRequired, ShotgunJumpsRequired, BallisticBouncerImpactsRequired, EndlessWaveRequired, XmasEventIndex;
var transient int ShotgunJumpsIdx;
private event Initialize(string MapName)
{
local string CapsMapName;
CapsMapName = Caps(MapName);
bObjectiveIsValidForMap[0] = 1; // Freeze 500 Zeds using ice arsenal
bObjectiveIsValidForMap[1] = 0; // Complete the Weekly on Crash
bObjectiveIsValidForMap[2] = 0; // Use 4 Boomstick Jumps in a same match on Crash
bObjectiveIsValidForMap[3] = 1; // Hit 3 Zeds with a shot of HRG Ballistic Bouncer (15 times)
bObjectiveIsValidForMap[4] = 0; // Complete wave 15 on Endless Hard or higher difficulty on Crash
if (CapsMapName == "KF-CRASH")
{
bObjectiveIsValidForMap[1] = 1;
bObjectiveIsValidForMap[2] = 1;
bObjectiveIsValidForMap[4] = 1;
}
SetSeasonalEventStatsMax(FrozenZedsRequired, 0, ShotgunJumpsRequired, BallisticBouncerImpactsRequired, 0);
}
simulated event OnStatsInitialized()
{
super.OnStatsInitialized();
CheckRestartObjective(ShotgunJumpsIdx, ShotgunJumpsRequired);
}
private event GrantEventItems()
{
if (Outer.IsEventObjectiveComplete(0) &&
Outer.IsEventObjectiveComplete(1) &&
Outer.IsEventObjectiveComplete(2) &&
Outer.IsEventObjectiveComplete(3) &&
Outer.IsEventObjectiveComplete(4))
{
GrantEventItem(9568);
}
}
simulated event OnGameWon(class<GameInfo> GameClass, int Difficulty, int GameLength, bool bCoOp)
{
local int ObjIdx;
ObjIdx = 1;
// Crash weekly
if (bObjectiveIsValidForMap[ObjIdx] != 0)
{
if (GameClass == class'KFGameInfo_WeeklySurvival')
{
FinishedObjective(XmasEventIndex, ObjIdx);
}
}
CheckRestartObjective(ShotgunJumpsIdx, ShotgunJumpsRequired);
}
simulated event OnGameEnd(class<GameInfo> GameClass)
{
CheckRestartObjective(ShotgunJumpsIdx, ShotgunJumpsRequired);
}
simulated function CheckRestartObjective(int ObjectiveIndex, int ObjectiveLimit)
{
local int StatValue;
StatValue = Outer.GetSeasonalEventStatValue(ObjectiveIndex);
if (StatValue != 0 && StatValue < ObjectiveLimit)
{
ResetSeasonalEventStat(ObjectiveIndex);
}
}
simulated function OnTryCompleteObjective(int ObjectiveIndex, int EventIndex)
{
local int FrozenZedsIdx, BallisticBouncerImpactsIdx, ObjectiveLimit;
local bool bValidIdx;
FrozenZedsIdx = 0;
BallisticBouncerImpactsIdx = 3;
bValidIdx = false;
if(EventIndex == XmasEventIndex)
{
if (ObjectiveIndex == FrozenZedsIdx)
{
ObjectiveLimit = FrozenZedsRequired;
bValidIdx = true;
}
else if (ObjectiveIndex == ShotgunJumpsIdx)
{
ObjectiveLimit = ShotgunJumpsRequired;
bValidIdx = true;
}
else if (ObjectiveIndex == BallisticBouncerImpactsIdx)
{
ObjectiveLimit = BallisticBouncerImpactsRequired;
bValidIdx = true;
}
if (bValidIdx && bObjectiveIsValidForMap[ObjectiveIndex] != 0)
{
IncrementSeasonalEventStat(ObjectiveIndex, 1);
if (Outer.GetSeasonalEventStatValue(ObjectiveIndex) >= ObjectiveLimit)
{
FinishedObjective(XmasEventIndex, ObjectiveIndex);
}
}
}
}
simulated event OnWaveCompleted(class<GameInfo> GameClass, int Difficulty, int WaveNum)
{
local int ObjIdx;
// Complete wave 15 on Endless Hard or higher difficulty on Crash
ObjIdx = 4;
if (bObjectiveIsValidForMap[ObjIdx] != 0)
{
if (WaveNum >= EndlessWaveRequired && GameClass == class'KFGameInfo_Endless' && Difficulty >= `DIFFICULTY_HARD)
{
FinishedObjective(XmasEventIndex, ObjIdx);
}
}
}
simulated function OnAfflictionCaused(EAfflictionType Type)
{
local int ObjIdx;
ObjIdx = 0;
if (bObjectiveIsValidForMap[ObjIdx] != 0)
{
if (Type == AF_Freeze)
{
IncrementSeasonalEventStat(ObjIdx, 1);
if (Outer.GetSeasonalEventStatValue(ObjIdx) >= FrozenZedsRequired)
{
FinishedObjective(XmasEventIndex, ObjIdx);
}
}
}
}
static function byte GetMaxBallisticBouncerImpacts()
{
return default.HRGBBProjectilImpactsRequired;
}
defaultproperties
{
ShotgunJumpsIdx=2
FrozenZedsRequired=500
ShotgunJumpsRequired=4
BallisticBouncerImpactsRequired=30
EndlessWaveRequired=15
XmasEventIndex=SEI_Winter
HRGBBProjectilImpactsRequired=2
}

View File

@ -0,0 +1,95 @@
//=============================================================================
// KFShotgunJumpEndVolume
//=============================================================================
// Barmwich volume used tracking the start of shotgun jumps.
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFShotgunJumpEndVolume extends PhysicsVolume
placeable;
/** Objective index for the event this is tied to */
var() int ObjectiveIndex;
/** Index of the event this is tied to */
var() int EventIndex;
/** All volumes that track the begin of a shotgun jump in the area */
var() array<KFShotgunJumpStartVolume> LinkedVolumes;
var transient bool bIsDataValid;
var array<KFPlayerController> SucceededControllers;
simulated event PostBeginPlay()
{
Super.PostBeginPlay();
bIsDataValid = IsObjectiveDataValid();
if (LinkedVolumes.Length == 0)
{
`warn("Volume [" $self $"] has no linked volumes (KFShotgunJumpStartVolume).");
}
}
simulated event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal )
{
local KFPawn_Human KFPH;
local KFPlayerController KFPC;
local KFShotgunJumpStartVolume StartVolume;
KFPH = KFPawn_Human(Other);
if (KFPH == none)
{
return;
}
KFPC = KFPlayerController(KFPH.Controller);
if (KFPC == none || IsCompletedByController(KFPC))
return;
foreach LinkedVolumes(StartVolume)
{
if (StartVolume.IsPlayerTracked(KFPC))
{
StartVolume.RemoveTrackedPlayer(KFPC);
if (KFPC.bShotgunJumping)
{
SucceededControllers.AddItem(KFPC);
KFPC.ClientOnTryCompleteObjective(ObjectiveIndex, EventIndex);
}
return;
}
}
}
simulated function bool IsCompletedByController(KFPlayerController Controller)
{
local KFPlayerController Current;
foreach SucceededControllers(Current)
{
if (Controller == Current)
{
return true;
}
}
return false;
}
simulated function bool IsObjectiveDataValid()
{
return ObjectiveIndex >= 0 && ObjectiveIndex < 5 && EventIndex > SEI_None && EventIndex < SEI_MAX;
}
DefaultProperties
{
bIsDataValid = false
}

View File

@ -0,0 +1,151 @@
//=============================================================================
// KFShotgunJumpStartVolume
//=============================================================================
// Barmwich volume used tracking the start of shotgun jumps.
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFShotgunJumpStartVolume extends PhysicsVolume
placeable;
/** Max time we keep track of players */
var() float TrackedPlayerLifetime;
var Info ShotgunJumpVolumeTimer;
struct KFTrackedController
{
var KFPlayerController KFPC;
var float ExitTimestamp;
var bool bInVolume;
structdefaultproperties
{
KFPC=none
ExitTimestamp=0
bInVolume=false
}
};
var transient array<KFTrackedController> TrackedControllers;
simulated event PostBeginPlay()
{
Super.PostBeginPlay();
if ( Role < ROLE_Authority )
return;
ShotgunJumpVolumeTimer = Spawn(class'VolumeTimer', self);
}
simulated event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal )
{
local KFPawn_Human KFPH;
local KFPlayerController KFPC;
local KFTrackedController NewTrackedController;
local int i;
KFPH = KFPawn_Human(Other);
if (KFPH == none)
{
return;
}
KFPC = KFPlayerController(KFPH.Controller);
if (KFPC == none)
return;
for (i = 0; i < TrackedControllers.Length; ++i)
{
if (TrackedControllers[i].KFPC == KFPC)
{
TrackedControllers[i].bInVolume = true;
return;
}
}
// Update current time
NewTrackedController.KFPC = KFPC;
NewTrackedController.bInVolume = true;
TrackedControllers.AddItem(NewTrackedController);
}
simulated event Untouch(Actor Other)
{
local KFPawn_Human KFPH;
local int i;
local KFPlayerController KFPC;
KFPH = KFPawn_Human(Other);
if (KFPH == none)
{
return;
}
KFPC = KFPlayerController(KFPH.Controller);
if (KFPC == none)
return;
for (i = 0; i < TrackedControllers.Length; ++i)
{
if (TrackedControllers[i].KFPC == KFPC)
{
TrackedControllers[i].ExitTimestamp = WorldInfo.TimeSeconds;
TrackedControllers[i].bInVolume = false;
return;
}
}
}
function TimerPop(VolumeTimer T)
{
local int i;
for (i = TrackedControllers.Length-1; i >= 0 ; --i)
{
if (!TrackedControllers[i].bInVolume && (`TimeSince(TrackedControllers[i].ExitTimestamp) >= TrackedPlayerLifetime) )
{
TrackedControllers.Remove(i, 1);
}
}
}
function RemoveTrackedPlayer(KFPlayerController KFPC)
{
local int i;
for (i = TrackedControllers.Length-1; i >= 0 ; --i)
{
if (TrackedControllers[i].KFPC == KFPC)
{
TrackedControllers.Remove(i, 1);
return;
}
}
}
function bool IsPlayerTracked(KFPlayerController KFPC)
{
local KFTrackedController TrackedController;
foreach TrackedControllers(TrackedController)
{
if (TrackedController.KFPC == KFPC)
{
return true;
}
}
return false;
}
DefaultProperties
{
TrackedPlayerLifetime = 1.0f;
}

View File

@ -0,0 +1,120 @@
//=============================================================================
// KFWeapAttach_HRG_BallisticBouncer
//=============================================================================
//
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFWeapAttach_HRG_BallisticBouncer extends KFWeaponAttachment;
var() StaticMesh ChargeStaticMesh;
var transient StaticMeshComponent ChargeAttachment;
var transient MaterialInstanceConstant ChargeMIC;
var float MinProjPlaceholderScale;
var float MaxProjPlaceHolderScale;
var bool bIsCharging;
var bool bIsFullyCharged;
var repnotify float StartFireTime;
var int ChargeLevel;
var float ChargeRTPC;
simulated function StartFire()
{
StartFireTime = WorldInfo.TimeSeconds;
bIsCharging = true;
if (ChargeAttachment == none)
{
ChargeAttachment = new (self) class'StaticMeshComponent';
ChargeAttachment.SetStaticMesh(ChargeStaticMesh);
if (WeapMesh != none)
{
WeapMesh.AttachComponentToSocket(ChargeAttachment, 'MuzzleFlash');
}
else
{
AttachComponent(ChargeAttachment);
}
}
if (ChargeAttachment != none)
{
ChargeAttachment.SetScale(MinProjPlaceholderScale);
ChargeAttachment.SetHidden(false);
}
}
simulated event Tick(float DeltaTime)
{
local float ChargeValue;
if(bIsCharging && !bIsFullyCharged)
{
ChargeValue = FMin(class'KFWeap_HRG_BallisticBouncer'.default.MaxChargeTime, WorldInfo.TimeSeconds - StartFireTime) / class'KFWeap_HRG_BallisticBouncer'.default.MaxChargeTime;
if (ChargeValue >= 1.0f)
{
bIsFullyCharged = true;
ChargeValue = 1.0f;
}
if( ChargeAttachment != none)
{
ChargeAttachment.SetScale(MinProjPlaceholderScale + (MaxProjPlaceHolderScale - MinProjPlaceholderScale) * ChargeValue);
}
}
Super.Tick(DeltaTime);
}
simulated function FirstPersonFireEffects(Weapon W, vector HitLocation)
{
super.FirstPersonFireEffects(W, HitLocation);
if (ChargeAttachment != none)
{
ChargeAttachment.SetHidden(true);
}
}
simulated function bool ThirdPersonFireEffects(vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte)
{
bIsCharging = false;
bIsFullyCharged = false;
ChargeRTPC=0;
if (ChargeAttachment != none)
{
ChargeAttachment.SetHidden(true);
}
return Super.ThirdPersonFireEffects(HitLocation, P, ThirdPersonAnimRateByte);
}
simulated function CauseMuzzleFlash(byte FiringMode)
{
if (MuzzleFlash == None && MuzzleFlashTemplate != None)
{
AttachMuzzleFlash();
}
Super.CauseMuzzleFlash(FiringMode);
}
defaultproperties
{
ChargeRTPC=0
MuzzleFlashTemplate=KFMuzzleFlash'WEP_Mine_Reconstructor_Arch.Wep_Mine_Reconstructor_MuzzleFlash_3P'
ChargeStaticMesh = StaticMesh'WEP_HRG_BallisticBouncer_EMIT.HRG_BallisticBouncer_ball_MESH'
MinProjPlaceholderScale = 2.0f;
MaxProjPlaceholderScale = 3.0f;
}

View File

@ -196,7 +196,7 @@ defaultproperties
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Bludgeon_Doshinegun_Shot'
FireInterval(DEFAULT_FIREMODE)=+0.2
Spread(DEFAULT_FIREMODE)=0.015
InstantHitDamage(DEFAULT_FIREMODE)=60.0 //55.0 //60.0
InstantHitDamage(DEFAULT_FIREMODE)=80 //60.0 //55.0 //60.0
FireOffset=(X=30,Y=4.5,Z=-5)
// ALT_FIREMODE
@ -206,7 +206,7 @@ defaultproperties
WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Dosh'
InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Bludgeon_Doshinegun_Shot'
FireInterval(ALTFIRE_FIREMODE)=+0.2
InstantHitDamage(ALTFIRE_FIREMODE)=60.0 //55.0 //60.0
InstantHitDamage(ALTFIRE_FIREMODE)=80 //60.0 //55.0 //60.0
Spread(ALTFIRE_FIREMODE)=0.015
// BASH_FIREMODE
@ -239,7 +239,7 @@ defaultproperties
WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.47f), (Stat=EWUS_Damage1, Scale=1.47f), (Stat=EWUS_Weight, Add=2)))
WeaponUpgrades[3]=(Stats=((Stat=EWUS_Damage0, Scale=1.70f), (Stat=EWUS_Damage1, Scale=1.70f), (Stat=EWUS_Weight, Add=3)))
DoshCost = 20; //25;
DoshCost = 10 //20; //25;
bUsesSecondaryAmmoAltHUD=true
bAllowClientAmmoTracking=false
bIsBeingDropped=false

View File

@ -142,7 +142,7 @@ defaultproperties
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Grenade_GravityImploder'
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_GravityImploderImpact'
InstantHitDamage(DEFAULT_FIREMODE)=150
FireInterval(DEFAULT_FIREMODE)=1.33 //45 RPM
FireInterval(DEFAULT_FIREMODE)=1 // 60 RPM //1.33 //45 RPM
Spread(DEFAULT_FIREMODE)=0.02 //0
PenetrationPower(DEFAULT_FIREMODE)=0
FireOffset=(X=25,Y=3.0,Z=-2.5)
@ -152,7 +152,7 @@ defaultproperties
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile
WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Grenade_GravityImploderAlt'
FireInterval(ALTFIRE_FIREMODE)=1.33 //45 RPM
FireInterval(ALTFIRE_FIREMODE)=1 // 60 RPM //1.33 //45 RPM
InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_GravityImploderImpactAlt'
InstantHitDamage(ALTFIRE_FIREMODE)=200
Spread(ALTFIRE_FIREMODE)=0.02 //0.0085

View File

@ -0,0 +1,766 @@
//=============================================================================
// KFWeap_HRG_BallisticBouncer
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFWeap_HRG_BallisticBouncer extends KFWeapon;
//Props related to charging the weapon
var float MaxChargeTime;
var float ValueIncreaseTime;
var float DmgIncreasePerCharge;
var float AOEIncreasePerCharge;
var float IncapIncreasePerCharge;
var int AmmoIncreasePerCharge;
var transient float ChargeTime;
var transient float ConsumeAmmoTime;
var transient float MaxChargeLevel;
var ParticleSystem ChargedEffect;
var transient ParticleSystemComponent FullyChargedPSC;
var transient bool bIsFullyCharged;
var const WeaponFireSndInfo FullyChargedSound;
var float FullChargedTimerInterval;
var float FXScalingFactorByCharge, ChargePercentage;
var float MinScale, MaxScale;
var int MaxDamageByCharge;
var int MinDamageByCharge;
const SecondaryFireAnim = 'Alt_Fire';
const SecondaryFireIronAnim = 'Alt_Fire_Iron';
const SecondaryFireAnimEmpty = 'Alt_Fire_Empty';
const SecondaryFireIronAnimEmpty = 'Alt_Fire_Iron_Empty';
var bool bHasToLaunchEmptyAnim;
var SkelControlSingleBone Control;
var bool bBlocked;
var() StaticMesh ChargeStaticMesh;
var transient StaticMeshComponent ChargeAttachment;
var transient MaterialInstanceConstant ChargeMIC;
var float MinProjPlaceholderScale;
var float MaxProjPlaceHolderScale;
Replication
{
if(Role == Role_Authority && bNetDirty)
ChargeTime;
}
simulated event PostInitAnimTree( SkeletalMeshComponent SkelComp )
{
local vector vec;
local float fPercentage;
super.PostInitAnimTree( SkelComp );
Control = SkelControlSingleBone( SkelComp.FindSkelControl('AmmoControl') );
if( Control != none )
{
Control.SetSkelControlActive( true );
}
//from 0 to -8
// If AmmoCount is being replicated, don't allow the client to modify it here
fPercentage = FMin((float(AmmoCount[0])/(MagazineCapacity[0])), 1);
vec.X = Control.BoneTranslation.X;
vec.Y = Control.BoneTranslation.Y;
vec.Z = Lerp(-8, 0, fPercentage);
Control.BoneTranslation = vec;
}
/**
* @see Weapon::ConsumeAmmo
*/
simulated function ConsumeAmmo(byte FireModeNum)
{
local vector vec;
local float fPercentage;
//from 0 to -8
// If AmmoCount is being replicated, don't allow the client to modify it here
if (Role == ROLE_Authority)
{
fPercentage = FMin(float(AmmoCount[0])/(MagazineCapacity[0]), 1);
super.ConsumeAmmo(FireModeNum);
if(Control != none)
{
vec.X = Control.BoneTranslation.X;
vec.Y = Control.BoneTranslation.Y;
vec.Z = Lerp(-8, 0, fPercentage);
Control.BoneTranslation = vec;
}
//Notify the client about the new percentage as the client is not tracking the ammo
ClientUpdateVisualAmmo(fPercentage);
}
}
reliable client function ClientUpdateVisualAmmo(float BoneControlTranslation)
{
local vector vec;
if ( Role < ROLE_Authority && Control != none )
{
vec.X = Control.BoneTranslation.X;
vec.Y = Control.BoneTranslation.Y;
vec.Z = Lerp(-8, 0, BoneControlTranslation);
Control.BoneTranslation = vec;
}
}
simulated function StartFire(byte FiremodeNum)
{
if (IsTimerActive('RefireCheckTimer') || bBlocked)
{
return;
}
if(Role != Role_Authority && FireModeNum == DEFAULT_FIREMODE && HasAmmo(DEFAULT_FIREMODE))
{
bBlocked = true;
if(IsTimerActive(nameof(UnlockClientFire)))
{
ClearTimer(nameof(UnlockClientFire));
}
}
super.StartFire(FiremodeNum);
if ( PendingFire(RELOAD_FIREMODE) && Role != Role_Authority)
{
bBlocked = false;
}
}
simulated function RefireCheckTimer()
{
Super.RefireCheckTimer();
if(bBlocked && Role != Role_Authority)
{
SetTimer(0.25f , false, nameof(UnlockClientFire));
}
}
reliable client function UnlockClientFire()
{
bBlocked = false;
}
simulated function OnStartFire()
{
local KFPawn PawnInst;
PawnInst = KFPawn(Instigator);
if (PawnInst != none)
{
PawnInst.OnStartFire();
}
}
simulated function FireAmmunition()
{
// Let the accuracy tracking system know that we fired
HandleWeaponShotTaken(CurrentFireMode);
// Handle the different fire types
switch (WeaponFireTypes[CurrentFireMode])
{
case EWFT_InstantHit:
// Launch a projectile if we are in zed time, and this weapon has a projectile to launch for this mode
if (`IsInZedTime(self) && WeaponProjectiles[CurrentFireMode] != none )
{
ProjectileFire();
}
else
{
InstantFireClient();
}
break;
case EWFT_Projectile:
ProjectileFire();
break;
case EWFT_Custom:
CustomFire();
break;
}
//// If we're firing without charging, still consume one ammo
if (GetChargeLevel() < 1)
{
ConsumeAmmo(CurrentFireMode);
}
NotifyWeaponFired(CurrentFireMode);
// Play fire effects now (don't wait for WeaponFired to replicate)
PlayFireEffects(CurrentFireMode, vect(0, 0, 0));
}
simulated function ANIMNOTIFY_FILLMAG()
{
local vector vec;
if (Control != none)
{
vec.X = Control.BoneTranslation.X;
vec.Y = Control.BoneTranslation.Y;
vec.Z = 0;
Control.BoneTranslation = vec;
}
}
/**
* @see Weapon::HasAmmo
*/
simulated event bool HasAmmo( byte FireModeNum, optional int Amount )
{
local KFPerk InstigatorPerk;
// we can always do a melee attack
if( FireModeNum == BASH_FIREMODE )
{
return TRUE;
}
else if ( FireModeNum == RELOAD_FIREMODE )
{
return CanReload();
}
else if ( FireModeNum == GRENADE_FIREMODE )
{
if( KFInventoryManager(InvManager) != none )
{
return KFInventoryManager(InvManager).HasGrenadeAmmo(Amount);
}
}
InstigatorPerk = GetPerk();
if( InstigatorPerk != none && InstigatorPerk.GetIsUberAmmoActive( self ) )
{
return true;
}
// If passed in ammo isn't set, use default ammo cost.
if( Amount == 0 )
{
Amount = AmmoCost[FireModeNum];
}
return AmmoCount[GetAmmoType(FireModeNum)] >= Amount;
}
simulated state MineReconstructorCharge extends WeaponFiring
{
//For minimal code purposes, I'll directly call global.FireAmmunition after charging is released
simulated function FireAmmunition()
{
return;
}
//Store start fire time so we don't have to timer this
simulated event BeginState(Name PreviousStateName)
{
local KFPerk InstigatorPerk;
super.BeginState(PreviousStateName);
InstigatorPerk = GetPerk();
if( InstigatorPerk != none )
{
SetZedTimeResist( InstigatorPerk.GetZedTimeModifier(self) );
}
ChargeTime = 0;
ConsumeAmmoTime = 0;
MaxChargeLevel = int(MaxChargeTime / ValueIncreaseTime);
if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone)
{
if (ChargeAttachment == none)
{
ChargeAttachment = new (self) class'StaticMeshComponent';
// ChargeAttachment.SetActorCollision(false, false);
ChargeAttachment.SetStaticMesh(ChargeStaticMesh);
// ChargeAttachment.SetShadowParent(Mesh);
ChargeMIC = ChargeAttachment.CreateAndSetMaterialInstanceConstant(0);
MySkelMesh.AttachComponentToSocket(ChargeAttachment, 'MuzzleFlash');
ChargeAttachment.SetHidden(true);
}
else
{
ChargeAttachment.SetStaticMesh(ChargeStaticMesh);
}
}
bIsFullyCharged = false;
global.OnStartFire();
ChargeTime = 0;
FXScalingFactorByCharge = 0;
}
simulated function bool ShouldRefire()
{
// ignore how much ammo is left (super/global counts ammo)
return StillFiring(CurrentFireMode);
}
simulated event Tick(float DeltaTime)
{
local float ChargeRTPC;
local float InstantHitDamageValue;
local float NewScale;
global.Tick(DeltaTime);
if (ChargeAttachment != none && ChargeAttachment.StaticMesh == none)
{
ChargeAttachment.SetStaticMesh(ChargeStaticMesh);
}
if(bIsFullyCharged) return;
// Don't charge unless we're holding down the button
if (PendingFire(CurrentFireMode))
{
ConsumeAmmoTime += DeltaTime;
}
if (bIsFullyCharged)
{
if (ConsumeAmmoTime >= FullChargedTimerInterval)
{
ConsumeAmmo(DEFAULT_FIREMODE);
ConsumeAmmoTime -= FullChargedTimerInterval;
}
return;
}
// Don't charge unless we're holding down the button
if (PendingFire(CurrentFireMode))
{
if(Role == Role_Authority && !bIsFullyCharged)
{
ChargeTime += DeltaTime;
bNetDirty = true;
}
ChargePercentage = FMin(ChargeTime / MaxChargeTime, 1);
if (ChargeAttachment != none && !bIsFullyCharged)
{
FXScalingFactorByCharge = FMin(Lerp(MinScale, MaxScale, ChargeTime / MaxChargeTime), MaxScale);
if(ChargePercentage < 0.1f)
{
InstantHitDamageValue = Lerp(MinDamageByCharge, MaxDamageByCharge, 0.1f);
}
else
{
InstantHitDamageValue = Lerp(MinDamageByCharge, MaxDamageByCharge, ChargePercentage);
}
InstantHitDamage[DEFAULT_FIREMODE] = InstantHitDamageValue;
NewScale = Lerp(MinProjPlaceholderScale, MaxProjPlaceholderScale, ChargePercentage);
ChargeAttachment.SetHidden(false);
ChargeAttachment.SetScale( NewScale );
if (ChargeMIC != none)
{
// Change Color
ChargeMIC.SetScalarParameterValue('Charge', ChargePercentage);
}
}
}
ChargeRTPC = FMin(ChargeTime / MaxChargeTime, 1.f);
KFPawn(Instigator).SetWeaponComponentRTPCValue("Weapon_Charge", ChargeRTPC); //For looping component
Instigator.SetRTPCValue('Weapon_Charge', ChargeRTPC); //For one-shot sounds
if (ConsumeAmmoTime >= ValueIncreaseTime && !bIsFullyCharged)
{
ConsumeAmmo(DEFAULT_FIREMODE);
ConsumeAmmoTime -= ValueIncreaseTime;
}
if (ChargeTime >= MaxChargeTime || !HasAmmo(DEFAULT_FIREMODE))
{
bIsFullyCharged = true;
if(( Instigator.Role != ROLE_Authority ) || WorldInfo.NetMode == NM_Standalone)
{
if (FullyChargedPSC == none)
{
FullyChargedPSC = new(self) class'ParticleSystemComponent';
if(MySkelMesh != none)
{
MySkelMesh.AttachComponentToSocket(FullyChargedPSC, 'MuzzleFlash');
}
else
{
AttachComponent(FullyChargedPSC);
}
}
else
{
FullyChargedPSC.ActivateSystem();
}
FullyChargedPSC.SetTemplate(ChargedEffect);
KFPawn(Instigator).SetWeaponAmbientSound(FullyChargedSound.DefaultCue, FullyChargedSound.FirstPersonCue);
}
}
}
//Now that we're done charging, directly call FireAmmunition. This will handle the actual projectile fire and scaling.
simulated event EndState(Name NextStateName)
{
if(Role == Role_Authority)
{
UnlockClientFire();
}
ClearZedTimeResist();
ClearPendingFire(CurrentFireMode);
ClearTimer(nameof(RefireCheckTimer));
KFPawn(Instigator).bHasStartedFire = false;
KFPawn(Instigator).bNetDirty = true;
if (ChargeAttachment != none)
{
ChargeAttachment.SetHidden(true);
ChargeAttachment.SetScale(MinProjPlaceholderScale);
}
if (FullyChargedPSC != none)
{
FullyChargedPSC.DeactivateSystem();
}
KFPawn(Instigator).SetWeaponAmbientSound(none);
}
simulated function HandleFinishedFiring()
{
global.FireAmmunition();
if (bPlayingLoopingFireAnim)
{
StopLoopingFireEffects(CurrentFireMode);
}
SetTimer(0.1f, false, 'Timer_StopFireEffects');
NotifyWeaponFinishedFiring(CurrentFireMode);
super.HandleFinishedFiring();
//`Log("ChargePercentage"@ChargePercentage);
}
simulated function PutDownWeapon()
{
global.FireAmmunition();
if (bPlayingLoopingFireAnim)
{
StopLoopingFireEffects(CurrentFireMode);
}
SetTimer(0.1f, false, 'Timer_StopFireEffects');
if (ChargeAttachment != none)
{
ChargeAttachment.SetHidden(true);
ChargeAttachment.SetScale(MinProjPlaceholderScale);
}
NotifyWeaponFinishedFiring(CurrentFireMode);
if(Role == Role_Authority)
{
UnlockClientFire();
}
super.PutDownWeapon();
}
}
simulated function StopFireEffects(byte FireModeNum)
{
Super.StopFireEffects(FireModeNum);
if (ChargeAttachment != none)
{
ChargeAttachment.SetStaticMesh(none); // if we don't do this it doesn't work ?!, when starting fire it assigns again so no problem..
ChargeAttachment.SetHidden(true);
ChargeAttachment.SetScale(MinProjPlaceholderScale);
}
}
// Placing the actual Weapon Firing end state here since we need it to happen at the end of the actual firing loop.
simulated function Timer_StopFireEffects()
{
// Simulate weapon firing effects on the local client
if (WorldInfo.NetMode == NM_Client)
{
Instigator.WeaponStoppedFiring(self, false);
if (ChargeAttachment != none)
{
ChargeAttachment.SetHidden(true);
}
if (FullyChargedPSC != none)
{
FullyChargedPSC.DeactivateSystem();
}
}
ClearFlashCount();
ClearFlashLocation();
}
simulated state Active
{
simulated function BeginState(name PreviousStateName)
{
Super.BeginState(PreviousStateName);
if(Role == Role_Authority)
{
UnlockClientFire();
}
}
}
simulated function KFProjectile SpawnProjectile(class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir)
{
local KFProj_HRG_BallisticBouncer BouncingProj;
BouncingProj = KFProj_HRG_BallisticBouncer(super.SpawnProjectile(KFProjClass, RealStartLoc, AimDir));
//Calc and set scaling values
if (BouncingProj != none)
{
ChargePercentage = FMax(0.1, ChargePercentage);
FXScalingFactorByCharge = FMax(0.1, FXScalingFactorByCharge);
BouncingProj.SetInheritedScale(FXScalingFactorByCharge, ChargePercentage);
return BouncingProj;
}
return none;
}
simulated function CauseMuzzleFlash(byte FireModeNum)
{
local vector vec;
if (MuzzleFlash == None)
{
AttachMuzzleFlash();
}
if (MuzzleFlash != none)
{
vec.X = ChargePercentage;
vec.Y = ChargePercentage;
vec.Z = ChargePercentage;
MuzzleFlash.MuzzleFlash.PSC.SetVectorParameter(name("Charge"), vec);
MuzzleFlash.CauseMuzzleFlash(FireModeNum);
}
super.CauseMuzzleFlash(FireModeNum);
}
simulated function int GetChargeLevel()
{
return Min(ChargeTime / ValueIncreaseTime, MaxChargeLevel);
}
/****************************************************************
PAWN ADJUST DAMAGE
****************************************************************/
// increase the instant hit damage based on the charge level
simulated function int GetModifiedDamage(byte FireModeNum, optional vector RayDir)
{
local int ModifiedDamage;
ModifiedDamage = super.GetModifiedDamage(FireModeNum, RayDir);
return ModifiedDamage;
}
state WeaponSingleFiring
{
/** Get whether we should play the reload anim as well or not */
simulated function name GetWeaponFireAnim(byte FireModeNum)
{
if(bUsingSights)
{
return (bHasToLaunchEmptyAnim == false) ? SecondaryFireIronAnim : SecondaryFireIronAnimEmpty;
}
else
{
return (bHasToLaunchEmptyAnim == false) ? SecondaryFireIronAnim : SecondaryFireIronAnimEmpty;
}
}
}
defaultproperties
{
//Gameplay Props
MaxChargeTime=0.6
AmmoIncreasePerCharge=1
ValueIncreaseTime=0.1
//FOR LERPING DAMANGE
MaxDamageByCharge=600
MinDamageByCharge=60
// FOV
Meshfov=80
MeshIronSightFOV=65 //52
PlayerIronSightFOV=50 //80
// Depth of field
DOF_FG_FocalRadius=150
DOF_FG_MaxNearBlurSize=1
// Content
PackageKey="HRG_BallisticBouncer"
FirstPersonMeshName="wep_1p_hrg_ballisticbouncer_mesh.Wep_1stP_HRG_BallisticBouncer_Rig"
FirstPersonAnimSetNames(0)="wep_1p_hrg_ballisticbouncer_anim.Wep_1stP_BallisticBouncer_Anim"
PickupMeshName="wep_3p_hrg_ballisticbouncer_mesh.Wep_3rdP_HRG_BallisticBouncer_Pickup"
AttachmentArchetypeName="wep_hrg_ballisticbouncer_arch.Wep_HRG_BallisticBouncer_3P"
MuzzleFlashTemplateName="WEP_HRG_BallisticBouncer_ARCH.Wep_HRG_BallisticBouncer_MuzzleFlash"
Begin Object Name=FirstPersonMesh
// new anim tree with skelcontrol to rotate cylinders
AnimTreeTemplate=AnimTree'WEP_HRG_BallisticBouncer_ARCH.WEP_1stP_Animtree_HRG_BallisticBouncer'
End Object
// Zooming/Position
PlayerViewOffset=(X=0.0,Y=12,Z=-1)
IronSightPosition=(X=0,Y=0,Z=0)
// Controls the rotation when Hans(the bastard) grabs you
QuickWeaponDownRotation=(Pitch=-19192,Yaw=-11500,Roll=16384) // (Pitch=-19192,Yaw=-11000,Roll=16384)
// Ammo
MagazineCapacity[0]=18
SpareAmmoCapacity[0]=162
InitialSpareMags[0]=3 //2
AmmoPickupScale[0]=1.5 //1 //0.75
bCanBeReloaded=true
bReloadFromMagazine=true
// Recoil
maxRecoilPitch=180
minRecoilPitch=140
maxRecoilYaw=150
minRecoilYaw=-150
RecoilRate=0.085
RecoilMaxYawLimit=500
RecoilMinYawLimit=65035
RecoilMaxPitchLimit=900
RecoilMinPitchLimit=65035
RecoilISMaxYawLimit=75
RecoilISMinYawLimit=65460
RecoilISMaxPitchLimit=375
RecoilISMinPitchLimit=65460
RecoilViewRotationScale=0.25
IronSightMeshFOVCompensationScale=1.5
HippedRecoilModifier=1.5
// Inventory
InventorySize=5
GroupPriority=80 //75
WeaponSelectTexture=Texture2D'wep_ui_hrg_ballisticbouncer_tex.UI_WeaponSelect_HRG_BallisticBouncer'
// DEFAULT_FIREMODE
//FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Grenade' //@TODO: Replace me
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunSingle'
FiringStatesArray(DEFAULT_FIREMODE)=MineReconstructorCharge
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_HRG_BallisticBouncer'
FireInterval(DEFAULT_FIREMODE)=+0.223 //+0.33
InstantHitDamage(DEFAULT_FIREMODE)=100
PenetrationPower(DEFAULT_FIREMODE)=0.0;
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Bludgeon_HRG_BallisticBouncer_Shot'
FireOffset=(X=39,Y=4.5,Z=-10)
// ALT_FIREMODE
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_BallisticBouncer'
InstantHitDamage(BASH_FIREMODE)=27
// Fire Effects
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_3P_Start', FirstPersonCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_1P_Start')
WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_3P_Shoot', FirstPersonCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_1P_Shoot')
FullyChargedSound=(DefaultCue = AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Charged_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_Charged')
WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_HRG_BallisticBouncer.Play_WEP_HRG_BallisticBouncer_DryFire'
// Advanced (High RPM) Fire Effects
bLoopingFireAnim(DEFAULT_FIREMODE)=true
bLoopingFireSnd(DEFAULT_FIREMODE)=true
SingleFireSoundIndex=FIREMODE_NONE
// Attachments
bHasIronSights=true
bHasFlashlight=false
AssociatedPerkClasses(0)= class'KFPerk_Support'
WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Weak_Recoil'
ChargedEffect=ParticleSystem'WEP_HRG_BallisticBouncer_EMIT.FX_Mine_HRG_BallisticBouncer_FullCharge'
FullChargedTimerInterval=2.0f
// Weapon Upgrade stat boosts
//WeaponUpgrades[1]=(IncrementDamage=1.1f,IncrementWeight=1)
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Weight, Add=1)))
WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Weight, Add=2)))
FXScalingFactorByCharge = 0;
MinScale=0.5
MaxScale=1.5
bBlocked = false;
bAllowClientAmmoTracking = false;
ChargeStaticMesh = StaticMesh'WEP_HRG_BallisticBouncer_EMIT.HRG_BallisticBouncer_ball_MESH'
MinProjPlaceholderScale = 2.0f;
MaxProjPlaceholderScale = 3.0f;
}

View File

@ -0,0 +1,210 @@
//=============================================================================
// KFWeap_HRG_MedicMissile
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFWeap_HRG_MedicMissile extends KFWeap_GrenadeLauncher_Base;
/** Back blash explosion template. */
var() GameExplosion ExplosionTemplate;
/** Holds an offest for spawning back blast effects. */
var() vector BackBlastOffset;
/** Fires a projectile, but also does the back blast */
simulated function CustomFire()
{
local KFExplosionActorReplicated ExploActor;
local vector SpawnLoc;
local rotator SpawnRot;
ProjectileFire();
if ( Instigator.Role < ROLE_Authority )
{
return;
}
GetBackBlastLocationAndRotation(SpawnLoc, SpawnRot);
// explode using the given template
ExploActor = Spawn(class'KFExplosionActorReplicated', self,, SpawnLoc, SpawnRot,, true);
if (ExploActor != None)
{
ExploActor.InstigatorController = Instigator.Controller;
ExploActor.Instigator = Instigator;
// So we get backblash decal from this explosion
ExploActor.bTraceForHitActorWhenDirectionalExplosion = true;
ExploActor.Explode(ExplosionTemplate, vector(SpawnRot));
}
if ( bDebug )
{
DrawDebugCone(SpawnLoc, vector(SpawnRot), ExplosionTemplate.DamageRadius, ExplosionTemplate.DirectionalExplosionAngleDeg * DegToRad,
ExplosionTemplate.DirectionalExplosionAngleDeg * DegToRad, 16, MakeColor(64,64,255,0), TRUE);
}
}
/**
* This function returns the world location for spawning back blast and the direction to send the back blast in
*/
simulated function GetBackBlastLocationAndRotation(out vector BlastLocation, out rotator BlastRotation)
{
local vector X, Y, Z;
local Rotator ViewRotation;
if( Instigator != none )
{
if( bUsingSights )
{
ViewRotation = Instigator.GetViewRotation();
// Add in the free-aim rotation
if ( KFPlayerController(Instigator.Controller) != None )
{
ViewRotation += KFPlayerController(Instigator.Controller).WeaponBufferRotation;
}
GetAxes(ViewRotation, X, Y, Z);
BlastRotation = Rotator(Vector(ViewRotation) * -1);
BlastLocation = Instigator.GetWeaponStartTraceLocation() + X * BackBlastOffset.X;
}
else
{
ViewRotation = Instigator.GetViewRotation();
// Add in the free-aim rotation
if ( KFPlayerController(Instigator.Controller) != None )
{
ViewRotation += KFPlayerController(Instigator.Controller).WeaponBufferRotation;
}
BlastRotation = Rotator(Vector(ViewRotation) * -1);
BlastLocation = Instigator.GetPawnViewLocation() + (BackBlastOffset >> ViewRotation);
}
}
}
/** Locks the bolt bone in place to the open position (Called by animnotify) */
simulated function ANIMNOTIFY_LockBolt()
{
// Consider us empty after every shot so the rocket gets hidden
EmptyMagBlendNode.SetBlendTarget(1, 0);
}
defaultproperties
{
ForceReloadTime=0.4f
// Inventory
InventoryGroup=IG_Primary
GroupPriority=100
InventorySize=7
WeaponSelectTexture=Texture2D'WEP_UI_HRG_MedicMissile_TEX.UI_WeaponSelect_HRG_MedicMissile'
// FOV
MeshFOV=75
MeshIronSightFOV=65
PlayerIronSightFOV=70
PlayerSprintFOV=95
// Depth of field
DOF_FG_FocalRadius=50
DOF_FG_MaxNearBlurSize=2.5
// Zooming/Position
PlayerViewOffset=(X=10.0,Y=10,Z=-2)
FastZoomOutTime=0.2
// Content
PackageKey="HRG_MedicMissile"
FirstPersonMeshName="WEP_1P_HRG_MedicMissile_MESH.Wep_1stP_HRG_MedicMissile_Rig"
FirstPersonAnimSetNames(0)="WEP_1P_HRG_MedicMissile_ANIM.Wep_1stP_HRG_MedicMissile_Anim"
PickupMeshName="WEP_3P_HRG_MedicMissile_MESH.Wep_HRG_MedicMissile_Pickup"
AttachmentArchetypeName="WEP_HRG_MedicMissile_ARCH.Wep_HRG_MedicMissile_3P"
MuzzleFlashTemplateName="WEP_HRG_MedicMissile_ARCH.Wep_HRG_MedicMissile_MuzzleFlash"
// Zooming/Position
IronSightPosition=(X=0,Y=0,Z=0)
// Ammo
MagazineCapacity[0]=1
SpareAmmoCapacity[0]=22
InitialSpareMags[0]=6
AmmoPickupScale[0]=4.0
bCanBeReloaded=true
bReloadFromMagazine=true
// Recoil
maxRecoilPitch=800
minRecoilPitch=675
maxRecoilYaw=400
minRecoilYaw=-400
RecoilRate=0.085
RecoilBlendOutRatio=0.35
RecoilMaxYawLimit=500
RecoilMinYawLimit=65035
RecoilMaxPitchLimit=1500
RecoilMinPitchLimit=64785
RecoilISMaxYawLimit=50
RecoilISMinYawLimit=65485
RecoilISMaxPitchLimit=500
RecoilISMinPitchLimit=65485
RecoilViewRotationScale=0.8
FallingRecoilModifier=1.5
HippedRecoilModifier=1.25
// DEFAULT_FIREMODE
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'UI_FireModes_TEX.UI_FireModeSelect_Rocket'
FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFireAndReload
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Custom
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Rocket_HRG_MedicMissile'
FireInterval(DEFAULT_FIREMODE)=+0.25
InstantHitDamage(DEFAULT_FIREMODE)=100.0
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_MedicMissile'
Spread(DEFAULT_FIREMODE)=0.025
FireOffset=(X=20,Y=4.0,Z=-3)
BackBlastOffset=(X=-20,Y=4.0,Z=-3)
// ALT_FIREMODE
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None
// Back blash explosion settings. Using archetype so that clients can serialize the content
// without loading the 1st person weapon content (avoid 'Begin Object')!
ExplosionTemplate=KFGameExplosion'WEP_HRG_MedicMissile_ARCH.Wep_HRG_MedicMissile_BackBlastExplosion'
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_MedicMissile'
InstantHitDamage(BASH_FIREMODE)=27
// Fire Effects
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_3P_Shoot', FirstPersonCue=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_1P_Shoot')
//@todo: add akevent when we have it
WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_HRG_MedicMissile.Play_WEP_HRG_MedicMissile_DryFire'
// Animation
bHasFireLastAnims=true
IdleFidgetAnims=(Guncheck_v1, Guncheck_v2)
BonesToLockOnEmpty=(RW_Grenade1)
// Attachments
bHasIronSights=true
bHasFlashlight=false
AssociatedPerkClasses(0)=class'KFPerk_FieldMedic'
WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot'
// Weapon Upgrade stat boosts
//WeaponUpgrades[1]=(IncrementDamage=1.1f,IncrementWeight=1)
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.1f), (Stat=EWUS_Weight, Add=1)))
}

View File

@ -76,6 +76,7 @@ var Array<bool> HolographicSightUseDefaultByChargeLevel;
simulated function PostBeginPlay()
{
CurrentChargeLevel=0;
if( WeaponMICs.Length > `SONICGUN_MIC_SIGHT_INDEX )
{
if (!HolographicSightUseDefaultByChargeLevel[0])
@ -94,7 +95,6 @@ simulated function PostBeginPlay()
simulated function AltFireMode()
{
// skip super
if (!Instigator.IsLocallyControlled())
{
return;
@ -127,19 +127,6 @@ simulated function name GetReloadAnimName(bool bTacticalReload)
}
}
simulated function StartFire(byte FireModeNum)
{
if (FireModeNum == ALTFIRE_FIREMODE)
{
if (!IsCanIncrementCharge())
{
return;
}
}
super.StartFire(FireModeNum);
}
/*********************************************************************************************
* State WeaponSonicGunCharging
* The weapon is in this state while detonating a charge
@ -184,7 +171,7 @@ simulated state WeaponSonicGunSingleFiring extends WeaponSingleFiring
local vector UsedKickMomentum;
// Push the player back when they fire a fully charged sonic blast
if (Instigator != none && CurrentChargeLevel == 1 ) //2
if (Instigator != none && CurrentChargeLevel == MaxChargeLevel )
{
UsedKickMomentum.X = -FullyChargedKickMomentum;
@ -208,21 +195,26 @@ simulated state WeaponSonicGunSingleFiring extends WeaponSingleFiring
simulated function FireAmmunition()
{
super.FireAmmunition();
CurrentChargeLevel=0;
if (CurrentFireMode == ALTFIRE_FIREMODE)
{
// Instant shoot: always use max level
CurrentChargeLevel = MaxChargeLevel;
}
if( WeaponMICs.Length > `SONICGUN_MIC_SIGHT_INDEX )
{
if (!HolographicSightUseDefaultByChargeLevel[0])
if (!HolographicSightUseDefaultByChargeLevel[CurrentChargeLevel])
{
WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].SetVectorParameterValue('Vector_Center_Color_A', HolographicSightByChargeLevel[0]);
WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].SetVectorParameterValue('Vector_Scanline_Color_Mult', HolographicSightScanlineByChargeLevel[0]);
WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].SetVectorParameterValue('Vector_Center_Color_A', HolographicSightByChargeLevel[CurrentChargeLevel]);
WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].SetVectorParameterValue('Vector_Scanline_Color_Mult', HolographicSightScanlineByChargeLevel[CurrentChargeLevel]);
}
else
{
else {
WeaponMICs[`SONICGUN_MIC_SIGHT_INDEX].ClearParameterValues();
}
}
super.FireAmmunition();
CurrentChargeLevel=0;
}
}
@ -319,7 +311,7 @@ simulated function float GetForceReloadDelay()
simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir )
{
local KFProjectile SpawnedProjectile;
local int ProjDamage;
local int ProjDamage;
// Spawn projectile
SpawnedProjectile = Spawn( KFProjClass, Self,, RealStartLoc,,,true);
@ -329,9 +321,9 @@ simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass
// these properties are replicated via TakeHitInfo
if ( InstantHitDamage.Length > CurrentFireMode && InstantHitDamageTypes.Length > CurrentFireMode )
{
InstantHitDamage[DEFAULT_FIREMODE] = SonicBlastDamageByChargeLevel[CurrentChargeLevel];
InstantHitMomentum[DEFAULT_FIREMODE]=SonicBlastMomentumByChargeLevel[CurrentChargeLevel];
InstantHitDamageTypes[DEFAULT_FIREMODE]=SonicBlastDamageTypeByChargeLevel[CurrentChargeLevel];
InstantHitDamage[CurrentFiremode] = SonicBlastDamageByChargeLevel[CurrentChargeLevel];
InstantHitMomentum[CurrentFiremode]=SonicBlastMomentumByChargeLevel[CurrentChargeLevel];
InstantHitDamageTypes[CurrentFiremode]=SonicBlastDamageTypeByChargeLevel[CurrentChargeLevel];
ProjDamage = GetModifiedDamage(CurrentFireMode);
SpawnedProjectile.Damage = ProjDamage;
@ -341,7 +333,7 @@ simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass
// Set the penetration power for this projectile
// because of clientside hit detection, we need two variables --
// one that replicates on init and one that updates but doesn't replicate
PenetrationPower[DEFAULT_FIREMODE]=SonicBlastPenetrationPowerByChargeLevel[CurrentChargeLevel];
PenetrationPower[CurrentFireMode]=SonicBlastPenetrationPowerByChargeLevel[CurrentChargeLevel];
SpawnedProjectile.InitialPenetrationPower = GetInitialPenetrationPower(CurrentFireMode);
SpawnedProjectile.PenetrationPower = SpawnedProjectile.InitialPenetrationPower;
@ -356,7 +348,7 @@ simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass
//Overriding to make KFProjectile Class for default fire mode to be dependant on charge level
simulated function class<KFProjectile> GetKFProjectileClass()
{
if (CurrentFireMode == DEFAULT_FIREMODE)
if (CurrentFireMode == DEFAULT_FIREMODE || CurrentFireMode == ALTFIRE_FIREMODE)
{
return SonicBlastProjectileClassByChargeLevel[CurrentChargeLevel];
}
@ -377,7 +369,7 @@ simulated function float GetUpgradedPenetration(optional int FireMode = DEFAULT_
//Overriding to change shot sounds for default fire mode to be dependant on charge level
simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation )
{
WeaponFireSnd[DEFAULT_FIREMODE]=SonicBlastFireSoundByChargeLevel[CurrentChargeLevel];
WeaponFireSnd[CurrentFireMode]=SonicBlastFireSoundByChargeLevel[CurrentChargeLevel];
super.PlayFireEffects(FireModeNum, HitLocation);
}
@ -398,7 +390,7 @@ simulated function ProcessInstantHitEx(byte FiringMode, ImpactInfo Impact, optio
IndexMomentumMultiplierByZed = MomentumMultiplierByZedArray.Find('ZedClassName', Impact.HitActor.Class.Name);
if (IndexMomentumMultiplierByZed != INDEX_NONE)
{
InstantHitMomentum[DEFAULT_FIREMODE] *= MomentumMultiplierByZedArray[IndexMomentumMultiplierByZed].MomentumMultiplier;
InstantHitMomentum[CurrentFireMode] *= MomentumMultiplierByZedArray[IndexMomentumMultiplierByZed].MomentumMultiplier;
}
}
@ -452,8 +444,8 @@ defaultproperties
DOF_FG_MaxNearBlurSize=3.5
// Ammo
MagazineCapacity[0]=12 //8
SpareAmmoCapacity[0]=96 //72
MagazineCapacity[0]=10 //12 //8
SpareAmmoCapacity[0]=90 //96 //72
InitialSpareMags[0]=1
bCanBeReloaded=true
bReloadFromMagazine=true
@ -500,9 +492,16 @@ defaultproperties
FallingMomentumReduction=0.5
// ALTFIRE_FIREMODE (remote detonate)
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSonicGunCharging
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Custom
AmmoCost(ALTFIRE_FIREMODE)=0
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSonicGunSingleFiring
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile
WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_SonicBlastFullyCharged_HRG_SonicGun'
FireInterval(ALTFIRE_FIREMODE)=0.75
InstantHitDamage(ALTFIRE_FIREMODE)=125
InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_SonicGun_SonicBlastFullyCharged'
InstantHitMomentum(ALTFIRE_FIREMODE)=200000
Spread(ALTFIRE_FIREMODE)=0.005
PenetrationPower(ALTFIRE_FIREMODE)=2.0
AmmoCost(ALTFIRE_FIREMODE)=1
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_SonicGun'

View File

@ -0,0 +1,417 @@
//=============================================================================
// KFWeap_HVStormCannon
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFWeap_HVStormCannon extends KFWeap_ScopedBase;
var const float TrackingInstanceTimeDelaySeconds;
var const float TrackingRadius;
var const int TrackingDamageValue;
var const array<float> TrackingDamage;
var int WeaponID;
struct HVStormCannon_ProjectileTrackingInstance
{
var int NumberHits;
var float TimeNextJump;
var KFPawn_Monster LastTarget;
var array<KFPawn_Monster> HitTargets;
var Controller Instigator; // Instigator that shot the bullet
var byte IDCounter;
structdefaultproperties
{
NumberHits=0
TimeNextJump=0
LastTarget=none
Instigator=none
}
};
var array<HVStormCannon_ProjectileTrackingInstance> HVStormCannon_ProjectileTracking;
event PostBeginPlay()
{
local int Counter;
local KFPlayerController KFPC;
Super.PostBeginPlay();
Counter = 0;
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
{
++Counter;
if (KFPC == Instigator.Controller)
{
WeaponID = Counter * 1000;
break;
}
}
}
simulated function ProcessInstantHitEx(byte FiringMode, ImpactInfo Impact, optional int NumHits, optional out float out_PenetrationVal, optional int ImpactNum )
{
local int HitZoneIdx;
local KFPawn_Monster Target;
local HVStormCannon_ProjectileTrackingInstance NewTrackingInstance;
local KFPlayerController KFPC;
if (Role == ROLE_Authority)
{
Target = KFPawn_Monster(Impact.HitActor);
if (Target != none && !Target.bIsHeadless)
{
HitZoneIdx = Target.HitZones.Find('ZoneName', Impact.HitInfo.BoneName);
if (HitZoneIdx == HZI_Head && Target.IsAliveAndWell())
{
NewTrackingInstance.TimeNextJump = WorldInfo.TimeSeconds + TrackingInstanceTimeDelaySeconds;
NewTrackingInstance.LastTarget = Target;
NewTrackingInstance.HitTargets.AddItem(Target);
NewTrackingInstance.Instigator = Instigator.Controller;
KFPC = KFPlayerController(Instigator.Controller);
NewTrackingInstance.IDCounter = KFPC.StormCannonIDCounter;
if (KFPC.StormCannonIDCounter < 255)
{
++KFPC.StormCannonIDCounter;
}
else
{
KFPC.StormCannonIDCounter = 0;
}
// We simulate EMP affliction on Server, we can't use the affliction itself because it's duration is super hard to control
// To completely sync with the logic of TrackingInstanceTimeDelaySeconds
// Simulate start EMP affliction
Target.bEmpPanicked = true;
Target.OnStackingAfflictionChanged(AF_EMP);
HVStormCannon_ProjectileTracking.AddItem(NewTrackingInstance);
StartBeamVFX(Target, NewTrackingInstance.IDCounter);
}
}
}
super.ProcessInstantHitEx( FiringMode, Impact, NumHits, out_PenetrationVal, ImpactNum );
}
function KFPawn_Monster SearchClosestTarget(HVStormCannon_ProjectileTrackingInstance CurrentTrackingInstance)
{
local KFPawn_Monster CurrentTarget, BestTarget;
local TraceHitInfo HitInfo;
local float BestDistance;
local float Distance;
local vector ReferenceLocation;
local vector HitLocation, HitNormal;
local Actor HitActor;
ReferenceLocation = CurrentTrackingInstance.LastTarget.Mesh.GetBoneLocation('head');
//CurrentTrackingInstance.LastTarget.DrawDebugSphere(ReferenceLocation, CurrentTrackingInstance.LastTarget.GetCollisionRadius() + TrackingRadius, 10, 0, 255, 0, true);
foreach CollidingActors(class'KFPawn_Monster'
, CurrentTarget
, CurrentTrackingInstance.LastTarget.GetCollisionRadius() + TrackingRadius
, ReferenceLocation
, true,, HitInfo)
{
if (!CurrentTarget.IsAliveAndWell() || CurrentTarget.bIsCloaking)
{
continue;
}
// Check if the target does have map geometry in between..
HitActor = Trace(HitLocation, HitNormal, CurrentTarget.Mesh.GetBoneLocation('head'), ReferenceLocation,,,,TRACEFLAG_Bullet);
if (HitActor != none && KFPawn_Monster(HitActor) == none)
{
continue;
}
// Don't hit again targets..
if (CurrentTrackingInstance.HitTargets.Find(CurrentTarget) != INDEX_NONE)
{
continue;
}
// The closest..
Distance = VSizeSq(ReferenceLocation - CurrentTarget.Mesh.GetBoneLocation('head'));
if (BestTarget == none)
{
BestTarget = CurrentTarget;
BestDistance = Distance;
}
else if (BestDistance > Distance)
{
BestTarget = CurrentTarget;
BestDistance = Distance;
}
}
return BestTarget;
}
function UpdateTracking()
{
local int i;
local KFPawn_Monster NextTarget;
local bool bClear;
local TraceHitInfo HitInfo;
HitInfo.BoneName = 'head'; // we force headshot
for (i = HVStormCannon_ProjectileTracking.Length - 1; i >= 0; i--)
{
bClear = false;
if (WorldInfo.TimeSeconds >= HVStormCannon_ProjectileTracking[i].TimeNextJump)
{
// Simulate stop EMP affliction
HVStormCannon_ProjectileTracking[i].LastTarget.bEmpPanicked = false;
HVStormCannon_ProjectileTracking[i].LastTarget.OnStackingAfflictionChanged(AF_EMP);
if (HVStormCannon_ProjectileTracking[i].NumberHits < TrackingDamage.length)
{
NextTarget = SearchClosestTarget(HVStormCannon_ProjectileTracking[i]);
if (NextTarget != none)
{
HVStormCannon_ProjectileTracking[i].TimeNextJump = WorldInfo.TimeSeconds + TrackingInstanceTimeDelaySeconds;
// Simulate start EMP affliction
NextTarget.bEmpPanicked = true;
NextTarget.OnStackingAfflictionChanged(AF_EMP);
StartBeamVFX(NextTarget, HVStormCannon_ProjectileTracking[i].IDCounter);
HVStormCannon_ProjectileTracking[i].LastTarget = NextTarget;
HVStormCannon_ProjectileTracking[i].HitTargets.AddItem(NextTarget);
NextTarget.TakeDamage(TrackingDamageValue * TrackingDamage[HVStormCannon_ProjectileTracking[i].NumberHits]
, HVStormCannon_ProjectileTracking[i].Instigator
, NextTarget.Mesh.GetBoneLocation('head')
, vect(0,0,0)
, class'KFDT_HVStormCannonSpread'
, HitInfo);
++HVStormCannon_ProjectileTracking[i].NumberHits;
}
else
{
// If there are no targets we finish early
bClear = true;
}
}
else
{
// Max hits reached
bClear = true;
}
}
if (bClear)
{
StopBeamVFX(HVStormCannon_ProjectileTracking[i].IDCounter);
HVStormCannon_ProjectileTracking.Remove(i, 1);
}
}
}
simulated event Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
if (Role == ROLE_Authority)
{
UpdateTracking();
}
if (Role != ROLE_Authority || WorldInfo.NetMode == NM_Standalone)
{
UpdateAmmoCounter();
}
}
simulated function StartBeamVFX(KFPawn_Monster KFPM, byte ID)
{
local KFPlayerController KFPC;
if (Role == ROLE_Authority)
{
// Send RPC to all players
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
{
KFPC.AddStormCannonVFX(KFPM, GetID(ID));
}
}
}
simulated function StopBeamVFX(byte ID)
{
local KFPlayerController KFPC;
if (Role == ROLE_Authority)
{
// Send RPC to all players
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
{
KFPC.RemoveStormCannonVFX(GetID(ID));
}
}
}
function int GetID(byte ID)
{
return WeaponID + ID;
}
simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir )
{
local vector SocketLocation;
local rotator SocketRotation;
local vector SpawnLocation;
local vector SpawnDirection;
if(bUsingSights && MySkelMesh.GetSocketWorldLocationAndRotation('MuzzleFlash_Scope', SocketLocation, SocketRotation, 0))
{
SpawnLocation = SocketLocation;
SpawnDirection = vector(SocketRotation);
}
else
{
SpawnLocation = RealStartLoc;
SpawnDirection = AimDir;
}
return super.SpawnProjectile(KFProjClass, SpawnLocation, SpawnDirection);
}
simulated function UpdateAmmoCounter()
{
local float PercentageAmmo;
PercentageAmmo = float((AmmoCount[0] - 1)) / float(MagazineCapacity[0]);
WeaponMICs[3].SetScalarParameterValue('opacity', PercentageAmmo);
}
defaultproperties
{
// FOV
MeshFOV=70
MeshIronSightFOV=52
PlayerIronSightFOV=70
// Depth of field
DOF_FG_FocalRadius=0
DOF_FG_MaxNearBlurSize=2.5
// Content
PackageKey="HVStormCannon"
FirstPersonMeshName="WEP_1P_HVStormCannon_MESH.Wep_1stP_HVStormCannon_Rig"
FirstPersonAnimSetNames(0)="WEP_1P_HVStormCannon_ANIM.Wep_1p_HVStormCannon_Anim"
PickupMeshName="WEP_3P_HVStormCannon_MESH.Wep_HRG_HVStormCannon_Pickup"
AttachmentArchetypeName = "WEP_HVStormCannon_ARCH.HVStormCannon_3P"
MuzzleFlashTemplateName="WEP_HVStormCannon_ARCH.Wep_HVStormCannon_MuzzleFlash"
// Ammo
MagazineCapacity[0]=8
SpareAmmoCapacity[0]=96
InitialSpareMags[0]=2
bCanBeReloaded=true
bReloadFromMagazine=true
// Zooming/Position
PlayerViewOffset=(X=12.0,Y=13,Z=-2) //x7
IronSightPosition=(X=-5,Y=-0.01,Z=0.30) //X=-5,Y=-0.019,Z=0.22
// AI warning system
bWarnAIWhenAiming=true
AimWarningDelay=(X=0.4f, Y=0.8f)
AimWarningCooldown=0.0f
// Recoil
maxRecoilPitch=300
minRecoilPitch=200
maxRecoilYaw=150
minRecoilYaw=-150
RecoilRate=0.08
RecoilMaxYawLimit=500
RecoilMinYawLimit=1000
RecoilMaxPitchLimit=1250
RecoilMinPitchLimit=1500
RecoilISMaxYawLimit=50
RecoilISMinYawLimit=1000
RecoilISMaxPitchLimit=500
RecoilISMinPitchLimit=1000
RecoilViewRotationScale=0.6
IronSightMeshFOVCompensationScale=1.5
// Inventory
InventorySize=8
GroupPriority=125
WeaponSelectTexture=Texture2D'wep_ui_hvstormcannon_tex.UI_WeaponSelect_HVStormCannon'
// DEFAULT_FIREMODE
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle'
FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_HVStormCannon'
InstantHitDamage(DEFAULT_FIREMODE)=150
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_EMP_HVStormCannon'
// ALT_FIREMODE
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None
FireInterval(DEFAULT_FIREMODE)=0.4 // 150 RPM // 0.8 // 75 RPM
Spread(DEFAULT_FIREMODE)=0.005
PenetrationPower(DEFAULT_FIREMODE)=0
FireOffset=(X=30,Y=16,Z=-8)
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HVStormCannon'
InstantHitDamage(BASH_FIREMODE)=27
// Fire Effects
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HVStormCannon.Play_WEP_HVStormCannon_Shoot_3P', FirstPersonCue=AkEvent'WW_WEP_HVStormCannon.Play_WEP_HVStormCannon_Shoot_1P')
WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_Winchester.Play_WEP_SA_Winchester_Handling_DryFire'
// Attachments
bHasIronSights=true
bHasFlashlight=false
AssociatedPerkClasses(0)=class'KFPerk_Sharpshooter'
TrackingInstanceTimeDelaySeconds=0.15
TrackingRadius=200.0 // in cm, this is added to the Radius of the Zed that's spreading
TrackingDamageValue=150
TrackingDamage = (0.75f, 0.5f, 0.25f)
WeaponID=0
// Scope Render
// 2D scene capture
Begin Object Name=SceneCapture2DComponent0
//TextureTarget=TextureRenderTarget2D'Wep_Mat_Lib.WEP_ScopeLense_Target'
FieldOfView=12.5 //23.0 // "1.5X" = 35.0(our real world FOV determinant)/1.5
End Object
ScopedSensitivityMod=8.0 //16.0
ScopeLenseMICTemplate=MaterialInstanceConstant'WEP_1P_HVStormCannon_MAT.Wep_1stP_HVStormCannon_Lens_MIC'
ScopeMICIndex = 1
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1)))
NumBloodMapMaterials=4
}

View File

@ -20,6 +20,9 @@ defaultproperties
FirstPersonMeshName="WEP_1P_Demo_Knife_MESH.Wep_1stP_Demo_Knife_Rig"
AttachmentArchetypeName="WEP_Demo_Knife_ARCH.Wep_Demo_Knife_3P"
Spread(DEFAULT_FIREMODE)=1.0
Spread(ALTFIRE_FIREMODE)=1.0
Begin Object Name=FirstPersonMesh
AnimSets(0)=AnimSet'WEP_1P_CommandoKnife_ANIM.Wep_1stP_CommKnife_Anim'
End Object

View File

@ -74,7 +74,6 @@ simulated state WeaponDoubleBarrelFiring extends WeaponSingleFiring
simulated function BeginState(name PreviousStateName)
{
local vector UsedKickMomentum;
local KFMapInfo KFMI;
Super.BeginState(PreviousStateName);
@ -84,24 +83,8 @@ simulated state WeaponDoubleBarrelFiring extends WeaponSingleFiring
return;
}
// Push the player back when they fire both barrels
if (Instigator != none )
{
UsedKickMomentum.X = -DoubleBarrelKickMomentum;
if( Instigator.Physics == PHYS_Falling )
{
UsedKickMomentum = UsedKickMomentum >> Instigator.GetViewRotation();
UsedKickMomentum *= FallingMomentumReduction;
}
else
{
UsedKickMomentum = UsedKickMomentum >> Instigator.Rotation;
UsedKickMomentum.Z = 0;
}
Instigator.AddVelocity(UsedKickMomentum,Instigator.Location,none);
}
// Push the player back when they fire both barrels
ApplyKickMomentum(DoubleBarrelKickMomentum, FallingMomentumReduction);
}
}

View File

@ -87,7 +87,6 @@ simulated state WeaponQuadBarrelFiring extends WeaponSingleFiring
simulated function BeginState(name PreviousStateName)
{
local vector UsedKickMomentum;
local KFMapInfo KFMI;
Super.BeginState(PreviousStateName);
@ -96,51 +95,17 @@ simulated state WeaponQuadBarrelFiring extends WeaponSingleFiring
{
return;
}
// Push the player back when they fire both barrels
if (Instigator != none)
{
UsedKickMomentum.X = -DoubleBarrelKickMomentum;
if (Instigator.Physics == PHYS_Falling)
{
UsedKickMomentum = UsedKickMomentum >> Instigator.GetViewRotation();
UsedKickMomentum *= FallingMomentumReduction;
}
else
{
UsedKickMomentum = UsedKickMomentum >> Instigator.Rotation;
UsedKickMomentum.Z = 0;
}
Instigator.AddVelocity(UsedKickMomentum,Instigator.Location,none);
}
ApplyKickMomentum(DoubleBarrelKickMomentum, FallingMomentumReduction);
}
}
simulated function BeginState(name PreviousStateName)
{
local vector UsedKickMomentum;
Super.BeginState(PreviousStateName);
// Push the player back when they fire both barrels
if (Instigator != none)
{
UsedKickMomentum.X = -DoubleBarrelKickMomentum;
if (Instigator.Physics == PHYS_Falling)
{
UsedKickMomentum = UsedKickMomentum >> Instigator.GetViewRotation();
UsedKickMomentum *= FallingMomentumReduction;
}
else
{
UsedKickMomentum = UsedKickMomentum >> Instigator.Rotation;
UsedKickMomentum.Z = 0;
}
Instigator.AddVelocity(UsedKickMomentum,Instigator.Location,none);
}
ApplyKickMomentum(DoubleBarrelKickMomentum, FallingMomentumReduction);
}
defaultproperties

View File

@ -0,0 +1,564 @@
//=============================================================================
// KFWeap_ZedMKIII
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFWeap_ZedMKIII extends KFWeap_RifleBase;
/** Number of shots required in a row before firing a rocket */
var const byte NumBulletsBeforeRocket;
/** Minimum distance a target can be from the crosshair to be considered for lock on */
var const float MinTargetDistFromCrosshairSQ;
/** Dot product FOV that targets need to stay within to maintain a target lock */
var const float MaxLockMaintainFOVDotThreshold;
/** Time interval for updating radar positions */
var const float RadarUpdateEntitiesTime;
/** Distance the radar can track enemies */
var const float MaxRadarDistance;
/** Speed at which the radar moves (rad/sec) */
var const float RadarSpeed;
/** */
var const byte RadarTargetSize;
var transient array<KFPawn_Monster> EnemiesInRadar;
var transient byte NumShotsFired;
var transient float PrevAngle;
var transient bool bRequiresRadarClear;
var class<KFGFxWorld_WeaponRadar> RadarUIClass;
var KFGFxWorld_WeaponRadar RadarUI;
var const float MaxTargetAngle;
var transient float CosTargetAngle;
var transient bool LastShotIsRocket;
var WeaponFireSndInfo NormalFireSound;
var WeaponFireSndInfo RocketFireSound;
simulated event PostBeginPlay()
{
Super.PostBeginPlay();
CosTargetAngle = Cos(MaxTargetAngle * DegToRad);
}
simulated function AltFireMode()
{
}
/**
* Given an potential target TA determine if we can lock on to it. By default only allow locking on
* to pawns.
*/
simulated function bool CanLockOnTo(Actor TA)
{
Local KFPawn PawnTarget;
PawnTarget = KFPawn(TA);
// Make sure the pawn is legit, isn't dead, and isn't already at full health
if ((TA == None) || !TA.bProjTarget || TA.bDeleteMe || (PawnTarget == None) ||
(TA == Instigator) || (PawnTarget.Health <= 0) ||
!HasAmmo(DEFAULT_FIREMODE))
{
return false;
}
// Make sure and only lock onto players on the same team
return !WorldInfo.GRI.OnSameTeam(Instigator, TA);
}
/** Finds a new lock on target */
simulated function bool FindTarget( out KFPawn RecentlyLocked )
{
local KFPawn P, BestTargetLock;
local byte TeamNum;
local vector AimStart, AimDir, TargetLoc, Projection, DirToPawn, LinePoint;
local Actor HitActor;
local float PointDistSQ, Score, BestScore, TargetSizeSQ;
TeamNum = Instigator.GetTeamNum();
AimStart = GetSafeStartTraceLocation();
AimDir = vector( GetAdjustedAim(AimStart) );
BestScore = 0.f;
foreach WorldInfo.AllPawns( class'KFPawn', P )
{
if (!CanLockOnTo(P))
{
continue;
}
// Want alive pawns and ones we already don't have locked
if( P != none && P.IsAliveAndWell() && P.GetTeamNum() != TeamNum )
{
TargetLoc = GetLockedTargetLoc( P );
Projection = TargetLoc - AimStart;
DirToPawn = Normal( Projection );
// Filter out pawns too far from center
if( AimDir dot DirToPawn < CosTargetAngle )
{
continue;
}
// Check to make sure target isn't too far from center
PointDistToLine( TargetLoc, AimDir, AimStart, LinePoint );
PointDistSQ = VSizeSQ( LinePoint - P.Location );
TargetSizeSQ = P.GetCollisionRadius() * 2.f;
TargetSizeSQ *= TargetSizeSQ;
// Make sure it's not obstructed
HitActor = class'KFAIController'.static.ActorBlockTest(self, TargetLoc, AimStart,, true, true);
if( HitActor != none && HitActor != P )
{
continue;
}
// Distance from target has much more impact on target selection score
Score = VSizeSQ( Projection ) + PointDistSQ;
if( BestScore == 0.f || Score < BestScore )
{
BestTargetLock = P;
BestScore = Score;
}
}
}
if( BestTargetLock != none )
{
RecentlyLocked = BestTargetLock;
// Plays sound/FX when locking on to a new target
// PlayTargetLockOnEffects();
return true;
}
RecentlyLocked = none;
return false;
}
/** Adjusts our destination target impact location */
static simulated function vector GetLockedTargetLoc( Pawn P )
{
// Go for the chest, but just in case we don't have something with a chest bone we'll use collision and eyeheight settings
if( P.Mesh.SkeletalMesh != none && P.Mesh.bAnimTreeInitialised )
{
if( P.Mesh.MatchRefBone('Spine2') != INDEX_NONE )
{
return P.Mesh.GetBoneLocation( 'Spine2' );
}
else if( P.Mesh.MatchRefBone('Spine1') != INDEX_NONE )
{
return P.Mesh.GetBoneLocation( 'Spine1' );
}
return P.Mesh.GetPosition() + ((P.CylinderComponent.CollisionHeight + (P.BaseEyeHeight * 0.5f)) * vect(0,0,1)) ;
}
// General chest area, fallback
return P.Location + ( vect(0,0,1) * P.BaseEyeHeight * 0.75f );
}
simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation )
{
if (LastShotIsRocket)
{
super.PlayFireEffects(ALTFIRE_FIREMODE, HitLocation);
WeaponPlayFireSound(WeaponFireSnd[ALTFIRE_FIREMODE].DefaultCue, WeaponFireSnd[ALTFIRE_FIREMODE].FirstPersonCue);
return;
}
super.PlayFireEffects(FireModeNum, HitLocation);
}
simulated function Projectile ProjectileFire()
{
if (CurrentFireMode == DEFAULT_FIREMODE || CurrentFireMode == ALTFIRE_FIREMODE)
{
if (NumShotsFired >= NumBulletsBeforeRocket)
{
WeaponFireSnd[ALTFIRE_FIREMODE]=RocketFireSound;
CurrentFireMode = ALTFIRE_FIREMODE;
NumShotsFired = 0;
LastShotIsRocket = true;
}
else
{
WeaponFireSnd[ALTFIRE_FIREMODE]=NormalFireSound;
CurrentFireMode = DEFAULT_FIREMODE;
++NumShotsFired;
LastShotIsRocket = false;
}
}
return super.ProjectileFire();
}
/** Spawn projectile is called once for each rocket fired. In burst mode it will cycle through targets until it runs out */
simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir )
{
local KFProj_Rocket_ZedMKIII RocketProj;
local KFPawn TargetPawn;
if ( CurrentFireMode == ALTFIRE_FIREMODE )
{
FindTarget(TargetPawn);
RocketProj = KFProj_Rocket_ZedMKIII( super.SpawnProjectile( class<KFProjectile>(WeaponProjectiles[CurrentFireMode]) , RealStartLoc, AimDir) );
if( RocketProj != none )
{
// We'll aim our rocket at a target here otherwise we will spawn a dumbfire rocket at the end of the function
if ( TargetPawn != none)
{
//Seek to new target, then remove it
RocketProj.SetLockedTarget( TargetPawn );
}
}
// Resetting the firemode to default.
CurrentFireMode = DEFAULT_FIREMODE;
return RocketProj;
}
return super.SpawnProjectile( KFProjClass, RealStartLoc, AimDir );
}
/*********************************************************************************************
* state WeaponFiring
* This is the default Firing State. It's performed on both the client and the server.
*********************************************************************************************/
simulated state WeaponFiring
{
// Reset num shots fired when stop shooting
simulated event EndState( Name NextStateName)
{
super.EndState(NextStateName);
NumShotsFired = 0;
}
}
/*********************************************************************************************
* Radar implementation
*********************************************************************************************/
simulated state WeaponEquipping
{
simulated function BeginState(Name PreviousStateName)
{
super.BeginState(PreviousStateName);
PrevAngle = 0.0f;
if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone)
{
StartRadar();
}
}
}
simulated state WeaponPuttingDown
{
simulated function BeginState(Name PreviousStateName)
{
super.BeginState(PreviousStateName);
if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone)
{
StopRadar();
}
}
}
simulated function StartRadar()
{
SetTimer(RadarUpdateEntitiesTime, true, nameof(UpdateRadarEntities));
}
simulated function StopRadar()
{
ClearTimer(nameof(UpdateRadarEntities));
EnemiesInRadar.Length = 0;
}
simulated function UpdateRadarEntities()
{
local KFPawn_Monster KFPM;
local int RadarIndex;
local bool bIsAlive;
bIsAlive = false;
// Get nearby enemies
foreach CollidingActors(class'KFPawn_Monster', KFPM, MaxRadarDistance, Location, true)
{
RadarIndex = FindEnemyTrackedByRadar(KFPM);
bIsAlive = KFPM.IsAliveAndWell();
if (RadarIndex == INDEX_NONE)
{
if (bIsAlive)
{
EnemiesInRadar.AddItem(KFPM);
}
}
else if(!bIsAlive)
{
EnemiesInRadar.RemoveItem(KFPM);
}
}
}
simulated function int FindEnemyTrackedByRadar(KFPawn_Monster KFPM)
{
local int i;
for (i = 0; i < EnemiesInRadar.Length; ++i)
{
if (KFPM == EnemiesInRadar[i] )
{
return i;
}
}
return INDEX_NONE;
}
simulated function Tick(float Delta)
{
local float DistanceSqrd;
local vector Distance, ScreenDirection, UILocation;
local rotator ViewRotation;
local int i;
local array<vector> RadarElements;
super.Tick(Delta);
if (RadarUI != none)
{
if (bRequiresRadarClear)
{
RadarUI.Clear();
}
if (EnemiesInRadar.Length == 0)
{
if (bRequiresRadarClear)
{
bRequiresRadarClear = false;
}
return;
}
ViewRotation = Rotation;
ViewRotation.Yaw *= -1;
ViewRotation.Pitch = 0;
ViewRotation.Roll = 0;
RadarElements.Length = 0;
for (i = EnemiesInRadar.Length - 1; i >= 0; --i)
{
if (!EnemiesInRadar[i].IsAliveAndWell())
{
EnemiesInRadar.Remove(i, 1);
continue;
}
Distance = EnemiesInRadar[i].Location - Location;
DistanceSqrd = VSizeSQ(Distance);
if (DistanceSqrd > MaxRadarDistance * MaxRadarDistance)
{
EnemiesInRadar.Remove(i, 1);
continue;
}
Distance.Z = 0;
ScreenDirection = Distance >> ViewRotation;
UILocation.X = ScreenDirection.Y / MaxRadarDistance;
UILocation.Y = ScreenDirection.X / MaxRadarDistance;
RadarElements.AddItem(UILocation);
}
if (RadarElements.length > 0)
{
RadarUI.AddRadarElements(RadarElements);
bRequiresRadarClear = true;
}
}
}
/** Radar UI */
reliable client function ClientWeaponSet(bool bOptionalSet, optional bool bDoNotActivate)
{
local KFInventoryManager KFIM;
super.ClientWeaponSet(bOptionalSet, bDoNotActivate);
if (RadarUI == none && RadarUIClass != none)
{
KFIM = KFInventoryManager(InvManager);
if (KFIM != none)
{
//Create the screen's UI piece
RadarUI = KFGFxWorld_WeaponRadar(KFIM.GetRadarUIMovie(RadarUIClass));
}
}
}
function ItemRemovedFromInvManager()
{
local KFInventoryManager KFIM;
Super.ItemRemovedFromInvManager();
if (RadarUI != none)
{
KFIM = KFInventoryManager(InvManager);
if (KFIM != none)
{
//Create the screen's UI piece
KFIM.RemoveRadarUIMovie(RadarUI.class);
RadarUI.Close();
RadarUI = none;
}
}
}
/** */
defaultproperties
{
// FOV
MeshFOV=70
MeshIronSightFOV=30 //52
PlayerIronSightFOV=70
// Depth of field
DOF_FG_FocalRadius=85
DOF_FG_MaxNearBlurSize=2.5
// Zooming/Position
IronSightPosition=(X=14,Y=0.05,Z=-0.5) // x=-8,Y=0.01,Z=0.85
PlayerViewOffset=(X=22,Y=12,Z=-2.5)
// Content
PackageKey="ZedMKIII"
FirstPersonMeshName="WEP_1P_ZEDMKIII_MESH.Wep_1stP_ZEDMKIII_Rig"
FirstPersonAnimSetNames(0)="WEP_1P_ZEDMKIII_ANIM.Wep_1stP_ZEDMKIII_Anim"
PickupMeshName="WEP_3P_ZEDMKIII_MESH.Wep_3rdP_ZEDMKIII_Pickup"
AttachmentArchetypeName = "WEP_ZEDMKIII_ARCH.Wep_ZEDMKIII_3P"
MuzzleFlashTemplateName="WEP_ZEDMKIII_ARCH.Wep_ZEDMKIII_MuzzleFlash"
// Ammo
MagazineCapacity[0]=100
SpareAmmoCapacity[0]=400
InitialSpareMags[0]=1
bCanBeReloaded=true
bReloadFromMagazine=true
bCanRefillSecondaryAmmo=0
// Recoil
maxRecoilPitch=125
minRecoilPitch=100
maxRecoilYaw=120
minRecoilYaw=-100
RecoilRate=0.085
RecoilMaxYawLimit=500
RecoilMinYawLimit=65035
RecoilMaxPitchLimit=900
RecoilMinPitchLimit=65035
RecoilISMaxYawLimit=75
RecoilISMinYawLimit=65460
RecoilISMaxPitchLimit=375
RecoilISMinPitchLimit=65460
IronSightMeshFOVCompensationScale=4.0
// Inventory
InventorySize=9
GroupPriority=125
WeaponSelectTexture=Texture2D'wep_ui_zedmkiii_tex.UI_WeaponSelect_ZEDMKIII'
// DEFAULT_FIREMODE
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto'
FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_ZedMKIII'
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Microwave_ZedMKIII'
// ALT_FIREMODE
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile
WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Rocket_ZedMKIII'
InstantHitDamage(ALTFIRE_FIREMODE)=100
InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_ZedMKIII_Rocket'
Spread(ALTFIRE_FIREMODE)=0.025
AmmoCost(ALTFIRE_FIREMODE)=1
FireInterval(DEFAULT_FIREMODE)=0.15 // 400 RPM //0.12 // 500 RPM //+0.1 // 600 RPM
Spread(DEFAULT_FIREMODE)=0.0085
PenetrationPower(DEFAULT_FIREMODE)=4.0
InstantHitDamage(DEFAULT_FIREMODE)=50.0
FireOffset=(X=30,Y=8,Z=-15)
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_ZedMKIII'
InstantHitDamage(BASH_FIREMODE)=27
// Fire Effects
NormalFireSound=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Single_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Single_1P')
RocketFireSound=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Rocket_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Rocket_1P')
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_LP_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_LP_1P')
WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Single_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_Single_1P')
WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Handling_DryFire'
WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Handling_DryFire'
// Advanced (High RPM) Fire Effects
bLoopingFireAnim(DEFAULT_FIREMODE)=true
bLoopingFireSnd(DEFAULT_FIREMODE)=true
WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_LP_End_3P', FirstPersonCue=AkEvent'WW_WEP_ZEDMKIII.Play_WEP_ZEDMKIII_Shoot_LP_End_1P')
SingleFireSoundIndex=ALTFIRE_FIREMODE
// Attachments
bHasIronSights=true
bHasFlashlight=false
AssociatedPerkClasses(0)=class'KFPerk_Demolitionist'
NumBulletsBeforeRocket=6
NumShotsFired=0
MinTargetDistFromCrosshairSQ=2500.0f
MaxLockMaintainFOVDotThreshold=0.36f
RadarUpdateEntitiesTime=0.1f
MaxRadarDistance=2000
RadarSpeed=2.0f
RadarTargetSize=10.0f
RadarUIClass=class'KFGFxWorld_WeaponRadar'
NumBloodMapMaterials=2
bRequiresRadarClear=false
MaxTargetAngle=10
LastShotIsRocket=false
}