//============================================================================= // ZombieHusk //============================================================================= // Husk burned up fire projectile launching zed pawn class //============================================================================= // Killing Floor Source // Copyright (C) 2009 Tripwire Interactive LLC // - John "Ramm-Jaeger" Gibson //============================================================================= class NiceZombieHusk extends NiceZombieHuskBase; //---------------------------------------------------------------------------- // NOTE: All Variables are declared in the base class to eliminate hitching //---------------------------------------------------------------------------- var class HuskFireProjClass; simulated function HeatTick(){ if(heat < 50){ if(heat > 40) heat = 50; else heat = 50 - 0.8 * (50 - heat); } super.HeatTick(); } simulated function PostBeginPlay() { // Difficulty Scaling if (Level.Game != none && !bDiffAdjusted) { ProjectileFireInterval = default.ProjectileFireInterval * 0.6; } // and why TWI removed this feature... MyAmmo = spawn(AmmunitionClass); 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 && Physics != PHYS_Falling) { 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]; } 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 float GetStunDurationMult(Pawn instigatedBy, Vector hitLocation, Vector momentum, class damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){ if(headshotLevel > 0) return stunDurationMultiplier; return 0.5 * stunDurationMultiplier; } function SpawnTwoShots() { local vector X, Y, Z, FireStart; local rotator FireRotation; local KFMonsterController KFMonstControl; // can NOT shoot if brainless, dying, falling, being moved by other husks if (Controller == none || IsInState('ZombieDying') || IsInState('GettingOutOfTheWayOfShot') || Physics == PHYS_Falling) return; if (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; if (!SavedFireProperties.bInitialized) { SavedFireProperties.AmmoClass = MyAmmo.Class; SavedFireProperties.ProjectileClass = MyAmmo.ProjectileClass; SavedFireProperties.WarnTargetPct = MyAmmo.WarnTargetPct; SavedFireProperties.MaxRange = MyAmmo.MaxRange; SavedFireProperties.bTossed = MyAmmo.bTossed; SavedFireProperties.bTrySplash = MyAmmo.bTrySplash; SavedFireProperties.bLeadTarget = MyAmmo.bLeadTarget; SavedFireProperties.bInstantHit = MyAmmo.bInstantHit; 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) { // ignore zeds that the husk can't see, Joabyy if (KFMonstControl == none || KFMonstControl == controller || !LineOfSightTo(KFMonstControl)) continue; if (PointDistToLine(KFMonstControl.Pawn.Location, vector(FireRotation), FireStart) < 75) KFMonstControl.GetOutOfTheWayOfShot(vector(FireRotation), FireStart); } // added projectile owner... Spawn(SavedFireProperties.ProjectileClass, self, , 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 ModDamage(out int damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){ local float damageMod; if(damageType != none){ damageMod = 0.2 * damageType.default.freezePower; damageMod = FMax(damageMod, 1.0); } damage *= damageMod; Super.ModDamage(damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI); } 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); } } } function bool CheckStun(int stunScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){ if(Health > 0 && damageType != none && damageType.default.HeadShotDamageMult >= 1.2 && stunScore >= 250 && ( headshotLevel > 0.0) )//MEANTODO return true; return super.CheckStun(stunScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI); } 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 { AmmunitionClass=class'NiceZombieHuskAmmo' stunLoopStart=0.080000 stunLoopEnd=0.900000 idleInsertFrame=0.930000 Heat=50.000000 EventClasses(0)="NicePack.NiceZombieHusk" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Talk' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Jump' DetachedArmClass=Class'KFChar.SeveredArmHusk' DetachedLegClass=Class'KFChar.SeveredLegHusk' DetachedHeadClass=Class'KFChar.SeveredHeadHusk' DetachedSpecialArmClass=Class'KFChar.SeveredArmHuskGun' HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge' ControllerClass=class'NiceZombieHuskController' AmbientSound=Sound'KF_BaseHusk.Husk_IdleLoop' Mesh=SkeletalMesh'KF_Freaks2_Trip.Burns_Freak' Skins(0)=Texture'KF_Specimens_Trip_T_Two.burns.burns_tatters' }