class ZombieShiver extends KFMonster; #exec load obj file=ScrnZedPack_T.utx #exec load obj file=ScrnZedPack_S.uax #exec load obj file=ScrnZedPack_A.ukx var name WalkAnim, RunAnim; // Head twitch var rotator CurHeadRot, NextHeadRot, HeadRot; var float NextHeadTime; var float MaxHeadTime; var float MaxTilt, MaxTurn; // Targetting, charging var float TeleportBlockTime; var float HeadOffsetY; var transient bool bRunning, bClientRunning; var bool bDelayedReaction; var bool bCanSeeTarget; var float SeeTargetTime; var float RunUntilTime; var float RunCooldownEnd; var float PeriodSeeTarget; var float PeriodRunBase; var float PeriodRunRan; var float PeriodRunCoolBase; var float PeriodRunCoolRan; // Teleporting var byte FadeStage; var byte OldFadeStage; var float AlphaFader; var bool bFlashTeleporting; var float LastFlashTime; var float MinTeleportDist, MaxTeleportDist; var float MinLandDist, MaxLandDist; // How close we can teleport to the target (collision cylinders are taken into account) var int MaxTeleportAttempts; // Attempts per angle var int MaxTeleportAngles; var ColorModifier MatAlphaSkin; replication { reliable if (Role == ROLE_Authority) FadeStage, bRunning; } simulated function PostBeginPlay() { Super.PostBeginPlay(); if (Level.NetMode != NM_DedicatedServer) { MatAlphaSkin = ColorModifier(Level.ObjectPool.AllocateObject(class'ColorModifier')); if (MatAlphaSkin != none) { MatAlphaSkin.Color = class'Canvas'.static.MakeColor(255, 255, 255, 255); MatAlphaSkin.RenderTwoSided = false; MatAlphaSkin.AlphaBlend = true; MatAlphaSkin.Material = Skins[0]; Skins[0] = MatAlphaSkin; } } } simulated function PostNetReceive() { super.PostNetReceive(); if( bClientRunning != bRunning ) { bClientRunning = bRunning; if( bRunning ) { MovementAnims[0] = RunAnim; } else { MovementAnims[0] = WalkAnim; } } } simulated function Destroyed() { if (Level.NetMode != NM_DedicatedServer && MatAlphaSkin != none) { Skins[0] = default.Skins[0]; Level.ObjectPool.FreeObject(MatAlphaSkin); } Super.Destroyed(); } simulated function StopBurnFX() { if (bBurnApplied) { MatAlphaSkin.Material = Texture'PatchTex.Common.ZedBurnSkin'; Skins[0] = MatAlphaSkin; } Super.StopBurnFX(); } function RangedAttack(Actor A) { if (bShotAnim || Physics == PHYS_Swimming) return; else if (CanAttack(A)) { bShotAnim = true; SetAnimAction('Claw'); return; } } state Running { function BeginState() { bRunning = true; RunUntilTime = Level.TimeSeconds + PeriodRunBase + FRand() * PeriodRunRan; MovementAnims[0] = RunAnim; } function EndState() { bRunning = false; GroundSpeed = global.GetOriginalGroundSpeed(); RunCooldownEnd = Level.TimeSeconds + PeriodRunCoolBase + FRand() * PeriodRunCoolRan; MovementAnims[0] = WalkAnim; } function float GetOriginalGroundSpeed() { return global.GetOriginalGroundSpeed() * 2.5; } function Tick(float Delta) { Global.Tick(Delta); if (RunUntilTime < Level.TimeSeconds) GotoState(''); GroundSpeed = GetOriginalGroundSpeed(); } function bool CanSpeedAdjust() { return false; } } function TakeDamage(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector momentum, class DamType, optional int HitIndex) { if (InstigatedBy == none || class(DamType) == none) Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error else Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); } // returns true also for KnockDown (stun) animation -- PooSH simulated function bool AnimNeedsWait(name TestAnim) { if( TestAnim == 'DoorBash' || TestAnim == 'KnockDown' ) { return true; } return ExpectingChannel == 0; } simulated function float GetOriginalGroundSpeed() { local float result; result = OriginalGroundSpeed; if( bZedUnderControl ) result *= 1.25; if ( bBurnified ) result *= 0.8; return result; } simulated function Tick(float Delta) { Super.Tick(Delta); if (Health > 0 && !bBurnApplied) { // Handle targetting if (Level.NetMode != NM_Client && !bDecapitated) { if (Controller == none || Controller.Target == none || !Controller.LineOfSightTo(Controller.Target)) { if (bCanSeeTarget) bCanSeeTarget = false; } else { if (!bCanSeeTarget) { bCanSeeTarget = true; SeeTargetTime = Level.TimeSeconds; } else if (Level.TimeSeconds > SeeTargetTime + PeriodSeeTarget) { if (VSize(Controller.Target.Location - Location) < MaxTeleportDist) { if (VSize(Controller.Target.Location - Location) > MinTeleportDist || !Controller.ActorReachable(Controller.Target)) { if (CanTeleport()) StartTelePort(); } else { if (CanRun()) GotoState('Running'); } } } } } } // Handle client-side teleport variables if (!bBurnApplied) { if (Level.NetMode != NM_DedicatedServer && OldFadeStage != FadeStage) { OldFadeStage = FadeStage; if (FadeStage == 2) AlphaFader = 0; else AlphaFader = 255; } // Handle teleporting if (FadeStage == 1) // Fade out (pre-teleport) { AlphaFader = FMax(AlphaFader - Delta * 512, 0); if (Level.NetMode != NM_Client && AlphaFader == 0) { SetCollision(true, true); FlashTeleport(); SetCollision(false, false); FadeStage = 2; } } else if (FadeStage == 2) // Fade in (post-teleport) { AlphaFader = FMin(AlphaFader + Delta * 512, 255); if (Level.NetMode != NM_Client && AlphaFader == 255) { FadeStage = 0; SetCollision(true, true); GotoState('Running'); } } if (Level.NetMode != NM_DedicatedServer && ColorModifier(Skins[0]) != none) ColorModifier(Skins[0]).Color.A = AlphaFader; } } /* simulated function DebugHead() { local coords C; local vector HeadLoc; local float Radius; Radius = HeadRadius * HeadScale; ClearStayingDebugLines(); // based on head bone C = GetBoneCoords(HeadBone); HeadLoc = C.Origin + (HeadHeight * HeadScale * C.XAxis) + HeadOffsetY * C.YAxis; DrawDebugSphere(HeadLoc, Radius, 16, 0, 100, 255); DrawStayingDebugLine(HeadLoc - Radius*vect(1,0,0), HeadLoc + Radius*vect(1,0,0), 0, 100, 255); DrawStayingDebugLine(HeadLoc - Radius*vect(0,1,0), HeadLoc + Radius*vect(0,1,0), 0, 100, 255); DrawStayingDebugLine(HeadLoc - Radius*vect(0,0,1), HeadLoc + Radius*vect(0,0,1), 0, 100, 255); // based on OnlineHeadshotOffset HeadLoc = Location + (OnlineHeadshotOffset >> Rotation); Radius *= OnlineHeadshotScale; DrawDebugSphere(HeadLoc, Radius, 16, 255, 0, 0); DrawStayingDebugLine(HeadLoc - Radius*vect(1,0,0), HeadLoc + Radius*vect(1,0,0), 255, 0, 0); DrawStayingDebugLine(HeadLoc - Radius*vect(0,1,0), HeadLoc + Radius*vect(0,1,0), 255, 0, 0); DrawStayingDebugLine(HeadLoc - Radius*vect(0,0,1), HeadLoc + Radius*vect(0,0,1), 255, 0, 0); } */ function bool CanTeleport() { return !bFlashTeleporting && !bBurnified && Physics == PHYS_Walking && Level.TimeSeconds > TeleportBlockTime && LastFlashTime + 7.5 < Level.TimeSeconds; } function bool CanRun() { return (!bFlashTeleporting && !IsInState('Running') && RunCooldownEnd < Level.TimeSeconds); } function bool IsHeadShot(vector loc, vector ray, float AdditionalScale) { local coords C; local vector HeadLoc, B, M, diff; local float t, DotMM, Distance; local int look; local bool bUseAltHeadShotLocation; local bool bWasAnimating; if (HeadBone == '') return False; // If we are a dedicated server estimate what animation is most likely playing on the client if (Level.NetMode == NM_DedicatedServer) { if (Physics == PHYS_Falling) PlayAnim(AirAnims[0], 1.0, 0.0); else if (Physics == PHYS_Walking) { // Only play the idle anim if we're not already doing a different anim. // This prevents anims getting interrupted on the server and borking things up - Ramm if( !IsAnimating(0) && !IsAnimating(1) ) { if (bIsCrouched) { PlayAnim(IdleCrouchAnim, 1.0, 0.0); } else { bUseAltHeadShotLocation=true; } } else { bWasAnimating = true; } if ( bDoTorsoTwist ) { SmoothViewYaw = Rotation.Yaw; SmoothViewPitch = ViewPitch; look = (256 * ViewPitch) & 65535; if (look > 32768) look -= 65536; SetTwistLook(0, look); } } else if (Physics == PHYS_Swimming) PlayAnim(SwimAnims[0], 1.0, 0.0); if( !bWasAnimating ) { SetAnimFrame(0.5); } } if( bUseAltHeadShotLocation ) { HeadLoc = Location + (OnlineHeadshotOffset >> Rotation); AdditionalScale *= OnlineHeadshotScale; } else { C = GetBoneCoords(HeadBone); HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis) + HeadOffsetY * C.YAxis; } //ServerHeadLocation = HeadLoc; // Express snipe trace line in terms of B + tM B = loc; M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius); // Find Point-Line Squared Distance diff = HeadLoc - B; t = M Dot diff; if (t > 0) { DotMM = M dot M; if (t < DotMM) { t = t / DotMM; diff = diff - (t * M); } else { t = 1; diff -= M; } } else t = 0; Distance = Sqrt(diff Dot diff); return (Distance < (HeadRadius * HeadScale * AdditionalScale)); } function StartTeleport() { FadeStage = 1; AlphaFader = 255; SetCollision(false, false); bFlashTeleporting = true; } function FlashTeleport() { local Actor Target; local vector OldLoc; local vector NewLoc; local vector HitLoc; local vector HitNorm; local rotator RotOld; local rotator RotNew; local float LandTargetDist; local int iEndAngle; local int iAttempts; if (Controller == none || Controller.Target == none) return; Target = Controller.Target; RotOld = rotator(Target.Location - Location); RotNew = RotOld; OldLoc = Location; for (iEndAngle = 0; iEndAngle < MaxTeleportAngles; iEndAngle++) { RotNew = RotOld; RotNew.Yaw += iEndAngle * (65536 / MaxTelePortAngles); for (iAttempts = 0; iAttempts < MaxTeleportAttempts; iAttempts++) { LandTargetDist = Target.CollisionRadius + CollisionRadius + MinLandDist + (MaxLandDist - MinLandDist) * (iAttempts / (MaxTeleportAttempts - 1.0)); NewLoc = Target.Location - vector(RotNew) * LandTargetDist; // Target.Location - Location NewLoc.Z = Target.Location.Z; if (Trace(HitLoc, HitNorm, NewLoc + vect(0, 0, -500), NewLoc) != none) NewLoc.Z = HitLoc.Z + CollisionHeight; // Try a new location if (SetLocation(NewLoc)) { SetPhysics(PHYS_Walking); if (Controller.PointReachable(Target.Location)) { Velocity = vect(0, 0, 0); Acceleration = vect(0, 0, 0); SetRotation(rotator(Target.Location - Location)); PlaySound(Sound'ScrnZedPack_S.Shiver.ShiverWarpGroup', SLOT_Interact, 4.0); Controller.GotoState(''); MonsterController(Controller).WhatToDoNext(0); goto Teleported; } } // Reset location SetLocation(OldLoc); } } Teleported: bFlashTeleporting = false; LastFlashTime = Level.TimeSeconds; } function Died(Controller Killer, class damageType, vector HitLocation) { // (!) Super.Died(Killer, damageType, HitLocation); } function RemoveHead() { local class KFDamType; KFDamType = class(LastDamagedByType); if ( KFDamType != none && !KFDamType.default.bIsPowerWeapon && !KFDamType.default.bSniperWeapon && !KFDamType.default.bIsMeleeDamage && !KFDamType.default.bIsExplosive && !KFDamType.default.bDealBurningDamage && !ClassIsChildOf(KFDamType, class'DamTypeDualies') && !ClassIsChildOf(KFDamType, class'DamTypeMK23Pistol') && !ClassIsChildOf(KFDamType, class'DamTypeMagnum44Pistol') ) { LastDamageAmount *= 3.5; //significantly raise decapitation bonus for Assault Rifles //award shiver kill on decap for Commandos if ( KFPawn(LastDamagedBy)!=None && KFPlayerController(LastDamagedBy.Controller) != none && KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements) != none ) { KFDamType.Static.AwardKill( KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements), KFPlayerController(LastDamagedBy.Controller), self); } } if (IsInState('Running')) GotoState(''); Super.RemoveHead(); } function bool FlipOver() { if ( super.FlipOver() ) { TeleportBlockTime = Level.TimeSeconds + 4.0; // can't teleport during stun // do not rotate while stunned Controller.Focus = none; Controller.FocalPoint = Location + 512*vector(Rotation); } return false; } function PlayTakeHit(vector HitLocation, int Damage, class DamType) { if (Level.TimeSeconds - LastPainSound > MinTimeBetweenPainSounds) { LastPainSound = Level.TimeSeconds; PlaySound(HitSound[0], SLOT_Pain, 1.25,,400); } if (!IsInState('Running') && Level.TimeSeconds - LastPainAnim > MinTimeBetweenPainAnims) { PlayDirectionalHit(HitLocation); LastPainAnim = Level.TimeSeconds; } } simulated function int DoAnimAction( name AnimName ) { if (AnimName=='Claw' || AnimName=='Claw2' || AnimName=='Claw3') { AnimBlendParams(1, 1.0, 0.1,, FireRootBone); PlayAnim(AnimName,, 0.1, 1); return 1; } return Super.DoAnimAction(AnimName); } defaultproperties { WalkAnim="ClotWalk" RunAnim="Run" MaxHeadTime=0.100000 MaxTilt=10000.000000 MaxTurn=20000.000000 HeadOffsetY=-3.000000 bDelayedReaction=True PeriodSeeTarget=2.000000 PeriodRunBase=4.000000 PeriodRunRan=4.000000 PeriodRunCoolBase=4.000000 PeriodRunCoolRan=3.000000 AlphaFader=255.000000 MinTeleportDist=550.000000 MaxTeleportDist=2000.000000 MinLandDist=150.000000 MaxLandDist=500.000000 MaxTeleportAttempts=3 MaxTeleportAngles=3 MoanVoice=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' bCannibal=True MeleeDamage=8 damageForce=5000 KFRagdollName="Clot_Trip" JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump' CrispUpThreshhold=9 PuntAnim="ClotPunt" Intelligence=BRAINS_Mammal bUseExtendedCollision=True ColOffset=(Z=48.000000) ColRadius=25.000000 ColHeight=5.000000 ExtCollAttachBoneName="Collision_Attach" SeveredArmAttachScale=0.800000 SeveredLegAttachScale=0.800000 SeveredHeadAttachScale=0.800000 DetachedArmClass=Class'ScrnZedPack.SeveredArmShiver' DetachedLegClass=Class'ScrnZedPack.SeveredLegShiver' DetachedHeadClass=Class'ScrnZedPack.SeveredHeadShiver' PlayerCountHealthScale=0.200000 OnlineHeadshotOffset=(X=19.000000,Z=39.000000) OnlineHeadshotScale=1.400000 MotionDetectorThreat=0.340000 HitSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverPainGroup' DeathSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverDeathGroup' ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup' ScoringValue=15 GroundSpeed=100.000000 WaterSpeed=100.000000 AccelRate=1024.000000 JumpZ=340.000000 HealthMax=300.000000 Health=300 HeadRadius=8.000000 HeadHeight=3.000000 HeadScale=1.300000 MenuName="Shiver.se" MovementAnims(0)="ClotWalk" AmbientSound=SoundGroup'ScrnZedPack_S.Shiver.ShiverIdleGroup' Mesh=SkeletalMesh'ScrnZedPack_A.ShiverMesh' DrawScale=1.100000 PrePivot=(Z=5.000000) Skins(0)=Combiner'ScrnZedPack_T.Shiver.CmbRemoveAlpha' RotationRate=(Yaw=45000,Roll=0) }