Payload elevators, what I know so far (a guide, of sorts)

English Mobster

L6: Sharp Member
Jul 10, 2011
355
299
Frozen, would this work for multiple platforms (let's say 2)? From what I understand, the team_train_watcher gets buggy with the HUD if you have more than one for Payload.
But what if the elevator got parented to the cart, using the cart's team_train_watcher? Would things still work?
I have a working elevator in my map, but it goes up at a 45 degree angle, thus making me clip through it and getting stuck and making things a mess in general. I just want to see if you know of any ways to work around this problem before I scrap it altogether.
 

re1wind

aa
Aug 12, 2009
644
588
Here the method i used to get my hl1-style vertical & horizontal platform to work in sunken rocket.

NOTE 1: this method and the I/o, train settings, etc. can be improved, optimized, tweaked, etc. I do not claim that this is the best method of doing this, but it works.

NOTE 2: Some parts of this tutorial are not optimized for a purely vertical elevator, as the hl1-style elevator i made is slightly more complex.

NOTE 3: I *HIGHLY* suggest to NOT compile using vvis and vrad while testing your logic entities. I.e. full-bright, no visibility control, etc. This improves the time between testing the entities and fixing/adjusting them, instead of waiting for the map to look pretty. You should not care about prettiness for entity testing, you're wasting time, and its an inefficient work-flow. In my opinion.

I suggest looking through the following tutorial first to get an understanding of how to set up the cart manually, as it is explained far better than i could.

LINK -> https://developer.valvesoftware.com/wiki/TF2/Creating_a_Payload_Map <- LINK

Let me attempt some explanation first before i go into the specific entities.

The default logic_case controls the cart's tiered speed along the 'normal' track sections of your map by using the SetSpeedDirAccel input. This basically tells the train to go at a fraction of the cart's maximum speed [0 = stop, 1 = full speed forward, -1 = full speed backwards, 0.5 = 50% of max speed forward, etc.]

So you now have one logic_case which controls the carts speed. The we I use this entity to control the cart is to split the track into sections. one logic_Case for the area before the elevator, one logic_case for the elevator section, and another logic_case for the area after the elevator area, if there is one.

Hightower only has two logic_cases per team: one for the track before the start of the elevator, and one for the elevator.

Hightower kills the old logic_case when the cart reaches the base of the elevator and spawns in the new one, which is also referenced by the math_remap from the link above.

The new logic_Case that is spawned sends setSpeedDirAccel inputs to BOTH the Cart's train and the platform, which is also a train. This is how the platform moves at the same speed as the payload cart.

That's it. It's that simple.

The i/o from hightower looks complicated because it does a lot of extra things, like enabling/disabling sounds, sparks, killing a func_detail, lots of relays, etc.

You may now have an idea on how to get your rollback working, and i can only encourage you to experiment. see note3 though.


Right, now for some entities.

The math_remap from the linked tutorial needs to reference these other log_cases as well, as that saves on entity logic.

i.e. math_remap:
Code:
outvalue       bomb_logicCase                   inValue
outvalue       bomb_logicCase_elv              inValue
outvalue       bomb_logicCase_post_elv      inValue

The path_Track that your elevator is supposed to start at needs some entity outputs.
Code:
	1) disable the path_Track behind it
	2) kill the first logic_case that the cart uses after 0.1seconds
	3) forcespawn the new elevator logic_case after 0.2 seconds
	3) disable the capture zone around the cart
	4) enable the capture zone around the cart after a second

What this does is disable the path_Track behind the start of the elevator so that the cart cannot rollback.
it disables the capture zone around the cart so that the cart doesn't move.
it removes the bomb_logicCase from the world and spawns in the bomb_logicCase_elv a tenth of a second later which is specifically designed to controll both the bomb cart train, and the platform train.
lastly, it enables the capture zone around the cart allowing for it to be pushed again.


At the end of your elevator, assuming that there is more of your map afterwards, the above procedure is repeated, just the bomb_logicCase_elv is deleted and replaced with the bomb_logicCase_post_elv which should be identical to the first logic_Case.

You may have noticed that there is no cart receding outside of the elevator area, because we told the team_Train_watcher that we wanted to handle the cart's movement manually. Here's how: the team_Train_watcher has a handy output called "OnTrainStartRecede".

team_train watcher -> onTrainStartRecede -> trigger logic relay -> onTriggered -> bomb_cart -> setspeedDirAccel -0.1

However, rollback is identical to receding for the train, so the logic_relay will need to be disabled along the elevator section. Once the cart is past that, though, the logic_relay can be enabled to allow the cart to recede again.


I hope this helped somewhat. :)
 

Dragonic

L1: Registered
Dec 30, 2011
34
3
Thanks for the explanation re1wind, but I'm still having trouble getting my elevator to function properly.
If possible, could you please upload an example elevator as you explained above? I tend to learn better through example.
 

English Mobster

L6: Sharp Member
Jul 10, 2011
355
299
Frozen, did you ever fix the "need to exit/re-enter capture zone" bug? I have a working elevator except for that bit. :/

I attempted to simplify Rewind's method using a bunch of math_counters, only to wind up with the same amount of entity work for the first elevator as he does, as well as more "wiring" and overall a more complex setup. :p
A plus, however: my system is completely reusable. I can call on it for multiple elevators with minimal new entities (only one more math_counter and one logic_relay per elevator).
If I can get this simplified some more and get all the kinks worked out perhaps I'll make a post as well. As it stands, I only JUST now got it perfectly stable.

E: Got it working! The trick is to enable/disable the 2 cap zones (on the cart and on the elevator) in tiers. I have the cart's cap zone disabled first, then the elevator's cap zone enabled .10 seconds later (note from the future: actually, it's a bit more complex than that. See below). Switching back to the cart's pushzone, I have it set up so the cart is enabled first, then the elevator's cap zone is disabled. This creates a seamless transition between the two.

As promised, here is my setup.
If you would like an example .vmf to study, click here.

There is an equal number of entities compared to Rewind's and it is easier to add in other elevators, but it is overall more "wiring"-intensive.


  • 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:

English Mobster

L6: Sharp Member
Jul 10, 2011
355
299
I just thought of a stupidly obvious solution to the elevator conundrum:
If you can stand having the cart stopped for a moment, you can go ahead and leave a 1-2 second gap inbetween the cart's capzone being disabled and the elevator's capzone being enabled. MAKE SURE YOU DON'T ENABLE YOUR "elevator_rollback_relay" UNTIL YOU ENABLE THE ELEVATOR CAPZONE. You can then do the opposite at the other end (this time taking care to disable the "elevator_rollback_relay" and enable your "stopped_relay" before you disable the elevator's capzone, unless you want to go back down the hill).
If you don't want an area where the cart is stopped, you can also handle it with the same method, only activating "crossing" logic in ABS' PLR setup while the capzone is disabled, temporarily automating the cart on and off of the elevator.

And Joodude, because I have a RED team_train_watcher, your HUD might get screwed up on the elevator. If so, remove the team_train_watcher and see what happens. I keep it in there to prevent players from getting stuck, but it MAY not be needed.
 
Last edited:

Boylee

pew pew pew
aa
Apr 29, 2008
1,068
709
Payload lifts hurt my brain. I currently have two (glitchy but) working lifts but I can't get them to display as roll back zones on the hud. If I try and flag the path_track entities that the cart follows up the lift as roll back zones my cart does a backflip. :S
 

grazr

Old Man Mutant Ninja Turtle
aa
Mar 4, 2008
5,441
3,814
How is it done in hightower? I'm guessing it's displayed as a rollback zone?

I think perhaps having that represented on the HUD isn't intirely important. People generally assume correctly what/how a lift mechanic does/works. So long as you have game mechanic hints like hazard strips it should be fine, when players see the lift fall once, they'll know in future.

But it might be worth having it near a CP, before or after or on the lift so that players can gauge distance. I mean it also makes sense as a gameplay element since it's a significant choke and area of importance.
 
Last edited:

tyler

aa
Sep 11, 2013
5,102
4,621
It is displayed as a rollback. The problem people are having is getting the cart off the platform in a way that makes sense and doesn't break everything.
 

Boylee

pew pew pew
aa
Apr 29, 2008
1,068
709
But it might be worth having it near a CP, before or after or on the lift so that players can gauge distance.

I have a cap point immediately after the elevator, it's symbolic more than anything, the elevator is, in essence the cap point.

It is displayed as a rollback. The problem people are having is getting the cart off the platform in a way that makes sense and doesn't break everything.

Odd, I have the exact opposite problem, I have no roll back zone displaying but my lift doesn't automate on and off. However once it gets to the top it won't roll back at all, but this is because I plan to have the cap on the elevator and it only captures once the lift is at the top.
 

English Mobster

L6: Sharp Member
Jul 10, 2011
355
299
Boylee, take a look at my .vmf (newly optimized!). I had it automate itself onto (and off of) the platform. You don't need to copy the whole system, just look at outputs on the path_tracks named "sspl_path_start1" and "sspl_path_start4" (notably the bits targeting "sspl_train").

To get the rollback zone displaying on your HUD, go to the CART'S path_tracks and make sure you have the "Rollback zone on HUD (Auto-roll if team_train_watcher handles movement)" flag checked for all path_tracks the cart follows along the course of the elevator.

As for capturing at the end of the elevator, add these outputs to the last path_track the cart touches in the elevator (assuming all payload entities follow Boojum's naming scheme):
Code:
OnPass | sspl_pushzone | CaptureCurrentCP | <none> | 0 | Yes
OnPass | *CAPTURE POINT NAME* | SetOwner | 3 | 0.10 | Yes
 

Boylee

pew pew pew
aa
Apr 29, 2008
1,068
709
Cheers dude. I tried enabling the roll back flag but like I said it makes my cart do a backflip.

On the other hand the roll backs are so close to the point you won't see them on the hud anyway so that doesn't matter too much.

It sounds like I'm using a similar set up to the one you have anyway, I have just noticed that sometimes the cart doesn't move when you start to capture the lift but the lift does. If you get off and let it reset it seems to behave itself again. I think it only happens when I sit on top of the cart though. I'll double check that now.
 

English Mobster

L6: Sharp Member
Jul 10, 2011
355
299
It sounds like your cart does a backflip because the team_train_watcher is handling movement. You CAN disable that, but it makes things a hell of a lot more complex.

As for the other bit, that sounds like just a timing issue with when you make your elevator move compared to the cart. Either the cart isn't moving at the same speed, they're moving at different angles, the elevator has a shorter units to reach max speed, or there is a timing issue going on in the path_track's outputs. I can't really know which without knowing the specifics of the system you're using, but I would just take a closer look at the area when the cart gets on the elevator. I don't think riding the cart should make a difference unless the trigger_capture_zone parented to the elevator is at an odd height.
 

Boylee

pew pew pew
aa
Apr 29, 2008
1,068
709
Yeah I have the team_train_watcher handling the movement, like I said though not too much of an issue when the point is on the lift.

As for the cart and the lift intersecting I'm not sure what's causing it. I'm using the same setup that ABS built for corrode, except there's a point on the lift, but that doesn't interfere with the setup of the lift at all. Speaking of relative velocities, I think it may have to do with not stopping the cart when it first gets on the lift...


EDIT: yep that fixed it, thanks for all the help. :)
 
Last edited:

Boylee

pew pew pew
aa
Apr 29, 2008
1,068
709
OK so I've made sure all my entities line up perfectly where they should, the origins are correct and timings are synchronised and I still have the problem of the cart tweaking out (doing a quarter backfilp), accompanied by this error in console:

Bad SetLocalAngularVelocity(-5987.276855,0.000000,0.000000) on sspl_train

I've tried setting up the lift with the cart's pushzone disabled, and also with it enabled and synchronised with the elevator pushzone. Both setups share this error, so it's either not related to the pushzones at all or it's related to only the elevator's push zone.

If anyone has any idea what's causing this I'd be really grateful for some help, I've rebuilt these elevators at least 5 times now. :/
 

Boylee

pew pew pew
aa
Apr 29, 2008
1,068
709
OK so I've come up with a work around for this problem (which only I seem to be having :().

The fix is to decouple the phys_constraint that couples the cart model with the func_tracktrain at the bottom of the elevator and enable one that couples the cart model to the elevator (or rather a propproxy func_brush parented to the lift).

It still chucks an error in console but it works fine in game. However, it's making me think that we're going about making the elevators all wrong and that coupling the func_tracktrain to the elevator is a better move than setting the number of cappers from the elevator's capture area. Would this work or would the func_tracktrain and train watcher freak out at the cart being manually moved through the points on the lift?