1
0
This commit is contained in:
2023-05-11 18:55:04 +03:00
parent 688183c1f1
commit d0b2d125ff
108 changed files with 6906 additions and 307 deletions

View File

@ -24,4 +24,7 @@ defaultproperties
//Perk
ModifierPerkList(0)=class'KFPerk_Commando'
bCanZedTime=false
bCanEnrage=false
}

View File

@ -0,0 +1,30 @@
//=============================================================================
// KFDT_Ballistic_HRG_Warthog
//=============================================================================
// Class Description
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFDT_Ballistic_HRG_Warthog extends KFDT_Ballistic_Shell
abstract
hidedropdown;
defaultproperties
{
KDamageImpulse=2000
KDeathUpKick=750
KDeathVel=350
KnockdownPower=125
StumblePower=340
GunHitPower=275
WeaponDef=class'KFWeapDef_HRG_Warthog'
//Perk
ModifierPerkList(0)=class'KFPerk_Demolitionist'
bCanZedTime=false
}

View File

@ -0,0 +1,53 @@
//=============================================================================
// KFDT_Ballistic_Shotgun_S12
//=============================================================================
// Damage type class for the S12 shotgun
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFDT_Ballistic_Shotgun_S12 extends KFDT_Ballistic_Shotgun
abstract
hidedropdown;
/** Allows the damage type to customize exactly which hit zones it can dismember */
static simulated function bool CanDismemberHitZone( name InHitZoneName )
{
if( super.CanDismemberHitZone( InHitZoneName ) )
{
return true;
}
switch ( InHitZoneName )
{
case 'lupperarm':
case 'rupperarm':
case 'chest':
case 'heart':
return true;
}
return false;
}
defaultproperties
{
BloodSpread=0.4
BloodScale=0.6
KDamageImpulse=900
KDeathUpKick=-500
KDeathVel=350
//KDamageImpulse=350
//KDeathUpKick=120
//KDeathVel=10
StumblePower=5
GunHitPower=0
ModifierPerkList(0)=class'KFPerk_Support'
WeaponDef=class'KFWeapDef_Shotgun_S12'
}

View File

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

View File

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

View File

@ -24,7 +24,9 @@ defaultproperties
KnockdownPower = 100
StumblePower = 300
WeaponDef=class'KFWeapDef_AutoTurret'
ModifierPerkList(0)=class'KFPerk_Commando'
bCanZedTime=false
bCanEnrage=false
}

View File

@ -0,0 +1,32 @@
//=============================================================================
// KFDT_Explosive_HRG_Warthog
//=============================================================================
// Explosive damage type for HRG Warthog explosion
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFDT_Explosive_HRG_Warthog extends KFDT_Explosive
abstract
hidedropdown;
defaultproperties
{
bShouldSpawnPersistentBlood = true
// physics impact
RadialDamageImpulse = 2000
GibImpulseScale = 0.15
KDeathUpKick = 1000
KDeathVel = 300
KnockdownPower = 50
StumblePower = 150
WeaponDef=class'KFWeapDef_HRG_Warthog'
ModifierPerkList(0)=class'KFPerk_Demolitionist'
bCanZedTime=false
bCanEnrage=false
}

View File

@ -0,0 +1,32 @@
//=============================================================================
// KFDT_Explosive_HRG_Warthog_HighExplosive
//=============================================================================
// Explosive damage type for HRG Warthog Projectile Explosion
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFDT_Explosive_HRG_Warthog_HighExplosive extends KFDT_Explosive
abstract
hidedropdown;
defaultproperties
{
bShouldSpawnPersistentBlood = true
// physics impact
RadialDamageImpulse = 2000
GibImpulseScale = 0.15
KDeathUpKick = 1000
KDeathVel = 300
KnockdownPower = 50
StumblePower = 150
WeaponDef=class'KFWeapDef_HRG_Warthog'
ModifierPerkList(0)=class'KFPerk_Demolitionist'
bCanZedTime=false
bCanEnrage=false
}

View File

@ -0,0 +1,39 @@
//=============================================================================
// KFDT_Explosive_Shotgun_S12
//=============================================================================
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFDT_Explosive_Shotgun_S12 extends KFDT_EMP
abstract
hidedropdown;
defaultproperties
{
bShouldSpawnPersistentBlood=true
// physics impact
RadialDamageImpulse=3000 //5000 //20000
GibImpulseScale=0.15
KDeathUpKick=1000
KDeathVel=300
// unreal physics momentum
bExtraMomentumZ=True
bCanGib=true
KnockdownPower=100
StunPower=25
StumblePower=200
EMPPower=100
//Perk
ModifierPerkList(0)=class'KFPerk_Support'
WeaponDef=class'KFWeapDef_Shotgun_S12'
bCanApplyRadialCalculationtoAffliction=false
}

View File

@ -0,0 +1,36 @@
//=============================================================================
// KFExplosion_HRG_Warthog
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFExplosion_HRG_Warthog extends KFExplosionActor;
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;
}
DefaultProperties
{
}

View File

@ -882,12 +882,33 @@ function StartWave()
WaveNum++;
MyKFGRI.WaveNum = WaveNum;
if (IsMapObjectiveEnabled())
if (MyKFGRI.IsContaminationMode())
{
MyKFGRI.ClearPreviousObjective();
if (MyKFGRI.StartNextObjective())
if (WaveNum == 1) // Only on first wave..
{
WaveBuffer = ObjectiveSpawnDelay;
MyKFGRI.ChooseNextObjective(WaveNum);
}
MyKFGRI.ClearPreviousObjective();
if (WaveNum != WaveMax)
{
if (MyKFGRI.StartNextObjective())
{
WaveBuffer = ObjectiveSpawnDelay;
}
}
}
else
{
if (IsMapObjectiveEnabled())
{
MyKFGRI.ClearPreviousObjective();
if (MyKFGRI.StartNextObjective())
{
WaveBuffer = ObjectiveSpawnDelay;
}
}
}

View File

@ -33,6 +33,32 @@ var array<PerkRoulette_PlayerMessageDelegate> PerkRoulette_PlayersDelegateData;
var array<KFPlayerController_WeeklySurvival> PerkRoulette_PlayersDelegateInventory;
struct ContaminationModeData
{
var() float FirstWaveInitialTimer;
var() float WaveInitialTimer;
var() float WaveCurrentTimer;
var() float GraceTimer; // We store on each Player the GraceCurrentTimer
var() float DamageTimer;
var() float DamageCurrentTimer;
var() bool ObjectiveHidden;
var() bool CanUpdate;
structdefaultproperties
{
FirstWaveInitialTimer = 45.f
WaveInitialTimer = 30.f
WaveCurrentTimer = 0.f
GraceTimer = 5.f
DamageTimer = 1.f
DamageCurrentTimer = 0.f
ObjectiveHidden = false
CanUpdate = false
}
};
var ContaminationModeData ContaminationMode;
//-----------------------------------------------------------------------------
// Statics
static event class<GameInfo> SetGameType(string MapName, string Options, string Portal)
@ -538,6 +564,18 @@ function Tick(float DeltaTime)
// This deals with players joining at any time (lobby, or in wave)
ChooseRandomPerks(false);
}
if (MyKFGRI.IsContaminationMode())
{
if (ContaminationMode.CanUpdate)
{
UpdateContaminationMode(DeltaTime);
}
else if (WaveNum < (WaveMax - 1))
{
UpdateContaminationModeTrader();
}
}
}
function TickZedTime( float DeltaTime )
@ -576,6 +614,11 @@ function WaveEnded(EWaveEndCondition WinCondition)
ChooseRandomPerks(true);
}
if (MyKFGRI.IsContaminationMode())
{
ContaminationMode.CanUpdate = false;
}
super.WaveEnded(WinCondition);
if (OutbreakEvent.ActiveEvent.bPermanentZedTime && ZedTimeRemaining > ZedTimeBlendOutTime)
@ -614,6 +657,19 @@ function GrantExtraDoshOnWaveWon()
KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo).AddDosh(ExtraDosh, true);
}
}
if (MyKFGRI.IsContaminationMode())
{
ExtraDosh = MyKFGRI.ContaminationModeExtraDosh();
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
{
if (KFPC.IsInState('Spectating') == false
&& KFPC.PlayerReplicationInfo.bOnlySpectator == false)
{
KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo).AddDosh(ExtraDosh, true);
}
}
}
}
function ClearZedTimePCTimers()
@ -687,6 +743,22 @@ function StartWave()
{
OverridePickupList();
}
if (MyKFGRI.IsContaminationMode())
{
if (WaveNum == 1)
{
ContaminationMode.WaveCurrentTimer = ContaminationMode.FirstWaveInitialTimer;
}
else
{
ContaminationMode.WaveCurrentTimer = ContaminationMode.WaveInitialTimer;
}
ContaminationMode.DamageCurrentTimer = ContaminationMode.DamageTimer;
ContaminationMode.ObjectiveHidden = false;
ContaminationMode.CanUpdate = true;
}
}
function bool OverridePickupList()
@ -1187,6 +1259,14 @@ function NotifyKilled(Controller Killer, Controller Killed, Pawn KilledPawn, cla
PerkRoulette_PlayersDelegateInventory.AddItem(KFPC_WS_Killed);
}
}
if (MyKFGRI.IsContaminationMode())
{
if (KFPC_WS_Killed != none)
{
KFPC_WS_Killed.HideContaminationMode();
}
}
}
function GunGameLevelGrantWeapon(KFPlayerController_WeeklySurvival KFPC_WS, class<KFWeaponDefinition> ToGrantWeaponDefinition)
@ -1827,6 +1907,189 @@ function BroadcastCustomDelegate()
}
}
/*
* Weekly 19: Contamination Mode
*/
function UpdatePlayersState(KFMapObjective_DoshHold Area
, out array<KFPlayerController_WeeklySurvival> ValidPlayers
, out array<KFPlayerController_WeeklySurvival> PlayersInsideArea
, out array<KFPlayerController_WeeklySurvival> PlayersOutsideArea)
{
local KFPlayerController_WeeklySurvival KFPC_WS;
local int i;
// Get available players
foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS)
{
if (KFPC_WS.Pawn.IsAliveAndWell() == false
|| KFPC_WS.PlayerReplicationInfo.bOnlySpectator
|| KFPC_WS.IsInState('Spectating'))
{
continue;
}
ValidPlayers.AddItem(KFPC_WS);
}
// Update who's in and who's out
for (i = 0 ; i < ValidPlayers.Length ; ++i)
{
// If is inside..
if (Area.TouchingHumans.Find(KFPawn_Human(ValidPlayers[i].Pawn)) != INDEX_NONE)
{
PlayersInsideArea.AddItem(ValidPlayers[i]);
}
else
{
PlayersOutsideArea.AddItem(ValidPlayers[i]);
}
}
}
function UpdateContaminationModeTrader()
{
local KFMapObjective_DoshHold Area;
local array<KFPlayerController_WeeklySurvival> ValidPlayers, PlayersInsideArea, PlayersOutsideArea;
local int i;
Area = KFMapObjective_DoshHold(MyKFGRI.NextObjective);
if (Area != none)
{
UpdatePlayersState(Area, ValidPlayers, PlayersInsideArea, PlayersOutsideArea);
for (i = 0 ; i < PlayersInsideArea.Length ; ++i)
{
PlayersInsideArea[i].ShowContaminationMode();
PlayersInsideArea[i].UpdateContaminationModeWidget(true);
}
for (i = 0 ; i < PlayersOutsideArea.Length ; ++i)
{
PlayersOutsideArea[i].ShowContaminationMode();
PlayersOutsideArea[i].ContaminationModePlayerIsInside = false;
PlayersOutsideArea[i].UpdateContaminationModeWidget_Timer(ContaminationMode.WaveInitialTimer);
}
}
}
function UpdateContaminationMode(float DeltaTime)
{
local KFMapObjective_DoshHold Area;
local array<KFPlayerController_WeeklySurvival> ValidPlayers, PlayersInsideArea, PlayersOutsideArea;
local KFPlayerController_WeeklySurvival KFPC_WS;
local int i;
local bool CheckPlayersInArea, CanApplyDamage;
CheckPlayersInArea = false;
// Update wave timer..
if (ContaminationMode.WaveCurrentTimer > 0.f)
{
ContaminationMode.WaveCurrentTimer -= DeltaTime;
}
if (MyKFGRI.CurrentObjective != none)
{
foreach WorldInfo.AllActors(class'KFMapObjective_DoshHold', Area)
{
if (Area.IsActive())
{
CheckPlayersInArea = true;
UpdatePlayersState(Area, ValidPlayers, PlayersInsideArea, PlayersOutsideArea);
break;
}
}
}
// If there's a valid area an objective..
if (CheckPlayersInArea && WaveNum != WaveMax)
{
// Trigger logic depending on state of game
if (ContaminationMode.WaveCurrentTimer > 0.f)
{
// If we are still on safe time to reach area, we can only notify Player if you are inside or outside, no Grace Timer, and No Damage applied
for (i = 0 ; i < PlayersInsideArea.Length ; ++i)
{
PlayersInsideArea[i].UpdateContaminationModeWidget(true);
}
for (i = 0 ; i < PlayersOutsideArea.Length ; ++i)
{
PlayersOutsideArea[i].ContaminationModePlayerIsInside = false;
PlayersOutsideArea[i].UpdateContaminationModeWidget_Timer(ContaminationMode.WaveCurrentTimer);
}
}
else
{
// If Time finished, we must Damage Players that are outside (use Grace Timer)
if (ContaminationMode.DamageCurrentTimer > 0.f)
{
ContaminationMode.DamageCurrentTimer -= DeltaTime;
}
CanApplyDamage = ContaminationMode.DamageCurrentTimer <= 0.f;
// Reset damage tick
if (CanApplyDamage)
{
ContaminationMode.DamageCurrentTimer = ContaminationMode.DamageTimer;
}
for (i = 0 ; i < PlayersInsideArea.Length ; ++i)
{
PlayersInsideArea[i].ContaminationModeGraceCurrentTimer = ContaminationMode.GraceTimer;
PlayersInsideArea[i].UpdateContaminationModeWidget(true);
}
for (i = 0 ; i < PlayersOutsideArea.Length ; ++i)
{
if (PlayersOutsideArea[i].ContaminationModeGraceCurrentTimer > 0.f)
{
PlayersOutsideArea[i].ContaminationModeGraceCurrentTimer -= DeltaTime;
}
PlayersOutsideArea[i].UpdateContaminationModeWidget(false);
if (CanApplyDamage && PlayersOutsideArea[i].ContaminationModeGraceCurrentTimer <= 0.f)
{
PlayersOutsideArea[i].Pawn.TakeDamage(class'KFDT_WeeklyContamination'.static.GetDamage(), none, vect(0,0,0), vect(0,0,0), class'KFDT_WeeklyContamination');
}
}
}
}
else
{
// Hide UIs if no more objective
if (ContaminationMode.ObjectiveHidden == false)
{
ContaminationMode.ObjectiveHidden = true;
foreach WorldInfo.AllControllers(class'KFPlayerController_WeeklySurvival', KFPC_WS)
{
if (KFPC_WS.Pawn.IsAliveAndWell() == false
|| KFPC_WS.PlayerReplicationInfo.bOnlySpectator
|| KFPC_WS.IsInState('Spectating'))
{
continue;
}
KFPC_WS.HideContaminationMode();
}
}
}
}
//
defaultproperties

View File

@ -62,6 +62,25 @@ simulated function NotifyWaveStart()
super.NotifyWaveStart();
}
function ChooseNextObjective(int NextWaveNum)
{
local KFMapInfo KFMI;
if (IsContaminationMode() == false)
{
super.ChooseNextObjective(NextWaveNum);
}
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
if (KFMI != none && NextWaveNum != WaveMax)
{
bForceNextObjective = true; // this overrides the objective chance, so it just chooses randomnly between all them
ChooseNextRandomObjective(KFMI, NextWaveNum, false);
}
}
DefaultProperties
{
bIsWeeklyMode=True

View File

@ -95,7 +95,7 @@ simulated function AddToOwnerArray()
}
}
simulated function TakeRadiusDamage(Controller InstigatedBy, float BaseDamage, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HurtOrigin, bool bFullDamage, Actor DamageCauser, optional float DamageFalloffExponent = 1.f)
simulated function TakeRadiusDamage(Controller InstigatedBy, float BaseDamage, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HurtOrigin, bool bFullDamage, Actor DamageCauser, optional float DamageFalloffExponent = 1.f, optional bool bAdjustRadiusDamage=true)
{
if (!bIgnoreRadiusDamage || AcceptedDamageTypes.Find(DamageType) != INDEX_NONE)
{

View File

@ -94,6 +94,7 @@ var() float RemindPlayersTime;
var transient float PrevWaveProgress;
var transient bool bRemindPlayers;
var Texture2D ContaminationIcon;
simulated event ReplicatedEvent(name VarName)
{
@ -125,11 +126,95 @@ event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vecto
}
}
simulated function bool ShouldDrawIcon()
{
local KFGameReplicationInfo KFGRI;
if (WorldInfo != None && WorldInfo.Game != none && WorldInfo.Game.GameReplicationInfo != none)
{
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
if (KFGRI != none && KFGRI.IsContaminationMode())
{
return KFGRI.AIRemaining > KFGRI.ContaminationModeZedsToFinish();
}
}
return Super.ShouldDrawIcon();
}
simulated function GrantReward(KFPlayerReplicationInfo KFPRI, KFPlayerController KFPC)
{
local KFGameReplicationInfo KFGRI;
Super.GrantReward(KFPRI, KFPC);
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
if (KFGRI == none)
{
return;
}
if (KFGRI.IsContaminationMode() == false)
{
if (KFPRI == none)
{
return;
}
if (KFPRI.bOnlySpectator)
{
return;
}
if (KFPC != none)
{
// Summer 2023 objective
KFPC.ClientOnTryCompleteObjective(3, SEI_Summer);
}
}
}
function NotifyZedKilled(Controller Killer, Pawn KilledPawn, bool bIsBoss)
{
local int i;
local KFGameInfo KFGI;
local KFGameReplicationInfo KFGRI;
local KFPlayerController KFPC;
local KFPlayerReplicationInfo KFPRI;
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
if (KFGRI != none && KFGRI.IsContaminationMode())
{
if (ROLE == Role_Authority)
{
if (bActive)
{
if (KFGRI.AIRemaining <= KFGRI.ContaminationModeZedsToFinish())
{
DeactivateObjective();
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
{
if (KFPC != none)
{
KFPRI = KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo);
if (KFPRI != none && KFPRI.bOnlySpectator == false)
{
// Summer 2023 objective
KFPC.ClientOnTryCompleteObjective(3, SEI_Summer);
}
}
}
}
}
}
return;
}
if (ROLE == Role_Authority)
{
@ -144,7 +229,6 @@ function NotifyZedKilled(Controller Killer, Pawn KilledPawn, bool bIsBoss)
{
if (RewardPerZed == 0)
{
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
RewardPerZed = GetMaxDoshReward() / (PctOfWaveZedsKilledForMaxReward * KFGRI.WaveTotalAICount);
}
CurrentRewardAmount = FMin(CurrentRewardAmount + RewardPerZed, float(GetMaxDoshReward()));
@ -237,6 +321,16 @@ function StartPenaltyCheck()
function ActivationVO()
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
if (KFGRI != none && KFGRI.IsContaminationMode())
{
PlaySoundBase(AkEvent'WW_VOX_NPC_Trader.Play_Trader_DEFA_Area', false, WorldInfo.NetMode == NM_DedicatedServer);
return;
}
if (ActivationSoundEventOverride != none)
{
PlaySoundBase(ActivationSoundEventOverride, false, WorldInfo.NetMode == NM_DedicatedServer);
@ -373,6 +467,15 @@ simulated function DeactivateObjective()
function PlayDeactivationDialog()
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
if (KFGRI != none && KFGRI.IsContaminationMode())
{
return;
}
if (CurrentRewardAmount <= 0)
{
if (FailureSoundEventOverride != none)
@ -499,6 +602,23 @@ simulated function bool ShouldShowObjectiveHUD()
return !IsComplete();
}
simulated function Texture2D GetIcon()
{
local KFGameReplicationInfo KFGRI;
if (WorldInfo != None && WorldInfo.Game != none && WorldInfo.Game.GameReplicationInfo != none)
{
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
if (KFGRI != none && KFGRI.IsContaminationMode())
{
return ContaminationIcon;
}
}
return ObjectiveIcon;
}
defaultproperties
{
DescriptionLocKey="DescriptionDoshHold"
@ -561,7 +681,7 @@ defaultproperties
ZedThresholds[5]=0
ObjectiveIcon=Texture2D'Objectives_UI.UI_Objectives_ObjectiveMode'
ContaminationIcon=Texture2D'Objectives_UI.UI_Objectives_Xmas_DefendObj'
RemindPlayersTime=30.f
bUseEarlyTrail=false

View File

@ -1235,6 +1235,21 @@ defaultproperties
)}
)}
// Contamination Mode
SetEvents[19]={(
EventDifficulty=2,
GameLength=GL_Normal,
ContaminationModeZedsToFinish=5,
ContaminationModeExtraDosh=200,
SpawnReplacementList={(
(SpawnEntry=AT_EliteCrawler,NewClass=(class'KFGameContent.KFPawn_ZedGorefast'),PercentChance=0.9),
(SpawnEntry=AT_Siren,NewClass=(class'KFGameContent.KFPawn_ZedDAR_Laser'),PercentChance=0.2),
(SpawnEntry=AT_Bloat,NewClass=(class'KFGameContent.KFPawn_ZedDAR_Rocket'),PercentChance=0.2)
)}
)}
//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

@ -105,6 +105,9 @@ const NoAmmoSocketName = 'malfunction';
const NoAmmoFXTemplate = ParticleSystem'WEP_AutoTurret_EMIT.FX_NoAmmo_Sparks';
var transient ParticleSystemComponent NoAmmoFX;
var transient vector DeployLastLocation;
var transient float LastMoveExpectedSize;
replication
{
if( bNetDirty )
@ -374,7 +377,7 @@ simulated state Deploy
SetTimer(AnimDuration, false, nameof(StartIdleAnim));
}
SetPhysics(PHYS_FLYING);
SetPhysics(PHYS_NONE);
if (Role == ROLE_Authority)
{
@ -390,22 +393,33 @@ simulated state Deploy
super.Tick(DeltaTime);
// If we didn't move..
if (VSize(Location - DeployLastLocation) < (LastMoveExpectedSize * 0.8f))
{
SetTurretState(ETS_TargetSearch);
return;
}
LocationNext = Location;
LocationNext.z += Velocity.z * DeltaTime;
// If there's little to no movement or we are going to collide
if (Velocity.z <= 0.01f || !FastTrace(LocationNext, Location, vect(25,25,25)))
// If we are going to collide stop
if (!FastTrace(LocationNext, Location, vect(25,25,25)))
{
SetTurretState(ETS_TargetSearch);
return;
}
else
DeployLastLocation = Location;
LastMoveExpectedSize = VSize(LocationNext - Location);
SetLocation(LocationNext);
// Check height to change state
CurrentHeight = Location.Z - GroundLocation.Z;
if (CurrentHeight >= DeployHeight)
{
// Check height to change state
CurrentHeight = Location.Z - GroundLocation.Z;
if (CurrentHeight >= DeployHeight)
{
SetTurretState(ETS_TargetSearch);
}
SetTurretState(ETS_TargetSearch);
}
}
@ -425,6 +439,8 @@ simulated state Deploy
SetTimer(0.25f, true, nameof(CheckEnemiesWithinExplosionRadius));
}
}
SetPhysics(PHYS_NONE);
}
}
@ -533,6 +549,8 @@ simulated state Combat
local float NewAmmoPercentage;
local bool bIsSpotted;
if (Role == ROLE_Authority)
{
TurretWeapon.GetMuzzleLocAndRot(MuzzleLoc, MuzzleRot);
@ -565,11 +583,14 @@ simulated state Combat
// Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone!
HitActor = Trace(HitLocation, HitNormal, EnemyTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet);
/** Search for new enemies if current is dead, cloaked or too far, or something between the drone and the target except a player */
// Visible by local player or team
bIsSpotted = (EnemyTarget.bIsCloakingSpottedByLP || EnemyTarget.bIsCloakingSpottedByTeam);
/** Search for new enemies if current is dead, cloaked or too far, or something between the drone that's world geometry */
if (!EnemyTarget.IsAliveAndWell()
|| EnemyTarget.bIsCloaking
|| (EnemyTarget.bIsCloaking && bIsSpotted == false)
|| VSizeSq(EnemyTarget.Location - Location) > EffectiveRadius * EffectiveRadius
|| (HitActor != none && KFPawn_Monster(HitActor) == none && KFPawn_Human(HitActor) == none))
|| (HitActor != none && HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None))
{
EnemyTarget = none;
CheckForTargets();
@ -590,13 +611,13 @@ simulated state Combat
RotateBySpeed(DesiredRotationRot);
if (Role == ROLE_Authority)
if (Role == ROLE_Authority && ReachedRotation())
{
HitActor = Trace(HitLocation, HitNormal, MuzzleLoc + vector(Rotation) * EffectiveRadius, MuzzleLoc, , , HitInfo, TRACEFLAG_Bullet);
if (TurretWeapon != none)
{
if (KFPawn_Monster(HitActor) != none)
if (HitActor != none && HitActor.bWorldGeometry == false)
{
TurretWeapon.Fire();
@ -641,6 +662,7 @@ simulated state Detonate
{
ExploActor.InstigatorController = Instigator.Controller;
ExploActor.Instigator = Instigator;
ExploActor.bIgnoreInstigator = true;
ExploActor.Explode(ExplosionTemplate);
}
@ -732,6 +754,8 @@ function CheckForTargets()
local vector HitLocation, HitNormal;
local Actor HitActor;
local bool bIsSpotted;
if (EnemyTarget != none)
{
CurrentDistance = VSizeSq(Location - EnemyTarget.Location);
@ -745,10 +769,19 @@ function CheckForTargets()
foreach CollidingActors(class'KFPawn_Monster', CurrentTarget, EffectiveRadius, Location, true,, HitInfo)
{
// Visible by local player or team
bIsSpotted = (CurrentTarget.bIsCloakingSpottedByLP || CurrentTarget.bIsCloakingSpottedByTeam);
if (!CurrentTarget.IsAliveAndWell()
|| (CurrentTarget.bIsCloaking && bIsSpotted == false))
{
continue;
}
// Trace from the Target reference to MuzzleLoc, because MuzzleLoc could be already inside physics, as it's outside the collider of the Drone!
HitActor = Trace(HitLocation, HitNormal, CurrentTarget.Mesh.GetBoneLocation('Spine1'), MuzzleLoc,,,,TRACEFLAG_Bullet);
if (!CurrentTarget.IsAliveAndWell() || CurrentTarget.bIsCloaking || HitActor == none || KFPawn_Monster(HitActor) == none)
if (HitActor == none || (HitActor.bWorldGeometry && KFFracturedMeshGlass(HitActor) == None))
{
continue;
}
@ -992,7 +1025,8 @@ simulated function TakeRadiusDamage(
vector HurtOrigin,
bool bFullDamage,
Actor DamageCauser,
optional float DamageFalloffExponent=1.f
optional float DamageFalloffExponent=1.f,
optional bool bAdjustRadiusDamage=true
)
{}
@ -1001,6 +1035,16 @@ function bool CanAITargetThisPawn(Controller TargetingController)
return false;
}
simulated function bool CanInteractWithPawnGrapple()
{
return false;
}
simulated function bool CanInteractWithZoneVelocity()
{
return false;
}
simulated function UpdateTurretMeshMaterialColor(float Value)
{
if (TurretWeaponAttachment == none)
@ -1104,6 +1148,9 @@ defaultproperties
CamShakeOuterRadius=900
CamShakeFalloff=1.5f
bOrientCameraShakeTowardsEpicenter=true
bIgnoreInstigator=true
ActorClassToIgnoreForDamage = class'KFPawn_Human'
End Object
ExplosionTemplate=ExploTemplate0
@ -1163,4 +1210,7 @@ defaultproperties
bAlwaysRelevant=true
AutoTurretFlashCount=0
DeployLastLocation=(X=-9999.f, Y=-9999.f, Z=-9999.f)
LastMoveExpectedSize= 0.f
}

File diff suppressed because it is too large Load Diff

View File

@ -391,6 +391,7 @@ defaultproperties
// ---------------------------------------------
// AI / Navigation
EliteAIType.Add(14); // AT_EliteCrawler
ElitePawnClass.Add(class'KFPawn_ZedCrawlerKing')
ControllerClass=class'KFAIController_ZedCrawler'
bDebugCrawlerPhysics=false

View File

@ -124,10 +124,19 @@ simulated function CreateExhaustFx()
event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
{
local class<KFDamageType> KFDT;
super.TakeDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType, HitInfo, DamageCauser );
if( bCanRage && !bPlayedDeath && (GetHealthPercentage() < RageHealthThreshold || GetHeadHealthPercent() < RageHealthThreshold) )
{
KFDT = class<KFDamageType>(DamageType);
if (KFDT != none && KFDT.default.bCanEnrage == false)
{
return;
}
SetEnraged( true );
}
}

View File

@ -35,6 +35,20 @@ function CausePainTo(Actor Other)
}
}
simulated event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal )
{
local KFPawn KFP;
KFP = KFPawn(Other);
if (KFP != none && KFP.CanInteractWithZoneVelocity() == false)
{
return;
}
Super.Touch(Other, OtherComp, HitLocation, HitNormal);
}
simulated event UnTouch(Actor Other)
{
local int RecentHitIdx;

View File

@ -205,7 +205,7 @@ defaultproperties
// explosion
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=350
DamageRadius=200
DamageRadius=250
DamageFalloffExponent=1.f
DamageDelay=0.f
@ -240,7 +240,7 @@ defaultproperties
End Object
StickHelper=StickHelper0
SecondsBeforeDetonation=0.5f
SecondsBeforeDetonation=0.2f
bIsProjActive=true
bCanDisintegrate=true
bAlwaysReplicateExplosion=true

View File

@ -241,7 +241,7 @@ defaultproperties
End Object
StickHelper=StickHelper0
SecondsBeforeDetonation=0.5f
SecondsBeforeDetonation=0.2f
bIsProjActive=true
bCanDisintegrate=true
bAlwaysReplicateExplosion=true

View File

@ -0,0 +1,137 @@
//=============================================================================
// KFProj_Bullet_Shotgun_S12_Alt
//=============================================================================
// Class Description
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFProj_Bullet_Shotgun_S12_Alt extends KFProj_BallisticExplosive
hidedropdown;
var protected KFWeapon OwnerWeapon;
/** Initialize the projectile */
function Init( vector Direction )
{
super.Init( Direction );
OwnerWeapon = KFWeapon( Owner );
if( OwnerWeapon != none )
{
OwnerWeapon.LastPelletFireTime = WorldInfo.TimeSeconds;
}
if (KFPawn(Instigator) != none)
{
// Explode right away
ForceExplode();
}
}
reliable client function ForceExplode()
{
local vector Normal, MuzzleLocation;
if (KFPawn(Instigator) != none)
{
Normal.X = 0.f;
Normal.Y = 0.f;
Normal.Z = 1.f;
KFSkeletalMeshComponent(OwnerWeapon.Mesh).GetSocketWorldLocationAndRotation('MuzzleFlashAlt', MuzzleLocation);
Explode(MuzzleLocation, Normal);
Detonate();
}
}
simulated function bool CanDud()
{
return false;
}
/** Don't allow more than one pellet projectile to perform this check in a single frame */
function bool ShouldWarnAIWhenFired()
{
return super.ShouldWarnAIWhenFired() && OwnerWeapon != none && OwnerWeapon.LastPelletFireTime < WorldInfo.TimeSeconds;
}
simulated protected function PrepareExplosionTemplate()
{
super.PrepareExplosionTemplate();
/** Since bIgnoreInstigator is transient, its value must be defined here */
ExplosionTemplate.bIgnoreInstigator = true;
}
simulated function AdjustCanDisintigrate() {}
/** Can be overridden in subclasses to exclude specific projectiles from nuking */
simulated function bool AllowNuke()
{
return false;
}
defaultproperties
{
ArmDistSquared=0.f
MaxSpeed=0.0
Speed=0.0
DamageRadius=0
// Grenade explosion light
Begin Object Class=PointLightComponent Name=ExplosionPointLight
LightColor=(R=252,G=218,B=171,A=255)
Brightness=0.5f
Radius=400.f
FalloffExponent=10.f
CastShadows=False
CastStaticShadows=FALSE
CastDynamicShadows=False
bCastPerObjectShadows=false
bEnabled=FALSE
LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
End Object
// explosion
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=200
DamageRadius=800
DamageFalloffExponent=0.f
DamageDelay=0.f
MomentumTransferScale=10000
bAlwaysFullDamage=true
bDoCylinderCheck=true
// Damage Effects
MyDamageType=class'KFDT_Explosive_Shotgun_S12'
KnockDownStrength=150
FractureMeshRadius=200.0
FracturePartVel=500.0
ExplosionSound=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Alt_Fire_1P'
ExplosionEffects=KFImpactEffectInfo'WEP_Saiga12_ARCH.WEP_Saiga12_Impacts'
// Dynamic Light
ExploLight=ExplosionPointLight
ExploLightStartFadeOutTime=0.0
ExploLightFadeOutTime=0.3
bIgnoreInstigator=true
ActorClassToIgnoreForDamage = class'KFPawn_Human'
// Camera Shake
CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble'
CamShakeInnerRadius=0
CamShakeOuterRadius=300
CamShakeFalloff=1.5f
bOrientCameraShakeTowardsEpicenter=true
End Object
ExplosionTemplate=ExploTemplate0
}

View File

@ -14,6 +14,8 @@ class KFProj_Explosive_HRG_Kaboomstick extends KFProj_BallisticExplosive
/** Cached reference to owner weapon */
var protected KFWeapon OwnerWeapon;
var bool bCanNuke;
/** Initialize the projectile */
function Init( vector Direction )
{
@ -37,25 +39,9 @@ function bool ShouldWarnAIWhenFired()
*/
simulated protected function PrepareExplosionTemplate()
{
local KFPawn KFP;
local KFPerk CurrentPerk;
ExplosionTemplate.bIgnoreInstigator = true;
super.PrepareExplosionTemplate();
if( ExplosionActorClass == class'KFPerk_Demolitionist'.static.GetNukeExplosionActorClass() )
{
KFP = KFPawn( Instigator );
if( KFP != none )
{
CurrentPerk = KFP.GetPerk();
if( CurrentPerk != none )
{
CurrentPerk.SetLastHX25NukeTime( WorldInfo.TimeSeconds );
}
}
}
}
simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallComp)
@ -76,20 +62,7 @@ simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallCom
/** Only allow this projectile to cause a nuke if there hasn't been another nuke very recently */
simulated function bool AllowNuke()
{
local KFPawn KFP;
local KFPerk CurrentPerk;
KFP = KFPawn( Instigator );
if( KFP != none )
{
CurrentPerk = KFP.GetPerk();
if( CurrentPerk != none && `TimeSince(CurrentPerk.GetLastHX25NukeTime()) < 0.25f )
{
return false;
}
}
return super.AllowNuke();
return bCanNuke;
}
defaultproperties
@ -164,4 +137,6 @@ defaultproperties
AmbientSoundPlayEvent=none
AmbientSoundStopEvent=none
bCanNuke = true
}

View File

@ -137,7 +137,7 @@ defaultproperties
// explosion
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=60
Damage=45
DamageRadius=200
DamageFalloffExponent=0.5f
DamageDelay=0.f

View File

@ -0,0 +1,422 @@
//=============================================================================
// KFProj_HighExplosive_HRG_Warthog
//=============================================================================
// High explosive grenade launcher grenade for the HRG Warthog
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFProj_HighExplosive_HRG_Warthog extends KFProj_BallisticExplosive
hidedropdown;
var transient bool bHitWall;
simulated event PreBeginPlay()
{
local KFPawn InstigatorPawn;
local KFPawn_HRG_Warthog InstigatorPawnWarthog;
InstigatorPawnWarthog = KFPawn_HRG_Warthog(Instigator);
if (InstigatorPawnWarthog != none)
{
InstigatorPawn = KFPawn(InstigatorPawnWarthog.OwnerWeapon.Instigator);
if (InstigatorPawn != none)
{
Instigator = InstigatorPawn;
}
}
Super.PreBeginPlay();
}
simulated function bool AllowNuke()
{
return true;
}
simulated function bool AllowDemolitionistConcussive()
{
return true;
}
simulated function bool AllowDemolitionistExplosionChangeRadius()
{
return true;
}
// Used by Demolitionist Nuke and Mad Bomber skills
simulated function bool CanDud()
{
return false;
}
simulated function SetupDetonationTimer(float FuseTime)
{
if (FuseTime > 0)
{
bIsTimedExplosive = true;
SetTimer(FuseTime, false, 'ExplodeTimer');
}
}
function ExplodeTimer()
{
local Actor HitActor;
local vector HitLocation, HitNormal;
GetExplodeEffectLocation(HitLocation, HitNormal, HitActor);
TriggerExplosion(HitLocation, HitNormal, HitActor);
}
/**
* Trace down and get the location to spawn the explosion effects and decal
*/
simulated function GetExplodeEffectLocation(out vector HitLocation, out vector HitRotation, out Actor HitActor)
{
local vector EffectStartTrace, EffectEndTrace;
local TraceHitInfo HitInfo;
EffectStartTrace = Location + vect(0,0,1) * 4.f;
EffectEndTrace = EffectStartTrace - vect(0,0,1) * 32.f;
// Find where to put the decal
HitActor = Trace(HitLocation, HitRotation, EffectEndTrace, EffectStartTrace, false,, HitInfo, TRACEFLAG_Bullet);
// If the locations are zero (probably because this exploded in the air) set defaults
if( IsZero(HitLocation) )
{
HitLocation = Location;
}
if( IsZero(HitRotation) )
{
HitRotation = vect(0,0,1);
}
}
simulated event HitWall(vector HitNormal, actor Wall, PrimitiveComponent WallComp)
{
local Vector VNorm;
local rotator NewRotation;
local Vector Offset;
bHitWall = true;
if (bIsTimedExplosive)
{
// Reflect off Wall w/damping
VNorm = (Velocity dot HitNormal) * HitNormal;
Velocity = -VNorm * DampenFactor + (Velocity - VNorm) * DampenFactorParallel;
Speed = VSize(Velocity);
if (WorldInfo.NetMode != NM_DedicatedServer && Pawn(Wall) == none)
{
// do the impact effects
`ImpactEffectManager.PlayImpactEffects(Location, Instigator, HitNormal, GrenadeBounceEffectInfo, true );
}
// if we hit a pawn or we are moving too slowly stop moving and lay down flat
if ( Speed < MinSpeedBeforeStop )
{
ImpactedActor = Wall;
SetPhysics(PHYS_None);
if( ProjEffects != none )
{
ProjEffects.SetTranslation(LandedTranslationOffset);
}
// Position the shell on the ground
RotationRate.Yaw = 0;
RotationRate.Pitch = 0;
RotationRate.Roll = 0;
NewRotation = Rotation;
NewRotation.Pitch = 0;
if(ResetRotationOnStop)
{
SetRotation(NewRotation);
}
Offset.Z = LandedTranslationOffset.X;
SetLocation(Location + Offset);
}
return;
}
if( WorldInfo.NetMode == NM_Standalone ||
(WorldInfo.NetMode == NM_ListenServer && Instigator != none && Instigator.IsLocallyControlled()) )
{
TriggerExplosion(Location, HitNormal, None);
Shutdown(); // cleanup/destroy projectile
return;
}
if( Owner != none && KFWeapon( Owner ) != none && Instigator != none )
{
if( Instigator.Role == ROLE_Authority)
{
KFWeap_HRG_WarthogWeapon(Owner).ForceExplosionReplicateKill(Location, self);
}
}
StopSimulating(); // cleanup/destroy projectile
}
simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal)
{
// If we collided with a Siren shield, let the shield code handle touches
if( Other.IsA('KFTrigger_SirenProjectileShield') )
{
return;
}
if ( !bCollideWithTeammates && Pawn(Other) != None )
{
// Don't hit teammates
if( Other.GetTeamNum() == GetTeamNum() )
{
return;
}
}
// Process impact hits
if (Other != Instigator && !Other.bStatic)
{
ProcessBulletTouch(Other, HitLocation, HitNormal);
}
if (bIsTimedExplosive)
{
return;
}
if( WorldInfo.NetMode == NM_Standalone ||
(WorldInfo.NetMode == NM_ListenServer && Instigator != none && Instigator.IsLocallyControlled()) )
{
// Call KFProjectile base code.. as we don't want to repeat the KFProj_BallisticExplosive base again
TriggerExplosion(HitLocation, HitNormal, Other);
Shutdown(); // cleanup/destroy projectile
return;
}
if( Owner != none && KFWeapon( Owner ) != none && Instigator != none )
{
// Special case, for some very complicate reason around replication, instigators and the Warthog pawn
// This code on the base class was never triggered on Clients: (IsLocallyControlled() always fails, also on the rightful owner of he projectile)
// if( Instigator.Role < ROLE_Authority && Instigator.IsLocallyControlled() )
// Hence we call here a reliable client function on the Owner
// That will call exactly the same code that's inside that If on the base class of this file
if( Instigator.Role == ROLE_Authority)
{
KFWeap_HRG_WarthogWeapon(Owner).ForceExplosionReplicate(Other, HitLocation, HitNormal, self);
}
}
StopSimulating();
}
simulated event Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
if (WorldInfo.NetMode != NM_DedicatedServer)
{
if (bHitWall == false)
{
SetRotation(rotator(Normal(Velocity)));
}
}
}
/** This will cause the projectile to move to the Original Spawn location when first replicated. This solves the issue of the projectile spawning some distance away from the player when first replicated */
simulated function SyncOriginalLocation()
{
local vector MuzzleLocation, PredictedHitLocation, AimDir, EndTrace, MuzzleToPredictedHit;
if ( WorldInfo.NetMode == NM_DedicatedServer )
{
return;
}
// For remote pawns, have the projectile look like its actually
// coming from the muzzle of the third person weapon
if( bSyncToThirdPersonMuzzleLocation && Instigator != none
&& !Instigator.IsFirstPerson() && KFPawn(Owner) != none
&& KFPawn(Owner).WeaponAttachment != none )
{
MuzzleLocation = KFPawn(Owner).WeaponAttachment.GetMuzzleLocation(bFiredFromLeftHandWeapon ? 1 : 0);
// Set the aim direction to the vector along the line where the
// projectile would hit based on velocity. This is the most accurate
if( !IsZero(Velocity) )
{
AimDir = Normal(Velocity);
}
// Set the aim direction to the vector along the line where the
// projectile would hit based on where it has moved away from
// the original location
if( IsZero(Velocity) )
{
AimDir = Normal(Location-OriginalLocation);
}
// Use the rotation if the location calcs give a zero direction
if( IsZero(AimDir) )
{
AimDir = Normal(Vector(Rotation));
}
if( Location != MuzzleLocation )
{
// if projectile is spawned at different location than the third
// person muzzle location, then simulate an instant trace where
// the projectile would hit
EndTrace = Location + AimDir * 16384;
PredictedHitLocation = GetPredictedHitLocation(Location, EndTrace);
MuzzleToPredictedHit = Normal(PredictedHitLocation - MuzzleLocation);
// only adjust AimDir if PredictedHitLocation is "forward" (i.e. don't let projectile fire back towards the shooter)
//@todo: still need to make this less wonky (can still shoot straight up sometimes when using long weapons, like the sawblade shooter)
if( MuzzleToPredictedHit dot vector(Rotation) > 0.f )
{
// Then we realign projectile aim direction to match where the projectile would hit.
AimDir = MuzzleToPredictedHit;
}
}
// Move the projectile to the MuzzleLocation
SetLocation(MuzzleLocation);
// If the Velocity is zero (usually because the projectile impacted
// something on the server in its first tick before replicating)
// then turn its phyics and collion back on
if( IsZero(Velocity) )
{
SetPhysics(default.Physics);
SetCollision( default.bCollideActors, default.bBlockActors );
}
// Adjust the velocity of the projectile so it will hit where
// it is supposed to
Velocity = Speed * Normal(AimDir);
}
// set location based on 'OriginalLocation'
else if ( Role < ROLE_Authority )
{
// If the Velocity is zero (usually because the projectile impacted
// something on the server in its first tick before replicating)
// then turn its physics and collion back on and give it velocity
// again so the simulation will work properly on the client
if( IsZero(Velocity) )
{
SetPhysics(default.Physics);
// Set the aim direction to the vector along the line where the
// projectile would hit
AimDir = Normal(Location-OriginalLocation);
// Use the rotation if the location calcs give a zero direction
if( IsZero(AimDir) )
{
AimDir = Vector(Rotation);
}
Velocity = Speed * AimDir;
SetCollision( default.bCollideActors, default.bBlockActors );
}
SetLocation(OriginalLocation);
}
}
defaultproperties
{
bCollideWithTeammates = false
Physics=PHYS_Falling
Speed=2000
MaxSpeed=2000
TerminalVelocity=2000
TossZ=0
GravityScale=1.0
MomentumTransfer=50000.0
ArmDistSquared=0
LifeSpan=25.0f
bIsTimedExplosive = false
bWarnAIWhenFired=true
ProjFlightTemplate=ParticleSystem'WEP_HRG_Warthog_EMIT.FX_HRG_Warthog_Projectile'
ProjFlightTemplateZedTime=ParticleSystem'WEP_HRG_Warthog_EMIT.FX_HRG_Warthog_Projectile_ZEDTIME'
ProjDudTemplate=ParticleSystem'WEP_HRG_Warthog_EMIT.FX_HRG_Warthog_Projectile_Dud'
GrenadeBounceEffectInfo=KFImpactEffectInfo'FX_Impacts_ARCH.DefaultGrenadeImpacts'
ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01'
AltExploEffects=KFImpactEffectInfo'WEP_HRG_Warthog_ARCH.HRG_Warthog_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
ExplosionActorClass=class'KFExplosion_HRG_Warthog'
// explosion
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=35
DamageRadius=200
DamageFalloffExponent=1
DamageDelay=0.f
// Damage Effects
MyDamageType=class'KFDT_Explosive_HRG_Warthog_HighExplosive'
KnockDownStrength=0
FractureMeshRadius=200.0
FracturePartVel=500.0
ExplosionEffects=KFImpactEffectInfo'WEP_HRG_WarthogWeapon_ARCH.HRG_WarthogWeapon_GrenadeExplosion'
ExplosionSound=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_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
bIgnoreInstigator=true
ActorClassToIgnoreForDamage = class'KFPawn_Human'
End Object
ExplosionTemplate=ExploTemplate0
AmbientSoundPlayEvent=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Projectile_LP'
AmbientSoundStopEvent=AkEvent'WW_WEP_HRG_Warthog.Stop_WEP_HRG_Warthog_Projectile'
// The higher the more bouncing
DampenFactor=0.4f
DampenFactorParallel=0.4f
bHitWall = false
}

View File

@ -83,7 +83,7 @@ defaultproperties
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'
AltExploEffects=KFImpactEffectInfo'WEP_HRG_MedicMissile_ARCH.HRG_MedicMissile_Explosion'
ExplosionActorClass=class'KFExplosion_HRG_MedicMissile'

View File

@ -0,0 +1,170 @@
//=============================================================================
// KFSeasonalEventStats_Summer2023
//=============================================================================
// Tracks event-specific challenges/accomplishments for Summer 2023
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFSeasonalEventStats_Summer2023 extends KFSeasonalEventStats;
var transient private const int HRGBombardierZedsRequired, EMPRequired, StandYourGroundRequired, EndlessWaveRequired, SummerEventIndex;
private event Initialize(string MapName)
{
local string CapsMapName;
CapsMapName = Caps(MapName);
bObjectiveIsValidForMap[0] = 1; // Kill 1500 Zeds with HRG Bombardier
bObjectiveIsValidForMap[1] = 0; // Complete the Weekly on Subduction
bObjectiveIsValidForMap[2] = 1; // Stun 2500 Zeds with EMP affliction
bObjectiveIsValidForMap[3] = 1; // Complete 25 Stand your Ground objectives
bObjectiveIsValidForMap[4] = 0; // Complete wave 15 on Endless Hard or higher difficulty on Subduction
if (CapsMapName == "KF-SUBDUCTION")
{
bObjectiveIsValidForMap[1] = 1;
bObjectiveIsValidForMap[4] = 1;
}
SetSeasonalEventStatsMax(HRGBombardierZedsRequired, 0, EMPRequired, StandYourGroundRequired, 0);
}
private event GrantEventItems()
{
if (Outer.IsEventObjectiveComplete(0) &&
Outer.IsEventObjectiveComplete(1) &&
Outer.IsEventObjectiveComplete(2) &&
Outer.IsEventObjectiveComplete(3) &&
Outer.IsEventObjectiveComplete(4))
{
// TODO
GrantEventItem(9672);
}
}
simulated event OnGameWon(class<GameInfo> GameClass, int Difficulty, int GameLength, bool bCoOp)
{
local int ObjIdx;
// Complete the Weekly on Subduction
ObjIdx = 1;
if (bObjectiveIsValidForMap[ObjIdx] != 0)
{
if (GameClass == class'KFGameInfo_WeeklySurvival')
{
FinishedObjective(SummerEventIndex, ObjIdx);
}
}
}
simulated function OnTryCompleteObjective(int ObjectiveIndex, int EventIndex)
{
local int HRGBombardierIdx, EMPIdx, StandYourGroundIdx, ObjectiveLimit;
local bool bValidIdx;
HRGBombardierIdx = 0;
EMPIdx = 2;
StandYourGroundIdx = 3;
bValidIdx = false;
if (EventIndex == SummerEventIndex)
{
if (ObjectiveIndex == HRGBombardierIdx)
{
ObjectiveLimit = HRGBombardierZedsRequired;
bValidIdx = true;
}
else if (ObjectiveIndex == EMPIdx)
{
ObjectiveLimit = EMPRequired;
bValidIdx = true;
}
else if (ObjectiveIndex == StandYourGroundIdx)
{
ObjectiveLimit = StandYourGroundRequired;
bValidIdx = true;
}
if (bValidIdx && bObjectiveIsValidForMap[ObjectiveIndex] != 0)
{
IncrementSeasonalEventStat(ObjectiveIndex, 1);
if (Outer.GetSeasonalEventStatValue(ObjectiveIndex) >= ObjectiveLimit)
{
FinishedObjective(SummerEventIndex, ObjectiveIndex);
}
}
}
}
simulated event OnWaveCompleted(class<GameInfo> GameClass, int Difficulty, int WaveNum)
{
local int ObjIdx;
// Complete wave 15 on Endless Hard or higher difficulty on Subduction
ObjIdx = 4;
if (bObjectiveIsValidForMap[ObjIdx] != 0)
{
if (WaveNum >= EndlessWaveRequired && GameClass == class'KFGameInfo_Endless' && Difficulty >= `DIFFICULTY_HARD)
{
FinishedObjective(SummerEventIndex, ObjIdx);
}
}
}
simulated function OnAfflictionCaused(EAfflictionType Type)
{
local int ObjIdx;
// Stun 2500 Zeds with EMP affliction
ObjIdx = 2;
if (bObjectiveIsValidForMap[ObjIdx] != 0)
{
if (Type == AF_EMP)
{
IncrementSeasonalEventStat(ObjIdx, 1);
if (Outer.GetSeasonalEventStatValue(ObjIdx) >= EMPRequired)
{
FinishedObjective(SummerEventIndex, ObjIdx);
}
}
}
}
simulated function OnZedKilled(class<KFPawn_Monster> MonsterClass, int Difficulty, class<DamageType> DT)
{
local int ObjIdx;
// Kill 1500 Zeds with HRG Bombardier
ObjIdx = 0;
if (bObjectiveIsValidForMap[ObjIdx] != 0)
{
if (DT == class'KFDT_Explosive_HRG_Warthog' ||
DT == class'KFDT_Explosive_HRG_Warthog_HighExplosive' ||
DT == class'KFDT_Ballistic_HRG_Warthog')
{
IncrementSeasonalEventStat(ObjIdx, 1);
if (Outer.GetSeasonalEventStatValue(ObjIdx) >= HRGBombardierZedsRequired)
{
FinishedObjective(SummerEventIndex, ObjIdx);
}
}
}
}
defaultproperties
{
HRGBombardierZedsRequired=1500
EMPRequired=2500
StandYourGroundRequired=25
EndlessWaveRequired=15
SummerEventIndex=SEI_Summer
}

View File

@ -0,0 +1,113 @@
//=============================================================================
// KFWeapAttach_HRG_Warthog
//=============================================================================
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFWeapAttach_HRG_Warthog extends KFWeapAttach_DualBase;
`define AUTOTURRET_ATTACH_MIC_LED_INDEX 2
const ThrowAnim = 'Drone_Throw';
const CrouchThrowAnim = 'Drone_Throw_CH';
const DetonateAnim = 'Shoot';
const CrouchDetonateAnim = 'CH_Shoot';
const TransitionParamName = 'transition_full_to_empty';
const EmptyParamName = 'Blinking_0_off___1_on';
/** Completely overridden to play anims for both C4 firemodes (throw and detonate), also doesn't need to play effects */
simulated function bool ThirdPersonFireEffects( vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte )
{
local float Duration;
// Effects below this point are culled based on visibility and distance
if ( !ActorEffectIsRelevant(P, false, MaxFireEffectDistance) )
{
return false;
}
DecodeThirdPersonAnimRate( ThirdPersonAnimRateByte );
// Weapon shoot anims
if (P.FiringMode == 0)
{
// anim simply hides and unhides bone
Duration = WeapMesh.GetAnimLength( ThrowAnim );
WeapMesh.PlayAnim( ThrowAnim, Duration / ThirdPersonAnimRate,, true );
// use timer to make sure bone gets un-hidden (in case anim gets interrupted)
SetTimer( 0.75f, false, nameof(UnHide) );
}
else if (P.FiringMode == 5)
{
Duration = WeapMesh.GetAnimLength( DetonateAnim );
LeftWeapMesh.PlayAnim( DetonateAnim, Duration / ThirdPersonAnimRate,, true );
}
// Additive character shoot anims
if ( !P.IsDoingSpecialMove() )
{
if( P.FiringMode == 0 )
{
if ( P.bIsCrouched )
{
P.PlayBodyAnim(CrouchThrowAnim, EAS_CH_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime);
}
else
{
P.PlayBodyAnim(ThrowAnim, EAS_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime);
}
}
else if( P.FiringMode == 5 )
{
if ( P.bIsCrouched )
{
P.PlayBodyAnim(CrouchDetonateAnim, EAS_CH_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime);
}
else
{
P.PlayBodyAnim(DetonateAnim, EAS_UpperBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime);
}
}
}
// prevent using "aiming" KFAnim_BlendByTargetingMode since we don't have/need the aim anims for C4
P.LastWeaponFireTime = -1.f;
return true;
}
/** Unhides the C4 unit in hand (basically the same as the notify, but don't use the notify) */
simulated function UnHide()
{
if( WeapMesh != none )
{
WeapMesh.UnHideBoneByName( 'RW_Weapon' );
}
}
/** Special event added for weap attachments. Free for use */
function OnSpecialEvent(int Arg)
{
local float Value;
Value = (Arg - 1) / 100.0f;
if (Value >= 0)
{
if ( WeaponMIC == None && LeftWeapMesh != None )
{
WeaponMIC = LeftWeapMesh.CreateAndSetMaterialInstanceConstant(`AUTOTURRET_ATTACH_MIC_LED_INDEX);
}
WeaponMIC.SetScalarParameterValue(TransitionParamName, 1.0f - Value);
WeaponMIC.SetScalarParameterValue(EmptyParamName, Value == 0 ? 1 : 0);
}
}
defaultproperties
{
bHasLaserSight=false
}

View File

@ -0,0 +1,271 @@
//=============================================================================
// KFWeapAttach_HRG_WarthogWeapon
//=============================================================================
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFWeapAttach_HRG_WarthogWeap extends KFWeaponAttachment;
const DeployAnimName = 'Drone_Deploy';
const DroneFireAnim = 'Drone_Shoot';
const DroneEmptyStartAnim = 'Drone_Start_Empty';
const DroneEmptyAnim = 'Drone_Empty';
const DroneIdleAnim = 'Drone_Idle';
const DroneClosedAnim = 'Drone_IdleClose';
const LaserSightSocketName = 'LaserSightSocket';
const LaserColorParamName = '0blue_1red';
var transient MaterialInstanceConstant LaserDotMIC;
var transient MaterialInstanceConstant LaserBeamMIC;
simulated function float PlayDeployAnim()
{
local float Duration;
Duration = WeapMesh.GetAnimLength( DeployAnimName );
WeapMesh.PlayAnim( DeployAnimName, Duration / ThirdPersonAnimRate, false, true );
return Duration;
}
simulated function PlayEmptyState()
{
local float Duration;
ClearTimer(nameof(PlayIdleAnim));
Duration = WeapMesh.GetAnimLength( DroneEmptyStartAnim );
WeapMesh.PlayAnim( DroneEmptyStartAnim, Duration / ThirdPersonAnimRate, true, false);
SetTimer(Duration, false, 'PlayEmptyAnim');
}
simulated function PlayEmptyAnim()
{
local float Duration;
Duration = WeapMesh.GetAnimLength( DroneEmptyAnim );
WeapMesh.PlayAnim( DroneEmptyAnim, Duration / ThirdPersonAnimRate, true, false);
if (LaserSight != none)
{
LaserSight.ChangeVisibility(false);
}
}
simulated function PlayIdleAnim()
{
local float Duration;
Duration = WeapMesh.GetAnimLength( DroneIdleAnim );
WeapMesh.PlayAnim( DroneIdleAnim, Duration / ThirdPersonAnimRate, true, false );
}
simulated function PlayCloseAnim()
{
local float Duration;
Duration = WeapMesh.GetAnimLength( DroneClosedAnim );
WeapMesh.PlayAnim( DroneClosedAnim, Duration / ThirdPersonAnimRate, true, false );
}
/**
* Spawn all of the effects that will be seen in behindview/remote clients. This
* function is called from the pawn, and should only be called when on a remote client or
* if the local client is in a 3rd person mode.
* @return TRUE if the effect culling check passes
*/
simulated function bool ThirdPersonFireEffects( vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte )
{
// local EAnimSlotStance AnimType;
SpawnTracer(GetMuzzleLocation(), HitLocation);
// Effects below this point are culled based on visibility and distance
if ( !ActorIsRelevant(self, false, MaxFireEffectDistance) )
{
return false;
}
DecodeThirdPersonAnimRate( ThirdPersonAnimRateByte );
// Weapon shoot anims
if( !bWeapMeshIsPawnMesh )
{
PlayWeaponFireAnim();
}
/*
if( P.IsDoingSpecialMove() && P.SpecialMoves[P.SpecialMove].bAllowFireAnims )
{
AnimType = EAS_Additive;
}
else
{
AnimType = EAS_FullBody;
}
*/
// AnimType = EAS_FullBody;
// Character shoot anims
/*
if ( AnimType == EAS_Additive )
{
PlayPawnFireAnim( P, AnimType );
// interrupt other weapon action anims (e.g. Reload)
if( !P.IsDoingSpecialMove() )
{
P.StopBodyAnim(P.bIsCrouched ? EAS_CH_UpperBody : EAS_UpperBody, 0.1f);
}
if ( OnWeaponStateChanged != None )
{
OnWeaponStateChanged(true);
}
}
*/
// Always DEFAULT_FIREMODE
CauseMuzzleFlash(0);
return true;
}
simulated function bool ActorIsRelevant(Actor EffectInstigator, bool bForceDedicated, optional float VisibleCullDistance=5000.0, optional float HiddenCullDistance=350.0 )
{
local PlayerController P;
local float DistSq;
local vector CameraLoc;
local rotator CameraRot;
if ( EffectInstigator == None )
{
return FALSE;
}
// No local player, so only spawn on dedicated server if bForceDedicated
if ( WorldInfo.NetMode == NM_DedicatedServer )
{
return bForceDedicated;
}
if ( bForceDedicated && (WorldInfo.NetMode == NM_ListenServer) && (WorldInfo.Game.NumPlayers > 1) )
{
// Is acting as server, so spawn effect if bForceDedicated
return TRUE;
}
// Determine how far to the nearest local viewer
DistSq = 10000000000.0;
ForEach LocalPlayerControllers(class'PlayerController', P)
{
if ( P.GetViewTarget() == self )
{
return true;
}
P.GetPlayerViewPoint(CameraLoc, CameraRot);
DistSq = FMin(DistSq, VSizeSq(Location - CameraLoc)*Square(P.LODDistanceFactor));
}
if ( DistSq > VisibleCullDistance*VisibleCullDistance )
{
// never spawn beyond cull distance
return FALSE;
}
else if ( DistSq < HiddenCullDistance*HiddenCullDistance )
{
// If close enough, always spawn even if hidden
return TRUE;
}
/* This doesn't seem to be updating the render time, so ignore it */
return TRUE;
}
/** Plays fire animation on weapon mesh */
simulated function PlayWeaponFireAnim()
{
local float Duration;
local bool bAnimPlayed;
Duration = WeapMesh.GetAnimLength( DroneFireAnim );
bAnimPlayed = WeapMesh.PlayAnim( DroneFireAnim, Duration / ThirdPersonAnimRate, false, false );
if (bAnimPlayed)
{
ClearTimer(nameof(PlayIdleAnim));
SetTimer(Duration, false, nameof(PlayIdleAnim));
}
}
/**
Laser
*/
simulated function AttachLaserSight()
{
if ( WeapMesh != none && LaserSight == None && LaserSightArchetype != None )
{
LaserSight = new(self) Class'KFLaserSightAttachment' (LaserSightArchetype);
LaserSight.AttachLaserSight(WeapMesh, false, LaserSightSocketName);
}
}
simulated function UpdateLaserColor(bool bInCombat)
{
if (LaserSight != none)
{
if (LaserDotMIC == none)
{
LaserDotMIC = LaserSight.LaserDotMeshComp.CreateAndSetMaterialInstanceConstant(0);
}
if (LaserBeamMIC == none)
{
LaserBeamMIC = LaserSight.LaserBeamMeshComp.CreateAndSetMaterialInstanceConstant(0);
}
}
if (LaserDotMIC != none)
{
LaserDotMIC.SetScalarParameterValue(LaserColorParamName, bInCombat ? 1 : 0);
}
if (LaserBeamMIC != none)
{
LaserBeamMIC.SetScalarParameterValue(LaserColorParamName, bInCombat ? 1 : 0);
}
}
/**
* Assign weapon skin to 3rd person mesh
*/
simulated event SetWeaponSkin(int ItemId, optional bool bFinishedLoading = false)
{
local array<MaterialInterface> SkinMICs;
if ( ItemId > 0 && WorldInfo.NetMode != NM_DedicatedServer && !bWaitingForWeaponSkinLoad)
{
if (!bFinishedLoading && StartLoadWeaponSkin(ItemId))
{
return;
}
SkinMICs = class'KFWeaponSkinList'.static.GetWeaponSkin(ItemId, WST_ThirdPerson);
if ( SkinMICs.Length > 0 )
{
WeapMesh.SetMaterial(0, SkinMICs[0]);
}
}
}
defaultproperties
{
//defaults
bHasLaserSight=true
}

View File

@ -0,0 +1,153 @@
//=============================================================================
// KFWeapAttach_shotgun_s12
//=============================================================================
//
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFWeapAttach_Shotgun_S12 extends KFWeaponAttachment;
const SecondaryMuzzleSocket = 'MuzzleFlashAlt';
const SecondaryReloadAnim = 'Reload_Secondary';
const SecondaryReloadCHAnim = 'Reload_Secondary_CH';
const SecondaryReloadEliteAnim = 'Reload_Secondary_Elite';
const SecondaryReloadEliteCHAnim = 'Reload_Secondary_Elite_CH';
var transient ParticleSystemComponent ExplosionPSC;
var transient ParticleSystem ExplosionEffect;
var protected transient KFMuzzleFlash SecondaryMuzzleFlash;
var protected transient bool bWasSecondaryShot;
simulated function bool ThirdPersonFireEffects( vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte )
{
local vector SocketLocation;
bWasSecondaryShot = P.FiringMode == 1;
if (Super.ThirdPersonFireEffects(HitLocation, P, ThirdPersonAnimRateByte))
{
if (P.FiringMode == 1 && !P.IsLocallyControlled())
{
if (ExplosionEffect != None)
{
WeapMesh.GetSocketWorldLocationAndRotation('MuzzleFlashAlt', SocketLocation);
ExplosionPSC = WorldInfo.MyEmitterPool.SpawnEmitter(class'KFWeap_Shotgun_S12'.default.ExplosionEffect, SocketLocation, rotator(vect(0,0,1)));
ExplosionPSC.ActivateSystem();
}
}
return true;
}
return false;
}
/** @return the starting location for effects (e.g. tracers) */
simulated function vector GetMuzzleLocation(optional byte MuzzleID)
{
if (bWasSecondaryShot)
{
return GetAltMuzzleLocation();
}
else
{
return Super.GetMuzzleLocation(MuzzleID);
}
}
simulated function vector GetAltMuzzleLocation(optional byte MuzzleID)
{
local vector SocketLocation;
if (SecondaryMuzzleFlash == None)
{
AttachMuzzleFlash();
}
if( SecondaryMuzzleFlash != none )
{
WeapMesh.GetSocketWorldLocationAndRotation(SecondaryMuzzleFlash.GetSocketName(), SocketLocation);
return SocketLocation;
}
return Super.GetMuzzleLocation(MuzzleID);
}
simulated event Tick(float DeltaTime)
{
local vector SocketLocation;
Super.Tick(DeltaTime);
if (ExplosionPSC != none && ExplosionPSC.bIsActive)
{
WeapMesh.GetSocketWorldLocationAndRotation('MuzzleFlashAlt', SocketLocation);
ExplosionPSC.SetVectorParameter('WeaponEndpoint', SocketLocation);
}
}
simulated function CauseMuzzleFlash(byte FiringMode)
{
if ( FiringMode == 1 ) // AltFire
{
if (SecondaryMuzzleFlash == None)
{
AttachMuzzleFlash();
}
if (SecondaryMuzzleFlash != None )
{
SecondaryMuzzleFlash.CauseMuzzleFlash(FiringMode);
if ( SecondaryMuzzleFlash.bAutoActivateShellEject )
{
SecondaryMuzzleFlash.CauseShellEject();
}
}
}
else
{
Super.CauseMuzzleFlash(FiringMode);
}
}
simulated function AttachMuzzleFlash()
{
Super.AttachMuzzleFlash();
if ( WeapMesh != none && SecondaryMuzzleFlash == None )
{
SecondaryMuzzleFlash = new(self) Class'KFMuzzleFlash'(class'KFWeap_Shotgun_S12'.default.SecondaryMuzzleFlashTemplate);
SecondaryMuzzleFlash.AttachMuzzleFlash(WeapMesh, SecondaryMuzzleSocket,);
}
}
simulated function PlayReloadMagazineAnim(EWeaponState NewWeaponState, KFPawn P)
{
local name AnimName;
switch (NewWeaponState)
{
case WEP_ReloadSecondary:
AnimName = (P.bIsCrouched) ? SecondaryReloadCHAnim : SecondaryReloadAnim;
break;
case WEP_ReloadSecondary_Elite:
AnimName = (P.bIsCrouched) ? SecondaryReloadEliteCHAnim : SecondaryReloadEliteAnim;
break;
}
if (AnimName != '')
{
PlayCharacterMeshAnim(P, AnimName, true);
}
else
{
Super.PlayReloadMagazineAnim(NewWeaponState, P);
}
}
defaultproperties
{
ExplosionEffect=ParticleSystem'WEP_1P_Saiga12_EMIT.FX_Saiga12_Explosion_3P'
bWasSecondaryShot=false
}

View File

@ -192,7 +192,14 @@ simulated function Detonate(optional bool bKeepTurret = false)
continue;
}
KFPawn_AutoTurret(TurretsCopy[i]).SetTurretState(ETS_Detonate);
if (KFPawn_HRG_Warthog(TurretsCopy[i]) != none)
{
KFPawn_HRG_Warthog(TurretsCopy[i]).SetTurretState(ETS_Detonate);
}
else if (KFPawn_Autoturret(TurretsCopy[i]) != none)
{
KFPawn_Autoturret(TurretsCopy[i]).SetTurretState(ETS_Detonate);
}
}
KFPC.DeployedTurrets.Remove(bKeepTurret ? 1 : 0, KFPC.DeployedTurrets.Length);
@ -231,6 +238,7 @@ function RemoveDeployedTurret( optional int Index = INDEX_NONE, optional Actor T
function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon )
{
local int i;
local Actor WeaponPawn;
super.SetOriginalValuesFromPickup( PickedUpWeapon );
@ -256,13 +264,24 @@ function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon )
for( i = 0; i < NumDeployedTurrets; ++i )
{
// charge alerts (beep, light) need current instigator
KFPC.DeployedTurrets[i].Instigator = Instigator;
KFPC.DeployedTurrets[i].SetOwner(self);
WeaponPawn = KFPC.DeployedTurrets[i];
if (WeaponPawn != none)
{
// charge alerts (beep, light) need current instigator
WeaponPawn.Instigator = Instigator;
WeaponPawn.SetOwner(self);
if( Instigator.Controller != none )
{
KFPawn_AutoTurret(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller;
if (Instigator.Controller != none)
{
if (KFPawn_HRG_Warthog(KFPC.DeployedTurrets[i]) != none)
{
KFPawn_HRG_Warthog(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller;
}
else if (KFPawn_Autoturret(KFPC.DeployedTurrets[i]) != none)
{
KFPawn_Autoturret(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller;
}
}
}
}
}
@ -343,6 +362,8 @@ simulated function bool HasAmmo( byte FireModeNum, optional int Amount )
simulated function BeginFire( byte FireModeNum )
{
local bool bCanDetonate;
// Clear any pending detonate if we pressed the main fire
// That prevents strange holding right click behaviour and sound issues
if (FireModeNum == DEFAULT_FIREMODE)
@ -359,11 +380,21 @@ simulated function BeginFire( byte FireModeNum )
if (NumDeployedTurrets > 0 && bTurretReadyToUse)
{
PrepareAndDetonate();
bCanDetonate = NumDeployedTurrets > 0;
if (bCanDetonate)
{
PrepareAndDetonate();
}
}
}
else
{
if (KFPC != none)
{
NumDeployedTurrets = KFPC.DeployedTurrets.Length;
}
if (FireModeNum == DEFAULT_FIREMODE
&& NumDeployedTurrets >= MaxTurretsDeployed
&& HasAnyAmmo())
@ -573,7 +604,15 @@ function CheckTurretAmmo()
if (KFPC.DeployedTurrets.Length > 0)
{
Weapon = KFWeapon(KFPawn_AutoTurret(KFPC.DeployedTurrets[0]).Weapon);
if (KFPawn_HRG_Warthog(KFPC.DeployedTurrets[0]) != none)
{
Weapon = KFWeapon(KFPawn_HRG_Warthog(KFPC.DeployedTurrets[0]).Weapon);
}
else if (KFPawn_Autoturret(KFPC.DeployedTurrets[0]) != none)
{
Weapon = KFWeapon(KFPawn_Autoturret(KFPC.DeployedTurrets[0]).Weapon);
}
if (Weapon != none)
{
Percentage = float(Weapon.AmmoCount[0]) / Weapon.MagazineCapacity[0];
@ -797,4 +836,5 @@ defaultproperties
NumBloodMapMaterials=3
bDetonateLocked=false
CurrentAmmoPercentage=-1.0f
}

View File

@ -45,13 +45,13 @@ defaultproperties
End Object
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Bludgeon_ChainBat'
InstantHitDamage(DEFAULT_FIREMODE)=68// 34
InstantHitDamage(DEFAULT_FIREMODE)=75// 34
InstantHitDamageTypes(HEAVY_ATK_FIREMODE)=class'KFDT_Bludgeon_ChainBatHeavy'
InstantHitDamage(HEAVY_ATK_FIREMODE)=90 //68
InstantHitDamage(HEAVY_ATK_FIREMODE)=100 //68
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_ChainBatBash'
InstantHitDamage(BASH_FIREMODE)=68
InstantHitDamage(BASH_FIREMODE)=75
// Inventory
GroupPriority=50

View File

@ -40,7 +40,7 @@ defaultproperties
End Object
// Inventory
GroupPriority=25
GroupPriority=47
InventorySize=4
WeaponSelectTexture=Texture2D'ui_weaponselect_tex.UI_WeaponSelect_Crovel'

View File

@ -22,6 +22,11 @@ var bool bLastFireWasAlt;
var const bool bDebugDrawVortex;
static simulated event EFilterTypeUI GetTraderFilter()
{
return FT_Explosive;
}
simulated function Activate()
{
super.Activate();

View File

@ -15,6 +15,8 @@ var protected const array<vector2D> PelletSpread;
/** Last time a submunition projectile was fired from this weapon */
var float LastSubmunitionFireTime;
var transient bool AlreadyIssuedCanNuke;
/*********************************************************************************************
Firing / Projectile
********************************************************************************************* */
@ -79,6 +81,38 @@ static simulated event EFilterTypeUI GetAltTraderFilter()
return FT_Pistol;
}
simulated function KFProjectile SpawnAllProjectiles(class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir)
{
local KFProjectile Proj;
AlreadyIssuedCanNuke = false;
Proj = Super.SpawnAllProjectiles(KFProjClass, RealStartLoc, AimDir);
AlreadyIssuedCanNuke = false;
return Proj;
}
simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir )
{
local KFProj_ExplosiveSubMunition_HX25 Proj;
Proj = KFProj_ExplosiveSubMunition_HX25(Super.SpawnProjectile(KFProjClass, RealStartLoc, AimDir));
if (AlreadyIssuedCanNuke == false)
{
Proj.bCanNuke = true;
AlreadyIssuedCanNuke = true;
}
else
{
Proj.bCanNuke = false;
}
return Proj;
}
defaultproperties
{
ForceReloadTime=0.3f
@ -200,4 +234,6 @@ defaultproperties
WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.4f), (Stat=EWUS_Weight, Add=2)))
WeaponUpgrades[3]=(Stats=((Stat=EWUS_Damage0, Scale=1.6f), (Stat=EWUS_Weight, Add=3)))
WeaponUpgrades[4]=(Stats=((Stat=EWUS_Damage0, Scale=1.9f), (Stat=EWUS_Weight, Add=4)))
AlreadyIssuedCanNuke = false
}

View File

@ -55,6 +55,11 @@ Replication
ChargeTime;
}
static simulated event EFilterTypeUI GetTraderFilter()
{
return FT_Projectile;
}
simulated event PostInitAnimTree( SkeletalMeshComponent SkelComp )
{
local vector vec;

View File

@ -343,7 +343,7 @@ defaultproperties
InstantHitDamage(ALTFIRE_FIREMODE)=0.0 //override by AfflictionHandler.GetBigHeadAfflictionDamageModifier()
InstantHitDamageTypes(ALTFIRE_FIREMODE)=Class'KFDT_Blast_HRG_CranialPopper'
FireInterval(ALTFIRE_FIREMODE)=+0.25
AmmoCost(ALTFIRE_FIREMODE)=100
AmmoCost(ALTFIRE_FIREMODE)=25
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_CranialPopper'

View File

@ -0,0 +1,844 @@
//=============================================================================
// KFWeap_HRG_Warthog
//=============================================================================
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFWeap_HRG_Warthog extends KFWeap_ThrownBase;
`define AUTOTURRET_MIC_LED_INDEX 2
const DETONATE_FIREMODE = 5; // NEW - IronSights Key
var(Animations) const editconst name DetonateAnim;
var(Animations) const editconst name DetonateLastAnim;
/** Sound to play upon successful detonation */
var() AkEvent DetonateAkEvent;
/** Strenght applied to forward dir to get the throwing velocity */
var const float ThrowStrength;
/** Max num of turrets that can be deployed */
var const byte MaxTurretsDeployed;
/** Offset to spawn the dron (screen coordinates) */
var const vector TurretSpawnOffset;
var transient byte NumDeployedTurrets;
var transient KFPlayerController KFPC;
/** If the turret is in a state available for throw another to fix some animation issues. */
var transient bool bTurretReadyToUse;
var repnotify float CurrentAmmoPercentage;
const TransitionParamName = 'transition_full_to_empty';
const EmptyParamName = 'Blinking_0_off___1_on';
var transient bool bDetonateLocked;
replication
{
if( bNetDirty )
NumDeployedTurrets, CurrentAmmoPercentage, bTurretReadyToUse;
}
simulated event ReplicatedEvent(name VarName)
{
if (VarName == nameof(CurrentAmmoPercentage))
{
UpdateMaterialColor(CurrentAmmoPercentage);
}
else
{
Super.ReplicatedEvent(VarName);
}
}
simulated event PreBeginPlay()
{
local class<KFWeap_HRG_WarthogWeapon> WeaponClass;
super.PreBeginPlay();
WeaponClass = class<KFWeap_HRG_WarthogWeapon> (DynamicLoadObject(class'KFPawn_HRG_Warthog'.default.WeaponDefinition.default.WeaponClassPath, class'Class'));
WeaponClass.static.TriggerAsyncContentLoad(WeaponClass);
}
simulated event PostBeginPlay()
{
super.PostBeginPlay();
if (Role == ROLE_Authority)
{
KFPC = KFPlayerController(Instigator.Controller);
NumDeployedTurrets = KFPC.DeployedTurrets.Length;
}
}
/** Route ironsight player input to detonate */
simulated function SetIronSights(bool bNewIronSights)
{
if ( !Instigator.IsLocallyControlled() )
{
return;
}
if ( bNewIronSights )
{
StartFire(DETONATE_FIREMODE);
}
else
{
StopFire(DETONATE_FIREMODE);
}
}
/** Overridded to add spawned turret to list of spawned turrets */
simulated function Projectile ProjectileFire()
{
local vector SpawnLocation, SpawnDirection;
local KFPawn_HRG_Warthog SpawnedActor;
if (Role != ROLE_Authority)
{
return none;
}
if (CurrentFireMode == DEFAULT_FIREMODE)
{
GetTurretSpawnLocationAndDir(SpawnLocation, SpawnDirection);
SpawnedActor = Spawn(class'KFPawn_HRG_Warthog', self,, SpawnLocation + (TurretSpawnOffset >> Rotation), Rotation,,true);
if( SpawnedActor != none )
{
SpawnedActor.OwnerWeapon = self;
SpawnedActor.SetPhysics(PHYS_Falling);
SpawnedActor.Velocity = SpawnDirection * ThrowStrength;
SpawnedActor.UpdateInstigator(Instigator);
SpawnedActor.UpdateWeaponUpgrade(CurrentWeaponUpgradeIndex);
SpawnedActor.SetTurretState(ETS_Throw);
if (KFPC != none)
{
KFPC.DeployedTurrets.AddItem( SpawnedActor );
NumDeployedTurrets = KFPC.DeployedTurrets.Length;
}
bTurretReadyToUse = false;
bForceNetUpdate = true;
}
return none;
}
return super.ProjectileFire();
}
simulated function GetTurretSpawnLocationAndDir(out vector SpawnLocation, out vector SpawnDirection)
{
local vector StartTrace, EndTrace, RealStartLoc, AimDir;
local ImpactInfo TestImpact;
local vector DirA, DirB;
local Quat Q;
// This is where we would start an instant trace. (what CalcWeaponFire uses)
StartTrace = GetSafeStartTraceLocation();
AimDir = Vector(GetAdjustedAim( StartTrace ));
// this is the location where the projectile is spawned.
RealStartLoc = GetPhysicalFireStartLoc(AimDir);
if( StartTrace != RealStartLoc )
{
// if projectile is spawned at different location of crosshair,
// then simulate an instant trace where crosshair is aiming at, Get hit info.
EndTrace = StartTrace + AimDir * GetTraceRange();
TestImpact = CalcWeaponFire( StartTrace, EndTrace );
// Store the original aim direction without correction
DirB = AimDir;
// Then we realign projectile aim direction to match where the crosshair did hit.
AimDir = Normal(TestImpact.HitLocation - RealStartLoc);
// Store the desired corrected aim direction
DirA = AimDir;
// Clamp the maximum aim adjustment for the AimDir so you don't get wierd
// cases where the projectiles velocity is going WAY off of where you
// are aiming. This can happen if you are really close to what you are
// shooting - Ramm
if ( (DirA dot DirB) < MaxAimAdjust_Cos )
{
Q = QuatFromAxisAndAngle(Normal(DirB cross DirA), MaxAimAdjust_Angle);
AimDir = QuatRotateVector(Q,DirB);
}
}
SpawnDirection = AimDir;
SpawnLocation = RealStartLoc;
}
/** Detonates the oldest turret */
simulated function Detonate(optional bool bKeepTurret = false)
{
local int i;
local array<Actor> TurretsCopy;
// auto switch weapon when out of ammo and after detonating the last deployed turret
if( Role == ROLE_Authority )
{
TurretsCopy = KFPC.DeployedTurrets;
for (i = 0; i < TurretsCopy.Length; i++)
{
if (bKeepTurret && i == 0)
{
continue;
}
if (KFPawn_HRG_Warthog(TurretsCopy[i]) != none)
{
KFPawn_HRG_Warthog(TurretsCopy[i]).SetTurretState(ETS_Detonate);
}
else if (KFPawn_Autoturret(TurretsCopy[i]) != none)
{
KFPawn_Autoturret(TurretsCopy[i]).SetTurretState(ETS_Detonate);
}
}
KFPC.DeployedTurrets.Remove(bKeepTurret ? 1 : 0, KFPC.DeployedTurrets.Length);
SetReadyToUse(true);
if( !HasAnyAmmo() && NumDeployedTurrets == 0 )
{
if( CanSwitchWeapons() )
{
Instigator.Controller.ClientSwitchToBestWeapon(false);
}
}
}
}
/** Removes a turret from the list using either an index or an actor and updates NumDeployedTurrets */
function RemoveDeployedTurret( optional int Index = INDEX_NONE, optional Actor TurretActor )
{
if( Index == INDEX_NONE )
{
if( TurretActor != none )
{
Index = KFPC.DeployedTurrets.Find( TurretActor );
}
}
if( Index != INDEX_NONE )
{
KFPC.DeployedTurrets.Remove( Index, 1 );
NumDeployedTurrets = KFPC.DeployedTurrets.Length;
bForceNetUpdate = true;
}
}
function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon )
{
local int i;
local Actor WeaponPawn;
super.SetOriginalValuesFromPickup( PickedUpWeapon );
if (PickedUpWeapon.KFPlayer != none && PickedUpWeapon.KFPlayer != KFPC)
{
for (i = 0; i < PickedUpWeapon.KFPlayer.DeployedTurrets.Length; i++)
{
KFPC.DeployedTurrets.AddItem(PickedUpWeapon.KFPlayer.DeployedTurrets[i]);
}
PickedUpWeapon.KFPlayer.DeployedTurrets.Remove(0, PickedUpWeapon.KFPlayer.DeployedTurrets.Length);
}
if (KFPC.DeployedTurrets.Length > 1)
{
Detonate(true);
}
PickedUpWeapon.KFPlayer = none;
NumDeployedTurrets = KFPC.DeployedTurrets.Length;
bForceNetUpdate = true;
for( i = 0; i < NumDeployedTurrets; ++i )
{
WeaponPawn = KFPC.DeployedTurrets[i];
if (WeaponPawn != none)
{
// charge alerts (beep, light) need current instigator
WeaponPawn.Instigator = Instigator;
WeaponPawn.SetOwner(self);
if (Instigator.Controller != none)
{
if (KFPawn_HRG_Warthog(KFPC.DeployedTurrets[i]) != none)
{
KFPawn_HRG_Warthog(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller;
}
else if (KFPawn_Autoturret(KFPC.DeployedTurrets[i]) != none)
{
KFPawn_Autoturret(KFPC.DeployedTurrets[i]).InstigatorController = Instigator.Controller;
}
}
}
}
}
/**
* Drop this item out in to the world
*/
function DropFrom(vector StartLocation, vector StartVelocity)
{
local DroppedPickup P;
// Offset spawn closer to eye location
StartLocation.Z += Instigator.BaseEyeHeight / 2;
// for some reason, Inventory::DropFrom removes weapon from inventory whether it was able to spawn the pickup or not.
// we only want the weapon removed from inventory if pickup was successfully spawned, so instead of calling the supers,
// do all the super functionality here.
if( !CanThrow() )
{
return;
}
if( DroppedPickupClass == None || DroppedPickupMesh == None )
{
Destroy();
return;
}
// the last bool param is to prevent collision from preventing spawns
P = Spawn(DroppedPickupClass,,, StartLocation,,,true);
if( P == None )
{
// if we can't spawn the pickup (likely for collision reasons),
// just return without removing from inventory or destroying, which removes from inventory
PlayerController(Instigator.Controller).ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_FailedDropInventory );
return;
}
if( Instigator != None && Instigator.InvManager != None )
{
Instigator.InvManager.RemoveFromInventory(Self);
if( Instigator.IsAliveAndWell() && !Instigator.InvManager.bPendingDelete )
{
`DialogManager.PlayDropWeaponDialog( KFPawn(Instigator) );
}
}
SetupDroppedPickup( P, StartVelocity );
KFDroppedPickup(P).PreviousOwner = KFPlayerController(Instigator.Controller);
Instigator = None;
GotoState('');
AIController = None;
}
/**
* Returns true if this weapon uses a secondary ammo pool
*/
static simulated event bool UsesAmmo()
{
return true;
}
simulated function bool HasAmmo( byte FireModeNum, optional int Amount )
{
if( FireModeNum == DETONATE_FIREMODE )
{
return NumDeployedTurrets > 0;
}
return super.HasAmmo( FireModeNum, Amount );
}
simulated function BeginFire( byte FireModeNum )
{
local bool bCanDetonate;
// Clear any pending detonate if we pressed the main fire
// That prevents strange holding right click behaviour and sound issues
if (FireModeNum == DEFAULT_FIREMODE)
{
ClearPendingFire(DETONATE_FIREMODE);
}
if (FireModeNum == DETONATE_FIREMODE )
{
if (bDetonateLocked)
{
return;
}
if (NumDeployedTurrets > 0 && bTurretReadyToUse)
{
bCanDetonate = NumDeployedTurrets > 0;
if (bCanDetonate)
{
PrepareAndDetonate();
}
}
}
else
{
if (KFPC != none)
{
NumDeployedTurrets = KFPC.DeployedTurrets.Length;
}
if (FireModeNum == DEFAULT_FIREMODE
&& NumDeployedTurrets >= MaxTurretsDeployed
&& HasAnyAmmo())
{
if (!bTurretReadyToUse)
{
return;
}
PrepareAndDetonate();
}
super.BeginFire( FireModeNum );
}
}
simulated function PrepareAndDetonate()
{
local name DetonateAnimName;
local float AnimDuration;
local bool bInSprintState;
DetonateAnimName = ShouldPlayLastAnims() ? DetonateLastAnim : DetonateAnim;
AnimDuration = MySkelMesh.GetAnimLength( DetonateAnimName );
bInSprintState = IsInState( 'WeaponSprinting' );
if( WorldInfo.NetMode != NM_DedicatedServer )
{
if( NumDeployedTurrets > 0 )
{
PlaySoundBase( DetonateAkEvent, true );
}
if( bInSprintState )
{
AnimDuration *= 0.25f;
PlayAnimation( DetonateAnimName, AnimDuration );
}
else
{
PlayAnimation( DetonateAnimName );
}
}
if( Role == ROLE_Authority )
{
Detonate();
}
IncrementFlashCount();
if( bInSprintState )
{
SetTimer( AnimDuration * 0.8f, false, nameof(PlaySprintStart) );
}
else
{
SetTimer( AnimDuration * 0.5f, false, nameof(GotoActiveState) );
}
}
// do nothing, as we have no alt fire mode
simulated function AltFireMode();
/** Allow weapons with abnormal state transitions to always use zed time resist*/
simulated function bool HasAlwaysOnZedTimeResist()
{
return GetStateName() != FiringStatesArray[BASH_FIREMODE];
}
/*********************************************************************************************
* State Active
* A Weapon this is being held by a pawn should be in the active state. In this state,
* a weapon should loop any number of idle animations, as well as check the PendingFire flags
* to see if a shot has been fired.
*********************************************************************************************/
simulated state Active
{
/** Overridden to prevent playing fidget if play has no more ammo */
simulated function bool CanPlayIdleFidget(optional bool bOnReload)
{
if( !HasAmmo(0) )
{
return false;
}
return super.CanPlayIdleFidget( bOnReload );
}
}
/*********************************************************************************************
* State WeaponDetonating
* The weapon is in this state while detonating a charge
*********************************************************************************************/
simulated function GotoActiveState();
simulated state WeaponDetonating
{
ignores AllowSprinting;
simulated event BeginState( name PreviousStateName )
{
PrepareAndDetonate();
}
simulated function GotoActiveState()
{
GotoState('Active');
}
}
/*********************************************************************************************
* State WeaponThrowing
* Handles throwing weapon (similar to state GrenadeFiring)
*********************************************************************************************/
simulated state WeaponThrowing
{
/** Never refires. Must re-enter this state instead. */
simulated function bool ShouldRefire()
{
return false;
}
simulated function EndState(Name NextStateName)
{
local KFPerk InstigatorPerk;
Super.EndState(NextStateName);
//Targeted fix for Demolitionist w/ the C4. It should remain in zed time while waiting on
// the fake reload to be triggered. This will return 0 for other perks.
InstigatorPerk = GetPerk();
if( InstigatorPerk != none )
{
SetZedTimeResist( InstigatorPerk.GetZedTimeModifier(self) );
}
}
}
/*********************************************************************************************
* State WeaponEquipping
* The Weapon is in this state while transitioning from Inactive to Active state.
* Typically, the weapon will remain in this state while its selection animation is being played.
* While in this state, the weapon cannot be fired.
*********************************************************************************************/
simulated state WeaponEquipping
{
simulated event BeginState( name PreviousStateName )
{
super.BeginState( PreviousStateName );
// perform a "reload" if we refilled our ammo from empty while it was unequipped
if( !HasAmmo(THROW_FIREMODE) && HasSpareAmmo() )
{
PerformArtificialReload();
}
StopFire(DETONATE_FIREMODE);
}
}
/*********************************************************************************************
* State WeaponPuttingDown
* Putting down weapon in favor of a new one.
* Weapon is transitioning to the Inactive state.
*
* Detonating while putting down causes C4 not to be put down, which causes problems, so let's
* just ignore SetIronSights, which causes detonation
*********************************************************************************************/
simulated state WeaponPuttingDown
{
ignores SetIronSights;
simulated event BeginState( name PreviousStateName )
{
super.BeginState( PreviousStateName );
StopFire(DETONATE_FIREMODE);
}
}
/*********************************************************************************************
* @name Trader
*********************************************************************************************/
/** Returns trader filter index based on weapon type */
static simulated event EFilterTypeUI GetTraderFilter()
{
return FT_Explosive;
}
function CheckTurretAmmo()
{
local float Percentage;
local KFWeapon Weapon;
local KFPawn KFP;
if (Role == Role_Authority)
{
if (KFPC == none)
{
return;
}
if (KFPC.DeployedTurrets.Length > 0)
{
if (KFPawn_HRG_Warthog(KFPC.DeployedTurrets[0]) != none)
{
Weapon = KFWeapon(KFPawn_HRG_Warthog(KFPC.DeployedTurrets[0]).Weapon);
}
else if (KFPawn_Autoturret(KFPC.DeployedTurrets[0]) != none)
{
Weapon = KFWeapon(KFPawn_Autoturret(KFPC.DeployedTurrets[0]).Weapon);
}
if (Weapon != none)
{
Percentage = float(Weapon.AmmoCount[0]) / Weapon.MagazineCapacity[0];
if (Percentage != CurrentAmmoPercentage)
{
CurrentAmmoPercentage = Percentage;
bNetDirty = true;
if (WorldInfo.NetMode == NM_Standalone)
{
UpdateMaterialColor(CurrentAmmoPercentage);
}
else
{
KFP = KFPawn(Instigator);
if (KFP != none)
{
KFP.OnWeaponSpecialAction( 1 + (CurrentAmmoPercentage * 100) );
}
}
}
}
}
}
}
function SetReadyToUse(bool bReady)
{
if (bTurretReadyToUse != bReady)
{
bTurretReadyToUse = bReady;
bNetDirty = true;
}
}
simulated event Tick(float DeltaTime)
{
super.Tick(DeltaTime);
if (Role == Role_Authority)
{
CheckTurretAmmo();
}
}
simulated function UpdateMaterialColor(float Percentage)
{
if (NumDeployedTurrets == 0)
{
WeaponMICs[`AUTOTURRET_MIC_LED_INDEX].SetScalarParameterValue(EmptyParamName, 0);
}
else if (Percentage >= 0)
{
WeaponMICs[`AUTOTURRET_MIC_LED_INDEX].SetScalarParameterValue(TransitionParamName, 1.0f - Percentage);
WeaponMICs[`AUTOTURRET_MIC_LED_INDEX].SetScalarParameterValue(EmptyParamName, Percentage == 0 ? 1 : 0);
}
}
simulated function SetWeaponUpgradeLevel(int WeaponUpgradeLevel)
{
local Actor Turret;
local KFPawn_HRG_Warthog TurretPawn;
super.SetWeaponUpgradeLevel(WeaponUpgradeLevel);
if (KFPC != none)
{
foreach KFPC.DeployedTurrets(Turret)
{
TurretPawn = KFPawn_HRG_Warthog(Turret);
if (TurretPawn != none)
{
TurretPawn.UpdateWeaponUpgrade(WeaponUpgradeLevel);
}
}
}
}
/**
* GRENADE FIRING
* There's a bug that alt fire interrupts the grenade anim at any moment,
* This avoids being able to altfire until the throw animation ends or the
* interrupt notify is reached.
*/
simulated state GrenadeFiring
{
simulated function EndState(Name NextStateName)
{
ClearDetonateLock();
Super.EndState(NextStateName);
}
}
/** Play animation at the start of the GrenadeFiring state */
simulated function PlayGrenadeThrow()
{
local name WeaponFireAnimName;
local float InterruptTime;
PlayFiringSound(CurrentFireMode);
if( Instigator != none && Instigator.IsFirstPerson() )
{
WeaponFireAnimName = GetGrenadeThrowAnim();
if ( WeaponFireAnimName != '' )
{
InterruptTime = MySkelMesh.GetAnimInterruptTime(WeaponFireAnimName);
PlayAnimation(WeaponFireAnimName, MySkelMesh.GetAnimLength(WeaponFireAnimName),,FireTweenTime);
bDetonateLocked = true;
SetTimer(InterruptTime, false, nameof(ClearDetonateLock));
}
}
}
simulated function ClearDetonateLock()
{
bDetonateLocked = false;
ClearTimer(nameof(ClearDetonateLock));
}
/***/
///////////////////////////////////////////////////////////////////////////////////////////
//
// Trader
//
///////////////////////////////////////////////////////////////////////////////////////////
/** Allows weapon to calculate its own stats for display in trader */
static simulated event SetTraderWeaponStats( out array<STraderItemWeaponStats> WeaponStats )
{
super.SetTraderWeaponStats(WeaponStats);
WeaponStats.Length = 4;
WeaponStats[0].StatType = TWS_Damage;
WeaponStats[0].StatValue = class'KFWeap_HRG_WarthogWeapon'.static.CalculateTraderWeaponStatDamage();
WeaponStats[1].StatType = TWS_Penetration;
WeaponStats[1].StatValue = class'KFWeap_HRG_WarthogWeapon'.default.PenetrationPower[DEFAULT_FIREMODE];
WeaponStats[2].StatType = TWS_Range;
// This is now set in native since EffectiveRange has been moved to KFWeaponDefinition
// WeaponStats[2].StatValue = CalculateTraderWeaponStatRange();
WeaponStats[3].StatType = TWS_RateOfFire;
WeaponStats[3].StatValue = class'KFWeap_HRG_WarthogWeapon'.static.CalculateTraderWeaponStatFireRate();
}
defaultproperties
{
// start in detonate mode so that an attempt to detonate before any charges are thrown results in
// the proper third-person anim
CurrentFireMode=DETONATE_FIREMODE
// Zooming/Position
PlayerViewOffset=(X=6.0,Y=2,Z=-4)
FireOffset=(X=0,Y=0)
TurretSpawnOffset=(X=0, Y=15, Z=-50)
// Content
PackageKey="HRG_Warthog"
FirstPersonMeshName="Wep_1P_HRG_Warthog_MESH.Wep_1P_Warthog_Rig"
FirstPersonAnimSetNames(0)="Wep_1P_HRG_Warthog_ANIM.Wep_1P_HRG_Warthog_ANIM"
PickupMeshName="wep_3p_hrg_warthog_mesh.Wep_HRG_Warthog_Pickup"
AttachmentArchetypeName="WEP_HRG_Warthog_ARCH.Wep_HRG_Warthog_3P"
// Anim
FireAnim=C4_Throw
FireLastAnim=C4_Throw_Last
DetonateAnim=Detonate
DetonateLastAnim=Detonate_Last
// Ammo
MagazineCapacity[0]=1
SpareAmmoCapacity[0]=3
InitialSpareMags[0]=1
AmmoPickupScale[0]=1.0
// THROW_FIREMODE
FireInterval(THROW_FIREMODE)=0.25
FireModeIconPaths(THROW_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Drone'
// DETONATE_FIREMODE
FiringStatesArray(DETONATE_FIREMODE)=WeaponDetonating
WeaponFireTypes(DETONATE_FIREMODE)=EWFT_Custom
AmmoCost(DETONATE_FIREMODE)=0
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Warthog'
InstantHitDamage(BASH_FIREMODE)=26
// Inventory / Grouping
InventoryGroup=IG_Equipment
GroupPriority=11
WeaponSelectTexture=Texture2D'WEP_UI_HRG_Warthog_TEX.UI_WeaponSelect_HRG_Warthog'
InventorySize=3
DetonateAkEvent=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Detonate_Trigger'
// Weapon Upgrade stat boosts
//WeaponUpgrades[1]=(IncrementDamage=1.05f,IncrementWeight=1)
//WeaponUpgrades[2]=(IncrementDamage=1.1f,IncrementWeight=2)
//WeaponUpgrades[3]=(IncrementDamage=1.15f,IncrementWeight=3)
AssociatedPerkClasses(0)=class'KFPerk_Demolitionist'
MaxTurretsDeployed=1
NumDeployedTurrets=0
ThrowStrength=1350.0f
bTurretReadyToUse=true
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1)))
WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Damage1, Scale=1.3f), (Stat=EWUS_Weight, Add=2)))
NumBloodMapMaterials=3
bDetonateLocked=false
CurrentAmmoPercentage=-1.0f
}

View File

@ -0,0 +1,492 @@
//=============================================================================
// KFWeap_HRG_WarthogWeapon
//=============================================================================
// An HRG Warthog Grenade Launcher
//=============================================================================
// Killing Floor 2
// Copyright (C) 2022 Tripwire Interactive LLC
//=============================================================================
class KFWeap_HRG_WarthogWeapon extends KFWeap_GrenadeLauncher_Base;
var KFPawn_HRG_Warthog InstigatorDrone;
// Used to calculate parabola when spawn projectile
var KFPawn_Monster CurrentTarget;
// Used to force distance when throwing projectiles when the pawn dies
var float CurrentDistanceProjectile;
var float DistanceParabolicLaunch;
var transient float LastTimeFireSeconds;
simulated event PreBeginPlay()
{
super.PreBeginPlay();
StartLoadWeaponContent();
}
simulated state WeaponFiring
{
simulated function EndState(Name NextStateName)
{
local Pawn OriginalInstigator;
// The Instigator for this weapon is the Player owning the weapon (for Perk, damage, etc,. calculations)
// But for Weapon Firing end state logic we need to point to the real Drone Pawn so we don't mess
// With whichever weapon the Player had equipped at that point
OriginalInstigator = Instigator;
Instigator = InstigatorDrone;
super.EndState(NextStateName);
Instigator = OriginalInstigator;
}
}
simulated function FireAmmunition()
{
CurrentFireMode = DEFAULT_FIREMODE;
super.FireAmmunition();
}
simulated function GetMuzzleLocAndRot(out vector MuzzleLoc, out rotator MuzzleRot)
{
if (KFSkeletalMeshComponent(Mesh).GetSocketWorldLocationAndRotation('MuzzleFlash', MuzzleLoc, MuzzleRot) == false)
{
`Log("MuzzleFlash not found!");
}
}
simulated function Projectile ProjectileFire()
{
local vector RealStartLoc, AimDir, TargetLocation;
local rotator AimRot;
local class<KFProjectile> MyProjectileClass;
// tell remote clients that we fired, to trigger effects
if ( ShouldIncrementFlashCountOnFire() )
{
IncrementFlashCount();
}
MyProjectileClass = GetKFProjectileClass();
if( Role == ROLE_Authority || (MyProjectileClass.default.bUseClientSideHitDetection
&& MyProjectileClass.default.bNoReplicationToInstigator && Instigator != none
&& Instigator.IsLocallyControlled()) )
{
GetMuzzleLocAndRot(RealStartLoc, AimRot);
if (CurrentTarget != none)
{
TargetLocation = CurrentTarget.Location;
TargetLocation.Z += CurrentTarget.GetCollisionHeight() * 0.5f; // Add an offset on the location, so it matches correctly
AimDir = Normal(TargetLocation - RealStartLoc);
}
else
{
AimDir = Vector(Owner.Rotation);
}
return SpawnAllProjectiles(MyProjectileClass, RealStartLoc, AimDir);
}
return None;
}
simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir )
{
local KFProjectile SpawnedProjectile;
local int ProjDamage;
local Pawn OriginalInstigator;
local vector TargetLocation, Distance;
local float HorizontalDistance, TermA, TermB, TermC, InitialSpeed;
/*
* Instigator issues here. The instigator of the weapon here is the PlayerController which needs to replicate the projectile.
* We spawn it with that instigator, then we change to be able to be able to apply perk effects.
*/
// Spawn projectile
OriginalInstigator = Instigator;
Instigator = InstigatorDrone;
SpawnedProjectile = Spawn( KFProjClass, self,, RealStartLoc);
if( SpawnedProjectile != none && !SpawnedProjectile.bDeleteMe )
{
if (CurrentTarget != none)
{
//TargetLocation = CurrentTarget.Mesh.GetBoneLocation('Spine1');
TargetLocation = CurrentTarget.Location;
TargetLocation.Z += CurrentTarget.GetCollisionHeight() * 0.5f; // Add an offset on the location, so it matches correctly
}
else if (CurrentDistanceProjectile > 0.f)
{
TargetLocation = RealStartLoc + AimDir * CurrentDistanceProjectile;
TargetLocation.Z -= InstigatorDrone.DeployHeight; // We target more or less the ground
}
//CurrentTarget.DrawDebugSphere(TargetLocation, 100, 10, 0, 255, 0, true);
Distance = TargetLocation - RealStartLoc;
Distance.Z = 0.f;
HorizontalDistance = VSize(Distance); // 2D
// If bigger than minimal horizontal distance, and drone is higher than target..
if (HorizontalDistance > DistanceParabolicLaunch
&& RealStartLoc.Z > TargetLocation.Z)
{
// Parabolic launch calculation
// Tweak speed so it can fall on the TargetLocation, use parabolic launch, we assume an Angle = 0
// We transform from 3D to 2D, assume horizontal start is 0, and horizontal distance is the modulus of the vector distance to target
// Angle = 0 -> sin(0) -> 0 so no need to apply any initial Y velocity
// ( ( Y - Y0 ) / ( 0.5 * gravity ) )
TermA = (TargetLocation.Z - RealStartLoc.Z) / (-11.5f * 0.5f * 100.f); // gravity to cm/s
// ( X - X0 )^2 / V^2 -> assume XO is 0
TermB = HorizontalDistance * HorizontalDistance;
TermC = TermB / TermA;
InitialSpeed = Sqrt(TermC);
AimDir = Normal(Distance);
AimDir.Z = 0.f;
}
else
{
// No parabollic, so we force Speed
if (RealStartLoc.Z < TargetLocation.Z)
{
InitialSpeed = 3000.f;
}
else
{
InitialSpeed = 1000.f;
}
}
SpawnedProjectile.Speed = InitialSpeed;
SpawnedProjectile.MaxSpeed = 0;
SpawnedProjectile.TerminalVelocity = InitialSpeed * 2.f;
SpawnedProjectile.TossZ = 0.f;
// Mirror damage and damage type from weapon. This is set on the server only and
// these properties are replicated via TakeHitInfo
if ( InstantHitDamage.Length > CurrentFireMode && InstantHitDamageTypes.Length > CurrentFireMode )
{
ProjDamage = GetModifiedDamage(CurrentFireMode);
SpawnedProjectile.Damage = ProjDamage;
SpawnedProjectile.MyDamageType = InstantHitDamageTypes[CurrentFireMode];
}
SpawnedProjectile.UpgradeDamageMod = GetUpgradeDamageMod();
SpawnedProjectile.Init( AimDir );
}
Instigator = OriginalInstigator;
return SpawnedProjectile;
}
simulated function IncrementFlashCount()
{
local KFPawn P;
P = KFPawn(Owner);
if( P != None )
{
P.IncrementFlashCount( Self, CurrentFireMode );
}
}
simulated function Fire()
{
if (HasAmmo(DEFAULT_FIREMODE))
{
//if (WorldInfo.TimeSeconds - LastTimeFireSeconds > GetFireInterval(DEFAULT_FIREMODE))
//{
LastTimeFireSeconds = WorldInfo.TimeSeconds;
SendToFiringState(DEFAULT_FIREMODE);
//}
}
}
simulated function StopFire(byte FireModeNum)
{
super.StopFire(FireModeNum);
GoToState('Inactive');
}
simulated function bool ShouldRefire()
{
return false;
}
simulated function ForceExplosionReplicateKill(Vector HitLocation, KFProj_HighExplosive_HRG_Warthog Proj)
{
HandleClientProjectileExplosion(HitLocation, Proj);
Proj.Shutdown(); // cleanup/destroy projectile
}
simulated function ForceExplosionReplicate(Actor Other, Vector HitLocation, Vector HitNormal, KFProj_HighExplosive_HRG_Warthog Proj)
{
HandleClientProjectileExplosion(HitLocation, Proj);
if (Proj.ExplosionTemplate != None)
{
Proj.TriggerExplosion(HitLocation, HitNormal, Other);
}
Proj.Shutdown(); // cleanup/destroy projectile
}
/**
* Starts playing looping FireSnd only (used for switching sounds in Zedtime)
*/
simulated function StartLoopingFireSound(byte FireModeNum)
{
if ( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !ShouldForceSingleFireSound() )
{
bPlayingLoopingFireSnd = true;
KFPawn(Owner).SetWeaponAmbientSound(WeaponFireSnd[FireModeNum].DefaultCue, WeaponFireSnd[FireModeNum].FirstPersonCue);
}
}
/**
* Stops playing looping FireSnd only (used for switching sounds in Zedtime)
*/
simulated function StopLoopingFireSound(byte FireModeNum)
{
if ( bPlayingLoopingFireSnd )
{
KFPawn(Owner).SetWeaponAmbientSound(None);
if ( FireModeNum < WeaponFireLoopEndSnd.Length )
{
WeaponPlayFireSound(WeaponFireLoopEndSnd[FireModeNum].DefaultCue, WeaponFireLoopEndSnd[FireModeNum].FirstPersonCue);
}
bPlayingLoopingFireSnd = false;
}
}
simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation )
{
local name WeaponFireAnimName;
local KFPerk CurrentPerk;
local float TempTweenTime, AdjustedAnimLength;
local KFPawn KFPO;
// If we have stopped the looping fire sound to play single fire sounds for zed time
// start the looping sound back up again when the time is back above zed time speed
if( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !bPlayingLoopingFireSnd )
{
StartLoopingFireSound(FireModeNum);
}
PlayFiringSound(CurrentFireMode);
KFPO = KFPawn(Owner);
if( KFPO != none )
{
// Tell our pawn about any changes in animation speed
UpdateWeaponAttachmentAnimRate( GetThirdPersonAnimRate() );
if( KFPO.IsLocallyControlled() )
{
if( KFPO.IsFirstPerson() )
{
if ( !bPlayingLoopingFireAnim )
{
WeaponFireAnimName = GetWeaponFireAnim(FireModeNum);
if ( WeaponFireAnimName != '' )
{
AdjustedAnimLength = MySkelMesh.GetAnimLength(WeaponFireAnimName);
TempTweenTime = FireTweenTime;
CurrentPerk = GetPerk();
if( CurrentPerk != none )
{
CurrentPerk.ModifyRateOfFire( AdjustedAnimLength, self );
// We need to unlock the slide if we fire from zero ammo while uber ammo is active
if( EmptyMagBlendNode != none
&& BonesToLockOnEmpty.Length > 0
&& AmmoCount[GetAmmoType(FireModeNum)] == 0
&& CurrentPerk.GetIsUberAmmoActive(self) )
{
EmptyMagBlendNode.SetBlendTarget( 0, 0 );
TempTweenTime = 0.f;
}
}
PlayAnimation(WeaponFireAnimName, AdjustedAnimLength,, TempTweenTime);
}
}
// Start muzzle flash effect
CauseMuzzleFlash(FireModeNum);
}
HandleRecoil();
ShakeView();
if (AmmoCount[0] == 0 && ForceReloadTimeOnEmpty > 0)
{
SetTimer(ForceReloadTimeOnEmpty, false, nameof(ForceReload));
}
}
}
}
simulated function WeaponPlayFireSound(AkBaseSoundObject DefaultSound, AkBaseSoundObject FirstPersonSound)
{
// ReplicateSound needs an "out" vector
local vector SoundLocation;
if( Owner != None && !bSuppressSounds )
{
SoundLocation = KFPawn(Owner).GetPawnViewLocation();
if ( DefaultSound != None )
{
Owner.PlaySoundBase( DefaultSound, false, false, false, SoundLocation );
}
}
}
/** True if we want to override the looping fire sounds with fire sounds from another firemode */
simulated function bool ShouldForceSingleFireSound()
{
// If this weapon has a single-shot firemode, disable looping fire sounds during zedtime
if ( `IsInZedTime(Instigator) && SingleFireSoundIndex != 255 )
{
return true;
}
return false;
}
simulated function bool HasAlwaysOnZedTimeResist()
{
return true;
}
defaultproperties
{
// Inventory / Grouping
InventorySize=5
GroupPriority=25
WeaponSelectTexture=Texture2D'WEP_UI_AutoTurret_TEX.UI_WeaponSelect_AutoTurret'
// FOV
MeshIronSightFOV=52
PlayerIronSightFOV=70
// Depth of field
DOF_FG_FocalRadius=75
DOF_FG_MaxNearBlurSize=3.5
// Zooming/Position
PlayerViewOffset=(X=9.0,Y=10,Z=-4)
// Content
PackageKey="HRG_WarthogWeapon"
FirstPersonMeshName="Wep_1P_HRG_WarthogWeapon_MESH.Wep_1stP_HRG_WarthogWeapon_Rig"
FirstPersonAnimSetNames(0)="Wep_1P_HRG_WarthogWeapon_ANIM.Wep_1stP_HRG_WarthogWeapon_Anim"
PickupMeshName="WEP_3P_HRG_WarthogWeapon_MESH.Wep_HRG_Warthog_Pickup"
AttachmentArchetypeName="WEP_HRG_WarthogWeapon_ARCH.HRG_WarthogWeaponAttachment"
MuzzleFlashTemplateName="WEP_HRG_WarthogWeapon_ARCH.HRG_WarthogWeapon_MuzzleFlash"
// Zooming/Position
IronSightPosition=(X=0,Y=0,Z=0)
// Ammo
MagazineCapacity[0]=35
SpareAmmoCapacity[0]=0
InitialSpareMags[0]=0
bCanBeReloaded=false
bReloadFromMagazine=false
// Recoil
maxRecoilPitch=225
minRecoilPitch=150
maxRecoilYaw=150
minRecoilYaw=-150
RecoilRate=0.085
RecoilMaxYawLimit=500
RecoilMinYawLimit=65035
RecoilMaxPitchLimit=900
RecoilMinPitchLimit=65035
RecoilISMaxYawLimit=75
RecoilISMinYawLimit=65460
RecoilISMaxPitchLimit=195
RecoilISMinPitchLimit=65460
RecoilViewRotationScale=0.25
IronSightMeshFOVCompensationScale=1.5
// DEFAULT_FIREMODE
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Grenade'
FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_HighExplosive_HRG_Warthog'
FireInterval(DEFAULT_FIREMODE)=+0.7
InstantHitDamage(DEFAULT_FIREMODE)=10.0
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_Warthog'
Spread(DEFAULT_FIREMODE)=0.0
FireOffset=(X=30,Y=4.5,Z=-4)
// ALT_FIREMODE
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None
// BASH_FIREMODE
WeaponFireTypes(BASH_FIREMODE)=EWFT_None
// Fire Effects
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_HRG_Warthog.Play_WEP_HRG_Warthog_Fire_1P')
//@todo: add akevent when we have it
WeaponDryFireSnd(DEFAULT_FIREMODE)=none
// Attachments
bHasIronSights=false
bHasFlashlight=false
AssociatedPerkClasses(0)=class'KFPerk_Demolitionist'
WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot'
// Weapon Upgrade stat boosts
//WeaponUpgrades[1]=(IncrementDamage=1.12f,IncrementWeight=1)
//WeaponUpgrades[2]=(IncrementDamage=1.3f,IncrementWeight=2)
//WeaponUpgrades[3]=(IncrementDamage=1.55f,IncrementWeight=3)
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.12f), (Stat=EWUS_Weight, Add=1)))
WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Weight, Add=2)))
WeaponUpgrades[3]=(Stats=((Stat=EWUS_Damage0, Scale=1.55f), (Stat=EWUS_Weight, Add=3)))
InstigatorDrone=none
CurrentTarget=none
CurrentDistanceProjectile=-1.f
DistanceParabolicLaunch=150.f //cm
LastTimeFireSeconds=0.f
}

View File

@ -905,7 +905,7 @@ defaultproperties
FiringStatesArray(DEFAULT_FIREMODE)=MineReconstructorCharge
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Mine_Reconstructor'
FireInterval(DEFAULT_FIREMODE)=+0.223 //+0.33
FireInterval(DEFAULT_FIREMODE)=+0.2 //+0.33
InstantHitDamage(DEFAULT_FIREMODE)=120
PenetrationPower(DEFAULT_FIREMODE)=0.0;
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Toxic_MineReconstructorImpact'
@ -969,4 +969,6 @@ defaultproperties
End Object
//iCounterA = 0
bBlocked = false;
bForceCrosshair=true
}

View File

@ -217,8 +217,8 @@ defaultproperties
IronSightPosition=(X=3.0,Y=15,Z=15)
// Ammo
MagazineCapacity[0]=90 //125 //175 //250
SpareAmmoCapacity[0]=540 //500 //750
MagazineCapacity[0]=120 //125 //175 //250
SpareAmmoCapacity[0]=600 //500 //750
InitialSpareMags[0]=1
AmmoPickupScale[0]=1 //0.67
bCanBeReloaded=true

View File

@ -53,6 +53,11 @@ replication
}
static simulated event EFilterTypeUI GetTraderFilter()
{
return FT_Explosive;
}
simulated function AltFireMode()
{
if ( !Instigator.IsLocallyControlled() )

View File

@ -10,6 +10,40 @@
class KFWeap_Shotgun_HRG_Kaboomstick extends KFWeap_Shotgun_DoubleBarrel;
var transient bool AlreadyIssuedCanNuke;
simulated function KFProjectile SpawnAllProjectiles(class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir)
{
local KFProjectile Proj;
AlreadyIssuedCanNuke = false;
Proj = Super.SpawnAllProjectiles(KFProjClass, RealStartLoc, AimDir);
AlreadyIssuedCanNuke = false;
return Proj;
}
simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir )
{
local KFProj_Explosive_HRG_Kaboomstick Proj;
Proj = KFProj_Explosive_HRG_Kaboomstick(Super.SpawnProjectile(KFProjClass, RealStartLoc, AimDir));
if (AlreadyIssuedCanNuke == false)
{
Proj.bCanNuke = true;
AlreadyIssuedCanNuke = true;
}
else
{
Proj.bCanNuke = false;
}
return Proj;
}
defaultproperties
{
// Inventory
@ -126,4 +160,6 @@ defaultproperties
WeaponUpgrades[0]=(Stats=((Stat=EWUS_Damage0, Scale=1.f, Add=0), (Stat=EWUS_Weight, Scale=1.f, Add=0)))
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1)))
WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.30f), (Stat=EWUS_Damage1, Scale=1.30f), (Stat=EWUS_Weight, Add=2)))
AlreadyIssuedCanNuke = false
}

View File

@ -0,0 +1,717 @@
//=============================================================================
// KFWeap_Shotgun_S12
//=============================================================================
// AA12 Auto Shotgun Weapon Class
//=============================================================================
// Killing Floor 2
// Copyright (C) 2023 Tripwire Interactive LLC
//=============================================================================
class KFWeap_Shotgun_S12 extends KFWeap_ShotgunBase;
var (Positioning) vector SecondaryFireOffset;
const SecondaryFireAnim = 'Shoot_Secondary';
const SecondaryFireIronAnim = 'Shoot_Secondary_Iron';
const SecondaryReloadAnim = 'Reload_Secondary';
const SecondaryReloadEliteAnim = 'Reload_Secondary_Elite';
var() KFMuzzleFlash SecondaryMuzzleFlashTemplate;
// Used on the server to keep track of grenades
var int ServerTotalAltAmmo;
var transient bool bCanceledAltAutoReload;
var GameExplosion ExplosionTemplate;
var transient ParticleSystemComponent ExplosionPSC;
var ParticleSystem ExplosionEffect;
var float ExplosionOriginalDamage;
simulated event PostBeginPlay()
{
super.PostBeginPlay();
ExplosionOriginalDamage = ExplosionTemplate.Damage;
}
/** Instead of switch fire mode use as immediate alt fire */
simulated function AltFireMode()
{
if ( !Instigator.IsLocallyControlled() )
{
return;
}
if (bCanceledAltAutoReload)
{
bCanceledAltAutoReload = false;
TryToAltReload();
return;
}
StartFire(ALTFIRE_FIREMODE);
}
simulated function BeginFire( Byte FireModeNum )
{
local bool bStoredAutoReload;
// We are trying to reload the weapon but the primary ammo in already at full capacity
if ( FireModeNum == RELOAD_FIREMODE && !CanReload() )
{
// Store the cuurent state of bCanceledAltAutoReload in case its not possible to do the reload
bStoredAutoReload = bCanceledAltAutoReload;
bCanceledAltAutoReload = false;
if(CanAltAutoReload())
{
TryToAltReload();
return;
}
bCanceledAltAutoReload = bStoredAutoReload;
}
super.BeginFire( FireModeNum );
}
/**
* Initializes ammo counts, when weapon is spawned.
*/
function InitializeAmmo()
{
Super.InitializeAmmo();
// Add Secondary ammo to our secondary spare ammo count both of these are important, in order to allow dropping the weapon to function properly.
SpareAmmoCount[1] = Min(SpareAmmoCount[1] + InitialSpareMags[1] * default.MagazineCapacity[1], GetMaxAmmoAmount(1) - AmmoCount[1]);
ServerTotalAltAmmo += SpareAmmoCount[1];
// Make sure the server doesn't get extra shots on listen servers.
if(Role == ROLE_Authority && !Instigator.IsLocallyControlled())
{
ServerTotalAltAmmo += AmmoCount[1];
}
}
/**
* @see Weapon::ConsumeAmmo
*/
simulated function ConsumeAmmo( byte FireModeNum )
{
local byte AmmoType;
local bool bNoInfiniteAmmo;
local int OldAmmoCount;
if(UsesSecondaryAmmo() && FireModeNum == ALTFIRE_FIREMODE && Role == ROLE_Authority && !Instigator.IsLocallyControlled())
{
AmmoType = GetAmmoType(FireModeNum);
OldAmmoCount = AmmoCount[AmmoType];
Super.ConsumeAmmo(FireModeNum);
bNoInfiniteAmmo = (OldAmmoCount - AmmoCount[AmmoType]) > 0 || AmmoCount[AmmoType] == 0;
if ( bNoInfiniteAmmo )
{
ServerTotalAltAmmo--;
}
}
else
{
Super.ConsumeAmmo(FireModeNum);
}
}
simulated function bool HasAnyAmmo()
{
return HasSpareAmmo() || HasAmmo(DEFAULT_FIREMODE) || SpareAmmoCount[1] > 0 || HasAmmo(ALTFIRE_FIREMODE);
}
/** Make sure user can't fire infinitely if they cheat to get infinite ammo locally. */
simulated event bool HasAmmo( byte FireModeNum, optional int Amount=1 )
{
local byte AmmoType;
AmmoType = GetAmmoType(FireModeNum);
if(AmmoType == 1 && Role == ROLE_Authority && Instigator != none && UsesSecondaryAmmo() && !Instigator.IsLocallyControlled())
{
if(ServerTotalAltAmmo <= 0)
{
return false;
}
}
return Super.HasAmmo(FireModeNum, Amount );
}
/**
* Overridden so any grenades added will go to the spare ammo and no the clip.
*/
function int AddSecondaryAmmo(int Amount)
{
local int OldAmmo;
// If we can't accept spare ammo, then abort
if( !CanRefillSecondaryAmmo() )
{
return 0;
}
if(Role == ROLE_Authority && !Instigator.IsLocallyControlled())
{
OldAmmo = ServerTotalAltAmmo;
ServerTotalAltAmmo = Min(ServerTotalAltAmmo + Amount, GetMaxAmmoAmount(1));
ClientGiveSecondaryAmmo(Amount);
return ServerTotalAltAmmo - OldAmmo;
}
else
{
OldAmmo = SpareAmmoCount[1];
ClientGiveSecondaryAmmo(Amount);
return SpareAmmoCount[1] - OldAmmo;
}
}
/** Give client specified amount of ammo (used player picks up ammo on the server) */
reliable client function ClientGiveSecondaryAmmo(byte Amount)
{
SpareAmmoCount[1] = Min(SpareAmmoCount[1] + Amount, GetMaxAmmoAmount(1) - AmmoCount[1]);
TryToAltReload();
}
function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon )
{
local KFWeap_Shotgun_S12 Weap;
Super.SetOriginalValuesFromPickup(PickedUpWeapon);
if(Role == ROLE_Authority && !Instigator.IsLocallyControlled())
{
Weap = KFWeap_Shotgun_S12(PickedUpWeapon);
ServerTotalAltAmmo = Weap.ServerTotalAltAmmo;
SpareAmmoCount[1] = ServerTotalAltAmmo - AmmoCount[1];
}
else
{
// If we're locally controlled, don't bother using ServerTotalAltAmmo.
SpareAmmoCount[1] = PickedUpWeapon.SpareAmmoCount[1];
}
}
/*********************************************************************************************
* State GrenadeFiring
* Handles firing grenade launcher.
*********************************************************************************************/
simulated state FiringSecondaryState extends WeaponSingleFiring
{
// Overriden to not call FireAmmunition right at the start of the state
simulated event BeginState( Name PreviousStateName )
{
Super.BeginState(PreviousStateName);
NotifyBeginState();
}
simulated function EndState(Name NextStateName)
{
Super.EndState(NextStateName);
NotifyEndState();
}
/**
* This function returns the world location for spawning the visual effects
* Overridden to use a special offset for throwing grenades
*/
simulated event vector GetMuzzleLoc()
{
local vector MuzzleLocation;
// swap fireoffset temporarily
FireOffset = SecondaryFireOffset;
MuzzleLocation = Global.GetMuzzleLoc();
FireOffset = default.FireOffset;
return MuzzleLocation;
}
/** Get whether we should play the reload anim as well or not */
simulated function name GetWeaponFireAnim(byte FireModeNum)
{
return bUsingSights ? SecondaryFireIronAnim : SecondaryFireAnim;
}
}
/**
* Don't allow secondary fire to make a primary fire shell particle come out of the gun.
*/
simulated function CauseMuzzleFlash(byte FireModeNum)
{
local bool AutoShellEject;
if(FireModeNum == ALTFIRE_FIREMODE)
{
if (MuzzleFlash == None)
{
AttachSecondaryMuzzleFlash();
}
AutoShellEject = MuzzleFlash.bAutoActivateShellEject;
MuzzleFlash.bAutoActivateShellEject = false;
Super.CauseMuzzleFlash(FireModeNum);
MuzzleFlash.bAutoActivateShellEject = AutoShellEject;
}
else
{
Super.CauseMuzzleFlash(FireModeNum);
}
}
simulated function AttachSecondaryMuzzleFlash()
{
if ( MySkelMesh != none )
{
if (MuzzleFlashTemplate != None)
{
MuzzleFlash = new(self) Class'KFMuzzleFlash'(SecondaryMuzzleFlashTemplate);
MuzzleFlash.AttachMuzzleFlash(MySkelMesh, 'MuzzleFlashAlt');
}
}
}
/*********************************************************************************************
* State Reloading
* This is the default Reloading State. It's performed on both the client and the server.
*********************************************************************************************/
/** Do not allow alternate fire to tell the weapon to reload. Alt reload occurs in a separate codepath */
simulated function bool ShouldAutoReload(byte FireModeNum)
{
if(FireModeNum == ALTFIRE_FIREMODE)
{
return false;
}
return Super.ShouldAutoReload(FireModeNum);
}
/** Called on local player when reload starts and replicated to server */
simulated function SendToAltReload()
{
ReloadAmountLeft = MagazineCapacity[1];
GotoState('AltReloading');
if ( Role < ROLE_Authority )
{
ServerSendToAltReload();
}
}
/** Called from client when reload starts */
reliable server function ServerSendToAltReload()
{
ReloadAmountLeft = MagazineCapacity[1];
GotoState('AltReloading');
}
/**
* State Reloading
* State the weapon is in when it is being reloaded (current magazine replaced with a new one, related animations and effects played).
*/
simulated state AltReloading extends Reloading
{
ignores ForceReload, ShouldAutoReload, AllowSprinting;
simulated function byte GetWeaponStateId()
{
local KFPerk Perk;
local bool bTacticalReload;
Perk = GetPerk();
bTacticalReload = (Perk != None && Perk.GetUsingTactialReload(self));
return (bTacticalReload ? WEP_ReloadSecondary_Elite : WEP_ReloadSecondary);
}
simulated event BeginState(Name PreviousStateName)
{
super.BeginState(PreviousStateName);
bCanceledAltAutoReload = true;
}
// Overridding super so we don't call functions we don't want to call.
simulated function EndState(Name NextStateName)
{
ClearZedTimeResist();
ClearTimer(nameof(ReloadStatusTimer));
ClearTimer(nameof(ReloadAmmoTimer));
CheckBoltLockPostReload();
NotifyEndState();
`DialogManager.PlayAmmoDialog( KFPawn(Instigator), float(SpareAmmoCount[1]) / float(GetMaxAmmoAmount(1)) );
}
// Overridding super so when this reload is called directly after normal reload state there
// are not complications resulting from back to back reloads.
simulated event ReplicatedEvent(name VarName)
{
Global.ReplicatedEvent(Varname);
}
/** Make sure we can inturrupt secondary reload with anything. */
simulated function bool CanOverrideMagReload(byte FireModeNum)
{
return true;
}
/** Returns animation to play based on reload type and status */
simulated function name GetReloadAnimName( bool bTacticalReload )
{
return (bTacticalReload) ? SecondaryReloadEliteAnim : SecondaryReloadAnim;
}
simulated function PerformReload(optional byte FireModeNum)
{
Global.PerformReload(ALTFIRE_FIREMODE);
if(Instigator.IsLocallyControlled() && Role < ROLE_Authority)
{
ServerSetAltAmmoCount(AmmoCount[1]);
}
bCanceledAltAutoReload = false;
}
simulated function EReloadStatus GetNextReloadStatus(optional byte FireModeNum)
{
return Global.GetNextReloadStatus(ALTFIRE_FIREMODE);
}
}
reliable server function ServerSetAltAmmoCount(byte Amount)
{
AmmoCount[1] = min(Amount, MagazineCapacity[1]);
}
/** Allow reloads for primary weapon to be interupted by firing secondary weapon. */
simulated function bool CanOverrideMagReload(byte FireModeNum)
{
if(FireModeNum == ALTFIRE_FIREMODE)
{
return true;
}
return Super.CanOverrideMagReload(FireModeNum);
}
/*********************************************************************************************
* State Active
* Try to get weapon to automatically reload secondary fire types when it can.
*********************************************************************************************/
simulated state Active
{
/** Initialize the weapon as being active and ready to go. */
simulated event BeginState(Name PreviousStateName)
{
// do this last so the above code happens before any state changes
Super.BeginState(PreviousStateName);
// If nothing happened, try to reload
TryToAltReload();
}
}
/** Network: Local Player */
simulated function bool CanAltAutoReload()
{
if ( !Instigator.IsLocallyControlled() )
{
return false;
}
if(!UsesSecondaryAmmo())
{
return false;
}
// If the weapon wants to fire its primary weapon, and it can fire, do not allow weapon to automatically alt reload
if(PendingFire(DEFAULT_FIREMODE) && HasAmmo(DEFAULT_FIREMODE))
{
return false;
}
if(!CanReload(ALTFIRE_FIREMODE))
{
return false;
}
if (bCanceledAltAutoReload)
{
return false;
}
return true;
}
simulated function TryToAltReload()
{
if ((IsInState('Active') || IsInState('WeaponSprinting')) && CanAltAutoReload())
{
SendToAltReload();
}
}
simulated function TriggerAltExplosion()
{
local vector MuzzleLocation, HitLocation, HitNormal;
local KFExplosionActorReplicated ExploActor;
// TriggerExplosion
if (Role == ROLE_Authority)
{
MuzzleLocation = GetMuzzleLoc();
Trace( HitLocation, HitNormal, MuzzleLocation + vect(0, 0, -1) * 250000, MuzzleLocation);
// Move a bit from hit location
HitLocation = HitLocation + (vect(0,0,1) * 128.f);
// Explode using the given template
ExploActor = Spawn(class'KFExplosionActorReplicated', self,, HitLocation, rotator(vect(0,0,1)),, true);
if (ExploActor != None)
{
ExploActor.InstigatorController = Instigator.Controller;
ExploActor.Instigator = Instigator;
ExploActor.bIgnoreInstigator = true;
ExplosionTemplate.Damage = ExplosionOriginalDamage * GetUpgradeDamageMod(ALTFIRE_FIREMODE);
ExploActor.Explode(ExplosionTemplate);
}
}
if (WorldInfo.NetMode != NM_DedicatedServer)
{
// Trigger VFX ?
if (HitLocation == vect(0,0,0))
{
MySkelMesh.GetSocketWorldLocationAndRotation('MuzzleFlashAlt', MuzzleLocation);
Trace( HitLocation, HitNormal, MuzzleLocation + vect(0, 0, -1) * 250000, MuzzleLocation);
// Move a bit from hit location
HitLocation = HitLocation + (vect(0,0,1) * 128.f);
}
if (ExplosionEffect != None)
{
ExplosionPSC = WorldInfo.MyEmitterPool.SpawnEmitter(ExplosionEffect, HitLocation, rotator(vect(0,0,1)));
ExplosionPSC.ActivateSystem();
}
}
}
simulated function CustomFire()
{
// Alt-fire blast only (server authoritative)
if ( CurrentFireMode != ALTFIRE_FIREMODE )
{
Super.CustomFire();
return;
}
TriggerAltExplosion();
IncrementFlashCount();
}
simulated event Tick(float DeltaTime)
{
local vector SocketLocation;
Super.Tick(DeltaTime);
// Client only
if (WorldInfo.NetMode != NM_DedicatedServer)
{
KFSkeletalMeshComponent(Mesh).GetSocketWorldLocationAndRotation('MuzzleFlashAlt', SocketLocation);
if (ExplosionPSC != None && ExplosionPSC.bIsActive)
{
ExplosionPSC.SetVectorParameter('WeaponEndpoint', SocketLocation);
}
}
}
defaultproperties
{
bCanRefillSecondaryAmmo = true;
// Shooting Animations
FireSightedAnims[0]=Shoot_Iron
FireSightedAnims[1]=Shoot_Iron2
FireSightedAnims[2]=Shoot_Iron3
// FOV
MeshFOV=86
MeshIronSightFOV=35
PlayerIronSightFOV=70
// Depth of field
DOF_FG_FocalRadius=85
DOF_FG_MaxNearBlurSize=3.5
// Content
PackageKey="Saiga12"
FirstPersonMeshName="Wep_1P_Saiga12_MESH.Wep_1stP_Saiga12_Rig"
FirstPersonAnimSetNames(0)="Wep_1P_Saiga12_ANIM.Wep_1stP_Saiga12_Anim_New"
PickupMeshName="WEP_3P_Saiga12_MESH.Wep_Saiga12_Pickup"
AttachmentArchetypeName="WEP_Saiga12_ARCH.Wep_Saiga12_3P"
MuzzleFlashTemplateName="WEP_Saiga12_ARCH.Wep_Saiga12_MuzzleFlash"
SecondaryMuzzleFlashTemplate=KFMuzzleFlash'WEP_Saiga12_ARCH.Wep_Saiga12_MuzzleFlashAlt'
// Zooming/Position
PlayerViewOffset=(X=0,Y=12.5,Z=-18)//
IronSightPosition=(X=12,Y=0,Z=-10.6)
// Pickup
AmmoPickupScale[0]=2.0
AmmoPickupScale[1]=1.0
// Ammo
MagazineCapacity[0]=10
SpareAmmoCapacity[0]=130
InitialSpareMags[0]=4
//grenades
MagazineCapacity[1]=1
SpareAmmoCapacity[1]=5
InitialSpareMags[1]=2
bCanBeReloaded=true
bReloadFromMagazine=true
// Recoil
maxRecoilPitch=250
minRecoilPitch=225
maxRecoilYaw=125
minRecoilYaw=-125
RecoilRate=0.075
RecoilBlendOutRatio=0.25
RecoilMaxYawLimit=500
RecoilMinYawLimit=65035
RecoilMaxPitchLimit=900
RecoilMinPitchLimit=64785
RecoilISMaxYawLimit=75
RecoilISMinYawLimit=65460
RecoilISMaxPitchLimit=375
RecoilISMinPitchLimit=65460
RecoilViewRotationScale=0.7
FallingRecoilModifier=1.5
HippedRecoilModifier=1.25
SecondaryAmmoTexture=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity'
bUseGrenadeAsSecondaryAmmo=true
// Inventory / Grouping
InventorySize=8
GroupPriority=100
WeaponSelectTexture=Texture2D'WEP_UI_Saiga12_TEX.UI_WeaponSelect_Saiga12'
AssociatedPerkClasses(0)=class'KFPerk_Support'
// DEFAULT_FIREMODE
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunSingle'
FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_Pellet'
InstantHitDamage(DEFAULT_FIREMODE)=24.0 //25 //20
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_Shotgun_S12'
PenetrationPower(DEFAULT_FIREMODE)=2.0
FireInterval(DEFAULT_FIREMODE)=0.2 // 300 RPM
Spread(DEFAULT_FIREMODE)=0.08
FireOffset=(X=30,Y=5,Z=-4)
NumPellets(DEFAULT_FIREMODE)=7
// ALT_FIREMODE
FiringStatesArray(ALTFIRE_FIREMODE)=FiringSecondaryState
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Custom
AmmoCost(ALTFIRE_FIREMODE)=1
FireInterval(ALTFIRE_FIREMODE)=+0.25 // 300 RPM
SecondaryFireOffset=(X=20.f,Y=4.5,Z=-7.f)
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_Shotgun_S12'
InstantHitDamage(BASH_FIREMODE)=30
// Fire Effects
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Fire_1P')
WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Alt_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Alt_Fire_1P')
WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Handling_DryFire'
WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Handling_DryFire'
WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil'
// Attachments
bHasIronSights=true
bHasFlashlight=false
// Weapon Upgrade stat boosts
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1)))
// Grenade explosion light
Begin Object Class=PointLightComponent Name=ExplosionPointLight
LightColor=(R=252,G=218,B=171,A=255)
Brightness=0.5f
Radius=400.f
FalloffExponent=10.f
CastShadows=False
CastStaticShadows=FALSE
CastDynamicShadows=False
bCastPerObjectShadows=false
bEnabled=FALSE
LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
End Object
// explosion
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=200
DamageRadius=800
DamageFalloffExponent=0.f
DamageDelay=0.f
MomentumTransferScale=10000
bAlwaysFullDamage=true
bDoCylinderCheck=true
// Damage Effects
MyDamageType=class'KFDT_Explosive_Shotgun_S12'
KnockDownStrength=150
FractureMeshRadius=200.0
FracturePartVel=500.0
ExplosionSound=AkEvent'WW_WEP_Saiga12.Play_WEP_Saiga12_Alt_Fire_3P'
ExplosionEffects=KFImpactEffectInfo'WEP_Saiga12_ARCH.WEP_Saiga12_Impacts'
// Dynamic Light
ExploLight=ExplosionPointLight
ExploLightStartFadeOutTime=0.0
ExploLightFadeOutTime=0.3
bIgnoreInstigator=true
ActorClassToIgnoreForDamage = class'KFPawn_Human'
// Camera Shake
CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble'
CamShakeInnerRadius=0
CamShakeOuterRadius=300
CamShakeFalloff=1.5f
bOrientCameraShakeTowardsEpicenter=true
End Object
ExplosionTemplate=ExploTemplate0
ExplosionEffect=ParticleSystem'WEP_1P_Saiga12_EMIT.FX_Saiga12_Explosion'
}