Legacy:Mover - Unreal Wiki

# Legacy:Mover

### From Unreal Wiki, The Unreal Engine Documentation Site

UT2004 :: Actor >> Mover (Package: Engine)

A StaticMesh actor that can move between predefined key positions and rotations.

See Mover (UT) for the UT version of this class, which uses a brush instead of a StaticMesh.

In UT3 movers have been replaced by InterpActors in combination with Kismet for activation control and Matinee for movement path definition.

## Properties

### Main

name AntiPortalTag
  Tag of attached AntiPortalActor.
bool bDamageTriggered
  Triggered by taking damage
bool bDynamicLightMover
  Apply dynamic lighting to mover.
bool bOscillatingLoop
  Goes from 0 up to X then back down to 0
byte BrushRaytraceKey
  Raytrace the brush here.
bool bSlave
  This brush is a slave.
bool bToggleDirection
  If this Mover is in the RotatingMover state, toggle directions between "Triggerings".
bool bTriggerOnceOnly
  Go dormant after first trigger.
name BumpEvent
  Optional event to cause when any valid bumper bumps the mover.
EBumpType BumpType
  Determines what actors can bump-trigger the mover (see declaration for EBumpType below).
bool bUseShortestRotation
  Rot by -90 instead of +270, etc. If end key was 360 and start key is 0, this means it won't have to rotate at all.
bool bUseTriggered
  This Mover is Triggered by any Touching player who presses the "Use" key.
float DamageThreshold
  Minimum damage to trigger
float DelayTime
  Delay before starting to open
int EncroachDamage
  How much to damage encroached actors. Note: This damage is taken by encroached actors before any `MoverEncroachType` reaction is applied.
byte KeyNum
  Current or destination keyframe.
EMoverEncroachType MoverEncroachType
  Determines how the mover reacts if encroached by another actor (see declaration for EMoverEncroachType below).
EMoverGlideType MoverGlideType
  GlideByTime will interpolate between keys by starting out slow, building speed to the halfway point, then slowing down until stopped at the next key. MoveByTime will interpolate linearly, at a constant speed between keys.
float MoveTime
  Time to spend moving between keyframes.
byte NumKeys
  Number of keyframes in total (0-3).
float OtherTime
  TriggerPound's StayOpenTime.
name PlayerBumpEvent
  Optional event to cause when the player bumps the mover.
float StayOpenTime
  How long to remain open before closing. (unless TriggerPound is used, see OtherTime)
byte WorldRaytraceKey
  Raytrace the world with the brush here.

### AI

bool bAutoDoor
  Automatically setup Door NavigationPoint for this mover
bool bNoAIRelevance
  Don't warn about this mover during path review

### MoverEvents

name ClosedEvent
  Event to cause when closed
name ClosingEvent
  Event to cause when closing
name LoopEvent
  Event to cause when the mover loops
name OpenedEvent
  Event to cause when opened
name OpeningEvent
  Event to cause when opening

### MoverSounds

sound ClosedSound
  When finish closing.
sound ClosingSound
  When start closing.
sound LoopSound
  Played on Loop
sound MoveAmbientSound
  Optional ambient sound when moving.
sound OpenedSound
  When finished opening.
sound OpeningSound
  When start opening.

### ReturnGroup

bool bIsLeader
  This Mover is the leader of a return group of movers.
name ReturnGroup
  The return group this mover belongs to. All movers in a return group form a linked list of "followers" with each mover's `Leader` pointing to the main mover that should be triggered, bumped, or otherwise activated. The leading mover will tell its `Follower` to open/close whenever it opens or closes itself. The follower in turn will do the same with its own follower, and so on. Make sure only one mover in a return group is flagged as the leader, otherwise you might experience weird stuff including "Infinite Script Recursion" crashes.

### Hidden

KeyRot[24]
KeyPos[24]
array<AntiPortalActor> AntiPortals
vector BasePos
rotator BaseRot
bool bClientPause
bool bDelaying
bool bOpening
bool bPlayerOnly
int ClientUpdate
Mover Follower
  Next mover in the linked list of movers belonging to a "return group".
Mover Leader
  The primary mover of a "return group".
NavigationPoint myMarker
int numTriggerEvents
  Number of times triggered ( count down to untrigger )
vector OldPos
vector OldPrePivot
rotator OldRot
float PhysRate
  Interpolation rate per second.
byte PrevKeyNum
  Previous keyframe.
vector RealPosition
rotator RealRotation
vector SavedPos
rotator SavedRot
Actor SavedTrigger
  The Actor that triggered this Mover.
vector SimInterpolate
vector SimOldPos
int SimOldRotPitch
int SimOldRotRoll
int SimOldRotYaw

## States

OpenTimedMover
  Master State for OpenTimed mover states (for movers that open and close between two keys)
StandOpenTimed (extends OpenTimedMover)
  Open when stood on, wait, then close.
BumpOpenTimed (extends OpenTimedMover)
  Open when bumped, wait, then close.
TriggerOpenTimed (extends OpenTimedMover)
  When triggered, open, wait, then close.
LoopMove
  When triggered, the mover keeps looping through each of the keys until it gets untriggered. It will go to the first key after the last key.
TriggerToggle
  Toggle between two keys when triggered.
TriggerControl
  Start opening immediately when Triggered, begin closing immediately if UnTriggered. Keep a constant speed, which is based on the MoveTime and the distance between the two keys.
TriggerPound
  Can be thought of as acting like the "Ring The Bell" carnival attraction. You "pound" (trigger) it to the top (open key) and it will fall back down. (closed key) When triggered, begin to Open immediately, and if left alone, Open completely and stay open for OtherTime, not StayOpenTime. If Untriggered, begin to Close immediately, similar to TriggerControl, and as if the "pound" didn't have enough strength to make to the top and ring the bell. Unlike TriggerControl which keeps a constant speed, TriggerPound will always use the MoveTime no matter where the Mover is between keys. So, if Triggered while closing or if UnTriggered in the midst of opening, it will reverse direction and move more slowly to the next key. (Note: Although the code appears to indicate that a TriggerPound Mover can loop from a fully Closed key after StayOpenTime if more Trigger events have been sent to it than UnTrigger events, it doesn't appear to loop in any situation tested.)
TriggerAdvance
  (UT200x only?) When Triggered, move toward the Open key. If UnTriggered, stop moving.
BumpButton
  Open when bumped, close when reset.
ConstantLoop
  Loop this mover from the moment we begin. It will go to the first key after the last key.
LeadInOutLooper
  A Looping move that goes from 0 to 1 when trigger, loops 1-x then returns to 0 when triggered again.

(For Scripters: Goes to state LeadInOutLooping when triggered and back to LeadInOutLooper when triggered again.)

RotatingMover
  Uses Movement -> RotationRate. It rotates when triggered and stops rotating when untriggered. If bToggleDirection is true, it toggles rotation directions when triggered.

## Enums

### EMoverEncroachType

ME_StopWhenEncroach
  Stop when the mover hit an actor.
ME_ReturnWhenEncroach
  Return to previous position when the mover hit an actor.
ME_CrushWhenEncroach
  Crush the poor helpless actor.
ME_IgnoreWhenEncroach
  Ignore encroached actors.

### EMoverGlideType

MV_MoveByTime
  Move linearly; a constant speed determined by the distance between the keys and the MoveTime.
MV_GlideByTime
  Move with smooth acceleration; starting out slow, building speed towards the half way point, then slowing down to a stop at the next key, all within the MoveTime.

### 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.

## Known Subclasses

- ClientMover
- DamageTriggerMover (custom)
- SuperApe's VariableTimedMover (custom)
- VitalOverdose/VariableTimedMover (custom)
- JDN:JBMoverDualDestination (custom)

## Related Topics

- The Mover Topics page
- Types of Trigger and Event

## Discussion

EricBlade: A question. In Land of the Dead (UE 2226, roughly UT2003 base code, it seems), movers do not replicate properly. I see there is a bunch of code in Mover, ActionableMover, and DestructableMover to accomodate replication, but it all doesn't seem to work. Would anyone be interested in taking a look at the code, to see if there's something that can be done to fix it without subclassing and reworking every single mover in the entire codebase?

SuperApe: From what I know, it seems that the bNoDelete setting is the primary reason for proper Mover replication. This sets the Mover object itself to replicate to all Clients. So besides the replication block in Mover, you may need to confirm that your engine version is set up to act the same way in that regard.

EricBlade: It turns out, that apparently the actual location/rotation of these movers is not replicated properly.. what the effect is, say you have a door that opens and closes. If the door's normal state is closed, but you open that door .. then another player logs in, they get the door in the original position on their client, even though it is in the open position on the server. This gives you a door that appears open, but is actually closed. Except that it's actually appearing in a third position, the exact opposite of it's normal rotation. It's very weird.

Xian: Well bNoDelete Actors will not be Spawned or Destroyed by the script. It is not responsible for replication DIRECTLY, as is bNetOptional for example, or Role/RemoteRole properties, but apparently the server will delete Server-Side only actors from the Client on game start (not sure if in the PostBeginPlay() session of Engine.GameInfo or earlier. bNoDelete will make the Engine assign the necessary Role/RemoteRole value on Client and Server. Yeah I guess I see what you mean now, you can say it is responsible for replication, but wouldn't say it's the primary reason. You can just as well use bStatic I guess, although apparently Movers don't seem to want it (even though a Brush is bStatic...)

What EricBlade said makes sense, but I am curious how long it takes. I am not that interested in UE2, but in UE1 Timer() is responsible for updating the Client to the Server position, so the first few seconds you join will have a wrong position, although it should be updated later on. Perhaps doing a ClientSetLocation() where you set the position and rotation would be helpful in this case, but I do admit I am too lazy to test, and since I don't have an UE2 dev folder it will be kinda difficult, but I suggest you try it :)

SuperApe: Thank you for elaborating the long answer for me. :)

EricBlade: Not to bring up an oldish subject, but I didn't notice there were some answers here. Although the code for Mover says "Movers default to bNoDelete==true", that was not the case in my actual code. I set bNoDelete=true to the Door movers in the game, and now they appear in the right place when you load the level, but then a few moments later, they either completely disappear, or jump to FSM-only-knows where in the world, apparently on the timer when the position is replicated. Weeeeird.

Sweavo: "Pound Open when triggered. Pound Closed when untriggered." what on earth does it mean "to pound"? There is no other reference to pounding in this context on the wiki

Tarquin: yeah, the TriggerPound stuff is a mystery

Xian: Rough skimming tells me that one difference is that there is no Trigger notification (i.e. calling EndEvent() on the triggering actor) and that the movement loops. Also, you can see that there is no Stop command after Open, but there is, however, a Sleep, and there is a GoTo command after Close which calls the Open label (making a looping session). Although I am really lazy to make a test map (or edit one) to check it, to me it seems like once a Mover with this state is triggered it will cause an Open-Close, Open-Close, Open-Close loop "animation". Considering it belong to UnrealI, I never played it, but I assume one use could be, for example, in a factory, where you have those crushing thingies, f.e. a conveyor belt, and at a point stuff on the belt gets smashed by this, then incinerated or whatever, like a trap-quest, and you'd have to find the right timing to get past it (which explains the name Pound). Just a thought, but if any of you kind people would test, I might get a confirmation :) I'm not sure but these are all assumptions after checking the code.

SuperApe: Making a test map to test TriggerPound took all of two minutes. That and after looking through the code more carefully, I think the best way to consider this Mover state is like that of a carinval attraction: "Ring the Bell" or "High Striker". This is where you use a large mallet to strike a see-saw to propel a weight up a pole, trying to get it to the top to hit a bell. (thus, "Pound") When a Mover set to TriggerPound is triggered, it will Open and Close and stop (like you hit the weight hard enough to go to the top, ring the bell and fall back down). If it is Untriggered during this sequence is will begin to Close immediately, like TriggerControl (also like if you didn't hit the weight hard enough to go to the top of the pole). Unlike TriggerControl, TriggerPound will use the same MoveTime and alter it's Speed to travel whatever distance it needs to Open when triggered, or to Close when Untriggered. IOW, TriggerControl will always move at a constant Speed when Opening or Closing, no matter where it is in that movement between the keys, but TriggerPound will move more slowly to the Open key if it is triggered inbetween the keys, as if you added extra force ("pound") during it's Close movement and you interrupted it's momentum to send it back Open. TriggerPound uses a method of recording how many Trigger and Untrigger events it recieves.

Xian: Except that it loops (or should according to the code). What is IOW btw ? Well I am sorry if my description was not 100% accurate (except maybe 15%-20%), but it's better that you tested.

SuperApe: A Mover set to TriggerPound does not loop. It works as I describe above. It was tested as well as confirmed in the code. IOW = In Other Words.

Xian: According to this...

    Open:
            Disable ('Trigger');
            DoOpen();
            FinishInterpolation();
            FinishedOpening();

            // If the next key frame is not the last, then
            // keep playing back the frames.
            //
            if (Keynum < NumKeys-1) {
                    GotoState ('GradualTriggerOpenTimed', 'Open');
            }
    Close:
            Disable ('Trigger');
            DoClose();
            FinishInterpolation();
            FinishedClosing();    // throw the current Event, if exists

            if (KeyNum > 0)          // Still more key-frames to go through
                    GotoState ('GradualTriggerOpenTimed', 'Close');

            Sleep(StayOpenTime);
            if( bTriggerOnceOnly )
            {
                    AmbientSound = None;
                    GotoState('');
            }
            if( SavedTrigger != None )
                    goto 'Open';
    }

... it does. At least in theory. As you can see, if the pointer to the SavedTrigger is still there, it loops to Open. Perhaps because it was not triggered using both params (Other and Instigator) it didn't loop in your case, but rest assured, it does loop. :) There are situations of calling Trigger(None,X) (where X is the Instigator) so I am not surprised if it doesn't happen in all cases.

SuperApe: We're obviously looking at two different versions of code. In UT200x, a Mover has no "GradualTriggerOpenTimed" state and the only time Trigger() is disabled is after a `bTriggerOnceOnly` check has been made. (Incidentally, I see minor differences between the code in UT2003 and UT2004 to accomodate a Reset() function, but it behaves the same way in both.) I found nothing in the UT200x Mover code that matches the code block you posted. But regardless, the SavedTrigger is simply allowing the Mover to keep track of multiple Trigger and UnTrigger events sent to it. When Triggered, SavedTrigger = something, when UnTriggered an equal number of times, SavedTrigger = None. But that does not seem to allow a fully Closed Mover set to TriggerPound to loop back Open in any case, whether Triggered by a NormalTrigger or via `causeevent` console command (which gives the Mover more Trigger events than Untrigger events). The description I state above is based on both my interpretation of the UT200x code and the results of an empirical test of the Mover state in Ued. It wasn't a guess or theorem. Didn't you ask for a confirmation of your intial assesment?

Xian: I did :) It was pasted from UT. If you'll check the code there you'll see that, as I pasted, the movement gets looped If the SavedTrigger is still referenced (as we both said via triggering). Unfortunately code changed a lot it seems over versions, though I appologize for the confusion.

SuperApe: During a long drive, I had time to think about this and I was bothered by the code that seemed to loop, but didn't in my tests. Then I remembered the weird thing TriggerPound does with the timing properties: OtherTime is used in place of StayOpenTime with TriggerPound, and StayOpenTime is used as shown above, to sleep after the Mover is Closed all the way. I'm convinced you're right, it must loop. Although I am not at a machine I can test this, I'll bet I just didn't wait the default 4 seconds after it Closed to see it loop. However, it should only loop if the Mover was given more Trigger events than Untrigger events, either by `causeevent` console command, ACTION_TriggerEvent from an AIScript subclass, UseTrigger or the like. A single NormalTrigger will always give the Mover an equal number of Trigger and Untrigger events, so in that case it would never loop. I apologize for the confusion. I think we should be able to refactor this Mover state now.

Xian:

> However, it should only loop if the Mover was given more Trigger events than Untrigger events

My point exactly :) Yeah np, I must have confused you a bit as well with the code above (forgetting to mention it is UT). Well trial and error is the key :) Hopefully we'll both get to the bottom of this.

SuperApe: Well, whattaya know. I did more testing, waited extra long times to be sure, but it actually never loops from the fully Closed key. Whether you give it more Trigger events than UnTrigger events, or not. Go figure. I am a little confused by the code now; it seems like it should loop in some instance, but it never appears to do so. So, I guess my analogy of the "ring the bell" carnival attraction describes the TriggerPound Mover state the best.

Xian: OK double checked both UE1 and UE2 and they are the same in the Close session. That is weird indeed. According to the code as long as one SavedTrigger pointer exists (as you said one extra trigger event, which most likely seems to be caused the easiest by CauseEvent) would cause it to loop. I do trust that you did it properly so I am confused as well now... the code makes my analogy correct but the way it works makes yours correct. I think I'll do some testing in UT once I get some free time... "this is too weird for school" as they say :)

SuperApe: I agree, the way the code makes it look, it is confusing. On the other hand, to me TriggerPound sounds like the way it is behaving, it sounds like a "ring the bell" attraction. BTW, it appears that a NormalTrigger is the only way to always get an even number of Trigger and UnTrigger events, but there are several ways a Mover can get just Trigger events along with the `causeevent` console command: ACTION_TriggerEvent, UseTrigger, other Mover Events, etc.

Sweavo: wow, great work guys! All I can offer is "is bTriggerOnceOnly set?" hope I'm not insulting you but it's all too common to miss something like that when you're too close to the task! I'm not able to get to the source at the moment to look for myself...

SuperApe: Not sure what you're asking, Sweavo. Anyway, this page has been refactored in terms of TriggerPound/TriggerControl (and TriggerAdvance), see above descriptions. We can probably remove this part of the discussion at least, if everyone is satisfied.

Xian: He's right. He means that if bTriggerOnceOnly was set then they won't react to other triggering effects. According to the code, if that is set it leaves that state completely. Would you mind testing with and without that var just to see why the code says one thing and the way it behaves is different?

Sweavo: You are saying "according to the code it should loop, but I don't see it looping"... so I added "the code doesn't loop if bTriggerOnceOnly is set," as Xian also said just there. Trouble is, I don't see bTriggerOnceOnly in the default properties for Mover or an of its super- or sub-classes. Weird.

SuperApe: My tests were with the default setting `bTriggerOnceOnly=false`. `bTriggerOnceOnly=true` does what you'd expect it to: that is, Trigger (and Untrigger, if Untriggered) once and immediately go to a dormant state no matter what Mover state you're using. Tests confirmed this.

Sweavo: OK so that's a blind alley then. As another aside I wonder why that default doesn't show up in my uscript refernece source tree.

SuperApe: Booleans are false by default. There's no need to declare that in defaultproperties.

Mychaeel: ...unless, of course, you're overriding the default inherited from some superclass (or making sure that you don't accidentally inherit some default other than `false`).

Xian: Unfortunately this is not the case since this is the parent/base class.

Kirk: Looks like ConstantLoop doesn't honor StayOpenTime :/

Category:Legacy Refactor Me

Categories: Legacy Refactor Me | Legacy Class (UT2003) | Legacy Class (UT2004)

##### Views

- Legacy
- Discussion
- Current revision

##### Search