1344 lines
39 KiB
Ucode
1344 lines
39 KiB
Ucode
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;
|
|
|
|
replication
|
|
{
|
|
if( true )
|
|
bFeigningDeath,RepRegenHP,BackpackWeaponClass;
|
|
if( bNetOwner )
|
|
bHasBunnyHop;
|
|
if( bNetDirty )
|
|
HealingSpeedBoostMod, HealingDamageBoostMod, HealingShieldMod;
|
|
}
|
|
|
|
function TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
|
|
{
|
|
if( KnockbackResist<1 )
|
|
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;
|
|
|
|
if( WorldInfo.NetMode!=NM_Client && PlayerReplicationInfo!=None )
|
|
{
|
|
if( Killer==None || Killer==Controller )
|
|
{
|
|
KillerPRI = PlayerReplicationInfo;
|
|
KillerPawn = None;
|
|
}
|
|
else
|
|
{
|
|
KillerPRI = Killer.PlayerReplicationInfo;
|
|
if( KillerPRI==None || KillerPRI.Team!=PlayerReplicationInfo.Team )
|
|
{
|
|
KillerPawn = Killer.Pawn!=None ? Killer.Pawn.Class : None;
|
|
if( PlayerController(Killer)==None ) // If was killed by a monster, don't broadcast PRI along with it.
|
|
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.
|
|
if( bRedeadMode && WorldInfo.NetMode!=NM_Client && damageType!=None && Killer!=None && Killer!=Controller && Killer.GetTeamNum()!=0 )
|
|
{
|
|
if ( bDeleteMe || WorldInfo.Game == None || WorldInfo.Game.bLevelChange )
|
|
return FALSE;
|
|
bPendingRedead = true;
|
|
if ( WorldInfo.Game.PreventDeath(self, Killer, damageType, HitLocation) )
|
|
{
|
|
bPendingRedead = false;
|
|
Health = max(Health, 1);
|
|
return false;
|
|
}
|
|
Health = 0;
|
|
foreach LatentActions(Action)
|
|
Action.AbortFor(self);
|
|
if ( Controller != None )
|
|
WorldInfo.Game.Killed(Killer, Controller, self, damageType);
|
|
else WorldInfo.Game.Killed(Killer, Controller(Owner), self, damageType);
|
|
|
|
if ( InvManager != None )
|
|
InvManager.OwnerDied();
|
|
|
|
Health = 1;
|
|
if( !bFeigningDeath )
|
|
PlayFeignDeath(true,,true);
|
|
Health = 0;
|
|
ClearTimer('UnsetFeignDeath');
|
|
GoToState('TransformZed');
|
|
return true;
|
|
}
|
|
return Super.Died(Killer, DamageType, HitLocation);
|
|
}
|
|
simulated function BroadcastDeathMessage( Controller Killer );
|
|
|
|
function SetBatteryRate( float Rate )
|
|
{
|
|
BatteryDrainRate = Default.BatteryDrainRate*Rate;
|
|
NVGBatteryDrainRate = Default.NVGBatteryDrainRate*Rate;
|
|
ClientSetBatteryRate(Rate);
|
|
}
|
|
simulated reliable client function ClientSetBatteryRate( float Rate )
|
|
{
|
|
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;
|
|
local int i;
|
|
local bool bRepairedArmor;
|
|
local ExtPlayerReplicationInfo EPRI;
|
|
local Ext_PerkBase InstigatorExtPerk;
|
|
|
|
InstigatorPC = ExtPlayerController(Healer);
|
|
InstigatorPerk = InstigatorPC.GetPerk();
|
|
|
|
if( InstigatorPerk != None && bCanRepairArmor )
|
|
bRepairedArmor = InstigatorPC.GetPerk().RepairArmor( self );
|
|
|
|
EPRI = ExtPlayerReplicationInfo(InstigatorPC.PlayerReplicationInfo);
|
|
if( EPRI != none )
|
|
{
|
|
InstigatorExtPerk = ExtPlayerController(Controller).ActivePerkManager.CurrentPerk;
|
|
if( InstigatorExtPerk != none && Ext_PerkFieldMedic(InstigatorExtPerk) != none )
|
|
{
|
|
if( Ext_PerkFieldMedic(InstigatorExtPerk).bHealingBoost )
|
|
UpdateHealingSpeedBoostMod(InstigatorPC);
|
|
|
|
if( Ext_PerkFieldMedic(InstigatorExtPerk).bHealingDamageBoost )
|
|
UpdateHealingDamageBoostMod(InstigatorPC);
|
|
|
|
if( Ext_PerkFieldMedic(InstigatorExtPerk).bHealingShield )
|
|
UpdateHealingShieldMod(InstigatorPC);
|
|
}
|
|
}
|
|
|
|
if( Amount > 0 && IsAliveAndWell() && Health < HealthMax )
|
|
{
|
|
// Play any healing effects attached to this damage type
|
|
KFDT = class<KFDamageType>(DamageType);
|
|
if( KFDT != none && KFDT.default.bNoPain )
|
|
PlayHeal( KFDT );
|
|
|
|
if( Role == ROLE_Authority )
|
|
{
|
|
if( Healer==None || Healer.PlayerReplicationInfo == None )
|
|
return false;
|
|
|
|
InstigatorPRI = KFPlayerReplicationInfo(Healer.PlayerReplicationInfo);
|
|
ScAmount = Amount;
|
|
if( InstigatorPerk != none )
|
|
InstigatorPerk.ModifyHealAmount( ScAmount );
|
|
UsedHealAmount = ScAmount;
|
|
|
|
// You can never have a HealthToRegen value that's greater than HealthMax
|
|
if( Health + HealthToRegen + UsedHealAmount > HealthMax )
|
|
UsedHealAmount = Min(HealthMax - (Health + HealthToRegen),255-HealthToRegen);
|
|
else UsedHealAmount = Min(UsedHealAmount,255-HealthToRegen);
|
|
|
|
HealthToRegen += UsedHealAmount;
|
|
RepRegenHP = HealthToRegen;
|
|
if( !IsTimerActive('GiveHealthOverTime') )
|
|
SetTimer(HealthRegenRate, true, 'GiveHealthOverTime');
|
|
|
|
// Give the healer money/XP for helping a teammate
|
|
if( Healer.Pawn != none && Healer.Pawn != self )
|
|
{
|
|
DoshEarned = ( UsedHealAmount / float(HealthMax) ) * HealerRewardScaler;
|
|
if( InstigatorPRI!=None )
|
|
InstigatorPRI.AddDosh(Max(DoshEarned, 0), true);
|
|
if( InstigatorPC!=None )
|
|
InstigatorPC.AddHealPoints( UsedHealAmount );
|
|
}
|
|
|
|
if( Healer.bIsPlayer )
|
|
{
|
|
if( Healer != Controller )
|
|
{
|
|
if( InstigatorPC!=None )
|
|
{
|
|
if( !InstigatorPC.bClientHideNumbers )
|
|
InstigatorPC.ClientNumberMsg(UsedHealAmount,Location,DMG_Heal);
|
|
InstigatorPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_HealedPlayer, PlayerReplicationInfo );
|
|
}
|
|
KFPC = ExtPlayerController(Controller);
|
|
if( KFPC!=None )
|
|
KFPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_HealedBy, Healer.PlayerReplicationInfo );
|
|
}
|
|
else if( bMessageHealer && InstigatorPC!=None )
|
|
InstigatorPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_HealedSelf, PlayerReplicationInfo );
|
|
}
|
|
|
|
// don't play dialog for healing done through perk skills (e.g. berserker vampire skill)
|
|
if( bMessageHealer )
|
|
{
|
|
`DialogManager.PlayHealingDialog( KFPawn(Healer.Pawn), self, float(Health + HealthToRegen) / float(HealthMax) );
|
|
}
|
|
|
|
// Reduce burn duration and damage in half if you heal while burning
|
|
for( i = 0; i < DamageOverTimeArray.Length; ++i )
|
|
{
|
|
if( DamageOverTimeArray[i].DoT_Type == DOT_Fire )
|
|
{
|
|
DamageOverTimeArray[i].Duration *= 0.5;
|
|
DamageOverTimeArray[i].Damage *= 0.5;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return bRepairedArmor;
|
|
}
|
|
|
|
function GiveHealthOverTime()
|
|
{
|
|
Super.GiveHealthOverTime();
|
|
RepRegenHP = HealthToRegen;
|
|
}
|
|
|
|
simulated event ReplicatedEvent(name VarName)
|
|
{
|
|
switch( VarName )
|
|
{
|
|
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)
|
|
{
|
|
if( damageType!=class'DmgType_Fell' ) // Not from falling!
|
|
{
|
|
if( bRagdollFromMomentum && Damage>2 && VSizeSq(Momentum)>1000000.f && Rand(3)==0 ) // Square(1000)
|
|
SetFeignDeath(3.f+FRand()*2.5f); // Randomly knockout a player if hit by a huge force.
|
|
else if( bRagdollFromBackhit && Damage>20 && VSizeSq(Momentum)>40000.f && (vector(Rotation) Dot Momentum)>0.f && Rand(4)==0 )
|
|
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);
|
|
if( bRagdollFromFalling )
|
|
{
|
|
ExcessSpeed = Velocity.Z / (-MaxFallSpeed);
|
|
if( ExcessSpeed>1.25 ) // Knockout a player after landed from too high.
|
|
{
|
|
Velocity.Z = 0; // Dont go clip through floor now...
|
|
Velocity.X*=0.5;
|
|
Velocity.Y*=0.5;
|
|
SetFeignDeath((3.f+FRand())*ExcessSpeed);
|
|
}
|
|
}
|
|
else if( BHopAccelSpeed>0 )
|
|
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:
|
|
function bool DoJump( bool bUpdating )
|
|
{
|
|
local float V;
|
|
|
|
if ( Super.DoJump(bUpdating) )
|
|
{
|
|
// Accelerate if bunnyhopping.
|
|
if( bHasBunnyHop && VSizeSq2D(Velocity)>Square(GroundSpeed*0.75) )
|
|
{
|
|
if( BHopAccelSpeed<20 )
|
|
{
|
|
if( BHopAccelSpeed==0 )
|
|
BHopSpeedMod = 1.f;
|
|
|
|
if( BHopAccelSpeed<5 )
|
|
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;
|
|
}
|
|
simulated function ResetBHopAccel( optional bool bSkipRep ) // Set on Landed, or Tick if falling 2D speed is too low.
|
|
{
|
|
if( BHopAccelSpeed>0 )
|
|
{
|
|
BHopAccelSpeed = 0;
|
|
AirControl = Default.AirControl;
|
|
GroundSpeed /= BHopSpeedMod;
|
|
UpdateGroundSpeed();
|
|
if( WorldInfo.NetMode==NM_Client && !bSkipRep )
|
|
NotifyHasStopped();
|
|
}
|
|
}
|
|
function UpdateGroundSpeed()
|
|
{
|
|
local KFInventoryManager InvM;
|
|
local float HealthMod;
|
|
|
|
if ( Role < ROLE_Authority )
|
|
return;
|
|
|
|
InvM = KFInventoryManager(InvManager);
|
|
HealthMod = (InvM != None) ? InvM.GetEncumbranceSpeedMod() : 1.f * (1.f - LowHealthSpeedPenalty);
|
|
if( BHopAccelSpeed>0 )
|
|
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
|
|
if( GetPerk() != none )
|
|
{
|
|
GetPerk().ModifySpeed(GroundSpeed);
|
|
GetPerk().ModifySpeed(SprintSpeed);
|
|
}
|
|
}
|
|
|
|
reliable server function NotifyHasStopped()
|
|
{
|
|
ResetBHopAccel(true);
|
|
}
|
|
|
|
// ==================================================================
|
|
// Feign death (UT3):
|
|
simulated function Tick( float Delta )
|
|
{
|
|
Super.Tick(Delta);
|
|
if( bPlayingFeignDeathRecovery )
|
|
{
|
|
// 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);
|
|
if( Mesh.PhysicsWeight<=0 )
|
|
StartFeignDeathRecoveryAnim();
|
|
}
|
|
if( BHopAccelSpeed>0 )
|
|
{
|
|
if( Physics==PHYS_Falling && VSizeSq2D(Velocity)<Square(GroundSpeed*0.7) )
|
|
ResetBHopAccel(true);
|
|
}
|
|
if( WorldInfo.NetMode!=NM_Client && BackpackWeaponClass!=none && (PlayerOldWeapon==None || PlayerOldWeapon.Instigator==None) )
|
|
{
|
|
PlayerOldWeapon = None;
|
|
SetBackpackWeapon(None);
|
|
}
|
|
}
|
|
|
|
function DelayedRagdoll()
|
|
{
|
|
SetFeignDeath(2.f+FRand()*3.f);
|
|
}
|
|
exec function FeignDeath( float Time )
|
|
{
|
|
SetFeignDeath(Time);
|
|
}
|
|
function SetFeignDeath( float Time )
|
|
{
|
|
if( WorldInfo.NetMode!=NM_Client && !bFeigningDeath && Health>0 && bCanBecomeRagdoll && NoRagdollChance<1.f && (NoRagdollChance==0.f || FRand()>NoRagdollChance) )
|
|
{
|
|
Time = FMax(1.f,Time);
|
|
PlayFeignDeath(true);
|
|
SetTimer(Time,false,'UnsetFeignDeath');
|
|
}
|
|
}
|
|
function UnsetFeignDeath()
|
|
{
|
|
if( bFeigningDeath )
|
|
PlayFeignDeath(false);
|
|
}
|
|
|
|
simulated function PlayFeignDeath( bool bEnable, optional bool bForce, optional bool bTransformMode )
|
|
{
|
|
local vector FeignLocation, HitLocation, HitNormal, TraceEnd, Impulse;
|
|
local rotator NewRotation;
|
|
local float UnFeignZAdjust;
|
|
|
|
if( Health<=0 && WorldInfo.NetMode!=NM_Client )
|
|
return; // If dead, don't do it.
|
|
|
|
NotifyOutOfBattery(); // Stop nightvision on client.
|
|
|
|
bFeigningDeath = bEnable;
|
|
if ( bEnable )
|
|
{
|
|
if( bFPLegsAttached )
|
|
{
|
|
bFPLegsAttached = false;
|
|
DetachComponent(FPBodyMesh);
|
|
}
|
|
WeaponAttachmentTemplate = None;
|
|
WeaponAttachmentChanged();
|
|
|
|
bPlayingFeignDeathRecovery = false;
|
|
ClearTimer('OnWakeUpFinished');
|
|
if( !bTransformMode )
|
|
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);
|
|
|
|
if( !InitRagdoll() ) // Ragdoll error!
|
|
{
|
|
if( PlayerController(Controller)!=None )
|
|
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);
|
|
if( UnFeignZAdjust>700.f ) // Limit by a maximum velocity force to prevent from going through walls.
|
|
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();
|
|
if (Trace(HitLocation, HitNormal, TraceEnd, Location, true, GetCollisionExtent()) == None )
|
|
{
|
|
HitLocation = TraceEnd;
|
|
}
|
|
if ( !SetFeignEndLocation(HitLocation, FeignLocation) && WorldInfo.NetMode!=NM_Client )
|
|
{
|
|
UnfeignFailedCount++;
|
|
if ( UnFeignfailedCount > 4 || bForce )
|
|
{
|
|
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.
|
|
if( Mesh.AnimSets.Find(WakeUpAnimSet)==-1 )
|
|
Mesh.AnimSets.AddItem(WakeUpAnimSet);
|
|
BodyStanceNodes[EAS_FullBody].bNoNotifies = true;
|
|
BodyStanceNodes[EAS_FullBody].PlayCustomAnim(FeignRecoverAnim,0.025f,,,,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);
|
|
if( Best==None || Dist<BestDist )
|
|
{
|
|
Best = N;
|
|
BestDist = Dist;
|
|
}
|
|
}
|
|
return (Best!=None ? Best.Location : Location);
|
|
}
|
|
simulated function bool SetFeignEndLocation(vector HitLocation, vector FeignLocation)
|
|
{
|
|
local vector NewDest;
|
|
|
|
if ( SetLocation(HitLocation) && CheckValidLocation(FeignLocation) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// try crouching
|
|
ForceCrouch();
|
|
if ( SetLocation(HitLocation) && CheckValidLocation(FeignLocation) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
newdest = HitLocation + GetCollisionRadius() * vect(1,1,0);
|
|
if ( SetLocation(newdest) && CheckValidLocation(FeignLocation) )
|
|
return true;
|
|
newdest = HitLocation + GetCollisionRadius() * vect(1,-1,0);
|
|
if ( SetLocation(newdest) && CheckValidLocation(FeignLocation) )
|
|
return true;
|
|
newdest = HitLocation + GetCollisionRadius() * vect(-1,1,0);
|
|
if ( SetLocation(newdest) && CheckValidLocation(FeignLocation) )
|
|
return true;
|
|
newdest = HitLocation + GetCollisionRadius() * vect(-1,-1,0);
|
|
if ( SetLocation(newdest) && CheckValidLocation(FeignLocation) )
|
|
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;
|
|
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 )
|
|
{
|
|
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);
|
|
if( bFPLegsAttached )
|
|
{
|
|
bFPLegsAttached = false;
|
|
DetachComponent(FPBodyMesh);
|
|
}
|
|
|
|
// Ensure we are always updating kinematic
|
|
Mesh.MinDistFactorForKinematicUpdate = 0.0;
|
|
|
|
PrepareRagdoll();
|
|
|
|
if ( InitRagdoll() )
|
|
{
|
|
// 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);
|
|
CheckHitInfo(HitInfo, Mesh, HitDirection, HitLoc);
|
|
|
|
// Play ragdoll death animation (bSkipReplication=TRUE)
|
|
if( CanDoSpecialMove(SM_DeathAnim) && ClassIsChildOf(DamageType, class'KFDamageType') )
|
|
{
|
|
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()
|
|
{
|
|
if( FPBodyMesh!=None && !bFPLegsAttached && bOnFirstPerson && Class'ExtPlayerController'.Default.bShowFPLegs )
|
|
{
|
|
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()
|
|
{
|
|
local KFPerk MyPerk;
|
|
|
|
MyPerk = GetPerk();
|
|
if( MyPerk != none )
|
|
MyPerk.AddDefaultInventory(self);
|
|
|
|
Super(KFPawn).AddDefaultInventory();
|
|
}
|
|
|
|
simulated event FellOutOfWorld(class<DamageType> dmgType)
|
|
{
|
|
if ( Role==ROLE_Authority && NextRedeemTimer<WorldInfo.TimeSeconds ) // Make sure to not to spam deathmessages while ghosting.
|
|
Super.FellOutOfWorld(dmgType);
|
|
}
|
|
simulated event OutsideWorldBounds()
|
|
{
|
|
if ( Role==ROLE_Authority && NextRedeemTimer<WorldInfo.TimeSeconds )
|
|
Super.OutsideWorldBounds();
|
|
}
|
|
|
|
simulated function KFCharacterInfoBase GetCharacterInfo()
|
|
{
|
|
if( ExtPlayerReplicationInfo(PlayerReplicationInfo)!=None )
|
|
return ExtPlayerReplicationInfo(PlayerReplicationInfo).GetSelectedArch();
|
|
return Super.GetCharacterInfo();
|
|
}
|
|
|
|
simulated function SetCharacterArch(KFCharacterInfoBase Info, optional bool bForce )
|
|
{
|
|
local KFPlayerReplicationInfo KFPRI;
|
|
|
|
KFPRI = KFPlayerReplicationInfo( PlayerReplicationInfo );
|
|
if (Info != CharacterArch || bForce)
|
|
{
|
|
// Set Family Info
|
|
CharacterArch = Info;
|
|
CharacterArch.SetCharacterFromArch( self, KFPRI );
|
|
class'ExtCharacterInfo'.Static.SetCharacterMeshFromArch( KFCharacterInfo_Human(CharacterArch), self, KFPRI );
|
|
class'ExtCharacterInfo'.Static.SetFirstPersonArmsFromArch( KFCharacterInfo_Human(CharacterArch), self, KFPRI );
|
|
|
|
SetCharacterAnimationInfo();
|
|
|
|
// Sounds
|
|
SoundGroupArch = Info.SoundGroupArch;
|
|
|
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
|
{
|
|
// refresh weapon attachment (attachment bone may have changed)
|
|
if (WeaponAttachmentTemplate != None)
|
|
{
|
|
WeaponAttachmentChanged(true);
|
|
}
|
|
}
|
|
if( WorldInfo.NetMode != NM_DedicatedServer )
|
|
{
|
|
// Attach/Reattach flashlight components when mesh is set
|
|
if ( Flashlight == None && FlashLightTemplate != None )
|
|
{
|
|
Flashlight = new(self) Class'KFFlashlightAttachment' (FlashLightTemplate);
|
|
}
|
|
if ( FlashLight != None )
|
|
{
|
|
Flashlight.AttachFlashlight(Mesh);
|
|
}
|
|
}
|
|
if( CharacterArch != none )
|
|
{
|
|
if( CharacterArch.VoiceGroupArchName != "" )
|
|
VoiceGroupArch = class<KFPawnVoiceGroup>(class'ExtCharacterInfo'.Static.SafeLoadObject(CharacterArch.VoiceGroupArchName, class'Class'));
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated state FeigningDeath
|
|
{
|
|
ignores FaceRotation, SetMovementPhysics;
|
|
|
|
function SetSprinting(bool bNewSprintStatus)
|
|
{
|
|
bIsSprinting = false;
|
|
}
|
|
simulated event RigidBodyCollision( PrimitiveComponent HitComponent, PrimitiveComponent OtherComponent, const out CollisionImpactData RigidCollisionData, int ContactIndex )
|
|
{
|
|
// 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);
|
|
}
|
|
function bool CanBeGrabbed(KFPawn GrabbingPawn, optional bool bIgnoreFalling, optional bool bAllowSameTeamGrab)
|
|
{
|
|
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;
|
|
|
|
if( bPlayingFeignDeathRecovery )
|
|
{
|
|
if( PlayerController(Controller) != None )
|
|
{
|
|
// 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);
|
|
if( Mesh.PhysicsWeight<=0 )
|
|
StartFeignDeathRecoveryAnim();
|
|
}
|
|
}
|
|
|
|
simulated event BeginState(name PreviousStateName)
|
|
{
|
|
local KFWeapon UTWeap;
|
|
|
|
// Abort current special move
|
|
if( IsDoingSpecialMove() )
|
|
SpecialMoveHandler.EndSpecialMove();
|
|
|
|
bCanPickupInventory = false;
|
|
StopFiring();
|
|
bNoWeaponFiring = true;
|
|
|
|
UTWeap = KFWeapon(Weapon);
|
|
if (UTWeap != None)
|
|
{
|
|
UTWeap.SetIronSights(false);
|
|
UTWeap.PlayWeaponPutDown(0.5f);
|
|
}
|
|
if( WorldInfo.NetMode!=NM_Client )
|
|
{
|
|
if( ExtPlayerController(Controller)!=None )
|
|
ExtPlayerController(Controller).EnterRagdollMode(true);
|
|
else if( Controller!=None )
|
|
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()
|
|
{
|
|
if( bFeigningDeath )
|
|
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;
|
|
if (NextStateName != 'Dying' )
|
|
{
|
|
bNoWeaponFiring = default.bNoWeaponFiring;
|
|
bCanPickupInventory = default.bCanPickupInventory;
|
|
|
|
UTWeap = KFWeapon(Weapon);
|
|
if ( UTWeap != None )
|
|
{
|
|
WeaponAttachmentTemplate = UTWeap.AttachmentArchetype;
|
|
UTWeap.PlayWeaponEquip(0.5f);
|
|
}
|
|
|
|
Global.SetMovementPhysics();
|
|
bPlayingFeignDeathRecovery = false;
|
|
if( WorldInfo.NetMode!=NM_Client )
|
|
{
|
|
if( ExtPlayerController(Controller)!=None )
|
|
ExtPlayerController(Controller).EnterRagdollMode(false);
|
|
else if( Controller!=None )
|
|
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;
|
|
if( ExtPlayerController(Controller)!=None )
|
|
ExtPlayerController(Controller).EnterRagdollMode(true);
|
|
else if( Controller!=None )
|
|
Controller.ReplicatedEvent('RagdollMove');
|
|
|
|
SetTimer(2,false,'TransformToZed');
|
|
}
|
|
simulated function EndState(name NextStateName)
|
|
{
|
|
}
|
|
function bool CanBeRedeemed()
|
|
{
|
|
return false;
|
|
}
|
|
function TransformToZed()
|
|
{
|
|
local VS_ZedRecentZed Z;
|
|
|
|
if( Controller==None )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
PlayFeignDeath(false);
|
|
SetCollision(false,false);
|
|
Z = Spawn(class'VS_ZedRecentZed',,,Location,Rotation,,true);
|
|
if( Z==None )
|
|
{
|
|
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);
|
|
if( ExtPlayerController(Controller)!=None )
|
|
Controller.GoToState('RagdollMove');
|
|
else if( Controller!=None )
|
|
Controller.ReplicatedEvent('RagdollMove');
|
|
Z.WakeUp();
|
|
if( ExtPlayerReplicationInfo(Controller.PlayerReplicationInfo)!=None )
|
|
{
|
|
ExtPlayerReplicationInfo(Controller.PlayerReplicationInfo).PlayerHealth = Min(Z.Health,255);
|
|
ExtPlayerReplicationInfo(Controller.PlayerReplicationInfo).PlayerHealthPercent = FloatToByte( float(Z.Health) / float(Z.HealthMax) );
|
|
}
|
|
}
|
|
Controller = None;
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
simulated final function InitFPLegs()
|
|
{
|
|
local int i;
|
|
|
|
bFPLegsInit = true;
|
|
|
|
FPBodyMesh.AnimSets = CharacterArch.AnimSets;
|
|
FPBodyMesh.SetAnimTreeTemplate(CharacterArch.AnimTreeTemplate);
|
|
FPBodyMesh.SetSkeletalMesh(Mesh.SkeletalMesh);
|
|
|
|
FPBodyMesh.SetActorCollision(false, false);
|
|
FPBodyMesh.SetNotifyRigidBodyCollision(false);
|
|
FPBodyMesh.SetTraceBlocking(false, false);
|
|
|
|
for( i=0; i<Mesh.Materials.length; i++ )
|
|
FPBodyMesh.SetMaterial(i, Mesh.Materials[i]);
|
|
|
|
FPBodyMesh.HideBoneByName('neck', PBO_None);
|
|
FPBodyMesh.HideBoneByName('Spine2', PBO_None);
|
|
FPBodyMesh.HideBoneByName('RightShoulder', PBO_None);
|
|
FPBodyMesh.HideBoneByName('LeftShoulder', PBO_None);
|
|
}
|
|
|
|
// ForrestMarkX's third person backpack weapon and first person legs:
|
|
simulated function SetMeshVisibility(bool bVisible)
|
|
{
|
|
Super.SetMeshVisibility(bVisible);
|
|
|
|
if( Health>0 )
|
|
{
|
|
bOnFirstPerson = !bVisible;
|
|
if( AttachedBackItem!=None )
|
|
AttachedBackItem.SetHidden(bOnFirstPerson);
|
|
UpdateFPLegs();
|
|
}
|
|
}
|
|
|
|
simulated final function UpdateFPLegs()
|
|
{
|
|
if( FPBodyMesh!=None )
|
|
{
|
|
if( !bFPLegsAttached && Physics!=PHYS_RigidBody && bOnFirstPerson && Class'ExtPlayerController'.Default.bShowFPLegs )
|
|
{
|
|
bFPLegsAttached = true;
|
|
AttachComponent(FPBodyMesh);
|
|
|
|
if( !bFPLegsInit && CharacterArch!=None )
|
|
InitFPLegs();
|
|
}
|
|
FPBodyMesh.SetHidden(!bOnFirstPerson || !Class'ExtPlayerController'.Default.bShowFPLegs);
|
|
}
|
|
}
|
|
|
|
simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
|
|
{
|
|
if( SkelComp==Mesh ) // Do not allow first person legs eat up animation slots.
|
|
Super.PostInitAnimTree(SkelComp);
|
|
}
|
|
|
|
simulated final function SetBackpackWeapon( class<KFWeapon> WC )
|
|
{
|
|
local KFCharacterInfo_Human MyCharacter;
|
|
local Rotator MyRot;
|
|
local Vector MyPos;
|
|
local name WM,B;
|
|
local int i;
|
|
|
|
BackpackWeaponClass = WC;
|
|
if( WorldInfo.NetMode==NM_DedicatedServer )
|
|
return;
|
|
|
|
if( WC!=None )
|
|
{
|
|
if( AttachedBackItem==None )
|
|
{
|
|
AttachedBackItem = new(Self) class'SkeletalMeshComponent';
|
|
AttachedBackItem.SetHidden(false);
|
|
AttachedBackItem.SetLightingChannels(PawnLightingChannel);
|
|
}
|
|
AttachedBackItem.SetSkeletalMesh(WC.Default.AttachmentArchetype.SkelMesh);
|
|
for( i=0; i<WC.Default.AttachmentArchetype.SkelMesh.Materials.length; i++ )
|
|
{
|
|
AttachedBackItem.SetMaterial(i, WC.Default.AttachmentArchetype.SkelMesh.Materials[i]);
|
|
}
|
|
|
|
Mesh.DetachComponent(AttachedBackItem);
|
|
|
|
MyCharacter = KFPlayerReplicationInfo(PlayerReplicationInfo).CharacterArchetypes[KFPlayerReplicationInfo(PlayerReplicationInfo).RepCustomizationInfo.CharacterIndex];
|
|
WM = WC.Default.AttachmentArchetype.SkelMesh.Name;
|
|
|
|
if( ClassIsChildOf(WC, class'KFWeap_Edged_Knife') )
|
|
{
|
|
MyPos = vect(0,0,10);
|
|
MyRot = rot(-16384,-8192,0);
|
|
B = 'LeftUpLeg';
|
|
}
|
|
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') )
|
|
{
|
|
MyPos = vect(0,0,10);
|
|
MyRot = rot(0,0,16384);
|
|
|
|
B = 'LeftUpLeg';
|
|
}
|
|
else if( ClassIsChildOf(WC, class'KFWeap_MeleeBase') )
|
|
{
|
|
MyPos = vect(-5,15,0);
|
|
MyRot = rot(0,0,0);
|
|
|
|
if( class<KFWeap_Edged_Katana>(WC) != none || class<KFWeap_Edged_Zweihander>(WC) != none )
|
|
MyPos.Z = -20;
|
|
|
|
B = 'Spine';
|
|
}
|
|
else
|
|
{
|
|
MyPos = vect(-18.5,16.5,-18);
|
|
MyRot = rot(0,0,0);
|
|
|
|
if( MyCharacter == KFCharacterInfo_Human'CHR_Playable_ARCH.chr_DJSkully_archetype' )
|
|
MyRot.Roll = 8192;
|
|
|
|
switch( WM )
|
|
{
|
|
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;
|
|
}
|
|
|
|
B = 'Spine2';
|
|
}
|
|
|
|
AttachedBackItem.SetTranslation(MyPos);
|
|
AttachedBackItem.SetRotation(MyRot);
|
|
Mesh.AttachComponent(AttachedBackItem, B);
|
|
AttachedBackItem.SetHidden(bOnFirstPerson);
|
|
}
|
|
else if( AttachedBackItem!=None )
|
|
AttachedBackItem.SetHidden(true);
|
|
}
|
|
|
|
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
|
{
|
|
FPBodyMesh.SetHidden(true);
|
|
if( AttachedBackItem!=None )
|
|
AttachedBackItem.SetHidden(true);
|
|
Super.PlayDying(DamageType,HitLoc);
|
|
}
|
|
|
|
simulated function SetCharacterAnimationInfo()
|
|
{
|
|
Super.SetCharacterAnimationInfo();
|
|
|
|
if( !bFPLegsInit && bFPLegsAttached )
|
|
InitFPLegs();
|
|
}
|
|
|
|
simulated function SetMeshLightingChannels(LightingChannelContainer NewLightingChannels)
|
|
{
|
|
Super.SetMeshLightingChannels(NewLightingChannels);
|
|
|
|
if (AttachedBackItem != none)
|
|
AttachedBackItem.SetLightingChannels(NewLightingChannels);
|
|
FPBodyMesh.SetLightingChannels(NewLightingChannels);
|
|
}
|
|
|
|
simulated function PlayWeaponSwitch(Weapon OldWeapon, Weapon NewWeapon)
|
|
{
|
|
Super.PlayWeaponSwitch(OldWeapon, NewWeapon);
|
|
|
|
if( WorldInfo.NetMode!=NM_Client )
|
|
{
|
|
PlayerOldWeapon = KFWeapon(OldWeapon);
|
|
SetBackpackWeapon(PlayerOldWeapon!=None ? PlayerOldWeapon.Class : None);
|
|
}
|
|
}
|
|
|
|
simulated function UpdateHealingSpeedBoostMod(ExtPlayerController Healer)
|
|
{
|
|
local Ext_PerkFieldMedic MedPerk;
|
|
|
|
MedPerk = GetMedicPerk(Healer);
|
|
if( MedPerk == None )
|
|
return;
|
|
|
|
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();
|
|
|
|
if( IsTimerActive( nameOf( ResetHealingSpeedBoost ) ) )
|
|
ClearTimer( nameOf( ResetHealingSpeedBoost ) );
|
|
}
|
|
|
|
simulated function UpdateHealingDamageBoostMod(ExtPlayerController Healer)
|
|
{
|
|
local Ext_PerkFieldMedic MedPerk;
|
|
|
|
MedPerk = GetMedicPerk(Healer);
|
|
if( MedPerk == None )
|
|
return;
|
|
|
|
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;
|
|
if( IsTimerActive( nameOf( ResetHealingDamageBoost ) ) )
|
|
ClearTimer( nameOf( ResetHealingDamageBoost ) );
|
|
}
|
|
|
|
simulated function UpdateHealingShieldMod(ExtPlayerController Healer)
|
|
{
|
|
local Ext_PerkFieldMedic MedPerk;
|
|
|
|
MedPerk = GetMedicPerk(Healer);
|
|
if( MedPerk == None )
|
|
return;
|
|
|
|
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;
|
|
if( IsTimerActive( nameOf( ResetHealingShield ) ) )
|
|
ClearTimer( nameOf( ResetHealingShield ) );
|
|
}
|
|
|
|
function SacrificeExplode()
|
|
{
|
|
local Ext_PerkDemolition DemoPerk;
|
|
|
|
Super.SacrificeExplode();
|
|
|
|
DemoPerk = Ext_PerkDemolition(ExtPlayerController(Controller).ActivePerkManager.CurrentPerk);
|
|
if( DemoPerk != none )
|
|
DemoPerk.bUsedSacrifice = true;
|
|
}
|
|
|
|
simulated function Ext_PerkFieldMedic GetMedicPerk(ExtPlayerController Healer)
|
|
{
|
|
local Ext_PerkFieldMedic MedPerk;
|
|
|
|
MedPerk = Ext_PerkFieldMedic(ExtPlayerController(Controller).ActivePerkManager.CurrentPerk);
|
|
if( MedPerk != None )
|
|
return MedPerk;
|
|
|
|
return None;
|
|
}
|
|
|
|
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
|
|
|
|
DefaultInventory.Empty()
|
|
DefaultInventory.Add(class'ExtWeap_Pistol_9mm')
|
|
// DefaultInventory.Add(class'KFWeap_Pistol_9mm')
|
|
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;
|
|
}
|