Class ExtHumanPawn extends KFPawn_Human; // Forrests backpack weapon and first person legs. var SkeletalMeshComponent AttachedBackItem; var SkeletalMeshComponent FPBodyMesh; var repnotify class 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,bThrowAllWeaponsOnDeath; 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, 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, vector HitLocation) { local ExtPlayerController C; local class 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, 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 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(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, 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)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 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 dmgType) { if (Role==ROLE_Authority && NextRedeemTimer(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; i0) { 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 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) != none || class(WC) != none || class(WC) != none || class(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(WC) != none || class(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, 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; } function ThrowActiveWeapon(optional bool bDestroyWeap) { local KFWeapon TempWeapon; if( Role < ROLE_Authority ) { return; } if (Health <= 0 && bThrowAllWeaponsOnDeath) { if (InvManager != none) foreach InvManager.InventoryActors(class'KFWeapon', TempWeapon) if (TempWeapon != none && TempWeapon.bDropOnDeath && TempWeapon.CanThrow()) TossInventory(TempWeapon); } else { super.ThrowActiveWeapon(bDestroyWeap); } } 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; }