483 lines
14 KiB
Ucode
483 lines
14 KiB
Ucode
//=============================================================================
|
|
// KFWeap_AutoTurretWeapon
|
|
//=============================================================================
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2022 Tripwire Interactive LLC
|
|
//=============================================================================
|
|
|
|
class KFWeap_AutoTurretWeapon extends KFWeap_SMGBase;
|
|
|
|
var KFPawn_AutoTurret InstigatorDrone;
|
|
|
|
var transient bool bFiring;
|
|
|
|
simulated event PreBeginPlay()
|
|
{
|
|
super.PreBeginPlay();
|
|
StartLoadWeaponContent();
|
|
}
|
|
|
|
simulated state WeaponFiring
|
|
{
|
|
simulated function EndState(Name NextStateName)
|
|
{
|
|
local Pawn OriginalInstigator;
|
|
|
|
// The Instigator for this weapon is the Player owning the weapon (for Perk, damage, etc,. calculations)
|
|
// But for Weapon Firing end state logic we need to point to the real Drone Pawn so we don't mess
|
|
// With whichever weapon the Player had equipped at that point
|
|
|
|
OriginalInstigator = Instigator;
|
|
|
|
Instigator = InstigatorDrone;
|
|
|
|
super.EndState(NextStateName);
|
|
|
|
Instigator = OriginalInstigator;
|
|
}
|
|
}
|
|
|
|
simulated function FireAmmunition()
|
|
{
|
|
CurrentFireMode = DEFAULT_FIREMODE;
|
|
super.FireAmmunition();
|
|
}
|
|
|
|
simulated function GetMuzzleLocAndRot(out vector MuzzleLoc, out rotator MuzzleRot)
|
|
{
|
|
if (KFSkeletalMeshComponent(Mesh).GetSocketWorldLocationAndRotation('MuzzleFlash', MuzzleLoc, MuzzleRot) == false)
|
|
{
|
|
`Log("MuzzleFlash not found!");
|
|
}
|
|
|
|
// To World Coordinates. (Rotation should be 0 so no taken into account)
|
|
// MuzzleLoc = Location + QuatRotateVector(QuatFromRotator(Rotation), MuzzleLoc);
|
|
}
|
|
|
|
simulated function InstantFireClient()
|
|
{
|
|
local vector StartTrace;
|
|
local vector EndTrace;
|
|
local rotator AimRot;
|
|
local Array<ImpactInfo> ImpactList;
|
|
local int Idx;
|
|
local ImpactInfo RealImpact;
|
|
local float CurPenetrationValue;
|
|
local KFPawn KFPOwner;
|
|
|
|
// see Controller AimHelpDot() / AimingHelp()
|
|
bInstantHit = true;
|
|
|
|
// define range to use for CalcWeaponFire()
|
|
GetMuzzleLocAndRot(StartTrace, AimRot );
|
|
EndTrace = StartTrace + vector(Owner.Rotation) * GetTraceRange();
|
|
|
|
bInstantHit = false;
|
|
|
|
// Initialize penetration power
|
|
PenetrationPowerRemaining = GetInitialPenetrationPower(CurrentFireMode);
|
|
CurPenetrationValue = PenetrationPowerRemaining;
|
|
|
|
// Perform shot
|
|
RealImpact = CalcWeaponFire(StartTrace, EndTrace, ImpactList);
|
|
|
|
// Set flash location to trigger client side effects. Bypass Weapon.SetFlashLocation since
|
|
// that function is not marked as simulated and we want instant client feedback.
|
|
// ProjectileFire/IncrementFlashCount has the right idea:
|
|
// 1) Call IncrementFlashCount on Server & Local
|
|
// 2) Replicate FlashCount if ( !bNetOwner )
|
|
// 3) Call WeaponFired() once on local player
|
|
KFPOwner = KFPawn(Owner);
|
|
if( KFPOwner != None )
|
|
{
|
|
KFPOwner.SetFlashLocation( Self, CurrentFireMode, RealImpact.HitLocation );
|
|
}
|
|
|
|
// local player only for clientside hit detection
|
|
// allow weapon to add extra bullet impacts (useful for shotguns)
|
|
InstantFireClient_AddImpacts(StartTrace, AimRot, ImpactList);
|
|
|
|
for (Idx = 0; Idx < ImpactList.Length; Idx++)
|
|
{
|
|
ProcessInstantHitEx(CurrentFireMode, ImpactList[Idx],, CurPenetrationValue, Idx);
|
|
}
|
|
|
|
if ( Instigator.Role < ROLE_Authority )
|
|
{
|
|
SendClientImpactList(CurrentFireMode, ImpactList);
|
|
}
|
|
}
|
|
|
|
simulated function HandleProjectileImpact(byte ProjectileFireMode, ImpactInfo Impact, optional float PenetrationValue)
|
|
{
|
|
// local player only for clientside hit detection
|
|
if ( Instigator != None)
|
|
{
|
|
if ( Instigator.Role < ROLE_Authority )
|
|
{
|
|
SendClientProjectileImpact(ProjectileFireMode, Impact, PenetrationValue);
|
|
}
|
|
|
|
ProcessInstantHitEx(ProjectileFireMode, Impact,, PenetrationValue, 0);
|
|
}
|
|
}
|
|
|
|
|
|
simulated function Projectile ProjectileFire()
|
|
{
|
|
local vector StartTrace, RealStartLoc, AimDir;
|
|
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()) )
|
|
{
|
|
// This is where we would start an instant trace. (what CalcWeaponFire uses)
|
|
// MySkelMesh.GetSocketWorldLocationAndRotation( 'MuzzleFlash', StartTrace, AimRot );
|
|
GetMuzzleLocAndRot(StartTrace, AimRot );
|
|
|
|
AimDir = Vector(Owner.Rotation);
|
|
|
|
// this is the location where the projectile is spawned.
|
|
RealStartLoc = StartTrace;
|
|
|
|
return SpawnAllProjectiles(MyProjectileClass, RealStartLoc, AimDir);
|
|
}
|
|
|
|
return None;
|
|
}
|
|
|
|
simulated function KFProjectile SpawnProjectile( class<KFProjectile> KFProjClass, vector RealStartLoc, vector AimDir )
|
|
{
|
|
local KFProjectile SpawnedProjectile;
|
|
local int ProjDamage;
|
|
local Pawn OriginalInstigator;
|
|
|
|
/*
|
|
* Instigator issues here. The instigator of the weapon here is the PlayerController which won't replicate the projectile.
|
|
* Changing it to the drone pawn for spawning, then swapping it again to be able to apply perk effects.
|
|
*/
|
|
|
|
// Spawn projectile
|
|
OriginalInstigator = Instigator;
|
|
Instigator = InstigatorDrone;
|
|
SpawnedProjectile = Spawn( KFProjClass, self,, RealStartLoc);
|
|
if( SpawnedProjectile != none && !SpawnedProjectile.bDeleteMe )
|
|
{
|
|
// 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];
|
|
}
|
|
|
|
// Set the penetration power for this projectile
|
|
// because of clientside hit detection, we need two variables --
|
|
// one that replicates on init and one that updates but doesn't replicate
|
|
SpawnedProjectile.InitialPenetrationPower = GetInitialPenetrationPower(CurrentFireMode);
|
|
SpawnedProjectile.PenetrationPower = SpawnedProjectile.InitialPenetrationPower;
|
|
|
|
SpawnedProjectile.UpgradeDamageMod = GetUpgradeDamageMod();
|
|
SpawnedProjectile.Init( AimDir );
|
|
}
|
|
|
|
if (MedicComp != none && KFProj_HealingDart(SpawnedProjectile) != None)
|
|
{
|
|
if (TargetingComp != none && TargetingComp.LockedTarget[1] != none)
|
|
{
|
|
KFProj_HealingDart(SpawnedProjectile).SeekTarget = TargetingComp.LockedTarget[1];
|
|
}
|
|
}
|
|
|
|
Instigator = OriginalInstigator;
|
|
// return it up the line
|
|
return SpawnedProjectile;
|
|
}
|
|
|
|
simulated function IncrementFlashCount()
|
|
{
|
|
local KFPawn P;
|
|
P = KFPawn(Owner);
|
|
|
|
if( P != None )
|
|
{
|
|
P.IncrementFlashCount( Self, CurrentFireMode );
|
|
}
|
|
}
|
|
|
|
simulated function Fire()
|
|
{
|
|
if (bFiring)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (HasAmmo(DEFAULT_FIREMODE))
|
|
{
|
|
SendToFiringState(DEFAULT_FIREMODE);
|
|
}
|
|
|
|
bFiring = true;
|
|
}
|
|
|
|
|
|
simulated function StopFire(byte FireModeNum)
|
|
{
|
|
if (!bFiring)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bFiring = false;
|
|
super.StopFire(FireModeNum);
|
|
GoToState('Inactive');
|
|
}
|
|
|
|
simulated function bool ShouldRefire()
|
|
{
|
|
// if doesn't have ammo to keep on firing, then stop
|
|
if( !HasAmmo( CurrentFireMode ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return bFiring;
|
|
}
|
|
|
|
/**
|
|
* Starts playing looping FireSnd only (used for switching sounds in Zedtime)
|
|
*/
|
|
simulated function StartLoopingFireSound(byte FireModeNum)
|
|
{
|
|
if ( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !ShouldForceSingleFireSound() )
|
|
{
|
|
bPlayingLoopingFireSnd = true;
|
|
KFPawn(Owner).SetWeaponAmbientSound(WeaponFireSnd[FireModeNum].DefaultCue, WeaponFireSnd[FireModeNum].FirstPersonCue);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stops playing looping FireSnd only (used for switching sounds in Zedtime)
|
|
*/
|
|
simulated function StopLoopingFireSound(byte FireModeNum)
|
|
{
|
|
if ( bPlayingLoopingFireSnd )
|
|
{
|
|
KFPawn(Owner).SetWeaponAmbientSound(None);
|
|
if ( FireModeNum < WeaponFireLoopEndSnd.Length )
|
|
{
|
|
WeaponPlayFireSound(WeaponFireLoopEndSnd[FireModeNum].DefaultCue, WeaponFireLoopEndSnd[FireModeNum].FirstPersonCue);
|
|
}
|
|
|
|
bPlayingLoopingFireSnd = false;
|
|
}
|
|
}
|
|
|
|
simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation )
|
|
{
|
|
local name WeaponFireAnimName;
|
|
local KFPerk CurrentPerk;
|
|
local float TempTweenTime, AdjustedAnimLength;
|
|
local KFPawn KFPO;
|
|
|
|
// If we have stopped the looping fire sound to play single fire sounds for zed time
|
|
// start the looping sound back up again when the time is back above zed time speed
|
|
if( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !bPlayingLoopingFireSnd )
|
|
{
|
|
StartLoopingFireSound(FireModeNum);
|
|
}
|
|
|
|
PlayFiringSound(CurrentFireMode);
|
|
KFPO = KFPawn(Owner);
|
|
|
|
if( KFPO != none )
|
|
{
|
|
// Tell our pawn about any changes in animation speed
|
|
UpdateWeaponAttachmentAnimRate( GetThirdPersonAnimRate() );
|
|
|
|
if( KFPO.IsLocallyControlled() )
|
|
{
|
|
if( KFPO.IsFirstPerson() )
|
|
{
|
|
if ( !bPlayingLoopingFireAnim )
|
|
{
|
|
WeaponFireAnimName = GetWeaponFireAnim(FireModeNum);
|
|
|
|
if ( WeaponFireAnimName != '' )
|
|
{
|
|
AdjustedAnimLength = MySkelMesh.GetAnimLength(WeaponFireAnimName);
|
|
TempTweenTime = FireTweenTime;
|
|
|
|
CurrentPerk = GetPerk();
|
|
if( CurrentPerk != none )
|
|
{
|
|
CurrentPerk.ModifyRateOfFire( AdjustedAnimLength, self );
|
|
|
|
// We need to unlock the slide if we fire from zero ammo while uber ammo is active
|
|
if( EmptyMagBlendNode != none
|
|
&& BonesToLockOnEmpty.Length > 0
|
|
&& AmmoCount[GetAmmoType(FireModeNum)] == 0
|
|
&& CurrentPerk.GetIsUberAmmoActive(self) )
|
|
{
|
|
EmptyMagBlendNode.SetBlendTarget( 0, 0 );
|
|
TempTweenTime = 0.f;
|
|
}
|
|
}
|
|
|
|
PlayAnimation(WeaponFireAnimName, AdjustedAnimLength,, TempTweenTime);
|
|
}
|
|
}
|
|
|
|
// Start muzzle flash effect
|
|
CauseMuzzleFlash(FireModeNum);
|
|
}
|
|
|
|
HandleRecoil();
|
|
ShakeView();
|
|
|
|
if (AmmoCount[0] == 0 && ForceReloadTimeOnEmpty > 0)
|
|
{
|
|
SetTimer(ForceReloadTimeOnEmpty, false, nameof(ForceReload));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
// Shooting Animations
|
|
FireSightedAnims[0]=Shoot_Iron
|
|
FireSightedAnims[1]=Shoot_Iron2
|
|
FireSightedAnims[2]=Shoot_Iron3
|
|
|
|
// FOV
|
|
MeshIronSightFOV=52
|
|
PlayerIronSightFOV=70
|
|
|
|
// Depth of field
|
|
DOF_FG_FocalRadius=75
|
|
DOF_FG_MaxNearBlurSize=3.5
|
|
|
|
// Zooming/Position
|
|
PlayerViewOffset=(X=9.0,Y=10,Z=-4)
|
|
|
|
// Content
|
|
PackageKey="AutoTurretWeapon"
|
|
FirstPersonMeshName="wep_1p_autoturretWeapon_mesh.Wep_1stP_AutoTurretWeapon_Rig"
|
|
FirstPersonAnimSetNames(0)="wep_1p_autoturretWeapon_anim.Wep_1stP_AutoTurretWeapon_Anim"
|
|
PickupMeshName="wep_3p_autoturretWeapon_mesh.Wep_AutoTurretWeapon_Pickup"
|
|
AttachmentArchetypeName="WEP_AutoTurretWeapon_ARCH.AutoTurretWeaponAttachment"
|
|
MuzzleFlashTemplateName="wep_autoturretWeapon_arch.Wep_AutoTurretWeapon_MuzzleFlash"
|
|
|
|
// Zooming/Position
|
|
IronSightPosition=(X=7,Y=0,Z=0)
|
|
|
|
// Ammo
|
|
MagazineCapacity[0]=150
|
|
SpareAmmoCapacity[0]=0
|
|
InitialSpareMags[0]=0
|
|
bCanBeReloaded=false
|
|
bReloadFromMagazine=false
|
|
|
|
// Recoil
|
|
maxRecoilPitch=225
|
|
minRecoilPitch=150
|
|
maxRecoilYaw=150
|
|
minRecoilYaw=-150
|
|
RecoilRate=0.085
|
|
RecoilMaxYawLimit=500
|
|
RecoilMinYawLimit=65035
|
|
RecoilMaxPitchLimit=900
|
|
RecoilMinPitchLimit=65035
|
|
RecoilISMaxYawLimit=75
|
|
RecoilISMinYawLimit=65460
|
|
RecoilISMaxPitchLimit=195
|
|
RecoilISMinPitchLimit=65460
|
|
RecoilViewRotationScale=0.25
|
|
IronSightMeshFOVCompensationScale=1.5
|
|
|
|
// Inventory / Grouping
|
|
InventorySize=5
|
|
GroupPriority=25
|
|
WeaponSelectTexture=Texture2D'WEP_UI_AutoTurret_TEX.UI_WeaponSelect_AutoTurret'
|
|
|
|
// DEFAULT_FIREMODE
|
|
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletBurst'
|
|
FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring
|
|
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit
|
|
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_Pistol9mm'
|
|
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_AutoTurret'
|
|
FireInterval(DEFAULT_FIREMODE)=+0.12 // 500 rpm
|
|
InstantHitDamage(DEFAULT_FIREMODE)=10
|
|
Spread(DEFAULT_FIREMODE)=0.01
|
|
FireOffset=(X=30,Y=4.5,Z=-4)
|
|
|
|
// ALT_FIREMODE
|
|
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring
|
|
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None
|
|
|
|
// BASH_FIREMODE
|
|
WeaponFireTypes(BASH_FIREMODE)=EWFT_None
|
|
|
|
// Fire Effects
|
|
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_LP_3P', FirstPersonCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_LP_1P')
|
|
WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_EndLP_3P', FirstPersonCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_EndLP_1P')
|
|
|
|
WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shoot_3P', FirstPersonCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shoot_3P')
|
|
SingleFireSoundIndex=ALTFIRE_FIREMODE
|
|
|
|
bLoopingFireSnd(DEFAULT_FIREMODE)=true
|
|
bLoopingFireAnim(DEFAULT_FIREMODE)=true
|
|
|
|
AssociatedPerkClasses(0)=class'KFPerk_Commando'
|
|
|
|
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1)))
|
|
WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.3f), (Stat=EWUS_Damage1, Scale=1.3f), (Stat=EWUS_Weight, Add=2)))
|
|
|
|
InstigatorDrone=none
|
|
|
|
bFiring=false
|
|
}
|
|
|