2017-10-20 02:00:49 +00:00
Class ExtHumanPawn extends KFPawn _Human ;
// Forrests backpack weapon and first person legs.
var SkeletalMeshComponent AttachedBackItem ;
var SkeletalMeshComponent FPBodyMesh ;
var repnotify class < KFWeapon > BackpackWeaponClass ;
var KFWeapon PlayerOldWeapon ;
var transient float NextRedeemTimer , BHopSpeedMod ;
var float KnockbackResist , NoRagdollChance ;
var AnimSet WakeUpAnimSet ;
var name FeignRecoverAnim ;
var byte UnfeignFailedCount , RepRegenHP , BHopAccelSpeed ;
var repnotify bool bFeigningDeath ;
var bool bPlayingFeignDeathRecovery , bRagdollFromFalling , bRagdollFromBackhit , bRagdollFromMomentum , bCanBecomeRagdoll , bRedeadMode , bPendingRedead , bHasBunnyHop , bOnFirstPerson , bFPLegsAttached , bFPLegsInit ;
2017-10-20 07:02:53 +00:00
var byte HealingShieldMod , HealingSpeedBoostMod , HealingDamageBoostMod ;
2017-10-20 02:00:49 +00:00
replication
{
2020-11-28 20:04:55 +00:00
if ( true )
2017-10-20 02:00:49 +00:00
bFeigningDeath , RepRegenHP , BackpackWeaponClass ;
2020-11-28 20:04:55 +00:00
if ( bNetOwner )
2017-10-20 02:00:49 +00:00
bHasBunnyHop ;
2020-11-28 20:04:55 +00:00
if ( bNetDirty )
2017-10-20 07:02:53 +00:00
HealingSpeedBoostMod , HealingDamageBoostMod , HealingShieldMod ;
2017-10-20 02:00:49 +00:00
}
function TakeDamage ( int Damage , Controller InstigatedBy , vector HitLocation , vector Momentum , class < DamageType > DamageType , optional TraceHitInfo HitInfo , optional Actor DamageCauser )
{
2020-11-28 20:04:55 +00:00
if ( KnockbackResist < 1 )
2017-10-20 02:00:49 +00:00
Momentum *= KnockbackResist ;
Super . TakeDamage ( Damage , InstigatedBy , HitLocation , Momentum , DamageType , HitInfo , DamageCauser ) ;
}
simulated function bool Died ( Controller Killer , class < DamageType > damageType , vector HitLocation )
{
local ExtPlayerController C ;
local class < Pawn > KillerPawn ;
local PlayerReplicationInfo KillerPRI ;
local SeqAct _Latent Action ;
2020-11-28 20:04:55 +00:00
if ( WorldInfo . NetMode != NM _Client && PlayerReplicationInfo != None )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( Killer == None || Killer == Controller )
2017-10-20 02:00:49 +00:00
{
KillerPRI = PlayerReplicationInfo ;
KillerPawn = None ;
}
else
{
KillerPRI = Killer . PlayerReplicationInfo ;
2020-11-28 20:04:55 +00:00
if ( KillerPRI == None || KillerPRI . Team != PlayerReplicationInfo . Team )
2017-10-20 02:00:49 +00:00
{
KillerPawn = Killer . Pawn != None ? Killer . Pawn . Class : None ;
2020-11-28 20:04:55 +00:00
if ( PlayerController ( Killer ) == None ) // If was killed by a monster, don't broadcast PRI along with it.
2017-10-20 02:00:49 +00:00
KillerPRI = None ;
}
else KillerPawn = None ;
}
foreach WorldInfo . AllControllers ( class 'ExtPlayerController' , C )
C . ClientKillMessage ( damageType , PlayerReplicationInfo , KillerPRI , KillerPawn ) ;
}
// If got killed by a zombie, turn player into a ragdoll and let em take control of a newly spawned ZED over the ragdoll.
2020-11-28 20:04:55 +00:00
if ( bRedeadMode && WorldInfo . NetMode != NM _Client && damageType != None && Killer != None && Killer != Controller && Killer . GetTeamNum ( ) != 0 )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( bDeleteMe || WorldInfo . Game == None || WorldInfo . Game . bLevelChange )
2017-10-20 02:00:49 +00:00
return FALSE ;
bPendingRedead = true ;
2020-11-28 20:04:55 +00:00
if ( WorldInfo . Game . PreventDeath ( self , Killer , damageType , HitLocation ) )
2017-10-20 02:00:49 +00:00
{
bPendingRedead = false ;
Health = max ( Health , 1 ) ;
return false ;
}
Health = 0 ;
foreach LatentActions ( Action )
Action . AbortFor ( self ) ;
2020-11-28 20:04:55 +00:00
if ( Controller != None )
2017-10-20 02:00:49 +00:00
WorldInfo . Game . Killed ( Killer , Controller , self , damageType ) ;
else WorldInfo . Game . Killed ( Killer , Controller ( Owner ) , self , damageType ) ;
2020-11-28 20:04:55 +00:00
if ( InvManager != None )
2017-10-20 02:00:49 +00:00
InvManager . OwnerDied ( ) ;
Health = 1 ;
2020-11-28 20:04:55 +00:00
if ( ! bFeigningDeath )
2017-10-20 02:00:49 +00:00
PlayFeignDeath ( true , , true ) ;
Health = 0 ;
ClearTimer ( 'UnsetFeignDeath' ) ;
GoToState ( 'TransformZed' ) ;
return true ;
}
return Super . Died ( Killer , DamageType , HitLocation ) ;
}
2020-11-28 20:04:55 +00:00
simulated function BroadcastDeathMessage ( Controller Killer ) ;
2017-10-20 02:00:49 +00:00
2020-11-28 20:04:55 +00:00
function SetBatteryRate ( float Rate )
2017-10-20 02:00:49 +00:00
{
BatteryDrainRate = Default . BatteryDrainRate * Rate ;
NVGBatteryDrainRate = Default . NVGBatteryDrainRate * Rate ;
ClientSetBatteryRate ( Rate ) ;
}
2020-11-28 20:04:55 +00:00
simulated reliable client function ClientSetBatteryRate ( float Rate )
2017-10-20 02:00:49 +00:00
{
BatteryDrainRate = Default . BatteryDrainRate * Rate ;
NVGBatteryDrainRate = Default . NVGBatteryDrainRate * Rate ;
}
event bool HealDamage ( int Amount , Controller Healer , class < DamageType > DamageType , optional bool bCanRepairArmor = true , optional bool bMessageHealer = true )
{
local int DoshEarned , UsedHealAmount ;
local float ScAmount ;
local KFPlayerReplicationInfo InstigatorPRI ;
local ExtPlayerController InstigatorPC , KFPC ;
local KFPerk InstigatorPerk ;
local class < KFDamageType > KFDT ;
2020-11-28 19:53:57 +00:00
local int i ;
local bool bRepairedArmor ;
2017-10-20 07:02:53 +00:00
local ExtPlayerReplicationInfo EPRI ;
local Ext _PerkBase InstigatorExtPerk ;
2017-10-20 02:00:49 +00:00
InstigatorPC = ExtPlayerController ( Healer ) ;
2017-10-20 07:02:53 +00:00
InstigatorPerk = InstigatorPC . GetPerk ( ) ;
2020-11-28 20:04:55 +00:00
if ( InstigatorPerk != None && bCanRepairArmor )
bRepairedArmor = InstigatorPC . GetPerk ( ) . RepairArmor ( self ) ;
2017-10-20 07:02:53 +00:00
EPRI = ExtPlayerReplicationInfo ( InstigatorPC . PlayerReplicationInfo ) ;
2020-11-28 20:04:55 +00:00
if ( EPRI != none )
2017-10-20 02:00:49 +00:00
{
2017-10-20 07:02:53 +00:00
InstigatorExtPerk = ExtPlayerController ( Controller ) . ActivePerkManager . CurrentPerk ;
2020-11-28 20:04:55 +00:00
if ( InstigatorExtPerk != none && Ext _PerkFieldMedic ( InstigatorExtPerk ) != none )
2017-10-20 07:02:53 +00:00
{
2020-11-28 20:04:55 +00:00
if ( Ext _PerkFieldMedic ( InstigatorExtPerk ) . bHealingBoost )
2017-10-20 07:02:53 +00:00
UpdateHealingSpeedBoostMod ( InstigatorPC ) ;
2020-11-28 20:04:55 +00:00
if ( Ext _PerkFieldMedic ( InstigatorExtPerk ) . bHealingDamageBoost )
2017-10-20 07:02:53 +00:00
UpdateHealingDamageBoostMod ( InstigatorPC ) ;
2020-11-28 20:04:55 +00:00
if ( Ext _PerkFieldMedic ( InstigatorExtPerk ) . bHealingShield )
2017-10-20 07:02:53 +00:00
UpdateHealingShieldMod ( InstigatorPC ) ;
}
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:04:55 +00:00
if ( Amount > 0 && IsAliveAndWell ( ) && Health < HealthMax )
2020-11-28 19:53:57 +00:00
{
2017-10-20 02:00:49 +00:00
// Play any healing effects attached to this damage type
KFDT = class < KFDamageType > ( DamageType ) ;
2020-11-28 20:04:55 +00:00
if ( KFDT != none && KFDT . default . bNoPain )
PlayHeal ( KFDT ) ;
2017-10-20 02:00:49 +00:00
2020-11-28 20:04:55 +00:00
if ( Role == ROLE _Authority )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( Healer == None || Healer . PlayerReplicationInfo == None )
2017-10-20 02:00:49 +00:00
return false ;
InstigatorPRI = KFPlayerReplicationInfo ( Healer . PlayerReplicationInfo ) ;
ScAmount = Amount ;
2020-11-28 20:04:55 +00:00
if ( InstigatorPerk != none )
InstigatorPerk . ModifyHealAmount ( ScAmount ) ;
2017-10-20 02:00:49 +00:00
UsedHealAmount = ScAmount ;
// You can never have a HealthToRegen value that's greater than HealthMax
2020-11-28 20:04:55 +00:00
if ( Health + HealthToRegen + UsedHealAmount > HealthMax )
2017-10-20 02:00:49 +00:00
UsedHealAmount = Min ( HealthMax - ( Health + HealthToRegen ) , 255 - HealthToRegen ) ;
else UsedHealAmount = Min ( UsedHealAmount , 255 - HealthToRegen ) ;
2020-11-28 19:53:57 +00:00
HealthToRegen += UsedHealAmount ;
2017-10-20 02:00:49 +00:00
RepRegenHP = HealthToRegen ;
2020-11-28 20:04:55 +00:00
if ( ! IsTimerActive ( 'GiveHealthOverTime' ) )
2017-10-20 02:00:49 +00:00
SetTimer ( HealthRegenRate , true , 'GiveHealthOverTime' ) ;
// Give the healer money/XP for helping a teammate
2020-11-28 20:04:55 +00:00
if ( Healer . Pawn != none && Healer . Pawn != self )
2020-11-28 19:53:57 +00:00
{
2020-11-28 20:04:55 +00:00
DoshEarned = ( UsedHealAmount / float ( HealthMax ) ) * HealerRewardScaler ;
if ( InstigatorPRI != None )
2017-10-20 02:00:49 +00:00
InstigatorPRI . AddDosh ( Max ( DoshEarned , 0 ) , true ) ;
2020-11-28 20:04:55 +00:00
if ( InstigatorPC != None )
InstigatorPC . AddHealPoints ( UsedHealAmount ) ;
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:04:55 +00:00
if ( Healer . bIsPlayer )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( Healer != Controller )
2020-11-28 19:53:57 +00:00
{
2020-11-28 20:04:55 +00:00
if ( InstigatorPC != None )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( ! InstigatorPC . bClientHideNumbers )
2017-10-20 02:00:49 +00:00
InstigatorPC . ClientNumberMsg ( UsedHealAmount , Location , DMG _Heal ) ;
2020-11-28 20:04:55 +00:00
InstigatorPC . ReceiveLocalizedMessage ( class 'KFLocalMessage_Game' , GMT _HealedPlayer , PlayerReplicationInfo ) ;
2017-10-20 02:00:49 +00:00
}
KFPC = ExtPlayerController ( Controller ) ;
2020-11-28 20:04:55 +00:00
if ( KFPC != None )
KFPC . ReceiveLocalizedMessage ( class 'KFLocalMessage_Game' , GMT _HealedBy , Healer . PlayerReplicationInfo ) ;
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:04:55 +00:00
else if ( bMessageHealer && InstigatorPC != None )
InstigatorPC . ReceiveLocalizedMessage ( class 'KFLocalMessage_Game' , GMT _HealedSelf , PlayerReplicationInfo ) ;
2017-10-20 02:00:49 +00:00
}
// don't play dialog for healing done through perk skills (e.g. berserker vampire skill)
2020-11-28 20:04:55 +00:00
if ( bMessageHealer )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
` DialogManager.PlayHealingDialog(KFPawn(Healer.Pawn), self, float(Health + HealthToRegen) / float(HealthMax));
2017-10-20 02:00:49 +00:00
}
2020-11-28 19:53:57 +00:00
// Reduce burn duration and damage in half if you heal while burning
2020-11-28 20:04:55 +00:00
for ( i = 0 ; i < DamageOverTimeArray . Length ; ++ i )
2020-11-28 19:53:57 +00:00
{
2020-11-28 20:04:55 +00:00
if ( DamageOverTimeArray [ i ] . DoT _Type == DOT _Fire )
2020-11-28 19:53:57 +00:00
{
DamageOverTimeArray [ i ] . Duration *= 0.5 ;
DamageOverTimeArray [ i ] . Damage *= 0.5 ;
break ;
}
}
return true ;
2017-10-20 02:00:49 +00:00
}
2020-11-28 19:53:57 +00:00
}
2017-10-20 02:00:49 +00:00
return bRepairedArmor ;
}
function GiveHealthOverTime ( )
{
Super . GiveHealthOverTime ( ) ;
RepRegenHP = HealthToRegen ;
}
simulated event ReplicatedEvent ( name VarName )
{
2020-11-28 20:04:55 +00:00
switch ( VarName )
2017-10-20 02:00:49 +00:00
{
case 'bFeigningDeath' :
PlayFeignDeath ( bFeigningDeath ) ;
break ;
case 'BackpackWeaponClass' :
SetBackpackWeapon ( BackpackWeaponClass ) ;
break ;
default :
Super . ReplicatedEvent ( VarName ) ;
}
}
// ==================================================================
// Feign death triggers:
function PlayHit ( float Damage , Controller InstigatedBy , vector HitLocation , class < DamageType > damageType , vector Momentum , TraceHitInfo HitInfo )
{
2020-11-28 20:04:55 +00:00
if ( damageType != class 'DmgType_Fell' ) // Not from falling!
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( bRagdollFromMomentum && Damage > 2 && VSizeSq ( Momentum ) > 1000000. f && Rand ( 3 ) == 0 ) // Square(1000)
2017-10-20 02:00:49 +00:00
SetFeignDeath ( 3. f + FRand ( ) * 2.5 f ) ; // Randomly knockout a player if hit by a huge force.
2020-11-28 20:04:55 +00:00
else if ( bRagdollFromBackhit && Damage > 20 && VSizeSq ( Momentum ) > 40000. f && ( vector ( Rotation ) Dot Momentum ) > 0. f && Rand ( 4 ) == 0 )
2017-10-20 02:00:49 +00:00
SetFeignDeath ( 2. f + FRand ( ) * 3. f ) ; // Randomly knockout a player if hit from behind.
}
Super . PlayHit ( Damage , InstigatedBy , HitLocation , damageType , Momentum , HitInfo ) ;
}
event Landed ( vector HitNormal , actor FloorActor )
{
local float ExcessSpeed ;
Super . Landed ( HitNormal , FloorActor ) ;
2020-11-28 20:04:55 +00:00
if ( bRagdollFromFalling )
2017-10-20 02:00:49 +00:00
{
ExcessSpeed = Velocity . Z / ( - MaxFallSpeed ) ;
2020-11-28 20:04:55 +00:00
if ( ExcessSpeed > 1.25 ) // Knockout a player after landed from too high.
2017-10-20 02:00:49 +00:00
{
Velocity . Z = 0 ; // Dont go clip through floor now...
Velocity . X *= 0.5 ;
Velocity . Y *= 0.5 ;
SetFeignDeath ( ( 3. f + FRand ( ) ) * ExcessSpeed ) ;
}
}
2020-11-28 20:04:55 +00:00
else if ( BHopAccelSpeed > 0 )
2017-10-20 02:00:49 +00:00
SetTimer ( ( IsLocallyControlled ( ) ? 0.17 : 1. f ) , false , 'ResetBHopAccel' ) ; // Replicating from client to server here because Server Tickrate may screw clients over from executing bunny hopping.
}
// ==================================================================
// Bunny hopping:
2020-11-28 20:04:55 +00:00
function bool DoJump ( bool bUpdating )
2017-10-20 02:00:49 +00:00
{
local float V ;
2020-11-28 20:04:55 +00:00
if ( Super . DoJump ( bUpdating ) )
2017-10-20 02:00:49 +00:00
{
// Accelerate if bunnyhopping.
2020-11-28 20:04:55 +00:00
if ( bHasBunnyHop && VSizeSq2D ( Velocity ) > Square ( GroundSpeed * 0.75 ) )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( BHopAccelSpeed < 20 )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( BHopAccelSpeed == 0 )
2017-10-20 02:00:49 +00:00
BHopSpeedMod = 1. f ;
2020-11-28 20:04:55 +00:00
if ( BHopAccelSpeed < 5 )
2017-10-20 02:00:49 +00:00
V = 1.15 ;
else
{
V = 1.05 ;
AirControl = 0.8 ;
}
BHopSpeedMod *= V ;
GroundSpeed *= V ;
SprintSpeed *= V ;
Velocity . X *= V ;
Velocity . Y *= V ;
++ BHopAccelSpeed ;
}
ClearTimer ( 'ResetBHopAccel' ) ;
}
return true ;
}
return false ;
}
2020-11-28 20:04:55 +00:00
simulated function ResetBHopAccel ( optional bool bSkipRep ) // Set on Landed, or Tick if falling 2D speed is too low.
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( BHopAccelSpeed > 0 )
2017-10-20 02:00:49 +00:00
{
BHopAccelSpeed = 0 ;
AirControl = Default . AirControl ;
GroundSpeed /= BHopSpeedMod ;
UpdateGroundSpeed ( ) ;
2020-11-28 20:04:55 +00:00
if ( WorldInfo . NetMode == NM _Client && ! bSkipRep )
2017-10-20 02:00:49 +00:00
NotifyHasStopped ( ) ;
}
}
function UpdateGroundSpeed ( )
{
local KFInventoryManager InvM ;
local float HealthMod ;
2020-11-28 20:04:55 +00:00
if ( Role < ROLE _Authority )
2017-10-20 02:00:49 +00:00
return ;
InvM = KFInventoryManager ( InvManager ) ;
HealthMod = ( InvM != None ) ? InvM . GetEncumbranceSpeedMod ( ) : 1. f * ( 1. f - LowHealthSpeedPenalty ) ;
2020-11-28 20:04:55 +00:00
if ( BHopAccelSpeed > 0 )
2017-10-20 02:00:49 +00:00
HealthMod *= BHopSpeedMod ;
// First reset to default so multipliers do not stack
GroundSpeed = default . GroundSpeed * HealthMod ;
// reset sprint too, because perk may want to scale it
SprintSpeed = default . SprintSpeed * HealthMod ;
// Ask our perk to set the new ground speed based on weapon type
2020-11-28 20:04:55 +00:00
if ( GetPerk ( ) != none )
2017-10-20 02:00:49 +00:00
{
GetPerk ( ) . ModifySpeed ( GroundSpeed ) ;
GetPerk ( ) . ModifySpeed ( SprintSpeed ) ;
}
}
reliable server function NotifyHasStopped ( )
{
ResetBHopAccel ( true ) ;
}
// ==================================================================
// Feign death (UT3):
2020-11-28 20:04:55 +00:00
simulated function Tick ( float Delta )
2017-10-20 02:00:49 +00:00
{
Super . Tick ( Delta ) ;
2020-11-28 20:04:55 +00:00
if ( bPlayingFeignDeathRecovery )
2017-10-20 02:00:49 +00:00
{
// interpolate Controller yaw to our yaw so that we don't get our rotation snapped around when we get out of feign death
Mesh . PhysicsWeight = FMax ( Mesh . PhysicsWeight - ( Delta * 2. f ) , 0. f ) ;
2020-11-28 20:04:55 +00:00
if ( Mesh . PhysicsWeight <= 0 )
2017-10-20 02:00:49 +00:00
StartFeignDeathRecoveryAnim ( ) ;
}
2020-11-28 20:04:55 +00:00
if ( BHopAccelSpeed > 0 )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( Physics == PHYS _Falling && VSizeSq2D ( Velocity ) < Square ( GroundSpeed * 0.7 ) )
2017-10-20 02:00:49 +00:00
ResetBHopAccel ( true ) ;
}
2020-11-28 20:04:55 +00:00
if ( WorldInfo . NetMode != NM _Client && BackpackWeaponClass != none && ( PlayerOldWeapon == None || PlayerOldWeapon . Instigator == None ) )
2017-10-20 02:00:49 +00:00
{
PlayerOldWeapon = None ;
SetBackpackWeapon ( None ) ;
}
}
function DelayedRagdoll ( )
{
SetFeignDeath ( 2. f + FRand ( ) * 3. f ) ;
}
2020-11-28 20:04:55 +00:00
exec function FeignDeath ( float Time )
2017-10-20 02:00:49 +00:00
{
SetFeignDeath ( Time ) ;
}
2020-11-28 20:04:55 +00:00
function SetFeignDeath ( float Time )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( WorldInfo . NetMode != NM _Client && ! bFeigningDeath && Health > 0 && bCanBecomeRagdoll && NoRagdollChance < 1. f && ( NoRagdollChance == 0. f || FRand ( ) > NoRagdollChance ) )
2017-10-20 02:00:49 +00:00
{
Time = FMax ( 1. f , Time ) ;
PlayFeignDeath ( true ) ;
SetTimer ( Time , false , 'UnsetFeignDeath' ) ;
}
}
function UnsetFeignDeath ( )
{
2020-11-28 20:04:55 +00:00
if ( bFeigningDeath )
2017-10-20 02:00:49 +00:00
PlayFeignDeath ( false ) ;
}
2020-11-28 20:04:55 +00:00
simulated function PlayFeignDeath ( bool bEnable , optional bool bForce , optional bool bTransformMode )
2017-10-20 02:00:49 +00:00
{
local vector FeignLocation , HitLocation , HitNormal , TraceEnd , Impulse ;
local rotator NewRotation ;
local float UnFeignZAdjust ;
2020-11-28 20:04:55 +00:00
if ( Health <= 0 && WorldInfo . NetMode != NM _Client )
2017-10-20 02:00:49 +00:00
return ; // If dead, don't do it.
NotifyOutOfBattery ( ) ; // Stop nightvision on client.
bFeigningDeath = bEnable ;
2020-11-28 20:04:55 +00:00
if ( bEnable )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( bFPLegsAttached )
2017-10-20 02:00:49 +00:00
{
bFPLegsAttached = false ;
DetachComponent ( FPBodyMesh ) ;
}
WeaponAttachmentTemplate = None ;
WeaponAttachmentChanged ( ) ;
bPlayingFeignDeathRecovery = false ;
ClearTimer ( 'OnWakeUpFinished' ) ;
2020-11-28 20:04:55 +00:00
if ( ! bTransformMode )
2017-10-20 02:00:49 +00:00
GotoState ( 'FeigningDeath' ) ;
// if we had some other rigid body thing going on, cancel it
if ( Physics == PHYS _RigidBody )
{
//@note: Falling instead of None so Velocity/Acceleration don't get cleared
setPhysics ( PHYS _Falling ) ;
}
PrepareRagdoll ( ) ;
SetPawnRBChannels ( TRUE ) ;
Mesh . ForceSkelUpdate ( ) ;
// Move into post so that we are hitting physics from last frame, rather than animated from this
SetTickGroup ( TG _PostAsyncWork ) ;
// Turn collision on for skelmeshcomp and off for cylinder
CylinderComponent . SetActorCollision ( false , false ) ;
Mesh . SetActorCollision ( true , true ) ;
Mesh . SetTraceBlocking ( true , true ) ;
Mesh . SetHasPhysicsAssetInstance ( false ) ;
2020-11-28 20:04:55 +00:00
if ( ! InitRagdoll ( ) ) // Ragdoll error!
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( PlayerController ( Controller ) != None )
2017-10-20 02:00:49 +00:00
PlayerController ( Controller ) . ClientMessage ( "Error: InitRagdoll() failed!" ) ;
return ;
}
// Ensure we are always updating kinematic
Mesh . MinDistFactorForKinematicUpdate = 0.0 ;
Mesh . bUpdateKinematicBonesFromAnimation = FALSE ;
// Set all kinematic bodies to the current root velocity, since they may not have been updated during normal animation
// and therefore have zero derived velocity (this happens in 1st person camera mode).
UnFeignZAdjust = VSize ( Velocity ) ;
2020-11-28 20:04:55 +00:00
if ( UnFeignZAdjust > 700. f ) // Limit by a maximum velocity force to prevent from going through walls.
2017-10-20 02:00:49 +00:00
Mesh . SetRBLinearVelocity ( ( Velocity / UnFeignZAdjust ) * 700. f , false ) ;
else Mesh . SetRBLinearVelocity ( Velocity , false ) ;
// reset mesh translation since adjustment code isn't executed on the server
// but the ragdoll code uses the translation so we need them to match up for the
// most accurate simulation
Mesh . SetTranslation ( vect ( 0 , 0 , 1 ) * BaseTranslationOffset ) ;
// we'll use the rigid body collision to check for falling damage
Mesh . ScriptRigidBodyCollisionThreshold = MaxFallSpeed ;
Mesh . SetNotifyRigidBodyCollision ( true ) ;
}
else
{
// fit cylinder collision into location, crouching if necessary
FeignLocation = Location ;
CollisionComponent = CylinderComponent ;
TraceEnd = Location + vect ( 0 , 0 , 1 ) * GetCollisionHeight ( ) ;
2020-11-28 20:04:55 +00:00
if ( Trace ( HitLocation , HitNormal , TraceEnd , Location , true , GetCollisionExtent ( ) ) == None )
2017-10-20 02:00:49 +00:00
{
HitLocation = TraceEnd ;
}
2020-11-28 20:04:55 +00:00
if ( ! SetFeignEndLocation ( HitLocation , FeignLocation ) && WorldInfo . NetMode != NM _Client )
2017-10-20 02:00:49 +00:00
{
UnfeignFailedCount ++ ;
2020-11-28 20:04:55 +00:00
if ( UnFeignfailedCount > 4 || bForce )
2017-10-20 02:00:49 +00:00
{
SetLocation ( PickNearestNode ( ) ) ; // Just teleport to nearest pathnode.
}
else
{
CollisionComponent = Mesh ;
SetLocation ( FeignLocation ) ;
bFeigningDeath = true ;
Impulse = VRand ( ) ;
Impulse . Z = 0.5 ;
Mesh . AddImpulse ( 800.0 * Impulse , Location ) ;
SetTimer ( 1. f , false , 'UnsetFeignDeath' ) ;
return ;
}
}
PreRagdollCollisionComponent = None ;
// Calculate how far we just moved the actor up.
UnFeignZAdjust = Location . Z - FeignLocation . Z ;
// If its positive, move back down by that amount until it hits the floor
if ( UnFeignZAdjust > 0.0 )
{
moveSmooth ( vect ( 0 , 0 , - 1 ) * UnFeignZAdjust ) ;
}
UnfeignFailedCount = 0 ;
bPlayingFeignDeathRecovery = true ;
// Reset collision.
Mesh . SetActorCollision ( true , false ) ;
Mesh . SetTraceBlocking ( true , false ) ;
SetTickGroup ( TG _PreAsyncWork ) ;
// don't need collision events anymore
Mesh . SetNotifyRigidBodyCollision ( false ) ;
// don't allow player to move while animation is in progress
SetPhysics ( PHYS _None ) ;
// physics weight interpolated to 0 in C++, then StartFeignDeathRecoveryAnim() is called
Mesh . PhysicsWeight = 1.0 ;
// force rotation to match the body's direction so the blend to the getup animation looks more natural
NewRotation = Rotation ;
NewRotation . Yaw = rotator ( Mesh . GetBoneAxis ( HeadBoneName , AXIS _X ) ) . Yaw ;
// flip it around if the head is facing upwards, since the animation for that makes the character
// end up facing in the opposite direction that its body is pointing on the ground
// FIXME: generalize this somehow (stick it in the AnimNode, I guess...)
if ( Mesh . GetBoneAxis ( HeadBoneName , AXIS _Y ) . Z < 0.0 )
{
NewRotation . Yaw += 32768 ;
FeignRecoverAnim = 'Getup_B_V1' ;
}
else FeignRecoverAnim = 'Getup_F_V1' ;
// Init wakeup anim.
2020-11-28 20:04:55 +00:00
if ( Mesh . AnimSets . Find ( WakeUpAnimSet ) == - 1 )
2017-10-20 02:00:49 +00:00
Mesh . AnimSets . AddItem ( WakeUpAnimSet ) ;
BodyStanceNodes [ EAS _FullBody ] . bNoNotifies = true ;
BodyStanceNodes [ EAS _FullBody ] . PlayCustomAnim ( FeignRecoverAnim , 0.025 f , , , , true ) ;
SetRotation ( NewRotation ) ;
}
}
final function vector PickNearestNode ( )
{
local NavigationPoint N , Best ;
local float Dist , BestDist ;
foreach WorldInfo . AllNavigationPoints ( class 'NavigationPoint' , N )
{
Dist = VSizeSq ( N . Location - Location ) ;
2020-11-28 20:04:55 +00:00
if ( Best == None || Dist < BestDist )
2017-10-20 02:00:49 +00:00
{
Best = N ;
BestDist = Dist ;
}
}
return ( Best != None ? Best . Location : Location ) ;
}
simulated function bool SetFeignEndLocation ( vector HitLocation , vector FeignLocation )
{
local vector NewDest ;
2020-11-28 20:04:55 +00:00
if ( SetLocation ( HitLocation ) && CheckValidLocation ( FeignLocation ) )
2017-10-20 02:00:49 +00:00
{
return true ;
}
// try crouching
ForceCrouch ( ) ;
2020-11-28 20:04:55 +00:00
if ( SetLocation ( HitLocation ) && CheckValidLocation ( FeignLocation ) )
2017-10-20 02:00:49 +00:00
{
return true ;
}
newdest = HitLocation + GetCollisionRadius ( ) * vect ( 1 , 1 , 0 ) ;
2020-11-28 20:04:55 +00:00
if ( SetLocation ( newdest ) && CheckValidLocation ( FeignLocation ) )
2017-10-20 02:00:49 +00:00
return true ;
newdest = HitLocation + GetCollisionRadius ( ) * vect ( 1 , - 1 , 0 ) ;
2020-11-28 20:04:55 +00:00
if ( SetLocation ( newdest ) && CheckValidLocation ( FeignLocation ) )
2017-10-20 02:00:49 +00:00
return true ;
newdest = HitLocation + GetCollisionRadius ( ) * vect ( - 1 , 1 , 0 ) ;
2020-11-28 20:04:55 +00:00
if ( SetLocation ( newdest ) && CheckValidLocation ( FeignLocation ) )
2017-10-20 02:00:49 +00:00
return true ;
newdest = HitLocation + GetCollisionRadius ( ) * vect ( - 1 , - 1 , 0 ) ;
2020-11-28 20:04:55 +00:00
if ( SetLocation ( newdest ) && CheckValidLocation ( FeignLocation ) )
2017-10-20 02:00:49 +00:00
return true ;
return false ;
}
simulated function bool CheckValidLocation ( vector FeignLocation )
{
local vector HitLocation , HitNormal , DestFinalZ ;
// try trace down to dest
if ( Trace ( HitLocation , HitNormal , Location , FeignLocation , false , vect ( 10 , 10 , 10 ) , , TRACEFLAG _Bullet ) == None )
{
return true ;
}
// try trace straight up, then sideways to final location
DestFinalZ = FeignLocation ;
FeignLocation . Z = Location . Z ;
2020-11-28 20:04:55 +00:00
if ( Trace ( HitLocation , HitNormal , DestFinalZ , FeignLocation , false , vect ( 10 , 10 , 10 ) ) == None &&
Trace ( HitLocation , HitNormal , Location , DestFinalZ , false , vect ( 10 , 10 , 10 ) , , TRACEFLAG _Bullet ) == None )
2017-10-20 02:00:49 +00:00
{
return true ;
}
return false ;
}
simulated function SetPawnRBChannels ( bool bRagdollMode )
{
if ( bRagdollMode )
{
Mesh . SetRBChannel ( RBCC _DeadPawn ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _Default , TRUE ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _Pawn , FALSE ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _Vehicle , TRUE ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _Untitled3 , FALSE ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _BlockingVolume , TRUE ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _DeadPawn , false ) ;
}
else
{
Mesh . SetRBChannel ( RBCC _Pawn ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _Default , FALSE ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _Pawn , FALSE ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _Vehicle , FALSE ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _Untitled3 , TRUE ) ;
Mesh . SetRBCollidesWithChannel ( RBCC _BlockingVolume , FALSE ) ;
}
}
simulated function PlayRagdollDeath ( class < DamageType > DamageType , vector HitLoc )
{
local TraceHitInfo HitInfo ;
local vector HitDirection ;
Mesh . SetHasPhysicsAssetInstance ( false ) ;
Mesh . SetHasPhysicsAssetInstance ( true ) ;
2020-11-28 20:04:55 +00:00
if ( bFPLegsAttached )
2017-10-20 02:00:49 +00:00
{
bFPLegsAttached = false ;
DetachComponent ( FPBodyMesh ) ;
}
// Ensure we are always updating kinematic
Mesh . MinDistFactorForKinematicUpdate = 0.0 ;
PrepareRagdoll ( ) ;
2020-11-28 20:04:55 +00:00
if ( InitRagdoll ( ) )
2017-10-20 02:00:49 +00:00
{
// Switch to a good RigidBody TickGroup to fix projectiles passing through the mesh
// https://udn.unrealengine.com/questions/190581/projectile-touch-not-called.html
//Mesh.SetTickGroup(TG_PostAsyncWork);
SetTickGroup ( TG _PostAsyncWork ) ;
// Allow all ragdoll bodies to collide with all physics objects (ie allow collision with things marked RigidBodyIgnorePawns)
SetPawnRBChannels ( true ) ;
// Call CheckHitInfo to give us a valid BoneName
HitDirection = Normal ( TearOffMomentum ) ;
2020-11-28 19:53:57 +00:00
CheckHitInfo ( HitInfo , Mesh , HitDirection , HitLoc ) ;
2017-10-20 02:00:49 +00:00
// Play ragdoll death animation (bSkipReplication=TRUE)
2020-11-28 20:04:55 +00:00
if ( CanDoSpecialMove ( SM _DeathAnim ) && ClassIsChildOf ( DamageType , class 'KFDamageType' ) )
2017-10-20 02:00:49 +00:00
{
DoSpecialMove ( SM _DeathAnim , TRUE , , , TRUE ) ;
KFSM _DeathAnim ( SpecialMoves [ SM _DeathAnim ] ) . PlayDeathAnimation ( DamageType , HitDirection , HitInfo . BoneName ) ;
}
else
{
StopAllAnimations ( ) ; // stops non-RBbones from animating (fingers)
}
}
}
simulated function StartFeignDeathRecoveryAnim ( )
{
2020-11-28 20:04:55 +00:00
if ( FPBodyMesh != None && ! bFPLegsAttached && bOnFirstPerson && Class 'ExtPlayerController' . Default . bShowFPLegs )
2017-10-20 02:00:49 +00:00
{
bFPLegsAttached = true ;
AttachComponent ( FPBodyMesh ) ;
}
bPlayingFeignDeathRecovery = false ;
// we're done with the ragdoll, so get rid of it
Mesh . PhysicsWeight = 0. f ;
Mesh . PhysicsAssetInstance . SetAllBodiesFixed ( TRUE ) ;
Mesh . PhysicsAssetInstance . SetFullAnimWeightBonesFixed ( FALSE , Mesh ) ;
SetPawnRBChannels ( FALSE ) ;
Mesh . bUpdateKinematicBonesFromAnimation = TRUE ;
// Turn collision on for cylinder and off for skelmeshcomp
CylinderComponent . SetActorCollision ( true , true ) ;
BodyStanceNodes [ EAS _FullBody ] . PlayCustomAnim ( FeignRecoverAnim , 1.2 f , , , , true ) ;
SetTimer ( 1.7 f , false , 'OnWakeUpFinished' ) ;
}
function bool CanBeRedeemed ( )
{
return true ;
}
simulated function OnWakeUpFinished ( ) ;
function AddDefaultInventory ( )
{
2020-11-28 19:53:57 +00:00
local KFPerk MyPerk ;
2017-10-20 02:00:49 +00:00
2020-11-28 19:53:57 +00:00
MyPerk = GetPerk ( ) ;
2020-11-28 20:04:55 +00:00
if ( MyPerk != none )
2020-11-28 19:53:57 +00:00
MyPerk . AddDefaultInventory ( self ) ;
2017-10-20 02:00:49 +00:00
Super ( KFPawn ) . AddDefaultInventory ( ) ;
}
simulated event FellOutOfWorld ( class < DamageType > dmgType )
{
2020-11-28 20:04:55 +00:00
if ( Role == ROLE _Authority && NextRedeemTimer < WorldInfo . TimeSeconds ) // Make sure to not to spam deathmessages while ghosting.
2017-10-20 02:00:49 +00:00
Super . FellOutOfWorld ( dmgType ) ;
}
simulated event OutsideWorldBounds ( )
{
2020-11-28 20:04:55 +00:00
if ( Role == ROLE _Authority && NextRedeemTimer < WorldInfo . TimeSeconds )
2017-10-20 02:00:49 +00:00
Super . OutsideWorldBounds ( ) ;
}
simulated function KFCharacterInfoBase GetCharacterInfo ( )
{
2020-11-28 20:04:55 +00:00
if ( ExtPlayerReplicationInfo ( PlayerReplicationInfo ) != None )
2017-10-20 02:00:49 +00:00
return ExtPlayerReplicationInfo ( PlayerReplicationInfo ) . GetSelectedArch ( ) ;
return Super . GetCharacterInfo ( ) ;
}
2020-11-28 20:04:55 +00:00
simulated function SetCharacterArch ( KFCharacterInfoBase Info , optional bool bForce )
2017-10-20 02:00:49 +00:00
{
local KFPlayerReplicationInfo KFPRI ;
2020-11-28 20:04:55 +00:00
KFPRI = KFPlayerReplicationInfo ( PlayerReplicationInfo ) ;
2017-10-20 02:00:49 +00:00
if ( Info != CharacterArch || bForce )
{
// Set Family Info
CharacterArch = Info ;
2020-11-28 20:04:55 +00:00
CharacterArch . SetCharacterFromArch ( self , KFPRI ) ;
class 'ExtCharacterInfo' . Static . SetCharacterMeshFromArch ( KFCharacterInfo _Human ( CharacterArch ) , self , KFPRI ) ;
class 'ExtCharacterInfo' . Static . SetFirstPersonArmsFromArch ( KFCharacterInfo _Human ( CharacterArch ) , self , KFPRI ) ;
2017-10-20 02:00:49 +00:00
SetCharacterAnimationInfo ( ) ;
// Sounds
SoundGroupArch = Info . SoundGroupArch ;
if ( WorldInfo . NetMode != NM _DedicatedServer )
{
// refresh weapon attachment (attachment bone may have changed)
if ( WeaponAttachmentTemplate != None )
{
WeaponAttachmentChanged ( true ) ;
}
}
2020-11-28 20:04:55 +00:00
if ( WorldInfo . NetMode != NM _DedicatedServer )
2017-10-20 02:00:49 +00:00
{
// Attach/Reattach flashlight components when mesh is set
2020-11-28 20:04:55 +00:00
if ( Flashlight == None && FlashLightTemplate != None )
2017-10-20 02:00:49 +00:00
{
Flashlight = new ( self ) Class 'KFFlashlightAttachment' ( FlashLightTemplate ) ;
}
2020-11-28 20:04:55 +00:00
if ( FlashLight != None )
2017-10-20 02:00:49 +00:00
{
Flashlight . AttachFlashlight ( Mesh ) ;
}
}
2020-11-28 20:04:55 +00:00
if ( CharacterArch != none )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( CharacterArch . VoiceGroupArchName != "" )
2017-10-20 02:00:49 +00:00
VoiceGroupArch = class < KFPawnVoiceGroup > ( class 'ExtCharacterInfo' . Static . SafeLoadObject ( CharacterArch . VoiceGroupArchName , class 'Class' ) ) ;
}
}
}
simulated state FeigningDeath
{
ignores FaceRotation , SetMovementPhysics ;
function SetSprinting ( bool bNewSprintStatus )
{
bIsSprinting = false ;
}
2020-11-28 20:04:55 +00:00
simulated event RigidBodyCollision ( PrimitiveComponent HitComponent , PrimitiveComponent OtherComponent , const out CollisionImpactData RigidCollisionData , int ContactIndex )
2017-10-20 02:00:49 +00:00
{
// only check fall damage for Z axis collisions
if ( Abs ( RigidCollisionData . ContactInfos [ 0 ] . ContactNormal . Z ) > 0.5 )
{
Velocity = Mesh . GetRootBodyInstance ( ) . PreviousVelocity ;
TakeFallingDamage ( ) ;
// zero out the z velocity on the body now so that we don't get stacked collisions
Velocity . Z = 0.0 ;
Mesh . SetRBLinearVelocity ( Velocity , false ) ;
Mesh . GetRootBodyInstance ( ) . PreviousVelocity = Velocity ;
Mesh . GetRootBodyInstance ( ) . Velocity = Velocity ;
}
}
simulated event bool CanDoSpecialMove ( ESpecialMove AMove , optional bool bForceCheck )
{
return ( bForceCheck ? Global . CanDoSpecialMove ( AMove , bForceCheck ) : false ) ;
}
2017-10-20 02:12:29 +00:00
function bool CanBeGrabbed ( KFPawn GrabbingPawn , optional bool bIgnoreFalling , optional bool bAllowSameTeamGrab )
2017-10-20 02:00:49 +00:00
{
return false ;
}
simulated function OnWakeUpFinished ( )
{
if ( Physics == PHYS _RigidBody )
setPhysics ( PHYS _Falling ) ;
Mesh . MinDistFactorForKinematicUpdate = default . Mesh . MinDistFactorForKinematicUpdate ;
GotoState ( 'Auto' ) ;
}
event bool EncroachingOn ( Actor Other )
{
// don't abort moves in ragdoll
return false ;
}
simulated function bool CanThrowWeapon ( )
{
return false ;
}
simulated function Tick ( float DeltaTime )
{
local rotator NewRotation ;
2020-11-28 20:04:55 +00:00
if ( bPlayingFeignDeathRecovery )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( PlayerController ( Controller ) != None )
2017-10-20 02:00:49 +00:00
{
// interpolate Controller yaw to our yaw so that we don't get our rotation snapped around when we get out of feign death
NewRotation = Controller . Rotation ;
NewRotation . Yaw = RInterpTo ( NewRotation , Rotation , DeltaTime , 2.0 ) . Yaw ;
Controller . SetRotation ( NewRotation ) ;
}
Mesh . PhysicsWeight = FMax ( Mesh . PhysicsWeight - ( DeltaTime * 2. f ) , 0. f ) ;
2020-11-28 20:04:55 +00:00
if ( Mesh . PhysicsWeight <= 0 )
2017-10-20 02:00:49 +00:00
StartFeignDeathRecoveryAnim ( ) ;
}
}
simulated event BeginState ( name PreviousStateName )
{
local KFWeapon UTWeap ;
// Abort current special move
2020-11-28 20:04:55 +00:00
if ( IsDoingSpecialMove ( ) )
2017-10-20 02:00:49 +00:00
SpecialMoveHandler . EndSpecialMove ( ) ;
bCanPickupInventory = false ;
StopFiring ( ) ;
bNoWeaponFiring = true ;
UTWeap = KFWeapon ( Weapon ) ;
if ( UTWeap != None )
{
UTWeap . SetIronSights ( false ) ;
UTWeap . PlayWeaponPutDown ( 0.5 f ) ;
}
2020-11-28 20:04:55 +00:00
if ( WorldInfo . NetMode != NM _Client )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( ExtPlayerController ( Controller ) != None )
2017-10-20 02:00:49 +00:00
ExtPlayerController ( Controller ) . EnterRagdollMode ( true ) ;
2020-11-28 20:04:55 +00:00
else if ( Controller != None )
2017-10-20 02:00:49 +00:00
Controller . ReplicatedEvent ( 'RagdollMove' ) ;
}
}
simulated function WeaponAttachmentChanged ( optional bool bForceReattach )
{
// Keep weapon hidden!
if ( WeaponAttachment != None )
{
WeaponAttachment . DetachFrom ( self ) ;
WeaponAttachment . Destroy ( ) ;
WeaponAttachment = None ;
}
}
function bool CanBeRedeemed ( )
{
2020-11-28 20:04:55 +00:00
if ( bFeigningDeath )
2017-10-20 02:00:49 +00:00
PlayFeignDeath ( false , true ) ;
NextRedeemTimer = WorldInfo . TimeSeconds + 0.25 ;
return false ;
}
simulated function EndState ( name NextStateName )
{
local KFWeapon UTWeap ;
Mesh . AnimSets . RemoveItem ( WakeUpAnimSet ) ;
BodyStanceNodes [ EAS _FullBody ] . bNoNotifies = false ;
2020-11-28 20:04:55 +00:00
if ( NextStateName != 'Dying' )
2017-10-20 02:00:49 +00:00
{
bNoWeaponFiring = default . bNoWeaponFiring ;
bCanPickupInventory = default . bCanPickupInventory ;
UTWeap = KFWeapon ( Weapon ) ;
2020-11-28 20:04:55 +00:00
if ( UTWeap != None )
2017-10-20 02:00:49 +00:00
{
WeaponAttachmentTemplate = UTWeap . AttachmentArchetype ;
UTWeap . PlayWeaponEquip ( 0.5 f ) ;
}
Global . SetMovementPhysics ( ) ;
bPlayingFeignDeathRecovery = false ;
2020-11-28 20:04:55 +00:00
if ( WorldInfo . NetMode != NM _Client )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( ExtPlayerController ( Controller ) != None )
2017-10-20 02:00:49 +00:00
ExtPlayerController ( Controller ) . EnterRagdollMode ( false ) ;
2020-11-28 20:04:55 +00:00
else if ( Controller != None )
2017-10-20 02:00:49 +00:00
Controller . ReplicatedEvent ( 'EndRagdollMove' ) ;
}
Global . WeaponAttachmentChanged ( ) ;
}
}
}
// VS mode.
state TransformZed extends FeigningDeath
{
Ignores FaceRotation , SetMovementPhysics , UnsetFeignDeath , Tick , TakeDamage , Died ;
simulated event BeginState ( name PreviousStateName )
{
bCanPickupInventory = false ;
bNoWeaponFiring = true ;
2020-11-28 20:04:55 +00:00
if ( ExtPlayerController ( Controller ) != None )
2017-10-20 02:00:49 +00:00
ExtPlayerController ( Controller ) . EnterRagdollMode ( true ) ;
2020-11-28 20:04:55 +00:00
else if ( Controller != None )
2017-10-20 02:00:49 +00:00
Controller . ReplicatedEvent ( 'RagdollMove' ) ;
SetTimer ( 2 , false , 'TransformToZed' ) ;
}
simulated function EndState ( name NextStateName )
{
}
function bool CanBeRedeemed ( )
{
return false ;
}
function TransformToZed ( )
{
local VS _ZedRecentZed Z ;
2020-11-28 20:04:55 +00:00
if ( Controller == None )
2017-10-20 02:00:49 +00:00
{
Destroy ( ) ;
return ;
}
PlayFeignDeath ( false ) ;
SetCollision ( false , false ) ;
Z = Spawn ( class 'VS_ZedRecentZed' , , , Location , Rotation , , true ) ;
2020-11-28 20:04:55 +00:00
if ( Z == None )
2017-10-20 02:00:49 +00:00
{
Super . Died ( None , Class 'DamageType' , Location ) ;
return ;
}
else
{
Z . SetPhysics ( PHYS _Falling ) ;
Z . LastStartTime = WorldInfo . TimeSeconds ;
Controller . Pawn = None ;
Controller . Possess ( Z , false ) ;
WorldInfo . Game . ChangeTeam ( Controller , 255 , true ) ;
WorldInfo . Game . SetPlayerDefaults ( Z ) ;
2020-11-28 20:04:55 +00:00
if ( ExtPlayerController ( Controller ) != None )
2017-10-20 02:00:49 +00:00
Controller . GoToState ( 'RagdollMove' ) ;
2020-11-28 20:04:55 +00:00
else if ( Controller != None )
2017-10-20 02:00:49 +00:00
Controller . ReplicatedEvent ( 'RagdollMove' ) ;
Z . WakeUp ( ) ;
2020-11-28 20:04:55 +00:00
if ( ExtPlayerReplicationInfo ( Controller . PlayerReplicationInfo ) != None )
2017-10-20 02:00:49 +00:00
{
ExtPlayerReplicationInfo ( Controller . PlayerReplicationInfo ) . PlayerHealth = Min ( Z . Health , 255 ) ;
2020-11-28 20:04:55 +00:00
ExtPlayerReplicationInfo ( Controller . PlayerReplicationInfo ) . PlayerHealthPercent = FloatToByte ( float ( Z . Health ) / float ( Z . HealthMax ) ) ;
2017-10-20 02:00:49 +00:00
}
}
Controller = None ;
Destroy ( ) ;
}
}
simulated final function InitFPLegs ( )
{
local int i ;
bFPLegsInit = true ;
FPBodyMesh . AnimSets = CharacterArch . AnimSets ;
FPBodyMesh . SetAnimTreeTemplate ( CharacterArch . AnimTreeTemplate ) ;
FPBodyMesh . SetSkeletalMesh ( Mesh . SkeletalMesh ) ;
2020-11-28 19:53:57 +00:00
FPBodyMesh . SetActorCollision ( false , false ) ;
2017-10-20 02:00:49 +00:00
FPBodyMesh . SetNotifyRigidBodyCollision ( false ) ;
FPBodyMesh . SetTraceBlocking ( false , false ) ;
2020-11-28 20:04:55 +00:00
for ( i = 0 ; i < Mesh . Materials . length ; i ++ )
2017-10-20 02:00:49 +00:00
FPBodyMesh . SetMaterial ( i , Mesh . Materials [ i ] ) ;
FPBodyMesh . HideBoneByName ( 'neck' , PBO _None ) ;
2020-11-28 19:53:57 +00:00
FPBodyMesh . HideBoneByName ( 'Spine2' , PBO _None ) ;
FPBodyMesh . HideBoneByName ( 'RightShoulder' , PBO _None ) ;
FPBodyMesh . HideBoneByName ( 'LeftShoulder' , PBO _None ) ;
2017-10-20 02:00:49 +00:00
}
// ForrestMarkX's third person backpack weapon and first person legs:
simulated function SetMeshVisibility ( bool bVisible )
{
Super . SetMeshVisibility ( bVisible ) ;
2020-11-28 20:04:55 +00:00
if ( Health > 0 )
2017-10-20 02:00:49 +00:00
{
bOnFirstPerson = ! bVisible ;
2020-11-28 20:04:55 +00:00
if ( AttachedBackItem != None )
2017-10-20 02:00:49 +00:00
AttachedBackItem . SetHidden ( bOnFirstPerson ) ;
UpdateFPLegs ( ) ;
}
}
simulated final function UpdateFPLegs ( )
{
2020-11-28 20:04:55 +00:00
if ( FPBodyMesh != None )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( ! bFPLegsAttached && Physics != PHYS _RigidBody && bOnFirstPerson && Class 'ExtPlayerController' . Default . bShowFPLegs )
2017-10-20 02:00:49 +00:00
{
bFPLegsAttached = true ;
AttachComponent ( FPBodyMesh ) ;
2020-11-28 20:04:55 +00:00
if ( ! bFPLegsInit && CharacterArch != None )
2017-10-20 02:00:49 +00:00
InitFPLegs ( ) ;
}
FPBodyMesh . SetHidden ( ! bOnFirstPerson || ! Class 'ExtPlayerController' . Default . bShowFPLegs ) ;
}
}
simulated event PostInitAnimTree ( SkeletalMeshComponent SkelComp )
{
2020-11-28 20:04:55 +00:00
if ( SkelComp == Mesh ) // Do not allow first person legs eat up animation slots.
2017-10-20 02:00:49 +00:00
Super . PostInitAnimTree ( SkelComp ) ;
}
2020-11-28 20:04:55 +00:00
simulated final function SetBackpackWeapon ( class < KFWeapon > WC )
2017-10-20 02:00:49 +00:00
{
local KFCharacterInfo _Human MyCharacter ;
local Rotator MyRot ;
local Vector MyPos ;
local name WM , B ;
2017-10-20 07:02:53 +00:00
local int i ;
2017-10-20 02:00:49 +00:00
BackpackWeaponClass = WC ;
2020-11-28 20:04:55 +00:00
if ( WorldInfo . NetMode == NM _DedicatedServer )
2017-10-20 02:00:49 +00:00
return ;
2020-11-28 20:04:55 +00:00
if ( WC != None )
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if ( AttachedBackItem == None )
2017-10-20 02:00:49 +00:00
{
AttachedBackItem = new ( Self ) class 'SkeletalMeshComponent' ;
AttachedBackItem . SetHidden ( false ) ;
AttachedBackItem . SetLightingChannels ( PawnLightingChannel ) ;
}
AttachedBackItem . SetSkeletalMesh ( WC . Default . AttachmentArchetype . SkelMesh ) ;
2020-11-28 20:04:55 +00:00
for ( i = 0 ; i < WC . Default . AttachmentArchetype . SkelMesh . Materials . length ; i ++ )
2017-10-20 07:02:53 +00:00
{
AttachedBackItem . SetMaterial ( i , WC . Default . AttachmentArchetype . SkelMesh . Materials [ i ] ) ;
}
2017-10-20 02:00:49 +00:00
Mesh . DetachComponent ( AttachedBackItem ) ;
MyCharacter = KFPlayerReplicationInfo ( PlayerReplicationInfo ) . CharacterArchetypes [ KFPlayerReplicationInfo ( PlayerReplicationInfo ) . RepCustomizationInfo . CharacterIndex ] ;
WM = WC . Default . AttachmentArchetype . SkelMesh . Name ;
2017-10-20 07:02:53 +00:00
2020-11-28 20:04:55 +00:00
if ( ClassIsChildOf ( WC , class 'KFWeap_Edged_Knife' ) )
2017-10-20 07:02:53 +00:00
{
MyPos = vect ( 0 , 0 , 10 ) ;
MyRot = rot ( - 16384 , - 8192 , 0 ) ;
B = 'LeftUpLeg' ;
}
2020-11-28 20:04:55 +00:00
else if ( class < KFWeap _Welder > ( WC ) != none || class < KFWeap _Healer _Syringe > ( WC ) != none || class < KFWeap _Pistol _Medic > ( WC ) != none || class < KFWeap _SMG _Medic > ( WC ) != none || ClassIsChildOf ( WC , class 'KFWeap_PistolBase' ) || ClassIsChildOf ( WC , class 'KFWeap_SMGBase' ) || ClassIsChildOf ( WC , class 'KFWeap_ThrownBase' ) )
2017-10-20 02:00:49 +00:00
{
MyPos = vect ( 0 , 0 , 10 ) ;
MyRot = rot ( 0 , 0 , 16384 ) ;
B = 'LeftUpLeg' ;
2017-10-20 07:02:53 +00:00
}
2020-11-28 20:04:55 +00:00
else if ( ClassIsChildOf ( WC , class 'KFWeap_MeleeBase' ) )
2017-10-20 07:02:53 +00:00
{
2017-10-20 02:00:49 +00:00
MyPos = vect ( - 5 , 15 , 0 ) ;
MyRot = rot ( 0 , 0 , 0 ) ;
2020-11-28 20:04:55 +00:00
if ( class < KFWeap _Edged _Katana > ( WC ) != none || class < KFWeap _Edged _Zweihander > ( WC ) != none )
2017-10-20 02:00:49 +00:00
MyPos . Z = - 20 ;
2017-10-20 07:02:53 +00:00
2017-10-20 02:00:49 +00:00
B = 'Spine' ;
2017-10-20 07:02:53 +00:00
}
else
{
2017-10-20 02:00:49 +00:00
MyPos = vect ( - 18.5 , 16.5 , - 18 ) ;
MyRot = rot ( 0 , 0 , 0 ) ;
2020-11-28 20:04:55 +00:00
if ( MyCharacter == KFCharacterInfo _Human 'CHR_Playable_ARCH.chr_DJSkully_archetype' )
2017-10-20 02:00:49 +00:00
MyRot . Roll = 8192 ;
2017-10-20 07:02:53 +00:00
2020-11-28 20:04:55 +00:00
switch ( WM )
2017-10-20 02:00:49 +00:00
{
case 'Wep_3rdP_MB500_Rig' :
MyPos . X = - 45 ;
break ;
case 'Wep_3rdP_M4Shotgun_Rig' :
MyPos . X = - 25 ;
break ;
case 'Wep_3rdP_SawBlade_Rig' :
MyPos . X = - 75 ;
MyRot . Roll = 16384 ;
break ;
2017-10-20 07:02:53 +00:00
case 'Wep_3rdP_RPG7_Rig' :
MyPos . X = 10 ;
break ;
2017-10-20 02:00:49 +00:00
}
2017-10-20 07:02:53 +00:00
2017-10-20 02:00:49 +00:00
B = 'Spine2' ;
}
AttachedBackItem . SetTranslation ( MyPos ) ;
AttachedBackItem . SetRotation ( MyRot ) ;
Mesh . AttachComponent ( AttachedBackItem , B ) ;
AttachedBackItem . SetHidden ( bOnFirstPerson ) ;
}
2020-11-28 20:04:55 +00:00
else if ( AttachedBackItem != None )
2017-10-20 02:00:49 +00:00
AttachedBackItem . SetHidden ( true ) ;
}
simulated function PlayDying ( class < DamageType > DamageType , vector HitLoc )
{
FPBodyMesh . SetHidden ( true ) ;
2020-11-28 20:04:55 +00:00
if ( AttachedBackItem != None )
2017-10-20 02:00:49 +00:00
AttachedBackItem . SetHidden ( true ) ;
Super . PlayDying ( DamageType , HitLoc ) ;
}
simulated function SetCharacterAnimationInfo ( )
{
Super . SetCharacterAnimationInfo ( ) ;
2020-11-28 20:04:55 +00:00
if ( ! bFPLegsInit && bFPLegsAttached )
2017-10-20 02:00:49 +00:00
InitFPLegs ( ) ;
}
simulated function SetMeshLightingChannels ( LightingChannelContainer NewLightingChannels )
{
2020-11-28 19:53:57 +00:00
Super . SetMeshLightingChannels ( NewLightingChannels ) ;
2017-10-20 02:00:49 +00:00
if ( AttachedBackItem != none )
2020-11-28 19:53:57 +00:00
AttachedBackItem . SetLightingChannels ( NewLightingChannels ) ;
2017-10-20 02:00:49 +00:00
FPBodyMesh . SetLightingChannels ( NewLightingChannels ) ;
}
simulated function PlayWeaponSwitch ( Weapon OldWeapon , Weapon NewWeapon )
{
2020-11-28 19:53:57 +00:00
Super . PlayWeaponSwitch ( OldWeapon , NewWeapon ) ;
2017-10-20 02:00:49 +00:00
2020-11-28 20:04:55 +00:00
if ( WorldInfo . NetMode != NM _Client )
2017-10-20 02:00:49 +00:00
{
PlayerOldWeapon = KFWeapon ( OldWeapon ) ;
SetBackpackWeapon ( PlayerOldWeapon != None ? PlayerOldWeapon . Class : None ) ;
}
}
2017-10-20 07:02:53 +00:00
simulated function UpdateHealingSpeedBoostMod ( ExtPlayerController Healer )
{
local Ext _PerkFieldMedic MedPerk ;
MedPerk = GetMedicPerk ( Healer ) ;
2020-11-28 20:04:55 +00:00
if ( MedPerk == None )
2017-10-20 07:02:53 +00:00
return ;
2020-11-28 20:04:55 +00:00
HealingSpeedBoostMod = Min ( HealingSpeedBoostMod + MedPerk . GetHealingSpeedBoost ( ) , MedPerk . GetMaxHealingSpeedBoost ( ) ) ;
SetTimer ( MedPerk . GetHealingSpeedBoostDuration ( ) , , nameOf ( ResetHealingSpeedBoost ) ) ;
2017-10-20 07:02:53 +00:00
UpdateGroundSpeed ( ) ;
}
simulated function float GetHealingSpeedModifier ( )
{
return 1 + ( float ( HealingSpeedBoostMod ) / 100 ) ;
}
simulated function ResetHealingSpeedBoost ( )
{
HealingSpeedBoostMod = 0 ;
UpdateGroundSpeed ( ) ;
2020-11-28 20:04:55 +00:00
if ( IsTimerActive ( nameOf ( ResetHealingSpeedBoost ) ) )
ClearTimer ( nameOf ( ResetHealingSpeedBoost ) ) ;
2017-10-20 07:02:53 +00:00
}
simulated function UpdateHealingDamageBoostMod ( ExtPlayerController Healer )
{
local Ext _PerkFieldMedic MedPerk ;
MedPerk = GetMedicPerk ( Healer ) ;
2020-11-28 20:04:55 +00:00
if ( MedPerk == None )
2017-10-20 07:02:53 +00:00
return ;
2020-11-28 20:04:55 +00:00
HealingDamageBoostMod = Min ( HealingDamageBoostMod + MedPerk . GetHealingDamageBoost ( ) , MedPerk . GetMaxHealingDamageBoost ( ) ) ;
SetTimer ( MedPerk . GetHealingDamageBoostDuration ( ) , , nameOf ( ResetHealingDamageBoost ) ) ;
2017-10-20 07:02:53 +00:00
}
simulated function float GetHealingDamageBoostModifier ( )
{
return 1 + ( float ( HealingDamageBoostMod ) / 100 ) ;
}
simulated function ResetHealingDamageBoost ( )
{
HealingDamageBoostMod = 0 ;
2020-11-28 20:04:55 +00:00
if ( IsTimerActive ( nameOf ( ResetHealingDamageBoost ) ) )
ClearTimer ( nameOf ( ResetHealingDamageBoost ) ) ;
2017-10-20 07:02:53 +00:00
}
simulated function UpdateHealingShieldMod ( ExtPlayerController Healer )
{
local Ext _PerkFieldMedic MedPerk ;
MedPerk = GetMedicPerk ( Healer ) ;
2020-11-28 20:04:55 +00:00
if ( MedPerk == None )
2017-10-20 07:02:53 +00:00
return ;
2020-11-28 20:04:55 +00:00
HealingShieldMod = Min ( HealingShieldMod + MedPerk . GetHealingShield ( ) , MedPerk . GetMaxHealingShield ( ) ) ;
SetTimer ( MedPerk . GetHealingShieldDuration ( ) , , nameOf ( ResetHealingShield ) ) ;
2017-10-20 07:02:53 +00:00
}
simulated function float GetHealingShieldModifier ( )
{
return 1 - ( float ( HealingShieldMod ) / 100 ) ;
}
simulated function ResetHealingShield ( )
{
HealingShieldMod = 0 ;
2020-11-28 20:04:55 +00:00
if ( IsTimerActive ( nameOf ( ResetHealingShield ) ) )
ClearTimer ( nameOf ( ResetHealingShield ) ) ;
2017-10-20 07:02:53 +00:00
}
function SacrificeExplode ( )
{
local Ext _PerkDemolition DemoPerk ;
Super . SacrificeExplode ( ) ;
DemoPerk = Ext _PerkDemolition ( ExtPlayerController ( Controller ) . ActivePerkManager . CurrentPerk ) ;
2020-11-28 20:04:55 +00:00
if ( DemoPerk != none )
2017-10-20 07:02:53 +00:00
DemoPerk . bUsedSacrifice = true ;
}
simulated function Ext _PerkFieldMedic GetMedicPerk ( ExtPlayerController Healer )
{
local Ext _PerkFieldMedic MedPerk ;
MedPerk = Ext _PerkFieldMedic ( ExtPlayerController ( Controller ) . ActivePerkManager . CurrentPerk ) ;
2020-11-28 20:04:55 +00:00
if ( MedPerk != None )
2017-10-20 07:02:53 +00:00
return MedPerk ;
return None ;
}
2017-10-20 02:00:49 +00:00
defaultproperties
{
KnockbackResist = 1
// Ragdoll mode:
bReplicateRigidBodyLocation = true
bCanBecomeRagdoll = true
InventoryManagerClass = class 'ExtInventoryManager'
WakeUpAnimSet = AnimSet 'ZED_Clot_Anim.Alpha_Clot_Master'
2017-10-20 07:02:53 +00:00
Begin Object Name = SpecialMoveHandler _0
SpecialMoveClasses ( SM _Emote ) = class 'ServerExt.ExtSM_Player_Emote'
End Object
2017-10-20 02:00:49 +00:00
DefaultInventory . Empty ( )
2017-10-20 07:02:53 +00:00
DefaultInventory . Add ( class 'ExtWeap_Pistol_9mm' )
2020-06-21 19:06:43 +00:00
// DefaultInventory.Add(class'KFWeap_Pistol_9mm')
2017-10-20 02:00:49 +00:00
DefaultInventory . Add ( class 'KFWeap_Healer_Syringe' )
DefaultInventory . Add ( class 'KFWeap_Welder' )
DefaultInventory . Add ( class 'KFInventory_Money' )
Begin Object Class = SkeletalMeshComponent Name = FP _BodyComp
MinDistFactorForKinematicUpdate = 0.0
bSkipAllUpdateWhenPhysicsAsleep = True
bIgnoreControllersWhenNotRendered = True
bHasPhysicsAssetInstance = False
bUpdateKinematicBonesFromAnimation = False
bPerBoneMotionBlur = True
bOverrideAttachmentOwnerVisibility = True
bChartDistanceFactor = True
DepthPriorityGroup = SDPG _Foreground
RBChannel = RBCC _Pawn
RBDominanceGroup = 20
HiddenGame = True
bOnlyOwnerSee = True
bAcceptsDynamicDecals = True
bUseOnePassLightingOnTranslucency = True
Translation = ( X = - 65.876999 , Y = 0.900000 , Z = - 95.500000 )
Scale = 1.210000
ScriptRigidBodyCollisionThreshold = 200.000000
PerObjectShadowCullDistance = 4000.000000
bAllowPerObjectShadows = True
bAllowPerObjectShadowBatching = True
End Object
FPBodyMesh = FP _BodyComp
2020-06-23 20:38:04 +00:00
}
simulated function bool CanBeHealed ( )
{
return true ;
2017-10-20 02:00:49 +00:00
}