495 lines
14 KiB
Ucode
495 lines
14 KiB
Ucode
//=============================================================================
|
|
// 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 FireLookAheadSeconds;
|
|
|
|
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) // This is used for regular shooting
|
|
{
|
|
//TargetLocation = CurrentTarget.Mesh.GetBoneLocation('Spine1');
|
|
TargetLocation = CurrentTarget.Location;
|
|
TargetLocation.Z += CurrentTarget.GetCollisionHeight() * 0.5f; // Add an offset on the location, so it matches correctly
|
|
|
|
// Apply look ahead
|
|
TargetLocation += CurrentTarget.Velocity * FireLookAheadSeconds;
|
|
}
|
|
else if (CurrentDistanceProjectile > 0.f) // This is used for the explosion when drone dies
|
|
{
|
|
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 (IsInState('WeaponFiring'))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (HasAmmo(DEFAULT_FIREMODE))
|
|
{
|
|
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
|
|
|
|
FireLookAheadSeconds=0.2f
|
|
} |