806 lines
23 KiB
Ucode
806 lines
23 KiB
Ucode
//=============================================================================
|
|
// KFWeap_Blunt_MedicBat
|
|
//=============================================================================
|
|
// A melee weapon that creates healing gas and can smack heals into teammates
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2018 Tripwire Interactive LLC
|
|
//=============================================================================
|
|
|
|
// Extends MeleeBase with code from KFWeap_HealerBase (for healing dart ammo functionality)
|
|
class KFWeap_Blunt_MedicBat extends KFWeap_MeleeBase;
|
|
|
|
/** Explosion actor class to spawn */
|
|
var class<KFExplosionActor> ExplosionActorClass;
|
|
var() KFGameExplosion ExplosionTemplate;
|
|
var() KFGameExplosion LightAttackExplosionTemplate;
|
|
|
|
/** Whether Friendly Fire is enabled for the game */
|
|
var bool bFriendlyFireEnabled;
|
|
|
|
/** Amount to heal when hitting a teammate per firemode */
|
|
var array<float> AttackHealAmounts;
|
|
/** Ammo dart cost for healing a teammate per firemode */
|
|
var array<byte> AttackHealCosts;
|
|
|
|
/** How many points of heal ammo to recharge per second */
|
|
var(Healing) 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;
|
|
|
|
/** Current amount of healing darts available */
|
|
var repnotify byte HealingDartAmmo;
|
|
|
|
/** The actor the alt attack explosion should attach to */
|
|
var transient Actor BlastAttachee;
|
|
|
|
/** The hit location of the blast */
|
|
var vector BlastHitLocation;
|
|
|
|
/** Spawn location offset to improve cone hit detection */
|
|
var transient float BlastSpawnOffset;
|
|
|
|
/** Starting Damage radius of the alt attack explosion*/
|
|
var float StartingDamageRadius;
|
|
|
|
/** Animations that play in reaction to hitting with the alt fire attack*/
|
|
const HardFire_L = 'HardFire_L';
|
|
const HardFire_R = 'HardFire_R';
|
|
const HardFire_F = 'HardFire_F';
|
|
const HardFire_B = 'HardFire_B';
|
|
|
|
/** Damage type that is used when hitting a teammate with an attack */
|
|
var class<DamageType> HealingDamageType;
|
|
|
|
/*********************************************************************************************
|
|
@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;
|
|
|
|
replication
|
|
{
|
|
if (bNetInitial)
|
|
bFriendlyFireEnabled;
|
|
|
|
if (bNetDirty && Role == ROLE_Authority && bAllowClientAmmoTracking)
|
|
HealingDartAmmo;
|
|
}
|
|
|
|
|
|
/* epic ===============================================
|
|
* ::ReplicatedEvent
|
|
*
|
|
* Called when a variable with the property flag "RepNotify" is replicated
|
|
*
|
|
* =====================================================
|
|
*/
|
|
simulated event ReplicatedEvent(name VarName)
|
|
{
|
|
if (VarName == nameof(HealingDartAmmo))
|
|
{
|
|
AmmoCount[ALTFIRE_FIREMODE] = HealingDartAmmo;
|
|
}
|
|
else
|
|
{
|
|
Super.ReplicatedEvent(VarName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* HealAmmo Regen client and server
|
|
*/
|
|
simulated event Tick(FLOAT DeltaTime)
|
|
{
|
|
// If we're not fully charged tick the HealAmmoRegen system
|
|
if (AmmoCount[ALTFIRE_FIREMODE] < MagazineCapacity[ALTFIRE_FIREMODE])
|
|
{
|
|
HealAmmoRegeneration(DeltaTime);
|
|
}
|
|
|
|
if (Instigator != none && Instigator.weapon == self)
|
|
{
|
|
UpdateOpticsUI();
|
|
}
|
|
|
|
Super.Tick(DeltaTime);
|
|
}
|
|
|
|
simulated function ConsumeAmmoDarts(int AmmoDartCost)
|
|
{
|
|
// Handles the consumption of ammo darts for the default attack
|
|
// If AmmoCount is being replicated, don't allow the client to modify it here
|
|
if (Role == ROLE_Authority || bAllowClientAmmoTracking)
|
|
{
|
|
// Don't consume ammo if magazine size is 0 (infinite ammo with no reload)
|
|
if (MagazineCapacity[1] > 0 && AmmoCount[1] > 0)
|
|
{
|
|
// Reduce ammo amount by heal ammo cost
|
|
AmmoCount[1] = Max(AmmoCount[1] - AmmoDartCost, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @see KFWeapon::ConsumeAmmo */
|
|
simulated function ConsumeAmmo(byte FireModeNum)
|
|
{
|
|
// If its not the healing fire mode (the heavy attack ammo consumption), use the super's consume
|
|
if (FireModeNum != DEFAULT_FIREMODE)
|
|
{
|
|
Super.ConsumeAmmo(FireModeNum);
|
|
return;
|
|
}
|
|
|
|
ConsumeAmmoDarts(AmmoCost[DEFAULT_FIREMODE]);
|
|
}
|
|
|
|
/** Overridden to call StartHealRecharge on server */
|
|
function GivenTo(Pawn thisPawn, optional bool bDoNotActivate)
|
|
{
|
|
super.GivenTo(thisPawn, bDoNotActivate);
|
|
|
|
// StartHealRecharge gets called on the client from ClientWeaponSet (called from ClientGivenTo, called from GivenTo),
|
|
// but we also need this called on the server for remote clients, since the server needs to track the ammo too (to know if/when to spawn projectiles)
|
|
if (Role == ROLE_Authority && !thisPawn.IsLocallyControlled())
|
|
{
|
|
StartHealRecharge();
|
|
}
|
|
}
|
|
|
|
/** Start the heal recharge cycle */
|
|
function StartHealRecharge()
|
|
{
|
|
local KFPerk InstigatorPerk;
|
|
local float UsedHealRechargeTime;
|
|
|
|
// begin ammo recharge on server
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
InstigatorPerk = GetPerk();
|
|
UsedHealRechargeTime = HealFullRechargeSeconds * static.GetUpgradeHealRechargeMod(CurrentWeaponUpgradeIndex);
|
|
|
|
InstigatorPerk.ModifyHealerRechargeTime(UsedHealRechargeTime);
|
|
// Set the healing recharge rate whenever we start charging
|
|
HealRechargePerSecond = MagazineCapacity[ALTFIRE_FIREMODE] / UsedHealRechargeTime;
|
|
HealingIncrement = 0;
|
|
}
|
|
}
|
|
|
|
/** Heal Ammo Regen */
|
|
function HealAmmoRegeneration(float DeltaTime)
|
|
{
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
HealingIncrement += HealRechargePerSecond * DeltaTime;
|
|
|
|
if (HealingIncrement >= 1.0 && AmmoCount[ALTFIRE_FIREMODE] < MagazineCapacity[ALTFIRE_FIREMODE])
|
|
{
|
|
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 (bAllowClientAmmoTracking)
|
|
{
|
|
HealingDartAmmo = AmmoCount[ALTFIRE_FIREMODE];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated event bool HasAmmo(byte FireModeNum, optional int Amount)
|
|
{
|
|
// Default fire mode either has ammo to trigger the heal or needs to return true to still allow a basic swing
|
|
if (FireModeNum == DEFAULT_FIREMODE)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return super.HasAmmo(FireModeNum, Amount);
|
|
}
|
|
|
|
reliable client function ClientWeaponSet(bool bOptionalSet, optional bool bDoNotActivate)
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
|
|
super.ClientWeaponSet(bOptionalSet, bDoNotActivate);
|
|
|
|
if (OpticsUI == none)
|
|
{
|
|
KFIM = KFInventoryManager(InvManager);
|
|
if (KFIM != none)
|
|
{
|
|
//Create the screen's UI piece
|
|
OpticsUI = KFGFxWorld_MedicOptics(KFIM.GetOpticsUIMovie(OpticsUIClass));
|
|
}
|
|
}
|
|
|
|
// Initialize our displayed ammo count and healer charge
|
|
StartHealRecharge();
|
|
}
|
|
|
|
function ItemRemovedFromInvManager()
|
|
{
|
|
local KFInventoryManager KFIM;
|
|
local KFWeap_Blunt_MedicBat KFW;
|
|
|
|
Super.ItemRemovedFromInvManager();
|
|
|
|
if (OpticsUI != none)
|
|
{
|
|
KFIM = KFInventoryManager(InvManager);
|
|
if (KFIM != none)
|
|
{
|
|
foreach KFIM.InventoryActors(class'KFWeap_Blunt_MedicBat', KFW)
|
|
{
|
|
if (KFW != self && KFW.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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Unpause our optics movie and reinitialize our ammo when we equip the weapon */
|
|
simulated function AttachWeaponTo(SkeletalMeshComponent MeshCpnt, optional Name SocketName)
|
|
{
|
|
super.AttachWeaponTo(MeshCpnt, SocketName);
|
|
|
|
if (OpticsUI != none)
|
|
{
|
|
OpticsUI.SetPause(false);
|
|
UpdateOpticsUI(true);
|
|
OpticsUI.SetShotPercentCost(AmmoCost[ALTFIRE_FIREMODE]);
|
|
}
|
|
}
|
|
|
|
/** Pause the optics movie once we unequip the weapon so it's not playing in the background */
|
|
simulated function DetachWeapon()
|
|
{
|
|
local Pawn OwnerPawn;
|
|
super.DetachWeapon();
|
|
|
|
OwnerPawn = Pawn(Owner);
|
|
if (OwnerPawn != none && OwnerPawn.Weapon == self)
|
|
{
|
|
if (OpticsUI != none)
|
|
{
|
|
OpticsUI.SetPause();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update our displayed ammo count if it's changed
|
|
*/
|
|
simulated function UpdateOpticsUI(optional bool bForceUpdate)
|
|
{
|
|
if (OpticsUI != none && OpticsUI.OpticsContainer != none)
|
|
{
|
|
if (AmmoCount[DEFAULT_FIREMODE] != StoredPrimaryAmmo || bForceUpdate)
|
|
{
|
|
StoredPrimaryAmmo = AmmoCount[DEFAULT_FIREMODE];
|
|
OpticsUI.SetPrimaryAmmo(StoredPrimaryAmmo);
|
|
}
|
|
|
|
if (AmmoCount[ALTFIRE_FIREMODE] != StoredSecondaryAmmo || bForceUpdate)
|
|
{
|
|
StoredSecondaryAmmo = AmmoCount[ALTFIRE_FIREMODE];
|
|
OpticsUI.SetHealerCharge(StoredSecondaryAmmo);
|
|
}
|
|
|
|
if (OpticsUI.MinPercentPerShot != AmmoCost[ALTFIRE_FIREMODE])
|
|
{
|
|
OpticsUI.SetShotPercentCost(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Healing charge doesn't count as ammo for purposes of inventory management (e.g. switching) */
|
|
simulated function bool HasAnyAmmo()
|
|
{
|
|
// Special ammo is stored in the default firemode (heal darts are separate)
|
|
if (HasSpareAmmo() || AmmoCount[DEFAULT_FIREMODE] >= AmmoCost[CUSTOM_FIREMODE])
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Determines the secondary ammo left for HUD display */
|
|
simulated function int GetSecondaryAmmoForHUD()
|
|
{
|
|
return AmmoCount[1];
|
|
}
|
|
|
|
simulated event PreBeginPlay()
|
|
{
|
|
Super.PreBeginPlay();
|
|
|
|
/** Initially check whether friendly fire is on or not. */
|
|
if (Role == ROLE_Authority && KFGameInfo(WorldInfo.Game).FriendlyFireScale != 0.f)
|
|
{
|
|
bFriendlyFireEnabled = true;
|
|
}
|
|
|
|
if (ExplosionTemplate != none)
|
|
{
|
|
StartingDamageRadius = ExplosionTemplate.DamageRadius;
|
|
}
|
|
}
|
|
|
|
/** should be able to interrupt its reload state with any melee attack */
|
|
simulated function bool CanOverrideMagReload(byte FireModeNum)
|
|
{
|
|
return FireModeNum != RELOAD_FIREMODE;
|
|
}
|
|
|
|
/** Override to allow for two different states associated with RELOAD_FIREMODE */
|
|
simulated function SendToFiringState(byte FireModeNum)
|
|
{
|
|
// Ammo needs to be synchronized on client/server for this to work!
|
|
if (FireModeNum == RELOAD_FIREMODE && !Super(KFWeapon).CanReload())
|
|
{
|
|
SetCurrentFireMode(FireModeNum);
|
|
GotoState('WeaponUpkeep');
|
|
return;
|
|
}
|
|
|
|
Super.SendToFiringState(FireModeNum);
|
|
}
|
|
|
|
/** Always allow reload and choose the correct state in SendToFiringState() */
|
|
simulated function bool CanReload(optional byte FireModeNum)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/** Skip calling StillFiring/PendingFire to fix log warning */
|
|
simulated function bool ShouldRefire()
|
|
{
|
|
if (CurrentFireMode == CUSTOM_FIREMODE)
|
|
return false;
|
|
|
|
return Super.ShouldRefire();
|
|
}
|
|
|
|
simulated protected function PrepareExplosion()
|
|
{
|
|
local KFPlayerController KFPC;
|
|
local KFPerk InstigatorPerk;
|
|
|
|
ExplosionTemplate = default.ExplosionTemplate;
|
|
ExplosionTemplate.DamageRadius = StartingDamageRadius;
|
|
|
|
// Change the radius and damage based on the perk
|
|
if (Owner.Role == ROLE_Authority)
|
|
{
|
|
KFPC = KFPlayerController(Instigator.Controller);
|
|
if (KFPC != none)
|
|
{
|
|
`Log("RADIUS BEFORE: " $ExplosionTemplate.DamageRadius);
|
|
|
|
InstigatorPerk = KFPC.GetPerk();
|
|
ExplosionTemplate.DamageRadius *= InstigatorPerk.GetAoERadiusModifier();
|
|
|
|
`Log("RADIUS BEFORE: " $ExplosionTemplate.DamageRadius);
|
|
|
|
}
|
|
}
|
|
|
|
ExplosionActorClass = default.ExplosionActorClass;
|
|
}
|
|
|
|
/** Get the hard fire anim when the alt fire attack connects */
|
|
simulated function name GetWeaponFireAnim(byte FireModeNum)
|
|
{
|
|
// Adjust cone fire angle based on swing direction
|
|
switch (MeleeAttackHelper.CurrentAttackDir)
|
|
{
|
|
case DIR_Forward:
|
|
case DIR_ForwardLeft:
|
|
case DIR_ForwardRight:
|
|
return HardFire_F;
|
|
case DIR_Backward:
|
|
case DIR_BackwardLeft:
|
|
case DIR_BackwardRight:
|
|
return HardFire_B;
|
|
case DIR_Left:
|
|
return HardFire_L;
|
|
case DIR_Right:
|
|
return HardFire_R;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
simulated function SpawnExplosionFromTemplate(KFGameExplosion Template)
|
|
{
|
|
local KFExplosionActor ExploActor;
|
|
local vector SpawnLoc;
|
|
local rotator SpawnRot;
|
|
|
|
SpawnLoc = BlastHitLocation;
|
|
SpawnRot = GetAdjustedAim(SpawnLoc);
|
|
|
|
// explode using the given template
|
|
ExploActor = Spawn(ExplosionActorClass, self, , SpawnLoc, SpawnRot, , true);
|
|
if (ExploActor != None)
|
|
{
|
|
ExploActor.InstigatorController = Instigator.Controller;
|
|
ExploActor.Instigator = Instigator;
|
|
ExplosionTemplate.bFullDamageToAttachee = true;
|
|
|
|
KFExplosionActorReplicated(ExploActor).bIgnoreInstigator = false;
|
|
ExploActor.bReplicateInstigator = true;
|
|
|
|
ExploActor.Explode(Template, vector(SpawnRot));
|
|
}
|
|
|
|
// Reset damage radius
|
|
ExplosionTemplate.DamageRadius = StartingDamageRadius;
|
|
}
|
|
|
|
simulated function CustomFire()
|
|
{
|
|
if (Instigator.Role < ROLE_Authority)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PrepareExplosion();
|
|
SpawnExplosionFromTemplate(ExplosionTemplate);
|
|
|
|
// tell remote clients that we fired, to trigger effects in third person
|
|
IncrementFlashCount();
|
|
}
|
|
|
|
simulated function HealTeammateWithAttack(Actor HitActor, vector HitLocation, float HealingAmount, byte HealCost)
|
|
{
|
|
local KFPawn Victim;
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
if (Instigator != None)
|
|
{
|
|
// only detonate when it hits a pawn so that level geometry doesn't get in the way
|
|
if (HitActor.bWorldGeometry)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Victim = KFPawn(HitActor);
|
|
|
|
// If the victim is a teammate and the player has default ammo (healing darts) then heal this teammate
|
|
// also make sure the victim is still alive and is actually missing health
|
|
if (Victim != None && (Victim.GetTeamNum() == Instigator.GetTeamNum()) && Victim.Health > 0 && Victim.Health < Victim.HealthMax)
|
|
{
|
|
if (AmmoCount[1] >= HealCost)
|
|
{
|
|
ConsumeAmmoDarts(HealCost);
|
|
Victim.HealDamage(HealingAmount, Instigator.Controller, HealingDamageType);
|
|
|
|
if (Instigator.Role >= ROLE_Authority)
|
|
{
|
|
BlastHitLocation = HitLocation;
|
|
SpawnExplosionFromTemplate(LightAttackExplosionTemplate);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// BASH
|
|
simulated state MeleeAttackBasic
|
|
{
|
|
/** Network: Local Player */
|
|
simulated function NotifyMeleeCollision(Actor HitActor, optional vector HitLocation)
|
|
{
|
|
HealTeammateWithAttack(HitActor, HitLocation, AttackHealAmounts[BASH_FIREMODE], AttackHealCosts[BASH_FIREMODE]);
|
|
}
|
|
}
|
|
|
|
// LIGHT ATTACK
|
|
simulated state MeleeChainAttacking
|
|
{
|
|
/** Network: Local Player */
|
|
simulated function NotifyMeleeCollision(Actor HitActor, optional vector HitLocation)
|
|
{
|
|
HealTeammateWithAttack(HitActor, HitLocation, AttackHealAmounts[DEFAULT_FIREMODE], AttackHealCosts[DEFAULT_FIREMODE]);
|
|
}
|
|
}
|
|
|
|
// HEAVY ATTACK
|
|
simulated state MeleeHeavyAttacking
|
|
{
|
|
/** Network: Local Player */
|
|
simulated function NotifyMeleeCollision(Actor HitActor, optional vector HitLocation)
|
|
{
|
|
local KFPawn Victim;
|
|
|
|
if (Instigator != None)
|
|
{
|
|
// only detonate when it hits a pawn so that level geometry doesn't get in the way
|
|
if (HitActor.bWorldGeometry)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Victim = KFPawn(HitActor);
|
|
if (Victim == None ||
|
|
(Victim.bPlayedDeath && `TimeSince(Victim.TimeOfDeath) > 0.f) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (AmmoCount[0] >= AmmoCost[CUSTOM_FIREMODE] && !IsTimerActive(nameof(BeginMedicBatExplosion)))
|
|
{
|
|
BlastAttachee = HitActor;
|
|
BlastHitLocation = HitLocation;
|
|
|
|
// need to delay one frame, since this is called from AnimNotify
|
|
SetTimer(0.001f, false, nameof(BeginMedicBatExplosion));
|
|
|
|
if (Role < ROLE_Authority && Instigator.IsLocallyControlled())
|
|
{
|
|
if (!HitActor.bTearOff || Victim == none)
|
|
{
|
|
ServerBeginMedicBatExplosion(HitActor, HitLocation);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HealTeammateWithAttack(HitActor, HitLocation, AttackHealAmounts[HEAVY_ATK_FIREMODE], AttackHealCosts[HEAVY_ATK_FIREMODE]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Called on the server */
|
|
reliable server private function ServerBeginMedicBatExplosion(Actor HitActor, optional vector HitLocation)
|
|
{
|
|
// Ignore if too far away (something went wrong!)
|
|
if (VSizeSq2D(HitLocation - Instigator.Location) > Square(500))
|
|
{
|
|
return;
|
|
}
|
|
|
|
BlastHitLocation = HitLocation;
|
|
BlastAttachee = HitActor;
|
|
SendToFiringState(CUSTOM_FIREMODE);
|
|
}
|
|
|
|
/** Called when altfire melee attack hits a target and there is ammo left */
|
|
simulated function BeginMedicBatExplosion()
|
|
{
|
|
SendToFiringState(CUSTOM_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
|
|
{
|
|
/**
|
|
* Called from Weapon:Active.BeginState when HasAnyAmmo (which is overridden above) returns false.
|
|
*/
|
|
simulated function WeaponEmpty()
|
|
{
|
|
local int i;
|
|
|
|
// Copied from Weapon:Active.BeginState where HasAnyAmmo returns true.
|
|
// Basically, pretend the weapon isn't empty in this case.
|
|
for (i=0; i<GetPendingFireLength(); i++)
|
|
{
|
|
if (PendingFire(i))
|
|
{
|
|
BeginFire(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
// Content
|
|
PackageKey="Medic_Bat"
|
|
FirstPersonMeshName="WEP_1P_Medic_Bat_MESH.Wep_1stP_Medic_Bat_Rig"
|
|
AttachmentArchetypeName = "WEP_Medic_Bat_ARCH.Wep_Medic_Bat_3P"
|
|
FirstPersonAnimSetNames(0)="WEP_1P_Medic_Bat_ANIM.WEP_1stP_Medic_Bat_ANIM"
|
|
PickupMeshName="WEP_3P_Medic_Bat_MESH.Wep_3rdP_Medic_Bat_Pickup"
|
|
|
|
PlayerViewOffset=(X=3.0,Y=1.0,Z=-3.0)
|
|
|
|
Begin Object Name=MeleeHelper_0
|
|
MaxHitRange = 250
|
|
// Override automatic hitbox creation (advanced)
|
|
HitboxChain.Add((BoneOffset=(X=-3,Z=250)))
|
|
HitboxChain.Add((BoneOffset=(X=+3,Z=230)))
|
|
HitboxChain.Add((BoneOffset=(X=-3,Z=210)))
|
|
HitboxChain.Add((BoneOffset=(X=+3,Z=190)))
|
|
HitboxChain.Add((BoneOffset=(X=-3,Z=170)))
|
|
HitboxChain.Add((BoneOffset=(X=+3,Z=150)))
|
|
HitboxChain.Add((BoneOffset=(X=-3,Z=130)))
|
|
HitboxChain.Add((BoneOffset=(X=+3,Z=110)))
|
|
HitboxChain.Add((BoneOffset=(X=-3,Z=90)))
|
|
HitboxChain.Add((BoneOffset=(X=+3,Z=70)))
|
|
HitboxChain.Add((BoneOffset=(X=-3,Z=50)))
|
|
HitboxChain.Add((BoneOffset=(X=+3,Z=30)))
|
|
HitboxChain.Add((BoneOffset=(Z=10)))
|
|
WorldImpactEffects = KFImpactEffectInfo'FX_Impacts_ARCH.Blunted_melee_impact'
|
|
// modified combo sequences
|
|
MeleeImpactCamShakeScale = 0.035f //0.4
|
|
ChainSequence_F = (DIR_Left, DIR_ForwardRight, DIR_ForwardLeft, DIR_ForwardRight, DIR_ForwardLeft)
|
|
ChainSequence_B = (DIR_BackwardLeft, DIR_Left, DIR_Right, DIR_ForwardRight, DIR_Left, DIR_Right, DIR_Left)
|
|
ChainSequence_L = (DIR_Right, DIR_BackwardRight, DIR_ForwardRight, DIR_ForwardLeft, DIR_Right, DIR_Left)
|
|
ChainSequence_R = (DIR_Left, DIR_BackwardLeft, DIR_ForwardLeft, DIR_ForwardRight, DIR_Left, DIR_Right)
|
|
End Object
|
|
|
|
// Reload
|
|
FiringStatesArray(RELOAD_FIREMODE)=Reloading
|
|
|
|
//Heal Dart Function
|
|
HealingDartAmmo=100
|
|
bCanRefillSecondaryAmmo=false
|
|
HealFullRechargeSeconds=12
|
|
|
|
// Heavy Attack Explosion Ammo
|
|
MagazineCapacity[0]=2 //3
|
|
SpareAmmoCapacity[0]=10 //12
|
|
InitialSpareMags[0]=1
|
|
bCanBeReloaded=true
|
|
bReloadFromMagazine=true
|
|
|
|
// Healing Dart (Alt) Ammo
|
|
MagazineCapacity[1]=100
|
|
SpareAmmoCapacity[1]=0
|
|
|
|
// Inventory
|
|
GroupPriority=75
|
|
InventorySize=4
|
|
WeaponSelectTexture=Texture2D'WEP_UI_Medic_Bat_TEX.UI_WeaponSelect_MedicBat'
|
|
SecondaryAmmoTexture=Texture2D'UI_SecondaryAmmo_TEX.MedicDarts'
|
|
|
|
// Default
|
|
// Maps to Alt Ammo
|
|
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Custom
|
|
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BluntMelee'
|
|
InstantHitDamage(DEFAULT_FIREMODE)=80
|
|
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Bludgeon_MedicBatLight'
|
|
AmmoCost(DEFAULT_FIREMODE)=40
|
|
|
|
// Heavy Attack
|
|
FireModeIconPaths(HEAVY_ATK_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BluntMelee'
|
|
InstantHitDamage(HEAVY_ATK_FIREMODE)=140 //130
|
|
InstantHitDamageTypes(HEAVY_ATK_FIREMODE) = class'KFDT_Bludgeon_MedicBatHeavy'
|
|
|
|
// Heavy Attack Explosion
|
|
// Maps to Default Ammot
|
|
FireModeIconPaths(CUSTOM_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_ShotgunSingle'
|
|
FiringStatesArray(CUSTOM_FIREMODE)=WeaponSingleFiring
|
|
WeaponFireTypes(CUSTOM_FIREMODE)=EWFT_Custom
|
|
FireInterval(CUSTOM_FIREMODE)=1.0f
|
|
AmmoCost(CUSTOM_FIREMODE)=1
|
|
|
|
// Bash
|
|
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_MedicBatBash'
|
|
InstantHitDamage(BASH_FIREMODE)=40
|
|
|
|
// Perk
|
|
AssociatedPerkClasses(0)=class'KFPerk_Berserker'
|
|
AssociatedPerkClasses(1)=class'KFPerk_FieldMedic'
|
|
|
|
// Block and Parry
|
|
BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Crovel'
|
|
ParrySound=AkEvent'WW_WEP_Bullet_Impacts.Play_Parry_Metal'
|
|
ParryDamageMitigationPercent=0.50
|
|
BlockDamageMitigation=0.60
|
|
ParryStrength=5
|
|
|
|
// Explosion light
|
|
Begin Object Class=PointLightComponent Name=ExplosionPointLight
|
|
LightColor=(R = 0,G = 128,B = 255,A = 255)
|
|
Brightness=4.f
|
|
Radius=500.f
|
|
FalloffExponent=10.f
|
|
CastShadows=False
|
|
CastStaticShadows=FALSE
|
|
CastDynamicShadows=True
|
|
bEnabled=FALSE
|
|
LightingChannels=(Indoor = TRUE,Outdoor = TRUE,bInitialized = TRUE)
|
|
End Object
|
|
|
|
// Explosion
|
|
ExplosionActorClass = class'KFExplosionActorReplicated'
|
|
Begin Object Class=KFGameExplosion Name=HeavyAttackHealingExplosion
|
|
Damage=225 //200
|
|
DamageRadius=500
|
|
DamageFalloffExponent=0.f
|
|
DamageDelay=0.f
|
|
MyDamageType=class'KFDT_Toxic_MedicBatGas'
|
|
HealingAmount=15 //20 //30
|
|
|
|
// Damage Effects
|
|
KnockDownStrength=0
|
|
KnockDownRadius=0
|
|
FractureMeshRadius=0
|
|
FracturePartVel=0
|
|
ExplosionEffects=KFImpactEffectInfo'WEP_Medic_Bat_ARCH.Medic_Bat_Explosion'
|
|
ExplosionSound=AkEvent'WW_WEP_MEL_MedicBat.Play_WEP_MedicBat_Smoke_Explode'
|
|
MomentumTransferScale=0
|
|
bIgnoreInstigator=false
|
|
|
|
// Dynamic Light
|
|
//ExploLight=ExplosionPointLight
|
|
//ExploLightStartFadeOutTime=7.0
|
|
//ExploLightFadeOutTime=1.0
|
|
//ExploLightFlickerIntensity=5.f
|
|
//ExploLightFlickerInterpSpeed=15.f
|
|
|
|
// Camera Shake
|
|
CamShake=none
|
|
CamShakeInnerRadius=0
|
|
CamShakeOuterRadius=0
|
|
CamShakeFalloff=1.5f
|
|
bOrientCameraShakeTowardsEpicenter=true
|
|
End Object
|
|
ExplosionTemplate=HeavyAttackHealingExplosion
|
|
|
|
Begin Object Class=KFGameExplosion Name=LightAttackExplosion
|
|
Damage=0
|
|
ParticleEmitterTemplate = ParticleSystem'WEP_Medic_Bat_EMIT.FX_Medic_Bat_Hit'
|
|
|
|
// Camera Shake
|
|
CamShake=none
|
|
CamShakeInnerRadius=0
|
|
CamShakeOuterRadius=0
|
|
CamShakeFalloff=1.5f
|
|
bOrientCameraShakeTowardsEpicenter=true
|
|
End Object
|
|
LightAttackExplosionTemplate=LightAttackExplosion;
|
|
|
|
//Weapon Upgrade Stats
|
|
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.1f), (Stat=EWUS_Damage1, Scale=1.1f), (Stat=EWUS_Damage2, Scale=1.1f), (Stat=EWUS_HealFullRecharge, Scale=0.8f), (Stat=EWUS_Weight, Add=1)))
|
|
WeaponUpgrades[2]=(Stats=((Stat=EWUS_Damage0, Scale=1.2f), (Stat=EWUS_Damage1, Scale=1.2f), (Stat=EWUS_Damage2, Scale=1.2f), (Stat=EWUS_HealFullRecharge, Scale=0.6f), (Stat=EWUS_Weight, Add=2)))
|
|
|
|
BlastSpawnOffset=-10.f
|
|
|
|
AttackHealAmounts(DEFAULT_FIREMODE)=30
|
|
AttackHealAmounts(BASH_FIREMODE)=30
|
|
AttackHealAmounts(HEAVY_ATK_FIREMODE)=40
|
|
|
|
AttackHealCosts(DEFAULT_FIREMODE)=30
|
|
AttackHealCosts(BASH_FIREMODE)=30
|
|
AttackHealCosts(HEAVY_ATK_FIREMODE)=30
|
|
|
|
OpticsUIClass=class'KFGFxWorld_MedicOptics'
|
|
|
|
HealingDamageType=class'KFDT_Healing'
|
|
}
|
|
|
|
|