Class VSZombie extends KFPawn_Monster abstract; var() class MeleeHitDT; var() string ZombieName; var transient VSFPZedHands FPHandModel; var() int MeleeDamage,HitsPerAttack; var() float PropDamageScale; var byte IsFiringMode[3]; var repnotify byte ServerAttackMode; var() vector FPHandOffset; var() name FPHandsIdle; var() float HPScaler; var bool bAttacking,bShowFPHands,bFPShowBody,bLockMovement; var() bool bBoss; replication { if( true ) ServerAttackMode; } // Bot AI. function bool BotFire(bool bFinished) { StartFire(0); return true; } function bool CanAttack(Actor Other) { return VSizeSq(Other.Location-Location)<62500.f; // 250.f } function bool IsFiring() { local byte i; for( i=0; i=ArrayCount(IsFiringMode) || WorldInfo.NetMode==NM_Client ) return; IsFiringMode[Mode] = byte(bStart); if( bStart ) PlayAttackAnim(Mode); } simulated function StartFire(byte FireModeNum) { if( bNoWeaponFiring ) return; ServerModeFire(true,FireModeNum); } simulated function StopFire(byte FireModeNum) { ServerModeFire(false,FireModeNum); } function DropProp(); // Handle animation replication. function ServerAttackAnim( byte Num ) { local float F; bAttacking = true; F = StartAttackAnim(Num); SetTimer(F,false,'AttackFinished'); if( WorldInfo.NetMode!=NM_StandAlone ) { Num+=1; if( Num==ServerAttackMode ) ServerAttackMode = ServerAttackMode | 128; else ServerAttackMode = Num; SetTimer(F+0.5,false,'ResetAttackTimer'); } } function ResetAttackTimer() { ServerAttackMode = 0; } simulated event ReplicatedEvent(name VarName) { switch( VarName ) { case 'ServerAttackMode': if( ServerAttackMode!=0 ) { ServerAttackMode = (ServerAttackMode & 127)-1; bAttacking = true; SetTimer(StartAttackAnim(ServerAttackMode),false,'AttackFinished'); } break; default: Super.ReplicatedEvent(VarName); } } // Handle melee damage. simulated function MeleeImpactNotify(KFAnimNotify_MeleeImpact Notify) { if( WorldInfo.NetMode!=NM_Client ) MeleeDamageTarget(vector(Rotation)*MeleeDamage*500.f); } function bool MeleeDamageTarget( vector pushdir ) { local Actor A; local vector Dir,HL,HN,Start; local byte T; local bool bResult,bHitActors; if( WorldInfo.NetMode==NM_Client || Controller==None ) Return False; // Never should be done on client. Start = GetPawnViewLocation(); Dir = vector(GetAdjustedAimFor(None,Start)); T = GetTeamNum(); // First try if can hit with a small extent trace. foreach TraceActors(Class'Actor',A,HL,HN,Start+Dir*120.f,Start,vect(8,8,8)) { if( A.bWorldGeometry || A==WorldInfo ) { if( KFDoorActor(A)!=None ) { DamageDoor(KFDoorActor(A),HL,pushdir); bHitActors = true; } bResult = true; break; } else if( Pawn(A)!=None ) { if( Pawn(A).Health>0 && (Pawn(A).GetTeamNum()!=T || WorldInfo.Game.bGameEnded) ) { DealMeleeDamage(A,HL,pushdir); bResult = true; bHitActors = true; } } } if( !bHitActors ) { // Then try with large extent. HN.X = GetCollisionRadius()*0.75; HN.Y = HN.X; HN.Z = GetCollisionHeight()*0.5; foreach TraceActors(Class'Actor',A,HL,HN,Location+Dir*120.f,Location,HN) { if( A.bWorldGeometry || A==WorldInfo ) { if( KFDoorActor(A)!=None ) DamageDoor(KFDoorActor(A),HL,pushdir); else DealMeleeDamage(A,HL,pushdir); bResult = true; break; } else if( Pawn(A)!=None ) { if( Pawn(A).Health>0 && (Pawn(A).GetTeamNum()!=T || WorldInfo.Game.bGameEnded) ) { DealMeleeDamage(A,HL,pushdir); bResult = true; } } else DealMeleeDamage(A,HL,pushdir); } } return bResult; } function DealMeleeDamage( Actor Other, vector HL, vector pushdir ) { if( KFPawn_Monster(Other)!=None && KFPawn_Monster(Other).GetTeamNum()==0 ) Other.TakeDamage(MeleeDamage*5, Controller, HL, pushdir, MeleeHitDT,,Self); // Almost insta-kill pet zeds. else Other.TakeDamage(MeleeDamage, Controller, HL, pushdir, MeleeHitDT,,Self); } function DamageDoor( KFDoorActor D, vector HL, vector pushdir ) { local bool bIsP; // Hack method, fake player being a NPC while damage is dealt to allow destroy the door. bIsP = Controller.bIsPlayer; Controller.bIsPlayer = false; D.TakeDamage(MeleeDamage, Controller, HL, pushdir, MeleeHitDT,,Self); Controller.bIsPlayer = bIsP; } /** Apply damage to a specific zone (useful for gore effects) */ function TakeHitZoneDamage(float Damage, class DamageType, int HitZoneIdx, vector InstigatorLocation) { local float HeadHealthPercentage; Super(KFPawn).TakeHitZoneDamage(Damage, DamageType, HitZoneIdx, InstigatorLocation); if ( HitZoneIdx == HZI_Head && Health>0 ) // Don't remove head until zombie actually dies. HitZones[HZI_Head].GoreHealth = Max(HitZones[HZI_Head].GoreHealth,1); // When GoreHealth <= 0, check to see if this weapon can dismember limbs if ( HitZones[HitZoneIdx].GoreHealth <= 0 && CanInjureHitZone(DamageType, HitZoneIdx) ) HitZoneInjured(HitZoneIdx); // Handle head injuries if ( HitZoneIdx == HZI_Head ) { // Based on head health, calculate number of head chunks we're allowed to remove if( !bPlayedDeath && !bIsHeadless && !bTearOff ) { HeadHealthPercentage = GetHeadHealthPercent(); if( HeadHealthPercentage > 0.5 ) { MaxHeadChunkGoreWhileAlive = 1; } else if ( HeadHealthPercentage > 0.25 ) { MaxHeadChunkGoreWhileAlive = 2; } else if ( HeadHealthPercentage > 0.0 ) { MaxHeadChunkGoreWhileAlive = 3; } } } } simulated function SetMeshVisibility(bool bVisible) { Super.SetMeshVisibility(bVisible); bShowFPHands = !bVisible; } // First person hands. simulated function DrawHUD( HUD H ) { Super.DrawHUD(H); if( Health<=0 ) return; if( !bShowFPHands ) { if( FPHandModel!=None && !FPHandModel.bHidden ) FPHandModel.SetHidden(true); } else { if( FPHandModel==None ) { FPHandModel = Spawn(class'VSFPZedHands',Self); FPHandModel.InitHands(Mesh); FPHandModel.Mesh.SetLightingChannels(PawnLightingChannel); InitFPHands(); } if( FPHandModel.bHidden ) FPHandModel.SetHidden(false); FPHandModel.SetRotation(GetViewRotation()); FPHandModel.SetLocation(GetPawnViewLocation()+(FPHandOffset >> FPHandModel.Rotation)); } } simulated function InitFPHands() { FPHandModel.Mesh.HideBoneByName('RightUpLeg',PBO_Term); FPHandModel.Mesh.HideBoneByName('LeftUpLeg',PBO_Term); FPHandModel.Mesh.HideBoneByName('Neck',PBO_Term); FPHandModel.SetIdleAnim(FPHandsIdle); } simulated function Destroyed() { DisableNightVision(); if( FPHandModel!=None ) FPHandModel.Destroy(); Super.Destroyed(); } simulated function PlayDying(class DamageType, vector HitLoc) { DisableNightVision(); if( FPHandModel!=None ) FPHandModel.Destroy(); Super.PlayDying(DamageType,HitLoc); } exec function TestFPOffset( vector V ) { FPHandOffset = V; } simulated final function DisableNightVision() { local KFPlayerController KFPC; if( IsLocallyControlled() ) { KFPC = KFPlayerController(Controller); if( KFPC!=None && KFPC.bNightVisionActive ) KFPC.SetNightVision(false); } } function PossessedBy( Controller C, bool bVehicleTransition ) { Super(KFPawn).PossessedBy( C, bVehicleTransition ); bReducedZedOnZedPinchPointCollisionStateActive = false; bIgnoreTeamCollision = true; } // Removed insane damage multiplier for headshot. function AdjustDamage(out int InDamage, out vector Momentum, Controller InstigatedBy, vector HitLocation, class DamageType, TraceHitInfo HitInfo, Actor DamageCauser) { local float TempDamage,DamageMod; local int HitZoneIdx; Super(KFPawn).AdjustDamage(InDamage, Momentum, InstigatedBy, HitLocation, DamageType, HitInfo, DamageCauser); TempDamage = InDamage; // Multiply by float value all the way for more precise result. // is vulnerable? DamageMod = 1.f; if( IsVulnerableTo(DamageType,DamageMod) ) { TempDamage *= DamageMod; } else if( IsResistantTo(DamageType,DamageMod) ) { TempDamage *= DamageMod; } // Cached hit params if( HitInfo.BoneName!='' && class(DamageType)==None && class(DamageType)==None ) { HitZoneIdx = HitZones.Find('ZoneName', HitInfo.BoneName); if( HitZoneIdx>=0 ) TempDamage *= HitZones[HitZoneIdx].DmgScale; } InDamage = FCeil( TempDamage ); if( InstigatedBy!=None && InstigatedBy!=Controller && InstigatedBy.GetTeamNum()==0 ) { // Give credits to pets owner. if( Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo)!=None ) InstigatedBy = Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo).OwnerController; if( InstigatedBy!=None ) AddTakenDamage( InstigatedBy, FMin(Health, InDamage), DamageCauser, class(DamageType) ); } } event Landed(vector HitNormal, actor FloorActor) { Super.Landed(HitNormal, FloorActor); if ( Velocity.Z < -200 ) { // Slow down after a jump. Velocity.X *= 0.02; Velocity.Y *= 0.02; } } function bool DoJump( bool bUpdating ) { if( bJumpCapable && (Physics == PHYS_Walking || Physics == PHYS_Ladder || Physics == PHYS_Spider) ) { if ( Physics == PHYS_Spider ) Velocity = Velocity + (JumpZ * Floor); else if ( Physics == PHYS_Ladder ) Velocity.Z = 0; else Velocity.Z = JumpZ; if (Base != None && !Base.bWorldGeometry && Base.Velocity.Z > 0.f) Velocity.Z += Base.Velocity.Z; SetPhysics(PHYS_Falling); return true; } return false; } // Nope. function bool CanBeGrabbed(KFPawn GrabbingPawn, optional bool bIgnoreFalling) { return false; } // UI stuff. simulated function String GetHumanReadableName() { if ( PlayerReplicationInfo != None ) return PlayerReplicationInfo.PlayerName; return ZombieName; } defaultproperties { bIgnoreBaseRotation=true MeleeDamage=22 HitsPerAttack=1 bCanCrouch=true FPHandOffset=(X=-40,Z=-70) FPHandsIdle="Walk_B_taunt_V2" MeleeHitDT=class'KFDT_ZombieHit' AccelRate=800 HPScaler=1 InventoryManagerClass=None // No weapons for bots! ZombieName="Zombie" PropDamageScale=0.65 HitZones(0)=(ZoneName="head",BoneName="head",GoreHealth=20,Limb=BP_Head,DmgScale=2) HitZones(1)=(ZoneName="neck",BoneName="neck",GoreHealth=20,Limb=BP_Head,DmgScale=2) HitZones(2)=(ZoneName="chest",BoneName="Spine2",GoreHealth=150,Limb=BP_Torso) HitZones(3)=(ZoneName="heart",BoneName="Spine2",GoreHealth=150,Limb=BP_Special) HitZones(4)=(ZoneName="lupperarm",BoneName="LeftArm",Limb=BP_LeftArm,DmgScale=0.4) HitZones(5)=(ZoneName="lforearm",BoneName="LeftForearm",GoreHealth=15,Limb=BP_LeftArm,DmgScale=0.4) HitZones(6)=(ZoneName="lhand",BoneName="LeftHand",GoreHealth=20,Limb=BP_LeftArm,DmgScale=0.4) HitZones(7)=(ZoneName="rupperarm",BoneName="RightArm",Limb=BP_RightArm,DmgScale=0.4) HitZones(8)=(ZoneName="rforearm",BoneName="RightForearm",GoreHealth=15,Limb=BP_RightArm,DmgScale=0.4) HitZones(9)=(ZoneName="rhand",BoneName="RightHand",GoreHealth=20,Limb=BP_RightArm,DmgScale=0.4) HitZones(10)=(ZoneName="stomach",BoneName="Spine1",GoreHealth=150,Limb=BP_Torso) HitZones(11)=(ZoneName="abdomen",BoneName="Hips",GoreHealth=150,Limb=BP_Torso) HitZones(12)=(ZoneName="lthigh",BoneName="LeftUpLeg",GoreHealth=75,Limb=BP_LeftLeg,DmgScale=0.75) HitZones(13)=(ZoneName="lcalf",BoneName="LeftLeg",GoreHealth=25,Limb=BP_LeftLeg,DmgScale=0.7) HitZones(14)=(ZoneName="lfoot",BoneName="LeftFoot",GoreHealth=15,Limb=BP_LeftLeg,DmgScale=0.7) HitZones(15)=(ZoneName="rthigh",BoneName="RightUpLeg",GoreHealth=75,Limb=BP_RightLeg,DmgScale=0.75) HitZones(16)=(ZoneName="rcalf",BoneName="RightLeg",GoreHealth=25,Limb=BP_RightLeg,DmgScale=0.7) HitZones(17)=(ZoneName="rfoot",BoneName="RightFoot",GoreHealth=15,Limb=BP_RightLeg,DmgScale=0.7) Begin Object Name=KFPawnSkeletalMeshComponent RBChannel=RBCC_Untitled3 End Object }