// a really creepy camera that is always looking in your direction. // it can be destroyed if you shoot it // Author: Alex Quick class CreepyCamera extends Actor placeable; var () bool DebugCamera; // the player this camera is focusing on. var protected Pawn CameraTarget; // the rotation of the camera at level startup var protected Rotator InitialRotation; // limits on the rotation of the camera var () const float PitchLimit,YawLimit; // effect to spawn when this camera is destroyed var protected class DestructionEffect; simulated function PostBeginPlay() { InitialRotation = Rotation; DesiredRotation = InitialRotation; } simulated function Tick(float DeltaTime) { OrientCameraToLocalPlayer(); if(DebugCamera) { PrintDebugLogs(); } } simulated function PrintDebugLogs() { log("======= CAMERA DEBUG ================"); log(" -->"@Self@" TARGET : "@CameraTarget@"InitialRotation :"@InitialRotation@" DesiredRotation :"@DesiredRotation); } // turn the camera so its looking at the local player simulated function OrientCameraToLocalPlayer() { local rotator NewRotation; local byte LockYaw,LockPitch; if(CameraTarget == none) { FindCameraTarget(); } else { NewRotation = Rotator((CameraTarget.Location + (Vect(0,0,1)* CameraTarget.EyeHeight)) - Location) ; // If the desired rotation goes beyond the camera's limit, stop turning it. if(CameraTarget.Health <= 0 || CameraTarget.bDeleteMe) { StopTracking(); return; } if(IsOutSideViewFrustrum(CameraTarget,LockYaw,LockPitch)) { StopTracking(); } else { if(LockPitch == 0) { DesiredRotation.Pitch = NewRotation.Pitch; } if(LockYaw == 0) { DesiredRotation.Yaw = NewRotation.Yaw; } } } } simulated function StopTracking() { CameraTarget = none; NetUpdateFrequency = 0.1; // camera isn't really do anything at the moment, so it doesn't need frequent replication updates. } simulated function bool IsOutsideViewFrustrum(Pawn TestTarget, optional out byte LockYaw, optional out byte LockPitch) { local Rotator RotExtent,IntendedRotation; IntendedRotation = Rotator((TestTarget.Location + (Vect(0,0,1)* TestTarget.EyeHeight)) - Location) ; RotExtent = IntendedRotation - InitialRotation; RotExtent.Yaw = RotExtent.Yaw & 65535; if( RotExtent.Yaw > YawLimit && RotExtent.Yaw < (65535+ (-YawLimit)) ) { LockYaw = 1; } RotExtent.Pitch = RotExtent.Pitch & 65535; if( RotExtent.Pitch > PitchLimit && RotExtent.Pitch < (65535+ (-PitchLimit)) ) { LockPitch = 1; } return LockPitch == 1 && LockYaw == 1; } simulated function FindCameraTarget() { // do this separately on each client so the camera is looking at them. if(Level.NetMode == NM_DedicatedServer) { return; } if(Level.GetLocalPlayerController().Pawn != none && Level.GetLocalPlayerController().Pawn.Health > 0) { CameraTarget = Level.GetLocalPlayerController().Pawn; NetUpdateTime = Level.TimeSeconds - 1; NetUpdateFrequency = default.NetUpdateFrequency; } } simulated function TakeDamage(int Damage, Pawn EventInstigator, vector HitLocation, vector Momentum, class DamageType, optional int HitIndex) { DestroyCamera(EventInstigator); } function DestroyCamera(Pawn Killer) { TriggerEvent(Event,self,Killer); bHidden = true; SetCollision(false,false); bSkipActorPropertyReplication = false; NetUpdateTime = Level.TimeSeconds - 1; if(Level.NetMode != NM_DedicatedServer) { SpawnDestructionEffect(); } } simulated function SpawnDestructionEffect() { Spawn(DestructionEffect); } simulated event PostNetReceive() { if ( bHidden ) { SpawnDestructionEffect(); bNetNotify = false; // finished } } defaultproperties { bDirectional = true CollisionRadius = 15 Collisionheight = 15 bCollideActors = true bBlockActors = true bUseCylinderCollision=true bBlockZeroExtentTraces=true bCanBeDamaged=true DestructionEffect = class 'Emitter_BreakerExplosion' DrawType = DT_StaticMesh StaticMesh = StaticMesh'FrightYard_SM.Camera.Trader_Security_Cam' PitchLimit = 10000 YawLimit = 16000 RotationRate=(Pitch=10000,Yaw=10000,Roll=0) bFixedRotationDir = false bRotateToDesired = true Physics = PHYS_Rotating // network bOnlyDirtyReplication=true bNoDelete=true bReplicateMovement=false bAlwaysRelevant=true bSkipActorPropertyReplication=true bNetNotify=true RemoteRole=Role_SimulatedProxy NetUpdateFrequency=0.1 }