// Zombie Monster for KF Invasion gametype class NiceZombieBloat extends NiceZombieBloatBase; #exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax //---------------------------------------------------------------------------- // NOTE: All Variables are declared in the base class to eliminate hitching //---------------------------------------------------------------------------- var class BileExplosion; var class BileExplosionHeadless; function bool FlipOver() { return true; } // 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('ZombieBarf'); } 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 || VSize(A.Location-Location) <= 250) && !bDecapitated ) { bShotAnim = true; // Randomly do a moving attack so the player can't kite the zed if( FRand() < 0.8 ) { SetAnimAction('ZombieBarfMoving'); RunAttackTimeout = GetAnimDuration('ZombieBarf', 1.0); bMovingPukeAttack=true; } else { SetAnimAction('ZombieBarf'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); } // Randomly send out a message about Bloat Vomit burning(3% chance) if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none ) { PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, ""); } } } // Overridden to handle playing upper body only attacks when moving simulated event SetAnimAction(name NewAction) { local int meleeAnimIndex; local bool bWantsToAttackAndMove; if( NewAction=='' ) Return; bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving'; if( NewAction == 'Claw' ) { meleeAnimIndex = Rand(3); NewAction = meleeAnims[meleeAnimIndex]; } if( bWantsToAttackAndMove ) { ExpectingChannel = AttackAndMoveDoAnimAction(NewAction); } else { 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; } } // Handle playing the anim action on the upper body only if we're attacking and moving simulated function int AttackAndMoveDoAnimAction( name AnimName ) { if( AnimName=='ZombieBarfMoving' ) { AnimBlendParams(1, 1.0, 0.0,, FireRootBone); PlayAnim('ZombieBarf',, 0.1, 1); return 1; } return super.DoAnimAction( AnimName ); } function PlayDyingSound() { if( Level.NetMode!=NM_Client ) { if ( bGibbed ) { PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525); return; } if( bDecapitated ) { PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525); } else { PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525); } } } // Barf Time. function SpawnTwoShots() { local vector X,Y,Z, FireStart; local rotator FireRotation; // check this from the very start to prevent any log spam if (Controller == none || IsInState('ZombieDying')) 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 = Location+(vect(30,0,64) >> Rotation)*DrawScale; if ( !SavedFireProperties.bInitialized ) { SavedFireProperties.AmmoClass = Class'SkaarjAmmo'; // use fixed bile class, to prevent super high damage to zeds SavedFireProperties.ProjectileClass = Class'NiceBloatVomit'; SavedFireProperties.WarnTargetPct = 1; SavedFireProperties.MaxRange = 500; SavedFireProperties.bTossed = False; SavedFireProperties.bTrySplash = False; SavedFireProperties.bLeadTarget = True; SavedFireProperties.bInstantHit = True; SavedFireProperties.bInitialized = True; } // Turn off extra collision before spawning vomit, otherwise spawn fails ToggleAuxCollision(false); FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600); Spawn(SavedFireProperties.ProjectileClass,,,FireStart,FireRotation); FireStart-=(0.5*CollisionRadius*Y); FireRotation.Yaw -= 1200; spawn(SavedFireProperties.ProjectileClass,,,FireStart, FireRotation); FireStart+=(CollisionRadius*Y); FireRotation.Yaw += 2400; spawn(SavedFireProperties.ProjectileClass,,,FireStart, FireRotation); // Turn extra collision back on ToggleAuxCollision(true); } simulated function Tick(float deltatime) { local vector BileExplosionLoc; local FleshHitEmitter GibBileExplosion; Super.tick(deltatime); if( Role == ROLE_Authority && bMovingPukeAttack ) { // Keep moving toward the target until the timer runs out (anim finishes) if( RunAttackTimeout > 0 ) { RunAttackTimeout -= DeltaTime; if( RunAttackTimeout <= 0 ) { RunAttackTimeout = 0; bMovingPukeAttack=false; } } // Keep the gorefast moving toward its target when attacking if( bShotAnim && !bWaitForAnim ) { if( LookTarget!=none ) { Acceleration = AccelRate * Normal(LookTarget.Location - Location); } } } // 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; } } if ( Level.NetMode!=NM_DedicatedServer && /*Gored>0*/Health <= 0 && !bPlayBileSplash && HitDamageType != class'DamTypeBleedOut' ) { if ( !class'GameInfo'.static.UseLowGore() ) { // remove self. BileExplosionLoc = Location; BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5)); if (bDecapitated) { GibBileExplosion = Spawn(BileExplosionHeadless,self,, BileExplosionLoc ); } else { GibBileExplosion = Spawn(BileExplosion,self,, BileExplosionLoc ); } bPlayBileSplash = true; } else { BileExplosionLoc = Location; BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5)); GibBileExplosion = Spawn(class'LowGoreBileExplosion',self,, BileExplosionLoc ); bPlayBileSplash = true; } } } function BileBomb() { BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity)); } function PlayDyingAnimation(class DamageType, vector HitLoc) { // local bool AttachSucess; super.PlayDyingAnimation(DamageType, HitLoc); // Don't blow up with bleed out if( bDecapitated && DamageType == class'DamTypeBleedOut' ) { return; } if ( !class'GameInfo'.static.UseLowGore() ) { HideBone(SpineBone2); } if(Role == ROLE_Authority) { BileBomb(); // if(BloatJet!=none) // { // if(Gored < 5) // AttachSucess=AttachToBone(BloatJet,FireRootBone); // // else // // AttachSucess=AttachToBone(BloatJet,SpineBone1); // // if(!AttachSucess) // { // log("DEAD Bloaty Bile didn't like the Boning :o"); // BloatJet.SetBase(self); // } // BloatJet.SetRelativeRotation(rot(0,-4096,0)); // } } } 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; switch( HitFX[SimHitFxTicker].bone ) { 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; } return; } 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( 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 ) ; 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; } // Don't do this right now until we get the effects sorted - Ramm if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone && HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone && HitFX[SimHitFXTicker].bone != 'head' && Health <=0 ) HideBone(HitFX[SimHitFxTicker].bone); } } } simulated function HideBone(name boneName) { local int BoneScaleSlot; local coords boneCoords; local bool bValidBoneToHide; if( boneName == LeftThighBone ) { boneScaleSlot = 0; bValidBoneToHide = true; if( SeveredLeftLeg == none ) { SeveredLeftLeg = Spawn(SeveredLegAttachClass,self); SeveredLeftLeg.SetDrawScale(SeveredLegAttachScale); boneCoords = GetBoneCoords( 'lleg' ); AttachEmitterEffect( LimbSpurtEmitterClass, 'lleg', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredLeftLeg, 'lleg'); } } else if ( boneName == RightThighBone ) { boneScaleSlot = 1; bValidBoneToHide = true; if( SeveredRightLeg == none ) { SeveredRightLeg = Spawn(SeveredLegAttachClass,self); SeveredRightLeg.SetDrawScale(SeveredLegAttachScale); boneCoords = GetBoneCoords( 'rleg' ); AttachEmitterEffect( LimbSpurtEmitterClass, 'rleg', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredRightLeg, 'rleg'); } } else if( boneName == RightFArmBone ) { boneScaleSlot = 2; bValidBoneToHide = true; if( SeveredRightArm == none ) { SeveredRightArm = Spawn(SeveredArmAttachClass,self); SeveredRightArm.SetDrawScale(SeveredArmAttachScale); boneCoords = GetBoneCoords( 'rarm' ); AttachEmitterEffect( LimbSpurtEmitterClass, 'rarm', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredRightArm, 'rarm'); } } else if ( boneName == LeftFArmBone ) { boneScaleSlot = 3; bValidBoneToHide = true; if( SeveredLeftArm == none ) { SeveredLeftArm = Spawn(SeveredArmAttachClass,self); SeveredLeftArm.SetDrawScale(SeveredArmAttachScale); boneCoords = GetBoneCoords( 'larm' ); AttachEmitterEffect( LimbSpurtEmitterClass, 'larm', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredLeftArm, 'larm'); } } else if ( boneName == HeadBone ) { // Only scale the bone down once if( SeveredHead == none ) { bValidBoneToHide = true; boneScaleSlot = 4; SeveredHead = Spawn(SeveredHeadAttachClass,self); SeveredHead.SetDrawScale(SeveredHeadAttachScale); boneCoords = GetBoneCoords( 'neck' ); AttachEmitterEffect( NeckSpurtEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) ); AttachToBone(SeveredHead, 'neck'); } else { return; } } else if ( boneName == 'spine' ) { bValidBoneToHide = true; boneScaleSlot = 5; } else if ( boneName == SpineBone2 ) { bValidBoneToHide = true; boneScaleSlot = 6; } // Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones if( bValidBoneToHide ) { SetBoneScale(BoneScaleSlot, 0.0, BoneName); } } State Dying { function tick(float deltaTime) { if (BloatJet != none) { BloatJet.SetLocation(location); BloatJet.SetRotation(GetBoneRotation(FireRootBone)); } super.tick(deltaTime); } } 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){ if(damageType == class 'DamTypeVomit' || damageType == class 'DamTypeBlowerThrower') Damage = 0; else Super.ModDamage(Damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI); } static simulated function PreCacheStaticMeshes(LevelInfo myLevel) {//should be derived and used. Super.PreCacheStaticMeshes(myLevel); myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.limbs.bloat_head'); } static simulated function PreCacheMaterials(LevelInfo myLevel) { myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_cmb'); myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_env_cmb'); myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.bloat_diffuse'); } defaultproperties { BileExplosion=Class'KFMod.BileExplosion' BileExplosionHeadless=Class'KFMod.BileExplosionHeadless' stunLoopStart=0.100000 stunLoopEnd=0.600000 idleInsertFrame=0.950000 EventClasses(0)="NicePack.NiceZombieBloat" MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Talk' MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer' JumpSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Jump' DetachedArmClass=Class'KFChar.SeveredArmBloat' DetachedLegClass=Class'KFChar.SeveredLegBloat' DetachedHeadClass=Class'KFChar.SeveredHeadBloat' HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Pain' DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Death' ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge' ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge' ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge' ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge' AmbientSound=Sound'KF_BaseBloat.Bloat_Idle1Loop' Mesh=SkeletalMesh'KF_Freaks_Trip.Bloat_Freak' Skins(0)=Combiner'KF_Specimens_Trip_T.bloat_cmb' }