//============================================================================= // ZombieHusk //============================================================================= // Husk burned up fire projectile launching zed pawn class //============================================================================= // Killing Floor Source // Copyright (C) 2009 Tripwire Interactive LLC // - John "Ramm-Jaeger" Gibson //============================================================================= class ZombieHusk extends ZombieHuskBase abstract; //---------------------------------------------------------------------------- // NOTE: All Variables are declared in the base class to eliminate hitching //---------------------------------------------------------------------------- var class HuskFireProjClass; simulated function PostBeginPlay() { // Difficulty Scaling if (Level.Game != none && !bDiffAdjusted) { if( Level.Game.GameDifficulty < 2.0 ) { ProjectileFireInterval = default.ProjectileFireInterval * 1.25; BurnDamageScale = default.BurnDamageScale * 2.0; } else if( Level.Game.GameDifficulty < 4.0 ) { ProjectileFireInterval = default.ProjectileFireInterval * 1.0; BurnDamageScale = default.BurnDamageScale * 1.0; } else if( Level.Game.GameDifficulty < 5.0 ) { ProjectileFireInterval = default.ProjectileFireInterval * 0.75; BurnDamageScale = default.BurnDamageScale * 0.75; } else // Hardest difficulty { ProjectileFireInterval = default.ProjectileFireInterval * 0.60; BurnDamageScale = default.BurnDamageScale * 0.5; } } super.PostBeginPlay(); } // don't interrupt the bloat while he is puking simulated function bool HitCanInterruptAction() { if( bShotAnim ) { return false; } return true; } function DoorAttack(Actor A) { if ( bShotAnim || Physics == PHYS_Swimming) return; else if ( A!=None ) { bShotAnim = true; if( !bDecapitated && bDistanceAttackingDoor ) { SetAnimAction('ShootBurns'); } else { SetAnimAction('DoorBash'); GotoState('DoorBashing'); } } } function RangedAttack(Actor A) { local int LastFireTime; if ( bShotAnim ) return; if ( Physics == PHYS_Swimming ) { SetAnimAction('Claw'); bShotAnim = true; LastFireTime = Level.TimeSeconds; } else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) { bShotAnim = true; LastFireTime = Level.TimeSeconds; SetAnimAction('Claw'); //PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this Controller.bPreparingMove = true; Acceleration = vect(0,0,0); } else if ( (KFDoorMover(A) != none || (!Region.Zone.bDistanceFog && VSize(A.Location-Location) <= 65535) || (Region.Zone.bDistanceFog && VSizeSquared(A.Location-Location) < (Square(Region.Zone.DistanceFogEnd) * 0.8))) // Make him come out of the fog a bit && !bDecapitated ) { bShotAnim = true; SetAnimAction('ShootBurns'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0); } } // Overridden to handle playing upper body only attacks when moving simulated event SetAnimAction(name NewAction) { local int meleeAnimIndex; local bool bWantsToAttackAndMove; if( NewAction=='' ) Return; if( NewAction == 'Claw' ) { meleeAnimIndex = Rand(3); NewAction = meleeAnims[meleeAnimIndex]; CurrentDamtype = ZombieDamType[meleeAnimIndex]; } else if( NewAction == 'DoorBash' ) { CurrentDamtype = ZombieDamType[Rand(3)]; } ExpectingChannel = DoAnimAction(NewAction); if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) ) { bWaitForAnim = true; } else { bWaitForAnim = false; } if( Level.NetMode!=NM_Client ) { AnimAction = NewAction; bResetAnimAct = True; ResetAnimActTime = Level.TimeSeconds+0.3; } } function SpawnTwoShots() { local vector X,Y,Z, FireStart; local rotator FireRotation; local KFMonsterController KFMonstControl; if( Controller!=None && KFDoorMover(Controller.Target)!=None ) { Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit'); return; } GetAxes(Rotation,X,Y,Z); FireStart = GetBoneCoords('Barrel').Origin; huskFireProjClass = Class'HuskFireProjectile'; if ( !SavedFireProperties.bInitialized ) { SavedFireProperties.AmmoClass = Class'SkaarjAmmo'; SavedFireProperties.ProjectileClass = HuskFireProjClass; SavedFireProperties.WarnTargetPct = 1; SavedFireProperties.MaxRange = 65535; SavedFireProperties.bTossed = False; SavedFireProperties.bTrySplash = true; SavedFireProperties.bLeadTarget = True; SavedFireProperties.bInstantHit = False; SavedFireProperties.bInitialized = True; } // Turn off extra collision before spawning vomit, otherwise spawn fails ToggleAuxCollision(false); FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600); foreach DynamicActors(class'KFMonsterController', KFMonstControl) { if( KFMonstControl != Controller ) { if( PointDistToLine(KFMonstControl.Pawn.Location, vector(FireRotation), FireStart) < 75 ) { KFMonstControl.GetOutOfTheWayOfShot(vector(FireRotation),FireStart); } } } Spawn(HuskFireProjClass,,,FireStart,FireRotation); // Turn extra collision back on ToggleAuxCollision(true); } // Get the closest point along a line to another point simulated function float PointDistToLine(vector Point, vector Line, vector Origin, optional out vector OutClosestPoint) { local vector SafeDir; SafeDir = Normal(Line); OutClosestPoint = Origin + (SafeDir * ((Point-Origin) dot SafeDir)); return VSize(OutClosestPoint-Point); } simulated function Tick(float deltatime) { Super.tick(deltatime); // Hack to force animation updates on the server for the bloat if he is relevant to someone // He has glitches when some of his animations don't play on the server. If we // find some other fix for the glitches take this out - Ramm if( Level.NetMode != NM_Client && Level.NetMode != NM_Standalone ) { if( (Level.TimeSeconds-LastSeenOrRelevantTime) < 1.0 ) { bForceSkelUpdate=true; } else { bForceSkelUpdate=false; } } } function RemoveHead() { bCanDistanceAttackDoors = False; Super.RemoveHead(); } function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class damageType, optional int HitIndex) { // Reduced damage from fire if (DamageType == class 'DamTypeBurned' || DamageType == class 'DamTypeFlamethrower') { Damage *= BurnDamageScale; } Super.TakeDamage(Damage,instigatedBy,hitlocation,momentum,damageType,HitIndex); } simulated function ProcessHitFX() { local Coords boneCoords; local class HitEffects[4]; local int i,j; local float GibPerterbation; if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh)) { SimHitFxTicker = HitFxTicker; return; } for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) ) { j++; if ( j > 30 ) { SimHitFxTicker = HitFxTicker; return; } if( (HitFX[SimHitFxTicker].damtype == None) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) ) continue; //log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype); if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore()) { SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1); bGibbed = true; // Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed if( Level.NetMode == NM_ListenServer ) { bDestroyNextTick = true; TimeSetDestroyNextTickTime = Level.TimeSeconds; } else { Destroy(); } return; } boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone ); if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() ) { //AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir ); HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health ); if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water { for( i = 0; i < ArrayCount(HitEffects); i++ ) { if( HitEffects[i] == None ) continue; AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir ); } } } if ( class'GameInfo'.static.UseLowGore() ) HitFX[SimHitFxTicker].bSever = false; if( HitFX[SimHitFxTicker].bSever ) { GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation; switch( HitFX[SimHitFxTicker].bone ) { case 'obliterate': break; case LeftThighBone: if( !bLeftLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bLeftLegGibbed=true; } break; case RightThighBone: if( !bRightLegGibbed ) { SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bRightLegGibbed=true; } break; case LeftFArmBone: if( !bLeftArmGibbed ) { SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;; bLeftArmGibbed=true; } break; case RightFArmBone: if( !bRightArmGibbed ) { SpawnSeveredGiblet( DetachedSpecialArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) ); KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ; bRightArmGibbed=true; } break; case 'head': if( !bHeadGibbed ) { if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true); } else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' ) { DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true); } bHeadGibbed=true; } break; } if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone && HitFX[SimHitFXTicker].bone != 'head' && Health <=0 ) HideBone(HitFX[SimHitFxTicker].bone); } } } // New Hit FX for Zombies! function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class damageType, vector Momentum, optional int HitIdx ) { local Vector HitNormal; local Vector HitRay ; local Name HitBone; local float HitBoneDist; local PlayerController PC; local bool bShowEffects, bRecentHit; local ProjectileBloodSplat BloodHit; local rotator SplatRot; bRecentHit = Level.TimeSeconds - LastPainTime < 0.2; LastDamageAmount = Damage; // Call the modified version of the original Pawn playhit OldPlayHit(Damage, InstigatedBy, HitLocation, DamageType,Momentum); if ( Damage <= 0 ) return; if( Health>0 && Damage>(float(Default.Health)/1.5) ) { FlipOver(); } else if( Health > 0 && (damageType == class'DamTypeCrossbow' || damageType == class'DamTypeCrossbowHeadShot' || damageType == class'DamTypeWinchester' || damageType == class'DamTypeM14EBR' || damageType == class'DamTypeM99HeadShot' || damageType == class'DamTypeM99SniperRifle' || damageType == class'DamTypeSPSniper' ) && Damage > 200 ) // 200 Damage will be a headshot with the Winchester or EBR, or a hit with the Crossbow { FlipOver(); } PC = PlayerController(Controller); bShowEffects = ( (Level.NetMode != NM_Standalone) || (Level.TimeSeconds - LastRenderTime < 2.5) || ((InstigatedBy != None) && (PlayerController(InstigatedBy.Controller) != None)) || (PC != None) ); if ( !bShowEffects ) return; if ( BurnDown > 0 && !bBurnified ) { bBurnified = true; } HitRay = vect(0,0,0); if( InstigatedBy != None ) HitRay = Normal(HitLocation-(InstigatedBy.Location+(vect(0,0,1)*InstigatedBy.EyeHeight))); if( DamageType.default.bLocationalHit ) { CalcHitLoc( HitLocation, HitRay, HitBone, HitBoneDist ); // Do a zapped effect is someone shoots us and we're zapped to help show that the zed is taking more damage if ( bZapped && DamageType.name != 'DamTypeZEDGun' ) { PlaySound(class'ZedGunProjectile'.default.ExplosionSound,,class'ZedGunProjectile'.default.ExplosionSoundVolume); Spawn(class'ZedGunProjectile'.default.ExplosionEmitter,,,HitLocation + HitNormal*20,rotator(HitNormal)); } } else { HitLocation = Location ; HitBone = FireRootBone; HitBoneDist = 0.0f; } if( DamageType.default.bAlwaysSevers && DamageType.default.bSpecial ) HitBone = 'head'; if( InstigatedBy != None ) HitNormal = Normal( Normal(InstigatedBy.Location-HitLocation) + VRand() * 0.2 + vect(0,0,2.8) ); else HitNormal = Normal( Vect(0,0,1) + VRand() * 0.2 + vect(0,0,2.8) ); //log("HitLocation "$Hitlocation) ; if ( DamageType.Default.bCausesBlood && (!bRecentHit || (bRecentHit && (FRand() > 0.8)))) { if ( !class'GameInfo'.static.NoBlood() && !class'GameInfo'.static.UseLowGore() ) { if ( Momentum != vect(0,0,0) ) SplatRot = rotator(Normal(Momentum)); else { if ( InstigatedBy != None ) SplatRot = rotator(Normal(Location - InstigatedBy.Location)); else SplatRot = rotator(Normal(Location - HitLocation)); } BloodHit = Spawn(ProjectileBloodSplatClass,InstigatedBy,, HitLocation, SplatRot); } } if( InstigatedBy != none && InstigatedBy.PlayerReplicationInfo != none && KFSteamStatsAndAchievements(InstigatedBy.PlayerReplicationInfo.SteamStatsAndAchievements) != none && Health <= 0 && Damage > DamageType.default.HumanObliterationThreshhold && Damage != 1000 && (!bDecapitated || bPlayBrainSplash) ) { KFSteamStatsAndAchievements(InstigatedBy.PlayerReplicationInfo.SteamStatsAndAchievements).AddGibKill(class(damageType) != none); if ( self.IsA('ZombieFleshPound') ) { KFSteamStatsAndAchievements(InstigatedBy.PlayerReplicationInfo.SteamStatsAndAchievements).AddFleshpoundGibKill(); } } DoDamageFX( HitBone, Damage, DamageType, Rotator(HitNormal) ); if (DamageType.default.DamageOverlayMaterial != None && Damage > 0 ) // additional check in case shield absorbed SetOverlayMaterial( DamageType.default.DamageOverlayMaterial, DamageType.default.DamageOverlayTime, false ); } static simulated function PreCacheMaterials(LevelInfo myLevel) {//should be derived and used. myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T_Two.burns_diff'); myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T_Two.burns_emissive_mask'); myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_energy_cmb'); myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_env_cmb'); myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_fire_cmb'); myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T_Two.burns_shdr'); myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T_Two.burns_cmb'); } defaultproperties { //------------------------------------------------------------------------------- // NOTE: Most Default Properties are set in the base class to eliminate hitching //------------------------------------------------------------------------------- EventClasses(0)="KFChar.ZombieHusk_STANDARD" HuskFireProjClass=Class'HuskFireProjectile' ControllerClass=Class'KFChar.HuskZombieController' }