KF2-Server-Extension/ServerExt/Classes/ExtHumanPawn.uc
inklesspen1scripter d848e56692 Trader's things
Trader:
Custom trader can be disabled
DLCs weapons added to trader (also can be disabled)
Finally fixed trader
Pistol upgrades can be disabled. Also.
2020-06-23 23:38:04 +03:00

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;
}