KF2-Server-Extension/ServerExt/Classes/ExtHumanPawn.uc

1359 lines
38 KiB
Ucode
Raw Normal View History

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;
var byte HealingShieldMod,HealingSpeedBoostMod,HealingDamageBoostMod;
2017-10-20 02:00:49 +00:00
replication
{
2020-11-28 20:12:58 +00:00
if (true)
2017-10-20 02:00:49 +00:00
bFeigningDeath,RepRegenHP,BackpackWeaponClass;
2020-11-28 20:12:58 +00:00
if (bNetOwner)
2017-10-20 02:00:49 +00:00
bHasBunnyHop;
2020-11-28 20:12:58 +00:00
if (bNetDirty)
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:12:58 +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:12:58 +00:00
if (WorldInfo.NetMode!=NM_Client && PlayerReplicationInfo!=None)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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:12:58 +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:12:58 +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:12:58 +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)
2020-11-28 20:12:58 +00:00
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:12:58 +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 21:54:57 +00:00
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 21:54:57 +00:00
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;
local ExtPlayerReplicationInfo EPRI;
local Ext_PerkBase InstigatorExtPerk;
2017-10-20 02:00:49 +00:00
InstigatorPC = ExtPlayerController(Healer);
InstigatorPerk = InstigatorPC.GetPerk();
2020-11-28 20:12:58 +00:00
if (InstigatorPerk != None && bCanRepairArmor)
2020-11-28 20:04:55 +00:00
bRepairedArmor = InstigatorPC.GetPerk().RepairArmor(self);
EPRI = ExtPlayerReplicationInfo(InstigatorPC.PlayerReplicationInfo);
2020-11-28 20:12:58 +00:00
if (EPRI != none)
2017-10-20 02:00:49 +00:00
{
InstigatorExtPerk = ExtPlayerController(Controller).ActivePerkManager.CurrentPerk;
2020-11-28 20:12:58 +00:00
if (InstigatorExtPerk != none && Ext_PerkFieldMedic(InstigatorExtPerk) != none)
{
2020-11-28 20:12:58 +00:00
if (Ext_PerkFieldMedic(InstigatorExtPerk).bHealingBoost)
UpdateHealingSpeedBoostMod(InstigatorPC);
2020-11-28 20:12:58 +00:00
if (Ext_PerkFieldMedic(InstigatorExtPerk).bHealingDamageBoost)
UpdateHealingDamageBoostMod(InstigatorPC);
2020-11-28 20:12:58 +00:00
if (Ext_PerkFieldMedic(InstigatorExtPerk).bHealingShield)
UpdateHealingShieldMod(InstigatorPC);
}
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:12:58 +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:12:58 +00:00
if (KFDT != none && KFDT.default.bNoPain)
2020-11-28 20:04:55 +00:00
PlayHeal(KFDT);
2017-10-20 02:00:49 +00:00
2020-11-28 20:12:58 +00:00
if (Role == ROLE_Authority)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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:12:58 +00:00
if (InstigatorPerk != none)
2020-11-28 20:04:55 +00:00
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:12:58 +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:12:58 +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:12:58 +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;
2020-11-28 20:12:58 +00:00
if (InstigatorPRI!=None)
2017-10-20 02:00:49 +00:00
InstigatorPRI.AddDosh(Max(DoshEarned, 0), true);
2020-11-28 20:12:58 +00:00
if (InstigatorPC!=None)
2020-11-28 20:04:55 +00:00
InstigatorPC.AddHealPoints(UsedHealAmount);
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:12:58 +00:00
if (Healer.bIsPlayer)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +00:00
if (Healer != Controller)
2020-11-28 19:53:57 +00:00
{
2020-11-28 20:12:58 +00:00
if (InstigatorPC!=None)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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:12:58 +00:00
if (KFPC!=None)
2020-11-28 20:04:55 +00:00
KFPC.ReceiveLocalizedMessage(class'KFLocalMessage_Game', GMT_HealedBy, Healer.PlayerReplicationInfo);
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:12:58 +00:00
else if (bMessageHealer && InstigatorPC!=None)
2020-11-28 20:04:55 +00:00
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:12:58 +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:12:58 +00:00
for (i = 0; i < DamageOverTimeArray.Length; ++i)
2020-11-28 19:53:57 +00:00
{
2020-11-28 20:12:58 +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:12:58 +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:12:58 +00:00
if (damageType!=class'DmgType_Fell') // Not from falling!
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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.5f); // Randomly knockout a player if hit by a huge force.
2020-11-28 20:12:58 +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);
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
event Landed(vector HitNormal, actor FloorActor)
{
local float ExcessSpeed;
Super.Landed(HitNormal, FloorActor);
2020-11-28 20:12:58 +00:00
if (bRagdollFromFalling)
2017-10-20 02:00:49 +00:00
{
ExcessSpeed = Velocity.Z / (-MaxFallSpeed);
2020-11-28 20:12:58 +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:12:58 +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:12:58 +00:00
if (bHasBunnyHop && VSizeSq2D(Velocity)>Square(GroundSpeed*0.75))
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +00:00
if (BHopAccelSpeed<20)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +00:00
if (BHopAccelSpeed==0)
2017-10-20 02:00:49 +00:00
BHopSpeedMod = 1.f;
2020-11-28 20:12:58 +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 21:54:57 +00:00
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:12:58 +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:12:58 +00:00
if (WorldInfo.NetMode==NM_Client && !bSkipRep)
2017-10-20 02:00:49 +00:00
NotifyHasStopped();
}
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
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:12:58 +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:12:58 +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:12:58 +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:12:58 +00:00
if (Mesh.PhysicsWeight<=0)
2017-10-20 02:00:49 +00:00
StartFeignDeathRecoveryAnim();
}
2020-11-28 20:12:58 +00:00
if (BHopAccelSpeed>0)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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:12:58 +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 21:54:57 +00:00
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 21:54:57 +00:00
2020-11-28 20:04:55 +00:00
function SetFeignDeath(float Time)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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');
}
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
function UnsetFeignDeath()
{
2020-11-28 20:12:58 +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:12:58 +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:12:58 +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:12:58 +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:12:58 +00:00
if (!InitRagdoll()) // Ragdoll error!
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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:12:58 +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
2020-11-28 20:12:58 +00:00
if (UnFeignZAdjust > 0.0)
2017-10-20 02:00:49 +00:00
{
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:12:58 +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.025f,,,,true);
SetRotation(NewRotation);
}
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
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:12:58 +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);
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
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;
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
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;
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
simulated function SetPawnRBChannels(bool bRagdollMode)
{
2020-11-28 20:12:58 +00:00
if (bRagdollMode)
2017-10-20 02:00:49 +00:00
{
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);
}
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
simulated function PlayRagdollDeath(class<DamageType> DamageType, vector HitLoc)
{
local TraceHitInfo HitInfo;
local vector HitDirection;
Mesh.SetHasPhysicsAssetInstance(false);
Mesh.SetHasPhysicsAssetInstance(true);
2020-11-28 20:12:58 +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:12:58 +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)
}
}
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
simulated function StartFeignDeathRecoveryAnim()
{
2020-11-28 20:12:58 +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.2f,,,,true);
SetTimer(1.7f,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:12:58 +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);
}
2020-11-28 21:54:57 +00:00
2017-10-20 02:00:49 +00:00
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:12:58 +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:12:58 +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:12:58 +00:00
if (CharacterArch != none)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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:12:58 +00:00
if (bPlayingFeignDeathRecovery)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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:12:58 +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:12:58 +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.5f);
}
2020-11-28 20:12:58 +00:00
if (WorldInfo.NetMode!=NM_Client)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +00:00
if (ExtPlayerController(Controller)!=None)
2017-10-20 02:00:49 +00:00
ExtPlayerController(Controller).EnterRagdollMode(true);
2020-11-28 20:12:58 +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:12:58 +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.5f);
}
Global.SetMovementPhysics();
bPlayingFeignDeathRecovery = false;
2020-11-28 20:12:58 +00:00
if (WorldInfo.NetMode!=NM_Client)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +00:00
if (ExtPlayerController(Controller)!=None)
2017-10-20 02:00:49 +00:00
ExtPlayerController(Controller).EnterRagdollMode(false);
2020-11-28 20:12:58 +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:12:58 +00:00
if (ExtPlayerController(Controller)!=None)
2017-10-20 02:00:49 +00:00
ExtPlayerController(Controller).EnterRagdollMode(true);
2020-11-28 20:12:58 +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:12:58 +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:12:58 +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:12:58 +00:00
if (ExtPlayerController(Controller)!=None)
2017-10-20 02:00:49 +00:00
Controller.GoToState('RagdollMove');
2020-11-28 20:12:58 +00:00
else if (Controller!=None)
2017-10-20 02:00:49 +00:00
Controller.ReplicatedEvent('RagdollMove');
Z.WakeUp();
2020-11-28 20:12:58 +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:12:58 +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:12:58 +00:00
if (Health>0)
2017-10-20 02:00:49 +00:00
{
bOnFirstPerson = !bVisible;
2020-11-28 20:12:58 +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:12:58 +00:00
if (FPBodyMesh!=None)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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:12:58 +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:12:58 +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;
local int i;
2017-10-20 02:00:49 +00:00
BackpackWeaponClass = WC;
2020-11-28 20:12:58 +00:00
if (WorldInfo.NetMode==NM_DedicatedServer)
2017-10-20 02:00:49 +00:00
return;
2020-11-28 20:12:58 +00:00
if (WC!=None)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +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:12:58 +00:00
for (i=0; i<WC.Default.AttachmentArchetype.SkelMesh.Materials.length; i++)
{
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;
2020-11-28 20:12:58 +00:00
if (ClassIsChildOf(WC, class'KFWeap_Edged_Knife'))
{
MyPos = vect(0,0,10);
MyRot = rot(-16384,-8192,0);
B = 'LeftUpLeg';
}
2020-11-28 20:12:58 +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';
}
2020-11-28 20:12:58 +00:00
else if (ClassIsChildOf(WC, class'KFWeap_MeleeBase'))
{
2017-10-20 02:00:49 +00:00
MyPos = vect(-5,15,0);
MyRot = rot(0,0,0);
2020-11-28 20:12:58 +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 02:00:49 +00:00
B = 'Spine';
}
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:12:58 +00:00
if (MyCharacter == KFCharacterInfo_Human'CHR_Playable_ARCH.chr_DJSkully_archetype')
2017-10-20 02:00:49 +00:00
MyRot.Roll = 8192;
2020-11-28 20:12:58 +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;
case 'Wep_3rdP_RPG7_Rig':
MyPos.X = 10;
break;
2017-10-20 02:00:49 +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:12:58 +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:12:58 +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:12:58 +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);
}
2020-11-28 20:12:58 +00:00
simulated function PlayWeaponswitch (Weapon OldWeapon, Weapon NewWeapon)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:12:58 +00:00
Super.PlayWeaponswitch (OldWeapon, NewWeapon);
2017-10-20 02:00:49 +00:00
2020-11-28 20:12:58 +00:00
if (WorldInfo.NetMode!=NM_Client)
2017-10-20 02:00:49 +00:00
{
PlayerOldWeapon = KFWeapon(OldWeapon);
SetBackpackWeapon(PlayerOldWeapon!=None ? PlayerOldWeapon.Class : None);
}
}
simulated function UpdateHealingSpeedBoostMod(ExtPlayerController Healer)
{
local Ext_PerkFieldMedic MedPerk;
MedPerk = GetMedicPerk(Healer);
2020-11-28 20:12:58 +00:00
if (MedPerk == None)
return;
2020-11-28 20:04:55 +00:00
HealingSpeedBoostMod = Min(HealingSpeedBoostMod + MedPerk.GetHealingSpeedBoost(), MedPerk.GetMaxHealingSpeedBoost());
SetTimer(MedPerk.GetHealingSpeedBoostDuration(),, nameOf(ResetHealingSpeedBoost));
UpdateGroundSpeed();
}
simulated function float GetHealingSpeedModifier()
{
return 1 + (float(HealingSpeedBoostMod) / 100);
}
simulated function ResetHealingSpeedBoost()
{
HealingSpeedBoostMod = 0;
UpdateGroundSpeed();
2020-11-28 20:12:58 +00:00
if (IsTimerActive(nameOf(ResetHealingSpeedBoost)))
2020-11-28 20:04:55 +00:00
ClearTimer(nameOf(ResetHealingSpeedBoost));
}
simulated function UpdateHealingDamageBoostMod(ExtPlayerController Healer)
{
local Ext_PerkFieldMedic MedPerk;
MedPerk = GetMedicPerk(Healer);
2020-11-28 20:12:58 +00:00
if (MedPerk == None)
return;
2020-11-28 20:04:55 +00:00
HealingDamageBoostMod = Min(HealingDamageBoostMod + MedPerk.GetHealingDamageBoost(), MedPerk.GetMaxHealingDamageBoost());
SetTimer(MedPerk.GetHealingDamageBoostDuration(),, nameOf(ResetHealingDamageBoost));
}
simulated function float GetHealingDamageBoostModifier()
{
return 1 + (float(HealingDamageBoostMod) / 100);
}
simulated function ResetHealingDamageBoost()
{
HealingDamageBoostMod = 0;
2020-11-28 20:12:58 +00:00
if (IsTimerActive(nameOf(ResetHealingDamageBoost)))
2020-11-28 20:04:55 +00:00
ClearTimer(nameOf(ResetHealingDamageBoost));
}
simulated function UpdateHealingShieldMod(ExtPlayerController Healer)
{
local Ext_PerkFieldMedic MedPerk;
MedPerk = GetMedicPerk(Healer);
2020-11-28 20:12:58 +00:00
if (MedPerk == None)
return;
2020-11-28 20:04:55 +00:00
HealingShieldMod = Min(HealingShieldMod + MedPerk.GetHealingShield(), MedPerk.GetMaxHealingShield());
SetTimer(MedPerk.GetHealingShieldDuration(),, nameOf(ResetHealingShield));
}
simulated function float GetHealingShieldModifier()
{
return 1 - (float(HealingShieldMod) / 100);
}
simulated function ResetHealingShield()
{
HealingShieldMod = 0;
2020-11-28 20:12:58 +00:00
if (IsTimerActive(nameOf(ResetHealingShield)))
2020-11-28 20:04:55 +00:00
ClearTimer(nameOf(ResetHealingShield));
}
function SacrificeExplode()
{
local Ext_PerkDemolition DemoPerk;
Super.SacrificeExplode();
DemoPerk = Ext_PerkDemolition(ExtPlayerController(Controller).ActivePerkManager.CurrentPerk);
2020-11-28 20:12:58 +00:00
if (DemoPerk != none)
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:12:58 +00:00
if (MedPerk != None)
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'
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()
DefaultInventory.Add(class'ExtWeap_Pistol_9mm')
// 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
}
simulated function bool CanBeHealed()
{
return true;
2017-10-20 02:00:49 +00:00
}