424 lines
11 KiB
Ucode
424 lines
11 KiB
Ucode
//=============================================================================
|
|
// KFMedicWeaponComponent
|
|
//=============================================================================
|
|
// Actor that encapsulates the management and replication of medic weapon info.
|
|
// Enables any weapon to be medic.
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2019 Tripwire Interactive LLC
|
|
//=============================================================================
|
|
class KFMedicWeaponComponent extends Actor
|
|
abstract;
|
|
|
|
/** Fire mode 0 is the default weapon firing. */
|
|
const DEFAULT_FIREMODE = 0;
|
|
/** Fire mode 1 is the alternate weapon firing. */
|
|
const ALTFIRE_FIREMODE = 1;
|
|
|
|
var KFWeapon KFW;
|
|
var byte AmmoCost;
|
|
var bool bOpticsPaused;
|
|
|
|
/*********************************************************************************************
|
|
* @name Healing Darts
|
|
********************************************************************************************* */
|
|
|
|
/** DamageTypes for Instant Hit Weapons */
|
|
var class<DamageType> HealingDartDamageType;
|
|
|
|
/** How much to heal for when using this weapon */
|
|
var int HealAmount;
|
|
|
|
/** How many points of heal ammo to recharge per second */
|
|
var float HealFullRechargeSeconds;
|
|
|
|
/** Keeps track of incremental healing since we have to heal in whole integers */
|
|
var float HealingIncrement;
|
|
|
|
/** How many points of heal ammo to recharge per second. Calculated from the HealFullRechargeSeconds */
|
|
var float HealRechargePerSecond;
|
|
|
|
/** The sound of the healing dart hitting someone they will heal */
|
|
var AKEvent HealImpactSoundPlayEvent;
|
|
|
|
/** The sound of the healing dart hitting someone they will hurt */
|
|
var AKEvent HurtImpactSoundPlayEvent;
|
|
|
|
/** Sound to play when the weapon is fired */
|
|
var WeaponFireSndInfo DartFireSnd;
|
|
|
|
const ShootDartAnim = 'Shoot_Dart';
|
|
const ShootDartIronAnim = 'Shoot_Iron_Dart';
|
|
|
|
/** How long after we shoot a healing dart before a zed can grab us.
|
|
* Prevents us from missing healing shots from being grabbed */
|
|
var float HealDartShotWeakZedGrabCooldown;
|
|
|
|
/** Recoil override for healing dart alt-fire */
|
|
var int DartMaxRecoilPitch;
|
|
var int DartMinRecoilPitch;
|
|
var int DartMaxRecoilYaw;
|
|
var int DartMinRecoilYaw;
|
|
|
|
/** Controller rumble override for healing dart. */
|
|
var ForceFeedbackWaveform HealingDartWaveForm;
|
|
|
|
var repnotify byte HealingDartAmmo;
|
|
|
|
var bool bRechargeHealAmmo;
|
|
|
|
/*********************************************************************************************
|
|
@name Optics UI
|
|
********************************************************************************************* */
|
|
|
|
var class<KFGFxWorld_MedicOptics> OpticsUIClass;
|
|
var KFGFxWorld_MedicOptics OpticsUI;
|
|
|
|
/** The last updated value for our ammo - Used to know when to update our optics ammo */
|
|
var byte StoredPrimaryAmmo;
|
|
var byte StoredSecondaryAmmo;
|
|
|
|
/*********************************************************************************************
|
|
@name methods
|
|
********************************************************************************************* */
|
|
|
|
replication
|
|
{
|
|
if (bNetDirty && bRechargeHealAmmo)
|
|
HealingDartAmmo;
|
|
}
|
|
|
|
simulated event ReplicatedEvent(name VarName)
|
|
{
|
|
if (VarName == nameof(HealingDartAmmo))
|
|
{
|
|
KFW.AmmoCount[ALTFIRE_FIREMODE] = HealingDartAmmo;
|
|
}
|
|
else
|
|
{
|
|
Super.ReplicatedEvent(VarName);
|
|
}
|
|
}
|
|
|
|
simulated function Init(KFWeapon InKFW, byte InAmmoCost)
|
|
{
|
|
KFW = InKFW;
|
|
Instigator = InKFW.Instigator;
|
|
AmmoCost = InAmmoCost;
|
|
}
|
|
|
|
simulated event Tick(float DeltaTime)
|
|
{
|
|
super.Tick(DeltaTime);
|
|
if(KFW != none)
|
|
{
|
|
if (KFW.AmmoCount[ALTFIRE_FIREMODE] < KFW.MagazineCapacity[ALTFIRE_FIREMODE])
|
|
{
|
|
HealAmmoRegeneration(DeltaTime);
|
|
}
|
|
}
|
|
if(Instigator != none)
|
|
{
|
|
if (Instigator.IsLocallyControlled() && Instigator.Weapon == KFW)
|
|
{
|
|
UpdateOpticsUI();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @name Healing Darts
|
|
********************************************************************************************* */
|
|
|
|
/** Start the heal recharge cycle */
|
|
function StartHealRecharge()
|
|
{
|
|
local KFPerk InstigatorPerk;
|
|
local float UsedHealRechargeTime;
|
|
|
|
if (!bRechargeHealAmmo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
InstigatorPerk = KFW.GetPerk();
|
|
UsedHealRechargeTime =
|
|
HealFullRechargeSeconds * KFW.static.GetUpgradeHealRechargeMod(KFW.CurrentWeaponUpgradeIndex);
|
|
|
|
InstigatorPerk.ModifyHealerRechargeTime(UsedHealRechargeTime);
|
|
HealRechargePerSecond = KFW.MagazineCapacity[ALTFIRE_FIREMODE] / UsedHealRechargeTime;
|
|
HealingIncrement = 0;
|
|
}
|
|
}
|
|
|
|
function HealAmmoRegeneration(float DeltaTime)
|
|
{
|
|
if (!bRechargeHealAmmo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
HealingIncrement += HealRechargePerSecond * DeltaTime;
|
|
|
|
if (HealingIncrement >= 1.0 && KFW.AmmoCount[ALTFIRE_FIREMODE] < KFW.MagazineCapacity[ALTFIRE_FIREMODE])
|
|
{
|
|
KFW.AmmoCount[ALTFIRE_FIREMODE]++;
|
|
HealingIncrement -= 1.0;
|
|
|
|
// Heal ammo regen is only tracked on the server, so even though we told the client he could
|
|
// keep track of ammo himself like a big boy, we still have to spoon-feed it to him.
|
|
if (KFW.bAllowClientAmmoTracking)
|
|
{
|
|
HealingDartAmmo = KFW.AmmoCount[ALTFIRE_FIREMODE];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
@name Optics UI
|
|
********************************************************************************************* */
|
|
|
|
simulated function InitOptics()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
|
|
if (OpticsUI == none && WorldInfo.NetMode != NM_DedicatedServer)
|
|
{
|
|
KFIM = KFInventoryManager(KFW.InvManager);
|
|
if (KFIM != none)
|
|
{
|
|
OpticsUI = KFGFxWorld_MedicOptics(KFIM.GetOpticsUIMovie(OpticsUIClass));
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function ShutdownOptics()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
local KFWeapon InvKFW;
|
|
|
|
if (OpticsUI != none)
|
|
{
|
|
KFIM = KFInventoryManager(KFW.InvManager);
|
|
if (KFIM != none)
|
|
{
|
|
foreach KFIM.InventoryActors(class'KFWeapon', InvKFW)
|
|
{
|
|
if (InvKFW != KFW &&
|
|
((InvKFW.MedicComp != none && InvKFW.MedicComp.OpticsUI.class == OpticsUI.class)) ||
|
|
((KFWeap_MedicBase(InvKFW) != none && KFWeap_MedicBase(InvKFW).OpticsUI.class == OpticsUI.Class)))
|
|
{
|
|
// A different weapon is still using this optics class
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Create the screen's UI piece
|
|
KFIM.RemoveOpticsUIMovie(OpticsUI.class);
|
|
|
|
OpticsUI.Close();
|
|
OpticsUI = none;
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function UpdateOpticsUI(optional bool bForceUpdate)
|
|
{
|
|
local int DefAmmoCount, AltAmmoCount;
|
|
|
|
DefAmmoCount = 0; // @TODO: change?
|
|
|
|
if (KFW.Instigator != none && KFW.Instigator.weapon == KFW)
|
|
{
|
|
if (OpticsUI == none)
|
|
{
|
|
InitOptics();
|
|
}
|
|
|
|
if (DefAmmoCount != StoredPrimaryAmmo || bForceUpdate)
|
|
{
|
|
StoredPrimaryAmmo = DefAmmoCount;
|
|
OpticsUI.SetPrimaryAmmo(StoredPrimaryAmmo);
|
|
}
|
|
|
|
AltAmmoCount = KFW.GetTotalAmmoAmount(ALTFIRE_FIREMODE);
|
|
if (AltAmmoCount != StoredSecondaryAmmo || bForceUpdate)
|
|
{
|
|
StoredSecondaryAmmo = AltAmmoCount;
|
|
OpticsUI.SetHealerCharge(StoredSecondaryAmmo * 100); //1 = full
|
|
}
|
|
|
|
if (OpticsUI.MinPercentPerShot != AltAmmoCount)
|
|
{
|
|
OpticsUI.SetShotPercentCost(AmmoCost);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
@name Weapon events
|
|
********************************************************************************************* */
|
|
|
|
simulated function bool ProcessInstantHitEx(ImpactInfo Impact)
|
|
{
|
|
local KFPawn HealTarget;
|
|
local float AdjustedHealAmount;
|
|
|
|
HealTarget = KFPawn(Impact.HitActor);
|
|
if (HealTarget == none)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (WorldInfo.GRI.OnSameTeam(Instigator, HealTarget))
|
|
{
|
|
if (KFPlayerController(Instigator.Controller) != none)
|
|
{
|
|
KFPlayerController(Instigator.Controller).AddShotsHit(1);
|
|
}
|
|
|
|
AdjustedHealAmount = HealAmount * KFW.static.GetUpgradeHealMod(KFW.CurrentWeaponUpgradeIndex);
|
|
HealTarget.HealDamage(AdjustedHealAmount, Instigator.Controller, HealingDartDamageType);
|
|
|
|
if (HealImpactSoundPlayEvent != None && !KFW.bSuppressSounds)
|
|
{
|
|
HealTarget.PlaySoundBase(HealImpactSoundPlayEvent, false, false ,, Impact.HitLocation);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (HurtImpactSoundPlayEvent != None && HealTarget != None && !KFW.bSuppressSounds)
|
|
{
|
|
HealTarget.PlaySoundBase(HurtImpactSoundPlayEvent, false, false,, Impact.HitLocation);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
simulated event AdjustRecoil(byte CurrentFireMode)
|
|
{
|
|
if (CurrentFireMode == ALTFIRE_FIREMODE)
|
|
{
|
|
KFW.minRecoilPitch = DartMinRecoilPitch;
|
|
KFW.maxRecoilPitch = DartMaxRecoilPitch;
|
|
KFW.minRecoilYaw = DartMinRecoilYaw;
|
|
KFW.maxRecoilYaw = DartMaxRecoilYaw;
|
|
}
|
|
else
|
|
{
|
|
KFW.minRecoilPitch = KFW.default.minRecoilPitch;
|
|
KFW.maxRecoilPitch = KFW.default.maxRecoilPitch;
|
|
KFW.minRecoilYaw = KFW.default.minRecoilYaw;
|
|
KFW.maxRecoilYaw = KFW.default.maxRecoilYaw;
|
|
}
|
|
}
|
|
|
|
simulated function AdjustShakeView(byte CurrentFireMode)
|
|
{
|
|
if (CurrentFireMode == ALTFIRE_FIREMODE)
|
|
{
|
|
KFW.WeaponFireWaveForm = HealingDartWaveForm;
|
|
}
|
|
else
|
|
{
|
|
KFW.WeaponFireWaveForm = KFW.default.WeaponFireWaveForm;
|
|
}
|
|
}
|
|
|
|
simulated function OnWeaponFireAmmunition(byte CurrentFireMode)
|
|
{
|
|
if (CurrentFireMode == ALTFIRE_FIREMODE)
|
|
{
|
|
// Don't let a weak zed grab us when we just shot a healing dart
|
|
KFW.SetWeakZedGrabCooldownOnPawn(HealDartShotWeakZedGrabCooldown);
|
|
StartHealRecharge();
|
|
}
|
|
}
|
|
|
|
// This makes it impossible for the server to fire before the fire animation has the chance to play on the client side.
|
|
simulated function bool ShouldStartFire(byte FireModeNum)
|
|
{
|
|
return FireModeNum != ALTFIRE_FIREMODE || KFW.HasAmmo(ALTFIRE_FIREMODE, AmmoCost);
|
|
}
|
|
|
|
function OnWeaponGivenTo(Pawn thisPawn, optional bool bDoNotActivate)
|
|
{
|
|
if (Role == ROLE_Authority && !thisPawn.IsLocallyControlled())
|
|
{
|
|
StartHealRecharge();
|
|
}
|
|
}
|
|
|
|
simulated function PlayFiringSound()
|
|
{
|
|
KFW.WeaponPlayFireSound(DartFireSnd.DefaultCue, DartFireSnd.FirstPersonCue);
|
|
}
|
|
|
|
simulated function name GetWeaponFireAnim()
|
|
{
|
|
return (KFW.bUsingSights) ? ShootDartIronAnim : ShootDartAnim;
|
|
}
|
|
|
|
simulated function OnClientWeaponSet()
|
|
{
|
|
InitOptics();
|
|
StartHealRecharge();
|
|
}
|
|
|
|
simulated function OnWeaponRemovedFromInvManager()
|
|
{
|
|
ShutdownOptics();
|
|
}
|
|
|
|
simulated function OnWeaponAttachedTo()
|
|
{
|
|
if (OpticsUI != none)
|
|
{
|
|
OpticsUI.SetPause(false);
|
|
OpticsUI.ClearLockOn();
|
|
UpdateOpticsUI(true);
|
|
OpticsUI.SetShotPercentCost(AmmoCost);
|
|
}
|
|
}
|
|
|
|
simulated function OnWeaponDetached()
|
|
{
|
|
if (Instigator != none && Instigator.Weapon == KFW)
|
|
{
|
|
if (OpticsUI != none)
|
|
{
|
|
OpticsUI.SetPause();
|
|
}
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
// Healing charge
|
|
bRechargeHealAmmo=true
|
|
HealingDartAmmo=100
|
|
HealAmount=20
|
|
HealFullRechargeSeconds=15
|
|
HealDartShotWeakZedGrabCooldown=0.5
|
|
|
|
DartMaxRecoilPitch=250
|
|
DartMinRecoilPitch=200
|
|
DartMaxRecoilYaw=100
|
|
DartMinRecoilYaw=-100
|
|
|
|
RemoteRole=ROLE_SimulatedProxy
|
|
bOnlyRelevantToOwner=true
|
|
bHidden=true
|
|
bCollideActors=false
|
|
bProjTarget=false
|
|
bStatic=false
|
|
bNoDelete=false
|
|
} |