[TUTORIAL] Making Payload Elevators

English Mobster

L6: Sharp Member
Jul 10, 2011
355
299
NOTE:
As far as entities go, this tutorial is fairly complex. Payloads in general are very finicky, and a single missplaced number or missed output can screw everything up. I would recommend taking a look at the example .vmf if you get lost.

This system is designed to be called upon multiple times. It effectively replaces what your team_train_watcher typically automates, giving you a lot of control over the Payload, should you know how to utilize it. While not the most efficient way for a single elevator, it can be used multiple times for multiple elevators with only 3 new logic entities per elevator (not counting the elevator's func_tracktrain, capzone, or path_tracks).

This MAY screw with the HUD in Payload Race maps. If it does, I would suggest you delete the elevator's team_train_watcher and place a trigger_hurt underneath the elevator's platform to kill anyone who gets stuck in it. To be honest, I'm not sure if the extra team_train_watcher is truly NEEDED, but, as the guy in the church in Left 4 Dead taught us, it's better to be safe than sorry. A lot of this stuff is probably also redundant (notably my OutValue and OnGetValue dual outputs), but redundancy never hurt much.

Now on to the meat of the tutorial:

  • The Push Logic
I'm using Boojum's stock team_train_watcher (called "sspl_watcher") with the "Handle Train Movement" flag set to "No" and one Output added:
Code:
OnTrainStartRecede | cart_rollback_relay | Trigger | *NO PARAMETER* | 0
A math_remap converts the number of current players on the cart into an integer we can use. I have mine named "cart_push_remap" with "Maximum Valid Input Value" and "Output Value When Input is Max" both set to 3.
I have a single output:
Code:
OutValue | bomb_cart_case | InValue | *NO PARAMETER* | 0
Of course, we can't do anything if the sspl_pushzone (the cart's trigger_capture_area) gives us no numbers to work with! Add the following outputs to Boojum's default sspl_pushzone:
Code:
OnNumCappersChanged | cart_push_remap | InValue | *NO PARAMETER* | 0
OnNumCappersChanged | sspl_watcher | SetNumTrainCappers | *NO PARAMETER* | 0
Now to set up our logic_case we named in the math_remap:
Code:
Name: bomb_cart_case
Case 01: 0
Case 02: 1
Case 03: 2
Case 04: 3
All this does is take the integer given to us by the remap and convert it into something we can work with. The outputs are going to be a doozy:
Code:
OnCase01| rollbackzone_relay | Trigger | *NO PARAMETER* | 0
OnCase01| stopped_relay | Trigger | *NO PARAMETER* | 0
OnCase01| elevator_rollback_relay | Trigger | *NO PARAMETER* | 0
OnCase02| cart_speed_multiplier | GetValue | *NO PARAMETER* | 0
OnCase02| sspl_cartsparks | StopSpark | *NO PARAMETER* | 0
OnCase02| cart_speed_watcher | Multiply | 0.4 | 0.10
OnCase02| elevator_speed_watcher | Multiply | 0.4 | 0.10
OnCase03| cart_speed_multiplier | GetValue | *NO PARAMETER* | 0
OnCase03| sspl_cartsparks | StopSpark | *NO PARAMETER* | 0
OnCase03| cart_speed_watcher | Multiply | 0.7 | 0.10
OnCase03| elevator_speed_watcher | Multiply | 0.7 | 0.10
OnCase04| cart_speed_multiplier | GetValue | *NO PARAMETER* | 0
OnCase04| sspl_cartsparks | StopSpark | *NO PARAMETER* | 0
OnCase04| cart_speed_watcher | Multiply | 1 | 0.10
OnCase04| elevator_speed_watcher | Multiply | 1 | 0.10
I have two math_counters. One of these I call my "watcher." Everything is set to the default settings for a math_counter except for the name field (in my case being "cart_speed_watcher").

All this does is receive the input from the logic_case and spit it back out with these two outputs:
Code:
OnGetValue | sspl_train | SetSpeedDirAccel | *NO PARAMETER* | 0
OutValue | sspl_train | SetSpeedDirAccel | *NO PARAMETER* | 0
The second one I call my "multiplier." This modifies your overall speed. We use it to make the elevator go slower, but you should also use it if you want to speed up or slow down the cart (whenever you would typically send a SetSpeedForwardMultiplier output to your team_train_watcher). Most of the fields are default, except for the "name" and "initial value" fields (cart_speed_multiplier and 1, respectively). It also has 2 outputs:
Code:
OnGetValue | cart_speed_watcher | Multiply | *NO PARAMETER* | 0
OutValue | cart_speed_watcher | Multiply | *NO PARAMETER* | 0
It SHOULD multiply against the cart's current speed when it changes, but I like to send a "GetValue" output to the entity after I send whatever speed adjustment I want to make to the multiplier with a delay of .10, just to be safe. Redundancy.
001_tongue.gif



Now to set up our three logic_relays for the conclusion of the push logic. These handle stopping the cart if no one is nearby, making it roll back down a hill should you want a traditional rollback zone (elevators get their own logic_case), and the usual rollback if a cart is left unattended.
The first is akin to your cart_speed_watcher in that it takes output from the pushzone and translates it into something your cart can use. I named mine "stopped_relay" and left the "Start Disabled" tag set to "No".
As for the output:
Code:
OnTrigger | cart_speed_watcher | SetValue | 0 | 0
The second is like your multiplier where it is only used for rolling back down a hill (for traditional rollback, see Rewind's post). This does NOT apply to your elevator.
Code:
Name: rollbackzone_relay
Start Disabled: Yes
Outputs:

Code:
OnTrigger | cart_speed_watcher | SetValue | 0 | 0
OnTrigger | sspl_train | SetSpeedDirAccel | -0.30 | 0.10
To use this for a rollback zone, just enable "rollback_relay" and disable "stopped_relay" as the cart enters the rollback zone (at the same time you disable the path_track behind it). Keep the flags set on your rollback zone to "Rollback zone on HUD (Auto-roll if team_train_watcher handles movement)," even though you don't have auto-roll. This keeps it on your HUD regardless. Make sure to disable it again after the rollback zone!

Your third should be named "cart_rollback_relay". Here are your outputs:
Code:
OnTrigger | sspl_train | SetSpeedDirAccel | -0.10 | 0.10
OnTrigger | sspl_cartsparks | StartSpark | *NO PARAMETER* | 0.10
  • Elevator Logic
Now we need one more math_counter. This ensures the elevator and the cart will be going the same speed.
Code:
Name: elevator_speed_watcher
Start Disabled: Yes
Outputs:
Code:
OnGetValue | sspl_train | SetSpeedDirAccel | *NO PARAMETER* | 0
OutValue | sspl_train | SetSpeedDirAccel | *NO PARAMETER* | 0
OnGetValue | elevator_train | SetSpeedDirAccel | *NO PARAMETER* | 0
OutValue | elevator_train | SetSpeedDirAccel | *NO PARAMETER* | 0
Create a team_train_watcher. This keeps people from getting stuck in the elevator (may not be needed; I haven't done much testing without it and might screw up the HUD on PLR setups):
Code:
Name: elevator_train_watcher
Team: Red
Start Disabled: Yes
Train to watch: elevator_train
Node the path starts at: elevator_platform_path_start
Node the path ends at: elevator_platform_path_end
I'm not sure if it needs to start disabled or not, but it can't hurt.

And add an output to your logic_auto:
Code:
OnMapSpawn | elevator_train | TeleportToPathTrack | elevator_platform_path_start | 0
Now create your elevator's func_tracktrain platform. Follow Frozen's instructions, giving it a dummy path alongside the cart. Make sure the keyvalues match those of the cart if you want them to appear to be going the same speed. If you're following the naming scheme of this tutorial, name it "elevator_train" and make the "First Stop Target" field "elevator_platform_path_start" (and make a path_track where you want it to wind up named "elevator_platform_path_end").


Copy the cart's trigger_capture_area and paste it on your platform. Change these keyvalues:
Code:
Name: elevator_pushzone
Parent: elevator_train
Start Disabled: Yes
Now create a logic_relay. This will handle the auto-rollback from the elevator.
Code:
Name: elevator_rollback_relay
Start Disabled: Yes
Outputs:
Code:
OnTrigger | elevator_speed_watcher | SetValue | 0 | 0
OnTrigger | cart_speed_watcher | SetValue | 0 | 0
OnTrigger | sspl_cartsparks | StartSpark | *NO PARAMETER* | 0
OnTrigger | elevator_train | SetSpeedDirAccel | -0.30 | 0.10
OnTrigger | sspl_train | SetSpeedDirAccel | -0.30 | 0.10
That's it for the logic! Now to hook it all up.



  • Path_track Logic
At the BOTTOM of your elevator, change the "Angles" field in the top right until the circle with a single line in it is lined up with the direction you want your cart to be facing as seen from the TOP viewport.
Change the "Orientation Type" to "Face this path_track's angles".
Do the same for any other path_tracks inside your elevator (including the dummy ones the elevator follows).
On all of the cart's path_tracks, make sure you have "rollback zone on HUD" checked. I don't believe you need this checked for the elevator's path_tracks, as the team_train_watcher isn't handling movement there, either.

BOTTOM CART PATH_TRACK OUTPUTS:
Code:
OnPass | cart_speed_multiplier | SetValue | .5 | 0
OnPass | elevator_train_watcher | Enable | *NO PARAMETER* | 0
OnPass | elevator_speed_watcher | Enable | *NO PARAMETER* | 0
OnPass | cart_speed_watcher | Disable | *NO PARAMETER* | 0
OnPass | *PATH_TRACK BEFORE THIS ONE* | DisablePath | *NO PARAMETER* | 0
OnPass | cart_speed_multiplier | GetValue | *NO PARAMETER* | 0.10
OnPass | elevator_speed_watcher | GetValue | *NO PARAMETER* | 0.20
OnPass | sspl_pushzone | Disable | *NO PARAMETER* | 0.30
OnPass | sspl_train | SetSpeedDirAccel | 0.55 | 0.30
OnPass | elevator_train | SetSpeedDirAccel | 0.55 | 0.30
OnPass | cart_rollback_relay | Disable | *NO PARAMETER* | 0.30
OnPass | elevator_pushzone | Enable | *NO PARAMETER* | 0.60
OnPass | sspl_train | SetSpeedDirAccel | 0 | 0.60
OnPass | elevator_train | SetSpeedDirAccel | 0 | 0.60
OnPass | elevator_rollback_relay | Enable | *NO PARAMETER* | 0.70
OnPass | stopped_relay | Disable | *NO PARAMETER* | 0.70
BOTTOM ELEVATOR PATH_TRACK OUTPUT:
Code:
OnPass | sspl_cartsparks | StopSpark | *NO PARAMETER* | 0

TOP ELEVATOR PATH_TRACK OUTPUTS:
Code:
OnPass | elevator_train | Stop | *NO PARAMETER* | 0
OnPass |elevator_train| SetSpeedDir | 0 | 0
TOP CART PATH_TRACK OUTPUTS:
Code:
OnPass | elevator_speed_watcher | Disable | *NO PARAMETER* | 0
OnPass | cart_speed_multiplier | SetValue | 1 | 0
OnPass | stopped_relay | Enable | *NO PARAMETER* | 0
OnPass | elevator_rollback_relay | Disable | *NO PARAMETER* | 0
OnPass | cart_speed_watcher | Enable | *NO PARAMETER* | 0
OnPass | elevator_train_watcher | Disable | *NO PARAMETER* | 0
OnPass | elevator_pushzone | Disable | *NO PARAMETER* | 0
OnPass | sspl_train | SetSpeedDirAccel | 0.55 | 0.10
OnPass | sspl_train | SetSpeedDirAccel | 0 | 0.40
OnPass | sspl_pushzone | Enable | *NO PARAMETER* | 0.40
OnPass | cart_rollback_relay | Enable | *NO PARAMETER* | 0.40
OnPass | cart_speed_multiplier | GetValue | *NO PARAMETER* | 0.40
NOTE: For all path_track outputs (except for the StopSpark output on the bottom elevator path_track) check the "Fire Once Only" checkbox.


Now if you (and I) have followed this properly, then you'll have a working elevator! There may need to be some tweaking involved; it is a fairly complex system and likely can be simplified some.
Follow everything from the "Elevator Logic" section down if you'd like a second elevator.
You can have up to 4 rollback/rollforward zones appearing on the HUD. Any more won't show up, so while you CAN have more than 4 elevators or other rollback zones, I wouldn't suggest it.
If I have missed some entity logic or this doesn't work somehow, let me know and I'll see if I can debug it. It was quite a bit of work getting all those outputs correct, and I may have missed something somewhere along the way.
 
Last edited:
Oct 6, 2008
1,948
446
Broken link for the vmf file - thanks