Introduction
The question "how do I make a train?" is asked a lot. Everybody wants to have a train in their map, because dynamic elements are cool, and trains are the best dynamic element in tf2. (that's a fact, not an opinion). From ctf_well and cp_freight, to cp_snowplow and koth_trainsawlazer- Many of the maps in the tf2 world feature trains. In this tutorial I'll go over creating a train and fitting it to your needs. This tutorial is very long, because I'll give in depth information and tips about all the entities we're using, so that you'll understand exactly what you're doing, and can later use that understanding to create your own train, or better- a whole new setup for something completely different! (I used it to make an elevator for my sd_ map, for example).
What entities we'll be using?
- Train components
- Logic system
Let's begin!
Now we'll build a basic train, like featured in cp_well, or cp_freight. It would only go in a straight line. We'll go later over curved tracks and how those work.
First, make a prop_dynamic. This would be your train model (the train you'll see in-game).
Set it's world model to a train engine. You can make more prop dynamic entities that would be the train cars, if you want to.
IMPORTANT!
The train must be built facing from left to right. However, that won't define it's in-game position, but rather it's orientation relative to the tracks we'll soon place. So you
don't need to rotate your entire map. You just need to build the tracks in the right direction (we'll soon talk about how to do that), and the train- facing right. Don't forget this tip!
Next, make a brush, textured nodraw. it's height should be from the ground to the top of the lowest train part (car or engine), the length should be the train's lenght, and it's width- the train width. Place it over the train props.
Tie the brush to an entity (ctrl+t). Set the entity to "func_tracktrain". That's our train entity! Give it a name, like "train" (w/o quotations). Set all the train prop_dynamic props, so their "parent" property would be set to train. Child entities move together with their parents, so now all the train props would move together with the func_tracktrain entity.
We now need to set some properties for the func_tracktrain. double click it, and set the render mode to "don't render" (that way it would be invisible and won't cast shadows on your beautiful train). Set "max speed" to whatever speed you want your train to have (trains tend to have only one speed- the max speed. "Those drivers never lift their foot off the pedal, I'm telling ya"). I personally use 800. Set damage on crush to a very high number (like 99999).
Set the Move sound property to something like ambient\train.wav or ambient\slow_train.wav, or whatever you want. (in the sound browser window, you can search by putting keywords like "train" in the "filter" textbox, just like in the texture browser).
Then head to the "flags" tab and check "fixed orientation" and "Is unblockable by player".
NOTE
You will need to change another property, if you use a different sized track prop than the usual track props in tf2. You see, by default, the tracks in tf2 are 4 hammer units high (and by track I mean the track prop, not the actual path_track). If yours have a different height, you'll need to change the "Height above track" property to that height.
And now add a brush in front of the train, textured with trigger texture, and tie it to an entity (ctrl+t). Set the entity class to trigger_hurt, set the damage type to train. That would set the kill icon to the train one.
Set the "damage" property to a very big number (like 9999999). That would kill all players touching the front end of the train, no matter how much they are healed.
Parent the trigger to the func_tracktrain. Then open the flags tab, and check the flag "Everything (not including physics debris)". That would be used so that the trigger also damages engineer machinery. (And I don't remember for sure, but I think it also has to do with killing Ubered players)
Now the train needs to move. For that, we'll have to define a track. Place an entity, and set it's class to "path_track". This is an entity used to define the path of objects that move, in the source engine (like rd_ bots, payload carts, etc.) The way path tracks work, is by "connecting" them. Two connected path tracks would form a path. Payloads might have hundreds of path_tracks, but we'll only need two for now.
The path_track needs to be located where you want the train to start, but you must remember that the train is not a real life train, it's a brush entity. Unlike real life trains, brush entity trains move from their origin. (marked with a little red circle when selected, by default it's set to the center of the brush). So you must think where you want the origin to be when the train starts, Not the front!!! Take that into consideration when planning the train area in hammer. Also remember to put the path_track in the proper height- the height should be the func_tracktrain origin's height.
IMPORTANT!
The train's start and end spots should be well hidden in your map. (In a tunnel like cp_freight, or a warehouse like cp_well. It's up to you). If it won't be hidden, people would see your train suddenly appear, or disappear.
Set the path track name to something simple, but for ease of usage later, make it end with "_01". (I'll name mine "path_train_01"). Now, all the default properties are good for us.
Now, here's a cool trick- If you shift+drag your path track, hammer would automatically assume that the new path track is the next path_track for this path, and would connect them without you having to do a thing! It would also automatically rename the new path track, so that's cool too. Shift+drag a new path_track, and place it where you want the train to end it's track. (Same as before- end=where the origin should be at the end)
After you've done that, open the 1st path_track properties, and in the "flags" tab, check "teleport to THIS path_track". That marks this path_track as the first.
Open the func_tracktrain properties again, and set the "first stop target" to the first path_track name. That tells the func_tracktrain what is the path_track path it should follow.
And now for the final setup before we are finished with the basic train components- we'll need to set an output for the train to go back to the first path_track, after it reached the end. (Otherwise it would just stay at the edge of the path, and won't go again)
Open the last path_track's properties window, select the "outputs" tab, and make a new output-
That's it! you have a train! but the train won't yet work, because you never told it when to start. And here's a great part- Everything can cause a train to start! (And I mean
everything). From picking up an intel or capping a point, to stepping into a trigger volume. But for staying consistent with ctf_well, cp_freight and the rest of the maps, we'll use a timer.
Place an entity somewhere in your map (it doesn't matter where, but I reccomend putting it near the train for ease of access). Set it's class to
logic_timer. Give it a name ("timer"). Now you'll need to chose how you want the timer to work- using random times, or pre-set times. You will set the "Use random times" property accordingly. Then you'll set the refire interval if you chose pre-set times, or the minimum/maximum time if you chose random times. (times are in seconds obviously).
Then, on the outputs tab, add an output to the timer.
That will tell the train to start driving. You can trigger this output (StartForward) from wherever you want- as I said, it can be a control point, a trigger, or anything else.
You may want to set the delay of the output to a few seconds, if you want to add the hazard bells sound to activate before the train comes, like in ctf_well. In that case, you'll need to add an ambient_generic entity (the entity used to play sound files in your map) with a name ("train_bells") and the sound property set to the ringing bells sound (ambient/railroad_bells.wav), and then add an output from the timer,
To make sure that the bells don't play forever, add another output:
Make sure you set the delay properly. The delay means how much time the output will wait between the time it was triggered (in this case-triggered by the clock) until it gets "sent". You want to give the train something around ~1 second and the bells StopSound something around ~5 seconds.
Finishing up
Well, from here, you just need to compile. (F9). Make sure everything works, and enjoy your train!
And just to sum this tutorial up, you've just learned how to make elevators, lifts, moving spaceships, or anything that needs to move around in the map.
Yes that's right. ITS THE SAME BASIC SETUP FOR ALL MOVING THINGS. (not including doors, which are done differently)
You just need to change the things you parent to the func_tracktrain (an elevator or a spaceship, in this case), and the trigger for the movement starting (a trigger brush for the elevator, a logic_auto for the spaceship).
What are you waiting for? Open hammer and fill your map with dynamic moving things!
If you have a question, a suggestion, If you found a mistake, or suddenly got an urge to show off your amazing train, be sure to add those in the comments below.
Useful train links:
Freyja's lazy train prefab. Also featuring gates, which I didn't go over.
Another tutorial, which is more Half-Life oriented, and less complete IMO, but still relevant