1
0
KF2-Dev-Scripts/KFGameContent/Classes/KFWeap_SMG_G18.uc
2023-10-18 02:03:17 +03:00

425 lines
14 KiB
Ucode

//=============================================================================
// KFWeap_SMG_G18
//=============================================================================
// full auto pistol with riot shield
//=============================================================================
// Killing Floor 2
// Copyright (C) 2019 Tripwire Interactive LLC
//=============================================================================
class KFWeap_SMG_G18 extends KFWeap_SMGBase;
// default fx
var AkBaseSoundObject BlockSound;
var ParticleSystem BlockParticleSystem;
var name BlockEffectsSocketName;
var array<BlockEffectInfo> BlockTypes;
var float BlockDamageMitigation;
var const name IdleToIronSightAnim;
var const name IronSightToIdleAnim;
var float BlockAngle;
var transient float BlockAngleCos;
/** Explosion actor class to spawn */
var class<KFExplosionActor> ExplosionActorClass;
var() KFGameExplosion ExplosionTemplate;
simulated event PostBeginPlay()
{
super.PostBeginPlay();
BlockAngleCos = cos((BlockAngle / 2.f) * DegToRad);
}
/*********************************************************************************************
* 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
{
simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo)
{
GotoState('ActiveIronSights');
}
}
/*********************************************************************************************
* State ActiveIronSights
* Plays an animation when transitioning to or from iron sights, but this animation
* should only be played in the "Active" state. This state allows us to encapsulate this
* functionality.
*********************************************************************************************/
simulated state ActiveIronSights extends Active
{
simulated function BeginState(Name PreviousStateName)
{
local float ZoomTimeToGo;
ZoomTimeToGo = MySkelMesh.GetAnimLength(IdleToIronSightAnim);
Global.ZoomIn(true, ZoomTimeToGo);
PlayAnimation(IdleToIronSightAnim, ZoomTime, false);
}
simulated function ZoomOut(bool bAnimateTransition, float ZoomTimeToGo)
{
ZoomTimeToGo = MySkelMesh.GetAnimLength(IronSightToIdleAnim);
Global.ZoomOut(true, ZoomTimeToGo);
PlayAnimation(IronSightToIdleAnim, ZoomTime, false);
GotoState('Active');
}
}
/*********************************************************************************************
* State MeleeAttackBasic
* This is a basic melee state that's used as a base for other more advanced states
*********************************************************************************************/
simulated state MeleeAttackBasic
{
simulated function NotifyMeleeCollision(Actor HitActor, optional vector HitLocation)
{
local KFPawn Victim;
if (Role == ROLE_Authority)
{
if (HitActor.bWorldGeometry)
{
return;
}
Victim = KFPawn(HitActor);
if (Victim == None ||
(Victim.GetTeamNum() == Instigator.GetTeamNum()) ||
(Victim.bPlayedDeath && `TimeSince(Victim.TimeOfDeath) > 0.f))
{
return;
}
// need to delay one frame, since this is called from AnimNotify
SetTimer(0.001f, false, nameof(DoBashImpulse));
}
}
}
simulated function DoBashImpulse()
{
local KFExplosionActor ExploActor;
local vector SpawnLoc;
local rotator SpawnRot;
if (Instigator.Role < ROLE_Authority)
{
return;
}
SpawnLoc = Instigator.Location;
SpawnRot = Instigator.Rotation;
// nudge backwards to give a wider code near the player
SpawnLoc += vector(SpawnRot);
// explode using the given template
ExploActor = Spawn(ExplosionActorClass, self,, SpawnLoc, SpawnRot,, true);
if (ExploActor != None)
{
ExploActor.InstigatorController = Instigator.Controller;
ExploActor.Instigator = Instigator;
ExploActor.bReplicateInstigator = true;
ExploActor.Explode(ExplosionTemplate, vector(SpawnRot));
}
}
/**
* 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 Reloading
{
/** Cancel reload when going into ironsights */
simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo)
{
GotoState('ActiveIronSights');
AbortReload();
}
}
function AdjustDamage(out int InDamage, class<DamageType> DamageType, Actor DamageCauser)
{
local KFPerk InstigatorPerk;
local byte BlockTypeIndex;
local float DmgCauserDot;
if (!bUsingSights)
{
return;
}
// don't apply block effects for teammates
if (Instigator.IsSameTeam(DamageCauser.Instigator))
{
return;
}
if (CanBlockDamageType(DamageType, BlockTypeIndex))
{
if (ClassIsChildOf(DamageCauser.class, class'Projectile'))
{
// Projectile might be beyond/behind player, resulting in bad dot
// Projectile won't have a velocity to check against, either
// Assume velocity is the vector between projectile and instigator
DmgCauserDot = Normal(DamageCauser.Instigator.Location - DamageCauser.Location) dot vector(Instigator.Rotation);
}
else
{
DmgCauserDot = Normal(DamageCauser.Location - Instigator.Location) dot vector(Instigator.Rotation);
}
if (DmgCauserDot > BlockAngleCos)
{
InDamage *= GetUpgradedBlockDamageMitigation(CurrentWeaponUpgradeIndex);
ClientPlayBlockEffects(BlockTypeIndex);
InstigatorPerk = GetPerk();
if (InstigatorPerk != none)
{
InstigatorPerk.SetSuccessfullBlock();
}
}
}
}
/** If true, this damage type can be blocked by the MeleeBlocking state */
function bool CanBlockDamageType(class<DamageType> DamageType, optional out byte out_Idx)
{
local int Idx;
// Check if this damage should be ignored completely
for (Idx = 0; Idx < BlockTypes.length; ++Idx)
{
if (ClassIsChildOf(DamageType, BlockTypes[Idx].DmgType))
{
out_Idx = Idx;
return true;
}
}
out_Idx = INDEX_NONE;
return false;
}
/** Called on the server when successfully block/parry an attack */
unreliable client function ClientPlayBlockEffects(optional byte BlockTypeIndex=255)
{
local AkBaseSoundObject Sound;
local ParticleSystem PSTemplate;
GetBlockEffects(BlockTypeIndex, Sound, PSTemplate);
PlayLocalBlockEffects(Sound, PSTemplate);
}
/** Returns sound and particle system overrides using index into BlockTypes array */
simulated function GetBlockEffects(byte BlockIndex, out AKBaseSoundObject outSound, out ParticleSystem outParticleSys)
{
outSound = BlockSound;
outParticleSys = BlockParticleSystem;
if (BlockIndex != 255)
{
if (BlockTypes[BlockIndex].BlockSound != None)
{
outSound = BlockTypes[BlockIndex].BlockSound;
}
if (BlockTypes[BlockIndex].BlockParticleSys != None)
{
outParticleSys = BlockTypes[BlockIndex].BlockParticleSys;
}
}
}
/** Called on the client when successfully block/parry an attack */
simulated function PlayLocalBlockEffects(AKBaseSoundObject Sound, ParticleSystem PSTemplate)
{
local vector Loc;
local rotator Rot;
local ParticleSystemComponent PSC;
if (Sound != None)
{
PlaySoundBase(Sound, true);
}
if (PSTemplate != None)
{
if (MySkelMesh.GetSocketWorldLocationAndRotation(BlockEffectsSocketName, Loc, Rot))
{
PSC = WorldInfo.MyEmitterPool.SpawnEmitter(PSTemplate, Loc, Rot);
PSC.SetDepthPriorityGroup(SDPG_Foreground);
}
else
{
`log(self@GetFuncName()@"missing BlockEffects Socket!");
}
}
}
/*********************************************************************************************
* Upgrades
********************************************************************************************/
static simulated function float GetUpgradedBlockDamageMitigation(int UpgradeIndex)
{
return GetUpgradedStatValue(default.BlockDamageMitigation, EWUS_BlockDmgMitigation, UpgradeIndex);
}
defaultproperties
{
FireSightedAnims[0]=Shoot_Iron
FireSightedAnims[1]=Shoot_Iron2
FireSightedAnims[2]=Shoot_Iron3
// Inventory
InventorySize=8
GroupPriority=100
WeaponSelectTexture=Texture2D'WEP_UI_RiotShield_TEX.UI_WeaponSelect_RiotShield'
// FOV
MeshFOV=96
MeshIronSightFOV=75
PlayerIronSightFOV=64
// Zooming/Position
IronSightPosition=(X=0,Y=0,Z=0)
PlayerViewOffset=(X=10.f,Y=0.f,Z=-7.0f)
IdleToIronSightAnim=Iron_Shield_Up
IronSightToIdleAnim=Iron_Shield_Down
// Content
PackageKey="RiotShield"
FirstPersonMeshName="WEP_1P_RiotShield_MESH.Wep_1P_RiotShield_Rig"
FirstPersonAnimSetNames(0)="WEP_1P_RiotShield_ANIM.Wep_1stP_RiotShield_Anim"
PickupMeshName="WEP_3P_RiotShield_MESH.Wep_3P_RiotShield_Pickup"
AttachmentArchetypeName="WEP_RiotShield_ARCH.Wep_G18_3P"
MuzzleFlashTemplateName="WEP_RiotShield_ARCH.Wep_G18_MuzzleFlash"
// Ammo
MagazineCapacity[0]=33
SpareAmmoCapacity[0]=462
InitialSpareMags[0]=4
bCanBeReloaded=true
bReloadFromMagazine=true
bHasFireLastAnims=true
BonesToLockOnEmpty=(RW_Bolt, RW_Bullets1, RW_Bullets2, RW_Barrel)
// Recoil
maxRecoilPitch=100
minRecoilPitch=75
maxRecoilYaw=85
minRecoilYaw=-85
RecoilRate=0.045
RecoilMaxYawLimit=500
RecoilMinYawLimit=65035
RecoilMaxPitchLimit=900
RecoilMinPitchLimit=65035
RecoilISMaxYawLimit=100
RecoilISMinYawLimit=65435
RecoilISMaxPitchLimit=375
RecoilISMinPitchLimit=65460
IronSightMeshFOVCompensationScale=1.65
WalkingRecoilModifier=1.1
JoggingRecoilModifier=1.2
// Block FX
BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Hammer'
BlockParticleSystem=ParticleSystem'FX_Impacts_EMIT.FX_Block_melee_01'
BlockEffectsSocketName=BlockEffect
// Blocking
BlockTypes.Add((DmgType=class'KFDT_Bludgeon', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_Slashing', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_Fire_HuskFireball', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_Fire_HuskFlamethrower'))
BlockTypes.Add((DmgType=class'KFDT_BloatPuke'))
BlockTypes.Add((DmgType=class'KFDT_EvilDAR_Rocket', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_EvilDAR_Laser', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_DAR_EMPBlast', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_Ballistic_PatMinigun', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_Explosive_PatMissile', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_Ballistic_HansAK12', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_EMP_MatriarchTeslaBlast', BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Bullet_Impact_Metal'))
BlockTypes.Add((DmgType=class'KFDT_EMP_MatriarchPlasmaCannon'))
BlockTypes.Add((DmgType=class'KFDT_FleshpoundKing_ChestBeam'))
BlockDamageMitigation=0.4
// How many degrees in front of the player to block
// 0 = don't block anything
// 180 = block everything in front of player
// 360 = block everything all around player
BlockAngle=170.f
// For procedural weapon hiding
QuickWeaponDownRotation=(Pitch=-8192,Yaw=0,Roll=0)
// DEFAULT_FIREMODE
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto'
FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_G18'
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_G18'
FireInterval(DEFAULT_FIREMODE)=+.05455 // 1100 RPM
Spread(DEFAULT_FIREMODE)=0.045
InstantHitDamage(DEFAULT_FIREMODE)=28
FireOffset=(X=30,Y=6.5,Z=-4)
// ALT_FIREMODE
FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle'
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_InstantHit
WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_G18'
InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_G18'
FireInterval(ALTFIRE_FIREMODE)=+0.175 // 343 RPM
InstantHitDamage(ALTFIRE_FIREMODE)=28
Spread(ALTFIRE_FIREMODE)=0.015
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_G18Shield'
InstantHitMomentum(BASH_FIREMODE)=10000.f
InstantHitDamage(BASH_FIREMODE)=35
// Explosion settings. Using archetype so that clients can serialize the content
// without loading the 1st person weapon content (avoid 'Begin Object')!
ExplosionActorClass=class'KFExplosionActorReplicated'
ExplosionTemplate=KFGameExplosion'WEP_RiotShield_ARCH.Wep_G18_Shield_Impulse'
//@todo: add akevents when we have them
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Auto_3P_Loop', FirstPersonCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Auto_Loop')
WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Fire_3P_Single', FirstPersonCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Fire_1P_Single')
WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_MedicSMG.Play_SA_MedicSMG_Handling_DryFire'
WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_MedicDart.Play_WEP_SA_Medic_Dart_DryFire'
// Advanced (High RPM) Fire Effects
bLoopingFireAnim(DEFAULT_FIREMODE)=true
bLoopingFireSnd(DEFAULT_FIREMODE)=true
WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Fire_3P_EndLoop', FirstPersonCue=AkEvent'WW_WEP_G18.Play_WEP_G18_Fire_1P_EndLoop')
SingleFireSoundIndex=ALTFIRE_FIREMODE
// Attachments
bHasIronSights=true
bHasFlashlight=true
//Perks
AssociatedPerkClasses(0)=class'KFPerk_Swat'
//Weapon Upgrade
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.2f), (Stat=EWUS_Damage1, Scale=1.2f), (Stat=EWUS_Weight, Add=1)))
}