Class VSZombie extends KFPawn_Monster abstract; var() class MeleeHitDT; var() localized 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, optional bool bAllowSameTeamGrab) { 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! 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 }