//============================================================================= // The moving brush class. // This is a built-in Unreal class and it shouldn't be modified. // Note that movers by default have bNoDelete==true. This makes movers and their default properties // remain on the client side. If a mover subclass has bNoDelete=false, then its default properties must // be replicated //============================================================================= class Mover extends Actor native nativereplication; // How the mover should react when it encroaches an actor. var() enum EMoverEncroachType { ME_StopWhenEncroach, // Stop when we hit an actor. ME_ReturnWhenEncroach, // Return to previous position when we hit an actor. ME_CrushWhenEncroach, // Crush the poor helpless actor. ME_IgnoreWhenEncroach, // Ignore encroached actors. } MoverEncroachType; // How the mover moves from one position to another. var() enum EMoverGlideType { MV_MoveByTime, // Move linearly. MV_GlideByTime, // Move with smooth acceleration. } MoverGlideType; // What classes can bump trigger this mover var() enum EBumpType { BT_PlayerBump, // Can only be bumped by player. BT_PawnBump, // Can be bumped by any pawn BT_AnyBump, // Can be bumped by any solid actor } BumpType; //----------------------------------------------------------------------------- // Keyframe numbers. var() byte KeyNum; // Current or destination keyframe. var byte PrevKeyNum; // Previous keyframe. var() const byte NumKeys; // Number of keyframes in total (0-3). var() const byte WorldRaytraceKey; // Raytrace the world with the brush here. var() const byte BrushRaytraceKey; // Raytrace the brush here. var byte StartKeyNum; //----------------------------------------------------------------------------- // Movement parameters. var() float MoveTime; // Time to spend moving between keyframes. var() float StayOpenTime; // How long to remain open before closing. var() float OtherTime; // TriggerPound stay-open time. var() int EncroachDamage; // How much to damage encroached actors. //----------------------------------------------------------------------------- // Mover state. var() bool bToggleDirection; var() bool bTriggerOnceOnly; // Go dormant after first trigger. var() bool bSlave; // This brush is a slave. var() bool bUseTriggered; // Triggered by player grab var() bool bDamageTriggered; // Triggered by taking damage var() bool bDynamicLightMover; // Apply dynamic lighting to mover. var() bool bUseShortestRotation; // rot by -90 instead of +270 and so on. var(ReturnGroup) bool bIsLeader; var() name PlayerBumpEvent; // Optional event to cause when the player bumps the mover. var() name BumpEvent; // Optional event to cause when any valid bumper bumps the mover. var actor SavedTrigger; // Who we were triggered by. var() float DamageThreshold; // minimum damage to trigger var int numTriggerEvents; // number of times triggered ( count down to untrigger ) var Mover Leader; // for having multiple movers return together var Mover Follower; var(ReturnGroup) name ReturnGroup; // if none, same as tag var() float DelayTime; // delay before starting to open //----------------------------------------------------------------------------- // Audio. var(MoverSounds) sound OpeningSound; // When start opening. var(MoverSounds) sound OpenedSound; // When finished opening. var(MoverSounds) sound ClosingSound; // When start closing. var(MoverSounds) sound ClosedSound; // When finish closing. var(MoverSounds) sound MoveAmbientSound; // Optional ambient sound when moving. var(MoverSounds) sound LoopSound; // Played on Loop //----------------------------------------------------------------------------- // Events var(MoverEvents) name OpeningEvent; // Event to cause when opening var(MoverEvents) name OpenedEvent; // Event to cause when opened var(MoverEvents) name ClosingEvent; // Event to cause when closing var(MoverEvents) name ClosedEvent; // Event to cause when closed var(MoverEvents) name LoopEvent; // Event to cause when the mover loops //----------------------------------------------------------------------------- // Other stuff //----------------------------------------------------------------------------- // Internal. var vector KeyPos[24]; var rotator KeyRot[24]; var vector BasePos, OldPos, OldPrePivot, SavedPos; var rotator BaseRot, OldRot, SavedRot; var float PhysAlpha; // Interpolating position, 0.0-1.0. var float PhysRate; // Interpolation rate per second. // AI related var NavigationPoint myMarker; var bool bOpening, bDelaying, bClientPause; var bool bClosed; // mover is in closed position, and no longer moving var bool bPlayerOnly; var(AI) bool bAutoDoor; // automatically setup Door NavigationPoint for this mover var(AI) bool bNoAIRelevance; // don't warn about this mover during path review var bool bJumpLift; var() bool bOscillatingLoop; // Goes from 0 up to X then back down to 0 // for client side replication var byte ClientStop; var vector SimOldPos; var int SimOldRotPitch, SimOldRotYaw, SimOldRotRoll; var vector SimInterpolate; var vector RealPosition; var rotator RealRotation; var int ClientUpdate; var byte StoppedPosition; // Used for Oscillation var int StepDirection; // 1 = Moving forward, -1 moving Backward // Used for controlling antiportals var array AntiPortals; var() name AntiPortalTag; // Resetting var bool bResetting, BACKUP_bHidden; var name Backup_InitialState; replication { // Things the server should send to the client. reliable if( Role==ROLE_Authority ) SimOldPos, SimOldRotPitch, SimOldRotYaw, SimOldRotRoll, SimInterpolate, RealPosition, RealRotation, StoppedPosition; } function bool SelfTriggered() { return true; } function Actor SpecialHandling(Pawn Other) { local Actor A; if ( myMarker != None ) { A = MyMarker.SpecialHandling(Other); if ( A == None ) return MyMarker; return A; } return self; } /* StartInterpolation() when this function is called, the actor will start moving along an interpolation path beginning at Dest */ simulated function StartInterpolation() { GotoState(''); bInterpolating = true; SetPhysics(PHYS_None); } simulated function Timer() { if ( Velocity != vect(0,0,0) ) { if ( bClientPause ) bClientPause = false; return; } if ( Level.NetMode == NM_Client && !bClientAuthoritative ) { if ( ClientUpdate == 0 ) // not doing a move { if ( bClientPause ) { if ( VSize(RealPosition - Location) > 3 ) SetLocation(RealPosition); else RealPosition = Location; SetRotation(RealRotation); bClientPause = false; } else if ( RealPosition != Location ) bClientPause = true; } else bClientPause = false; } else { if ( bCollideActors ) { if ( RealRotation != Rotation ) RealRotation = Rotation; if ( RealPosition != Location ) RealPosition = Location; } } } //----------------------------------------------------------------------------- // Movement functions. // Interpolate to keyframe KeyNum in Seconds time. simulated final function InterpolateTo( byte NewKeyNum, float Seconds ) { local Mover M; ForEach BasedActors(class'Mover', M) M.BaseStarted(); NewKeyNum = Clamp( NewKeyNum, 0, ArrayCount(KeyPos)-1 ); if( NewKeyNum==PrevKeyNum && KeyNum!=PrevKeyNum ) { // Reverse the movement smoothly. PhysAlpha = 1.0 - PhysAlpha; OldPos = BasePos + KeyPos[KeyNum]; OldRot = BaseRot + KeyRot[KeyNum]; } else { // Start a new movement. OldPos = Location; OldRot = Rotation; PhysAlpha = 0.0; } if ( bResetting ) // resetting mover, do it at max speed Seconds = 0.005; // Setup physics. SetPhysics(PHYS_MovingBrush); bInterpolating = true; PrevKeyNum = KeyNum; KeyNum = NewKeyNum; PhysRate = 1.0 / FMax(Seconds, 0.005); ClientUpdate++; SimOldPos = OldPos; SimOldRotYaw = OldRot.Yaw; SimOldRotPitch = OldRot.Pitch; SimOldRotRoll = OldRot.Roll; SimInterpolate.X = 100 * PhysAlpha; SimInterpolate.Y = 100 * FMax(0.01, PhysRate); SimInterpolate.Z = 256 * PrevKeyNum + KeyNum; NetUpdateTime = Level.TimeSeconds - 1; } // Set the specified keyframe. final function SetKeyframe( byte NewKeyNum, vector NewLocation, rotator NewRotation ) { KeyNum = Clamp( NewKeyNum, 0, ArrayCount(KeyPos)-1 ); KeyPos[KeyNum] = NewLocation; KeyRot[KeyNum] = NewRotation; } // Interpolation ended. simulated event KeyFrameReached() { local byte OldKeyNum; local Mover M; OldKeyNum = PrevKeyNum; PrevKeyNum = KeyNum; PhysAlpha = 0; ClientUpdate--; // If more than two keyframes, chain them. if( KeyNum>0 && KeyNumOldKeyNum ) { // Chain to next. InterpolateTo(KeyNum+1,MoveTime); } else { // Finished interpolating. AmbientSound = None; NetUpdateTime = Level.TimeSeconds - 1; if ( bJumpLift && (KeyNum == 1) ) FinishNotify(); if ( (ClientUpdate == 0) && ((Level.NetMode != NM_Client) || bClientAuthoritative) ) { RealPosition = Location; RealRotation = Rotation; ForEach BasedActors(class'Mover', M) M.BaseFinished(); } } } //----------------------------------------------------------------------------- // Mover functions. // Notify AI that mover finished movement function FinishNotify() { local Controller C; for ( C=Level.ControllerList; C!=None; C=C.nextController ) if ( (C.Pawn != None) && (C.PendingMover == self) ) C.MoverFinished(); } // Handle when the mover finishes closing. function FinishedClosing() { local Mover M; // Update sound effects. PlaySound( ClosedSound, SLOT_None, SoundVolume / 255.0, false, SoundRadius, SoundPitch / 64.0); // Handle Events TriggerEvent( ClosedEvent, Self, Instigator ); // Notify our triggering actor that we have completed. if( SavedTrigger != None ) SavedTrigger.EndEvent(); SavedTrigger = None; Instigator = None; If ( MyMarker != None ) MyMarker.MoverClosed(); bClosed = true; FinishNotify(); for ( M=Leader; M!=None; M=M.Follower ) if ( !M.bClosed ) return; UnTriggerEvent(OpeningEvent, Self, Instigator); } // Handle when the mover finishes opening. function FinishedOpening() { // Update sound effects. PlaySound( OpenedSound, SLOT_None, SoundVolume / 255.0, false, SoundRadius, SoundPitch / 64.0); // Trigger any chained movers / Events TriggerEvent(Event, Self, Instigator); TriggerEvent(OpenedEvent, Self, Instigator); If ( MyMarker != None ) MyMarker.MoverOpened(); FinishNotify(); } // Open the mover. function DoOpen() { bOpening = true; bDelaying = false; InterpolateTo( 1, MoveTime ); MakeNoise(1.0); PlaySound( OpeningSound, SLOT_None, SoundVolume / 255.0, false, SoundRadius, SoundPitch / 64.0); AmbientSound = MoveAmbientSound; TriggerEvent(OpeningEvent, Self, Instigator); if ( Follower != None ) Follower.DoOpen(); } // Close the mover. function DoClose() { bOpening = false; bDelaying = false; InterpolateTo( Max(0,KeyNum-1), MoveTime ); MakeNoise(1.0); PlaySound( ClosingSound, SLOT_None, SoundVolume / 255.0, false, SoundRadius, SoundPitch / 64.0); UntriggerEvent(Event, self, Instigator); AmbientSound = MoveAmbientSound; TriggerEvent(ClosingEvent,Self,Instigator); if ( Follower != None ) Follower.DoClose(); } //----------------------------------------------------------------------------- // Engine notifications. // When mover enters gameplay. simulated function BeginPlay() { local AntiPortalActor AntiPortalA; if(AntiPortalTag != '') { foreach AllActors(class'AntiPortalActor',AntiPortalA,AntiPortalTag) { AntiPortals.Length = AntiPortals.Length + 1; AntiPortals[AntiPortals.Length - 1] = AntiPortalA; } } // timer updates real position every second in network play if ( Level.NetMode != NM_Standalone ) { if ( Level.NetMode == NM_Client && bClientAuthoritative ) settimer(4.0, true); else settimer(1.0, true); if ( Role < ROLE_Authority ) return; } RealPosition = Location; RealRotation = Rotation; // Init key info. Super.BeginPlay(); KeyNum = Clamp( KeyNum, 0, ArrayCount(KeyPos)-1 ); PhysAlpha = 0.0; StartKeyNum = KeyNum; // Set initial location. Move( BasePos + KeyPos[KeyNum] - Location ); // Initial rotation. SetRotation( BaseRot + KeyRot[KeyNum] ); // find movers in same group if ( ReturnGroup == '' ) ReturnGroup = tag; Leader = None; Follower = None; } // Immediately after mover enters gameplay. function PostBeginPlay() { local mover M; RealRotation = Rotation; RealPosition = Location; Backup_InitialState = InitialState; BACKUP_bHidden = bHidden; // Initialize all slaves. if( !bSlave ) { foreach DynamicActors( class 'Mover', M, Tag ) { if( M.bSlave ) { M.GotoState(''); M.SetBase( Self ); } } } if ( bIsLeader ) { Leader = self; ForEach DynamicActors( class'Mover', M ) if ( (M != self) && (M.ReturnGroup == ReturnGroup) ) { M.Leader = self; M.Follower = Follower; Follower = M; } } else if ( Leader == None ) { // if no one in returngroup, I am the leader anyway ForEach DynamicActors( class'Mover', M ) { if ( (M != self) && (M.ReturnGroup == ReturnGroup) ) return; } Leader = self; } } function SetResetStatus( bool bNewStatus ) { NetUpdateTime = Level.TimeSeconds - 1; bHidden = BACKUP_bHidden; bResetting = bNewStatus; if ( Follower != None ) Follower.SetResetStatus( bNewStatus ); } function Reset() { SetResetStatus( true ); DoClose(); SetResetStatus( false ); EnableTrigger(); GotoState(Backup_InitialState, ''); } function MakeGroupStop() { // Stop moving immediately. bInterpolating = false; NetUpdateTime = Level.TimeSeconds - 1; AmbientSound = None; GotoState( , '' ); if ( Follower != None ) Follower.MakeGroupStop(); } function MakeGroupReturn() { // Abort move and reverse course. bInterpolating = false; NetUpdateTime = Level.TimeSeconds - 1; AmbientSound = None; if(bIsLeader || Leader==Self) { if( KeyNum damageType, optional int HitIndex) { if ( bDamageTriggered && (Damage >= DamageThreshold) ) { if ( (AIController(instigatedBy.Controller) != None) && (instigatedBy.Controller.Focus == self) ) instigatedBy.Controller.StopFiring(); self.Trigger(self, instigatedBy); if ( (AIController(instigatedBy.Controller) != None) && (instigatedBy.Controller.Target == self) ) instigatedBy.Controller.StopFiring(); } } //======================================================================== // Master State for OpenTimed mover states (for movers that open and close) // Laurent -- Moved out of state for reset function DisableTrigger(); function EnableTrigger(); state OpenTimedMover { function bool ShouldReTrigger() { return( false ); } function Reset() { SetResetStatus( true ); GotoState(Backup_InitialState, 'Close'); } function BeginState() { EnableTrigger(); } Open: if ( bTriggerOnceOnly ) Disable('Trigger'); bClosed = false; DisableTrigger(); if ( DelayTime > 0 ) { bDelaying = true; Sleep(DelayTime); } DoOpen(); FinishInterpolation(); FinishedOpening(); Sleep( StayOpenTime ); if ( bTriggerOnceOnly ) GotoState('WasOpenTimedMover', ''); Close: DoClose(); FinishInterpolation(); FinishedClosing(); EnableTrigger(); if ( bResetting ) { SetResetStatus( false ); GotoState( Backup_InitialState, '' ); Stop; } Sleep( StayOpenTime ); if( ShouldReTrigger() ) { SavedTrigger = None; GotoState( 'StandOpenTimed', 'Open' ); } } state WasOpenTimedMover { function Reset() { SetResetStatus( true ); GotoState(Backup_InitialState, 'Close'); } } // Open when stood on, wait, then close. state() StandOpenTimed extends OpenTimedMover { function bool ShouldReTrigger() { local int i; for( i = 0; i < Attached.Length; i++ ) if( CanTrigger( Attached[i] ) ) return( true ); return( false ); } function bool CanTrigger( Actor Other ) { local pawn P; P = Pawn(Other); if ( (BumpType != BT_AnyBump) && (P == None) ) return (false); if ( (BumpType == BT_PlayerBump) && !P.IsPlayerPawn() ) return (false); if ( (BumpType == BT_PawnBump) && (Other.Mass < 10) ) return (false); TriggerEvent(BumpEvent, self, P); // gam -- this _might_ cause problems. Blame Goose. return( true ); } function Attach( actor Other ) { if( !CanTrigger( Other ) ) return; SavedTrigger = None; GotoState( 'StandOpenTimed', 'Open' ); } function DisableTrigger() { Disable( 'Attach' ); } function EnableTrigger() { Enable('Attach'); } } // Open when bumped, wait, then close. state() BumpOpenTimed extends OpenTimedMover { function Bump( actor Other ) { if ( (BumpType != BT_AnyBump) && (Pawn(Other) == None) ) return; if ( (BumpType == BT_PlayerBump) && !Pawn(Other).IsPlayerPawn() ) return; if ( (BumpType == BT_PawnBump) && (Other.Mass < 10) ) return; Global.Bump( Other ); SavedTrigger = None; Instigator = Pawn(Other); Instigator.Controller.WaitForMover(self); GotoState( 'BumpOpenTimed', 'Open' ); } function DisableTrigger() { Disable( 'Bump' ); } function EnableTrigger() { Enable('Bump'); } } // When triggered, open, wait, then close. state() TriggerOpenTimed extends OpenTimedMover { function bool SelfTriggered() { return false; } function Trigger( actor Other, pawn EventInstigator ) { SavedTrigger = Other; Instigator = EventInstigator; if ( SavedTrigger != None ) SavedTrigger.BeginEvent(); GotoState( 'TriggerOpenTimed', 'Open' ); } function DisableTrigger() { Disable('Trigger'); } function EnableTrigger() { Enable('Trigger'); } } state() LoopMove { function bool SelfTriggered() { return false; } event Trigger( actor Other, pawn EventInstigator ) { Disable ('Trigger'); Enable ('UnTrigger'); SavedTrigger = Other; Instigator = EventInstigator; if ( SavedTrigger != None ) SavedTrigger.BeginEvent(); bOpening = true; PlaySound( OpeningSound, SLOT_None ); AmbientSound = MoveAmbientSound; GotoState( 'LoopMove', 'Running' ); } event UnTrigger( actor Other, pawn EventInstigator ) { Disable ('UnTrigger'); Enable ('Trigger'); SavedTrigger = Other; Instigator = EventInstigator; GotoState( 'LoopMove', 'Stopping' ); } event KeyFrameReached() { } function BeginState() { bOpening = false; bDelaying = false; } Running: FinishInterpolation(); InterpolateTo( (KeyNum + 1) % NumKeys, MoveTime ); GotoState( 'LoopMove', 'Running' ); Stopping: FinishInterpolation(); FinishedOpening(); UnTriggerEvent(Event, self, Instigator); bOpening = false; Stop; } //================================================================= // Other Mover States // Toggle when triggered. state() TriggerToggle { function bool SelfTriggered() { return false; } function Reset() { super.Reset(); if ( bOpening || bDelaying ) { // Reset instantly SetResetStatus( true ); GotoState( 'TriggerToggle', 'Close' ); } } function Trigger( actor Other, pawn EventInstigator ) { SavedTrigger = Other; Instigator = EventInstigator; if ( SavedTrigger != None ) SavedTrigger.BeginEvent(); if( KeyNum==0 || KeyNum